@pikku/core 0.12.92 → 0.12.93

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 (99) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/errors/error-handler.d.ts +11 -0
  3. package/dist/errors/error-handler.js +8 -0
  4. package/dist/errors/serialized-error.d.ts +3 -0
  5. package/dist/function/function-runner.d.ts +1 -1
  6. package/dist/function/function-runner.js +3 -3
  7. package/dist/function/functions.types.d.ts +33 -21
  8. package/dist/middleware/auth-apikey.d.ts +7 -0
  9. package/dist/middleware/auth-apikey.js +6 -0
  10. package/dist/middleware/auth-bearer.d.ts +3 -0
  11. package/dist/middleware/auth-bearer.js +2 -0
  12. package/dist/middleware/auth-cookie.d.ts +9 -0
  13. package/dist/middleware/auth-cookie.js +6 -4
  14. package/dist/middleware/cors.d.ts +8 -0
  15. package/dist/middleware/cors.js +2 -0
  16. package/dist/middleware/middleware-factories.d.ts +14 -0
  17. package/dist/middleware/middleware-factories.js +14 -0
  18. package/dist/services/local-secrets.js +1 -1
  19. package/dist/services/personas-service.d.ts +4 -0
  20. package/dist/services/personas-service.js +4 -0
  21. package/dist/services/secret-service.d.ts +0 -1
  22. package/dist/testing/service-tests/secret-service-tests.js +1 -1
  23. package/dist/types/core.types.d.ts +5 -0
  24. package/dist/wirings/addon/wire-addon.d.ts +17 -15
  25. package/dist/wirings/addon/wire-addon.js +6 -0
  26. package/dist/wirings/addon/wire-remote-addon.d.ts +4 -0
  27. package/dist/wirings/addon/wire-remote-addon.js +4 -0
  28. package/dist/wirings/agent/agent-prepare.js +2 -2
  29. package/dist/wirings/agent/agent.types.d.ts +31 -27
  30. package/dist/wirings/agent-scorer/agent-scorer.d.ts +4 -0
  31. package/dist/wirings/agent-scorer/agent-scorer.js +4 -0
  32. package/dist/wirings/channel/channel.types.d.ts +11 -0
  33. package/dist/wirings/cli/cli.types.d.ts +43 -1
  34. package/dist/wirings/cli/define-cli-commands.d.ts +6 -1
  35. package/dist/wirings/cli/define-cli-commands.js +6 -1
  36. package/dist/wirings/credential/credential.types.d.ts +7 -0
  37. package/dist/wirings/gateway/gateway.types.d.ts +23 -0
  38. package/dist/wirings/http/http.types.d.ts +51 -37
  39. package/dist/wirings/mcp/mcp.types.d.ts +27 -0
  40. package/dist/wirings/queue/queue.types.d.ts +6 -0
  41. package/dist/wirings/scheduler/scheduler.types.d.ts +5 -0
  42. package/dist/wirings/secret/secret.types.d.ts +9 -14
  43. package/dist/wirings/secret/secret.types.js +7 -0
  44. package/dist/wirings/trigger/trigger.types.d.ts +4 -1
  45. package/dist/wirings/variable/variable.types.d.ts +14 -13
  46. package/dist/wirings/variable/variable.types.js +7 -0
  47. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +7 -0
  48. package/dist/wirings/workflow/scenario-cookie-jar.d.ts +6 -0
  49. package/dist/wirings/workflow/scenario-cookie-jar.js +6 -0
  50. package/dist/wirings/workflow/scenario-poll.d.ts +6 -0
  51. package/dist/wirings/workflow/scenario-poll.js +6 -0
  52. package/dist/wirings/workflow/scenario-step-guards.d.ts +6 -0
  53. package/dist/wirings/workflow/scenario-step-guards.js +6 -0
  54. package/dist/wirings/workflow/workflow-errors.d.ts +4 -0
  55. package/dist/wirings/workflow/workflow-errors.js +4 -0
  56. package/package.json +1 -1
  57. package/src/app-leaf-surface.test.ts +2 -2
  58. package/src/errors/error-handler.ts +11 -0
  59. package/src/errors/serialized-error.ts +3 -0
  60. package/src/function/function-runner.ts +4 -4
  61. package/src/function/functions.types.ts +33 -21
  62. package/src/middleware/auth-apikey.ts +7 -0
  63. package/src/middleware/auth-bearer.ts +3 -0
  64. package/src/middleware/auth-cookie.ts +9 -4
  65. package/src/middleware/cors.ts +8 -0
  66. package/src/middleware/middleware-factories.ts +14 -0
  67. package/src/services/local-secrets.test.ts +2 -2
  68. package/src/services/local-secrets.ts +1 -1
  69. package/src/services/personas-service.ts +4 -0
  70. package/src/services/secret-service.ts +0 -1
  71. package/src/testing/service-tests/secret-service-tests.ts +1 -1
  72. package/src/types/core.types.ts +5 -0
  73. package/src/wirings/addon/addon-auth-tags.test.ts +4 -4
  74. package/src/wirings/addon/wire-addon.ts +17 -15
  75. package/src/wirings/addon/wire-remote-addon.ts +4 -0
  76. package/src/wirings/agent/agent-authorization.test.ts +7 -3
  77. package/src/wirings/agent/agent-prepare.ts +2 -2
  78. package/src/wirings/agent/agent.types.ts +31 -27
  79. package/src/wirings/agent-scorer/agent-scorer.ts +4 -0
  80. package/src/wirings/channel/channel.types.ts +11 -0
  81. package/src/wirings/cli/cli.types.ts +44 -1
  82. package/src/wirings/cli/define-cli-commands.ts +6 -1
  83. package/src/wirings/credential/credential.types.ts +7 -0
  84. package/src/wirings/gateway/gateway-authorization.test.ts +3 -3
  85. package/src/wirings/gateway/gateway.types.ts +23 -0
  86. package/src/wirings/http/http-runner-addon-ref.test.ts +1 -1
  87. package/src/wirings/http/http.types.ts +94 -69
  88. package/src/wirings/mcp/mcp.types.ts +27 -0
  89. package/src/wirings/queue/queue.types.ts +6 -0
  90. package/src/wirings/scheduler/scheduler.types.ts +5 -0
  91. package/src/wirings/secret/secret.types.ts +9 -14
  92. package/src/wirings/trigger/trigger.types.ts +4 -1
  93. package/src/wirings/variable/variable.types.ts +14 -13
  94. package/src/wirings/workflow/graph/wire-workflow-graph.ts +7 -0
  95. package/src/wirings/workflow/scenario-cookie-jar.ts +6 -0
  96. package/src/wirings/workflow/scenario-poll.ts +6 -0
  97. package/src/wirings/workflow/scenario-step-guards.ts +6 -0
  98. package/src/wirings/workflow/workflow-errors.ts +4 -0
  99. package/tsconfig.tsbuildinfo +1 -1
