@rivus/agent 0.11.0 → 0.12.4
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/README.md +3 -3
- package/dist/acp.d.ts +6 -1
- package/dist/acp.js +121 -29
- package/dist/agent-loop.d.ts +8 -1
- package/dist/index.d.ts +136 -21
- package/dist/index.js +715 -236
- package/dist/rivus-daemon-cli.js +6 -0
- package/examples/pi-feishu-deployment.bootstrap.ts +13 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -30,71 +30,76 @@ function createSerialExecutor() {
|
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/application/feishu/feishu-stream-projector.ts
|
|
32
32
|
function createFeishuStreamProjector() {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}];
|
|
62
|
-
case "agent_run_cancelled":
|
|
63
|
-
textByRun.delete(event.runId);
|
|
64
|
-
return [{
|
|
65
|
-
...event.reason ? { reason: event.reason } : {},
|
|
66
|
-
runId: event.runId,
|
|
67
|
-
type: "cancel"
|
|
68
|
-
}];
|
|
69
|
-
default: return [];
|
|
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
|
+
};
|
|
44
|
+
const applyEvent = (event, canonicalText) => {
|
|
45
|
+
switch (event.type) {
|
|
46
|
+
case "agent_run_accepted":
|
|
47
|
+
currentTextByRun.set(event.runId, "");
|
|
48
|
+
lastTextByRun.set(event.runId, "");
|
|
49
|
+
return [];
|
|
50
|
+
case "agent_model_execution_started":
|
|
51
|
+
currentTextByRun.set(event.runId, "");
|
|
52
|
+
return update(event.runId, "正在分析…");
|
|
53
|
+
case "agent_skill_execution_started": return update(event.runId, `正在读取技能 \`${presentProgressIdentifier(event.skillId)}\`…`);
|
|
54
|
+
case "agent_tool_execution_started": return update(event.runId, `正在执行工具 \`${presentProgressIdentifier(event.toolName)}\`…`);
|
|
55
|
+
case "agent_tool_execution_ended": return update(event.runId, event.isError ? "工具执行失败,正在调整方案…" : "已取得工具结果,正在继续分析…");
|
|
56
|
+
case "assistant_text_delta": {
|
|
57
|
+
const text = `${currentTextByRun.get(event.runId) ?? ""}${event.delta}`;
|
|
58
|
+
currentTextByRun.set(event.runId, text);
|
|
59
|
+
if (text.trim()) lastTextByRun.set(event.runId, text);
|
|
60
|
+
return update(event.runId, text);
|
|
70
61
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
type: "update_text"
|
|
78
|
-
}];
|
|
79
|
-
case "agent_run_completed": return [{
|
|
62
|
+
case "agent_run_completed": {
|
|
63
|
+
const currentText = currentTextByRun.get(event.runId)?.trim();
|
|
64
|
+
const lastText = lastTextByRun.get(event.runId)?.trim();
|
|
65
|
+
const text = currentText || lastText || canonicalText || event.finalText;
|
|
66
|
+
clear(event.runId);
|
|
67
|
+
return [{
|
|
80
68
|
runId: event.runId,
|
|
81
|
-
text
|
|
69
|
+
text,
|
|
82
70
|
type: "finish"
|
|
83
71
|
}];
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
}
|
|
73
|
+
case "agent_run_failed":
|
|
74
|
+
clear(event.runId);
|
|
75
|
+
return [{
|
|
76
|
+
errorMessage: event.errorMessage,
|
|
86
77
|
runId: event.runId,
|
|
87
78
|
type: "fail"
|
|
88
79
|
}];
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
case "agent_run_cancelled":
|
|
81
|
+
clear(event.runId);
|
|
82
|
+
return [{
|
|
83
|
+
...event.reason ? { reason: event.reason } : {},
|
|
91
84
|
runId: event.runId,
|
|
92
85
|
type: "cancel"
|
|
93
86
|
}];
|
|
94
|
-
|
|
95
|
-
}
|
|
87
|
+
default: return [];
|
|
96
88
|
}
|
|
97
89
|
};
|
|
90
|
+
return {
|
|
91
|
+
apply: (event) => applyEvent(event),
|
|
92
|
+
applyUpdate: ({ event, state }) => event.type === "agent_run_failed" ? applyEvent({
|
|
93
|
+
...event,
|
|
94
|
+
errorMessage: state.errorMessage ?? event.errorMessage
|
|
95
|
+
}, state.finalText) : event.type === "agent_run_cancelled" ? applyEvent(state.cancellationReason ? {
|
|
96
|
+
...event,
|
|
97
|
+
reason: state.cancellationReason
|
|
98
|
+
} : event, state.finalText) : applyEvent(event, state.finalText)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function presentProgressIdentifier(value) {
|
|
102
|
+
return value.replace(/[^\p{L}\p{N}._:/-]+/gu, " ").trim().slice(0, 80) || "unknown";
|
|
98
103
|
}
|
|
99
104
|
//#endregion
|
|
100
105
|
//#region src/application/feishu/feishu-card-delivery-ledger.ts
|
|
@@ -172,7 +177,7 @@ function initialRecord(runId) {
|
|
|
172
177
|
}
|
|
173
178
|
//#endregion
|
|
174
179
|
//#region src/infrastructure/persistence/persistence-value.ts
|
|
175
|
-
function isRecord$
|
|
180
|
+
function isRecord$5(value) {
|
|
176
181
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
177
182
|
}
|
|
178
183
|
function isNodeErrorWithCode(error, code) {
|
|
@@ -209,7 +214,7 @@ async function load$3(filePath) {
|
|
|
209
214
|
for (const [index, line] of raw.split("\n").entries()) {
|
|
210
215
|
if (line.trim().length === 0) continue;
|
|
211
216
|
const envelope = JSON.parse(line);
|
|
212
|
-
if (!isRecord$
|
|
217
|
+
if (!isRecord$5(envelope) || envelope.version !== 1 || !isCardDeliveryRecord(envelope.record)) throw new Error(`invalid Feishu card delivery snapshot at line ${index + 1}`);
|
|
213
218
|
const record = envelope.record;
|
|
214
219
|
const previous = records.get(record.runId);
|
|
215
220
|
if (record.revision !== (previous?.revision ?? 0) + 1 || (previous ? !isValidTransition$3(previous, record) : !isValidInitialRecord(record))) throw new Error(`invalid Feishu card delivery revision at line ${index + 1}`);
|
|
@@ -225,7 +230,7 @@ function isValidTransition$3(previous, next) {
|
|
|
225
230
|
return next.terminalPublished ? next.sequence === previous.sequence : next.sequence === previous.sequence + 1;
|
|
226
231
|
}
|
|
227
232
|
function isCardDeliveryRecord(value) {
|
|
228
|
-
return isRecord$
|
|
233
|
+
return isRecord$5(value) && Number.isInteger(value.revision) && value.revision > 0 && typeof value.runId === "string" && value.runId.length > 0 && Number.isInteger(value.sequence) && value.sequence >= 0 && typeof value.terminalPublished === "boolean";
|
|
229
234
|
}
|
|
230
235
|
//#endregion
|
|
231
236
|
//#region src/domain/recovery-action.ts
|
|
@@ -298,6 +303,35 @@ function createFeishuInboxRepository(options = {}) {
|
|
|
298
303
|
}),
|
|
299
304
|
catch: (error) => error
|
|
300
305
|
}),
|
|
306
|
+
claimById: (input) => Effect.tryPromise({
|
|
307
|
+
try: () => serial.run(async () => {
|
|
308
|
+
const current = deliveries.get(input.id);
|
|
309
|
+
if (!current) return void 0;
|
|
310
|
+
const available = current.state.status === "leased" && Date.parse(current.state.leaseExpiresAt) <= Date.parse(input.now) ? {
|
|
311
|
+
...current,
|
|
312
|
+
revision: current.revision + 1,
|
|
313
|
+
state: {
|
|
314
|
+
availableAt: input.now,
|
|
315
|
+
status: "pending"
|
|
316
|
+
}
|
|
317
|
+
} : current;
|
|
318
|
+
if (available !== current) await save(available);
|
|
319
|
+
if (available.state.status !== "pending" || Date.parse(available.state.availableAt) > Date.parse(input.now)) return;
|
|
320
|
+
const claimed = {
|
|
321
|
+
...available,
|
|
322
|
+
revision: available.revision + 1,
|
|
323
|
+
state: {
|
|
324
|
+
leaseExpiresAt: input.leaseExpiresAt,
|
|
325
|
+
leaseId: input.leaseId,
|
|
326
|
+
leasedAt: input.now,
|
|
327
|
+
status: "leased"
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
await save(claimed);
|
|
331
|
+
return structuredClone(claimed);
|
|
332
|
+
}),
|
|
333
|
+
catch: (error) => error
|
|
334
|
+
}),
|
|
301
335
|
complete: (input) => mutateLeased(deliveries, serial, save, input.id, input.leaseId, (current) => ({
|
|
302
336
|
...current,
|
|
303
337
|
revision: current.revision + 1,
|
|
@@ -349,7 +383,15 @@ function createFeishuInboxRepository(options = {}) {
|
|
|
349
383
|
return structuredClone(requeued);
|
|
350
384
|
}),
|
|
351
385
|
catch: (error) => error
|
|
352
|
-
})
|
|
386
|
+
}),
|
|
387
|
+
release: (input) => mutateLeased(deliveries, serial, save, input.id, input.leaseId, (current) => ({
|
|
388
|
+
...current,
|
|
389
|
+
revision: current.revision + 1,
|
|
390
|
+
state: {
|
|
391
|
+
availableAt: input.availableAt,
|
|
392
|
+
status: "pending"
|
|
393
|
+
}
|
|
394
|
+
}))
|
|
353
395
|
};
|
|
354
396
|
}
|
|
355
397
|
function validateNewDelivery(delivery) {
|
|
@@ -523,7 +565,7 @@ async function load$2(filePath) {
|
|
|
523
565
|
for (const [index, line] of raw.split("\n").entries()) {
|
|
524
566
|
if (line.trim().length === 0) continue;
|
|
525
567
|
const envelope = JSON.parse(line);
|
|
526
|
-
if (!isRecord$
|
|
568
|
+
if (!isRecord$5(envelope) || envelope.version !== 1 || !isDelivery(envelope.delivery)) throw new Error(`invalid Feishu inbox snapshot at line ${index + 1}`);
|
|
527
569
|
const delivery = envelope.delivery;
|
|
528
570
|
const previous = latest.get(delivery.id);
|
|
529
571
|
if (delivery.revision !== (previous?.revision ?? 0) + 1) throw new Error(`invalid Feishu inbox revision at line ${index + 1}`);
|
|
@@ -535,14 +577,14 @@ async function load$2(filePath) {
|
|
|
535
577
|
function isValidTransition$2(previous, next) {
|
|
536
578
|
if (previous.id !== next.id || previous.acceptedAt !== next.acceptedAt || previous.laneKey !== next.laneKey || !isDeepStrictEqual(previous.payload, next.payload) || !isDeepStrictEqual(previous.options, next.options)) return false;
|
|
537
579
|
switch (previous.state.status) {
|
|
538
|
-
case "pending": return next.state.status === "leased" && next.attempts === previous.attempts + 1 && isDeepStrictEqual(previous.recovery, next.recovery);
|
|
580
|
+
case "pending": return next.state.status === "leased" && (next.attempts === previous.attempts || next.attempts === previous.attempts + 1) && isDeepStrictEqual(previous.recovery, next.recovery);
|
|
539
581
|
case "leased": return (next.state.status === "pending" || next.state.status === "completed" || next.state.status === "dead") && next.attempts === previous.attempts && isDeepStrictEqual(previous.recovery, next.recovery);
|
|
540
582
|
case "completed": return false;
|
|
541
583
|
case "dead": return next.state.status === "pending" && next.attempts === 0 && next.recovery !== void 0 && isRecovery(next.recovery) && next.state.availableAt === next.recovery.at && !isDeepStrictEqual(previous.recovery, next.recovery);
|
|
542
584
|
}
|
|
543
585
|
}
|
|
544
586
|
function isDelivery(value) {
|
|
545
|
-
if (!isRecord$
|
|
587
|
+
if (!isRecord$5(value) || !nonEmpty$1(value.acceptedAt) || !Number.isFinite(Date.parse(value.acceptedAt)) || !Number.isInteger(value.attempts) || value.attempts < 0 || !nonEmpty$1(value.id) || !nonEmpty$1(value.laneKey) || !Number.isInteger(value.revision) || value.revision < 1 || !isRecord$5(value.payload) || value.recovery !== void 0 && !isRecovery(value.recovery) || !isRecord$5(value.state)) return false;
|
|
546
588
|
switch (value.state.status) {
|
|
547
589
|
case "pending": return timestamp(value.state.availableAt);
|
|
548
590
|
case "leased": return timestamp(value.state.leasedAt) && timestamp(value.state.leaseExpiresAt) && nonEmpty$1(value.state.leaseId) && Date.parse(value.state.leaseExpiresAt) > Date.parse(value.state.leasedAt);
|
|
@@ -552,13 +594,13 @@ function isDelivery(value) {
|
|
|
552
594
|
}
|
|
553
595
|
}
|
|
554
596
|
function isRecovery(value) {
|
|
555
|
-
return isRecord$
|
|
597
|
+
return isRecord$5(value) && nonEmpty$1(value.actorId) && timestamp(value.at) && nonEmpty$1(value.note);
|
|
556
598
|
}
|
|
557
599
|
function timestamp(value) {
|
|
558
600
|
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
559
601
|
}
|
|
560
602
|
function nonEmpty$1(value, key) {
|
|
561
|
-
const candidate = key === void 0 ? value : isRecord$
|
|
603
|
+
const candidate = key === void 0 ? value : isRecord$5(value) ? value[key] : void 0;
|
|
562
604
|
return typeof candidate === "string" && candidate.trim().length > 0;
|
|
563
605
|
}
|
|
564
606
|
//#endregion
|
|
@@ -640,7 +682,7 @@ async function readSnapshot$2(filePath) {
|
|
|
640
682
|
const raw = await readPersistenceFile(filePath);
|
|
641
683
|
if (raw === void 0) return [];
|
|
642
684
|
const value = JSON.parse(raw);
|
|
643
|
-
if (!isRecord$
|
|
685
|
+
if (!isRecord$5(value) || value.version !== SNAPSHOT_VERSION || !Array.isArray(value.sessions)) throw new Error("Feishu session store must contain version 1 sessions");
|
|
644
686
|
return value.sessions.map(readRecord$2);
|
|
645
687
|
}
|
|
646
688
|
async function writeSnapshot$1(filePath, records) {
|
|
@@ -650,7 +692,7 @@ async function writeSnapshot$1(filePath, records) {
|
|
|
650
692
|
});
|
|
651
693
|
}
|
|
652
694
|
function readRecord$2(value) {
|
|
653
|
-
if (!isRecord$
|
|
695
|
+
if (!isRecord$5(value) || typeof value.baseSessionKey !== "string" || !Number.isSafeInteger(value.generation)) throw new Error("Feishu session record must contain a baseSessionKey and generation");
|
|
654
696
|
return {
|
|
655
697
|
baseSessionKey: value.baseSessionKey,
|
|
656
698
|
generation: value.generation
|
|
@@ -712,6 +754,10 @@ function createSessionScheduler(options) {
|
|
|
712
754
|
const runtime = runtimes.get(input.sessionKey);
|
|
713
755
|
return runtime ? (await runtime).cancel?.(input) ?? false : false;
|
|
714
756
|
},
|
|
757
|
+
steer: async (input) => {
|
|
758
|
+
const runtime = runtimes.get(input.sessionKey);
|
|
759
|
+
return runtime ? (await runtime).steer?.(input) ?? false : false;
|
|
760
|
+
},
|
|
715
761
|
dispose: async () => {
|
|
716
762
|
if (disposed) return;
|
|
717
763
|
disposed = true;
|
|
@@ -1191,6 +1237,18 @@ function createAgentRunUpdateHandler(handle) {
|
|
|
1191
1237
|
catch: (cause) => cause
|
|
1192
1238
|
});
|
|
1193
1239
|
}
|
|
1240
|
+
function createSteeringChannel() {
|
|
1241
|
+
const pending = [];
|
|
1242
|
+
const waiters = [];
|
|
1243
|
+
return {
|
|
1244
|
+
next: () => pending.length > 0 ? Promise.resolve(pending.shift()) : new Promise((resolve) => waiters.push(resolve)),
|
|
1245
|
+
push: (text) => {
|
|
1246
|
+
const waiter = waiters.shift();
|
|
1247
|
+
if (waiter) waiter(text);
|
|
1248
|
+
else pending.push(text);
|
|
1249
|
+
}
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1194
1252
|
function createAgentHarness(options) {
|
|
1195
1253
|
if (options.runTimeoutMs !== void 0 && (!Number.isSafeInteger(options.runTimeoutMs) || options.runTimeoutMs < 1)) throw new Error("Agent run timeout must be a positive integer");
|
|
1196
1254
|
let activeRun;
|
|
@@ -1254,7 +1312,8 @@ function createAgentHarness(options) {
|
|
|
1254
1312
|
const previousSessionTranscript = getSessionTranscript(command.sessionKey);
|
|
1255
1313
|
const cancellation = {
|
|
1256
1314
|
abortController: new AbortController(),
|
|
1257
|
-
deferred: yield* Deferred.make()
|
|
1315
|
+
deferred: yield* Deferred.make(),
|
|
1316
|
+
steering: createSteeringChannel()
|
|
1258
1317
|
};
|
|
1259
1318
|
activeCancellation = cancellation;
|
|
1260
1319
|
const events = [];
|
|
@@ -1331,6 +1390,7 @@ function createAgentHarness(options) {
|
|
|
1331
1390
|
...previousSessionTranscript.turnCount > 0 ? { previousSessionTranscript } : {},
|
|
1332
1391
|
runId,
|
|
1333
1392
|
sessionKey: command.sessionKey,
|
|
1393
|
+
...options.loop.supportsSteering ? { steering: cancellation.steering } : {},
|
|
1334
1394
|
text: command.text
|
|
1335
1395
|
};
|
|
1336
1396
|
const loop = Effect.try({
|
|
@@ -1430,10 +1490,20 @@ function createAgentHarness(options) {
|
|
|
1430
1490
|
}
|
|
1431
1491
|
return completed;
|
|
1432
1492
|
});
|
|
1493
|
+
const requestSteering = (runId, text) => Effect.sync(() => {
|
|
1494
|
+
const normalized = text.trim();
|
|
1495
|
+
if (!normalized || !options.loop.supportsSteering || !activeRun || activeRun.runId !== runId || !activeCancellation || activeCancellation.requested) return false;
|
|
1496
|
+
activeCancellation.steering.push(normalized);
|
|
1497
|
+
return true;
|
|
1498
|
+
});
|
|
1433
1499
|
const requestSessionCancellation = (sessionKey, runId, reason) => {
|
|
1434
1500
|
if (!activeRun || activeRun.sessionKey !== sessionKey) return Effect.succeed(false);
|
|
1435
1501
|
return requestCancellation(runId, reason);
|
|
1436
1502
|
};
|
|
1503
|
+
const requestSessionSteering = (sessionKey, runId, text) => {
|
|
1504
|
+
if (!activeRun || activeRun.sessionKey !== sessionKey) return Effect.succeed(false);
|
|
1505
|
+
return requestSteering(runId, text);
|
|
1506
|
+
};
|
|
1437
1507
|
const requestSessionActiveCancellation = (sessionKey, reason) => {
|
|
1438
1508
|
if (!activeRun || activeRun.sessionKey !== sessionKey) return Effect.succeed(false);
|
|
1439
1509
|
return requestCancellation(activeRun.runId, reason);
|
|
@@ -1531,9 +1601,11 @@ function createAgentHarness(options) {
|
|
|
1531
1601
|
return yield* requestCancellation(activeRun.runId, reason);
|
|
1532
1602
|
}),
|
|
1533
1603
|
cancelRun: requestCancellation,
|
|
1604
|
+
steerRun: requestSteering,
|
|
1534
1605
|
forSession: (sessionKey) => ({
|
|
1535
1606
|
cancelActiveRun: (reason) => requestSessionActiveCancellation(sessionKey, reason),
|
|
1536
1607
|
cancelRun: (runId, reason) => requestSessionCancellation(sessionKey, runId, reason),
|
|
1608
|
+
steerRun: (runId, text) => requestSessionSteering(sessionKey, runId, text),
|
|
1537
1609
|
getActiveRun: () => getSessionActiveRun(sessionKey),
|
|
1538
1610
|
getActiveRunState: () => getSessionActiveRunState(sessionKey),
|
|
1539
1611
|
getAvailability: () => getSessionAvailability(sessionKey),
|
|
@@ -1961,7 +2033,7 @@ function createAgentCommandFromFeishuCardAction(payload) {
|
|
|
1961
2033
|
const messageId = readNonEmptyString(event.context?.open_message_id);
|
|
1962
2034
|
if (!messageId) return yield* Effect.fail(new InvalidFeishuCardAction("card action message id is missing"));
|
|
1963
2035
|
const value = event.action?.value;
|
|
1964
|
-
if (!isRecord$
|
|
2036
|
+
if (!isRecord$4(value)) return yield* Effect.fail(new UnsupportedFeishuCardAction("unsupported card action value"));
|
|
1965
2037
|
if (value.rivus_action === "resolve_interaction") {
|
|
1966
2038
|
const interactionId = readNonEmptyString(value.interaction_id);
|
|
1967
2039
|
if (!interactionId) return yield* Effect.fail(new InvalidFeishuCardAction("card interaction action requires an interaction id"));
|
|
@@ -2006,7 +2078,7 @@ function readInteractionAction(value) {
|
|
|
2006
2078
|
function readNonEmptyString(value) {
|
|
2007
2079
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
2008
2080
|
}
|
|
2009
|
-
function isRecord$
|
|
2081
|
+
function isRecord$4(value) {
|
|
2010
2082
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2011
2083
|
}
|
|
2012
2084
|
//#endregion
|
|
@@ -2043,10 +2115,7 @@ function encodeSegment(value) {
|
|
|
2043
2115
|
return encodeURIComponent(value);
|
|
2044
2116
|
}
|
|
2045
2117
|
//#endregion
|
|
2046
|
-
//#region src/application/feishu/feishu-message-
|
|
2047
|
-
function readTenantKey(payload) {
|
|
2048
|
-
return payload.event.sender?.tenant_key ?? payload.header?.tenant_key ?? "";
|
|
2049
|
-
}
|
|
2118
|
+
//#region src/application/feishu/feishu-message-content.ts
|
|
2050
2119
|
var UnsupportedFeishuMessage = class extends Error {
|
|
2051
2120
|
messageType;
|
|
2052
2121
|
name = "UnsupportedFeishuMessage";
|
|
@@ -2065,10 +2134,76 @@ var InvalidFeishuMessageContent = class extends Error {
|
|
|
2065
2134
|
this.reason = reason;
|
|
2066
2135
|
}
|
|
2067
2136
|
};
|
|
2137
|
+
function readFeishuMessageContent(input) {
|
|
2138
|
+
return input.messageType === "text" ? parseTextContent(input.content) : input.messageType === "post" ? parsePostContent(input.content) : Effect.fail(new UnsupportedFeishuMessage(input.messageType));
|
|
2139
|
+
}
|
|
2140
|
+
function parseTextContent(content) {
|
|
2141
|
+
return Effect.try({
|
|
2142
|
+
try: () => {
|
|
2143
|
+
const parsed = JSON.parse(content);
|
|
2144
|
+
if (typeof parsed.text !== "string") throw new Error("text content is missing");
|
|
2145
|
+
return parsed.text;
|
|
2146
|
+
},
|
|
2147
|
+
catch: (error) => new InvalidFeishuMessageContent(error instanceof Error ? error.message : "invalid JSON content")
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
function parsePostContent(content) {
|
|
2151
|
+
return Effect.try({
|
|
2152
|
+
try: () => {
|
|
2153
|
+
const parsed = JSON.parse(content);
|
|
2154
|
+
const paragraphs = Array.isArray(parsed.content_v2) ? parsed.content_v2 : parsed.content;
|
|
2155
|
+
if (!Array.isArray(paragraphs)) throw new Error("post content is missing");
|
|
2156
|
+
let imageIndex = 0;
|
|
2157
|
+
const body = paragraphs.map((paragraph) => {
|
|
2158
|
+
if (!Array.isArray(paragraph)) throw new Error("post paragraph is invalid");
|
|
2159
|
+
return paragraph.map((element) => {
|
|
2160
|
+
if (element === null || typeof element !== "object") return "";
|
|
2161
|
+
if ("text" in element && typeof element.text === "string") return element.text;
|
|
2162
|
+
if ("user_name" in element && typeof element.user_name === "string") return `@${element.user_name}`;
|
|
2163
|
+
if ("tag" in element && element.tag === "img") {
|
|
2164
|
+
imageIndex += 1;
|
|
2165
|
+
return `[图片 ${imageIndex}]`;
|
|
2166
|
+
}
|
|
2167
|
+
return "";
|
|
2168
|
+
}).join("");
|
|
2169
|
+
}).filter((paragraph) => paragraph.length > 0).join("\n").trim();
|
|
2170
|
+
const text = [typeof parsed.title === "string" ? parsed.title.trim() : "", body].filter((part) => part.length > 0).join("\n");
|
|
2171
|
+
if (!text) throw new Error("post content has no text");
|
|
2172
|
+
return text;
|
|
2173
|
+
},
|
|
2174
|
+
catch: (error) => new InvalidFeishuMessageContent(error instanceof Error ? error.message : "invalid JSON content")
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
//#endregion
|
|
2178
|
+
//#region src/application/feishu/feishu-message-intake.ts
|
|
2179
|
+
function readTenantKey(payload) {
|
|
2180
|
+
return payload.event.sender?.tenant_key ?? payload.header?.tenant_key ?? "";
|
|
2181
|
+
}
|
|
2182
|
+
/**
|
|
2183
|
+
* Classifies `/new` and `/reset` without advancing the persisted session.
|
|
2184
|
+
* The receive-path control lane uses this before the stateful intake parser so
|
|
2185
|
+
* a reset-and-prompt message is never applied twice when it falls through to
|
|
2186
|
+
* the durable prompt queue.
|
|
2187
|
+
*/
|
|
2188
|
+
function readFeishuSessionResetDirective(payload, options) {
|
|
2189
|
+
return Effect.gen(function* () {
|
|
2190
|
+
const text = yield* readNormalizedMessageText(payload, options);
|
|
2191
|
+
const reset = parseNewSessionCommand(payload.event.message.message_id, text);
|
|
2192
|
+
if (!reset) return void 0;
|
|
2193
|
+
const sessionReference = toSessionReference(payload, options);
|
|
2194
|
+
const baseSessionKey = yield* createFeishuSessionKey(sessionReference);
|
|
2195
|
+
return {
|
|
2196
|
+
baseSessionKey,
|
|
2197
|
+
previousSessionKey: yield* resolveCurrentSessionKey(baseSessionKey, options.sessionStore),
|
|
2198
|
+
prompt: reset.prompt,
|
|
2199
|
+
sessionReference
|
|
2200
|
+
};
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2068
2203
|
function createAgentCommandFromFeishuMessage(payload, options) {
|
|
2069
2204
|
return Effect.gen(function* () {
|
|
2070
2205
|
const message = payload.event.message;
|
|
2071
|
-
const normalizedText =
|
|
2206
|
+
const normalizedText = yield* readNormalizedMessageText(payload, options);
|
|
2072
2207
|
const sessionReference = toSessionReference(payload, options);
|
|
2073
2208
|
const baseSessionKey = yield* createFeishuSessionKey(sessionReference);
|
|
2074
2209
|
const reset = parseNewSessionCommand(message.message_id, normalizedText);
|
|
@@ -2108,9 +2243,19 @@ function createAgentCommandFromFeishuMessage(payload, options) {
|
|
|
2108
2243
|
};
|
|
2109
2244
|
});
|
|
2110
2245
|
}
|
|
2246
|
+
function readNormalizedMessageText(payload, options) {
|
|
2247
|
+
const message = payload.event.message;
|
|
2248
|
+
return Effect.gen(function* () {
|
|
2249
|
+
return normalizeTrustedBotMention(yield* readFeishuMessageContent({
|
|
2250
|
+
content: message.content,
|
|
2251
|
+
messageType: message.message_type
|
|
2252
|
+
}), message.mentions, options.botOpenId);
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2111
2255
|
function normalizeTrustedBotMention(text, mentions, botOpenId) {
|
|
2112
2256
|
if (!botOpenId || !mentions) return text.trim();
|
|
2113
|
-
|
|
2257
|
+
const tokens = mentions.filter((mention) => mention.id?.open_id === botOpenId && mention.key).flatMap((mention) => [mention.key, ...mention.name ? [`@${mention.name}`] : []]);
|
|
2258
|
+
return [...new Set(tokens)].reduce((current, token) => current.replaceAll(token, ""), text).trim();
|
|
2114
2259
|
}
|
|
2115
2260
|
function validateSkillCommand(text) {
|
|
2116
2261
|
if (!/^\/skill(?::|\s|$)/.test(text)) return Effect.void;
|
|
@@ -2139,7 +2284,7 @@ function describeFeishuMessageIntake(payload, options) {
|
|
|
2139
2284
|
commandType: "cancel_run",
|
|
2140
2285
|
messageId: command.messageId,
|
|
2141
2286
|
reason: command.reason,
|
|
2142
|
-
runId: command.runId,
|
|
2287
|
+
...command.runId ? { runId: command.runId } : {},
|
|
2143
2288
|
sessionKey: command.sessionKey ?? (yield* createFeishuSessionKey(sessionReference)),
|
|
2144
2289
|
sessionReference
|
|
2145
2290
|
};
|
|
@@ -2167,67 +2312,94 @@ function toSessionReference(payload, options) {
|
|
|
2167
2312
|
...payload.event.message.thread_id ? { threadId: payload.event.message.thread_id } : {}
|
|
2168
2313
|
};
|
|
2169
2314
|
}
|
|
2170
|
-
function parseTextContent(content) {
|
|
2171
|
-
return Effect.try({
|
|
2172
|
-
try: () => {
|
|
2173
|
-
const parsed = JSON.parse(content);
|
|
2174
|
-
if (typeof parsed.text !== "string") throw new Error("text content is missing");
|
|
2175
|
-
return parsed.text;
|
|
2176
|
-
},
|
|
2177
|
-
catch: (error) => new InvalidFeishuMessageContent(error instanceof Error ? error.message : "invalid JSON content")
|
|
2178
|
-
});
|
|
2179
|
-
}
|
|
2180
|
-
function parsePostContent(content) {
|
|
2181
|
-
return Effect.try({
|
|
2182
|
-
try: () => {
|
|
2183
|
-
const parsed = JSON.parse(content);
|
|
2184
|
-
const paragraphs = Array.isArray(parsed.content_v2) ? parsed.content_v2 : parsed.content;
|
|
2185
|
-
if (!Array.isArray(paragraphs)) throw new Error("post content is missing");
|
|
2186
|
-
const body = paragraphs.map((paragraph) => {
|
|
2187
|
-
if (!Array.isArray(paragraph)) throw new Error("post paragraph is invalid");
|
|
2188
|
-
return paragraph.map((element) => {
|
|
2189
|
-
if (element === null || typeof element !== "object") return "";
|
|
2190
|
-
if ("text" in element && typeof element.text === "string") return element.text;
|
|
2191
|
-
if ("user_name" in element && typeof element.user_name === "string") return `@${element.user_name}`;
|
|
2192
|
-
return "";
|
|
2193
|
-
}).join("");
|
|
2194
|
-
}).join("\n").trim();
|
|
2195
|
-
const text = [typeof parsed.title === "string" ? parsed.title.trim() : "", body].filter((part) => part.length > 0).join("\n");
|
|
2196
|
-
if (!text) throw new Error("post content has no text");
|
|
2197
|
-
return text;
|
|
2198
|
-
},
|
|
2199
|
-
catch: (error) => new InvalidFeishuMessageContent(error instanceof Error ? error.message : "invalid JSON content")
|
|
2200
|
-
});
|
|
2201
|
-
}
|
|
2202
2315
|
function parseCancelRunCommand(messageId, text) {
|
|
2203
2316
|
const trimmed = text.trim();
|
|
2204
2317
|
if (!/^\/cancel(?:\s|$)/.test(trimmed)) return Effect.succeed(void 0);
|
|
2205
|
-
const match = /^\/cancel
|
|
2206
|
-
if (!match) return Effect.fail(new InvalidFeishuMessageContent("cancel command
|
|
2318
|
+
const match = /^\/cancel(?:\s+(\S+))?\s*$/.exec(trimmed);
|
|
2319
|
+
if (!match) return Effect.fail(new InvalidFeishuMessageContent("cancel command accepts at most one run id"));
|
|
2207
2320
|
return Effect.succeed({
|
|
2208
2321
|
messageId,
|
|
2209
2322
|
reason: "Feishu cancel command",
|
|
2210
|
-
runId: match[1],
|
|
2323
|
+
...match[1] ? { runId: match[1] } : {},
|
|
2211
2324
|
type: "cancel_run"
|
|
2212
2325
|
});
|
|
2213
2326
|
}
|
|
2214
2327
|
//#endregion
|
|
2215
2328
|
//#region src/application/feishu/feishu-agent-daemon.ts
|
|
2329
|
+
const STEERING_ACK_TEXT = "已收到这条消息,已加入当前任务的优先处理;不会新开任务,回答会继续写入当前任务卡。";
|
|
2330
|
+
const CANCELLED_ACK_TEXT = "已停止当前任务。";
|
|
2331
|
+
const NO_ACTIVE_RUN_ACK_TEXT = "当前没有正在运行的任务。";
|
|
2216
2332
|
function createFeishuAgentDaemon(options) {
|
|
2217
2333
|
const sessionStore = options.sessionStore ?? createFeishuSessionStore();
|
|
2218
2334
|
const execution = options.execution ?? {
|
|
2335
|
+
cancelActiveRun: (input) => Effect.gen(function* () {
|
|
2336
|
+
const session = options.harness.forSession(input.sessionKey);
|
|
2337
|
+
const active = session.getActiveRun();
|
|
2338
|
+
if (!active) return void 0;
|
|
2339
|
+
return (yield* session.cancelRun(active.runId, input.reason)) ? active.runId : void 0;
|
|
2340
|
+
}),
|
|
2219
2341
|
cancelRun: (input) => input.sessionKey === void 0 ? options.harness.cancelRun(input.runId, input.reason) : options.harness.forSession(input.sessionKey).cancelRun(input.runId, input.reason),
|
|
2342
|
+
steerRun: (input) => Effect.sync(() => typeof options.harness.forSession === "function" ? options.harness.forSession(input.sessionKey).getActiveRun() : void 0).pipe(Effect.flatMap((active) => active ? options.harness.forSession(input.sessionKey).steerRun(active.runId, input.text).pipe(Effect.map((steered) => steered ? {
|
|
2343
|
+
runId: active.runId,
|
|
2344
|
+
steered: true
|
|
2345
|
+
} : void 0)) : Effect.succeed(void 0))),
|
|
2220
2346
|
promptWithUpdates: (command, onUpdate) => options.harness.promptWithUpdates(command, onUpdate)
|
|
2221
2347
|
};
|
|
2222
2348
|
const seenCardActionTokens = /* @__PURE__ */ new Set();
|
|
2223
2349
|
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
2224
2350
|
const cancelRun = (command) => Effect.gen(function* () {
|
|
2225
|
-
const
|
|
2351
|
+
const activeRunId = command.runId ? void 0 : command.sessionKey ? yield* execution.cancelActiveRun({
|
|
2352
|
+
reason: command.reason,
|
|
2353
|
+
sessionKey: command.sessionKey
|
|
2354
|
+
}) : void 0;
|
|
2355
|
+
const cancelled = command.runId ? yield* execution.cancelRun({
|
|
2356
|
+
...command,
|
|
2357
|
+
runId: command.runId
|
|
2358
|
+
}) : !!activeRunId;
|
|
2359
|
+
const runId = command.runId ?? activeRunId;
|
|
2226
2360
|
return {
|
|
2227
2361
|
cancelled,
|
|
2228
2362
|
messageId: command.messageId,
|
|
2229
2363
|
...cancelled ? {} : { reason: "not_active" },
|
|
2230
|
-
runId:
|
|
2364
|
+
...runId ? { runId } : {}
|
|
2365
|
+
};
|
|
2366
|
+
});
|
|
2367
|
+
const replyToCancellation = (messageId, cancelled) => options.reply ? options.reply(messageId, cancelled ? CANCELLED_ACK_TEXT : NO_ACTIVE_RUN_ACK_TEXT) : Effect.void;
|
|
2368
|
+
const duplicateResult = (messageId) => options.dedupe && seenMessageIds.has(messageId) ? {
|
|
2369
|
+
messageId,
|
|
2370
|
+
reason: "duplicate",
|
|
2371
|
+
skipped: true
|
|
2372
|
+
} : void 0;
|
|
2373
|
+
const markSeen = (messageId) => {
|
|
2374
|
+
if (options.dedupe) seenMessageIds.add(messageId);
|
|
2375
|
+
};
|
|
2376
|
+
const resetSession = (inbound, replyEnabled) => Effect.gen(function* () {
|
|
2377
|
+
const cancelledRunId = yield* execution.cancelActiveRun({
|
|
2378
|
+
reason: "Feishu new session command",
|
|
2379
|
+
sessionKey: inbound.previousSessionKey
|
|
2380
|
+
});
|
|
2381
|
+
if (replyEnabled && options.reply) yield* options.reply(inbound.messageId, cancelledRunId ? "已停止当前任务并开启新会话。请发送下一条指令。" : "已开启新会话。请发送下一条指令。");
|
|
2382
|
+
return {
|
|
2383
|
+
messageId: inbound.messageId,
|
|
2384
|
+
previousSessionKey: inbound.previousSessionKey,
|
|
2385
|
+
reset: true,
|
|
2386
|
+
sessionKey: inbound.sessionKey
|
|
2387
|
+
};
|
|
2388
|
+
});
|
|
2389
|
+
const steerPrompt = (inbound, replyEnabled) => Effect.gen(function* () {
|
|
2390
|
+
const steered = yield* execution.steerRun({
|
|
2391
|
+
sessionKey: inbound.command.sessionKey,
|
|
2392
|
+
text: inbound.command.text
|
|
2393
|
+
});
|
|
2394
|
+
if (!steered) return void 0;
|
|
2395
|
+
if (replyEnabled) {
|
|
2396
|
+
if (options.acknowledgeSteering) yield* options.acknowledgeSteering(inbound.messageId).pipe(Effect.catchAll(() => options.reply ? options.reply(inbound.messageId, STEERING_ACK_TEXT) : Effect.void));
|
|
2397
|
+
else if (options.reply) yield* options.reply(inbound.messageId, STEERING_ACK_TEXT);
|
|
2398
|
+
}
|
|
2399
|
+
return {
|
|
2400
|
+
messageId: inbound.messageId,
|
|
2401
|
+
runId: steered.runId,
|
|
2402
|
+
steered: true
|
|
2231
2403
|
};
|
|
2232
2404
|
});
|
|
2233
2405
|
return {
|
|
@@ -2242,6 +2414,43 @@ function createFeishuAgentDaemon(options) {
|
|
|
2242
2414
|
if (inbound.type === "cancel_run" && inbound.token) seenCardActionTokens.add(inbound.token);
|
|
2243
2415
|
return result;
|
|
2244
2416
|
}),
|
|
2417
|
+
trySteerMessage: (payload) => Effect.gen(function* () {
|
|
2418
|
+
const resetDirective = yield* readFeishuSessionResetDirective(payload, {
|
|
2419
|
+
agentId: options.agentId,
|
|
2420
|
+
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
2421
|
+
sessionStore
|
|
2422
|
+
}).pipe(Effect.either);
|
|
2423
|
+
if (Either.isRight(resetDirective) && resetDirective.right?.prompt) {
|
|
2424
|
+
yield* execution.cancelActiveRun({
|
|
2425
|
+
reason: "Feishu new session command",
|
|
2426
|
+
sessionKey: resetDirective.right.previousSessionKey
|
|
2427
|
+
});
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
const parsed = yield* createAgentCommandFromFeishuMessage(payload, {
|
|
2431
|
+
agentId: options.agentId,
|
|
2432
|
+
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
2433
|
+
sessionStore
|
|
2434
|
+
}).pipe(Effect.either);
|
|
2435
|
+
if (Either.isLeft(parsed)) return void 0;
|
|
2436
|
+
const inbound = parsed.right;
|
|
2437
|
+
const duplicate = duplicateResult(inbound.messageId);
|
|
2438
|
+
if (duplicate) return duplicate;
|
|
2439
|
+
if (inbound.type === "cancel_run") {
|
|
2440
|
+
const result = yield* cancelRun(inbound);
|
|
2441
|
+
markSeen(inbound.messageId);
|
|
2442
|
+
yield* replyToCancellation(inbound.messageId, result.cancelled);
|
|
2443
|
+
return result;
|
|
2444
|
+
}
|
|
2445
|
+
if (inbound.type === "new_session") {
|
|
2446
|
+
markSeen(inbound.messageId);
|
|
2447
|
+
return yield* resetSession(inbound, true);
|
|
2448
|
+
}
|
|
2449
|
+
const steered = yield* steerPrompt(inbound, true);
|
|
2450
|
+
if (!steered) return void 0;
|
|
2451
|
+
markSeen(inbound.messageId);
|
|
2452
|
+
return steered;
|
|
2453
|
+
}),
|
|
2245
2454
|
handleMessage: (payload, handleOptions) => {
|
|
2246
2455
|
let dedupeMessageId;
|
|
2247
2456
|
const sideEffectsDisabled = handleOptions?.sideEffects === "disabled";
|
|
@@ -2255,29 +2464,29 @@ function createFeishuAgentDaemon(options) {
|
|
|
2255
2464
|
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
2256
2465
|
sessionStore
|
|
2257
2466
|
});
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
reason: "duplicate",
|
|
2261
|
-
skipped: true
|
|
2262
|
-
};
|
|
2467
|
+
const duplicate = duplicateResult(inbound.messageId);
|
|
2468
|
+
if (duplicate) return duplicate;
|
|
2263
2469
|
if (options.dedupe) {
|
|
2264
2470
|
seenMessageIds.add(inbound.messageId);
|
|
2265
2471
|
dedupeMessageId = inbound.messageId;
|
|
2266
2472
|
}
|
|
2267
|
-
if (inbound.type === "cancel_run")
|
|
2268
|
-
|
|
2269
|
-
if (!sideEffectsDisabled
|
|
2270
|
-
return
|
|
2271
|
-
messageId: inbound.messageId,
|
|
2272
|
-
previousSessionKey: inbound.previousSessionKey,
|
|
2273
|
-
reset: true,
|
|
2274
|
-
sessionKey: inbound.sessionKey
|
|
2275
|
-
};
|
|
2473
|
+
if (inbound.type === "cancel_run") {
|
|
2474
|
+
const result = yield* cancelRun(inbound);
|
|
2475
|
+
if (!sideEffectsDisabled) yield* replyToCancellation(inbound.messageId, result.cancelled);
|
|
2476
|
+
return result;
|
|
2276
2477
|
}
|
|
2478
|
+
if (inbound.type === "new_session") return yield* resetSession(inbound, !sideEffectsDisabled);
|
|
2479
|
+
const steered = yield* steerPrompt(inbound, !sideEffectsDisabled);
|
|
2480
|
+
if (steered) return steered;
|
|
2277
2481
|
const projector = createFeishuStreamProjector();
|
|
2278
2482
|
const acceptedRunIds = /* @__PURE__ */ new Set();
|
|
2483
|
+
const promptText = options.promptContext ? yield* options.promptContext.resolve({
|
|
2484
|
+
payload,
|
|
2485
|
+
text: inbound.command.text
|
|
2486
|
+
}) : inbound.command.text;
|
|
2279
2487
|
const command = {
|
|
2280
2488
|
...inbound.command,
|
|
2489
|
+
text: promptText,
|
|
2281
2490
|
...options.resolveInvocation ? { invocation: options.resolveInvocation(payload, inbound.conversationId) } : {}
|
|
2282
2491
|
};
|
|
2283
2492
|
const result = yield* execution.promptWithUpdates(command, (update) => Effect.gen(function* () {
|
|
@@ -2350,6 +2559,32 @@ function createFeishuMessageQueue(options) {
|
|
|
2350
2559
|
pending: repository.pendingCount(),
|
|
2351
2560
|
reason: admission
|
|
2352
2561
|
};
|
|
2562
|
+
if (options.handleControl) {
|
|
2563
|
+
const claimedAt = now();
|
|
2564
|
+
const leaseId = randomUUID();
|
|
2565
|
+
const control = yield* repository.claimById({
|
|
2566
|
+
id: messageId,
|
|
2567
|
+
leaseExpiresAt: new Date(Date.parse(claimedAt) + leaseMs).toISOString(),
|
|
2568
|
+
leaseId,
|
|
2569
|
+
now: claimedAt
|
|
2570
|
+
});
|
|
2571
|
+
if (control) if (yield* options.handleControl(control.payload).pipe(Effect.catchAll((error) => repository.fail({
|
|
2572
|
+
failedAt: now(),
|
|
2573
|
+
id: control.id,
|
|
2574
|
+
leaseId,
|
|
2575
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
2576
|
+
terminal: true
|
|
2577
|
+
}).pipe(Effect.flatMap(() => Effect.fail(error)))))) yield* repository.complete({
|
|
2578
|
+
completedAt: now(),
|
|
2579
|
+
id: control.id,
|
|
2580
|
+
leaseId
|
|
2581
|
+
});
|
|
2582
|
+
else yield* repository.release({
|
|
2583
|
+
availableAt: now(),
|
|
2584
|
+
id: control.id,
|
|
2585
|
+
leaseId
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2353
2588
|
return {
|
|
2354
2589
|
accepted: true,
|
|
2355
2590
|
messageId,
|
|
@@ -2368,6 +2603,22 @@ function createFeishuMessageQueue(options) {
|
|
|
2368
2603
|
handled: false,
|
|
2369
2604
|
reason: "empty"
|
|
2370
2605
|
};
|
|
2606
|
+
if (options.shouldSkipReplay ? yield* options.shouldSkipReplay(message.payload) : false) {
|
|
2607
|
+
yield* repository.complete({
|
|
2608
|
+
completedAt: now(),
|
|
2609
|
+
id: message.id,
|
|
2610
|
+
leaseId
|
|
2611
|
+
});
|
|
2612
|
+
return {
|
|
2613
|
+
handled: true,
|
|
2614
|
+
messageId: message.id,
|
|
2615
|
+
result: {
|
|
2616
|
+
messageId: message.id,
|
|
2617
|
+
reason: "duplicate",
|
|
2618
|
+
skipped: true
|
|
2619
|
+
}
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2371
2622
|
const result = yield* options.handleMessage(message.payload, message.options).pipe(Effect.catchAll((error) => repository.fail({
|
|
2372
2623
|
availableAt: new Date(Date.parse(now()) + retryDelayMs).toISOString(),
|
|
2373
2624
|
failedAt: now(),
|
|
@@ -2790,7 +3041,10 @@ function toast(type, enUs, zhCn) {
|
|
|
2790
3041
|
//#endregion
|
|
2791
3042
|
//#region src/infrastructure/feishu/feishu-event-handlers.ts
|
|
2792
3043
|
function createFeishuEventHandlers(options) {
|
|
2793
|
-
const handlers = { "im.message.receive_v1": (payload) =>
|
|
3044
|
+
const handlers = { "im.message.receive_v1": (payload) => {
|
|
3045
|
+
const normalized = normalizeReceiveMessagePayload(payload);
|
|
3046
|
+
return Effect.runPromise(options.queue.accept(normalized));
|
|
3047
|
+
} };
|
|
2794
3048
|
if (options.cardActions) return {
|
|
2795
3049
|
...handlers,
|
|
2796
3050
|
"card.action.trigger": (payload) => Effect.runPromise(options.cardActions.handleCardAction(payload).pipe(Effect.map(createFeishuCardActionCallbackResponse), Effect.catchAll(() => Effect.succeed(createFeishuCardActionErrorResponse()))))
|
|
@@ -2821,23 +3075,24 @@ function createFeishuAgentRuntime(options) {
|
|
|
2821
3075
|
runIds: options.runIds
|
|
2822
3076
|
});
|
|
2823
3077
|
const daemon = createFeishuAgentDaemon({
|
|
3078
|
+
...options.acknowledgeSteering ? { acknowledgeSteering: options.acknowledgeSteering } : {},
|
|
2824
3079
|
agentId: options.agentId,
|
|
2825
3080
|
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
3081
|
+
dedupe: true,
|
|
2826
3082
|
...options.finalizeRun ? { finalizeRun: options.finalizeRun } : {},
|
|
2827
3083
|
harness,
|
|
2828
3084
|
...options.prepareRun ? { prepareRun: options.prepareRun } : {},
|
|
3085
|
+
...options.promptContext ? { promptContext: options.promptContext } : {},
|
|
2829
3086
|
publish: options.publish,
|
|
3087
|
+
...options.reply ? { reply: options.reply } : {},
|
|
2830
3088
|
sessionStore
|
|
2831
3089
|
});
|
|
2832
3090
|
let lastAccepted;
|
|
2833
3091
|
let lastHandled;
|
|
2834
3092
|
const queue = createFeishuMessageQueue({
|
|
3093
|
+
handleControl: (payload) => daemon.trySteerMessage(payload),
|
|
2835
3094
|
handleMessage: (payload, handleOptions) => {
|
|
2836
|
-
const effect = daemon.handleMessage(payload, handleOptions).pipe(Effect.tap((result) =>
|
|
2837
|
-
agentId: options.agentId,
|
|
2838
|
-
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
2839
|
-
sessionStore
|
|
2840
|
-
}).pipe(Effect.tap((intake) => Effect.gen(function* () {
|
|
3095
|
+
const effect = daemon.handleMessage(payload, handleOptions).pipe(Effect.tap((result) => describeHandledFeishuMessage(payload, result, harness, options.agentId, options.botOpenId, sessionStore).pipe(Effect.tap((intake) => Effect.gen(function* () {
|
|
2841
3096
|
const observedAt = yield* options.clock.now;
|
|
2842
3097
|
lastHandled = {
|
|
2843
3098
|
intake,
|
|
@@ -2849,6 +3104,11 @@ function createFeishuAgentRuntime(options) {
|
|
|
2849
3104
|
return options.periodicFlush ? options.periodicFlush.withPeriodicFlush(effect) : effect;
|
|
2850
3105
|
},
|
|
2851
3106
|
...options.inboxRepository ? { repository: options.inboxRepository } : {},
|
|
3107
|
+
...options.initialEvents ? { shouldSkipReplay: createInterruptedRunReplayGuard(options.initialEvents, {
|
|
3108
|
+
agentId: options.agentId,
|
|
3109
|
+
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
3110
|
+
sessionStore
|
|
3111
|
+
}) } : {},
|
|
2852
3112
|
shouldRetryError: isRetryableFeishuMessageError
|
|
2853
3113
|
});
|
|
2854
3114
|
const worker = createFeishuMessageWorker({ queue });
|
|
@@ -2865,7 +3125,10 @@ function createFeishuAgentRuntime(options) {
|
|
|
2865
3125
|
drainOne: () => queue.drainOne(),
|
|
2866
3126
|
handlers: createFeishuEventHandlers({
|
|
2867
3127
|
cardActions: daemon,
|
|
2868
|
-
queue:
|
|
3128
|
+
queue: {
|
|
3129
|
+
...observedQueue,
|
|
3130
|
+
pending: () => queue.pending()
|
|
3131
|
+
}
|
|
2869
3132
|
}),
|
|
2870
3133
|
harness,
|
|
2871
3134
|
pending: () => queue.pending(),
|
|
@@ -2875,21 +3138,75 @@ function createFeishuAgentRuntime(options) {
|
|
|
2875
3138
|
...lastHandled ? { lastHandled } : {}
|
|
2876
3139
|
}),
|
|
2877
3140
|
replayReceiveMessage: (payload, replayOptions) => Effect.gen(function* () {
|
|
2878
|
-
const
|
|
3141
|
+
const intakeOptions = {
|
|
2879
3142
|
agentId: options.agentId,
|
|
2880
|
-
...options.botOpenId ? { botOpenId: options.botOpenId } : {}
|
|
2881
|
-
|
|
3143
|
+
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
3144
|
+
sessionStore
|
|
3145
|
+
};
|
|
3146
|
+
const reset = yield* readFeishuSessionResetDirective(payload, intakeOptions);
|
|
3147
|
+
const initialIntake = reset ? void 0 : yield* describeFeishuMessageIntake(payload, intakeOptions);
|
|
3148
|
+
const accepted = yield* observedQueue.accept(payload, replayOptions);
|
|
3149
|
+
const drained = yield* worker.drainAvailable();
|
|
3150
|
+
const handledIntake = lastHandled?.messageId === payload.event.message.message_id ? lastHandled.intake : void 0;
|
|
3151
|
+
let intake;
|
|
3152
|
+
if (handledIntake) intake = handledIntake;
|
|
3153
|
+
else if (reset) {
|
|
3154
|
+
const sessionKey = yield* sessionStore.current(reset.baseSessionKey);
|
|
3155
|
+
intake = reset.prompt ? {
|
|
3156
|
+
commandType: "prompt",
|
|
3157
|
+
messageId: payload.event.message.message_id,
|
|
3158
|
+
sessionKey,
|
|
3159
|
+
sessionReference: reset.sessionReference,
|
|
3160
|
+
text: reset.prompt
|
|
3161
|
+
} : {
|
|
3162
|
+
commandType: "new_session",
|
|
3163
|
+
messageId: payload.event.message.message_id,
|
|
3164
|
+
previousSessionKey: reset.previousSessionKey,
|
|
3165
|
+
sessionKey,
|
|
3166
|
+
sessionReference: reset.sessionReference
|
|
3167
|
+
};
|
|
3168
|
+
} else intake = initialIntake;
|
|
2882
3169
|
return {
|
|
2883
|
-
accepted
|
|
2884
|
-
drained
|
|
3170
|
+
accepted,
|
|
3171
|
+
drained,
|
|
2885
3172
|
intake
|
|
2886
3173
|
};
|
|
2887
3174
|
}),
|
|
2888
3175
|
worker
|
|
2889
3176
|
};
|
|
2890
3177
|
}
|
|
3178
|
+
function describeHandledFeishuMessage(payload, result, harness, agentId, botOpenId, sessionStore) {
|
|
3179
|
+
const sessionReference = {
|
|
3180
|
+
agentId,
|
|
3181
|
+
chatId: payload.event.message.chat_id,
|
|
3182
|
+
tenantKey: readTenantKey(payload),
|
|
3183
|
+
...payload.event.message.thread_id ? { threadId: payload.event.message.thread_id } : {}
|
|
3184
|
+
};
|
|
3185
|
+
if ("finalText" in result) {
|
|
3186
|
+
const state = harness.getRunState(result.runId);
|
|
3187
|
+
if (state?.sessionKey && state.prompt !== void 0) return Effect.succeed({
|
|
3188
|
+
commandType: "prompt",
|
|
3189
|
+
messageId: result.messageId,
|
|
3190
|
+
sessionKey: state.sessionKey,
|
|
3191
|
+
sessionReference,
|
|
3192
|
+
text: state.prompt
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
if ("reset" in result) return Effect.succeed({
|
|
3196
|
+
commandType: "new_session",
|
|
3197
|
+
messageId: result.messageId,
|
|
3198
|
+
previousSessionKey: result.previousSessionKey,
|
|
3199
|
+
sessionKey: result.sessionKey,
|
|
3200
|
+
sessionReference
|
|
3201
|
+
});
|
|
3202
|
+
return describeFeishuMessageIntake(payload, {
|
|
3203
|
+
agentId,
|
|
3204
|
+
...botOpenId ? { botOpenId } : {},
|
|
3205
|
+
sessionStore
|
|
3206
|
+
});
|
|
3207
|
+
}
|
|
2891
3208
|
function isRetryableFeishuMessageError(error) {
|
|
2892
|
-
return !(error instanceof AgentRunCancelled || error instanceof UnsupportedFeishuMessage || error instanceof InvalidFeishuMessageContent || error instanceof InvalidFeishuSessionReference);
|
|
3209
|
+
return !(error instanceof AgentEventHandlerFailed || error instanceof AgentEventSinkFailed || error instanceof AgentLoopFailed || error instanceof AgentRunCancelled || error instanceof UnsupportedFeishuMessage || error instanceof InvalidFeishuMessageContent || error instanceof InvalidFeishuSessionReference);
|
|
2893
3210
|
}
|
|
2894
3211
|
function summarizeReceiveMessage(payload) {
|
|
2895
3212
|
const tenantKey = readTenantKey(payload);
|
|
@@ -2900,21 +3217,47 @@ function summarizeReceiveMessage(payload) {
|
|
|
2900
3217
|
...payload.event.message.thread_id ? { threadId: payload.event.message.thread_id } : {}
|
|
2901
3218
|
};
|
|
2902
3219
|
}
|
|
3220
|
+
function createInterruptedRunReplayGuard(events, intakeOptions, sessionNamespace) {
|
|
3221
|
+
const accepted = /* @__PURE__ */ new Map();
|
|
3222
|
+
const interrupted = [];
|
|
3223
|
+
for (const event of events) {
|
|
3224
|
+
if (event.type === "agent_run_accepted") {
|
|
3225
|
+
accepted.set(event.runId, {
|
|
3226
|
+
acceptedAt: event.occurredAt,
|
|
3227
|
+
prompt: event.prompt,
|
|
3228
|
+
sessionKey: event.sessionKey
|
|
3229
|
+
});
|
|
3230
|
+
continue;
|
|
3231
|
+
}
|
|
3232
|
+
if (event.type !== "agent_run_cancelled") continue;
|
|
3233
|
+
const run = accepted.get(event.runId);
|
|
3234
|
+
if (!run) continue;
|
|
3235
|
+
interrupted.push({
|
|
3236
|
+
...run,
|
|
3237
|
+
cancelledAt: event.occurredAt
|
|
3238
|
+
});
|
|
3239
|
+
}
|
|
3240
|
+
return (payload) => describeFeishuMessageIntake(payload, intakeOptions).pipe(Effect.map((intake) => {
|
|
3241
|
+
if (intake.commandType !== "prompt") return false;
|
|
3242
|
+
const createdAt = readFeishuMessageCreatedAt(payload);
|
|
3243
|
+
if (!createdAt) return false;
|
|
3244
|
+
return interrupted.some((run) => (run.sessionKey === intake.sessionKey || sessionNamespace !== void 0 && run.sessionKey === `${sessionNamespace}:${intake.sessionKey}`) && run.prompt === intake.text && createdAt.getTime() <= run.cancelledAt.getTime() && createdAt.getTime() >= run.acceptedAt.getTime() - 5 * 6e4);
|
|
3245
|
+
}));
|
|
3246
|
+
}
|
|
3247
|
+
function readFeishuMessageCreatedAt(payload) {
|
|
3248
|
+
const raw = payload.event.message.create_time;
|
|
3249
|
+
if (raw === void 0) return void 0;
|
|
3250
|
+
const milliseconds = Number(raw);
|
|
3251
|
+
if (!Number.isFinite(milliseconds) || milliseconds <= 0) return void 0;
|
|
3252
|
+
const createdAt = new Date(milliseconds);
|
|
3253
|
+
return Number.isNaN(createdAt.getTime()) ? void 0 : createdAt;
|
|
3254
|
+
}
|
|
2903
3255
|
//#endregion
|
|
2904
3256
|
//#region src/application/agent/agent-history.ts
|
|
2905
3257
|
function restoreAgentHistory(eventLog) {
|
|
2906
3258
|
return eventLog.readAll().pipe(Effect.map(replayAgentHistory));
|
|
2907
3259
|
}
|
|
2908
3260
|
//#endregion
|
|
2909
|
-
//#region src/infrastructure/feishu/feishu-periodic-flush.ts
|
|
2910
|
-
function createFeishuPeriodicFlush(options) {
|
|
2911
|
-
const loop = Effect.forever(Effect.gen(function* () {
|
|
2912
|
-
yield* options.sleep(options.intervalMs);
|
|
2913
|
-
yield* options.flush();
|
|
2914
|
-
}));
|
|
2915
|
-
return { withPeriodicFlush: (effect) => Effect.raceFirst(effect, loop) };
|
|
2916
|
-
}
|
|
2917
|
-
//#endregion
|
|
2918
3261
|
//#region src/infrastructure/feishu/feishu-websocket-daemon.ts
|
|
2919
3262
|
function createLazyFeishuWebSocketEventDispatcher(create) {
|
|
2920
3263
|
let dispatcher;
|
|
@@ -2988,7 +3331,7 @@ function createFeishuAgentRunCard(input) {
|
|
|
2988
3331
|
content: `_${presentation.note}_`,
|
|
2989
3332
|
tag: "markdown"
|
|
2990
3333
|
},
|
|
2991
|
-
...input.status === "running" ? [{
|
|
3334
|
+
...input.status === "running" && input.cancelButton !== false ? [{
|
|
2992
3335
|
behaviors: [{
|
|
2993
3336
|
type: "callback",
|
|
2994
3337
|
value: {
|
|
@@ -3023,15 +3366,15 @@ function resolveRunPresentation(input) {
|
|
|
3023
3366
|
switch (input.status) {
|
|
3024
3367
|
case "running": return {
|
|
3025
3368
|
content: input.text || "正在思考…",
|
|
3026
|
-
note: input.generation ? "接续上一条消息,回答将实时更新" : "回答将实时更新",
|
|
3369
|
+
note: input.cancelButton === false ? `回答将实时更新;如需停止,请发送 \`/cancel ${input.runId}\`` : input.generation ? "接续上一条消息,回答将实时更新" : "回答将实时更新",
|
|
3027
3370
|
template: "blue",
|
|
3028
3371
|
title: input.generation ? "正在处理(续)" : "正在处理"
|
|
3029
3372
|
};
|
|
3030
3373
|
case "handoff": return {
|
|
3031
|
-
content: "
|
|
3032
|
-
note: "
|
|
3374
|
+
content: "这是同一任务的续卡。任务仍在后台处理,进度将继续显示在下一条消息。",
|
|
3375
|
+
note: "本卡片已停止更新;请查看下一条消息,不会创建第二个任务",
|
|
3033
3376
|
template: "blue",
|
|
3034
|
-
title: "
|
|
3377
|
+
title: "同一任务已续卡"
|
|
3035
3378
|
};
|
|
3036
3379
|
case "completed": return {
|
|
3037
3380
|
content: input.text || "已完成。",
|
|
@@ -3339,6 +3682,7 @@ function createFeishuCardTargetPreparation(options) {
|
|
|
3339
3682
|
function createConfiguredFeishuCardKitTargetCreator(options) {
|
|
3340
3683
|
return createFeishuCardKitOpenApiTargetCreator({
|
|
3341
3684
|
baseUrl: options.config.feishu.baseUrl,
|
|
3685
|
+
...options.cancelButton === void 0 ? {} : { cancelButton: options.cancelButton },
|
|
3342
3686
|
client: options.client,
|
|
3343
3687
|
elementId: options.elementId ?? DEFAULT_ELEMENT_ID,
|
|
3344
3688
|
...options.initialContent === void 0 ? {} : { initialContent: options.initialContent },
|
|
@@ -3359,6 +3703,7 @@ function createFeishuCardKitOpenApiTargetCreator(options) {
|
|
|
3359
3703
|
runId: run.runId,
|
|
3360
3704
|
sessionKey: run.sessionKey,
|
|
3361
3705
|
status: "running",
|
|
3706
|
+
...options.cancelButton === void 0 ? {} : { cancelButton: options.cancelButton },
|
|
3362
3707
|
text: createOptions?.initialContent ?? initialContent
|
|
3363
3708
|
})),
|
|
3364
3709
|
type: "card_json"
|
|
@@ -3385,10 +3730,10 @@ function createFeishuCardKitOpenApiTargetCreator(options) {
|
|
|
3385
3730
|
}
|
|
3386
3731
|
function readStringData(response, field) {
|
|
3387
3732
|
const data = response.body.data;
|
|
3388
|
-
if (!isRecord$
|
|
3733
|
+
if (!isRecord$3(data) || typeof data[field] !== "string" || data[field].length === 0) return Effect.fail(new FeishuOpenApiError(response.status, response.body.code, `Feishu OpenAPI response missing data.${field}`));
|
|
3389
3734
|
return Effect.succeed(data[field]);
|
|
3390
3735
|
}
|
|
3391
|
-
function isRecord$
|
|
3736
|
+
function isRecord$3(value) {
|
|
3392
3737
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3393
3738
|
}
|
|
3394
3739
|
//#endregion
|
|
@@ -3408,6 +3753,7 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
|
|
|
3408
3753
|
sleep: options.sleep
|
|
3409
3754
|
});
|
|
3410
3755
|
const creator = createConfiguredFeishuCardKitTargetCreator({
|
|
3756
|
+
...options.cancelButton === void 0 ? {} : { cancelButton: options.cancelButton },
|
|
3411
3757
|
client: options.client,
|
|
3412
3758
|
config: options.config,
|
|
3413
3759
|
...options.elementId === void 0 ? {} : { elementId: options.elementId },
|
|
@@ -3433,6 +3779,12 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
|
|
|
3433
3779
|
rollover: cardRollover,
|
|
3434
3780
|
sleep: options.sleep
|
|
3435
3781
|
});
|
|
3782
|
+
const streaming = createPeriodicEffectLoop({
|
|
3783
|
+
intervalMs: options.flushIntervalMs ?? options.config.feishu.streamMinIntervalMs,
|
|
3784
|
+
...options.onError ? { onError: options.onError } : {},
|
|
3785
|
+
run: () => publisher.flush(),
|
|
3786
|
+
sleep: options.sleep
|
|
3787
|
+
});
|
|
3436
3788
|
return {
|
|
3437
3789
|
prepareRun: createFeishuCardTargetPreparation({
|
|
3438
3790
|
createTarget: (run) => creator.createTarget(run),
|
|
@@ -3441,15 +3793,20 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
|
|
|
3441
3793
|
rollover: cardRollover,
|
|
3442
3794
|
supervisor,
|
|
3443
3795
|
transport: {
|
|
3444
|
-
running: () => supervisor.running(),
|
|
3796
|
+
running: () => supervisor.running() && streaming.running(),
|
|
3445
3797
|
start: async () => {
|
|
3446
3798
|
await Effect.runPromise(supervisor.recover().pipe(Effect.catchAll((error) => {
|
|
3447
3799
|
const reported = options.onError?.(error);
|
|
3448
3800
|
return reported instanceof Promise ? Effect.promise(() => reported) : Effect.void;
|
|
3449
3801
|
})));
|
|
3450
3802
|
supervisor.start();
|
|
3803
|
+
streaming.start();
|
|
3451
3804
|
},
|
|
3452
|
-
stop: () =>
|
|
3805
|
+
stop: async () => {
|
|
3806
|
+
const errors = (await Promise.allSettled([streaming.stop(), supervisor.stop()])).filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
3807
|
+
if (errors.length === 1) throw errors[0];
|
|
3808
|
+
if (errors.length > 1) throw new AggregateError(errors, "Feishu card runtime shutdown failed");
|
|
3809
|
+
}
|
|
3453
3810
|
}
|
|
3454
3811
|
};
|
|
3455
3812
|
}
|
|
@@ -3457,9 +3814,74 @@ function resolveSupervisorInterval(leaseMs) {
|
|
|
3457
3814
|
return Math.max(MIN_SUPERVISOR_INTERVAL_MS, Math.min(MAX_SUPERVISOR_INTERVAL_MS, Math.floor(leaseMs / 10)));
|
|
3458
3815
|
}
|
|
3459
3816
|
//#endregion
|
|
3817
|
+
//#region src/infrastructure/feishu/feishu-text-reply.ts
|
|
3818
|
+
function createConfiguredFeishuTextReplySender(options) {
|
|
3819
|
+
const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
|
|
3820
|
+
return { reply: (messageId, text) => Effect.gen(function* () {
|
|
3821
|
+
yield* options.client.request({
|
|
3822
|
+
body: {
|
|
3823
|
+
content: JSON.stringify({ text }),
|
|
3824
|
+
msg_type: "text"
|
|
3825
|
+
},
|
|
3826
|
+
method: "POST",
|
|
3827
|
+
url: `${baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}/reply`
|
|
3828
|
+
}, "Feishu reply failed");
|
|
3829
|
+
}) };
|
|
3830
|
+
}
|
|
3831
|
+
//#endregion
|
|
3832
|
+
//#region src/infrastructure/feishu/feishu-message-reaction.ts
|
|
3833
|
+
function createConfiguredFeishuMessageReactionSender(options) {
|
|
3834
|
+
const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
|
|
3835
|
+
return { add: (messageId, emojiType = "OK") => options.client.request({
|
|
3836
|
+
body: { reaction_type: { emoji_type: emojiType } },
|
|
3837
|
+
method: "POST",
|
|
3838
|
+
url: `${baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}/reactions`
|
|
3839
|
+
}, "Feishu message reaction failed").pipe(Effect.asVoid) };
|
|
3840
|
+
}
|
|
3841
|
+
//#endregion
|
|
3842
|
+
//#region src/application/feishu/feishu-prompt-context.ts
|
|
3843
|
+
function composeFeishuTopicPrompt(currentText, rootText) {
|
|
3844
|
+
return `${currentText.trim()}\n\n<feishu_topic_context>\n话题原消息:\n${rootText.trim()}\n</feishu_topic_context>`;
|
|
3845
|
+
}
|
|
3846
|
+
//#endregion
|
|
3847
|
+
//#region src/infrastructure/feishu/feishu-topic-context-resolver.ts
|
|
3848
|
+
var InvalidFeishuTopicContext = class extends Error {
|
|
3849
|
+
name = "InvalidFeishuTopicContext";
|
|
3850
|
+
};
|
|
3851
|
+
function createFeishuTopicContextResolver(options) {
|
|
3852
|
+
const baseUrl = (options.baseUrl ?? "https://open.feishu.cn").replace(/\/+$/u, "");
|
|
3853
|
+
return { resolve: ({ payload, text }) => {
|
|
3854
|
+
const message = payload.event.message;
|
|
3855
|
+
const rootId = message.root_id;
|
|
3856
|
+
if (!message.thread_id || !rootId || rootId === message.message_id) return Effect.succeed(text);
|
|
3857
|
+
return Effect.gen(function* () {
|
|
3858
|
+
const root = readRootMessage(yield* options.client.request({
|
|
3859
|
+
method: "GET",
|
|
3860
|
+
url: `${baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(rootId)}`
|
|
3861
|
+
}, "Failed to read Feishu topic root message"), rootId);
|
|
3862
|
+
return composeFeishuTopicPrompt(text, yield* readFeishuMessageContent({
|
|
3863
|
+
content: root.content,
|
|
3864
|
+
messageType: root.messageType
|
|
3865
|
+
}));
|
|
3866
|
+
});
|
|
3867
|
+
} };
|
|
3868
|
+
}
|
|
3869
|
+
function readRootMessage(response, rootId) {
|
|
3870
|
+
const data = response.body.data;
|
|
3871
|
+
if (!isRecord$2(data) || !Array.isArray(data.items)) throw new InvalidFeishuTopicContext("Feishu topic root response is missing data.items");
|
|
3872
|
+
const item = data.items.find((candidate) => isRecord$2(candidate) && candidate.message_id === rootId);
|
|
3873
|
+
if (!isRecord$2(item) || typeof item.msg_type !== "string" || !isRecord$2(item.body) || typeof item.body.content !== "string") throw new InvalidFeishuTopicContext(`Feishu topic root message is invalid: ${rootId}`);
|
|
3874
|
+
return {
|
|
3875
|
+
content: item.body.content,
|
|
3876
|
+
messageType: item.msg_type
|
|
3877
|
+
};
|
|
3878
|
+
}
|
|
3879
|
+
function isRecord$2(value) {
|
|
3880
|
+
return value !== null && typeof value === "object";
|
|
3881
|
+
}
|
|
3882
|
+
//#endregion
|
|
3460
3883
|
//#region src/composition/rivus-daemon-bootstrap.ts
|
|
3461
3884
|
const DEFAULT_WORKER_INTERVAL_MS$1 = 250;
|
|
3462
|
-
const DEFAULT_RUN_TIMEOUT_MS = 900 * 1e3;
|
|
3463
3885
|
function restoreConfiguredRivusDaemonBootstrap(options) {
|
|
3464
3886
|
return options.eventLog.readAll().pipe(Effect.map((events) => createConfiguredRivusDaemonBootstrap({
|
|
3465
3887
|
...options,
|
|
@@ -3473,19 +3895,33 @@ function createConfiguredRivusDaemonBootstrap(options) {
|
|
|
3473
3895
|
});
|
|
3474
3896
|
const cardRollover = createConfiguredFeishuCardRolloverRuntime({
|
|
3475
3897
|
cardTargets: options.cardTargets,
|
|
3898
|
+
cancelButton: false,
|
|
3476
3899
|
client: openApiClient,
|
|
3477
3900
|
clock: options.clock,
|
|
3478
3901
|
config: options.config,
|
|
3479
3902
|
...options.cardLedger ? { ledger: options.cardLedger } : {},
|
|
3903
|
+
...options.flushIntervalMs === void 0 ? {} : { flushIntervalMs: options.flushIntervalMs },
|
|
3480
3904
|
...options.observeCardRollover ? { observe: options.observeCardRollover } : {},
|
|
3481
3905
|
...options.onWorkerError ? { onError: options.onWorkerError } : {},
|
|
3482
3906
|
sleep: options.sleep,
|
|
3483
3907
|
...options.cardRolloverIntervalMs === void 0 ? {} : { supervisorIntervalMs: options.cardRolloverIntervalMs }
|
|
3484
3908
|
});
|
|
3485
3909
|
const publisher = cardRollover.rollover;
|
|
3910
|
+
const replies = createConfiguredFeishuTextReplySender({
|
|
3911
|
+
client: openApiClient,
|
|
3912
|
+
config: options.config
|
|
3913
|
+
});
|
|
3914
|
+
const reactions = createConfiguredFeishuMessageReactionSender({
|
|
3915
|
+
client: openApiClient,
|
|
3916
|
+
config: options.config
|
|
3917
|
+
});
|
|
3918
|
+
const promptContext = createFeishuTopicContextResolver({
|
|
3919
|
+
baseUrl: options.config.feishu.baseUrl,
|
|
3920
|
+
client: openApiClient
|
|
3921
|
+
});
|
|
3486
3922
|
const prepareRun = (run) => cardRollover.prepareRun(run);
|
|
3487
|
-
const periodicFlush = createPeriodicFlush(options, publisher);
|
|
3488
3923
|
const runtime = createFeishuAgentRuntime({
|
|
3924
|
+
acknowledgeSteering: (messageId) => reactions.add(messageId),
|
|
3489
3925
|
agentId: options.config.agentId,
|
|
3490
3926
|
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
3491
3927
|
clock: options.clock,
|
|
@@ -3495,10 +3931,11 @@ function createConfiguredRivusDaemonBootstrap(options) {
|
|
|
3495
3931
|
...options.initialEvents ? { initialEvents: options.initialEvents } : {},
|
|
3496
3932
|
...options.initialRunStates ? { initialRunStates: options.initialRunStates } : {},
|
|
3497
3933
|
loop: options.loop,
|
|
3498
|
-
periodicFlush,
|
|
3499
3934
|
prepareRun,
|
|
3935
|
+
promptContext,
|
|
3500
3936
|
publish: (action) => publisher.publish(action),
|
|
3501
|
-
|
|
3937
|
+
reply: (messageId, text) => replies.reply(messageId, text),
|
|
3938
|
+
...options.runTimeoutMs === void 0 ? {} : { runTimeoutMs: options.runTimeoutMs },
|
|
3502
3939
|
runIds: options.runIds
|
|
3503
3940
|
});
|
|
3504
3941
|
const websocketTransport = createFeishuWebSocketDaemon({
|
|
@@ -3553,13 +3990,6 @@ function createConfiguredRivusDaemonBootstrap(options) {
|
|
|
3553
3990
|
workerLoop
|
|
3554
3991
|
};
|
|
3555
3992
|
}
|
|
3556
|
-
function createPeriodicFlush(options, publisher) {
|
|
3557
|
-
return createFeishuPeriodicFlush({
|
|
3558
|
-
flush: () => publisher.flush(),
|
|
3559
|
-
intervalMs: options.flushIntervalMs ?? options.config.feishu.streamMinIntervalMs,
|
|
3560
|
-
sleep: options.sleep
|
|
3561
|
-
});
|
|
3562
|
-
}
|
|
3563
3993
|
//#endregion
|
|
3564
3994
|
//#region src/infrastructure/persistence/jsonl-agent-event-log.ts
|
|
3565
3995
|
var AgentEventLogStoreError = class {
|
|
@@ -3596,7 +4026,7 @@ function parseJsonlEvents(raw) {
|
|
|
3596
4026
|
return raw.split("\n").filter((line) => line.trim().length > 0).map((line, index) => parseAgentDomainEvent(JSON.parse(line), index + 1));
|
|
3597
4027
|
}
|
|
3598
4028
|
function parseAgentDomainEvent(value, lineNumber) {
|
|
3599
|
-
if (!isRecord$
|
|
4029
|
+
if (!isRecord$5(value) || typeof value.type !== "string") throw new Error(`Invalid agent event at line ${lineNumber}.`);
|
|
3600
4030
|
const occurredAt = readDate(value, "occurredAt", lineNumber);
|
|
3601
4031
|
switch (value.type) {
|
|
3602
4032
|
case "agent_run_accepted": return {
|
|
@@ -3767,7 +4197,7 @@ function readOptionalNumber(value, field, lineNumber) {
|
|
|
3767
4197
|
}
|
|
3768
4198
|
function readRecord$1(value, field, lineNumber) {
|
|
3769
4199
|
const fieldValue = value[field];
|
|
3770
|
-
if (!isRecord$
|
|
4200
|
+
if (!isRecord$5(fieldValue)) throw new Error(`Invalid agent event field '${field}' at line ${lineNumber}.`);
|
|
3771
4201
|
return fieldValue;
|
|
3772
4202
|
}
|
|
3773
4203
|
function readOptionalString(value, field, lineNumber) {
|
|
@@ -4775,13 +5205,13 @@ async function saveChains(filePath, chains) {
|
|
|
4775
5205
|
}
|
|
4776
5206
|
}
|
|
4777
5207
|
function parseChains(value) {
|
|
4778
|
-
if (!isRecord$
|
|
5208
|
+
if (!isRecord$5(value)) throw new Error("Feishu card presentation file must contain a JSON object.");
|
|
4779
5209
|
if (value.version === void 0) return Object.entries(value).map(([runId, target]) => parseLegacyChain(runId, target));
|
|
4780
|
-
if (value.version !== PRESENTATION_FILE_VERSION || !isRecord$
|
|
5210
|
+
if (value.version !== PRESENTATION_FILE_VERSION || !isRecord$5(value.runs)) throw new Error(`Unsupported Feishu card presentation file version: ${JSON.stringify(value.version)}`);
|
|
4781
5211
|
return Object.entries(value.runs).map(([runId, chain]) => parseChain(runId, chain));
|
|
4782
5212
|
}
|
|
4783
5213
|
function parseLegacyChain(runId, target) {
|
|
4784
|
-
if (!isRecord$
|
|
5214
|
+
if (!isRecord$5(target) || typeof target.cardId !== "string" || typeof target.elementId !== "string") throw new Error(`Invalid Feishu card target entry for run '${runId}'.`);
|
|
4785
5215
|
return createCardPresentationChain({
|
|
4786
5216
|
cardId: target.cardId,
|
|
4787
5217
|
createdAt: LEGACY_PRESENTATION_TIMESTAMP,
|
|
@@ -4793,9 +5223,9 @@ function parseLegacyChain(runId, target) {
|
|
|
4793
5223
|
});
|
|
4794
5224
|
}
|
|
4795
5225
|
function parseChain(runId, value) {
|
|
4796
|
-
if (!isRecord$
|
|
5226
|
+
if (!isRecord$5(value) || value.runId !== runId || !Number.isInteger(value.activeGeneration) || typeof value.activePresentationId !== "string" || !Number.isInteger(value.revision) || !Array.isArray(value.presentations) || value.presentations.length === 0) throw new Error(`Invalid Feishu card presentation chain for run '${runId}'.`);
|
|
4797
5227
|
const chain = value;
|
|
4798
|
-
for (const presentation of chain.presentations) if (!isRecord$
|
|
5228
|
+
for (const presentation of chain.presentations) if (!isRecord$5(presentation) || typeof presentation.cardId !== "string" || typeof presentation.presentationId !== "string" || typeof presentation.status !== "string" || !Number.isInteger(presentation.generation)) throw new Error(`Invalid Feishu card presentation entry for run '${runId}'.`);
|
|
4799
5229
|
activeCardPresentation(chain);
|
|
4800
5230
|
return chain;
|
|
4801
5231
|
}
|
|
@@ -5167,6 +5597,15 @@ function readRedactedString(serialized) {
|
|
|
5167
5597
|
return typeof value === "string" ? value : serialized;
|
|
5168
5598
|
}
|
|
5169
5599
|
//#endregion
|
|
5600
|
+
//#region src/infrastructure/feishu/feishu-periodic-flush.ts
|
|
5601
|
+
function createFeishuPeriodicFlush(options) {
|
|
5602
|
+
const loop = Effect.forever(Effect.gen(function* () {
|
|
5603
|
+
yield* options.sleep(options.intervalMs);
|
|
5604
|
+
yield* options.flush();
|
|
5605
|
+
}));
|
|
5606
|
+
return { withPeriodicFlush: (effect) => Effect.raceFirst(effect, loop) };
|
|
5607
|
+
}
|
|
5608
|
+
//#endregion
|
|
5170
5609
|
//#region src/testing/fakes.ts
|
|
5171
5610
|
function createFixedClock(now) {
|
|
5172
5611
|
return { now: Effect.succeed(now) };
|
|
@@ -5596,7 +6035,7 @@ async function load$1(filePath) {
|
|
|
5596
6035
|
for (const [index, line] of raw.split("\n").entries()) {
|
|
5597
6036
|
if (!line.trim()) continue;
|
|
5598
6037
|
const envelope = JSON.parse(line);
|
|
5599
|
-
if (!isRecord$
|
|
6038
|
+
if (!isRecord$5(envelope) || envelope.version !== 1 || !isOperationRecord(envelope.record)) throw new Error(`invalid tool operation snapshot at line ${index + 1}`);
|
|
5600
6039
|
const record = envelope.record;
|
|
5601
6040
|
const previous = latest.get(record.operationId);
|
|
5602
6041
|
if (record.revision !== (previous?.revision ?? 0) + 1) throw new Error(`invalid tool operation revision at line ${index + 1}`);
|
|
@@ -5619,7 +6058,7 @@ function isValidTransition$1(previous, next) {
|
|
|
5619
6058
|
}
|
|
5620
6059
|
}
|
|
5621
6060
|
function isOperationRecord(value) {
|
|
5622
|
-
if (!isRecord$
|
|
6061
|
+
if (!isRecord$5(value) || !isRecord$5(value.binding) || !isRecord$5(value.state)) return false;
|
|
5623
6062
|
const binding = value.binding;
|
|
5624
6063
|
const state = value.state;
|
|
5625
6064
|
if (typeof value.operationId !== "string" || !Number.isInteger(value.revision) || value.revision < 1) return false;
|
|
@@ -5650,7 +6089,7 @@ function isStableJson(value) {
|
|
|
5650
6089
|
}
|
|
5651
6090
|
}
|
|
5652
6091
|
function isReconciliation(value) {
|
|
5653
|
-
return isRecord$
|
|
6092
|
+
return isRecord$5(value) && typeof value.actorId === "string" && value.actorId.trim().length > 0 && typeof value.at === "string" && Number.isFinite(Date.parse(value.at)) && typeof value.note === "string" && value.note.trim().length > 0 && (value.outcome === "applied" || value.outcome === "not-applied");
|
|
5654
6093
|
}
|
|
5655
6094
|
//#endregion
|
|
5656
6095
|
//#region src/infrastructure/persistence/jsonl-recovery-control.ts
|
|
@@ -5687,14 +6126,20 @@ function isMissingDirectory(error) {
|
|
|
5687
6126
|
function createAgentHarnessPooledRuntime(harness) {
|
|
5688
6127
|
return {
|
|
5689
6128
|
cancel: (input) => Effect.runPromise(harness.forSession(input.sessionKey).cancelRun(input.runId, input.reason)),
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
6129
|
+
steer: (input) => Effect.runPromise(harness.forSession(input.sessionKey).steerRun(input.runId, input.text)),
|
|
6130
|
+
run: async (input) => {
|
|
6131
|
+
const exit = await Effect.runPromiseExit(harness.promptWithUpdates({
|
|
6132
|
+
...input.invocation ? { invocation: input.invocation } : {},
|
|
6133
|
+
sessionKey: input.sessionKey,
|
|
6134
|
+
text: input.text
|
|
6135
|
+
}, (update) => Effect.tryPromise({
|
|
6136
|
+
try: async () => input.onUpdate?.(update),
|
|
6137
|
+
catch: (error) => error
|
|
6138
|
+
})));
|
|
6139
|
+
if (Exit.isSuccess(exit)) return exit.value;
|
|
6140
|
+
const failure = Cause.failureOption(exit.cause);
|
|
6141
|
+
throw Option.isSome(failure) ? failure.value : Cause.squash(exit.cause);
|
|
6142
|
+
}
|
|
5698
6143
|
};
|
|
5699
6144
|
}
|
|
5700
6145
|
//#endregion
|
|
@@ -5707,12 +6152,15 @@ function createFeishuDeploymentEndpoint(options) {
|
|
|
5707
6152
|
shutdownTimeoutMs: options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS
|
|
5708
6153
|
});
|
|
5709
6154
|
const daemon = createFeishuAgentDaemon({
|
|
6155
|
+
...options.acknowledgeSteering ? { acknowledgeSteering: options.acknowledgeSteering } : {},
|
|
5710
6156
|
agentId: options.agentId,
|
|
5711
6157
|
botOpenId: options.botOpenId,
|
|
6158
|
+
dedupe: true,
|
|
5712
6159
|
execution: execution.port,
|
|
5713
6160
|
...options.finalizeRun ? { finalizeRun: options.finalizeRun } : {},
|
|
5714
6161
|
...options.interactions ? { interactions: options.interactions } : {},
|
|
5715
6162
|
...options.prepareRun ? { prepareRun: options.prepareRun } : {},
|
|
6163
|
+
...options.promptContext ? { promptContext: options.promptContext } : {},
|
|
5716
6164
|
publish: options.publish,
|
|
5717
6165
|
...options.publishRunUpdate ? { publishRunUpdate: options.publishRunUpdate } : {},
|
|
5718
6166
|
...options.reply ? { reply: options.reply } : {},
|
|
@@ -5720,9 +6168,15 @@ function createFeishuDeploymentEndpoint(options) {
|
|
|
5720
6168
|
...options.endpointId ? { resolveInvocation: (payload, conversationId) => createInvocation(payload, options.endpointId, options.memoryTenantId, conversationId, options.projectSpaceId) } : {}
|
|
5721
6169
|
});
|
|
5722
6170
|
const queue = createFeishuMessageQueue({
|
|
6171
|
+
handleControl: (payload) => daemon.trySteerMessage(payload),
|
|
5723
6172
|
handleMessage: (payload, handleOptions) => daemon.handleMessage(payload, handleOptions),
|
|
5724
6173
|
...options.inboxRepository ? { repository: options.inboxRepository } : {},
|
|
5725
6174
|
...options.maxPendingMessages === void 0 ? {} : { maxPending: options.maxPendingMessages },
|
|
6175
|
+
...options.initialEvents ? { shouldSkipReplay: createInterruptedRunReplayGuard(options.initialEvents, {
|
|
6176
|
+
agentId: options.agentId,
|
|
6177
|
+
botOpenId: options.botOpenId,
|
|
6178
|
+
...options.sessionStore ? { sessionStore: options.sessionStore } : {}
|
|
6179
|
+
}, options.sessionNamespace) } : {},
|
|
5726
6180
|
shouldRetryError: isRetryableFeishuMessageError
|
|
5727
6181
|
});
|
|
5728
6182
|
const worker = createFeishuMessageWorker({
|
|
@@ -5740,14 +6194,17 @@ function createFeishuDeploymentEndpoint(options) {
|
|
|
5740
6194
|
eventDispatcher: options.eventDispatcher,
|
|
5741
6195
|
handlers: createFeishuEventHandlers({
|
|
5742
6196
|
cardActions: daemon,
|
|
5743
|
-
queue: {
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
6197
|
+
queue: {
|
|
6198
|
+
accept: (payload) => shouldAcceptFeishuEndpointMessage(payload, options.groupPolicy, options.botOpenId) ? queue.accept(payload).pipe(Effect.tap((result) => result.accepted || result.reason !== "capacity" || !options.onCapacityExceeded ? Effect.void : Effect.sync(() => {
|
|
6199
|
+
Effect.runPromise(options.onCapacityExceeded(payload)).catch((error) => options.onWorkerError?.(error)).catch(() => void 0);
|
|
6200
|
+
}))) : Effect.succeed({
|
|
6201
|
+
accepted: false,
|
|
6202
|
+
messageId: payload.event.message.message_id,
|
|
6203
|
+
pending: queue.pending(),
|
|
6204
|
+
reason: "ignored"
|
|
6205
|
+
}),
|
|
6206
|
+
pending: () => queue.pending()
|
|
6207
|
+
}
|
|
5751
6208
|
})
|
|
5752
6209
|
});
|
|
5753
6210
|
return {
|
|
@@ -5788,6 +6245,10 @@ function createDeploymentExecution(options) {
|
|
|
5788
6245
|
const activeHandles = /* @__PURE__ */ new Set();
|
|
5789
6246
|
const activeRuns = /* @__PURE__ */ new Map();
|
|
5790
6247
|
let stopping = false;
|
|
6248
|
+
const findActiveRun = (requestedSessionKey) => {
|
|
6249
|
+
const namespacedSessionKey = namespaceSessionKey(options.sessionNamespace, requestedSessionKey);
|
|
6250
|
+
return [...activeRuns].find(([, sessionKey]) => sessionKey === requestedSessionKey || sessionKey === namespacedSessionKey);
|
|
6251
|
+
};
|
|
5791
6252
|
const requestShutdownCancellation = (runId, sessionKey) => {
|
|
5792
6253
|
const current = cancellationRequests.get(runId);
|
|
5793
6254
|
if (current) return current;
|
|
@@ -5832,6 +6293,19 @@ function createDeploymentExecution(options) {
|
|
|
5832
6293
|
if (errors.length > 1) throw new AggregateError(errors, "Active Agent run cancellation failed");
|
|
5833
6294
|
},
|
|
5834
6295
|
port: {
|
|
6296
|
+
cancelActiveRun: (input) => Effect.tryPromise({
|
|
6297
|
+
try: async () => {
|
|
6298
|
+
const activeRun = findActiveRun(input.sessionKey);
|
|
6299
|
+
if (!activeRun) return void 0;
|
|
6300
|
+
const [runId, sessionKey] = activeRun;
|
|
6301
|
+
return await options.cancel({
|
|
6302
|
+
reason: input.reason,
|
|
6303
|
+
runId,
|
|
6304
|
+
sessionKey
|
|
6305
|
+
}) ? runId : void 0;
|
|
6306
|
+
},
|
|
6307
|
+
catch: (error) => error
|
|
6308
|
+
}),
|
|
5835
6309
|
cancelRun: (input) => input.sessionKey === void 0 ? Effect.succeed(false) : Effect.tryPromise({
|
|
5836
6310
|
try: () => options.cancel({
|
|
5837
6311
|
reason: input.reason,
|
|
@@ -5840,6 +6314,23 @@ function createDeploymentExecution(options) {
|
|
|
5840
6314
|
}),
|
|
5841
6315
|
catch: (error) => error
|
|
5842
6316
|
}),
|
|
6317
|
+
steerRun: (input) => Effect.tryPromise({
|
|
6318
|
+
try: async () => {
|
|
6319
|
+
if (!options.steer) return void 0;
|
|
6320
|
+
const activeRun = findActiveRun(input.sessionKey);
|
|
6321
|
+
if (!activeRun) return void 0;
|
|
6322
|
+
const [runId, sessionKey] = activeRun;
|
|
6323
|
+
return await options.steer({
|
|
6324
|
+
runId,
|
|
6325
|
+
sessionKey,
|
|
6326
|
+
text: input.text
|
|
6327
|
+
}) ? {
|
|
6328
|
+
runId,
|
|
6329
|
+
steered: true
|
|
6330
|
+
} : void 0;
|
|
6331
|
+
},
|
|
6332
|
+
catch: (error) => error
|
|
6333
|
+
}),
|
|
5843
6334
|
promptWithUpdates: (command, onUpdate) => Effect.tryPromise({
|
|
5844
6335
|
try: async () => {
|
|
5845
6336
|
const sessionKey = namespaceSessionKey(options.sessionNamespace, command.sessionKey);
|
|
@@ -7255,8 +7746,8 @@ async function loadJsonlSnapshotStore(options) {
|
|
|
7255
7746
|
}
|
|
7256
7747
|
throw new JsonlSnapshotStoreCorrupted(`${options.errorLabel} JSONL at line ${index + 1} is not valid JSON`);
|
|
7257
7748
|
}
|
|
7258
|
-
if (!isRecord$
|
|
7259
|
-
if (isRecord$
|
|
7749
|
+
if (!isRecord$5(parsed) || parsed.version !== options.version) throw new JsonlSnapshotStoreCorrupted(`unsupported ${options.errorLabel} version at line ${index + 1}`);
|
|
7750
|
+
if (isRecord$5(parsed.snapshot)) {
|
|
7260
7751
|
if (!Array.isArray(parsed.snapshot.records)) throw new JsonlSnapshotStoreCorrupted(`invalid ${options.errorLabel} snapshot at line ${index + 1}`);
|
|
7261
7752
|
latest.clear();
|
|
7262
7753
|
for (const value of parsed.snapshot.records) {
|
|
@@ -7320,7 +7811,7 @@ function validateSessionSequence(previous, record, _lineNumber) {
|
|
|
7320
7811
|
return isValidBackgroundSessionTransition(previous, record);
|
|
7321
7812
|
}
|
|
7322
7813
|
function isBackgroundSessionState(value) {
|
|
7323
|
-
if (!isRecord$
|
|
7814
|
+
if (!isRecord$5(value)) return false;
|
|
7324
7815
|
const phases = [
|
|
7325
7816
|
"queued",
|
|
7326
7817
|
"running",
|
|
@@ -7338,14 +7829,14 @@ function isBackgroundSessionState(value) {
|
|
|
7338
7829
|
if (!Number.isInteger(value.revision) || value.revision < 1) return false;
|
|
7339
7830
|
if (typeof value.parentRunId !== "string" || value.parentRunId.trim() === "") return false;
|
|
7340
7831
|
if (typeof value.sourceMessageId !== "string" || value.sourceMessageId.trim() === "") return false;
|
|
7341
|
-
if (!isRecord$
|
|
7832
|
+
if (!isRecord$5(value.origin)) return false;
|
|
7342
7833
|
const origin = value.origin;
|
|
7343
7834
|
if (typeof origin.endpointId !== "string" || origin.endpointId.trim() === "") return false;
|
|
7344
7835
|
if (typeof origin.tenantKey !== "string") return false;
|
|
7345
7836
|
if (!Array.isArray(origin.allowedActorOpenIds) || origin.allowedActorOpenIds.some((id) => typeof id !== "string")) return false;
|
|
7346
7837
|
if (origin.conversationId !== void 0 && typeof origin.conversationId !== "string") return false;
|
|
7347
|
-
if (origin.memory !== void 0 && !isRecord$
|
|
7348
|
-
if (!isRecord$
|
|
7838
|
+
if (origin.memory !== void 0 && !isRecord$5(origin.memory)) return false;
|
|
7839
|
+
if (!isRecord$5(value.authority)) return false;
|
|
7349
7840
|
const authority = value.authority;
|
|
7350
7841
|
if (typeof authority.agentId !== "string" || authority.agentId.trim() === "") return false;
|
|
7351
7842
|
if (typeof authority.profileRevision !== "string" || authority.profileRevision.trim() === "") return false;
|
|
@@ -7368,17 +7859,17 @@ function isBackgroundSessionState(value) {
|
|
|
7368
7859
|
if (typeof value.updatedAt !== "string" || !isIso(value.updatedAt)) return false;
|
|
7369
7860
|
if (value.lease !== void 0 && !isLease(value.lease)) return false;
|
|
7370
7861
|
if (value.cancellation !== void 0) {
|
|
7371
|
-
if (!isRecord$
|
|
7862
|
+
if (!isRecord$5(value.cancellation)) return false;
|
|
7372
7863
|
if (typeof value.cancellation.reason !== "string" || !isIso(value.cancellation.requestedAt)) return false;
|
|
7373
7864
|
}
|
|
7374
7865
|
if (value.result !== void 0) {
|
|
7375
|
-
if (!isRecord$
|
|
7866
|
+
if (!isRecord$5(value.result)) return false;
|
|
7376
7867
|
if (typeof value.result.text !== "string" || typeof value.result.stepRunId !== "string" || !isIso(value.result.completedAt)) return false;
|
|
7377
7868
|
}
|
|
7378
7869
|
return true;
|
|
7379
7870
|
}
|
|
7380
7871
|
function isLease(value) {
|
|
7381
|
-
if (!isRecord$
|
|
7872
|
+
if (!isRecord$5(value)) return false;
|
|
7382
7873
|
return typeof value.owner === "string" && value.owner.trim() !== "" && Number.isInteger(value.epoch) && value.epoch >= 0 && isIso(value.expiresAt);
|
|
7383
7874
|
}
|
|
7384
7875
|
function isIso(value) {
|
|
@@ -7407,7 +7898,7 @@ function validateDeliverySequence(previous, record, _lineNumber) {
|
|
|
7407
7898
|
return previous.state === "pending" && record.state === "delivered" ? void 0 : "transition";
|
|
7408
7899
|
}
|
|
7409
7900
|
function isDeliveryRecord(value) {
|
|
7410
|
-
if (!isRecord$
|
|
7901
|
+
if (!isRecord$5(value)) return false;
|
|
7411
7902
|
if (typeof value.deliveryId !== "string" || value.deliveryId.trim() === "" || typeof value.sessionId !== "string" || value.sessionId.trim() === "") return false;
|
|
7412
7903
|
if (typeof value.kind !== "string" || ![
|
|
7413
7904
|
"progress",
|
|
@@ -7737,10 +8228,13 @@ function createProjectMemoryPromptPreparer(options) {
|
|
|
7737
8228
|
//#endregion
|
|
7738
8229
|
//#region src/application/agent/agent-loop-prompt-transformer.ts
|
|
7739
8230
|
function createAgentLoopPromptTransformer(options) {
|
|
7740
|
-
return {
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
8231
|
+
return {
|
|
8232
|
+
run: (input) => Stream.unwrap(Effect.promise(() => Promise.resolve(options.transform(input))).pipe(Effect.map((text) => options.loop.run({
|
|
8233
|
+
...input,
|
|
8234
|
+
text
|
|
8235
|
+
})))),
|
|
8236
|
+
...options.loop.supportsSteering ? { supportsSteering: true } : {}
|
|
8237
|
+
};
|
|
7744
8238
|
}
|
|
7745
8239
|
//#endregion
|
|
7746
8240
|
//#region src/application/project/project-skill-catalog.ts
|
|
@@ -7785,7 +8279,7 @@ async function load(filePath) {
|
|
|
7785
8279
|
for (const [index, line] of raw.split("\n").entries()) {
|
|
7786
8280
|
if (!line.trim()) continue;
|
|
7787
8281
|
const envelope = JSON.parse(line);
|
|
7788
|
-
if (!isRecord$
|
|
8282
|
+
if (!isRecord$5(envelope) || envelope.version !== 1 || !isMemorySnapshot(envelope.snapshot)) throw new Error(`invalid Agent Memory snapshot at line ${index + 1}`);
|
|
7789
8283
|
const snapshot = envelope.snapshot;
|
|
7790
8284
|
const key = snapshotKey(snapshot);
|
|
7791
8285
|
const previous = latest.get(key);
|
|
@@ -7796,7 +8290,7 @@ async function load(filePath) {
|
|
|
7796
8290
|
return [...latest.values()];
|
|
7797
8291
|
}
|
|
7798
8292
|
function isMemorySnapshot(value) {
|
|
7799
|
-
if (!isRecord$
|
|
8293
|
+
if (!isRecord$5(value) || !isRecord$5(value.binding) || !isRecord$5(value.record)) return false;
|
|
7800
8294
|
const binding = value.binding;
|
|
7801
8295
|
const record = value.record;
|
|
7802
8296
|
return nonEmpty(binding.tenantId) && nonEmpty(binding.agentId) && nonEmpty(binding.subjectId) && isMemoryScope(binding.scope) && (binding.scope === "conversation" ? nonEmpty(binding.conversationId) && binding.projectId === void 0 : binding.scope === "project" ? nonEmpty(binding.projectId) && binding.conversationId === void 0 : binding.conversationId === void 0 && binding.projectId === void 0) && nonEmpty(record.id) && record.id.startsWith("memory:") && nonEmpty(record.content) && typeof record.conversationSafe === "boolean" && (record.tombstoneReason === void 0 || record.state === "tombstoned" && nonEmpty(record.tombstoneReason)) && Number.isInteger(record.revision) && record.revision > 0 && record.scope === binding.scope && isMemoryScope(record.scope) && [
|
|
@@ -8241,7 +8735,7 @@ async function readSnapshot$1(filePath) {
|
|
|
8241
8735
|
version: 3
|
|
8242
8736
|
});
|
|
8243
8737
|
const value = JSON.parse(raw);
|
|
8244
|
-
if (!isRecord$
|
|
8738
|
+
if (!isRecord$5(value) || value.version !== 2 && value.version !== 3 || !Array.isArray(value.records)) throw new Error("Automation Tick snapshot must contain version 2 or 3 records");
|
|
8245
8739
|
return Object.freeze({
|
|
8246
8740
|
records: Object.freeze(value.records.map(readRecord)),
|
|
8247
8741
|
version: value.version
|
|
@@ -8254,7 +8748,7 @@ async function writeSnapshot(filePath, records) {
|
|
|
8254
8748
|
});
|
|
8255
8749
|
}
|
|
8256
8750
|
function readRecord(value) {
|
|
8257
|
-
if (!isRecord$
|
|
8751
|
+
if (!isRecord$5(value)) throw new Error("Automation Tick record must be an object");
|
|
8258
8752
|
for (const field of [
|
|
8259
8753
|
"automationId",
|
|
8260
8754
|
"mandateId",
|
|
@@ -8668,21 +9162,6 @@ function createRoutedHumanInteractionToolApprovalService(registry) {
|
|
|
8668
9162
|
} };
|
|
8669
9163
|
}
|
|
8670
9164
|
//#endregion
|
|
8671
|
-
//#region src/infrastructure/feishu/feishu-text-reply.ts
|
|
8672
|
-
function createConfiguredFeishuTextReplySender(options) {
|
|
8673
|
-
const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
|
|
8674
|
-
return { reply: (messageId, text) => Effect.gen(function* () {
|
|
8675
|
-
yield* options.client.request({
|
|
8676
|
-
body: {
|
|
8677
|
-
content: JSON.stringify({ text }),
|
|
8678
|
-
msg_type: "text"
|
|
8679
|
-
},
|
|
8680
|
-
method: "POST",
|
|
8681
|
-
url: `${baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}/reply`
|
|
8682
|
-
}, "Feishu reply failed");
|
|
8683
|
-
}) };
|
|
8684
|
-
}
|
|
8685
|
-
//#endregion
|
|
8686
9165
|
//#region src/infrastructure/feishu/feishu-automation-card-sender.ts
|
|
8687
9166
|
function createConfiguredFeishuAutomationCardSender(options) {
|
|
8688
9167
|
const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
|
|
@@ -8848,19 +9327,19 @@ async function loadSnapshots(filePath) {
|
|
|
8848
9327
|
return interactions;
|
|
8849
9328
|
}
|
|
8850
9329
|
function readSnapshot(value, line) {
|
|
8851
|
-
if (!isRecord$
|
|
9330
|
+
if (!isRecord$5(value) || value.version !== 1 || !isRecord$5(value.interaction)) throw new Error(`invalid human interaction envelope at line ${line}`);
|
|
8852
9331
|
const interaction = value.interaction;
|
|
8853
9332
|
if (!isHumanInteraction(interaction)) throw new Error(`invalid human interaction snapshot at line ${line}`);
|
|
8854
9333
|
return structuredClone(interaction);
|
|
8855
9334
|
}
|
|
8856
9335
|
function isHumanInteraction(value) {
|
|
8857
|
-
if (!nonEmptyStrings(value, "agentId", "createdAt", "expiresAt", "id", "instanceId", "runId", "sessionKey", "sourceMessageId", "summary", "tenantKey", "title") || !isTimestamp(value.createdAt) || !isTimestamp(value.expiresAt) || Date.parse(value.expiresAt) <= Date.parse(value.createdAt) || !Number.isInteger(value.revision) || value.revision < 1 || !isNonEmptyStringArray(value.allowedActorOpenIds) || !isFacts(value.facts) || !isRecord$
|
|
9336
|
+
if (!nonEmptyStrings(value, "agentId", "createdAt", "expiresAt", "id", "instanceId", "runId", "sessionKey", "sourceMessageId", "summary", "tenantKey", "title") || !isTimestamp(value.createdAt) || !isTimestamp(value.expiresAt) || Date.parse(value.expiresAt) <= Date.parse(value.createdAt) || !Number.isInteger(value.revision) || value.revision < 1 || !isNonEmptyStringArray(value.allowedActorOpenIds) || !isFacts(value.facts) || !isRecord$5(value.state)) return false;
|
|
8858
9337
|
if (value.kind === "tool-approval") return isToolApprovalRequest(value.request) && isToolApprovalState(value.state) && stateRespectsInteraction(value.state, value);
|
|
8859
9338
|
if (value.kind === "user-decision") return isUserDecisionOptions(value.options, value.recommendedOptionId) && isUserDecisionState(value.state, value.options) && stateRespectsInteraction(value.state, value);
|
|
8860
9339
|
return false;
|
|
8861
9340
|
}
|
|
8862
9341
|
function isToolApprovalRequest(value) {
|
|
8863
|
-
return isRecord$
|
|
9342
|
+
return isRecord$5(value) && nonEmptyStrings(value, "callId", "inputDigest", "operationId", "toolId", "toolVersion") && (value.risk === "observe" || value.risk === "mutate" || value.risk === "irreversible" || value.risk === "host-control");
|
|
8864
9343
|
}
|
|
8865
9344
|
function isToolApprovalState(state) {
|
|
8866
9345
|
const common = isCommonInteractionState(state);
|
|
@@ -8874,7 +9353,7 @@ function isUserDecisionState(state, options) {
|
|
|
8874
9353
|
const common = isCommonInteractionState(state);
|
|
8875
9354
|
if (common !== void 0) return common;
|
|
8876
9355
|
switch (state.status) {
|
|
8877
|
-
case "selected": return isActor(state.actor) && isTimestamp(state.resolvedAt) && typeof state.optionId === "string" && Array.isArray(options) && options.some((option) => isRecord$
|
|
9356
|
+
case "selected": return isActor(state.actor) && isTimestamp(state.resolvedAt) && typeof state.optionId === "string" && Array.isArray(options) && options.some((option) => isRecord$5(option) && option.id === state.optionId);
|
|
8878
9357
|
default: return false;
|
|
8879
9358
|
}
|
|
8880
9359
|
}
|
|
@@ -8889,14 +9368,14 @@ function isCommonInteractionState(state) {
|
|
|
8889
9368
|
}
|
|
8890
9369
|
function isUserDecisionOptions(value, recommendedOptionId) {
|
|
8891
9370
|
if (!Array.isArray(value) || value.length < 2 || value.length > 5) return false;
|
|
8892
|
-
const ids = value.map((option) => isRecord$
|
|
8893
|
-
return value.every((option) => isRecord$
|
|
9371
|
+
const ids = value.map((option) => isRecord$5(option) ? option.id : void 0);
|
|
9372
|
+
return value.every((option) => isRecord$5(option) && nonEmptyStrings(option, "description", "id", "label")) && ids.every((id) => typeof id === "string") && new Set(ids).size === ids.length && (recommendedOptionId === void 0 || typeof recommendedOptionId === "string" && ids.includes(recommendedOptionId));
|
|
8894
9373
|
}
|
|
8895
9374
|
function isFacts(value) {
|
|
8896
|
-
return Array.isArray(value) && value.every((fact) => isRecord$
|
|
9375
|
+
return Array.isArray(value) && value.every((fact) => isRecord$5(fact) && nonEmptyStrings(fact, "label", "value"));
|
|
8897
9376
|
}
|
|
8898
9377
|
function isActor(value) {
|
|
8899
|
-
return isRecord$
|
|
9378
|
+
return isRecord$5(value) && nonEmptyStrings(value, "openId", "tenantKey");
|
|
8900
9379
|
}
|
|
8901
9380
|
function isNonEmptyStringArray(value) {
|
|
8902
9381
|
return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "string" && entry.length > 0);
|
|
@@ -8911,7 +9390,7 @@ function isTimestamp(value) {
|
|
|
8911
9390
|
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
8912
9391
|
}
|
|
8913
9392
|
function stateRespectsInteraction(state, interaction) {
|
|
8914
|
-
if (isRecord$
|
|
9393
|
+
if (isRecord$5(state.actor)) {
|
|
8915
9394
|
if (state.actor.tenantKey !== interaction.tenantKey || !Array.isArray(interaction.allowedActorOpenIds) || !interaction.allowedActorOpenIds.includes(state.actor.openId)) return false;
|
|
8916
9395
|
}
|
|
8917
9396
|
const expiresAt = interaction.expiresAt;
|
|
@@ -9087,4 +9566,4 @@ function validateDecisionInput(input) {
|
|
|
9087
9566
|
if (input.recommendedOptionId && !optionIds.includes(input.recommendedOptionId)) throw new Error("recommended user decision option must be available");
|
|
9088
9567
|
}
|
|
9089
9568
|
//#endregion
|
|
9090
|
-
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, 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, createAgentCommandFromFeishuCardAction, createAgentCommandFromFeishuMessage, createAgentConversationMessages, createAgentDomainEventHandler, createAgentDomainEventSink, createAgentDomainEventSinkFromCallback, createAgentHarness, createAgentHarnessClient, createAgentHarnessPooledRuntime, createAgentInstanceRegistry, createAgentLoopFromCallback, createAgentLoopModelExecutionEnd, createAgentLoopModelExecutionStart, createAgentLoopPromptTransformer, createAgentLoopSkillExecutionEnd, createAgentLoopSkillExecutionStart, createAgentLoopTextDelta, createAgentLoopThinkingDelta, createAgentLoopToolExecutionEnd, createAgentLoopToolExecutionStart, createAgentLoopToolExecutionUpdate, 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, 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, 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, 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 };
|
|
9569
|
+
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, 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 };
|