@hraness/dawg 0.6.1 → 0.8.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 +118 -0
- package/DAWG.md +454 -140
- package/README.md +30 -26
- package/core/autotune.ts +1119 -0
- package/core/chords.ts +271 -23
- package/core/clips.ts +499 -0
- package/core/diff.ts +184 -104
- package/core/expression.ts +15 -0
- package/core/fx.ts +99 -29
- package/core/ids.ts +52 -0
- package/core/instruments.ts +19 -0
- package/core/keys.ts +3 -3
- package/core/loop.ts +8 -0
- package/core/lyrics.ts +297 -0
- package/core/master.ts +3 -3
- package/core/range.ts +581 -0
- package/core/resonators.ts +16 -2
- package/core/routing.ts +165 -0
- package/core/score.ts +955 -14
- package/core/sdk/eval-child.ts +7 -2
- package/core/sdk/eval.ts +35 -6
- package/core/sdk/print.ts +287 -5
- package/core/sdk/sync-lyrics.ts +49 -0
- package/core/sdk/v1.ts +2132 -46
- package/core/sections.ts +480 -35
- package/core/sing.ts +815 -0
- 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/guides/agent.md +29 -0
- package/guides/arrange.md +31 -0
- package/guides/audio.md +31 -0
- package/guides/audition.md +20 -14
- package/guides/automation.md +12 -7
- package/guides/chords.md +15 -15
- package/guides/effects.md +17 -16
- 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 +15 -7
- package/guides/music.md +23 -8
- package/guides/notes.md +15 -9
- package/guides/panes.md +32 -0
- package/guides/performance.md +15 -13
- package/guides/play.md +21 -13
- package/guides/project.md +26 -8
- package/guides/providers.md +20 -14
- package/guides/resample.md +15 -9
- package/guides/rhythm.md +18 -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 -13
- package/guides/styles.md +31 -0
- package/guides/tape.md +32 -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 +6 -2
- package/src/agent/agent.ts +130 -18
- package/src/agent/calibration-tools.ts +53 -0
- package/src/agent/clip-tools.ts +453 -0
- package/src/agent/command-agent.ts +378 -0
- package/src/agent/drum-tools.ts +2 -2
- package/src/agent/expression-tools.ts +1 -1
- package/src/agent/gateway.ts +246 -60
- package/src/agent/models.ts +53 -12
- package/src/agent/ops.ts +27 -1
- package/src/agent/pack-tools.ts +1 -1
- package/src/agent/planner.ts +25 -0
- package/src/agent/portable-schema.ts +80 -0
- package/src/agent/preview-tool.ts +4 -1
- package/src/agent/provider.ts +22 -8
- package/src/agent/range-tools.ts +216 -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 +110 -23
- package/src/agent/usage.ts +2 -2
- package/src/agent/voice-tools.ts +925 -0
- package/src/agent/xcb-agent.ts +13 -10
- package/src/argv.ts +38 -0
- package/src/audio/analysis.ts +253 -0
- package/src/audio/arrange.ts +49 -5
- package/src/audio/audio-command.ts +218 -0
- 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/devices.ts +264 -0
- package/src/audio/doctor.ts +157 -0
- package/src/audio/dsp/bandbank.ts +138 -0
- package/src/audio/dsp/envelope.ts +10 -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/effects/chain.ts +3 -1
- package/src/audio/effects/common.ts +43 -0
- package/src/audio/effects/convolution.ts +7 -4
- package/src/audio/effects/filter.ts +48 -69
- package/src/audio/effects/formant.ts +263 -0
- package/src/audio/engine.ts +360 -40
- package/src/audio/fit.ts +35 -3
- package/src/audio/instrument-check.ts +59 -43
- package/src/audio/instruments.ts +4 -0
- package/src/audio/keys/calibration.ts +56 -0
- package/src/audio/keys/electric.ts +8 -1
- package/src/audio/keys/engine.ts +13 -1
- package/src/audio/keys/piano.ts +22 -2
- package/src/audio/kits.ts +135 -6
- package/src/audio/live.ts +114 -20
- package/src/audio/native.ts +615 -0
- package/src/audio/preview.ts +30 -2
- package/src/audio/render-worker.ts +2 -0
- package/src/audio/renderer.ts +2 -0
- package/src/audio/resample.ts +2 -1
- package/src/audio/sampler.ts +85 -4
- package/src/audio/samples.ts +20 -2
- package/src/audio/sing/analysis.ts +193 -0
- package/src/audio/sing/engine.ts +949 -0
- package/src/audio/strings/bow.ts +48 -5
- package/src/audio/strings/engine.ts +8 -2
- 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 +500 -77
- package/src/audio/winds/engine.ts +5 -1
- package/src/audio/winds/trim.ts +28 -4
- package/src/audio/winds/trims1.ts +297 -0
- package/src/audio/winds/voice.ts +15 -2
- 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 +82 -32
- 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 +1 -1
- package/src/commands/formant.ts +221 -0
- package/src/commands/fx.ts +101 -32
- package/src/commands/grammar.ts +560 -0
- package/src/commands/help.ts +774 -366
- package/src/commands/history.ts +139 -10
- package/src/commands/keys.ts +25 -8
- package/src/commands/modal.ts +1 -1
- 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 +135 -0
- package/src/commands/progression.ts +170 -0
- package/src/commands/range.ts +763 -0
- package/src/commands/resample.ts +13 -10
- package/src/commands/rhythm.ts +3 -0
- package/src/commands/rig.ts +3 -24
- package/src/commands/sample.ts +11 -1
- package/src/commands/sing.ts +474 -0
- package/src/commands/strum.ts +13 -1
- 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 +617 -0
- package/src/commands/vocal.ts +147 -0
- package/src/commands/vocoder.ts +627 -0
- package/src/commands/wind.ts +2 -2
- package/src/fs/durable.ts +50 -0
- package/src/identity/actor.ts +127 -0
- package/src/lang/glossary.ts +511 -0
- package/src/launch-args.ts +267 -0
- package/src/main.ts +2508 -308
- package/src/media/cli.ts +20 -3
- package/src/media/import.ts +3 -1
- package/src/project/check.ts +21 -2
- 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 +20 -1
- package/src/session/client.ts +107 -3
- package/src/session/clipboard.ts +79 -0
- package/src/session/daemon.ts +199 -5
- package/src/session/live-host.ts +232 -0
- package/src/session/meta.ts +14 -0
- package/src/session/origin.ts +174 -0
- package/src/session/port.ts +109 -14
- package/src/session/presence.ts +34 -4
- package/src/session/protocol.ts +365 -7
- package/src/session/rebase.ts +63 -12
- package/src/session/receipt.ts +265 -0
- package/src/session/shared-live.ts +131 -0
- package/src/session/store.ts +126 -45
- package/src/tui/arrange-menu.ts +225 -23
- package/src/tui/audition.ts +1 -1
- package/src/tui/euclid.ts +113 -27
- package/src/tui/fader.ts +282 -42
- package/src/tui/granular-menu.ts +2 -4
- package/src/tui/knob-fields.ts +107 -0
- package/src/tui/knob-map.ts +198 -0
- 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 +987 -179
- package/src/tui/modal-menu.ts +6 -6
- package/src/tui/performance-menu.ts +5 -2
- package/src/tui/play-chords.ts +4 -2
- package/src/tui/play-mode.ts +15 -1
- package/src/tui/play-session.ts +243 -28
- package/src/tui/sing-menu.ts +278 -0
- package/src/tui/style-menu.ts +104 -0
- package/src/tui/tape-mode.ts +580 -0
- package/src/tui/tape-view.ts +263 -0
- package/src/tui/vocoder-menu.ts +244 -0
- package/src/tui/wind-menu.ts +3 -3
- package/src/version.ts +8 -0
- package/src/web/fetch.ts +115 -29
- package/tui/activity.ts +180 -9
- package/tui/app.ts +491 -84
- package/tui/clip-row.ts +132 -0
- package/tui/delight.ts +193 -0
- package/tui/drawer.ts +233 -27
- package/tui/frame-gate.ts +76 -0
- package/tui/grammar.ts +143 -65
- package/tui/guide.ts +50 -5
- package/tui/highway.ts +309 -30
- package/tui/hints.ts +197 -0
- package/tui/hits.ts +7 -1
- package/tui/input.ts +60 -9
- package/tui/keys.ts +1 -1
- package/tui/knobs.ts +268 -0
- package/tui/play-strip.ts +80 -14
- package/tui/prompt.ts +1 -1
- package/tui/screen.ts +151 -18
- package/tui/tape.ts +439 -0
- package/tui/text.ts +35 -2
- package/tui/theme.ts +46 -1
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command mode (show-me): the agent acts by writing dawg prompt commands, one
|
|
3
|
+
* per line, the way a human types them. Each line runs through the host's
|
|
4
|
+
* command path (the same parser and commit a typed Enter uses) the moment
|
|
5
|
+
* it is complete, while the model is still streaming the next one, so the
|
|
6
|
+
* score changes during the turn. The partial line is reported as it grows
|
|
7
|
+
* (`command-typing`) for the prompt bar's ghost text. A small JSON tool set
|
|
8
|
+
* stays for what commands cannot express: workspace files, the web, media
|
|
9
|
+
* and measuring. Measured against the JSON tool loop on the same tasks this
|
|
10
|
+
* sends about a ninth of the input tokens and finishes in about half the
|
|
11
|
+
* time (docs/show-me.md).
|
|
12
|
+
*/
|
|
13
|
+
import { newId } from "../../core/ids.ts";
|
|
14
|
+
import { HELP_SECTIONS } from "../commands/help.ts";
|
|
15
|
+
import { MEDIA_TOOLS } from "../media/tools.ts";
|
|
16
|
+
import {
|
|
17
|
+
AGENT_LIMITS,
|
|
18
|
+
MEDIA_PROMPT,
|
|
19
|
+
WORKSPACE_PROMPT,
|
|
20
|
+
classifyAgentError,
|
|
21
|
+
executeCall,
|
|
22
|
+
hostProjectOutline,
|
|
23
|
+
tighten,
|
|
24
|
+
turnDeadline,
|
|
25
|
+
type AgentEvent,
|
|
26
|
+
type AgentTurnOptions,
|
|
27
|
+
type AgentTurnResult,
|
|
28
|
+
} from "./agent.ts";
|
|
29
|
+
import { compositionBrief } from "./brief.ts";
|
|
30
|
+
import type { ChatMessage, ChatToolCall } from "./gateway.ts";
|
|
31
|
+
import { CommandLines } from "./show-me.ts";
|
|
32
|
+
import { AGENT_TOOLS, chatTools } from "./tools.ts";
|
|
33
|
+
import { SseBudgetError } from "./sse.ts";
|
|
34
|
+
|
|
35
|
+
/** What a host does with one complete command line. */
|
|
36
|
+
export type CommandOutcome = Readonly<{
|
|
37
|
+
ok: boolean;
|
|
38
|
+
/** The receipt a human would see (`tempo · 96`) or the error. */
|
|
39
|
+
message: string;
|
|
40
|
+
baseRevision: number;
|
|
41
|
+
resultRevision: number;
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
export type CommandHost = Readonly<{
|
|
45
|
+
/** Whether a complete line is a song-editing command (else it is prose). */
|
|
46
|
+
isCommand(line: string): boolean;
|
|
47
|
+
/** Run a line exactly as if the user had typed it and pressed Enter. */
|
|
48
|
+
run(line: string): Promise<CommandOutcome>;
|
|
49
|
+
}>;
|
|
50
|
+
|
|
51
|
+
/** The tools a command-mode model still gets: what commands cannot say. */
|
|
52
|
+
export const COMMAND_MODE_TOOL_NAMES: ReadonlySet<string> = new Set([
|
|
53
|
+
"list_files",
|
|
54
|
+
"read_file",
|
|
55
|
+
"write_file",
|
|
56
|
+
"edit_file",
|
|
57
|
+
"web_search",
|
|
58
|
+
"fetch_url",
|
|
59
|
+
"explain",
|
|
60
|
+
"preview_sound",
|
|
61
|
+
"measure_mix",
|
|
62
|
+
...MEDIA_TOOLS.map((tool) => tool.name),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
/** Steps per command-mode turn: each extra one is a correction round. */
|
|
66
|
+
export const COMMAND_MAX_STEPS = 4;
|
|
67
|
+
|
|
68
|
+
/** Reference groups the command agent is not shown. */
|
|
69
|
+
const AGENT_SKIPS: ReadonlySet<string> = new Set([
|
|
70
|
+
"project · window",
|
|
71
|
+
"project · panes",
|
|
72
|
+
"project · audio",
|
|
73
|
+
"agent",
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
function commandReference(): string {
|
|
77
|
+
// Song commands only. Panes, audio devices and the window groups change the person's window or
|
|
78
|
+
// machine; the clipboard row is a two-step buffer the agent never needs.
|
|
79
|
+
return HELP_SECTIONS.filter((section) => !AGENT_SKIPS.has(section.group))
|
|
80
|
+
.flatMap((section) =>
|
|
81
|
+
section.entries
|
|
82
|
+
.filter(
|
|
83
|
+
(entry) => !/\bpaste\b/.test(`${entry.command} ${entry.summary}`),
|
|
84
|
+
)
|
|
85
|
+
.map((entry) => `${entry.command} — ${entry.summary}`),
|
|
86
|
+
)
|
|
87
|
+
.join("\n");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const COMMAND_AGENT_PROMPT = [
|
|
91
|
+
"You are dawg, a loop composer inside a terminal music workstation, and you teach it by doing.",
|
|
92
|
+
"Act by writing dawg prompt commands, exactly as a human types them in the prompt bar, one per line. The user watches each command appear in their prompt bar as you write it, and each line runs the moment it ends, so they learn the commands by watching you.",
|
|
93
|
+
"Prefer the gestures a person would use: a parameter as one value command (fx reverb mix 0.4, volume 0.7, pan -0.3, which the user sees as a fader moving), notes one per line with add (add C4 at 0 for 0.5, played on the user's keyboard as you write them), drums with hit, pattern and euclid on a kit track, a whole groove with groove <name>.",
|
|
94
|
+
"To arrange, name bars: loop 5-6 loops them, copy bass 5-6 to 7 and move bass 5-6 to 9 place them in one step, clear bass 5-6 empties them. Never copy to the clipboard and paste.",
|
|
95
|
+
"Commands apply to the focused track. /track <name> focuses a track or creates it; instrument kit makes a drum track. Times are beats from 0: in 4/4 musicians' beats 2 and 4 are beats 1 and 3 here, and bar n starts at (n-1)×beats per bar.",
|
|
96
|
+
"Write commands first, with no numbering, quotes or code fences. Then write one short plain sentence (not a command) that says what changed and names the key command so the user can do it by hand next time.",
|
|
97
|
+
"If a command fails you get its error; correct it with another command or stop.",
|
|
98
|
+
"Use the tools only for what commands cannot do: project files, the web, media, previewing and measuring.",
|
|
99
|
+
WORKSPACE_PROMPT,
|
|
100
|
+
MEDIA_PROMPT,
|
|
101
|
+
].join(" ");
|
|
102
|
+
|
|
103
|
+
/** The system prompt with the command reference, built once. */
|
|
104
|
+
let systemPrompt: string | undefined;
|
|
105
|
+
export function commandAgentSystemPrompt(): string {
|
|
106
|
+
systemPrompt ??= `${COMMAND_AGENT_PROMPT}\n\nCommands:\n${commandReference()}\n/track <name> — focus or create a track`;
|
|
107
|
+
return systemPrompt;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type CommandAgentTurnOptions = AgentTurnOptions &
|
|
111
|
+
Readonly<{ commands: CommandHost }>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* One command-mode turn. Lines run in order on one chain, so a slow command
|
|
115
|
+
* never reorders the next, and reading the stream never waits on them.
|
|
116
|
+
*/
|
|
117
|
+
export async function runCommandAgentTurn(
|
|
118
|
+
options: CommandAgentTurnOptions,
|
|
119
|
+
): Promise<AgentTurnResult> {
|
|
120
|
+
const limits = {
|
|
121
|
+
maxSteps: Math.min(
|
|
122
|
+
COMMAND_MAX_STEPS,
|
|
123
|
+
tighten(options.budget?.maxSteps, AGENT_LIMITS.maxSteps),
|
|
124
|
+
),
|
|
125
|
+
maxToolCalls: tighten(
|
|
126
|
+
options.budget?.maxToolCalls,
|
|
127
|
+
AGENT_LIMITS.maxToolCalls,
|
|
128
|
+
),
|
|
129
|
+
maxResponseBytes: tighten(
|
|
130
|
+
options.budget?.maxResponseBytes,
|
|
131
|
+
AGENT_LIMITS.maxResponseBytes,
|
|
132
|
+
),
|
|
133
|
+
timeoutMs: tighten(options.budget?.timeoutMs, AGENT_LIMITS.timeoutMs),
|
|
134
|
+
};
|
|
135
|
+
const emit = (event: AgentEvent) => {
|
|
136
|
+
try {
|
|
137
|
+
options.onEvent?.(event);
|
|
138
|
+
} catch {
|
|
139
|
+
// A rendering failure must never corrupt the turn.
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const allTools = options.tools ?? AGENT_TOOLS;
|
|
143
|
+
const offered = chatTools(
|
|
144
|
+
allTools.filter((tool) => COMMAND_MODE_TOOL_NAMES.has(tool.name)),
|
|
145
|
+
);
|
|
146
|
+
const newNoteId =
|
|
147
|
+
options.newNoteId ??
|
|
148
|
+
((trackId: string, _revision: number, _index: number) => newId(trackId));
|
|
149
|
+
const deadline = turnDeadline(limits.timeoutMs);
|
|
150
|
+
const signal = options.signal
|
|
151
|
+
? AbortSignal.any([options.signal, deadline.signal])
|
|
152
|
+
: deadline.signal;
|
|
153
|
+
const currentRevision = () => options.host.snapshot().revision;
|
|
154
|
+
const turnStartRevision = currentRevision();
|
|
155
|
+
|
|
156
|
+
let applied = 0;
|
|
157
|
+
let rejected = 0;
|
|
158
|
+
let toolCalls = 0;
|
|
159
|
+
let bytesUsed = 0;
|
|
160
|
+
let finalText = "";
|
|
161
|
+
const messages: ChatMessage[] = [
|
|
162
|
+
{ role: "user", content: options.prompt.slice(0, 8_000) },
|
|
163
|
+
];
|
|
164
|
+
const finish = (result: AgentTurnResult): AgentTurnResult => {
|
|
165
|
+
deadline.clear();
|
|
166
|
+
emit(result);
|
|
167
|
+
return result;
|
|
168
|
+
};
|
|
169
|
+
const done = (reason: "stop" | "max-steps" | "max-tool-calls") =>
|
|
170
|
+
finish({
|
|
171
|
+
type: "done",
|
|
172
|
+
reason,
|
|
173
|
+
text: finalText,
|
|
174
|
+
applied,
|
|
175
|
+
rejected,
|
|
176
|
+
revision: currentRevision(),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
for (let step = 1; step <= limits.maxSteps; step += 1) {
|
|
181
|
+
if (signal.aborted) throw signal.reason;
|
|
182
|
+
for (const steer of options.host.takeSteering?.() ?? [])
|
|
183
|
+
messages.push({
|
|
184
|
+
role: "user",
|
|
185
|
+
content: `Steering from the user mid-turn: ${steer.slice(0, 2_000)}`,
|
|
186
|
+
});
|
|
187
|
+
emit({ type: "step", step });
|
|
188
|
+
const snapshot = options.host.snapshot();
|
|
189
|
+
const brief = compositionBrief({
|
|
190
|
+
...snapshot,
|
|
191
|
+
project: await hostProjectOutline(options.host, snapshot),
|
|
192
|
+
});
|
|
193
|
+
const briefText =
|
|
194
|
+
snapshot.revision === turnStartRevision
|
|
195
|
+
? `Composition brief (JSON): ${brief}`
|
|
196
|
+
: `Composition brief (JSON, current score at revision ${snapshot.revision}: it already includes every command you ran this turn; do not repeat them): ${brief}`;
|
|
197
|
+
const remaining = limits.maxResponseBytes - bytesUsed;
|
|
198
|
+
if (remaining <= 0) throw new SseBudgetError(limits.maxResponseBytes);
|
|
199
|
+
|
|
200
|
+
const lines = new CommandLines();
|
|
201
|
+
let text = "";
|
|
202
|
+
const prose: string[] = [];
|
|
203
|
+
const results: string[] = [];
|
|
204
|
+
let failed = 0;
|
|
205
|
+
let chain: Promise<void> = Promise.resolve();
|
|
206
|
+
const runLine = (line: string) => {
|
|
207
|
+
if (!options.commands.isCommand(line)) {
|
|
208
|
+
prose.push(line);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (toolCalls >= limits.maxToolCalls) return;
|
|
212
|
+
toolCalls += 1;
|
|
213
|
+
const callId = `cmd-${step}-${toolCalls}`;
|
|
214
|
+
chain = chain.then(async () => {
|
|
215
|
+
if (signal.aborted) return;
|
|
216
|
+
emit({ type: "tool-start", callId, name: "command", step });
|
|
217
|
+
let outcome: CommandOutcome;
|
|
218
|
+
try {
|
|
219
|
+
outcome = await options.commands.run(line);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
outcome = {
|
|
222
|
+
ok: false,
|
|
223
|
+
message: error instanceof Error ? error.message : String(error),
|
|
224
|
+
baseRevision: currentRevision(),
|
|
225
|
+
resultRevision: currentRevision(),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
emit({ type: "command", line, ...outcome });
|
|
229
|
+
if (outcome.ok) {
|
|
230
|
+
if (outcome.resultRevision !== outcome.baseRevision) applied += 1;
|
|
231
|
+
results.push(`${line} → ${outcome.message}`);
|
|
232
|
+
} else {
|
|
233
|
+
failed += 1;
|
|
234
|
+
rejected += 1;
|
|
235
|
+
results.push(`${line} → FAILED: ${outcome.message}`);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
const pending = new Map<
|
|
240
|
+
number,
|
|
241
|
+
{ id: string; name: string; arguments: string }
|
|
242
|
+
>();
|
|
243
|
+
let typing = "";
|
|
244
|
+
for await (const event of options.client.stream(
|
|
245
|
+
{
|
|
246
|
+
model: options.model,
|
|
247
|
+
...(options.fallbackModel
|
|
248
|
+
? { fallbackModelId: options.fallbackModel }
|
|
249
|
+
: {}),
|
|
250
|
+
messages: [
|
|
251
|
+
{ role: "system", content: commandAgentSystemPrompt() },
|
|
252
|
+
{ role: "system", content: briefText },
|
|
253
|
+
...messages,
|
|
254
|
+
],
|
|
255
|
+
...(offered.length > 0 ? { tools: offered } : {}),
|
|
256
|
+
maxResponseBytes: remaining,
|
|
257
|
+
},
|
|
258
|
+
signal,
|
|
259
|
+
)) {
|
|
260
|
+
if (event.type === "usage") {
|
|
261
|
+
const { type: _type, ...usage } = event;
|
|
262
|
+
emit({ type: "usage", ...usage });
|
|
263
|
+
} else if (event.type === "activity") {
|
|
264
|
+
emit({ type: "activity", message: event.message });
|
|
265
|
+
} else if (event.type === "text") {
|
|
266
|
+
bytesUsed += event.delta.length;
|
|
267
|
+
if (text.length >= AGENT_LIMITS.maxTextChars) continue;
|
|
268
|
+
text += event.delta;
|
|
269
|
+
const { complete, partial } = lines.push(event.delta);
|
|
270
|
+
for (const line of complete) runLine(line);
|
|
271
|
+
if (partial !== typing) {
|
|
272
|
+
typing = partial;
|
|
273
|
+
emit({ type: "command-typing", text: partial });
|
|
274
|
+
}
|
|
275
|
+
} else if (event.type === "tool-delta") {
|
|
276
|
+
const call = pending.get(event.index) ?? {
|
|
277
|
+
id: "",
|
|
278
|
+
name: "",
|
|
279
|
+
arguments: "",
|
|
280
|
+
};
|
|
281
|
+
if (event.id) call.id = event.id;
|
|
282
|
+
if (event.name) call.name += event.name;
|
|
283
|
+
if (event.arguments) {
|
|
284
|
+
call.arguments += event.arguments;
|
|
285
|
+
bytesUsed += event.arguments.length;
|
|
286
|
+
if (call.arguments.length > AGENT_LIMITS.maxToolArgumentBytes)
|
|
287
|
+
throw new SseBudgetError(AGENT_LIMITS.maxToolArgumentBytes);
|
|
288
|
+
}
|
|
289
|
+
pending.set(event.index, call);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const last = lines.flush();
|
|
293
|
+
if (last) runLine(last);
|
|
294
|
+
if (typing) emit({ type: "command-typing", text: "" });
|
|
295
|
+
await chain;
|
|
296
|
+
if (signal.aborted) throw signal.reason;
|
|
297
|
+
if (prose.length > 0) {
|
|
298
|
+
finalText = prose.join(" ");
|
|
299
|
+
emit({ type: "text-delta", delta: finalText });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const calls = [...pending.entries()]
|
|
303
|
+
.sort(([left], [right]) => left - right)
|
|
304
|
+
.map(([index, call]) => ({
|
|
305
|
+
...call,
|
|
306
|
+
id: call.id || `call-${step}-${index}`,
|
|
307
|
+
}));
|
|
308
|
+
if (calls.length === 0 && failed === 0) return done("stop");
|
|
309
|
+
if (calls.length > 0) {
|
|
310
|
+
const assistantCalls: ChatToolCall[] = calls.map((call) => ({
|
|
311
|
+
id: call.id,
|
|
312
|
+
type: "function",
|
|
313
|
+
function: { name: call.name, arguments: call.arguments || "{}" },
|
|
314
|
+
}));
|
|
315
|
+
messages.push({
|
|
316
|
+
role: "assistant",
|
|
317
|
+
content: text || null,
|
|
318
|
+
tool_calls: assistantCalls,
|
|
319
|
+
});
|
|
320
|
+
for (const call of calls) {
|
|
321
|
+
if (signal.aborted) throw signal.reason;
|
|
322
|
+
toolCalls += 1;
|
|
323
|
+
emit({ type: "tool-start", callId: call.id, name: call.name, step });
|
|
324
|
+
const outcome = await executeCall(call, {
|
|
325
|
+
tools: allTools,
|
|
326
|
+
host: options.host,
|
|
327
|
+
newNoteId,
|
|
328
|
+
signal,
|
|
329
|
+
suspendTimeout: deadline.suspend,
|
|
330
|
+
onProgress: (line) =>
|
|
331
|
+
emit({
|
|
332
|
+
type: "tool-progress",
|
|
333
|
+
callId: call.id,
|
|
334
|
+
name: call.name,
|
|
335
|
+
line,
|
|
336
|
+
}),
|
|
337
|
+
});
|
|
338
|
+
if (outcome.ok) {
|
|
339
|
+
applied += outcome.mutated ? 1 : 0;
|
|
340
|
+
if (outcome.explanation) finalText = outcome.explanation;
|
|
341
|
+
emit(outcome.event);
|
|
342
|
+
} else {
|
|
343
|
+
rejected += 1;
|
|
344
|
+
emit({
|
|
345
|
+
type: "tool-rejected",
|
|
346
|
+
callId: call.id,
|
|
347
|
+
name: call.name,
|
|
348
|
+
diagnostic: outcome.diagnostic,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
messages.push({
|
|
352
|
+
role: "tool",
|
|
353
|
+
tool_call_id: call.id,
|
|
354
|
+
content: outcome.content,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
} else messages.push({ role: "assistant", content: text });
|
|
358
|
+
if (results.length > 0)
|
|
359
|
+
messages.push({
|
|
360
|
+
role: "user",
|
|
361
|
+
content: `Command results:\n${results.join("\n").slice(0, 6_000)}`,
|
|
362
|
+
});
|
|
363
|
+
if (toolCalls >= limits.maxToolCalls) return done("max-tool-calls");
|
|
364
|
+
}
|
|
365
|
+
return done("max-steps");
|
|
366
|
+
} catch (error) {
|
|
367
|
+
const { code, message } = classifyAgentError(error, signal, options.signal);
|
|
368
|
+
return finish({
|
|
369
|
+
type: "error",
|
|
370
|
+
code,
|
|
371
|
+
message,
|
|
372
|
+
applied,
|
|
373
|
+
revision: currentRevision(),
|
|
374
|
+
});
|
|
375
|
+
} finally {
|
|
376
|
+
deadline.clear();
|
|
377
|
+
}
|
|
378
|
+
}
|
package/src/agent/drum-tools.ts
CHANGED
|
@@ -38,7 +38,7 @@ export const DRUM_TOOLS: readonly AgentTool[] = Object.freeze([
|
|
|
38
38
|
{
|
|
39
39
|
name: "list_drum_patterns",
|
|
40
40
|
description:
|
|
41
|
-
"List the built-in
|
|
41
|
+
"List the built-in grooves (name · tempo range · style tags · drums). Filter by a word such as house, trap or 2-step.",
|
|
42
42
|
parameters: {
|
|
43
43
|
type: "object",
|
|
44
44
|
properties: { filter: { type: "string", maxLength: 40 } },
|
|
@@ -105,7 +105,7 @@ export const DRUM_TOOLS: readonly AgentTool[] = Object.freeze([
|
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
name: "set_drum_kit",
|
|
108
|
-
description: `Choose the synthesized
|
|
108
|
+
description: `Choose the synthesized kit of a drum track: ${SYNTH_KITS.map((kit) => kit.name).join(", ")}, or default. Offline and instant; sample kits (909, 808, linn, …) go through use_sound.`,
|
|
109
109
|
parameters: {
|
|
110
110
|
type: "object",
|
|
111
111
|
properties: {
|
|
@@ -340,7 +340,7 @@ export const EXPRESSION_TOOLS: readonly AgentTool[] = Object.freeze([
|
|
|
340
340
|
// keys-electric (0.6.1): the modelled piano's soft and middle pedals.
|
|
341
341
|
name: "set_piano_pedals",
|
|
342
342
|
description:
|
|
343
|
-
'
|
|
343
|
+
'Modeled piano soft (una corda: fewer strings, softer, darker) and sostenuto (holds only keys down when it presses) pedals. Each: [{beat, state}], "bars" (whole loop) or null. Sostenuto has no half.',
|
|
344
344
|
parameters: {
|
|
345
345
|
type: "object",
|
|
346
346
|
properties: {
|