@spotpatch/runtime 1.5.2 → 1.7.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.cjs CHANGED
@@ -1729,7 +1729,8 @@ var AGENT_PANEL_STYLES = `
1729
1729
  font-weight: 650;
1730
1730
  }
1731
1731
  .spotpatch-agent-setup { padding: 12px; }
1732
- .spotpatch-agent-selectors { display: grid; grid-template-columns: 1fr; gap: 10px; }
1732
+ .spotpatch-agent-selectors { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
1733
+ .spotpatch-agent-mode { grid-column: 1 / -1; }
1733
1734
  .spotpatch-agent-selectors label { min-width: 0; color: var(--spotpatch-text-secondary); font-size: 11.5px; font-weight: 580; }
1734
1735
  .spotpatch-agent select {
1735
1736
  box-sizing: border-box;
@@ -1810,6 +1811,13 @@ var AGENT_PANEL_STYLES = `
1810
1811
  line-height: 1.5;
1811
1812
  }
1812
1813
  .spotpatch-consent input { width: 14px; height: 14px; flex: none; margin: 2px 0 0; accent-color: var(--spotpatch-accent); }
1814
+ .spotpatch-trusted-consent {
1815
+ border: 1px solid rgb(251 191 36 / 20%);
1816
+ border-radius: 10px;
1817
+ padding: 9px 10px;
1818
+ color: #e5e7eb;
1819
+ background: rgb(120 53 15 / 10%);
1820
+ }
1813
1821
  .spotpatch-workspace-consent {
1814
1822
  margin-top: 9px;
1815
1823
  border: 1px solid rgb(251 191 36 / 18%);
@@ -1907,12 +1915,17 @@ var AGENT_PANEL_STYLES = `
1907
1915
  white-space: pre;
1908
1916
  user-select: text;
1909
1917
  }
1918
+ @media (max-width: 520px) {
1919
+ .spotpatch-agent-selectors { grid-template-columns: 1fr; }
1920
+ .spotpatch-agent-mode { grid-column: auto; }
1921
+ }
1910
1922
  `;
1911
1923
  function addOption(document, select, value, label) {
1912
1924
  const option = document.createElement("option");
1913
1925
  option.value = value;
1914
1926
  option.textContent = label;
1915
1927
  select.append(option);
1928
+ return option;
1916
1929
  }
1917
1930
  function createAgentPanel(document, ai, localizer) {
1918
1931
  let messages = localizer.messages();
@@ -1930,6 +1943,11 @@ function createAgentPanel(document, ai, localizer) {
1930
1943
  setup.className = "spotpatch-agent-setup";
1931
1944
  const selectors = createMarkedElement(document, "div");
1932
1945
  selectors.className = "spotpatch-agent-selectors";
1946
+ const modeLabel = createMarkedElement(document, "label");
1947
+ modeLabel.className = "spotpatch-agent-mode";
1948
+ const modeText = createMarkedElement(document, "span");
1949
+ const modeSelect = createMarkedElement(document, "select");
1950
+ modeLabel.append(modeText, modeSelect);
1933
1951
  const providerLabel = createMarkedElement(document, "label");
1934
1952
  const providerText = createMarkedElement(document, "span");
1935
1953
  const providerSelect = createMarkedElement(document, "select");
@@ -1938,7 +1956,7 @@ function createAgentPanel(document, ai, localizer) {
1938
1956
  const modelText = createMarkedElement(document, "span");
1939
1957
  const modelSelect = createMarkedElement(document, "select");
1940
1958
  modelLabel.append(modelText, modelSelect);
1941
- selectors.append(providerLabel, modelLabel);
1959
+ selectors.append(modeLabel, providerLabel, modelLabel);
1942
1960
  const consent = createMarkedElement(document, "label");
1943
1961
  consent.className = "spotpatch-consent";
1944
1962
  const consentCheckbox = createMarkedElement(document, "input");
@@ -2005,6 +2023,8 @@ function createAgentPanel(document, ai, localizer) {
2005
2023
  const revertButton = createButton(document, messages.agent.revert);
2006
2024
  const resetButton = createButton(document, messages.agent.revise);
2007
2025
  const providers = ai.enabled ? ai.providers : [];
2026
+ const trustedFastModeAvailable = ai.enabled && ai.applyMode === "trusted-auto";
2027
+ const configuredApplyMode = ai.enabled ? ai.applyMode : "review";
2008
2028
  const providerById = new Map(providers.map((provider) => [provider.id, provider]));
2009
2029
  let contextReady = false;
2010
2030
  let editingEnabled = true;
@@ -2028,19 +2048,54 @@ function createAgentPanel(document, ai, localizer) {
2028
2048
  if (ai.enabled) {
2029
2049
  providerSelect.value = ai.defaultProvider;
2030
2050
  }
2051
+ let reviewOption;
2052
+ let trustedOption;
2053
+ let autoOption;
2054
+ if (configuredApplyMode === "auto") {
2055
+ autoOption = addOption(document, modeSelect, "auto", messages.agent.autoGated);
2056
+ } else {
2057
+ reviewOption = addOption(document, modeSelect, "review", messages.agent.review);
2058
+ if (trustedFastModeAvailable) {
2059
+ trustedOption = addOption(
2060
+ document,
2061
+ modeSelect,
2062
+ "trusted-auto",
2063
+ messages.agent.trustedFast
2064
+ );
2065
+ }
2066
+ }
2067
+ const defaultApplyMode = trustedFastModeAvailable ? "review" : configuredApplyMode;
2068
+ modeSelect.value = defaultApplyMode;
2069
+ modeLabel.hidden = !trustedFastModeAvailable;
2070
+ const selectedApplyMode = () => modeSelect.value === "trusted-auto" ? "trusted-auto" : defaultApplyMode;
2071
+ const trustedFastMode = () => selectedApplyMode() === "trusted-auto";
2031
2072
  const selectedProvider = () => providerById.get(providerSelect.value);
2073
+ const consentMessage = (provider) => {
2074
+ if (provider === void 0) {
2075
+ return messages.agent.providerUnavailable;
2076
+ }
2077
+ return trustedFastMode() ? messages.agent.trustedFastConsent(provider.label) : messages.agent.consent(provider.label);
2078
+ };
2079
+ const localChangesConsentGranted = () => trustedFastMode() ? consentCheckbox.checked : workspaceConsentCheckbox.checked;
2080
+ const renderMode = () => {
2081
+ const trusted = trustedFastMode();
2082
+ consent.classList.toggle("spotpatch-trusted-consent", trusted);
2083
+ badge.textContent = trusted ? messages.agent.trustedFast : configuredApplyMode === "auto" ? messages.agent.autoGated : messages.agent.review;
2084
+ consentText.textContent = consentMessage(selectedProvider());
2085
+ workspaceConsent.hidden = trusted || latestWorkspaceState !== "consent-required";
2086
+ };
2032
2087
  const populateModels = () => {
2033
2088
  const provider = selectedProvider();
2034
2089
  modelSelect.replaceChildren();
2035
2090
  if (provider === void 0) {
2036
- consentText.textContent = messages.agent.providerUnavailable;
2091
+ consentText.textContent = consentMessage(provider);
2037
2092
  return;
2038
2093
  }
2039
2094
  for (const model of provider.models) {
2040
2095
  addOption(document, modelSelect, model.id, model.label);
2041
2096
  }
2042
2097
  modelSelect.value = provider.defaultModel;
2043
- consentText.textContent = messages.agent.consent(provider.label);
2098
+ consentText.textContent = consentMessage(provider);
2044
2099
  };
2045
2100
  const refreshActions = () => {
2046
2101
  const setupVisible = ai.enabled && selectionVisible && !jobPresented;
@@ -2049,7 +2104,7 @@ function createAgentPanel(document, ai, localizer) {
2049
2104
  testButton.hidden = !setupVisible;
2050
2105
  runButton.hidden = !setupVisible;
2051
2106
  testButton.disabled = !editingEnabled || capabilityProbing || selectedProvider() === void 0;
2052
- runButton.disabled = !editingEnabled || capabilityProbing || !contextReady || !consentCheckbox.checked || latestWorkspaceState === "idle" || latestWorkspaceState === "checking" || latestWorkspaceState === "blocked" || latestWorkspaceState === "consent-required" && !workspaceConsentCheckbox.checked || selectedProvider() === void 0 || modelSelect.value.length === 0;
2107
+ runButton.disabled = !editingEnabled || capabilityProbing || !contextReady || !consentCheckbox.checked || latestWorkspaceState === "idle" || latestWorkspaceState === "checking" || latestWorkspaceState === "blocked" || latestWorkspaceState === "consent-required" && !localChangesConsentGranted() || selectedProvider() === void 0 || modelSelect.value.length === 0;
2053
2108
  if (!jobPresented || !selectionVisible) {
2054
2109
  cancelButton.hidden = true;
2055
2110
  applyButton.hidden = true;
@@ -2095,7 +2150,14 @@ function createAgentPanel(document, ai, localizer) {
2095
2150
  capabilityReady = false;
2096
2151
  refreshActions();
2097
2152
  }
2153
+ function handleModeChange() {
2154
+ consentCheckbox.checked = false;
2155
+ workspaceConsentCheckbox.checked = false;
2156
+ renderMode();
2157
+ refreshActions();
2158
+ }
2098
2159
  populateModels();
2160
+ modeSelect.addEventListener("change", handleModeChange);
2099
2161
  providerSelect.addEventListener("change", handleProviderChange);
2100
2162
  modelSelect.addEventListener("change", handleModelChange);
2101
2163
  consentCheckbox.addEventListener("change", refreshActions);
@@ -2149,7 +2211,11 @@ function createAgentPanel(document, ai, localizer) {
2149
2211
  function applyMessages() {
2150
2212
  messages = localizer.messages();
2151
2213
  title.textContent = messages.agent.title;
2152
- badge.textContent = ai.enabled && ai.applyMode === "auto" ? messages.agent.autoGated : messages.agent.review;
2214
+ modeText.textContent = messages.agent.mode;
2215
+ if (reviewOption !== void 0) reviewOption.textContent = messages.agent.review;
2216
+ if (trustedOption !== void 0)
2217
+ trustedOption.textContent = messages.agent.trustedFast;
2218
+ if (autoOption !== void 0) autoOption.textContent = messages.agent.autoGated;
2153
2219
  providerText.textContent = messages.agent.provider;
2154
2220
  modelText.textContent = messages.agent.model;
2155
2221
  providerSelect.setAttribute("aria-label", messages.agent.providerAriaLabel);
@@ -2161,8 +2227,7 @@ function createAgentPanel(document, ai, localizer) {
2161
2227
  applyButton.textContent = messages.agent.apply;
2162
2228
  revertButton.textContent = messages.agent.revert;
2163
2229
  resetButton.textContent = messages.agent.revise;
2164
- const provider = selectedProvider();
2165
- consentText.textContent = provider === void 0 ? messages.agent.providerUnavailable : messages.agent.consent(provider.label);
2230
+ renderMode();
2166
2231
  if (latestCapabilityState === "idle") {
2167
2232
  capability.textContent = messages.agent.connectionNotTested;
2168
2233
  } else if (latestCapabilityState === "probing") {
@@ -2184,6 +2249,7 @@ function createAgentPanel(document, ai, localizer) {
2184
2249
  applyMessages();
2185
2250
  return Object.freeze({
2186
2251
  root,
2252
+ modeSelect,
2187
2253
  providerSelect,
2188
2254
  modelSelect,
2189
2255
  consentCheckbox,
@@ -2198,7 +2264,7 @@ function createAgentPanel(document, ai, localizer) {
2198
2264
  return consentCheckbox.checked;
2199
2265
  },
2200
2266
  workspaceConsentGranted() {
2201
- return workspaceConsentCheckbox.checked;
2267
+ return localChangesConsentGranted();
2202
2268
  },
2203
2269
  readSelection() {
2204
2270
  const provider = selectedProvider();
@@ -2206,6 +2272,7 @@ function createAgentPanel(document, ai, localizer) {
2206
2272
  (candidate) => candidate.id === modelSelect.value
2207
2273
  );
2208
2274
  return provider === void 0 || model === void 0 ? void 0 : Object.freeze({
2275
+ applyMode: selectedApplyMode(),
2209
2276
  providerProfileId: provider.id,
2210
2277
  modelProfileId: model.id
2211
2278
  });
@@ -2226,7 +2293,7 @@ function createAgentPanel(document, ai, localizer) {
2226
2293
  latestWorkspaceErrorCode = errorCode ?? snapshot?.errorCode;
2227
2294
  workspace.dataset.state = state;
2228
2295
  if (state !== "checking") {
2229
- workspaceConsent.hidden = state !== "consent-required";
2296
+ workspaceConsent.hidden = trustedFastMode() || state !== "consent-required";
2230
2297
  }
2231
2298
  if (state === "idle" || state === "ready" || state === "blocked") {
2232
2299
  workspaceConsentCheckbox.checked = false;
@@ -2240,6 +2307,7 @@ function createAgentPanel(document, ai, localizer) {
2240
2307
  job.hidden = false;
2241
2308
  providerSelect.disabled = true;
2242
2309
  modelSelect.disabled = true;
2310
+ modeSelect.disabled = true;
2243
2311
  consentCheckbox.disabled = true;
2244
2312
  workspaceConsentCheckbox.disabled = true;
2245
2313
  latestSnapshot = snapshot;
@@ -2258,6 +2326,7 @@ function createAgentPanel(document, ai, localizer) {
2258
2326
  job.hidden = true;
2259
2327
  providerSelect.disabled = false;
2260
2328
  modelSelect.disabled = false;
2329
+ modeSelect.disabled = false;
2261
2330
  consentCheckbox.disabled = false;
2262
2331
  workspaceConsentCheckbox.disabled = false;
2263
2332
  activity.replaceChildren();
@@ -2276,6 +2345,7 @@ function createAgentPanel(document, ai, localizer) {
2276
2345
  editingEnabled = enabled;
2277
2346
  providerSelect.disabled = !enabled || jobPresented;
2278
2347
  modelSelect.disabled = !enabled || jobPresented;
2348
+ modeSelect.disabled = !enabled || jobPresented;
2279
2349
  consentCheckbox.disabled = !enabled || jobPresented;
2280
2350
  workspaceConsentCheckbox.disabled = !enabled || jobPresented;
2281
2351
  refreshActions();
@@ -2298,6 +2368,7 @@ function createAgentPanel(document, ai, localizer) {
2298
2368
  unsubscribeLocale();
2299
2369
  providerSelect.removeEventListener("change", handleProviderChange);
2300
2370
  modelSelect.removeEventListener("change", handleModelChange);
2371
+ modeSelect.removeEventListener("change", handleModeChange);
2301
2372
  consentCheckbox.removeEventListener("change", refreshActions);
2302
2373
  workspaceConsentCheckbox.removeEventListener("change", refreshActions);
2303
2374
  }
@@ -2701,14 +2772,17 @@ var UI_MESSAGES = Object.freeze({
2701
2772
  }),
2702
2773
  agent: Object.freeze({
2703
2774
  title: "AI code agent",
2775
+ mode: "Execution mode",
2704
2776
  review: "Review",
2705
2777
  autoGated: "Auto gated",
2778
+ trustedFast: "Trusted fast",
2706
2779
  provider: "Provider",
2707
2780
  model: "Model",
2708
2781
  providerAriaLabel: "AI provider",
2709
2782
  modelAriaLabel: "AI model",
2710
2783
  providerUnavailable: "Provider configuration is unavailable.",
2711
2784
  consent: (provider) => `I understand selected context and allowed source may be sent to ${provider}; its data policy is my responsibility.`,
2785
+ trustedFastConsent: (provider) => `Enable trusted fast mode for ${provider}: send allowed project context, include current local changes, and directly apply AI changes after required checks, including file deletions and configuration changes.`,
2712
2786
  connectionNotTested: "Optional connection check not run",
2713
2787
  workspaceNotChecked: "Local workspace not checked",
2714
2788
  checkingWorkspace: "Checking Git workspace and isolated execution\u2026",
@@ -2836,14 +2910,17 @@ var UI_MESSAGES = Object.freeze({
2836
2910
  }),
2837
2911
  agent: Object.freeze({
2838
2912
  title: "AI \u4EE3\u7801 Agent",
2913
+ mode: "\u6267\u884C\u65B9\u5F0F",
2839
2914
  review: "\u5BA1\u9605\u6A21\u5F0F",
2840
2915
  autoGated: "\u53D7\u63A7\u81EA\u52A8\u6A21\u5F0F",
2916
+ trustedFast: "\u53EF\u4FE1\u5FEB\u901F\u6A21\u5F0F",
2841
2917
  provider: "\u6A21\u578B\u670D\u52A1",
2842
2918
  model: "\u6A21\u578B",
2843
2919
  providerAriaLabel: "AI \u6A21\u578B\u670D\u52A1",
2844
2920
  modelAriaLabel: "AI \u6A21\u578B",
2845
2921
  providerUnavailable: "\u6A21\u578B\u670D\u52A1\u914D\u7F6E\u4E0D\u53EF\u7528\u3002",
2846
2922
  consent: (provider) => `\u6211\u4E86\u89E3\u9009\u4E2D\u4E0A\u4E0B\u6587\u4E0E\u83B7\u51C6\u6E90\u7801\u53EF\u80FD\u53D1\u9001\u5230 ${provider}\uFF0C\u5E76\u81EA\u884C\u8D1F\u8D23\u5176\u6570\u636E\u7B56\u7565\u3002`,
2923
+ trustedFastConsent: (provider) => `\u4E3A ${provider} \u542F\u7528\u53EF\u4FE1\u5FEB\u901F\u6A21\u5F0F\uFF1A\u53D1\u9001\u83B7\u51C6\u7684\u9879\u76EE\u4E0A\u4E0B\u6587\u3001\u7EB3\u5165\u5F53\u524D\u672C\u5730\u4FEE\u6539\uFF0C\u5E76\u5728\u5FC5\u9700\u68C0\u67E5\u901A\u8FC7\u540E\u76F4\u63A5\u5E94\u7528 AI \u53D8\u66F4\uFF0C\u5305\u62EC\u5220\u9664\u6587\u4EF6\u4E0E\u914D\u7F6E\u53D8\u66F4\u3002`,
2847
2924
  connectionNotTested: "\u5C1A\u672A\u6267\u884C\u53EF\u9009\u8FDE\u63A5\u68C0\u67E5",
2848
2925
  workspaceNotChecked: "\u5C1A\u672A\u68C0\u67E5\u672C\u5730\u5DE5\u4F5C\u533A",
2849
2926
  checkingWorkspace: "\u6B63\u5728\u68C0\u67E5 Git \u5DE5\u4F5C\u533A\u4E0E\u9694\u79BB\u6267\u884C\u73AF\u5883\u2026\u2026",
@@ -4198,6 +4275,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4198
4275
  closeButton,
4199
4276
  agentProviderSelect: agentPanel.providerSelect,
4200
4277
  agentModelSelect: agentPanel.modelSelect,
4278
+ agentModeSelect: agentPanel.modeSelect,
4201
4279
  agentConsentCheckbox: agentPanel.consentCheckbox,
4202
4280
  agentWorkspaceConsentCheckbox: agentPanel.workspaceConsentCheckbox,
4203
4281
  agentTestButton: agentPanel.testButton,
@@ -4439,6 +4517,7 @@ function createAgentWorkflow(options) {
4439
4517
  let appliedDuringCurrentJob = false;
4440
4518
  let actionPending = false;
4441
4519
  const selectedProfiles = () => options.view.readAgentSelection();
4520
+ const consentKey = (selection) => `${selection.providerProfileId}:${selection.applyMode}`;
4442
4521
  const refreshWorkspaceHealth = async (workflowRevision) => {
4443
4522
  options.view.renderAgentWorkspaceHealth("checking");
4444
4523
  try {
@@ -4476,9 +4555,7 @@ function createAgentWorkflow(options) {
4476
4555
  );
4477
4556
  return;
4478
4557
  }
4479
- options.view.setAgentProviderConsent(
4480
- providerConsents.has(selection.providerProfileId)
4481
- );
4558
+ options.view.setAgentProviderConsent(providerConsents.has(consentKey(selection)));
4482
4559
  const cached = capabilities.get(
4483
4560
  capabilityKey(selection.providerProfileId, selection.modelProfileId)
4484
4561
  );
@@ -4646,9 +4723,9 @@ function createAgentWorkflow(options) {
4646
4723
  return;
4647
4724
  }
4648
4725
  if (options.view.agentConsentGranted()) {
4649
- providerConsents.add(selection.providerProfileId);
4726
+ providerConsents.add(consentKey(selection));
4650
4727
  } else {
4651
- providerConsents.delete(selection.providerProfileId);
4728
+ providerConsents.delete(consentKey(selection));
4652
4729
  }
4653
4730
  },
4654
4731
  disposeSelection() {
@@ -4700,7 +4777,7 @@ function createAgentWorkflow(options) {
4700
4777
  options.view.announce(options.view.messages().agent.consentRequired);
4701
4778
  return;
4702
4779
  }
4703
- providerConsents.add(selection.providerProfileId);
4780
+ providerConsents.add(consentKey(selection));
4704
4781
  const workflowRevision = ++revision;
4705
4782
  options.view.setAgentEditingEnabled(false);
4706
4783
  void refreshWorkspaceHealth(workflowRevision).then(async (health) => {
@@ -4717,9 +4794,11 @@ function createAgentWorkflow(options) {
4717
4794
  }
4718
4795
  const created = await options.api.createAgentJob({
4719
4796
  annotation,
4797
+ applyMode: selection.applyMode,
4720
4798
  providerProfileId: selection.providerProfileId,
4721
4799
  modelProfileId: selection.modelProfileId,
4722
4800
  providerDataConsent: true,
4801
+ ...selection.applyMode === "trusted-auto" ? { trustedFastModeConsent: true } : {},
4723
4802
  workingTreeMode: health.state === "consent-required" ? "include-local-changes" : "require-clean"
4724
4803
  });
4725
4804
  if (workflowRevision !== revision) {
@@ -5616,6 +5695,10 @@ ${summary}`;
5616
5695
  "change",
5617
5696
  agentWorkflow.providerOrModelChanged
5618
5697
  );
5698
+ view.agentModeSelect.addEventListener(
5699
+ "change",
5700
+ agentWorkflow.providerOrModelChanged
5701
+ );
5619
5702
  view.agentConsentCheckbox.addEventListener("change", agentWorkflow.consentChanged);
5620
5703
  view.agentTestButton.addEventListener("click", agentWorkflow.testCapability);
5621
5704
  view.agentRunButton.addEventListener("click", agentWorkflow.run);
@@ -5682,6 +5765,10 @@ ${summary}`;
5682
5765
  "change",
5683
5766
  agentWorkflow.providerOrModelChanged
5684
5767
  );
5768
+ view.agentModeSelect.removeEventListener(
5769
+ "change",
5770
+ agentWorkflow.providerOrModelChanged
5771
+ );
5685
5772
  view.agentConsentCheckbox.removeEventListener(
5686
5773
  "change",
5687
5774
  agentWorkflow.consentChanged