copilot-operator 0.1.31 → 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.
- package/dist/src/api/operator.service.js +10 -2
- package/dist/src/orchestrator/derivedChecks.js +34 -1
- package/dist/src/orchestrator/review.js +25 -4
- package/dist/src/orchestrator/taskRunner.js +44 -15
- package/dist/src/plan/importPlan.js +5 -0
- package/dist/src/plan/systemGuide.js +8 -0
- package/dist/src/transport/copilotTransport.js +70 -36
- package/dist/src/transport/locators.js +11 -0
- package/dist/web/404.html +2 -2
- package/dist/web/_next/static/chunks/867-dfaaa8047cbf2fc2.js +1 -0
- package/dist/web/_next/static/chunks/app/defaults/page-54f8f1ab4cac47fa.js +1 -0
- package/dist/web/_next/static/chunks/app/sessions/view/page-ce99494b1997ee25.js +1 -0
- package/dist/web/appearance.html +2 -2
- package/dist/web/appearance.txt +10 -10
- package/dist/web/defaults.html +2 -2
- package/dist/web/defaults.txt +10 -10
- package/dist/web/history.html +2 -2
- package/dist/web/history.txt +10 -10
- package/dist/web/import.html +2 -2
- package/dist/web/import.txt +10 -10
- package/dist/web/index.html +2 -2
- package/dist/web/index.txt +10 -10
- package/dist/web/level1.html +2 -2
- package/dist/web/level1.txt +10 -10
- package/dist/web/presets.html +2 -2
- package/dist/web/presets.txt +10 -10
- package/dist/web/sessions/view.html +2 -2
- package/dist/web/sessions/view.txt +10 -10
- package/dist/web/system.html +2 -2
- package/dist/web/system.txt +10 -10
- package/package.json +3 -1
- package/prompts/review1.md +9 -0
- package/dist/web/_next/static/chunks/867-52e371cb1ec49089.js +0 -1
- package/dist/web/_next/static/chunks/app/defaults/page-17dd586d1d6bd068.js +0 -1
- package/dist/web/_next/static/chunks/app/sessions/view/page-54ab3cb89b84679d.js +0 -1
- /package/dist/web/_next/static/{sLgvkKhjVTf3TqVsYNxB7 → G_-u_mF-aLMdcOv0kc9us}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{sLgvkKhjVTf3TqVsYNxB7 → 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
|
}
|
|
@@ -47,11 +47,16 @@ export function withAttemptIds(reviewChecks) {
|
|
|
47
47
|
* Runs each finding's check on the current state. Kept if it fails now, refused if it passes.
|
|
48
48
|
*/
|
|
49
49
|
export async function validateDerivedChecks(findings, opts) {
|
|
50
|
-
const out = { kept: [], refused: [], blocked: [] };
|
|
50
|
+
const out = { kept: [], refused: [], blocked: [], lexical: [] };
|
|
51
51
|
for (const [i, finding] of findings.entries()) {
|
|
52
52
|
if (!finding.check)
|
|
53
53
|
continue;
|
|
54
54
|
const check = { ...finding.check, name: derivedCheckName(finding.id, finding.check.name) };
|
|
55
|
+
const lexical = selfAttestationReason(check);
|
|
56
|
+
if (lexical) {
|
|
57
|
+
out.lexical.push({ finding, check, why: lexical });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
55
60
|
const outcome = await runCheck(check, 500 + i, {
|
|
56
61
|
cwd: opts.cwd,
|
|
57
62
|
logDir: join(opts.logDir, 'derived'),
|
|
@@ -71,6 +76,34 @@ export async function validateDerivedChecks(findings, opts) {
|
|
|
71
76
|
}
|
|
72
77
|
return out;
|
|
73
78
|
}
|
|
79
|
+
/** Words a text uses to vouch for itself. Anyone can write them; they prove nothing about the work. */
|
|
80
|
+
const SELF_ATTESTATION = /\b(?:prove[dn]?|proof|derived|verif(?:y|ied|ies|ication)|validated|confirmed|complete[ds]?|completion|done|finished|tested|passe[sd]|successful(?:ly)?|success|resolved|fixed|activity|evidence|works|working)\b/i;
|
|
81
|
+
/** Commands that only read or search text: what they print is a file's words, not the work running. */
|
|
82
|
+
const TEXT_READ = /^\s*(?:Get-Content|gc|cat|type|more|Select-String|sls|findstr|grep|rg|Get-ChildItem\b.*\|\s*Select-String)\b/i;
|
|
83
|
+
/**
|
|
84
|
+
* Why a check given with a finding is lexical self-attestation, or null when it is not.
|
|
85
|
+
*
|
|
86
|
+
* It is when all it asks is that a text contain a word the text can simply say — "proved", "derived",
|
|
87
|
+
* "verified", "Activity", "complete" — whether by `file-contains` or by reading a file and matching its
|
|
88
|
+
* output. Written so, the check passes the moment the deliverable says the word, and the finding is
|
|
89
|
+
* closed by the claim it was about (operator's feedback, 2026-10-06: "Review findings must not be closed
|
|
90
|
+
* by lexical self-attestation checks"). Text that is structure — a JSON envelope (`"status": "complete"`),
|
|
91
|
+
* an identifier, a path, a number — is not prose and is left alone: those test what the work produced.
|
|
92
|
+
*/
|
|
93
|
+
export function selfAttestationReason(check) {
|
|
94
|
+
const value = (check.value ?? '').trim();
|
|
95
|
+
if (!value || !SELF_ATTESTATION.test(value))
|
|
96
|
+
return null;
|
|
97
|
+
// A sentence, identifiers in it included ("bill_type is derived"); JSON, code and paths are structure,
|
|
98
|
+
// evidence of a kind, and left alone.
|
|
99
|
+
if (/[{}[\]()<>=:;"`/\\]/.test(value))
|
|
100
|
+
return null;
|
|
101
|
+
const textPresence = check.expect === 'file-contains' ||
|
|
102
|
+
((check.expect === 'output-contains' || check.expect === 'output-matches') && TEXT_READ.test(check.run ?? ''));
|
|
103
|
+
if (!textPresence)
|
|
104
|
+
return null;
|
|
105
|
+
return `it only asks that a text contain "${value}", which the text can simply say; a check must test the primary evidence instead`;
|
|
106
|
+
}
|
|
74
107
|
/** The derived checks that run in the gate. */
|
|
75
108
|
export function activeChecks(reviewChecks) {
|
|
76
109
|
return reviewChecks.filter((rc) => rc.state === 'active').map((rc) => rc.check);
|
|
@@ -153,9 +153,26 @@ export function reviewBrief(session, task, changedFiles, cwd, deviations = [], p
|
|
|
153
153
|
'resolve it at all — a finding that survives a reported fix is often one the task itself made',
|
|
154
154
|
'unsatisfiable: an instruction a tool overwrites, a setting a version removed.',
|
|
155
155
|
'',
|
|
156
|
+
'A finding is resolved by its primary evidence — the artifact exists, the JSON has its envelope,',
|
|
157
|
+
'the telemetry carries the correlation ID and the rule or output signal, the probe runs and',
|
|
158
|
+
'succeeds, the cited symbol and path exist — never because the text now says "proved",',
|
|
159
|
+
'"verified", "derived" or "complete". Run the evidence for each one below.',
|
|
160
|
+
'',
|
|
156
161
|
previous.findings
|
|
157
|
-
.map((f, i) =>
|
|
158
|
-
.
|
|
162
|
+
.map((f, i) => {
|
|
163
|
+
const check = previous.evidenceChecks?.find((c) => c.findingId === f.id);
|
|
164
|
+
return [
|
|
165
|
+
`${i + 1}. ${f.id ? `[${f.id}] ` : ''}${f.what}${f.where ? ` (${f.where})` : ''}${f.repeated ? ' — raised in more than one round already' : ''}`,
|
|
166
|
+
...(f.evidence ? [` Evidence then: ${f.evidence}`] : []),
|
|
167
|
+
check
|
|
168
|
+
? ` Its check "${check.name}": ${check.passed === undefined ? 'not run since' : check.passed ? 'passed at the last gate' : 'FAILED at the last gate'}${check.detail ? ` — ${check.detail}` : ''}${check.state !== 'active' ? ` (${check.state})` : ''}`
|
|
169
|
+
: ' No machine check: judge it on the evidence yourself.',
|
|
170
|
+
].join('\n');
|
|
171
|
+
})
|
|
172
|
+
.join('\n\n'),
|
|
173
|
+
...(previous.correction
|
|
174
|
+
? ['', "The implementer's account of what it corrected (a claim; test it, do not take it):", '', previous.correction.length > 3000 ? `${previous.correction.slice(0, 3000)}…` : previous.correction]
|
|
175
|
+
: []),
|
|
159
176
|
...(previous.leftovers && leftoverNote(previous.leftovers)
|
|
160
177
|
? ['', leftoverNote(previous.leftovers).replace('After this review', `After round ${previous.round}`), 'Stop what you start, in the same step, and check the port only after that.']
|
|
161
178
|
: []),
|
|
@@ -240,8 +257,8 @@ export async function runReview(transport, session, task, deps) {
|
|
|
240
257
|
// The machine's shells, when they are not what the contract's example assumes — the same note
|
|
241
258
|
// the implementer's conversation opens with, for a conversation working to the same example.
|
|
242
259
|
const machineNote = shellNote(undefined, defaultShell);
|
|
243
|
-
let before = await transport.sendAndConfirm(machineNote ? `${contract}
|
|
244
|
-
|
|
260
|
+
let before = await transport.sendAndConfirm(machineNote ? `${contract}
|
|
261
|
+
|
|
245
262
|
${machineNote}` : contract);
|
|
246
263
|
await transport.waitForReply(before);
|
|
247
264
|
await pacer.throttleSend();
|
|
@@ -407,6 +424,10 @@ ${machineNote}` : contract);
|
|
|
407
424
|
markdown = (await transport.waitForReply(b)).markdown;
|
|
408
425
|
continue;
|
|
409
426
|
}
|
|
427
|
+
// A check that only asks for a word the text can say closes nothing: the finding stays a finding.
|
|
428
|
+
for (const l of validation.lexical) {
|
|
429
|
+
deps.event('review-check-lexical', { round, finding: l.finding.id, check: l.check.name, value: l.check.value ?? '' }, `the check given with ${l.finding.id} is not kept: ${l.why}. The finding stays, for the next reviewer to judge on the evidence`, 'warn');
|
|
430
|
+
}
|
|
410
431
|
for (const b of validation.blocked) {
|
|
411
432
|
deps.event('review-check-blocked', { round, finding: b.finding.id, detail: b.outcome.detail }, `the check given with ${b.finding.id} was refused by the runner and never ran; dropped, the finding stands without it — ${b.outcome.detail}`, 'warn');
|
|
412
433
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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)
|
|
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}${
|
|
542
|
-
: `could not switch to "${wanted}"${
|
|
543
|
-
data: { asked: wanted,
|
|
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
|
|
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}"${
|
|
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
|
}
|
|
@@ -739,6 +749,8 @@ export async function runTask(transport, session, task, deps) {
|
|
|
739
749
|
let reviewChecks = withAttemptIds(task.reviewChecks ?? []);
|
|
740
750
|
/** Derived checks that still failed when the rounds ran out; the reviewer is told. */
|
|
741
751
|
let derivedStillFailing = [];
|
|
752
|
+
/** The last gate's result for each check, by name: what a review's findings' checks came to (see `PreviousRound`). */
|
|
753
|
+
const lastGate = new Map();
|
|
742
754
|
/**
|
|
743
755
|
* What was running before the task, so that what it and its reviews leave running can be
|
|
744
756
|
* told apart and stopped. Taken once the working directory is known; null means "do not".
|
|
@@ -1485,6 +1497,8 @@ export async function runTask(transport, session, task, deps) {
|
|
|
1485
1497
|
};
|
|
1486
1498
|
runAfterCommit = (cs) => runChecks(cs, checkOptions);
|
|
1487
1499
|
const ran = await runChecks(checks, checkOptions);
|
|
1500
|
+
for (const o of ran)
|
|
1501
|
+
lastGate.set(o.check.name, { passed: o.passed, detail: o.detail });
|
|
1488
1502
|
/*
|
|
1489
1503
|
* The operator stopped the run, which is not a verdict on the work. The check in flight was
|
|
1490
1504
|
* killed and the ones after it never ran, so what this round "found" is only the Stop: it is
|
|
@@ -1744,7 +1758,7 @@ export async function runTask(transport, session, task, deps) {
|
|
|
1744
1758
|
if (model) {
|
|
1745
1759
|
const reviewLocator = await deps.models?.locate?.(model).catch(() => undefined);
|
|
1746
1760
|
const picked = await transport.selectModel(model, { locator: reviewLocator }).catch((e) => ({ ok: false, current: null, reason: e.message }));
|
|
1747
|
-
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);
|
|
1748
1762
|
if (renamed)
|
|
1749
1763
|
sink.event('model-renamed', { asked: model, chosen: picked.matched, review: true }, renamed, 'warn');
|
|
1750
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');
|
|
@@ -1763,7 +1777,22 @@ export async function runTask(transport, session, task, deps) {
|
|
|
1763
1777
|
deliverable,
|
|
1764
1778
|
deviations,
|
|
1765
1779
|
disputes,
|
|
1766
|
-
|
|
1780
|
+
/*
|
|
1781
|
+
* Each earlier finding with its evidence, the correction the implementer reports, and what the
|
|
1782
|
+
* finding's own check came to at the last gate: the next reviewer judges the finding on the evidence,
|
|
1783
|
+
* not on whether the new text says it is fixed (operator's feedback, 2026-10-06).
|
|
1784
|
+
*/
|
|
1785
|
+
previous: reviewRounds > 1
|
|
1786
|
+
? {
|
|
1787
|
+
round: reviewRounds - 1,
|
|
1788
|
+
findings: previousFindings,
|
|
1789
|
+
leftovers: previousLeftovers,
|
|
1790
|
+
correction: closing,
|
|
1791
|
+
evidenceChecks: reviewChecks
|
|
1792
|
+
.filter((rc) => previousFindings.some((f) => f.id === rc.findingId))
|
|
1793
|
+
.map((rc) => ({ findingId: rc.findingId, name: rc.check.name, state: rc.state, ...(lastGate.get(rc.check.name) ?? {}) })),
|
|
1794
|
+
}
|
|
1795
|
+
: undefined,
|
|
1767
1796
|
repoDir: willCommit ? repoDirOf(session) : undefined,
|
|
1768
1797
|
derivedFailing: derivedStillFailing,
|
|
1769
1798
|
// Before the verdict is judged: a check given with a finding must fail on the work,
|
|
@@ -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,
|
|
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
|
-
|
|
969
|
-
|
|
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,
|
|
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
|
-
|
|
987
|
-
|
|
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
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
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><!--
|
|
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,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";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\":\"sLgvkKhjVTf3TqVsYNxB7\",\"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,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";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>
|