codexmate 0.0.56 → 0.0.57

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 (31) hide show
  1. package/cli.js +680 -129
  2. package/lib/task-orchestrator.js +90 -21
  3. package/lib/task-workspace-chat.js +292 -0
  4. package/package.json +2 -2
  5. package/web-ui/app.js +55 -129
  6. package/web-ui/modules/app.computed.main-tabs.mjs +304 -7
  7. package/web-ui/modules/app.methods.agents.mjs +3 -0
  8. package/web-ui/modules/app.methods.claude-config.mjs +65 -25
  9. package/web-ui/modules/app.methods.navigation.mjs +67 -83
  10. package/web-ui/modules/app.methods.openclaw-editing.mjs +3 -6
  11. package/web-ui/modules/app.methods.session-actions.mjs +1 -12
  12. package/web-ui/modules/app.methods.session-browser.mjs +23 -54
  13. package/web-ui/modules/app.methods.session-trash.mjs +0 -1
  14. package/web-ui/modules/app.methods.startup-claude.mjs +16 -31
  15. package/web-ui/modules/app.methods.task-orchestration.mjs +347 -8
  16. package/web-ui/modules/app.methods.tool-config-permissions.mjs +0 -3
  17. package/web-ui/modules/app.methods.web-ui-preferences.mjs +415 -68
  18. package/web-ui/modules/config-template-confirm-pref.mjs +2 -3
  19. package/web-ui/modules/i18n/locales/en.mjs +146 -26
  20. package/web-ui/modules/i18n/locales/ja.mjs +143 -23
  21. package/web-ui/modules/i18n/locales/vi.mjs +145 -25
  22. package/web-ui/modules/i18n/locales/zh-tw.mjs +146 -26
  23. package/web-ui/modules/i18n/locales/zh.mjs +148 -28
  24. package/web-ui/modules/i18n.mjs +5 -12
  25. package/web-ui/modules/sessions-filters-url.mjs +1 -2
  26. package/web-ui/partials/index/layout-header.html +37 -14
  27. package/web-ui/partials/index/panel-orchestration.html +489 -282
  28. package/web-ui/res/web-ui-render.precompiled.js +1049 -610
  29. package/web-ui/styles/layout-shell.css +157 -1
  30. package/web-ui/styles/navigation-panels.css +11 -0
  31. package/web-ui/styles/task-orchestration.css +2161 -4
