@pikku/core 0.12.63 → 0.12.66

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 (225) hide show
  1. package/CHANGELOG.md +368 -0
  2. package/dist/dev/hot-reload.js +13 -55
  3. package/dist/dev/module-runner.d.ts +14 -0
  4. package/dist/dev/module-runner.js +59 -0
  5. package/dist/dev/reload-meta.d.ts +13 -0
  6. package/dist/dev/reload-meta.js +22 -0
  7. package/dist/errors/errors.d.ts +12 -0
  8. package/dist/errors/errors.js +19 -0
  9. package/dist/function/function-runner.d.ts +5 -5
  10. package/dist/function/function-runner.js +39 -111
  11. package/dist/function/functions.types.d.ts +17 -7
  12. package/dist/index.d.ts +5 -1
  13. package/dist/index.js +3 -1
  14. package/dist/middleware/auth-apikey.d.ts +1 -0
  15. package/dist/middleware/auth-bearer.d.ts +1 -0
  16. package/dist/middleware/auth-cookie.d.ts +1 -0
  17. package/dist/middleware/cors.d.ts +1 -0
  18. package/dist/middleware/index.d.ts +1 -1
  19. package/dist/middleware/index.js +1 -1
  20. package/dist/middleware/remote-auth.d.ts +1 -0
  21. package/dist/middleware/telemetry.d.ts +2 -0
  22. package/dist/middleware/timeout.d.ts +1 -0
  23. package/dist/permissions.d.ts +42 -27
  24. package/dist/permissions.js +101 -203
  25. package/dist/pikku-state.js +0 -2
  26. package/dist/scopes.d.ts +17 -0
  27. package/dist/scopes.js +59 -0
  28. package/dist/services/ai-embedding-service.d.ts +31 -0
  29. package/dist/services/ai-embedding-service.js +1 -0
  30. package/dist/services/credential-wire-service.d.ts +3 -1
  31. package/dist/services/credential-wire-service.js +10 -4
  32. package/dist/services/index.d.ts +4 -0
  33. package/dist/services/index.js +2 -0
  34. package/dist/services/meta-service.d.ts +15 -2
  35. package/dist/services/meta-service.js +51 -1
  36. package/dist/services/queue-webhook-service.d.ts +55 -0
  37. package/dist/services/queue-webhook-service.js +136 -0
  38. package/dist/services/scope-service.d.ts +67 -0
  39. package/dist/services/scope-service.js +1 -0
  40. package/dist/services/typed-secret-service.d.ts +8 -0
  41. package/dist/services/typed-secret-service.js +39 -4
  42. package/dist/services/webhook-service.d.ts +140 -0
  43. package/dist/services/webhook-service.js +44 -0
  44. package/dist/services/workflow-service.d.ts +7 -0
  45. package/dist/testing/service-tests.js +37 -0
  46. package/dist/types/core.types.d.ts +34 -6
  47. package/dist/types/state.types.d.ts +20 -2
  48. package/dist/utils/hmac.d.ts +16 -0
  49. package/dist/utils/hmac.js +26 -0
  50. package/dist/utils/safe-fetch.d.ts +51 -0
  51. package/dist/utils/safe-fetch.js +192 -0
  52. package/dist/wirings/ai-agent/ai-agent-agui.d.ts +84 -0
  53. package/dist/wirings/ai-agent/ai-agent-agui.js +305 -0
  54. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +105 -1
  55. package/dist/wirings/ai-agent/ai-agent-prepare.js +249 -11
  56. package/dist/wirings/ai-agent/ai-agent-runner.js +10 -1
  57. package/dist/wirings/ai-agent/ai-agent-stream.js +88 -16
  58. package/dist/wirings/ai-agent/ai-agent.types.d.ts +56 -2
  59. package/dist/wirings/ai-agent/index.d.ts +2 -1
  60. package/dist/wirings/ai-agent/index.js +2 -1
  61. package/dist/wirings/ai-agent/voice-input.d.ts +1 -0
  62. package/dist/wirings/ai-agent/voice-input.js +5 -41
  63. package/dist/wirings/ai-agent/voice-output.d.ts +1 -0
  64. package/dist/wirings/channel/channel-common.js +0 -1
  65. package/dist/wirings/channel/channel-handler.js +1 -4
  66. package/dist/wirings/channel/channel-middleware-runner.d.ts +12 -0
  67. package/dist/wirings/channel/channel-middleware-runner.js +34 -19
  68. package/dist/wirings/channel/channel.types.d.ts +2 -6
  69. package/dist/wirings/cli/cli-runner.js +3 -2
  70. package/dist/wirings/cli/cli.types.d.ts +1 -2
  71. package/dist/wirings/cli/command-parser.d.ts +2 -0
  72. package/dist/wirings/cli/command-parser.js +59 -2
  73. package/dist/wirings/credential/credential.types.d.ts +14 -0
  74. package/dist/wirings/credential/validate-credential-definitions.js +1 -0
  75. package/dist/wirings/gateway/gateway-runner.js +100 -50
  76. package/dist/wirings/gateway/gateway.types.d.ts +8 -5
  77. package/dist/wirings/http/http-routes.js +0 -3
  78. package/dist/wirings/http/http-runner.d.ts +9 -29
  79. package/dist/wirings/http/http-runner.js +9 -34
  80. package/dist/wirings/http/http.types.d.ts +4 -12
  81. package/dist/wirings/mcp/mcp-runner.js +0 -2
  82. package/dist/wirings/mcp/mcp.types.d.ts +5 -11
  83. package/dist/wirings/oauth2/index.d.ts +0 -3
  84. package/dist/wirings/oauth2/index.js +1 -2
  85. package/dist/wirings/rpc/addon-runner.d.ts +28 -0
  86. package/dist/wirings/rpc/addon-runner.js +173 -0
  87. package/dist/wirings/rpc/index.d.ts +5 -1
  88. package/dist/wirings/rpc/index.js +3 -1
  89. package/dist/wirings/rpc/remote-addon-auth.d.ts +26 -0
  90. package/dist/wirings/rpc/remote-addon-auth.js +43 -0
  91. package/dist/wirings/rpc/rpc-runner.d.ts +19 -0
  92. package/dist/wirings/rpc/rpc-runner.js +109 -3
  93. package/dist/wirings/rpc/rpc-types.d.ts +15 -1
  94. package/dist/wirings/rpc/wire-addon.js +9 -0
  95. package/dist/wirings/rpc/wire-remote-addon.d.ts +47 -0
  96. package/dist/wirings/rpc/wire-remote-addon.js +19 -0
  97. package/dist/wirings/scope/index.d.ts +3 -0
  98. package/dist/wirings/scope/index.js +2 -0
  99. package/dist/wirings/scope/scope.types.d.ts +39 -0
  100. package/dist/wirings/scope/scope.types.js +1 -0
  101. package/dist/wirings/scope/validate-scope-definitions.d.ts +16 -0
  102. package/dist/wirings/scope/validate-scope-definitions.js +76 -0
  103. package/dist/wirings/scope/wire-scope.d.ts +33 -0
  104. package/dist/wirings/scope/wire-scope.js +32 -0
  105. package/dist/wirings/secret/secret.types.d.ts +14 -0
  106. package/dist/wirings/secret/validate-secret-definitions.js +2 -0
  107. package/dist/wirings/variable/validate-variable-definitions.js +2 -0
  108. package/dist/wirings/variable/variable.types.d.ts +14 -0
  109. package/dist/wirings/workflow/dsl/index.d.ts +1 -1
  110. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +89 -7
  111. package/dist/wirings/workflow/graph/graph-node.d.ts +2 -0
  112. package/dist/wirings/workflow/graph/graph-runner.js +3 -0
  113. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +6 -0
  114. package/dist/wirings/workflow/graph/wire-workflow-graph.js +1 -0
  115. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +5 -0
  116. package/dist/wirings/workflow/index.d.ts +2 -2
  117. package/dist/wirings/workflow/index.js +1 -1
  118. package/dist/wirings/workflow/pikku-workflow-service.d.ts +59 -1
  119. package/dist/wirings/workflow/pikku-workflow-service.js +187 -0
  120. package/dist/wirings/workflow/workflow.types.d.ts +1 -3
  121. package/package.json +6 -1
  122. package/run-tests.sh +1 -0
  123. package/src/dev/hot-reload.ts +13 -68
  124. package/src/dev/module-runner.test.ts +169 -0
  125. package/src/dev/module-runner.ts +103 -0
  126. package/src/dev/reload-meta.test.ts +31 -2
  127. package/src/dev/reload-meta.ts +26 -0
  128. package/src/errors/errors.ts +24 -0
  129. package/src/function/function-runner.test.ts +276 -142
  130. package/src/function/function-runner.ts +52 -145
  131. package/src/function/functions.types.ts +28 -38
  132. package/src/index.ts +8 -6
  133. package/src/middleware/index.ts +1 -5
  134. package/src/permissions.test.ts +160 -373
  135. package/src/permissions.ts +137 -279
  136. package/src/pikku-state.ts +0 -2
  137. package/src/scopes.test.ts +167 -0
  138. package/src/scopes.ts +69 -0
  139. package/src/services/ai-embedding-service.ts +31 -0
  140. package/src/services/credential-wire-service.ts +10 -4
  141. package/src/services/index.ts +20 -0
  142. package/src/services/meta-service.ts +68 -3
  143. package/src/services/queue-webhook-service.test.ts +408 -0
  144. package/src/services/queue-webhook-service.ts +182 -0
  145. package/src/services/scope-service.ts +75 -0
  146. package/src/services/typed-secret-service.test.ts +35 -0
  147. package/src/services/typed-secret-service.ts +39 -4
  148. package/src/services/webhook-service.ts +180 -0
  149. package/src/services/workflow-service.ts +7 -0
  150. package/src/testing/service-tests.ts +49 -0
  151. package/src/types/core.types.ts +42 -14
  152. package/src/types/state.types.ts +23 -3
  153. package/src/utils/hmac.ts +27 -0
  154. package/src/utils/safe-fetch.test.ts +373 -0
  155. package/src/utils/safe-fetch.ts +213 -0
  156. package/src/wirings/ai-agent/ai-agent-agui.test.ts +1127 -0
  157. package/src/wirings/ai-agent/ai-agent-agui.ts +386 -0
  158. package/src/wirings/ai-agent/ai-agent-authorization.test.ts +204 -0
  159. package/src/wirings/ai-agent/ai-agent-prepare.test.ts +483 -0
  160. package/src/wirings/ai-agent/ai-agent-prepare.ts +324 -13
  161. package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +207 -0
  162. package/src/wirings/ai-agent/ai-agent-runner.test.ts +48 -1
  163. package/src/wirings/ai-agent/ai-agent-runner.ts +28 -0
  164. package/src/wirings/ai-agent/ai-agent-stream.test.ts +301 -10
  165. package/src/wirings/ai-agent/ai-agent-stream.ts +122 -16
  166. package/src/wirings/ai-agent/ai-agent.types.ts +57 -0
  167. package/src/wirings/ai-agent/index.ts +5 -0
  168. package/src/wirings/ai-agent/voice-input.test.ts +90 -0
  169. package/src/wirings/ai-agent/voice-input.ts +9 -40
  170. package/src/wirings/channel/channel-common.ts +0 -1
  171. package/src/wirings/channel/channel-handler.ts +1 -9
  172. package/src/wirings/channel/channel-middleware-runner.test.ts +28 -2
  173. package/src/wirings/channel/channel-middleware-runner.ts +39 -27
  174. package/src/wirings/channel/channel.types.ts +0 -6
  175. package/src/wirings/cli/cli-runner.ts +4 -2
  176. package/src/wirings/cli/cli.types.ts +0 -2
  177. package/src/wirings/cli/command-parser.test.ts +130 -0
  178. package/src/wirings/cli/command-parser.ts +80 -2
  179. package/src/wirings/credential/credential.types.ts +14 -0
  180. package/src/wirings/credential/validate-credential-definitions.ts +1 -0
  181. package/src/wirings/gateway/gateway-authorization.test.ts +444 -0
  182. package/src/wirings/gateway/gateway-runner.ts +114 -68
  183. package/src/wirings/gateway/gateway.types.ts +7 -9
  184. package/src/wirings/http/http-routes.ts +0 -3
  185. package/src/wirings/http/http-runner.test.ts +1 -11
  186. package/src/wirings/http/http-runner.ts +16 -39
  187. package/src/wirings/http/http.types.ts +7 -14
  188. package/src/wirings/mcp/mcp-runner.ts +0 -2
  189. package/src/wirings/mcp/mcp.types.ts +3 -11
  190. package/src/wirings/oauth2/index.ts +0 -3
  191. package/src/wirings/rpc/addon-runner.ts +233 -0
  192. package/src/wirings/rpc/index.ts +17 -1
  193. package/src/wirings/rpc/remote-addon-auth.ts +69 -0
  194. package/src/wirings/rpc/rpc-runner.test.ts +319 -16
  195. package/src/wirings/rpc/rpc-runner.ts +155 -3
  196. package/src/wirings/rpc/rpc-types.ts +16 -5
  197. package/src/wirings/rpc/wire-addon.test.ts +6 -3
  198. package/src/wirings/rpc/wire-addon.ts +9 -0
  199. package/src/wirings/rpc/wire-remote-addon.ts +57 -0
  200. package/src/wirings/scope/index.ts +14 -0
  201. package/src/wirings/scope/scope.test.ts +135 -0
  202. package/src/wirings/scope/scope.types.ts +44 -0
  203. package/src/wirings/scope/validate-scope-definitions.ts +110 -0
  204. package/src/wirings/scope/wire-scope.ts +34 -0
  205. package/src/wirings/secret/secret.types.ts +14 -0
  206. package/src/wirings/secret/validate-secret-definitions.ts +2 -0
  207. package/src/wirings/variable/validate-variable-definitions.ts +2 -0
  208. package/src/wirings/variable/variable.types.ts +14 -0
  209. package/src/wirings/workflow/dsl/index.ts +4 -0
  210. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +97 -6
  211. package/src/wirings/workflow/graph/graph-node.ts +2 -0
  212. package/src/wirings/workflow/graph/graph-runner.test.ts +58 -0
  213. package/src/wirings/workflow/graph/graph-runner.ts +3 -0
  214. package/src/wirings/workflow/graph/wire-workflow-graph.ts +7 -0
  215. package/src/wirings/workflow/graph/workflow-graph.types.ts +5 -0
  216. package/src/wirings/workflow/index.ts +5 -0
  217. package/src/wirings/workflow/pikku-workflow-service.test.ts +341 -0
  218. package/src/wirings/workflow/pikku-workflow-service.ts +272 -0
  219. package/src/wirings/workflow/workflow-on-error.test.ts +154 -0
  220. package/src/wirings/workflow/workflow-step-session.test.ts +21 -5
  221. package/src/wirings/workflow/workflow.types.ts +4 -2
  222. package/tsconfig.tsbuildinfo +1 -1
  223. package/src/wirings/oauth2/oauth2-client.test.ts +0 -930
  224. package/src/wirings/oauth2/oauth2-client.ts +0 -331
  225. package/src/wirings/oauth2/oauth2-routes.ts +0 -234
