@vgai/sdk 0.5.12 → 0.5.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.12",
5
+ "version": "0.5.14",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@modelcontextprotocol/sdk": "^1.30.0",
33
- "@vgai/engine": "0.5.12",
33
+ "@vgai/engine": "0.5.14",
34
34
  "playwright": "^1.58.2",
35
35
  "zod": "^4.3.6"
36
36
  }
@@ -31,7 +31,7 @@ import { existsSync, readFileSync } from 'node:fs';
31
31
  import { isAbsolute, join } from 'node:path';
32
32
  import { z } from 'zod';
33
33
  import { ToolError } from '../errors.js';
34
- import { listProjectFiles } from '../project/shared.js';
34
+ import { isTheatreProjectPath, listProjectFiles } from '../project/shared.js';
35
35
  import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
36
36
  import type { ToolContext } from '../types.js';
37
37
 
@@ -153,7 +153,7 @@ export const cinematicProjectList = defineTool({
153
153
  permission: { risk: 'read', summary: 'Walks a file tree; no writes.' },
154
154
  async impl(input, ctx) {
155
155
  const root = resolveRoot(input.root, ctx);
156
- const theatreProjectFiles = listProjectFiles(root, (p) => p.endsWith('theatre-project.json'));
156
+ const theatreProjectFiles = listProjectFiles(root, isTheatreProjectPath);
157
157
  return { root, theatreProjectFiles };
158
158
  },
159
159
  });
@@ -159,7 +159,7 @@ export function createMcpServer(options: CreateMcpServerOptions = {}): VgaiMcpSe
159
159
  request.params.name === 'discover-project'
160
160
  ? 'Call project.discover, project.status, and project.manifest.read. Summarize each adapter root and then call editor.session.list. Do not write files.'
161
161
  : request.params.name === 'bring-existing-game'
162
- ? 'Call project.inspect before writing anything. Use its detected technologies, entry candidates, and blockers to identify Three.js/R3F, PixiJS, or React by renderer; explain the honest adapter tier and obtain consent before metadata or source writes.'
162
+ ? 'Call project.inspect before writing anything. Use its detected technologies, entry candidates, and blockers to identify Three.js/R3F, PixiJS, or React by renderer; name any capability the adapter has not yet reached and obtain consent before metadata or source writes.'
163
163
  : `Implement only this requested outcome: ${task}. Discover the project first, preserve unrelated work, use dryRun for file mutations when available, then verify through the exact matching editor with Play, status/logs, and a legible capture.`;
