@pikku/core 0.12.19 → 0.12.21

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 (146) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/dist/dev/hot-reload.js +20 -6
  3. package/dist/errors/error-handler.d.ts +1 -1
  4. package/dist/errors/error-handler.js +2 -2
  5. package/dist/function/function-runner.js +53 -3
  6. package/dist/function/functions.types.d.ts +3 -2
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +2 -2
  9. package/dist/middleware/index.d.ts +2 -2
  10. package/dist/middleware/index.js +2 -2
  11. package/dist/middleware/telemetry.d.ts +2 -2
  12. package/dist/middleware/telemetry.js +2 -2
  13. package/dist/middleware-runner.d.ts +15 -27
  14. package/dist/middleware-runner.js +25 -30
  15. package/dist/permissions.d.ts +15 -22
  16. package/dist/permissions.js +30 -25
  17. package/dist/pikku-request.js +1 -1
  18. package/dist/pikku-state.js +2 -3
  19. package/dist/services/ai-agent-runner-service.d.ts +1 -0
  20. package/dist/services/content-service.d.ts +66 -37
  21. package/dist/services/in-memory-queue-service.js +1 -1
  22. package/dist/services/in-memory-workflow-service.d.ts +15 -13
  23. package/dist/services/in-memory-workflow-service.js +31 -13
  24. package/dist/services/index.d.ts +1 -1
  25. package/dist/services/local-content.d.ts +10 -12
  26. package/dist/services/local-content.js +54 -38
  27. package/dist/services/user-session-service.d.ts +1 -1
  28. package/dist/testing/service-tests.js +24 -0
  29. package/dist/types/core.types.d.ts +4 -0
  30. package/dist/types/state.types.d.ts +2 -18
  31. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +24 -5
  32. package/dist/wirings/ai-agent/ai-agent-memory.js +128 -23
  33. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +10 -1
  34. package/dist/wirings/ai-agent/ai-agent-model-config.js +15 -35
  35. package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -1
  36. package/dist/wirings/ai-agent/ai-agent-runner.js +45 -31
  37. package/dist/wirings/ai-agent/ai-agent-stream.js +63 -34
  38. package/dist/wirings/ai-agent/ai-agent.types.d.ts +20 -0
  39. package/dist/wirings/channel/channel-handler.js +1 -1
  40. package/dist/wirings/channel/channel-store.d.ts +13 -0
  41. package/dist/wirings/channel/channel.types.d.ts +3 -0
  42. package/dist/wirings/channel/local/local-channel-runner.js +23 -5
  43. package/dist/wirings/channel/pikku-abstract-channel-handler.js +8 -0
  44. package/dist/wirings/channel/serverless/serverless-channel-runner.js +9 -0
  45. package/dist/wirings/cli/cli-runner.js +24 -5
  46. package/dist/wirings/cli/command-parser.js +19 -4
  47. package/dist/wirings/http/http-runner.js +72 -36
  48. package/dist/wirings/http/http.types.d.ts +1 -0
  49. package/dist/wirings/http/pikku-fetch-http-request.js +3 -1
  50. package/dist/wirings/http/web-request.js +32 -0
  51. package/dist/wirings/rpc/rpc-runner.d.ts +3 -2
  52. package/dist/wirings/rpc/rpc-runner.js +45 -11
  53. package/dist/wirings/workflow/graph/graph-node.d.ts +8 -0
  54. package/dist/wirings/workflow/graph/graph-node.js +4 -0
  55. package/dist/wirings/workflow/graph/graph-runner.js +12 -10
  56. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +4 -0
  57. package/dist/wirings/workflow/index.d.ts +1 -1
  58. package/dist/wirings/workflow/pikku-workflow-service.d.ts +81 -16
  59. package/dist/wirings/workflow/pikku-workflow-service.js +266 -45
  60. package/dist/wirings/workflow/workflow.types.d.ts +34 -0
  61. package/package.json +1 -1
  62. package/run-tests.sh +4 -1
  63. package/src/dev/hot-reload.test.ts +62 -11
  64. package/src/dev/hot-reload.ts +28 -7
  65. package/src/errors/error-handler.ts +8 -2
  66. package/src/errors/error.test.ts +2 -0
  67. package/src/function/function-runner.test.ts +500 -10
  68. package/src/function/function-runner.ts +68 -3
  69. package/src/function/functions.types.ts +3 -2
  70. package/src/index.ts +22 -3
  71. package/src/middleware/index.ts +11 -2
  72. package/src/middleware/telemetry.ts +2 -2
  73. package/src/middleware-runner.test.ts +16 -16
  74. package/src/middleware-runner.ts +42 -30
  75. package/src/permissions.test.ts +27 -24
  76. package/src/permissions.ts +41 -25
  77. package/src/pikku-request.test.ts +35 -0
  78. package/src/pikku-request.ts +1 -1
  79. package/src/pikku-state.ts +2 -3
  80. package/src/run-tests-script.test.ts +18 -0
  81. package/src/services/ai-agent-runner-service.ts +1 -0
  82. package/src/services/content-service.ts +79 -51
  83. package/src/services/in-memory-queue-service.ts +1 -1
  84. package/src/services/in-memory-session-store.ts +1 -2
  85. package/src/services/in-memory-workflow-service.test.ts +33 -11
  86. package/src/services/in-memory-workflow-service.ts +49 -13
  87. package/src/services/index.ts +10 -1
  88. package/src/services/local-content.test.ts +54 -0
  89. package/src/services/local-content.ts +80 -53
  90. package/src/services/typed-credential-service.ts +3 -3
  91. package/src/services/typed-secret-service.ts +3 -3
  92. package/src/services/typed-variables-service.ts +3 -3
  93. package/src/services/user-session-service.ts +4 -4
  94. package/src/testing/service-tests.ts +30 -0
  95. package/src/types/core.types.ts +6 -2
  96. package/src/types/state.types.ts +2 -13
  97. package/src/wirings/ai-agent/ai-agent-memory.test.ts +324 -0
  98. package/src/wirings/ai-agent/ai-agent-memory.ts +187 -36
  99. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +12 -90
  100. package/src/wirings/ai-agent/ai-agent-model-config.ts +14 -38
  101. package/src/wirings/ai-agent/ai-agent-prepare.test.ts +292 -0
  102. package/src/wirings/ai-agent/ai-agent-prepare.ts +4 -1
  103. package/src/wirings/ai-agent/ai-agent-registry.test.ts +230 -3
  104. package/src/wirings/ai-agent/ai-agent-runner.test.ts +625 -6
  105. package/src/wirings/ai-agent/ai-agent-runner.ts +65 -50
  106. package/src/wirings/ai-agent/ai-agent-stream.test.ts +544 -5
  107. package/src/wirings/ai-agent/ai-agent-stream.ts +71 -69
  108. package/src/wirings/ai-agent/ai-agent.types.ts +24 -0
  109. package/src/wirings/channel/channel-handler.test.ts +272 -0
  110. package/src/wirings/channel/channel-handler.ts +1 -1
  111. package/src/wirings/channel/channel-middleware-runner.test.ts +163 -0
  112. package/src/wirings/channel/channel-store.ts +19 -0
  113. package/src/wirings/channel/channel.types.ts +4 -0
  114. package/src/wirings/channel/local/local-channel-runner.test.ts +63 -0
  115. package/src/wirings/channel/local/local-channel-runner.ts +41 -5
  116. package/src/wirings/channel/local/local-eventhub-service.ts +3 -3
  117. package/src/wirings/channel/pikku-abstract-channel-handler.ts +9 -2
  118. package/src/wirings/channel/serverless/serverless-channel-runner.ts +23 -5
  119. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +7 -2
  120. package/src/wirings/cli/cli-runner.test.ts +255 -2
  121. package/src/wirings/cli/cli-runner.ts +31 -10
  122. package/src/wirings/cli/cli.types.ts +4 -8
  123. package/src/wirings/cli/command-parser.test.ts +83 -0
  124. package/src/wirings/cli/command-parser.ts +23 -4
  125. package/src/wirings/http/http-runner.test.ts +296 -1
  126. package/src/wirings/http/http-runner.ts +87 -57
  127. package/src/wirings/http/http.types.ts +11 -26
  128. package/src/wirings/http/pikku-fetch-http-request.ts +8 -4
  129. package/src/wirings/http/pikku-fetch-http-response.test.ts +41 -0
  130. package/src/wirings/http/web-request.test.ts +115 -0
  131. package/src/wirings/http/web-request.ts +43 -5
  132. package/src/wirings/mcp/mcp-runner.test.ts +367 -0
  133. package/src/wirings/queue/queue.types.ts +2 -5
  134. package/src/wirings/rpc/rpc-runner.test.ts +511 -0
  135. package/src/wirings/rpc/rpc-runner.ts +60 -21
  136. package/src/wirings/rpc/wire-addon.test.ts +57 -0
  137. package/src/wirings/workflow/graph/graph-node.ts +12 -0
  138. package/src/wirings/workflow/graph/graph-runner.test.ts +98 -0
  139. package/src/wirings/workflow/graph/graph-runner.ts +28 -10
  140. package/src/wirings/workflow/graph/workflow-graph.types.ts +4 -0
  141. package/src/wirings/workflow/index.ts +1 -0
  142. package/src/wirings/workflow/pikku-workflow-service.test.ts +19 -2
  143. package/src/wirings/workflow/pikku-workflow-service.ts +370 -71
  144. package/src/wirings/workflow/workflow-queue-workers.test.ts +131 -0
  145. package/src/wirings/workflow/workflow.types.ts +68 -5
  146. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,80 @@
