@vgai/sdk 0.5.42 → 0.5.44

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.42",
5
+ "version": "0.5.44",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@modelcontextprotocol/sdk": "^1.30.0",
34
- "@vgai/engine": "0.5.42",
34
+ "@vgai/engine": "0.5.44",
35
35
  "playwright": "^1.58.2",
36
36
  "zod": "^4.3.6"
37
37
  }
@@ -459,7 +459,12 @@ export function unplayedSessionTier(
459
459
  sessionStartedAtMs: number | null,
460
460
  newestEvidence: number | null,
461
461
  now: number,
462
+ /** Does the project declare anything to play? A MODELS project (no roots)
463
+ * has no game to run; the nag sent one to `vgai play`, which refused,
464
+ * and the refusal sat on the console (blind lantern round, 2026-09-06). */
465
+ playable = true,
462
466
  ): TripwireTier {
467
+ if (!playable) return 'silent';
463
468
  if (sessionStartedAtMs === null) return 'silent';
464
469
  if (newestEvidence !== null && newestEvidence >= sessionStartedAtMs) return 'silent';
465
470
  const servingForMs = now - sessionStartedAtMs;
@@ -482,8 +487,9 @@ export function unplayedSessionBanner(
482
487
  sessionStartedAtMs: number | null,
483
488
  newestEvidence: number | null,
484
489
  now: number,
490
+ playable = true,
485
491
  ): string | null {
486
- const tier = unplayedSessionTier(sessionStartedAtMs, newestEvidence, now);
492
+ const tier = unplayedSessionTier(sessionStartedAtMs, newestEvidence, now, playable);
487
493
  if (tier === 'silent' || sessionStartedAtMs === null) return null;
488
494
  const elapsed = formatElapsed(now - sessionStartedAtMs);
489
495
  if (tier === 'notice') {
@@ -290,6 +290,22 @@ export type SessionJournalEvent =
290
290
  | { readonly kind: 'tab-gap-closed'; readonly tabId8: string; readonly gapMs: number }
291
291
  /** Same tabId, new epoch: the page reloaded. The tab never left. */
292
292
  | { readonly kind: 'tab-reloaded'; readonly tabId8: string; readonly epochCount: number }
293
+ /** The page announced main-thread work before starting it (a play-boot
294
+ * step, a model's `building src/models/x.ts`), or cleared it (`null`). */
295
+ | { readonly kind: 'page-phase'; readonly phase: string | null }
296
+ /**
297
+ * What the project's source watcher saw: one line per chokidar event under
298
+ * `src/` (`add`/`change`/`unlink`), with the file's mtime as the watcher
299
+ * found it (`null` when the path was gone). The instrument for "why did
300
+ * the page reload / the table refresh" — a reload on a file whose mtime
301
+ * never moved is a phantom event, and this is where it shows.
302
+ */
303
+ | {
304
+ readonly kind: 'src-watch';
305
+ readonly event: string;
306
+ readonly path: string;
307
+ readonly mtimeMs: number | null;
308
+ }
293
309
  /** Absent past its grace: this tab is gone. */
294
310
  | { readonly kind: 'tab-departed'; readonly tabId8: string; readonly absentMs: number }
295
311
  /**
@@ -392,6 +408,17 @@ export type SessionJournalEvent =
392
408
  readonly by: string;
393
409
  readonly reason: string;
394
410
  readonly message: string;
411
+ }
412
+ /** A condition cleared by its OWNER, which proved it gone by an event the
413
+ * reload rule cannot see — a play remount resolving its own
414
+ * `Restart required` warning. `by` names that owner, never a person. */
415
+ | {
416
+ readonly kind: 'console-resolved';
417
+ readonly id: string;
418
+ readonly severity: 'error' | 'warn';
419
+ readonly count: number;
420
+ readonly by: string;
421
+ readonly message: string;
395
422
  };
396
423
 
397
424
  /** A parsed journal line: the event plus when it was appended. */
@@ -631,6 +658,10 @@ export function formatJournalLine(line: SessionJournalLine): string {
631
658
  return `journal: ${at} tab-gap-closed ${line.tabId8} after ${Math.round(line.gapMs / 100) / 10}s`;
632
659
  case 'tab-reloaded':
633
660
  return `journal: ${at} tab-reloaded ${line.tabId8} (page load ${line.epochCount})`;
661
+ case 'page-phase':
662
+ return `journal: ${at} page-phase ${line.phase ?? '(clear)'}`;
663
+ case 'src-watch':
664
+ return `journal: ${at} src-watch ${line.event} ${line.path}${line.mtimeMs === null ? ' (gone)' : ''}`;
634
665
  case 'tab-departed':
635
666
  return `journal: ${at} tab-departed ${line.tabId8} absent ${Math.round(line.absentMs / 100) / 10}s`;
636
667
  case 'tab-duplicated':
@@ -652,6 +683,8 @@ export function formatJournalLine(line: SessionJournalLine): string {
652
683
  return `journal: ${at} console-retired ${line.id} after ${line.count} — did not recur after reload`;
653
684
  case 'console-ack':
654
685
  return `journal: ${at} console-ack ${line.id} (×${line.count}) by ${line.by}: ${line.reason}`;
686
+ case 'console-resolved':
687
+ return `journal: ${at} console-resolved ${line.id} after ${line.count} — ${line.by} cleared the condition it raised`;
655
688
  }
656
689
  }
657
690
 
package/src/registry.ts CHANGED
@@ -139,7 +139,7 @@ export type ToolOutcome<TResult = unknown> =
139
139
  * `Error`, or a non-Error throw — normalized into INTERNAL_ERROR. The
140
140
  * raw exception/message is never used as the identifying `code`, but it
141
141
  * IS carried in `message` as well as `data.message`: every projection
142
- * (the CLI's `vgai run`, the oclif commands, `vgai screenshot`'s module
142
+ * (the CLI's `vgai tool`, the oclif commands, `vgai screenshot`'s module
143
143
  * lane) shows `error.message` and only some of them dump `data`, so a
144
144
  * `message` that said nothing but "threw an unstructured exception"
145
145
  * hid the one sentence the caller needed ("No editor connected — open
package/src/types.ts CHANGED
@@ -127,5 +127,15 @@ export interface ToolContext {
127
127
  * game binds `game.instance(ctx.instance)` from it; omitted means the sole
128
128
  * live instance. */
129
129
  instance?: string;
130
+ /** The HOST's project-module loader (the editor server's Vite SSR loader,
131
+ * wrapped with dependency-change invalidation). A tool that imports the
132
+ * project's own source at run time — `project.bake.preview` importing
133
+ * `src/models/barrel.ts` — MUST load through this rather than a raw
134
+ * `import()`: Node's ESM cache never invalidates, so a raw import returned
135
+ * the FIRST version of a model for the life of the server and every later
136
+ * edit re-rendered byte-identical until `vgai restart` (measured on the
137
+ * blind modeling bench, 2026-09-05: three restarts in one barrel). Absent
138
+ * only when the tool runs outside an editor server. */
139
+ loadProjectModule?: (absolutePath: string) => Promise<unknown>;
130
140
  [key: string]: unknown;
131
141
  }