@rolepod/uiproof 0.5.0 → 0.6.0

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/dist/index.d.ts CHANGED
@@ -3,10 +3,18 @@ import { z } from 'zod';
3
3
  import { Page } from 'playwright';
4
4
 
5
5
  /**
6
- * Writes artifacts under `./.rolepod-uiproof/artifacts/{run_id}/` (D-026).
6
+ * Writes run artifacts. In **standalone** mode (default) — and in v0.4 and
7
+ * earlier — runs live under `./.rolepod-uiproof/artifacts/{prefix}_{ts}_{uuid}/`.
7
8
  *
8
- * v0.1 emits two formats: PNG screenshots and a JSON replay bundle. Future
9
- * milestones extend the format set (HAR, console log, a11y tree, video).
9
+ * In **with-parent** mode activated automatically when the env var
10
+ * `ROLEPOD_PARENT=1` is set by the parent `rolepod` plugin's SessionStart
11
+ * hook — runs live under `./.rolepod/evidence/{ts}-rolepod-uiproof-{skill}/`,
12
+ * per the Extension Protocol v1 evidence-path convention. Parent's
13
+ * `check-work` skill aggregates manifest.json files from this directory.
14
+ *
15
+ * Baselines for `visual_diff` always live in `./.rolepod-uiproof/baselines/`
16
+ * regardless of mode — they are user-curated configuration, not per-run
17
+ * evidence.
10
18
  */
11
19
  type ReplayStep = Record<string, unknown>;
