opendevbrowser 0.0.19 → 0.0.21

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 (173) hide show
  1. package/README.md +32 -24
  2. package/dist/automation/coordinator.d.ts +61 -2
  3. package/dist/automation/coordinator.d.ts.map +1 -1
  4. package/dist/browser/browser-manager.d.ts +6 -1
  5. package/dist/browser/browser-manager.d.ts.map +1 -1
  6. package/dist/browser/canvas-manager.d.ts +4 -0
  7. package/dist/browser/canvas-manager.d.ts.map +1 -1
  8. package/dist/browser/manager-types.d.ts +6 -1
  9. package/dist/browser/manager-types.d.ts.map +1 -1
  10. package/dist/browser/ops-browser-manager.d.ts +6 -1
  11. package/dist/browser/ops-browser-manager.d.ts.map +1 -1
  12. package/dist/browser/screencast-recorder.d.ts.map +1 -1
  13. package/dist/browser/session-inspector.d.ts +39 -0
  14. package/dist/browser/session-inspector.d.ts.map +1 -1
  15. package/dist/canvas/document-store.d.ts +14 -5
  16. package/dist/canvas/document-store.d.ts.map +1 -1
  17. package/dist/canvas/starters/catalog.d.ts.map +1 -1
  18. package/dist/canvas/types.d.ts +120 -9
  19. package/dist/canvas/types.d.ts.map +1 -1
  20. package/dist/challenges/action-loop.d.ts +2 -1
  21. package/dist/challenges/action-loop.d.ts.map +1 -1
  22. package/dist/challenges/capture.d.ts +14 -0
  23. package/dist/challenges/capture.d.ts.map +1 -0
  24. package/dist/challenges/index.d.ts +3 -1
  25. package/dist/challenges/index.d.ts.map +1 -1
  26. package/dist/challenges/inspect-plan.d.ts +40 -0
  27. package/dist/challenges/inspect-plan.d.ts.map +1 -0
  28. package/dist/challenges/orchestrator.d.ts.map +1 -1
  29. package/dist/challenges/types.d.ts +34 -0
  30. package/dist/challenges/types.d.ts.map +1 -1
  31. package/dist/{chunk-YBQECXZX.js → chunk-3VA6XR25.js} +3 -3
  32. package/dist/chunk-3VA6XR25.js.map +1 -0
  33. package/dist/{chunk-W4IHGDXV.js → chunk-4KVXCXV3.js} +25778 -24279
  34. package/dist/chunk-4KVXCXV3.js.map +1 -0
  35. package/dist/{chunk-5FZQJRBQ.js → chunk-ZE2E7ZGH.js} +3013 -1010
  36. package/dist/chunk-ZE2E7ZGH.js.map +1 -0
  37. package/dist/cli/commands/challenge-automation-mode.d.ts +3 -0
  38. package/dist/cli/commands/challenge-automation-mode.d.ts.map +1 -0
  39. package/dist/cli/commands/inspiredesign.d.ts +25 -0
  40. package/dist/cli/commands/inspiredesign.d.ts.map +1 -0
  41. package/dist/cli/commands/macro-resolve.d.ts.map +1 -1
  42. package/dist/cli/commands/nav/review-desktop.d.ts +7 -0
  43. package/dist/cli/commands/nav/review-desktop.d.ts.map +1 -0
  44. package/dist/cli/commands/nav/review-shared.d.ts +10 -0
  45. package/dist/cli/commands/nav/review-shared.d.ts.map +1 -0
  46. package/dist/cli/commands/nav/review.d.ts.map +1 -1
  47. package/dist/cli/commands/serve.d.ts.map +1 -1
  48. package/dist/cli/commands/session/inspector-audit.d.ts +7 -0
  49. package/dist/cli/commands/session/inspector-audit.d.ts.map +1 -0
  50. package/dist/cli/commands/session/inspector-plan.d.ts +7 -0
  51. package/dist/cli/commands/session/inspector-plan.d.ts.map +1 -0
  52. package/dist/cli/commands/session/inspector-shared.d.ts +11 -0
  53. package/dist/cli/commands/session/inspector-shared.d.ts.map +1 -0
  54. package/dist/cli/commands/session/inspector.d.ts +1 -11
  55. package/dist/cli/commands/session/inspector.d.ts.map +1 -1
  56. package/dist/cli/commands/status-capabilities.d.ts +7 -0
  57. package/dist/cli/commands/status-capabilities.d.ts.map +1 -0
  58. package/dist/cli/daemon-client.d.ts.map +1 -1
  59. package/dist/cli/daemon-commands.d.ts.map +1 -1
  60. package/dist/cli/daemon-status.d.ts +5 -0
  61. package/dist/cli/daemon-status.d.ts.map +1 -1
  62. package/dist/cli/daemon.d.ts +5 -0
  63. package/dist/cli/daemon.d.ts.map +1 -1
  64. package/dist/cli/help.d.ts +2 -0
  65. package/dist/cli/help.d.ts.map +1 -1
  66. package/dist/cli/index.js +660 -244
  67. package/dist/cli/index.js.map +1 -1
  68. package/dist/cli/remote-manager.d.ts +6 -0
  69. package/dist/cli/remote-manager.d.ts.map +1 -1
  70. package/dist/cli/utils/parse.d.ts +1 -0
  71. package/dist/cli/utils/parse.d.ts.map +1 -1
  72. package/dist/cli/utils/skills.d.ts +1 -1
  73. package/dist/cli/utils/skills.d.ts.map +1 -1
  74. package/dist/cli/utils/workflow-message.d.ts +3 -0
  75. package/dist/cli/utils/workflow-message.d.ts.map +1 -1
  76. package/dist/config.d.ts +1 -0
  77. package/dist/config.d.ts.map +1 -1
  78. package/dist/core/bootstrap.d.ts.map +1 -1
  79. package/dist/core/runtime-assemblies.d.ts +2 -1
  80. package/dist/core/runtime-assemblies.d.ts.map +1 -1
  81. package/dist/core/types.d.ts +1 -1
  82. package/dist/core/types.d.ts.map +1 -1
  83. package/dist/desktop/runtime.d.ts +1 -0
  84. package/dist/desktop/runtime.d.ts.map +1 -1
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +729 -448
  87. package/dist/index.js.map +1 -1
  88. package/dist/inspiredesign/handoff.d.ts +34 -0
  89. package/dist/inspiredesign/handoff.d.ts.map +1 -0
  90. package/dist/opendevbrowser.d.ts.map +1 -1
  91. package/dist/opendevbrowser.js +729 -448
  92. package/dist/opendevbrowser.js.map +1 -1
  93. package/dist/providers/browser-fallback.d.ts.map +1 -1
  94. package/dist/providers/constraint.d.ts +11 -0
  95. package/dist/providers/constraint.d.ts.map +1 -1
  96. package/dist/providers/cookie-source.d.ts +8 -0
  97. package/dist/providers/cookie-source.d.ts.map +1 -0
  98. package/dist/providers/index.d.ts.map +1 -1
  99. package/dist/providers/inspiredesign-capture.d.ts +17 -0
  100. package/dist/providers/inspiredesign-capture.d.ts.map +1 -0
  101. package/dist/providers/inspiredesign-contract.d.ts +110 -0
  102. package/dist/providers/inspiredesign-contract.d.ts.map +1 -0
  103. package/dist/providers/renderer.d.ts +23 -0
  104. package/dist/providers/renderer.d.ts.map +1 -1
  105. package/dist/providers/runtime-bundle.d.ts +2 -1
  106. package/dist/providers/runtime-bundle.d.ts.map +1 -1
  107. package/dist/providers/runtime-factory.d.ts +4 -2
  108. package/dist/providers/runtime-factory.d.ts.map +1 -1
  109. package/dist/providers/shopping/index.d.ts +1 -1
  110. package/dist/providers/types.d.ts +3 -2
  111. package/dist/providers/types.d.ts.map +1 -1
  112. package/dist/providers/workflow-contracts.d.ts +1 -1
  113. package/dist/providers/workflow-contracts.d.ts.map +1 -1
  114. package/dist/providers/workflow-handoff.d.ts +14 -0
  115. package/dist/providers/workflow-handoff.d.ts.map +1 -0
  116. package/dist/providers/workflows.d.ts +21 -2
  117. package/dist/providers/workflows.d.ts.map +1 -1
  118. package/dist/{providers-G36AM3Z2.js → providers-ZIVHHH4F.js} +6 -2
  119. package/dist/public-surface/generated-manifest.d.ts +143 -7
  120. package/dist/public-surface/generated-manifest.d.ts.map +1 -1
  121. package/dist/public-surface/source.d.ts +102 -3
  122. package/dist/public-surface/source.d.ts.map +1 -1
  123. package/dist/relay/protocol.d.ts +1 -1
  124. package/dist/relay/protocol.d.ts.map +1 -1
  125. package/dist/relay/relay-server.d.ts +1 -0
  126. package/dist/relay/relay-server.d.ts.map +1 -1
  127. package/dist/skills/skill-loader.js +1 -1
  128. package/dist/tools/automation-shared.d.ts +6 -0
  129. package/dist/tools/automation-shared.d.ts.map +1 -0
  130. package/dist/tools/index.d.ts.map +1 -1
  131. package/dist/tools/inspiredesign_run.d.ts +4 -0
  132. package/dist/tools/inspiredesign_run.d.ts.map +1 -0
  133. package/dist/tools/review_desktop.d.ts +4 -0
  134. package/dist/tools/review_desktop.d.ts.map +1 -0
  135. package/dist/tools/session_inspector.d.ts.map +1 -1
  136. package/dist/tools/session_inspector_audit.d.ts +4 -0
  137. package/dist/tools/session_inspector_audit.d.ts.map +1 -0
  138. package/dist/tools/session_inspector_plan.d.ts +4 -0
  139. package/dist/tools/session_inspector_plan.d.ts.map +1 -0
  140. package/dist/tools/status_capabilities.d.ts +4 -0
  141. package/dist/tools/status_capabilities.d.ts.map +1 -0
  142. package/extension/dist/background.js +70 -10
  143. package/extension/dist/canvas/canvas-runtime.js +14 -1
  144. package/extension/dist/ops/ops-runtime.js +18 -1
  145. package/extension/dist/popup.js +29 -16
  146. package/extension/dist/services/ConnectionManager.js +27 -2
  147. package/extension/manifest.json +1 -1
  148. package/extension/popup.html +11 -0
  149. package/package.json +5 -5
  150. package/skills/AGENTS.md +2 -2
  151. package/skills/opendevbrowser-best-practices/SKILL.md +50 -15
  152. package/skills/opendevbrowser-best-practices/artifacts/canvas-governance-playbook.md +31 -12
  153. package/skills/opendevbrowser-best-practices/artifacts/command-channel-reference.md +64 -15
  154. package/skills/opendevbrowser-best-practices/artifacts/provider-workflows.md +4 -0
  155. package/skills/opendevbrowser-best-practices/artifacts/skill-runtime-surface-matrix.md +11 -10
  156. package/skills/opendevbrowser-best-practices/assets/templates/canvas-blocker-checklist.json +28 -22
  157. package/skills/opendevbrowser-best-practices/assets/templates/canvas-generation-plan.v1.json +18 -17
  158. package/skills/opendevbrowser-best-practices/assets/templates/canvas-handshake-example.json +135 -17
  159. package/skills/opendevbrowser-best-practices/assets/templates/skill-runtime-pack-matrix.json +55 -8
  160. package/skills/opendevbrowser-best-practices/assets/templates/surface-audit-checklist.json +18 -4
  161. package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +16 -4
  162. package/skills/opendevbrowser-best-practices/scripts/run-robustness-audit.sh +3 -1
  163. package/skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh +68 -25
  164. package/skills/opendevbrowser-design-agent/SKILL.md +9 -4
  165. package/skills/opendevbrowser-design-agent/artifacts/design-workflows.md +15 -6
  166. package/skills/opendevbrowser-design-agent/assets/templates/canvas-generation-plan.design.v1.json +18 -17
  167. package/skills/opendevbrowser-design-agent/scripts/design-workflow.sh +11 -0
  168. package/skills/opendevbrowser-design-agent/scripts/validate-skill-assets.sh +57 -0
  169. package/skills/opendevbrowser-product-presentation-asset/SKILL.md +2 -2
  170. package/dist/chunk-5FZQJRBQ.js.map +0 -1
  171. package/dist/chunk-W4IHGDXV.js.map +0 -1
  172. package/dist/chunk-YBQECXZX.js.map +0 -1
  173. /package/dist/{providers-G36AM3Z2.js.map → providers-ZIVHHH4F.js.map} +0 -0
