@pikku/core 0.12.2 → 0.12.3
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/CHANGELOG.md +18 -0
- package/dist/crypto-utils.d.ts +10 -0
- package/dist/crypto-utils.js +62 -0
- package/dist/function/function-runner.js +4 -4
- package/dist/function/functions.types.d.ts +26 -1
- package/dist/function/functions.types.js +16 -0
- package/dist/handle-error.d.ts +1 -1
- package/dist/handle-error.js +7 -3
- package/dist/index.d.ts +14 -2
- package/dist/index.js +1 -1
- package/dist/pikku-state.js +3 -1
- package/dist/schema.js +1 -1
- package/dist/services/in-memory-workflow-service.d.ts +8 -2
- package/dist/services/in-memory-workflow-service.js +28 -1
- package/dist/services/workflow-service.d.ts +8 -2
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/service-tests.d.ts +31 -0
- package/dist/testing/service-tests.js +598 -0
- package/dist/types/core.types.d.ts +9 -1
- package/dist/types/state.types.d.ts +4 -2
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +6 -0
- package/dist/wirings/ai-agent/agent-dynamic-workflow.js +348 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +1 -1
- package/dist/wirings/ai-agent/ai-agent-memory.js +6 -5
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -7
- package/dist/wirings/ai-agent/ai-agent-prepare.js +127 -47
- package/dist/wirings/ai-agent/ai-agent-runner.d.ts +4 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +381 -27
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +7 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +109 -46
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +22 -1
- package/dist/wirings/ai-agent/index.d.ts +1 -2
- package/dist/wirings/ai-agent/index.js +1 -2
- package/dist/wirings/channel/channel-handler.js +5 -1
- package/dist/wirings/channel/channel.types.d.ts +1 -7
- package/dist/wirings/channel/local/local-channel-handler.d.ts +1 -1
- package/dist/wirings/channel/local/local-channel-handler.js +5 -3
- package/dist/wirings/channel/local/local-channel-runner.js +14 -4
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +4 -1
- package/dist/wirings/cli/cli-runner.js +10 -2
- package/dist/wirings/cli/cli.types.d.ts +1 -0
- package/dist/wirings/http/http-routes.js +0 -1
- package/dist/wirings/http/http-runner.d.ts +1 -1
- package/dist/wirings/http/http-runner.js +25 -3
- package/dist/wirings/http/http.types.d.ts +3 -11
- package/dist/wirings/http/index.d.ts +2 -1
- package/dist/wirings/http/index.js +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
- package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-response.js +4 -4
- package/dist/wirings/http/web-request.d.ts +12 -0
- package/dist/wirings/http/web-request.js +85 -0
- package/dist/wirings/mcp/mcp-endpoint-registry.js +1 -1
- package/dist/wirings/mcp/mcp-runner.js +13 -1
- package/dist/wirings/rpc/rpc-runner.d.ts +30 -6
- package/dist/wirings/rpc/rpc-runner.js +36 -14
- package/dist/wirings/rpc/rpc-types.d.ts +6 -1
- package/dist/wirings/rpc/wire-addon.d.ts +1 -0
- package/dist/wirings/rpc/wire-addon.js +1 -1
- package/dist/wirings/scheduler/scheduler-runner.js +1 -0
- package/dist/wirings/workflow/graph/graph-validation.d.ts +3 -0
- package/dist/wirings/workflow/graph/graph-validation.js +175 -0
- package/dist/wirings/workflow/graph/index.d.ts +1 -0
- package/dist/wirings/workflow/graph/index.js +1 -0
- package/dist/wirings/workflow/index.d.ts +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -2
- package/dist/wirings/workflow/pikku-workflow-service.js +1 -2
- package/dist/wirings/workflow/workflow.types.d.ts +10 -1
- package/package.json +4 -2
- package/src/crypto-utils.test.ts +99 -1
- package/src/crypto-utils.ts +114 -0
- package/src/function/function-runner.ts +5 -5
- package/src/function/functions.types.ts +38 -1
- package/src/handle-error.test.ts +65 -2
- package/src/handle-error.ts +8 -4
- package/src/index.ts +14 -0
- package/src/pikku-state.ts +3 -1
- package/src/schema.ts +1 -1
- package/src/services/in-memory-workflow-service.ts +45 -2
- package/src/services/workflow-service.ts +12 -1
- package/src/testing/index.ts +2 -0
- package/src/testing/service-tests.ts +873 -0
- package/src/types/core.types.ts +9 -1
- package/src/types/state.types.ts +4 -2
- package/src/utils/hash.test.ts +68 -0
- package/src/utils/hash.ts +26 -0
- package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +7 -6
- package/src/wirings/ai-agent/ai-agent-prepare.ts +175 -61
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +3 -1
- package/src/wirings/ai-agent/ai-agent-runner.ts +553 -31
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +209 -2
- package/src/wirings/ai-agent/ai-agent-stream.ts +182 -89
- package/src/wirings/ai-agent/ai-agent.types.ts +23 -1
- package/src/wirings/ai-agent/index.ts +1 -5
- package/src/wirings/channel/channel-handler.ts +11 -2
- package/src/wirings/channel/channel.types.ts +1 -7
- package/src/wirings/channel/local/local-channel-handler.ts +5 -3
- package/src/wirings/channel/local/local-channel-runner.ts +17 -7
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +4 -1
- package/src/wirings/cli/cli-runner.ts +12 -2
- package/src/wirings/cli/cli.types.ts +1 -0
- package/src/wirings/http/http-routes.ts +0 -1
- package/src/wirings/http/http-runner.ts +35 -10
- package/src/wirings/http/http.types.ts +3 -12
- package/src/wirings/http/index.ts +3 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +4 -0
- package/src/wirings/http/pikku-fetch-http-response.ts +4 -4
- package/src/wirings/http/web-request.test.ts +236 -0
- package/src/wirings/http/web-request.ts +104 -0
- package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
- package/src/wirings/mcp/mcp-runner.ts +30 -12
- package/src/wirings/rpc/rpc-runner.ts +45 -24
- package/src/wirings/rpc/rpc-types.ts +6 -1
- package/src/wirings/rpc/wire-addon.ts +2 -1
- package/src/wirings/scheduler/scheduler-runner.ts +1 -0
- package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
- package/src/wirings/workflow/graph/graph-validation.ts +237 -0
- package/src/wirings/workflow/graph/index.ts +5 -0
- package/src/wirings/workflow/index.ts +1 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +14 -3
- package/src/wirings/workflow/workflow.types.ts +9 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +0 -5
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +0 -168
- package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +0 -363
- package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +0 -238
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { saveMessages } from './ai-agent-memory.js';
|
|
2
|
-
import { prepareAgentRun } from './ai-agent-prepare.js';
|
|
3
|
-
import {
|
|
1
|
+
import { saveMessages, resolveMemoryServices, loadContextMessages, trimMessages, } from './ai-agent-memory.js';
|
|
2
|
+
import { prepareAgentRun, resolveAgent, buildInstructions, buildToolDefs, } from './ai-agent-prepare.js';
|
|
3
|
+
import { checkForApprovals, appendStepMessages } from './ai-agent-stream.js';
|
|
4
|
+
import { pikkuState, getSingletonServices } from '../../pikku-state.js';
|
|
5
|
+
import { resolveModelConfig } from './ai-agent-model-config.js';
|
|
4
6
|
import { randomUUID } from 'crypto';
|
|
5
7
|
export async function runAIAgent(agentName, input, params, agentSessionMap) {
|
|
6
8
|
const sessionMap = agentSessionMap ?? new Map();
|
|
@@ -105,33 +107,79 @@ export async function runAIAgent(agentName, input, params, agentSessionMap) {
|
|
|
105
107
|
});
|
|
106
108
|
if (stepResult.toolCalls.length === 0)
|
|
107
109
|
break;
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
110
|
+
const approvalsNeeded = checkForApprovals(stepResult, runnerParams.tools, runId);
|
|
111
|
+
if (approvalsNeeded.length > 0) {
|
|
112
|
+
for (const approval of approvalsNeeded) {
|
|
113
|
+
const toolDef = runnerParams.tools.find((t) => t.name === approval.toolName);
|
|
114
|
+
if (toolDef?.approvalDescriptionFn && !approval.reason) {
|
|
115
|
+
try {
|
|
116
|
+
approval.reason = await toolDef.approvalDescriptionFn(approval.args);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// If description generation fails, continue without it
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const pendingApprovals = approvalsNeeded.map((a) => a.agentRunId
|
|
124
|
+
? {
|
|
125
|
+
type: 'agent-call',
|
|
126
|
+
toolCallId: a.toolCallId,
|
|
127
|
+
agentName: a.toolName,
|
|
128
|
+
agentRunId: a.agentRunId,
|
|
129
|
+
displayToolName: a.displayToolName ?? a.toolName,
|
|
130
|
+
displayArgs: a.displayArgs ?? a.args,
|
|
131
|
+
}
|
|
132
|
+
: {
|
|
133
|
+
type: 'tool-call',
|
|
134
|
+
toolCallId: a.toolCallId,
|
|
135
|
+
toolName: a.toolName,
|
|
136
|
+
args: a.args,
|
|
137
|
+
});
|
|
138
|
+
const completedStepsForSave = accumulatedSteps.slice(0, -1);
|
|
139
|
+
await saveMessages(storage, threadId, input.resourceId, memoryConfig, userMessage, { text: '', steps: completedStepsForSave }, {
|
|
140
|
+
workingMemoryJsonSchema,
|
|
141
|
+
workingMemorySchemaName,
|
|
142
|
+
logger: singletonServices.logger,
|
|
143
|
+
schemaService: singletonServices.schema,
|
|
144
|
+
});
|
|
145
|
+
if (storage) {
|
|
146
|
+
await storage.saveMessages(threadId, [
|
|
147
|
+
{
|
|
148
|
+
id: randomUUID(),
|
|
149
|
+
role: 'assistant',
|
|
150
|
+
toolCalls: stepResult.toolCalls.map((tc) => ({
|
|
151
|
+
id: tc.toolCallId,
|
|
152
|
+
name: tc.toolName,
|
|
153
|
+
args: tc.args,
|
|
154
|
+
})),
|
|
155
|
+
createdAt: new Date(),
|
|
156
|
+
},
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
await aiRunState.updateRun(runId, {
|
|
160
|
+
status: 'suspended',
|
|
161
|
+
suspendReason: 'approval',
|
|
162
|
+
pendingApprovals,
|
|
163
|
+
usage: { ...totalUsage, model: agent.model },
|
|
164
|
+
});
|
|
165
|
+
const suspendedFinalText = lastStepResult?.text ?? '';
|
|
166
|
+
return {
|
|
167
|
+
runId,
|
|
168
|
+
text: suspendedFinalText,
|
|
169
|
+
threadId,
|
|
170
|
+
steps: accumulatedSteps,
|
|
171
|
+
usage: totalUsage,
|
|
172
|
+
status: 'suspended',
|
|
173
|
+
pendingApprovals: approvalsNeeded.map((a) => ({
|
|
174
|
+
toolCallId: a.toolCallId,
|
|
175
|
+
toolName: a.displayToolName ?? a.toolName,
|
|
176
|
+
args: a.displayArgs ?? a.args,
|
|
177
|
+
reason: a.reason,
|
|
178
|
+
runId,
|
|
130
179
|
})),
|
|
131
|
-
createdAt: new Date(),
|
|
132
180
|
};
|
|
133
|
-
runnerParams.messages.push(toolMsg);
|
|
134
181
|
}
|
|
182
|
+
appendStepMessages(runnerParams, stepResult);
|
|
135
183
|
}
|
|
136
184
|
const finalText = lastStepResult?.text ?? '';
|
|
137
185
|
const finalObject = lastStepResult?.object;
|
|
@@ -189,6 +237,312 @@ export async function runAIAgent(agentName, input, params, agentSessionMap) {
|
|
|
189
237
|
}
|
|
190
238
|
await aiRunState.updateRun(runId, {
|
|
191
239
|
status: 'failed',
|
|
240
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
241
|
+
});
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
export async function resumeAIAgentSync(runId, approvals, params, expectedAgentName) {
|
|
246
|
+
const singletonServices = getSingletonServices();
|
|
247
|
+
const { aiRunState } = singletonServices;
|
|
248
|
+
if (!aiRunState) {
|
|
249
|
+
throw new Error('AIRunStateService not available in singletonServices');
|
|
250
|
+
}
|
|
251
|
+
const run = await aiRunState.getRun(runId);
|
|
252
|
+
if (!run)
|
|
253
|
+
throw new Error(`No run found for runId ${runId}`);
|
|
254
|
+
if (expectedAgentName && run.agentName !== expectedAgentName) {
|
|
255
|
+
throw new Error(`Run ${runId} belongs to agent '${run.agentName}', not '${expectedAgentName}'`);
|
|
256
|
+
}
|
|
257
|
+
if (run.status !== 'suspended') {
|
|
258
|
+
throw new Error(`Run ${runId} is not suspended (status: ${run.status})`);
|
|
259
|
+
}
|
|
260
|
+
const { agent, packageName, resolvedName } = resolveAgent(run.agentName);
|
|
261
|
+
const { storage } = resolveMemoryServices(agent, singletonServices);
|
|
262
|
+
const memoryConfig = agent.memory;
|
|
263
|
+
const agentRunner = singletonServices.aiAgentRunner;
|
|
264
|
+
if (!agentRunner) {
|
|
265
|
+
throw new Error('AIAgentRunnerService not available');
|
|
266
|
+
}
|
|
267
|
+
const approvedIds = new Set(approvals.filter((a) => a.approved).map((a) => a.toolCallId));
|
|
268
|
+
const rejectedIds = new Set(approvals.filter((a) => !a.approved).map((a) => a.toolCallId));
|
|
269
|
+
const savedPendingApprovals = [...(run.pendingApprovals ?? [])];
|
|
270
|
+
for (const { toolCallId, approved } of approvals) {
|
|
271
|
+
await aiRunState.resolveApproval(toolCallId, approved ? 'approved' : 'denied');
|
|
272
|
+
}
|
|
273
|
+
const { tools } = await buildToolDefs(params, new Map(), run.resourceId, resolvedName, packageName, undefined, agent.aiMiddleware ?? []);
|
|
274
|
+
const toolCallMessages = [];
|
|
275
|
+
for (const pending of savedPendingApprovals) {
|
|
276
|
+
if (pending.type !== 'tool-call')
|
|
277
|
+
continue;
|
|
278
|
+
const toolCallId = pending.toolCallId;
|
|
279
|
+
let resultStr;
|
|
280
|
+
if (rejectedIds.has(toolCallId)) {
|
|
281
|
+
resultStr =
|
|
282
|
+
'The user explicitly declined this action. Inform them that it was declined and do not retry.';
|
|
283
|
+
}
|
|
284
|
+
else if (approvedIds.has(toolCallId)) {
|
|
285
|
+
const matchingTool = tools.find((t) => t.name === pending.toolName);
|
|
286
|
+
if (!matchingTool) {
|
|
287
|
+
throw new Error(`Tool "${pending.toolName}" not found in agent definition`);
|
|
288
|
+
}
|
|
289
|
+
const toolArgs = typeof pending.args === 'string'
|
|
290
|
+
? JSON.parse(pending.args)
|
|
291
|
+
: pending.args;
|
|
292
|
+
try {
|
|
293
|
+
const toolResult = await matchingTool.execute(toolArgs);
|
|
294
|
+
resultStr =
|
|
295
|
+
typeof toolResult === 'string'
|
|
296
|
+
? toolResult
|
|
297
|
+
: JSON.stringify(toolResult);
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
resultStr = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
toolCallMessages.push({
|
|
307
|
+
toolCallId,
|
|
308
|
+
toolName: pending.toolName,
|
|
309
|
+
args: typeof pending.args === 'string'
|
|
310
|
+
? JSON.parse(pending.args)
|
|
311
|
+
: pending.args,
|
|
312
|
+
result: resultStr,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (storage && toolCallMessages.length > 0) {
|
|
316
|
+
await storage.saveMessages(run.threadId, [
|
|
317
|
+
{
|
|
318
|
+
id: randomUUID(),
|
|
319
|
+
role: 'tool',
|
|
320
|
+
toolResults: toolCallMessages.map((tc) => ({
|
|
321
|
+
id: tc.toolCallId,
|
|
322
|
+
name: tc.toolName,
|
|
323
|
+
result: tc.result,
|
|
324
|
+
})),
|
|
325
|
+
createdAt: new Date(),
|
|
326
|
+
},
|
|
327
|
+
]);
|
|
328
|
+
}
|
|
329
|
+
await aiRunState.updateRun(runId, { status: 'running' });
|
|
330
|
+
return continueAfterToolResultSync(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, params, aiRunState);
|
|
331
|
+
}
|
|
332
|
+
async function continueAfterToolResultSync(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, params, aiRunState) {
|
|
333
|
+
const singletonServices = getSingletonServices();
|
|
334
|
+
const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
|
|
335
|
+
const meta = agentsMeta[resolvedName];
|
|
336
|
+
const workingMemorySchemaName = meta?.workingMemorySchema ?? null;
|
|
337
|
+
const messages = storage
|
|
338
|
+
? await storage.getMessages(run.threadId, {
|
|
339
|
+
lastN: memoryConfig?.lastMessages ?? 20,
|
|
340
|
+
})
|
|
341
|
+
: [];
|
|
342
|
+
const workingMemoryJsonSchema = workingMemorySchemaName
|
|
343
|
+
? pikkuState(packageName, 'misc', 'schemas').get(workingMemorySchemaName)
|
|
344
|
+
: undefined;
|
|
345
|
+
const contextMessages = await loadContextMessages(memoryConfig, storage, { message: '', threadId: run.threadId, resourceId: run.resourceId }, workingMemoryJsonSchema);
|
|
346
|
+
const allMessages = [...contextMessages, ...messages];
|
|
347
|
+
const trimmedMessages = trimMessages(allMessages);
|
|
348
|
+
const instructions = await buildInstructions(resolvedName, packageName);
|
|
349
|
+
const aiMiddlewares = agent.aiMiddleware ?? [];
|
|
350
|
+
let modifiedMessages = trimmedMessages;
|
|
351
|
+
let modifiedInstructions = instructions;
|
|
352
|
+
for (const mw of aiMiddlewares) {
|
|
353
|
+
if (mw.modifyInput) {
|
|
354
|
+
const result = await mw.modifyInput(singletonServices, {
|
|
355
|
+
messages: modifiedMessages,
|
|
356
|
+
instructions: modifiedInstructions,
|
|
357
|
+
});
|
|
358
|
+
modifiedMessages = result.messages;
|
|
359
|
+
modifiedInstructions = result.instructions;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const { tools: resumeTools } = await buildToolDefs(params, new Map(), run.resourceId, resolvedName, packageName, undefined, aiMiddlewares);
|
|
363
|
+
const resolved = resolveModelConfig(resolvedName, agent);
|
|
364
|
+
const maxSteps = resolved.maxSteps ?? 10;
|
|
365
|
+
const runnerParams = {
|
|
366
|
+
model: resolved.model,
|
|
367
|
+
temperature: resolved.temperature,
|
|
368
|
+
instructions: modifiedInstructions,
|
|
369
|
+
messages: modifiedMessages,
|
|
370
|
+
tools: resumeTools,
|
|
371
|
+
maxSteps: 1,
|
|
372
|
+
toolChoice: (agent.toolChoice ?? 'auto'),
|
|
373
|
+
outputSchema: meta?.outputSchema
|
|
374
|
+
? pikkuState(packageName, 'misc', 'schemas').get(meta.outputSchema)
|
|
375
|
+
: undefined,
|
|
376
|
+
};
|
|
377
|
+
try {
|
|
378
|
+
const accumulatedSteps = [];
|
|
379
|
+
const totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
380
|
+
let lastStepResult = null;
|
|
381
|
+
for (let step = 0; step < maxSteps; step++) {
|
|
382
|
+
const stepResult = await agentRunner.run(runnerParams);
|
|
383
|
+
lastStepResult = stepResult;
|
|
384
|
+
totalUsage.inputTokens += stepResult.usage.inputTokens;
|
|
385
|
+
totalUsage.outputTokens += stepResult.usage.outputTokens;
|
|
386
|
+
for (const mw of aiMiddlewares) {
|
|
387
|
+
if (mw.afterStep) {
|
|
388
|
+
await mw.afterStep(singletonServices, {
|
|
389
|
+
stepNumber: step,
|
|
390
|
+
text: stepResult.text,
|
|
391
|
+
toolCalls: stepResult.toolCalls,
|
|
392
|
+
toolResults: stepResult.toolResults,
|
|
393
|
+
usage: stepResult.usage,
|
|
394
|
+
finishReason: stepResult.finishReason,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
accumulatedSteps.push({
|
|
399
|
+
usage: stepResult.usage,
|
|
400
|
+
toolCalls: stepResult.toolCalls.map((tc) => {
|
|
401
|
+
const tr = stepResult.toolResults.find((r) => r.toolCallId === tc.toolCallId);
|
|
402
|
+
return {
|
|
403
|
+
name: tc.toolName,
|
|
404
|
+
args: tc.args,
|
|
405
|
+
result: typeof tr?.result === 'string'
|
|
406
|
+
? tr.result
|
|
407
|
+
: JSON.stringify(tr?.result ?? ''),
|
|
408
|
+
};
|
|
409
|
+
}),
|
|
410
|
+
});
|
|
411
|
+
if (stepResult.toolCalls.length === 0)
|
|
412
|
+
break;
|
|
413
|
+
const approvalsNeeded = checkForApprovals(stepResult, runnerParams.tools, run.runId);
|
|
414
|
+
if (approvalsNeeded.length > 0) {
|
|
415
|
+
for (const approval of approvalsNeeded) {
|
|
416
|
+
const toolDef = runnerParams.tools.find((t) => t.name === approval.toolName);
|
|
417
|
+
if (toolDef?.approvalDescriptionFn && !approval.reason) {
|
|
418
|
+
try {
|
|
419
|
+
approval.reason = await toolDef.approvalDescriptionFn(approval.args);
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
// ignore
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const pendingApprovals = approvalsNeeded.map((a) => a.agentRunId
|
|
427
|
+
? {
|
|
428
|
+
type: 'agent-call',
|
|
429
|
+
toolCallId: a.toolCallId,
|
|
430
|
+
agentName: a.toolName,
|
|
431
|
+
agentRunId: a.agentRunId,
|
|
432
|
+
displayToolName: a.displayToolName ?? a.toolName,
|
|
433
|
+
displayArgs: a.displayArgs ?? a.args,
|
|
434
|
+
}
|
|
435
|
+
: {
|
|
436
|
+
type: 'tool-call',
|
|
437
|
+
toolCallId: a.toolCallId,
|
|
438
|
+
toolName: a.toolName,
|
|
439
|
+
args: a.args,
|
|
440
|
+
});
|
|
441
|
+
const completedSteps = accumulatedSteps.slice(0, -1);
|
|
442
|
+
if (completedSteps.length > 0) {
|
|
443
|
+
await saveMessages(storage, run.threadId, run.resourceId, memoryConfig, null, { text: '', steps: completedSteps }, {
|
|
444
|
+
workingMemoryJsonSchema,
|
|
445
|
+
workingMemorySchemaName,
|
|
446
|
+
logger: singletonServices.logger,
|
|
447
|
+
schemaService: singletonServices.schema,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
if (storage) {
|
|
451
|
+
await storage.saveMessages(run.threadId, [
|
|
452
|
+
{
|
|
453
|
+
id: randomUUID(),
|
|
454
|
+
role: 'assistant',
|
|
455
|
+
toolCalls: stepResult.toolCalls.map((tc) => ({
|
|
456
|
+
id: tc.toolCallId,
|
|
457
|
+
name: tc.toolName,
|
|
458
|
+
args: tc.args,
|
|
459
|
+
})),
|
|
460
|
+
createdAt: new Date(),
|
|
461
|
+
},
|
|
462
|
+
]);
|
|
463
|
+
}
|
|
464
|
+
await aiRunState.updateRun(run.runId, {
|
|
465
|
+
status: 'suspended',
|
|
466
|
+
suspendReason: 'approval',
|
|
467
|
+
pendingApprovals,
|
|
468
|
+
usage: { ...totalUsage, model: agent.model },
|
|
469
|
+
});
|
|
470
|
+
const suspendedText = lastStepResult?.text ?? '';
|
|
471
|
+
return {
|
|
472
|
+
runId: run.runId,
|
|
473
|
+
text: suspendedText,
|
|
474
|
+
threadId: run.threadId,
|
|
475
|
+
steps: accumulatedSteps,
|
|
476
|
+
usage: totalUsage,
|
|
477
|
+
status: 'suspended',
|
|
478
|
+
pendingApprovals: approvalsNeeded.map((a) => ({
|
|
479
|
+
toolCallId: a.toolCallId,
|
|
480
|
+
toolName: a.displayToolName ?? a.toolName,
|
|
481
|
+
args: a.displayArgs ?? a.args,
|
|
482
|
+
reason: a.reason,
|
|
483
|
+
runId: run.runId,
|
|
484
|
+
})),
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
appendStepMessages(runnerParams, stepResult);
|
|
488
|
+
}
|
|
489
|
+
const finalText = lastStepResult?.text ?? '';
|
|
490
|
+
const finalObject = lastStepResult?.object;
|
|
491
|
+
const result = {
|
|
492
|
+
text: finalText,
|
|
493
|
+
steps: accumulatedSteps,
|
|
494
|
+
};
|
|
495
|
+
const responseText = await saveMessages(storage, run.threadId, run.resourceId, memoryConfig, null, result, {
|
|
496
|
+
workingMemoryJsonSchema,
|
|
497
|
+
workingMemorySchemaName,
|
|
498
|
+
logger: singletonServices.logger,
|
|
499
|
+
schemaService: singletonServices.schema,
|
|
500
|
+
});
|
|
501
|
+
let outputText = responseText;
|
|
502
|
+
let outputMessages = runnerParams.messages;
|
|
503
|
+
for (let i = aiMiddlewares.length - 1; i >= 0; i--) {
|
|
504
|
+
const mw = aiMiddlewares[i];
|
|
505
|
+
if (mw.modifyOutput) {
|
|
506
|
+
const modResult = await mw.modifyOutput(singletonServices, {
|
|
507
|
+
text: outputText,
|
|
508
|
+
messages: outputMessages,
|
|
509
|
+
usage: totalUsage,
|
|
510
|
+
});
|
|
511
|
+
outputText = modResult.text;
|
|
512
|
+
outputMessages = modResult.messages;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
await aiRunState.updateRun(run.runId, {
|
|
516
|
+
status: 'completed',
|
|
517
|
+
usage: { ...totalUsage, model: agent.model },
|
|
518
|
+
});
|
|
519
|
+
return {
|
|
520
|
+
runId: run.runId,
|
|
521
|
+
text: outputText,
|
|
522
|
+
object: finalObject,
|
|
523
|
+
threadId: run.threadId,
|
|
524
|
+
steps: accumulatedSteps,
|
|
525
|
+
usage: totalUsage,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
catch (error) {
|
|
529
|
+
for (const mw of aiMiddlewares) {
|
|
530
|
+
if (mw.onError) {
|
|
531
|
+
try {
|
|
532
|
+
await mw.onError(singletonServices, {
|
|
533
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
534
|
+
stepNumber: -1,
|
|
535
|
+
messages: runnerParams.messages,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
// ignore
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
await aiRunState.updateRun(run.runId, {
|
|
544
|
+
status: 'failed',
|
|
545
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
192
546
|
});
|
|
193
547
|
throw error;
|
|
194
548
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { AIStreamChannel } from './ai-agent.types.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { AIAgentRunnerParams, AIAgentStepResult } from '../../services/ai-agent-runner-service.js';
|
|
3
|
+
import { ToolApprovalRequired, type RunAIAgentParams, type StreamAIAgentOptions } from './ai-agent-prepare.js';
|
|
4
|
+
export declare function checkForApprovals(stepResult: AIAgentStepResult, tools: AIAgentRunnerParams['tools'], runId: string): ToolApprovalRequired[];
|
|
5
|
+
export declare function appendStepMessages(runnerParams: AIAgentRunnerParams, stepResult: AIAgentStepResult): void;
|
|
3
6
|
export declare function streamAIAgent(agentName: string, input: {
|
|
4
7
|
message: string;
|
|
5
8
|
threadId: string;
|
|
6
9
|
resourceId: string;
|
|
7
|
-
|
|
10
|
+
model?: string;
|
|
11
|
+
temperature?: number;
|
|
12
|
+
}, channel: AIStreamChannel, params: RunAIAgentParams, agentSessionMap?: Map<string, string>, options?: StreamAIAgentOptions): Promise<string>;
|
|
8
13
|
export declare function resumeAIAgent(input: {
|
|
9
14
|
runId: string;
|
|
10
15
|
toolCallId: string;
|