copilot-operator 0.1.32 → 0.1.33

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 (34) hide show
  1. package/dist/src/api/operator.service.js +10 -2
  2. package/dist/src/orchestrator/taskRunner.js +24 -14
  3. package/dist/src/plan/importPlan.js +5 -0
  4. package/dist/src/plan/systemGuide.js +8 -0
  5. package/dist/src/transport/copilotTransport.js +70 -36
  6. package/dist/src/transport/locators.js +11 -0
  7. package/dist/web/404.html +2 -2
  8. package/dist/web/_next/static/chunks/867-dfaaa8047cbf2fc2.js +1 -0
  9. package/dist/web/_next/static/chunks/app/defaults/page-54f8f1ab4cac47fa.js +1 -0
  10. package/dist/web/_next/static/chunks/app/sessions/view/page-ce99494b1997ee25.js +1 -0
  11. package/dist/web/appearance.html +2 -2
  12. package/dist/web/appearance.txt +10 -10
  13. package/dist/web/defaults.html +2 -2
  14. package/dist/web/defaults.txt +10 -10
  15. package/dist/web/history.html +2 -2
  16. package/dist/web/history.txt +10 -10
  17. package/dist/web/import.html +2 -2
  18. package/dist/web/import.txt +10 -10
  19. package/dist/web/index.html +2 -2
  20. package/dist/web/index.txt +10 -10
  21. package/dist/web/level1.html +2 -2
  22. package/dist/web/level1.txt +10 -10
  23. package/dist/web/presets.html +2 -2
  24. package/dist/web/presets.txt +10 -10
  25. package/dist/web/sessions/view.html +2 -2
  26. package/dist/web/sessions/view.txt +10 -10
  27. package/dist/web/system.html +2 -2
  28. package/dist/web/system.txt +10 -10
  29. package/package.json +2 -1
  30. package/dist/web/_next/static/chunks/867-52e371cb1ec49089.js +0 -1
  31. package/dist/web/_next/static/chunks/app/defaults/page-17dd586d1d6bd068.js +0 -1
  32. package/dist/web/_next/static/chunks/app/sessions/view/page-54ab3cb89b84679d.js +0 -1
  33. /package/dist/web/_next/static/{_Or9hW0soHRVNEEgEqiGt → G_-u_mF-aLMdcOv0kc9us}/_buildManifest.js +0 -0
  34. /package/dist/web/_next/static/{_Or9hW0soHRVNEEgEqiGt → G_-u_mF-aLMdcOv0kc9us}/_ssgManifest.js +0 -0
