@codingame/monaco-vscode-chat-cloud-service-override 0.0.0-stage → 38.0.7
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/common.d.ts +2 -0
- package/common.js +29 -0
- package/index.d.ts +3 -0
- package/index.js +9 -0
- package/package.json +52 -3
- package/session.d.ts +3 -0
- package/session.js +11 -0
- package/vscode/src/vs/sessions/contrib/chat/browser/newSession.d.ts +9 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudAutomationStore.d.ts +67 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudAutomationStore.js +695 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudSandboxSessionConfig.d.ts +4 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudSandboxSessionConfig.js +39 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessions.contribution.d.ts +1 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessions.contribution.js +59 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsActions.d.ts +1 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsActions.js +83 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsChangesets.d.ts +87 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsChangesets.js +176 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsProvider.d.ts +422 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsProvider.js +2444 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/githubCloudAutomationStore.d.ts +91 -0
- package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/githubCloudAutomationStore.js +620 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHost.contribution.d.ts +1 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHost.contribution.js +5 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostContribution.d.ts +46 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostContribution.js +321 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxSessionsProvider.d.ts +104 -0
- package/vscode/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxSessionsProvider.js +345 -0
- package/README.md +0 -3
package/vscode/src/vs/sessions/contrib/providers/copilotChatSessions/browser/cloudAutomationStore.js
ADDED
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
|
|
2
|
+
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
|
|
3
|
+
import { RunOnceScheduler } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
4
|
+
import { CancellationToken } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
|
|
5
|
+
import { Codicon } from '@codingame/monaco-vscode-api/vscode/vs/base/common/codicons';
|
|
6
|
+
import { isCancellationError, CancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
7
|
+
import { Disposable, DisposableStore } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
8
|
+
import { Schemas } from '@codingame/monaco-vscode-api/vscode/vs/base/common/network';
|
|
9
|
+
import '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/index';
|
|
10
|
+
import { isEqual } from '@codingame/monaco-vscode-api/vscode/vs/base/common/resources';
|
|
11
|
+
import { isStringArray } from '@codingame/monaco-vscode-api/vscode/vs/base/common/types';
|
|
12
|
+
import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
|
|
13
|
+
import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
|
|
14
|
+
import { ChatAIDisabledSettingId } from '@codingame/monaco-vscode-api/vscode/vs/platform/chat/common/chatSettings';
|
|
15
|
+
import { IConfigurationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service';
|
|
16
|
+
import { IDefaultAccountService } from '@codingame/monaco-vscode-api/vscode/vs/platform/defaultAccount/common/defaultAccount.service';
|
|
17
|
+
import { MutationUncertainError, ApiRequestError } from '@codingame/monaco-vscode-api/vscode/vs/platform/github/common/missionControl/missionControlClient';
|
|
18
|
+
import { IInstantiationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation';
|
|
19
|
+
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
|
|
20
|
+
import { getGitHubRepositoryId } from '@codingame/monaco-vscode-api/vscode/vs/platform/github/common/githubUrls';
|
|
21
|
+
import { RepositoryPicker } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/browser/agentSessions/repositoryPicker';
|
|
22
|
+
import { IGitHubService } from '@codingame/monaco-vscode-api/vscode/vs/sessions/contrib/github/browser/githubService.service';
|
|
23
|
+
import { AutomationUnavailableError, serializeAutomationEditableState, assertAutomationSessionTemplateAuthority } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/common/automations/automationService';
|
|
24
|
+
import { CHAT_CLOUD_AUTOMATIONS_ENABLED_SETTING, CHAT_AUTOMATIONS_ENABLED_SETTING } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/common/automations/automationsEnabled';
|
|
25
|
+
import { IChatEntitlementService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/chat/common/chatEntitlementService.service';
|
|
26
|
+
import { IWorkbenchGitHubService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/github/common/githubService.service';
|
|
27
|
+
import { GITHUB_REMOTE_FILE_SCHEME } from '@codingame/monaco-vscode-api/vscode/vs/sessions/services/sessions/common/session';
|
|
28
|
+
import { GitHubCloudAutomationStore } from './githubCloudAutomationStore.js';
|
|
29
|
+
import { observableValue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/observableValue';
|
|
30
|
+
import { derived } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/derived';
|
|
31
|
+
import { observableSignalFromEvent } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/observableSignalFromEvent';
|
|
32
|
+
import { autorun } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/reactions/autorun';
|
|
33
|
+
import { transaction } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/transaction';
|
|
34
|
+
|
|
35
|
+
let CloudAutomationStore = class CloudAutomationStore extends Disposable {
|
|
36
|
+
constructor(
|
|
37
|
+
providerId,
|
|
38
|
+
sessionTypeId,
|
|
39
|
+
resolveRepositoryUri,
|
|
40
|
+
configurationService,
|
|
41
|
+
defaultAccountService,
|
|
42
|
+
entitlementService,
|
|
43
|
+
instantiationService,
|
|
44
|
+
logService,
|
|
45
|
+
gitHubService,
|
|
46
|
+
workbenchGitHubService
|
|
47
|
+
) {
|
|
48
|
+
super();
|
|
49
|
+
this.providerId = providerId;
|
|
50
|
+
this.sessionTypeId = sessionTypeId;
|
|
51
|
+
this.defaultAccountService = defaultAccountService;
|
|
52
|
+
this.instantiationService = instantiationService;
|
|
53
|
+
this.logService = logService;
|
|
54
|
+
this.gitHubService = gitHubService;
|
|
55
|
+
this.store = observableValue(this, undefined);
|
|
56
|
+
this.refreshError = observableValue(this, undefined);
|
|
57
|
+
this.historyState = observableValue(this, "ready");
|
|
58
|
+
this.discoveryAttempts = 0;
|
|
59
|
+
this.historyScheduler = this._register(( new RunOnceScheduler(() => {
|
|
60
|
+
this.discoveryAttempts = Math.max(0, this.discoveryAttempts - 1);
|
|
61
|
+
this.refreshHistoryInBackground();
|
|
62
|
+
}, 15_000)));
|
|
63
|
+
this.catalogueState = derived(
|
|
64
|
+
this,
|
|
65
|
+
reader => this.refreshError.read(reader) ? "error" : this.store.read(reader)?.catalogueState.read(reader) ?? "unavailable"
|
|
66
|
+
);
|
|
67
|
+
this.unavailableReason = this.refreshError;
|
|
68
|
+
this.canCreateAutomation = derived(
|
|
69
|
+
this,
|
|
70
|
+
reader => this.enabled.read(reader) && this.catalogueState.read(reader) === "ready" && this.store.read(reader)?.mutationUncertain.read(reader) === false
|
|
71
|
+
);
|
|
72
|
+
this.automations = derived(
|
|
73
|
+
this,
|
|
74
|
+
reader => (this.store.read(reader)?.entries.read(reader) ?? []).map(entry => this.toAutomation(entry))
|
|
75
|
+
);
|
|
76
|
+
this.runs = derived(
|
|
77
|
+
this,
|
|
78
|
+
reader => (this.store.read(reader)?.history.read(reader) ?? []).map(entry => this.toRun(entry)).filter(run => run !== undefined)
|
|
79
|
+
);
|
|
80
|
+
this.configuration = {
|
|
81
|
+
sessionTypes: [sessionTypeId],
|
|
82
|
+
description: ( localize(5027, "Runs even when your computer is off, triggered on a schedule.")),
|
|
83
|
+
timeZone: "UTC",
|
|
84
|
+
targetChangeDisabledReason: ( localize(
|
|
85
|
+
5028,
|
|
86
|
+
"The repository and provider cannot be changed for an existing cloud automation. Duplicate this automation to use another target."
|
|
87
|
+
)),
|
|
88
|
+
tools: derived(this, reader => toToolCatalog(
|
|
89
|
+
this.store.read(reader)?.toolCatalog.read(reader),
|
|
90
|
+
!!this.store.read(reader)
|
|
91
|
+
)),
|
|
92
|
+
loadTools: () => this.store.get()?.loadTools(),
|
|
93
|
+
pickWorkspace: token => this.pickWorkspace(token),
|
|
94
|
+
getWorkspaceTarget: workspace => derived(
|
|
95
|
+
reader => this.store.read(reader)?.getWorkspaceTarget(workspace).read(reader) ?? {
|
|
96
|
+
disabledReason: ( localize(5029, "Cloud automations are unavailable."))
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
};
|
|
100
|
+
const configurationChanged = observableSignalFromEvent(this, configurationService.onDidChangeConfiguration);
|
|
101
|
+
const sentimentChanged = observableSignalFromEvent(this, entitlementService.onDidChangeSentiment);
|
|
102
|
+
const accountChanged = observableSignalFromEvent(this, defaultAccountService.onDidChangeDefaultAccount);
|
|
103
|
+
const clientChanged = observableSignalFromEvent(this, workbenchGitHubService.onDidChangeDefaultClient);
|
|
104
|
+
this.enabled = derived(this, reader => {
|
|
105
|
+
configurationChanged.read(reader);
|
|
106
|
+
sentimentChanged.read(reader);
|
|
107
|
+
return configurationService.getValue(CHAT_CLOUD_AUTOMATIONS_ENABLED_SETTING) === true && configurationService.getValue(CHAT_AUTOMATIONS_ENABLED_SETTING) === true && configurationService.getValue(ChatAIDisabledSettingId) !== true && !entitlementService.sentiment.hidden;
|
|
108
|
+
});
|
|
109
|
+
this._register(autorun(reader => {
|
|
110
|
+
accountChanged.read(reader);
|
|
111
|
+
clientChanged.read(reader);
|
|
112
|
+
this.historyScheduler.cancel();
|
|
113
|
+
this.discoveryAttempts = 0;
|
|
114
|
+
this.historyRefresh = undefined;
|
|
115
|
+
const account = defaultAccountService.currentDefaultAccount;
|
|
116
|
+
let store;
|
|
117
|
+
if (this.enabled.read(reader) && account && !account.enterprise) {
|
|
118
|
+
store = reader.store.add(
|
|
119
|
+
instantiationService.createInstance(GitHubCloudAutomationStore, resolveRepositoryUri)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
transaction(tx => {
|
|
123
|
+
this.store.set(store, tx);
|
|
124
|
+
this.refreshError.set(undefined, tx);
|
|
125
|
+
this.historyState.set("ready", tx);
|
|
126
|
+
});
|
|
127
|
+
if (store) {
|
|
128
|
+
void (async () => {
|
|
129
|
+
await Promise.resolve();
|
|
130
|
+
this.assertCurrentStore(store);
|
|
131
|
+
await this.refresh();
|
|
132
|
+
})().catch(error => {
|
|
133
|
+
if (!isCancellationError(error)) {
|
|
134
|
+
this.logService.warn("[CloudAutomations] Initial refresh failed", error);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
async pickWorkspace(token) {
|
|
141
|
+
const store = this.requireStore();
|
|
142
|
+
const resources = ( new DisposableStore());
|
|
143
|
+
const checkAccount = () => {
|
|
144
|
+
this.assertCurrentStore(store);
|
|
145
|
+
if (token.isCancellationRequested) {
|
|
146
|
+
throw ( new CancellationError());
|
|
147
|
+
}
|
|
148
|
+
if (this.gitHubService.enterpriseHost !== undefined) {
|
|
149
|
+
throw ( new Error(( localize(5030, "Select a private repository on GitHub.com."))));
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
try {
|
|
153
|
+
checkAccount();
|
|
154
|
+
await this.gitHubService.authenticateForRepositoryAccess(token);
|
|
155
|
+
checkAccount();
|
|
156
|
+
const picker = resources.add(this.instantiationService.createInstance(RepositoryPicker));
|
|
157
|
+
const result = await picker.pickRepository(async (query, requestToken) => {
|
|
158
|
+
checkAccount();
|
|
159
|
+
const repositories = await this.gitHubService.getRepositories(getGitHubRepositoryId(query.trim()) ?? query, requestToken);
|
|
160
|
+
checkAccount();
|
|
161
|
+
return repositories.filter(repository => repository.isPrivate).map(repository => repository.fullName);
|
|
162
|
+
}, {
|
|
163
|
+
allowRepositoryUrl: true,
|
|
164
|
+
placeholder: ( localize(5031, "Search for a private repository or paste a repository URL..."))
|
|
165
|
+
}, token);
|
|
166
|
+
checkAccount();
|
|
167
|
+
if (!result) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
const repository = getGitHubRepositoryId(result.repository ?? result.cloneUrl ?? "");
|
|
171
|
+
if (!repository) {
|
|
172
|
+
throw ( new Error(( localize(5030, "Select a private repository on GitHub.com."))));
|
|
173
|
+
}
|
|
174
|
+
return ( URI.from({
|
|
175
|
+
scheme: GITHUB_REMOTE_FILE_SCHEME,
|
|
176
|
+
authority: "github",
|
|
177
|
+
path: `/${repository}/HEAD`
|
|
178
|
+
}));
|
|
179
|
+
} finally {
|
|
180
|
+
resources.dispose();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async refresh() {
|
|
184
|
+
const store = this.requireStore();
|
|
185
|
+
try {
|
|
186
|
+
await store.refresh();
|
|
187
|
+
if (this.store.get() === store) {
|
|
188
|
+
this.refreshError.set(undefined, undefined);
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (this.store.get() === store && !isCancellationError(error)) {
|
|
192
|
+
this.refreshError.set(error instanceof Error ? error.message : ( localize(5032, "Cloud automations could not be refreshed.")), undefined);
|
|
193
|
+
}
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
this.assertCurrentStore(store);
|
|
197
|
+
await this.refreshHistory();
|
|
198
|
+
}
|
|
199
|
+
refreshHistory() {
|
|
200
|
+
if (this.historyRefresh) {
|
|
201
|
+
return this.historyRefresh;
|
|
202
|
+
}
|
|
203
|
+
const store = this.requireStore();
|
|
204
|
+
this.historyScheduler.cancel();
|
|
205
|
+
this.historyState.set("loading", undefined);
|
|
206
|
+
this.historyRefresh = (async () => {
|
|
207
|
+
try {
|
|
208
|
+
await store.refreshHistory();
|
|
209
|
+
this.assertCurrentStore(store);
|
|
210
|
+
this.historyState.set("ready", undefined);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
if (this.store.get() === store && !isCancellationError(error)) {
|
|
213
|
+
this.historyState.set("error", undefined);
|
|
214
|
+
}
|
|
215
|
+
throw error;
|
|
216
|
+
} finally {
|
|
217
|
+
if (this.store.get() === store && !this._store.isDisposed) {
|
|
218
|
+
this.historyRefresh = undefined;
|
|
219
|
+
if (this.historyState.get() === "ready" && (this.discoveryAttempts > 0 || ( this.runs.get().some(run => run.status === "pending" || run.status === "running")))) {
|
|
220
|
+
this.historyScheduler.schedule();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
})();
|
|
225
|
+
return this.historyRefresh;
|
|
226
|
+
}
|
|
227
|
+
refreshHistoryInBackground() {
|
|
228
|
+
void this.refreshHistory().catch(error => {
|
|
229
|
+
if (!isCancellationError(error)) {
|
|
230
|
+
this.logService.warn("[CloudAutomations] History refresh failed", error);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
getAutomation(id) {
|
|
235
|
+
return this.automations.get().find(automation => automation.id === id);
|
|
236
|
+
}
|
|
237
|
+
runsFor(id) {
|
|
238
|
+
return derived(
|
|
239
|
+
this,
|
|
240
|
+
reader => this.runs.read(reader).filter(run => run.automationId === id)
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
getActiveRunFor(id) {
|
|
244
|
+
return this.runs.get().filter(
|
|
245
|
+
run => run.automationId === id && (run.status === "pending" || run.status === "running")
|
|
246
|
+
).sort((a, b) => b.startedAt.localeCompare(a.startedAt))[0];
|
|
247
|
+
}
|
|
248
|
+
canRunAutomation(id) {
|
|
249
|
+
return this.canCreateAutomation.get() && this.getAutomation(id) !== undefined;
|
|
250
|
+
}
|
|
251
|
+
canUpdateAutomation(id) {
|
|
252
|
+
return this.canRunAutomation(id) && !this.getAutomation(id)?.readOnlyReason;
|
|
253
|
+
}
|
|
254
|
+
canDeleteAutomation(id) {
|
|
255
|
+
return this.canRunAutomation(id);
|
|
256
|
+
}
|
|
257
|
+
async createAutomation(options, guard) {
|
|
258
|
+
const store = this.requireWritableStore();
|
|
259
|
+
this.validateTarget(options.target);
|
|
260
|
+
validateLocalOptions(options);
|
|
261
|
+
const value = {
|
|
262
|
+
name: options.name,
|
|
263
|
+
description: "",
|
|
264
|
+
prompt: options.prompt,
|
|
265
|
+
disabled: !(options.enabled ?? true),
|
|
266
|
+
triggers: cloudAutomationTriggers(options.schedule),
|
|
267
|
+
...templateMutation(options.sessionTemplate),
|
|
268
|
+
...(options.modelId !== undefined && options.sessionTemplate === undefined ? {
|
|
269
|
+
model: options.modelId
|
|
270
|
+
} : {})
|
|
271
|
+
};
|
|
272
|
+
const entry = await store.create(options.target.folderUri, value, guard);
|
|
273
|
+
this.assertCurrentStore(store);
|
|
274
|
+
return this.toAutomation(entry);
|
|
275
|
+
}
|
|
276
|
+
async updateAutomation(id, patch) {
|
|
277
|
+
const store = this.requireWritableStore();
|
|
278
|
+
const {
|
|
279
|
+
entry
|
|
280
|
+
} = await store.update(this.requireEntry(id), current => this.updateValue(current, id, patch));
|
|
281
|
+
this.assertCurrentStore(store);
|
|
282
|
+
if (!entry) {
|
|
283
|
+
throw ( new AutomationUnavailableError(( localize(
|
|
284
|
+
5033,
|
|
285
|
+
"This cloud automation is no longer available. Refresh the catalogue."
|
|
286
|
+
))));
|
|
287
|
+
}
|
|
288
|
+
return this.toAutomation(entry);
|
|
289
|
+
}
|
|
290
|
+
async updateAutomationIfUnchanged(id, patch, expected, guard) {
|
|
291
|
+
const store = this.requireWritableStore();
|
|
292
|
+
const entry = this.requireEntry(id);
|
|
293
|
+
const result = await store.update(entry, current => {
|
|
294
|
+
const latest = this.toAutomation({
|
|
295
|
+
repository: entry.repository,
|
|
296
|
+
definition: current
|
|
297
|
+
});
|
|
298
|
+
return serializeAutomationEditableState(latest) === serializeAutomationEditableState(expected) ? this.updateValue(current, id, patch) : undefined;
|
|
299
|
+
}, guard);
|
|
300
|
+
this.assertCurrentStore(store);
|
|
301
|
+
if (!result.entry) {
|
|
302
|
+
return {
|
|
303
|
+
kind: "conflict",
|
|
304
|
+
current: undefined
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const automation = this.toAutomation(result.entry);
|
|
308
|
+
return result.updated ? {
|
|
309
|
+
kind: "updated",
|
|
310
|
+
automation
|
|
311
|
+
} : {
|
|
312
|
+
kind: "conflict",
|
|
313
|
+
current: automation
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
async deleteAutomation(id, guard) {
|
|
317
|
+
await this.requireWritableStore().delete(this.requireEntry(id), guard);
|
|
318
|
+
}
|
|
319
|
+
async runAutomation(id, token = CancellationToken.None) {
|
|
320
|
+
const store = this.requireWritableStore();
|
|
321
|
+
try {
|
|
322
|
+
await store.run(this.requireEntry(id), token);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if ((error instanceof MutationUncertainError || error instanceof ApiRequestError && error.outcome === "indeterminate") && this.store.get() === store) {
|
|
325
|
+
this.discoveryAttempts = 4;
|
|
326
|
+
this.refreshHistoryInBackground();
|
|
327
|
+
}
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
this.assertCurrentStore(store);
|
|
331
|
+
this.discoveryAttempts = 4;
|
|
332
|
+
this.refreshHistoryInBackground();
|
|
333
|
+
return {
|
|
334
|
+
kind: "accepted"
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
canStopRun(run) {
|
|
338
|
+
return this.canRunAutomation(run.automationId) && ( this.runs.get().some(
|
|
339
|
+
current => current.id === run.id && (current.status === "pending" || current.status === "running")
|
|
340
|
+
));
|
|
341
|
+
}
|
|
342
|
+
async stopRun(run) {
|
|
343
|
+
const store = this.requireWritableStore();
|
|
344
|
+
const entry = store.history.get().find(entry => this.toRun(entry)?.id === run.id);
|
|
345
|
+
if (!entry || !this.canStopRun(run)) {
|
|
346
|
+
throw ( new AutomationUnavailableError(( localize(5034, "Unable to stop this cloud automation run."))));
|
|
347
|
+
}
|
|
348
|
+
await store.stop(entry);
|
|
349
|
+
this.assertCurrentStore(store);
|
|
350
|
+
this.discoveryAttempts = 4;
|
|
351
|
+
this.refreshHistoryInBackground();
|
|
352
|
+
}
|
|
353
|
+
updateValue(definition, id, patch) {
|
|
354
|
+
validateLocalOptions(patch);
|
|
355
|
+
if (patch.sessionTemplate === null) {
|
|
356
|
+
throw ( new Error(( localize(5035, "Resetting cloud automation configuration is not supported."))));
|
|
357
|
+
}
|
|
358
|
+
const current = this.getAutomation(id);
|
|
359
|
+
assertAutomationSessionTemplateAuthority(current, patch);
|
|
360
|
+
if (cloudAutomationSchedule(definition.triggers).interval === "custom") {
|
|
361
|
+
throw ( new Error(( localize(5036, "This automation uses triggers that cannot be edited in VS Code."))));
|
|
362
|
+
}
|
|
363
|
+
if (patch.target) {
|
|
364
|
+
this.validateTarget(patch.target);
|
|
365
|
+
if (current.target.kind !== "workspace" || !isEqual(current.target.folderUri, patch.target.folderUri)) {
|
|
366
|
+
throw ( new Error(( localize(5037, "Duplicate this automation to use another repository."))));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
let triggers = patch.schedule ? cloudAutomationTriggers(patch.schedule) : undefined;
|
|
370
|
+
if (triggers?.interval && definition.triggers?.interval) {
|
|
371
|
+
const {
|
|
372
|
+
types: _types,
|
|
373
|
+
hour_utc: _hour,
|
|
374
|
+
minute_utc: _minute,
|
|
375
|
+
day_of_week: _day,
|
|
376
|
+
...otherFields
|
|
377
|
+
} = definition.triggers.interval;
|
|
378
|
+
triggers = {
|
|
379
|
+
interval: {
|
|
380
|
+
...otherFields,
|
|
381
|
+
...triggers.interval
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
...(patch.name !== undefined ? {
|
|
387
|
+
name: patch.name
|
|
388
|
+
} : {}),
|
|
389
|
+
...(patch.prompt !== undefined ? {
|
|
390
|
+
prompt: patch.prompt
|
|
391
|
+
} : {}),
|
|
392
|
+
...(patch.enabled !== undefined ? {
|
|
393
|
+
disabled: !patch.enabled
|
|
394
|
+
} : {}),
|
|
395
|
+
...(triggers !== undefined ? {
|
|
396
|
+
triggers
|
|
397
|
+
} : {}),
|
|
398
|
+
...(patch.sessionTemplate !== undefined ? templateMutation(patch.sessionTemplate) : {}),
|
|
399
|
+
...(patch.modelId !== undefined ? {
|
|
400
|
+
model: patch.modelId ?? ""
|
|
401
|
+
} : {})
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
validateTarget(target) {
|
|
405
|
+
if (target.providerId !== this.providerId || target.sessionTypeId !== this.sessionTypeId || target.kind !== "workspace" || target.isolation.kind !== "default") {
|
|
406
|
+
throw ( new Error(( localize(
|
|
407
|
+
5038,
|
|
408
|
+
"Select a GitHub repository with default isolation for this cloud automation."
|
|
409
|
+
))));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
requireStore() {
|
|
413
|
+
const store = this.store.get();
|
|
414
|
+
if (!store || !this.enabled.get() || this._store.isDisposed) {
|
|
415
|
+
throw ( new AutomationUnavailableError(( localize(
|
|
416
|
+
5039,
|
|
417
|
+
"Sign in to GitHub.com and enable cloud automations before continuing."
|
|
418
|
+
))));
|
|
419
|
+
}
|
|
420
|
+
return store;
|
|
421
|
+
}
|
|
422
|
+
assertCurrentStore(store) {
|
|
423
|
+
if (this.store.get() !== store || this._store.isDisposed) {
|
|
424
|
+
throw ( new CancellationError());
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
requireWritableStore() {
|
|
428
|
+
const store = this.requireStore();
|
|
429
|
+
if (!this.canCreateAutomation.get()) {
|
|
430
|
+
throw ( new AutomationUnavailableError(( localize(5040, "Refresh cloud automations before submitting another request."))));
|
|
431
|
+
}
|
|
432
|
+
return store;
|
|
433
|
+
}
|
|
434
|
+
requireEntry(id) {
|
|
435
|
+
const entry = this.requireStore().entries.get().find(entry => this.toAutomation(entry).id === id);
|
|
436
|
+
if (!entry) {
|
|
437
|
+
throw ( new AutomationUnavailableError(( localize(
|
|
438
|
+
5033,
|
|
439
|
+
"This cloud automation is no longer available. Refresh the catalogue."
|
|
440
|
+
))));
|
|
441
|
+
}
|
|
442
|
+
return entry;
|
|
443
|
+
}
|
|
444
|
+
toAutomation(entry) {
|
|
445
|
+
const {
|
|
446
|
+
definition,
|
|
447
|
+
repository
|
|
448
|
+
} = entry;
|
|
449
|
+
const schedule = cloudAutomationSchedule(definition.triggers);
|
|
450
|
+
return {
|
|
451
|
+
id: JSON.stringify([
|
|
452
|
+
this.providerId,
|
|
453
|
+
this.defaultAccountService.currentDefaultAccount?.accountName,
|
|
454
|
+
repository.owner.toLowerCase(),
|
|
455
|
+
repository.name.toLowerCase(),
|
|
456
|
+
definition.id
|
|
457
|
+
]),
|
|
458
|
+
name: definition.name,
|
|
459
|
+
prompt: definition.prompt,
|
|
460
|
+
schedule,
|
|
461
|
+
targetDisplay: {
|
|
462
|
+
label: `${repository.owner}/${repository.name}`,
|
|
463
|
+
icon: Codicon.cloud
|
|
464
|
+
},
|
|
465
|
+
externalResource: ( URI.from({
|
|
466
|
+
scheme: Schemas.https,
|
|
467
|
+
authority: "github.com",
|
|
468
|
+
path: `/${repository.owner}/${repository.name}/agents/automations/${definition.id}`
|
|
469
|
+
})),
|
|
470
|
+
target: {
|
|
471
|
+
kind: "workspace",
|
|
472
|
+
providerId: this.providerId,
|
|
473
|
+
sessionTypeId: this.sessionTypeId,
|
|
474
|
+
isolation: {
|
|
475
|
+
kind: "default"
|
|
476
|
+
},
|
|
477
|
+
folderUri: ( URI.from({
|
|
478
|
+
scheme: GITHUB_REMOTE_FILE_SCHEME,
|
|
479
|
+
authority: "github",
|
|
480
|
+
path: `/${repository.owner}/${repository.name}/HEAD`
|
|
481
|
+
}))
|
|
482
|
+
},
|
|
483
|
+
sessionTemplate: {
|
|
484
|
+
...(definition.model ? {
|
|
485
|
+
modelId: definition.model
|
|
486
|
+
} : {}),
|
|
487
|
+
config: {
|
|
488
|
+
...(definition.tools ? {
|
|
489
|
+
tools: definition.tools
|
|
490
|
+
} : {}),
|
|
491
|
+
...(definition.reasoning_effort ? {
|
|
492
|
+
reasoningEffort: definition.reasoning_effort
|
|
493
|
+
} : {})
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
enabled: definition.disabled !== true,
|
|
497
|
+
createdAt: definition.created_at,
|
|
498
|
+
updatedAt: definition.updated_at,
|
|
499
|
+
...(schedule.interval === "custom" ? {
|
|
500
|
+
readOnlyReason: ( localize(5041, "This automation uses triggers that cannot be edited in VS Code."))
|
|
501
|
+
} : {})
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
toRun(
|
|
505
|
+
{
|
|
506
|
+
entry,
|
|
507
|
+
task
|
|
508
|
+
}
|
|
509
|
+
) {
|
|
510
|
+
const status = cloudTaskStatus(task);
|
|
511
|
+
if (status === undefined) {
|
|
512
|
+
this.logService.warn(
|
|
513
|
+
`[CloudAutomations] Skipping run ${task.id} with unsupported state: ${task.state}`
|
|
514
|
+
);
|
|
515
|
+
return undefined;
|
|
516
|
+
}
|
|
517
|
+
const automationId = this.toAutomation(entry).id;
|
|
518
|
+
return {
|
|
519
|
+
id: JSON.stringify([automationId, task.id]),
|
|
520
|
+
automationId,
|
|
521
|
+
status,
|
|
522
|
+
trigger: "external",
|
|
523
|
+
startedAt: task.created_at,
|
|
524
|
+
updatedAt: task.updated_at,
|
|
525
|
+
...(status === "completed" || status === "failed" ? {
|
|
526
|
+
completedAt: task.updated_at
|
|
527
|
+
} : {}),
|
|
528
|
+
...(status === "failed" ? {
|
|
529
|
+
errorMessage: task.status || task.state
|
|
530
|
+
} : {}),
|
|
531
|
+
...(task.state === "waiting_for_user" ? {
|
|
532
|
+
needsInput: true,
|
|
533
|
+
statusDescription: ( localize(5042, "Needs input on GitHub"))
|
|
534
|
+
} : {}),
|
|
535
|
+
externalResource: ( URI.from({
|
|
536
|
+
scheme: Schemas.https,
|
|
537
|
+
authority: "github.com",
|
|
538
|
+
path: `/${entry.repository.owner}/${entry.repository.name}/tasks/${task.id}`
|
|
539
|
+
}))
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
CloudAutomationStore = ( __decorate([( __param(3, IConfigurationService)), ( __param(4, IDefaultAccountService)), ( __param(5, IChatEntitlementService)), ( __param(6, IInstantiationService)), ( __param(7, ILogService)), ( __param(8, IGitHubService)), ( __param(9, IWorkbenchGitHubService))], CloudAutomationStore));
|
|
544
|
+
function toToolCatalog(catalog, available) {
|
|
545
|
+
if (!available) {
|
|
546
|
+
return {
|
|
547
|
+
kind: "error",
|
|
548
|
+
message: ( localize(5043, "Cloud automations are unavailable."))
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
if (catalog?.kind === "error") {
|
|
552
|
+
return {
|
|
553
|
+
kind: "error",
|
|
554
|
+
message: ( localize(5044, "Available tools could not be loaded."))
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
if (catalog?.kind !== "ready") {
|
|
558
|
+
return {
|
|
559
|
+
kind: "loading"
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
return {
|
|
563
|
+
kind: "ready",
|
|
564
|
+
groups: catalog.groups.map(group => ({
|
|
565
|
+
id: group.id,
|
|
566
|
+
label: group.name || group.title || group.id,
|
|
567
|
+
tools: group.tools.map(tool => ({
|
|
568
|
+
id: tool.id,
|
|
569
|
+
label: tool.name || tool.title || tool.id,
|
|
570
|
+
description: tool.description || undefined
|
|
571
|
+
}))
|
|
572
|
+
}))
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function validateLocalOptions(options) {
|
|
576
|
+
if (options.mode !== undefined || options.permissionLevel !== undefined) {
|
|
577
|
+
throw ( new Error(( localize(5045, "Cloud automations do not support local mode or approval settings."))));
|
|
578
|
+
}
|
|
579
|
+
if ((options.name !== undefined && !options.name.trim()) || (options.prompt !== undefined && !options.prompt.trim())) {
|
|
580
|
+
throw ( new Error(( localize(5046, "An automation name and prompt are required."))));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function templateMutation(template) {
|
|
584
|
+
const tools = template?.config?.tools;
|
|
585
|
+
const reasoning = template?.config?.reasoningEffort;
|
|
586
|
+
if (template?.agent || template?.modelConfiguration || ( ( Object.keys(template?.config ?? {})).some(key => key !== "tools" && key !== "reasoningEffort")) || (tools !== undefined && !isStringArray(tools)) || (reasoning !== undefined && typeof reasoning !== "string")) {
|
|
587
|
+
throw ( new Error(( localize(5047, "This session configuration is not supported by cloud automations."))));
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
model: template?.modelId ?? "",
|
|
591
|
+
...(isStringArray(tools) ? {
|
|
592
|
+
tools
|
|
593
|
+
} : {}),
|
|
594
|
+
...(typeof reasoning === "string" ? {
|
|
595
|
+
reasoning_effort: reasoning
|
|
596
|
+
} : {})
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
function cloudAutomationSchedule(triggers) {
|
|
600
|
+
const base = {
|
|
601
|
+
scheduleHour: 0,
|
|
602
|
+
scheduleMinute: 0,
|
|
603
|
+
scheduleDay: 0,
|
|
604
|
+
timeZone: "UTC"
|
|
605
|
+
};
|
|
606
|
+
if (( Object.keys(triggers ?? {})).length === 0) {
|
|
607
|
+
return {
|
|
608
|
+
...base,
|
|
609
|
+
interval: "manual"
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
const trigger = triggers?.interval;
|
|
613
|
+
if (( Object.keys(triggers ?? {})).length !== 1 || !trigger || trigger.types?.length !== 1) {
|
|
614
|
+
return {
|
|
615
|
+
...base,
|
|
616
|
+
interval: "custom"
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
const interval = trigger.types[0];
|
|
620
|
+
if (interval === "hourly") {
|
|
621
|
+
return {
|
|
622
|
+
...base,
|
|
623
|
+
interval
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
const hour = trigger.hour_utc;
|
|
627
|
+
const minute = trigger.minute_utc ?? 0;
|
|
628
|
+
const day = trigger.day_of_week ?? 0;
|
|
629
|
+
if ((interval === "daily" || interval === "weekly") && typeof hour === "number" && Number.isInteger(hour) && hour >= 0 && hour < 24 && typeof minute === "number" && [0, 15, 30, 45].includes(minute) && typeof day === "number" && Number.isInteger(day) && day >= 0 && day < 7) {
|
|
630
|
+
return {
|
|
631
|
+
...base,
|
|
632
|
+
interval,
|
|
633
|
+
scheduleHour: hour,
|
|
634
|
+
scheduleMinute: minute,
|
|
635
|
+
scheduleDay: day
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
return {
|
|
639
|
+
...base,
|
|
640
|
+
interval: "custom"
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
function cloudAutomationTriggers(schedule) {
|
|
644
|
+
if (schedule.interval === "manual") {
|
|
645
|
+
return {};
|
|
646
|
+
}
|
|
647
|
+
if (schedule.interval === "hourly") {
|
|
648
|
+
return {
|
|
649
|
+
interval: {
|
|
650
|
+
types: ["hourly"]
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
const minute = schedule.scheduleMinute;
|
|
655
|
+
if (schedule.timeZone !== "UTC" || (schedule.interval !== "daily" && schedule.interval !== "weekly") || !Number.isInteger(schedule.scheduleHour) || schedule.scheduleHour < 0 || schedule.scheduleHour > 23 || (minute !== 0 && minute !== 15 && minute !== 30 && minute !== 45) || !Number.isInteger(schedule.scheduleDay) || schedule.scheduleDay < 0 || schedule.scheduleDay > 6) {
|
|
656
|
+
throw ( new Error(( localize(
|
|
657
|
+
5048,
|
|
658
|
+
"Choose a daily or weekly UTC schedule with minutes 00, 15, 30, or 45."
|
|
659
|
+
))));
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
interval: {
|
|
663
|
+
types: [schedule.interval],
|
|
664
|
+
hour_utc: schedule.scheduleHour,
|
|
665
|
+
minute_utc: minute,
|
|
666
|
+
...(schedule.interval === "weekly" ? {
|
|
667
|
+
day_of_week: schedule.scheduleDay
|
|
668
|
+
} : {})
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
function cloudTaskStatus(task) {
|
|
673
|
+
const state = task.state;
|
|
674
|
+
switch (state) {
|
|
675
|
+
case "queued":
|
|
676
|
+
return "pending";
|
|
677
|
+
case "in_progress":
|
|
678
|
+
case "running":
|
|
679
|
+
case "waiting_for_user":
|
|
680
|
+
return "running";
|
|
681
|
+
case "completed":
|
|
682
|
+
case "idle":
|
|
683
|
+
return "completed";
|
|
684
|
+
case "failed":
|
|
685
|
+
case "timed_out":
|
|
686
|
+
case "cancelled":
|
|
687
|
+
case "canceled":
|
|
688
|
+
case "error":
|
|
689
|
+
return "failed";
|
|
690
|
+
default:
|
|
691
|
+
return undefined;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export { CloudAutomationStore, cloudAutomationSchedule, cloudAutomationTriggers };
|