waygraph 0.15.3 → 0.15.4

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.
@@ -1,4 +1,6 @@
1
1
  import { AutoSession, type AutoSessionInit, type SessionSnapshot, type InspectDomOptions, type DomSnapshot, type TraceStep, type ApplyPathResult, type ConsoleLogEntry, type StorageSnapshot, type StubFileKind } from "./auto-session.js";
2
+ import type { PilotHighlightFixtures } from "./pilot-overlay.js";
3
+ export type { PilotHighlightFixtures, PilotFixtureRing } from "./pilot-overlay.js";
2
4
  export interface SessionMeta {
3
5
  sessionId: string;
4
6
  socketPath: string;
@@ -43,6 +45,15 @@ export type StorageResponse = ({
43
45
  error: string;
44
46
  };
45
47
  export type ReachResponse = ApplyPathResult;
48
+ export type HighlightResponse = {
49
+ ok: true;
50
+ painted: number;
51
+ missing: string[];
52
+ snapshot: SessionSnapshot;
53
+ } | {
54
+ ok: false;
55
+ error: string;
56
+ };
46
57
  /**
47
58
  * Client-side: sends one request to a running session and returns its
48
59
  * response. Reports a stale/unreachable session as an error within
@@ -94,6 +105,9 @@ export declare function requestSession(projectDir: string, sessionId: string, re
94
105
  op: "reach";
95
106
  checkpoint: string;
96
107
  }, timeoutMs?: number): Promise<ReachResponse>;
108
+ export declare function requestSession(projectDir: string, sessionId: string, request: {
109
+ op: "highlight";
110
+ } & PilotHighlightFixtures, timeoutMs?: number): Promise<HighlightResponse>;
97
111
  /**
98
112
  * Server-side: runs `session` behind a unix socket at
99
113
  * `.waygraph-auto/<sessionId>.sock`, writing session metadata only once the
@@ -235,6 +235,10 @@ export async function serveSession(session, projectDir, sessionId, headless = tr
235
235
  else if (request.op === "resync") {
236
236
  response = await session.resync();
237
237
  }
238
+ else if (request.op === "highlight") {
239
+ const { op: _op, ...fixtures } = request;
240
+ response = await session.applyHighlight(fixtures);
241
+ }
238
242
  else {
239
243
  response = { ok: false, error: `unknown op "${request.op}"` };
240
244
  }
@@ -1,6 +1,7 @@
1
1
  import { type BlockEntry, type ExploreMenu } from "./auto-explore.js";
2
2
  import { type PickResult } from "./auto-explore-run.js";
3
3
  import { type StubPhaseResult } from "./highlights.js";
4
+ import type { PilotHighlightFixtures } from "./pilot-overlay.js";
4
5
  export interface SessionSnapshotEdge {
5
6
  /** 1-based, matching the numbers a human/agent picks by (same as printCliMenu). */
6
7
  index: number;
@@ -299,6 +300,20 @@ export declare class AutoSession {
299
300
  rawUpload(selector: string, stub: StubFileKind | {
300
301
  filePath: string;
301
302
  }): Promise<ApplyPickResult>;
303
+ /**
304
+ * Agent-sent highlight fixtures (rings + optional todos) on the live page -
305
+ * the Pilot equivalent of Block `stubBefore` narration in `waygraph demo`.
306
+ * Does not run a Block; paint-only. Missing selectors are reported, not fatal.
307
+ */
308
+ applyHighlight(fixtures: PilotHighlightFixtures): Promise<{
309
+ ok: true;
310
+ painted: number;
311
+ missing: string[];
312
+ snapshot: SessionSnapshot;
313
+ } | {
314
+ ok: false;
315
+ error: string;
316
+ }>;
302
317
  /**
303
318
  * Runs a whole multi-step route to `targetCheckpoint` in one call, instead
304
319
  * of an agent hand-picking one index at a time across many separate
@@ -5,7 +5,7 @@ import { buildExploreContext, buildExploreMenu, } from "./auto-explore.js";
5
5
  import { ensureLivePage, detectHere, seedDefaultMem, ensureMem, runOneBlock, resolveBaseUrl, } from "./auto-explore-run.js";
6
6
  import { runStubPhase } from "./highlights.js";
7
7
  import { findBlockPathDetailed } from "./graph.js";
8
- import { updatePilotOverlay, showPilotActivity, showPilotVision } from "./pilot-overlay.js";
8
+ import { updatePilotOverlay, showPilotActivity, showPilotVision, showPilotFixtures } from "./pilot-overlay.js";
9
9
  /** True when a stub-phase result actually carries authored content worth keeping. */
10
10
  function stubPhaseHasContent(r) {
11
11
  return (Object.keys(r.highlights).length > 0 ||
@@ -670,6 +670,39 @@ export class AutoSession {
670
670
  quit: false,
671
671
  };
672
672
  }
673
+ /**
674
+ * Agent-sent highlight fixtures (rings + optional todos) on the live page -
675
+ * the Pilot equivalent of Block `stubBefore` narration in `waygraph demo`.
676
+ * Does not run a Block; paint-only. Missing selectors are reported, not fatal.
677
+ */
678
+ async applyHighlight(fixtures) {
679
+ this.page = await ensureLivePage(this.context, this.page, this.startUrl);
680
+ const label = fixtures.clear === true
681
+ ? "clear fixtures"
682
+ : `${fixtures.rings?.length ?? 0} ring(s)` +
683
+ (fixtures.todos?.length ? `, ${fixtures.todos.length} todo(s)` : "");
684
+ await showPilotActivity(this.page, `Highlight: ${label}`);
685
+ try {
686
+ const { painted, missing } = await showPilotFixtures(this.page, fixtures);
687
+ const menu = await this.currentMenu();
688
+ this.lastRunNote = fixtures.clear
689
+ ? "highlight cleared"
690
+ : `highlight painted ${painted}` +
691
+ (missing.length ? ` (missing: ${missing.join(", ")})` : "");
692
+ return {
693
+ ok: true,
694
+ painted,
695
+ missing,
696
+ snapshot: buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote),
697
+ };
698
+ }
699
+ catch (err) {
700
+ return {
701
+ ok: false,
702
+ error: `highlight failed - ${err instanceof Error ? err.message : String(err)}`,
703
+ };
704
+ }
705
+ }
673
706
  /**
674
707
  * Runs a whole multi-step route to `targetCheckpoint` in one call, instead
675
708
  * of an agent hand-picking one index at a time across many separate
package/dist/cli.js CHANGED
@@ -7006,6 +7006,13 @@ Primary (less is more):
7006
7006
  --non-headless session someone is co-driving);
7007
7007
  send/reach never do this on their own, since they
7008
7008
  only re-detect when the position is already unknown
7009
+ waygraph auto highlight <sessionId> '<json>'
7010
+ Paint agent highlight fixtures on the live page
7011
+ (rings + optional todos). Same visual language as
7012
+ Block stubBefore in demo. JSON: rings[{selector,
7013
+ label,tone?,size?,weight?}], todos[], todoIndex?,
7014
+ todoTitle?, holdMs? (0=until next), clear:true.
7015
+ Missing selectors listed in response, not fatal.
7009
7016
  waygraph pilot start Bootstrap for an agent: starts a --detach session
7010
7017
  (same as auto --cli --detach) AND reads back the
7011
7018
  whole project's Block graph (same as waygraph
@@ -7468,7 +7475,8 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
7468
7475
  args[1] === "goto" ||
7469
7476
  args[1] === "reload" ||
7470
7477
  args[1] === "reach" ||
7471
- args[1] === "resync")) {
7478
+ args[1] === "resync" ||
7479
+ args[1] === "highlight")) {
7472
7480
  const sub = args[1];
7473
7481
  const sessionId = args[2];
7474
7482
  if (!sessionId) {
@@ -7670,6 +7678,34 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
7670
7678
  process.exitCode = 1;
7671
7679
  break;
7672
7680
  }
7681
+ if (sub === "highlight") {
7682
+ const raw = args[3];
7683
+ if (raw === undefined) {
7684
+ console.error('waygraph auto highlight: usage: waygraph auto highlight <sessionId> \'{"rings":[{"selector":"#x","label":"X"}]}\'');
7685
+ process.exit(1);
7686
+ }
7687
+ let fixtures;
7688
+ try {
7689
+ fixtures = JSON.parse(raw);
7690
+ }
7691
+ catch {
7692
+ console.error("waygraph auto highlight: body must be valid JSON");
7693
+ process.exit(1);
7694
+ }
7695
+ if (fixtures.op !== undefined && fixtures.op !== "highlight") {
7696
+ console.error('waygraph auto highlight: do not set "op" (or set it to "highlight")');
7697
+ process.exit(1);
7698
+ }
7699
+ const { op: _ignore, ...rest } = fixtures;
7700
+ const res = await requestSession(proj, sessionId, {
7701
+ op: "highlight",
7702
+ ...rest,
7703
+ });
7704
+ console.log(JSON.stringify(res));
7705
+ if (!res.ok)
7706
+ process.exitCode = 1;
7707
+ break;
7708
+ }
7673
7709
  const res = await requestSession(proj, sessionId, { op: "status" });
7674
7710
  console.log(JSON.stringify(res));
7675
7711
  if (!res.ok)
@@ -52,8 +52,50 @@ export declare function showPilotActivity(page: Page, text: string): Promise<voi
52
52
  * raw click/type/upload IS a real Playwright-driven action (`auto` was
53
53
  * always documented as meaning exactly that), while a pure `auto dom` read
54
54
  * touches nothing, so it gets its own "orange" tone instead.
55
+ *
56
+ * For agent-authored multi-ring / todo narration, use {@link showPilotFixtures}
57
+ * (`auto highlight`) instead - this vision ring is one-shot inspect feedback.
55
58
  */
56
59
  export declare function showPilotVision(page: Page, selector: string, label: string, tone?: HighlightTone): Promise<void>;
60
+ /** One ring the Pilot agent wants painted (same fields as demo stub rings). */
61
+ export type PilotFixtureRing = {
62
+ selector: string;
63
+ label: string;
64
+ tone?: HighlightTone;
65
+ size?: "sm" | "md" | "lg";
66
+ weight?: "normal" | "bold";
67
+ };
68
+ /**
69
+ * Agent-sent highlight fixtures for a live Pilot / auto session.
70
+ * Painted by {@link showPilotFixtures}; driven via IPC `op: "highlight"`.
71
+ */
72
+ export type PilotHighlightFixtures = {
73
+ /** Replace current fixture rings with these (empty + no todos = clear). */
74
+ rings?: readonly PilotFixtureRing[];
75
+ /** Optional floating todo list (demo-dock language, simplified). */
76
+ todos?: readonly string[];
77
+ /** 0-based index of the "current" todo row (others before it = done). */
78
+ todoIndex?: number;
79
+ /** Optional title above the todo list. */
80
+ todoTitle?: string;
81
+ /**
82
+ * How long fixtures stay visible (ms). `0` = until the next highlight /
83
+ * clear. Default `12000`. Vision rings (`showPilotVision`) stay separate.
84
+ */
85
+ holdMs?: number;
86
+ /** Drop every agent fixture ring/todo immediately. */
87
+ clear?: boolean;
88
+ };
89
+ /**
90
+ * Paint (or clear) agent-authored highlight fixtures on the live page.
91
+ * Multi-ring + optional todo strip - what Block `stubBefore` does in
92
+ * `waygraph demo`, available to the Pilot agent without going through demo.
93
+ * Does not import `cli.ts` (see file header).
94
+ */
95
+ export declare function showPilotFixtures(page: Page, fixtures: PilotHighlightFixtures): Promise<{
96
+ painted: number;
97
+ missing: string[];
98
+ }>;
57
99
  /** Minimal shape needed to render the graph tree - matches WaygraphGraph. */
58
100
  export interface PilotOverlayGraph {
59
101
  nodes: readonly {
@@ -60,6 +60,44 @@ const OVERLAY_CSS = `
60
60
  opacity: 0; transform: translateY(4px); transition: opacity 0.15s, transform 0.15s;
61
61
  }
62
62
  #wg-pilot-activity.wg-pilot-toast-show { opacity: 1; transform: translateY(0); }
63
+ /* Agent-sent fixture rings share tone/size CSS with #wg-ring (demo language). */
64
+ .wg-pilot-fx-ring{position:fixed;z-index:2147483645;pointer-events:none;opacity:0;
65
+ border:3px solid #7C3AED;border-radius:10px;box-sizing:border-box;
66
+ transition:opacity .12s ease,left .12s,top .12s,width .12s,height .12s;}
67
+ .wg-pilot-fx-ring[data-tone=planned]{border-color:#7C3AED;box-shadow:0 0 0 4px rgba(124,58,237,.16);}
68
+ .wg-pilot-fx-ring[data-tone=auto]{border-color:#9CA3AF;box-shadow:0 0 0 4px rgba(156,163,175,.28);}
69
+ .wg-pilot-fx-ring[data-tone=info]{border-color:#3B82F6;box-shadow:0 0 0 4px rgba(59,130,246,.22);}
70
+ .wg-pilot-fx-ring[data-tone=warning]{border-color:#EAB308;box-shadow:0 0 0 4px rgba(234,179,8,.22);}
71
+ .wg-pilot-fx-ring[data-tone=danger]{border-color:#EF4444;box-shadow:0 0 0 4px rgba(239,68,68,.22);}
72
+ .wg-pilot-fx-ring[data-tone=success]{border-color:#22C55E;box-shadow:0 0 0 4px rgba(34,197,94,.22);}
73
+ .wg-pilot-fx-ring[data-tone=orange]{border-color:#F97316;box-shadow:0 0 0 4px rgba(249,115,22,.22);}
74
+ .wg-pilot-fx-label{position:fixed;z-index:2147483645;pointer-events:none;opacity:0;
75
+ font:600 12px/1.3 ui-sans-serif,system-ui,sans-serif;padding:5px 9px;border-radius:6px;
76
+ max-width:min(360px,80vw);box-shadow:0 2px 8px rgba(0,0,0,.25);white-space:nowrap;
77
+ overflow:hidden;text-overflow:ellipsis;transition:opacity .12s ease;}
78
+ .wg-pilot-fx-label[data-tone=planned]{background:#7C3AED;color:#fff;}
79
+ .wg-pilot-fx-label[data-tone=auto]{background:#6B7280;color:#fff;}
80
+ .wg-pilot-fx-label[data-tone=info]{background:#2563EB;color:#fff;}
81
+ .wg-pilot-fx-label[data-tone=warning]{background:#EAB308;color:#1c1917;}
82
+ .wg-pilot-fx-label[data-tone=danger]{background:#DC2626;color:#fff;}
83
+ .wg-pilot-fx-label[data-tone=success]{background:#16A34A;color:#fff;}
84
+ .wg-pilot-fx-label[data-tone=orange]{background:#EA580C;color:#fff;}
85
+ .wg-pilot-fx-ring[data-size=sm]{border-width:1.5px;border-radius:8px;}
86
+ .wg-pilot-fx-ring[data-size=lg]{border-width:4px;border-radius:12px;}
87
+ .wg-pilot-fx-label[data-size=sm]{font-size:10px;padding:4px 7px;}
88
+ .wg-pilot-fx-label[data-size=lg]{font-size:16px;padding:8px 14px;}
89
+ .wg-pilot-fx-label[data-weight=bold]{font-weight:800;}
90
+ #wg-pilot-fx-todos{
91
+ position:fixed;z-index:2147483645;top:72px;left:14px;max-width:280px;
92
+ font:12px/1.4 ui-sans-serif,system-ui,sans-serif;color:#fff;
93
+ background:rgba(20,10,40,.94);border-radius:10px;padding:10px 12px;
94
+ box-shadow:0 2px 10px rgba(0,0,0,.3);pointer-events:none;
95
+ }
96
+ #wg-pilot-fx-todos .wg-pilot-fx-todo-title{font-weight:700;color:#c9a6ff;margin-bottom:6px;}
97
+ #wg-pilot-fx-todos ol{margin:0;padding-left:18px;}
98
+ #wg-pilot-fx-todos li{margin:3px 0;color:#ddd;}
99
+ #wg-pilot-fx-todos li.wg-pilot-fx-todo-done{color:#888;text-decoration:line-through;}
100
+ #wg-pilot-fx-todos li.wg-pilot-fx-todo-now{color:#fff;font-weight:700;}
63
101
  ` + WAYGRAPH_RING_CSS;
64
102
  /** Idempotent - safe to call before every update, matching narrate mode's own precedent. */
65
103
  async function ensureInstalled(page) {
@@ -159,6 +197,9 @@ export async function showPilotActivity(page, text) {
159
197
  * raw click/type/upload IS a real Playwright-driven action (`auto` was
160
198
  * always documented as meaning exactly that), while a pure `auto dom` read
161
199
  * touches nothing, so it gets its own "orange" tone instead.
200
+ *
201
+ * For agent-authored multi-ring / todo narration, use {@link showPilotFixtures}
202
+ * (`auto highlight`) instead - this vision ring is one-shot inspect feedback.
162
203
  */
163
204
  export async function showPilotVision(page, selector, label, tone = "auto") {
164
205
  await ensureInstalled(page);
@@ -211,6 +252,113 @@ export async function showPilotVision(page, selector, label, tone = "auto") {
211
252
  }, { selector, label, tone })
212
253
  .catch(() => { });
213
254
  }
255
+ /**
256
+ * Paint (or clear) agent-authored highlight fixtures on the live page.
257
+ * Multi-ring + optional todo strip - what Block `stubBefore` does in
258
+ * `waygraph demo`, available to the Pilot agent without going through demo.
259
+ * Does not import `cli.ts` (see file header).
260
+ */
261
+ export async function showPilotFixtures(page, fixtures) {
262
+ await ensureInstalled(page);
263
+ const holdMs = fixtures.clear === true
264
+ ? 0
265
+ : fixtures.holdMs === undefined
266
+ ? 12_000
267
+ : Math.max(0, fixtures.holdMs);
268
+ const rings = fixtures.clear ? [] : (fixtures.rings ?? []);
269
+ const todos = fixtures.clear ? [] : (fixtures.todos ?? []);
270
+ const todoIndex = fixtures.todoIndex ?? 0;
271
+ const todoTitle = fixtures.todoTitle ?? "Plan";
272
+ return page
273
+ .evaluate(({ rings, todos, todoIndex, todoTitle, holdMs }) => {
274
+ const w = window;
275
+ if (w.__wgPilotFxHideTimer)
276
+ clearTimeout(w.__wgPilotFxHideTimer);
277
+ const clearFx = () => {
278
+ document.querySelectorAll(".wg-pilot-fx-ring, .wg-pilot-fx-label").forEach((el) => el.remove());
279
+ document.getElementById("wg-pilot-fx-todos")?.remove();
280
+ };
281
+ w.__wgPilotFxClear = clearFx;
282
+ clearFx();
283
+ const missing = [];
284
+ let painted = 0;
285
+ for (const ring of rings) {
286
+ const el = document.querySelector(ring.selector);
287
+ if (!el) {
288
+ missing.push(ring.selector);
289
+ continue;
290
+ }
291
+ const rect = el.getBoundingClientRect();
292
+ if (rect.width === 0 && rect.height === 0) {
293
+ missing.push(ring.selector);
294
+ continue;
295
+ }
296
+ const tone = ring.tone || "planned";
297
+ const size = ring.size === "sm" || ring.size === "lg" ? ring.size : "md";
298
+ const weight = ring.weight === "bold" ? "bold" : "normal";
299
+ const pad = size === "sm" ? 3 : size === "lg" ? 10 : 6;
300
+ const ringEl = document.createElement("div");
301
+ ringEl.className = "wg-pilot-fx-ring";
302
+ ringEl.dataset.tone = tone;
303
+ ringEl.dataset.size = size;
304
+ ringEl.style.left = `${rect.left - pad}px`;
305
+ ringEl.style.top = `${rect.top - pad}px`;
306
+ ringEl.style.width = `${Math.max(4, rect.width + pad * 2)}px`;
307
+ ringEl.style.height = `${Math.max(4, rect.height + pad * 2)}px`;
308
+ ringEl.style.opacity = "1";
309
+ const labelEl = document.createElement("div");
310
+ labelEl.className = "wg-pilot-fx-label";
311
+ labelEl.dataset.tone = tone;
312
+ labelEl.dataset.size = size;
313
+ labelEl.dataset.weight = weight;
314
+ labelEl.textContent = ring.label;
315
+ labelEl.style.opacity = "1";
316
+ document.documentElement.appendChild(ringEl);
317
+ document.documentElement.appendChild(labelEl);
318
+ const lw = labelEl.offsetWidth;
319
+ const lh = labelEl.offsetHeight;
320
+ let labelLeft = rect.left - pad;
321
+ let labelTop = rect.top - pad + rect.height + pad * 2 + 8;
322
+ if (labelTop + lh > window.innerHeight - 6)
323
+ labelTop = rect.top - pad - lh - 8;
324
+ if (labelTop < 6)
325
+ labelTop = 6;
326
+ if (labelLeft + lw > window.innerWidth - 6) {
327
+ labelLeft = Math.max(6, window.innerWidth - 6 - lw);
328
+ }
329
+ if (labelLeft < 6)
330
+ labelLeft = 6;
331
+ labelEl.style.left = `${labelLeft}px`;
332
+ labelEl.style.top = `${labelTop}px`;
333
+ painted += 1;
334
+ }
335
+ if (todos.length > 0) {
336
+ const dock = document.createElement("div");
337
+ dock.id = "wg-pilot-fx-todos";
338
+ const title = document.createElement("div");
339
+ title.className = "wg-pilot-fx-todo-title";
340
+ title.textContent = todoTitle;
341
+ dock.appendChild(title);
342
+ const ol = document.createElement("ol");
343
+ todos.forEach((text, i) => {
344
+ const li = document.createElement("li");
345
+ li.textContent = text;
346
+ if (i < todoIndex)
347
+ li.className = "wg-pilot-fx-todo-done";
348
+ else if (i === todoIndex)
349
+ li.className = "wg-pilot-fx-todo-now";
350
+ ol.appendChild(li);
351
+ });
352
+ dock.appendChild(ol);
353
+ document.documentElement.appendChild(dock);
354
+ }
355
+ if (holdMs > 0 && (painted > 0 || todos.length > 0)) {
356
+ w.__wgPilotFxHideTimer = setTimeout(() => clearFx(), holdMs);
357
+ }
358
+ return { painted, missing };
359
+ }, { rings, todos, todoIndex, todoTitle, holdMs })
360
+ .catch(() => ({ painted: 0, missing: rings.map((r) => r.selector) }));
361
+ }
214
362
  /**
215
363
  * Refreshes the badge text and the (possibly-collapsed) panel's two tabs
216
364
  * ("Here" - current live menu; "All nodes" - whole project graph as a
File without changes
File without changes
@@ -0,0 +1,4 @@
1
+ (node:218615) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
2
+ (Use `node --trace-warnings ...` to show where the warning was created)
3
+ (node:218615) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
4
+ (Use `node --trace-warnings ...` to show where the warning was created)
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.15.3",
3
+ "version": "0.15.4",
4
4
  "type": "module",
5
5
  "description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
6
6
  "license": "MIT",
@@ -25,7 +25,8 @@ permanent, reusable, typed coverage instead of leaving nothing behind.
25
25
  (`(group)/<page-slug>/page.block.ts` + `nav.block.ts` + `methods/*.block.ts` + one `*.sel.ts`)
26
26
  so the result becomes part of the Waygraph Map, not a scattered one-off file.
27
27
  4. `auto reload <sessionId>` to pick up the newly-written Block(s) without restarting.
28
- 5. Run the new Block for real via `auto send` - prove it works before moving to the next
28
+ 5. Optionally narrate for a headful watcher: `auto highlight <sessionId> '{"rings":[...],"todos":[...]}'`
29
+ 6. Run the new Block for real via `auto send` - prove it works before moving to the next
29
30
  pattern, exactly like any other real Block.
30
31
  6. Ask the user a clarifying question only when something genuinely can't be resolved from
31
32
  the DOM alone (is this instance local or published? does a downstream integration exist?)
@@ -16,12 +16,29 @@ waygraph pilot start # bootstraps: {sessionId, socketPath,
16
16
  waygraph auto status <sessionId> # re-read the live menu, no side effects
17
17
  waygraph auto send <sessionId> "<pick>" # run exactly one Block (by menu index or name)
18
18
  waygraph auto reach <sessionId> <Checkpoint> # path-find + run a whole route in one call
19
+ waygraph auto highlight <sessionId> '<json>' # paint rings/todos on the live page (agent fixtures)
19
20
  ```
20
21
 
21
22
  `graph` in `pilot start`'s response is the **whole project's** Block/Checkpoint graph, not
22
23
  just what's reachable from here - use it to plan several steps ahead before you start
23
24
  sending picks.
24
25
 
26
+ ## Highlight fixtures (agent-sent narration)
27
+
28
+ Block `stubBefore` / `withHighlightFixtures` only paint in `waygraph demo`. In Pilot they
29
+ land in `auto trace` as metadata but do **not** draw on the page. When a human is watching
30
+ a headful session (or you want to narrate intent before a send), paint yourself:
31
+
32
+ ```
33
+ waygraph auto highlight <sessionId> '{"rings":[{"selector":"#user-name","label":"Username","tone":"planned"},{"selector":"#password","label":"Password","tone":"info"}],"todos":["Fill username","Fill password","Submit"],"todoIndex":0,"holdMs":0}'
34
+ waygraph auto highlight <sessionId> '{"clear":true}'
35
+ ```
36
+
37
+ - `tone`: `planned` (purple, default) | `auto` | `info` | `warning` | `danger` | `success` (pilot `orange` is for `auto dom` vision only)
38
+ - `holdMs`: ms to keep fixtures; `0` = until the next highlight / clear (default 12000)
39
+ - Missing selectors are listed in the JSON response (`missing`) - not a hard failure
40
+ - Does not replace `auto send` - paint, then run the real Block
41
+
25
42
  ## Rule 1: run real Blocks, not raw primitives
26
43
 
27
44
  Default action is always `auto send`/`auto reach` (a real Block). `auto dom/click/type/
@@ -1,8 +0,0 @@
1
- {
2
- "sessionId": "0b1f5b26",
3
- "socketPath": "/tmp/waygraph-auto/0b1f5b26.sock",
4
- "pid": 1136773,
5
- "projectDir": "/home/dan/Desktop/Work/zsign/services/waygraph/examples/saucedemo",
6
- "startedAt": "2026-09-20T18:04:00.000Z",
7
- "headless": false
8
- }