opendevbrowser 0.0.27 → 0.0.28

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.
Files changed (76) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/canvas-manager.d.ts.map +1 -1
  3. package/dist/canvas/document-store.d.ts.map +1 -1
  4. package/dist/canvas/guidance.d.ts +22 -0
  5. package/dist/canvas/guidance.d.ts.map +1 -0
  6. package/dist/{chunk-MWBDO2L5.js → chunk-I5ZCOZZV.js} +224 -80
  7. package/dist/chunk-I5ZCOZZV.js.map +1 -0
  8. package/dist/{chunk-V5DJUSPV.js → chunk-T3VVHJTK.js} +1381 -318
  9. package/dist/chunk-T3VVHJTK.js.map +1 -0
  10. package/dist/cli/commands/inspiredesign.d.ts +2 -0
  11. package/dist/cli/commands/inspiredesign.d.ts.map +1 -1
  12. package/dist/cli/commands/macro-resolve.d.ts +2 -0
  13. package/dist/cli/commands/macro-resolve.d.ts.map +1 -1
  14. package/dist/cli/commands/product-video.d.ts +2 -0
  15. package/dist/cli/commands/product-video.d.ts.map +1 -1
  16. package/dist/cli/commands/research.d.ts +2 -0
  17. package/dist/cli/commands/research.d.ts.map +1 -1
  18. package/dist/cli/daemon-commands.d.ts.map +1 -1
  19. package/dist/cli/help.d.ts.map +1 -1
  20. package/dist/cli/index.js +125 -28
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/cli/utils/workflow-message.d.ts +3 -0
  23. package/dist/cli/utils/workflow-message.d.ts.map +1 -1
  24. package/dist/daemon-fingerprint.json +1 -1
  25. package/dist/index.js +39 -15
  26. package/dist/index.js.map +1 -1
  27. package/dist/inspiredesign/contract.d.ts +52 -1
  28. package/dist/inspiredesign/contract.d.ts.map +1 -1
  29. package/dist/inspiredesign/handoff.d.ts +12 -0
  30. package/dist/inspiredesign/handoff.d.ts.map +1 -1
  31. package/dist/inspiredesign/reference-pattern-board.d.ts +1 -0
  32. package/dist/inspiredesign/reference-pattern-board.d.ts.map +1 -1
  33. package/dist/macros/execute-runtime.d.ts +2 -1
  34. package/dist/macros/execute-runtime.d.ts.map +1 -1
  35. package/dist/macros/execute.d.ts +4 -2
  36. package/dist/macros/execute.d.ts.map +1 -1
  37. package/dist/opendevbrowser.js +39 -15
  38. package/dist/opendevbrowser.js.map +1 -1
  39. package/dist/providers/browser-fallback.d.ts +7 -0
  40. package/dist/providers/browser-fallback.d.ts.map +1 -1
  41. package/dist/providers/community/index.d.ts.map +1 -1
  42. package/dist/providers/index.d.ts.map +1 -1
  43. package/dist/providers/renderer.d.ts.map +1 -1
  44. package/dist/providers/research-compiler.d.ts.map +1 -1
  45. package/dist/providers/runtime-bundle.d.ts +1 -1
  46. package/dist/providers/runtime-bundle.d.ts.map +1 -1
  47. package/dist/providers/runtime-factory.d.ts.map +1 -1
  48. package/dist/providers/shopping/index.d.ts.map +1 -1
  49. package/dist/providers/social/platform.d.ts.map +1 -1
  50. package/dist/providers/social/search-quality.d.ts.map +1 -1
  51. package/dist/providers/social/youtube.d.ts.map +1 -1
  52. package/dist/providers/workflow-handoff.d.ts +18 -1
  53. package/dist/providers/workflow-handoff.d.ts.map +1 -1
  54. package/dist/providers/workflows.d.ts +3 -0
  55. package/dist/providers/workflows.d.ts.map +1 -1
  56. package/dist/{providers-TR3DUJZV.js → providers-QF2RFB4J.js} +2 -2
  57. package/dist/public-surface/generated-manifest.d.ts +6 -6
  58. package/dist/public-surface/generated-manifest.d.ts.map +1 -1
  59. package/dist/public-surface/source.d.ts +8 -8
  60. package/dist/public-surface/source.d.ts.map +1 -1
  61. package/dist/tools/inspiredesign_run.d.ts.map +1 -1
  62. package/dist/tools/macro_resolve.d.ts.map +1 -1
  63. package/dist/tools/product_video_run.d.ts.map +1 -1
  64. package/dist/tools/research_run.d.ts.map +1 -1
  65. package/dist/tools/shopping_run.d.ts.map +1 -1
  66. package/extension/manifest.json +1 -1
  67. package/package.json +1 -1
  68. package/skills/opendevbrowser-best-practices/SKILL.md +6 -6
  69. package/skills/opendevbrowser-design-agent/SKILL.md +5 -0
  70. package/skills/opendevbrowser-design-agent/artifacts/design-contract-playbook.md +6 -1
  71. package/skills/opendevbrowser-design-agent/assets/templates/design-contract.v1.json +15 -1
  72. package/skills/opendevbrowser-design-agent/assets/templates/inspiredesign-advanced-brief.v1.json +5 -2
  73. package/skills/opendevbrowser-design-agent/assets/templates/reference-pattern-board.v1.json +2 -0
  74. package/dist/chunk-MWBDO2L5.js.map +0 -1
  75. package/dist/chunk-V5DJUSPV.js.map +0 -1
  76. /package/dist/{providers-TR3DUJZV.js.map → providers-QF2RFB4J.js.map} +0 -0
@@ -3595,7 +3595,7 @@ var readFallbackRecord = (details, key) => {
3595
3595
  };
3596
3596
  var readFallbackString = (output, key) => {
3597
3597
  const value = output?.[key];
3598
- return typeof value === "string" && value.length > 0 ? value : void 0;
3598
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
3599
3599
  };
