codexmate 0.1.9 → 0.1.12

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 (45) hide show
  1. package/README.md +0 -4
  2. package/README.vi.md +0 -4
  3. package/README.zh.md +0 -4
  4. package/cli/agents-files.js +27 -3
  5. package/cli/prompt-history.js +171 -0
  6. package/cli/system-prompt-files.js +20 -2
  7. package/cli.js +83 -2694
  8. package/package.json +1 -1
  9. package/web-ui/app.js +39 -56
  10. package/web-ui/index.html +0 -1
  11. package/web-ui/modules/app.computed.dashboard.mjs +0 -3
  12. package/web-ui/modules/app.computed.main-tabs.mjs +0 -474
  13. package/web-ui/modules/app.methods.agents.mjs +90 -0
  14. package/web-ui/modules/app.methods.index.mjs +1 -3
  15. package/web-ui/modules/app.methods.navigation.mjs +8 -3
  16. package/web-ui/modules/app.methods.session-actions.mjs +57 -25
  17. package/web-ui/modules/app.methods.system-prompt.mjs +81 -0
  18. package/web-ui/modules/app.methods.web-ui-preferences.mjs +26 -9
  19. package/web-ui/modules/i18n/locales/en.mjs +10 -265
  20. package/web-ui/modules/i18n/locales/ja.mjs +10 -265
  21. package/web-ui/modules/i18n/locales/vi.mjs +10 -266
  22. package/web-ui/modules/i18n/locales/zh-tw.mjs +10 -265
  23. package/web-ui/modules/i18n/locales/zh.mjs +10 -265
  24. package/web-ui/partials/index/layout-header.html +5 -48
  25. package/web-ui/partials/index/panel-config-claude.html +3 -3
  26. package/web-ui/partials/index/panel-config-codex.html +3 -3
  27. package/web-ui/partials/index/panel-config-kilocode.html +5 -5
  28. package/web-ui/partials/index/panel-config-openclaw.html +3 -3
  29. package/web-ui/partials/index/panel-config-opencode.html +5 -5
  30. package/web-ui/partials/index/panel-dashboard.html +0 -21
  31. package/web-ui/partials/index/panel-prompts.html +52 -0
  32. package/web-ui/partials/index/panel-sessions.html +12 -0
  33. package/web-ui/partials/index/panel-settings.html +35 -0
  34. package/web-ui/res/web-ui-render.precompiled.js +543 -1376
  35. package/web-ui/session-helpers.mjs +0 -16
  36. package/web-ui/styles/modals-core.css +86 -0
  37. package/web-ui/styles/sessions-preview.css +38 -0
  38. package/web-ui/styles/titles-cards.css +2 -2
  39. package/web-ui/styles.css +0 -1
  40. package/lib/automation.js +0 -404
  41. package/lib/task-orchestrator.js +0 -938
  42. package/lib/task-workspace-chat.js +0 -292
  43. package/web-ui/modules/app.methods.task-orchestration.mjs +0 -895
  44. package/web-ui/partials/index/panel-orchestration.html +0 -598
  45. package/web-ui/styles/task-orchestration.css +0 -2979
