copilot-operator 0.1.26 → 0.1.27
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 +1 -0
- package/dist/src/orchestrator/taskRunner.js +58 -9
- package/dist/src/transport/copilotTransport.js +258 -197
- package/dist/src/transport/locators.js +2 -0
- package/dist/web/404.html +2 -2
- package/dist/web/appearance.html +2 -2
- package/dist/web/appearance.txt +1 -1
- package/dist/web/defaults.html +2 -2
- package/dist/web/defaults.txt +1 -1
- package/dist/web/history.html +2 -2
- package/dist/web/history.txt +1 -1
- package/dist/web/import.html +2 -2
- package/dist/web/import.txt +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/index.txt +1 -1
- package/dist/web/level1.html +2 -2
- package/dist/web/level1.txt +1 -1
- package/dist/web/presets.html +2 -2
- package/dist/web/presets.txt +1 -1
- package/dist/web/sessions/view.html +2 -2
- package/dist/web/sessions/view.txt +1 -1
- package/dist/web/system.html +2 -2
- package/dist/web/system.txt +1 -1
- package/package.json +3 -1
- /package/dist/web/_next/static/{jYvSWuPPywbxckIuz6cNs → MJtx49m8j0mFQ3oflKI-n}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{jYvSWuPPywbxckIuz6cNs → MJtx49m8j0mFQ3oflKI-n}/_ssgManifest.js +0 -0
|
@@ -1096,6 +1096,7 @@ let OperatorService = class OperatorService {
|
|
|
1096
1096
|
...(transport || !runGroup ? {} : { beforeBrowser: () => this.browserRequested(runGroup.id, [sessionId]) }),
|
|
1097
1097
|
// The page is the source of the model names: Settings and the saved list follow it (see `ModelHooks`).
|
|
1098
1098
|
models: {
|
|
1099
|
+
locate: async (name) => (await this.store.getModels())?.options.find((o) => sameModel(o.name, name))?.locator,
|
|
1099
1100
|
renamedDefault: async (which, from, to) => {
|
|
1100
1101
|
const now = (await this.settings.load()).copilot;
|
|
1101
1102
|
if (which === 'model' && (now.defaultModel ?? '').trim() === from)
|
|
@@ -523,8 +523,9 @@ export function effectiveModels(session, cfg) {
|
|
|
523
523
|
async function applySessionModel(transport, session, bus, cfg, store, hooks) {
|
|
524
524
|
const wanted = effectiveModels(session, cfg).model;
|
|
525
525
|
if (!wanted)
|
|
526
|
-
return
|
|
527
|
-
const
|
|
526
|
+
return {};
|
|
527
|
+
const locator = await hooks?.locate?.(wanted).catch(() => undefined);
|
|
528
|
+
const result = await transport.selectModel(wanted, { locator }).catch((e) => ({
|
|
528
529
|
ok: false,
|
|
529
530
|
current: null,
|
|
530
531
|
reason: e.message,
|
|
@@ -538,8 +539,8 @@ async function applySessionModel(transport, session, bus, cfg, store, hooks) {
|
|
|
538
539
|
level: result.ok ? 'info' : 'warn',
|
|
539
540
|
message: result.ok
|
|
540
541
|
? `model: ${result.current ?? wanted}${session.model?.trim() ? '' : ' (from Settings)'}`
|
|
541
|
-
: `could not switch to "${wanted}"${session.model?.trim() ? '' : ' (the default model in Settings
|
|
542
|
-
data: { asked: wanted, fromSettings: !session.model?.trim(), current: result.current, ok: result.ok },
|
|
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 },
|
|
543
544
|
});
|
|
544
545
|
// Kept on the session, so each task's record says which model it ran on and why (see `runTask`).
|
|
545
546
|
await store
|
|
@@ -555,7 +556,18 @@ async function applySessionModel(transport, session, bus, cfg, store, hooks) {
|
|
|
555
556
|
};
|
|
556
557
|
})
|
|
557
558
|
.catch(() => undefined);
|
|
558
|
-
|
|
559
|
+
/*
|
|
560
|
+
* A model the operator chose and the chat is not on is not worked around: the run went on Auto with a
|
|
561
|
+
* warning nobody saw, and its work was then taken for the chosen model's (2026-10-05). Nothing goes out.
|
|
562
|
+
*/
|
|
563
|
+
if (!result.ok) {
|
|
564
|
+
return {
|
|
565
|
+
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]+$/, '')}. ` +
|
|
567
|
+
'Nothing was sent. Read the list again under Settings → Model, choose the model there, and start again.',
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
return { current: result.current ?? undefined };
|
|
559
571
|
}
|
|
560
572
|
/** Runs one task to completion inside an already-open transport. */
|
|
561
573
|
export async function runTask(transport, session, task, deps) {
|
|
@@ -580,12 +592,39 @@ export async function runTask(transport, session, task, deps) {
|
|
|
580
592
|
*/
|
|
581
593
|
let sentCount = 0;
|
|
582
594
|
let inOpening = false;
|
|
595
|
+
/*
|
|
596
|
+
* A conversation Copilot had not yet put in its sidebar when it was registered is named before a later
|
|
597
|
+
* message goes out in it: the row comes some seconds after the first reply, and fifteen were not always
|
|
598
|
+
* enough (live run 2026-10-05), which left the chat under Copilot's own title, where a later run looking
|
|
599
|
+
* for it by name could not find it. A few tries, only while the page is on that conversation.
|
|
600
|
+
*/
|
|
601
|
+
let nameTries = 0;
|
|
602
|
+
const nameIfStillUnnamed = async (target) => {
|
|
603
|
+
const chat = session.chat;
|
|
604
|
+
if (!chat || chat.named !== false || nameTries >= 3)
|
|
605
|
+
return;
|
|
606
|
+
if ((await target.currentChatId().catch(() => null)) !== chat.chatId)
|
|
607
|
+
return;
|
|
608
|
+
nameTries += 1;
|
|
609
|
+
const named = await target.nameChat(chat.chatId, chat.name, { waitMs: 5_000 }).catch(() => false);
|
|
610
|
+
if (!named)
|
|
611
|
+
return;
|
|
612
|
+
const { named: _was, ...done } = chat;
|
|
613
|
+
session.chat = done;
|
|
614
|
+
await store.updateSession(session.id, (s) => {
|
|
615
|
+
if (s.chat?.chatId === chat.chatId)
|
|
616
|
+
s.chat = { ...s.chat, named: undefined };
|
|
617
|
+
}).catch(() => undefined);
|
|
618
|
+
await savePointer(log.path('chat.json'), done).catch(() => undefined);
|
|
619
|
+
sink.event('chat-named-later', { chatId: chat.chatId, name: chat.name, tries: nameTries }, `chat named "${chat.name}" in Copilot's sidebar, now that it is there`);
|
|
620
|
+
};
|
|
583
621
|
const chatOf = transport;
|
|
584
622
|
transport = new Proxy(chatOf, {
|
|
585
623
|
get(target, key) {
|
|
586
624
|
if (key !== 'sendAndConfirm')
|
|
587
625
|
return Reflect.get(target, key);
|
|
588
626
|
return async (...args) => {
|
|
627
|
+
await nameIfStillUnnamed(target);
|
|
589
628
|
const before = await target.sendAndConfirm(...args);
|
|
590
629
|
sentCount += 1;
|
|
591
630
|
const [text, attachments] = args;
|
|
@@ -1240,9 +1279,13 @@ export async function runTask(transport, session, task, deps) {
|
|
|
1240
1279
|
const modelNow = await applySessionModel(transport, session, bus, cfg, store, deps.models);
|
|
1241
1280
|
if (effectiveModels(session, cfg).model) {
|
|
1242
1281
|
await store.updateSession(session.id, (s) => {
|
|
1243
|
-
s.modelInUse = modelNow;
|
|
1282
|
+
s.modelInUse = modelNow.current;
|
|
1244
1283
|
});
|
|
1245
1284
|
}
|
|
1285
|
+
if (modelNow.refused) {
|
|
1286
|
+
stopCode = 'environment';
|
|
1287
|
+
return await finish('blocked', modelNow.refused);
|
|
1288
|
+
}
|
|
1246
1289
|
}
|
|
1247
1290
|
}
|
|
1248
1291
|
// --- opening messages -------------------------------------------------------------
|
|
@@ -1699,7 +1742,8 @@ export async function runTask(transport, session, task, deps) {
|
|
|
1699
1742
|
try {
|
|
1700
1743
|
await transport.newChat();
|
|
1701
1744
|
if (model) {
|
|
1702
|
-
const
|
|
1745
|
+
const reviewLocator = await deps.models?.locate?.(model).catch(() => undefined);
|
|
1746
|
+
const picked = await transport.selectModel(model, { locator: reviewLocator }).catch((e) => ({ ok: false, current: null, reason: e.message }));
|
|
1703
1747
|
const renamed = await followPageModels('review', model, picked, session, store, deps.models);
|
|
1704
1748
|
if (renamed)
|
|
1705
1749
|
sink.event('model-renamed', { asked: model, chosen: picked.matched, review: true }, renamed, 'warn');
|
|
@@ -2590,9 +2634,12 @@ export async function runSession(sessionId, deps) {
|
|
|
2590
2634
|
const modelInUse = await applySessionModel(chat, session, bus, cfg, store, deps.models);
|
|
2591
2635
|
if (effectiveModels(session, cfg).model) {
|
|
2592
2636
|
await store.updateSession(sessionId, (s) => {
|
|
2593
|
-
s.modelInUse = modelInUse;
|
|
2637
|
+
s.modelInUse = modelInUse.current;
|
|
2594
2638
|
});
|
|
2595
2639
|
}
|
|
2640
|
+
// Refused before a task is marked started: its tasks stay queued, and the run says why.
|
|
2641
|
+
if (modelInUse.refused)
|
|
2642
|
+
throw new Error(modelInUse.refused);
|
|
2596
2643
|
for (const queuedTask of queued) {
|
|
2597
2644
|
if (deps.signal?.aborted)
|
|
2598
2645
|
break;
|
|
@@ -2671,9 +2718,11 @@ export async function runSession(sessionId, deps) {
|
|
|
2671
2718
|
const freshModel = await applySessionModel(chat, session, bus, cfg, store, deps.models);
|
|
2672
2719
|
if (effectiveModels(session, cfg).model) {
|
|
2673
2720
|
session = await store.updateSession(sessionId, (s) => {
|
|
2674
|
-
s.modelInUse = freshModel;
|
|
2721
|
+
s.modelInUse = freshModel.current;
|
|
2675
2722
|
});
|
|
2676
2723
|
}
|
|
2724
|
+
if (freshModel.refused)
|
|
2725
|
+
throw new Error(freshModel.refused);
|
|
2677
2726
|
const fresh = session.tasks.find((x) => x.id === task.id);
|
|
2678
2727
|
if (!fresh)
|
|
2679
2728
|
break;
|