@spotpatch/runtime 1.8.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,27 @@
1
+ import {
2
+ createDataFlowRuntime,
3
+ getDataFlowRuntime,
4
+ installDataFlowPrelude,
5
+ mergeComponentDataFlowReport,
6
+ mergePageDataFlowReport
7
+ } from "./chunk-QY5T4DPA.js";
8
+ import {
9
+ getDataFlowExtension
10
+ } from "./chunk-NII5OBAN.js";
11
+ import {
12
+ getExternalHandoffExtension
13
+ } from "./chunk-XBD55BCF.js";
14
+ import {
15
+ UI_MARKER_ATTRIBUTE,
16
+ UI_Z_INDEX,
17
+ createButton,
18
+ createMarkedElement
19
+ } from "./chunk-7ES63LA7.js";
20
+
1
21
  // src/controller/runtime-controller.ts
2
22
  import { createReact18Adapter } from "@spotpatch/react-adapter";
3
23
  import {
24
+ DATA_FLOW_SCHEMA_VERSION as DATA_FLOW_SCHEMA_VERSION2,
4
25
  MAX_ANNOTATION_INSTRUCTION_CHARACTERS as MAX_ANNOTATION_INSTRUCTION_CHARACTERS3
5
26
  } from "@spotpatch/shared";
6
27
 