1
+ ## 0.12.21
2
+
3
+ ### Patch Changes
4
+
5
+ - 9060165: Agents now declare their model directly as `<provider>/<model>` (e.g. `openai/gpt-4o`). The `models`, `agentDefaults`, and `agentOverrides` config blocks have been removed.
6
+
7
+ **Migration:** replace any bare `model: 'alias'` values with the full provider-qualified form and remove those blocks from `pikku.config.json`.
8
+
9
+ - 9060165: WebSocket channels now expose `setState`, `getState`, and `clearState` — channel state and session lifecycle are managed independently.
10
+ - 9060165: Workflow steps now support per-step `retries` and `retryDelay` configuration. Cloudflare deployments gain Workflow Durable Object bindings for graph-DSL workflows on Workers-for-Platforms, and the deploy bundle now boots cleanly on the Cloudflare Workers runtime.
11
+
12
+ ## 0.12.20
13
+
14
+ ### Patch Changes
15
+
16
+ - 18acebe: feat(core): scope bare `rpc.invoke()` calls to the caller's addon package
17
+
18
+ Addon functions calling `rpc.invoke('foo')` (bare, no colon) previously only
19
+ resolved against root RPC meta and threw `RPCNotFoundError` for the addon's
20
+ own functions, forcing authors to prefix every call with their consumer-facing
21
+ namespace (`'cli:foo'`) — which couples the addon to its caller's `wireAddon({ name })`.
22
+
23
+ `ContextAwareRPCService` now carries an optional `packageName` passed through
24
+ from `runPikkuFunc` via `getContextRPCService`. For bare names from inside an
25
+ addon, resolution first checks the caller's package function meta, then falls
26
+ back to root. Applies to both `rpc.invoke()` and `rpc.rpcWithWire()`. Explicit
27
+ namespaced calls (`'stripe:createCharge'`) and root-namespace calls are unchanged.
28
+
29
+ - 66d1b4f: feat(content)!: bucket-aware ContentService with typed object args
30
+
31
+ BREAKING CHANGE: All `ContentService` methods now take object args with a
32
+ required `bucket` field. The interface is generic over `TBucket extends string`
33
+ so callers can constrain bucket names to a typed union.
34
+
35
+ Migration:
36
+
37
+ ```ts
38
+ // Before
39
+ content.getUploadURL(fileKey, contentType)
40
+ content.signContentKey(key, expiresAt)
41
+ content.writeFile(assetKey, stream)
42
+ content.readFile(assetKey)
43
+ content.deleteFile(assetKey)
44
+
45
+ // After
46
+ content.getUploadURL({ bucket, fileKey, contentType })
47
+ content.signContentKey({ bucket, contentKey, dateLessThan: expiresAt })
48
+ content.writeFile({ bucket, key, stream })
49
+ content.readFile({ bucket, key })
50
+ content.deleteFile({ bucket, key })
51
+ ```
52
+
53
+ - New exported types: `SignContentKeyArgs`, `SignURLArgs`, `GetUploadURLArgs`,
54
+ `UploadURLResult`, `BucketKeyArgs`, `WriteFileArgs`, `CopyFileArgs`.
55
+ - `LocalContent` stores objects under `<base>/<bucket>/<key>`.
56
+ - `S3Content` and `B2Content` treat the logical bucket as a key prefix within
57
+ the configured underlying storage bucket.
58
+ - `workflow-screenshot` addon takes `bucket?` / `key?` input; default bucket
59
+ resolved from `PIKKU_WORKFLOW_SCREENSHOT_BUCKET` variable, no hardcoded
60
+ fallback.
61
+
62
+ - 3e35b99: feat(core): scope bare workflow names to the caller's addon package
63
+
64
+ Parallel to the RPC scoping fix for addon functions. Addon code calling
65
+ `services.workflowService.runToCompletion('myWorkflow', ...)` (bare name,
66
+ no colon) previously missed workflows registered under the addon's package
67
+ scope and threw `WorkflowNotFoundError`, forcing authors to hard-code
68
+ the consumer-facing namespace (`'cli:myWorkflow'`) — which couples the
69
+ addon to its caller's `wireAddon({ name })`.
70
+
71
+ `getOrCreatePackageSingletonServices` in the function-runner now wraps
72
+ the package's `workflowService` with a Proxy that auto-prefixes bare
73
+ workflow names on `startWorkflow` / `runToCompletion` with the addon's
74
+ consumer-defined namespace (looked up from `pikkuState(null, 'addons',
75
+ 'packages')`). Explicit `'ns:name'` calls and root-namespace workflows
76
+ are unchanged.
77
+
1
78
  ## 0.12.19
