@hraness/dawg 0.6.1 → 0.7.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 (253) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/DAWG.md +343 -116
  3. package/README.md +27 -25
  4. package/core/autotune.ts +1119 -0
  5. package/core/chords.ts +271 -23
  6. package/core/clips.ts +499 -0
  7. package/core/diff.ts +182 -104
  8. package/core/expression.ts +15 -0
  9. package/core/fx.ts +99 -29
  10. package/core/instruments.ts +19 -0
  11. package/core/keys.ts +3 -3
  12. package/core/loop.ts +5 -0
  13. package/core/lyrics.ts +297 -0
  14. package/core/master.ts +3 -3
  15. package/core/resonators.ts +16 -2
  16. package/core/routing.ts +165 -0
  17. package/core/score.ts +832 -13
  18. package/core/sdk/eval-child.ts +7 -2
  19. package/core/sdk/eval.ts +35 -6
  20. package/core/sdk/print.ts +276 -3
  21. package/core/sdk/sync-lyrics.ts +49 -0
  22. package/core/sdk/v1.ts +1849 -42
  23. package/core/sections.ts +292 -26
  24. package/core/sing.ts +815 -0
  25. package/core/style-provenance.ts +80 -0
  26. package/core/styles/africa-mena-southasia.ts +2893 -0
  27. package/core/styles/americas.ts +3810 -0
  28. package/core/styles/art.ts +4993 -0
  29. package/core/styles/base.ts +123 -0
  30. package/core/styles/cycles.ts +106 -0
  31. package/core/styles/electronic.ts +2723 -0
  32. package/core/styles/europe-asia-pacific.ts +2838 -0
  33. package/core/styles/excerpt.ts +29 -0
  34. package/core/styles/gamelan.ts +283 -0
  35. package/core/styles/generate.ts +2199 -0
  36. package/core/styles/index.ts +515 -0
  37. package/core/styles/parts.ts +106 -0
  38. package/core/styles/pop.ts +3189 -0
  39. package/core/styles/rock.ts +2993 -0
  40. package/core/styles/roots.ts +4175 -0
  41. package/core/styles/schema.ts +429 -0
  42. package/core/styles/taxonomy.ts +940 -0
  43. package/core/styles/validate.ts +528 -0
  44. package/core/tempo.ts +32 -2
  45. package/core/tuning.ts +19 -3
  46. package/core/vocoder.ts +524 -0
  47. package/guides/agent.md +29 -0
  48. package/guides/arrange.md +30 -0
  49. package/guides/audition.md +20 -14
  50. package/guides/automation.md +12 -7
  51. package/guides/chords.md +15 -15
  52. package/guides/effects.md +17 -16
  53. package/guides/faders.md +20 -16
  54. package/guides/files.md +13 -9
  55. package/guides/getting-started.md +15 -11
  56. package/guides/keys.md +19 -15
  57. package/guides/media.md +17 -12
  58. package/guides/mix.md +12 -6
  59. package/guides/music.md +23 -8
  60. package/guides/notes.md +15 -9
  61. package/guides/performance.md +15 -13
  62. package/guides/play.md +21 -13
  63. package/guides/project.md +25 -8
  64. package/guides/providers.md +20 -14
  65. package/guides/resample.md +15 -9
  66. package/guides/rhythm.md +17 -13
  67. package/guides/sessions.md +17 -7
  68. package/guides/show-me.md +31 -0
  69. package/guides/sound.md +25 -9
  70. package/guides/sounds.md +15 -13
  71. package/guides/styles.md +31 -0
  72. package/guides/tempo.md +15 -10
  73. package/guides/tracks.md +15 -10
  74. package/guides/tuning.md +32 -0
  75. package/guides/voice.md +31 -0
  76. package/guides/web-search.md +18 -8
  77. package/native/prebuilt/darwin-arm64/libdawg_sink.dylib +0 -0
  78. package/native/prebuilt/darwin-x64/libdawg_sink.dylib +0 -0
  79. package/native/prebuilt/linux-arm64/libdawg_sink.so +0 -0
  80. package/native/prebuilt/linux-x64/libdawg_sink.so +0 -0
  81. package/native/prebuilt/manifest.json +21 -0
  82. package/package.json +5 -2
  83. package/src/agent/agent.ts +126 -14
  84. package/src/agent/calibration-tools.ts +53 -0
  85. package/src/agent/clip-tools.ts +453 -0
  86. package/src/agent/command-agent.ts +369 -0
  87. package/src/agent/drum-tools.ts +2 -2
  88. package/src/agent/expression-tools.ts +1 -1
  89. package/src/agent/gateway.ts +246 -60
  90. package/src/agent/models.ts +53 -12
  91. package/src/agent/ops.ts +12 -1
  92. package/src/agent/pack-tools.ts +1 -1
  93. package/src/agent/planner.ts +13 -0
  94. package/src/agent/portable-schema.ts +80 -0
  95. package/src/agent/preview-tool.ts +4 -1
  96. package/src/agent/provider.ts +22 -8
  97. package/src/agent/rhythm-tools.ts +1 -1
  98. package/src/agent/section-tools.ts +1 -1
  99. package/src/agent/show-me.ts +497 -0
  100. package/src/agent/steer.ts +15 -0
  101. package/src/agent/style-tools.ts +217 -0
  102. package/src/agent/tool-error.ts +12 -0
  103. package/src/agent/tools.ts +108 -23
  104. package/src/agent/usage.ts +2 -2
  105. package/src/agent/voice-tools.ts +925 -0
  106. package/src/agent/xcb-agent.ts +11 -7
  107. package/src/argv.ts +38 -0
  108. package/src/audio/analysis.ts +253 -0
  109. package/src/audio/arrange.ts +37 -3
  110. package/src/audio/autotune-engine.ts +101 -0
  111. package/src/audio/autotune.ts +640 -0
  112. package/src/audio/clips.ts +240 -0
  113. package/src/audio/doctor.ts +86 -0
  114. package/src/audio/dsp/bandbank.ts +138 -0
  115. package/src/audio/dsp/envelope.ts +10 -0
  116. package/src/audio/dsp/follow.ts +120 -0
  117. package/src/audio/dsp/formant.ts +427 -0
  118. package/src/audio/dsp/glottal.ts +243 -0
  119. package/src/audio/dsp/interp.ts +7 -2
  120. package/src/audio/dsp/lpc.ts +50 -0
  121. package/src/audio/dsp/periodicity.ts +59 -0
  122. package/src/audio/dsp/pitch.ts +995 -0
  123. package/src/audio/dsp/psola.ts +199 -0
  124. package/src/audio/effects/chain.ts +3 -1
  125. package/src/audio/effects/common.ts +43 -0
  126. package/src/audio/effects/convolution.ts +7 -4
  127. package/src/audio/effects/filter.ts +48 -69
  128. package/src/audio/effects/formant.ts +263 -0
  129. package/src/audio/engine.ts +160 -28
  130. package/src/audio/fit.ts +35 -3
  131. package/src/audio/instrument-check.ts +59 -43
  132. package/src/audio/instruments.ts +4 -0
  133. package/src/audio/keys/calibration.ts +56 -0
  134. package/src/audio/keys/electric.ts +8 -1
  135. package/src/audio/keys/engine.ts +13 -1
  136. package/src/audio/keys/piano.ts +22 -2
  137. package/src/audio/kits.ts +135 -6
  138. package/src/audio/live.ts +114 -20
  139. package/src/audio/native.ts +615 -0
  140. package/src/audio/preview.ts +30 -2
  141. package/src/audio/render-worker.ts +2 -0
  142. package/src/audio/renderer.ts +2 -0
  143. package/src/audio/resample.ts +2 -1
  144. package/src/audio/sampler.ts +85 -4
  145. package/src/audio/samples.ts +20 -2
  146. package/src/audio/sing/analysis.ts +193 -0
  147. package/src/audio/sing/engine.ts +949 -0
  148. package/src/audio/strings/bow.ts +48 -5
  149. package/src/audio/strings/engine.ts +8 -2
  150. package/src/audio/synth/oscillators.ts +31 -21
  151. package/src/audio/synth/voice.ts +34 -1
  152. package/src/audio/vocoder/bank.ts +314 -0
  153. package/src/audio/vocoder/carrier.ts +165 -0
  154. package/src/audio/vocoder/control.ts +68 -0
  155. package/src/audio/vocoder/detect.ts +50 -0
  156. package/src/audio/vocoder/index.ts +304 -0
  157. package/src/audio/vocoder/talkbox.ts +143 -0
  158. package/src/audio/wav.ts +500 -77
  159. package/src/audio/winds/engine.ts +5 -1
  160. package/src/audio/winds/trim.ts +28 -4
  161. package/src/audio/winds/trims1.ts +297 -0
  162. package/src/audio/winds/voice.ts +15 -2
  163. package/src/auth/cli.ts +38 -36
  164. package/src/auth/credentials.ts +30 -1
  165. package/src/auth/login.ts +15 -9
  166. package/src/auth/tui.ts +19 -10
  167. package/src/commands/arrange.ts +44 -29
  168. package/src/commands/autotune.ts +421 -0
  169. package/src/commands/calibration.ts +74 -0
  170. package/src/commands/clips.ts +887 -0
  171. package/src/commands/drums.ts +3 -2
  172. package/src/commands/edit.ts +11 -4
  173. package/src/commands/expression.ts +1 -1
  174. package/src/commands/formant.ts +221 -0
  175. package/src/commands/fx.ts +101 -32
  176. package/src/commands/grammar.ts +558 -0
  177. package/src/commands/help.ts +610 -380
  178. package/src/commands/history.ts +18 -0
  179. package/src/commands/keys.ts +8 -8
  180. package/src/commands/modal.ts +1 -1
  181. package/src/commands/music.ts +1 -1
  182. package/src/commands/nearest.ts +53 -0
  183. package/src/commands/pack.ts +9 -2
  184. package/src/commands/param-range.ts +56 -0
  185. package/src/commands/parses.ts +130 -0
  186. package/src/commands/progression.ts +170 -0
  187. package/src/commands/rhythm.ts +3 -0
  188. package/src/commands/rig.ts +3 -24
  189. package/src/commands/sing.ts +478 -0
  190. package/src/commands/strum.ts +13 -1
  191. package/src/commands/style.ts +415 -0
  192. package/src/commands/time.ts +6 -3
  193. package/src/commands/tuning.ts +3 -3
  194. package/src/commands/vocal-pitch.ts +616 -0
  195. package/src/commands/vocal.ts +147 -0
  196. package/src/commands/vocoder.ts +627 -0
  197. package/src/commands/wind.ts +2 -2
  198. package/src/fs/durable.ts +50 -0
  199. package/src/lang/glossary.ts +493 -0
  200. package/src/launch-args.ts +163 -0
  201. package/src/main.ts +1397 -253
  202. package/src/media/cli.ts +20 -3
  203. package/src/media/import.ts +3 -1
  204. package/src/project/check.ts +21 -2
  205. package/src/project/clip-pins.ts +72 -0
  206. package/src/project/init.ts +23 -8
  207. package/src/project/sync.ts +418 -86
  208. package/src/render.ts +20 -1
  209. package/src/session/daemon.ts +3 -0
  210. package/src/session/meta.ts +14 -0
  211. package/src/session/origin.ts +154 -0
  212. package/src/session/port.ts +22 -4
  213. package/src/session/presence.ts +34 -4
  214. package/src/session/protocol.ts +5 -1
  215. package/src/session/rebase.ts +18 -5
  216. package/src/session/receipt.ts +258 -0
  217. package/src/session/store.ts +65 -43
  218. package/src/tui/arrange-menu.ts +65 -31
  219. package/src/tui/audition.ts +1 -1
  220. package/src/tui/euclid.ts +18 -13
  221. package/src/tui/fader.ts +228 -41
  222. package/src/tui/granular-menu.ts +2 -4
  223. package/src/tui/menu-clips.ts +297 -0
  224. package/src/tui/menu-time.ts +20 -13
  225. package/src/tui/menu-voice.ts +405 -0
  226. package/src/tui/menu.ts +759 -177
  227. package/src/tui/modal-menu.ts +6 -6
  228. package/src/tui/performance-menu.ts +5 -2
  229. package/src/tui/play-chords.ts +4 -2
  230. package/src/tui/play-mode.ts +15 -1
  231. package/src/tui/play-session.ts +47 -7
  232. package/src/tui/sing-menu.ts +278 -0
  233. package/src/tui/style-menu.ts +104 -0
  234. package/src/tui/vocoder-menu.ts +244 -0
  235. package/src/tui/wind-menu.ts +3 -3
  236. package/src/version.ts +8 -0
  237. package/src/web/fetch.ts +115 -29
  238. package/tui/activity.ts +180 -9
  239. package/tui/app.ts +274 -40
  240. package/tui/clip-row.ts +132 -0
  241. package/tui/delight.ts +144 -0
  242. package/tui/drawer.ts +70 -22
  243. package/tui/frame-gate.ts +76 -0
  244. package/tui/grammar.ts +112 -63
  245. package/tui/guide.ts +42 -4
  246. package/tui/highway.ts +269 -25
  247. package/tui/hints.ts +192 -0
  248. package/tui/input.ts +60 -9
  249. package/tui/keys.ts +1 -1
  250. package/tui/play-strip.ts +68 -14
  251. package/tui/prompt.ts +1 -1
  252. package/tui/screen.ts +144 -14
  253. package/tui/theme.ts +27 -0
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Tool parameter schemas as every provider accepts them.
3
+ *
4
+ * dawg writes unions as `type: ["array", "string"]` with `items` beside it.
5
+ * OpenAI and Anthropic accept that, but Gemini (through the AI Gateway)
6
+ * rewrites the union to `anyOf` and leaves `items` outside, then rejects the
7
+ * whole request, so no Gemini model could run a turn. Here each union
8
+ * becomes an `anyOf` whose branches carry only their own keywords, and an
9
+ * array branch always has `items`.
10
+ */
11
+
12
+ type Schema = Record<string, unknown>;
13
+
14
+ /** Keywords that belong to one JSON type's branch of a union. */
15
+ const BRANCH_KEYWORDS: Readonly<Record<string, readonly string[]>> = {
16
+ array: ["items", "minItems", "maxItems"],
17
+ object: ["properties", "required", "additionalProperties"],
18
+ string: ["enum", "minLength", "maxLength", "pattern"],
19
+ number: ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"],
20
+ integer: ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"],
21
+ };
22
+ const ALL_BRANCH_KEYWORDS = new Set(Object.values(BRANCH_KEYWORDS).flat());
23
+
24
+ /** Items for an array branch that declared none: any scalar. */
25
+ const ANY_SCALAR_ITEMS: Schema = {
26
+ anyOf: [{ type: "number" }, { type: "string" }, { type: "boolean" }],
27
+ };
28
+
29
+ function isSchema(value: unknown): value is Schema {
30
+ return typeof value === "object" && value !== null && !Array.isArray(value);
31
+ }
32
+
33
+ function branch(type: string, schema: Schema): Schema {
34
+ const out: Schema = { type };
35
+ for (const key of BRANCH_KEYWORDS[type] ?? []) {
36
+ if (!(key in schema)) continue;
37
+ const value = schema[key];
38
+ // An enum only constrains the branch whose type its values have.
39
+ if (key === "enum" && Array.isArray(value)) {
40
+ const kept = value.filter((v) => typeof v === "string");
41
+ if (kept.length > 0) out.enum = kept;
42
+ continue;
43
+ }
44
+ out[key] = value;
45
+ }
46
+ if (type === "array" && out.items === undefined) out.items = ANY_SCALAR_ITEMS;
47
+ return portableSchema(out);
48
+ }
49
+
50
+ /** A copy of `schema` with every type union rewritten as `anyOf`. */
51
+ export function portableSchema(schema: unknown): Schema {
52
+ if (!isSchema(schema)) return schema as Schema;
53
+ const out: Schema = {};
54
+ for (const [key, value] of Object.entries(schema)) {
55
+ if (key === "properties" && isSchema(value)) {
56
+ out.properties = Object.fromEntries(
57
+ Object.entries(value).map(([name, sub]) => [name, portableSchema(sub)]),
58
+ );
59
+ } else if (
60
+ (key === "items" || key === "additionalProperties") &&
61
+ isSchema(value)
62
+ ) {
63
+ out[key] = portableSchema(value);
64
+ } else if ((key === "anyOf" || key === "oneOf") && Array.isArray(value)) {
65
+ out[key] = value.map(portableSchema);
66
+ } else {
67
+ out[key] = value;
68
+ }
69
+ }
70
+ if (Array.isArray(out.type)) {
71
+ const types = out.type as string[];
72
+ const rest: Schema = {};
73
+ for (const [key, value] of Object.entries(out))
74
+ if (key !== "type" && !ALL_BRANCH_KEYWORDS.has(key)) rest[key] = value;
75
+ return { ...rest, anyOf: types.map((type) => branch(type, out)) };
76
+ }
77
+ if (out.type === "array" && out.items === undefined)
78
+ out.items = ANY_SCALAR_ITEMS;
79
+ return out;
80
+ }
@@ -23,6 +23,7 @@ import {
23
23
  } from "../audio/preview.ts";
