agent-nuvira 3.5.6 → 3.5.7

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 (110) hide show
  1. package/.agents/skills/index.json +16 -0
  2. package/.agents/skills/motion/SKILL.md +27 -0
  3. package/README.md +56 -1
  4. package/dist/cli/chat.d.ts +76 -17
  5. package/dist/cli/chat.d.ts.map +1 -1
  6. package/dist/cli/chat.js +258 -118
  7. package/dist/cli/chat.js.map +1 -1
  8. package/dist/cli/doctor.d.ts +31 -2
  9. package/dist/cli/doctor.d.ts.map +1 -1
  10. package/dist/cli/doctor.js +143 -7
  11. package/dist/cli/doctor.js.map +1 -1
  12. package/dist/cli/loop-executor.d.ts.map +1 -1
  13. package/dist/cli/loop-executor.js +4 -1
  14. package/dist/cli/loop-executor.js.map +1 -1
  15. package/dist/cli/parity.d.ts +12 -0
  16. package/dist/cli/parity.d.ts.map +1 -1
  17. package/dist/cli/parity.js +46 -1
  18. package/dist/cli/parity.js.map +1 -1
  19. package/dist/cli/tool-install-prompt.d.ts +2 -7
  20. package/dist/cli/tool-install-prompt.d.ts.map +1 -1
  21. package/dist/cli/tool-install-prompt.js +11 -28
  22. package/dist/cli/tool-install-prompt.js.map +1 -1
  23. package/dist/config/scratch-workspace.d.ts +134 -0
  24. package/dist/config/scratch-workspace.d.ts.map +1 -0
  25. package/dist/config/scratch-workspace.js +213 -0
  26. package/dist/config/scratch-workspace.js.map +1 -0
  27. package/dist/gateway/adapters.d.ts +24 -0
  28. package/dist/gateway/adapters.d.ts.map +1 -1
  29. package/dist/gateway/adapters.js +12 -0
  30. package/dist/gateway/adapters.js.map +1 -1
  31. package/dist/gateway/oversized-reply.d.ts +39 -0
  32. package/dist/gateway/oversized-reply.d.ts.map +1 -0
  33. package/dist/gateway/oversized-reply.js +54 -0
  34. package/dist/gateway/oversized-reply.js.map +1 -0
  35. package/dist/gateway/registry.d.ts +0 -10
  36. package/dist/gateway/registry.d.ts.map +1 -1
  37. package/dist/gateway/registry.js +148 -4
  38. package/dist/gateway/registry.js.map +1 -1
  39. package/dist/learning/quota-ledger.d.ts +24 -0
  40. package/dist/learning/quota-ledger.d.ts.map +1 -1
  41. package/dist/learning/quota-ledger.js +40 -0
  42. package/dist/learning/quota-ledger.js.map +1 -1
  43. package/dist/learning/runtime-dependencies.d.ts +183 -0
  44. package/dist/learning/runtime-dependencies.d.ts.map +1 -0
  45. package/dist/learning/runtime-dependencies.js +349 -0
  46. package/dist/learning/runtime-dependencies.js.map +1 -0
  47. package/dist/learning/skill-store.d.ts.map +1 -1
  48. package/dist/learning/skill-store.js +6 -5
  49. package/dist/learning/skill-store.js.map +1 -1
  50. package/dist/skills/bundled-skills-phase3.d.ts +14 -0
  51. package/dist/skills/bundled-skills-phase3.d.ts.map +1 -1
  52. package/dist/skills/bundled-skills-phase3.js +50 -0
  53. package/dist/skills/bundled-skills-phase3.js.map +1 -1
  54. package/dist/tools/modality/motion-presets.d.ts +62 -0
  55. package/dist/tools/modality/motion-presets.d.ts.map +1 -0
  56. package/dist/tools/modality/motion-presets.js +399 -0
  57. package/dist/tools/modality/motion-presets.js.map +1 -0
  58. package/dist/tools/modality/motion-tool.d.ts +62 -0
  59. package/dist/tools/modality/motion-tool.d.ts.map +1 -0
  60. package/dist/tools/modality/motion-tool.js +220 -0
  61. package/dist/tools/modality/motion-tool.js.map +1 -0
  62. package/dist/tools/modality/motion.d.ts +298 -0
  63. package/dist/tools/modality/motion.d.ts.map +1 -0
  64. package/dist/tools/modality/motion.js +514 -0
  65. package/dist/tools/modality/motion.js.map +1 -0
  66. package/dist/tools/registry.d.ts.map +1 -1
  67. package/dist/tools/registry.js +75 -0
  68. package/dist/tools/registry.js.map +1 -1
  69. package/dist/tools/tool-loop.d.ts.map +1 -1
  70. package/dist/tools/tool-loop.js +66 -16
  71. package/dist/tools/tool-loop.js.map +1 -1
  72. package/dist/tools/toolsets.js +2 -2
  73. package/dist/tools/toolsets.js.map +1 -1
  74. package/dist/utils/workspace-need.d.ts +84 -0
  75. package/dist/utils/workspace-need.d.ts.map +1 -0
  76. package/dist/utils/workspace-need.js +227 -0
  77. package/dist/utils/workspace-need.js.map +1 -0
  78. package/dist/web-dashboard/chat-console.d.ts +22 -0
  79. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  80. package/dist/web-dashboard/chat-console.js +65 -11
  81. package/dist/web-dashboard/chat-console.js.map +1 -1
  82. package/dist/web-dashboard/hub-data.d.ts +9 -1
  83. package/dist/web-dashboard/hub-data.d.ts.map +1 -1
  84. package/dist/web-dashboard/hub-data.js +2 -0
  85. package/dist/web-dashboard/hub-data.js.map +1 -1
  86. package/dist/web-dashboard/project-context.d.ts +1 -1
  87. package/dist/web-dashboard/project-context.d.ts.map +1 -1
  88. package/dist/web-dashboard/project-context.js +7 -1
  89. package/dist/web-dashboard/project-context.js.map +1 -1
  90. package/dist/web-dashboard/server.d.ts +33 -0
  91. package/dist/web-dashboard/server.d.ts.map +1 -1
  92. package/dist/web-dashboard/server.js +204 -21
  93. package/dist/web-dashboard/server.js.map +1 -1
  94. package/dist/web-dashboard/src/types.d.ts +46 -0
  95. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  96. package/dist/web-dashboard/workspace-guard.d.ts +20 -21
  97. package/dist/web-dashboard/workspace-guard.d.ts.map +1 -1
  98. package/dist/web-dashboard/workspace-guard.js +20 -153
  99. package/dist/web-dashboard/workspace-guard.js.map +1 -1
  100. package/dist/web-dashboard/workspace-resolution.d.ts +23 -5
  101. package/dist/web-dashboard/workspace-resolution.d.ts.map +1 -1
  102. package/dist/web-dashboard/workspace-resolution.js +22 -4
  103. package/dist/web-dashboard/workspace-resolution.js.map +1 -1
  104. package/package.json +1 -1
  105. package/src/web-dashboard/public/assets/{index-DeSsNU94.css → index-CzRU84ET.css} +1 -1
  106. package/src/web-dashboard/public/assets/index-V0rIqVbg.js +236 -0
  107. package/src/web-dashboard/public/assets/index-V0rIqVbg.js.map +1 -0
  108. package/src/web-dashboard/public/index.html +2 -2
  109. package/src/web-dashboard/public/assets/index-Do_O67Kv.js +0 -236
  110. package/src/web-dashboard/public/assets/index-Do_O67Kv.js.map +0 -1
