copilot-operator 0.1.19 → 0.1.22

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 (43) hide show
  1. package/dist/src/api/operator.controller.js +49 -0
  2. package/dist/src/api/operator.service.js +179 -7
  3. package/dist/src/orchestrator/taskRunner.js +14 -2
  4. package/dist/src/plan/systemGuide.js +119 -15
  5. package/dist/src/session/exports.js +72 -1
  6. package/dist/src/session/runLog.js +60 -0
  7. package/dist/src/vcs/git.js +11 -1
  8. package/dist/src/vcs/inputs.js +26 -2
  9. package/dist/src/vcs/prepareFromRemote.js +263 -0
  10. package/dist/src/vcs/runPreflight.js +286 -0
  11. package/dist/src/vcs/taskVcs.js +60 -20
  12. package/dist/web/404.html +2 -2
  13. package/dist/web/_next/static/chunks/549-5f90780046c1c24f.js +1 -0
  14. package/dist/web/_next/static/chunks/867-79f07bebe3084811.js +1 -0
  15. package/dist/web/_next/static/chunks/app/defaults/page-cdc730adea753446.js +1 -0
  16. package/dist/web/_next/static/chunks/app/page-0a861b38da0a1c51.js +1 -0
  17. package/dist/web/_next/static/chunks/app/sessions/view/page-185755ada66e2930.js +1 -0
  18. package/dist/web/appearance.html +2 -2
  19. package/dist/web/appearance.txt +10 -10
  20. package/dist/web/defaults.html +2 -2
  21. package/dist/web/defaults.txt +10 -10
  22. package/dist/web/history.html +2 -2
  23. package/dist/web/history.txt +10 -10
  24. package/dist/web/import.html +2 -2
  25. package/dist/web/import.txt +10 -10
  26. package/dist/web/index.html +2 -2
  27. package/dist/web/index.txt +10 -10
  28. package/dist/web/level1.html +2 -2
  29. package/dist/web/level1.txt +10 -10
  30. package/dist/web/presets.html +2 -2
  31. package/dist/web/presets.txt +10 -10
  32. package/dist/web/sessions/view.html +2 -2
  33. package/dist/web/sessions/view.txt +10 -10
  34. package/dist/web/system.html +2 -2
  35. package/dist/web/system.txt +10 -10
  36. package/package.json +150 -147
  37. package/dist/web/_next/static/chunks/549-5d7c845fb0a27dbe.js +0 -1
  38. package/dist/web/_next/static/chunks/867-d9c54f7a2b285b3f.js +0 -1
  39. package/dist/web/_next/static/chunks/app/defaults/page-fd819bbfa7f33982.js +0 -1
  40. package/dist/web/_next/static/chunks/app/page-409e37e208dc12e5.js +0 -1
  41. package/dist/web/_next/static/chunks/app/sessions/view/page-99f9153fd75078d0.js +0 -1
  42. /package/dist/web/_next/static/{ofnRLEgWx9th5w_qtuFnB → 9Ze8aagkvT-WLfD4KCKKS}/_buildManifest.js +0 -0
  43. /package/dist/web/_next/static/{ofnRLEgWx9th5w_qtuFnB → 9Ze8aagkvT-WLfD4KCKKS}/_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.
@@ -146,6 +159,7 @@ async function prepare(session, task, bus, saveSession, allSessions) {
146
159
  message: `version control cannot start: ${problem}` });
147
160
  return { vcs: { problem }, note: '' };
148
161
  }
162
+ let startIsNew = false;
149
163
  if (!base) {
150
164
  const resolved = await sessionStart(session, dir, state.head, allSessions);
151
165
  if ('problem' in resolved) {
@@ -155,15 +169,13 @@ async function prepare(session, task, bus, saveSession, allSessions) {
155
169
  }
156
170
  start = resolved.start;
157
171
  base = start?.commit;
158
- if (start) {
159
- const chosen = start;
160
- await saveSession((s) => {
161
- s.vcsBaseCommit = chosen.commit;
162
- s.vcsStart = chosen;
163
- });
164
- bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-start', level: chosen.note || updateWarns(chosen) ? 'warn' : 'info',
165
- message: describeStart(chosen), data: { ...chosen } });
166
- }
172
+ /*
173
+ * Recorded below, once the inputs are settled too, not here. Recorded here, a task refused for its
174
+ * inputs (a pattern matching nothing yet) left the session started: the retry, with the files added,
175
+ * skipped the starting snapshot and ran with version control off (reported on 0.1.18). A start that
176
+ * is refused records nothing, and the retry starts afresh.
177
+ */
178
+ startIsNew = !!start;
167
179
  }
