foundry-design-web-adapter 0.2.0-beta.6 → 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,7 +6,7 @@ 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
- > **Current public beta:** `0.2.0-beta.6`. Install Foundry with the `@beta` tag. The unqualified npm `latest` tag still points to an earlier beta and is not the current testing channel.
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
11
  Full documentation is available at [withfoundry.ai](https://withfoundry.ai).
12
12
 
@@ -96,7 +96,9 @@ Foundry remains local-first. Installing the plugin does not create an account, e
96
96
  - Persistent, coalescing change ledger with JSON and consolidated prompt export
97
97
  - Compact in-preview review with editable approved batches and unresolved-target blocking
98
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
99
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
100
102
  - Local MCP bridge for agent access
101
103
  - Debug adapters for web, SwiftUI, and React Native on iOS Simulator
102
104
  - Verification records that compare requested and rendered values
package/dist/adapter.js CHANGED
@@ -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
  `;
@@ -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;
@@ -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;
@@ -3520,6 +3528,12 @@ function installFoundryInspector(options = {}) {
3520
3528
  const agentName = activeAgentPresence.presence?.agent?.name;
3521
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>`;
3522
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");
3536
+ }
3523
3537
  function updateAgentConnection() {
3524
3538
  const current = reviewBody.querySelector(".review-agent");
3525
3539
  if (!current) {
@@ -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
  }
@@ -6394,6 +6410,7 @@ function installFoundryInspector(options = {}) {
6394
6410
  applyButton.addEventListener("click", () => {
6395
6411
  if (applyButton.dataset.action === "apply") void submitReviewedRun();
6396
6412
  if (applyButton.dataset.action === "retry") void retryRun();
6413
+ if (applyButton.dataset.action === "reconnect") copyAgentListenerInstruction();
6397
6414
  });
6398
6415
  function handleGlobalShortcuts(event) {
6399
6416
  if (workspaceState.reviewOpen && event.key === "Tab") {
@@ -6505,6 +6522,7 @@ function installFoundryInspector(options = {}) {
6505
6522
  systemDarkTheme.removeEventListener("change", handleSystemThemeChange);
6506
6523
  document.removeEventListener("pointerdown", handleInterfaceThemeDismiss, true);
6507
6524
  clearInterval(reviewPoll);
6525
+ clearInterval(sessionPoll);
6508
6526
  document.removeEventListener("click", handlePointer, true);
6509
6527
  document.removeEventListener("dblclick", handleTextEdit, true);
6510
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,CAm9K5B"}
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.6",
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",