@pikku/core 0.12.64 → 0.12.67
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.
- package/CHANGELOG.md +311 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/permissions.d.ts +12 -4
- package/dist/permissions.js +11 -32
- package/dist/scopes.d.ts +14 -0
- package/dist/scopes.js +39 -8
- package/dist/testing/service-tests.js +37 -0
- package/dist/types/core.types.d.ts +22 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +64 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +103 -5
- package/dist/wirings/ai-agent/ai-agent-runner.js +5 -1
- package/dist/wirings/ai-agent/ai-agent-stream.js +28 -7
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +29 -1
- package/dist/wirings/ai-agent/index.d.ts +1 -1
- package/dist/wirings/ai-agent/index.js +1 -1
- package/dist/wirings/ai-agent/voice-input.js +3 -3
- package/dist/wirings/cli/cli-runner.js +3 -0
- package/dist/wirings/cli/command-parser.d.ts +2 -0
- package/dist/wirings/cli/command-parser.js +59 -2
- package/dist/wirings/credential/credential.types.d.ts +14 -0
- package/dist/wirings/credential/validate-credential-definitions.js +1 -0
- package/dist/wirings/gateway/gateway-runner.js +100 -50
- package/dist/wirings/gateway/gateway.types.d.ts +8 -5
- package/dist/wirings/http/http.types.d.ts +3 -3
- package/dist/wirings/scope/validate-scope-definitions.d.ts +8 -0
- package/dist/wirings/scope/validate-scope-definitions.js +16 -1
- package/dist/wirings/secret/secret.types.d.ts +14 -0
- package/dist/wirings/secret/validate-secret-definitions.js +2 -0
- package/dist/wirings/variable/validate-variable-definitions.js +2 -0
- package/dist/wirings/variable/variable.types.d.ts +14 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +36 -6
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +16 -0
- package/dist/wirings/workflow/workflow.types.d.ts +0 -2
- package/package.json +2 -1
- package/src/index.ts +2 -1
- package/src/permissions.test.ts +14 -8
- package/src/permissions.ts +14 -36
- package/src/scopes.test.ts +37 -1
- package/src/scopes.ts +48 -9
- package/src/testing/service-tests.ts +49 -0
- package/src/types/core.types.ts +23 -0
- package/src/wirings/ai-agent/ai-agent-authorization.test.ts +204 -0
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +175 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +132 -5
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +207 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +7 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +103 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +38 -6
- package/src/wirings/ai-agent/ai-agent.types.ts +29 -0
- package/src/wirings/ai-agent/index.ts +4 -0
- package/src/wirings/ai-agent/voice-input.test.ts +90 -0
- package/src/wirings/ai-agent/voice-input.ts +8 -10
- package/src/wirings/cli/cli-runner.ts +4 -0
- package/src/wirings/cli/command-parser.test.ts +130 -0
- package/src/wirings/cli/command-parser.ts +80 -2
- package/src/wirings/credential/credential.types.ts +14 -0
- package/src/wirings/credential/validate-credential-definitions.ts +1 -0
- package/src/wirings/gateway/gateway-authorization.test.ts +444 -0
- package/src/wirings/gateway/gateway-runner.ts +114 -68
- package/src/wirings/gateway/gateway.types.ts +7 -9
- package/src/wirings/http/http.types.ts +6 -4
- package/src/wirings/scope/scope.test.ts +25 -0
- package/src/wirings/scope/validate-scope-definitions.ts +16 -1
- package/src/wirings/secret/secret.types.ts +14 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -0
- package/src/wirings/variable/validate-variable-definitions.ts +2 -0
- package/src/wirings/variable/variable.types.ts +14 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +36 -6
- package/src/wirings/workflow/pikku-workflow-service.ts +36 -0
- package/src/wirings/workflow/workflow-on-error.test.ts +154 -0
- package/src/wirings/workflow/workflow.types.ts +0 -2
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,314 @@
|
|
|
1
|
+
## 0.12.67
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- ae4f59a: Gate admin capabilities on scopes, and scaffold user management
|
|
6
|
+
|
|
7
|
+
Admin capabilities were gated on `user.role === 'admin'` — a single text column
|
|
8
|
+
meaning "can do everything". Impersonating a user, rebinding a shared
|
|
9
|
+
credential and reading the user directory are distinct capabilities that one
|
|
10
|
+
user can hold independently, so they are now scopes on an `admin` tree:
|
|
11
|
+
|
|
12
|
+
| Gate | Scope |
|
|
13
|
+
| -------------------------------------- | ------------------------ |
|
|
14
|
+
| impersonation | `admin:impersonate` |
|
|
15
|
+
| `credentialOAuth`'s `canLinkSingleton` | `admin:credentials:link` |
|
|
16
|
+
| reading the user directory | `admin:users:list` |
|
|
17
|
+
| creating a user out of band | `admin:users:create` |
|
|
18
|
+
| ban / unban | `admin:users:ban` |
|
|
19
|
+
| delete a user | `admin:users:remove` |
|
|
20
|
+
| revoke a user's sessions | `admin:users:sessions` |
|
|
21
|
+
| set a user's password | `admin:users:password` |
|
|
22
|
+
|
|
23
|
+
Holding the bare `admin` scope satisfies all of them via pikku's existing
|
|
24
|
+
parent-grant rule, so it is a one-for-one replacement for the old role.
|
|
25
|
+
|
|
26
|
+
better-auth's `admin()` plugin is still what implements ban, delete,
|
|
27
|
+
session-revocation and set-password, so it stays. Its `user.role` column is no
|
|
28
|
+
longer something pikku grants: it is _projected_ from the scope store when a
|
|
29
|
+
session is built, and only from the scopes whose capability better-auth's own
|
|
30
|
+
endpoints gate on the caller's role. Someone granted `admin:users:list` can read
|
|
31
|
+
the directory — which goes straight to the auth adapter — without gaining the
|
|
32
|
+
power to ban, and revoking a scope demotes the role on the next sign-in. Scopes
|
|
33
|
+
remain the single source of truth.
|
|
34
|
+
|
|
35
|
+
New `scaffold.userAdmin` in `pikku.config.json` generates the whole set —
|
|
36
|
+
`pikkuAdminListUsers`, `pikkuAdminCreateUser`, `pikkuAdminSetUserBanned`,
|
|
37
|
+
`pikkuAdminRemoveUser`, `pikkuAdminRevokeUserSessions` and
|
|
38
|
+
`pikkuAdminSetUserPassword` — into your project. Listing or banning a user is
|
|
39
|
+
ordinary application behaviour and must not require installing the console.
|
|
40
|
+
Codegen fails with an actionable error if better-auth is wired without
|
|
41
|
+
`admin()`. The console's Users page calls these same functions, showing each
|
|
42
|
+
action only where the caller holds its scope.
|
|
43
|
+
|
|
44
|
+
Every scaffold now emits a directory named for its domain — `scaffold/admin/`,
|
|
45
|
+
`scaffold/rpc/`, `scaffold/agent/`, `scaffold/auth/`, `scaffold/console/`,
|
|
46
|
+
`scaffold/graph/`, `scaffold/realtime/`, `scaffold/scenarios/`,
|
|
47
|
+
`scaffold/webhook/`, `scaffold/workflow/` — holding its wiring file beside a
|
|
48
|
+
`*.schemas.gen.ts` sibling, and every generated payload is a zod schema instead
|
|
49
|
+
of a TypeScript generic. The schemas have to stand alone: the inspector reads a
|
|
50
|
+
zod schema by importing the module that declares it, which it cannot do for a
|
|
51
|
+
wiring file whose relative pikku-types import per-unit deploy codegen rewrites.
|
|
52
|
+
|
|
53
|
+
Resolving a schema by reference rather than by name also fixes the agent HTTP
|
|
54
|
+
surface. `agentCaller` and `agentStreamCaller` take the same payload but had to
|
|
55
|
+
repeat the type literal verbatim in each generic position, because the extractor
|
|
56
|
+
synthesised the schema name from the _function_ name and so recorded an
|
|
57
|
+
`inputSchemaName` with no schema behind it whenever the two shared a named
|
|
58
|
+
alias — every agent call through that alias failed with `MissingSchemaError`.
|
|
59
|
+
One `AgentCall` schema now backs both.
|
|
60
|
+
|
|
61
|
+
Where a payload's shape belongs to `@pikku/core` (`WorkflowRunStatus`,
|
|
62
|
+
`FunctionCoverageReport`, `StubCall[]`) the generated function carries no
|
|
63
|
+
`output` schema and the inspector infers it from the handler's return type;
|
|
64
|
+
re-declaring a core type in zod would be a second definition free to drift.
|
|
65
|
+
|
|
66
|
+
Upgrading rewrites the layout in place: codegen prunes the pre-directory copy of
|
|
67
|
+
each scaffold file before it inspects the source tree, since the old flat file
|
|
68
|
+
still wires the same routes and leaving it behind would wire everything twice.
|
|
69
|
+
|
|
70
|
+
`@pikku/core` gains `hasScopes(required, held)`, the non-throwing counterpart to
|
|
71
|
+
`verifyScopes`, and declares `auth` on `CoreSingletonServices` — the auth
|
|
72
|
+
instance the generated `pikkuServices` wrapper already injected but never typed.
|
|
73
|
+
A scope root declared twice (an addon and its host both contributing the same
|
|
74
|
+
`admin` tree) now flattens to one entry per id instead of emitting it twice.
|
|
75
|
+
|
|
76
|
+
BREAKING: there is no role fallback for the scope-gated capabilities. An app
|
|
77
|
+
that relied on the old default must register a `ScopeService` and grant `admin`
|
|
78
|
+
(or a narrower `admin:*` scope). Every gate fails closed and warns when no
|
|
79
|
+
`ScopeService` is registered. `delegatedAuth`'s `defaultRole`/`mapRole` now
|
|
80
|
+
grant a pikku role through the `ScopeService` instead of writing better-auth's
|
|
81
|
+
`role` column, and the `credentialOAuth` platform user no longer sets `banned`.
|
|
82
|
+
|
|
83
|
+
BREAKING: the console reads its user directory over the scaffolded
|
|
84
|
+
`pikkuAdminListUsers` RPC (gated on `admin:users:list`, backed by better-auth's
|
|
85
|
+
`$context.adapter`) instead of `client.admin.listUsers`, and
|
|
86
|
+
`UsersTableUser`/`UsersTableLabels` no longer carry `role` — there is no role
|
|
87
|
+
column to render. `@pikku/addon-console` no longer ships a `console:listUsers`
|
|
88
|
+
function: user management is not the console's job, so a host that wants the
|
|
89
|
+
Users page must enable `scaffold.userAdmin`.
|
|
90
|
+
|
|
91
|
+
## 0.12.66
|
|
92
|
+
|
|
93
|
+
### Patch Changes
|
|
94
|
+
|
|
95
|
+
- 5f19016: Widen the generated agent HTTP surface, and guard attachment downloads against SSRF.
|
|
96
|
+
|
|
97
|
+
`agentCaller` and `agentStreamCaller` declared only `message`, `threadId` and
|
|
98
|
+
`resourceId` (plus `context` on the stream route), so `attachments`, `model`,
|
|
99
|
+
`temperature` — all accepted by `AIAgentInput` — were unreachable over the
|
|
100
|
+
shipped HTTP contract. No deployed app could send an attachment or a per-request
|
|
101
|
+
model override. Both callers now share an `AgentCallerInput` type covering every
|
|
102
|
+
optional field and forward each one to the RPC.
|
|
103
|
+
|
|
104
|
+
Both callers declare that shape **inline** in the generic position rather than
|
|
105
|
+
behind a shared named alias: the schema extractor only reads type literals there
|
|
106
|
+
and synthesises the schema name from the function name. Behind an alias it
|
|
107
|
+
records an `inputSchemaName` with no schema generated for it, and every agent
|
|
108
|
+
HTTP call then fails at runtime with `MissingSchemaError`.
|
|
109
|
+
|
|
110
|
+
Widening that surface makes caller-supplied attachment URLs reachable, which is
|
|
111
|
+
an SSRF vector: the AI SDK downloads attachment URLs **server-side** whenever the
|
|
112
|
+
model cannot consume them natively, using an unguarded `fetch`. A caller could
|
|
113
|
+
point an attachment at the cloud metadata endpoint or another internal host and
|
|
114
|
+
have the response relayed into the model's context. `VercelAIAgentRunner` now
|
|
115
|
+
passes an `experimental_download` implementation backed by `safeFetch` (which
|
|
116
|
+
refuses private/internal hosts and non-HTTP schemes, and re-validates every
|
|
117
|
+
redirect hop) to both `streamText` and `generateText`. URLs the model supports
|
|
118
|
+
natively are passed through untouched, so the provider still fetches those
|
|
119
|
+
itself.
|
|
120
|
+
|
|
121
|
+
The runner takes an optional `allowedAttachmentHosts` allowlist, carried across
|
|
122
|
+
`withApiKey`. `safeFetch` is now exported from `@pikku/core/safe-fetch`.
|
|
123
|
+
|
|
124
|
+
- 78e4778: Stop a failed message persist during an agent stream from killing the process.
|
|
125
|
+
|
|
126
|
+
The persisting channel flushes from inside `send`, which is synchronous and so cannot await the flush. Any rejection — a dropped storage connection, or a model reusing a `toolCallId`, which is a primary key in AI storage — escaped as an unhandled rejection and took the whole server down. Persistence from `send` is now best-effort and logged; the awaited `flush()` on the suspend paths still surfaces failures to its caller.
|
|
127
|
+
|
|
128
|
+
- 4324652: Scope AI agent thread reads to the calling session.
|
|
129
|
+
|
|
130
|
+
The generated thread-management functions (`getAgentThreads`,
|
|
131
|
+
`getAgentThreadMessages`, `getAgentThreadRuns`, `deleteAgentThread`) keyed purely
|
|
132
|
+
off a caller-supplied `threadId` and treated `resourceId` as an optional filter,
|
|
133
|
+
so omitting it enumerated every tenant's threads.
|
|
134
|
+
- `listThreads` gains an `owners` **authorization constraint** (distinct from the
|
|
135
|
+
`resourceId` filter): an empty array matches nothing, and it is always derived
|
|
136
|
+
from the session, never from input. Implemented across the Kysely, Redis and
|
|
137
|
+
MongoDB agent run services, with LIKE/regex metacharacter escaping so an owner
|
|
138
|
+
id containing `_` or `%` cannot match a foreign owner.
|
|
139
|
+
- The three `threadId`-keyed functions are now guarded by an `isThreadOwner`
|
|
140
|
+
`pikkuPermission` rather than an in-body check. A thread that does not exist is
|
|
141
|
+
denied rather than 404'd, so it is indistinguishable from one owned by someone
|
|
142
|
+
else.
|
|
143
|
+
- New `@pikku/core/ai-agent` helpers: `canAccessThread`, `threadOwnerConstraint`,
|
|
144
|
+
`sessionPrincipals`, `isOwnedByPrincipal`.
|
|
145
|
+
|
|
146
|
+
Services destructured by a wired function are now non-optional inside it.
|
|
147
|
+
|
|
148
|
+
The inspector already aggregated the services used by every wired `func`,
|
|
149
|
+
`permissions` and `middleware` into `RequiredSingletonServices`, but the
|
|
150
|
+
generated function types defaulted their service parameter to the raw `Services`
|
|
151
|
+
— so a service declared `foo?: Foo` still arrived as possibly-undefined, forcing
|
|
152
|
+
`if (!foo) throw new MissingServiceError(...)` guards that could never fire.
|
|
153
|
+
Generated types now expose `WiredSingletonServices` / `WiredServices`
|
|
154
|
+
(`RequiredSingletonServices & Services`) and default the `RequiredServices`
|
|
155
|
+
generic of functions, permissions, middleware, auth and approval-description
|
|
156
|
+
helpers to them. Optionality now means only what it should: "this service may
|
|
157
|
+
not be created, because nothing uses it".
|
|
158
|
+
|
|
159
|
+
- de044f8: Fix the agent tool-list permission filter failing open.
|
|
160
|
+
|
|
161
|
+
`buildToolDefs` filtered permission-gated tools by resolving `checkAuthPermissions` from a function's _metadata_ — a by-name lookup into the `misc/permissions` state that nothing ever populates. It therefore collected no predicate and returned `true`, so every auth-gated tool was offered to the model regardless of session (its input schema and description leaked, and the model could attempt calls that then failed at invocation).
|
|
162
|
+
|
|
163
|
+
`checkAuthPermissions` now takes the live `CorePermissionGroup` from the function/agent config, where the `pikkuAuth` brand actually survives — matching how the agent's own gate and the function runner already resolve permissions by reference. The dead by-name lookup (`getPermissionByName`) is removed. Enforcement on invocation was never affected; this closes the exposure gap in the offered tool list.
|
|
164
|
+
|
|
165
|
+
- cd1a811: warn instead of silently ignoring unknown long CLI options
|
|
166
|
+
|
|
167
|
+
An unknown long option (`--sektion functions` or `--sektion=functions`) was parsed
|
|
168
|
+
into the options object and then silently dropped by the command's input schema —
|
|
169
|
+
the command ran with the real option at its default and produced plausible-but-wrong
|
|
170
|
+
output. Unknown long options are still accepted (forward compatibility is preserved),
|
|
171
|
+
but the parser now records a warning that the runner prints to stderr, e.g.
|
|
172
|
+
`Warning: Unknown option: --sektion (ignored) Did you mean --section?`.
|
|
173
|
+
|
|
174
|
+
- 19fa6f0: Fix `HTTPRouteConfig` and `HTTPRoutesGroupConfig`'s default `PikkuPermission`/`PikkuMiddleware` type parameters under-specifying their own generic arguments (e.g. `CorePikkuPermission<any>` instead of `CorePikkuPermission<any, any, any>`). The missing arguments silently fell back to `CorePikkuPermission`'s own defaults (`CoreServices`, with `schema` optional) instead of `any`, so a project whose generated services type guarantees `schema` is always present (any project using `WiredServices`-style non-optional services) failed to type-check against `defineHTTPRoutes`/`wireHTTPRoutes` with a misleading `index signature` error.
|
|
175
|
+
- b501612: Enforce authorization consistently across `pikku*` primitives.
|
|
176
|
+
- `pikkuAIAgent` now enforces `permissions` (previously accepted but never
|
|
177
|
+
checked) and gains `auth` and `scopes`. Scopes are checked before permissions.
|
|
178
|
+
`auth` defaults to `false`, matching `pikkuSessionlessFunc`, since agents are
|
|
179
|
+
typically invoked from an already-authenticated function or from sessionless
|
|
180
|
+
contexts such as crons and queue workers.
|
|
181
|
+
- `pikkuWorkflowFunc` / `pikkuWorkflowComplexFunc` schema config gains `auth`
|
|
182
|
+
and `scopes` alongside `permissions`.
|
|
183
|
+
- `pikkuScenario` no longer accepts `auth`, `scopes`, or `permissions` —
|
|
184
|
+
scenarios drive the app as actors and authorize per step.
|
|
185
|
+
- `wireGateway` no longer accepts `permissions`. A gateway proxies to an agent,
|
|
186
|
+
so access is governed by normal auth plus the target agent's own rules.
|
|
187
|
+
- Removed the dead `permissions` field from `CoreWorkflow`, which was never read.
|
|
188
|
+
|
|
189
|
+
Closed two paths that reached user code without authorization:
|
|
190
|
+
- Gateway handlers were invoked directly, so a handler's own `auth`, `scopes`
|
|
191
|
+
and `permissions` were never evaluated. Webhook, websocket and listener
|
|
192
|
+
gateways now invoke the handler through the function runner. Handlers are
|
|
193
|
+
sessionless by default (inbound gateway traffic is platform-authenticated by
|
|
194
|
+
the adapter, not session-bearing); declare `auth: true` to require a session.
|
|
195
|
+
A gateway's own `auth` field is now honoured too — it was previously ignored.
|
|
196
|
+
Gateway middleware runs before the gate, so `wire.setSession()` in gateway
|
|
197
|
+
middleware — the idiomatic way to map a verified platform sender to a user —
|
|
198
|
+
is visible to the handler's `auth` and `scopes`.
|
|
199
|
+
- Resuming a suspended agent run (`resumeAIAgentSync`, `resumeAIAgent`) checked
|
|
200
|
+
run ownership but never re-ran the agent's own gate, so a scope or permission
|
|
201
|
+
revoked while a run was suspended did not prevent the caller from resuming it
|
|
202
|
+
and approving its pending tool calls. Both now re-run `assertAgentAuthorized`
|
|
203
|
+
before any state is mutated.
|
|
204
|
+
|
|
205
|
+
- eb37b1e: Fix `voiceInput` middleware losing the runner receiver: it grabbed
|
|
206
|
+
`aiAgentRunner.transcribe` as a bare method reference, so calling it left `this`
|
|
207
|
+
undefined and threw `Cannot read properties of undefined (reading 'getModel')`
|
|
208
|
+
on the first audio attachment. It now calls `aiAgentRunner.transcribe(...)`
|
|
209
|
+
directly, preserving the receiver.
|
|
210
|
+
|
|
211
|
+
## 0.12.65
|
|
212
|
+
|
|
213
|
+
### Patch Changes
|
|
214
|
+
|
|
215
|
+
- 1a86d3f: Fix a fanout collapsing into a single step, and preserve graph node config.
|
|
216
|
+
- A fanout took its `stepName` from the first step of its body. Node ids _are_
|
|
217
|
+
step names, so the loop and that step got the same id and the step overwrote
|
|
218
|
+
the loop: `await Promise.all(users.map(...))` rendered as one plain call, and
|
|
219
|
+
everything after the loop became unreachable. A fanout is not itself a cached
|
|
220
|
+
step, so it no longer borrows a name.
|
|
221
|
+
- A `workflow.sleep` or `workflow.suspend` inside a fanout body was dropped at
|
|
222
|
+
extraction — `FanoutStepMeta.body` was typed RPC-only. It now admits sleep and
|
|
223
|
+
suspend, and the regenerated body emits them.
|
|
224
|
+
- Regenerating a `pikkuWorkflowGraph` dropped `onError`, `retries` and
|
|
225
|
+
`retryDelay` from every node, and graph-level `notes`. All four are honoured
|
|
226
|
+
at runtime, so the round trip silently changed behaviour.
|
|
227
|
+
|
|
228
|
+
- 1a86d3f: Support multi-step fanout bodies in DSL workflows.
|
|
229
|
+
|
|
230
|
+
A `Promise.all(array.map(...))` (or `for...of`) body containing more than one
|
|
231
|
+
`workflow.do` call previously extracted only a single step: `const`-captured
|
|
232
|
+
steps were skipped entirely by the parallel extractor, so a body like
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
await Promise.all(
|
|
236
|
+
users.map(async (u) => {
|
|
237
|
+
const digestData = await workflow.do('Get pipeline', 'getDigestData', {
|
|
238
|
+
userId: u.id,
|
|
239
|
+
})
|
|
240
|
+
await workflow.do('Send digest', 'sendDigestEmail', { ...digestData })
|
|
241
|
+
})
|
|
242
|
+
)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
produced a graph with `getDigestData` missing and `sendDigestEmail` referencing
|
|
246
|
+
an unregistered variable. `FanoutStepMeta.child` is replaced by
|
|
247
|
+
`FanoutStepMeta.body: RpcStepMeta[]`, holding the per-iteration steps inline in
|
|
248
|
+
the same workflow — no sub-workflow boundary. Per-iteration `const` bindings are
|
|
249
|
+
now registered so later steps in the same iteration can reference them, and the
|
|
250
|
+
sequential path no longer hard-errors on bodies with more than one step.
|
|
251
|
+
|
|
252
|
+
- 1a86d3f: Add `onError` compensation to DSL workflows.
|
|
253
|
+
|
|
254
|
+
A DSL workflow had no way to express error handling at all — `try/catch` is not
|
|
255
|
+
an allowed statement, and step options carried only `retries`/`retryDelay`. A
|
|
256
|
+
step can now name a compensation RPC:
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
await workflow.do(
|
|
260
|
+
'Charge',
|
|
261
|
+
'chargeCard',
|
|
262
|
+
{ id },
|
|
263
|
+
{
|
|
264
|
+
retries: 3,
|
|
265
|
+
onError: 'refundOrder',
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Semantics mirror a graph node's `onError` exactly: once the step's retries are
|
|
271
|
+
exhausted the handler is invoked with `{ error: { message } }` and the original
|
|
272
|
+
error is still thrown. This is compensation, not recovery — the workflow fails
|
|
273
|
+
either way. The handler runs as its own durable step, so a replay cannot
|
|
274
|
+
compensate twice, and it does not inherit `onError` itself.
|
|
275
|
+
|
|
276
|
+
The handler is materialised as a real graph node, so it is wired like any other
|
|
277
|
+
RPC and the console draws a dashed red "on error" edge to it rather than the
|
|
278
|
+
route being invisible.
|
|
279
|
+
|
|
280
|
+
- 1a86d3f: Stop silently dropping switch cases and spread returns from workflow graphs.
|
|
281
|
+
- A fall-through case (`case 'a': case 'b': ...`) recorded only the last value.
|
|
282
|
+
A run entering on `'a'` therefore appeared to match no case at all. Empty
|
|
283
|
+
clauses now carry through to the entry they fall into — the next non-empty
|
|
284
|
+
case, otherwise `default`, otherwise the switch exit.
|
|
285
|
+
- `return { ...r, extra: 1 }` produced a return node listing only `extra`, so
|
|
286
|
+
the graph claimed an output shape the workflow does not have, with no
|
|
287
|
+
diagnostic. `return r` produced no return node at all. `ReturnStepMeta` now
|
|
288
|
+
records a `spread` list, and the regenerated code emits it.
|
|
289
|
+
|
|
290
|
+
- 1a86d3f: Stop corrupting values when regenerating a workflow from its graph.
|
|
291
|
+
- A numeric `workflow.sleep('Wait', 5000)` came back as `'5000'`, and a numeric
|
|
292
|
+
`retryDelay` likewise. Durations are `string | number`; only strings are
|
|
293
|
+
quoted now.
|
|
294
|
+
- An assignment to a context variable was stored as an opaque `value`, so
|
|
295
|
+
`count = count + 1` regenerated as `count = 'count + 1'` — an expression
|
|
296
|
+
turned into a string literal. `SetStepMeta` now carries a separate
|
|
297
|
+
`expression` field (mirroring `SwitchCaseMeta`), so a string literal and a
|
|
298
|
+
code expression are no longer indistinguishable in the meta.
|
|
299
|
+
- A `next` that was not a single node id was coerced with a string cast: an
|
|
300
|
+
array became the bogus id `'a,b'` and a branch-key record became
|
|
301
|
+
`'[object Object]'`, severing every downstream node. Arrays, key-based
|
|
302
|
+
routing tables and condition lists now each render in their own shape.
|
|
303
|
+
- A `filter`/`some`/`every` node with no `outputVar` emitted
|
|
304
|
+
`const undefined = ...`, which does not parse.
|
|
305
|
+
|
|
306
|
+
- 1a86d3f: Keep a `workflow.sleep` whose duration is only known at runtime (a loop
|
|
307
|
+
variable, a field off the input). The closure evaluates it, so it is legal DSL;
|
|
308
|
+
its source text is recorded as an `expression` and emitted raw when regenerating
|
|
309
|
+
code, as a set step already does.
|
|
310
|
+
- 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.
|
|
311
|
+
|
|
1
312
|
## 0.12.64
|
|
2
313
|
|
|
3
314
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @pikku/core
|
|
3
3
|
*/
|
|
4
|
-
export type { CommonWireMeta, CoreConfig, CorePikkuMiddleware, CorePikkuMiddlewareConfig, CorePikkuMiddlewareFactory, CorePikkuMiddlewareGroup, CoreServices, CoreSingletonServices, CoreUserSession, CreateConfig, ServerLifecycle, FunctionMeta, FunctionRuntimeMeta, FunctionServicesMeta, FunctionWiresMeta, FunctionsMeta, FunctionsRuntimeMeta, JSONPrimitive, JSONValue, MakeRequired, MiddlewareMetadata, MiddlewarePriority, PermissionMetadata, PickOptional, PickRequired, PikkuAIMiddlewareHooks, PikkuWire, PikkuRawWire, PikkuWiringTypes, PostgresConfig, RequireAtLeastOne, SecurityAuditIssue, SecurityAuditReport, SecurityAuditSummary, SecurityAuditUpdate, SecuritySeverity, SecurityUpdateLevel, SerializedError, WireServices, } from './types/core.types.js';
|
|
4
|
+
export type { AuthInstance, CommonWireMeta, CoreConfig, CorePikkuMiddleware, CorePikkuMiddlewareConfig, CorePikkuMiddlewareFactory, CorePikkuMiddlewareGroup, CoreServices, CoreSingletonServices, CoreUserSession, CreateConfig, ServerLifecycle, FunctionMeta, FunctionRuntimeMeta, FunctionServicesMeta, FunctionWiresMeta, FunctionsMeta, FunctionsRuntimeMeta, JSONPrimitive, JSONValue, MakeRequired, MiddlewareMetadata, MiddlewarePriority, PermissionMetadata, PickOptional, PickRequired, PikkuAIMiddlewareHooks, PikkuWire, PikkuRawWire, PikkuWiringTypes, PostgresConfig, RequireAtLeastOne, SecurityAuditIssue, SecurityAuditReport, SecurityAuditSummary, SecurityAuditUpdate, SecuritySeverity, SecurityUpdateLevel, SerializedError, WireServices, } from './types/core.types.js';
|
|
5
5
|
export { pikkuAIMiddleware, pikkuChannelMiddleware, pikkuChannelMiddlewareFactory, pikkuMiddleware, pikkuMiddlewareFactory, } from './types/core.types.js';
|
|
6
6
|
export type { CorePikkuAuth, CorePikkuAuthConfig, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuPermission, CorePikkuPermissionConfig, CorePikkuPermissionFactory, CorePikkuApprovalDescription, CorePermissionGroup, ZodLike, } from './function/functions.types.js';
|
|
7
7
|
export { pikkuAuth, pikkuPermission, pikkuPermissionFactory, pikkuApprovalDescription, } from './function/functions.types.js';
|
|
@@ -50,7 +50,7 @@ export type { WireRemoteAddonConfig, RemoteAddonAuth, } from './wirings/rpc/wire
|
|
|
50
50
|
export type { PikkuPackageState } from './types/state.types.js';
|
|
51
51
|
export { runMiddleware, addTagMiddleware, addTagMiddleware as addMiddleware, addGlobalMiddleware, } from './middleware-runner.js';
|
|
52
52
|
export { addGlobalPermission, checkAuthPermissions } from './permissions.js';
|
|
53
|
-
export { verifyScopes } from './scopes.js';
|
|
53
|
+
export { hasScopes, verifyScopes } from './scopes.js';
|
|
54
54
|
export { isSerializable, stopSingletonServices, pikkuServerLifecycle, } from './utils.js';
|
|
55
55
|
export { getSingletonServices, getCreateWireServices, setSingletonServices, } from './pikku-state.js';
|
|
56
56
|
export { clearPikkuRuntimeState } from './test-utils.js';
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export { wireAddon } from './wirings/rpc/wire-addon.js';
|
|
|
18
18
|
export { wireRemoteAddon } from './wirings/rpc/wire-remote-addon.js';
|
|
19
19
|
export { runMiddleware, addTagMiddleware, addTagMiddleware as addMiddleware, addGlobalMiddleware, } from './middleware-runner.js';
|
|
20
20
|
export { addGlobalPermission, checkAuthPermissions } from './permissions.js';
|
|
21
|
-
export { verifyScopes } from './scopes.js';
|
|
21
|
+
export { hasScopes, verifyScopes } from './scopes.js';
|
|
22
22
|
export { isSerializable, stopSingletonServices, pikkuServerLifecycle, } from './utils.js';
|
|
23
23
|
export { getSingletonServices, getCreateWireServices, setSingletonServices, } from './pikku-state.js';
|
|
24
24
|
export { clearPikkuRuntimeState } from './test-utils.js';
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CoreServices, CoreUserSession,
|
|
1
|
+
import type { CoreServices, CoreUserSession, PikkuWire } from './types/core.types.js';
|
|
2
2
|
import type { CorePermissionGroup, CorePikkuPermission } from './function/functions.types.js';
|
|
3
3
|
export declare const clearPermissionsCache: () => void;
|
|
4
4
|
/**
|
|
@@ -36,12 +36,14 @@ export declare const addTagPermission: (_tag: string, _permissions: CorePermissi
|
|
|
36
36
|
* A passing global requirement never contributes to the function gate, so a
|
|
37
37
|
* broad global like `signedIn` can't satisfy an admin-only function.
|
|
38
38
|
*/
|
|
39
|
-
export declare const runPermissions: ({ funcPermissions, services, wire, data, packageName, }: {
|
|
39
|
+
export declare const runPermissions: ({ funcPermissions, services, wire, data, packageName, label, }: {
|
|
40
40
|
funcPermissions?: CorePermissionGroup | CorePikkuPermission[];
|
|
41
41
|
services: CoreServices;
|
|
42
42
|
wire: PikkuWire<any, never, any, CoreUserSession, never, never, never>;
|
|
43
43
|
data: any;
|
|
44
44
|
packageName?: string | null;
|
|
45
|
+
/** What the non-global gate is called in debug logs, e.g. 'function', 'agent'. */
|
|
46
|
+
label?: string;
|
|
45
47
|
}) => Promise<void>;
|
|
46
48
|
/**
|
|
47
49
|
* Checks whether a session passes the auth checks (pikkuAuth only) for a
|
|
@@ -49,10 +51,16 @@ export declare const runPermissions: ({ funcPermissions, services, wire, data, p
|
|
|
49
51
|
* request data which isn't available at filter time. Global auth requirements
|
|
50
52
|
* are included so a filtered list honours app-wide auth.
|
|
51
53
|
*
|
|
52
|
-
*
|
|
54
|
+
* `funcPermissions` is the live {@link CorePermissionGroup} from the function or
|
|
55
|
+
* agent config, not the metadata form: the `pikkuAuth` brand only survives on
|
|
56
|
+
* the actual predicate objects, and the by-name registry those metadata entries
|
|
57
|
+
* would resolve against is never populated. Passing metadata here would silently
|
|
58
|
+
* collect nothing and let every gated tool through.
|
|
59
|
+
*
|
|
60
|
+
* @param funcPermissions - The live permission group from the func/agent config
|
|
53
61
|
* @param session - The user's session
|
|
54
62
|
* @param services - Singleton services
|
|
55
63
|
* @param packageName - Optional package namespace
|
|
56
64
|
* @returns true if the session passes the auth checks (or no auth checks exist)
|
|
57
65
|
*/
|
|
58
|
-
export declare const checkAuthPermissions: (funcPermissions:
|
|
66
|
+
export declare const checkAuthPermissions: (funcPermissions: CorePermissionGroup | undefined, session: CoreUserSession, services: CoreServices, packageName?: string | null) => Promise<boolean>;
|
package/dist/permissions.js
CHANGED
|
@@ -40,26 +40,6 @@ const verifyPermissions = async (permissions, services, data, wire) => {
|
|
|
40
40
|
}
|
|
41
41
|
return false;
|
|
42
42
|
};
|
|
43
|
-
/**
|
|
44
|
-
* Retrieves a registered permission function by its name.
|
|
45
|
-
*
|
|
46
|
-
* This function looks up permissions that was registered with registerPermission.
|
|
47
|
-
* It's used internally by the framework to resolve permission references in metadata.
|
|
48
|
-
*
|
|
49
|
-
* @param {string} name - The unique name (pikkuFuncId) of the permission function.
|
|
50
|
-
* @param {string | null} packageName - Optional package namespace.
|
|
51
|
-
* @returns {CorePikkuPermission | undefined} The permission function, or undefined if not found.
|
|
52
|
-
*
|
|
53
|
-
* @internal
|
|
54
|
-
*/
|
|
55
|
-
const getPermissionByName = (name, packageName = null) => {
|
|
56
|
-
const permissionStore = pikkuState(packageName, 'misc', 'permissions');
|
|
57
|
-
const permission = permissionStore[name];
|
|
58
|
-
if (Array.isArray(permission) && permission.length === 1) {
|
|
59
|
-
return permission[0];
|
|
60
|
-
}
|
|
61
|
-
return undefined;
|
|
62
|
-
};
|
|
63
43
|
const globalPermissionsCache = {};
|
|
64
44
|
export const clearPermissionsCache = () => {
|
|
65
45
|
for (const key of Object.keys(globalPermissionsCache)) {
|
|
@@ -131,7 +111,7 @@ const asGroup = (entry) => typeof entry === 'function' ? { permission: entry } :
|
|
|
131
111
|
* A passing global requirement never contributes to the function gate, so a
|
|
132
112
|
* broad global like `signedIn` can't satisfy an admin-only function.
|
|
133
113
|
*/
|
|
134
|
-
export const runPermissions = async ({ funcPermissions, services, wire, data, packageName = null, }) => {
|
|
114
|
+
export const runPermissions = async ({ funcPermissions, services, wire, data, packageName = null, label = 'function', }) => {
|
|
135
115
|
const globals = resolveGlobalPermissions(packageName);
|
|
136
116
|
for (const entry of globals) {
|
|
137
117
|
if (!(await verifyPermissions(asGroup(entry), services, data, wire))) {
|
|
@@ -145,7 +125,7 @@ export const runPermissions = async ({ funcPermissions, services, wire, data, pa
|
|
|
145
125
|
: funcPermissions;
|
|
146
126
|
if (group && Object.keys(group).length > 0) {
|
|
147
127
|
if (!(await verifyPermissions(group, services, data, wire))) {
|
|
148
|
-
services.logger.debug(
|
|
128
|
+
services.logger.debug(`Permission denied - ${label} permission`);
|
|
149
129
|
throw new ForbiddenError('Permission denied');
|
|
150
130
|
}
|
|
151
131
|
}
|
|
@@ -157,7 +137,13 @@ export const runPermissions = async ({ funcPermissions, services, wire, data, pa
|
|
|
157
137
|
* request data which isn't available at filter time. Global auth requirements
|
|
158
138
|
* are included so a filtered list honours app-wide auth.
|
|
159
139
|
*
|
|
160
|
-
*
|
|
140
|
+
* `funcPermissions` is the live {@link CorePermissionGroup} from the function or
|
|
141
|
+
* agent config, not the metadata form: the `pikkuAuth` brand only survives on
|
|
142
|
+
* the actual predicate objects, and the by-name registry those metadata entries
|
|
143
|
+
* would resolve against is never populated. Passing metadata here would silently
|
|
144
|
+
* collect nothing and let every gated tool through.
|
|
145
|
+
*
|
|
146
|
+
* @param funcPermissions - The live permission group from the func/agent config
|
|
161
147
|
* @param session - The user's session
|
|
162
148
|
* @param services - Singleton services
|
|
163
149
|
* @param packageName - Optional package namespace
|
|
@@ -189,15 +175,8 @@ export const checkAuthPermissions = async (funcPermissions, session, services, p
|
|
|
189
175
|
for (const entry of resolveGlobalPermissions(packageName)) {
|
|
190
176
|
collect(entry);
|
|
191
177
|
}
|
|
192
|
-
if (funcPermissions
|
|
193
|
-
|
|
194
|
-
if (meta.type === 'wire') {
|
|
195
|
-
const permission = getPermissionByName(meta.name, packageName);
|
|
196
|
-
if (permission) {
|
|
197
|
-
collect(permission);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
178
|
+
if (funcPermissions) {
|
|
179
|
+
collect(funcPermissions);
|
|
201
180
|
}
|
|
202
181
|
// No auth permissions = allowed (only data-dependent permissions exist)
|
|
203
182
|
if (authPerms.length === 0)
|
package/dist/scopes.d.ts
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import type { CoreUserSession } from './types/core.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a set of held grants satisfies every required scope.
|
|
4
|
+
*
|
|
5
|
+
* The non-throwing counterpart to {@link verifyScopes}, for deciding rather
|
|
6
|
+
* than enforcing — an authorization gate that falls back to another check when
|
|
7
|
+
* it is not satisfied, rather than rejecting the request outright.
|
|
8
|
+
*
|
|
9
|
+
* Fails closed: an absent or empty `held` satisfies nothing. An empty
|
|
10
|
+
* `required` is satisfied by anything.
|
|
11
|
+
*
|
|
12
|
+
* @param required - Scopes to check for. Empty means no gate.
|
|
13
|
+
* @param held - The grants held, e.g. `session.scopes`. May be undefined.
|
|
14
|
+
*/
|
|
15
|
+
export declare const hasScopes: (required: readonly string[] | undefined, held: Iterable<string> | undefined) => boolean;
|
|
2
16
|
/**
|
|
3
17
|
* Verifies that a session holds every required scope, throwing on the first
|
|
4
18
|
* one it does not.
|
package/dist/scopes.js
CHANGED
|
@@ -31,6 +31,42 @@ const satisfyingGrants = (scope) => {
|
|
|
31
31
|
* `admin`.
|
|
32
32
|
*/
|
|
33
33
|
const holds = (held, scope) => satisfyingGrants(scope).some((grant) => held.has(grant));
|
|
34
|
+
/**
|
|
35
|
+
* The first required scope a set of held grants does not satisfy, or `null`
|
|
36
|
+
* when every one is satisfied.
|
|
37
|
+
*
|
|
38
|
+
* Scopes are an AND gate: every entry in `required` must be satisfied. This is
|
|
39
|
+
* deliberately distinct from `permissions`, which OR together — a scope can
|
|
40
|
+
* only ever narrow access, so adding one to a function can never widen it.
|
|
41
|
+
*
|
|
42
|
+
* Fails closed: an absent `held` satisfies nothing.
|
|
43
|
+
*/
|
|
44
|
+
const firstUnsatisfied = (required, held) => {
|
|
45
|
+
if (!required || required.length === 0) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const grants = new Set(held ?? []);
|
|
49
|
+
for (const scope of required) {
|
|
50
|
+
if (!holds(grants, scope)) {
|
|
51
|
+
return scope;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Whether a set of held grants satisfies every required scope.
|
|
58
|
+
*
|
|
59
|
+
* The non-throwing counterpart to {@link verifyScopes}, for deciding rather
|
|
60
|
+
* than enforcing — an authorization gate that falls back to another check when
|
|
61
|
+
* it is not satisfied, rather than rejecting the request outright.
|
|
62
|
+
*
|
|
63
|
+
* Fails closed: an absent or empty `held` satisfies nothing. An empty
|
|
64
|
+
* `required` is satisfied by anything.
|
|
65
|
+
*
|
|
66
|
+
* @param required - Scopes to check for. Empty means no gate.
|
|
67
|
+
* @param held - The grants held, e.g. `session.scopes`. May be undefined.
|
|
68
|
+
*/
|
|
69
|
+
export const hasScopes = (required, held) => firstUnsatisfied(required, held) === null;
|
|
34
70
|
/**
|
|
35
71
|
* Verifies that a session holds every required scope, throwing on the first
|
|
36
72
|
* one it does not.
|
|
@@ -47,13 +83,8 @@ const holds = (held, scope) => satisfyingGrants(scope).some((grant) => held.has(
|
|
|
47
83
|
* @throws {MissingScopeError} Naming the first unsatisfied scope.
|
|
48
84
|
*/
|
|
49
85
|
export const verifyScopes = (required, session) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const held = new Set(session?.scopes ?? []);
|
|
54
|
-
for (const scope of required) {
|
|
55
|
-
if (!holds(held, scope)) {
|
|
56
|
-
throw new MissingScopeError(scope);
|
|
57
|
-
}
|
|
86
|
+
const missing = firstUnsatisfied(required, session?.scopes);
|
|
87
|
+
if (missing !== null) {
|
|
88
|
+
throw new MissingScopeError(missing);
|
|
58
89
|
}
|
|
59
90
|
};
|
|
@@ -742,6 +742,43 @@ export function defineServiceTests(config) {
|
|
|
742
742
|
const threads = await agentService.listThreads();
|
|
743
743
|
assert.ok(Array.isArray(threads));
|
|
744
744
|
});
|
|
745
|
+
if (services.aiStorageService) {
|
|
746
|
+
const storageFactory = services.aiStorageService;
|
|
747
|
+
// The `owners` constraint is what keeps the generated thread-management
|
|
748
|
+
// functions from leaking across tenants: a caller may only list threads
|
|
749
|
+
// owned by one of their session principals, matching the
|
|
750
|
+
// `principal:sub-partition` composition resolveOwnerResourceId writes.
|
|
751
|
+
test('listThreads scopes to the given owners, including sub-partitions', async () => {
|
|
752
|
+
const storage = await storageFactory();
|
|
753
|
+
await storage.createThread('owner-alice');
|
|
754
|
+
await storage.createThread('owner-alice:project-1');
|
|
755
|
+
await storage.createThread('owner-bob:secret');
|
|
756
|
+
const threads = await agentService.listThreads({
|
|
757
|
+
owners: ['owner-alice'],
|
|
758
|
+
});
|
|
759
|
+
const ids = threads.map((t) => t.resourceId);
|
|
760
|
+
assert.ok(ids.includes('owner-alice'));
|
|
761
|
+
assert.ok(ids.includes('owner-alice:project-1'));
|
|
762
|
+
assert.ok(!ids.some((id) => id.startsWith('owner-bob')), "another owner's threads must not be listed");
|
|
763
|
+
});
|
|
764
|
+
test('listThreads with an owner does not match a lookalike prefix', async () => {
|
|
765
|
+
const storage = await storageFactory();
|
|
766
|
+
await storage.createThread('owner-al');
|
|
767
|
+
await storage.createThread('owner-alice-evil:p');
|
|
768
|
+
const threads = await agentService.listThreads({
|
|
769
|
+
owners: ['owner-al'],
|
|
770
|
+
});
|
|
771
|
+
const ids = threads.map((t) => t.resourceId);
|
|
772
|
+
assert.ok(ids.includes('owner-al'));
|
|
773
|
+
assert.ok(!ids.includes('owner-alice-evil:p'));
|
|
774
|
+
});
|
|
775
|
+
test('listThreads with an empty owners list returns nothing', async () => {
|
|
776
|
+
const storage = await storageFactory();
|
|
777
|
+
await storage.createThread('owner-empty-check');
|
|
778
|
+
const threads = await agentService.listThreads({ owners: [] });
|
|
779
|
+
assert.deepEqual(threads, []);
|
|
780
|
+
});
|
|
781
|
+
}
|
|
745
782
|
test('getThread returns null for missing', async () => {
|
|
746
783
|
const thread = await agentService.getThread('missing-thread');
|
|
747
784
|
assert.equal(thread, null);
|
|
@@ -209,6 +209,21 @@ export interface CoreUserSession {
|
|
|
209
209
|
*/
|
|
210
210
|
scopes?: string[];
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* The shape pikku needs from whatever auth library a project wires: something
|
|
214
|
+
* that can answer an HTTP request and expose its own endpoints as callable
|
|
215
|
+
* methods. Kept structural so core stays independent of any one auth package —
|
|
216
|
+
* `@pikku/better-auth`'s `BetterAuthInstance` is this type.
|
|
217
|
+
*/
|
|
218
|
+
export interface AuthInstance {
|
|
219
|
+
handler: (request: Request) => Promise<Response>;
|
|
220
|
+
api: Record<string, any>;
|
|
221
|
+
/**
|
|
222
|
+
* The auth library's resolved context. Optional because a hand-built instance
|
|
223
|
+
* may omit it.
|
|
224
|
+
*/
|
|
225
|
+
$context?: Promise<any>;
|
|
226
|
+
}
|
|
212
227
|
/**
|
|
213
228
|
* Interface for core singleton services provided by Pikku.
|
|
214
229
|
*/
|
|
@@ -280,6 +295,13 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
|
|
|
280
295
|
* better-auth's `mapSession`), never by the function runner.
|
|
281
296
|
*/
|
|
282
297
|
scopeService?: ScopeService;
|
|
298
|
+
/**
|
|
299
|
+
* The project's resolved auth instance, built once by the factory an auth
|
|
300
|
+
* package registers (e.g. `pikkuBetterAuth`) and injected by the generated
|
|
301
|
+
* `pikkuServices` wrapper — which is why service factories are forbidden from
|
|
302
|
+
* returning it themselves. Absent when the project wires no auth.
|
|
303
|
+
*/
|
|
304
|
+
auth?: () => Promise<AuthInstance>;
|
|
283
305
|
}
|
|
284
306
|
/**
|
|
285
307
|
* Represents different forms of wire within Pikku and the outside world.
|