@rivus/agent 0.14.4 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap/pi-feishu.d.ts +2 -2
- package/dist/bootstrap/pi-feishu.js +4118 -388
- package/dist/chunks/index.d.ts +117 -1
- package/dist/chunks/pi.js +56 -19
- package/dist/chunks/rivus-daemon-cli.js +452 -144
- package/dist/chunks/rivus-model-management-wire.js +344 -0
- package/dist/chunks/rivus-plugin-testkit.js +1 -1
- package/dist/chunks/{tool-input-digest.js → rivus-tool.js} +67 -67
- package/dist/chunks/src.js +1885 -1687
- package/dist/cli.js +152 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/mcp.js +1 -1
- package/dist/pi.d.ts +2 -0
- package/dist/pi.js +1 -1
- package/examples/pi-feishu-deployment.bootstrap.ts +557 -462
- package/package.json +5 -3
- package/skills/runtime-management/SKILL.md +61 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createPiManagedSessionContext } from "../src/adapters/pi/model-management/pi-managed-session-context.js";
|
|
2
|
+
import { ensurePiModel } from "../src/adapters/pi/model-management/pi-model-resolution.js";
|
|
3
|
+
import { openPiModelManagementDeployment } from "../src/bootstrap/deployment/model-management/pi-model-management-deployment.js";
|
|
4
|
+
import { mkdir, readFile, stat } from "node:fs/promises";
|
|
2
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
6
|
import { homedir } from "node:os";
|
|
4
7
|
import { join, relative } from "node:path";
|
|
5
8
|
import { Effect } from "effect";
|
|
6
9
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
7
|
-
import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { createAgentSession, ModelRuntime, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
8
11
|
import {
|
|
9
12
|
createAgentsMdInstructionsProvider,
|
|
10
13
|
createAgentHarness,
|
|
@@ -29,6 +32,7 @@ import {
|
|
|
29
32
|
createHumanInteractionEndpointRegistry,
|
|
30
33
|
createHumanInteractionService,
|
|
31
34
|
createHumanInteractionToolApprovalGateway,
|
|
35
|
+
createHumanInteractionModelChangeApproval,
|
|
32
36
|
createJsonFetchRequest,
|
|
33
37
|
createJsonFileFeishuCardTargetRegistry,
|
|
34
38
|
createJsonlAgentEventLog,
|
|
@@ -70,6 +74,8 @@ import {
|
|
|
70
74
|
type FeishuBackgroundSessionDelivery,
|
|
71
75
|
type FeishuWebSocketClient,
|
|
72
76
|
type RivusDaemonConfig,
|
|
77
|
+
type RivusDaemonEnv,
|
|
78
|
+
type RivusEndpointDeployment,
|
|
73
79
|
type RivusDeploymentBackgroundSession,
|
|
74
80
|
type RivusDeploymentBootstrapContext,
|
|
75
81
|
type RivusThinkingLevel
|
|
@@ -94,9 +100,17 @@ const THINKING_LEVELS = new Set<RivusThinkingLevel>(["off", "minimal", "low", "m
|
|
|
94
100
|
|
|
95
101
|
export async function createRivusDeploymentAdapters(context: RivusDeploymentBootstrapContext) {
|
|
96
102
|
await mkdir(PI_AGENT_DIR, { recursive: true });
|
|
97
|
-
const
|
|
103
|
+
const hasManagedState = await stat(join(STATE_DIR, "model-management", "model-state.json")).then(
|
|
104
|
+
() => true,
|
|
105
|
+
(error: NodeJS.ErrnoException) => {
|
|
106
|
+
if (error.code === "ENOENT") return false;
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
const deferModel = context.env.RIVUS_MODEL_MANAGEMENT_ENABLED?.trim() === "true" || hasManagedState;
|
|
111
|
+
const piOptions = await createPiSessionOptions(context, deferModel);
|
|
98
112
|
const telemetryConfig = resolveLangfuseTelemetryConfig(context.env);
|
|
99
|
-
|
|
113
|
+
let telemetry: ReturnType<typeof createLangfuseAgentTelemetry> | undefined;
|
|
100
114
|
const memory = await openJsonlAgentMemoryService({
|
|
101
115
|
filePath: join(STATE_DIR, "memory", "agent-memory.jsonl")
|
|
102
116
|
});
|
|
@@ -109,477 +123,526 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
109
123
|
});
|
|
110
124
|
const interactionRegistry = createHumanInteractionEndpointRegistry();
|
|
111
125
|
const manifest = await loadRivusDeploymentManifest(context.manifestPath);
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
const defaultEndpoint = manifest.endpoints.find((endpoint) => endpoint.id === manifest.defaultEndpointId)!;
|
|
127
|
+
const management = await openPiModelManagementDeployment({
|
|
128
|
+
agentId: manifest.defaultAgentId,
|
|
129
|
+
approval: createHumanInteractionModelChangeApproval({
|
|
130
|
+
endpointId: manifest.defaultEndpointId,
|
|
131
|
+
registry: interactionRegistry
|
|
132
|
+
}),
|
|
133
|
+
agentCount: manifest.agents.length,
|
|
134
|
+
endpointId: manifest.defaultEndpointId,
|
|
135
|
+
env: context.env,
|
|
136
|
+
environmentOverrides: context.environmentOverrides ?? process.env,
|
|
137
|
+
...(context.envFilePath ? { envFilePath: context.envFilePath } : {}),
|
|
138
|
+
modelAuthPath: PI_AUTH_FILE,
|
|
139
|
+
modelCatalogPath: PI_MODELS_FILE,
|
|
140
|
+
modelRuntime: piOptions.modelRuntime!,
|
|
141
|
+
reply: (messageId, text) => {
|
|
142
|
+
const config = createEndpointConfig(defaultEndpoint, manifest.defaultAgentId, context.env);
|
|
143
|
+
return createConfiguredFeishuTextReplySender({ config, client: createOpenApiClient(config) }).reply(
|
|
144
|
+
messageId,
|
|
145
|
+
text
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
stateDirectory: STATE_DIR,
|
|
149
|
+
thinkingLevel: piOptions.thinkingLevel ?? "medium"
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
telemetry = telemetryConfig ? createLangfuseAgentTelemetry(telemetryConfig) : undefined;
|
|
153
|
+
if (management && !management.enabled) {
|
|
154
|
+
piOptions.model = await ensurePiModel({
|
|
155
|
+
modelRuntime: piOptions.modelRuntime!,
|
|
156
|
+
modelsPath: PI_MODELS_FILE,
|
|
157
|
+
target: management.model
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (management?.enabled && management.recoveryRequired) {
|
|
161
|
+
console.error("Model management requires recovery; business Runs are paused. Query rivus model status --json.");
|
|
162
|
+
}
|
|
163
|
+
if (management?.enabled && management.skillInstallation.status === "conflict") {
|
|
164
|
+
console.error("The runtime-management Skill has local edits; its installed instructions were preserved.");
|
|
165
|
+
}
|
|
166
|
+
const backgroundSessionsConfig = manifest.backgroundSessions;
|
|
167
|
+
const sessionRepository = backgroundSessionsConfig?.enabled
|
|
168
|
+
? await openJsonlBackgroundSessionRepository({
|
|
169
|
+
filePath: join(STATE_DIR, "background-sessions", "sessions.jsonl")
|
|
170
|
+
})
|
|
171
|
+
: undefined;
|
|
172
|
+
const sessionDeliveries = backgroundSessionsConfig?.enabled
|
|
173
|
+
? await openJsonlBackgroundSessionDeliveryStore({
|
|
174
|
+
filePath: join(STATE_DIR, "background-sessions", "deliveries.jsonl")
|
|
175
|
+
})
|
|
176
|
+
: undefined;
|
|
177
|
+
const deliveryClients = new Map<string, FeishuBackgroundSessionDelivery>();
|
|
178
|
+
const backgroundService = backgroundSessionsConfig?.enabled
|
|
179
|
+
? createBackgroundSessionService({
|
|
180
|
+
clock: { now: () => new Date().toISOString() },
|
|
181
|
+
deliveries: sessionDeliveries!,
|
|
182
|
+
repository: sessionRepository!
|
|
183
|
+
})
|
|
184
|
+
: undefined;
|
|
185
|
+
const createBackgroundSessionAdapter = (
|
|
186
|
+
input: CreateRivusDeploymentBackgroundSessionInput
|
|
187
|
+
): RivusDeploymentBackgroundSession => {
|
|
188
|
+
const resolveDeliverySender = async (endpointId: string): Promise<FeishuBackgroundSessionDelivery> => {
|
|
189
|
+
const existing = deliveryClients.get(endpointId);
|
|
190
|
+
if (existing) return existing;
|
|
191
|
+
const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
|
|
192
|
+
if (!endpoint) throw new Error(`background session delivery endpoint not found: ${endpointId}`);
|
|
193
|
+
const config = createEndpointConfig(endpoint, endpoint.agentId, context.env);
|
|
194
|
+
const sender = createConfiguredFeishuBackgroundSessionDelivery({
|
|
195
|
+
client: createOpenApiClient(config),
|
|
196
|
+
config
|
|
197
|
+
});
|
|
198
|
+
deliveryClients.set(endpointId, sender);
|
|
199
|
+
return sender;
|
|
200
|
+
};
|
|
201
|
+
const supervisor = createBackgroundSessionSupervisor({
|
|
126
202
|
clock: { now: () => new Date().toISOString() },
|
|
203
|
+
config: {
|
|
204
|
+
intervalMs: resolveBackgroundSessionSupervisorIntervalMs(input.config.leaseMs),
|
|
205
|
+
leaseMs: input.config.leaseMs,
|
|
206
|
+
leaseRenewalIntervalMs: input.config.leaseRenewalIntervalMs,
|
|
207
|
+
maxConcurrentSessions: input.config.maxConcurrentSessions,
|
|
208
|
+
maxConsecutiveFailures: input.config.maxConsecutiveFailures,
|
|
209
|
+
retryBackoffMs: input.config.retryBackoffMs,
|
|
210
|
+
sessionLifetimeMs: input.config.sessionLifetimeMs
|
|
211
|
+
},
|
|
127
212
|
deliveries: sessionDeliveries!,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
...credentials,
|
|
144
|
-
baseUrl: endpoint.baseUrl,
|
|
145
|
-
cardStreamLeaseMs: endpoint.cardStreamLeaseMs,
|
|
146
|
-
streamMinIntervalMs: endpoint.streamMinIntervalMs
|
|
213
|
+
deliver: async (delivery) => {
|
|
214
|
+
const session = await sessionRepository!.get(delivery.sessionId);
|
|
215
|
+
if (!session) throw new Error(`background session not found for delivery: ${delivery.sessionId}`);
|
|
216
|
+
if (!session.origin.conversationId) {
|
|
217
|
+
throw new Error(`background session has no delivery conversation: ${delivery.sessionId}`);
|
|
218
|
+
}
|
|
219
|
+
const sender = await resolveDeliverySender(session.origin.endpointId);
|
|
220
|
+
return sender.deliver({
|
|
221
|
+
chatId: resolveFeishuDeliveryChatId(session.origin.conversationId),
|
|
222
|
+
deliveryId: delivery.deliveryId,
|
|
223
|
+
displayName: session.displayName,
|
|
224
|
+
kind: delivery.kind,
|
|
225
|
+
sessionId: session.sessionId,
|
|
226
|
+
text: delivery.text
|
|
227
|
+
});
|
|
147
228
|
},
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
229
|
+
onError: (error) => {
|
|
230
|
+
console.error("Background session supervisor failed", error);
|
|
231
|
+
},
|
|
232
|
+
repository: sessionRepository!,
|
|
233
|
+
runStep: async ({ session, signal, wakeText }) => {
|
|
234
|
+
let runId: string | undefined;
|
|
235
|
+
const invocation = {
|
|
236
|
+
allowedActorOpenIds: session.origin.allowedActorOpenIds,
|
|
237
|
+
...(session.origin.conversationId ? { conversationId: session.origin.conversationId } : {}),
|
|
238
|
+
endpointId: session.origin.endpointId,
|
|
239
|
+
kind: "background-session" as const,
|
|
240
|
+
...(session.origin.memory ? { memory: session.origin.memory } : {}),
|
|
241
|
+
sessionId: session.sessionId,
|
|
242
|
+
sourceMessageId: createBackgroundSessionStepSourceMessageId(session.sessionId, session.stepCount + 1),
|
|
243
|
+
tenantKey: session.origin.tenantKey
|
|
244
|
+
};
|
|
245
|
+
const abortPromise = new Promise<never>((_resolve, reject) => {
|
|
246
|
+
signal.addEventListener(
|
|
247
|
+
"abort",
|
|
248
|
+
() => {
|
|
249
|
+
if (runId) {
|
|
250
|
+
void input.cancel({
|
|
251
|
+
agentId: session.authority.agentId,
|
|
252
|
+
reason: "background session step aborted",
|
|
253
|
+
runId,
|
|
254
|
+
sessionKey: session.authority.sessionKey
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
reject(new Error("background session step aborted"));
|
|
258
|
+
},
|
|
259
|
+
{ once: true }
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
const runPromise = input
|
|
263
|
+
.run({
|
|
264
|
+
agentId: session.authority.agentId,
|
|
265
|
+
invocation,
|
|
266
|
+
onUpdate: (update) => {
|
|
267
|
+
if (update.event.type === "agent_run_accepted" && !runId) {
|
|
268
|
+
runId = update.event.runId;
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
sessionKey: session.authority.sessionKey,
|
|
272
|
+
text: wakeText
|
|
273
|
+
})
|
|
274
|
+
.then((result) => readStepRunResult(result));
|
|
275
|
+
return Promise.race([runPromise, abortPromise]);
|
|
276
|
+
},
|
|
277
|
+
sleep
|
|
153
278
|
});
|
|
154
|
-
|
|
155
|
-
return
|
|
279
|
+
let running = false;
|
|
280
|
+
return {
|
|
281
|
+
running: () => running,
|
|
282
|
+
status: () => supervisor.status(),
|
|
283
|
+
start: async () => {
|
|
284
|
+
await Effect.runPromise(supervisor.recover());
|
|
285
|
+
await Effect.runPromise(supervisor.start());
|
|
286
|
+
running = true;
|
|
287
|
+
},
|
|
288
|
+
stop: async () => {
|
|
289
|
+
await Effect.runPromise(supervisor.stop());
|
|
290
|
+
running = false;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
156
293
|
};
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
leaseMs: input.config.leaseMs,
|
|
162
|
-
leaseRenewalIntervalMs: input.config.leaseRenewalIntervalMs,
|
|
163
|
-
maxConcurrentSessions: input.config.maxConcurrentSessions,
|
|
164
|
-
maxConsecutiveFailures: input.config.maxConsecutiveFailures,
|
|
165
|
-
retryBackoffMs: input.config.retryBackoffMs,
|
|
166
|
-
sessionLifetimeMs: input.config.sessionLifetimeMs
|
|
294
|
+
return {
|
|
295
|
+
dispose: async () => {
|
|
296
|
+
if (management?.enabled) await management.close();
|
|
297
|
+
await telemetry?.shutdown();
|
|
167
298
|
},
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
text: delivery.text
|
|
299
|
+
createRecoveryControl: () =>
|
|
300
|
+
openJsonlRecoveryControl({
|
|
301
|
+
endpointsDirectory: join(STATE_DIR, "endpoints"),
|
|
302
|
+
instancesDirectory: join(STATE_DIR, "instances")
|
|
303
|
+
}),
|
|
304
|
+
createBackgroundSession: backgroundSessionsConfig?.enabled
|
|
305
|
+
? (input: CreateRivusDeploymentBackgroundSessionInput) => createBackgroundSessionAdapter(input)
|
|
306
|
+
: undefined,
|
|
307
|
+
createAutomation: async (input: CreateRivusDeploymentAutomationInput) => {
|
|
308
|
+
const config = createEndpointConfig(input.deliveryEndpoint, input.definition.agentId, context.env);
|
|
309
|
+
const openApiClient = createOpenApiClient(config);
|
|
310
|
+
const sender = createConfiguredFeishuAutomationCardSender({
|
|
311
|
+
client: openApiClient,
|
|
312
|
+
config
|
|
183
313
|
});
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const invocation = {
|
|
192
|
-
allowedActorOpenIds: session.origin.allowedActorOpenIds,
|
|
193
|
-
...(session.origin.conversationId ? { conversationId: session.origin.conversationId } : {}),
|
|
194
|
-
endpointId: session.origin.endpointId,
|
|
195
|
-
kind: "background-session" as const,
|
|
196
|
-
...(session.origin.memory ? { memory: session.origin.memory } : {}),
|
|
197
|
-
sessionId: session.sessionId,
|
|
198
|
-
sourceMessageId: createBackgroundSessionStepSourceMessageId(session.sessionId, session.stepCount + 1),
|
|
199
|
-
tenantKey: session.origin.tenantKey
|
|
200
|
-
};
|
|
201
|
-
const abortPromise = new Promise<never>((_resolve, reject) => {
|
|
202
|
-
signal.addEventListener(
|
|
203
|
-
"abort",
|
|
204
|
-
() => {
|
|
205
|
-
if (runId) {
|
|
206
|
-
void input.cancel({
|
|
207
|
-
agentId: session.authority.agentId,
|
|
208
|
-
reason: "background session step aborted",
|
|
209
|
-
runId,
|
|
210
|
-
sessionKey: session.authority.sessionKey
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
reject(new Error("background session step aborted"));
|
|
214
|
-
},
|
|
215
|
-
{ once: true }
|
|
216
|
-
);
|
|
314
|
+
const repository = await openJsonAutomationTickRepository({
|
|
315
|
+
filePath: join(
|
|
316
|
+
STATE_DIR,
|
|
317
|
+
"automations",
|
|
318
|
+
createHash("sha256").update(input.automationId).digest("hex").slice(0, 16),
|
|
319
|
+
"ticks.json"
|
|
320
|
+
)
|
|
217
321
|
});
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
322
|
+
const target = resolveAutomationTarget(input.definition.delivery.targetRef, context.env);
|
|
323
|
+
const automation = createScheduledAutomation({
|
|
324
|
+
automationId: input.automationId,
|
|
325
|
+
binding: {
|
|
326
|
+
agentId: input.definition.agentId,
|
|
327
|
+
bindingId: input.automationId,
|
|
328
|
+
delivery: {
|
|
329
|
+
endpointId: input.definition.delivery.endpointId,
|
|
330
|
+
targetKey: createHash("sha256").update(target).digest("hex"),
|
|
331
|
+
targetType: input.definition.delivery.targetType
|
|
226
332
|
},
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
});
|
|
235
|
-
let running = false;
|
|
236
|
-
return {
|
|
237
|
-
running: () => running,
|
|
238
|
-
status: () => supervisor.status(),
|
|
239
|
-
start: async () => {
|
|
240
|
-
await Effect.runPromise(supervisor.recover());
|
|
241
|
-
await Effect.runPromise(supervisor.start());
|
|
242
|
-
running = true;
|
|
243
|
-
},
|
|
244
|
-
stop: async () => {
|
|
245
|
-
await Effect.runPromise(supervisor.stop());
|
|
246
|
-
running = false;
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
};
|
|
250
|
-
return {
|
|
251
|
-
dispose: () => telemetry?.shutdown(),
|
|
252
|
-
createRecoveryControl: () =>
|
|
253
|
-
openJsonlRecoveryControl({
|
|
254
|
-
endpointsDirectory: join(STATE_DIR, "endpoints"),
|
|
255
|
-
instancesDirectory: join(STATE_DIR, "instances")
|
|
256
|
-
}),
|
|
257
|
-
createBackgroundSession: backgroundSessionsConfig?.enabled
|
|
258
|
-
? (input: CreateRivusDeploymentBackgroundSessionInput) => createBackgroundSessionAdapter(input)
|
|
259
|
-
: undefined,
|
|
260
|
-
createAutomation: async (input: CreateRivusDeploymentAutomationInput) => {
|
|
261
|
-
const credentials = resolveFeishuEndpointCredentials(input.deliveryEndpoint.credentialRef, context.env);
|
|
262
|
-
const config: RivusDaemonConfig = {
|
|
263
|
-
agentId: input.definition.agentId,
|
|
264
|
-
feishu: {
|
|
265
|
-
...credentials,
|
|
266
|
-
baseUrl: input.deliveryEndpoint.baseUrl,
|
|
267
|
-
cardStreamLeaseMs: input.deliveryEndpoint.cardStreamLeaseMs,
|
|
268
|
-
streamMinIntervalMs: input.deliveryEndpoint.streamMinIntervalMs
|
|
269
|
-
},
|
|
270
|
-
pi: {}
|
|
271
|
-
};
|
|
272
|
-
const openApiClient = createOpenApiClient(config);
|
|
273
|
-
const sender = createConfiguredFeishuAutomationCardSender({
|
|
274
|
-
client: openApiClient,
|
|
275
|
-
config
|
|
276
|
-
});
|
|
277
|
-
const repository = await openJsonAutomationTickRepository({
|
|
278
|
-
filePath: join(
|
|
279
|
-
STATE_DIR,
|
|
280
|
-
"automations",
|
|
281
|
-
createHash("sha256").update(input.automationId).digest("hex").slice(0, 16),
|
|
282
|
-
"ticks.json"
|
|
283
|
-
)
|
|
284
|
-
});
|
|
285
|
-
const target = resolveAutomationTarget(input.definition.delivery.targetRef, context.env);
|
|
286
|
-
const automation = createScheduledAutomation({
|
|
287
|
-
automationId: input.automationId,
|
|
288
|
-
binding: {
|
|
289
|
-
agentId: input.definition.agentId,
|
|
290
|
-
bindingId: input.automationId,
|
|
291
|
-
delivery: {
|
|
292
|
-
endpointId: input.definition.delivery.endpointId,
|
|
293
|
-
targetKey: createHash("sha256").update(target).digest("hex"),
|
|
294
|
-
targetType: input.definition.delivery.targetType
|
|
333
|
+
schedule: input.definition.schedule,
|
|
334
|
+
servicePrincipalId: `automation:${input.automationId}`,
|
|
335
|
+
skillGrantRevision: input.definition.runtimeDefinition.skillGrantSet.revision,
|
|
336
|
+
skillIds: input.definition.template.requestedSkillIds,
|
|
337
|
+
templateId: input.definition.templateId,
|
|
338
|
+
timeZone: input.definition.timeZone,
|
|
339
|
+
toolIds: input.definition.template.requestedToolIds
|
|
295
340
|
},
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
});
|
|
344
|
-
const inbox = await openJsonlFeishuInboxRepository({ filePath: join(endpointState, "feishu-inbox.jsonl") });
|
|
345
|
-
const sessionStore = await openJsonFeishuSessionStore({
|
|
346
|
-
filePath: join(endpointState, "feishu-session-store.json")
|
|
347
|
-
});
|
|
348
|
-
const config: RivusDaemonConfig = {
|
|
349
|
-
agentId: input.agentId,
|
|
350
|
-
feishu: {
|
|
351
|
-
...credentials,
|
|
341
|
+
createInput: input.definition.template.createInput,
|
|
342
|
+
deliver: ({ body, idempotencyKey, presentation }) =>
|
|
343
|
+
Effect.runPromise(
|
|
344
|
+
sender.send({
|
|
345
|
+
idempotencyKey,
|
|
346
|
+
receiveId: target,
|
|
347
|
+
receiveIdType: input.definition.delivery.targetType,
|
|
348
|
+
markdown: body,
|
|
349
|
+
...(presentation === undefined ? {} : { presentation })
|
|
350
|
+
})
|
|
351
|
+
),
|
|
352
|
+
onError: () => {
|
|
353
|
+
console.error(`Scheduled Automation ${input.automationId} failed; it will retry`);
|
|
354
|
+
},
|
|
355
|
+
repository,
|
|
356
|
+
run: async (runInput) => {
|
|
357
|
+
const result = readAutomationRunResult(await input.run(runInput));
|
|
358
|
+
const projected =
|
|
359
|
+
result.body === undefined
|
|
360
|
+
? undefined
|
|
361
|
+
: input.definition.template.createPresentation?.({
|
|
362
|
+
occurrence: runInput.occurrence,
|
|
363
|
+
text: result.body
|
|
364
|
+
});
|
|
365
|
+
const presentation = projected ? readAutomationPresentation(projected) : undefined;
|
|
366
|
+
return { ...result, ...(presentation === undefined ? {} : { presentation }) };
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
return automation;
|
|
370
|
+
},
|
|
371
|
+
createEndpoint: async (input: CreateRivusDeploymentEndpointInput) => {
|
|
372
|
+
const credentials = resolveFeishuEndpointCredentials(input.definition.credentialRef, context.env);
|
|
373
|
+
const botOpenId = await resolveFeishuBotOpenId(credentials, input.definition.baseUrl);
|
|
374
|
+
const endpointState = join(STATE_DIR, "endpoints", input.endpointId);
|
|
375
|
+
const cardTargets = createJsonFileFeishuCardTargetRegistry({
|
|
376
|
+
filePath: join(endpointState, "feishu-card-targets.json")
|
|
377
|
+
});
|
|
378
|
+
const cardLedger = await openJsonlFeishuCardDeliveryLedger({
|
|
379
|
+
filePath: join(endpointState, "feishu-card-delivery.jsonl")
|
|
380
|
+
});
|
|
381
|
+
const inbox = await openJsonlFeishuInboxRepository({ filePath: join(endpointState, "feishu-inbox.jsonl") });
|
|
382
|
+
const sessionStore = await openJsonFeishuSessionStore({
|
|
383
|
+
filePath: join(endpointState, "feishu-session-store.json")
|
|
384
|
+
});
|
|
385
|
+
const config = createEndpointConfig(input.definition, input.agentId, context.env);
|
|
386
|
+
const openApiClient = createOpenApiClient(config);
|
|
387
|
+
const promptContext = createFeishuTopicContextResolver({
|
|
352
388
|
baseUrl: input.definition.baseUrl,
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
baseUrl: resolveExperimentalCotBaseUrl(context.env),
|
|
389
|
+
client: openApiClient
|
|
390
|
+
});
|
|
391
|
+
const cotPublisher = input.definition.experimental?.cotMessages
|
|
392
|
+
? createFeishuCotPublisher({
|
|
393
|
+
baseUrl: resolveExperimentalCotBaseUrl(context.env),
|
|
394
|
+
client: openApiClient,
|
|
395
|
+
minIntervalMs: input.definition.streamMinIntervalMs
|
|
396
|
+
})
|
|
397
|
+
: undefined;
|
|
398
|
+
const interactions = createHumanInteractionService({
|
|
399
|
+
clock: { now: () => new Date().toISOString() },
|
|
400
|
+
presenter: createConfiguredFeishuHumanInteractionPresenter({
|
|
366
401
|
client: openApiClient,
|
|
367
|
-
|
|
402
|
+
config
|
|
403
|
+
}),
|
|
404
|
+
repository: createJsonlHumanInteractionRepository({
|
|
405
|
+
filePath: join(endpointState, "human-interactions.jsonl")
|
|
368
406
|
})
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
407
|
+
});
|
|
408
|
+
const cardRollover = createConfiguredFeishuCardRolloverRuntime({
|
|
409
|
+
agentName: input.agentId,
|
|
410
|
+
cardTargets,
|
|
373
411
|
client: openApiClient,
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
repository: createJsonlHumanInteractionRepository({
|
|
377
|
-
filePath: join(endpointState, "human-interactions.jsonl")
|
|
378
|
-
})
|
|
379
|
-
});
|
|
380
|
-
const cardRollover = createConfiguredFeishuCardRolloverRuntime({
|
|
381
|
-
agentName: input.agentId,
|
|
382
|
-
cardTargets,
|
|
383
|
-
client: openApiClient,
|
|
384
|
-
clock: createSystemClock(),
|
|
385
|
-
config,
|
|
386
|
-
ledger: cardLedger,
|
|
387
|
-
onError: (error) => {
|
|
388
|
-
console.error(`Feishu card rollover failed for endpoint ${input.endpointId}`, error);
|
|
389
|
-
},
|
|
390
|
-
...(input.definition.progressDisplay === undefined
|
|
391
|
-
? {}
|
|
392
|
-
: { progressDisplay: input.definition.progressDisplay }),
|
|
393
|
-
sleep,
|
|
394
|
-
title: input.agentId
|
|
395
|
-
});
|
|
396
|
-
const publisher = cardRollover.rollover;
|
|
397
|
-
const endpointEvents = createJsonlAgentEventLog({
|
|
398
|
-
filePath: join(STATE_DIR, "instances", input.instanceId, "agent-events.jsonl")
|
|
399
|
-
});
|
|
400
|
-
const endpointInitialEvents = await Effect.runPromise(endpointEvents.readAll());
|
|
401
|
-
await Effect.runPromise(
|
|
402
|
-
createFeishuCardDeliveryReconciler({
|
|
403
|
-
events: endpointInitialEvents,
|
|
412
|
+
clock: createSystemClock(),
|
|
413
|
+
config,
|
|
404
414
|
ledger: cardLedger,
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
415
|
+
onError: (error) => {
|
|
416
|
+
console.error(`Feishu card rollover failed for endpoint ${input.endpointId}`, error);
|
|
417
|
+
},
|
|
418
|
+
...(input.definition.progressDisplay === undefined
|
|
419
|
+
? {}
|
|
420
|
+
: { progressDisplay: input.definition.progressDisplay }),
|
|
421
|
+
sleep,
|
|
422
|
+
title: input.agentId
|
|
423
|
+
});
|
|
424
|
+
const publisher = cardRollover.rollover;
|
|
425
|
+
const endpointEvents = createJsonlAgentEventLog({
|
|
426
|
+
filePath: join(STATE_DIR, "instances", input.instanceId, "agent-events.jsonl")
|
|
427
|
+
});
|
|
428
|
+
const endpointInitialEvents = await Effect.runPromise(endpointEvents.readAll());
|
|
429
|
+
await Effect.runPromise(
|
|
430
|
+
createFeishuCardDeliveryReconciler({
|
|
431
|
+
events: endpointInitialEvents,
|
|
432
|
+
ledger: cardLedger,
|
|
433
|
+
publish: (action) => publisher.publish(action)
|
|
434
|
+
}).reconcile()
|
|
435
|
+
);
|
|
436
|
+
const replies = createConfiguredFeishuTextReplySender({
|
|
437
|
+
client: openApiClient,
|
|
438
|
+
config
|
|
439
|
+
});
|
|
440
|
+
const prepareCardTarget = (run: FeishuAgentRunPreparation) => cardRollover.prepareRun(run);
|
|
441
|
+
const endpoint = createFeishuDeploymentEndpoint({
|
|
442
|
+
agentId: input.agentId,
|
|
443
|
+
botOpenId,
|
|
444
|
+
cardRollover: cardRollover.transport,
|
|
445
|
+
finalizeRun: (runId) => cardRollover.rollover.releaseRun(runId),
|
|
446
|
+
cancel: input.cancel,
|
|
447
|
+
endpointId: input.endpointId,
|
|
448
|
+
eventDispatcher: createLazyFeishuWebSocketEventDispatcher(() => new Lark.EventDispatcher({})),
|
|
449
|
+
groupPolicy: input.definition.groupPolicy,
|
|
450
|
+
handle: input.handle,
|
|
451
|
+
inboxRepository: inbox,
|
|
452
|
+
initialEvents: endpointInitialEvents,
|
|
453
|
+
interactions,
|
|
454
|
+
maxPendingMessages: 100,
|
|
455
|
+
memoryTenantId,
|
|
456
|
+
...(input.projectSpaceId ? { projectSpaceId: input.projectSpaceId } : {}),
|
|
457
|
+
onCapacityExceeded: (payload) =>
|
|
458
|
+
replies.reply(payload.event.message.message_id, "Rivus is busy. Please retry in a moment."),
|
|
459
|
+
prepareRun: createFeishuPresentationPreparation({
|
|
460
|
+
...(cotPublisher ? { cotPublisher } : {}),
|
|
461
|
+
prepareCardTarget,
|
|
462
|
+
reportCotError: (operation, error) => reportCotError(input.endpointId, operation, error)
|
|
463
|
+
}),
|
|
464
|
+
promptContext,
|
|
465
|
+
publish: (action) => publisher.publish(action),
|
|
466
|
+
reply: (messageId, text) => replies.reply(messageId, text),
|
|
467
|
+
sessionStore,
|
|
468
|
+
...(input.steer ? { steer: input.steer } : {}),
|
|
469
|
+
...(cotPublisher
|
|
470
|
+
? {
|
|
471
|
+
publishRunUpdate: (update) =>
|
|
472
|
+
cotPublisher
|
|
473
|
+
.publish(update)
|
|
474
|
+
.pipe(Effect.catchAll((error) => reportCotError(input.endpointId, "update", error)))
|
|
475
|
+
}
|
|
476
|
+
: {}),
|
|
477
|
+
sessionNamespace: input.definition.sessionNamespace,
|
|
478
|
+
sleep,
|
|
479
|
+
websocketClient: createLazyFeishuWebSocketClient(credentials, input.definition.baseUrl),
|
|
480
|
+
workerConcurrency: 4
|
|
481
|
+
});
|
|
482
|
+
interactionRegistry.register(input.endpointId, interactions);
|
|
483
|
+
return endpoint;
|
|
484
|
+
},
|
|
485
|
+
createRuntime: async (input: CreateRivusDeploymentRuntimeInput) => {
|
|
486
|
+
const instanceState = join(STATE_DIR, "instances", input.instanceId);
|
|
487
|
+
await mkdir(instanceState, { recursive: true });
|
|
488
|
+
const eventLog = createJsonlAgentEventLog({ filePath: join(instanceState, "agent-events.jsonl") });
|
|
489
|
+
const initialEvents = await Effect.runPromise(eventLog.readAll());
|
|
490
|
+
const broker = createToolBroker({
|
|
491
|
+
approvals: createRoutedHumanInteractionToolApprovalService(interactionRegistry),
|
|
492
|
+
catalog: input.catalog,
|
|
493
|
+
hostTools: [
|
|
494
|
+
...(input.definition.memory.tool ? [createRivusMemoryToolDescriptor({ memory })] : []),
|
|
495
|
+
...(backgroundService
|
|
496
|
+
? [
|
|
497
|
+
...createBackgroundSessionHostTools({
|
|
498
|
+
createSessionId: () => `bg-${randomUUID()}`,
|
|
499
|
+
definition: input.definition,
|
|
500
|
+
service: backgroundService
|
|
501
|
+
})
|
|
502
|
+
]
|
|
503
|
+
: [])
|
|
504
|
+
],
|
|
505
|
+
operations: await openJsonlToolOperationLedger({ filePath: join(instanceState, "tool-operations.jsonl") }),
|
|
506
|
+
policy: { current: async () => ({ epoch: 1, revokedToolIds: [] }) }
|
|
507
|
+
});
|
|
508
|
+
const resolveToolName = createPiToolNameResolver(input.definition.tools);
|
|
509
|
+
const skillRuntime = createPiSkillRuntime(input.definition.skills);
|
|
510
|
+
const workingDirectory = input.projectSpace?.workingDirectory ?? process.cwd();
|
|
511
|
+
const runContexts = management?.enabled
|
|
512
|
+
? management.createRunContexts({
|
|
492
513
|
agentId: input.agentId,
|
|
493
|
-
|
|
494
|
-
|
|
514
|
+
instanceId: input.instanceId,
|
|
515
|
+
toolGrantSet: input.definition.toolGrantSet
|
|
495
516
|
})
|
|
496
517
|
: undefined;
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
518
|
+
const workspaceRoot = input.projectSpace?.root ?? process.cwd();
|
|
519
|
+
const workspaceInstructions = await createAgentsMdInstructionsProvider().resolve({
|
|
520
|
+
maxBytes: 64 * 1024,
|
|
521
|
+
workingDirectory: relative(workspaceRoot, workingDirectory) || ".",
|
|
522
|
+
workspaceRoot: await createWorkspaceRootHandle(workspaceRoot)
|
|
523
|
+
});
|
|
524
|
+
const prepareProjectMemory =
|
|
525
|
+
input.projectSpace && input.definition.memory.scopes.includes("project")
|
|
526
|
+
? createProjectMemoryPromptPreparer({
|
|
527
|
+
agentId: input.agentId,
|
|
528
|
+
memory,
|
|
529
|
+
projectId: input.projectSpace.id
|
|
530
|
+
})
|
|
531
|
+
: undefined;
|
|
532
|
+
const sessionRegistry = createPiSessionRegistry({
|
|
533
|
+
createSession: async (firstInput) => {
|
|
534
|
+
let activeInput = firstInput;
|
|
535
|
+
const managedContext =
|
|
536
|
+
management?.enabled && runContexts && input.definition.runtimeToolGrantSet.toolIds.includes("bash")
|
|
537
|
+
? createPiManagedSessionContext({
|
|
538
|
+
binDirectory: management.binDirectory,
|
|
539
|
+
contexts: runContexts.registry,
|
|
540
|
+
cwd: workingDirectory,
|
|
541
|
+
nodeExecutable: process.execPath,
|
|
542
|
+
socketPath: management.socketPath
|
|
543
|
+
})
|
|
544
|
+
: undefined;
|
|
545
|
+
const resources = await createPiSessionResources({
|
|
546
|
+
agentDir: PI_AGENT_DIR,
|
|
547
|
+
appendSystemPromptOverride: () =>
|
|
548
|
+
[workspaceInstructions.content, skillRuntime.prompt].filter((content) => content.length > 0),
|
|
511
549
|
cwd: workingDirectory,
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
550
|
+
homeDirectory: homedir(),
|
|
551
|
+
...(input.projectSpace ? { projectSkillPaths: input.projectSpace.skillPaths } : {}),
|
|
552
|
+
systemPromptOverride: () => input.definition.systemPrompt
|
|
553
|
+
});
|
|
554
|
+
const customTools = [
|
|
555
|
+
...(managedContext ? [managedContext.tool] : []),
|
|
556
|
+
...createPiSkillReadTools({
|
|
557
|
+
cwd: workingDirectory,
|
|
558
|
+
runtimeToolIds: input.definition.runtimeToolGrantSet.toolIds,
|
|
559
|
+
skillPaths: resources.skillNames.size > 0 ? resources.skillPaths : []
|
|
560
|
+
}),
|
|
561
|
+
...createPiToolProxyDefinitions({
|
|
562
|
+
agentId: input.agentId,
|
|
563
|
+
approvals: createHumanInteractionToolApprovalGateway({ registry: interactionRegistry }),
|
|
564
|
+
broker,
|
|
565
|
+
getActiveInput: () => activeInput,
|
|
566
|
+
instanceId: input.instanceId,
|
|
567
|
+
memoryScopes: input.definition.memory.scopes,
|
|
568
|
+
toolGrantSet: input.definition.toolGrantSet,
|
|
569
|
+
tools: input.definition.tools
|
|
570
|
+
}),
|
|
571
|
+
...(skillRuntime.tool ? [skillRuntime.tool] : [])
|
|
572
|
+
];
|
|
573
|
+
const nativeToolIds = managedContext
|
|
574
|
+
? input.definition.runtimeToolGrantSet.toolIds.filter((toolId) => toolId !== "bash")
|
|
575
|
+
: input.definition.runtimeToolGrantSet.toolIds;
|
|
576
|
+
const activeToolNames = resolvePiSessionToolNames(nativeToolIds, customTools);
|
|
577
|
+
const result = await createAgentSession(
|
|
578
|
+
resources.withSessionOptions({
|
|
579
|
+
...piOptions,
|
|
580
|
+
...(management?.enabled ? management.runtime.getSessionOptions() : {}),
|
|
581
|
+
customTools,
|
|
582
|
+
excludeTools: [],
|
|
583
|
+
sessionManager: SessionManager.create(workingDirectory, join(instanceState, "sessions")),
|
|
584
|
+
tools: [...activeToolNames]
|
|
585
|
+
})
|
|
586
|
+
);
|
|
587
|
+
return {
|
|
588
|
+
activate: (loopInput) => {
|
|
589
|
+
activeInput = loopInput;
|
|
590
|
+
managedContext?.activate(loopInput);
|
|
591
|
+
},
|
|
592
|
+
deactivate: () => managedContext?.deactivate(),
|
|
593
|
+
dispose: () => result.session.dispose(),
|
|
594
|
+
preparePrompt: async (loopInput) => {
|
|
595
|
+
validatePiSkillCommand(loopInput.text, resources.skillNames);
|
|
596
|
+
return prepareProjectMemory ? prepareProjectMemory(loopInput) : loopInput.text;
|
|
597
|
+
},
|
|
598
|
+
resolveToolName,
|
|
599
|
+
refreshResources: () => resources.refresh(),
|
|
600
|
+
session: result.session
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
const unregisterModels = management?.enabled
|
|
605
|
+
? management.runtime.registerSessionRegistry(sessionRegistry)
|
|
606
|
+
: undefined;
|
|
607
|
+
const loop = createPiAgentLoop({
|
|
608
|
+
disposeSessionAfterRun: false,
|
|
609
|
+
...(management?.enabled ? { runBoundary: management.boundary } : {}),
|
|
610
|
+
...(telemetry ? { modelContentObserver: telemetry.modelContentObserver } : {}),
|
|
611
|
+
resolveSession: (loopInput) => sessionRegistry.resolve(loopInput)
|
|
612
|
+
});
|
|
613
|
+
const scheduler = createSessionScheduler({
|
|
614
|
+
createRuntime: (sessionKey) =>
|
|
615
|
+
createAgentHarnessPooledRuntime(
|
|
616
|
+
createAgentHarness({
|
|
617
|
+
clock: createSystemClock(),
|
|
618
|
+
eventSinks: telemetry ? [eventLog, telemetry.sink] : [eventLog],
|
|
619
|
+
initialEvents: eventsForSession(initialEvents, sessionKey),
|
|
620
|
+
loop,
|
|
621
|
+
runIds: createUuidRunIds(),
|
|
622
|
+
...(input.binding.kind === "background-session" && backgroundSessionsConfig
|
|
623
|
+
? { runTimeoutMs: backgroundSessionsConfig.stepTimeoutMs }
|
|
624
|
+
: {})
|
|
625
|
+
})
|
|
626
|
+
),
|
|
627
|
+
maxConcurrentSessions:
|
|
628
|
+
input.binding.kind === "background-session" ? (backgroundSessionsConfig?.maxConcurrentSessions ?? 4) : 4,
|
|
629
|
+
maxQueuedRuns: 32
|
|
630
|
+
});
|
|
631
|
+
return {
|
|
632
|
+
...scheduler,
|
|
633
|
+
dispose: async () => {
|
|
634
|
+
await scheduler.dispose?.();
|
|
635
|
+
await sessionRegistry.disposeAll();
|
|
636
|
+
unregisterModels?.();
|
|
637
|
+
runContexts?.unregister();
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
} catch (error) {
|
|
643
|
+
await Promise.allSettled([management?.enabled ? management.close() : Promise.resolve(), telemetry?.shutdown()]);
|
|
644
|
+
throw error;
|
|
645
|
+
}
|
|
583
646
|
}
|
|
584
647
|
|
|
585
648
|
function eventsForSession<T extends { readonly runId: string; readonly sessionKey?: string }>(
|
|
@@ -719,7 +782,10 @@ async function resolveFeishuBotOpenId(
|
|
|
719
782
|
return openId;
|
|
720
783
|
}
|
|
721
784
|
|
|
722
|
-
async function createPiSessionOptions(
|
|
785
|
+
async function createPiSessionOptions(
|
|
786
|
+
context: RivusDeploymentBootstrapContext,
|
|
787
|
+
deferModel = false
|
|
788
|
+
): Promise<PiSessionOptions> {
|
|
723
789
|
const modelReference = optional(context.env.PI_MODEL);
|
|
724
790
|
const configuredModel = modelReference ? parseModelReference(modelReference) : undefined;
|
|
725
791
|
const provider = configuredModel?.provider;
|
|
@@ -728,16 +794,28 @@ async function createPiSessionOptions(context: RivusDeploymentBootstrapContext):
|
|
|
728
794
|
if ((apiKey || baseUrl) && !provider) {
|
|
729
795
|
throw new Error("PI_API_KEY and PI_BASE_URL require PI_MODEL in provider/model form");
|
|
730
796
|
}
|
|
731
|
-
if (baseUrl) await writeProviderBaseUrlOverride(provider!, baseUrl);
|
|
797
|
+
if (baseUrl && !deferModel) await writeProviderBaseUrlOverride(provider!, baseUrl);
|
|
732
798
|
const modelRuntime = await ModelRuntime.create({
|
|
733
799
|
allowModelNetwork: false,
|
|
734
800
|
authPath: PI_AUTH_FILE,
|
|
735
|
-
modelsPath: baseUrl ? PI_MODELS_FILE : null
|
|
801
|
+
modelsPath: baseUrl || deferModel ? PI_MODELS_FILE : null
|
|
736
802
|
});
|
|
737
803
|
if (apiKey) await modelRuntime.setRuntimeApiKey(provider!, apiKey);
|
|
738
|
-
const model =
|
|
739
|
-
|
|
740
|
-
|
|
804
|
+
const model =
|
|
805
|
+
configuredModel && !deferModel
|
|
806
|
+
? modelRuntime.getModel(configuredModel.provider, configuredModel.modelId)
|
|
807
|
+
: undefined;
|
|
808
|
+
if (modelReference && !model && !deferModel)
|
|
809
|
+
throw new Error(`PI_MODEL ${modelReference} was not found in the Pi model runtime`);
|
|
810
|
+
const settings = deferModel
|
|
811
|
+
? SettingsManager.create(process.cwd(), PI_AGENT_DIR, { projectTrusted: false })
|
|
812
|
+
: undefined;
|
|
813
|
+
const thinkingLevel =
|
|
814
|
+
readThinkingLevel(context.env.PI_THINKING_LEVEL) ??
|
|
815
|
+
(configuredModel
|
|
816
|
+
? settings?.getModelThinkingLevel(configuredModel.provider, configuredModel.modelId)
|
|
817
|
+
: undefined) ??
|
|
818
|
+
settings?.getDefaultThinkingLevel();
|
|
741
819
|
return {
|
|
742
820
|
cwd: process.cwd(),
|
|
743
821
|
modelRuntime,
|
|
@@ -746,6 +824,23 @@ async function createPiSessionOptions(context: RivusDeploymentBootstrapContext):
|
|
|
746
824
|
};
|
|
747
825
|
}
|
|
748
826
|
|
|
827
|
+
function createEndpointConfig(
|
|
828
|
+
definition: RivusEndpointDeployment,
|
|
829
|
+
agentId: string,
|
|
830
|
+
env: RivusDaemonEnv
|
|
831
|
+
): RivusDaemonConfig {
|
|
832
|
+
return {
|
|
833
|
+
agentId,
|
|
834
|
+
feishu: {
|
|
835
|
+
...resolveFeishuEndpointCredentials(definition.credentialRef, env),
|
|
836
|
+
baseUrl: definition.baseUrl,
|
|
837
|
+
cardStreamLeaseMs: definition.cardStreamLeaseMs,
|
|
838
|
+
streamMinIntervalMs: definition.streamMinIntervalMs
|
|
839
|
+
},
|
|
840
|
+
pi: {}
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
|
|
749
844
|
async function resolvePiApiKey(env: Readonly<Record<string, string | undefined>>): Promise<string | undefined> {
|
|
750
845
|
const inline = optional(env.PI_API_KEY);
|
|
751
846
|
const filePath = optional(env.PI_API_KEY_FILE);
|