@vgai/sdk 0.5.0 → 0.5.2

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 (54) hide show
  1. package/package.json +2 -2
  2. package/src/account.ts +5 -4
  3. package/src/cinematic/capabilities-operations.ts +3 -3
  4. package/src/cinematic/cue-operations.ts +11 -11
  5. package/src/cinematic/gsap-operations.ts +5 -5
  6. package/src/cinematic/index.ts +3 -3
  7. package/src/cinematic/preview-operations.ts +10 -10
  8. package/src/cinematic/preview-transport.ts +2 -2
  9. package/src/cinematic/render-operations.ts +15 -15
  10. package/src/cinematic/render-transport.ts +2 -2
  11. package/src/cinematic/theatre-operations.ts +17 -17
  12. package/src/editor/camera-operations.ts +7 -7
  13. package/src/editor/console-operations.ts +5 -5
  14. package/src/editor/hierarchy-operations.ts +5 -5
  15. package/src/editor/index.ts +3 -3
  16. package/src/editor/open-operations.ts +10 -10
  17. package/src/editor/screenshot-operations.ts +5 -5
  18. package/src/editor/selection-operations.ts +7 -7
  19. package/src/editor/session-operations.ts +3 -3
  20. package/src/editor/source-location-operations.ts +6 -6
  21. package/src/editor/transport.ts +24 -11
  22. package/src/errors.ts +8 -8
  23. package/src/generations.ts +12 -1
  24. package/src/http/http-projection.ts +26 -26
  25. package/src/index.ts +22 -30
  26. package/src/mcp/index.ts +2 -2
  27. package/src/mcp/mcp-projection.ts +25 -25
  28. package/src/mcp/mcp-server.ts +2 -2
  29. package/src/operations.ts +5 -5
  30. package/src/play/control-operations.ts +10 -10
  31. package/src/play/debug-command-operations.ts +12 -12
  32. package/src/play/index.ts +3 -3
  33. package/src/play/input-operations.ts +10 -10
  34. package/src/play/lifecycle-operations.ts +11 -11
  35. package/src/play/log-operations.ts +7 -7
  36. package/src/play/run-ticks-operations.ts +8 -8
  37. package/src/play/state-operations.ts +9 -9
  38. package/src/play/status-operations.ts +7 -7
  39. package/src/play/transport.ts +93 -22
  40. package/src/project/asset-operations.ts +8 -8
  41. package/src/project/component-operations.ts +10 -10
  42. package/src/project/discovery-operations.ts +9 -9
  43. package/src/project/entity-operations.ts +8 -8
  44. package/src/project/index.ts +4 -4
  45. package/src/project/input-map-operations.ts +9 -9
  46. package/src/project/inspection-operation.ts +3 -3
  47. package/src/project/manifest-operations.ts +9 -9
  48. package/src/project/scene-operations.ts +14 -14
  49. package/src/project/shared.ts +17 -17
  50. package/src/project-tool-catalog.ts +21 -5
  51. package/src/registry.ts +39 -39
  52. package/src/render/render-cinematic.ts +24 -1
  53. package/src/tools.ts +19 -12
  54. package/src/types.ts +4 -4
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@vgai/sdk",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "Apache-2.0",
5
- "version": "0.5.0",
5
+ "version": "0.5.2",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@modelcontextprotocol/sdk": "^1.29.0",
30
- "@vgai/engine": "0.5.0",
30
+ "@vgai/engine": "0.5.2",
31
31
  "playwright": "^1.58.2",
32
32
  "zod": "^4.3.6"
33
33
  }
