@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,652 +0,0 @@
1
- import { beforeEach, describe, test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
-
4
- import { resetPikkuState, pikkuState } from '../../pikku-state.js'
5
- import { streamAIAgent } from './ai-agent-stream.js'
6
- import { ToolApprovalRequired } from './ai-agent-prepare.js'
7
- import type {
8
- CoreAIAgent,
9
- AIStreamEvent,
10
- PikkuAIMiddlewareHooks,
11
- } from './ai-agent.types.js'
12
- import type { AIAgentStepResult } from '../../services/ai-agent-runner-service.js'
13
-
14
- beforeEach(() => {
15
- resetPikkuState()
16
- })
17
-
18
- const addTestAgent = (agentName: string) => {
19
- const agent: CoreAIAgent = {
20
- name: agentName,
21
- description: 'test agent',
22
- instructions: 'be helpful',
23
- model: 'test-model',
24
- }
25
-
26
- pikkuState(null, 'agent', 'agentsMeta')[agentName] = {
27
- ...agent,
28
- inputSchema: null,
29
- outputSchema: null,
30
- workingMemorySchema: null,
31
- }
32
- pikkuState(null, 'agent', 'agents').set(agentName, agent)
33
- pikkuState(null, 'models', 'config', {
34
- models: { 'test-model': 'test/test-model' },
35
- })
36
- }
37
-
38
- const makeStepResult = (
39
- overrides?: Partial<AIAgentStepResult>
40
- ): AIAgentStepResult => ({
41
- text: '',
42
- toolCalls: [],
43
- toolResults: [],
44
- usage: { inputTokens: 0, outputTokens: 0 },
45
- finishReason: 'stop',
46
- ...overrides,
47
- })
48
-
49
- describe('streamAIAgent', () => {
50
- test('marks run as failed and emits error/done when stream runner throws', async () => {
51
- addTestAgent('failing-stream-agent')
52
-
53
- const updates: Array<{ runId: string; patch: unknown }> = []
54
- const events: AIStreamEvent[] = []
55
-
56
- const mockServices = {
57
- logger: {
58
- info: () => {},
59
- warn: () => {},
60
- error: () => {},
61
- debug: () => {},
62
- },
63
- aiAgentRunner: {
64
- stream: async () => {
65
- throw new Error('stream failed')
66
- },
67
- },
68
- aiRunState: {
69
- createRun: async () => 'run-1',
70
- updateRun: async (runId: string, patch: unknown) => {
71
- updates.push({ runId, patch })
72
- },
73
- },
74
- } as any
75
-
76
- pikkuState(null, 'package', 'singletonServices', mockServices)
77
-
78
- await streamAIAgent(
79
- 'failing-stream-agent',
80
- {
81
- message: 'hello',
82
- threadId: 'thread-1',
83
- resourceId: 'resource-1',
84
- },
85
- {
86
- channelId: 'channel-1',
87
- openingData: undefined,
88
- state: 'open',
89
- send: (event: AIStreamEvent) => {
90
- events.push(event)
91
- },
92
- close: () => {},
93
- },
94
- {}
95
- )
96
-
97
- assert.deepEqual(updates, [{ runId: 'run-1', patch: { status: 'failed' } }])
98
- assert.deepEqual(
99
- events.map((event) => event.type),
100
- ['step-start', 'error', 'done']
101
- )
102
- })
103
-
104
- test('completes normally when stream returns no tool calls', async () => {
105
- addTestAgent('simple-stream-agent')
106
-
107
- const updates: Array<{ runId: string; patch: unknown }> = []
108
- const events: AIStreamEvent[] = []
109
-
110
- const mockServices = {
111
- logger: {
112
- info: () => {},
113
- warn: () => {},
114
- error: () => {},
115
- debug: () => {},
116
- },
117
- aiAgentRunner: {
118
- stream: async (): Promise<AIAgentStepResult> => {
119
- return makeStepResult({ text: 'Hello world' })
120
- },
121
- },
122
- aiRunState: {
123
- createRun: async () => 'run-simple',
124
- updateRun: async (runId: string, patch: unknown) => {
125
- updates.push({ runId, patch })
126
- },
127
- },
128
- } as any
129
-
130
- pikkuState(null, 'package', 'singletonServices', mockServices)
131
-
132
- await streamAIAgent(
133
- 'simple-stream-agent',
134
- {
135
- message: 'hello',
136
- threadId: 'thread-simple',
137
- resourceId: 'resource-simple',
138
- },
139
- {
140
- channelId: 'channel-simple',
141
- openingData: undefined,
142
- state: 'open',
143
- send: (event: AIStreamEvent) => {
144
- events.push(event)
145
- },
146
- close: () => {},
147
- },
148
- {}
149
- )
150
-
151
- assert.deepEqual(updates, [
152
- { runId: 'run-simple', patch: { status: 'completed' } },
153
- ])
154
- assert.ok(events.some((e) => e.type === 'done'))
155
- })
156
-
157
- test('suspends run when tool needs approval (needsApproval flag)', async () => {
158
- addTestAgent('approval-stream-agent')
159
-
160
- const updates: Array<{ runId: string; patch: unknown }> = []
161
- const events: AIStreamEvent[] = []
162
-
163
- const mockServices = {
164
- logger: {
165
- info: () => {},
166
- warn: () => {},
167
- error: () => {},
168
- debug: () => {},
169
- },
170
- aiAgentRunner: {
171
- stream: async (): Promise<AIAgentStepResult> => {
172
- return makeStepResult({
173
- toolCalls: [
174
- {
175
- toolCallId: 'tool-call-1',
176
- toolName: 'tool-x',
177
- args: { id: 1 },
178
- },
179
- ],
180
- })
181
- },
182
- },
183
- aiRunState: {
184
- createRun: async () => 'run-2',
185
- updateRun: async (runId: string, patch: unknown) => {
186
- updates.push({ runId, patch })
187
- },
188
- },
189
- } as any
190
-
191
- pikkuState(null, 'package', 'singletonServices', mockServices)
192
-
193
- const agentName = 'approval-stream-agent'
194
- const agent = pikkuState(null, 'agent', 'agents').get(agentName)!
195
- agent.tools = ['tool-x']
196
- pikkuState(null, 'agent', 'agents').set(agentName, agent)
197
- pikkuState(null, 'agent', 'agentsMeta')[agentName].tools = ['tool-x']
198
- pikkuState(null, 'rpc', 'meta')['tool-x'] = 'tool-x'
199
- pikkuState(null, 'function', 'meta')['tool-x'] = {
200
- description: 'test tool',
201
- requiresApproval: true,
202
- }
203
- pikkuState(null, 'misc', 'schemas').set('ToolXInput', {
204
- type: 'object',
205
- properties: { id: { type: 'number' } },
206
- })
207
-
208
- await streamAIAgent(
209
- agentName,
210
- {
211
- message: 'hello',
212
- threadId: 'thread-2',
213
- resourceId: 'resource-2',
214
- },
215
- {
216
- channelId: 'channel-2',
217
- openingData: undefined,
218
- state: 'open',
219
- send: (event: AIStreamEvent) => {
220
- events.push(event)
221
- },
222
- close: () => {},
223
- },
224
- {},
225
- undefined,
226
- { requiresToolApproval: 'all' }
227
- )
228
-
229
- assert.deepEqual(updates, [
230
- {
231
- runId: 'run-2',
232
- patch: {
233
- status: 'suspended',
234
- suspendReason: 'approval',
235
- pendingApprovals: [
236
- {
237
- type: 'tool-call',
238
- toolCallId: 'tool-call-1',
239
- toolName: 'tool-x',
240
- args: { id: 1 },
241
- },
242
- ],
243
- },
244
- },
245
- ])
246
-
247
- const approvalEvent = events.find(
248
- (e) => e.type === 'approval-request'
249
- ) as any
250
- assert.ok(approvalEvent)
251
- assert.equal(approvalEvent.toolCallId, 'tool-call-1')
252
- assert.equal(approvalEvent.toolName, 'tool-x')
253
- assert.deepEqual(approvalEvent.args, { id: 1 })
254
-
255
- assert.deepEqual(
256
- events
257
- .filter((e) => e.type === 'approval-request' || e.type === 'done')
258
- .map((e) => e.type),
259
- ['approval-request', 'done']
260
- )
261
- })
262
-
263
- test('suspends with agent-call type when sub-agent returns approval sentinel', async () => {
264
- addTestAgent('parent-agent')
265
-
266
- const updates: Array<{ runId: string; patch: unknown }> = []
267
- const events: AIStreamEvent[] = []
268
-
269
- const mockServices = {
270
- logger: {
271
- info: () => {},
272
- warn: () => {},
273
- error: () => {},
274
- debug: () => {},
275
- },
276
- aiAgentRunner: {
277
- stream: async (): Promise<AIAgentStepResult> => {
278
- return makeStepResult({
279
- toolCalls: [
280
- {
281
- toolCallId: 'tool-call-2',
282
- toolName: 'sub-agent',
283
- args: { message: 'hi' },
284
- },
285
- ],
286
- toolResults: [
287
- {
288
- toolCallId: 'tool-call-2',
289
- toolName: 'sub-agent',
290
- result: {
291
- __approvalRequired: true,
292
- toolName: 'sub-agent',
293
- args: { message: 'hi' },
294
- displayToolName: 'deleteTodo',
295
- displayArgs: { todoId: '2' },
296
- agentRunId: 'sub-run-1',
297
- },
298
- },
299
- ],
300
- })
301
- },
302
- },
303
- aiRunState: {
304
- createRun: async () => 'run-3',
305
- updateRun: async (runId: string, patch: unknown) => {
306
- updates.push({ runId, patch })
307
- },
308
- },
309
- } as any
310
-
311
- pikkuState(null, 'package', 'singletonServices', mockServices)
312
-
313
- await streamAIAgent(
314
- 'parent-agent',
315
- {
316
- message: 'hello',
317
- threadId: 'thread-3',
318
- resourceId: 'resource-3',
319
- },
320
- {
321
- channelId: 'channel-3',
322
- openingData: undefined,
323
- state: 'open',
324
- send: (event: AIStreamEvent) => {
325
- events.push(event)
326
- },
327
- close: () => {},
328
- },
329
- {}
330
- )
331
-
332
- assert.deepEqual(updates, [
333
- {
334
- runId: 'run-3',
335
- patch: {
336
- status: 'suspended',
337
- suspendReason: 'approval',
338
- pendingApprovals: [
339
- {
340
- type: 'agent-call',
341
- toolCallId: 'tool-call-2',
342
- agentName: 'sub-agent',
343
- agentRunId: 'sub-run-1',
344
- displayToolName: 'deleteTodo',
345
- displayArgs: { todoId: '2' },
346
- },
347
- ],
348
- },
349
- },
350
- ])
351
-
352
- const approvalEvent = events.find(
353
- (e) => e.type === 'approval-request'
354
- ) as any
355
- assert.ok(approvalEvent)
356
- assert.equal(approvalEvent.toolCallId, 'tool-call-2')
357
- assert.equal(approvalEvent.toolName, 'deleteTodo')
358
- assert.deepEqual(approvalEvent.args, { todoId: '2' })
359
- })
360
-
361
- test('loops through multiple steps when tools are called', async () => {
362
- addTestAgent('multi-step-agent')
363
-
364
- const updates: Array<{ runId: string; patch: unknown }> = []
365
- const events: AIStreamEvent[] = []
366
- let callCount = 0
367
-
368
- const mockServices = {
369
- logger: {
370
- info: () => {},
371
- warn: () => {},
372
- error: () => {},
373
- debug: () => {},
374
- },
375
- aiAgentRunner: {
376
- stream: async (): Promise<AIAgentStepResult> => {
377
- callCount++
378
- if (callCount === 1) {
379
- return makeStepResult({
380
- text: 'Calling tool...',
381
- toolCalls: [
382
- { toolCallId: 'tc-1', toolName: 'myTool', args: { q: 'test' } },
383
- ],
384
- toolResults: [
385
- {
386
- toolCallId: 'tc-1',
387
- toolName: 'myTool',
388
- result: 'tool result',
389
- },
390
- ],
391
- finishReason: 'tool-calls',
392
- })
393
- }
394
- return makeStepResult({ text: 'Final answer', finishReason: 'stop' })
395
- },
396
- },
397
- aiRunState: {
398
- createRun: async () => 'run-multi',
399
- updateRun: async (runId: string, patch: unknown) => {
400
- updates.push({ runId, patch })
401
- },
402
- },
403
- } as any
404
-
405
- pikkuState(null, 'package', 'singletonServices', mockServices)
406
-
407
- await streamAIAgent(
408
- 'multi-step-agent',
409
- {
410
- message: 'hello',
411
- threadId: 'thread-multi',
412
- resourceId: 'resource-multi',
413
- },
414
- {
415
- channelId: 'channel-multi',
416
- openingData: undefined,
417
- state: 'open',
418
- send: (event: AIStreamEvent) => {
419
- events.push(event)
420
- },
421
- close: () => {},
422
- },
423
- {}
424
- )
425
-
426
- assert.equal(callCount, 2)
427
- assert.deepEqual(updates, [
428
- { runId: 'run-multi', patch: { status: 'completed' } },
429
- ])
430
- assert.ok(events.some((e) => e.type === 'done'))
431
- })
432
-
433
- test('afterStep hook is called for each stream step', async () => {
434
- addTestAgent('afterstep-stream-agent')
435
-
436
- const afterStepCalls: unknown[] = []
437
- const middleware: PikkuAIMiddlewareHooks = {
438
- afterStep: async (_services, ctx) => {
439
- afterStepCalls.push(ctx)
440
- },
441
- }
442
- const agent = pikkuState(null, 'agent', 'agents').get(
443
- 'afterstep-stream-agent'
444
- )!
445
- agent.aiMiddleware = [middleware] as any
446
- pikkuState(null, 'agent', 'agents').set('afterstep-stream-agent', agent)
447
-
448
- let callCount = 0
449
- const events: AIStreamEvent[] = []
450
-
451
- const mockServices = {
452
- logger: {
453
- info: () => {},
454
- warn: () => {},
455
- error: () => {},
456
- debug: () => {},
457
- },
458
- aiAgentRunner: {
459
- stream: async (): Promise<AIAgentStepResult> => {
460
- callCount++
461
- if (callCount === 1) {
462
- return makeStepResult({
463
- text: 'step1',
464
- toolCalls: [
465
- { toolCallId: 'tc-1', toolName: 'myTool', args: { q: 'x' } },
466
- ],
467
- toolResults: [
468
- { toolCallId: 'tc-1', toolName: 'myTool', result: 'ok' },
469
- ],
470
- usage: { inputTokens: 10, outputTokens: 5 },
471
- finishReason: 'tool-calls',
472
- })
473
- }
474
- return makeStepResult({
475
- text: 'final',
476
- finishReason: 'stop',
477
- })
478
- },
479
- },
480
- aiRunState: {
481
- createRun: async () => 'run-as',
482
- updateRun: async () => {},
483
- },
484
- } as any
485
-
486
- pikkuState(null, 'package', 'singletonServices', mockServices)
487
-
488
- await streamAIAgent(
489
- 'afterstep-stream-agent',
490
- { message: 'hi', threadId: 't-as', resourceId: 'r-as' },
491
- {
492
- channelId: 'c-as',
493
- openingData: undefined,
494
- state: 'open',
495
- send: (event: AIStreamEvent) => events.push(event),
496
- close: () => {},
497
- },
498
- {}
499
- )
500
-
501
- assert.equal(afterStepCalls.length, 2)
502
- assert.equal((afterStepCalls[0] as any).stepNumber, 0)
503
- assert.equal((afterStepCalls[0] as any).finishReason, 'tool-calls')
504
- assert.equal((afterStepCalls[1] as any).stepNumber, 1)
505
- assert.equal((afterStepCalls[1] as any).text, 'final')
506
- })
507
-
508
- test('onError hook is called when stream throws', async () => {
509
- addTestAgent('onerror-stream-agent')
510
-
511
- const onErrorCalls: unknown[] = []
512
- const middleware: PikkuAIMiddlewareHooks = {
513
- onError: async (_services, ctx) => {
514
- onErrorCalls.push(ctx)
515
- },
516
- }
517
- const agent = pikkuState(null, 'agent', 'agents').get(
518
- 'onerror-stream-agent'
519
- )!
520
- agent.aiMiddleware = [middleware] as any
521
- pikkuState(null, 'agent', 'agents').set('onerror-stream-agent', agent)
522
-
523
- const events: AIStreamEvent[] = []
524
- const mockServices = {
525
- logger: {
526
- info: () => {},
527
- warn: () => {},
528
- error: () => {},
529
- debug: () => {},
530
- },
531
- aiAgentRunner: {
532
- stream: async () => {
533
- throw new Error('stream boom')
534
- },
535
- },
536
- aiRunState: {
537
- createRun: async () => 'run-oe',
538
- updateRun: async () => {},
539
- },
540
- } as any
541
-
542
- pikkuState(null, 'package', 'singletonServices', mockServices)
543
-
544
- await streamAIAgent(
545
- 'onerror-stream-agent',
546
- { message: 'hi', threadId: 't-oe', resourceId: 'r-oe' },
547
- {
548
- channelId: 'c-oe',
549
- openingData: undefined,
550
- state: 'open',
551
- send: (event: AIStreamEvent) => events.push(event),
552
- close: () => {},
553
- },
554
- {}
555
- )
556
-
557
- assert.equal(onErrorCalls.length, 1)
558
- assert.equal((onErrorCalls[0] as any).error.message, 'stream boom')
559
- assert.ok(events.some((e) => e.type === 'error'))
560
- assert.ok(events.some((e) => e.type === 'done'))
561
- })
562
-
563
- test('beforeToolCall and afterToolCall hooks fire for tool executions', async () => {
564
- addTestAgent('toolhooks-stream-agent')
565
-
566
- const hookCalls: { hook: string; ctx: any }[] = []
567
- const middleware: PikkuAIMiddlewareHooks = {
568
- beforeToolCall: async (_services, ctx) => {
569
- hookCalls.push({ hook: 'before', ctx })
570
- return { args: { ...ctx.args, injected: true } }
571
- },
572
- afterToolCall: async (_services, ctx) => {
573
- hookCalls.push({ hook: 'after', ctx })
574
- return { result: `modified:${ctx.result}` }
575
- },
576
- }
577
- const agentName = 'toolhooks-stream-agent'
578
- const agent = pikkuState(null, 'agent', 'agents').get(agentName)!
579
- agent.aiMiddleware = [middleware] as any
580
- agent.tools = ['myTool']
581
- pikkuState(null, 'agent', 'agents').set(agentName, agent)
582
- pikkuState(null, 'agent', 'agentsMeta')[agentName].tools = ['myTool']
583
- pikkuState(null, 'rpc', 'meta')['myTool'] = 'myTool'
584
- pikkuState(null, 'function', 'meta')['myTool'] = {
585
- description: 'test tool',
586
- }
587
-
588
- const events: AIStreamEvent[] = []
589
- let callCount = 0
590
-
591
- const mockServices = {
592
- logger: {
593
- info: () => {},
594
- warn: () => {},
595
- error: () => {},
596
- debug: () => {},
597
- },
598
- aiAgentRunner: {
599
- stream: async (
600
- params: any,
601
- _channel: any
602
- ): Promise<AIAgentStepResult> => {
603
- callCount++
604
- if (callCount === 1) {
605
- const tool = params.tools.find((t: any) => t.name === 'myTool')
606
- if (tool) {
607
- try {
608
- await tool.execute({ q: 'test' })
609
- } catch {
610
- // runPikkuFunc not fully mocked; hooks still fire
611
- }
612
- }
613
- return makeStepResult({
614
- text: 'done',
615
- finishReason: 'stop',
616
- })
617
- }
618
- return makeStepResult({ text: 'done', finishReason: 'stop' })
619
- },
620
- },
621
- aiRunState: {
622
- createRun: async () => 'run-th',
623
- updateRun: async () => {},
624
- },
625
- } as any
626
-
627
- pikkuState(null, 'package', 'singletonServices', mockServices)
628
- pikkuState(null, 'package', 'createWireServices', () => ({}))
629
-
630
- await streamAIAgent(
631
- agentName,
632
- { message: 'hi', threadId: 't-th', resourceId: 'r-th' },
633
- {
634
- channelId: 'c-th',
635
- openingData: undefined,
636
- state: 'open',
637
- send: (event: AIStreamEvent) => events.push(event),
638
- close: () => {},
639
- },
640
- {}
641
- )
642
-
643
- assert.equal(hookCalls.length, 2)
644
- assert.equal(hookCalls[0].hook, 'before')
645
- assert.equal(hookCalls[0].ctx.toolName, 'myTool')
646
- assert.deepEqual(hookCalls[0].ctx.args, { q: 'test' })
647
- assert.equal(hookCalls[1].hook, 'after')
648
- assert.equal(hookCalls[1].ctx.toolName, 'myTool')
649
- assert.deepEqual(hookCalls[1].ctx.args, { q: 'test', injected: true })
650
- assert.ok(hookCalls[1].ctx.durationMs >= 0)
651
- })
652
- })