copilot-operator 0.1.17 → 0.1.19

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 (56) hide show
  1. package/dist/src/api/operator.service.js +33 -2
  2. package/dist/src/cli.js +11 -29
  3. package/dist/src/config/version.js +30 -0
  4. package/dist/src/orchestrator/derivedChecks.js +19 -1
  5. package/dist/src/orchestrator/review.js +2 -2
  6. package/dist/src/orchestrator/taskRunner.js +130 -10
  7. package/dist/src/plan/brief.js +38 -4
  8. package/dist/src/plan/importPlan.js +42 -1
  9. package/dist/src/plan/schema.js +53 -0
  10. package/dist/src/plan/systemGuide.js +50 -2
  11. package/dist/src/protocol/replySchema.js +1 -1
  12. package/dist/src/protocol/reviewSchema.js +10 -5
  13. package/dist/src/session/exports.js +53 -13
  14. package/dist/src/session/model.js +20 -0
  15. package/dist/src/session/store.js +80 -0
  16. package/dist/src/vcs/artifacts.js +185 -0
  17. package/dist/src/vcs/inputs.js +449 -0
  18. package/dist/src/vcs/snapshot.js +174 -22
  19. package/dist/src/vcs/taskVcs.js +272 -37
  20. package/dist/web/404.html +2 -2
  21. package/dist/web/_next/static/chunks/549-5d7c845fb0a27dbe.js +1 -0
  22. package/dist/web/_next/static/chunks/867-d9c54f7a2b285b3f.js +1 -0
  23. package/dist/web/_next/static/chunks/app/appearance/page-a18019cdc395de3d.js +1 -0
  24. package/dist/web/_next/static/chunks/app/history/page-a0bd59986f7c8beb.js +1 -0
  25. package/dist/web/_next/static/chunks/app/layout-3efbb282292ccc38.js +1 -0
  26. package/dist/web/_next/static/chunks/app/sessions/view/page-99f9153fd75078d0.js +1 -0
  27. package/dist/web/_next/static/chunks/app/system/page-5ae1f8054208e6eb.js +1 -0
  28. package/dist/web/appearance.html +2 -2
  29. package/dist/web/appearance.txt +25 -26
  30. package/dist/web/defaults.html +2 -2
  31. package/dist/web/defaults.txt +25 -26
  32. package/dist/web/history.html +2 -2
  33. package/dist/web/history.txt +25 -26
  34. package/dist/web/import.html +2 -2
  35. package/dist/web/import.txt +25 -26
  36. package/dist/web/index.html +2 -2
  37. package/dist/web/index.txt +25 -26
  38. package/dist/web/level1.html +2 -2
  39. package/dist/web/level1.txt +25 -26
  40. package/dist/web/presets.html +2 -2
  41. package/dist/web/presets.txt +25 -26
  42. package/dist/web/sessions/view.html +2 -2
  43. package/dist/web/sessions/view.txt +25 -26
  44. package/dist/web/system.html +2 -2
  45. package/dist/web/system.txt +25 -26
  46. package/package.json +8 -3
  47. package/prompts/level1.md +547 -546
  48. package/dist/web/_next/static/chunks/549-a13ae394c5e6a837.js +0 -1
  49. package/dist/web/_next/static/chunks/867-7f27ef819d251cc5.js +0 -1
  50. package/dist/web/_next/static/chunks/app/appearance/page-9b13583bfee63b32.js +0 -1
  51. package/dist/web/_next/static/chunks/app/history/page-d7314ac22d47a3ab.js +0 -1
  52. package/dist/web/_next/static/chunks/app/layout-560bd6d173302e5a.js +0 -1
  53. package/dist/web/_next/static/chunks/app/sessions/view/page-d28825430ca280de.js +0 -1
  54. package/dist/web/_next/static/chunks/app/system/page-3aa2a0fef5454e69.js +0 -1
  55. /package/dist/web/_next/static/{kkxFcGzA9Uo1uRAgcoDti → ofnRLEgWx9th5w_qtuFnB}/_buildManifest.js +0 -0
  56. /package/dist/web/_next/static/{kkxFcGzA9Uo1uRAgcoDti → ofnRLEgWx9th5w_qtuFnB}/_ssgManifest.js +0 -0
