copilot-operator 0.1.17 → 0.1.18

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 (48) hide show
  1. package/dist/src/api/operator.service.js +23 -2
  2. package/dist/src/orchestrator/derivedChecks.js +19 -1
  3. package/dist/src/orchestrator/review.js +2 -2
  4. package/dist/src/orchestrator/taskRunner.js +81 -8
  5. package/dist/src/plan/brief.js +26 -0
  6. package/dist/src/plan/importPlan.js +28 -1
  7. package/dist/src/plan/schema.js +38 -0
  8. package/dist/src/plan/systemGuide.js +40 -0
  9. package/dist/src/protocol/replySchema.js +1 -1
  10. package/dist/src/protocol/reviewSchema.js +10 -5
  11. package/dist/src/session/exports.js +44 -13
  12. package/dist/src/session/model.js +20 -0
  13. package/dist/src/session/store.js +78 -0
  14. package/dist/src/vcs/artifacts.js +138 -0
  15. package/dist/src/vcs/inputs.js +332 -0
  16. package/dist/src/vcs/snapshot.js +59 -18
  17. package/dist/src/vcs/taskVcs.js +136 -25
  18. package/dist/web/404.html +2 -2
  19. package/dist/web/_next/static/chunks/549-5d7c845fb0a27dbe.js +1 -0
  20. package/dist/web/_next/static/chunks/867-c078b5b504bf4289.js +1 -0
  21. package/dist/web/_next/static/chunks/app/history/page-a0bd59986f7c8beb.js +1 -0
  22. package/dist/web/_next/static/chunks/app/sessions/view/page-27cb2d544b498d92.js +1 -0
  23. package/dist/web/appearance.html +2 -2
  24. package/dist/web/appearance.txt +11 -11
  25. package/dist/web/defaults.html +2 -2
  26. package/dist/web/defaults.txt +11 -11
  27. package/dist/web/history.html +2 -2
  28. package/dist/web/history.txt +11 -11
  29. package/dist/web/import.html +2 -2
  30. package/dist/web/import.txt +11 -11
  31. package/dist/web/index.html +2 -2
  32. package/dist/web/index.txt +11 -11
  33. package/dist/web/level1.html +2 -2
  34. package/dist/web/level1.txt +11 -11
  35. package/dist/web/presets.html +2 -2
  36. package/dist/web/presets.txt +11 -11
  37. package/dist/web/sessions/view.html +2 -2
  38. package/dist/web/sessions/view.txt +11 -11
  39. package/dist/web/system.html +2 -2
  40. package/dist/web/system.txt +11 -11
  41. package/package.json +7 -3
  42. package/prompts/level1.md +547 -546
  43. package/dist/web/_next/static/chunks/549-a13ae394c5e6a837.js +0 -1
  44. package/dist/web/_next/static/chunks/867-7f27ef819d251cc5.js +0 -1
  45. package/dist/web/_next/static/chunks/app/history/page-d7314ac22d47a3ab.js +0 -1
  46. package/dist/web/_next/static/chunks/app/sessions/view/page-d28825430ca280de.js +0 -1
  47. /package/dist/web/_next/static/{kkxFcGzA9Uo1uRAgcoDti → JPmlfAEvbNkbghoyvY9uC}/_buildManifest.js +0 -0
  48. /package/dist/web/_next/static/{kkxFcGzA9Uo1uRAgcoDti → JPmlfAEvbNkbghoyvY9uC}/_ssgManifest.js +0 -0
@@ -22,12 +22,15 @@
22
22
  * Like the rest of `vcs/`: nothing is reset, stashed, deleted or rewritten. The snapshot adds a
23
23
  * branch and a commit.
24
24
  */
25
- import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
25
+ import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
26
26
  import { tmpdir } from 'node:os';
27
27
  import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
28
28
  import { looksGenerated } from './commitHygiene.js';
29
29
  import { branchNameFrom, freeBranchName, git, gitBytes, isValidBranchName, RUNNER_EMAIL } from './git.js';
30
30
  import { repoDirOf, sessionStart } from './taskVcs.js';
31
+ import { inputFilesInTree, inputSettings } from './inputs.js';
32
+ import { artifactPatterns } from './artifacts.js';
33
+ import { inScope } from './scope.js';
31
34
  /** The session's dirty-tree policy, absent fields filled in: reject, and asking first. */