@@ -171,6 +171,7 @@ export interface AgentToolDef extends Partial<ApprovalPolicy> {
171
171
  forwardsApproval?: boolean;
172
172
  }
173
173
  export interface PikkuAgentMiddlewareHooks<State extends Record<string, unknown> = Record<string, unknown>, SingletonServices extends CoreSingletonServices = CoreSingletonServices> {
174
+ /** Rewrites what the model is about to see — messages and instructions — before each turn. */
174
175
  modifyInput?: (services: SingletonServices, ctx: {
175
176
  messages: AgentMessage[];
176
177
  instructions: string;
@@ -198,6 +199,7 @@ export interface PikkuAgentMiddlewareHooks<State extends Record<string, unknown>
198
199
  messages: AgentMessage[];
199
200
  instructions: string;
200
201
  };
202
+ /** Sees the model's output as it streams, for redaction or live inspection. Keeps its own `state` across chunks, unlike the shared run notes. */
201
203
  modifyOutputStream?: (services: SingletonServices, ctx: {
202
204
  event: AgentStreamEvent;
203
205
  allEvents: readonly AgentStreamEvent[];
@@ -242,6 +244,7 @@ export interface PikkuAgentMiddlewareHooks<State extends Record<string, unknown>
242
244
  * and handed to anything that grades the run, and scrubbing the reply alone
243
245
  * leaves them untouched.
244
246
  */
247
+ /** Rewrites the finished output of a turn, after streaming has completed. */
245
248
  modifyOutput?: (services: SingletonServices, ctx: {
246
249
  text: string;
247
250
  messages: AgentMessage[];
@@ -259,6 +262,7 @@ export interface PikkuAgentMiddlewareHooks<State extends Record<string, unknown>
259
262
  messages: AgentMessage[];
260
263
  toolCalls?: NonNullable<AgentStep['toolCalls']>;
261
264
  };
265
+ /** Runs before a tool executes, and may rewrite the arguments the model chose. Returning nothing leaves them as they are. */
262
266
  beforeToolCall?: (services: SingletonServices, ctx: {
263
267
  toolName: string;
264
268
  toolCallId: string;
@@ -268,6 +272,7 @@ export interface PikkuAgentMiddlewareHooks<State extends Record<string, unknown>
268
272
  } | void> | {
269
273
  args: Record<string, unknown>;
270
274
  } | void;
275
+ /** Runs once a tool has returned, and may replace its result before the model sees it. */
271
276
  afterToolCall?: (services: SingletonServices, ctx: {
272
277
  toolName: string;
273
278
  toolCallId: string;
@@ -279,6 +284,7 @@ export interface PikkuAgentMiddlewareHooks<State extends Record<string, unknown>
279
284
  } | void> | {
280
285
  result: unknown;
281
286
  } | void;
287
+ /** Runs at the end of each model turn. For observation — it cannot change what happened. */
282
288
  afterStep?: (services: SingletonServices, ctx: {
283
289
  stepNumber: number;
284
290
  text: string;
@@ -300,6 +306,7 @@ export interface PikkuAgentMiddlewareHooks<State extends Record<string, unknown>
300
306
  };
301
307
  finishReason: string;
302
308
  }) => Promise<void> | void;
309
+ /** Runs when a turn throws, with the step it failed on. For logging and cleanup; it does not swallow the error. */
303
310
  onError?: (services: SingletonServices, ctx: {
304
311
  error: Error;
305
312
  stepNumber: number;
@@ -314,58 +321,51 @@ export type AgentMemoryConfig = {
314
321
  workingMemory?: unknown;
315
322
  };
316
323
  export type CoreAgent<PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>, Scope extends string = string, Scorer extends string = string> = {
324
+ /** Unique across the project. It is how the agent is invoked and how its runs are grouped. */
317
325
  name: string;
326
+ /** What the agent is for. Another agent choosing whether to delegate to this one reads it, so write it for that reader. */
318
327
  description: string;
328
+ /** A one-line description for listings, where the full `description` is too long. */
319
329
  summary?: string;
330
+ /** Names of error classes this may throw, so each one's registered status is used instead of a 500. */
320
331
  errors?: string[];
321
- /**
322
- * The three fields below are the system prompt. `buildInstructions` joins
323
- * whichever are set with a blank line, always in this order — role, then
324
- * personality, then goal — and appends the tool-usage rules if the agent has
325
- * tools. Nothing checks them against each other: the split is there to keep a
326
- * prompt legible, and text put in the wrong one still reaches the model.
327
- *
328
- * Who the agent is. 'You are a support engineer triaging inbound bugs.'
329
- */
332
+ /** Who the agent is. Joined with `personality` and `goal`, in that order, to form the system prompt. */
330
333
  role?: string;
331
334
  /** How it should sound: tone, vocabulary, how much it says at a time. */
332
335
  personality?: string;
333
336
  /** What it is for — the only one of the three that is required. */
334
337
  goal: string;
338
+ /** Which model to run on, as the provider names it. */
335
339
  model: string;
340
+ /** How much the model is allowed to vary its answer. Lower is more repeatable, which is what a tool-driving agent usually wants. */
336
341
  temperature?: number;
337
342
  /** Ownership/partitioning of this agent's threads and runs. Defaults to `'user'`. */
338
343
  sessionScope?: SessionScope;
344
+ /** Functions the model may call, given to it as tools. A `readonly` one may be called without asking first. */
339
345
  tools?: unknown[];
346
+ /** Other agents this one may hand work to. `agentMode` decides whether it delegates or supervises. */
340
347
  agents?: unknown[];
348
+ /** Workflows the model may start, for work too long to sit inside one agent run. */
341
349
  workflows?: unknown[];
342
- /**
343
- * Grades this agent's finished runs on live traffic, named by the generated
344
- * `ScorerName` union rather than by `ref()` — a scorer is not a function, so
345
- * there is nothing in the function map for a ref to resolve against.
346
- *
347
- * A reference-based judge listed here is never sampled: live traffic has no
348
- * answer key. Scenarios name scorers directly and may grade with scorers an
349
- * agent does not ship with.
350
- */
350
+ /** Grades finished runs on live traffic. A reference-based judge is never sampled here — live traffic has no answer key. */
351
351
  scorers?: Scorer[];
352
+ /** `delegate` hands a sub-agent the task and takes its answer; `supervise` keeps this agent in the loop over each step. */
352
353
  agentMode?: 'delegate' | 'supervise';
354
+ /** What the agent carries between runs, and how much of it. */
353
355
  memory?: AgentMemoryConfig;
356
+ /** How many model turns a single run may take before it is stopped. The guard against a tool loop that never converges. */
354
357
  maxSteps?: number;
358
+ /** Whether the model may answer without calling a tool (`auto`), must call one (`required`), or may not (`none`). */
355
359
  toolChoice?: 'auto' | 'required' | 'none';
356
- /**
357
- * Per-provider model settings, keyed by provider id and passed through
358
- * untouched — for anything only one vendor offers, which the fields above
359
- * deliberately do not try to unify.
360
- *
361
- * `{ openai: { reasoningEffort: 'minimal' } }` is the one that matters for
362
- * voice: on gpt-5-mini it measured 0.9s to first token against 2.5s at the
363
- * default, and a spoken reply is waited through rather than skimmed.
364
- */
360
+ /** Per-provider settings passed through untouched, for what only one vendor offers. */
365
361
  providerOptions?: AIProviderOptions;
362
+ /** The schema of what starts a run, which is also its input type. */
366
363
  input?: unknown;
364
+ /** The schema the final answer must match, which is also its return type. Without one the agent returns free text. */
367
365
  output?: unknown;
366
+ /** Filters this agent in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
368
367
  tags?: string[];
368
+ /** Runs before each model turn, to change what that turn sees or to stop the run. This is where a step budget or a tool narrowing goes. */
369
369
  prepareStep?: (ctx: {
370
370
  stepNumber: number;
371
371
  messages: AgentMessage[];
@@ -374,8 +374,11 @@ export type CoreAgent<PikkuPermission = CorePikkuPermission<any, any>, PikkuMidd
374
374
  model: string;
375
375
  stop: () => void;
376
376
  }) => void | Promise<void>;
377
+ /** Wraps the whole run: auth, tracing, spend limits. */
377
378
  middleware?: PikkuMiddleware[];
379
+ /** Wraps each message when the agent is driven over a channel. */
378
380
  channelMiddleware?: CorePikkuChannelMiddleware<any, any>[];
381
+ /** Hooks around each step and each tool call — the place to inspect, rewrite or veto what the model is about to do. */
379
382
  agentMiddleware?: PikkuAgentMiddlewareHooks<any, any>[];
380
383
  /**
381
384
  * Whether a session is required to run this agent. Defaults to `false`, since
@@ -394,6 +397,7 @@ export type CoreAgent<PikkuPermission = CorePikkuPermission<any, any>, PikkuMidd
394
397
  * `#pikku/scopes`, so an undeclared scope is a compile error.
395
398
  */
396
399
  scopes?: Scope[];
400
+ /** Checks that run before the agent starts. Grouped names OR together, so any one passing admits the caller. */
397
401
  permissions?: CorePermissionGroup<PikkuPermission>;
398
402
  };
399
403
  export type AgentStreamEvent = {
@@ -2,6 +2,8 @@ import type { JudgeToolCallDisclosure, PikkuAgentScorer, ScorerInput, ScorerOutp
2
2
  /**
3
3
  * A heuristic scorer: pure code over the finished run, no model call, so it
4
4
  * grades on the fast lane.
5
+ *
6
+ * @example snippet: agentScorer
5
7
  */
6
8
  export declare const pikkuAgentScorer: <Services = any>(config: {
7
9
  name: string;
@@ -22,6 +24,8 @@ export declare const pikkuAgentScorer: <Services = any>(config: {
22
24
  * The rubric field is `goal`, matching `pikkuAgent`'s prompt vocabulary — a
23
25
  * judge is a degenerate agent, and should use the same word for the same thing.
24
26
  * `prompt` is the escape hatch for non-standard framing.
27
+ *
28
+ * @example snippet: agentJudge
25
29
  */
26
30
  export declare const pikkuAgentJudge: <Services = any>(config: {
27
31
  name: string;
@@ -9,6 +9,8 @@ const assertSampleRate = (name, sampleRate) => {
9
9
  /**
10
10
  * A heuristic scorer: pure code over the finished run, no model call, so it
11
11
  * grades on the fast lane.
12
+ *
13
+ * @example snippet: agentScorer
12
14
  */
13
15
  export const pikkuAgentScorer = (config) => ({
14
16
  name: config.name,
@@ -25,6 +27,8 @@ export const pikkuAgentScorer = (config) => ({
25
27
  * The rubric field is `goal`, matching `pikkuAgent`'s prompt vocabulary — a
26
28
  * judge is a degenerate agent, and should use the same word for the same thing.
27
29
  * `prompt` is the escape hatch for non-standard framing.
30
+ *
31
+ * @example snippet: agentJudge
28
32
  */
29
33
  export const pikkuAgentJudge = (config) => ({
30
34
  name: config.name,
@@ -48,8 +48,11 @@ export interface ChannelMeta {
48
48
  }
49
49
  export type ChannelsMeta = Record<string, ChannelMeta>;
50
50
  export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = CorePikkuFunctionConfig<CorePikkuFunction<void, unknown> | CorePikkuFunctionSessionless<void, unknown>, CorePikkuPermission<void>, CorePikkuMiddleware>, ChannelDisconnect = CorePikkuFunctionConfig<CorePikkuFunction<void, void> | CorePikkuFunctionSessionless<void, void>, CorePikkuPermission<void>, CorePikkuMiddleware>, ChannelFunctionMessage = CorePikkuFunctionConfig<CorePikkuFunction<unknown, unknown> | CorePikkuFunctionSessionless<unknown, unknown>, CorePikkuPermission<unknown>, CorePikkuMiddleware>, PikkuPermission = CorePikkuPermission<ChannelData>, PikkuMiddleware = CorePikkuMiddleware> = {
51
+ /** Unique across the project. It is how the channel is addressed in `pikku meta` and by the generated client. */
51
52
  name: string;
53
+ /** The path a client opens the socket on. */
52
54
  route: Channel;
55
+ /** Runs once when a client connects, before any message. Its return value is sent as the first message. */
53
56
  onConnect?: ChannelConnect | {
54
57
  func?: ChannelConnect;
55
58
  middleware?: PikkuMiddleware[];
@@ -58,17 +61,25 @@ export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = Co
58
61
  func?: ChannelDisconnect;
59
62
  middleware?: PikkuMiddleware[];
60
63
  };
64
+ /** Handles any message that no `onMessageWiring` entry claimed. Without one, an unrouted message is dropped. */
61
65
  onMessage?: ChannelFunctionMessage;
66
+ /** Routes messages by a key in their payload: the outer key is the field to switch on, the inner key its value. This is how one socket carries many operations. */
62
67
  onMessageWiring?: Record<string, Record<string, ChannelFunctionMessage | {
63
68
  func: ChannelFunctionMessage;
64
69
  auth?: boolean;
65
70
  middleware?: PikkuMiddleware[];
66
71
  }>>;
72
+ /** Wraps the connection: it runs on connect, where the session is established, not on every message. */
67
73
  middleware?: PikkuMiddleware[];
74
+ /** Wraps every message, which is where per-message concerns like rate limiting belong. */
68
75
  channelMiddleware?: Array<CorePikkuChannelMiddleware | CorePikkuChannelMiddlewareFactory>;
76
+ /** Whether opening the channel requires a session. Defaults to true — a channel is closed unless it says otherwise. */
69
77
  auth?: boolean;
78
+ /** Whether this channel carries binary frames. Text frames are still parsed as JSON. */
70
79
  binary?: boolean | null;
80
+ /** Handles binary frames, which never reach `onMessage` because they are not JSON. Return a value to reply in kind. */
71
81
  onBinaryMessage?: (services: any, data: BinaryData, channel: PikkuChannel<ChannelData, any>) => Promise<BinaryData | void> | BinaryData | void;
82
+ /** Filters this channel in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
72
83
  tags?: string[];
73
84
  };
74
85
  /**
@@ -127,13 +127,18 @@ type StripBrackets<S extends string> = S extends `<${infer Inner}>` ? Inner : S
127
127
  * Split string by spaces
128
128
  */
129
129
  type SplitBySpace<S extends string> = S extends `${infer First} ${infer Rest}` ? [First, ...SplitBySpace<Rest>] : S extends '' ? [] : [S];
130
+ /**
131
+ * Strip the `...` a variadic positional carries, so `[files...]` validates
132
+ * against the `files` key the runtime parser fills.
133
+ */
134
+ type StripVariadic<S extends string> = S extends `${infer Name}...` ? Name : S;
130
135
  /**
131
136
  * Recursively build a tuple by stripping brackets from each element
132
137
  */
133
138
  type BuildParamsTuple<Parts extends readonly string[]> = Parts extends readonly [
134
139
  infer First extends string,
135
140
  ...infer Rest extends string[]
136
- ] ? [StripBrackets<First>, ...BuildParamsTuple<Rest>] : [];
141
+ ] ? [StripVariadic<StripBrackets<First>>, ...BuildParamsTuple<Rest>] : [];
137
142
  /**
138
143
  * Extract parameter names from CLI parameter string into a tuple
139
144
  * Example: "<env> [region]" => ["env", "region"]
@@ -152,16 +157,39 @@ export type ValidateParameters<Params extends string, Input> = AllParamsValid<Ex
152
157
  * This is a helper type for creating type-safe CLI commands.
153
158
  */
154
159
  export type CoreCLICommandConfig<FuncConfig, PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<any, any>, PikkuCLIRender extends CorePikkuCLIRender<any, any, any> = CorePikkuCLIRender<any, any>, Params extends string = string> = {
160
+ /**
161
+ * Positional arguments, written the way `--help` prints them: `<env>` is
162
+ * required, `[region]` optional. Each name must be a key of the function's
163
+ * input schema, so a typo is a compile error rather than an argument that
164
+ * silently never arrives.
165
+ */
155
166
  parameters?: ValidateParameters<Params, ExtractFunctionInput<FuncConfig>>;
167
+ /** The function to run. Omit it on a command that exists only to hold `subcommands`. */
156
168
  func?: FuncConfig;
169
+ /** The heading shown above this command's own help. */
157
170
  title?: string;
171
+ /** The one-line summary listed beside the command name. */
158
172
  description?: string;
173
+ /**
174
+ * Prints the result. The function returns data and this is the only thing
175
+ * that writes to stdout, which is what lets one command serve both a human
176
+ * reading it and a script parsing it.
177
+ */
159
178
  render?: PikkuCLIRender;
179
+ /**
180
+ * Flags, keyed by the input field each one fills. A field with no entry is
181
+ * still accepted as `--field`; an entry is how it gets a short form, a
182
+ * default, or help text.
183
+ */
160
184
  options?: {
161
185
  [K in keyof ExtractFunctionInput<FuncConfig>]?: {
186
+ /** What this flag is for, as `--help` prints it. */
162
187
  description?: string;
188
+ /** A single-letter alias, so `--verbose` can also be `-v`. */
163
189
  short?: string;
190
+ /** How the argument is parsed. Inferred from the input schema when omitted. */
164
191
  type?: CLIOptionType;
192
+ /** Used when the flag is absent, and shown in `--help` so nobody has to guess it. */
165
193
  default?: ExtractFunctionInput<FuncConfig>[K];
166
194
  /**
167
195
  * The values this option accepts. Rejected by the parser and listed in
@@ -171,10 +199,15 @@ export type CoreCLICommandConfig<FuncConfig, PikkuMiddleware extends CorePikkuMi
171
199
  choices?: ReadonlyArray<ExtractFunctionInput<FuncConfig>[K]>;
172
200
  };
173
201
  };
202
+ /** Wraps every run of this command. */
174
203
  middleware?: PikkuMiddleware[];
204
+ /** Nested commands, keyed by the word that selects them: `deploy plan` is `deploy` with a `plan` subcommand. */
175
205
  subcommands?: Record<string, CoreCLICommandConfig<any, PikkuMiddleware, PikkuCLIRender, any>>;
206
+ /** Whether running this requires a logged-in session. Defaults to true — a command is closed unless it says otherwise. */
176
207
  auth?: boolean;
208
+ /** Permission checks run before the function, for a command that not every logged-in user may run. */
177
209
  permissions?: any[];
210
+ /** Runs when the parent is invoked with no subcommand named. */
178
211
  isDefault?: boolean;
179
212
  };
180
213
  /**
@@ -203,14 +236,23 @@ export type CLICommandShorthand<In, Out, PikkuFunctionConfig extends CorePikkuFu
203
236
  * CLI wiring configuration
204
237
  */
205
238
  export interface CoreCLI<Commands extends Record<string, CoreCLICommandConfig<any, any, any>>, Options, PikkuMiddleware, PikkuCLIRender> {
239
+ /** The command name a user types. It is also what `--help` prints as the program. */
206
240
  program: string;
241
+ /** What the program is for, shown at the top of `--help`. */
207
242
  description?: string;
243
+ /** Top-level commands, keyed by the word that selects them. */
208
244
  commands: Commands;
245
+ /** Flags accepted before any command, for things that apply to the whole program. */
209
246
  options?: CLIOptions<Options>;
247
+ /** Wraps every command. */
210
248
  middleware?: PikkuMiddleware[];
249
+ /** The fallback printer for commands that declare none of their own. */
211
250
  render?: PikkuCLIRender;
251
+ /** A one-line description for listings, where the full `description` is too long. */
212
252
  summary?: string;
253
+ /** Names of error classes any command may throw, so each one's registered exit behaviour is used. */
213
254
  errors?: string[];
255
+ /** Filters this program in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
214
256
  tags?: string[];
215
257
  /**
216
258
  * Requires a session on the websocket serving this program remotely. Has no
@@ -1,3 +1,8 @@
1
1
  import type { CoreCLICommandConfig } from './cli.types.js';
2
- /** Identity at runtime; exists so a commands record can be typed and composed before `wireCLI`. */
2
+ /**
3
+ * Identity at runtime; exists so a commands record can be typed and composed
4
+ * before `wireCLI`.
5
+ *
6
+ * @example snippet: cliCommandContract
7
+ */
3
8
  export declare function defineCLICommands<T extends Record<string, CoreCLICommandConfig<any, any, any, any>>>(commands: T): T;
@@ -1,4 +1,9 @@
1
- /** Identity at runtime; exists so a commands record can be typed and composed before `wireCLI`. */
1
+ /**
2
+ * Identity at runtime; exists so a commands record can be typed and composed
3
+ * before `wireCLI`.
4
+ *
5
+ * @example snippet: cliCommandContract
6
+ */
2
7
  export function defineCLICommands(commands) {
3
8
  return commands;
4
9
  }
@@ -1,11 +1,18 @@
1
1
  import type { OAuth2CredentialConfig } from '../secret/secret.types.js';
2
2
  export type CoreCredential<T = unknown> = {
3
+ /** How the credential is asked for in code. Generated into `CredentialsMap`, so it is what `credentials.get` autocompletes. */
3
4
  name: string;
5
+ /** The name shown to whoever has to supply the value, who is often not the person who wrote this. */
4
6
  displayName: string;
7
+ /** What the credential is for and where to obtain one. */
5
8
  description?: string;
9
+ /** `singleton` is one value for the whole deployment; `wire` is one per user, supplied by them and stored against their account. */
6
10
  type: 'singleton' | 'wire';
11
+ /** The shape of the value, validated when it is supplied rather than when it is first used. */
7
12
  schema: T;
13
+ /** Where to go to create one, shown next to the field asking for it. */
8
14
  docsUrl?: string;
15
+ /** Makes this an OAuth connection rather than a value pasted in: the user is sent to the provider and the tokens are stored for them. */
9
16
  oauth2?: OAuth2CredentialConfig & {
10
17
  appCredentialSecretId: string;
11
18
  };
@@ -9,6 +9,10 @@ export interface GatewayAttachment {
9
9
  mimeType?: string;
10
10
  filename?: string;
11
11
  }
12
+ /**
13
+ * One message arriving from a gateway, normalised: who sent it, in which
14
+ * conversation, and what they said.
15
+ */
12
16
  export interface GatewayInboundMessage {
13
17
  /** Platform-specific: a phone number, a Slack user id, and so on. */
14
18
  senderId: string;
@@ -17,17 +21,29 @@ export interface GatewayInboundMessage {
17
21
  attachments?: GatewayAttachment[];
18
22
  metadata?: Record<string, unknown>;
19
23
  }
24
+ /**
25
+ * One message to send back through a gateway — plain text, or the provider's
26
+ * own rich content.
27
+ */
20
28
  export interface GatewayOutboundMessage {
21
29
  text?: string;
22
30
  richContent?: Record<string, unknown>;
23
31
  attachments?: GatewayAttachment[];
24
32
  }
33
+ /**
34
+ * What a gateway's `verifyWebhook` returns — verified, with the response the
35
+ * provider expects back, or not.
36
+ */
25
37
  export type WebhookVerificationResult = {
26
38
  verified: true;
27
39
  response: unknown;
28
40
  } | {
29
41
  verified: false;
30
42
  };
43
+ /**
44
+ * What a gateway integration implements: parse an incoming event into a
45
+ * message, send one back, and open and close the connection.
46
+ */
31
47
  export interface GatewayAdapter {
32
48
  name: string;
33
49
  /** Return null to ignore the event, e.g. a delivery receipt. */
@@ -49,14 +65,21 @@ export interface PikkuGateway {
49
65
  /** 'webhook' the platform POSTs to us, 'websocket' the client connects to us, 'listener' no route at all. */
50
66
  export type GatewayTransportType = 'webhook' | 'websocket' | 'listener';
51
67
  export type CoreGateway<PikkuFunctionConfig = CorePikkuFunctionConfig<any, any>, PikkuMiddleware extends CorePikkuMiddleware = CorePikkuMiddleware> = Partial<Pick<CommonWireMeta, 'title' | 'summary' | 'description' | 'errors'>> & {
68
+ /** Unique across the project. It is how the gateway is addressed in `pikku meta` and in logs. */
52
69
  name: string;
70
+ /** How the platform reaches us: a `webhook` it posts to, a `websocket` it holds open, or a `listener` we open outward. */
53
71
  type: GatewayTransportType;
54
72
  /** Required for 'webhook' and 'websocket'; unused for 'listener'. */
55
73
  route?: string;
74
+ /** Which service this speaks to — slack, whatsapp, discord. It selects the adapter's dialect, not the transport. */
56
75
  platform?: string;
76
+ /** Translates between the platform's message format and pikku's. A factory is called with services, for an adapter that needs a token or a client. */
57
77
  adapter: GatewayAdapter | GatewayAdapterFactory;
78
+ /** The function to run per inbound message. It receives the normalised message, not the platform's raw payload. */
58
79
  func: PikkuFunctionConfig;
80
+ /** Wraps every inbound message: signature verification, tracing, rate limiting. */
59
81
  middleware?: CorePikkuMiddlewareGroup<any, any>;
82
+ /** Filters this gateway in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
60
83
  tags?: string[];
61
84
  /** Unset lets the handler's own `auth` govern; gateway handlers are sessionless by default. */
62
85
  auth?: boolean;
@@ -26,14 +26,18 @@ export type RunHTTPWiringOptions = Partial<{
26
26
  export type HTTPMethod = 'post' | 'get' | 'delete' | 'patch' | 'head' | 'put' | 'options';
27
27
  export type HTTPHeadersSchema = StandardSchemaV1<Record<string, string | string[] | undefined>>;
28
28
  export type HTTPRouteBaseConfig = {
29
+ /** How the body is serialised. Defaults to JSON; `xml` is for routes a caller you do not control insists on. */
29
30
  contentType?: 'xml' | 'json';
31
+ /** Seconds before the request is abandoned. Work that can outlast a request should be dispatched instead, not given a longer timeout. */
30
32
  timeout?: number;
33
+ /** Filters this route in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
31
34
  tags?: string[];
35
+ /** A schema the request headers are validated against, so a missing or malformed header fails before the function body runs. */
32
36
  headers?: HTTPHeadersSchema;
33
37
  };
34
38
  export type CoreHTTPFunction = HTTPRouteBaseConfig & {
35
39
  route: string;
36
- eventChannel?: false;
40
+ /** Sends the returned value as-is rather than JSON-encoding it, for a route whose body is binary or already serialised. */
37
41
  returnsJSON?: false;
38
42
  };
39
43
  export interface PikkuHTTP<In = unknown> {
@@ -41,51 +45,61 @@ export interface PikkuHTTP<In = unknown> {
41
45
  response?: PikkuHTTPResponse;
42
46
  }
43
47
  export type PikkuQuery<T = Record<string, string | undefined>> = Record<string, string | T | null | Array<T | null>>;
44
- export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction extends CorePikkuFunction<In, Out, any, any, any> = CorePikkuFunction<In, Out>, PikkuFunctionSessionless extends CorePikkuFunctionSessionless<In, Out, any, any, any> = CorePikkuFunctionSessionless<In, Out>, PikkuPermission extends CorePikkuPermission<In, any, any> = CorePikkuPermission<In, any, any>, PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<any, any>> = (CoreHTTPFunction & {
48
+ /**
49
+ * The keys every HTTP wiring takes, whatever it is wired to. The three that
50
+ * are not here — `method`, `auth` and `func` — are correlated rather than
51
+ * independent, and live in the unions below.
52
+ */
53
+ type HTTPWiringShared<R extends string, PikkuMiddleware extends CorePikkuMiddleware<any, any>> = CoreHTTPFunction & {
54
+ /**
55
+ * The path this wiring answers on. `:name` marks a parameter, and every
56
+ * parameter in the path must be a key of the function's input schema —
57
+ * a mismatch is a compile error rather than a 404 at runtime.
58
+ */
45
59
  route: R;
46
- method: HTTPMethod;
47
- func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>;
48
- auth?: true;
60
+ /** Wraps every request to this route: auth, tracing, rate limiting. Runs before the permissions on `func`. */
49
61
  middleware?: PikkuMiddleware[];
50
- sse?: undefined;
51
- }) | (CoreHTTPFunction & {
52
- route: R;
53
- method: HTTPMethod;
54
- func: CorePikkuFunctionConfig<PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>;
62
+ };
63
+ /**
64
+ * `auth` decides which kind of function this route can point at, so the two
65
+ * travel together: a route that requires a session gets a function that is
66
+ * handed one, and an open route gets a sessionless function that has none to
67
+ * read. Splitting them would let a function ask for a session the route never
68
+ * establishes.
69
+ */
70
+ type HTTPWiringAuth<In, Out, PikkuFunction extends CorePikkuFunction<In, Out, any, any, any>, PikkuFunctionSessionless extends CorePikkuFunctionSessionless<In, Out, any, any, any>, PikkuPermission extends CorePikkuPermission<In, any, any>, PikkuMiddleware extends CorePikkuMiddleware<any, any>> = {
71
+ /** Whether reaching this route requires a session. Defaults to true — a route is closed unless it says otherwise. */
72
+ auth?: true;
73
+ /** The function to run. It is handed the session this route required. */
74
+ func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>;
75
+ } | {
76
+ /** Whether reaching this route requires a session. Defaults to true — a route is closed unless it says otherwise. */
55
77
  auth?: false;
56
- middleware?: PikkuMiddleware[];
78
+ /** On an open route there is no session, so this must be a sessionless function. */
79
+ func: CorePikkuFunctionConfig<PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>;
80
+ };
81
+ /**
82
+ * `sse` and `query` are each valid on one method only, so the method carries
83
+ * them: streaming is a GET, and naming which input keys arrive in the query
84
+ * string is only a question on a POST, where the rest of the input is a body.
85
+ */
86
+ type HTTPWiringMethod<In> = {
87
+ /** The HTTP method. A route and method together address one wiring. */
88
+ method: HTTPMethod;
57
89
  sse?: undefined;
58
- }) | (CoreHTTPFunction & {
59
- route: R;
60
- method: 'get';
61
- func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>;
62
- auth?: true;
63
- middleware?: PikkuMiddleware[];
64
- sse?: boolean;
65
- }) | (CoreHTTPFunction & {
66
- route: R;
90
+ } | {
91
+ /** The HTTP method. A route and method together address one wiring. */
67
92
  method: 'get';
68
- func: CorePikkuFunctionConfig<PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>;
69
- auth?: false;
70
- middleware?: PikkuMiddleware[];
93
+ /** Streams the response as server-sent events instead of returning it once. GET only. */
71
94
  sse?: boolean;
72
- }) | (CoreHTTPFunction & {
73
- route: R;
95
+ } | {
96
+ /** The HTTP method. A route and method together address one wiring. */
74
97
  method: 'post';
75
- func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>;
76
- auth?: true;
77
- middleware?: PikkuMiddleware[];
98
+ /** Input keys that arrive in the query string rather than the body. POST only, where the body is the default home for input. */
78
99
  query?: Array<keyof In>;
79
100
  sse?: undefined;
80
- }) | (CoreHTTPFunction & {
81
- route: R;
82
- method: 'post';
83
- func: CorePikkuFunctionConfig<PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>;
84
- auth?: false;
85
- middleware?: PikkuMiddleware[];
86
- query?: Array<keyof In>;
87
- sse?: undefined;
88
- });
101
+ };
102
+ export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction extends CorePikkuFunction<In, Out, any, any, any> = CorePikkuFunction<In, Out>, PikkuFunctionSessionless extends CorePikkuFunctionSessionless<In, Out, any, any, any> = CorePikkuFunctionSessionless<In, Out>, PikkuPermission extends CorePikkuPermission<In, any, any> = CorePikkuPermission<In, any, any>, PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<any, any>> = HTTPWiringShared<R, PikkuMiddleware> & HTTPWiringAuth<In, Out, PikkuFunction, PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware> & HTTPWiringMethod<In>;
89
103
  export type HTTPFunctionMetaInputTypes = {
90
104
  params?: string;
91
105
  query?: string;
@@ -40,36 +40,63 @@ export type MCPPromptMeta = Record<string, Omit<CoreMCPPrompt, 'func' | 'middlew
40
40
  middleware?: MiddlewareMetadata[];
41
41
  }>;
42
42
  export type CoreMCPResource<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>>, PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
43
+ /** How the client addresses this resource. `{name}` marks a parameter, and every parameter must be a key of the function's input schema. */
43
44
  uri: string;
45
+ /** The name a human sees in a client's resource list. */
44
46
  title: string;
47
+ /** What the resource holds, written for the model deciding whether to read it. */
45
48
  description: string;
49
+ /** A one-line description for listings, where the full `description` is too long. */
46
50
  summary?: string;
51
+ /** Names of error classes this may throw, so the client is told which failures are its own fault. */
47
52
  errors?: string[];
53
+ /** The media type of what the function returns, so the client knows whether it is text, JSON or an image. */
48
54
  mimeType?: string;
55
+ /** Size in bytes, where it is known ahead of the read. A client uses it to decide whether to fetch at all. */
49
56
  size?: number;
57
+ /** Whether the function returns the content in chunks rather than at once. */
50
58
  streaming?: boolean;
59
+ /** The function to run. It is sessionless: an MCP client is not a logged-in user. */
51
60
  func: PikkuFunctionConfig;
61
+ /** Filters this wiring in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
52
62
  tags?: string[];
63
+ /** Wraps every call: tracing, rate limiting, whatever the transport does not do. */
53
64
  middleware?: PikkuMiddleware[];
54
65
  };
55
66
  export type CoreMCPTool<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>>, PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
67
+ /** How the model calls this tool. It is the tool's identity, so renaming it breaks any client that already knows it. */
56
68
  name: string;
69
+ /** The name a human sees, where the calling `name` is not readable. */
57
70
  title?: string;
71
+ /** What the tool does and when to reach for it. This is what the model decides on, so it earns more care than the rest of this object. */
58
72
  description?: string;
73
+ /** A one-line description for listings, where the full `description` is too long. */
59
74
  summary?: string;
75
+ /** Names of error classes this may throw, so the client is told which failures are its own fault. */
60
76
  errors?: string[];
77
+ /** The function to run. It is sessionless: an MCP client is not a logged-in user. */
61
78
  func: PikkuFunctionConfig;
79
+ /** Filters this wiring in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
62
80
  tags?: string[];
81
+ /** Whether the function returns its result in chunks rather than at once. */
63
82
  streaming?: boolean;
83
+ /** Wraps every call: tracing, rate limiting, whatever the transport does not do. */
64
84
  middleware?: PikkuMiddleware[];
65
85
  };
66
86
  export type CoreMCPPrompt<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, MCPPromptResponse>>, PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
87
+ /** How the client asks for this prompt. */
67
88
  name: string;
89
+ /** What the prompt is for, written for the human picking it out of a list. */
68
90
  description: string;
91
+ /** A one-line description for listings, where the full `description` is too long. */
69
92
  summary?: string;
93
+ /** Names of error classes this may throw, so the client is told which failures are its own fault. */
70
94
  errors?: string[];
95
+ /** The function to run. It is sessionless: an MCP client is not a logged-in user. */
71
96
  func: PikkuFunctionConfig;
97
+ /** Filters this wiring in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
72
98
  tags?: string[];
99
+ /** Wraps every call: tracing, rate limiting, whatever the transport does not do. */
73
100
  middleware?: PikkuMiddleware[];
74
101
  };
75
102
  export type JsonRpcRequest = {
@@ -110,11 +110,17 @@ export type QueueWorkersMeta = Record<string, CommonWireMeta & {
110
110
  config?: PikkuWorkerConfig;
111
111
  }>;
112
112
  export type CoreQueueWorker<PikkuFunctionConfig extends CorePikkuFunctionConfig<any, any, any> = CorePikkuFunctionConfig<any, any, any>> = {
113
+ /** The queue this worker consumes. Whoever enqueues a job names the same string, so it is the contract between producer and consumer. */
113
114
  name: string;
115
+ /** The function to run per job. Its `input` schema is the job payload's schema — a job that does not match is rejected before the body runs. */
114
116
  func: PikkuFunctionConfig;
117
+ /** Concurrency, retry and backoff, passed through to the queue service backing this worker. Defaults come from the service, not from here. */
115
118
  config?: PikkuWorkerConfig;
119
+ /** Names of error classes that mean the job is bad rather than the run — thrown, they fail the job permanently instead of being retried. */
116
120
  errors?: string[];
121
+ /** Filters this worker in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
117
122
  tags?: string[];
123
+ /** Wraps every job. There is no request to read from, so this is for tracing, locking and teardown rather than auth. */
118
124
  middleware?: PikkuFunctionConfig['middleware'];
119
125
  };
120
126
  export interface PikkuQueue {