@@ -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) {
@@ -27,6 +31,23 @@ export function trackedRepoOf(session) {
27
31
  export async function prepareForTask(session, task, bus, saveSession,
28
32
  /** Every session on record, for `startFrom: previous-session` to find the one before this. */
29
33
  allSessions = async () => []) {
34
+ const prepared = await prepare(session, task, bus, saveSession, allSessions);
35
+ /*
36
+ * Version control that is on but cannot do its part refuses the task, whatever the cause — a dirty
37
+ * tree, a starting branch that is not there, a branch git would not make. It used to be "on but
38
+ * inactive": the task ran, nothing was committed, and the task still ended done with its work
39
+ * loose in the tree. A task that cannot be committed is not run.
40
+ */
41
+ // Not when the operator chose no commits (`commitOnFinish` false): earlier tasks' files are then
42
+ // meant to stay in the tree, and refusing would stop every task after the first.
43
+ if (session.vcs?.enabled && session.vcs.commitOnFinish !== false && !prepared.refuse && !prepared.vcs.branch && prepared.vcs.problem) {
44
+ const why = `version control is on and cannot work for this task: ${prepared.vcs.problem.replace(/\.?\s*$/, '.')} Nothing was run.`;
45
+ bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-refused', level: 'error', message: why });
46
+ return { ...prepared, refuse: why };
47
+ }
48
+ return prepared;
49
+ }
50
+ async function prepare(session, task, bus, saveSession, allSessions) {
30
51
  const settings = session.vcs;
31
52
  if (!settings?.enabled)
32
53
  return { vcs: {}, note: '' };
@@ -38,35 +59,79 @@ allSessions = async () => []) {
38
59
  message: `version control is on but inactive: ${problem}` });
39
60
  return { vcs: { problem }, note: '' };
40
61
  }
