claude-controller 0.2.0 → 0.3.0

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.
Files changed (68) hide show
  1. package/README.md +2 -2
  2. package/bin/autoloop.sh +382 -0
  3. package/bin/ctl +327 -5
  4. package/bin/native-app.py +5 -2
  5. package/bin/watchdog.sh +357 -0
  6. package/cognitive/__init__.py +14 -0
  7. package/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  8. package/cognitive/__pycache__/dispatcher.cpython-314.pyc +0 -0
  9. package/cognitive/__pycache__/evaluator.cpython-314.pyc +0 -0
  10. package/cognitive/__pycache__/goal_engine.cpython-314.pyc +0 -0
  11. package/cognitive/__pycache__/learning.cpython-314.pyc +0 -0
  12. package/cognitive/__pycache__/orchestrator.cpython-314.pyc +0 -0
  13. package/cognitive/__pycache__/planner.cpython-314.pyc +0 -0
  14. package/cognitive/dispatcher.py +192 -0
  15. package/cognitive/evaluator.py +289 -0
  16. package/cognitive/goal_engine.py +232 -0
  17. package/cognitive/learning.py +189 -0
  18. package/cognitive/orchestrator.py +303 -0
  19. package/cognitive/planner.py +207 -0
  20. package/cognitive/prompts/analyst.md +31 -0
  21. package/cognitive/prompts/coder.md +22 -0
  22. package/cognitive/prompts/reviewer.md +33 -0
  23. package/cognitive/prompts/tester.md +21 -0
  24. package/cognitive/prompts/writer.md +25 -0
  25. package/config.sh +6 -1
  26. package/dag/__init__.py +5 -0
  27. package/dag/__pycache__/__init__.cpython-314.pyc +0 -0
  28. package/dag/__pycache__/graph.cpython-314.pyc +0 -0
  29. package/dag/graph.py +222 -0
  30. package/lib/jobs.sh +12 -1
  31. package/package.json +5 -1
  32. package/postinstall.sh +1 -1
  33. package/service/controller.sh +43 -11
  34. package/web/audit.py +122 -0
  35. package/web/checkpoint.py +80 -0
  36. package/web/config.py +2 -5
  37. package/web/handler.py +464 -26
  38. package/web/handler_fs.py +15 -14
  39. package/web/handler_goals.py +203 -0
  40. package/web/handler_jobs.py +165 -42
  41. package/web/handler_memory.py +203 -0
  42. package/web/jobs.py +576 -12
  43. package/web/personas.py +419 -0
  44. package/web/pipeline.py +682 -50
  45. package/web/presets.py +506 -0
  46. package/web/projects.py +58 -4
  47. package/web/static/api.js +90 -3
  48. package/web/static/app.js +8 -0
  49. package/web/static/base.css +51 -12
  50. package/web/static/context.js +14 -4
  51. package/web/static/form.css +3 -2
  52. package/web/static/goals.css +363 -0
  53. package/web/static/goals.js +300 -0
  54. package/web/static/i18n.js +288 -0
  55. package/web/static/index.html +142 -6
  56. package/web/static/jobs.css +951 -4
  57. package/web/static/jobs.js +890 -54
  58. package/web/static/memoryview.js +117 -0
  59. package/web/static/personas.js +228 -0
  60. package/web/static/pipeline.css +308 -1
  61. package/web/static/pipelines.js +249 -14
  62. package/web/static/presets.js +244 -0
  63. package/web/static/send.js +26 -4
  64. package/web/static/settings-style.css +34 -3
  65. package/web/static/settings.js +37 -1
  66. package/web/static/stream.js +242 -19
  67. package/web/static/utils.js +54 -2
  68. package/web/webhook.py +210 -0
