foundry-design-web-adapter 0.2.0-beta.5 → 0.2.0-beta.7

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/README.md CHANGED
@@ -6,30 +6,40 @@ Foundry is a local-first precision design workbench for Codex, Cursor, and Claud
6
6
 
7
7
  Foundry is distributed through npm, so testers do not need GitHub access.
8
8
 
9
- ### Recommended: let your coding agent install it
9
+ > **Current public beta:** `0.2.0-beta.7`. Install Foundry with the `@beta` tag. The unqualified npm `latest` tag still points to an earlier beta and is not the current testing channel.
10
10
 
11
- Open the project in Codex, Cursor, or Claude Code and paste:
11
+ Full documentation is available at [withfoundry.ai](https://withfoundry.ai).
12
12
 
13
- ```text
14
- Install Foundry for this project by running npx foundry-design@beta setup --yes. Check the result and tell me when I need to restart.
13
+ ### Recommended: one command
14
+
15
+ Open a terminal in the project, or ask Codex, Cursor, or Claude Code to run:
16
+
17
+ ```bash
18
+ npx foundry-design@beta
15
19
  ```
16
20
 
17
- Setup detects the project and active coding agent, installs development-only instrumentation, configures the local agent connection, validates the integration, and records everything it owns for safe updates or removal.
21
+ That command detects the project and active coding agent, installs or safely updates Foundry, repairs the shared agent connection, validates its integration, starts the project when needed, and opens the visual session. On the first installation only, restart the coding agent once so it can load the shared MCP connection. A reviewed batch can be queued before the restart and will be claimed when the agent reconnects.
18
22
 
19
- Restart the coding agent once, reopen the same project folder, and paste:
23
+ After the restart, reopen the same project folder and ask:
20
24
 
21
25
  ```text
22
26
  Start Foundry for this project and keep listening for Apply with agent requests.
23
27
  ```
24
28
 
25
- That is the normal workflow. Foundry starts the project when needed, opens an authenticated local preview, and shows **Agent is ready** before it allows an apply request.
29
+ That is the normal workflow. Foundry resumes the most recent session for the current source revision and keeps all review and apply state local.
26
30
 
27
31
  ### Manual installation
28
32
 
29
- From the project you want to inspect, run:
33
+ From the first project you want to inspect, run:
34
+
35
+ ```bash
36
+ npx foundry-design@beta setup --global
37
+ ```
38
+
39
+ For later projects, keep the shared agent connection and install only the development adapter:
30
40
 
31
41
  ```bash
32
- npx foundry-design@beta setup
42
+ npx foundry-design@beta setup --agent none
33
43
  ```
34
44
 
35
45
  Then restart the coding agent, reopen the same project folder, and ask:
@@ -38,7 +48,7 @@ Then restart the coding agent, reopen the same project folder, and ask:
38
48
  Start Foundry for this project and keep listening for Apply with agent requests.
39
49
  ```
40
50
 
41
- You do not need to run a second terminal command when the agent starts Foundry for you. For a manual-only preview, run `npx foundry-design@beta start`, but Apply with agent still requires a restarted and actively listening coding agent.
51
+ You do not need to run a second terminal command when the agent starts Foundry for you. Apply requests remain queued safely when the agent is offline. Run `npx foundry-design@beta doctor --repair` if the connection or generated integration needs repair.
42
52
 
43
53
  Update an existing installation with:
44
54
 
@@ -56,13 +66,13 @@ npx foundry-design@beta uninstall
56
66
 
57
67
  Supported automatic web integration currently includes Next.js App Router, Vite, and plain HTML. Generic web, SwiftUI, and React Native projects receive explicit setup guidance when a safe automatic edit is not available.
58
68
 
59
- The repository also contains a portable plugin bundle for future marketplace distribution. The npm setup above is the public beta installation path.
69
+ The npm setup above is the public beta installation path and includes the same portable skill and MCP connection used by the agent plugin bundle.
60
70
 
61
- The beta supports Node.js 20 or newer. Read [Privacy](PRIVACY.md), [Security](SECURITY.md), and the [beta changelog](CHANGELOG.md) before using it with sensitive work.
71
+ The beta supports Node.js 20 or newer. Read the [local-first safety model](https://withfoundry.ai/#safety) before using it with sensitive work.
62
72
 
63
73
  ## Agent plugin
64
74
 
65
- The portable plugin lives at `plugins/foundry-design-control`. The repository also includes marketplace manifests for Codex, Cursor, and Claude-compatible plugin import. Once installed, ask your agent:
75
+ The portable plugin lives at `plugins/foundry-design-control`. The repository also includes marketplace manifests for Codex, Cursor, and Claude-compatible plugin import. Until public marketplace review is complete, `setup --global` installs the same reusable skill and MCP connection from npm without requiring source-repository access. Once installed, ask your agent:
66
76
 
67
77
  ```text
68
78
  Start Foundry for this project.
@@ -86,7 +96,9 @@ Foundry remains local-first. Installing the plugin does not create an account, e
86
96
  - Persistent, coalescing change ledger with JSON and consolidated prompt export
87
97
  - Compact in-preview review with editable approved batches and unresolved-target blocking
88
98
  - Persistent Apply with agent runs across Codex, Cursor, and Claude Code through MCP
99
+ - Leased agent handoffs that safely return abandoned or interrupted claims to the queue
89
100
  - Live source, rebuild, validation, retry, and rendered-verification progress
101
+ - Rendered verification that resumes after refresh and does not depend on Review remaining open
90
102
  - Local MCP bridge for agent access
91
103
  - Debug adapters for web, SwiftUI, and React Native on iOS Simulator
92
104
  - Verification records that compare requested and rendered values
package/dist/adapter.js CHANGED
@@ -1216,7 +1216,7 @@ var PANEL_CSS = `
1216
1216
  .review-head strong { font-size:12px; }.review-summary { padding:12px 16px 12px;border-bottom:1px solid var(--fdc-line);background:#fcfcfc; }.review-summary strong { display:block;font-size:12px;font-weight:550;letter-spacing:-.01em; }.review-summary span { display:block;margin-top:4px;color:var(--fdc-muted);font-size:12px;line-height:1.45; }.review-summary.attention strong { color:#8b4d3d; }.review-toolbar button { height:28px;padding:0 8px;font-size:12px; }
1217
1217
  .onboarding-card { bottom:84px;width:340px;padding:16px;border-radius:12px;box-shadow:0 16px 40px rgb(0 0 0 / 15%); }.onboarding-card-head { gap:8px; }.onboarding-card-head span { width:28px;height:28px;display:grid;place-items:center;color:#a75031;background:#fbf1ed;border-radius:8px; }.onboarding-card-head svg { width:16px;height:16px;color:inherit; }.onboarding-card-head strong { font-size:12px;letter-spacing:-.015em; }.onboarding-card>p { margin:8px 0 16px;font-size:12px;line-height:1.55; }.onboarding-steps { display:grid;grid-template-columns:1fr;gap:1px;border:1px solid var(--fdc-line);border-radius:8px;overflow:hidden;background:var(--fdc-line); }.onboarding-step { display:grid;grid-template-columns:24px 1fr;gap:8px;padding:8px 12px;background:white;text-align:left; }.onboarding-step b { color:#8a8a8a;font-size:8px;font-weight:500;letter-spacing:.04em; }.onboarding-step strong { display:block;font-size:12px;font-weight:550; }.onboarding-step small { display:block;margin-top:4px;color:var(--fdc-muted);font-size:8px;line-height:1.35; }.onboarding-actions { gap:8px;margin-top:12px; }.onboarding-actions button { height:32px;padding:0 12px;border-radius:8px;font-size:12px; }
1218
1218
  .onboarding-card,.onboarding-step { color:var(--fdc-ink);background:var(--fdc-surface); }.onboarding-card-head span { color:#e7a68c;background:#36231d; }.onboarding-actions button { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }.onboarding-actions .onboarding-start { color:#141416;background:#f0f1f3;border-color:#f0f1f3; }
1219
- .status-popover { width:256px; }.status-popover-actions { display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-top:8px; }.status-popover .status-popover-actions button { height:32px;margin:0; }.status-popover .status-popover-actions [data-status-retry] { grid-column:1/-1; }
1219
+ .status-popover { width:256px; }.status-popover-actions { display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-top:8px; }.status-popover .status-popover-actions button { height:32px;margin:0; }.status-popover .status-popover-actions [data-status-retry],.status-popover .status-popover-actions [data-status-repair] { grid-column:1/-1; }
1220
1220
  .onboarding-card { width:360px; }.onboarding-card-head>strong { flex:1; }.onboarding-card-head .onboarding-close { width:28px;height:28px;display:grid;place-items:center;padding:0;border:0;border-radius:4px;background:transparent;color:var(--fdc-muted);cursor:pointer; }.onboarding-card-head .onboarding-close:hover { color:var(--fdc-ink);background:var(--fdc-subtle); }.onboarding-card-head .onboarding-close svg { width:12px;height:12px; }.onboarding-steps { gap:4px;border:0;overflow:visible;background:transparent; }.onboarding-step { min-height:40px;grid-template-columns:24px minmax(0,1fr);align-items:center;gap:8px;padding:8px;border:1px solid var(--fdc-line);border-radius:8px; }.onboarding-step b { width:20px;height:20px;display:grid;place-items:center;border:1px solid var(--fdc-line);border-radius:50%;color:var(--fdc-muted);font:500 8px/1 var(--fdc-font);letter-spacing:0; }.onboarding-step b svg { width:12px;height:12px; }.onboarding-step.complete>b { color:#141416;background:#8ae0c2;border-color:#8ae0c2; }.onboarding-step.current { border-color:var(--fdc-signal);box-shadow:0 0 0 4px rgb(59 130 246 / 10%); }
1221
1221
  .review-summary { color:var(--fdc-ink);background:var(--fdc-paper); }.empty-state-icon { color:#9ec5ff;background:var(--fdc-signal-soft); }
1222
1222
  .component-card,.health-card { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line); }.component-card.selected { color:var(--fdc-ink);background:var(--fdc-component-soft);border-color:#5c4b9e;box-shadow:0 0 0 4px rgb(155 135 245 / 10%); }.component-variants span,.component-actions button,.component-variants button { color:#c1b5f5;background:var(--fdc-component-soft);border-color:#4a406f; }.health-score::before { background:var(--fdc-surface); }.health-filters button { color:var(--fdc-muted); }.health-filters button:hover,.health-filters button.active { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }.health-card p,.health-evidence { color:var(--fdc-muted); }.mapping-chooser { color:#f0c9a8;background:#35291d;border-color:#665039; }.mapping-chooser>strong,.mapping-option,.mapping-option small { color:#e7c29f; }.review-card.locating { background:var(--fdc-signal-soft); }.baseline-badge { color:#83d8bb;background:#18352c; }
@@ -1399,6 +1399,7 @@ var PANEL_CSS = `
1399
1399
  .property-control input,.property-control select,.compact-control input,.compact-control select { height:36px;font-size:12px;font-weight:400; }
1400
1400
  .control-field .unit-select { width:44px;padding-right:24px!important;background-position:right 8px center!important; }
1401
1401
  .section-grid { gap:8px;padding:0 16px 16px; }
1402
+ .inspector-category[data-category="position"]>.category-body>.property-section>.section-grid { padding-top:12px; }
1402
1403
  @media (max-width:680px){.panel,.layers-panel{width:auto}.panel-resizer{display:none}.change-tray{right:8px;bottom:8px;left:auto;width:calc(100vw - 16px)}.review-takeover{padding:8px}.review-modal{width:calc(100vw - 16px);height:calc(100vh - 16px);border-radius:12px}.review-modal .review-actions{grid-template-columns:84px 1fr}.panel.change-summary-visible .inspector-scroll{padding-bottom:60px}}
1403
1404
  @media (prefers-reduced-motion:reduce){*{transition-duration:.01ms!important}.layer-row.entering,.layer-branch{animation:none!important}}
1404
1405
  `;
@@ -1912,7 +1913,7 @@ function installFoundryInspector(options = {}) {
1912
1913
  statusPill.setAttribute("aria-label", "Foundry session status");
1913
1914
  statusPill.insertAdjacentHTML(
1914
1915
  "afterend",
1915
- '<div class="status-popover" hidden><strong data-status-title>Session connected</strong><span data-status-detail>Changes are stored locally.</span><code data-status-project></code><code data-status-revision></code><div class="status-popover-actions"><button data-status-checklist>Getting started</button><button data-status-diagnostics>Copy diagnostics</button><button data-status-retry>Check connection</button></div></div>'
1916
+ '<div class="status-popover" hidden><strong data-status-title>Session connected</strong><span data-status-detail>Changes are stored locally.</span><code data-status-project></code><code data-status-revision></code><div class="status-popover-actions"><button data-status-checklist>Getting started</button><button data-status-diagnostics>Copy diagnostics</button><button data-status-retry>Check connection</button><button data-status-repair>Copy repair command</button></div></div>'
1916
1917
  );
1917
1918
  shadow.querySelector(".panel").insertAdjacentHTML(
1918
1919
  "beforeend",
@@ -2122,6 +2123,7 @@ function installFoundryInspector(options = {}) {
2122
2123
  let reviewShowRejected = false;
2123
2124
  const collapsedReviewGroups = /* @__PURE__ */ new Set();
2124
2125
  let reviewPoll;
2126
+ let sessionPoll;
2125
2127
  let lastReviewTrigger = null;
2126
2128
  let reviewScrollTop = 0;
2127
2129
  let reviewSuspended = false;
@@ -2189,8 +2191,8 @@ function installFoundryInspector(options = {}) {
2189
2191
  },
2190
2192
  {
2191
2193
  id: "agent",
2192
- title: "Agent restarted and connected",
2193
- detail: "Keep the coding agent listening while you design."
2194
+ title: "Agent connection",
2195
+ detail: "Keep it listening while you design. Offline batches wait safely."
2194
2196
  },
2195
2197
  {
2196
2198
  id: "selection",
@@ -2514,7 +2516,8 @@ function installFoundryInspector(options = {}) {
2514
2516
  (change) => change.status !== "rejected" && String(change.before) !== String(change.after)
2515
2517
  );
2516
2518
  recordedChangeCount = activeChanges.length;
2517
- latestApplyState = payload.applyRuns?.at(-1)?.state ?? "none";
2519
+ const latestRun = payload.applyRuns?.at(-1);
2520
+ latestApplyState = latestRun?.state ?? "none";
2518
2521
  updateChangeCount(activeChanges.length, activeChanges.at(-1));
2519
2522
  completeOnboardingStep("setup");
2520
2523
  if (activeAgentPresence.connected) completeOnboardingStep("agent");
@@ -2524,6 +2527,7 @@ function installFoundryInspector(options = {}) {
2524
2527
  renderOnboardingChecklist();
2525
2528
  setSessionStatus("live");
2526
2529
  if (!libraryPanel.hidden) renderDesignMemory();
2530
+ if (latestRun?.state === "verifying") maybeVerifyRun(latestRun);
2527
2531
  if (changeSet.changes.length === 0 && !hydratedOnce) {
2528
2532
  showToast("Click any element. Shift-click builds a selection.");
2529
2533
  }
@@ -2537,7 +2541,11 @@ function installFoundryInspector(options = {}) {
2537
2541
  }
2538
2542
  }
2539
2543
  void hydrateSession();
2540
- const healthPoll = setInterval(() => void hydrateSession(), 5e3);
2544
+ function startSessionPolling() {
2545
+ clearInterval(sessionPoll);
2546
+ sessionPoll = setInterval(() => void hydrateSession(), 5e3);
2547
+ }
2548
+ startSessionPolling();
2541
2549
  function showToast(message) {
2542
2550
  const toast = shadow.querySelector(".toast");
2543
2551
  toast.textContent = message;
@@ -3513,12 +3521,18 @@ function installFoundryInspector(options = {}) {
3513
3521
  const count = group.querySelector(".included-count");
3514
3522
  if (count) count.textContent = `${included} included`;
3515
3523
  });
3516
- applyButton.textContent = !activeAgentPresence.connected ? "Connect agent to apply" : selectedCount ? `Apply ${selectedCount} with agent` : "Apply with agent";
3517
- applyButton.disabled = selectedCount === 0 || !activeAgentPresence.connected;
3524
+ applyButton.textContent = selectedCount ? activeAgentPresence.connected ? `Apply ${selectedCount} with agent` : `Queue ${selectedCount} for agent` : "Apply with agent";
3525
+ applyButton.disabled = selectedCount === 0;
3518
3526
  }
3519
3527
  function agentConnectionMarkup() {
3520
3528
  const agentName = activeAgentPresence.presence?.agent?.name;
3521
- return `<div class="review-agent ${activeAgentPresence.connected ? "connected" : "disconnected"}" aria-live="polite"><i></i><div><strong>${activeAgentPresence.connected ? `${escapeHtml(agentName ?? "Coding agent")} is ready` : "Connect your coding agent"}</strong><span>${activeAgentPresence.connected ? "Apply requests will be claimed automatically while this agent keeps listening." : "In the same project, ask your agent to start Foundry and keep listening for Apply requests."}</span></div>${activeAgentPresence.connected ? "" : "<button data-copy-agent-listener>Copy instruction</button>"}</div>`;
3529
+ return `<div class="review-agent ${activeAgentPresence.connected ? "connected" : "disconnected"}" aria-live="polite"><i></i><div><strong>${activeAgentPresence.connected ? `${escapeHtml(agentName ?? "Coding agent")} is ready` : "Agent currently offline"}</strong><span>${activeAgentPresence.connected ? "Apply requests will be claimed automatically while this agent keeps listening." : "You can queue this batch now. Codex, Cursor, or Claude Code will claim it when the Foundry listener reconnects."}</span></div>${activeAgentPresence.connected ? "" : "<button data-copy-agent-listener>Copy reconnect instruction</button>"}</div>`;
3530
+ }
3531
+ function copyAgentListenerInstruction() {
3532
+ void navigator.clipboard.writeText(
3533
+ "Start Foundry for this project and keep listening for Apply with agent requests."
3534
+ );
3535
+ showToast("Agent instruction copied");
3522
3536
  }
3523
3537
  function updateAgentConnection() {
3524
3538
  const current = reviewBody.querySelector(".review-agent");
@@ -3529,12 +3543,7 @@ function installFoundryInspector(options = {}) {
3529
3543
  const replacement = document.createElement("div");
3530
3544
  replacement.innerHTML = agentConnectionMarkup();
3531
3545
  current.replaceWith(replacement.firstElementChild);
3532
- reviewBody.querySelector("[data-copy-agent-listener]")?.addEventListener("click", () => {
3533
- void navigator.clipboard.writeText(
3534
- "Start Foundry for this project and keep listening for Apply with agent requests."
3535
- );
3536
- showToast("Agent instruction copied");
3537
- });
3546
+ reviewBody.querySelector("[data-copy-agent-listener]")?.addEventListener("click", copyAgentListenerInstruction);
3538
3547
  updateReviewSelection();
3539
3548
  }
3540
3549
  function renderReviewList(changes) {
@@ -3700,7 +3709,7 @@ function installFoundryInspector(options = {}) {
3700
3709
  }
3701
3710
  const runStateLabels = {
3702
3711
  queued: "Queued for agent",
3703
- claimed: "Agent connected",
3712
+ claimed: "Handoff received",
3704
3713
  applying: "Applying source changes",
3705
3714
  rebuilding: "Rebuilding and checking",
3706
3715
  verifying: "Verifying rendered values",
@@ -3709,6 +3718,19 @@ function installFoundryInspector(options = {}) {
3709
3718
  failed: "Apply failed",
3710
3719
  cancelled: "Apply cancelled"
3711
3720
  };
3721
+ function maybeVerifyRun(run) {
3722
+ if (run.state !== "verifying" || verifyingRuns.has(run.id)) return;
3723
+ const reloadKey = "__foundry_verifying_run";
3724
+ if (sessionStorage.getItem(reloadKey) !== run.id) {
3725
+ sessionStorage.setItem(reloadKey, run.id);
3726
+ location.reload();
3727
+ return;
3728
+ }
3729
+ verifyingRuns.add(run.id);
3730
+ void verify(run.changeIds, run.id).then(() => sessionStorage.removeItem(reloadKey)).catch((error) => {
3731
+ showToast(error instanceof Error ? error.message : "Rendered verification was interrupted");
3732
+ }).finally(() => verifyingRuns.delete(run.id));
3733
+ }
3712
3734
  function captureVerifiedRun(run) {
3713
3735
  if (run.state !== "passed" || capturedBaselineRuns.has(run.id)) return;
3714
3736
  const changes = (activeReviewPayload?.changeSet?.changes ?? []).filter(
@@ -3748,13 +3770,13 @@ function installFoundryInspector(options = {}) {
3748
3770
  const attention = ["needs_attention", "failed"].includes(run.state);
3749
3771
  const passed = run.state === "passed";
3750
3772
  const active = ["queued", "claimed", "applying", "rebuilding", "verifying"].includes(run.state);
3751
- const latestMessage = run.state === "queued" ? "The reviewed changes are queued. Keep your coding agent active, or ask it to apply your reviewed Foundry changes." : run.messages.at(-1)?.message ?? run.error ?? "Apply run created.";
3773
+ const latestMessage = run.state === "queued" ? run.requeueCount > 0 ? "The previous agent did not begin source work, so Foundry safely returned this batch to the queue." : "The reviewed changes are queued and ready for an active coding agent." : run.state === "claimed" ? activeAgentPresence.connected ? "The agent received this batch. Waiting for source work to begin." : "The agent disconnected before source work began. Foundry will return this batch to the queue shortly." : run.messages.at(-1)?.message ?? run.error ?? "Apply run created.";
3752
3774
  latestApplyState = run.state;
3753
3775
  if (run.state === "passed") completeOnboardingStep("apply");
3754
3776
  captureVerifiedRun(run);
3755
3777
  reviewCount.textContent = `Attempt ${run.attempts}`;
3756
3778
  reviewBody.innerHTML = `<div class="run-summary"><div class="run-state"><i class="${passed ? "passed" : attention ? "attention" : active ? "active" : ""}"></i><strong>${escapeHtml(runStateLabels[run.state] ?? run.state)}</strong></div><p>${escapeHtml(latestMessage)}</p></div><div class="run-steps">${run.messages.map(
3757
- (message, index) => `<div class="run-step"><span>${String(index + 1).padStart(2, "0")}</span><div><strong>${escapeHtml(runStateLabels[message.state] ?? message.state)}</strong><p>${escapeHtml(message.state === "queued" ? "Ready for the active coding agent to claim." : message.message)}</p></div></div>`
3779
+ (message, index) => `<div class="run-step"><span>${String(index + 1).padStart(2, "0")}</span><div><strong>${escapeHtml(runStateLabels[message.state] ?? message.state)}</strong><p>${escapeHtml(message.message)}</p></div></div>`
3758
3780
  ).join(
3759
3781
  ""
3760
3782
  )}</div>${run.changedFiles.length ? `<div class="run-files"><strong>Changed files</strong>${run.changedFiles.map((file) => `<code>${escapeHtml(file)}</code>`).join("")}</div>` : ""}${run.validationResults.length ? `<div class="result-list">${run.validationResults.map((result) => `<div class="result-row ${result.passed ? "pass" : "fail"}"><span>${result.passed ? "Passed" : "Failed"} \xB7 ${escapeHtml(result.name)}</span><span>${escapeHtml(result.summary ?? "")}</span></div>`).join("")}</div>` : ""}${run.verificationResults.length ? `<div class="result-list">${run.verificationResults.map((result) => `<div class="result-row ${result.passed ? "pass" : "fail"}"><span>${result.passed ? "Matched" : "Mismatch"} \xB7 ${escapeHtml(result.property)}</span><span>${escapeHtml(reviewValue(result.requested))} \u2192 ${escapeHtml(reviewValue(result.rendered))}</span></div>`).join("")}</div>` : ""}`;
@@ -3762,6 +3784,10 @@ function installFoundryInspector(options = {}) {
3762
3784
  applyButton.dataset.action = "retry";
3763
3785
  applyButton.textContent = "Retry with agent";
3764
3786
  applyButton.disabled = false;
3787
+ } else if (["queued", "claimed"].includes(run.state) && !activeAgentPresence.connected) {
3788
+ applyButton.dataset.action = "reconnect";
3789
+ applyButton.textContent = "Reconnect agent";
3790
+ applyButton.disabled = false;
3765
3791
  } else {
3766
3792
  applyButton.dataset.action = "status";
3767
3793
  applyButton.textContent = passed ? "Verified" : active ? runStateLabels[run.state] ?? run.state : "Run complete";
@@ -3769,19 +3795,7 @@ function installFoundryInspector(options = {}) {
3769
3795
  }
3770
3796
  reviewCancel.dataset.action = active ? "cancel" : "back";
3771
3797
  reviewCancel.textContent = active ? "Cancel" : "Back";
3772
- if (run.state === "verifying" && !verifyingRuns.has(run.id)) {
3773
- const reloadKey = "__foundry_verifying_run";
3774
- if (sessionStorage.getItem(reloadKey) !== run.id) {
3775
- sessionStorage.setItem(reloadKey, run.id);
3776
- location.reload();
3777
- return;
3778
- }
3779
- verifyingRuns.add(run.id);
3780
- void verify(run.changeIds, run.id).finally(() => {
3781
- verifyingRuns.delete(run.id);
3782
- sessionStorage.removeItem(reloadKey);
3783
- });
3784
- }
3798
+ maybeVerifyRun(run);
3785
3799
  }
3786
3800
  function renderReviewPayload(payload) {
3787
3801
  const preservedScrollTop = reviewBody.scrollTop || reviewScrollTop;
@@ -3868,7 +3882,8 @@ function installFoundryInspector(options = {}) {
3868
3882
  shadow.querySelector(".review-back")?.focus();
3869
3883
  });
3870
3884
  clearInterval(reviewPoll);
3871
- clearInterval(healthPoll);
3885
+ clearInterval(sessionPoll);
3886
+ sessionPoll = void 0;
3872
3887
  reviewPoll = setInterval(() => void refreshReview(), 1e3);
3873
3888
  }
3874
3889
  function closeReview(restoreFocus = true) {
@@ -3877,6 +3892,7 @@ function installFoundryInspector(options = {}) {
3877
3892
  reviewTakeover.hidden = true;
3878
3893
  clearInterval(reviewPoll);
3879
3894
  reviewPoll = void 0;
3895
+ startSessionPolling();
3880
3896
  updateOutline();
3881
3897
  if (restoreFocus) lastReviewTrigger?.focus();
3882
3898
  }
@@ -3904,11 +3920,6 @@ function installFoundryInspector(options = {}) {
3904
3920
  applyButton.disabled = true;
3905
3921
  try {
3906
3922
  activeAgentPresence = await sessionRequest("/agent-presence");
3907
- if (!activeAgentPresence.connected) {
3908
- updateAgentConnection();
3909
- showToast("No coding agent is listening yet");
3910
- return;
3911
- }
3912
3923
  const payload = await sessionRequest("/apply-runs", {
3913
3924
  method: "POST",
3914
3925
  body: JSON.stringify({
@@ -3918,6 +3929,8 @@ function installFoundryInspector(options = {}) {
3918
3929
  });
3919
3930
  clearReviewDraft();
3920
3931
  renderReviewPayload(payload);
3932
+ if (!activeAgentPresence.connected)
3933
+ showToast("Queued. Your coding agent will claim this batch when it reconnects.");
3921
3934
  } catch (error) {
3922
3935
  showToast(error instanceof Error ? error.message : "Could not start apply run");
3923
3936
  updateReviewSelection();
@@ -6221,6 +6234,10 @@ function installFoundryInspector(options = {}) {
6221
6234
  void navigator.clipboard.writeText(JSON.stringify(diagnostics, null, 2));
6222
6235
  showToast("Privacy-safe diagnostics copied");
6223
6236
  });
6237
+ statusPopover.querySelector("[data-status-repair]").addEventListener("click", () => {
6238
+ void navigator.clipboard.writeText("npx foundry-design@beta doctor --repair");
6239
+ showToast("Repair command copied");
6240
+ });
6224
6241
  interfaceThemeTrigger.addEventListener("click", () => {
6225
6242
  if (interfaceThemeMenu.hidden) openInterfaceThemeMenu();
6226
6243
  else closeInterfaceThemeMenu();
@@ -6243,7 +6260,7 @@ function installFoundryInspector(options = {}) {
6243
6260
  void navigator.clipboard.writeText(
6244
6261
  "Start Foundry for this project and keep listening for Apply with agent requests."
6245
6262
  );
6246
- showToast("Restart prompt copied");
6263
+ showToast("Listener prompt copied");
6247
6264
  return;
6248
6265
  }
6249
6266
  if (action === "apply" && recordedChangeCount > 0) {
@@ -6393,6 +6410,7 @@ function installFoundryInspector(options = {}) {
6393
6410
  applyButton.addEventListener("click", () => {
6394
6411
  if (applyButton.dataset.action === "apply") void submitReviewedRun();
6395
6412
  if (applyButton.dataset.action === "retry") void retryRun();
6413
+ if (applyButton.dataset.action === "reconnect") copyAgentListenerInstruction();
6396
6414
  });
6397
6415
  function handleGlobalShortcuts(event) {
6398
6416
  if (workspaceState.reviewOpen && event.key === "Tab") {
@@ -6504,6 +6522,7 @@ function installFoundryInspector(options = {}) {
6504
6522
  systemDarkTheme.removeEventListener("change", handleSystemThemeChange);
6505
6523
  document.removeEventListener("pointerdown", handleInterfaceThemeDismiss, true);
6506
6524
  clearInterval(reviewPoll);
6525
+ clearInterval(sessionPoll);
6507
6526
  document.removeEventListener("click", handlePointer, true);
6508
6527
  document.removeEventListener("dblclick", handleTextEdit, true);
6509
6528
  document.removeEventListener("pointermove", handleSelectionHover, true);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA8DA,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,IAAI,IAAI,CAAC;IAChB,cAAc,IAAI,IAAI,CAAC;IACvB,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IACnC,OAAO,IAAI,IAAI,CAAC;CACjB;AAu/BD,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,uBAA4B,GACpC,0BAA0B,CAg9K5B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA8DA,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,IAAI,IAAI,CAAC;IAChB,cAAc,IAAI,IAAI,CAAC;IACvB,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IACnC,OAAO,IAAI,IAAI,CAAC;CACjB;AAw/BD,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,uBAA4B,GACpC,0BAA0B,CA++K5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foundry-design-web-adapter",
3
- "version": "0.2.0-beta.5",
3
+ "version": "0.2.0-beta.7",
4
4
  "description": "Development-only browser inspector for Foundry",
5
5
  "type": "module",
6
6
  "main": "dist/adapter.js",