@pikku/skills 0.12.9 → 0.12.11

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 (75) hide show
  1. package/CHANGELOG.md +768 -0
  2. package/dist/skills.gen.js +1 -1
  3. package/package.json +4 -4
  4. package/skills/pikku-addon/SKILL.md +20 -14
  5. package/skills/pikku-addon/references/addon-package-manifest.md +2 -2
  6. package/skills/{pikku-ai-agent → pikku-agent}/SKILL.md +24 -24
  7. package/skills/pikku-ai-vercel/SKILL.md +18 -18
  8. package/skills/pikku-ai-voice/SKILL.md +15 -15
  9. package/skills/pikku-audit/SKILL.md +28 -13
  10. package/skills/pikku-aws/SKILL.md +2 -2
  11. package/skills/pikku-better-auth/SKILL.md +97 -17
  12. package/skills/pikku-build-app/SKILL.md +621 -0
  13. package/skills/pikku-build-app/references/multi-app.md +117 -0
  14. package/skills/pikku-build-app/references/ship.md +98 -0
  15. package/skills/pikku-build-app/references/theming.md +70 -0
  16. package/skills/pikku-build-platform/SKILL.md +239 -0
  17. package/skills/pikku-build-quick/SKILL.md +238 -0
  18. package/skills/pikku-cli/SKILL.md +7 -7
  19. package/skills/pikku-cli/references/complete-example.md +1 -1
  20. package/skills/pikku-concepts/SKILL.md +10 -7
  21. package/skills/pikku-concepts/references/concept-mapping.md +1 -1
  22. package/skills/pikku-config/SKILL.md +5 -3
  23. package/skills/pikku-deploy-azure/SKILL.md +5 -4
  24. package/skills/pikku-deploy-cloudflare/SKILL.md +9 -9
  25. package/skills/pikku-deploy-uws/SKILL.md +5 -2
  26. package/skills/pikku-deps/SKILL.md +42 -3
  27. package/skills/pikku-emails/SKILL.md +5 -5
  28. package/skills/pikku-fabric/SKILL.md +27 -3
  29. package/skills/pikku-fabric-debug/SKILL.md +1 -1
  30. package/skills/pikku-feature/SKILL.md +5 -4
  31. package/skills/pikku-http/SKILL.md +4 -4
  32. package/skills/pikku-http/references/http-options.md +13 -13
  33. package/skills/pikku-i18n/SKILL.md +2 -1
  34. package/skills/pikku-info/SKILL.md +1 -1
  35. package/skills/pikku-knowledge/SKILL.md +13 -13
  36. package/skills/pikku-kysely/SKILL.md +68 -41
  37. package/skills/pikku-machine-auth/SKILL.md +10 -10
  38. package/skills/pikku-mcp/SKILL.md +23 -20
  39. package/skills/pikku-middleware/SKILL.md +19 -12
  40. package/skills/pikku-middleware/references/middleware-patterns.md +14 -8
  41. package/skills/pikku-mongodb/SKILL.md +11 -11
  42. package/skills/pikku-n8n-import/SKILL.md +12 -12
  43. package/skills/pikku-n8n-import/SPEC.md +3 -0
  44. package/skills/pikku-n8n-import/references/addon-mapping.md +14 -8
  45. package/skills/pikku-n8n-import/references/code-translation.md +26 -22
  46. package/skills/pikku-n8n-import/references/loops-and-control.md +7 -7
  47. package/skills/pikku-paraglide/SKILL.md +11 -6
  48. package/skills/pikku-permissions/SKILL.md +19 -15
  49. package/skills/pikku-product-second-opinion/README.md +3 -3
  50. package/skills/pikku-product-second-opinion/SKILL.md +83 -73
  51. package/skills/pikku-product-second-opinion/example/sample-report.md +15 -12
  52. package/skills/pikku-product-second-opinion/references/report-template.md +10 -7
  53. package/skills/pikku-queue/SKILL.md +1 -1
  54. package/skills/pikku-react/SKILL.md +53 -13
  55. package/skills/pikku-realtime/SKILL.md +51 -19
  56. package/skills/pikku-rpc/SKILL.md +1 -1
  57. package/skills/pikku-rtl/SKILL.md +1 -1
  58. package/skills/pikku-scenario/SKILL.md +164 -44
  59. package/skills/pikku-schedule/SKILL.md +6 -1
  60. package/skills/pikku-schema-ajv/SKILL.md +2 -2
  61. package/skills/pikku-schema-cfworker/SKILL.md +1 -1
  62. package/skills/pikku-security/SKILL.md +9 -5
  63. package/skills/pikku-services/SKILL.md +27 -18
  64. package/skills/pikku-services/references/audit-wire-service.md +14 -8
  65. package/skills/pikku-software-archaeology/SKILL.md +27 -23
  66. package/skills/pikku-software-archaeology/references/blueprint.schema.json +580 -102
  67. package/skills/pikku-software-archaeology/scripts/validate.mjs +146 -69
  68. package/skills/pikku-tag-middleware/SKILL.md +1 -0
  69. package/skills/pikku-template-clone/SKILL.md +2 -1
  70. package/skills/pikku-trigger/SKILL.md +3 -3
  71. package/skills/pikku-versioning/SKILL.md +87 -3
  72. package/skills/pikku-websocket/SKILL.md +4 -3
  73. package/skills/pikku-workflow/SKILL.md +2 -2
  74. package/skills/pikku-workflow/references/workflow-reference.md +24 -10
  75. package/skills/pikku-ws/SKILL.md +5 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/skills",