12
20
  type ReplayBundle = {
@@ -17,16 +25,45 @@ type ReplayBundle = {
17
25
  steps: ReplayStep[];
18
26
  expect: ReplayStep[];
19
27
  };
28
+ type ArtifactMode = "standalone" | "with-parent";
29
+ type StartRunOptions = {
30
+ /**
31
+ * Skill name as it appears in marketplace (`verify-ui`, `audit-a11y`,
32
+ * `visual-diff`, `scaffold-e2e`, `check-errors`). REQUIRED when emitting
33
+ * a manifest.json (Extension Protocol v1) so parent's `check-work` can
34
+ * route artifacts to the right phase.
35
+ *
36
+ * In `with-parent` mode the run dirname is derived from this field; if
37
+ * omitted the `prefix` argument is used as a fallback (legacy).
38
+ */
39
+ skill?: string;
40
+ };
41
+ type StartRunResult = {
42
+ runId: string;
43
+ runDir: string;
44
+ skill: string;
45
+ mode: ArtifactMode;
46
+ };
20
47
  declare class ArtifactStore {
21
48
  readonly rootDir: string;
49
+ readonly mode: ArtifactMode;
50
+ private readonly baselineRoot;
22
51
  constructor(opts?: {
23
52
  rootDir?: string;
53
+ mode?: ArtifactMode;
24
54
  });
25
- /** Allocate a fresh run id and ensure its directory exists. */
26
- startRun(prefix?: string): Promise<{
27
- runId: string;
28
- runDir: string;
29
- }>;
55
+ /**
56
+ * Allocate a fresh run dir and ensure it exists.
57
+ *
58
+ * - standalone: `./.rolepod-uiproof/artifacts/{prefix}_{ts}_{uuid}/`
59
+ * - with-parent: `./.rolepod/evidence/{ts}-rolepod-uiproof-{skill}/`
60
+ *
61
+ * `prefix` is preserved for back-compat with v0.5 callers; new callers
62
+ * should also pass `opts.skill` so the with-parent path can be derived
63
+ * unambiguously and the manifest can be emitted with the canonical
64
+ * skill name.
65
+ */
66
+ startRun(prefix?: string, opts?: StartRunOptions): Promise<StartRunResult>;
30
67
  writeScreenshot(runDir: string, buf: Buffer, name: string): Promise<string>;
31
68
  writeReplayBundle(runDir: string, bundle: ReplayBundle, name?: string): Promise<string>;
32
69
  writeReport(runDir: string, name: string, body: string): Promise<string>;
@@ -1258,7 +1295,7 @@ declare class SessionRegistry {
1258
1295
  }
1259
1296
 
1260
1297
  declare const SERVER_NAME = "rolepod-uiproof";
1261
- declare const SERVER_VERSION = "0.5.0";
1298
+ declare const SERVER_VERSION = "0.6.0";
1262
1299
  type ServerHandle = {
1263
1300
  mcp: McpServer;
1264
1301
  registry: SessionRegistry;
package/dist/index.js CHANGED
@@ -28,16 +28,49 @@ var log = {
28
28
  // src/artifact/ArtifactStore.ts
29
29
  var ArtifactStore = class {
30
30
  rootDir;
31
+ mode;
32
+ baselineRoot;
31
33
  constructor(opts = {}) {
32
- this.rootDir = opts.rootDir ?? resolve(process.cwd(), ".rolepod-uiproof", "artifacts");
34
+ const detectedParent = process.env.ROLEPOD_PARENT === "1";
35
+ this.mode = opts.mode ?? (detectedParent ? "with-parent" : "standalone");
36
+ if (opts.rootDir !== void 0) {
37
+ this.rootDir = opts.rootDir;
38
+ } else if (this.mode === "with-parent") {
39
+ this.rootDir = resolve(process.cwd(), ".rolepod", "evidence");
40
+ } else {
41
+ this.rootDir = resolve(process.cwd(), ".rolepod-uiproof", "artifacts");
42
+ }
43
+ this.baselineRoot = resolve(process.cwd(), ".rolepod-uiproof", "baselines");
33
44
  }
34
- /** Allocate a fresh run id and ensure its directory exists. */
35
- async startRun(prefix = "run") {
36
- const runId = `${prefix}_${this.timestampSlug()}_${randomUUID().slice(0, 8)}`;
45
+ /**
46
+ * Allocate a fresh run dir and ensure it exists.
47
+ *
48
+ * - standalone: `./.rolepod-uiproof/artifacts/{prefix}_{ts}_{uuid}/`
49
+ * - with-parent: `./.rolepod/evidence/{ts}-rolepod-uiproof-{skill}/`
50
+ *
51
+ * `prefix` is preserved for back-compat with v0.5 callers; new callers
52
+ * should also pass `opts.skill` so the with-parent path can be derived
53
+ * unambiguously and the manifest can be emitted with the canonical
54
+ * skill name.
55
+ */
56
+ async startRun(prefix = "run", opts = {}) {
57
+ const ts = this.timestampSlug();
58
+ const skill = opts.skill ?? prefix;
59
+ let runId;
60
+ if (this.mode === "with-parent") {
61
+ runId = `${ts}-rolepod-uiproof-${skill}`;
62
+ } else {
63
+ runId = `${prefix}_${ts}_${randomUUID().slice(0, 8)}`;
64
+ }
37
65
  const runDir = resolve(this.rootDir, runId);
38
66
  await mkdir(runDir, { recursive: true });
39
- log.debug("artifact run started", { run_id: runId, dir: runDir });
40
- return { runId, runDir };
67
+ log.debug("artifact run started", {
68
+ run_id: runId,
69
+ dir: runDir,
70
+ mode: this.mode,
71
+ skill
72
+ });
73
+ return { runId, runDir, skill, mode: this.mode };
41
74
  }
42
75
  async writeScreenshot(runDir, buf, name) {
43
76
  const path = resolve(runDir, `${name}.png`);
@@ -65,7 +98,7 @@ var ArtifactStore = class {
65
98
  }
66
99
  /** Root for stored visual baselines: `./.rolepod-uiproof/baselines/`. */
67
100
  get baselineDir() {
68
- return resolve(this.rootDir, "..", "baselines");
101
+ return this.baselineRoot;
69
102
  }
70
103
  timestampSlug() {
71
104
  const d = /* @__PURE__ */ new Date();
@@ -1003,21 +1036,21 @@ var PlaywrightEngine = class {
1003
1036
  if (s.dialogArming) {
1004
1037
  s.dialogArming.resolve(false);
1005
1038
  }
1006
- return new Promise((resolve4) => {
1039
+ return new Promise((resolve5) => {
1007
1040
  const arming = {
1008
1041
  action: opts.action,
1009
1042
  text: opts.text,
1010
1043
  expiresAt,
1011
1044
  resolve: (handled) => {
1012
1045
  s.dialogArming = null;
1013
- resolve4({ handled });
1046
+ resolve5({ handled });
1014
1047
  }
1015
1048
  };
1016
1049
  s.dialogArming = arming;
1017
1050
  const timer = setTimeout(() => {
1018
1051
  if (s.dialogArming === arming) {
1019
1052
  s.dialogArming = null;
1020
- resolve4({ handled: false });
1053
+ resolve5({ handled: false });
1021
1054
  }
1022
1055
  }, timeoutMs);
1023
1056
  timer.unref?.();
@@ -2336,6 +2369,39 @@ var browserWaitForTool = {
2336
2369
 
2337
2370
  // src/tools/composite/audit_a11y.ts
2338
2371
  import AxeBuilder from "@axe-core/playwright";
2372
+
2373
+ // src/util/manifest.ts
2374
+ import { writeFile as writeFile2 } from "fs/promises";
2375
+ import { resolve as resolve2 } from "path";
2376
+ var ROLEPOD_PROTOCOL_VERSION = "rolepod/v1";
2377
+ async function writeManifest(input) {
2378
+ const manifest = {
2379
+ protocol: ROLEPOD_PROTOCOL_VERSION,
2380
+ plugin: "rolepod-uiproof",
2381
+ skill: input.skill,
2382
+ phase: input.phase,
2383
+ status: input.status,
2384
+ summary: input.summary,
2385
+ started_at: input.startedAt,
2386
+ finished_at: input.finishedAt,
2387
+ artifacts: input.artifacts,
2388
+ metadata: input.metadata ?? {}
2389
+ };
2390
+ const path = resolve2(input.runDir, "manifest.json");
2391
+ try {
2392
+ await writeFile2(path, JSON.stringify(manifest, null, 2), "utf8");
2393
+ return path;
2394
+ } catch (err) {
2395
+ log.warn("manifest write failed", {
2396
+ run_dir: input.runDir,
2397
+ skill: input.skill,
2398
+ err: String(err)
2399
+ });
2400
+ return void 0;
2401
+ }
2402
+ }
2403
+
2404
+ // src/tools/composite/audit_a11y.ts
2339
2405
  var TAGS_BY_LEVEL = {
2340
2406
  "wcag-a": ["wcag2a", "wcag21a"],
2341
2407
  "wcag-aa": ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"],
@@ -2360,7 +2426,11 @@ var auditA11yTool = {
2360
2426
  inputShape: auditA11yShape,
2361
2427
  build(ctx) {
2362
2428
  return safeHandler(async (args) => {
2363
- const { runId, runDir } = await ctx.store.startRun("audit");
2429
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
2430
+ const { runId, runDir, skill } = await ctx.store.startRun(
2431
+ "audit",
2432
+ { skill: "audit-a11y" }
2433
+ );
2364
2434
  const session = await ctx.registry.open(args.open);
2365
2435
  const engine = ctx.registry.engineFor(session.id);
2366
2436
  if (!(engine instanceof PlaywrightEngine)) {
@@ -2428,15 +2498,45 @@ var auditA11yTool = {
2428
2498
  await ctx.registry.close(session).catch(() => void 0);
2429
2499
  }
2430
2500
  }
2501
+ const counts = countBySeverity(issues);
2502
+ const status = a11yStatus(counts);
2503
+ const artifacts = reportPath ? [{ type: "report", path: reportPath }] : [];
2504
+ const manifestPath = await writeManifest({
2505
+ runDir,
2506
+ skill,
2507
+ phase: "verify",
2508
+ status,
2509
+ summary: buildAuditSummary(args.level, counts, status),
2510
+ startedAt,
2511
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
2512
+ artifacts,
2513
+ metadata: {
2514
+ level: args.level,
2515
+ scope: args.scope,
2516
+ counts,
2517
+ report_format: args.report_format
2518
+ }
2519
+ });
2431
2520
  return ok({
2432
2521
  run_id: runId,
2433
- counts: countBySeverity(issues),
2522
+ counts,
2434
2523
  issues,
2435
- report_path: reportPath
2524
+ report_path: reportPath,
2525
+ ...manifestPath ? { manifest: manifestPath } : {}
2436
2526
  });
2437
2527
  });
2438
2528
  }
2439
2529
  };
2530
+ function a11yStatus(counts) {
2531
+ if ((counts.critical ?? 0) + (counts.serious ?? 0) > 0) return "fail";
2532
+ if ((counts.moderate ?? 0) + (counts.minor ?? 0) > 0) return "warn";
2533
+ return "pass";
2534
+ }
2535
+ function buildAuditSummary(level, counts, status) {
2536
+ const total = (counts.critical ?? 0) + (counts.serious ?? 0) + (counts.moderate ?? 0) + (counts.minor ?? 0);
2537
+ if (status === "pass") return `${level}: 0 issues`;
2538
+ return `${level}: ${total} issue(s) \u2014 critical=${counts.critical ?? 0}, serious=${counts.serious ?? 0}, moderate=${counts.moderate ?? 0}, minor=${counts.minor ?? 0}`;
2539
+ }
2440
2540
  function pickWcagRef(tags) {
2441
2541
  return tags.find((t) => /^wcag\d/.test(t));
2442
2542
  }
@@ -2553,14 +2653,18 @@ function scoreTree(root, tokens) {
2553
2653
 
2554
2654
  // src/tools/composite/scaffold_e2e.ts
2555
2655
  import { readFile } from "fs/promises";
2556
- import { resolve as resolve2 } from "path";
2656
+ import { resolve as resolve3 } from "path";
2557
2657
  var scaffoldE2eTool = {
2558
2658
  name: ToolNames.scaffoldE2e,
2559
2659
  description: "Generate a runnable e2e test file (playwright-test, vitest+playwright, or pytest+selenium) from a scenario description and optional replay bundle from a prior verify_ui_flow run.",
2560
2660
  inputShape: scaffoldE2eShape,
2561
2661
  build(ctx) {
2562
2662
  return safeHandler(async (args) => {
2563
- const { runId, runDir } = await ctx.store.startRun("scaffold");
2663
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
2664
+ const { runId, runDir, skill } = await ctx.store.startRun(
2665
+ "scaffold",
2666
+ { skill: "scaffold-e2e" }
2667
+ );
2564
2668
  const slug = slugify(args.scenario_nl);
2565
2669
  const bundle = args.recorded_bundle ? await loadReplay(args.recorded_bundle) : null;
2566
2670
  const ctxObj = { args, slug, bundle };
@@ -2598,19 +2702,35 @@ var scaffoldE2eTool = {
2598
2702
  );
2599
2703
  }
2600
2704
  const path = await ctx.store.writeReport(runDir, filename, body);
2705
+ const manifestPath = await writeManifest({
2706
+ runDir,
2707
+ skill,
2708
+ phase: "build",
2709
+ status: "pass",
2710
+ summary: `generated ${args.framework} test "${filename}" from ${bundle ? "replay bundle" : "scenario"}`,
2711
+ startedAt,
2712
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
2713
+ artifacts: [{ type: "test_file", path }],
2714
+ metadata: {
2715
+ framework: args.framework,
2716
+ language,
2717
+ from_replay_bundle: Boolean(bundle)
2718
+ }
2719
+ });
2601
2720
  return ok({
2602
2721
  run_id: runId,
2603
2722
  test_file_path: path,
2604
2723
  language,
2605
2724
  dependencies,
2606
2725
  setup_notes: setupNotes,
2607
- from_replay_bundle: Boolean(bundle)
2726
+ from_replay_bundle: Boolean(bundle),
2727
+ ...manifestPath ? { manifest: manifestPath } : {}
2608
2728
  });
2609
2729
  });
2610
2730
  }
2611
2731
  };
2612
2732
  async function loadReplay(bundlePath) {
2613
- const raw = await readFile(resolve2(bundlePath), "utf8");
2733
+ const raw = await readFile(resolve3(bundlePath), "utf8");
2614
2734
  return JSON.parse(raw);
2615
2735
  }
2616
2736
  function slugify(s) {
@@ -2934,7 +3054,11 @@ var verifyUiFlowTool = {
2934
3054
  inputShape: verifyUiFlowShape,
2935
3055
  build(ctx) {
2936
3056
  return safeHandler(async (args) => {
2937
- const { runId, runDir } = await ctx.store.startRun("verify");
3057
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
3058
+ const { runId, runDir, skill } = await ctx.store.startRun(
3059
+ "verify",
3060
+ { skill: "verify-ui" }
3061
+ );
2938
3062
  const initial = await runFlow(ctx, args, args.steps, runDir, {
2939
3063
  captureEvidence: true,
2940
3064
  bundleName: "replay.json"
@@ -2959,10 +3083,49 @@ var verifyUiFlowTool = {
2959
3083
  attempts: min.attempts
2960
3084
  };
2961
3085
  }
3086
+ const manifestPath = await writeManifest({
3087
+ runDir,
3088
+ skill,
3089
+ phase: "verify",
3090
+ status: initial.passed ? "pass" : "fail",
3091
+ summary: buildVerifySummary(args, initial),
3092
+ startedAt,
3093
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
3094
+ artifacts: flattenVerifyEvidence(initial.evidence),
3095
+ metadata: {
3096
+ mode: args.mode,
3097
+ step_count: args.steps.length,
3098
+ expect_count: args.expect.length,
3099
+ ...initial.finalUrl !== void 0 ? { final_url: initial.finalUrl } : {}
3100
+ }
3101
+ });
3102
+ if (manifestPath) result.manifest = manifestPath;
2962
3103
  return ok(result);
2963
3104
  });
2964
3105
  }
2965
3106
  };
3107
+ function buildVerifySummary(args, outcome) {
3108
+ const stepCount = args.steps.length;
3109
+ const expectCount = args.expect.length;
3110
+ if (outcome.passed) {
3111
+ return `${stepCount} step(s), ${expectCount} expect(s) passed`;
3112
+ }
3113
+ if (outcome.failedAtStep !== void 0) {
3114
+ return `failed at step ${outcome.failedAtStep}: ${outcome.failureReason ?? "unknown"}`;
3115
+ }
3116
+ return `failed: ${outcome.failureReason ?? "unknown"}`;
3117
+ }
3118
+ function flattenVerifyEvidence(ev) {
3119
+ const out = [];
3120
+ for (const s of ev.screenshots) out.push({ type: "screenshot", path: s });
3121
+ if (ev.replay_bundle) out.push({ type: "replay_bundle", path: ev.replay_bundle });
3122
+ if (ev.console) out.push({ type: "console", path: ev.console });
3123
+ if (ev.a11y_tree) out.push({ type: "a11y_tree", path: ev.a11y_tree });
3124
+ if (ev.har) out.push({ type: "har", path: ev.har });
3125
+ if (ev.trace) out.push({ type: "trace", path: ev.trace });
3126
+ if (ev.video) for (const v of ev.video) out.push({ type: "video", path: v });
3127
+ return out;
3128
+ }
2966
3129
  function buildCaptureOptions(captures, runDir) {
2967
3130
  const cap = {};
2968
3131
  if (captures.has("har")) {
@@ -3377,7 +3540,7 @@ function treeHasText(tree, text) {
3377
3540
  // src/tools/composite/visual_diff.ts
3378
3541
  import { existsSync } from "fs";
3379
3542
  import { readFile as readFile2 } from "fs/promises";
3380
- import { resolve as resolve3 } from "path";
3543
+ import { resolve as resolve4 } from "path";
3381
3544
  import pixelmatch from "pixelmatch";
3382
3545
  import { PNG } from "pngjs";
3383
3546
  var visualDiffTool = {
@@ -3386,7 +3549,11 @@ var visualDiffTool = {
3386
3549
  inputShape: visualDiffShape,
3387
3550
  build(ctx) {
3388
3551
  return safeHandler(async (args) => {
3389
- const { runId, runDir } = await ctx.store.startRun("vdiff");
3552
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
3553
+ const { runId, runDir, skill } = await ctx.store.startRun(
3554
+ "vdiff",
3555
+ { skill: "visual-diff" }
3556
+ );
3390
3557
  const session = await ctx.registry.open({
3391
3558
  ...args.open,
3392
3559
  ...args.viewport ? { viewport: args.viewport } : {}
@@ -3405,7 +3572,7 @@ var visualDiffTool = {
3405
3572
  );
3406
3573
  const currentPath = await ctx.store.writeScreenshot(runDir, buf, "current");
3407
3574
  await ctx.store.ensureDir(ctx.store.baselineDir);
3408
- const baselinePath = resolve3(
3575
+ const baselinePath = resolve4(
3409
3576
  ctx.store.baselineDir,
3410
3577
  `${args.baseline_id}.png`
3411
3578
  );
@@ -3415,6 +3582,20 @@ var visualDiffTool = {
3415
3582
  `${args.baseline_id}.png`,
3416
3583
  buf
3417
3584
  );
3585
+ const manifestPath2 = await writeManifest({
3586
+ runDir,
3587
+ skill,
3588
+ phase: "verify",
3589
+ status: "pass",
3590
+ summary: `baseline "${args.baseline_id}" seeded from current capture`,
3591
+ startedAt,
3592
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
3593
+ artifacts: [
3594
+ { type: "baseline", path: baselinePath },
3595
+ { type: "screenshot", path: currentPath }
3596
+ ],
3597
+ metadata: { baseline_id: args.baseline_id, seeded: true }
3598
+ });
3418
3599
  return ok({
3419
3600
  run_id: runId,
3420
3601
  baseline_id: args.baseline_id,
@@ -3422,6 +3603,7 @@ var visualDiffTool = {
3422
3603
  passed: true,
3423
3604
  baseline_path: baselinePath,
3424
3605
  current_path: currentPath,
3606
+ ...manifestPath2 ? { manifest: manifestPath2 } : {},
3425
3607
  note: "Baseline did not exist \u2014 current capture saved as the new baseline."
3426
3608
  });
3427
3609
  }
@@ -3452,21 +3634,45 @@ var visualDiffTool = {
3452
3634
  );
3453
3635
  const total = baseline.width * baseline.height;
3454
3636
  const diffPct = diffPixels / total;
3637
+ const passed = diffPct <= args.threshold_pct;
3455
3638
  const diffImagePath = await ctx.store.writeBytes(
3456
3639
  runDir,
3457
3640
  "diff.png",
3458
3641
  PNG.sync.write(diff)
3459
3642
  );
3643
+ const artifacts = [
3644
+ { type: "baseline", path: baselinePath },
3645
+ { type: "screenshot", path: currentPath },
3646
+ { type: "diff", path: diffImagePath }
3647
+ ];
3648
+ const manifestPath = await writeManifest({
3649
+ runDir,
3650
+ skill,
3651
+ phase: "verify",
3652
+ status: passed ? "pass" : "fail",
3653
+ summary: `diff ${(diffPct * 100).toFixed(3)}% vs baseline "${args.baseline_id}" (threshold ${(args.threshold_pct * 100).toFixed(3)}%)`,
3654
+ startedAt,
3655
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
3656
+ artifacts,
3657
+ metadata: {
3658
+ baseline_id: args.baseline_id,
3659
+ diff_pct: Number(diffPct.toFixed(6)),
3660
+ diff_pixels: diffPixels,
3661
+ total_pixels: total,
3662
+ threshold_pct: args.threshold_pct
3663
+ }
3664
+ });
3460
3665
  return ok({
3461
3666
  run_id: runId,
3462
3667
  baseline_id: args.baseline_id,
3463
3668
  diff_pct: Number(diffPct.toFixed(6)),
3464
3669
  diff_pixels: diffPixels,
3465
3670
  total_pixels: total,
3466
- passed: diffPct <= args.threshold_pct,
3671
+ passed,
3467
3672
  baseline_path: baselinePath,
3468
3673
  current_path: currentPath,
3469
- diff_image_path: diffImagePath
3674
+ diff_image_path: diffImagePath,
3675
+ ...manifestPath ? { manifest: manifestPath } : {}
3470
3676
  });
3471
3677
  } finally {
3472
3678
  if (args.close_on_finish) {
@@ -3735,8 +3941,19 @@ var toolMetadata = {
3735
3941
 
3736
3942
  // src/server.ts
3737
3943
  var SERVER_NAME = "rolepod-uiproof";
3738
- var SERVER_VERSION = "0.5.0";
3944
+ var SERVER_VERSION = "0.6.0";
3945
+ var SUPPORTED_PROTOCOL = "v1";
3946
+ function checkProtocolCompat() {
3947
+ const requested = process.env.ROLEPOD_PROTOCOL;
3948
+ if (!requested) return;
3949
+ if (requested !== SUPPORTED_PROTOCOL) {
3950
+ console.warn(
3951
+ `rolepod protocol mismatch: expected ${SUPPORTED_PROTOCOL}, got ${requested}. Manifest will still be written in ${SUPPORTED_PROTOCOL} shape \u2014 parent may not parse it correctly.`
3952
+ );
3953
+ }
3954
+ }
3739
3955
  function buildServer(opts = {}) {
3956
+ checkProtocolCompat();
3740
3957
  const webEngine = createWebEngine();
3741
3958
  const registry = new SessionRegistry({ idleTimeoutMs: opts.idleTimeoutMs });
3742
3959
  registry.register("web", webEngine);
@@ -3797,6 +4014,8 @@ function buildServer(opts = {}) {
3797
4014
  }
3798
4015
  log.info("rolepod-uiproof server built", {
3799
4016
  version: SERVER_VERSION,
4017
+ protocol: SUPPORTED_PROTOCOL,
4018
+ mode: store.mode,
3800
4019
  tools: tools.map((t) => t.name)
3801
4020
  });
3802
4021
  return {