@pikku/core 0.12.4 → 0.12.6

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 (235) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/dist/dev/hot-reload.d.ts +10 -0
  3. package/dist/dev/hot-reload.js +158 -0
  4. package/dist/middleware-runner.d.ts +1 -0
  5. package/dist/middleware-runner.js +5 -0
  6. package/dist/permissions.d.ts +1 -0
  7. package/dist/permissions.js +5 -0
  8. package/dist/services/content-service.d.ts +2 -0
  9. package/dist/services/in-memory-workflow-service.d.ts +1 -0
  10. package/dist/services/in-memory-workflow-service.js +16 -11
  11. package/dist/services/workflow-service.d.ts +1 -0
  12. package/dist/wirings/ai-agent/ai-agent-prepare.js +16 -1
  13. package/dist/wirings/channel/channel-middleware-runner.d.ts +1 -0
  14. package/dist/wirings/channel/channel-middleware-runner.js +5 -0
  15. package/dist/wirings/http/http-runner.js +1 -1
  16. package/dist/wirings/workflow/pikku-workflow-service.d.ts +6 -0
  17. package/dist/wirings/workflow/pikku-workflow-service.js +51 -16
  18. package/dist/wirings/workflow/workflow.types.d.ts +2 -0
  19. package/package.json +5 -3
  20. package/src/crypto-utils.test.ts +214 -0
  21. package/src/crypto-utils.ts +213 -0
  22. package/src/dev/hot-reload.test.ts +484 -0
  23. package/src/dev/hot-reload.ts +212 -0
  24. package/src/errors/error-handler.ts +62 -0
  25. package/src/errors/error.test.ts +195 -0
  26. package/src/errors/errors.ts +374 -0
  27. package/src/errors/index.ts +2 -0
  28. package/src/factory-functions.test.ts +109 -0
  29. package/src/function/function-runner.test.ts +536 -0
  30. package/src/function/function-runner.ts +365 -0
  31. package/src/function/functions.types.ts +304 -0
  32. package/src/function/index.ts +5 -0
  33. package/src/handle-error.test.ts +424 -0
  34. package/src/handle-error.ts +69 -0
  35. package/src/index.ts +118 -0
  36. package/src/internal.ts +6 -0
  37. package/src/middleware/auth-apikey.test.ts +363 -0
  38. package/src/middleware/auth-apikey.ts +47 -0
  39. package/src/middleware/auth-bearer.test.ts +450 -0
  40. package/src/middleware/auth-bearer.ts +72 -0
  41. package/src/middleware/auth-cookie.test.ts +528 -0
  42. package/src/middleware/auth-cookie.ts +80 -0
  43. package/src/middleware/cors.test.ts +424 -0
  44. package/src/middleware/cors.ts +104 -0
  45. package/src/middleware/index.ts +5 -0
  46. package/src/middleware/remote-auth.test.ts +488 -0
  47. package/src/middleware/remote-auth.ts +68 -0
  48. package/src/middleware/timeout.ts +15 -0
  49. package/src/middleware-runner.test.ts +418 -0
  50. package/src/middleware-runner.ts +240 -0
  51. package/src/permissions.test.ts +434 -0
  52. package/src/permissions.ts +327 -0
  53. package/src/pikku-request.ts +23 -0
  54. package/src/pikku-response.ts +5 -0
  55. package/src/pikku-state.test.ts +224 -0
  56. package/src/pikku-state.ts +216 -0
  57. package/src/run-tests-script.test.ts +49 -0
  58. package/src/schema.test.ts +249 -0
  59. package/src/schema.ts +151 -0
  60. package/src/services/ai-agent-runner-service.ts +41 -0
  61. package/src/services/ai-run-state-service.ts +20 -0
  62. package/src/services/ai-storage-service.ts +39 -0
  63. package/src/services/content-service.ts +81 -0
  64. package/src/services/deployment-service.ts +22 -0
  65. package/src/services/gateway-service.ts +20 -0
  66. package/src/services/gopass-secrets.ts +98 -0
  67. package/src/services/in-memory-ai-run-state-service.ts +73 -0
  68. package/src/services/in-memory-trigger-service.ts +64 -0
  69. package/src/services/in-memory-workflow-service.test.ts +351 -0
  70. package/src/services/in-memory-workflow-service.ts +512 -0
  71. package/src/services/index.ts +56 -0
  72. package/src/services/jwt-service.ts +30 -0
  73. package/src/services/local-content.ts +120 -0
  74. package/src/services/local-gateway-service.ts +62 -0
  75. package/src/services/local-secrets.test.ts +80 -0
  76. package/src/services/local-secrets.ts +94 -0
  77. package/src/services/local-variables.test.ts +61 -0
  78. package/src/services/local-variables.ts +39 -0
  79. package/src/services/logger-console.test.ts +118 -0
  80. package/src/services/logger-console.ts +98 -0
  81. package/src/services/logger.ts +57 -0
  82. package/src/services/scheduler-service.ts +86 -0
  83. package/src/services/schema-service.ts +33 -0
  84. package/src/services/scoped-secret-service.test.ts +74 -0
  85. package/src/services/scoped-secret-service.ts +41 -0
  86. package/src/services/secret-service.ts +38 -0
  87. package/src/services/trigger-service.ts +17 -0
  88. package/src/services/typed-secret-service.test.ts +93 -0
  89. package/src/services/typed-secret-service.ts +70 -0
  90. package/src/services/typed-variables-service.test.ts +73 -0
  91. package/src/services/typed-variables-service.ts +81 -0
  92. package/src/services/user-session-service.test.ts +113 -0
  93. package/src/services/user-session-service.ts +86 -0
  94. package/src/services/variables-service.ts +11 -0
  95. package/src/services/workflow-service.ts +96 -0
  96. package/src/testing/index.ts +2 -0
  97. package/src/testing/service-tests.ts +873 -0
  98. package/src/time-utils.test.ts +56 -0
  99. package/src/time-utils.ts +107 -0
  100. package/src/types/core.types.ts +478 -0
  101. package/src/types/state.types.ts +188 -0
  102. package/src/utils/hash.test.ts +68 -0
  103. package/src/utils/hash.ts +26 -0
  104. package/src/utils.test.ts +350 -0
  105. package/src/utils.ts +129 -0
  106. package/src/version.test.ts +80 -0
  107. package/src/version.ts +25 -0
  108. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
  109. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
  110. package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
  111. package/src/wirings/ai-agent/ai-agent-memory.ts +310 -0
  112. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
  113. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  114. package/src/wirings/ai-agent/ai-agent-prepare.ts +659 -0
  115. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  116. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  117. package/src/wirings/ai-agent/ai-agent-runner.test.ts +319 -0
  118. package/src/wirings/ai-agent/ai-agent-runner.ts +773 -0
  119. package/src/wirings/ai-agent/ai-agent-stream.test.ts +859 -0
  120. package/src/wirings/ai-agent/ai-agent-stream.ts +1153 -0
  121. package/src/wirings/ai-agent/ai-agent.types.ts +382 -0
  122. package/src/wirings/ai-agent/index.ts +37 -0
  123. package/src/wirings/channel/channel-common.ts +90 -0
  124. package/src/wirings/channel/channel-handler.ts +250 -0
  125. package/src/wirings/channel/channel-middleware-runner.ts +126 -0
  126. package/src/wirings/channel/channel-runner.ts +166 -0
  127. package/src/wirings/channel/channel-store.ts +29 -0
  128. package/src/wirings/channel/channel.types.ts +172 -0
  129. package/src/wirings/channel/define-channel-routes.ts +25 -0
  130. package/src/wirings/channel/eventhub-service.ts +34 -0
  131. package/src/wirings/channel/eventhub-store.ts +13 -0
  132. package/src/wirings/channel/index.ts +24 -0
  133. package/src/wirings/channel/local/index.ts +3 -0
  134. package/src/wirings/channel/local/local-channel-handler.ts +81 -0
  135. package/src/wirings/channel/local/local-channel-runner.test.ts +215 -0
  136. package/src/wirings/channel/local/local-channel-runner.ts +210 -0
  137. package/src/wirings/channel/local/local-eventhub-service.test.ts +95 -0
  138. package/src/wirings/channel/local/local-eventhub-service.ts +101 -0
  139. package/src/wirings/channel/log-channels.ts +20 -0
  140. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +54 -0
  141. package/src/wirings/channel/pikku-abstract-channel-handler.ts +45 -0
  142. package/src/wirings/channel/serverless/index.ts +5 -0
  143. package/src/wirings/channel/serverless/serverless-channel-runner.ts +289 -0
  144. package/src/wirings/cli/channel/cli-channel-runner.ts +136 -0
  145. package/src/wirings/cli/channel/index.ts +1 -0
  146. package/src/wirings/cli/cli-runner.test.ts +403 -0
  147. package/src/wirings/cli/cli-runner.ts +529 -0
  148. package/src/wirings/cli/cli.types.ts +358 -0
  149. package/src/wirings/cli/command-parser.test.ts +445 -0
  150. package/src/wirings/cli/command-parser.ts +504 -0
  151. package/src/wirings/cli/define-cli-commands.ts +24 -0
  152. package/src/wirings/cli/index.ts +19 -0
  153. package/src/wirings/gateway/gateway-runner.test.ts +474 -0
  154. package/src/wirings/gateway/gateway-runner.ts +411 -0
  155. package/src/wirings/gateway/gateway.types.ts +149 -0
  156. package/src/wirings/gateway/index.ts +13 -0
  157. package/src/wirings/http/http-routes.test.ts +322 -0
  158. package/src/wirings/http/http-routes.ts +197 -0
  159. package/src/wirings/http/http-runner.test.ts +144 -0
  160. package/src/wirings/http/http-runner.ts +588 -0
  161. package/src/wirings/http/http.types.ts +369 -0
  162. package/src/wirings/http/index.ts +28 -0
  163. package/src/wirings/http/log-http-routes.ts +22 -0
  164. package/src/wirings/http/pikku-fetch-http-request.test.ts +237 -0
  165. package/src/wirings/http/pikku-fetch-http-request.ts +170 -0
  166. package/src/wirings/http/pikku-fetch-http-response.test.ts +82 -0
  167. package/src/wirings/http/pikku-fetch-http-response.ts +147 -0
  168. package/src/wirings/http/routers/http-router.ts +13 -0
  169. package/src/wirings/http/routers/path-to-regex.test.ts +319 -0
  170. package/src/wirings/http/routers/path-to-regex.ts +126 -0
  171. package/src/wirings/http/web-request.test.ts +236 -0
  172. package/src/wirings/http/web-request.ts +104 -0
  173. package/src/wirings/mcp/index.ts +27 -0
  174. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +389 -0
  175. package/src/wirings/mcp/mcp-endpoint-registry.ts +159 -0
  176. package/src/wirings/mcp/mcp-runner.ts +323 -0
  177. package/src/wirings/mcp/mcp.types.ts +216 -0
  178. package/src/wirings/node/index.ts +2 -0
  179. package/src/wirings/node/node.types.ts +23 -0
  180. package/src/wirings/oauth2/index.ts +3 -0
  181. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  182. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  183. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  184. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  185. package/src/wirings/queue/index.ts +31 -0
  186. package/src/wirings/queue/queue-runner.test.ts +710 -0
  187. package/src/wirings/queue/queue-runner.ts +192 -0
  188. package/src/wirings/queue/queue.types.ts +189 -0
  189. package/src/wirings/queue/register-queue-helper.ts +60 -0
  190. package/src/wirings/queue/validate-worker-config.test.ts +108 -0
  191. package/src/wirings/queue/validate-worker-config.ts +116 -0
  192. package/src/wirings/rpc/index.ts +4 -0
  193. package/src/wirings/rpc/rpc-runner.ts +460 -0
  194. package/src/wirings/rpc/rpc-types.ts +56 -0
  195. package/src/wirings/rpc/wire-addon.ts +21 -0
  196. package/src/wirings/scheduler/index.ts +11 -0
  197. package/src/wirings/scheduler/log-schedulers.ts +20 -0
  198. package/src/wirings/scheduler/scheduler-runner.test.ts +660 -0
  199. package/src/wirings/scheduler/scheduler-runner.ts +133 -0
  200. package/src/wirings/scheduler/scheduler.types.ts +53 -0
  201. package/src/wirings/secret/index.ts +9 -0
  202. package/src/wirings/secret/secret.types.ts +32 -0
  203. package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
  204. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  205. package/src/wirings/trigger/index.ts +10 -0
  206. package/src/wirings/trigger/pikku-trigger-service.ts +112 -0
  207. package/src/wirings/trigger/trigger-runner.test.ts +79 -0
  208. package/src/wirings/trigger/trigger-runner.ts +135 -0
  209. package/src/wirings/trigger/trigger.types.ts +178 -0
  210. package/src/wirings/variable/index.ts +8 -0
  211. package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
  212. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  213. package/src/wirings/variable/variable.types.ts +22 -0
  214. package/src/wirings/workflow/dsl/index.ts +31 -0
  215. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
  216. package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
  217. package/src/wirings/workflow/graph/graph-node.ts +222 -0
  218. package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
  219. package/src/wirings/workflow/graph/graph-runner.ts +816 -0
  220. package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
  221. package/src/wirings/workflow/graph/graph-validation.ts +237 -0
  222. package/src/wirings/workflow/graph/index.ts +19 -0
  223. package/src/wirings/workflow/graph/template.test.ts +49 -0
  224. package/src/wirings/workflow/graph/template.ts +42 -0
  225. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  226. package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
  227. package/src/wirings/workflow/index.ts +82 -0
  228. package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
  229. package/src/wirings/workflow/pikku-workflow-service.ts +1229 -0
  230. package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
  231. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  232. package/src/wirings/workflow/workflow.types.ts +276 -0
  233. package/tsconfig.json +14 -0
  234. package/tsconfig.tsbuildinfo +1 -0
  235. package/lcov.info +0 -18891