3
- "version": "0.12.9",
3
+ "version": "0.12.11",
4
4
  "description": "The Pikku agent skills — the instruction set coding agents read to build, wire and deploy Pikku projects",
5
5
  "author": "yasser.fadl@gmail.com",
6
6
  "license": "MIT",
@@ -26,11 +26,11 @@
26
26
  "skills"
27
27
  ],
28
28
  "devDependencies": {
29
- "@types/node": "^24.11.0",
29
+ "@types/node": "^24.13.3",
30
30
  "typescript": "^6.0.3",
31
- "yaml": "^2.8.2"
31
+ "yaml": "^2.9.0"
32
32
  },
33
33
  "engines": {
34
34
  "node": ">=24"
35
35
  }
36
- }
36
+ }
@@ -40,7 +40,7 @@ See `pikku-concepts` for the core mental model.
40
40
  Register an addon in the consuming project:
41
41
 
42
42
  ```typescript
43
- import { wireAddon } from '#pikku'
43
+ import { wireAddon } from '#pikku/addon'
44
44
 
45
45
  wireAddon({
46
46
  name: string, // Namespace for addon functions (e.g. 'todos')
@@ -123,7 +123,7 @@ Type-safe reference to a function — local or addon — for use in any wiring.
123
123
  returns a function config that proxies the call via RPC at runtime:
124
124
 
125
125
  ```typescript
126
- import { ref } from '#pikku'
126
+ import { ref } from '#pikku/function'
127
127
 
128
128
  ref('todos:addTodo') // namespace:functionName for an addon function
129
129
  ref('myLocalFunc') // a local function by name
@@ -134,7 +134,7 @@ There is no `addon()` helper; `ref()` covers both. For an addon that publishes
134
134
  `refChannel` and `refCLI`, which carry the addon's own route/config metadata:
135
135
 
136
136
  ```typescript
137
- import { refHTTP } from '#pikku'
137
+ import { refHTTP } from '#pikku/function'
138
138
 
139
139
  wireHTTP(refHTTP('todos:listTodos', { basePath: '/api' }))
140
140
  ```
@@ -146,7 +146,7 @@ second argument is always present — an addon never falls back to its own logge
146
146
  variables or secrets; the consuming app supplies them:
147
147
 
148
148
  ```typescript
149
- import { pikkuAddonServices } from '#pikku'
149
+ import { pikkuAddonServices } from '#pikku/addon/setup'
150
150
 
151
151
  export const createSingletonServices = pikkuAddonServices(
152
152
  async (config, { secrets, logger }) => {
@@ -167,7 +167,7 @@ config object.
167
167
  Define per-request services for an addon package (created fresh per HTTP request, queue job, etc.):
168
168
 
169
169
  ```typescript
170
- import { pikkuAddonWireServices } from '#pikku'
170
+ import { pikkuAddonWireServices } from '#pikku/addon/setup'
171
171
 