24
24
  import { renderScorePcm, type RenderedAudio } from "../audio/wav.ts";
25
25
  import type { ScoreMeasurement } from "../audio/measure.ts";
26
+ import { VOICE_PREVIEWABLE_TOOLS } from "./voice-tools.ts";
26
27
  import type {
27
28
  ActionContext,
28
29
  AgentTool,
@@ -48,6 +49,8 @@ export const PREVIEWABLE_TOOLS: readonly string[] = Object.freeze([
48
49
  "set_granular",
49
50
  "set_drum_kit",
50
51
  "use_sound",
52
+ // 0.7 Voice: flagged per lane in voice-tools.ts.
53
+ ...VOICE_PREVIEWABLE_TOOLS,
51
54
  ]);
52
55
 
53
56
  const MAX_CHANGES = 8;
@@ -160,7 +163,7 @@ export function previewSoundTool(
160
163
  return {
161
164
  name: "preview_sound",
162
165
  description:
163
- "Hear a track, or a candidate sound change, without committing it. changes: sound tool calls to try ({tool, args} for set_fx, set_rig, set_synth, set_string, set_modal, set_wind, set_wavetable, set_instrument, set_sample, fit_sample, set_effects, set_mix, set_automation, set_drum_kit, use_sound), applied to a copy. Renders the track's notes over up to 4 bars (or a short phrase by role when it has none), solo or in context, and returns RMS/peak dBFS, spectral centroid and a one-line description for the current and the candidate sound. When the user's window is open it plays the snippet once. Then commit with the normal tools if it sounds right.",
166
+ "Hear a track, or a candidate sound change, without committing it. changes: sound tool calls to try ({tool, args} for set_fx, set_rig, set_synth, set_string, set_modal, set_wind, set_wavetable, set_instrument, set_sample, fit_sample, set_mix, set_automation, set_drum_kit, use_sound), applied to a copy. Renders the track's notes over up to 4 bars (or a short phrase by role when it has none), solo or in context, and returns RMS/peak dBFS, spectral centroid and a one-line description for the current and the candidate sound. When the user's window is open it plays the snippet once. Then commit with the normal tools if it sounds right.",
164
167
  parameters: {
165
168
  type: "object",
166
169
  properties: {
@@ -1,3 +1,4 @@
1
+ import { runCommandAgentTurn } from "./command-agent.ts";
1
2
  import { statSync } from "node:fs";
2
3
  import { join } from "node:path";
3
4
  import {
@@ -29,6 +30,7 @@ import {
29
30
  } from "./gateway.ts";
30
31
  import {
31
32
  defaultModelId,
33
+ fallbackModelId,
32
34
  MODEL_CATALOG,
33
35
  modelAlias,
34
36
  resolveModelChoice,
@@ -90,7 +92,7 @@ export function isApiSelection(
90
92
  return selection.kind === "gateway" || selection.kind === "openrouter";
91
93
  }
92
94
 
93
- export const LOGIN_HINT = "run `dawg login` to enable the agent";
95
+ export const LOGIN_HINT = "run `dawg model key` to enable the agent";
94
96
  /** The small, cheap model used for one-line helpers such as session names. */
95
97
  export const GATEWAY_SMALL_MODEL = "anthropic/claude-haiku-4.5";
96
98
  export const OPENROUTER_SMALL_MODEL = "anthropic/claude-haiku-4.5";
@@ -113,7 +115,7 @@ export function providerChoice(
113
115
 
114
116
  /**
115
117
  * The model for a key-based provider: `DAWG_MODEL`, then the model saved by
116
- * `/model` or `dawg login`, then Opus 5.5. Throws on an unknown `DAWG_MODEL`.
118
+ * `/model` or `dawg model key`, then Opus 5.5. Throws on an unknown `DAWG_MODEL`.
117
119
  */
118
120
  export function apiModelId(
119
121
  service: ApiProvider,
@@ -186,14 +188,14 @@ export async function selectProvider(
186
188
  }
187
189
  if (choice === service)
188
190
  return offline(
189
- `no ${service === "gateway" ? "AI Gateway" : "OpenRouter"} key any more; run \`dawg login ${service}\``,
191
+ `no ${service === "gateway" ? "AI Gateway" : "OpenRouter"} key any more; run \`dawg model key ${service}\``,
190
192
  );
191
193
  }
192
194
  const bin = resolveXcbBin(auth.env, auth.runner);
193
195
  if (!bin)
194
196
  return offline(
195
197
  explicit
196
- ? `xcb is not installed; run \`dawg login ${choice === "xcb" ? "--xcb" : choice}\``
198
+ ? `xcb is not installed; run \`dawg model key ${choice === "xcb" ? "--xcb" : choice}\``
197
199
  : `no model configured; ${LOGIN_HINT}`,
198
200
  );
199
201
  let capabilities: XcbCapabilities;
@@ -251,7 +253,7 @@ export async function selectProvider(
251
253
  "<account>",
252
254
  account.id,
253
255
  )
254
- }; run \`dawg login ${family === "xcb" ? "--xcb" : family}\``,
256
+ }; run \`dawg model key ${family === "xcb" ? "--xcb" : family}\``,
255
257
  );
256
258
  }
257
259
  // No saved pick: the first usable account, ready before admission pending.
@@ -274,7 +276,7 @@ export async function selectProvider(
274
276
  }
275
277
  return offline(
276
278
  explicit
277
- ? `no ${FAMILY_NAMES[choice] ?? choice} is ready; run \`dawg login ${choice === "xcb" ? "--xcb" : choice}\``
279
+ ? `no ${FAMILY_NAMES[choice] ?? choice} is ready; run \`dawg model key ${choice === "xcb" ? "--xcb" : choice}\``
278
280
  : `no model configured; ${LOGIN_HINT}`,
279
281
  );
280
282
  }
@@ -349,12 +351,24 @@ export async function runProviderTurn(
349
351
  ...(options.signal ? { signal: options.signal } : {}),
350
352
  ...(options.budget ? { budget: options.budget } : {}),
351
353
  };
352
- if (isApiSelection(selection))
354
+ if (isApiSelection(selection)) {
355
+ const model = options.model ?? selection.modelId;
356
+ const fallback = fallbackModelId(selection.kind, model);
357
+ if (options.host.commands && !options.model)
358
+ return runCommandAgentTurn({
359
+ ...common,
360
+ model,
361
+ ...(fallback ? { fallbackModel: fallback } : {}),
362
+ client: options.gatewayClient ?? apiClient(selection),
363
+ commands: options.host.commands,
364
+ });
353
365
  return runAgentTurn({
354
366
  ...common,
355
- model: options.model ?? selection.modelId,
367
+ model,
368
+ ...(fallback ? { fallbackModel: fallback } : {}),
356
369
  client: options.gatewayClient ?? apiClient(selection),
357
370
  });
371
+ }
358
372
  if (selection.kind === "xcb") {
359
373
  const runner = options.runner ?? systemRunner;
360
374
  return runTextAgentTurn({
@@ -75,7 +75,7 @@ export const RHYTHM_TOOLS: readonly AgentTool[] = Object.freeze([
75
75
  {
76
76
  name: "set_rhythm",
77
77
  description:
78
- "Preferred for drums: one row per voice of a kit/oneshot track; its notes regenerate from the row. pulses (4) over steps (16..64) Euclidean, rotate later; division 1/32..1/1 (1/16); grid 'x.X.' explicit (X accent); repeats 0..16 after each pulse every time, pace -1..1, ramp -1..1; velocity (0.8); accent 0..1 on E(accents,pulses); gate; legato; probability+seed; swing/nudge ±0.5 step; cycles [{pulses,rotate,repeats,probability,velocity}] per pass. remove [voices] (freeze keeps notes).",
78
+ "Preferred for drums: one row per voice of a kit/oneshot track; its notes regenerate from the row. pulses (4) over steps (16..64) Euclidean, rotate later; division 1/32..1/1 (1/16) is one step, and the steps×division cycle repeats from beat 0 to fill the loop (E(4,16) at 1/16 = a hit every beat, four on the floor; a backbeat on counts 2 and 4 is grid '....x.......x...' or E(2,16) rotate 4; offbeat 'and's are grid '..x.' ); grid 'x.X.' explicit (X accent); repeats 0..16 after each pulse every time, pace -1..1, ramp -1..1; velocity (0.8); accent 0..1 on E(accents,pulses); gate; legato; probability+seed; swing/nudge ±0.5 step; cycles [{pulses,rotate,repeats,probability,velocity}] per pass. remove [voices] (freeze keeps notes).",
79
79
  parameters: {
80
80
  type: "object",
81
81
  properties: {
@@ -380,7 +380,7 @@ export const SECTION_TOOLS: readonly AgentTool[] = Object.freeze([
380
380
  },
381
381
  {
382
382
  name: "add_transition",
383
- description: `Generate a transition with the built-in voices (bars count from 1). build: noise riser, accelerating snare roll, filter sweep and uplifter (all on by default), landing on the bar after its range. The range is \`bars\` bars (default 4) just before section \`into\` (the usual build into a drop or chorus); or a section's own bars (its last \`bars\` bars when bars is given); or \`bars\` bars starting at atBar; default the song's last 4 bars. The sweep is a high-pass up to 1.2 kHz on unfiltered or high-pass pitched tracks and a low-pass opening from cutoff/10 on low-pass tracks, on an octave curve, snapping back on the downbeat. drop: a pre-drop cut (cutBeats of silence, default 1, up to two bars) and an impact hit on the section's first bar or atBar (default: the section named drop, else chorus). fill: a drum fill (${FILL_STYLES.join(", ")}) in the last beats (default 1, ${FILL_BEATS_MIN} up to two bars) leading into the section or into atBar, plus a crash on its downbeat. Without section or atBar, fills go at every section boundary. Out-of-range values are clamped and the result says so.`,
383
+ description: `Generate a transition with the built-in voices (bars count from 1). build: noise riser, accelerating snare roll, filter sweep and uplifter (all on by default), landing on the bar after its range. The range is \`bars\` bars (default 4) just before section \`into\` (the usual build into a drop or chorus); or a section's own bars (its last \`bars\` bars when bars is given); or \`bars\` bars starting at atBar; default the song's last 4 bars. The sweep is a high-pass rising to 1.2 kHz on pitched tracks (a low-pass opening from cutoff/10 on low-pass tracks), snapping back on the downbeat. drop: a pre-drop cut (cutBeats of silence, default 1, up to two bars) and an impact hit on the section's first bar or atBar (default: the section named drop, else chorus). fill: a drum fill (${FILL_STYLES.join(", ")}) in the last beats (default 1, ${FILL_BEATS_MIN} up to two bars) leading into the section or into atBar, plus a crash on its downbeat. Without section or atBar, fills go at every section boundary. Out-of-range values are clamped.`,
384
384
  parameters: {
385
385
  type: "object",
386
386
  properties: {