@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
@@ -11,8 +11,8 @@
11
11
  import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'node:fs';
12
12
  import { dirname, extname, join, relative } from 'node:path';
13
13
  import { z } from 'zod';
14
- import { OperationError } from '../errors.js';
15
- import { defineOperation, type OperationRegistry } from '../registry.js';
14
+ import { ToolError } from '../errors.js';
15
+ import { defineTool, type ToolRegistry } from '../registry.js';
16
16
  import {
17
17
  DryRunField,
18
18
  mutationResultSchema,
@@ -49,7 +49,7 @@ const AssetEntrySchema = z.object({
49
49
  ext: z.string().describe('Lowercased file extension including the leading dot, or "" if none.'),
50
50
  });
51
51
 
52
- export const projectAssetList = defineOperation({
52
+ export const projectAssetList = defineTool({
53
53
  name: 'project.asset.list',
54
54
  summary: 'List every file under the project’s public/ asset root (or a subdirectory of it).',
55
55
  description:
@@ -99,7 +99,7 @@ const ProjectAssetInspectInput = z.object({
99
99
  assetPath: z.string().describe('public/-relative path to inspect.'),
100
100
  });
101
101
 
102
- export const projectAssetInspect = defineOperation({
102
+ export const projectAssetInspect = defineTool({
103
103
  name: 'project.asset.inspect',
104
104
  summary:
105
105
  'Inspect one asset under public/ — existence, size, mtime, and a content hash for small files.',
@@ -174,7 +174,7 @@ const ProjectAssetImportInput = z.object({
174
174
  dryRun: DryRunField,
175
175
  });
176
176
 
177
- export const projectAssetImport = defineOperation({
177
+ export const projectAssetImport = defineTool({
178
178
  name: 'project.asset.import',
179
179
  summary: 'Copy an external file into the project’s public/ asset root.',
180
180
  description:
@@ -203,7 +203,7 @@ export const projectAssetImport = defineOperation({
203
203
  async impl(input, ctx) {
204
204
  const projectRoot = requireProjectRoot(ctx);
205
205
  if (!existsSync(input.sourcePath) || statSync(input.sourcePath).isDirectory()) {
206
- throw new OperationError('SOURCE_NOT_FOUND', `Source file not found: ${input.sourcePath}`, {
206
+ throw new ToolError('SOURCE_NOT_FOUND', `Source file not found: ${input.sourcePath}`, {
207
207
  sourcePath: input.sourcePath,
208
208
  });
209
209
  }
@@ -211,7 +211,7 @@ export const projectAssetImport = defineOperation({
211
211
  const destAbs = resolveProjectPath(publicRoot, input.destPath);
212
212
  const destExists = existsSync(destAbs);
213
213
  if (destExists && !input.overwrite) {
214
- throw new OperationError(
214
+ throw new ToolError(
215
215
  'DEST_EXISTS',
216
216
  `${input.destPath} already exists — pass overwrite:true to replace it.`,
217
217
  {
@@ -239,7 +239,7 @@ export const projectAssetImport = defineOperation({
239
239
  });
240
240
 
241
241
  /** Register every B2 `project.asset.*` operation onto `registry`. */
242
- export function registerAssetOperations(registry: OperationRegistry): void {
242
+ export function registerAssetOperations(registry: ToolRegistry): void {
243
243
  registry.register(projectAssetList);
244
244
  registry.register(projectAssetInspect);
245
245
  registry.register(projectAssetImport);
@@ -13,8 +13,8 @@
13
13
 
14
14
  import type { SceneEntity } from '@vgai/engine/scene/scene-types';
15
15
  import { z } from 'zod';
16
- import { OperationError } from '../errors.js';
17
- import { defineOperation, type OperationRegistry } from '../registry.js';
16
+ import { ToolError } from '../errors.js';
17
+ import { defineTool, type ToolRegistry } from '../registry.js';
18
18
  import { finalizeSceneMutation } from './entity-operations.js';
19
19
  import {
20
20
  APPLY_DIFF_FAILED_ERROR,
@@ -77,7 +77,7 @@ const ProjectSceneComponentListInput = z.object({
77
77
  entityId: z.string().describe('Entity to list components for.'),
78
78
  });
79
79
 
80
- export const projectSceneComponentList = defineOperation({
80
+ export const projectSceneComponentList = defineTool({
81
81
  name: 'project.scene.component.list',
82
82
  summary: 'List every component (by name) attached to an entity.',
83
83
  description: 'Read-only. Resolves the entity by stable id.',
@@ -127,7 +127,7 @@ const ComponentAfterSchema = z.object({
127
127
  data: z.unknown(),
128
128
  });
129
129
 
130
- export const projectSceneComponentAdd = defineOperation({
130
+ export const projectSceneComponentAdd = defineTool({
131
131
  name: 'project.scene.component.add',
132
132
  summary: 'Attach a new named component to an entity.',
133
133
  description:
@@ -157,7 +157,7 @@ export const projectSceneComponentAdd = defineOperation({
157
157
  const entity = requireEntityById(scene.entities, input.entityId);
158
158
  const existing = entity.components ?? {};
159
159
  if (input.componentName in existing) {
160
- throw new OperationError(
160
+ throw new ToolError(
161
161
  'COMPONENT_ALREADY_EXISTS',
162
162
  `Entity "${input.entityId}" already has a component named "${input.componentName}" — use project.scene.component.update instead.`,
163
163
  { entityId: input.entityId, componentName: input.componentName },
@@ -196,7 +196,7 @@ const ProjectSceneComponentUpdateInput = z.object({
196
196
  baseHash: BaseHashField,
197
197
  });
198
198
 
199
- export const projectSceneComponentUpdate = defineOperation({
199
+ export const projectSceneComponentUpdate = defineTool({
200
200
  name: 'project.scene.component.update',
201
201
  summary: 'Merge field changes onto an existing named component.',
202
202
  description: 'Delegates to applyDiff’s `update` op with a merged components record.',
@@ -226,7 +226,7 @@ export const projectSceneComponentUpdate = defineOperation({
226
226
  const existing = entity.components ?? {};
227
227
  const currentData = existing[input.componentName];
228
228
  if (currentData === undefined) {
229
- throw new OperationError(
229
+ throw new ToolError(
230
230
  'COMPONENT_NOT_FOUND',
231
231
  `Entity "${input.entityId}" has no component named "${input.componentName}".`,
232
232
  { entityId: input.entityId, componentName: input.componentName },
@@ -266,7 +266,7 @@ const ProjectSceneComponentRemoveInput = z.object({
266
266
  baseHash: BaseHashField,
267
267
  });
268
268
 
269
- export const projectSceneComponentRemove = defineOperation({
269
+ export const projectSceneComponentRemove = defineTool({
270
270
  name: 'project.scene.component.remove',
271
271
  summary: 'Detach a named component from an entity.',
272
272
  description:
@@ -300,7 +300,7 @@ export const projectSceneComponentRemove = defineOperation({
300
300
  const existing = entity.components ?? {};
301
301
  const currentData = existing[input.componentName];
302
302
  if (currentData === undefined) {
303
- throw new OperationError(
303
+ throw new ToolError(
304
304
  'COMPONENT_NOT_FOUND',
305
305
  `Entity "${input.entityId}" has no component named "${input.componentName}".`,
306
306
  { entityId: input.entityId, componentName: input.componentName },
@@ -329,7 +329,7 @@ export const projectSceneComponentRemove = defineOperation({
329
329
  });
330
330
 
331
331
  /** Register every B2 `project.scene.component.*` operation onto `registry`. */
332
- export function registerComponentOperations(registry: OperationRegistry): void {
332
+ export function registerComponentOperations(registry: ToolRegistry): void {
333
333
  registry.register(projectSceneComponentList);
334
334
  registry.register(projectSceneComponentAdd);
335
335
  registry.register(projectSceneComponentUpdate);
@@ -26,8 +26,8 @@
26
26
 
27
27
  import { readFileSync } from 'node:fs';
28
28
  import { z } from 'zod';
29
- import { OperationError } from '../errors.js';
30
- import { defineOperation, type OperationRegistry } from '../registry.js';
29
+ import { ToolError } from '../errors.js';
30
+ import { defineTool, type ToolRegistry } from '../registry.js';
31
31
  import {
32
32
  listProjectFiles,
33
33
  NO_PROJECT_ROOT_ERROR,
@@ -40,7 +40,7 @@ import {
40
40
  // project.story.discover
41
41
  // ---------------------------------------------------------------------------
42
42
 
43
- export const projectStoryDiscover = defineOperation({
43
+ export const projectStoryDiscover = defineTool({
44
44
  name: 'project.story.discover',
45
45
  summary: 'List every Storybook CSF story module (*.stories.tsx / *.stories.ts) in the project.',
46
46
  description:
@@ -68,7 +68,7 @@ export const projectStoryDiscover = defineOperation({
68
68
  // project.theatre.discover
69
69
  // ---------------------------------------------------------------------------
70
70
 
71
- export const projectTheatreDiscover = defineOperation({
71
+ export const projectTheatreDiscover = defineTool({
72
72
  name: 'project.theatre.discover',
73
73
  summary:
74
74
  'List every committed Theatre project-state file (*.theatre-project.json) in the project.',
@@ -112,7 +112,7 @@ const ProjectTheatreValidateInput = z.object({
112
112
  .describe('Validate this raw document instead of reading `theatreProjectPath`.'),
113
113
  });
114
114
 
115
- export const projectTheatreValidate = defineOperation({
115
+ export const projectTheatreValidate = defineTool({
116
116
  name: 'project.theatre.validate',
117
117
  summary:
118
118
  'Validate a Theatre project-state file’s OUTER shape (sheetsById/definitionVersion/revisionHistory).',
@@ -148,7 +148,7 @@ export const projectTheatreValidate = defineOperation({
148
148
  const absPath = resolveProjectPath(projectRoot, input.theatreProjectPath);
149
149
  json = JSON.parse(readFileSync(absPath, 'utf-8'));
150
150
  } else {
151
- throw new OperationError(
151
+ throw new ToolError(
152
152
  'BAD_INPUT',
153
153
  'One of theatreProjectPath or document must be given.',
154
154
  {},
@@ -176,7 +176,7 @@ function looksLikeXStateMachine(text: string): { importsXstate: boolean; defines
176
176
  return { importsXstate, definesMachine };
177
177
  }
178
178
 
179
- export const projectXstateDiscover = defineOperation({
179
+ export const projectXstateDiscover = defineTool({
180
180
  name: 'project.xstate.discover',
181
181
  summary:
182
182
  'Best-effort static discovery of TypeScript modules that appear to define an XState machine.',
@@ -230,7 +230,7 @@ const ProjectXstateValidateInput = z.object({
230
230
  machinePath: z.string().describe('Project-relative path to a TypeScript module to check.'),
231
231
  });
232
232
 
233
- export const projectXstateValidate = defineOperation({
233
+ export const projectXstateValidate = defineTool({
234
234
  name: 'project.xstate.validate',
235
235
  summary:
236
236
  'Best-effort static check that a TypeScript module imports xstate and defines a machine.',
@@ -260,7 +260,7 @@ export const projectXstateValidate = defineOperation({
260
260
  });
261
261
 
262
262
  /** Register every B2 story/Theatre/XState discovery operation onto `registry`. */
263
- export function registerDiscoveryOperations(registry: OperationRegistry): void {
263
+ export function registerDiscoveryOperations(registry: ToolRegistry): void {
264
264
  registry.register(projectStoryDiscover);
265
265
  registry.register(projectTheatreDiscover);
266
266
  registry.register(projectTheatreValidate);
@@ -23,8 +23,8 @@ import type { applyDiff } from '@vgai/engine/scene/scene-apply';
23
23
  import type { SceneEntity } from '@vgai/engine/scene/scene-types';
24
24
  import { SceneEntitySchema } from '@vgai/engine/scene/schema/entity';
25
25
  import { z } from 'zod';
26
- import { OperationError } from '../errors.js';
27
- import { defineOperation, type OperationRegistry } from '../registry.js';
26
+ import { ToolError } from '../errors.js';
27
+ import { defineTool, type ToolRegistry } from '../registry.js';
28
28
  import {
29
29
  APPLY_DIFF_FAILED_ERROR,
30
30
  applyDiffOrThrow,
@@ -73,7 +73,7 @@ function finalizeSceneMutation<TBefore, TAfter>(
73
73
  parseSceneFile(result, scenePath);
74
74
  } catch (err) {
75
75
  if (err instanceof SceneParseError) {
76
- throw new OperationError('SCENE_INVALID', `Result fails validation: ${err.message}`, {
76
+ throw new ToolError('SCENE_INVALID', `Result fails validation: ${err.message}`, {
77
77
  issues: err.issues.map((i) => `${i.path.join('.')}: ${i.message}`),
78
78
  });
79
79
  }
@@ -103,7 +103,7 @@ const ProjectSceneEntityAddInput = z.object({
103
103
  baseHash: BaseHashField,
104
104
  });
105
105
 
106
- export const projectSceneEntityAdd = defineOperation({
106
+ export const projectSceneEntityAdd = defineTool({
107
107
  name: 'project.scene.entity.add',
108
108
  summary: 'Insert a new entity into a scene, by parent id + index.',
109
109
  description:
@@ -183,7 +183,7 @@ const ProjectSceneEntityUpdateInput = z.object({
183
183
  baseHash: BaseHashField,
184
184
  });
185
185
 
186
- export const projectSceneEntityUpdate = defineOperation({
186
+ export const projectSceneEntityUpdate = defineTool({
187
187
  name: 'project.scene.entity.update',
188
188
  summary: 'Merge field changes onto an existing entity, addressed by stable id.',
189
189
  description:
@@ -252,7 +252,7 @@ const ProjectSceneEntityMoveInput = z.object({
252
252
  baseHash: BaseHashField,
253
253
  });
254
254
 
255
- export const projectSceneEntityMove = defineOperation({
255
+ export const projectSceneEntityMove = defineTool({
256
256
  name: 'project.scene.entity.move',
257
257
  summary: 'Reparent and/or reorder an existing entity, addressed by stable id.',
258
258
  description: 'Builds a single `move` DiffOp and delegates to applyDiff.',
@@ -307,7 +307,7 @@ const ProjectSceneEntityRemoveInput = z.object({
307
307
  baseHash: BaseHashField,
308
308
  });
309
309
 
310
- export const projectSceneEntityRemove = defineOperation({
310
+ export const projectSceneEntityRemove = defineTool({
311
311
  name: 'project.scene.entity.remove',
312
312
  summary: 'Delete an entity (and its subtree), addressed by stable id.',
313
313
  description: 'Builds a single `remove` DiffOp and delegates to applyDiff.',
@@ -354,7 +354,7 @@ export const projectSceneEntityRemove = defineOperation({
354
354
  });
355
355
 
356
356
  /** Register every B2 `project.scene.entity.*` operation onto `registry`. */
357
- export function registerEntityOperations(registry: OperationRegistry): void {
357
+ export function registerEntityOperations(registry: ToolRegistry): void {
358
358
  registry.register(projectSceneEntityAdd);
359
359
  registry.register(projectSceneEntityUpdate);
360
360
  registry.register(projectSceneEntityMove);
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * B2 — `project.*` operations
3
3
  * (docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md §8 B2). Registered
4
- * SEPARATELY from B1's `registerBuiltinOperations` (see `../operations.ts`)
5
- * so B1's own registry tests — which assert `registerBuiltinOperations`
4
+ * SEPARATELY from B1's `registerBuiltinTools` (see `../operations.ts`)
5
+ * so B1's own registry tests — which assert `registerBuiltinTools`
6
6
  * registers "exactly the three B1 sample operations" — are unaffected; the
7
7
  * default `operations` singleton (`../index.ts`) calls both.
8
8
  */
@@ -37,7 +37,7 @@ export {
37
37
  writeFileAtomic,
38
38
  } from './shared.js';
39
39
 
40
- import type { OperationRegistry } from '../registry.js';
40
+ import type { ToolRegistry } from '../registry.js';
41
41
  import { registerAssetOperations } from './asset-operations.js';
42
42
  import { registerComponentOperations } from './component-operations.js';
43
43
  import { registerDiscoveryOperations } from './discovery-operations.js';
@@ -48,7 +48,7 @@ import { registerManifestOperations } from './manifest-operations.js';
48
48
  import { registerSceneOperations } from './scene-operations.js';
49
49
 
50
50
  /** Register every B2 `project.*` operation onto `registry`. */
51
- export function registerProjectOperations(registry: OperationRegistry): void {
51
+ export function registerProjectOperations(registry: ToolRegistry): void {
52
52
  registerManifestOperations(registry);
53
53
  registerSceneOperations(registry);
54
54
  registerEntityOperations(registry);
@@ -12,8 +12,8 @@
12
12
 
13
13
  import { InputMapFileSchema } from '@vgai/engine/input/schema';
14
14
  import { z } from 'zod';
15
- import { OperationError } from '../errors.js';
16
- import { defineOperation, type OperationRegistry } from '../registry.js';
15
+ import { ToolError } from '../errors.js';
16
+ import { defineTool, type ToolRegistry } from '../registry.js';
17
17
  import {
18
18
  BaseHashField,
19
19
  CONFLICT_ERROR,
@@ -51,7 +51,7 @@ const ProjectInputMapReadInput = z.object({
51
51
  ),
52
52
  });
53
53
 
54
- export const projectInputMapRead = defineOperation({
54
+ export const projectInputMapRead = defineTool({
55
55
  name: 'project.inputMap.read',
56
56
  summary: 'Read + validate an .inputmap.json file.',
57
57
  description:
@@ -82,7 +82,7 @@ export const projectInputMapRead = defineOperation({
82
82
  const json: unknown = JSON.parse(raw);
83
83
  const parsed = InputMapFileSchema.safeParse(json);
84
84
  if (!parsed.success) {
85
- throw new OperationError(
85
+ throw new ToolError(
86
86
  'INPUT_MAP_INVALID',
87
87
  `${input.inputMapPath} failed input-map validation.`,
88
88
  {
@@ -109,7 +109,7 @@ const ProjectInputMapValidateInput = z.object({
109
109
  .describe('Validate this raw document instead of reading `inputMapPath`.'),
110
110
  });
111
111
 
112
- export const projectInputMapValidate = defineOperation({
112
+ export const projectInputMapValidate = defineTool({
113
113
  name: 'project.inputMap.validate',
114
114
  summary: 'Validate an input map file or raw document against the real input-map schema.',
115
115
  description:
@@ -142,7 +142,7 @@ export const projectInputMapValidate = defineOperation({
142
142
  const absPath = resolveProjectPath(projectRoot, input.inputMapPath);
143
143
  json = JSON.parse(readFileWithHash(absPath).raw);
144
144
  } else {
145
- throw new OperationError('BAD_INPUT', 'One of inputMapPath or inputMap must be given.', {});
145
+ throw new ToolError('BAD_INPUT', 'One of inputMapPath or inputMap must be given.', {});
146
146
  }
147
147
  const parsed = InputMapFileSchema.safeParse(json);
148
148
  if (!parsed.success) return { valid: false, issues: formatIssues(parsed.error.issues) };
@@ -166,7 +166,7 @@ const ProjectInputMapUpdateInput = z.object({
166
166
  baseHash: BaseHashField,
167
167
  });
168
168
 
169
- export const projectInputMapUpdate = defineOperation({
169
+ export const projectInputMapUpdate = defineTool({
170
170
  name: 'project.inputMap.update',
171
171
  summary: 'Add, replace, or delete actions in an input map — dry-run capable, conflict-checked.',
172
172
  description:
@@ -201,7 +201,7 @@ export const projectInputMapUpdate = defineOperation({
201
201
 
202
202
  const parsed = InputMapFileSchema.safeParse(merged);
203
203
  if (!parsed.success) {
204
- throw new OperationError(
204
+ throw new ToolError(
205
205
  'INPUT_MAP_INVALID',
206
206
  'The patched input map failed schema validation.',
207
207
  {
@@ -226,7 +226,7 @@ export const projectInputMapUpdate = defineOperation({
226
226
  });
227
227
 
228
228
  /** Register every B2 `project.inputMap.*` operation onto `registry`. */
229
- export function registerInputMapOperations(registry: OperationRegistry): void {
229
+ export function registerInputMapOperations(registry: ToolRegistry): void {
230
230
  registry.register(projectInputMapRead);
231
231
  registry.register(projectInputMapValidate);
232
232
  registry.register(projectInputMapUpdate);
@@ -1,7 +1,7 @@
1
1
  /** Read-only operation-registry projection of first-contact folder inspection. */
2
2
 
3
3
  import { z } from 'zod';
4
- import { defineOperation, type OperationRegistry } from '../registry.js';
4
+ import { defineTool, type ToolRegistry } from '../registry.js';
5
5
  import { inspectProject, type ProjectInspection } from './inspection.js';
6
6
  import { nodeProjectInspectionReader } from './inspection-node.js';
7
7
  import { NO_PROJECT_ROOT_ERROR, requireProjectRoot } from './shared.js';
@@ -18,7 +18,7 @@ const ProjectInspectionResult = z.object({
18
18
  nextAction: z.string(),
19
19
  });
20
20
 
21
- export const projectInspect = defineOperation({
21
+ export const projectInspect = defineTool({
22
22
  name: 'project.inspect',
23
23
  summary: 'Inspect this folder as a possible VGAI or existing browser-game project.',
24
24
  description:
@@ -38,6 +38,6 @@ export const projectInspect = defineOperation({
38
38
  },
39
39
  });
40
40
 
41
- export function registerInspectionOperations(registry: OperationRegistry): void {
41
+ export function registerInspectionOperations(registry: ToolRegistry): void {
42
42
  registry.register(projectInspect);
43
43
  }
@@ -15,8 +15,8 @@ import { existsSync } from 'node:fs';
15
15
  import { loadGameManifest } from '@vgai/engine/manifest/load';
16
16
  import { GameManifestSchema } from '@vgai/engine/manifest/schema';
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
  BaseHashField,
22
22
  CONFLICT_ERROR,
@@ -73,7 +73,7 @@ const ProjectDiscoverResult = z
73
73
  })
74
74
  .describe('A one-call survey of the project at ctx.projectRoot.');
75
75
 
76
- export const projectDiscover = defineOperation({
76
+ export const projectDiscover = defineTool({
77
77
  name: 'project.discover',
78
78
  summary:
79
79
  "Survey a project's manifest, scenes, input maps, stories, and Theatre project state files.",
@@ -151,7 +151,7 @@ const ProjectManifestReadResult = z
151
151
  })
152
152
  .describe('Raw manifest read result.');
153
153
 
154
- export const projectManifestRead = defineOperation({
154
+ export const projectManifestRead = defineTool({
155
155
  name: 'project.manifest.read',
156
156
  summary: 'Read the raw vgai.game.json manifest for the project.',
157
157
  description:
@@ -193,7 +193,7 @@ const ProjectManifestValidateResult = z
193
193
  })
194
194
  .describe('Structured manifest validation result — never throws for an invalid document.');
195
195
 
196
- export const projectManifestValidate = defineOperation({
196
+ export const projectManifestValidate = defineTool({
197
197
  name: 'project.manifest.validate',
198
198
  summary: 'Validate the project manifest against the real engine schema + cross-field rules.',
199
199
  description:
@@ -272,7 +272,7 @@ const ProjectManifestUpdateResult = mutationResultSchema(
272
272
  .describe('The manifest document after the patch (or that WOULD result).'),
273
273
  );
274
274
 
275
- export const projectManifestUpdate = defineOperation({
275
+ export const projectManifestUpdate = defineTool({
276
276
  name: 'project.manifest.update',
277
277
  summary: 'Patch name/version/resolution on vgai.game.json, dry-run capable, conflict-checked.',
278
278
  description:
@@ -311,7 +311,7 @@ export const projectManifestUpdate = defineOperation({
311
311
 
312
312
  const parsed = GameManifestSchema.safeParse(merged);
313
313
  if (!parsed.success) {
314
- throw new OperationError(
314
+ throw new ToolError(
315
315
  'INVALID_MANIFEST',
316
316
  'The patched manifest failed schema validation.',
317
317
  {
@@ -324,7 +324,7 @@ export const projectManifestUpdate = defineOperation({
324
324
  try {
325
325
  loadGameManifest(merged);
326
326
  } catch (err) {
327
- throw new OperationError(
327
+ throw new ToolError(
328
328
  'INVALID_MANIFEST',
329
329
  'The patched manifest failed cross-field validation.',
330
330
  {
@@ -349,7 +349,7 @@ export const projectManifestUpdate = defineOperation({
349
349
  });
350
350
 
351
351
  /** Register every B2 `project.discover` / `project.manifest.*` operation onto `registry`. */
352
- export function registerManifestOperations(registry: OperationRegistry): void {
352
+ export function registerManifestOperations(registry: ToolRegistry): void {
353
353
  registry.register(projectDiscover);
354
354
  registry.register(projectManifestRead);
355
355
  registry.register(projectManifestValidate);
@@ -24,8 +24,8 @@ import { ApplyDiffError, applyDiff } from '@vgai/engine/scene/scene-apply';
24
24
  import { SceneDiffSchema } from '@vgai/engine/scene/scene-diff-schema';
25
25
  import type { SceneFile } from '@vgai/engine/scene/scene-types';
26
26
  import { z } from 'zod';
27
- import { OperationError } from '../errors.js';
28
- import { defineOperation, type OperationRegistry } from '../registry.js';
27
+ import { ToolError } from '../errors.js';
28
+ import { defineTool, type ToolRegistry } from '../registry.js';
29
29
  import {
30
30
  BaseHashField,
31
31
  CONFLICT_ERROR,
@@ -102,7 +102,7 @@ export function readAndParseScene(
102
102
  try {
103
103
  json = JSON.parse(raw);
104
104
  } catch (err) {
105
- throw new OperationError(
105
+ throw new ToolError(
106
106
  'SCENE_INVALID',
107
107
  `${scenePath} is not valid JSON: ${err instanceof Error ? err.message : err}`,
108
108
  {
@@ -115,7 +115,7 @@ export function readAndParseScene(
115
115
  return { absPath, hash, scene };
116
116
  } catch (err) {
117
117
  if (err instanceof SceneParseError) {
118
- throw new OperationError('SCENE_INVALID', err.message, { issues: formatIssues(err.issues) });
118
+ throw new ToolError('SCENE_INVALID', err.message, { issues: formatIssues(err.issues) });
119
119
  }
120
120
  throw err;
121
121
  }
@@ -130,7 +130,7 @@ export function applyDiffOrThrow(
130
130
  return applyDiff(scene, patch);
131
131
  } catch (err) {
132
132
  if (err instanceof ApplyDiffError) {
133
- throw new OperationError('APPLY_DIFF_FAILED', err.message, {
133
+ throw new ToolError('APPLY_DIFF_FAILED', err.message, {
134
134
  opIndex: err.opIndex,
135
135
  opType: err.opType,
136
136
  entityId: err.entityId,
@@ -159,7 +159,7 @@ const ProjectSceneReadResult = z
159
159
  })
160
160
  .describe('Scene read result.');
161
161
 
162
- export const projectSceneRead = defineOperation({
162
+ export const projectSceneRead = defineTool({
163
163
  name: 'project.scene.read',
164
164
  summary: 'Read + validate a .vscn.json scene file.',
165
165
  description:
@@ -205,7 +205,7 @@ const ProjectSceneValidateResult = z
205
205
  .object({ valid: z.boolean(), issues: z.array(z.string()) })
206
206
  .describe('Structured validation result — never throws for a malformed scene.');
207
207
 
208
- export const projectSceneValidate = defineOperation({
208
+ export const projectSceneValidate = defineTool({
209
209
  name: 'project.scene.validate',
210
210
  summary: 'Validate a scene file or raw scene document against the real scene schema.',
211
211
  description:
@@ -241,7 +241,7 @@ export const projectSceneValidate = defineOperation({
241
241
  }
242
242
  json = JSON.parse(readFileWithHash(absPath).raw);
243
243
  } else {
244
- throw new OperationError('BAD_INPUT', 'One of scenePath or scene must be given.', {});
244
+ throw new ToolError('BAD_INPUT', 'One of scenePath or scene must be given.', {});
245
245
  }
246
246
 
247
247
  try {
@@ -279,7 +279,7 @@ const ProjectSceneDiffResult = z
279
279
  })
280
280
  .describe('What applying this patch would do — no write, ever.');
281
281
 
282
- export const projectSceneDiff = defineOperation({
282
+ export const projectSceneDiff = defineTool({
283
283
  name: 'project.scene.diff',
284
284
  summary: 'Preview a SceneDiff patch against a scene file without writing anything.',
285
285
  description:
@@ -317,7 +317,7 @@ export const projectSceneDiff = defineOperation({
317
317
  return { wouldBeValid: true, issues: [], opCounts, entitiesTouched };
318
318
  } catch (err) {
319
319
  const message =
320
- err instanceof OperationError
320
+ err instanceof ToolError
321
321
  ? err.message
322
322
  : err instanceof SceneParseError
323
323
  ? err.message
@@ -348,7 +348,7 @@ const ProjectSceneApplyResult = mutationResultSchema(
348
348
  .describe('The scene document after the patch (or that WOULD result).'),
349
349
  );
350
350
 
351
- export const projectSceneApply = defineOperation({
351
+ export const projectSceneApply = defineTool({
352
352
  name: 'project.scene.apply',
353
353
  summary: 'Apply a SceneDiff patch to a scene file — dry-run capable, conflict-checked.',
354
354
  description:
@@ -381,7 +381,7 @@ export const projectSceneApply = defineOperation({
381
381
  parseSceneFile(result, input.scenePath);
382
382
  } catch (err) {
383
383
  if (err instanceof SceneParseError) {
384
- throw new OperationError(
384
+ throw new ToolError(
385
385
  'SCENE_INVALID',
386
386
  `applyDiff produced a scene that fails validation: ${err.message}`,
387
387
  { issues: formatIssues(err.issues) },
@@ -394,7 +394,7 @@ export const projectSceneApply = defineOperation({
394
394
  // write (dry-run or real) exactly like `vgai apply-diff`.
395
395
  const refErrors = dereferenceNewAssetRefs(scene, result, absPath);
396
396
  if (refErrors.length > 0) {
397
- throw new OperationError(
397
+ throw new ToolError(
398
398
  'ASSET_REF_INVALID',
399
399
  `The patch introduces ${refErrors.length} broken asset reference(s): ` +
400
400
  refErrors.map((e) => `${e.field} -> "${e.path}": ${e.reason}`).join('; '),
@@ -418,7 +418,7 @@ export const projectSceneApply = defineOperation({
418
418
  });
419
419
 
420
420
  /** Register every B2 `project.scene.*` operation onto `registry`. */
421
- export function registerSceneOperations(registry: OperationRegistry): void {
421
+ export function registerSceneOperations(registry: ToolRegistry): void {
422
422
  registry.register(projectSceneRead);
423
423
  registry.register(projectSceneValidate);
424
424
  registry.register(projectSceneDiff);