Voice and chat agents that understand people and act safely.

Callers talk naturally, and the system maps what they mean to the right action. A fast decision model reads what was said and answers typed questions; it never writes the reply. What the agent says and does is predictable: the lines are fixed, code decides what happens next, and a policy gate checks every action. Replies are fast, because a decision is a short typed question, not a composed answer.

GitHub repository Quick start Read the design

An open-source TypeScript framework. Apache-2.0. Pre-release. Runs on a laptop with no keys.

How it works

Three parts, each with one job. The model never decides, and the code never guesses.

  1. The model understands

    A fast decision model reads what the caller said and answers typed questions: is this a request to reschedule, which of these three appointments, what date was given. It returns judgments with probabilities, never generated text. Callers can say it in their own words, give three answers at once or change their mind.

  2. Code decides

    Deterministic code owns the dialog: which form is active, which slot to ask next, when to read back, when to hand off to a person. The same judgments and state always lead to the same step, and every line the caller hears is a fixed, approved line filled with checked values.

  3. The gate checks every action

    Before any tool runs, a policy gate checks the caller's identity level, whose record it is, exact confirmation, role and attempt limits. The gate does not read the conversation, so no one can talk past it. Every decision, allowed or refused, goes to a hash-chained audit log.

  • Reproducible. Replay a call against its recorded model answers and get the same outcome.
  • Explainable. The audit names the rule that passed or failed, and what it compared.
  • Not steerable by the caller. What a caller says is data the model classifies, never instructions the code follows.
  • Tunable. Thresholds are numbers, adjusted by measurement rather than by rewording a prompt.

An app is a folder

YAML for what is data: intents, forms, slots, prompts, policy, identity, locales. TypeScript for what runs: tools, form hooks, rules of your own. defineApp joins the two. Most slots are configuration: name a type from the slot library (digits, choice, date, birthdate, name, record, text) and give it a few options.

apps/utility/slots.yaml (excerpt)
account:
  type: digits
  noun: account
  length: 8
  group: [4, 4]
  keypad: true

firstDate:
  type: date
  range: future
  keypad: true
apps/utility/policy.yaml (excerpt)
actions:
  setUpPlan:
    say: set up a payment arrangement
    level: 2
    rules:
      - identity
      - role: { manager: person }
      - scope: { param: accountId }
      - confirmed: [accountId, place, symptom, count, firstDate, total]
      - limit: { field: total, min: 0.01, max: amountDue(accountId) }
      - dateInRange: { field: firstDate, notBefore: today, notAfter: today+30 }

pnpm check says what to fix

It reads every YAML file against its schema and cross-checks the folder against the code: a slot a form names that does not exist, a line the engine needs that no prompt provides, a tool with no policy. Each problem is one line, file:line:column  path  message  ->  fix, written for a person or an assistant to act on. Here is the first line it printed for a misspelt slot name in the utility's forms.yaml, and the run after the fix:

$ pnpm check
forms.yaml:11:20  forms.report_outage.slots[1]  slot "symptm" is not defined  ->  rename it to "symptom", or add "symptm:" to slots.yaml (a library type, or { type: code } with the slot in src/app.ts (code.slots.symptm))

$ pnpm check
apps/clinic: ok
apps/utility: ok

Built for AI coding assistants

The repository is written to be read by an AI coding assistant as well as by you. A root CLAUDE.md gives the roles, the commands and the rules, llms.txt indexes the docs, and every app has a CLAUDE.md of its own.

The create-app skill

The procedure an assistant follows to turn a paragraph into an app: a worksheet, the mapping onto slot types, policy and identity, the scaffold, the corpus and scripted calls, then the checks until they pass. It comes with a page of verified patterns and a corpus guide.

Read the skill

pnpm create-app <name>

The scaffold the skill calls. It writes a small app under apps/<name> that passes pnpm check, its own tests and its regression as created. Add --identity for an app that verifies its callers.

Where to start

We tried it

Two fresh assistants, with no context from the people who built the framework, were each given one paragraph (a fictional electric utility, a fictional transit agency) and told to use the skill. Each built a working app, in about 15 and 11 minutes by their own count, with pnpm check and the scripted calls green. Both caught the same real hole in their own policy by reading the policy matrix: a delegate could have had a one-time code texted to someone else's phone. The engine now refuses that for every app. Every stumble they logged was fixed or recorded with its reason.

Read the trials and their logs

Compliance can read it

Policy is a file, not code. policy.yaml names the rules each action runs, and identity.yaml says who the app serves and how a caller proves who they are. Both compile to what the gate runs. Three generated pages make a change readable by someone who does not write code:

  • POLICY.md, the policy card: each action and each rule in plain English, with diagrams, written from the same compiled policy the gate runs.
  • policy.matrix: for every action and every kind of caller, the verdict and its reason, so a policy change shows up as a diff of what the gate decides.
  • APP-MAP.md: the app's intents, forms, slots, actions and rules as diagrams.

Each is written by a command and compared by a test, never written by CI. CODEOWNERS makes a change to the policy files and these pages need compliance's review.

From apps/utility/policy.matrix: who may set up a payment arrangement. A customer needs the one-time code (level 2), their own account and an exact read-back; a property manager goes to a person.
setUpPlan · level 2 · identity, role(manager person), scope(accountId), confirmed(accountId, place, symptom, count, firstDate, total), limit(total), dateInRange(firstDate)
  anonymous           STEP_UP to 2
  subject@1           as anonymous
  subject@2           subject own, fields exact, confirmed none|mismatch  BLOCK confirmation
                      subject own, fields exact, confirmed match          ALLOW
                      subject own, fields extra|missing                   BLOCK confirmation
                      subject inScope|outOfScope|unknown|empty            BLOCK scope
  delegate:manager    NEEDS_HUMAN role-person
  delegate:manager@1  BLOCK identity
  unlisted-role       BLOCK role

Examples

Two fictional organizations. Every name, date and number in them is made up, and neither needs a key to run.

Example Family Practice

A clinic's appointment line: a caller can schedule, reschedule, cancel or confirm an appointment with one of eight providers. The first app to read. It has every slot configured from the slot library, writes that go through the gate after a read-back, 241 labelled utterances and 89 scripted calls.

Example Power & Light

An electric utility's phone and chat line, built by an AI coding assistant from one paragraph. Report an outage, hear a balance after verifying, or set up a payment arrangement behind a one-time code. A property manager acts for their buildings. The app to read for identity and policy.

Quick start

Requires Node 22.19 or later and pnpm. Nothing needs a key: with no model attached, each app answers from its own labelled examples.

  1. Clone the repository and install.

    git clone https://github.com/DialogWright/dialogwright.git
    cd dialogwright
    pnpm install
  2. Check every app folder. It prints apps/clinic: ok when there is nothing to fix.

    pnpm check
  3. Talk to the clinic in a text console.

    pnpm --filter @dialogwright/example-clinic cli --client heuristic
  4. Start an app of your own. It passes the checks as created; add --identity for one that verifies callers.

    pnpm create-app <name>

Then read Authoring an app, or give an AI coding assistant a paragraph describing your app and point it at the create-app skill.

Pre-release: APIs will change, and the package is not on npm yet. Next on the roadmap, coming soon: a knowledge base of approved answers, chosen by the model and spoken as approved, never generated.