62
+ const refuse = (why) => {
63
+ bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-refused', level: 'error', message: why });
64
+ return { vcs: { problem: why }, note: '', refuse: why };
65
+ };
66
+ /*
67
+ * The session's artifacts are never committed: their patterns go into the repository's own
68
+ * exclude file before anything looks at the tree, so they neither make it dirty nor go into a
69
+ * commit. See `artifacts.ts`.
70
+ */
71
+ const excluded = await excludeArtifacts(dir, session);
72
+ if (excluded.problem)
73
+ return refuse(`the session's artifacts could not be kept out of git: ${excluded.problem}. Nothing was run.`);
74
+ if (excluded.tracked.length > 0) {
75
+ bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-artifacts-tracked', level: 'warn',
76
+ 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.` });
77
+ }
78
+ if (excluded.tracked.length > 0 || artifactPatterns(settings).length > 0)
79
+ state = await repoState(dir);
41
80
  if (settings.startFrom === 'existing-branch')
42
- return await onExistingBranch(session, task, dir, state, bus, saveSession);
81
+ return await onExistingBranch(session, task, dir, state, bus, saveSession, allSessions);
43
82
  // The session's base is fixed the first time it runs: every per-task branch is cut from it,
44
83
  // which is what makes the tasks independent of each other rather than of the calendar. Where
45
84
  // it is taken from is the session's `startFrom`; see `sessionStart`.
46
85
  let base = session.vcsBaseCommit;
47
86
  let start = session.vcsStart;
48
87
  /*
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.
88
+ * The operator's own changes before the session's first task — under a snapshot policy, or its
89
+ * input files: they become the commit the session starts from (see `snapshot.ts`). Asked for
90
+ * approval, the run is refused until the list has been approved on the session's page; running
91
+ * without version control instead would be the one outcome the operator chose against.
53
92
  */
54
93
  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.');
94
+ const inputs = inputSettings(settings);
95
+ // Input files are looked for even in a clean tree: an ignored input does not make it dirty.
96
+ if ((state.dirty || inputs) && !base && (dirty.policy !== 'reject' || inputs)) {
97
+ const plan = await planSnapshot(session, allSessions);
98
+ if (plan.needed) {
99
+ if (!plan.ok)
100
+ return refuse(`the starting snapshot cannot be taken: ${plan.problem} Nothing was run.`);
101
+ if (plan.requireApproval) {
102
+ return refuse(`the repository has uncommitted changes (${someOf(plan.entries.map((e) => e.path))}), and this session takes them as its starting snapshot ` +
103
+ '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.');
104
+ }
105
+ const taken = await takeSnapshot(session, { approved: false }, bus, saveSession, allSessions);
106
+ if (!taken.ok)
107
+ return refuse(`the starting snapshot of the uncommitted changes was not taken: ${taken.problem} Nothing was run.`);
108
+ base = taken.start.commit;
109
+ start = taken.start;
110
+ state = await repoState(dir);
111
+ }
112
+ }
113
+ /*
114
+ * Started already, and input files changed or were added since: taken onto the session's line of
115
+ * work (see `recaptureInputs`), with the operator's approval unless they said otherwise. They used
116
+ * to be "a dirty tree to commit or stash": version control went off and the task ran without it.
117
+ */
118
+ if (base && inputs) {
119
+ const plan = await planSnapshot(session, allSessions);
120
+ if (plan.needed && plan.recapture) {
121
+ if (!plan.ok)
122
+ return refuse(`the input files changed since the session started cannot be taken: ${plan.problem} Nothing was run.`);
123
+ if (plan.requireApproval) {
124
+ return refuse(`input files changed or were added since the session started (${someOf(plan.entries.map((e) => e.path))}). Approve them on the session's page, ` +
125
+ 'Version control → "Uncommitted changes", and they are committed on its line of work before the next task. Nothing was run.');
126
+ }
127
+ const taken = await takeSnapshot(session, { approved: false }, bus, saveSession, allSessions);
128
+ if (!taken.ok)
129
+ return refuse(`the changed input files were not taken: ${taken.problem} Nothing was run.`);
130
+ start = taken.start;
131
+ if (plan.recapture.newBranch)
132
+ base = taken.start.inputs?.recaptured?.at(-1)?.commit ?? base;
133
+ state = await repoState(dir);
63
134
  }
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
135
  }
71
136
  // A dirty tree is the operator's own work in progress. Committing it under the bot's name
72
137
  // or moving it to another branch would both be decisions that are not ours to make — unless
@@ -74,9 +139,11 @@ allSessions = async () => []) {
74
139
  if (state.dirty) {
75
140
  const problem = `the repository has uncommitted changes (${state.changed.slice(0, 5).join(', ')}` +
76
141
  `${state.changed.length > 5 ? `, and ${state.changed.length - 5} more` : ''}). ` +
77
- 'Commit or stash them, so the task starts from a known state.';
142
+ (base
143
+ ? 'Commit or stash them, so the task starts from a known state.'
144
+ : 'Commit or stash them, or choose "Take them as a starting snapshot" under Version control → "Uncommitted changes" (input files: "Input files").');
78
145
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-dirty', level: 'warn',
79
- message: `version control is on but inactive: ${problem}` });
146
+ message: `version control cannot start: ${problem}` });
80
147
  return { vcs: { problem }, note: '' };
81
148
  }
82
149
  if (!base) {
@@ -98,6 +165,24 @@ allSessions = async () => []) {
98
165
  message: describeStart(chosen), data: { ...chosen } });
99
166
  }
100
167
  }