32
35
  export function dirtyPolicy(vcs) {
33
36
  const policy = vcs?.dirtyWorktree?.policy;
@@ -128,9 +131,10 @@ async function startProblem(session, dir, branch, head, allSessions) {
128
131
  */
129
132
  export async function planSnapshot(session, allSessions = async () => []) {
130
133
  const { policy, requireApproval } = dirtyPolicy(session.vcs);
134
+ const inputs = inputSettings(session.vcs);
131
135
  const repoDir = repoDirOf(session);
132
136
  const plan = { needed: false, ok: false, policy, requireApproval, repoDir, branch: null, head: null, entries: [] };
133
- if (!session.vcs?.enabled || policy === 'reject' || session.vcsBaseCommit)
137
+ if (!session.vcs?.enabled || (policy === 'reject' && !inputs) || session.vcsBaseCommit)
134
138
  return plan;
135
139
  const top = repoDir ? await topOf(repoDir) : null;
136
140
  if (!top)
@@ -142,13 +146,43 @@ export async function planSnapshot(session, allSessions = async () => []) {
142
146
  const headR = await git(top, ['rev-parse', '--verify', '--quiet', 'HEAD']);
143
147
  plan.branch = branchR.ok && branchR.stdout !== 'HEAD' ? branchR.stdout : null;
144
148
  plan.head = headR.ok && headR.stdout ? headR.stdout : null;
145
- if (status.length === 0)
146
- return plan;
147
- plan.needed = true;
148
149
  // The project folder, when it is a folder inside the repository: a change outside it is not this session's.
149
150
  const project = session.projectDir?.trim() ? relative(top, resolve(session.projectDir.trim())).replace(/\\/g, '/') : '';
150
151
  const inProject = (p) => !project || project.startsWith('..') || isAbsolute(project) || p.toLowerCase() === project.toLowerCase() || p.toLowerCase().startsWith(`${project.toLowerCase()}/`);
151
- for (const s of status) {
152
+ const size = async (p) => (await stat(join(top, p)).catch(() => null))?.size;
153
+ /*
154
+ * The operator's input files that are not committed as they are: new, changed, or ignored. They
155
+ * are taken whatever the dirty-tree policy, or the snapshot is not; the rest of the tree is the
156
+ * policy's. A secrets file or one outside the project is never an input.
157
+ */
158
+ const inputPaths = new Set();
159
+ if (inputs) {
160
+ const dirtyNow = new Set(status.map((s) => s.path));
161
+ for (const f of await inputFilesInTree(top, inputs.patterns)) {
162
+ if (f.kind === 'tracked' && !dirtyNow.has(f.path))
163
+ continue;
164
+ inputPaths.add(f.path);
165
+ const blocked = looksGenerated(f.path)?.reason === 'a secrets file' ? 'a secrets file is never taken as an input' : !inProject(f.path) ? `outside the project folder ${project}` : undefined;
166
+ plan.entries.push({ path: f.path, kind: f.kind, input: true, size: await size(f.path), allowed: blocked ? [] : ['include'], choice: blocked ? null : 'include', reason: blocked ?? 'an input file: committed with the starting snapshot, then read-only' });
167
+ }
168
+ }
169
+ const others = status.filter((s) => !inputPaths.has(s.path));
170
+ if (policy === 'reject') {
171
+ if (plan.entries.length === 0)
172
+ return plan;
173
+ plan.needed = true;
174
+ if (others.length > 0) {
175
+ plan.problem =
176
+ `besides the input files there are other uncommitted changes (${someOf(others.map((o) => o.path))}). ` +
177
+ 'Commit them yourself, or choose "Take them as a starting snapshot" under "Uncommitted changes".';
178
+ return plan;
179
+ }
180
+ }
181
+ if (plan.entries.length === 0 && others.length === 0)
182
+ return plan;
183
+ plan.needed = true;
184
+ plan.requireApproval = (others.length > 0 && requireApproval) || (plan.entries.some((e) => e.input) && !!inputs?.requireApproval);
185
+ for (const s of policy === 'reject' ? [] : others) {
152
186
  const generated = looksGenerated(s.path);
153
187
  const outside = !inProject(s.path);
154
188
  if (!s.untracked) {
@@ -157,15 +191,18 @@ export async function planSnapshot(session, allSessions = async () => []) {
157
191
  : outside
158
192
  ? `outside the project folder ${project}; it is tracked, so it cannot be left out. Commit or undo that change yourself`
159
193
  : undefined;
160
- plan.entries.push({ path: s.path, ...(s.from ? { from: s.from } : {}), kind: 'tracked', allowed: blocked ? [] : ['include'], choice: blocked ? null : 'include', ...(blocked ? { reason: blocked } : {}) });
194
+ plan.entries.push({ path: s.path, ...(s.from ? { from: s.from } : {}), kind: 'tracked', size: await size(s.path), allowed: blocked ? [] : ['include'], choice: blocked ? null : 'include', ...(blocked ? { reason: blocked } : {}) });
161
195
  continue;
162
196
  }
163
- const why = generated ? generated.reason : outside ? `outside the project folder ${project}` : policy === 'tracked-only-snapshot' ? 'a new file, and this session takes tracked changes only' : undefined;
164
- plan.entries.push({ path: s.path, kind: 'untracked', allowed: why ? ['leave-out'] : ['include', 'leave-out'], choice: why ? 'leave-out' : 'include', ...(why ? { reason: why } : {}) });
197
+ const artifact = artifactPatterns(session.vcs).length > 0 && inScope(s.path, artifactPatterns(session.vcs));
198
+ const why = artifact
199
+ ? 'an artifact: kept with the run, never committed'
200
+ : generated ? generated.reason : outside ? `outside the project folder ${project}` : policy === 'tracked-only-snapshot' ? 'a new file, and this session takes tracked changes only' : undefined;
201
+ plan.entries.push({ path: s.path, kind: 'untracked', size: await size(s.path), allowed: why ? ['leave-out'] : ['include', 'leave-out'], choice: why ? 'leave-out' : 'include', ...(why ? { reason: why } : {}) });
165
202
  }
166
203
  if (policy === 'snapshot') {
167
204
  for (const path of await scopedIgnored(top, scopedPatterns(session))) {
168
- if (looksGenerated(path) || !inProject(path))
205
+ if (looksGenerated(path) || !inProject(path) || inputPaths.has(path))
169
206
  continue;
170
207
  plan.entries.push({ path, kind: 'ignored', allowed: ['include', 'leave-out'], choice: 'leave-out', reason: "ignored by git, and a task's scope names it: taken only when you tick it" });
171
208
  }
@@ -215,16 +252,20 @@ async function leaveOut(top, session, paths) {
215
252
  return undefined;
216
253
  }
217
254
  /** The commit message: what was taken, what was left out, from where, and on whose word. */
218
- function snapshotMessage(session, plan, included, leftOut, approved) {
255
+ function snapshotMessage(session, plan, taken, leftOut, approved) {
219
256
  const list = (paths) => [...paths.slice(0, 200).map((p) => `- ${p}`), ...(paths.length > 200 ? [`- and ${paths.length - 200} more`] : [])].join('\n');
257
+ const inputs = taken.filter((e) => e.input);
258
+ const rest = taken.filter((e) => !e.input).map((e) => e.path);
259
+ const onlyInputs = rest.length === 0;
220
260
  return [
221
- 'Capture operator baseline before run',
261
+ onlyInputs ? 'Capture user-provided inputs' : 'Capture operator baseline before run',
222
262
  '',
223
- `The uncommitted changes in the repository when session "${session.name}" was first run, committed so that`,
263
+ `The ${onlyInputs ? "operator's input files" : 'uncommitted changes in the repository'} when session "${session.name}" was first run, committed so that`,
224
264
  "the session starts from them and every task's commit shows only that task's work.",
225
- '',
226
- `Taken (${included.length}):`,
227
- list(included),
265
+ ...(inputs.length > 0
266
+ ? ['', `Input files, read by the work and not changed by it (${inputs.length}):`, list(inputs.map((e) => `${e.path}${e.size !== undefined ? ` (${e.size} bytes, ${e.kind})` : ''}`))]
267
+ : []),
268
+ ...(rest.length > 0 ? ['', `Taken (${rest.length}):`, list(rest)] : []),
228
269
  ...(leftOut.length > 0 ? ['', `Left out, in .git/info/exclude (${leftOut.length}):`, list(leftOut)] : []),
229
270
  '',
230
271
  `Taken from ${plan.branch ?? 'a detached HEAD'} at ${(plan.head ?? '').slice(0, 8)}, ${approved ? "on the operator's approval of this list" : 'automatically (requireApproval: false)'}.`,
@@ -240,7 +281,7 @@ function snapshotMessage(session, plan, included, leftOut, approved) {
240
281
  * `approved`, every file takes its default and anything to be left out refuses (see `DirtyWorktree`).
241
282
  */
242
283
  export async function takeSnapshot(session, opts, bus, saveSession, allSessions = async () => []) {
243
- if (dirtyPolicy(session.vcs).policy === 'reject') {
284
+ if (dirtyPolicy(session.vcs).policy === 'reject' && !inputSettings(session.vcs)) {
244
285
  return { ok: false, problem: 'this session does not take uncommitted changes as a snapshot. Choose that under Version control → "Uncommitted changes" first.' };
245
286
  }
246
287
  if (session.vcsBaseCommit)
@@ -320,7 +361,7 @@ export async function takeSnapshot(session, opts, bus, saveSession, allSessions
320
361
  finally {
321
362
  await rm(list, { force: true });
322
363
  }
323
- const message = snapshotMessage(session, plan, take.map((e) => e.path), leftOutAll, opts.approved);
364
+ const message = snapshotMessage(session, plan, take, leftOutAll, opts.approved);
324
365
  const commit = await git(top, ['-c', 'user.name=copilot-operator', '-c', `user.email=${RUNNER_EMAIL}`, 'commit', '--no-verify', '-q', '-m', message]);
325
366
  if (!commit.ok)
326
367
  return await back(`the snapshot could not be committed: ${commit.stderr || commit.stdout}.`);
@@ -1,5 +1,9 @@
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';
4
+ import { artifactPatterns, excludeArtifacts } from './artifacts.js';
5
+ import { workingTreePaths } from './git.js';
6
+ import { inScope } from './scope.js';
3
7
  import { branchExists, branchNameFrom, describeUpdate, updateFromRemote, localBranches, commitAll, commitFiles, commitsBetween, commitSubject, createBranch, foreignCommits, isAncestor, checkoutExisting, freeBranchName, git, isValidBranchName, plannedBranchName, repoState } from './git.js';
4
8
  /** Which repository a session works in: its own setting, else the project it mirrors. */
5
9
  export function repoDirOf(session) {
@@ -38,35 +42,56 @@ allSessions = async () => []) {
38
42
  message: `version control is on but inactive: ${problem}` });
39
43
  return { vcs: { problem }, note: '' };
40
44
  }
45
+ const refuse = (why) => {
46
+ bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-refused', level: 'error', message: why });
47
+ return { vcs: { problem: why }, note: '', refuse: why };
48
+ };
49
+ /*
50
+ * The session's artifacts are never committed: their patterns go into the repository's own
51
+ * exclude file before anything looks at the tree, so they neither make it dirty nor go into a
52
+ * commit. See `artifacts.ts`.
53
+ */
54
+ const excluded = await excludeArtifacts(dir, session);
55
+ if (excluded.problem)
56
+ return refuse(`the session's artifacts could not be kept out of git: ${excluded.problem}. Nothing was run.`);
57
+ if (excluded.tracked.length > 0) {
58
+ bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-artifacts-tracked', level: 'warn',
59
+ message: `${excluded.tracked.length} artifact file(s) are already tracked by git, so a change to them is still committed: ${someOf(excluded.tracked)}. Take them out of git yourself (git rm --cached) if they should not be.` });
60
+ }
61
+ if (excluded.tracked.length > 0 || artifactPatterns(settings).length > 0)
62
+ state = await repoState(dir);
41
63
  if (settings.startFrom === 'existing-branch')
42
- return await onExistingBranch(session, task, dir, state, bus, saveSession);
64
+ return await onExistingBranch(session, task, dir, state, bus, saveSession, allSessions);
43
65
  // The session's base is fixed the first time it runs: every per-task branch is cut from it,
44
66
  // which is what makes the tasks independent of each other rather than of the calendar. Where
45
67
  // it is taken from is the session's `startFrom`; see `sessionStart`.
46
68
  let base = session.vcsBaseCommit;
47
69
  let start = session.vcsStart;
48
70
  /*
49
- * The operator's own changes before the session's first task, under a snapshot policy: they
50
- * become the commit the session starts from (see `snapshot.ts`). Asked for approval, the run is
51
- * refused until the list has been approved on the session's page; running without version
52
- * control instead would be the one outcome the operator chose against.
71
+ * The operator's own changes before the session's first task — under a snapshot policy, or its
72
+ * input files: they become the commit the session starts from (see `snapshot.ts`). Asked for
73
+ * approval, the run is refused until the list has been approved on the session's page; running
74
+ * without version control instead would be the one outcome the operator chose against.
53
75
  */
54
76
  const dirty = dirtyPolicy(settings);
55
- if (state.dirty && !base && dirty.policy !== 'reject') {
56
- const refuse = (why) => {
57
- bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-refused', level: 'error', message: why });
58
- return { vcs: { problem: why }, note: '', refuse: why };
59
- };
60
- if (dirty.requireApproval) {
61
- return refuse(`the repository has uncommitted changes (${someOf(state.changed)}), and this session takes them as its starting snapshot ` +
62
- 'once you have approved the list: on the session\'s page, Version control → "Uncommitted changes", choose what goes in and take the snapshot. Nothing was run.');
77
+ const inputs = inputSettings(settings);
78
+ // Input files are looked for even in a clean tree: an ignored input does not make it dirty.
79
+ if ((state.dirty || inputs) && !base && (dirty.policy !== 'reject' || inputs)) {
80
+ const plan = await planSnapshot(session, allSessions);
81
+ if (plan.needed) {
82
+ if (!plan.ok)
83
+ return refuse(`the starting snapshot cannot be taken: ${plan.problem} Nothing was run.`);
84
+ if (plan.requireApproval) {
85
+ return refuse(`the repository has uncommitted changes (${someOf(plan.entries.map((e) => e.path))}), and this session takes them as its starting snapshot ` +
86
+ 'once you have approved the list: on the session\'s page, Version control → "Uncommitted changes", choose what goes in and take the snapshot. Nothing was run.');
87
+ }
88
+ const taken = await takeSnapshot(session, { approved: false }, bus, saveSession, allSessions);
89
+ if (!taken.ok)
90
+ return refuse(`the starting snapshot of the uncommitted changes was not taken: ${taken.problem} Nothing was run.`);
91
+ base = taken.start.commit;
92
+ start = taken.start;
93
+ state = await repoState(dir);
63
94
  }
64
- const taken = await takeSnapshot(session, { approved: false }, bus, saveSession, allSessions);
65
- if (!taken.ok)
66
- return refuse(`the starting snapshot of the uncommitted changes was not taken: ${taken.problem} Nothing was run.`);
67
- base = taken.start.commit;
68
- start = taken.start;
69
- state = await repoState(dir);
70
95
  }
71
96
  // A dirty tree is the operator's own work in progress. Committing it under the bot's name
72
97
  // or moving it to another branch would both be decisions that are not ours to make — unless
@@ -98,6 +123,24 @@ allSessions = async () => []) {
98
123
  message: describeStart(chosen), data: { ...chosen } });
99
124
  }
100
125
  }
126
+ /*
127
+ * The operator's input files, in the commit the session starts from whatever its `startFrom`, with
128
+ * their sums on the record. Once per session: a start that has recorded them has them.
129
+ */
130
+ if (inputs && start && !start.inputs) {
131
+ const settled = await settleInputs(session, dir, start, allSessions);
132
+ if ('problem' in settled)
133
+ return refuse(`${settled.problem} Nothing was run.`);
134
+ const chosen = settled.start;
135
+ start = chosen;
136
+ base = chosen.commit;
137
+ await saveSession((s) => {
138
+ s.vcsBaseCommit = chosen.commit;
139
+ s.vcsStart = chosen;
140
+ });
141
+ bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-inputs', level: 'info',
142
+ message: describeInputs(chosen), data: { files: chosen.inputs?.files.length ?? 0, carried: chosen.inputs?.carried } });
143
+ }
101
144
  const attempt = task.attempt ?? 1;
102
145
  const prefix = settings.branchPrefix || 'cop/';
103
146
  if (settings.branchMode === 'per-session') {
@@ -149,7 +192,7 @@ export function namedSessionBranch(vcs) {
149
192
  * refuses the task before a message is sent. The first run records the branch's tip as where the
150
193
  * session started, so the record and a restore know what was there before the work.
151
194
  */
152
- async function onExistingBranch(session, task, dir, state, bus, saveSession) {
195
+ async function onExistingBranch(session, task, dir, state, bus, saveSession, allSessions) {
153
196
  const wanted = (session.vcs?.existingBranch ?? '').trim();
154
197
  const refuse = (why) => {
155
198
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-refused', level: 'error', message: why });
@@ -181,6 +224,24 @@ async function onExistingBranch(session, task, dir, state, bus, saveSession) {
181
224
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-start', level: updateWarns(chosen) ? 'warn' : 'info', message: describeStart(chosen), data: { ...chosen } });
182
225
  }
183
226
  }
227
+ /*
228
+ * The input files must already be on the branch, as they are: the runner commits nothing onto a
229
+ * branch the operator named, so one that lacks them, or has them otherwise, is refused.
230
+ */
231
+ if (inputSettings(session.vcs) && start && !start.inputs) {
232
+ const settled = await settleInputs(session, dir, start, allSessions);
233
+ if ('problem' in settled)
234
+ return refuse(`${settled.problem} Nothing was run.`);
235
+ if (settled.start.commit !== start.commit) {
236
+ return refuse(`the input files are not on "${wanted}" as they were last committed (${settled.start.inputs?.carried?.from ?? 'elsewhere'}), and the runner does not commit onto a branch you named. ` +
237
+ `Commit them on "${wanted}" yourself. Nothing was run.`);
238
+ }
239
+ const chosen = settled.start;
240
+ start = chosen;
241
+ await saveSession((s) => {
242
+ s.vcsStart = chosen;
243
+ });
244
+ }
184
245
  const prepared = await switchTo(session, task, dir, wanted, undefined, bus, { reuseExisting: true, mustExist: true, start });
185
246
  return prepared.vcs.branch === wanted ? prepared : refuse(`the repository could not be put on "${wanted}": ${prepared.vcs.problem ?? 'unknown reason'}. Nothing was run.`);
186
247
  }
@@ -228,6 +289,31 @@ export async function sessionStart(session, dir, head, allSessions) {
228
289
  if (!tip) {
229
290
  return await fromBranch(`the branch of the previous session "${best.session.name}" (${best.branch}) is no longer in the repository, so it starts from "${baseBranch}"`);
230
291
  }
292
+ /*
293
+ * The whole of that session's done work, or nothing. In per-task mode every task has a branch of
294
+ * its own, all cut from one commit, so the branch of the task that finished last holds that task's
295
+ * work only — a chain that started there began without the files the earlier tasks had made,
296
+ * though they were in the repository on other branches. The runner does not merge them; it says
297
+ * which are missing and refuses, rather than start from part of the work as if it were all.
298
+ */
299
+ const missing = [];
300
+ for (const t of best.session.tasks) {
301
+ if (t.status !== 'done')
302
+ continue;
303
+ const done = [{ vcs: t.vcs, at: t.finishedAt }, ...(t.attempts ?? []).map((a) => ({ vcs: a.vcs, at: a.finishedAt }))]
304
+ .filter((v) => !!v.vcs?.commit && !!v.vcs.branch)
305
+ .sort((a, b) => (b.at ?? '').localeCompare(a.at ?? ''))[0];
306
+ if (done?.vcs?.commit && !(await isAncestor(dir, done.vcs.commit, tip)))
307
+ missing.push(`"${t.title}" (on ${done.vcs.branch})`);
308
+ }
309
+ if (missing.length > 0) {
310
+ return {
311
+ problem: `this session continues the previous session "${best.session.name}", whose done work is not on one branch: ${best.branch} does not have ` +
312
+ `${missing.slice(0, 8).join(', ')}${missing.length > 8 ? `, and ${missing.length - 8} more` : ''}. ` +
313
+ 'That session ran per task, so each task\'s work is on its own branch. Merge them into one branch and continue it ("Carry on an existing branch"), ' +
314
+ 'or run that session "One branch for the whole session" so the next one can carry on from it.',
315
+ };
316
+ }
231
317
  return { start: { kind: 'previous-session', commit: tip, branch: best.branch, fromSession: { id: best.session.id, name: best.session.name } } };
232
318
  }
233
319
  /**
@@ -271,6 +357,13 @@ export function describeStart(start) {
271
357
  const updated = start.update ? ` (${describeUpdate(start.update)})` : '';
272
358
  return start.note ? `${said}${updated} — ${start.note}` : `${said}${updated}`;
273
359
  }
360
+ /** One line for the log: the session's input files, and whether the runner had to commit them on its start. */
361
+ export function describeInputs(start) {
362
+ const n = start.inputs?.files.length ?? 0;
363
+ const carried = start.inputs?.carried;
364
+ return (`${n} input file(s) in the session's start (${start.commit.slice(0, 8)})${start.inputs?.readOnly ? ', read-only' : ''}` +
365
+ (carried ? `: they were not in ${carried.onto.slice(0, 8)}, so they were committed on top of it as ${carried.branch}, from ${carried.from}` : ''));
366
+ }
274
367
  /** Whether the update before a session's start is worth a warning: it did not bring the branch up to date. */
275
368
  export function updateWarns(start) {
276
369
  return !!start.update && !['updated', 'up-to-date', 'no-remote'].includes(start.update.outcome);
@@ -346,6 +439,7 @@ opts) {
346
439
  base: vcs.baseCommit ? { commit: vcs.baseCommit, subject } : undefined,
347
440
  earlier,
348
441
  start: opts.start,
442
+ artifacts: artifactPatterns(session.vcs),
349
443
  });
350
444
  return { vcs, note };
351
445
  }
@@ -408,6 +502,14 @@ export function noteFor(repoDir, vcs, ctx = { mode: 'per-task', earlier: [] }) {
408
502
  'commit instead: `git merge-base --is-ancestor <commit> HEAD` exits 0 when it is.',
409
503
  '',
410
504
  ...(began ? [began, ''] : []),
505
+ ...(inputsNote(ctx.start?.inputs) ? [inputsNote(ctx.start?.inputs), ''] : []),
506
+ ...((ctx.artifacts?.length ?? 0) > 0
507
+ ? [
508
+ `Evidence goes under ${ctx.artifacts?.map((a) => `\`${a}\``).join(', ')}: those files are kept with the run's record and are never committed, ` +
509
+ 'so write reports, archives and test results there rather than changing .gitignore for them.',
510
+ '',
511
+ ]
512
+ : []),
411
513
  standing,
412
514
  '',
413
515
  '- Do not create branches, switch branches, commit, stash, reset or revert. That is the',
@@ -699,27 +801,36 @@ export async function vcsPreflight(session, allSessions = async () => []) {
699
801
  const state = await repoState(repoDir);
700
802
  if (!state.isRepo)
701
803
  return { ok: false, repoDir, problem: state.problem };
702
- // Before the first task, under a snapshot policy: the list to approve, file by file.
703
- if (state.dirty && !session.vcsBaseCommit && dirtyPolicy(settings).policy !== 'reject' && settings.startFrom !== 'existing-branch') {
804
+ // Artifacts do not count: the run keeps them out of git before it looks (see `excludeArtifacts`).
805
+ const artifacts = artifactPatterns(settings);
806
+ const changed = state.dirty && artifacts.length > 0 ? (await workingTreePaths(repoDir)).filter((p) => !inScope(p, artifacts)) : state.changed;
807
+ const dirty = changed.length > 0;
808
+ /*
809
+ * Before the first task, under a snapshot policy or with input files: the list to approve, file by
810
+ * file. Input files are looked for even in a clean tree — an ignored input does not make it dirty.
811
+ */
812
+ const inputs = !!inputSettings(settings);
813
+ if ((dirty || inputs) && !session.vcsBaseCommit && (dirtyPolicy(settings).policy !== 'reject' || inputs) && settings.startFrom !== 'existing-branch') {
704
814
  const snapshot = await planSnapshot(session, allSessions);
705
815
  if (snapshot.needed) {
816
+ const listed = snapshot.entries.map((e) => e.path);
706
817
  return {
707
818
  ok: false,
708
819
  repoDir,
709
820
  branch: state.branch ?? undefined,
710
821
  problem: snapshot.ok
711
- ? `There are uncommitted changes (${someOf(state.changed, 3)}). They become the session's starting snapshot ${snapshot.requireApproval ? 'once you approve the list below' : 'when its first task starts'}.`
822
+ ? `There are uncommitted changes (${someOf(listed, 3)}). They become the session's starting snapshot ${snapshot.requireApproval ? 'once you approve the list below' : 'when its first task starts'}.`
712
823
  : `There are uncommitted changes, and no starting snapshot can be taken: ${snapshot.problem}`,
713
824
  snapshot,
714
825
  };
715
826
  }
716
827
  }
717
- if (state.dirty) {
828
+ if (dirty) {
718
829
  return {
719
830
  ok: false,
720
831
  repoDir,
721
832
  branch: state.branch ?? undefined,
722
- problem: `There are uncommitted changes (${state.changed.slice(0, 3).join(', ')}${state.changed.length > 3 ? '…' : ''}). Commit or stash them first.`,
833
+ problem: `There are uncommitted changes (${changed.slice(0, 3).join(', ')}${changed.length > 3 ? '…' : ''}). Commit or stash them first.`,
723
834
  };
724
835
  }
725
836
  return { ok: true, repoDir, branch: state.branch ?? undefined };
package/dist/web/404.html CHANGED
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html><!--kkxFcGzA9Uo1uRAgcoDti--><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-7f27ef819d251cc5.js" async=""></script><script src="/_next/static/chunks/549-a13ae394c5e6a837.js" async=""></script><script src="/_next/static/chunks/app/layout-560bd6d173302e5a.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><!--JPmlfAEvbNkbghoyvY9uC--><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-c078b5b504bf4289.js" async=""></script><script src="/_next/static/chunks/549-5d7c845fb0a27dbe.js" async=""></script><script src="/_next/static/chunks/app/layout-560bd6d173302e5a.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[1205,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"themeScript\"]\n3:I[1205,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"AppearanceProvider\"]\n4:I[867,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"LanguageProvider\"]\n5:I[1035,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"DialogHost\"]\n6:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"SkipLink\"]\n7:I[8197,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"\"]\n8:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"Nav\"]\n9:I[6657,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"GlobalApprovals\"]\na:I[5204,[],\"\"]\nb:I[2642,[],\"\"]\nc:I[7712,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-7f27ef819d251cc5.js\",\"549\",\"static/chunks/549-a13ae394c5e6a837.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"ScrollJump\"]\nd:I[433,[],\"OutletBoundary\"]\nf:I[9884,[],\"AsyncMeta"])</script><script>self.__next_f.push([1,"dataOutlet\"]\n11:I[433,[],\"ViewportBoundary\"]\n13:I[433,[],\"MetadataBoundary\"]\n14:\"$Sreact.suspense\"\n16:I[3536,[],\"\"]\n:HL[\"/_next/static/css/c37978b44194c37b.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"kkxFcGzA9Uo1uRAgcoDti\",\"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\":\"$2\"}}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$L5\",null,{\"children\":[[\"$\",\"$L6\",null,{}],[\"$\",\"div\",null,{\"className\":\"wrap\",\"children\":[[\"$\",\"header\",null,{\"className\":\"top\",\"children\":[[\"$\",\"h1\",null,{\"children\":[\"$\",\"$L7\",null,{\"href\":\"/\",\"children\":\"copilot-operator\"}]}],[\"$\",\"$L8\",null,{}]]}],[\"$\",\"$L9\",null,{}],[\"$\",\"main\",null,{\"id\":\"main\",\"children\":[\"$\",\"$La\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$Lb\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}],[\"$\",\"$Lc\",null,{}]]}]}]}]}]]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$La\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$Lb\",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,[\"$\",\"$Ld\",null,{\"children\":[\"$Le\",[\"$\",\"$Lf\",null,{\"promise\":\"$@10\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L11\",null,{\"children\":\"$L12\"}],null],[\"$\",\"$L13\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$14\",null,{\"fallback\":null,\"children\":\"$L15\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$16\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"12:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\ne:null\n"])</script><script>self.__next_f.push([1,"10:{\"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,"15:\"$10: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[1205,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"themeScript\"]\n3:I[1205,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"AppearanceProvider\"]\n4:I[867,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"LanguageProvider\"]\n5:I[1035,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"DialogHost\"]\n6:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"SkipLink\"]\n7:I[8197,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"\"]\n8:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"Nav\"]\n9:I[6657,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"GlobalApprovals\"]\na:I[5204,[],\"\"]\nb:I[2642,[],\"\"]\nc:I[7712,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-c078b5b504bf4289.js\",\"549\",\"static/chunks/549-5d7c845fb0a27dbe.js\",\"177\",\"static/chunks/app/layout-560bd6d173302e5a.js\"],\"ScrollJump\"]\nd:I[433,[],\"OutletBoundary\"]\nf:I[9884,[],\"AsyncMeta"])</script><script>self.__next_f.push([1,"dataOutlet\"]\n11:I[433,[],\"ViewportBoundary\"]\n13:I[433,[],\"MetadataBoundary\"]\n14:\"$Sreact.suspense\"\n16:I[3536,[],\"\"]\n:HL[\"/_next/static/css/c37978b44194c37b.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"JPmlfAEvbNkbghoyvY9uC\",\"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\":\"$2\"}}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$L5\",null,{\"children\":[[\"$\",\"$L6\",null,{}],[\"$\",\"div\",null,{\"className\":\"wrap\",\"children\":[[\"$\",\"header\",null,{\"className\":\"top\",\"children\":[[\"$\",\"h1\",null,{\"children\":[\"$\",\"$L7\",null,{\"href\":\"/\",\"children\":\"copilot-operator\"}]}],[\"$\",\"$L8\",null,{}]]}],[\"$\",\"$L9\",null,{}],[\"$\",\"main\",null,{\"id\":\"main\",\"children\":[\"$\",\"$La\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$Lb\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}],[\"$\",\"$Lc\",null,{}]]}]}]}]}]]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$La\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$Lb\",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,[\"$\",\"$Ld\",null,{\"children\":[\"$Le\",[\"$\",\"$Lf\",null,{\"promise\":\"$@10\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L11\",null,{\"children\":\"$L12\"}],null],[\"$\",\"$L13\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$14\",null,{\"fallback\":null,\"children\":\"$L15\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$16\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"12:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\ne:null\n"])</script><script>self.__next_f.push([1,"10:{\"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,"15:\"$10:metadata\"\n"])</script></body></html>
@@ -0,0 +1 @@
1
+ "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[549],{5549:(t,o,e)=>{e.d(o,{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,o){return"/sessions/view?id=".concat(encodeURIComponent(t)).concat(o?"#".concat(o):"")}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 o=a();return o?"".concat(t).concat(t.includes("?")?"&":"?","token=").concat(encodeURIComponent(o)):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,o){let e,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},c=void 0===s.timeoutMs?3e4:s.timeoutMs;try{var i;e=await fetch("".concat(n).concat(t),{...o,...null!==c&&!(null==o?void 0:o.signal)?{signal:AbortSignal.timeout(c)}:{},headers:{"content-type":"application/json",...a()?{"x-cop-token":a()}:{},...null!=(i=null==o?void 0:o.headers)?i:{}}})}catch(o){if("TimeoutError"===o.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 o}if(!e.ok){let t="".concat(e.status," ").concat(e.statusText);try{let o=await e.json();(null==o?void 0:o.message)&&(t=Array.isArray(o.message)?o.message.join("; "):o.message)}catch(t){}throw Error(t)}let r=await e.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,o)=>m("/presets/".concat(encodeURIComponent(t)),{method:"PUT",body:JSON.stringify({content:o})}),deletePreset:t=>m("/presets/".concat(encodeURIComponent(t)),{method:"DELETE"}),personas:()=>m("/personas"),savePersona:(t,o)=>m("/personas/".concat(encodeURIComponent(t)),{method:"PUT",body:JSON.stringify({content:o})}),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"),syncPlan:t=>m("/repo/sync?dir=".concat(encodeURIComponent(t))),session:t=>m("/sessions/".concat(t)),createSession:(t,o)=>m("/sessions",{method:"POST",body:JSON.stringify({name:t,projectDir:o})}),updateSession:(t,o)=>m("/sessions/".concat(t),{method:"PUT",body:JSON.stringify(o)}),deleteSession:t=>m("/sessions/".concat(t),{method:"DELETE"}),addTask:(t,o)=>m("/sessions/".concat(t,"/tasks"),{method:"POST",body:JSON.stringify(o)}),updateTask:(t,o,e)=>m("/sessions/".concat(t,"/tasks/").concat(o),{method:"PUT",body:JSON.stringify(e)}),deleteTask:(t,o)=>m("/sessions/".concat(t,"/tasks/").concat(o),{method:"DELETE"}),continueTask:(t,o)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/continue"),{method:"POST",body:"{}"}),rerunTask:function(t,o){let e=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(o,"/rerun"),{method:"POST",body:JSON.stringify({...e,...n.buildOnFinished?{buildOnFinished:!0}:{}})})},taskChanges:(t,o,e)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/changes").concat(e?"?run=".concat(encodeURIComponent(e)):"")),taskChangeFile:(t,o,e,n)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/changes/file?path=").concat(encodeURIComponent(e)).concat(n?"&run=".concat(encodeURIComponent(n)):"")),taskStory:(t,o,e)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/story").concat(e?"?run=".concat(encodeURIComponent(e)):"")),taskFiles:(t,o,e)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/files").concat(e?"?run=".concat(encodeURIComponent(e)):"")),taskLogUrl:(t,o,e)=>"".concat(n,"/sessions/").concat(t,"/tasks/").concat(o,"/log").concat(e?"?run=".concat(encodeURIComponent(e)):""),taskFileUrl:(t,o,e,s,c)=>"".concat(n,"/sessions/").concat(t,"/tasks/").concat(o,"/files/").concat(e,"/").concat(encodeURIComponent(s)).concat(c?"?run=".concat(encodeURIComponent(c)):""),saveTaskLog:(t,o,e)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/log").concat(e?"?run=".concat(encodeURIComponent(e)):""),{method:"POST",body:"{}"}),saveTaskFile:(t,o,e,n,s)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/files/").concat(e,"/").concat(encodeURIComponent(n)).concat(s?"?run=".concat(encodeURIComponent(s)):""),{method:"POST",body:"{}"}),start:(t,o,e)=>m("/sessions/".concat(t,"/start"),{method:"POST",body:JSON.stringify({mode:o,name:e})}),stop:t=>m("/sessions/".concat(t,"/stop"),{method:"POST",body:"{}"}),approvals:t=>m("/approvals".concat(t?"?session=".concat(encodeURIComponent(t)):"")),decide:(t,o)=>m("/approvals/".concat(t),{method:"POST",body:JSON.stringify({action:o})}),setRunMode:(t,o)=>m("/sessions/".concat(t,"/mode"),{method:"POST",body:JSON.stringify({mode:o})}),streamUrl:t=>i("".concat(n,"/sessions/").concat(t,"/stream")),batch:()=>m("/batch").then(t=>null!=t?t:null),startBatch:(t,o,e,n,s,c,a)=>m("/batch/start",{method:"POST",body:JSON.stringify({sessionIds:t,mode:o,onFailure:e,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,o)=>m("/context/".concat(t,"?lang=").concat(encodeURIComponent(o))),setContext:(t,o)=>m("/context/".concat(t),{method:"PUT",body:JSON.stringify({content:o})}),resetContext:(t,o)=>m("/context/".concat(t,"?lang=").concat(encodeURIComponent(o)),{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 o,e,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==(o=/filename\*=UTF-8''([^;]+)/i.exec(i))?void 0:o[1],d=null==(e=/filename="([^"]+)"/i.exec(i))?void 0:e[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,o)=>{let e=new URLSearchParams;return o.run&&e.set("run",o.run),o.session&&e.set("session",o.session),o.task&&e.set("task",o.task),i("".concat(n,"/export/").concat(t,"?").concat(e.toString()))},debugExportUrl:t=>i("".concat(n,"/debug/export").concat(t.length>0?"?sessions=".concat(encodeURIComponent(t.join(","))):"")),vcsStatus:t=>m("/sessions/".concat(t,"/vcs")),vcsSnapshot:(t,o)=>m("/sessions/".concat(t,"/vcs/snapshot"),{method:"POST",body:JSON.stringify({choices:o})}),restorePreview:(t,o)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/restore")),restore:(t,o)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/restore"),{method:"POST",body:"{}"}),restartPlan:(t,o)=>m("/sessions/".concat(t,"/tasks/").concat(o,"/restart")),restartFrom:function(t,o){let e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return m("/sessions/".concat(t,"/tasks/").concat(o,"/restart"),{method:"POST",body:JSON.stringify(e)})},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 o=Math.round(t/1e3);if(o<60)return"".concat(o,"s");let e=Math.floor(o/60);return e<60?"".concat(e,"m ").concat(o%60,"s"):"".concat(Math.floor(e/60),"h ").concat(e%60,"m")}}}]);