@alexkroman1/aai-cli 13.3.0 → 14.0.0

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 (134) hide show
  1. package/dist/{_agent-Clbq3a4h.mjs → _agent-BzUeqOdj.mjs} +32 -9
  2. package/dist/_agent.d.ts +23 -0
  3. package/dist/{_bundler-DEkjO1d8.mjs → _bundler-DolUCMxu.mjs} +8 -7
  4. package/dist/_cli-common.d.ts +45 -6
  5. package/dist/{_config-ZY2euhdh.mjs → _config-DYzC6WMD.mjs} +2 -2
  6. package/dist/_dev-env.d.ts +1 -1
  7. package/dist/{_dev-server-C13xuGB1.mjs → _dev-server-CSMqF8PN.mjs} +20 -22
  8. package/dist/_dev-vite-config.d.ts +3 -3
  9. package/dist/{_init-Ci4QGjp8.mjs → _init-CQ8idAwo.mjs} +9 -23
  10. package/dist/{_output-DbJt9gsl.mjs → _output-DBP9Op_d.mjs} +22 -13
  11. package/dist/_output.d.ts +10 -9
  12. package/dist/{_preflight-B2laWPdU.mjs → _preflight-CcAR577V.mjs} +2 -2
  13. package/dist/_resource-commands.d.ts +9 -9
  14. package/dist/{_server-common-BV4_CrP3.mjs → _server-common-vILJp3it.mjs} +1 -1
  15. package/dist/{_slug-api-DlKpj91X.mjs → _slug-api-CGvc2YSQ.mjs} +18 -9
  16. package/dist/_slug-api.d.ts +19 -0
  17. package/dist/{_studio-DTi3EdXQ.mjs → _studio-DnR_BqFp.mjs} +7 -3
  18. package/dist/_studio-commands.d.ts +13 -13
  19. package/dist/{_templates-V4l5sZJy.mjs → _templates-CK4oKoeX.mjs} +3 -3
  20. package/dist/{_typecheck-gate-sV4N5F93.mjs → _typecheck-gate-U3jcil8L.mjs} +3 -2
  21. package/dist/_typecheck-gate.d.ts +3 -1
  22. package/dist/{_utils-Dhd058D9.mjs → _utils-D5JGcjiW.mjs} +43 -1
  23. package/dist/_utils.d.ts +35 -0
  24. package/dist/{_workflow-determinism-C9l8uC2R.mjs → _workflow-determinism-CvpqMpdh.mjs} +18 -26
  25. package/dist/_workflow-determinism.d.ts +3 -3
  26. package/dist/{build-CWyFGe-W.mjs → build-Mxk8gWvX.mjs} +46 -7
  27. package/dist/build.d.ts +5 -0
  28. package/dist/cli.d.ts +1 -2
  29. package/dist/cli.mjs +117 -135
  30. package/dist/{client-bundler-CT-_26c5.mjs → client-bundler-BJgREAh6.mjs} +1 -1
  31. package/dist/client-bundler.mjs +1 -1
  32. package/dist/{delete-RqlQZ6Y_.mjs → delete-BdFnbnVR.mjs} +4 -4
  33. package/dist/{deploy-B7f3CK7v.mjs → deploy-uAJ4NukN.mjs} +11 -11
  34. package/dist/{dev-DamXieW_.mjs → dev-DApPSaE_.mjs} +3 -3
  35. package/dist/{eval-h3DTVfRw.mjs → eval-BK47A_K5.mjs} +3 -3
  36. package/dist/{init-D-vFrV8h.mjs → init-DukDxECd.mjs} +11 -19
  37. package/dist/{login-CDiemNmC.mjs → login-DaLrPOFg.mjs} +4 -6
  38. package/dist/{logs-DGwPNCzB.mjs → logs-7BRwq0cG.mjs} +21 -14
  39. package/dist/project-config.mjs +1 -1
  40. package/dist/scaffold/CLAUDE.md +84 -56
  41. package/dist/scaffold/package.json +4 -4
  42. package/dist/scaffold/server.mjs +129 -25
  43. package/dist/{secret-V1w7y9-j.mjs → secret-Cjp912yF.mjs} +5 -5
  44. package/dist/secret.d.ts +8 -6
  45. package/dist/{studio-BpMCfUNx.mjs → studio-CpHlNHUZ.mjs} +13 -15
  46. package/dist/templates/call-audit/agent.test.ts +11 -6
  47. package/dist/templates/call-audit/agent.ts +2 -2
  48. package/dist/templates/call-audit/client.tsx +27 -42
  49. package/dist/templates/call-audit/workflows/audit.ts +7 -5
  50. package/dist/templates/call-audit/workflows/ingest.ts +7 -7
  51. package/dist/templates/call-audit/workflows/media.ts +12 -37
  52. package/dist/templates/call-audit/workflows/summarize.ts +8 -8
  53. package/dist/templates/call-audit/workflows/sync-api.ts +3 -3
  54. package/dist/templates/dispatch-center/agent.test.ts +7 -7
  55. package/dist/templates/dispatch-center/client.tsx +53 -61
  56. package/dist/templates/dispatch-center/shared.ts +0 -8
  57. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +4 -1
  58. package/dist/templates/embedded-assets/agent.eval.test.ts +4 -2
  59. package/dist/templates/health-assistant/agent.eval.test.ts +4 -2
  60. package/dist/templates/infocom-adventure/client.tsx +40 -36
  61. package/dist/templates/link-digest/agent.test.ts +3 -3
  62. package/dist/templates/link-digest/agent.ts +2 -2
  63. package/dist/templates/link-digest/client.tsx +27 -17
  64. package/dist/templates/link-digest/workflows/digest.ts +13 -16
  65. package/dist/templates/night-owl/agent.eval.test.ts +4 -2
  66. package/dist/templates/night-owl/client.tsx +8 -2
  67. package/dist/templates/pipeline-simple/agent.test.ts +2 -2
  68. package/dist/templates/pizza-ordering/client.tsx +5 -4
  69. package/dist/templates/pizza-ordering/shared.ts +3 -8
  70. package/dist/templates/pizza-ordering/tools/add_pizza.ts +3 -2
  71. package/dist/templates/pizza-ordering/tools/place_order.ts +3 -2
  72. package/dist/templates/pizza-ordering/tools/remove_pizza.ts +3 -2
  73. package/dist/templates/pizza-ordering/tools/update_pizza.ts +3 -2
  74. package/dist/templates/pizza-ordering/tools/view_order.ts +3 -2
  75. package/dist/templates/plan-and-execute/agent.test.ts +10 -5
  76. package/dist/templates/plan-and-execute/client.tsx +2 -2
  77. package/dist/templates/podcast-digest/agent.test.ts +20 -11
  78. package/dist/templates/podcast-digest/client.tsx +16 -22
  79. package/dist/templates/podcast-digest/workflows/digest.ts +21 -19
  80. package/dist/templates/podcast-digest/workflows/feeds.ts +61 -33
  81. package/dist/templates/podcast-digest/workflows/slack.ts +7 -7
  82. package/dist/templates/recap-workflow/agent.eval.test.ts +21 -13
  83. package/dist/templates/recap-workflow/agent.test.ts +27 -23
  84. package/dist/templates/recap-workflow/workflows/recap.ts +24 -28
  85. package/dist/templates/redline/agent.eval.test.ts +5 -8
  86. package/dist/templates/redline/agent.test.ts +1 -1
  87. package/dist/templates/redline/client.tsx +36 -34
  88. package/dist/templates/redline/workflows/redline.ts +17 -17
  89. package/dist/templates/research-workflow/agent.eval.test.ts +5 -5
  90. package/dist/templates/research-workflow/agent.test.ts +10 -7
  91. package/dist/templates/research-workflow/workflows/research.ts +28 -30
  92. package/dist/templates/retail/agent.test.ts +17 -15
  93. package/dist/templates/retail/cancel.ts +7 -6
  94. package/dist/templates/retail/client.tsx +39 -58
  95. package/dist/templates/retail/payment.ts +4 -3
  96. package/dist/templates/retail/pending.test.ts +19 -19
  97. package/dist/templates/retail/store.test.ts +5 -5
  98. package/dist/templates/retail/swap.ts +8 -11
  99. package/dist/templates/solo-rpg/agent.eval.test.ts +2 -16
  100. package/dist/templates/solo-rpg/agent.test.ts +12 -12
  101. package/dist/templates/solo-rpg/client.tsx +3 -3
  102. package/dist/templates/spoken-summary/agent.test.ts +9 -9
  103. package/dist/templates/spoken-summary/agent.ts +3 -3
  104. package/dist/templates/spoken-summary/client.tsx +23 -30
  105. package/dist/templates/spoken-summary/workflows/summarize.ts +11 -11
  106. package/dist/templates/spoken-summary/workflows/transcribe.ts +14 -14
  107. package/dist/templates/support-line/client.tsx +4 -3
  108. package/dist/templates/support-line/prompts.ts +0 -2
  109. package/dist/templates/transcription-workflow/agent.test.ts +20 -12
  110. package/dist/templates/transcription-workflow/agent.ts +2 -2
  111. package/dist/templates/transcription-workflow/client.tsx +15 -14
  112. package/dist/templates/transcription-workflow/recover.ts +1 -1
  113. package/dist/templates/transcription-workflow/run-panel.tsx +34 -14
  114. package/dist/templates/transcription-workflow/total-latency.tsx +46 -20
  115. package/dist/templates/transcription-workflow/workflows/batch.ts +15 -15
  116. package/dist/templates/transcription-workflow/workflows/downsample.ts +1 -1
  117. package/dist/templates/transcription-workflow/workflows/normalize.ts +15 -19
  118. package/dist/templates/transcription-workflow/workflows/stream.ts +12 -12
  119. package/dist/templates/transcription-workflow/workflows/sync-api.ts +3 -3
  120. package/dist/templates/transcription-workflow/workflows/transcribe.ts +23 -34
  121. package/dist/templates/travel-concierge/agent.eval.test.ts +2 -2
  122. package/dist/templates/travel-concierge/agent.test.ts +15 -7
  123. package/dist/templates/travel-concierge/client.tsx +2 -2
  124. package/dist/templates/travel-concierge/shared.ts +10 -14
  125. package/dist/templates/travel-concierge/tools/lookup_booking.ts +4 -3
  126. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +3 -2
  127. package/dist/templates/travel-concierge/tools/search_excursions.ts +3 -2
  128. package/dist/templates/travel-concierge/tools/search_flights.ts +3 -2
  129. package/dist/templates/travel-concierge/tools/search_hotels.ts +3 -2
  130. package/dist/{test-guenJUxi.mjs → test-D032V2cA.mjs} +4 -17
  131. package/dist/typecheck.mjs +1 -1
  132. package/dist/worker-bundler.mjs +1 -1
  133. package/dist/{workflow-BZyKLkhV.mjs → workflow-CVioyO3V.mjs} +6 -5
  134. package/package.json +4 -4
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok, t as CliError } from "./_output-DbJt9gsl.mjs";
2
+ import { a as ok, t as CliError } from "./_output-DBP9Op_d.mjs";
3
3
  import { n as log } from "./_ui-DCt4qZrk.mjs";