package/CHANGELOG.md CHANGED
@@ -1,9 +1,29 @@
1
- ## 0.12.0
1
+ ## 0.12.4
2
+
3
+ ## 0.12.6
4
+
5
+ ### Patch Changes
6
+
7
+ - bb27710: Add optional `uploadHeaders` to `ContentService.getUploadURL` return type, allowing storage backends (e.g. Backblaze B2) to provide required headers for direct uploads.
8
+ - a31bc63: Fix SSE error handler to send `[DONE]` as JSON (`{"type":"done"}`) for consistency with all other SSE messages.
9
+ - 3e79248: Add setStepChildRunId to workflow service implementations and auto-bootstrap in pikku all
10
+ - b0a81cc: Support sub-workflows in `workflow.do()`: when a string name is passed, it now checks if the name refers to a registered workflow and runs it as a sub-workflow, falling back to RPC invocation if not found. The `TypedWorkflow.do` type now also accepts workflow names with typed input/output. Steps that spawn sub-workflows expose `childRunId` on the step state so clients can stream sub-workflow progress.
11
+ - 6413df7: Propagate session and RPC service from the originating request to workflow runs, fixing "Authentication required" errors for workflows with `auth: true`.
12
+
13
+ ## 0.12.5
14
+
15
+ ### Patch Changes
16
+
17
+ - 198e68f: Add hot-reload for dev mode: reload functions, middleware, and permissions without server restart.
2
18
 
