@goodandready/dsh-goal 0.1.8 → 0.1.10
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 +121 -44
- package/{docs/README.ru.md → README.ru.md} +12 -1
- package/README.zh.md +219 -0
- package/docs/design/DESIGN.md +23 -0
- package/lib/client.js +146 -61
- package/lib/command-handler.js +76 -34
- package/lib/goal-engine.js +49 -5
- package/lib/index.js +344 -131
- package/package.json +6 -4
- package/docs/README.zh.md +0 -143
package/lib/client.js
CHANGED
|
@@ -789,14 +789,14 @@ window.__ModuleLoader__.load({
|
|
|
789
789
|
isCompleted
|
|
790
790
|
? React.createElement(IconCheck, { size: 18, className: 'cb-badge-ok' })
|
|
791
791
|
: React.createElement(IconTarget, { size: 18 }),
|
|
792
|
-
isCompleted ? (t('modalTitleCompleted') || '
|
|
792
|
+
isCompleted ? (t('modalTitleCompleted') || 'Goal completed: plan and results') : (t('modalTitle') || 'Goal plan and status'),
|
|
793
793
|
),
|
|
794
794
|
React.createElement(
|
|
795
795
|
'button',
|
|
796
796
|
{
|
|
797
797
|
className: 'dsh-goal-btn icon-only close',
|
|
798
798
|
onClick: onClose,
|
|
799
|
-
title: t('close') || '
|
|
799
|
+
title: t('close') || 'Close',
|
|
800
800
|
},
|
|
801
801
|
'✕',
|
|
802
802
|
),
|
|
@@ -817,39 +817,39 @@ window.__ModuleLoader__.load({
|
|
|
817
817
|
'div',
|
|
818
818
|
{ className: 'dsh-goal-stat-box' },
|
|
819
819
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedElapsed || '0s'),
|
|
820
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('time') || '
|
|
820
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('time') || 'Running time'),
|
|
821
821
|
),
|
|
822
822
|
React.createElement(
|
|
823
823
|
'div',
|
|
824
824
|
{ className: 'dsh-goal-stat-box' },
|
|
825
|
-
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedETA || (isCompleted ? '
|
|
826
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('eta') || '
|
|
825
|
+
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedETA || (isCompleted ? (t('done') || 'Done') : '—')),
|
|
826
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('eta') || 'ETA'),
|
|
827
827
|
),
|
|
828
828
|
React.createElement(
|
|
829
829
|
'div',
|
|
830
830
|
{ className: 'dsh-goal-stat-box', title: tokensTooltip },
|
|
831
831
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, tokensFormatted),
|
|
832
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('tokens') || '
|
|
832
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('tokens') || 'Tokens'),
|
|
833
833
|
),
|
|
834
834
|
React.createElement(
|
|
835
835
|
'div',
|
|
836
836
|
{ className: 'dsh-goal-stat-box' },
|
|
837
837
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, `${state.iterationsCount || 0}/${state.maxIterations || 25}`),
|
|
838
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, '
|
|
838
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('iterations') || 'Iterations'),
|
|
839
839
|
),
|
|
840
840
|
React.createElement(
|
|
841
841
|
'div',
|
|
842
842
|
{ className: 'dsh-goal-stat-box' },
|
|
843
843
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, `${state.progressPercent || 0}%`),
|
|
844
|
-
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, '
|
|
844
|
+
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('progress') || 'Progress'),
|
|
845
845
|
),
|
|
846
846
|
),
|
|
847
847
|
React.createElement(
|
|
848
848
|
'div',
|
|
849
849
|
{ style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 4 } },
|
|
850
|
-
React.createElement('div', { style: { fontWeight: 600, fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, t('milestones') || '
|
|
850
|
+
React.createElement('div', { style: { fontWeight: 600, fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, t('milestones') || 'Plan of work:'),
|
|
851
851
|
milestones.length === 0
|
|
852
|
-
? React.createElement('div', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12, padding: '8px 0' } }, t('noMilestones') || '
|
|
852
|
+
? React.createElement('div', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12, padding: '8px 0' } }, t('noMilestones') || 'Agent is preparing the plan of work...')
|
|
853
853
|
: milestones.map((m, i) =>
|
|
854
854
|
React.createElement(
|
|
855
855
|
'div',
|
|
@@ -1036,13 +1036,33 @@ window.__ModuleLoader__.load({
|
|
|
1036
1036
|
stateRef.current = state;
|
|
1037
1037
|
const lastStateRef = useRef(null);
|
|
1038
1038
|
|
|
1039
|
-
const
|
|
1039
|
+
const resolveLocale = () => {
|
|
1040
|
+
let active = 'en';
|
|
1040
1041
|
if (ctx?.locale?.getSnapshot) {
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1043
|
-
|
|
1042
|
+
const snapLoc = ctx.locale.getSnapshot();
|
|
1043
|
+
if (snapLoc?.active) {
|
|
1044
|
+
const code = snapLoc.active.toLowerCase();
|
|
1045
|
+
if (code.startsWith('zh')) active = 'zh';
|
|
1046
|
+
else if (code.startsWith('ru')) active = 'ru';
|
|
1047
|
+
else active = 'en';
|
|
1048
|
+
}
|
|
1049
|
+
} else if (typeof navigator !== 'undefined' && navigator.language) {
|
|
1050
|
+
const code = navigator.language.toLowerCase();
|
|
1051
|
+
if (code.startsWith('zh')) active = 'zh';
|
|
1052
|
+
else if (code.startsWith('ru')) active = 'ru';
|
|
1053
|
+
else active = 'en';
|
|
1044
1054
|
}
|
|
1045
|
-
|
|
1055
|
+
if (state?.lang) {
|
|
1056
|
+
if (state.lang === 'zh') active = 'zh';
|
|
1057
|
+
else if (state.lang === 'ru') active = 'ru';
|
|
1058
|
+
else if (state.lang === 'en') active = 'en';
|
|
1059
|
+
}
|
|
1060
|
+
return active;
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
const t = (key) => {
|
|
1064
|
+
const active = resolveLocale();
|
|
1065
|
+
return LOCALES[active]?.[key] || LOCALES.en?.[key] || LOCALES.ru?.[key] || key;
|
|
1046
1066
|
};
|
|
1047
1067
|
|
|
1048
1068
|
useEffect(() => {
|
|
@@ -1310,7 +1330,7 @@ window.__ModuleLoader__.load({
|
|
|
1310
1330
|
{
|
|
1311
1331
|
className: `dsh-goal-badge ${isCompleted ? 'dsh-goal-badge-ok' : isPaused ? 'dsh-goal-badge-warn' : ''}`,
|
|
1312
1332
|
},
|
|
1313
|
-
isCompleted ? (t('goalCompleted') || '
|
|
1333
|
+
isCompleted ? (t('goalCompleted') || 'Goal completed') : isPaused ? (t('paused') || 'Paused') : (t('goalLabel') || 'Current goal'),
|
|
1314
1334
|
),
|
|
1315
1335
|
React.createElement('span', { className: 'dsh-goal-title', title: state.title }, state.title),
|
|
1316
1336
|
),
|
|
@@ -1327,7 +1347,7 @@ window.__ModuleLoader__.load({
|
|
|
1327
1347
|
'button',
|
|
1328
1348
|
{
|
|
1329
1349
|
className: 'dsh-goal-btn icon-only',
|
|
1330
|
-
title: isPaused ? (t('resume') || '
|
|
1350
|
+
title: isPaused ? (t('resume') || 'Resume') : (t('pause') || 'Pause'),
|
|
1331
1351
|
onClick: () => handleAction(isPaused ? 'resume' : 'pause'),
|
|
1332
1352
|
},
|
|
1333
1353
|
isPaused ? React.createElement(IconPlay, { size: 14 }) : React.createElement(IconPause, { size: 14 }),
|
|
@@ -1337,7 +1357,7 @@ window.__ModuleLoader__.load({
|
|
|
1337
1357
|
'button',
|
|
1338
1358
|
{
|
|
1339
1359
|
className: 'dsh-goal-btn icon-only',
|
|
1340
|
-
title: t('details') || '
|
|
1360
|
+
title: t('details') || 'Expand goal details',
|
|
1341
1361
|
onClick: () => setIsModalOpen(true),
|
|
1342
1362
|
},
|
|
1343
1363
|
React.createElement(IconExpand, { size: 14 }),
|
|
@@ -1347,7 +1367,7 @@ window.__ModuleLoader__.load({
|
|
|
1347
1367
|
'button',
|
|
1348
1368
|
{
|
|
1349
1369
|
className: 'dsh-goal-btn icon-only close',
|
|
1350
|
-
title: t('closeBanner') || '
|
|
1370
|
+
title: t('closeBanner') || 'Close goal banner',
|
|
1351
1371
|
onClick: () => handleAction('clear'),
|
|
1352
1372
|
},
|
|
1353
1373
|
'✕',
|
|
@@ -1475,13 +1495,28 @@ window.__ModuleLoader__.load({
|
|
|
1475
1495
|
});
|
|
1476
1496
|
}, [scope]);
|
|
1477
1497
|
|
|
1478
|
-
const
|
|
1498
|
+
const resolveLocale = () => {
|
|
1499
|
+
let active = 'en';
|
|
1479
1500
|
if (ctx?.locale?.getSnapshot) {
|
|
1480
1501
|
const snapLoc = ctx.locale.getSnapshot();
|
|
1481
|
-
|
|
1482
|
-
|
|
1502
|
+
if (snapLoc?.active) {
|
|
1503
|
+
const code = snapLoc.active.toLowerCase();
|
|
1504
|
+
if (code.startsWith('zh')) active = 'zh';
|
|
1505
|
+
else if (code.startsWith('ru')) active = 'ru';
|
|
1506
|
+
else active = 'en';
|
|
1507
|
+
}
|
|
1508
|
+
} else if (typeof navigator !== 'undefined' && navigator.language) {
|
|
1509
|
+
const code = navigator.language.toLowerCase();
|
|
1510
|
+
if (code.startsWith('zh')) active = 'zh';
|
|
1511
|
+
else if (code.startsWith('ru')) active = 'ru';
|
|
1512
|
+
else active = 'en';
|
|
1483
1513
|
}
|
|
1484
|
-
return
|
|
1514
|
+
return active;
|
|
1515
|
+
};
|
|
1516
|
+
|
|
1517
|
+
const t = (key) => {
|
|
1518
|
+
const active = resolveLocale();
|
|
1519
|
+
return LOCALES[active]?.[key] || LOCALES.en[key] || LOCALES.ru?.[key] || key;
|
|
1485
1520
|
};
|
|
1486
1521
|
|
|
1487
1522
|
const status = (snap && snap.status) || 'ready';
|
|
@@ -1733,50 +1768,13 @@ window.__ModuleLoader__.load({
|
|
|
1733
1768
|
|
|
1734
1769
|
// --- СЛОВАРИ ЛОКАЛИЗАЦИИ ---
|
|
1735
1770
|
const LOCALES = {
|
|
1736
|
-
ru: {
|
|
1737
|
-
goalLabel: 'Текущая цель',
|
|
1738
|
-
goalCompleted: 'Цель выполнена',
|
|
1739
|
-
clearGoal: 'Очистить цель',
|
|
1740
|
-
closeBanner: 'Закрыть плашку цели',
|
|
1741
|
-
pause: 'Приостановить',
|
|
1742
|
-
resume: 'Возобновить',
|
|
1743
|
-
details: 'Развернуть детали цели',
|
|
1744
|
-
modalTitle: 'План и статус цели',
|
|
1745
|
-
modalTitleCompleted: 'Цель выполнена: план и результаты',
|
|
1746
|
-
time: 'Время работы',
|
|
1747
|
-
eta: 'Оценка (ETA)',
|
|
1748
|
-
tokens: 'Токены',
|
|
1749
|
-
milestones: 'План работ:',
|
|
1750
|
-
noMilestones: 'Агент формирует план работ...',
|
|
1751
|
-
close: 'Закрыть',
|
|
1752
|
-
pluginTitle: 'Цели и автономный режим (Goal Mode)',
|
|
1753
|
-
pluginDesc: 'Панель цели над полем ввода, декомпозиция вех и авто-драйв',
|
|
1754
|
-
maxIterLabel: 'Максимум итераций (Safety Limit):',
|
|
1755
|
-
autoDriveLabel: 'Авто-драйв: продолжать цикл автоматически',
|
|
1756
|
-
soundLabel: 'Звук по завершении цели',
|
|
1757
|
-
quickLaunchLabel: 'Кнопка быстрого запуска цели над полем ввода',
|
|
1758
|
-
quickLaunch: 'Запустить цель',
|
|
1759
|
-
quickLaunchTitle: 'Быстрый запуск цели',
|
|
1760
|
-
quickLaunchDesc: 'Сформулируйте задачу для агента в автономном режиме:',
|
|
1761
|
-
quickLaunchPlaceholder: 'Например: Реализовать экспорт отчетов и покрыть тестами...',
|
|
1762
|
-
startGoal: 'Запустить цель',
|
|
1763
|
-
copyReport: '📋 Скопировать отчёт в Markdown',
|
|
1764
|
-
reportCopied: '✅ Скопировано!',
|
|
1765
|
-
save: 'Сохранить',
|
|
1766
|
-
saving: 'Сохранение…',
|
|
1767
|
-
discard: 'Отменить правки',
|
|
1768
|
-
resetField: 'По умолчанию',
|
|
1769
|
-
overridden: 'изменено',
|
|
1770
|
-
saveFailed: 'Не сохранено — исправьте и повторите',
|
|
1771
|
-
loading: 'Загрузка настроек…',
|
|
1772
|
-
unavailable: 'Настройки недоступны',
|
|
1773
|
-
},
|
|
1774
1771
|
en: {
|
|
1775
1772
|
goalLabel: 'Current goal',
|
|
1776
1773
|
goalCompleted: 'Goal completed',
|
|
1777
1774
|
clearGoal: 'Clear goal',
|
|
1778
1775
|
closeBanner: 'Close goal banner',
|
|
1779
1776
|
pause: 'Pause',
|
|
1777
|
+
paused: 'Paused',
|
|
1780
1778
|
resume: 'Resume',
|
|
1781
1779
|
details: 'Expand Goal Details',
|
|
1782
1780
|
modalTitle: 'Goal Plan & Status',
|
|
@@ -1784,6 +1782,9 @@ window.__ModuleLoader__.load({
|
|
|
1784
1782
|
time: 'Running time',
|
|
1785
1783
|
eta: 'ETA',
|
|
1786
1784
|
tokens: 'Tokens',
|
|
1785
|
+
iterations: 'Iterations',
|
|
1786
|
+
progress: 'Progress',
|
|
1787
|
+
done: 'Done',
|
|
1787
1788
|
milestones: 'Plan of work:',
|
|
1788
1789
|
noMilestones: 'Agent is preparing the plan of work...',
|
|
1789
1790
|
close: 'Close',
|
|
@@ -1809,6 +1810,90 @@ window.__ModuleLoader__.load({
|
|
|
1809
1810
|
loading: 'Loading settings…',
|
|
1810
1811
|
unavailable: 'Settings unavailable',
|
|
1811
1812
|
},
|
|
1813
|
+
zh: {
|
|
1814
|
+
goalLabel: '当前目标',
|
|
1815
|
+
goalCompleted: '目标已完成',
|
|
1816
|
+
clearGoal: '清除目标',
|
|
1817
|
+
closeBanner: '关闭目标横幅',
|
|
1818
|
+
pause: '暂停',
|
|
1819
|
+
paused: '已暂停',
|
|
1820
|
+
resume: '恢复',
|
|
1821
|
+
details: '展开目标详情',
|
|
1822
|
+
modalTitle: '目标计划与状态',
|
|
1823
|
+
modalTitleCompleted: '目标已完成:计划与成果',
|
|
1824
|
+
time: '运行时间',
|
|
1825
|
+
eta: '预估剩余 (ETA)',
|
|
1826
|
+
tokens: 'Token 消耗',
|
|
1827
|
+
iterations: '迭代次数',
|
|
1828
|
+
progress: '进度',
|
|
1829
|
+
done: '已完成',
|
|
1830
|
+
milestones: '工作计划:',
|
|
1831
|
+
noMilestones: '智能体正在制定工作计划...',
|
|
1832
|
+
close: '关闭',
|
|
1833
|
+
pluginTitle: '目标模式与自主循环 (Goal Mode)',
|
|
1834
|
+
pluginDesc: '输入框上方常驻目标横幅、里程碑拆解与自主循环',
|
|
1835
|
+
maxIterLabel: '最大迭代次数 (Safety Limit):',
|
|
1836
|
+
autoDriveLabel: '自动执行:在各轮次间自动保持循环',
|
|
1837
|
+
soundLabel: '目标完成时播放提示音',
|
|
1838
|
+
quickLaunchLabel: '在输入框上方显示快速启动目标按钮',
|
|
1839
|
+
quickLaunch: '启动目标',
|
|
1840
|
+
quickLaunchTitle: '快速启动目标',
|
|
1841
|
+
quickLaunchDesc: '为自主模式下的智能体设定任务目标:',
|
|
1842
|
+
quickLaunchPlaceholder: '例如:实现报表导出功能并编写单元测试...',
|
|
1843
|
+
startGoal: '启动目标',
|
|
1844
|
+
copyReport: '📋 复制 Markdown 报告',
|
|
1845
|
+
reportCopied: '✅ 已复制!',
|
|
1846
|
+
save: '保存',
|
|
1847
|
+
saving: '保存中…',
|
|
1848
|
+
discard: '放弃更改',
|
|
1849
|
+
resetField: '恢复默认',
|
|
1850
|
+
overridden: '已修改',
|
|
1851
|
+
saveFailed: '保存失败 — 请检查并重试',
|
|
1852
|
+
loading: '正在加载设置…',
|
|
1853
|
+
unavailable: '设置不可用',
|
|
1854
|
+
},
|
|
1855
|
+
ru: {
|
|
1856
|
+
goalLabel: 'Текущая цель',
|
|
1857
|
+
goalCompleted: 'Цель выполнена',
|
|
1858
|
+
clearGoal: 'Очистить цель',
|
|
1859
|
+
closeBanner: 'Закрыть плашку цели',
|
|
1860
|
+
pause: 'Приостановить',
|
|
1861
|
+
paused: 'На паузе',
|
|
1862
|
+
resume: 'Возобновить',
|
|
1863
|
+
details: 'Развернуть детали цели',
|
|
1864
|
+
modalTitle: 'План и статус цели',
|
|
1865
|
+
modalTitleCompleted: 'Цель выполнена: план и результаты',
|
|
1866
|
+
time: 'Время работы',
|
|
1867
|
+
eta: 'Оценка (ETA)',
|
|
1868
|
+
tokens: 'Токены',
|
|
1869
|
+
iterations: 'Итерации',
|
|
1870
|
+
progress: 'Прогресс',
|
|
1871
|
+
done: 'Готово',
|
|
1872
|
+
milestones: 'План работ:',
|
|
1873
|
+
noMilestones: 'Агент формирует план работ...',
|
|
1874
|
+
close: 'Закрыть',
|
|
1875
|
+
pluginTitle: 'Цели и автономный режим (Goal Mode)',
|
|
1876
|
+
pluginDesc: 'Панель цели над полем ввода, декомпозиция вех и авто-драйв',
|
|
1877
|
+
maxIterLabel: 'Максимум итераций (Safety Limit):',
|
|
1878
|
+
autoDriveLabel: 'Авто-драйв: продолжать цикл автоматически',
|
|
1879
|
+
soundLabel: 'Звук по завершении цели',
|
|
1880
|
+
quickLaunchLabel: 'Кнопка быстрого запуска цели над полем ввода',
|
|
1881
|
+
quickLaunch: 'Запустить цель',
|
|
1882
|
+
quickLaunchTitle: 'Быстрый запуск цели',
|
|
1883
|
+
quickLaunchDesc: 'Сформулируйте задачу для агента в автономном режиме:',
|
|
1884
|
+
quickLaunchPlaceholder: 'Например: Реализовать экспорт отчетов и покрыть тестами...',
|
|
1885
|
+
startGoal: 'Запустить цель',
|
|
1886
|
+
copyReport: '📋 Скопировать отчёт в Markdown',
|
|
1887
|
+
reportCopied: '✅ Скопировано!',
|
|
1888
|
+
save: 'Сохранить',
|
|
1889
|
+
saving: 'Сохранение…',
|
|
1890
|
+
discard: 'Отменить правки',
|
|
1891
|
+
resetField: 'По умолчанию',
|
|
1892
|
+
overridden: 'изменено',
|
|
1893
|
+
saveFailed: 'Не сохранено — исправьте и повторите',
|
|
1894
|
+
loading: 'Загрузка настроек…',
|
|
1895
|
+
unavailable: 'Настройки недоступны',
|
|
1896
|
+
},
|
|
1812
1897
|
};
|
|
1813
1898
|
|
|
1814
1899
|
module.exports.inject = ['slots', 'locale', 'settingsScope'];
|
package/lib/command-handler.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { GoalState } from './goal-engine.js';
|
|
2
|
+
import { GoalState, detectLanguage } from './goal-engine.js';
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const USAGE_RU = 'Использование: /goal [<цель>|clear|pause|resume]';
|
|
5
|
+
export const USAGE_EN = 'Usage: /goal [<goal>|clear|pause|resume]';
|
|
6
|
+
export const USAGE = USAGE_RU;
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Парсинг строки ввода для команды /goal
|
|
@@ -39,15 +41,26 @@ export function createGoalUserMessage(text) {
|
|
|
39
41
|
/**
|
|
40
42
|
* Формирование стартового промпта с жестким требованием составить план работ
|
|
41
43
|
* @param {string} goalText
|
|
44
|
+
* @param {string} [explicitLang]
|
|
42
45
|
* @returns {string}
|
|
43
46
|
*/
|
|
44
|
-
export function formatGoalStartPrompt(goalText) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
export function formatGoalStartPrompt(goalText, explicitLang) {
|
|
48
|
+
const lang = explicitLang || detectLanguage(goalText);
|
|
49
|
+
if (lang === 'ru') {
|
|
50
|
+
return `🎯 Активирован режим цели (Goal Mode): "${goalText}"\n\n` +
|
|
51
|
+
`СТРОГИЙ КОНТРАКТ АВТОНОМНОГО РЕЖИМА:\n` +
|
|
52
|
+
`1. ОБЯЗАТЕЛЬНЫЙ ШАГ №1: Твоим ПЕРВЫМ действием ДО выполнения любой другой работы или ответа должен быть вызов инструмента goal_set_milestones с массивом из 3-7 конкретных, последовательных пунктов плана работ. Пользователь видит этот план в интерфейсе в реальном времени.\n` +
|
|
53
|
+
`2. ШАГ №2: После установки плана переходи к выполнению шагов. Перед началом работы над каждым шагом отметь его статус как in_progress через goal_update_progress(milestone_id, "in_progress").\n` +
|
|
54
|
+
`3. ШАГ №3: После успешного завершения шага отметь его статус как completed через goal_update_progress(milestone_id, "completed", "краткие итоги шага").\n` +
|
|
55
|
+
`4. ШАГ №4: Когда все пункты плана выполнены, вызови инструмент goal_finish(summary) с подробным резюме достигнутых результатов.`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return `🎯 Goal Mode activated: "${goalText}"\n\n` +
|
|
59
|
+
`STRICT AUTONOMOUS CONTRACT:\n` +
|
|
60
|
+
`1. MANDATORY STEP 1: Your FIRST action BEFORE doing any other work or reply MUST be calling tool goal_set_milestones with an array of 3-7 concrete, sequential milestones. The user sees this plan in real time.\n` +
|
|
61
|
+
`2. STEP 2: After establishing the plan, proceed to execute milestones. Before starting each milestone, mark it as in_progress via goal_update_progress(milestone_id, "in_progress").\n` +
|
|
62
|
+
`3. STEP 3: Upon completing a milestone, mark it as completed via goal_update_progress(milestone_id, "completed", "brief milestone outcome").\n` +
|
|
63
|
+
`4. STEP 4: When all milestones are completed, call tool goal_finish(summary) with a comprehensive summary of achieved results.`;
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
/**
|
|
@@ -63,33 +76,49 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
63
76
|
const sid = sessionId || 'default';
|
|
64
77
|
const snap = engine.getSnapshot(sid);
|
|
65
78
|
|
|
79
|
+
const lang = (parsed.text ? detectLanguage(parsed.text) : (snap.lang || (snap.title ? detectLanguage(snap.title) : 'en'))) || 'en';
|
|
80
|
+
const isRu = lang === 'ru';
|
|
81
|
+
const usage = isRu ? USAGE_RU : USAGE_EN;
|
|
82
|
+
|
|
66
83
|
switch (parsed.action) {
|
|
67
84
|
case 'show': {
|
|
68
85
|
if (!snap.hasActiveGoal) {
|
|
69
86
|
return {
|
|
70
87
|
kind: 'success',
|
|
71
|
-
text:
|
|
88
|
+
text: isRu
|
|
89
|
+
? `🎯 Режим цели (Goal Mode): цель не установлена.\n${usage}`
|
|
90
|
+
: `🎯 Goal Mode: no active goal set.\n${usage}`,
|
|
72
91
|
};
|
|
73
92
|
}
|
|
74
93
|
|
|
75
94
|
const milestonesInfo = snap.milestones.length > 0
|
|
76
|
-
? `\nПлан работ:\n` + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
|
|
95
|
+
? (isRu ? `\nПлан работ:\n` : `\nWork plan:\n`) + snap.milestones.map((m, i) => ` ${i + 1}. [${m.status}] ${m.title}`).join('\n')
|
|
77
96
|
: '';
|
|
78
97
|
|
|
79
98
|
const etaText = snap.formattedETA ? ` (ETA: ${snap.formattedETA})` : '';
|
|
80
99
|
const tokensInfo = (snap.tokensUsage && snap.tokensUsage.totalTokens > 0)
|
|
81
|
-
?
|
|
100
|
+
? (isRu
|
|
101
|
+
? `\nТокены: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`
|
|
102
|
+
: `\nTokens: ${snap.tokensUsage.totalTokens.toLocaleString()} (in: ${snap.tokensUsage.promptTokens.toLocaleString()}, out: ${snap.tokensUsage.completionTokens.toLocaleString()})`)
|
|
82
103
|
: '';
|
|
83
104
|
|
|
84
105
|
return {
|
|
85
106
|
kind: 'success',
|
|
86
|
-
text:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
107
|
+
text: isRu
|
|
108
|
+
? `🎯 Цель: «${snap.title}»\n` +
|
|
109
|
+
`Статус: ${snap.state}\n` +
|
|
110
|
+
`Время: ${snap.formattedElapsed}${etaText}\n` +
|
|
111
|
+
`Итераций: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
112
|
+
tokensInfo +
|
|
113
|
+
milestonesInfo +
|
|
114
|
+
`\n\nКоманды: /goal pause, /goal resume, /goal clear, /goal <новая цель>`
|
|
115
|
+
: `🎯 Goal: "${snap.title}"\n` +
|
|
116
|
+
`Status: ${snap.state}\n` +
|
|
117
|
+
`Duration: ${snap.formattedElapsed}${etaText}\n` +
|
|
118
|
+
`Iterations: ${snap.iterationsCount}/${snap.maxIterations}` +
|
|
119
|
+
tokensInfo +
|
|
120
|
+
milestonesInfo +
|
|
121
|
+
`\n\nCommands: /goal pause, /goal resume, /goal clear, /goal <new goal>`,
|
|
93
122
|
};
|
|
94
123
|
}
|
|
95
124
|
|
|
@@ -97,7 +126,7 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
97
126
|
if (!snap.hasActiveGoal) {
|
|
98
127
|
return {
|
|
99
128
|
kind: 'success',
|
|
100
|
-
text: 'Цель не была установлена.',
|
|
129
|
+
text: isRu ? 'Цель не была установлена.' : 'No active goal set.',
|
|
101
130
|
};
|
|
102
131
|
}
|
|
103
132
|
engine.clear(sid);
|
|
@@ -110,7 +139,7 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
110
139
|
}
|
|
111
140
|
return {
|
|
112
141
|
kind: 'success',
|
|
113
|
-
text: '🎯 Цель сброшена.',
|
|
142
|
+
text: isRu ? '🎯 Цель сброшена.' : '🎯 Goal cleared.',
|
|
114
143
|
};
|
|
115
144
|
}
|
|
116
145
|
|
|
@@ -118,16 +147,18 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
118
147
|
if (!snap.hasActiveGoal) {
|
|
119
148
|
return {
|
|
120
149
|
kind: 'error',
|
|
121
|
-
text:
|
|
150
|
+
text: isRu
|
|
151
|
+
? `Нельзя приостановить: активная цель отсутствует.\n${usage}`
|
|
152
|
+
: `Cannot pause: no active goal.\n${usage}`,
|
|
122
153
|
};
|
|
123
154
|
}
|
|
124
155
|
if (snap.state === GoalState.PAUSED) {
|
|
125
156
|
return {
|
|
126
157
|
kind: 'success',
|
|
127
|
-
text: '⏸ Цель уже на паузе.',
|
|
158
|
+
text: isRu ? '⏸ Цель уже на паузе.' : '⏸ Goal is already paused.',
|
|
128
159
|
};
|
|
129
160
|
}
|
|
130
|
-
engine.pause('Пауза по команде пользователя (/goal pause)', sid);
|
|
161
|
+
engine.pause(isRu ? 'Пауза по команде пользователя (/goal pause)' : 'Paused by user command (/goal pause)', sid);
|
|
131
162
|
if (agent && typeof agent.cancel === 'function') {
|
|
132
163
|
try {
|
|
133
164
|
agent.cancel({ kind: 'user', reason: 'Goal paused by user' });
|
|
@@ -137,7 +168,9 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
137
168
|
}
|
|
138
169
|
return {
|
|
139
170
|
kind: 'success',
|
|
140
|
-
text:
|
|
171
|
+
text: isRu
|
|
172
|
+
? `⏸ Цель приостановлена: «${snap.title}». Работа агента остановлена. Используйте /goal resume или кнопку ▶️ для продолжения.`
|
|
173
|
+
: `⏸ Goal paused: "${snap.title}". Agent stopped. Use /goal resume or ▶️ button to continue.`,
|
|
141
174
|
};
|
|
142
175
|
}
|
|
143
176
|
|
|
@@ -145,19 +178,23 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
145
178
|
if (!snap.hasActiveGoal) {
|
|
146
179
|
return {
|
|
147
180
|
kind: 'error',
|
|
148
|
-
text:
|
|
181
|
+
text: isRu
|
|
182
|
+
? `Нельзя возобновить: активная цель отсутствует.\n${usage}`
|
|
183
|
+
: `Cannot resume: no active goal.\n${usage}`,
|
|
149
184
|
};
|
|
150
185
|
}
|
|
151
186
|
if (snap.state === GoalState.RUNNING) {
|
|
152
187
|
return {
|
|
153
188
|
kind: 'success',
|
|
154
|
-
text: '▶️ Цель уже выполняется.',
|
|
189
|
+
text: isRu ? '▶️ Цель уже выполняется.' : '▶️ Goal is already running.',
|
|
155
190
|
};
|
|
156
191
|
}
|
|
157
192
|
engine.resume(sid);
|
|
158
193
|
if (agent && typeof agent.followup === 'function') {
|
|
159
194
|
try {
|
|
160
|
-
const resumePrompt =
|
|
195
|
+
const resumePrompt = isRu
|
|
196
|
+
? `▶️ Цель возобновлена пользователем. Продолжай выполнение плана работ с того места, где остановился. Отмечай шаги через goal_update_progress.`
|
|
197
|
+
: `▶️ Goal resumed by user. Continue executing the work plan from where you stopped. Update steps via goal_update_progress.`;
|
|
161
198
|
agent.followup(createGoalUserMessage(resumePrompt));
|
|
162
199
|
} catch (err) {
|
|
163
200
|
console.warn('[dsh-goal] Failed to followup agent on resume:', err);
|
|
@@ -165,18 +202,20 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
165
202
|
}
|
|
166
203
|
return {
|
|
167
204
|
kind: 'success',
|
|
168
|
-
text:
|
|
205
|
+
text: isRu
|
|
206
|
+
? `▶️ Цель возобновлена: «${snap.title}». Агент продолжает работу.`
|
|
207
|
+
: `▶️ Goal resumed: "${snap.title}". Agent continues working.`,
|
|
169
208
|
};
|
|
170
209
|
}
|
|
171
210
|
|
|
172
211
|
case 'start': {
|
|
173
212
|
const maxIterations = config.maxIterations ?? snap.maxIterations ?? 25;
|
|
174
|
-
const newSnap = engine.startGoal(parsed.text, { maxIterations }, sid);
|
|
213
|
+
const newSnap = engine.startGoal(parsed.text, { maxIterations, lang }, sid);
|
|
175
214
|
|
|
176
215
|
// Если команда вызвана в контексте агента, отправляем цель в очередь модели
|
|
177
216
|
if (agent && typeof agent.followup === 'function') {
|
|
178
217
|
try {
|
|
179
|
-
const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text));
|
|
218
|
+
const userMsg = createGoalUserMessage(formatGoalStartPrompt(parsed.text, lang));
|
|
180
219
|
agent.followup(userMsg);
|
|
181
220
|
} catch (err) {
|
|
182
221
|
console.warn('[dsh-goal] Failed to dispatch goal to agent followup:', err);
|
|
@@ -185,15 +224,18 @@ export function executeGoalSlashCommand(engine, parsed, config = {}, agent = nul
|
|
|
185
224
|
|
|
186
225
|
return {
|
|
187
226
|
kind: 'success',
|
|
188
|
-
text:
|
|
189
|
-
|
|
227
|
+
text: isRu
|
|
228
|
+
? `🎯 Активирована цель: «${newSnap.title}» (макс. ${newSnap.maxIterations} итераций).\n` +
|
|
229
|
+
`Для паузы: /goal pause | Для сброса: /goal clear`
|
|
230
|
+
: `🎯 Goal activated: "${newSnap.title}" (max ${newSnap.maxIterations} iterations).\n` +
|
|
231
|
+
`To pause: /goal pause | To clear: /goal clear`,
|
|
190
232
|
};
|
|
191
233
|
}
|
|
192
234
|
|
|
193
235
|
default:
|
|
194
236
|
return {
|
|
195
237
|
kind: 'error',
|
|
196
|
-
text: `Неизвестная команда. ${
|
|
238
|
+
text: isRu ? `Неизвестная команда. ${usage}` : `Unknown command. ${usage}`,
|
|
197
239
|
};
|
|
198
240
|
}
|
|
199
241
|
}
|