@pikku/core 0.12.91 → 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.
- package/CHANGELOG.md +102 -0
- package/dist/errors/error-handler.d.ts +11 -0
- package/dist/errors/error-handler.js +8 -0
- package/dist/errors/serialized-error.d.ts +3 -0
- package/dist/function/function-runner.d.ts +1 -1
- package/dist/function/function-runner.js +3 -3
- package/dist/function/functions.types.d.ts +33 -21
- package/dist/middleware/auth-apikey.d.ts +7 -0
- package/dist/middleware/auth-apikey.js +6 -0
- package/dist/middleware/auth-bearer.d.ts +3 -0
- package/dist/middleware/auth-bearer.js +2 -0
- package/dist/middleware/auth-cookie.d.ts +9 -0
- package/dist/middleware/auth-cookie.js +6 -4
- package/dist/middleware/cors.d.ts +8 -0
- package/dist/middleware/cors.js +2 -0
- package/dist/middleware/middleware-factories.d.ts +14 -0
- package/dist/middleware/middleware-factories.js +14 -0
- package/dist/services/local-secrets.js +1 -1
- package/dist/services/persona-sign-in.d.ts +0 -2
- package/dist/services/persona-sign-in.js +15 -66
- package/dist/services/personas-service.d.ts +4 -0
- package/dist/services/personas-service.js +4 -0
- package/dist/services/secret-service.d.ts +0 -1
- package/dist/testing/service-tests/secret-service-tests.js +1 -1
- package/dist/types/core.types.d.ts +5 -0
- package/dist/wirings/addon/wire-addon.d.ts +17 -15
- package/dist/wirings/addon/wire-addon.js +6 -0
- package/dist/wirings/addon/wire-remote-addon.d.ts +4 -0
- package/dist/wirings/addon/wire-remote-addon.js +4 -0
- package/dist/wirings/agent/agent-prepare.js +2 -2
- package/dist/wirings/agent/agent.types.d.ts +31 -27
- package/dist/wirings/agent-scorer/agent-scorer.d.ts +4 -0
- package/dist/wirings/agent-scorer/agent-scorer.js +4 -0
- package/dist/wirings/channel/channel.types.d.ts +11 -0
- package/dist/wirings/cli/cli.types.d.ts +43 -1
- package/dist/wirings/cli/define-cli-commands.d.ts +6 -1
- package/dist/wirings/cli/define-cli-commands.js +6 -1
- package/dist/wirings/credential/credential.types.d.ts +7 -0
- package/dist/wirings/gateway/gateway.types.d.ts +23 -0
- package/dist/wirings/http/http.types.d.ts +51 -37
- package/dist/wirings/mcp/mcp.types.d.ts +27 -0
- package/dist/wirings/queue/queue.types.d.ts +6 -0
- package/dist/wirings/scheduler/scheduler.types.d.ts +5 -0
- package/dist/wirings/secret/secret.types.d.ts +9 -14
- package/dist/wirings/secret/secret.types.js +7 -0
- package/dist/wirings/trigger/trigger.types.d.ts +4 -1
- package/dist/wirings/variable/variable.types.d.ts +14 -13
- package/dist/wirings/variable/variable.types.js +7 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +7 -0
- package/dist/wirings/workflow/scenario-cookie-jar.d.ts +6 -0
- package/dist/wirings/workflow/scenario-cookie-jar.js +6 -0
- package/dist/wirings/workflow/scenario-poll.d.ts +6 -0
- package/dist/wirings/workflow/scenario-poll.js +6 -0
- package/dist/wirings/workflow/scenario-step-guards.d.ts +6 -0
- package/dist/wirings/workflow/scenario-step-guards.js +6 -0
- package/dist/wirings/workflow/workflow-errors.d.ts +4 -0
- package/dist/wirings/workflow/workflow-errors.js +4 -0
- package/package.json +1 -1
- package/src/app-leaf-surface.test.ts +2 -2
- package/src/errors/error-handler.ts +11 -0
- package/src/errors/serialized-error.ts +3 -0
- package/src/function/function-runner.ts +4 -4
- package/src/function/functions.types.ts +33 -21
- package/src/middleware/auth-apikey.ts +7 -0
- package/src/middleware/auth-bearer.ts +3 -0
- package/src/middleware/auth-cookie.ts +9 -4
- package/src/middleware/cors.ts +8 -0
- package/src/middleware/middleware-factories.ts +14 -0
- package/src/services/local-secrets.test.ts +2 -2
- package/src/services/local-secrets.ts +1 -1
- package/src/services/persona-sign-in.test.ts +28 -34
- package/src/services/persona-sign-in.ts +16 -88
- package/src/services/personas-service.ts +4 -0
- package/src/services/secret-service.ts +0 -1
- package/src/testing/service-tests/secret-service-tests.ts +1 -1
- package/src/types/core.types.ts +5 -0
- package/src/wirings/addon/addon-auth-tags.test.ts +4 -4
- package/src/wirings/addon/wire-addon.ts +17 -15
- package/src/wirings/addon/wire-remote-addon.ts +4 -0
- package/src/wirings/agent/agent-authorization.test.ts +7 -3
- package/src/wirings/agent/agent-prepare.ts +2 -2
- package/src/wirings/agent/agent.types.ts +31 -27
- package/src/wirings/agent-scorer/agent-scorer.ts +4 -0
- package/src/wirings/channel/channel.types.ts +11 -0
- package/src/wirings/cli/cli.types.ts +44 -1
- package/src/wirings/cli/define-cli-commands.ts +6 -1
- package/src/wirings/credential/credential.types.ts +7 -0
- package/src/wirings/gateway/gateway-authorization.test.ts +3 -3
- package/src/wirings/gateway/gateway.types.ts +23 -0
- package/src/wirings/http/http-runner-addon-ref.test.ts +1 -1
- package/src/wirings/http/http.types.ts +94 -69
- package/src/wirings/mcp/mcp.types.ts +27 -0
- package/src/wirings/queue/queue.types.ts +6 -0
- package/src/wirings/scheduler/scheduler.types.ts +5 -0
- package/src/wirings/secret/secret.types.ts +9 -14
- package/src/wirings/trigger/trigger.types.ts +4 -1
- package/src/wirings/variable/variable.types.ts +14 -13
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +7 -0
- package/src/wirings/workflow/scenario-cookie-jar.ts +6 -0
- package/src/wirings/workflow/scenario-poll.ts +6 -0
- package/src/wirings/workflow/scenario-step-guards.ts +6 -0
- package/src/wirings/workflow/workflow-errors.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -31,7 +31,7 @@ export const defineSecretServiceTests = (name, secretService) => {
|
|
|
31
31
|
test('getSecret throws for missing key', async () => {
|
|
32
32
|
const service = await factory({ key: kek });
|
|
33
33
|
await assert.rejects(() => service.getSecret('nonexistent'), {
|
|
34
|
-
message: 'Requested secret not found',
|
|
34
|
+
message: 'Requested secret not found: nonexistent',
|
|
35
35
|
});
|
|
36
36
|
});
|
|
37
37
|
test('setSecret upserts existing key', async () => {
|
|
@@ -271,10 +271,15 @@ export type ServerLifecycle<SingletonServices extends CoreSingletonServices = Co
|
|
|
271
271
|
export type CommonWireMeta = {
|
|
272
272
|
pikkuFuncId: string;
|
|
273
273
|
packageName?: string;
|
|
274
|
+
/** A human name for this wiring, shown wherever it is listed rather than called. */
|
|
274
275
|
title?: string;
|
|
276
|
+
/** Filters this wiring in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
|
|
275
277
|
tags?: string[];
|
|
278
|
+
/** A one-line description for listings, where the full `description` is too long. */
|
|
276
279
|
summary?: string;
|
|
280
|
+
/** What this does, for whoever is reading the wiring rather than writing it. */
|
|
277
281
|
description?: string;
|
|
282
|
+
/** Names of error classes this may throw, so each one's registered status is used instead of a 500. */
|
|
278
283
|
errors?: string[];
|
|
279
284
|
middleware?: MiddlewareMetadata[];
|
|
280
285
|
permissions?: PermissionMetadata[];
|
|
@@ -1,34 +1,30 @@
|
|
|
1
1
|
import type { CorePikkuMiddleware } from '../../middleware/middleware.types.js';
|
|
2
2
|
export type WireAddonConfig = {
|
|
3
|
+
/** How this instance is addressed. One package may be wired more than once, and the name is what tells the instances apart. */
|
|
3
4
|
name: string;
|
|
5
|
+
/** The npm package the addon ships in. */
|
|
4
6
|
package: string;
|
|
7
|
+
/** Where to reach the addon when it runs as its own service rather than in-process. */
|
|
5
8
|
rpcEndpoint?: string;
|
|
9
|
+
/** Requires a session for every function in the addon, whatever each one declares. Gates an addon whose functions are individually open. */
|
|
6
10
|
auth?: boolean;
|
|
11
|
+
/** Offers the addon's functions to MCP clients as tools, without wiring each one. */
|
|
7
12
|
mcp?: boolean;
|
|
13
|
+
/** Filters this addon in and out of a build — see the `tags` option on `pikku all`. It has no effect at runtime. */
|
|
8
14
|
tags?: string[];
|
|
9
15
|
/** Required of every function in the addon, on top of the function's own. */
|
|
10
16
|
scopes?: string[];
|
|
17
|
+
/** Points a secret the addon reads at a different key in this deployment, so two instances can hold different credentials. */
|
|
11
18
|
secretOverrides?: Record<string, string>;
|
|
19
|
+
/** Points a variable the addon reads at a different key in this deployment. */
|
|
12
20
|
variableOverrides?: Record<string, string>;
|
|
21
|
+
/** Points a credential the addon reads at a different key in this deployment. */
|
|
13
22
|
credentialOverrides?: Record<string, string>;
|
|
14
|
-
/**
|
|
15
|
-
* Secrets this instance may read on top of the ones it declared, named as the
|
|
16
|
-
* addon reads them — the scope check runs before `secretOverrides` renames
|
|
17
|
-
* them, so an overridden secret is named here by its addon-side key. Listing
|
|
18
|
-
* one in `secretOverrides` grants it too.
|
|
19
|
-
*
|
|
20
|
-
* For an addon whose secret names come off its input rather than its own
|
|
21
|
-
* source, this is how the host lends names the addon could not declare.
|
|
22
|
-
*/
|
|
23
|
+
/** Extra secrets this instance may read, named as the addon reads them — the scope check runs before `secretOverrides` renames them. */
|
|
23
24
|
secretGrants?: string[];
|
|
24
25
|
/** Credentials this instance may read on top of the ones it declared. */
|
|
25
26
|
credentialGrants?: string[];
|
|
26
|
-
/**
|
|
27
|
-
* Hands this instance the whole `SecretService` instead of one scoped to the
|
|
28
|
-
* secrets it declared. The value is the reason, recorded in the deploy
|
|
29
|
-
* manifest — an addon that names secrets at runtime cannot be scoped, and
|
|
30
|
-
* only the consuming app, never the addon, can grant it.
|
|
31
|
-
*/
|
|
27
|
+
/** Hands over the whole `SecretService` unscoped. The value is the reason, recorded in the deploy manifest. */
|
|
32
28
|
globalSecrets?: string;
|
|
33
29
|
/**
|
|
34
30
|
* Hands this instance the whole `CredentialService` instead of one narrowed
|
|
@@ -37,6 +33,12 @@ export type WireAddonConfig = {
|
|
|
37
33
|
*/
|
|
38
34
|
globalCredentials?: string;
|
|
39
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Installs an addon into this project: its functions, wirings and scopes become
|
|
38
|
+
* part of the app, under the namespace and options given here.
|
|
39
|
+
*
|
|
40
|
+
* @example snippet: addonWiring
|
|
41
|
+
*/
|
|
40
42
|
export declare const wireAddon: (config: WireAddonConfig) => void;
|
|
41
43
|
/**
|
|
42
44
|
* knowledge: decisions/security/addon-scopes-are-resolved-where-the-function-runs.md
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { pikkuState } from '../../pikku-state.js';
|
|
2
2
|
import { getTagGroups } from '../../utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Installs an addon into this project: its functions, wirings and scopes become
|
|
5
|
+
* part of the app, under the namespace and options given here.
|
|
6
|
+
*
|
|
7
|
+
* @example snippet: addonWiring
|
|
8
|
+
*/
|
|
3
9
|
export const wireAddon = (config) => {
|
|
4
10
|
pikkuState(null, 'addons', 'packages').set(config.name, {
|
|
5
11
|
package: config.package,
|
|
@@ -19,4 +19,8 @@ export type WireRemoteAddonConfig = {
|
|
|
19
19
|
remoteName?: (fn: string) => string;
|
|
20
20
|
tags?: string[];
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Installs an addon that runs as its own deployed service: the contract is
|
|
24
|
+
* local, the calls go over the wire to the addon's own host.
|
|
25
|
+
*/
|
|
22
26
|
export declare const wireRemoteAddon: (config: WireRemoteAddonConfig) => void;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { pikkuState } from '../../pikku-state.js';
|
|
2
|
+
/**
|
|
3
|
+
* Installs an addon that runs as its own deployed service: the contract is
|
|
4
|
+
* local, the calls go over the wire to the addon's own host.
|
|
5
|
+
*/
|
|
2
6
|
export const wireRemoteAddon = (config) => {
|
|
3
7
|
pikkuState(null, 'addons', 'packages').set(config.name, {
|
|
4
8
|
package: config.package,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PikkuError } from '../../errors/error-handler.js';
|
|
2
2
|
import { checkAuthPermissions, runPermissions, } from '../../permissions.js';
|
|
3
3
|
import { AIProviderNotConfiguredError } from '../../errors/errors.js';
|
|
4
|
-
import { ForbiddenError } from '../../errors/errors.js';
|
|
4
|
+
import { ForbiddenError, MissingSessionError } from '../../errors/errors.js';
|
|
5
5
|
import { verifyScopes } from '../../scopes.js';
|
|
6
6
|
import { pikkuState, getSingletonServices } from '../../pikku-state.js';
|
|
7
7
|
import { createMiddlewareSessionWireProps } from '../../services/user-session-service.js';
|
|
@@ -166,7 +166,7 @@ export async function assertAgentAuthorized(agent, params, packageName) {
|
|
|
166
166
|
? await params.sessionService.get()
|
|
167
167
|
: undefined;
|
|
168
168
|
if (agent.auth === true && !session) {
|
|
169
|
-
throw new
|
|
169
|
+
throw new MissingSessionError('Authentication required');
|
|
170
170
|
}
|
|
171
171
|
verifyScopes(agent.scopes, session);
|
|
172
172
|
const singletonServices = getSingletonServices();
|
|
@@ -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
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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;
|