package/src/account.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
- export const AccountModeSchema = z.enum(['mock', 'live']);
3
+ // Which account backend this session is signed into: the zero-charge in-process mock control plane, or the live VGAI account service. NOT a generation switch — generation execution routes (mock/managed/byok) are chosen per call; see preferredRoute for the UI default.
4
+ export const AccountBackendSchema = z.enum(['mock', 'live']);
4
5
  export const AccountPlanSchema = z.object({
5
6
  id: z.string().min(1),
6
7
  name: z.string().min(1),
@@ -47,10 +48,10 @@ export const AccountUsageEntrySchema = z.object({
47
48
  externalId: z.string().optional(),
48
49
  });
49
50
  export const AccountSnapshotSchema = z.discriminatedUnion('authenticated', [
50
- z.object({ authenticated: z.literal(false), mode: AccountModeSchema }),
51
+ z.object({ authenticated: z.literal(false), backend: AccountBackendSchema }),
51
52
  z.object({
52
53
  authenticated: z.literal(true),
53
- mode: AccountModeSchema,
54
+ backend: AccountBackendSchema,
54
55
  user: AccountUserSchema,
55
56
  plan: AccountPlanSchema,
56
57
  credits: AccountCreditsSchema,
@@ -83,7 +84,7 @@ export const ProviderCredentialTestResultSchema = z.object({
83
84
  testedAt: z.string().datetime(),
84
85
  });
85
86
 
86
- export type AccountMode = z.infer<typeof AccountModeSchema>;
87
+ export type AccountBackend = z.infer<typeof AccountBackendSchema>;
87
88
  export type AccountPlan = z.infer<typeof AccountPlanSchema>;
88
89
  export type AccountCredits = z.infer<typeof AccountCreditsSchema>;
89
90
  export type AccountSpendPolicy = z.infer<typeof AccountSpendPolicySchema>;
@@ -27,7 +27,7 @@
27
27
  */
28
28
 
29
29
  import { z } from 'zod';
30
- import { defineOperation, type OperationRegistry } from '../registry.js';
30
+ import { defineTool, type ToolRegistry } from '../registry.js';
31
31
  import { checkFfmpegCapability } from '../render/capabilities/ffmpeg.js';
32
32
 
33
33
  const BinaryCapability = z.discriminatedUnion('ok', [
@@ -76,7 +76,7 @@ const DECLARED_FORMATS = [
76
76
  { format: 'png' as const, alpha: true },
77
77
  ];
78
78
 
79
- export const cinematicCapabilities = defineOperation({
79
+ export const cinematicCapabilities = defineTool({
80
80
  name: 'cinematic.capabilities',
81
81
  summary:
82
82
  'Report ffmpeg/ffprobe capture capability and the render pipeline’s declared codec/format support.',
@@ -123,6 +123,6 @@ export const cinematicCapabilities = defineOperation({
123
123
  });
124
124
 
125
125
  /** Register cinematic.capabilities onto `registry`. */
126
- export function registerCapabilitiesOperations(registry: OperationRegistry): void {
126
+ export function registerCapabilitiesOperations(registry: ToolRegistry): void {
127
127
  registry.register(cinematicCapabilities);
128
128
  }
@@ -24,12 +24,12 @@
24
24
  import { existsSync, readFileSync } from 'node:fs';
25
25
  import { isAbsolute, join } from 'node:path';
26
26
  import { z } from 'zod';
27
- import { OperationError } from '../errors.js';
28
- import { defineOperation, type ErrorDefinition, type OperationRegistry } from '../registry.js';
29
- import type { OperationContext } from '../types.js';
27
+ import { ToolError } from '../errors.js';
28
+ import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
29
+ import type { ToolContext } from '../types.js';
30
30
  import { NO_ROOT_ERROR } from './theatre-operations.js';
31
31
 
32
- export const CUE_FILE_NOT_FOUND_ERROR: ErrorDefinition = {
32
+ export const CUE_FILE_NOT_FOUND_ERROR: ToolErrorDefinition = {
33
33
  code: 'FILE_NOT_FOUND',
34
34
  summary: 'The referenced cue module file does not exist on disk.',
35
35
  data: z.object({ path: z.string() }),
@@ -71,22 +71,22 @@ const CueSourceInput = z.object({
71
71
  .describe('Base directory for a relative `path`. Defaults to ctx.projectRoot.'),
72
72
  });
73
73
 
74
- function resolveSource(input: z.infer<typeof CueSourceInput>, ctx: OperationContext): string {
74
+ function resolveSource(input: z.infer<typeof CueSourceInput>, ctx: ToolContext): string {
75
75
  if (input.source !== undefined) return input.source;
76
76
  if (input.path === undefined) {
77
- throw new OperationError('BAD_INPUT', 'One of `source` or `path` must be given.', {});
77
+ throw new ToolError('BAD_INPUT', 'One of `source` or `path` must be given.', {});
78
78
  }
79
79
  const root = input.root ?? ctx.projectRoot;
80
80
  const absPath = isAbsolute(input.path) ? input.path : root ? join(root, input.path) : undefined;
81
81
  if (!absPath) {
82
- throw new OperationError(
82
+ throw new ToolError(
83
83
  'NO_ROOT',
84
84
  'A relative `path` was given but neither `root` nor ctx.projectRoot is available.',
85
85
  {},
86
86
  );
87
87
  }
88
88
  if (!existsSync(absPath)) {
89
- throw new OperationError('FILE_NOT_FOUND', `File not found: ${absPath}`, { path: absPath });
89
+ throw new ToolError('FILE_NOT_FOUND', `File not found: ${absPath}`, { path: absPath });
90
90
  }
91
91
  return readFileSync(absPath, 'utf-8');
92
92
  }
@@ -103,7 +103,7 @@ const CinematicCueListResult = z.object({
103
103
  .describe('Every {id, time} pair found, in source declaration order.'),
104
104
  });
105
105
 
106
- export const cinematicCueList = defineOperation({
106
+ export const cinematicCueList = defineTool({
107
107
  name: 'cinematic.cue.list',
108
108
  summary: 'Best-effort static list of {id, time} cue entries in a TypeScript cue module.',
109
109
  description:
@@ -137,7 +137,7 @@ const CinematicCueValidateResult = z.object({
137
137
  cues: z.array(HeuristicCueSchema),
138
138
  });
139
139
 
140
- export const cinematicCueValidate = defineOperation({
140
+ export const cinematicCueValidate = defineTool({
141
141
  name: 'cinematic.cue.validate',
142
142
  summary:
143
143
  'Validate unique cue IDs and ascending time ordering for the cues found by cinematic.cue.list’s ' +
@@ -192,7 +192,7 @@ export const cinematicCueValidate = defineOperation({
192
192
  });
193
193
 
194
194
  /** Register cinematic.cue.list / cinematic.cue.validate onto `registry`. */
195
- export function registerCueOperations(registry: OperationRegistry): void {
195
+ export function registerCueOperations(registry: ToolRegistry): void {
196
196
  registry.register(cinematicCueList);
197
197
  registry.register(cinematicCueValidate);
198
198
  }
@@ -25,9 +25,9 @@
25
25
  import { readFileSync, statSync } from 'node:fs';
26
26
  import { join } from 'node:path';
27
27
  import { z } from 'zod';
28
- import { OperationError } from '../errors.js';
28
+ import { ToolError } from '../errors.js';
29
29
  import { listProjectFiles } from '../project/shared.js';
30
- import { defineOperation, type OperationRegistry } from '../registry.js';
30
+ import { defineTool, type ToolRegistry } from '../registry.js';
31
31
  import { NO_ROOT_ERROR } from './theatre-operations.js';
32
32
 
33
33
  /** True when the file text looks like it registers a GSAP timeline for deterministic scrub/export. */
@@ -70,7 +70,7 @@ const CinematicGsapInspectResult = z.object({
70
70
  .describe('Every *.ts/*.tsx file whose text imports gsap-registration, sorted by path.'),
71
71
  });
72
72
 
73
- export const cinematicGsapInspect = defineOperation({
73
+ export const cinematicGsapInspect = defineTool({
74
74
  name: 'cinematic.gsap.inspect',
75
75
  summary:
76
76
  'Best-effort static discovery of source files that call registerGsap(...) to register a ' +
@@ -90,7 +90,7 @@ export const cinematicGsapInspect = defineOperation({
90
90
  async impl(input, ctx) {
91
91
  const root = input.root ?? ctx.projectRoot;
92
92
  if (!root) {
93
- throw new OperationError(
93
+ throw new ToolError(
94
94
  'NO_ROOT',
95
95
  'Neither an explicit `root` input nor ctx.projectRoot was given.',
96
96
  {},
@@ -121,6 +121,6 @@ export const cinematicGsapInspect = defineOperation({
121
121
  });
122
122
 
123
123
  /** Register cinematic.gsap.inspect onto `registry`. */
124
- export function registerGsapOperations(registry: OperationRegistry): void {
124
+ export function registerGsapOperations(registry: ToolRegistry): void {
125
125
  registry.register(cinematicGsapInspect);
126
126
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * B5 — `cinematic.*` operations
3
3
  * (docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md §8 B5). Registered
4
- * separately from B1's `registerBuiltinOperations` (`../operations.ts`),
4
+ * separately from B1's `registerBuiltinTools` (`../operations.ts`),
5
5
  * B2's `registerProjectOperations` (`../project/index.ts`), B3's
6
6
  * `registerEditorOperations` (`../editor/index.ts`), and B4's
7
7
  * `registerPlayOperations` (`../play/index.ts`) — the default `operations`
@@ -40,7 +40,7 @@ export {
40
40
  } from './render-transport.js';
41
41
  export * from './theatre-operations.js';
42
42
 
43
- import type { OperationRegistry } from '../registry.js';
43
+ import type { ToolRegistry } from '../registry.js';
44
44
  import { registerCapabilitiesOperations } from './capabilities-operations.js';
45
45
  import { registerCueOperations } from './cue-operations.js';
46
46
  import { registerGsapOperations } from './gsap-operations.js';
@@ -49,7 +49,7 @@ import { registerRenderOperations } from './render-operations.js';
49
49
  import { registerTheatreOperations } from './theatre-operations.js';
50
50
 
51
51
  /** Register every B5 `cinematic.*` operation onto `registry`. */
52
- export function registerCinematicOperations(registry: OperationRegistry): void {
52
+ export function registerCinematicOperations(registry: ToolRegistry): void {
53
53
  registerTheatreOperations(registry);
54
54
  registerCueOperations(registry);
55
55
  registerGsapOperations(registry);
@@ -14,8 +14,8 @@
14
14
  import { tmpdir } from 'node:os';
15
15
  import { join } from 'node:path';
16
16
  import { z } from 'zod';
17
- import { OperationError } from '../errors.js';
18
- import { defineOperation, type ErrorDefinition, type OperationRegistry } from '../registry.js';
17
+ import { ToolError } from '../errors.js';
18
+ import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
19
19
  import {
20
20
  createPreviewSession,
21
21
  getCinematicOpenPreviewFn,
@@ -25,7 +25,7 @@ import {
25
25
  stopPreviewSession,
26
26
  } from './preview-transport.js';
27
27
 
28
- const PREVIEW_SESSION_NOT_FOUND_ERROR: ErrorDefinition = {
28
+ const PREVIEW_SESSION_NOT_FOUND_ERROR: ToolErrorDefinition = {
29
29
  code: 'PREVIEW_SESSION_NOT_FOUND',
30
30
  summary:
31
31
  'No open preview session with the given previewId exists (this process’s in-memory registry).',
@@ -67,7 +67,7 @@ function stateOf(session: {
67
67
  function requireSession(previewId: string) {
68
68
  const session = getPreviewSession(previewId);
69
69
  if (!session) {
70
- throw new OperationError(
70
+ throw new ToolError(
71
71
  'PREVIEW_SESSION_NOT_FOUND',
72
72
  `No open preview session "${previewId}".`,
73
73
  { previewId },
@@ -113,7 +113,7 @@ const CinematicPreviewInput = z.object({
113
113
  playbackRate: z.number().positive().optional().describe('Autoplay speed multiplier. Default 1.'),
114
114
  });
115
115
 
116
- export const cinematicPreview = defineOperation({
116
+ export const cinematicPreview = defineTool({
117
117
  name: 'cinematic.preview',
118
118
  summary:
119
119
  'Open a headless preview session against a render-control-mode page and (by default) start playing it.',
@@ -180,7 +180,7 @@ export const cinematicPreview = defineOperation({
180
180
 
181
181
  const PreviewIdInput = z.object({ previewId: z.string() });
182
182
 
183
- export const cinematicPause = defineOperation({
183
+ export const cinematicPause = defineTool({
184
184
  name: 'cinematic.pause',
185
185
  summary: 'Stop a preview session’s autoplay loop without closing it.',
186
186
  description:
@@ -214,7 +214,7 @@ const CinematicSeekInput = z.object({
214
214
  frame: z.number().int().min(0).optional().describe('Seek to this exact frame index.'),
215
215
  });
216
216
 
217
- export const cinematicSeek = defineOperation({
217
+ export const cinematicSeek = defineTool({
218
218
  name: 'cinematic.seek',
219
219
  summary: 'Seek a preview session to an exact time or frame.',
220
220
  description:
@@ -238,7 +238,7 @@ export const cinematicSeek = defineOperation({
238
238
  async impl(input) {
239
239
  const session = requireSession(input.previewId);
240
240
  if ((input.time === undefined) === (input.frame === undefined)) {
241
- throw new OperationError('BAD_INPUT', 'Supply exactly one of time or frame.', {});
241
+ throw new ToolError('BAD_INPUT', 'Supply exactly one of time or frame.', {});
242
242
  }
243
243
  const frame = input.frame ?? Math.round(input.time! * session.handle.fps);
244
244
  await seekPreviewSession(session, frame);
@@ -250,7 +250,7 @@ export const cinematicSeek = defineOperation({
250
250
  // cinematic.stop
251
251
  // ---------------------------------------------------------------------------
252
252
 
253
- export const cinematicStop = defineOperation({
253
+ export const cinematicStop = defineTool({
254
254
  name: 'cinematic.stop',
255
255
  summary: 'Close a preview session (its page/browser) and forget it.',
256
256
  description:
@@ -271,7 +271,7 @@ export const cinematicStop = defineOperation({
271
271
  });
272
272
 
273
273
  /** Register cinematic.preview / .pause / .seek / .stop onto `registry`. */
274
- export function registerPreviewOperations(registry: OperationRegistry): void {
274
+ export function registerPreviewOperations(registry: ToolRegistry): void {
275
275
  registry.register(cinematicPreview);
276
276
  registry.register(cinematicPause);
277
277
  registry.register(cinematicSeek);
@@ -42,7 +42,7 @@
42
42
 
43
43
  import type { RenderCinematicRequest } from '../render/render-cinematic.js';
44
44
  import { openRenderCinematicPage } from '../render/render-cinematic.js';
45
- import type { OperationContext } from '../types.js';
45
+ import type { ToolContext } from '../types.js';
46
46
 
47
47
  /** The narrow surface preview sessions need — deliberately NOT Playwright's `Page` (see module jsdoc). */
48
48
  export interface CinematicPreviewHandle {
@@ -96,7 +96,7 @@ async function openRealPreviewHandle(
96
96
  }
97
97
 
98
98
  /** Resolve the preview-page opener to use: `ctx['cinematicOpenPreviewFn']` when injected (tests), else the real launcher. */
99
- export function getCinematicOpenPreviewFn(ctx: OperationContext): CinematicOpenPreviewFn {
99
+ export function getCinematicOpenPreviewFn(ctx: ToolContext): CinematicOpenPreviewFn {
100
100
  const injected = ctx['cinematicOpenPreviewFn'];
101
101
  return (injected as CinematicOpenPreviewFn | undefined) ?? openRealPreviewHandle;
102
102
  }
@@ -16,8 +16,8 @@
16
16
  */
17
17
 
18
18
  import { z } from 'zod';
19
- import { OperationError } from '../errors.js';
20
- import { defineOperation, type ErrorDefinition, type OperationRegistry } from '../registry.js';
19
+ import { ToolError } from '../errors.js';
20
+ import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
21
21
  import {
22
22
  RenderCinematicFfmpegError,
23
23
  type RenderCinematicRequest,
@@ -34,7 +34,7 @@ import {
34
34
  // Shared error definitions
35
35
  // ---------------------------------------------------------------------------
36
36
 
37
- const RENDER_REQUEST_INVALID_ERROR: ErrorDefinition = {
37
+ const RENDER_REQUEST_INVALID_ERROR: ToolErrorDefinition = {
38
38
  code: 'RENDER_REQUEST_INVALID',
39
39
  summary:
40
40
  'The render request failed validation (I1 AC: "fail before launching the browser") — no job ' +
@@ -42,19 +42,19 @@ const RENDER_REQUEST_INVALID_ERROR: ErrorDefinition = {
42
42
  data: z.object({ message: z.string() }),
43
43
  };
44
44
 
45
- const RENDER_CAPABILITY_UNAVAILABLE_ERROR: ErrorDefinition = {
45
+ const RENDER_CAPABILITY_UNAVAILABLE_ERROR: ToolErrorDefinition = {
46
46
  code: 'RENDER_CAPABILITY_UNAVAILABLE',
47
47
  summary: 'ffmpeg/ffprobe preflight failed (A1) — no job was created.',
48
48
  data: z.object({ message: z.string() }),
49
49
  };
50
50
 
51
- const RENDER_JOB_NOT_FOUND_ERROR: ErrorDefinition = {
51
+ const RENDER_JOB_NOT_FOUND_ERROR: ToolErrorDefinition = {
52
52
  code: 'RENDER_JOB_NOT_FOUND',
53
53
  summary: 'No render job with the given jobId exists (this process’s in-memory job registry).',
54
54
  data: z.object({ jobId: z.string() }),
55
55
  };
56
56
 
57
- const RENDER_JOB_NOT_RUNNING_ERROR: ErrorDefinition = {
57
+ const RENDER_JOB_NOT_RUNNING_ERROR: ToolErrorDefinition = {
58
58
  code: 'RENDER_JOB_NOT_RUNNING',
59
59
  summary: 'The job has already settled (completed/failed/cancelled) — nothing to cancel.',
60
60
  data: z.object({ jobId: z.string(), status: z.string() }),
@@ -255,7 +255,7 @@ function toRenderCinematicRequest(
255
255
  // cinematic.render
256
256
  // ---------------------------------------------------------------------------
257
257
 
258
- export const cinematicRender = defineOperation({
258
+ export const cinematicRender = defineTool({
259
259
  name: 'cinematic.render',
260
260
  summary: 'Render a Theatre-driven cinematic sequence to a video or frame-sequence artifact.',
261
261
  description:
@@ -285,10 +285,10 @@ export const cinematicRender = defineOperation({
285
285
  job = startRenderJob(toRenderCinematicRequest(input), renderFn);
286
286
  } catch (err) {
287
287
  if (err instanceof RenderCinematicRequestError) {
288
- throw new OperationError('RENDER_REQUEST_INVALID', err.message, { message: err.message });
288
+ throw new ToolError('RENDER_REQUEST_INVALID', err.message, { message: err.message });
289
289
  }
290
290
  if (err instanceof RenderCinematicFfmpegError) {
291
- throw new OperationError('RENDER_CAPABILITY_UNAVAILABLE', err.message, {
291
+ throw new ToolError('RENDER_CAPABILITY_UNAVAILABLE', err.message, {
292
292
  message: err.message,
293
293
  });
294
294
  }
@@ -309,7 +309,7 @@ export const cinematicRender = defineOperation({
309
309
 
310
310
  const CinematicRenderStatusInput = z.object({ jobId: z.string() });
311
311
 
312
- export const cinematicRenderStatus = defineOperation({
312
+ export const cinematicRenderStatus = defineTool({
313
313
  name: 'cinematic.render.status',
314
314
  summary: 'Poll a cinematic.render job’s progress, current frame, elapsed time, and output paths.',
315
315
  description:
@@ -327,7 +327,7 @@ export const cinematicRenderStatus = defineOperation({
327
327
  async impl(input) {
328
328
  const job = getRenderJob(input.jobId);
329
329
  if (!job) {
330
- throw new OperationError('RENDER_JOB_NOT_FOUND', `No render job "${input.jobId}".`, {
330
+ throw new ToolError('RENDER_JOB_NOT_FOUND', `No render job "${input.jobId}".`, {
331
331
  jobId: input.jobId,
332
332
  });
333
333
  }
@@ -339,7 +339,7 @@ export const cinematicRenderStatus = defineOperation({
339
339
  // cinematic.render.cancel
340
340
  // ---------------------------------------------------------------------------
341
341
 
342
- export const cinematicRenderCancel = defineOperation({
342
+ export const cinematicRenderCancel = defineTool({
343
343
  name: 'cinematic.render.cancel',
344
344
  summary: 'Cancel a running cinematic.render job.',
345
345
  description:
@@ -361,12 +361,12 @@ export const cinematicRenderCancel = defineOperation({
361
361
  async impl(input) {
362
362
  const job = getRenderJob(input.jobId);
363
363
  if (!job) {
364
- throw new OperationError('RENDER_JOB_NOT_FOUND', `No render job "${input.jobId}".`, {
364
+ throw new ToolError('RENDER_JOB_NOT_FOUND', `No render job "${input.jobId}".`, {
365
365
  jobId: input.jobId,
366
366
  });
367
367
  }
368
368
  if (job.status !== 'running') {
369
- throw new OperationError(
369
+ throw new ToolError(
370
370
  'RENDER_JOB_NOT_RUNNING',
371
371
  `Render job "${input.jobId}" has already settled (${job.status}) — nothing to cancel.`,
372
372
  { jobId: input.jobId, status: job.status },
@@ -402,7 +402,7 @@ export const cinematicRenderCancel = defineOperation({
402
402
  });
403
403
 
404
404
  /** Register cinematic.render / .status / .cancel onto `registry`. */
405
- export function registerRenderOperations(registry: OperationRegistry): void {
405
+ export function registerRenderOperations(registry: ToolRegistry): void {
406
406
  registry.register(cinematicRender);
407
407
  registry.register(cinematicRenderStatus);
408
408
  registry.register(cinematicRenderCancel);
@@ -52,7 +52,7 @@ import {
52
52
  renderCinematic,
53
53
  resolveRenderCinematicRequest,
54
54
  } from '../render/render-cinematic.js';
55
- import type { OperationContext } from '../types.js';
55
+ import type { ToolContext } from '../types.js';
56
56
 
57
57
  export type CinematicRenderFn = (
58
58
  request: RenderCinematicRequest,
@@ -60,7 +60,7 @@ export type CinematicRenderFn = (
60
60
  ) => Promise<RenderCinematicResult>;
61
61
 
62
62
  /** Resolve the render function to use: `ctx['cinematicRenderFn']` when injected (tests), else the real pipeline. */
63
- export function getCinematicRenderFn(ctx: OperationContext): CinematicRenderFn {
63
+ export function getCinematicRenderFn(ctx: ToolContext): CinematicRenderFn {
64
64
  const injected = ctx['cinematicRenderFn'];
65
65
  return (injected as CinematicRenderFn | undefined) ?? renderCinematic;
66
66
  }
@@ -30,28 +30,28 @@
30
30
  import { existsSync, readFileSync } from 'node:fs';
31
31
  import { isAbsolute, join } from 'node:path';
32
32
  import { z } from 'zod';
33
- import { OperationError } from '../errors.js';
33
+ import { ToolError } from '../errors.js';
34
34
  import { listProjectFiles } from '../project/shared.js';
35
- import { defineOperation, type ErrorDefinition, type OperationRegistry } from '../registry.js';
36
- import type { OperationContext } from '../types.js';
35
+ import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
36
+ import type { ToolContext } from '../types.js';
37
37
 
38
38
  // ---------------------------------------------------------------------------
39
39
  // Shared path resolution: `root` (explicit) > `ctx.projectRoot` (fallback).
40
40
  // ---------------------------------------------------------------------------
41
41
 
42
- export const NO_ROOT_ERROR: ErrorDefinition = {
42
+ export const NO_ROOT_ERROR: ToolErrorDefinition = {
43
43
  code: 'NO_ROOT',
44
44
  summary: 'Neither an explicit `root` input nor ctx.projectRoot was given.',
45
45
  data: z.object({}).describe('No additional data.'),
46
46
  };
47
47
 
48
- export const THEATRE_FILE_NOT_FOUND_ERROR: ErrorDefinition = {
48
+ export const THEATRE_FILE_NOT_FOUND_ERROR: ToolErrorDefinition = {
49
49
  code: 'FILE_NOT_FOUND',
50
50
  summary: 'The referenced Theatre project-state file does not exist on disk.',
51
51
  data: z.object({ path: z.string() }),
52
52
  };
53
53
 
54
- export const INVALID_THEATRE_PROJECT_ERROR: ErrorDefinition = {
54
+ export const INVALID_THEATRE_PROJECT_ERROR: ToolErrorDefinition = {
55
55
  code: 'INVALID_THEATRE_PROJECT',
56
56
  summary:
57
57
  "The file's outer shape (sheetsById/definitionVersion/revisionHistory, and — for " +
@@ -59,17 +59,17 @@ export const INVALID_THEATRE_PROJECT_ERROR: ErrorDefinition = {
59
59
  data: z.object({ issues: z.array(z.string()) }),
60
60
  };
61
61
 
62
- export const SHEET_NOT_FOUND_ERROR: ErrorDefinition = {
62
+ export const SHEET_NOT_FOUND_ERROR: ToolErrorDefinition = {
63
63
  code: 'SHEET_NOT_FOUND',
64
64
  summary: 'No sheet with the given name exists in this Theatre project’s sheetsById.',
65
65
  data: z.object({ sheetName: z.string(), availableSheets: z.array(z.string()) }),
66
66
  };
67
67
 
68
68
  /** Resolve `root` input against `ctx.projectRoot`, requiring at least one. */
69
- function resolveRoot(root: string | undefined, ctx: OperationContext): string {
69
+ function resolveRoot(root: string | undefined, ctx: ToolContext): string {
70
70
  const resolved = root ?? ctx.projectRoot;
71
71
  if (!resolved) {
72
- throw new OperationError(
72
+ throw new ToolError(
73
73
  'NO_ROOT',
74
74
  'Neither an explicit `root` input nor ctx.projectRoot was given.',
75
75
  {},
@@ -85,7 +85,7 @@ function resolveTheatreProjectPath(root: string, theatreProjectPath: string): st
85
85
 
86
86
  function readTheatreProjectFile(absPath: string): unknown {
87
87
  if (!existsSync(absPath)) {
88
- throw new OperationError('FILE_NOT_FOUND', `File not found: ${absPath}`, { path: absPath });
88
+ throw new ToolError('FILE_NOT_FOUND', `File not found: ${absPath}`, { path: absPath });
89
89
  }
90
90
  return JSON.parse(readFileSync(absPath, 'utf-8'));
91
91
  }
@@ -135,7 +135,7 @@ const CinematicProjectListResult = z.object({
135
135
  ),
136
136
  });
137
137
 
138
- export const cinematicProjectList = defineOperation({
138
+ export const cinematicProjectList = defineTool({
139
139
  name: 'cinematic.project.list',
140
140
  summary: 'List every committed Theatre project-state file under a directory.',
141
141
  description:
@@ -176,7 +176,7 @@ const CinematicSheetListResult = z.object({
176
176
  sheets: z.array(z.string()).describe('Every sheet name in sheetsById, sorted.'),
177
177
  });
178
178
 
179
- export const cinematicSheetList = defineOperation({
179
+ export const cinematicSheetList = defineTool({
180
180
  name: 'cinematic.sheet.list',
181
181
  summary: 'List every sheet name in a committed Theatre project-state file.',
182
182
  description: 'Static, file-native (§8 B5 "list Theatre ... sheets"). Outer-shape validated only.',
@@ -194,7 +194,7 @@ export const cinematicSheetList = defineOperation({
194
194
  const json = readTheatreProjectFile(absPath);
195
195
  const parsed = TheatreProjectOuterShape.safeParse(json);
196
196
  if (!parsed.success) {
197
- throw new OperationError(
197
+ throw new ToolError(
198
198
  'INVALID_THEATRE_PROJECT',
199
199
  `${absPath} does not match Theatre project-state's outer shape.`,
200
200
  { issues: parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`) },
@@ -236,7 +236,7 @@ const CinematicSequenceInspectResult = z.object({
236
236
  ),
237
237
  });
238
238
 
239
- export const cinematicSequenceInspect = defineOperation({
239
+ export const cinematicSequenceInspect = defineTool({
240
240
  name: 'cinematic.sequence.inspect',
241
241
  summary:
242
242
  'Inspect one sheet’s sequence length and tracked objects in a Theatre project-state file.',
@@ -263,7 +263,7 @@ export const cinematicSequenceInspect = defineOperation({
263
263
  const json = readTheatreProjectFile(absPath);
264
264
  const parsed = TheatreProjectDeepShape.safeParse(json);
265
265
  if (!parsed.success) {
266
- throw new OperationError(
266
+ throw new ToolError(
267
267
  'INVALID_THEATRE_PROJECT',
268
268
  `${absPath} does not match Theatre project-state's shape (incl. per-sheet sequence).`,
269
269
  { issues: parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`) },
@@ -271,7 +271,7 @@ export const cinematicSequenceInspect = defineOperation({
271
271
  }
272
272
  const sheet = parsed.data.sheetsById[input.sheetName];
273
273
  if (!sheet) {
274
- throw new OperationError(
274
+ throw new ToolError(
275
275
  'SHEET_NOT_FOUND',
276
276
  `No sheet named "${input.sheetName}" in ${absPath}.`,
277
277
  { sheetName: input.sheetName, availableSheets: Object.keys(parsed.data.sheetsById).sort() },
@@ -299,7 +299,7 @@ export const cinematicSequenceInspect = defineOperation({
299
299
  });
300
300
 
301
301
  /** Register cinematic.project.list / cinematic.sheet.list / cinematic.sequence.inspect onto `registry`. */
302
- export function registerTheatreOperations(registry: OperationRegistry): void {
302
+ export function registerTheatreOperations(registry: ToolRegistry): void {
303
303
  registry.register(cinematicProjectList);
304
304
  registry.register(cinematicSheetList);
305
305
  registry.register(cinematicSequenceInspect);
@@ -22,8 +22,8 @@
22
22
  */
23
23
 
24
24
  import { z } from 'zod';
25
- import { OperationError } from '../errors.js';
26
- import { defineOperation, type OperationRegistry } from '../registry.js';
25
+ import { ToolError } from '../errors.js';
26
+ import { defineTool, type ToolRegistry } from '../registry.js';
27
27
  import {
28
28
  EDITOR_COMMAND_TIMEOUT_MS,
29
29
  EDITOR_NOT_RUNNING_ERROR,
@@ -65,7 +65,7 @@ const EditorViewportCameraGetInput = z
65
65
 
66
66
  const EditorViewportCameraGetResult = CameraPoseSchema.describe('Live viewport camera pose.');
67
67
 
68
- export const editorViewportCameraGet = defineOperation({
68
+ export const editorViewportCameraGet = defineTool({
69
69
  name: 'editor.viewport.camera.get',
70
70
  summary: 'Read the viewport camera pose (position/target/fov) from a connected editor session.',
71
71
  description:
@@ -89,7 +89,7 @@ export const editorViewportCameraGet = defineOperation({
89
89
  'editor.viewport.camera.get',
90
90
  ).catch(() => undefined);
91
91
  if (!camera) {
92
- throw new OperationError(
92
+ throw new ToolError(
93
93
  'CAMERA_STATE_UNAVAILABLE',
94
94
  'No browser tab has bound/reported a viewport camera pose yet.',
95
95
  {},
@@ -124,7 +124,7 @@ const EditorViewportCameraSetInput = z
124
124
 
125
125
  const EditorViewportCameraSetResult = z.object({ ok: z.literal(true) });
126
126
 
127
- export const editorViewportCameraSet = defineOperation({
127
+ export const editorViewportCameraSet = defineTool({
128
128
  name: 'editor.viewport.camera.set',
129
129
  summary:
130
130
  'Move the viewport camera via focus-entity, focus-selection, a view preset, or an arbitrary pose.',
@@ -155,7 +155,7 @@ export const editorViewportCameraSet = defineOperation({
155
155
  error: err instanceof Error ? err.message : String(err),
156
156
  }));
157
157
  if (!result.ok) {
158
- throw new OperationError('COMMAND_FAILED', result.error ?? 'camera command failed', {
158
+ throw new ToolError('COMMAND_FAILED', result.error ?? 'camera command failed', {
159
159
  message: result.error ?? 'camera command failed',
160
160
  });
161
161
  }
@@ -163,7 +163,7 @@ export const editorViewportCameraSet = defineOperation({
163
163
  },
164
164
  });
165
165
 
166
- export function registerCameraOperations(registry: OperationRegistry): void {
166
+ export function registerCameraOperations(registry: ToolRegistry): void {
167
167
  registry.register(editorViewportCameraGet);
168
168
  registry.register(editorViewportCameraSet);
169
169
  }
@@ -15,8 +15,8 @@
15
15
  */
16
16
 
17
17
  import { z } from 'zod';
18
- import { OperationError } from '../errors.js';
19
- import { defineOperation, type OperationRegistry } from '../registry.js';
18
+ import { ToolError } from '../errors.js';
19
+ import { defineTool, type ToolRegistry } from '../registry.js';
20
20
  import {
21
21
  EDITOR_NOT_RUNNING_ERROR,
22
22
  EDITOR_READ_TIMEOUT_MS,
@@ -47,7 +47,7 @@ const EditorConsoleSubscribeResult = z.object({
47
47
  .describe('Number of play-mode log entries currently buffered/persisted.'),
48
48
  });
49
49
 
50
- export const editorConsoleSubscribe = defineOperation({
50
+ export const editorConsoleSubscribe = defineTool({
51
51
  name: 'editor.console.subscribe',
52
52
  summary: "Describe how to subscribe to a connected editor session's events and logs.",
53
53
  description:
@@ -72,7 +72,7 @@ export const editorConsoleSubscribe = defineOperation({
72
72
  'editor.console.subscribe',
73
73
  ).catch(() => undefined);
74
74
  if (!metadata) {
75
- throw new OperationError(
75
+ throw new ToolError(
76
76
  'CONSOLE_METADATA_UNAVAILABLE',
77
77
  'The connected editor session did not answer the log-entries read in time.',
78
78
  {},
@@ -82,6 +82,6 @@ export const editorConsoleSubscribe = defineOperation({
82
82
  },
83
83
  });
84
84
 
85
- export function registerConsoleOperations(registry: OperationRegistry): void {
85
+ export function registerConsoleOperations(registry: ToolRegistry): void {
86
86
  registry.register(editorConsoleSubscribe);
87
87
  }