@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.js CHANGED
@@ -1725,7 +1725,8 @@ var AGENT_PANEL_STYLES = `
1725
1725
  font-weight: 650;
1726
1726
  }
1727
1727
  .spotpatch-agent-setup { padding: 12px; }
1728
- .spotpatch-agent-selectors { display: grid; grid-template-columns: 1fr; gap: 10px; }
1728
+ .spotpatch-agent-selectors { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
1729
+ .spotpatch-agent-mode { grid-column: 1 / -1; }
1729
1730
  .spotpatch-agent-selectors label { min-width: 0; color: var(--spotpatch-text-secondary); font-size: 11.5px; font-weight: 580; }
1730
1731
  .spotpatch-agent select {
1731
1732
  box-sizing: border-box;
@@ -1806,6 +1807,13 @@ var AGENT_PANEL_STYLES = `
1806
1807
  line-height: 1.5;
1807
1808
  }
1808
1809
  .spotpatch-consent input { width: 14px; height: 14px; flex: none; margin: 2px 0 0; accent-color: var(--spotpatch-accent); }
1810
+ .spotpatch-trusted-consent {
1811
+ border: 1px solid rgb(251 191 36 / 20%);
1812
+ border-radius: 10px;
1813
+ padding: 9px 10px;
1814
+ color: #e5e7eb;
1815
+ background: rgb(120 53 15 / 10%);
1816
+ }
1809
1817
  .spotpatch-workspace-consent {
1810
1818
  margin-top: 9px;
1811
1819
  border: 1px solid rgb(251 191 36 / 18%);
@@ -1903,12 +1911,17 @@ var AGENT_PANEL_STYLES = `
1903
1911
  white-space: pre;
1904
1912
  user-select: text;
1905
1913
  }
1914
+ @media (max-width: 520px) {
1915
+ .spotpatch-agent-selectors { grid-template-columns: 1fr; }
1916
+ .spotpatch-agent-mode { grid-column: auto; }
1917
+ }
1906
1918
  `;
1907
1919
  function addOption(document, select, value, label) {
1908
1920
  const option = document.createElement("option");
1909
1921
  option.value = value;
1910
1922
  option.textContent = label;
1911
1923
  select.append(option);
1924
+ return option;
1912
1925
  }
1913
1926
  function createAgentPanel(document, ai, localizer) {
1914
1927
  let messages = localizer.messages();
@@ -1926,6 +1939,11 @@ function createAgentPanel(document, ai, localizer) {
1926
1939
  setup.className = "spotpatch-agent-setup";
1927
1940
  const selectors = createMarkedElement(document, "div");
1928
1941
  selectors.className = "spotpatch-agent-selectors";
1942
+ const modeLabel = createMarkedElement(document, "label");
1943
+ modeLabel.className = "spotpatch-agent-mode";
1944
+ const modeText = createMarkedElement(document, "span");
1945
+ const modeSelect = createMarkedElement(document, "select");
1946
+ modeLabel.append(modeText, modeSelect);
1929
1947
  const providerLabel = createMarkedElement(document, "label");
1930
1948
  const providerText = createMarkedElement(document, "span");
1931
1949
  const providerSelect = createMarkedElement(document, "select");
@@ -1934,7 +1952,7 @@ function createAgentPanel(document, ai, localizer) {
1934
1952
  const modelText = createMarkedElement(document, "span");
1935
1953
  const modelSelect = createMarkedElement(document, "select");
1936
1954
  modelLabel.append(modelText, modelSelect);
1937
- selectors.append(providerLabel, modelLabel);
1955
+ selectors.append(modeLabel, providerLabel, modelLabel);
1938
1956
  const consent = createMarkedElement(document, "label");
1939
1957
  consent.className = "spotpatch-consent";
1940
1958
  const consentCheckbox = createMarkedElement(document, "input");
@@ -2001,6 +2019,8 @@ function createAgentPanel(document, ai, localizer) {
2001
2019
  const revertButton = createButton(document, messages.agent.revert);
2002
2020
  const resetButton = createButton(document, messages.agent.revise);
2003
2021
  const providers = ai.enabled ? ai.providers : [];
2022
+ const trustedFastModeAvailable = ai.enabled && ai.applyMode === "trusted-auto";
2023
+ const configuredApplyMode = ai.enabled ? ai.applyMode : "review";
2004
2024
  const providerById = new Map(providers.map((provider) => [provider.id, provider]));
2005
2025
  let contextReady = false;
2006
2026
  let editingEnabled = true;
@@ -2024,19 +2044,54 @@ function createAgentPanel(document, ai, localizer) {
2024
2044
  if (ai.enabled) {
2025
2045
  providerSelect.value = ai.defaultProvider;
2026
2046
  }
2047
+ let reviewOption;
2048
+ let trustedOption;
2049
+ let autoOption;
2050
+ if (configuredApplyMode === "auto") {
2051
+ autoOption = addOption(document, modeSelect, "auto", messages.agent.autoGated);
2052
+ } else {
2053
+ reviewOption = addOption(document, modeSelect, "review", messages.agent.review);
2054
+ if (trustedFastModeAvailable) {
2055
+ trustedOption = addOption(
2056
+ document,
2057
+ modeSelect,
2058
+ "trusted-auto",
2059
+ messages.agent.trustedFast
2060
+ );
2061
+ }
2062
+ }
2063
+ const defaultApplyMode = trustedFastModeAvailable ? "review" : configuredApplyMode;
2064
+ modeSelect.value = defaultApplyMode;
2065
+ modeLabel.hidden = !trustedFastModeAvailable;
2066
+ const selectedApplyMode = () => modeSelect.value === "trusted-auto" ? "trusted-auto" : defaultApplyMode;
2067
+ const trustedFastMode = () => selectedApplyMode() === "trusted-auto";
2027
2068
  const selectedProvider = () => providerById.get(providerSelect.value);
2069
+ const consentMessage = (provider) => {
2070
+ if (provider === void 0) {
2071
+ return messages.agent.providerUnavailable;
2072
+ }
2073
+ return trustedFastMode() ? messages.agent.trustedFastConsent(provider.label) : messages.agent.consent(provider.label);
2074
+ };
2075
+ const localChangesConsentGranted = () => trustedFastMode() ? consentCheckbox.checked : workspaceConsentCheckbox.checked;
2076
+ const renderMode = () => {
2077
+ const trusted = trustedFastMode();
2078
+ consent.classList.toggle("spotpatch-trusted-consent", trusted);
2079
+ badge.textContent = trusted ? messages.agent.trustedFast : configuredApplyMode === "auto" ? messages.agent.autoGated : messages.agent.review;
2080
+ consentText.textContent = consentMessage(selectedProvider());
2081
+ workspaceConsent.hidden = trusted || latestWorkspaceState !== "consent-required";
2082
+ };
2028
2083
  const populateModels = () => {
2029
2084
  const provider = selectedProvider();
2030
2085
  modelSelect.replaceChildren();
2031
2086
  if (provider === void 0) {
2032
- consentText.textContent = messages.agent.providerUnavailable;
2087
+ consentText.textContent = consentMessage(provider);
2033
2088
  return;
2034
2089
  }
2035
2090
  for (const model of provider.models) {
2036
2091
  addOption(document, modelSelect, model.id, model.label);
2037
2092
  }
2038
2093
  modelSelect.value = provider.defaultModel;
2039
- consentText.textContent = messages.agent.consent(provider.label);
2094
+ consentText.textContent = consentMessage(provider);
2040
2095
  };
2041
2096
  const refreshActions = () => {
2042
2097
  const setupVisible = ai.enabled && selectionVisible && !jobPresented;
@@ -2045,7 +2100,7 @@ function createAgentPanel(document, ai, localizer) {
2045
2100
  testButton.hidden = !setupVisible;
2046
2101
  runButton.hidden = !setupVisible;
2047
2102
  testButton.disabled = !editingEnabled || capabilityProbing || selectedProvider() === void 0;
2048
- 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;
2103
+ runButton.disabled = !editingEnabled || capabilityProbing || !contextReady || !consentCheckbox.checked || latestWorkspaceState === "idle" || latestWorkspaceState === "checking" || latestWorkspaceState === "blocked" || latestWorkspaceState === "consent-required" && !localChangesConsentGranted() || selectedProvider() === void 0 || modelSelect.value.length === 0;
2049
2104
  if (!jobPresented || !selectionVisible) {
2050
2105
  cancelButton.hidden = true;
2051
2106
  applyButton.hidden = true;
@@ -2091,7 +2146,14 @@ function createAgentPanel(document, ai, localizer) {
2091
2146
  capabilityReady = false;
2092
2147
  refreshActions();
2093
2148
  }
2149
+ function handleModeChange() {
2150
+ consentCheckbox.checked = false;
2151
+ workspaceConsentCheckbox.checked = false;
2152
+ renderMode();
2153
+ refreshActions();
2154
+ }
2094
2155
  populateModels();
2156
+ modeSelect.addEventListener("change", handleModeChange);
2095
2157
  providerSelect.addEventListener("change", handleProviderChange);
2096
2158
  modelSelect.addEventListener("change", handleModelChange);
2097
2159
  consentCheckbox.addEventListener("change", refreshActions);
@@ -2145,7 +2207,11 @@ function createAgentPanel(document, ai, localizer) {
2145
2207
  function applyMessages() {
2146
2208
  messages = localizer.messages();
2147
2209
  title.textContent = messages.agent.title;
2148
- badge.textContent = ai.enabled && ai.applyMode === "auto" ? messages.agent.autoGated : messages.agent.review;
2210
+ modeText.textContent = messages.agent.mode;
2211
+ if (reviewOption !== void 0) reviewOption.textContent = messages.agent.review;
2212
+ if (trustedOption !== void 0)
2213
+ trustedOption.textContent = messages.agent.trustedFast;
2214
+ if (autoOption !== void 0) autoOption.textContent = messages.agent.autoGated;
2149
2215
  providerText.textContent = messages.agent.provider;
2150
2216
  modelText.textContent = messages.agent.model;
2151
2217
  providerSelect.setAttribute("aria-label", messages.agent.providerAriaLabel);
@@ -2157,8 +2223,7 @@ function createAgentPanel(document, ai, localizer) {
2157
2223
  applyButton.textContent = messages.agent.apply;
2158
2224
  revertButton.textContent = messages.agent.revert;
2159
2225
  resetButton.textContent = messages.agent.revise;
2160
- const provider = selectedProvider();
2161
- consentText.textContent = provider === void 0 ? messages.agent.providerUnavailable : messages.agent.consent(provider.label);
2226
+ renderMode();
2162
2227
  if (latestCapabilityState === "idle") {
2163
2228
  capability.textContent = messages.agent.connectionNotTested;
2164
2229
  } else if (latestCapabilityState === "probing") {
@@ -2180,6 +2245,7 @@ function createAgentPanel(document, ai, localizer) {
2180
2245
  applyMessages();
2181
2246
  return Object.freeze({
2182
2247
  root,
2248
+ modeSelect,
2183
2249
  providerSelect,
2184
2250
  modelSelect,
2185
2251
  consentCheckbox,
@@ -2194,7 +2260,7 @@ function createAgentPanel(document, ai, localizer) {
2194
2260
  return consentCheckbox.checked;
2195
2261
  },
2196
2262
  workspaceConsentGranted() {
2197
- return workspaceConsentCheckbox.checked;
2263
+ return localChangesConsentGranted();
2198
2264
  },
2199
2265
  readSelection() {
2200
2266
  const provider = selectedProvider();
@@ -2202,6 +2268,7 @@ function createAgentPanel(document, ai, localizer) {
2202
2268
  (candidate) => candidate.id === modelSelect.value
2203
2269
  );
2204
2270
  return provider === void 0 || model === void 0 ? void 0 : Object.freeze({
2271
+ applyMode: selectedApplyMode(),
2205
2272
  providerProfileId: provider.id,
2206
2273
  modelProfileId: model.id
2207
2274
  });
@@ -2222,7 +2289,7 @@ function createAgentPanel(document, ai, localizer) {
2222
2289
  latestWorkspaceErrorCode = errorCode ?? snapshot?.errorCode;
2223
2290
  workspace.dataset.state = state;
2224
2291
  if (state !== "checking") {
2225
- workspaceConsent.hidden = state !== "consent-required";
2292
+ workspaceConsent.hidden = trustedFastMode() || state !== "consent-required";
2226
2293
  }
2227
2294
  if (state === "idle" || state === "ready" || state === "blocked") {
2228
2295
  workspaceConsentCheckbox.checked = false;
@@ -2236,6 +2303,7 @@ function createAgentPanel(document, ai, localizer) {
2236
2303
  job.hidden = false;
2237
2304
  providerSelect.disabled = true;
2238
2305
  modelSelect.disabled = true;
2306
+ modeSelect.disabled = true;
2239
2307
  consentCheckbox.disabled = true;
2240
2308
  workspaceConsentCheckbox.disabled = true;
2241
2309
  latestSnapshot = snapshot;
@@ -2254,6 +2322,7 @@ function createAgentPanel(document, ai, localizer) {
2254
2322
  job.hidden = true;
2255
2323
  providerSelect.disabled = false;
2256
2324
  modelSelect.disabled = false;
2325
+ modeSelect.disabled = false;
2257
2326
  consentCheckbox.disabled = false;
2258
2327
  workspaceConsentCheckbox.disabled = false;
2259
2328
  activity.replaceChildren();
@@ -2272,6 +2341,7 @@ function createAgentPanel(document, ai, localizer) {
2272
2341
  editingEnabled = enabled;
2273
2342
  providerSelect.disabled = !enabled || jobPresented;
2274
2343
  modelSelect.disabled = !enabled || jobPresented;
2344
+ modeSelect.disabled = !enabled || jobPresented;
2275
2345
  consentCheckbox.disabled = !enabled || jobPresented;
2276
2346
  workspaceConsentCheckbox.disabled = !enabled || jobPresented;
2277
2347
  refreshActions();
@@ -2294,6 +2364,7 @@ function createAgentPanel(document, ai, localizer) {
2294
2364
  unsubscribeLocale();
2295
2365
  providerSelect.removeEventListener("change", handleProviderChange);
2296
2366
  modelSelect.removeEventListener("change", handleModelChange);
2367
+ modeSelect.removeEventListener("change", handleModeChange);
2297
2368
  consentCheckbox.removeEventListener("change", refreshActions);
2298
2369
  workspaceConsentCheckbox.removeEventListener("change", refreshActions);
2299
2370
  }
@@ -2699,14 +2770,17 @@ var UI_MESSAGES = Object.freeze({
2699
2770
  }),
2700
2771
  agent: Object.freeze({
2701
2772
  title: "AI code agent",
2773
+ mode: "Execution mode",
2702
2774
  review: "Review",
2703
2775
  autoGated: "Auto gated",
2776
+ trustedFast: "Trusted fast",
2704
2777
  provider: "Provider",
2705
2778
  model: "Model",
2706
2779
  providerAriaLabel: "AI provider",
2707
2780
  modelAriaLabel: "AI model",
2708
2781
  providerUnavailable: "Provider configuration is unavailable.",
2709
2782
  consent: (provider) => `I understand selected context and allowed source may be sent to ${provider}; its data policy is my responsibility.`,
2783
+ 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.`,
2710
2784
  connectionNotTested: "Optional connection check not run",
2711
2785
  workspaceNotChecked: "Local workspace not checked",
2712
2786
  checkingWorkspace: "Checking Git workspace and isolated execution\u2026",
@@ -2834,14 +2908,17 @@ var UI_MESSAGES = Object.freeze({
2834
2908
  }),
2835
2909
  agent: Object.freeze({
2836
2910
  title: "AI \u4EE3\u7801 Agent",
2911
+ mode: "\u6267\u884C\u65B9\u5F0F",
2837
2912
  review: "\u5BA1\u9605\u6A21\u5F0F",
2838
2913
  autoGated: "\u53D7\u63A7\u81EA\u52A8\u6A21\u5F0F",
2914
+ trustedFast: "\u53EF\u4FE1\u5FEB\u901F\u6A21\u5F0F",
2839
2915
  provider: "\u6A21\u578B\u670D\u52A1",
2840
2916
  model: "\u6A21\u578B",
2841
2917
  providerAriaLabel: "AI \u6A21\u578B\u670D\u52A1",
2842
2918
  modelAriaLabel: "AI \u6A21\u578B",
2843
2919
  providerUnavailable: "\u6A21\u578B\u670D\u52A1\u914D\u7F6E\u4E0D\u53EF\u7528\u3002",
2844
2920
  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`,
2921
+ 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`,
2845
2922
  connectionNotTested: "\u5C1A\u672A\u6267\u884C\u53EF\u9009\u8FDE\u63A5\u68C0\u67E5",
2846
2923
  workspaceNotChecked: "\u5C1A\u672A\u68C0\u67E5\u672C\u5730\u5DE5\u4F5C\u533A",
2847
2924
  checkingWorkspace: "\u6B63\u5728\u68C0\u67E5 Git \u5DE5\u4F5C\u533A\u4E0E\u9694\u79BB\u6267\u884C\u73AF\u5883\u2026\u2026",
@@ -4196,6 +4273,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4196
4273
  closeButton,
4197
4274
  agentProviderSelect: agentPanel.providerSelect,
4198
4275
  agentModelSelect: agentPanel.modelSelect,
4276
+ agentModeSelect: agentPanel.modeSelect,
4199
4277
  agentConsentCheckbox: agentPanel.consentCheckbox,
4200
4278
  agentWorkspaceConsentCheckbox: agentPanel.workspaceConsentCheckbox,
4201
4279
  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