copilot-operator 0.1.20 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/src/api/operator.controller.js +49 -0
  2. package/dist/src/api/operator.service.js +181 -7
  3. package/dist/src/orchestrator/taskRunner.js +14 -2
  4. package/dist/src/plan/systemGuide.js +143 -15
  5. package/dist/src/session/exports.js +70 -1
  6. package/dist/src/session/runLog.js +60 -0
  7. package/dist/src/vcs/git.js +15 -3
  8. package/dist/src/vcs/inputs.js +26 -2
  9. package/dist/src/vcs/prepareFromRemote.js +293 -0
  10. package/dist/src/vcs/remoteAuth.js +61 -0
  11. package/dist/src/vcs/runPreflight.js +286 -0
  12. package/dist/src/vcs/taskVcs.js +42 -6
  13. package/dist/web/404.html +2 -2
  14. package/dist/web/_next/static/chunks/549-df7a4876917615da.js +1 -0
  15. package/dist/web/_next/static/chunks/867-a67ea8ba3f9b24aa.js +1 -0
  16. package/dist/web/_next/static/chunks/app/defaults/page-0e778dafc69afbd3.js +1 -0
  17. package/dist/web/_next/static/chunks/app/page-0a861b38da0a1c51.js +1 -0
  18. package/dist/web/_next/static/chunks/app/sessions/view/page-9b66b729748595bb.js +1 -0
  19. package/dist/web/appearance.html +2 -2
  20. package/dist/web/appearance.txt +10 -10
  21. package/dist/web/defaults.html +2 -2
  22. package/dist/web/defaults.txt +10 -10
  23. package/dist/web/history.html +2 -2
  24. package/dist/web/history.txt +10 -10
  25. package/dist/web/import.html +2 -2
  26. package/dist/web/import.txt +10 -10
  27. package/dist/web/index.html +2 -2
  28. package/dist/web/index.txt +10 -10
  29. package/dist/web/level1.html +2 -2
  30. package/dist/web/level1.txt +10 -10
  31. package/dist/web/presets.html +2 -2
  32. package/dist/web/presets.txt +10 -10
  33. package/dist/web/sessions/view.html +2 -2
  34. package/dist/web/sessions/view.txt +10 -10
  35. package/dist/web/system.html +2 -2
  36. package/dist/web/system.txt +10 -10
  37. package/package.json +150 -148
  38. package/dist/web/_next/static/chunks/549-5d7c845fb0a27dbe.js +0 -1
  39. package/dist/web/_next/static/chunks/867-d9c54f7a2b285b3f.js +0 -1
  40. package/dist/web/_next/static/chunks/app/defaults/page-fd819bbfa7f33982.js +0 -1
  41. package/dist/web/_next/static/chunks/app/page-409e37e208dc12e5.js +0 -1
  42. package/dist/web/_next/static/chunks/app/sessions/view/page-99f9153fd75078d0.js +0 -1
  43. /package/dist/web/_next/static/{P0lpNIJvdSO3yuWRpIPQf → -dZtu-yqxc-HXzYTmqYSc}/_buildManifest.js +0 -0
  44. /package/dist/web/_next/static/{P0lpNIJvdSO3yuWRpIPQf → -dZtu-yqxc-HXzYTmqYSc}/_ssgManifest.js +0 -0
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Version control for a run, made ready on the run screen before anything opens the browser.
3
+ *
4
+ * From the feedback of 2026-10-02: the run screen named the right problem once per session, left the
5
+ * run buttons on, and told the operator to approve a list they could not see; and version control was
6
+ * only checked once the browser was open and the first task had begun. Version control should be light
7
+ * and never in the way: every problem comes with the fix, done from here, said exactly before it is
8
+ * done, and nothing about version control is left to find out after the browser opens.
9
+ *
10
+ * So the sessions of a run are grouped by repository — one problem is one problem, however many
11
+ * sessions share the repository — and each group says where it stands (the repository, its branch and
12
+ * HEAD, the base branch the plan asked for and its HEAD, the sessions, the input patterns, the matched
13
+ * files with status and size, the unrelated uncommitted files apart) and what can be done:
14
+ *
15
+ * review-inputs show the input files; changes nothing
16
+ * snapshot-on-base the starting snapshot on the base branch: that branch's tree plus the approved
17
+ * input files only, committed with a temporary index — no branch switched, the
18
+ * operator's checkout untouched. Every session of the run in that repository
19
+ * that starts from that branch starts from it; a chained one carries it on.
20
+ * snapshot-here the starting snapshot where the repository is, as the session page takes it
21
+ * use-current-branch the sessions start from the branch the repository is on instead
22
+ * allow-snapshot uncommitted changes become a starting snapshot to approve, instead of a refusal
23
+ *
24
+ * The run is refused before the browser opens while any group is not ready (see the service).
25
+ */
26
+ import { join, resolve } from 'node:path';
27
+ import { branchNameFrom, freeBranchName, git } from './git.js';
28
+ import { commitOnto, dirtyBesidesInputs, inputSettings, inputSumsAt, missingInputPatterns } from './inputs.js';
29
+ import { dirtyPolicy, takeSnapshot } from './snapshot.js';
30
+ import { repoDirOf, vcsPreflight } from './taskVcs.js';
31
+ import { artifactPatterns } from './artifacts.js';
32
+ import { inScope } from './scope.js';
33
+ const norm = (dir) => dir.trim().replace(/[\\/]+$/, '').replace(/\//g, '\\').toLowerCase();
34
+ const short = (c) => (c ?? '').slice(0, 8);
35
+ const names = (list) => list.map((s) => `"${s.name}"`).join(', ');
36
+ /** The groups, one per repository, for the sessions of a run in run order. */
37
+ export async function runVcsPreflight(sessions, allSessions) {
38
+ const byRepo = new Map();
39
+ for (const s of sessions) {
40
+ if (!s.vcs?.enabled || !repoDirOf(s))
41
+ continue;
42
+ const key = norm(repoDirOf(s));
43
+ byRepo.set(key, [...(byRepo.get(key) ?? []), s]);
44
+ }
45
+ const groups = [];
46
+ for (const list of byRepo.values())
47
+ groups.push(await groupFor(list, allSessions));
48
+ return groups;
49
+ }
50
+ async function groupFor(list, allSessions) {
51
+ const first = list[0];
52
+ const repoDir = repoDirOf(first);
53
+ const branchR = await git(repoDir, ['rev-parse', '--abbrev-ref', 'HEAD']);
54
+ const headR = await git(repoDir, ['rev-parse', '--verify', '--quiet', 'HEAD']);
55
+ const branch = branchR.ok && branchR.stdout !== 'HEAD' ? branchR.stdout : null;
56
+ const head = headR.ok && headR.stdout ? headR.stdout : null;
57
+ const startFrom = first.vcs?.startFrom ?? 'head';
58
+ const baseBranch = startFrom === 'branch' || startFrom === 'previous-session' ? first.vcs?.baseBranch?.trim() || 'main' : undefined;
59
+ const baseHead = baseBranch ? (await git(repoDir, ['rev-parse', '--verify', '--quiet', `refs/heads/${baseBranch}^{commit}`])).stdout || null : undefined;
60
+ const settings = inputSettings(first.vcs);
61
+ const sessions = list.map((s, i) => {
62
+ const sf = s.vcs?.startFrom ?? 'head';
63
+ const role = i === 0 ? 'first' : sf === 'previous-session' ? 'inherits' : sf === 'branch' && (s.vcs?.baseBranch?.trim() || 'main') === baseBranch && !s.vcsBaseCommit ? 'shares' : 'own';
64
+ return { id: s.id, name: s.name, startFrom: sf, started: !!s.vcsBaseCommit, role };
65
+ });
66
+ const pre = await vcsPreflight(first, allSessions);
67
+ const plan = pre.snapshot;
68
+ const inputs = (plan?.entries ?? []).filter((e) => e.input);
69
+ /*
70
+ * Every uncommitted file that is not an input, read from the tree itself: the snapshot's list stops at
71
+ * the first thing in the way (other changes beside the inputs) and would leave them out.
72
+ */
73
+ const inputPaths = new Set(inputs.map((e) => e.path));
74
+ const artifacts = artifactPatterns(first.vcs);
75
+ const reasons = new Map((plan?.entries ?? []).filter((e) => !e.input && e.reason).map((e) => [e.path, e.reason]));
76
+ const unrelated = pre.problem === 'off'
77
+ ? []
78
+ : (await dirtyBesidesInputs(repoDir, first.vcsStart)).other
79
+ .filter((path) => !inputPaths.has(path) && !(artifacts.length > 0 && inScope(path, artifacts)))
80
+ .map((path) => ({ path, ...(reasons.has(path) ? { reason: reasons.get(path) } : {}) }));
81
+ // Before anything is approved: a pattern that matches nothing is said here, not after the browser opens.
82
+ let problem = pre.ok ? undefined : pre.problem;
83
+ // Problems no approval or setting of version control's own can get past: they need the operator.
84
+ let blocking = false;
85
+ if (!first.vcsBaseCommit && settings) {
86
+ const topR = await git(repoDir, ['rev-parse', '--show-toplevel']);
87
+ const missing = topR.ok ? await missingInputPatterns(first, repoDir, resolve(topR.stdout), allSessions) : [];
88
+ if (missing.length > 0) {
89
+ blocking = true;
90
+ problem = `the input file pattern(s) ${missing.map((m) => `"${m}"`).join(', ')} match no file in the project: put the files in, or correct "Input files" on the session's page.`;
91
+ }
92
+ }
93
+ /*
94
+ * The branches the sessions name, before the browser opens rather than at their first task: a
95
+ * branch to carry on that is not there, a base branch that is not there.
96
+ */
97
+ for (const s of list) {
98
+ if (s.vcsBaseCommit)
99
+ continue;
100
+ const named = s.vcs?.startFrom === 'existing-branch' ? s.vcs.existingBranch?.trim() : s.vcs?.startFrom === 'branch' ? s.vcs.baseBranch?.trim() || 'main' : '';
101
+ if (!named)
102
+ continue;
103
+ const there = (await git(repoDir, ['rev-parse', '--verify', '--quiet', `refs/heads/${named}^{commit}`])).stdout;
104
+ if (!there) {
105
+ blocking = true;
106
+ problem = `${problem ? `${problem} ` : ''}"${s.name}" is set to ${s.vcs?.startFrom === 'existing-branch' ? 'carry on' : 'start from'} the local branch "${named}", and there is no such branch: create it, or change "Start from" on the session's page.`;
107
+ }
108
+ }
109
+ const noCommits = first.vcs?.commitOnFinish === false;
110
+ const ready = blocking ? false : !problem ? true : noCommits && !plan?.needed ? true : !!plan?.needed && plan.ok && !plan.requireApproval;
111
+ const sharing = sessions.filter((s) => s.role === 'first' || s.role === 'shares');
112
+ const inheriting = sessions.filter((s) => s.role === 'inherits');
113
+ const blockedInputs = inputs.filter((e) => e.allowed.length === 0);
114
+ const prefix = first.vcs?.branchPrefix || 'cop/';
115
+ const baselineName = `${branchNameFrom(['baseline'], prefix)}/${first.id}`;
116
+ const actions = [];
117
+ if (inputs.length > 0) {
118
+ actions.push({ id: 'review-inputs', available: true, result: `Shows the ${inputs.length} input file(s) with their git status and size. Changes nothing.` });
119
+ }
120
+ if (baseBranch && !first.vcsBaseCommit && inputs.length > 0) {
121
+ const why = !baseHead
122
+ ? `there is no local branch "${baseBranch}"`
123
+ : unrelated.length > 0
124
+ ? `besides the input files there are other uncommitted changes (${unrelated.slice(0, 5).map((u) => u.path).join(', ')}${unrelated.length > 5 ? '…' : ''}): commit or stash them first, or use the current branch instead`
125
+ : blockedInputs.length > 0
126
+ ? `${blockedInputs.map((b) => b.path).join(', ')} cannot be taken as input files`
127
+ : problem && /match no file/.test(problem)
128
+ ? problem
129
+ : undefined;
130
+ actions.push({
131
+ id: 'snapshot-on-base',
132
+ available: !why,
133
+ recommended: !why && branch !== baseBranch,
134
+ ...(why ? { why } : {}),
135
+ result: `Commits the ${inputs.length} input file(s) on top of "${baseBranch}" (${short(baseHead)}) as "Capture user-provided inputs", on the new branch ${baselineName}, ` +
136
+ `without switching branches: your checkout${branch ? ` of "${branch}"` : ''} and the files stay exactly as they are. ` +
137
+ `${names(sharing)} start from it${inheriting.length > 0 ? `; ${names(inheriting)} carry it on through the chain` : ''}.`,
138
+ });
139
+ }
140
+ if (plan?.needed && plan.ok && plan.requireApproval) {
141
+ actions.push({
142
+ id: 'snapshot-here',
143
+ available: true,
144
+ result: plan.recapture
145
+ ? `Commits the ${plan.entries.length} changed input file(s) on "${first.name}"'s line of work (${plan.baselineBranch}); the next task starts from them.`
146
+ : `Commits the ${plan.entries.filter((e) => e.choice === 'include').length} file(s) ticked on top of "${branch ?? 'HEAD'}" (${short(head)}) as one commit on the new branch ${plan.baselineBranch}, and puts the repository on it; "${branch ?? 'HEAD'}" itself does not move.` +
147
+ (plan.entries.some((e) => e.choice === 'leave-out') ? ' Files left out go into .git/info/exclude.' : ''),
148
+ });
149
+ }
150
+ if (baseBranch && startFrom === 'branch' && !first.vcsBaseCommit && branch && branch !== baseBranch) {
151
+ actions.push({
152
+ id: 'use-current-branch',
153
+ available: true,
154
+ result: `${names(sharing)} start from "${branch}" (${short(head)}) instead of "${baseBranch}": their "Start from" becomes "From wherever the repository is". ` +
155
+ 'Nothing is committed yet; a snapshot on that branch is offered next if one is needed.',
156
+ });
157
+ }
158
+ if (!plan?.needed && unrelated.length > 0 && dirtyPolicy(first.vcs).policy === 'reject' && !first.vcsBaseCommit && !noCommits) {
159
+ actions.push({
160
+ id: 'allow-snapshot',
161
+ available: true,
162
+ result: `"Uncommitted changes" for "${first.name}" becomes "Take them as a starting snapshot": the files are listed here for you to approve. Nothing is committed yet.`,
163
+ });
164
+ }
165
+ return {
166
+ repoDir,
167
+ ready,
168
+ ...(problem && !ready ? { problem: problem.replace(/^There are uncommitted changes, and no starting snapshot can be taken: /, '') } : {}),
169
+ branch,
170
+ head,
171
+ ...(baseBranch ? { baseBranch, baseHead } : {}),
172
+ sessions,
173
+ inputPatterns: settings?.patterns ?? [],
174
+ inputs,
175
+ unrelated,
176
+ entries: plan?.entries ?? [],
177
+ actions,
178
+ };
179
+ }
180
+ /**
181
+ * Does one action for one repository of a run, after checking it is still what was shown: the group is
182
+ * worked out again, and an action that is no longer available, or a list of files that changed, is
183
+ * refused with nothing done.
184
+ */
185
+ export async function runVcsPrepare(sessions, repoDir, action, choices, bus, saveSession, allSessions) {
186
+ const list = sessions.filter((s) => s.vcs?.enabled && norm(repoDirOf(s)) === norm(repoDir));
187
+ if (list.length === 0)
188
+ return { ok: false, problem: 'none of the run\'s sessions works in that repository.' };
189
+ const group = await groupFor(list, allSessions);
190
+ const offered = group.actions.find((a) => a.id === action);
191
+ if (!offered || !offered.available)
192
+ return { ok: false, problem: offered?.why ?? 'that is not offered for this repository now. Look again; nothing was done.' };
193
+ const first = list[0];
194
+ if (action === 'review-inputs')
195
+ return { ok: true, result: offered.result };
196
+ if (action === 'use-current-branch') {
197
+ const moved = group.sessions.filter((s) => (s.role === 'first' || s.role === 'shares') && !s.started);
198
+ for (const s of moved) {
199
+ await saveSession(s.id, (x) => {
200
+ if (x.vcs)
201
+ x.vcs.startFrom = 'head';
202
+ x.vcsBaseCommit = undefined;
203
+ x.vcsStart = undefined;
204
+ });
205
+ }
206
+ return { ok: true, result: `${names(moved)} now start from "${group.branch}".` };
207
+ }
208
+ if (action === 'allow-snapshot') {
209
+ await saveSession(first.id, (x) => {
210
+ if (x.vcs)
211
+ x.vcs.dirtyWorktree = { policy: 'snapshot' };
212
+ });
213
+ return { ok: true, result: `"${first.name}" takes its uncommitted changes as a starting snapshot once you approve the list.` };
214
+ }
215
+ if (action === 'snapshot-here') {
216
+ const taken = await takeSnapshot(first, { approved: true, choices }, bus, async (m) => saveSession(first.id, m), allSessions);
217
+ return taken.ok ? { ok: true, result: `taken: ${short(taken.start.commit)} on ${taken.start.branch ?? taken.start.inputs?.recaptured?.at(-1)?.branch ?? ''}.` } : { ok: false, problem: taken.problem };
218
+ }
219
+ // snapshot-on-base: the base branch's tree plus the approved inputs only, with a temporary index.
220
+ const paths = group.inputs.map((e) => e.path);
221
+ const shown = Object.keys(choices).sort();
222
+ if (JSON.stringify(shown) !== JSON.stringify([...paths].sort()) || paths.some((p) => choices[p] !== 'include')) {
223
+ return { ok: false, problem: 'the input files changed since the list was shown. Look at the list again; nothing was done.' };
224
+ }
225
+ const topR = await git(repoDir, ['rev-parse', '--show-toplevel']);
226
+ if (!topR.ok || !topR.stdout || !group.baseHead || !group.baseBranch)
227
+ return { ok: false, problem: 'the base branch could not be read.' };
228
+ const top = resolve(topR.stdout);
229
+ const files = [];
230
+ for (const path of paths) {
231
+ const blob = await git(top, ['hash-object', '-w', `--path=${path}`, '--', join(top, path)]);
232
+ if (!blob.ok || !blob.stdout)
233
+ return { ok: false, problem: `${path} could not be read into git: ${blob.stderr}` };
234
+ const mode = (await git(top, ['ls-tree', group.baseHead, '--', path])).stdout.split(' ')[0] || '100644';
235
+ files.push({ path, mode, blob: blob.stdout });
236
+ }
237
+ const branch = await freeBranchName(top, `${branchNameFrom(['baseline'], first.vcs?.branchPrefix || 'cop/')}/${first.id}`);
238
+ const sharing = group.sessions.filter((s) => (s.role === 'first' || s.role === 'shares') && !s.started);
239
+ const message = [
240
+ 'Capture user-provided inputs',
241
+ '',
242
+ `The operator's input files, approved on the run screen, on top of "${group.baseBranch}" (${short(group.baseHead)}).`,
243
+ `Made without switching branches; the sessions ${names(sharing)} start from it. The work reads them and does not change them.`,
244
+ '',
245
+ ...paths.slice(0, 200).map((p) => `- ${p}`),
246
+ '',
247
+ 'Committed by copilot-operator. Not pushed.',
248
+ '',
249
+ ].join('\n');
250
+ const made = await commitOnto(top, group.baseHead, files, message, branch);
251
+ if ('problem' in made)
252
+ return { ok: false, problem: made.problem };
253
+ const settings = inputSettings(first.vcs);
254
+ const sums = await inputSumsAt(top, made.commit, settings?.patterns ?? []);
255
+ const approvedAt = new Date().toISOString();
256
+ for (const s of sharing) {
257
+ const start = {
258
+ kind: 'snapshot',
259
+ commit: made.commit,
260
+ branch,
261
+ snapshot: {
262
+ fromBranch: group.baseBranch,
263
+ fromCommit: group.baseHead,
264
+ included: paths.slice(0, 500),
265
+ leftOut: [],
266
+ approved: true,
267
+ onBase: true,
268
+ approvedAt,
269
+ sharedWith: sharing.map((x) => ({ id: x.id, name: x.name })),
270
+ },
271
+ inputs: { patterns: settings?.patterns ?? [], files: sums, readOnly: settings?.readOnly ?? true },
272
+ };
273
+ await saveSession(s.id, (x) => {
274
+ x.vcsBaseCommit = made.commit;
275
+ x.vcsStart = start;
276
+ });
277
+ bus.publish({
278
+ sessionId: s.id,
279
+ type: 'vcs-snapshot',
280
+ level: 'info',
281
+ message: `starting snapshot approved on the run screen: ${paths.length} input file(s) on top of ${group.baseBranch} (${short(group.baseHead)}) as ${short(made.commit)} on ${branch}; the checkout was not touched`,
282
+ data: { commit: made.commit, branch, base: group.baseHead, baseBranch: group.baseBranch, inputs: paths, approvedAt, sessions: sharing.map((x) => x.id) },
283
+ });
284
+ }
285
+ return { ok: true, result: `taken: ${short(made.commit)} on ${branch}, on top of "${group.baseBranch}"; ${names(sharing)} start from it.` };
286
+ }
@@ -1,6 +1,6 @@
1
1
  import { findSuspicious } from './commitHygiene.js';
2
2
  import { dirtyPolicy, planSnapshot, someOf, takeSnapshot } from './snapshot.js';
3
- import { inputSettings, inputsNote, settleInputs } from './inputs.js';
3
+ import { dirtyBesidesInputs, inputSettings, inputsNote, settleInputs } from './inputs.js';
4
4
  import { artifactPatterns, excludeArtifacts } from './artifacts.js';
5
5
  import { workingTreePaths } from './git.js';
6
6
  import { inScope } from './scope.js';
@@ -133,6 +133,19 @@ async function prepare(session, task, bus, saveSession, allSessions) {
133
133
  state = await repoState(dir);
134
134
  }
135
135
  }