@@ -0,0 +1,300 @@
1
+ /* ═══════════════════════════════════════════════
2
+ Goals — 목표 관리 UI (CRUD + DAG 트리 뷰 + Gate 승인)
3
+ ═══════════════════════════════════════════════ */
4
+
5
+ let _goalFilter = 'all';
6
+ let _goalsCache = [];
7
+ let _goalPollTimer = null;
8
+
9
+ /* ── Status helpers ── */
10
+ const _GOAL_STATUS_ICON = {
11
+ pending: 'pending', planning: 'planning', ready: 'ready',
12
+ running: 'running', gate_waiting: 'gate_waiting', evaluating: 'evaluating',
13
+ completed: 'completed', failed: 'failed', cancelled: 'cancelled',
14
+ };
15
+
16
+ function _isActiveGoal(g) {
17
+ return !['completed', 'failed', 'cancelled'].includes(g.status);
18
+ }
19
+
20
+ /* ── Fetch & Render ── */
21
+ async function loadGoals() {
22
+ try {
23
+ const data = await fetchGoals();
24
+ _goalsCache = Array.isArray(data) ? data : (data.goals || []);
25
+ renderGoals();
26
+ _updateGoalCount();
27
+ _startGoalPoll();
28
+ } catch (e) {
29
+ const list = document.getElementById('goalList');
30
+ if (list) list.innerHTML = `<div class="goal-empty">${t('msg_goal_failed')}: ${e.message}</div>`;
31
+ }
32
+ }
33
+
34
+ function _updateGoalCount() {
35
+ const el = document.getElementById('goalCount');
36
+ if (!el) return;
37
+ const active = _goalsCache.filter(_isActiveGoal).length;
38
+ el.textContent = active > 0 ? `${active}` : '';
39
+ }
40
+
41
+ function renderGoals() {
42
+ const list = document.getElementById('goalList');
43
+ if (!list) return;
44
+
45
+ const filtered = _goalFilter === 'all' ? _goalsCache
46
+ : _goalFilter === 'active' ? _goalsCache.filter(_isActiveGoal)
47
+ : _goalFilter === 'done' ? _goalsCache.filter(g => g.status === 'completed')
48
+ : _goalsCache.filter(g => g.status === 'cancelled');
49
+
50
+ if (filtered.length === 0) {
51
+ list.innerHTML = `<div class="goal-empty">${t('goal_no_goals')}</div>`;
52
+ return;
53
+ }
54
+
55
+ list.innerHTML = filtered.map(g => _renderGoalCard(g)).join('');
56
+ }
57
+
58
+ function _renderGoalCard(g) {
59
+ const statusLabel = t(`goal_status_${g.status}`) || g.status;
60
+ const modeLabel = t(`goal_mode_${g.mode}`) || g.mode;
61
+ const progress = _calcProgress(g);
62
+ const progressClass = g.status === 'completed' ? 'done' : g.status === 'failed' ? 'failed' : '';
63
+ const created = g.created_at ? _timeAgo(g.created_at) : '';
64
+ const budget = g.budget_usd != null ? `$${Number(g.budget_usd).toFixed(2)}` : '';
65
+
66
+ let actions = '';
67
+ if (g.status === 'gate_waiting') {
68
+ actions += `<button class="btn btn-sm btn-primary" onclick="event.stopPropagation();onApproveGoal('${g.id}')">${t('goal_approve')}</button>`;
69
+ }
70
+ if (_isActiveGoal(g)) {
71
+ actions += `<button class="btn btn-sm" onclick="event.stopPropagation();onCancelGoal('${g.id}')" title="${t('goal_cancel')}"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>`;
72
+ }
73
+
74
+ return `<div class="goal-card" id="goal-${g.id}" onclick="toggleGoalDetail('${g.id}')">
75
+ <div class="goal-card-header">
76
+ <span class="goal-card-status ${g.status}"></span>
77
+ <div class="goal-card-body">
78
+ <div class="goal-card-objective">${_esc(g.objective)}</div>
79
+ <div class="goal-card-meta">
80
+ <span class="goal-mode-badge">${modeLabel}</span>
81
+ <span class="goal-status-label">${statusLabel}</span>
82
+ ${budget ? `<span>${budget}</span>` : ''}
83
+ ${created ? `<span>${created}</span>` : ''}
84
+ </div>
85
+ ${progress >= 0 ? `<div class="goal-progress-bar"><div class="goal-progress-fill ${progressClass}" style="width:${progress}%"></div></div>` : ''}
86
+ </div>
87
+ <div class="goal-card-actions">${actions}</div>
88
+ </div>
89
+ <div class="goal-detail" id="goal-detail-${g.id}"></div>
90
+ </div>`;
91
+ }
92
+
93
+ function _calcProgress(g) {
94
+ const dag = g.dag || g.tasks;
95
+ if (!dag) return -1;
96
+ const tasks = Array.isArray(dag) ? dag : (dag.nodes || []);
97
+ if (tasks.length === 0) return 0;
98
+ const done = tasks.filter(t => t.status === 'completed').length;
99
+ return Math.round((done / tasks.length) * 100);
100
+ }
101
+
102
+ /* ── Detail toggle ── */
103
+ async function toggleGoalDetail(id) {
104
+ const card = document.getElementById(`goal-${id}`);
105
+ if (!card) return;
106
+ if (card.classList.contains('expanded')) {
107
+ card.classList.remove('expanded');
108
+ return;
109
+ }
110
+ // collapse others
111
+ document.querySelectorAll('.goal-card.expanded').forEach(c => c.classList.remove('expanded'));
112
+ card.classList.add('expanded');
113
+
114
+ const detail = document.getElementById(`goal-detail-${id}`);
115
+ if (!detail) return;
116
+ detail.innerHTML = `<div style="color:var(--text-muted);font-size:0.72rem;">...</div>`;
117
+
118
+ try {
119
+ const goal = await getGoal(id);
120
+ detail.innerHTML = _renderGoalDetail(goal);
121
+ } catch (e) {
122
+ detail.innerHTML = `<div style="color:var(--red);font-size:0.72rem;">${e.message}</div>`;
123
+ }
124
+ }
125
+
126
+ function _renderGoalDetail(g) {
127
+ let html = '';
128
+
129
+ // Success criteria
130
+ if (g.success_criteria && g.success_criteria.length) {
131
+ html += `<div style="margin-bottom:10px;"><div style="font-size:0.72rem;font-weight:500;color:var(--text-secondary);margin-bottom:4px;">Success Criteria</div>`;
132
+ html += g.success_criteria.map(c => `<div style="font-size:0.7rem;color:var(--text-muted);padding-left:8px;">• ${_esc(c)}</div>`).join('');
133
+ html += `</div>`;
134
+ }
135
+
136
+ // DAG tree
137
+ const tasks = g.dag?.nodes || g.tasks || g.next_tasks || [];
138
+ if (tasks.length > 0) {
139
+ html += `<div style="font-size:0.72rem;font-weight:500;color:var(--text-secondary);margin-bottom:4px;">${t('goal_dag')}</div>`;
140
+ html += `<div class="dag-tree">${_renderDagTree(tasks)}</div>`;
141
+ }
142
+
143
+ // Context
144
+ if (g.context && Object.keys(g.context).length) {
145
+ html += `<div style="margin-top:10px;font-size:0.68rem;color:var(--text-muted);font-family:var(--font-mono,monospace);white-space:pre-wrap;max-height:100px;overflow:auto;">${_esc(JSON.stringify(g.context, null, 2))}</div>`;
146
+ }
147
+
148
+ if (!html) {
149
+ html = `<div style="font-size:0.72rem;color:var(--text-muted);">${t('goal_status_' + g.status)}</div>`;
150
+ }
151
+
152
+ return html;
153
+ }
154
+
155
+ /* ── DAG Tree Rendering (pure CSS tree) ── */
156
+ function _renderDagTree(nodes) {
157
+ if (!nodes || nodes.length === 0) return '';
158
+ // Build adjacency: find root nodes (no dependencies or deps all outside)
159
+ const nodeMap = {};
160
+ nodes.forEach(n => { nodeMap[n.id || n.task_id] = n; });
161
+ const childSet = new Set();
162
+ nodes.forEach(n => {
163
+ (n.depends_on || n.dependencies || []).forEach(dep => childSet.add(n.id || n.task_id));
164
+ });
165
+ const roots = nodes.filter(n => {
166
+ const deps = n.depends_on || n.dependencies || [];
167
+ return deps.length === 0;
168
+ });
169
+
170
+ if (roots.length === 0) {
171
+ // no dependency info — render flat
172
+ return `<ul>${nodes.map(n => _dagNodeLi(n)).join('')}</ul>`;
173
+ }
174
+
175
+ // Build children map
176
+ const children = {};
177
+ nodes.forEach(n => {
178
+ (n.depends_on || n.dependencies || []).forEach(dep => {
179
+ if (!children[dep]) children[dep] = [];
180
+ children[dep].push(n);
181
+ });
182
+ });
183
+
184
+ function buildUl(nodeList) {
185
+ return `<ul>${nodeList.map(n => {
186
+ const nid = n.id || n.task_id;
187
+ const kids = children[nid] || [];
188
+ return `<li>${_dagNodeHtml(n)}${kids.length ? buildUl(kids) : ''}</li>`;
189
+ }).join('')}</ul>`;
190
+ }
191
+
192
+ return buildUl(roots);
193
+ }
194
+
195
+ function _dagNodeLi(n) {
196
+ return `<li>${_dagNodeHtml(n)}</li>`;
197
+ }
198
+
199
+ function _dagNodeHtml(n) {
200
+ const status = n.status || 'pending';
201
+ const label = n.description || n.title || n.objective || n.id || n.task_id || '?';
202
+ const type = n.worker_type || n.type || '';
203
+ return `<span class="dag-node"><span class="dag-node-dot ${status}"></span><span class="dag-node-label">${_esc(label)}</span>${type ? `<span class="dag-node-type">${_esc(type)}</span>` : ''}</span>`;
204
+ }
205
+
206
+ /* ── Goal Create ── */
207
+ function toggleGoalForm() {
208
+ const form = document.getElementById('goalCreateForm');
209
+ if (!form) return;
210
+ form.classList.toggle('visible');
211
+ if (form.classList.contains('visible')) {
212
+ const input = document.getElementById('goalObjectiveInput');
213
+ if (input) input.focus();
214
+ }
215
+ }
216
+
217
+ async function submitGoalForm() {
218
+ const objective = (document.getElementById('goalObjectiveInput')?.value || '').trim();
219
+ if (!objective) return showToast(t('msg_prompt_required'), 'error');
220
+
221
+ const mode = document.getElementById('goalModeSelect')?.value || 'gate';
222
+ const budget = parseFloat(document.getElementById('goalBudgetInput')?.value) || 5.0;
223
+
224
+ try {
225
+ await createGoal(objective, mode, { budget_usd: budget });
226
+ showToast(t('msg_goal_created'));
227
+ document.getElementById('goalObjectiveInput').value = '';
228
+ document.getElementById('goalCreateForm')?.classList.remove('visible');
229
+ loadGoals();
230
+ } catch (e) {
231
+ showToast(`${t('msg_goal_failed')}: ${e.message}`, 'error');
232
+ }
233
+ }
234
+
235
+ /* ── Goal Actions ── */
236
+ async function onApproveGoal(id) {
237
+ try {
238
+ await approveGoal(id);
239
+ showToast(t('msg_goal_approved'));
240
+ loadGoals();
241
+ } catch (e) {
242
+ showToast(`${t('msg_goal_failed')}: ${e.message}`, 'error');
243
+ }
244
+ }
245
+
246
+ async function onCancelGoal(id) {
247
+ if (!confirm(t('goal_confirm_cancel'))) return;
248
+ try {
249
+ await cancelGoal(id);
250
+ showToast(t('msg_goal_cancelled'));
251
+ loadGoals();
252
+ } catch (e) {
253
+ showToast(`${t('msg_goal_failed')}: ${e.message}`, 'error');
254
+ }
255
+ }
256
+
257
+ /* ── Filter ── */
258
+ function setGoalFilter(filter) {
259
+ _goalFilter = filter;
260
+ document.querySelectorAll('.goal-filter-btn').forEach(btn => {
261
+ btn.classList.toggle('active', btn.dataset.filter === filter);
262
+ });
263
+ renderGoals();
264
+ }
265
+
266
+ /* ── Polling (active goals) ── */
267
+ function _startGoalPoll() {
268
+ if (_goalPollTimer) return;
269
+ const hasActive = _goalsCache.some(_isActiveGoal);
270
+ if (!hasActive) return;
271
+ _goalPollTimer = setInterval(async () => {
272
+ try {
273
+ const data = await fetchGoals();
274
+ _goalsCache = Array.isArray(data) ? data : (data.goals || []);
275
+ renderGoals();
276
+ _updateGoalCount();
277
+ if (!_goalsCache.some(_isActiveGoal)) _stopGoalPoll();
278
+ } catch { /* silent */ }
279
+ }, 5000);
280
+ }
281
+
282
+ function _stopGoalPoll() {
283
+ if (_goalPollTimer) { clearInterval(_goalPollTimer); _goalPollTimer = null; }
284
+ }
285
+
286
+ /* ── Util ── */
287
+ function _esc(s) {
288
+ if (!s) return '';
289
+ const d = document.createElement('div');
290
+ d.textContent = String(s);
291
+ return d.innerHTML;
292
+ }
293
+
294
+ function _timeAgo(ts) {
295
+ const sec = Math.floor(Date.now() / 1000 - ts);
296
+ if (sec < 60) return `${sec}s`;
297
+ if (sec < 3600) return `${Math.floor(sec / 60)}m`;
298
+ if (sec < 86400) return `${Math.floor(sec / 3600)}h`;
299
+ return `${Math.floor(sec / 86400)}d`;
300
+ }
@@ -17,6 +17,11 @@ const I18N = {
17
17
  disconnect:'연결 해제', connect:'연결', settings:'설정',
18
18
  language_settings:'언어 설정', display_language:'표시 언어',
19
19
  display_language_desc:'인터페이스에 표시되는 언어를 선택합니다',
20
+ webhook_settings:'웹훅 설정', webhook_url:'Webhook URL',
21
+ webhook_url_desc:'작업 완료/실패 시 결과를 POST할 URL',
22
+ webhook_secret:'Webhook Secret', webhook_secret_desc:'HMAC-SHA256 서명용 비밀 키 (선택사항)',
23
+ webhook_events:'이벤트 필터', webhook_events_desc:'웹훅을 보낼 이벤트 (done, failed)',
24
+ webhook_test:'연결 테스트', webhook_test_desc:'설정된 URL로 테스트 이벤트를 전송합니다',
20
25
  close:'닫기', save:'저장', select_session:'세션 선택',
21
26
  this_project_only:'이 프로젝트만', search_prompt:'프롬프트 검색...',
22
27
  msg_settings_saved:'설정이 저장되었습니다', msg_settings_save_failed:'설정 저장 실패',
@@ -40,6 +45,61 @@ const I18N = {
40
45
  msg_service_restart:'서비스 재시작 요청 완료', msg_service_failed:'서비스 요청 실패',
41
46
  status_running:'실행 중', status_done:'완료', status_failed:'실패', status_pending:'대기 중',
42
47
  no_jobs:'작업이 없습니다', connected_to:'연결됨', no_project:'프로젝트 미지정',
48
+ conn_lost:'서버 연결이 끊어졌습니다. 재연결 시도 중...',
49
+ all_projects:'전체 프로젝트',
50
+ presets:'프리셋', automations:'자동화', save_as_preset:'프리셋 저장',
51
+ confirm_delete_job:'이 작업을 삭제하시겠습니까?', confirm_delete_completed:'완료된 작업을 모두 삭제하시겠습니까?',
52
+ err_show_log:'상세 로그 보기',
53
+ stream_no_output:'출력 데이터 없음',
54
+ checkpoints:'체크포인트', diff_viewer:'Diff 뷰어', diff_compare:'비교',
55
+ diff_no_checkpoints:'체크포인트가 없습니다.', diff_select_hint:'두 체크포인트를 선택하여 비교하세요.',
56
+ diff_loading:'Diff를 불러오는 중...', diff_no_changes:'변경사항이 없습니다.',
57
+ diff_files:'파일', diff_additions:'추가', diff_deletions:'삭제',
58
+ stream_job_done:'작업 완료', stream_job_failed:'작업 실패',
59
+ stream_copy_all:'전체 복사', stream_delete_job:'작업 제거',
60
+ stream_followup_label:'이어서',
61
+ stream_followup_placeholder:'이 세션에 이어서 실행할 명령... (파일/이미지 붙여넣기 가능)',
62
+ stream_load_failed:'스트림 데이터를 불러오지 못했습니다',
63
+ stream_retry:'다시 시도', stream_reconnect:'다시 연결',
64
+ stat_jobs_summary:'{total} 작업 ({running} 실행)',
65
+ stat_success:'성공 {pct}%',
66
+ pd_total:'전체', pd_running:'실행 중', pd_done:'완료', pd_failed:'실패',
67
+ pd_avg:'평균', pd_success_rate:'성공률', pd_registered:'등록',
68
+ pd_send_task:'작업 전송', pd_send_task_title:'이 프로젝트에 작업 전송', pd_show_all_title:'전체 프로젝트 보기',
69
+ pd_other:'기타',
70
+ job_retry:'다시 실행', job_retry_title:'같은 프롬프트로 다시 실행',
71
+ job_resume_title:'세션 이어서 명령 (resume)', job_fork_title:'이 세션에서 분기 (fork)',
72
+ job_delete_title:'작업 제거',
73
+ job_zombie:'응답 없음', job_zombie_title:'5분 이상 응답 없음 — 프로세스 확인 필요',
74
+ job_dep_title:'선행 작업: {deps}',
75
+ job_count:'{n}건',
76
+ job_count_filtered:'{filtered}/{total}건',
77
+ stream_loading:'스트림 데이터를 불러오는 중...',
78
+ stream_preview_wait:'대기 중...',
79
+ msg_resume_mode:'Resume 모드: {sid} 세션에 이어서 전송합니다.',
80
+ // Goals
81
+ goals:'목표', goal_create:'목표 생성', goal_objective:'목표', goal_mode:'실행 모드',
82
+ goal_budget:'예산 (USD)', goal_max_tasks:'최대 태스크 수',
83
+ goal_mode_full_auto:'완전 자율', goal_mode_gate:'단계별 승인', goal_mode_watch:'관찰 모드', goal_mode_pair:'페어 모드',
84
+ goal_status_pending:'대기', goal_status_planning:'계획 중', goal_status_ready:'준비 완료',
85
+ goal_status_running:'실행 중', goal_status_gate_waiting:'승인 대기', goal_status_evaluating:'평가 중',
86
+ goal_status_completed:'완료', goal_status_failed:'실패', goal_status_cancelled:'취소됨',
87
+ goal_approve:'승인', goal_cancel:'취소', goal_no_goals:'등록된 목표가 없습니다.',
88
+ goal_dag:'실행 계획 (DAG)', goal_tasks:'태스크', goal_progress:'진행률',
89
+ goal_filter_all:'전체', goal_filter_active:'진행 중', goal_filter_done:'완료', goal_filter_cancelled:'취소',
90
+ goal_create_placeholder:'달성하고자 하는 목표를 입력하세요...',
91
+ goal_confirm_cancel:'이 목표를 취소하시겠습니까?',
92
+ msg_goal_created:'목표가 생성되었습니다.', msg_goal_approved:'다음 단계가 승인되었습니다.',
93
+ msg_goal_cancelled:'목표가 취소되었습니다.', msg_goal_failed:'목표 작업 실패',
94
+ // Memory
95
+ memory:'메모리', memory_search:'메모리 검색', memory_add:'메모리 추가',
96
+ memory_type:'유형', memory_title:'제목', memory_content:'내용', memory_tags:'태그',
97
+ memory_project:'프로젝트', memory_type_decision:'결정', memory_type_pattern:'패턴',
98
+ memory_type_failure:'실패', memory_type_context:'맥락',
99
+ memory_no_items:'저장된 메모리가 없습니다.', memory_search_placeholder:'키워드로 검색...',
100
+ memory_confirm_delete:'이 메모리를 삭제하시겠습니까?',
101
+ msg_memory_created:'메모리가 저장되었습니다.', msg_memory_deleted:'메모리가 삭제되었습니다.',
102
+ msg_memory_updated:'메모리가 수정되었습니다.', msg_memory_failed:'메모리 작업 실패',
43
103
  },
44
104
  en: {
45
105
  title:'Controller Service', send_task:'Send Task', prompt:'Prompt',
@@ -55,6 +115,11 @@ const I18N = {
55
115
  disconnect:'Disconnect', connect:'Connect', settings:'Settings',
56
116
  language_settings:'Language', display_language:'Display Language',
57
117
  display_language_desc:'Select the language for the interface',
118
+ webhook_settings:'Webhook Settings', webhook_url:'Webhook URL',
119
+ webhook_url_desc:'URL to POST results when a job completes or fails',
120
+ webhook_secret:'Webhook Secret', webhook_secret_desc:'Secret key for HMAC-SHA256 signature (optional)',
121
+ webhook_events:'Event Filter', webhook_events_desc:'Events to send webhooks for (done, failed)',
122
+ webhook_test:'Test Connection', webhook_test_desc:'Send a test event to the configured URL',
58
123
  close:'Close', save:'Save', select_session:'Select Session',
59
124
  this_project_only:'This project only', search_prompt:'Search prompts...',
60
125
  msg_settings_saved:'Settings saved', msg_settings_save_failed:'Failed to save settings',
@@ -78,6 +143,61 @@ const I18N = {
78
143
  msg_service_restart:'Service restart requested', msg_service_failed:'Service request failed',
79
144
  status_running:'Running', status_done:'Done', status_failed:'Failed', status_pending:'Pending',
80
145
  no_jobs:'No jobs', connected_to:'Connected', no_project:'No project',
146
+ conn_lost:'Server connection lost. Reconnecting...',
147
+ all_projects:'All Projects',
148
+ presets:'Presets', automations:'Automations', save_as_preset:'Save Preset',
149
+ confirm_delete_job:'Delete this job?', confirm_delete_completed:'Delete all completed jobs?',
150
+ err_show_log:'Show detailed log',
151
+ stream_no_output:'No output data',
152
+ checkpoints:'Checkpoints', diff_viewer:'Diff Viewer', diff_compare:'Compare',
153
+ diff_no_checkpoints:'No checkpoints found.', diff_select_hint:'Select two checkpoints to compare.',
154
+ diff_loading:'Loading diff...', diff_no_changes:'No changes.',
155
+ diff_files:'files', diff_additions:'additions', diff_deletions:'deletions',
156
+ stream_job_done:'Job Complete', stream_job_failed:'Job Failed',
157
+ stream_copy_all:'Copy All', stream_delete_job:'Delete Job',
158
+ stream_followup_label:'Follow Up',
159
+ stream_followup_placeholder:'Enter a command to continue in this session... (paste files/images)',
160
+ stream_load_failed:'Failed to load stream data',
161
+ stream_retry:'Retry', stream_reconnect:'Reconnect',
162
+ stat_jobs_summary:'{total} jobs ({running} running)',
163
+ stat_success:'Success {pct}%',
164
+ pd_total:'Total', pd_running:'Running', pd_done:'Done', pd_failed:'Failed',
165
+ pd_avg:'Avg', pd_success_rate:'Success Rate', pd_registered:'Registered',
166
+ pd_send_task:'Send Task', pd_send_task_title:'Send task to this project', pd_show_all_title:'View all projects',
167
+ pd_other:'Other',
168
+ job_retry:'Retry', job_retry_title:'Re-run with the same prompt',
169
+ job_resume_title:'Continue session (resume)', job_fork_title:'Branch from this session (fork)',
170
+ job_delete_title:'Delete job',
171
+ job_zombie:'No Response', job_zombie_title:'No response for 5+ minutes — check process',
172
+ job_dep_title:'Depends on: {deps}',
173
+ job_count:'{n}',
174
+ job_count_filtered:'{filtered}/{total}',
175
+ stream_loading:'Loading stream data...',
176
+ stream_preview_wait:'Waiting...',
177
+ msg_resume_mode:'Resume mode: continuing session {sid}',
178
+ // Goals
179
+ goals:'Goals', goal_create:'Create Goal', goal_objective:'Objective', goal_mode:'Mode',
180
+ goal_budget:'Budget (USD)', goal_max_tasks:'Max Tasks',
181
+ goal_mode_full_auto:'Full Auto', goal_mode_gate:'Gate', goal_mode_watch:'Watch', goal_mode_pair:'Pair',
182
+ goal_status_pending:'Pending', goal_status_planning:'Planning', goal_status_ready:'Ready',
183
+ goal_status_running:'Running', goal_status_gate_waiting:'Awaiting Approval', goal_status_evaluating:'Evaluating',
184
+ goal_status_completed:'Completed', goal_status_failed:'Failed', goal_status_cancelled:'Cancelled',
185
+ goal_approve:'Approve', goal_cancel:'Cancel', goal_no_goals:'No goals registered.',
186
+ goal_dag:'Execution Plan (DAG)', goal_tasks:'Tasks', goal_progress:'Progress',
187
+ goal_filter_all:'All', goal_filter_active:'Active', goal_filter_done:'Done', goal_filter_cancelled:'Cancelled',
188
+ goal_create_placeholder:'Describe the goal you want to achieve...',
189
+ goal_confirm_cancel:'Cancel this goal?',
190
+ msg_goal_created:'Goal created.', msg_goal_approved:'Next stage approved.',
191
+ msg_goal_cancelled:'Goal cancelled.', msg_goal_failed:'Goal operation failed',
192
+ // Memory
193
+ memory:'Memory', memory_search:'Search Memory', memory_add:'Add Memory',
194
+ memory_type:'Type', memory_title:'Title', memory_content:'Content', memory_tags:'Tags',
195
+ memory_project:'Project', memory_type_decision:'Decision', memory_type_pattern:'Pattern',
196
+ memory_type_failure:'Failure', memory_type_context:'Context',
197
+ memory_no_items:'No memories stored.', memory_search_placeholder:'Search by keyword...',
198
+ memory_confirm_delete:'Delete this memory?',
199
+ msg_memory_created:'Memory saved.', msg_memory_deleted:'Memory deleted.',
200
+ msg_memory_updated:'Memory updated.', msg_memory_failed:'Memory operation failed',
81
201
  },
82
202
  ja: {
83
203
  title:'Controller Service', send_task:'新しいタスク送信', prompt:'プロンプト',
@@ -116,6 +236,34 @@ const I18N = {
116
236
  msg_service_restart:'サービス再起動を要求', msg_service_failed:'サービスリクエスト失敗',
117
237
  status_running:'実行中', status_done:'完了', status_failed:'失敗', status_pending:'待機中',
118
238
  no_jobs:'ジョブがありません', connected_to:'接続済み', no_project:'プロジェクト未指定',
239
+ conn_lost:'サーバー接続が切断されました。再接続中...',
240
+ all_projects:'全プロジェクト',
241
+ presets:'プリセット', automations:'自動化', save_as_preset:'プリセット保存',
242
+ confirm_delete_job:'このジョブを削除しますか?', confirm_delete_completed:'完了したジョブをすべて削除しますか?',
243
+ err_show_log:'詳細ログを表示',
244
+ stream_no_output:'出力データなし',
245
+ stream_job_done:'ジョブ完了', stream_job_failed:'ジョブ失敗',
246
+ stream_copy_all:'全体コピー', stream_delete_job:'ジョブ削除',
247
+ stream_followup_label:'続行',
248
+ stream_followup_placeholder:'このセッションで続行するコマンドを入力... (ファイル/画像の貼り付け可)',
249
+ stream_load_failed:'ストリームデータの読み込みに失敗しました',
250
+ stream_retry:'再試行', stream_reconnect:'再接続',
251
+ stat_jobs_summary:'{total} ジョブ ({running} 実行中)',
252
+ stat_success:'成功 {pct}%',
253
+ pd_total:'全体', pd_running:'実行中', pd_done:'完了', pd_failed:'失敗',
254
+ pd_avg:'平均', pd_success_rate:'成功率', pd_registered:'登録済み',
255
+ pd_send_task:'タスク送信', pd_send_task_title:'このプロジェクトにタスク送信', pd_show_all_title:'全プロジェクト表示',
256
+ pd_other:'その他',
257
+ job_retry:'再実行', job_retry_title:'同じプロンプトで再実行',
258
+ job_resume_title:'セッション続行 (resume)', job_fork_title:'このセッションから分岐 (fork)',
259
+ job_delete_title:'ジョブ削除',
260
+ job_zombie:'応答なし', job_zombie_title:'5分以上応答なし — プロセスを確認してください',
261
+ job_dep_title:'先行タスク: {deps}',
262
+ job_count:'{n}件',
263
+ job_count_filtered:'{filtered}/{total}件',
264
+ stream_loading:'ストリームデータを読み込み中...',
265
+ stream_preview_wait:'待機中...',
266
+ msg_resume_mode:'Resumeモード: {sid} セッションに続行します。',
119
267
  },
120
268
  'zh-CN': {
121
269
  title:'Controller Service', send_task:'发送任务', prompt:'提示词',
@@ -152,6 +300,34 @@ const I18N = {
152
300
  msg_service_restart:'服务重启请求已完成', msg_service_failed:'服务请求失败',
153
301
  status_running:'运行中', status_done:'完成', status_failed:'失败', status_pending:'等待中',
154
302
  no_jobs:'没有任务', connected_to:'已连接', no_project:'未指定项目',
303
+ conn_lost:'服务器连接已断开,正在重新连接...',
304
+ all_projects:'全部项目',
305
+ presets:'预设', automations:'自动化', save_as_preset:'保存预设',
306
+ confirm_delete_job:'此任务要删除吗?', confirm_delete_completed:'删除所有已完成的任务吗?',
307
+ err_show_log:'详细日志查看',
308
+ stream_no_output:'无输出数据',
309
+ stream_job_done:'任务完成', stream_job_failed:'任务失败',
310
+ stream_copy_all:'全部复制', stream_delete_job:'删除任务',
311
+ stream_followup_label:'继续',
312
+ stream_followup_placeholder:'输入继续执行的命令... (可粘贴文件/图片)',
313
+ stream_load_failed:'无法加载流数据',
314
+ stream_retry:'重试', stream_reconnect:'重新连接',
315
+ stat_jobs_summary:'{total} 任务 ({running} 运行中)',
316
+ stat_success:'成�� {pct}%',
317
+ pd_total:'总计', pd_running:'运行中', pd_done:'完成', pd_failed:'��败',
318
+ pd_avg:'平均', pd_success_rate:'成功率', pd_registered:'已注册',
319
+ pd_send_task:'发送任务', pd_send_task_title:'向此项目发送任务', pd_show_all_title:'查看全部项目',
320
+ pd_other:'其他',
321
+ job_retry:'重新执行', job_retry_title:'使用相同提示词重��执行',
322
+ job_resume_title:'继续会话 (resume)', job_fork_title:'从此会话分支 (fork)',
323
+ job_delete_title:'删除任务',
324
+ job_zombie:'无响应', job_zombie_title:'超过5分钟无响应 — 请检查进程',
325
+ job_dep_title:'前置任务: {deps}',
326
+ job_count:'{n}个',
327
+ job_count_filtered:'{filtered}/{total}个',
328
+ stream_loading:'正在加载流数据...',
329
+ stream_preview_wait:'等待中...',
330
+ msg_resume_mode:'Resume模式: 继续会话 {sid}',
155
331
  },
156
332
  'zh-TW': {
157
333
  title:'Controller Service', send_task:'傳送任務', prompt:'提示詞',
@@ -188,6 +364,34 @@ const I18N = {
188
364
  msg_service_restart:'服務重新啟動請求已完成', msg_service_failed:'服務請求失敗',
189
365
  status_running:'執行中', status_done:'完成', status_failed:'失敗', status_pending:'等待中',
190
366
  no_jobs:'沒有任務', connected_to:'已連線', no_project:'未指定專案',
367
+ conn_lost:'伺服器連線已中斷,正在重新連線...',
368
+ all_projects:'全部專案',
369
+ presets:'預設', automations:'自動化', save_as_preset:'儲存預設',
370
+ confirm_delete_job:'此任務要刪除嗎?', confirm_delete_completed:'刪除所有已完成的任務嗎?',
371
+ err_show_log:'詳細日誌查看',
372
+ stream_no_output:'無輸出資料',
373
+ stream_job_done:'任務完成', stream_job_failed:'任務失敗',
374
+ stream_copy_all:'全部複製', stream_delete_job:'刪除任務',
375
+ stream_followup_label:'繼續',
376
+ stream_followup_placeholder:'輸入繼續執行的命令... (可貼上檔案/圖片)',
377
+ stream_load_failed:'無法載入串流資料',
378
+ stream_retry:'重試', stream_reconnect:'重新連線',
379
+ stat_jobs_summary:'{total} 任務 ({running} 執行中)',
380
+ stat_success:'成功 {pct}%',
381
+ pd_total:'全部', pd_running:'執行中', pd_done:'完成', pd_failed:'失敗',
382
+ pd_avg:'平均', pd_success_rate:'成功率', pd_registered:'已註冊',
383
+ pd_send_task:'傳送任務', pd_send_task_title:'向此專案傳送任務', pd_show_all_title:'檢視全部專案',
384
+ pd_other:'其他',
385
+ job_retry:'重新執行', job_retry_title:'使用相同提示詞重新執行',
386
+ job_resume_title:'繼續工作階段 (resume)', job_fork_title:'從此工作階段分支 (fork)',
387
+ job_delete_title:'刪除任務',
388
+ job_zombie:'無回應', job_zombie_title:'超過5分鐘無回應 — 請檢查程序',
389
+ job_dep_title:'前置任務: {deps}',
390
+ job_count:'{n}個',
391
+ job_count_filtered:'{filtered}/{total}個',
392
+ stream_loading:'正在載入串流資料...',
393
+ stream_preview_wait:'等待中...',
394
+ msg_resume_mode:'Resume模式: 繼續工作階段 {sid}',
191
395
  },
192
396
  es: {
193
397
  title:'Controller Service', send_task:'Enviar Tarea', prompt:'Prompt',
@@ -225,6 +429,34 @@ const I18N = {
225
429
  msg_service_restart:'Servicio reiniciado', msg_service_failed:'Error de servicio',
226
430
  status_running:'Ejecutando', status_done:'Completado', status_failed:'Fallido', status_pending:'Pendiente',
227
431
  no_jobs:'Sin tareas', connected_to:'Conectado', no_project:'Sin proyecto',
432
+ conn_lost:'Conexión con el servidor perdida. Reconectando...',
433
+ all_projects:'Todos los Proyectos',
434
+ presets:'Presets', automations:'Automatizaciones', save_as_preset:'Guardar Preset',
435
+ confirm_delete_job:'¿Eliminar esta tarea?', confirm_delete_completed:'¿Eliminar todas las tareas completadas?',
436
+ err_show_log:'Ver registro detallado',
437
+ stream_no_output:'Sin datos de salida',
438
+ stream_job_done:'Tarea Completada', stream_job_failed:'Tarea Fallida',
439
+ stream_copy_all:'Copiar Todo', stream_delete_job:'Eliminar Tarea',
440
+ stream_followup_label:'Continuar',
441
+ stream_followup_placeholder:'Ingrese un comando para continuar en esta sesión... (pegue archivos/imágenes)',
442
+ stream_load_failed:'No se pudieron cargar los datos del stream',
443
+ stream_retry:'Reintentar', stream_reconnect:'Reconectar',
444
+ stat_jobs_summary:'{total} tareas ({running} ejecutando)',
445
+ stat_success:'Éxito {pct}%',
446
+ pd_total:'Total', pd_running:'Ejecutando', pd_done:'Completado', pd_failed:'Fallido',
447
+ pd_avg:'Promedio', pd_success_rate:'Tasa de Éxito', pd_registered:'Registrado',
448
+ pd_send_task:'Enviar Tarea', pd_send_task_title:'Enviar tarea a este proyecto', pd_show_all_title:'Ver todos los proyectos',
449
+ pd_other:'Otros',
450
+ job_retry:'Reintentar', job_retry_title:'Re-ejecutar con el mismo prompt',
451
+ job_resume_title:'Continuar sesión (resume)', job_fork_title:'Bifurcar desde esta sesión (fork)',
452
+ job_delete_title:'Eliminar tarea',
453
+ job_zombie:'Sin respuesta', job_zombie_title:'Sin respuesta por más de 5 minutos — verificar proceso',
454
+ job_dep_title:'Depende de: {deps}',
455
+ job_count:'{n}',
456
+ job_count_filtered:'{filtered}/{total}',
457
+ stream_loading:'Cargando datos del stream...',
458
+ stream_preview_wait:'Esperando...',
459
+ msg_resume_mode:'Modo Resume: continuando sesión {sid}',
228
460
  },
229
461
  fr: {
230
462
  title:'Controller Service', send_task:'Envoyer une Tâche', prompt:'Prompt',
@@ -262,6 +494,34 @@ const I18N = {
262
494
  msg_service_restart:'Redémarrage du service', msg_service_failed:'Échec du service',
263
495
  status_running:'En cours', status_done:'Terminé', status_failed:'Échoué', status_pending:'En attente',
264
496
  no_jobs:'Aucune tâche', connected_to:'Connecté', no_project:'Aucun projet',
497
+ conn_lost:'Connexion au serveur perdue. Reconnexion en cours...',
498
+ all_projects:'Tous les Projets',
499
+ presets:'Presets', automations:'Automatisations', save_as_preset:'Enregistrer le Preset',
500
+ confirm_delete_job:'Supprimer cette tâche ?', confirm_delete_completed:'Supprimer toutes les tâches terminées ?',
501
+ err_show_log:'Voir le journal détaillé',
502
+ stream_no_output:'Aucune donnée de sortie',
503
+ stream_job_done:'Tâche Terminée', stream_job_failed:'Tâche Échouée',
504
+ stream_copy_all:'Tout Copier', stream_delete_job:'Supprimer la Tâche',
505
+ stream_followup_label:'Continuer',
506
+ stream_followup_placeholder:'Entrez une commande pour continuer dans cette session... (collez fichiers/images)',
507
+ stream_load_failed:'Impossible de charger les données du stream',
508
+ stream_retry:'Réessayer', stream_reconnect:'Reconnecter',
509
+ stat_jobs_summary:'{total} tâches ({running} en cours)',
510
+ stat_success:'Réussite {pct}%',
511
+ pd_total:'Total', pd_running:'En cours', pd_done:'Terminé', pd_failed:'Échoué',
512
+ pd_avg:'Moyenne', pd_success_rate:'Taux de Réussite', pd_registered:'Enregistré',
513
+ pd_send_task:'Envoyer Tâche', pd_send_task_title:'Envoyer une tâche à ce projet', pd_show_all_title:'Voir tous les projets',
514
+ pd_other:'Autres',
515
+ job_retry:'Réessayer', job_retry_title:'Re-exécuter avec le même prompt',
516
+ job_resume_title:'Continuer la session (resume)', job_fork_title:'Bifurquer depuis cette session (fork)',
517
+ job_delete_title:'Supprimer la tâche',
518
+ job_zombie:'Pas de réponse', job_zombie_title:'Pas de réponse depuis 5+ minutes — vérifier le processus',
519
+ job_dep_title:'Dépend de : {deps}',
520
+ job_count:'{n}',
521
+ job_count_filtered:'{filtered}/{total}',
522
+ stream_loading:'Chargement des données du stream...',
523
+ stream_preview_wait:'En attente...',
524
+ msg_resume_mode:'Mode Resume : continuation de la session {sid}',
265
525
  },
266
526
  de: {
267
527
  title:'Controller Service', send_task:'Aufgabe senden', prompt:'Prompt',
@@ -299,6 +559,34 @@ const I18N = {
299
559
  msg_service_restart:'Dienstneustart angefordert', msg_service_failed:'Dienstanforderung fehlgeschlagen',
300
560
  status_running:'Läuft', status_done:'Fertig', status_failed:'Fehlgeschlagen', status_pending:'Wartend',
301
561
  no_jobs:'Keine Aufgaben', connected_to:'Verbunden', no_project:'Kein Projekt',
562
+ conn_lost:'Serververbindung verloren. Erneuter Verbindungsversuch...',
563
+ all_projects:'Alle Projekte',
564
+ presets:'Presets', automations:'Automatisierungen', save_as_preset:'Preset speichern',
565
+ confirm_delete_job:'Diese Aufgabe löschen?', confirm_delete_completed:'Alle erledigten Aufgaben löschen?',
566
+ err_show_log:'Detailliertes Protokoll anzeigen',
567
+ stream_no_output:'Keine Ausgabedaten',
568
+ stream_job_done:'Aufgabe Abgeschlossen', stream_job_failed:'Aufgabe Fehlgeschlagen',
569
+ stream_copy_all:'Alles Kopieren', stream_delete_job:'Aufgabe Löschen',
570
+ stream_followup_label:'Fortfahren',
571
+ stream_followup_placeholder:'Befehl zum Fortfahren in dieser Sitzung eingeben... (Dateien/Bilder einfügen)',
572
+ stream_load_failed:'Stream-Daten konnten nicht geladen werden',
573
+ stream_retry:'Wiederholen', stream_reconnect:'Erneut Verbinden',
574
+ stat_jobs_summary:'{total} Aufgaben ({running} laufend)',
575
+ stat_success:'Erfolg {pct}%',
576
+ pd_total:'Gesamt', pd_running:'Laufend', pd_done:'Fertig', pd_failed:'Fehlgeschlagen',
577
+ pd_avg:'Durchschn.', pd_success_rate:'Erfolgsrate', pd_registered:'Registriert',
578
+ pd_send_task:'Aufgabe senden', pd_send_task_title:'Aufgabe an dieses Projekt senden', pd_show_all_title:'Alle Projekte anzeigen',
579
+ pd_other:'Sonstige',
580
+ job_retry:'Wiederholen', job_retry_title:'Mit gleichem Prompt erneut ausführen',
581
+ job_resume_title:'Sitzung fortsetzen (resume)', job_fork_title:'Von dieser Sitzung abzweigen (fork)',
582
+ job_delete_title:'Aufgabe löschen',
583
+ job_zombie:'Keine Antwort', job_zombie_title:'Keine Antwort seit 5+ Minuten — Prozess überprüfen',
584
+ job_dep_title:'Abhängig von: {deps}',
585
+ job_count:'{n}',
586
+ job_count_filtered:'{filtered}/{total}',
587
+ stream_loading:'Stream-Daten werden geladen...',
588
+ stream_preview_wait:'Warten...',
589
+ msg_resume_mode:'Resume-Modus: Sitzung {sid} wird fortgesetzt',
302
590
  },
303
591
  };
304
592