copilot-operator 0.1.12 → 0.1.15
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/README.md +9 -14
- package/dist/src/api/operator.controller.js +31 -42
- package/dist/src/api/operator.service.js +692 -364
- package/dist/src/api/saveToDesktop.js +1 -1
- package/dist/src/api/security.js +40 -10
- package/dist/src/api/server.js +75 -12
- package/dist/src/api/settings.js +156 -15
- package/dist/src/cli.js +108 -97
- package/dist/src/config/handWritten.js +36 -0
- package/dist/src/config/layout.js +13 -4
- package/dist/src/config/lockedPolicy.js +56 -22
- package/dist/src/config/schema.js +13 -84
- package/dist/src/context/desktopDir.js +44 -0
- package/dist/src/exec/authorizer.js +9 -1
- package/dist/src/exec/checks.js +240 -23
- package/dist/src/exec/dangerous.js +29 -5
- package/dist/src/exec/network.js +105 -28
- package/dist/src/exec/redaction.js +52 -9
- package/dist/src/exec/reportFile.js +4 -5
- package/dist/src/exec/runner.js +146 -13
- package/dist/src/exec/shells.js +41 -6
- package/dist/src/exec/workDir.js +4 -4
- package/dist/src/orchestrator/derivedChecks.js +1 -0
- package/dist/src/orchestrator/review.js +25 -3
- package/dist/src/orchestrator/taskRunner.js +482 -150
- package/dist/src/plan/brief.js +56 -44
- package/dist/src/plan/importPlan.js +18 -7
- package/dist/src/plan/schema.js +90 -31
- package/dist/src/plan/script.js +79 -90
- package/dist/src/plan/systemGuide.js +39 -182
- package/dist/src/protocol/parser.js +5 -1
- package/dist/src/session/debugExport.js +1 -1
- package/dist/src/session/exportRecord.js +1 -1
- package/dist/src/session/exports.js +52 -9
- package/dist/src/session/store.js +215 -80
- package/dist/src/vcs/git.js +77 -2
- package/dist/src/vcs/scope.js +29 -6
- package/dist/src/vcs/taskVcs.js +149 -23
- package/dist/web/404.html +2 -2
- package/dist/web/_next/static/chunks/36-94660f1cde2da529.js +1 -0
- package/dist/web/_next/static/chunks/549-9548d9a132c8c7d4.js +1 -0
- package/dist/web/_next/static/chunks/867-8b95ae984befc0e2.js +1 -0
- package/dist/web/_next/static/chunks/app/defaults/page-71a43ed69134b48d.js +1 -0
- package/dist/web/_next/static/chunks/app/history/page-d7314ac22d47a3ab.js +1 -0
- package/dist/web/_next/static/chunks/app/sessions/view/page-886f5fc99e146408.js +1 -0
- package/dist/web/_next/static/css/{a0642fdb8adf9edb.css → e2a2c9eb3c226313.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 +18 -10
- package/prompts/level1.md +2 -1
- package/prompts/organisation.bg.md +0 -1
- package/prompts/organisation.en.md +0 -1
- package/dist/src/context/contextFiles.js +0 -295
- package/dist/src/context/desktopMirror.js +0 -135
- package/dist/src/context/projectMirror.js +0 -432
- package/dist/web/_next/static/chunks/295-04151ded0741d18d.js +0 -1
- package/dist/web/_next/static/chunks/36-c3dd06c6a459c114.js +0 -1
- package/dist/web/_next/static/chunks/549-a5a59a94f92bb3eb.js +0 -1
- package/dist/web/_next/static/chunks/867-b64c436f5b25f39e.js +0 -1
- package/dist/web/_next/static/chunks/app/defaults/page-5f180a88398be4c9.js +0 -1
- package/dist/web/_next/static/chunks/app/history/page-854e3dde672e38a2.js +0 -1
- package/dist/web/_next/static/chunks/app/sessions/view/page-cc99401da5173b36.js +0 -1
- /package/dist/web/_next/static/{bcBE4tY_aNh6kzLzN1luP → IwNZiBrEMAcXEUFj2miiU}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{bcBE4tY_aNh6kzLzN1luP → IwNZiBrEMAcXEUFj2miiU}/_ssgManifest.js +0 -0
package/dist/src/plan/script.js
CHANGED
|
@@ -173,14 +173,6 @@ export const SCRIPT = [
|
|
|
173
173
|
},
|
|
174
174
|
],
|
|
175
175
|
},
|
|
176
|
-
{
|
|
177
|
-
choose: { en: 'How do I read its code from this chat?', bg: 'Как да чета кода му от този чат?' },
|
|
178
|
-
options: [
|
|
179
|
-
{ en: 'As files attached to the first message of a task', bg: 'Като прикачени файлове към първото съобщение на задачата' },
|
|
180
|
-
{ en: 'Through the Desktop mirror (Desktop/copilot-operator-context/)', bg: 'През огледалото на Desktop-а (Desktop/copilot-operator-context/)' },
|
|
181
|
-
{ en: 'Both', bg: 'И двете' },
|
|
182
|
-
],
|
|
183
|
-
},
|
|
184
176
|
{
|
|
185
177
|
choose: {
|
|
186
178
|
en: 'May the bot make git branches and commits in this project?',
|
|
@@ -669,6 +661,10 @@ export const SCRIPT = [
|
|
|
669
661
|
en: "From the previous session's branch — the sessions form a chain (previous-session)",
|
|
670
662
|
bg: 'От клона на предната сесия — сесиите образуват верига (previous-session)',
|
|
671
663
|
},
|
|
664
|
+
{
|
|
665
|
+
en: 'On an existing branch — no new branch, the work continues on it; name it (existing-branch + existingBranch)',
|
|
666
|
+
bg: 'На съществуващ клон — без нов клон, работата продължава на него; посочи го (existing-branch + existingBranch)',
|
|
667
|
+
},
|
|
672
668
|
],
|
|
673
669
|
},
|
|
674
670
|
{
|
|
@@ -700,13 +696,6 @@ export const SCRIPT = [
|
|
|
700
696
|
{ en: 'Off', bg: 'Изключена' },
|
|
701
697
|
],
|
|
702
698
|
},
|
|
703
|
-
{
|
|
704
|
-
choose: { en: 'Project files for the chat (`mirror`):', bg: 'Файлове на проекта към чата (`mirror`):' },
|
|
705
|
-
options: [
|
|
706
|
-
{ en: 'No', bg: 'Не' },
|
|
707
|
-
{ en: 'Yes — give the root and the folders', bg: 'Да — напиши корена и папките' },
|
|
708
|
-
],
|
|
709
|
-
},
|
|
710
699
|
{
|
|
711
700
|
choose: { en: 'Name of the run (`plan`):', bg: 'Име на пускането (`plan`):' },
|
|
712
701
|
options: [{ en: '{the name you propose from the goal}', bg: '{името, което предлагаш от целта}' }],
|
|
@@ -944,82 +933,82 @@ export function scriptSection(stages, projects, lang) {
|
|
|
944
933
|
return `### ${m.when[lang]}\n\n${quoted.join('\n')}`;
|
|
945
934
|
});
|
|
946
935
|
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-ът, стъпките в приложението, диагнозата, таблицата с
|
|
936
|
+
? `## Въпросите, дословно
|
|
937
|
+
|
|
938
|
+
Всяко съобщение, в което питаш оператора нещо, е тук — дословно, в реда, в който идват. **Копирай го
|
|
939
|
+
точно:** същите въпроси, в същия ред, със същите номера, същите опции и същите редове *Пример:*. Не
|
|
940
|
+
добавяй въпрос, не махай, не сливай, не разделяй, не преформулирай и не пренареждай — операторът учи
|
|
941
|
+
разговора, а това става само ако той е един и същ всеки път. Заглавието над цитата е за теб: казва
|
|
942
|
+
кога се праща съобщението; операторът вижда само цитираното, без знака \`>\`.
|
|
943
|
+
|
|
944
|
+
- **Първият ред на всеки цитат е редът за фазата.** Копираш го както е; не го сменяш и не добавяш
|
|
945
|
+
друг.
|
|
946
|
+
- **Номерата са само цифри.** Въпросите са 1, 2, 3; опциите под въпрос 2 са 2.1, 2.2, 2.3. Никога
|
|
947
|
+
букви. Съобщение с един-единствен избор номерира опциите си 1, 2, 3.
|
|
948
|
+
- **Попълваш само това в {скоби}** — път, име, брой, присъда — и нищо друго не пишеш наново.
|
|
949
|
+
**Съобщение с непопълнени {скоби} не се изпраща никога**: ако не знаеш с какво да ги попълниш,
|
|
950
|
+
значи съобщението не е за сега.
|
|
951
|
+
- **Препоръка:** на въпрос с опции, освен на тези за това какво операторът има или знае, може да
|
|
952
|
+
добавиш след една опция *(препоръчвам — причината в няколко думи)*. Само ако имаш причина.
|
|
953
|
+
- **Вече казано:** ако отговорът на въпрос вече е даден — в документите на оператора по-горе или
|
|
954
|
+
по-рано в този разговор — въпросът остава на мястото си, а под него, след празен ред и със същия
|
|
955
|
+
отстъп като редовете *Пример:*, пишеш \`${ALREADY_SAID.bg} …\` с отговора — отделен ред, не в края на
|
|
956
|
+
въпроса; на избор отбелязваш опцията с *(вече казано)*. Отговорът е целият, не етикет: за
|
|
957
|
+
заданието — заглавието, целта и контекстът от „Тази работа“, не само заглавието; за критериите —
|
|
958
|
+
всеки от тях. Операторът потвърждава това, което вижда, и не може да потвърди нещо, което не е
|
|
959
|
+
показано. Последният ред
|
|
960
|
+
на цитата тогава е точно един от тези два: когато всеки въпрос е вече казан — *${ON_RECORD_ALL.bg}*
|
|
961
|
+
— а когато само някои — *${ON_RECORD_MIXED.bg}*
|
|
962
|
+
- **Около цитата:** преди него — само поздравът, и то само в първото съобщение; след него — нищо: без
|
|
963
|
+
„Очаквам отговорите ти“ и без „Когато си готов…“. Последният ред на цитата вече казва какво следва.
|
|
964
|
+
Съобщенията, които не питат нищо, също започват с реда за фазата си и свършват на последната си
|
|
965
|
+
стъпка. Редът за фаза 2 е \`${PHASE_LINES.p2.bg}\`.
|
|
966
|
+
- **Единственото място за твои въпроси** е мъгляв или липсващ отговор във фаза 1: тогава, преди
|
|
967
|
+
настройките, пращаш едно съобщение с уточнения — редът за фазата, само номерирани въпроси, 1, 2, 3,
|
|
968
|
+
с опции 1.1, 1.2, когато отговорите могат да се изброят, и същия последен ред като тук.
|
|
969
|
+
|
|
970
|
+
Съобщенията, които не питат нищо — JSON-ът, стъпките в приложението, диагнозата, таблицата с
|
|
982
971
|
доказателствата — следват описанието във фазата си.`
|
|
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 —
|
|
972
|
+
: `## The questions, word for word
|
|
973
|
+
|
|
974
|
+
Every message in which you ask the operator something is here — word for word, in the order they
|
|
975
|
+
come. **Copy it exactly:** the same questions, in the same order, with the same numbers, the same
|
|
976
|
+
options and the same *Example:* lines. Do not add a question, drop one, merge, split, reword or
|
|
977
|
+
reorder — the operator learns the conversation, and that only works if it is the same every time. The
|
|
978
|
+
heading above each quotation is for you: it says when the message is sent; the operator sees only
|
|
979
|
+
what is quoted, without the \`>\`.
|
|
980
|
+
|
|
981
|
+
- **The first line of every quotation is the phase line.** Copy it as it is; do not change it and do
|
|
982
|
+
not add another.
|
|
983
|
+
- **Numbers are digits only.** Questions are 1, 2, 3; the options under question 2 are 2.1, 2.2, 2.3.
|
|
984
|
+
Never letters. A message with one single choice numbers its options 1, 2, 3.
|
|
985
|
+
- **You fill in only what is in {braces}** — a path, a name, a count, a verdict — and write nothing
|
|
986
|
+
else afresh. **A message with unfilled {braces} is never sent**: if you do not know what goes in
|
|
987
|
+
them, the message is not for now.
|
|
988
|
+
- **Recommending:** on a question with options, except those about what the operator has or knows,
|
|
989
|
+
you may add after one option *(recommended — the reason in a few words)*. Only when you have one.
|
|
990
|
+
- **Already said:** if a question has already been answered — in the operator's documents above or
|
|
991
|
+
earlier in this conversation — the question stays where it is and, under it, after a blank line
|
|
992
|
+
and indented like the *Example:* lines, you write \`${ALREADY_SAID.en} …\` with the answer — a line
|
|
993
|
+
of its own, not the end of the question; on a choice you mark the option *(already said)*. The
|
|
994
|
+
answer is the whole of it, not a label: for the assignment, the title, the goal and the context from
|
|
995
|
+
"This work", not the title alone; for the criteria, every one of them. The operator confirms what
|
|
996
|
+
they see, and cannot confirm what is not shown.
|
|
997
|
+
The last line of the quotation is then exactly one of these two: when every question is already
|
|
998
|
+
answered — *${ON_RECORD_ALL.en}* — and when only some are — *${ON_RECORD_MIXED.en}*
|
|
999
|
+
- **Around the quotation:** before it, only the greeting, and only in the first message; after it,
|
|
1000
|
+
nothing: no "I look forward to your answers", no "When you are ready…". The quotation's last line
|
|
1001
|
+
already says what happens next. Messages that ask nothing also open with their phase line and end
|
|
1002
|
+
on their last step. The phase 2 line is \`${PHASE_LINES.p2.en}\`.
|
|
1003
|
+
- **The one place for questions of your own** is a vague or missing answer in phase 1: then, before
|
|
1004
|
+
the settings, you send one message of follow-ups — the phase line, numbered questions only, 1, 2,
|
|
1005
|
+
3, with options 1.1, 1.2 where the answers can be listed, and the same last line as here.
|
|
1006
|
+
|
|
1007
|
+
Messages that ask nothing — the JSON, the steps in the app, the diagnosis, the table of evidence —
|
|
1019
1008
|
follow the description in their phase.`;
|
|
1020
|
-
return `
|
|
1021
|
-
${head}
|
|
1022
|
-
|
|
1023
|
-
${blocks.join('\n\n')}
|
|
1009
|
+
return `
|
|
1010
|
+
${head}
|
|
1011
|
+
|
|
1012
|
+
${blocks.join('\n\n')}
|
|
1024
1013
|
`;
|
|
1025
1014
|
}
|
|
@@ -1128,6 +1128,30 @@ export const SYSTEM_GUIDE = [
|
|
|
1128
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.',
|
|
1129
1129
|
doesBg: 'Старото поведение и това, което прави сесия без избор: какъвто клон е избран при първото ѝ пускане, често оставеният от предната сесия.',
|
|
1130
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
|
+
},
|
|
1131
1155
|
{
|
|
1132
1156
|
key: 'vcs.baseBranch',
|
|
1133
1157
|
kind: 'field',
|
|
@@ -1217,157 +1241,6 @@ export const SYSTEM_GUIDE = [
|
|
|
1217
1241
|
},
|
|
1218
1242
|
],
|
|
1219
1243
|
},
|
|
1220
|
-
{
|
|
1221
|
-
headingKey: 'mirror.title',
|
|
1222
|
-
en: 'Project files for the chat',
|
|
1223
|
-
bg: 'Файлове на проекта за чата',
|
|
1224
|
-
controls: [
|
|
1225
|
-
{
|
|
1226
|
-
key: 'mirror.enable',
|
|
1227
|
-
kind: 'checkbox',
|
|
1228
|
-
en: 'attach project files',
|
|
1229
|
-
bg: 'прикачай файловете на проекта',
|
|
1230
|
-
doesEn: 'Attaches the selected files to the first message of every task; it takes effect only after Save below.',
|
|
1231
|
-
doesBg: 'Прикача избраните файлове към първото съобщение на всяка задача; влиза в сила чак след Запази отдолу.',
|
|
1232
|
-
},
|
|
1233
|
-
{
|
|
1234
|
-
key: 'mirror.root',
|
|
1235
|
-
kind: 'field',
|
|
1236
|
-
en: 'Project root',
|
|
1237
|
-
bg: 'Корен на проекта',
|
|
1238
|
-
doesEn: 'The folder the include and exclude lists below are relative to.',
|
|
1239
|
-
doesBg: 'Папката, спрямо която са списъците за включване и изключване отдолу.',
|
|
1240
|
-
},
|
|
1241
|
-
{
|
|
1242
|
-
key: 'mirror.listDirs',
|
|
1243
|
-
kind: 'button',
|
|
1244
|
-
en: 'List directories',
|
|
1245
|
-
bg: 'Покажи директориите',
|
|
1246
|
-
doesEn: 'Lists the root\'s folders, to pick what to include.',
|
|
1247
|
-
doesBg: 'Изброява папките в корена, за да се избере какво да се включи.',
|
|
1248
|
-
},
|
|
1249
|
-
{
|
|
1250
|
-
key: 'mirror.rules',
|
|
1251
|
-
kind: 'note',
|
|
1252
|
-
en: 'What is copied from it',
|
|
1253
|
-
bg: 'Какво се копира от него',
|
|
1254
|
-
doesEn: 'The heading above the include and exclude lists.',
|
|
1255
|
-
doesBg: 'Заглавието над списъците за включване и изключване.',
|
|
1256
|
-
},
|
|
1257
|
-
{
|
|
1258
|
-
key: 'mirror.include',
|
|
1259
|
-
kind: 'field',
|
|
1260
|
-
en: 'Include (one per line; a directory includes everything beneath it)',
|
|
1261
|
-
bg: 'Включи (по една на ред; директория включва всичко под нея)',
|
|
1262
|
-
doesEn: 'One folder or file per line; a folder brings everything beneath it.',
|
|
1263
|
-
doesBg: 'По една папка или файл на ред; папка включва всичко под нея.',
|
|
1264
|
-
},
|
|
1265
|
-
{
|
|
1266
|
-
key: 'mirror.exclude',
|
|
1267
|
-
kind: 'field',
|
|
1268
|
-
en: 'Exclude (one per line)',
|
|
1269
|
-
bg: 'Изключи (по една на ред)',
|
|
1270
|
-
doesEn: 'One per line: what to leave out even inside an included folder.',
|
|
1271
|
-
doesBg: 'По един на ред: какво да се изостави дори в включена папка.',
|
|
1272
|
-
},
|
|
1273
|
-
{
|
|
1274
|
-
key: 'tree.load',
|
|
1275
|
-
kind: 'button',
|
|
1276
|
-
en: 'Show the folders',
|
|
1277
|
-
bg: 'Покажи папките',
|
|
1278
|
-
doesEn: 'Reads the project folder and draws its tree, so folders can be ticked instead of typed.',
|
|
1279
|
-
doesBg: 'Прочита папката на проекта и показва дървото ѝ, за да се избират папки с клик, вместо да се пишат.',
|
|
1280
|
-
},
|
|
1281
|
-
{
|
|
1282
|
-
key: 'tree.reload',
|
|
1283
|
-
kind: 'button',
|
|
1284
|
-
en: 'Read the folders again',
|
|
1285
|
-
bg: 'Прочети папките отново',
|
|
1286
|
-
doesEn: 'Reads the folder tree again from disk.',
|
|
1287
|
-
doesBg: 'Прочита отново дървото с папки от диска.',
|
|
1288
|
-
},
|
|
1289
|
-
{
|
|
1290
|
-
key: 'tree.exclude',
|
|
1291
|
-
kind: 'button',
|
|
1292
|
-
en: 'carve this folder out',
|
|
1293
|
-
bg: 'изрежи тази папка',
|
|
1294
|
-
doesEn: 'The "−" on a tree row: carves that folder out of an included one.',
|
|
1295
|
-
doesBg: '„−“ на ред в дървото: изважда тази папка от включена.',
|
|
1296
|
-
},
|
|
1297
|
-
{
|
|
1298
|
-
key: 'tree.unexclude',
|
|
1299
|
-
kind: 'button',
|
|
1300
|
-
en: 'stop carving this folder out',
|
|
1301
|
-
bg: 'спри да изрязваш тази папка',
|
|
1302
|
-
doesEn: 'Puts a carved-out folder back.',
|
|
1303
|
-
doesBg: 'Връща изважданата папка обратно.',
|
|
1304
|
-
},
|
|
1305
|
-
{
|
|
1306
|
-
key: 'tree.fold',
|
|
1307
|
-
kind: 'button',
|
|
1308
|
-
en: 'fold',
|
|
1309
|
-
bg: 'сгъни',
|
|
1310
|
-
doesEn: 'Folds a tree row.',
|
|
1311
|
-
doesBg: 'Сгъва ред в дървото.',
|
|
1312
|
-
},
|
|
1313
|
-
{
|
|
1314
|
-
key: 'tree.unfold',
|
|
1315
|
-
kind: 'button',
|
|
1316
|
-
en: 'unfold',
|
|
1317
|
-
bg: 'разгъни',
|
|
1318
|
-
doesEn: 'Unfolds a tree row.',
|
|
1319
|
-
doesBg: 'Разгъва ред в дървото.',
|
|
1320
|
-
},
|
|
1321
|
-
{
|
|
1322
|
-
key: 'mirror.check',
|
|
1323
|
-
kind: 'button',
|
|
1324
|
-
en: 'Check what would be copied',
|
|
1325
|
-
bg: 'Провери какво ще се копира',
|
|
1326
|
-
doesEn: 'Counts what would be copied, without copying: files and size.',
|
|
1327
|
-
doesBg: 'Преброява какво би се копирало, без да копира: файлове и размер.',
|
|
1328
|
-
},
|
|
1329
|
-
{
|
|
1330
|
-
key: 'mirror.showFiles',
|
|
1331
|
-
kind: 'disclosure',
|
|
1332
|
-
en: 'show the list',
|
|
1333
|
-
bg: 'покажи списъка',
|
|
1334
|
-
doesEn: 'Under that count: the list of files.',
|
|
1335
|
-
doesBg: 'Под това преброяване: списъкът с файлове.',
|
|
1336
|
-
},
|
|
1337
|
-
{
|
|
1338
|
-
key: 'mirror.showSkipped',
|
|
1339
|
-
kind: 'disclosure',
|
|
1340
|
-
en: 'show what was left out',
|
|
1341
|
-
bg: 'покажи какво остава навън',
|
|
1342
|
-
doesEn: 'Under that count: what was left out, and why.',
|
|
1343
|
-
doesBg: 'Под това преброяване: какво е изоставено и защо.',
|
|
1344
|
-
},
|
|
1345
|
-
{
|
|
1346
|
-
key: 'mirror.gitignore',
|
|
1347
|
-
kind: 'checkbox',
|
|
1348
|
-
en: 'Leave out what .gitignore lists',
|
|
1349
|
-
bg: 'Не добавяй това, което е в .gitignore',
|
|
1350
|
-
doesEn: 'Keeps everything .gitignore names out of the files sent to the chat.',
|
|
1351
|
-
doesBg: 'Държи всичко от .gitignore извън файловете, пратени в чата.',
|
|
1352
|
-
},
|
|
1353
|
-
{
|
|
1354
|
-
key: 'mirror.env',
|
|
1355
|
-
kind: 'checkbox',
|
|
1356
|
-
en: 'Copy .env files as well',
|
|
1357
|
-
bg: 'Копирай и .env файловете',
|
|
1358
|
-
doesEn: 'Sends the .env files too; it asks for confirmation first, because they usually hold passwords and keys.',
|
|
1359
|
-
doesBg: 'Праща и .env файловете; първо иска потвърждение, защото в тях обикновено има пароли и ключове.',
|
|
1360
|
-
},
|
|
1361
|
-
{
|
|
1362
|
-
key: 'mirror.save',
|
|
1363
|
-
kind: 'button',
|
|
1364
|
-
en: 'Save',
|
|
1365
|
-
bg: 'Запази',
|
|
1366
|
-
doesEn: 'Stores the switch, the project root and both lists onto this session.',
|
|
1367
|
-
doesBg: 'Записва отметката, корена на проекта и двата списъка върху тази сесия.',
|
|
1368
|
-
},
|
|
1369
|
-
],
|
|
1370
|
-
},
|
|
1371
1244
|
{
|
|
1372
1245
|
headingKey: 'tasks.title',
|
|
1373
1246
|
en: 'Tasks',
|
|
@@ -1514,8 +1387,8 @@ export const SYSTEM_GUIDE = [
|
|
|
1514
1387
|
kind: 'button',
|
|
1515
1388
|
en: 'Run again from here',
|
|
1516
1389
|
bg: 'Пусни отново оттук',
|
|
1517
|
-
doesEn: 'Puts the code back, queues that task and every task after it — later sessions of its run included — and starts at once, in the mode that run had.',
|
|
1518
|
-
doesBg: 'Връща кода, нарежда отново тази задача и всички след нея — включително следващите сесии от пускането ѝ — и тръгва веднага, в режима на онова
|
|
1390
|
+
doesEn: 'Puts the code back, queues that task and every task after it — later sessions of its run included — and starts at once, in the mode that run had: step by step when that run left no record of its own, or went on its own and this machine no longer allows that.',
|
|
1391
|
+
doesBg: 'Връща кода, нарежда отново тази задача и всички след нея — включително следващите сесии от пускането ѝ — и тръгва веднага, в режима на онова пускане: стъпка по стъпка, когато то не е оставило запис за себе си или е вървяло самостоятелно, а тази машина вече не го позволява.',
|
|
1519
1392
|
},
|
|
1520
1393
|
{
|
|
1521
1394
|
key: 'task.delete',
|
|
@@ -2190,6 +2063,14 @@ export const SYSTEM_GUIDE = [
|
|
|
2190
2063
|
doesEn: 'The same button once the story is open: folds it away again.',
|
|
2191
2064
|
doesBg: 'Същият бутон, когато историята е отворена: сгъва я обратно.',
|
|
2192
2065
|
},
|
|
2066
|
+
{
|
|
2067
|
+
key: 'reg.rerunHere',
|
|
2068
|
+
kind: 'button',
|
|
2069
|
+
en: 'Run only this one again',
|
|
2070
|
+
bg: 'Пусни само тази отново',
|
|
2071
|
+
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.',
|
|
2072
|
+
doesBg: 'На ред, провален или блокиран по работата: казва какво става с чата, клона и записа, после пуска нов опит само за тази задача; останалите в опашката остават.',
|
|
2073
|
+
},
|
|
2193
2074
|
{
|
|
2194
2075
|
key: 'reg.continueHere',
|
|
2195
2076
|
kind: 'button',
|
|
@@ -2439,17 +2320,17 @@ export const SYSTEM_GUIDE = [
|
|
|
2439
2320
|
{
|
|
2440
2321
|
key: 'reg.pickAll',
|
|
2441
2322
|
kind: 'button',
|
|
2442
|
-
en: '
|
|
2443
|
-
bg: '
|
|
2323
|
+
en: 'Tick all',
|
|
2324
|
+
bg: 'Маркирай всички',
|
|
2444
2325
|
doesEn: 'In that panel: ticks every task.',
|
|
2445
2326
|
doesBg: 'В панела: отметва всички задачи.',
|
|
2446
2327
|
},
|
|
2447
2328
|
{
|
|
2448
2329
|
key: 'reg.pickNone',
|
|
2449
2330
|
kind: 'button',
|
|
2450
|
-
en: '
|
|
2451
|
-
bg: '
|
|
2452
|
-
doesEn: 'In that panel: unticks every task (the start buttons then do nothing until one is ticked).',
|
|
2331
|
+
en: 'Untick all',
|
|
2332
|
+
bg: 'Размаркирай всички',
|
|
2333
|
+
doesEn: 'In that panel: unticks every task, beside the count of ticked ones (the start buttons then do nothing until one is ticked).',
|
|
2453
2334
|
doesBg: 'В панела: маха всички отметки (бутоните за пускане тогава не действат, докато не се отметне поне една).',
|
|
2454
2335
|
},
|
|
2455
2336
|
{
|
|
@@ -2704,30 +2585,6 @@ export const SYSTEM_GUIDE = [
|
|
|
2704
2585
|
doesEn: 'Empties that field, so new sessions start with nothing chosen.',
|
|
2705
2586
|
doesBg: 'Изпразва полето, така че новите сесии тръгват без избрано.',
|
|
2706
2587
|
},
|
|
2707
|
-
{
|
|
2708
|
-
key: 'proj.mirrorToDesktop',
|
|
2709
|
-
kind: 'checkbox',
|
|
2710
|
-
en: 'Keep the projects on the Desktop',
|
|
2711
|
-
bg: 'Дръж проектите на Desktop-а',
|
|
2712
|
-
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.',
|
|
2713
|
-
doesBg: 'Главният ключ: копира проектите от тази страница в собствени папки на Desktop-а преди пускане и след всеки кръг стъпки — така файловете стигат до чата.',
|
|
2714
|
-
},
|
|
2715
|
-
{
|
|
2716
|
-
key: 'proj.desktopThis',
|
|
2717
|
-
kind: 'checkbox',
|
|
2718
|
-
en: 'Keep a copy of this project on the Desktop',
|
|
2719
|
-
bg: 'Дръж копие на този проект на Desktop-а',
|
|
2720
|
-
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.',
|
|
2721
|
-
doesBg: 'Под всеки проект, включено по подразбиране: махнете отметката, за да не се копира точно той, докато ключът е включен. Неактивно, докато ключът е изключен.',
|
|
2722
|
-
},
|
|
2723
|
-
{
|
|
2724
|
-
key: 'proj.folders',
|
|
2725
|
-
kind: 'disclosure',
|
|
2726
|
-
en: 'Folders that go to the Desktop',
|
|
2727
|
-
bg: 'Папки, които отиват на Desktop-а',
|
|
2728
|
-
doesEn: 'On each project: which of its folders go to the Desktop copy — the same include and exclude lists and folder tree as on a session page.',
|
|
2729
|
-
doesBg: 'На всеки проект: кои папки отиват в копието на Desktop-а — същите списъци за включване и изключване и дърво с папки като на страницата на сесия.',
|
|
2730
|
-
},
|
|
2731
2588
|
{
|
|
2732
2589
|
key: 'proj.affects',
|
|
2733
2590
|
kind: 'note',
|
|
@@ -169,7 +169,11 @@ export function parseReply(markdown, opts) {
|
|
|
169
169
|
reply.notes = stripCitations(reply.notes);
|
|
170
170
|
reply.summary = stripCitations(reply.summary);
|
|
171
171
|
const steps = reply.steps.map((s) => ({ ...s, shell: s.shell ?? opts.defaultShell }));
|
|
172
|
-
|
|
172
|
+
// A blank stop word is no stop word. The setting is free text, and every reply contains the
|
|
173
|
+
// empty string and nearly every one a space, so searching for either would read each
|
|
174
|
+
// `continue` that carries a summary as done and close the task after one round.
|
|
175
|
+
const marker = opts.stopMarker.trim();
|
|
176
|
+
const markerHit = marker !== '' && markdown.includes(marker);
|
|
173
177
|
const hasSummary = (reply.summary ?? '').trim().length > 0;
|
|
174
178
|
// The stop word alone is not enough to end a task any more: the summary is the
|
|
175
179
|
// deliverable, and a marker without one would let a task close with nothing to show.
|
|
@@ -145,7 +145,7 @@ export async function buildDebugExport(input) {
|
|
|
145
145
|
chat: session.chat ? { name: session.chat.name, url: session.chat.url } : undefined,
|
|
146
146
|
vcs: session.vcs,
|
|
147
147
|
vcsBaseCommit: session.vcsBaseCommit,
|
|
148
|
-
|
|
148
|
+
projectDir: session.projectDir,
|
|
149
149
|
tasks,
|
|
150
150
|
});
|
|
151
151
|
}
|
|
@@ -245,7 +245,7 @@ export async function buildExport(input) {
|
|
|
245
245
|
`chat : ${session.chat?.url ?? '(no conversation was opened)'}`,
|
|
246
246
|
`model : ${session.model || 'whatever the chat was set to'}${session.modelInUse ? ` (ran on ${session.modelInUse})` : ''}`,
|
|
247
247
|
`repository : ${session.vcs?.enabled
|
|
248
|
-
? `${session.vcs.repoDir || session.
|
|
248
|
+
? `${session.vcs.repoDir || session.projectDir || '(none set)'} — ${session.vcs.branchMode === 'per-session' ? 'one branch for the whole session' : 'a branch of its own for every task'}, prefix ${session.vcs.branchPrefix}${session.vcs.commitOnFinish ? '' : ', commits off'}`
|
|
249
249
|
: 'version control is off for this session'}`,
|
|
250
250
|
`tasks : ${tasks.length} of ${session.tasks.length}`,
|
|
251
251
|
`exported : ${when(new Date().toISOString())}`,
|
|
@@ -144,16 +144,12 @@ 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
|
-
|
|
150
|
-
enabled: s.mirror.enabled,
|
|
151
|
-
rootDir: s.mirror.rootDir,
|
|
152
|
-
includeDirs: s.mirror.includeDirs,
|
|
153
|
-
excludeDirs: s.mirror.excludeDirs,
|
|
154
|
-
respectGitignore: s.mirror.respectGitignore ?? true,
|
|
155
|
-
includeEnvFiles: s.mirror.includeEnvFiles ?? false,
|
|
156
|
-
},
|
|
152
|
+
...(s.projectDir ? { projectDir: s.projectDir } : {}),
|
|
157
153
|
tasks: tasks.map((t) => {
|
|
158
154
|
const p = splitPrompt(t.prompt);
|
|
159
155
|
const task = { title: t.title, prompt: p.prompt, expected: p.expected };
|
|
@@ -266,6 +262,54 @@ export function taskAtAttempt(task, attempt) {
|
|
|
266
262
|
export function withTask(session, task) {
|
|
267
263
|
return { ...session, tasks: session.tasks.map((t) => (t.id === task.id ? task : t)) };
|
|
268
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* The task as it ended in one run: the last of its attempts that ran in it, live or archived, or
|
|
267
|
+
* null when none did. Several attempts can share a run (a retry in a fresh chat is one); the last
|
|
268
|
+
* is how the run left it, with the ones before it as its history.
|
|
269
|
+
*/
|
|
270
|
+
export function taskInRun(task, runId) {
|
|
271
|
+
if (task.runGroup?.id === runId)
|
|
272
|
+
return task;
|
|
273
|
+
const attempts = task.attempts ?? [];
|
|
274
|
+
for (let i = attempts.length - 1; i >= 0; i -= 1) {
|
|
275
|
+
if (attempts[i]?.runGroup?.id === runId)
|
|
276
|
+
return taskAtAttempt(task, i + 1);
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* One run's sessions, with each task as it ended in that run, and only the tasks that were part of it.
|
|
282
|
+
*
|
|
283
|
+
* A run is a record of what happened, and the task on the record moves on after it: "run again"
|
|
284
|
+
* queues it as a new attempt, and another run may finish it. Taken as it is now, a task that
|
|
285
|
+
* failed in the run read as queued, its failure only among the earlier attempts, and the run's
|
|
286
|
+
* counts said nothing failed in it; once a later run had stamped it, the task was not in the
|
|
287
|
+
* first run's export at all. So each task is taken at its attempt in the run (`taskInRun`), and
|
|
288
|
+
* the three views describe the run whichever is asked for. A task the run was asked to do and
|
|
289
|
+
* never reached has no such attempt; it is still the run's, as it stands, through the run's own
|
|
290
|
+
* record on the session.
|
|
291
|
+
*/
|
|
292
|
+
export function runScope(sessions, runId, label) {
|
|
293
|
+
const chosen = new Map();
|
|
294
|
+
const asInRun = sessions.map((s) => {
|
|
295
|
+
let session = s;
|
|
296
|
+
const ids = new Set();
|
|
297
|
+
for (const t of s.tasks) {
|
|
298
|
+
const ran = taskInRun(t, runId);
|
|
299
|
+
if (ran) {
|
|
300
|
+
ids.add(t.id);
|
|
301
|
+
if (ran !== t)
|
|
302
|
+
session = withTask(session, ran);
|
|
303
|
+
}
|
|
304
|
+
else if (s.runGroup?.id === runId && s.runGroup.taskIds.includes(t.id)) {
|
|
305
|
+
ids.add(t.id);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
chosen.set(s.id, ids);
|
|
309
|
+
return session;
|
|
310
|
+
});
|
|
311
|
+
return { sessions: asInRun, taskFilter: (s, t) => !!chosen.get(s.id)?.has(t.id), label };
|
|
312
|
+
}
|
|
269
313
|
/** What the runner export says about the machine, from the configuration in force. */
|
|
270
314
|
export function exportMachine(cfg) {
|
|
271
315
|
return {
|
|
@@ -537,7 +581,6 @@ async function botTask(session, task, runsDir) {
|
|
|
537
581
|
fromReviews: task.reviewChecks,
|
|
538
582
|
},
|
|
539
583
|
processes: { leftovers: task.leftovers, reaped: events.filter((e) => e.type === 'processes-reaped').map(trimmed), notOurs: events.filter((e) => e.type === 'processes-not-ours').map(trimmed) },
|
|
540
|
-
mirror: events.filter((e) => String(e.type).startsWith('mirror')).map(trimmed),
|
|
541
584
|
review: {
|
|
542
585
|
verdict: task.review?.verdict,
|
|
543
586
|
rounds: task.review?.rounds,
|