136
+ /*
137
+ * Input files taken into a starting snapshot on the base branch stay untracked where they are, as
138
+ * they were approved: not "a dirty tree" while their content is the recorded one. They are staged
139
+ * when the task's branch is checked out, so the switch keeps them (see `switchTo`).
140
+ */
141
+ let carryInputs = [];
142
+ if (state.dirty && start?.inputs) {
143
+ const split = await dirtyBesidesInputs(dir, start);
144
+ if (split.other.length === 0 && split.inputs.length > 0) {
145
+ carryInputs = split.inputs;
146
+ state = { ...state, dirty: false };
147
+ }
148
+ }
136
149
  // A dirty tree is the operator's own work in progress. Committing it under the bot's name
137
150
  // or moving it to another branch would both be decisions that are not ours to make — unless
138
151
  // the operator made it, with a snapshot policy, above.
@@ -190,7 +203,7 @@ async function prepare(session, task, bus, saveSession, allSessions) {
190
203
  const attempt = task.attempt ?? 1;
191
204
  const prefix = settings.branchPrefix || 'cop/';
192
205
  if (settings.branchMode === 'per-session') {
193
- return await switchTo(session, task, dir, sessionBranchName(session), base, bus, { reuseExisting: true, start });
206
+ return await switchTo(session, task, dir, sessionBranchName(session), base, bus, { reuseExisting: true, start, stage: carryInputs });
194
207
  }
195
208
  // A name the task carries wins over one derived from its title: whoever wrote the plan knew
196
209
  // what the task was for, and a title is only ever a label for the list.
@@ -203,11 +216,11 @@ async function prepare(session, task, bus, saveSession, allSessions) {
203
216
  // The same for a new prompt given to a finished task: it builds on that attempt's work.
204
217
  const previousBranch = task.continuing || task.buildsOn ? task.attempts?.at(-1)?.vcs?.branch : undefined;
205
218
  if (previousBranch)
206
- return await switchTo(session, task, dir, previousBranch, base, bus, { reuseExisting: true, start });
219
+ return await switchTo(session, task, dir, previousBranch, base, bus, { reuseExisting: true, start, stage: carryInputs });
207
220
  // A re-run starts from where that task started the first time, not from where the previous
208
221
  // attempt ended. That is the whole point of recording the base commit.
209
222
  const from = firstAttemptBase(task) ?? base;
210
- return await switchTo(session, task, dir, wanted, from, bus, { reuseExisting: false, start });
223
+ return await switchTo(session, task, dir, wanted, from, bus, { reuseExisting: false, start, stage: carryInputs });
211
224
  }
212
225
  /**
213
226
  * The one branch a `per-session` session works on: the name it was given, else one made from its
@@ -364,6 +377,7 @@ export async function sessionStart(session, dir, head, allSessions) {
364
377
  commit: startedAt,
365
378
  ...(before.vcsStart?.branch ? { branch: before.vcsStart.branch } : {}),
366
379
  fromSession: { id: before.id, name: before.name },
380
+ ...inheritedBaseline(before),
367
381
  note: artifactsOnly
368
382
  ? `"${before.name}" committed nothing — its result is artifacts only — so this session starts where it started (${startedAt.slice(0, 8)})`
369
383
  : `"${before.name}" changed nothing, so this session starts where it started (${startedAt.slice(0, 8)})`,
@@ -403,7 +417,7 @@ export async function sessionStart(session, dir, head, allSessions) {
403
417
  'or run that session "One branch for the whole session" so the next one can carry on from it.',
404
418
  };
405
419
  }
406
- return { start: { kind: 'previous-session', commit: tip, branch: best.branch, fromSession: { id: best.session.id, name: best.session.name } } };
420
+ return { start: { kind: 'previous-session', commit: tip, branch: best.branch, fromSession: { id: best.session.id, name: best.session.name }, ...inheritedBaseline(best.session) } };
407
421
  }
408
422
  /**
409
423
  * The branch holding a session's latest work, and when that work ended: in `per-session` mode its
@@ -422,6 +436,15 @@ function lastWorkOf(session) {
422
436
  }
423
437
  return best;
424
438
  }
439
+ /**
440
+ * The starting snapshot a session carries on, for the one that continues it: its own when it made
441
+ * one, else the one it inherited. Recorded so the export can say each session of a chain shares it.
442
+ */
443
+ function inheritedBaseline(before) {
444
+ const own = before.vcsStart?.kind === 'snapshot' ? { commit: before.vcsStart.commit, branch: before.vcsStart.branch, fromSession: { id: before.id, name: before.name } } : undefined;
445
+ const baseline = own ?? before.vcsStart?.baseline;
446
+ return baseline ? { baseline } : {};
447
+ }
425
448
  /** When a session last did anything: the latest start or end of any of its tasks' attempts. */
426
449
  function lastActivityOf(session) {
427
450
  let at = null;
@@ -499,6 +522,10 @@ opts) {
499
522
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-problem', level: 'warn', message: problem });
500
523
  return { vcs: { problem }, note: '' };
501
524
  }
525
+ // Approved inputs left untracked where they are: staged first, so the switch keeps them.
526
+ const stage = opts.stage ?? [];
527
+ if (stage.length > 0)
528
+ await git(dir, ['--literal-pathspecs', 'add', '-f', '--', ...stage]);
502
529
  const state = await repoState(dir);
503
530
  let name = wantedName;
504
531
  let result;
@@ -514,6 +541,9 @@ opts) {
514
541
  result = await createBranch(dir, name, from);
515
542
  }