168
+ /*
169
+ * The operator's input files, in the commit the session starts from whatever its `startFrom`, with
170
+ * their sums on the record. Once per session: a start that has recorded them has them.
171
+ */
172
+ if (inputs && start && !start.inputs) {
173
+ const settled = await settleInputs(session, dir, start, allSessions);
174
+ if ('problem' in settled)
175
+ return refuse(`${settled.problem} Nothing was run.`);
176
+ const chosen = settled.start;
177
+ start = chosen;
178
+ base = chosen.commit;
179
+ await saveSession((s) => {
180
+ s.vcsBaseCommit = chosen.commit;
181
+ s.vcsStart = chosen;
182
+ });
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 } });
185
+ }
101
186
  const attempt = task.attempt ?? 1;
102
187
  const prefix = settings.branchPrefix || 'cop/';
103
188
  if (settings.branchMode === 'per-session') {
@@ -149,7 +234,7 @@ export function namedSessionBranch(vcs) {
149
234
  * refuses the task before a message is sent. The first run records the branch's tip as where the
150
235
  * session started, so the record and a restore know what was there before the work.
151
236
  */
152
- async function onExistingBranch(session, task, dir, state, bus, saveSession) {
237
+ async function onExistingBranch(session, task, dir, state, bus, saveSession, allSessions) {
153
238
  const wanted = (session.vcs?.existingBranch ?? '').trim();
154
239
  const refuse = (why) => {
155
240
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-refused', level: 'error', message: why });
@@ -181,6 +266,24 @@ async function onExistingBranch(session, task, dir, state, bus, saveSession) {
181
266
  bus.publish({ sessionId: session.id, taskId: task.id, type: 'vcs-start', level: updateWarns(chosen) ? 'warn' : 'info', message: describeStart(chosen), data: { ...chosen } });
182
267
  }
183
268
  }
269
+ /*
270
+ * The input files must already be on the branch, as they are: the runner commits nothing onto a
271
+ * branch the operator named, so one that lacks them, or has them otherwise, is refused.
272
+ */
273
+ if (inputSettings(session.vcs) && start && !start.inputs) {
274
+ const settled = await settleInputs(session, dir, start, allSessions);
275
+ if ('problem' in settled)
276
+ return refuse(`${settled.problem} Nothing was run.`);
277
+ if (settled.start.commit !== start.commit) {
278
+ 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. ` +
279
+ `Commit them on "${wanted}" yourself. Nothing was run.`);
280
+ }
281
+ const chosen = settled.start;
282
+ start = chosen;
283
+ await saveSession((s) => {
284
+ s.vcsStart = chosen;
285
+ });
286
+ }
184
287
  const prepared = await switchTo(session, task, dir, wanted, undefined, bus, { reuseExisting: true, mustExist: true, start });
185
288
  return prepared.vcs.branch === wanted ? prepared : refuse(`the repository could not be put on "${wanted}": ${prepared.vcs.problem ?? 'unknown reason'}. Nothing was run.`);
186
289
  }
@@ -213,20 +316,88 @@ export async function sessionStart(session, dir, head, allSessions) {
213
316
  };
214
317
  if (how === 'branch')
215
318
  return await fromBranch();
319
+ /*
320
+ * The session before this one: the one that ran last in this repository, however it ended — not the
321
+ * last one that happened to commit, which skipped a predecessor that had failed or committed
322
+ * nothing and continued from older work as if it were the latest. A session set aside does not count.
323
+ */
216
324
  const here = normalise(dir);
217
- let best = null;
325
+ let pred = null;
218
326
  for (const other of await allSessions()) {
219
- if (other.id === session.id || !other.vcs?.enabled || normalise(repoDirOf(other)) !== here)
327
+ if (other.id === session.id || !other.vcs?.enabled || other.active === false || normalise(repoDirOf(other)) !== here)
220
328
  continue;
221
- const last = lastWorkOf(other);
222
- if (last && (!best || last.at > best.at))
223
- best = { session: other, ...last };
329
+ const at = lastActivityOf(other);
330
+ if (at && (!pred || at > pred.at))
331
+ pred = { session: other, at };
332
+ }
333
+ if (!pred)
334
+ return await fromBranch(`no earlier session has run in this repository, so it starts from "${baseBranch}"`);
335
+ const before = pred.session;
336
+ /*
337
+ * Checked before anything is carried on: the previous session finished, and what it produced is
338
+ * there — a final commit on a branch that still exists, or a result that is artifacts only (or no
339
+ * change at all), which ends where it started. Anything else is refused with what is wrong; it used
340
+ * to fall back to the base branch with a note, and the chain went on without the work it needed.
341
+ */
342
+ const unfinished = before.tasks.filter((t) => t.status !== 'done');
343
+ if (unfinished.length > 0) {
344
+ return {
345
+ problem: `this session continues the previous session in this repository, "${before.name}", which has not finished: ` +
346
+ `${unfinished.slice(0, 6).map((t) => `"${t.title}" is ${t.status}`).join(', ')}${unfinished.length > 6 ? `, and ${unfinished.length - 6} more` : ''}. ` +
347
+ 'Finish it first (Continue or Run again), or choose another "Start from".',
348
+ };
349
+ }
350
+ const work = lastWorkOf(before);
351
+ if (!work) {
352
+ const startedAt = before.vcsBaseCommit;
353
+ if (!startedAt || !(await git(dir, ['cat-file', '-e', `${startedAt}^{commit}`])).ok) {
354
+ return { problem: `the previous session "${before.name}" committed nothing, and the commit it started from is not in the repository, so there is nothing to carry on from.` };
355
+ }
356
+ const artifactsOnly = before.tasks.some((t) => (t.artifactsKept?.length ?? 0) > 0);
357
+ return {
358
+ start: {
359
+ kind: 'previous-session',
360
+ commit: startedAt,
361
+ ...(before.vcsStart?.branch ? { branch: before.vcsStart.branch } : {}),
362
+ fromSession: { id: before.id, name: before.name },
363
+ note: artifactsOnly
364
+ ? `"${before.name}" committed nothing — its result is artifacts only — so this session starts where it started (${startedAt.slice(0, 8)})`
365
+ : `"${before.name}" changed nothing, so this session starts where it started (${startedAt.slice(0, 8)})`,
366
+ },
367
+ };
224
368
  }
225
- if (!best)
226
- return await fromBranch(`no earlier session has committed work in this repository, so it starts from "${baseBranch}"`);
369
+ const best = { session: before, ...work };
227
370
  const tip = await branchTip(dir, best.branch);
228
371
  if (!tip) {
229
- 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}"`);
372
+ return {
373
+ problem: `the branch of the previous session "${best.session.name}" (${best.branch}) is no longer in the repository, so its final commit cannot be carried on. ` +
374
+ 'Restore the branch, or choose another "Start from".',
375
+ };
376
+ }
377
+ /*
378
+ * The whole of that session's done work, or nothing. In per-task mode every task has a branch of
379
+ * its own, all cut from one commit, so the branch of the task that finished last holds that task's
380
+ * work only — a chain that started there began without the files the earlier tasks had made,
381
+ * though they were in the repository on other branches. The runner does not merge them; it says
382
+ * which are missing and refuses, rather than start from part of the work as if it were all.
383
+ */
384
+ const missing = [];
385
+ for (const t of best.session.tasks) {
386
+ if (t.status !== 'done')
387
+ continue;
388
+ const done = [{ vcs: t.vcs, at: t.finishedAt }, ...(t.attempts ?? []).map((a) => ({ vcs: a.vcs, at: a.finishedAt }))]
389
+ .filter((v) => !!v.vcs?.commit && !!v.vcs.branch)
390
+ .sort((a, b) => (b.at ?? '').localeCompare(a.at ?? ''))[0];
391
+ if (done?.vcs?.commit && !(await isAncestor(dir, done.vcs.commit, tip)))
392
+ missing.push(`"${t.title}" (on ${done.vcs.branch})`);
393
+ }
394
+ if (missing.length > 0) {
395
+ return {
396
+ problem: `this session continues the previous session "${best.session.name}", whose done work is not on one branch: ${best.branch} does not have ` +
397
+ `${missing.slice(0, 8).join(', ')}${missing.length > 8 ? `, and ${missing.length - 8} more` : ''}. ` +
398
+ '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"), ' +
399
+ 'or run that session "One branch for the whole session" so the next one can carry on from it.',
400
+ };
230
401
  }
231
402
  return { start: { kind: 'previous-session', commit: tip, branch: best.branch, fromSession: { id: best.session.id, name: best.session.name } } };
232
403
  }
@@ -247,6 +418,17 @@ function lastWorkOf(session) {
247
418
  }
248
419
  return best;
249
420
  }
421
+ /** When a session last did anything: the latest start or end of any of its tasks' attempts. */
422
+ function lastActivityOf(session) {
423
+ let at = null;
424
+ for (const t of session.tasks) {
425
+ for (const v of [t.finishedAt, t.startedAt, ...(t.attempts ?? []).flatMap((a) => [a.finishedAt, a.startedAt])]) {
426
+ if (v && (!at || v > at))
427
+ at = v;
428
+ }
429
+ }
430
+ return at;
431
+ }
250
432
  async function branchTip(dir, branch) {
251
433
  if (!(await isValidBranchName(dir, branch)))
252
434
  return null;
@@ -271,6 +453,13 @@ export function describeStart(start) {
271
453
  const updated = start.update ? ` (${describeUpdate(start.update)})` : '';
272
454
  return start.note ? `${said}${updated} — ${start.note}` : `${said}${updated}`;
273
455
  }
456
+ /** One line for the log: the session's input files, and whether the runner had to commit them on its start. */
457
+ export function describeInputs(start) {
458
+ const n = start.inputs?.files.length ?? 0;
459
+ const carried = start.inputs?.carried;
460
+ return (`${n} input file(s) in the session's start (${start.commit.slice(0, 8)})${start.inputs?.readOnly ? ', read-only' : ''}` +
461
+ (carried ? `: they were not in ${carried.onto.slice(0, 8)}, so they were committed on top of it as ${carried.branch}, from ${carried.from}` : ''));
462
+ }
274
463
  /** Whether the update before a session's start is worth a warning: it did not bring the branch up to date. */
275
464
  export function updateWarns(start) {
276
465
  return !!start.update && !['updated', 'up-to-date', 'no-remote'].includes(start.update.outcome);
@@ -346,6 +535,7 @@ opts) {
346
535
  base: vcs.baseCommit ? { commit: vcs.baseCommit, subject } : undefined,
347
536
  earlier,
348
537
  start: opts.start,
538
+ artifacts: artifactPatterns(session.vcs),
349
539
  });
350
540
  return { vcs, note };
351
541
  }
@@ -408,6 +598,14 @@ export function noteFor(repoDir, vcs, ctx = { mode: 'per-task', earlier: [] }) {
408
598
  'commit instead: `git merge-base --is-ancestor <commit> HEAD` exits 0 when it is.',
409
599
  '',
410
600
  ...(began ? [began, ''] : []),
601
+ ...(inputsNote(ctx.start?.inputs) ? [inputsNote(ctx.start?.inputs), ''] : []),
602
+ ...((ctx.artifacts?.length ?? 0) > 0
603
+ ? [
604
+ `Evidence goes under ${ctx.artifacts?.map((a) => `\`${a}\``).join(', ')}: those files are kept with the run's record and are never committed, ` +
605
+ 'so write reports, archives and test results there rather than changing .gitignore for them.',
606
+ '',
607
+ ]
608
+ : []),
411
609
  standing,
