@pikku/skills 0.12.9 → 0.12.10
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/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-addon/SKILL.md +3 -3
- package/skills/{pikku-ai-agent → pikku-agent}/SKILL.md +21 -21
- package/skills/pikku-ai-vercel/SKILL.md +18 -18
- package/skills/pikku-ai-voice/SKILL.md +15 -15
- package/skills/pikku-concepts/SKILL.md +5 -5
- package/skills/pikku-deploy-cloudflare/SKILL.md +9 -9
- package/skills/pikku-deploy-uws/SKILL.md +5 -2
- package/skills/pikku-deps/SKILL.md +42 -3
- package/skills/pikku-kysely/SKILL.md +68 -41
- package/skills/pikku-machine-auth/SKILL.md +10 -10
- package/skills/pikku-mcp/SKILL.md +19 -16
- package/skills/pikku-middleware/SKILL.md +14 -7
- package/skills/pikku-mongodb/SKILL.md +11 -11
- package/skills/pikku-n8n-import/references/addon-mapping.md +14 -8
- package/skills/pikku-product-second-opinion/SKILL.md +83 -73
- package/skills/pikku-rpc/SKILL.md +1 -1
- package/skills/pikku-scenario/SKILL.md +39 -31
- package/skills/pikku-software-archaeology/SKILL.md +27 -23
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +580 -102
- package/skills/pikku-versioning/SKILL.md +87 -3
- package/skills/pikku-ws/SKILL.md +5 -2
package/package.json
CHANGED
|
@@ -320,10 +320,10 @@ wireHTTPRoutes({ basePath: '/api', routes: { todos: todoRoutes } })
|
|
|
320
320
|
### Use in AI Agents
|
|
321
321
|
|
|
322
322
|
```typescript
|
|
323
|
-
import {
|
|
323
|
+
import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
|
|
324
324
|
import { ref } from '#pikku'
|
|
325
325
|
|
|
326
|
-
export const todoAgent =
|
|
326
|
+
export const todoAgent = pikkuAgent({
|
|
327
327
|
name: 'todo-agent',
|
|
328
328
|
description: 'Manages a todo list',
|
|
329
329
|
goal: 'You help users manage their todos.',
|
|
@@ -337,4 +337,4 @@ export const todoAgent = pikkuAIAgent({
|
|
|
337
337
|
})
|
|
338
338
|
```
|
|
339
339
|
|
|
340
|
-
See `pikku-
|
|
340
|
+
See `pikku-agent` — an addon function is just another `ref()` in `tools`.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: pikku-
|
|
2
|
+
name: pikku-agent
|
|
3
3
|
description: >-
|
|
4
4
|
Use when building AI agents, chatbots, or LLM-powered assistants with Pikku. Covers
|
|
5
|
-
|
|
6
|
-
invocation via rpc.agent. TRIGGER when: code uses
|
|
7
|
-
|
|
5
|
+
pikkuAgent, ref() tool registration, memory, streaming, tool approval, thread ownership, and
|
|
6
|
+
invocation via rpc.agent. TRIGGER when: code uses pikkuAgent/rpc.agent/runAgent/
|
|
7
|
+
streamAgent, user asks about AI agents, chatbots, LLM assistants, tool-calling agents, agent
|
|
8
8
|
memory/streaming, or `pikku enable agent`. DO NOT TRIGGER when: user asks about MCP tool
|
|
9
9
|
exposure (use pikku-mcp) or general function definitions (use pikku-concepts).
|
|
10
10
|
installGroups: [core]
|
|
@@ -35,15 +35,15 @@ See `pikku-concepts` for the core mental model.
|
|
|
35
35
|
|
|
36
36
|
## API Reference
|
|
37
37
|
|
|
38
|
-
### `
|
|
38
|
+
### `pikkuAgent(config)`
|
|
39
39
|
|
|
40
40
|
Import it from the generated agent types file — `#pikku` does not re-export it:
|
|
41
41
|
|
|
42
42
|
```typescript
|
|
43
|
-
import {
|
|
43
|
+
import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
|
|
44
44
|
import { ref } from '#pikku/pikku-types.gen.js'
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
pikkuAgent({
|
|
47
47
|
name: string, // Unique agent identifier
|
|
48
48
|
description: string, // What the agent does (shown in agent listings)
|
|
49
49
|
summary?: string,
|
|
@@ -67,7 +67,7 @@ pikkuAIAgent({
|
|
|
67
67
|
agentMode?: 'delegate' | 'supervise',
|
|
68
68
|
|
|
69
69
|
memory?: {
|
|
70
|
-
storage?: string, // Service name for persistence (e.g. '
|
|
70
|
+
storage?: string, // Service name for persistence (e.g. 'agentStorage')
|
|
71
71
|
vector?: string, // Vector store service name
|
|
72
72
|
embedder?: string, // Embedding service name
|
|
73
73
|
lastMessages?: number, // How many messages to retain in context
|
|
@@ -88,7 +88,7 @@ pikkuAIAgent({
|
|
|
88
88
|
|
|
89
89
|
middleware?: PikkuMiddleware[],
|
|
90
90
|
channelMiddleware?: PikkuChannelMiddleware[],
|
|
91
|
-
|
|
91
|
+
agentMiddleware?: PikkuAgentMiddlewareHooks[],
|
|
92
92
|
})
|
|
93
93
|
```
|
|
94
94
|
|
|
@@ -140,15 +140,15 @@ asking the user for identifiers it could have been handed.
|
|
|
140
140
|
}
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
`
|
|
144
|
-
this. Their third argument is `
|
|
143
|
+
`runAgent` / `streamAgent` from `@pikku/core/agent` are the layer beneath
|
|
144
|
+
this. Their third argument is `RunAgentParams` (`{ sessionService?,
|
|
145
145
|
getCredential?, anonymousOwnerResourceId? }`) — **not** `{ singletonServices }`.
|
|
146
146
|
Reach for them only outside a wired function; inside one, `rpc.agent` is the
|
|
147
147
|
supported path.
|
|
148
148
|
|
|
149
149
|
### Stream events
|
|
150
150
|
|
|
151
|
-
`rpc.agent.stream` pushes `
|
|
151
|
+
`rpc.agent.stream` pushes `AgentStreamEvent`s onto the channel:
|
|
152
152
|
|
|
153
153
|
```typescript
|
|
154
154
|
// { type: 'step-start', stepNumber }
|
|
@@ -178,10 +178,10 @@ than folding it into the parent's transcript.
|
|
|
178
178
|
### Define an Agent
|
|
179
179
|
|
|
180
180
|
```typescript
|
|
181
|
-
import {
|
|
181
|
+
import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
|
|
182
182
|
import { ref } from '#pikku/pikku-types.gen.js'
|
|
183
183
|
|
|
184
|
-
export const todoAgent =
|
|
184
|
+
export const todoAgent = pikkuAgent({
|
|
185
185
|
name: 'todo-agent',
|
|
186
186
|
description: 'Manages a todo list',
|
|
187
187
|
goal: 'You help users manage their todos. You can list, add, complete and delete them.',
|
|
@@ -192,7 +192,7 @@ export const todoAgent = pikkuAIAgent({
|
|
|
192
192
|
ref('todos:completeTodo'),
|
|
193
193
|
ref('graph:sleep'),
|
|
194
194
|
],
|
|
195
|
-
memory: { storage: '
|
|
195
|
+
memory: { storage: 'agentStorage', lastMessages: 20 },
|
|
196
196
|
maxSteps: 10,
|
|
197
197
|
toolChoice: 'auto',
|
|
198
198
|
})
|
|
@@ -216,7 +216,7 @@ tools** — with a tool present the runner falls back to free text, silently. If
|
|
|
216
216
|
you need both, split the classification into its own tool-free agent.
|
|
217
217
|
|
|
218
218
|
```typescript
|
|
219
|
-
export const structuredAgent =
|
|
219
|
+
export const structuredAgent = pikkuAgent({
|
|
220
220
|
name: 'structured-agent',
|
|
221
221
|
description: 'Classifies a message and returns a structured verdict',
|
|
222
222
|
goal: 'You classify the sentiment of the user message.',
|
|
@@ -234,14 +234,14 @@ rather than signalling that it was short-circuited.
|
|
|
234
234
|
|
|
235
235
|
```typescript
|
|
236
236
|
prepareStep: ({ stepNumber, tools }) => {
|
|
237
|
-
if (stepNumber >= 1) tools.length = 0
|
|
237
|
+
if (stepNumber >= 1) tools.length = 0 // withdraw tools after the first step
|
|
238
238
|
}
|
|
239
239
|
```
|
|
240
240
|
|
|
241
241
|
### Tool approval
|
|
242
242
|
|
|
243
243
|
A tool that should pause for a human sets `approvalRequired: true` (with an
|
|
244
|
-
optional `approvalDescription`) on the
|
|
244
|
+
optional `approvalDescription`) on the _function_, not on the agent. The run then
|
|
245
245
|
resolves with `status: 'suspended'` and `pendingApprovals`, and streaming emits
|
|
246
246
|
`approval-request`. Answer with `rpc.agent.approve(runId, approvals)`.
|
|
247
247
|
|
|
@@ -282,10 +282,10 @@ export const completeTodo = pikkuFunc({
|
|
|
282
282
|
})
|
|
283
283
|
|
|
284
284
|
// agents/todo-assistant.agent.ts
|
|
285
|
-
import {
|
|
285
|
+
import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
|
|
286
286
|
import { ref } from '#pikku/pikku-types.gen.js'
|
|
287
287
|
|
|
288
|
-
export const todoAssistant =
|
|
288
|
+
export const todoAssistant = pikkuAgent({
|
|
289
289
|
name: 'todo-assistant',
|
|
290
290
|
description: 'A helpful assistant that manages todos',
|
|
291
291
|
role: 'You are an assistant that manages a user’s todo list.',
|
|
@@ -299,7 +299,7 @@ export const todoAssistant = pikkuAIAgent({
|
|
|
299
299
|
ref('todos:createTodo'),
|
|
300
300
|
ref('todos:completeTodo'),
|
|
301
301
|
],
|
|
302
|
-
memory: { storage: '
|
|
302
|
+
memory: { storage: 'agentStorage', lastMessages: 20 },
|
|
303
303
|
maxSteps: 5,
|
|
304
304
|
temperature: 0.7,
|
|
305
305
|
})
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
name: pikku-ai-vercel
|
|
3
3
|
description: >-
|
|
4
4
|
Use when setting up AI agent execution with the Vercel AI SDK in a Pikku app. Covers
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@pikku/ai-vercel. DO NOT TRIGGER when: user asks about AI agent wiring (use pikku-
|
|
5
|
+
VercelAgentRunner for streaming and non-streaming AI agent steps. TRIGGER when: code uses
|
|
6
|
+
VercelAgentRunner, user asks about Vercel AI SDK integration, AI agent runners, or
|
|
7
|
+
@pikku/ai-vercel. DO NOT TRIGGER when: user asks about AI agent wiring (use pikku-agent) or
|
|
8
8
|
voice I/O (use pikku-ai-voice).
|
|
9
9
|
installGroups: [core]
|
|
10
10
|
---
|
|
@@ -21,7 +21,7 @@ Use this skill as an execution checklist, not reference material.
|
|
|
21
21
|
4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
|
|
22
22
|
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
23
23
|
|
|
24
|
-
`@pikku/ai-vercel` provides an AI agent runner backed by the [Vercel AI SDK](https://sdk.vercel.ai/). Implements `
|
|
24
|
+
`@pikku/ai-vercel` provides an AI agent runner backed by the [Vercel AI SDK](https://sdk.vercel.ai/). Implements `AgentRunnerService` from `@pikku/core`.
|
|
25
25
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
@@ -31,12 +31,12 @@ yarn add @pikku/ai-vercel ai @ai-sdk/openai # or any AI SDK provider
|
|
|
31
31
|
|
|
32
32
|
## API Reference
|
|
33
33
|
|
|
34
|
-
### `
|
|
34
|
+
### `VercelAgentRunner`
|
|
35
35
|
|
|
36
36
|
```typescript
|
|
37
|
-
import {
|
|
37
|
+
import { VercelAgentRunner } from '@pikku/ai-vercel'
|
|
38
38
|
|
|
39
|
-
const runner = new
|
|
39
|
+
const runner = new VercelAgentRunner(
|
|
40
40
|
providers: Record<string, any>, // provider name → AI SDK provider
|
|
41
41
|
providerFactory?: (apiKey: string) => Record<string, any>,
|
|
42
42
|
allowedAttachmentHosts?: string[]
|
|
@@ -45,8 +45,8 @@ const runner = new VercelAIAgentRunner(
|
|
|
45
45
|
|
|
46
46
|
**Methods:**
|
|
47
47
|
|
|
48
|
-
- `stream(params:
|
|
49
|
-
- `run(params:
|
|
48
|
+
- `stream(params: AgentRunnerParams, channel: AgentStreamChannel): Promise<AgentStepResult>` — Stream AI responses with tool calls
|
|
49
|
+
- `run(params: AgentRunnerParams): Promise<AgentStepResult>` — Execute a single AI step (non-streaming)
|
|
50
50
|
- `transcribe({ model, audio, … })` / `generateSpeech({ model, text, voice, … })` — what `voiceInput`/`voiceOutput` call; see `pikku-ai-voice`
|
|
51
51
|
- `generateImage`, `embed`, `embedMany`, `rerank` — the remaining AI SDK surfaces
|
|
52
52
|
- `withApiKey(apiKey)` — returns a **new** runner built from `providerFactory`; returns `this` unchanged when no factory was supplied or the key is blank. This is the per-user-credential path
|
|
@@ -75,7 +75,7 @@ gateway-routed providers after construction.
|
|
|
75
75
|
### Basic Setup
|
|
76
76
|
|
|
77
77
|
```typescript
|
|
78
|
-
import {
|
|
78
|
+
import { VercelAgentRunner } from '@pikku/ai-vercel'
|
|
79
79
|
import { createOpenAI } from '@ai-sdk/openai'
|
|
80
80
|
import { createAnthropic } from '@ai-sdk/anthropic'
|
|
81
81
|
|
|
@@ -86,19 +86,19 @@ const createSingletonServices = pikkuServices(async (config, { secrets }) => {
|
|
|
86
86
|
apiKey: (await secrets.getSecret('OPENAI_API_KEY')).reveal(),
|
|
87
87
|
})
|
|
88
88
|
}
|
|
89
|
-
return { config,
|
|
89
|
+
return { config, agentRunner: new VercelAgentRunner(providers) }
|
|
90
90
|
})
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
The service key is **`
|
|
93
|
+
The service key is **`agentRunner`** — that is the name the agent wiring looks
|
|
94
94
|
up. Registering it as `aiRunner` leaves every agent unable to call a model.
|
|
95
95
|
|
|
96
96
|
### With an agent
|
|
97
97
|
|
|
98
98
|
```typescript
|
|
99
|
-
import {
|
|
99
|
+
import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
|
|
100
100
|
|
|
101
|
-
export const assistant =
|
|
101
|
+
export const assistant = pikkuAgent({
|
|
102
102
|
name: 'assistant',
|
|
103
103
|
description: 'Answers questions',
|
|
104
104
|
goal: 'You are a helpful assistant.',
|
|
@@ -106,16 +106,16 @@ export const assistant = pikkuAIAgent({
|
|
|
106
106
|
})
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
There is no `
|
|
110
|
-
generated agent types. See `pikku-
|
|
109
|
+
There is no `wireAgent` — agents are declared with `pikkuAgent` from the
|
|
110
|
+
generated agent types. See `pikku-agent` for the full config.
|
|
111
111
|
|
|
112
112
|
### Testing without a real provider
|
|
113
113
|
|
|
114
|
-
Replacing the
|
|
114
|
+
Replacing the _provider_ rather than the runner keeps every code path under test
|
|
115
115
|
real — tool loop, streaming, memory, approvals — and only scripts the replies.
|
|
116
116
|
Sealing it with `'*'` means no model string, including ones added later, can
|
|
117
117
|
reach a live endpoint:
|
|
118
118
|
|
|
119
119
|
```typescript
|
|
120
|
-
new
|
|
120
|
+
new VercelAgentRunner({ '*': createMockLlmProvider() })
|
|
121
121
|
```
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
name: pikku-ai-voice
|
|
3
3
|
description: >-
|
|
4
4
|
Use when adding voice input (speech-to-text) or voice output (text-to-speech) to AI agents in a
|
|
5
|
-
Pikku app. Covers the voiceInput/voiceOutput AI middleware from @pikku/core/
|
|
5
|
+
Pikku app. Covers the voiceInput/voiceOutput AI middleware from @pikku/core/agent, per-script
|
|
6
6
|
voices, and barge-in. TRIGGER when: code uses voiceInput, voiceOutput, or user asks about voice
|
|
7
7
|
agents, speech-to-text, text-to-speech, transcription, or @pikku/ai-voice. DO NOT TRIGGER when:
|
|
8
|
-
user asks about AI agent wiring generally (use pikku-
|
|
8
|
+
user asks about AI agent wiring generally (use pikku-agent) or the runner itself (use
|
|
9
9
|
pikku-ai-vercel).
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -27,14 +27,14 @@ The package still publishes, but its entire source is `export {}` — there are
|
|
|
27
27
|
`STTService`/`TTSService` interfaces and nothing to import. Do not add it as a
|
|
28
28
|
dependency.
|
|
29
29
|
|
|
30
|
-
Voice now lives in **`@pikku/core/
|
|
31
|
-
speech models are reached through the `
|
|
30
|
+
Voice now lives in **`@pikku/core/agent`** as two AI middlewares, and the
|
|
31
|
+
speech models are reached through the `agentRunner` (`transcribe` /
|
|
32
32
|
`generateSpeech`) rather than through separate services. See `pikku-ai-vercel`.
|
|
33
33
|
|
|
34
34
|
## API Reference
|
|
35
35
|
|
|
36
36
|
```typescript
|
|
37
|
-
import { voiceInput, voiceOutput } from '@pikku/core/
|
|
37
|
+
import { voiceInput, voiceOutput } from '@pikku/core/agent'
|
|
38
38
|
|
|
39
39
|
voiceInput(config?: {
|
|
40
40
|
model?: string // transcription model — required in practice
|
|
@@ -54,9 +54,9 @@ voiceOutput(config?: {
|
|
|
54
54
|
})
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Both attach through the agent's **`
|
|
58
|
-
`middlewareHooks` option, and the agent is declared with `
|
|
59
|
-
is no `
|
|
57
|
+
Both attach through the agent's **`agentMiddleware`** array, not a
|
|
58
|
+
`middlewareHooks` option, and the agent is declared with `pikkuAgent` — there
|
|
59
|
+
is no `wireAgent`.
|
|
60
60
|
|
|
61
61
|
### `voiceInput` — audio in, text in its place
|
|
62
62
|
|
|
@@ -74,7 +74,7 @@ spoken, which is why it records two shared-notes keys on the way past:
|
|
|
74
74
|
|
|
75
75
|
Behaviours that decide how a voice loop should be written:
|
|
76
76
|
|
|
77
|
-
- **It is a no-op without `
|
|
77
|
+
- **It is a no-op without `agentRunner.transcribe`** — no error, the audio
|
|
78
78
|
simply passes through untouched.
|
|
79
79
|
- **`config.model` is required once audio actually arrives**, and throws then
|
|
80
80
|
rather than at wiring time.
|
|
@@ -84,7 +84,7 @@ Behaviours that decide how a voice loop should be written:
|
|
|
84
84
|
distinct from a transcription failure, which is worth reporting.
|
|
85
85
|
- **Non-speech means an empty transcript, and nothing cleverer.** There was a
|
|
86
86
|
per-segment confidence gate here and it was removed: Whisper is
|
|
87
|
-
subtitle-trained, so it is
|
|
87
|
+
subtitle-trained, so it is _confident_ when it invents ("Thank you." scored
|
|
88
88
|
better than the real sentence beside it). Pick an ASR that returns an empty
|
|
89
89
|
string on silence rather than trying to filter one that doesn't.
|
|
90
90
|
- Audio arrives either inline (base64 `data`) or as a `url` fetched through
|
|
@@ -112,7 +112,7 @@ awaits the chain, and emits `audio-done` before the `done` event.
|
|
|
112
112
|
### `speakableScripts` — declare what the model can pronounce
|
|
113
113
|
|
|
114
114
|
Handed a script it has no voice for, a speech model typically neither fails nor
|
|
115
|
-
stays quiet: Kokoro reads out the
|
|
115
|
+
stays quiet: Kokoro reads out the _letter names_ — 24 seconds of "Arabic meem,
|
|
116
116
|
Arabic ra" for a one-line sentence. Declaring the range leaves anything outside
|
|
117
117
|
it unspoken and reports it once per reply as a `voice-unsupported` data event.
|
|
118
118
|
|
|
@@ -133,15 +133,15 @@ fallback)` are exported if you need the same decision outside the middleware.
|
|
|
133
133
|
## Usage Pattern
|
|
134
134
|
|
|
135
135
|
```typescript
|
|
136
|
-
import {
|
|
137
|
-
import { voiceInput, voiceOutput } from '@pikku/core/
|
|
136
|
+
import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
|
|
137
|
+
import { voiceInput, voiceOutput } from '@pikku/core/agent'
|
|
138
138
|
|
|
139
|
-
export const voiceAssistant =
|
|
139
|
+
export const voiceAssistant = pikkuAgent({
|
|
140
140
|
name: 'voice-assistant',
|
|
141
141
|
description: 'Holds a spoken conversation',
|
|
142
142
|
goal: 'You are a voice assistant. You are being listened to, not read.',
|
|
143
143
|
model: 'openai/gpt-5-mini',
|
|
144
|
-
|
|
144
|
+
agentMiddleware: [
|
|
145
145
|
voiceInput({ model: 'deepinfra/openai/whisper-large-v3-turbo' }),
|
|
146
146
|
voiceOutput({
|
|
147
147
|
model: 'deepinfra/hexgrad/Kokoro-82M',
|
|
@@ -27,7 +27,7 @@ Pikku is a TypeScript framework that separates business logic from transport mec
|
|
|
27
27
|
|
|
28
28
|
For deep-dive on each topic, see the dedicated skills:
|
|
29
29
|
|
|
30
|
-
- **Wiring**: `pikku-http`, `pikku-websocket`, `pikku-rpc`, `pikku-mcp`, `pikku-queue`, `pikku-cron`, `pikku-trigger`, `pikku-cli`, `pikku-
|
|
30
|
+
- **Wiring**: `pikku-http`, `pikku-websocket`, `pikku-rpc`, `pikku-mcp`, `pikku-queue`, `pikku-cron`, `pikku-trigger`, `pikku-cli`, `pikku-agent`, `pikku-workflow`
|
|
31
31
|
- **Authorization**: `pikku-security` (authentication/sessions), `pikku-permissions` (permission checks, scopes), `pikku-middleware` (global/tag/route middleware)
|
|
32
32
|
- **Infrastructure**: `pikku-services`, `pikku-config`
|
|
33
33
|
- **Project introspection**: `pikku-info`
|
|
@@ -44,7 +44,7 @@ pikkuFunc (pure business logic)
|
|
|
44
44
|
├── wireMCPTool → Model Context Protocol (AI tools)
|
|
45
45
|
├── wireCLI → CLI commands
|
|
46
46
|
├── wireTrigger → Event-driven (Redis pub/sub, PG LISTEN/NOTIFY)
|
|
47
|
-
├──
|
|
47
|
+
├── pikkuAgent → AI agents / chatbots
|
|
48
48
|
├── pikkuWorkflow → Multi-step durable workflows
|
|
49
49
|
└── wire.rpc → Internal function-to-function calls
|
|
50
50
|
```
|
|
@@ -122,7 +122,7 @@ pikkuFunc({
|
|
|
122
122
|
permissionsInBody?: boolean, // Last resort; needs allow.permissionsInBody in config
|
|
123
123
|
middleware?: PikkuMiddleware[], // See pikku-middleware
|
|
124
124
|
|
|
125
|
-
// Agent tooling — see pikku-
|
|
125
|
+
// Agent tooling — see pikku-agent
|
|
126
126
|
approvalRequired?: boolean,
|
|
127
127
|
approvalDescription?: (services, data) => Promise<string>,
|
|
128
128
|
|
|
@@ -144,7 +144,7 @@ reject every caller it exists to serve. Gate those with `permissions`, which
|
|
|
144
144
|
receive the optional session and may pass anonymous.
|
|
145
145
|
|
|
146
146
|
**Generics XOR `input`/`output` — never both.** A function's data and return
|
|
147
|
-
types come from
|
|
147
|
+
types come from _one_ source: either the `input`/`output` schemas (preferred —
|
|
148
148
|
they double as runtime validation and OpenAPI) or type generics
|
|
149
149
|
(`pikkuFunc<In, Out>({ ... })`). Passing both makes the two disagree and forces
|
|
150
150
|
`as any` casts. Do not annotate the `func` return type inline either — let the
|
|
@@ -230,7 +230,7 @@ await server.start()
|
|
|
230
230
|
|
|
231
231
|
**Lifecycle hooks do not run on this path** — only `pikku dev` and `pikku serve` invoke them. Do your startup work directly in the entrypoint instead.
|
|
232
232
|
|
|
233
|
-
`pikku validate` warns when a project starts a server by hand
|
|
233
|
+
`pikku validate` warns when a project starts a server by hand _and_ depends on no runtime adapter, since that combination means path 1 was available and unused. Silence it with `"lint": { "customServerBootstrap": "off" }` in `pikku.config.json`.
|
|
234
234
|
|
|
235
235
|
## Code Generation
|
|
236
236
|
|
|
@@ -42,14 +42,14 @@ export default createCloudflareHandler(
|
|
|
42
42
|
)
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
| Factory
|
|
46
|
-
|
|
|
47
|
-
| `createCloudflareHandler(factories, handlerTypes)` | combined `fetch`/`queue`/`scheduled`
|
|
48
|
-
| `createCloudflareWorkerHandler(factories)`
|
|
49
|
-
| `createCloudflareCronHandler(factories)`
|
|
50
|
-
| `createCloudflareQueueHandler(factories)`
|
|
51
|
-
| `createCloudflareMCPHandler(factories)`
|
|
52
|
-
| `createCloudflareWebSocketHandler(factories)`
|
|
45
|
+
| Factory | For |
|
|
46
|
+
| -------------------------------------------------- | ------------------------------------------------ |
|
|
47
|
+
| `createCloudflareHandler(factories, handlerTypes)` | combined `fetch`/`queue`/`scheduled` |
|
|
48
|
+
| `createCloudflareWorkerHandler(factories)` | HTTP / agent / RPC / workflow-orchestrator units |
|
|
49
|
+
| `createCloudflareCronHandler(factories)` | cron units |
|
|
50
|
+
| `createCloudflareQueueHandler(factories)` | queue-consumer and workflow-step units |
|
|
51
|
+
| `createCloudflareMCPHandler(factories)` | MCP units (same HTTP transport) |
|
|
52
|
+
| `createCloudflareWebSocketHandler(factories)` | channel units, delegating to the DO |
|
|
53
53
|
|
|
54
54
|
`factories` is `{ createConfig, createSingletonServices, createPlatformServices? }`.
|
|
55
55
|
|
|
@@ -71,7 +71,7 @@ const services = await setupServices(env, {
|
|
|
71
71
|
**Do not hand-roll this.** Beyond building `LocalVariablesService` /
|
|
72
72
|
`LocalSecretService` and caching the result, it calls `setSingletonServices()` —
|
|
73
73
|
and the core runners (`fetchData`, `runQueueJob`, `runScheduled`) resolve
|
|
74
|
-
services through that global slot,
|
|
74
|
+
services through that global slot, _not_ through the value you were returned. A
|
|
75
75
|
setup function that only returns the services leaves every request throwing
|
|
76
76
|
"Singleton services not initialized" as a CF `1101`. It also stashes the env via
|
|
77
77
|
`setCloudflareEnv`, which `getCloudflareEnv()` reads for bindings.
|
|
@@ -102,14 +102,17 @@ yarn add @pikku/ws
|
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
```typescript
|
|
105
|
-
import { pikkuWebsocketHandler } from '@pikku/ws'
|
|
105
|
+
import { DEFAULT_WS_MAX_PAYLOAD, pikkuWebsocketHandler } from '@pikku/ws'
|
|
106
106
|
import { stopSingletonServices } from '@pikku/core'
|
|
107
107
|
import { Server } from 'http'
|
|
108
108
|
import { WebSocketServer } from 'ws'
|
|
109
109
|
import './.pikku/pikku-bootstrap.gen.js'
|
|
110
110
|
|
|
111
111
|
const server = new Server()
|
|
112
|
-
const wss = new WebSocketServer({
|
|
112
|
+
const wss = new WebSocketServer({
|
|
113
|
+
noServer: true,
|
|
114
|
+
maxPayload: DEFAULT_WS_MAX_PAYLOAD,
|
|
115
|
+
})
|
|
113
116
|
|
|
114
117
|
pikkuWebsocketHandler({
|
|
115
118
|
server,
|
|
@@ -4,8 +4,11 @@ description: >-
|
|
|
4
4
|
Use for the Pikku dependency security audit: the `pikku audit` CLI command, the
|
|
5
5
|
`.pikku/audit.json` artifact, the `SecurityAuditReport` type in @pikku/core, and the console
|
|
6
6
|
Security screen (getSecurityAudit / runSecurityAudit / updateDependency + SecurityAuditView).
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
Also covers `pikku update`, which moves the @pikku/* dependency set forward and reports the
|
|
8
|
+
peers those versions need.
|
|
9
|
+
TRIGGER when: user asks about `pikku audit` or `pikku update`, dependency
|
|
10
|
+
vulnerabilities/advisories, outdated dependencies, upgrading Pikku itself, peer dependency
|
|
11
|
+
conflicts, the Security screen/page in the console, updating a vulnerable dependency, or
|
|
9
12
|
reading/rendering audit.json. DO NOT TRIGGER when: user asks about authentication/sessions/JWT
|
|
10
13
|
(use pikku-security), permissions (use pikku-permissions), or secrets/env vars (use
|
|
11
14
|
pikku-config).
|
|
@@ -43,11 +46,47 @@ installGroups: [core]
|
|
|
43
46
|
`bun outdated`, normalised into one `SecurityAuditReport` with per-severity /
|
|
44
47
|
per-update-level counts). Other PMs are detected but **stubbed** with a `note`
|
|
45
48
|
field until their shapes are normalised — issues/updates come back empty.
|
|
46
|
-
- `bun audit` exits non-zero when it
|
|
49
|
+
- `bun audit` exits non-zero when it _finds_ advisories but still writes the
|
|
47
50
|
payload to stdout, so a non-zero exit **with output** is data. A non-zero exit
|
|
48
51
|
with **no** output — or a launch failure, timeout, or a blown 32MB buffer —
|
|
49
52
|
throws, precisely so a failed run can't masquerade as "0 advisories".
|
|
50
53
|
|
|
54
|
+
## The `pikku update` command
|
|
55
|
+
|
|
56
|
+
Narrower than `audit --outdated`, and the only one that writes: it moves the
|
|
57
|
+
**@pikku/\* set** forward and reports the peers those versions need. Use `audit`
|
|
58
|
+
to learn a dependency is vulnerable; use `update` to move Pikku itself.
|
|
59
|
+
|
|
60
|
+
- `pikku update` — reports only. Nothing is written without `--update`.
|
|
61
|
+
- `pikku update --update` — writes the new ranges into every covered
|
|
62
|
+
package.json, then runs an install. `--no-install` writes and stops.
|
|
63
|
+
- `pikku update --update-peers` — implies `--update` and additionally writes the
|
|
64
|
+
ranges unsatisfied peers require, **for peers the project already declares**.
|
|
65
|
+
A peer it does not declare is reported and never added — adding a dependency
|
|
66
|
+
is not an update. Separate from `--update` because a peer bump can cross a
|
|
67
|
+
**major** of a third-party package (`ai` 5 → 6), which is not a call to make
|
|
68
|
+
on the user's behalf.
|
|
69
|
+
- `--tag <dist-tag>` (default `latest`) reads each package's own dist-tag, so
|
|
70
|
+
`--tag next` moves the whole set onto prereleases. `--registry <url>` defaults
|
|
71
|
+
to `npm_config_registry`.
|
|
72
|
+
- Coverage is the nearest package.json walking up from the project root, plus
|
|
73
|
+
every workspace it declares — a monorepo updates in one pass. All four
|
|
74
|
+
dependency fields are read, `peerDependencies` included, so an addon's own
|
|
75
|
+
declared peer range moves with it.
|
|
76
|
+
|
|
77
|
+
Statuses, per dependency: `outdated` (the range floor is behind latest — this is
|
|
78
|
+
what `--update` writes), `stale-install` (the range already admits latest but
|
|
79
|
+
node_modules is behind — an install fixes it, no edit needed), `linked` (a
|
|
80
|
+
`workspace:`/`file:`/`link:`/`portal:` range — a deliberate local checkout,
|
|
81
|
+
counted but never listed), `manual` (a registry range we refuse to substitute
|
|
82
|
+
into: a union, an x-range, a `*`), `unresolved` (the registry had no such tag —
|
|
83
|
+
this must **never** read as "current", the same rule as a failed audit).
|
|
84
|
+
|
|
85
|
+
Peers are read off the version the run **lands on**, not the one installed —
|
|
86
|
+
the point is what the target needs. An @pikku peer the same run already brings
|
|
87
|
+
forward is not reported, and an unsatisfied _optional_ peer the project never
|
|
88
|
+
declared is skipped.
|
|
89
|
+
|
|
51
90
|
## Console integration (@pikku/addon-console)
|
|
52
91
|
|
|
53
92
|
Three RPCs, all reading/writing the same artifact via the meta service. Shared
|