2
79
 
3
80
  ### Patch Changes
@@ -1,6 +1,8 @@
1
1
  import { watch } from 'node:fs';
2
2
  import { stat, readFile } from 'node:fs/promises';
3
3
  import { join, resolve, relative } from 'node:path';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { register } from 'tsx/esm/api';
4
6
  import { pikkuState } from '../pikku-state.js';
5
7
  import { addFunction } from '../function/function-runner.js';
6
8
  import { clearMiddlewareCache } from '../middleware-runner.js';
@@ -35,8 +37,19 @@ const findCompiledFile = async (tsFile, srcDir, pikkuDir) => {
35
37
  // Use data: URLs to import modules. This bypasses TypeScript loaders
36
38
  // (e.g. tsx) that intercept file:// imports and break dynamic ESM loading.
37
39
  // Each import gets unique content so there's no module cache to worry about.
38
- const reimportModule = async (filePath) => {
40
+ let tsxRegistered = false;
41
+ const ensureTsxRegistered = () => {
42
+ if (tsxRegistered)
43
+ return;
44
+ register();
45
+ tsxRegistered = true;
46
+ };
47
+ const reimportModule = async (filePath, useTsx = false) => {
39
48
  try {
49
+ if (useTsx) {
50
+ ensureTsxRegistered();
51
+ return await import(`${pathToFileURL(resolve(filePath)).href}?t=${Date.now()}`);
52
+ }
40
53
  const content = await readFile(resolve(filePath), 'utf-8');
41
54
  const dataUrl = 'data:text/javascript;base64,' + Buffer.from(content).toString('base64');
42
55
  return await import(dataUrl);
@@ -64,13 +77,14 @@ export async function pikkuDevReloader(options) {
64
77
  if (!srcDir)
65
78
  return;
66
79
  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;
80
+ const importPath = compiledFile ?? changedTsFile;
81
+ const usedTsxFallback = !compiledFile;
82
+ if (usedTsxFallback) {
83
+ logger.debug(`Hot-reload using tsx fallback for: ${relative(process.cwd(), changedTsFile)}`);
70
84
  }
71
- const mod = await reimportModule(compiledFile);
85
+ const mod = await reimportModule(importPath, usedTsxFallback);
72
86
  if (!mod) {
73
- logger.error(`Failed to import: ${relative(process.cwd(), compiledFile)} (keeping old code)`);
87
+ logger.error(`Failed to import: ${relative(process.cwd(), importPath)} (keeping old code)`);
74
88
  return;
75
89
  }
76
90
  let schemasChanged = false;
@@ -22,7 +22,7 @@ export interface ErrorDetails {
22
22
  * @param error - The error to add.
23
23
  * @param details - The details of the error.
24
24
  */
25
- export declare const addError: (error: any, { status, message }: ErrorDetails) => void;
25
+ export declare const addError: (error: any, { status, message, mcpCode }: ErrorDetails) => void;
26
26
  /**
27
27
  * Adds multiple errors to the API errors map.
28
28
  * @param errors - An array of errors and their details.
@@ -18,8 +18,8 @@ export class PikkuError extends Error {
18
18
  * @param error - The error to add.
19
19
  * @param details - The details of the error.
20
20
  */
21
- export const addError = (error, { status, message }) => {
22
- pikkuState(null, 'misc', 'errors').set(error, { status, message });
21
+ export const addError = (error, { status, message, mcpCode }) => {
22
+ pikkuState(null, 'misc', 'errors').set(error, mcpCode === undefined ? { status, message } : { status, message, mcpCode });
23
23
  };
24
24
  /**
25
25
  * Adds multiple errors to the API errors map.
@@ -37,6 +37,51 @@ async function resolveSession(wire, singletonServices, sessionService) {
37
37
  * @param parentServices - The parent/caller's singleton services (used as base)
38
38
  * @returns The package's singleton services
39
39
  */
40
+ /**
41
+ * Find the consumer-defined namespace (from wireAddon) for a given addon package.
42
+ * Returns null if the package isn't registered as an addon.
43
+ */
44
+ const findAddonNamespaceForPackage = (packageName) => {
45
+ const addons = pikkuState(null, 'addons', 'packages');
46
+ if (!addons)
47
+ return null;
48
+ for (const [namespace, cfg] of addons.entries()) {
49
+ if (cfg?.package === packageName)
50
+ return namespace;
51
+ }
52
+ return null;
53
+ };
54
+ /**
55
+ * Wrap a workflow service so that bare workflow names passed from inside an
56
+ * addon function are auto-prefixed with the addon's consumer-facing namespace.
57
+ * Without this, `runToCompletion('myWorkflow')` from inside an addon misses
58
+ * the workflow registered under the addon's package scope and throws
59
+ * WorkflowNotFoundError — forcing addons to hardcode their consumer-defined
60
+ * namespace, which couples the addon to its caller.
61
+ *
62
+ * Explicit `'ns:name'` and bare names that already exist in root meta are
63
+ * unaffected; only bare names that would otherwise miss resolution get
64
+ * prefixed.
65
+ */
66
+ const wrapWorkflowServiceForPackage = (service, packageName) => {
67
+ return new Proxy(service, {
68
+ get(target, prop, receiver) {
69
+ if (prop === 'startWorkflow' || prop === 'runToCompletion') {
70
+ const original = Reflect.get(target, prop, receiver);
71
+ return function (name, ...rest) {
72
+ if (typeof name === 'string' && !name.includes(':')) {
73
+ const namespace = findAddonNamespaceForPackage(packageName);
74
+ if (namespace) {
75
+ name = `${namespace}:${name}`;
76
+ }
77
+ }
78
+ return original.call(this, name, ...rest);
79
+ };
80
+ }
81
+ return Reflect.get(target, prop, receiver);
82
+ },
83
+ });
84
+ };
40
85
  const getOrCreatePackageSingletonServices = async (packageName, parentServices) => {
41
86
  // Check if we already have cached singleton services for this package
42
87
  const cachedServices = pikkuState(packageName, 'package', 'singletonServices');
@@ -56,6 +101,12 @@ const getOrCreatePackageSingletonServices = async (packageName, parentServices)
56
101
  }
57
102
  // Create singleton services for the package, passing parent services as existing
58
103
  const packageServices = await factories.createSingletonServices(config, parentServices);
104
+ // Wrap workflowService so that bare names used inside the addon's functions
105
+ // resolve to workflows registered under the addon's package scope.
106
+ if (packageServices.workflowService &&
107
+ typeof packageServices.workflowService === 'object') {
108
+ packageServices.workflowService = wrapWorkflowServiceForPackage(packageServices.workflowService, packageName);
109
+ }
59
110
  // Cache the services
60
111
  pikkuState(packageName, 'package', 'singletonServices', packageServices);
61
112
  return packageServices;
@@ -223,11 +274,10 @@ export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServic
223
274
  const services = wireServices && Object.keys(wireServices).length > 0
224
275
  ? { ...resolvedSingletonServices, ...wireServices }
225
276
  : resolvedSingletonServices;
277
+ const callerPackageName = packageName;
226
278
  Object.defineProperty(resolvedWire, 'rpc', {
227
279
  get() {
228
- const rpc = rpcService.getContextRPCService(services, resolvedWire, {
229
- sessionService,
230
- });
280
+ const rpc = rpcService.getContextRPCService(services, resolvedWire, { sessionService }, 0, callerPackageName);
231
281
  Object.defineProperty(resolvedWire, 'rpc', {
232
282
  value: rpc,
233
283
  writable: true,
@@ -135,10 +135,11 @@ export type CorePikkuAuthConfig<Services extends CoreSingletonServices = CoreSer
135
135
  export declare const pikkuAuth: <Services extends CoreSingletonServices = CoreServices, Session extends CoreUserSession = CoreUserSession>(auth: CorePikkuAuth<Services, Session> | CorePikkuAuthConfig<Services, Session>) => CorePikkuPermission<any, Services, any>;
136
136
  export type CorePermissionGroup<PikkuPermission = CorePikkuPermission<any>> = Record<string, PikkuPermission | PikkuPermission[]> | undefined;
137
137
  export type CorePikkuFunctionConfig<PikkuFunction extends CorePikkuFunction<any, any, any, any, any> | CorePikkuFunctionSessionless<any, any, any, any, any>, PikkuPermission extends CorePikkuPermission<any, any, any> = CorePikkuPermission<any>, PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<any, any>, InputSchema extends StandardSchemaV1 | undefined = undefined, OutputSchema extends StandardSchemaV1 | undefined = undefined> = {
138
- /** Optional human-readable title for the function */
138
+ /** Short human-readable name (e.g. "Create Todo") */
139
139
  title?: string;
140
- /** Optional description of what the function does */
140
+ /** Longer-form description of what the function does */
141
141
  description?: string;
142
+ /** Explicit logical name override; lets multiple exports share a versioned base */
142
143
  override?: string;
143
144
  version?: number;
144
145
  tags?: string[];
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ export type { QueueService } from './wirings/queue/queue.types.js';
24
24
  export type { JWTService } from './services/jwt-service.js';
25
25
  export type { SecretService } from './services/secret-service.js';
26
26
  export type { VariablesService } from './services/variables-service.js';
27
- export type { ContentService } from './services/content-service.js';
27
+ export type { ContentService, SignContentKeyArgs, SignURLArgs, GetUploadURLArgs, UploadURLResult, BucketKeyArgs, WriteFileArgs, CopyFileArgs, } from './services/content-service.js';
28
28
  export type { DeploymentService } from './services/deployment-service.js';
29
29
  export type { WorkflowService } from './services/workflow-service.js';
30
30
  export type { GatewayService } from './services/gateway-service.js';
@@ -40,8 +40,8 @@ export { createGraph } from './wirings/workflow/graph/graph-node.js';
40
40
  export { wireAddon } from './wirings/rpc/wire-addon.js';
41
41
  export type { WireAddonConfig } from './wirings/rpc/wire-addon.js';
42
42
  export type { PikkuPackageState } from './types/state.types.js';
43
- export { runMiddleware, addMiddleware } from './middleware-runner.js';
44
- export { addPermission, checkAuthPermissions } from './permissions.js';
43
+ export { runMiddleware, addTagMiddleware, addTagMiddleware as addMiddleware, addGlobalMiddleware, } from './middleware-runner.js';
44
+ export { addTagPermission, addTagPermission as addPermission, addGlobalPermission, checkAuthPermissions, } from './permissions.js';
45
45
  export { isSerializable, stopSingletonServices } from './utils.js';
46
46
  export { getSingletonServices, getCreateWireServices } from './pikku-state.js';
47
47
  export { type ScheduledTaskInfo, type ScheduledTaskSummary, } from './services/scheduler-service.js';
package/dist/index.js CHANGED
@@ -13,8 +13,8 @@ export { runScheduledTask } from './wirings/scheduler/scheduler-runner.js';
13
13
  export { NotFoundError } from './errors/errors.js';
14
14
  export { createGraph } from './wirings/workflow/graph/graph-node.js';
15
15
  export { wireAddon } from './wirings/rpc/wire-addon.js';
16
- export { runMiddleware, addMiddleware } from './middleware-runner.js';
17
- export { addPermission, checkAuthPermissions } from './permissions.js';
16
+ export { runMiddleware, addTagMiddleware, addTagMiddleware as addMiddleware, addGlobalMiddleware, } from './middleware-runner.js';
17
+ export { addTagPermission, addTagPermission as addPermission, addGlobalPermission, checkAuthPermissions, } from './permissions.js';
18
18
  export { isSerializable, stopSingletonServices } from './utils.js';
19
19
  export { getSingletonServices, getCreateWireServices } from './pikku-state.js';
20
20
  export { SchedulerService } from './services/scheduler-service.js';
@@ -4,5 +4,5 @@ export { authBearer } from './auth-bearer.js';
4
4
  export { pikkuRemoteAuthMiddleware } from './remote-auth.js';
5
5
  export { cors } from './cors.js';
6
6
  export { telemetryOuter, telemetryInner } from './telemetry.js';
7
- export { addMiddleware, runMiddleware } from '../middleware-runner.js';
8
- export { addPermission } from '../permissions.js';
7
+ export { addTagMiddleware, addTagMiddleware as addMiddleware, addGlobalMiddleware, runMiddleware, } from '../middleware-runner.js';
8
+ export { addTagPermission, addTagPermission as addPermission, addGlobalPermission, } from '../permissions.js';
@@ -4,5 +4,5 @@ export { authBearer } from './auth-bearer.js';
4
4
  export { pikkuRemoteAuthMiddleware } from './remote-auth.js';
5
5
  export { cors } from './cors.js';
6
6
  export { telemetryOuter, telemetryInner } from './telemetry.js';
7
- export { addMiddleware, runMiddleware } from '../middleware-runner.js';
8
- export { addPermission } from '../permissions.js';
7
+ export { addTagMiddleware, addTagMiddleware as addMiddleware, addGlobalMiddleware, runMiddleware, } from '../middleware-runner.js';
8
+ export { addTagPermission, addTagPermission as addPermission, addGlobalPermission, } from '../permissions.js';
@@ -11,7 +11,7 @@
11
11
  * @example
12
12
  * ```typescript
13
13
  * import { telemetryOuter } from '@pikku/core/middleware'
14
- * addMiddleware('myTag', [telemetryOuter()])
14
+ * addTagMiddleware('myTag', [telemetryOuter()])
15
15
  * ```
16
16
  */
17
17
  export declare const telemetryOuter: import("../types/core.types.js").CorePikkuMiddlewareFactory<void | {
@@ -34,7 +34,7 @@ export declare const telemetryOuter: import("../types/core.types.js").CorePikkuM
34
34
  * @example
35
35
  * ```typescript
36
36
  * import { telemetryInner } from '@pikku/core/middleware'
37
- * addMiddleware('myTag', [telemetryInner()])
37
+ * addTagMiddleware('myTag', [telemetryInner()])
38
38
  * ```
39
39
  */
40
40
  export declare const telemetryInner: import("../types/core.types.js").CorePikkuMiddlewareFactory<void | {
@@ -12,7 +12,7 @@ import { pikkuMiddleware, pikkuMiddlewareFactory } from '../types/core.types.js'
12
12
  * @example
13
13
  * ```typescript
14
14
  * import { telemetryOuter } from '@pikku/core/middleware'
15
- * addMiddleware('myTag', [telemetryOuter()])
15
+ * addTagMiddleware('myTag', [telemetryOuter()])
16
16
  * ```
17
17
  */
18
18
  export const telemetryOuter = pikkuMiddlewareFactory(({ environmentId, orgId } = {}) => {
@@ -67,7 +67,7 @@ export const telemetryOuter = pikkuMiddlewareFactory(({ environmentId, orgId } =
67
67
  * @example
68
68
  * ```typescript
69
69
  * import { telemetryInner } from '@pikku/core/middleware'
70
- * addMiddleware('myTag', [telemetryInner()])
70
+ * addTagMiddleware('myTag', [telemetryInner()])
71
71
  * ```
72
72
  */
73
73
  export const telemetryInner = pikkuMiddlewareFactory(({ environmentId, orgId } = {}) => {
@@ -18,39 +18,27 @@ import type { CoreSingletonServices, PikkuWire, CorePikkuMiddleware, CorePikkuMi
18
18
  */
19
19
  export declare const runMiddleware: <Middleware extends CorePikkuMiddleware>(services: CoreSingletonServices, wire: PikkuWire, middlewares: readonly Middleware[], main?: () => Promise<unknown>) => Promise<unknown>;
20
20
  /**
21
- * Registers global middleware for a specific tag.
21
+ * Registers tag-scoped middleware. Applies to any wiring (HTTP, Channel,
22
+ * Queue, Scheduler, MCP, CLI, Workflow, Agent) that carries the matching tag.
22
23
  *
23
- * This function registers middleware at runtime that will be applied to
24
- * any wiring (HTTP, Channel, Queue, Scheduler, MCP, CLI) that includes the matching tag.
24
+ * For tree-shaking, wrap in a factory:
25
+ * `export const x = () => addTagMiddleware('tag', [...])`
25
26
  *
26
- * For tree-shaking benefits, wrap in a factory function:
27
- * `export const x = () => addMiddleware('tag', [...])`
28
- *
29
- * Accepts an array that can contain:
30
- * - Direct middleware functions (CorePikkuMiddleware)
31
- * - Factory middleware functions (CorePikkuMiddlewareFactory)
32
- *
33
- * @template PikkuMiddleware The middleware type.
34
- * @param {string} tag - The tag that the middleware should apply to.
35
- * @param {CorePikkuMiddlewareGroup} middleware - Array of middleware for this tag.
27
+ * @example
28
+ * export const adminMiddleware = () => addTagMiddleware('admin', [authMiddleware])
29
+ */
30
+ export declare const addTagMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(tag: string, middleware: CorePikkuMiddlewareGroup, packageName?: string | null) => CorePikkuMiddlewareGroup;
31
+ /**
32
+ * Registers wire-agnostic global middleware. Runs at the top of every
33
+ * wiring's middleware chain — before wire-, tag-, and function-level
34
+ * entries across HTTP, Channel, Queue, Scheduler, MCP, CLI, Workflow, Agent.
36
35
  *
37
- * @returns {CorePikkuMiddlewareGroup} The middleware array (for chaining/wrapping).
36
+ * Resolution order: global wire tag → function.
38
37
  *
39
38
  * @example
40
- * ```typescript
41
- * // Recommended: tree-shakeable
42
- * export const adminMiddleware = () => addMiddleware('admin', [
43
- * authMiddleware,
44
- * loggingMiddleware({ level: 'info' })
45
- * ])
46
- *
47
- * // Also works: no tree-shaking
48
- * export const apiMiddleware = addMiddleware('api', [
49
- * rateLimitMiddleware
50
- * ])
51
- * ```
39
+ * addGlobalMiddleware([telemetryMiddleware])
52
40
  */
53
- export declare const addMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(tag: string, middleware: CorePikkuMiddlewareGroup, packageName?: string | null) => CorePikkuMiddlewareGroup;
41
+ export declare const addGlobalMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(middleware: CorePikkuMiddlewareGroup, packageName?: string | null) => CorePikkuMiddlewareGroup;
54
42
  export declare const clearMiddlewareCache: () => void;
55
43
  /**
56
44
  * Combines wiring-specific middleware with function-level middleware.
@@ -58,43 +58,35 @@ const isSortedByPriority = (middlewares) => {
58
58
  return true;
59
59
  };
60
60
  /**
61
- * Registers global middleware for a specific tag.
61
+ * Registers tag-scoped middleware. Applies to any wiring (HTTP, Channel,
62
+ * Queue, Scheduler, MCP, CLI, Workflow, Agent) that carries the matching tag.
62
63
  *
63
- * This function registers middleware at runtime that will be applied to
64
- * any wiring (HTTP, Channel, Queue, Scheduler, MCP, CLI) that includes the matching tag.
65
- *
66
- * For tree-shaking benefits, wrap in a factory function:
67
- * `export const x = () => addMiddleware('tag', [...])`
68
- *
69
- * Accepts an array that can contain:
70
- * - Direct middleware functions (CorePikkuMiddleware)
71
- * - Factory middleware functions (CorePikkuMiddlewareFactory)
72
- *
73
- * @template PikkuMiddleware The middleware type.
74
- * @param {string} tag - The tag that the middleware should apply to.
75
- * @param {CorePikkuMiddlewareGroup} middleware - Array of middleware for this tag.
76
- *
77
- * @returns {CorePikkuMiddlewareGroup} The middleware array (for chaining/wrapping).
64
+ * For tree-shaking, wrap in a factory:
65
+ * `export const x = () => addTagMiddleware('tag', [...])`
78
66
  *
79
67
  * @example
80
- * ```typescript
81
- * // Recommended: tree-shakeable
82
- * export const adminMiddleware = () => addMiddleware('admin', [
83
- * authMiddleware,
84
- * loggingMiddleware({ level: 'info' })
85
- * ])
86
- *
87
- * // Also works: no tree-shaking
88
- * export const apiMiddleware = addMiddleware('api', [
89
- * rateLimitMiddleware
90
- * ])
91
- * ```
68
+ * export const adminMiddleware = () => addTagMiddleware('admin', [authMiddleware])
92
69
  */
93
- export const addMiddleware = (tag, middleware, packageName = null) => {
70
+ export const addTagMiddleware = (tag, middleware, packageName = null) => {
94
71
  const tagGroups = pikkuState(packageName, 'middleware', 'tagGroup');
95
72
  tagGroups[tag] = middleware;
96
73
  return middleware;
97
74
  };
75
+ /**
76
+ * Registers wire-agnostic global middleware. Runs at the top of every
77
+ * wiring's middleware chain — before wire-, tag-, and function-level
78
+ * entries — across HTTP, Channel, Queue, Scheduler, MCP, CLI, Workflow, Agent.
79
+ *
80
+ * Resolution order: global → wire → tag → function.
81
+ *
82
+ * @example
83
+ * addGlobalMiddleware([telemetryMiddleware])
84
+ */
85
+ export const addGlobalMiddleware = (middleware, packageName = null) => {
86
+ const state = pikkuState(packageName, 'middleware', 'global');
87
+ state.push(...middleware);
88
+ return middleware;
89
+ };
98
90
  /**
99
91
  * Retrieves a registered middleware function by its name.
100
92
  *
@@ -156,7 +148,10 @@ export const combineMiddleware = (wireType, uid, { wireInheritedMiddleware, wire
156
148
  return middlewareCache[wireType][uid];
157
149
  }
158
150
  const resolved = [];
159
- // 1. Resolve wire inherited middleware (HTTP + tag groups + individual wire middleware)
151
+ const globals = pikkuState(packageName, 'middleware', 'global');
152
+ if (globals && globals.length > 0) {
153
+ resolved.push(...globals);
154
+ }
160
155
  if (wireInheritedMiddleware) {
161
156
  for (const meta of wireInheritedMiddleware) {
162
157
  if (meta.type === 'http') {
@@ -1,34 +1,27 @@
1
1
  import type { CoreServices, CoreUserSession, PikkuWiringTypes, PermissionMetadata, PikkuWire } from './types/core.types.js';
2
2
  import type { CorePermissionGroup, CorePikkuPermission } from './function/functions.types.js';
3
3
  /**
4
- * Adds global permissions for a specific tag.
4
+ * Registers tag-scoped permissions. Applies to any wiring that carries the
5
+ * matching tag.
5
6
  *
6
- * This function allows you to register permissions that will be applied to
7
- * any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
7
+ * For tree-shaking, wrap in a factory:
8
+ * `export const x = () => addTagPermission('tag', [...])`
8
9
  *
9
- * For tree-shaking benefits, wrap in a factory function:
10
- * `export const x = () => addPermission('tag', [...])`
11
- *
12
- * @param {string} tag - The tag that the permissions should apply to.
13
- * @param {any[]} permissions - The permissions array to apply for the specified tag.
10
+ * @example
11
+ * export const adminPermissions = () => addTagPermission('admin', [adminPermission])
12
+ */
13
+ export declare const addTagPermission: (tag: string, permissions: CorePermissionGroup | CorePikkuPermission[], packageName?: string | null) => CorePermissionGroup | CorePikkuPermission[];
14
+ /**
15
+ * Registers wire-agnostic global permissions. Runs at the top of every
16
+ * wiring's permission resolution — before wire-, tag-, and function-level
17
+ * entries — across all wire types.
14
18
  *
15
- * @returns {CorePermissionGroup | CorePikkuPermission[]} The permissions (for chaining/wrapping).
19
+ * Resolution order: global wire tag → function.
16
20
  *
17
21
  * @example
18
- * ```typescript
19
- * // Recommended: tree-shakeable
20
- * export const adminPermissions = () => addPermission('admin', [
21
- * adminPermission,
22
- * rolePermission({ role: 'admin' })
23
- * ])
24
- *
25
- * // Also works: no tree-shaking
26
- * export const apiPermissions = addPermission('api', [
27
- * readPermission
28
- * ])
29
- * ```
22
+ * addGlobalPermission([signedInUser])
30
23
  */
31
- export declare const addPermission: (tag: string, permissions: CorePermissionGroup | CorePikkuPermission[], packageName?: string | null) => CorePermissionGroup | CorePikkuPermission[];
24
+ export declare const addGlobalPermission: (permissions: CorePermissionGroup | CorePikkuPermission[], packageName?: string | null) => CorePermissionGroup | CorePikkuPermission[];
32
25
  export declare const clearPermissionsCache: () => void;
33
26
  /**
34
27
  * Runs permission checks using combined permissions from all sources.
@@ -53,34 +53,16 @@ const getPermissionByName = (name) => {
53
53
  return undefined;
54
54
  };
55
55
  /**
56
- * Adds global permissions for a specific tag.
56
+ * Registers tag-scoped permissions. Applies to any wiring that carries the
57
+ * matching tag.
57
58
  *
58
- * This function allows you to register permissions that will be applied to
59
- * any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
60
- *
61
- * For tree-shaking benefits, wrap in a factory function:
62
- * `export const x = () => addPermission('tag', [...])`
63
- *
64
- * @param {string} tag - The tag that the permissions should apply to.
65
- * @param {any[]} permissions - The permissions array to apply for the specified tag.
66
- *
67
- * @returns {CorePermissionGroup | CorePikkuPermission[]} The permissions (for chaining/wrapping).
59
+ * For tree-shaking, wrap in a factory:
60
+ * `export const x = () => addTagPermission('tag', [...])`
68
61
  *
69
62
  * @example
70
- * ```typescript
71
- * // Recommended: tree-shakeable
72
- * export const adminPermissions = () => addPermission('admin', [
73
- * adminPermission,
74
- * rolePermission({ role: 'admin' })
75
- * ])
76
- *
77
- * // Also works: no tree-shaking
78
- * export const apiPermissions = addPermission('api', [
79
- * readPermission
80
- * ])
81
- * ```
63
+ * export const adminPermissions = () => addTagPermission('admin', [adminPermission])
82
64
  */
83
- export const addPermission = (tag, permissions, packageName = null) => {
65
+ export const addTagPermission = (tag, permissions, packageName = null) => {
84
66
  const tagGroups = pikkuState(packageName, 'permissions', 'tagGroup');
85
67
  if (tagGroups[tag]) {
86
68
  throw new Error(`Permissions for tag '${tag}' already exist. Use a different tag or remove the existing permissions first.`);
@@ -88,6 +70,26 @@ export const addPermission = (tag, permissions, packageName = null) => {
88
70
  tagGroups[tag] = permissions;
89
71
  return permissions;
90
72
  };
73
+ /**
74
+ * Registers wire-agnostic global permissions. Runs at the top of every
75
+ * wiring's permission resolution — before wire-, tag-, and function-level
76
+ * entries — across all wire types.
77
+ *
78
+ * Resolution order: global → wire → tag → function.
79
+ *
80
+ * @example
81
+ * addGlobalPermission([signedInUser])
82
+ */
83
+ export const addGlobalPermission = (permissions, packageName = null) => {
84
+ const state = pikkuState(packageName, 'permissions', 'global');
85
+ if (Array.isArray(permissions)) {
86
+ state.push(...permissions);
87
+ }
88
+ else {
89
+ state.push(permissions);
90
+ }
91
+ return permissions;
92
+ };
91
93
  const combinedPermissionsCache = {
92
94
  http: {},
93
95
  rpc: {},
@@ -135,7 +137,10 @@ const combinePermissions = (wireType, uid, { wireInheritedPermissions, wirePermi
135
137
  return combinedPermissionsCache[wireType][uid];
136
138
  }
137
139
  const resolved = [];
138
- // 1. Resolve wire inherited permissions (HTTP + tag groups + individual wire permissions)
140
+ const globals = pikkuState(packageName, 'permissions', 'global');
141
+ if (globals && globals.length > 0) {
142
+ resolved.push(...globals);
143
+ }
139
144
  if (wireInheritedPermissions) {
140
145
  for (const meta of wireInheritedPermissions) {
141
146
  if (meta.type === 'http') {
@@ -13,7 +13,7 @@ export class PikkuRequest {
13
13
  * @returns A promise that resolves to an object containing the combined data.
14
14
  */
15
15
  async data() {
16
- if (!this.#data) {
16
+ if (this.#data === undefined) {
17
17
  throw new Error('Data not found');
18
18
  }
19
19
  return this.#data;
@@ -105,6 +105,7 @@ const createEmptyPackageState = () => ({
105
105
  middleware: {
106
106
  tagGroup: {},
107
107
  httpGroup: {},
108
+ global: [],
108
109
  },
109
110
  channelMiddleware: {
110
111
  tagGroup: {},
@@ -112,6 +113,7 @@ const createEmptyPackageState = () => ({
112
113
  permissions: {
113
114
  tagGroup: {},
114
115
  httpGroup: {},
116
+ global: [],
115
117
  },
116
118
  misc: {
117
119
  errors: new Map(),
@@ -120,9 +122,6 @@ const createEmptyPackageState = () => ({
120
122
  channelMiddleware: {},
121
123
  permissions: {},
122
124
  },
123
- models: {
124
- config: null,
125
- },
126
125
  package: {
127
126
  factories: null,
128
127
  singletonServices: null,
@@ -36,6 +36,7 @@ export type AIAgentStepResult = {
36
36
  outputTokens: number;
37
37
  };
38
38
  finishReason: 'stop' | 'tool-calls' | 'length' | 'error' | 'unknown';
39
+ reasoningContent?: string;
39
40
  };
40
41
  export interface AIAgentRunnerService {
41
42
  stream(params: AIAgentRunnerParams, channel: AIStreamChannel): Promise<AIAgentStepResult>;