@@ -398,8 +398,11 @@ let OperatorService = class OperatorService {
398
398
  if (patch.onFailure === 'stop' || patch.onFailure === 'continue')
399
399
  s.onFailure = patch.onFailure;
400
400
  // An empty string is a real choice here: it means "leave the chat on whatever it is".
401
- if (patch.model !== undefined)
401
+ if (patch.model !== undefined) {
402
402
  s.model = patch.model.trim() || undefined;
403
+ // Chosen on the session's page: the operator's own word, which outranks Settings.
404
+ s.modelSource = s.model ? 'operator' : undefined;
405
+ }
403
406
  // The same for the group: clearing it gives this session its conversation back. It does
404
407
  // not move the conversation it is already in; that history is where it is.
405
408
  if (patch.conversationGroup !== undefined)
@@ -408,6 +411,8 @@ let OperatorService = class OperatorService {
408
411
  // Empty model means "the session's own", which is a real choice and not a missing one.
409
412
  const merged = { ...DEFAULT_REVIEW, ...s.review, ...patch.review };
410
413
  s.review = { enabled: merged.enabled !== false, model: (merged.model ?? '').trim() };
414
+ if (patch.review.model !== undefined)
415
+ s.reviewModelSource = s.review.model ? 'operator' : undefined;
411
416
  }
412
417
  if (patch.projectDir !== undefined)
413
418
  s.projectDir = patch.projectDir.trim();
@@ -1339,13 +1344,16 @@ let OperatorService = class OperatorService {
1339
1344
  if (entry.state !== 'waiting')
1340
1345
  continue;
1341
1346
  await this.store.updateSession(entry.sessionId, (s) => {
1342
- if (wantedModel && (s.model?.trim() || wantedModel !== followModel))
1347
+ if (wantedModel && (s.model?.trim() || wantedModel !== followModel)) {
1343
1348
  s.model = wantedModel;
1349
+ s.modelSource = 'operator';
1350
+ }
1344
1351
  // Only the model is set here, never whether the review happens: a run panel is about
1345
1352
  // this run, and silently switching a session's review on or off from it would be a
1346
1353
  // change to the session that outlives the run.
1347
1354
  if (wantedReviewModel && (s.review?.model?.trim() || wantedReviewModel !== followReview)) {
1348
1355
  s.review = { ...DEFAULT_REVIEW, ...s.review, model: wantedReviewModel };
1356
+ s.reviewModelSource = 'operator';
1349
1357
  }
1350
1358
  });
1351
1359
  }
@@ -60,12 +60,13 @@ export const NOT_THE_CHATS = ['contract-conflict', 'environment', 'invalid-check
60
60
  * Records a model the page offers under a new name where it was chosen — the session, or Settings — and
61
61
  * the line-up read on the way. Returns the sentence that says so, or ''.
62
62
  */
63
- async function followPageModels(which, wanted, result, session, store, hooks) {
63
+ async function followPageModels(which, wanted, result, session, store, hooks, from) {
64
64
  if (result.options?.length)
65
65
  await hooks?.seen?.(result.options, result.current).catch(() => undefined);
66
66
  if (!result.ok || !result.matched)
67
67
  return '';
68
- const own = which === 'model' ? session.model?.trim() : session.review?.model?.trim();
68
+ // Written back where it came from: Settings for a model from Settings, the session for the rest.
69
+ const own = from !== 'settings';
69
70
  if (own) {
70
71
  await store
71
72
  ?.updateSession(session.id, (s) => {
@@ -514,14 +515,23 @@ export async function openSessionTransport(cfg, session, bus, runsDir) {
514
515
  * A session gets a model of its own from the plan or from its own picker; one that has none
515
516
  * follows Settings, so a model chosen there after the session was made still reaches it.
516
517
  */
518
+ /**
519
+ * The models a session runs on, and where each came from.
520
+ *
521
+ * The operator's choice on the session's page or the run panel first; then Settings; the plan's last.
522
+ * Settings used to come after any model the session held, and a plan imported with a "model" — written
523
+ * by another chat, often a model the picker no longer had — overrode what the operator chose in Settings
524
+ * on every run (operator's rule, 2026-10-06: what is set in Settings outranks the same thing in the JSON).
525
+ */
517
526
  export function effectiveModels(session, cfg) {
518
- return {
519
- model: (session.model ?? '').trim() || (cfg.copilot.defaultModel ?? '').trim(),
520
- reviewModel: (session.review?.model ?? '').trim() || (cfg.copilot.defaultReviewModel ?? '').trim(),
521
- };
527
+ const pick = (own, source, settings) => own && source === 'operator' ? [own, 'session'] : settings ? [settings, 'settings'] : own ? [own, 'plan'] : ['', 'none'];
528
+ const [model, modelFrom] = pick((session.model ?? '').trim(), session.modelSource, (cfg.copilot.defaultModel ?? '').trim());
529
+ const [reviewModel, reviewModelFrom] = pick((session.review?.model ?? '').trim(), session.reviewModelSource, (cfg.copilot.defaultReviewModel ?? '').trim());
530
+ return { model, reviewModel, modelFrom, reviewModelFrom };
522
531
  }
523
532
  async function applySessionModel(transport, session, bus, cfg, store, hooks) {
524
- const wanted = effectiveModels(session, cfg).model;
533
+ const { model: wanted, modelFrom } = effectiveModels(session, cfg);
534
+ const fromSettings = modelFrom === 'settings';
525
535
  if (!wanted)
526
536
  return {};
527
537
  const locator = await hooks?.locate?.(wanted).catch(() => undefined);
@@ -530,7 +540,7 @@ async function applySessionModel(transport, session, bus, cfg, store, hooks) {
530
540
  current: null,
531
541
  reason: e.message,
532
542
  }));
533
- const renamed = await followPageModels('model', wanted, result, session, store, hooks);
543
+ const renamed = await followPageModels('model', wanted, result, session, store, hooks, modelFrom);
534
544
  if (renamed)
535
545
  bus.publish({ sessionId: session.id, type: 'model-renamed', level: 'warn', message: renamed, data: { asked: wanted, chosen: result.matched } });
536
546
  bus.publish({
@@ -538,9 +548,9 @@ async function applySessionModel(transport, session, bus, cfg, store, hooks) {
538
548
  type: result.ok ? 'model-selected' : 'model-not-selected',
539
549
  level: result.ok ? 'info' : 'warn',
540
550
  message: result.ok
541
- ? `model: ${result.current ?? wanted}${session.model?.trim() ? '' : ' (from Settings)'}`
542
- : `could not switch to "${wanted}"${session.model?.trim() ? '' : ' (the default model in Settings)'}: ${(result.reason ?? 'unknown reason').replace(/[.\s]+$/, '')}. Nothing is sent on ${result.current ?? 'the chat default'} instead.`,
543
- data: { asked: wanted, fromSettings: !session.model?.trim(), current: result.current, ok: result.ok, by: result.by ?? null, savedLocator: !!locator },
551
+ ? `model: ${result.current ?? wanted} (${modelFrom === 'settings' ? 'from Settings' : modelFrom === 'session' ? "the session's own" : 'from the plan'})`
552
+ : `could not switch to "${wanted}" (${modelFrom === 'settings' ? 'the default model in Settings' : modelFrom === 'session' ? "the session's own" : 'from the plan'}): ${(result.reason ?? 'unknown reason').replace(/[.\s]+$/, '')}. Nothing is sent on ${result.current ?? 'the chat default'} instead.`,
553
+ data: { asked: wanted, from: modelFrom, fromSettings, current: result.current, ok: result.ok, by: result.by ?? null, savedLocator: !!locator },
544
554
  });
545
555
  // Kept on the session, so each task's record says which model it ran on and why (see `runTask`).
546
556
  await store
@@ -549,7 +559,7 @@ async function applySessionModel(transport, session, bus, cfg, store, hooks) {
549
559
  asked: wanted,
550
560
  current: result.current ?? null,
551
561
  ok: result.ok,
552
- fromSettings: !session.model?.trim(),
562
+ fromSettings,
553
563
  ...(result.matched ? { renamedTo: result.matched } : {}),
554
564
  ...(result.ok ? {} : { reason: (result.reason ?? 'unknown reason').replace(/[.\s]+$/, '') }),
555
565
  at: new Date().toISOString(),
@@ -563,7 +573,7 @@ async function applySessionModel(transport, session, bus, cfg, store, hooks) {
563
573
  if (!result.ok) {
564
574
  return {
565
575
  current: result.current ?? undefined,
566
- refused: `The chat could not be put on the chosen model "${wanted}"${session.model?.trim() ? '' : ' (Settings)'}: ${(result.reason ?? 'unknown reason').replace(/[.\s]+$/, '')}. ` +
576
+ refused: `The chat could not be put on the chosen model "${wanted}"${fromSettings ? ' (Settings)' : ''}: ${(result.reason ?? 'unknown reason').replace(/[.\s]+$/, '')}. ` +
567
577
  'Nothing was sent. Read the list again under Settings → Model, choose the model there, and start again.',
568
578
  };
569
579
  }
@@ -1748,7 +1758,7 @@ export async function runTask(transport, session, task, deps) {
1748
1758
  if (model) {
1749
1759
  const reviewLocator = await deps.models?.locate?.(model).catch(() => undefined);
1750
1760
  const picked = await transport.selectModel(model, { locator: reviewLocator }).catch((e) => ({ ok: false, current: null, reason: e.message }));
1751
- const renamed = await followPageModels('review', model, picked, session, store, deps.models);
1761
+ const renamed = await followPageModels('review', model, picked, session, store, deps.models, effectiveModels(session, cfg).reviewModelFrom);
1752
1762
  if (renamed)
1753
1763
  sink.event('model-renamed', { asked: model, chosen: picked.matched, review: true }, renamed, 'warn');
1754
1764
  sink.event(picked.ok ? 'review-model-selected' : 'review-model-not-selected', { asked: model, current: picked.current }, picked.ok ? `the review runs on ${picked.current ?? model}` : `the review could not switch to "${model}": ${picked.reason ?? 'unknown reason'}`, picked.ok ? 'info' : 'warn');
@@ -232,6 +232,11 @@ defaultProjectDir = '') {
232
232
  s.review = { enabled: planned.review?.enabled !== false, model: (planned.review?.model || defaultReviewModel).trim() };
233
233
  s.conversationGroup = group || undefined;
234
234
  s.model = (planned.model || defaultModel).trim() || undefined;
235
+ // The plan's models are kept, and marked as the plan's: Settings outrank them (see `effectiveModels`).
236
+ if (planned.model?.trim())
237
+ s.modelSource = 'plan';
238
+ if (planned.review?.model?.trim())
239
+ s.reviewModelSource = 'plan';
235
240
  s.planName = plan.plan.trim() || undefined;
236
241
  s.vcs = vcs;
237
242
  // After the plan's own folders are in place, so only a session that named none is pointed at it.
@@ -3004,6 +3004,14 @@ export const SYSTEM_GUIDE = [
3004
3004
  doesEn: 'How many different approaches a "blocked" must list (2–20); with fewer the chat is asked for another.',
3005
3005
  doesBg: 'Колко различни подхода трябва да изброи „blocked“ (2–20); с по-малко чатът е помолен за още един.',
3006
3006
  },
3007
+ {
3008
+ key: 'exec.maxReviewRounds',
3009
+ kind: 'field',
3010
+ en: 'Rounds of fixing after a failed review',
3011
+ bg: 'Кръгове поправка след неуспешен преглед',
3012
+ doesEn: 'How many times a failed review sends its findings back to be fixed and reviewed again (1–6) before the task ends blocked; separate from the approaches above.',
3013
+ doesBg: 'Колко пъти неуспешен преглед връща находките за поправка и нов преглед (1–6), преди задачата да завърши „blocked“; отделно от подходите по-горе.',
3014
+ },
3007
3015
  {
3008
3016
  key: 'exec.retryBlocked',
3009
3017
  kind: 'field',
@@ -10,13 +10,13 @@
10
10
  * 2. Enter does not submit the composer. The Send button has to be clicked.
11
11
  * 3. A message cannot consist of an attachment alone; Send stays disabled without text.
12
12
  */
13
- import { normModel, pageModelFor, sameModel } from './modelMatch.js';
13
+ import { buttonShows, normModel, pageModelFor, sameModel } from './modelMatch.js';
14
14
  import { chromium } from 'playwright';
15
15
  import { mkdir, writeFile } from 'node:fs/promises';
16
16
  import { join } from 'node:path';
17
17
  import { findRecentCrash, describeCrash } from './edgeCrash.js';
18
18
  import { landed } from './acceptance.js';
19
- import { Blocker, Css, Label, Model, OperatorModelLocators, Rename, Sidebar, Signal, Surface, TestId, Upload, Url } from './locators.js';
19
+ import { Blocker, Css, Label, Model, ModelPicking, OperatorModelLocators, Rename, Sidebar, Signal, Surface, TestId, Upload, Url } from './locators.js';
20
20
  import { acquireProfileLock } from './profileLock.js';
21
21
  import { parseChatId } from './chatSession.js';
22
22
  /**
@@ -938,17 +938,23 @@ Current URL: ${url}`);
938
938
  async chooseByOperatorLocators(name, target) {
939
939
  const before = await this.currentModel();
940
940
  const textOf = async (el) => (((await el.innerText().catch(() => '')) ?? '').split('\n')[0] ?? '').trim();
941
+ // A fixed pause after every opening and every press (see `ModelPicking`); the checks give it less.
942
+ const settle = () => this.p.waitForTimeout(this.opts.modelSettleMs ?? ModelPicking.settleMs);
943
+ const appear = this.opts.modelAppearMs ?? ModelPicking.appearMs;
944
+ /** Waits until the element is there and visible. */
945
+ const present = async (el) => await el.waitFor({ state: 'visible', timeout: appear }).then(() => true).catch(() => false);
941
946
  const reach = async () => {
942
947
  const button = await this.resolveModelButton();
943
948
  if (!button)
944
949
  return { problem: 'This chat does not show a model picker.' };
945
950
  await button.click();
951
+ await settle();
946
952
  if (!(await this.waitForPopup()))
947
953
  return { problem: 'The model picker did not open.' };
948
954
  this.emit('model-step', { step: 'picker opened' });
949
955
  if (target.group) {
950
- const group = await this.rowByXPath(target.group.xpath, target.group.name, 5_000);
951
- if (!group)
956
+ const group = await this.rowByXPath(target.group.xpath, target.group.name, appear);
957
+ if (!group || !(await present(group)))
952
958
  return { problem: `the "${target.group.name}" row was not found by ${target.group.xpath}` };
953
959
  this.emit('model-step', { step: 'group found', text: await textOf(group) });
954
960
  /*
@@ -965,8 +971,9 @@ Current URL: ${url}`);
965
971
  ];
966
972
  for (const [how, act] of ways) {
967
973
  await act().catch(() => undefined);
968
- const row = await this.rowByXPath(target.xpath, target.model, 3_000);
969
- if (row) {
974
+ await settle();
975
+ const row = await this.rowByXPath(target.xpath, target.model, appear);
976
+ if (row && (await present(row))) {
970
977
  this.emit('model-step', { step: 'group opened', how, model: await textOf(row) });
971
978
  return { row };
972
979
  }
@@ -975,45 +982,59 @@ Current URL: ${url}`);
975
982
  await this.captureMenu('select-failed', await this.readMenuRows().catch(() => [])).catch(() => undefined);
976
983
  return { problem: `"${target.model}" did not appear after opening "${target.group.name}" every way (${target.xpath}); the menu is kept as models-select-failed.html/.png` };
977
984
  }
978
- const row = await this.rowByXPath(target.xpath, target.model, 5_000);
979
- if (!row) {
985
+ const row = await this.rowByXPath(target.xpath, target.model, appear);
986
+ if (!row || !(await present(row))) {
980
987
  await this.captureMenu('select-failed', await this.readMenuRows().catch(() => [])).catch(() => undefined);
981
988
  return { problem: `"${target.model}" was not found by ${target.xpath}; the menu is kept as models-select-failed.html/.png` };
982
989
  }
983
990
  return { row };
984
991
  };
985
992
  const checked = async (row) => (await row.getAttribute('aria-checked').catch(() => null)) === 'true';
986
- const found = await reach();
987
- if ('problem' in found) {
993
+ /*
994
+ * The whole choice, tried again from a closed menu while the picker does not show the model as chosen,
995
+ * up to `ModelPicking.attempts` times (operator's request, 2026-10-06: "if it is not set, try again").
996
+ */
997
+ const attempts = this.opts.modelAttempts ?? ModelPicking.attempts;
998
+ let why = '';
999
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
1000
+ this.emit('model-step', { step: 'attempt', attempt, of: attempts, model: name });
1001
+ const found = await reach();
1002
+ if ('problem' in found) {
1003
+ why = found.problem;
1004
+ this.emit('model-step', { step: 'not reached', attempt, why });
1005
+ await this.closeMenu();
1006
+ await settle();
1007
+ continue;
1008
+ }
1009
+ if (await checked(found.row)) {
1010
+ await this.closeMenu();
1011
+ this.emit('model-already-selected', { model: name, buttonShows: before, by: 'operator', attempt });
1012
+ return { ok: true, current: name, by: 'operator' };
1013
+ }
1014
+ // On the element itself, for the same reason as the group; the mouse only when that does nothing.
1015
+ this.emit('model-step', { step: 'model pressed', attempt, text: await textOf(found.row) });
1016
+ await found.row.evaluate((el) => el.click(), undefined, { timeout: 3_000 }).catch(() => undefined);
1017
+ await settle();
1018
+ if ((await this.modelMenuOpen()) && !(await checked(found.row))) {
1019
+ await found.row.click({ timeout: 3_000 }).catch(() => undefined);
1020
+ await settle();
1021
+ }
988
1022
  await this.closeMenu();
989
- return { ok: false, current: before, reason: found.problem };
990
- }
991
- if (await checked(found.row)) {
1023
+ await settle();
1024
+ // Is it set? Read again, the same way, from a fresh menu.
1025
+ const again = await reach();
1026
+ const ok = 'row' in again && (await checked(again.row));
992
1027
  await this.closeMenu();
993
- this.emit('model-already-selected', { model: name, buttonShows: before, by: 'operator' });
994
- return { ok: true, current: name, by: 'operator' };
995
- }
996
- // On the element itself, for the same reason as the group; the mouse only when that does nothing.
997
- this.emit('model-step', { step: 'model pressed', text: await textOf(found.row) });
998
- await found.row.evaluate((el) => el.click(), undefined, { timeout: 3_000 }).catch(() => undefined);
999
- await this.p.waitForTimeout(800);
1000
- if (await this.modelMenuOpen() && !(await checked(found.row)))
1001
- await found.row.click({ timeout: 3_000 }).catch(() => undefined);
1002
- await this.p.waitForTimeout(400);
1003
- await this.closeMenu();
1004
- const again = await reach();
1005
- const ok = 'row' in again && (await checked(again.row));
1006
- await this.closeMenu();
1007
- const after = await this.currentModel();
1008
- if (ok) {
1009
- this.emit('model-selected', { model: name, buttonShows: after, by: 'operator' });
1010
- return { ok: true, current: name, by: 'operator' };
1028
+ const after = await this.currentModel();
1029
+ if (ok) {
1030
+ this.emit('model-selected', { model: name, buttonShows: after, by: 'operator', attempt });
1031
+ return { ok: true, current: name, by: 'operator' };
1032
+ }
1033
+ why = 'problem' in again ? `after pressing "${target.model}": ${again.problem}` : `"${target.model}" was pressed, but the picker does not mark it as chosen (the button shows "${after ?? 'nothing'}")`;
1034
+ this.emit('model-step', { step: 'not set, trying again', attempt, why });
1035
+ await settle();
1011
1036
  }
1012
- return {
1013
- ok: false,
1014
- current: after,
1015
- reason: 'problem' in again ? `after pressing "${target.model}": ${again.problem}` : `"${target.model}" was pressed, but the picker does not mark it as chosen (the button shows "${after ?? 'nothing'}").`,
1016
- };
1037
+ return { ok: false, current: await this.currentModel(), reason: `not chosen after ${attempts} attempts: ${why}` };
1017
1038
  }
1018
1039
  /**
1019
1040
  * Whether the model picker is open: its button says so (`aria-expanded`), or its rows are on screen.
@@ -1118,6 +1139,17 @@ Current URL: ${url}`);
1118
1139
  const button = await this.resolveModelButton();
1119
1140
  if (!button)
1120
1141
  return { ok: false, current: null, reason: 'This chat does not show a model picker.' };
1142
+ /*
1143
+ * Looked at before anything is opened: a conversation the run comes back to is usually on its model
1144
+ * already, and choosing it again was a menu walk for nothing (operator's request, 2026-10-06). The
1145
+ * button shows the model, shortened by words dropped from its end ("GPT-5.6 Sol Think"), and
1146
+ * `buttonShows` takes only that, so "Think deeper" is never taken for "GPT-5.6 Sol Think deeper".
1147
+ */
1148
+ const shown = await this.currentModel();
1149
+ if (buttonShows(shown, name)) {
1150
+ this.emit('model-already-selected', { model: name, buttonShows: shown, by: 'button' });
1151
+ return { ok: true, current: name, by: 'already' };
1152
+ }
1121
1153
  // The operator's own locators first, for the models they cover (see `OperatorModelLocators`).
1122
1154
  const target = operatorTargetFor(name);
1123
1155
  let first = null;
@@ -1149,6 +1181,8 @@ Current URL: ${url}`);
1149
1181
  const chosen = await this.chooseAt(match.name, match.locator);
1150
1182
  return {
1151
1183
  ...chosen,
1184
+ // When both ways failed, why the operator's locators did is said too: that is the one to fix.
1185
+ ...(!chosen.ok && first?.reason ? { reason: `${chosen.reason ?? 'not chosen'} (${target ? "by the operator's locators" : 'at the saved place'}: ${first.reason})` } : {}),
1152
1186
  ...(chosen.ok && !sameModel(match.name, name) ? { matched: match.name } : {}),
1153
1187
  ...(chosen.ok ? { by: opts.locator ? 'reread' : 'name' } : {}),
1154
1188
  // Why the saved locator missed, kept: what to look at when the page has changed.
@@ -159,6 +159,17 @@ export const OperatorModelLocators = {
159
159
  },
160
160
  ],
161
161
  };
162
+ /**
163
+ * How patiently a model is chosen with those locators (operator's request, 2026-10-06): every element is
164
+ * waited for until it is there and visible (`appearMs`), a fixed pause follows every opening and every
165
+ * press (`settleMs`), and a choice the picker does not show as made is tried again from the start, up to
166
+ * `attempts` times, before the run is told it failed.
167
+ */
168
+ export const ModelPicking = {
169
+ attempts: 6,
170
+ settleMs: 1_500,
171
+ appearMs: 10_000,
172
+ };
162
173
  /** A text as an XPath string literal, whatever quotes it holds. */
163
174
  export function xpathText(text) {
164
175
  if (!text.includes("'"))
package/dist/web/404.html CHANGED
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html><!--_Or9hW0soHRVNEEgEqiGt--><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-52e371cb1ec49089.js" async=""></script><script src="/_next/static/chunks/549-7e35e4fdefadb05d.js" async=""></script><script src="/_next/static/chunks/app/layout-3efbb282292ccc38.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>copilot-operator</title><meta name="description" content="Runs task loops with Microsoft 365 Copilot on this machine."/><script>
1
+ <!DOCTYPE html><!--G__u_mF_aLMdcOv0kc9us--><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-dfaaa8047cbf2fc2.js" async=""></script><script src="/_next/static/chunks/549-7e35e4fdefadb05d.js" async=""></script><script src="/_next/static/chunks/app/layout-3efbb282292ccc38.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>copilot-operator</title><meta name="description" content="Runs task loops with Microsoft 365 Copilot on this machine."/><script>
2
2
  (function () {
3
3
  try {
4
4
  var d = document.documentElement;
@@ -13,4 +13,4 @@
13
13
  d.dataset.focus = a.strongFocus ? 'strong' : 'normal';
14
14
  } catch (e) {}
15
15
  })();
16
- </script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><a class="skip-link" href="#main">Skip to the content</a><div class="wrap"><header class="top"><h1><a href="/">copilot-operator</a></h1><nav aria-label="Main"><a href="/"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M4 5h16v4H4zM4 11h16v4H4zM4 17h10v2H4z"></path></svg><span>Sessions</span></a><a href="/defaults"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm8.5 4 1.5 1-1.6 2.8-1.8-.4a7 7 0 0 1-1.5 1.5l.4 1.8L14.7 21l-1-1.5a7 7 0 0 1-3.4 0l-1 1.5-2.8-1.6.4-1.8A7 7 0 0 1 5.4 16l-1.8.4L2 13.6 3.5 12.6V11.4L2 10.4l1.6-2.8 1.8.4A7 7 0 0 1 6.9 6.5l-.4-1.8L9.3 3l1 1.5a7 7 0 0 1 3.4 0l1-1.5 2.8 1.6-.4 1.8a7 7 0 0 1 1.5 1.5l1.8-.4L22 10.4l-1.5 1z"></path></svg><span>Settings</span></a><a href="/import"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3v10m0 0 4-4m-4 4-4-4M4 15v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg><span>Plan from JSON</span></a><a href="/history"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3a9 9 0 1 0 8.5 6M12 7v5l3 2M3 4v5h5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg><span>Task register</span></a><a href="/level1"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M6 3h9l5 5v13H6zM14 3v6h6M9 13h7M9 17h7" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>Level 1 prompt</span></a><a href="/presets"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M5 4h14v4H5zM5 10h14v4H5zM5 16h9v4H5z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>Level 2 presets</span></a><a href="/appearance"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3a9 9 0 0 0 0 18c1.5 0 2-1 2-2s-1-1.5-1-2.5S14 15 15.5 15H17a4 4 0 0 0 4-4 8 8 0 0 0-9-8zM8 9a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm4-3a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm4 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path></svg><span>Appearance</span></a><a href="/system"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M4 5h16v10H4zM2 19h20M9 15v4M15 15v4" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>System</span></a><select aria-label="Language" style="width:auto"><option value="en" selected="">English</option><option value="bg">Български</option></select></nav></header><main id="main"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/webpack-0f31b926d9a9c5a4.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[8078,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-52e371cb1ec49089.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"AppearanceProvider\"]\n3:I[867,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-52e371cb1ec49089.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"LanguageProvider\"]\n4:I[1035,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-52e371cb1ec49089.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"DialogHost\"]\n5:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-52e371cb1ec49089.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"SkipLink\"]\n6:I[8197,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-52e371cb1ec49089.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"\"]\n7:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-52e371cb1ec49089.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"Nav\"]\n8:I[6657,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-52e371cb1ec49089.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.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-52e371cb1ec49089.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.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\":\"_Or9hW0soHRVNEEgEqiGt\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/c37978b44194c37b.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"suppressHydrationWarning\":true,\"data-theme\":\"light\",\"data-textsize\":\"normal\",\"data-contrast\":\"normal\",\"data-motion\":\"normal\",\"data-links\":\"plain\",\"data-focus\":\"normal\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n(function () {\\n try {\\n var d = document.documentElement;\\n var raw = localStorage.getItem('cop.appearance');\\n var a = raw ? JSON.parse(raw) : {};\\n if (!a || typeof a !== 'object') a = {};\\n d.dataset.theme = a.theme === 'dark' ? 'dark' : 'light';\\n d.dataset.textsize = [\\\"small\\\",\\\"normal\\\",\\\"large\\\",\\\"huge\\\",\\\"giant\\\"].indexOf(a.textSize) \u003e= 0 ? a.textSize : 'normal';\\n d.dataset.contrast = a.highContrast ? 'high' : 'normal';\\n d.dataset.motion = a.reduceMotion ? 'reduced' : 'normal';\\n d.dataset.links = a.underlineLinks ? 'underline' : 'plain';\\n d.dataset.focus = a.strongFocus ? 'strong' : 'normal';\\n } catch (e) {}\\n})();\\n\"}}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"children\":[[\"$\",\"$L5\",null,{}],[\"$\",\"div\",null,{\"className\":\"wrap\",\"children\":[[\"$\",\"header\",null,{\"className\":\"top\",\"children\":[[\"$\",\"h1\",null,{\"children\":[\"$\",\"$L6\",null,{\"href\":\"/\",\"children\":\"copilot-operator\"}]}],[\"$\",\"$L7\",null,{}]]}],[\"$\",\"$L8\",null,{}],[\"$\",\"main\",null,{\"id\":\"main\",\"children\":[\"$\",\"$L9\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}],[\"$\",\"$Lb\",null,{}]]}]}]}]}]]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L9\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$La\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$Lc\",null,{\"children\":[\"$Ld\",[\"$\",\"$Le\",null,{\"promise\":\"$@f\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L10\",null,{\"children\":\"$L11\"}],null],[\"$\",\"$L12\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$13\",null,{\"fallback\":null,\"children\":\"$L14\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$15\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"11:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\nd:null\n"])</script><script>self.__next_f.push([1,"f:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"copilot-operator\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Runs task loops with Microsoft 365 Copilot on this machine.\"}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"14:\"$f:metadata\"\n"])</script></body></html>
16
+ </script><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><a class="skip-link" href="#main">Skip to the content</a><div class="wrap"><header class="top"><h1><a href="/">copilot-operator</a></h1><nav aria-label="Main"><a href="/"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M4 5h16v4H4zM4 11h16v4H4zM4 17h10v2H4z"></path></svg><span>Sessions</span></a><a href="/defaults"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm8.5 4 1.5 1-1.6 2.8-1.8-.4a7 7 0 0 1-1.5 1.5l.4 1.8L14.7 21l-1-1.5a7 7 0 0 1-3.4 0l-1 1.5-2.8-1.6.4-1.8A7 7 0 0 1 5.4 16l-1.8.4L2 13.6 3.5 12.6V11.4L2 10.4l1.6-2.8 1.8.4A7 7 0 0 1 6.9 6.5l-.4-1.8L9.3 3l1 1.5a7 7 0 0 1 3.4 0l1-1.5 2.8 1.6-.4 1.8a7 7 0 0 1 1.5 1.5l1.8-.4L22 10.4l-1.5 1z"></path></svg><span>Settings</span></a><a href="/import"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3v10m0 0 4-4m-4 4-4-4M4 15v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg><span>Plan from JSON</span></a><a href="/history"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3a9 9 0 1 0 8.5 6M12 7v5l3 2M3 4v5h5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg><span>Task register</span></a><a href="/level1"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M6 3h9l5 5v13H6zM14 3v6h6M9 13h7M9 17h7" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>Level 1 prompt</span></a><a href="/presets"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M5 4h14v4H5zM5 10h14v4H5zM5 16h9v4H5z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>Level 2 presets</span></a><a href="/appearance"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 3a9 9 0 0 0 0 18c1.5 0 2-1 2-2s-1-1.5-1-2.5S14 15 15.5 15H17a4 4 0 0 0 4-4 8 8 0 0 0-9-8zM8 9a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm4-3a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm4 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path></svg><span>Appearance</span></a><a href="/system"><svg class="nav-icon" viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M4 5h16v10H4zM2 19h20M9 15v4M15 15v4" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"></path></svg><span>System</span></a><select aria-label="Language" style="width:auto"><option value="en" selected="">English</option><option value="bg">Български</option></select></nav></header><main id="main"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/webpack-0f31b926d9a9c5a4.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[8078,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-dfaaa8047cbf2fc2.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"AppearanceProvider\"]\n3:I[867,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-dfaaa8047cbf2fc2.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"LanguageProvider\"]\n4:I[1035,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-dfaaa8047cbf2fc2.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"DialogHost\"]\n5:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-dfaaa8047cbf2fc2.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"SkipLink\"]\n6:I[8197,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-dfaaa8047cbf2fc2.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"\"]\n7:I[8214,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-dfaaa8047cbf2fc2.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.js\",\"177\",\"static/chunks/app/layout-3efbb282292ccc38.js\"],\"Nav\"]\n8:I[6657,[\"197\",\"static/chunks/197-f7c33a0114fc3db3.js\",\"867\",\"static/chunks/867-dfaaa8047cbf2fc2.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.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-dfaaa8047cbf2fc2.js\",\"549\",\"static/chunks/549-7e35e4fdefadb05d.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\":\"G_-u_mF-aLMdcOv0kc9us\",\"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>