516
543
  if (!result.ok) {
544
+ // The operator's checkout as it was: the staged inputs go back to untracked.
545
+ if (stage.length > 0)
546
+ await git(dir, ['--literal-pathspecs', 'restore', '--staged', '--', ...stage]);
517
547
  const problem = result.stderr || result.stdout || 'the branch could not be created';
518
548
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-problem', level: 'warn',
519
549
  message: `version control is on but inactive: ${problem}` });
@@ -927,7 +957,13 @@ export async function vcsPreflight(session, allSessions = async () => []) {
927
957
  return { ok: false, repoDir, problem: state.problem };
928
958
  // Artifacts do not count: the run keeps them out of git before it looks (see `excludeArtifacts`).
929
959
  const artifacts = artifactPatterns(settings);
930
- const changed = state.dirty && artifacts.length > 0 ? (await workingTreePaths(repoDir)).filter((p) => !inScope(p, artifacts)) : state.changed;
960
+ // File by file when anything is to be told apart: plain status folds a new folder into one line.
961
+ let changed = state.dirty && (artifacts.length > 0 || !!session.vcsStart?.inputs) ? (await workingTreePaths(repoDir)).filter((p) => !artifacts.length || !inScope(p, artifacts)) : state.changed;
962
+ // Approved inputs left untracked as they were (a snapshot on the base branch) are not dirt either.
963
+ if (changed.length > 0 && session.vcsStart?.inputs) {
964
+ const split = await dirtyBesidesInputs(repoDir, session.vcsStart);
965
+ changed = changed.filter((p) => !split.inputs.includes(p));
966
+ }
931
967
  const dirty = changed.length > 0;
932
968
  /*
933
969
  * Before the first task, under a snapshot policy or with input files: the list to approve, file by
package/dist/web/404.html CHANGED
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html><!--P0lpNIJvdSO3yuWRpIPQf--><html lang="en" data-theme="light" data-textsize="normal" data-contrast="normal" data-motion="normal" data-links="plain" data-focus="normal"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/c37978b44194c37b.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-0f31b926d9a9c5a4.js"/><script src="/_next/static/chunks/c7879cf7-b5ab1053c1d9a2e7.js" async=""></script><script src="/_next/static/chunks/131-bae98f790a541679.js" async=""></script><script src="/_next/static/chunks/main-app-e3a5f713ba31557e.js" async=""></script><script src="/_next/static/chunks/197-f7c33a0114fc3db3.js" async=""></script><script src="/_next/static/chunks/867-d9c54f7a2b285b3f.js" async=""></script><script src="/_next/static/chunks/549-5d7c845fb0a27dbe.js" async=""></script><script src="/_next/static/chunks/app/layout-3efbb282292ccc38.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>copilot-operator</title><meta name="description" content="Runs task loops with Microsoft 365 Copilot on this machine."/><script>
1
+ <!DOCTYPE html><!--_dZtu_yqxc_HXzYTmqYSc--><html lang="en" data-theme="light" data-textsize="normal" data-contrast="normal" data-motion="normal" data-links="plain" data-focus="normal"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/c37978b44194c37b.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-0f31b926d9a9c5a4.js"/><script src="/_next/static/chunks/c7879cf7-b5ab1053c1d9a2e7.js" async=""></script><script src="/_next/static/chunks/131-bae98f790a541679.js" async=""></script><script src="/_next/static/chunks/main-app-e3a5f713ba31557e.js" async=""></script><script src="/_next/static/chunks/197-f7c33a0114fc3db3.js" async=""></script><script src="/_next/static/chunks/867-a67ea8ba3f9b24aa.js" async=""></script><script src="/_next/static/chunks/549-df7a4876917615da.js" async=""></script><script src="/_next/static/chunks/app/layout-3efbb282292ccc38.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>copilot-operator</title><meta name="description" content="Runs task loops with Microsoft 365 Copilot on this machine."/><script>
2
2
  (function () {
3
3
  try {
4
4
  var d = document.documentElement;
@@ -13,4 +13,4 @@
13
13
  d.dataset.focus = a.strongFocus ? 'strong' : 'normal';
14
14
  } catch (e) {}
15
15
  })();
16
- </script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><a class="skip-link" href="#main">Skip to the content</a><div class="wrap"><header class="top"><h1><a href="/">copilot-operator</a></h1><nav aria-label="Main"><a href="/"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M4 5h16v4H4zM4 11h16v4H4zM4 17h10v2H4z"></path></svg><span>Sessions</span></a><a href="/defaults"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm8.5 4 1.5 1-1.6 2.8-1.8-.4a7 7 0 0 1-1.5 1.5l.4 1.8L14.7 21l-1-1.5a7 7 0 0 1-3.4 0l-1 1.5-2.8-1.6.4-1.8A7 7 0 0 1 5.4 16l-1.8.4L2 13.6 3.5 12.6V11.4L2 10.4l1.6-2.8 1.8.4A7 7 0 0 1 6.9 6.5l-.4-1.8L9.3 3l1 1.5a7 7 0 0 1 3.4 0l1-1.5 2.8 1.6-.4 1.8a7 7 0 0 1 1.5 1.5l1.8-.4L22 10.4l-1.5 1z"></path></svg><span>Settings</span></a><a href="/import"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3v10m0 0 4-4m-4 4-4-4M4 15v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg><span>Plan from JSON</span></a><a href="/history"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3a9 9 0 1 0 8.5 6M12 7v5l3 2M3 4v5h5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg><span>Task register</span></a><a href="/level1"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M6 3h9l5 5v13H6zM14 3v6h6M9 13h7M9 17h7" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>Level 1 prompt</span></a><a href="/presets"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M5 4h14v4H5zM5 10h14v4H5zM5 16h9v4H5z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>Level 2 presets</span></a><a href="/appearance"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3a9 9 0 0 0 0 18c1.5 0 2-1 2-2s-1-1.5-1-2.5S14 15 15.5 15H17a4 4 0 0 0 4-4 8 8 0 0 0-9-8zM8 9a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm4-3a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm4 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path></svg><span>Appearance</span></a><a href="/system"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M4 5h16v10H4zM2 19h20M9 15v4M15 15v4" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>System</span></a><select aria-label="Language" style="width:auto"><option value="en" selected="">English</option><option value="bg">Български</option></select></nav></header><main id="main"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/webpack-0f31b926d9a9c5a4.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[8078,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-d9c54f7a2b285b3f.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"AppearanceProvider\"]\n3:I[867,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-d9c54f7a2b285b3f.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"LanguageProvider\"]\n4:I[1035,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-d9c54f7a2b285b3f.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"DialogHost\"]\n5:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-d9c54f7a2b285b3f.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"SkipLink\"]\n6:I[8197,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-d9c54f7a2b285b3f.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"\"]\n7:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-d9c54f7a2b285b3f.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"Nav\"]\n8:I[6657,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-d9c54f7a2b285b3f.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"GlobalApprovals\"]\n9:I[5204,[],\"\"]\na:I[2642,[],\"\"]\nb:I[7712,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-d9c54f7a2b285b3f.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"ScrollJump\"]\nc:I[433,[],\"OutletBoundary\"]\ne:I[9884,[],\"AsyncMetadataOutlet\"]\n10:I[433,[],\"ViewportBoundary\"]\n12:I[433,[],\"MetadataBoundary\"]\n13:\"$Sreact.suspense\"\n15:I[3536,[],\"\"]\n:HL[\"/_next/static/css/c37978b44194c37b.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"P0lpNIJvdSO3yuWRpIPQf\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/c37978b44194c37b.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"suppressHydrationWarning\":true,\"data-theme\":\"light\",\"data-textsize\":\"normal\",\"data-contrast\":\"normal\",\"data-motion\":\"normal\",\"data-links\":\"plain\",\"data-focus\":\"normal\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n(function () {\\n try {\\n var d = document.documentElement;\\n var raw = localStorage.getItem('cop.appearance');\\n var a = raw ? JSON.parse(raw) : {};\\n if (!a || typeof a !== 'object') a = {};\\n d.dataset.theme = a.theme === 'dark' ? 'dark' : 'light';\\n d.dataset.textsize = [\\\"small\\\",\\\"normal\\\",\\\"large\\\",\\\"huge\\\",\\\"giant\\\"].indexOf(a.textSize) \u003e= 0 ? a.textSize : 'normal';\\n d.dataset.contrast = a.highContrast ? 'high' : 'normal';\\n d.dataset.motion = a.reduceMotion ? 'reduced' : 'normal';\\n d.dataset.links = a.underlineLinks ? 'underline' : 'plain';\\n d.dataset.focus = a.strongFocus ? 'strong' : 'normal';\\n } catch (e) {}\\n})();\\n\"}}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"children\":[[\"$\",\"$L5\",null,{}],[\"$\",\"div\",null,{\"className\":\"wrap\",\"children\":[[\"$\",\"header\",null,{\"className\":\"top\",\"children\":[[\"$\",\"h1\",null,{\"children\":[\"$\",\"$L6\",null,{\"href\":\"/\",\"children\":\"copilot-operator\"}]}],[\"$\",\"$L7\",null,{}]]}],[\"$\",\"$L8\",null,{}],[\"$\",\"main\",null,{\"id\":\"main\",\"children\":[\"$\",\"$L9\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}],[\"$\",\"$Lb\",null,{}]]}]}]}]}]]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L9\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$Lc\",null,{\"children\":[\"$Ld\",[\"$\",\"$Le\",null,{\"promise\":\"$@f\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L10\",null,{\"children\":\"$L11\"}],null],[\"$\",\"$L12\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$13\",null,{\"fallback\":null,\"children\":\"$L14\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$15\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"11:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\nd:null\n"])</script><script>self.__next_f.push([1,"f:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"copilot-operator\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Runs task loops with Microsoft 365 Copilot on this machine.\"}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"14:\"$f:metadata\"\n"])</script></body></html>
16
+ </script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><a class="skip-link" href="#main">Skip to the content</a><div class="wrap"><header class="top"><h1><a href="/">copilot-operator</a></h1><nav aria-label="Main"><a href="/"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M4 5h16v4H4zM4 11h16v4H4zM4 17h10v2H4z"></path></svg><span>Sessions</span></a><a href="/defaults"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm8.5 4 1.5 1-1.6 2.8-1.8-.4a7 7 0 0 1-1.5 1.5l.4 1.8L14.7 21l-1-1.5a7 7 0 0 1-3.4 0l-1 1.5-2.8-1.6.4-1.8A7 7 0 0 1 5.4 16l-1.8.4L2 13.6 3.5 12.6V11.4L2 10.4l1.6-2.8 1.8.4A7 7 0 0 1 6.9 6.5l-.4-1.8L9.3 3l1 1.5a7 7 0 0 1 3.4 0l1-1.5 2.8 1.6-.4 1.8a7 7 0 0 1 1.5 1.5l1.8-.4L22 10.4l-1.5 1z"></path></svg><span>Settings</span></a><a href="/import"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3v10m0 0 4-4m-4 4-4-4M4 15v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg><span>Plan from JSON</span></a><a href="/history"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3a9 9 0 1 0 8.5 6M12 7v5l3 2M3 4v5h5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg><span>Task register</span></a><a href="/level1"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M6 3h9l5 5v13H6zM14 3v6h6M9 13h7M9 17h7" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>Level 1 prompt</span></a><a href="/presets"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M5 4h14v4H5zM5 10h14v4H5zM5 16h9v4H5z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>Level 2 presets</span></a><a href="/appearance"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3a9 9 0 0 0 0 18c1.5 0 2-1 2-2s-1-1.5-1-2.5S14 15 15.5 15H17a4 4 0 0 0 4-4 8 8 0 0 0-9-8zM8 9a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm4-3a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm4 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path></svg><span>Appearance</span></a><a href="/system"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M4 5h16v10H4zM2 19h20M9 15v4M15 15v4" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>System</span></a><select aria-label="Language" style="width:auto"><option value="en" selected="">English</option><option value="bg">Български</option></select></nav></header><main id="main"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/webpack-0f31b926d9a9c5a4.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[8078,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-a67ea8ba3f9b24aa.js\",\"549\",\"static/chunks/549-df7a4876917615da.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"AppearanceProvider\"]\n3:I[867,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-a67ea8ba3f9b24aa.js\",\"549\",\"static/chunks/549-df7a4876917615da.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"LanguageProvider\"]\n4:I[1035,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-a67ea8ba3f9b24aa.js\",\"549\",\"static/chunks/549-df7a4876917615da.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"DialogHost\"]\n5:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-a67ea8ba3f9b24aa.js\",\"549\",\"static/chunks/549-df7a4876917615da.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"SkipLink\"]\n6:I[8197,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-a67ea8ba3f9b24aa.js\",\"549\",\"static/chunks/549-df7a4876917615da.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"\"]\n7:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-a67ea8ba3f9b24aa.js\",\"549\",\"static/chunks/549-df7a4876917615da.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"Nav\"]\n8:I[6657,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-a67ea8ba3f9b24aa.js\",\"549\",\"static/chunks/549-df7a4876917615da.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"GlobalApprovals\"]\n9:I[5204,[],\"\"]\na:I[2642,[],\"\"]\nb:I[7712,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-a67ea8ba3f9b24aa.js\",\"549\",\"static/chunks/549-df7a4876917615da.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"ScrollJump\"]\nc:I[433,[],\"OutletBoundary\"]\ne:I[9884,[],\"AsyncMetadataOutlet\"]\n10:I[433,[],\"ViewportBoundary\"]\n12:I[433,[],\"MetadataBoundary\"]\n13:\"$Sreact.suspense\"\n15:I[3536,[],\"\"]\n:HL[\"/_next/static/css/c37978b44194c37b.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"-dZtu-yqxc-HXzYTmqYSc\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/c37978b44194c37b.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"suppressHydrationWarning\":true,\"data-theme\":\"light\",\"data-textsize\":\"normal\",\"data-contrast\":\"normal\",\"data-motion\":\"normal\",\"data-links\":\"plain\",\"data-focus\":\"normal\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n(function () {\\n try {\\n var d = document.documentElement;\\n var raw = localStorage.getItem('cop.appearance');\\n var a = raw ? JSON.parse(raw) : {};\\n if (!a || typeof a !== 'object') a = {};\\n d.dataset.theme = a.theme === 'dark' ? 'dark' : 'light';\\n d.dataset.textsize = [\\\"small\\\",\\\"normal\\\",\\\"large\\\",\\\"huge\\\",\\\"giant\\\"].indexOf(a.textSize) \u003e= 0 ? a.textSize : 'normal';\\n d.dataset.contrast = a.highContrast ? 'high' : 'normal';\\n d.dataset.motion = a.reduceMotion ? 'reduced' : 'normal';\\n d.dataset.links = a.underlineLinks ? 'underline' : 'plain';\\n d.dataset.focus = a.strongFocus ? 'strong' : 'normal';\\n } catch (e) {}\\n})();\\n\"}}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"children\":[[\"$\",\"$L5\",null,{}],[\"$\",\"div\",null,{\"className\":\"wrap\",\"children\":[[\"$\",\"header\",null,{\"className\":\"top\",\"children\":[[\"$\",\"h1\",null,{\"children\":[\"$\",\"$L6\",null,{\"href\":\"/\",\"children\":\"copilot-operator\"}]}],[\"$\",\"$L7\",null,{}]]}],[\"$\",\"$L8\",null,{}],[\"$\",\"main\",null,{\"id\":\"main\",\"children\":[\"$\",\"$L9\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}],[\"$\",\"$Lb\",null,{}]]}]}]}]}]]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L9\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$Lc\",null,{\"children\":[\"$Ld\",[\"$\",\"$Le\",null,{\"promise\":\"$@f\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L10\",null,{\"children\":\"$L11\"}],null],[\"$\",\"$L12\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$13\",null,{\"fallback\":null,\"children\":\"$L14\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$15\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"11:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\nd:null\n"])</script><script>self.__next_f.push([1,"f:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"copilot-operator\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Runs task loops with Microsoft 365 Copilot on this machine.\"}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"14:\"$f:metadata\"\n"])</script></body></html>
@@ -0,0 +1 @@
1
+ "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[549],{5549:(t,e,o)=>{o.d(e,{AV:()=>a,FH:()=>h,FZ:()=>r,Zm:()=>u,aT:()=>s,c5:()=>y,dK:()=>l,nC:()=>n,rt:()=>d,ub:()=>i,zi:()=>p});let n="/api";function s(t,e){return"/sessions/view?id=".concat(encodeURIComponent(t)).concat(e?"#".concat(e):"")}let c="cop-api-token";function a(){try{let t=/(?:^#|&)token=([0-9a-f]{16,})/i.exec(window.location.hash);if(t)return window.localStorage.setItem(c,t[1]),window.history.replaceState(null,"",window.location.pathname+window.location.search),t[1];return window.localStorage.getItem(c)||""}catch(t){return""}}function i(t){let e=a();return e?"".concat(t).concat(t.includes("?")?"&":"?","token=").concat(encodeURIComponent(e)):t}let r=["exit-zero","exit-nonzero","output-contains","output-omits","output-matches","file-exists","file-missing","file-contains"];function d(t){return t.startsWith("exit-")||t.startsWith("output-")}function l(t){return"output-contains"===t||"output-omits"===t||"output-matches"===t||"file-contains"===t}function u(t){return"limit-reached"===t.status||"aborted"===t.status||!!t.limit&&("failed"===t.status||"blocked"===t.status)}async function m(t,e){let o,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},c=void 0===s.timeoutMs?3e4:s.timeoutMs;try{var i;o=await fetch("".concat(n).concat(t),{...e,...null!==c&&!(null==e?void 0:e.signal)?{signal:AbortSignal.timeout(c)}:{},headers:{"content-type":"application/json",...a()?{"x-cop-token":a()}:{},...null!=(i=null==e?void 0:e.headers)?i:{}}})}catch(e){if("TimeoutError"===e.name)throw Error("The API did not answer within ".concat(Math.round((null!=c?c:0)/1e3)," s (").concat(t.split("?")[0],"). It may be busy; try again."));throw e}if(!o.ok){let t="".concat(o.status," ").concat(o.statusText);try{let e=await o.json();(null==e?void 0:e.message)&&(t=Array.isArray(e.message)?e.message.join("; "):e.message)}catch(t){}throw Error(t)}let r=await o.text();return r?JSON.parse(r):void 0}let h={health:()=>m("/health"),doctor:()=>m("/doctor"),settings:()=>m("/settings"),saveSettings:t=>m("/settings",{method:"PUT",body:JSON.stringify(t)}),level1:()=>m("/level1"),setLevel1:t=>m("/level1",{method:"PUT",body:JSON.stringify({content:t})}),resetLevel1:()=>m("/level1",{method:"DELETE"}),presets:()=>m("/presets"),savePreset:(t,e)=>m("/presets/".concat(encodeURIComponent(t)),{method:"PUT",body:JSON.stringify({content:e})}),deletePreset:t=>m("/presets/".concat(encodeURIComponent(t)),{method:"DELETE"}),personas:()=>m("/personas"),savePersona:(t,e)=>m("/personas/".concat(encodeURIComponent(t)),{method:"PUT",body:JSON.stringify({content:e})}),deletePersona:t=>m("/personas/".concat(encodeURIComponent(t)),{method:"DELETE"}),sessions:()=>m("/sessions"),reorderSessions:t=>m("/sessions-order",{method:"PUT",body:JSON.stringify({ids:t})}),metrics:()=>m("/metrics"),preparePreview:function(t){let e=!(arguments.length>1)||void 0===arguments[1]||arguments[1];return m("/repo/prepare/preview",{method:"POST",body:JSON.stringify({dir:t,fetch:e})})},prepareProject:(t,e)=>m("/repo/prepare",{method:"POST",body:JSON.stringify({dir:t,fingerprint:e})}),session:t=>m("/sessions/".concat(t)),createSession:(t,e)=>m("/sessions",{method:"POST",body:JSON.stringify({name:t,projectDir:e})}),updateSession:(t,e)=>m("/sessions/".concat(t),{method:"PUT",body:JSON.stringify(e)}),deleteSession:t=>m("/sessions/".concat(t),{method:"DELETE"}),addTask:(t,e)=>m("/sessions/".concat(t,"/tasks"),{method:"POST",body:JSON.stringify(e)}),updateTask:(t,e,o)=>m("/sessions/".concat(t,"/tasks/").concat(e),{method:"PUT",body:JSON.stringify(o)}),deleteTask:(t,e)=>m("/sessions/".concat(t,"/tasks/").concat(e),{method:"DELETE"}),continueTask:(t,e)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/continue"),{method:"POST",body:"{}"}),rerunTask:function(t,e){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return m("/sessions/".concat(t,"/tasks/").concat(e,"/rerun"),{method:"POST",body:JSON.stringify({...o,...n.buildOnFinished?{buildOnFinished:!0}:{}})})},taskChanges:(t,e,o)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/changes").concat(o?"?run=".concat(encodeURIComponent(o)):"")),taskChangeFile:(t,e,o,n)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/changes/file?path=").concat(encodeURIComponent(o)).concat(n?"&run=".concat(encodeURIComponent(n)):"")),taskStory:(t,e,o)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/story").concat(o?"?run=".concat(encodeURIComponent(o)):"")),taskFiles:(t,e,o)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/files").concat(o?"?run=".concat(encodeURIComponent(o)):"")),taskLogUrl:(t,e,o)=>"".concat(n,"/sessions/").concat(t,"/tasks/").concat(e,"/log").concat(o?"?run=".concat(encodeURIComponent(o)):""),taskFileUrl:(t,e,o,s,c)=>"".concat(n,"/sessions/").concat(t,"/tasks/").concat(e,"/files/").concat(o,"/").concat(encodeURIComponent(s)).concat(c?"?run=".concat(encodeURIComponent(c)):""),saveTaskLog:(t,e,o)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/log").concat(o?"?run=".concat(encodeURIComponent(o)):""),{method:"POST",body:"{}"}),saveTaskFile:(t,e,o,n,s)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/files/").concat(o,"/").concat(encodeURIComponent(n)).concat(s?"?run=".concat(encodeURIComponent(s)):""),{method:"POST",body:"{}"}),start:(t,e,o)=>m("/sessions/".concat(t,"/start"),{method:"POST",body:JSON.stringify({mode:e,name:o})}),stop:t=>m("/sessions/".concat(t,"/stop"),{method:"POST",body:"{}"}),approvals:t=>m("/approvals".concat(t?"?session=".concat(encodeURIComponent(t)):"")),decide:(t,e)=>m("/approvals/".concat(t),{method:"POST",body:JSON.stringify({action:e})}),setRunMode:(t,e)=>m("/sessions/".concat(t,"/mode"),{method:"POST",body:JSON.stringify({mode:e})}),streamUrl:t=>i("".concat(n,"/sessions/").concat(t,"/stream")),batch:()=>m("/batch").then(t=>null!=t?t:null),startBatch:(t,e,o,n,s,c,a)=>m("/batch/start",{method:"POST",body:JSON.stringify({sessionIds:t,mode:e,onFailure:o,model:n,reviewModel:s,name:c,taskIds:a})}),suggestedRunName:t=>m("/batch/name?sessions=".concat(encodeURIComponent(t.join(",")))),stopBatch:()=>m("/batch/stop",{method:"POST",body:"{}"}),pauseBatch:()=>m("/batch/pause",{method:"POST",body:"{}"}),resumeBatch:()=>m("/batch/resume",{method:"POST",body:"{}"}),planBrief:t=>m("/plan/brief?lang=".concat(encodeURIComponent(t))),context:(t,e)=>m("/context/".concat(t,"?lang=").concat(encodeURIComponent(e))),setContext:(t,e)=>m("/context/".concat(t),{method:"PUT",body:JSON.stringify({content:e})}),resetContext:(t,e)=>m("/context/".concat(t,"?lang=").concat(encodeURIComponent(e)),{method:"DELETE"}),checkPlan:t=>m("/plan/check",{method:"POST",body:JSON.stringify({text:t})}),importPlan:t=>m("/plan/import",{method:"POST",body:JSON.stringify({text:t})}),browseFolder:t=>m("/browse-folder",{method:"POST",body:JSON.stringify({start:t})},{timeoutMs:null}),tasks:()=>m("/tasks"),activity:()=>m("/activity"),downloadBundle:async t=>{var e,o,s;let c=await fetch("".concat(n,"/export/bundle"),{method:"POST",headers:{"content-type":"application/json",...a()?{"x-cop-token":a()}:{}},body:JSON.stringify({tasks:t})});if(!c.ok)throw Error(await c.text()||"".concat(c.status," ").concat(c.statusText));let i=null!=(s=c.headers.get("content-disposition"))?s:"",r=null==(e=/filename\*=UTF-8''([^;]+)/i.exec(i))?void 0:e[1],d=null==(o=/filename="([^"]+)"/i.exec(i))?void 0:o[1],l=null!=d?d:"copilot-operator-bundle.json";if(r)try{l=decodeURIComponent(r)}catch(t){}let u=URL.createObjectURL(await c.blob()),m=document.createElement("a");return m.href=u,m.download=l,document.body.appendChild(m),m.click(),m.remove(),setTimeout(()=>URL.revokeObjectURL(u),1e4),l},exportUrl:(t,e)=>{let o=new URLSearchParams;return e.run&&o.set("run",e.run),e.session&&o.set("session",e.session),e.task&&o.set("task",e.task),i("".concat(n,"/export/").concat(t,"?").concat(o.toString()))},debugExportUrl:t=>i("".concat(n,"/debug/export").concat(t.length>0?"?sessions=".concat(encodeURIComponent(t.join(","))):"")),vcsStatus:t=>m("/sessions/".concat(t,"/vcs")),runVcs:t=>m("/batch/vcs?ids=".concat(encodeURIComponent(t.join(",")))),runVcsPrepare:function(t,e,o){let n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return m("/batch/vcs/prepare",{method:"POST",body:JSON.stringify({sessionIds:t,repoDir:e,action:o,choices:n})})},vcsSnapshot:(t,e)=>m("/sessions/".concat(t,"/vcs/snapshot"),{method:"POST",body:JSON.stringify({choices:e})}),restorePreview:(t,e)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/restore")),restore:(t,e)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/restore"),{method:"POST",body:"{}"}),restartPlan:(t,e)=>m("/sessions/".concat(t,"/tasks/").concat(e,"/restart")),restartFrom:function(t,e){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return m("/sessions/".concat(t,"/tasks/").concat(e,"/restart"),{method:"POST",body:JSON.stringify(o)})},repoCheck:t=>m("/repo?dir=".concat(encodeURIComponent(t))),project:()=>m("/project"),setProject:t=>m("/project",{method:"PUT",body:JSON.stringify(t)}),models:()=>m("/models"),setDefaultModel:t=>m("/models/default",{method:"PUT",body:JSON.stringify({model:t})}),setDefaultReviewModel:t=>m("/models/review-default",{method:"PUT",body:JSON.stringify({model:t})}),refreshModels:()=>m("/models/refresh",{method:"POST",body:"{}"},{timeoutMs:null}),login:t=>m("/login",{method:"POST",body:JSON.stringify({account:(null==t?void 0:t.trim())||void 0})},{timeoutMs:null})};function p(t){return t<1024?"".concat(t," B"):t<1048576?"".concat((t/1024).toFixed(1)," KB"):"".concat((t/1048576).toFixed(1)," MB")}function y(t){if(void 0===t)return"";let e=Math.round(t/1e3);if(e<60)return"".concat(e,"s");let o=Math.floor(e/60);return o<60?"".concat(o,"m ").concat(e%60,"s"):"".concat(Math.floor(o/60),"h ").concat(o%60,"m")}}}]);