@@ -13,21 +13,16 @@ import type {
13
13
  PikkuWire,
14
14
  PikkuRawWire,
15
15
  MiddlewareMetadata,
16
- PermissionMetadata,
17
16
  CoreSingletonServices,
18
17
  CreateWireServices,
19
- CoreConfig,
20
18
  } from '../types/core.types.js'
21
19
  import type { CorePikkuChannelMiddleware } from '../wirings/channel/channel.types.js'
22
- import type {
23
- CorePermissionGroup,
24
- CorePikkuFunctionConfig,
25
- CorePikkuPermission,
26
- } from './functions.types.js'
20
+ import type { CorePikkuFunctionConfig } from './functions.types.js'
27
21
  import { parseVersionedId } from '../version.js'
28
22
  import type { SessionService } from '../services/user-session-service.js'
29
23
  import { PikkuSessionService } from '../services/user-session-service.js'
30
24
  import { ForbiddenError, ReadonlySessionError } from '../errors/errors.js'
25
+ import { verifyScopes } from '../scopes.js'
31
26
  import {
32
27
  PikkuCredentialWireService,
33
28
  createWireServicesCredentialWireProps,
@@ -39,6 +34,8 @@ import {
39
34
  type AuditLog,
40
35
  } from '../services/audit-service.js'
41
36
  import { rpcService } from '../wirings/rpc/rpc-runner.js'
37
+ import { getOrCreatePackageSingletonServices } from '../wirings/rpc/addon-runner.js'
38
+ import type { AddonInstance } from '../wirings/rpc/addon-runner.js'
42
39
  import { closeWireServices } from '../utils.js'
43
40
 
44
41
  async function resolveSession(
@@ -70,109 +67,6 @@ async function resolveSession(
70
67
  }
71
68
  }
72
69
 
73
- /**
74
- * Get or create singleton services for an addon package.
75
- * Services are cached in pikkuState to avoid recreation on each call.
76
- *
77
- * @param packageName - The addon package name
78
- * @param parentServices - The parent/caller's singleton services (used as base)
79
- * @returns The package's singleton services
80
- */
81
- /**
82
- * Find the consumer-defined namespace (from wireAddon) for a given addon package.
83
- * Returns null if the package isn't registered as an addon.
84
- */
85
- const findAddonNamespaceForPackage = (packageName: string): string | null => {
86
- const addons = pikkuState(null, 'addons', 'packages')
87
- if (!addons) return null
88
- for (const [namespace, cfg] of addons.entries()) {
89
- if (cfg?.package === packageName) return namespace
90
- }
91
- return null
92
- }
93
-
94
- /**
95
- * Wrap a workflow service so that bare workflow names passed from inside an
96
- * addon function are auto-prefixed with the addon's consumer-facing namespace.
97
- * Without this, `runToCompletion('myWorkflow')` from inside an addon misses
98
- * the workflow registered under the addon's package scope and throws
99
- * WorkflowNotFoundError — forcing addons to hardcode their consumer-defined
100
- * namespace, which couples the addon to its caller.
101
- *
102
- * Explicit `'ns:name'` and bare names that already exist in root meta are
103
- * unaffected; only bare names that would otherwise miss resolution get
104
- * prefixed.
105
- */
106
- const wrapWorkflowServiceForPackage = <T extends object>(
107
- service: T,
108
- packageName: string
109
- ): T => {
110
- return new Proxy(service, {
111
- get(target, prop, receiver) {
112
- if (prop === 'startWorkflow' || prop === 'runToCompletion') {
113
- const original = Reflect.get(target, prop, receiver) as Function
114
- return function (this: any, name: string, ...rest: any[]) {
115
- if (typeof name === 'string' && !name.includes(':')) {
116
- const namespace = findAddonNamespaceForPackage(packageName)
117
- if (namespace) {
118
- name = `${namespace}:${name}`
119
- }
120
- }
121
- return original.call(this, name, ...rest)
122
- }
123
- }
124
- return Reflect.get(target, prop, receiver)
125
- },
126
- })
127
- }
128
-
129
- const getOrCreatePackageSingletonServices = async (
130
- packageName: string,
131
- parentServices: CoreSingletonServices
132
- ): Promise<CoreSingletonServices> => {
133
- // Check if we already have cached singleton services for this package
134
- const cachedServices = pikkuState(packageName, 'package', 'singletonServices')
135
- if (cachedServices) {
136
- return cachedServices
137
- }
138
-
139
- // Get the package's service factories
140
- const factories = pikkuState(packageName, 'package', 'factories')
141
- if (!factories || !factories.createSingletonServices) {
142
- // No factories registered, use parent services
143
- return parentServices
144
- }
145
-
146
- // Create config for the package (use parent config if no factory)
147
- let config: CoreConfig = parentServices.config
148
- if (factories.createConfig) {
149
- config = await factories.createConfig(parentServices.variables)
150
- }
151
-
152
- // Create singleton services for the package, passing parent services as existing
153
- const packageServices = await factories.createSingletonServices(
154
- config,
155
- parentServices
156
- )
157
-
158
- // Wrap workflowService so that bare names used inside the addon's functions
159
- // resolve to workflows registered under the addon's package scope.
160
- if (
161
- packageServices.workflowService &&
162
- typeof packageServices.workflowService === 'object'
163
- ) {
164
- packageServices.workflowService = wrapWorkflowServiceForPackage(
165
- packageServices.workflowService as object,
166
- packageName
167
- ) as typeof packageServices.workflowService
168
- }
169
-
170
- // Cache the services
171
- pikkuState(packageName, 'package', 'singletonServices', packageServices)
172
-
173
- return packageServices
174
- }
175
-
176
70
  export const addFunction = (
177
71
  funcName: string,
178
72
  funcConfig: CorePikkuFunctionConfig<any, any>,
@@ -218,14 +112,12 @@ export const runPikkuFunc = async <In = any, Out = any>(
218
112
  wireMiddleware,
219
113
  inheritedChannelMiddleware,
220
114
  wireChannelMiddleware,
221
- inheritedPermissions,
222
- wirePermissions,
223
115
  coerceDataFromSchema,
224
- tags = [],
225
116
  wire,
226
117
  sessionService,
227
118
  credentialWireService,
228
119
  packageName = null,
120
+ addonInstance,
229
121
  }: {
230
122
  singletonServices: CoreSingletonServices
231
123
  createWireServices?: CreateWireServices
@@ -235,14 +127,13 @@ export const runPikkuFunc = async <In = any, Out = any>(
235
127
  wireMiddleware?: CorePikkuMiddleware[]
236
128
  inheritedChannelMiddleware?: MiddlewareMetadata[]
237
129
  wireChannelMiddleware?: CorePikkuChannelMiddleware[]
238
- inheritedPermissions?: PermissionMetadata[]
239
- wirePermissions?: CorePermissionGroup | CorePikkuPermission[]
240
130
  coerceDataFromSchema?: boolean
241
131
  tags?: string[]
242
132
  wire: PikkuRawWire
243
133
  sessionService?: SessionService<CoreUserSession>
244
134
  credentialWireService?: PikkuCredentialWireService
245
135
  packageName?: string | null
136
+ addonInstance?: AddonInstance
246
137
  }
247
138
  ): Promise<Out> => {
248
139
  wire.wireType ??= wireType
@@ -277,7 +168,11 @@ export const runPikkuFunc = async <In = any, Out = any>(
277
168
 
278
169
  // For addon packages, get or create their singleton services
279
170
  const resolvedSingletonServices = packageName
280
- ? await getOrCreatePackageSingletonServices(packageName, singletonServices)
171
+ ? await getOrCreatePackageSingletonServices(
172
+ packageName,
173
+ singletonServices,
174
+ addonInstance
175
+ )
281
176
  : singletonServices
282
177
 
283
178
  // Get the package's createWireServices if available
@@ -305,8 +200,24 @@ export const runPikkuFunc = async <In = any, Out = any>(
305
200
  : wire
306
201
 
307
202
  // Set up credential wire service early so middleware can use setCredential.
308
- // Skip if already set up (e.g. addon functions reuse the parent wire).
309
- if (!resolvedWire.getCredentials) {
203
+ // An addon instance with credentialOverrides always gets a fresh alias-aware
204
+ // service so its logical credential names resolve to instance-specific ones,
205
+ // even when a parent credential service is already present on the wire.
206
+ // Otherwise skip if already set up (e.g. addon functions reuse the parent wire).
207
+ if (addonInstance?.credentialOverrides) {
208
+ // Credentials belong to the consuming project, so resolve them via the
209
+ // project's credentialService (the addon's own singletons may not carry it).
210
+ const aliasedCredentialWireService = new PikkuCredentialWireService(
211
+ singletonServices.credentialService ??
212
+ resolvedSingletonServices.credentialService,
213
+ resolvedWire,
214
+ addonInstance.credentialOverrides
215
+ )
216
+ Object.assign(
217
+ resolvedWire,
218
+ createWireServicesCredentialWireProps(aliasedCredentialWireService)
219
+ )
220
+ } else if (!resolvedWire.getCredentials) {
310
221
  const resolvedCredentialWireService =
311
222
  credentialWireService ??
312
223
  new PikkuCredentialWireService(
@@ -323,22 +234,17 @@ export const runPikkuFunc = async <In = any, Out = any>(
323
234
  const invocationWire = resolvedWire as PikkuWire
324
235
  const previousFunctionId = invocationWire.functionId
325
236
  const previousAudit = invocationWire.audit
237
+ const previousAddonNamespace = invocationWire.addonNamespace
326
238
  const previousRpcDescriptor = Object.getOwnPropertyDescriptor(
327
239
  invocationWire,
328
240
  'rpc'
329
241
  )
330
242
  invocationWire.functionId = resolvedFunctionId
331
243
  invocationWire.audit = resolvedAuditConfig
332
-
333
- // Convert tags to PermissionMetadata and merge with inheritedPermissions
334
- let mergedInheritedPermissions: PermissionMetadata[]
335
- if (tags && tags.length > 0) {
336
- mergedInheritedPermissions = [
337
- ...(inheritedPermissions || []),
338
- ...tags.map((tag) => ({ type: 'tag' as const, tag })),
339
- ]
340
- } else {
341
- mergedInheritedPermissions = inheritedPermissions || []
244
+ // Track which addon instance is executing so intra-addon sibling calls
245
+ // resolve the same per-instance singleton services and overrides.
246
+ if (addonInstance) {
247
+ invocationWire.addonNamespace = addonInstance.namespace
342
248
  }
343
249
 
344
250
  // Helper function to run permissions and execute the function
@@ -380,6 +286,12 @@ export const runPikkuFunc = async <In = any, Out = any>(
380
286
  throw new ReadonlySessionError()
381
287
  }
382
288
 
289
+ // Scopes gate before the data is evaluated: they depend only on the
290
+ // session, so a denied request never pays to parse or validate its body.
291
+ // Scopes are an AND gate and stay separate from runPermissions, which
292
+ // evaluates the function's own OR-groups against request data.
293
+ verifyScopes(funcConfig.scopes ?? funcMeta.scopes, session)
294
+
383
295
  // Evaluate the data from the lazy function
384
296
  const actualData = await data()
385
297
 
@@ -400,23 +312,13 @@ export const runPikkuFunc = async <In = any, Out = any>(
400
312
  )
401
313
  }
402
314
 
403
- if (
404
- mergedInheritedPermissions.length > 0 ||
405
- wirePermissions ||
406
- funcMeta.permissions ||
407
- funcConfig.permissions
408
- ) {
409
- await runPermissions(wireType, wireId, {
410
- wireInheritedPermissions: mergedInheritedPermissions,
411
- wirePermissions: wirePermissions,
412
- funcInheritedPermissions: funcMeta.permissions,
413
- funcPermissions: funcConfig.permissions,
414
- services: resolvedSingletonServices,
415
- wire: invocationWire as any,
416
- data: actualData,
417
- packageName,
418
- })
419
- }
315
+ await runPermissions({
316
+ funcPermissions: funcConfig.permissions,
317
+ services: resolvedSingletonServices,
318
+ wire: invocationWire as any,
319
+ data: actualData,
320
+ packageName,
321
+ })
420
322
 
421
323
  let wireServices: Record<string, unknown> | undefined
422
324
  let invocationAuditLog: AuditLog | undefined
@@ -508,6 +410,11 @@ export const runPikkuFunc = async <In = any, Out = any>(
508
410
  } else {
509
411
  invocationWire.audit = previousAudit
510
412
  }
413
+ if (previousAddonNamespace === undefined) {
414
+ delete (invocationWire as any).addonNamespace
415
+ } else {
416
+ invocationWire.addonNamespace = previousAddonNamespace
417
+ }
511
418
  }
512
419
  }
513
420
 
@@ -6,6 +6,7 @@ import type {
6
6
  PikkuWire,
7
7
  PickRequired,
8
8
  } from '../types/core.types.js'
9
+ import type { PikkuRPC } from '../wirings/rpc/rpc-types.js'
9
10
  import type { StandardSchemaV1 } from '@standard-schema/spec'
10
11
  import type { PikkuError } from '../errors/error-handler.js'
11
12
  import type { CoreNodeConfig } from '../wirings/node/node.types.js'
@@ -29,12 +30,8 @@ export type CorePikkuFunction<
29
30
  Out,
30
31
  Services extends CoreSingletonServices = CoreServices,
31
32
  Session extends CoreUserSession = CoreUserSession,
32
- Wire extends PikkuWire<In, Out, true, Session> = PikkuWire<
33
- In,
34
- Out,
35
- true,
36
- Session
37
- >,
33
+ Wire extends PikkuWire<In, Out, true, Session, PikkuRPC, null, string> =
34
+ PikkuWire<In, Out, true, Session>,
38
35
  > = (
39
36
  services: Services,
40
37
  data: In,
@@ -54,15 +51,8 @@ export type CorePikkuFunctionSessionless<
54
51
  Out,
55
52
  Services extends CoreSingletonServices = CoreServices,
56
53
  Session extends CoreUserSession = CoreUserSession,
57
- Wire extends PikkuWire<In, Out, false, Session, any, any, any> = PikkuWire<
58
- In,
59
- Out,
60
- false,
61
- Session,
62
- any,
63
- any,
64
- any
65
- >,
54
+ Wire extends PikkuWire<In, Out, false, Session, PikkuRPC, null, string> =
55
+ PikkuWire<In, Out, false, Session>,
66
56
  > = (
67
57
  services: Services,
68
58
  data: In,
@@ -79,15 +69,8 @@ export type CorePikkuFunctionSessionless<
79
69
  export type CorePikkuPermission<
80
70
  In = any,
81
71
  Services extends CoreSingletonServices = CoreServices,
82
- Wire extends PikkuWire<In, never, false, any, any, never, never> = PikkuWire<
83
- In,
84
- never,
85
- false,
86
- any,
87
- never,
88
- never,
89
- never
90
- >,
72
+ Wire extends PikkuWire<In, never, false, any, PikkuRPC, never, never> =
73
+ PikkuWire<In, never, false, any, PikkuRPC, never, never>,
91
74
  > = (services: Services, data: In, wire: Wire) => Promise<boolean>
92
75
 
93
76
  /**
@@ -100,12 +83,8 @@ export type CorePikkuPermission<
100
83
  export type CorePikkuPermissionConfig<
101
84
  In = any,
102
85
  Services extends CoreSingletonServices = CoreServices,
103
- Wire extends PikkuWire<In, never, false, any> = PikkuWire<
104
- In,
105
- never,
106
- false,
107
- any
108
- >,
86
+ Wire extends PikkuWire<In, never, false, any, PikkuRPC, never, never> =
87
+ PikkuWire<In, never, false, any, PikkuRPC, never, never>,
109
88
  > = {
110
89
  /** The permission function */
111
90
  func: CorePikkuPermission<In, Services, Wire>
@@ -143,8 +122,13 @@ export type CorePikkuPermissionConfig<
143
122
  export const pikkuPermission = <
144
123
  In = any,
145
124
  Services extends CoreSingletonServices = CoreServices,
146
- Wire extends PickRequired<PikkuWire<In, never, false, any>, 'session'> =
147
- PickRequired<PikkuWire<In, never, false, any>, 'session'>,
125
+ Wire extends PickRequired<
126
+ PikkuWire<In, never, false, any, PikkuRPC, never, never>,
127
+ 'session'
128
+ > = PickRequired<
129
+ PikkuWire<In, never, false, any, PikkuRPC, never, never>,
130
+ 'session'
131
+ >,
148
132
  >(
149
133
  permission:
150
134
  | CorePikkuPermission<In, Services, Wire>
@@ -164,12 +148,8 @@ export const pikkuPermission = <
164
148
  export type CorePikkuPermissionFactory<
165
149
  In = any,
166
150
  Services extends CoreSingletonServices = CoreServices,
167
- Wire extends PikkuWire<In, never, false, any> = PikkuWire<
168
- In,
169
- never,
170
- false,
171
- any
172
- >,
151
+ Wire extends PikkuWire<In, never, false, any, PikkuRPC, never, never> =
152
+ PikkuWire<In, never, false, any, PikkuRPC, never, never>,
173
153
  > = (input: In) => CorePikkuPermission<any, Services, Wire>
174
154
 
175
155
  /**
@@ -280,6 +260,7 @@ export type CorePikkuFunctionConfig<
280
260
  >,
281
261
  InputSchema extends StandardSchemaV1 | undefined = undefined,
282
262
  OutputSchema extends StandardSchemaV1 | undefined = undefined,
263
+ Scope extends string = string,
283
264
  > = {
284
265
  /** Short human-readable name (e.g. "Create Todo") */
285
266
  title?: string
@@ -309,6 +290,15 @@ export type CorePikkuFunctionConfig<
309
290
  approvalDescription?: any
310
291
  func: PikkuFunction
311
292
  auth?: boolean
293
+ /**
294
+ * Scopes the session must hold to run this function. All of them are
295
+ * required (AND), and they are checked before `permissions` — unlike
296
+ * permissions, which OR together, a scope can only narrow access.
297
+ *
298
+ * Narrowed to the generated `ScopeId` union in a project's own
299
+ * `pikku-types.gen.ts`, so an undeclared scope is a compile error.
300
+ */
301
+ scopes?: Scope[]
312
302
  permissions?: CorePermissionGroup<PikkuPermission>
313
303
  middleware?: PikkuMiddleware[]
314
304
  input?: InputSchema
package/src/index.ts CHANGED
@@ -169,6 +169,7 @@ export type {
169
169
  AITranscriptionParams,
170
170
  AITranscriptionResult,
171
171
  } from './services/ai-agent-runner-service.js'
172
+ export type { AIEmbeddingService } from './services/ai-embedding-service.js'
172
173
  export type { AIRunStateService } from './services/ai-run-state-service.js'
173
174
  export type { AIStorageService } from './services/ai-storage-service.js'
174
175
  export type {
@@ -183,6 +184,11 @@ export type { GraphNodeConfig } from './wirings/workflow/graph/workflow-graph.ty
183
184
  export { createGraph } from './wirings/workflow/graph/graph-node.js'
184
185
  export { wireAddon } from './wirings/rpc/wire-addon.js'
185
186
  export type { WireAddonConfig } from './wirings/rpc/wire-addon.js'
187
+ export { wireRemoteAddon } from './wirings/rpc/wire-remote-addon.js'
188
+ export type {
189
+ WireRemoteAddonConfig,
190
+ RemoteAddonAuth,
191
+ } from './wirings/rpc/wire-remote-addon.js'
186
192
  export type { PikkuPackageState } from './types/state.types.js'
187
193
  export {
188
194
  runMiddleware,
@@ -190,12 +196,8 @@ export {
190
196
  addTagMiddleware as addMiddleware,
191
197
  addGlobalMiddleware,
192
198
  } from './middleware-runner.js'
193
- export {
194
- addTagPermission,
195
- addTagPermission as addPermission,
196
- addGlobalPermission,
197
- checkAuthPermissions,
198
- } from './permissions.js'
199
+ export { addGlobalPermission, checkAuthPermissions } from './permissions.js'
200
+ export { verifyScopes } from './scopes.js'
199
201
  export {
200
202
  isSerializable,
201
203
  stopSingletonServices,
@@ -10,8 +10,4 @@ export {
10
10
  addGlobalMiddleware,
11
11
  runMiddleware,
12
12
  } from '../middleware-runner.js'
13
- export {
14
- addTagPermission,
15
- addTagPermission as addPermission,
16
- addGlobalPermission,
17
- } from '../permissions.js'
13
+ export { addGlobalPermission, addTagPermission } from '../permissions.js'