168
180
  /*
169
181
  * The operator's input files, in the commit the session starts from whatever its `startFrom`, with
@@ -173,20 +185,25 @@ async function prepare(session, task, bus, saveSession, allSessions) {
173
185
  const settled = await settleInputs(session, dir, start, allSessions);
174
186
  if ('problem' in settled)
175
187
  return refuse(`${settled.problem} Nothing was run.`);
176
- const chosen = settled.start;
177
- start = chosen;
178
- base = chosen.commit;
188
+ start = settled.start;
189
+ base = settled.start.commit;
190
+ bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-inputs', level: 'info',
191
+ message: describeInputs(settled.start), data: { files: settled.start.inputs?.files.length ?? 0, carried: settled.start.inputs?.carried } });
192
+ startIsNew = true;
193
+ }
194
+ if (startIsNew && start) {
195
+ const chosen = start;
179
196
  await saveSession((s) => {
180
197
  s.vcsBaseCommit = chosen.commit;
181
198
  s.vcsStart = chosen;
182
199
  });
183
- bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-inputs', level: 'info',
184
- message: describeInputs(chosen), data: { files: chosen.inputs?.files.length ?? 0, carried: chosen.inputs?.carried } });
200
+ bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-start', level: chosen.note || updateWarns(chosen) ? 'warn' : 'info',
201
+ message: describeStart(chosen), data: { ...chosen } });
185
202
  }
186
203
  const attempt = task.attempt ?? 1;
187
204
  const prefix = settings.branchPrefix || 'cop/';
188
205
  if (settings.branchMode === 'per-session') {
189
- 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 });
190
207
  }
191
208
  // A name the task carries wins over one derived from its title: whoever wrote the plan knew
192
209
  // what the task was for, and a title is only ever a label for the list.
@@ -199,11 +216,11 @@ async function prepare(session, task, bus, saveSession, allSessions) {
199
216
  // The same for a new prompt given to a finished task: it builds on that attempt's work.
200
217
  const previousBranch = task.continuing || task.buildsOn ? task.attempts?.at(-1)?.vcs?.branch : undefined;
201
218
  if (previousBranch)
202
- 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 });
203
220
  // A re-run starts from where that task started the first time, not from where the previous
204
221
  // attempt ended. That is the whole point of recording the base commit.
205
222
  const from = firstAttemptBase(task) ?? base;
206
- 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 });
207
224
  }
208
225
  /**
209
226
  * The one branch a `per-session` session works on: the name it was given, else one made from its
@@ -360,6 +377,7 @@ export async function sessionStart(session, dir, head, allSessions) {
360
377
  commit: startedAt,
361
378
  ...(before.vcsStart?.branch ? { branch: before.vcsStart.branch } : {}),
362
379
  fromSession: { id: before.id, name: before.name },
380
+ ...inheritedBaseline(before),
363
381
  note: artifactsOnly
364
382
  ? `"${before.name}" committed nothing — its result is artifacts only — so this session starts where it started (${startedAt.slice(0, 8)})`
365
383
  : `"${before.name}" changed nothing, so this session starts where it started (${startedAt.slice(0, 8)})`,
@@ -399,7 +417,7 @@ export async function sessionStart(session, dir, head, allSessions) {
399
417
  'or run that session "One branch for the whole session" so the next one can carry on from it.',
400
418
  };
401
419
  }
402
- 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) } };
403
421
  }
404
422
  /**
405
423
  * The branch holding a session's latest work, and when that work ended: in `per-session` mode its
@@ -418,6 +436,15 @@ function lastWorkOf(session) {
418
436
  }
419
437
  return best;
420
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
+ }
421
448
  /** When a session last did anything: the latest start or end of any of its tasks' attempts. */
422
449
  function lastActivityOf(session) {
423
450
  let at = null;
@@ -495,6 +522,10 @@ opts) {
495
522
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-problem', level: 'warn', message: problem });
496
523
  return { vcs: { problem }, note: '' };
497
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]);
498
529
  const state = await repoState(dir);
499
530
  let name = wantedName;
500
531
  let result;
@@ -510,6 +541,9 @@ opts) {
510
541
  result = await createBranch(dir, name, from);
511
542
  }
512
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]);
513
547
  const problem = result.stderr || result.stdout || 'the branch could not be created';
514
548
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-problem', level: 'warn',
515
549
  message: `version control is on but inactive: ${problem}` });
@@ -923,7 +957,13 @@ export async function vcsPreflight(session, allSessions = async () => []) {
923
957
  return { ok: false, repoDir, problem: state.problem };
924
958
  // Artifacts do not count: the run keeps them out of git before it looks (see `excludeArtifacts`).
925
959
  const artifacts = artifactPatterns(settings);
926
- 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
+ }
927
967
  const dirty = changed.length > 0;
928
968
  /*
929
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><!--ofnRLEgWx9th5w_qtuFnB--><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><!--9Ze8aagkvT_WLfD4KCKKS--><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-79f07bebe3084811.js" async=""></script><script src="/_next/static/chunks/549-5f90780046c1c24f.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\":\"ofnRLEgWx9th5w_qtuFnB\",\"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-79f07bebe3084811.js\",\"549\",\"static/chunks/549-5f90780046c1c24f.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"AppearanceProvider\"]\n3:I[867,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-79f07bebe3084811.js\",\"549\",\"static/chunks/549-5f90780046c1c24f.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"LanguageProvider\"]\n4:I[1035,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-79f07bebe3084811.js\",\"549\",\"static/chunks/549-5f90780046c1c24f.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"DialogHost\"]\n5:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-79f07bebe3084811.js\",\"549\",\"static/chunks/549-5f90780046c1c24f.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"SkipLink\"]\n6:I[8197,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-79f07bebe3084811.js\",\"549\",\"static/chunks/549-5f90780046c1c24f.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"\"]\n7:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-79f07bebe3084811.js\",\"549\",\"static/chunks/549-5f90780046c1c24f.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"Nav\"]\n8:I[6657,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-79f07bebe3084811.js\",\"549\",\"static/chunks/549-5f90780046c1c24f.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-79f07bebe3084811.js\",\"549\",\"static/chunks/549-5f90780046c1c24f.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\":\"9Ze8aagkvT-WLfD4KCKKS\",\"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:t=>m("/repo/prepare/preview",{method:"POST",body:JSON.stringify({dir:t})}),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")}}}]);