3
19
  ## 0.12.4
4
20
 
5
21
  ### Patch Changes
6
22
 
23
+ - 688b5e8: InMemoryWorkflowService now implements WorkflowRunService interface, adding listRuns, getRunSteps, getDistinctWorkflowNames, and deleteRun methods.
24
+
25
+ ### Patch Changes
26
+
7
27
  - InMemoryWorkflowService now implements WorkflowRunService interface (listRuns, getRunSteps, getDistinctWorkflowNames, deleteRun)
8
28
 
9
29
  ## 0.12.3
@@ -0,0 +1,10 @@
1
+ import type { Logger } from '../services/logger.js';
2
+ interface PikkuDevReloaderOptions {
3
+ srcDirectories: string[];
4
+ logger: Logger;
5
+ pikkuDir?: string;
6
+ }
7
+ export declare function pikkuDevReloader(options: PikkuDevReloaderOptions): Promise<{
8
+ close: () => void;
9
+ }>;
10
+ export {};
@@ -0,0 +1,158 @@
1
+ import { watch } from 'node:fs';
2
+ import { stat, readFile } from 'node:fs/promises';
3
+ import { join, resolve, relative } from 'node:path';
4
+ import { pikkuState } from '../pikku-state.js';
5
+ import { addFunction } from '../function/function-runner.js';
6
+ import { clearMiddlewareCache } from '../middleware-runner.js';
7
+ import { clearPermissionsCache } from '../permissions.js';
8
+ import { clearChannelMiddlewareCache } from '../wirings/channel/channel-middleware-runner.js';
9
+ import { httpRouter } from '../wirings/http/routers/http-router.js';
10
+ import { addSchema, compileAllSchemas } from '../schema.js';
11
+ const isFunctionConfig = (value) => {
12
+ return (typeof value === 'object' &&
13
+ value !== null &&
14
+ 'func' in value &&
15
+ typeof value.func === 'function');
16
+ };
17
+ const findCompiledFile = async (tsFile, srcDir, pikkuDir) => {
18
+ const rel = relative(srcDir, tsFile).replace(/\.ts$/, '.js');
19
+ const candidates = [
20
+ join(pikkuDir, 'dist', rel),
21
+ join(srcDir, rel),
22
+ tsFile.replace(/\.ts$/, '.js'),
23
+ ];
24
+ for (const candidate of candidates) {
25
+ try {
26
+ await stat(candidate);
27
+ return candidate;
28
+ }
29
+ catch {
30
+ // not found, try next
31
+ }
32
+ }
33
+ return null;
34
+ };
35
+ // Use data: URLs to import modules. This bypasses TypeScript loaders
36
+ // (e.g. tsx) that intercept file:// imports and break dynamic ESM loading.
37
+ // Each import gets unique content so there's no module cache to worry about.
38
+ const reimportModule = async (filePath) => {
39
+ try {
40
+ const content = await readFile(resolve(filePath), 'utf-8');
41
+ const dataUrl = 'data:text/javascript;base64,' + Buffer.from(content).toString('base64');
42
+ return await import(dataUrl);
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ };
48
+ const isWatchedTsFile = (filename) => {
49
+ return (filename.endsWith('.ts') &&
50
+ !filename.endsWith('.test.ts') &&
51
+ !filename.endsWith('.d.ts') &&
52
+ !filename.endsWith('.gen.ts'));
53
+ };
54
+ export async function pikkuDevReloader(options) {
55
+ const { srcDirectories, logger, pikkuDir = '.pikku' } = options;
56
+ const absSrcDirs = srcDirectories.map((d) => resolve(d));
57
+ const absPikkuDir = resolve(pikkuDir);
58
+ const watchers = [];
59
+ const functionsMap = pikkuState(null, 'function', 'functions');
60
+ const handleFileChange = async (changedTsFile) => {
61
+ const start = Date.now();
62
+ const reloadedNames = [];
63
+ const srcDir = absSrcDirs.find((d) => changedTsFile.startsWith(d));
64
+ if (!srcDir)
65
+ return;
66
+ const compiledFile = await findCompiledFile(changedTsFile, srcDir, absPikkuDir);
67
+ if (!compiledFile) {
68
+ logger.warn(`Could not find compiled JS for: ${relative(process.cwd(), changedTsFile)}`);
69
+ return;
70
+ }
71
+ const mod = await reimportModule(compiledFile);
72
+ if (!mod) {
73
+ logger.error(`Failed to import: ${relative(process.cwd(), compiledFile)} (keeping old code)`);
74
+ return;
75
+ }
76
+ let schemasChanged = false;
77
+ for (const [exportName, exportValue] of Object.entries(mod)) {
78
+ if (isFunctionConfig(exportValue) && functionsMap.has(exportName)) {
79
+ addFunction(exportName, exportValue);
80
+ reloadedNames.push(exportName);
81
+ if (exportValue.input) {
82
+ addSchema(exportName, exportValue.input);
83
+ schemasChanged = true;
84
+ }
85
+ if (exportValue.output) {
86
+ addSchema(`${exportName}Output`, exportValue.output);
87
+ schemasChanged = true;
88
+ }
89
+ }
90
+ }
91
+ if (reloadedNames.length > 0) {
92
+ clearMiddlewareCache();
93
+ clearPermissionsCache();
94
+ clearChannelMiddlewareCache();
95
+ httpRouter.reset();
96
+ if (schemasChanged) {
97
+ try {
98
+ compileAllSchemas(logger);
99
+ }
100
+ catch (err) {
101
+ const msg = err instanceof Error ? err.message : String(err);
102
+ if (msg.includes('SchemaService') ||
103
+ (msg.includes('schema') && msg.includes('not'))) {
104
+ logger.warn('Schema recompilation skipped (no SchemaService)');
105
+ }
106
+ else {
107
+ logger.error(`Schema recompilation failed: ${msg}`);
108
+ return;
109
+ }
110
+ }
111
+ }
112
+ const elapsed = Date.now() - start;
113
+ logger.info(`Hot-reloaded: ${reloadedNames.join(', ')} (${elapsed}ms)`);
114
+ }
115
+ };
116
+ let debounceTimer;
117
+ const pendingChanges = new Set();
118
+ const scheduleReload = (filePath) => {
119
+ pendingChanges.add(filePath);
120
+ if (debounceTimer)
121
+ clearTimeout(debounceTimer);
122
+ debounceTimer = setTimeout(async () => {
123
+ const files = [...pendingChanges];
124
+ pendingChanges.clear();
125
+ for (const file of files) {
126
+ try {
127
+ await handleFileChange(file);
128
+ }
129
+ catch (err) {
130
+ logger.error(`Hot-reload error for ${relative(process.cwd(), file)}: ${err instanceof Error ? err.message : String(err)}`);
131
+ }
132
+ }
133
+ }, 50);
134
+ };
135
+ for (const srcDir of absSrcDirs) {
136
+ try {
137
+ const watcher = watch(srcDir, { recursive: true }, (eventType, filename) => {
138
+ if (filename && isWatchedTsFile(filename)) {
139
+ scheduleReload(join(srcDir, filename));
140
+ }
141
+ });
142
+ watchers.push(watcher);
143
+ }
144
+ catch (err) {
145
+ logger.error(`Failed to watch directory ${srcDir}: ${err?.message || err}`);
146
+ }
147
+ }
148
+ logger.info(`Hot-reload active for: ${srcDirectories.join(', ')}`);
149
+ return {
150
+ close: () => {
151
+ if (debounceTimer)
152
+ clearTimeout(debounceTimer);
153
+ for (const watcher of watchers) {
154
+ watcher.close();
155
+ }
156
+ },
157
+ };
158
+ }
@@ -51,6 +51,7 @@ export declare const runMiddleware: <Middleware extends CorePikkuMiddleware>(ser
51
51
  * ```
52
52
  */
53
53
  export declare const addMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(tag: string, middleware: CorePikkuMiddlewareGroup, packageName?: string | null) => CorePikkuMiddlewareGroup;
54
+ export declare const clearMiddlewareCache: () => void;
54
55
  /**
55
56
  * Combines wiring-specific middleware with function-level middleware.
56
57
  *
@@ -98,6 +98,11 @@ const middlewareCache = {
98
98
  workflow: {},
99
99
  gateway: {},
100
100
  };
101
+ export const clearMiddlewareCache = () => {
102
+ for (const key of Object.keys(middlewareCache)) {
103
+ middlewareCache[key] = {};
104
+ }
105
+ };
101
106
  /**
102
107
  * Combines wiring-specific middleware with function-level middleware.
103
108
  *
@@ -29,6 +29,7 @@ import type { CorePermissionGroup, CorePikkuPermission } from './function/functi
29
29
  * ```
30
30
  */
31
31
  export declare const addPermission: (tag: string, permissions: CorePermissionGroup | CorePikkuPermission[], packageName?: string | null) => CorePermissionGroup | CorePikkuPermission[];
32
+ export declare const clearPermissionsCache: () => void;
32
33
  /**
33
34
  * Runs permission checks using combined permissions from all sources.
34
35
  * Combines permissions from wire and function levels, then validates them.
@@ -101,6 +101,11 @@ const combinedPermissionsCache = {
101
101
  workflow: {},
102
102
  gateway: {},
103
103
  };
104
+ export const clearPermissionsCache = () => {
105
+ for (const key of Object.keys(combinedPermissionsCache)) {
106
+ combinedPermissionsCache[key] = {};
107
+ }
108
+ };
104
109
  /**
105
110
  * Combines wiring-specific permissions with function-level permissions.
106
111
  *
@@ -22,6 +22,8 @@ export interface ContentService {
22
22
  getUploadURL(fileKey: string, contentType: string): Promise<{
23
23
  uploadUrl: string;
24
24
  assetKey: string;
25
+ uploadHeaders?: Record<string, string>;
26
+ uploadMethod?: 'PUT' | 'POST';
25
27
  }>;
26
28
  /**
27
29
  * Deletes a file from the storage backend.
@@ -36,6 +36,7 @@ export declare class InMemoryWorkflowService extends PikkuWorkflowService implem
36
36
  setStepScheduled(stepId: string): Promise<void>;
37
37
  setStepResult(stepId: string, result: any): Promise<void>;
38
38
  setStepError(stepId: string, error: Error): Promise<void>;
39
+ setStepChildRunId(stepId: string, childRunId: string): Promise<void>;
39
40
  createRetryAttempt(failedStepId: string, status: 'pending' | 'running'): Promise<StepState>;
40
41
  listRuns(options?: {
41
42
  workflowName?: string;
@@ -73,13 +73,14 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
73
73
  retryDelay: stepOptions?.retryDelay,
74
74
  createdAt: now,
75
75
  updatedAt: now,
76
+ stepName,
76
77
  };
77
78
  const key = `${runId}:${stepName}`;
78
79
  this.steps.set(key, step);
79
80
  this.stepData.set(stepId, { rpcName, data, stepName });
80
- // Add to history
81
+ // Add to history (same reference so mutations are reflected)
81
82
  const history = this.stepHistory.get(runId) || [];
82
- history.push({ ...step, stepName });
83
+ history.push(step);
83
84
  this.stepHistory.set(runId, history);
84
85
  return step;
85
86
  }
@@ -87,13 +88,7 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
87
88
  const key = `${runId}:${stepName}`;
88
89
  const step = this.steps.get(key);
89
90
  if (!step) {
90
- return {
91
- stepId: '',
92
- status: 'pending',
93
- attemptCount: 0,
94
- createdAt: new Date(),
95
- updatedAt: new Date(),
96
- };
91
+ throw new Error(`Step not found: runId=${runId}, stepName=${stepName}. Use insertStepState to create it.`);
97
92
  }
98
93
  return step;
99
94
  }
@@ -143,6 +138,15 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
143
138
  }
144
139
  }
145
140
  }
141
+ async setStepChildRunId(stepId, childRunId) {
142
+ for (const step of this.steps.values()) {
143
+ if (step.stepId === stepId) {
144
+ step.childRunId = childRunId;
145
+ step.updatedAt = new Date();
146
+ break;
147
+ }
148
+ }
149
+ }
146
150
  async createRetryAttempt(failedStepId, status) {
147
151
  // Find the failed step
148
152
  let failedStep;
@@ -171,6 +175,7 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
171
175
  retryDelay: failedStep.retryDelay,
172
176
  createdAt: now,
173
177
  updatedAt: now,
178
+ stepName: stepName,
174
179
  };
175
180
  if (status === 'running') {
176
181
  newStep.runningAt = now;
@@ -181,9 +186,9 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
181
186
  if (failedStepData) {
182
187
  this.stepData.set(newStepId, { ...failedStepData });
183
188
  }
184
- // Add to history
189
+ // Add to history (same reference so mutations are reflected)
185
190
  const history = this.stepHistory.get(runId) || [];
186
- history.push({ ...newStep, stepName });
191
+ history.push(newStep);
187
192
  this.stepHistory.set(runId, history);
188
193
  return newStep;
189
194
  }
@@ -31,6 +31,7 @@ export interface WorkflowService {
31
31
  setStepRunning(stepId: string): Promise<void>;
32
32
  setStepScheduled(stepId: string): Promise<void>;
33
33
  setStepResult(stepId: string, result: any): Promise<void>;
34
+ setStepChildRunId(stepId: string, childRunId: string): Promise<void>;
34
35
  setStepError(stepId: string, error: Error): Promise<void>;
35
36
  createRetryAttempt(stepId: string, status: 'pending' | 'running'): Promise<StepState>;
36
37
  executeWorkflowStep(runId: string, stepName: string, rpcName: string | null, data: any, rpcService: any): Promise<void>;
@@ -5,7 +5,7 @@ import { randomUUID } from 'crypto';
5
5
  import { streamAIAgent } from './ai-agent-stream.js';
6
6
  import { runAIAgent } from './ai-agent-runner.js';
7
7
  import { resolveNamespace, ContextAwareRPCService, } from '../../wirings/rpc/rpc-runner.js';
8
- import { buildDynamicWorkflowInstructions, buildWorkflowTools } from './agent-dynamic-workflow.js';
8
+ import { buildDynamicWorkflowInstructions, buildWorkflowTools, } from './agent-dynamic-workflow.js';
9
9
  import { resolveMemoryServices, loadContextMessages, trimMessages, } from './ai-agent-memory.js';
10
10
  import { resolveModelConfig } from './ai-agent-model-config.js';
11
11
  export class ToolApprovalRequired extends PikkuError {
@@ -272,6 +272,21 @@ export async function buildToolDefs(params, agentSessionMap, resourceId, agentNa
272
272
  }
273
273
  // No stream context: sub-agent runs non-streaming
274
274
  const result = await runAIAgent(subAgentName, { message, threadId, resourceId }, params, agentSessionMap);
275
+ if (result.status === 'suspended' &&
276
+ result.pendingApprovals?.length) {
277
+ return {
278
+ __approvalRequired: true,
279
+ toolName: subAgentName,
280
+ args: toolInput,
281
+ agentRunId: result.runId,
282
+ subApprovals: result.pendingApprovals.map((a) => ({
283
+ toolCallId: a.toolCallId,
284
+ toolName: a.toolName,
285
+ args: a.args,
286
+ runId: a.runId,
287
+ })),
288
+ };
289
+ }
275
290
  return result.object ?? result.text;
276
291
  },
277
292
  });
@@ -1,6 +1,7 @@
1
1
  import type { CoreSingletonServices, MiddlewareMetadata, PikkuWire } from '../../types/core.types.js';
2
2
  import type { CorePikkuChannelMiddleware } from './channel.types.js';
3
3
  export declare const addChannelMiddleware: (tag: string, middleware: CorePikkuChannelMiddleware[], packageName?: string | null) => CorePikkuChannelMiddleware[];
4
+ export declare const clearChannelMiddlewareCache: () => void;
4
5
  export declare const combineChannelMiddleware: (wireType: string, uid: string, { wireInheritedChannelMiddleware, wireChannelMiddleware, packageName, }?: {
5
6
  wireInheritedChannelMiddleware?: MiddlewareMetadata[];
6
7
  wireChannelMiddleware?: CorePikkuChannelMiddleware[];
@@ -11,6 +11,11 @@ const getChannelMiddlewareByName = (name) => {
11
11
  return middleware?.[0];
12
12
  };
13
13
  const channelMiddlewareCache = {};
14
+ export const clearChannelMiddlewareCache = () => {
15
+ for (const key of Object.keys(channelMiddlewareCache)) {
16
+ delete channelMiddlewareCache[key];
17
+ }
18
+ };
14
19
  export const combineChannelMiddleware = (wireType, uid, { wireInheritedChannelMiddleware, wireChannelMiddleware, packageName = null, } = {}) => {
15
20
  const cacheKey = `${wireType}:${uid}`;
16
21
  if (channelMiddlewareCache[cacheKey]) {
@@ -402,7 +402,7 @@ export const fetchData = async (request, response, { skipUserSession = false, re
402
402
  type: 'error',
403
403
  errorText: e instanceof Error ? e.message : String(e),
404
404
  }));
405
- response.arrayBuffer('[DONE]');
405
+ response.arrayBuffer(JSON.stringify({ type: 'done' }));
406
406
  }
407
407
  catch (streamErr) {
408
408
  singletonServices.logger.error(`SSE error while sending error payload: ${streamErr instanceof Error ? streamErr.message : String(streamErr)}`);
@@ -119,6 +119,12 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
119
119
  * @param result - Step result
120
120
  */
121
121
  abstract setStepResult(stepId: string, result: any): Promise<void>;
122
+ /**
123
+ * Set the child workflow run ID on a step
124
+ * @param stepId - Step ID
125
+ * @param childRunId - Child workflow run ID
126
+ */
127
+ abstract setStepChildRunId(stepId: string, childRunId: string): Promise<void>;
122
128
  /**
123
129
  * Store step error and mark as failed
124
130
  * Updates both workflow_step and workflow_step_history
@@ -175,17 +175,16 @@ export class PikkuWorkflowService {
175
175
  if (!workflowMeta.graphHash) {
176
176
  throw new Error(`Missing workflow graphHash for '${name}'`);
177
177
  }
178
- const runId = await this.createRun(name, input, options?.inline ?? false, workflowMeta.graphHash, wire);
179
- if (options?.inline) {
178
+ const shouldInline = options?.inline || !getSingletonServices()?.queueService;
179
+ const runId = await this.createRun(name, input, shouldInline, workflowMeta.graphHash, wire);
180
+ if (shouldInline) {
180
181
  this.inlineRuns.add(runId);
181
- }
182
- if (options?.inline || !getSingletonServices()?.queueService) {
183
182
  this.runWorkflowJob(runId, rpcService)
184
- .catch(() => { })
183
+ .catch((err) => {
184
+ getSingletonServices().logger.error(`Workflow ${name} (run ${runId}) failed:`, err);
185
+ })
185
186
  .finally(() => {
186
- if (options?.inline) {
187
- this.inlineRuns.delete(runId);
188
- }
187
+ this.inlineRuns.delete(runId);
189
188
  });
190
189
  }
191
190
  else {
@@ -237,7 +236,11 @@ export class PikkuWorkflowService {
237
236
  }
238
237
  await this.withRunLock(runId, async () => {
239
238
  const workflowWire = this.createWorkflowWire(run.workflow, runId, rpcService);
240
- const wire = { workflow: workflowWire };
239
+ const wire = {
240
+ workflow: workflowWire,
241
+ session: rpcService.wire?.session,
242
+ rpc: rpcService.wire?.rpc,
243
+ };
241
244
  try {
242
245
  const result = await runPikkuFunc('workflow', workflowMeta.name, workflowMeta.pikkuFuncId, {
243
246
  singletonServices: getSingletonServices(),
@@ -460,13 +463,45 @@ export class PikkuWorkflowService {
460
463
  while (true) {
461
464
  try {
462
465
  await this.setStepRunning(currentStepState.stepId);
463
- const result = await rpcService.rpcWithWire(rpcName, data, {
464
- workflowStep: {
465
- runId,
466
- stepId: currentStepState.stepId,
467
- attemptCount: currentStepState.attemptCount,
468
- },
469
- });
466
+ // Check if the name refers to a workflow
467
+ const workflowMeta = pikkuState(null, 'workflows', 'meta')[rpcName];
468
+ let result;
469
+ if (workflowMeta) {
470
+ const childWire = {
471
+ type: 'workflow',
472
+ id: rpcName,
473
+ parentRunId: runId,
474
+ };
475
+ const { runId: childRunId } = await this.startWorkflow(rpcName, data, childWire, rpcService, { inline: true });
476
+ await this.setStepChildRunId(currentStepState.stepId, childRunId);
477
+ // Poll until child workflow completes
478
+ while (true) {
479
+ const childRun = await this.getRun(childRunId);
480
+ if (!childRun) {
481
+ throw new WorkflowRunNotFoundError(childRunId);
482
+ }
483
+ if (WORKFLOW_END_STATES.has(childRun.status)) {
484
+ if (childRun.status === 'failed') {
485
+ throw new Error(childRun.error?.message || 'Sub-workflow failed');
486
+ }
487
+ if (childRun.status === 'cancelled') {
488
+ throw new Error('Sub-workflow was cancelled');
489
+ }
490
+ result = childRun.output;
491
+ break;
492
+ }
493
+ await new Promise((resolve) => setTimeout(resolve, 500));
494
+ }
495
+ }
496
+ else {
497
+ result = await rpcService.rpcWithWire(rpcName, data, {
498
+ workflowStep: {
499
+ runId,
500
+ stepId: currentStepState.stepId,
501
+ attemptCount: currentStepState.attemptCount,
502
+ },
503
+ });
504
+ }
470
505
  await this.setStepResult(currentStepState.stepId, result);
471
506
  return result;
472
507
  }
@@ -76,6 +76,8 @@ export interface StepState {
76
76
  createdAt: Date;
77
77
  /** Last update timestamp */
78
78
  updatedAt: Date;
79
+ /** Child workflow run ID (if this step spawned a sub-workflow) */
80
+ childRunId?: string;
79
81
  /** Timestamp when step started running */
80
82
  runningAt?: Date;
81
83
  /** Timestamp when step was scheduled */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.4",
3
+ "version": "0.12.6",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",
@@ -13,7 +13,8 @@
13
13
  "release": "npm run build && npm test",
14
14
  "test": "bash run-tests.sh",
15
15
  "test:watch": "bash run-tests.sh --watch",
16
- "test:coverage": "bash run-tests.sh --coverage"
16
+ "test:coverage": "bash run-tests.sh --coverage",
17
+ "prepublishOnly": "yarn build"
17
18
  },
18
19
  "exports": {
19
20
  ".": "./dist/index.js",
@@ -44,7 +45,8 @@
44
45
  "./crypto-utils": "./dist/crypto-utils.js",
45
46
  "./internal": "./dist/internal.js",
46
47
  "./schema": "./dist/schema.js",
47
- "./testing": "./dist/testing/index.js"
48
+ "./testing": "./dist/testing/index.js",
49
+ "./dev": "./dist/dev/hot-reload.js"
48
50
  },
49
51
  "dependencies": {
50
52
  "@standard-schema/spec": "^1.1.0",