@@ -7,11 +7,14 @@ function createDefaultTaskOrchestrationState() {
7
7
  queueStarting: false,
8
8
  retrying: false,
9
9
  target: '',
10
+ chatDraft: '',
10
11
  title: '',
11
12
  notes: '',
13
+ workspacePath: '',
14
+ threadId: '',
12
15
  followUpsText: '',
13
16
  workflowIdsText: '',
14
- selectedEngine: 'codex',
17
+ selectedEngine: 'openai-chat',
15
18
  runMode: 'write',
16
19
  concurrency: 2,
17
20
  autoFixRounds: 1,
@@ -20,6 +23,7 @@ function createDefaultTaskOrchestrationState() {
20
23
  planIssues: [],
21
24
  planWarnings: [],
22
25
  overviewWarnings: [],
26
+ openAiChatStatus: null,
23
27
  workflows: [],
24
28
  queue: [],
25
29
  runs: [],
@@ -29,6 +33,7 @@ function createDefaultTaskOrchestrationState() {
29
33
  selectedRunLoading: false,
30
34
  selectedRunError: '',
31
35
  detailRequestToken: 0,
36
+ settingsOpen: false,
32
37
  lastLoadedAt: '',
33
38
  lastError: ''
34
39
  };
@@ -41,6 +46,81 @@ function normalizeLines(text) {
41
46
  .filter(Boolean);
42
47
  }
43
48
 
49
+ let taskOrchestrationScrollObserver = null;
50
+
51
+ function scrollTaskOrchestrationThreadToEnd() {
52
+ if (typeof document === 'undefined') {
53
+ return;
54
+ }
55
+ const alignLatestCard = (remainingAttempts = 20) => {
56
+ const panel = document.querySelector('#panel-orchestration');
57
+ const thread = panel && panel.querySelector('.task-chat-thread');
58
+ if (!panel || !thread) {
59
+ return;
60
+ }
61
+ const latestCard = Array.from(panel.querySelectorAll('.task-thread-plan-request, .task-thread-message-card')).pop()
62
+ || thread.lastElementChild;
63
+ if (latestCard && Number.isFinite(latestCard.offsetTop)) {
64
+ if (thread.contains(latestCard)) {
65
+ thread.scrollTop = Math.max(0, latestCard.offsetTop - thread.offsetTop - 12);
66
+ }
67
+ const composer = document.querySelector('#panel-orchestration .task-thread-composer');
68
+ const composerRect = composer && typeof composer.getBoundingClientRect === 'function'
69
+ ? composer.getBoundingClientRect()
70
+ : null;
71
+ const latestRect = typeof latestCard.getBoundingClientRect === 'function'
72
+ ? latestCard.getBoundingClientRect()
73
+ : null;
74
+ if (composerRect && latestRect) {
75
+ const safeBottom = composerRect.top - 20;
76
+ const overlap = latestRect.bottom - safeBottom;
77
+ if (overlap > 0) {
78
+ let scrollElement = latestCard.parentElement;
79
+ while (scrollElement && scrollElement !== document.body) {
80
+ const style = window.getComputedStyle(scrollElement);
81
+ const canScroll = /(auto|scroll)/.test(style.overflowY) && scrollElement.scrollHeight > scrollElement.clientHeight;
82
+ if (canScroll) {
83
+ break;
84
+ }
85
+ scrollElement = scrollElement.parentElement;
86
+ }
87
+ if (!scrollElement || scrollElement === document.body) {
88
+ scrollElement = document.scrollingElement || document.documentElement;
89
+ }
90
+ const maxScrollTop = Math.max(0, scrollElement.scrollHeight - scrollElement.clientHeight);
91
+ const targetScrollTop = Math.min(maxScrollTop, scrollElement.scrollTop + overlap);
92
+ if (targetScrollTop > scrollElement.scrollTop) {
93
+ scrollElement.scrollTo({ top: targetScrollTop, behavior: 'auto' });
94
+ }
95
+ }
96
+ }
97
+ if (remainingAttempts > 0) {
98
+ setTimeout(() => alignLatestCard(remainingAttempts - 1), 160);
99
+ }
100
+ return;
101
+ }
102
+ thread.scrollTop = thread.scrollHeight;
103
+ if (remainingAttempts > 0) {
104
+ setTimeout(() => alignLatestCard(remainingAttempts - 1), 160);
105
+ }
106
+ };
107
+ setTimeout(() => alignLatestCard(), 0);
108
+ const panel = document.querySelector('#panel-orchestration');
109
+ if (panel && typeof MutationObserver !== 'undefined') {
110
+ if (taskOrchestrationScrollObserver) {
111
+ taskOrchestrationScrollObserver.disconnect();
112
+ }
113
+ taskOrchestrationScrollObserver = new MutationObserver(() => alignLatestCard(2));
114
+ taskOrchestrationScrollObserver.observe(panel, { childList: true, subtree: true });
115
+ setTimeout(() => {
116
+ if (taskOrchestrationScrollObserver) {
117
+ taskOrchestrationScrollObserver.disconnect();
118
+ taskOrchestrationScrollObserver = null;
119
+ }
120
+ }, 4000);
121
+ }
122
+ }
123
+
44
124
  function normalizePositiveInteger(value, fallback, min = 1, max = 8) {
45
125
  const numeric = Number.parseInt(String(value), 10);
46
126
  if (!Number.isFinite(numeric)) {
@@ -69,6 +149,12 @@ function normalizeTaskRunMode(value) {
69
149
  return 'write';
70
150
  }
71
151
 
152
+ function normalizeTaskSelectedEngine(value) {
153
+ const normalized = String(value || '').trim().toLowerCase().replace(/[\s_]+/g, '-');
154
+ if (normalized === 'workflow') return 'workflow';
155
+ return 'openai-chat';
156
+ }
157
+
72
158
  function buildRunModeFlags(runMode) {
73
159
  const normalized = normalizeTaskRunMode(runMode);
74
160
  return {
@@ -78,6 +164,21 @@ function buildRunModeFlags(runMode) {
78
164
  };
79
165
  }
80
166
 
167
+ function clearTaskRunSelectionForDraft(state) {
168
+ if (!state || typeof state !== 'object') {
169
+ return;
170
+ }
171
+ state.selectedRunId = '';
172
+ state.selectedRunDetail = null;
173
+ state.selectedRunError = '';
174
+ state.selectedRunLoading = false;
175
+ state.workspaceTab = 'queue';
176
+ }
177
+
178
+ function normalizeTaskWorkspaceInputPath(value) {
179
+ return String(value || '').trim().replace(/\\+/g, '/').replace(/\/$/, '');
180
+ }
181
+
81
182
  export function createTaskOrchestrationMethods(options = {}) {
82
183
  const { api } = options;
83
184
 
@@ -92,6 +193,8 @@ export function createTaskOrchestrationMethods(options = {}) {
92
193
  }
93
194
  }
94
195
  current.runMode = normalizeTaskRunMode(current.runMode);
196
+ current.selectedEngine = normalizeTaskSelectedEngine(current.selectedEngine);
197
+ current.chatDraft = String(current.chatDraft || '');
95
198
  return current;
96
199
  }
97
200
  this.taskOrchestration = createDefaultTaskOrchestrationState();
@@ -101,13 +204,20 @@ export function createTaskOrchestrationMethods(options = {}) {
101
204
  buildTaskOrchestrationRequest() {
102
205
  const state = this.ensureTaskOrchestrationState();
103
206
  const flags = buildRunModeFlags(state.runMode);
207
+ const selectedEngine = normalizeTaskSelectedEngine(state.selectedEngine);
208
+ const workflowIds = selectedEngine === 'workflow' ? normalizeLines(state.workflowIdsText) : [];
209
+ if (selectedEngine !== 'workflow' && state.workflowIdsText) {
210
+ state.workflowIdsText = '';
211
+ }
104
212
  return {
105
213
  title: String(state.title || '').trim(),
106
214
  target: String(state.target || '').trim(),
107
215
  notes: String(state.notes || '').trim(),
216
+ cwd: String(state.workspacePath || '').trim(),
217
+ threadId: String(state.threadId || '').trim(),
108
218
  followUps: normalizeLines(state.followUpsText),
109
- workflowIds: normalizeLines(state.workflowIdsText),
110
- engine: String(state.selectedEngine || 'codex').trim().toLowerCase() === 'workflow' ? 'workflow' : 'codex',
219
+ workflowIds,
220
+ engine: selectedEngine,
111
221
  allowWrite: flags.allowWrite,
112
222
  dryRun: flags.dryRun,
113
223
  concurrency: normalizePositiveInteger(state.concurrency, 2, 1, 8),
@@ -121,6 +231,8 @@ export function createTaskOrchestrationMethods(options = {}) {
121
231
  title: req.title,
122
232
  target: req.target,
123
233
  notes: req.notes,
234
+ cwd: req.cwd,
235
+ threadId: req.threadId,
124
236
  followUps: req.followUps,
125
237
  workflowIds: req.workflowIds,
126
238
  engine: req.engine,
@@ -151,6 +263,33 @@ export function createTaskOrchestrationMethods(options = {}) {
151
263
  return logs.map((item) => `${item && item.at ? item.at : ''} ${item && item.level ? item.level : ''} ${item && item.message ? item.message : ''}`.trim()).join('\n');
152
264
  },
153
265
 
266
+ formatTaskNodeOutputText(node) {
267
+ const output = node && node.output && typeof node.output === 'object' ? node.output : null;
268
+ const text = output && typeof output.text === 'string' ? output.text.trim() : '';
269
+ return text || '(no output)';
270
+ },
271
+
272
+ openTaskOpenAiChatConfig() {
273
+ if (typeof this.switchMainTab === 'function') {
274
+ this.switchMainTab('config');
275
+ } else {
276
+ this.mainTab = 'config';
277
+ }
278
+ this.configMode = 'codex';
279
+ const status = this.taskOrchestration && this.taskOrchestration.openAiChatStatus
280
+ ? this.taskOrchestration.openAiChatStatus
281
+ : null;
282
+ const providerName = status && typeof status.providerName === 'string' ? status.providerName.trim() : '';
283
+ const provider = providerName && Array.isArray(this.providersList)
284
+ ? this.providersList.find((item) => item && item.name === providerName)
285
+ : null;
286
+ if (provider && typeof this.openEditModal === 'function') {
287
+ this.openEditModal(provider);
288
+ } else if (typeof this.openAddProviderModal === 'function') {
289
+ this.openAddProviderModal();
290
+ }
291
+ },
292
+
154
293
  appendTaskWorkflowId(workflowId) {
155
294
  const state = this.ensureTaskOrchestrationState();
156
295
  const normalizedWorkflowId = typeof workflowId === 'string' ? workflowId.trim() : '';
@@ -188,11 +327,11 @@ export function createTaskOrchestrationMethods(options = {}) {
188
327
  state.workflows = Array.isArray(res && res.workflows) ? res.workflows : [];
189
328
  state.queue = Array.isArray(res && res.queue) ? res.queue : [];
190
329
  state.runs = Array.isArray(res && res.runs) ? res.runs : [];
330
+ state.openAiChatStatus = res && res.openAiChatStatus && typeof res.openAiChatStatus === 'object'
331
+ ? res.openAiChatStatus
332
+ : null;
191
333
  state.overviewWarnings = Array.isArray(res && res.warnings) ? res.warnings : [];
192
334
  state.lastLoadedAt = new Date().toISOString();
193
- if (!state.selectedRunId && state.runs.length > 0) {
194
- state.selectedRunId = state.runs[0].runId || '';
195
- }
196
335
  const shouldRefreshSelectedDetail = !!state.selectedRunId
197
336
  && (options.includeDetail !== false
198
337
  || (state.selectedRunDetail && this.isTaskRunActive(state.selectedRunDetail && state.selectedRunDetail.run && state.selectedRunDetail.run.status)));
@@ -213,18 +352,74 @@ export function createTaskOrchestrationMethods(options = {}) {
213
352
  }
214
353
  },
215
354
 
355
+ appendTaskChatMessage() {
356
+ const state = this.ensureTaskOrchestrationState();
357
+ const message = String(state.chatDraft || '').trim();
358
+ if (!message) {
359
+ return false;
360
+ }
361
+ if (!String(state.target || '').trim()) {
362
+ state.target = message;
363
+ } else {
364
+ const existing = normalizeLines(state.followUpsText);
365
+ state.followUpsText = existing.concat(message).join('\n');
366
+ }
367
+ state.chatDraft = '';
368
+ state.plan = null;
369
+ state.planFingerprint = '';
370
+ state.planIssues = [];
371
+ state.planWarnings = [];
372
+ state.lastError = '';
373
+ clearTaskRunSelectionForDraft(state);
374
+ scrollTaskOrchestrationThreadToEnd();
375
+ return true;
376
+ },
377
+
378
+ async submitTaskOrchestrationChatMessage() {
379
+ const state = this.ensureTaskOrchestrationState();
380
+ const rawMessage = String(state.chatDraft || '').trim();
381
+ if (!rawMessage) {
382
+ return false;
383
+ }
384
+ const planCommand = rawMessage.match(/^\/plan(?:\s+([\s\S]*))?$/i);
385
+ if (!planCommand) {
386
+ return this.appendTaskChatMessage();
387
+ }
388
+ const planTarget = String(planCommand[1] || '').trim();
389
+ if (planTarget) {
390
+ state.chatDraft = planTarget;
391
+ if (!this.appendTaskChatMessage()) {
392
+ return false;
393
+ }
394
+ } else if (!String(state.target || '').trim()) {
395
+ this.showMessage('先输入任务需求,再发送 /plan', 'error');
396
+ return false;
397
+ } else {
398
+ state.chatDraft = '';
399
+ }
400
+ return this.previewTaskPlan({ silent: false });
401
+ },
402
+
216
403
  async previewTaskPlan(options = {}) {
217
404
  const state = this.ensureTaskOrchestrationState();
218
405
  if (state.planning) {
219
406
  return null;
220
407
  }
408
+ clearTaskRunSelectionForDraft(state);
221
409
  state.planning = true;
222
410
  try {
223
- const res = await api('task-plan', this.buildTaskOrchestrationRequest());
411
+ const res = await api('task-plan', {
412
+ ...this.buildTaskOrchestrationRequest(),
413
+ previewOnly: true
414
+ });
224
415
  state.plan = res && res.plan ? res.plan : null;
416
+ if (state.plan && state.plan.threadId && !state.threadId) {
417
+ state.threadId = state.plan.threadId;
418
+ }
225
419
  state.planIssues = Array.isArray(res && res.issues) ? res.issues : [];
226
420
  state.planWarnings = Array.isArray(res && res.warnings) ? res.warnings : [];
227
421
  state.planFingerprint = state.plan ? this.buildTaskOrchestrationFingerprint() : '';
422
+ scrollTaskOrchestrationThreadToEnd();
228
423
  if (res && res.error) {
229
424
  if (!options.silent) {
230
425
  this.showMessage(res.error, 'error');
@@ -249,6 +444,24 @@ export function createTaskOrchestrationMethods(options = {}) {
249
444
  }
250
445
  },
251
446
 
447
+ async previewTaskPlanFromChat() {
448
+ const state = this.ensureTaskOrchestrationState();
449
+ if (state.planning || state.running) {
450
+ return null;
451
+ }
452
+ if (String(state.chatDraft || '').trim()) {
453
+ const appended = this.appendTaskChatMessage();
454
+ if (!appended) {
455
+ return null;
456
+ }
457
+ }
458
+ if (!String(state.target || '').trim()) {
459
+ this.showMessage('先输入任务需求,再探讨方案', 'error');
460
+ return null;
461
+ }
462
+ return this.previewTaskPlan({ silent: false });
463
+ },
464
+
252
465
  async runTaskOrchestration() {
253
466
  const state = this.ensureTaskOrchestrationState();
254
467
  if (state.running) {
@@ -265,6 +478,9 @@ export function createTaskOrchestrationMethods(options = {}) {
265
478
  return res;
266
479
  }
267
480
  state.selectedRunId = res.runId || state.selectedRunId;
481
+ if (res && res.threadId) {
482
+ state.threadId = res.threadId;
483
+ }
268
484
  await this.loadTaskOrchestrationOverview({ silent: true, includeDetail: false });
269
485
  if (state.selectedRunId) {
270
486
  await this.loadTaskRunDetail(state.selectedRunId, { silent: true });
@@ -295,6 +511,9 @@ export function createTaskOrchestrationMethods(options = {}) {
295
511
  }
296
512
  return res;
297
513
  }
514
+ if (res && res.task && res.task.threadId) {
515
+ state.threadId = res.task.threadId;
516
+ }
298
517
  if (!options.deferRefresh) {
299
518
  await this.loadTaskOrchestrationOverview({ silent: true, includeDetail: false });
300
519
  }
@@ -340,6 +559,24 @@ export function createTaskOrchestrationMethods(options = {}) {
340
559
  return this.runTaskOrchestration();
341
560
  },
342
561
 
562
+ async planAndRunTaskOrchestrationFromChat() {
563
+ const state = this.ensureTaskOrchestrationState();
564
+ if (state.running || state.planning) {
565
+ return null;
566
+ }
567
+ if (String(state.chatDraft || '').trim()) {
568
+ const appended = this.appendTaskChatMessage();
569
+ if (!appended) {
570
+ return null;
571
+ }
572
+ }
573
+ if (!String(state.target || '').trim()) {
574
+ this.showMessage('先输入任务需求,再开始执行', 'error');
575
+ return null;
576
+ }
577
+ return this.planAndRunTaskOrchestration();
578
+ },
579
+
343
580
  async queueTaskOrchestrationAndStart() {
344
581
  const state = this.ensureTaskOrchestrationState();
345
582
  if (state.queueAdding || state.queueStarting || state.planning || state.running) {
@@ -418,6 +655,7 @@ export function createTaskOrchestrationMethods(options = {}) {
418
655
  }
419
656
  state.selectedRunDetail = res;
420
657
  state.selectedRunError = '';
658
+ scrollTaskOrchestrationThreadToEnd();
421
659
  this.syncTaskOrchestrationPolling();
422
660
  return res;
423
661
  } catch (error) {
@@ -441,6 +679,76 @@ export function createTaskOrchestrationMethods(options = {}) {
441
679
  return this.loadTaskRunDetail(runId, { silent: false, switchToDetail: true });
442
680
  },
443
681
 
682
+ selectTaskWorkspace(workspacePath) {
683
+ const state = this.ensureTaskOrchestrationState();
684
+ const nextWorkspacePath = normalizeTaskWorkspaceInputPath(workspacePath);
685
+ const previousWorkspacePath = normalizeTaskWorkspaceInputPath(state.workspacePath);
686
+ state.workspacePath = nextWorkspacePath;
687
+ state.workspaceTab = 'runs';
688
+ if (nextWorkspacePath !== previousWorkspacePath) {
689
+ state.plan = null;
690
+ state.planFingerprint = '';
691
+ state.planIssues = [];
692
+ state.planWarnings = [];
693
+ const selectedDetailWorkspacePath = normalizeTaskWorkspaceInputPath(state.selectedRunDetail && state.selectedRunDetail.cwd);
694
+ if (selectedDetailWorkspacePath && selectedDetailWorkspacePath !== nextWorkspacePath) {
695
+ state.selectedRunId = '';
696
+ state.selectedRunDetail = null;
697
+ state.selectedRunError = '';
698
+ }
699
+ }
700
+ this.syncTaskOrchestrationPolling();
701
+ },
702
+
703
+ startNewTaskWorkspaceSession(workspacePath = '') {
704
+ const state = this.ensureTaskOrchestrationState();
705
+ const selectedWorkspacePath = normalizeTaskWorkspaceInputPath(workspacePath || state.workspacePath || (state.selectedRunDetail && state.selectedRunDetail.cwd));
706
+ state.workspacePath = selectedWorkspacePath;
707
+ state.threadId = '';
708
+ state.target = '';
709
+ state.chatDraft = '';
710
+ state.title = '';
711
+ state.notes = '';
712
+ state.followUpsText = '';
713
+ state.plan = null;
714
+ state.planFingerprint = '';
715
+ state.planIssues = [];
716
+ state.planWarnings = [];
717
+ state.selectedRunId = '';
718
+ state.selectedRunDetail = null;
719
+ state.selectedRunError = '';
720
+ state.selectedRunLoading = false;
721
+ state.workspaceTab = 'queue';
722
+ state.lastError = '';
723
+ this.showMessage(selectedWorkspacePath ? '已为当前工作区开始新会话' : '已开始新任务会话', 'success');
724
+ scrollTaskOrchestrationThreadToEnd();
725
+ this.syncTaskOrchestrationPolling();
726
+ },
727
+
728
+ async continueTaskWorkspaceSession(session) {
729
+ const item = session && typeof session === 'object' ? session : {};
730
+ const workspacePath = normalizeTaskWorkspaceInputPath(item.cwd || item.workspacePath || '');
731
+ const threadId = String(item.threadId || '').trim();
732
+ const runId = String(item.runId || '').trim();
733
+ const taskId = String(item.taskId || '').trim();
734
+ const state = this.ensureTaskOrchestrationState();
735
+ if (workspacePath) state.workspacePath = workspacePath;
736
+ if (threadId) state.threadId = threadId;
737
+ if (runId) {
738
+ await this.loadTaskRunDetail(runId, { silent: true, switchToDetail: true });
739
+ this.continueTaskThreadFromUi();
740
+ return;
741
+ }
742
+ state.selectedRunId = '';
743
+ state.selectedRunDetail = null;
744
+ state.selectedRunError = '';
745
+ state.workspaceTab = 'queue';
746
+ state.title = String(item.title || state.title || '').trim();
747
+ this.showMessage(taskId ? `已恢复队列任务上下文: ${taskId}` : '已恢复会话上下文', 'success');
748
+ scrollTaskOrchestrationThreadToEnd();
749
+ this.syncTaskOrchestrationPolling();
750
+ },
751
+
444
752
  async retryTaskRunFromUi(runId) {
445
753
  const state = this.ensureTaskOrchestrationState();
446
754
  const normalizedRunId = String(runId || '').trim();
@@ -455,6 +763,9 @@ export function createTaskOrchestrationMethods(options = {}) {
455
763
  return res;
456
764
  }
457
765
  state.selectedRunId = res.runId || state.selectedRunId;
766
+ if (res && res.threadId) {
767
+ state.threadId = res.threadId;
768
+ }
458
769
  await this.loadTaskOrchestrationOverview({ silent: true, includeDetail: false });
459
770
  if (state.selectedRunId) {
460
771
  await this.loadTaskRunDetail(state.selectedRunId, { silent: true });
@@ -496,6 +807,31 @@ export function createTaskOrchestrationMethods(options = {}) {
496
807
  }
497
808
  },
498
809
 
810
+ continueTaskThreadFromUi() {
811
+ const state = this.ensureTaskOrchestrationState();
812
+ const detail = state.selectedRunDetail && typeof state.selectedRunDetail === 'object' ? state.selectedRunDetail : null;
813
+ if (!detail) {
814
+ return;
815
+ }
816
+ const continuedEngine = normalizeTaskSelectedEngine(detail.engine || state.selectedEngine || 'openai-chat');
817
+ state.threadId = String(detail.threadId || state.threadId || '').trim();
818
+ state.workspacePath = String(detail.cwd || state.workspacePath || '').trim();
819
+ state.title = '';
820
+ state.target = '';
821
+ state.selectedEngine = continuedEngine;
822
+ state.workflowIdsText = continuedEngine === 'workflow'
823
+ ? (Array.isArray(detail.plan && detail.plan.workflowIds) ? detail.plan.workflowIds.join('\n') : '')
824
+ : '';
825
+ state.runMode = detail.dryRun ? 'dry-run' : (detail.allowWrite ? 'write' : 'read');
826
+ state.notes = '';
827
+ state.followUpsText = '';
828
+ state.chatDraft = '';
829
+ state.plan = null;
830
+ state.planIssues = [];
831
+ state.planWarnings = [];
832
+ this.showMessage('已继承该任务的会话与工作区,可继续追加要求', 'success');
833
+ },
834
+
499
835
  taskOrchestrationHasLiveActivity() {
500
836
  const state = this.ensureTaskOrchestrationState();
501
837
  const queue = Array.isArray(state.queue) ? state.queue : [];
@@ -538,11 +874,14 @@ export function createTaskOrchestrationMethods(options = {}) {
538
874
  resetTaskOrchestrationDraft() {
539
875
  const state = this.ensureTaskOrchestrationState();
540
876
  state.target = '';
877
+ state.chatDraft = '';
541
878
  state.title = '';
542
879
  state.notes = '';
880
+ state.workspacePath = '';
881
+ state.threadId = '';
543
882
  state.followUpsText = '';
544
883
  state.workflowIdsText = '';
545
- state.selectedEngine = 'codex';
884
+ state.selectedEngine = 'openai-chat';
546
885
  state.runMode = 'write';
547
886
  state.concurrency = 2;
548
887
  state.autoFixRounds = 1;
@@ -65,9 +65,6 @@ export function createToolConfigPermissionMethods(options = {}) {
65
65
  return;
66
66
  }
67
67
  this.toolConfigPermissions = normalizePermissions(res && res.permissions);
68
- try {
69
- localStorage.setItem('toolConfigPermissions', JSON.stringify(this.toolConfigPermissions));
70
- } catch (_) {}
71
68
  this.showMessage(
72
69
  nextAllowWrite
73
70
  ? this.t('toolConfig.allowToast')