172
172
  export const createWireServices = pikkuAddonWireServices(
173
173
  async (singletonServices, wire) => {
@@ -195,7 +195,7 @@ This generates `package.json` (exports `.pikku/*` + `dist/`), `pikku.config.json
195
195
 
196
196
  ```typescript
197
197
  // src/services.ts
198
- import { pikkuAddonServices, pikkuAddonWireServices } from '#pikku'
198
+ import { pikkuAddonServices, pikkuAddonWireServices } from '#pikku/addon/setup'
199
199
  import { TodoStore } from './todo-store.service.js'
200
200
 
201
201
  export const createSingletonServices = pikkuAddonServices(async () => {
@@ -213,10 +213,14 @@ export const createWireServices = pikkuAddonWireServices(
213
213
 
214
214
  ### Functions
215
215
 
216
+ An addon generates its whole tree under `#pikku/addon/*`, so it authors against
217
+ `#pikku/addon/function`, `#pikku/addon/http` and so on. An application's leaves
218
+ stay flat, which is what stops a linked addon resolving against its host.
219
+
216
220
  ```typescript
217
221
  // src/functions/addTodo.function.ts
218
222
  import { z } from 'zod'
219
- import { pikkuSessionlessFunc } from '#pikku'
223
+ import { pikkuSessionlessFunc } from '#pikku/addon/function'
220
224
 
221
225
  const AddTodoInput = z.object({ title: z.string() })
222
226
  const AddTodoOutput = z.object({ id: z.string(), title: z.string() })
@@ -269,7 +273,7 @@ yarn add @my-org/addon-todos
269
273
 
270
274
  ```typescript
271
275
  // wirings/todos.wirings.ts
272
- import { wireAddon } from '#pikku'
276
+ import { wireAddon } from '#pikku/addon'
273
277
 
274
278
  wireAddon({ name: 'todos', package: '@my-org/addon-todos' })
275
279
  ```
@@ -290,7 +294,8 @@ export const myFunc = pikkuFunc({
290
294
  ### Wire to HTTP
291
295
 
292
296
  ```typescript
293
- import { wireHTTP, ref } from '#pikku'
297
+ import { wireHTTP } from '#pikku/http'
298
+ import { ref } from '#pikku/function'
294
299
 
295
300
  wireHTTP({
296
301
  method: 'get',
@@ -303,7 +308,8 @@ wireHTTP({
303
308
  Or batch multiple addon routes with `defineHTTPRoutes` + `wireHTTPRoutes`:
304
309
 
305
310
  ```typescript
306
- import { wireHTTPRoutes, defineHTTPRoutes, ref } from '#pikku'
311
+ import { wireHTTPRoutes, defineHTTPRoutes } from '#pikku/http'
312
+ import { ref } from '#pikku/function'
307
313
 
308
314
  const todoRoutes = defineHTTPRoutes({
309
315
  tags: ['todos'],
@@ -320,10 +326,10 @@ wireHTTPRoutes({ basePath: '/api', routes: { todos: todoRoutes } })
320
326
  ### Use in AI Agents
321
327
 
322
328
  ```typescript
323
- import { pikkuAIAgent } from '#pikku/agent/pikku-agent-types.gen.js'
324
- import { ref } from '#pikku'
329
+ import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
330
+ import { ref } from '#pikku/function'
325
331
 
326
- export const todoAgent = pikkuAIAgent({
332
+ export const todoAgent = pikkuAgent({
327
333
  name: 'todo-agent',
328
334
  description: 'Manages a todo list',
329
335
  goal: 'You help users manage their todos.',
@@ -337,4 +343,4 @@ export const todoAgent = pikkuAIAgent({
337
343
  })
338
344
  ```
339
345
 
340
- See `pikku-ai-agent` — an addon function is just another `ref()` in `tools`.
346
+ See `pikku-agent` — an addon function is just another `ref()` in `tools`.
@@ -40,8 +40,8 @@ my-addon/
40
40
  {
41
41
  "name": "@my-org/addon-todos",
42
42
  "imports": {
43
- "#pikku": "./.pikku/pikku-types.gen.ts",
44
- "#pikku/*": "./.pikku/*"
43
+ "#pikku/*.js": "./.pikku/*.ts",
44
+ "#pikku/*": "./.pikku/*/index.ts"
45
45
  },
46
46
  "exports": {
47
47
  ".": { "types": "./dist/src/index.d.ts", "import": "./dist/src/index.js" },
@@ -1,10 +1,10 @@
1
1
  ---
2
- name: pikku-ai-agent
2
+ name: pikku-agent
3
3
  description: >-
4
4
  Use when building AI agents, chatbots, or LLM-powered assistants with Pikku. Covers
5
- pikkuAIAgent, ref() tool registration, memory, streaming, tool approval, thread ownership, and
6
- invocation via rpc.agent. TRIGGER when: code uses pikkuAIAgent/rpc.agent/runAIAgent/
7
- streamAIAgent, user asks about AI agents, chatbots, LLM assistants, tool-calling agents, agent
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
- ### `pikkuAIAgent(config)`
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 { pikkuAIAgent } from '#pikku/agent/pikku-agent-types.gen.js'
44
- import { ref } from '#pikku/pikku-types.gen.js'
43
+ import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
44
+ import { ref } from '#pikku/function'
45
45
 
46
- pikkuAIAgent({
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. 'aiStorage')
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
- aiMiddleware?: PikkuAIMiddlewareHooks[],
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
- `runAIAgent` / `streamAIAgent` from `@pikku/core/ai-agent` are the layer beneath
144
- this. Their third argument is `RunAIAgentParams` (`{ sessionService?,
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 `AIStreamEvent`s onto the channel:
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 { pikkuAIAgent } from '#pikku/agent/pikku-agent-types.gen.js'
182
- import { ref } from '#pikku/pikku-types.gen.js'
181
+ import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
182
+ import { ref } from '#pikku/function'
183
183
 
184
- export const todoAgent = pikkuAIAgent({
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: 'aiStorage', lastMessages: 20 },
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 = pikkuAIAgent({
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 // withdraw tools after the first step
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 *function*, not on the agent. The run then
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 { pikkuAIAgent } from '#pikku/agent/pikku-agent-types.gen.js'
286
- import { ref } from '#pikku/pikku-types.gen.js'
285
+ import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
286
+ import { ref } from '#pikku/function'
287
287
 
288
- export const todoAssistant = pikkuAIAgent({
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: 'aiStorage', lastMessages: 20 },
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
- VercelAIAgentRunner for streaming and non-streaming AI agent steps. TRIGGER when: code uses
6
- VercelAIAgentRunner, 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-ai-agent) or
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 `AIAgentRunnerService` from `@pikku/core`.
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
- ### `VercelAIAgentRunner`
34
+ ### `VercelAgentRunner`
35
35
 
36
36
  ```typescript
37
- import { VercelAIAgentRunner } from '@pikku/ai-vercel'
37
+ import { VercelAgentRunner } from '@pikku/ai-vercel'
38
38
 
39
- const runner = new VercelAIAgentRunner(
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: AIAgentRunnerParams, channel: AIStreamChannel): Promise<AIAgentStepResult>` — Stream AI responses with tool calls
49
- - `run(params: AIAgentRunnerParams): Promise<AIAgentStepResult>` — Execute a single AI step (non-streaming)
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 { VercelAIAgentRunner } from '@pikku/ai-vercel'
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, aiAgentRunner: new VercelAIAgentRunner(providers) }
89
+ return { config, agentRunner: new VercelAgentRunner(providers) }
90
90
  })
91
91
  ```
92
92
 
93
- The service key is **`aiAgentRunner`** — that is the name the agent wiring looks
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 { pikkuAIAgent } from '#pikku/agent/pikku-agent-types.gen.js'
99
+ import { pikkuAgent } from '#pikku/agent/pikku-agent-types.gen.js'
100
100
 
101
- export const assistant = pikkuAIAgent({
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 `wireAIAgent` — agents are declared with `pikkuAIAgent` from the
110
- generated agent types. See `pikku-ai-agent` for the full config.
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 *provider* rather than the runner keeps every code path under test
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 VercelAIAgentRunner({ '*': createMockLlmProvider() })
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/ai-agent, per-script
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-ai-agent) or the runner itself (use
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/ai-agent`** as two AI middlewares, and the
31
- speech models are reached through the `aiAgentRunner` (`transcribe` /
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/ai-agent'
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 **`aiMiddleware`** array, not a
58
- `middlewareHooks` option, and the agent is declared with `pikkuAIAgent` — there
59
- is no `wireAIAgent`.
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 `aiAgentRunner.transcribe`** — no error, the audio
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 *confident* when it invents ("Thank you." scored
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 *letter names* — 24 seconds of "Arabic meem,
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 { pikkuAIAgent } from '#pikku/agent/pikku-agent-types.gen.js'
137
- import { voiceInput, voiceOutput } from '@pikku/core/ai-agent'
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 = pikkuAIAgent({
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
- aiMiddleware: [
144
+ agentMiddleware: [
145
145
  voiceInput({ model: 'deepinfra/openai/whisper-large-v3-turbo' }),
146
146
  voiceOutput({
147
147
  model: 'deepinfra/hexgrad/Kokoro-82M',
@@ -38,11 +38,13 @@ An event only persists when the function opts in with **`audit: true`** — othe
38
38
  ```typescript
39
39
  import { NoopAuditService, createInvocationAudit } from '@pikku/core/services'
40
40
 
41
- export const createSingletonServices = pikkuServices(async (config, existing) => {
42
- // Prod platforms may inject a queue-backed sink as existing.audit.
43
- const audit = existing?.audit ?? new NoopAuditService()
44
- return { ...existing, config, /* ... */ audit }
45
- })
41
+ export const createSingletonServices = pikkuServices(
42
+ async (config, existing) => {
43
+ // Prod platforms may inject a queue-backed sink as existing.audit.
44
+ const audit = existing?.audit ?? new NoopAuditService()
45
+ return { ...existing, config, /* ... */ audit }
46
+ }
47
+ )
46
48
 
47
49
  // auditLog is created per invocation from the sink. Returned unconditionally so
48
50
  // a write from a function that forgot `audit: true` warns instead of vanishing.
@@ -67,12 +69,17 @@ Mark the function `audit: true` and call `auditLog?.write(...)`. Domain history
67
69
 
68
70
  ```typescript
69
71
  export const cancelInvoice = pikkuFunc({
70
- audit: true, // REQUIRED — else write() is a no-op
72
+ audit: true, // REQUIRED — else write() is a no-op
71
73
  input: CancelInvoiceInput,
72
74
  output: CancelInvoiceOutput,
73
75
  func: async ({ kysely, auditLog }, { invoiceId }, { session }) => {
74
- const inv = await kysely.selectFrom('invoice')/* ... */.executeTakeFirstOrThrow()
75
- await kysely.updateTable('invoice').set({ status: 'cancelled' })/* ... */.execute()
76
+ const inv = await kysely
77
+ .selectFrom('invoice') /* ... */
78
+ .executeTakeFirstOrThrow()
79
+ await kysely
80
+ .updateTable('invoice')
81
+ .set({ status: 'cancelled' }) /* ... */
82
+ .execute()
76
83
 
77
84
  await auditLog?.write({
78
85
  type: 'invoice.update',
@@ -108,7 +115,10 @@ import { createAuditedKysely } from '@pikku/kysely'
108
115
  export const createWireServices = pikkuWireServices(async (services, wire) => {
109
116
  if (!services.audit) return {}
110
117
  const auditLog = createInvocationAudit(services.audit, wire)
111
- return { auditLog, kysely: createAuditedKysely(services.kysely, { audit: auditLog }) }
118
+ return {
119
+ auditLog,
120
+ kysely: createAuditedKysely(services.kysely, { audit: auditLog }),
121
+ }
112
122
  })
113
123
  ```
114
124
 
@@ -172,15 +182,20 @@ const rows = await kysely
172
182
 
173
183
  ```typescript
174
184
  type AuditEvent = {
175
- type: string // e.g. 'invoice.update'
185
+ type: string // e.g. 'invoice.update'
176
186
  source: 'auto' | 'explicit'
177
- occurredAt: string // auto-filled by auditLog
187
+ occurredAt: string // auto-filled by auditLog
178
188
  eventId?: string
179
189
  outcome?: 'success' | 'failed' | 'denied'
180
- functionId?; wireType?; wireId?; traceId?; transactionId?; queryId? // auto
190
+ functionId?
191
+ wireType?
192
+ wireId?
193
+ traceId?
194
+ transactionId?
195
+ queryId? // auto
181
196
  userIdentity?: { userId?; orgId?; pikkuUserId? } // auto from wire session
182
197
  input?: unknown
183
- metadata?: Record<string, unknown> // your domain payload
198
+ metadata?: Record<string, unknown> // your domain payload
184
199
  }
185
200
  ```
186
201
 
@@ -64,12 +64,12 @@ provision an S3 bucket per logical bucket — the config takes only one.
64
64
 
65
65
  ### Behaviours worth knowing before you rely on them
66
66
 
67
- - **`signURL` fails open.** A signing error is logged and the *unsigned* URL is
67
+ - **`signURL` fails open.** A signing error is logged and the _unsigned_ URL is
68
68
  returned rather than thrown. If your CloudFront distribution is private the
69
69
  client then gets a 403; if it isn't, you have just handed out an unrestricted
70
70
  link. Check that `signConfig` is a valid CloudFront key pair at boot.
71
71
  - **`signContentKey` builds `https://<bucketName>/<bucket>/<contentKey>`** — it
72
- uses `bucketName` as the *host*. For signed content the value must therefore be
72
+ uses `bucketName` as the _host_. For signed content the value must therefore be
73
73
  your CloudFront domain, not a plain bucket name, which also means the same
74
74
  config field is doing two jobs.
75
75
  - **Presigned upload URLs expire after a fixed 3600s.** It is not configurable