@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
@@ -0,0 +1,16 @@
1
+ import type { FlatScope, ScopeDefinitions, ScopeDefinitionsMeta } from './scope.types.js';
2
+ /**
3
+ * Flattens declared scope trees into the full list of grantable scope ids,
4
+ * depth-first. Every node is emitted, including intermediate ones.
5
+ *
6
+ * Used by codegen to build the `ScopeId` union, and by a ScopeService to sync
7
+ * the declared set into its store.
8
+ */
9
+ export declare const flattenScopeDefinitions: (definitions: ScopeDefinitions) => FlatScope[];
10
+ /**
11
+ * Validates declared scopes and keys them by name.
12
+ *
13
+ * Definitions sharing a name must be identical; a conflicting redeclaration is
14
+ * a hard error naming both source files.
15
+ */
16
+ export declare function validateAndBuildScopeDefinitionsMeta(definitions: ScopeDefinitions): ScopeDefinitionsMeta;
@@ -0,0 +1,76 @@
1
+ const SEPARATOR = ':';
2
+ const WILDCARD = '*';
3
+ const assertSegment = (segment, scopeName) => {
4
+ if (segment.includes(SEPARATOR)) {
5
+ throw new Error(`Scope segment '${segment}' in '${scopeName}' contains the '${SEPARATOR}' separator. ` +
6
+ `Nest scopes with the 'scopes' property instead of embedding '${SEPARATOR}' in a name.`);
7
+ }
8
+ if (segment === WILDCARD) {
9
+ throw new Error(`Scope segment '${segment}' in '${scopeName}' is the wildcard. ` +
10
+ `'${WILDCARD}' is reserved for granting a scope and its descendants, and cannot be declared.`);
11
+ }
12
+ if (segment.length === 0) {
13
+ throw new Error(`Scope '${scopeName}' contains an empty segment.`);
14
+ }
15
+ };
16
+ const assertNodesValid = (nodes, scopeName) => {
17
+ for (const [segment, node] of Object.entries(nodes ?? {})) {
18
+ assertSegment(segment, scopeName);
19
+ assertNodesValid(node.scopes, scopeName);
20
+ }
21
+ };
22
+ const flattenNodes = (nodes, prefix, out) => {
23
+ for (const [segment, node] of Object.entries(nodes ?? {})) {
24
+ const id = `${prefix}${SEPARATOR}${segment}`;
25
+ out.push({ id, description: node.description });
26
+ flattenNodes(node.scopes, id, out);
27
+ }
28
+ };
29
+ /**
30
+ * Flattens declared scope trees into the full list of grantable scope ids,
31
+ * depth-first. Every node is emitted, including intermediate ones.
32
+ *
33
+ * Used by codegen to build the `ScopeId` union, and by a ScopeService to sync
34
+ * the declared set into its store.
35
+ */
36
+ export const flattenScopeDefinitions = (definitions) => {
37
+ const out = [];
38
+ for (const def of definitions) {
39
+ out.push({ id: def.name, description: def.description });
40
+ flattenNodes(def.scopes, def.name, out);
41
+ }
42
+ return out;
43
+ };
44
+ /**
45
+ * Validates declared scopes and keys them by name.
46
+ *
47
+ * Definitions sharing a name must be identical; a conflicting redeclaration is
48
+ * a hard error naming both source files.
49
+ */
50
+ export function validateAndBuildScopeDefinitionsMeta(definitions) {
51
+ const meta = {};
52
+ for (const def of definitions) {
53
+ assertSegment(def.name, def.name);
54
+ assertNodesValid(def.scopes, def.name);
55
+ const existing = meta[def.name];
56
+ if (existing) {
57
+ const sameShape = JSON.stringify(existing.scopes ?? {}) ===
58
+ JSON.stringify(def.scopes ?? {});
59
+ if (!sameShape) {
60
+ throw new Error(`Scope '${def.name}' is declared with different nested scopes.\n` +
61
+ ` First declaration: ${existing.sourceFile ?? 'unknown'}\n` +
62
+ ` Second declaration: ${def.sourceFile ?? 'unknown'}\n` +
63
+ `Scopes sharing a name must declare the same tree.`);
64
+ }
65
+ continue;
66
+ }
67
+ meta[def.name] = {
68
+ name: def.name,
69
+ displayName: def.displayName,
70
+ description: def.description,
71
+ scopes: def.scopes,
72
+ sourceFile: def.sourceFile,
73
+ };
74
+ }
75
+ return meta;
76
+ }
@@ -0,0 +1,33 @@
1
+ import type { CoreScopes } from './scope.types.js';
2
+ /**
3
+ * No-op function for declaring scopes.
4
+ * This exists purely for TypeScript type checking and will be tree-shaken.
5
+ * The CLI extracts metadata via AST parsing and generates a `ScopeId` union,
6
+ * so a function referencing an undeclared scope fails the build.
7
+ *
8
+ * Scopes are keyed by segment at every level: a scope is named by its key, and
9
+ * its value describes it. Every node is grantable — the declaration below
10
+ * yields `admin`, `admin:invoices`, `admin:invoices:create`,
11
+ * `admin:invoices:void` and `billing`.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * wireScope({
16
+ * admin: {
17
+ * displayName: 'Administration',
18
+ * description: 'Administrative access',
19
+ * scopes: {
20
+ * invoices: {
21
+ * description: 'Invoice management',
22
+ * scopes: {
23
+ * create: { description: 'Create invoices' },
24
+ * void: { description: 'Void invoices' },
25
+ * },
26
+ * },
27
+ * },
28
+ * },
29
+ * billing: {},
30
+ * })
31
+ * ```
32
+ */
33
+ export declare const wireScope: (_config: CoreScopes) => void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * No-op function for declaring scopes.
3
+ * This exists purely for TypeScript type checking and will be tree-shaken.
4
+ * The CLI extracts metadata via AST parsing and generates a `ScopeId` union,
5
+ * so a function referencing an undeclared scope fails the build.
6
+ *
7
+ * Scopes are keyed by segment at every level: a scope is named by its key, and
8
+ * its value describes it. Every node is grantable — the declaration below
9
+ * yields `admin`, `admin:invoices`, `admin:invoices:create`,
10
+ * `admin:invoices:void` and `billing`.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * wireScope({
15
+ * admin: {
16
+ * displayName: 'Administration',
17
+ * description: 'Administrative access',
18
+ * scopes: {
19
+ * invoices: {
20
+ * description: 'Invoice management',
21
+ * scopes: {
22
+ * create: { description: 'Create invoices' },
23
+ * void: { description: 'Void invoices' },
24
+ * },
25
+ * },
26
+ * },
27
+ * },
28
+ * billing: {},
29
+ * })
30
+ * ```
31
+ */
32
+ export const wireScope = (_config) => { };
@@ -4,6 +4,13 @@ export type CoreSecret<T = unknown> = {
4
4
  description?: string;
5
5
  secretId: string;
6
6
  schema: T;
7
+ /**
8
+ * Link to documentation explaining how to obtain this value — a provider's
9
+ * API-key page, a setup guide, an internal runbook. Surfaced by consoles and
10
+ * deploy UIs so a user facing a missing value has somewhere to go instead of
11
+ * an opaque identifier.
12
+ */
13
+ docsUrl?: string;
7
14
  /**
8
15
  * Optional rotation cadence for this secret, e.g. '1d', '30day', '1w'.
9
16
  * Stored in the generated secrets metadata so consumers can tell when a
@@ -25,6 +32,13 @@ export type SecretDefinitionMeta = {
25
32
  description?: string;
26
33
  secretId: string;
27
34
  schema?: Record<string, unknown> | string;
35
+ /**
36
+ * Link to documentation explaining how to obtain this value — a provider's
37
+ * API-key page, a setup guide, an internal runbook. Surfaced by consoles and
38
+ * deploy UIs so a user facing a missing value has somewhere to go instead of
39
+ * an opaque identifier.
40
+ */
41
+ docsUrl?: string;
28
42
  oauth2?: OAuth2CredentialConfig;
29
43
  rotationPeriod?: string;
30
44
  sourceFile?: string;
@@ -34,6 +34,7 @@ export function validateAndBuildSecretDefinitionsMeta(definitions, schemaLookup)
34
34
  schema: def.schema,
35
35
  oauth2: def.oauth2,
36
36
  rotationPeriod: def.rotationPeriod,
37
+ docsUrl: def.docsUrl,
37
38
  sourceFile: def.sourceFile,
38
39
  };
39
40
  }
@@ -49,6 +50,7 @@ export function validateAndBuildSecretDefinitionsMeta(definitions, schemaLookup)
49
50
  schema: def.schema,
50
51
  oauth2: def.oauth2,
51
52
  rotationPeriod: def.rotationPeriod,
53
+ docsUrl: def.docsUrl,
52
54
  sourceFile: def.sourceFile,
53
55
  };
54
56
  }
@@ -24,6 +24,7 @@ export function validateAndBuildVariableDefinitionsMeta(definitions, schemaLooku
24
24
  description: def.description,
25
25
  variableId: def.variableId,
26
26
  schema: def.schema,
27
+ docsUrl: def.docsUrl,
27
28
  sourceFile: def.sourceFile,
28
29
  };
29
30
  }
@@ -37,6 +38,7 @@ export function validateAndBuildVariableDefinitionsMeta(definitions, schemaLooku
37
38
  description: def.description,
38
39
  variableId: def.variableId,
39
40
  schema: def.schema,
41
+ docsUrl: def.docsUrl,
40
42
  sourceFile: def.sourceFile,
41
43
  };
42
44
  }
@@ -4,6 +4,13 @@ export type CoreVariable<T = unknown> = {
4
4
  description?: string;
5
5
  variableId: string;
6
6
  schema: T;
7
+ /**
8
+ * Link to documentation explaining how to obtain this value — a provider's
9
+ * API-key page, a setup guide, an internal runbook. Surfaced by consoles and
10
+ * deploy UIs so a user facing a missing value has somewhere to go instead of
11
+ * an opaque identifier.
12
+ */
13
+ docsUrl?: string;
7
14
  };
8
15
  export type VariableDefinitionMeta = {
9
16
  name: string;
@@ -11,6 +18,13 @@ export type VariableDefinitionMeta = {
11
18
  description?: string;
12
19
  variableId: string;
13
20
  schema?: Record<string, unknown> | string;
21
+ /**
22
+ * Link to documentation explaining how to obtain this value — a provider's
23
+ * API-key page, a setup guide, an internal runbook. Surfaced by consoles and
24
+ * deploy UIs so a user facing a missing value has somewhere to go instead of
25
+ * an opaque identifier.
26
+ */
27
+ docsUrl?: string;
14
28
  sourceFile?: string;
15
29
  };
16
30
  export type VariableDefinitionsMeta = Record<string, VariableDefinitionMeta>;
@@ -2,4 +2,4 @@
2
2
  * DSL (Domain Specific Language) workflow exports
3
3
  */
4
4
  export { addWorkflow } from './workflow-runner.js';
5
- export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow-dsl.types.js';
5
+ export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, WorkflowWireApproval, WorkflowApprovalOptions, ApprovalOutcome, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, ApprovalStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow-dsl.types.js';
@@ -2,6 +2,7 @@
2
2
  * DSL (Domain Specific Language) workflow types
3
3
  * These types define the step-based workflow format extracted by the inspector
4
4
  */
5
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
5
6
  import type { WorkflowRun } from '../workflow.types.js';
6
7
  import type { ScenarioActor } from '../../../services/scenario-actors-service.js';
7
8
  /**
@@ -14,6 +15,13 @@ export interface WorkflowStepOptions {
14
15
  retries?: number;
15
16
  /** Delay between retry attempts (e.g., '1s', '2s', '2min') */
16
17
  retryDelay?: string | number;
18
+ /**
19
+ * RPC to invoke for compensation when this step fails after exhausting its
20
+ * retries. Mirrors a graph node's `onError`: the handler receives
21
+ * `{ error: { message } }` and the original error is still thrown, so the
22
+ * workflow fails — this is compensation, not recovery.
23
+ */
24
+ onError?: string;
17
25
  /**
18
26
  * Run this step as an actor (scenarios). The RPC is sent through the
19
27
  * actor's authenticated client over the REAL transport — never dispatched
@@ -64,6 +72,43 @@ export type WorkflowWireSleep = (stepName: string, duration: string) => Promise<
64
72
  * loops, like dynamic `do()` step names.
65
73
  */
66
74
  export type WorkflowWireSuspend = (reason: string) => Promise<void>;
75
+ /**
76
+ * Options for workflow.approval().
77
+ */
78
+ export interface WorkflowApprovalOptions<TSchema extends StandardSchemaV1 = StandardSchemaV1> {
79
+ /**
80
+ * Schema the decision payload is validated against. This is a VALUE, not a
81
+ * type generic: the payload arrives from an untrusted caller over the approve
82
+ * wire, and a generic is erased at compile time — it would validate nothing.
83
+ * Any standard-schema library (zod, valibot, arktype) satisfies this.
84
+ */
85
+ schema: TSchema;
86
+ /**
87
+ * Give up waiting after this long (e.g. '3d'), yielding `{ status: 'expired' }`
88
+ * instead of waiting forever. Evaluated on replay from a recorded deadline, so
89
+ * the answer is correct even if the wake-up timer is never delivered.
90
+ */
91
+ expiry?: string | number;
92
+ }
93
+ /**
94
+ * The result of an approval gate. A union rather than a throw so that callers
95
+ * must handle the deadline case, and so "skip it and carry on" stays trivial.
96
+ * `decided` means a human answered — whether that answer was yes or no is
97
+ * carried in `data` and is the application's business, not the framework's.
98
+ */
99
+ export type ApprovalOutcome<T> = {
100
+ status: 'decided';
101
+ data: T;
102
+ } | {
103
+ status: 'expired';
104
+ };
105
+ /**
106
+ * Type signature for workflow.approval() - used by inspector.
107
+ * Like {@link WorkflowWireSuspend}, `reason` is the approval point's stable
108
+ * durable identity. Unlike suspend, the gate stays closed until a decision is
109
+ * recorded against it, and the decision is handed back to the caller.
110
+ */
111
+ export type WorkflowWireApproval = <TSchema extends StandardSchemaV1>(reason: string, options: WorkflowApprovalOptions<TSchema>) => Promise<ApprovalOutcome<StandardSchemaV1.InferOutput<TSchema>>>;
67
112
  /**
68
113
  * Input source for step arguments in DSL workflows
69
114
  */
@@ -176,16 +221,20 @@ export interface ParallelGroupStepMeta {
176
221
  */
177
222
  export interface FanoutStepMeta {
178
223
  type: 'fanout';
179
- /** Step name for this fanout */
180
- stepName: string;
224
+ /**
225
+ * Step name for this fanout. Optional: a fanout is not itself a cached step,
226
+ * and node ids are step names — borrowing a body step's name would give the
227
+ * loop and that step the same id, collapsing one onto the other.
228
+ */
229
+ stepName?: string;
181
230
  /** Source array variable name */
182
231
  sourceVar: string;
183
232
  /** Iterator variable name */
184
233
  itemVar: string;
185
234
  /** Execution mode */
186
235
  mode: 'parallel' | 'sequential';
187
- /** Child step to execute per iteration */
188
- child: RpcStepMeta;
236
+ /** Steps to execute inline per iteration, in order */
237
+ body: Array<RpcStepMeta | SleepStepMeta | SuspendStepMeta>;
189
238
  /** Time between iterations (sequential mode only) */
190
239
  timeBetween?: string;
191
240
  }
@@ -196,6 +245,12 @@ export interface ReturnStepMeta {
196
245
  type: 'return';
197
246
  /** Output bindings */
198
247
  outputs: Record<string, OutputBinding>;
248
+ /**
249
+ * Variables spread into the returned object (`return { ...r }`), or the sole
250
+ * returned variable (`return r`). Their fields are not enumerable statically,
251
+ * so they are recorded by name rather than expanded into `outputs`.
252
+ */
253
+ spread?: string[];
199
254
  }
200
255
  /**
201
256
  * Inline step metadata (legacy support)
@@ -220,6 +275,13 @@ export interface SleepStepMeta {
220
275
  stepName: string;
221
276
  /** Sleep duration */
222
277
  duration: string | number;
278
+ /**
279
+ * Source text of a duration only known at runtime (e.g. a loop variable).
280
+ * The closure evaluates it, so it is legal DSL; it is kept separate from
281
+ * `duration` so regenerated code emits it raw rather than as a string
282
+ * literal, exactly as `expression` does on a set step.
283
+ */
284
+ expression?: string;
223
285
  }
224
286
  /**
225
287
  * Cancel step metadata
@@ -238,8 +300,14 @@ export interface SetStepMeta {
238
300
  type: 'set';
239
301
  /** Variable name to set (must be in context) */
240
302
  variable: string;
241
- /** Value to assign (literal or expression) */
242
- value: unknown;
303
+ /** Literal value to assign. Mutually exclusive with `expression`. */
304
+ value?: unknown;
305
+ /**
306
+ * Source text of a non-literal assignment (e.g. `count + 1`). Kept separate
307
+ * from `value` so regenerated code can emit it raw — a string `value` is a
308
+ * string literal, an `expression` is code.
309
+ */
310
+ expression?: string;
243
311
  }
244
312
  /**
245
313
  * Switch case metadata
@@ -272,6 +340,18 @@ export interface SuspendStepMeta {
272
340
  /** Reason string passed to workflow.suspend() — becomes the durable step key */
273
341
  reason: string;
274
342
  }
343
+ /**
344
+ * Approval step metadata (workflow.approval())
345
+ */
346
+ export interface ApprovalStepMeta {
347
+ type: 'approval';
348
+ /** Reason string passed to workflow.approval() — becomes the durable step key */
349
+ reason: string;
350
+ /** Output variable name (if assigned) */
351
+ outputVar?: string;
352
+ /** Expiry duration, when one was given */
353
+ expiry?: string | number;
354
+ }
275
355
  /**
276
356
  * Filter step metadata (array.filter)
277
357
  */
@@ -305,7 +385,7 @@ export interface ArrayPredicateStepMeta {
305
385
  /**
306
386
  * Workflow step metadata (extracted by inspector)
307
387
  */
308
- export type WorkflowStepMeta = RpcStepMeta | BranchStepMeta | ParallelGroupStepMeta | FanoutStepMeta | ReturnStepMeta | InlineStepMeta | SleepStepMeta | CancelStepMeta | SuspendStepMeta | SwitchStepMeta | FilterStepMeta | ArrayPredicateStepMeta | SetStepMeta;
388
+ export type WorkflowStepMeta = RpcStepMeta | BranchStepMeta | ParallelGroupStepMeta | FanoutStepMeta | ReturnStepMeta | InlineStepMeta | SleepStepMeta | CancelStepMeta | SuspendStepMeta | ApprovalStepMeta | SwitchStepMeta | FilterStepMeta | ArrayPredicateStepMeta | SetStepMeta;
309
389
  /**
310
390
  * Workflow step wire context for RPC functions
311
391
  * Provides step-level metadata including retry attempt tracking
@@ -355,6 +435,8 @@ export interface PikkuWorkflowWire {
355
435
  sleep: WorkflowWireSleep;
356
436
  /** Suspend workflow until explicitly resumed */
357
437
  suspend: WorkflowWireSuspend;
438
+ /** Suspend workflow until a human records a decision against this gate */
439
+ approval: WorkflowWireApproval;
358
440
  }
359
441
  export interface PikkuScenarioWire extends PikkuWorkflowWire {
360
442
  /**
@@ -93,6 +93,8 @@ type GraphNodeConfigMap<FuncMap extends Record<string, string>, RPCMap extends R
93
93
  onError?: Extract<keyof FuncMap, string> | Extract<keyof FuncMap, string>[];
94
94
  retries?: number;
95
95
  retryDelay?: string | number;
96
+ /** Free-text node documentation. Non-semantic — excluded from graphHash. */
97
+ notes?: string;
96
98
  };
97
99
  };
98
100
  /**
@@ -246,6 +246,9 @@ function resolveTemplate(template, nodeResults) {
246
246
  }
247
247
  function resolveValue(value, nodeResults) {
248
248
  if (isDataRef(value)) {
249
+ if (value.$ref === '$item') {
250
+ return value;
251
+ }
249
252
  const source = nodeResults[value.$ref];
250
253
  return value.path ? getValueAtPath(source, value.path) : source;
251
254
  }
@@ -5,11 +5,17 @@ export interface PikkuWorkflowGraphConfig<FuncMap extends Record<string, string>
5
5
  tags?: string[];
6
6
  nodes: FuncMap;
7
7
  config?: T;
8
+ /**
9
+ * Graph-level free-text notes (e.g. imported sticky notes). Non-semantic:
10
+ * excluded from the graph topology hash.
11
+ */
12
+ notes?: string[];
8
13
  }
9
14
  export interface PikkuWorkflowGraphResult {
10
15
  __type: 'pikkuWorkflowGraph';
11
16
  name?: string;
12
17
  description?: string;
13
18
  tags?: string[];
19
+ notes?: string[];
14
20
  }
15
21
  export declare function pikkuWorkflowGraph<const FuncMap extends Record<string, string>>(config: PikkuWorkflowGraphConfig<FuncMap, any>): PikkuWorkflowGraphResult;
@@ -4,5 +4,6 @@ export function pikkuWorkflowGraph(config) {
4
4
  name: config.name,
5
5
  description: config.description,
6
6
  tags: config.tags,
7
+ notes: config.notes,
7
8
  };
8
9
  }
@@ -49,6 +49,11 @@ export interface GraphNodeConfig<NodeIds extends string = string> {
49
49
  retries?: number;
50
50
  /** Delay between retries — milliseconds, duration string, or 'exponential' */
51
51
  retryDelay?: string | number;
52
+ /**
53
+ * Free-text documentation for this node. Non-semantic: excluded from the
54
+ * graph topology hash, so editing a note never marks the workflow as changed.
55
+ */
56
+ notes?: string;
52
57
  }
53
58
  /**
54
59
  * Graph wire context - available to functions running in a workflow graph
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Workflow module exports
3
3
  */
4
- export { PikkuWorkflowService, WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, DEFAULT_STEP_RETRIES, } from './pikku-workflow-service.js';
4
+ export { PikkuWorkflowService, WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, WorkflowApprovalResolvedError, DEFAULT_STEP_RETRIES, } from './pikku-workflow-service.js';
5
5
  export { deriveInvocationId, uuidv5 } from './workflow-invocation-id.js';
6
6
  export { buildRunTimeline, reconstructStateAt, reconstructFinalState, } from './run-timeline.js';
7
7
  export type { RunTimeline, RunTimelineEvent, ReconstructedRunState, ReconstructedStep, RunPhase, } from './run-timeline.js';
@@ -12,4 +12,4 @@ export { validateWorkflowWiring, computeEntryNodeIds, } from './graph/graph-vali
12
12
  export { pikkuWorkflowWorkerFunc, pikkuWorkflowOrchestratorFunc, pikkuWorkflowSleeperFunc, } from './workflow-queue-workers.js';
13
13
  export type { WorkflowStepInput as WorkflowStepQueueInput, PikkuWorkflowOrchestratorInput, PikkuWorkflowSleeperInput, } from './workflow-queue-workers.js';
14
14
  export type { WorkflowService, WorkflowServiceConfig, WorkflowPlannedStep, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, StepStatus, WorkflowRun, WorkflowRunStatus, StepState, WorkflowRunService, WorkflowRunMirror, CoreWorkflow, PikkuWorkflow, ContextVariable, WorkflowContext, WorkflowsMeta, WorkflowRuntimeMeta, WorkflowsRuntimeMeta, WorkflowStepInput, WorkflowOrchestratorInput, WorkflowSleeperInput, } from './workflow.types.js';
15
- export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, PikkuScenarioWire, } from './workflow.types.js';
15
+ export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, WorkflowWireApproval, WorkflowApprovalOptions, ApprovalOutcome, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, ApprovalStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, PikkuScenarioWire, } from './workflow.types.js';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Workflow module exports
3
3
  */
4
- export { PikkuWorkflowService, WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, DEFAULT_STEP_RETRIES, } from './pikku-workflow-service.js';
4
+ export { PikkuWorkflowService, WorkflowCancelledException, WorkflowSuspendedException, WorkflowDispatchException, WorkflowNotFoundError, WorkflowRunNotFoundError, WorkflowApprovalResolvedError, DEFAULT_STEP_RETRIES, } from './pikku-workflow-service.js';
5
5
  export { deriveInvocationId, uuidv5 } from './workflow-invocation-id.js';
6
6
  // Time-travel: reconstruct run state at any point from durable history
7
7
  export { buildRunTimeline, reconstructStateAt, reconstructFinalState, } from './run-timeline.js';
@@ -1,5 +1,5 @@
1
1
  import type { SerializedError } from '../../types/core.types.js';
2
- import type { PikkuScenarioWire, StepState, StepStatus, WorkflowPlannedStep, WorkflowRun, WorkflowRunMirror, WorkflowRunStatus, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, WorkflowStepOptions } from './workflow.types.js';
2
+ import type { ApprovalOutcome, PikkuScenarioWire, StepState, StepStatus, WorkflowPlannedStep, WorkflowRun, WorkflowRunMirror, WorkflowRunStatus, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, WorkflowStepOptions } from './workflow.types.js';
3
3
  import type { WorkflowService } from '../../services/workflow-service.js';
4
4
  import type { ScenarioActors } from '../../services/scenario-actors-service.js';
5
5
  import { PikkuError } from '../../errors/error-handler.js';
@@ -70,6 +70,19 @@ export declare class WorkflowRunFailedError extends PikkuError {
70
70
  export declare class WorkflowRunCancelledError extends PikkuError {
71
71
  constructor();
72
72
  }
73
+ /**
74
+ * A decision arrived for an approval gate that has already resolved. The gate
75
+ * caches its outcome as the step result and never re-reads run state, so the
76
+ * decision could not take effect — it is rejected rather than accepted and
77
+ * dropped.
78
+ */
79
+ export declare class WorkflowApprovalResolvedError extends PikkuError {
80
+ payload: {
81
+ reason: string;
82
+ outcome: ApprovalOutcome<unknown>['status'];
83
+ };
84
+ constructor(reason: string, outcome: ApprovalOutcome<unknown>['status']);
85
+ }
73
86
  export declare class WorkflowServiceNotInitialized extends Error {
74
87
  }
75
88
  export declare class WorkflowStepNameNotString extends Error {
@@ -430,6 +443,14 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
430
443
  * loop exits immediately without recording a step error or retrying.
431
444
  */
432
445
  private runInlineRetryLoop;
446
+ /**
447
+ * Run a failed step's compensation handler as a durable step of its own.
448
+ *
449
+ * Durable rather than a bare invoke so a replay does not compensate twice —
450
+ * a handler is typically a refund or a rollback. `onError` is deliberately
451
+ * not forwarded: a compensation handler cannot itself compensate.
452
+ */
453
+ private runStepCompensation;
433
454
  private rpcStep;
434
455
  private inlineStep;
435
456
  private sleepStep;
@@ -446,6 +467,43 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
446
467
  */
447
468
  private getSuspendStepName;
448
469
  private suspendStep;
470
+ /**
471
+ * Wake a run later by enqueuing a delayed orchestrator pass. Deliberately NOT
472
+ * {@link scheduleSleep}: that resolves the step it is given, which for an
473
+ * approval would resolve the gate itself. This only nudges the run to replay
474
+ * and re-evaluate — the gate stays the sole judge of its own outcome.
475
+ *
476
+ * Best-effort by design. Expiry is decided from the recorded deadline on
477
+ * replay, so losing this wake costs liveness (the run sits until something
478
+ * else resumes it), never correctness.
479
+ */
480
+ private scheduleRunWake;
481
+ /**
482
+ * Durable step name for an approval gate. Namespaced separately from suspend
483
+ * so the two can't collide, and derived from `reason` for the same reason
484
+ * {@link getSuspendStepName} is: it must be stable across replays.
485
+ */
486
+ private getApprovalStepName;
487
+ /**
488
+ * Run-state key holding an approval gate's record. Hex-encoded because the
489
+ * Mongo backend restricts state keys to `/^[a-zA-Z0-9_]+$/` and a `reason` is
490
+ * arbitrary human text. One key per gate, so two gates resolving concurrently
491
+ * can't clobber each other through a read-modify-write.
492
+ */
493
+ private approvalStateKey;
494
+ /**
495
+ * Record a decision against an approval gate and wake the run. Called from
496
+ * outside the workflow (an HTTP route, an RPC), so the schema value is NOT in
497
+ * scope here — the payload is stored raw and validated on replay inside the
498
+ * workflow body, which is the only place the schema exists. An invalid payload
499
+ * therefore leaves the gate closed rather than failing the run.
500
+ *
501
+ * `reason` addresses the first reach of that gate. An approval reached more
502
+ * than once under the same reason (e.g. in a loop) gets `#N`-suffixed step
503
+ * rows that this cannot currently target.
504
+ */
505
+ approveStep(runId: string, reason: string, decision: unknown): Promise<void>;
506
+ private approvalStep;
449
507
  createWorkflowWire(name: string, runId: string, rpcService: any, addonNamespace?: string | null): PikkuScenarioWire;
450
508
  private verifyStepName;
451
509
  private getConfig;