3600
3600
  var fallbackDispositionMessage = (fallback, url) => {
3601
3601
  if (typeof fallback.details?.message === "string" && fallback.details.message.trim().length > 0) {
@@ -3610,32 +3610,67 @@ var fallbackDispositionMessage = (fallback, url) => {
3610
3610
  return `Browser fallback failed for ${url}`;
3611
3611
  }
3612
3612
  };
3613
+ var fallbackErrorDetails = (args) => ({
3614
+ url: args.url,
3615
+ disposition: args.fallback.disposition,
3616
+ ...args.fallback.mode ? { browserFallbackMode: args.fallback.mode } : {},
3617
+ ...args.fallback.challenge ? { challenge: toJsonRecord(args.fallback.challenge) } : {},
3618
+ ...args.fallback.preservedSessionId ? { preservedSessionId: args.fallback.preservedSessionId } : {},
3619
+ ...args.fallback.preservedTargetId ? { preservedTargetId: args.fallback.preservedTargetId } : {},
3620
+ ...toJsonRecord(args.fallback.details ?? {}),
3621
+ ...args.extra ?? {}
3622
+ });
3623
+ var addProviderIssueGuidance = (args) => {
3624
+ const hint = readProviderIssueHint({
3625
+ reasonCode: args.reasonCode,
3626
+ details: args.details
3627
+ });
3628
+ const guidanceDetails = args.includeCompleted ? { ...args.details, disposition: "failed" } : args.details;
3629
+ const guidance = hint ? buildProviderIssueGuidance({ provider: args.provider, hint, details: guidanceDetails }) : void 0;
3630
+ return guidance ? { ...args.details, guidance } : args.details;
3631
+ };
3613
3632
  var toProviderFallbackError = (args) => {
3614
3633
  const { fallback } = args;
3615
3634
  const reasonCode = fallback.reasonCode;
3616
- const details = {
3635
+ const details = fallbackErrorDetails({ url: args.url, fallback });
3636
+ return new ProviderRuntimeError(
3637
+ providerErrorCodeFromReasonCode(reasonCode),
3638
+ fallbackDispositionMessage(fallback, args.url),
3639
+ {
3640
+ provider: args.provider,
3641
+ source: args.source,
3642
+ retryable: reasonCode === "rate_limited",
3643
+ reasonCode,
3644
+ details: addProviderIssueGuidance({
3645
+ provider: args.provider,
3646
+ reasonCode,
3647
+ details
3648
+ })
3649
+ }
3650
+ );
3651
+ };
3652
+ var toCompletedFallbackOutputError = (args) => {
3653
+ const { fallback } = args;
3654
+ const reasonCode = fallback.reasonCode;
3655
+ const details = fallbackErrorDetails({
3617
3656
  url: args.url,
3618
- disposition: fallback.disposition,
3619
- ...fallback.mode ? { browserFallbackMode: fallback.mode } : {},
3620
- ...fallback.challenge ? { challenge: toJsonRecord(fallback.challenge) } : {},
3621
- ...fallback.preservedSessionId ? { preservedSessionId: fallback.preservedSessionId } : {},
3622
- ...fallback.preservedTargetId ? { preservedTargetId: fallback.preservedTargetId } : {},
3623
- ...toJsonRecord(fallback.details ?? {})
3624
- };
3625
- const hint = readProviderIssueHint({
3626
- reasonCode,
3627
- details
3657
+ fallback,
3658
+ extra: { fallbackOutputReason: args.outputReason }
3628
3659
  });
3629
- const guidance = hint ? buildProviderIssueGuidance({ provider: args.provider, hint, details }) : void 0;
3630
3660
  return new ProviderRuntimeError(
3631
3661
  providerErrorCodeFromReasonCode(reasonCode),
3632
- fallbackDispositionMessage(fallback, args.url),
3662
+ `Browser fallback completed for ${args.url} without usable HTML content.`,
3633
3663
  {
3634
3664
  provider: args.provider,
3635
3665
  source: args.source,
3636
3666
  retryable: reasonCode === "rate_limited",
3637
3667
  reasonCode,
3638
- details: guidance ? { ...details, guidance } : details
3668
+ details: addProviderIssueGuidance({
3669
+ provider: args.provider,
3670
+ reasonCode,
3671
+ details,
3672
+ includeCompleted: true
3673
+ })
3639
3674
  }
3640
3675
  );
3641
3676
  };
@@ -5070,6 +5105,10 @@ var toPositiveInt = (value, fallback) => {
5070
5105
  if (typeof value !== "number" || Number.isNaN(value)) return fallback;
5071
5106
  return Math.max(1, Math.floor(value));
5072
5107
  };
5108
+ var toNonNegativeInt = (value, fallback) => {
5109
+ if (typeof value !== "number" || Number.isNaN(value)) return fallback;
5110
+ return Math.max(0, Math.floor(value));
5111
+ };
5073
5112
  var isHttpUrl2 = (value) => {
5074
5113
  try {
5075
5114
  const protocol = new URL(value).protocol;
@@ -5115,11 +5154,11 @@ var mergedTraversal = (options, input) => {
5115
5154
  asNumber(filters.pageLimit) ?? options.defaultTraversal?.pageLimit,
5116
5155
  DEFAULT_TRAVERSAL.pageLimit
5117
5156
  ),
5118
- hopLimit: toPositiveInt(
5157
+ hopLimit: toNonNegativeInt(
5119
5158
  asNumber(filters.hopLimit) ?? options.defaultTraversal?.hopLimit,
5120
5159
  DEFAULT_TRAVERSAL.hopLimit
5121
5160
  ),
5122
- expansionPerRecord: toPositiveInt(
5161
+ expansionPerRecord: toNonNegativeInt(
5123
5162
  asNumber(filters.expansionPerRecord) ?? options.defaultTraversal?.expansionPerRecord,
5124
5163
  DEFAULT_TRAVERSAL.expansionPerRecord
5125
5164
  ),
@@ -5428,7 +5467,7 @@ var createCommunityProvider = (options = {}) => {
5428
5467
  };
5429
5468
 
5430
5469
  // src/providers/social/search-quality.ts
5431
- var TARGETED_PLATFORMS = /* @__PURE__ */ new Set(["x", "bluesky", "reddit", "facebook"]);
5470
+ var TARGETED_PLATFORMS = /* @__PURE__ */ new Set(["x", "bluesky", "reddit", "facebook", "threads"]);
5432
5471
  var SOCIAL_JS_REQUIRED_RE = /\b(?:javascript (?:is not available|required|is disabled(?: in this browser)?)|you need to enable javascript|please enable javascript)\b/i;
5433
5472
  var BLUESKY_LOGGED_OUT_SEARCH_RE = /\bsearch is currently unavailable when logged out\b/i;
5434
5473
  var BLUESKY_EMPTY_SEARCH_SHELL_RE = /\b(?:follow 10 people to get started|find people to follow)\b/i;
@@ -5496,6 +5535,10 @@ var isPrimaryFacebookHost = (host) => {
5496
5535
  const normalized = host.toLowerCase();
5497
5536
  return normalized === "www.facebook.com" || normalized === "facebook.com" || normalized === "m.facebook.com";
5498
5537
  };
5538
+ var isPrimaryThreadsHost = (host) => {
5539
+ const normalized = host.toLowerCase();
5540
+ return normalized === "www.threads.net" || normalized === "threads.net";
5541
+ };
5499
5542
  var isFacebookSearchLikePath = (pathname) => pathname === "/watch/search" || pathname === "/watch/search/" || pathname.startsWith("/watch/explore/") || pathname.startsWith("/search/") || pathname.startsWith("/public/") || pathname.startsWith("/hashtag/");
5500
5543
  var isBlockedFacebookNonContentUrl = (parsed, options) => {
5501
5544
  if (!isPrimaryFacebookHost(parsed.hostname)) {
@@ -5522,7 +5565,7 @@ var isBlockedRedditNonContentUrl = (parsed, options) => {
5522
5565
  return false;
5523
5566
  }
5524
5567
  const pathname = parsed.pathname.toLowerCase();
5525
- if (pathname === "/" || pathname === "/login" || options.includeSearchRoute && pathname === "/search") {
5568
+ if (pathname === "/" || pathname === "/login" || pathname === "/login/" || options.includeSearchRoute && (pathname === "/search" || pathname === "/search/")) {
5526
5569
  return true;
5527
5570
  }
5528
5571
  const pathSegment = firstPathSegment(pathname);
@@ -5540,6 +5583,8 @@ var isRootShellUrl = (platform, parsed) => {
5540
5583
  return isBlockedRedditNonContentUrl(parsed, { includeSearchRoute: false });
5541
5584
  case "facebook":
5542
5585
  return isBlockedFacebookNonContentUrl(parsed, { includeSearchRoute: false });
5586
+ case "threads":
5587
+ return isPrimaryThreadsHost(host) && (pathname === "/" || pathname === "/login" || pathname === "/login/");
5543
5588
  default:
5544
5589
  return false;
5545
5590
  }
@@ -5556,6 +5601,8 @@ var isBlockedExpansionPath = (platform, parsed) => {
5556
5601
  return isBlockedRedditNonContentUrl(parsed, { includeSearchRoute: true });
5557
5602
  case "facebook":
5558
5603
  return isBlockedFacebookNonContentUrl(parsed, { includeSearchRoute: true });
5604
+ case "threads":
5605
+ return isPrimaryThreadsHost(host) && (pathname === "/" || pathname === "/login" || pathname === "/search" || pathname === "/search/" || isStaticMetadataPath(pathname));
5559
5606
  default:
5560
5607
  return false;
5561
5608
  }
@@ -5569,9 +5616,11 @@ var isFirstPartySearchRoute = (platform, parsed) => {
5569
5616
  case "bluesky":
5570
5617
  return host === "bsky.app" && pathname === "/search";
5571
5618
  case "reddit":
5572
- return isPrimaryRedditHost(host) && pathname === "/search";
5619
+ return isPrimaryRedditHost(host) && (pathname === "/search" || pathname === "/search/");
5573
5620
  case "facebook":
5574
5621
  return isPrimaryFacebookHost(host) && isFacebookSearchLikePath(pathname);
5622
+ case "threads":
5623
+ return isPrimaryThreadsHost(host) && (pathname === "/search" || pathname === "/search/");
5575
5624
  default:
5576
5625
  return false;
5577
5626
  }
@@ -5648,6 +5697,10 @@ var isUsableFacebookSearchEvidenceUrl = (url) => {
5648
5697
  }
5649
5698
  return /^\/reel\/[^/]+\/?$/.test(pathname) || /^\/groups\/[^/]+\/posts\/[^/]+\/?$/.test(pathname) || /^\/[^/]+\/videos\/[^/]+\/?$/.test(pathname) || /^\/share\/v\/[^/]+\/?$/.test(pathname) || (pathname === "/permalink.php" || pathname === "/story.php") && parsed.searchParams.has("story_fbid") || pathname === "/photo/" && parsed.searchParams.has("fbid");
5650
5699
  };
5700
+ var isUsableThreadsSearchEvidenceUrl = (url) => {
5701
+ const parsed = parseUrl(url);
5702
+ return parsed !== null && isPrimaryThreadsHost(parsed.hostname) && /^\/@[^/]+\/post\/[^/]+\/?$/.test(parsed.pathname.toLowerCase());
5703
+ };
5651
5704
  var isRetainableFacebookSearchSupportUrl = (url) => {
5652
5705
  const parsed = parseUrl(url);
5653
5706
  if (parsed === null || !isPrimaryFacebookHost(parsed.hostname)) {
@@ -5670,17 +5723,18 @@ var hasFacebookSearchResultSignals = (input) => {
5670
5723
  const hasSearchHeading = FACEBOOK_SEARCH_RESULTS_HEADING_RE.test(combined);
5671
5724
  const markerCount = FACEBOOK_SEARCH_RESULT_MARKERS.filter((pattern) => pattern.test(combined)).length;
5672
5725
  const evidence = collectSocialSearchLinkEvidence("facebook", parsed.toString(), Array.isArray(input.links) ? input.links : []);
5726
+ const hasContentEvidence = evidence.usableContentLinks.length > 0;
5673
5727
  const supportLinkCount = evidence.usableLinks.filter(isRetainableFacebookSearchSupportUrl).length;
5674
- if (markerCount >= 2) {
5728
+ if (markerCount >= 2 && hasContentEvidence) {
5675
5729
  return true;
5676
5730
  }
5677
5731
  if (!hasSearchHeading) {
5678
5732
  return false;
5679
5733
  }
5680
- if (markerCount >= 1) {
5734
+ if (markerCount >= 1 && hasContentEvidence) {
5681
5735
  return true;
5682
5736
  }
5683
- return supportLinkCount >= 2;
5737
+ return supportLinkCount >= 2 && hasContentEvidence;
5684
5738
  };
5685
5739
  var isUsableSocialSearchContentUrl = (platform, url) => {
5686
5740
  switch (platform) {
@@ -5692,6 +5746,8 @@ var isUsableSocialSearchContentUrl = (platform, url) => {
5692
5746
  return isUsableRedditSearchEvidenceUrl(url);
5693
5747
  case "facebook":
5694
5748
  return isUsableFacebookSearchEvidenceUrl(url);
5749
+ case "threads":
5750
+ return isUsableThreadsSearchEvidenceUrl(url);
5695
5751
  default:
5696
5752
  return false;
5697
5753
  }
@@ -5865,6 +5921,10 @@ var toPositiveInt2 = (value, fallback) => {
5865
5921
  if (typeof value !== "number" || Number.isNaN(value)) return fallback;
5866
5922
  return Math.max(1, Math.floor(value));
5867
5923
  };
5924
+ var toNonNegativeInt2 = (value, fallback) => {
5925
+ if (typeof value !== "number" || Number.isNaN(value)) return fallback;
5926
+ return Math.max(0, Math.floor(value));
5927
+ };
5868
5928
  var isHttpUrl3 = (value) => {
5869
5929
  try {
5870
5930
  const protocol = new URL(value).protocol;
@@ -5954,11 +6014,11 @@ var mergedTraversal2 = (input, options) => {
5954
6014
  asNumber2(filters.pageLimit) ?? options.defaultTraversal?.pageLimit,
5955
6015
  DEFAULT_TRAVERSAL2.pageLimit
5956
6016
  ),
5957
- hopLimit: toPositiveInt2(
6017
+ hopLimit: toNonNegativeInt2(
5958
6018
  asNumber2(filters.hopLimit) ?? options.defaultTraversal?.hopLimit,
5959
6019
  DEFAULT_TRAVERSAL2.hopLimit
5960
6020
  ),
5961
- expansionPerRecord: toPositiveInt2(
6021
+ expansionPerRecord: toNonNegativeInt2(
5962
6022
  asNumber2(filters.expansionPerRecord) ?? options.defaultTraversal?.expansionPerRecord,
5963
6023
  DEFAULT_TRAVERSAL2.expansionPerRecord
5964
6024
  ),
@@ -7412,6 +7472,47 @@ var normalizeYouTubeVideoLink = (url) => {
7412
7472
  }
7413
7473
  };
7414
7474
  var YOUTUBE_SEARCH_SHELL_RE = /\b(?:about press copyright contact us creators advertise developers terms privacy policy|google for developers skip to main content youtube)\b/i;
7475
+ var YOUTUBE_FALLBACK_MIN_TEXT_CHARS = 40;
7476
+ var YOUTUBE_CONTENT_MARKER_RE = /"videoId"\s*:|"captionTracks"\s*:|itemprop="author"|itemprop="datePublished"|"viewCount"\s*:/i;
7477
+ var hasYouTubeFallbackEvidence = (html, url) => {
7478
+ if (YOUTUBE_CONTENT_MARKER_RE.test(html)) return true;
7479
+ return extractStructuredContent(html, url).links.some((link) => normalizeYouTubeVideoLink(link) !== null);
7480
+ };
7481
+ var reviewedFallback = (fallback, reasonCode) => ({
7482
+ ...fallback,
7483
+ reasonCode: reasonCode ?? fallback.reasonCode
7484
+ });
7485
+ var assertUsableYouTubeFallbackPage = (url, fallback, html) => {
7486
+ const extracted = extractStructuredContent(html, url);
7487
+ const text = extracted.text.trim();
7488
+ const blocker = classifyBlockerSignal({
7489
+ source: "runtime_fetch",
7490
+ url,
7491
+ message: text,
7492
+ status: 200,
7493
+ providerErrorCode: "unavailable",
7494
+ retryable: true
7495
+ });
7496
+ if (blocker && blocker.type !== "unknown") {
7497
+ throw toCompletedFallbackOutputError({
7498
+ provider: "social/youtube",
7499
+ source: "social",
7500
+ url,
7501
+ fallback: reviewedFallback(fallback, blocker.reasonCode),
7502
+ outputReason: blocker.type
7503
+ });
7504
+ }
7505
+ if (hasYouTubeFallbackEvidence(html, url)) return;
7506
+ if (text.length < YOUTUBE_FALLBACK_MIN_TEXT_CHARS || YOUTUBE_SEARCH_SHELL_RE.test(text)) {
7507
+ throw toCompletedFallbackOutputError({
7508
+ provider: "social/youtube",
7509
+ source: "social",
7510
+ url,
7511
+ fallback,
7512
+ outputReason: "youtube_shell_or_metadata"
7513
+ });
7514
+ }
7515
+ };
7415
7516
  var extractSearchResultSegment = (html, videoId) => {
7416
7517
  const marker = new RegExp(`"videoId"\\s*:\\s*"${escapeRegExp(videoId)}"`);
7417
7518
  const match = marker.exec(html);
@@ -7514,7 +7615,7 @@ var summarizeTranscript = (transcript) => {
7514
7615
  const lines = transcript.split(/\n+/).map((line) => line.trim()).filter(Boolean);
7515
7616
  return lines.slice(0, 8).join(" ").slice(0, 800);
7516
7617
  };
7517
- var fetchPage = async (url, context) => {
7618
+ var fetchPageDirect = async (url, context) => {
7518
7619
  let response;
7519
7620
  try {
7520
7621
  response = await fetch(url, {
@@ -7567,6 +7668,108 @@ var fetchPage = async (url, context) => {
7567
7668
  html: await response.text()
7568
7669
  };
7569
7670
  };
7671
+ var forcedFallbackReasonCode = (context) => {
7672
+ const policy = context.runtimePolicy;
7673
+ if (policy?.cookies.policy === "required") return "auth_required";
7674
+ return "env_limited";
7675
+ };
7676
+ var fallbackReasonCodeForPageError = (error, context) => {
7677
+ if (!isProviderRuntimeError(error)) return "env_limited";
7678
+ if (error.code === "auth") {
7679
+ return context.runtimePolicy?.cookies.policy === "required" ? "auth_required" : "token_required";
7680
+ }
7681
+ if (error.code === "rate_limited") return "rate_limited";
7682
+ if (error.code === "network" || error.code === "timeout") return "env_limited";
7683
+ if (error.code === "upstream") return "ip_blocked";
7684
+ if (error.reasonCode === "ip_blocked" || error.reasonCode === "challenge_detected") return error.reasonCode;
7685
+ return null;
7686
+ };
7687
+ var fallbackDetailsForError = (error) => {
7688
+ if (!isProviderRuntimeError(error)) return {};
7689
+ return {
7690
+ errorCode: error.code,
7691
+ message: error.message,
7692
+ ...error.details ?? {}
7693
+ };
7694
+ };
7695
+ var pageFromFallback = (url, fallback) => {
7696
+ const html = readFallbackString(fallback.output, "html");
7697
+ if (!html) {
7698
+ throw toCompletedFallbackOutputError({
7699
+ provider: "social/youtube",
7700
+ source: "social",
7701
+ url,
7702
+ fallback,
7703
+ outputReason: "missing_or_empty_html"
7704
+ });
7705
+ }
7706
+ assertUsableYouTubeFallbackPage(url, fallback, html);
7707
+ return {
7708
+ status: 200,
7709
+ url: readFallbackString(fallback.output, "url") ?? url,
7710
+ html,
7711
+ browserFallback: toBrowserFallbackObservation(fallback)
7712
+ };
7713
+ };
7714
+ var resolveFallbackPage = async (args) => {
7715
+ const fallback = await resolveProviderBrowserFallback({
7716
+ browserFallbackPort: args.context.browserFallbackPort ?? args.options.browserFallbackPort,
7717
+ provider: "social/youtube",
7718
+ source: "social",
7719
+ operation: args.operation,
7720
+ reasonCode: args.reasonCode,
7721
+ url: args.url,
7722
+ context: args.context,
7723
+ details: args.details,
7724
+ recoveryHints: args.options.recoveryHints?.()
7725
+ });
7726
+ if (!fallback) return null;
7727
+ if (fallback.disposition !== "completed") {
7728
+ throw toProviderFallbackError({
7729
+ provider: "social/youtube",
7730
+ source: "social",
7731
+ url: args.url,
7732
+ fallback
7733
+ });
7734
+ }
7735
+ return pageFromFallback(args.url, fallback);
7736
+ };
7737
+ var fetchPage = async (url, context, options, operation) => {
7738
+ if (context.runtimePolicy?.browser.forceTransport) {
7739
+ const page = await resolveFallbackPage({
7740
+ url,
7741
+ context,
7742
+ options,
7743
+ operation,
7744
+ reasonCode: forcedFallbackReasonCode(context),
7745
+ details: { message: "Direct browser transport was selected for this YouTube provider run." }
7746
+ });
7747
+ if (page) return page;
7748
+ throw new ProviderRuntimeError(providerErrorCodeFromReasonCode(forcedFallbackReasonCode(context)), "Direct browser transport is required for this YouTube provider run, but no browser transport is available.", {
7749
+ provider: "social/youtube",
7750
+ source: "social",
7751
+ retryable: false,
7752
+ reasonCode: forcedFallbackReasonCode(context),
7753
+ details: { browserTransportRequired: true }
7754
+ });
7755
+ }
7756
+ try {
7757
+ return await fetchPageDirect(url, context);
7758
+ } catch (error) {
7759
+ const reasonCode = fallbackReasonCodeForPageError(error, context);
7760
+ if (!reasonCode) throw error;
7761
+ const page = await resolveFallbackPage({
7762
+ url,
7763
+ context,
7764
+ options,
7765
+ operation,
7766
+ reasonCode,
7767
+ details: fallbackDetailsForError(error)
7768
+ });
7769
+ if (page) return page;
7770
+ throw error;
7771
+ }
7772
+ };
7570
7773
  var parseBooleanFilter = (value, fallback = false) => {
7571
7774
  if (typeof value === "boolean") return value;
7572
7775
  if (typeof value === "string") {
@@ -7609,7 +7812,7 @@ var resolveTranscriptStrategyDetail = (transcript) => {
7609
7812
  return transcript.attemptChain.at(-1)?.strategy;
7610
7813
  };
7611
7814
  var buildSearch = (options) => {
7612
- if (options) return options;
7815
+ if (options.search) return options.search;
7613
7816
  return async (input, context) => {
7614
7817
  assertYouTubeLegalReviewChecklist();
7615
7818
  const query = input.query.trim();
@@ -7622,7 +7825,7 @@ var buildSearch = (options) => {
7622
7825
  }
7623
7826
  const lookupUrl = isHttpUrl4(query) ? query : `https://www.youtube.com/results?search_query=${encodeURIComponent(query)}`;
7624
7827
  const directUrlQuery = isHttpUrl4(query);
7625
- const page = await fetchPage(lookupUrl, context);
7828
+ const page = await fetchPage(lookupUrl, context, options, "search");
7626
7829
  const extracted = extractStructuredContent(page.html, page.url);
7627
7830
  const extractedVideoLinks = [...new Set(
7628
7831
  extracted.links.map((link) => normalizeYouTubeVideoLink(link)).filter((link) => typeof link === "string" && link.length > 0)
@@ -7668,7 +7871,8 @@ var buildSearch = (options) => {
7668
7871
  retrievalPath: isHttpUrl4(query) ? "social:youtube:search:url" : "social:youtube:search:index",
7669
7872
  video_id: resolvedVideoId,
7670
7873
  ...primaryResult?.channel ? { channel: primaryResult.channel } : {},
7671
- links: extractedVideoLinks.slice(0, 20)
7874
+ links: extractedVideoLinks.slice(0, 20),
7875
+ ...browserFallbackObservationAttributes(page.browserFallback)
7672
7876
  }
7673
7877
  }];
7674
7878
  };
@@ -7677,7 +7881,7 @@ var buildFetch = (options) => {
7677
7881
  if (options.fetch) return options.fetch;
7678
7882
  return async (input, context) => {
7679
7883
  assertYouTubeLegalReviewChecklist();
7680
- const page = await fetchPage(input.url, context);
7884
+ const page = await fetchPage(input.url, context, options, "fetch");
7681
7885
  const extracted = extractStructuredContent(page.html, page.url);
7682
7886
  const includeFullTranscript = parseBooleanFilter(input.filters?.include_full_transcript, false);
7683
7887
  const requireTranscript = parseBooleanFilter(input.filters?.requireTranscript, false);
@@ -7692,7 +7896,7 @@ var buildFetch = (options) => {
7692
7896
  legalChecklist: YOUTUBE_LEGAL_REVIEW_CHECKLIST,
7693
7897
  config: transcriptConfig,
7694
7898
  mode: requestedMode,
7695
- browserFallbackPort: options.browserFallbackPort,
7899
+ browserFallbackPort: context.browserFallbackPort ?? options.browserFallbackPort,
7696
7900
  recoveryHints,
7697
7901
  allowBrowserFallbackEscalation: options.antiBotPolicy?.allowBrowserEscalation ?? true,
7698
7902
  asrTranscribe: options.asrTranscribe
@@ -7747,6 +7951,7 @@ var buildFetch = (options) => {
7747
7951
  transcript_mode: transcript.mode,
7748
7952
  translation_applied: translationApplied,
7749
7953
  transcript_summary: transcriptSummary,
7954
+ ...browserFallbackObservationAttributes(page.browserFallback),
7750
7955
  ...includeFullTranscript ? { transcript_full: transcriptContent } : {},
7751
7956
  ...transcriptStrategyDetail ? { transcript_strategy_detail: transcriptStrategyDetail } : {},
7752
7957
  ...transcript.ok ? {
@@ -7789,7 +7994,7 @@ var withDefaultYouTubeOptions = (options = {}) => {
7789
7994
  };
7790
7995
  return {
7791
7996
  ...resolvedOptions,
7792
- search: buildSearch(resolvedOptions.search),
7997
+ search: buildSearch(resolvedOptions),
7793
7998
  fetch: buildFetch(resolvedOptions)
7794
7999
  };
7795
8000
  };
@@ -7989,6 +8194,73 @@ var getShoppingRegionSupportDiagnostics = (providerIds, region) => {
7989
8194
  });
7990
8195
  };
7991
8196
  var hasValues2 = (values) => values.some((value) => value.trim().length > 0);
8197
+ var FALLBACK_HEAD_RE = /<head\b[^>]*>[\s\S]*?<\/head>/i;
8198
+ var FALLBACK_BODY_RE = /<body\b[^>]*>([\s\S]*?)<\/body>/i;
8199
+ var SHOPPING_FALLBACK_EVIDENCE_LIMIT = 1;
8200
+ var extractShoppingFallbackBodyText = (html) => {
8201
+ const body = FALLBACK_BODY_RE.exec(html)?.[1];
8202
+ return extractText(body ?? html.replace(FALLBACK_HEAD_RE, " "));
8203
+ };
8204
+ var hasShoppingMetadataEvidence = (extracted) => {
8205
+ return extracted.metadata.price !== void 0;
8206
+ };
8207
+ var hasShoppingOfferTextEvidence = (text) => {
8208
+ const price = parsePrice(text);
8209
+ if (price.amount <= 0) return false;
8210
+ return parseRating(text) > 0 || parseReviews(text) > 0 || parseAvailability(text) !== "unknown" || /\b(?:add to cart|buy now|shipping|pickup|deal|save)\b/i.test(text);
8211
+ };
8212
+ var hasShoppingBlockingPageEvidence = (url, extracted) => {
8213
+ const blocker = classifyBlockerSignal({
8214
+ source: "runtime_fetch",
8215
+ url,
8216
+ finalUrl: url,
8217
+ title: typeof extracted.metadata.title === "string" ? extracted.metadata.title : void 0,
8218
+ message: extracted.text,
8219
+ status: 200,
8220
+ providerErrorCode: "unavailable",
8221
+ retryable: true
8222
+ });
8223
+ return blocker?.type === "auth_required" || blocker?.type === "anti_bot_challenge";
8224
+ };
8225
+ var hasShoppingFallbackEvidence = (args) => {
8226
+ return args.extracted.links.some((link) => isLikelyProductUrl(canonicalizeUrl(link), args.profile)) || extractSearchCandidates(args.html, args.url, args.profile, SHOPPING_FALLBACK_EVIDENCE_LIMIT).length > 0 || hasShoppingMetadataEvidence(args.extracted) || hasShoppingOfferTextEvidence(extractShoppingFallbackBodyText(args.html)) || hasShoppingBlockingPageEvidence(args.url, args.extracted);
8227
+ };
8228
+ var toFallbackShellIssueError = (args) => {
8229
+ const issue = classifyProviderIssue({
8230
+ url: args.url,
8231
+ title: args.requirement.title,
8232
+ message: args.requirement.message,
8233
+ providerShell: args.requirement.reason,
8234
+ browserRequired: true,
8235
+ status: 200
8236
+ });
8237
+ const reasonCode = issue?.reasonCode ?? "env_limited";
8238
+ const extracted = extractStructuredContent(args.html, args.url);
8239
+ return new ProviderRuntimeError(
8240
+ providerErrorCodeFromReasonCode(reasonCode),
8241
+ reasonCode === "challenge_detected" ? `Detected anti-bot challenge while retrieving ${args.url}` : `Browser assistance required for ${args.url}`,
8242
+ {
8243
+ provider: args.provider,
8244
+ source: SHOPPING_SOURCE,
8245
+ retryable: reasonCode === "env_limited",
8246
+ reasonCode,
8247
+ details: {
8248
+ ...applyProviderIssueHint({
8249
+ status: 200,
8250
+ url: args.url,
8251
+ ...args.requirement.title ? { title: args.requirement.title } : {},
8252
+ ...args.requirement.message ? { message: args.requirement.message } : {},
8253
+ providerShell: args.requirement.reason,
8254
+ browserRequired: true,
8255
+ extractionFocus: args.profile.extractionFocus,
8256
+ extractedTextLength: extracted.text.length,
8257
+ extractedLinkCount: extracted.links.length
8258
+ }, issue),
8259
+ ...browserFallbackObservationDetails(toBrowserFallbackObservation(args.fallback))
8260
+ }
8261
+ }
8262
+ );
8263
+ };
7992
8264
  var parseIsoDate2 = (value) => {
7993
8265
  const parsed = Date.parse(value);
7994
8266
  return Number.isNaN(parsed) ? NaN : parsed;
@@ -8120,11 +8392,47 @@ var resolveBrowserFallback = async (args) => {
8120
8392
  };
8121
8393
  }
8122
8394
  const resolvedUrl = canonicalizeUrl(readFallbackString(fallback.output, "url") ?? args.url);
8395
+ const html = readFallbackString(fallback.output, "html");
8396
+ if (!html) {
8397
+ throw toCompletedFallbackOutputError({
8398
+ provider: args.provider,
8399
+ source: SHOPPING_SOURCE,
8400
+ url: resolvedUrl,
8401
+ fallback,
8402
+ outputReason: "missing_or_empty_html"
8403
+ });
8404
+ }
8405
+ const extracted = extractStructuredContent(html, resolvedUrl);
8406
+ const browserRequirement = requiresBrowserAssistance(args.profile, resolvedUrl, html);
8407
+ if (browserRequirement) {
8408
+ throw toFallbackShellIssueError({
8409
+ provider: args.provider,
8410
+ url: resolvedUrl,
8411
+ html,
8412
+ fallback,
8413
+ profile: args.profile,
8414
+ requirement: browserRequirement
8415
+ });
8416
+ }
8417
+ if (!hasShoppingFallbackEvidence({
8418
+ html,
8419
+ url: resolvedUrl,
8420
+ extracted,
8421
+ profile: args.profile
8422
+ })) {
8423
+ throw toCompletedFallbackOutputError({
8424
+ provider: args.provider,
8425
+ source: SHOPPING_SOURCE,
8426
+ url: resolvedUrl,
8427
+ fallback,
8428
+ outputReason: "empty_extracted_content"
8429
+ });
8430
+ }
8123
8431
  return {
8124
8432
  record: {
8125
8433
  status: 200,
8126
8434
  url: resolvedUrl,
8127
- html: readFallbackString(fallback.output, "html") ?? "",
8435
+ html,
8128
8436
  browserFallback: toBrowserFallbackObservation(fallback)
8129
8437
  }
8130
8438
  };
@@ -8237,6 +8545,7 @@ var defaultFetcher = async ({ url, signal, provider, operation, context }) => {
8237
8545
  error,
8238
8546
  url,
8239
8547
  provider: providerId,
8548
+ profile,
8240
8549
  operation,
8241
8550
  recoveryHints,
8242
8551
  context
@@ -8644,8 +8953,7 @@ var requiresBrowserAssistance = (profile, responseUrl, html) => {
8644
8953
  }
8645
8954
  return null;
8646
8955
  };
8647
- var classifySearchPageIssue = (profile, fetched, extracted, content) => {
8648
- const providerShell = requiresBrowserAssistance(profile, fetched.url, fetched.html);
8956
+ var classifySearchPageIssue = (profile, fetched, extracted, content, providerShell = requiresBrowserAssistance(profile, fetched.url, fetched.html)) => {
8649
8957
  return classifyProviderIssue({
8650
8958
  url: fetched.url,
8651
8959
  title: providerShell?.title ?? (typeof extracted.metadata.title === "string" ? extracted.metadata.title : void 0),
@@ -8657,6 +8965,51 @@ var classifySearchPageIssue = (profile, fetched, extracted, content) => {
8657
8965
  retryable: true
8658
8966
  });
8659
8967
  };
8968
+ var toExplicitProviderShellIssue = (providerShell, content) => ({
8969
+ reasonCode: "env_limited",
8970
+ blockerType: "env_limited",
8971
+ constraint: {
8972
+ kind: "render_required",
8973
+ evidenceCode: providerShell.reason,
8974
+ providerShell: providerShell.reason,
8975
+ ...content ? { message: content } : {}
8976
+ }
8977
+ });
8978
+ var ensureProviderShellIssue = (issue, providerShell, content) => {
8979
+ if (!issue || issue.reasonCode === "env_limited" && !issue.constraint) {
8980
+ return toExplicitProviderShellIssue(providerShell, content);
8981
+ }
8982
+ return issue;
8983
+ };
8984
+ var toShoppingPageIssueMessage = (reasonCode, url) => {
8985
+ if (reasonCode === "token_required") return `Authentication required for ${url}`;
8986
+ if (reasonCode === "env_limited") return `Browser assistance required for ${url}`;
8987
+ return `Detected anti-bot challenge while retrieving ${url}`;
8988
+ };
8989
+ var throwShoppingPageIssue = (args) => {
8990
+ const reasonCode = args.pageIssue.reasonCode;
8991
+ throw new ProviderRuntimeError(
8992
+ providerErrorCodeFromReasonCode(reasonCode),
8993
+ toShoppingPageIssueMessage(reasonCode, args.fetched.url),
8994
+ {
8995
+ provider: args.providerId,
8996
+ source: SHOPPING_SOURCE,
8997
+ retryable: reasonCode === "env_limited",
8998
+ reasonCode,
8999
+ details: {
9000
+ ...applyProviderIssueHint({
9001
+ status: args.fetched.status,
9002
+ url: args.fetched.url,
9003
+ ...typeof args.extracted.metadata.title === "string" ? { title: args.extracted.metadata.title } : {},
9004
+ ...args.content ? { message: args.content } : {},
9005
+ ...args.providerShell?.reason ? { providerShell: args.providerShell.reason } : {}
9006
+ }, args.pageIssue),
9007
+ ...args.providerShell?.reason ? { browserRequired: true } : {},
9008
+ ...browserFallbackObservationDetails(args.fetched.browserFallback)
9009
+ }
9010
+ }
9011
+ );
9012
+ };
8660
9013
  var unwrapTrackingUrl = (url, profile) => {
8661
9014
  const normalizedUrl = decodeHrefValue(url);
8662
9015
  try {
@@ -9054,6 +9407,7 @@ var createDefaultSearch = (profile, providerId, fetcher) => async (input, contex
9054
9407
  context
9055
9408
  });
9056
9409
  const extracted = extractStructuredContent(fetched.html, fetched.url);
9410
+ const providerShell = requiresBrowserAssistance(profile, fetched.url, fetched.html);
9057
9411
  const limit = Math.max(1, Math.min(input.limit ?? 10, 20));
9058
9412
  const links = dedupeLinks(
9059
9413
  extracted.links.filter((link) => isLikelyProductUrl(canonicalizeUrl(link), profile)),
@@ -9061,7 +9415,17 @@ var createDefaultSearch = (profile, providerId, fetcher) => async (input, contex
9061
9415
  );
9062
9416
  const content = toSnippet(extracted.text, 2e3);
9063
9417
  const candidates = extractSearchCandidates(fetched.html, fetched.url, profile, limit);
9064
- const pageIssue = candidates.length === 0 ? classifySearchPageIssue(profile, fetched, extracted, content) : null;
9418
+ const pageIssue = providerShell ? classifySearchPageIssue(profile, fetched, extracted, content, providerShell) : candidates.length === 0 ? classifySearchPageIssue(profile, fetched, extracted, content, providerShell) : null;
9419
+ if (providerShell) {
9420
+ throwShoppingPageIssue({
9421
+ providerId,
9422
+ fetched,
9423
+ extracted,
9424
+ content,
9425
+ pageIssue: ensureProviderShellIssue(pageIssue, providerShell, content),
9426
+ providerShell
9427
+ });
9428
+ }
9065
9429
  if (candidates.length > 0) {
9066
9430
  return candidates.map((candidate, index) => ({
9067
9431
  url: candidate.url,
@@ -9096,28 +9460,7 @@ var createDefaultSearch = (profile, providerId, fetcher) => async (input, contex
9096
9460
  }));
9097
9461
  }
9098
9462
  if (pageIssue && (pageIssue.reasonCode !== "env_limited" || pageIssue.constraint)) {
9099
- const reasonCode = pageIssue.reasonCode;
9100
- const providerShell = requiresBrowserAssistance(profile, fetched.url, fetched.html);
9101
- throw new ProviderRuntimeError(
9102
- providerErrorCodeFromReasonCode(reasonCode),
9103
- reasonCode === "token_required" ? `Authentication required for ${fetched.url}` : `Detected anti-bot challenge while retrieving ${fetched.url}`,
9104
- {
9105
- provider: providerId,
9106
- source: SHOPPING_SOURCE,
9107
- retryable: reasonCode === "env_limited",
9108
- reasonCode,
9109
- details: {
9110
- ...applyProviderIssueHint({
9111
- status: fetched.status,
9112
- url: fetched.url,
9113
- ...typeof extracted.metadata.title === "string" ? { title: extracted.metadata.title } : {},
9114
- ...content ? { message: content } : {},
9115
- ...providerShell?.reason ? { providerShell: providerShell.reason } : {}
9116
- }, pageIssue),
9117
- ...browserFallbackObservationDetails(fetched.browserFallback)
9118
- }
9119
- }
9120
- );
9463
+ throwShoppingPageIssue({ providerId, fetched, extracted, content, pageIssue, providerShell });
9121
9464
  }
9122
9465
  const rows = [
9123
9466
  {
@@ -10023,7 +10366,13 @@ var design_contract_v1_default = {
10023
10366
  "section reveal",
10024
10367
  "primary CTA feedback"
10025
10368
  ],
10026
- parallaxPolicy: "off unless explicitly justified"
10369
+ parallaxPolicy: "off unless explicitly justified",
10370
+ advancedMotionAdvisory: [
10371
+ "Advisory shader-like gradient depth: describe intent only; implement with approved runtime primitives unless a separate runtime change authorizes more.",
10372
+ "Advisory WebGL-style spatial reveal: document depth and hierarchy without implying WebGL runtime support.",
10373
+ "Advisory Spline-style product orbit: document scene posture and static fallback without approving custom 3D runtime dependencies.",
10374
+ "Runtime boundary: libraryPolicy.motion and libraryPolicy.threeD stay empty unless separately approved."
10375
+ ]
10027
10376
  },
10028
10377
  performanceModel: {
10029
10378
  renderHotspots: [
@@ -10116,6 +10465,14 @@ var design_contract_v1_default = {
10116
10465
  "spinner-stacking",
10117
10466
  "scan-surface-jank"
10118
10467
  ]
10468
+ },
10469
+ designVectors: {
10470
+ advancedMotionAdvisory: [
10471
+ "Advisory shader-like gradient depth",
10472
+ "Advisory WebGL-style spatial reveal",
10473
+ "Advisory Spline-style product orbit",
10474
+ "Runtime support: none. Library policy authorization: none."
10475
+ ]
10119
10476
  }
10120
10477
  }
10121
10478
  };
@@ -10159,6 +10516,172 @@ var INSPIREDESIGN_HANDOFF_GUIDANCE = {
10159
10516
  prepareCanvasPlanRequest: `Fill canvasSessionId, leaseId, and documentId in ${INSPIREDESIGN_HANDOFF_FILES.canvasPlanRequest} before running ${INSPIREDESIGN_HANDOFF_COMMANDS.continueInCanvas}.`,
10160
10517
  deepCaptureRecommendation: "Any inspiredesign run with reference URLs already uses captureMode=deep. Rerun with the same URLs only when you need refreshed DOM/layout evidence, restored session state, or capture-specific debugging."
10161
10518
  };
10519
+ var INSPIREDESIGN_ARTIFACT_GUIDE = {
10520
+ [INSPIREDESIGN_HANDOFF_FILES.advancedBrief]: {
10521
+ purpose: "Authoritative reference-first brief for the downstream design agent.",
10522
+ expectedContents: ["Selected prompt format", "reference pattern board", "route guardrails"],
10523
+ howToUse: ["Read first", "treat captured evidence as creative priority", "use guardrails to avoid route drift"],
10524
+ mustNot: ["Do not treat defaults as stronger than captured references"]
10525
+ },
10526
+ [INSPIREDESIGN_HANDOFF_FILES.designMarkdown]: {
10527
+ purpose: "Human-readable design specification and implementation narrative.",
10528
+ expectedContents: ["inspiration analysis", "unified direction", "governance summary", "deliverables"],
10529
+ howToUse: ["Use as the readable project brief", "cross-check implementation choices against its sections"],
10530
+ mustNot: ["Do not use prose as a substitute for the JSON contract when patching Canvas"]
10531
+ },
10532
+ [INSPIREDESIGN_HANDOFF_FILES.designContract]: {
10533
+ purpose: "Narrowed Canvas governance contract for design decisions.",
10534
+ expectedContents: ["emitted governance blocks", "motion system", "library policy", "runtime budgets"],
10535
+ howToUse: ["Patch only emitted governance blocks", "compare implementation against this contract before shipping"],
10536
+ mustNot: ["Do not add navigation, async, or performance context as Canvas governance patches"]
10537
+ },
10538
+ [INSPIREDESIGN_HANDOFF_FILES.canvasPlanRequest]: {
10539
+ purpose: "Ready-to-fill request payload for `canvas.plan.set`.",
10540
+ expectedContents: ["request ids", "Canvas session ids", "mutation-safe generationPlan"],
10541
+ howToUse: ["Fill canvasSessionId, leaseId, and documentId", "submit with the provided canvas.plan.set command"],
10542
+ mustNot: ["Do not add handoff-only fields or reference-only analysis to generationPlan"]
10543
+ },
10544
+ [INSPIREDESIGN_HANDOFF_FILES.designAgentHandoff]: {
10545
+ purpose: "Downstream index for artifact usage, skills, commands, and omitted implementation context.",
10546
+ expectedContents: ["skills", "commands", "contract scope", "implementation context", "artifact and section guides"],
10547
+ howToUse: ["Use as the navigation map for the bundle", "load recommended skills before implementation"],
10548
+ mustNot: ["Do not treat handoff context as runtime Canvas schema"]
10549
+ },
10550
+ [INSPIREDESIGN_HANDOFF_FILES.generationPlan]: {
10551
+ purpose: "Full generated plan for reasoning about design intent.",
10552
+ expectedContents: ["Canvas plan fields", "design vectors", "reference analysis when available"],
10553
+ howToUse: ["Use for agent reasoning and audit traceability", "compare with canvas-plan.request.json for runtime subset"],
10554
+ mustNot: ["Do not submit this file directly to Canvas when it contains non-request context"]
10555
+ },
10556
+ [INSPIREDESIGN_HANDOFF_FILES.implementationPlanMarkdown]: {
10557
+ purpose: "Human-readable engineering sequence for the first implementation pass.",
10558
+ expectedContents: ["build sequence", "component plan", "token strategy", "QA and risk checks"],
10559
+ howToUse: ["Convert sections into implementation tasks", "keep tests and browser validation aligned to the plan"],
10560
+ mustNot: ["Do not implement sections unsupported by brief or reference evidence"]
10561
+ },
10562
+ [INSPIREDESIGN_HANDOFF_FILES.implementationPlan]: {
10563
+ purpose: "Machine-readable implementation plan matching the Markdown plan.",
10564
+ expectedContents: ["architecture steps", "component inventory", "state and validation tasks"],
10565
+ howToUse: ["Use for structured task extraction", "keep it synchronized with implementation-plan.md"],
10566
+ mustNot: ["Do not treat it as a Canvas document patch payload"]
10567
+ },
10568
+ [INSPIREDESIGN_HANDOFF_FILES.evidence]: {
10569
+ purpose: "Evidence digest for brief, reference, capture, and design-vector provenance.",
10570
+ expectedContents: ["brief expansion", "reference outcomes", "capture attempts", "design vectors"],
10571
+ howToUse: ["Audit why choices were made", "prefer evidence over generic template defaults"],
10572
+ mustNot: ["Do not ignore failed or skipped capture statuses when judging confidence"]
10573
+ },
10574
+ [INSPIREDESIGN_HANDOFF_FILES.prototypeGuidance]: {
10575
+ purpose: "Optional first prototype guidance when the workflow requests prototype output.",
10576
+ expectedContents: ["prototype structure", "design-vector guidance", "browser proof checklist"],
10577
+ howToUse: ["Use only for the first prototype pass", "promote proven ideas back into contract-aligned work"],
10578
+ mustNot: ["Do not treat prototype guidance as final implementation authority"]
10579
+ }
10580
+ };
10581
+ var INSPIREDESIGN_CONTRACT_SECTION_GUIDE = {
10582
+ intent: {
10583
+ purpose: "Define why the design exists and what success means.",
10584
+ expectedContents: ["audience", "task", "success criteria", "trust posture"],
10585
+ howToUse: ["Validate the primary user job before styling", "reject sections that do not serve the task"],
10586
+ mustNot: ["Do not start visual polish before the audience and task are clear"]
10587
+ },
10588
+ generationPlan: {
10589
+ purpose: "Mutation-safe subset accepted by Canvas planning.",
10590
+ expectedContents: ["target outcome", "visual, layout, content, component, motion, responsive, accessibility posture"],
10591
+ howToUse: ["Submit only through canvas-plan.request.json", "repair generationPlanIssues before mutation"],
10592
+ mustNot: ["Do not add handoff-only guide fields to the Canvas generation plan"]
10593
+ },
10594
+ designLanguage: {
10595
+ purpose: "Name the coherent visual direction and token ownership.",
10596
+ expectedContents: ["direction", "style axes", "semantic token source", "approved libraries"],
10597
+ howToUse: ["Keep one design language per task", "align repeated components to semantic tokens"],
10598
+ mustNot: ["Do not mix unrelated visual families inside one surface"]
10599
+ },
10600
+ contentModel: {
10601
+ purpose: "Define real content, message hierarchy, and UI states.",
10602
+ expectedContents: ["primary message", "supporting messages", "states", "loading, empty, and error behavior"],
10603
+ howToUse: ["Use real content first", "plan non-happy-path states before polish"],
10604
+ mustNot: ["Do not ship placeholder copy as product content"]
10605
+ },
10606
+ layoutSystem: {
10607
+ purpose: "Describe page architecture and section rhythm.",
10608
+ expectedContents: ["grid", "containers", "spacing rhythm", "alignment rules"],
10609
+ howToUse: ["Use to place sections and scan units consistently", "verify desktop and mobile structure"],
10610
+ mustNot: ["Do not invent one-off layout rules for repeated sections"]
10611
+ },
10612
+ typographySystem: {
10613
+ purpose: "Define type families, scale, measure, and loading behavior.",
10614
+ expectedContents: ["families", "scale", "measure", "fallback policy", "loading strategy"],
10615
+ howToUse: ["Apply type hierarchy consistently", "avoid layout shift from font loading"],
10616
+ mustNot: ["Do not default to unapproved system stacks for a distinctive design"]
10617
+ },
10618
+ colorSystem: {
10619
+ purpose: "Define semantic color roles and theme behavior.",
10620
+ expectedContents: ["primary roles", "surface roles", "text roles", "state colors"],
10621
+ howToUse: ["Map repeated UI to semantic tokens", "validate contrast in every required theme"],
10622
+ mustNot: ["Do not scatter raw color values across leaf components"]
10623
+ },
10624
+ surfaceSystem: {
10625
+ purpose: "Define material, depth, borders, and background behavior.",
10626
+ expectedContents: ["surface hierarchy", "border rules", "shadow rules", "material effects"],
10627
+ howToUse: ["Use depth only to clarify hierarchy", "align material effects with design vectors"],
10628
+ mustNot: ["Do not turn every content group into a card by default"]
10629
+ },
10630
+ iconSystem: {
10631
+ purpose: "Define icon usage and decorative asset boundaries.",
10632
+ expectedContents: ["icon family", "stroke policy", "labeling rules", "decorative rules"],
10633
+ howToUse: ["Use icons to clarify actions", "keep accessible names on icon-only controls"],
10634
+ mustNot: ["Do not rely on icons as the only explanation for critical actions"]
10635
+ },
10636
+ motionSystem: {
10637
+ purpose: "Define motion that supports comprehension.",
10638
+ expectedContents: ["timing", "interaction moments", "reduced-motion posture", "advanced motion advisory"],
10639
+ howToUse: ["Keep shader, WebGL, and Spline cues advisory", "provide reduced-motion replacements"],
10640
+ mustNot: ["Do not use motion cues to authorize new runtime libraries"]
10641
+ },
10642
+ responsiveSystem: {
10643
+ purpose: "Define authored behavior across desktop, tablet, and mobile.",
10644
+ expectedContents: ["breakpoints", "adaptation rules", "touch policy", "overflow policy"],
10645
+ howToUse: ["Validate the primary action at every viewport", "collapse structure before copy becomes cramped"],
10646
+ mustNot: ["Do not assume desktop layouts naturally scale down"]
10647
+ },
10648
+ accessibilityPolicy: {
10649
+ purpose: "Set accessibility requirements before implementation.",
10650
+ expectedContents: ["WCAG target", "keyboard requirements", "focus policy", "semantic requirements"],
10651
+ howToUse: ["Block release on contrast or keyboard regressions", "validate focus on every interactive state"],
10652
+ mustNot: ["Do not defer accessibility until after visual implementation"]
10653
+ },
10654
+ libraryPolicy: {
10655
+ purpose: "Declare approved implementation libraries and runtime boundaries.",
10656
+ expectedContents: ["components", "icons", "styling", "motion", "threeD"],
10657
+ howToUse: ["Use as the dependency authorization boundary", "keep motion and threeD empty unless separately approved"],
10658
+ mustNot: ["Do not infer WebGL, shader, Spline, or 3D runtime support from advisory motion"]
10659
+ },
10660
+ runtimeBudgets: {
10661
+ purpose: "Set practical limits for sections, actions, interaction latency, and preview cost.",
10662
+ expectedContents: ["section budgets", "action budgets", "latency budgets", "preview notes"],
10663
+ howToUse: ["Use as a constraint during implementation", "validate slow or animation-heavy surfaces against it"],
10664
+ mustNot: ["Do not add decorative weight that violates the budget"]
10665
+ },
10666
+ navigationModel: {
10667
+ purpose: "Implementation-only context for route, tab, overlay, and deep-link ownership.",
10668
+ expectedContents: ["route owner", "deep-link policy", "invalid route fallback", "overlay entry points"],
10669
+ howToUse: ["Use from design-agent-handoff.json when wiring implementation state"],
10670
+ mustNot: ["Do not patch this omitted block into Canvas governance"]
10671
+ },
10672
+ asyncModel: {
10673
+ purpose: "Implementation-only context for loading, restart, cancellation, and URL-owned query state.",
10674
+ expectedContents: ["owner", "load trigger", "restart triggers", "cancellation policy"],
10675
+ howToUse: ["Use when wiring fetch/search state and stale-request handling"],
10676
+ mustNot: ["Do not let components invent independent async ownership"]
10677
+ },
10678
+ performanceModel: {
10679
+ purpose: "Implementation-only context for render hotspots and measurement posture.",
10680
+ expectedContents: ["render hotspots", "stable identity policy", "list strategy", "measurement plan"],
10681
+ howToUse: ["Use before building scan-heavy or motion-heavy surfaces"],
10682
+ mustNot: ["Do not ship heavy interaction surfaces without measurement evidence"]
10683
+ }
10684
+ };
10162
10685
  var buildInspiredesignFollowthroughSummary = () => `Read ${INSPIREDESIGN_HANDOFF_FILES.advancedBrief} first, then continue in OpenDevBrowser Canvas with ${INSPIREDESIGN_HANDOFF_FILES.canvasPlanRequest} and ${INSPIREDESIGN_HANDOFF_FILES.designAgentHandoff}, load ${INSPIREDESIGN_HANDOFF_RECOMMENDED_SKILLS[0]} plus ${INSPIREDESIGN_HANDOFF_RECOMMENDED_SKILLS[1]} before implementation, and note that any supplied reference URL already uses captureMode=deep.`;
10163
10686
  var buildInspiredesignNextStep = () => `Read ${INSPIREDESIGN_HANDOFF_FILES.advancedBrief} first. ${INSPIREDESIGN_HANDOFF_GUIDANCE.prepareCanvasPlanRequest} Then run ${INSPIREDESIGN_HANDOFF_COMMANDS.continueInCanvas}, confirm planStatus=accepted, then patch only the governance blocks listed in ${INSPIREDESIGN_HANDOFF_FILES.designAgentHandoff}.`;
10164
10687
 
@@ -10169,11 +10692,14 @@ var inspiredesign_advanced_brief_v1_default = {
10169
10692
  commonRules: [
10170
10693
  "Preserve only the product, brand, audience, platform, and tone cues that are explicitly present in the source brief.",
10171
10694
  "Treat missing details as open constraints instead of inventing brand facts or user needs.",
10172
- "Use inspiration references to extract transferable design logic rather than copying a single layout, visual treatment, or narrative beat literally."
10695
+ "Use inspiration references to extract transferable design logic rather than copying a single layout, visual treatment, or narrative beat literally.",
10696
+ "Treat shader, WebGL, Spline-style, and 3D motion ideas as advisory design cues only, not as runtime support or dependency approval.",
10697
+ "Carry accepted advanced motion cues in designVectors and motionSystem as intent, and keep libraryPolicy authorization separate."
10173
10698
  ],
10174
10699
  outputRequirements: [
10175
10700
  "Return a reusable design contract that can drive design.md, implementation-plan artifacts, and a Canvas-ready prototype path.",
10176
- "Keep the direction premium, specific, and implementable instead of generic or template-driven."
10701
+ "Keep the direction premium, specific, and implementable instead of generic or template-driven.",
10702
+ "Keep libraryPolicy.motion and libraryPolicy.threeD empty in generated samples unless a separate runtime change explicitly approves those libraries."
10177
10703
  ],
10178
10704
  formats: [
10179
10705
  {
@@ -11765,6 +12291,13 @@ var expandInspiredesignBrief = (brief, preferredFormatId) => {
11765
12291
  var SIGNAL_LIMIT = 5;
11766
12292
  var SIGNAL_CLIP = 180;
11767
12293
  var PATTERN_LIMIT = 6;
12294
+ var ADVANCED_MOTION_FIELDS = [
12295
+ "Advisory shader-style gradients: specify effect type, uniforms, static fallback, and reduced-motion replacement as design language only.",
12296
+ "Advisory WebGL-style depth cues: describe layered depth, camera-like parallax, and spatial hierarchy without requiring WebGL runtime.",
12297
+ "Advisory Spline-style staging: describe object-like hero composition, scene count, camera posture, depth model, asset source, and spatial sequencing as implementation guidance only.",
12298
+ "Advanced motion performance policy: define frame budget, lazy loading, offscreen pause behavior, and vestibular risk before implementation.",
12299
+ "Runtime boundary: implement with approved CSS and Canvas-safe primitives unless explicit source-owned runtime support is added later."
12300
+ ];
11768
12301
  var trimText = (value) => value.trim().replace(/\s+/g, " ");
11769
12302
  var clipText = (value, maxLength) => {
11770
12303
  if (value.length <= maxLength) return value;
@@ -12125,6 +12658,7 @@ var buildInspiredesignDesignVectors = (format, board) => {
12125
12658
  interactionDensity: buildInteractionDensity(format, board),
12126
12659
  interactionMoments: buildInteractionMoments(format, board),
12127
12660
  materialEffects: buildMaterialEffects(board),
12661
+ advancedMotionAdvisory: [...ADVANCED_MOTION_FIELDS],
12128
12662
  referenceInfluence: influence,
12129
12663
  patternsToBorrow: board.references.flatMap((entry) => entry.patternsToBorrow).slice(0, 8),
12130
12664
  patternsToReject: board.references.flatMap((entry) => entry.patternsToReject).slice(0, 8),
@@ -12512,7 +13046,8 @@ var renderReferenceFirstAdvancedBrief = (briefExpansion, board, vectors, referen
12512
13046
  `motionPosture: ${vectors.motionPosture.join(" ")}`,
12513
13047
  `sectionArchitecture: ${vectors.sectionArchitecture.join(" ")}`,
12514
13048
  `interactionMoments: ${vectors.interactionMoments.join(" ")}`,
12515
- `materialEffects: ${vectors.materialEffects.join(" ")}`
13049
+ `materialEffects: ${vectors.materialEffects.join(" ")}`,
13050
+ `advancedMotionAdvisory: ${vectors.advancedMotionAdvisory.join(" ")}`
12516
13051
  ]),
12517
13052
  "",
12518
13053
  "Fixed format guardrails:",
@@ -12598,7 +13133,8 @@ var summarizeDesignVectors = (designVectors) => [
12598
13133
  `sections: ${designVectors.sectionArchitecture.join(" ")}`,
12599
13134
  `motion: ${designVectors.motionPosture.slice(0, 1).join(" ")}`,
12600
13135
  `interactions: ${designVectors.interactionMoments.slice(0, 1).join(" ")}`,
12601
- `materials: ${designVectors.materialEffects.slice(0, 1).join(" ")}`
13136
+ `materials: ${designVectors.materialEffects.slice(0, 1).join(" ")}`,
13137
+ `advancedMotion: ${designVectors.advancedMotionAdvisory.slice(0, 1).join(" ")}`
12602
13138
  ].join(" ");
12603
13139
  var isReferenceFirstPublicLanding = (designVectors) => {
12604
13140
  return designVectors.sourcePriority === "reference-evidence-first" && designVectors.surfaceIntent.toLowerCase().includes("public landing page");
@@ -12620,20 +13156,244 @@ var buildEvidenceDerivedFormat = (format, designVectors) => {
12620
13156
  }
12621
13157
  };
12622
13158
  };
12623
- var buildGenerationPlan = ({
13159
+ var TARGET_KIND_ORDER = ["page", "component", "asset"];
13160
+ var TARGET_CONFIDENCE = {
13161
+ defaultPage: 0.55,
13162
+ pageIntentStep: 0.05,
13163
+ nonPageBase: 0.55,
13164
+ intentStep: 0.08,
13165
+ supportingStep: 0.05,
13166
+ maxPage: 0.7,
13167
+ maxNonPage: 0.95
13168
+ };
13169
+ var TARGET_SIGNAL_PROFILES = {
13170
+ page: {
13171
+ intent: ["page", "landing page", "website", "homepage", "dashboard", "workspace", "screen", "flow", "surface", "microsite"],
13172
+ supporting: ["section", "sections", "navigation", "footer", "hero section", "conversion flow"],
13173
+ incidental: ["card", "cards", "button", "buttons", "image", "images", "media", "hero", "cta", "background"]
13174
+ },
13175
+ component: {
13176
+ intent: ["component", "component family", "reusable component", "component prototype", "hero component", "card component", "storybook"],
13177
+ supporting: ["prop", "props", "slot", "slots", "variant", "variants", "state matrix", "hover", "focus", "disabled", "loading", "error", "fixture", "fixtures", "arg", "args"],
13178
+ incidental: ["card", "cards", "button", "buttons", "form", "modal", "drawer", "navbar", "hero", "input", "tabs", "cta"]
13179
+ },
13180
+ asset: {
13181
+ intent: ["asset", "asset pack", "visual asset", "icon pack", "logo pack", "illustration set", "artwork set"],
13182
+ supporting: ["responsive variant", "responsive variants", "responsive artwork", "provenance", "usage rules", "alt text", "replacement rules", "tokenized usage", "source asset", "source assets"],
13183
+ incidental: ["icon", "icons", "illustration", "illustrations", "logo", "logos", "media", "image", "images", "background", "texture", "sprite", "artwork"]
13184
+ }
13185
+ };
13186
+ var buildTargetCorpus = (brief, references, synthesis) => [
12624
13187
  brief,
12625
- format,
12626
- synthesis,
12627
- referencePatternBoard,
12628
- designVectors
12629
- }) => {
12630
- const plan = cloneTemplate(BASE_GENERATION_PLAN);
12631
- const profile = format.route.profile;
12632
- const vectorSummary = summarizeDesignVectors(designVectors);
12633
- plan.targetOutcome.summary = clipText2(
12634
- `${summarizeBrief(brief)} Reference cues: ${synthesis.summary} ${vectorSummary}`,
12635
- GENERATION_PLAN_REFERENCE_CLIP_LENGTH
12636
- );
13188
+ synthesis.lines.join(" "),
13189
+ ...references.filter(hasInspiredesignUsableReferenceEvidence).flatMap((reference) => getInspiredesignReferenceSignals(reference))
13190
+ ].join(" ").toLowerCase();
13191
+ var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13192
+ var matchesTargetSignal = (corpus, signal) => {
13193
+ const pattern = escapeRegex(signal.toLowerCase()).replace(/\s+/g, "\\s+");
13194
+ return new RegExp(`(^|[^a-z0-9])${pattern}([^a-z0-9]|$)`).test(corpus);
13195
+ };
13196
+ var findTargetSignalIndex = (corpus, signal) => {
13197
+ const pattern = escapeRegex(signal.toLowerCase()).replace(/\s+/g, "\\s+");
13198
+ return new RegExp(`(^|[^a-z0-9])${pattern}([^a-z0-9]|$)`).exec(corpus)?.index ?? null;
13199
+ };
13200
+ var findFirstBriefTargetKind = (brief) => {
13201
+ const corpus = brief.toLowerCase();
13202
+ const hits = TARGET_KIND_ORDER.flatMap((kind) => TARGET_SIGNAL_PROFILES[kind].intent.map((signal) => findTargetSignalIndex(corpus, signal)).filter((index) => index !== null).map((index) => ({ kind, index })));
13203
+ return hits.sort((left, right) => left.index - right.index)[0]?.kind ?? null;
13204
+ };
13205
+ var collectTargetSignals = (corpus) => {
13206
+ const collect = (kind) => {
13207
+ const profile = TARGET_SIGNAL_PROFILES[kind];
13208
+ return {
13209
+ intent: profile.intent.filter((signal) => matchesTargetSignal(corpus, signal)),
13210
+ supporting: profile.supporting.filter((signal) => matchesTargetSignal(corpus, signal)),
13211
+ incidental: profile.incidental.filter((signal) => matchesTargetSignal(corpus, signal))
13212
+ };
13213
+ };
13214
+ return {
13215
+ page: collect("page"),
13216
+ component: collect("component"),
13217
+ asset: collect("asset")
13218
+ };
13219
+ };
13220
+ var clampTargetConfidence = (value, max) => Math.min(max, Number(value.toFixed(2)));
13221
+ var buildPageConfidence = (signals) => {
13222
+ return clampTargetConfidence(
13223
+ TARGET_CONFIDENCE.defaultPage + signals.intent.length * TARGET_CONFIDENCE.pageIntentStep,
13224
+ TARGET_CONFIDENCE.maxPage
13225
+ );
13226
+ };
13227
+ var buildTargetEligibility = (kind, briefSignals, supportSignals) => {
13228
+ const eligible = briefSignals.intent.length > 0 && supportSignals.supporting.length > 0;
13229
+ const confidence = eligible ? clampTargetConfidence(
13230
+ TARGET_CONFIDENCE.nonPageBase + briefSignals.intent.length * TARGET_CONFIDENCE.intentStep + supportSignals.supporting.length * TARGET_CONFIDENCE.supportingStep,
13231
+ TARGET_CONFIDENCE.maxNonPage
13232
+ ) : 0;
13233
+ return {
13234
+ kind,
13235
+ eligible,
13236
+ confidence,
13237
+ triggeringSignals: [
13238
+ ...briefSignals.intent.map((signal) => `${kind} intent: ${signal}`),
13239
+ ...supportSignals.supporting.map((signal) => `${kind} support: ${signal}`)
13240
+ ]
13241
+ };
13242
+ };
13243
+ var choosePrimaryTargetKind = (pageConfidence, pageSignals, firstBriefTargetKind, component, asset) => {
13244
+ const eligibleTargets = [component, asset].filter((target) => target.eligible);
13245
+ if (eligibleTargets.length === 0) return { primaryKind: "page", reason: "no_non_page_gate" };
13246
+ if (firstBriefTargetKind === "page" && pageSignals.intent.length > 0) {
13247
+ return { primaryKind: "page", reason: "page_first_brief_target" };
13248
+ }
13249
+ const [first, second] = eligibleTargets.sort((left, right) => right.confidence - left.confidence);
13250
+ if (!first || first.confidence <= pageConfidence) {
13251
+ return { primaryKind: "page", reason: "non_page_did_not_beat_page" };
13252
+ }
13253
+ if (second && first.confidence === second.confidence && pageSignals.intent.length > 0) {
13254
+ return { primaryKind: "page", reason: "page_tie_break" };
13255
+ }
13256
+ return { primaryKind: first.kind, reason: "non_page_selected" };
13257
+ };
13258
+ var chooseTargetKinds = (primaryKind, component, asset) => {
13259
+ if (primaryKind === "page") return ["page"];
13260
+ const eligible = new Set([component, asset].filter((target) => target.eligible).map((target) => target.kind));
13261
+ const secondaryKinds = TARGET_KIND_ORDER.filter((kind) => kind !== primaryKind && eligible.has(kind));
13262
+ return [primaryKind, ...secondaryKinds];
13263
+ };
13264
+ var buildTriggeringSignals = (decision, signals, component, asset) => {
13265
+ const pageSignals = signals.page.intent.map((signal) => `page intent: ${signal}`);
13266
+ const eligibleTargets = [component, asset].filter((target) => target.eligible);
13267
+ const targetIntents = eligibleTargets.flatMap((target) => target.triggeringSignals.filter((signal) => signal.includes(" intent: ")));
13268
+ const targetSupport = eligibleTargets.flatMap((target) => target.triggeringSignals.filter((signal) => signal.includes(" support: ")));
13269
+ const defaultSignal = decision.primaryKind === "page" ? [buildPageDecisionSignal(decision.reason)] : [];
13270
+ return [...pageSignals, ...targetIntents, ...targetSupport, ...defaultSignal].slice(0, 12);
13271
+ };
13272
+ var buildPageDecisionSignal = (reason) => {
13273
+ if (reason === "no_non_page_gate") return "page default: non-page targets did not clear brief intent plus support gates";
13274
+ if (reason === "non_page_did_not_beat_page") return "page default: non-page targets did not beat page confidence";
13275
+ if (reason === "page_first_brief_target") return "page default: page was the first explicit target in the brief";
13276
+ if (reason === "page_tie_break") return "page default: page intent won a tied non-page confidence score";
13277
+ return "page default: page selected";
13278
+ };
13279
+ var getTargetConfidence = (primaryKind, pageConfidence, component, asset) => {
13280
+ if (primaryKind === "component") return component.confidence;
13281
+ if (primaryKind === "asset") return asset.confidence;
13282
+ return pageConfidence;
13283
+ };
13284
+ var buildTargetEvidenceBuckets = (primaryKind, format, designVectors) => ({
13285
+ anatomy: [`Map ${primaryKind} anatomy before styling: root, content hierarchy, interaction zones, and supporting regions.`],
13286
+ propsSlots: [`Define props/slots from ${format.componentGrammar}; separate data props, content slots, and visual variant controls.`],
13287
+ stateMatrix: ["Cover default, hover, focus, active, disabled, loading, empty, error, success, and selected where relevant."],
13288
+ tokens: [`Resolve typography, color, spacing, radius, shadow, motion, and z-index through semantic tokens for ${format.paletteIntent}.`],
13289
+ assets: [`Inventory source assets, derived assets, usage rights, responsive variants, and replacement notes for ${primaryKind} prototypes.`],
13290
+ accessibility: ["Validate keyboard order, visible focus, accessible names, ARIA pattern fit, contrast, and WCAG 2.2 states."],
13291
+ motion: [`Use ${format.motionGrammar}; include reduced-motion alternatives for ${designVectors.motionPosture.join(" ") || "all transitions"}.`],
13292
+ previewFixtures: [`Build isolated preview fixtures for ${primaryKind} default, responsive, reduced-motion, and failure states.`]
13293
+ });
13294
+ var buildComponentTargetAnalysis = (briefHash, format) => ({
13295
+ canvasType: "CanvasComponentInventoryItem",
13296
+ inventoryItems: [{
13297
+ id: `component_${briefHash}`,
13298
+ name: `${format.label} Component`,
13299
+ componentName: `${format.route.profile.replace(/-/g, "")}PrototypeComponent`,
13300
+ description: `Reusable component prototype derived from ${format.componentGrammar}.`,
13301
+ sourceFamily: "framework_component",
13302
+ origin: "code_sync",
13303
+ variants: [{
13304
+ id: "default",
13305
+ name: "Default",
13306
+ selector: { interaction: "default" },
13307
+ description: "Default preview fixture.",
13308
+ metadata: {}
13309
+ }],
13310
+ props: [
13311
+ { name: "variant", type: "string", required: false, description: "Visual variant key.", metadata: {} },
13312
+ { name: "state", type: "string", required: false, description: "Interaction state fixture.", metadata: {} }
13313
+ ],
13314
+ slots: [
13315
+ { name: "media", description: "Optional visual or icon slot.", allowedKinds: ["asset", "image", "icon"], metadata: {} },
13316
+ { name: "content", description: "Primary text or rich content slot.", allowedKinds: ["text", "rich-text"], metadata: {} }
13317
+ ],
13318
+ events: [{ name: "onPrimaryAction", description: "Primary interaction callback.", payloadShape: {}, metadata: {} }],
13319
+ content: { acceptsText: true, acceptsRichText: true, slotNames: ["media", "content"], metadata: {} },
13320
+ metadata: { targetKind: "component" }
13321
+ }],
13322
+ prototypeGuidance: [
13323
+ "Component prototype target: document anatomy, props/slots, variant rules, and interaction state fixtures before page composition.",
13324
+ "Use Storybook-style args and interaction checks when converting this guidance into executable component previews."
13325
+ ]
13326
+ });
13327
+ var buildAssetTargetAnalysis = (briefHash) => ({
13328
+ canvasType: "CanvasAsset",
13329
+ assets: [{
13330
+ id: `asset_${briefHash}`,
13331
+ sourceType: "page-derived",
13332
+ kind: "visual-asset",
13333
+ url: null,
13334
+ status: "needs-production-source",
13335
+ provenanceNotes: ["Derived from brief/reference evidence; recreate rather than copy proprietary source assets."],
13336
+ usageNotes: ["Define responsive variants, token usage, alt text, and replacement rules before implementation."],
13337
+ metadata: { targetKind: "asset" }
13338
+ }],
13339
+ prototypeGuidance: [
13340
+ "Asset prototype target: catalog provenance, variants, token usage, responsive behavior, and replacement rules.",
13341
+ "Pair each visual asset with preview fixtures for default, high contrast, and reduced-motion contexts when relevant."
13342
+ ]
13343
+ });
13344
+ var buildPageTargetAnalysis = (format, designVectors) => ({
13345
+ canvasType: "CanvasPage",
13346
+ assemblyFocus: [
13347
+ format.layoutArchetype,
13348
+ ...designVectors.sectionArchitecture
13349
+ ],
13350
+ implementationNotes: [
13351
+ "Page prototype target: validate section order, navigation model, CTA visibility, responsive collapse, and reduced-motion behavior.",
13352
+ "Use component primitives before page-specific wrappers."
13353
+ ]
13354
+ });
13355
+ var buildTargetAnalysis = (brief, format, references, synthesis, designVectors) => {
13356
+ const briefSignals = collectTargetSignals(brief.toLowerCase());
13357
+ const supportSignals = collectTargetSignals(buildTargetCorpus(brief, references, synthesis));
13358
+ const pageConfidence = buildPageConfidence(briefSignals.page);
13359
+ const component = buildTargetEligibility("component", briefSignals.component, supportSignals.component);
13360
+ const asset = buildTargetEligibility("asset", briefSignals.asset, supportSignals.asset);
13361
+ const decision = choosePrimaryTargetKind(
13362
+ pageConfidence,
13363
+ briefSignals.page,
13364
+ findFirstBriefTargetKind(brief),
13365
+ component,
13366
+ asset
13367
+ );
13368
+ const primaryKind = decision.primaryKind;
13369
+ const kinds = chooseTargetKinds(primaryKind, component, asset);
13370
+ const briefHash = referenceFingerprint(brief);
13371
+ return {
13372
+ primaryKind,
13373
+ kinds,
13374
+ confidence: getTargetConfidence(primaryKind, pageConfidence, component, asset),
13375
+ triggeringSignals: buildTriggeringSignals(decision, briefSignals, component, asset),
13376
+ evidenceBuckets: buildTargetEvidenceBuckets(primaryKind, format, designVectors),
13377
+ ...kinds.includes("page") ? { page: buildPageTargetAnalysis(format, designVectors) } : {},
13378
+ ...kinds.includes("component") ? { component: buildComponentTargetAnalysis(briefHash, format) } : {},
13379
+ ...kinds.includes("asset") ? { asset: buildAssetTargetAnalysis(briefHash) } : {}
13380
+ };
13381
+ };
13382
+ var buildGenerationPlan = ({
13383
+ brief,
13384
+ format,
13385
+ synthesis,
13386
+ referencePatternBoard,
13387
+ designVectors,
13388
+ targetAnalysis
13389
+ }) => {
13390
+ const plan = cloneTemplate(BASE_GENERATION_PLAN);
13391
+ const profile = format.route.profile;
13392
+ const vectorSummary = summarizeDesignVectors(designVectors);
13393
+ plan.targetOutcome.summary = clipText2(
13394
+ `${summarizeBrief(brief)} Reference cues: ${synthesis.summary} ${vectorSummary}`,
13395
+ GENERATION_PLAN_REFERENCE_CLIP_LENGTH
13396
+ );
12637
13397
  plan.visualDirection.profile = profile;
12638
13398
  plan.visualDirection.themeStrategy = format.route.themeStrategy;
12639
13399
  plan.layoutStrategy.approach = format.route.layoutApproach;
@@ -12652,6 +13412,7 @@ var buildGenerationPlan = ({
12652
13412
  ...plan,
12653
13413
  referencePatternBoard,
12654
13414
  designVectors,
13415
+ targetAnalysis,
12655
13416
  interactionMoments: [...designVectors.interactionMoments],
12656
13417
  materialEffects: [...designVectors.materialEffects]
12657
13418
  };
@@ -12770,6 +13531,8 @@ var buildMotionSystemBlock = (format, designVectors) => {
12770
13531
  posture: [...designVectors.motionPosture],
12771
13532
  interactionMoments: [...designVectors.interactionMoments],
12772
13533
  materialEffects: [...designVectors.materialEffects],
13534
+ advancedMotionAdvisory: [...designVectors.advancedMotionAdvisory],
13535
+ advancedMotionRuntimePolicy: "Advanced motion fields are advisory contract metadata only and do not authorize shader, WebGL, Spline, R3F, Pixi, Babylon, or GLSL runtime support.",
12773
13536
  parallaxPolicy: "Use parallax only as a restrained hierarchy cue and remove transform-based depth for reduced-motion users.",
12774
13537
  hoverPolicy: "Hover effects must clarify clickability without becoming the only visible affordance.",
12775
13538
  cursorPolicy: "Cursor effects are allowed only on premium hero or CTA moments and must not interfere with reading or form controls.",
@@ -12803,7 +13566,7 @@ var buildLibraryPolicyBlock = () => ({
12803
13566
  components: ["shadcn"],
12804
13567
  icons: ["tabler"],
12805
13568
  styling: ["tailwindcss"],
12806
- motion: ["css"],
13569
+ motion: [],
12807
13570
  threeD: []
12808
13571
  });
12809
13572
  var buildRuntimeBudgetsBlock = (plan) => ({
@@ -12893,10 +13656,13 @@ var buildFollowthrough = ({
12893
13656
  synthesis,
12894
13657
  includePrototypeGuidance,
12895
13658
  referencePatternBoard,
12896
- designVectors
13659
+ designVectors,
13660
+ targetAnalysis
12897
13661
  }) => ({
12898
13662
  summary: buildInspiredesignFollowthroughSummary(),
12899
13663
  nextStep: buildInspiredesignNextStep(),
13664
+ artifactGuide: INSPIREDESIGN_ARTIFACT_GUIDE,
13665
+ contractSectionGuide: INSPIREDESIGN_CONTRACT_SECTION_GUIDE,
12900
13666
  briefExpansion: buildBriefExpansionMetadata(briefExpansion),
12901
13667
  recommendedSkills: [...INSPIREDESIGN_HANDOFF_RECOMMENDED_SKILLS],
12902
13668
  commandExamples: { ...INSPIREDESIGN_HANDOFF_COMMANDS },
@@ -12911,7 +13677,8 @@ var buildFollowthrough = ({
12911
13677
  cues: synthesis.lines
12912
13678
  },
12913
13679
  referencePatternBoard,
12914
- designVectors
13680
+ designVectors,
13681
+ targetAnalysis
12915
13682
  }
12916
13683
  });
12917
13684
  var buildDesignContract = ({
@@ -13015,6 +13782,7 @@ var buildImplementationPlan = ({
13015
13782
  ...designVectors.motionPosture,
13016
13783
  ...designVectors.interactionMoments,
13017
13784
  ...designVectors.materialEffects,
13785
+ ...designVectors.advancedMotionAdvisory,
13018
13786
  "Implement hero entrance reveal, section scroll reveal, and CTA/focus feedback as the minimum motion system for landing pages.",
13019
13787
  "Use @media (prefers-reduced-motion: reduce) to preserve hierarchy without motion.",
13020
13788
  "Preserve layout during loading and keep transient confirmations out of the main flow.",
@@ -13237,7 +14005,32 @@ var renderImplementationMarkdown = (implementationPlan) => {
13237
14005
  implementationPlan.buildSequence.map((step, index) => `${index + 1}. ${step}`).join("\n")
13238
14006
  ].join("\n");
13239
14007
  };
13240
- var renderPrototypeGuidance = (profile, synthesis, designVectors) => {
14008
+ var renderTargetAnalysisGuidance = (targetAnalysis) => {
14009
+ const targetGuidance = [
14010
+ ...targetAnalysis.page?.implementationNotes ?? [],
14011
+ ...targetAnalysis.component?.prototypeGuidance ?? [],
14012
+ ...targetAnalysis.asset?.prototypeGuidance ?? []
14013
+ ];
14014
+ return [
14015
+ "## 6.3 Target Analysis",
14016
+ formatBulletList2([
14017
+ `primary target: ${targetAnalysis.primaryKind}`,
14018
+ `target kinds: ${targetAnalysis.kinds.join(", ")}`,
14019
+ `confidence: ${targetAnalysis.confidence.toFixed(2)}`,
14020
+ `triggering signals: ${targetAnalysis.triggeringSignals.join("; ")}`,
14021
+ ...targetGuidance,
14022
+ `anatomy: ${targetAnalysis.evidenceBuckets.anatomy.join(" ")}`,
14023
+ `props/slots: ${targetAnalysis.evidenceBuckets.propsSlots.join(" ")}`,
14024
+ `state matrix: ${targetAnalysis.evidenceBuckets.stateMatrix.join(" ")}`,
14025
+ `tokens: ${targetAnalysis.evidenceBuckets.tokens.join(" ")}`,
14026
+ `assets: ${targetAnalysis.evidenceBuckets.assets.join(" ")}`,
14027
+ `accessibility: ${targetAnalysis.evidenceBuckets.accessibility.join(" ")}`,
14028
+ `motion: ${targetAnalysis.evidenceBuckets.motion.join(" ")}`,
14029
+ `preview fixtures: ${targetAnalysis.evidenceBuckets.previewFixtures.join(" ")}`
14030
+ ])
14031
+ ].join("\n");
14032
+ };
14033
+ var renderPrototypeGuidance = (profile, synthesis, designVectors, targetAnalysis) => {
13241
14034
  return [
13242
14035
  "# 6. Optional Prototype Plan",
13243
14036
  "",
@@ -13252,6 +14045,10 @@ var renderPrototypeGuidance = (profile, synthesis, designVectors) => {
13252
14045
  `- interaction expectations: ${designVectors.interactionMoments.join(" ")}`,
13253
14046
  `- motion expectations: ${designVectors.motionPosture.join(" ")}`,
13254
14047
  `- material and depth expectations: ${designVectors.materialEffects.join(" ")}`,
14048
+ `- advisory advanced motion: ${designVectors.advancedMotionAdvisory.join(" ")}`,
14049
+ "",
14050
+ renderTargetAnalysisGuidance(targetAnalysis),
14051
+ "",
13255
14052
  "- browser proof: capture desktop and mobile browser screenshots, verify reduced-motion behavior, inspect focus states, and confirm the primary CTA remains visible without overlap.",
13256
14053
  "- HTML skeleton guidance: start with one main landmark, one primary CTA group, and semantic sections that follow the design vector section architecture instead of fixed industry-specific defaults.",
13257
14054
  "- styling approach: define CSS variables for timing, easing, elevation, translucency, backdrop blur, cursor effects, hover effects, and parallax distance before mapping components to semantic tokens.",
@@ -13280,7 +14077,8 @@ var buildEvidencePayload = ({
13280
14077
  urls,
13281
14078
  references,
13282
14079
  referencePatternBoard,
13283
- designVectors
14080
+ designVectors,
14081
+ targetAnalysis
13284
14082
  }) => ({
13285
14083
  brief,
13286
14084
  briefHash: referenceFingerprint(brief),
@@ -13294,7 +14092,8 @@ var buildEvidencePayload = ({
13294
14092
  referenceCount: references.length,
13295
14093
  references: references.map((reference) => toReferenceEvidenceJson(reference)),
13296
14094
  referencePatternBoard,
13297
- designVectors
14095
+ designVectors,
14096
+ targetAnalysis
13298
14097
  });
13299
14098
  var toCaptureEvidenceJson = (reference) => {
13300
14099
  const normalized = normalizeInspiredesignCaptureEvidence(reference.capture);
@@ -13336,6 +14135,13 @@ var buildInspiredesignPacket = (input) => {
13336
14135
  );
13337
14136
  const designVectors = buildInspiredesignDesignVectors(selectedFormat, referencePatternBoard);
13338
14137
  const effectiveFormat = buildEvidenceDerivedFormat(selectedFormat, designVectors);
14138
+ const targetAnalysis = buildTargetAnalysis(
14139
+ brief,
14140
+ effectiveFormat,
14141
+ references,
14142
+ synthesis,
14143
+ designVectors
14144
+ );
13339
14145
  const effectiveBriefExpansion = {
13340
14146
  ...input.briefExpansion,
13341
14147
  advancedBrief: isReferenceFirstPublicLanding(designVectors) ? renderEvidenceDerivedAdvancedBrief(input.briefExpansion, effectiveFormat) : input.briefExpansion.advancedBrief,
@@ -13352,7 +14158,8 @@ var buildInspiredesignPacket = (input) => {
13352
14158
  format: effectiveFormat,
13353
14159
  synthesis,
13354
14160
  referencePatternBoard,
13355
- designVectors
14161
+ designVectors,
14162
+ targetAnalysis
13356
14163
  });
13357
14164
  const profile = generationPlan.visualDirection.profile;
13358
14165
  const canvasPlanRequest = buildCanvasPlanRequest(brief, generationPlan);
@@ -13369,7 +14176,8 @@ var buildInspiredesignPacket = (input) => {
13369
14176
  synthesis,
13370
14177
  includePrototypeGuidance,
13371
14178
  referencePatternBoard,
13372
- designVectors
14179
+ designVectors,
14180
+ targetAnalysis
13373
14181
  });
13374
14182
  const implementationPlan = buildImplementationPlan({
13375
14183
  profile,
@@ -13380,7 +14188,7 @@ var buildInspiredesignPacket = (input) => {
13380
14188
  });
13381
14189
  const governanceMarkdown = renderGovernanceMarkdown(designContract, implementationPlan, effectiveFormat);
13382
14190
  const implementationPlanMarkdown = renderImplementationMarkdown(implementationPlan);
13383
- const prototypeGuidanceMarkdown = includePrototypeGuidance ? renderPrototypeGuidance(profile, synthesis, designVectors) : null;
14191
+ const prototypeGuidanceMarkdown = includePrototypeGuidance ? renderPrototypeGuidance(profile, synthesis, designVectors, targetAnalysis) : null;
13384
14192
  const designMarkdown = [
13385
14193
  "# 1. Executive Summary",
13386
14194
  "",
@@ -13416,7 +14224,8 @@ var buildInspiredesignPacket = (input) => {
13416
14224
  `motion posture: ${designVectors.motionPosture.join(" ")}`,
13417
14225
  `section architecture: ${designVectors.sectionArchitecture.join(" ")}`,
13418
14226
  `interaction moments: ${designVectors.interactionMoments.join(" ")}`,
13419
- `material effects: ${designVectors.materialEffects.join(" ")}`
14227
+ `material effects: ${designVectors.materialEffects.join(" ")}`,
14228
+ `advanced motion advisory: ${designVectors.advancedMotionAdvisory.join(" ")}`
13420
14229
  ]),
13421
14230
  "",
13422
14231
  "## 3.4 System Direction",
@@ -13462,11 +14271,167 @@ var buildInspiredesignPacket = (input) => {
13462
14271
  urls,
13463
14272
  references,
13464
14273
  referencePatternBoard,
13465
- designVectors
14274
+ designVectors,
14275
+ targetAnalysis
13466
14276
  })
13467
14277
  };
13468
14278
  };
13469
14279
 
14280
+ // src/providers/workflow-handoff.ts
14281
+ var PRODUCT_VIDEO_BRIEF_HELPER_PATH = "./skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh";
14282
+ var PRODUCT_VIDEO_BRIEF_HELPER_COMMAND = `${PRODUCT_VIDEO_BRIEF_HELPER_PATH} <pack>/manifest.json`;
14283
+ var createSuccessHandoff = (followthroughSummary, suggestedNextAction, suggestedSteps) => ({
14284
+ followthroughSummary,
14285
+ suggestedNextAction,
14286
+ suggestedSteps
14287
+ });
14288
+ var cliExample = (command, args = "") => `npx opendevbrowser ${command}${args ? ` ${args}` : ""}`;
14289
+ var quoteCliValue = (value) => JSON.stringify(value);
14290
+ var buildResearchRerunCommand = (input) => cliExample(
14291
+ "research run",
14292
+ `--topic ${quoteCliValue(input.topic)} --days 14 --source-selection auto --sources web,community --browser-mode ${input.browserMode ?? "managed"} --mode json --output-format json`
14293
+ );
14294
+ var buildShoppingRerunCommand = (input) => {
14295
+ const providers = input.providers?.length ? ` --providers ${input.providers.join(",")}` : " --providers shopping/bestbuy,shopping/ebay";
14296
+ const budget = typeof input.budget === "number" ? ` --budget ${input.budget}` : "";
14297
+ const region = input.region ? ` --region ${quoteCliValue(input.region)}` : "";
14298
+ const browserMode = ` --browser-mode ${input.browserMode ?? "managed"}`;
14299
+ const sort = input.sort ? ` --sort ${input.sort}` : "";
14300
+ return cliExample(
14301
+ "shopping run",
14302
+ `--query ${quoteCliValue(input.query)}${providers}${budget}${region}${browserMode}${sort} --use-cookies --challenge-automation-mode browser_with_helper --mode json --output-format json`
14303
+ );
14304
+ };
14305
+ var buildProductVideoRerunCommand = (input = {}) => {
14306
+ const target = input.productUrl ? `--product-url ${quoteCliValue(input.productUrl)}` : `--product-name ${quoteCliValue(input.productName ?? "<product-name>")}`;
14307
+ const providerHint = input.providerHint ? ` --provider-hint ${input.providerHint}` : "";
14308
+ const screenshots = input.includeScreenshots ? " --include-screenshots" : "";
14309
+ const allImages = input.includeAllImages ? " --include-all-images" : "";
14310
+ const includeCopy = input.includeCopy ? " --include-copy" : "";
14311
+ const browserMode = ` --browser-mode ${input.browserMode ?? "managed"}`;
14312
+ return cliExample(
14313
+ "product-video run",
14314
+ `${target}${providerHint}${screenshots}${allImages}${includeCopy}${browserMode} --use-cookies --challenge-automation-mode browser_with_helper --output-format json`
14315
+ );
14316
+ };
14317
+ var buildMacroResolveArgs = (input, options) => {
14318
+ const defaultProvider = input.defaultProvider ? ` --default-provider ${input.defaultProvider}` : "";
14319
+ const execute = options?.execute ? " --execute" : "";
14320
+ const browserMode = options?.browserMode ? ` --browser-mode ${options.browserMode}` : "";
14321
+ const challenge = options?.challengeAutomationMode ? ` --challenge-automation-mode ${options.challengeAutomationMode}` : "";
14322
+ const outputFormat = options?.includeOutputFormat === false ? "" : " --output-format json";
14323
+ return `--expression ${quoteCliValue(input.expression)}${defaultProvider}${execute}${browserMode}${challenge}${outputFormat}`;
14324
+ };
14325
+ var buildMacroPreviewCommand = (input) => cliExample("macro-resolve", buildMacroResolveArgs(input));
14326
+ var buildMacroExecuteCommand = (input, challengeAutomationMode, browserMode) => cliExample("macro-resolve", buildMacroResolveArgs(input, {
14327
+ execute: true,
14328
+ browserMode,
14329
+ challengeAutomationMode
14330
+ }));
14331
+ var buildResearchSuccessHandoff = (input) => {
14332
+ const rerunCommand = buildResearchRerunCommand(input);
14333
+ return createSuccessHandoff(
14334
+ "Review the ranked records and artifact bundle before turning the result into a publishable claim.",
14335
+ `Open the returned artifact path, inspect the supporting records, and rerun ${rerunCommand} if you need a tighter evidence set.`,
14336
+ [
14337
+ { reason: "Check which records actually support the final claim." },
14338
+ {
14339
+ reason: "Rerun with explicit sources and a narrower timebox if the evidence set is still too broad.",
14340
+ command: rerunCommand
14341
+ }
14342
+ ]
14343
+ );
14344
+ };
14345
+ var buildShoppingSuccessHandoff = (input) => {
14346
+ const rerunCommand = buildShoppingRerunCommand(input);
14347
+ return createSuccessHandoff(
14348
+ "Review the offer set and diagnostics before calling any result a strong deal.",
14349
+ `Inspect the offers and meta.offerFilterDiagnostics, then rerun ${rerunCommand} if you need a tighter comparison.`,
14350
+ [
14351
+ { reason: "Check which offers survived the workflow filters and why." },
14352
+ {
14353
+ reason: "Rerun with explicit providers or updated budget and region inputs if the comparison is still noisy.",
14354
+ command: rerunCommand
14355
+ }
14356
+ ]
14357
+ );
14358
+ };
14359
+ var buildProductVideoSuccessHandoff = (input = {}) => {
14360
+ const rerunCommand = buildProductVideoRerunCommand(input);
14361
+ return createSuccessHandoff(
14362
+ "Review the generated asset pack to confirm whether it is visual-ready or metadata-first before briefing production.",
14363
+ "Open the returned pack path, inspect manifest.json plus copy and features, then run the product-video brief helper with that manifest path to generate production briefs and sourcing notes.",
14364
+ [
14365
+ { reason: "Confirm whether the pack already includes enough images or screenshots for production." },
14366
+ {
14367
+ reason: "Run the product-presentation-asset brief helper on manifest.json to generate the production brief files.",
14368
+ command: PRODUCT_VIDEO_BRIEF_HELPER_COMMAND
14369
+ },
14370
+ {
14371
+ reason: "Rerun the asset workflow with adjusted provider or media flags when the current pack is too thin.",
14372
+ command: rerunCommand
14373
+ },
14374
+ { reason: "Source or capture visuals before final handoff if the pack is metadata-first." }
14375
+ ]
14376
+ );
14377
+ };
14378
+ var buildMacroResolveSuccessHandoff = (input) => {
14379
+ const previewCommand = buildMacroPreviewCommand(input);
14380
+ const executeCommand = buildMacroExecuteCommand(input);
14381
+ const browserRetryCommand = buildMacroExecuteCommand(input, "browser_with_helper", "extension");
14382
+ if (!input.execute) {
14383
+ return createSuccessHandoff(
14384
+ "Review the resolved provider action and provenance before executing the macro.",
14385
+ `Run ${executeCommand} when the resolved action looks correct.`,
14386
+ [
14387
+ { reason: "Inspect resolution.action and resolution.provenance to confirm provider and query shaping." },
14388
+ { reason: "Execute the resolved macro once the plan looks correct.", command: executeCommand },
14389
+ { reason: "Add --default-provider only when you need to force a different provider lane.", command: previewCommand }
14390
+ ]
14391
+ );
14392
+ }
14393
+ if (input.blocked) {
14394
+ return createSuccessHandoff(
14395
+ "Review execution.meta.blocker and failures before retrying the macro.",
14396
+ `Run ${browserRetryCommand} after checking execution.meta.blocker and the current recovery path.`,
14397
+ [
14398
+ { reason: "Inspect execution.meta.blocker and execution.failures before retrying." },
14399
+ { reason: "Retry with browser-scoped challenge automation when the blocker requires live follow-up.", command: browserRetryCommand },
14400
+ { reason: "Preview the resolved action again if you need to switch providers before another execute attempt.", command: previewCommand }
14401
+ ]
14402
+ );
14403
+ }
14404
+ return createSuccessHandoff(
14405
+ "Review execution.records and trace metadata before widening the macro or changing providers.",
14406
+ `Inspect execution.records and execution.meta, then rerun ${previewCommand} if you need a narrower plan.`,
14407
+ [
14408
+ { reason: "Inspect execution.records and execution.meta to confirm the resolved action hit the expected lane." },
14409
+ { reason: "Preview the macro again before changing providers or expression scope.", command: previewCommand },
14410
+ { reason: "Re-execute with browser-scoped challenge automation when the target requires live browser recovery.", command: browserRetryCommand }
14411
+ ]
14412
+ );
14413
+ };
14414
+ var buildInspiredesignSuccessHandoff = (input) => createSuccessHandoff(
14415
+ input.summary,
14416
+ input.nextStep,
14417
+ [
14418
+ { reason: INSPIREDESIGN_HANDOFF_GUIDANCE.reviewAdvancedBrief },
14419
+ {
14420
+ reason: "Load the baseline workflow runbook before implementation.",
14421
+ command: input.commandExamples.loadBestPractices
14422
+ },
14423
+ {
14424
+ reason: "Load the Canvas contract lane before patching.",
14425
+ command: input.commandExamples.loadDesignAgent
14426
+ },
14427
+ {
14428
+ reason: INSPIREDESIGN_HANDOFF_GUIDANCE.prepareCanvasPlanRequest,
14429
+ command: input.commandExamples.continueInCanvas
14430
+ },
14431
+ { reason: input.deepCaptureRecommendation }
14432
+ ]
14433
+ );
14434
+
13470
14435
  // src/providers/renderer.ts
13471
14436
  var toCurrency = (value) => `$${value.toFixed(2)}`;
13472
14437
  var primaryConstraintSummaryFromMeta = (meta) => {
@@ -13730,26 +14695,12 @@ var renderInspiredesign = (args) => {
13730
14695
  evidence: args.evidence,
13731
14696
  meta: args.meta
13732
14697
  };
13733
- const suggestedSteps = [
13734
- {
13735
- reason: INSPIREDESIGN_HANDOFF_GUIDANCE.reviewAdvancedBrief
13736
- },
13737
- {
13738
- reason: "Load the baseline workflow runbook before implementation.",
13739
- command: args.designAgentHandoff.commandExamples.loadBestPractices
13740
- },
13741
- {
13742
- reason: "Load the Canvas contract lane before patching.",
13743
- command: args.designAgentHandoff.commandExamples.loadDesignAgent
13744
- },
13745
- {
13746
- reason: INSPIREDESIGN_HANDOFF_GUIDANCE.prepareCanvasPlanRequest,
13747
- command: args.designAgentHandoff.commandExamples.continueInCanvas
13748
- },
13749
- {
13750
- reason: args.designAgentHandoff.deepCaptureRecommendation
13751
- }
13752
- ];
14698
+ const handoff = buildInspiredesignSuccessHandoff({
14699
+ summary: followthroughSummary,
14700
+ nextStep: args.designAgentHandoff.nextStep,
14701
+ commandExamples: args.designAgentHandoff.commandExamples,
14702
+ deepCaptureRecommendation: args.designAgentHandoff.deepCaptureRecommendation
14703
+ });
13753
14704
  const files = [
13754
14705
  { path: INSPIREDESIGN_HANDOFF_FILES.designMarkdown, content: args.designMarkdown },
13755
14706
  { path: INSPIREDESIGN_HANDOFF_FILES.advancedBrief, content: args.advancedBriefMarkdown },
@@ -13773,9 +14724,7 @@ var renderInspiredesign = (args) => {
13773
14724
  response: {
13774
14725
  mode: args.mode,
13775
14726
  summary,
13776
- followthroughSummary,
13777
- suggestedNextAction: args.designAgentHandoff.nextStep,
13778
- suggestedSteps,
14727
+ ...handoff,
13779
14728
  ...captureAttemptFields,
13780
14729
  meta: args.meta
13781
14730
  },
@@ -13796,9 +14745,7 @@ var renderInspiredesign = (args) => {
13796
14745
  implementationPlan: args.implementationPlan,
13797
14746
  prototypeGuidanceMarkdown: args.prototypeGuidanceMarkdown,
13798
14747
  evidence: args.evidence,
13799
- followthroughSummary,
13800
- suggestedNextAction: args.designAgentHandoff.nextStep,
13801
- suggestedSteps,
14748
+ ...handoff,
13802
14749
  ...captureAttemptFields,
13803
14750
  meta: args.meta
13804
14751
  },
@@ -13812,9 +14759,7 @@ var renderInspiredesign = (args) => {
13812
14759
  markdown: args.designMarkdown,
13813
14760
  implementationPlanMarkdown: args.implementationPlanMarkdown,
13814
14761
  prototypeGuidanceMarkdown: args.prototypeGuidanceMarkdown,
13815
- followthroughSummary,
13816
- suggestedNextAction: args.designAgentHandoff.nextStep,
13817
- suggestedSteps,
14762
+ ...handoff,
13818
14763
  ...captureAttemptFields,
13819
14764
  meta: args.meta
13820
14765
  },
@@ -13826,9 +14771,7 @@ var renderInspiredesign = (args) => {
13826
14771
  response: {
13827
14772
  mode: args.mode,
13828
14773
  context: contextPayload,
13829
- followthroughSummary,
13830
- suggestedNextAction: args.designAgentHandoff.nextStep,
13831
- suggestedSteps,
14774
+ ...handoff,
13832
14775
  ...captureAttemptFields,
13833
14776
  meta: args.meta
13834
14777
  },
@@ -13838,9 +14781,7 @@ var renderInspiredesign = (args) => {
13838
14781
  return {
13839
14782
  response: {
13840
14783
  mode: "path",
13841
- followthroughSummary,
13842
- suggestedNextAction: args.designAgentHandoff.nextStep,
13843
- suggestedSteps,
14784
+ ...handoff,
13844
14785
  ...captureAttemptFields,
13845
14786
  meta: args.meta
13846
14787
  },
@@ -13975,138 +14916,6 @@ var CANVAS_VALIDATION_TARGET_BLOCK_ON_CODES = [
13975
14916
  ];
13976
14917
  var CANVAS_PUBLIC_WARNING_CLASSES = CANVAS_VALIDATION_TARGET_BLOCK_ON_CODES;
13977
14918
 
13978
- // src/providers/workflow-handoff.ts
13979
- var PRODUCT_VIDEO_BRIEF_HELPER_PATH = "./skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh";
13980
- var PRODUCT_VIDEO_BRIEF_HELPER_COMMAND = `${PRODUCT_VIDEO_BRIEF_HELPER_PATH} <pack>/manifest.json`;
13981
- var createSuccessHandoff = (followthroughSummary, suggestedNextAction, suggestedSteps) => ({
13982
- followthroughSummary,
13983
- suggestedNextAction,
13984
- suggestedSteps
13985
- });
13986
- var cliExample = (command, args = "") => `npx opendevbrowser ${command}${args ? ` ${args}` : ""}`;
13987
- var quoteCliValue = (value) => JSON.stringify(value);
13988
- var buildResearchRerunCommand = (topic) => cliExample(
13989
- "research run",
13990
- `--topic ${quoteCliValue(topic)} --days 14 --source-selection auto --sources web,community --mode json --output-format json`
13991
- );
13992
- var buildShoppingRerunCommand = (input) => {
13993
- const providers = input.providers?.length ? ` --providers ${input.providers.join(",")}` : " --providers shopping/bestbuy,shopping/ebay";
13994
- const budget = typeof input.budget === "number" ? ` --budget ${input.budget}` : "";
13995
- const region = input.region ? ` --region ${quoteCliValue(input.region)}` : "";
13996
- const browserMode = ` --browser-mode ${input.browserMode ?? "managed"}`;
13997
- const sort = input.sort ? ` --sort ${input.sort}` : "";
13998
- return cliExample(
13999
- "shopping run",
14000
- `--query ${quoteCliValue(input.query)}${providers}${budget}${region}${browserMode}${sort} --mode json --output-format json`
14001
- );
14002
- };
14003
- var buildProductVideoRerunCommand = (input = {}) => {
14004
- const target = input.productUrl ? `--product-url ${quoteCliValue(input.productUrl)}` : `--product-name ${quoteCliValue(input.productName ?? "<product-name>")}`;
14005
- const providerHint = input.providerHint ? ` --provider-hint ${input.providerHint}` : "";
14006
- const screenshots = input.includeScreenshots ? " --include-screenshots" : "";
14007
- const allImages = input.includeAllImages ? " --include-all-images" : "";
14008
- const includeCopy = input.includeCopy ? " --include-copy" : "";
14009
- return cliExample(
14010
- "product-video run",
14011
- `${target}${providerHint}${screenshots}${allImages}${includeCopy} --output-format json`
14012
- );
14013
- };
14014
- var buildMacroResolveArgs = (input, options) => {
14015
- const defaultProvider = input.defaultProvider ? ` --default-provider ${input.defaultProvider}` : "";
14016
- const execute = options?.execute ? " --execute" : "";
14017
- const challenge = options?.challengeAutomationMode ? ` --challenge-automation-mode ${options.challengeAutomationMode}` : "";
14018
- const outputFormat = options?.includeOutputFormat === false ? "" : " --output-format json";
14019
- return `--expression ${quoteCliValue(input.expression)}${defaultProvider}${execute}${challenge}${outputFormat}`;
14020
- };
14021
- var buildMacroPreviewCommand = (input) => cliExample("macro-resolve", buildMacroResolveArgs(input));
14022
- var buildMacroExecuteCommand = (input, challengeAutomationMode) => cliExample("macro-resolve", buildMacroResolveArgs(input, {
14023
- execute: true,
14024
- challengeAutomationMode
14025
- }));
14026
- var buildResearchSuccessHandoff = (topic) => {
14027
- const rerunCommand = buildResearchRerunCommand(topic);
14028
- return createSuccessHandoff(
14029
- "Review the ranked records and artifact bundle before turning the result into a publishable claim.",
14030
- `Open the returned artifact path, inspect the supporting records, and rerun ${rerunCommand} if you need a tighter evidence set.`,
14031
- [
14032
- { reason: "Check which records actually support the final claim." },
14033
- {
14034
- reason: "Rerun with explicit sources and a narrower timebox if the evidence set is still too broad.",
14035
- command: rerunCommand
14036
- }
14037
- ]
14038
- );
14039
- };
14040
- var buildShoppingSuccessHandoff = (input) => {
14041
- const rerunCommand = buildShoppingRerunCommand(input);
14042
- return createSuccessHandoff(
14043
- "Review the offer set and diagnostics before calling any result a strong deal.",
14044
- `Inspect the offers and meta.offerFilterDiagnostics, then rerun ${rerunCommand} if you need a tighter comparison.`,
14045
- [
14046
- { reason: "Check which offers survived the workflow filters and why." },
14047
- {
14048
- reason: "Rerun with explicit providers or updated budget and region inputs if the comparison is still noisy.",
14049
- command: rerunCommand
14050
- }
14051
- ]
14052
- );
14053
- };
14054
- var buildProductVideoSuccessHandoff = (input = {}) => {
14055
- const rerunCommand = buildProductVideoRerunCommand(input);
14056
- return createSuccessHandoff(
14057
- "Review the generated asset pack to confirm whether it is visual-ready or metadata-first before briefing production.",
14058
- `Open the returned pack path, inspect manifest.json plus copy and features, then run ${PRODUCT_VIDEO_BRIEF_HELPER_COMMAND} to generate production briefs and sourcing notes.`,
14059
- [
14060
- { reason: "Confirm whether the pack already includes enough images or screenshots for production." },
14061
- {
14062
- reason: "Run the product-presentation-asset brief helper on manifest.json to generate the production brief files.",
14063
- command: PRODUCT_VIDEO_BRIEF_HELPER_COMMAND
14064
- },
14065
- {
14066
- reason: "Rerun the asset workflow with adjusted provider or media flags when the current pack is too thin.",
14067
- command: rerunCommand
14068
- },
14069
- { reason: "Source or capture visuals before final handoff if the pack is metadata-first." }
14070
- ]
14071
- );
14072
- };
14073
- var buildMacroResolveSuccessHandoff = (input) => {
14074
- const previewCommand = buildMacroPreviewCommand(input);
14075
- const executeCommand = buildMacroExecuteCommand(input);
14076
- const browserRetryCommand = buildMacroExecuteCommand(input, "browser");
14077
- if (!input.execute) {
14078
- return createSuccessHandoff(
14079
- "Review the resolved provider action and provenance before executing the macro.",
14080
- `Run ${executeCommand} when the resolved action looks correct.`,
14081
- [
14082
- { reason: "Inspect resolution.action and resolution.provenance to confirm provider and query shaping." },
14083
- { reason: "Execute the resolved macro once the plan looks correct.", command: executeCommand },
14084
- { reason: "Add --default-provider only when you need to force a different provider lane.", command: previewCommand }
14085
- ]
14086
- );
14087
- }
14088
- if (input.blocked) {
14089
- return createSuccessHandoff(
14090
- "Review execution.meta.blocker and failures before retrying the macro.",
14091
- `Run ${browserRetryCommand} after checking execution.meta.blocker and the current recovery path.`,
14092
- [
14093
- { reason: "Inspect execution.meta.blocker and execution.failures before retrying." },
14094
- { reason: "Retry with browser-scoped challenge automation when the blocker requires live follow-up.", command: browserRetryCommand },
14095
- { reason: "Preview the resolved action again if you need to switch providers before another execute attempt.", command: previewCommand }
14096
- ]
14097
- );
14098
- }
14099
- return createSuccessHandoff(
14100
- "Review execution.records and trace metadata before widening the macro or changing providers.",
14101
- `Inspect execution.records and execution.meta, then rerun ${previewCommand} if you need a narrower plan.`,
14102
- [
14103
- { reason: "Inspect execution.records and execution.meta to confirm the resolved action hit the expected lane." },
14104
- { reason: "Preview the macro again before changing providers or expression scope.", command: previewCommand },
14105
- { reason: "Re-execute with browser-scoped challenge automation when the target requires live browser recovery.", command: browserRetryCommand }
14106
- ]
14107
- );
14108
- };
14109
-
14110
14919
  // src/providers/shopping-postprocess.ts
14111
14920
  import { createHash as createHash4 } from "crypto";
14112
14921
  var LOOKS_LIKE_URL_RE = /^https?:\/\/\S+$/i;
@@ -15413,6 +16222,16 @@ var resolveResearchAutoExcludedProviders = (sourceSelection, resolvedSources, de
15413
16222
  return source !== null && sourceSet.has(source);
15414
16223
  }).sort((left, right) => left.localeCompare(right));
15415
16224
  };
16225
+ var buildResearchSearchFilters = (source, args) => ({
16226
+ include_engagement: args.includeEngagement ?? false,
16227
+ timebox_from: args.timebox.from,
16228
+ timebox_to: args.timebox.to,
16229
+ ...source === "community" || source === "social" ? {
16230
+ pageLimit: 1,
16231
+ hopLimit: 0,
16232
+ expansionPerRecord: 0
16233
+ } : {}
16234
+ });
15416
16235
  var compileResearchExecutionPlan = (args) => {
15417
16236
  const topic = args.input.topic?.trim();
15418
16237
  if (!topic) {
@@ -15449,11 +16268,10 @@ var compileResearchExecutionPlan = (args) => {
15449
16268
  source,
15450
16269
  query: topic,
15451
16270
  limit: searchLimit,
15452
- filters: {
15453
- include_engagement: args.input.includeEngagement ?? false,
15454
- timebox_from: timebox.from,
15455
- timebox_to: timebox.to
15456
- }
16271
+ filters: buildResearchSearchFilters(source, {
16272
+ includeEngagement: args.input.includeEngagement,
16273
+ timebox
16274
+ })
15457
16275
  }
15458
16276
  }));
15459
16277
  return {
@@ -16261,6 +17079,7 @@ var workflowTestUtils = {
16261
17079
  buildWorkflowResumePayload: (kind, input) => buildWorkflowResumePayload(kind, input)
16262
17080
  };
16263
17081
  var PRODUCT_ASSET_FETCH_TIMEOUT_MS = 15e3;
17082
+ var RESEARCH_PROVIDER_STEP_TIMEOUT_MS = 3e4;
16264
17083
  var resolveAuxiliaryFetchTimeoutMs = (timeoutMs) => {
16265
17084
  if (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs <= 0) {
16266
17085
  return PRODUCT_ASSET_FETCH_TIMEOUT_MS;
@@ -16444,9 +17263,16 @@ var createRemainingTimeoutResolver = (timeoutMs) => {
16444
17263
  return Math.max(1, timeoutMs - Math.max(0, Date.now() - startedAtMs));
16445
17264
  };
16446
17265
  };
17266
+ var resolveResearchProviderStepTimeoutMs = (timeoutMs) => {
17267
+ if (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs <= 0) {
17268
+ return void 0;
17269
+ }
17270
+ return Math.max(1, Math.min(timeoutMs, RESEARCH_PROVIDER_STEP_TIMEOUT_MS));
17271
+ };
16447
17272
  var INSPIREDESIGN_RENDER_MODES = /* @__PURE__ */ new Set(["compact", "json", "md", "context", "path"]);
16448
17273
  var INSPIREDESIGN_CAPTURE_MODES = /* @__PURE__ */ new Set(["off", "deep"]);
16449
17274
  var INSPIREDESIGN_COOKIE_POLICIES = /* @__PURE__ */ new Set(["off", "auto", "required"]);
17275
+ var WORKFLOW_BROWSER_MODES = /* @__PURE__ */ new Set(["auto", "extension", "managed"]);
16450
17276
  var isJsonRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16451
17277
  var INSPIREDESIGN_CAPTURE_UNAVAILABLE_FAILURE = "Deep capture requested, but browser capture is unavailable in this execution lane.";
16452
17278
  var isCanvasVisualDirectionProfile = (value) => {
@@ -16469,6 +17295,7 @@ var serializeInspiredesignRunInput = (input) => ({
16469
17295
  ...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
16470
17296
  ...input.outputDir ? { outputDir: input.outputDir } : {},
16471
17297
  ...typeof input.ttlHours === "number" ? { ttlHours: input.ttlHours } : {},
17298
+ ...input.browserMode ? { browserMode: input.browserMode } : {},
16472
17299
  ...typeof input.useCookies === "boolean" ? { useCookies: input.useCookies } : {},
16473
17300
  ...input.challengeAutomationMode ? { challengeAutomationMode: input.challengeAutomationMode } : {},
16474
17301
  ...input.cookiePolicyOverride ? { cookiePolicyOverride: input.cookiePolicyOverride } : {}
@@ -16543,6 +17370,7 @@ var parseInspiredesignEnvelopeInput = (input) => {
16543
17370
  ...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
16544
17371
  ...typeof input.outputDir === "string" && input.outputDir.length > 0 ? { outputDir: input.outputDir } : {},
16545
17372
  ...typeof input.ttlHours === "number" ? { ttlHours: input.ttlHours } : {},
17373
+ ...typeof input.browserMode === "string" && WORKFLOW_BROWSER_MODES.has(input.browserMode) ? { browserMode: input.browserMode } : {},
16546
17374
  ...typeof input.useCookies === "boolean" ? { useCookies: input.useCookies } : {},
16547
17375
  ...isChallengeAutomationMode(input.challengeAutomationMode) ? { challengeAutomationMode: input.challengeAutomationMode } : {},
16548
17376
  ...typeof input.cookiePolicyOverride === "string" && INSPIREDESIGN_COOKIE_POLICIES.has(input.cookiePolicyOverride) ? { cookiePolicyOverride: input.cookiePolicyOverride } : {}
@@ -16636,10 +17464,13 @@ var buildInspiredesignStepEnvelope = (workflowInput, trace, stepIndex, url) => b
16636
17464
  }
16637
17465
  );
16638
17466
  var buildInspiredesignFetchOptions = (workflowInput, envelope, timeoutMs) => withWorkflowResumeEnvelopeIntent(
16639
- withChallengeAutomationOverride(
16640
- withCookieOverrides({
16641
- ...typeof timeoutMs === "number" ? { timeoutMs } : {}
16642
- }, workflowInput),
17467
+ withBrowserModeOverride(
17468
+ withChallengeAutomationOverride(
17469
+ withCookieOverrides({
17470
+ ...typeof timeoutMs === "number" ? { timeoutMs } : {}
17471
+ }, workflowInput),
17472
+ workflowInput
17473
+ ),
16643
17474
  workflowInput
16644
17475
  ),
16645
17476
  "workflow.inspiredesign",
@@ -16880,6 +17711,7 @@ var buildInspiredesignMeta = (runtime, workflowInput, references, failures, foll
16880
17711
  selection: {
16881
17712
  urls: workflowInput.urls,
16882
17713
  capture_mode: workflowInput.captureMode,
17714
+ ...workflowInput.browserMode ? { requested_browser_mode: workflowInput.browserMode } : {},
16883
17715
  include_prototype_guidance: Boolean(workflowInput.includePrototypeGuidance)
16884
17716
  },
16885
17717
  metrics: {
@@ -17434,10 +18266,17 @@ var runResearchWorkflow = async (runtime, input) => {
17434
18266
  }
17435
18267
  ];
17436
18268
  const buildResearchStepOptions = (step, stepEnvelope) => {
17437
- const stepOptions = withChallengeAutomationOverride(withCookieOverrides({
17438
- source: step.input.source,
17439
- ...typeof workflowInput.timeoutMs === "number" ? { timeoutMs: workflowInput.timeoutMs } : {}
17440
- }, workflowInput), workflowInput);
18269
+ const timeoutMs = resolveResearchProviderStepTimeoutMs(workflowInput.timeoutMs);
18270
+ const stepOptions = withBrowserModeOverride(
18271
+ withChallengeAutomationOverride(
18272
+ withCookieOverrides({
18273
+ source: step.input.source,
18274
+ ...typeof timeoutMs === "number" ? { timeoutMs } : {}
18275
+ }, workflowInput),
18276
+ workflowInput
18277
+ ),
18278
+ workflowInput
18279
+ );
17441
18280
  return withWorkflowResumeEnvelopeIntent(
17442
18281
  stepOptions,
17443
18282
  "workflow.research",
@@ -17500,7 +18339,8 @@ var runResearchWorkflow = async (runtime, input) => {
17500
18339
  timebox: resolvedTimebox,
17501
18340
  selection: withExcludedProviders({
17502
18341
  source_selection: plan.compiled.sourceSelection,
17503
- resolved_sources: plan.compiled.resolvedSources
18342
+ resolved_sources: plan.compiled.resolvedSources,
18343
+ ...workflowInput.browserMode ? { requested_browser_mode: workflowInput.browserMode } : {}
17504
18344
  }, plan.compiled.autoExcludedProviders),
17505
18345
  metrics: {
17506
18346
  total_records: mergedRecords.length,
@@ -17527,7 +18367,10 @@ var runResearchWorkflow = async (runtime, input) => {
17527
18367
  failures: mergedFailures,
17528
18368
  alerts: buildWorkflowAlerts(runtime, mergedFailures)
17529
18369
  }, primaryConstraintFailures);
17530
- const handoff = buildResearchSuccessHandoff(plan.compiled.topic);
18370
+ const handoff = buildResearchSuccessHandoff({
18371
+ topic: plan.compiled.topic,
18372
+ browserMode: workflowInput.browserMode
18373
+ });
17531
18374
  const responseMeta = withFollowthroughMeta(meta, handoff);
17532
18375
  const rendered = renderResearch({
17533
18376
  mode: workflowInput.mode,
@@ -17962,6 +18805,7 @@ var runProductVideoWorkflow = async (runtime, input, options = {}) => {
17962
18805
  providers: providerHint ? [providerHint] : void 0,
17963
18806
  mode: "json",
17964
18807
  ...timeoutOptions,
18808
+ browserMode: workflowInput.browserMode,
17965
18809
  useCookies: workflowInput.useCookies,
17966
18810
  challengeAutomationMode: workflowInput.challengeAutomationMode,
17967
18811
  cookiePolicyOverride: workflowInput.cookiePolicyOverride
@@ -18040,12 +18884,15 @@ var runProductVideoWorkflow = async (runtime, input, options = {}) => {
18040
18884
  details = await runtime.fetch(
18041
18885
  { url: productUrl },
18042
18886
  withWorkflowResumeEnvelopeIntent(
18043
- withChallengeAutomationOverride(
18044
- withCookieOverrides({
18045
- source,
18046
- providerIds: shoppingProviderId ? [shoppingProviderId] : void 0,
18047
- ...timeoutOptions
18048
- }, workflowInput),
18887
+ withBrowserModeOverride(
18888
+ withChallengeAutomationOverride(
18889
+ withCookieOverrides({
18890
+ source,
18891
+ providerIds: shoppingProviderId ? [shoppingProviderId] : void 0,
18892
+ ...timeoutOptions
18893
+ }, workflowInput),
18894
+ workflowInput
18895
+ ),
18049
18896
  workflowInput
18050
18897
  ),
18051
18898
  "workflow.product_video",
@@ -18195,14 +19042,16 @@ var runProductVideoWorkflow = async (runtime, input, options = {}) => {
18195
19042
  productUrl,
18196
19043
  productName: workflowInput.product_name,
18197
19044
  providerHint,
19045
+ browserMode: workflowInput.browserMode,
18198
19046
  includeScreenshots: workflowInput.include_screenshots,
18199
19047
  includeAllImages: workflowInput.include_all_images,
18200
19048
  includeCopy: workflowInput.include_copy
18201
19049
  });
18202
19050
  const meta = withFollowthroughMeta({
19051
+ ...workflowInput.browserMode ? { selection: { requested_browser_mode: workflowInput.browserMode } } : {},
18203
19052
  alerts: buildWorkflowAlerts(runtime, details.failures),
18204
19053
  failures: details.failures,
18205
- ...primaryIssue ? { primaryConstraintSummary: primaryIssue.summary } : {},
19054
+ ...primaryIssue ? { primaryConstraint: primaryIssue, primaryConstraintSummary: primaryIssue.summary } : {},
18206
19055
  reasonCodeDistribution,
18207
19056
  transcript_strategy_failures: transcriptStrategyFailures,
18208
19057
  transcriptStrategyFailures,
@@ -18243,11 +19092,11 @@ var WORKFLOW_KIND_BY_SUSPENDED_INTENT_KIND2 = {
18243
19092
  "workflow.inspiredesign": "inspiredesign",
18244
19093
  "workflow.product_video": "product_video"
18245
19094
  };
18246
- var EXTENSION_FIRST_SOCIAL_FALLBACK_PLATFORMS = /* @__PURE__ */ new Set(["x", "reddit", "bluesky", "facebook", "linkedin"]);
19095
+ var EXTENSION_FIRST_SOCIAL_FALLBACK_PLATFORMS = /* @__PURE__ */ new Set(["x", "reddit", "bluesky", "facebook", "linkedin", "threads"]);
18247
19096
  var EXTENSION_MINIMAL_TRAVERSAL_SOCIAL_PLATFORMS = /* @__PURE__ */ new Set(["linkedin"]);
18248
19097
  var EXTENSION_FIRST_FALLBACK_MODES = ["extension", "managed_headed"];
18249
19098
  var SOCIAL_BROWSER_RECOVERY_REASON_CODES = /* @__PURE__ */ new Set(["challenge_detected"]);
18250
- var SEARCH_RENDER_RECOVERY_SOCIAL_PLATFORMS = /* @__PURE__ */ new Set(["x", "bluesky", "reddit", "facebook"]);
19099
+ var SEARCH_RENDER_RECOVERY_SOCIAL_PLATFORMS = /* @__PURE__ */ new Set(["x", "bluesky", "reddit", "facebook", "threads"]);
18251
19100
  var withPrioritizedFallbackModes = (prioritized, existing) => [.../* @__PURE__ */ new Set([...prioritized, ...existing ?? []])];
18252
19101
  var buildSocialRecoveryHints = (platform, options) => {
18253
19102
  const existing = options?.recoveryHints?.();
@@ -18406,6 +19255,154 @@ var SOCIAL_SEARCH_ENDPOINTS = {
18406
19255
  threads: (query, page) => `https://www.threads.net/search?q=${encodeURIComponent(query)}&page=${page}`,
18407
19256
  youtube: (query, page) => `https://www.youtube.com/results?search_query=${encodeURIComponent(query)}&page=${page}`
18408
19257
  };
19258
+ var FALLBACK_SHELL_LINK_SEGMENTS = /* @__PURE__ */ new Set([
19259
+ "account",
19260
+ "auth",
19261
+ "about",
19262
+ "cart",
19263
+ "categories",
19264
+ "category",
19265
+ "collections",
19266
+ "contact",
19267
+ "explore",
19268
+ "help",
19269
+ "home",
19270
+ "legal",
19271
+ "login",
19272
+ "policies",
19273
+ "product",
19274
+ "products",
19275
+ "privacy",
19276
+ "search",
19277
+ "settings",
19278
+ "shop",
19279
+ "signin",
19280
+ "sign-in",
19281
+ "sitemap",
19282
+ "store",
19283
+ "submit",
19284
+ "support",
19285
+ "terms"
19286
+ ]);
19287
+ var FALLBACK_SHELL_TEXT_TOKENS = /* @__PURE__ */ new Set([
19288
+ "account",
19289
+ "ad",
19290
+ "ads",
19291
+ "about",
19292
+ "cart",
19293
+ "contact",
19294
+ "explore",
19295
+ "help",
19296
+ "home",
19297
+ "in",
19298
+ "legal",
19299
+ "log",
19300
+ "login",
19301
+ "policies",
19302
+ "product",
19303
+ "products",
19304
+ "privacy",
19305
+ "search",
19306
+ "settings",
19307
+ "sign",
19308
+ "signin",
19309
+ "submit",
19310
+ "support",
19311
+ "terms"
19312
+ ]);
19313
+ var FALLBACK_HEAD_RE2 = /<head\b[^>]*>[\s\S]*?<\/head>/i;
19314
+ var FALLBACK_BODY_RE2 = /<body\b[^>]*>([\s\S]*?)<\/body>/i;
19315
+ var FALLBACK_MIN_MEANINGFUL_TEXT_CHARS = 20;
19316
+ var FALLBACK_MIN_MEANINGFUL_TOKENS = 3;
19317
+ var FALLBACK_SHELL_PHRASES_RE = /\b(?:search results?|please enable javascript|enable javascript|continue|welcome|accept cookies?|privacy policy|terms of service)\b/i;
19318
+ var extractFallbackBodyHtml = (html) => {
19319
+ const body = FALLBACK_BODY_RE2.exec(html)?.[1];
19320
+ return body ?? html.replace(FALLBACK_HEAD_RE2, " ");
19321
+ };
19322
+ var extractFallbackBodyText = (html) => {
19323
+ return extractText(extractFallbackBodyHtml(html));
19324
+ };
19325
+ var hasUsableFallbackText = (html) => {
19326
+ const text = extractFallbackBodyText(html);
19327
+ const blocker = classifyBlockerSignal({
19328
+ source: "runtime_fetch",
19329
+ message: text,
19330
+ status: 200,
19331
+ providerErrorCode: "unavailable",
19332
+ retryable: true
19333
+ });
19334
+ if (blocker && blocker.type !== "unknown") return false;
19335
+ if (text.length < FALLBACK_MIN_MEANINGFUL_TEXT_CHARS || FALLBACK_SHELL_PHRASES_RE.test(text)) {
19336
+ return false;
19337
+ }
19338
+ const tokens = text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
19339
+ const meaningfulTokens = tokens.filter((token) => !FALLBACK_SHELL_TEXT_TOKENS.has(token));
19340
+ return meaningfulTokens.length >= FALLBACK_MIN_MEANINGFUL_TOKENS;
19341
+ };
19342
+ var isUsableFallbackLink = (link, documentUrl) => {
19343
+ try {
19344
+ const parsed = new URL(link);
19345
+ const source = new URL(documentUrl);
19346
+ const segments = parsed.pathname.toLowerCase().split("/").filter(Boolean);
19347
+ const firstSegment = segments[0];
19348
+ if (parsed.origin === source.origin && parsed.pathname === source.pathname) {
19349
+ return false;
19350
+ }
19351
+ if (segments.length === 0 && parsed.search.length === 0) {
19352
+ return false;
19353
+ }
19354
+ return firstSegment === void 0 || !FALLBACK_SHELL_LINK_SEGMENTS.has(firstSegment);
19355
+ } catch {
19356
+ return false;
19357
+ }
19358
+ };
19359
+ var hasUsableFallbackBodyLink = (documentUrl, html) => extractStructuredContent(extractFallbackBodyHtml(html), documentUrl).links.some((link) => isUsableFallbackLink(link, documentUrl));
19360
+ var hasUsableFallbackContent = (documentUrl, html) => hasUsableFallbackText(html) || hasUsableFallbackBodyLink(documentUrl, html);
19361
+ var isBlockedRedditFallbackLink = (link) => {
19362
+ try {
19363
+ const hostname = new URL(link).hostname.toLowerCase();
19364
+ const isReddit = hostname === "reddit.com" || hostname.endsWith(".reddit.com");
19365
+ return isReddit && !isAllowedSocialSearchExpansionUrl("reddit", link);
19366
+ } catch {
19367
+ return false;
19368
+ }
19369
+ };
19370
+ var shouldOwnerReviewCommunityRedditSearchFallback = (input) => input.providerId === "community/default" && input.source === "community" && input.operation === "search" && isFirstPartySocialSearchRoute("reddit", input.url) && input.extracted.links.length > 0 && input.extracted.links.every(isBlockedRedditFallbackLink);
19371
+ var shouldReturnCompletedFallbackForOwnerReview = (args) => args.document.browserFallback !== void 0 && args.ownerReview?.({
19372
+ providerId: args.providerId,
19373
+ source: args.source,
19374
+ operation: args.operation,
19375
+ url: args.document.url,
19376
+ html: args.document.html,
19377
+ extracted: extractStructuredContent(args.document.html, args.document.url)
19378
+ }) === true;
19379
+ var toCompletedFallbackDocument = (args) => {
19380
+ const resolvedUrl = canonicalizeUrl(readFallbackString(args.fallback.output, "url") ?? args.url);
19381
+ const html = readFallbackString(args.fallback.output, "html");
19382
+ if (!html) {
19383
+ throw toCompletedFallbackOutputError({ ...args, provider: args.providerId, url: resolvedUrl, outputReason: "missing_or_empty_html" });
19384
+ }
19385
+ const extracted = extractStructuredContent(html, resolvedUrl);
19386
+ const reviewInput = {
19387
+ providerId: args.providerId,
19388
+ source: args.source,
19389
+ operation: args.operation,
19390
+ url: resolvedUrl,
19391
+ html,
19392
+ extracted
19393
+ };
19394
+ if (!hasUsableFallbackContent(resolvedUrl, html) && !args.ownerReview?.(reviewInput)) {
19395
+ throw toCompletedFallbackOutputError({ ...args, provider: args.providerId, url: resolvedUrl, outputReason: "empty_extracted_content" });
19396
+ }
19397
+ return {
19398
+ url: resolvedUrl,
19399
+ status: 200,
19400
+ html,
19401
+ text: extracted.text,
19402
+ links: extracted.links,
19403
+ browserFallback: toBrowserFallbackObservation(args.fallback)
19404
+ };
19405
+ };
18409
19406
  var describeDefaultFetchedIssue = (document) => {
18410
19407
  const extracted = extractStructuredContent(document.html, document.url);
18411
19408
  const title = typeof extracted.metadata.title === "string" ? extracted.metadata.title : void 0;
@@ -18467,6 +19464,9 @@ var resolveDefaultFallbackDocumentIfNeeded = async (args) => {
18467
19464
  if (!initialIssue) {
18468
19465
  return { document: currentDocument, ...described };
18469
19466
  }
19467
+ if (shouldReturnCompletedFallbackForOwnerReview({ ...args, document: currentDocument })) {
19468
+ return { document: currentDocument, ...described };
19469
+ }
18470
19470
  if (initialIssue.reasonCode === "env_limited" && !initialIssue.constraint) {
18471
19471
  return { document: currentDocument, ...described };
18472
19472
  }
@@ -18497,18 +19497,18 @@ var resolveDefaultFallbackDocumentIfNeeded = async (args) => {
18497
19497
  fallback
18498
19498
  });
18499
19499
  }
18500
- const resolvedUrl = canonicalizeUrl(readFallbackString(fallback.output, "url") ?? currentDocument.url);
18501
- const html = readFallbackString(fallback.output, "html") ?? "";
18502
- const extracted = extractStructuredContent(html, resolvedUrl);
18503
- currentDocument = {
18504
- url: resolvedUrl,
18505
- status: 200,
18506
- html,
18507
- text: extracted.text,
18508
- links: extracted.links,
18509
- browserFallback: toBrowserFallbackObservation(fallback)
18510
- };
19500
+ currentDocument = toCompletedFallbackDocument({
19501
+ providerId: args.providerId,
19502
+ source: args.source,
19503
+ operation: args.operation,
19504
+ url: currentDocument.url,
19505
+ fallback,
19506
+ ownerReview: args.ownerReview
19507
+ });
18511
19508
  described = describeDefaultFetchedIssue(currentDocument);
19509
+ if (shouldReturnCompletedFallbackForOwnerReview({ ...args, document: currentDocument })) {
19510
+ return { document: currentDocument, ...described };
19511
+ }
18512
19512
  if (!described.issue || described.issue.reasonCode === "env_limited" && !described.issue.constraint) {
18513
19513
  return { document: currentDocument, ...described };
18514
19514
  }
@@ -18785,6 +19785,57 @@ var readResponseTextWithAbort = async (response, args) => {
18785
19785
  }
18786
19786
  };
18787
19787
  var fetchRuntimeDocumentWithFallback = async (args) => {
19788
+ const fallbackPort = args.context?.browserFallbackPort ?? args.browserFallbackPort;
19789
+ const runtimePolicy = args.context?.runtimePolicy;
19790
+ const forcedBrowserTransport = runtimePolicy?.browser.forceTransport === true;
19791
+ const forcedReasonCode = runtimePolicy?.cookies.policy === "required" ? "auth_required" : "env_limited";
19792
+ if (forcedBrowserTransport) {
19793
+ const fallback = await resolveProviderBrowserFallback({
19794
+ browserFallbackPort: fallbackPort,
19795
+ provider: args.provider,
19796
+ source: args.source,
19797
+ operation: args.operation,
19798
+ reasonCode: forcedReasonCode,
19799
+ url: args.url,
19800
+ context: args.context,
19801
+ details: {
19802
+ message: "Direct browser transport was selected for this provider run."
19803
+ },
19804
+ recoveryHints: args.recoveryHints
19805
+ });
19806
+ if (!fallback) {
19807
+ throw new ProviderRuntimeError(
19808
+ providerErrorCodeFromReasonCode(forcedReasonCode),
19809
+ "Direct browser transport is required for this provider run, but no browser transport is available.",
19810
+ {
19811
+ provider: args.provider,
19812
+ source: args.source,
19813
+ retryable: false,
19814
+ reasonCode: forcedReasonCode,
19815
+ details: {
19816
+ reasonCode: forcedReasonCode,
19817
+ browserTransportRequired: true
19818
+ }
19819
+ }
19820
+ );
19821
+ }
19822
+ if (fallback.disposition !== "completed") {
19823
+ throw toProviderFallbackError({
19824
+ provider: args.provider,
19825
+ source: args.source,
19826
+ url: args.url,
19827
+ fallback
19828
+ });
19829
+ }
19830
+ return toCompletedFallbackDocument({
19831
+ providerId: args.provider,
19832
+ source: args.source,
19833
+ operation: args.operation,
19834
+ url: args.url,
19835
+ fallback,
19836
+ ownerReview: args.ownerReview
19837
+ });
19838
+ }
18788
19839
  try {
18789
19840
  return await fetchRuntimeDocument({
18790
19841
  url: args.url,
@@ -18797,7 +19848,9 @@ var fetchRuntimeDocumentWithFallback = async (args) => {
18797
19848
  provider: args.provider,
18798
19849
  source: args.source
18799
19850
  });
18800
- const fallbackPort = args.context?.browserFallbackPort ?? args.browserFallbackPort;
19851
+ if (args.recoverRuntimeErrors === false) {
19852
+ throw error;
19853
+ }
18801
19854
  if (!fallbackPort) {
18802
19855
  throw error;
18803
19856
  }
@@ -18831,17 +19884,14 @@ var fetchRuntimeDocumentWithFallback = async (args) => {
18831
19884
  fallback
18832
19885
  });
18833
19886
  }
18834
- const resolvedUrl = canonicalizeUrl(readFallbackString(fallback.output, "url") ?? args.url);
18835
- const html = readFallbackString(fallback.output, "html") ?? "";
18836
- const extracted = extractStructuredContent(html, resolvedUrl);
18837
- return {
18838
- url: resolvedUrl,
18839
- status: 200,
18840
- html,
18841
- text: extracted.text,
18842
- links: extracted.links,
18843
- browserFallback: toBrowserFallbackObservation(fallback)
18844
- };
19887
+ return toCompletedFallbackDocument({
19888
+ providerId: args.provider,
19889
+ source: args.source,
19890
+ operation: args.operation,
19891
+ url: args.url,
19892
+ fallback,
19893
+ ownerReview: args.ownerReview
19894
+ });
18845
19895
  }
18846
19896
  };
18847
19897
  var ProviderRuntime = class {
@@ -19997,7 +21047,8 @@ var withDefaultCommunityOptions = (options, browserFallbackPort) => {
19997
21047
  operation: "search",
19998
21048
  signal: context.signal,
19999
21049
  context,
20000
- browserFallbackPort
21050
+ browserFallbackPort,
21051
+ ownerReview: shouldOwnerReviewCommunityRedditSearchFallback
20001
21052
  });
20002
21053
  const { document: resolvedDocument, pageMessage } = await resolveDefaultFallbackDocumentIfNeeded({
20003
21054
  providerId,
@@ -20005,7 +21056,8 @@ var withDefaultCommunityOptions = (options, browserFallbackPort) => {
20005
21056
  operation: "search",
20006
21057
  document,
20007
21058
  context,
20008
- browserFallbackPort
21059
+ browserFallbackPort,
21060
+ ownerReview: shouldOwnerReviewCommunityRedditSearchFallback
20009
21061
  });
20010
21062
  const links = resolveCommunitySearchLinks(resolvedDocument, limit);
20011
21063
  if (shouldRejectBlockedCommunityFallback(resolvedDocument, links)) {
@@ -20117,6 +21169,12 @@ var withDefaultSocialPlatformOptions = (platform, options, browserFallbackPort)
20117
21169
  }
20118
21170
  );
20119
21171
  };
21172
+ const shouldOwnerReviewSocialFallback = (input) => input.source === "social" && input.operation === "search" && detectSocialSearchShell(platform, {
21173
+ url: input.url,
21174
+ title: typeof input.extracted.metadata.title === "string" ? input.extracted.metadata.title : void 0,
21175
+ content: toSnippet(stripUrls(input.extracted.text), 1600),
21176
+ links: input.extracted.links
21177
+ })?.browserRequired === true;
20120
21178
  const resolveFallbackDocumentIfNeeded = async (operation, document, context) => {
20121
21179
  let currentDocument = document;
20122
21180
  let described = describeDocumentIssue(operation, currentDocument);
@@ -20151,23 +21209,24 @@ var withDefaultSocialPlatformOptions = (platform, options, browserFallbackPort)
20151
21209
  });
20152
21210
  }
20153
21211
  const requestedUrl = currentDocument.url;
20154
- const resolvedUrl = canonicalizeUrl(readFallbackString(fallback.output, "url") ?? currentDocument.url);
20155
- const html = readFallbackString(fallback.output, "html") ?? "";
20156
- const extracted = extractStructuredContent(html, resolvedUrl);
21212
+ const fallbackDocument = toCompletedFallbackDocument({
21213
+ providerId,
21214
+ source: "social",
21215
+ operation,
21216
+ url: currentDocument.url,
21217
+ fallback,
21218
+ ownerReview: shouldOwnerReviewSocialFallback
21219
+ });
20157
21220
  const effectiveUrl = resolveRecoveredSocialSearchDocumentUrl({
20158
21221
  platform,
20159
21222
  operation,
20160
21223
  requestedUrl,
20161
- recoveredUrl: resolvedUrl,
20162
- recoveredLinks: extracted.links
21224
+ recoveredUrl: fallbackDocument.url,
21225
+ recoveredLinks: fallbackDocument.links
20163
21226
  });
20164
21227
  currentDocument = {
20165
- url: effectiveUrl,
20166
- status: 200,
20167
- html,
20168
- text: extracted.text,
20169
- links: extracted.links,
20170
- browserFallback: toBrowserFallbackObservation(fallback)
21228
+ ...fallbackDocument,
21229
+ url: effectiveUrl
20171
21230
  };
20172
21231
  described = describeDocumentIssue(operation, currentDocument);
20173
21232
  if (!described.issue || described.issue.reasonCode === "env_limited" && !described.issue.constraint) {
@@ -20191,7 +21250,9 @@ var withDefaultSocialPlatformOptions = (platform, options, browserFallbackPort)
20191
21250
  signal: context.signal,
20192
21251
  context,
20193
21252
  browserFallbackPort,
20194
- recoveryHints: extensionFirstRecoveryHints
21253
+ recoveryHints: extensionFirstRecoveryHints,
21254
+ recoverRuntimeErrors: EXTENSION_FIRST_SOCIAL_FALLBACK_PLATFORMS.has(platform),
21255
+ ownerReview: shouldOwnerReviewSocialFallback
20195
21256
  });
20196
21257
  const { document: resolvedDocument, extracted, pageMessage } = await resolveFallbackDocumentIfNeeded("search", document, context);
20197
21258
  const links = dedupeLinks2(
@@ -20224,7 +21285,9 @@ var withDefaultSocialPlatformOptions = (platform, options, browserFallbackPort)
20224
21285
  signal: context.signal,
20225
21286
  context,
20226
21287
  browserFallbackPort,
20227
- recoveryHints: extensionFirstRecoveryHints
21288
+ recoveryHints: extensionFirstRecoveryHints,
21289
+ recoverRuntimeErrors: EXTENSION_FIRST_SOCIAL_FALLBACK_PLATFORMS.has(platform),
21290
+ ownerReview: shouldOwnerReviewSocialFallback
20228
21291
  });
20229
21292
  const { document: resolvedDocument, extracted } = await resolveFallbackDocumentIfNeeded("fetch", document, context);
20230
21293
  const links = dedupeLinks2(resolvedDocument.links, resolvedDocument.url, 20);
@@ -20387,10 +21450,10 @@ export {
20387
21450
  enrichResearchRecords,
20388
21451
  INSPIREDESIGN_HANDOFF_COMMANDS,
20389
21452
  INSPIREDESIGN_HANDOFF_GUIDANCE,
21453
+ buildMacroResolveSuccessHandoff,
20390
21454
  renderResearch,
20391
21455
  renderShopping,
20392
21456
  renderInspiredesign,
20393
- buildMacroResolveSuccessHandoff,
20394
21457
  resolveInspiredesignCaptureMode,
20395
21458
  workflowTestUtils,
20396
21459
  runResearchWorkflow,
@@ -20402,4 +21465,4 @@ export {
20402
21465
  createProviderRuntime,
20403
21466
  createDefaultRuntime
20404
21467
  };
20405
- //# sourceMappingURL=chunk-V5DJUSPV.js.map
21468
+ //# sourceMappingURL=chunk-T3VVHJTK.js.map