@@ -76,6 +97,7 @@ function createAnnotation(input) {
76
97
 
77
98
  // src/api/runtime-api.ts
78
99
  import {
100
+ DATA_FLOW_SCHEMA_VERSION,
79
101
  SPOTPATCH_ENDPOINTS,
80
102
  SPOTPATCH_TOKEN_HEADER,
81
103
  getAgentJobEndpoint
@@ -337,15 +359,36 @@ function parseJson(text2) {
337
359
  throw new RuntimeApiError();
338
360
  }
339
361
  }
340
- async function readJsonEnvelope(response) {
362
+ async function readJsonEnvelope(response, maximumBytes = MAX_JSON_RESPONSE_BYTES) {
341
363
  const payload = parseJson(
342
- await readBoundedText(response, MAX_JSON_RESPONSE_BYTES)
364
+ await readBoundedText(response, maximumBytes)
343
365
  );
344
366
  if (!response.ok) {
345
367
  throw new RuntimeApiError(readFailureCode(payload));
346
368
  }
347
369
  return readSuccessData(payload);
348
370
  }
371
+ function isStringArray(value) {
372
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
373
+ }
374
+ function isReportCompleteness(value) {
375
+ return isRecord2(value) && typeof value.complete === "boolean" && typeof value.visitedModules === "number" && typeof value.visitedCallsites === "number" && typeof value.frontierCount === "number";
376
+ }
377
+ function isDataFlowCapability(value) {
378
+ return isRecord2(value) && typeof value.enabled === "boolean" && (value.staticAnalysis === "available" || value.staticAnalysis === "partial" || value.staticAnalysis === "unavailable") && value.runtimeObservation === "dispatch-only" && value.responseShape === "consumed-fields-only" && value.aiAssistance === "disabled" && Array.isArray(value.reasons);
379
+ }
380
+ function isDataFlowDependency(value) {
381
+ return isRecord2(value) && typeof value.id === "string" && typeof value.kind === "string" && typeof value.direction === "string" && typeof value.execution === "string" && typeof value.proof === "string" && typeof value.association === "string" && Array.isArray(value.parameters) && isRecord2(value.response) && isStringArray(value.response.consumedFields) && isStringArray(value.evidenceIds) && isStringArray(value.observationIds);
382
+ }
383
+ function isDataFlowReportBase(value) {
384
+ return isRecord2(value) && value.schemaVersion === DATA_FLOW_SCHEMA_VERSION && typeof value.reportId === "string" && isRecord2(value.baseline) && typeof value.baseline.registryEpoch === "string" && typeof value.baseline.analyzerVersion === "string" && isStringArray(value.baseline.analyzedSourceVersions) && isDataFlowCapability(value.capability) && Array.isArray(value.dependencies) && value.dependencies.every(isDataFlowDependency) && Array.isArray(value.evidence) && Array.isArray(value.diagnostics) && isReportCompleteness(value.completeness);
385
+ }
386
+ function isComponentDataFlowReport(value) {
387
+ return isDataFlowReportBase(value) && isRecord2(value) && isRecord2(value.component) && isRecord2(value.component.source) && typeof value.component.source.fileId === "string" && typeof value.component.source.sourceVersion === "string";
388
+ }
389
+ function isPageDataFlowReport(value) {
390
+ return isDataFlowReportBase(value);
391
+ }
349
392
  function parseCapability(value, expected) {
350
393
  if (!isAgentCapabilitySnapshot(value) || value.providerProfileId !== expected.providerProfileId || value.modelProfileId !== expected.modelProfileId) {
351
394
  throw new RuntimeApiError();
@@ -397,7 +440,7 @@ function createRuntimeApi(options) {
397
440
  }
398
441
  pendingRequests.clear();
399
442
  }
400
- async function requestJson(endpoint, method, body) {
443
+ async function requestJson(endpoint, method, body, maximumResponseBytes = MAX_JSON_RESPONSE_BYTES) {
401
444
  const abortController = new AbortController();
402
445
  pendingRequests.add(abortController);
403
446
  try {
@@ -410,7 +453,7 @@ function createRuntimeApi(options) {
410
453
  ...body === void 0 ? {} : { body: JSON.stringify(body) },
411
454
  signal: abortController.signal
412
455
  });
413
- return await readJsonEnvelope(response);
456
+ return await readJsonEnvelope(response, maximumResponseBytes);
414
457
  } finally {
415
458
  pendingRequests.delete(abortController);
416
459
  }
@@ -504,6 +547,30 @@ function createRuntimeApi(options) {
504
547
  }
505
548
  return Object.freeze({ ...data });
506
549
  },
550
+ async componentDataFlowReport(request) {
551
+ const data = await requestJson(
552
+ SPOTPATCH_ENDPOINTS.dataFlowComponentReport,
553
+ "POST",
554
+ request,
555
+ options.dataFlowReportMaxBytes
556
+ );
557
+ if (!isComponentDataFlowReport(data)) {
558
+ throw new RuntimeApiError();
559
+ }
560
+ return deepFreeze(data);
561
+ },
562
+ async pageDataFlowReport(request) {
563
+ const data = await requestJson(
564
+ SPOTPATCH_ENDPOINTS.dataFlowPageReport,
565
+ "POST",
566
+ request,
567
+ options.dataFlowReportMaxBytes
568
+ );
569
+ if (!isPageDataFlowReport(data)) {
570
+ throw new RuntimeApiError();
571
+ }
572
+ return deepFreeze(data);
573
+ },
507
574
  async openEditor(request) {
508
575
  const data = await requestJson(SPOTPATCH_ENDPOINTS.openEditor, "POST", request);
509
576
  if (!isRecord2(data) || data.editor !== "auto" && data.editor !== "vscode" && data.editor !== "cursor") {
@@ -1071,13 +1138,6 @@ function getVisibleElementRect(element2, view) {
1071
1138
  return rect.width > 0 && rect.height > 0 ? rect : void 0;
1072
1139
  }
1073
1140
 
1074
- // src/ui/ui-constants.ts
1075
- var UI_MARKER_ATTRIBUTE = "data-spotpatch-ui";
1076
- var UI_Z_INDEX = Object.freeze({
1077
- highlight: 2147483646,
1078
- controls: 2147483647
1079
- });
1080
-
1081
1141
  // src/picker/hit-test.ts
1082
1142
  function isInsideSpotPatchUI(element2) {
1083
1143
  if (element2.closest(`[${UI_MARKER_ATTRIBUTE}]`) !== null) {
@@ -1683,20 +1743,6 @@ import {
1683
1743
  SPOTPATCH_REPOSITORY_URL
1684
1744
  } from "@spotpatch/shared";
1685
1745
 
1686
- // src/ui/dom.ts
1687
- function createMarkedElement(document, tagName) {
1688
- const element2 = document.createElement(tagName);
1689
- element2.setAttribute(UI_MARKER_ATTRIBUTE, "");
1690
- return element2;
1691
- }
1692
- function createButton(document, label, className = "") {
1693
- const button = createMarkedElement(document, "button");
1694
- button.type = "button";
1695
- button.className = className;
1696
- button.textContent = label;
1697
- return button;
1698
- }
1699
-
1700
1746
  // src/ui/agent-panel.ts
1701
1747
  var AGENT_PANEL_STYLES = `
1702
1748
  .spotpatch-agent {
@@ -2552,6 +2598,8 @@ var STATUS_ZH = Object.freeze({
2552
2598
  reverted: "\u5DF2\u64A4\u9500",
2553
2599
  failed: "\u5931\u8D25"
2554
2600
  });
2601
+ var EXTERNAL_HANDOFF_ERROR_EN = "The external Agent handoff request failed.";
2602
+ var EXTERNAL_HANDOFF_ERROR_ZH = "\u5916\u90E8 Agent \u4EA4\u63A5\u8BF7\u6C42\u5931\u8D25\u3002";
2555
2603
  var ERROR_MESSAGES_EN = Object.freeze({
2556
2604
  [ERROR_CODES2.INVALID_REQUEST]: "The Agent request was rejected as invalid.",
2557
2605
  [ERROR_CODES2.INVALID_TOKEN]: "The local SpotPatch session expired.",
@@ -2560,6 +2608,9 @@ var ERROR_MESSAGES_EN = Object.freeze({
2560
2608
  [ERROR_CODES2.SOURCE_OUTSIDE_ROOT]: "The selected source is outside the project.",
2561
2609
  [ERROR_CODES2.SOURCE_TOO_LARGE]: "The selected source exceeds the safety limit.",
2562
2610
  [ERROR_CODES2.EDITOR_OPEN_FAILED]: "The editor request failed.",
2611
+ [ERROR_CODES2.DATA_FLOW_DISABLED]: "Component data-flow analysis is disabled.",
2612
+ [ERROR_CODES2.DATA_FLOW_SOURCE_STALE]: "The selected source changed. Select the component again.",
2613
+ [ERROR_CODES2.DATA_FLOW_ANALYSIS_CANCELLED]: "Component data-flow analysis was cancelled.",
2563
2614
  [ERROR_CODES2.AI_DISABLED]: "AI execution is disabled in Vite configuration.",
2564
2615
  [ERROR_CODES2.PROVIDER_NOT_CONFIGURED]: "The provider Key environment variable is missing on the Vite process.",
2565
2616
  [ERROR_CODES2.PROVIDER_AUTH_FAILED]: "The provider rejected authentication. Check the server-side Key.",
@@ -2570,6 +2621,24 @@ var ERROR_MESSAGES_EN = Object.freeze({
2570
2621
  [ERROR_CODES2.AGENT_BUSY]: "Another write Agent job is still active.",
2571
2622
  [ERROR_CODES2.AGENT_LIMIT_EXCEEDED]: "The Agent stopped at a configured time, turn, output, or size limit.",
2572
2623
  [ERROR_CODES2.AGENT_CANCELLED]: "The Agent job was cancelled.",
2624
+ [ERROR_CODES2.EXTERNAL_HANDOFF_DISABLED]: EXTERNAL_HANDOFF_ERROR_EN,
2625
+ [ERROR_CODES2.EXTERNAL_HANDOFF_UNAVAILABLE]: EXTERNAL_HANDOFF_ERROR_EN,
2626
+ [ERROR_CODES2.HANDOFF_VALIDATION_FAILED]: EXTERNAL_HANDOFF_ERROR_EN,
2627
+ [ERROR_CODES2.HANDOFF_SOURCE_STALE]: EXTERNAL_HANDOFF_ERROR_EN,
2628
+ [ERROR_CODES2.HANDOFF_NOT_FOUND]: EXTERNAL_HANDOFF_ERROR_EN,
2629
+ [ERROR_CODES2.HANDOFF_EXPIRED]: EXTERNAL_HANDOFF_ERROR_EN,
2630
+ [ERROR_CODES2.HANDOFF_CURSOR_INVALID]: EXTERNAL_HANDOFF_ERROR_EN,
2631
+ [ERROR_CODES2.HANDOFF_RESPONSE_TOO_LARGE]: EXTERNAL_HANDOFF_ERROR_EN,
2632
+ [ERROR_CODES2.BRIDGE_UNAUTHORIZED]: EXTERNAL_HANDOFF_ERROR_EN,
2633
+ [ERROR_CODES2.BRIDGE_PROTOCOL_MISMATCH]: EXTERNAL_HANDOFF_ERROR_EN,
2634
+ [ERROR_CODES2.BRIDGE_BUSY]: EXTERNAL_HANDOFF_ERROR_EN,
2635
+ [ERROR_CODES2.EXTERNAL_AGENT_BUSY]: EXTERNAL_HANDOFF_ERROR_EN,
2636
+ [ERROR_CODES2.ACTIVE_ADAPTER_CONFLICT]: EXTERNAL_HANDOFF_ERROR_EN,
2637
+ [ERROR_CODES2.ACTIVE_ADAPTER_LEASE_INVALID]: EXTERNAL_HANDOFF_ERROR_EN,
2638
+ [ERROR_CODES2.ACTIVE_DISPATCH_INVALID]: EXTERNAL_HANDOFF_ERROR_EN,
2639
+ [ERROR_CODES2.SESSION_NOT_FOUND]: EXTERNAL_HANDOFF_ERROR_EN,
2640
+ [ERROR_CODES2.SESSION_AMBIGUOUS]: EXTERNAL_HANDOFF_ERROR_EN,
2641
+ [ERROR_CODES2.SESSION_CLOSED]: EXTERNAL_HANDOFF_ERROR_EN,
2573
2642
  [ERROR_CODES2.WORKTREE_DIRTY]: "Confirm inclusion of local changes before running AI.",
2574
2643
  [ERROR_CODES2.WORKTREE_NOT_REPOSITORY]: "Vite root must be an initialized Git repository root.",
2575
2644
  [ERROR_CODES2.WORKTREE_OPERATION_IN_PROGRESS]: "Finish the active merge, rebase, cherry-pick, or revert.",
@@ -2595,6 +2664,9 @@ var ERROR_MESSAGES_ZH = Object.freeze({
2595
2664
  [ERROR_CODES2.SOURCE_OUTSIDE_ROOT]: "\u9009\u4E2D\u6E90\u7801\u4F4D\u4E8E\u9879\u76EE\u6839\u76EE\u5F55\u4E4B\u5916\u3002",
2596
2665
  [ERROR_CODES2.SOURCE_TOO_LARGE]: "\u9009\u4E2D\u6E90\u7801\u8D85\u8FC7\u5B89\u5168\u5927\u5C0F\u9650\u5236\u3002",
2597
2666
  [ERROR_CODES2.EDITOR_OPEN_FAILED]: "\u7F16\u8F91\u5668\u6253\u5F00\u8BF7\u6C42\u5931\u8D25\u3002",
2667
+ [ERROR_CODES2.DATA_FLOW_DISABLED]: "\u7EC4\u4EF6\u6570\u636E\u94FE\u8DEF\u5206\u6790\u672A\u542F\u7528\u3002",
2668
+ [ERROR_CODES2.DATA_FLOW_SOURCE_STALE]: "\u9009\u4E2D\u6E90\u7801\u5DF2\u7ECF\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u7EC4\u4EF6\u3002",
2669
+ [ERROR_CODES2.DATA_FLOW_ANALYSIS_CANCELLED]: "\u7EC4\u4EF6\u6570\u636E\u94FE\u8DEF\u5206\u6790\u5DF2\u53D6\u6D88\u3002",
2598
2670
  [ERROR_CODES2.AI_DISABLED]: "Vite \u914D\u7F6E\u672A\u542F\u7528 AI \u6267\u884C\u3002",
2599
2671
  [ERROR_CODES2.PROVIDER_NOT_CONFIGURED]: "\u542F\u52A8 Vite \u7684\u8FDB\u7A0B\u4E2D\u7F3A\u5C11\u6A21\u578B\u670D\u52A1 Key \u73AF\u5883\u53D8\u91CF\u3002",
2600
2672
  [ERROR_CODES2.PROVIDER_AUTH_FAILED]: "\u6A21\u578B\u670D\u52A1\u9274\u6743\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u670D\u52A1\u7AEF Key\u3002",
@@ -2605,6 +2677,24 @@ var ERROR_MESSAGES_ZH = Object.freeze({
2605
2677
  [ERROR_CODES2.AGENT_BUSY]: "\u5F53\u524D\u9879\u76EE\u5DF2\u6709\u4E00\u4E2A\u5199\u5165\u4EFB\u52A1\u6B63\u5728\u8FD0\u884C\u3002",
2606
2678
  [ERROR_CODES2.AGENT_LIMIT_EXCEEDED]: "Agent \u8FBE\u5230\u65F6\u95F4\u3001\u8F6E\u6B21\u3001\u8F93\u51FA\u6216\u53D8\u66F4\u89C4\u6A21\u9650\u5236\u3002",
2607
2679
  [ERROR_CODES2.AGENT_CANCELLED]: "Agent \u4EFB\u52A1\u5DF2\u53D6\u6D88\u3002",
2680
+ [ERROR_CODES2.EXTERNAL_HANDOFF_DISABLED]: EXTERNAL_HANDOFF_ERROR_ZH,
2681
+ [ERROR_CODES2.EXTERNAL_HANDOFF_UNAVAILABLE]: EXTERNAL_HANDOFF_ERROR_ZH,
2682
+ [ERROR_CODES2.HANDOFF_VALIDATION_FAILED]: EXTERNAL_HANDOFF_ERROR_ZH,
2683
+ [ERROR_CODES2.HANDOFF_SOURCE_STALE]: EXTERNAL_HANDOFF_ERROR_ZH,
2684
+ [ERROR_CODES2.HANDOFF_NOT_FOUND]: EXTERNAL_HANDOFF_ERROR_ZH,
2685
+ [ERROR_CODES2.HANDOFF_EXPIRED]: EXTERNAL_HANDOFF_ERROR_ZH,
2686
+ [ERROR_CODES2.HANDOFF_CURSOR_INVALID]: EXTERNAL_HANDOFF_ERROR_ZH,
2687
+ [ERROR_CODES2.HANDOFF_RESPONSE_TOO_LARGE]: EXTERNAL_HANDOFF_ERROR_ZH,
2688
+ [ERROR_CODES2.BRIDGE_UNAUTHORIZED]: EXTERNAL_HANDOFF_ERROR_ZH,
2689
+ [ERROR_CODES2.BRIDGE_PROTOCOL_MISMATCH]: EXTERNAL_HANDOFF_ERROR_ZH,
2690
+ [ERROR_CODES2.BRIDGE_BUSY]: EXTERNAL_HANDOFF_ERROR_ZH,
2691
+ [ERROR_CODES2.EXTERNAL_AGENT_BUSY]: EXTERNAL_HANDOFF_ERROR_ZH,
2692
+ [ERROR_CODES2.ACTIVE_ADAPTER_CONFLICT]: EXTERNAL_HANDOFF_ERROR_ZH,
2693
+ [ERROR_CODES2.ACTIVE_ADAPTER_LEASE_INVALID]: EXTERNAL_HANDOFF_ERROR_ZH,
2694
+ [ERROR_CODES2.ACTIVE_DISPATCH_INVALID]: EXTERNAL_HANDOFF_ERROR_ZH,
2695
+ [ERROR_CODES2.SESSION_NOT_FOUND]: EXTERNAL_HANDOFF_ERROR_ZH,
2696
+ [ERROR_CODES2.SESSION_AMBIGUOUS]: EXTERNAL_HANDOFF_ERROR_ZH,
2697
+ [ERROR_CODES2.SESSION_CLOSED]: EXTERNAL_HANDOFF_ERROR_ZH,
2608
2698
  [ERROR_CODES2.WORKTREE_DIRTY]: "\u8FD0\u884C AI \u524D\uFF0C\u8BF7\u660E\u786E\u540C\u610F\u5C06\u5F53\u524D\u672C\u5730\u4FEE\u6539\u7EB3\u5165\u9694\u79BB\u57FA\u7EBF\u3002",
2609
2699
  [ERROR_CODES2.WORKTREE_NOT_REPOSITORY]: "Vite \u6839\u76EE\u5F55\u4E0D\u662F\u5DF2\u521D\u59CB\u5316 Git \u4ED3\u5E93\u7684\u9876\u5C42\u76EE\u5F55\u3002",
2610
2700
  [ERROR_CODES2.WORKTREE_OPERATION_IN_PROGRESS]: "\u8BF7\u5148\u5B8C\u6210\u5F53\u524D merge\u3001rebase\u3001cherry-pick \u6216 revert\uFF0C\u518D\u8FD0\u884C AI\u3002",
@@ -3029,6 +3119,15 @@ var DIALOG_FALLBACK_HEIGHT = Object.freeze({
3029
3119
  previewing: 560,
3030
3120
  selected: DIALOG_MAX_HEIGHT
3031
3121
  });
3122
+ function resolveStyleNonce(document) {
3123
+ const nonces = new Set(
3124
+ [...document.querySelectorAll("script[nonce]")].map((script) => script.nonce?.trim() ?? "").filter(Boolean)
3125
+ );
3126
+ if (nonces.size !== 1) {
3127
+ return void 0;
3128
+ }
3129
+ return nonces.values().next().value;
3130
+ }
3032
3131
  function createStyles(document) {
3033
3132
  const style = document.createElement("style");
3034
3133
  style.textContent = `
@@ -3739,7 +3838,18 @@ function summaryLine(summary, prefix) {
3739
3838
  const line = summary.split("\n").find((candidate) => candidate.startsWith(`${prefix}: `));
3740
3839
  return line?.slice(prefix.length + 2).trim();
3741
3840
  }
3742
- function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: false }), localePreference = "auto") {
3841
+ function createUnavailableDataFlowPanel(document, changesRoot, diagnosticsRoot) {
3842
+ changesRoot.append(diagnosticsRoot);
3843
+ return Object.freeze({
3844
+ root: changesRoot,
3845
+ refreshButton: createButton(document, ""),
3846
+ styles: document.createElement("style"),
3847
+ dispose: () => void 0,
3848
+ render: () => void 0,
3849
+ resetView: () => void 0
3850
+ });
3851
+ }
3852
+ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: false }), localePreference = "auto", dataFlowEnabled = false, externalAgentEnabled = false, framework = "vite", sessionId = "") {
3743
3853
  const localizer = createUiLocalizer(document, localePreference);
3744
3854
  let messages = localizer.messages();
3745
3855
  const host = document.createElement("spotpatch-root");
@@ -3855,7 +3965,29 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3855
3965
  summary.className = "spotpatch-summary";
3856
3966
  diagnostics.append(diagnosticsLabel, summary);
3857
3967
  const agentPanel = createAgentPanel(document, ai, localizer);
3858
- selectionPanel.append(targetsPanel, diagnostics, agentPanel.root);
3968
+ const changesPanel = createMarkedElement(document, "div");
3969
+ const externalHandoffPanel = externalAgentEnabled ? getExternalHandoffExtension()?.createPanel(
3970
+ document,
3971
+ framework,
3972
+ localizer.locale,
3973
+ sessionId,
3974
+ localizer.subscribe,
3975
+ placeDialog
3976
+ ) : void 0;
3977
+ changesPanel.append(
3978
+ targetsPanel,
3979
+ agentPanel.root,
3980
+ ...externalHandoffPanel === void 0 ? [] : [externalHandoffPanel.root]
3981
+ );
3982
+ const dataFlowPanel = getDataFlowExtension()?.createPanel(
3983
+ document,
3984
+ dataFlowEnabled,
3985
+ localizer.locale,
3986
+ changesPanel,
3987
+ diagnostics,
3988
+ placeDialog
3989
+ ) ?? createUnavailableDataFlowPanel(document, changesPanel, diagnostics);
3990
+ selectionPanel.append(dataFlowPanel.root);
3859
3991
  const previewPanel = createMarkedElement(document, "div");
3860
3992
  previewPanel.className = "spotpatch-preview-panel";
3861
3993
  previewPanel.hidden = true;
@@ -3898,6 +4030,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3898
4030
  agentPanel.testButton,
3899
4031
  addTargetButton,
3900
4032
  agentPanel.runButton,
4033
+ ...externalHandoffPanel === void 0 ? [] : [externalHandoffPanel.sendButton],
3901
4034
  previewButton,
3902
4035
  agentPanel.cancelButton,
3903
4036
  agentPanel.applyButton,
@@ -3913,8 +4046,19 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3913
4046
  liveRegion.className = "spotpatch-live";
3914
4047
  liveRegion.setAttribute("aria-live", "polite");
3915
4048
  liveRegion.setAttribute("aria-atomic", "true");
3916
- shadowRoot.append(
4049
+ const styles2 = [
3917
4050
  createStyles(document),
4051
+ dataFlowPanel.styles,
4052
+ ...externalHandoffPanel === void 0 ? [] : [externalHandoffPanel.styles]
4053
+ ];
4054
+ const styleNonce = resolveStyleNonce(document);
4055
+ if (styleNonce !== void 0) {
4056
+ for (const style of styles2) {
4057
+ style.nonce = styleNonce;
4058
+ }
4059
+ }
4060
+ shadowRoot.append(
4061
+ ...styles2,
3918
4062
  selectionHighlights,
3919
4063
  highlight,
3920
4064
  dialog,
@@ -3932,6 +4076,13 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3932
4076
  let currentTargets = [];
3933
4077
  let currentMaximum = 0;
3934
4078
  let currentEditorFeedbackState = "idle";
4079
+ let currentDataFlowState = Object.freeze({
4080
+ component: Object.freeze({
4081
+ status: dataFlowEnabled ? "idle" : "disabled"
4082
+ }),
4083
+ page: Object.freeze({ status: dataFlowEnabled ? "idle" : "disabled" }),
4084
+ observationCount: 0
4085
+ });
3935
4086
  function renderEditorStatus(state) {
3936
4087
  currentEditorFeedbackState = state;
3937
4088
  editorFeedback.dataset.state = state;
@@ -4189,6 +4340,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4189
4340
  openEditorButton.disabled = !canOpenEditor;
4190
4341
  previewButton.disabled = !canPreview;
4191
4342
  agentPanel.setContextReady(canPreview);
4343
+ externalHandoffPanel?.setContextReady(canPreview);
4192
4344
  updateContextOverview(summaryText);
4193
4345
  placeDialog();
4194
4346
  }
@@ -4205,6 +4357,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4205
4357
  previewButton.hidden = !selected;
4206
4358
  secondaryActions.hidden = !selected;
4207
4359
  agentPanel.setSelectionVisible(selected);
4360
+ externalHandoffPanel?.setSelectionVisible(selected);
4208
4361
  copyButton.hidden = !previewing;
4209
4362
  backButton.hidden = !previewing;
4210
4363
  title.textContent = previewing ? messages.dialog.previewTitle : messages.dialog.editTitle;
@@ -4233,6 +4386,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4233
4386
  previewButton.textContent = messages.actions.preview;
4234
4387
  copyButton.textContent = messages.actions.copy;
4235
4388
  backButton.textContent = messages.actions.back;
4389
+ dataFlowPanel.render(currentDataFlowState);
4236
4390
  triggerButton.title = messages.trigger.title(shortcut);
4237
4391
  triggerButton.textContent = currentStatus === "inspecting" ? messages.trigger.stop : messages.trigger.select;
4238
4392
  renderPanelStatus(currentStatus);
@@ -4271,6 +4425,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4271
4425
  repositoryLink,
4272
4426
  previewButton,
4273
4427
  copyButton,
4428
+ dataFlowRefreshButton: dataFlowPanel.refreshButton,
4274
4429
  backButton,
4275
4430
  closeButton,
4276
4431
  agentProviderSelect: agentPanel.providerSelect,
@@ -4284,6 +4439,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4284
4439
  agentApplyButton: agentPanel.applyButton,
4285
4440
  agentRevertButton: agentPanel.revertButton,
4286
4441
  agentResetButton: agentPanel.resetButton,
4442
+ ...externalHandoffPanel === void 0 ? {} : { externalHandoffPanel },
4287
4443
  renderStatus(status) {
4288
4444
  const inspecting = status === "inspecting";
4289
4445
  triggerButton.setAttribute("aria-pressed", String(inspecting));
@@ -4291,6 +4447,11 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4291
4447
  renderPanelStatus(status);
4292
4448
  },
4293
4449
  renderEditorStatus,
4450
+ renderDataFlow(state) {
4451
+ currentDataFlowState = state;
4452
+ dataFlowPanel.render(state);
4453
+ placeDialog();
4454
+ },
4294
4455
  showHighlight(rect, label) {
4295
4456
  currentRect = rect;
4296
4457
  highlight.hidden = false;
@@ -4327,6 +4488,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4327
4488
  currentCanPreview = enabled;
4328
4489
  previewButton.disabled = !enabled;
4329
4490
  agentPanel.setContextReady(enabled);
4491
+ externalHandoffPanel?.setContextReady(enabled);
4330
4492
  },
4331
4493
  hideSelection() {
4332
4494
  dialog.hidden = true;
@@ -4351,8 +4513,11 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4351
4513
  currentCanPreview = false;
4352
4514
  agentPanel.setContextReady(false);
4353
4515
  agentPanel.setSelectionVisible(false);
4516
+ externalHandoffPanel?.setContextReady(false);
4517
+ externalHandoffPanel?.setSelectionVisible(false);
4354
4518
  agentPanel.setEditingEnabled(true);
4355
4519
  agentPanel.resetJob();
4520
+ dataFlowPanel.resetView();
4356
4521
  },
4357
4522
  hideSelectionTemporarily() {
4358
4523
  dialog.hidden = true;
@@ -4382,6 +4547,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4382
4547
  });
4383
4548
  openEditorButton.disabled = !currentCanOpenEditor;
4384
4549
  previewButton.disabled = !enabled || !currentCanPreview;
4550
+ externalHandoffPanel?.setContextReady(enabled && currentCanPreview);
4385
4551
  agentPanel.setEditingEnabled(enabled);
4386
4552
  placeDialog();
4387
4553
  },
@@ -4421,6 +4587,8 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4421
4587
  diagnostics.removeEventListener("toggle", placeDialog);
4422
4588
  localeButton.removeEventListener("click", localizer.toggle);
4423
4589
  unsubscribeLocale();
4590
+ dataFlowPanel.dispose();
4591
+ externalHandoffPanel?.dispose();
4424
4592
  agentPanel.dispose();
4425
4593
  host.remove();
4426
4594
  }
@@ -4913,9 +5081,19 @@ function resolveBrowserDependencies(dependencies) {
4913
5081
  }
4914
5082
  function createController(config, dependencies = {}) {
4915
5083
  const browser = resolveBrowserDependencies(dependencies);
4916
- const view = dependencies.view ?? createRuntimeView(browser.document, config.shortcut, config.ai, config.locale);
5084
+ const view = dependencies.view ?? createRuntimeView(
5085
+ browser.document,
5086
+ config.shortcut,
5087
+ config.ai,
5088
+ config.locale,
5089
+ config.dataFlow.enabled,
5090
+ config.externalAgent.enabled,
5091
+ config.framework,
5092
+ config.sessionId
5093
+ );
4917
5094
  const api = dependencies.api ?? createRuntimeApi({
4918
5095
  apiBase: config.apiBase,
5096
+ dataFlowReportMaxBytes: config.dataFlow.limits.reportMaxBytes,
4919
5097
  fetch: browser.window.fetch.bind(browser.window),
4920
5098
  sessionToken: config.sessionToken
4921
5099
  });
@@ -4927,6 +5105,9 @@ function createController(config, dependencies = {}) {
4927
5105
  const restoredSelection = selectionSession.load();
4928
5106
  const sourceResolver = createSourceResolver({
4929
5107
  adapter: dependencies.reactAdapter ?? createReact18Adapter({
5108
+ ...config.dataFlow.enabled ? {
5109
+ getComponentRegistration: (component) => getDataFlowExtension()?.getComponentRegistration(component)
5110
+ } : {},
4930
5111
  maxComponentDepth: config.budget.maxComponentDepth
4931
5112
  }),
4932
5113
  onAdapterError() {
@@ -4950,6 +5131,8 @@ function createController(config, dependencies = {}) {
4950
5131
  apiStatus: marker === void 0 ? "not-required" : target2.code === void 0 ? "failed" : "connected",
4951
5132
  code: target2.code,
4952
5133
  collectionStatus: "ready",
5134
+ dataFlowReport: void 0,
5135
+ dataFlowStatus: "idle",
4953
5136
  element: void 0,
4954
5137
  elementContext: target2.element,
4955
5138
  instruction: target2.instruction,
@@ -4964,6 +5147,9 @@ function createController(config, dependencies = {}) {
4964
5147
  let workflowSelectionActive = false;
4965
5148
  let sessionRevision = 0;
4966
5149
  let editorRequestRevision = 0;
5150
+ let dataFlowRequestRevision = 0;
5151
+ let pageDataFlowReport;
5152
+ let pageDataFlowStatus = "idle";
4967
5153
  let addingTarget = false;
4968
5154
  let previewPrompt = "";
4969
5155
  let previousFocus;
@@ -4978,6 +5164,90 @@ function createController(config, dependencies = {}) {
4978
5164
  function activeTarget() {
4979
5165
  return targets.find((target2) => target2.id === activeTargetId) ?? targets.at(-1);
4980
5166
  }
5167
+ function dataFlowRequest(target2) {
5168
+ const react2 = target2.resolution.react;
5169
+ if (react2.componentSourceId !== void 0 && react2.sourceVersion !== void 0) {
5170
+ return Object.freeze({
5171
+ schemaVersion: DATA_FLOW_SCHEMA_VERSION2,
5172
+ componentSourceId: react2.componentSourceId,
5173
+ sourceVersion: react2.sourceVersion
5174
+ });
5175
+ }
5176
+ const marker = target2.marker;
5177
+ return marker === void 0 ? void 0 : Object.freeze({
5178
+ schemaVersion: DATA_FLOW_SCHEMA_VERSION2,
5179
+ fileId: marker.fileId,
5180
+ line: marker.line,
5181
+ column: marker.column
5182
+ });
5183
+ }
5184
+ function renderDataFlowView() {
5185
+ const dataFlowClient = getDataFlowExtension();
5186
+ const observations = dataFlowClient?.observations(browser.window.location.pathname) ?? [];
5187
+ const current = activeTarget();
5188
+ view.renderDataFlow({
5189
+ component: Object.freeze({
5190
+ status: config.dataFlow.enabled ? current?.dataFlowStatus ?? "idle" : "disabled",
5191
+ ...current?.dataFlowReport === void 0 ? {} : {
5192
+ report: dataFlowClient?.mergeComponentReport(
5193
+ current.dataFlowReport,
5194
+ observations
5195
+ ) ?? current.dataFlowReport
5196
+ }
5197
+ }),
5198
+ page: Object.freeze({
5199
+ status: config.dataFlow.enabled ? pageDataFlowStatus : "disabled",
5200
+ ...pageDataFlowReport === void 0 ? {} : {
5201
+ report: dataFlowClient?.mergePageReport(pageDataFlowReport, observations) ?? pageDataFlowReport
5202
+ }
5203
+ }),
5204
+ observationCount: observations.length
5205
+ });
5206
+ }
5207
+ async function loadDataFlowReports() {
5208
+ if (!config.dataFlow.enabled) {
5209
+ renderDataFlowView();
5210
+ return;
5211
+ }
5212
+ const current = activeTarget();
5213
+ const currentRequest = current === void 0 ? void 0 : dataFlowRequest(current);
5214
+ const pageTargets = targets.flatMap((target2) => {
5215
+ const request = dataFlowRequest(target2);
5216
+ return request === void 0 ? [] : [request];
5217
+ });
5218
+ const revision = ++dataFlowRequestRevision;
5219
+ const selectionRevision = sessionRevision;
5220
+ if (current !== void 0) current.dataFlowStatus = "loading";
5221
+ pageDataFlowStatus = pageTargets.length === 0 ? "idle" : "loading";
5222
+ renderDataFlowView();
5223
+ const [componentResult, pageResult] = await Promise.allSettled([
5224
+ currentRequest === void 0 ? Promise.resolve(void 0) : api.componentDataFlowReport(currentRequest),
5225
+ pageTargets.length === 0 ? Promise.resolve(void 0) : api.pageDataFlowReport({
5226
+ schemaVersion: DATA_FLOW_SCHEMA_VERSION2,
5227
+ targets: pageTargets
5228
+ })
5229
+ ]);
5230
+ if (!mounted || revision !== dataFlowRequestRevision || selectionRevision !== sessionRevision) {
5231
+ return;
5232
+ }
5233
+ if (current !== void 0 && targets.includes(current)) {
5234
+ if (componentResult.status === "fulfilled") {
5235
+ current.dataFlowReport = componentResult.value;
5236
+ current.dataFlowStatus = componentResult.value === void 0 ? "idle" : "ready";
5237
+ } else {
5238
+ current.dataFlowReport = void 0;
5239
+ current.dataFlowStatus = "error";
5240
+ }
5241
+ }
5242
+ if (pageResult.status === "fulfilled") {
5243
+ pageDataFlowReport = pageResult.value;
5244
+ pageDataFlowStatus = pageResult.value === void 0 ? "idle" : "ready";
5245
+ } else {
5246
+ pageDataFlowReport = void 0;
5247
+ pageDataFlowStatus = "error";
5248
+ }
5249
+ renderDataFlowView();
5250
+ }
4981
5251
  function snapshotTarget(target2) {
4982
5252
  if (target2.elementContext === void 0 || target2.styles === void 0) {
4983
5253
  return void 0;
@@ -5142,10 +5412,14 @@ ${summary}`;
5142
5412
  sessionRevision += 1;
5143
5413
  api.cancelPending();
5144
5414
  agentWorkflow.disposeSelection();
5415
+ externalHandoffWorkflow?.cancelPending();
5145
5416
  clearCollectionTimers();
5146
5417
  resizeObserver?.disconnect();
5147
5418
  targets = [];
5148
5419
  activeTargetId = void 0;
5420
+ dataFlowRequestRevision += 1;
5421
+ pageDataFlowReport = void 0;
5422
+ pageDataFlowStatus = "idle";
5149
5423
  selectionOpen = false;
5150
5424
  workflowSelectionActive = false;
5151
5425
  addingTarget = false;
@@ -5200,6 +5474,7 @@ ${summary}`;
5200
5474
  workflowSelectionActive = true;
5201
5475
  }
5202
5476
  refreshSelectionView(true);
5477
+ void loadDataFlowReports();
5203
5478
  view.focusTargetInstruction(activeTargetId);
5204
5479
  persistSelection();
5205
5480
  return;
@@ -5256,6 +5531,13 @@ ${summary}`;
5256
5531
  },
5257
5532
  view
5258
5533
  });
5534
+ const externalHandoffWorkflow = config.externalAgent.enabled && view.externalHandoffPanel !== void 0 ? getExternalHandoffExtension()?.createWorkflow(
5535
+ browser.window.fetch.bind(browser.window),
5536
+ view.externalHandoffPanel,
5537
+ selectedAnnotation,
5538
+ config.sessionToken,
5539
+ browser.window
5540
+ ) : void 0;
5259
5541
  async function loadSourceContext(target2, revision) {
5260
5542
  const marker = target2.marker;
5261
5543
  if (marker === void 0) {
@@ -5350,6 +5632,7 @@ ${summary}`;
5350
5632
  transition({ type: "SELECT" });
5351
5633
  view.hideHighlight();
5352
5634
  refreshSelectionView(true);
5635
+ void loadDataFlowReports();
5353
5636
  view.focusTargetInstruction(duplicate.id);
5354
5637
  view.announce(view.messages().announcements.duplicate);
5355
5638
  return;
@@ -5381,7 +5664,9 @@ ${summary}`;
5381
5664
  styles: void 0,
5382
5665
  instruction: "",
5383
5666
  apiStatus: marker === void 0 ? "not-required" : "loading",
5384
- collectionStatus: "loading"
5667
+ collectionStatus: "loading",
5668
+ dataFlowReport: void 0,
5669
+ dataFlowStatus: "idle"
5385
5670
  };
5386
5671
  targets.push(target2);
5387
5672
  activeTargetId = target2.id;
@@ -5391,6 +5676,7 @@ ${summary}`;
5391
5676
  view.hideHighlight();
5392
5677
  resizeObserver?.observe(element2);
5393
5678
  refreshSelectionView(true);
5679
+ void loadDataFlowReports();
5394
5680
  selectionOpen = true;
5395
5681
  view.focusTargetInstruction(target2.id);
5396
5682
  scheduleBrowserContextCollection(target2, revision);
@@ -5495,11 +5781,15 @@ ${summary}`;
5495
5781
  }
5496
5782
  view.hideSelectionTemporarily();
5497
5783
  view.hideSelectionHighlights();
5784
+ pageDataFlowReport = void 0;
5785
+ pageDataFlowStatus = "idle";
5786
+ renderDataFlowView();
5498
5787
  selectionSession.clear();
5499
5788
  view.announce(view.messages().announcements.allTargetsRemoved);
5500
5789
  return;
5501
5790
  }
5502
5791
  refreshSelectionView();
5792
+ void loadDataFlowReports();
5503
5793
  persistSelection();
5504
5794
  view.announce(view.messages().announcements.targetRemoved);
5505
5795
  }
@@ -5662,16 +5952,21 @@ ${summary}`;
5662
5952
  }
5663
5953
  activeTargetId = targetId;
5664
5954
  refreshSelectionView();
5955
+ void loadDataFlowReports();
5665
5956
  view.focusTargetInstruction(targetId);
5666
5957
  }
5667
5958
  function handleReselect() {
5668
5959
  beginReselect();
5669
5960
  }
5961
+ function handleDataFlowRefresh() {
5962
+ void loadDataFlowReports();
5963
+ }
5670
5964
  function mount() {
5671
5965
  if (mounted) {
5672
5966
  return;
5673
5967
  }
5674
5968
  mounted = true;
5969
+ externalHandoffWorkflow?.mount();
5675
5970
  view.renderStatus(state.status);
5676
5971
  browser.document.addEventListener("pointermove", handlePointerMove, true);
5677
5972
  browser.document.addEventListener("click", handleClick, true);
@@ -5685,6 +5980,7 @@ ${summary}`;
5685
5980
  view.targetList.addEventListener("input", handleTargetListInput);
5686
5981
  view.targetList.addEventListener("keydown", handleTargetListKeydown);
5687
5982
  view.openEditorButton.addEventListener("click", handleOpenEditorButtonClick);
5983
+ view.dataFlowRefreshButton.addEventListener("click", handleDataFlowRefresh);
5688
5984
  view.previewButton.addEventListener("click", handlePreview);
5689
5985
  view.copyButton.addEventListener("click", handleCopy);
5690
5986
  view.backButton.addEventListener("click", handleBack);
@@ -5737,6 +6033,7 @@ ${summary}`;
5737
6033
  api.cancelPending();
5738
6034
  api.dispose();
5739
6035
  agentWorkflow.disposeSelection();
6036
+ externalHandoffWorkflow?.dispose();
5740
6037
  sourceResolver.dispose();
5741
6038
  return;
5742
6039
  }
@@ -5755,6 +6052,7 @@ ${summary}`;
5755
6052
  view.targetList.removeEventListener("input", handleTargetListInput);
5756
6053
  view.targetList.removeEventListener("keydown", handleTargetListKeydown);
5757
6054
  view.openEditorButton.removeEventListener("click", handleOpenEditorButtonClick);
6055
+ view.dataFlowRefreshButton.removeEventListener("click", handleDataFlowRefresh);
5758
6056
  view.previewButton.removeEventListener("click", handlePreview);
5759
6057
  view.copyButton.removeEventListener("click", handleCopy);
5760
6058
  view.backButton.removeEventListener("click", handleBack);
@@ -5799,6 +6097,7 @@ ${summary}`;
5799
6097
  api.cancelPending();
5800
6098
  api.dispose();
5801
6099
  agentWorkflow.disposeSelection();
6100
+ externalHandoffWorkflow?.dispose();
5802
6101
  sourceResolver.dispose();
5803
6102
  view.dispose();
5804
6103
  state = INITIAL_RUNTIME_STATE;
@@ -5817,6 +6116,11 @@ function bootstrapSpotPatch(config) {
5817
6116
  }
5818
6117
  export {
5819
6118
  UI_MARKER_ATTRIBUTE,
5820
- bootstrapSpotPatch
6119
+ bootstrapSpotPatch,
6120
+ createDataFlowRuntime,
6121
+ getDataFlowRuntime,
6122
+ installDataFlowPrelude,
6123
+ mergeComponentDataFlowReport,
6124
+ mergePageDataFlowReport
5821
6125
  };
5822
6126
  //# sourceMappingURL=index.js.map