@@ -2,6 +2,7 @@ import {
2
2
  ProviderRuntimeError,
3
3
  createProviderError,
4
4
  isProviderReasonCode,
5
+ isProviderRuntimeError,
5
6
  normalizeProviderReasonCode,
6
7
  providerErrorCodeFromReasonCode,
7
8
  toProviderError
@@ -796,6 +797,12 @@ var applyProviderIssueHint = (details, hint) => {
796
797
  if (hint.constraint && !isProviderConstraint(next.constraint)) {
797
798
  next.constraint = hint.constraint;
798
799
  }
800
+ if (typeof next.guidance === "undefined") {
801
+ const guidance = buildProviderIssueGuidance({ hint, details: next });
802
+ if (guidance) {
803
+ next.guidance = guidance;
804
+ }
805
+ }
799
806
  return next;
800
807
  };
801
808
  var providerLabel = (provider) => {
@@ -805,6 +812,77 @@ var providerLabel = (provider) => {
805
812
  const tail = separator >= 0 ? normalized.slice(separator + 1) : normalized;
806
813
  return tail.charAt(0).toUpperCase() + tail.slice(1);
807
814
  };
815
+ var hasPreservedBrowserState = (details) => {
816
+ return typeof details?.preservedSessionId === "string" || typeof details?.preservedTargetId === "string";
817
+ };
818
+ var buildGuidance = (reason, recommendedNextCommands) => ({
819
+ reason,
820
+ recommendedNextCommands
821
+ });
822
+ var buildAuthGuidance = (subject, preservedBrowserState) => {
823
+ return preservedBrowserState ? buildGuidance(
824
+ `${subject} preserved browser state that can finish authentication.`,
825
+ [
826
+ "Complete the login or account checkpoint in the preserved browser session.",
827
+ "Rerun the same provider or workflow after the session is fully authenticated."
828
+ ]
829
+ ) : buildGuidance(
830
+ `${subject} needs an authenticated session before retrying.`,
831
+ [
832
+ "Reuse an authenticated browser session, import logged-in cookies, or use the provider sign-in flow.",
833
+ "Rerun the same provider or workflow once the session is active."
834
+ ]
835
+ );
836
+ };
837
+ var buildChallengeGuidance = (subject, preservedBrowserState) => {
838
+ return preservedBrowserState ? buildGuidance(
839
+ `${subject} preserved browser state that can complete the current challenge.`,
840
+ [
841
+ "Finish the login or anti-bot challenge in the preserved browser session.",
842
+ "Rerun the same provider or workflow after the page unlocks."
843
+ ]
844
+ ) : buildGuidance(
845
+ `${subject} hit a challenge that still needs browser-assisted follow-up.`,
846
+ [
847
+ "Retry with browser assistance so the challenge can be completed interactively.",
848
+ "Only ask for manual credentials if browser-assisted recovery still cannot unlock the page."
849
+ ]
850
+ );
851
+ };
852
+ var buildRenderGuidance = (subject, preservedBrowserState) => {
853
+ return preservedBrowserState ? buildGuidance(
854
+ `${subject} still needs a live browser-rendered page, but browser state is already preserved.`,
855
+ [
856
+ "Inspect the preserved browser session until usable content is visible.",
857
+ "Rerun the same provider or workflow after the rendered page is ready."
858
+ ]
859
+ ) : buildGuidance(
860
+ `${subject} needs a live browser-rendered page before retrying.`,
861
+ [
862
+ "Retry with browser assistance or a headed browser session.",
863
+ "Rerun the same provider or workflow after the rendered page is ready."
864
+ ]
865
+ );
866
+ };
867
+ var buildProviderIssueGuidance = (args) => {
868
+ const subject = providerLabel(args.provider);
869
+ const preservedBrowserState = hasPreservedBrowserState(args.details);
870
+ const disposition = toNonEmptyString(args.details?.disposition);
871
+ if (disposition === "completed") return void 0;
872
+ if (disposition === "challenge_preserved") {
873
+ return buildChallengeGuidance(subject, true);
874
+ }
875
+ if (args.hint.reasonCode === "token_required" || args.hint.reasonCode === "auth_required") {
876
+ return buildAuthGuidance(subject, preservedBrowserState);
877
+ }
878
+ if (args.hint.reasonCode === "challenge_detected") {
879
+ return buildChallengeGuidance(subject, preservedBrowserState);
880
+ }
881
+ if (args.hint.constraint?.kind === "render_required" || args.hint.reasonCode === "env_limited") {
882
+ return buildRenderGuidance(subject, preservedBrowserState);
883
+ }
884
+ return void 0;
885
+ };
808
886
  var summaryPriority = (hint) => {
809
887
  if (hint.reasonCode === "token_required" || hint.reasonCode === "auth_required") return 3;
810
888
  if (hint.reasonCode === "challenge_detected") return 2;
@@ -836,10 +914,16 @@ var summarizePrimaryProviderIssue = (failures) => {
836
914
  });
837
915
  if (!hint) continue;
838
916
  const summary = summarizeProviderIssue({ provider: failure.provider, hint });
917
+ const guidance = buildProviderIssueGuidance({
918
+ provider: failure.provider,
919
+ hint,
920
+ details: failure.error?.details
921
+ });
839
922
  const candidate = {
840
923
  provider: failure.provider,
841
924
  summary,
842
- ...hint
925
+ ...hint,
926
+ ...guidance ? { guidance } : {}
843
927
  };
844
928
  if (!best || summaryPriority(candidate) > summaryPriority(best)) {
845
929
  best = candidate;
@@ -1983,6 +2067,138 @@ var verifyChallengeProgress = async (args) => {
1983
2067
  };
1984
2068
  };
1985
2069
 
2070
+ // src/challenges/capability-matrix.ts
2071
+ var buildCapabilityMatrix = (bundle, interpretation, gate) => {
2072
+ const hasActionables = bundle.actionables.length > 0;
2073
+ const helperActionRefs = [
2074
+ ...bundle.continuity.loginRefs,
2075
+ ...bundle.continuity.sessionReuseRefs,
2076
+ ...bundle.continuity.humanVerificationRefs,
2077
+ ...bundle.continuity.checkpointRefs,
2078
+ ...bundle.interaction?.clickRefs ?? [],
2079
+ ...bundle.interaction?.holdRefs ?? [],
2080
+ ...bundle.interaction?.dragRefs ?? []
2081
+ ];
2082
+ const authLaneRelevant = interpretation.classification === "auth_required" || interpretation.classification === "existing_session_reuse" || bundle.continuity.likelyLoginPage || bundle.continuity.likelySessionPicker;
2083
+ const canNavigateToAuth = gate.allowedActions.includes("auth_navigation") && authLaneRelevant && (bundle.continuity.loginRefs.length > 0 || typeof bundle.url === "string");
2084
+ const canReuseExistingSession = gate.allowedActions.includes("session_reuse") && (bundle.continuity.hasPreservedSession || bundle.continuity.attachedSession || bundle.continuity.sessionReuseRefs.length > 0);
2085
+ const canReuseCookies = gate.allowedActions.includes("cookie_reuse") && authLaneRelevant && (bundle.continuity.canReuseExistingCookies || bundle.continuity.canImportCookies);
2086
+ const canFillNonSecretFields = gate.allowedActions.includes("non_secret_form_fill") && bundle.continuity.hasNonSecretTaskData && bundle.continuity.nonSecretFieldRefs.length > 0;
2087
+ const canExploreClicks = gate.allowedActions.includes("click_path") && (hasActionables || bundle.continuity.loginRefs.length > 0 || bundle.continuity.sessionReuseRefs.length > 0 || bundle.continuity.checkpointRefs.length > 0);
2088
+ const mustYield = interpretation.humanBoundary === "secret_entry" || interpretation.humanBoundary === "mfa" || interpretation.humanBoundary === "explicit_consent" || interpretation.humanBoundary === "exhausted_no_progress";
2089
+ const mustDefer = interpretation.humanBoundary === "policy_blocked" || !bundle.blocker && bundle.blockerState === "clear";
2090
+ let helperEligibility = gate.helperEligibility ?? (gate.optionalComputerUseBridge ? {
2091
+ allowed: true,
2092
+ reason: "Optional helper bridge remains eligible after policy resolution."
2093
+ } : {
2094
+ allowed: false,
2095
+ reason: "Optional computer-use bridge is disabled by policy.",
2096
+ standDownReason: "helper_disabled_by_policy"
2097
+ });
2098
+ if (helperEligibility.allowed && interpretation.humanBoundary !== "none") {
2099
+ helperEligibility = {
2100
+ allowed: false,
2101
+ reason: `Helper bridge is blocked by human boundary: ${interpretation.humanBoundary}.`,
2102
+ standDownReason: "helper_blocked_by_human_boundary"
2103
+ };
2104
+ } else if (helperEligibility.allowed && helperActionRefs.length === 0) {
2105
+ helperEligibility = {
2106
+ allowed: false,
2107
+ reason: "Canonical evidence did not expose any safe browser-scoped helper actions.",
2108
+ standDownReason: "helper_no_safe_actions"
2109
+ };
2110
+ }
2111
+ return {
2112
+ canNavigateToAuth,
2113
+ canReuseExistingSession,
2114
+ canReuseCookies,
2115
+ canFillNonSecretFields,
2116
+ canExploreClicks,
2117
+ canUseOwnedEnvironmentFixture: interpretation.classification === "owned_environment_test_challenge" && gate.governedLanes.includes("owned_environment_fixture"),
2118
+ canUseSanctionedIdentity: gate.governedLanes.includes("sanctioned_identity"),
2119
+ canUseServiceAdapter: gate.governedLanes.includes("service_adapter"),
2120
+ canUseComputerUseBridge: helperEligibility.allowed,
2121
+ helperEligibility,
2122
+ mustYield,
2123
+ mustDefer
2124
+ };
2125
+ };
2126
+
2127
+ // src/challenges/capture.ts
2128
+ async function captureChallengeEvidence(args) {
2129
+ const status = await args.handle.status(args.sessionId);
2130
+ const effectiveTargetId = args.targetId ?? status.activeTargetId ?? null;
2131
+ const snapshot = await args.handle.snapshot(
2132
+ args.sessionId,
2133
+ "actionables",
2134
+ 2400,
2135
+ void 0,
2136
+ effectiveTargetId
2137
+ );
2138
+ const debugTrace = await args.handle.debugTraceSnapshot(args.sessionId, { max: 50 });
2139
+ const cookies = status.url ? await args.handle.cookieList(args.sessionId, [status.url]) : { count: 0 };
2140
+ return buildChallengeEvidenceBundle({
2141
+ status,
2142
+ snapshot: {
2143
+ snapshotId: snapshot.snapshotId,
2144
+ content: snapshot.content,
2145
+ warnings: snapshot.warnings
2146
+ },
2147
+ debugTrace,
2148
+ cookieCount: cookies.count,
2149
+ canImportCookies: args.canImportCookies,
2150
+ fallbackDisposition: args.fallbackDisposition,
2151
+ registryPressure: args.registryPressure,
2152
+ taskData: args.taskData
2153
+ });
2154
+ }
2155
+
2156
+ // src/challenges/human-yield-gate.ts
2157
+ var shouldYieldToHuman = (args) => {
2158
+ if (args.noProgressExhausted) {
2159
+ return {
2160
+ yield: true,
2161
+ reason: "exhausted_no_progress"
2162
+ };
2163
+ }
2164
+ return {
2165
+ yield: args.interpretation.humanBoundary !== "none",
2166
+ reason: args.interpretation.humanBoundary
2167
+ };
2168
+ };
2169
+ var buildHumanYieldPacket = (args) => {
2170
+ const challengeId = args.bundle.challengeId ?? `challenge-${Date.now()}`;
2171
+ const targetHints = [
2172
+ ...args.bundle.continuity.loginRefs,
2173
+ ...args.bundle.continuity.sessionReuseRefs,
2174
+ ...args.bundle.continuity.humanVerificationRefs,
2175
+ ...args.bundle.continuity.checkpointRefs
2176
+ ];
2177
+ return {
2178
+ challengeId,
2179
+ classification: args.interpretation.classification,
2180
+ reason: args.reason,
2181
+ sessionId: args.sessionId,
2182
+ targetId: args.targetId ?? args.bundle.activeTargetId,
2183
+ ownerSurface: args.bundle.challenge?.ownerSurface ?? args.bundle.ownerSurface,
2184
+ url: args.bundle.url,
2185
+ title: args.bundle.title,
2186
+ requiredHumanStep: args.reason === "mfa" ? "Complete MFA or passkey verification, then continue." : args.reason === "secret_entry" ? "Enter the required secret-bearing credentials, then continue." : args.reason === "unsupported_third_party" ? "Complete the unsupported third-party challenge manually, then continue." : "Review the page state and continue when ready.",
2187
+ targetHints,
2188
+ expectedPostAuthCheckpoint: args.interpretation.likelyCheckpoint,
2189
+ preserveUntil: args.bundle.challenge?.preserveUntil,
2190
+ verifyUntil: args.bundle.challenge?.verifyUntil,
2191
+ traceRequestId: args.bundle.diagnostics.traceRequestId,
2192
+ ...args.verification ? {
2193
+ lastVerificationStatus: args.verification.status,
2194
+ lastVerificationReason: args.verification.reason
2195
+ } : {},
2196
+ evidenceSummary: args.interpretation.summary,
2197
+ reclaimHint: "Resume the preserved session and re-run verification after the human step completes.",
2198
+ resumeRule: "Re-run manager-owned verification after the human step completes before resuming automation."
2199
+ };
2200
+ };
2201
+
1986
2202
  // src/challenges/optional-computer-use-bridge.ts
1987
2203
  var OPTIONAL_BRIDGE_SUGGESTION_REASON = "Optional bridge suggested a browser-scoped click follow-up from canonical evidence.";
1988
2204
  var buildComputerUseSuggestions = (bundle, maxSuggestions) => {
@@ -2027,907 +2243,955 @@ var suggestComputerUseActions = (args) => {
2027
2243
  };
2028
2244
  };
2029
2245
 
2030
- // src/challenges/action-loop.ts
2031
- var SENSITIVE_FIELD_RE = /\b(password|passcode|secret|otp|mfa|token|verification code|passkey)\b/i;
2032
- var DEFAULT_HOLD_MS2 = 1500;
2033
- var hasExecuted = (steps, kind, ref, url) => {
2034
- return steps.some((step) => step.kind === kind && step.ref === ref && step.url === url);
2035
- };
2036
- var getActionable = (bundle, ref) => {
2037
- return bundle.actionables.find((entry) => entry.ref === ref);
2038
- };
2039
- var deriveAuthUrls = (url) => {
2040
- if (!url) return [];
2041
- try {
2042
- const current = new URL(url);
2043
- const candidates = ["/login", "/signin", "/sign-in", "/account/login", "/session", "/auth/login"];
2044
- return candidates.map((path2) => new URL(path2, current.origin).toString());
2045
- } catch {
2046
- return [];
2246
+ // src/challenges/policy-gate.ts
2247
+ var ALL_ACTIONS = [
2248
+ "wait",
2249
+ "auth_navigation",
2250
+ "session_reuse",
2251
+ "cookie_reuse",
2252
+ "element_discovery",
2253
+ "click_path",
2254
+ "click_and_hold",
2255
+ "non_secret_form_fill",
2256
+ "dropdown",
2257
+ "scroll",
2258
+ "hover",
2259
+ "press",
2260
+ "pointer",
2261
+ "drag",
2262
+ "verification",
2263
+ "debug_trace"
2264
+ ];
2265
+ var DEFAULT_HANDOFF_TRIGGERS = [
2266
+ "secret_entry",
2267
+ "mfa",
2268
+ "explicit_consent",
2269
+ "policy_blocked",
2270
+ "unsupported_third_party",
2271
+ "exhausted_no_progress"
2272
+ ];
2273
+ var buildResolvedPolicy = (mode, source) => {
2274
+ if (mode === "off") {
2275
+ return {
2276
+ mode,
2277
+ source,
2278
+ standDownReason: "challenge_automation_off"
2279
+ };
2047
2280
  }
2048
- };
2049
- var resolveTaskValue = (bundle, ref) => {
2050
- const actionable = getActionable(bundle, ref);
2051
- const name = actionable?.name?.toLowerCase();
2052
- if (!name || !bundle.taskData) return void 0;
2053
- const entries = Object.entries(bundle.taskData);
2054
- for (const [key, value] of entries) {
2055
- if (SENSITIVE_FIELD_RE.test(key) || SENSITIVE_FIELD_RE.test(name)) {
2056
- continue;
2057
- }
2058
- if (!name.includes(key.toLowerCase())) {
2059
- continue;
2060
- }
2061
- switch (typeof value) {
2062
- case "string":
2063
- case "number":
2064
- case "boolean":
2065
- return String(value);
2066
- default:
2067
- break;
2068
- }
2281
+ if (mode === "browser") {
2282
+ return {
2283
+ mode,
2284
+ source,
2285
+ standDownReason: "helper_disabled_for_browser_mode"
2286
+ };
2069
2287
  }
2070
- return void 0;
2071
- };
2072
- var nextUnusedRef = (refs, steps, kind) => {
2073
- return refs.find((ref) => !hasExecuted(steps, kind, ref));
2288
+ return { mode, source };
2074
2289
  };
2075
- var planInteractionStep = (bundle, decision, executedSteps) => {
2076
- const interaction = bundle.interaction;
2077
- if (!interaction || interaction.preferredAction === "unknown") {
2078
- return void 0;
2079
- }
2080
- if (interaction.preferredAction === "click" && decision.allowedActionFamilies.includes("click_path")) {
2081
- const clickRef = nextUnusedRef(interaction.clickRefs, executedSteps, "click");
2082
- if (clickRef) {
2083
- return {
2084
- kind: "click",
2085
- ref: clickRef,
2086
- reason: "Visible popup or interstitial exposes a bounded click path."
2087
- };
2088
- }
2290
+ var resolveChallengeAutomationPolicy = (args) => {
2291
+ if (args.runMode) {
2292
+ return buildResolvedPolicy(args.runMode, "run");
2089
2293
  }
2090
- if (interaction.preferredAction === "click_and_hold" && decision.allowedActionFamilies.includes("click_and_hold")) {
2091
- const holdRef = nextUnusedRef(interaction.holdRefs, executedSteps, "click_and_hold");
2092
- if (holdRef || !hasExecuted(executedSteps, "click_and_hold")) {
2093
- return {
2094
- kind: "click_and_hold",
2095
- ...holdRef ? { ref: holdRef } : {},
2096
- ...typeof interaction.holdMs === "number" ? { holdMs: interaction.holdMs } : {},
2097
- reason: "Visible challenge requests a bounded click-and-hold gesture."
2098
- };
2099
- }
2294
+ if (args.sessionMode) {
2295
+ return buildResolvedPolicy(args.sessionMode, "session");
2100
2296
  }
2101
- if (interaction.preferredAction === "drag" && decision.allowedActionFamilies.includes("drag") && !hasExecuted(executedSteps, "drag")) {
2102
- return {
2103
- kind: "drag",
2104
- ...interaction.dragRefs[0] ? { ref: interaction.dragRefs[0] } : {},
2105
- coordinates: { x: 640, y: 360 },
2106
- reason: "Visible challenge requests a bounded drag gesture."
2107
- };
2108
- }
2109
- return void 0;
2297
+ return buildResolvedPolicy(args.configMode, "config");
2110
2298
  };
2111
- var planGenericStep = (bundle, decision, executedSteps) => {
2112
- const sessionRef = nextUnusedRef(bundle.continuity.sessionReuseRefs, executedSteps, "click");
2113
- if (sessionRef) {
2299
+ var resolveHelperEligibility = (config, policy) => {
2300
+ if (policy.mode === "off") {
2114
2301
  return {
2115
- kind: "click",
2116
- ref: sessionRef,
2117
- reason: "Try the existing-session or account-selection path first."
2302
+ allowed: false,
2303
+ reason: "Challenge automation mode is off; detection and reporting remain active.",
2304
+ standDownReason: "challenge_automation_off"
2118
2305
  };
2119
2306
  }
2120
- const loginRef = nextUnusedRef(bundle.continuity.loginRefs, executedSteps, "click");
2121
- if (decision.allowedActionFamilies.includes("auth_navigation") && loginRef) {
2307
+ if (policy.mode === "browser") {
2122
2308
  return {
2123
- kind: "click",
2124
- ref: loginRef,
2125
- reason: "Try the visible auth-navigation entrypoint."
2309
+ allowed: false,
2310
+ reason: "Browser mode keeps the optional helper bridge disabled.",
2311
+ standDownReason: "helper_disabled_for_browser_mode"
2126
2312
  };
2127
2313
  }
2128
- if (decision.allowedActionFamilies.includes("auth_navigation")) {
2129
- const loginUrl = deriveAuthUrls(bundle.url).find((candidate) => !hasExecuted(executedSteps, "goto", void 0, candidate));
2130
- if (loginUrl) {
2131
- return {
2132
- kind: "goto",
2133
- url: loginUrl,
2134
- reason: "Try a conventional auth-navigation URL on the current origin."
2135
- };
2136
- }
2137
- }
2138
- const fieldRef = nextUnusedRef(bundle.continuity.nonSecretFieldRefs, executedSteps, "type");
2139
- const fieldValue = fieldRef ? resolveTaskValue(bundle, fieldRef) : void 0;
2140
- if (decision.allowedActionFamilies.includes("non_secret_form_fill") && fieldRef && fieldValue) {
2314
+ if (!config.optionalComputerUseBridge.enabled) {
2141
2315
  return {
2142
- kind: "type",
2143
- ref: fieldRef,
2144
- text: fieldValue,
2145
- reason: "Fill a non-secret field from caller-provided task data."
2316
+ allowed: false,
2317
+ reason: "Optional computer-use bridge is disabled by policy.",
2318
+ standDownReason: "helper_disabled_by_policy"
2146
2319
  };
2147
2320
  }
2148
- const checkpointRef = nextUnusedRef(bundle.continuity.checkpointRefs, executedSteps, "click");
2149
- if (decision.allowedActionFamilies.includes("click_path") && checkpointRef) {
2321
+ return {
2322
+ allowed: true,
2323
+ reason: "Optional helper bridge remains eligible after mode resolution."
2324
+ };
2325
+ };
2326
+ var buildChallengePolicyGate = (config, interpretation, resolvedPolicy = resolveChallengeAutomationPolicy({
2327
+ configMode: config.mode
2328
+ })) => {
2329
+ const helperEligibility = resolveHelperEligibility(config, resolvedPolicy);
2330
+ if (resolvedPolicy.mode === "off") {
2150
2331
  return {
2151
- kind: "click",
2152
- ref: checkpointRef,
2153
- reason: "Try the next visible checkpoint or continue action."
2332
+ resolvedPolicy,
2333
+ allowedActions: [],
2334
+ forbiddenActions: [...ALL_ACTIONS],
2335
+ handoffTriggers: [...DEFAULT_HANDOFF_TRIGGERS],
2336
+ governedLanes: [],
2337
+ optionalComputerUseBridge: false,
2338
+ helperEligibility
2154
2339
  };
2155
2340
  }
2156
- const hoverRef = nextUnusedRef([...bundle.continuity.loginRefs, ...bundle.continuity.checkpointRefs], executedSteps, "hover");
2157
- if (decision.allowedActionFamilies.includes("hover") && hoverRef) {
2158
- return {
2159
- kind: "hover",
2160
- ref: hoverRef,
2161
- reason: "Hover a likely action target to reveal hidden menus or session pickers."
2162
- };
2341
+ const allowed = /* @__PURE__ */ new Set(["wait", "verification", "debug_trace"]);
2342
+ if (config.allowAuthNavigation) {
2343
+ allowed.add("auth_navigation");
2163
2344
  }
2164
- if (decision.allowedActionFamilies.includes("scroll") && !hasExecuted(executedSteps, "scroll")) {
2165
- return {
2166
- kind: "scroll",
2167
- dy: 900,
2168
- reason: "Scroll down to uncover the next actionable region."
2169
- };
2345
+ if (config.allowSessionReuse) {
2346
+ allowed.add("session_reuse");
2170
2347
  }
2171
- if (decision.allowedActionFamilies.includes("scroll") && executedSteps.filter((step) => step.kind === "scroll").length === 1) {
2172
- return {
2173
- kind: "scroll",
2174
- dy: -450,
2175
- reason: "Scroll back up to re-evaluate the visible challenge state."
2176
- };
2348
+ if (config.allowCookieReuse) {
2349
+ allowed.add("cookie_reuse");
2177
2350
  }
2178
- if (decision.allowedActionFamilies.includes("press") && !hasExecuted(executedSteps, "press")) {
2179
- return {
2180
- kind: "press",
2181
- text: "Tab",
2182
- reason: "Advance focus through the challenge surface."
2183
- };
2351
+ if (config.allowNonSecretFormFill) {
2352
+ allowed.add("non_secret_form_fill");
2353
+ allowed.add("dropdown");
2184
2354
  }
2185
- if (decision.allowedActionFamilies.includes("pointer") && !hasExecuted(executedSteps, "pointer")) {
2186
- return {
2187
- kind: "pointer",
2188
- coordinates: { x: 640, y: 360 },
2189
- reason: "Move the pointer through the center of the current browser surface."
2190
- };
2355
+ if (config.allowInteractionExploration) {
2356
+ allowed.add("element_discovery");
2357
+ allowed.add("click_path");
2358
+ allowed.add("click_and_hold");
2359
+ allowed.add("scroll");
2360
+ allowed.add("hover");
2361
+ allowed.add("press");
2362
+ allowed.add("pointer");
2363
+ allowed.add("drag");
2191
2364
  }
2192
- if (decision.allowedActionFamilies.includes("drag") && !hasExecuted(executedSteps, "drag")) {
2193
- return {
2194
- kind: "drag",
2195
- coordinates: { x: 640, y: 360 },
2196
- reason: "Attempt one bounded vertical drag across the visible surface."
2197
- };
2365
+ if (interpretation.humanBoundary === "secret_entry" || interpretation.humanBoundary === "mfa") {
2366
+ allowed.delete("non_secret_form_fill");
2198
2367
  }
2199
- if (!hasExecuted(executedSteps, "wait")) {
2200
- return {
2201
- kind: "wait",
2202
- reason: "Give the page a short bounded settle window before yielding."
2203
- };
2368
+ const governedLanes = [];
2369
+ if (config.governed.allowOwnedEnvironmentFixtures) {
2370
+ governedLanes.push("owned_environment_fixture");
2204
2371
  }
2205
- return void 0;
2206
- };
2207
- var resolveStepPoint = async (args) => {
2208
- if (!args.step.ref) {
2209
- return args.step.coordinates ?? args.fallback;
2372
+ if (config.governed.allowSanctionedIdentity) {
2373
+ governedLanes.push("sanctioned_identity");
2210
2374
  }
2211
- return await args.handle.resolveRefPoint(args.sessionId, args.step.ref, args.targetId);
2212
- };
2213
- var executeStep = async (args) => {
2214
- const timeoutMs = args.config.stepTimeoutMs;
2215
- switch (args.step.kind) {
2216
- case "goto":
2217
- if (args.step.url) {
2218
- await args.handle.goto(args.sessionId, args.step.url, "domcontentloaded", timeoutMs, void 0, args.targetId);
2219
- }
2220
- return;
2221
- case "click":
2222
- if (args.step.ref) {
2223
- await args.handle.click(args.sessionId, args.step.ref, args.targetId);
2224
- }
2225
- return;
2226
- case "click_and_hold": {
2227
- const point = await resolveStepPoint({
2228
- handle: args.handle,
2229
- sessionId: args.sessionId,
2230
- step: args.step,
2231
- targetId: args.targetId,
2232
- fallback: { x: 640, y: 360 }
2233
- });
2234
- await args.handle.pointerMove(args.sessionId, point.x, point.y, args.targetId, 12);
2235
- await args.handle.pointerDown(args.sessionId, point.x, point.y, args.targetId, "left", 1);
2236
- await new Promise((resolve2) => setTimeout(resolve2, Math.max(250, args.step.holdMs ?? DEFAULT_HOLD_MS2)));
2237
- await args.handle.pointerUp(args.sessionId, point.x, point.y, args.targetId, "left", 1);
2238
- return;
2239
- }
2240
- case "hover":
2241
- if (args.step.ref) {
2242
- await args.handle.hover(args.sessionId, args.step.ref, args.targetId);
2243
- }
2244
- return;
2245
- case "press":
2246
- await args.handle.press(args.sessionId, args.step.text ?? "Tab", void 0, args.targetId);
2247
- return;
2248
- case "type":
2249
- if (args.step.ref && typeof args.step.text === "string") {
2250
- await args.handle.type(args.sessionId, args.step.ref, args.step.text, true, false, args.targetId);
2251
- }
2252
- return;
2253
- case "select":
2254
- if (args.step.ref && args.step.values?.length) {
2255
- await args.handle.select(args.sessionId, args.step.ref, args.step.values, args.targetId);
2256
- }
2257
- return;
2258
- case "scroll":
2259
- await args.handle.scroll(args.sessionId, args.step.dy ?? 600, void 0, args.targetId);
2260
- return;
2261
- case "pointer":
2262
- await args.handle.pointerMove(
2263
- args.sessionId,
2264
- args.step.coordinates?.x ?? 640,
2265
- args.step.coordinates?.y ?? 360,
2266
- args.targetId,
2267
- 12
2268
- );
2269
- return;
2270
- case "drag":
2271
- {
2272
- const point = await resolveStepPoint({
2273
- handle: args.handle,
2274
- sessionId: args.sessionId,
2275
- step: args.step,
2276
- targetId: args.targetId,
2277
- fallback: args.step.coordinates ?? { x: 640, y: 360 }
2278
- });
2279
- await args.handle.drag(
2280
- args.sessionId,
2281
- point,
2282
- {
2283
- x: point.x,
2284
- y: point.y + 260
2285
- },
2286
- args.targetId,
2287
- 16
2288
- );
2289
- }
2290
- return;
2291
- case "cookie_list":
2292
- await args.handle.cookieList(args.sessionId, args.step.url ? [args.step.url] : void 0);
2293
- return;
2294
- case "cookie_import":
2295
- await args.handle.cookieImport(args.sessionId, args.step.cookies ?? [], true);
2296
- return;
2297
- case "snapshot":
2298
- await args.handle.snapshot(
2299
- args.sessionId,
2300
- "actionables",
2301
- args.step.snapshotChars ?? 2400,
2302
- void 0,
2303
- args.targetId
2304
- );
2305
- return;
2306
- case "debug_trace":
2307
- await args.handle.debugTraceSnapshot(args.sessionId, { max: args.step.traceMax ?? 50 });
2308
- return;
2309
- case "wait":
2310
- await args.handle.waitForLoad(args.sessionId, "networkidle", Math.min(timeoutMs, 3e3), args.targetId);
2311
- return;
2312
- default:
2313
- return;
2375
+ if (config.governed.allowServiceAdapters) {
2376
+ governedLanes.push("service_adapter");
2314
2377
  }
2378
+ return {
2379
+ resolvedPolicy,
2380
+ allowedActions: ALL_ACTIONS.filter((action) => allowed.has(action)),
2381
+ forbiddenActions: ALL_ACTIONS.filter((action) => !allowed.has(action)),
2382
+ handoffTriggers: [...DEFAULT_HANDOFF_TRIGGERS],
2383
+ governedLanes,
2384
+ optionalComputerUseBridge: helperEligibility.allowed,
2385
+ helperEligibility
2386
+ };
2315
2387
  };
2316
- var runChallengeActionLoop = async (args) => {
2317
- let currentBundle = args.initialBundle;
2318
- let currentTargetId = args.targetId ?? args.initialBundle.activeTargetId ?? null;
2319
- const executedSteps = [];
2320
- let noProgressCount = 0;
2321
- let reusedExistingSession = false;
2322
- let reusedCookies = false;
2323
- for (let attempt = 1; attempt <= args.decision.attemptBudget; attempt += 1) {
2324
- const suggestedStep = args.suggestedSteps?.find((candidate) => !hasExecuted(executedSteps, candidate.kind, candidate.ref, candidate.url));
2325
- const helperSuggestedStep = args.config.optionalComputerUseBridge.enabled ? buildComputerUseSuggestions(
2326
- currentBundle,
2327
- args.config.optionalComputerUseBridge.maxSuggestions
2328
- ).find((candidate) => !hasExecuted(executedSteps, candidate.kind, candidate.ref, candidate.url)) : void 0;
2329
- const interactionStep = planInteractionStep(currentBundle, args.decision, executedSteps);
2330
- const step = args.decision.lane === "optional_computer_use_bridge" ? suggestedStep ?? helperSuggestedStep : suggestedStep ?? interactionStep ?? planGenericStep(currentBundle, args.decision, executedSteps) ?? helperSuggestedStep;
2331
- if (!step) {
2332
- return {
2333
- status: "no_progress",
2334
- attempts: attempt - 1,
2335
- noProgressCount,
2336
- executedSteps,
2337
- verification: {
2338
- status: "still_blocked",
2339
- blockerState: currentBundle.blockerState,
2340
- blocker: currentBundle.blocker,
2341
- challenge: currentBundle.challenge,
2342
- bundle: currentBundle,
2343
- changed: false,
2344
- reason: "No additional safe browser action remained.",
2345
- url: currentBundle.url,
2346
- title: currentBundle.title
2347
- },
2348
- reusedExistingSession,
2349
- reusedCookies
2350
- };
2351
- }
2352
- try {
2353
- await executeStep({
2354
- handle: args.handle,
2355
- sessionId: args.sessionId,
2356
- targetId: currentTargetId,
2357
- step,
2358
- config: args.config
2359
- });
2360
- } catch {
2388
+
2389
+ // src/challenges/strategy-selector.ts
2390
+ var buildDecision = (config, lane, rationale, allowedActionFamilies, verificationLevel, stopConditions, governedLane) => ({
2391
+ lane,
2392
+ ...governedLane ? { governedLane } : {},
2393
+ rationale,
2394
+ attemptBudget: config.attemptBudget,
2395
+ noProgressLimit: config.noProgressLimit,
2396
+ verificationLevel,
2397
+ stopConditions,
2398
+ allowedActionFamilies: [...allowedActionFamilies]
2399
+ });
2400
+ var selectChallengeStrategy = (args) => {
2401
+ const { config, bundle, capabilityMatrix, gate, interpretation } = args;
2402
+ const registryCooldownActive = (bundle.registryPressure?.cooldownUntilMs ?? 0) > Date.now();
2403
+ const registryPressureElevated = (bundle.registryPressure?.activeChallenges ?? 0) > 0 || (bundle.registryPressure?.recentChallengeRatio ?? 0) >= 0.5 || (bundle.registryPressure?.recentRateLimitRatio ?? 0) >= 0.5;
2404
+ const stopConditions = [
2405
+ "manager_verification_clears_blocker",
2406
+ "policy_gate_denies_next_action",
2407
+ "human_boundary_detected",
2408
+ "no_progress_budget_exhausted"
2409
+ ];
2410
+ const { mode, standDownReason } = gate.resolvedPolicy;
2411
+ if (mode === "off") {
2412
+ return buildDecision(
2413
+ config,
2414
+ "defer",
2415
+ "Challenge automation mode is off; challenge handling is detect-and-report only.",
2416
+ [],
2417
+ "light",
2418
+ [standDownReason ?? "challenge_automation_off"]
2419
+ );
2420
+ }
2421
+ if (capabilityMatrix.mustDefer) {
2422
+ return buildDecision(
2423
+ config,
2424
+ "defer",
2425
+ "Current policy or blocker state requires deferral before further automation.",
2426
+ gate.allowedActions,
2427
+ interpretation.requiredVerification,
2428
+ ["policy_blocked_or_clear_state"]
2429
+ );
2430
+ }
2431
+ if (registryCooldownActive && !capabilityMatrix.canReuseExistingSession && !capabilityMatrix.canReuseCookies && !capabilityMatrix.canNavigateToAuth) {
2432
+ return buildDecision(
2433
+ config,
2434
+ "defer",
2435
+ "Registry cooldown is still active and no legitimate continuity or auth-navigation lane is currently available.",
2436
+ gate.allowedActions,
2437
+ interpretation.requiredVerification,
2438
+ ["registry_cooldown_active"]
2439
+ );
2440
+ }
2441
+ if (capabilityMatrix.canUseOwnedEnvironmentFixture) {
2442
+ return buildDecision(
2443
+ config,
2444
+ "owned_environment_fixture",
2445
+ "Owned-environment fixture detected and explicitly allowlisted.",
2446
+ gate.allowedActions,
2447
+ interpretation.requiredVerification,
2448
+ stopConditions,
2449
+ "owned_environment_fixture"
2450
+ );
2451
+ }
2452
+ if (capabilityMatrix.mustYield) {
2453
+ return buildDecision(
2454
+ config,
2455
+ "human_yield",
2456
+ `Human authority boundary reached: ${interpretation.humanBoundary}.`,
2457
+ gate.allowedActions,
2458
+ interpretation.requiredVerification,
2459
+ [...stopConditions, "human_authority_required"]
2460
+ );
2461
+ }
2462
+ if (capabilityMatrix.canNavigateToAuth || capabilityMatrix.canReuseExistingSession || capabilityMatrix.canReuseCookies || capabilityMatrix.canFillNonSecretFields || capabilityMatrix.canExploreClicks) {
2463
+ return buildDecision(
2464
+ config,
2465
+ "generic_browser_autonomy",
2466
+ registryPressureElevated ? "Registry pressure is elevated, but legitimate browser continuity remains available for one bounded generic autonomy pass." : "Existing browser controls can attempt bounded auth navigation, session reuse, non-secret fill, or interaction exploration.",
2467
+ gate.allowedActions,
2468
+ interpretation.requiredVerification,
2469
+ registryPressureElevated ? [...stopConditions, "registry_pressure_elevated"] : stopConditions
2470
+ );
2471
+ }
2472
+ if (capabilityMatrix.canUseComputerUseBridge) {
2473
+ return buildDecision(
2474
+ config,
2475
+ "optional_computer_use_bridge",
2476
+ "DOM-native autonomy is exhausted, but the optional browser-scoped bridge is enabled.",
2477
+ gate.allowedActions,
2478
+ interpretation.requiredVerification,
2479
+ stopConditions
2480
+ );
2481
+ }
2482
+ if (capabilityMatrix.canUseSanctionedIdentity) {
2483
+ return buildDecision(
2484
+ config,
2485
+ "sanctioned_identity",
2486
+ "Sanctioned identity lane is enabled and generic browser autonomy is insufficient.",
2487
+ gate.allowedActions,
2488
+ interpretation.requiredVerification,
2489
+ stopConditions,
2490
+ "sanctioned_identity"
2491
+ );
2492
+ }
2493
+ if (capabilityMatrix.canUseServiceAdapter) {
2494
+ return buildDecision(
2495
+ config,
2496
+ "service_adapter",
2497
+ "Governed service-adapter lane is enabled as the last non-human option.",
2498
+ gate.allowedActions,
2499
+ interpretation.requiredVerification,
2500
+ stopConditions,
2501
+ "service_adapter"
2502
+ );
2503
+ }
2504
+ return buildDecision(
2505
+ config,
2506
+ "human_yield",
2507
+ `No legitimate autonomous lane remains after applying policy and continuity checks. ${capabilityMatrix.helperEligibility.reason}`,
2508
+ gate.allowedActions,
2509
+ interpretation.requiredVerification,
2510
+ [...stopConditions, "no_legitimate_lane_remaining"]
2511
+ );
2512
+ };
2513
+
2514
+ // src/challenges/inspect-plan.ts
2515
+ var SENSITIVE_FIELD_RE = /\b(password|passcode|secret|otp|mfa|token|verification code|passkey)\b/i;
2516
+ var hasExecuted = (steps, kind, ref, url) => {
2517
+ return steps.some((step) => step.kind === kind && step.ref === ref && step.url === url);
2518
+ };
2519
+ var getActionable = (bundle, ref) => {
2520
+ return bundle.actionables.find((entry) => entry.ref === ref);
2521
+ };
2522
+ var deriveAuthUrls = (url) => {
2523
+ if (!url) {
2524
+ return [];
2525
+ }
2526
+ try {
2527
+ const current = new URL(url);
2528
+ const candidates = [
2529
+ "/login",
2530
+ "/signin",
2531
+ "/sign-in",
2532
+ "/account/login",
2533
+ "/session",
2534
+ "/auth/login"
2535
+ ];
2536
+ return candidates.map((path2) => new URL(path2, current.origin).toString());
2537
+ } catch {
2538
+ return [];
2539
+ }
2540
+ };
2541
+ var resolveTaskValue = (bundle, ref) => {
2542
+ const actionable = getActionable(bundle, ref);
2543
+ const name = actionable?.name?.toLowerCase();
2544
+ if (!name || !bundle.taskData) {
2545
+ return void 0;
2546
+ }
2547
+ for (const [key, value] of Object.entries(bundle.taskData)) {
2548
+ if (SENSITIVE_FIELD_RE.test(key) || SENSITIVE_FIELD_RE.test(name)) {
2549
+ continue;
2361
2550
  }
2362
- executedSteps.push(step);
2363
- if (step.ref && currentBundle.continuity.sessionReuseRefs.includes(step.ref)) {
2364
- reusedExistingSession = true;
2551
+ if (!name.includes(key.toLowerCase())) {
2552
+ continue;
2365
2553
  }
2366
- if (step.kind === "goto" && currentBundle.continuity.canReuseExistingCookies) {
2367
- reusedCookies = true;
2554
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2555
+ return String(value);
2368
2556
  }
2369
- const verification = await verifyChallengeProgress({
2370
- handle: args.handle,
2371
- sessionId: args.sessionId,
2372
- targetId: currentTargetId,
2373
- previous: currentBundle,
2374
- canImportCookies: currentBundle.continuity.canImportCookies,
2375
- fallbackDisposition: currentBundle.fallbackDisposition,
2376
- registryPressure: currentBundle.registryPressure,
2377
- taskData: currentBundle.taskData
2378
- });
2379
- currentBundle = verification.bundle ?? currentBundle;
2380
- currentTargetId = verification.bundle?.activeTargetId ?? currentTargetId;
2381
- if (verification.status === "clear") {
2557
+ }
2558
+ return void 0;
2559
+ };
2560
+ var nextUnusedRef = (refs, steps, kind) => {
2561
+ return refs.find((ref) => !hasExecuted(steps, kind, ref));
2562
+ };
2563
+ var planInteractionStep = (bundle, decision, executedSteps) => {
2564
+ const interaction = bundle.interaction;
2565
+ if (!interaction || interaction.preferredAction === "unknown") {
2566
+ return void 0;
2567
+ }
2568
+ if (interaction.preferredAction === "click" && decision.allowedActionFamilies.includes("click_path")) {
2569
+ const clickRef = nextUnusedRef(interaction.clickRefs, executedSteps, "click");
2570
+ if (clickRef) {
2382
2571
  return {
2383
- status: "resolved",
2384
- attempts: attempt,
2385
- noProgressCount,
2386
- executedSteps,
2387
- verification,
2388
- reusedExistingSession,
2389
- reusedCookies
2572
+ kind: "click",
2573
+ ref: clickRef,
2574
+ reason: "Visible popup or interstitial exposes a bounded click path."
2390
2575
  };
2391
2576
  }
2392
- if (verification.status === "yield_required") {
2577
+ }
2578
+ if (interaction.preferredAction === "click_and_hold" && decision.allowedActionFamilies.includes("click_and_hold")) {
2579
+ const holdRef = nextUnusedRef(interaction.holdRefs, executedSteps, "click_and_hold");
2580
+ if (holdRef || !hasExecuted(executedSteps, "click_and_hold")) {
2393
2581
  return {
2394
- status: "yield_required",
2395
- attempts: attempt,
2396
- noProgressCount,
2397
- executedSteps,
2398
- verification,
2399
- reusedExistingSession,
2400
- reusedCookies
2582
+ kind: "click_and_hold",
2583
+ ...holdRef ? { ref: holdRef } : {},
2584
+ ...typeof interaction.holdMs === "number" ? { holdMs: interaction.holdMs } : {},
2585
+ reason: "Visible challenge requests a bounded click-and-hold gesture."
2401
2586
  };
2402
2587
  }
2403
- if (verification.status === "deferred") {
2404
- return {
2405
- status: "deferred",
2406
- attempts: attempt,
2407
- noProgressCount,
2408
- executedSteps,
2409
- verification,
2410
- reusedExistingSession,
2411
- reusedCookies
2412
- };
2413
- }
2414
- if (verification.changed) {
2415
- noProgressCount = 0;
2416
- continue;
2417
- }
2418
- noProgressCount += 1;
2419
- if (noProgressCount >= args.decision.noProgressLimit) {
2588
+ }
2589
+ if (interaction.preferredAction === "drag" && decision.allowedActionFamilies.includes("drag") && !hasExecuted(executedSteps, "drag")) {
2590
+ return {
2591
+ kind: "drag",
2592
+ ...interaction.dragRefs[0] ? { ref: interaction.dragRefs[0] } : {},
2593
+ coordinates: { x: 640, y: 360 },
2594
+ reason: "Visible challenge requests a bounded drag gesture."
2595
+ };
2596
+ }
2597
+ return void 0;
2598
+ };
2599
+ var planGenericStep = (bundle, decision, executedSteps) => {
2600
+ const sessionRef = nextUnusedRef(
2601
+ bundle.continuity.sessionReuseRefs,
2602
+ executedSteps,
2603
+ "click"
2604
+ );
2605
+ if (sessionRef) {
2606
+ return {
2607
+ kind: "click",
2608
+ ref: sessionRef,
2609
+ reason: "Try the existing-session or account-selection path first."
2610
+ };
2611
+ }
2612
+ const loginRef = nextUnusedRef(bundle.continuity.loginRefs, executedSteps, "click");
2613
+ if (decision.allowedActionFamilies.includes("auth_navigation") && loginRef) {
2614
+ return {
2615
+ kind: "click",
2616
+ ref: loginRef,
2617
+ reason: "Try the visible auth-navigation entrypoint."
2618
+ };
2619
+ }
2620
+ if (decision.allowedActionFamilies.includes("auth_navigation")) {
2621
+ const loginUrl = deriveAuthUrls(bundle.url).find((candidate) => {
2622
+ return !hasExecuted(executedSteps, "goto", void 0, candidate);
2623
+ });
2624
+ if (loginUrl) {
2420
2625
  return {
2421
- status: "no_progress",
2422
- attempts: attempt,
2423
- noProgressCount,
2424
- executedSteps,
2425
- verification,
2426
- reusedExistingSession,
2427
- reusedCookies
2626
+ kind: "goto",
2627
+ url: loginUrl,
2628
+ reason: "Try a conventional auth-navigation URL on the current origin."
2428
2629
  };
2429
2630
  }
2430
2631
  }
2431
- return {
2432
- status: "still_blocked",
2433
- attempts: args.decision.attemptBudget,
2434
- noProgressCount,
2632
+ const fieldRef = nextUnusedRef(
2633
+ bundle.continuity.nonSecretFieldRefs,
2435
2634
  executedSteps,
2436
- verification: {
2437
- status: "still_blocked",
2438
- blockerState: currentBundle.blockerState,
2439
- blocker: currentBundle.blocker,
2440
- challenge: currentBundle.challenge,
2441
- bundle: currentBundle,
2442
- changed: false,
2443
- reason: "Attempt budget exhausted without clearing the blocker.",
2444
- url: currentBundle.url,
2445
- title: currentBundle.title
2446
- },
2447
- reusedExistingSession,
2448
- reusedCookies
2449
- };
2450
- };
2451
-
2452
- // src/challenges/capability-matrix.ts
2453
- var buildCapabilityMatrix = (bundle, interpretation, gate) => {
2454
- const hasActionables = bundle.actionables.length > 0;
2455
- const helperActionRefs = [
2456
- ...bundle.continuity.loginRefs,
2457
- ...bundle.continuity.sessionReuseRefs,
2458
- ...bundle.continuity.humanVerificationRefs,
2459
- ...bundle.continuity.checkpointRefs,
2460
- ...bundle.interaction?.clickRefs ?? [],
2461
- ...bundle.interaction?.holdRefs ?? [],
2462
- ...bundle.interaction?.dragRefs ?? []
2463
- ];
2464
- const authLaneRelevant = interpretation.classification === "auth_required" || interpretation.classification === "existing_session_reuse" || bundle.continuity.likelyLoginPage || bundle.continuity.likelySessionPicker;
2465
- const canNavigateToAuth = gate.allowedActions.includes("auth_navigation") && authLaneRelevant && (bundle.continuity.loginRefs.length > 0 || typeof bundle.url === "string");
2466
- const canReuseExistingSession = gate.allowedActions.includes("session_reuse") && (bundle.continuity.hasPreservedSession || bundle.continuity.attachedSession || bundle.continuity.sessionReuseRefs.length > 0);
2467
- const canReuseCookies = gate.allowedActions.includes("cookie_reuse") && authLaneRelevant && (bundle.continuity.canReuseExistingCookies || bundle.continuity.canImportCookies);
2468
- const canFillNonSecretFields = gate.allowedActions.includes("non_secret_form_fill") && bundle.continuity.hasNonSecretTaskData && bundle.continuity.nonSecretFieldRefs.length > 0;
2469
- const canExploreClicks = gate.allowedActions.includes("click_path") && (hasActionables || bundle.continuity.loginRefs.length > 0 || bundle.continuity.sessionReuseRefs.length > 0 || bundle.continuity.checkpointRefs.length > 0);
2470
- const mustYield = interpretation.humanBoundary === "secret_entry" || interpretation.humanBoundary === "mfa" || interpretation.humanBoundary === "explicit_consent" || interpretation.humanBoundary === "exhausted_no_progress";
2471
- const mustDefer = interpretation.humanBoundary === "policy_blocked" || !bundle.blocker && bundle.blockerState === "clear";
2472
- let helperEligibility = gate.helperEligibility ?? (gate.optionalComputerUseBridge ? {
2473
- allowed: true,
2474
- reason: "Optional helper bridge remains eligible after policy resolution."
2475
- } : {
2476
- allowed: false,
2477
- reason: "Optional computer-use bridge is disabled by policy.",
2478
- standDownReason: "helper_disabled_by_policy"
2479
- });
2480
- if (helperEligibility.allowed && interpretation.humanBoundary !== "none") {
2481
- helperEligibility = {
2482
- allowed: false,
2483
- reason: `Helper bridge is blocked by human boundary: ${interpretation.humanBoundary}.`,
2484
- standDownReason: "helper_blocked_by_human_boundary"
2635
+ "type"
2636
+ );
2637
+ const fieldValue = fieldRef ? resolveTaskValue(bundle, fieldRef) : void 0;
2638
+ if (decision.allowedActionFamilies.includes("non_secret_form_fill") && fieldRef && fieldValue) {
2639
+ return {
2640
+ kind: "type",
2641
+ ref: fieldRef,
2642
+ text: fieldValue,
2643
+ reason: "Fill a non-secret field from caller-provided task data."
2485
2644
  };
2486
- } else if (helperEligibility.allowed && helperActionRefs.length === 0) {
2487
- helperEligibility = {
2488
- allowed: false,
2489
- reason: "Canonical evidence did not expose any safe browser-scoped helper actions.",
2490
- standDownReason: "helper_no_safe_actions"
2645
+ }
2646
+ const checkpointRef = nextUnusedRef(
2647
+ bundle.continuity.checkpointRefs,
2648
+ executedSteps,
2649
+ "click"
2650
+ );
2651
+ if (decision.allowedActionFamilies.includes("click_path") && checkpointRef) {
2652
+ return {
2653
+ kind: "click",
2654
+ ref: checkpointRef,
2655
+ reason: "Try the next visible checkpoint or continue action."
2491
2656
  };
2492
2657
  }
2493
- return {
2494
- canNavigateToAuth,
2495
- canReuseExistingSession,
2496
- canReuseCookies,
2497
- canFillNonSecretFields,
2498
- canExploreClicks,
2499
- canUseOwnedEnvironmentFixture: interpretation.classification === "owned_environment_test_challenge" && gate.governedLanes.includes("owned_environment_fixture"),
2500
- canUseSanctionedIdentity: gate.governedLanes.includes("sanctioned_identity"),
2501
- canUseServiceAdapter: gate.governedLanes.includes("service_adapter"),
2502
- canUseComputerUseBridge: helperEligibility.allowed,
2503
- helperEligibility,
2504
- mustYield,
2505
- mustDefer
2506
- };
2658
+ const hoverRef = nextUnusedRef(
2659
+ [...bundle.continuity.loginRefs, ...bundle.continuity.checkpointRefs],
2660
+ executedSteps,
2661
+ "hover"
2662
+ );
2663
+ if (decision.allowedActionFamilies.includes("hover") && hoverRef) {
2664
+ return {
2665
+ kind: "hover",
2666
+ ref: hoverRef,
2667
+ reason: "Hover a likely action target to reveal hidden menus or session pickers."
2668
+ };
2669
+ }
2670
+ if (decision.allowedActionFamilies.includes("scroll") && !hasExecuted(executedSteps, "scroll")) {
2671
+ return {
2672
+ kind: "scroll",
2673
+ dy: 900,
2674
+ reason: "Scroll down to uncover the next actionable region."
2675
+ };
2676
+ }
2677
+ if (decision.allowedActionFamilies.includes("scroll") && executedSteps.filter((step) => step.kind === "scroll").length === 1) {
2678
+ return {
2679
+ kind: "scroll",
2680
+ dy: -450,
2681
+ reason: "Scroll back up to re-evaluate the visible challenge state."
2682
+ };
2683
+ }
2684
+ if (decision.allowedActionFamilies.includes("press") && !hasExecuted(executedSteps, "press")) {
2685
+ return {
2686
+ kind: "press",
2687
+ text: "Tab",
2688
+ reason: "Advance focus through the challenge surface."
2689
+ };
2690
+ }
2691
+ if (decision.allowedActionFamilies.includes("pointer") && !hasExecuted(executedSteps, "pointer")) {
2692
+ return {
2693
+ kind: "pointer",
2694
+ coordinates: { x: 640, y: 360 },
2695
+ reason: "Move the pointer through the center of the current browser surface."
2696
+ };
2697
+ }
2698
+ if (decision.allowedActionFamilies.includes("drag") && !hasExecuted(executedSteps, "drag")) {
2699
+ return {
2700
+ kind: "drag",
2701
+ coordinates: { x: 640, y: 360 },
2702
+ reason: "Attempt one bounded vertical drag across the visible surface."
2703
+ };
2704
+ }
2705
+ if (!hasExecuted(executedSteps, "wait")) {
2706
+ return {
2707
+ kind: "wait",
2708
+ reason: "Give the page a short bounded settle window before yielding."
2709
+ };
2710
+ }
2711
+ return void 0;
2507
2712
  };
2508
-
2509
- // src/challenges/owned-environment-lane.ts
2510
- var APPROVED_FIXTURE_RE = /\b(turnstile-checkbox|recaptcha-v2-checkbox|1x00000000000000000000AA|6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI)\b/i;
2511
- var runOwnedEnvironmentLane = (request) => {
2512
- const haystack = [request.bundle.url, request.bundle.title, request.bundle.snapshotText].filter((value) => typeof value === "string" && value.length > 0).join(" ");
2513
- const approved = APPROVED_FIXTURE_RE.test(haystack);
2514
- return {
2515
- status: approved ? "executed" : "blocked",
2516
- lane: "owned_environment_fixture",
2517
- reason: approved ? "Approved owned-environment fixture detected." : "Owned-environment lane requires an approved vendor test fixture.",
2518
- auditMetadata: {
2519
- approvedFixture: approved
2713
+ var pendingSteps = (steps, executedSteps) => {
2714
+ return (steps ?? []).filter((step) => {
2715
+ return !hasExecuted(executedSteps, step.kind, step.ref, step.url);
2716
+ });
2717
+ };
2718
+ var dedupeSteps = (steps) => {
2719
+ const seen = /* @__PURE__ */ new Set();
2720
+ return steps.filter((step) => {
2721
+ const key = JSON.stringify([
2722
+ step.kind,
2723
+ step.ref ?? null,
2724
+ step.url ?? null,
2725
+ step.text ?? null,
2726
+ step.reason
2727
+ ]);
2728
+ if (seen.has(key)) {
2729
+ return false;
2520
2730
  }
2521
- };
2731
+ seen.add(key);
2732
+ return true;
2733
+ });
2522
2734
  };
2523
-
2524
- // src/challenges/sanctioned-identity-lane.ts
2525
- var runSanctionedIdentityLane = (request) => {
2526
- const entitlement = request.auditContext?.identityEntitlement;
2527
- const approved = typeof entitlement === "string" && entitlement.trim().length > 0;
2735
+ var buildChallengeActionSuggestions = (args) => {
2736
+ const executedSteps = args.executedSteps ?? [];
2737
+ const helper = suggestComputerUseActions({
2738
+ helperEligibility: args.helperEligibility,
2739
+ bundle: args.bundle,
2740
+ maxSuggestions: args.config.optionalComputerUseBridge.maxSuggestions
2741
+ });
2742
+ const preferredSteps = pendingSteps(args.preferredSteps, executedSteps);
2743
+ const interactionStep = planInteractionStep(args.bundle, args.decision, executedSteps);
2744
+ const genericStep = planGenericStep(args.bundle, args.decision, executedSteps);
2745
+ const helperSteps = pendingSteps(helper.suggestedSteps, executedSteps);
2746
+ const suggestedSteps = args.decision.lane === "optional_computer_use_bridge" ? dedupeSteps([...preferredSteps, ...helperSteps]) : dedupeSteps([
2747
+ ...preferredSteps,
2748
+ ...interactionStep ? [interactionStep] : [],
2749
+ ...genericStep ? [genericStep] : [],
2750
+ ...helperSteps
2751
+ ]);
2528
2752
  return {
2529
- status: approved ? "executed" : "blocked",
2530
- lane: "sanctioned_identity",
2531
- reason: approved ? "Sanctioned identity lane approved by explicit entitlement." : "Sanctioned identity lane requires explicit entitlement metadata.",
2532
- auditMetadata: {
2533
- approved,
2534
- ...approved ? { entitlement } : {}
2535
- }
2753
+ helper,
2754
+ suggestedSteps
2536
2755
  };
2537
2756
  };
2538
-
2539
- // src/challenges/service-adapter-lane.ts
2540
- var runServiceAdapterLane = (request) => {
2541
- const adapter = request.auditContext?.adapterId;
2542
- const approved = typeof adapter === "string" && adapter.trim().length > 0;
2757
+ var selectChallengeActionStep = (args) => {
2758
+ return buildChallengeActionSuggestions(args).suggestedSteps[0];
2759
+ };
2760
+ function buildChallengeInspectPlan(args) {
2761
+ const interpretation = interpretChallengeEvidence(args.bundle);
2762
+ const policy = resolveChallengeAutomationPolicy({
2763
+ runMode: args.runMode,
2764
+ sessionMode: args.sessionMode,
2765
+ configMode: args.config.mode
2766
+ });
2767
+ const gate = buildChallengePolicyGate(args.config, interpretation, policy);
2768
+ const capabilityMatrix = buildCapabilityMatrix(args.bundle, interpretation, gate);
2769
+ const decision = selectChallengeStrategy({
2770
+ config: args.config,
2771
+ bundle: args.bundle,
2772
+ interpretation,
2773
+ capabilityMatrix,
2774
+ gate
2775
+ });
2776
+ const actionSuggestions = buildChallengeActionSuggestions({
2777
+ bundle: args.bundle,
2778
+ decision,
2779
+ helperEligibility: capabilityMatrix.helperEligibility,
2780
+ config: args.config,
2781
+ preferredSteps: args.preferredSteps,
2782
+ executedSteps: args.executedSteps
2783
+ });
2784
+ const yieldDecision = shouldYieldToHuman({
2785
+ interpretation,
2786
+ noProgressExhausted: false
2787
+ });
2788
+ const standDownReason = capabilityMatrix.helperEligibility.standDownReason ?? gate.resolvedPolicy.standDownReason ?? actionSuggestions.helper.standDownReason;
2543
2789
  return {
2544
- status: approved ? "executed" : "blocked",
2545
- lane: "service_adapter",
2546
- reason: approved ? "Governed service adapter approved by explicit adapter metadata." : "Service-adapter lane requires an explicit adapter identifier and entitlement.",
2547
- auditMetadata: {
2548
- approved,
2549
- ...approved ? { adapterId: adapter } : {}
2790
+ challengeId: args.bundle.challengeId,
2791
+ ...args.bundle.mode ? { sessionMode: args.bundle.mode } : {},
2792
+ classification: interpretation.classification,
2793
+ authState: interpretation.authState,
2794
+ summary: interpretation.summary,
2795
+ mode: gate.resolvedPolicy.mode,
2796
+ source: gate.resolvedPolicy.source,
2797
+ helperEligibility: capabilityMatrix.helperEligibility,
2798
+ ...standDownReason ? { standDownReason } : {},
2799
+ yield: {
2800
+ required: yieldDecision.yield,
2801
+ reason: yieldDecision.reason
2802
+ },
2803
+ decision,
2804
+ allowedActionFamilies: gate.allowedActions,
2805
+ forbiddenActionFamilies: gate.forbiddenActions,
2806
+ governedLanes: gate.governedLanes,
2807
+ capabilityMatrix,
2808
+ helper: actionSuggestions.helper,
2809
+ suggestedSteps: actionSuggestions.suggestedSteps,
2810
+ evidence: {
2811
+ blockerState: args.bundle.blockerState,
2812
+ ...args.bundle.blocker?.type ? { blockerType: args.bundle.blocker.type } : {},
2813
+ ...args.bundle.url ? { url: args.bundle.url } : {},
2814
+ ...args.bundle.title ? { title: args.bundle.title } : {},
2815
+ ...typeof args.bundle.activeTargetId !== "undefined" ? { activeTargetId: args.bundle.activeTargetId } : {},
2816
+ ...args.bundle.snapshotId ? { snapshotId: args.bundle.snapshotId } : {},
2817
+ loginRefs: args.bundle.continuity.loginRefs,
2818
+ sessionReuseRefs: args.bundle.continuity.sessionReuseRefs,
2819
+ humanVerificationRefs: args.bundle.continuity.humanVerificationRefs,
2820
+ checkpointRefs: args.bundle.continuity.checkpointRefs
2550
2821
  }
2551
2822
  };
2552
- };
2823
+ }
2824
+ async function inspectChallengePlanFromRuntime(args) {
2825
+ const bundle = await captureChallengeEvidence({
2826
+ handle: args.handle,
2827
+ sessionId: args.sessionId,
2828
+ targetId: args.targetId,
2829
+ canImportCookies: args.canImportCookies ?? true
2830
+ });
2831
+ return buildChallengeInspectPlan({
2832
+ bundle,
2833
+ config: args.config,
2834
+ runMode: args.runMode,
2835
+ sessionMode: args.sessionMode
2836
+ });
2837
+ }
2553
2838
 
2554
- // src/challenges/governed-adapter-gateway.ts
2555
- var evaluateGovernedLane = (config, request) => {
2556
- if (config.mode === "off") {
2557
- return {
2558
- status: "blocked",
2559
- lane: request.lane,
2560
- reason: "Challenge automation mode is off.",
2561
- auditMetadata: {}
2562
- };
2839
+ // src/challenges/action-loop.ts
2840
+ var DEFAULT_HOLD_MS2 = 1500;
2841
+ var resolveStepPoint = async (args) => {
2842
+ if (!args.step.ref) {
2843
+ return args.step.coordinates ?? args.fallback;
2563
2844
  }
2564
- switch (request.lane) {
2565
- case "owned_environment_fixture":
2566
- if (!config.governed.allowOwnedEnvironmentFixtures) {
2567
- return {
2568
- status: "blocked",
2569
- lane: request.lane,
2570
- reason: "Owned-environment fixtures are disabled by policy.",
2571
- auditMetadata: {}
2572
- };
2845
+ return await args.handle.resolveRefPoint(args.sessionId, args.step.ref, args.targetId);
2846
+ };
2847
+ var executeStep = async (args) => {
2848
+ const timeoutMs = args.config.stepTimeoutMs;
2849
+ switch (args.step.kind) {
2850
+ case "goto":
2851
+ if (args.step.url) {
2852
+ await args.handle.goto(args.sessionId, args.step.url, "domcontentloaded", timeoutMs, void 0, args.targetId);
2573
2853
  }
2574
- return runOwnedEnvironmentLane(request);
2575
- case "sanctioned_identity":
2576
- if (!config.governed.allowSanctionedIdentity) {
2577
- return {
2578
- status: "blocked",
2579
- lane: request.lane,
2580
- reason: "Sanctioned identity is disabled by policy.",
2581
- auditMetadata: {}
2582
- };
2854
+ return;
2855
+ case "click":
2856
+ if (args.step.ref) {
2857
+ await args.handle.click(args.sessionId, args.step.ref, args.targetId);
2583
2858
  }
2584
- return runSanctionedIdentityLane(request);
2585
- case "service_adapter":
2586
- if (!config.governed.allowServiceAdapters) {
2587
- return {
2588
- status: "blocked",
2589
- lane: request.lane,
2590
- reason: "Service adapters are disabled by policy.",
2591
- auditMetadata: {}
2592
- };
2859
+ return;
2860
+ case "click_and_hold": {
2861
+ const point = await resolveStepPoint({
2862
+ handle: args.handle,
2863
+ sessionId: args.sessionId,
2864
+ step: args.step,
2865
+ targetId: args.targetId,
2866
+ fallback: { x: 640, y: 360 }
2867
+ });
2868
+ await args.handle.pointerMove(args.sessionId, point.x, point.y, args.targetId, 12);
2869
+ await args.handle.pointerDown(args.sessionId, point.x, point.y, args.targetId, "left", 1);
2870
+ await new Promise((resolve2) => setTimeout(resolve2, Math.max(250, args.step.holdMs ?? DEFAULT_HOLD_MS2)));
2871
+ await args.handle.pointerUp(args.sessionId, point.x, point.y, args.targetId, "left", 1);
2872
+ return;
2873
+ }
2874
+ case "hover":
2875
+ if (args.step.ref) {
2876
+ await args.handle.hover(args.sessionId, args.step.ref, args.targetId);
2593
2877
  }
2594
- return runServiceAdapterLane(request);
2595
- }
2596
- };
2597
-
2598
- // src/challenges/human-yield-gate.ts
2599
- var shouldYieldToHuman = (args) => {
2600
- if (args.noProgressExhausted) {
2601
- return {
2602
- yield: true,
2603
- reason: "exhausted_no_progress"
2604
- };
2605
- }
2606
- return {
2607
- yield: args.interpretation.humanBoundary !== "none",
2608
- reason: args.interpretation.humanBoundary
2609
- };
2610
- };
2611
- var buildHumanYieldPacket = (args) => {
2612
- const challengeId = args.bundle.challengeId ?? `challenge-${Date.now()}`;
2613
- const targetHints = [
2614
- ...args.bundle.continuity.loginRefs,
2615
- ...args.bundle.continuity.sessionReuseRefs,
2616
- ...args.bundle.continuity.humanVerificationRefs,
2617
- ...args.bundle.continuity.checkpointRefs
2618
- ];
2619
- return {
2620
- challengeId,
2621
- classification: args.interpretation.classification,
2622
- reason: args.reason,
2623
- sessionId: args.sessionId,
2624
- targetId: args.targetId ?? args.bundle.activeTargetId,
2625
- ownerSurface: args.bundle.challenge?.ownerSurface ?? args.bundle.ownerSurface,
2626
- url: args.bundle.url,
2627
- title: args.bundle.title,
2628
- requiredHumanStep: args.reason === "mfa" ? "Complete MFA or passkey verification, then continue." : args.reason === "secret_entry" ? "Enter the required secret-bearing credentials, then continue." : args.reason === "unsupported_third_party" ? "Complete the unsupported third-party challenge manually, then continue." : "Review the page state and continue when ready.",
2629
- targetHints,
2630
- expectedPostAuthCheckpoint: args.interpretation.likelyCheckpoint,
2631
- preserveUntil: args.bundle.challenge?.preserveUntil,
2632
- verifyUntil: args.bundle.challenge?.verifyUntil,
2633
- traceRequestId: args.bundle.diagnostics.traceRequestId,
2634
- ...args.verification ? {
2635
- lastVerificationStatus: args.verification.status,
2636
- lastVerificationReason: args.verification.reason
2637
- } : {},
2638
- evidenceSummary: args.interpretation.summary,
2639
- reclaimHint: "Resume the preserved session and re-run verification after the human step completes.",
2640
- resumeRule: "Re-run manager-owned verification after the human step completes before resuming automation."
2641
- };
2642
- };
2643
-
2644
- // src/challenges/outcome-recorder.ts
2645
- var MAX_RECORDS_PER_CHALLENGE = 25;
2646
- var OutcomeRecorder = class {
2647
- records = /* @__PURE__ */ new Map();
2648
- record(record) {
2649
- const key = record.challengeId ?? "__untracked__";
2650
- const existing = this.records.get(key) ?? [];
2651
- const next = [...existing, record].slice(-MAX_RECORDS_PER_CHALLENGE);
2652
- this.records.set(key, next);
2653
- }
2654
- latest(challengeId) {
2655
- if (!challengeId) return void 0;
2656
- const entries = this.records.get(challengeId);
2657
- return entries?.[entries.length - 1];
2658
- }
2659
- read(challengeId) {
2660
- if (!challengeId) return [];
2661
- return [...this.records.get(challengeId) ?? []];
2878
+ return;
2879
+ case "press":
2880
+ await args.handle.press(args.sessionId, args.step.text ?? "Tab", void 0, args.targetId);
2881
+ return;
2882
+ case "type":
2883
+ if (args.step.ref && typeof args.step.text === "string") {
2884
+ await args.handle.type(args.sessionId, args.step.ref, args.step.text, true, false, args.targetId);
2885
+ }
2886
+ return;
2887
+ case "select":
2888
+ if (args.step.ref && args.step.values?.length) {
2889
+ await args.handle.select(args.sessionId, args.step.ref, args.step.values, args.targetId);
2890
+ }
2891
+ return;
2892
+ case "scroll":
2893
+ await args.handle.scroll(args.sessionId, args.step.dy ?? 600, void 0, args.targetId);
2894
+ return;
2895
+ case "pointer":
2896
+ await args.handle.pointerMove(
2897
+ args.sessionId,
2898
+ args.step.coordinates?.x ?? 640,
2899
+ args.step.coordinates?.y ?? 360,
2900
+ args.targetId,
2901
+ 12
2902
+ );
2903
+ return;
2904
+ case "drag":
2905
+ {
2906
+ const point = await resolveStepPoint({
2907
+ handle: args.handle,
2908
+ sessionId: args.sessionId,
2909
+ step: args.step,
2910
+ targetId: args.targetId,
2911
+ fallback: args.step.coordinates ?? { x: 640, y: 360 }
2912
+ });
2913
+ await args.handle.drag(
2914
+ args.sessionId,
2915
+ point,
2916
+ {
2917
+ x: point.x,
2918
+ y: point.y + 260
2919
+ },
2920
+ args.targetId,
2921
+ 16
2922
+ );
2923
+ }
2924
+ return;
2925
+ case "cookie_list":
2926
+ await args.handle.cookieList(args.sessionId, args.step.url ? [args.step.url] : void 0);
2927
+ return;
2928
+ case "cookie_import":
2929
+ await args.handle.cookieImport(args.sessionId, args.step.cookies ?? [], true);
2930
+ return;
2931
+ case "snapshot":
2932
+ await args.handle.snapshot(
2933
+ args.sessionId,
2934
+ "actionables",
2935
+ args.step.snapshotChars ?? 2400,
2936
+ void 0,
2937
+ args.targetId
2938
+ );
2939
+ return;
2940
+ case "debug_trace":
2941
+ await args.handle.debugTraceSnapshot(args.sessionId, { max: args.step.traceMax ?? 50 });
2942
+ return;
2943
+ case "wait":
2944
+ await args.handle.waitForLoad(args.sessionId, "networkidle", Math.min(timeoutMs, 3e3), args.targetId);
2945
+ return;
2946
+ default:
2947
+ return;
2662
2948
  }
2663
2949
  };
2664
-
2665
- // src/challenges/policy-gate.ts
2666
- var ALL_ACTIONS = [
2667
- "wait",
2668
- "auth_navigation",
2669
- "session_reuse",
2670
- "cookie_reuse",
2671
- "element_discovery",
2672
- "click_path",
2673
- "click_and_hold",
2674
- "non_secret_form_fill",
2675
- "dropdown",
2676
- "scroll",
2677
- "hover",
2678
- "press",
2679
- "pointer",
2680
- "drag",
2681
- "verification",
2682
- "debug_trace"
2683
- ];
2684
- var DEFAULT_HANDOFF_TRIGGERS = [
2685
- "secret_entry",
2686
- "mfa",
2687
- "explicit_consent",
2688
- "policy_blocked",
2689
- "unsupported_third_party",
2690
- "exhausted_no_progress"
2691
- ];
2692
- var buildResolvedPolicy = (mode, source) => {
2693
- if (mode === "off") {
2694
- return {
2695
- mode,
2696
- source,
2697
- standDownReason: "challenge_automation_off"
2698
- };
2699
- }
2700
- if (mode === "browser") {
2701
- return {
2702
- mode,
2703
- source,
2704
- standDownReason: "helper_disabled_for_browser_mode"
2705
- };
2950
+ var runChallengeActionLoop = async (args) => {
2951
+ let currentBundle = args.initialBundle;
2952
+ let currentTargetId = args.targetId ?? args.initialBundle.activeTargetId ?? null;
2953
+ const executedSteps = [];
2954
+ let noProgressCount = 0;
2955
+ let reusedExistingSession = false;
2956
+ let reusedCookies = false;
2957
+ for (let attempt = 1; attempt <= args.decision.attemptBudget; attempt += 1) {
2958
+ const step = selectChallengeActionStep({
2959
+ bundle: currentBundle,
2960
+ decision: args.decision,
2961
+ helperEligibility: args.helperEligibility,
2962
+ config: args.config,
2963
+ preferredSteps: args.suggestedSteps,
2964
+ executedSteps
2965
+ });
2966
+ if (!step) {
2967
+ return {
2968
+ status: "no_progress",
2969
+ attempts: attempt - 1,
2970
+ noProgressCount,
2971
+ executedSteps,
2972
+ verification: {
2973
+ status: "still_blocked",
2974
+ blockerState: currentBundle.blockerState,
2975
+ blocker: currentBundle.blocker,
2976
+ challenge: currentBundle.challenge,
2977
+ bundle: currentBundle,
2978
+ changed: false,
2979
+ reason: "No additional safe browser action remained.",
2980
+ url: currentBundle.url,
2981
+ title: currentBundle.title
2982
+ },
2983
+ reusedExistingSession,
2984
+ reusedCookies
2985
+ };
2986
+ }
2987
+ try {
2988
+ await executeStep({
2989
+ handle: args.handle,
2990
+ sessionId: args.sessionId,
2991
+ targetId: currentTargetId,
2992
+ step,
2993
+ config: args.config
2994
+ });
2995
+ } catch {
2996
+ }
2997
+ executedSteps.push(step);
2998
+ if (step.ref && currentBundle.continuity.sessionReuseRefs.includes(step.ref)) {
2999
+ reusedExistingSession = true;
3000
+ }
3001
+ if (step.kind === "goto" && currentBundle.continuity.canReuseExistingCookies) {
3002
+ reusedCookies = true;
3003
+ }
3004
+ const verification = await verifyChallengeProgress({
3005
+ handle: args.handle,
3006
+ sessionId: args.sessionId,
3007
+ targetId: currentTargetId,
3008
+ previous: currentBundle,
3009
+ canImportCookies: currentBundle.continuity.canImportCookies,
3010
+ fallbackDisposition: currentBundle.fallbackDisposition,
3011
+ registryPressure: currentBundle.registryPressure,
3012
+ taskData: currentBundle.taskData
3013
+ });
3014
+ currentBundle = verification.bundle ?? currentBundle;
3015
+ currentTargetId = verification.bundle?.activeTargetId ?? currentTargetId;
3016
+ if (verification.status === "clear") {
3017
+ return {
3018
+ status: "resolved",
3019
+ attempts: attempt,
3020
+ noProgressCount,
3021
+ executedSteps,
3022
+ verification,
3023
+ reusedExistingSession,
3024
+ reusedCookies
3025
+ };
3026
+ }
3027
+ if (verification.status === "yield_required") {
3028
+ return {
3029
+ status: "yield_required",
3030
+ attempts: attempt,
3031
+ noProgressCount,
3032
+ executedSteps,
3033
+ verification,
3034
+ reusedExistingSession,
3035
+ reusedCookies
3036
+ };
3037
+ }
3038
+ if (verification.status === "deferred") {
3039
+ return {
3040
+ status: "deferred",
3041
+ attempts: attempt,
3042
+ noProgressCount,
3043
+ executedSteps,
3044
+ verification,
3045
+ reusedExistingSession,
3046
+ reusedCookies
3047
+ };
3048
+ }
3049
+ if (verification.changed) {
3050
+ noProgressCount = 0;
3051
+ continue;
3052
+ }
3053
+ noProgressCount += 1;
3054
+ if (noProgressCount >= args.decision.noProgressLimit) {
3055
+ return {
3056
+ status: "no_progress",
3057
+ attempts: attempt,
3058
+ noProgressCount,
3059
+ executedSteps,
3060
+ verification,
3061
+ reusedExistingSession,
3062
+ reusedCookies
3063
+ };
3064
+ }
2706
3065
  }
2707
- return { mode, source };
3066
+ return {
3067
+ status: "still_blocked",
3068
+ attempts: args.decision.attemptBudget,
3069
+ noProgressCount,
3070
+ executedSteps,
3071
+ verification: {
3072
+ status: "still_blocked",
3073
+ blockerState: currentBundle.blockerState,
3074
+ blocker: currentBundle.blocker,
3075
+ challenge: currentBundle.challenge,
3076
+ bundle: currentBundle,
3077
+ changed: false,
3078
+ reason: "Attempt budget exhausted without clearing the blocker.",
3079
+ url: currentBundle.url,
3080
+ title: currentBundle.title
3081
+ },
3082
+ reusedExistingSession,
3083
+ reusedCookies
3084
+ };
2708
3085
  };
2709
- var resolveChallengeAutomationPolicy = (args) => {
2710
- if (args.runMode) {
2711
- return buildResolvedPolicy(args.runMode, "run");
2712
- }
2713
- if (args.sessionMode) {
2714
- return buildResolvedPolicy(args.sessionMode, "session");
2715
- }
2716
- return buildResolvedPolicy(args.configMode, "config");
3086
+
3087
+ // src/challenges/owned-environment-lane.ts
3088
+ var APPROVED_FIXTURE_RE = /\b(turnstile-checkbox|recaptcha-v2-checkbox|1x00000000000000000000AA|6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI)\b/i;
3089
+ var runOwnedEnvironmentLane = (request) => {
3090
+ const haystack = [request.bundle.url, request.bundle.title, request.bundle.snapshotText].filter((value) => typeof value === "string" && value.length > 0).join(" ");
3091
+ const approved = APPROVED_FIXTURE_RE.test(haystack);
3092
+ return {
3093
+ status: approved ? "executed" : "blocked",
3094
+ lane: "owned_environment_fixture",
3095
+ reason: approved ? "Approved owned-environment fixture detected." : "Owned-environment lane requires an approved vendor test fixture.",
3096
+ auditMetadata: {
3097
+ approvedFixture: approved
3098
+ }
3099
+ };
2717
3100
  };
2718
- var resolveHelperEligibility = (config, policy) => {
2719
- if (policy.mode === "off") {
2720
- return {
2721
- allowed: false,
2722
- reason: "Challenge automation mode is off; detection and reporting remain active.",
2723
- standDownReason: "challenge_automation_off"
2724
- };
2725
- }
2726
- if (policy.mode === "browser") {
2727
- return {
2728
- allowed: false,
2729
- reason: "Browser mode keeps the optional helper bridge disabled.",
2730
- standDownReason: "helper_disabled_for_browser_mode"
2731
- };
2732
- }
2733
- if (!config.optionalComputerUseBridge.enabled) {
2734
- return {
2735
- allowed: false,
2736
- reason: "Optional computer-use bridge is disabled by policy.",
2737
- standDownReason: "helper_disabled_by_policy"
2738
- };
2739
- }
3101
+
3102
+ // src/challenges/sanctioned-identity-lane.ts
3103
+ var runSanctionedIdentityLane = (request) => {
3104
+ const entitlement = request.auditContext?.identityEntitlement;
3105
+ const approved = typeof entitlement === "string" && entitlement.trim().length > 0;
2740
3106
  return {
2741
- allowed: true,
2742
- reason: "Optional helper bridge remains eligible after mode resolution."
3107
+ status: approved ? "executed" : "blocked",
3108
+ lane: "sanctioned_identity",
3109
+ reason: approved ? "Sanctioned identity lane approved by explicit entitlement." : "Sanctioned identity lane requires explicit entitlement metadata.",
3110
+ auditMetadata: {
3111
+ approved,
3112
+ ...approved ? { entitlement } : {}
3113
+ }
2743
3114
  };
2744
3115
  };
2745
- var buildChallengePolicyGate = (config, interpretation, resolvedPolicy = resolveChallengeAutomationPolicy({
2746
- configMode: config.mode
2747
- })) => {
2748
- const helperEligibility = resolveHelperEligibility(config, resolvedPolicy);
2749
- if (resolvedPolicy.mode === "off") {
2750
- return {
2751
- resolvedPolicy,
2752
- allowedActions: [],
2753
- forbiddenActions: [...ALL_ACTIONS],
2754
- handoffTriggers: [...DEFAULT_HANDOFF_TRIGGERS],
2755
- governedLanes: [],
2756
- optionalComputerUseBridge: false,
2757
- helperEligibility
2758
- };
2759
- }
2760
- const allowed = /* @__PURE__ */ new Set(["wait", "verification", "debug_trace"]);
2761
- if (config.allowAuthNavigation) {
2762
- allowed.add("auth_navigation");
2763
- }
2764
- if (config.allowSessionReuse) {
2765
- allowed.add("session_reuse");
2766
- }
2767
- if (config.allowCookieReuse) {
2768
- allowed.add("cookie_reuse");
2769
- }
2770
- if (config.allowNonSecretFormFill) {
2771
- allowed.add("non_secret_form_fill");
2772
- allowed.add("dropdown");
2773
- }
2774
- if (config.allowInteractionExploration) {
2775
- allowed.add("element_discovery");
2776
- allowed.add("click_path");
2777
- allowed.add("click_and_hold");
2778
- allowed.add("scroll");
2779
- allowed.add("hover");
2780
- allowed.add("press");
2781
- allowed.add("pointer");
2782
- allowed.add("drag");
2783
- }
2784
- if (interpretation.humanBoundary === "secret_entry" || interpretation.humanBoundary === "mfa") {
2785
- allowed.delete("non_secret_form_fill");
2786
- }
2787
- const governedLanes = [];
2788
- if (config.governed.allowOwnedEnvironmentFixtures) {
2789
- governedLanes.push("owned_environment_fixture");
2790
- }
2791
- if (config.governed.allowSanctionedIdentity) {
2792
- governedLanes.push("sanctioned_identity");
2793
- }
2794
- if (config.governed.allowServiceAdapters) {
2795
- governedLanes.push("service_adapter");
2796
- }
3116
+
3117
+ // src/challenges/service-adapter-lane.ts
3118
+ var runServiceAdapterLane = (request) => {
3119
+ const adapter = request.auditContext?.adapterId;
3120
+ const approved = typeof adapter === "string" && adapter.trim().length > 0;
2797
3121
  return {
2798
- resolvedPolicy,
2799
- allowedActions: ALL_ACTIONS.filter((action) => allowed.has(action)),
2800
- forbiddenActions: ALL_ACTIONS.filter((action) => !allowed.has(action)),
2801
- handoffTriggers: [...DEFAULT_HANDOFF_TRIGGERS],
2802
- governedLanes,
2803
- optionalComputerUseBridge: helperEligibility.allowed,
2804
- helperEligibility
3122
+ status: approved ? "executed" : "blocked",
3123
+ lane: "service_adapter",
3124
+ reason: approved ? "Governed service adapter approved by explicit adapter metadata." : "Service-adapter lane requires an explicit adapter identifier and entitlement.",
3125
+ auditMetadata: {
3126
+ approved,
3127
+ ...approved ? { adapterId: adapter } : {}
3128
+ }
2805
3129
  };
2806
3130
  };
2807
3131
 
2808
- // src/challenges/strategy-selector.ts
2809
- var buildDecision = (config, lane, rationale, allowedActionFamilies, verificationLevel, stopConditions, governedLane) => ({
2810
- lane,
2811
- ...governedLane ? { governedLane } : {},
2812
- rationale,
2813
- attemptBudget: config.attemptBudget,
2814
- noProgressLimit: config.noProgressLimit,
2815
- verificationLevel,
2816
- stopConditions,
2817
- allowedActionFamilies: [...allowedActionFamilies]
2818
- });
2819
- var selectChallengeStrategy = (args) => {
2820
- const { config, bundle, capabilityMatrix, gate, interpretation } = args;
2821
- const registryCooldownActive = (bundle.registryPressure?.cooldownUntilMs ?? 0) > Date.now();
2822
- const registryPressureElevated = (bundle.registryPressure?.activeChallenges ?? 0) > 0 || (bundle.registryPressure?.recentChallengeRatio ?? 0) >= 0.5 || (bundle.registryPressure?.recentRateLimitRatio ?? 0) >= 0.5;
2823
- const stopConditions = [
2824
- "manager_verification_clears_blocker",
2825
- "policy_gate_denies_next_action",
2826
- "human_boundary_detected",
2827
- "no_progress_budget_exhausted"
2828
- ];
2829
- const { mode, standDownReason } = gate.resolvedPolicy;
2830
- if (mode === "off") {
2831
- return buildDecision(
2832
- config,
2833
- "defer",
2834
- "Challenge automation mode is off; challenge handling is detect-and-report only.",
2835
- [],
2836
- "light",
2837
- [standDownReason ?? "challenge_automation_off"]
2838
- );
2839
- }
2840
- if (capabilityMatrix.mustDefer) {
2841
- return buildDecision(
2842
- config,
2843
- "defer",
2844
- "Current policy or blocker state requires deferral before further automation.",
2845
- gate.allowedActions,
2846
- interpretation.requiredVerification,
2847
- ["policy_blocked_or_clear_state"]
2848
- );
2849
- }
2850
- if (registryCooldownActive && !capabilityMatrix.canReuseExistingSession && !capabilityMatrix.canReuseCookies && !capabilityMatrix.canNavigateToAuth) {
2851
- return buildDecision(
2852
- config,
2853
- "defer",
2854
- "Registry cooldown is still active and no legitimate continuity or auth-navigation lane is currently available.",
2855
- gate.allowedActions,
2856
- interpretation.requiredVerification,
2857
- ["registry_cooldown_active"]
2858
- );
2859
- }
2860
- if (capabilityMatrix.canUseOwnedEnvironmentFixture) {
2861
- return buildDecision(
2862
- config,
2863
- "owned_environment_fixture",
2864
- "Owned-environment fixture detected and explicitly allowlisted.",
2865
- gate.allowedActions,
2866
- interpretation.requiredVerification,
2867
- stopConditions,
2868
- "owned_environment_fixture"
2869
- );
2870
- }
2871
- if (capabilityMatrix.mustYield) {
2872
- return buildDecision(
2873
- config,
2874
- "human_yield",
2875
- `Human authority boundary reached: ${interpretation.humanBoundary}.`,
2876
- gate.allowedActions,
2877
- interpretation.requiredVerification,
2878
- [...stopConditions, "human_authority_required"]
2879
- );
3132
+ // src/challenges/governed-adapter-gateway.ts
3133
+ var evaluateGovernedLane = (config, request) => {
3134
+ if (config.mode === "off") {
3135
+ return {
3136
+ status: "blocked",
3137
+ lane: request.lane,
3138
+ reason: "Challenge automation mode is off.",
3139
+ auditMetadata: {}
3140
+ };
2880
3141
  }
2881
- if (capabilityMatrix.canNavigateToAuth || capabilityMatrix.canReuseExistingSession || capabilityMatrix.canReuseCookies || capabilityMatrix.canFillNonSecretFields || capabilityMatrix.canExploreClicks) {
2882
- return buildDecision(
2883
- config,
2884
- "generic_browser_autonomy",
2885
- registryPressureElevated ? "Registry pressure is elevated, but legitimate browser continuity remains available for one bounded generic autonomy pass." : "Existing browser controls can attempt bounded auth navigation, session reuse, non-secret fill, or interaction exploration.",
2886
- gate.allowedActions,
2887
- interpretation.requiredVerification,
2888
- registryPressureElevated ? [...stopConditions, "registry_pressure_elevated"] : stopConditions
2889
- );
3142
+ switch (request.lane) {
3143
+ case "owned_environment_fixture":
3144
+ if (!config.governed.allowOwnedEnvironmentFixtures) {
3145
+ return {
3146
+ status: "blocked",
3147
+ lane: request.lane,
3148
+ reason: "Owned-environment fixtures are disabled by policy.",
3149
+ auditMetadata: {}
3150
+ };
3151
+ }
3152
+ return runOwnedEnvironmentLane(request);
3153
+ case "sanctioned_identity":
3154
+ if (!config.governed.allowSanctionedIdentity) {
3155
+ return {
3156
+ status: "blocked",
3157
+ lane: request.lane,
3158
+ reason: "Sanctioned identity is disabled by policy.",
3159
+ auditMetadata: {}
3160
+ };
3161
+ }
3162
+ return runSanctionedIdentityLane(request);
3163
+ case "service_adapter":
3164
+ if (!config.governed.allowServiceAdapters) {
3165
+ return {
3166
+ status: "blocked",
3167
+ lane: request.lane,
3168
+ reason: "Service adapters are disabled by policy.",
3169
+ auditMetadata: {}
3170
+ };
3171
+ }
3172
+ return runServiceAdapterLane(request);
2890
3173
  }
2891
- if (capabilityMatrix.canUseComputerUseBridge) {
2892
- return buildDecision(
2893
- config,
2894
- "optional_computer_use_bridge",
2895
- "DOM-native autonomy is exhausted, but the optional browser-scoped bridge is enabled.",
2896
- gate.allowedActions,
2897
- interpretation.requiredVerification,
2898
- stopConditions
2899
- );
3174
+ };
3175
+
3176
+ // src/challenges/outcome-recorder.ts
3177
+ var MAX_RECORDS_PER_CHALLENGE = 25;
3178
+ var OutcomeRecorder = class {
3179
+ records = /* @__PURE__ */ new Map();
3180
+ record(record) {
3181
+ const key = record.challengeId ?? "__untracked__";
3182
+ const existing = this.records.get(key) ?? [];
3183
+ const next = [...existing, record].slice(-MAX_RECORDS_PER_CHALLENGE);
3184
+ this.records.set(key, next);
2900
3185
  }
2901
- if (capabilityMatrix.canUseSanctionedIdentity) {
2902
- return buildDecision(
2903
- config,
2904
- "sanctioned_identity",
2905
- "Sanctioned identity lane is enabled and generic browser autonomy is insufficient.",
2906
- gate.allowedActions,
2907
- interpretation.requiredVerification,
2908
- stopConditions,
2909
- "sanctioned_identity"
2910
- );
3186
+ latest(challengeId) {
3187
+ if (!challengeId) return void 0;
3188
+ const entries = this.records.get(challengeId);
3189
+ return entries?.[entries.length - 1];
2911
3190
  }
2912
- if (capabilityMatrix.canUseServiceAdapter) {
2913
- return buildDecision(
2914
- config,
2915
- "service_adapter",
2916
- "Governed service-adapter lane is enabled as the last non-human option.",
2917
- gate.allowedActions,
2918
- interpretation.requiredVerification,
2919
- stopConditions,
2920
- "service_adapter"
2921
- );
3191
+ read(challengeId) {
3192
+ if (!challengeId) return [];
3193
+ return [...this.records.get(challengeId) ?? []];
2922
3194
  }
2923
- return buildDecision(
2924
- config,
2925
- "human_yield",
2926
- `No legitimate autonomous lane remains after applying policy and continuity checks. ${capabilityMatrix.helperEligibility.reason}`,
2927
- gate.allowedActions,
2928
- interpretation.requiredVerification,
2929
- [...stopConditions, "no_legitimate_lane_remaining"]
2930
- );
2931
3195
  };
2932
3196
 
2933
3197
  // src/challenges/orchestrator.ts
@@ -2999,25 +3263,7 @@ var ChallengeOrchestrator = class {
2999
3263
  return this.recorder;
3000
3264
  }
3001
3265
  async captureEvidence(args) {
3002
- const status = await args.handle.status(args.sessionId);
3003
- const effectiveTargetId = status.activeTargetId ?? args.targetId ?? null;
3004
- const snapshot = await args.handle.snapshot(args.sessionId, "actionables", 2400, void 0, effectiveTargetId);
3005
- const debugTrace = await args.handle.debugTraceSnapshot(args.sessionId, { max: 50 });
3006
- const cookies = status.url ? await args.handle.cookieList(args.sessionId, [status.url]) : { count: 0 };
3007
- return buildChallengeEvidenceBundle({
3008
- status,
3009
- snapshot: {
3010
- snapshotId: snapshot.snapshotId,
3011
- content: snapshot.content,
3012
- warnings: snapshot.warnings
3013
- },
3014
- debugTrace,
3015
- cookieCount: cookies.count,
3016
- canImportCookies: args.canImportCookies,
3017
- fallbackDisposition: args.fallbackDisposition,
3018
- registryPressure: args.registryPressure,
3019
- taskData: args.taskData
3020
- });
3266
+ return captureChallengeEvidence(args);
3021
3267
  }
3022
3268
  async orchestrate(args) {
3023
3269
  const bundle = await this.captureEvidence({
@@ -3204,6 +3450,7 @@ var ChallengeOrchestrator = class {
3204
3450
  targetId: args.targetId,
3205
3451
  initialBundle: bundle,
3206
3452
  decision,
3453
+ helperEligibility: capabilityMatrix.helperEligibility,
3207
3454
  config: this.config,
3208
3455
  suggestedSteps
3209
3456
  });
@@ -3366,6 +3613,20 @@ var fallbackDispositionMessage = (fallback, url) => {
3366
3613
  var toProviderFallbackError = (args) => {
3367
3614
  const { fallback } = args;
3368
3615
  const reasonCode = fallback.reasonCode;
3616
+ const details = {
3617
+ 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
3628
+ });
3629
+ const guidance = hint ? buildProviderIssueGuidance({ provider: args.provider, hint, details }) : void 0;
3369
3630
  return new ProviderRuntimeError(
3370
3631
  providerErrorCodeFromReasonCode(reasonCode),
3371
3632
  fallbackDispositionMessage(fallback, args.url),
@@ -3374,15 +3635,7 @@ var toProviderFallbackError = (args) => {
3374
3635
  source: args.source,
3375
3636
  retryable: reasonCode === "rate_limited",
3376
3637
  reasonCode,
3377
- details: {
3378
- url: args.url,
3379
- disposition: fallback.disposition,
3380
- ...fallback.mode ? { browserFallbackMode: fallback.mode } : {},
3381
- ...fallback.challenge ? { challenge: toJsonRecord(fallback.challenge) } : {},
3382
- ...fallback.preservedSessionId ? { preservedSessionId: fallback.preservedSessionId } : {},
3383
- ...fallback.preservedTargetId ? { preservedTargetId: fallback.preservedTargetId } : {},
3384
- ...toJsonRecord(fallback.details ?? {})
3385
- }
3638
+ details: guidance ? { ...details, guidance } : details
3386
3639
  }
3387
3640
  );
3388
3641
  };
@@ -9173,7 +9426,7 @@ var isJsonRecord = (value) => typeof value === "object" && value !== null && !Ar
9173
9426
  var isWorkflowStage = (value) => value === "compile" || value === "execute" || value === "postprocess" || value === "resume";
9174
9427
  var isWorkflowCheckpoint = (value) => isJsonRecord(value) && isWorkflowStage(value.stage) && (value.stepId === void 0 || typeof value.stepId === "string") && (value.stepIndex === void 0 || typeof value.stepIndex === "number") && (value.state === void 0 || isJsonRecord(value.state)) && (value.updatedAt === void 0 || typeof value.updatedAt === "string");
9175
9428
  var isWorkflowTraceEntry = (value) => isJsonRecord(value) && typeof value.at === "string" && isWorkflowStage(value.stage) && typeof value.event === "string" && (value.details === void 0 || isJsonRecord(value.details));
9176
- var isWorkflowKind = (value) => value === "research" || value === "shopping" || value === "product_video";
9429
+ var isWorkflowKind = (value) => value === "research" || value === "shopping" || value === "product_video" || value === "inspiredesign";
9177
9430
  var buildWorkflowResumeEnvelope = (kind, input, options = {}) => ({
9178
9431
  kind,
9179
9432
  input,
@@ -9184,7 +9437,7 @@ var isWorkflowResumeEnvelope = (value) => isJsonRecord(value) && isWorkflowKind(
9184
9437
  var isWorkflowResumePayload = (value) => isJsonRecord(value) && isWorkflowResumeEnvelope(value.workflow);
9185
9438
 
9186
9439
  // src/providers/workflows.ts
9187
- import { createHash as createHash6 } from "crypto";
9440
+ import { createHash as createHash7 } from "crypto";
9188
9441
 
9189
9442
  // src/providers/artifacts.ts
9190
9443
  import { mkdir, readdir as readdir2, readFile, rm as rm2, stat, writeFile } from "fs/promises";
@@ -9483,6 +9736,46 @@ var enrichResearchRecords = (records, timebox, now = /* @__PURE__ */ new Date())
9483
9736
  return records.map((record) => toResearchRecord(record, timebox, now));
9484
9737
  };
9485
9738
 
9739
+ // src/inspiredesign/handoff.ts
9740
+ var INSPIREDESIGN_HANDOFF_FILES = {
9741
+ designMarkdown: "design.md",
9742
+ designContract: "design-contract.json",
9743
+ canvasPlanRequest: "canvas-plan.request.json",
9744
+ designAgentHandoff: "design-agent-handoff.json",
9745
+ generationPlan: "generation-plan.json",
9746
+ implementationPlanMarkdown: "implementation-plan.md",
9747
+ implementationPlan: "implementation-plan.json",
9748
+ evidence: "evidence.json",
9749
+ prototypeGuidance: "prototype-guidance.md"
9750
+ };
9751
+ var INSPIREDESIGN_HANDOFF_SKILLS = {
9752
+ bestPractices: {
9753
+ name: "opendevbrowser-best-practices",
9754
+ topic: "quick start"
9755
+ },
9756
+ designAgent: {
9757
+ name: "opendevbrowser-design-agent",
9758
+ topic: "canvas-contract"
9759
+ }
9760
+ };
9761
+ var formatSkillReference = (skill) => `${skill.name} "${skill.topic}"`;
9762
+ var formatSkillLoadCommand = (skill) => `opendevbrowser_skill_load ${skill.name} "${skill.topic}"`;
9763
+ var INSPIREDESIGN_HANDOFF_COMMANDS = {
9764
+ loadBestPractices: formatSkillLoadCommand(INSPIREDESIGN_HANDOFF_SKILLS.bestPractices),
9765
+ loadDesignAgent: formatSkillLoadCommand(INSPIREDESIGN_HANDOFF_SKILLS.designAgent),
9766
+ continueInCanvas: `opendevbrowser canvas --command canvas.plan.set --params-file ./${INSPIREDESIGN_HANDOFF_FILES.canvasPlanRequest}`
9767
+ };
9768
+ var INSPIREDESIGN_HANDOFF_RECOMMENDED_SKILLS = [
9769
+ formatSkillReference(INSPIREDESIGN_HANDOFF_SKILLS.bestPractices),
9770
+ formatSkillReference(INSPIREDESIGN_HANDOFF_SKILLS.designAgent)
9771
+ ];
9772
+ var INSPIREDESIGN_HANDOFF_GUIDANCE = {
9773
+ prepareCanvasPlanRequest: `Fill canvasSessionId, leaseId, and documentId in ${INSPIREDESIGN_HANDOFF_FILES.canvasPlanRequest} before running ${INSPIREDESIGN_HANDOFF_COMMANDS.continueInCanvas}.`,
9774
+ deepCaptureRecommendation: "Rerun inspiredesign with captureMode=deep only when you need richer evidence for visual hierarchy, protected references, or capture-specific debugging."
9775
+ };
9776
+ var buildInspiredesignFollowthroughSummary = () => `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 rerun with captureMode=deep only when you need richer evidence.`;
9777
+ var buildInspiredesignNextStep = () => `${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}.`;
9778
+
9486
9779
  // src/providers/renderer.ts
9487
9780
  var toCurrency = (value) => `$${value.toFixed(2)}`;
9488
9781
  var primaryConstraintSummaryFromMeta = (meta) => {
@@ -9517,22 +9810,225 @@ var renderResearch = (args) => {
9517
9810
  "```"
9518
9811
  ].join("\n");
9519
9812
  const contextPayload = {
9520
- topic: args.topic,
9521
- highlights: lines,
9522
- records: args.records,
9813
+ topic: args.topic,
9814
+ highlights: lines,
9815
+ records: args.records,
9816
+ meta: args.meta
9817
+ };
9818
+ const files = [
9819
+ { path: "summary.md", content: markdown },
9820
+ { path: "records.json", content: { records: args.records } },
9821
+ { path: "context.json", content: contextPayload },
9822
+ { path: "meta.json", content: args.meta }
9823
+ ];
9824
+ if (args.mode === "compact") {
9825
+ return {
9826
+ response: {
9827
+ mode: args.mode,
9828
+ summary,
9829
+ meta: args.meta
9830
+ },
9831
+ files
9832
+ };
9833
+ }
9834
+ if (args.mode === "json") {
9835
+ return {
9836
+ response: {
9837
+ mode: args.mode,
9838
+ records: args.records,
9839
+ meta: args.meta
9840
+ },
9841
+ files
9842
+ };
9843
+ }
9844
+ if (args.mode === "md") {
9845
+ return {
9846
+ response: {
9847
+ mode: args.mode,
9848
+ markdown,
9849
+ meta: args.meta
9850
+ },
9851
+ files
9852
+ };
9853
+ }
9854
+ if (args.mode === "context") {
9855
+ return {
9856
+ response: {
9857
+ mode: args.mode,
9858
+ context: contextPayload,
9859
+ meta: args.meta
9860
+ },
9861
+ files
9862
+ };
9863
+ }
9864
+ return {
9865
+ response: {
9866
+ mode: "path",
9867
+ meta: args.meta
9868
+ },
9869
+ files
9870
+ };
9871
+ };
9872
+ var toComparisonCsv = (offers) => {
9873
+ const header = ["provider", "title", "price", "shipping", "deal_score", "availability", "url"].join(",");
9874
+ const rows = offers.map((offer) => {
9875
+ return [
9876
+ offer.provider,
9877
+ JSON.stringify(offer.title),
9878
+ offer.price.amount.toFixed(2),
9879
+ offer.shipping.amount.toFixed(2),
9880
+ offer.deal_score.toFixed(4),
9881
+ offer.availability,
9882
+ canonicalizeUrl(offer.url)
9883
+ ].join(",");
9884
+ });
9885
+ return [header, ...rows].join("\n");
9886
+ };
9887
+ var compactShoppingLines = (offers, meta) => {
9888
+ if (offers.length === 0) {
9889
+ const summary = primaryConstraintSummaryFromMeta(meta);
9890
+ return summary ? [
9891
+ "No offers available from the selected providers.",
9892
+ `Primary constraint: ${summary}`
9893
+ ] : ["No offers available from the selected providers."];
9894
+ }
9895
+ return offers.slice(0, 10).map((offer, index) => {
9896
+ const total = offer.price.amount + offer.shipping.amount;
9897
+ return `${index + 1}. ${offer.title} - ${toCurrency(total)} (${offer.provider}, deal=${offer.deal_score.toFixed(2)})`;
9898
+ });
9899
+ };
9900
+ var renderShopping = (args) => {
9901
+ const lines = compactShoppingLines(args.offers, args.meta);
9902
+ const markdown = [
9903
+ `# Shopping: ${args.query}`,
9904
+ "",
9905
+ ...lines,
9906
+ "",
9907
+ "## Metadata",
9908
+ "```json",
9909
+ JSON.stringify(args.meta, null, 2),
9910
+ "```"
9911
+ ].join("\n");
9912
+ const comparisonCsv = toComparisonCsv(args.offers);
9913
+ const contextPayload = {
9914
+ query: args.query,
9915
+ highlights: lines,
9916
+ offers: args.offers,
9917
+ meta: args.meta
9918
+ };
9919
+ const files = [
9920
+ { path: "deals.md", content: markdown },
9921
+ { path: "offers.json", content: { offers: args.offers } },
9922
+ { path: "comparison.csv", content: comparisonCsv },
9923
+ { path: "meta.json", content: args.meta },
9924
+ { path: "deals-context.json", content: contextPayload }
9925
+ ];
9926
+ if (args.mode === "compact") {
9927
+ return {
9928
+ response: {
9929
+ mode: args.mode,
9930
+ summary: lines.join("\n"),
9931
+ meta: args.meta
9932
+ },
9933
+ files
9934
+ };
9935
+ }
9936
+ if (args.mode === "json") {
9937
+ return {
9938
+ response: {
9939
+ mode: args.mode,
9940
+ offers: args.offers,
9941
+ meta: args.meta
9942
+ },
9943
+ files
9944
+ };
9945
+ }
9946
+ if (args.mode === "md") {
9947
+ return {
9948
+ response: {
9949
+ mode: args.mode,
9950
+ markdown,
9951
+ meta: args.meta
9952
+ },
9953
+ files
9954
+ };
9955
+ }
9956
+ if (args.mode === "context") {
9957
+ return {
9958
+ response: {
9959
+ mode: args.mode,
9960
+ context: contextPayload,
9961
+ meta: args.meta
9962
+ },
9963
+ files
9964
+ };
9965
+ }
9966
+ return {
9967
+ response: {
9968
+ mode: "path",
9969
+ meta: args.meta
9970
+ },
9971
+ files
9972
+ };
9973
+ };
9974
+ var renderInspiredesign = (args) => {
9975
+ const summary = [
9976
+ `Brief: ${args.brief}`,
9977
+ `References: ${args.urls.length}`,
9978
+ `Profile: ${args.generationPlan.visualDirection.profile}`
9979
+ ].join("\n");
9980
+ const contextPayload = {
9981
+ brief: args.brief,
9982
+ urls: args.urls,
9983
+ designContract: args.designContract,
9984
+ canvasPlanRequest: args.canvasPlanRequest,
9985
+ designAgentHandoff: args.designAgentHandoff,
9986
+ generationPlan: args.generationPlan,
9987
+ implementationPlan: args.implementationPlan,
9988
+ designMarkdown: args.designMarkdown,
9989
+ implementationPlanMarkdown: args.implementationPlanMarkdown,
9990
+ prototypeGuidanceMarkdown: args.prototypeGuidanceMarkdown,
9991
+ evidence: args.evidence,
9523
9992
  meta: args.meta
9524
9993
  };
9994
+ const suggestedSteps = [
9995
+ {
9996
+ reason: "Load the baseline workflow runbook before implementation.",
9997
+ command: args.designAgentHandoff.commandExamples.loadBestPractices
9998
+ },
9999
+ {
10000
+ reason: "Load the Canvas contract lane before patching.",
10001
+ command: args.designAgentHandoff.commandExamples.loadDesignAgent
10002
+ },
10003
+ {
10004
+ reason: INSPIREDESIGN_HANDOFF_GUIDANCE.prepareCanvasPlanRequest,
10005
+ command: args.designAgentHandoff.commandExamples.continueInCanvas
10006
+ },
10007
+ {
10008
+ reason: args.designAgentHandoff.deepCaptureRecommendation
10009
+ }
10010
+ ];
9525
10011
  const files = [
9526
- { path: "summary.md", content: markdown },
9527
- { path: "records.json", content: { records: args.records } },
9528
- { path: "context.json", content: contextPayload },
9529
- { path: "meta.json", content: args.meta }
10012
+ { path: INSPIREDESIGN_HANDOFF_FILES.designMarkdown, content: args.designMarkdown },
10013
+ { path: INSPIREDESIGN_HANDOFF_FILES.designContract, content: args.designContract },
10014
+ { path: INSPIREDESIGN_HANDOFF_FILES.canvasPlanRequest, content: args.canvasPlanRequest },
10015
+ { path: INSPIREDESIGN_HANDOFF_FILES.designAgentHandoff, content: args.designAgentHandoff },
10016
+ { path: INSPIREDESIGN_HANDOFF_FILES.generationPlan, content: args.generationPlan },
10017
+ { path: INSPIREDESIGN_HANDOFF_FILES.implementationPlanMarkdown, content: args.implementationPlanMarkdown },
10018
+ { path: INSPIREDESIGN_HANDOFF_FILES.implementationPlan, content: args.implementationPlan },
10019
+ { path: INSPIREDESIGN_HANDOFF_FILES.evidence, content: args.evidence }
9530
10020
  ];
10021
+ if (args.prototypeGuidanceMarkdown) {
10022
+ files.push({ path: INSPIREDESIGN_HANDOFF_FILES.prototypeGuidance, content: args.prototypeGuidanceMarkdown });
10023
+ }
9531
10024
  if (args.mode === "compact") {
9532
10025
  return {
9533
10026
  response: {
9534
10027
  mode: args.mode,
9535
10028
  summary,
10029
+ followthroughSummary: args.designAgentHandoff.summary,
10030
+ suggestedNextAction: args.designAgentHandoff.nextStep,
10031
+ suggestedSteps,
9536
10032
  meta: args.meta
9537
10033
  },
9538
10034
  files
@@ -9542,7 +10038,18 @@ var renderResearch = (args) => {
9542
10038
  return {
9543
10039
  response: {
9544
10040
  mode: args.mode,
9545
- records: args.records,
10041
+ brief: args.brief,
10042
+ urls: args.urls,
10043
+ canvasPlanRequest: args.canvasPlanRequest,
10044
+ designAgentHandoff: args.designAgentHandoff,
10045
+ designContract: args.designContract,
10046
+ generationPlan: args.generationPlan,
10047
+ implementationPlan: args.implementationPlan,
10048
+ prototypeGuidanceMarkdown: args.prototypeGuidanceMarkdown,
10049
+ evidence: args.evidence,
10050
+ followthroughSummary: args.designAgentHandoff.summary,
10051
+ suggestedNextAction: args.designAgentHandoff.nextStep,
10052
+ suggestedSteps,
9546
10053
  meta: args.meta
9547
10054
  },
9548
10055
  files
@@ -9552,7 +10059,12 @@ var renderResearch = (args) => {
9552
10059
  return {
9553
10060
  response: {
9554
10061
  mode: args.mode,
9555
- markdown,
10062
+ markdown: args.designMarkdown,
10063
+ implementationPlanMarkdown: args.implementationPlanMarkdown,
10064
+ prototypeGuidanceMarkdown: args.prototypeGuidanceMarkdown,
10065
+ followthroughSummary: args.designAgentHandoff.summary,
10066
+ suggestedNextAction: args.designAgentHandoff.nextStep,
10067
+ suggestedSteps,
9556
10068
  meta: args.meta
9557
10069
  },
9558
10070
  files
@@ -9563,6 +10075,9 @@ var renderResearch = (args) => {
9563
10075
  response: {
9564
10076
  mode: args.mode,
9565
10077
  context: contextPayload,
10078
+ followthroughSummary: args.designAgentHandoff.summary,
10079
+ suggestedNextAction: args.designAgentHandoff.nextStep,
10080
+ suggestedSteps,
9566
10081
  meta: args.meta
9567
10082
  },
9568
10083
  files
@@ -9571,116 +10086,1209 @@ var renderResearch = (args) => {
9571
10086
  return {
9572
10087
  response: {
9573
10088
  mode: "path",
10089
+ followthroughSummary: args.designAgentHandoff.summary,
10090
+ suggestedNextAction: args.designAgentHandoff.nextStep,
10091
+ suggestedSteps,
9574
10092
  meta: args.meta
9575
10093
  },
9576
10094
  files
9577
10095
  };
9578
10096
  };
9579
- var toComparisonCsv = (offers) => {
9580
- const header = ["provider", "title", "price", "shipping", "deal_score", "availability", "url"].join(",");
9581
- const rows = offers.map((offer) => {
9582
- return [
9583
- offer.provider,
9584
- JSON.stringify(offer.title),
9585
- offer.price.amount.toFixed(2),
9586
- offer.shipping.amount.toFixed(2),
9587
- offer.deal_score.toFixed(4),
9588
- offer.availability,
9589
- canonicalizeUrl(offer.url)
9590
- ].join(",");
9591
- });
9592
- return [header, ...rows].join("\n");
10097
+
10098
+ // src/providers/inspiredesign-contract.ts
10099
+ import { createHash as createHash3 } from "crypto";
10100
+
10101
+ // skills/opendevbrowser-design-agent/assets/templates/canvas-generation-plan.design.v1.json
10102
+ var canvas_generation_plan_design_v1_default = {
10103
+ requestId: "req_plan_design_01",
10104
+ canvasSessionId: "<canvas-session-id>",
10105
+ leaseId: "<lease-id>",
10106
+ documentId: "<document-id>",
10107
+ generationPlan: {
10108
+ targetOutcome: {
10109
+ mode: "high-fi-live-edit",
10110
+ summary: "Clarify hierarchy, trust, and action flow"
10111
+ },
10112
+ visualDirection: {
10113
+ profile: "product-story",
10114
+ themeStrategy: "single-theme"
10115
+ },
10116
+ layoutStrategy: {
10117
+ approach: "hero-led-grid",
10118
+ navigationModel: "global-header"
10119
+ },
10120
+ contentStrategy: {
10121
+ source: "real-content-first"
10122
+ },
10123
+ componentStrategy: {
10124
+ mode: "reuse-first",
10125
+ interactionStates: [
10126
+ "default",
10127
+ "hover",
10128
+ "focus",
10129
+ "disabled"
10130
+ ]
10131
+ },
10132
+ motionPosture: {
10133
+ level: "subtle",
10134
+ reducedMotion: "respect-user-preference"
10135
+ },
10136
+ responsivePosture: {
10137
+ primaryViewport: "desktop",
10138
+ requiredViewports: [
10139
+ "desktop",
10140
+ "tablet",
10141
+ "mobile"
10142
+ ]
10143
+ },
10144
+ accessibilityPosture: {
10145
+ target: "WCAG_2_2_AA",
10146
+ keyboardNavigation: "full"
10147
+ },
10148
+ validationTargets: {
10149
+ blockOn: [
10150
+ "contrast-failure",
10151
+ "responsive-mismatch"
10152
+ ],
10153
+ requiredThemes: [
10154
+ "light"
10155
+ ],
10156
+ browserValidation: "required",
10157
+ maxInteractionLatencyMs: 160
10158
+ }
10159
+ }
9593
10160
  };
9594
- var compactShoppingLines = (offers, meta) => {
9595
- if (offers.length === 0) {
9596
- const summary = primaryConstraintSummaryFromMeta(meta);
9597
- return summary ? [
9598
- "No offers available from the selected providers.",
9599
- `Primary constraint: ${summary}`
9600
- ] : ["No offers available from the selected providers."];
10161
+
10162
+ // skills/opendevbrowser-design-agent/assets/templates/design-contract.v1.json
10163
+ var design_contract_v1_default = {
10164
+ intent: {
10165
+ audience: "Operators evaluating a new OpenDevBrowser design flow",
10166
+ task: "Understand the primary page value and complete the intended action quickly",
10167
+ successCriteria: [
10168
+ "Primary action is obvious within 5 seconds",
10169
+ "Layout remains coherent on desktop, tablet, and mobile"
10170
+ ],
10171
+ trustPosture: "clear and confident"
10172
+ },
10173
+ designLanguage: {
10174
+ direction: "product-led clarity",
10175
+ styleAxes: {
10176
+ contrast: "high",
10177
+ density: "airy",
10178
+ shapeLanguage: "rounded-structured",
10179
+ depth: "subtle",
10180
+ texture: "minimal",
10181
+ motion: "purposeful"
10182
+ },
10183
+ approvedLibraries: {
10184
+ components: [
10185
+ "shadcn"
10186
+ ],
10187
+ icons: [
10188
+ "tabler"
10189
+ ],
10190
+ styling: [
10191
+ "tailwindcss"
10192
+ ]
10193
+ },
10194
+ semanticTokenSource: {
10195
+ owner: "app-shell theme provider",
10196
+ tokens: [
10197
+ "color",
10198
+ "typography",
10199
+ "spacing",
10200
+ "radius",
10201
+ "shadow",
10202
+ "motion"
10203
+ ],
10204
+ rule: "Use semantic tokens in repeated UI; reserve raw values for rare one-off illustration"
10205
+ }
10206
+ },
10207
+ contentModel: {
10208
+ primaryMessage: "Show the core value immediately with real copy",
10209
+ supportingMessages: [
10210
+ "Reinforce trust",
10211
+ "Clarify the primary action",
10212
+ "Reduce ambiguity"
10213
+ ],
10214
+ realContentRequired: true,
10215
+ states: [
10216
+ "default",
10217
+ "hover",
10218
+ "focus",
10219
+ "empty",
10220
+ "loading",
10221
+ "success",
10222
+ "error"
10223
+ ],
10224
+ loadingStrategy: "Preserve the final layout with 3-6 placeholders or one section-level loader when structure cannot be previewed",
10225
+ emptyStateStrategy: "Explain why nothing is shown and provide the next useful action",
10226
+ errorStateStrategy: "Keep retry or recovery actions close to the failed region",
10227
+ transientFeedbackStrategy: "Use short, non-blocking overlays for confirmations instead of reflowing the main layout",
10228
+ avoidPlaceholderCopy: true
10229
+ },
10230
+ navigationModel: {
10231
+ owner: "feature shell route controller",
10232
+ primaryRouteModel: "One canonical route map translates tabs, selected records, and editor modes into URL-backed state",
10233
+ deepLinkPolicy: "Shareable tabs, filters, sort, and selected entities stay in the URL with a safe fallback when params are stale or incomplete",
10234
+ invalidRouteFallback: "Recover to the closest safe baseline view instead of leaving the screen in a partially initialized state",
10235
+ overlayEntryPoints: [
10236
+ "route-owned detail drawer",
10237
+ "feature-owned confirmation modal"
10238
+ ]
10239
+ },
10240
+ asyncModel: {
10241
+ owner: "feature-local controller with URL-owned query state when sharing or refresh matters",
10242
+ loadTrigger: "load-on-enter for the first view; debounced restart on query, scope, or sort change",
10243
+ restartTriggers: [
10244
+ "query",
10245
+ "scope",
10246
+ "sort"
10247
+ ],
10248
+ debounceMs: 250,
10249
+ emptyQueryBehavior: "Reset to the baseline or empty state without a remote fetch unless the product explicitly needs default results",
10250
+ cancellationPolicy: "Treat stale requests as normal and do not surface cancellation as a user-facing error",
10251
+ moveToServiceWhen: [
10252
+ "work must survive dismissal",
10253
+ "caching or retry policy becomes shared",
10254
+ "multiple screens depend on the same in-flight state"
10255
+ ],
10256
+ urlOwnership: "Put query, filter, and sort in the URL when the state should survive refresh, sharing, or handoff"
10257
+ },
10258
+ layoutSystem: {
10259
+ grid: "12-column responsive grid",
10260
+ containers: "max-width sections with asymmetric emphasis",
10261
+ spacingRhythm: "8px base with larger section jumps",
10262
+ alignmentRules: [
10263
+ "Primary content aligns to a consistent reading column",
10264
+ "Actions stay near the message they complete"
10265
+ ]
10266
+ },
10267
+ typographySystem: {
10268
+ families: [
10269
+ "Space Grotesk",
10270
+ "Source Sans 3"
10271
+ ],
10272
+ scale: [
10273
+ "12",
10274
+ "14",
10275
+ "16",
10276
+ "20",
10277
+ "28",
10278
+ "40",
10279
+ "56"
10280
+ ],
10281
+ measure: "60-72 characters for dense reading blocks",
10282
+ fallbackPolicy: "Use repo-native or licensed alternatives when these families are unavailable",
10283
+ loadingStrategy: "Avoid layout shift and keep fallback metrics close"
10284
+ },
10285
+ motionSystem: {
10286
+ timing: "short and decisive",
10287
+ reducedMotionPolicy: "Preserve meaning with reduced movement",
10288
+ interactionMoments: [
10289
+ "page entry",
10290
+ "section reveal",
10291
+ "primary CTA feedback"
10292
+ ],
10293
+ parallaxPolicy: "off unless explicitly justified"
10294
+ },
10295
+ performanceModel: {
10296
+ renderHotspots: [
10297
+ "scan-heavy collection region",
10298
+ "secondary inspector or preview pane"
10299
+ ],
10300
+ stableIdentityPolicy: "Rows, cards, tabs, and overlays use stable domain ids instead of array indexes",
10301
+ listStrategy: "Use the smallest structure that stays fast with realistic data; choose progressive reveal, lazy containers, or virtualization deliberately",
10302
+ secondaryPanelPolicy: "Heavy inspectors, previews, and editors load on demand when they are not required for first paint",
10303
+ measurementPlan: "Validate realistic data volume with browser traces and the framework profiler before claiming the surface is performant"
10304
+ },
10305
+ responsiveSystem: {
10306
+ breakpoints: [
10307
+ "mobile",
10308
+ "tablet",
10309
+ "desktop"
10310
+ ],
10311
+ adaptationRules: [
10312
+ "Collapse multicolumn sections before copy becomes cramped",
10313
+ "Preserve primary CTA prominence on the smallest viewport"
10314
+ ],
10315
+ touchPolicy: "Comfortable targets and low-friction scroll paths",
10316
+ overflowPolicy: "No horizontal scroll for primary content"
10317
+ },
10318
+ accessibilityPolicy: {
10319
+ target: "WCAG 2.2 AA",
10320
+ keyboardRequirements: [
10321
+ "Visible focus",
10322
+ "Logical tab order"
10323
+ ],
10324
+ focusPolicy: "Focus rings must remain visible against every background",
10325
+ semanticRequirements: [
10326
+ "Landmarks are explicit",
10327
+ "Headings remain hierarchical"
10328
+ ]
10329
+ },
10330
+ generationPlan: {
10331
+ targetOutcome: {
10332
+ mode: "high-fi-live-edit",
10333
+ summary: "Improve hierarchy, clarity, and conversion confidence"
10334
+ },
10335
+ visualDirection: {
10336
+ profile: "product-led-clarity",
10337
+ styleAxes: {
10338
+ contrast: "high",
10339
+ density: "airy"
10340
+ }
10341
+ },
10342
+ layoutStrategy: {
10343
+ approach: "hero-led-grid"
10344
+ },
10345
+ contentStrategy: {
10346
+ source: "real-content-first"
10347
+ },
10348
+ componentStrategy: {
10349
+ mode: "reuse-first",
10350
+ approvedLibraries: [
10351
+ "shadcn"
10352
+ ]
10353
+ },
10354
+ motionPosture: {
10355
+ level: "subtle",
10356
+ allow3D: false,
10357
+ allowParallax: false,
10358
+ allowCustomSmoothScroll: false
10359
+ },
10360
+ responsivePosture: {
10361
+ primaryViewport: "desktop",
10362
+ requiredViewports: [
10363
+ "desktop",
10364
+ "tablet",
10365
+ "mobile"
10366
+ ]
10367
+ },
10368
+ accessibilityPosture: {
10369
+ target: "WCAG_2_2_AA",
10370
+ reducedMotion: "required",
10371
+ keyboardParity: true
10372
+ },
10373
+ validationTargets: {
10374
+ blockOn: [
10375
+ "contrast-failure",
10376
+ "keyboard-regression",
10377
+ "stale-search-results",
10378
+ "invalid-route-recovery-failure"
10379
+ ],
10380
+ warnOn: [
10381
+ "responsive-mismatch",
10382
+ "layout-shift",
10383
+ "spinner-stacking",
10384
+ "scan-surface-jank"
10385
+ ]
10386
+ }
9601
10387
  }
9602
- return offers.slice(0, 10).map((offer, index) => {
9603
- const total = offer.price.amount + offer.shipping.amount;
9604
- return `${index + 1}. ${offer.title} - ${toCurrency(total)} (${offer.provider}, deal=${offer.deal_score.toFixed(2)})`;
9605
- });
9606
10388
  };
9607
- var renderShopping = (args) => {
9608
- const lines = compactShoppingLines(args.offers, args.meta);
9609
- const markdown = [
9610
- `# Shopping: ${args.query}`,
10389
+
10390
+ // src/providers/inspiredesign-contract.ts
10391
+ var BASE_CONTRACT_TEMPLATE = design_contract_v1_default;
10392
+ var BASE_PLAN_REQUEST_TEMPLATE = canvas_generation_plan_design_v1_default;
10393
+ var BASE_GENERATION_PLAN = BASE_PLAN_REQUEST_TEMPLATE.generationPlan;
10394
+ var PROFILE_MATCHERS = [
10395
+ { profile: "auth-focused", keywords: ["auth", "login", "signin", "sign-in", "signup", "sign-up", "onboarding"] },
10396
+ { profile: "settings-system", keywords: ["settings", "preferences", "account", "profile", "billing"] },
10397
+ { profile: "ops-control", keywords: ["dashboard", "admin", "control", "analytics", "monitor", "reporting"] },
10398
+ { profile: "documentation", keywords: ["docs", "documentation", "knowledge base", "reference", "guide"] },
10399
+ { profile: "commerce-system", keywords: ["shop", "commerce", "pricing", "checkout", "product page", "catalog"] }
10400
+ ];
10401
+ var PROFILE_CONFIG = {
10402
+ "clean-room": {
10403
+ direction: "clean-room execution",
10404
+ visualPersonality: "disciplined, quiet, system-first",
10405
+ brandTone: "neutral and exacting",
10406
+ hierarchyPrinciples: ["Keep one message per section.", "Use spacing, not ornament, to separate priority."],
10407
+ interactionPhilosophy: "Minimal motion, zero ambiguity, strong focus affordances.",
10408
+ navigationModel: "contextual",
10409
+ layoutApproach: "modular-grid",
10410
+ pagePatterns: ["Single-message hero", "Dense decision panel", "Clean spec sheet"],
10411
+ componentSequence: ["Buttons", "Inputs", "Decision cards", "Structured tables"],
10412
+ colors: {
10413
+ primary: "#0F172A",
10414
+ accent: "#0F766E",
10415
+ accentSurface: "#E6FFFB",
10416
+ background: "#F8FAFC",
10417
+ surface: "#FFFFFF",
10418
+ border: "#CBD5E1",
10419
+ text: "#0F172A",
10420
+ mutedText: "#475569",
10421
+ success: "#15803D",
10422
+ warning: "#B45309",
10423
+ danger: "#B91C1C"
10424
+ }
10425
+ },
10426
+ "cinematic-minimal": {
10427
+ direction: "cinematic restraint",
10428
+ visualPersonality: "dramatic, sparse, image-led",
10429
+ brandTone: "premium and deliberate",
10430
+ hierarchyPrinciples: ["Let the visual plane carry atmosphere.", "Keep copy short and decisive."],
10431
+ interactionPhilosophy: "Use motion for presence, not explanation.",
10432
+ navigationModel: "immersive",
10433
+ layoutApproach: "full-bleed-hero",
10434
+ pagePatterns: ["Full-bleed hero", "Editorial feature strip", "High-contrast CTA block"],
10435
+ componentSequence: ["Hero shell", "CTA buttons", "Image-led sections", "Minimal footer"],
10436
+ colors: {
10437
+ primary: "#111827",
10438
+ accent: "#C2410C",
10439
+ accentSurface: "#FFF7ED",
10440
+ background: "#F8F5F0",
10441
+ surface: "#FFFFFF",
10442
+ border: "#D6D3D1",
10443
+ text: "#111827",
10444
+ mutedText: "#57534E",
10445
+ success: "#15803D",
10446
+ warning: "#B45309",
10447
+ danger: "#B91C1C"
10448
+ }
10449
+ },
10450
+ "product-story": {
10451
+ direction: "product-led clarity",
10452
+ visualPersonality: "confident, editorial, product-first",
10453
+ brandTone: "clear and ambitious",
10454
+ hierarchyPrinciples: ["Lead with value before proof.", "Keep action close to the message it completes."],
10455
+ interactionPhilosophy: "Short, decisive motion with obvious focus states.",
10456
+ navigationModel: "global-header",
10457
+ layoutApproach: "hero-led-grid",
10458
+ pagePatterns: ["Hero with anchored CTA", "Feature narrative strip", "Proof and conversion band"],
10459
+ componentSequence: ["Hero", "Buttons", "Cards", "Feature sections", "Footer"],
10460
+ colors: {
10461
+ primary: "#0B6BFF",
10462
+ accent: "#F97316",
10463
+ accentSurface: "#FFF7ED",
10464
+ background: "#F5F7FB",
10465
+ surface: "#FFFFFF",
10466
+ border: "#D7E3F4",
10467
+ text: "#111827",
10468
+ mutedText: "#475569",
10469
+ success: "#15803D",
10470
+ warning: "#B45309",
10471
+ danger: "#B91C1C"
10472
+ }
10473
+ },
10474
+ "commerce-system": {
10475
+ direction: "trust-driven commerce",
10476
+ visualPersonality: "decisive, reassuring, conversion-aware",
10477
+ brandTone: "credible and practical",
10478
+ hierarchyPrinciples: ["Keep purchasing signals scannable.", "Surface trust proof before commitment moments."],
10479
+ interactionPhilosophy: "Fast feedback, strong affordances, no ambiguous states.",
10480
+ navigationModel: "global-header",
10481
+ layoutApproach: "commerce-grid",
10482
+ pagePatterns: ["Merchandising hero", "Offer comparison band", "Decision-support detail section"],
10483
+ componentSequence: ["Hero", "Offer cards", "Buttons", "Trust badges", "Comparison table"],
10484
+ colors: {
10485
+ primary: "#0F766E",
10486
+ accent: "#D97706",
10487
+ accentSurface: "#FFFBEB",
10488
+ background: "#F8FAFC",
10489
+ surface: "#FFFFFF",
10490
+ border: "#D1D5DB",
10491
+ text: "#111827",
10492
+ mutedText: "#4B5563",
10493
+ success: "#15803D",
10494
+ warning: "#B45309",
10495
+ danger: "#B91C1C"
10496
+ }
10497
+ },
10498
+ "control-room": {
10499
+ direction: "high-signal control room",
10500
+ visualPersonality: "dense, sharp, operational",
10501
+ brandTone: "authoritative and fast",
10502
+ hierarchyPrinciples: ["Use structure to reduce scanning cost.", "Separate primary metrics from diagnostics."],
10503
+ interactionPhilosophy: "Fast updates, explicit states, motion only when it clarifies change.",
10504
+ navigationModel: "sidebar",
10505
+ layoutApproach: "panel-grid",
10506
+ pagePatterns: ["Metric summary rail", "Split-pane workspace", "Diagnostic detail panel"],
10507
+ componentSequence: ["Sidebar", "Stat blocks", "Tables", "Filters", "Panels"],
10508
+ colors: {
10509
+ primary: "#155EEF",
10510
+ accent: "#0F766E",
10511
+ accentSurface: "#ECFDF3",
10512
+ background: "#F8FAFC",
10513
+ surface: "#FFFFFF",
10514
+ border: "#CBD5E1",
10515
+ text: "#0F172A",
10516
+ mutedText: "#475569",
10517
+ success: "#15803D",
10518
+ warning: "#B45309",
10519
+ danger: "#B91C1C"
10520
+ }
10521
+ },
10522
+ "ops-control": {
10523
+ direction: "operational precision",
10524
+ visualPersonality: "structured, dense, high-confidence",
10525
+ brandTone: "decisive and exact",
10526
+ hierarchyPrinciples: ["Pin the primary metric and action path.", "Separate overview from detail panes."],
10527
+ interactionPhilosophy: "Low-latency controls, strong state contrast, keyboard-friendly flows.",
10528
+ navigationModel: "sidebar",
10529
+ layoutApproach: "workspace-shell",
10530
+ pagePatterns: ["KPI overview bar", "Filterable data shell", "Detail drawer or inspector"],
10531
+ componentSequence: ["Sidebar", "Toolbar", "Data table", "Filters", "Detail panel"],
10532
+ colors: {
10533
+ primary: "#155EEF",
10534
+ accent: "#0891B2",
10535
+ accentSurface: "#ECFEFF",
10536
+ background: "#F8FAFC",
10537
+ surface: "#FFFFFF",
10538
+ border: "#CBD5E1",
10539
+ text: "#0F172A",
10540
+ mutedText: "#475569",
10541
+ success: "#15803D",
10542
+ warning: "#B45309",
10543
+ danger: "#B91C1C"
10544
+ }
10545
+ },
10546
+ "auth-focused": {
10547
+ direction: "trust-forward entry flow",
10548
+ visualPersonality: "calm, direct, reassuring",
10549
+ brandTone: "safe and premium",
10550
+ hierarchyPrinciples: ["One job per screen.", "Trust and recovery paths must be visible without clutter."],
10551
+ interactionPhilosophy: "Low-friction focus flow with immediate validation feedback.",
10552
+ navigationModel: "contextual",
10553
+ layoutApproach: "two-panel-auth",
10554
+ pagePatterns: ["Auth split layout", "Trust panel", "Recovery and help strip"],
10555
+ componentSequence: ["Auth form", "Inputs", "Buttons", "Alerts", "Trust panel"],
10556
+ colors: {
10557
+ primary: "#1D4ED8",
10558
+ accent: "#0F766E",
10559
+ accentSurface: "#ECFDF5",
10560
+ background: "#F8FAFC",
10561
+ surface: "#FFFFFF",
10562
+ border: "#D1D5DB",
10563
+ text: "#111827",
10564
+ mutedText: "#4B5563",
10565
+ success: "#15803D",
10566
+ warning: "#B45309",
10567
+ danger: "#B91C1C"
10568
+ }
10569
+ },
10570
+ "settings-system": {
10571
+ direction: "calm settings system",
10572
+ visualPersonality: "ordered, practical, quietly premium",
10573
+ brandTone: "confident and low-friction",
10574
+ hierarchyPrinciples: ["Group related decisions tightly.", "Keep destructive actions isolated and explicit."],
10575
+ interactionPhilosophy: "Prefer clear toggles and inline explanations over hidden complexity.",
10576
+ navigationModel: "tabbed",
10577
+ layoutApproach: "settings-grid",
10578
+ pagePatterns: ["Sectioned settings page", "Inline form groups", "Preference summary rail"],
10579
+ componentSequence: ["Tabs", "Forms", "Inputs", "Alerts", "Confirmation modal"],
10580
+ colors: {
10581
+ primary: "#1D4ED8",
10582
+ accent: "#0F766E",
10583
+ accentSurface: "#ECFDF5",
10584
+ background: "#F8FAFC",
10585
+ surface: "#FFFFFF",
10586
+ border: "#D1D5DB",
10587
+ text: "#111827",
10588
+ mutedText: "#475569",
10589
+ success: "#15803D",
10590
+ warning: "#B45309",
10591
+ danger: "#B91C1C"
10592
+ }
10593
+ },
10594
+ "documentation": {
10595
+ direction: "reference-first documentation",
10596
+ visualPersonality: "legible, calm, highly structured",
10597
+ brandTone: "expert and accessible",
10598
+ hierarchyPrinciples: ["Make scanning effortless.", "Keep code, steps, and warnings visually distinct."],
10599
+ interactionPhilosophy: "Light motion, sticky wayfinding, strong anchor visibility.",
10600
+ navigationModel: "sidebar",
10601
+ layoutApproach: "docs-shell",
10602
+ pagePatterns: ["Docs shell", "Procedure section", "Reference table block"],
10603
+ componentSequence: ["Sidebar", "Search", "Anchored headings", "Code blocks", "Callouts"],
10604
+ colors: {
10605
+ primary: "#1D4ED8",
10606
+ accent: "#0F766E",
10607
+ accentSurface: "#ECFDF5",
10608
+ background: "#F8FAFC",
10609
+ surface: "#FFFFFF",
10610
+ border: "#CBD5E1",
10611
+ text: "#0F172A",
10612
+ mutedText: "#475569",
10613
+ success: "#15803D",
10614
+ warning: "#B45309",
10615
+ danger: "#B91C1C"
10616
+ }
10617
+ }
10618
+ };
10619
+ var trimText = (value) => {
10620
+ return value.trim().replace(/\s+/g, " ");
10621
+ };
10622
+ var clipText = (value, maxLength) => {
10623
+ if (value.length <= maxLength) return value;
10624
+ return `${value.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
10625
+ };
10626
+ var cloneTemplate = (value) => structuredClone(value);
10627
+ var referenceFingerprint = (value) => {
10628
+ return createHash3("sha256").update(value).digest("hex").slice(0, 12);
10629
+ };
10630
+ var hasKeyword = (value, keywords) => {
10631
+ const haystack = value.toLowerCase();
10632
+ return keywords.some((keyword) => haystack.includes(keyword));
10633
+ };
10634
+ var classifyProfile = (brief, references) => {
10635
+ const combined = [brief, ...references.map((reference) => `${reference.title ?? ""} ${reference.excerpt ?? ""}`)].join(" ").toLowerCase();
10636
+ return PROFILE_MATCHERS.find((matcher) => hasKeyword(combined, matcher.keywords))?.profile ?? "product-story";
10637
+ };
10638
+ var resolveThemeStrategy = (brief, references) => {
10639
+ const combined = `${brief} ${references.map((reference) => reference.excerpt ?? "").join(" ")}`.toLowerCase();
10640
+ return combined.includes("dark") ? "light-dark-parity" : "single-theme";
10641
+ };
10642
+ var summarizeBrief = (brief) => {
10643
+ const normalized = trimText(brief);
10644
+ const sentence = normalized.split(/[.!?]/).map((part) => part.trim()).find(Boolean);
10645
+ return clipText(sentence ?? normalized, 140);
10646
+ };
10647
+ var buildSupportingMessages = (references) => {
10648
+ const messages = references.map((reference) => reference.title ?? reference.excerpt ?? "").map((value) => clipText(trimText(value), 72)).filter((value) => value.length > 0);
10649
+ return messages.slice(0, 3);
10650
+ };
10651
+ var buildGenerationPlan = (brief, profile, references) => {
10652
+ const plan = cloneTemplate(BASE_GENERATION_PLAN);
10653
+ plan.targetOutcome.summary = summarizeBrief(brief);
10654
+ plan.visualDirection.profile = profile;
10655
+ plan.visualDirection.themeStrategy = resolveThemeStrategy(brief, references);
10656
+ plan.layoutStrategy.approach = PROFILE_CONFIG[profile].layoutApproach;
10657
+ plan.layoutStrategy.navigationModel = PROFILE_CONFIG[profile].navigationModel;
10658
+ plan.componentStrategy.interactionStates = ["default", "hover", "focus", "disabled", "loading"];
10659
+ plan.validationTargets.requiredThemes = plan.visualDirection.themeStrategy === "light-dark-parity" ? ["light", "dark"] : ["light"];
10660
+ return plan;
10661
+ };
10662
+ var buildIntentBlock = (brief, urls, references) => {
10663
+ const intent = cloneTemplate(BASE_CONTRACT_TEMPLATE.intent);
10664
+ return {
10665
+ ...intent,
10666
+ task: summarizeBrief(brief),
10667
+ brief,
10668
+ briefHash: referenceFingerprint(brief),
10669
+ referenceCount: references.length,
10670
+ referenceUrls: urls,
10671
+ evidenceStatus: {
10672
+ fetched: references.filter((reference) => reference.fetchStatus === "captured").length,
10673
+ captured: references.filter((reference) => reference.captureStatus === "captured").length
10674
+ }
10675
+ };
10676
+ };
10677
+ var buildDesignLanguageBlock = (profile) => {
10678
+ const block = cloneTemplate(BASE_CONTRACT_TEMPLATE.designLanguage);
10679
+ const config = PROFILE_CONFIG[profile];
10680
+ return {
10681
+ ...block,
10682
+ direction: config.direction,
10683
+ visualPersonality: config.visualPersonality,
10684
+ brandTone: config.brandTone
10685
+ };
10686
+ };
10687
+ var buildContentModelBlock = (brief, references) => {
10688
+ const block = cloneTemplate(BASE_CONTRACT_TEMPLATE.contentModel);
10689
+ return {
10690
+ ...block,
10691
+ primaryMessage: summarizeBrief(brief),
10692
+ supportingMessages: buildSupportingMessages(references)
10693
+ };
10694
+ };
10695
+ var buildLayoutSystemBlock = (profile) => {
10696
+ const block = cloneTemplate(BASE_CONTRACT_TEMPLATE.layoutSystem);
10697
+ return {
10698
+ ...block,
10699
+ pagePatterns: PROFILE_CONFIG[profile].pagePatterns
10700
+ };
10701
+ };
10702
+ var buildTypographySystemBlock = () => {
10703
+ const block = cloneTemplate(BASE_CONTRACT_TEMPLATE.typographySystem);
10704
+ return {
10705
+ ...block,
10706
+ tokens: {
10707
+ display: "56/1.0",
10708
+ h1: "40/1.05",
10709
+ h2: "28/1.1",
10710
+ h3: "20/1.2",
10711
+ body: "16/1.6",
10712
+ label: "14/1.4",
10713
+ caption: "12/1.4"
10714
+ }
10715
+ };
10716
+ };
10717
+ var buildColorSystemBlock = (profile) => {
10718
+ const colors = PROFILE_CONFIG[profile].colors;
10719
+ return {
10720
+ paletteName: `${profile}-default`,
10721
+ tokens: colors,
10722
+ contrastRequirements: {
10723
+ bodyText: "4.5:1",
10724
+ largeText: "3:1",
10725
+ focusRing: "3:1"
10726
+ }
10727
+ };
10728
+ };
10729
+ var buildSurfaceSystemBlock = () => ({
10730
+ radiusScale: {
10731
+ xs: "6px",
10732
+ sm: "10px",
10733
+ md: "16px",
10734
+ lg: "24px"
10735
+ },
10736
+ borderPolicy: "Use 1px neutral strokes before adding elevation.",
10737
+ shadowPolicy: {
10738
+ sm: "0 1px 2px rgba(15, 23, 42, 0.06)",
10739
+ md: "0 10px 30px rgba(15, 23, 42, 0.10)",
10740
+ lg: "0 24px 60px rgba(15, 23, 42, 0.14)"
10741
+ }
10742
+ });
10743
+ var buildIconSystemBlock = () => ({
10744
+ family: "tabler",
10745
+ strokeWidth: 1.5,
10746
+ style: "rounded-linear",
10747
+ usageRules: [
10748
+ "Use icons to reinforce labels, not replace them.",
10749
+ "Decorative icons should remain visually lighter than primary copy."
10750
+ ]
10751
+ });
10752
+ var buildMotionSystemBlock = () => {
10753
+ const block = cloneTemplate(BASE_CONTRACT_TEMPLATE.motionSystem);
10754
+ return {
10755
+ ...block,
10756
+ durations: {
10757
+ quick: "120ms",
10758
+ standard: "180ms",
10759
+ emphasis: "240ms"
10760
+ }
10761
+ };
10762
+ };
10763
+ var buildResponsiveSystemBlock = () => {
10764
+ const block = cloneTemplate(BASE_CONTRACT_TEMPLATE.responsiveSystem);
10765
+ return {
10766
+ ...block,
10767
+ breakpoints: {
10768
+ mobile: "0-639px",
10769
+ tablet: "640-1023px",
10770
+ desktop: "1024px+"
10771
+ }
10772
+ };
10773
+ };
10774
+ var buildAccessibilityBlock = () => {
10775
+ const block = cloneTemplate(BASE_CONTRACT_TEMPLATE.accessibilityPolicy);
10776
+ return {
10777
+ ...block,
10778
+ reducedMotion: "Respect user preference and preserve information without animation."
10779
+ };
10780
+ };
10781
+ var buildLibraryPolicyBlock = () => ({
10782
+ components: ["shadcn"],
10783
+ icons: ["tabler"],
10784
+ styling: ["tailwindcss"],
10785
+ motion: ["css"],
10786
+ threeD: []
10787
+ });
10788
+ var buildRuntimeBudgetsBlock = (plan) => ({
10789
+ maxHeroActions: 2,
10790
+ maxPrimarySections: 8,
10791
+ maxInteractionLatencyMs: plan.validationTargets.maxInteractionLatencyMs,
10792
+ previewBudgetMs: 1500,
10793
+ notes: [
10794
+ "Keep above-the-fold content inside one visual composition.",
10795
+ "Avoid heavyweight decorative animation before interaction clarity is established."
10796
+ ]
10797
+ });
10798
+ var EMITTED_GOVERNANCE_BLOCKS = [
10799
+ "intent",
10800
+ "generationPlan",
10801
+ "designLanguage",
10802
+ "contentModel",
10803
+ "layoutSystem",
10804
+ "typographySystem",
10805
+ "colorSystem",
10806
+ "surfaceSystem",
10807
+ "iconSystem",
10808
+ "motionSystem",
10809
+ "responsiveSystem",
10810
+ "accessibilityPolicy",
10811
+ "libraryPolicy",
10812
+ "runtimeBudgets"
10813
+ ];
10814
+ var buildNavigationModelBlock = (profile) => {
10815
+ const block = cloneTemplate(BASE_CONTRACT_TEMPLATE.navigationModel);
10816
+ return {
10817
+ ...block,
10818
+ primaryRouteModel: `Use a ${PROFILE_CONFIG[profile].navigationModel} route shell so the primary action remains stable through state changes.`
10819
+ };
10820
+ };
10821
+ var buildAsyncModelBlock = () => {
10822
+ return cloneTemplate(BASE_CONTRACT_TEMPLATE.asyncModel);
10823
+ };
10824
+ var buildPerformanceModelBlock = () => {
10825
+ return cloneTemplate(BASE_CONTRACT_TEMPLATE.performanceModel);
10826
+ };
10827
+ var buildCanvasPlanRequest = (brief, generationPlan) => ({
10828
+ ...cloneTemplate(BASE_PLAN_REQUEST_TEMPLATE),
10829
+ requestId: `req_plan_${referenceFingerprint(brief).slice(0, 12)}`,
10830
+ generationPlan
10831
+ });
10832
+ var buildContractScope = () => ({
10833
+ emittedContract: "CanvasDesignGovernance",
10834
+ emittedGovernanceBlocks: [...EMITTED_GOVERNANCE_BLOCKS],
10835
+ omittedTemplateBlocks: ["navigationModel", "asyncModel", "performanceModel"],
10836
+ note: `${INSPIREDESIGN_HANDOFF_FILES.designContract} is the narrowed canvas governance contract. Use ${INSPIREDESIGN_HANDOFF_FILES.designAgentHandoff} for navigation, async, and performance context that informs implementation but does not belong in canvas governance patches.`
10837
+ });
10838
+ var buildFollowthrough = (generationPlan) => ({
10839
+ summary: buildInspiredesignFollowthroughSummary(),
10840
+ nextStep: buildInspiredesignNextStep(),
10841
+ recommendedSkills: [...INSPIREDESIGN_HANDOFF_RECOMMENDED_SKILLS],
10842
+ commandExamples: { ...INSPIREDESIGN_HANDOFF_COMMANDS },
10843
+ deepCaptureRecommendation: INSPIREDESIGN_HANDOFF_GUIDANCE.deepCaptureRecommendation,
10844
+ contractScope: buildContractScope(),
10845
+ implementationContext: {
10846
+ navigationModel: buildNavigationModelBlock(generationPlan.visualDirection.profile),
10847
+ asyncModel: buildAsyncModelBlock(),
10848
+ performanceModel: buildPerformanceModelBlock()
10849
+ }
10850
+ });
10851
+ var buildDesignContract = (brief, urls, references, plan) => ({
10852
+ intent: buildIntentBlock(brief, urls, references),
10853
+ generationPlan: plan,
10854
+ designLanguage: buildDesignLanguageBlock(plan.visualDirection.profile),
10855
+ contentModel: buildContentModelBlock(brief, references),
10856
+ layoutSystem: buildLayoutSystemBlock(plan.visualDirection.profile),
10857
+ typographySystem: buildTypographySystemBlock(),
10858
+ colorSystem: buildColorSystemBlock(plan.visualDirection.profile),
10859
+ surfaceSystem: buildSurfaceSystemBlock(),
10860
+ iconSystem: buildIconSystemBlock(),
10861
+ motionSystem: buildMotionSystemBlock(),
10862
+ responsiveSystem: buildResponsiveSystemBlock(),
10863
+ accessibilityPolicy: buildAccessibilityBlock(),
10864
+ libraryPolicy: buildLibraryPolicyBlock(),
10865
+ runtimeBudgets: buildRuntimeBudgetsBlock(plan)
10866
+ });
10867
+ var buildTokenStrategy = (profile) => ({
10868
+ colors: PROFILE_CONFIG[profile].colors,
10869
+ typography: {
10870
+ display: "font-display text-[56px] leading-[1.0]",
10871
+ h1: "font-display text-[40px] leading-[1.05]",
10872
+ h2: "font-display text-[28px] leading-[1.1]",
10873
+ h3: "font-display text-[20px] leading-[1.2]",
10874
+ body: "font-body text-[16px] leading-[1.6]",
10875
+ label: "font-body text-[14px] leading-[1.4]",
10876
+ caption: "font-body text-[12px] leading-[1.4]"
10877
+ },
10878
+ spacing: {
10879
+ xs: "4px",
10880
+ sm: "8px",
10881
+ md: "16px",
10882
+ lg: "24px",
10883
+ xl: "40px",
10884
+ section: "96px"
10885
+ },
10886
+ radius: {
10887
+ xs: "6px",
10888
+ sm: "10px",
10889
+ md: "16px",
10890
+ lg: "24px"
10891
+ },
10892
+ shadow: {
10893
+ sm: "0 1px 2px rgba(15, 23, 42, 0.06)",
10894
+ md: "0 10px 30px rgba(15, 23, 42, 0.10)",
10895
+ lg: "0 24px 60px rgba(15, 23, 42, 0.14)"
10896
+ },
10897
+ motion: {
10898
+ quick: "120ms ease-out",
10899
+ standard: "180ms ease-out",
10900
+ emphasis: "240ms cubic-bezier(0.22, 1, 0.36, 1)"
10901
+ },
10902
+ zIndex: {
10903
+ base: 0,
10904
+ sticky: 20,
10905
+ overlay: 40,
10906
+ modal: 60,
10907
+ toast: 70
10908
+ },
10909
+ breakpoints: {
10910
+ mobile: "640px",
10911
+ tablet: "1024px",
10912
+ desktop: "1280px"
10913
+ }
10914
+ });
10915
+ var buildComponentBuildPlan = (profile) => {
10916
+ return PROFILE_CONFIG[profile].componentSequence.map((name) => ({
10917
+ name,
10918
+ purpose: `Establish the ${name.toLowerCase()} pattern as a reusable system primitive.`,
10919
+ states: ["default", "hover", "focus", "disabled"],
10920
+ implementationNote: "Use semantic tokens first and keep copy/state logic outside the visual component."
10921
+ }));
10922
+ };
10923
+ var buildImplementationPlan = (brief, profile, references) => ({
10924
+ architectureRecommendation: "Implement the surface as token-first components using shared semantic CSS variables, then compose page sections from those primitives before adding any page-specific polish.",
10925
+ tokenStrategy: buildTokenStrategy(profile),
10926
+ componentBuildPlan: buildComponentBuildPlan(profile),
10927
+ pageAssemblyPlan: [
10928
+ "Start with the shell and primary navigation pattern.",
10929
+ "Compose the hero or primary decision section before supporting sections.",
10930
+ "Add proof, utility, and footer sections only after the top-level hierarchy is stable."
10931
+ ],
10932
+ stateAndInteractionPlan: [
10933
+ "Keep hover, focus, loading, success, and error states visually distinct.",
10934
+ "Preserve layout during loading and keep transient confirmations out of the main flow.",
10935
+ "Use reduced-motion-safe transitions for reveals and CTA feedback."
10936
+ ],
10937
+ accessibilityChecklist: [
10938
+ "Maintain 4.5:1 body text contrast across all surfaces.",
10939
+ "Preserve visible focus rings on every interactive element.",
10940
+ "Keep landmarks and heading levels explicit and sequential."
10941
+ ],
10942
+ responsiveChecklist: [
10943
+ "Collapse multicolumn layouts before text measure becomes cramped.",
10944
+ "Keep the primary CTA visible without overlap on narrow screens.",
10945
+ "Avoid horizontal scrolling for primary content."
10946
+ ],
10947
+ risksAndAmbiguities: [
10948
+ references.length === 0 ? "No live references were supplied, so visual cues are derived entirely from the written brief." : "Live references were reduced into reusable patterns; unique brand assets should still be recreated, not copied.",
10949
+ "Any missing interaction states must be validated during visual QA."
10950
+ ],
10951
+ buildSequence: [
10952
+ "Define semantic tokens and typography.",
10953
+ "Build the shell, navigation, and primary CTA components.",
10954
+ "Implement section-level patterns and proof blocks.",
10955
+ "Add loading, empty, and error states.",
10956
+ "Run accessibility, responsive, and browser QA before final polish."
10957
+ ]
10958
+ });
10959
+ var formatBulletList = (items) => items.map((item) => `- ${item}`).join("\n");
10960
+ var formatRecordList = (record) => {
10961
+ return Object.entries(record).map(([key, value]) => `- \`${key}\`: ${value}`).join("\n");
10962
+ };
10963
+ var referenceContribution = (reference) => {
10964
+ if (reference.captureStatus === "captured") return "Live hierarchy and component evidence captured from the page.";
10965
+ if (reference.fetchStatus === "captured") return "Content and structural cues inferred from fetched page data.";
10966
+ return "Only operator brief context was available for this reference.";
10967
+ };
10968
+ var referenceMotionNote = (reference) => {
10969
+ if (reference.capture?.snapshot?.warnings?.length) {
10970
+ return `Capture warnings: ${reference.capture.snapshot.warnings.join(", ")}`;
10971
+ }
10972
+ if (reference.captureStatus === "captured") return "Motion should remain subtle until validated against the live capture.";
10973
+ return "Motion is inferred from the brief rather than directly observed.";
10974
+ };
10975
+ var renderReferenceMarkdown = (reference, index) => {
10976
+ const excerpt = reference.excerpt ? clipText(reference.excerpt, 220) : "No fetched excerpt captured.";
10977
+ const title = reference.title ?? reference.url;
10978
+ return [
10979
+ `### Source ${index + 1}: ${title}`,
10980
+ `- what it contributes: ${referenceContribution(reference)}`,
10981
+ `- notable UI patterns: ${reference.capture?.snapshot ? "Primary hierarchy and actionables were captured from the live page." : "Patterns inferred from brief and fetched content."}`,
10982
+ `- typography observations: ${reference.title ? "Headline density and copy hierarchy were inferred from the fetched title and excerpt." : "Typography is inferred."}`,
10983
+ `- color and theme observations: ${reference.captureStatus === "captured" ? "Color posture should be validated against the captured page before cloning brand treatment." : "Color posture remains a synthesis decision."}`,
10984
+ `- layout and hierarchy observations: ${reference.capture?.snapshot ? clipText(reference.capture.snapshot.content, 180) : excerpt}`,
10985
+ `- component patterns: ${reference.capture?.clone ? "Buttons, cards, or layout wrappers can be inferred from the captured clone preview." : "Component families were inferred from available reference text."}`,
10986
+ `- motion/interaction observations: ${referenceMotionNote(reference)}`,
10987
+ `- accessibility/responsiveness notes: ${reference.captureStatus === "captured" ? "Validate focus order, CTA prominence, and stacked layouts during build QA." : "Accessibility and responsiveness are inferred from system defaults."}`,
10988
+ `- what should be adopted, adapted, or avoided: adopt layout hierarchy, adapt it to the new brand tokens, avoid copying proprietary copy or visual assets directly.`
10989
+ ].join("\n");
10990
+ };
10991
+ var resolveProfileConfigFromGenerationPlan = (generationPlan) => {
10992
+ if (!generationPlan || typeof generationPlan !== "object" || !("visualDirection" in generationPlan)) {
10993
+ throw new Error("Inspiredesign design contract requires a generation plan.");
10994
+ }
10995
+ const visualDirection = generationPlan.visualDirection;
10996
+ if (!visualDirection || typeof visualDirection !== "object" || !("profile" in visualDirection)) {
10997
+ throw new Error("Inspiredesign design contract requires a visual direction profile.");
10998
+ }
10999
+ const profile = visualDirection.profile;
11000
+ if (typeof profile !== "string" || !(profile in PROFILE_CONFIG)) {
11001
+ throw new Error("Inspiredesign design contract profile is invalid.");
11002
+ }
11003
+ return PROFILE_CONFIG[profile];
11004
+ };
11005
+ var renderGovernanceMarkdown = (designContract, implementationPlan) => {
11006
+ const profileConfig = resolveProfileConfigFromGenerationPlan(designContract.generationPlan);
11007
+ return [
11008
+ "## 4.1 Design Intent",
11009
+ formatBulletList([
11010
+ `Purpose: ${designContract.intent.task || ""}`,
11011
+ "Capture the reusable visual logic from the references without cloning proprietary brand assets.",
11012
+ "Keep the resulting system executable for downstream build agents."
11013
+ ]),
11014
+ "",
11015
+ "## 4.2 Core UX Principles",
11016
+ formatBulletList(profileConfig.hierarchyPrinciples),
11017
+ "",
11018
+ "## 4.3 Visual Identity",
11019
+ formatBulletList([
11020
+ `Aesthetic style: ${profileConfig.visualPersonality}`,
11021
+ `Brand tone: ${profileConfig.brandTone}`,
11022
+ `Quality posture: ${profileConfig.direction}`,
11023
+ "Direction: modern, system-led, and implementation-aware."
11024
+ ]),
11025
+ "",
11026
+ "## 4.4 Color System",
11027
+ formatRecordList(implementationPlan.tokenStrategy.colors),
11028
+ "",
11029
+ "## 4.5 Typography System",
11030
+ formatRecordList(implementationPlan.tokenStrategy.typography),
11031
+ "",
11032
+ "## 4.6 Spacing and Layout System",
11033
+ formatRecordList(implementationPlan.tokenStrategy.spacing),
11034
+ "",
11035
+ "## 4.7 Shape, Border, and Elevation Rules",
11036
+ [
11037
+ formatRecordList(implementationPlan.tokenStrategy.radius),
11038
+ formatRecordList(implementationPlan.tokenStrategy.shadow)
11039
+ ].join("\n"),
11040
+ "",
11041
+ "## 4.8 Motion and Interaction Rules",
11042
+ formatRecordList(implementationPlan.tokenStrategy.motion),
11043
+ "",
11044
+ "## 4.9 Component System",
11045
+ implementationPlan.componentBuildPlan.map((component) => [
11046
+ `### ${component.name}`,
11047
+ formatBulletList([
11048
+ `purpose: ${component.purpose}`,
11049
+ `structure: build from semantic tokens and keep state visuals explicit`,
11050
+ `visual style: ${profileConfig.direction}`,
11051
+ `states: ${component.states.join(", ")}`,
11052
+ "spacing: keep internal padding on the 8px rhythm",
11053
+ "accessibility notes: preserve focus visibility and keyboard parity",
11054
+ "responsive behavior: stack content before line length becomes cramped",
11055
+ `implementation notes: ${component.implementationNote}`
11056
+ ])
11057
+ ].join("\n")).join("\n\n"),
11058
+ "",
11059
+ "## 4.10 Page or Section Patterns",
11060
+ formatBulletList(profileConfig.pagePatterns),
11061
+ "",
11062
+ "## 4.11 Accessibility Requirements",
11063
+ formatBulletList(implementationPlan.accessibilityChecklist),
11064
+ "",
11065
+ "## 4.12 Responsiveness Requirements",
11066
+ formatBulletList(implementationPlan.responsiveChecklist),
11067
+ "",
11068
+ "## 4.13 Content and Microcopy Guidance",
11069
+ formatBulletList([
11070
+ "Use direct, confident labels and short CTA copy.",
11071
+ "Keep error and success messages specific to the affected region.",
11072
+ "Prefer descriptive button copy over generic verbs."
11073
+ ]),
11074
+ "",
11075
+ "## 4.14 Do / Don't Rules",
11076
+ formatBulletList([
11077
+ "Do preserve one dominant message per section.",
11078
+ "Do encode repeated visual rules into semantic tokens.",
11079
+ "Don't copy proprietary logos, screenshots, or brand-only illustrations.",
11080
+ "Don't hide important actions inside ambiguous hover-only affordances."
11081
+ ]),
11082
+ "",
11083
+ "## 4.15 Acceptance Criteria",
11084
+ formatBulletList([
11085
+ "Primary action is obvious within one viewport.",
11086
+ "Color, typography, spacing, and elevation all resolve through named tokens.",
11087
+ "Keyboard, focus, and reduced-motion behavior remain intact across breakpoints."
11088
+ ])
11089
+ ].join("\n");
11090
+ };
11091
+ var renderImplementationMarkdown = (implementationPlan) => {
11092
+ return [
11093
+ "# 5. Implementation Plan",
11094
+ "",
11095
+ "## 5.1 Architecture Recommendation",
11096
+ implementationPlan.architectureRecommendation,
11097
+ "",
11098
+ "## 5.2 Design Token Strategy",
11099
+ formatRecordList(implementationPlan.tokenStrategy.colors),
11100
+ "",
11101
+ formatRecordList(implementationPlan.tokenStrategy.typography),
11102
+ "",
11103
+ "## 5.3 Component Build Plan",
11104
+ implementationPlan.componentBuildPlan.map((component, index) => `${index + 1}. ${component.name}: ${component.purpose}`).join("\n"),
11105
+ "",
11106
+ "## 5.4 Page Assembly Plan",
11107
+ formatBulletList(implementationPlan.pageAssemblyPlan),
11108
+ "",
11109
+ "## 5.5 State and Interaction Plan",
11110
+ formatBulletList(implementationPlan.stateAndInteractionPlan),
11111
+ "",
11112
+ "## 5.6 Accessibility Implementation Checklist",
11113
+ formatBulletList(implementationPlan.accessibilityChecklist),
11114
+ "",
11115
+ "## 5.7 Responsive Implementation Checklist",
11116
+ formatBulletList(implementationPlan.responsiveChecklist),
11117
+ "",
11118
+ "## 5.8 Risks and Ambiguities",
11119
+ formatBulletList(implementationPlan.risksAndAmbiguities),
11120
+ "",
11121
+ "## 5.9 Recommended Build Sequence",
11122
+ implementationPlan.buildSequence.map((step, index) => `${index + 1}. ${step}`).join("\n")
11123
+ ].join("\n");
11124
+ };
11125
+ var renderPrototypeGuidance = (profile) => {
11126
+ return [
11127
+ "# 6. Optional Prototype Plan",
11128
+ "",
11129
+ "- page structure: establish the shell, hero or primary action zone, proof sections, and footer in that order.",
11130
+ `- section order: ${PROFILE_CONFIG[profile].pagePatterns.join(" -> ")}`,
11131
+ "- component composition: reuse button, card, input, and navigation primitives before page-specific wrappers.",
11132
+ "- interaction expectations: provide visible focus, compact hover feedback, and reduced-motion-safe entry transitions.",
11133
+ "- HTML skeleton guidance: start with one main landmark, one primary CTA group, and semantic sections for proof or detail bands.",
11134
+ "- styling approach: define CSS variables first, then map components to semantic tokens rather than raw values.",
11135
+ "- first prototype should include vs omit: include shell, hero, CTA, one proof section, and one form or action cluster; omit analytics, heavy animation, and tertiary content until hierarchy is proven."
11136
+ ].join("\n");
11137
+ };
11138
+ var renderDeliverablesSummary = (includePrototypeGuidance) => {
11139
+ const deliverables = [
11140
+ "Structured `designContract` JSON aligned to canvas governance",
11141
+ "Valid `generationPlan` JSON aligned to the canvas generation plan contract",
11142
+ "Ready-to-fill `canvasPlanRequest` JSON for `canvas.plan.set`",
11143
+ "Design-agent handoff JSON with contract scope, skill nudges, and richer implementation context",
11144
+ "Human-readable `design.md` design specification",
11145
+ "Engineering implementation plan in JSON and Markdown"
11146
+ ];
11147
+ if (includePrototypeGuidance) {
11148
+ deliverables.push("Prototype guidance Markdown for the first HTML pass");
11149
+ }
11150
+ deliverables.push("Evidence digest describing brief, references, fetch outcomes, and capture outcomes");
11151
+ return formatBulletList(deliverables);
11152
+ };
11153
+ var buildEvidencePayload = (brief, urls, references) => ({
11154
+ brief,
11155
+ briefHash: referenceFingerprint(brief),
11156
+ urls,
11157
+ referenceCount: references.length,
11158
+ references: references.map((reference) => toReferenceEvidenceJson(reference))
11159
+ });
11160
+ var toCaptureEvidenceJson = (capture) => {
11161
+ if (!capture) return null;
11162
+ return {
11163
+ ...capture.title ? { title: capture.title } : {},
11164
+ ...capture.snapshot ? { snapshot: capture.snapshot } : {},
11165
+ ...capture.dom ? { dom: capture.dom } : {},
11166
+ ...capture.clone ? { clone: capture.clone } : {}
11167
+ };
11168
+ };
11169
+ var toReferenceEvidenceJson = (reference) => ({
11170
+ id: reference.id,
11171
+ url: reference.url,
11172
+ ...reference.title ? { title: reference.title } : {},
11173
+ ...reference.excerpt ? { excerpt: reference.excerpt } : {},
11174
+ fetchStatus: reference.fetchStatus,
11175
+ captureStatus: reference.captureStatus,
11176
+ ...reference.fetchFailure ? { fetchFailure: reference.fetchFailure } : {},
11177
+ ...reference.captureFailure ? { captureFailure: reference.captureFailure } : {},
11178
+ capture: toCaptureEvidenceJson(reference.capture)
11179
+ });
11180
+ var buildInspiredesignPacket = (input) => {
11181
+ const brief = trimText(input.brief);
11182
+ const urls = [...new Set(input.urls.map((url) => trimText(url)).filter(Boolean))];
11183
+ const references = input.references.map((reference) => ({
11184
+ ...reference,
11185
+ title: reference.title ? trimText(reference.title) : void 0,
11186
+ excerpt: reference.excerpt ? trimText(reference.excerpt) : void 0
11187
+ }));
11188
+ const profile = classifyProfile(brief, references);
11189
+ const generationPlan = buildGenerationPlan(brief, profile, references);
11190
+ const canvasPlanRequest = buildCanvasPlanRequest(brief, generationPlan);
11191
+ const designContract = buildDesignContract(brief, urls, references, generationPlan);
11192
+ const followthrough = buildFollowthrough(generationPlan);
11193
+ const implementationPlan = buildImplementationPlan(brief, profile, references);
11194
+ const governanceMarkdown = renderGovernanceMarkdown(designContract, implementationPlan);
11195
+ const implementationPlanMarkdown = renderImplementationMarkdown(implementationPlan);
11196
+ const prototypeGuidanceMarkdown = input.includePrototypeGuidance ? renderPrototypeGuidance(profile) : null;
11197
+ const designMarkdown = [
11198
+ "# 1. Executive Summary",
9611
11199
  "",
9612
- ...lines,
11200
+ formatBulletList([
11201
+ `Analyzed brief plus ${references.length || 0} inspiration reference(s).`,
11202
+ `Chosen design direction: ${PROFILE_CONFIG[profile].direction}.`,
11203
+ "Final outcome: a reusable design contract, engineering plan, and optional prototype guidance.",
11204
+ `Scope mode: ${references.length > 1 ? "full-site synthesis" : "single-surface synthesis"}.`
11205
+ ]),
9613
11206
  "",
9614
- "## Metadata",
9615
- "```json",
9616
- JSON.stringify(args.meta, null, 2),
9617
- "```"
11207
+ "# 2. Inspiration Analysis",
11208
+ "",
11209
+ references.length > 0 ? references.map(renderReferenceMarkdown).join("\n\n") : "- No live inspiration source was provided. The system is derived entirely from the brief.",
11210
+ "",
11211
+ "# 3. Unified Design Direction",
11212
+ "",
11213
+ formatBulletList([
11214
+ `visual personality: ${PROFILE_CONFIG[profile].visualPersonality}`,
11215
+ `tone: ${PROFILE_CONFIG[profile].brandTone}`,
11216
+ `UX principles: ${PROFILE_CONFIG[profile].hierarchyPrinciples.join(" ")}`,
11217
+ `interaction philosophy: ${PROFILE_CONFIG[profile].interactionPhilosophy}`,
11218
+ "branding posture: preserve the intent of the references without cloning brand-only assets.",
11219
+ "system coherence rules: encode tokens first, keep one dominant idea per section, and keep states explicit."
11220
+ ]),
11221
+ "",
11222
+ "# 4. Design Governance (`design.md`)",
11223
+ "",
11224
+ renderGovernanceMarkdown(designContract, implementationPlan),
11225
+ "",
11226
+ renderImplementationMarkdown(implementationPlan),
11227
+ "",
11228
+ prototypeGuidanceMarkdown ?? "# 6. Optional Prototype Plan\n\n- Prototype guidance omitted for this run.",
11229
+ "",
11230
+ "# 7. Deliverables Summary",
11231
+ "",
11232
+ renderDeliverablesSummary(Boolean(input.includePrototypeGuidance))
9618
11233
  ].join("\n");
9619
- const comparisonCsv = toComparisonCsv(args.offers);
9620
- const contextPayload = {
9621
- query: args.query,
9622
- highlights: lines,
9623
- offers: args.offers,
9624
- meta: args.meta
9625
- };
9626
- const files = [
9627
- { path: "deals.md", content: markdown },
9628
- { path: "offers.json", content: { offers: args.offers } },
9629
- { path: "comparison.csv", content: comparisonCsv },
9630
- { path: "meta.json", content: args.meta },
9631
- { path: "deals-context.json", content: contextPayload }
9632
- ];
9633
- if (args.mode === "compact") {
9634
- return {
9635
- response: {
9636
- mode: args.mode,
9637
- summary: lines.join("\n"),
9638
- meta: args.meta
9639
- },
9640
- files
9641
- };
9642
- }
9643
- if (args.mode === "json") {
9644
- return {
9645
- response: {
9646
- mode: args.mode,
9647
- offers: args.offers,
9648
- meta: args.meta
9649
- },
9650
- files
9651
- };
9652
- }
9653
- if (args.mode === "md") {
9654
- return {
9655
- response: {
9656
- mode: args.mode,
9657
- markdown,
9658
- meta: args.meta
9659
- },
9660
- files
9661
- };
9662
- }
9663
- if (args.mode === "context") {
9664
- return {
9665
- response: {
9666
- mode: args.mode,
9667
- context: contextPayload,
9668
- meta: args.meta
9669
- },
9670
- files
9671
- };
9672
- }
9673
11234
  return {
9674
- response: {
9675
- mode: "path",
9676
- meta: args.meta
9677
- },
9678
- files
11235
+ designContract,
11236
+ generationPlan,
11237
+ canvasPlanRequest,
11238
+ followthrough,
11239
+ designMarkdown,
11240
+ implementationPlan,
11241
+ implementationPlanMarkdown,
11242
+ prototypeGuidanceMarkdown,
11243
+ evidence: buildEvidencePayload(brief, urls, references)
9679
11244
  };
9680
11245
  };
9681
11246
 
11247
+ // src/providers/workflow-handoff.ts
11248
+ var PRODUCT_VIDEO_BRIEF_HELPER_PATH = "./skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh";
11249
+ var PRODUCT_VIDEO_BRIEF_HELPER_COMMAND = `${PRODUCT_VIDEO_BRIEF_HELPER_PATH} <pack>/manifest.json`;
11250
+ var createSuccessHandoff = (followthroughSummary, suggestedNextAction, suggestedSteps) => ({
11251
+ followthroughSummary,
11252
+ suggestedNextAction,
11253
+ suggestedSteps
11254
+ });
11255
+ var buildResearchSuccessHandoff = () => {
11256
+ return createSuccessHandoff(
11257
+ "Review the ranked records and artifact bundle before turning the result into a publishable claim.",
11258
+ "Open the returned artifact path, inspect the supporting records, and rerun with explicit --sources or a tighter timebox if you need stronger evidence.",
11259
+ [
11260
+ { reason: "Check which records actually support the final claim." },
11261
+ { reason: "Rerun with explicit sources or a narrower timebox if the evidence set is still too broad." }
11262
+ ]
11263
+ );
11264
+ };
11265
+ var buildShoppingSuccessHandoff = () => {
11266
+ return createSuccessHandoff(
11267
+ "Review the offer set and diagnostics before calling any result a strong deal.",
11268
+ "Inspect the offers and meta.offerFilterDiagnostics, then rerun with explicit providers or budget and region adjustments if you need a tighter comparison.",
11269
+ [
11270
+ { reason: "Check which offers survived the workflow filters and why." },
11271
+ { reason: "Rerun with explicit providers or updated budget and region inputs if the comparison is still noisy." }
11272
+ ]
11273
+ );
11274
+ };
11275
+ var buildProductVideoSuccessHandoff = () => {
11276
+ return createSuccessHandoff(
11277
+ "Review the generated asset pack to confirm whether it is visual-ready or metadata-first before briefing production.",
11278
+ `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.`,
11279
+ [
11280
+ { reason: "Confirm whether the pack already includes enough images or screenshots for production." },
11281
+ {
11282
+ reason: "Run the product-presentation-asset brief helper on manifest.json to generate the production brief files.",
11283
+ command: PRODUCT_VIDEO_BRIEF_HELPER_COMMAND
11284
+ },
11285
+ { reason: "Source or capture visuals before final handoff if the pack is metadata-first." }
11286
+ ]
11287
+ );
11288
+ };
11289
+
9682
11290
  // src/providers/shopping-postprocess.ts
9683
- import { createHash as createHash3 } from "crypto";
11291
+ import { createHash as createHash4 } from "crypto";
9684
11292
  var LOOKS_LIKE_URL_RE = /^https?:\/\/\S+$/i;
9685
11293
  var PRICE_TOKEN_RE = /((?:US\$|CA\$|C\$|USD|CAD|EUR|GBP|[$€£]))\s*([0-9]{1,4}(?:[.,][0-9]{3})*(?:[.,][0-9]{1,2})?)/gi;
9686
11294
  var CONTEXTUAL_PRICE_TOKEN_RE = /(?:connect to any carrier later|starting at|starts at|starting from|from|buy now for|buy for)\s*((?:US\$|CA\$|C\$|USD|CAD|EUR|GBP|[$€£]))\s*([0-9]{1,4}(?:[.,][0-9]{3})*(?:[.,][0-9]{1,2})?)/gi;
@@ -9766,7 +11374,7 @@ var REGION_CURRENCY_BY_REGION = {
9766
11374
  uk: "GBP",
9767
11375
  eu: "EUR"
9768
11376
  };
9769
- var hash = (value) => createHash3("sha1").update(value).digest("hex").slice(0, 16);
11377
+ var hash = (value) => createHash4("sha1").update(value).digest("hex").slice(0, 16);
9770
11378
  var normalizePlainText = (value) => {
9771
11379
  if (!value) return "";
9772
11380
  return extractText(value).replace(/[\u0000-\u001F\u007F-\u009F]+/g, " ").replace(/\s+/g, " ").trim();
@@ -10395,7 +12003,7 @@ var compileShoppingWorkflow = (input, options = {}) => {
10395
12003
  };
10396
12004
 
10397
12005
  // src/providers/shopping-compiler.ts
10398
- import { createHash as createHash4 } from "crypto";
12006
+ import { createHash as createHash5 } from "crypto";
10399
12007
  var DEFAULT_SHOPPING_SEARCH_LIMIT = 8;
10400
12008
  var SHOPPING_FETCH_RECOVERY_LIMIT = 2;
10401
12009
  var SEARCH_INDEX_RETRIEVAL_PATHS = /* @__PURE__ */ new Set(["shopping:search:index", "shopping:search:link"]);
@@ -10412,7 +12020,7 @@ var emptyCheckpointState = () => ({
10412
12020
  step_results_by_id: {}
10413
12021
  });
10414
12022
  var createShoppingSearchStepId = (providerId) => `search:${providerId}`;
10415
- var createShoppingFetchStepId = (providerId, url) => `fetch:${providerId}:${createHash4("sha1").update(canonicalizeUrl(url)).digest("hex").slice(0, 16)}`;
12023
+ var createShoppingFetchStepId = (providerId, url) => `fetch:${providerId}:${createHash5("sha1").update(canonicalizeUrl(url)).digest("hex").slice(0, 16)}`;
10416
12024
  var serializeShoppingCheckpointState = (state) => ({
10417
12025
  completed_step_ids: [...state.completed_step_ids],
10418
12026
  step_results_by_id: state.step_results_by_id
@@ -10898,7 +12506,7 @@ var compileProductVideoExecutionPlan = (args) => {
10898
12506
  };
10899
12507
 
10900
12508
  // src/providers/research-compiler.ts
10901
- import { createHash as createHash5 } from "crypto";
12509
+ import { createHash as createHash6 } from "crypto";
10902
12510
  var RESEARCH_AUTO_SOURCES = ["web", "community", "social"];
10903
12511
  var RESEARCH_ALL_SOURCES = [...RESEARCH_AUTO_SOURCES];
10904
12512
  var DEFAULT_RESEARCH_SEARCH_LIMIT = 10;
@@ -10923,7 +12531,7 @@ var toProviderSource = (providerId) => {
10923
12531
  return null;
10924
12532
  };
10925
12533
  var createResearchSearchStepId = (source) => `search:${source}`;
10926
- var createResearchFetchStepId = (url) => `fetch:web:${createHash5("sha1").update(canonicalizeUrl(url)).digest("hex").slice(0, 16)}`;
12534
+ var createResearchFetchStepId = (url) => `fetch:web:${createHash6("sha1").update(canonicalizeUrl(url)).digest("hex").slice(0, 16)}`;
10927
12535
  var serializeResearchCheckpointState = (state) => ({
10928
12536
  completed_step_ids: [...state.completed_step_ids],
10929
12537
  step_results_by_id: state.step_results_by_id
@@ -11266,6 +12874,10 @@ var SIGNAL_WINDOW = 50;
11266
12874
  var RECOVERY_WINDOWS_REQUIRED = 2;
11267
12875
  var providerSignalMap = /* @__PURE__ */ new Map();
11268
12876
  var workflowLogger = createLogger("provider-workflows");
12877
+ var withFollowthroughMeta = (meta, handoff) => ({
12878
+ ...meta,
12879
+ followthroughSummary: handoff.followthroughSummary
12880
+ });
11269
12881
  var detectSignal = (error) => {
11270
12882
  const reasonCode = error.reasonCode ?? normalizeProviderReasonCode({
11271
12883
  code: error.code,
@@ -11509,24 +13121,54 @@ var withPrimaryConstraintMeta = (meta, failures) => {
11509
13121
  const primaryIssue = summarizePrimaryProviderIssue(failures);
11510
13122
  return primaryIssue ? {
11511
13123
  ...meta,
11512
- primary_constraint: primaryIssue,
11513
13124
  primaryConstraint: primaryIssue,
11514
13125
  primaryConstraintSummary: primaryIssue.summary
11515
13126
  } : meta;
11516
13127
  };
11517
- var withPrimaryConstraintSummaryOverride = (meta, summary) => {
13128
+ var withCamelCasePrimaryConstraintMeta = withPrimaryConstraintMeta;
13129
+ var readPrimaryConstraintGuidance = (constraint) => {
13130
+ const guidance = constraint.guidance;
13131
+ return guidance && typeof guidance === "object" && !Array.isArray(guidance) ? guidance : void 0;
13132
+ };
13133
+ var withPrimaryConstraintSummaryOverride = (meta, summary, guidance) => {
11518
13134
  const currentPrimaryConstraint = meta.primaryConstraint;
11519
- const nextPrimaryConstraint = currentPrimaryConstraint && typeof currentPrimaryConstraint === "object" && !Array.isArray(currentPrimaryConstraint) ? {
11520
- ...currentPrimaryConstraint,
13135
+ const baseConstraint = currentPrimaryConstraint && typeof currentPrimaryConstraint === "object" && !Array.isArray(currentPrimaryConstraint) ? currentPrimaryConstraint : { reasonCode: "env_limited" };
13136
+ const existingGuidance = readPrimaryConstraintGuidance(baseConstraint);
13137
+ const { guidance: _existingGuidance, ...nextPrimaryConstraintBase } = baseConstraint;
13138
+ const nextGuidance = guidance ?? existingGuidance;
13139
+ const nextPrimaryConstraint = nextGuidance ? {
13140
+ ...nextPrimaryConstraintBase,
13141
+ summary,
13142
+ guidance: nextGuidance
13143
+ } : {
13144
+ ...nextPrimaryConstraintBase,
11521
13145
  summary
11522
- } : { summary, reasonCode: "env_limited" };
13146
+ };
11523
13147
  return {
11524
13148
  ...meta,
11525
- primary_constraint: nextPrimaryConstraint,
11526
13149
  primaryConstraint: nextPrimaryConstraint,
11527
13150
  primaryConstraintSummary: summary
11528
13151
  };
11529
13152
  };
13153
+ var withReasonCodeDistributionMeta = (meta, reasonCodeDistribution) => {
13154
+ const metrics = meta.metrics;
13155
+ const nextMetrics = metrics && typeof metrics === "object" && !Array.isArray(metrics) ? {
13156
+ ...metrics,
13157
+ reasonCodeDistribution
13158
+ } : { reasonCodeDistribution };
13159
+ return {
13160
+ ...meta,
13161
+ metrics: nextMetrics,
13162
+ reasonCodeDistribution
13163
+ };
13164
+ };
13165
+ var incrementReasonCodeDistribution = (reasonCodeDistribution, reasonCode, count) => {
13166
+ if (count <= 0) return reasonCodeDistribution;
13167
+ return Object.fromEntries(Object.entries({
13168
+ ...reasonCodeDistribution,
13169
+ [reasonCode]: (reasonCodeDistribution[reasonCode] ?? 0) + count
13170
+ }).sort(([left], [right]) => left.localeCompare(right)));
13171
+ };
11530
13172
  var summarizeShoppingOfferFilterConstraint = (args) => {
11531
13173
  const primaryIssue = summarizePrimaryProviderIssue(args.failures);
11532
13174
  if (primaryIssue && (primaryIssue.reasonCode !== "env_limited" || primaryIssue.constraint || primaryIssue.blockerType === "anti_bot_challenge")) {
@@ -11586,6 +13228,7 @@ var withBrowserModeOverride = (options, input) => {
11586
13228
  var WORKFLOW_KIND_BY_SUSPENDED_INTENT_KIND = {
11587
13229
  "workflow.research": "research",
11588
13230
  "workflow.shopping": "shopping",
13231
+ "workflow.inspiredesign": "inspiredesign",
11589
13232
  "workflow.product_video": "product_video"
11590
13233
  };
11591
13234
  var buildWorkflowResumePayload = (kind, input) => ({
@@ -11837,7 +13480,7 @@ var rankResearchRecords = (records) => {
11837
13480
  return left.id.localeCompare(right.id);
11838
13481
  });
11839
13482
  };
11840
- var hash2 = (value) => createHash6("sha1").update(value).digest("hex").slice(0, 16);
13483
+ var hash2 = (value) => createHash7("sha1").update(value).digest("hex").slice(0, 16);
11841
13484
  var RESEARCH_ALWAYS_SANITIZED_PATHS = /* @__PURE__ */ new Set([
11842
13485
  "community:search:index",
11843
13486
  "social:search:index"
@@ -11956,6 +13599,235 @@ var createRemainingTimeoutResolver = (timeoutMs) => {
11956
13599
  const startedAtMs = Date.now();
11957
13600
  return () => Math.max(1, timeoutMs - Math.max(0, Date.now() - startedAtMs));
11958
13601
  };
13602
+ var INSPIREDESIGN_RENDER_MODES = /* @__PURE__ */ new Set(["compact", "json", "md", "context", "path"]);
13603
+ var INSPIREDESIGN_CAPTURE_MODES = /* @__PURE__ */ new Set(["off", "deep"]);
13604
+ var INSPIREDESIGN_COOKIE_POLICIES = /* @__PURE__ */ new Set(["off", "auto", "required"]);
13605
+ var serializeInspiredesignRunInput = (input) => ({
13606
+ brief: input.brief,
13607
+ urls: input.urls,
13608
+ captureMode: input.captureMode,
13609
+ mode: input.mode,
13610
+ ...input.includePrototypeGuidance !== void 0 ? { includePrototypeGuidance: input.includePrototypeGuidance } : {},
13611
+ ...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
13612
+ ...input.outputDir ? { outputDir: input.outputDir } : {},
13613
+ ...typeof input.ttlHours === "number" ? { ttlHours: input.ttlHours } : {},
13614
+ ...typeof input.useCookies === "boolean" ? { useCookies: input.useCookies } : {},
13615
+ ...input.challengeAutomationMode ? { challengeAutomationMode: input.challengeAutomationMode } : {},
13616
+ ...input.cookiePolicyOverride ? { cookiePolicyOverride: input.cookiePolicyOverride } : {}
13617
+ });
13618
+ var parseInspiredesignEnvelopeInput = (input) => ({
13619
+ brief: typeof input.brief === "string" ? input.brief : "",
13620
+ mode: typeof input.mode === "string" && INSPIREDESIGN_RENDER_MODES.has(input.mode) ? input.mode : "compact",
13621
+ ...Array.isArray(input.urls) ? { urls: input.urls.filter((url) => typeof url === "string") } : {},
13622
+ ...typeof input.captureMode === "string" && INSPIREDESIGN_CAPTURE_MODES.has(input.captureMode) ? { captureMode: input.captureMode } : {},
13623
+ ...typeof input.includePrototypeGuidance === "boolean" ? { includePrototypeGuidance: input.includePrototypeGuidance } : {},
13624
+ ...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
13625
+ ...typeof input.outputDir === "string" && input.outputDir.length > 0 ? { outputDir: input.outputDir } : {},
13626
+ ...typeof input.ttlHours === "number" ? { ttlHours: input.ttlHours } : {},
13627
+ ...typeof input.useCookies === "boolean" ? { useCookies: input.useCookies } : {},
13628
+ ...isChallengeAutomationMode(input.challengeAutomationMode) ? { challengeAutomationMode: input.challengeAutomationMode } : {},
13629
+ ...typeof input.cookiePolicyOverride === "string" && INSPIREDESIGN_COOKIE_POLICIES.has(input.cookiePolicyOverride) ? { cookiePolicyOverride: input.cookiePolicyOverride } : {}
13630
+ });
13631
+ var normalizeInspiredesignUrls = (urls) => {
13632
+ if (!urls || urls.length === 0) return [];
13633
+ const normalized = urls.map((url) => url.trim()).filter(Boolean);
13634
+ const invalid = normalized.find((url) => !LOOKS_LIKE_URL_RE.test(url));
13635
+ if (invalid) {
13636
+ throw new Error(`Inspiredesign workflow received an invalid URL: ${invalid}`);
13637
+ }
13638
+ return [...new Set(normalized.map((url) => canonicalizeUrl(url)))];
13639
+ };
13640
+ var normalizeInspiredesignInput = (input) => {
13641
+ const brief = input.brief.trim();
13642
+ if (!brief) {
13643
+ throw new Error("Inspiredesign workflow requires a non-empty brief.");
13644
+ }
13645
+ return {
13646
+ ...input,
13647
+ brief,
13648
+ urls: normalizeInspiredesignUrls(input.urls),
13649
+ captureMode: input.captureMode ?? "off",
13650
+ mode: input.mode ?? "compact"
13651
+ };
13652
+ };
13653
+ var isInspiredesignWorkflowEnvelopeInput = (input) => {
13654
+ return "kind" in input && "input" in input;
13655
+ };
13656
+ var buildInspiredesignEnvelope = (input) => {
13657
+ if (isInspiredesignWorkflowEnvelopeInput(input)) {
13658
+ if (!isWorkflowResumeEnvelope(input)) {
13659
+ throw new Error("Inspiredesign workflow envelope is invalid.");
13660
+ }
13661
+ if (input.kind !== "inspiredesign") {
13662
+ throw new Error(`Inspiredesign workflow envelope kind mismatch. Expected inspiredesign but received ${input.kind}.`);
13663
+ }
13664
+ return {
13665
+ envelope: input,
13666
+ workflowInput: normalizeInspiredesignInput(parseInspiredesignEnvelopeInput(input.input))
13667
+ };
13668
+ }
13669
+ const workflowInput = normalizeInspiredesignInput(input);
13670
+ return {
13671
+ envelope: buildWorkflowResumeEnvelope("inspiredesign", serializeInspiredesignRunInput(workflowInput)),
13672
+ workflowInput
13673
+ };
13674
+ };
13675
+ var appendWorkflowTrace = (trace, stage, event, details) => [
13676
+ ...trace,
13677
+ {
13678
+ at: (/* @__PURE__ */ new Date()).toISOString(),
13679
+ stage,
13680
+ event,
13681
+ details
13682
+ }
13683
+ ];
13684
+ var buildInspiredesignStepEnvelope = (workflowInput, trace, stepIndex, url) => buildWorkflowResumeEnvelope(
13685
+ "inspiredesign",
13686
+ serializeInspiredesignRunInput(workflowInput),
13687
+ {
13688
+ checkpoint: {
13689
+ stage: "execute",
13690
+ stepId: "fetch_reference",
13691
+ stepIndex,
13692
+ state: { url },
13693
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13694
+ },
13695
+ trace
13696
+ }
13697
+ );
13698
+ var buildInspiredesignFetchOptions = (workflowInput, envelope, timeoutMs) => withWorkflowResumeEnvelopeIntent(
13699
+ withChallengeAutomationOverride(
13700
+ withCookieOverrides({
13701
+ ...typeof timeoutMs === "number" ? { timeoutMs } : {}
13702
+ }, workflowInput),
13703
+ workflowInput
13704
+ ),
13705
+ "workflow.inspiredesign",
13706
+ envelope
13707
+ );
13708
+ var hasInspiredesignCaptureEvidence = (capture) => {
13709
+ if (!capture) return false;
13710
+ return Boolean(capture.title || capture.snapshot || capture.dom || capture.clone);
13711
+ };
13712
+ var captureInspiredesignReference = async (url, captureMode, workflowInput, captureReference, timeoutMs) => {
13713
+ if (captureMode === "off") {
13714
+ return { captureStatus: "off" };
13715
+ }
13716
+ if (!captureReference) {
13717
+ return {
13718
+ captureStatus: "failed",
13719
+ captureFailure: "Deep capture requested, but no browser capture callback was available."
13720
+ };
13721
+ }
13722
+ try {
13723
+ const capture = await captureReference(url, {
13724
+ timeoutMs,
13725
+ useCookies: workflowInput.useCookies,
13726
+ challengeAutomationMode: workflowInput.challengeAutomationMode,
13727
+ cookiePolicyOverride: workflowInput.cookiePolicyOverride
13728
+ });
13729
+ if (!hasInspiredesignCaptureEvidence(capture)) {
13730
+ return {
13731
+ captureStatus: "failed",
13732
+ captureFailure: "Deep capture did not return usable snapshot, DOM, or clone evidence."
13733
+ };
13734
+ }
13735
+ return {
13736
+ captureStatus: "captured",
13737
+ capture
13738
+ };
13739
+ } catch (error) {
13740
+ return {
13741
+ captureStatus: "failed",
13742
+ captureFailure: error instanceof Error ? error.message : "Deep capture failed."
13743
+ };
13744
+ }
13745
+ };
13746
+ var getInspiredesignPrimaryRecord = (result, url) => {
13747
+ const canonicalUrl = canonicalizeUrl(url);
13748
+ return result.records.find((record) => record.url && canonicalizeUrl(record.url) === canonicalUrl) ?? result.records[0];
13749
+ };
13750
+ var summarizeInspiredesignFetchFailure = (result) => {
13751
+ return summarizePrimaryProviderIssue(result.failures)?.summary ?? result.error?.message;
13752
+ };
13753
+ var excerptFromInspiredesignRecord = (record) => {
13754
+ const content = normalizePlainText(record?.content);
13755
+ if (!content) return void 0;
13756
+ return toSnippet(content, 240);
13757
+ };
13758
+ var buildInspiredesignReference = (url, result, capture) => {
13759
+ const primary = getInspiredesignPrimaryRecord(result, url);
13760
+ const title = primary?.title ?? capture.capture?.title;
13761
+ const excerpt = excerptFromInspiredesignRecord(primary);
13762
+ const fetchStatus = result.records.length > 0 ? "captured" : "failed";
13763
+ return {
13764
+ id: createHash7("sha256").update(url).digest("hex").slice(0, 12),
13765
+ url,
13766
+ ...title ? { title } : {},
13767
+ ...excerpt ? { excerpt } : {},
13768
+ fetchStatus,
13769
+ captureStatus: capture.captureStatus,
13770
+ ...fetchStatus === "failed" && summarizeInspiredesignFetchFailure(result) ? { fetchFailure: summarizeInspiredesignFetchFailure(result) } : {},
13771
+ ...capture.captureFailure ? { captureFailure: capture.captureFailure } : {},
13772
+ ...capture.capture ? { capture: capture.capture } : {}
13773
+ };
13774
+ };
13775
+ var summarizeInspiredesignCaptureConstraint = (references) => {
13776
+ const failedReferences = references.filter((reference) => reference.captureStatus === "failed");
13777
+ if (failedReferences.length === 0) {
13778
+ return void 0;
13779
+ }
13780
+ const summary = `Deep capture failed for ${failedReferences.length} ${failedReferences.length === 1 ? "reference" : "references"}.`;
13781
+ const retryUrls = failedReferences.slice(0, 2).map((reference) => `Retry deep capture for ${reference.url} after restoring the required browser session state.`);
13782
+ return {
13783
+ summary,
13784
+ guidance: {
13785
+ reason: summary,
13786
+ recommendedNextCommands: [
13787
+ "Rerun inspiredesign after configuring providers.cookieSource for the protected references you need to capture.",
13788
+ ...retryUrls
13789
+ ]
13790
+ }
13791
+ };
13792
+ };
13793
+ var buildInspiredesignMeta = (runtime, workflowInput, references, failures, followthrough) => {
13794
+ const failedCaptures = references.filter((reference) => reference.captureStatus === "failed");
13795
+ let reasonCodeDistribution = summarizeReasonCodeDistribution(failures);
13796
+ let meta = withCamelCasePrimaryConstraintMeta(withReasonCodeDistributionMeta({
13797
+ selection: {
13798
+ urls: workflowInput.urls,
13799
+ capture_mode: workflowInput.captureMode,
13800
+ include_prototype_guidance: Boolean(workflowInput.includePrototypeGuidance)
13801
+ },
13802
+ metrics: {
13803
+ reference_count: references.length,
13804
+ fetched_references: references.filter((reference) => reference.fetchStatus === "captured").length,
13805
+ captured_references: references.filter((reference) => reference.captureStatus === "captured").length,
13806
+ failed_fetches: references.filter((reference) => reference.fetchStatus === "failed").length,
13807
+ failed_captures: failedCaptures.length
13808
+ },
13809
+ alerts: buildWorkflowAlerts(runtime, failures)
13810
+ }, reasonCodeDistribution), failures);
13811
+ if (!meta.primaryConstraint) {
13812
+ const captureConstraint = summarizeInspiredesignCaptureConstraint(references);
13813
+ if (captureConstraint) {
13814
+ reasonCodeDistribution = incrementReasonCodeDistribution(
13815
+ reasonCodeDistribution,
13816
+ "env_limited",
13817
+ failedCaptures.length
13818
+ );
13819
+ meta = withReasonCodeDistributionMeta(meta, reasonCodeDistribution);
13820
+ meta = withPrimaryConstraintSummaryOverride(meta, captureConstraint.summary, captureConstraint.guidance);
13821
+ }
13822
+ }
13823
+ return {
13824
+ ...meta,
13825
+ followthroughSummary: followthrough.summary,
13826
+ recommendedSkills: followthrough.recommendedSkills,
13827
+ deepCaptureRecommendation: followthrough.deepCaptureRecommendation,
13828
+ contractScope: followthrough.contractScope
13829
+ };
13830
+ };
11959
13831
  var inferBrandFromContent = (content) => {
11960
13832
  const normalized = normalizePlainText(content);
11961
13833
  if (!normalized) return void 0;
@@ -12551,11 +14423,13 @@ var runResearchWorkflow = async (runtime, input) => {
12551
14423
  failures: mergedFailures,
12552
14424
  alerts: buildWorkflowAlerts(runtime, mergedFailures)
12553
14425
  }, primaryConstraintFailures);
14426
+ const handoff = buildResearchSuccessHandoff();
14427
+ const responseMeta = withFollowthroughMeta(meta, handoff);
12554
14428
  const rendered = renderResearch({
12555
14429
  mode: workflowInput.mode,
12556
14430
  topic: plan.compiled.topic,
12557
14431
  records: ranked,
12558
- meta
14432
+ meta: responseMeta
12559
14433
  });
12560
14434
  const bundle = await createArtifactBundle({
12561
14435
  namespace: "research",
@@ -12566,20 +14440,22 @@ var runResearchWorkflow = async (runtime, input) => {
12566
14440
  if (workflowInput.mode === "path") {
12567
14441
  return {
12568
14442
  ...rendered.response,
14443
+ ...handoff,
12569
14444
  path: bundle.basePath,
12570
14445
  records: ranked,
12571
14446
  meta: {
12572
- ...meta,
14447
+ ...responseMeta,
12573
14448
  artifact_manifest: bundle.manifest
12574
14449
  }
12575
14450
  };
12576
14451
  }
12577
14452
  return {
12578
14453
  ...rendered.response,
14454
+ ...handoff,
12579
14455
  artifact_path: bundle.basePath,
12580
14456
  records: ranked,
12581
14457
  meta: {
12582
- ...meta,
14458
+ ...responseMeta,
12583
14459
  artifact_manifest: bundle.manifest
12584
14460
  }
12585
14461
  };
@@ -12726,11 +14602,13 @@ var runShoppingWorkflow = async (runtime, input) => {
12726
14602
  if (typeof filterConstraintSummary === "string") {
12727
14603
  meta = withPrimaryConstraintSummaryOverride(meta, filterConstraintSummary);
12728
14604
  }
14605
+ const handoff = buildShoppingSuccessHandoff();
14606
+ const responseMeta = withFollowthroughMeta(meta, handoff);
12729
14607
  const rendered = renderShopping({
12730
14608
  mode: workflowInput.mode,
12731
14609
  query: plan.compiled.query,
12732
14610
  offers,
12733
- meta
14611
+ meta: responseMeta
12734
14612
  });
12735
14613
  const bundle = await createArtifactBundle({
12736
14614
  namespace: "shopping",
@@ -12741,18 +14619,112 @@ var runShoppingWorkflow = async (runtime, input) => {
12741
14619
  if (workflowInput.mode === "path") {
12742
14620
  return {
12743
14621
  ...rendered.response,
14622
+ ...handoff,
12744
14623
  path: bundle.basePath,
12745
14624
  offers,
12746
14625
  meta: {
12747
- ...meta,
14626
+ ...responseMeta,
12748
14627
  artifact_manifest: bundle.manifest
12749
14628
  }
12750
14629
  };
12751
14630
  }
12752
14631
  return {
12753
14632
  ...rendered.response,
14633
+ ...handoff,
12754
14634
  offers,
12755
14635
  artifact_path: bundle.basePath,
14636
+ meta: {
14637
+ ...responseMeta,
14638
+ artifact_manifest: bundle.manifest
14639
+ }
14640
+ };
14641
+ };
14642
+ var runInspiredesignWorkflow = async (runtime, input, options = {}) => {
14643
+ const { envelope, workflowInput } = buildInspiredesignEnvelope(input);
14644
+ const remainingTimeoutMs = createRemainingTimeoutResolver(workflowInput.timeoutMs);
14645
+ let trace = appendWorkflowTrace(envelope.trace ?? [], "compile", "compile_started", {
14646
+ kind: "inspiredesign"
14647
+ });
14648
+ trace = appendWorkflowTrace(trace, "compile", "compile_completed", {
14649
+ kind: "inspiredesign",
14650
+ urlCount: workflowInput.urls.length,
14651
+ captureMode: workflowInput.captureMode
14652
+ });
14653
+ const references = [];
14654
+ const failures = [];
14655
+ for (const [index, url] of workflowInput.urls.entries()) {
14656
+ const stepTrace = appendWorkflowTrace(trace, "execute", "reference_started", {
14657
+ stepIndex: index,
14658
+ url
14659
+ });
14660
+ const fetchTimeoutMs = remainingTimeoutMs();
14661
+ const result = await runtime.fetch(
14662
+ { url },
14663
+ buildInspiredesignFetchOptions(
14664
+ workflowInput,
14665
+ buildInspiredesignStepEnvelope(workflowInput, stepTrace, index, url),
14666
+ fetchTimeoutMs
14667
+ )
14668
+ );
14669
+ observeWorkflowSignals(runtime, result);
14670
+ failures.push(...result.failures);
14671
+ const captureTimeoutMs = remainingTimeoutMs();
14672
+ const capture = await captureInspiredesignReference(
14673
+ url,
14674
+ workflowInput.captureMode,
14675
+ workflowInput,
14676
+ options.captureReference,
14677
+ captureTimeoutMs
14678
+ );
14679
+ references.push(buildInspiredesignReference(url, result, capture));
14680
+ trace = appendWorkflowTrace(stepTrace, "execute", "reference_completed", {
14681
+ stepIndex: index,
14682
+ url,
14683
+ fetchStatus: result.records.length > 0 ? "captured" : "failed",
14684
+ captureStatus: capture.captureStatus
14685
+ });
14686
+ }
14687
+ const packet = buildInspiredesignPacket({
14688
+ brief: workflowInput.brief,
14689
+ urls: workflowInput.urls,
14690
+ references,
14691
+ includePrototypeGuidance: workflowInput.includePrototypeGuidance
14692
+ });
14693
+ const meta = buildInspiredesignMeta(runtime, workflowInput, references, failures, packet.followthrough);
14694
+ const rendered = renderInspiredesign({
14695
+ mode: workflowInput.mode,
14696
+ brief: workflowInput.brief,
14697
+ urls: workflowInput.urls,
14698
+ designContract: packet.designContract,
14699
+ canvasPlanRequest: packet.canvasPlanRequest,
14700
+ designAgentHandoff: packet.followthrough,
14701
+ generationPlan: packet.generationPlan,
14702
+ implementationPlan: packet.implementationPlan,
14703
+ designMarkdown: packet.designMarkdown,
14704
+ implementationPlanMarkdown: packet.implementationPlanMarkdown,
14705
+ prototypeGuidanceMarkdown: packet.prototypeGuidanceMarkdown,
14706
+ evidence: packet.evidence,
14707
+ meta
14708
+ });
14709
+ const bundle = await createArtifactBundle({
14710
+ namespace: "inspiredesign",
14711
+ outputDir: workflowInput.outputDir,
14712
+ ttlHours: workflowInput.ttlHours,
14713
+ files: rendered.files
14714
+ });
14715
+ if (workflowInput.mode === "path") {
14716
+ return {
14717
+ ...rendered.response,
14718
+ path: bundle.basePath,
14719
+ meta: {
14720
+ ...meta,
14721
+ artifact_manifest: bundle.manifest
14722
+ }
14723
+ };
14724
+ }
14725
+ return {
14726
+ ...rendered.response,
14727
+ artifact_path: bundle.basePath,
12756
14728
  meta: {
12757
14729
  ...meta,
12758
14730
  artifact_manifest: bundle.manifest
@@ -13101,32 +15073,35 @@ var runProductVideoWorkflow = async (runtime, input, options = {}) => {
13101
15073
  const cookieDiagnostics = summarizeCookieDiagnostics(details.failures, details.records);
13102
15074
  const antiBotPressure = summarizeAntiBotPressure(details.failures);
13103
15075
  const primaryIssue = summarizePrimaryProviderIssue(details.failures);
15076
+ const handoff = buildProductVideoSuccessHandoff();
15077
+ const meta = withFollowthroughMeta({
15078
+ alerts: buildWorkflowAlerts(runtime, details.failures),
15079
+ failures: details.failures,
15080
+ ...primaryIssue ? { primaryConstraintSummary: primaryIssue.summary } : {},
15081
+ reasonCodeDistribution,
15082
+ transcript_strategy_failures: transcriptStrategyFailures,
15083
+ transcriptStrategyFailures,
15084
+ transcript_strategy_detail_failures: transcriptStrategyFailures,
15085
+ transcriptStrategyDetailFailures: transcriptStrategyFailures,
15086
+ transcript_strategy_detail_distribution: transcriptStrategyDetailDistribution,
15087
+ transcriptStrategyDetailDistribution,
15088
+ transcript_durability: transcriptDurability,
15089
+ transcriptDurability,
15090
+ cookie_diagnostics: cookieDiagnostics,
15091
+ cookieDiagnostics,
15092
+ anti_bot_pressure: antiBotPressure,
15093
+ antiBotPressure,
15094
+ artifact_manifest: bundle.manifest
15095
+ }, handoff);
13104
15096
  return {
15097
+ ...handoff,
13105
15098
  path: bundle.basePath,
13106
15099
  manifest: manifestPayload,
13107
15100
  product: productPayload,
13108
15101
  pricing,
13109
15102
  screenshots: screenshotPaths,
13110
15103
  images: imagePaths,
13111
- meta: {
13112
- alerts: buildWorkflowAlerts(runtime, details.failures),
13113
- failures: details.failures,
13114
- ...primaryIssue ? { primaryConstraintSummary: primaryIssue.summary } : {},
13115
- reasonCodeDistribution,
13116
- transcript_strategy_failures: transcriptStrategyFailures,
13117
- transcriptStrategyFailures,
13118
- transcript_strategy_detail_failures: transcriptStrategyFailures,
13119
- transcriptStrategyDetailFailures: transcriptStrategyFailures,
13120
- transcript_strategy_detail_distribution: transcriptStrategyDetailDistribution,
13121
- transcriptStrategyDetailDistribution,
13122
- transcript_durability: transcriptDurability,
13123
- transcriptDurability,
13124
- cookie_diagnostics: cookieDiagnostics,
13125
- cookieDiagnostics,
13126
- anti_bot_pressure: antiBotPressure,
13127
- antiBotPressure,
13128
- artifact_manifest: bundle.manifest
13129
- }
15104
+ meta
13130
15105
  };
13131
15106
  };
13132
15107
 
@@ -13140,6 +15115,7 @@ var DEFAULT_PROVIDER_SUSPENDED_INTENT_KIND = {
13140
15115
  var WORKFLOW_KIND_BY_SUSPENDED_INTENT_KIND2 = {
13141
15116
  "workflow.research": "research",
13142
15117
  "workflow.shopping": "shopping",
15118
+ "workflow.inspiredesign": "inspiredesign",
13143
15119
  "workflow.product_video": "product_video"
13144
15120
  };
13145
15121
  var EXTENSION_FIRST_SOCIAL_FALLBACK_PLATFORMS = /* @__PURE__ */ new Set(["x", "reddit", "bluesky", "linkedin"]);
@@ -14586,6 +16562,14 @@ var ProviderRuntime = class {
14586
16562
  input
14587
16563
  )
14588
16564
  );
16565
+ case "workflow.inspiredesign":
16566
+ return runInspiredesignWorkflow(
16567
+ this,
16568
+ unwrapWorkflowResumeEnvelope(
16569
+ WORKFLOW_KIND_BY_SUSPENDED_INTENT_KIND2[intent.kind],
16570
+ input
16571
+ )
16572
+ );
14589
16573
  case "workflow.product_video":
14590
16574
  return runProductVideoWorkflow(
14591
16575
  this,
@@ -14743,25 +16727,38 @@ var ProviderRuntime = class {
14743
16727
  }
14744
16728
  async withTimeout(timeoutMs, task) {
14745
16729
  const controller = new AbortController();
14746
- const timeoutId = setTimeout(() => {
14747
- controller.abort("timeout");
14748
- }, timeoutMs);
16730
+ const createTimeoutError = (cause) => {
16731
+ return new ProviderRuntimeError("timeout", `Provider request timed out after ${timeoutMs}ms`, {
16732
+ retryable: true,
16733
+ ...cause !== void 0 ? { cause } : {}
16734
+ });
16735
+ };
16736
+ const taskPromise = Promise.resolve().then(() => task(controller.signal));
16737
+ const timeoutPromise = new Promise((_, reject) => {
16738
+ const timeoutId = setTimeout(() => {
16739
+ controller.abort("timeout");
16740
+ reject(createTimeoutError());
16741
+ }, timeoutMs);
16742
+ controller.signal.addEventListener("abort", () => clearTimeout(timeoutId), { once: true });
16743
+ });
14749
16744
  try {
14750
- const result = await task(controller.signal);
14751
- if (controller.signal.aborted) {
14752
- throw new ProviderRuntimeError("timeout", `Provider request timed out after ${timeoutMs}ms`);
14753
- }
14754
- return result;
16745
+ return await Promise.race([
16746
+ taskPromise.catch((error) => {
16747
+ if (controller.signal.aborted) {
16748
+ throw createTimeoutError(error);
16749
+ }
16750
+ throw error;
16751
+ }),
16752
+ timeoutPromise
16753
+ ]);
14755
16754
  } catch (error) {
16755
+ if (isProviderRuntimeError(error) && error.code === "timeout") {
16756
+ throw error;
16757
+ }
14756
16758
  if (controller.signal.aborted) {
14757
- throw new ProviderRuntimeError("timeout", `Provider request timed out after ${timeoutMs}ms`, {
14758
- retryable: true,
14759
- cause: error
14760
- });
16759
+ throw createTimeoutError(error);
14761
16760
  }
14762
16761
  throw error;
14763
- } finally {
14764
- clearTimeout(timeoutId);
14765
16762
  }
14766
16763
  }
14767
16764
  };
@@ -15189,6 +17186,7 @@ var mergeBudgets = (base, partial) => {
15189
17186
  };
15190
17187
 
15191
17188
  export {
17189
+ redactSensitive,
15192
17190
  createRequestId,
15193
17191
  stderrSink,
15194
17192
  setDefaultLogSink,
@@ -15204,6 +17202,7 @@ export {
15204
17202
  CHALLENGE_AUTOMATION_MODES,
15205
17203
  isChallengeAutomationMode,
15206
17204
  resolveChallengeAutomationPolicy,
17205
+ inspectChallengePlanFromRuntime,
15207
17206
  ChallengeOrchestrator,
15208
17207
  summarizePrimaryProviderIssue,
15209
17208
  createTraceContext,
@@ -15242,15 +17241,19 @@ export {
15242
17241
  filterByTimebox,
15243
17242
  toResearchRecord,
15244
17243
  enrichResearchRecords,
17244
+ INSPIREDESIGN_HANDOFF_COMMANDS,
17245
+ INSPIREDESIGN_HANDOFF_GUIDANCE,
15245
17246
  renderResearch,
15246
17247
  renderShopping,
17248
+ renderInspiredesign,
15247
17249
  workflowTestUtils,
15248
17250
  runResearchWorkflow,
15249
17251
  runShoppingWorkflow,
17252
+ runInspiredesignWorkflow,
15250
17253
  runProductVideoWorkflow,
15251
17254
  DEFAULT_PROVIDER_BUDGETS,
15252
17255
  ProviderRuntime,
15253
17256
  createProviderRuntime,
15254
17257
  createDefaultRuntime
15255
17258
  };
15256
- //# sourceMappingURL=chunk-5FZQJRBQ.js.map
17259
+ //# sourceMappingURL=chunk-ZE2E7ZGH.js.map