@@ -1,379 +1,3 @@
1
- function normalizeTaskDraftLines(text) {
2
- return String(text || '')
3
- .split(/\r?\n/g)
4
- .map((item) => item.trim())
5
- .filter(Boolean);
6
- }
7
-
8
- function readTaskOrchestrationDraftMetrics(taskOrchestration) {
9
- const state = taskOrchestration && typeof taskOrchestration === 'object' ? taskOrchestration : {};
10
- const target = String(state.target || '').trim();
11
- const notes = String(state.notes || '').trim();
12
- const title = String(state.title || '').trim();
13
- const workflowIds = normalizeTaskDraftLines(state.workflowIdsText);
14
- const followUps = normalizeTaskDraftLines(state.followUpsText);
15
- const requestCount = target ? followUps.length + 1 : 0;
16
- const engine = String(state.selectedEngine || 'openai-chat').trim().toLowerCase() === 'workflow' ? 'workflow' : 'openai-chat';
17
- const runMode = String(state.runMode || 'write').trim().toLowerCase();
18
- const allowWrite = runMode === 'write';
19
- const dryRun = runMode === 'dry-run';
20
- const plan = state.plan && typeof state.plan === 'object' ? state.plan : null;
21
- const planNodes = Array.isArray(plan && plan.nodes) ? plan.nodes : [];
22
- const planIssues = Array.isArray(state.planIssues) ? state.planIssues : [];
23
- const planWarnings = Array.isArray(state.planWarnings) ? state.planWarnings : [];
24
- return {
25
- engine,
26
- runMode,
27
- title,
28
- target,
29
- notes,
30
- workflowIds,
31
- followUps,
32
- hasTarget: target.length > 0,
33
- hasNotes: notes.length > 0,
34
- hasTitle: title.length > 0,
35
- hasPlan: !!plan,
36
- planNodes,
37
- planIssues,
38
- planWarnings,
39
- workflowCount: workflowIds.length,
40
- followUpCount: followUps.length,
41
- requestCount,
42
- hasSequentialFollowUps: followUps.length > 0,
43
- planNodeCount: planNodes.length,
44
- allowWrite,
45
- dryRun
46
- };
47
- }
48
-
49
-
50
- function formatTaskConversationMeta(items) {
51
- return items.filter(Boolean).join(' · ');
52
- }
53
-
54
- function collectTaskRunWorkspaceFileLines(detail) {
55
- const run = detail && detail.run && typeof detail.run === 'object' ? detail.run : null;
56
- const nodes = Array.isArray(detail && detail.nodes)
57
- ? detail.nodes
58
- : (Array.isArray(run && run.nodes) ? run.nodes : []);
59
- const files = [];
60
- for (const node of nodes) {
61
- const output = node && node.output && typeof node.output === 'object' ? node.output : null;
62
- const workspaceFiles = Array.isArray(output && output.workspaceFiles) ? output.workspaceFiles : [];
63
- const materializedFiles = Array.isArray(output && output.materializedFiles) ? output.materializedFiles : [];
64
- for (const file of workspaceFiles) {
65
- if (!file || typeof file !== 'object') continue;
66
- const op = String(file.operation || '').trim().toUpperCase() || 'FILE';
67
- const filePath = String(file.relativePath || file.path || '').trim();
68
- if (filePath) files.push(`${op} ${filePath}`);
69
- }
70
- for (const file of materializedFiles) {
71
- if (!file || typeof file !== 'object') continue;
72
- const filePath = String(file.relativePath || file.path || '').trim();
73
- if (filePath) files.push(`WRITE ${filePath}`);
74
- }
75
- }
76
- return Array.from(new Set(files)).slice(0, 6);
77
- }
78
-
79
- function normalizeTaskWorkspacePath(value) {
80
- return String(value || '').trim().replace(/\\+/g, '/').replace(/\/$/, '');
81
- }
82
-
83
- function readTaskWorkspacePathFromItem(item) {
84
- if (!item || typeof item !== 'object') return '';
85
- return normalizeTaskWorkspacePath(item.cwd || item.workspacePath || item.projectPath || item.path);
86
- }
87
-
88
- function formatTaskWorkspaceLabel(workspacePath, fallback = 'Auto workspace') {
89
- const normalized = normalizeTaskWorkspacePath(workspacePath);
90
- if (!normalized) return fallback;
91
- const parts = normalized.split('/').filter(Boolean);
92
- return parts.length ? parts[parts.length - 1] : normalized;
93
- }
94
-
95
- function readTaskItemTimestamp(item) {
96
- if (!item || typeof item !== 'object') return '';
97
- return String(item.updatedAt || item.completedAt || item.startedAt || item.createdAt || item.enqueuedAt || '').trim();
98
- }
99
-
100
- function addTaskWorkspaceCandidate(workspaces, workspacePath, source, timestamp = '') {
101
- const normalized = normalizeTaskWorkspacePath(workspacePath);
102
- if (!normalized) return;
103
- const key = normalized.toLowerCase();
104
- const current = workspaces.get(key) || {
105
- key,
106
- path: normalized,
107
- label: formatTaskWorkspaceLabel(normalized),
108
- meta: normalized,
109
- queueCount: 0,
110
- runCount: 0,
111
- lastActivity: ''
112
- };
113
- if (source === 'queue') current.queueCount += 1;
114
- if (source === 'run') current.runCount += 1;
115
- if (timestamp && (!current.lastActivity || timestamp > current.lastActivity)) {
116
- current.lastActivity = timestamp;
117
- }
118
- workspaces.set(key, current);
119
- }
120
-
121
- function taskItemMatchesWorkspace(item, workspacePath) {
122
- const selectedWorkspacePath = normalizeTaskWorkspacePath(workspacePath);
123
- if (!selectedWorkspacePath) return true;
124
- return readTaskWorkspacePathFromItem(item) === selectedWorkspacePath;
125
- }
126
-
127
- function createTaskWorkspaceItems(taskOrchestration) {
128
- const state = taskOrchestration && typeof taskOrchestration === 'object' ? taskOrchestration : {};
129
- const workspaces = new Map();
130
- addTaskWorkspaceCandidate(workspaces, state.workspacePath, 'draft');
131
- const detail = state.selectedRunDetail && typeof state.selectedRunDetail === 'object' ? state.selectedRunDetail : null;
132
- if (detail) addTaskWorkspaceCandidate(workspaces, detail.cwd, 'detail');
133
- const queue = Array.isArray(state.queue) ? state.queue : [];
134
- const runs = Array.isArray(state.runs) ? state.runs : [];
135
- queue.forEach((item) => addTaskWorkspaceCandidate(workspaces, readTaskWorkspacePathFromItem(item), 'queue', readTaskItemTimestamp(item)));
136
- runs.forEach((item) => addTaskWorkspaceCandidate(workspaces, readTaskWorkspacePathFromItem(item), 'run', readTaskItemTimestamp(item)));
137
- const items = Array.from(workspaces.values()).sort((a, b) => {
138
- const byActivity = String(b.lastActivity || '').localeCompare(String(a.lastActivity || ''));
139
- if (byActivity !== 0) return byActivity;
140
- return a.label.localeCompare(b.label);
141
- });
142
- if (items.length === 0) {
143
- items.push({
144
- key: '__auto__',
145
- path: '',
146
- label: 'Auto workspace',
147
- meta: 'Server default cwd',
148
- queueCount: queue.length,
149
- runCount: runs.length,
150
- lastActivity: ''
151
- });
152
- }
153
- return items;
154
- }
155
-
156
- function createTaskWorkspaceSessions(taskOrchestration, workspacePath) {
157
- const state = taskOrchestration && typeof taskOrchestration === 'object' ? taskOrchestration : {};
158
- const queue = Array.isArray(state.queue) ? state.queue : [];
159
- const runs = Array.isArray(state.runs) ? state.runs : [];
160
- const queueSessions = queue
161
- .filter((item) => taskItemMatchesWorkspace(item, workspacePath))
162
- .map((item) => ({
163
- id: item.taskId || item.lastRunId || `queue-${readTaskItemTimestamp(item)}`,
164
- type: 'queue',
165
- status: item.status || item.runStatus || 'queued',
166
- title: item.title || item.target || item.taskId || 'Queued task',
167
- meta: item.threadId || item.cwd || item.taskId || '',
168
- taskId: item.taskId || '',
169
- runId: item.lastRunId || '',
170
- threadId: item.threadId || '',
171
- cwd: readTaskWorkspacePathFromItem(item),
172
- timestamp: readTaskItemTimestamp(item),
173
- active: true
174
- }));
175
- const runSessions = runs
176
- .filter((item) => taskItemMatchesWorkspace(item, workspacePath))
177
- .map((item) => ({
178
- id: item.runId || item.taskId || `run-${readTaskItemTimestamp(item)}`,
179
- type: 'run',
180
- status: item.status || 'completed',
181
- title: item.title || item.summary || item.taskId || item.runId || 'Run',
182
- meta: item.threadId || item.cwd || `${item.durationMs || 0}ms`,
183
- taskId: item.taskId || '',
184
- runId: item.runId || '',
185
- threadId: item.threadId || '',
186
- cwd: readTaskWorkspacePathFromItem(item),
187
- timestamp: readTaskItemTimestamp(item),
188
- active: false
189
- }));
190
- return queueSessions.concat(runSessions).sort((a, b) => {
191
- if (a.active !== b.active) return a.active ? -1 : 1;
192
- return String(b.timestamp || '').localeCompare(String(a.timestamp || ''));
193
- });
194
- }
195
-
196
- function createTaskConversationMessages(taskOrchestration, t = null) {
197
- const state = taskOrchestration && typeof taskOrchestration === 'object' ? taskOrchestration : {};
198
- const messages = [];
199
- const target = String(state.target || '').trim();
200
- const followUps = normalizeTaskDraftLines(state.followUpsText);
201
- const detail = state.selectedRunDetail && typeof state.selectedRunDetail === 'object' ? state.selectedRunDetail : null;
202
- const detailRun = detail && detail.run && typeof detail.run === 'object' ? detail.run : null;
203
- if (detail) {
204
- const fileLines = collectTaskRunWorkspaceFileLines(detail);
205
- const resultLines = [];
206
- if (detailRun && detailRun.summary) {
207
- resultLines.push(detailRun.summary);
208
- } else {
209
- resultLines.push(translateTaskText(t, 'orchestration.chat.assistant.contextFallback', '已选中一个历史任务,继续时会继承它的线程和工作区。'));
210
- }
211
- if (fileLines.length > 0) {
212
- resultLines.push(translateTaskText(t, 'orchestration.chat.assistant.filesSummary', `文件操作:${fileLines.join(';')}`, { files: fileLines.join(';') }));
213
- }
214
- messages.push({
215
- id: 'context-run',
216
- role: 'assistant',
217
- label: detailRun && detailRun.status === 'success'
218
- ? translateTaskText(t, 'orchestration.chat.assistant.resultLabel', '实现结果')
219
- : translateTaskText(t, 'orchestration.chat.assistant.contextLabel', '上一轮上下文'),
220
- text: resultLines.join('\n'),
221
- meta: formatTaskConversationMeta([
222
- detail.threadId ? translateTaskText(t, 'orchestration.chat.meta.thread', `线程 ${detail.threadId}`, { value: detail.threadId }) : '',
223
- detail.cwd ? translateTaskText(t, 'orchestration.chat.meta.workspace', `工作区 ${detail.cwd}`, { value: detail.cwd }) : '',
224
- detailRun && detailRun.status ? detailRun.status : ''
225
- ])
226
- });
227
- } else if (!target && followUps.length === 0) {
228
- messages.push({
229
- id: 'assistant-empty',
230
- role: 'assistant',
231
- label: translateTaskText(t, 'orchestration.chat.assistant.readyLabel', 'Codexmate'),
232
- text: translateTaskText(t, 'orchestration.chat.assistant.empty', '先发第一个需求;如果还有第二个需求,继续发送,Codexmate 会按顺序处理并保留上下文。'),
233
- meta: translateTaskText(t, 'orchestration.chat.meta.order', '顺序执行 · 保留上下文')
234
- });
235
- }
236
- if (target) {
237
- messages.push({
238
- id: 'user-target',
239
- role: 'user',
240
- label: translateTaskText(t, 'orchestration.chat.user.step', '需求 {count}', { count: 1 }),
241
- text: target,
242
- meta: translateTaskText(t, 'orchestration.chat.meta.first', '先完成这一条')
243
- });
244
- }
245
- followUps.forEach((item, index) => {
246
- const step = index + 2;
247
- messages.push({
248
- id: `user-follow-up-${index}`,
249
- role: 'user',
250
- label: translateTaskText(t, 'orchestration.chat.user.step', '需求 {count}', { count: step }),
251
- text: item,
252
- meta: translateTaskText(t, 'orchestration.chat.meta.afterPrevious', '等待前一条完成后继续')
253
- });
254
- });
255
- if (!state.plan && target) {
256
- messages.push({
257
- id: 'assistant-next',
258
- role: 'assistant',
259
- label: translateTaskText(t, 'orchestration.chat.assistant.readyLabel', 'Codexmate'),
260
- text: followUps.length > 0
261
- ? translateTaskText(t, 'orchestration.chat.assistant.sequenceReady', '已收到多条需求;执行时会先完成需求 1,再带着上下文继续后续需求。')
262
- : translateTaskText(t, 'orchestration.chat.assistant.singleReady', '已收到第一条需求。可以继续补需求 2,或直接预览并执行。'),
263
- meta: translateTaskText(t, 'orchestration.chat.meta.previewNext', '下一步:预览计划')
264
- });
265
- }
266
- return messages;
267
- }
268
-
269
- function translateTaskText(t, key, fallback, params = null) {
270
- if (typeof t !== 'function') return fallback;
271
- const translated = t(key, params);
272
- return translated === key ? fallback : translated;
273
- }
274
-
275
- function createTaskDraftChecklist(metrics, t = null) {
276
- const workflowReady = metrics.engine !== 'workflow' || metrics.workflowCount > 0;
277
- const scopeReady = metrics.hasNotes || !metrics.allowWrite;
278
- const previewReady = metrics.hasPlan && metrics.planIssues.length === 0;
279
- return [
280
- {
281
- key: 'target',
282
- label: translateTaskText(t, 'orchestration.readiness.target.label', '目标'),
283
- done: metrics.hasTarget,
284
- detail: metrics.hasTarget ? translateTaskText(t, 'orchestration.readiness.target.done', '已写目标') : translateTaskText(t, 'orchestration.readiness.target.missing', '还没写目标')
285
- },
286
- {
287
- key: 'sequence',
288
- label: translateTaskText(t, 'orchestration.readiness.sequence.label', '顺序'),
289
- done: metrics.hasTarget,
290
- detail: !metrics.hasTarget
291
- ? translateTaskText(t, 'orchestration.readiness.sequence.missing', '先发送需求 1')
292
- : (metrics.hasSequentialFollowUps
293
- ? translateTaskText(t, 'orchestration.readiness.sequence.multiple', '{count} 条需求会按顺序执行:先完成需求 1,再继续需求 2。', { count: metrics.requestCount })
294
- : translateTaskText(t, 'orchestration.readiness.sequence.single', '当前只有需求 1;继续发送会变成需求 2。'))
295
- },
296
- {
297
- key: 'engine',
298
- label: metrics.engine === 'workflow' ? 'Workflow' : translateTaskText(t, 'orchestration.readiness.engine.label', '执行策略'),
299
- done: workflowReady,
300
- detail: metrics.engine === 'workflow'
301
- ? (metrics.workflowCount > 0 ? translateTaskText(t, 'orchestration.readiness.workflow.done', `已选 ${metrics.workflowCount} 个 Workflow`, { count: metrics.workflowCount }) : translateTaskText(t, 'orchestration.readiness.workflow.missing', '还没选 Workflow ID'))
302
- : translateTaskText(t, 'orchestration.readiness.engine.openaiChat', '使用 OpenAI Chat-compatible 节点')
303
- },
304
- {
305
- key: 'scope',
306
- label: translateTaskText(t, 'orchestration.readiness.scope.label', '边界'),
307
- done: scopeReady,
308
- detail: metrics.hasNotes
309
- ? translateTaskText(t, 'orchestration.readiness.scope.done', '已补充说明')
310
- : (metrics.allowWrite ? translateTaskText(t, 'orchestration.readiness.scope.writeHint', '建议补说明后再写入') : translateTaskText(t, 'orchestration.readiness.scope.readonlyHint', '当前是只读,可直接试'))
311
- },
312
- {
313
- key: 'preview',
314
- label: translateTaskText(t, 'orchestration.readiness.preview.label', '预览'),
315
- done: previewReady,
316
- detail: !metrics.hasPlan
317
- ? translateTaskText(t, 'orchestration.readiness.preview.missing', '还没发送 /plan')
318
- : (metrics.planIssues.length > 0 ? translateTaskText(t, 'orchestration.readiness.preview.blocked', `有 ${metrics.planIssues.length} 个阻塞项`, { count: metrics.planIssues.length }) : translateTaskText(t, 'orchestration.readiness.preview.ready', `计划可用,${metrics.planNodeCount} 个节点`, { count: metrics.planNodeCount }))
319
- }
320
- ];
321
- }
322
-
323
- function createTaskDraftReadiness(metrics, t = null) {
324
- if (!metrics.hasTarget) {
325
- return {
326
- tone: 'neutral',
327
- title: translateTaskText(t, 'orchestration.readiness.empty.title', '先写目标'),
328
- summary: translateTaskText(t, 'orchestration.readiness.empty.summary', '先把想完成的结果写清楚,再让编排器拆节点。')
329
- };
330
- }
331
- if (metrics.engine === 'workflow' && metrics.workflowCount === 0) {
332
- return {
333
- tone: 'warn',
334
- title: translateTaskText(t, 'orchestration.readiness.workflow.title', '缺少 Workflow'),
335
- summary: translateTaskText(t, 'orchestration.readiness.workflow.summary', '你已经选了 Workflow 模式,但还没指定可复用流程。')
336
- };
337
- }
338
- if (!metrics.hasPlan) {
339
- return {
340
- tone: 'warn',
341
- title: translateTaskText(t, 'orchestration.readiness.preview.title', '建议先预览'),
342
- summary: metrics.hasSequentialFollowUps
343
- ? translateTaskText(t, 'orchestration.readiness.preview.sequenceSummary', '草稿已成形,已锁定 {count} 条顺序需求:先完成需求 1,再继续需求 2。', { count: metrics.requestCount })
344
- : translateTaskText(t, 'orchestration.readiness.preview.summary', '草稿已成形,发送 /plan 预览方案,确认节点和依赖再执行。')
345
- };
346
- }
347
- if (metrics.planIssues.length > 0) {
348
- return {
349
- tone: 'error',
350
- title: translateTaskText(t, 'orchestration.readiness.blocked.title', '预览有阻塞'),
351
- summary: translateTaskText(t, 'orchestration.readiness.blocked.summary', `当前计划里还有 ${metrics.planIssues.length} 个阻塞项,先处理它们。`, { count: metrics.planIssues.length })
352
- };
353
- }
354
- if (metrics.planWarnings.length > 0) {
355
- return {
356
- tone: 'warn',
357
- title: translateTaskText(t, 'orchestration.readiness.warn.title', '可以执行,但有提醒'),
358
- summary: translateTaskText(t, 'orchestration.readiness.warn.summary', `/plan 方案已生成,但还有 ${metrics.planWarnings.length} 条提醒值得先看一眼。`, { count: metrics.planWarnings.length })
359
- };
360
- }
361
- if (metrics.dryRun) {
362
- return {
363
- tone: 'success',
364
- title: translateTaskText(t, 'orchestration.readiness.dryRun.title', '适合先预演'),
365
- summary: translateTaskText(t, 'orchestration.readiness.dryRun.summary', '现在可以安全地跑一次仅预演,先看结果再决定是否真实执行。')
366
- };
367
- }
368
- return {
369
- tone: 'success',
370
- title: translateTaskText(t, 'orchestration.readiness.ready.title', '可以执行'),
371
- summary: metrics.followUpCount > 0
372
- ? translateTaskText(t, 'orchestration.readiness.ready.withFollowUps', `已锁定 ${metrics.requestCount} 条顺序需求:先完成需求 1,再带上下文继续需求 2。`, { count: metrics.requestCount })
373
- : translateTaskText(t, 'orchestration.readiness.ready.summary', '主目标已经够清楚了,可以直接执行或入队。')
374
- };
375
- }
376
-
377
1
  export function createMainTabsComputed() {
378
2
  return {
379
3
  mainTabKicker() {
@@ -381,7 +5,6 @@ export function createMainTabsComputed() {
381
5
  if (this.mainTab === 'config') return this.t('kicker.config');
382
6
  if (this.mainTab === 'sessions') return this.t('kicker.sessions');
383
7
  if (this.mainTab === 'usage') return this.t('kicker.usage');
384
- if (this.mainTab === 'orchestration') return this.t('kicker.orchestration');
385
8
  if (this.mainTab === 'market') return this.t('kicker.market');
386
9
  if (this.mainTab === 'plugins') return this.t('kicker.plugins');
387
10
  if (this.mainTab === 'docs') return this.t('kicker.docs');
@@ -394,7 +17,6 @@ export function createMainTabsComputed() {
394
17
  if (this.mainTab === 'config') return this.t('title.config');
395
18
  if (this.mainTab === 'sessions') return this.t('title.sessions');
396
19
  if (this.mainTab === 'usage') return this.t('title.usage');
397
- if (this.mainTab === 'orchestration') return this.t('title.orchestration');
398
20
  if (this.mainTab === 'market') return this.t('title.market');
399
21
  if (this.mainTab === 'plugins') return this.t('title.plugins');
400
22
  if (this.mainTab === 'docs') return this.t('title.docs');
@@ -407,108 +29,12 @@ export function createMainTabsComputed() {
407
29
  if (this.mainTab === 'config') return this.t('subtitle.config');
408
30
  if (this.mainTab === 'sessions') return this.t('subtitle.sessions');
409
31
  if (this.mainTab === 'usage') return this.t('subtitle.usage');
410
- if (this.mainTab === 'orchestration') return this.t('subtitle.orchestration');
411
32
  if (this.mainTab === 'market') return this.t('subtitle.market');
412
33
  if (this.mainTab === 'plugins') return this.t('subtitle.plugins');
413
34
  if (this.mainTab === 'docs') return this.t('subtitle.docs');
414
35
  if (this.mainTab === 'trash') return this.t('settings.trash.meta');
415
36
  if (this.mainTab === 'prompts') return this.t('subtitle.prompts');
416
37
  return this.t('subtitle.settings');
417
- },
418
- taskOrchestrationSelectedRun() {
419
- return this.taskOrchestration && this.taskOrchestration.selectedRunDetail
420
- ? this.taskOrchestration.selectedRunDetail
421
- : null;
422
- },
423
- taskOrchestrationSelectedRunNodes() {
424
- const detail = this.taskOrchestrationSelectedRun;
425
- const run = detail && detail.run && typeof detail.run === 'object' ? detail.run : {};
426
- if (detail && Array.isArray(detail.nodes)) return detail.nodes;
427
- return Array.isArray(run.nodes) ? run.nodes : [];
428
- },
429
- taskOrchestrationActiveQueue() {
430
- const queue = this.taskOrchestration && Array.isArray(this.taskOrchestration.queue)
431
- ? this.taskOrchestration.queue
432
- : [];
433
- return queue.filter((item) => {
434
- const status = String((item && (item.status || item.runStatus)) || '').trim().toLowerCase();
435
- return status === 'queued' || status === 'running';
436
- });
437
- },
438
- taskOrchestrationEngineLabel() {
439
- const state = this.taskOrchestration && typeof this.taskOrchestration === 'object'
440
- ? this.taskOrchestration
441
- : {};
442
- return state.selectedEngine === 'workflow' ? 'Workflow' : 'Codex';
443
- },
444
- taskOrchestrationWorkbenchVisible() {
445
- const state = this.taskOrchestration && typeof this.taskOrchestration === 'object'
446
- ? this.taskOrchestration
447
- : {};
448
- const activeQueue = Array.isArray(this.taskOrchestrationActiveQueue)
449
- ? this.taskOrchestrationActiveQueue
450
- : [];
451
- return activeQueue.length > 0
452
- || !!String(state.selectedRunId || '').trim()
453
- || !!String(state.selectedRunError || '').trim()
454
- || (state.workspaceTab === 'runs' && Array.isArray(state.runs) && state.runs.length > 0);
455
- },
456
- taskOrchestrationWorkspacePath() {
457
- const state = this.taskOrchestration && typeof this.taskOrchestration === 'object'
458
- ? this.taskOrchestration
459
- : {};
460
- const detail = state.selectedRunDetail && typeof state.selectedRunDetail === 'object'
461
- ? state.selectedRunDetail
462
- : null;
463
- return normalizeTaskWorkspacePath(state.workspacePath || (detail && detail.cwd) || '');
464
- },
465
- taskOrchestrationWorkspaceItems() {
466
- const items = createTaskWorkspaceItems(this.taskOrchestration);
467
- const selectedPath = this.taskOrchestrationWorkspacePath;
468
- return items.map((item) => ({
469
- ...item,
470
- active: normalizeTaskWorkspacePath(item.path) === selectedPath || (!selectedPath && !item.path)
471
- }));
472
- },
473
- taskOrchestrationWorkspaceQueue() {
474
- const queue = this.taskOrchestration && Array.isArray(this.taskOrchestration.queue)
475
- ? this.taskOrchestration.queue
476
- : [];
477
- return queue.filter((item) => taskItemMatchesWorkspace(item, this.taskOrchestrationWorkspacePath));
478
- },
479
- taskOrchestrationWorkspaceRuns() {
480
- const runs = this.taskOrchestration && Array.isArray(this.taskOrchestration.runs)
481
- ? this.taskOrchestration.runs
482
- : [];
483
- return runs.filter((item) => taskItemMatchesWorkspace(item, this.taskOrchestrationWorkspacePath));
484
- },
485
- taskOrchestrationWorkspaceSessions() {
486
- return createTaskWorkspaceSessions(this.taskOrchestration, this.taskOrchestrationWorkspacePath);
487
- },
488
- taskOrchestrationQueueStats() {
489
- const queue = this.taskOrchestration && Array.isArray(this.taskOrchestration.queue)
490
- ? this.taskOrchestration.queue
491
- : [];
492
- const stats = { queued: 0, running: 0, failed: 0 };
493
- for (const item of queue) {
494
- const status = String((item && (item.status || item.runStatus)) || '').trim().toLowerCase();
495
- if (status === 'queued') stats.queued += 1;
496
- else if (status === 'running') stats.running += 1;
497
- else if (status === 'failed') stats.failed += 1;
498
- }
499
- return stats;
500
- },
501
- taskOrchestrationDraftMetrics() {
502
- return readTaskOrchestrationDraftMetrics(this.taskOrchestration);
503
- },
504
- taskOrchestrationDraftChecklist() {
505
- return createTaskDraftChecklist(this.taskOrchestrationDraftMetrics, this.t && this.t.bind(this));
506
- },
507
- taskOrchestrationDraftReadiness() {
508
- return createTaskDraftReadiness(this.taskOrchestrationDraftMetrics, this.t && this.t.bind(this));
509
- },
510
- taskOrchestrationConversationMessages() {
511
- return createTaskConversationMessages(this.taskOrchestration, this.t && this.t.bind(this));
512
38
  }
513
39
  };
514
40
  }
@@ -20,6 +20,22 @@ function isValidOpenclawWorkspaceFileName(fileName) {
20
20
  return true;
21
21
  }
22
22
 
23
+ function sanitizePromptHistoryId(raw) {
24
+ const safe = typeof raw === 'string' ? raw.trim() : '';
25
+ if (!safe) return 'global';
26
+ const replaced = safe.replace(/[^A-Za-z0-9_.-]/g, '_');
27
+ return (replaced || 'global').slice(0, 64);
28
+ }
29
+
30
+ function resolveAgentsHistoryBucket(instance) {
31
+ const subTab = typeof instance.promptsSubTab === 'string' ? instance.promptsSubTab : 'codex';
32
+ if (subTab === 'claude-project') {
33
+ const projectPath = (instance.projectClaudeMdPath || '').trim();
34
+ return 'claude-project_' + sanitizePromptHistoryId(projectPath || 'global');
35
+ }
36
+ return 'codex_global';
37
+ }
38
+
23
39
  export function createAgentsMethods(options = {}) {
24
40
  const {
25
41
  api,
@@ -681,6 +697,9 @@ export function createAgentsMethods(options = {}) {
681
697
  this.showMessage(res.error, 'error');
682
698
  return;
683
699
  }
700
+ if (typeof res.historyBucket === 'string' && res.historyBucket) {
701
+ this.promptHistoryBucket = res.historyBucket;
702
+ }
684
703
  const successLabel = this.agentsContext === 'openclaw-workspace'
685
704
  ? this.t('toast.agents.saved.workspace', { name: this.agentsWorkspaceFileName || '' }).replace(/:\s*$/, '')
686
705
  : (this.agentsContext === 'claude-project'
@@ -884,6 +903,77 @@ export function createAgentsMethods(options = {}) {
884
903
  await this.persistPromptPresets();
885
904
  this.showMessage(this.t('prompts.presets.toast.deleted'), 'success');
886
905
  },
906
+ async openPromptHistory() {
907
+ if (this.promptHistoryLoading) return;
908
+ const bucket = this.promptHistoryBucket || resolveAgentsHistoryBucket(this);
909
+ this.promptHistoryVisible = true;
910
+ this.promptHistoryError = '';
911
+ await this.loadPromptHistory(bucket);
912
+ },
913
+
914
+ async loadPromptHistory(bucketArg) {
915
+ if (this.promptHistoryLoading) return;
916
+ const bucket = typeof bucketArg === 'string' && bucketArg.trim()
917
+ ? bucketArg.trim()
918
+ : (this.promptHistoryBucket || resolveAgentsHistoryBucket(this));
919
+ this.promptHistoryBucket = bucket;
920
+ this.promptHistoryLoading = true;
921
+ this.promptHistoryError = '';
922
+ this.promptHistoryItems = [];
923
+ this.promptHistoryPreviewId = '';
924
+ this.promptHistoryPreviewContent = '';
925
+ try {
926
+ const res = await api('list-prompt-history', { bucket });
927
+ if (res && res.error) {
928
+ this.promptHistoryError = res.error;
929
+ return;
930
+ }
931
+ this.promptHistoryItems = Array.isArray(res) ? res : [];
932
+ } catch (e) {
933
+ this.promptHistoryError = this.t('toast.load.fail');
934
+ } finally {
935
+ this.promptHistoryLoading = false;
936
+ }
937
+ },
938
+
939
+ closePromptHistory() {
940
+ this.promptHistoryVisible = false;
941
+ this.promptHistoryPreviewId = '';
942
+ this.promptHistoryPreviewContent = '';
943
+ this.promptHistoryError = '';
944
+ },
945
+
946
+ async viewPromptHistoryItem(item) {
947
+ if (!item || !item.id) return;
948
+ if (this.promptHistoryLoading) return;
949
+ if (this.promptHistoryPreviewId === item.id && this.promptHistoryPreviewContent) return;
950
+ this.promptHistoryPreviewId = item.id;
951
+ this.promptHistoryPreviewContent = '';
952
+ try {
953
+ const res = await api('get-prompt-history', { bucket: this.promptHistoryBucket, id: item.id });
954
+ if (res && res.error) {
955
+ this.promptHistoryError = res.error;
956
+ this.promptHistoryPreviewId = '';
957
+ return;
958
+ }
959
+ this.promptHistoryPreviewContent = typeof res.content === 'string' ? res.content : '';
960
+ } catch (e) {
961
+ this.promptHistoryError = this.t('toast.load.fail');
962
+ this.promptHistoryPreviewId = '';
963
+ }
964
+ },
965
+
966
+ applyPromptHistoryToEditor() {
967
+ if (!this.promptHistoryPreviewContent) return;
968
+ if (this.agentsSaving || this.agentsDiffVisible) return;
969
+ this.agentsContent = this.promptHistoryPreviewContent;
970
+ if (typeof this.onAgentsContentInput === 'function') this.onAgentsContentInput();
971
+ this.promptHistoryVisible = false;
972
+ this.promptHistoryPreviewId = '';
973
+ this.promptHistoryPreviewContent = '';
974
+ this.showMessage(this.t('toast.history.restored'), 'success');
975
+ },
976
+
887
977
  switchPromptsSubTab(subTab) {
888
978
  const normalized = subTab === 'claude-project' || subTab === 'system' ? subTab : 'codex';
889
979
  if (normalized === 'claude-project' && !this.projectPathOptions.length && !this.projectPathOptionsLoading) {
@@ -26,7 +26,6 @@ import { createProvidersMethods } from './app.methods.providers.mjs';
26
26
  import { createProviderCacheMethods } from './app.methods.provider-cache.mjs';
27
27
  import { createRuntimeMethods } from './app.methods.runtime.mjs';
28
28
  import { createToolConfigPermissionMethods } from './app.methods.tool-config-permissions.mjs';
29
- import { createTaskOrchestrationMethods } from './app.methods.task-orchestration.mjs';
30
29
  import { createSessionActionMethods } from './app.methods.session-actions.mjs';
31
30
  import { createSessionBrowserMethods } from './app.methods.session-browser.mjs';
32
31
  import { createSessionTimelineMethods } from './app.methods.session-timeline.mjs';
@@ -104,7 +103,6 @@ export function createAppMethods() {
104
103
  }),
105
104
  ...createKilocodeConfigMethods({ api }),
106
105
  ...createInstallMethods({ api }),
107
- ...createRuntimeMethods({ api }),
108
- ...createTaskOrchestrationMethods({ api })
106
+ ...createRuntimeMethods({ api })
109
107
  };
110
108
  }
@@ -9,7 +9,6 @@
9
9
  'config',
10
10
  'sessions',
11
11
  'usage',
12
- 'orchestration',
13
12
  'market',
14
13
  'plugins',
15
14
  'docs',
@@ -18,7 +17,7 @@
18
17
  'prompts'
19
18
  ];
20
19
  const MAIN_TAB_SET = new Set(MAIN_TAB_ORDER);
21
- const DISABLED_MAIN_TAB_SET = new Set(['orchestration']);
20
+ const DISABLED_MAIN_TAB_SET = new Set();
22
21
  const normalizeMainTab = (tab) => typeof tab === 'string'
23
22
  ? tab.trim().toLowerCase()
24
23
  : '';
@@ -146,7 +145,13 @@
146
145
  if (typeof this.ensureMainTabSwitchState === 'function') {
147
146
  this.ensureMainTabSwitchState().pendingConfigMode = '';
148
147
  }
149
- this.configMode = configModeSet.has(normalizedMode) ? normalizedMode : 'codex';
148
+ const resolvedMode = configModeSet.has(normalizedMode) ? normalizedMode : 'codex';
149
+ if (typeof this.isConfigModeVisible === 'function' && !this.isConfigModeVisible(resolvedMode)) {
150
+ const fallback = ['codex', 'claude', 'openclaw', 'opencode', 'kilocode'].find(m => this.isConfigModeVisible(m)) || 'codex';
151
+ this.configMode = configModeSet.has(fallback) ? fallback : 'codex';
152
+ } else {
153
+ this.configMode = resolvedMode;
154
+ }
150
155
  if (this.mainTab === 'config') {
151
156
  if (this.configMode === 'kilocode' && typeof this.loadKilocodeConfig === 'function') {
152
157
  this.loadKilocodeConfig();