@@ -0,0 +1,220 @@
1
+ /**
2
+ * The `motion_render` tool's behaviour, kept out of the registry so it can be
3
+ * called (and tested) directly — the same split `browser.ts` uses.
4
+ *
5
+ * Three things this layer owns, none of which belong in the renderer:
6
+ *
7
+ * - SOURCE RESOLUTION: raw HTML, an HTML file, or a preset plus the caller's
8
+ * data. A data-driven preset with no usable data is refused with the shapes
9
+ * that WOULD work, rather than rendered as an empty chart.
10
+ * - HISTORY: every render keeps its source and settings, so `revise` re-renders
11
+ * the stored source instead of re-inventing it (that is the difference
12
+ * between a re-edit and a second opinion).
13
+ * - HONEST REPORTING: frames, duration, size, the seek mechanism that was found
14
+ * and every warning — a render that could not be seeked or could not take its
15
+ * narration says so instead of returning a path and nothing else.
16
+ */
17
+ import { existsSync, readFileSync } from 'node:fs';
18
+ import { isAbsolute, join, resolve } from 'node:path';
19
+ import { MOTION_LIMITS, loadMotionManifest, listMotionManifests, motionAvailability, newMotionId, renderMotion, resolveFrameSize, saveMotionManifest, buildFramePlan, } from './motion.js';
20
+ import { MOTION_PRESETS, isMotionPreset, parseMotionData, presetHtml, presetNeedsData, } from './motion-presets.js';
21
+ const MIME_BY_FORMAT = {
22
+ mp4: 'video/mp4',
23
+ webm: 'video/webm',
24
+ gif: 'image/gif',
25
+ };
26
+ const formatBytes = (bytes) => bytes >= 1_048_576 ? `${(bytes / 1_048_576).toFixed(1)}MB` : `${Math.max(1, Math.round(bytes / 1024))}KB`;
27
+ /** Resolve a caller-supplied path against the turn's working directory. */
28
+ function resolvePath(path, cwd) {
29
+ return isAbsolute(path) ? path : resolve(join(cwd, path));
30
+ }
31
+ const SEEK_CONTRACT = `The animation must be SEEKABLE: define window.__motion = { seek(tMs) } (or animate with CSS/Web Animations, or a requestAnimationFrame loop — both are stepped for you). Never drive motion from Date.now() or a counter: only a pure function of t renders reproducibly.`;
32
+ function presetsListing() {
33
+ const lines = MOTION_PRESETS.map((name) => {
34
+ const need = presetNeedsData(name) ? 'needs data' : 'no data needed';
35
+ return `- ${name} (${need})`;
36
+ });
37
+ return (`motion_render: ${MOTION_PRESETS.length} presets — ready-made scenes with a verify-ready seek implementation:\n` +
38
+ `${lines.join('\n')}\n` +
39
+ 'Pass preset + params {title, subtitle, kicker, accent, background, unit, decimals} and, for the data-driven ' +
40
+ 'ones, data (JSON array of numbers, [{label,value}], {series:[…]}) or CSV — inline or via data_path.');
41
+ }
42
+ function listRenders(cwd) {
43
+ const manifests = listMotionManifests(cwd);
44
+ if (manifests.length === 0)
45
+ return 'motion_render: no renders yet in this workspace.';
46
+ const rows = manifests
47
+ .slice(0, 20)
48
+ .map((m) => `- ${m.id} · ${m.preset ?? 'custom html'} · ${(m.durationMs / 1000).toFixed(1)}s ${m.width}x${m.height} ${m.format}` +
49
+ `${m.revisions > 0 ? ` · ${m.revisions} revision${m.revisions === 1 ? '' : 's'}` : ''} · ${m.artifact}`);
50
+ return `motion_render: ${manifests.length} render(s), newest first:\n${rows.join('\n')}\nRevise one with action:'revise', source_id:'<id>'.`;
51
+ }
52
+ function reportSuccess(result, preset) {
53
+ const seek = result.seek === 'hook'
54
+ ? 'seek hook'
55
+ : result.seek === 'waapi'
56
+ ? 'Web Animations'
57
+ : result.seek === 'raf'
58
+ ? 'requestAnimationFrame step'
59
+ : 'STATIC (not seekable)';
60
+ return (`motion_render: ${result.format} written to ${result.file}` +
61
+ ` — ${result.frames} frames @ ${result.fps}fps, ${(result.durationMs / 1000).toFixed(2)}s, ${result.width}x${result.height}, ${formatBytes(result.bytes)}` +
62
+ ` · ${preset ?? 'custom source'} · ${seek}` +
63
+ (result.srt ? `\nCaptions: ${result.srt}` : '') +
64
+ (result.warnings.length > 0 ? `\nWarning: ${result.warnings.join(' ')}` : ''));
65
+ }
66
+ /**
67
+ * Run one `motion_render` call. Returns text plus, on a successful render, the
68
+ * media the registry should register and deliver.
69
+ */
70
+ export async function runMotionTool(args, ctx) {
71
+ const cwd = ctx?.cwd ?? process.cwd();
72
+ const action = args.action ?? 'render';
73
+ if (action === 'presets')
74
+ return { text: presetsListing() };
75
+ if (action === 'list')
76
+ return { text: listRenders(cwd) };
77
+ // ─── Settings: a revision inherits from the render it revises ─────────────
78
+ let base = null;
79
+ if (action === 'revise') {
80
+ if (!args.source_id) {
81
+ return { text: `Error: motion_render: action:'revise' needs source_id. ${listRenders(cwd)}` };
82
+ }
83
+ base = loadMotionManifest(args.source_id, cwd);
84
+ if (!base) {
85
+ return { text: `Error: motion_render: no render with id '${args.source_id}' in this workspace. ${listRenders(cwd)}` };
86
+ }
87
+ }
88
+ const format = args.format === 'gif' || args.format === 'webm' ? args.format : base?.format === 'webm' || base?.format === 'gif' ? base.format : 'mp4';
89
+ const plan = buildFramePlan({
90
+ fps: args.fps ?? base?.fps ?? MOTION_LIMITS.defaultFps,
91
+ duration_ms: args.duration_ms ?? base?.durationMs ?? MOTION_LIMITS.defaultDurationMs,
92
+ });
93
+ const size = resolveFrameSize({
94
+ width: args.width ?? base?.width,
95
+ height: args.height ?? base?.height,
96
+ aspect_ratio: args.aspect_ratio,
97
+ });
98
+ // ─── Source ──────────────────────────────────────────────────────────────
99
+ let source = null;
100
+ let preset = null;
101
+ let params = null;
102
+ if (args.html && args.html.trim()) {
103
+ source = args.html;
104
+ }
105
+ else if (args.html_path) {
106
+ const path = resolvePath(args.html_path, cwd);
107
+ if (!existsSync(path))
108
+ return { text: `Error: motion_render: no such html file '${args.html_path}'.` };
109
+ source = readFileSync(path, 'utf-8');
110
+ }
111
+ else if (args.preset) {
112
+ if (!isMotionPreset(args.preset)) {
113
+ return { text: `Error: motion_render: unknown preset '${args.preset}'. Use one of: ${MOTION_PRESETS.join(', ')}.` };
114
+ }
115
+ preset = args.preset;
116
+ let data = args.data;
117
+ if (data === undefined && args.data_path) {
118
+ const path = resolvePath(args.data_path, cwd);
119
+ if (!existsSync(path))
120
+ return { text: `Error: motion_render: no such data file '${args.data_path}'.` };
121
+ data = readFileSync(path, 'utf-8');
122
+ }
123
+ params = {
124
+ title: args.title ?? args.params?.title,
125
+ ...(args.params ?? {}),
126
+ ...(data !== undefined ? { data } : {}),
127
+ };
128
+ if (presetNeedsData(preset)) {
129
+ const points = parseMotionData(params.data);
130
+ if (points.length === 0) {
131
+ return {
132
+ text: `Error: motion_render: preset '${preset}' animates YOUR data, and nothing parseable was passed. ` +
133
+ `Give data as JSON (array of numbers, [{label,value}], {series:[…]}) or CSV with one label/value per row — ` +
134
+ `inline as 'data' or as a file with 'data_path'.`,
135
+ };
136
+ }
137
+ }
138
+ source = presetHtml(preset, params, { durationMs: plan.durationMs, width: size.width, height: size.height });
139
+ }
140
+ else if (base?.preset && isMotionPreset(base.preset)) {
141
+ // A revision of a preset render may come back with only new data/params.
142
+ preset = base.preset;
143
+ let data = args.data;
144
+ if (data === undefined && args.data_path) {
145
+ const path = resolvePath(args.data_path, cwd);
146
+ if (!existsSync(path))
147
+ return { text: `Error: motion_render: no such data file '${args.data_path}'.` };
148
+ data = readFileSync(path, 'utf-8');
149
+ }
150
+ params = { ...(base.params ?? {}), ...(args.params ?? {}), ...(data !== undefined ? { data } : {}), ...(args.title ? { title: args.title } : {}) };
151
+ if (presetNeedsData(preset) && parseMotionData(params.data).length === 0) {
152
+ return { text: `Error: motion_render: preset '${preset}' needs data (JSON or CSV) — pass data or data_path.` };
153
+ }
154
+ source = presetHtml(preset, params, { durationMs: plan.durationMs, width: size.width, height: size.height });
155
+ }
156
+ else if (base) {
157
+ source = base.source;
158
+ params = base.params;
159
+ }
160
+ if (!source) {
161
+ return {
162
+ text: 'Error: motion_render: nothing to render. Pass html (a self-contained animation), html_path (a file), ' +
163
+ 'or preset + data (see action:\'presets\'). ' +
164
+ SEEK_CONTRACT,
165
+ };
166
+ }
167
+ const availability = motionAvailability();
168
+ if (!availability.ok) {
169
+ return {
170
+ text: `Error: motion_render: cannot render — missing ${availability.missing.join(', ')}. Install: ${availability.hint}`,
171
+ };
172
+ }
173
+ const result = await renderMotion({
174
+ html: source,
175
+ fps: plan.fps,
176
+ duration_ms: plan.durationMs,
177
+ width: size.width,
178
+ height: size.height,
179
+ format,
180
+ crf: args.crf,
181
+ narration: args.narration,
182
+ voice: args.voice,
183
+ captions: args.captions,
184
+ burn_captions: args.burn_captions,
185
+ out_name: args.out_name ?? preset ?? 'motion',
186
+ cwd,
187
+ });
188
+ if (!result.ok) {
189
+ return { text: `Error: ${result.error}${result.hint ? ` Install: ${result.hint}` : ''}` };
190
+ }
191
+ const manifest = {
192
+ id: base?.id ?? newMotionId(),
193
+ createdAt: Date.now(),
194
+ source,
195
+ preset,
196
+ params,
197
+ fps: result.fps,
198
+ durationMs: result.durationMs,
199
+ width: result.width,
200
+ height: result.height,
201
+ format: result.format,
202
+ artifact: result.file,
203
+ srt: result.srt,
204
+ narration: args.narration ?? base?.narration ?? null,
205
+ captions: args.captions ?? base?.captions ?? null,
206
+ revisions: base ? base.revisions + 1 : 0,
207
+ };
208
+ saveMotionManifest(manifest, cwd);
209
+ return {
210
+ text: reportSuccess(result, preset ?? (base?.preset && isMotionPreset(base.preset) ? base.preset : null)) +
211
+ `\nRender id: ${manifest.id} (revise it with action:'revise', source_id:'${manifest.id}').`,
212
+ media: {
213
+ path: result.file,
214
+ mime: MIME_BY_FORMAT[result.format],
215
+ title: `motion: ${preset ?? args.out_name ?? 'animation'} (${plan.frames} frames)`,
216
+ },
217
+ ...(result.srt ? { sidecar: { path: result.srt, mime: 'application/x-subrip', title: 'captions (.srt)' } } : {}),
218
+ };
219
+ }
220
+ //# sourceMappingURL=motion-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"motion-tool.js","sourceRoot":"","sources":["../../../src/tools/modality/motion-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Motion — animation authored as CODE, rendered to a real video file.
3
+ *
4
+ * WHY IT EXISTS. "Make me a short animated explainer" is not a video-model
5
+ * request: a diffusion video model cannot label a chart with the user's own
6
+ * numbers, cannot spell a product name correctly, and gives a different take
7
+ * every run. The thing that can is an animation written as code — HTML/CSS/SVG
8
+ * plus a timeline — and the only missing half was a way to turn that code into a
9
+ * file. This module is that half: it drives a headless Chromium over the
10
+ * animation's TIMELINE (not over wall-clock time) and hands the frames to
11
+ * ffmpeg.
12
+ *
13
+ * THE CONTRACT, and it is the whole trick. An animation that only knows how to
14
+ * play in real time cannot be rendered reproducibly: a slow frame, a dropped
15
+ * rAF or a GC pause changes the output, and two runs of the same storyboard
16
+ * disagree. So the page is asked to be SEEKABLE:
17
+ *
18
+ * - `window.__motion = { seek(tMs) }` — set that state for time `t`. The
19
+ * preferred hook, and the only one that is exact.
20
+ * - a `requestAnimationFrame` loop is stepped by the injected runner at the
21
+ * same `tMs` (see `MOTION_INIT_SCRIPT`), so rAF-driven animation is
22
+ * deterministic even without the hook.
23
+ * - Web Animations (CSS animations/transitions, `element.animate`) are
24
+ * paused and moved to `currentTime = tMs`.
25
+ *
26
+ * Frames are captured at `t = i * 1000/fps`, so the same input produces the same
27
+ * frames, the same duration and the same file — which is what makes a re-edit
28
+ * ("scene 3, again, with the new number") meaningful.
29
+ *
30
+ * Everything expensive is gated on tools the user may not have: chromium
31
+ * (playwright, opt-in exactly as `browser.ts` treats it) and `ffmpeg`. A missing
32
+ * one is reported by name WITH the command that installs it, and never as a
33
+ * half-written video file.
34
+ */
35
+ /** What ffmpeg container/codec the render targets. */
36
+ export type MotionFormat = 'mp4' | 'gif' | 'webm';
37
+ export declare const MOTION_LIMITS: {
38
+ readonly minFps: 1;
39
+ readonly maxFps: 60;
40
+ readonly defaultFps: 30;
41
+ readonly minDurationMs: 200;
42
+ readonly maxDurationMs: number;
43
+ readonly defaultDurationMs: 8000;
44
+ readonly maxFrames: 6000;
45
+ readonly maxWidth: 3840;
46
+ readonly maxHeight: 2160;
47
+ readonly defaultWidth: 1280;
48
+ readonly defaultHeight: 720;
49
+ };
50
+ /**
51
+ * The frame size: an aspect ratio names the SHAPE, a width/height names the
52
+ * size, and both may be given (`16:9` at `width: 1920`). Whichever side the
53
+ * caller pinned sets the scale and the ratio sets the other; nothing pinned
54
+ * means a landscape default.
55
+ */
56
+ export declare function resolveFrameSize(raw: {
57
+ width?: unknown;
58
+ height?: unknown;
59
+ aspect_ratio?: unknown;
60
+ }): {
61
+ width: number;
62
+ height: number;
63
+ };
64
+ export interface MotionFramePlan {
65
+ fps: number;
66
+ durationMs: number;
67
+ frames: number;
68
+ }
69
+ /**
70
+ * How many frames a render is made of, and the fps it is played at.
71
+ *
72
+ * Frame `i` shows the animation at `t = i * 1000/fps`, so a 30fps 8s clip is 240
73
+ * frames covering 0 → 7.967s: the last frame is the START of the last frame's
74
+ * interval, which is what a video player expects (the duration comes from the
75
+ * frame count, not from the timeline's last timestamp).
76
+ */
77
+ export declare function buildFramePlan(opts: {
78
+ fps?: unknown;
79
+ duration_ms?: unknown;
80
+ }): MotionFramePlan;
81
+ /** The timeline position a frame is captured at (ms). */
82
+ export declare function frameTimestampMs(index: number, fps: number): number;
83
+ /**
84
+ * Runs BEFORE the page's own scripts.
85
+ *
86
+ * Two jobs. (1) `window.__motionRunner` steps any `requestAnimationFrame` loop
87
+ * at a caller-chosen time instead of at the wall clock, so a rAF-driven
88
+ * animation is as reproducible as one using the seek hook. (2) It is the ONLY
89
+ * thing here that touches the page: a page that defines
90
+ * `window.__motion.seek(tMs)` keeps full control of its own determinism.
91
+ *
92
+ * rAF callbacks are queued rather than run immediately — that is what makes them
93
+ * steppable — and each is handed the same `tMs` a browser would have handed it.
94
+ */
95
+ export declare const MOTION_INIT_SCRIPT = "\n(() => {\n const queued = [];\n window.requestAnimationFrame = (cb) => { queued.push(cb); return queued.length; };\n window.cancelAnimationFrame = () => {};\n window.__motionRunner = {\n step(t) {\n const due = queued.splice(0, queued.length);\n for (const cb of due) { try { cb(t); } catch (e) { /* one bad frame must not stop the render */ } }\n },\n pending() { return queued.length; },\n };\n})();\n";
96
+ /**
97
+ * Put the runner in the DOCUMENT, first in script order.
98
+ *
99
+ * Not `page.addInitScript`: that runs per new DOCUMENT, and `page.setContent`
100
+ * (which this renderer uses, so an animation need not be a file on disk) writes
101
+ * into the existing one — MEASURED against a real chromium, the runner was simply
102
+ * absent and a rAF animation reported `seek: none`. Injecting the script into the
103
+ * markup is also browser-agnostic, which is why it is done here rather than
104
+ * through a page-API seam.
105
+ */
106
+ export declare function injectRunner(html: string): string;
107
+ /**
108
+ * Move the page to `tMs` — the hook the skill documents, then the rAF runner,
109
+ * then Web Animations. Returns which mechanism answered (`hook` / `raf` /
110
+ * `waapi` / `none`), so the render can tell
111
+ * the caller when a page is NOT seekable (the one condition under which the
112
+ * output would be a wall-clock recording rather than a render).
113
+ *
114
+ * The timestamp is INLINED and the whole thing is an immediately-invoked
115
+ * expression, because `page.evaluate` treats a string as an expression rather
116
+ * than as a function to call with an argument: a `(t) => …` string is evaluated,
117
+ * its function VALUE comes back, and the page never moves. Reproduced against a
118
+ * real chromium — the render reported `seek: none` on a page that had a working
119
+ * hook — which is why this is a builder rather than a constant and a parameter.
120
+ */
121
+ export declare const MOTION_SEEK_SCRIPT = " let how = 'none';\n const hook = window.__motion;\n if (hook && typeof hook.seek === 'function') { hook.seek(t); how = 'hook'; }\n if (window.__motionRunner) {\n window.__motionRunner.step(t);\n // A stepped rAF loop IS a seek mechanism, so a page that animates only that\n // way is not reported as unseekable.\n if (how === 'none') how = 'raf';\n }\n if (document.getAnimations) {\n const anims = document.getAnimations();\n for (const a of anims) { try { a.pause(); a.currentTime = t; } catch (e) {} }\n if (anims.length > 0 && how === 'none') how = 'waapi';\n }\n return how;\n";
122
+ /** The expression that seeks the page to `tMs`, ready for `page.evaluate`. */
123
+ export declare function seekScript(tMs: number): string;
124
+ /**
125
+ * Wait for fonts before the first frame is taken — the same IIFE rule as
126
+ * `seekScript`: an expression, not a function value.
127
+ */
128
+ export declare const MOTION_READY_SCRIPT = "(() =>\n new Promise((resolve) => {\n const done = () => resolve(true);\n if (document.fonts && document.fonts.ready) document.fonts.ready.then(done, done);\n else done();\n }))()";
129
+ export interface MotionEncodeOptions {
130
+ fps: number;
131
+ /** printf-style frame pattern, absolute. */
132
+ pattern: string;
133
+ out: string;
134
+ format: MotionFormat;
135
+ /** Quality knob (lower = better for x264/VP9). */
136
+ crf?: number;
137
+ audioPath?: string;
138
+ srtPath?: string;
139
+ burnCaptions?: boolean;
140
+ }
141
+ /**
142
+ * The ffmpeg argv for a frame sequence → video.
143
+ *
144
+ * `-start_number 1` because the capture numbers frames from 1 (`frame-00001.png`),
145
+ * `yuv420p` and even dimensions because that is what players actually play, and
146
+ * `+faststart` so an mp4 can be streamed/previewed without a full download.
147
+ */
148
+ export declare function buildFfmpegArgs(o: MotionEncodeOptions): string[];
149
+ /** The argv that attaches narration to an already-rendered video (stream copy). */
150
+ export declare function buildMuxArgs(o: {
151
+ videoPath: string;
152
+ audioPath: string;
153
+ out: string;
154
+ }): string[];
155
+ export interface MotionCaption {
156
+ from_ms: number;
157
+ to_ms: number;
158
+ text: string;
159
+ }
160
+ /** `HH:MM:SS,mmm` — the only timestamp shape SRT accepts. */
161
+ export declare function formatSrtTime(ms: number): string;
162
+ /**
163
+ * A caption track. `from`/`to` are what the caller asked for, and overlapping or
164
+ * inverted cues are repaired rather than emitted (a file a player rejects is
165
+ * worse than no captions at all).
166
+ */
167
+ export declare function buildSrt(captions: MotionCaption[]): string;
168
+ export interface MotionAvailability {
169
+ ok: boolean;
170
+ missing: string[];
171
+ /** The install commands, ready to hand to a user. */
172
+ hint: string;
173
+ }
174
+ /** Test seam: answer availability without depending on what this machine has. */
175
+ export declare function setMotionAvailabilityForTests(availability: MotionAvailability | null): void;
176
+ export declare function motionAvailability(): MotionAvailability;
177
+ /** The slice of a playwright page this renderer uses. */
178
+ export interface MotionPageLike {
179
+ setContent(html: string, opts?: {
180
+ waitUntil?: string;
181
+ }): Promise<void>;
182
+ evaluate(script: string | ((arg: unknown) => unknown), arg?: unknown): Promise<unknown>;
183
+ screenshot(opts: {
184
+ path: string;
185
+ clip?: {
186
+ x: number;
187
+ y: number;
188
+ width: number;
189
+ height: number;
190
+ };
191
+ }): Promise<unknown>;
192
+ close(): Promise<void>;
193
+ }
194
+ export interface MotionBrowserLike {
195
+ newPage(opts?: {
196
+ viewport?: {
197
+ width: number;
198
+ height: number;
199
+ };
200
+ deviceScaleFactor?: number;
201
+ }): Promise<MotionPageLike>;
202
+ close(): Promise<void>;
203
+ }
204
+ type MotionLauncher = (viewport: {
205
+ width: number;
206
+ height: number;
207
+ }) => Promise<MotionBrowserLike>;
208
+ /** Test seam: replace `chromium.launch()` so the pipeline runs without a browser. */
209
+ export declare function setMotionLauncherForTests(launcher: MotionLauncher | null): void;
210
+ type FfmpegRunner = (args: string[]) => Promise<{
211
+ code: number;
212
+ stderr: string;
213
+ }>;
214
+ /** Test seam: replace the real ffmpeg invocation (recording argv, faking files). */
215
+ export declare function setFfmpegRunnerForTests(runner: FfmpegRunner | null): void;
216
+ export interface MotionRenderRequest {
217
+ html: string;
218
+ fps?: unknown;
219
+ duration_ms?: unknown;
220
+ width?: unknown;
221
+ height?: unknown;
222
+ aspect_ratio?: unknown;
223
+ format?: MotionFormat;
224
+ crf?: number;
225
+ /** Narration text; synthesized with `speak` (edge-tts/piper) and muxed in. */
226
+ narration?: string;
227
+ voice?: string;
228
+ captions?: MotionCaption[];
229
+ burn_captions?: boolean;
230
+ /** Artifact filename prefix (slugs to a safe name). */
231
+ out_name?: string;
232
+ cwd?: string;
233
+ }
234
+ export type MotionRenderResult = {
235
+ ok: true;
236
+ file: string;
237
+ srt: string | null;
238
+ frames: number;
239
+ fps: number;
240
+ durationMs: number;
241
+ width: number;
242
+ height: number;
243
+ format: MotionFormat;
244
+ bytes: number;
245
+ seek: string;
246
+ warnings: string[];
247
+ } | {
248
+ ok: false;
249
+ error: string;
250
+ hint?: string;
251
+ };
252
+ /**
253
+ * Render a seekable HTML animation to a video file.
254
+ *
255
+ * Order matters: the frames are captured first (so a page that cannot be seeked
256
+ * is reported before anything is encoded), the encode lands in the artifact dir
257
+ * only once it succeeded (a failed render leaves no file behind), and narration
258
+ * is muxed onto the finished video by stream copy.
259
+ */
260
+ export declare function renderMotion(req: MotionRenderRequest): Promise<MotionRenderResult>;
261
+ /** The slice of `speak` this module needs (see `modality/voice.ts`). */
262
+ type SpeakFn = (text: string, opts?: {
263
+ voice?: string;
264
+ }) => Promise<{
265
+ ok: boolean;
266
+ file?: string;
267
+ error?: string;
268
+ }>;
269
+ /** Test seam: replace speech synthesis (so the mux path is testable offline). */
270
+ export declare function setSpeakForTests(speak: SpeakFn | null): void;
271
+ export interface MotionManifest {
272
+ id: string;
273
+ createdAt: number;
274
+ source: string;
275
+ preset: string | null;
276
+ params: Record<string, unknown> | null;
277
+ fps: number;
278
+ durationMs: number;
279
+ width: number;
280
+ height: number;
281
+ format: MotionFormat;
282
+ artifact: string;
283
+ srt: string | null;
284
+ narration: string | null;
285
+ captions: MotionCaption[] | null;
286
+ revisions: number;
287
+ }
288
+ /** Where a render's source and settings are kept so it can be revised or replayed. */
289
+ export declare function motionHomeDir(cwd?: string): string;
290
+ export declare function motionManifestPath(id: string, cwd?: string): string;
291
+ export declare function saveMotionManifest(manifest: MotionManifest, cwd?: string): string;
292
+ export declare function loadMotionManifest(id: string, cwd?: string): MotionManifest | null;
293
+ /** Newest first, so `list` reads like the history it is. */
294
+ export declare function listMotionManifests(cwd?: string): MotionManifest[];
295
+ /** A short, filesystem-safe render id (`motion-20261015-ab12cd`). */
296
+ export declare function newMotionId(now?: number): string;
297
+ export {};
298
+ //# sourceMappingURL=motion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../../src/tools/modality/motion.ts"],"names":[],"mappings":"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"}