@rivus/agent 0.10.3 → 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 +186 -23
- package/dist/index.js +869 -250
- package/dist/rivus-daemon-cli.js +6 -0
- package/examples/pi-feishu-deployment.bootstrap.ts +19 -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
|
-
|
|
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 [{
|
|
75
68
|
runId: event.runId,
|
|
76
|
-
text
|
|
77
|
-
type: "update_text"
|
|
78
|
-
}];
|
|
79
|
-
case "agent_run_completed": return [{
|
|
80
|
-
runId: event.runId,
|
|
81
|
-
text: state.finalText,
|
|
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,16 +594,111 @@ 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
|
|
607
|
+
//#region src/application/feishu/feishu-session-store.ts
|
|
608
|
+
function createFeishuSessionStore(options = {}) {
|
|
609
|
+
const generations = /* @__PURE__ */ new Map();
|
|
610
|
+
for (const record of options.initial ?? []) {
|
|
611
|
+
validateRecord(record);
|
|
612
|
+
const current = generations.get(record.baseSessionKey) ?? 0;
|
|
613
|
+
generations.set(record.baseSessionKey, Math.max(current, record.generation));
|
|
614
|
+
}
|
|
615
|
+
const serial = createSerialExecutor();
|
|
616
|
+
return {
|
|
617
|
+
current: (baseSessionKey) => {
|
|
618
|
+
validateBaseSessionKey(baseSessionKey);
|
|
619
|
+
return Effect.succeed(sessionKey(baseSessionKey, generations.get(baseSessionKey) ?? 0));
|
|
620
|
+
},
|
|
621
|
+
reset: (baseSessionKey) => Effect.tryPromise({
|
|
622
|
+
try: () => serial.run(async () => {
|
|
623
|
+
validateBaseSessionKey(baseSessionKey);
|
|
624
|
+
const previousGeneration = generations.get(baseSessionKey) ?? 0;
|
|
625
|
+
const generation = previousGeneration + 1;
|
|
626
|
+
const previousSessionKey = sessionKey(baseSessionKey, previousGeneration);
|
|
627
|
+
const nextSessionKey = sessionKey(baseSessionKey, generation);
|
|
628
|
+
const next = new Map(generations);
|
|
629
|
+
next.set(baseSessionKey, generation);
|
|
630
|
+
await options.persist?.([...next.entries()].map(([key, value]) => ({
|
|
631
|
+
baseSessionKey: key,
|
|
632
|
+
generation: value
|
|
633
|
+
})));
|
|
634
|
+
generations.clear();
|
|
635
|
+
for (const [key, value] of next) generations.set(key, value);
|
|
636
|
+
return {
|
|
637
|
+
generation,
|
|
638
|
+
previousSessionKey,
|
|
639
|
+
sessionKey: nextSessionKey
|
|
640
|
+
};
|
|
641
|
+
}),
|
|
642
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
643
|
+
})
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function sessionKey(baseSessionKey, generation) {
|
|
647
|
+
return generation === 0 ? baseSessionKey : `${baseSessionKey}:new-${generation}`;
|
|
648
|
+
}
|
|
649
|
+
function validateBaseSessionKey(value) {
|
|
650
|
+
if (value.trim() === "") throw new Error("Feishu base session key must not be empty");
|
|
651
|
+
}
|
|
652
|
+
function validateRecord(record) {
|
|
653
|
+
validateBaseSessionKey(record.baseSessionKey);
|
|
654
|
+
if (!Number.isSafeInteger(record.generation) || record.generation < 0) throw new Error("Feishu session generation must be a non-negative safe integer");
|
|
655
|
+
}
|
|
656
|
+
//#endregion
|
|
657
|
+
//#region src/infrastructure/persistence/write-persistence-file.ts
|
|
658
|
+
async function writePersistenceFile(filePath, value) {
|
|
659
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
660
|
+
const temporaryPath = `${filePath}.${randomUUID()}.tmp`;
|
|
661
|
+
try {
|
|
662
|
+
await writeFile(temporaryPath, `${JSON.stringify(value)}\n`, {
|
|
663
|
+
encoding: "utf8",
|
|
664
|
+
flag: "wx"
|
|
665
|
+
});
|
|
666
|
+
await rename(temporaryPath, filePath);
|
|
667
|
+
} catch (error) {
|
|
668
|
+
await unlink(temporaryPath).catch(() => void 0);
|
|
669
|
+
throw error;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
//#endregion
|
|
673
|
+
//#region src/infrastructure/persistence/json-feishu-session-store.ts
|
|
674
|
+
const SNAPSHOT_VERSION = 1;
|
|
675
|
+
async function openJsonFeishuSessionStore(options) {
|
|
676
|
+
return createFeishuSessionStore({
|
|
677
|
+
initial: await readSnapshot$2(options.filePath),
|
|
678
|
+
persist: (records) => writeSnapshot$1(options.filePath, records)
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
async function readSnapshot$2(filePath) {
|
|
682
|
+
const raw = await readPersistenceFile(filePath);
|
|
683
|
+
if (raw === void 0) return [];
|
|
684
|
+
const value = JSON.parse(raw);
|
|
685
|
+
if (!isRecord$5(value) || value.version !== SNAPSHOT_VERSION || !Array.isArray(value.sessions)) throw new Error("Feishu session store must contain version 1 sessions");
|
|
686
|
+
return value.sessions.map(readRecord$2);
|
|
687
|
+
}
|
|
688
|
+
async function writeSnapshot$1(filePath, records) {
|
|
689
|
+
await writePersistenceFile(filePath, {
|
|
690
|
+
sessions: records,
|
|
691
|
+
version: SNAPSHOT_VERSION
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
function readRecord$2(value) {
|
|
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");
|
|
696
|
+
return {
|
|
697
|
+
baseSessionKey: value.baseSessionKey,
|
|
698
|
+
generation: value.generation
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
//#endregion
|
|
565
702
|
//#region src/application/host/session-scheduler.ts
|
|
566
703
|
var SessionSchedulerCapacityExceeded = class extends Error {
|
|
567
704
|
name = "SessionSchedulerCapacityExceeded";
|
|
@@ -617,6 +754,10 @@ function createSessionScheduler(options) {
|
|
|
617
754
|
const runtime = runtimes.get(input.sessionKey);
|
|
618
755
|
return runtime ? (await runtime).cancel?.(input) ?? false : false;
|
|
619
756
|
},
|
|
757
|
+
steer: async (input) => {
|
|
758
|
+
const runtime = runtimes.get(input.sessionKey);
|
|
759
|
+
return runtime ? (await runtime).steer?.(input) ?? false : false;
|
|
760
|
+
},
|
|
620
761
|
dispose: async () => {
|
|
621
762
|
if (disposed) return;
|
|
622
763
|
disposed = true;
|
|
@@ -1096,6 +1237,18 @@ function createAgentRunUpdateHandler(handle) {
|
|
|
1096
1237
|
catch: (cause) => cause
|
|
1097
1238
|
});
|
|
1098
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
|
+
}
|
|
1099
1252
|
function createAgentHarness(options) {
|
|
1100
1253
|
if (options.runTimeoutMs !== void 0 && (!Number.isSafeInteger(options.runTimeoutMs) || options.runTimeoutMs < 1)) throw new Error("Agent run timeout must be a positive integer");
|
|
1101
1254
|
let activeRun;
|
|
@@ -1159,7 +1312,8 @@ function createAgentHarness(options) {
|
|
|
1159
1312
|
const previousSessionTranscript = getSessionTranscript(command.sessionKey);
|
|
1160
1313
|
const cancellation = {
|
|
1161
1314
|
abortController: new AbortController(),
|
|
1162
|
-
deferred: yield* Deferred.make()
|
|
1315
|
+
deferred: yield* Deferred.make(),
|
|
1316
|
+
steering: createSteeringChannel()
|
|
1163
1317
|
};
|
|
1164
1318
|
activeCancellation = cancellation;
|
|
1165
1319
|
const events = [];
|
|
@@ -1236,6 +1390,7 @@ function createAgentHarness(options) {
|
|
|
1236
1390
|
...previousSessionTranscript.turnCount > 0 ? { previousSessionTranscript } : {},
|
|
1237
1391
|
runId,
|
|
1238
1392
|
sessionKey: command.sessionKey,
|
|
1393
|
+
...options.loop.supportsSteering ? { steering: cancellation.steering } : {},
|
|
1239
1394
|
text: command.text
|
|
1240
1395
|
};
|
|
1241
1396
|
const loop = Effect.try({
|
|
@@ -1335,10 +1490,20 @@ function createAgentHarness(options) {
|
|
|
1335
1490
|
}
|
|
1336
1491
|
return completed;
|
|
1337
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
|
+
});
|
|
1338
1499
|
const requestSessionCancellation = (sessionKey, runId, reason) => {
|
|
1339
1500
|
if (!activeRun || activeRun.sessionKey !== sessionKey) return Effect.succeed(false);
|
|
1340
1501
|
return requestCancellation(runId, reason);
|
|
1341
1502
|
};
|
|
1503
|
+
const requestSessionSteering = (sessionKey, runId, text) => {
|
|
1504
|
+
if (!activeRun || activeRun.sessionKey !== sessionKey) return Effect.succeed(false);
|
|
1505
|
+
return requestSteering(runId, text);
|
|
1506
|
+
};
|
|
1342
1507
|
const requestSessionActiveCancellation = (sessionKey, reason) => {
|
|
1343
1508
|
if (!activeRun || activeRun.sessionKey !== sessionKey) return Effect.succeed(false);
|
|
1344
1509
|
return requestCancellation(activeRun.runId, reason);
|
|
@@ -1436,9 +1601,11 @@ function createAgentHarness(options) {
|
|
|
1436
1601
|
return yield* requestCancellation(activeRun.runId, reason);
|
|
1437
1602
|
}),
|
|
1438
1603
|
cancelRun: requestCancellation,
|
|
1604
|
+
steerRun: requestSteering,
|
|
1439
1605
|
forSession: (sessionKey) => ({
|
|
1440
1606
|
cancelActiveRun: (reason) => requestSessionActiveCancellation(sessionKey, reason),
|
|
1441
1607
|
cancelRun: (runId, reason) => requestSessionCancellation(sessionKey, runId, reason),
|
|
1608
|
+
steerRun: (runId, text) => requestSessionSteering(sessionKey, runId, text),
|
|
1442
1609
|
getActiveRun: () => getSessionActiveRun(sessionKey),
|
|
1443
1610
|
getActiveRunState: () => getSessionActiveRunState(sessionKey),
|
|
1444
1611
|
getAvailability: () => getSessionAvailability(sessionKey),
|
|
@@ -1866,7 +2033,7 @@ function createAgentCommandFromFeishuCardAction(payload) {
|
|
|
1866
2033
|
const messageId = readNonEmptyString(event.context?.open_message_id);
|
|
1867
2034
|
if (!messageId) return yield* Effect.fail(new InvalidFeishuCardAction("card action message id is missing"));
|
|
1868
2035
|
const value = event.action?.value;
|
|
1869
|
-
if (!isRecord$
|
|
2036
|
+
if (!isRecord$4(value)) return yield* Effect.fail(new UnsupportedFeishuCardAction("unsupported card action value"));
|
|
1870
2037
|
if (value.rivus_action === "resolve_interaction") {
|
|
1871
2038
|
const interactionId = readNonEmptyString(value.interaction_id);
|
|
1872
2039
|
if (!interactionId) return yield* Effect.fail(new InvalidFeishuCardAction("card interaction action requires an interaction id"));
|
|
@@ -1911,7 +2078,7 @@ function readInteractionAction(value) {
|
|
|
1911
2078
|
function readNonEmptyString(value) {
|
|
1912
2079
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
1913
2080
|
}
|
|
1914
|
-
function isRecord$
|
|
2081
|
+
function isRecord$4(value) {
|
|
1915
2082
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1916
2083
|
}
|
|
1917
2084
|
//#endregion
|
|
@@ -1948,10 +2115,7 @@ function encodeSegment(value) {
|
|
|
1948
2115
|
return encodeURIComponent(value);
|
|
1949
2116
|
}
|
|
1950
2117
|
//#endregion
|
|
1951
|
-
//#region src/application/feishu/feishu-message-
|
|
1952
|
-
function readTenantKey(payload) {
|
|
1953
|
-
return payload.event.sender?.tenant_key ?? payload.header?.tenant_key ?? "";
|
|
1954
|
-
}
|
|
2118
|
+
//#region src/application/feishu/feishu-message-content.ts
|
|
1955
2119
|
var UnsupportedFeishuMessage = class extends Error {
|
|
1956
2120
|
messageType;
|
|
1957
2121
|
name = "UnsupportedFeishuMessage";
|
|
@@ -1970,21 +2134,107 @@ var InvalidFeishuMessageContent = class extends Error {
|
|
|
1970
2134
|
this.reason = reason;
|
|
1971
2135
|
}
|
|
1972
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
|
+
}
|
|
1973
2203
|
function createAgentCommandFromFeishuMessage(payload, options) {
|
|
1974
2204
|
return Effect.gen(function* () {
|
|
1975
2205
|
const message = payload.event.message;
|
|
1976
|
-
const normalizedText =
|
|
1977
|
-
yield* validateSkillCommand(normalizedText);
|
|
1978
|
-
const cancel = yield* parseCancelRunCommand(message.message_id, normalizedText);
|
|
2206
|
+
const normalizedText = yield* readNormalizedMessageText(payload, options);
|
|
1979
2207
|
const sessionReference = toSessionReference(payload, options);
|
|
1980
|
-
const
|
|
2208
|
+
const baseSessionKey = yield* createFeishuSessionKey(sessionReference);
|
|
2209
|
+
const reset = parseNewSessionCommand(message.message_id, normalizedText);
|
|
2210
|
+
const cancel = yield* parseCancelRunCommand(message.message_id, normalizedText);
|
|
1981
2211
|
if (cancel) return {
|
|
1982
2212
|
...cancel,
|
|
1983
|
-
sessionKey
|
|
2213
|
+
sessionKey: yield* resolveCurrentSessionKey(baseSessionKey, options.sessionStore)
|
|
1984
2214
|
};
|
|
2215
|
+
if (reset) {
|
|
2216
|
+
yield* validateSkillCommand(reset.prompt);
|
|
2217
|
+
const session = yield* resetSession(baseSessionKey, options.sessionStore);
|
|
2218
|
+
if (reset.prompt.length === 0) return {
|
|
2219
|
+
messageId: message.message_id,
|
|
2220
|
+
previousSessionKey: session.previousSessionKey,
|
|
2221
|
+
sessionKey: session.sessionKey,
|
|
2222
|
+
type: "new_session"
|
|
2223
|
+
};
|
|
2224
|
+
return {
|
|
2225
|
+
command: {
|
|
2226
|
+
sessionKey: session.sessionKey,
|
|
2227
|
+
text: reset.prompt
|
|
2228
|
+
},
|
|
2229
|
+
conversationId: yield* createFeishuConversationId(sessionReference),
|
|
2230
|
+
messageId: message.message_id,
|
|
2231
|
+
type: "prompt"
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
yield* validateSkillCommand(normalizedText);
|
|
1985
2235
|
return {
|
|
1986
2236
|
command: {
|
|
1987
|
-
sessionKey,
|
|
2237
|
+
sessionKey: yield* resolveCurrentSessionKey(baseSessionKey, options.sessionStore),
|
|
1988
2238
|
text: normalizedText
|
|
1989
2239
|
},
|
|
1990
2240
|
conversationId: yield* createFeishuConversationId(sessionReference),
|
|
@@ -1993,32 +2243,62 @@ function createAgentCommandFromFeishuMessage(payload, options) {
|
|
|
1993
2243
|
};
|
|
1994
2244
|
});
|
|
1995
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
|
+
}
|
|
1996
2255
|
function normalizeTrustedBotMention(text, mentions, botOpenId) {
|
|
1997
2256
|
if (!botOpenId || !mentions) return text.trim();
|
|
1998
|
-
|
|
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();
|
|
1999
2259
|
}
|
|
2000
2260
|
function validateSkillCommand(text) {
|
|
2001
2261
|
if (!/^\/skill(?::|\s|$)/.test(text)) return Effect.void;
|
|
2002
2262
|
if (/^\/skill:[a-z0-9][a-z0-9-]*(?:\s[\s\S]*)?$/.test(text)) return Effect.void;
|
|
2003
2263
|
return Effect.fail(new InvalidFeishuMessageContent("skill command must use /skill:<lowercase-name> followed by optional arguments"));
|
|
2004
2264
|
}
|
|
2265
|
+
function parseNewSessionCommand(messageId, text) {
|
|
2266
|
+
const match = /^(?:\/new|\/reset)(?:\s+([\s\S]*))?$/.exec(text.trim());
|
|
2267
|
+
return match ? {
|
|
2268
|
+
messageId,
|
|
2269
|
+
prompt: match[1]?.trim() ?? ""
|
|
2270
|
+
} : void 0;
|
|
2271
|
+
}
|
|
2272
|
+
function resolveCurrentSessionKey(baseSessionKey, store) {
|
|
2273
|
+
return store ? store.current(baseSessionKey).pipe(Effect.mapError((error) => new InvalidFeishuMessageContent(error.message))) : Effect.succeed(baseSessionKey);
|
|
2274
|
+
}
|
|
2275
|
+
function resetSession(baseSessionKey, store) {
|
|
2276
|
+
if (!store) return Effect.fail(new InvalidFeishuMessageContent("new session command is not configured"));
|
|
2277
|
+
return store.reset(baseSessionKey).pipe(Effect.mapError((error) => new InvalidFeishuMessageContent(error.message)));
|
|
2278
|
+
}
|
|
2005
2279
|
function describeFeishuMessageIntake(payload, options) {
|
|
2006
2280
|
return Effect.gen(function* () {
|
|
2007
2281
|
const command = yield* createAgentCommandFromFeishuMessage(payload, options);
|
|
2008
2282
|
const sessionReference = toSessionReference(payload, options);
|
|
2009
|
-
const sessionKey = yield* createFeishuSessionKey(sessionReference);
|
|
2010
2283
|
if (command.type === "cancel_run") return {
|
|
2011
2284
|
commandType: "cancel_run",
|
|
2012
2285
|
messageId: command.messageId,
|
|
2013
2286
|
reason: command.reason,
|
|
2014
|
-
runId: command.runId,
|
|
2015
|
-
sessionKey,
|
|
2287
|
+
...command.runId ? { runId: command.runId } : {},
|
|
2288
|
+
sessionKey: command.sessionKey ?? (yield* createFeishuSessionKey(sessionReference)),
|
|
2289
|
+
sessionReference
|
|
2290
|
+
};
|
|
2291
|
+
if (command.type === "new_session") return {
|
|
2292
|
+
commandType: "new_session",
|
|
2293
|
+
messageId: command.messageId,
|
|
2294
|
+
previousSessionKey: command.previousSessionKey,
|
|
2295
|
+
sessionKey: command.sessionKey,
|
|
2016
2296
|
sessionReference
|
|
2017
2297
|
};
|
|
2018
2298
|
return {
|
|
2019
2299
|
commandType: "prompt",
|
|
2020
2300
|
messageId: command.messageId,
|
|
2021
|
-
sessionKey,
|
|
2301
|
+
sessionKey: command.command.sessionKey,
|
|
2022
2302
|
sessionReference,
|
|
2023
2303
|
text: command.command.text
|
|
2024
2304
|
};
|
|
@@ -2032,66 +2312,94 @@ function toSessionReference(payload, options) {
|
|
|
2032
2312
|
...payload.event.message.thread_id ? { threadId: payload.event.message.thread_id } : {}
|
|
2033
2313
|
};
|
|
2034
2314
|
}
|
|
2035
|
-
function parseTextContent(content) {
|
|
2036
|
-
return Effect.try({
|
|
2037
|
-
try: () => {
|
|
2038
|
-
const parsed = JSON.parse(content);
|
|
2039
|
-
if (typeof parsed.text !== "string") throw new Error("text content is missing");
|
|
2040
|
-
return parsed.text;
|
|
2041
|
-
},
|
|
2042
|
-
catch: (error) => new InvalidFeishuMessageContent(error instanceof Error ? error.message : "invalid JSON content")
|
|
2043
|
-
});
|
|
2044
|
-
}
|
|
2045
|
-
function parsePostContent(content) {
|
|
2046
|
-
return Effect.try({
|
|
2047
|
-
try: () => {
|
|
2048
|
-
const parsed = JSON.parse(content);
|
|
2049
|
-
const paragraphs = Array.isArray(parsed.content_v2) ? parsed.content_v2 : parsed.content;
|
|
2050
|
-
if (!Array.isArray(paragraphs)) throw new Error("post content is missing");
|
|
2051
|
-
const body = paragraphs.map((paragraph) => {
|
|
2052
|
-
if (!Array.isArray(paragraph)) throw new Error("post paragraph is invalid");
|
|
2053
|
-
return paragraph.map((element) => {
|
|
2054
|
-
if (element === null || typeof element !== "object") return "";
|
|
2055
|
-
if ("text" in element && typeof element.text === "string") return element.text;
|
|
2056
|
-
if ("user_name" in element && typeof element.user_name === "string") return `@${element.user_name}`;
|
|
2057
|
-
return "";
|
|
2058
|
-
}).join("");
|
|
2059
|
-
}).join("\n").trim();
|
|
2060
|
-
const text = [typeof parsed.title === "string" ? parsed.title.trim() : "", body].filter((part) => part.length > 0).join("\n");
|
|
2061
|
-
if (!text) throw new Error("post content has no text");
|
|
2062
|
-
return text;
|
|
2063
|
-
},
|
|
2064
|
-
catch: (error) => new InvalidFeishuMessageContent(error instanceof Error ? error.message : "invalid JSON content")
|
|
2065
|
-
});
|
|
2066
|
-
}
|
|
2067
2315
|
function parseCancelRunCommand(messageId, text) {
|
|
2068
2316
|
const trimmed = text.trim();
|
|
2069
2317
|
if (!/^\/cancel(?:\s|$)/.test(trimmed)) return Effect.succeed(void 0);
|
|
2070
|
-
const match = /^\/cancel
|
|
2071
|
-
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"));
|
|
2072
2320
|
return Effect.succeed({
|
|
2073
2321
|
messageId,
|
|
2074
2322
|
reason: "Feishu cancel command",
|
|
2075
|
-
runId: match[1],
|
|
2323
|
+
...match[1] ? { runId: match[1] } : {},
|
|
2076
2324
|
type: "cancel_run"
|
|
2077
2325
|
});
|
|
2078
2326
|
}
|
|
2079
2327
|
//#endregion
|
|
2080
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 = "当前没有正在运行的任务。";
|
|
2081
2332
|
function createFeishuAgentDaemon(options) {
|
|
2333
|
+
const sessionStore = options.sessionStore ?? createFeishuSessionStore();
|
|
2082
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
|
+
}),
|
|
2083
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))),
|
|
2084
2346
|
promptWithUpdates: (command, onUpdate) => options.harness.promptWithUpdates(command, onUpdate)
|
|
2085
2347
|
};
|
|
2086
2348
|
const seenCardActionTokens = /* @__PURE__ */ new Set();
|
|
2087
2349
|
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
2088
2350
|
const cancelRun = (command) => Effect.gen(function* () {
|
|
2089
|
-
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;
|
|
2090
2360
|
return {
|
|
2091
2361
|
cancelled,
|
|
2092
2362
|
messageId: command.messageId,
|
|
2093
2363
|
...cancelled ? {} : { reason: "not_active" },
|
|
2094
|
-
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
|
|
2095
2403
|
};
|
|
2096
2404
|
});
|
|
2097
2405
|
return {
|
|
@@ -2106,6 +2414,43 @@ function createFeishuAgentDaemon(options) {
|
|
|
2106
2414
|
if (inbound.type === "cancel_run" && inbound.token) seenCardActionTokens.add(inbound.token);
|
|
2107
2415
|
return result;
|
|
2108
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
|
+
}),
|
|
2109
2454
|
handleMessage: (payload, handleOptions) => {
|
|
2110
2455
|
let dedupeMessageId;
|
|
2111
2456
|
const sideEffectsDisabled = handleOptions?.sideEffects === "disabled";
|
|
@@ -2116,22 +2461,32 @@ function createFeishuAgentDaemon(options) {
|
|
|
2116
2461
|
return Effect.gen(function* () {
|
|
2117
2462
|
const inbound = yield* createAgentCommandFromFeishuMessage(payload, {
|
|
2118
2463
|
agentId: options.agentId,
|
|
2119
|
-
...options.botOpenId ? { botOpenId: options.botOpenId } : {}
|
|
2464
|
+
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
2465
|
+
sessionStore
|
|
2120
2466
|
});
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
reason: "duplicate",
|
|
2124
|
-
skipped: true
|
|
2125
|
-
};
|
|
2467
|
+
const duplicate = duplicateResult(inbound.messageId);
|
|
2468
|
+
if (duplicate) return duplicate;
|
|
2126
2469
|
if (options.dedupe) {
|
|
2127
2470
|
seenMessageIds.add(inbound.messageId);
|
|
2128
2471
|
dedupeMessageId = inbound.messageId;
|
|
2129
2472
|
}
|
|
2130
|
-
if (inbound.type === "cancel_run")
|
|
2473
|
+
if (inbound.type === "cancel_run") {
|
|
2474
|
+
const result = yield* cancelRun(inbound);
|
|
2475
|
+
if (!sideEffectsDisabled) yield* replyToCancellation(inbound.messageId, result.cancelled);
|
|
2476
|
+
return result;
|
|
2477
|
+
}
|
|
2478
|
+
if (inbound.type === "new_session") return yield* resetSession(inbound, !sideEffectsDisabled);
|
|
2479
|
+
const steered = yield* steerPrompt(inbound, !sideEffectsDisabled);
|
|
2480
|
+
if (steered) return steered;
|
|
2131
2481
|
const projector = createFeishuStreamProjector();
|
|
2132
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;
|
|
2133
2487
|
const command = {
|
|
2134
2488
|
...inbound.command,
|
|
2489
|
+
text: promptText,
|
|
2135
2490
|
...options.resolveInvocation ? { invocation: options.resolveInvocation(payload, inbound.conversationId) } : {}
|
|
2136
2491
|
};
|
|
2137
2492
|
const result = yield* execution.promptWithUpdates(command, (update) => Effect.gen(function* () {
|
|
@@ -2204,6 +2559,32 @@ function createFeishuMessageQueue(options) {
|
|
|
2204
2559
|
pending: repository.pendingCount(),
|
|
2205
2560
|
reason: admission
|
|
2206
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
|
+
}
|
|
2207
2588
|
return {
|
|
2208
2589
|
accepted: true,
|
|
2209
2590
|
messageId,
|
|
@@ -2222,6 +2603,22 @@ function createFeishuMessageQueue(options) {
|
|
|
2222
2603
|
handled: false,
|
|
2223
2604
|
reason: "empty"
|
|
2224
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
|
+
}
|
|
2225
2622
|
const result = yield* options.handleMessage(message.payload, message.options).pipe(Effect.catchAll((error) => repository.fail({
|
|
2226
2623
|
availableAt: new Date(Date.parse(now()) + retryDelayMs).toISOString(),
|
|
2227
2624
|
failedAt: now(),
|
|
@@ -2644,7 +3041,10 @@ function toast(type, enUs, zhCn) {
|
|
|
2644
3041
|
//#endregion
|
|
2645
3042
|
//#region src/infrastructure/feishu/feishu-event-handlers.ts
|
|
2646
3043
|
function createFeishuEventHandlers(options) {
|
|
2647
|
-
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
|
+
} };
|
|
2648
3048
|
if (options.cardActions) return {
|
|
2649
3049
|
...handlers,
|
|
2650
3050
|
"card.action.trigger": (payload) => Effect.runPromise(options.cardActions.handleCardAction(payload).pipe(Effect.map(createFeishuCardActionCallbackResponse), Effect.catchAll(() => Effect.succeed(createFeishuCardActionErrorResponse()))))
|
|
@@ -2664,6 +3064,7 @@ function normalizeReceiveMessagePayload(payload) {
|
|
|
2664
3064
|
//#endregion
|
|
2665
3065
|
//#region src/composition/feishu-agent-runtime.ts
|
|
2666
3066
|
function createFeishuAgentRuntime(options) {
|
|
3067
|
+
const sessionStore = options.sessionStore ?? createFeishuSessionStore();
|
|
2667
3068
|
const harness = createAgentHarness({
|
|
2668
3069
|
clock: options.clock,
|
|
2669
3070
|
...options.eventSinks ? { eventSinks: options.eventSinks } : {},
|
|
@@ -2674,21 +3075,24 @@ function createFeishuAgentRuntime(options) {
|
|
|
2674
3075
|
runIds: options.runIds
|
|
2675
3076
|
});
|
|
2676
3077
|
const daemon = createFeishuAgentDaemon({
|
|
3078
|
+
...options.acknowledgeSteering ? { acknowledgeSteering: options.acknowledgeSteering } : {},
|
|
2677
3079
|
agentId: options.agentId,
|
|
2678
3080
|
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
3081
|
+
dedupe: true,
|
|
2679
3082
|
...options.finalizeRun ? { finalizeRun: options.finalizeRun } : {},
|
|
2680
3083
|
harness,
|
|
2681
3084
|
...options.prepareRun ? { prepareRun: options.prepareRun } : {},
|
|
2682
|
-
|
|
3085
|
+
...options.promptContext ? { promptContext: options.promptContext } : {},
|
|
3086
|
+
publish: options.publish,
|
|
3087
|
+
...options.reply ? { reply: options.reply } : {},
|
|
3088
|
+
sessionStore
|
|
2683
3089
|
});
|
|
2684
3090
|
let lastAccepted;
|
|
2685
3091
|
let lastHandled;
|
|
2686
3092
|
const queue = createFeishuMessageQueue({
|
|
3093
|
+
handleControl: (payload) => daemon.trySteerMessage(payload),
|
|
2687
3094
|
handleMessage: (payload, handleOptions) => {
|
|
2688
|
-
const effect = daemon.handleMessage(payload, handleOptions).pipe(Effect.tap((result) =>
|
|
2689
|
-
agentId: options.agentId,
|
|
2690
|
-
...options.botOpenId ? { botOpenId: options.botOpenId } : {}
|
|
2691
|
-
}).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* () {
|
|
2692
3096
|
const observedAt = yield* options.clock.now;
|
|
2693
3097
|
lastHandled = {
|
|
2694
3098
|
intake,
|
|
@@ -2700,6 +3104,11 @@ function createFeishuAgentRuntime(options) {
|
|
|
2700
3104
|
return options.periodicFlush ? options.periodicFlush.withPeriodicFlush(effect) : effect;
|
|
2701
3105
|
},
|
|
2702
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
|
+
}) } : {},
|
|
2703
3112
|
shouldRetryError: isRetryableFeishuMessageError
|
|
2704
3113
|
});
|
|
2705
3114
|
const worker = createFeishuMessageWorker({ queue });
|
|
@@ -2716,7 +3125,10 @@ function createFeishuAgentRuntime(options) {
|
|
|
2716
3125
|
drainOne: () => queue.drainOne(),
|
|
2717
3126
|
handlers: createFeishuEventHandlers({
|
|
2718
3127
|
cardActions: daemon,
|
|
2719
|
-
queue:
|
|
3128
|
+
queue: {
|
|
3129
|
+
...observedQueue,
|
|
3130
|
+
pending: () => queue.pending()
|
|
3131
|
+
}
|
|
2720
3132
|
}),
|
|
2721
3133
|
harness,
|
|
2722
3134
|
pending: () => queue.pending(),
|
|
@@ -2726,21 +3138,75 @@ function createFeishuAgentRuntime(options) {
|
|
|
2726
3138
|
...lastHandled ? { lastHandled } : {}
|
|
2727
3139
|
}),
|
|
2728
3140
|
replayReceiveMessage: (payload, replayOptions) => Effect.gen(function* () {
|
|
2729
|
-
const
|
|
3141
|
+
const intakeOptions = {
|
|
2730
3142
|
agentId: options.agentId,
|
|
2731
|
-
...options.botOpenId ? { botOpenId: options.botOpenId } : {}
|
|
2732
|
-
|
|
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;
|
|
2733
3169
|
return {
|
|
2734
|
-
accepted
|
|
2735
|
-
drained
|
|
3170
|
+
accepted,
|
|
3171
|
+
drained,
|
|
2736
3172
|
intake
|
|
2737
3173
|
};
|
|
2738
3174
|
}),
|
|
2739
3175
|
worker
|
|
2740
3176
|
};
|
|
2741
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
|
+
}
|
|
2742
3208
|
function isRetryableFeishuMessageError(error) {
|
|
2743
|
-
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);
|
|
2744
3210
|
}
|
|
2745
3211
|
function summarizeReceiveMessage(payload) {
|
|
2746
3212
|
const tenantKey = readTenantKey(payload);
|
|
@@ -2751,21 +3217,47 @@ function summarizeReceiveMessage(payload) {
|
|
|
2751
3217
|
...payload.event.message.thread_id ? { threadId: payload.event.message.thread_id } : {}
|
|
2752
3218
|
};
|
|
2753
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
|
+
}
|
|
2754
3255
|
//#endregion
|
|
2755
3256
|
//#region src/application/agent/agent-history.ts
|
|
2756
3257
|
function restoreAgentHistory(eventLog) {
|
|
2757
3258
|
return eventLog.readAll().pipe(Effect.map(replayAgentHistory));
|
|
2758
3259
|
}
|
|
2759
3260
|
//#endregion
|
|
2760
|
-
//#region src/infrastructure/feishu/feishu-periodic-flush.ts
|
|
2761
|
-
function createFeishuPeriodicFlush(options) {
|
|
2762
|
-
const loop = Effect.forever(Effect.gen(function* () {
|
|
2763
|
-
yield* options.sleep(options.intervalMs);
|
|
2764
|
-
yield* options.flush();
|
|
2765
|
-
}));
|
|
2766
|
-
return { withPeriodicFlush: (effect) => Effect.raceFirst(effect, loop) };
|
|
2767
|
-
}
|
|
2768
|
-
//#endregion
|
|
2769
3261
|
//#region src/infrastructure/feishu/feishu-websocket-daemon.ts
|
|
2770
3262
|
function createLazyFeishuWebSocketEventDispatcher(create) {
|
|
2771
3263
|
let dispatcher;
|
|
@@ -2839,7 +3331,7 @@ function createFeishuAgentRunCard(input) {
|
|
|
2839
3331
|
content: `_${presentation.note}_`,
|
|
2840
3332
|
tag: "markdown"
|
|
2841
3333
|
},
|
|
2842
|
-
...input.status === "running" ? [{
|
|
3334
|
+
...input.status === "running" && input.cancelButton !== false ? [{
|
|
2843
3335
|
behaviors: [{
|
|
2844
3336
|
type: "callback",
|
|
2845
3337
|
value: {
|
|
@@ -2874,15 +3366,15 @@ function resolveRunPresentation(input) {
|
|
|
2874
3366
|
switch (input.status) {
|
|
2875
3367
|
case "running": return {
|
|
2876
3368
|
content: input.text || "正在思考…",
|
|
2877
|
-
note: input.generation ? "接续上一条消息,回答将实时更新" : "回答将实时更新",
|
|
3369
|
+
note: input.cancelButton === false ? `回答将实时更新;如需停止,请发送 \`/cancel ${input.runId}\`` : input.generation ? "接续上一条消息,回答将实时更新" : "回答将实时更新",
|
|
2878
3370
|
template: "blue",
|
|
2879
3371
|
title: input.generation ? "正在处理(续)" : "正在处理"
|
|
2880
3372
|
};
|
|
2881
3373
|
case "handoff": return {
|
|
2882
|
-
content: "
|
|
2883
|
-
note: "
|
|
3374
|
+
content: "这是同一任务的续卡。任务仍在后台处理,进度将继续显示在下一条消息。",
|
|
3375
|
+
note: "本卡片已停止更新;请查看下一条消息,不会创建第二个任务",
|
|
2884
3376
|
template: "blue",
|
|
2885
|
-
title: "
|
|
3377
|
+
title: "同一任务已续卡"
|
|
2886
3378
|
};
|
|
2887
3379
|
case "completed": return {
|
|
2888
3380
|
content: input.text || "已完成。",
|
|
@@ -3190,6 +3682,7 @@ function createFeishuCardTargetPreparation(options) {
|
|
|
3190
3682
|
function createConfiguredFeishuCardKitTargetCreator(options) {
|
|
3191
3683
|
return createFeishuCardKitOpenApiTargetCreator({
|
|
3192
3684
|
baseUrl: options.config.feishu.baseUrl,
|
|
3685
|
+
...options.cancelButton === void 0 ? {} : { cancelButton: options.cancelButton },
|
|
3193
3686
|
client: options.client,
|
|
3194
3687
|
elementId: options.elementId ?? DEFAULT_ELEMENT_ID,
|
|
3195
3688
|
...options.initialContent === void 0 ? {} : { initialContent: options.initialContent },
|
|
@@ -3210,6 +3703,7 @@ function createFeishuCardKitOpenApiTargetCreator(options) {
|
|
|
3210
3703
|
runId: run.runId,
|
|
3211
3704
|
sessionKey: run.sessionKey,
|
|
3212
3705
|
status: "running",
|
|
3706
|
+
...options.cancelButton === void 0 ? {} : { cancelButton: options.cancelButton },
|
|
3213
3707
|
text: createOptions?.initialContent ?? initialContent
|
|
3214
3708
|
})),
|
|
3215
3709
|
type: "card_json"
|
|
@@ -3236,10 +3730,10 @@ function createFeishuCardKitOpenApiTargetCreator(options) {
|
|
|
3236
3730
|
}
|
|
3237
3731
|
function readStringData(response, field) {
|
|
3238
3732
|
const data = response.body.data;
|
|
3239
|
-
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}`));
|
|
3240
3734
|
return Effect.succeed(data[field]);
|
|
3241
3735
|
}
|
|
3242
|
-
function isRecord$
|
|
3736
|
+
function isRecord$3(value) {
|
|
3243
3737
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3244
3738
|
}
|
|
3245
3739
|
//#endregion
|
|
@@ -3259,6 +3753,7 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
|
|
|
3259
3753
|
sleep: options.sleep
|
|
3260
3754
|
});
|
|
3261
3755
|
const creator = createConfiguredFeishuCardKitTargetCreator({
|
|
3756
|
+
...options.cancelButton === void 0 ? {} : { cancelButton: options.cancelButton },
|
|
3262
3757
|
client: options.client,
|
|
3263
3758
|
config: options.config,
|
|
3264
3759
|
...options.elementId === void 0 ? {} : { elementId: options.elementId },
|
|
@@ -3284,6 +3779,12 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
|
|
|
3284
3779
|
rollover: cardRollover,
|
|
3285
3780
|
sleep: options.sleep
|
|
3286
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
|
+
});
|
|
3287
3788
|
return {
|
|
3288
3789
|
prepareRun: createFeishuCardTargetPreparation({
|
|
3289
3790
|
createTarget: (run) => creator.createTarget(run),
|
|
@@ -3292,15 +3793,20 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
|
|
|
3292
3793
|
rollover: cardRollover,
|
|
3293
3794
|
supervisor,
|
|
3294
3795
|
transport: {
|
|
3295
|
-
running: () => supervisor.running(),
|
|
3796
|
+
running: () => supervisor.running() && streaming.running(),
|
|
3296
3797
|
start: async () => {
|
|
3297
3798
|
await Effect.runPromise(supervisor.recover().pipe(Effect.catchAll((error) => {
|
|
3298
3799
|
const reported = options.onError?.(error);
|
|
3299
3800
|
return reported instanceof Promise ? Effect.promise(() => reported) : Effect.void;
|
|
3300
3801
|
})));
|
|
3301
3802
|
supervisor.start();
|
|
3803
|
+
streaming.start();
|
|
3302
3804
|
},
|
|
3303
|
-
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
|
+
}
|
|
3304
3810
|
}
|
|
3305
3811
|
};
|
|
3306
3812
|
}
|
|
@@ -3308,9 +3814,74 @@ function resolveSupervisorInterval(leaseMs) {
|
|
|
3308
3814
|
return Math.max(MIN_SUPERVISOR_INTERVAL_MS, Math.min(MAX_SUPERVISOR_INTERVAL_MS, Math.floor(leaseMs / 10)));
|
|
3309
3815
|
}
|
|
3310
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
|
|
3311
3883
|
//#region src/composition/rivus-daemon-bootstrap.ts
|
|
3312
3884
|
const DEFAULT_WORKER_INTERVAL_MS$1 = 250;
|
|
3313
|
-
const DEFAULT_RUN_TIMEOUT_MS = 900 * 1e3;
|
|
3314
3885
|
function restoreConfiguredRivusDaemonBootstrap(options) {
|
|
3315
3886
|
return options.eventLog.readAll().pipe(Effect.map((events) => createConfiguredRivusDaemonBootstrap({
|
|
3316
3887
|
...options,
|
|
@@ -3324,19 +3895,33 @@ function createConfiguredRivusDaemonBootstrap(options) {
|
|
|
3324
3895
|
});
|
|
3325
3896
|
const cardRollover = createConfiguredFeishuCardRolloverRuntime({
|
|
3326
3897
|
cardTargets: options.cardTargets,
|
|
3898
|
+
cancelButton: false,
|
|
3327
3899
|
client: openApiClient,
|
|
3328
3900
|
clock: options.clock,
|
|
3329
3901
|
config: options.config,
|
|
3330
3902
|
...options.cardLedger ? { ledger: options.cardLedger } : {},
|
|
3903
|
+
...options.flushIntervalMs === void 0 ? {} : { flushIntervalMs: options.flushIntervalMs },
|
|
3331
3904
|
...options.observeCardRollover ? { observe: options.observeCardRollover } : {},
|
|
3332
3905
|
...options.onWorkerError ? { onError: options.onWorkerError } : {},
|
|
3333
3906
|
sleep: options.sleep,
|
|
3334
3907
|
...options.cardRolloverIntervalMs === void 0 ? {} : { supervisorIntervalMs: options.cardRolloverIntervalMs }
|
|
3335
3908
|
});
|
|
3336
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
|
+
});
|
|
3337
3922
|
const prepareRun = (run) => cardRollover.prepareRun(run);
|
|
3338
|
-
const periodicFlush = createPeriodicFlush(options, publisher);
|
|
3339
3923
|
const runtime = createFeishuAgentRuntime({
|
|
3924
|
+
acknowledgeSteering: (messageId) => reactions.add(messageId),
|
|
3340
3925
|
agentId: options.config.agentId,
|
|
3341
3926
|
...options.botOpenId ? { botOpenId: options.botOpenId } : {},
|
|
3342
3927
|
clock: options.clock,
|
|
@@ -3346,10 +3931,11 @@ function createConfiguredRivusDaemonBootstrap(options) {
|
|
|
3346
3931
|
...options.initialEvents ? { initialEvents: options.initialEvents } : {},
|
|
3347
3932
|
...options.initialRunStates ? { initialRunStates: options.initialRunStates } : {},
|
|
3348
3933
|
loop: options.loop,
|
|
3349
|
-
periodicFlush,
|
|
3350
3934
|
prepareRun,
|
|
3935
|
+
promptContext,
|
|
3351
3936
|
publish: (action) => publisher.publish(action),
|
|
3352
|
-
|
|
3937
|
+
reply: (messageId, text) => replies.reply(messageId, text),
|
|
3938
|
+
...options.runTimeoutMs === void 0 ? {} : { runTimeoutMs: options.runTimeoutMs },
|
|
3353
3939
|
runIds: options.runIds
|
|
3354
3940
|
});
|
|
3355
3941
|
const websocketTransport = createFeishuWebSocketDaemon({
|
|
@@ -3404,13 +3990,6 @@ function createConfiguredRivusDaemonBootstrap(options) {
|
|
|
3404
3990
|
workerLoop
|
|
3405
3991
|
};
|
|
3406
3992
|
}
|
|
3407
|
-
function createPeriodicFlush(options, publisher) {
|
|
3408
|
-
return createFeishuPeriodicFlush({
|
|
3409
|
-
flush: () => publisher.flush(),
|
|
3410
|
-
intervalMs: options.flushIntervalMs ?? options.config.feishu.streamMinIntervalMs,
|
|
3411
|
-
sleep: options.sleep
|
|
3412
|
-
});
|
|
3413
|
-
}
|
|
3414
3993
|
//#endregion
|
|
3415
3994
|
//#region src/infrastructure/persistence/jsonl-agent-event-log.ts
|
|
3416
3995
|
var AgentEventLogStoreError = class {
|
|
@@ -3447,7 +4026,7 @@ function parseJsonlEvents(raw) {
|
|
|
3447
4026
|
return raw.split("\n").filter((line) => line.trim().length > 0).map((line, index) => parseAgentDomainEvent(JSON.parse(line), index + 1));
|
|
3448
4027
|
}
|
|
3449
4028
|
function parseAgentDomainEvent(value, lineNumber) {
|
|
3450
|
-
if (!isRecord$
|
|
4029
|
+
if (!isRecord$5(value) || typeof value.type !== "string") throw new Error(`Invalid agent event at line ${lineNumber}.`);
|
|
3451
4030
|
const occurredAt = readDate(value, "occurredAt", lineNumber);
|
|
3452
4031
|
switch (value.type) {
|
|
3453
4032
|
case "agent_run_accepted": return {
|
|
@@ -3618,7 +4197,7 @@ function readOptionalNumber(value, field, lineNumber) {
|
|
|
3618
4197
|
}
|
|
3619
4198
|
function readRecord$1(value, field, lineNumber) {
|
|
3620
4199
|
const fieldValue = value[field];
|
|
3621
|
-
if (!isRecord$
|
|
4200
|
+
if (!isRecord$5(fieldValue)) throw new Error(`Invalid agent event field '${field}' at line ${lineNumber}.`);
|
|
3622
4201
|
return fieldValue;
|
|
3623
4202
|
}
|
|
3624
4203
|
function readOptionalString(value, field, lineNumber) {
|
|
@@ -4626,13 +5205,13 @@ async function saveChains(filePath, chains) {
|
|
|
4626
5205
|
}
|
|
4627
5206
|
}
|
|
4628
5207
|
function parseChains(value) {
|
|
4629
|
-
if (!isRecord$
|
|
5208
|
+
if (!isRecord$5(value)) throw new Error("Feishu card presentation file must contain a JSON object.");
|
|
4630
5209
|
if (value.version === void 0) return Object.entries(value).map(([runId, target]) => parseLegacyChain(runId, target));
|
|
4631
|
-
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)}`);
|
|
4632
5211
|
return Object.entries(value.runs).map(([runId, chain]) => parseChain(runId, chain));
|
|
4633
5212
|
}
|
|
4634
5213
|
function parseLegacyChain(runId, target) {
|
|
4635
|
-
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}'.`);
|
|
4636
5215
|
return createCardPresentationChain({
|
|
4637
5216
|
cardId: target.cardId,
|
|
4638
5217
|
createdAt: LEGACY_PRESENTATION_TIMESTAMP,
|
|
@@ -4644,9 +5223,9 @@ function parseLegacyChain(runId, target) {
|
|
|
4644
5223
|
});
|
|
4645
5224
|
}
|
|
4646
5225
|
function parseChain(runId, value) {
|
|
4647
|
-
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}'.`);
|
|
4648
5227
|
const chain = value;
|
|
4649
|
-
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}'.`);
|
|
4650
5229
|
activeCardPresentation(chain);
|
|
4651
5230
|
return chain;
|
|
4652
5231
|
}
|
|
@@ -5018,6 +5597,15 @@ function readRedactedString(serialized) {
|
|
|
5018
5597
|
return typeof value === "string" ? value : serialized;
|
|
5019
5598
|
}
|
|
5020
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
|
|
5021
5609
|
//#region src/testing/fakes.ts
|
|
5022
5610
|
function createFixedClock(now) {
|
|
5023
5611
|
return { now: Effect.succeed(now) };
|
|
@@ -5447,7 +6035,7 @@ async function load$1(filePath) {
|
|
|
5447
6035
|
for (const [index, line] of raw.split("\n").entries()) {
|
|
5448
6036
|
if (!line.trim()) continue;
|
|
5449
6037
|
const envelope = JSON.parse(line);
|
|
5450
|
-
if (!isRecord$
|
|
6038
|
+
if (!isRecord$5(envelope) || envelope.version !== 1 || !isOperationRecord(envelope.record)) throw new Error(`invalid tool operation snapshot at line ${index + 1}`);
|
|
5451
6039
|
const record = envelope.record;
|
|
5452
6040
|
const previous = latest.get(record.operationId);
|
|
5453
6041
|
if (record.revision !== (previous?.revision ?? 0) + 1) throw new Error(`invalid tool operation revision at line ${index + 1}`);
|
|
@@ -5470,7 +6058,7 @@ function isValidTransition$1(previous, next) {
|
|
|
5470
6058
|
}
|
|
5471
6059
|
}
|
|
5472
6060
|
function isOperationRecord(value) {
|
|
5473
|
-
if (!isRecord$
|
|
6061
|
+
if (!isRecord$5(value) || !isRecord$5(value.binding) || !isRecord$5(value.state)) return false;
|
|
5474
6062
|
const binding = value.binding;
|
|
5475
6063
|
const state = value.state;
|
|
5476
6064
|
if (typeof value.operationId !== "string" || !Number.isInteger(value.revision) || value.revision < 1) return false;
|
|
@@ -5501,7 +6089,7 @@ function isStableJson(value) {
|
|
|
5501
6089
|
}
|
|
5502
6090
|
}
|
|
5503
6091
|
function isReconciliation(value) {
|
|
5504
|
-
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");
|
|
5505
6093
|
}
|
|
5506
6094
|
//#endregion
|
|
5507
6095
|
//#region src/infrastructure/persistence/jsonl-recovery-control.ts
|
|
@@ -5538,14 +6126,20 @@ function isMissingDirectory(error) {
|
|
|
5538
6126
|
function createAgentHarnessPooledRuntime(harness) {
|
|
5539
6127
|
return {
|
|
5540
6128
|
cancel: (input) => Effect.runPromise(harness.forSession(input.sessionKey).cancelRun(input.runId, input.reason)),
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
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
|
+
}
|
|
5549
6143
|
};
|
|
5550
6144
|
}
|
|
5551
6145
|
//#endregion
|
|
@@ -5558,20 +6152,31 @@ function createFeishuDeploymentEndpoint(options) {
|
|
|
5558
6152
|
shutdownTimeoutMs: options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS
|
|
5559
6153
|
});
|
|
5560
6154
|
const daemon = createFeishuAgentDaemon({
|
|
6155
|
+
...options.acknowledgeSteering ? { acknowledgeSteering: options.acknowledgeSteering } : {},
|
|
5561
6156
|
agentId: options.agentId,
|
|
5562
6157
|
botOpenId: options.botOpenId,
|
|
6158
|
+
dedupe: true,
|
|
5563
6159
|
execution: execution.port,
|
|
5564
6160
|
...options.finalizeRun ? { finalizeRun: options.finalizeRun } : {},
|
|
5565
6161
|
...options.interactions ? { interactions: options.interactions } : {},
|
|
5566
6162
|
...options.prepareRun ? { prepareRun: options.prepareRun } : {},
|
|
6163
|
+
...options.promptContext ? { promptContext: options.promptContext } : {},
|
|
5567
6164
|
publish: options.publish,
|
|
5568
6165
|
...options.publishRunUpdate ? { publishRunUpdate: options.publishRunUpdate } : {},
|
|
6166
|
+
...options.reply ? { reply: options.reply } : {},
|
|
6167
|
+
...options.sessionStore ? { sessionStore: options.sessionStore } : {},
|
|
5569
6168
|
...options.endpointId ? { resolveInvocation: (payload, conversationId) => createInvocation(payload, options.endpointId, options.memoryTenantId, conversationId, options.projectSpaceId) } : {}
|
|
5570
6169
|
});
|
|
5571
6170
|
const queue = createFeishuMessageQueue({
|
|
6171
|
+
handleControl: (payload) => daemon.trySteerMessage(payload),
|
|
5572
6172
|
handleMessage: (payload, handleOptions) => daemon.handleMessage(payload, handleOptions),
|
|
5573
6173
|
...options.inboxRepository ? { repository: options.inboxRepository } : {},
|
|
5574
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) } : {},
|
|
5575
6180
|
shouldRetryError: isRetryableFeishuMessageError
|
|
5576
6181
|
});
|
|
5577
6182
|
const worker = createFeishuMessageWorker({
|
|
@@ -5589,14 +6194,17 @@ function createFeishuDeploymentEndpoint(options) {
|
|
|
5589
6194
|
eventDispatcher: options.eventDispatcher,
|
|
5590
6195
|
handlers: createFeishuEventHandlers({
|
|
5591
6196
|
cardActions: daemon,
|
|
5592
|
-
queue: {
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
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
|
+
}
|
|
5600
6208
|
})
|
|
5601
6209
|
});
|
|
5602
6210
|
return {
|
|
@@ -5637,6 +6245,10 @@ function createDeploymentExecution(options) {
|
|
|
5637
6245
|
const activeHandles = /* @__PURE__ */ new Set();
|
|
5638
6246
|
const activeRuns = /* @__PURE__ */ new Map();
|
|
5639
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
|
+
};
|
|
5640
6252
|
const requestShutdownCancellation = (runId, sessionKey) => {
|
|
5641
6253
|
const current = cancellationRequests.get(runId);
|
|
5642
6254
|
if (current) return current;
|
|
@@ -5681,6 +6293,19 @@ function createDeploymentExecution(options) {
|
|
|
5681
6293
|
if (errors.length > 1) throw new AggregateError(errors, "Active Agent run cancellation failed");
|
|
5682
6294
|
},
|
|
5683
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
|
+
}),
|
|
5684
6309
|
cancelRun: (input) => input.sessionKey === void 0 ? Effect.succeed(false) : Effect.tryPromise({
|
|
5685
6310
|
try: () => options.cancel({
|
|
5686
6311
|
reason: input.reason,
|
|
@@ -5689,6 +6314,23 @@ function createDeploymentExecution(options) {
|
|
|
5689
6314
|
}),
|
|
5690
6315
|
catch: (error) => error
|
|
5691
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
|
+
}),
|
|
5692
6334
|
promptWithUpdates: (command, onUpdate) => Effect.tryPromise({
|
|
5693
6335
|
try: async () => {
|
|
5694
6336
|
const sessionKey = namespaceSessionKey(options.sessionNamespace, command.sessionKey);
|
|
@@ -7104,8 +7746,8 @@ async function loadJsonlSnapshotStore(options) {
|
|
|
7104
7746
|
}
|
|
7105
7747
|
throw new JsonlSnapshotStoreCorrupted(`${options.errorLabel} JSONL at line ${index + 1} is not valid JSON`);
|
|
7106
7748
|
}
|
|
7107
|
-
if (!isRecord$
|
|
7108
|
-
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)) {
|
|
7109
7751
|
if (!Array.isArray(parsed.snapshot.records)) throw new JsonlSnapshotStoreCorrupted(`invalid ${options.errorLabel} snapshot at line ${index + 1}`);
|
|
7110
7752
|
latest.clear();
|
|
7111
7753
|
for (const value of parsed.snapshot.records) {
|
|
@@ -7169,7 +7811,7 @@ function validateSessionSequence(previous, record, _lineNumber) {
|
|
|
7169
7811
|
return isValidBackgroundSessionTransition(previous, record);
|
|
7170
7812
|
}
|
|
7171
7813
|
function isBackgroundSessionState(value) {
|
|
7172
|
-
if (!isRecord$
|
|
7814
|
+
if (!isRecord$5(value)) return false;
|
|
7173
7815
|
const phases = [
|
|
7174
7816
|
"queued",
|
|
7175
7817
|
"running",
|
|
@@ -7187,14 +7829,14 @@ function isBackgroundSessionState(value) {
|
|
|
7187
7829
|
if (!Number.isInteger(value.revision) || value.revision < 1) return false;
|
|
7188
7830
|
if (typeof value.parentRunId !== "string" || value.parentRunId.trim() === "") return false;
|
|
7189
7831
|
if (typeof value.sourceMessageId !== "string" || value.sourceMessageId.trim() === "") return false;
|
|
7190
|
-
if (!isRecord$
|
|
7832
|
+
if (!isRecord$5(value.origin)) return false;
|
|
7191
7833
|
const origin = value.origin;
|
|
7192
7834
|
if (typeof origin.endpointId !== "string" || origin.endpointId.trim() === "") return false;
|
|
7193
7835
|
if (typeof origin.tenantKey !== "string") return false;
|
|
7194
7836
|
if (!Array.isArray(origin.allowedActorOpenIds) || origin.allowedActorOpenIds.some((id) => typeof id !== "string")) return false;
|
|
7195
7837
|
if (origin.conversationId !== void 0 && typeof origin.conversationId !== "string") return false;
|
|
7196
|
-
if (origin.memory !== void 0 && !isRecord$
|
|
7197
|
-
if (!isRecord$
|
|
7838
|
+
if (origin.memory !== void 0 && !isRecord$5(origin.memory)) return false;
|
|
7839
|
+
if (!isRecord$5(value.authority)) return false;
|
|
7198
7840
|
const authority = value.authority;
|
|
7199
7841
|
if (typeof authority.agentId !== "string" || authority.agentId.trim() === "") return false;
|
|
7200
7842
|
if (typeof authority.profileRevision !== "string" || authority.profileRevision.trim() === "") return false;
|
|
@@ -7217,17 +7859,17 @@ function isBackgroundSessionState(value) {
|
|
|
7217
7859
|
if (typeof value.updatedAt !== "string" || !isIso(value.updatedAt)) return false;
|
|
7218
7860
|
if (value.lease !== void 0 && !isLease(value.lease)) return false;
|
|
7219
7861
|
if (value.cancellation !== void 0) {
|
|
7220
|
-
if (!isRecord$
|
|
7862
|
+
if (!isRecord$5(value.cancellation)) return false;
|
|
7221
7863
|
if (typeof value.cancellation.reason !== "string" || !isIso(value.cancellation.requestedAt)) return false;
|
|
7222
7864
|
}
|
|
7223
7865
|
if (value.result !== void 0) {
|
|
7224
|
-
if (!isRecord$
|
|
7866
|
+
if (!isRecord$5(value.result)) return false;
|
|
7225
7867
|
if (typeof value.result.text !== "string" || typeof value.result.stepRunId !== "string" || !isIso(value.result.completedAt)) return false;
|
|
7226
7868
|
}
|
|
7227
7869
|
return true;
|
|
7228
7870
|
}
|
|
7229
7871
|
function isLease(value) {
|
|
7230
|
-
if (!isRecord$
|
|
7872
|
+
if (!isRecord$5(value)) return false;
|
|
7231
7873
|
return typeof value.owner === "string" && value.owner.trim() !== "" && Number.isInteger(value.epoch) && value.epoch >= 0 && isIso(value.expiresAt);
|
|
7232
7874
|
}
|
|
7233
7875
|
function isIso(value) {
|
|
@@ -7256,7 +7898,7 @@ function validateDeliverySequence(previous, record, _lineNumber) {
|
|
|
7256
7898
|
return previous.state === "pending" && record.state === "delivered" ? void 0 : "transition";
|
|
7257
7899
|
}
|
|
7258
7900
|
function isDeliveryRecord(value) {
|
|
7259
|
-
if (!isRecord$
|
|
7901
|
+
if (!isRecord$5(value)) return false;
|
|
7260
7902
|
if (typeof value.deliveryId !== "string" || value.deliveryId.trim() === "" || typeof value.sessionId !== "string" || value.sessionId.trim() === "") return false;
|
|
7261
7903
|
if (typeof value.kind !== "string" || ![
|
|
7262
7904
|
"progress",
|
|
@@ -7586,10 +8228,13 @@ function createProjectMemoryPromptPreparer(options) {
|
|
|
7586
8228
|
//#endregion
|
|
7587
8229
|
//#region src/application/agent/agent-loop-prompt-transformer.ts
|
|
7588
8230
|
function createAgentLoopPromptTransformer(options) {
|
|
7589
|
-
return {
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
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
|
+
};
|
|
7593
8238
|
}
|
|
7594
8239
|
//#endregion
|
|
7595
8240
|
//#region src/application/project/project-skill-catalog.ts
|
|
@@ -7634,7 +8279,7 @@ async function load(filePath) {
|
|
|
7634
8279
|
for (const [index, line] of raw.split("\n").entries()) {
|
|
7635
8280
|
if (!line.trim()) continue;
|
|
7636
8281
|
const envelope = JSON.parse(line);
|
|
7637
|
-
if (!isRecord$
|
|
8282
|
+
if (!isRecord$5(envelope) || envelope.version !== 1 || !isMemorySnapshot(envelope.snapshot)) throw new Error(`invalid Agent Memory snapshot at line ${index + 1}`);
|
|
7638
8283
|
const snapshot = envelope.snapshot;
|
|
7639
8284
|
const key = snapshotKey(snapshot);
|
|
7640
8285
|
const previous = latest.get(key);
|
|
@@ -7645,7 +8290,7 @@ async function load(filePath) {
|
|
|
7645
8290
|
return [...latest.values()];
|
|
7646
8291
|
}
|
|
7647
8292
|
function isMemorySnapshot(value) {
|
|
7648
|
-
if (!isRecord$
|
|
8293
|
+
if (!isRecord$5(value) || !isRecord$5(value.binding) || !isRecord$5(value.record)) return false;
|
|
7649
8294
|
const binding = value.binding;
|
|
7650
8295
|
const record = value.record;
|
|
7651
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) && [
|
|
@@ -8090,31 +8735,20 @@ async function readSnapshot$1(filePath) {
|
|
|
8090
8735
|
version: 3
|
|
8091
8736
|
});
|
|
8092
8737
|
const value = JSON.parse(raw);
|
|
8093
|
-
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");
|
|
8094
8739
|
return Object.freeze({
|
|
8095
8740
|
records: Object.freeze(value.records.map(readRecord)),
|
|
8096
8741
|
version: value.version
|
|
8097
8742
|
});
|
|
8098
8743
|
}
|
|
8099
8744
|
async function writeSnapshot(filePath, records) {
|
|
8100
|
-
await
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
records,
|
|
8105
|
-
version: 3
|
|
8106
|
-
})}\n`, {
|
|
8107
|
-
encoding: "utf8",
|
|
8108
|
-
flag: "wx"
|
|
8109
|
-
});
|
|
8110
|
-
await rename(temporaryPath, filePath);
|
|
8111
|
-
} catch (error) {
|
|
8112
|
-
await unlink(temporaryPath).catch(() => void 0);
|
|
8113
|
-
throw error;
|
|
8114
|
-
}
|
|
8745
|
+
await writePersistenceFile(filePath, {
|
|
8746
|
+
records,
|
|
8747
|
+
version: 3
|
|
8748
|
+
});
|
|
8115
8749
|
}
|
|
8116
8750
|
function readRecord(value) {
|
|
8117
|
-
if (!isRecord$
|
|
8751
|
+
if (!isRecord$5(value)) throw new Error("Automation Tick record must be an object");
|
|
8118
8752
|
for (const field of [
|
|
8119
8753
|
"automationId",
|
|
8120
8754
|
"mandateId",
|
|
@@ -8528,21 +9162,6 @@ function createRoutedHumanInteractionToolApprovalService(registry) {
|
|
|
8528
9162
|
} };
|
|
8529
9163
|
}
|
|
8530
9164
|
//#endregion
|
|
8531
|
-
//#region src/infrastructure/feishu/feishu-text-reply.ts
|
|
8532
|
-
function createConfiguredFeishuTextReplySender(options) {
|
|
8533
|
-
const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
|
|
8534
|
-
return { reply: (messageId, text) => Effect.gen(function* () {
|
|
8535
|
-
yield* options.client.request({
|
|
8536
|
-
body: {
|
|
8537
|
-
content: JSON.stringify({ text }),
|
|
8538
|
-
msg_type: "text"
|
|
8539
|
-
},
|
|
8540
|
-
method: "POST",
|
|
8541
|
-
url: `${baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}/reply`
|
|
8542
|
-
}, "Feishu reply failed");
|
|
8543
|
-
}) };
|
|
8544
|
-
}
|
|
8545
|
-
//#endregion
|
|
8546
9165
|
//#region src/infrastructure/feishu/feishu-automation-card-sender.ts
|
|
8547
9166
|
function createConfiguredFeishuAutomationCardSender(options) {
|
|
8548
9167
|
const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
|
|
@@ -8708,19 +9327,19 @@ async function loadSnapshots(filePath) {
|
|
|
8708
9327
|
return interactions;
|
|
8709
9328
|
}
|
|
8710
9329
|
function readSnapshot(value, line) {
|
|
8711
|
-
if (!isRecord$
|
|
9330
|
+
if (!isRecord$5(value) || value.version !== 1 || !isRecord$5(value.interaction)) throw new Error(`invalid human interaction envelope at line ${line}`);
|
|
8712
9331
|
const interaction = value.interaction;
|
|
8713
9332
|
if (!isHumanInteraction(interaction)) throw new Error(`invalid human interaction snapshot at line ${line}`);
|
|
8714
9333
|
return structuredClone(interaction);
|
|
8715
9334
|
}
|
|
8716
9335
|
function isHumanInteraction(value) {
|
|
8717
|
-
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;
|
|
8718
9337
|
if (value.kind === "tool-approval") return isToolApprovalRequest(value.request) && isToolApprovalState(value.state) && stateRespectsInteraction(value.state, value);
|
|
8719
9338
|
if (value.kind === "user-decision") return isUserDecisionOptions(value.options, value.recommendedOptionId) && isUserDecisionState(value.state, value.options) && stateRespectsInteraction(value.state, value);
|
|
8720
9339
|
return false;
|
|
8721
9340
|
}
|
|
8722
9341
|
function isToolApprovalRequest(value) {
|
|
8723
|
-
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");
|
|
8724
9343
|
}
|
|
8725
9344
|
function isToolApprovalState(state) {
|
|
8726
9345
|
const common = isCommonInteractionState(state);
|
|
@@ -8734,7 +9353,7 @@ function isUserDecisionState(state, options) {
|
|
|
8734
9353
|
const common = isCommonInteractionState(state);
|
|
8735
9354
|
if (common !== void 0) return common;
|
|
8736
9355
|
switch (state.status) {
|
|
8737
|
-
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);
|
|
8738
9357
|
default: return false;
|
|
8739
9358
|
}
|
|
8740
9359
|
}
|
|
@@ -8749,14 +9368,14 @@ function isCommonInteractionState(state) {
|
|
|
8749
9368
|
}
|
|
8750
9369
|
function isUserDecisionOptions(value, recommendedOptionId) {
|
|
8751
9370
|
if (!Array.isArray(value) || value.length < 2 || value.length > 5) return false;
|
|
8752
|
-
const ids = value.map((option) => isRecord$
|
|
8753
|
-
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));
|
|
8754
9373
|
}
|
|
8755
9374
|
function isFacts(value) {
|
|
8756
|
-
return Array.isArray(value) && value.every((fact) => isRecord$
|
|
9375
|
+
return Array.isArray(value) && value.every((fact) => isRecord$5(fact) && nonEmptyStrings(fact, "label", "value"));
|
|
8757
9376
|
}
|
|
8758
9377
|
function isActor(value) {
|
|
8759
|
-
return isRecord$
|
|
9378
|
+
return isRecord$5(value) && nonEmptyStrings(value, "openId", "tenantKey");
|
|
8760
9379
|
}
|
|
8761
9380
|
function isNonEmptyStringArray(value) {
|
|
8762
9381
|
return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "string" && entry.length > 0);
|
|
@@ -8771,7 +9390,7 @@ function isTimestamp(value) {
|
|
|
8771
9390
|
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
8772
9391
|
}
|
|
8773
9392
|
function stateRespectsInteraction(state, interaction) {
|
|
8774
|
-
if (isRecord$
|
|
9393
|
+
if (isRecord$5(state.actor)) {
|
|
8775
9394
|
if (state.actor.tenantKey !== interaction.tenantKey || !Array.isArray(interaction.allowedActorOpenIds) || !interaction.allowedActorOpenIds.includes(state.actor.openId)) return false;
|
|
8776
9395
|
}
|
|
8777
9396
|
const expiresAt = interaction.expiresAt;
|
|
@@ -8947,4 +9566,4 @@ function validateDecisionInput(input) {
|
|
|
8947
9566
|
if (input.recommendedOptionId && !optionIds.includes(input.recommendedOptionId)) throw new Error("recommended user decision option must be available");
|
|
8948
9567
|
}
|
|
8949
9568
|
//#endregion
|
|
8950
|
-
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, 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, 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 };
|