164
164
  return {
165
165
  description: requested.description,
@@ -9,7 +9,8 @@
9
9
  * - Stories (CSF) are ordinary `.tsx`/`.ts` modules (§5.2/C3) — VGAI stores
10
10
  * no duplicate story registry, so there is nothing to mutate here; listing
11
11
  * `*.stories.tsx`/`*.stories.ts` is the whole B2 surface.
12
- * - Theatre project state (`*.theatre-project.json`) is a real, versioned,
12
+ * - Theatre project state (`theatre-project.json` or `*.theatre-project.json`)
13
+ * is a real, versioned,
13
14
  * proprietary Theatre.js format this repo does not own (§3.2/§5.3 — "the
14
15
  * committed Theatre project state is authoritative authored data"). B2
15
16
  * validates only the OUTER shape (`sheetsById`/`definitionVersion`/
@@ -29,6 +30,7 @@ import { z } from 'zod';
29
30
  import { ToolError } from '../errors.js';
30
31
  import { defineTool, type ToolRegistry } from '../registry.js';
31
32
  import {
33
+ isTheatreProjectPath,
32
34
  listProjectFiles,
33
35
  NO_PROJECT_ROOT_ERROR,
34
36
  PATH_OUTSIDE_PROJECT_ERROR,
@@ -71,7 +73,7 @@ export const projectStoryDiscover = defineTool({
71
73
  export const projectTheatreDiscover = defineTool({
72
74
  name: 'project.theatre.discover',
73
75
  summary:
74
- 'List every committed Theatre project-state file (*.theatre-project.json) in the project.',
76
+ 'List every committed Theatre project-state file (theatre-project.json or *.theatre-project.json) in the project.',
75
77
  description:
76
78
  'List-only (§3.2/§5.3: the committed Theatre project state is authoritative authored data VGAI does not own the internal format of).',
77
79
  input: z.object({}),
@@ -86,9 +88,7 @@ export const projectTheatreDiscover = defineTool({
86
88
  permission: { risk: 'read', summary: 'Walks the project file tree; no writes.' },
87
89
  async impl(_input, ctx) {
88
90
  const projectRoot = requireProjectRoot(ctx);
89
- const theatreProjectFiles = listProjectFiles(projectRoot, (p) =>
90
- p.endsWith('.theatre-project.json'),
91
- );
91
+ const theatreProjectFiles = listProjectFiles(projectRoot, isTheatreProjectPath);
92
92
  return { theatreProjectFiles };
93
93
  },
94
94
  });
@@ -22,6 +22,7 @@ import {
22
22
  checkNotStale,
23
23
  DryRunField,
24
24
  FILE_NOT_FOUND_ERROR,
25
+ isTheatreProjectPath,
25
26
  listProjectFiles,
26
27
  mutationResultSchema,
27
28
  NO_PROJECT_ROOT_ERROR,
@@ -65,7 +66,7 @@ const ProjectDiscoverResult = z
65
66
  .describe('Every *.stories.tsx / *.stories.ts found under the project.'),
66
67
  theatreProjectFiles: z
67
68
  .array(z.string())
68
- .describe('Every *.theatre-project.json found under the project.'),
69
+ .describe('Every theatre-project.json / *.theatre-project.json found under the project.'),
69
70
  })
70
71
  .describe('A one-call survey of the project at ctx.projectRoot.');
71
72
 
@@ -115,9 +116,7 @@ export const projectDiscover = defineTool({
115
116
  projectRoot,
116
117
  (p) => p.endsWith('.stories.tsx') || p.endsWith('.stories.ts'),
117
118
  ),
118
- theatreProjectFiles: listProjectFiles(projectRoot, (p) =>
119
- p.endsWith('.theatre-project.json'),
120
- ),
119
+ theatreProjectFiles: listProjectFiles(projectRoot, isTheatreProjectPath),
121
120
  };
122
121
  },
123
122
  });
@@ -235,16 +235,78 @@ export type SessionJournalEvent =
235
235
  /** Two epochs beating under one tabId — "Duplicate Tab" copied sessionStorage. */
236
236
  | { readonly kind: 'tab-duplicated'; readonly tabId8: string }
237
237
  /**
238
- * The tab is present (its worker is beating) but its PAGE has never opened
239
- * a command channel this page-load. A heartbeat proves the tab exists, not
240
- * that the document works a main thread that died after the inline
241
- * bootstrap beats forever and can run nothing. Such a tab is passed over
242
- * for blessing and named in the refusal.
238
+ * The tab is present but its PAGE never came up this page-load. Presence
239
+ * proves the tab exists, not that the document works a main thread that
240
+ * died after the inline bootstrap keeps beating and holding its control
241
+ * connection, and can run nothing. Such a tab is passed over for blessing
242
+ * and named in the refusal.
243
+ *
244
+ * `reason` says which stage it is stuck at: `'no-channel'` (the page never
245
+ * opened a control connection at all) or `'no-command-listener'` (it did,
246
+ * and the module graph behind it never produced a listener).
243
247
  */
244
248
  | {
245
249
  readonly kind: 'tab-unresponsive';
246
250
  readonly tabId8: string;
247
- readonly noChannelForMs: number;
251
+ readonly reason: 'no-channel' | 'no-command-listener';
252
+ readonly unresponsiveForMs: number;
253
+ }
254
+ /**
255
+ * An uncaught error or unhandled rejection in a tab's PAGE, captured by
256
+ * `index.html`'s inline bootstrap — before the module graph, so it is
257
+ * recorded even when the boot that would have reported it is the thing that
258
+ * died. This is the line that says WHY a `tab-unresponsive` tab is
259
+ * unresponsive.
260
+ */
261
+ | {
262
+ readonly kind: 'page-error';
263
+ readonly tabId8: string;
264
+ readonly message: string;
265
+ }
266
+ /**
267
+ * One batch of occurrences of ONE console error/warning condition, as the
268
+ * server's unresolved-console ledger recorded it
269
+ * (`packages/editor/server/console-ledger.ts`).
270
+ *
271
+ * This is the DURABLE half of the loudness convention, and the reason it is
272
+ * a row per observation rather than a row per distinct message: `count` is
273
+ * the running total after this batch, so the file answers "how many times
274
+ * did this actually fire, and when did it stop" long after the ledger (and
275
+ * the tab, and the server) are gone. A repeat is never filtered out here —
276
+ * deduping repeats to silence is precisely the failure that made a session's
277
+ * eleven errors read as one line and then nothing.
278
+ */
279
+ | {
280
+ readonly kind: 'console-entry';
281
+ readonly id: string;
282
+ readonly severity: 'error' | 'warn';
283
+ readonly source: string | null;
284
+ /** Occurrences in THIS batch. */
285
+ readonly added: number;
286
+ /** Running total for this condition, across every page-load. */
287
+ readonly count: number;
288
+ readonly message: string;
289
+ }
290
+ /** A condition cleared by the honest re-test: the page reloaded and it did
291
+ * not recur. `count` is what it reached before it stopped. */
292
+ | {
293
+ readonly kind: 'console-retired';
294
+ readonly id: string;
295
+ readonly severity: 'error' | 'warn';
296
+ readonly count: number;
297
+ readonly message: string;
298
+ }
299
+ /** A condition waved through BY NAME (`vgai console ack`). The audit row is
300
+ * the whole point: an acknowledgment records who and why, and never erases
301
+ * what was acknowledged. */
302
+ | {
303
+ readonly kind: 'console-ack';
304
+ readonly id: string;
305
+ readonly severity: 'error' | 'warn';
306
+ readonly count: number;
307
+ readonly by: string;
308
+ readonly reason: string;
309
+ readonly message: string;
248
310
  };
249
311
 
250
312
  /** A parsed journal line: the event plus when it was appended. */
@@ -449,9 +511,22 @@ export function formatJournalLine(line: SessionJournalLine): string {
449
511
  case 'tab-duplicated':
450
512
  return `journal: ${at} tab-duplicated ${line.tabId8}`;
451
513
  case 'tab-unresponsive':
452
- return `journal: ${at} tab-unresponsive ${line.tabId8} no channel for ${Math.round(line.noChannelForMs / 1000)}s`;
514
+ return `journal: ${at} tab-unresponsive ${line.tabId8} ${line.reason} for ${Math.round(line.unresponsiveForMs / 1000)}s`;
515
+ case 'page-error':
516
+ return `journal: ${at} page-error ${line.tabId8} ${line.message}`;
453
517
  case 'tab-death-profile':
454
518
  return `journal: ${at} tab-death-profile ${line.tabId8} code ${line.code} — ${tabDeathProfileBody(line)}`;
519
+ // The CONSOLE arm. `count` is the running total for that condition, so a
520
+ // reader scanning the column sees a repeat climbing rather than the same
521
+ // line over and over with nothing to distinguish the fiftieth from the
522
+ // first. Every line leads with the ack id, because that is what a reader
523
+ // types next.
524
+ case 'console-entry':
525
+ return `journal: ${at} console-${line.severity} ${line.id} +${line.added} (total ${line.count}) ${line.source === null ? '' : `[${line.source}] `}${line.message.split('\n')[0]}`;
526
+ case 'console-retired':
527
+ return `journal: ${at} console-retired ${line.id} after ${line.count} — did not recur after reload`;
528
+ case 'console-ack':
529
+ return `journal: ${at} console-ack ${line.id} (×${line.count}) by ${line.by}: ${line.reason}`;
455
530
  }
456
531
  }
457
532
 
@@ -44,6 +44,12 @@ import { ToolError } from '../errors.js';
44
44
  import type { ToolErrorDefinition } from '../registry.js';
45
45
  import type { ToolContext } from '../types.js';
46
46
 
47
+ /** Both Theatre project-state names already established in shipped projects. */
48
+ export function isTheatreProjectPath(path: string): boolean {
49
+ const filename = path.slice(Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')) + 1);
50
+ return filename === 'theatre-project.json' || filename.endsWith('.theatre-project.json');
51
+ }
52
+
47
53
  // ---------------------------------------------------------------------------
48
54
  // Project-root resolution
49
55
  // ---------------------------------------------------------------------------