copilot-operator 0.1.11 → 0.1.13
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.controller.js +1 -1
- package/dist/src/api/operator.service.js +98 -8
- package/dist/src/config/schema.js +14 -3
- package/dist/src/context/desktopMirror.js +73 -13
- package/dist/src/context/projectMirror.js +69 -1
- package/dist/src/orchestrator/contract.js +56 -1
- package/dist/src/orchestrator/taskRunner.js +140 -16
- package/dist/src/plan/brief.js +39 -8
- package/dist/src/plan/importPlan.js +3 -0
- package/dist/src/plan/schema.js +26 -2
- package/dist/src/plan/script.js +79 -75
- package/dist/src/plan/systemGuide.js +79 -15
- package/dist/src/session/compose.js +16 -1
- package/dist/src/session/exportRecord.js +8 -1
- package/dist/src/session/exports.js +3 -0
- package/dist/src/session/handoff.js +41 -3
- package/dist/src/session/model.js +9 -0
- package/dist/src/session/store.js +11 -7
- package/dist/src/session/story.js +74 -18
- package/dist/src/transport/chatTransport.js +1 -0
- package/dist/src/transport/copilotTransport.js +80 -61
- package/dist/src/vcs/git.js +92 -2
- package/dist/src/vcs/taskVcs.js +100 -19
- package/dist/web/404.html +2 -2
- package/dist/web/_next/static/chunks/36-c3dd06c6a459c114.js +1 -0
- package/dist/web/_next/static/chunks/549-a5a59a94f92bb3eb.js +1 -0
- package/dist/web/_next/static/chunks/867-84c19f047972ffc3.js +1 -0
- package/dist/web/_next/static/chunks/app/defaults/page-5f180a88398be4c9.js +1 -0
- package/dist/web/_next/static/chunks/app/history/page-d7314ac22d47a3ab.js +1 -0
- package/dist/web/_next/static/chunks/app/page-13572a726de0b7db.js +1 -0
- package/dist/web/_next/static/chunks/app/sessions/view/page-34d7988849f9aa76.js +1 -0
- package/dist/web/_next/static/css/{2ad2a74b7e07dcc9.css → a0642fdb8adf9edb.css} +1 -1
- package/dist/web/appearance.html +2 -2
- package/dist/web/appearance.txt +12 -12
- package/dist/web/defaults.html +2 -2
- package/dist/web/defaults.txt +12 -12
- package/dist/web/history.html +2 -2
- package/dist/web/history.txt +12 -12
- package/dist/web/import.html +2 -2
- package/dist/web/import.txt +12 -12
- package/dist/web/index.html +2 -2
- package/dist/web/index.txt +12 -12
- package/dist/web/level1.html +2 -2
- package/dist/web/level1.txt +12 -12
- package/dist/web/presets.html +2 -2
- package/dist/web/presets.txt +12 -12
- package/dist/web/sessions/view.html +2 -2
- package/dist/web/sessions/view.txt +12 -12
- package/dist/web/system.html +2 -2
- package/dist/web/system.txt +12 -12
- package/package.json +6 -3
- package/dist/web/_next/static/chunks/36-17a3bcb0c2553c7c.js +0 -1
- package/dist/web/_next/static/chunks/549-8f69ef8086802748.js +0 -1
- package/dist/web/_next/static/chunks/867-f82c74b7b2ed5c54.js +0 -1
- package/dist/web/_next/static/chunks/app/defaults/page-abfa73fb08822e36.js +0 -1
- package/dist/web/_next/static/chunks/app/history/page-a9f2060f86a4a10e.js +0 -1
- package/dist/web/_next/static/chunks/app/page-2ac6b67f4ddcb808.js +0 -1
- package/dist/web/_next/static/chunks/app/sessions/view/page-d7e1f83d13c6356b.js +0 -1
- /package/dist/web/_next/static/{oyfXbuB_cq0sRZP7ukHlQ → NosxirjkOgV-Boyq8dGPl}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{oyfXbuB_cq0sRZP7ukHlQ → NosxirjkOgV-Boyq8dGPl}/_ssgManifest.js +0 -0
package/dist/src/plan/script.js
CHANGED
|
@@ -669,6 +669,10 @@ export const SCRIPT = [
|
|
|
669
669
|
en: "From the previous session's branch — the sessions form a chain (previous-session)",
|
|
670
670
|
bg: 'От клона на предната сесия — сесиите образуват верига (previous-session)',
|
|
671
671
|
},
|
|
672
|
+
{
|
|
673
|
+
en: 'On an existing branch — no new branch, the work continues on it; name it (existing-branch + existingBranch)',
|
|
674
|
+
bg: 'На съществуващ клон — без нов клон, работата продължава на него; посочи го (existing-branch + existingBranch)',
|
|
675
|
+
},
|
|
672
676
|
],
|
|
673
677
|
},
|
|
674
678
|
{
|
|
@@ -944,82 +948,82 @@ export function scriptSection(stages, projects, lang) {
|
|
|
944
948
|
return `### ${m.when[lang]}\n\n${quoted.join('\n')}`;
|
|
945
949
|
});
|
|
946
950
|
const head = lang === 'bg'
|
|
947
|
-
? `## Въпросите, дословно
|
|
948
|
-
|
|
949
|
-
Всяко съобщение, в което питаш оператора нещо, е тук — дословно, в реда, в който идват. **Копирай го
|
|
950
|
-
точно:** същите въпроси, в същия ред, със същите номера, същите опции и същите редове *Пример:*. Не
|
|
951
|
-
добавяй въпрос, не махай, не сливай, не разделяй, не преформулирай и не пренареждай — операторът учи
|
|
952
|
-
разговора, а това става само ако той е един и същ всеки път. Заглавието над цитата е за теб: казва
|
|
953
|
-
кога се праща съобщението; операторът вижда само цитираното, без знака \`>\`.
|
|
954
|
-
|
|
955
|
-
- **Първият ред на всеки цитат е редът за фазата.** Копираш го както е; не го сменяш и не добавяш
|
|
956
|
-
друг.
|
|
957
|
-
- **Номерата са само цифри.** Въпросите са 1, 2, 3; опциите под въпрос 2 са 2.1, 2.2, 2.3. Никога
|
|
958
|
-
букви. Съобщение с един-единствен избор номерира опциите си 1, 2, 3.
|
|
959
|
-
- **Попълваш само това в {скоби}** — път, име, брой, присъда — и нищо друго не пишеш наново.
|
|
960
|
-
**Съобщение с непопълнени {скоби} не се изпраща никога**: ако не знаеш с какво да ги попълниш,
|
|
961
|
-
значи съобщението не е за сега.
|
|
962
|
-
- **Препоръка:** на въпрос с опции, освен на тези за това какво операторът има или знае, може да
|
|
963
|
-
добавиш след една опция *(препоръчвам — причината в няколко думи)*. Само ако имаш причина.
|
|
964
|
-
- **Вече казано:** ако отговорът на въпрос вече е даден — в документите на оператора по-горе или
|
|
965
|
-
по-рано в този разговор — въпросът остава на мястото си, а под него, след празен ред и със същия
|
|
966
|
-
отстъп като редовете *Пример:*, пишеш \`${ALREADY_SAID.bg} …\` с отговора — отделен ред, не в края на
|
|
967
|
-
въпроса; на избор отбелязваш опцията с *(вече казано)*. Отговорът е целият, не етикет: за
|
|
968
|
-
заданието — заглавието, целта и контекстът от „Тази работа“, не само заглавието; за критериите —
|
|
969
|
-
всеки от тях. Операторът потвърждава това, което вижда, и не може да потвърди нещо, което не е
|
|
970
|
-
показано. Последният ред
|
|
971
|
-
на цитата тогава е точно един от тези два: когато всеки въпрос е вече казан — *${ON_RECORD_ALL.bg}*
|
|
972
|
-
— а когато само някои — *${ON_RECORD_MIXED.bg}*
|
|
973
|
-
- **Около цитата:** преди него — само поздравът, и то само в първото съобщение; след него — нищо: без
|
|
974
|
-
„Очаквам отговорите ти“ и без „Когато си готов…“. Последният ред на цитата вече казва какво следва.
|
|
975
|
-
Съобщенията, които не питат нищо, също започват с реда за фазата си и свършват на последната си
|
|
976
|
-
стъпка. Редът за фаза 2 е \`${PHASE_LINES.p2.bg}\`.
|
|
977
|
-
- **Единственото място за твои въпроси** е мъгляв или липсващ отговор във фаза 1: тогава, преди
|
|
978
|
-
настройките, пращаш едно съобщение с уточнения — редът за фазата, само номерирани въпроси, 1, 2, 3,
|
|
979
|
-
с опции 1.1, 1.2, когато отговорите могат да се изброят, и същия последен ред като тук.
|
|
980
|
-
|
|
981
|
-
Съобщенията, които не питат нищо — JSON-ът, стъпките в приложението, диагнозата, таблицата с
|
|
951
|
+
? `## Въпросите, дословно
|
|
952
|
+
|
|
953
|
+
Всяко съобщение, в което питаш оператора нещо, е тук — дословно, в реда, в който идват. **Копирай го
|
|
954
|
+
точно:** същите въпроси, в същия ред, със същите номера, същите опции и същите редове *Пример:*. Не
|
|
955
|
+
добавяй въпрос, не махай, не сливай, не разделяй, не преформулирай и не пренареждай — операторът учи
|
|
956
|
+
разговора, а това става само ако той е един и същ всеки път. Заглавието над цитата е за теб: казва
|
|
957
|
+
кога се праща съобщението; операторът вижда само цитираното, без знака \`>\`.
|
|
958
|
+
|
|
959
|
+
- **Първият ред на всеки цитат е редът за фазата.** Копираш го както е; не го сменяш и не добавяш
|
|
960
|
+
друг.
|
|
961
|
+
- **Номерата са само цифри.** Въпросите са 1, 2, 3; опциите под въпрос 2 са 2.1, 2.2, 2.3. Никога
|
|
962
|
+
букви. Съобщение с един-единствен избор номерира опциите си 1, 2, 3.
|
|
963
|
+
- **Попълваш само това в {скоби}** — път, име, брой, присъда — и нищо друго не пишеш наново.
|
|
964
|
+
**Съобщение с непопълнени {скоби} не се изпраща никога**: ако не знаеш с какво да ги попълниш,
|
|
965
|
+
значи съобщението не е за сега.
|
|
966
|
+
- **Препоръка:** на въпрос с опции, освен на тези за това какво операторът има или знае, може да
|
|
967
|
+
добавиш след една опция *(препоръчвам — причината в няколко думи)*. Само ако имаш причина.
|
|
968
|
+
- **Вече казано:** ако отговорът на въпрос вече е даден — в документите на оператора по-горе или
|
|
969
|
+
по-рано в този разговор — въпросът остава на мястото си, а под него, след празен ред и със същия
|
|
970
|
+
отстъп като редовете *Пример:*, пишеш \`${ALREADY_SAID.bg} …\` с отговора — отделен ред, не в края на
|
|
971
|
+
въпроса; на избор отбелязваш опцията с *(вече казано)*. Отговорът е целият, не етикет: за
|
|
972
|
+
заданието — заглавието, целта и контекстът от „Тази работа“, не само заглавието; за критериите —
|
|
973
|
+
всеки от тях. Операторът потвърждава това, което вижда, и не може да потвърди нещо, което не е
|
|
974
|
+
показано. Последният ред
|
|
975
|
+
на цитата тогава е точно един от тези два: когато всеки въпрос е вече казан — *${ON_RECORD_ALL.bg}*
|
|
976
|
+
— а когато само някои — *${ON_RECORD_MIXED.bg}*
|
|
977
|
+
- **Около цитата:** преди него — само поздравът, и то само в първото съобщение; след него — нищо: без
|
|
978
|
+
„Очаквам отговорите ти“ и без „Когато си готов…“. Последният ред на цитата вече казва какво следва.
|
|
979
|
+
Съобщенията, които не питат нищо, също започват с реда за фазата си и свършват на последната си
|
|
980
|
+
стъпка. Редът за фаза 2 е \`${PHASE_LINES.p2.bg}\`.
|
|
981
|
+
- **Единственото място за твои въпроси** е мъгляв или липсващ отговор във фаза 1: тогава, преди
|
|
982
|
+
настройките, пращаш едно съобщение с уточнения — редът за фазата, само номерирани въпроси, 1, 2, 3,
|
|
983
|
+
с опции 1.1, 1.2, когато отговорите могат да се изброят, и същия последен ред като тук.
|
|
984
|
+
|
|
985
|
+
Съобщенията, които не питат нищо — JSON-ът, стъпките в приложението, диагнозата, таблицата с
|
|
982
986
|
доказателствата — следват описанието във фазата си.`
|
|
983
|
-
: `## The questions, word for word
|
|
984
|
-
|
|
985
|
-
Every message in which you ask the operator something is here — word for word, in the order they
|
|
986
|
-
come. **Copy it exactly:** the same questions, in the same order, with the same numbers, the same
|
|
987
|
-
options and the same *Example:* lines. Do not add a question, drop one, merge, split, reword or
|
|
988
|
-
reorder — the operator learns the conversation, and that only works if it is the same every time. The
|
|
989
|
-
heading above each quotation is for you: it says when the message is sent; the operator sees only
|
|
990
|
-
what is quoted, without the \`>\`.
|
|
991
|
-
|
|
992
|
-
- **The first line of every quotation is the phase line.** Copy it as it is; do not change it and do
|
|
993
|
-
not add another.
|
|
994
|
-
- **Numbers are digits only.** Questions are 1, 2, 3; the options under question 2 are 2.1, 2.2, 2.3.
|
|
995
|
-
Never letters. A message with one single choice numbers its options 1, 2, 3.
|
|
996
|
-
- **You fill in only what is in {braces}** — a path, a name, a count, a verdict — and write nothing
|
|
997
|
-
else afresh. **A message with unfilled {braces} is never sent**: if you do not know what goes in
|
|
998
|
-
them, the message is not for now.
|
|
999
|
-
- **Recommending:** on a question with options, except those about what the operator has or knows,
|
|
1000
|
-
you may add after one option *(recommended — the reason in a few words)*. Only when you have one.
|
|
1001
|
-
- **Already said:** if a question has already been answered — in the operator's documents above or
|
|
1002
|
-
earlier in this conversation — the question stays where it is and, under it, after a blank line
|
|
1003
|
-
and indented like the *Example:* lines, you write \`${ALREADY_SAID.en} …\` with the answer — a line
|
|
1004
|
-
of its own, not the end of the question; on a choice you mark the option *(already said)*. The
|
|
1005
|
-
answer is the whole of it, not a label: for the assignment, the title, the goal and the context from
|
|
1006
|
-
"This work", not the title alone; for the criteria, every one of them. The operator confirms what
|
|
1007
|
-
they see, and cannot confirm what is not shown.
|
|
1008
|
-
The last line of the quotation is then exactly one of these two: when every question is already
|
|
1009
|
-
answered — *${ON_RECORD_ALL.en}* — and when only some are — *${ON_RECORD_MIXED.en}*
|
|
1010
|
-
- **Around the quotation:** before it, only the greeting, and only in the first message; after it,
|
|
1011
|
-
nothing: no "I look forward to your answers", no "When you are ready…". The quotation's last line
|
|
1012
|
-
already says what happens next. Messages that ask nothing also open with their phase line and end
|
|
1013
|
-
on their last step. The phase 2 line is \`${PHASE_LINES.p2.en}\`.
|
|
1014
|
-
- **The one place for questions of your own** is a vague or missing answer in phase 1: then, before
|
|
1015
|
-
the settings, you send one message of follow-ups — the phase line, numbered questions only, 1, 2,
|
|
1016
|
-
3, with options 1.1, 1.2 where the answers can be listed, and the same last line as here.
|
|
1017
|
-
|
|
1018
|
-
Messages that ask nothing — the JSON, the steps in the app, the diagnosis, the table of evidence —
|
|
987
|
+
: `## The questions, word for word
|
|
988
|
+
|
|
989
|
+
Every message in which you ask the operator something is here — word for word, in the order they
|
|
990
|
+
come. **Copy it exactly:** the same questions, in the same order, with the same numbers, the same
|
|
991
|
+
options and the same *Example:* lines. Do not add a question, drop one, merge, split, reword or
|
|
992
|
+
reorder — the operator learns the conversation, and that only works if it is the same every time. The
|
|
993
|
+
heading above each quotation is for you: it says when the message is sent; the operator sees only
|
|
994
|
+
what is quoted, without the \`>\`.
|
|
995
|
+
|
|
996
|
+
- **The first line of every quotation is the phase line.** Copy it as it is; do not change it and do
|
|
997
|
+
not add another.
|
|
998
|
+
- **Numbers are digits only.** Questions are 1, 2, 3; the options under question 2 are 2.1, 2.2, 2.3.
|
|
999
|
+
Never letters. A message with one single choice numbers its options 1, 2, 3.
|
|
1000
|
+
- **You fill in only what is in {braces}** — a path, a name, a count, a verdict — and write nothing
|
|
1001
|
+
else afresh. **A message with unfilled {braces} is never sent**: if you do not know what goes in
|
|
1002
|
+
them, the message is not for now.
|
|
1003
|
+
- **Recommending:** on a question with options, except those about what the operator has or knows,
|
|
1004
|
+
you may add after one option *(recommended — the reason in a few words)*. Only when you have one.
|
|
1005
|
+
- **Already said:** if a question has already been answered — in the operator's documents above or
|
|
1006
|
+
earlier in this conversation — the question stays where it is and, under it, after a blank line
|
|
1007
|
+
and indented like the *Example:* lines, you write \`${ALREADY_SAID.en} …\` with the answer — a line
|
|
1008
|
+
of its own, not the end of the question; on a choice you mark the option *(already said)*. The
|
|
1009
|
+
answer is the whole of it, not a label: for the assignment, the title, the goal and the context from
|
|
1010
|
+
"This work", not the title alone; for the criteria, every one of them. The operator confirms what
|
|
1011
|
+
they see, and cannot confirm what is not shown.
|
|
1012
|
+
The last line of the quotation is then exactly one of these two: when every question is already
|
|
1013
|
+
answered — *${ON_RECORD_ALL.en}* — and when only some are — *${ON_RECORD_MIXED.en}*
|
|
1014
|
+
- **Around the quotation:** before it, only the greeting, and only in the first message; after it,
|
|
1015
|
+
nothing: no "I look forward to your answers", no "When you are ready…". The quotation's last line
|
|
1016
|
+
already says what happens next. Messages that ask nothing also open with their phase line and end
|
|
1017
|
+
on their last step. The phase 2 line is \`${PHASE_LINES.p2.en}\`.
|
|
1018
|
+
- **The one place for questions of your own** is a vague or missing answer in phase 1: then, before
|
|
1019
|
+
the settings, you send one message of follow-ups — the phase line, numbered questions only, 1, 2,
|
|
1020
|
+
3, with options 1.1, 1.2 where the answers can be listed, and the same last line as here.
|
|
1021
|
+
|
|
1022
|
+
Messages that ask nothing — the JSON, the steps in the app, the diagnosis, the table of evidence —
|
|
1019
1023
|
follow the description in their phase.`;
|
|
1020
|
-
return `
|
|
1021
|
-
${head}
|
|
1022
|
-
|
|
1023
|
-
${blocks.join('\n\n')}
|
|
1024
|
+
return `
|
|
1025
|
+
${head}
|
|
1026
|
+
|
|
1027
|
+
${blocks.join('\n\n')}
|
|
1024
1028
|
`;
|
|
1025
1029
|
}
|
|
@@ -798,18 +798,34 @@ export const SYSTEM_GUIDE = [
|
|
|
798
798
|
{
|
|
799
799
|
key: 'home.tickForBoth',
|
|
800
800
|
kind: 'note',
|
|
801
|
-
en: 'The ticks
|
|
802
|
-
bg: '
|
|
803
|
-
doesEn: '
|
|
804
|
-
doesBg: '
|
|
801
|
+
en: 'The first column of ticks manages the list — status and delete — and works while a run is going, except on the session running now. The second chooses what to run; only active sessions with something queued can be run.',
|
|
802
|
+
bg: 'Първата колона отметки управлява списъка — статус и изтриване — и работи и докато тече пускане, освен за сесията, която работи в момента. Втората избира какво да се пусне; пускат се само активни сесии с нещо в опашката.',
|
|
803
|
+
doesEn: 'Two sets of tick boxes: the first for status and delete, the second for the run panel above.',
|
|
804
|
+
doesBg: 'Два набора отметки: първият за статус и изтриване, вторият за панела за пускане горе.',
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
key: 'home.makeActive',
|
|
808
|
+
kind: 'button',
|
|
809
|
+
en: 'Make active',
|
|
810
|
+
bg: 'Направи активни',
|
|
811
|
+
doesEn: 'Every session ticked in the first column can be run again. New and imported sessions are active.',
|
|
812
|
+
doesBg: 'Всички отметнати в първата колона сесии отново могат да се пускат. Новите и импортираните са активни.',
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
key: 'home.makeInactive',
|
|
816
|
+
kind: 'button',
|
|
817
|
+
en: 'Make inactive',
|
|
818
|
+
bg: 'Направи неактивни',
|
|
819
|
+
doesEn: 'Sets them aside: nothing starts them — not a run of several, not "Continue" — until made active. Not for a running session.',
|
|
820
|
+
doesBg: 'Оставя ги настрана: нищо не ги пуска — нито пускане на няколко, нито „Продължи“ — докато не станат активни. Не и за работеща сесия.',
|
|
805
821
|
},
|
|
806
822
|
{
|
|
807
823
|
key: 'home.deleteSelected',
|
|
808
824
|
kind: 'button',
|
|
809
825
|
en: 'Delete the {n} selected',
|
|
810
826
|
bg: 'Изтрий избраните {n}',
|
|
811
|
-
doesEn: 'Deletes every ticked
|
|
812
|
-
doesBg: 'Изтрива всички отметнати сесии след един
|
|
827
|
+
doesEn: 'Deletes every session ticked in the first column after one question, also during a run; never the one running. Run folders and chats survive.',
|
|
828
|
+
doesBg: 'Изтрива всички отметнати в първата колона сесии след един въпрос, и по време на пускане; никога работещата. Папките и разговорите остават.',
|
|
813
829
|
},
|
|
814
830
|
{
|
|
815
831
|
key: 'home.delete',
|
|
@@ -1112,6 +1128,30 @@ export const SYSTEM_GUIDE = [
|
|
|
1112
1128
|
doesEn: 'The old behaviour, and what a session without a choice does: whatever branch is checked out at its first run, often the one the previous session left.',
|
|
1113
1129
|
doesBg: 'Старото поведение и това, което прави сесия без избор: какъвто клон е избран при първото ѝ пускане, често оставеният от предната сесия.',
|
|
1114
1130
|
},
|
|
1131
|
+
{
|
|
1132
|
+
key: 'vcs.startExisting',
|
|
1133
|
+
kind: 'radio',
|
|
1134
|
+
en: 'Carry on an existing branch',
|
|
1135
|
+
bg: 'Продължи съществуващ клон',
|
|
1136
|
+
doesEn: 'No new branch: every task works on the branch named under it, exactly as in git. A missing branch or uncommitted changes refuse the task; nothing runs.',
|
|
1137
|
+
doesBg: 'Без нов клон: всяка задача работи на клона, посочен под него, точно както е в git. Липсващ клон или незакомитнати промени отказват задачата; нищо не се изпълнява.',
|
|
1138
|
+
},
|
|
1139
|
+
{
|
|
1140
|
+
key: 'vcs.existingBranch',
|
|
1141
|
+
kind: 'field',
|
|
1142
|
+
en: 'The branch to carry on',
|
|
1143
|
+
bg: 'Клонът, който да продължи',
|
|
1144
|
+
doesEn: 'The exact name of the branch "Carry on an existing branch" works on; the repository\'s local branches are offered as you type.',
|
|
1145
|
+
doesBg: 'Точното име на клона, на който работи „Продължи съществуващ клон“; докато пишеш, се предлагат локалните клонове.',
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
key: 'vcs.updateFromRemote',
|
|
1149
|
+
kind: 'checkbox',
|
|
1150
|
+
en: 'Bring the starting branch up to date from the remote first',
|
|
1151
|
+
bg: 'Първо обнови началния клон от отдалеченото хранилище',
|
|
1152
|
+
doesEn: 'On by default. Before the session\'s first branch: git fetch, then a fast-forward of the starting branch only; a branch with its own commits is left and said.',
|
|
1153
|
+
doesBg: 'Включено по подразбиране. Преди първия клон на сесията: git fetch и само fast-forward на началния клон; клон със свои commit-и се оставя и се казва.',
|
|
1154
|
+
},
|
|
1115
1155
|
{
|
|
1116
1156
|
key: 'vcs.baseBranch',
|
|
1117
1157
|
kind: 'field',
|
|
@@ -1474,8 +1514,8 @@ export const SYSTEM_GUIDE = [
|
|
|
1474
1514
|
kind: 'button',
|
|
1475
1515
|
en: 'Continue in the same chat',
|
|
1476
1516
|
bg: 'Продължи в същия чат',
|
|
1477
|
-
doesEn: 'On a task that stopped before it finished (
|
|
1478
|
-
doesBg: 'На задача, спряла преди да приключи (
|
|
1517
|
+
doesEn: 'On a task that stopped before it finished (a limit from the settings, the bot stopping, the operator): queues it to carry on in the same chat and branch. Then "Run {n} task(s)".',
|
|
1518
|
+
doesBg: 'На задача, спряла преди да приключи (лимит от настройките, спрял бот, оператор): нарежда я да продължи в същия чат и клон. После „Пусни {n} задача(и)“.',
|
|
1479
1519
|
},
|
|
1480
1520
|
{
|
|
1481
1521
|
key: 'task.rerun',
|
|
@@ -2174,6 +2214,22 @@ export const SYSTEM_GUIDE = [
|
|
|
2174
2214
|
doesEn: 'The same button once the story is open: folds it away again.',
|
|
2175
2215
|
doesBg: 'Същият бутон, когато историята е отворена: сгъва я обратно.',
|
|
2176
2216
|
},
|
|
2217
|
+
{
|
|
2218
|
+
key: 'reg.rerunHere',
|
|
2219
|
+
kind: 'button',
|
|
2220
|
+
en: 'Run only this one again',
|
|
2221
|
+
bg: 'Пусни само тази отново',
|
|
2222
|
+
doesEn: 'On a row that failed or blocked on its work: says what happens to the chat, branch and record, then starts a new attempt of that task alone; the rest of the queue stays.',
|
|
2223
|
+
doesBg: 'На ред, провален или блокиран по работата: казва какво става с чата, клона и записа, после пуска нов опит само за тази задача; останалите в опашката остават.',
|
|
2224
|
+
},
|
|
2225
|
+
{
|
|
2226
|
+
key: 'reg.continueHere',
|
|
2227
|
+
kind: 'button',
|
|
2228
|
+
en: 'Continue where it stopped',
|
|
2229
|
+
bg: 'Продължи откъдето спря',
|
|
2230
|
+
doesEn: 'On a row stopped by a limit from the settings (minutes, messages, reply wait, rounds of fixing), by the bot stopping or by the operator: says what happens to the chat, branch and files, then starts that task alone in its chat.',
|
|
2231
|
+
doesBg: 'На ред, спрян от лимит в настройките (минути, съобщения, чакане на отговор, кръгове поправки), от спрял бот или от оператора: казва какво става с чата, клона и файловете, после пуска само тази задача в чата ѝ.',
|
|
2232
|
+
},
|
|
2177
2233
|
{
|
|
2178
2234
|
key: 'reg.fixPrompt',
|
|
2179
2235
|
kind: 'button',
|
|
@@ -2415,17 +2471,17 @@ export const SYSTEM_GUIDE = [
|
|
|
2415
2471
|
{
|
|
2416
2472
|
key: 'reg.pickAll',
|
|
2417
2473
|
kind: 'button',
|
|
2418
|
-
en: '
|
|
2419
|
-
bg: '
|
|
2474
|
+
en: 'Tick all',
|
|
2475
|
+
bg: 'Маркирай всички',
|
|
2420
2476
|
doesEn: 'In that panel: ticks every task.',
|
|
2421
2477
|
doesBg: 'В панела: отметва всички задачи.',
|
|
2422
2478
|
},
|
|
2423
2479
|
{
|
|
2424
2480
|
key: 'reg.pickNone',
|
|
2425
2481
|
kind: 'button',
|
|
2426
|
-
en: '
|
|
2427
|
-
bg: '
|
|
2428
|
-
doesEn: 'In that panel: unticks every task (the start buttons then do nothing until one is ticked).',
|
|
2482
|
+
en: 'Untick all',
|
|
2483
|
+
bg: 'Размаркирай всички',
|
|
2484
|
+
doesEn: 'In that panel: unticks every task, beside the count of ticked ones (the start buttons then do nothing until one is ticked).',
|
|
2429
2485
|
doesBg: 'В панела: маха всички отметки (бутоните за пускане тогава не действат, докато не се отметне поне една).',
|
|
2430
2486
|
},
|
|
2431
2487
|
{
|
|
@@ -2685,8 +2741,16 @@ export const SYSTEM_GUIDE = [
|
|
|
2685
2741
|
kind: 'checkbox',
|
|
2686
2742
|
en: 'Keep the projects on the Desktop',
|
|
2687
2743
|
bg: 'Дръж проектите на Desktop-а',
|
|
2688
|
-
doesEn: '
|
|
2689
|
-
doesBg: '
|
|
2744
|
+
doesEn: 'The master switch: copies the projects on this page to their own Desktop folders before a run and after every round of steps, which is how the files reach the chat.',
|
|
2745
|
+
doesBg: 'Главният ключ: копира проектите от тази страница в собствени папки на Desktop-а преди пускане и след всеки кръг стъпки — така файловете стигат до чата.',
|
|
2746
|
+
},
|
|
2747
|
+
{
|
|
2748
|
+
key: 'proj.desktopThis',
|
|
2749
|
+
kind: 'checkbox',
|
|
2750
|
+
en: 'Keep a copy of this project on the Desktop',
|
|
2751
|
+
bg: 'Дръж копие на този проект на Desktop-а',
|
|
2752
|
+
doesEn: 'Under each project, on by default: untick to keep that one off the Desktop while the switch is on. Greyed out while the switch is off.',
|
|
2753
|
+
doesBg: 'Под всеки проект, включено по подразбиране: махнете отметката, за да не се копира точно той, докато ключът е включен. Неактивно, докато ключът е изключен.',
|
|
2690
2754
|
},
|
|
2691
2755
|
{
|
|
2692
2756
|
key: 'proj.folders',
|
|
@@ -54,8 +54,23 @@ function whyItStopped(c) {
|
|
|
54
54
|
return 'It was stopped by the operator before it finished — not because anything failed. The files are as you left them.';
|
|
55
55
|
}
|
|
56
56
|
const why = c.stoppedBecause ? ` (${c.stoppedBecause})` : '';
|
|
57
|
-
|
|
57
|
+
// Out of rounds of fixing: something did say no, and the chat needs to hear it was not waved through.
|
|
58
|
+
if (c.limit?.setting === 'maxCheckRounds' || c.limit?.setting === 'maxReviewRounds') {
|
|
59
|
+
return (`It stopped because the runner's limit of ${LIMIT_WORDS[c.limit.setting](c.limit.value)} was reached${why}. ` +
|
|
60
|
+
'What was still failing then is still open: deal with it first. The count starts again now. The files are as you left them.');
|
|
61
|
+
}
|
|
62
|
+
const which = c.limit ? ` of ${LIMIT_WORDS[c.limit.setting](c.limit.value)}` : '';
|
|
63
|
+
return `It stopped because the runner's limit${which} was reached${why} — not because anything failed. The files are as you left them.`;
|
|
58
64
|
}
|
|
65
|
+
/** Each limit from the settings, as the chat is told about it. See `TaskLimit`. */
|
|
66
|
+
const LIMIT_WORDS = {
|
|
67
|
+
maxRunMinutes: (n) => `${n} minutes per task`,
|
|
68
|
+
maxIterations: (n) => `${n} messages per task`,
|
|
69
|
+
replyTimeoutSec: (n) => `${n} seconds to wait for one reply`,
|
|
70
|
+
maxFormatRetries: (n) => `${n} replies in the wrong format in a row`,
|
|
71
|
+
maxCheckRounds: (n) => `${n} rounds of fixing failed checks`,
|
|
72
|
+
maxReviewRounds: (n) => `${n} rounds of fixing review findings`,
|
|
73
|
+
};
|
|
59
74
|
export function composeOpening(input) {
|
|
60
75
|
const taskBlock = `${TASK_HEADER} ${input.taskNumber}: ${input.taskTitle.trim() || 'untitled'}\n\n${input.prompt.trim()}`;
|
|
61
76
|
/*
|
|
@@ -87,10 +87,17 @@ function versionControlLines(task, session) {
|
|
|
87
87
|
if (!vcs.branch)
|
|
88
88
|
return [`version control : did not run — ${vcs.problem}`];
|
|
89
89
|
const commits = vcs.commits ?? [];
|
|
90
|
+
// In the order it happened: the tree before the runner's commit, the commit, the tree after it.
|
|
91
|
+
const before = vcs.beforeCommit;
|
|
92
|
+
const after = vcs.afterCommit;
|
|
90
93
|
const lines = [
|
|
91
94
|
`branch : ${vcs.branch}`,
|
|
92
95
|
`started at : ${vcs.baseCommit ? vcs.baseCommit.slice(0, 8) : '—'} (the commit this task branched from)`,
|
|
93
|
-
`commit
|
|
96
|
+
...(before ? [`before commit: ${before.changed.length} path(s) changed or new in the working tree${before.changed.length ? ` — ${before.changed.slice(0, 10).join(', ')}` : ''}`] : []),
|
|
97
|
+
`commit : ${vcs.commit ? vcs.commit.slice(0, 8) : vcs.problem ? 'none — see the note below' : 'none — the task changed no files'}`,
|
|
98
|
+
...(after
|
|
99
|
+
? [`after commit: on ${after.branch ?? '?'} at ${after.head ? after.head.slice(0, 8) : '?'}, ${after.clean ? 'working tree clean' : `still uncommitted: ${after.changed.slice(0, 10).join(', ')}`}`]
|
|
100
|
+
: []),
|
|
94
101
|
`commits : ${commits.length} on this branch since it was cut`,
|
|
95
102
|
];
|
|
96
103
|
for (const line of commits)
|
|
@@ -144,6 +144,9 @@ export function buildPlanExport(scope) {
|
|
|
144
144
|
// Only when chosen: absent is the old behaviour, and writing it out would suggest a choice.
|
|
145
145
|
...(s.vcs?.startFrom ? { startFrom: s.vcs.startFrom } : {}),
|
|
146
146
|
...(s.vcs?.baseBranch ? { baseBranch: s.vcs.baseBranch } : {}),
|
|
147
|
+
...(s.vcs?.existingBranch ? { existingBranch: s.vcs.existingBranch } : {}),
|
|
148
|
+
// Only when switched off: absent is on.
|
|
149
|
+
...(s.vcs?.updateFromRemote === false ? { updateFromRemote: false } : {}),
|
|
147
150
|
},
|
|
148
151
|
review: { enabled: s.review?.enabled !== false, model: s.review?.model ?? '' },
|
|
149
152
|
mirror: {
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The end of a task in one fixed shape, put together by the runner from what it recorded.
|
|
3
|
+
*
|
|
4
|
+
* The chat's summary is the model's account, in its words and in whatever order it chose. The
|
|
5
|
+
* operator reading a finished run needs the same eight answers every time — what came of it, which
|
|
6
|
+
* files, what was checked, what is still wrong, where the evidence is, where the code is, what is
|
|
7
|
+
* left for a person to do, and what was never run — and until now had to piece them together from
|
|
8
|
+
* the summary, the check list, the review panel, the branch line and the iteration reports. This
|
|
9
|
+
* reads them off the task record instead, so every entry is a fact the runner saw rather than a
|
|
10
|
+
* claim, and nothing here is written by the model.
|
|
11
|
+
*/
|
|
12
|
+
import { isContinuable } from './model.js';
|
|
1
13
|
export function composeHandoff(task, notRun = []) {
|
|
2
14
|
const failedChecks = (task.checkResults ?? []).filter((c) => !c.passed);
|
|
3
15
|
const openFindings = task.review?.verdict === 'fail' ? (task.review.findings ?? []).filter((f) => f.about !== 'task') : [];
|
|
@@ -18,10 +30,10 @@ export function composeHandoff(task, notRun = []) {
|
|
|
18
30
|
manual.push(`version control: ${vcs.problem}`);
|
|
19
31
|
if (vcs.commit && vcs.branch)
|
|
20
32
|
manual.push(`push ${vcs.branch} when you are ready — the runner never pushes`);
|
|
33
|
+
if (isContinuable(task))
|
|
34
|
+
manual.push('continue it in the same chat from the register, or run it again');
|
|
21
35
|
if (task.status === 'blocked')
|
|
22
36
|
manual.push(`unblock it: ${task.reason ?? 'see the reason'}`);
|
|
23
|
-
if (task.status === 'limit-reached' || task.status === 'aborted')
|
|
24
|
-
manual.push('continue it in the same chat from the register, or run it again');
|
|
25
37
|
if (task.status === 'failed')
|
|
26
38
|
manual.push('read why it failed, fix the prompt or the checks, and queue it again');
|
|
27
39
|
if (openFindings.length > 0)
|
|
@@ -32,7 +44,33 @@ export function composeHandoff(task, notRun = []) {
|
|
|
32
44
|
outcome: { status: task.status, ...(task.reason ? { reason: task.reason } : {}), ...(task.stopCode ? { stopCode: task.stopCode } : {}) },
|
|
33
45
|
changedFiles: (vcs.files ?? []).map((f) => ({ path: f.path, added: f.added, removed: f.removed })),
|
|
34
46
|
validation: (task.checkResults ?? []).map((c) => ({ name: c.name, passed: c.passed })),
|
|
35
|
-
...(task.review
|
|
47
|
+
...(task.review
|
|
48
|
+
? { review: { verdict: task.review.verdict, open: openFindings.length, ...(task.review.skippedBecause ? { skippedBecause: task.review.skippedBecause } : {}) } }
|
|
49
|
+
: {}),
|
|
50
|
+
...(vcs.branch || vcs.problem
|
|
51
|
+
? {
|
|
52
|
+
lifecycle: {
|
|
53
|
+
...(vcs.beforeCommit ? { preCommitState: { changed: vcs.beforeCommit.changed } } : {}),
|
|
54
|
+
checksResult: (task.checkResults ?? []).map((c) => ({ name: c.name, passed: c.passed })),
|
|
55
|
+
commitResult: {
|
|
56
|
+
...(vcs.branch ? { branch: vcs.branch } : {}),
|
|
57
|
+
...(vcs.commit ? { commit: vcs.commit } : {}),
|
|
58
|
+
files: vcs.files?.length ?? 0,
|
|
59
|
+
...(vcs.problem ? { problem: vcs.problem } : {}),
|
|
60
|
+
},
|
|
61
|
+
...(vcs.afterCommit
|
|
62
|
+
? {
|
|
63
|
+
postCommitState: {
|
|
64
|
+
...(vcs.afterCommit.branch ? { branch: vcs.afterCommit.branch } : {}),
|
|
65
|
+
...(vcs.afterCommit.head ? { head: vcs.afterCommit.head } : {}),
|
|
66
|
+
clean: vcs.afterCommit.clean,
|
|
67
|
+
uncommitted: vcs.afterCommit.changed,
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
: {}),
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
: {}),
|
|
36
74
|
knownIssues,
|
|
37
75
|
evidence: { ...(task.runId ? { runId: task.runId } : {}), checks: task.checkResults?.length ?? 0, reviewRounds: task.review?.rounds ?? 0 },
|
|
38
76
|
vcs: { ...(vcs.branch ? { branch: vcs.branch } : {}), ...(vcs.commit ? { commit: vcs.commit } : {}), pushed: false, ...(vcs.problem ? { problem: vcs.problem } : {}) },
|
|
@@ -12,6 +12,15 @@ export function tidyVcsPlan(plan) {
|
|
|
12
12
|
return undefined;
|
|
13
13
|
return { ...(branch ? { branch } : {}), ...(commitMessage ? { commitMessage } : {}) };
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether "Continue" may carry a task on where it stopped, in its chat and on its branch.
|
|
17
|
+
*
|
|
18
|
+
* A task that stopped before it finished qualifies (`limit-reached`, `aborted`), and so does one
|
|
19
|
+
* whose ending came from a limit in the settings whatever its status says. See `TaskLimit`.
|
|
20
|
+
*/
|
|
21
|
+
export function isContinuable(t) {
|
|
22
|
+
return t.status === 'limit-reached' || t.status === 'aborted' || (!!t.limit && (t.status === 'failed' || t.status === 'blocked'));
|
|
23
|
+
}
|
|
15
24
|
export function newId(prefix = '') {
|
|
16
25
|
const now = new Date();
|
|
17
26
|
const p = (n) => String(n).padStart(2, '0');
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { mkdir, readFile, writeFile, readdir, rename, rm } from 'node:fs/promises';
|
|
10
10
|
import { existsSync } from 'node:fs';
|
|
11
11
|
import { join, resolve } from 'node:path';
|
|
12
|
-
import { newId, tidyVcsPlan } from './model.js';
|
|
12
|
+
import { isContinuable, newId, tidyVcsPlan } from './model.js';
|
|
13
13
|
/** A task the runner is inside of. It cannot be edited, deleted, or left behind at startup. */
|
|
14
14
|
const ACTIVE_STATUSES = ['running', 'waiting-approval'];
|
|
15
15
|
/**
|
|
@@ -341,22 +341,24 @@ export class SessionStore {
|
|
|
341
341
|
*
|
|
342
342
|
* Two kinds of stop qualify. The runner's limit (`limit-reached`), and a stop that was not the
|
|
343
343
|
* task's doing (`aborted`): the bot itself stopping under it — power, a shutdown, Ctrl+C, a crash,
|
|
344
|
-
* recorded in `interruption` at the next start — or the operator stopping it. A task that failed
|
|
345
|
-
*
|
|
346
|
-
*
|
|
344
|
+
* recorded in `interruption` at the next start — or the operator stopping it. A task that failed
|
|
345
|
+
* or blocked has a verdict on its work, and carrying that work on would carry the verdict's cause
|
|
346
|
+
* on with it; those are run again — unless what ended them was a limit from the settings (out of
|
|
347
|
+
* rounds of fixing checks or review findings), which is a counter, not the verdict. See `TaskLimit`.
|
|
347
348
|
*/
|
|
348
349
|
async continueTask(sessionId, taskId) {
|
|
349
350
|
const current = (await this.getSession(sessionId))?.tasks.find((x) => x.id === taskId);
|
|
350
351
|
if (!current)
|
|
351
352
|
throw new Error(`Task ${taskId} does not exist in session ${sessionId}.`);
|
|
352
|
-
if (current
|
|
353
|
-
throw new Error('Only a task that stopped before it finished can be continued; run this one again instead.');
|
|
353
|
+
if (!isContinuable(current)) {
|
|
354
|
+
throw new Error('Only a task that stopped before it finished, or at a limit from the settings, can be continued; run this one again instead.');
|
|
354
355
|
}
|
|
355
|
-
const how = current.status === 'limit-reached' ? 'limit' : current.interruption ? 'interrupted' : 'stopped';
|
|
356
|
+
const how = current.status === 'limit-reached' || current.limit ? 'limit' : current.interruption ? 'interrupted' : 'stopped';
|
|
356
357
|
return await this.rerunTask(sessionId, taskId, {}, {
|
|
357
358
|
fromAttempt: current.attempt ?? 1,
|
|
358
359
|
stoppedBecause: current.reason,
|
|
359
360
|
how,
|
|
361
|
+
...(current.limit ? { limit: current.limit } : {}),
|
|
360
362
|
...(current.interruption ? { interruption: current.interruption } : {}),
|
|
361
363
|
});
|
|
362
364
|
}
|
|
@@ -400,6 +402,7 @@ export class SessionStore {
|
|
|
400
402
|
buildsOn: t.buildsOn,
|
|
401
403
|
freshRetry: t.freshRetry,
|
|
402
404
|
stopCode: t.stopCode,
|
|
405
|
+
limit: t.limit,
|
|
403
406
|
scope: t.scope,
|
|
404
407
|
scopeReverted: t.scopeReverted,
|
|
405
408
|
vcs: t.vcs,
|
|
@@ -438,6 +441,7 @@ export class SessionStore {
|
|
|
438
441
|
t.scopeReverted = undefined;
|
|
439
442
|
t.freshRetry = undefined;
|
|
440
443
|
t.stopCode = undefined;
|
|
444
|
+
t.limit = undefined;
|
|
441
445
|
// The branch of the finished attempt stays in the repository and stays on the record
|
|
442
446
|
// above; the next attempt gets its own, cut from the same commit this one started at.
|
|
443
447
|
t.vcs = undefined;
|