Skip to content

Functions

Functions run a Verb as code, a model call, an agent or a human Task, behind one signature.

Functions run a Verb as code, a model call, an Agent or a Human Task, behind the Verb's own typed signature. The caller never sees which kind ran.

Contract

The Verb is the contract and the Function is replaceable. One fn() per Verb binds the Verb's generated input and output types. Moving work from a person to an Agent is a one-line edit no caller sees.

KindRuns asDeclares
codeDeterministic code: same input, same outputcode(input, ctx)
generativeOne model call that writes, extracts or classifiesinstructions, model
agenticAn Agent that plans and calls Tools over many stepsinstructions, tools, model, maxSteps, budget
humanA person working a Task in an App, Mailbox or Channelrole, sla, escalateTo, form
GuaranteeDetail
Typedfn() takes the Verb's input type and must return its output type; a mismatch fails at compile time
Cascadecascade lists kinds in order, and ctx.decline passes the work to the next; each hand-off is an Event
RecordedThe Action keeps the input, the output, the cost and Function.version
Boundedtools are Verbs, so Permissions and Limits apply to each call; budget caps one Action's spend
Durablectx.step, ctx.sleep and ctx.waitForEvent survive restarts, as in any Workflow
TestedThe cases beside the Function pass before its Change merges

Write a Function

The file name binds the Function to its Verb. fixwell.app settles warranty claims; it declares a Claim Noun. Code settles small claims and declines the rest to the Customer Lead Role.

TypeScript
// nouns/Claim.settle.ts
import { fn } from 'startup-builder'

export default fn('Claim.settle', {
  cascade: ['code', 'human'],
  async code({ claim }, ctx) {
    const c = await ctx.$.Claim.get({ id: claim })
    if (c.amount.amount > 200) return ctx.decline('Over the automatic settlement line')
    await ctx.step('refund', () => ctx.$.Order.refund({ order: c.order, amount: c.amount, reason: 'Warranty claim' }))
    return { claim: c }
  },
  role: 'customer-lead',
  sla: 'PT4H',
  escalateTo: 'operations-lead',
})

The human kind needs no form: one generates from the Verb's input and output.

The context

Every kind receives the same ctx.

MemberDoes
ctx.$The client, acting as this Function's Worker, with on set to this Action
ctx.step, ctx.sleep, ctx.waitForEventDurable steps and waits; a wait shows as pending with reason awaitingTime
ctx.askRuns Action.ask and waits for the answer, with reason awaitingInput
ctx.approveOpens an Approval and waits, with reason awaitingAction
ctx.declinePasses to the next kind in the cascade, with a reason
ctx.budgetSpend so far against the Function's budget and the Mandate's Limits
ctx.emitStreams a partial result to a caller in send

An agentic Function

TypeScript
// nouns/Claim.assess.ts
export default fn('Claim.assess', {
  kind: 'agentic',
  instructions: 'Decide whether the warranty covers the claim. Cite the photo and the term that decide it.',
  tools: ['Claim.get', 'Order.get', 'Claim.settle'],
  model: 'review',
  maxSteps: 20,
  budget: { amount: 2, currency: 'USD' },
})

Cases

A case is { input, expect, evaluators, clock, fixtures }; the last two are optional. Code asserts on expect, and an Evaluator grades agentic and human output.

TypeScript
// nouns/Claim.settle.test.ts
import { cases } from 'startup-builder'

export default cases('Claim.settle', [
  { input: { claim: 'claim_small' }, expect: { claim: { status: 'settled' } } },
  { input: { claim: 'claim_large' }, expect: { claim: { status: 'open' } }, evaluators: ['settlement-reason'] },
])

Verbs

VerbObjectEvent
Function.createFunctionFunction.created
Function.getFunctionNone.

Interfaces

Example

matchbook.ai reconciles payments for agencies. Code matches by amount and date, a model reads remittance notes, an Agent chases what remains, and an accountant approves each write-off. Its Customers call one Verb, Payment.match, and see none of this.