@pikku/core 0.12.2 → 0.12.4

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 (282) hide show
  1. package/CHANGELOG.md +24 -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/pikku-state.js +3 -1
  12. package/dist/schema.js +1 -1
  13. package/dist/services/in-memory-workflow-service.d.ts +22 -3
  14. package/dist/services/in-memory-workflow-service.js +77 -1
  15. package/dist/services/workflow-service.d.ts +8 -2
  16. package/dist/testing/index.d.ts +2 -0
  17. package/dist/testing/index.js +1 -0
  18. package/dist/testing/service-tests.d.ts +31 -0
  19. package/dist/testing/service-tests.js +598 -0
  20. package/dist/types/core.types.d.ts +9 -1
  21. package/dist/types/state.types.d.ts +4 -2
  22. package/dist/utils/hash.d.ts +2 -0
  23. package/dist/utils/hash.js +23 -0
  24. package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +6 -0
  25. package/dist/wirings/ai-agent/agent-dynamic-workflow.js +348 -0
  26. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +1 -1
  27. package/dist/wirings/ai-agent/ai-agent-memory.js +6 -5
  28. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -7
  29. package/dist/wirings/ai-agent/ai-agent-prepare.js +127 -47
  30. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +4 -0
  31. package/dist/wirings/ai-agent/ai-agent-runner.js +381 -27
  32. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +7 -2
  33. package/dist/wirings/ai-agent/ai-agent-stream.js +109 -46
  34. package/dist/wirings/ai-agent/ai-agent.types.d.ts +22 -1
  35. package/dist/wirings/ai-agent/index.d.ts +1 -2
  36. package/dist/wirings/ai-agent/index.js +1 -2
  37. package/dist/wirings/channel/channel-handler.js +5 -1
  38. package/dist/wirings/channel/channel.types.d.ts +1 -7
  39. package/dist/wirings/channel/local/local-channel-handler.d.ts +1 -1
  40. package/dist/wirings/channel/local/local-channel-handler.js +5 -3
  41. package/dist/wirings/channel/local/local-channel-runner.js +14 -4
  42. package/dist/wirings/channel/serverless/serverless-channel-runner.js +4 -1
  43. package/dist/wirings/cli/cli-runner.js +10 -2
  44. package/dist/wirings/cli/cli.types.d.ts +1 -0
  45. package/dist/wirings/http/http-routes.js +0 -1
  46. package/dist/wirings/http/http-runner.d.ts +1 -1
  47. package/dist/wirings/http/http-runner.js +25 -3
  48. package/dist/wirings/http/http.types.d.ts +3 -11
  49. package/dist/wirings/http/index.d.ts +2 -1
  50. package/dist/wirings/http/index.js +1 -0
  51. package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
  52. package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
  53. package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
  54. package/dist/wirings/http/pikku-fetch-http-response.js +4 -4
  55. package/dist/wirings/http/web-request.d.ts +12 -0
  56. package/dist/wirings/http/web-request.js +85 -0
  57. package/dist/wirings/mcp/mcp-endpoint-registry.js +1 -1
  58. package/dist/wirings/mcp/mcp-runner.js +13 -1
  59. package/dist/wirings/rpc/rpc-runner.d.ts +30 -6
  60. package/dist/wirings/rpc/rpc-runner.js +36 -14
  61. package/dist/wirings/rpc/rpc-types.d.ts +6 -1
  62. package/dist/wirings/rpc/wire-addon.d.ts +1 -0
  63. package/dist/wirings/rpc/wire-addon.js +1 -1
  64. package/dist/wirings/scheduler/scheduler-runner.js +1 -0
  65. package/dist/wirings/workflow/graph/graph-validation.d.ts +3 -0
  66. package/dist/wirings/workflow/graph/graph-validation.js +175 -0
  67. package/dist/wirings/workflow/graph/index.d.ts +1 -0
  68. package/dist/wirings/workflow/graph/index.js +1 -0
  69. package/dist/wirings/workflow/index.d.ts +1 -1
  70. package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -2
  71. package/dist/wirings/workflow/pikku-workflow-service.js +1 -2
  72. package/dist/wirings/workflow/workflow.types.d.ts +10 -1
  73. package/lcov.info +18891 -0
  74. package/package.json +4 -2
  75. package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +0 -5
  76. package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +0 -168
  77. package/src/crypto-utils.test.ts +0 -116
  78. package/src/crypto-utils.ts +0 -99
  79. package/src/errors/error-handler.ts +0 -62
  80. package/src/errors/error.test.ts +0 -195
  81. package/src/errors/errors.ts +0 -374
  82. package/src/errors/index.ts +0 -2
  83. package/src/factory-functions.test.ts +0 -109
  84. package/src/function/function-runner.test.ts +0 -536
  85. package/src/function/function-runner.ts +0 -365
  86. package/src/function/functions.types.ts +0 -267
  87. package/src/function/index.ts +0 -5
  88. package/src/handle-error.test.ts +0 -361
  89. package/src/handle-error.ts +0 -65
  90. package/src/index.ts +0 -104
  91. package/src/internal.ts +0 -6
  92. package/src/middleware/auth-apikey.test.ts +0 -363
  93. package/src/middleware/auth-apikey.ts +0 -47
  94. package/src/middleware/auth-bearer.test.ts +0 -450
  95. package/src/middleware/auth-bearer.ts +0 -72
  96. package/src/middleware/auth-cookie.test.ts +0 -528
  97. package/src/middleware/auth-cookie.ts +0 -80
  98. package/src/middleware/cors.test.ts +0 -424
  99. package/src/middleware/cors.ts +0 -104
  100. package/src/middleware/index.ts +0 -5
  101. package/src/middleware/remote-auth.test.ts +0 -488
  102. package/src/middleware/remote-auth.ts +0 -68
  103. package/src/middleware/timeout.ts +0 -15
  104. package/src/middleware-runner.test.ts +0 -418
  105. package/src/middleware-runner.ts +0 -234
  106. package/src/permissions.test.ts +0 -434
  107. package/src/permissions.ts +0 -319
  108. package/src/pikku-request.ts +0 -23
  109. package/src/pikku-response.ts +0 -5
  110. package/src/pikku-state.test.ts +0 -224
  111. package/src/pikku-state.ts +0 -214
  112. package/src/run-tests-script.test.ts +0 -49
  113. package/src/schema.test.ts +0 -249
  114. package/src/schema.ts +0 -151
  115. package/src/services/ai-agent-runner-service.ts +0 -41
  116. package/src/services/ai-run-state-service.ts +0 -20
  117. package/src/services/ai-storage-service.ts +0 -39
  118. package/src/services/content-service.ts +0 -76
  119. package/src/services/deployment-service.ts +0 -22
  120. package/src/services/gateway-service.ts +0 -20
  121. package/src/services/gopass-secrets.ts +0 -98
  122. package/src/services/in-memory-ai-run-state-service.ts +0 -73
  123. package/src/services/in-memory-trigger-service.ts +0 -64
  124. package/src/services/in-memory-workflow-service.test.ts +0 -351
  125. package/src/services/in-memory-workflow-service.ts +0 -395
  126. package/src/services/index.ts +0 -56
  127. package/src/services/jwt-service.ts +0 -30
  128. package/src/services/local-content.ts +0 -120
  129. package/src/services/local-gateway-service.ts +0 -62
  130. package/src/services/local-secrets.test.ts +0 -80
  131. package/src/services/local-secrets.ts +0 -94
  132. package/src/services/local-variables.test.ts +0 -61
  133. package/src/services/local-variables.ts +0 -39
  134. package/src/services/logger-console.test.ts +0 -118
  135. package/src/services/logger-console.ts +0 -98
  136. package/src/services/logger.ts +0 -57
  137. package/src/services/scheduler-service.ts +0 -86
  138. package/src/services/schema-service.ts +0 -33
  139. package/src/services/scoped-secret-service.test.ts +0 -74
  140. package/src/services/scoped-secret-service.ts +0 -41
  141. package/src/services/secret-service.ts +0 -38
  142. package/src/services/trigger-service.ts +0 -17
  143. package/src/services/typed-secret-service.test.ts +0 -93
  144. package/src/services/typed-secret-service.ts +0 -70
  145. package/src/services/typed-variables-service.test.ts +0 -73
  146. package/src/services/typed-variables-service.ts +0 -81
  147. package/src/services/user-session-service.test.ts +0 -113
  148. package/src/services/user-session-service.ts +0 -86
  149. package/src/services/variables-service.ts +0 -11
  150. package/src/services/workflow-service.ts +0 -84
  151. package/src/time-utils.test.ts +0 -56
  152. package/src/time-utils.ts +0 -107
  153. package/src/types/core.types.ts +0 -470
  154. package/src/types/state.types.ts +0 -186
  155. package/src/utils.test.ts +0 -350
  156. package/src/utils.ts +0 -129
  157. package/src/version.test.ts +0 -80
  158. package/src/version.ts +0 -25
  159. package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +0 -363
  160. package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +0 -238
  161. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +0 -152
  162. package/src/wirings/ai-agent/ai-agent-helpers.ts +0 -76
  163. package/src/wirings/ai-agent/ai-agent-memory.ts +0 -309
  164. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +0 -115
  165. package/src/wirings/ai-agent/ai-agent-model-config.ts +0 -43
  166. package/src/wirings/ai-agent/ai-agent-prepare.ts +0 -522
  167. package/src/wirings/ai-agent/ai-agent-registry.test.ts +0 -37
  168. package/src/wirings/ai-agent/ai-agent-registry.ts +0 -82
  169. package/src/wirings/ai-agent/ai-agent-runner.test.ts +0 -317
  170. package/src/wirings/ai-agent/ai-agent-runner.ts +0 -251
  171. package/src/wirings/ai-agent/ai-agent-stream.test.ts +0 -652
  172. package/src/wirings/ai-agent/ai-agent-stream.ts +0 -1060
  173. package/src/wirings/ai-agent/ai-agent.types.ts +0 -360
  174. package/src/wirings/ai-agent/index.ts +0 -41
  175. package/src/wirings/channel/channel-common.ts +0 -90
  176. package/src/wirings/channel/channel-handler.ts +0 -241
  177. package/src/wirings/channel/channel-middleware-runner.ts +0 -120
  178. package/src/wirings/channel/channel-runner.ts +0 -166
  179. package/src/wirings/channel/channel-store.ts +0 -29
  180. package/src/wirings/channel/channel.types.ts +0 -178
  181. package/src/wirings/channel/define-channel-routes.ts +0 -25
  182. package/src/wirings/channel/eventhub-service.ts +0 -34
  183. package/src/wirings/channel/eventhub-store.ts +0 -13
  184. package/src/wirings/channel/index.ts +0 -24
  185. package/src/wirings/channel/local/index.ts +0 -3
  186. package/src/wirings/channel/local/local-channel-handler.ts +0 -79
  187. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -215
  188. package/src/wirings/channel/local/local-channel-runner.ts +0 -200
  189. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -95
  190. package/src/wirings/channel/local/local-eventhub-service.ts +0 -101
  191. package/src/wirings/channel/log-channels.ts +0 -20
  192. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -54
  193. package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -45
  194. package/src/wirings/channel/serverless/index.ts +0 -5
  195. package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -286
  196. package/src/wirings/cli/channel/cli-channel-runner.ts +0 -136
  197. package/src/wirings/cli/channel/index.ts +0 -1
  198. package/src/wirings/cli/cli-runner.test.ts +0 -403
  199. package/src/wirings/cli/cli-runner.ts +0 -519
  200. package/src/wirings/cli/cli.types.ts +0 -357
  201. package/src/wirings/cli/command-parser.test.ts +0 -445
  202. package/src/wirings/cli/command-parser.ts +0 -504
  203. package/src/wirings/cli/define-cli-commands.ts +0 -24
  204. package/src/wirings/cli/index.ts +0 -19
  205. package/src/wirings/gateway/gateway-runner.test.ts +0 -474
  206. package/src/wirings/gateway/gateway-runner.ts +0 -411
  207. package/src/wirings/gateway/gateway.types.ts +0 -149
  208. package/src/wirings/gateway/index.ts +0 -13
  209. package/src/wirings/http/http-routes.test.ts +0 -322
  210. package/src/wirings/http/http-routes.ts +0 -198
  211. package/src/wirings/http/http-runner.test.ts +0 -144
  212. package/src/wirings/http/http-runner.ts +0 -563
  213. package/src/wirings/http/http.types.ts +0 -378
  214. package/src/wirings/http/index.ts +0 -25
  215. package/src/wirings/http/log-http-routes.ts +0 -22
  216. package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -237
  217. package/src/wirings/http/pikku-fetch-http-request.ts +0 -166
  218. package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -82
  219. package/src/wirings/http/pikku-fetch-http-response.ts +0 -147
  220. package/src/wirings/http/routers/http-router.ts +0 -13
  221. package/src/wirings/http/routers/path-to-regex.test.ts +0 -319
  222. package/src/wirings/http/routers/path-to-regex.ts +0 -126
  223. package/src/wirings/mcp/index.ts +0 -27
  224. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -389
  225. package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
  226. package/src/wirings/mcp/mcp-runner.ts +0 -305
  227. package/src/wirings/mcp/mcp.types.ts +0 -216
  228. package/src/wirings/node/index.ts +0 -2
  229. package/src/wirings/node/node.types.ts +0 -23
  230. package/src/wirings/oauth2/index.ts +0 -3
  231. package/src/wirings/oauth2/oauth2-client.test.ts +0 -929
  232. package/src/wirings/oauth2/oauth2-client.ts +0 -335
  233. package/src/wirings/oauth2/oauth2.types.ts +0 -69
  234. package/src/wirings/oauth2/wire-oauth2-credential.ts +0 -23
  235. package/src/wirings/queue/index.ts +0 -31
  236. package/src/wirings/queue/queue-runner.test.ts +0 -710
  237. package/src/wirings/queue/queue-runner.ts +0 -192
  238. package/src/wirings/queue/queue.types.ts +0 -189
  239. package/src/wirings/queue/register-queue-helper.ts +0 -60
  240. package/src/wirings/queue/validate-worker-config.test.ts +0 -108
  241. package/src/wirings/queue/validate-worker-config.ts +0 -116
  242. package/src/wirings/rpc/index.ts +0 -4
  243. package/src/wirings/rpc/rpc-runner.ts +0 -439
  244. package/src/wirings/rpc/rpc-types.ts +0 -51
  245. package/src/wirings/rpc/wire-addon.ts +0 -20
  246. package/src/wirings/scheduler/index.ts +0 -11
  247. package/src/wirings/scheduler/log-schedulers.ts +0 -20
  248. package/src/wirings/scheduler/scheduler-runner.test.ts +0 -660
  249. package/src/wirings/scheduler/scheduler-runner.ts +0 -132
  250. package/src/wirings/scheduler/scheduler.types.ts +0 -53
  251. package/src/wirings/secret/index.ts +0 -9
  252. package/src/wirings/secret/secret.types.ts +0 -32
  253. package/src/wirings/secret/validate-secret-definitions.test.ts +0 -140
  254. package/src/wirings/secret/validate-secret-definitions.ts +0 -82
  255. package/src/wirings/trigger/index.ts +0 -10
  256. package/src/wirings/trigger/pikku-trigger-service.ts +0 -112
  257. package/src/wirings/trigger/trigger-runner.test.ts +0 -79
  258. package/src/wirings/trigger/trigger-runner.ts +0 -135
  259. package/src/wirings/trigger/trigger.types.ts +0 -178
  260. package/src/wirings/variable/index.ts +0 -8
  261. package/src/wirings/variable/validate-variable-definitions.test.ts +0 -91
  262. package/src/wirings/variable/validate-variable-definitions.ts +0 -69
  263. package/src/wirings/variable/variable.types.ts +0 -22
  264. package/src/wirings/workflow/dsl/index.ts +0 -31
  265. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -320
  266. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -28
  267. package/src/wirings/workflow/graph/graph-node.ts +0 -222
  268. package/src/wirings/workflow/graph/graph-runner.test.ts +0 -487
  269. package/src/wirings/workflow/graph/graph-runner.ts +0 -816
  270. package/src/wirings/workflow/graph/index.ts +0 -14
  271. package/src/wirings/workflow/graph/template.test.ts +0 -49
  272. package/src/wirings/workflow/graph/template.ts +0 -42
  273. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -29
  274. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -104
  275. package/src/wirings/workflow/index.ts +0 -81
  276. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -200
  277. package/src/wirings/workflow/pikku-workflow-service.ts +0 -1165
  278. package/src/wirings/workflow/workflow-helpers.test.ts +0 -129
  279. package/src/wirings/workflow/workflow-helpers.ts +0 -79
  280. package/src/wirings/workflow/workflow.types.ts +0 -266
  281. package/tsconfig.json +0 -14
  282. package/tsconfig.tsbuildinfo +0 -1