4
- import { o as updateGlobalConfig, r as readGlobalConfig, t as approveServer } from "./_config-ZY2euhdh.mjs";
5
- import { o as resolveServerUrl } from "./_agent-Clbq3a4h.mjs";
4
+ import { o as updateGlobalConfig } from "./_config-DYzC6WMD.mjs";
5
+ import { a as resolveApprovedServer } from "./_agent-BzUeqOdj.mjs";
6
6
  import { linkConfirmationCode, sleep } from "@alexkroman1/aai/internal";
7
7
  import { spawn } from "node:child_process";
8
8
  import { randomBytes } from "node:crypto";
@@ -133,9 +133,7 @@ function defaultOpenBrowser(url) {
133
133
  async function executeLogin(opts, deps = {}) {
134
134
  const fetchFn = deps.fetchFn ?? globalThis.fetch;
135
135
  requireTty();
136
- const globalConfig = await readGlobalConfig();
137
- const serverUrl = resolveServerUrl(opts.server, void 0, globalConfig.approvedServers ?? []);
138
- if (opts.server) await approveServer(serverUrl);
136
+ const { serverUrl } = await resolveApprovedServer(null, opts.server);
139
137
  if ((await jsonBody(await reachable(fetchFn, `${serverUrl}/studio/auth`, serverUrl), "Reading the server's login configuration")).mode === "none") throw new CliError("login_unavailable", "This server has no browser login configured, so there is no account to link.", "Point `--server` at a platform with browser login configured — linking an account there is the only way to authenticate.");
140
138
  const code = randomBytes(32).toString("base64url");
141
139
  const linkUrl = `${serverUrl}/?cli-link=${code}`;
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok } from "./_output-DbJt9gsl.mjs";
2
+ import { a as ok } from "./_output-DBP9Op_d.mjs";
3
3
  import { n as log } from "./_ui-DCt4qZrk.mjs";
4
- import { f as checkedResponse } from "./_studio-DTi3EdXQ.mjs";
5
- import { n as slugRequest } from "./_slug-api-DlKpj91X.mjs";
4
+ import { n as getServerInfo } from "./_agent-BzUeqOdj.mjs";
5
+ import { f as checkedResponse } from "./_studio-DnR_BqFp.mjs";
6
+ import { n as slugRequestOn } from "./_slug-api-CGvc2YSQ.mjs";
6
7
  import { isRecord, omitUndefined } from "@alexkroman1/aai/utils";
7
8
  import { sleep } from "@alexkroman1/aai/internal";
8
9
  function isLogsPage(value) {
@@ -19,12 +20,16 @@ function formatLine(line) {
19
20
  const mark = line.stream === "stderr" ? " ERR" : "";
20
21
  return `${formatTime(line.at)}${mark} ${line.text}`;
21
22
  }
22
- async function readPage(cwd, after, server) {
23
- const { data, slug } = await slugRequest(cwd, `/logs?after=${after}`, { action: "logs" }, server);
24
- return {
25
- page: checkedResponse(data, isLogsPage, `the logs route for ${slug}`),
26
- slug
27
- };
23
+ /**
24
+ * One page, against a target resolved ONCE by the caller.
25
+ *
26
+ * `slugRequestOn` rather than `slugRequest`: under `--follow` this runs every
27
+ * second for the life of the command, and the target is immutable — see that
28
+ * function's doc for what re-resolving it cost.
29
+ */
30
+ async function readPage(target, after) {
31
+ const data = await slugRequestOn(target, `/logs?after=${after}`, { action: "logs" });
32
+ return checkedResponse(data, isLogsPage, `the logs route for ${target.slug}`);
28
33
  }
29
34
  /**
30
35
  * Print the agent's buffered output, once or continuously.
@@ -34,7 +39,9 @@ async function readPage(cwd, after, server) {
34
39
  * into a result object would duplicate what it just printed.
35
40
  */
36
41
  async function executeLogs(cwd, opts = {}) {
37
- const { page, slug } = await readPage(cwd, -1, opts.server);
42
+ const target = await getServerInfo(cwd, opts.server);
43
+ const { slug } = target;
44
+ const page = await readPage(target, -1);
38
45
  let cursor = page.cursor;
39
46
  let printed = printPage(page);
40
47
  if (!opts.follow) {
@@ -52,11 +59,11 @@ async function executeLogs(cwd, opts = {}) {
52
59
  while (!opts.signal?.aborted) {
53
60
  await sleep(pollMs, omitUndefined({ signal: opts.signal }));
54
61
  if (opts.signal?.aborted) break;
55
- const next = await readPage(cwd, cursor, opts.server).catch(() => void 0);
62
+ const next = await readPage(target, cursor).catch(() => void 0);
56
63
  if (!next) continue;
57
- cursor = next.page.cursor;
58
- running = next.page.running;
59
- printed += printPage(next.page);
64
+ cursor = next.cursor;
65
+ running = next.running;
66
+ printed += printPage(next);
60
67
  }
61
68
  return ok({
62
69
  slug,
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { c as writeGlobalConfig, s as updateProjectConfig } from "./_config-ZY2euhdh.mjs";
2
+ import { c as writeGlobalConfig, s as updateProjectConfig } from "./_config-DYzC6WMD.mjs";
3
3
  export { updateProjectConfig, writeGlobalConfig as writeConfigHome };
@@ -77,7 +77,8 @@ The fast loop: edit → `pnpm dev` (browser, talk to it) →
77
77
  you stopped talking, barge-in, two sentences merging into one turn. Those
78
78
  need `pnpm dev` and your own voice.
79
79
  4. **Run `pnpm build` before declaring done** — bundles `agent.ts`,
80
- type-checks, and validates the manifest. Catches issues `dev` won't.
80
+ type-checks, validates the manifest, and runs the WHOLE spec suite first.
81
+ Catches issues `dev` won't.
81
82
  5. **Make small, focused changes** — verify each one before stacking the
82
83
  next.
83
84
  6. **Look at templates before writing custom code** — the CLI ships working
@@ -109,6 +110,7 @@ npx @alexkroman1/aai-cli init # Scaffold a new agent
109
110
  npx @alexkroman1/aai-cli templates # List available templates
110
111
  npx @alexkroman1/aai-cli dev # Start local dev server
111
112
  npx @alexkroman1/aai-cli test # Run agent.test.ts via vitest
113
+ npx @alexkroman1/aai-cli test --all # ...or every spec in the project
112
114
  npx @alexkroman1/aai-cli eval # Run agent.eval.test.ts against a model
113
115
  npx @alexkroman1/aai-cli build # Bundle and validate
114
116
  npx @alexkroman1/aai-cli deploy # Deploy to production
@@ -122,6 +124,13 @@ The scaffold's `package.json` exposes `dev`, `build`, `test`, `eval` and
122
124
  `deploy` as `pnpm <name>` shortcuts. Other commands (`init`, `templates`,
123
125
  `delete`, `secret`) are CLI-only.
124
126
 
127
+ **`aai test` targets `agent.test.ts` and nothing else**, which `--all` widens.
128
+ What matters is that a narrowed run does not report itself as a pass: when the
129
+ project holds spec files the run did not cover, it FAILS and names them rather
130
+ than printing a green line that says nothing about `tools/*.test.ts`. `pnpm
131
+ test` (the scaffold's own script) already runs the whole suite, and so does the
132
+ gate in front of `aai build`.
133
+
125
134
  ## Running it yourself (`npm start`)
126
135
 
127
136
  `server.mjs` serves this agent from a plain Node process — no platform
@@ -416,14 +425,14 @@ below.
416
425
 
417
426
  ### Workflow bodies live in `workflows/`
418
427
 
419
- A body is an ordinary exported async function of its input and a `WorkflowCtx`.
428
+ A body is an ordinary exported async function of its input and a `WorkflowContext`.
420
429
  There is no directive and no compile step of its own — the agent bundle compiles
421
430
  `workflows/` like any other source file — and durability is a method call:
422
431
 
423
432
  ```ts
424
- import type { WorkflowCtx } from "@alexkroman1/aai";
433
+ import type { WorkflowContext } from "@alexkroman1/aai";
425
434
 
426
- export async function digestFlow(input: { url: string }, ctx: WorkflowCtx) {
435
+ export async function digestFlow(input: { url: string }, ctx: WorkflowContext) {
427
436
  const digest = await ctx.step("summarize", () => summarize(input.url));
428
437
 
429
438
  // Suspended, not blocked: the container is free to exit here and the run
@@ -515,11 +524,11 @@ The three undurable reads a body most often wants, each journaled — read once
515
524
  the first reach, and the same value on every later walk:
516
525
 
517
526
  ```ts
518
- import type { WorkflowCtx } from "@alexkroman1/aai";
527
+ import type { WorkflowContext } from "@alexkroman1/aai";
519
528
 
520
529
  declare function charge(amount: number, idempotencyKey: string, jitter: number): Promise<void>;
521
530
 
522
- export async function chargeFlow(input: { amount: number }, ctx: WorkflowCtx) {
531
+ export async function chargeFlow(input: { amount: number }, ctx: WorkflowContext) {
523
532
  const startedAt = await ctx.now(); // epoch ms, decided once
524
533
  const idempotencyKey = await ctx.uuid(); // still the same id after a crash
525
534
  const jitter = await ctx.random(); // one float in [0, 1), journaled per call
@@ -601,6 +610,22 @@ Five things worth knowing:
601
610
  which is how a "send it now" tool cuts a scheduled wait short. Naming no ids
602
611
  wakes every outstanding SLEEP and deliberately not a `waitFor` deadline, so
603
612
  cutting a schedule short cannot also close an approval window.
613
+ - **A SUSPEND is not free, so `ctx.sleep` is not a `setTimeout`.** A wait costs
614
+ a journal write to record it, a queued delivery to bring the run back, and a
615
+ fresh WALK of the body — measured on a deployed agent at roughly a second of
616
+ overhead around the sleep itself, on top of whatever you asked for. There is a
617
+ cliff at the bottom of the range worth knowing about: a sleep shorter
618
+ than the round trip that records it never suspends AT ALL — `ctx.sleep("beat",
619
+ 100)` and `ctx.sleep("beat", 0)` are both simply free — while anything longer
620
+ pays the whole cost. Measured, `nap(100)` and `nap(0)` came back within 30 ms
621
+ of each other on a run whose total was 2.1 s.
622
+
623
+ So a sub-second pause is not what this is for. For a short backoff inside a
624
+ step, use an ordinary timer (`sleep` from `@alexkroman1/aai/internal`) — a step
625
+ body may not call `ctx.sleep` anyway, and the engine refuses one that does. Use
626
+ `ctx.sleep` for a wait you want to SURVIVE the process, which is the thing a
627
+ timer cannot do. A body that polls in a loop pays the suspend per iteration,
628
+ which is the strongest argument for the next section: park on the callback.
604
629
 
605
630
  #### A third-party callback is an OPTIMIZATION over a reconciling read
606
631
 
@@ -656,15 +681,15 @@ trap somebody has already paid for:
656
681
  Steps are ordinary exported functions, so a spec imports and calls them. The
657
682
  BODY needs an engine, and there are two, for two different questions.
658
683
 
659
- **"What did the body ask for?"** — `createWorkflowCtx` from
684
+ **"What did the body ask for?"** — `createWorkflowContext` from
660
685
  `@alexkroman1/aai/testing`. It runs the steps and records the names, the retry
661
686
  policies and the sleeps, over one walk with no journal. Nothing replays, so a
662
687
  spec built on it must not claim to test durability.
663
688
 
664
689
  ```ts no-check
665
- import { createWorkflowCtx } from "@alexkroman1/aai/testing";
690
+ import { createWorkflowContext } from "@alexkroman1/aai/testing";
666
691
 
667
- const ctx = createWorkflowCtx({ runSteps: false });
692
+ const ctx = createWorkflowContext({ runSteps: false });
668
693
  await digestFlow({ url: "https://example.com/a" }, ctx);
669
694
 
670
695
  expect(ctx.steps.map((s) => s.name)).toEqual(["fetchArticle", "summarize", "file"]);
@@ -729,7 +754,7 @@ root barrel would drag the whole SDK into that bundle.
729
754
 
730
755
  ```ts
731
756
  import { stepEnv } from "@alexkroman1/aai/step";
732
- import { stepGenerateClassified } from "@alexkroman1/aai/step-errors";
757
+ import { stepGenerateOrFail } from "@alexkroman1/aai/step-errors";
733
758
 
734
759
  async function summarize(text: string) {
735
760
  // The agent's env by name — the same values a tool reads from `ctx.env`.
@@ -737,7 +762,7 @@ async function summarize(text: string) {
737
762
  const style = stepEnv("DIGEST_STYLE") ?? "plain";
738
763
 
739
764
  // One model call, on the agent's own ASSEMBLYAI_API_KEY and default model.
740
- return await stepGenerateClassified(`${style} summary of:\n\n${text}`, {
765
+ return await stepGenerateOrFail(`${style} summary of:\n\n${text}`, {
741
766
  system: "Reply with two sentences and nothing else.",
742
767
  });
743
768
  }
@@ -749,9 +774,9 @@ before and after a deploy. List what you read in `requiredEnv` and a deploy
749
774
  checks it for you. And **`stepGenerate` is not `ctx.generate`**: it is one
750
775
  request to the AssemblyAI LLM Gateway, with no tools and no structured output,
751
776
  because bundling the AI SDK into a step artifact costs megabytes on every
752
- deploy. Use `stepGenerateJsonClassified` with a Zod `schema` if you need a shape.
777
+ deploy. Use `stepGenerateJsonOrFail` with a Zod `schema` if you need a shape.
753
778
 
754
- ### From a step, reach for the `Classified` call
779
+ ### From a step, reach for the `OrFail` call
755
780
 
756
781
  `@alexkroman1/aai/step-errors` publishes a wrapper for every `/step` call that
757
782
  can fail against a remote service, and **inside a step the wrapper is the one to
@@ -759,14 +784,14 @@ use**:
759
784
 
760
785
  | Raw, on `@alexkroman1/aai/step` | Use this instead, on `@alexkroman1/aai/step-errors` |
761
786
  | --- | --- |
762
- | `stepGenerate` | `stepGenerateClassified` |
763
- | `stepGenerateJson` | `stepGenerateJsonClassified` |
764
- | `stepFetch` | `stepFetchOk` |
765
- | `stepTranscribeSync` | `stepTranscribeSyncClassified` |
766
- | `stepTranscribeUpload` / `Submit` / `Poll` | the matching `*Classified` |
767
- | `sendToChannel` (`/channels`) | `sendToChannelClassified` |
768
-
769
- `stepFetchOk` is the one that is not spelled `*Classified`, and the name is the
787
+ | `stepGenerate` | `stepGenerateOrFail` |
788
+ | `stepGenerateJson` | `stepGenerateJsonOrFail` |
789
+ | `stepFetch` | `stepFetchOrFail` |
790
+ | `stepTranscribeSync` | `stepTranscribeSyncOrFail` |
791
+ | `stepTranscribeUpload` / `Submit` / `Poll` | the matching `*OrFail` |
792
+ | `sendToChannel` (`/channels`) | `sendToChannelOrFail` |
793
+
794
+ `stepFetchOrFail` is the one that is not spelled `*OrFail`, and the name is the
770
795
  difference: the others turn an already-thrown failure into a classified one,
771
796
  while this also turns a NON-2XX RESPONSE into a throw — `stepFetch` resolves
772
797
  with a `404` rather than raising it. Two changes, so two names.
@@ -804,7 +829,7 @@ bundling rule as `/step` — import them there, never through the root barrel:
804
829
  recording, or `stepTranscribeUpload` → `stepTranscribeSubmit` →
805
830
  `stepTranscribePoll` for a long one, plus `Transcript`, `TranscribeError` and
806
831
  the `TRANSCRIBE_*` limits. (There is no `/transcribe` subpath; transcription
807
- lives on `/step` with the other step primitives.) Use the `Classified`
832
+ lives on `/step` with the other step primitives.) Use the `OrFail`
808
833
  wrappers above: a provider refusal — a container it will not read, a
809
834
  recording with no speech — arrives
810
835
  with `retryable: false`, and unclassified a step re-uploads the same bytes
@@ -841,19 +866,19 @@ export async function measure(uploadId: string) {
841
866
 
842
867
  A run that finishes while nobody is on the line needs somewhere to put the
843
868
  result. `slackChannel({ webhookUrl })` names a destination and
844
- `sendToChannelClassified(channel, message)` posts to it:
869
+ `sendToChannelOrFail(channel, message)` posts to it:
845
870
 
846
871
  ```ts no-check
847
872
  import { type ChannelMessage, slackChannel } from "@alexkroman1/aai/channels";
848
873
  import { requireStepEnv } from "@alexkroman1/aai/step";
849
- import { sendToChannelClassified } from "@alexkroman1/aai/step-errors";
874
+ import { sendToChannelOrFail } from "@alexkroman1/aai/step-errors";
850
875
 
851
876
  export async function announce(headline: string, points: string[]) {
852
877
  const message: ChannelMessage = {
853
878
  text: headline,
854
879
  sections: points.map((point) => ({ text: point })),
855
880
  };
856
- return await sendToChannelClassified(slackChannel({ webhookUrl: requireStepEnv("SLACK_WEBHOOK_URL") }), message);
881
+ return await sendToChannelOrFail(slackChannel({ webhookUrl: requireStepEnv("SLACK_WEBHOOK_URL") }), message);
857
882
  }
858
883
  ```
859
884
 
@@ -862,7 +887,7 @@ it with `aai secret put`. A channel's credential is its DESTINATION and is
862
887
  passed in, which is why no channel reads an env var of its own. `ChannelMessage`
863
888
  is rendered per platform, so the same message is legal on a channel kind added
864
889
  later; `isSlackWebhookUrl` / `isSlackWorkflowTriggerUrl` validate a pasted URL
865
- before a run depends on it, and `channelAdvice` turns a refusal into a sentence
890
+ before a run depends on it, and `explainChannelFailure` turns a refusal into a sentence
866
891
  a person can act on. `podcast-digest` is the worked example.
867
892
 
868
893
  ### A step's HTTP: use `stepFetch`, not `fetch`
@@ -941,11 +966,11 @@ Both are on `@alexkroman1/aai/step`, and `spoken-summary` is the template that
941
966
  shows the whole round trip.
942
967
 
943
968
  ```ts
944
- import { stepSpeak, writeUpload } from "@alexkroman1/aai/step";
969
+ import { stepSpeak, stepWriteUpload } from "@alexkroman1/aai/step";
945
970
 
946
971
  export async function narrate(script: string) {
947
972
  const spoken = await stepSpeak(script, { voice: "jane" });
948
- const stored = await writeUpload(spoken.audio, { name: "summary.wav", type: "audio/wav" });
973
+ const stored = await stepWriteUpload(spoken.audio, { name: "summary.wav", type: "audio/wav" });
949
974
  return { audio: stored.id, durationMs: spoken.durationMs };
950
975
  }
951
976
  ```
@@ -962,11 +987,13 @@ opens and produces silence rather than an error. The `AssemblyAITtsVoice` type
962
987
  gives you autocomplete over it and nothing more: it accepts any string, so that
963
988
  a voice the service adds after this release still compiles.
964
989
 
965
- **`writeUpload` is `readUpload`'s other direction, and you need it.** A run's
966
- output is read back as JSON, so audio cannot travel in one — the same rule that
967
- keeps an uploaded recording's bytes out of a run's INPUT, arriving at the other
968
- end of the run. Store the bytes, return the **id**, and let the page fetch it
969
- with `api.download(id)`.
990
+ **`stepWriteUpload` is `stepReadUpload`'s other direction, and you need it.** A
991
+ run's output is read back as JSON, so audio cannot travel in one — the same rule
992
+ that keeps an uploaded recording's bytes out of a run's INPUT, arriving at the
993
+ other end of the run. Store the bytes, return the **id**, and let the page fetch
994
+ it with `api.download(id)`. A step that wants the record rather than the bytes
995
+ reads it with `stepUploadInfo(id)`, which answers an `UploadInfo` — the name,
996
+ the size stored so far, and whether that is all of it.
970
997
 
971
998
  Three rules come with it:
972
999
 
@@ -1020,12 +1047,13 @@ naming a shape is what asks the compiler to make you handle the failure.
1020
1047
 
1021
1048
  ### The page
1022
1049
 
1023
- A workflow app's `client.tsx` mounts with `page()` rather than `client()` —
1024
- there is no session to build, so no socket, no audio graph and no microphone
1025
- request. Everything else is the same file, React and Tailwind included.
1050
+ A workflow app's `client.tsx` mounts with `mountPage()` rather than
1051
+ `mountClient()` — there is no session to build, so no socket, no audio graph
1052
+ and no microphone request. Everything else is the same file, React and
1053
+ Tailwind included.
1026
1054
 
1027
1055
  ```tsx no-check
1028
- import { createWorkflowApi, page, useWorkflowRun } from "@alexkroman1/aai-ui";
1056
+ import { createWorkflowApi, mountPage, useWorkflowRun } from "@alexkroman1/aai-ui";
1029
1057
  import "@alexkroman1/aai-ui/styles.css";
1030
1058
  import type { WorkflowOutputOf } from "@alexkroman1/aai/workflow-api";
1031
1059
  import { useState } from "react";
@@ -1053,7 +1081,7 @@ export function App() {
1053
1081
  );
1054
1082
  }
1055
1083
 
1056
- page({ name: "Link Digest", component: App });
1084
+ mountPage({ name: "Link Digest", component: App });
1057
1085
  ```
1058
1086
 
1059
1087
  `api.start()` resolves as soon as the RUN EXISTS, not when it finishes — that
@@ -1172,7 +1200,7 @@ statement about pipeline mode, not about the SDK.
1172
1200
  ### Answering a phone call
1173
1201
 
1174
1202
  A deployed voice agent already serves carrier media streams — there is nothing
1175
- to switch on. `createServer` mounts `WS /phone` whenever the agent is a voice
1203
+ to switch on. `createRuntimeServer` mounts `WS /phone` whenever the agent is a voice
1176
1204
  agent (`telephony` defaults to `true`, and to `false` for a `page: "static"`
1177
1205
  workflow app, which has no stages to put on a call). Point the carrier at it
1178
1206
  with a `carrier` query parameter naming who is dialling:
@@ -1188,7 +1216,7 @@ the bridge transcodes in both directions, so the agent, its tools and its slots
1188
1216
  behave exactly as they do in the browser — a phone call is a transport, not a
1189
1217
  mode. Nothing about `agent.ts` changes to support one.
1190
1218
 
1191
- Turn the route off with `telephony: false` on `createServer`. If you are
1219
+ Turn the route off with `telephony: false` on `createRuntimeServer`. If you are
1192
1220
  embedding the runtime yourself rather than deploying, the pieces are
1193
1221
  `createTelephonyBridge`, `startTelephonySession`, `TELEPHONY_PATH` and
1194
1222
  `carrierByName`, all on `@alexkroman1/aai-runtime`.
@@ -1268,12 +1296,12 @@ API keys require it; the US endpoints reject them. Example:
1268
1296
  | Factory | SDK package | Env var |
1269
1297
  | --------------- | ------------------- | ------------------------------ |
1270
1298
  | `anthropicLlm` | `@ai-sdk/anthropic` | `ANTHROPIC_API_KEY` |
1271
- | `openaiLlm` | `@ai-sdk/openai` | `OPENAI_API_KEY` |
1299
+ | `openAILlm` | `@ai-sdk/openai` | `OPENAI_API_KEY` |
1272
1300
  | `googleLlm` | `@ai-sdk/google` | `GOOGLE_GENERATIVE_AI_API_KEY` |
1273
1301
  | `mistralLlm` | `@ai-sdk/mistral` | `MISTRAL_API_KEY` |
1274
- | `xaiLlm` | `@ai-sdk/xai` | `XAI_API_KEY` |
1302
+ | `xAILlm` | `@ai-sdk/xai` | `XAI_API_KEY` |
1275
1303
  | `groqLlm` | `@ai-sdk/groq` | `GROQ_API_KEY` |
1276
- | `openrouterLlm` | `@ai-sdk/openai` | `OPENROUTER_API_KEY` |
1304
+ | `openRouterLlm` | `@ai-sdk/openai` | `OPENROUTER_API_KEY` |
1277
1305
  | `gatewayLlm` | `ai` (built in) | `AI_GATEWAY_API_KEY` |
1278
1306
  | `assemblyAILlm` | `@ai-sdk/openai` | `ASSEMBLYAI_API_KEY` |
1279
1307
 
@@ -1283,10 +1311,10 @@ shared by all of them except `assemblyAILlm`. Example:
1283
1311
  because a third-party vendor's catalog is not this SDK's to default from;
1284
1312
  `assemblyAILlm()` is the one bare call, since it has a default model.
1285
1313
 
1286
- `openrouterLlm` routes through [OpenRouter](https://openrouter.ai) — an
1314
+ `openRouterLlm` routes through [OpenRouter](https://openrouter.ai) — an
1287
1315
  OpenAI-compatible endpoint fronting hundreds of models addressed as
1288
1316
  `"creator/model"`, e.g.
1289
- `openrouterLlm({ model: "meta-llama/llama-3.3-70b-instruct" })`. It needs
1317
+ `openRouterLlm({ model: "meta-llama/llama-3.3-70b-instruct" })`. It needs
1290
1318
  no extra SDK install (it reuses the `@ai-sdk/openai` client).
1291
1319
 
1292
1320
  `gatewayLlm` routes through the [Vercel AI
@@ -1947,7 +1975,7 @@ Those three cover almost everything an agent wants. A database is for data that
1947
1975
  must outlive a session AND be queryable: a ledger, filed records, cross-session
1948
1976
  saves.
1949
1977
 
1950
- ## Custom UI — `client()`
1978
+ ## Custom UI — `mountClient()`
1951
1979
 
1952
1980
  File: `client.tsx` alongside `agent.ts`. Uses **React** (not Preact).
1953
1981
  Always import `"@alexkroman1/aai-ui/styles.css"` first.
@@ -1957,9 +1985,9 @@ Always import `"@alexkroman1/aai-ui/styles.css"` first.
1957
1985
  ```tsx
1958
1986
  /// <reference types="vite/client" />
1959
1987
  import "@alexkroman1/aai-ui/styles.css";
1960
- import { client } from "@alexkroman1/aai-ui";
1988
+ import { mountClient } from "@alexkroman1/aai-ui";
1961
1989
 
1962
- client({ name: "My Agent" });
1990
+ mountClient({ name: "My Agent" });
1963
1991
  ```
1964
1992
 
1965
1993
  ### Tier 1 with sidebar
@@ -1967,7 +1995,7 @@ client({ name: "My Agent" });
1967
1995
  ```tsx
1968
1996
  /// <reference types="vite/client" />
1969
1997
  import "@alexkroman1/aai-ui/styles.css";
1970
- import { client, useEvent } from "@alexkroman1/aai-ui";
1998
+ import { mountClient, useEvent } from "@alexkroman1/aai-ui";
1971
1999
  import { useState } from "react";
1972
2000
 
1973
2001
  function Sidebar() {
@@ -1982,7 +2010,7 @@ function Sidebar() {
1982
2010
  );
1983
2011
  }
1984
2012
 
1985
- client({ name: "My Agent", sidebar: Sidebar });
2013
+ mountClient({ name: "My Agent", sidebar: Sidebar });
1986
2014
  ```
1987
2015
 
1988
2016
  ### Tier 2 — full custom component
@@ -1990,7 +2018,7 @@ client({ name: "My Agent", sidebar: Sidebar });
1990
2018
  ```tsx
1991
2019
  /// <reference types="vite/client" />
1992
2020
  import "@alexkroman1/aai-ui/styles.css";
1993
- import { client, useSession } from "@alexkroman1/aai-ui";
2021
+ import { mountClient, useSession } from "@alexkroman1/aai-ui";
1994
2022
 
1995
2023
  function MyApp() {
1996
2024
  const { messages, userTranscript, started, running, start, toggle, end } =
@@ -2011,10 +2039,10 @@ function MyApp() {
2011
2039
  );
2012
2040
  }
2013
2041
 
2014
- client({ component: MyApp });
2042
+ mountClient({ component: MyApp });
2015
2043
  ```
2016
2044
 
2017
- ### `client()` config
2045
+ ### `mountClient()` config
2018
2046
 
2019
2047
  | Field | Type | Default | Description |
2020
2048
  | --- | --- | --- | --- |
@@ -2172,7 +2200,7 @@ Property 'children' is missing` build error:
2172
2200
  ```tsx
2173
2201
  /// <reference types="vite/client" />
2174
2202
  import "@alexkroman1/aai-ui/styles.css";
2175
- import { ChatView, client, StartScreen } from "@alexkroman1/aai-ui";
2203
+ import { ChatView, mountClient, StartScreen } from "@alexkroman1/aai-ui";
2176
2204
 
2177
2205
  function PizzaApp() {
2178
2206
  return (
@@ -2182,7 +2210,7 @@ function PizzaApp() {
2182
2210
  );
2183
2211
  }
2184
2212
 
2185
- client({ component: PizzaApp });
2213
+ mountClient({ component: PizzaApp });
2186
2214
  ```
2187
2215
 
2188
2216
  ## Styling
@@ -2190,7 +2218,7 @@ client({ component: PizzaApp });
2190
2218
  - **Tailwind CSS v4** — compiled at bundle time, configured via CSS.
2191
2219
  Do NOT create `tailwind.config.js` — it will be ignored.
2192
2220
  - Use Tailwind classes for layout, `useTheme()` for dynamic colors.
2193
- - Set theme: `client({ theme: { bg, primary, text, surface, border } })`.
2221
+ - Set theme: `mountClient({ theme: { bg, primary, text, surface, border } })`.
2194
2222
  - Override CSS custom properties for extra tokens:
2195
2223
  `--color-aai-*`, `--radius-aai`, `--font-aai`.
2196
2224
  - Always import `"@alexkroman1/aai-ui/styles.css"` at the top of `client.tsx`.
@@ -15,9 +15,9 @@
15
15
  "publish:agent": "aai publish"
16
16
  },
17
17
  "dependencies": {
18
- "@alexkroman1/aai": "^13.3.0",
19
- "@alexkroman1/aai-runtime": "^13.3.0",
20
- "@alexkroman1/aai-ui": "^13.3.0",
18
+ "@alexkroman1/aai": "^14.0.0",
19
+ "@alexkroman1/aai-runtime": "^14.0.0",
20
+ "@alexkroman1/aai-ui": "^14.0.0",
21
21
  "react": "^19.2.8",
22
22
  "react-dom": "^19.2.8",
23
23
  "tailwindcss": "^4.0.0",
@@ -25,7 +25,7 @@
25
25
  "zod": "^4.5.4"
26
26
  },
27
27
  "devDependencies": {
28
- "@alexkroman1/aai-cli": "^13.3.0",
28
+ "@alexkroman1/aai-cli": "^14.0.0",
29
29
  "@tailwindcss/vite": "^4.3.3",
30
30
  "@types/node": "^26.4.1",
31
31
  "@types/react": "^19.2.18",