@rivus/agent 0.6.1 → 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 +493 -3
- package/dist/index.js +1589 -79
- package/dist/pi-tool-proxy.d.ts +3 -0
- package/dist/pi.js +3 -0
- package/dist/rivus-daemon-cli.js +163 -23
- 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,227 @@
|
|
|
1
1
|
import { c as InvalidRivusPlugin, o as createRivusMemoryToolContract, r as RIVUS_MEMORY_TOOL_PLUGIN_ID, t as MEMORY_SCOPES } from "./agent-memory.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
+
//#region src/application/background-session/background-session-authority.ts
|
|
4
|
+
const BACKGROUND_SESSION_TOOL_IDS = [
|
|
5
|
+
"background.start",
|
|
6
|
+
"background.wait",
|
|
7
|
+
"background.list",
|
|
8
|
+
"background.status",
|
|
9
|
+
"background.send",
|
|
10
|
+
"background.stop"
|
|
11
|
+
];
|
|
12
|
+
const BACKGROUND_SESSION_START_TOOL_ID = "background.start";
|
|
13
|
+
const BACKGROUND_SESSION_TOOL_PLUGIN_ID = "rivus-core";
|
|
14
|
+
const BACKGROUND_SESSION_TOOL_VERSION = "1.0.0";
|
|
15
|
+
const BACKGROUND_SESSION_SESSION_KEY_PREFIX = "background";
|
|
16
|
+
function createBackgroundSessionKey(sessionId) {
|
|
17
|
+
return `${BACKGROUND_SESSION_SESSION_KEY_PREFIX}:${sessionId}`;
|
|
18
|
+
}
|
|
19
|
+
function createBackgroundSessionStepSourceMessageId(sessionId, stepCount) {
|
|
20
|
+
return `bg:${sessionId}:step:${stepCount}`;
|
|
21
|
+
}
|
|
22
|
+
function createBackgroundSessionToolContracts() {
|
|
23
|
+
return [
|
|
24
|
+
Object.freeze({
|
|
25
|
+
description: "Start a background agent session. Use when the request must wait for external changes, observe over time, or continue working after the foreground run ends. Returns a stable session id immediately; the foreground response can finish here. The detached session continues with the granted Skills, CLI, Tools, Project Space, and Memory of this agent.",
|
|
26
|
+
digest: contractDigest("background.start"),
|
|
27
|
+
id: "background.start",
|
|
28
|
+
idempotency: "supported",
|
|
29
|
+
inputSchema: Object.freeze({
|
|
30
|
+
additionalProperties: false,
|
|
31
|
+
properties: Object.freeze({
|
|
32
|
+
displayName: {
|
|
33
|
+
type: "string",
|
|
34
|
+
maxLength: 200
|
|
35
|
+
},
|
|
36
|
+
prompt: {
|
|
37
|
+
type: "string",
|
|
38
|
+
minLength: 1,
|
|
39
|
+
maxLength: 2e4
|
|
40
|
+
}
|
|
41
|
+
}),
|
|
42
|
+
required: ["prompt"],
|
|
43
|
+
type: "object"
|
|
44
|
+
}),
|
|
45
|
+
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
46
|
+
risk: "mutate",
|
|
47
|
+
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
48
|
+
}),
|
|
49
|
+
Object.freeze({
|
|
50
|
+
description: "Pause the current background session durably and end the current step. Call with delayMs to resume after a delay, with until to resume at an absolute ISO time, or with neither to wait for user input. After this call no further tool calls are accepted in this step.",
|
|
51
|
+
digest: contractDigest("background.wait"),
|
|
52
|
+
id: "background.wait",
|
|
53
|
+
idempotency: "supported",
|
|
54
|
+
inputSchema: Object.freeze({
|
|
55
|
+
additionalProperties: false,
|
|
56
|
+
properties: Object.freeze({
|
|
57
|
+
delayMs: {
|
|
58
|
+
type: "integer",
|
|
59
|
+
minimum: 1e3,
|
|
60
|
+
maximum: 864e5
|
|
61
|
+
},
|
|
62
|
+
reason: {
|
|
63
|
+
type: "string",
|
|
64
|
+
maxLength: 500
|
|
65
|
+
},
|
|
66
|
+
until: {
|
|
67
|
+
type: "string",
|
|
68
|
+
maxLength: 64
|
|
69
|
+
}
|
|
70
|
+
}),
|
|
71
|
+
type: "object"
|
|
72
|
+
}),
|
|
73
|
+
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
74
|
+
risk: "mutate",
|
|
75
|
+
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
76
|
+
}),
|
|
77
|
+
Object.freeze({
|
|
78
|
+
description: "List background sessions owned by this conversation, newest first. Optionally filter by phase and limit the number of results.",
|
|
79
|
+
digest: contractDigest("background.list"),
|
|
80
|
+
id: "background.list",
|
|
81
|
+
idempotency: "supported",
|
|
82
|
+
inputSchema: Object.freeze({
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
properties: Object.freeze({
|
|
85
|
+
limit: {
|
|
86
|
+
type: "integer",
|
|
87
|
+
minimum: 1,
|
|
88
|
+
maximum: 50
|
|
89
|
+
},
|
|
90
|
+
phase: {
|
|
91
|
+
enum: [
|
|
92
|
+
"queued",
|
|
93
|
+
"running",
|
|
94
|
+
"waiting",
|
|
95
|
+
"input-required",
|
|
96
|
+
"stopping",
|
|
97
|
+
"stopped",
|
|
98
|
+
"completed",
|
|
99
|
+
"failed",
|
|
100
|
+
"reconciliation-required"
|
|
101
|
+
],
|
|
102
|
+
type: "string"
|
|
103
|
+
}
|
|
104
|
+
}),
|
|
105
|
+
type: "object"
|
|
106
|
+
}),
|
|
107
|
+
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
108
|
+
risk: "observe",
|
|
109
|
+
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
110
|
+
}),
|
|
111
|
+
Object.freeze({
|
|
112
|
+
description: "Return the current phase, step counts, wake time, and result of one background session owned by this conversation.",
|
|
113
|
+
digest: contractDigest("background.status"),
|
|
114
|
+
id: "background.status",
|
|
115
|
+
idempotency: "supported",
|
|
116
|
+
inputSchema: Object.freeze({
|
|
117
|
+
additionalProperties: false,
|
|
118
|
+
properties: Object.freeze({ sessionId: {
|
|
119
|
+
type: "string",
|
|
120
|
+
minLength: 1,
|
|
121
|
+
maxLength: 200
|
|
122
|
+
} }),
|
|
123
|
+
required: ["sessionId"],
|
|
124
|
+
type: "object"
|
|
125
|
+
}),
|
|
126
|
+
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
127
|
+
risk: "observe",
|
|
128
|
+
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
129
|
+
}),
|
|
130
|
+
Object.freeze({
|
|
131
|
+
description: "Send new user instruction text to a background session owned by this conversation and wake it. The input is delivered exactly once in the next step.",
|
|
132
|
+
digest: contractDigest("background.send"),
|
|
133
|
+
id: "background.send",
|
|
134
|
+
idempotency: "supported",
|
|
135
|
+
inputSchema: Object.freeze({
|
|
136
|
+
additionalProperties: false,
|
|
137
|
+
properties: Object.freeze({
|
|
138
|
+
message: {
|
|
139
|
+
type: "string",
|
|
140
|
+
minLength: 1,
|
|
141
|
+
maxLength: 2e4
|
|
142
|
+
},
|
|
143
|
+
sessionId: {
|
|
144
|
+
type: "string",
|
|
145
|
+
minLength: 1,
|
|
146
|
+
maxLength: 200
|
|
147
|
+
}
|
|
148
|
+
}),
|
|
149
|
+
required: ["message", "sessionId"],
|
|
150
|
+
type: "object"
|
|
151
|
+
}),
|
|
152
|
+
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
153
|
+
risk: "mutate",
|
|
154
|
+
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
155
|
+
}),
|
|
156
|
+
Object.freeze({
|
|
157
|
+
description: "Stop a background session owned by this conversation. Persists the cancellation, aborts the active step and its owned process, and delivers a terminal notice.",
|
|
158
|
+
digest: contractDigest("background.stop"),
|
|
159
|
+
id: "background.stop",
|
|
160
|
+
idempotency: "supported",
|
|
161
|
+
inputSchema: Object.freeze({
|
|
162
|
+
additionalProperties: false,
|
|
163
|
+
properties: Object.freeze({
|
|
164
|
+
reason: {
|
|
165
|
+
type: "string",
|
|
166
|
+
maxLength: 500
|
|
167
|
+
},
|
|
168
|
+
sessionId: {
|
|
169
|
+
type: "string",
|
|
170
|
+
minLength: 1,
|
|
171
|
+
maxLength: 200
|
|
172
|
+
}
|
|
173
|
+
}),
|
|
174
|
+
required: ["sessionId"],
|
|
175
|
+
type: "object"
|
|
176
|
+
}),
|
|
177
|
+
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
178
|
+
risk: "mutate",
|
|
179
|
+
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
180
|
+
})
|
|
181
|
+
];
|
|
182
|
+
}
|
|
183
|
+
function backgroundSessionToolIds() {
|
|
184
|
+
return [...BACKGROUND_SESSION_TOOL_IDS];
|
|
185
|
+
}
|
|
186
|
+
function isBackgroundSessionToolId(toolId) {
|
|
187
|
+
return BACKGROUND_SESSION_TOOL_IDS.includes(toolId);
|
|
188
|
+
}
|
|
189
|
+
function extendBackgroundSessionDefinition(definition) {
|
|
190
|
+
const contracts = createBackgroundSessionToolContracts();
|
|
191
|
+
const existingIds = new Set(definition.tools.map(({ id }) => id));
|
|
192
|
+
const additions = contracts.filter((contract) => !existingIds.has(contract.id));
|
|
193
|
+
const toolGrantSet = Object.freeze({
|
|
194
|
+
revision: grantRevision(definition.toolGrantSet.revision, additions.map(({ id }) => id)),
|
|
195
|
+
toolIds: Object.freeze([...definition.toolGrantSet.toolIds, ...additions.map(({ id }) => id)].sort())
|
|
196
|
+
});
|
|
197
|
+
return Object.freeze({
|
|
198
|
+
...definition,
|
|
199
|
+
tools: Object.freeze([...definition.tools, ...additions]),
|
|
200
|
+
toolGrantSet
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function narrowBackgroundSessionDefinition(definition) {
|
|
204
|
+
const childToolIds = definition.toolGrantSet.toolIds.filter((id) => id !== BACKGROUND_SESSION_START_TOOL_ID);
|
|
205
|
+
const toolGrantSet = Object.freeze({
|
|
206
|
+
revision: grantRevision(definition.toolGrantSet.revision, childToolIds),
|
|
207
|
+
toolIds: Object.freeze(childToolIds)
|
|
208
|
+
});
|
|
209
|
+
return Object.freeze({
|
|
210
|
+
...definition,
|
|
211
|
+
tools: Object.freeze(definition.tools.filter(({ id }) => id !== BACKGROUND_SESSION_START_TOOL_ID)),
|
|
212
|
+
toolGrantSet
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function grantRevision(parentRevision, toolIds) {
|
|
216
|
+
return `sha256:${createHash("sha256").update(JSON.stringify({
|
|
217
|
+
parentRevision,
|
|
218
|
+
toolIds: [...toolIds].sort()
|
|
219
|
+
})).digest("hex")}`;
|
|
220
|
+
}
|
|
221
|
+
function contractDigest(toolId) {
|
|
222
|
+
return `sha256:${createHash("sha256").update(`background-tool:${toolId}:${BACKGROUND_SESSION_TOOL_VERSION}`).digest("hex")}`;
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
3
225
|
//#region src/application/plugin/deep-freeze.ts
|
|
4
226
|
function deepFreeze(value) {
|
|
5
227
|
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -70,7 +292,7 @@ function createRivusPluginCatalog() {
|
|
|
70
292
|
})
|
|
71
293
|
};
|
|
72
294
|
}
|
|
73
|
-
function resolveRivusAgentDefinition(catalog, deployment) {
|
|
295
|
+
function resolveRivusAgentDefinition(catalog, deployment, options = {}) {
|
|
74
296
|
const snapshot = catalog.snapshot();
|
|
75
297
|
const plugin = snapshot.plugins.find((candidate) => candidate.id === deployment.pluginId);
|
|
76
298
|
if (!plugin) throw new InvalidRivusPlugin(`unknown deployment plugin: ${deployment.pluginId}`);
|
|
@@ -135,7 +357,7 @@ function resolveRivusAgentDefinition(catalog, deployment) {
|
|
|
135
357
|
}),
|
|
136
358
|
skillIds
|
|
137
359
|
});
|
|
138
|
-
|
|
360
|
+
const definition = deepFreeze({
|
|
139
361
|
agentId: deployment.agentId,
|
|
140
362
|
endpointIds: [...deployment.endpointIds],
|
|
141
363
|
memory: {
|
|
@@ -153,6 +375,8 @@ function resolveRivusAgentDefinition(catalog, deployment) {
|
|
|
153
375
|
toolGrantSet,
|
|
154
376
|
tools
|
|
155
377
|
});
|
|
378
|
+
if (options.backgroundSessions === true) return extendBackgroundSessionDefinition(definition);
|
|
379
|
+
return definition;
|
|
156
380
|
}
|
|
157
381
|
function validateMemoryScopes(scopes, owner) {
|
|
158
382
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -209,4 +433,4 @@ function stableJson(value) {
|
|
|
209
433
|
return JSON.stringify(value);
|
|
210
434
|
}
|
|
211
435
|
//#endregion
|
|
212
|
-
export { resolveRivusAgentDefinition as n, deepFreeze as r, createRivusPluginCatalog as t };
|
|
436
|
+
export { BACKGROUND_SESSION_START_TOOL_ID as a, BACKGROUND_SESSION_TOOL_VERSION as c, createBackgroundSessionStepSourceMessageId as d, createBackgroundSessionToolContracts as f, narrowBackgroundSessionDefinition as h, BACKGROUND_SESSION_SESSION_KEY_PREFIX as i, backgroundSessionToolIds as l, isBackgroundSessionToolId as m, resolveRivusAgentDefinition as n, BACKGROUND_SESSION_TOOL_IDS as o, extendBackgroundSessionDefinition as p, deepFreeze as r, BACKGROUND_SESSION_TOOL_PLUGIN_ID as s, createRivusPluginCatalog as t, createBackgroundSessionKey as u };
|
package/dist/rivus-plugin.d.ts
CHANGED
|
@@ -16,6 +16,12 @@ interface RivusPluginManifest {
|
|
|
16
16
|
interface RivusToolExecutor {
|
|
17
17
|
execute(input: unknown, context: RivusToolExecutionContext): unknown;
|
|
18
18
|
}
|
|
19
|
+
interface RivusToolExecutionOrigin {
|
|
20
|
+
readonly endpointId: string;
|
|
21
|
+
readonly tenantKey: string;
|
|
22
|
+
readonly conversationId?: string;
|
|
23
|
+
readonly allowedActorOpenIds: ReadonlyArray<string>;
|
|
24
|
+
}
|
|
19
25
|
interface RivusToolExecutionContext {
|
|
20
26
|
readonly agentId: string;
|
|
21
27
|
readonly instanceId: string;
|
|
@@ -27,6 +33,8 @@ interface RivusToolExecutionContext {
|
|
|
27
33
|
readonly toolId: string;
|
|
28
34
|
readonly toolVersion: string;
|
|
29
35
|
readonly sessionKey: string;
|
|
36
|
+
readonly origin?: RivusToolExecutionOrigin;
|
|
37
|
+
readonly sourceMessageId?: string;
|
|
30
38
|
}
|
|
31
39
|
interface RivusToolFactoryContext {
|
|
32
40
|
readonly toolId: string;
|
|
@@ -64,6 +64,7 @@ var InvalidInvocationAuthority = class extends Error {
|
|
|
64
64
|
};
|
|
65
65
|
function createInvocationAuthority(authority) {
|
|
66
66
|
if (!authority.sourceMessageId.trim()) throw new InvalidInvocationAuthority("invocation authority requires a trusted source message id");
|
|
67
|
+
if (authority.endpointId !== void 0 && !authority.endpointId.trim()) throw new InvalidInvocationAuthority("invocation authority requires a trusted endpoint id");
|
|
67
68
|
const reference = Object.freeze({ id: `authority:${randomUUID()}` });
|
|
68
69
|
const memory = authority.memory ? Object.freeze({
|
|
69
70
|
...authority.memory,
|
|
@@ -71,6 +72,7 @@ function createInvocationAuthority(authority) {
|
|
|
71
72
|
}) : void 0;
|
|
72
73
|
authorities.set(reference, Object.freeze({
|
|
73
74
|
...authority,
|
|
75
|
+
...authority.allowedActorOpenIds ? { allowedActorOpenIds: Object.freeze([...authority.allowedActorOpenIds]) } : {},
|
|
74
76
|
...memory ? { memory } : {}
|
|
75
77
|
}));
|
|
76
78
|
return reference;
|
|
@@ -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;
|