@pikku/inspector 0.12.106 → 0.12.108

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 (96) hide show
  1. package/dist/add/add-auth.d.ts +1 -9
  2. package/dist/add/add-auth.js +64 -17
  3. package/dist/add/add-cli.js +4 -0
  4. package/dist/add/add-functions.js +27 -31
  5. package/dist/add/add-keyed-wiring.js +22 -2
  6. package/dist/add/add-middleware.js +17 -16
  7. package/dist/add/add-permission.d.ts +0 -3
  8. package/dist/add/add-permission.js +213 -22
  9. package/dist/add/add-scope.d.ts +1 -7
  10. package/dist/add/add-scope.js +1 -21
  11. package/dist/add/check-indirect-registrations.d.ts +9 -0
  12. package/dist/add/check-indirect-registrations.js +114 -0
  13. package/dist/error-codes.d.ts +1 -0
  14. package/dist/error-codes.js +1 -0
  15. package/dist/finalize.d.ts +10 -0
  16. package/dist/finalize.js +102 -0
  17. package/dist/index.d.ts +8 -6
  18. package/dist/index.js +6 -5
  19. package/dist/initial-state.d.ts +7 -0
  20. package/dist/initial-state.js +216 -0
  21. package/dist/inspector.d.ts +6 -4
  22. package/dist/inspector.js +28 -302
  23. package/dist/lite.d.ts +26 -0
  24. package/dist/lite.js +25 -0
  25. package/dist/source-file-cache.d.ts +11 -0
  26. package/dist/source-file-cache.js +34 -10
  27. package/dist/types-map.js +2 -1
  28. package/dist/types.d.ts +32 -0
  29. package/dist/utils/apply-declared-scopes.d.ts +8 -0
  30. package/dist/utils/apply-declared-scopes.js +21 -0
  31. package/dist/utils/canonical-type-string.d.ts +14 -0
  32. package/dist/utils/canonical-type-string.js +162 -0
  33. package/dist/utils/context-based-id.d.ts +7 -0
  34. package/dist/utils/context-based-id.js +9 -0
  35. package/dist/utils/custom-types-generator.d.ts +15 -1
  36. package/dist/utils/custom-types-generator.js +17 -5
  37. package/dist/utils/ensure-function-metadata.d.ts +0 -14
  38. package/dist/utils/ensure-function-metadata.js +4 -1
  39. package/dist/utils/extract-function-name.d.ts +1 -1
  40. package/dist/utils/extract-function-name.js +1 -3
  41. package/dist/utils/filter-inspector-state.js +31 -1
  42. package/dist/utils/get-property-value.d.ts +5 -0
  43. package/dist/utils/get-property-value.js +14 -0
  44. package/dist/utils/inspector-constants.d.ts +16 -0
  45. package/dist/utils/inspector-constants.js +16 -0
  46. package/dist/utils/post-process.d.ts +1 -2
  47. package/dist/utils/post-process.js +27 -27
  48. package/dist/utils/read-jsx-literal-text.js +31 -1
  49. package/dist/utils/resolve-callee-name.d.ts +27 -0
  50. package/dist/utils/resolve-callee-name.js +147 -0
  51. package/dist/utils/resolve-service-types.d.ts +8 -0
  52. package/dist/utils/resolve-service-types.js +21 -0
  53. package/dist/utils/schema-generator.js +29 -2
  54. package/dist/utils/serialize-inspector-state.d.ts +7 -1
  55. package/dist/utils/serialize-inspector-state.js +16 -1
  56. package/dist/utils/workflow/dsl/patterns.d.ts +1 -8
  57. package/dist/utils/workflow/dsl/patterns.js +1 -7
  58. package/dist/visit.js +2 -0
  59. package/package.json +25 -9
  60. package/CHANGELOG.md +0 -4368
  61. package/OPTIMIZATION-PLAN.md +0 -201
  62. package/knowledge/decisions/a-channel-with-auth-false-marks-its-connect-and-disconnect-sessionless.md +0 -22
  63. package/knowledge/decisions/a-declared-scope-id-never-shadows-a-definition.md +0 -20
  64. package/knowledge/decisions/a-dsl-step-virtual-user-is-the-literal-name-in-its-options.md +0 -27
  65. package/knowledge/decisions/a-feature-flags-anyof-is-read-from-the-ast-and-an-empty-list-is-refused.md +0 -20
  66. package/knowledge/decisions/a-non-literal-string-array-reads-as-absent.md +0 -25
  67. package/knowledge/decisions/a-permission-with-an-underscore-data-parameter-takes-no-data.md +0 -20
  68. package/knowledge/decisions/a-provider-login-virtual-user-is-never-runnable.md +0 -27
  69. package/knowledge/decisions/a-remote-addon-bound-credential-or-secret-must-be-wired.md +0 -27
  70. package/knowledge/decisions/a-route-auth-overrides-its-groups-auth.md +0 -22
  71. package/knowledge/decisions/a-route-requires-a-session-when-any-auth-signal-says-so.md +0 -29
  72. package/knowledge/decisions/a-webhook-source-with-verify-gets-a-singleton-secret-credential.md +0 -21
  73. package/knowledge/decisions/a-wire-with-auth-disabled-cannot-point-at-a-pikkufunc.md +0 -21
  74. package/knowledge/decisions/addon-contracts-cannot-wire-or-declare-handlers.md +0 -25
  75. package/knowledge/decisions/addon-declarations-name-the-secrets-and-credentials-the-app-lends-it.md +0 -19
  76. package/knowledge/decisions/an-inline-wiring-function-is-sessionless-unless-built-with-pikkufunc.md +0 -23
  77. package/knowledge/decisions/better-auth-admin-plugin-is-refused.md +0 -24
  78. package/knowledge/decisions/define-screens-scopes-must-be-string-literals.md +0 -18
  79. package/knowledge/decisions/index.md +0 -38
  80. package/knowledge/decisions/inspector-records-a-non-literal-global-grant-as-source-text.md +0 -20
  81. package/knowledge/decisions/only-one-pikku-better-auth-export-per-codebase.md +0 -19
  82. package/knowledge/decisions/scope-references-and-secret-aliases-are-critical-diagnostics.md +0 -23
  83. package/knowledge/decisions/scope-segments-may-not-be-wildcards-or-contain-colons.md +0 -23
  84. package/knowledge/decisions/shorthand-permission-entries-are-extracted.md +0 -22
  85. package/knowledge/decisions/system-roles-declare-literal-unqualified-scopes.md +0 -28
  86. package/knowledge/decisions/the-inspector-records-agent-permissions-only-to-aggregate-services.md +0 -14
  87. package/knowledge/decisions/vault-secrets-reach-no-sink-and-pii-stays-out-of-logs-and-webhooks.md +0 -30
  88. package/knowledge/decisions/webhook-gateway-wrappers-are-sessionless-and-gateway-auth-runs-inside-the-handler.md +0 -26
  89. package/knowledge/index.md +0 -14
  90. package/knowledge/questions/does-a-non-literal-remote-addon-auth-get-checked.md +0 -26
  91. package/knowledge/questions/index.md +0 -16
  92. package/knowledge/questions/is-an-incomplete-oauth2-block-dropped-without-a-log.md +0 -23
  93. package/knowledge/questions/should-app-functions-named-like-scenario-instrumentation-be-refused.md +0 -28
  94. package/knowledge/questions/should-the-openapi-document-default-every-operation-to-a-bearer-or-api-key.md +0 -22
  95. package/run-tests.sh +0 -68
  96. package/src/utils/test-data/inspector-state.json +0 -1687