412
610
  '',
413
611
  '- Do not create branches, switch branches, commit, stash, reset or revert. That is the',
@@ -515,6 +713,30 @@ export async function commitTaskResult(session, task, outcome, bus) {
515
713
  ...(foreign.length > 0 ? { foreignCommits: foreign } : {}),
516
714
  });
517
715
  }
716
+ /**
717
+ * Why a task's work is not committed although version control is on and commits it, or undefined
718
+ * when it is (or there was nothing to commit). Read after `commitTaskResult`; a task that would end
719
+ * done ends failed with this instead. See `finish` in the runner.
720
+ */
721
+ export async function commitShortfall(session, before, after, error) {
722
+ if (!session.vcs?.enabled || !session.vcs.commitOnFinish)
723
+ return undefined;
724
+ if (error)
725
+ return `version control failed after the task, so its work was not committed: ${error}. The changes are left in the working tree.`;
726
+ if (!after?.branch) {
727
+ // Never on a branch of its own. The start refuses that now; whatever got through, it is said.
728
+ const state = await repoState(repoDirOf(session));
729
+ return state.isRepo && state.dirty
730
+ ? `version control is on but was not active for this task (${after?.problem ?? before?.problem ?? 'no branch was made'}), so its changes were not committed: ${someOf(state.changed)}. They are left in the working tree.`
731
+ : undefined;
732
+ }
733
+ if (after.problem)
734
+ return `the runner could not commit the task's work: ${after.problem}`;
735
+ if (after.afterCommit && !after.afterCommit.clean) {
736
+ return `after the runner's commit the working tree still has uncommitted changes (${someOf(after.afterCommit.changed)}), so not all of the task's work is on ${after.branch}.`;
737
+ }
738
+ return undefined;
739
+ }
518
740
  /**
519
741
  * Commits what a task left behind when the process running it ended before it could.
520
742
  *
@@ -699,27 +921,40 @@ export async function vcsPreflight(session, allSessions = async () => []) {
699
921
  const state = await repoState(repoDir);
700
922
  if (!state.isRepo)
701
923
  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') {
924
+ // Artifacts do not count: the run keeps them out of git before it looks (see `excludeArtifacts`).
925
+ const artifacts = artifactPatterns(settings);
926
+ const changed = state.dirty && artifacts.length > 0 ? (await workingTreePaths(repoDir)).filter((p) => !inScope(p, artifacts)) : state.changed;
927
+ const dirty = changed.length > 0;
928
+ /*
929
+ * Before the first task, under a snapshot policy or with input files: the list to approve, file by
930
+ * file. Input files are looked for even in a clean tree — an ignored input does not make it dirty.
931
+ */
932
+ const inputs = !!inputSettings(settings);
933
+ if ((dirty || inputs) && (!session.vcsBaseCommit || inputs) && (dirtyPolicy(settings).policy !== 'reject' || inputs) && settings.startFrom !== 'existing-branch') {
704
934
  const snapshot = await planSnapshot(session, allSessions);
705
935
  if (snapshot.needed) {
936
+ const listed = snapshot.entries.map((e) => e.path);
706
937
  return {
707
938
  ok: false,
708
939
  repoDir,
709
940
  branch: state.branch ?? undefined,
710
- 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'}.`
712
- : `There are uncommitted changes, and no starting snapshot can be taken: ${snapshot.problem}`,
941
+ problem: snapshot.recapture
942
+ ? snapshot.ok
943
+ ? `Input files changed or were added since the session started (${someOf(listed, 3)}). They are committed on its line of work ${snapshot.requireApproval ? 'once you approve the list below' : 'when its next task starts'}.`
944
+ : `Input files changed since the session started, and they cannot be taken: ${snapshot.problem}`
945
+ : snapshot.ok
946
+ ? `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'}.`
947
+ : `There are uncommitted changes, and no starting snapshot can be taken: ${snapshot.problem}`,
713
948
  snapshot,
714
949
  };
715
950
  }
716
951
  }
717
- if (state.dirty) {
952
+ if (dirty) {
718
953
  return {
719
954
  ok: false,
720
955
  repoDir,
721
956
  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.`,
957
+ problem: `There are uncommitted changes (${changed.slice(0, 3).join(', ')}${changed.length > 3 ? '…' : ''}). Commit or stash them first.`,
723
958
  };
724
959
  }
725
960
  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><!--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>
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[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>
@@ -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")}}}]);