@pikku/core 0.12.1 → 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.
Files changed (165) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/crypto-utils.d.ts +10 -0
  3. package/dist/crypto-utils.js +62 -0
  4. package/dist/function/function-runner.js +4 -4
  5. package/dist/function/functions.types.d.ts +26 -1
  6. package/dist/function/functions.types.js +16 -0
  7. package/dist/handle-error.d.ts +1 -1
  8. package/dist/handle-error.js +7 -3
  9. package/dist/index.d.ts +14 -2
  10. package/dist/index.js +1 -1
  11. package/dist/middleware-runner.js +1 -0
  12. package/dist/permissions.js +1 -0
  13. package/dist/pikku-state.js +7 -1
  14. package/dist/schema.js +1 -1
  15. package/dist/services/gateway-service.d.ts +19 -0
  16. package/dist/services/gateway-service.js +1 -0
  17. package/dist/services/in-memory-workflow-service.d.ts +8 -2
  18. package/dist/services/in-memory-workflow-service.js +28 -1
  19. package/dist/services/index.d.ts +2 -0
  20. package/dist/services/index.js +1 -0
  21. package/dist/services/local-gateway-service.d.ts +22 -0
  22. package/dist/services/local-gateway-service.js +51 -0
  23. package/dist/services/workflow-service.d.ts +8 -2
  24. package/dist/testing/index.d.ts +2 -0
  25. package/dist/testing/index.js +1 -0
  26. package/dist/testing/service-tests.d.ts +31 -0
  27. package/dist/testing/service-tests.js +598 -0
  28. package/dist/types/core.types.d.ts +12 -2
  29. package/dist/types/state.types.d.ts +9 -2
  30. package/dist/utils/hash.d.ts +2 -0
  31. package/dist/utils/hash.js +23 -0
  32. package/dist/utils.js +1 -1
  33. package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +6 -0
  34. package/dist/wirings/ai-agent/agent-dynamic-workflow.js +348 -0
  35. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +1 -1
  36. package/dist/wirings/ai-agent/ai-agent-memory.js +19 -7
  37. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -7
  38. package/dist/wirings/ai-agent/ai-agent-prepare.js +141 -48
  39. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +4 -0
  40. package/dist/wirings/ai-agent/ai-agent-runner.js +381 -27
  41. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +7 -2
  42. package/dist/wirings/ai-agent/ai-agent-stream.js +126 -48
  43. package/dist/wirings/ai-agent/ai-agent.types.d.ts +57 -3
  44. package/dist/wirings/ai-agent/index.d.ts +2 -3
  45. package/dist/wirings/ai-agent/index.js +1 -2
  46. package/dist/wirings/channel/channel-handler.d.ts +5 -2
  47. package/dist/wirings/channel/channel-handler.js +16 -2
  48. package/dist/wirings/channel/channel-middleware-runner.js +1 -0
  49. package/dist/wirings/channel/channel.types.d.ts +7 -7
  50. package/dist/wirings/channel/index.d.ts +1 -1
  51. package/dist/wirings/channel/local/local-channel-handler.d.ts +8 -1
  52. package/dist/wirings/channel/local/local-channel-handler.js +22 -3
  53. package/dist/wirings/channel/local/local-channel-runner.js +28 -5
  54. package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +2 -1
  55. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  56. package/dist/wirings/channel/serverless/serverless-channel-runner.js +5 -2
  57. package/dist/wirings/cli/cli-runner.js +13 -2
  58. package/dist/wirings/cli/cli.types.d.ts +1 -0
  59. package/dist/wirings/gateway/gateway-runner.d.ts +24 -0
  60. package/dist/wirings/gateway/gateway-runner.js +325 -0
  61. package/dist/wirings/gateway/gateway.types.d.ts +127 -0
  62. package/dist/wirings/gateway/gateway.types.js +1 -0
  63. package/dist/wirings/gateway/index.d.ts +2 -0
  64. package/dist/wirings/gateway/index.js +1 -0
  65. package/dist/wirings/http/http-routes.js +0 -1
  66. package/dist/wirings/http/http-runner.d.ts +1 -1
  67. package/dist/wirings/http/http-runner.js +28 -3
  68. package/dist/wirings/http/http.types.d.ts +3 -11
  69. package/dist/wirings/http/index.d.ts +2 -1
  70. package/dist/wirings/http/index.js +1 -0
  71. package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
  72. package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
  73. package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
  74. package/dist/wirings/http/pikku-fetch-http-response.js +4 -4
  75. package/dist/wirings/http/web-request.d.ts +12 -0
  76. package/dist/wirings/http/web-request.js +85 -0
  77. package/dist/wirings/mcp/mcp-endpoint-registry.js +1 -1
  78. package/dist/wirings/mcp/mcp-runner.js +13 -1
  79. package/dist/wirings/rpc/rpc-runner.d.ts +30 -6
  80. package/dist/wirings/rpc/rpc-runner.js +36 -14
  81. package/dist/wirings/rpc/rpc-types.d.ts +6 -1
  82. package/dist/wirings/rpc/wire-addon.d.ts +1 -0
  83. package/dist/wirings/rpc/wire-addon.js +1 -1
  84. package/dist/wirings/scheduler/scheduler-runner.js +1 -0
  85. package/dist/wirings/workflow/graph/graph-validation.d.ts +3 -0
  86. package/dist/wirings/workflow/graph/graph-validation.js +175 -0
  87. package/dist/wirings/workflow/graph/index.d.ts +1 -0
  88. package/dist/wirings/workflow/graph/index.js +1 -0
  89. package/dist/wirings/workflow/index.d.ts +1 -1
  90. package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -2
  91. package/dist/wirings/workflow/pikku-workflow-service.js +1 -2
  92. package/dist/wirings/workflow/workflow.types.d.ts +10 -1
  93. package/package.json +6 -3
  94. package/src/crypto-utils.test.ts +99 -1
  95. package/src/crypto-utils.ts +114 -0
  96. package/src/function/function-runner.ts +5 -5
  97. package/src/function/functions.types.ts +38 -1
  98. package/src/handle-error.test.ts +65 -2
  99. package/src/handle-error.ts +8 -4
  100. package/src/index.ts +14 -0
  101. package/src/middleware-runner.ts +1 -0
  102. package/src/permissions.ts +1 -0
  103. package/src/pikku-state.ts +8 -1
  104. package/src/schema.ts +1 -1
  105. package/src/services/gateway-service.ts +20 -0
  106. package/src/services/in-memory-workflow-service.ts +45 -2
  107. package/src/services/index.ts +2 -0
  108. package/src/services/local-gateway-service.ts +62 -0
  109. package/src/services/workflow-service.ts +12 -1
  110. package/src/testing/index.ts +2 -0
  111. package/src/testing/service-tests.ts +873 -0
  112. package/src/types/core.types.ts +12 -1
  113. package/src/types/state.types.ts +12 -2
  114. package/src/utils/hash.test.ts +68 -0
  115. package/src/utils/hash.ts +26 -0
  116. package/src/utils.ts +1 -1
  117. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
  118. package/src/wirings/ai-agent/ai-agent-memory.ts +17 -7
  119. package/src/wirings/ai-agent/ai-agent-prepare.ts +194 -62
  120. package/src/wirings/ai-agent/ai-agent-runner.test.ts +3 -1
  121. package/src/wirings/ai-agent/ai-agent-runner.ts +553 -31
  122. package/src/wirings/ai-agent/ai-agent-stream.test.ts +209 -2
  123. package/src/wirings/ai-agent/ai-agent-stream.ts +195 -91
  124. package/src/wirings/ai-agent/ai-agent.types.ts +57 -3
  125. package/src/wirings/ai-agent/index.ts +3 -5
  126. package/src/wirings/channel/channel-handler.ts +41 -4
  127. package/src/wirings/channel/channel-middleware-runner.ts +1 -0
  128. package/src/wirings/channel/channel.types.ts +12 -7
  129. package/src/wirings/channel/index.ts +1 -0
  130. package/src/wirings/channel/local/local-channel-handler.ts +31 -3
  131. package/src/wirings/channel/local/local-channel-runner.ts +32 -8
  132. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +2 -0
  133. package/src/wirings/channel/pikku-abstract-channel-handler.ts +8 -1
  134. package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -2
  135. package/src/wirings/cli/cli-runner.ts +15 -2
  136. package/src/wirings/cli/cli.types.ts +1 -0
  137. package/src/wirings/gateway/gateway-runner.test.ts +474 -0
  138. package/src/wirings/gateway/gateway-runner.ts +411 -0
  139. package/src/wirings/gateway/gateway.types.ts +149 -0
  140. package/src/wirings/gateway/index.ts +13 -0
  141. package/src/wirings/http/http-routes.ts +0 -1
  142. package/src/wirings/http/http-runner.ts +38 -10
  143. package/src/wirings/http/http.types.ts +3 -12
  144. package/src/wirings/http/index.ts +3 -0
  145. package/src/wirings/http/pikku-fetch-http-request.ts +4 -0
  146. package/src/wirings/http/pikku-fetch-http-response.ts +4 -4
  147. package/src/wirings/http/web-request.test.ts +236 -0
  148. package/src/wirings/http/web-request.ts +104 -0
  149. package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
  150. package/src/wirings/mcp/mcp-runner.ts +30 -12
  151. package/src/wirings/rpc/rpc-runner.ts +45 -24
  152. package/src/wirings/rpc/rpc-types.ts +6 -1
  153. package/src/wirings/rpc/wire-addon.ts +2 -1
  154. package/src/wirings/scheduler/scheduler-runner.ts +1 -0
  155. package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
  156. package/src/wirings/workflow/graph/graph-validation.ts +237 -0
  157. package/src/wirings/workflow/graph/index.ts +5 -0
  158. package/src/wirings/workflow/index.ts +1 -0
  159. package/src/wirings/workflow/pikku-workflow-service.ts +14 -3
  160. package/src/wirings/workflow/workflow.types.ts +9 -1
  161. package/tsconfig.tsbuildinfo +1 -1
  162. package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +0 -5
  163. package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +0 -146
  164. package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +0 -363
  165. package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +0 -214