package/CHANGELOG.md DELETED
@@ -1,4368 +0,0 @@
1
- ## 0.12.106
2
-
3
- ### Patch Changes
4
-
5
- - fdcc821: Fabric is gone from the open toolchain. The previous names are removed with no aliases.
6
-
7
- The CLI group is `pikku cloud …` and the export is `@pikku/cli/cloud`. The project link is the `cloud` key of `pikku.config.json` (`cloud.projectId`), the CLI keeps its local state in `~/.pikkustudio` (run `pikku cloud login` again), and the project config file is `pikkustudio.config.json`.
8
-
9
- Every environment variable drops the `FABRIC_` prefix for `PIKKUSTUDIO_` with the same suffix: `PIKKUSTUDIO_API_URL`, `PIKKUSTUDIO_PROJECT_ID`, `PIKKUSTUDIO_TOKEN`, `PIKKUSTUDIO_OPERATOR_TOKEN`, `PIKKUSTUDIO_AUTH_PUBLIC_KEY`, `PIKKUSTUDIO_STAGE_ID` and the rest. Rename them wherever you set them.
10
-
11
- Wire ids: the operator sign-in route is `/sign-in/cloud`, the plugin and mode literal is `'cloud'`, the Better Auth plugin is `pikkuCloud`, its session field is `pikkustudioadmin`, and the signature header is `x-cloud-sig`. The cloud API calls are `getCloudLogs`, `getCloudLogsByStageKind`, `cloudCliProjects`, `cloudCliDeployments`, `providedByCloud` and `recordCloudAnalyticsEvents`. Addon packages are `@pikkustudio/*`, scaffolded files live under `scaffold/cloud` and `__cloud_*__`, and the API hosts are the `pikkustudio.app` and `pikkustudio.dev` domains.
12
-
13
- `pikku skills install --fabric` is `--cloud`, the install group is `cloud`, and the skills are `pikku-cloud`, `pikku-cloud-debug`, `pikku-admin-to-cloud` and `pikku-blueprint-to-cloud`.
14
-
15
- The stale analytics forwarder copies are removed from the online-shop example and the starter template.
16
-
17
- The sealed-box salt stays `pikkufabric/sealed-box/v1`: it is a cryptographic constant that already-sealed secrets depend on.
18
-
19
- - 33b102d: Virtual users now have one name everywhere. The id column of `virtual_user_run` and `virtual_user_schedule` is `virtual_user_id` (a migration renames it in place), the CLI group is `pikku virtual-user …`, and the declarations and imports live under `@pikku/core/virtual-user`. The previous names are removed with no aliases.
20
-
21
- The default virtual user email domain is now `virtualuser.invalid`: apps that rely on the default domain get new accounts at the next provisioning (`pikku virtual-user sync`, deploy, or `pikku dev` start), and the old rows stay unused.
22
-
23
- The sign-in surface carries the same name. The route is `/sign-in/virtual-user`, the environment variables are `PIKKU_ALLOW_VIRTUAL_USER_SIGN_IN` and the `PIKKU_DEV_VIRTUAL_USER_SIGN_IN` / `VITE_*` equivalents, the Better Auth plugin is `pikkuVirtualUser`, the scenario step option and wire member are `virtualUser` and `virtualUsers`, and the Better Auth user flag column is `virtual_user`. Rename the environment variables wherever you set them, and add a migration that renames the flag column on the `user` table. Nothing is aliased.
24
-
25
- The conversation runner is now `@pikku/core/virtual-user/conversation`, replacing the old `-flow` subpath. Update the import; the old subpath is gone.
26
-
27
- - Updated dependencies [fdcc821]
28
- - Updated dependencies [33b102d]
29
- - Updated dependencies [6a25c70]
30
- - Updated dependencies [a084c3b]
31
- - @pikku/core@0.12.142
32
-
33
- ## 0.12.105
34
-
35
- ### Patch Changes
36
-
37
- - b04e444: Fix CLI commands that come from an addon package. Auth and permissions set on such a command are now merged into the addon's own function instead of replacing it, and the command's input is shaped by the addon's own schema (for example a comma list becomes an array). The inspector now also resolves `...refCLI('addon:contract')`, spreads and `defineCLICommands` identifiers inside a command's `subcommands`.
38
- - Updated dependencies [b04e444]
39
- - Updated dependencies [d3d8a98]
40
- - Updated dependencies [658da50]
41
- - @pikku/core@0.12.141
42
-
43
- ## 0.12.104
44
-
45
- ### Patch Changes
46
-
47
- - 5464b86: Add the `pikku verify` command over `@pikku/code-edit/verify`: codegen, the backend and frontend type-checks and static correctness checks as structured findings with fix hints. The inspector warns about single-step workflows (PKU644) and PKU111 now names the file and line.
48
- - Updated dependencies [f396538]
49
- - @pikku/core@0.12.140
50
-
51
- ## 0.12.103
52
-
53
- ### Patch Changes
54
-
55
- - 53ac40d: An addon can ship screens: `wireAddon({ ui: true })` mounts what the package declares with `defineScreens`, the inspector reads them, `pikku all` emits the screens manifest and the installed-addons registry, and `x-pikku-addon` narrows a session to the addon's role. `pikku new addon --ui` scaffolds one; its tables must be prefixed `<name>_`.
56
- - Updated dependencies [a196a21]
57
- - Updated dependencies [53ac40d]
58
- - Updated dependencies [443fd40]
59
- - Updated dependencies [56c8047]
60
- - @pikku/core@0.12.139
61
-
62
- ## 0.12.102
63
-
64
- ### Patch Changes
65
-
66
- - 3c0b151: `pikku validate` checks block libraries. The project's `pikku.config.json` lists them by package name under `blocks`; each listed package's `src/blocks` is checked. A block that renders a literal string is an error, because its words have to come from messages, and a block with no `.stories.tsx` is a warning. A block with no errors is reported as productized. A listed name that matches no package, or a package with no `src/blocks`, is an error.
67
- - 0963b73: Every scope tree now records where it came from: `origin` on `ScopeDefinitionMeta` is `{ kind: 'app' }` for the app's own `defineScope`, `{ kind: 'generated' }` for a tree the CLI wrote into the project (a `.gen.ts`, or the `app` root derived from personas), and `{ kind: 'addon', package, displayName }` for one an installed addon declared. An addon's build stamps its own package and display name on its trees, and the host keeps that name while recording the package it actually installed. `MetaService.getScopesMeta()` reads the result, and the console's all-meta payload carries it as `scopes`.
68
-
69
- The console's Scopes page becomes **Permissions** and is grouped by that origin: the app's own permissions first, then what Pikku generated into the project, then one card per addon (Pikku's own before third-party, each alphabetical), each card folding to its header — the app's own starts open, every addon's starts folded, and any search or filter opens them all. Above the cards, two filters narrow the page to what one role is given (or to what no role is given yet) and to one source. A permission group no longer says "Not part of any role yet" above lines that are each given to a role.
70
-
71
- - Updated dependencies [0c865d9]
72
- - Updated dependencies [c36b066]
73
- - Updated dependencies [0963b73]
74
- - @pikku/core@0.12.137
75
-
76
- ## 0.12.101
77
-
78
- ### Patch Changes
79
-
80
- - 9112494: `pikku knowledge validate` reports code that no note describes as a warning rather than info, and raises one `PKU960` diagnostic for it, so `--fail-on-warn` can gate on it and the "consistent" line no longer prints while notes are missing. A plain run still exits 0: only errors fail it.
81
-
82
- ## 0.12.100
83
-
84
- ### Patch Changes
85
-
86
- - 149faae: Webhook `receive` steps lose their boilerplate. `pikkuWebhookReceive` (from `#pikku/trigger` or `#pikku/addon/trigger`) declares one: it is public, typed to the raw request with a required `http`, and never registered as an RPC, and the inspector rejects a `receive` declared with any other wrapper. `parseJson` (in `@pikku/core/utils`, generated as `#pikku/utils` and `#pikku/addon/utils`) parses text or bytes and answers a body that is not JSON with a 400. A webhook source whose `method` includes `'head'` answers HEAD probes itself with a 200.
87
-
88
- `receive` no longer returns `{ respond }`: a handshake returns nothing and writes its answer to `http.response`, like any other HTTP function. The webhook route no longer returns a fetch `Response` either. An HTTP function that returns nothing is now answered with whatever status its response has (200 unless it set another), no longer a forced 204. A webhook receive is also left out of contract versioning, since nothing but its own route calls it.
89
-
90
- - Updated dependencies [149faae]
91
- - @pikku/core@0.12.136
92
-
93
- ## 0.12.99
94
-
95
- ### Patch Changes
96
-
97
- - ce3e5f5: Saga compensation for workflows. A function declares `compensate` inline; when a workflow step fails, completed steps are undone newest-first as durable `<step>:compensate` steps. New run statuses `compensating`, `compensated` and `compensation_failed`, `workflow.milestone(name)` to bound the unwind, `{ compensate: false }` to opt a call out, nested-workflow unwinding, and `PikkuWorkflowService.cancelRun` which unwinds too. Graph nodes replace `onError` with `recover` (`nodeId`, `nodeId[]` or `'ignore'`), exposing the error as `wire.graph.recoveringFrom`. **Breaking:** the DSL `onError` step option and the graph `onError` node field are removed; `getRunSteps` is now abstract on `PikkuWorkflowService`.
98
- - Updated dependencies [ce3e5f5]
99
- - Updated dependencies [fb36c37]
100
- - @pikku/core@0.12.135
101
-
102
- ## 0.12.98
103
-
104
- ### Patch Changes
105
-
106
- - 8ac25a8: An OAuth2 credential can set `scopeSeparator` for a provider that wants scopes joined by something other than a space (Twist wants a comma). A credential with no scopes already sent no `scope` parameter; that is now tested.
107
- - 6606777: A webhook source can declare how its requests are signed, and the runner checks every request before `receive` runs:
108
-
109
- ```ts
110
- wireTriggerWebhookSource({
111
- name: 'github',
112
- verify: {
113
- hmac: {
114
- header: 'x-hub-signature-256',
115
- prefix: 'sha256=',
116
- algorithm: 'sha256',
117
- encoding: 'hex',
118
- },
119
- },
120
- receive: githubWebhookReceive,
121
- })
122
- ```
123
-
124
- `verify` is an HMAC over the raw body, a shared token or a public-key signature in one header, or a function `(request, secret, services) => boolean` for anything else. A request is refused while the secret is unset or when the signature does not match. A request without a body reaches `receive` unchecked so handshakes still work, but it may only be answered: events from it are refused.
125
-
126
- Declaring `verify` declares the secret's credential too, so it needs no `defineCredential`. It is a singleton string named `<source>WebhookSecret` in camelCase (`microsoft-outlook` → `microsoftOutlookWebhookSecret`, see `webhookSecretCredentialName`), or whatever `credential` names, described by `credentialDescription`.
127
-
128
- `@pikku/core/hmac` gains `hmacDigest`, `verifyHmacSignature` and `verifyPublicKeySignature`. `WebhookSigningSecret` is deprecated.
129
-
130
- - Updated dependencies [d327fa5]
131
- - Updated dependencies [942ebdd]
132
- - Updated dependencies [8ac25a8]
133
- - Updated dependencies [6606777]
134
- - @pikku/core@0.12.132
135
-
136
- ## 0.12.97
137
-
138
- ### Patch Changes
139
-
140
- - 26dbfc0: A `func` written inline in a `wireCLI` command now registers its function metadata under `cli:<program>:<command path>`, with `sessionless` taken from the helper it was built with. Before, the command pointed at an id nothing had registered. An inline channel `onMessage` still fails the build with the named "No function metadata found" error, and a test now pins that.
141
- - 2dcc651: The inspector finds a project's source on Windows: its root directory is compared with TypeScript's forward-slash file names, not the backslash path `path.resolve` returns.
142
- - Updated dependencies [58cccc1]
143
- - Updated dependencies [a26c60e]
144
- - @pikku/core@0.12.131
145
-
146
- ## 0.12.96
147
-
148
- ### Patch Changes
149
-
150
- - dfcd351: The deploy planner now binds a unit to the units of the functions its functions call with `rpc.invoke('name')` / `rpc.remote('name')`. Before, a caller and callee split into different units (e.g. a no-service function in `svc-base` calling a DB function in `svc-kysely`) got no service binding and failed on the deployed stage with "No service binding for function". The inspector records literal RPC names per function as `invokes` (single, double or substitution-free template quotes; `rpc!`, `wire.rpc`) and warns on a computed name, which the planner cannot see (#1883).
151
- - cf40182: Deployed units that call `rpc.startWorkflow('x')` now get x's meta, so the call no longer fails with `WorkflowNotFoundError`. The inspector records `startsWorkflows` for literal `rpc.startWorkflow(...)` calls. It warns when a handler computes the workflow name or passes `rpc` to a helper, because the planner cannot see those calls.
152
-
153
- With workflow queues, the deploy planner gives a starter unit the workflow meta and orchestrator queue meta only (new `--workflowMeta` filter), plus `workflow-state` and `queue` services. Without queues the start runs inline, so the whole workflow is bundled. Core's `startWorkflow` now requires the workflow registration only for inline runs; queued runs need only the meta.
154
-
155
- - f817f1c: Schema generation no longer re-walks every source file for each type. ts-json-schema-generator looks each root type up by name by scanning the whole program, so a project with 677 types scanned it 677 times. The inspector now builds that name index once per program. On the e2e project, TS schema generation drops from 8.7s to 0.1s and a cold `pikku all` from 38s to 8s, with identical output.
156
- - 5bce779: Webhook trigger sources are off until someone turns them on, and addons declare their own.
157
-
158
- An addon calls `wireTriggerWebhookSource` in its own package, and an app that wires the addon gets the source (its route included) without declaring it: named and routed after the addon's namespace, so two instances get one each. A source the app declares under the same name wins.
159
-
160
- Every source now has an `enabled` switch in the `triggerSourceStore`, off by default. `reconcileTriggerSources` registers only enabled sources and records each one's `baseUrl` and `labelPrefix`; a disabled source's route answers 404 without running `receive`. New in core: `enableTriggerSource` registers a source with its provider and `disableTriggerSource` stops it receiving, then tears it down, both at the recorded address unless one is given. The admin addon exposes them as `triggerSourceEnable` and `triggerSourceDisable`. The `pikkuTriggerSource` table gains `enabled`, `baseUrl` and `labelPrefix`: run `pikku db generate` for the migration.
161
-
162
- - Updated dependencies [dfcd351]
163
- - Updated dependencies [cf40182]
164
- - Updated dependencies [5bce779]
165
- - Updated dependencies [5bce779]
166
- - @pikku/core@0.12.130
167
-
168
- ## 0.12.95
169
-
170
- ### Patch Changes
171
-
172
- - 6ecf80e: Stop the services-destructure lint (PKU410) firing on the stub a wiring registers for its function. Since inline functions carry a source file, a queue worker wired to a named function failed the build with a critical PKU410 even though that function destructures its services.
173
- - 2612a67: The services-destructure lint also skips the `helper` wiring functions `ensureFunctionMetadata` synthesizes, alongside the `inline` ones it already skips, and a test pins that an inline wiring function that records a source file is not flagged.
174
-
175
- ## 0.12.94
176
-
177
- ### Patch Changes
178
-
179
- - 4323bcf: Inline wiring functions now record their source file, so `pikku release` leaves scaffold-injected ones out of the API surface.
180
-
181
- ## 0.12.93
182
-
183
- ### Patch Changes
184
-
185
- - 658f047: A function whose services are all singletons no longer builds wire services, so an addon's credential-bound wire factory does not run for, or fail, a webhook `receive` step that needs no connection. The inspector decides this from the `Services` type and records it as `singletonServicesOnly` on the function's runtime meta; the runner only reads the flag.
186
- - 658f047: Webhook signing secrets live in the credential store. `WebhookSigningSecret.fromCredential(provider, credentialService, name)` reads the secret per delivery via `load()`, so a `setup` step (or a handshake) that stores a new one with `credentialService.set` takes effect without a deploy.
187
-
188
- Breaking: `setup` no longer returns `secret`, `wireTriggerWebhookSource` no longer takes `secret`, lifecycle outcomes drop `secretName`/`secret`, and `pikku webhooks setup` drops `--secretsOut`.
189
-
190
- - 658f047: A webhook source's `method` may be a list, mounting one route per method, for providers that verify the URL with a GET and deliver events with a POST (WhatsApp, Strava, Onfleet, Mailchimp), or a HEAD (Trello, Mandrill, SurveyMonkey).
191
- - Updated dependencies [658f047]
192
- - Updated dependencies [de63ab2]
193
- - Updated dependencies [658f047]
194
- - Updated dependencies [658f047]
195
- - Updated dependencies [658f047]
196
- - Updated dependencies [658f047]
197
- - @pikku/core@0.12.128
198
-
199
- ## 0.12.92
200
-
201
- ### Patch Changes
202
-
203
- - f84e173: An addon wired from a workspace package now resolves from that package. The installed-addon check (PKU340), the remote addon `devDependencies` check and `pikku db generate`'s addon schemas all looked only at the project root, so an addon declared where it is wired — the only place bun links it — failed codegen unless it was also declared at the root.
204
- - Updated dependencies [f02585e]
205
- - Updated dependencies [5586749]
206
- - @pikku/core@0.12.127
207
-
208
- ## 0.12.91
209
-
210
- ### Patch Changes
211
-
212
- - 5e93f30: Add `defineOutgoingWebhook({ event, title, description?, payload })` in `@pikku/core/webhook`. The CLI collects every exported declaration into `.pikku/webhooks/pikku-outgoing-webhooks-meta.gen.json` and `pikku-outgoing-webhooks.gen.ts`, which exports `OutgoingWebhooksMap`, `TypedWebhookService` and `typedWebhookService(service)`: `send` checks `data` against the declared payload for a declared event and accepts any other event unchanged. `MetaService.getOutgoingWebhooksMeta()` and the console addon's `outgoingWebhooksMeta` serve the declarations.
213
- - 5e93f30: Add `wireTriggerWebhookSource({ name, method?, route?, secret?, events, receive?, check?, setup?, teardown? })` in `#pikku/trigger`. Each source becomes a `POST /webhooks/<name>` route whose events are validated against their schemas and queued on `pikku-incoming-webhooks` through `IncomingWebhookService`; a generated worker runs the matching `wireTrigger({ name: '<source>:<event>' })` and the queue retries it on failure. `pikku webhooks status | setup | teardown --url --labelPrefix [--previous]` registers the routes with the provider and prints one JSON line per source.
214
-
215
- `KyselyIncomingWebhookService` (with the `incoming-webhook` schema) records a receipt per event, drops a provider's redelivery of an event it already accepted, and keeps each dispatch's attempts and last error. `pikku dev` and `pikku serve` use it when a Kysely database is configured.
216
-
217
- - Updated dependencies [5e93f30]
218
- - Updated dependencies [5e93f30]
219
- - @pikku/core@0.12.125
220
-
221
- ## 0.12.90
222
-
223
- ### Patch Changes
224
-
225
- - 4e05a10: Print the TypeScript text for a Zod schema directly, dropping `zod-to-ts`.
226
-
227
- `processZodSchema` built a TypeScript AST only to print it straight back to a
228
- string. `zodToTypeText` walks Zod's own definitions instead, which removes
229
- `zod-to-ts`, `ts.createPrinter`, `ts.EmitHint` and `ts.createSourceFile` from
230
- the Zod path — it no longer touches the compiler API at all. `@pikku/cli`
231
- declared `zod-to-ts` without importing it; that is dropped too.
232
-
233
- A defaulted field is now optional in the generated type. `processZodSchema`
234
- already strips defaulted fields out of the JSON Schema's `required`, so the two
235
- halves of the same contract disagreed: the validator accepted a payload that
236
- omitted the field, while the type said a caller had to pass it.
237
-
238
- - Updated dependencies [1394385]
239
- - @pikku/core@0.12.124
240
-
241
- ## 0.12.89
242
-
243
- ### Patch Changes
244
-
245
- - bc488cf: fix(inspector): an explicit `auth: false` declares an exposed sessionless function public, so PKU574 no longer warns about it
246
-
247
- A genuinely public endpoint — a published programme, a health check — had no
248
- honest way to quiet PKU574: the only options were an always-true permission or
249
- `permissionsInBody: true`, both of which claim a gate that does not exist. The
250
- inspector now records `auth` on function meta exactly as written instead of
251
- dropping `false`, and the check treats an explicit `auth: false` as the author
252
- declaring the function public on purpose. A sessionless function that leaves
253
- `auth` out still warns.
254
-
255
- - Updated dependencies [50b59a3]
256
- - Updated dependencies [2b946e9]
257
- - Updated dependencies [bc488cf]
258
- - @pikku/core@0.12.123
259
-
260
- ## 0.12.88
261
-
262
- ### Patch Changes
263
-
264
- - 55ab4d1: An addon's metadata now resolves from the package that calls `wireAddon`, as well as from the repo root, so a workspace addon listed only in `packages/functions` loads. When it still cannot be found, the warning names the package, the directories tried and the fix: add the dependency and install, or build the addon if it is installed but has no generated metadata.
265
- - f6c1dd6: A fresh project with Better Auth no longer gets PKU951 for `BETTER_AUTH_SECRET` on its first `pikku all`. The secret is declared by the generated `auth-secrets.gen.ts`, which did not exist yet when the first inspection ran.
266
- - 0210e96: Review fixes for the OpenAPI addon onboarding:
267
-
268
- - A delegated sign-in whose email the upstream did not return, including a login typed as an email, never links to an existing user. An authenticator that omits `syntheticEmail` counts as synthetic.
269
- - `pikkuActor` credentials take an optional `remove`, which drops a credential the environment no longer sets. The actor log line names the user id, not the email.
270
- - Basic credentials are UTF-8 encoded, and a Swagger 2 `application` OAuth flow keeps its token URL.
271
- - `pikku new addon` writes a `file:` path relative to each package when the app is not a workspace, and reports an auth.ts factory it cannot edit instead of half-wiring it.
272
- - The inspector reads an addon from the package that declares it before the root.
273
- - The dev credentials key file is created exclusively, so two `pikku dev` processes agree on one key.
274
-
275
- - 86c2f1d: `pikku dev` keeps stored credentials in the dev database, so connected accounts and delegated sign-ins survive a restart. The key comes from `PIKKU_DEV_CREDENTIALS_KEY`, or is generated once into `.pikku-runtime/dev-credentials.key`. A project that declares a credential now gets the credential tables in its generated migration; without them dev falls back to the in-memory store and says so once.
276
- - da9b931: An inline `input`/`output` schema (PKU489) is now an `error` diagnostic instead of a critical one, so `pikku dev` keeps running. The function is validated against its TypeScript type until the schema is extracted to an exported variable; `--fail-on-error` still blocks it.
277
- - Updated dependencies [e84abd0]
278
- - @pikku/core@0.12.121
279
-
280
- ## 0.12.87
281
-
282
- ### Patch Changes
283
-
284
- - a95179a: An AI agent's `providerOptions` now reaches the generated metadata.
285
-
286
- The inspector read `model`, `temperature`, `maxSteps` and `toolChoice` off an agent declaration but never `providerOptions`, so `agentsMeta` always showed an agent with no provider configuration — even though the type says it carries one. Runs were unaffected, since the runner takes the value from the live agent object, which is exactly why the gap went unnoticed by everything except what reads the compiled meta: the console's agent view and `infra.json`.
287
-
288
- The value is read as an object literal of strings, numbers, booleans, arrays and nested objects, all-or-nothing. A computed one is reported as `PKU156` rather than silently dropped.
289
-
290
- - 145b32b: Generated type aliases are deterministic
291
-
292
- A type name that collided across files got a `Math.random()` suffix, so every
293
- `pikku all` emitted different names into `pikku-agent-map.gen.d.ts` and
294
- `pikku-workflow-map.gen.d.ts`. Those files sit in the schema generator's
295
- dependency closure, so rewriting them invalidated the schema cache partway
296
- through the same run and forced a full `ts-json-schema-generator` pass each
297
- time. The suffix now comes from the declaring file's path.
298
-
299
- - 42b7ac3: The app decides which of an addon's functions `rpc.exposed` reaches
300
-
301
- `wireAddon` takes `expose?: boolean | string[]`, mirroring `mcp`. Unset or
302
- `true` keeps the functions the addon declared `expose: true`; `false` exposes
303
- none of the instance's functions; a list names exactly the functions to expose,
304
- whether or not the addon declared them, typed against the addon's function
305
- names. A listed name the addon does not publish fails the build with PKU343, a value that is not written inline fails it with
306
- PKU344,
307
- and the deploy analyzer's per-addon unit carries only what the wiring exposes.
308
-
309
- - Updated dependencies [4a9dcd2]
310
- - Updated dependencies [5ab24ad]
311
- - Updated dependencies [42b7ac3]
312
- - @pikku/core@0.12.120
313
-
314
- ## 0.12.86
315
-
316
- ### Patch Changes
317
-
318
- - f05fb4e: A scenario that belongs to no feature is now reported.
319
-
320
- Features are what the guide is written against: prose cites a feature, and the compiler fills that citation with the evidence from the scenarios the feature owns. A scenario nobody listed is therefore invisible — it runs, it captures screenshots and recordings, and none of it ever reaches a page. That failure is silent today, and it is the single reason two real projects have hundreds of captures bound to nothing.
321
-
322
- The inspector now walks every feature's scenario list, and any scenario not claimed by one is reported as `PKU682`. It is an error rather than a critical, so `pikku all` keeps passing by default and the list is there to work through; `--fail-on-error` makes it a wall once a project has caught up.
323
-
324
- Membership reads the scenario each entry names, not the entry itself. A `data` the AST cannot evaluate — `'a'.repeat(64)`, a call, anything computed — leaves the entry partial for the console but says nothing about who owns the scenario, and the check no longer treats the two as the same thing.
325
-
326
- An entry that names no scenario at all — a spread, a `.map()` — does leave membership unknown. That only matters once something is already unowned, so the check reports it then, as a warn naming both the unowned scenarios and the features that could be hiding them, rather than accusing a scenario that may well be in one of those arrays.
327
-
328
- - b4a895e: `pikku scenario guide` writes the user guide a scenario suite already contains. A feature reads as a page and a scenario as a section, and a run leaves screenshots behind with the captions their author took them under — the command joins that to the editorial prose a project checks in under `docs/` and writes markdown. It renders no HTML, ships no components, resolves no asset URLs and calls no model: an image is an ordinary relative `![caption](path)`, and whoever consumes the markdown rewrites the paths.
329
-
330
- A page cites a feature by leaving the marker pair where the block belongs:
331
-
332
- ```markdown
333
- ---
334
- title: Deployments
335
- ---
336
-
337
- A deployment is one tracked shipment of your app.
338
-
339
- <!-- pikku:guide feature=deploymentsFeature -->
340
- <!-- /pikku:guide -->
341
-
342
- ## Does my app go down during a deploy?
343
- ```
344
-
345
- That one line does both halves of the job. It says _where_ the block goes, which a frontmatter list cannot express, and it is what the coverage gate counts to decide _whether_ a feature is documented at all. The mapping stays many-to-many and falls out of the union of every marker in the tree. A rebuild rewrites exactly the regions between the markers, so every sentence a human wrote around them survives.
346
-
347
- **The steps are evidence, not content.** A generated block is the scenario's title, the description its author wrote, and the shots it filed — never a numbered Given/When/Then ladder, which is a test report and not something anybody arrives at a documentation page wanting. The sentences are still what the guide is kept honest against: `docs/.guide.lock` records a hash of each feature's step sentences and artifact ids, deliberately not of the image bytes. Restyling a UI changes every screenshot and no sentence; inserting or renaming a step changes what the prose was describing, and the page is reported stale. The lock is generated and checked in, so no hash is ever typed or merged by hand — and a tree whose lock is untracked reports every page as current forever.
348
-
349
- Every registered feature has to be cited by some page, and a feature that is pure plumbing says so rather than being written about — `pikkuFeature({ document: false })`, threaded through the inspector and `FeatureMeta`. An uncited feature fails the command by name; `--allow-undocumented` downgrades that one failure to a report. A page citing a feature id that is not registered stays an error either way.
350
-
351
- A guide is only written out of a run that can stand behind it. A run that failed or was killed halfway is refused, because a page is a claim that the product does what it says. So is a narrowed one: `pikku scenario run --flows`/`--features`/`--tags` leaves out scenarios the suite has, and a guide built from it would describe those flows as though they do not exist. `ScenarioRunRecord.selection` records the filters a run was selected with, since nothing in the results afterwards can tell a suite of forty from forty that were asked for.
352
-
353
- Results are joined to features by `featureId`, falling back to the display name only for records written before that field existed — a title is rewritten freely and two features may share one.
354
-
355
- Emission is deterministic — identical inputs give byte-identical output, and no timestamp goes in that did not come from the run record. Frontmatter the compiler does not own (`slug`, `draft`, `sidebar_position`, anything else a docs site reads) passes through untouched, and a source written with CRLF line endings is read as having frontmatter.
356
-
357
- - Updated dependencies [b4a895e]
358
- - Updated dependencies [b4a895e]
359
- - @pikku/core@0.12.118
360
-
361
- ## 0.12.85
362
-
363
- ### Patch Changes
364
-
365
- - b312867: A project can now serve several MCP endpoints, one per connector.
366
-
367
- Until now every MCP tool in a project was pooled onto a single `/mcp`, so a hub offering three connectors offered one endpoint listing all three connectors' tools at once. A client pointed at it saw tools it had no business calling, and the only way to give a connector an endpoint of its own was to give it a deployment of its own — three deploys, three bills, three service graphs.
368
-
369
- `wireAddon` gains `mcpEndpoint`. `true` serves that instance's tools at `/mcp/<name>`; a string is the path, used as given. Leaving it unset keeps the tools on the shared endpoint, which is where they have always been, so nothing existing moves.
370
-
371
- A surfaced instance now gets its own manifest (`.pikku/mcp/mcp.<name>.gen.json`, carrying the path it answers on), its own deploy unit (`mcp-<name>`, routed on that path), and its own MCP server — with its own tool list, so a client pointed at one endpoint never sees another's tools. The plumbing for the per-surface manifest and unit already existed in `deploy apply`; nothing had ever produced one.
372
-
373
- `pikku dev` mounts every endpoint the generated tree describes, not just the default one. Without that a project that moved its tools onto their own endpoints would have served nothing locally at all — the default manifest it reads is empty precisely because they moved — and the only way to try a connector would have been to deploy it.
374
-
375
- The node and bun transports take `mcpSurfaces` alongside `mcpJson` and mount each at its own path, longest path first. `/mcp` claims everything beneath `/mcp/`, so without that ordering the default endpoint answers `/mcp/weather` and the surface's tools are unreachable.
376
-
377
- OAuth discovery is split between the endpoints rather than duplicated across them. RFC 9728 folds a resource's path into its well-known route, so each endpoint's own document is already distinct, but the path-less `/.well-known/oauth-protected-resource` predates that and describes whichever resource answers it. Only the default endpoint claims it — otherwise every unit registers the same route and the provider's router decides which resource a client is told about, and in dev a client probing it is described whichever surface sorted first.
378
-
379
- - Updated dependencies [87971bd]
380
- - Updated dependencies [b312867]
381
- - Updated dependencies [51bd35a]
382
- - @pikku/core@0.12.116
383
-
384
- ## 0.12.84
385
-
386
- ### Patch Changes
387
-
388
- - 238902c: A channel's `auth` flag now reaches its meta. The inspector already read it to mark the connect and disconnect functions sessionless, then dropped it, so nothing downstream could tell a public channel from a private one without reading the generated source.
389
-
390
- That matters in front of the channel rather than inside it. A `wireCLI({ auth: false })` program is reachable by anyone holding its address; a router or deploy that cannot see the flag either guesses or demands a token in front of a surface it was never protecting, locking out the clients the program was opened for.
391
-
392
- The flag is only recorded when the channel actually said. A channel that never mentioned `auth` leaves it absent rather than claiming a default it did not declare, which the runtime continues to read as requiring a session.
393
-
394
- - Updated dependencies [238902c]
395
- - Updated dependencies [2fd2b22]
396
- - @pikku/core@0.12.115
397
-
398
- ## 0.12.83
399
-
400
- ### Patch Changes
401
-
402
- - 6db6a14: A wiring can now decide whether an addon's credential is per-user or deployment-wide
403
-
404
- `wireAddon`'s `credentialOverrides` takes an object as well as a rename string:
405
-
406
- ```ts
407
- wireAddon({
408
- name: 'gmail',
409
- package: '@pikku/addon-gmail',
410
- credentialOverrides: {
411
- gmailOAuth: { mode: 'wire' }, // each user connects their own
412
- calendarOAuth: { name: 'CAL', mode: 'singleton' },
413
- },
414
- })
415
- ```
416
-
417
- An addon author declares a default with `defineCredential`; the deployment decides, so one addon serves both a per-user product and a single team account.
418
-
419
- The wire credential service now resolves each credential by what it _is_ rather than by what a lookup returned: `wire` reads only the user's value, `singleton` reads the deployment's. A per-user credential can no longer pick up a platform-level value because the user has not connected — which, before, would have quietly run someone's request against the deployment's own account.
420
-
421
- A credential is never read from the secret vault. The wire credential service no longer takes a `SecretService` at all, so the only way a credential arrives is the one its mode names — the user's own value, or the deployment's.
422
-
423
- Modes are resolved at generation time into the credentials meta, so the console's connect flow reflects the wiring rather than the addon's default.
424
-
425
- Two credentials that resolve to one name are rejected unless they agree on the mode. Generation writes a single metadata entry per name, so a `wire` credential aliased onto a `singleton` one used to take whichever wiring was read last — a per-user credential served from the deployment's own account, or a deployment credential handed out per user. The inspector reports the collision against both wirings by name, and `buildCredentialResolutions` refuses it at runtime too.
426
-
427
- A mode-only override is checked against the credential it names. `{ mode: 'wire' }` renames nothing, so nothing was validated: an override naming a credential that does not exist was dropped in silence and the credential kept the mode its author declared.
428
-
429
- A credential the wiring resolves as `singleton` is no longer read out of the user's own store. Per-user values are imported first and a name already present is left alone, so a value stored under a singleton's name — from an earlier wiring, or a connect flow since rewired — decided what the deployment's own slot resolved to. It is now skipped by mode, the same way a `wire` credential is kept away from the deployment's value.
430
-
431
- The project's own credentials are registered into pikku state from the generated credentials file, so `wire.getCredential` resolves an app-level singleton the same way it resolves an addon's.
432
-
433
- `pikku new addon` no longer requires a `pikku.config.json` in the working directory. Scaffolding an addon is something you do before a project config exists, so the command now reads one when it is there and falls back to the working directory when it is not.
434
-
435
- An addon's `pikkuAddonWireServices` factory now declares the same service contract its singleton factory does: what it destructures off the parent's bag is required, and what it returns is built by the addon. Before, a service an addon built per wire was demanded from the consumer, and a wire-only addon declared no contract at all. A nested callback that names its own parameter after the factory's is no longer read as the factory's own: what it destructured went onto the addon's contract, so a consumer was asked for a service the addon never wanted from them.
436
-
437
- An OAuth2 credential now implies its app secret, so nobody hand-writes one. The client id and secret an OAuth app needs is the same shape every time — `OAuth2AppCredential`, which is what the runtime already reads it as — so the inspector registers a secret for each credential's `appCredentialSecretId`. A hand-written `defineSecret` covering that id still wins, so an author who wants their own description or `docsUrl` keeps it. The derived secret is optional, matching what every hand-written declaration chose: an addon that also authenticates by API key must still deploy without an OAuth app configured.
438
-
439
- `optional` now means a secret is not reported as missing. `getMissing()` filtered on "not configured" alone, so a secret whose declaration said absence was supported still showed up on the list of things a deployment had to go and supply — burying the ones that genuinely were. `getAllStatus()` still reports it, flagged `optional`.
440
-
441
- An OAuth2 secret carries its declaration's `optional` through code generation, to the app credential and to the token store alike: nobody connects without the client id and secret, so a deployment allowed to omit the app is never asked for its tokens either. That branch never looked at the flag before.
442
-
443
- `credentialOAuthProviders` treats an app secret that resolves `undefined` as unconfigured. An optional secret resolves rather than throws, so the absence arrived past the `catch` that was meant to skip the provider — and `.reveal()` on it threw a `TypeError` that took every `getSession` down with it, which is the exact failure that code exists to prevent.
444
-
445
- - Updated dependencies [cb8f57e]
446
- - Updated dependencies [cb8f57e]
447
- - Updated dependencies [6db6a14]
448
- - @pikku/core@0.12.114
449
-
450
- ## 0.12.82
451
-
452
- ### Patch Changes
453
-
454
- - c842054: Middleware instance ids are now allocated per group across the whole inspection, not per source file, and a build that would drop a global middleware registration fails instead of shipping.
455
-
456
- The per-group index restarted at 0 for every file, so the second file to register against a group minted ids the first already owned and overwrote its entries in `middleware.instances` — `instanceIds` listed one key twice while `count` correctly said two. For `addGlobalMiddleware` that was not cosmetic. Global middleware belongs to no wire group, so the instance map is the only record that its module must be imported, and per-unit deploy codegen emits its side-effect imports from that map. An app file registering a global middleware was erased by the generated auth scaffold registering its own, the app's module was never imported, its registration never ran — and because auth commonly lives in global middleware, a deployed unit answered every authenticated route with a 401 against a clean build log.
457
-
458
- The inspector now also records each `addGlobalMiddleware` file in `middleware.globalFiles`, a plain set that no key collision can corrupt; codegen emits the side-effect imports from it, generates the middleware file when global middleware is a project's only middleware, and asserts against the text it emitted that every such file is imported — naming the files if not, rather than letting the unit deploy without its auth gate.
459
-
460
- - dfd7019: Resolve the MCP server instance per log message, and restore the wireAddon
461
- declaration's source file.
462
- - 1469e73: The app names which of an addon's functions reach MCP
463
-
464
- `wireAddon`'s `mcp` takes a list as well as `true`. `true` still offers every
465
- function the addon declared `mcp: true`; a list names the tools this deployment
466
- offers, whether or not the addon declared them, and is typed against the
467
- function names that addon publishes — a typo is a compile error rather than a
468
- tool silently missing from the menu.
469
-
470
- - Updated dependencies [c842054]
471
- - Updated dependencies [dfd7019]
472
- - Updated dependencies [dfd7019]
473
- - Updated dependencies [9b978e7]
474
- - Updated dependencies [1469e73]
475
- - @pikku/core@0.12.113
476
-
477
- ## 0.12.81
478
-
479
- ### Patch Changes
480
-
481
- - 78237ac: The console shows feature flags as a launch board, and analytics events as a
482
- declared catalog.
483
-
484
- A flag's state is four fields — `enabled`, `rolloutPercent`, `declared`,
485
- `backed` — and none of them is the question an operator actually asks, which is
486
- how far this has got. The board answers that directly: every declared flag sits
487
- in the lane its rollout has reached, `dark` → `rolling` → `live`, with
488
- `attention` for the flags that are not resolving in a way anybody chose.
489
-
490
- Attention is evaluated before every other reading, and that ordering is the
491
- whole point rather than an implementation detail. A flag declared in code with
492
- no row in the store fails open, and it reports `enabled: true` with no rollout
493
- limit — byte-identical to a deliberate full launch. Read the fields in the
494
- obvious order and the one production-severity state on the board renders as the
495
- healthiest one. `groupFlagsByLane` also returns all four lanes whether or not
496
- they hold anything, because a board read by shape must not rearrange itself the
497
- moment a lane empties.
498
-
499
- Selecting a flag opens its controls beside the board rather than over it: the
500
- switch, the rollout bucket, and the subjects pinned on or off past that bucket.
501
- They share one surface because they are read together — "off for everyone except
502
- these three" is a switch and an override row, and an operator who has to leave
503
- one screen to see the other is the operator who turns a flag on for everybody by
504
- mistake. The panel tracks the open flag by name and re-reads it from the list, so
505
- committing a rollout moves the card and the panel together instead of leaving the
506
- panel describing the flag as it was before the operator changed it.
507
-
508
- A board whose flags come from a `FeatureFlagSource` — a provider Pikku reads but
509
- does not own — says so and offers no controls, rather than offering controls that
510
- would fail.
511
-
512
- The analytics screen is a catalog of what the code declares, read from build-time
513
- meta: the events a client may emit and the props each carries, with every prop
514
- rendered as the schema source text the declaration wrote. It is deliberately not
515
- a volume report — nothing here counts what was emitted — and saying which of the
516
- two it is turned out to be the only design question the screen had.
517
-
518
- Turning a flag on while it carries no rollout limit now asks first. That press
519
- is the one irreversible thing the board can do — with no bucket left to hold
520
- anybody back it admits everyone the flag's capabilities allow — and it used to
521
- be a single click on a switch. The panel also reports what a mutation is doing
522
- and what it refused to do: the controls it is waiting on go into a pending
523
- state, and a failed write raises an inline `<Alert>` rather than reverting in
524
- silence. The console mounts no notification provider, so the feedback is inline
525
- where the action is, which is the idiom the rest of the console already uses.
526
-
527
- `@pikku/mantine`'s theme gains four aliases, and they close a contrast defect
528
- that predates this board:
529
-
530
- ```ts
531
- '--mantine-color-orange-light': 'var(--app-surface-warning)',
532
- '--mantine-color-orange-light-color': 'var(--app-amber)',
533
- '--mantine-color-teal-light': 'var(--app-surface-success)',
534
- '--mantine-color-teal-light-color': 'var(--app-green)',
535
- ```
536
-
537
- The theme already documents two colour tiers — TEXT must clear AA, GRAPHIC only
538
- 3:1 — and already pulls Mantine's own variables into that contract rather than
539
- linting call sites. What it had not covered was Mantine's _light variants_:
540
- `<Alert color="orange">` renders #d9480f on #ffe8cc at 3.62:1, and
541
- `<Badge variant="light" color="teal">` at 4.33:1, neither reachable from an
542
- `--app-*` token because the component never asks for one. The ramp itself is
543
- deliberately untouched: Mantine conflates `--mantine-color-orange-filled`
544
- between `c="orange"` text and a `variant="filled"` background, so darkening it
545
- for light-mode text would make an existing filled badge unreadable in dark mode.
546
- Eight pre-existing `c="orange"` call sites moved onto the amber TEXT tier in the
547
- same pass.
548
-
549
- The analytics catalog is grouped by the file that declares each event, and its
550
- rows are real buttons rather than table cells carrying a tabindex.
551
-
552
- `ShellHeaderAction` gains `loading` and `testId`, and `ActionCluster` gains a
553
- `measurement` flag. `ShellHeader` sizes its actions by rendering them a second
554
- time off-screen, and that measuring copy was carrying the same `data-testid` and
555
- `data-help` as the real one — so every action on a page with a collapsing header
556
- resolved to two elements. The flags board also moved its two header actions from
557
- `actionsNode` (documented as the non-collapsing escape hatch) onto the structured
558
- `actions` array, which is what stopped them clipping off the right edge at 400px.
559
-
560
- - Updated dependencies [78237ac]
561
- - Updated dependencies [add56d2]
562
- - @pikku/core@0.12.111
563
-
564
- ## 0.12.80
565
-
566
- ### Patch Changes
567
-
568
- - 1c9a55a: Give `pikku dev` and `pikku serve` a database-backed flag store and analytics sink.
569
-
570
- Both followed `agentStorage` and friends everywhere except in being wired: with a
571
- local database present, `dev` built one for agents, workflows and scopes and left
572
- feature flags unregistered — which makes every gate resolve open — and analytics
573
- on the logger fallback. Both are now built from the same `kysely`, and dropped
574
- with a warning naming `pikku db generate` rather than failing the boot when the
575
- tables are not there yet.
576
-
577
- The inspector now counts a `featureFlag:` declaration as needing `featureFlags`
578
- and a `defineAnalyticsEvents` declaration as needing `analyticsService`, the same
579
- way a declared scope implies `scopeService`. Neither service is ever destructured
580
- — the runner reaches both out of the singleton services — so without this the
581
- declaration was invisible to service aggregation and `pikku db generate` wrote no
582
- tables for either.
583
-
584
- - 52eb461: Prune a unit's addon wiring files alongside its addon declarations, so a
585
- deployment unit no longer imports (and registers) addons whose functions the
586
- unit filter dropped. `wireAddon` now records the app file that declared it, and
587
- a file is dropped only when every addon it wires is dropped.
588
- - 1c9a55a: Feature flags, declared in source and resolved from two independent booleans.
589
-
590
- `defineFeatureFlags` declares a flag the way `defineScopes` declares a scope —
591
- the inspector collects it, the CLI emits a `FeatureFlagName` union, and a
592
- misspelled `featureFlag:` is a type error rather than a gate that silently fails
593
- open.
594
-
595
- A flag answers two questions that are not the same question. `capable` comes
596
- from the session's scopes and is per-user, advisory, and protects nothing — it
597
- hides UI. `available` comes from one global config snapshot, is caller-blind,
598
- and is the only half the runner enforces: `override(subject) ?? (enabled AND
599
- bucket)`, so "off for everyone except these three organizations" is one row, and
600
- a kill is one write rather than a fan-out. Authorization stays where it was, in
601
- the function's own `scopes:` — enforcing capability would make a flag a second
602
- authorization path OR-ing against them. An unavailable feature throws 503, not
603
- 403, because the caller was allowed; the feature was not on.
604
-
605
- `featureFlag:` is deliberately available on sessionless functions too. It reads
606
- the config, not the session, so the kill switch reaches a cron task, a queue
607
- worker and a webhook — which is where it matters most, since nobody is watching
608
- a UI to notice the feature is off.
609
-
610
- Refresh is pull-on-demand, so the TTL is the kill-switch latency. Where that is
611
- too long, `invalidate()` is public: a provider's change webhook lands on an HTTP
612
- wiring, drops the cache, and the next request does the read. It deliberately
613
- does not fetch — a burst of webhooks would be N round trips, and on a serverless
614
- runtime the isolate that took the signal may be gone before anything reads the
615
- result.
616
-
617
- Availability fails open through three layers: a fresh read, then the last good
618
- cached read, then the compiled declaration. The middle layer is load-bearing —
619
- dropping straight to the compiled fallback on a blip would switch on every flag
620
- that was deliberately dark.
621
-
622
- Backing stores split along what they can honestly do. `FeatureFlagSource` is
623
- read-only (`snapshot()`) and is what a third-party provider implements;
624
- `FeatureFlagStore` adds the write half and is for stores Pikku owns.
625
- `@pikku/kysely` ships the store, with declarations synced additively — a removed
626
- declaration is marked undeclared, never revoked, and a sync never re-enables a
627
- killed flag. Third-party providers implement the read-only half in the addons
628
- repository, over plain `fetch` rather than a vendor SDK — those poll on a timer
629
- belonging to a long-lived process, which a serverless isolate cannot hold
630
- between requests.
631
-
632
- A client asks for its flags once per session rather than per flag:
633
- `scaffold.featureFlags` generates a `GET /feature-flags` returning every
634
- declared flag resolved for the caller, keyed by this app's `FeatureFlagName`.
635
- Generated into the app rather than shipped in an addon precisely for that union
636
- — an addon never sees the host's, and could only answer
637
- `Record<string, boolean>`. It returns `show` alone: sending `available` apart
638
- from `capable` would tell every visitor which features exist but are dark.
639
-
640
- The write half is the operator's, and lives in `@pikku/addon-admin` beside the
641
- scope RPCs, under a new `admin:flags` scope. `flagList` reports `writable:
642
- false` rather than failing when flags come from a provider, because a provider's
643
- own UI is its operator surface and a console full of buttons that 500 is worse
644
- than a read-only tab. It still lists the flags: a source may report its declared
645
- set through `declaredFlags()`, and each row carries `backed`, false where the
646
- provider has never heard of a declared flag. That row is the one worth seeing —
647
- an absent row fails open, so a dark launch nobody created in PostHog is already
648
- live for everyone, and a store pikku owns can reconcile that on deploy where a
649
- provider cannot.
650
-
651
- On the client, `createFeatureFlags` fetches that map once and `useFeatureFlag`
652
- reads it synchronously after, through the same provider the analytics client
653
- hangs off. It takes a `bootstrap` map so a server-rendered page hydrates onto
654
- the answer it already computed: without one there is a gap in which neither
655
- default is right, since false hides a feature the user has and true flashes one
656
- they do not. A failed refresh keeps the map already on screen rather than
657
- relabelling every flag on a blip.
658
-
659
- - Updated dependencies [1c9a55a]
660
- - Updated dependencies [1c9a55a]
661
- - Updated dependencies [1c9a55a]
662
- - @pikku/core@0.12.110
663
-
664
- ## 0.12.79
665
-
666
- ### Patch Changes
667
-
668
- - 54138b1: Product analytics moves into core behind a `scaffold.analytics` generator.
669
-
670
- An app declares what it measures, and nothing else:
671
-
672
- ```ts
673
- import { defineAnalyticsEvents } from '#pikku/analytics'
674
-
675
- export const analyticsEvents = defineAnalyticsEvents({
676
- page_viewed: z.object({ path: z.string() }),
677
- todo_created: z.object({ priority: z.enum(['low', 'medium', 'high']) }),
678
- })
679
- ```
680
-
681
- The definer reaches an app through the generated `#pikku/analytics` leaf, the
682
- way `defineFeatureFlags` reaches it through `#pikku/scopes` — an app declares
683
- what it measures without importing out of core. The leaf is written on every
684
- run, whatever `scaffold.analytics` says: the declaration is how a project names
685
- what it measures, and the scaffold only decides whether an ingest wire is
686
- generated for it. Its path is `analyticsTypesFile`, defaulting to
687
- `<outDir>/analytics/pikku-analytics-types.gen.ts`. It carries the sink surface
688
- as well as the definer — `AnalyticsService`, `AnalyticsRecord`,
689
- `AnalyticsIdentity`, `AnalyticsClientContext`, `AnalyticsLog`,
690
- `LoggerAnalyticsService` — because writing the destination events go to is the
691
- other half of the same job, and both halves should come through one door.
692
-
693
- The name is the key, so it is never repeated as a `z.literal` inside the
694
- schema. Declare in as many modules as suits the project — a feature declares
695
- its events beside its functions — and the CLI unions them into the generated
696
- `/analytics` ingest and its schemas. The inspector finds the declarations the
697
- way it finds every other wiring, so there is no path to configure.
698
-
699
- Where events go is not part of the declaration. `services.analytics` is the
700
- request-scoped buffer a function records into, typed against the declared
701
- names:
702
-
703
- ```ts
704
- await analytics.record({ name: 'todo_created', priority: 'high' })
705
- ```
706
-
707
- It stamps identity, trace and wire fields from the invocation, buffers for the
708
- length of the call and flushes once when it ends. Identity is always
709
- server-side and never read from a request body, which is what makes the
710
- unauthenticated ingest safe to expose.
711
-
712
- The transport behind it is an `AnalyticsService` on singleton services — the
713
- one swappable slot. With none wired the runner installs
714
- `LoggerAnalyticsService`, so an app that turns the scaffold on can watch events
715
- arrive without first choosing a store; a platform injects its own through
716
- singleton services, and an app that wants neither sets `analyticsService` in
717
- its own `services.ts`, which runs last and wins. There is no process-global
718
- sink registry and no noop: events are never silently dropped.
719
-
720
- The generated wire is added to the set of scaffolds the inspector reads.
721
- Nothing imports it — it is a wiring, not a module anyone calls — so without
722
- that the route was written and never registered: no HTTP wiring, no entry in
723
- the fetch client, an endpoint that 404s.
724
-
725
- No middleware is emitted with the wire: an origin lock rejects every native
726
- client and is forgeable anyway, so guarding the route is the project's call.
727
-
728
- `name` belongs to the event, not to its props. The generated union rebuilds
729
- each member from the declared schema's shape and attaches the literal name
730
- last, so a declaration that happens to carry a `name` prop of its own cannot
731
- displace the discriminator the union is built on. Props schemas are typed to
732
- require that shape, so a schema that has none — a union, a primitive — is
733
- refused at the declaration rather than in generated code.
734
-
735
- - Updated dependencies [54138b1]
736
- - @pikku/core@0.12.109
737
-
738
- ## 0.12.78
739
-
740
- ### Patch Changes
741
-
742
- - d067ac9: Skip the schema-generator retention assertion under `--coverage`. Coverage
743
- instrumentation keeps per-line counters alive for the life of the run, so the
744
- heap delta cannot discriminate a retained `ts.Program` from accumulated
745
- counters, and `Unit Coverage` failed intermittently while `Unit Tests` passed on
746
- the same commit. Matches the existing guard in `module-runner.test.ts`.
747
-
748
- ## 0.12.77
749
-
750
- ### Patch Changes
751
-
752
- - 5c236ee: A deployment unit records why it is where it is. `DeploymentUnit` gains `groupedBy` — the `deploy.grouping` rule that put its functions together — and `targetForcedBy` — the `serverlessIncompatible` services that crossed it to `target: 'server'`. Both are absent for the fallback and for a target that was chosen rather than forced, so anything reading a manifest can tell a deliberate `server` unit from a crossed one. `resolveDeployTarget` now shares `incompatibleServicesFor` with the analyzer, so resolving a target and explaining it cannot drift.
753
-
754
- ## 0.12.76
755
-
756
- ### Patch Changes
757
-
758
- - 4c7a1b5: Run the monorepo's own scripts through bun instead of yarn. What moves is the
759
- package manager each package's `prepublishOnly` and build scripts invoke, plus
760
- the two manifest fixes bun needs to resolve the tree: `uWebSockets.js` is
761
- declared with an explicit `github:` specifier, and `@pikku/uws-handler` marks
762
- its `uWebSockets.js` peer optional so a bun install of a consumer that brings
763
- its own uWS app does not try to fetch it from the registry.
764
-
765
- Three published behaviours change, all of them cases where an isolated
766
- `node_modules` or bun as the runtime had been papered over by yarn's hoisting:
767
-
768
- - `@pikku/migrator-sql` turns foreign keys on when it opens a sqlite database
769
- through bun. `node:sqlite` enforces them by default and `bun:sqlite` does not,
770
- which silently turned every `ON DELETE CASCADE` into a no-op under
771
- `bunx --bun pikku`.
772
- - `@pikku/cli` resolves a deploy provider against the project being deployed
773
- rather than against wherever the CLI itself is installed, which is what its
774
- own "is not installed" error asks the user to arrange.
775
- - `@pikku/cli` treats a specifier a runtime hands straight back — bun does this
776
- for the modules it implements itself — as not resolved from the project, so
777
- it falls back rather than loading the runtime's own copy.
778
-
779
- - 9d7a96d: Deprioritise the generated session middleware instead of skipping it
780
-
781
- Two session middlewares were reconciled by having the CLI inspect the app for
782
- its own `betterAuthSession` / `betterAuthStatelessSession` registration and then
783
- generate nothing (pikkujs/pikku#754). That silently dropped everything emitted
784
- alongside — including the `PIKKU_CONSOLE_TOKEN` bearer, so an app that
785
- customized `mapSession` answered `MissingScopeError` on every `console:*` RPC.
786
-
787
- All three session middlewares now take a `priority`, and the CLI generates its
788
- registration at `lowest`. An app's own registration defaults to `medium`, so it
789
- resolves the session first and the generated one short-circuits on its existing
790
- `if (session) next()`. Nothing is detected and nothing is skipped;
791
- `state.auth.userStatelessSession` and `state.auth.hasUserSessionMiddleware` are
792
- gone.
793
-
794
- `betterAuthSession`, `betterAuthStatelessSession` and the session store now also
795
- carry the organization plugin's `activeOrganizationId` onto `session.orgId`
796
- without a `mapSession`, and `impersonation.loadUser` defaults to better-auth's
797
- own `findUserById` — the two things an app most often wrote a whole `mapSession`
798
- for.
799
-
800
- - bb6abc2: The CLI's own code no longer imports the TypeScript compiler API. `collectSurface` moved out of `@pikku/cli` into a new `@pikku/inspector/surface` entry point, and the two smaller compiler needs the CLI had are exposed as `readModuleSpecifiers` and `readTsconfigOutDir`.
801
-
802
- `typescript` drops to a devDependency on `@pikku/cli`. It still arrives transitively through `@pikku/code-edit`, which needs a parser to rewrite your source and is loaded only by `pikku meta apply` and the console's edit RPCs.
803
-
804
- - Updated dependencies [4c7a1b5]
805
- - @pikku/core@0.12.108
806
-
807
- ## 0.12.75
808
-
809
- ### Patch Changes
810
-
811
- - b28a8c0: An addon's exposed functions get their own deployment unit
812
-
813
- `collectFilterNames` adds the `/rpc/:rpcName` scaffold name to every unit holding an exposed function, so that unit can serve its own `/rpc/<funcName>`. `filterInspectorState` read that same string back as "this unit is the generic dispatcher" and kept every wired addon's declaration for it, so almost every function unit imported each addon's whole package bootstrap — and the node-only modules it reaches.
814
-
815
- No unit retains a wired addon on the strength of a catch-all route any more. Instead the analyzer emits a unit per addon carrying that addon's exposed functions, and the unit serving `/rpc/:rpcName` dispatches to it over a service binding rather than bundling it. Granularity is per addon rather than per function because an addon publishes a single prebuilt bootstrap that registers all of its functions at once.
816
-
817
- `node:child_process` joins `node:fs` as a stubbed builtin: Workers have no implementation of it under any compatibility flag, and it is reached from the console addon's audit tooling.
818
-
819
- `DeploymentUnit` gains `dispatch`, an RPC-name → unit-name map for calls whose target unit isn't the kebab-cased RPC name. `rpc.exposed(name)` falls through to the deployment service for a namespaced name with no local metadata, and Cloudflare function units with a `dependsOn` now get a `deploymentService`, which previously only gateway units had.
820
-
821
- - Updated dependencies [b28a8c0]
822
- - @pikku/core@0.12.107
823
-
824
- ## 0.12.74
825
-
826
- ### Patch Changes
827
-
828
- - 1becc2d: Publish an MCP tool under its declared `name`
829
-
830
- `pikkuMCPToolFunc`'s config accepts a `name`, and it is the name an AI client
831
- calls the tool by, but the inspector never read it — the tool was always
832
- published under whatever the export happened to be called. The declared name
833
- now keys `toolsMeta`; `pikkuFuncId` is unchanged.
834
-
835
- ## 0.12.73
836
-
837
- ### Patch Changes
838
-
839
- - 6519c66: Report what each inspector pass did, not only how long it took. `inspect()` now records per-pass work counters on the state (`stats`: files in the program, files reused from the previous program, type/instantiation/symbol counts, CPU and wall time, heap in use), and `pikku all` prints one `[INSPECT]` row per pass under `PIKKU_TIMING`. The codegen benchmark gates on those counts and on live heap per pass instead of on step timings, which swing with the runner, and its fixture now includes an agent so the post-agent re-inspection is actually exercised.
840
- - 5447dba: Keep parsed source files across inspector passes. A re-inspection in `pikku all` used to re-parse and re-bind every file, because the previous `ts.Program` — the thing TypeScript reuses parses from — had to be dropped to free its type checker. The inspector now builds each program through a compiler host with a content-hashed `SourceFile` cache, so a re-inspection parses only what codegen just wrote while holding no checker: on the 500-function benchmark the re-inspection reuses all 908 files, its CPU time falls by a third, and peak heap drops.
841
-
842
- ## 0.12.72
843
-
844
- ### Patch Changes
845
-
846
- - 202cb72: Refuse a scenario step whose prose opens with its own actor (PKU681). The
847
- reporter already renders the actor as the sentence's subject, so a step
848
- authored as `'sam' creates the client` and run as `{ actor: actors.sam }`
849
- reads "Given sam 'sam' creates the client" — and the hardcoded name desyncs
850
- the moment the call site changes actor. Only the subject position is refused:
851
- naming someone else mid-sentence, or an actor keyed after a role noun used as
852
- a noun ("creates the admin client"), is left alone.
853
-
854
- ## 0.12.71
855
-
856
- ### Patch Changes
857
-
858
- - 9015400: Derive an addon's required parent services from what its functions actually use.
859
-
860
- An addon's functions are its published surface — a consumer calls them, nothing
861
- wires them locally — so `usedFunctions` was empty for an addon's own build and no
862
- function services were ever aggregated. The generated services map came out
863
- claiming the addon needed nothing beyond the framework defaults, and
864
- `requiredParentServices` was derived only from the second parameter of
865
- `pikkuAddonServices`, which names what the factory reads rather than what its
866
- functions destructure.
867
-
868
- The visible cost was an addon that consumed `kysely` in all fifteen of its
869
- functions and declared it nowhere: the consuming project was never told to supply
870
- it, the addon lost it at runtime, and every query failed on the first call. The
871
- only way out was to list the missing names under `forceRequiredServices`, which
872
- is a detection escape hatch and was never meant to carry a contract.
873
-
874
- An addon build now aggregates services from every declared function, and treats
875
- as parent-provided whatever it uses but does not build itself. What the factory
876
- builds is read from the object literals it returns, minus anything it took off
877
- the parent bag first — a forwarded service is not a created one, which is how
878
- `@pikku/addon-admin` returns `scopeService` and friends without claiming to own
879
- them. Names destructured from the second parameter in the function body, rather
880
- than in the parameter list, are picked up too.
881
-
882
- - 8852a75: Register an agent invoked from a function body in the calling deployment unit.
883
-
884
- `runAgent('houseAssistant', ...)` and `rpc.agent.run('houseAssistant', ...)` resolve against the in-process agent registry, but the deploy analyzer only ever put an agent's registration in its own `agent-*` unit. A function calling one landed in a separate unit whose bootstrap never registered it, so the deployed worker threw `AI agent not found: houseAssistant`.
885
-
886
- The inspector now records a string-literal agent name passed to `runAgent` / `streamAgent` / `rpc.agent.run` / `rpc.agent.stream` in a function body under `agents.invokedAgentsByFile`, mirroring what it already does for `rpc.invoke` targets. The analyzer carries those names on the calling unit as `invokedAgents` and adds the `ai-model` / `ai-storage` service requirements, and per-unit codegen puts the agent — and its tools — into that unit's filter names so its wiring is generated there too. A dynamic (template-literal) agent name is warned about, as it is for `rpc.invoke`.
887
-
888
- - Updated dependencies [f4e2e89]
889
- - @pikku/core@0.12.102
890
-
891
- ## 0.12.70
892
-
893
- ### Patch Changes
894
-
895
- - d64db40: Prune a filtered-out workflow's file entry, not just its meta.
896
-
897
- `filterInspectorState` deleted a pruned workflow from `workflows.graphMeta` and `workflows.meta` but left it in `workflows.files`, which is what the wiring generator iterates. Per-unit deploy codegen therefore registered every workflow in the project into every unit, and — because a scenario is told apart from an app workflow by `graphMeta[name].source === 'scenario'`, which the same prune had just removed — each scenario was reclassified as an app workflow. That pulled scenario bodies, their steps, and whatever those import into deployed bundles; on Cloudflare a step reading a fixture off disk failed the whole publish with `No such module "node:fs"`.
898
-
899
- ## 0.12.69
900
-
901
- ### Patch Changes
902
-
903
- - 32d1280: Prefix the better-auth plugin factories with `pikku`: `pikkuActor`, `pikkuBan`,
904
- `pikkuFabric`, `pikkuDelegatedAuth` and `pikkuCredentialOAuth`.
905
-
906
- A `betterAuth({ plugins: [...] })` array mixes this package's plugins with
907
- better-auth's own, and until now nothing at the call site told them apart —
908
- `plugins: [actor(...), ban(), fabric(...), organization()]` reads as four
909
- plugins from one place when only the last is better-auth's. The prefix says
910
- which package a plugin came from where it is actually wired.
911
-
912
- The old names are still exported as deprecated aliases bound to the same
913
- functions, so no import has to change. Nothing about the plugins themselves
914
- moved: the `id` each registers under — `pikku-ban`, `actor`, `fabric`,
915
- `delegated-auth`, `credential-oauth` — is unchanged, so no deployed database or
916
- session is affected.
917
-
918
- The pieces that read a plugin's _export_ name rather than its id accept both:
919
- `PLUGIN_REGISTRY` is keyed under the prefixed and the bare name, and the
920
- `pikku validate` ban/actor checks and the `scaffold.userAdmin` ban check count
921
- either spelling as wired. Their messages now point at the new names.
922
-
923
- - Updated dependencies [a0ed1e8]
924
- - @pikku/core@0.12.100
925
-
926
- ## 0.12.68
927
-
928
- ### Patch Changes
929
-
930
- - ee9da9e: A `.transform()` in a schema used as a function's `input` now reports PKU491, naming the schema and the path to the transform, instead of surfacing downstream as PKU724 — the bad `#pikku/*` import error.
931
- - Updated dependencies [ee9da9e]
932
- - Updated dependencies [7a15c9c]
933
- - Updated dependencies [ee9da9e]
934
- - @pikku/core@0.12.99
935
-
936
- ## 0.12.67
937
-
938
- ### Patch Changes
939
-
940
- - af2b764: **Breaking:** extend the classification sink scan to personal data, as PKU954.
941
- A project that passes `pikku all --security --fail-on-error` today can fail
942
- after upgrading, without a line of its own code changing.
943
-
944
- PKU953 already followed a revealed vault secret into a logger, a queue, an
945
- email, an audit or a webhook. A PII column reaches those same sinks with no
946
- `.reveal()` to mark the moment — `Pii<T>` is simply the brand the generated row
947
- types carry — and nothing flagged it.
948
-
949
- It is not the same rule, because PII is not a secret: the question is not
950
- whether the value is written down, but whether writing it there hands it to
951
- somebody else. A log is shipped to a third-party aggregator and retained long
952
- past any consent, and a webhook posts to another server, so both are reported.
953
- An email is addressed to the data subject, an audit is a row in the operator's
954
- own database, and a queue payload is consumed by the operator's own worker, so
955
- none of those are. `private` is never reported at all — it says who may read
956
- the row, not that recording it is a disclosure.
957
-
958
- The scan is still opt-in behind `pikku all --security` and still reports at
959
- `error` severity, so the dev server keeps starting and `--fail-on-error` is
960
- what blocks a build. Where a finding is wrong, an explicit type annotation
961
- erases the brand (`const email: string = shopper.email`) — but the better fix
962
- is usually to correct the column's classification in `db/annotations.ts`.
963
-
964
- Also fixes a false positive in the existing PKU953 scan: property-access
965
- receivers were typed on the way down, and a row type is branded wherever any of
966
- its columns are, so `user.userId` reported the row's `email`.
967
-
968
- - Updated dependencies [8154b1c]
969
- - Updated dependencies [6d9c09c]
970
- - Updated dependencies [239332b]
971
- - @pikku/core@0.12.97
972
-
973
- ## 0.12.66
974
-
975
- ### Patch Changes
976
-
977
- - 2f94a0a: Stop `pikku all` failing on a diagnostic its own codegen went on to fix.
978
-
979
- The run inspects several times, because generating the scaffold, the leaf indexes and the type files each changes the source graph the next inspection reads. Every full inspection re-runs every validator, so the newest pass is the complete one — but the logger accumulated diagnostics across all of them, and the build gate failed the run if any pass had ever recorded a critical.
980
-
981
- A project whose system roles grant a scaffold-declared scope (`virtualUser:*`) therefore could not build from clean: the pass taken before the scaffold existed raised PKU124, the pass taken after was clean, and the run failed anyway. Same for PKU951 on a scaffold-declared secret.
982
-
983
- Validation diagnostics are now scoped to the pass that recorded them, so a later pass supersedes an earlier one. Diagnostics reported outside a validation pass — by a generator or a command — are untouched, and a fault every pass reports still fails the run.
984
-
985
- - 7eeff81: Record `rpc!.invoke(...)` as an invocation
986
-
987
- `rpc` is optional on the wire, so a caller that knows it is there writes
988
- `rpc!.invoke('someFunction', …)`. The inspector matched the receiver as a bare
989
- identifier, but a non-null assertion is a node of its own wrapping that
990
- identifier, so those call sites were never recorded.
991
-
992
- That is silent rather than loud. An invocation is what puts a function that is
993
- not `expose: true` into the runtime registry, so the target was left
994
- unregistered and the dispatch failed with `Function not found` — at run time,
995
- on a call site that type-checked.
996
-
997
- The generated virtual-user scaffold dispatches exactly this way, which meant
998
- `executeVirtualUserRun` was never registered: every virtual-user run sat at
999
- `running` forever with nothing logged, because the scaffold's own
1000
- `.catch(() => {})` swallowed the rejection.
1001
-
1002
- The receiver is now unwrapped through non-null assertions and parentheses
1003
- before it is matched.
1004
-
1005
- - Updated dependencies [88629af]
1006
- - Updated dependencies [f1ccfe3]
1007
- - @pikku/core@0.12.96
1008
-
1009
- ## 0.12.65
1010
-
1011
- ### Patch Changes
1012
-
1013
- - b5e79c1: Resolve `Config`, `SingletonServices` and `Services` by declaration rather than
1014
- by name.
1015
-
1016
- Service extraction read `typesLookup` under the hardcoded names the scaffold
1017
- happens to use, but the lookup is keyed by whatever the project named its
1018
- interface. A project that renamed one satisfied every required-type check and
1019
- then resolved to no services at all, surfacing much later as PKU724 or as every
1020
- singleton service turning optional. These now go through the import maps, which
1021
- carry the real name.
1022
-
1023
- `pikku workflow` carried the same lookup as its fallback when aggregation came
1024
- back empty, so a project with a renamed interface was told
1025
- `WORKFLOW_ORCHESTRATOR_NOT_CONFIGURED` while holding a perfectly good
1026
- `workflowService`.
1027
-
1028
- - Updated dependencies [1cc50ef]
1029
- - Updated dependencies [a3deea4]
1030
- - Updated dependencies [2a02288]
1031
- - @pikku/core@0.12.95
1032
-
1033
- ## 0.12.64
1034
-
1035
- ### Patch Changes
1036
-
1037
- - 0b1bf53: Refuse better-auth's `admin()` plugin, and point at `ban()`.
1038
-
1039
- `admin()` authorizes its endpoints against a `user.role` column while pikku
1040
- authorizes on scopes, so wiring it means running two authorization models and
1041
- projecting one onto the other — coarsely, since any single `admin:users:*` scope
1042
- has to project to `role='admin'` and thereby unlocks every one of its endpoints
1043
- underneath. Pikku dropped that projection when user management moved to
1044
- `@pikku/addon-admin`'s scoped RPCs, so nothing reads the column any more.
1045
-
1046
- The inspector now throws when `admin()` appears in a `betterAuth({ plugins })`
1047
- array, naming the replacement:
1048
-
1049
- ```ts
1050
- import { ban } from '@pikku/better-auth'
1051
- betterAuth({ plugins: [ban()] })
1052
- ```
1053
-
1054
- `ban()` keeps the one capability `admin()` had that pikku cannot supply from
1055
- outside better-auth: the `banned`/`banReason`/`banExpires` columns and the
1056
- session hook that refuses a banned user a session.
1057
-
1058
- `pikku validate` reports the same thing without a prebuild, and additionally
1059
- warns about the quieter half of the migration — an app that dropped `admin()`
1060
- and never wired `ban()`, which keeps its ban columns and its ban UI while
1061
- silently enforcing nothing.
1062
-
1063
- Both resolve the plugin's provenance before applying the policy: the entry has
1064
- to be better-auth's `admin`, imported from `better-auth/plugins` — by name, by
1065
- alias or through a namespace — and actually present in the `plugins` array. A
1066
- project's own helper called `admin` passes, and an import left behind after the
1067
- call was removed configures nothing.
1068
-
1069
- - Updated dependencies [0b1bf53]
1070
- - @pikku/core@0.12.94
1071
-
1072
- ## 0.12.63
1073
-
1074
- ### Patch Changes
1075
-
1076
- - 274cab3: The singleton intersection moves to the leaves that name it, the runtime stops
1077
- creating schema, and `db generate` writes only the runtime tables a project's
1078
- services own
1079
-
1080
- `WiredSingletonServices` was exported from the generated function leaf so the
1081
- auth leaf could import it. Nothing outside a generated leaf ever names it —
1082
- emit declarations for a project of any size and it appears in none of them —
1083
- so the auth and middleware leaves derive the intersection themselves and the
1084
- function leaf keeps it private. `WiredServices` stays exported: 147 `.d.ts`
1085
- files name it, and unexporting it asks every wired module to name each member
1086
- service through a specifier it does not have.
1087
-
1088
- `ensurePikkuSchema` is gone. `requirePikkuSchema` replaces it: a service calls
1089
- it at boot, it looks, and it issues no DDL at all. `pikku db generate` writes
1090
- the declaration down as a migration and `pikku db migrate` applies it, and
1091
- those two are now the only way pikku's runtime tables come into existence. A
1092
- service that finds them missing says so and stops, naming both commands.
1093
- Half-present is no longer a distinct case — the remedy is the same migration
1094
- either way. `audit` and `virtual-user` join `pikkuSchemas` as a consequence:
1095
- boot was the only thing that had ever created them.
1096
-
1097
- `pikku db generate` applied all of `pikkuSchemas`, so a project with no agents,
1098
- no channels and no workflows still had `agent_threads`, `channels` and
1099
- `workflow_runs` written into its migrations, and then carried them forever. A
1100
- schema now names the services that own it, and generation gates on
1101
- `requiredServices` — the set the inspector already builds for service
1102
- tree-shaking. The gate is one-sided: a schema that names no owner is always
1103
- written, because the session and secret stores and the deployment record are
1104
- reached by the runtime itself and nothing in a project's source implies them.
1105
- Declared scopes now imply `scopeService`, which nothing destructures because
1106
- the generated auth layer is what reaches it.
1107
-
1108
- Drift keeps asking the unscoped question. A table already in a database has to
1109
- stay recognisable as a runtime table after the service that needed it is
1110
- dropped — scoping it there would report those tables as unexplained.
1111
-
1112
- Every project in this repo that had been relying on boot-time creation now says
1113
- where its tables come from. `createConfig` moves into its own `config.ts` in the
1114
- templates — `pikku db` looks for it there — and the three postgres templates plus
1115
- the workflow verifier declare `postgresUrl` and run `pikku db generate && pikku
1116
- db migrate` before the server starts, from the single connection string their
1117
- runtime opens. The e2e harness cannot: its databases are in-memory sqlite built
1118
- inside the services factory, so nothing outside the process can migrate them. It
1119
- applies the schemas it owns with `applyPikkuSchemas` instead — the same DDL, run
1120
- by the one process that has the database.
1121
-
1122
- - Updated dependencies [32616af]
1123
- - Updated dependencies [6848cd9]
1124
- - @pikku/core@0.12.89
1125
-
1126
- ## 0.12.62
1127
-
1128
- ### Patch Changes
1129
-
1130
- - 985b87b: Follow through on the variable `required` → `optional` rename: regenerate the
1131
- core API report and update the inspector's `defineVariable` gating-flag test,
1132
- both of which #1369 left describing the deleted `required` flag.
1133
- - 3a83f85: Stop re-exporting package internals through entry points
1134
-
1135
- 66 names reached consumers only because an `export *` in an entry point swept
1136
- them up. Each one is referenced solely inside its own package, so the star is
1137
- now an explicit named re-export listing what is genuinely public. The
1138
- declarations themselves are untouched — this narrows the entry point, not the
1139
- module.
1140
-
1141
- - Updated dependencies [9687ad1]
1142
- - Updated dependencies [2d21628]
1143
- - Updated dependencies [985b87b]
1144
- - Updated dependencies [3a83f85]
1145
- - @pikku/core@0.12.87
1146
-
1147
- ## 0.12.61
1148
-
1149
- ### Patch Changes
1150
-
1151
- - ab52d8e: Raise an addon's deploy conflict only where the consuming app wires it. A
1152
- function an addon publishes as `deploy: 'serverless'` while naming one of its
1153
- own services serverless-incompatible previously failed the build of any app
1154
- that merely declared the addon, whether or not that function was used.
1155
- - a281de6: A CLI option's type now drives how it is parsed, and it comes from the command function's input schema rather than a second hand-written declaration.
1156
-
1157
- `CLIOption` gains `type?: 'string' | 'number' | 'boolean' | 'string[]'` (replacing the never-honoured `array` flag). Declared, it wins over whatever the schema says; left unset, the parser reads the type off the command function's input schema — the same schema the function is validated against — falling back to `default` and then to `'string'`. A `'string[]'` option consumes one token and splits it on commas; every other non-boolean option consumes the next token verbatim, so values that begin with `-` (base64url tokens, negative numbers, dash-leading name patterns) parse correctly. A boolean option is a flag: it consumes the next token only when that token is an explicit literal (`true`/`false`/`1`/`0`/`yes`/`no`), so `--watch false` still turns a default-on flag off instead of leaving `false` behind as a positional.
1158
-
1159
- Because the schema is now what types an option, `pikku serve --console --port 4077` no longer reads `--port` as the value of `--console`, and a numeric option arrives as a number instead of a string. An explicit `type` is mostly needed for options that belong to no function input — the `pikku all` filters, which the config factory reads straight off the CLI data, are declared `string[]`, and the CLI's ad-hoc `parseCommaSeparated` normalisation is gone.
1160
-
1161
- An array option takes either one comma list or the flag repeated. It never consumes more than one token, because `--tags alpha beta` cannot be told apart from an option followed by a positional; the stray token is reported as an unexpected argument rather than dropped.
1162
-
1163
- - 266e3bc: One door per name: `@pikku/core/ecosystem/*` and the package root are gone
1164
-
1165
- `@pikku/core` published every module twice. `ecosystem/http` re-exported
1166
- `./http`, `ecosystem/services` re-exported `./services`, and a name was
1167
- reachable through either — so every addition had to be made in two places, and a
1168
- consumer's import said nothing about what it actually used. The package root was
1169
- the same problem at a larger scale: a single barrel of 206 names that no bundler
1170
- could take apart, and the one specifier that revealed nothing at all.
1171
-
1172
- Both are deleted. Every name now lives on the subpath that owns it, and every
1173
- import carries that subpath — `@pikku/core/http`, `@pikku/core/services`,
1174
- `@pikku/core/errors`, `@pikku/core/types`.
1175
-
1176
- Deleting the facades meant the raw subpaths had to become a superset of them,
1177
- which they were not: the facade tree had accumulated 25 names with no raw home
1178
- and about 26 more filed under a different area than the module they came from.
1179
- Those names moved to the area that owns them, and three areas were published as
1180
- new entry points rather than left on a root that is going away — `./types` (the
1181
- shared type surface, the largest single destination), `./state` and
1182
- `./classification`.
1183
-
1184
- `./classification` is one door onto one subject: what a value is and how it must
1185
- be handled. Its three halves would each have been an entry point — the brands
1186
- and manifest types, the stored-form helpers (`hashToken`, `unsafeAsSealed` and
1187
- friends), and `SecretValue` — split by whether a name happens to be a type or a
1188
- value, which is the same defect as the facades. The duration and versioned-id
1189
- helpers went to `./utils`, which already published, and `PikkuRequest` went to
1190
- `./function`: it is the transport-agnostic request base, not an HTTP one — HTTP
1191
- has `PikkuHTTPAbstractRequest`, and the only thing outside core that extends
1192
- `PikkuRequest` is Azure's timer request.
1193
-
1194
- `./types` inherited the root barrel's habit before it inherited its contents, so
1195
- the names with an owner elsewhere were moved off it. The middleware types and the
1196
- five middleware factories — `pikkuMiddleware`, `pikkuMiddlewareFactory`,
1197
- `pikkuChannelMiddleware`, `pikkuChannelMiddlewareFactory` and
1198
- `pikkuAgentMiddleware`, runtime values on a types entry point — are now
1199
- `@pikku/core/middleware`; the function meta types are `@pikku/core/function`;
1200
- `SerializedError` is `@pikku/core/errors`; and the generic TypeScript helpers
1201
- (`MakeRequired`, `PickRequired`, `PickOptional`, `RequireAtLeastOne`,
1202
- `JSONPrimitive`, `JSONValue`) are `@pikku/core/utils`. What is left on `./types`
1203
- is the vocabulary the wirings share, which no single module owns.
1204
-
1205
- `pikku` was itself a root barrel — `export * from '@pikku/core'` — and
1206
- now exports only the services it bundles.
1207
-
1208
- One module survives at the old specifier, and only for the bootstrap:
1209
- `packages/cli` is generated by the _published_ CLI pinned in its `build.sh`, and
1210
- that CLI still writes a bare `@pikku/core` into the files it generates for the
1211
- CLI itself. `bootstrap-compat/root.ts` carries the eight types it names, a test
1212
- in core fails if that list grows, and it goes when the pin moves to a CLI
1213
- released from this branch. The adapter names the pinned CLI reaches for —
1214
- `pikkuState` and `CreateWireServices` — are rewritten to `@pikku/core/state` and
1215
- `@pikku/core/types` by the same `build.sh` patch pass, so no second shim is
1216
- needed for them.
1217
-
1218
- A guard test keeps the root shut: it parses imports and rejects a bare
1219
- `@pikku/core` rather than grepping for it, because several tests hold a user's
1220
- file as a template literal, where `import … from '@pikku/core'` is fixture text
1221
- rather than an import this repo makes.
1222
-
1223
- An agent scaffold a project generated under an older CLI is refreshed rather
1224
- than left to fail: `pikku all` already deleted one importing an entry point
1225
- `@pikku/core` no longer publishes, and the `#pikku` hub joins that list.
1226
- Without it a project that scaffolds the agent endpoint but
1227
- declares no agents keeps the old file forever — the generator that would rewrite
1228
- it only runs when agents exist, and the file being present is what stops it
1229
- being regenerated as missing.
1230
-
1231
- `pikku new addon` also wrote a tsconfig `paths` map naming only the deleted hub.
1232
- An addon's `imports` map points into `dist`, so `paths` is what resolves
1233
- `#pikku/<leaf>` for the addon's own source build — it now names the two leaf
1234
- patterns, in both the addon and its test harness.
1235
-
1236
- - 6eef0a0: Bump every dependency to its latest compatible minor/patch across the monorepo.
1237
- - 20d8a39: Drop better-auth's `admin()` plugin.
1238
-
1239
- `admin()` bundles three unrelated things: a `role` column, fifteen HTTP
1240
- endpoints authorized against that column, and the enforcement that stops a
1241
- banned user from getting a session. Pikku only ever wanted the third. The
1242
- endpoints were never reachable directly — each sat behind a pikku function with
1243
- its own `admin:users:*` scope — so all the plugin added was a second gate on a
1244
- column that had to be kept in step with the scopes it duplicated. That sync
1245
- (`syncProjectedAdminRole`, run on every authenticated request) is now gone
1246
- along with the column.
1247
-
1248
- User administration is implemented against better-auth's internal adapter
1249
- instead, exported from `@pikku/better-auth` so `@pikku/addon-admin` and the
1250
- `scaffold.userAdmin` generator share one implementation:
1251
-
1252
- ```ts
1253
- import {
1254
- createAuthUser,
1255
- deleteAuthUser,
1256
- revokeAuthUserSessions,
1257
- setAuthUserBanned,
1258
- setAuthUserPassword,
1259
- } from '@pikku/better-auth'
1260
- ```
1261
-
1262
- Ban keeps its schema and its enforcement, in a plugin that does nothing else:
1263
-
1264
- ```ts
1265
- import { ban } from '@pikku/better-auth'
1266
-
1267
- betterAuth({ plugins: [ban()] })
1268
- ```
1269
-
1270
- `ban()` adds `banned`, `banReason` and `banExpires` to `user`, refuses to create
1271
- a session for a banned user, and lapses an expired ban at the sign-in that would
1272
- otherwise be refused. It makes no authorization decision of its own, so it never
1273
- needs to know about scopes.
1274
-
1275
- Breaking:
1276
-
1277
- - Remove `admin()` from your better-auth `plugins`, and add `ban()` if you ban
1278
- users. `pikku db generate` writes the migration; `user.role` and
1279
- `session.impersonatedBy` are no longer declared by anything.
1280
- - `callAdminApi`, `AdminApiHttpWire`, `syncProjectedAdminRole`,
1281
- `projectedAdminRole` and `ADMIN_ROLE_SCOPES` are removed. `ADMIN_SCOPES`,
1282
- `ADMIN_SCOPE_ROOT` and `ADMIN_SCOPE_TREE` are unchanged.
1283
- - The `scaffold.userAdmin` codegen no longer fails without `admin()`. It now
1284
- fails only when there is no better-auth at all, and warns when `ban()` is
1285
- missing — banning is one capability of six, so the other five still generate.
1286
- - The scope on each function is now the whole authorization decision. Anywhere
1287
- that relied on `user.role` as a backstop no longer has one.
1288
-
1289
- - 727671b: Make the public surface an artifact the CLI produces.
1290
-
1291
- `@pikku/cli` now ships `surface.json`, computed when the CLI is built by
1292
- generating one application and one addon and reading what each `#pikku/*` leaf
1293
- exports, plus a curated `@pikku/core` entry point for people building on the
1294
- ecosystem. Consumers resolve it as `@pikku/cli/surface.json`.
1295
-
1296
- `pikku all` writes the matching per-project overlay to
1297
- `<outDir>/surface-usage.gen.json`: how often each export is imported and which
1298
- source areas it was seen in. The counting happens inside the sweep the inspector
1299
- already makes over every source file, so a prebuild pays no extra pass.
1300
-
1301
- - f25f4a2: Fix seven defects found taking one project through `pikku fabric` to deploy
1302
-
1303
- **`deploy plan` rewrote the project's own scaffold.** Per-unit codegen re-runs
1304
- `pikku all` with `--outDir` pointed at a unit's `.pikku`, and scaffold imports
1305
- are computed against `config.outDir` — so `console.gen.ts` came back importing
1306
- `../../../../../.deploy/cloudflare/units/<unit>/.pikku/pikku-types.gen.js` and
1307
- the source stopped typechecking until the next ordinary `pikku all`.
1308
-
1309
- A guard for this already existed in four generators and had never once fired:
1310
- `LocalVariablesService.get` runs values through `JSON.parse`, so
1311
- `PIKKU_DEPLOY_CODEGEN=1` arrived as the number `1` and every `=== '1'` test
1312
- was false. The comparison is fixed behind a shared `isDeployCodegen`, and the
1313
- real guard now sits in the file writer, which refuses writes _and removals_
1314
- under the scaffold directory for the duration of a per-unit run. Guarding the
1315
- writer rather than each generator matters here: seven further generators had no
1316
- guard at all, several write scaffold source and `.pikku` artifacts in the same
1317
- pass (so an early return would skip too much), and the legacy-scaffold pruners
1318
- delete from the source tree without going through a generator.
1319
-
1320
- **`fabric validate` passed on a project that could not deploy.** Deploy clones
1321
- the repository, so a `pikkufabric.config.json` that exists only in the working
1322
- tree is absent exactly when it is needed, and the build container aborts with
1323
- `pikkufabric.config.json not found in repository root`. Validate now reports
1324
- that as an error, and its success line distinguishes "can be linked" from
1325
- "will deploy" instead of reporting unqualified success at a project that is not
1326
- linked yet.
1327
-
1328
- **`description` reached `infra.json` as raw source.** `getPropertyValue` fell
1329
- back to `node.getText()`, which is indistinguishable from the value for a lone
1330
- literal — so nobody noticed that a description written as `'a ' + 'b'` arrived
1331
- with the quotes and the `+` still in it, and rendered that way in the console.
1332
- Compile-time constant strings are now folded, checker-free, so a node that
1333
- cannot be resolved statically still takes the old path.
1334
-
1335
- **A wired addon that was not installed failed silently.** A missing package
1336
- makes `resolveAddonMeta` return null, which was caught and downgraded to a
1337
- warning; every `ref('<namespace>:…')` then resolved to nothing and the surface
1338
- was dead at runtime with nothing in the build output saying why. The generated
1339
- console is the common case. `wireAddon` now requires its package to be
1340
- installed (`PKU340`), the mirror of the existing `wireRemoteAddon` check whose
1341
- own docs already described this half as if it existed.
1342
-
1343
- **The audit-table check demanded an unquoted identifier.** Kysely's schema
1344
- builder always quotes, so `create table "audit"` read as missing on the
1345
- projects most likely to have it. Both this and the better-auth table checks now
1346
- share one matcher that accepts each dialect's quoting — matched pairs only, so
1347
- `"audit'` is not a hit — plus an optional schema qualifier.
1348
-
1349
- **Cloudflare bundles kept `pg`.** `getStubModules()` named `postgres` and
1350
- `kysely-postgres-js` but not `pg`, which is the more common driver in
1351
- application code, equally unreachable on a Worker, and additionally pulls at
1352
- `net`/`tls` and `pg-native`, which a Worker build cannot resolve at all.
1353
-
1354
- **The deploy plan listed one secret twice.** Two `defineSecret` calls may
1355
- legally share a `secretId` under different local names — the auth scaffold's
1356
- `betterAuthSecret` alongside a hand-written one is the everyday case — and the
1357
- manifest mapped them straight through, so the plan printed two identical
1358
- `create` lines for one resource and `countUnchanged` counted it twice. Secrets
1359
- and variables are now deduplicated in the manifest itself, where variables were
1360
- already being collapsed by accident downstream.
1361
-
1362
- - 20d8a39: Generate the schemas a first build used to leave out.
1363
-
1364
- A contract type reaches the schema generator only through a file that imports
1365
- it, and for a type exported by nothing but its own function file that file is
1366
- the RPC internal map — which `pikku all` writes _after_ schemas. On a first
1367
- build there is no map to read yet, so those schemas came out missing from a run
1368
- that otherwise succeeded, and the RPC failed with `MissingSchemaError` on its
1369
- first call in a deployment. A second `pikku all` fixed it, which is why this
1370
- only ever bit fresh checkouts and CI.
1371
-
1372
- `pikku all` now re-inspects and re-generates schemas when the build that just
1373
- wrote the RPC internal map for the first time left contract references
1374
- unresolved — the condition PKU463 already reports. Confined to that build: a
1375
- project whose references are unresolved for some other reason would otherwise
1376
- pay a second inspection on every run for a re-generation that cannot help it.
1377
-
1378
- `@pikku/inspector` exports `unresolvedSchemaReferences(state)`, the check behind
1379
- PKU463, so the decision can be made without re-deriving it.
1380
-
1381
- - 3561d67: feat(graph): per-item `forEach` fanout for declarative workflow graphs
1382
-
1383
- A graph node can now run once per element of an upstream array:
1384
-
1385
- ```ts
1386
- postVideo: {
1387
- forEach: 'getMyVideo', // or (ref) => ref('getMyVideo', 'rows')
1388
- mode: 'sequential', // optional, defaults to 'parallel'
1389
- input: (ref, template, $item) => ({ url: $item('URL VIDEO') }),
1390
- }
1391
- ```
1392
-
1393
- Each element runs as its own step instance (`postVideo[0]`, `postVideo[1]`, …)
1394
- and the node's result is the ordered array of per-item results, so a fanned node
1395
- chains straight into another `forEach`. Downstream nodes wait for every item. A
1396
- non-array source fails the run loudly instead of coercing.
1397
-
1398
- The change is additive: `forEach` and `mode` are new optional node fields, and
1399
- `$item` is appended after `template` so existing `input: (ref) => …` and
1400
- `input: (ref, template) => …` nodes are unchanged.
1401
-
1402
- - a91c433: HTTP routes wired with `ref('ns:fn')` now record the addon function id as their own `pikkuFuncId`, the same way the CLI and channel wirings already do, instead of minting a per-route wrapper function and linking it back through `refTarget`. The `refTarget` field is gone from `HTTPWiringMeta`, and the runtime resolves a namespaced route function against the addon package's own metadata.
1403
- - 2b57ca8: A persona can name the `app` they sign into, and a browser run takes a url per app (`--app-url <app>=<url>`, or `appUrls` on the environment). Each actor's browser context navigates against its own app's base, so a product that is more than one frontend can be proved in one run — including a scenario that crosses from one app to the other. A run whose personas name an app nobody gave a url for is refused rather than browsing the wrong app's pages.
1404
- - 456c88b: Keep an addon function's contract on a route that reaches it through `ref()`, whether or not `.pikku` already exists.
1405
-
1406
- The input and output types were read off the type checker, which can only see them once the consumer's own RPC map already lists the addon's functions — that is, on the second run. A first, cold generation widened the route's input to the whole `FlattenedRPCMap`, so the same sources produced a different http-map on CI than on a developer's machine, and a client compiled against the good one failed to build against the other. The addon's own metadata says what the contract is either way, and its published declaration file is where those names live.
1407
-
1408
- - 727671b: `wireAddon` and `wireRemoteAddon` move from `#pikku/function` to `#pikku/addon`.
1409
-
1410
- Installing an addon and authoring one are the same concept from opposite ends,
1411
- so they are one import: an application's `#pikku/addon` carries the two install
1412
- functions, an addon package's carries `pikkuAddonConfig`, `pikkuAddonServices`,
1413
- `pikkuAddonWireServices` and `AddonBaseServices`.
1414
-
1415
- Two generation fixes came with it:
1416
-
1417
- - `CredentialsMap` is generated as a type alias rather than an interface. An
1418
- interface has no implicit index signature, so it was never assignable to the
1419
- `Record<string, unknown>` that `GetCredential` is constrained by, and every
1420
- generated project reported two errors on its own function types.
1421
- - An unresolved `SingletonServices` type is now `PKU724` instead of a services
1422
- map with no entries in it. Written out, the empty map made every service
1423
- optional and the real failure resurfaced as unrelated "possibly undefined"
1424
- errors in files nobody had touched.
1425
-
1426
- - Updated dependencies [7722ceb]
1427
- - Updated dependencies [375c1ff]
1428
- - Updated dependencies [02a70cd]
1429
- - Updated dependencies [aeef159]
1430
- - Updated dependencies [a281de6]
1431
- - Updated dependencies [266e3bc]
1432
- - Updated dependencies [02a70cd]
1433
- - Updated dependencies [786dae5]
1434
- - Updated dependencies [6eef0a0]
1435
- - Updated dependencies [3561d67]
1436
- - Updated dependencies [a91c433]
1437
- - Updated dependencies [02a70cd]
1438
- - Updated dependencies [9537f74]
1439
- - Updated dependencies [2b57ca8]
1440
- - Updated dependencies [266e3bc]
1441
- - Updated dependencies [9fce0f1]
1442
- - Updated dependencies [83683a0]
1443
- - Updated dependencies [456c88b]
1444
- - Updated dependencies [456c88b]
1445
- - Updated dependencies [c127273]
1446
- - @pikku/core@0.12.85
1447
-
1448
- ## 0.12.60
1449
-
1450
- ### Patch Changes
1451
-
1452
- - 7406bfe: Rename the agent runtime from `AI*` to `Agent*` (#596)
1453
-
1454
- `AI` described the model provider, not the thing being named. Every symbol that
1455
- belongs to the agent runtime now says `Agent`; the symbols that genuinely wrap a
1456
- model provider — `AIEmbeddingService`, `AIProviderOptions`, `AIEmbedParams`,
1457
- `AITranscriptionParams`, `AIGenerateImageParams` and their siblings, and the
1458
- `@pikku/ai-vercel` / `@pikku/ai-deepinfra` / `@pikku/ai-voice` packages — keep
1459
- their names.
1460
-
1461
- **Wiring**
1462
- - `pikkuAIAgent` → `pikkuAgent`, `pikkuAIScorer` → `pikkuAgentScorer`,
1463
- `pikkuAIJudge` → `pikkuAgentJudge`
1464
- - `CoreAIAgent` → `CoreAgent`, `AIAgentInput` → `AgentInput`, `AIAgentStep` →
1465
- `AgentStep`, `AIMessage` → `AgentMessage`, and the rest of the agent types
1466
- - `AIAgentRunnerService` → `AgentRunnerService`, `AIStorageService` →
1467
- `AgentStorageService`, `AIRunStateService` → `AgentRunStateService`
1468
-
1469
- **Entry points**
1470
-
1471
- `@pikku/core/agent` → `@pikku/core/agent`, `@pikku/core/agent-scorer` →
1472
- `@pikku/core/agent-scorer`.
1473
-
1474
- **Queues**
1475
-
1476
- The scorer queues are now `agent-score-fast` and `agent-score-slow`. Drain the
1477
- old `ai-score-fast` / `ai-score-slow` queues before deploying — jobs still
1478
- sitting on them when the new workers start will never be picked up.
1479
-
1480
- **Scaffolds**
1481
-
1482
- The agent scaffold pikku wrote for your project — `<scaffold>/agent/agent.gen.ts`
1483
- and its schemas file — imports `@pikku/core/ai-agent`, which no longer exists. A
1484
- scaffold is normally written once and then left alone, so `pikku all` would find
1485
- it present and leave the broken import in place. It now deletes an agent scaffold
1486
- importing either removed entry point and regenerates it in the same run. Anything
1487
- you added to that file goes with it, so move local edits out first.
1488
-
1489
- **Database**
1490
-
1491
- The agent tables are renamed: `ai_threads`, `ai_message`, `ai_tool_call`,
1492
- `ai_working_memory`, `ai_run` and `ai_run_score` become `agent_threads`,
1493
- `agent_message`, `agent_tool_call`, `agent_working_memory`, `agent_run` and
1494
- `agent_run_score`, along with their indexes and the `ai_working_memory_pk`
1495
- constraint. The same rename applies to the MongoDB collections.
1496
-
1497
- `ensurePikkuSchema` creates tables it cannot find, so an existing database will
1498
- get empty `agent_*` tables and leave the old data stranded in `ai_*`. Rename
1499
- them before the first boot on the new version:
1500
-
1501
- ```sql
1502
- ALTER TABLE ai_threads RENAME TO agent_threads;
1503
- ALTER TABLE ai_message RENAME TO agent_message;
1504
- ALTER TABLE ai_tool_call RENAME TO agent_tool_call;
1505
- ALTER TABLE ai_working_memory RENAME TO agent_working_memory;
1506
- ALTER TABLE ai_run RENAME TO agent_run;
1507
- ALTER TABLE ai_run_score RENAME TO agent_run_score;
1508
- ```
1509
-
1510
- - 6794681: Publish the ecosystem surface as per-area sub-barrels under `@pikku/core/ecosystem/*`, and point generated code, the CLI, the inspector and the runtime adapters at them.
1511
-
1512
- 348 names that only generated code, the toolchain or a runtime adapter ever imports now have a second home on `@pikku/core/ecosystem/<area>` — one sub-barrel per area, matching how core already publishes its entrypoints, so no single barrel grows without bound and a consumer only pulls in the area it uses.
1513
-
1514
- This step is additive: every name is still exported from the entrypoint it was published from before, so nothing downstream breaks. Removing them from the app-facing barrels is a later change, and needs a release carrying `./ecosystem/*` first.
1515
-
1516
- - Updated dependencies [7406bfe]
1517
- - Updated dependencies [6794681]
1518
- - @pikku/core@0.12.84
1519
-
1520
- ## 0.12.59
1521
-
1522
- ### Patch Changes
1523
-
1524
- - 02c4fe5: fix(core,inspector): let a host grant an addon secrets it could not declare
1525
-
1526
- Scoping an addon's `SecretService` to its `declaredSecrets` left generic addons
1527
- with nothing readable: `declaredSecrets` is derived from the addon package's own
1528
- source, but the secrets an addon like `@pikku/addon-graph` reads are named by the
1529
- consuming app's workflow nodes at runtime. Every authenticated `graph:httpRequest`
1530
- threw.
1531
-
1532
- `wireAddon` now takes `secretGrants: string[]` and `credentialGrants: string[]`,
1533
- completing the grant family alongside `secretOverrides` (grant + rename) and
1534
- `globalSecrets` (grant everything, with a reason). Grants name the secret as the
1535
- addon reads it, since the scope check runs before the override map renames it —
1536
- which is also why an override's key grants and its value does not.
1537
-
1538
- A grant naming a secret the project does not declare is an `INVALID_VALUE`
1539
- critical at codegen, resolved through the override map before lookup.
1540
-
1541
- - 438b776: Move the scenario and feature surface off `@pikku/core/workflow` and onto
1542
- `@pikku/core/scenario`. Scenarios extend workflows, so the production workflow
1543
- wiring no longer names a scenario module in its import graph. Feature and
1544
- scenario types are declared in their own `scenario.types.ts` rather than in
1545
- `workflow.types.ts`. Import `requireActor`, `requireScenarioEnv`, `pollUntil`,
1546
- `createCookieJar`, `addFeature`, `ScenarioHttpResponse` and the rest from
1547
- `@pikku/core/scenario`; `HttpPersonasConfig` now comes from
1548
- `@pikku/core/persona` rather than `@pikku/core/services`.
1549
- - ad63f47: feat(cli): warn before codegen when a linked dependency splits a package's type identity
1550
-
1551
- `pikku all` now runs the split-type-identity check as a preflight, beside the
1552
- existing `@pikku/core` one, and warns with `PKU719` naming each package, both
1553
- versions and both paths.
1554
-
1555
- It has to run _before_ the work rather than after it fails. The failure it
1556
- explains is a V8 heap OOM, which aborts the process — `process.on('exit')`,
1557
- `uncaughtException` and `finally` never run, so nothing printed after the fact is
1558
- ever seen. By the time there is a symptom, the only thing that can help is
1559
- already on screen above it. Without this the user sees a codegen step that dies
1560
- of memory pressure with no indication that two copies of one package are the
1561
- reason, and the obvious next move is to raise `--max-old-space-size`, which
1562
- hides it further.
1563
-
1564
- Warns rather than throws: a skewed linked dependency is a strong signal, not a
1565
- certainty, and refusing to build on a heuristic would break working setups.
1566
- `PIKKU_SKIP_TYPE_IDENTITY_CHECK=1` silences it, matching
1567
- `PIKKU_ALLOW_DUPLICATE_CORE`. It also swallows its own errors — it runs on every
1568
- codegen, so it must never be the reason a build stops.
1569
-
1570
- - Updated dependencies [02c4fe5]
1571
- - Updated dependencies [438b776]
1572
- - Updated dependencies [438b776]
1573
- - @pikku/core@0.12.83
1574
-
1575
- ## 0.12.58
1576
-
1577
- ### Patch Changes
1578
-
1579
- - 3ad2131: Name models by what they are for, and switch them all in one place
1580
-
1581
- A `models` table in pikku.config.json maps an alias to a provider-qualified
1582
- model, so a declaration can say `model: 'cheap'` and the project repoints every
1583
- use of that tier at once instead of editing each agent. A model containing `/`
1584
- is still concrete and used exactly as written, which is how an agent that needs
1585
- one specific model pins it — aliases are opt-in.
1586
-
1587
- The table is baked into codegen rather than read at runtime, so it applies to
1588
- deployed units and not just local runs, and `pikku dev`/`pikku serve` take
1589
- `--model cheap:openai/gpt-5-nano` to repoint a tier for one run without editing
1590
- the config.
1591
-
1592
- Because the inspector already holds every agent's model literal, a bare name
1593
- with no matching alias now fails the build (PKU146) naming the aliases that do
1594
- exist, rather than reaching a provider as an unknown model.
1595
-
1596
- Aliases resolve for every modality, not just agents: image, speech,
1597
- transcription, embedding and reranking all reach a provider through the same
1598
- point in the Vercel runner.
1599
-
1600
- - b930dca: Remove the `secretBroker` escape hatch and scope addon secrets and credentials
1601
-
1602
- `secretBroker` let three named console functions receive the real `SecretService`,
1603
- against the rule that a function never sees one. It is gone: the inspector allowlist,
1604
- the `FunctionRuntimeMeta` flag, the runner branches, and the `WiredSecretBrokerServices`
1605
- type. Console secret administration moved into the console addon, where a
1606
- `SecretAdminService` holds the `SecretService` and the functions hold none.
1607
-
1608
- Addons are now scoped rather than trusted. The CLI emits each package's declared secret
1609
- keys, and the host wraps the `SecretService` in a `ScopedSecretService` and the
1610
- `CredentialService` in a new `ScopedCredentialService` before the addon's service factory
1611
- runs — so an addon reads only what it declared, cannot write secrets, and cannot enumerate
1612
- the app's users. `wireAddon({ globalSecrets, globalCredentials })` waives this, taking the
1613
- reason as its value; only the consuming app can grant it, and the deploy manifest reports
1614
- every grant under `unscopedSecretAddons` / `unscopedCredentialAddons`.
1615
-
1616
- - 8978fbd: feat(workflow): let an approval gate declare who may answer it
1617
-
1618
- `workflow.approval()` gains `approvers` (`'any' | 'owner' | 'not-initiator'`)
1619
- and `approverScope`, so a gate can require four-eyes sign-off, restrict itself
1620
- to the run's initiator, or require the decider to hold a named scope.
1621
-
1622
- Both are enforced when the workflow replays the gate — the same place, and for
1623
- the same reason, the decision payload is validated: the policy is a value on
1624
- the workflow, and a decision can be recorded before the run has ever reached
1625
- the gate. A decision that fails the policy is discarded and the gate stays
1626
- closed. Where the run has already published its policy, the check also runs at
1627
- submission time so the caller gets a 403 rather than silence.
1628
-
1629
- An answer is now recorded where it can be answered for later. The settled
1630
- decision carries `decidedBy` and `decidedAt` in its `ApprovalOutcome`, so who
1631
- signed reaches `workflowStep.result` and `workflowStepHistory` rather than
1632
- living only in mutable run state. Every answer — accepted, refused at the door,
1633
- or cleared on replay — is also written to the audit sink as
1634
- `workflow.approval.decided`, which outlives the run: `deleteRun` cascades to
1635
- steps and history, and a refused attempt never reaches a step at all. Projects
1636
- with no audit service wired are unaffected.
1637
-
1638
- **This loosens the default.** `approveStep` previously refused anyone but the
1639
- run's initiator, unconditionally. A gate that declares no `approvers` now
1640
- accepts a decision from anyone the approve entrypoint admits — restore the old
1641
- behaviour per-gate with `approvers: 'owner'`, or gate the approve route with
1642
- `auth`/`permissions`. Ownership still governs _reads_ of a run unchanged.
1643
-
1644
- - Updated dependencies [063f43a]
1645
- - Updated dependencies [ce66bf8]
1646
- - Updated dependencies [d0307a8]
1647
- - Updated dependencies [ce66bf8]
1648
- - Updated dependencies [3ad2131]
1649
- - Updated dependencies [b930dca]
1650
- - Updated dependencies [b95e77d]
1651
- - Updated dependencies [fd9d834]
1652
- - Updated dependencies [8978fbd]
1653
- - @pikku/core@0.12.82
1654
-
1655
- ## 0.12.57
1656
-
1657
- ### Patch Changes
1658
-
1659
- - e110c55: Wire `pikkuAIScorer` / `pikkuAIJudge` through the inspector and codegen, and let an agent name the scorers that grade it.
1660
-
1661
- The inspector reads a scorer's lane off which constructor was called rather than
1662
- off a field, so the two lanes cannot disagree with the code that produced them,
1663
- and refuses a scorer with no name or description — the meta is the only thing
1664
- that names it at runtime. An agent naming a scorer that was never declared is a
1665
- build error (`PKU155`) rather than an agent that quietly grades nothing forever.
1666
-
1667
- Codegen emits a `ScorerName` union, so `scorers` on an agent is checked against
1668
- the scorers the project actually declares, plus the scorer wirings and meta.
1669
- `pikku validate` now also flags a scorer declared outside a `*.scorer.ts` file,
1670
- for the same reason scenarios have to live in files named for them: a rubric
1671
- buried in an agent definition is one nobody reviews as a rubric.
1672
-
1673
- - e110c55: Add `scenario.expectScore` — grade a finished agent run with a declared scorer and assert on it.
1674
-
1675
- An agent's answer cannot be matched against a fixed string, so a scenario grades
1676
- it instead. `expectScore(step, runId, scorer, { atLeast, atMost, reference })`
1677
- runs one declared scorer against the run the scenario just triggered and fails
1678
- with the reason the judge gave. The default bound is `atLeast: 0.5`, so an
1679
- unqualified assertion still fails a run graded zero.
1680
-
1681
- Grading goes over the new `pikkuScenarioGradeRun` instrumentation RPC, which the
1682
- dev server registers alongside the coverage and stub RPCs — so it exists only in
1683
- processes that should have it, and never in a deployed bundle. It grades from
1684
- the snapshot the runtime already took when the run finished, which is what makes
1685
- a scenario's grade the same measurement production's sampler makes rather than
1686
- an approximation of it: a run's prompt, answer and tool calls are spread across
1687
- a thread's messages, where the boundary of one run is not recoverable.
1688
-
1689
- Two things differ deliberately from live scoring. The sample rate is ignored — a
1690
- scorer grading 1% of traffic still grades every scenario run — and the grade is
1691
- returned rather than recorded, so a test's score never lands among the
1692
- production figures. `reference` supplies the answer key a `requiresReference`
1693
- judge grades against, which is the only way such a judge is reachable at all.
1694
-
1695
- - Updated dependencies [e110c55]
1696
- - Updated dependencies [e110c55]
1697
- - Updated dependencies [e110c55]
1698
- - Updated dependencies [acc8077]
1699
- - Updated dependencies [905f737]
1700
- - Updated dependencies [3cc6428]
1701
- - Updated dependencies [c524adf]
1702
- - Updated dependencies [e110c55]
1703
- - @pikku/core@0.12.81
1704
-
1705
- ## 0.12.56
1706
-
1707
- ### Patch Changes
1708
-
1709
- - 7e60867: Delete exports nothing references
1710
-
1711
- A sweep of the `@pikku/core` surface for exports with no consumer anywhere in the repo — no package, template, verifier or e2e project imports them: `ExtractFunctionOutput`, `CLICommandDefinition`, `RequestHeaders`, `HTTPFunctionsMeta`, `HTTPWiringMiddleware`, `JsonRpcError`, `TriggerSourceInfo`, `getMCPResources`, `getMCPPrompts`, `onGraphNodeComplete` and `InputRef`.
1712
-
1713
- Every one was a compatibility promise with nothing on the other end of it. Removing them narrows what 0.13 has to keep stable.
1714
-
1715
- `isRef` looked like the twelfth, and isn't. It is the type guard that reads what `createRef` writes — the `__isRef` brand marking a graph node input as "substitute another node's output here". Nothing imported it because neither it nor `RefValue` was reachable from any entry point, so the one consumer that needed it, the inspector's graph serializer, had reimplemented the same four conditions privately as `isRefValue` along with its own structural copy of `RefValue`. Deleting `isRef` would have made that duplicate permanent, with the brand's shape asserted in two places free to drift apart.
1716
-
1717
- So `isRef` and `RefValue` are exported from `@pikku/core/workflow` instead, and the inspector imports them rather than keeping its own copy.
1718
-
1719
- - a879ab3: **Codegen reports a schema it named but never generated (PKU463).** A named
1720
- contract type that is declared but not exported is imported by the virtual
1721
- source file the schema generator compiles, resolves to nothing, and yields no
1722
- schema — while the function meta still carries the name. `pikku all` exited 0
1723
- and the first call to that function failed in a deployment with
1724
- `MissingSchemaError`. The reference is now checked once addon schemas are
1725
- merged, since an addon supplies its own and checking earlier would report every
1726
- one of them as unresolved.
1727
- - Updated dependencies [41c1a95]
1728
- - Updated dependencies [ce96383]
1729
- - Updated dependencies [7e60867]
1730
- - Updated dependencies [f8f1244]
1731
- - Updated dependencies [dcf20cb]
1732
- - Updated dependencies [6512384]
1733
- - Updated dependencies [e3b4c14]
1734
- - Updated dependencies [efd0ed1]
1735
- - Updated dependencies [cba98fb]
1736
- - Updated dependencies [ce96383]
1737
- - Updated dependencies [f8f1244]
1738
- - Updated dependencies [f8f1244]
1739
- - Updated dependencies [6e93a35]
1740
- - Updated dependencies [6dada45]
1741
- - @pikku/core@0.12.80
1742
-
1743
- ## 0.12.55
1744
-
1745
- ### Patch Changes
1746
-
1747
- - ae4e898: Carry a secret's `allowedHosts` through code generation, and close three gaps in
1748
- the SSRF guard.
1749
-
1750
- `allowedHosts` was declared and enforced but never survived codegen: the
1751
- inspector did not read the property off the `defineSecret` literal, and the meta
1752
- builder rebuilt its objects without it. Enforcement in `assertSecretAllowedForHost`
1753
- then always saw `undefined`, so the egress restriction was a no-op by default —
1754
- and, with `secrets.requireAllowedHosts` set, threw for every secret including the
1755
- ones that correctly declared hosts. Both stages now carry the field, and the
1756
- secrets verifier asserts it against the generated JSON rather than a hand-written
1757
- meta literal, which is why the existing tests stayed green.
1758
-
1759
- `isPrivateHost` now checks an explicit CIDR table instead of ad-hoc octet
1760
- comparisons. It previously missed `100.64.0.0/10` — which contains Alibaba
1761
- Cloud's `100.100.100.200` metadata endpoint — along with `192.0.0.0/24`,
1762
- `198.18.0.0/15`, `192.88.99.0/24`, the TEST-NETs, multicast and reserved space.
1763
- IPv6 gains a real parser, so `fec0::/10`, `ff00::/8`, and NAT64 (`64:ff9b::/96`)
1764
- and 6to4 (`2002::/16`) forms wrapping an internal IPv4 address are caught.
1765
-
1766
- `safeFetch` takes an optional `resolveHost`, checked on the initial URL and every
1767
- redirect hop, so a _public_ hostname pointing at a private address is refused —
1768
- the `169-254-169-254.nip.io` shape a literal-only check cannot see. Core cannot
1769
- resolve DNS itself (Workers has no DNS API), so the Node resolver ships as
1770
- `@pikku/core/node-host-resolver` and the Node server runtimes install it during
1771
- `init()`. The connection is not pinned to the address that was checked, so a
1772
- rebind between check and connect is still possible.
1773
-
1774
- The graph addon's `httpRequest` node called bare `fetch`, bypassing the guard
1775
- entirely; it now goes through `safeFetch`.
1776
-
1777
- - Updated dependencies [e848eb2]
1778
- - Updated dependencies [b170489]
1779
- - Updated dependencies [ae4e898]
1780
- - @pikku/core@0.12.79
1781
-
1782
- ## 0.12.54
1783
-
1784
- ### Patch Changes
1785
-
1786
- - 62ea4cc: The audit trail is now readable — in the generated meta, through an RPC, and as
1787
- a page in the console.
1788
-
1789
- `audit: true` reaches `FunctionRuntimeMeta.audit` as its resolved form
1790
- (`{ durability }`), so which functions record anything is answerable without
1791
- running them. It is informational: the runner still resolves audit from the live
1792
- function config, so meta and runtime cannot disagree.
1793
-
1794
- `AuditService` grows an optional read side — `query(AuditQuery)` and `facets()`.
1795
- Optional because a sink can legitimately be write-only: a queue producer that
1796
- hands events to another system has nothing to read back, and a reader that finds
1797
- these absent should say the trail is not readable here rather than that it is
1798
- empty. The two are very different answers to give someone auditing a system.
1799
-
1800
- `KyselyAuditService` implements both, newest first with offset paging, filtered
1801
- by user, action and time window. Two things it now gets right that are easy to
1802
- get wrong: an empty filter array means "match nothing" rather than "no filter",
1803
- and results are read by physical _and_ camelCase key, because `CamelCasePlugin`
1804
- is on most pikku Kysely instances and renames result keys on the way out — the
1805
- mismatch does not throw, it returns a page of `undefined`. `init()` creates the
1806
- `audit` table for projects that do not migrate it themselves, from a new
1807
- exported `auditSchema` that stays out of `pikkuSchemas` because the runtime does
1808
- not need it.
1809
-
1810
- The console addon exposes `console:getAudits` and `console:getAuditFilters`
1811
- behind a new `pikku:audit:read` scope, and forwards the application's `audit`
1812
- service into the addon's own services — without that last part every install
1813
- reported the trail as unreadable, whatever sink it had configured.
1814
-
1815
- The console gets an Audit trail page: an infinite list filtered server-side by
1816
- user and action, and a row that opens the whole event, metadata rendered as a
1817
- JSON tree. Refused, unreadable and empty are three different screens, because
1818
- "you may not read this", "nobody can read this" and "nothing happened" are three
1819
- different facts.
1820
-
1821
- Events name the person who caused them. The trail records a user id — the only
1822
- thing stable enough to record, since a name can change after the event — so
1823
- `getAudits` resolves those ids against better-auth's user directory at read
1824
- time, and the page shows the name while keeping the recorded id on the event.
1825
- The filter follows: pick a colleague by name, filter by the id. A scenario
1826
- actor is labelled as one, so synthetic traffic is not mistaken for real, and a
1827
- caller who was signed out shows the wire identity pikku resolved for them
1828
- rather than being credited to the system.
1829
-
1830
- **Breaking, for anyone already reading `AuditEvent`:** `actor` is now
1831
- `userIdentity`, and its type `AuditActor` is `AuditUserIdentity`; `AuditQuery`
1832
- takes `userIds`/`orgId` in place of `actorUserIds`/`actorOrgId`, and
1833
- `AuditFacets` returns `userIds`. In pikku an _actor_ is a synthetic person a
1834
- scenario drives, flagged on the user row — so naming the causer of an event
1835
- `actor` made the synthetic case unsayable (`actor.actor === true`) and implied
1836
- every recorded action was a test. The overwhelming majority are ordinary
1837
- customers. The `audit` table follows: `actor_user_id` / `actor_org_id`
1838
- are now `user_id` / `org_id`, and a `pikku_user_id` column joins them so the
1839
- wire identity of a caller who never signed in survives the round trip — the
1840
- sink was dropping it, which left the console's Session field permanently
1841
- blank. A project that already migrated the table needs to rename the two
1842
- columns and add the third; `KyselyAuditService.init()` creates the new shape
1843
- for anyone who did not.
1844
-
1845
- - 9dddff8: Split a column's at-rest form out of its classification.
1846
-
1847
- `security: 'encrypted'` sat beside `'secret'` as though the two were
1848
- alternatives, which made the field unanswerable: a token hash and a live bearer
1849
- token are both secret, one must never be encrypted — the digest _is_ the lookup
1850
- key — and the other must always be. A column now carries a second, independent
1851
- `form: 'plain' | 'hashed' | 'wrapped' | 'sealed'` saying how the bytes are held.
1852
-
1853
- Declaring a form other than `plain` makes the column's INSERT/UPDATE type
1854
- nominal — `WrappedValue`, `SealedValue`, `HashedValue` — so a plain string no
1855
- longer compiles there and the only way to write the column is with something an
1856
- encrypt, seal or hash call produced. `envelopeEncrypt`, `envelopeRewrap` and
1857
- `wrapDEK` now return the brand, and a new `hashToken` produces `HashedValue`, so
1858
- the round trip needs no casts; `column-form.ts` exports deliberately-named
1859
- `unsafeAs*` assertions for backfills, fixtures and values sealed elsewhere.
1860
- Reads are unaffected — the brands widen to `string` and compose with the
1861
- classification brand as `Secret<WrappedValue>`.
1862
-
1863
- `wrapped` and `sealed` stay distinct because a sealed value is one the
1864
- application cannot read back; storing one where the other belongs is a row
1865
- nobody can open.
1866
-
1867
- A `secret` column that has not declared a form now warns (PKU483), and a form on
1868
- a non-text column warns and is dropped (PKU484). Both are warnings, so existing
1869
- projects keep migrating — `pikku db --fail-on-warn` opts into the ratchet, and
1870
- an explicit `form: 'plain'` is the acknowledgement that silences it. The legacy
1871
- `security: 'encrypted'` keeps working and now expands to the pair it always
1872
- meant, `secret` + `wrapped`.
1873
-
1874
- - 9dddff8: Read schema-qualified annotations, and let a project name its default schema.
1875
-
1876
- `DbClassificationMap` nests tables under their schema whenever a project sets
1877
- `db.schema`, but `loadAnnotations` read the sidecar as a flat table→column map.
1878
- It therefore took the schema for a table and each table for a column, found no
1879
- recognised fields, and dropped every annotation on the floor — so a project
1880
- could mark a column `secret` and watch it generate as `private`, with no error
1881
- anywhere. The parser now detects the extra level (a column entry's values are
1882
- primitives; a table's are objects) and flattens it, so both shapes load.
1883
-
1884
- New `db.defaultSchema` drops one schema's qualifier from the generated types:
1885
- with `defaultSchema: 'app'`, `app.user` is queried as `selectFrom('user')` and
1886
- typed as `User` rather than `AppUser`, matching what a project whose
1887
- `search_path` already resolves the schema actually writes. Tables in other
1888
- schemas stay qualified. Where dropping the qualifier would make two tables
1889
- share a name, the table keeps its qualifier and the codegen warns (PKU485)
1890
- rather than letting one silently shadow the other — queries against the loser
1891
- would have typechecked against the wrong columns.
1892
-
1893
- The generated key is what Kysely puts in the SQL, so this is opt-in and
1894
- separate from `db.schema`: setting it for a schema the connection does not
1895
- resolve gives you queries that compile and then fail to find their table.
1896
-
1897
- - 155528a: The PKU910 sessionless-output diagnostic advised marking a column `@public`, a
1898
- SQL-comment annotation syntax that no longer exists. Column classification is
1899
- sourced solely from the hand-authored `db/annotations.ts`, so the message now
1900
- points there.
1901
- - 78b29f0: `SecretService` now returns a `SecretValue<T>` rather than the bare value, so a
1902
- vault secret cannot reach a sink by accident.
1903
-
1904
- `SecretValue` is nominally typed, which means it is not assignable to `string`
1905
- (or to any other concretely-typed field). Every sink with a real type — a
1906
- database column, an email body, a session payload — rejects it with no lint
1907
- rule involved. The sinks typed `any`, `unknown`, or a free generic — the logger,
1908
- queue payloads, webhook and email inputs, and a function's own output — are
1909
- guarded with `Safe<T>`, which collapses a `SecretValue` found anywhere inside
1910
- `T`, however deeply nested, to `never`.
1911
-
1912
- Unwrap deliberately at the point the secret reaches the wire:
1913
-
1914
- ```ts
1915
- const secret = await secrets.getSecret('BETTER_AUTH_SECRET')
1916
- betterAuth({ secret: secret.reveal() })
1917
- ```
1918
-
1919
- Two behaviours cover what types cannot see. Structured serialization redacts —
1920
- `JSON.stringify` and node's inspect both yield `[secret]`, so an audit or log
1921
- write stays honest without crashing the request. String coercion throws
1922
- `SecretCoercionError`, because a template literal is always a leak.
1923
-
1924
- `AuditLog.write` is guarded the same way as the logger, since an audit event
1925
- carries `input` and `metadata` as `unknown` and nominality alone cannot stop a
1926
- secret landing in one.
1927
-
1928
- `.reveal()` is the deliberate escape hatch, and what it hands back is an
1929
- ordinary string as far as every sink signature is concerned. **PKU953** closes
1930
- that gap: under `pikku all --security` the inspector reports a revealed secret
1931
- that flows into a logger, an audit, a queue, an email or a webhook — `console` included.
1932
-
1933
- This also fixed a real one: `remote-addon-auth.ts` called `String(token)` on an
1934
- `unknown` and wrote the result straight into an `Authorization` header.
1935
-
1936
- - Updated dependencies [62ea4cc]
1937
- - Updated dependencies [9dddff8]
1938
- - Updated dependencies [78b29f0]
1939
- - @pikku/core@0.12.76
1940
-
1941
- ## 0.12.53
1942
-
1943
- ### Patch Changes
1944
-
1945
- - fd72e58: Drop `scenario.step` — a scenario step is now always a `given`, `when` or
1946
- `then`.
1947
-
1948
- `step` rendered no keyword, which made it the phase to reach for whenever a
1949
- step did not obviously fit one of the three. That is exactly the step a reader
1950
- cannot check: a scenario is read by people deciding whether it describes the
1951
- behaviour they wanted, and a row that says what it does without saying whether
1952
- it is setup, action or claim tells them nothing to agree or disagree with. It
1953
- was also the escape hatch from the assertion lint — a scenario with no `then`
1954
- could be made to stop complaining by demoting its steps rather than by
1955
- asserting anything.
1956
-
1957
- Replace `scenario.step(...)` with whichever of `given`, `when` or `then` the
1958
- step actually is. `then` is not a rename: it makes the step's bindings
1959
- witnesses rather than alternatives, so every declared surface runs and they
1960
- must agree.
1961
-
1962
- - d041d5b: Let a generated file declare scopes, roles and personas without taking the
1963
- single-declaration slot.
1964
-
1965
- `defineScope`, `defineSystemRole` and `definePersonas` are one-per-codebase, and
1966
- the CLI generates declarations of its own — `user-admin.gen.ts` ships the whole
1967
- `admin` scope tree. Any project that scaffolded one therefore had two
1968
- declarations and failed codegen with PKU583, and the losing file's scopes were
1969
- dropped from the metadata rather than merged.
1970
-
1971
- The rule exists to name the one place a person reads from and adds to, and
1972
- nobody adds to a file the next codegen run overwrites. A generated declaration
1973
- is still extracted; it just neither claims the slot nor collides with the app's
1974
- own.
1975
-
1976
- - fd72e58: Make personas a first-class surface rather than a detail of the test runner.
1977
-
1978
- A persona is now read in three places — the knowledge base resolves `persona:`
1979
- URIs against it, scenarios cast it as an actor, and a virtual user runs as it —
1980
- so it gets its own page at `/personas` under a new **People** section in the
1981
- rail, alongside Users. The card is a profile: avatar, name, job title, computed
1982
- address, the system roles they hold, and how many scenarios cast them. Opening
1983
- one expands each role to the scopes it confers, which is the half of the picture
1984
- that explains a 403.
1985
-
1986
- `definePersonas` takes an optional `avatarUrl` — any URL a browser can load.
1987
- Nothing is derived from the address: a persona's address is synthetic, so a
1988
- derived identicon would be the same shrug for everyone. Omitted, the console
1989
- keeps drawing the deterministic colour-and-icon avatar from the persona's id.
1990
-
1991
- - fd72e58: Read the actors that are not people on the personas page.
1992
-
1993
- The platform — the app acting on itself, what `pikkuPlatformScenarioStep`
1994
- declares — now has a row of its own, alongside one per addon whose system a
1995
- step makes act. They sit behind a People / System / All filter that opens on
1996
- the people: a subject holds no roles and signs in as nobody, so leading with it
1997
- would put the rows nothing is authorized through above the ones that are.
1998
-
1999
- The platform row is built in rather than derived. A project that has never
2000
- written a platform step still has a platform, and a card that appeared the
2001
- moment somebody declared their first step would read as a feature they had
2002
- switched on.
2003
-
2004
- Also: PKU680 now counts `expectService`, `expectError` and `expectEventually`
2005
- as assertions. They are inline steps and carry no phase, so a scenario whose
2006
- only witness was a recorded service call was being told it never asserts.
2007
-
2008
- - 894b2f8: `defineScope` and `defineSystemRole` accumulate across call sites again. Only `definePersonas` is one-per-codebase.
2009
-
2010
- The previous release made all three single-declaration constructs, which no project scaffolding user-admin could satisfy: the CLI generates a `defineScope` of its own in `user-admin.gen.ts` carrying the whole `admin` tree, and `@pikku/addon-console` spells the same tree out again, so a second hand-written declaration failed the build with PKU583 — and the losing file's scopes were dropped from the metadata rather than merged.
2011
-
2012
- Exempting generated files would have reinstated exactly the ambiguity the rule removes, only for the files nobody can read the rule from. The real fix is for `admin` to be a default scope nobody declares, at which point the rule can come back for scopes and roles.
2013
-
2014
- `definePersonas` is unaffected: nothing generates one, so its single call site stands.
2015
-
2016
- - dd19aa7: Drop `scopes` from sessionless functions, rename `selfAuthenticated`, and make both
2017
- escape hatches opt-in.
2018
-
2019
- **`scopes` are gone from `pikkuSessionlessFunc`.** They are AND-ed and `verifyScopes`
2020
- fails closed on a session that does not exist, so every scope listed on a sessionless
2021
- function rejected the anonymous caller it exists to serve. `CorePikkuSessionlessFunctionConfig`
2022
- now states this once in core, and the generated `pikkuSessionlessFunc` / `pikkuVoidFunc`
2023
- configs derive from it — so the field is absent rather than subtracted.
2024
-
2025
- `@pikku/addon-console`'s `installAddon` and `installOpenapiAddon` are now `pikkuFunc`.
2026
- Both set `auth: true` and `scopes: ['admin']`, and a test exercises that gate, so the
2027
- scopes were load-bearing — they only compiled as sessionless because the config accepted
2028
- a field it could not honour. No behaviour change: both already required a session.
2029
-
2030
- **`selfAuthenticated` is now `permissionsInBody`.** It never described authentication:
2031
- what it records is that the permission check lives in the function body rather than in a
2032
- declared `permissions` entry.
2033
-
2034
- **Both escape hatches must be opted into**, via a new `allow` block in
2035
- `pikku.config.json`:
2036
-
2037
- ```json
2038
- "allow": { "permissionsInBody": true, "complexWorkflows": true }
2039
- ```
2040
-
2041
- Unset means unavailable, and using the feature is a build error naming the flag that
2042
- would permit it — PKU576 for `permissionsInBody`, PKU643 for `pikkuWorkflowComplexFunc`.
2043
- Both trade something the tooling can inspect for something only a reader can verify: a
2044
- permission check buried in a body, or workflow steps that cannot be serialized into the
2045
- graph, replayed, or migrated. Both are occasionally right, and both are the path of least
2046
- resistance whenever the declarative form is merely inconvenient. Whoever owns the project
2047
- makes that call once, in writing, instead of every author making it silently at the call
2048
- site.
2049
-
2050
- **PKU574's message no longer contradicts any of this.** Every function it reports is
2051
- sessionless — that is how the population is selected, not a finding — yet it opened by
2052
- reporting that they "require neither a session", then advised adding scopes. It now names
2053
- them as sessionless and recommends only gates an anonymous caller can meet:
2054
- `permissions`, `auth: true`, `wireAddon({ auth: true })`, or dropping `expose: true`.
2055
- `permissionsInBody` is deliberately absent from that list: a diagnostic should not
2056
- advertise its own escape hatch.
2057
-
2058
- - 50ec500: Make `defineScope`, `defineSystemRole` and `definePersonas` single-declaration constructs
2059
- — exactly one call site per codebase, the rule `pikkuBetterAuth` has always had.
2060
-
2061
- Each of the three already takes a keyed object, so one call declares as many entries as
2062
- you like. Spreading the calls across files bought nothing and cost the thing that matters:
2063
- there was no answer to "where do I add a persona?", so downstream tooling and agents had
2064
- nowhere unambiguous to read from or append to. The only duplicate handling that existed
2065
- caught a narrow case — the same id declared twice with different content — and said
2066
- nothing about the same id declared twice in two files.
2067
-
2068
- A second call now fails the build with `PKU583` (`defineScope`), `PKU584`
2069
- (`defineSystemRole`) or `PKU585` (`definePersonas`), naming both source files and saying
2070
- to declare them all in one call. A second call in the _same_ file is refused too: "the
2071
- file" is not an answer either when the file holds two calls.
2072
-
2073
- - Updated dependencies [32277d5]
2074
- - Updated dependencies [ea8aabf]
2075
- - Updated dependencies [33e96ab]
2076
- - Updated dependencies [fd72e58]
2077
- - Updated dependencies [fd72e58]
2078
- - Updated dependencies [fd72e58]
2079
- - Updated dependencies [894b2f8]
2080
- - Updated dependencies [dd19aa7]
2081
- - Updated dependencies [50ec500]
2082
- - @pikku/core@0.12.75
2083
-
2084
- ## 0.12.52
2085
-
2086
- ### Patch Changes
2087
-
2088
- - 8075f6a: Confine `SecretService` to the places an app is wired.
2089
-
2090
- `secrets` is now omitted from the services every function, AI agent, workflow,
2091
- permission and wire receives, and the function runner replaces it with a
2092
- throwing accessor so a cast cannot reach past the type. It stays available in
2093
- `pikkuServices`, `pikkuWireServices`, addon service factories and middleware —
2094
- read a secret there, give it to a service, and have the function ask that
2095
- service.
2096
-
2097
- Alongside it:
2098
- - `wireSecret` gains `allowedHosts`, refusing a secret attached to a host it was
2099
- not declared for. Permissive by default; strict via
2100
- `config.secrets.requireAllowedHosts`.
2101
- - `pikku-graph`'s `httpRequest` resolves and attaches its credential inside a new
2102
- `httpRequester` service instead of holding the plaintext in the function.
2103
- - New inspector diagnostics: `PKU950` (a `SecretService` exposed under another
2104
- service name), `PKU951` (a secret read that no `wireSecret` declares) and
2105
- `PKU952` (a secret read with a non-literal key).
2106
-
2107
- - Updated dependencies [6a307f0]
2108
- - Updated dependencies [afef587]
2109
- - Updated dependencies [8075f6a]
2110
- - @pikku/core@0.12.74
2111
-
2112
- ## 0.12.51
2113
-
2114
- ### Patch Changes
2115
-
2116
- - c984df6: Give an agent's tools back the descriptions their authors wrote
2117
-
2118
- A tool's description is what the model is told the tool does, and the main
2119
- thing it chooses between tools on. It was not reaching the model. `description`
2120
- is classed as a verbose field, so it is stripped from the metadata bundled into
2121
- the generated bootstrap — the copy `pikkuState('function', 'meta')` is built
2122
- from. `buildToolDefs` read the description from there, found it always
2123
- undefined, and fell through to the tool's own name. Every agent has been
2124
- choosing between bare identifiers. The same fallback was offering an addon's
2125
- MCP tools under their names, for the same reason.
2126
-
2127
- Tool definitions now resolve descriptions through `metaService`, which reads
2128
- the verbose metadata and falls back to the minimal copy, so the authored text
2129
- is recovered wherever the generated `.pikku` directory is readable. Where it is
2130
- not — no `metaService`, or a deployment shipping only the stripped copy — a
2131
- tool falls back to its name, which is what it did before. Addon metadata is
2132
- likewise loaded verbose-first. `title` is no longer part of the chain: a title
2133
- labels a tool in a UI, it does not tell a model when to reach for it.
2134
-
2135
- An addon has to ship the verbose file for any of this to reach it. `tsc` only
2136
- emits the JSON it sees imported and nothing imports the verbose meta, so the
2137
- bundled addons now copy it into `dist` explicitly.
2138
-
2139
- `ref()` is resolved at build time. It used to be pushed through codegen as an
2140
- opaque string, so `ref('todos:doesNotExist')` generated cleanly and failed only
2141
- when the agent ran. The inspector now resolves each reference against the
2142
- project's functions, or — using the namespace-to-package mapping `wireAddon`
2143
- already provides — against the addon's own metadata, and reports an unwired
2144
- namespace (`PKU152`) or a missing function (`PKU153`) at codegen. An addon that
2145
- has not been built yet contributed no metadata and is skipped rather than
2146
- reported missing.
2147
-
2148
- New `pikku --strict-meta` additionally fails the build on any agent tool with
2149
- no description (`PKU154`), including tools reached through an addon. It is off
2150
- by default, so nothing that builds today stops building; turn it on to hold a
2151
- project to the metadata its agents actually run on.
2152
-
2153
- - 63ff32b: Run a CLI's commands on the server, over the connection the client opened
2154
-
2155
- A CLI that talks to a service has to ship the service's command tree, so the
2156
- two versions drift: the binary someone installed months ago still believes in
2157
- flags and commands the server has since changed. This makes the command tree
2158
- the server's, and leaves the client holding only a socket.
2159
-
2160
- `wireCLI` gains `auth`, and a program wired with a channel entrypoint now
2161
- generates a `__raw` route: the client forwards argv untouched, the server
2162
- parses it, runs the command, and streams the output back as it happens. The
2163
- terminating frame carries the exit code, so a failed remote command still exits
2164
- non-zero locally. Renderers stay on the client and are matched by the command
2165
- id the server reports; an unrecognised command falls back to JSON rather than
2166
- failing.
2167
-
2168
- Every channel gains `channel.remote(...)`: calling a function on the peer at
2169
- the other end of the connection and waiting for its answer. A channel is
2170
- otherwise fire-and-forget in both directions, so this is what reaches a peer
2171
- that has no address of its own — a CLI on a laptop, a browser tab, a sandbox
2172
- behind NAT. It is on `channel` rather than `rpc` because it is bound to one
2173
- connection: which peer answers is the socket the call goes out on, not
2174
- something the RPC map could resolve. Any `wireChannel` gets it — a client
2175
- registers what it is willing to answer to, and a name outside that list is
2176
- refused.
2177
-
2178
- Requests are correlated by id, time out, and fail fast when the socket closes
2179
- rather than waiting out the timeout. Replies are taken off the socket ahead of
2180
- routing, so a channel needs no route for them and an answer can never be
2181
- mistaken for a new message; the transport is created on first use and released
2182
- when the channel closes, which is also what fails anything the departing peer
2183
- still owed an answer to. Channels that only flow one way — SSE, an agent's
2184
- output stream, a locally-run CLI — refuse the call outright instead of waiting
2185
- for an answer that was never going to come.
2186
-
2187
- What a peer answers with is its word, so it is checked before a caller sees it
2188
- — against the schema codegen already generated from the function's declared
2189
- return type, the same one an agent tool or an HTTP response is checked against.
2190
- A capability is declared with `pikkuRemoteChannelFunc`, which takes the usual
2191
- `title` / `description` / `input` / `output` but no `func` — this side owns the
2192
- contract, the peer owns the body. It registers under its name like any other
2193
- function, so `channel.remote` is typed off the same generated map as
2194
- `rpc.remote` and no caller has to cast, and a local call throws rather than
2195
- missing: reaching it locally means a command asked the server for something
2196
- only a client knows. A client on an older build fails the call it answered
2197
- rather than the caller failing later somewhere with no reason to expect a bad
2198
- shape; a name with no declared contract is left alone. Both frame guards
2199
- validate the whole envelope rather than the action tag alone, and a failure
2200
- payload with a non-string name or message falls back rather than being attached
2201
- to an `Error`.
2202
-
2203
- The arguments going the other way are checked too, against the schema for the
2204
- capability's declared input, before anything is registered or sent. That is not
2205
- a boundary — the peer runs the code and has to check what it was handed, and a
2206
- caller that meant harm would send arguments that pass. It catches drift, where a
2207
- server built against a newer capability signature calls a client that predates
2208
- it, and fails it here rather than inside someone else's process.
2209
-
2210
- A channel-driven CLI command uses this to ask its caller for machine-local
2211
- facts mid-run — a git sha, a working tree, a local file. The CLI wire's own
2212
- channel is synthetic (it exists so a command can stream progress without
2213
- knowing where that goes), so it delegates `remote` to the connection the
2214
- command actually arrived on.
2215
-
2216
- Because that runs code on someone's machine at a remote caller's request, the
2217
- capability map says what _can_ run and approval says whether a particular call
2218
- _should_. A capability may be declared `{ execute, needsApproval }`, sharing
2219
- `ApprovalPolicy` — `needsApproval` and `approvalDescriptionFn` — with
2220
- `AIAgentToolDef`, which has carried both since before channels could call back:
2221
- both are an allowlist of named callables invoked by something other than the
2222
- code that wrote them. The runtime around them is deliberately not shared, since
2223
- an agent suspends its run and resumes it later while a reverse call is a live
2224
- await with a person at the other end.
2225
-
2226
- A capability written as a bare function is unclassified, and unclassified means
2227
- approval is required — the annotation nobody got round to writing is the one
2228
- most likely to matter, so it fails closed. Declare
2229
- `{ execute, needsApproval: false }` for a capability that may run unattended.
2230
- Nothing infers this: core cannot tell a read-only capability from a destructive
2231
- one, so `needsApproval: false` is the author asserting it, and the assertion is
2232
- the only thing standing between a remote caller and the machine.
2233
-
2234
- The default is the opposite of `AIAgentToolDef`'s, where absence means "do not
2235
- ask" — a tool is written by the same people who run the server it executes on,
2236
- and a capability is not.
2237
-
2238
- `executeRawCLIViaChannel` reads `--auto-approve` and
2239
- `--dangerously-auto-approve` out of argv (or `PIKKU_AUTO_APPROVE` /
2240
- `PIKKU_DANGEROUSLY_AUTO_APPROVE`) and strips them before argv reaches the
2241
- server — what may run on this machine is this machine's decision, and a flag
2242
- the server can see is one the server could act on. `--auto-approve` permits the
2243
- classified-safe set and refuses the rest; `--dangerously-auto-approve` permits
2244
- everything and says so once on stderr. Interactively the user is asked per
2245
- call, with `y` / `n` / `a`, where `a` is remembered for that one capability for
2246
- the rest of the run and never written to disk — widening it to the session
2247
- would quietly turn an interactive run into `--dangerously-auto-approve`. A run
2248
- with no terminal and no flag refuses rather than assuming yes, because CI is
2249
- exactly where an unattended `git push` would otherwise happen. The tiers are
2250
- meaningful here in a way they would not be for an agent: the caller is a
2251
- deterministic program whose source can be read, so "these calls are always
2252
- fine" is a claim someone can actually justify.
2253
-
2254
- A peer that is asking a human sends a pending frame first, which stops the
2255
- caller's timeout. Without it any approval slower than the timeout would fail
2256
- the call and then discard the decision when it finally arrived. The call is
2257
- still failed the moment the socket drops — what actually happens when a peer
2258
- dies mid-prompt — and a peer that sends the frame dishonestly can do nothing
2259
- but keep its own call waiting. A refusal is sent as an answer, so a denied call
2260
- fails its command immediately rather than hanging.
2261
-
2262
- Fixes found on the way, each of which broke this path:
2263
- - A websocket upgrade wrote middleware headers (CORS, on every request)
2264
- straight onto the socket, so the first bytes a client saw were headers rather
2265
- than `ws`'s `101` status line and the handshake failed to parse. Header
2266
- writes are now buffered and flushed behind a status line only when the
2267
- upgrade is actually being rejected.
2268
- - An upgrade socket had no error listener while the channel opened, so a client
2269
- that gave up mid-handshake took the whole server process down with an
2270
- unhandled `ECONNRESET`.
2271
- - `onConnect` and `onDisconnect` never saw the session established during the
2272
- upgrade, so a channel could not tell who had just connected.
2273
- - Setting the routing key on a channel result mutated the value in place, which
2274
- throws for a primitive under ESM strict mode.
2275
-
2276
- - a7b26c5: rename the inspected declarations to `define*`: `wireScope` → `defineScope`, `wireSecret` → `defineSecret`, `wireVariable` → `defineVariable`, `wireCredential` → `defineCredential`
2277
-
2278
- `wire*` meant two unrelated things. A transport wiring attaches a function to
2279
- something that can invoke it — `wireHTTP`, `wireChannel`, `wireScheduler`,
2280
- `wireQueueWorker` and the rest — and the thing it wires runs. These four wire
2281
- nothing: they are no-ops that exist only so the call typechecks, they are
2282
- tree-shaken out of the build, and their whole job is to be found by the
2283
- inspector's AST pass and turned into a type union. One word for both left the
2284
- declaration reading like a registration with a runtime.
2285
-
2286
- So the vocabulary splits: **`wire*` is a transport, `define*` is an inspected
2287
- declaration.**
2288
-
2289
- ```ts
2290
- import { defineScope } from '@pikku/core/scope'
2291
- import { defineSecret } from '@pikku/core/secret'
2292
- import { defineVariable } from '@pikku/core/variable'
2293
- import { defineCredential } from '@pikku/core/credential'
2294
-
2295
- defineScope({ admin: { scopes: { invoices: { scopes: { create: {} } } } } })
2296
- ```
2297
-
2298
- **Breaking:** no alias is kept. Rename the four call sites; the module subpaths
2299
- (`@pikku/core/scope`, `/secret`, `/variable`) are unchanged.
2300
-
2301
- The inspector matches these by identifier text, so a stale `wire*` call is not a
2302
- type error — it is silently not extracted, and the generated union comes back
2303
- empty. That fails as "this scope isn't declared" on code that was fine a moment
2304
- ago, nowhere near the declaration. Grep for the old names rather than trusting a
2305
- clean build.
2306
-
2307
- An addon published with `.pikku` output generated before this release re-exports
2308
- `wireSecret` from `@pikku/core/secret` and will not typecheck against this core
2309
- until it is rebuilt and republished.
2310
-
2311
- - 457cb25: Add `definePersonas()`: the people a project's scenarios and virtual users run
2312
- as, declared in code.
2313
-
2314
- There used to be three names for two-and-a-bit things — an _actor_ in
2315
- `scenarios.actors`, a _persona_ in `scenarios.personas`, and a _virtual user_
2316
- declared separately against an actor. In practice almost every actor was its own
2317
- kind, so the second set carried no information and the third was a third place
2318
- for a name to drift. There is now one declaration:
2319
-
2320
- ```ts
2321
- definePersonas({
2322
- shopper: {
2323
- name: 'Sam Shopper',
2324
- jobTitle: 'Shopper',
2325
- personality: 'Buys in a hurry and leaves tabs open',
2326
- roles: ['customer'],
2327
- disposition: 'careless',
2328
- goals: ['Buy something without reading anything'],
2329
- account: {},
2330
- },
2331
- })
2332
- ```
2333
-
2334
- A persona is a person: what they are like, what they want, the roles they hold,
2335
- and **one** account they sign in with — `account: {}` plus `linkedAccounts` for
2336
- the rare case of more, modelled on how better-auth does linking. A persona with a
2337
- `disposition` is a virtual user; `runnable: false` marks someone who only ever
2338
- exists to be acted upon — banned, shared with, reset — and is never handed a
2339
- session.
2340
-
2341
- **A persona names roles, never scopes.** Scopes come from `defineSystemRole()`
2342
- expansion, so the build fails if a persona names a role nobody declared, and
2343
- fails again if a role confers a scope no `defineScope` declares. Running one only
2344
- ever has to check that its roles are still valid.
2345
-
2346
- **Addresses are computed, never declared.** `personaEmail(id, domain, runId)`
2347
- derives `<id>[+runId]@<domain>` from `scenarios.emailDomain`, so a seed, a
2348
- scenario run and a virtual-user run cannot disagree about who they are signing in
2349
- as. `scenarios.actors` and `scenarios.personas` are gone from
2350
- `pikku.config.json` — only `emailDomain` remains.
2351
-
2352
- `actor` survives in exactly one place: the name of a **slot in a scenario step**,
2353
- which is the role a persona is cast in for that step. `pikkuVirtualUser()`,
2354
- `kind`, `grants` and the `actor` field are removed; the `actors` service is now
2355
- `personas`, and the CLI's `virtual-user` commands are now `pikku persona list` /
2356
- `pikku persona run`. `budget` and `allowApprovalRequired` moved to run flags —
2357
- how much you will spend today is not a fact about a person.
2358
-
2359
- `@pikku/cucumber` drops its `Actor` class and `ActorDispatchContext`: a
2360
- hand-rolled cookie jar that a persona's own typed session replaces outright.
2361
-
2362
- - f7567ad: Add `defineSystemRole()`: roles that ship with the product, declared in code.
2363
-
2364
- A system role is to a console-composed role what an AWS managed policy is to a
2365
- customer-managed one — the console may show and grant it, but not rename,
2366
- re-scope or delete it. The CLI extracts declarations by AST and generates a
2367
- `SystemRoleName` union, so naming a role that does not exist fails the build,
2368
- and a role granting a scope no `defineScope` declares fails it too.
2369
-
2370
- Removal is additive on the same terms as `defineScope`: deleting a declaration
2371
- leaves an inert row rather than revoking everyone's grant mid-deploy.
2372
-
2373
- `ScopeService` gains `syncSystemRoles`, `findStaleSystemRoles` and
2374
- `pruneSystemRoles`; `Role` gains `system` and `declared`. Implementations
2375
- enforce immutability through the shared `assertRoleIsMutable` /
2376
- `assertRoleNameAvailable` guards rather than each inventing the rule.
2377
-
2378
- - 457cb25: Let a persona do a real job in production, and say where it may act.
2379
-
2380
- A persona was only ever a test subject: something you pointed at a stage to find
2381
- out what the product does wrong. But the same declaration — a name, a job, the
2382
- roles it holds and what it is trying to get done — describes a teammate doing
2383
- the work for real, and nothing about the engine cared which one it was.
2384
-
2385
- Four changes make that difference explicit and enforced.
2386
-
2387
- **`environments` moves to the top level of `pikku.config.json`**, out from under
2388
- `scenarios`. It was never a scenario's anything: `persona run` targets one, and
2389
- now so does `persona sync`. An environment may be flagged `production: true` —
2390
- a flag rather than a reserved name, because projects call it `prod`, `live` or
2391
- `eu-prod`, and more than one environment can be production.
2392
-
2393
- **A persona may name its `environments`.** Omitting them means every configured
2394
- environment _except_ the production ones, so nothing reaches production by being
2395
- forgotten. Naming a production environment requires `disposition: 'accountable'`.
2396
- The rule is checked twice, on purpose: the inspector refuses to generate a
2397
- declaration that breaks it, and sign-in re-checks against the environment
2398
- actually resolved — the build check trusts the file, and the run check does not
2399
- trust which artifact got deployed. An unresolved environment fails closed.
2400
-
2401
- **`disposition: 'accountable'`** is that production disposition. It sits opposite
2402
- `adversarial` on the intent axis rather than the care axis: what it changes stays
2403
- changed, every call is recorded against its name, and it stops to ask rather than
2404
- acting and reporting afterwards. Alongside it, **agents now appear in a persona's
2405
- computed catalogue**, gated by the same scopes as the RPCs — an agent is reached
2406
- rather than declared, so a persona finds the specialists its roles unlock and
2407
- chooses between calling the API itself and handing the work over. That also fixes
2408
- a latent gap: `talkTo` was wired at the target but never advertised in the
2409
- instructions, so it was never used.
2410
-
2411
- **`pikku persona sync <environment>`** provisions them: it creates each account
2412
- and applies the roles it declares, additively, and never revokes. Seeding is test
2413
- data and `db seed` does not run in production; a teammate doing a real job still
2414
- needs an account and its grants. It needs both halves of an environment — its API
2415
- to sign the person in, its database to write the grants — and `--dry-run` reports
2416
- who would be provisioned, with what, and why anyone was skipped.
2417
-
2418
- In the console, a virtual user now says where it may act — the environments it
2419
- named, or the rule when it named none — and its dossier carries the `sync`
2420
- command alongside the `run` one, because the account is not a by-product of a
2421
- run. `accountable` reads as a disposition like the rest.
2422
-
2423
- - 86a50b9: scenario: replace `browser: true` + `func` with per-surface bindings on `pikkuScenarioStep`
2424
-
2425
- A step now declares one implementation per surface it can be driven through:
2426
-
2427
- ```ts
2428
- export const buysTheItem = pikkuScenarioStep<{ sku: string }, { orderId: string }>({
2429
- name: 'buysTheItem',
2430
- description: 'buys the item',
2431
- browser: async (services, data, { browser }) => { ... },
2432
- default: async (services, data, { rpc }) => { ... },
2433
- })
2434
- ```
2435
-
2436
- `pikku scenario run --run browser|cli|default` picks which surface the run drives,
2437
- and the two phases resolve bindings differently:
2438
- - **Actions** (`given` / `when` / `step`) run exactly one binding — the run
2439
- surface if it has one, otherwise `default`. A step with neither now fails with
2440
- `ScenarioNoSurfaceBinding` instead of silently running server-side.
2441
- - **Assertions** (`then`) are witnesses, not alternatives: every declared binding
2442
- runs and they must agree. Two surfaces reporting different things fails the run
2443
- with `ScenarioWitnessDisagreement` rather than reporting a pass. An assertion
2444
- with no witness the run can execute at all fails with `ScenarioNoWitness` —
2445
- without it the step returns `undefined` and renders as a tick, reporting a pass
2446
- for something nobody checked.
2447
-
2448
- A scenario written as a step ladder that never calls `then` is now a **PKU680**
2449
- critical. It proves only that nothing threw, so an assertion-free ladder of
2450
- browser-bound actions would score perfect coverage while checking nothing.
2451
-
2452
- The report gains a surface-coverage line — `n/m steps ran on browser`, counted
2453
- over every step, so an action that fell back to the server lowers the ratio
2454
- rather than needing a footnote. That also makes surfaces comparable over one
2455
- denominator: a scenario is `4/4` on a default run and `3/4` on a browser one.
2456
- Assertions that fell back are named separately and gate `--strict`, since a
2457
- sentence claiming the actor saw something nobody looked at is a different problem
2458
- from an action taking a shortcut.
2459
-
2460
- **Breaking:** `browser: true` and the third `B extends boolean` type argument are
2461
- gone. Rename `func` to `default` (or to `browser` where the step drove a browser)
2462
- and drop the type argument.
2463
-
2464
- - 0e0f6eb: Add virtual users: LLM-driven synthetic users that work a real stage in
2465
- character.
2466
-
2467
- A scenario proves a path somebody thought of. A virtual user works the same
2468
- ground without the script — it signs in as a declared persona over the app's own
2469
- auth, is handed the scenarios' BDD prose and the schema of every endpoint it may
2470
- reach, and decides for itself what to do. It asserts nothing; a run produces
2471
- findings, and their absence only ever means "not this time, not with this seed".
2472
-
2473
- There is nothing extra to declare. A persona with a `disposition` is a virtual
2474
- user, and running it is what makes it one — see the `definePersonas` changeset
2475
- for the declaration itself. Listing, describing or running one never loads the
2476
- app: the inspector reads the literal declaration, the CLI writes
2477
- `scopes/pikku-personas-meta.gen.json`, and `MetaService.getPersonasMeta()`
2478
- serves it.
2479
-
2480
- **Dispositions are engine dials, not prose.** Each carries its own intent weights
2481
- (continue / suspend / resume / abandon), temperature, re-read and repeat rates,
2482
- and switches: `careless` puts things down and picks them up in the wrong order,
2483
- `newcomer` starts with no memory, `auditor` is never offered a mutation,
2484
- `adversarial` is shown the catalogue its roles do not cover — being offered a
2485
- call it should not be able to make is the test — while those roles stay live as
2486
- the oracle, so a success outside them is authorization drift rather than a pass.
2487
-
2488
- **Nothing is retrieved against.** The whole reachable catalogue goes into the
2489
- instructions (~8k tokens on a 430-RPC project, cached for the run), because a
2490
- ranking function would make the user only as adventurous as the ranking and lose
2491
- exactly the endpoints worth stumbling into. Schema first: an endpoint must be
2492
- described before it may be called.
2493
-
2494
- **No money in core.** The engine counts steps, calls, mutations and tokens; what
2495
- they cost is the app's to decide through `stop(tally)`.
2496
-
2497
- CLI: `pikku persona list` and `pikku persona run <environment> [name]`, with
2498
- flags overriding a declaration for reproduction (`--seed`, `--steps`,
2499
- `--disposition`). Spending is a run flag too — `--steps`, `--mutations` and
2500
- `--duration` bound a run, because how much you will spend today is not a fact
2501
- about a person. Console: a Virtual Users screen beside Scenarios, built out of
2502
- core's own derivation functions so it shows a run's actual inputs rather than a
2503
- second implementation of them.
2504
-
2505
- `dev-ai-runner` now ships its own `@pikku/ai-vercel` and
2506
- `@ai-sdk/openai-compatible` instead of requiring them from the project. Behind a
2507
- proxy one openai-compatible provider answers for every prefix, so there was never
2508
- a per-vendor package worth making somebody install; the project's copies still
2509
- win when it has them, and both load from the same place or neither does.
2510
-
2511
- - Updated dependencies [c984df6]
2512
- - Updated dependencies [63ff32b]
2513
- - Updated dependencies [ba6cc08]
2514
- - Updated dependencies [d007191]
2515
- - Updated dependencies [a7b26c5]
2516
- - Updated dependencies [457cb25]
2517
- - Updated dependencies [f7567ad]
2518
- - Updated dependencies [ba6cc08]
2519
- - Updated dependencies [a2e21e5]
2520
- - Updated dependencies [457cb25]
2521
- - Updated dependencies [86a50b9]
2522
- - Updated dependencies [0e0f6eb]
2523
- - @pikku/core@0.12.73
2524
-
2525
- ## 0.12.50
2526
-
2527
- ### Patch Changes
2528
-
2529
- - b89d3b3: Bring the knowledge base into OSS: a package, a CLI gate, a console browser and a skill
2530
-
2531
- `knowledge/` is where a project records the things `pikku meta` cannot tell you —
2532
- what a slice is for, which rule was chosen and what it rules out, what is still an
2533
- open question. Tables, routes, schemas and permissions are generated, so a note
2534
- that repeats them is a copy that will drift, and the profile refuses the sections
2535
- where that happens.
2536
- - **`@pikku/knowledge`** (new) reads the notes, builds the link graph in both
2537
- directions, and validates the app-project profile: every note typed, every
2538
- section indexed, every slice carrying a third-person gherkin scenario and at
2539
- most three entities, and every `resource:` URI resolving against the generated
2540
- meta. The resource check fails closed on drift and open on ignorance — a prefix
2541
- whose meta is absent is skipped rather than called dangling.
2542
- - **`pikku knowledge validate`** and **`pikku knowledge index`** replace the dead
2543
- three-flat-files check. Both exit non-zero on an inconsistent base, so a
2544
- pipeline can stop on one; `index` refreshes each `index.md` listing while
2545
- leaving the prose around it alone, and now gives a section that holds only
2546
- sub-sections an index of its own instead of leaving it unreachable.
2547
- - **The console** gains a read-only Knowledge page: notes grouped by section,
2548
- a rendered document with its tags, resources, links in both directions and the
2549
- findings against it, and intra-bundle markdown links that open the linked note
2550
- instead of leaving the page. Read-only by design — a note is edited in the repo,
2551
- in the same commit as the code it describes.
2552
- - **The `pikku-knowledge` skill** documents the format for agents, and Fabric
2553
- builds on it rather than restating it.
2554
- - **`@pikku/inspector`**: a zod schema imported from a built workspace package
2555
- resolved to that package's `.d.ts`, which has no runtime exports at all, so
2556
- every schema in it was reported missing. The emitted JS beside it is imported
2557
- instead.
2558
-
2559
- - b89d3b3: Invalidate the TS-schema cache when a type it was built from changes
2560
-
2561
- The on-disk schema cache was keyed on the synthesized custom-types source, the
2562
- generator options and the inspector version — but not on the types it actually
2563
- resolves. A named type (anything a function declares as its input or output)
2564
- appears in that source only as a name; its definition is read out of a project
2565
- file or a dependency's `.d.ts`. Change the shape without changing the name and
2566
- the key is identical, so pikku serves a schema for a type that no longer looks
2567
- like that, and requests are validated against it.
2568
-
2569
- The stale schema also outlives `rm -rf .pikku`, because the cache lives in
2570
- `node_modules/.cache/pikku/ts-schemas.json`.
2571
-
2572
- Codegen now records the files each schema set was derived from, storing their
2573
- mtime and size beside it, and regenerates when any of them moves or disappears.
2574
- The schema program is rooted at the virtual types file, so its source files are
2575
- exactly the transitive closure the schemas depend on. A cache written by an
2576
- earlier version carries no dep list and is treated as stale. The check costs
2577
- about 3ms per cache hit, and also guards the in-process cache, so `pikku dev`
2578
- picks up a type edit without a restart.
2579
-
2580
- - Updated dependencies [384e484]
2581
- - Updated dependencies [b5a73fb]
2582
- - Updated dependencies [6be5ab0]
2583
- - @pikku/core@0.12.72
2584
-
2585
- ## 0.12.49
2586
-
2587
- ### Patch Changes
2588
-
2589
- - 426610a: Scenario instrumentation is no longer scaffolded into projects, and no longer deploys.
2590
-
2591
- `scaffold.scenarios` generated four functions — `pikkuScenarioTakeLiveCoverage`, `pikkuScenarioResetLiveCoverage`, `pikkuScenarioResetStubs`, `pikkuScenarioGetStubCalls` — into the project's own source. As project source they were indistinguishable from application code: registered in the app bootstrap, listed in the app's function and RPC meta, and shipped `expose: true` inside every deployed bundle. Coverage and stub inspection are things you do to a development server; production carried two endpoints that fingerprint the build and one that resets a global tracker, gated only by whether a metadata file happened to sit beside the bundle.
2592
-
2593
- `pikku dev` now registers the implementations itself, after the app bootstrap. Nothing is generated, nothing is written to the project, and a bundle cannot carry what was never in its bootstrap — `pikku serve` and every deployed unit have no trace of them. The scenario runner reaches them over `/rpc/<name>` exactly as before.
2594
-
2595
- Also:
2596
- - The inspector ignores these four names wherever it finds them, so a project that has not regenerated — and still has the scaffolded file checked in — stops deploying it immediately. Codegen deletes the retired scaffold on its next run.
2597
- - They no longer count towards a project's function total, so `pikku scenario --coverage` stops reporting four permanently-uncovered functions that were never the project's to cover.
2598
- - The instrumentation no longer carries schemas (there was nothing to validate but one optional string), which drops the `zod` dependency the scaffold silently required of every project that enabled it.
2599
- - They are registered sessionless, so `scaffold.scenarios: true` — as opposed to `'auth'` — now genuinely means "no session required". As a sessioned `pikkuFunc` with `auth: false`, it demanded a session anyway and logged a warning saying so.
2600
-
2601
- - 09973b9: Scenarios, features and steps no longer reach a deployment.
2602
-
2603
- Steps were already held back from the app bootstrap, so a deployed server never imported a step body. Everything _about_ a scenario still travelled with the application: a `pikkuScenario(...)` is a function, so its name, schemas and hashes sat in the app function meta; the schemas it and its steps validate against sat in the app's `register.gen.ts` — on one project 458 of the 582 registered schemas belonged to tests; its name sat in the internal RPC meta; and because a scenario is _also_ a workflow, the inspector synthesised a `wf-orchestrator-<scenario>` queue worker for each one. The deploy analyzer, which reads inspector state rather than the partitioned codegen output, then read all of it back as application code: a unit per scenario, a `WorkflowDefinition` per scenario, and a real queue per scenario. A 13-scenario suite turned into 13 production queues named after tests, waiting for a provider to create them.
2604
-
2605
- The existing scenario/app partition is now applied everywhere it was missing. `FunctionRuntimeMeta` gains a `scenario` marker (the counterpart of `scenarioStep`) so a scenario body is recognisable without walking the workflow graph; scenario bodies join their steps on the scenario side of the function-meta and registration split; schemas only a scenario or step needs are written and registered under `.pikku/scenarios/schemas/` and imported by the scenario bootstrap alone; scenario names are dropped from the internal RPC meta; no orchestrator queue worker is synthesised for a scenario; and the deploy analyzer drops both scenario functions and scenario workflows before it decides what a deployment contains.
2606
-
2607
- The MCP metas are keyed by wiring rather than by function, so a scenario wired as an MCP tool, resource or prompt was the one id that still reached the manifest after the function and workflow filters — as an endpoint on the gateway plus a gateway dependency on a unit that was never emitted. Those ids are now filtered too.
2608
-
2609
- `scenarioSchemaDirectory` is rejected when it resolves to the same directory as `schemaDirectory`. A schema write owns its directory — it emits `register.gen.ts` and prunes every schema file its own required-set does not name — so sharing one would replace the application register with the scenario-only one and delete the app's schema files, which nothing downstream can detect.
2610
-
2611
- Nothing changes for `pikku scenario run` — the scenario bootstrap still registers every scenario, feature, step, meta and schema. What changes is that a bundle stops carrying them.
2612
-
2613
- - Updated dependencies [8a2c993]
2614
- - Updated dependencies [a261006]
2615
- - Updated dependencies [09973b9]
2616
- - @pikku/core@0.12.71
2617
-
2618
- ## 0.12.48
2619
-
2620
- ### Patch Changes
2621
-
2622
- - 04b8607: Make the unmodellable-for-of diagnostic tell you what is actually wrong. It always blamed the iterable ("its iterable must be a data array"), which was false whenever the real cause was a `workflow.do` nested inside an `if`/`switch` — a DSL fanout body is a flat list of steps with no branch member, so the diagnostic now says so and points at `.filter` or `pikkuWorkflowComplexFunc`. Also stops the same diagnostic firing on a `for-of` that contains no workflow call at all: a loop that only massages locals has no step to lose, so erroring on it was a false positive.
2623
- - 7c8f015: Hard-error on a for-of a DSL workflow can't model, instead of silently dropping it.
2624
-
2625
- A `pikkuWorkflowFunc` (DSL) whose body contained a computed/counting loop — e.g. `for (const i of [...Array(n).keys()])` — used to serialize with **zero steps**: the DSL extractor only models a for-of as a sequential fanout over a data-array identifier/field (`data.items`), so a non-path iterable made `extractSequentialFanout` return null and the extractor dropped the loop _and every `workflow.do` inside it_. The invoked functions then never entered `invokedFunctions`, so they got no `addFunction()` registration and threw `Function not found` at runtime — a silent codegen footgun that bricked every prod sandbox create.
2626
-
2627
- Now the extractor pushes a validation error naming the offending for-of, so `pikkuWorkflowFunc` reports `INVALID_DSL_WORKFLOW` at codegen. A genuine control-flow/counting loop belongs in `pikkuWorkflowComplexFunc`, which falls back to the basic AST walk that _does_ register loop-invoked functions.
2628
-
2629
- - 5962e51: Give the inspector's program the project's tsconfig path mappings.
2630
-
2631
- The inspector builds its own TypeScript program rather than reusing the project's, which is what keeps a cold run affordable — but it hand-rolled its compiler options and so had no `paths`. An import the project resolves only through a `paths` entry therefore did not resolve inside the inspector, and the factory the inspected function was passed to came back as `any`. A function whose input type comes from that factory's contextual type — rather than an explicit generic or a named `input:` schema — then recorded no input at all, silently, with no diagnostic.
2632
-
2633
- `inspect` takes a new `tsconfig` option (the CLI passes `config.tsconfig`, including on a setup-only run, where `schemaConfig` is absent). Only resolution keys are copied across — `baseUrl`, `paths`, `rootDirs`, `pathsBasePath`. Type-inference options such as `strict` deliberately stay the inspector's own, because changing those would change the types it records.
2634
-
2635
- This was latent rather than live: the generic path reads type _nodes_, the schema path reads an identifier, output types come from the handler body, and JSON schemas are generated by a separate tsconfig-aware program — so nothing in the tree reached the contextual-type fallback. Regenerating the e2e project and the `functions` template produces byte-identical output.
2636
-
2637
- - 5962e51: Add `pikkuFeature`, a grouping primitive for scenarios.
2638
-
2639
- A feature groups scenarios the way gherkin's `Feature:` groups `Scenario:`, and gets `Examples:` for free as an ordinary loop:
2640
-
2641
- ```ts
2642
- export const credentialFeature = pikkuFeature({
2643
- name: 'Credential API',
2644
- tags: ['credential'],
2645
- before: startsMockOAuthServer,
2646
- after: stopsMockOAuthServer,
2647
- scenarios: [
2648
- credentialLazyLoadScenario,
2649
- ...['stripe', 'google', 'hmac-key'].map((name) => ({
2650
- scenario: credentialRoundTripScenario,
2651
- data: { name },
2652
- })),
2653
- ],
2654
- })
2655
- ```
2656
-
2657
- - Scenarios are referenced by **imported identifier**, not by string name, so a renamed or deleted scenario is a compile error rather than a silent skip. A `{ scenario, data }` entry's `data` is typed against that scenario's own input.
2658
- - Feature hooks run **once around the whole group** (`before → a → b → c → after`), not per scenario, and `after` runs in a `finally`. Per-scenario setup stays the scenario's own `before`; gherkin's `Background:` is deliberately not expressible.
2659
- - A scenario's effective tags are its own plus its feature's, so `--tags credential` selects through the feature.
2660
- - New `--features` selector on `pikku scenario run`, and `pikku scenario list` now prints features with their scenarios indented. Every filter narrows the same plan, so narrowing a feature to two of its five scenarios still runs its hooks exactly once around those two.
2661
- - The **feature is the run unit**: `--flows` on a scenario whose every feature entry carries `data` errors and names the features containing it, because the feature is what supplies that data. A scenario referenced bare anywhere, or in no feature at all, still runs standalone.
2662
-
2663
- `pikkuFeature` infers its scenario list with a `const` generic, so `CoreFeature['scenarios']` is `readonly` — otherwise the emitted `addFeature(id, feature)` call does not typecheck.
2664
-
2665
- Membership is resolved at runtime by object identity — `pikkuScenario` returns its config verbatim, so a feature holds the very object that was registered. That is what lets the scenario list be built by a loop, which no static analysis could enumerate. It also means a scenario constructed inline inside a feature is never registered, and is reported as unresolved rather than silently running as something else.
2666
-
2667
- - 5962e51: Add `before` / `after` hooks to `pikkuScenario`, and make an unextractable scenario a hard error.
2668
-
2669
- A scenario config now takes `before` and `after`. Both have the same signature as `func` — `(services, data, wire)` — with the return value discarded, so there is no new type to learn and a hook reaches the app the same way the body does, through `wire.actors`:
2670
-
2671
- ```ts
2672
- export const credentialScenario = pikkuScenario({
2673
- title: 'A credential is loaded on first use',
2674
- tags: ['scenario', 'credential'],
2675
- before: resetsCredentials,
2676
- after: removesInstalledAddon,
2677
- func: async (services, data, { scenario, actors }) => { ... },
2678
- })
2679
- ```
2680
-
2681
- - `before` throwing skips the body and fails the run, but `after` still runs.
2682
- - `after` always runs, in a `finally`. Throwing fails a run that would otherwise have passed; on an already-failed run it attaches as the `cause` and never replaces the original error.
2683
- - Neither runs when the run is suspended or waiting — teardown only fires at a terminal outcome.
2684
- - Hooks are not ladder rows: the runner records nothing for them, and a failure is labelled by phase via the new `ScenarioHookError`.
2685
- - Hooks are scenario-only. A `before`/`after` on a `pikkuWorkflowFunc` never runs — a workflow is durable and resumable, so a callback that reran on every replay would have no honest meaning.
2686
-
2687
- Two fixes that scenarios needed to be safe to write:
2688
- - A closure in a complex-workflow or scenario body is no longer held to the DSL statement whitelist. A single `try`/`catch` inside any callback previously failed extraction, and the fallback path understands `do`/`sleep` but not `step`/`given`/`when`/`then` — so the scenario registered with **zero steps** and passed vacuously, with no diagnostic. Plain DSL workflows still descend into callbacks, which is what validates fanout bodies.
2689
- - New `PKU679`: a scenario that fails DSL extraction is now a critical error and refuses to register, instead of silently registering empty. A scenario that declares no input parameter at all is legitimate and still extracts.
2690
-
2691
- - a436645: Redesign the console's scenarios screen as living documentation of a project's BDD features.
2692
-
2693
- The inspector now statically extracts `pikkuFeature` declarations — name, description, tags, the scenarios each one groups (including `{ scenario, data }` examples), and whether it declares `before`/`after` — and the CLI writes them to `<outDir>/scenarios/features.gen.json`, which `MetaService.getFeaturesMeta()` reads and the console addon returns from `getAllMeta`.
2694
-
2695
- The scenarios page reads that back as a document: features on the left, and on the right the selected feature's scenarios, each rendered as the given/when/then ladder of prose its author actually wrote, with repeats shown as `for each x in xs`, `Examples:` tables for parameterised entries, skip reasons stated rather than hidden, and each scenario's cast of personas inline. The Flows/Personas segmented control is gone; tags filter the document the same way `pikku scenario run --tags` filters a run.
2696
-
2697
- - 47478a4: Let a scenario declare why it is held out of a default run.
2698
-
2699
- `pikkuScenario({ skip: 'why' })` keeps the scenario in the plan and reports it as `SKIP <name> (<reason>)` on the ladder, instead of the alternatives available until now: deleting it, commenting it out, or leaving it red. Naming it directly with `--flows` clears the quarantine and runs it; selecting the feature it belongs to does not, because a feature is a group and running the group should not silently drag a quarantined member in.
2700
-
2701
- The run report's `skipped` list now carries a reason per scenario rather than assuming `--no-browser`, so a browser scenario held back on a machine with no browser reads differently from one the project quarantined itself.
2702
-
2703
- `@pikku/console` gains a test id on the addon detail page's Setup tab, which was previously only reachable through its translated label.
2704
-
2705
- - 2434f1b: Fix two silent losses in scenario step extraction.
2706
-
2707
- Destructuring a scenario step result (`const { threadId } = await scenario.given(...)`) dropped the
2708
- step from the graph without a diagnostic — the step stayed fully typed and present in the step map,
2709
- then failed at runtime with `Function not found`. It now reports PKU679, like every other step form
2710
- the DSL cannot model.
2711
-
2712
- A constant referenced as step input (`{ resourceId: RESOURCE_ID }`) was serialized as
2713
- `{ $ref: 'trigger', path: 'RESOURCE_ID' }` — a read of a trigger field that does not exist. A `const`
2714
- with a literal initializer is now inlined as that literal.
2715
-
2716
- - 5962e51: Add `template` to `pikkuScenarioStep`, so a step's reported prose names the values it was called with.
2717
-
2718
- `description` documents what a step does, for the console and for whoever reads the source. `template` is what a reader of the report sees, with `{placeholders}` filled from the input the step was actually called with:
2719
-
2720
- ```ts
2721
- export const seesAddonCard = pikkuScenarioStep<
2722
- { packageName: string; state?: 'installed' | 'available' },
2723
- { visible: true },
2724
- true
2725
- >({
2726
- name: 'seesAddonCard',
2727
- description: 'sees an addon in the gallery',
2728
- template: 'sees {state} addon {packageName}',
2729
- browser: true,
2730
- func: async (_services, { packageName, state }, { browser }) => { … },
2731
- })
2732
- ```
2733
-
2734
- ```
2735
- Then the admin sees at least 10 addons on offer ✓ 3ms
2736
- When the admin searches for stripe ✓ 10ms
2737
- Then the admin sees available addon @pikku/addon-stripe ✓ 77ms
2738
- ```
2739
-
2740
- Previously the only way to get that was a `description` at every call site, which meant writing the sentence once per call rather than once per step — and a call site that forgot it reported the same sentence three times in a row.
2741
- - A placeholder with no recorded value renders as nothing and the surrounding whitespace collapses, so an omitted optional input reads as a shorter sentence rather than leaking a literal `{state}` into the report. Type placeholder values so they read as words (`state?: 'installed' | 'available'`, not `installed?: boolean`).
2742
- - A call-site `description` still wins, the same way it already won over the step's `description`.
2743
- - `renderStepTemplate` is exported from `@pikku/core/workflow` alongside `composeStepProse`, so the CLI reporter and the console render identically.
2744
-
2745
- Scenario steps now record their input on the run (`inlineStep` persisted `null` for every inline step, so there was nothing for a reporter to interpolate). This is what `getRunSteps` already exposes as `data` for RPC steps.
2746
-
2747
- A step called from a loop gets its template too. Its durable name is built at runtime (`sees @pikku/addon-todos`) from a declaration the static meta records verbatim (`sees ${packageName}`), so the two can never match by name — it used to fall back to the bare name, with no keyword, actor or template:
2748
-
2749
- ```
2750
- sees @pikku/addon-console ✓ 85ms
2751
- Then the admin sees installed addon @pikku/addon-console ✓ 92ms
2752
- ```
2753
-
2754
- The join is by **step function**. A scenario step is dispatched by name exactly as an RPC is, so it now records that name in the run's existing `rpcName` slot — no new field, no schema change in any workflow store. Nothing dispatches off that value anywhere; step identity always comes from the code being replayed.
2755
-
2756
- To keep the slot honest, a scenario step is now its own **kind of RPC**, alongside public / private / remote: `FunctionMeta.scenarioStep` marks it, and `rpcExposed` refuses it even if something marks it `expose: true`. Steps were already left out of the RPC registry; this makes "never network-callable" a property the runtime enforces rather than one the registration path happens to produce.
2757
-
2758
- `collectScenarioStepProse` now returns `{ byStepName, byStepFunc }` rather than a bare `Map`, and `buildStepLadder` takes that. The step name still wins; the function index only decides steps recorded under a name no declaration carries, and a function called from several sites that disagree on their prose is left out rather than guessed at.
2759
-
2760
- - 5962e51: Add `pikkuScenarioStep` — named, typed scenario steps whose body is an ordinary pikku function.
2761
-
2762
- A scenario step is referenced by typed string name, the same way `workflow.do` references an RPC, and is checked against a generated `FlattenedScenarioStepMap`:
2763
-
2764
- ```ts
2765
- export const buysAnApple = pikkuScenarioStep({
2766
- name: 'buysAnApple',
2767
- description: 'buys an apple',
2768
- func: async (services, data: { qty: number }) => { ... },
2769
- })
2770
-
2771
- await scenario.given('buys an apple', 'buysAnApple', { qty: 1 }, { actor: actors.shopper })
2772
- // renders: Given the shopper buys an apple
2773
- ```
2774
-
2775
- - `given`/`when`/`then` are sugar over `step`, setting only the prose prefix. The runner renders a step ladder from the recorded run.
2776
- - Steps default to `retries: 0` — a failed assertion is not retried.
2777
- - Steps are deliberately **not** registered as RPCs, so a browser-driving step is never network-callable.
2778
- - `browser: true` steps receive a browser handle on the wire. `@pikku/playwright` is a new package providing the Playwright-backed provider, signing each actor's browser context in through the same actor path the HTTP actors use. Without a provider, `pikku scenario run --no-browser` **skips** browser scenarios instead of failing them.
2779
- - New diagnostics: PKU677 (a `browser: true` step called without an actor) and PKU678 (a step target that is not a static string literal).
2780
- - Fixes `--no-<flag>` boolean negation in the CLI command parser, which previously parsed as an unknown option.
2781
- - Fixes PKU673 (a scenario func destructuring services), which never fired because it ran before function meta existed; it now runs in post-processing.
2782
- - Fixes scenario/workflow steps nested in `for...of` and `Promise.all` being dropped from workflow meta.
2783
-
2784
- - Updated dependencies [539ee0b]
2785
- - Updated dependencies [a1a6816]
2786
- - Updated dependencies [dc3e11e]
2787
- - Updated dependencies [24da616]
2788
- - Updated dependencies [04bfe3f]
2789
- - Updated dependencies [5962e51]
2790
- - Updated dependencies [5962e51]
2791
- - Updated dependencies [cd6453c]
2792
- - Updated dependencies [a436645]
2793
- - Updated dependencies [46cf63e]
2794
- - Updated dependencies [9e666bc]
2795
- - Updated dependencies [1c841d8]
2796
- - Updated dependencies [47478a4]
2797
- - Updated dependencies [9e666bc]
2798
- - Updated dependencies [5962e51]
2799
- - Updated dependencies [5962e51]
2800
- - Updated dependencies [61b9bf8]
2801
- - @pikku/core@0.12.70
2802
-
2803
- ## 0.12.47
2804
-
2805
- ### Patch Changes
2806
-
2807
- - 91077ff: The auth factory's services are read from the auth definition, not from the handler generated out of it.
2808
-
2809
- `pikkuBetterAuth((services) => ...)`'s destructured services are what `authorize` and the session callbacks actually use, and they reach the generated services map by being stamped onto the auth handler's function meta. On the first run in a clean checkout that handler's file has not been written yet — so there is nothing to stamp, nothing to aggregate, and the map comes out marking those services unused. Run it a second time, with the file now on disk, and it says the opposite.
2810
-
2811
- The consequence is not cosmetic: `RequiredSingletonServices` is built from that map, so a clean build types the services as optional and tree-shakes them out of the deployed auth worker — the exact failure the stamping exists to prevent, reintroduced by the one case where it cannot run. A CI checkout is always the first run.
2812
-
2813
- The definition is inspected from hand-written source on every pass, so its services are now folded into the required set directly. A clean build and an incremental one give the same answer.
2814
-
2815
- ## 0.12.46
2816
-
2817
- ### Patch Changes
2818
-
2819
- - a8f9a7d: Distinguish an unresolvable schema type from an unsupported schema library.
2820
-
2821
- Both failures shared one message, so a plain `z.object({...})` whose type TypeScript
2822
- could not resolve — a file outside tsconfig `include`, or a generated file such as
2823
- `.pikku/db/zod.gen.ts` that had not been written yet — was reported as
2824
- "Ensure your schema is imported from a supported validation library". The schema is
2825
- dropped either way (`inputSchemaName: null`, no generated schema file), so the message
2826
- is the only signal that a function has silently lost its input contract, and that
2827
- advice is unactionable when the schema already is zod.
2828
-
2829
- - eaabcbf: fix(inspector): don't reject plain destructuring in DSL workflows
2830
-
2831
- `extractDestructuredDeclaration` reported "Destructuring a step result is not
2832
- supported in DSL workflows" for EVERY destructuring statement whose initializer
2833
- wasn't `await Promise.all([...])` — including ordinary local bindings that
2834
- involve no step at all, such as `const { runId } = input`.
2835
-
2836
- Destructuring the workflow's own input is the single most idiomatic line in a
2837
- DSL workflow, so this rejected working workflows wholesale under a message
2838
- about step results that named nothing the author had written.
2839
-
2840
- The diagnostic now fires only when the destructured initializer really is a
2841
- step (`await workflow.do(...)`, a parallel group, or a parallel fanout).
2842
- Anything else passes through as a non-step, exactly as the identifier path
2843
- already does for `const x = someLocal`.
2844
-
2845
- - Updated dependencies [f11675f]
2846
- - @pikku/core@0.12.68
2847
-
2848
- ## 0.12.45
2849
-
2850
- ### Patch Changes
2851
-
2852
- - ae4f59a: Gate admin capabilities on scopes, and scaffold user management
2853
-
2854
- Admin capabilities were gated on `user.role === 'admin'` — a single text column
2855
- meaning "can do everything". Impersonating a user, rebinding a shared
2856
- credential and reading the user directory are distinct capabilities that one
2857
- user can hold independently, so they are now scopes on an `admin` tree:
2858
-
2859
- | Gate | Scope |
2860
- | -------------------------------------- | ------------------------ |
2861
- | impersonation | `admin:impersonate` |
2862
- | `credentialOAuth`'s `canLinkSingleton` | `admin:credentials:link` |
2863
- | reading the user directory | `admin:users:list` |
2864
- | creating a user out of band | `admin:users:create` |
2865
- | ban / unban | `admin:users:ban` |
2866
- | delete a user | `admin:users:remove` |
2867
- | revoke a user's sessions | `admin:users:sessions` |
2868
- | set a user's password | `admin:users:password` |
2869
-
2870
- Holding the bare `admin` scope satisfies all of them via pikku's existing
2871
- parent-grant rule, so it is a one-for-one replacement for the old role.
2872
-
2873
- better-auth's `admin()` plugin is still what implements ban, delete,
2874
- session-revocation and set-password, so it stays. Its `user.role` column is no
2875
- longer something pikku grants: it is _projected_ from the scope store when a
2876
- session is built, and only from the scopes whose capability better-auth's own
2877
- endpoints gate on the caller's role. Someone granted `admin:users:list` can read
2878
- the directory — which goes straight to the auth adapter — without gaining the
2879
- power to ban, and revoking a scope demotes the role on the next sign-in. Scopes
2880
- remain the single source of truth.
2881
-
2882
- New `scaffold.userAdmin` in `pikku.config.json` generates the whole set —
2883
- `pikkuAdminListUsers`, `pikkuAdminCreateUser`, `pikkuAdminSetUserBanned`,
2884
- `pikkuAdminRemoveUser`, `pikkuAdminRevokeUserSessions` and
2885
- `pikkuAdminSetUserPassword` — into your project. Listing or banning a user is
2886
- ordinary application behaviour and must not require installing the console.
2887
- Codegen fails with an actionable error if better-auth is wired without
2888
- `admin()`. The console's Users page calls these same functions, showing each
2889
- action only where the caller holds its scope.
2890
-
2891
- Every scaffold now emits a directory named for its domain — `scaffold/admin/`,
2892
- `scaffold/rpc/`, `scaffold/agent/`, `scaffold/auth/`, `scaffold/console/`,
2893
- `scaffold/graph/`, `scaffold/realtime/`, `scaffold/scenarios/`,
2894
- `scaffold/webhook/`, `scaffold/workflow/` — holding its wiring file beside a
2895
- `*.schemas.gen.ts` sibling, and every generated payload is a zod schema instead
2896
- of a TypeScript generic. The schemas have to stand alone: the inspector reads a
2897
- zod schema by importing the module that declares it, which it cannot do for a
2898
- wiring file whose relative pikku-types import per-unit deploy codegen rewrites.
2899
-
2900
- Resolving a schema by reference rather than by name also fixes the agent HTTP
2901
- surface. `agentCaller` and `agentStreamCaller` take the same payload but had to
2902
- repeat the type literal verbatim in each generic position, because the extractor
2903
- synthesised the schema name from the _function_ name and so recorded an
2904
- `inputSchemaName` with no schema behind it whenever the two shared a named
2905
- alias — every agent call through that alias failed with `MissingSchemaError`.
2906
- One `AgentCall` schema now backs both.
2907
-
2908
- Where a payload's shape belongs to `@pikku/core` (`WorkflowRunStatus`,
2909
- `FunctionCoverageReport`, `StubCall[]`) the generated function carries no
2910
- `output` schema and the inspector infers it from the handler's return type;
2911
- re-declaring a core type in zod would be a second definition free to drift.
2912
-
2913
- Upgrading rewrites the layout in place: codegen prunes the pre-directory copy of
2914
- each scaffold file before it inspects the source tree, since the old flat file
2915
- still wires the same routes and leaving it behind would wire everything twice.
2916
-
2917
- `@pikku/core` gains `hasScopes(required, held)`, the non-throwing counterpart to
2918
- `verifyScopes`, and declares `auth` on `CoreSingletonServices` — the auth
2919
- instance the generated `pikkuServices` wrapper already injected but never typed.
2920
- A scope root declared twice (an addon and its host both contributing the same
2921
- `admin` tree) now flattens to one entry per id instead of emitting it twice.
2922
-
2923
- BREAKING: there is no role fallback for the scope-gated capabilities. An app
2924
- that relied on the old default must register a `ScopeService` and grant `admin`
2925
- (or a narrower `admin:*` scope). Every gate fails closed and warns when no
2926
- `ScopeService` is registered. `delegatedAuth`'s `defaultRole`/`mapRole` now
2927
- grant a pikku role through the `ScopeService` instead of writing better-auth's
2928
- `role` column, and the `credentialOAuth` platform user no longer sets `banned`.
2929
-
2930
- BREAKING: the console reads its user directory over the scaffolded
2931
- `pikkuAdminListUsers` RPC (gated on `admin:users:list`, backed by better-auth's
2932
- `$context.adapter`) instead of `client.admin.listUsers`, and
2933
- `UsersTableUser`/`UsersTableLabels` no longer carry `role` — there is no role
2934
- column to render. `@pikku/addon-console` no longer ships a `console:listUsers`
2935
- function: user management is not the console's job, so a host that wants the
2936
- Users page must enable `scaffold.userAdmin`.
2937
-
2938
- - Updated dependencies [ae4f59a]
2939
- - @pikku/core@0.12.67
2940
-
2941
- ## 0.12.44
2942
-
2943
- ### Patch Changes
2944
-
2945
- - 1a86d3f: Stop silently dropping DSL workflow constructs during extraction.
2946
-
2947
- Four constructs produced a wrong workflow graph with no diagnostic:
2948
- - A property access on the fanout item (`users.map((u) => workflow.do(..., { userId: u.id }))`)
2949
- was dropped from the step's inputs — `extractInputSource` resolved property
2950
- access against the input param and output variables but not loop variables.
2951
- - `const [org, user] = await Promise.all([...])` dropped **both** steps, because
2952
- extraction bailed on any non-identifier binding name before reaching the
2953
- `Promise.all` branch. Array destructuring now binds each name to its matching
2954
- child step's output.
2955
- - A brace-less `for (const x of xs) await workflow.do(...)` dropped the entire
2956
- loop, since only block bodies were walked.
2957
- - Object destructuring of a step result and multi-declarator statements now
2958
- report a diagnostic instead of vanishing.
2959
-
2960
- - 1a86d3f: Fix a fanout collapsing into a single step, and preserve graph node config.
2961
- - A fanout took its `stepName` from the first step of its body. Node ids _are_
2962
- step names, so the loop and that step got the same id and the step overwrote
2963
- the loop: `await Promise.all(users.map(...))` rendered as one plain call, and
2964
- everything after the loop became unreachable. A fanout is not itself a cached
2965
- step, so it no longer borrows a name.
2966
- - A `workflow.sleep` or `workflow.suspend` inside a fanout body was dropped at
2967
- extraction — `FanoutStepMeta.body` was typed RPC-only. It now admits sleep and
2968
- suspend, and the regenerated body emits them.
2969
- - Regenerating a `pikkuWorkflowGraph` dropped `onError`, `retries` and
2970
- `retryDelay` from every node, and graph-level `notes`. All four are honoured
2971
- at runtime, so the round trip silently changed behaviour.
2972
-
2973
- - 1a86d3f: Support multi-step fanout bodies in DSL workflows.
2974
-
2975
- A `Promise.all(array.map(...))` (or `for...of`) body containing more than one
2976
- `workflow.do` call previously extracted only a single step: `const`-captured
2977
- steps were skipped entirely by the parallel extractor, so a body like
2978
-
2979
- ```ts
2980
- await Promise.all(
2981
- users.map(async (u) => {
2982
- const digestData = await workflow.do('Get pipeline', 'getDigestData', {
2983
- userId: u.id,
2984
- })
2985
- await workflow.do('Send digest', 'sendDigestEmail', { ...digestData })
2986
- })
2987
- )
2988
- ```
2989
-
2990
- produced a graph with `getDigestData` missing and `sendDigestEmail` referencing
2991
- an unregistered variable. `FanoutStepMeta.child` is replaced by
2992
- `FanoutStepMeta.body: RpcStepMeta[]`, holding the per-iteration steps inline in
2993
- the same workflow — no sub-workflow boundary. Per-iteration `const` bindings are
2994
- now registered so later steps in the same iteration can reference them, and the
2995
- sequential path no longer hard-errors on bodies with more than one step.
2996
-
2997
- - 1a86d3f: Add `onError` compensation to DSL workflows.
2998
-
2999
- A DSL workflow had no way to express error handling at all — `try/catch` is not
3000
- an allowed statement, and step options carried only `retries`/`retryDelay`. A
3001
- step can now name a compensation RPC:
3002
-
3003
- ```ts
3004
- await workflow.do(
3005
- 'Charge',
3006
- 'chargeCard',
3007
- { id },
3008
- {
3009
- retries: 3,
3010
- onError: 'refundOrder',
3011
- }
3012
- )
3013
- ```
3014
-
3015
- Semantics mirror a graph node's `onError` exactly: once the step's retries are
3016
- exhausted the handler is invoked with `{ error: { message } }` and the original
3017
- error is still thrown. This is compensation, not recovery — the workflow fails
3018
- either way. The handler runs as its own durable step, so a replay cannot
3019
- compensate twice, and it does not inherit `onError` itself.
3020
-
3021
- The handler is materialised as a real graph node, so it is wired like any other
3022
- RPC and the console draws a dashed red "on error" edge to it rather than the
3023
- route being invisible.
3024
-
3025
- - 1a86d3f: Keep a numeric `retryDelay` numeric through the graph round-trip. The serialized
3026
- graph typed it as `string` and the DSL→graph conversion called `.toString()`, so
3027
- `retryDelay: 500` regenerated as `retryDelay: '500'` — a different value to the
3028
- runtime, which parses strings as durations.
3029
- - 1a86d3f: Stop silently dropping switch cases and spread returns from workflow graphs.
3030
- - A fall-through case (`case 'a': case 'b': ...`) recorded only the last value.
3031
- A run entering on `'a'` therefore appeared to match no case at all. Empty
3032
- clauses now carry through to the entry they fall into — the next non-empty
3033
- case, otherwise `default`, otherwise the switch exit.
3034
- - `return { ...r, extra: 1 }` produced a return node listing only `extra`, so
3035
- the graph claimed an output shape the workflow does not have, with no
3036
- diagnostic. `return r` produced no return node at all. `ReturnStepMeta` now
3037
- records a `spread` list, and the regenerated code emits it.
3038
-
3039
- - 1a86d3f: Stop deleting code when regenerating a DSL workflow from its graph.
3040
-
3041
- Regenerating a workflow (as the console's graph editor does) silently dropped
3042
- steps:
3043
- - Only the **first** step of an `if` arm survived. The walk stopped via a
3044
- heuristic that tested whether the next node id contained `_then_`/`_else_`,
3045
- but node ids are step names, so it never matched. Branch and switch bodies now
3046
- walk the `next` chain up to an explicit exit boundary — the enclosing flow
3047
- node's own `next`.
3048
- - Switch cases emitted only their entry node, never walking `next` at all.
3049
- - Every step was renamed to `Call <rpcName>`, because the graph conversion never
3050
- wrote `stepName` onto the node. Step names are the durable replay cache key,
3051
- so a round-trip silently invalidated in-flight runs.
3052
- - `workflow.suspend()` had no graph node, yet the preceding step's `next` still
3053
- pointed at its id — traversal dead-ended there and every following step was
3054
- deleted. `suspend` is now a real flow node, and both `suspend` and `approval`
3055
- have deserializer cases.
3056
- - Numeric and boolean `switch` case values were emitted quoted (`case '1':`),
3057
- so the case could never match. Step names and reasons containing a quote are
3058
- now escaped.
3059
-
3060
- Also fixed in the same pass:
3061
- - `const [org, user] = await Promise.all([...])` regenerated as a bare
3062
- `await Promise.all([...])`, leaving both names unbound.
3063
- - A step result assigned inside a branch was re-declared with `const` inside
3064
- that branch, so any later reference was out of scope. Hoisting analysis was
3065
- keyed off the same dead node-id heuristic and never fired.
3066
- - A top-level step whose _name_ contained `_case`, `_item_`, `_then_`,
3067
- `_else_`, `_child_` or `_default_` was silently deleted, because node ids are
3068
- step names and were matched against those structural substrings. Ownership is
3069
- now read from the parent constructs themselves.
3070
-
3071
- - 1a86d3f: Stop corrupting values when regenerating a workflow from its graph.
3072
- - A numeric `workflow.sleep('Wait', 5000)` came back as `'5000'`, and a numeric
3073
- `retryDelay` likewise. Durations are `string | number`; only strings are
3074
- quoted now.
3075
- - An assignment to a context variable was stored as an opaque `value`, so
3076
- `count = count + 1` regenerated as `count = 'count + 1'` — an expression
3077
- turned into a string literal. `SetStepMeta` now carries a separate
3078
- `expression` field (mirroring `SwitchCaseMeta`), so a string literal and a
3079
- code expression are no longer indistinguishable in the meta.
3080
- - A `next` that was not a single node id was coerced with a string cast: an
3081
- array became the bogus id `'a,b'` and a branch-key record became
3082
- `'[object Object]'`, severing every downstream node. Arrays, key-based
3083
- routing tables and condition lists now each render in their own shape.
3084
- - A `filter`/`some`/`every` node with no `outputVar` emitted
3085
- `const undefined = ...`, which does not parse.
3086
-
3087
- - 1a86d3f: Keep a `workflow.sleep` whose duration is only known at runtime (a loop
3088
- variable, a field off the input). The closure evaluates it, so it is legal DSL;
3089
- its source text is recorded as an `expression` and emitted raw when regenerating
3090
- code, as a set step already does.
3091
- - 314ace3: Cut the schema generator's ts.Program cost — the dominant contributor to `pikku all` memory and time.
3092
-
3093
- Two independent fixes: the program is now scoped to the virtual file's import
3094
- closure (870 source files instead of the whole tsconfig's 2572), and it is
3095
- released once schemas are generated instead of being pinned at module scope for
3096
- the life of the process. On a 279-function tree this cuts cold codegen ~20% and
3097
- in-pass live heap ~21%, with byte-identical schema output.
3098
-
3099
- - 3d76f51: Add an optional `docsUrl` to `wireSecret`, `wireVariable`, and `wireCredential`, so a console or deploy UI reporting a missing value can link the user to where they obtain it instead of showing a bare identifier.
3100
- - Updated dependencies [1a86d3f]
3101
- - Updated dependencies [1a86d3f]
3102
- - Updated dependencies [1a86d3f]
3103
- - Updated dependencies [1a86d3f]
3104
- - Updated dependencies [1a86d3f]
3105
- - Updated dependencies [1a86d3f]
3106
- - Updated dependencies [3d76f51]
3107
- - @pikku/core@0.12.65
3108
-
3109
- ## 0.12.43
3110
-
3111
- ### Patch Changes
3112
-
3113
- - c478794: Simplify authorization to be session + function based (#972). Permissions are now function-scoped only: global permissions AND together, a function's own permissions OR together, and the two are independent gates that both must pass — a broad global can no longer satisfy an admin-only function. Removed wire-, tag-, and HTTP-route-level permissions (`addTagPermission`, `addHTTPPermission`, wire-level `permissions` on HTTP/channel/MCP wirings). Tags are now organizational only. `auth` (session presence) and tag/HTTP middleware are unchanged.
3114
- - b714fd4: Merge an addon's credential meta into the consuming app during inspection, the
3115
- same way addon secrets and variables are already merged. Without this, a
3116
- credential declared by an addon (e.g. an OAuth2 integration) never reached the
3117
- app's `CREDENTIAL_OAUTH2_CONFIGS`, so the `credential-oauth` provider and the
3118
- credential service could not resolve it — the addon's Connect flow and status
3119
- silently no-op'd. Addon credentials are added as fallbacks: an app-declared
3120
- credential of the same name still wins.
3121
- - cb079cc: `pikkuAIAgent` gains a `workflows: []` capability: a referenced workflow is exposed to the LLM as a tool that runs inline and returns its output.
3122
- - 13474a6: Generate a `ScopeId` union from `wireScope` declarations.
3123
-
3124
- `pikku all` now emits `.pikku/scopes/pikku-scopes.gen.ts` with a `ScopeId` union
3125
- of every declared scope, plus a wildcard form for each node that has
3126
- descendants. A project's generated `pikkuFunc` narrows `scopes` to that union,
3127
- so an undeclared scope is a compile error with editor autocomplete:
3128
-
3129
- ```ts
3130
- wireScope({ admin: { scopes: { invoices: { scopes: { create: {} } } } } })
3131
-
3132
- pikkuFunc({
3133
- scopes: ['admin:invoices:create'], // ✓ autocompleted
3134
- func: ...,
3135
- })
3136
-
3137
- pikkuFunc({
3138
- scopes: ['admin:invoice:create'], // ✗ compile error (typo)
3139
- func: ...,
3140
- })
3141
- ```
3142
-
3143
- The inspector independently rejects undeclared scopes, so a cast that defeats
3144
- the compiler is still caught at build time.
3145
-
3146
- Also fixes `getArrayPropertyValue` dropping any array behind a cast — idiomatic
3147
- `tags: ['a'] as const` was previously invisible to the inspector and silently
3148
- omitted from meta.
3149
-
3150
- - ca0d14f: Apply `credentialOverrides` when merging an addon's credentials into the consuming app, mirroring the existing `secretOverrides`/`variableOverrides` handling. Previously the credentials merge ignored overrides and always registered the addon's logical credential name, so a second instance of the same package (`wireAddon` with a `credentialOverrides` map) failed `validateCredentialOverrides` and both instances shared one OAuth provider. Now each override's resolved name is provisioned as its own credential — and since the credential name doubles as the better-auth providerId, two instances surface two distinct providers instead of a shared account pool.
3151
- - 13474a6: feat: propagate an addon's declared scopes to the host
3152
-
3153
- An addon can now declare scopes with `wireScope`, and a host that wires it picks
3154
- them up: they merge into the host's `ScopeId` union and its declared set, so a
3155
- host function can require an addon scope and the `pikku_scopes` foreign key
3156
- accepts granting one. This mirrors how addon secrets and variables are loaded.
3157
-
3158
- The generated `pikku-scopes.gen.ts` now imports its metadata sidecar and derives
3159
- `SCOPES` from it, rather than inlining the list. TypeScript only emits a `.json`
3160
- into the build output when something imports it, and an addon publishes only
3161
- that output — without the import, an addon's scopes never reached a host.
3162
-
3163
- - cb079cc: `pikkuWorkflowGraph` nodes accept an optional `notes?: string` and the graph an optional `notes?: string[]`; notes are documentation only and excluded from `graphHash`.
3164
- - cb079cc: Fix two corpus type-check failures: n8n `graph:sort`/`graph:summarize` enum rows now emit `as const`, and the inspector's `sanitizeTypeName` prefixes an underscore when a name starts with a digit.
3165
- - 8601505: Make `wireCredential` the single source of truth for an addon's OAuth2 config: `pikku-credentials.gen.ts` exports `CREDENTIAL_OAUTH2_CONFIGS`, generated services import from it, the OpenAPI importer emits a `wireCredential`, and the inspector now extracts `oauth2.additionalParams`.
3166
- - 70fa400: Add outgoing webhooks — `webhookService.send()` enqueues signed deliveries onto a retrying queue, `@pikku/kysely`'s `KyselyWebhookService` persists per-attempt delivery history, and `@pikku/console` gains a read-only `/webhooks` page; also caches resolved secrets in `TypedSecretService` and registers inline-`func` metadata for queue/scheduler/trigger/gateway wirings.
3167
- - 3c75366: Key `secretOverrides`/`variableOverrides` on the secretId/variableId (the string the addon actually reads by — its typed map is keyed by id, e.g. `getSecret('MAILGUN_CREDENTIALS')`), not the logical meta name. The runtime aliaser already keys on the id, but the inspector merge + validation keyed on the logical name, so a correctly-keyed override failed validation and never provisioned its target whenever an addon's logical name differed from its id (the common case — `mailgun`/`MAILGUN_CREDENTIALS`). The existing test masked it by using a secret whose name equalled its id. The merge now resolves and provisions by id (with a name-fallback for older meta), validation checks ids, and the console install codegen generates overrides keyed by id.
3168
- - 7b2ea23: `wireAddon` can install one addon package as multiple named instances, each with its own per-instance singleton services and `secretOverrides`/`variableOverrides`/`credentialOverrides` that alias logical names to real project secrets/variables/credentials.
3169
- - 13474a6: Extract `wireScope` declarations and validate scope references.
3170
-
3171
- Functions referencing a scope that no `wireScope` declares now fail the build
3172
- with an `INVALID_VALUE` critical listing the available scopes, so a typo like
3173
- `admin:invoice:create` is caught at codegen rather than at runtime.
3174
-
3175
- `wireScope` declarations wrapped in a cast (`as const`, `as any`) are unwrapped
3176
- before extraction rather than being silently skipped.
3177
-
3178
- - d2a6eea: Add `wireRemoteAddon` — consume a hosted addon's `remote: true` RPCs transparently over HTTP, with the addon installed as a devDependency (types only). `rpc('ns:fn', input)` dispatches to the host's `/remote/rpc/:rpcName`, authenticating as a client with a token bound from a local source (`{ credentialId }` per-user, `{ secretId }` platform, or a custom `resolve()`), or omitted for a public surface. This is any-machine → hosted-library client auth, distinct from the trusted mesh (`PIKKU_REMOTE_SECRET`). A new `.remote.gen.d.ts` RPC map exposes only the `remote: true` surface to consumers. `pikku` verify errors if a `wireRemoteAddon` package is a production dependency (or missing) instead of a devDependency, and if a bound `credentialId`/`secretId` isn't wired.
3179
- - 30e62ee: Add `workflow.approval(reason, { schema, expiry })` — a return-valued, expiring human-in-the-loop gate that stays closed until a decision is recorded (via `workflowService.approveStep` or `POST /workflow/:workflowName/approve/:runId`), unlike the one-shot `workflow.suspend()`.
3180
- - Updated dependencies [7ab5287]
3181
- - Updated dependencies [e86bc17]
3182
- - Updated dependencies [a9b96a0]
3183
- - Updated dependencies [3f7fc54]
3184
- - Updated dependencies [c478794]
3185
- - Updated dependencies [3f04ae4]
3186
- - Updated dependencies [90d9f04]
3187
- - Updated dependencies [cb079cc]
3188
- - Updated dependencies [cb079cc]
3189
- - Updated dependencies [0a7db82]
3190
- - Updated dependencies [981c4db]
3191
- - Updated dependencies [13474a6]
3192
- - Updated dependencies [5a2b0d5]
3193
- - Updated dependencies [13474a6]
3194
- - Updated dependencies [ee040dc]
3195
- - Updated dependencies [cb079cc]
3196
- - Updated dependencies [13474a6]
3197
- - Updated dependencies [9f0d0eb]
3198
- - Updated dependencies [13474a6]
3199
- - Updated dependencies [70fa400]
3200
- - Updated dependencies [7b2ea23]
3201
- - Updated dependencies [1dc77d5]
3202
- - Updated dependencies [416606c]
3203
- - Updated dependencies [d2a6eea]
3204
- - Updated dependencies [30e62ee]
3205
- - @pikku/core@0.12.64
3206
-
3207
- ## 0.12.42
3208
-
3209
- ### Patch Changes
3210
-
3211
- - 854c342: Fix workspace addon integration: exclude nested pikku projects from inspection (prevents "More than one CoreUserSession/CoreConfig found" when a workspace addon is linked), widen the generated addon service `call()` data param to `unknown` so schema-less function inputs compile, and add `@pikku/inspector` + `@standard-schema/spec` to the generated addon devDependencies so its `.pikku` gen files typecheck.
3212
-
3213
- ## 0.12.41
3214
-
3215
- ### Patch Changes
3216
-
3217
- - bb65430: Fail codegen with a clear error when the installed `@pikku/core` violates the CLI's peer range (PKU718).
3218
-
3219
- Some package managers (bun, yarn) install straight past an unsatisfied `peerDependencies` range instead of failing, so `@pikku/cli` could end up next to a `@pikku/core` outside the range it declares — and the only symptom was a cryptic missing-export crash deep in codegen or at runtime (e.g. `The requested module '@pikku/core/dev' does not provide an export named 'reloadGeneratedMeta'`).
3220
-
3221
- The existing preflight that catches a _split_ core (two installed versions, `PKU717`) now also validates the _single_ installed core's version against the CLI's own `@pikku/core` peer range, and fails with the exact versions and the fix (`@pikku/cli` and `@pikku/core` move together — bump both to the same release, update any overrides/resolutions pins, reinstall). Set `PIKKU_ALLOW_CORE_SKEW=1` to downgrade the failure to a warning if you have verified the installed pair is compatible, mirroring `PIKKU_ALLOW_DUPLICATE_CORE`.
3222
-
3223
- - 982d3f5: Webhook gateway routes are now fully compiled instead of runtime-registered. The inspector projects `wireGateway` into the generated HTTP and function meta (deterministic `gateway__<name>__post`/`__verify` ids), and the gateway runner no longer mutates meta state at runtime — it only registers the handler implementations at module load, like every other wire. Previously the runtime-only meta was invisible to codegen and the dev-server meta reload wiped it, 500ing every gateway request.
3224
-
3225
- Also fixes the GET verification echo: string challenges return as a raw body (platforms compare byte-for-byte; the old JSON quoting failed Meta's check), object responses stay JSON, and failed verification now throws `UnauthorizedError` (401) instead of returning 200 with an error body.
3226
-
3227
- - Updated dependencies [982d3f5]
3228
- - @pikku/core@0.12.61
3229
-
3230
- ## 0.12.40
3231
-
3232
- ### Patch Changes
3233
-
3234
- - 1f3f510: Warn when a Pikku function body performs a runtime dynamic `import(...)`.
3235
-
3236
- The inspector now flags any `pikkuFunc`/`pikkuSessionlessFunc` (and friends) whose handler body contains a dynamic `import(...)` call — including nested callbacks — with the new `PKU498` diagnostic. Function bodies run on every invocation, so a dynamic import there adds per-call latency and defeats bundling/tree-shaking; the import belongs at the top of the module or in your services/`wireServices` setup instead.
3237
-
3238
- Type-only positions like `import('x').Foo` are not flagged. The rule defaults to `warn` — a printed yellow warning that does not fail the build — and is configurable via `lint.functionDynamicImport` in `pikku.config.json` (`'off'` to silence, `'error'` to make it a hard build failure), matching the existing `servicesNotDestructured`/`wiresNotDestructured` lints.
3239
-
3240
- - Updated dependencies [1f3f510]
3241
- - @pikku/core@0.12.59
3242
-
3243
- ## 0.12.39
3244
-
3245
- ### Patch Changes
3246
-
3247
- - 4f92e6f: `pikku db` schema-codegen warnings are now coded diagnostics routed through the CLI logger instead of raw `console.warn`, so they participate in the existing `--fail-on-warn` gate.
3248
-
3249
- Each warning now carries a PKU code and `warn` severity: `PKU481` (JSON/JSONB column with no concrete `tsType`, degrading to `unknown`), `PKU480` (column named like a date/bool but whose DB type contradicts it), and `PKU482` (a `format` annotation ignored on a non-string column). Running `pikku db migrate --fail-on-warn` (e.g. in CI) now turns these into a hard failure, forcing the `db/annotations.ts` entry — closing the loophole where an untyped jsonb column silently degrades type-safety. Default behaviour is unchanged: the warnings still print, and only fail the build when `--fail-on-warn` is set.
3250
-
3251
- - daec082: Drop Node 22 support — the minimum supported runtime is now Node 24 (LTS).
3252
-
3253
- Node 22 deadlocks `pikku dev` at `loadUserBootstrap` (tsx `register()` + `require(esm)` cycle handling on node 22.12+), and Node 20 is already below our floor. The `engines.node` requirement is raised to `>=24` across all packages, matching `.nvmrc` and the CI test matrix. Closes #751.
3254
-
3255
- - ad26273: Remove 16 dormant `ErrorCode` enum entries that were defined but never emitted anywhere in the framework. These were placeholder registrations that were never wired to a diagnostic, or codes whose emission sites were removed in later refactors (e.g. `PKU901`, `PKU431`). A whole-repo audit found zero emission sites — no user could ever see them — so they only cluttered the registry and demanded docs pages for errors that cannot occur.
3256
-
3257
- Removed: `PKU300`, `PKU426`, `PKU427`, `PKU431`, `PKU488`, `PKU529`, `PKU568`, `PKU685`, `PKU715`, `PKU736`, `PKU787`, `PKU835`, `PKU836`, `PKU901`, `PKU937`, `PKU975`.
3258
-
3259
- A new guard test (`error-codes-emitted.test.ts`) fails if any `ErrorCode` value has no `ErrorCode.<NAME>` or raw `PKU###` reference in the source, so dead entries can't silently accumulate again.
3260
-
3261
- - Updated dependencies [7b17b14]
3262
- - Updated dependencies [daec082]
3263
- - Updated dependencies [e0fd352]
3264
- - @pikku/core@0.12.58
3265
-
3266
- ## 0.12.38
3267
-
3268
- ### Patch Changes
3269
-
3270
- - 66f3dae: Move `@pikku/core` from `dependencies` to `peerDependencies` in the last packages that still declared it as a regular dependency.
3271
-
3272
- `@pikku/core` holds a single `pikkuState` registry and must resolve to exactly one copy at runtime — every wiring (workflows, RPCs, queue workers, middleware) registers into the copy it imports, and the runner reads the copy it imports. 35 packages already declare core as a peer for this reason; these six were the stragglers. Because they carried a regular `@pikku/core` dependency, bumping any one of them could leave a second, older core locked in a consumer's tree, splitting the registry so wirings silently fail to resolve (surfaced as `[PKU717] Multiple @pikku/core versions installed`).
3273
-
3274
- Making core a peer everywhere means the consuming app provides the one copy (the react/react-dom singleton pattern), so duplication is structurally impossible. `@pikku/core` is also kept as a devDependency in each package so it still builds/typechecks standalone.
3275
-
3276
- Backward-compatible for consumers that already list `@pikku/core` directly (every template does). A consumer that only pulled core transitively now gets a loud install-time peer warning instead of a silent runtime split — strictly better.
3277
-
3278
- - Updated dependencies [ded4f90]
3279
- - @pikku/core@0.12.54
3280
-
3281
- ## 0.12.37
3282
-
3283
- ### Patch Changes
3284
-
3285
- - efb0406: Add in-process V8 precise coverage (`pikku dev --coverage` / `pikku serve --coverage`) with per-scenario attribution.
3286
- - `@pikku/core`: new `V8CoverageService` (node:inspector precise coverage with snapshot + reset), exposed as the optional `coverageService` singleton service.
3287
- - `@pikku/inspector`: function meta now records `bodyStart`/`bodyEnd` body spans (verbose meta only) so coverage can be mapped without a runtime TypeScript dependency.
3288
- - `@pikku/cli`: `--coverage` on `pikku dev` and `pikku serve` starts the collector in-process; `pikku scenario run --coverage` resets/snapshots the server between flows and writes `.pikku/coverage/scenario-coverage.json` with per-scenario function coverage.
3289
- - `@pikku/addon-console`: new exposed `takeLiveCoverage` / `resetLiveCoverage` RPCs; V8 ranges are mapped through inline source maps to original TypeScript lines (offset-based, so esbuild/tsx single-line output keeps full resolution).
3290
-
3291
- - Updated dependencies [efb0406]
3292
- - Updated dependencies [fe4f5ca]
3293
- - @pikku/core@0.12.53
3294
-
3295
- ## 0.12.36
3296
-
3297
- ### Patch Changes
3298
-
3299
- - 61c9ce9: Add `actor.converse(...)` — actor agents for user journeys (#850)
3300
-
3301
- An actor can now hold a dynamic, LLM-driven conversation with a target Pikku AI
3302
- agent in its own persona:
3303
-
3304
- ```ts
3305
- const verdict = await actors.pm.converse({
3306
- agent: 'todoBot',
3307
- task: 'Get a todo created for the launch',
3308
- evaluate: 'A todo about the launch now exists',
3309
- })
3310
- // verdict: { passed, reasoning, transcript }
3311
- // then assert deterministically as the same actor:
3312
- const todos = await actors.pm.invoke('listTodos', {})
3313
- ```
3314
-
3315
- The actor drives the target over the real transport (the agent's own
3316
- `agentRun` / `agentApprove` HTTP routes, signed in as the actor), plays the
3317
- persona from its `pikku.config.json` config, answers the agent's tool-approval
3318
- requests in-persona (`approvals: 'in-persona' | 'always' | 'never'`), and
3319
- returns its verdict on whether the task was met. Deterministic checks stay the
3320
- caller's job — they already hold the actor.
3321
-
3322
- The conversation engine is transport-agnostic (persona LLM + injected target
3323
- driver); the persona's own turns run in-process via the configured
3324
- `aiAgentRunner` (`model` from the call or the actors-service default).
3325
-
3326
- `agent` is typed against the generated agent-name union (`keyof AgentMap`), so
3327
- it's author-time checked and autocompleted in a typed project.
3328
-
3329
- - 472a349: Rename the userflow concept to scenario (#862). `pikkuUserFlow` becomes `pikkuScenario`, `pikku userflow run/list` becomes `pikku scenario run/list`, the workflow meta flag `userFlow` becomes `scenario`, actor types are now `ScenarioActor`/`ScenarioActors`/`ScenarioActorConfig` (`createHttpScenarioActors`), pikku.config.json's `userFlows` key becomes `scenarios`, the generated actors file is `pikku-scenario-actors.gen.ts` (`createScenarioActors`), the actor sign-in secret env var is `SCENARIO_ACTOR_SECRET`, and the console's User Flows view is now Scenarios.
3330
- - Updated dependencies [61c9ce9]
3331
- - Updated dependencies [f1f39f8]
3332
- - Updated dependencies [c45e98d]
3333
- - Updated dependencies [472a349]
3334
- - @pikku/core@0.12.52
3335
-
3336
- ## 0.12.35
3337
-
3338
- ### Patch Changes
3339
-
3340
- - 7ebea62: Tree-shake addon registrations in filtered inspector states (per-unit deploy codegen).
3341
- - `filterInspectorState` drops an addon's `wireAddonDeclarations`/`usedAddons` unless something kept actually references it (kept wiring targeting `namespace:*`, kept agent/MCP tool, or a body-level `rpc.invoke('namespace:*')` from a file that still contains a kept function). The generated per-unit bootstrap no longer imports unused addon package bootstraps — previously every deploy unit registered every addon's entire function surface, which pulled dev-only code (e.g. `@pikku/addon-console`'s static `node:fs` imports) into Cloudflare Worker bundles and failed upload with `No such module "node:fs"`.
3342
- - Body-level `rpc.invoke()` targets are now tracked per source file (`rpc.invokedFunctionsByFile`) so wiring-level `ref()` targets no longer pin an addon into every unit.
3343
- - `aggregateRequiredServices` computes addon parent services per used addon function (from the addon's shipped per-function `services` meta) instead of blanket-adding `addonRequiredParentServices` — and matches namespaced ids only, so bare project function names colliding with addon function names no longer force the blanket.
3344
- - Addon builds keep per-function `services` in the shipped `pikku-functions-meta.gen.json` so parent projects can do the above; addons built before this fall back to the blanket.
3345
- - HTTP route meta records `refTarget` for `ref('namespace:fn')`-wired routes, so per-unit filtering keeps the addon registration (and only that function's services) when the route deploys.
3346
-
3347
- - Updated dependencies [7ebea62]
3348
- - Updated dependencies [e57dd65]
3349
- - @pikku/core@0.12.51
3350
-
3351
- ## 0.12.34
3352
-
3353
- ### Patch Changes
3354
-
3355
- - 92bd643: User flows in the console: workflow graph extraction now captures
3356
- `workflow.expectEventually` steps and per-step actor names (`{ actor:
3357
- actors.x }`), workflow meta carries `actors`/`title` into the serialized
3358
- graph, the CLI emits `user-flow-actors.gen.json` for the new
3359
- `MetaService.getUserFlowActorsMeta()`, and the console Workflows page gains a
3360
- Workflows / User Flows / Personas toggle. Also fixes complex-workflow graphs
3361
- being clobbered by a duplicate basic-extraction pass after successful DSL
3362
- extraction.
3363
- - Updated dependencies [35a9bab]
3364
- - Updated dependencies [92bd643]
3365
- - @pikku/core@0.12.50
3366
-
3367
- ## 0.12.33
3368
-
3369
- ### Patch Changes
3370
-
3371
- - 4c17f7e: user flows: actors move onto the workflow wire + `pikku userflow` command
3372
- - Actors are no longer a singleton service: `startWorkflow(..., { actors })`
3373
- registers them per run and they arrive on the wire —
3374
- `func: async ({ logger }, input, { workflow, actors })`.
3375
- - Inspector enforces user flows are pure remote stories (PKU673): a
3376
- pikkuUserFlow func may only destructure `logger`/`config` from services.
3377
- - New `pikku userflow run <environment> [--flows a,b] [--tags x,y]` runs flows
3378
- against `userFlows.environments` from pikku.config.json (secret from
3379
- USER_FLOW_ACTOR_SECRET env), refusing internal (non-actor) steps so runs
3380
- against staging/production never touch local services; non-zero exit on
3381
- failure. `pikku userflow list` prints names, descriptions and tags.
3382
- - Workflow meta now carries `title` (parity with HTTP routes/functions).
3383
-
3384
- - Updated dependencies [4c17f7e]
3385
- - @pikku/core@0.12.49
3386
-
3387
- ## 0.12.32
3388
-
3389
- ### Patch Changes
3390
-
3391
- - 8dfddc3: pikkuUserFlow: user flows as workflows. A complex workflow whose steps can run
3392
- as actors over the real transport — `workflow.do(step, rpc, data, { actor:
3393
- actors.yasser })` — plus `workflow.expectEventually(...)` for polling async
3394
- effects. Actor steps never queue and never dispatch internally, so auth
3395
- middleware/permissions are exercised end-to-end; flows double as e2e tests and
3396
- staged/production health checks. Ships UserFlowActor types +
3397
- createHttpUserFlowActors (lazy sign-in via `/auth/sign-in/actor` with a
3398
- server-held secret), inspector source `'user-flow'`, and a console badge.
3399
- - Updated dependencies [5f2c566]
3400
- - Updated dependencies [8dfddc3]
3401
- - @pikku/core@0.12.48
3402
-
3403
- ## 0.12.31
3404
-
3405
- ### Patch Changes
3406
-
3407
- - 029fe2c: Fail `pikku all` when more than one `@pikku/core` version is installed. A split
3408
- `@pikku/core` produces two separate `pikkuState` registries at runtime, so wirings
3409
- (workflows, RPCs, queue workers, middleware) register into one copy while the runner
3410
- reads the other and they silently fail to resolve (e.g. `WorkflowNotFoundError` for a
3411
- workflow that is clearly registered). The preflight scans the project's `node_modules`,
3412
- and errors (`PKU717`) with the offending versions/paths. Override with
3413
- `PIKKU_ALLOW_DUPLICATE_CORE=1` to downgrade to a warning.
3414
- - Updated dependencies [e9a778f]
3415
- - @pikku/core@0.12.45
3416
-
3417
- ## 0.12.30
3418
-
3419
- ### Patch Changes
3420
-
3421
- - 41ce2cb: Upgrade to TypeScript 6 and raise the minimum Node.js version to 22.
3422
-
3423
- All packages now build against `typescript@^6.0.3` and declare `engines.node >= 22`. Internal tooling (`ts-json-schema-generator`, `zod-to-ts`) was bumped to TypeScript 6-compatible releases.
3424
-
3425
- - Updated dependencies [41ce2cb]
3426
- - @pikku/core@0.12.44
3427
-
3428
- ## 0.12.29
3429
-
3430
- ### Patch Changes
3431
-
3432
- - 7b5b10a: fix(workflow): include suspend steps in plannedSteps with readable displayName
3433
-
3434
- `workflow.suspend(reason)` calls now appear in the static `plannedSteps` ladder
3435
- produced by `deriveWorkflowPlan`. Previously the inspector ignored them, so the
3436
- runtime's `__workflow_suspend:<reason>` steps had no planned counterpart and
3437
- the UI appended them as orphans at the bottom of the step list instead of
3438
- showing them at the correct position.
3439
-
3440
- Changes:
3441
- - `WorkflowPlannedStep` gains an optional `displayName` field — the human-
3442
- readable label to show in the UI (falls back to `stepName` when absent).
3443
- - New `SuspendStepMeta` type added to `WorkflowStepMeta`.
3444
- - Inspector extracts `workflow.suspend('reason')` calls and emits a
3445
- `SuspendStepMeta` step with `type: 'suspend'` and `reason`.
3446
- - `collectNamedSteps` maps a suspend step to
3447
- `{ stepName: '__workflow_suspend:<reason>', displayName: '<reason>' }`,
3448
- matching the key the runtime stores so the UI can overlay live status
3449
- onto the planned position.
3450
-
3451
- - Updated dependencies [7b5b10a]
3452
- - @pikku/core@0.12.42
3453
-
3454
- ## 0.12.28
3455
-
3456
- ### Patch Changes
3457
-
3458
- - 66d43d1: Add `deploy.defaultTarget` to `pikku.config.json` to override the default deploy target ('serverless') for functions without an explicit `deploy` flag.
3459
- - a8c9e6d: feat(inspector): add PKU940 — block type casts on rpc.invoke() calls
3460
-
3461
- The inspector now emits a critical PKU940 error when `rpc.invoke()` is called
3462
- with an `as` cast on an argument (`rpc.invoke('fn', data as any)`) or when its
3463
- result is cast (`rpc.invoke('fn', data) as any`). Both patterns defeat Pikku's
3464
- generated type safety and are rejected at build time.
3465
-
3466
- - ba1ab08: refactor(workflow): replace `inline: false` with `workflowQueued: true` on function meta
3467
-
3468
- The per-function workflow dispatch flag has been renamed from the confusing
3469
- negative `inline: false` to the explicit positive `workflowQueued: true`.
3470
- Two companion fields are also added: `workflowRetries` and `workflowTimeout`
3471
- as function-level equivalents of the per-call-site `NodeOptions` fields.
3472
-
3473
- **Breaking change (patch — flag was undocumented):** rename `inline: false`
3474
- to `workflowQueued: true` on any `pikkuSessionlessFunc` / `pikkuFunc` that
3475
- dispatches its workflow steps via the queue.
3476
-
3477
- **Behaviour change:** a step marked `workflowQueued: true` now throws if no
3478
- queue service is configured, instead of silently falling back to inline
3479
- execution.
3480
-
3481
- **Bug fix:** `post-process.ts` was registering `wf-step-*` queues for every
3482
- workflow step node; it now only registers them for steps that are actually
3483
- `workflowQueued: true`, avoiding spurious queue resource allocation.
3484
-
3485
- - Updated dependencies [ba1ab08]
3486
- - @pikku/core@0.12.40
3487
-
3488
- ## 0.12.27
3489
-
3490
- ### Patch Changes
3491
-
3492
- - 41ff485: fix(inspector): register functions in a dedicated pass before wiring resolution
3493
-
3494
- The deterministic-codegen change sorted `program.getSourceFiles()` so generated
3495
- output is byte-identical across runs. But function registration (`addFunctions`)
3496
- ran in the same sweep as wiring resolution (`visitRoutes`), so once traversal
3497
- became alphabetical, a wiring file could be visited before the file that defines
3498
- the function it references — e.g. an addon contract (`hello.contracts.ts`)
3499
- before `hello.functions.ts` — producing a spurious `PKU559` ("No function
3500
- metadata found for channel handler").
3501
-
3502
- Function registration now runs in its own pass (`visitFunctions`) over the
3503
- sorted files, completing before any transport/wiring resolution, so resolution
3504
- no longer depends on source-file order. Also sort the `register.gen.ts` schema
3505
- imports (driven by a `Set`) so that file is stable too, and opt the PII-check
3506
- tests into the now-opt-in classification scan.
3507
-
3508
- - d2078c8: fix(inspector): make codegen output deterministic across runs
3509
-
3510
- Two sources of non-reproducible `pikku all` output are fixed:
3511
- 1. **Random placeholder ids.** Anonymous/unnamed functions and inline (non-exported) permissions were given a `__temp_${randomUUID()}` id, so a referenced-but-not-exported `pikkuPermission` const (e.g. `permissions: { admin: [requiresPlatformAdmin] }`) produced a fresh UUID in the generated meta on every run. The placeholder is now derived deterministically from the call expression's source location (relative path + start offset), still `__temp_`-prefixed so downstream name resolution is unchanged.
3512
- 2. **Unstable file-traversal order.** The two inspector sweeps iterated `program.getSourceFiles()` in glob + import-graph order, which varies run to run, so meta keys (and anything serialized in insertion order) were emitted in a different order each time — making a plain `git diff` of generated files look like functions were appearing/vanishing when the set was identical. Source files are now sorted by file name before the sweeps.
3513
-
3514
- Net effect: byte-identical generated output across repeated runs with no source changes (verified across the full `.pikku` tree of a 331-function project).
3515
-
3516
- - e6fd12b: perf(inspector,cli): persist generated TS schemas to disk across runs
3517
-
3518
- `generateAllSchemas` already cached its `ts-json-schema-generator` output
3519
- in-memory (keyed by the generated custom-types content), so the 2nd and 3rd
3520
- inspector passes within a single `pikku all` were near-free. But the cache
3521
- never survived the process, so every fresh `pikku all` paid the full cold cost
3522
- of building a second TS program + running ts-json-schema-generator — on a
3523
- 331-function project that's ~2.2s, the single largest line item of a run.
3524
-
3525
- The cache is now also persisted to disk (`node_modules/.cache/pikku/ts-schemas.json`,
3526
- gitignored by convention), keyed by a hash of the custom-types content plus the
3527
- generator options that affect output. A warm `pikku all` whose function types
3528
- are unchanged loads the schemas from disk and skips schema generation entirely;
3529
- the cold first pass drops by ~3.4s in practice (it also primes the in-memory
3530
- cache for the re-inspect passes). Zod schemas are still regenerated every run
3531
- (already ~1ms each). Output is byte-identical to a cold run (verified across the
3532
- full generated tree). The key is derived from the same content the in-memory
3533
- cache uses, so any type change busts it. It also folds in the `@pikku/inspector`
3534
- package version, so upgrading the inspector (the channel a schema-format change
3535
- ships through) auto-invalidates every cache; `SCHEMA_CACHE_VERSION` remains a
3536
- manual lever for in-development format changes between releases.
3537
-
3538
- Opt-out: omit `schemaConfig.cacheDir` (the CLI sets it by default).
3539
-
3540
- - 244d892: perf(cli,inspector): make the data-classification scan opt-in (`pikku all --security`)
3541
-
3542
- `pikku all` was spending the bulk of its wall-clock on the data-classification
3543
- leak check. For every function, on every inspector pass, it called
3544
- `checker.getReturnTypeOfSignature` to infer the handler's return type and scan it
3545
- for `Private`/`Pii`/`Secret` brands — the single most expensive type-checker
3546
- operation. On a 331-function project that was ~7.3s (≈half the total), repeated
3547
- across all three inspector passes, even though the scan only emits diagnostics
3548
- and never affects generated output.
3549
-
3550
- The scan is a security lint, not codegen, so it's now **off by default** and gated
3551
- behind a new `--security` flag (or `security: true` in the config). A plain
3552
- `pikku all` skips return-type inference entirely; run `pikku all --security`
3553
- (optionally with `--fail-on-error`) in CI/pre-deploy to enforce it. On the
3554
- 331-function project this cut `pikku all` from ~15.3s to ~9.6s.
3555
-
3556
- Also: the `all` command now reads back the run's recorded per-step durations and,
3557
- under `PIKKU_TIMING=1`, prints a slowest-first timing table — making it easy to
3558
- see where codegen time goes without adding any hot-path instrumentation.
3559
-
3560
- - 940c253: Populate `plannedSteps` and `deterministic` on serialized DSL workflow graphs. For a DSL workflow with no loops (fanout), the inspector now records every named step in source order, so a UI can render the run's step skeleton up front without executing it or hand-listing steps. `deterministic` is `true` only for a flat, loopless, branch-free workflow (exact sequence known ahead of time); a branchy-but-loopless workflow lists all possible steps with `deterministic: false`; any fanout makes the count runtime-dependent so neither field is emitted (just `deterministic: false`). Only `source: 'dsl'` workflows are planned — `complex` step trees omit inline branches and flatten loops, so their plans would misreport determinism. The runtime already threads these fields from workflow meta onto each run via `getRun`.
3561
- - Updated dependencies [4be205f]
3562
- - Updated dependencies [061c717]
3563
- - Updated dependencies [2c55e13]
3564
- - Updated dependencies [c745c26]
3565
- - Updated dependencies [57900b5]
3566
- - Updated dependencies [72694f6]
3567
- - @pikku/core@0.12.39
3568
-
3569
- ## 0.12.26
3570
-
3571
- ### Patch Changes
3572
-
3573
- - ed548d5: fix(auth): skip the generated global `betterAuthSession()` when the user registers their own
3574
-
3575
- The CLI's `auth.gen.ts` unconditionally wired a global
3576
- `addHTTPMiddleware('*', [betterAuthSession()])` (default map) on the stateful
3577
- path. A project that needs a customized session bridge — `mapSession`,
3578
- `impersonation`, `apiKey` — had to register a second global
3579
- `betterAuthSession({...})`, leaving two in the chain; the generated default ran
3580
- first and short-circuited (`if (session) next()`) so the custom one never took
3581
- effect.
3582
-
3583
- The inspector now records `state.auth.hasUserSessionMiddleware` when it sees a
3584
- user-authored **global** `betterAuthSession` registration (route-scoped and
3585
- `.gen.ts` registrations are ignored, so regeneration never self-suppresses).
3586
- The CLI omits its own global `betterAuthSession()` from `auth.gen.ts` when that
3587
- flag is set — exactly one session bridge in the chain, the user's. Mirrors the
3588
- existing stateless skip (`userStatelessSession`, #754).
3589
-
3590
- ## 0.12.25
3591
-
3592
- ### Patch Changes
3593
-
3594
- - b6ba601: fix(lint): don't flag pikkuAuth's session param as a non-destructured wire
3595
-
3596
- `pikkuAuth`'s handler is `(services, session)` — the second parameter is the
3597
- resolved user session, not a wires bag. The inspector was extracting "wires"
3598
- from that parameter (`extractUsedWires(handler, 1)`), so a permission like
3599
- `pikkuAuth(async ({ logger }, session) => !!session)` tripped
3600
- `wiresNotDestructured` even though `session` cannot be destructured. pikkuAuth
3601
- exposes no user-facing wires parameter, so no wires meta is recorded for it.
3602
-
3603
- - ae7fc5d: Include gateway platform and auth fields in inspected gateway metadata.
3604
- - decdad5: fix(lint): don't fail the build on framework-synthesized functions
3605
-
3606
- The `servicesNotDestructured`/`wiresNotDestructured` defaults (`error`) were
3607
- tripping on functions the user can't edit: generated `.gen.ts` wrappers (the
3608
- opaque `authHandler`, the cli channel raw dispatcher) and synthetic route→addon
3609
- bridges (`http:<method>:<route>`, no source file). `computeDiagnostics` now skips
3610
- any function without a real, non-generated source file, so the lint only nudges
3611
- hand-written user code. Also destructures the CLI's own `all` command.
3612
-
3613
- - Updated dependencies [ae7fc5d]
3614
- - Updated dependencies [fa7a09c]
3615
- - @pikku/core@0.12.37
3616
-
3617
- ## 0.12.24
3618
-
3619
- ### Patch Changes
3620
-
3621
- - 5fe3f47: fix(better-auth): skip the auto-generated stateless session middleware when the
3622
- project registers its own. Closes #754.
3623
-
3624
- With `session.cookieCache` enabled the CLI generates a global
3625
- `betterAuthStatelessSession()` using the default `{ userId }` map. Because session
3626
- middleware short-circuits once a session is set (`if (session) next()`) and the
3627
- generated file is imported before user wirings, that default-map middleware ran
3628
- first and **pre-empted** a project's own `betterAuthStatelessSession({ mapSession })`
3629
- — silently dropping custom session fields (`role`, `locale`, …).
3630
-
3631
- The inspector now detects a user-owned global registration (a
3632
- `betterAuthStatelessSession(...)` call inside `addGlobalMiddleware` or the global
3633
- form of `addHTTPMiddleware` — the array form or the `'*'` pattern, not a
3634
- route-scoped `addHTTPMiddleware('/path', …)`; ignoring `.gen.ts` files and bare
3635
- standalone calls) and
3636
- sets `state.auth.userStatelessSession`. When set, the CLI skips writing
3637
- `auth-middleware.gen.ts` (and removes a stale one) so the project's own middleware
3638
- — with its custom `mapSession` — is the only one registered. Projects without a
3639
- custom map are unaffected: the default middleware is still generated.
3640
-
3641
- - 3ba12ca: Stop consumed-addon parent services from polluting every per-unit deploy bundle, and stub the AI SDKs out of non-agent units.
3642
-
3643
- `aggregateRequiredServices` added `addonRequiredParentServices` (the services a consumed addon needs from its parent — e.g. `aiAgentRunner`, `deploymentService`, `metaService`) to **every** unit's `requiredServices` unconditionally. For any project that consumes an addon, this marked those services required on all units, so the per-unit service tree-shaking (and the gen-file/module stubs that key off the `false` flags) never fired — every unit shipped the full set. These parent services are now added only to units that actually deploy an addon function (its `pikkuFuncId` appears in `usedFunctions`); a unit that only calls the addon over RPC, or never touches it, no longer carries them.
3644
-
3645
- On the back of the now-honest flags, the bundler stubs the AI SDK packages (`@pikku/ai-vercel`, `@ai-sdk/*`, `ai`) out of any unit where `aiAgentRunner` is not required, via a new service→module stub map alongside the existing gen-file stub map. The shared services factory must guard runner construction behind a defined-check on the dynamic import so a stubbed unit simply skips building the runner.
3646
-
3647
- ## 0.12.23
3648
-
3649
- ### Patch Changes
3650
-
3651
- - 807a8d0: Add `refHTTP` / `refChannel` / `refCLI` so a consumer can wire an addon's HTTP routes, channel actions, and CLI commands directly from the addon's published `.pikku` contract metadata — no addon source is imported and nothing is hand-wired. These mirror the existing `ref('namespace:fn')` helper: each reference resolves the addon's already-loaded contract (via `wireAddon`) and proxies every function through `ref()` (RPC) at runtime.
3652
- - **Inspector:** `wireHTTPRoutes`/`wireChannel`/`wireCLI` now expand `refHTTP('ns:contract')` / `refChannel('ns:contract')` / `refCLI('ns:contract')` call expressions against `state.exportedContracts.addon{Http,Channel,Cli}` (already namespaced and `packageName`-tagged by `loadAddonFunctionsMeta`). An optional second argument overrides the mount basePath, e.g. `refHTTP('ext:helloRoutes', { basePath: '/ext' })`; otherwise the addon contract's own basePath is preserved.
3653
- - **CLI codegen:** the generated `pikku-function-types.gen.ts` now emits `refHTTP`/`refChannel`/`refCLI` (exported through `#pikku`) backed by const maps built from each wired addon's contract metadata, with every function pre-bound to `ref('ns:fn')`. Type-checking and runtime wiring resolve from the same generated artifact, so a reference can never be an inert marker.
3654
- - **Addon authoring bans:** when inspecting an addon package (`isAddon`), the inspector now raises a critical error if the addon calls a transport wiring helper (`wireHTTP`/`wireHTTPRoutes`/`wireChannel`/`wireCLI`/`wireScheduler`/`wireQueueWorker`/`wireMCPPrompt`/`wireMCPResource`/`wireTrigger`/`wireTriggerSource`/`wireGateway`/`wireAddon`) — these are the consuming app's responsibility (`PKU920`) — or if a `define*` contract carries `middleware`/`permissions`, which the consuming app applies, not the addon (`PKU921`). Service declarations (`wireSecret`/`wireVariable`/`wireCredential`) and function-level middleware/permissions remain allowed.
3655
- - **Deploy-bundle fix:** the HTTP/channel/CLI codegen commands now always emit their wiring and meta gen files once they report the category as active (truthy return), including the contracts-only or synthetic-route case where there are no local `wireHTTP`/`addChannel`/`wireCLI` source files. The generated bootstrap imports those files unconditionally, so skipping them left per-unit deploy bundles (e.g. Cloudflare units for scheduled tasks and workflow steps) unable to resolve `pikku-http-wirings.gen.js` and failing to build.
3656
-
3657
- ## 0.12.22
3658
-
3659
- ### Patch Changes
3660
-
3661
- - 06234a9: Fix DSL `Promise.all` fanout silently failing to register its child RPC (causing a runtime "Function not found").
3662
-
3663
- Two distinct causes are addressed:
3664
- - A fanout/group captured into a variable (`const results = await Promise.all(array.map(e => workflow.do(...)))`) was dropped entirely, because the `const`-declaration path had no `Promise.all` branch — fanout handling only ran on the bare/assignment path. The declaration path now extracts fanout and parallel groups too.
3665
- - `extractStringLiteral` threw on a `+` concatenation with a non-static operand (e.g. `'Enrich ' + (e.id ?? e.name)`), unlike a template literal (`` `Enrich ${e.id ?? e.name}` ``) which never threw. The throw was uncaught while scanning workflow invocations and aborted the run. The `+` branch now falls back to `${...}` placeholders to match template literals, and a step's cosmetic display name can no longer block RPC registration.
3666
-
3667
- - 8e72c93: Exclude `node_modules` from inspector source scanning. A locally-installed addon (under the project's `node_modules`) is a dependency, not project source — scanning it double-counted the addon's own application types (`CoreConfig`/`CoreServices`/`CoreSingletonServices`) and failed `pikku all` with "More than one … found". Addons still contribute via their generated metadata, not by being re-scanned as source.
3668
- - 6645e7a: Add a severity model for coded diagnostics so security findings can surface without blocking the dev server.
3669
- - `InspectorLogger` gains `diagnostic({ severity, code, message })` (`severity: 'warn' | 'error' | 'critical'`). `critical(code, message)` is now sugar for `diagnostic({ severity: 'critical', ... })`.
3670
- - The CLI fails the build only on `critical` diagnostics by default. New global flags `--fail-on-error` and `--fail-on-warn` (implies `--fail-on-error`) opt into stricter gating; `--fail-on-critical` is always on.
3671
- - Data-classification leaks (`PKU910`) are now emitted at `error` severity instead of `critical`. They are still printed, but no longer abort `pikku all` / the dev server — pass `--fail-on-error` (e.g. at deploy) to make them blocking and recommend a fix.
3672
- - Contract-immutability drift (`PKU861`) during `pikku versions update` (run inside `pikku all`) no longer calls `process.exit(1)`. It is surfaced as an `error` diagnostic and skips saving the manifest, so a stale baseline can't crash-loop the dev server. `pikku versions check` remains the hard gate, and `--fail-on-error` makes `pikku all` block on it at deploy.
3673
-
3674
- - Updated dependencies [6bca38f]
3675
- - @pikku/core@0.12.35
3676
-
3677
- ## 0.12.21
3678
-
3679
- ### Patch Changes
3680
-
3681
- - ef50347: Tree-shake the better-auth server out of non-auth units.
3682
- - `@pikku/better-auth`: add `betterAuthStatelessSession()` — a session middleware that verifies the signed better-auth cookie cache via `better-auth/cookies` (`getCookieCache`) using only `BETTER_AUTH_SECRET`, with no `services.auth()`, DB round-trip, or full server import. Mark the package `sideEffects: false` so unused barrel re-exports drop.
3683
- - `@pikku/cli`: when `session.cookieCache` is enabled in the better-auth config, generate the stateless session middleware into a separate `auth-middleware.gen.ts` and wire it globally, keeping the full `/api/auth/**` server only in the auth unit. Deploy artifacts (esbuild metafile + sourcemap) are now off by default; `--debug-artifacts` re-enables them.
3684
- - `@pikku/inspector`: ensure the orphan `auth-middleware.gen.ts` (imported by nothing) is still inspected so its global `addHTTPMiddleware('*')` registration is not dropped.
3685
-
3686
- Net effect: a non-auth unit carries ~22KB (cookie-verify floor) instead of the full ~1.25MB better-auth backend.
3687
-
3688
- ## 0.12.20
3689
-
3690
- ### Patch Changes
3691
-
3692
- - a027a8e: feat: emit auth provider + plugin metadata as `auth-meta.gen.json` for the console SSO page
3693
-
3694
- The enabled social providers and Better Auth plugins are now extracted statically
3695
- and written to a generated `auth-meta.gen.json`, replacing the runtime
3696
- `setAuthRegistry`/`getAuthRegistry` approach — so the console can show them without
3697
- evaluating the Better Auth factory.
3698
- - **inspector**: the `pikkuBetterAuth` inspector now reads the `plugins` array from
3699
- the `betterAuth({ ... })` config and records each plugin id (the callee name of
3700
- each `plugins: [organization(), bearer()]` entry) on the auth definition.
3701
- - **cli**: `pikku auth` (and `pikku all`) emit `auth/pikku-auth-meta.gen.json` (path
3702
- configurable via `authMetaJsonFile`) containing `basePath`, `hasCredentials`, the
3703
- enabled `providers` (`id` + `displayName` + `secretId`), and the enabled `plugins`
3704
- (`id` + `displayName`). The previous `setAuthRegistry(...)` runtime wiring is
3705
- removed from the generated `auth.gen.ts`.
3706
- - **better-auth**: exports a `PLUGIN_REGISTRY` and `pluginDisplayName(id)` helper so
3707
- plugin ids resolve to human-readable names.
3708
- - **core**: removes the unreleased `setAuthRegistry`/`getAuthRegistry` runtime auth
3709
- registry (now superseded by `auth-meta.gen.json`).
3710
- - **addon-console**: `getAuthProviders` reads `auth-meta.gen.json` and returns the
3711
- configured providers, plugins, and `hasCredentials` flag.
3712
- - **console**: the Auth Providers (SSO) page fetches `console:getAuthProviders` and
3713
- marks each provider configured/unconfigured, lists email+password credentials as a
3714
- provider, and shows the enabled Better Auth plugins.
3715
-
3716
- - a027a8e: fix: address Better Auth review findings (secret/variable batch typing, auth init, guards)
3717
- - **core**: `SecretService.getSecrets` / `VariablesService.getVariables` (and the
3718
- Local/Typed/Scoped/AWS implementations) now return `Partial<T>`, honestly
3719
- reflecting that missing keys are omitted at runtime rather than typing partial
3720
- data as fully populated. `ScopedSecretService.getSecrets` now throws on a
3721
- disallowed key instead of silently filtering it out.
3722
- - **cli**: the generated `services.auth()` thunk clears its memoised promise on
3723
- rejection, so a transient Better Auth/Kysely startup failure no longer
3724
- permanently poisons auth for the process lifetime.
3725
- - **inspector**: the `pikkuBetterAuth` export guard now requires an exported
3726
- `const` (rejects `export let`/`export var`), matching its error message.
3727
- - **console**: the Microsoft auth provider's `callbackId` is `microsoft` (the
3728
- Better Auth provider id) rather than `microsoft-entra-id`.
3729
-
3730
- - a027a8e: feat(auth): migrate auth integration from Auth.js to Better Auth
3731
-
3732
- The auth integration is now built on [Better Auth](https://better-auth.com)
3733
- and ships as a single package, `@pikku/better-auth` (replacing the former
3734
- `@pikku/auth-js`). There is exactly one auth package now.
3735
- - `pikkuBetterAuth(async ({ secrets, variables }) => betterAuth({ ... }))` is the new
3736
- single entry point. The CLI inspects the `betterAuth(...)` call and generates:
3737
- - `auth.gen.ts` — a catch-all `${basePath}{/*splat}` HTTP route per method and
3738
- a global `betterAuthSession({ auth })` middleware that bridges the Better
3739
- Auth session into the Pikku wire session.
3740
- - `auth-secrets.gen.ts` — `wireSecret(BETTER_AUTH_SECRET)` plus a
3741
- `<PROVIDER>_OAUTH` secret for each configured social provider, and
3742
- `wireVariable` for non-secret provider config (e.g. `MICROSOFT_TENANT_ID`,
3743
- `COGNITO_DOMAIN`/`REGION`/`USER_POOL_ID`).
3744
- - `auth.types.ts` — a typed `pikkuBetterAuth` re-export.
3745
- - `add-auth` (inspector) walks into the `betterAuth(...)` options to discover the
3746
- configured providers and required secrets/variables.
3747
- - The auth secret is now auto-wired by codegen from `BETTER_AUTH_SECRET` — it no
3748
- longer needs to be registered as a JWT signing key in `services.ts`.
3749
-
3750
- CLI fix included: scaffold files generated outside `srcDirectories` (e.g. an
3751
- `auth.gen.ts` under a project's `pikku/` dir) are now added to the inspector's
3752
- wiring files, so their routes and secret metadata are picked up. The generated
3753
- wiring imports Pikku types via a resolved relative path instead of a hardcoded
3754
- `#pikku` specifier, so templates without a `#pikku` import map type-check.
3755
-
3756
- - Updated dependencies [a027a8e]
3757
- - Updated dependencies [a027a8e]
3758
- - Updated dependencies [a027a8e]
3759
- - Updated dependencies [a027a8e]
3760
- - @pikku/core@0.12.32
3761
-
3762
- ## 0.12.19
3763
-
3764
- ### Patch Changes
3765
-
3766
- - fe70fe0: fix(db): make classified columns usable in Kysely queries and emit real zod
3767
-
3768
- Two fixes so data-classified DB columns (`@private`/`@pii`/`@secret`, default
3769
- `private`) are usable end-to-end instead of poisoning ordinary app code:
3770
- 1. **Brand marker is now optional** (`{ readonly __classification__?: ... }`)
3771
- in both `@pikku/core` and the `pikku db migrate` schema header. A required
3772
- marker made a plain value (e.g. `string`) unassignable to a branded column
3773
- (`Private<string>`), breaking every Kysely `where`/insert/`.set()` operand —
3774
- any project with classified columns failed to type-check. Optional keeps the
3775
- brand structurally present (so the inspector's PKU910 output check still
3776
- detects it) while letting plain values flow IN. The inspector's level read is
3777
- now union-aware (`'pii' | undefined`) so pii/secret no longer silently
3778
- downgrade to private.
3779
- 2. **Zod codegen resolves classified `ColumnType<>`** to proper scalars instead
3780
- of `z.unknown()`. `pikku db migrate` emits `<Table>Z`/`InsertZ`/`PatchZ` from
3781
- the Select slot, unwrapping the brand and honoring insert-optionality from the
3782
- Insert slot's `| undefined`. Public `Generated<T>`/bare/nested shapes are
3783
- unchanged.
3784
-
3785
- - Updated dependencies [fe70fe0]
3786
- - @pikku/core@0.12.31
3787
-
3788
- ## 0.12.18
3789
-
3790
- ### Patch Changes
3791
-
3792
- - 20750fd: feat(workflow): decide step dispatch purely per-function
3793
-
3794
- Workflow step execution (inline vs queue dispatch) is now decided entirely by
3795
- the step's function `inline` flag — the workflow-level / run-level `inline`
3796
- meta no longer participates in per-step dispatch.
3797
- - Steps default to **inline**, so a normally-started (queue-backed) workflow
3798
- runs its whole chain in one orchestrator pass instead of one queue
3799
- round-trip per step.
3800
- - A function marked `inline: false` is dispatched via the queue (its own
3801
- worker, retry isolation). When `inline: false` but no `queueService` is
3802
- configured, the step falls back to inline and emits a `logger.warn` instead
3803
- of silently swallowing the misconfiguration.
3804
- - Removed the now-unused workflow-level `inline` from `WorkflowsMeta` /
3805
- `WorkflowRuntimeMeta`, the inspector's workflow extraction, the DSL→graph
3806
- converter, and the deploy analyzer / service inference (which now key off
3807
- the per-function flag). Run-level `inline` is retained: it still controls
3808
- whether a whole run executes in-process without queue infrastructure.
3809
-
3810
- - Updated dependencies [cd101a5]
3811
- - Updated dependencies [ac16265]
3812
- - Updated dependencies [a05e864]
3813
- - Updated dependencies [20750fd]
3814
- - @pikku/core@0.12.30
3815
-
3816
- ## 0.12.17
3817
-
3818
- ### Patch Changes
3819
-
3820
- - 2cf67be: Add inline option to pikkuFunc/pikkuSessionlessFunc for workflow step dispatch
3821
-
3822
- By default, workflow steps now run inline (no queue hop). Set inline: false on a function to force dispatch through the queue for that step.
3823
-
3824
- - Updated dependencies [2cf67be]
3825
- - @pikku/core@0.12.28
3826
-
3827
- ## 0.12.16
3828
-
3829
- ### Patch Changes
3830
-
3831
- - 646c5a8: Fix inspector failing to extract descriptions written as string concatenation (`+`). Descriptions like `'line one ' + 'line two'` are now correctly resolved to their full value. The `checker` parameter is also threaded through `getCommonWireMetaData` so all wiring types benefit from static string evaluation.
3832
-
3833
- ## 0.12.15
3834
-
3835
- ### Patch Changes
3836
-
3837
- - 0db854e: Fix workflow DSL extractor treating `x = await workflow.do(...)` as a set-step when `x` was previously declared as `null`. The referenced function is now correctly registered in `invokedFunctions` and `internalFiles`, so it appears in the generated `pikku-functions.gen.ts`.
3838
- - 8249f6f: Fix `isStringLike` to unwrap type assertion expressions (`as T` / `<T>expr`) so that `workflow.do('step', 'rpcName' as any, data)` is correctly parsed as an RPC step rather than silently dropped as an inline step. Also removes the `as any` cast from the `Emails` step in `all.workflow.ts` now that the inspector handles it, and ensures `pikku all` generates email template artifacts.
3839
- - f373a87: Fix PKU910 classification semantics and Postgres annotation propagation.
3840
-
3841
- **Inspector (`@pikku/inspector`):**
3842
- - `findPiiPaths()` now returns `ClassifiedField[]` (path + classification level) so `private`/`pii` and `secret` brands are distinguished
3843
- - `Secret<T>` fields are blocked in the output of all exposed functions (sessioned or not)
3844
- - `Private<T>` / `Pii<T>` fields are only blocked in sessionless functions — authenticated (sessioned) functions may return private-classified data to their callers
3845
-
3846
- **CLI (`@pikku/cli`):**
3847
- - Fix missing `rootDir` in the Postgres `generateSchemaTypes` call — the annotations sidecar file (`db/annotations.gen.json`) was silently ignored during Postgres migrations, causing columns annotated `@public` to remain branded as `Private<T>` in the generated schema
3848
-
3849
- ## 0.12.14
3850
-
3851
- ### Patch Changes
3852
-
3853
- - 4b5c75b: feat(auth-js): wire OIDC config (issuer/tenantId) as variables, expand provider registry
3854
- - Move `issuer` and `tenantId` out of the secret blob for OIDC providers (auth0, okta, azure-ad, keycloak, cognito, microsoft-entra-id) — they are public config URLs, not secrets. Now registered via `wireVariable` and loaded at runtime via `services.variables.get()`.
3855
- - Expand provider registry from 13 to 31 providers: reddit, notion, instagram, zoom, figma, tiktok, threads, patreon, dropbox, bitbucket, hubspot, salesforce, atlassian, strava, keycloak, cognito, microsoft-entra-id added.
3856
- - `serialize-auth-gen` emits `wireVariable({...})` declarations and `services.variables.get()` calls in the generated factory for OIDC providers.
3857
- - Integration verifier exercises real `/auth/providers` endpoint with `LocalSecretService` + `LocalVariablesService`, including a spy test proving `services.variables.get('AUTH0_ISSUER')` is called at request time.
3858
-
3859
- - 4b5c75b: Add end-to-end data classification for SQLite and Postgres projects.
3860
-
3861
- **Core (`@pikku/core`):** New `Private<T>` and `Secret<T>` intersection brands, `ClassificationManifest`, `ColumnClassification`, and `AnonymizeStrategy` types exported from `data-classification.ts`.
3862
-
3863
- **CLI (`@pikku/cli`):**
3864
- - SQL comment annotations: `-- @public`, `-- @private[:strategy]`, `-- @secret[:strategy]` on `CREATE TABLE` columns and `ALTER TABLE ... ADD COLUMN` statements. Unannotated columns default to `private`.
3865
- - `pikku db migrate` now emits a `classification.gen.ts` manifest alongside `schema.d.ts`.
3866
- - New `pikku db audit` command — prints a per-column classification summary and warns on `private`/`secret` columns with no anonymize strategy.
3867
- - Postgres dialect support in `resolveDb`, `PostgresMigrationExecutor`, and `PostgresIntrospector`.
3868
-
3869
- **Inspector (`@pikku/inspector`):** New PKU910 check — `findPiiPaths()` walks inferred function return types looking for `__pii__` brands (including inside `Array<T>`, `Record<K,V>`, and index signatures) and fails the build if a function exposes branded fields in its output.
3870
-
3871
- - Updated dependencies [4b5c75b]
3872
- - Updated dependencies [4b5c75b]
3873
- - @pikku/core@0.12.27
3874
-
3875
- ## 0.12.13
3876
-
3877
- ### Patch Changes
3878
-
3879
- - 665bdb0: Add end-to-end data classification for SQLite and Postgres projects.
3880
-
3881
- **Core (`@pikku/core`):** New `Private<T>` and `Secret<T>` intersection brands, `ClassificationManifest`, `ColumnClassification`, and `AnonymizeStrategy` types exported from `data-classification.ts`.
3882
-
3883
- **CLI (`@pikku/cli`):**
3884
- - SQL comment annotations: `-- @public`, `-- @private[:strategy]`, `-- @secret[:strategy]` on `CREATE TABLE` columns and `ALTER TABLE ... ADD COLUMN` statements. Unannotated columns default to `private`.
3885
- - `pikku db migrate` now emits a `classification.gen.ts` manifest alongside `schema.d.ts`.
3886
- - New `pikku db audit` command — prints a per-column classification summary and warns on `private`/`secret` columns with no anonymize strategy.
3887
- - Postgres dialect support in `resolveDb`, `PostgresMigrationExecutor`, and `PostgresIntrospector`.
3888
-
3889
- **Inspector (`@pikku/inspector`):** New PKU910 check — `findPiiPaths()` walks inferred function return types looking for `__pii__` brands (including inside `Array<T>`, `Record<K,V>`, and index signatures) and fails the build if a function exposes branded fields in its output.
3890
-
3891
- - Updated dependencies [665bdb0]
3892
- - @pikku/core@0.12.25
3893
-
3894
- ## 0.12.12
3895
-
3896
- ### Patch Changes
3897
-
3898
- - 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.
3899
-
3900
- **Migration:** replace any bare `model: 'alias'` values with the full provider-qualified form and remove those blocks from `pikku.config.json`.
3901
-
3902
- - Updated dependencies [9060165]
3903
- - Updated dependencies [9060165]
3904
- - Updated dependencies [9060165]
3905
- - @pikku/core@0.12.21
3906
-
3907
- ## 0.12.11
3908
-
3909
- ### Patch Changes
3910
-
3911
- - 033d172: Log a critical inspector error when multiple functions resolve to the same `pikku` function name, instead of silently allowing routing map collisions. This may cause builds to fail if multiple functions previously resolved to the same `pikku` function name.
3912
- - Updated dependencies [b9ed73e]
3913
- - @pikku/core@0.12.19
3914
-
3915
- ## 0.12.0
3916
-
3917
- ## 0.12.10
3918
-
3919
- ### Patch Changes
3920
-
3921
- - ba8d6ff: Support inline functions in pikkuWorkflowComplexFunc with full DSL extraction
3922
- - d3ace0e: Inspector now captures the `deploy: 'serverless' | 'server' | 'auto'` option
3923
- from `pikkuFunc` / `pikkuSessionlessFunc` calls, alongside the other runtime
3924
- metadata (`expose`, `remote`, `mcp`, `readonly`, `approvalRequired`).
3925
-
3926
- Previously this field was defined on `FunctionRuntimeMeta` but never read
3927
- from the user's source, so `deploy: 'server'` was silently dropped. That
3928
- left downstream consumers — notably `@pikku/cli`'s deployment analyzer,
3929
- which routes server-targeted functions to a container unit — treating
3930
- every function as `serverless` regardless of its declared intent.
3931
-
3932
- - Updated dependencies [311c0c4]
3933
- - @pikku/core@0.12.18
3934
-
3935
- ## 0.12.9
3936
-
3937
- ### Patch Changes
3938
-
3939
- - 2ac6468: Fix workflow inspector crash when workflow.do() data object has a 'description' property
3940
- - fbcf5b9: Add version awareness to RPC handler: versioned functions now appear in the exposed RPC type map (e.g. `getData@v1`, `getData@v2`), enabling type-safe `rpc.invoke('getData@v1', data)` calls. Tree-shaking respects specific version filters without pulling in all versions. HTTP wirings correctly resolve versioned function IDs.
3941
- - Updated dependencies [fbcf5b9]
3942
- - @pikku/core@0.12.16
3943
-
3944
- ## 0.12.8
3945
-
3946
- ### Patch Changes
3947
-
3948
- - 624097e: Add deploy pipeline with provider-agnostic architecture
3949
- - Add MetaService with explicit typed API, absorb WiringService reads
3950
- - Add deployment service, traceId propagation, scoped logger
3951
- - Rewrite analyzer: one function = one worker, gateways dispatch via RPC
3952
- - Add Cloudflare deploy provider with plan/apply commands
3953
- - Add per-unit filtered codegen for deploy pipeline
3954
- - Skip missing metadata in wiring registration for deploy units
3955
- - Fix schema coercion crash when schema has no properties
3956
- - Fix E2E codegen: double-pass resolves cross-package Zod type imports
3957
-
3958
- - Updated dependencies [9e8605f]
3959
- - Updated dependencies [624097e]
3960
- - Updated dependencies [7ab3243]
3961
- - @pikku/core@0.12.15
3962
-
3963
- ## 0.12.7
3964
-
3965
- ### Patch Changes
3966
-
3967
- - 2ce0733: Fix credential services template variable passing, duplicate body/path param collision, and add credentialOverrides to wireAddon.
3968
- - Updated dependencies [2ce0733]
3969
- - @pikku/core@0.12.13
3970
-
3971
- ## 0.12.6
3972
-
3973
- ### Patch Changes
3974
-
3975
- - 84f01ad: Add credentialOverrides to wireAddon for remapping credential names, fix credential services template to pass variables argument.
3976
- - Updated dependencies [84f01ad]
3977
- - @pikku/core@0.12.12
3978
-
3979
- ## 0.12.5
3980
-
3981
- ### Patch Changes
3982
-
3983
- - 65eccc6: Cache Zod schema generation between re-inspection passes and batch imports by source file. Schemas are cached using a fingerprint of schemaLookup entries + file mtimes, so reinspections skip Zod generation entirely when schemas haven't changed. Source file imports are grouped so each file is imported once instead of per-schema. Reduces `pikku all` from ~5 minutes to ~13 seconds on projects with many Zod schemas.
3984
- - 0f59432: Add per-user credential system with CredentialService, OAuth2 route handlers, and KyselyCredentialService with envelope encryption
3985
- - Updated dependencies [0f59432]
3986
- - Updated dependencies [52b64d1]
3987
- - @pikku/core@0.12.10
3988
-
3989
- ## 0.12.4
3990
-
3991
- ### Patch Changes
3992
-
3993
- - 5866b66: Add critical error (PKU490) when Zod schemas and wiring calls (wireHTTPRoutes, addPermission, addHTTPMiddleware) coexist in the same file. The CLI uses tsImport to extract Zod schemas at runtime, which executes all top-level code — wiring side-effects crash in this context because pikku state metadata doesn't exist. Schemas and wirings must be in separate files.
3994
- - e412b4d: Optimize CLI codegen performance: 12x faster `pikku all`
3995
- - Reuse schemas across re-inspections (skip redundant `ts-json-schema-generator` runs)
3996
- - Cache TS schemas to disk (`.pikku/schema-cache.json`) for cross-run reuse
3997
- - Pass `oldProgram` to `ts.createProgram` for incremental TS compilation
3998
- - Cache parsed tsconfig in schema generator between runs
3999
- - Auto-include direct `addPermission`/`addHTTPMiddleware` in bootstrap via side-effect imports
4000
- - Skip `pikkuAuth()` errors when nested inside `addPermission`/`addHTTPPermission`
4001
-
4002
- - Updated dependencies [e412b4d]
4003
- - Updated dependencies [53dc8c8]
4004
- - Updated dependencies [0a1cc51]
4005
- - Updated dependencies [0a1cc51]
4006
- - Updated dependencies [0a1cc51]
4007
- - Updated dependencies [0a1cc51]
4008
- - Updated dependencies [0a1cc51]
4009
- - Updated dependencies [0a1cc51]
4010
- - Updated dependencies [0a1cc51]
4011
- - Updated dependencies [0a1cc51]
4012
- - Updated dependencies [0a1cc51]
4013
- - Updated dependencies [8b9b2e9]
4014
- - Updated dependencies [8b9b2e9]
4015
- - Updated dependencies [b973d44]
4016
- - Updated dependencies [8b9b2e9]
4017
- - Updated dependencies [8b9b2e9]
4018
- - @pikku/core@0.12.9
4019
-
4020
- ## 0.12.3
4021
-
4022
- ### Patch Changes
4023
-
4024
- - 508a796: Fix MCP server not exposing addon tools: resolve namespaced function IDs in MCP runner, load addon schemas after schema generation, and use resolveFunctionMeta for MCP JSON serialization
4025
- - 387b2ee: Add approval description inspection, track packageName on wire metadata, and resolve addon package names in channel/RPC wirings
4026
- - Updated dependencies [387b2ee]
4027
- - Updated dependencies [32ed003]
4028
- - Updated dependencies [7d369f3]
4029
- - Updated dependencies [508a796]
4030
- - Updated dependencies [ffe83af]
4031
- - Updated dependencies [c7ff141]
4032
- - @pikku/core@0.12.3
4033
-
4034
- ## 0.12.2
4035
-
4036
- ### Patch Changes
4037
-
4038
- - 3e04565: chore: update dependencies to latest minor/patch versions
4039
- - Updated dependencies [cc4c9e9]
4040
- - Updated dependencies [3e04565]
4041
- - @pikku/core@0.12.2
4042
-
4043
- ## 0.12.1
4044
-
4045
- ### Patch Changes
4046
-
4047
- - 62a8725: Rename 'external' to 'addon' throughout the codebase. All types, functions, config keys, and CLI options previously named `external` or `External` are now named `addon` or `Addon` (e.g. `ExternalPackageConfig` → `AddonConfig`, `externalPackages` → `addons`, `function-external` → `function-addon`).
4048
- - 8eed717: Add `readonly` flag to function config and runtime enforcement. Functions can be marked `readonly: true` in their config. At runtime, if a session has `readonly: true`, only functions marked as readonly can be called — otherwise a `ReadonlySessionError` (403) is thrown.
4049
- - 62a8725: `pikku versions check` now prints rich, human-readable output for all contract version errors instead of raw error codes. Each error type (PKU861–PKU865) shows the function name, separate input/output schema hashes with a `prev → current` arrow, and clear next-step instructions.
4050
-
4051
- The version manifest now stores separate `inputHash` and `outputHash` per version entry (backward-compatible — old string-hash manifests still load and validate correctly). `VersionValidateError` gains optional detail fields (`functionKey`, `version`, `previousInputHash`, `currentInputHash`, `previousOutputHash`, `currentOutputHash`, `nextVersion`, `latestVersion`, `expectedNextVersion`) for use by tooling.
4052
-
4053
- - 62a8725: Replace config-based addon declarations with the new `wireAddon()` code-based API. Addons are now declared directly in wiring files using `wireAddon({ name, package, rpcEndpoint?, auth?, tags? })` instead of the `addons` field in `pikku.config.json`. The inspector reads these declarations from the TypeScript AST at build time.
4054
- - 62a8725: Add `secretOverrides` and `variableOverrides` support to `wireAddon()`. These optional maps allow an app to remap an addon's secret/variable keys to its own names (e.g. `secretOverrides: { SENDGRID_API_KEY: 'MY_EMAIL_API_KEY' }`). The inspector validates that all override keys exist in the app's own secrets/variables definitions.
4055
- - Updated dependencies [62a8725]
4056
- - Updated dependencies [a3bdb0d]
4057
- - Updated dependencies [e0349ff]
4058
- - Updated dependencies [62a8725]
4059
- - Updated dependencies [e04531f]
4060
- - Updated dependencies [62a8725]
4061
- - Updated dependencies [a83efb8]
4062
- - Updated dependencies [8eed717]
4063
- - Updated dependencies [62a8725]
4064
- - Updated dependencies [62a8725]
4065
- - Updated dependencies [62a8725]
4066
- - @pikku/core@0.12.1
4067
-
4068
- ### New Features
4069
-
4070
- - AI agent metadata extraction
4071
- - HTTP route groups analysis
4072
- - Trigger and trigger source analysis
4073
- - Secret and variable declaration extraction
4074
- - Workflow graph inspection and DSL extraction
4075
- - Contract hashing for change detection
4076
- - OpenAPI spec generation (moved from CLI)
4077
-
4078
- ## 0.11.2
4079
-
4080
- ### Patch Changes
4081
-
4082
- - db9c7bf: Add workflow graph inspection and DSL extraction
4083
- - Updated dependencies [db9c7bf]
4084
- - @pikku/core@0.11.2
4085
-
4086
- ### Features
4087
-
4088
- - f35e89da: Add workflow graph inspection and DSL extraction
4089
- - Workflow graph inspection with `add-workflow-graph.ts`
4090
- - DSL workflow extraction utilities (extract, deserialize, validate)
4091
- - DSL to graph conversion for metadata generation
4092
-
4093
- ## 0.11.1
4094
-
4095
- ### Patch Changes
4096
-
4097
- - 4b811db: chore: updating all dependencies
4098
- - 28aeb7f: breaking: extract docs in the wiring meta
4099
- - ce902b1: feat: adding in pikkuSimpleWorkflowFunc
4100
- - 06e1a31: breaking: change session services to wire services
4101
- - Updated dependencies [4b811db]
4102
- - Updated dependencies [e12a00c]
4103
- - Updated dependencies [4579434]
4104
- - Updated dependencies [28aeb7f]
4105
- - Updated dependencies [ce902b1]
4106
- - Updated dependencies [06e1a31]
4107
- - @pikku/core@0.11.1
4108
-
4109
- ### Minor Changes
4110
-
4111
- - Add workflow inspection and analysis
4112
- - Add enhanced type extraction utilities
4113
-
4114
- # @pikku/inspector
4115
-
4116
- ## 0.10.2
4117
-
4118
- ### Patch Changes
4119
-
4120
- - 1967172: Update code generation to support channel middleware enhancements
4121
-
4122
- **Code Generation Updates:**
4123
- - Update channel type serialization to include middleware support
4124
- - Improve WebSocket wrapper generation for middleware handling
4125
- - Update CLI channel client generation with better type support
4126
- - Enhance services and schema generation for channel configurations
4127
-
4128
- **Inspector Updates:**
4129
- - Improve channel metadata extraction for middleware
4130
- - Better type analysis for channel lifecycle functions
4131
- - Enhanced post-processing for channel configurations
4132
-
4133
- - 753481a: Add bootstrap command, performance optimizations, and CLI improvements
4134
-
4135
- **New Features:**
4136
- - Add `pikku bootstrap` command for type-only generation (~13.5% faster than `pikku all`)
4137
- - Add configurable `ignoreFiles` option to pikku.config.json with sensible defaults (_.gen.ts, _.test.ts, \*.spec.ts)
4138
- - Export pikkuCLIRender helper from serialize-cli-types.ts with JSDoc documentation
4139
-
4140
- **Performance Improvements:**
4141
- - Add aggressive TypeScript compiler options (skipDefaultLibCheck, types: []) - ~37% faster TypeScript setup
4142
- - Add detailed performance timing to inspector phases (--logLevel=debug)
4143
- - Optimize file inspection with ignore patterns - ~10-20% faster overall
4144
-
4145
- **Enhancements:**
4146
- - Fix --logLevel flag to properly apply log level to logger
4147
- - Update middleware logging to use structured log format
4148
- - Improve CLI renderers to consistently use destructured logger service
4149
- - Fix middleware file generation when middleware groups exist
4150
-
4151
- - 44d71a8: fix: fixing inspector ensuring pikkuConfig is set
4152
- - Updated dependencies [ea652dc]
4153
- - Updated dependencies [4349ec5]
4154
- - Updated dependencies [44d71a8]
4155
- - @pikku/core@0.10.2
4156
-
4157
- ## 0.10.1
4158
-
4159
- ### Patch Changes
4160
-
4161
- - 778267e: fix: fixing inspector ensuring pikkuConfig is set
4162
- - Updated dependencies [778267e]
4163
- - @pikku/core@0.10.1
4164
-
4165
- ## 0.10.0
4166
-
4167
- This release includes significant improvements across the framework including tree-shaking support, middleware/permission factories, enhanced CLI functionality, improved TypeScript type safety, and comprehensive test strategies.
4168
-
4169
- For complete details, see https://pikku.dev/changelogs/0_10_0.md
4170
-
4171
- ## 0.9.6-next.0
4172
-
4173
- ### Patch Changes
4174
-
4175
- - feat: running @pikku/cli using pikku
4176
- - Updated dependencies
4177
- - @pikku/core@0.9.12-next.0
4178
-
4179
- ## 0.9.5
4180
-
4181
- ### Patch Changes
4182
-
4183
- - 501c120: fix: rpc internal meta file wasn't being imported
4184
-
4185
- ## 0.9.4
4186
-
4187
- ### Patch Changes
4188
-
4189
- - 6059c87: refactor: move PikkuPermission to pikkuPermission and same for middleware for api consistency to to improve future features
4190
- - 6db63bb: perf: changing http meta to a lookup map to reduce loops
4191
- - Updated dependencies [6059c87]
4192
- - Updated dependencies [6db63bb]
4193
- - Updated dependencies [74f8634]
4194
- - Updated dependencies [766fef1]
4195
- - @pikku/core@0.9.6
4196
-
4197
- ## 0.9.3
4198
-
4199
- ### Patch Changes
4200
-
4201
- - 9691aba: fix: add-functions should support both functions only and objects
4202
- - 2ab0278: refactor: no longer import ALL functions, only the ones used by rpcs
4203
- - 81005ba: feat: creating a smaller meta file for functions to reduce size
4204
- - b3c2829: fix (using ai): generating custom types broke imports.. this fixes it, but needs more robust training
4205
- - Updated dependencies [9691aba]
4206
- - Updated dependencies [2ab0278]
4207
- - Updated dependencies [81005ba]
4208
- - @pikku/core@0.9.3
4209
-
4210
- ## 0.9.2
4211
-
4212
- ### Patch Changes
4213
-
4214
- - 6cf8efd: feat: Adding PikkuDocs to function definition
4215
-
4216
- refactor: renaming APIDocs to PikkuDocs
4217
-
4218
- - a5905a9: chore: updating all dependencies
4219
- - Updated dependencies [1256238]
4220
- - Updated dependencies [6cf8efd]
4221
- - Updated dependencies [d3a9a09]
4222
- - Updated dependencies [840e078]
4223
- - Updated dependencies [667d23c]
4224
- - Updated dependencies [a5905a9]
4225
- - @pikku/core@0.9.2
4226
-
4227
- ## 0.9.1
4228
-
4229
- ### Patch Changes
4230
-
4231
- - fdb1593: core: bumping everything with a patch to sync up the major release inconsistencies in dependencies
4232
- - Updated dependencies [fdb1593]
4233
- - @pikku/core@0.9.1
4234
-
4235
- ## 0.9.0
4236
-
4237
- ### Breaking Changes
4238
-
4239
- - Normalized all transports to use "wirings" instead of events/routes/transports for consistency across the framework
4240
-
4241
- ## 0.8.1
4242
-
4243
- ### Patch Changes
4244
-
4245
- - 44e3ff4: feat: enhance CLI filtering with type and directory filters
4246
- - Add --types filter to filter by PikkuEventTypes (http, channel, queue, scheduler, rpc, mcp)
4247
- - Add --directories filter to filter by file paths/directories
4248
- - All filters (tags, types, directories) now work together with AND logic
4249
- - Add comprehensive logging interface to inspector package
4250
- - Add comprehensive test suite for matchesFilters function
4251
- - Support cross-platform path handling
4252
-
4253
- - 7c592b8: feat: support for required services and improved service configuration
4254
-
4255
- This release includes several enhancements to service management and configuration:
4256
- - Added support for required services configuration
4257
- - Improved service discovery and registration
4258
- - Added typed RPC clients for service communication
4259
- - Updated middleware to run per function
4260
-
4261
- - Updated dependencies [3261090]
4262
- - Updated dependencies [7c592b8]
4263
- - Updated dependencies [30a082f]
4264
- - @pikku/core@0.8.1
4265
-
4266
- ## 0.8.0
4267
-
4268
- ### Major Features
4269
-
4270
- - **Model Context Protocol (MCP) Analysis**: Added comprehensive MCP endpoint analysis
4271
- - **Queue Worker Analysis**: Added queue analysis
4272
- - **Enhanced Service Analysis**: Added service destructuring analysis for better code generation and type safety
4273
-
4274
- ## 0.7.7
4275
-
4276
- ### Patch Changes
4277
-
4278
- - 8b4f52e: refactor: moving schemas in channels to functions
4279
- - Updated dependencies [8b4f52e]
4280
- - Updated dependencies [8b4f52e]
4281
- - Updated dependencies [1d70184]
4282
- - @pikku/core@0.7.8
4283
-
4284
- ## 0.7.6
4285
-
4286
- ### Patch Changes
4287
-
4288
- - faa1369: refactor: moving function imports into pikku-fun.gen file
4289
-
4290
- ## 0.7.5
4291
-
4292
- ### Patch Changes
4293
-
4294
- - c5e724c: fix: rerelease as previous publish is missing something
4295
-
4296
- ## 0.7.4
4297
-
4298
- ### Patch Changes
4299
-
4300
- - 598588f: fix: generating output schemas from function meta
4301
- - Updated dependencies [598588f]
4302
- - @pikku/core@0.7.4
4303
-
4304
- ## 0.7.3
4305
-
4306
- ### Patch Changes
4307
-
4308
- - 534fdef: feat: adding rpc (locally for now)
4309
- - Updated dependencies [534fdef]
4310
- - @pikku/core@0.7.3
4311
-
4312
- ## 0.7.2
4313
-
4314
- ### Patch Changes
4315
-
4316
- - 7acd53a: fix: ignore return type if it's void
4317
- - Updated dependencies [bb59874]
4318
- - @pikku/core@0.7.2
4319
-
4320
- ## 0.7.1
4321
-
4322
- ### Patch Changes
4323
-
4324
- - ebfb786: fix: only inspect function calls with pikku\*func in name
4325
-
4326
- ## 0.7.0
4327
-
4328
- This has changed significantly. The inspector now finds all functions and then links them to events.
4329
-
4330
- This means we can now get:
4331
-
4332
- - RPCs out of the box
4333
- - Schemas are per function, not event
4334
- - Supports inline functions, external functions, anonymous functions
4335
-
4336
- ## 0.6.4
4337
-
4338
- ### Patch Changes
4339
-
4340
- - 60b2265: refactor: supporting request and response objects
4341
- - Updated dependencies [60b2265]
4342
- - @pikku/core@0.6.22
4343
-
4344
- ## 0.6.3
4345
-
4346
- ### Patch Changes
4347
-
4348
- - c1d8381: feat: adding filtering by tags to minimize produced payload
4349
- - ee5c874: feat: moving towards using middleware for http and channels
4350
- - Updated dependencies [c1d8381]
4351
- - Updated dependencies [ee5c874]
4352
- - @pikku/core@0.6.14
4353
-
4354
- ## 0.6.2
4355
-
4356
- ### Patch Changes
4357
-
4358
- - a40a508: fix: Fixing some generation bugs and other minors
4359
- - Updated dependencies [a40a508]
4360
- - @pikku/core@0.6.5
4361
-
4362
- ## 0.6.1
4363
-
4364
- ### Patch Changes
4365
-
4366
- - f26880f: feat: extracting inspector and adding unique type references
4367
- - Updated dependencies [f26880f]
4368
- - @pikku/core@0.6.4