@@ -1,1060 +0,0 @@
1
- import type {
2
- AIStreamChannel,
3
- AIStreamEvent,
4
- AIMessage,
5
- AIToolCall,
6
- AIToolResult,
7
- PikkuAIMiddlewareHooks,
8
- AgentRunState,
9
- CoreAIAgent,
10
- AIAgentMemoryConfig,
11
- } from './ai-agent.types.js'
12
- import { pikkuState, getSingletonServices } from '../../pikku-state.js'
13
- import {
14
- combineChannelMiddleware,
15
- wrapChannelWithMiddleware,
16
- } from '../channel/channel-middleware-runner.js'
17
- import { randomUUID } from 'crypto'
18
- import type { AIStorageService } from '../../services/ai-storage-service.js'
19
- import type {
20
- AIAgentRunnerParams,
21
- AIAgentStepResult,
22
- } from '../../services/ai-agent-runner-service.js'
23
-
24
- import {
25
- parseWorkingMemory,
26
- deepMergeWorkingMemory,
27
- resolveMemoryServices,
28
- loadContextMessages,
29
- trimMessages,
30
- } from './ai-agent-memory.js'
31
- import {
32
- prepareAgentRun,
33
- resolveAgent,
34
- buildInstructions,
35
- buildToolDefs,
36
- createScopedChannel,
37
- ToolApprovalRequired,
38
- type RunAIAgentParams,
39
- type StreamAIAgentOptions,
40
- type StreamContext,
41
- } from './ai-agent-prepare.js'
42
- import {
43
- createAssistantUIChannel,
44
- parseAssistantUIInput,
45
- } from './ai-agent-assistant-ui.js'
46
- import { resolveModelConfig } from './ai-agent-model-config.js'
47
- import type { AIRunStateService } from '../../services/ai-run-state-service.js'
48
- import type { AIAgentRunnerService } from '../../services/ai-agent-runner-service.js'
49
-
50
- type PersistingChannel = AIStreamChannel & {
51
- fullText: string
52
- flush: () => Promise<void>
53
- totalUsage: { inputTokens: number; outputTokens: number; model?: string }
54
- }
55
-
56
- function createPersistingChannel(
57
- parent: AIStreamChannel,
58
- storage: AIStorageService | undefined,
59
- threadId: string
60
- ): PersistingChannel {
61
- let fullText = ''
62
- let stepText = ''
63
- let stepToolCalls: AIToolCall[] = []
64
- let stepToolResults: AIToolResult[] = []
65
- const totalUsage: {
66
- inputTokens: number
67
- outputTokens: number
68
- model?: string
69
- } = {
70
- inputTokens: 0,
71
- outputTokens: 0,
72
- }
73
-
74
- const flushStep = async () => {
75
- if (!storage) return
76
- const text = stepText
77
- const calls = stepToolCalls
78
- const results = stepToolResults
79
- stepText = ''
80
- stepToolCalls = []
81
- stepToolResults = []
82
- if (text || calls.length > 0) {
83
- const assistantMsg: AIMessage = {
84
- id: randomUUID(),
85
- role: 'assistant',
86
- content: text || undefined,
87
- toolCalls: calls.length > 0 ? calls : undefined,
88
- createdAt: new Date(),
89
- }
90
- const messages: AIMessage[] = [assistantMsg]
91
- if (results.length > 0) {
92
- messages.push({
93
- id: randomUUID(),
94
- role: 'tool',
95
- toolResults: results,
96
- createdAt: new Date(),
97
- })
98
- }
99
- await storage.saveMessages(threadId, messages)
100
- }
101
- }
102
-
103
- const channel: PersistingChannel = {
104
- channelId: parent.channelId,
105
- openingData: parent.openingData,
106
- get state() {
107
- return parent.state
108
- },
109
- get fullText() {
110
- return fullText
111
- },
112
- get totalUsage() {
113
- return totalUsage
114
- },
115
- flush: flushStep,
116
- close: () => parent.close(),
117
- sendBinary: (data) => parent.sendBinary(data),
118
- send: (event: AIStreamEvent) => {
119
- if (storage) {
120
- switch (event.type) {
121
- case 'text-delta':
122
- stepText += event.text
123
- fullText += event.text
124
- break
125
- case 'tool-call':
126
- stepToolCalls.push({
127
- id: event.toolCallId,
128
- name: event.toolName,
129
- args: event.args as Record<string, unknown>,
130
- })
131
- break
132
- case 'tool-result':
133
- stepToolResults.push({
134
- id: event.toolCallId,
135
- name: event.toolName,
136
- result:
137
- typeof event.result === 'string'
138
- ? event.result
139
- : JSON.stringify(event.result),
140
- })
141
- break
142
- case 'usage':
143
- totalUsage.inputTokens += event.tokens.input
144
- totalUsage.outputTokens += event.tokens.output
145
- if (event.model) totalUsage.model = event.model
146
- flushStep()
147
- break
148
- case 'done':
149
- flushStep()
150
- break
151
- }
152
- }
153
- parent.send(event)
154
- },
155
- }
156
- return channel
157
- }
158
-
159
- async function postStreamCleanup(
160
- persistingChannel: PersistingChannel,
161
- aiMiddlewares: PikkuAIMiddlewareHooks[],
162
- singletonServices: any,
163
- storage: AIStorageService | undefined,
164
- memoryConfig: any,
165
- threadId: string,
166
- workingMemorySchemaName: string | null,
167
- messages: AIMessage[],
168
- aiRunState: AIRunStateService,
169
- runId: string
170
- ): Promise<void> {
171
- const usage = persistingChannel.totalUsage
172
- let outputText = persistingChannel.fullText
173
- let outputMessages = messages
174
- for (let i = aiMiddlewares.length - 1; i >= 0; i--) {
175
- const mw = aiMiddlewares[i]
176
- if (mw.modifyOutput) {
177
- const result = await mw.modifyOutput(singletonServices, {
178
- text: outputText,
179
- messages: outputMessages,
180
- usage: {
181
- inputTokens: usage.inputTokens,
182
- outputTokens: usage.outputTokens,
183
- },
184
- })
185
- outputText = result.text
186
- outputMessages = result.messages
187
- }
188
- }
189
-
190
- if (storage && memoryConfig?.workingMemory && outputText) {
191
- const parsed = parseWorkingMemory(outputText)
192
- if (parsed) {
193
- const existing =
194
- (await storage.getWorkingMemory(threadId, 'thread')) ?? {}
195
- const merged = deepMergeWorkingMemory(existing, parsed)
196
-
197
- if (singletonServices.schema && workingMemorySchemaName) {
198
- try {
199
- await singletonServices.schema.validateSchema(
200
- workingMemorySchemaName,
201
- merged
202
- )
203
- } catch (err) {
204
- singletonServices.logger.warn(
205
- `Working memory validation failed: ${err instanceof Error ? err.message : String(err)}`
206
- )
207
- }
208
- }
209
-
210
- await storage.saveWorkingMemory(threadId, 'thread', merged)
211
- }
212
- }
213
-
214
- await aiRunState.updateRun(runId, {
215
- status: 'completed',
216
- ...(usage.model
217
- ? {
218
- usage: {
219
- inputTokens: usage.inputTokens,
220
- outputTokens: usage.outputTokens,
221
- model: usage.model,
222
- },
223
- }
224
- : {}),
225
- })
226
- }
227
-
228
- type StepLoopParams = {
229
- agent: CoreAIAgent
230
- runnerParams: AIAgentRunnerParams
231
- maxSteps: number
232
- agentRunner: AIAgentRunnerService
233
- persistingChannel: PersistingChannel
234
- channel: AIStreamChannel
235
- runId: string
236
- aiMiddlewares: PikkuAIMiddlewareHooks[]
237
- }
238
-
239
- type StepLoopResult =
240
- | { outcome: 'done' }
241
- | { outcome: 'approval'; approval: ToolApprovalRequired }
242
-
243
- async function runStreamStepLoop(
244
- params: StepLoopParams
245
- ): Promise<StepLoopResult> {
246
- const {
247
- agent,
248
- runnerParams,
249
- maxSteps,
250
- agentRunner,
251
- persistingChannel,
252
- channel,
253
- runId,
254
- aiMiddlewares,
255
- } = params
256
-
257
- const singletonServices = getSingletonServices()
258
-
259
- for (let step = 0; step < maxSteps; step++) {
260
- if (agent.prepareStep) {
261
- let stopped = false
262
- await agent.prepareStep({
263
- stepNumber: step,
264
- messages: runnerParams.messages,
265
- tools: runnerParams.tools,
266
- toolChoice: runnerParams.toolChoice,
267
- model: runnerParams.model,
268
- stop: () => {
269
- stopped = true
270
- },
271
- })
272
- if (stopped) break
273
- }
274
-
275
- channel.send({ type: 'step-start', stepNumber: step })
276
-
277
- const stepResult = await agentRunner.stream(runnerParams, persistingChannel)
278
-
279
- for (const mw of aiMiddlewares) {
280
- if (mw.afterStep) {
281
- await mw.afterStep(singletonServices, {
282
- stepNumber: step,
283
- text: stepResult.text,
284
- toolCalls: stepResult.toolCalls,
285
- toolResults: stepResult.toolResults,
286
- usage: stepResult.usage,
287
- finishReason: stepResult.finishReason,
288
- })
289
- }
290
- }
291
-
292
- if (stepResult.toolCalls.length === 0) break
293
-
294
- const approvalNeeded = checkForApproval(
295
- stepResult,
296
- runnerParams.tools,
297
- runId
298
- )
299
- if (approvalNeeded) {
300
- return { outcome: 'approval', approval: approvalNeeded }
301
- }
302
-
303
- appendStepMessages(runnerParams, stepResult)
304
- }
305
-
306
- return { outcome: 'done' }
307
- }
308
-
309
- function checkForApproval(
310
- stepResult: AIAgentStepResult,
311
- tools: AIAgentRunnerParams['tools'],
312
- runId: string
313
- ): ToolApprovalRequired | null {
314
- for (const tc of stepResult.toolCalls) {
315
- const toolDef = tools.find((t) => t.name === tc.toolName)
316
-
317
- if (toolDef?.needsApproval) {
318
- return new ToolApprovalRequired(tc.toolCallId, tc.toolName, tc.args)
319
- }
320
-
321
- const tr = stepResult.toolResults.find(
322
- (r) => r.toolCallId === tc.toolCallId
323
- )
324
- if (
325
- tr?.result &&
326
- typeof tr.result === 'object' &&
327
- '__approvalRequired' in (tr.result as object)
328
- ) {
329
- const r = tr.result as {
330
- toolName: string
331
- args: unknown
332
- reason?: string
333
- displayToolName?: string
334
- displayArgs?: unknown
335
- agentRunId?: string
336
- }
337
- return new ToolApprovalRequired(
338
- tc.toolCallId,
339
- r.toolName,
340
- r.args,
341
- r.reason,
342
- r.displayToolName,
343
- r.displayArgs,
344
- r.agentRunId
345
- )
346
- }
347
- }
348
- return null
349
- }
350
-
351
- function appendStepMessages(
352
- runnerParams: AIAgentRunnerParams,
353
- stepResult: AIAgentStepResult
354
- ): void {
355
- const assistantMsg: AIMessage = {
356
- id: randomUUID(),
357
- role: 'assistant',
358
- content: stepResult.text || undefined,
359
- toolCalls:
360
- stepResult.toolCalls.length > 0
361
- ? stepResult.toolCalls.map((tc) => ({
362
- id: tc.toolCallId,
363
- name: tc.toolName,
364
- args: tc.args as Record<string, unknown>,
365
- }))
366
- : undefined,
367
- createdAt: new Date(),
368
- }
369
- runnerParams.messages.push(assistantMsg)
370
-
371
- if (stepResult.toolResults.length > 0) {
372
- const toolMsg: AIMessage = {
373
- id: randomUUID(),
374
- role: 'tool',
375
- toolResults: stepResult.toolResults.map((tr) => ({
376
- id: tr.toolCallId,
377
- name: tr.toolName,
378
- result:
379
- typeof tr.result === 'string' ? tr.result : JSON.stringify(tr.result),
380
- })),
381
- createdAt: new Date(),
382
- }
383
- runnerParams.messages.push(toolMsg)
384
- }
385
- }
386
-
387
- function handleApproval(
388
- err: ToolApprovalRequired,
389
- runId: string,
390
- channel: AIStreamChannel,
391
- aiRunState: AIRunStateService,
392
- persistingChannel: PersistingChannel
393
- ): Promise<void> {
394
- return (async () => {
395
- await persistingChannel.flush()
396
-
397
- const pendingApproval = err.agentRunId
398
- ? {
399
- type: 'agent-call' as const,
400
- toolCallId: err.toolCallId,
401
- agentName: err.toolName,
402
- agentRunId: err.agentRunId,
403
- displayToolName: err.displayToolName ?? err.toolName,
404
- displayArgs: err.displayArgs ?? err.args,
405
- }
406
- : {
407
- type: 'tool-call' as const,
408
- toolCallId: err.toolCallId,
409
- toolName: err.toolName,
410
- args: err.args,
411
- }
412
-
413
- await aiRunState.updateRun(runId, {
414
- status: 'suspended',
415
- suspendReason: 'approval',
416
- pendingApprovals: [pendingApproval],
417
- })
418
-
419
- const approvalEvent = {
420
- type: 'approval-request' as const,
421
- toolCallId: err.toolCallId,
422
- toolName: err.displayToolName ?? err.toolName,
423
- args: err.displayArgs ?? err.args,
424
- reason: err.reason,
425
- runId,
426
- }
427
- channel.send(approvalEvent as any)
428
- channel.send({ type: 'done' })
429
- channel.close()
430
- })()
431
- }
432
-
433
- export async function streamAIAgent(
434
- agentName: string,
435
- input:
436
- | { message: string; threadId: string; resourceId: string }
437
- | Record<string, unknown>,
438
- channel: AIStreamChannel,
439
- params: RunAIAgentParams,
440
- agentSessionMap?: Map<string, string>,
441
- options?: StreamAIAgentOptions
442
- ): Promise<void> {
443
- const sessionMap = agentSessionMap ?? new Map<string, string>()
444
-
445
- const { agent: resolvedAgentForProtocol } = resolveAgent(agentName)
446
- const inputRecord = input as Record<string, unknown>
447
- const useUIMessageStream =
448
- resolvedAgentForProtocol.protocol === 'ui-message-stream' ||
449
- Array.isArray(inputRecord.messages)
450
- let normalizedInput: { message: string; threadId: string; resourceId: string }
451
- if (useUIMessageStream) {
452
- normalizedInput = parseAssistantUIInput(inputRecord)
453
- channel = createAssistantUIChannel(channel)
454
- } else {
455
- normalizedInput = input as {
456
- message: string
457
- threadId: string
458
- resourceId: string
459
- }
460
- }
461
-
462
- const streamContext: StreamContext = { channel, options }
463
-
464
- const {
465
- agent,
466
- packageName,
467
- resolvedName,
468
- agentRunner,
469
- storage,
470
- memoryConfig,
471
- threadId,
472
- userMessage,
473
- runnerParams,
474
- maxSteps,
475
- missingRpcs,
476
- workingMemorySchemaName,
477
- } = await prepareAgentRun(
478
- agentName,
479
- normalizedInput,
480
- params,
481
- sessionMap,
482
- streamContext
483
- )
484
-
485
- const singletonServices = getSingletonServices()
486
- const { aiRunState } = singletonServices
487
- if (!aiRunState) {
488
- throw new Error('AIRunStateService not available in singletonServices')
489
- }
490
-
491
- if (missingRpcs.length > 0) {
492
- await aiRunState.createRun({
493
- agentName,
494
- threadId,
495
- resourceId: normalizedInput.resourceId,
496
- status: 'suspended',
497
- suspendReason: 'rpc-missing',
498
- missingRpcs,
499
- usage: { inputTokens: 0, outputTokens: 0, model: agent.model },
500
- createdAt: new Date(),
501
- updatedAt: new Date(),
502
- })
503
- channel.send({ type: 'suspended', reason: 'rpc-missing', missingRpcs })
504
- channel.send({ type: 'done' })
505
- return
506
- }
507
-
508
- const aiMiddlewares: PikkuAIMiddlewareHooks[] = agent.aiMiddleware ?? []
509
-
510
- let modifiedMessages = runnerParams.messages
511
- let modifiedInstructions = runnerParams.instructions
512
- for (const mw of aiMiddlewares) {
513
- if (mw.modifyInput) {
514
- const result = await mw.modifyInput(singletonServices, {
515
- messages: modifiedMessages,
516
- instructions: modifiedInstructions,
517
- })
518
- modifiedMessages = result.messages
519
- modifiedInstructions = result.instructions
520
- }
521
- }
522
- runnerParams.messages = modifiedMessages
523
- runnerParams.instructions = modifiedInstructions
524
-
525
- const runId = await aiRunState.createRun({
526
- agentName,
527
- threadId,
528
- resourceId: normalizedInput.resourceId,
529
- status: 'running',
530
- usage: { inputTokens: 0, outputTokens: 0, model: agent.model },
531
- createdAt: new Date(),
532
- updatedAt: new Date(),
533
- })
534
-
535
- if (storage) {
536
- await storage.saveMessages(threadId, [userMessage])
537
- }
538
-
539
- const streamMiddleware = aiMiddlewares
540
- .filter((mw) => mw.modifyOutputStream)
541
- .map((mw) => {
542
- const state: Record<string, unknown> = {}
543
- const allEvents: AIStreamEvent[] = []
544
- return async (services: any, event: any, next: any) => {
545
- allEvents.push(event)
546
- const result = await mw.modifyOutputStream!(services, {
547
- event,
548
- allEvents,
549
- state,
550
- })
551
- if (result == null) return
552
- if (Array.isArray(result)) {
553
- for (const r of result) await next(r)
554
- } else {
555
- await next(result)
556
- }
557
- }
558
- })
559
-
560
- const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta')
561
- const meta = agentsMeta[resolvedName]
562
- const allChannelMiddleware = combineChannelMiddleware(
563
- 'agent',
564
- `stream:${agentName}`,
565
- {
566
- wireInheritedChannelMiddleware: meta?.channelMiddleware,
567
- wireChannelMiddleware: [
568
- ...((agent.channelMiddleware as any[]) ?? []),
569
- ...streamMiddleware,
570
- ],
571
- }
572
- )
573
-
574
- const wrappedChannel =
575
- allChannelMiddleware.length > 0
576
- ? (wrapChannelWithMiddleware(
577
- { channel },
578
- singletonServices,
579
- allChannelMiddleware
580
- ).channel as AIStreamChannel)
581
- : channel
582
-
583
- const persistingChannel = createPersistingChannel(
584
- wrappedChannel,
585
- storage,
586
- threadId
587
- )
588
-
589
- try {
590
- const loopResult = await runStreamStepLoop({
591
- agent,
592
- runnerParams,
593
- maxSteps,
594
- agentRunner,
595
- persistingChannel,
596
- channel,
597
- runId,
598
- aiMiddlewares,
599
- })
600
-
601
- if (loopResult.outcome === 'approval') {
602
- await handleApproval(
603
- loopResult.approval,
604
- runId,
605
- channel,
606
- aiRunState,
607
- persistingChannel
608
- )
609
- return
610
- }
611
-
612
- await postStreamCleanup(
613
- persistingChannel,
614
- aiMiddlewares,
615
- singletonServices,
616
- storage,
617
- memoryConfig,
618
- threadId,
619
- workingMemorySchemaName,
620
- runnerParams.messages,
621
- aiRunState,
622
- runId
623
- )
624
-
625
- channel.send({ type: 'done' })
626
- channel.close()
627
- } catch (err) {
628
- for (const mw of aiMiddlewares) {
629
- if (mw.onError) {
630
- try {
631
- await mw.onError(singletonServices, {
632
- error: err instanceof Error ? err : new Error(String(err)),
633
- stepNumber: -1,
634
- messages: runnerParams.messages,
635
- })
636
- } catch {
637
- // onError hooks must not affect error flow
638
- }
639
- }
640
- }
641
- await aiRunState.updateRun(runId, { status: 'failed' })
642
- channel.send({
643
- type: 'error',
644
- message: err instanceof Error ? err.message : String(err),
645
- })
646
- channel.send({ type: 'done' })
647
- channel.close()
648
- }
649
- }
650
-
651
- export async function resumeAIAgent(
652
- input: {
653
- runId: string
654
- toolCallId: string
655
- approved: boolean
656
- },
657
- channel: AIStreamChannel,
658
- params: RunAIAgentParams,
659
- options?: StreamAIAgentOptions
660
- ): Promise<void> {
661
- const singletonServices = getSingletonServices()
662
- const { aiRunState } = singletonServices
663
- if (!aiRunState) {
664
- throw new Error('AIRunStateService not available in singletonServices')
665
- }
666
-
667
- const run = await aiRunState.getRun(input.runId)
668
- if (!run) {
669
- throw new Error(`No run found for runId ${input.runId}`)
670
- }
671
-
672
- const pending = run.pendingApprovals?.find(
673
- (p) => p.toolCallId === input.toolCallId
674
- )
675
- if (!pending) {
676
- throw new Error(
677
- `No pending approval for toolCallId ${input.toolCallId} on run ${input.runId}`
678
- )
679
- }
680
-
681
- await aiRunState.resolveApproval(
682
- input.toolCallId,
683
- input.approved ? 'approved' : 'denied'
684
- )
685
-
686
- const { agent, packageName, resolvedName } = resolveAgent(run.agentName)
687
- const { storage } = resolveMemoryServices(agent, singletonServices)
688
- const memoryConfig = agent.memory
689
- const agentRunner = singletonServices.aiAgentRunner
690
- if (!agentRunner) {
691
- throw new Error('AIAgentRunnerService not available in singletonServices')
692
- }
693
-
694
- if (!input.approved) {
695
- if (pending.type === 'agent-call') {
696
- await aiRunState.updateRun(pending.agentRunId, { status: 'failed' })
697
- }
698
-
699
- const denialResult = 'Tool call was not approved by the user'
700
-
701
- if (storage) {
702
- await storage.saveMessages(run.threadId, [
703
- {
704
- id: randomUUID(),
705
- role: 'tool',
706
- toolResults: [
707
- {
708
- id: input.toolCallId,
709
- name:
710
- pending.type === 'tool-call'
711
- ? pending.toolName
712
- : pending.agentName,
713
- result: denialResult,
714
- },
715
- ],
716
- createdAt: new Date(),
717
- },
718
- ])
719
- }
720
-
721
- await aiRunState.updateRun(run.runId, { status: 'running' })
722
-
723
- await continueAfterToolResult(
724
- run,
725
- agent,
726
- packageName,
727
- resolvedName,
728
- storage,
729
- memoryConfig,
730
- agentRunner,
731
- channel,
732
- params,
733
- aiRunState,
734
- options
735
- )
736
- return
737
- }
738
-
739
- if (pending.type === 'agent-call') {
740
- const subRun = await aiRunState.getRun(pending.agentRunId)
741
- if (!subRun) {
742
- throw new Error(`Sub-agent run not found: ${pending.agentRunId}`)
743
- }
744
- const subPending = subRun.pendingApprovals?.[0]
745
- if (!subPending) {
746
- throw new Error(
747
- `No pending approval on sub-agent run ${pending.agentRunId}`
748
- )
749
- }
750
-
751
- const subChannel = createScopedChannel(channel, subRun.agentName, 'resume')
752
- channel.send({
753
- type: 'agent-call',
754
- agentName: subRun.agentName,
755
- session: 'resume',
756
- input: null,
757
- })
758
-
759
- await resumeAIAgent(
760
- {
761
- runId: pending.agentRunId,
762
- toolCallId: subPending.toolCallId,
763
- approved: true,
764
- },
765
- subChannel,
766
- params,
767
- options
768
- )
769
-
770
- channel.send({
771
- type: 'agent-result',
772
- agentName: subRun.agentName,
773
- session: 'resume',
774
- result: null,
775
- })
776
-
777
- if (storage) {
778
- await storage.saveMessages(run.threadId, [
779
- {
780
- id: randomUUID(),
781
- role: 'tool',
782
- toolResults: [
783
- {
784
- id: input.toolCallId,
785
- name: pending.agentName,
786
- result: 'Sub-agent completed successfully',
787
- },
788
- ],
789
- createdAt: new Date(),
790
- },
791
- ])
792
- }
793
- } else {
794
- const streamContext: StreamContext = {
795
- channel,
796
- options: { ...options, requiresToolApproval: false },
797
- }
798
- const aiMiddlewaresForResume: PikkuAIMiddlewareHooks[] =
799
- agent.aiMiddleware ?? []
800
- const { tools } = buildToolDefs(
801
- params,
802
- new Map<string, string>(),
803
- run.resourceId,
804
- resolvedName,
805
- packageName,
806
- streamContext,
807
- aiMiddlewaresForResume
808
- )
809
-
810
- const matchingTool = tools.find((t) => t.name === pending.toolName)
811
- if (!matchingTool) {
812
- throw new Error(
813
- `Tool "${pending.toolName}" not found in agent definition`
814
- )
815
- }
816
-
817
- const toolArgs =
818
- typeof pending.args === 'string' ? JSON.parse(pending.args) : pending.args
819
-
820
- let toolResult: unknown
821
- try {
822
- toolResult = await matchingTool.execute(toolArgs)
823
- } catch (execErr) {
824
- toolResult = `Error: ${execErr instanceof Error ? execErr.message : String(execErr)}`
825
- }
826
-
827
- const resultStr =
828
- typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult)
829
- if (storage) {
830
- await storage.saveMessages(run.threadId, [
831
- {
832
- id: randomUUID(),
833
- role: 'tool',
834
- toolResults: [
835
- {
836
- id: input.toolCallId,
837
- name: pending.toolName,
838
- result: resultStr,
839
- },
840
- ],
841
- createdAt: new Date(),
842
- },
843
- ])
844
- }
845
-
846
- channel.send({
847
- type: 'tool-result',
848
- toolCallId: input.toolCallId,
849
- toolName: pending.toolName,
850
- result: toolResult,
851
- })
852
- }
853
-
854
- await aiRunState.updateRun(run.runId, { status: 'running' })
855
-
856
- await continueAfterToolResult(
857
- run,
858
- agent,
859
- packageName,
860
- resolvedName,
861
- storage,
862
- memoryConfig,
863
- agentRunner,
864
- channel,
865
- params,
866
- aiRunState,
867
- options
868
- )
869
- }
870
-
871
- async function continueAfterToolResult(
872
- run: AgentRunState,
873
- agent: CoreAIAgent,
874
- packageName: string | null,
875
- resolvedName: string,
876
- storage: AIStorageService | undefined,
877
- memoryConfig: AIAgentMemoryConfig | undefined,
878
- agentRunner: AIAgentRunnerService,
879
- channel: AIStreamChannel,
880
- params: RunAIAgentParams,
881
- aiRunState: AIRunStateService,
882
- options?: StreamAIAgentOptions
883
- ): Promise<void> {
884
- const singletonServices = getSingletonServices()
885
- const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta')
886
- const meta = agentsMeta[resolvedName]
887
- const workingMemorySchemaName = meta?.workingMemorySchema ?? null
888
-
889
- const messages = storage
890
- ? await storage.getMessages(run.threadId, {
891
- lastN: memoryConfig?.lastMessages ?? 20,
892
- })
893
- : []
894
-
895
- const workingMemoryJsonSchema = workingMemorySchemaName
896
- ? pikkuState(packageName, 'misc', 'schemas').get(workingMemorySchemaName)
897
- : undefined
898
-
899
- const contextMessages = await loadContextMessages(
900
- memoryConfig,
901
- storage,
902
- { message: '', threadId: run.threadId, resourceId: run.resourceId },
903
- workingMemoryJsonSchema
904
- )
905
-
906
- const allMessages = [...contextMessages, ...messages]
907
- const trimmedMessages = trimMessages(allMessages)
908
-
909
- const instructions = buildInstructions(resolvedName, packageName)
910
-
911
- const aiMiddlewares: PikkuAIMiddlewareHooks[] = agent.aiMiddleware ?? []
912
- let modifiedMessages = trimmedMessages
913
- let modifiedInstructions = instructions
914
- for (const mw of aiMiddlewares) {
915
- if (mw.modifyInput) {
916
- const result = await mw.modifyInput(singletonServices, {
917
- messages: modifiedMessages,
918
- instructions: modifiedInstructions,
919
- })
920
- modifiedMessages = result.messages
921
- modifiedInstructions = result.instructions
922
- }
923
- }
924
-
925
- const streamMiddleware = aiMiddlewares
926
- .filter((mw) => mw.modifyOutputStream)
927
- .map((mw) => {
928
- const state: Record<string, unknown> = {}
929
- const allEvents: AIStreamEvent[] = []
930
- return async (services: any, event: any, next: any) => {
931
- allEvents.push(event)
932
- const result = await mw.modifyOutputStream!(services, {
933
- event,
934
- allEvents,
935
- state,
936
- })
937
- if (result == null) return
938
- if (Array.isArray(result)) {
939
- for (const r of result) await next(r)
940
- } else {
941
- await next(result)
942
- }
943
- }
944
- })
945
-
946
- const allChannelMiddleware = combineChannelMiddleware(
947
- 'agent',
948
- `stream:${run.agentName}`,
949
- {
950
- wireInheritedChannelMiddleware: meta?.channelMiddleware,
951
- wireChannelMiddleware: [
952
- ...((agent.channelMiddleware as any[]) ?? []),
953
- ...streamMiddleware,
954
- ],
955
- }
956
- )
957
-
958
- const wrappedChannel =
959
- allChannelMiddleware.length > 0
960
- ? (wrapChannelWithMiddleware(
961
- { channel },
962
- singletonServices,
963
- allChannelMiddleware
964
- ).channel as AIStreamChannel)
965
- : channel
966
-
967
- const persistingChannel = createPersistingChannel(
968
- wrappedChannel,
969
- storage,
970
- run.threadId
971
- )
972
-
973
- const streamContext: StreamContext = { channel, options }
974
- const resumeTools = buildToolDefs(
975
- params,
976
- new Map<string, string>(),
977
- run.resourceId,
978
- resolvedName,
979
- packageName,
980
- streamContext,
981
- aiMiddlewares
982
- ).tools
983
-
984
- const resolved = resolveModelConfig(resolvedName, agent)
985
- const maxSteps = resolved.maxSteps ?? 10
986
-
987
- const runnerParams: AIAgentRunnerParams = {
988
- model: resolved.model,
989
- temperature: resolved.temperature,
990
- instructions: modifiedInstructions,
991
- messages: modifiedMessages,
992
- tools: resumeTools,
993
- maxSteps: 1,
994
- toolChoice: (agent.toolChoice ?? 'auto') as 'auto' | 'required' | 'none',
995
- outputSchema: meta?.outputSchema
996
- ? pikkuState(packageName, 'misc', 'schemas').get(meta.outputSchema)
997
- : undefined,
998
- }
999
-
1000
- try {
1001
- const loopResult = await runStreamStepLoop({
1002
- agent,
1003
- runnerParams,
1004
- maxSteps,
1005
- agentRunner,
1006
- persistingChannel,
1007
- channel,
1008
- runId: run.runId,
1009
- aiMiddlewares,
1010
- })
1011
-
1012
- if (loopResult.outcome === 'approval') {
1013
- await handleApproval(
1014
- loopResult.approval,
1015
- run.runId,
1016
- channel,
1017
- aiRunState,
1018
- persistingChannel
1019
- )
1020
- return
1021
- }
1022
-
1023
- await postStreamCleanup(
1024
- persistingChannel,
1025
- aiMiddlewares,
1026
- singletonServices,
1027
- storage,
1028
- memoryConfig,
1029
- run.threadId,
1030
- workingMemorySchemaName,
1031
- runnerParams.messages,
1032
- aiRunState,
1033
- run.runId
1034
- )
1035
-
1036
- channel.send({ type: 'done' })
1037
- channel.close()
1038
- } catch (err) {
1039
- for (const mw of aiMiddlewares) {
1040
- if (mw.onError) {
1041
- try {
1042
- await mw.onError(singletonServices, {
1043
- error: err instanceof Error ? err : new Error(String(err)),
1044
- stepNumber: -1,
1045
- messages: runnerParams.messages,
1046
- })
1047
- } catch {
1048
- // onError hooks must not affect error flow
1049
- }
1050
- }
1051
- }
1052
- await aiRunState.updateRun(run.runId, { status: 'failed' })
1053
- channel.send({
1054
- type: 'error',
1055
- message: err instanceof Error ? err.message : String(err),
1056
- })
1057
- channel.send({ type: 'done' })
1058
- channel.close()
1059
- }
1060
- }