@hraness/dawg 0.6.0 → 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.
- package/CHANGELOG.md +126 -0
- package/DAWG.md +655 -141
- package/README.md +27 -25
- package/core/autotune.ts +1119 -0
- package/core/chords.ts +759 -23
- package/core/clips.ts +499 -0
- package/core/diff.ts +182 -99
- package/core/expression.ts +158 -6
- package/core/fx.ts +354 -32
- package/core/granular.ts +91 -0
- package/core/instruments.ts +180 -0
- package/core/keys.ts +573 -4
- package/core/loop.ts +5 -0
- package/core/lyrics.ts +297 -0
- package/core/master.ts +3 -3
- package/core/resonators.ts +306 -4
- package/core/routing.ts +165 -0
- package/core/score.ts +1181 -16
- package/core/sdk/eval-child.ts +7 -2
- package/core/sdk/eval.ts +35 -6
- package/core/sdk/print.ts +354 -6
- package/core/sdk/sync-lyrics.ts +49 -0
- package/core/sdk/v1.ts +3160 -69
- package/core/sections.ts +298 -24
- package/core/sing.ts +815 -0
- package/core/strings.ts +237 -2
- package/core/style-provenance.ts +80 -0
- package/core/styles/africa-mena-southasia.ts +2893 -0
- package/core/styles/americas.ts +3810 -0
- package/core/styles/art.ts +4993 -0
- package/core/styles/base.ts +123 -0
- package/core/styles/cycles.ts +106 -0
- package/core/styles/electronic.ts +2723 -0
- package/core/styles/europe-asia-pacific.ts +2838 -0
- package/core/styles/excerpt.ts +29 -0
- package/core/styles/gamelan.ts +283 -0
- package/core/styles/generate.ts +2199 -0
- package/core/styles/index.ts +515 -0
- package/core/styles/parts.ts +106 -0
- package/core/styles/pop.ts +3189 -0
- package/core/styles/rock.ts +2993 -0
- package/core/styles/roots.ts +4175 -0
- package/core/styles/schema.ts +429 -0
- package/core/styles/taxonomy.ts +940 -0
- package/core/styles/validate.ts +528 -0
- package/core/tempo.ts +32 -2
- package/core/tuning.ts +19 -3
- package/core/vocoder.ts +524 -0
- package/core/winds.ts +652 -0
- package/guides/agent.md +29 -0
- package/guides/arrange.md +30 -0
- package/guides/audition.md +20 -14
- package/guides/automation.md +12 -6
- package/guides/chords.md +16 -14
- package/guides/effects.md +17 -15
- package/guides/faders.md +20 -16
- package/guides/files.md +13 -9
- package/guides/getting-started.md +15 -11
- package/guides/keys.md +19 -15
- package/guides/media.md +17 -12
- package/guides/mix.md +12 -6
- package/guides/music.md +23 -8
- package/guides/notes.md +15 -9
- package/guides/performance.md +15 -10
- package/guides/play.md +21 -13
- package/guides/project.md +25 -8
- package/guides/providers.md +20 -14
- package/guides/resample.md +32 -0
- package/guides/rhythm.md +17 -13
- package/guides/sessions.md +17 -7
- package/guides/show-me.md +31 -0
- package/guides/sound.md +25 -9
- package/guides/sounds.md +15 -12
- package/guides/styles.md +31 -0
- package/guides/tempo.md +15 -10
- package/guides/tracks.md +15 -10
- package/guides/tuning.md +32 -0
- package/guides/voice.md +31 -0
- package/guides/web-search.md +18 -8
- package/native/prebuilt/darwin-arm64/libdawg_sink.dylib +0 -0
- package/native/prebuilt/darwin-x64/libdawg_sink.dylib +0 -0
- package/native/prebuilt/linux-arm64/libdawg_sink.so +0 -0
- package/native/prebuilt/linux-x64/libdawg_sink.so +0 -0
- package/native/prebuilt/manifest.json +21 -0
- package/package.json +5 -2
- package/src/agent/agent.ts +131 -13
- package/src/agent/calibration-tools.ts +53 -0
- package/src/agent/chord-tools.ts +153 -1
- package/src/agent/clip-tools.ts +453 -0
- package/src/agent/command-agent.ts +369 -0
- package/src/agent/drum-tools.ts +2 -2
- package/src/agent/expression-tools.ts +91 -0
- package/src/agent/gateway.ts +246 -60
- package/src/agent/models.ts +53 -12
- package/src/agent/ops.ts +15 -1
- package/src/agent/pack-tools.ts +1 -1
- package/src/agent/planner.ts +13 -0
- package/src/agent/portable-schema.ts +80 -0
- package/src/agent/preview-tool.ts +5 -1
- package/src/agent/provider.ts +22 -8
- package/src/agent/resample-tool.ts +131 -0
- package/src/agent/rhythm-tools.ts +1 -1
- package/src/agent/section-tools.ts +1 -1
- package/src/agent/show-me.ts +497 -0
- package/src/agent/steer.ts +15 -0
- package/src/agent/style-tools.ts +217 -0
- package/src/agent/tool-error.ts +12 -0
- package/src/agent/tools.ts +307 -37
- package/src/agent/usage.ts +2 -2
- package/src/agent/voice-tools.ts +925 -0
- package/src/agent/xcb-agent.ts +16 -6
- package/src/argv.ts +38 -0
- package/src/audio/analysis.ts +253 -0
- package/src/audio/arrange.ts +58 -3
- package/src/audio/autotune-engine.ts +101 -0
- package/src/audio/autotune.ts +640 -0
- package/src/audio/clips.ts +240 -0
- package/src/audio/doctor.ts +86 -0
- package/src/audio/dsp/bandbank.ts +138 -0
- package/src/audio/dsp/envelope.ts +171 -0
- package/src/audio/dsp/follow.ts +120 -0
- package/src/audio/dsp/formant.ts +427 -0
- package/src/audio/dsp/glottal.ts +243 -0
- package/src/audio/dsp/interp.ts +7 -2
- package/src/audio/dsp/lpc.ts +50 -0
- package/src/audio/dsp/periodicity.ts +59 -0
- package/src/audio/dsp/pitch.ts +995 -0
- package/src/audio/dsp/psola.ts +199 -0
- package/src/audio/dsp/shift.ts +256 -0
- package/src/audio/effects/chain.ts +21 -4
- package/src/audio/effects/common.ts +57 -0
- package/src/audio/effects/convolution.ts +113 -5
- package/src/audio/effects/filter.ts +48 -69
- package/src/audio/effects/formant.ts +263 -0
- package/src/audio/effects/gaze.ts +354 -0
- package/src/audio/engine.ts +206 -33
- package/src/audio/fit.ts +80 -3
- package/src/audio/granular.ts +352 -21
- package/src/audio/instrument-check.ts +67 -47
- package/src/audio/instruments.ts +36 -3
- package/src/audio/keys/calibration.ts +56 -0
- package/src/audio/keys/electric.ts +427 -0
- package/src/audio/keys/engine.ts +156 -20
- package/src/audio/keys/organ.ts +1335 -0
- package/src/audio/keys/piano.ts +72 -8
- package/src/audio/keys/sympathetic.ts +127 -0
- package/src/audio/kits.ts +135 -6
- package/src/audio/live-worker.ts +3 -1
- package/src/audio/live.ts +247 -26
- package/src/audio/native.ts +615 -0
- package/src/audio/preview.ts +45 -3
- package/src/audio/render-worker.ts +2 -0
- package/src/audio/renderer.ts +2 -0
- package/src/audio/resample.ts +286 -0
- package/src/audio/resonators.ts +10 -2
- package/src/audio/sampler.ts +252 -24
- package/src/audio/samples.ts +46 -3
- package/src/audio/sing/analysis.ts +193 -0
- package/src/audio/sing/engine.ts +949 -0
- package/src/audio/strings/body.ts +29 -16
- package/src/audio/strings/bow.ts +699 -0
- package/src/audio/strings/engine.ts +274 -22
- package/src/audio/strings/measure.test-helpers.ts +2 -0
- package/src/audio/synth/oscillators.ts +31 -21
- package/src/audio/synth/voice.ts +34 -1
- package/src/audio/vocoder/bank.ts +314 -0
- package/src/audio/vocoder/carrier.ts +165 -0
- package/src/audio/vocoder/control.ts +68 -0
- package/src/audio/vocoder/detect.ts +50 -0
- package/src/audio/vocoder/index.ts +304 -0
- package/src/audio/vocoder/talkbox.ts +143 -0
- package/src/audio/wav.ts +608 -85
- package/src/audio/winds/engine.ts +306 -0
- package/src/audio/winds/filters.ts +153 -0
- package/src/audio/winds/pitch.ts +104 -0
- package/src/audio/winds/trim.ts +104 -0
- package/src/audio/winds/trims.ts +917 -0
- package/src/audio/winds/trims1.ts +297 -0
- package/src/audio/winds/voice.ts +492 -0
- package/src/auth/cli.ts +38 -36
- package/src/auth/credentials.ts +30 -1
- package/src/auth/login.ts +15 -9
- package/src/auth/tui.ts +19 -10
- package/src/commands/arrange.ts +44 -29
- package/src/commands/autotune.ts +421 -0
- package/src/commands/calibration.ts +74 -0
- package/src/commands/clips.ts +887 -0
- package/src/commands/drums.ts +3 -2
- package/src/commands/edit.ts +11 -4
- package/src/commands/expression.ts +104 -23
- package/src/commands/formant.ts +221 -0
- package/src/commands/fx.ts +105 -33
- package/src/commands/grammar.ts +558 -0
- package/src/commands/granular.ts +7 -2
- package/src/commands/help.ts +652 -339
- package/src/commands/history.ts +18 -0
- package/src/commands/keys.ts +293 -17
- package/src/commands/modal.ts +67 -4
- package/src/commands/music.ts +1 -1
- package/src/commands/nearest.ts +53 -0
- package/src/commands/pack.ts +9 -2
- package/src/commands/param-range.ts +56 -0
- package/src/commands/parses.ts +130 -0
- package/src/commands/progression.ts +170 -0
- package/src/commands/resample.ts +281 -0
- package/src/commands/rhythm.ts +3 -0
- package/src/commands/rig.ts +8 -25
- package/src/commands/sample.ts +49 -2
- package/src/commands/shift.ts +119 -0
- package/src/commands/sing.ts +478 -0
- package/src/commands/string.ts +28 -2
- package/src/commands/strum.ts +485 -0
- package/src/commands/style.ts +415 -0
- package/src/commands/time.ts +6 -3
- package/src/commands/tuning.ts +3 -3
- package/src/commands/vocal-pitch.ts +616 -0
- package/src/commands/vocal.ts +147 -0
- package/src/commands/vocoder.ts +627 -0
- package/src/commands/wind.ts +244 -0
- package/src/fs/durable.ts +50 -0
- package/src/lang/glossary.ts +493 -0
- package/src/launch-args.ts +163 -0
- package/src/main.ts +1521 -255
- package/src/media/cli.ts +20 -3
- package/src/media/import.ts +3 -1
- package/src/project/check.ts +26 -3
- package/src/project/clip-pins.ts +72 -0
- package/src/project/init.ts +23 -8
- package/src/project/sync.ts +418 -86
- package/src/render.ts +36 -1
- package/src/session/daemon.ts +3 -0
- package/src/session/meta.ts +14 -0
- package/src/session/origin.ts +154 -0
- package/src/session/port.ts +22 -4
- package/src/session/presence.ts +50 -7
- package/src/session/protocol.ts +5 -1
- package/src/session/rebase.ts +18 -5
- package/src/session/receipt.ts +258 -0
- package/src/session/store.ts +65 -43
- package/src/tui/arrange-menu.ts +65 -31
- package/src/tui/audition.ts +1 -1
- package/src/tui/euclid.ts +18 -13
- package/src/tui/fader.ts +228 -41
- package/src/tui/granular-menu.ts +2 -4
- package/src/tui/menu-clips.ts +297 -0
- package/src/tui/menu-time.ts +20 -13
- package/src/tui/menu-voice.ts +405 -0
- package/src/tui/menu.ts +1377 -193
- package/src/tui/modal-menu.ts +38 -11
- package/src/tui/performance-menu.ts +46 -1
- package/src/tui/play-chords.ts +108 -5
- package/src/tui/play-mode.ts +16 -1
- package/src/tui/play-session.ts +81 -16
- package/src/tui/sing-menu.ts +278 -0
- package/src/tui/style-menu.ts +104 -0
- package/src/tui/vocoder-menu.ts +244 -0
- package/src/tui/wind-menu.ts +144 -0
- package/src/version.ts +8 -0
- package/src/web/fetch.ts +115 -29
- package/tui/activity.ts +180 -9
- package/tui/app.ts +274 -40
- package/tui/clip-row.ts +132 -0
- package/tui/delight.ts +144 -0
- package/tui/drawer.ts +70 -22
- package/tui/frame-gate.ts +76 -0
- package/tui/grammar.ts +112 -63
- package/tui/guide.ts +42 -4
- package/tui/highway.ts +269 -25
- package/tui/hints.ts +192 -0
- package/tui/input.ts +60 -9
- package/tui/keys.ts +1 -1
- package/tui/play-strip.ts +68 -14
- package/tui/prompt.ts +1 -1
- package/tui/screen.ts +144 -14
- 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,
|
|
@@ -40,6 +41,7 @@ export const PREVIEWABLE_TOOLS: readonly string[] = Object.freeze([
|
|
|
40
41
|
"set_synth",
|
|
41
42
|
"set_string",
|
|
42
43
|
"set_modal",
|
|
44
|
+
"set_wind",
|
|
43
45
|
"set_sample",
|
|
44
46
|
"fit_sample",
|
|
45
47
|
"set_effects",
|
|
@@ -47,6 +49,8 @@ export const PREVIEWABLE_TOOLS: readonly string[] = Object.freeze([
|
|
|
47
49
|
"set_granular",
|
|
48
50
|
"set_drum_kit",
|
|
49
51
|
"use_sound",
|
|
52
|
+
// 0.7 Voice: flagged per lane in voice-tools.ts.
|
|
53
|
+
...VOICE_PREVIEWABLE_TOOLS,
|
|
50
54
|
]);
|
|
51
55
|
|
|
52
56
|
const MAX_CHANGES = 8;
|
|
@@ -159,7 +163,7 @@ export function previewSoundTool(
|
|
|
159
163
|
return {
|
|
160
164
|
name: "preview_sound",
|
|
161
165
|
description:
|
|
162
|
-
"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_wavetable, set_instrument, set_sample, fit_sample,
|
|
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.",
|
|
163
167
|
parameters: {
|
|
164
168
|
type: "object",
|
|
165
169
|
properties: {
|
package/src/agent/provider.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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({
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool `resample` (0.6.1): the `resample` prompt command for the
|
|
3
|
+
* model. Renders a track, an orbit or the master over the song, a section
|
|
4
|
+
* or a bar range to a pinned WAV under the project and adds a sampler (or,
|
|
5
|
+
* with grain, a granular) track that plays it, committed as ordinary score
|
|
6
|
+
* operations.
|
|
7
|
+
*/
|
|
8
|
+
import { SCORE_LIMITS } from "../../core/score.ts";
|
|
9
|
+
import {
|
|
10
|
+
parseResampleCommand,
|
|
11
|
+
runResample,
|
|
12
|
+
type ResampleCommand,
|
|
13
|
+
} from "../commands/resample.ts";
|
|
14
|
+
import { hasSamplerTracks, SampleLibrary } from "../audio/samples.ts";
|
|
15
|
+
import type { AgentTool } from "./tools.ts";
|
|
16
|
+
|
|
17
|
+
class ResampleToolError extends Error {
|
|
18
|
+
constructor(message: string) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "ToolArgumentError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The tool's arguments as the prompt command they stand for. */
|
|
25
|
+
export function resampleToolCommand(
|
|
26
|
+
args: Record<string, unknown>,
|
|
27
|
+
focusedTrackId: string,
|
|
28
|
+
): ResampleCommand {
|
|
29
|
+
const words = ["resample"];
|
|
30
|
+
const source = args.source ?? "track";
|
|
31
|
+
if (source === "track") {
|
|
32
|
+
const trackId = args.trackId ?? focusedTrackId;
|
|
33
|
+
if (typeof trackId !== "string" || trackId.length === 0)
|
|
34
|
+
throw new ResampleToolError("trackId must be a track id");
|
|
35
|
+
words.push(trackId);
|
|
36
|
+
} else if (source === "orbit") {
|
|
37
|
+
if (!Number.isInteger(args.orbit))
|
|
38
|
+
throw new ResampleToolError("orbit must be an integer");
|
|
39
|
+
words.push("orbit", String(args.orbit));
|
|
40
|
+
} else if (source === "master") words.push("master");
|
|
41
|
+
else throw new ResampleToolError("source is track, orbit or master");
|
|
42
|
+
if (args.section !== undefined) {
|
|
43
|
+
if (typeof args.section !== "string" || args.section.length === 0)
|
|
44
|
+
throw new ResampleToolError("section must be a section name");
|
|
45
|
+
words.push("section", args.section);
|
|
46
|
+
} else if (args.bars !== undefined) {
|
|
47
|
+
const bars = args.bars;
|
|
48
|
+
if (
|
|
49
|
+
!Array.isArray(bars) ||
|
|
50
|
+
bars.length !== 2 ||
|
|
51
|
+
!bars.every((bar) => Number.isInteger(bar))
|
|
52
|
+
)
|
|
53
|
+
throw new ResampleToolError("bars must be [first, last], 1-based");
|
|
54
|
+
words.push("bars", `${bars[0]}-${bars[1]}`);
|
|
55
|
+
}
|
|
56
|
+
if (args.post === true) words.push("post");
|
|
57
|
+
if (args.grain === true) words.push("grain");
|
|
58
|
+
if (args.as !== undefined) {
|
|
59
|
+
if (typeof args.as !== "string")
|
|
60
|
+
throw new ResampleToolError("as must be a track id");
|
|
61
|
+
words.push("as", args.as);
|
|
62
|
+
}
|
|
63
|
+
const command = parseResampleCommand(words.join(" "));
|
|
64
|
+
if (!command) throw new ResampleToolError(`cannot ${words.join(" ")}`);
|
|
65
|
+
return command;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const RESAMPLE_TOOLS: readonly AgentTool[] = Object.freeze([
|
|
69
|
+
{
|
|
70
|
+
name: "resample",
|
|
71
|
+
description:
|
|
72
|
+
"Bounce a track (default focused), an orbit (no master) or the master over the song, a section or bars [first,last] to a pinned WAV, adding a one-shot sampler track that plays it in place; grain adds a granular track instead; post keeps the master chain.",
|
|
73
|
+
parameters: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
source: { type: "string", enum: ["track", "orbit", "master"] },
|
|
77
|
+
trackId: { type: "string", maxLength: SCORE_LIMITS.maxIdLength },
|
|
78
|
+
orbit: { type: "integer", minimum: 1, maximum: 16 },
|
|
79
|
+
section: { type: "string" },
|
|
80
|
+
bars: {
|
|
81
|
+
type: "array",
|
|
82
|
+
items: { type: "integer", minimum: 1 },
|
|
83
|
+
minItems: 2,
|
|
84
|
+
maxItems: 2,
|
|
85
|
+
},
|
|
86
|
+
grain: { type: "boolean" },
|
|
87
|
+
post: { type: "boolean" },
|
|
88
|
+
as: { type: "string", maxLength: SCORE_LIMITS.maxIdLength },
|
|
89
|
+
},
|
|
90
|
+
additionalProperties: false,
|
|
91
|
+
},
|
|
92
|
+
plan(args, context) {
|
|
93
|
+
const command = resampleToolCommand(args, context.focusedTrackId);
|
|
94
|
+
return {
|
|
95
|
+
kind: "prepare",
|
|
96
|
+
summary: "resample",
|
|
97
|
+
run: async (action) => {
|
|
98
|
+
const root = action.workspace?.root ?? process.cwd();
|
|
99
|
+
const samples = hasSamplerTracks(context.score)
|
|
100
|
+
? await new SampleLibrary({
|
|
101
|
+
projectRoot: root,
|
|
102
|
+
...(action.packs ? { packs: action.packs } : {}),
|
|
103
|
+
}).load(context.score)
|
|
104
|
+
: undefined;
|
|
105
|
+
const result = await runResample({
|
|
106
|
+
projectRoot: root,
|
|
107
|
+
score: context.score,
|
|
108
|
+
command,
|
|
109
|
+
...(samples ? { samples } : {}),
|
|
110
|
+
});
|
|
111
|
+
if (!result.ok) throw new ResampleToolError(result.message);
|
|
112
|
+
const track = result.next.tracks.find(
|
|
113
|
+
(t) => t.id === result.trackId,
|
|
114
|
+
)!;
|
|
115
|
+
const notes = result.next.notes.filter(
|
|
116
|
+
(note) => note.trackId === result.trackId,
|
|
117
|
+
);
|
|
118
|
+
return {
|
|
119
|
+
kind: "score",
|
|
120
|
+
operations: [
|
|
121
|
+
{ type: "addTrack", track },
|
|
122
|
+
...notes.map((note) => ({ type: "addNote" as const, note })),
|
|
123
|
+
],
|
|
124
|
+
summary: result.message,
|
|
125
|
+
trackId: result.trackId,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
]);
|
|
@@ -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
|
|
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: {
|