@rivus/agent 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/agent-loop.d.ts +10 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +477 -2
- package/dist/index.js +1540 -66
- package/dist/pi-tool-proxy.d.ts +3 -0
- package/dist/pi.js +3 -0
- package/dist/rivus-daemon-cli.js +133 -7
- package/dist/rivus-plugin-registry.js +227 -3
- package/dist/rivus-plugin.d.ts +8 -0
- package/dist/tool-input-digest.js +2 -0
- package/examples/pi-feishu-deployment.bootstrap.ts +187 -4
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { join, relative } from "node:path";
|
|
4
4
|
import { Effect } from "effect";
|
|
5
5
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
@@ -14,8 +14,13 @@ import {
|
|
|
14
14
|
createAgentsMdInstructionsProvider,
|
|
15
15
|
createAgentHarness,
|
|
16
16
|
createAgentHarnessPooledRuntime,
|
|
17
|
+
createBackgroundSessionHostTools,
|
|
18
|
+
createBackgroundSessionService,
|
|
19
|
+
createBackgroundSessionStepSourceMessageId,
|
|
20
|
+
createBackgroundSessionSupervisor,
|
|
17
21
|
createRivusMemoryToolDescriptor,
|
|
18
22
|
createConfiguredFeishuAutomationCardSender,
|
|
23
|
+
createConfiguredFeishuBackgroundSessionDelivery,
|
|
19
24
|
createConfiguredFeishuCardRolloverRuntime,
|
|
20
25
|
createConfiguredFeishuHumanInteractionPresenter,
|
|
21
26
|
createConfiguredFeishuOpenApiClient,
|
|
@@ -43,6 +48,10 @@ import {
|
|
|
43
48
|
createToolBroker,
|
|
44
49
|
createUuidRunIds,
|
|
45
50
|
createWorkspaceRootHandle,
|
|
51
|
+
loadRivusDeploymentManifest,
|
|
52
|
+
openJsonlBackgroundSessionDeliveryStore,
|
|
53
|
+
openJsonlBackgroundSessionRepository,
|
|
54
|
+
resolveBackgroundSessionSupervisorIntervalMs,
|
|
46
55
|
openJsonlFeishuCardDeliveryLedger,
|
|
47
56
|
openJsonlFeishuInboxRepository,
|
|
48
57
|
openJsonlAgentMemoryService,
|
|
@@ -53,13 +62,16 @@ import {
|
|
|
53
62
|
resolveLangfuseTelemetryConfig,
|
|
54
63
|
validateProjectSkillCatalog,
|
|
55
64
|
validateProjectSkillCommand,
|
|
65
|
+
type CreateRivusDeploymentBackgroundSessionInput,
|
|
56
66
|
type CreateRivusDeploymentEndpointInput,
|
|
57
67
|
type CreateRivusDeploymentAutomationInput,
|
|
58
68
|
type CreateRivusDeploymentRuntimeInput,
|
|
59
69
|
type ConfiguredFeishuOpenApiResponse,
|
|
60
70
|
type FeishuAgentRunPreparation,
|
|
71
|
+
type FeishuBackgroundSessionDelivery,
|
|
61
72
|
type FeishuWebSocketClient,
|
|
62
73
|
type RivusDaemonConfig,
|
|
74
|
+
type RivusDeploymentBackgroundSession,
|
|
63
75
|
type RivusDeploymentBootstrapContext,
|
|
64
76
|
type RivusThinkingLevel
|
|
65
77
|
} from "@rivus/agent";
|
|
@@ -94,6 +106,144 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
94
106
|
request: (input) => request(input).pipe(Effect.map((response) => response as ConfiguredFeishuOpenApiResponse))
|
|
95
107
|
});
|
|
96
108
|
const interactionRegistry = createHumanInteractionEndpointRegistry();
|
|
109
|
+
const manifest = await loadRivusDeploymentManifest(context.manifestPath);
|
|
110
|
+
const backgroundSessionsConfig = manifest.backgroundSessions;
|
|
111
|
+
const sessionRepository = backgroundSessionsConfig?.enabled
|
|
112
|
+
? await openJsonlBackgroundSessionRepository({
|
|
113
|
+
filePath: join(STATE_DIR, "background-sessions", "sessions.jsonl")
|
|
114
|
+
})
|
|
115
|
+
: undefined;
|
|
116
|
+
const sessionDeliveries = backgroundSessionsConfig?.enabled
|
|
117
|
+
? await openJsonlBackgroundSessionDeliveryStore({
|
|
118
|
+
filePath: join(STATE_DIR, "background-sessions", "deliveries.jsonl")
|
|
119
|
+
})
|
|
120
|
+
: undefined;
|
|
121
|
+
const deliveryClients = new Map<string, FeishuBackgroundSessionDelivery>();
|
|
122
|
+
const backgroundService = backgroundSessionsConfig?.enabled
|
|
123
|
+
? createBackgroundSessionService({
|
|
124
|
+
clock: { now: () => new Date().toISOString() },
|
|
125
|
+
deliveries: sessionDeliveries!,
|
|
126
|
+
repository: sessionRepository!
|
|
127
|
+
})
|
|
128
|
+
: undefined;
|
|
129
|
+
const createBackgroundSessionAdapter = (
|
|
130
|
+
input: CreateRivusDeploymentBackgroundSessionInput
|
|
131
|
+
): RivusDeploymentBackgroundSession => {
|
|
132
|
+
const resolveDeliverySender = async (endpointId: string): Promise<FeishuBackgroundSessionDelivery> => {
|
|
133
|
+
const existing = deliveryClients.get(endpointId);
|
|
134
|
+
if (existing) return existing;
|
|
135
|
+
const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
|
|
136
|
+
if (!endpoint) throw new Error(`background session delivery endpoint not found: ${endpointId}`);
|
|
137
|
+
const credentials = resolveFeishuEndpointCredentials(endpoint.credentialRef, context.env);
|
|
138
|
+
const config: RivusDaemonConfig = {
|
|
139
|
+
agentId: endpoint.agentId,
|
|
140
|
+
feishu: {
|
|
141
|
+
...credentials,
|
|
142
|
+
baseUrl: endpoint.baseUrl,
|
|
143
|
+
cardStreamLeaseMs: endpoint.cardStreamLeaseMs,
|
|
144
|
+
streamMinIntervalMs: endpoint.streamMinIntervalMs
|
|
145
|
+
},
|
|
146
|
+
pi: {}
|
|
147
|
+
};
|
|
148
|
+
const sender = createConfiguredFeishuBackgroundSessionDelivery({
|
|
149
|
+
client: createOpenApiClient(config),
|
|
150
|
+
config
|
|
151
|
+
});
|
|
152
|
+
deliveryClients.set(endpointId, sender);
|
|
153
|
+
return sender;
|
|
154
|
+
};
|
|
155
|
+
const supervisor = createBackgroundSessionSupervisor({
|
|
156
|
+
clock: { now: () => new Date().toISOString() },
|
|
157
|
+
config: {
|
|
158
|
+
intervalMs: resolveBackgroundSessionSupervisorIntervalMs(input.config.leaseMs),
|
|
159
|
+
leaseMs: input.config.leaseMs,
|
|
160
|
+
leaseRenewalIntervalMs: input.config.leaseRenewalIntervalMs,
|
|
161
|
+
maxConcurrentSessions: input.config.maxConcurrentSessions,
|
|
162
|
+
maxConsecutiveFailures: input.config.maxConsecutiveFailures,
|
|
163
|
+
retryBackoffMs: input.config.retryBackoffMs,
|
|
164
|
+
sessionLifetimeMs: input.config.sessionLifetimeMs
|
|
165
|
+
},
|
|
166
|
+
deliveries: sessionDeliveries!,
|
|
167
|
+
deliver: async (delivery) => {
|
|
168
|
+
const session = await sessionRepository!.get(delivery.sessionId);
|
|
169
|
+
if (!session) throw new Error(`background session not found for delivery: ${delivery.sessionId}`);
|
|
170
|
+
if (!session.origin.conversationId) {
|
|
171
|
+
throw new Error(`background session has no delivery conversation: ${delivery.sessionId}`);
|
|
172
|
+
}
|
|
173
|
+
const sender = await resolveDeliverySender(session.origin.endpointId);
|
|
174
|
+
return sender.deliver({
|
|
175
|
+
chatId: session.origin.conversationId,
|
|
176
|
+
deliveryId: delivery.deliveryId,
|
|
177
|
+
displayName: session.displayName,
|
|
178
|
+
kind: delivery.kind,
|
|
179
|
+
sessionId: session.sessionId,
|
|
180
|
+
text: delivery.text
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
onError: (error) => {
|
|
184
|
+
console.error("Background session supervisor failed", error);
|
|
185
|
+
},
|
|
186
|
+
repository: sessionRepository!,
|
|
187
|
+
runStep: async ({ session, signal, wakeText }) => {
|
|
188
|
+
let runId: string | undefined;
|
|
189
|
+
const invocation = {
|
|
190
|
+
allowedActorOpenIds: session.origin.allowedActorOpenIds,
|
|
191
|
+
endpointId: session.origin.endpointId,
|
|
192
|
+
kind: "background-session" as const,
|
|
193
|
+
...(session.origin.memory ? { memory: session.origin.memory } : {}),
|
|
194
|
+
sessionId: session.sessionId,
|
|
195
|
+
sourceMessageId: createBackgroundSessionStepSourceMessageId(session.sessionId, session.stepCount + 1),
|
|
196
|
+
tenantKey: session.origin.tenantKey
|
|
197
|
+
};
|
|
198
|
+
const abortPromise = new Promise<never>((_resolve, reject) => {
|
|
199
|
+
signal.addEventListener(
|
|
200
|
+
"abort",
|
|
201
|
+
() => {
|
|
202
|
+
if (runId) {
|
|
203
|
+
void input.cancel({
|
|
204
|
+
agentId: session.authority.agentId,
|
|
205
|
+
reason: "background session step aborted",
|
|
206
|
+
runId,
|
|
207
|
+
sessionKey: session.authority.sessionKey
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
reject(new Error("background session step aborted"));
|
|
211
|
+
},
|
|
212
|
+
{ once: true }
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
const runPromise = input
|
|
216
|
+
.run({
|
|
217
|
+
agentId: session.authority.agentId,
|
|
218
|
+
invocation,
|
|
219
|
+
onUpdate: (update) => {
|
|
220
|
+
if (update.event.type === "agent_run_accepted" && !runId) {
|
|
221
|
+
runId = update.event.runId;
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
sessionKey: session.authority.sessionKey,
|
|
225
|
+
text: wakeText
|
|
226
|
+
})
|
|
227
|
+
.then((result) => readStepRunResult(result));
|
|
228
|
+
return Promise.race([runPromise, abortPromise]);
|
|
229
|
+
},
|
|
230
|
+
sleep
|
|
231
|
+
});
|
|
232
|
+
let running = false;
|
|
233
|
+
return {
|
|
234
|
+
running: () => running,
|
|
235
|
+
status: () => supervisor.status(),
|
|
236
|
+
start: async () => {
|
|
237
|
+
await Effect.runPromise(supervisor.recover());
|
|
238
|
+
await Effect.runPromise(supervisor.start());
|
|
239
|
+
running = true;
|
|
240
|
+
},
|
|
241
|
+
stop: async () => {
|
|
242
|
+
await Effect.runPromise(supervisor.stop());
|
|
243
|
+
running = false;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
};
|
|
97
247
|
return {
|
|
98
248
|
dispose: () => telemetry?.shutdown(),
|
|
99
249
|
createRecoveryControl: () =>
|
|
@@ -101,6 +251,9 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
101
251
|
endpointsDirectory: join(STATE_DIR, "endpoints"),
|
|
102
252
|
instancesDirectory: join(STATE_DIR, "instances")
|
|
103
253
|
}),
|
|
254
|
+
createBackgroundSession: backgroundSessionsConfig?.enabled
|
|
255
|
+
? (input: CreateRivusDeploymentBackgroundSessionInput) => createBackgroundSessionAdapter(input)
|
|
256
|
+
: undefined,
|
|
104
257
|
createAutomation: async (input: CreateRivusDeploymentAutomationInput) => {
|
|
105
258
|
const credentials = resolveFeishuEndpointCredentials(input.deliveryEndpoint.credentialRef, context.env);
|
|
106
259
|
const config: RivusDaemonConfig = {
|
|
@@ -278,7 +431,18 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
278
431
|
const broker = createToolBroker({
|
|
279
432
|
approvals: createRoutedHumanInteractionToolApprovalService(interactionRegistry),
|
|
280
433
|
catalog: input.catalog,
|
|
281
|
-
|
|
434
|
+
hostTools: [
|
|
435
|
+
...(input.definition.memory.tool ? [createRivusMemoryToolDescriptor({ memory })] : []),
|
|
436
|
+
...(backgroundService
|
|
437
|
+
? [
|
|
438
|
+
...createBackgroundSessionHostTools({
|
|
439
|
+
createSessionId: () => `bg-${randomUUID()}`,
|
|
440
|
+
definition: input.definition,
|
|
441
|
+
service: backgroundService
|
|
442
|
+
})
|
|
443
|
+
]
|
|
444
|
+
: [])
|
|
445
|
+
],
|
|
282
446
|
operations: await openJsonlToolOperationLedger({ filePath: join(instanceState, "tool-operations.jsonl") }),
|
|
283
447
|
policy: { current: async () => ({ epoch: 1, revokedToolIds: [] }) }
|
|
284
448
|
});
|
|
@@ -374,10 +538,14 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
374
538
|
eventSinks: telemetry ? [eventLog, telemetry.sink] : [eventLog],
|
|
375
539
|
initialEvents: eventsForSession(initialEvents, sessionKey),
|
|
376
540
|
loop,
|
|
377
|
-
runIds: createUuidRunIds()
|
|
541
|
+
runIds: createUuidRunIds(),
|
|
542
|
+
...(input.binding.kind === "background-session" && backgroundSessionsConfig
|
|
543
|
+
? { runTimeoutMs: backgroundSessionsConfig.stepTimeoutMs }
|
|
544
|
+
: {})
|
|
378
545
|
})
|
|
379
546
|
),
|
|
380
|
-
maxConcurrentSessions:
|
|
547
|
+
maxConcurrentSessions:
|
|
548
|
+
input.binding.kind === "background-session" ? (backgroundSessionsConfig?.maxConcurrentSessions ?? 4) : 4,
|
|
381
549
|
maxQueuedRuns: 32
|
|
382
550
|
});
|
|
383
551
|
return {
|
|
@@ -439,6 +607,21 @@ function readAutomationRunResult(result: unknown): { readonly body: string; read
|
|
|
439
607
|
throw new Error("Scheduled Automation Agent Run did not produce a runId and final text");
|
|
440
608
|
}
|
|
441
609
|
|
|
610
|
+
function readStepRunResult(result: unknown): { readonly finalText: string; readonly runId: string } {
|
|
611
|
+
if (
|
|
612
|
+
result !== null &&
|
|
613
|
+
typeof result === "object" &&
|
|
614
|
+
"finalText" in result &&
|
|
615
|
+
typeof result.finalText === "string" &&
|
|
616
|
+
"runId" in result &&
|
|
617
|
+
typeof result.runId === "string" &&
|
|
618
|
+
result.runId.trim() !== ""
|
|
619
|
+
) {
|
|
620
|
+
return { finalText: result.finalText, runId: result.runId };
|
|
621
|
+
}
|
|
622
|
+
throw new Error("Background session Agent Run did not produce a runId and final text");
|
|
623
|
+
}
|
|
624
|
+
|
|
442
625
|
function createLazyFeishuWebSocketClient(
|
|
443
626
|
credentials: {
|
|
444
627
|
readonly appId: string;
|