@@ -1,6 +1,8 @@
1
- import { saveMessages } from './ai-agent-memory.js';
2
- import { prepareAgentRun } from './ai-agent-prepare.js';
3
- import { getSingletonServices } from '../../pikku-state.js';
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 assistantMsg = {
109
- id: randomUUID(),
110
- role: 'assistant',
111
- content: stepResult.text || undefined,
112
- toolCalls: stepResult.toolCalls.map((tc) => ({
113
- id: tc.toolCallId,
114
- name: tc.toolName,
115
- args: tc.args,
116
- })),
117
- createdAt: new Date(),
118
- };
119
- runnerParams.messages.push(assistantMsg);
120
- if (stepResult.toolResults.length > 0) {
121
- const toolMsg = {
122
- id: randomUUID(),
123
- role: 'tool',
124
- toolResults: stepResult.toolResults.map((tr) => ({
125
- id: tr.toolCallId,
126
- name: tr.toolName,
127
- result: typeof tr.result === 'string'
128
- ? tr.result
129
- : JSON.stringify(tr.result),
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 { type RunAIAgentParams, type StreamAIAgentOptions } from './ai-agent-prepare.js';
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
- } | Record<string, unknown>, channel: AIStreamChannel, params: RunAIAgentParams, agentSessionMap?: Map<string, string>, options?: StreamAIAgentOptions): Promise<void>;
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;