@mokuyoaxis/dsh-iris 0.1.1 → 0.1.3

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 (44) hide show
  1. package/CHANGELOG.md +75 -1
  2. package/README.en.md +259 -0
  3. package/README.md +70 -7
  4. package/bin/dsh-iris.js +23 -0
  5. package/cordis.patch.yml +1 -1
  6. package/docs/ARCHITECTURE.md +81 -0
  7. package/docs/FAULT_INJECTION.md +26 -0
  8. package/docs/PROVIDER_SUBMISSION_CONTRACT.md +60 -0
  9. package/docs/ROADMAP.md +25 -8
  10. package/docs/SECURITY.md +53 -0
  11. package/docs/TASK_SEMANTICS.md +147 -0
  12. package/docs/assets/diagrams/iris-architecture.drawio +1 -0
  13. package/docs/assets/diagrams/iris-architecture.png +0 -0
  14. package/docs/assets/diagrams/iris-architecture.svg +63 -0
  15. package/docs/assets/diagrams/iris-task-lifecycle.drawio +1 -0
  16. package/docs/assets/diagrams/iris-task-lifecycle.png +0 -0
  17. package/docs/assets/diagrams/iris-task-lifecycle.svg +64 -0
  18. package/docs/assets/diagrams/iris-workflow-compose.drawio +1 -0
  19. package/docs/assets/diagrams/iris-workflow-compose.png +0 -0
  20. package/docs/assets/diagrams/iris-workflow-compose.svg +41 -0
  21. package/docs/assets/examples/iris-flower-generated.webp +0 -0
  22. package/docs/assets/screenshots/image-task-succeeded.webp +0 -0
  23. package/docs/assets/screenshots/prompt-general-after.webp +0 -0
  24. package/docs/assets/screenshots/prompt-general-before.webp +0 -0
  25. package/docs/assets/screenshots/prompt-general-preview.webp +0 -0
  26. package/docs/assets/screenshots/prompt-image-before.webp +0 -0
  27. package/docs/assets/screenshots/prompt-image-preview.webp +0 -0
  28. package/docs/releases/0.1.2.md +26 -0
  29. package/docs/releases/0.1.3.md +30 -0
  30. package/docs/screenshots.md +34 -0
  31. package/lib/actions.js +609 -175
  32. package/lib/api.js +122 -18
  33. package/lib/bundled-skills.js +62 -0
  34. package/lib/client.js +460 -22
  35. package/lib/doctor.js +243 -0
  36. package/lib/index.js +57 -46
  37. package/lib/prompt-optimizer-config.js +170 -0
  38. package/lib/prompt-optimizer.js +244 -0
  39. package/lib/provider-contract.js +234 -0
  40. package/lib/task-semantics.js +209 -0
  41. package/lib/tasks.js +473 -29
  42. package/package.json +13 -4
  43. package/screenshots.json +6 -0
  44. package/user_guide.md +84 -3
package/lib/client.js CHANGED
@@ -37,6 +37,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
37
37
  '.iris-wb-badge { font-size: 11px; padding: 1px 6px; border-radius: 4px; background: var(--dsw-alias-bg-layer-2, #32323c); }',
38
38
  '.iris-wb-badge.ok { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
39
39
  '.iris-wb-badge.err { color: var(--dsw-alias-state-error-primary, #e06c6c); }',
40
+ '.iris-wb-badge.warn { color: var(--dsw-alias-state-warn-primary, #d9a941); }',
40
41
  '.iris-wb-progress { margin-top: 6px; }',
41
42
  '.iris-wb-bar { height: 5px; border-radius: 3px; background: var(--dsw-alias-bg-layer-2, #32323c); overflow: hidden; }',
42
43
  '.iris-wb-bar i { display: block; height: 100%; background: var(--dsw-alias-brand-primary, #7aa2f7); }',
@@ -111,7 +112,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
111
112
  '.iris-bubble.lit .iris-bubble-btn { border-color: var(--dsw-alias-state-success-primary, #6fcf6f); box-shadow: 0 0 14px var(--dsw-alias-state-success-primary, #6fcf6f); }',
112
113
  '.iris-bubble.dim .iris-bubble-btn { opacity: 0.55; border-color: var(--dsw-alias-border-l1, #3a3a44); }',
113
114
  '.iris-bubble-badge { position: absolute; top: -2px; right: -2px; min-width: 16px; height: 16px; border-radius: 8px; background: var(--dsw-alias-state-warn-primary, #d9a941); color: #1a1a1e; font-size: 10px; font-weight: 700; display: flex; align-items: center; justify-content: center; padding: 0 3px; }',
114
- '.iris-bubble-panel { position: fixed; z-index: 9998; width: 360px; max-height: min(520px, 70vh); overflow: auto; background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 12px; padding: 12px 14px; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45); }',
115
+ '.iris-bubble-panel { position: fixed; z-index: 9998; width: 360px; max-height: min(520px, 70vh); overflow: auto; background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 12px; padding: 12px 14px; font-size: 12px; line-height: 1.45; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45); }',
115
116
  '.iris-bubble-panel-head { position: absolute; top: 6px; right: 6px; z-index: 2; }',
116
117
  '.iris-bubble-close { border: none; background: transparent; color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 14px; cursor: pointer; padding: 4px 6px; }',
117
118
  '.iris-bubble-close:hover { color: var(--dsw-alias-label-primary, #e6e6e6); }',
@@ -160,7 +161,48 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
160
161
  '.iris-ff-list { display: grid; gap: 2px; max-height: 160px; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 6px; padding: 4px; background: var(--dsw-alias-bg-layer-2, #32323c); }',
161
162
  '.iris-ff-item { display: flex; align-items: center; gap: 6px; font-size: 11px; cursor: pointer; padding: 2px 4px; border-radius: 4px; }',
162
163
  '.iris-ff-item:hover { background: var(--dsw-alias-bg-layer-1, #26262e); }',
163
- '.iris-ff-item .iris-ff-src { color: var(--dsw-alias-brand-primary, #7aa2f7); font-size: 10px; }'
164
+ '.iris-ff-item .iris-ff-src { color: var(--dsw-alias-brand-primary, #7aa2f7); font-size: 10px; }',
165
+ /* 对话框级提示词优化器:桌面浮层 + 窄屏底部 Sheet */
166
+ '.iris-po-wrap { position: relative; display: inline-flex; flex: 0 0 auto; }',
167
+ '.iris-po-trigger { appearance: none; -webkit-appearance: none; width: 30px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: 0; outline: 0; background: transparent; color: inherit; font-size: 19px; line-height: 1; cursor: pointer; box-shadow: none; filter: drop-shadow(0 3px 7px rgba(91,113,220,.22)); transition: transform .18s ease, filter .18s ease, opacity .18s ease; }',
168
+ '.iris-po-trigger:hover { transform: translateY(-2px) scale(1.10); filter: drop-shadow(0 5px 10px rgba(112,137,255,.42)); }',
169
+ '.iris-po-trigger:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #7aa2f7); outline-offset: 2px; }',
170
+ '.iris-po-backdrop { position: fixed; inset: 0; z-index: 10019; background: rgba(8,10,18,.30); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); touch-action: none; animation: iris-po-fade .16s ease-out; }',
171
+ '.iris-po-panel { position: fixed; z-index: 10020; right: max(14px, env(safe-area-inset-right)); bottom: max(78px, calc(env(safe-area-inset-bottom) + 68px)); width: min(440px, calc(100vw - 28px)); max-height: min(680px, calc(100dvh - 104px)); overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; box-sizing: border-box; padding: 18px; display: grid; gap: 13px; background-color: rgba(35,36,47,.84); background: radial-gradient(circle at 10% -5%, rgba(132,160,255,.24), transparent 42%), color-mix(in srgb, var(--dsw-alias-bg-layer-1, #26262e) 82%, transparent); color: var(--dsw-alias-label-primary, #eeeeF2); border: 1px solid rgba(150,164,215,.25); border-radius: 20px; box-shadow: 0 24px 70px rgba(0,0,0,.48), inset 0 1px 0 rgba(255,255,255,.07); backdrop-filter: blur(28px) saturate(1.28); -webkit-backdrop-filter: blur(28px) saturate(1.28); animation: iris-po-rise .20s cubic-bezier(.2,.8,.2,1); scrollbar-width: thin; }',
172
+ '.iris-po-head { position: sticky; top: -18px; z-index: 1; margin: -18px -18px 0; padding: 16px 18px 10px; display: flex; align-items: center; justify-content: space-between; gap: 10px; background: linear-gradient(to bottom, color-mix(in srgb, var(--dsw-alias-bg-layer-1, #26262e) 88%, transparent) 72%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }',
173
+ '.iris-po-title { display: inline-flex; align-items: center; gap: 9px; font-size: 14px; letter-spacing: .01em; }',
174
+ '.iris-po-mark, .iris-po-settings-mark { display: inline-flex; align-items: center; justify-content: center; width: 29px; height: 29px; border-radius: 999px; background: rgba(255,255,255,.055); box-shadow: inset 0 1px 0 rgba(255,255,255,.10), 0 5px 16px rgba(67,83,168,.16); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); filter: drop-shadow(0 2px 5px rgba(105,128,235,.16)); }',
175
+ '.iris-po-close { width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 999px; background: rgba(255,255,255,.05); color: var(--dsw-alias-label-secondary, #a6a6b2); font-size: 14px; cursor: pointer; transition: background .15s, color .15s; }',
176
+ '.iris-po-close:hover { background: rgba(255,255,255,.10); color: var(--dsw-alias-label-primary, #eeeef2); }',
177
+ '.iris-po-muted, .iris-po-route, .iris-po-note { font-size: 11px; line-height: 1.55; color: var(--dsw-alias-label-secondary, #a6a6b2); word-break: break-word; }',
178
+ '.iris-po-route { padding: 7px 10px; border-radius: 10px; background: rgba(255,255,255,.045); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
179
+ '.iris-po-note { padding: 8px 10px; border-radius: 10px; color: var(--dsw-alias-state-warn-primary, #dfbd69); background: rgba(217,169,65,.08); }',
180
+ '.iris-po-field { display: grid; gap: 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #a6a6b2); }',
181
+ '.iris-po-field select, .iris-po-result textarea { width: 100%; box-sizing: border-box; background: rgba(255,255,255,.055); color: var(--dsw-alias-label-primary, #eeeef2); border: 1px solid rgba(150,164,215,.20); border-radius: 11px; padding: 9px 11px; font: inherit; font-size: 12px; outline: none; transition: border-color .15s, background .15s, box-shadow .15s; }',
182
+ '.iris-po-field select:focus, .iris-po-result textarea:focus { border-color: rgba(122,162,247,.72); background: rgba(255,255,255,.075); box-shadow: 0 0 0 3px rgba(122,162,247,.11); }',
183
+ '.iris-po-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }',
184
+ '.iris-po-btn, .iris-po-primary { min-height: 34px; border: 1px solid rgba(150,164,215,.20); border-radius: 10px; padding: 6px 11px; font: inherit; font-size: 11px; cursor: pointer; transition: transform .15s, background .15s, border-color .15s; }',
185
+ '.iris-po-btn { background: rgba(255,255,255,.045); color: var(--dsw-alias-label-secondary, #b0b0bb); }',
186
+ '.iris-po-btn:hover { background: rgba(255,255,255,.085); color: var(--dsw-alias-label-primary, #eeeef2); border-color: rgba(150,174,255,.50); }',
187
+ '.iris-po-primary { background: linear-gradient(135deg, #779cf3, #8c79de); color: #fff; border-color: rgba(174,187,255,.55); font-weight: 650; box-shadow: 0 6px 18px rgba(76,92,188,.22); }',
188
+ '.iris-po-primary:hover { transform: translateY(-1px); box-shadow: 0 8px 22px rgba(76,92,188,.32); }',
189
+ '.iris-po-primary:disabled, .iris-po-btn:disabled { opacity: .45; cursor: not-allowed; transform: none; box-shadow: none; }',
190
+ '.iris-po-result { display: grid; gap: 8px; padding: 12px; border: 1px solid rgba(137,155,218,.18); border-radius: 15px; background: rgba(10,12,20,.16); }',
191
+ '.iris-po-result-label { font-size: 11px; font-weight: 650; letter-spacing: .02em; }',
192
+ '.iris-po-result textarea { min-height: 156px; max-height: 34dvh; resize: vertical; line-height: 1.58; }',
193
+ '.iris-po-config { padding: 11px 12px; border: 1px solid rgba(150,164,215,.14); border-radius: 13px; background: rgba(255,255,255,.025); font-size: 11px; }',
194
+ '.iris-po-config summary { cursor: pointer; color: var(--dsw-alias-label-secondary, #a6a6b2); }',
195
+ '.iris-po-config[open] summary { margin-bottom: 9px; color: var(--dsw-alias-label-primary, #eeeef2); }',
196
+ '.iris-po-config .iris-po-actions { margin: 9px 0; }',
197
+ '.iris-po-btn.danger:hover { color: var(--dsw-alias-state-error-primary, #eb7d86); border-color: rgba(235,125,134,.55); background: rgba(235,125,134,.08); }',
198
+ '.iris-po-file { display: none; }',
199
+ '.iris-po-disable { justify-self: start; border: 0; padding: 2px 0; background: transparent; color: var(--dsw-alias-label-tertiary, #777986); font: inherit; font-size: 10px; cursor: pointer; }',
200
+ '.iris-po-disable:hover { color: var(--dsw-alias-state-error-primary, #eb7d86); }',
201
+ '@keyframes iris-po-fade { from { opacity: 0; } to { opacity: 1; } }',
202
+ '@keyframes iris-po-rise { from { opacity: 0; transform: translateY(12px) scale(.985); } to { opacity: 1; transform: translateY(0) scale(1); } }',
203
+ '@media (max-width: 640px) { .iris-po-backdrop { background: rgba(6,8,14,.48); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); } .iris-po-panel { left: max(8px, env(safe-area-inset-left)); right: max(8px, env(safe-area-inset-right)); bottom: max(8px, env(safe-area-inset-bottom)); width: auto; max-height: calc(100dvh - max(16px, env(safe-area-inset-top))); padding: 16px; gap: 11px; border-radius: 22px; } .iris-po-head { top: -16px; margin: -16px -16px 0; padding: 14px 16px 10px; } .iris-po-btn, .iris-po-primary { min-height: 42px; flex: 1 1 auto; } .iris-po-actions .iris-po-primary:first-child { flex-basis: 100%; } .iris-po-result textarea { min-height: 122px; max-height: 28dvh; resize: none; } .iris-po-route { white-space: normal; } }',
204
+ '@media (max-width: 360px) { .iris-po-panel { left: 4px; right: 4px; bottom: max(4px, env(safe-area-inset-bottom)); padding: 13px; border-radius: 18px; } .iris-po-head { top: -13px; margin: -13px -13px 0; padding: 12px 13px 8px; } .iris-po-actions { gap: 6px; } }',
205
+ '@media (prefers-reduced-motion: reduce) { .iris-po-panel, .iris-po-backdrop { animation: none; } .iris-po-trigger, .iris-po-btn, .iris-po-primary { transition: none; } }',
164
206
  ].join('\n');
165
207
 
166
208
  function adoptStyles() {
@@ -211,6 +253,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
211
253
  if (sharedState) setState(sharedState);
212
254
  if (!sharedTimer && !sharedSource) {
213
255
  function apply(data) {
256
+ if (sharedState && data && sharedState.stateEpoch && data.stateEpoch === sharedState.stateEpoch
257
+ && Number(data.stateRevision || 0) <= Number(sharedState.stateRevision || 0)) return;
214
258
  sharedState = data;
215
259
  for (var i = 0; i < sharedListeners.length; i++) {
216
260
  sharedListeners[i](data);
@@ -250,6 +294,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
250
294
  return sharedState;
251
295
  }
252
296
 
297
+ /** 功能卡就绪态直接来自共享 Iris 状态,不再等用户点开卡片后才加载。 */
298
+ function capabilityReady(state, capability) {
299
+ if (!capability) return true;
300
+ var providers = (state && state.providers) || [];
301
+ return providers.some(function (p) {
302
+ return p.enabled && p.apiKeyHint && Array.isArray(p.capabilities) && p.capabilities.indexOf(capability) >= 0;
303
+ });
304
+ }
305
+
253
306
  /* ---- 任务详情抽屉:按需拉取 /iris/api/task/:id ---- */
254
307
  function useTaskDetail(taskId) {
255
308
  var pair = React.useState(null);
@@ -297,10 +350,38 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
297
350
  return '更早';
298
351
  }
299
352
 
353
+ var TASK_STATE_LABELS = {
354
+ queued: '排队中', running: '运行中', watching_paused: '观察暂停', needs_attention: '需要确认',
355
+ artifact_unavailable: '结果待取回', succeeded: '成功', failed: '失败', canceled: '已取消'
356
+ };
357
+ function taskState(task) {
358
+ if (task && task.userState) return task.userState;
359
+ if (task && task.status === 'succeeded') return 'succeeded';
360
+ if (task && task.status === 'failed') return 'failed';
361
+ if (task && task.status === 'canceled') return 'canceled';
362
+ return 'running';
363
+ }
364
+ function taskStateLabel(task) {
365
+ var state = taskState(task);
366
+ return TASK_STATE_LABELS[state] || state;
367
+ }
368
+ function taskStateClass(task) {
369
+ var state = taskState(task);
370
+ if (state === 'succeeded') return 'ok';
371
+ if (state === 'failed') return 'err';
372
+ if (state === 'watching_paused' || state === 'needs_attention' || state === 'artifact_unavailable') return 'warn';
373
+ return '';
374
+ }
375
+ function attentionDispositionLabel(task) {
376
+ var disposition = task && task.attentionDisposition;
377
+ if (!disposition || disposition.status !== 'acknowledged') return '';
378
+ return disposition.reason === 'retried' ? '已通过重试处理' : '提醒已读';
379
+ }
380
+
300
381
  /* 紧凑任务行:泡泡「任务」标签用,单行摘要 */
301
382
  function taskRowMini(task, selected, onSelect) {
302
- var st = task.status === 'succeeded' ? 'ok' : task.status === 'failed' ? 'err' : '';
303
- var label = task.status === 'succeeded' ? '成功' : task.status === 'failed' ? '失败' : task.status === 'canceled' ? '取消' : (task.progress || '运行中');
383
+ var st = taskStateClass(task);
384
+ var label = taskStateLabel(task);
304
385
  return React.createElement('div', {
305
386
  key: task.id, className: 'iris-task-mini' + (selected ? ' selected' : ''),
306
387
  onClick: function () { onSelect(selected ? null : task.id); }, title: '点击展开详情'
@@ -311,6 +392,47 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
311
392
  React.createElement('span', { className: 'iris-wb-muted' }, task.createdAt ? new Date(task.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : ''));
312
393
  }
313
394
 
395
+ function TaskRecoveryActions({ detail }) {
396
+ var pair = React.useState('');
397
+ var note = pair[0];
398
+ var setNote = pair[1];
399
+ var busyPair = React.useState(false);
400
+ var busy = busyPair[0];
401
+ var setBusy = busyPair[1];
402
+ var state = taskState(detail);
403
+ var attentionState = ['watching_paused', 'needs_attention', 'artifact_unavailable'].indexOf(state) >= 0;
404
+ var acknowledged = detail.attentionDisposition && detail.attentionDisposition.status === 'acknowledged';
405
+ var canAcknowledge = detail.schemaVersion === 2 && attentionState && !acknowledged;
406
+ var canRestore = detail.schemaVersion === 2 && attentionState && acknowledged;
407
+ var canReobserve = detail.schemaVersion === 2 && !acknowledged && detail.acceptance === 'accepted' && detail.remoteTaskId
408
+ && detail.outcome !== 'succeeded' && detail.outcome !== 'failed' && detail.outcome !== 'canceled';
409
+ var canRedeliver = detail.schemaVersion === 2 && !acknowledged && state === 'artifact_unavailable' && detail.remoteTaskId;
410
+ var canRetry = detail.schemaVersion === 2 && !acknowledged && ['needs_attention', 'failed', 'canceled'].indexOf(state) >= 0
411
+ && (detail.cap === 'image' || (detail.cap === 'video' && detail.mode === 't2v'));
412
+ if (!canAcknowledge && !canRestore && !canReobserve && !canRedeliver && !canRetry) return null;
413
+ function run(action, body, confirmMessage) {
414
+ if (confirmMessage && !window.confirm(confirmMessage)) return;
415
+ setBusy(true);
416
+ setNote('处理中…');
417
+ postAction(action, body).then(function (r) {
418
+ setNote(r.ok ? ((r.d && r.d.text) || '操作已接受') : ('失败:' + ((r.d && r.d.error) || '未知错误')));
419
+ }).catch(function (error) { setNote('失败:' + String((error && error.message) || error)); })
420
+ .finally(function () { setBusy(false); });
421
+ }
422
+ return React.createElement('div', { className: 'iris-wb-row', onClick: function (e) { e.stopPropagation(); } },
423
+ canReobserve ? React.createElement('button', { className: 'iris-pm-btn', disabled: busy, onClick: function () { run('task_reobserve', { task_id: detail.id }); } }, '重新观察') : null,
424
+ canRedeliver ? React.createElement('button', { className: 'iris-pm-btn', disabled: busy, onClick: function () { run('task_redeliver', { task_id: detail.id }); } }, '重新交付') : null,
425
+ canRetry ? React.createElement('button', { className: 'iris-pm-btn danger', disabled: busy, onClick: function () {
426
+ run('task_manual_retry', { task_id: detail.id, confirm_duplicate_charge: true },
427
+ '远端任务可能已经产生,本次会创建新的生成任务,可能重复计费。确认知情重试?');
428
+ } }, '知情重试') : null,
429
+ canAcknowledge ? React.createElement('button', { className: 'iris-pm-btn', disabled: busy,
430
+ onClick: function () { run('task_ack_attention', { task_id: detail.id }); } }, '标为已读') : null,
431
+ canRestore ? React.createElement('button', { className: 'iris-pm-btn', disabled: busy,
432
+ onClick: function () { run('task_restore_attention', { task_id: detail.id }); } }, '恢复提醒') : null,
433
+ note ? React.createElement('span', { className: 'iris-clean-note' }, note) : null);
434
+ }
435
+
314
436
  /* ---- 任务详情抽屉:点击任务卡片行展开 ---- */
315
437
  function TaskDetailDrawer({ taskId }) {
316
438
  var detail = useTaskDetail(taskId);
@@ -320,6 +442,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
320
442
  var el = [];
321
443
  el.push(React.createElement('div', { key: 'k', className: 'iris-wb-k' }, '提示词'));
322
444
  el.push(React.createElement('div', { key: 'p', className: 'iris-wb-prompt' }, detail.prompt || '(空)'));
445
+ if (Number(detail.schemaVersion) === 1) {
446
+ el.push(React.createElement('div', { key: 'legacy', className: 'iris-clean-note' },
447
+ '旧版任务记录 · 仅支持查看。由于缺少可靠的远端受理与交付事实,不提供重新观察、重新交付或知情重试;0.1.3 新任务将使用可信任务语义。'));
448
+ }
449
+ var dispositionLabel = attentionDispositionLabel(detail);
450
+ if (dispositionLabel) {
451
+ var related = detail.attentionDisposition.relatedTaskId ? ' · 关联任务 ' + detail.attentionDisposition.relatedTaskId : '';
452
+ el.push(React.createElement('div', { key: 'attention-disposition', className: 'iris-clean-note' }, dispositionLabel + related));
453
+ }
323
454
  if (detail.error) {
324
455
  el.push(React.createElement('div', { key: 'ek', className: 'iris-wb-k' }, '错误'));
325
456
  el.push(React.createElement('div', { key: 'e', className: 'iris-wb-err' }, detail.error));
@@ -327,11 +458,13 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
327
458
  var kv = [
328
459
  ['ID', detail.id],
329
460
  ['能力', detail.cap],
330
- ['状态', detail.status],
461
+ ['状态', taskStateLabel(detail)],
331
462
  ['模型', detail.model],
332
463
  ['供应商', detail.providerName],
333
464
  ['模式', detail.mode || '—'],
334
465
  ['远端任务', detail.remoteTaskId || '—'],
466
+ ['来源任务', detail.retryOf || '—'],
467
+ ['后续重试', (detail.manualRetries || []).map(function (item) { return item.taskId; }).join('、') || '—'],
335
468
  ['发起', detail.createdAt ? fmtTime(detail.createdAt) : '—'],
336
469
  ['完成', detail.finishedAt ? fmtTime(detail.finishedAt) : '—'],
337
470
  ['耗时', detail.elapsedMs ? fmtElapsed(detail.elapsedMs) : '—']
@@ -368,24 +501,25 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
368
501
  return React.createElement('span', { key: a.attachmentId, className: 'iris-wb-kv' }, a.attachmentId + (a.file ? ' (' + a.file + ')' : ''));
369
502
  })));
370
503
  }
504
+ el.push(React.createElement(TaskRecoveryActions, { key: 'recovery', detail: detail }));
371
505
  return React.createElement('div', { className: 'iris-wb-drawer' }, ...el);
372
506
  }
373
507
 
374
508
  function taskRow(task, isRunning, selected, onSelect) {
375
- var badge;
376
- if (task.status === 'succeeded') {
377
- badge = React.createElement('span', { className: 'iris-wb-badge ok' }, '成功');
378
- } else if (task.status === 'failed') {
379
- badge = React.createElement('span', { className: 'iris-wb-badge err' }, '失败');
380
- } else {
381
- badge = React.createElement('span', { className: 'iris-wb-badge' }, task.status);
382
- }
509
+ var stateClass = taskStateClass(task);
510
+ var badge = React.createElement('span', { className: 'iris-wb-badge' + (stateClass ? ' ' + stateClass : '') }, taskStateLabel(task));
383
511
  var cells = [
384
512
  React.createElement('span', { className: 'iris-wb-kv' }, task.cap),
385
513
  badge,
386
514
  React.createElement('span', { className: 'iris-wb-kv' }, task.model || ''),
387
515
  React.createElement('span', { className: 'iris-wb-muted' }, String(task.prompt || '').slice(0, 60))
388
516
  ];
517
+ if (Number(task.schemaVersion) === 1) {
518
+ cells.push(React.createElement('span', { className: 'iris-wb-badge', title: '旧版记录缺少可靠的远端受理与交付事实,仅支持查看' }, '旧任务 · 只读'));
519
+ }
520
+ var dispositionLabel = attentionDispositionLabel(task);
521
+ if (dispositionLabel) cells.push(React.createElement('span', { className: 'iris-wb-badge ok' }, dispositionLabel));
522
+ if (task.retryOf) cells.push(React.createElement('span', { className: 'iris-wb-badge' }, '重试任务'));
389
523
  if (task.error) cells.push(React.createElement('span', { className: 'iris-wb-badge err' }, String(task.error).slice(0, 40)));
390
524
  var links = (task.media || []).map(function (m) {
391
525
  return React.createElement('a', {
@@ -650,6 +784,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
650
784
  var modelPair = React.useState(null); // { assigned, options:[{id,providerId}], tested }
651
785
  var modelInfo = modelPair[0];
652
786
  var setModelInfo = modelPair[1];
787
+ var irisState = useIrisState(5000);
653
788
  function setVal(key, value) { var nv = {}; Object.keys(vals).forEach(function (k) { nv[k] = vals[k]; }); nv[key] = value; setVals(nv); }
654
789
  function loadModels() {
655
790
  if (!capability) return;
@@ -729,11 +864,13 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
729
864
  run === 'running' ? '执行中…' : '执行'),
730
865
  result);
731
866
  }
732
- var headLit = !capability || (modelInfo && modelInfo.loaded && modelInfo.options.length > 0);
867
+ var headError = !!(run && run !== 'running' && run.ok === false);
868
+ var headLit = !capability || (capabilityReady(irisState, capability) && !headError);
869
+ var headTitle = headError ? '最近一次执行出错' : (headLit ? 'API 与能力已配置' : '缺少可用 API 或能力模型');
733
870
  return React.createElement('div', { className: 'iris-act-card' + (open ? ' open' : '') + (headLit ? ' lit' : ' dim') },
734
871
  React.createElement('div', { className: 'iris-act-head', onClick: function () { setOpen(!open); } },
735
872
  React.createElement('span', { className: 'iris-act-title' }, title),
736
- capability ? React.createElement('span', { className: headLit ? 'iris-pm-dot lit' : 'iris-pm-dot', title: headLit ? '模型可用' : '无可用模型' }, '●') : null,
873
+ capability ? React.createElement('span', { className: headLit ? 'iris-pm-dot lit' : 'iris-pm-dot', title: headTitle }, '●') : null,
737
874
  React.createElement('span', { className: 'iris-act-arrow' }, '▶')),
738
875
  body);
739
876
  }
@@ -957,10 +1094,58 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
957
1094
  note ? React.createElement('div', { className: 'iris-clean-note', style: { width: '100%' } }, note) : null);
958
1095
  }
959
1096
 
1097
+ function PromptOptimizerSettings() {
1098
+ var pair = React.useState(null);
1099
+ var state = pair[0];
1100
+ var setState = pair[1];
1101
+ var notePair = React.useState('');
1102
+ var note = notePair[0];
1103
+ var setNote = notePair[1];
1104
+
1105
+ function load() {
1106
+ fetch('/iris/api/prompt-optimizer/config')
1107
+ .then(function (res) { return res.ok ? res.json() : null; })
1108
+ .then(function (data) { if (data && data.config) setState(data); })
1109
+ .catch(function () { setNote('读取提示词优化配置失败'); });
1110
+ }
1111
+ React.useEffect(function () {
1112
+ function changed(event) { if (event.detail && event.detail.config) setState(event.detail); }
1113
+ window.addEventListener('iris-prompt-config-changed', changed);
1114
+ load();
1115
+ return function () { window.removeEventListener('iris-prompt-config-changed', changed); };
1116
+ }, []);
1117
+
1118
+ function toggle() {
1119
+ var enabled = !(state && state.config && state.config.enabled !== false);
1120
+ fetch('/iris/api/prompt-optimizer/enabled', {
1121
+ method: 'POST',
1122
+ headers: { 'Content-Type': 'application/json' },
1123
+ body: JSON.stringify({ enabled: enabled })
1124
+ }).then(function (res) {
1125
+ return res.json().then(function (data) {
1126
+ if (!res.ok) throw new Error(data.error || '更新失败');
1127
+ setState(data);
1128
+ window.dispatchEvent(new CustomEvent('iris-prompt-config-changed', { detail: data }));
1129
+ setNote(enabled ? '对话框 🫧 入口已启用' : '对话框入口已关闭;Iris 工作台与后台能力不受影响');
1130
+ });
1131
+ }).catch(function (err) { setNote(String(err && err.message || err)); });
1132
+ }
1133
+
1134
+ var enabled = !state || !state.config || state.config.enabled !== false;
1135
+ return React.createElement('div', { className: 'iris-wb-card' },
1136
+ React.createElement('div', { className: 'iris-wb-row' },
1137
+ React.createElement('span', { className: 'iris-po-settings-mark' }, '🫧'),
1138
+ React.createElement('span', { style: { flex: 1 } }, enabled ? '对话框入口已启用' : '对话框入口已关闭'),
1139
+ React.createElement('button', { className: 'iris-pm-btn', onClick: toggle }, enabled ? '关闭入口' : '重新启用')),
1140
+ React.createElement('div', { className: 'iris-wb-muted' }, '这里只控制对话框提示词优化入口;Iris 工作台、Agent 工具和任务后台始终保持运行。'),
1141
+ note ? React.createElement('div', { className: 'iris-clean-note' }, note) : null);
1142
+ }
1143
+
960
1144
  function WorkbenchPanel() {
961
1145
  var state = useIrisState(5000);
962
1146
  var running = (state && state.tasks && state.tasks.running) || [];
963
1147
  var recent = (state && state.tasks && state.tasks.recent) || [];
1148
+ var attention = (state && state.tasks && state.tasks.attention) || [];
964
1149
  var selPair = React.useState(null);
965
1150
  var selectedTask = selPair[0];
966
1151
  var setSelectedTask = selPair[1];
@@ -975,10 +1160,16 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
975
1160
  React.createElement(CapabilityAssigner, {}),
976
1161
  React.createElement('div', { className: 'iris-wb-sec' }, 'Iris 泡泡快捷卡片(勾选后显示在「⚡ 常用」标签)'),
977
1162
  React.createElement(BubbleCardPicker, {}),
1163
+ React.createElement('div', { className: 'iris-wb-sec' }, '对话框提示词优化'),
1164
+ React.createElement(PromptOptimizerSettings, {}),
978
1165
  React.createElement('div', { className: 'iris-wb-sec' }, '运行中任务'),
979
1166
  React.createElement('div', { className: 'iris-wb-box' },
980
1167
  running.length ? running.map(function (t) { return taskRow(t, true, selectedTask === t.id, onSelect); })
981
1168
  : React.createElement('div', { className: 'iris-wb-empty' }, '暂无进行中任务')),
1169
+ attention.length ? React.createElement('div', {},
1170
+ React.createElement('div', { className: 'iris-wb-sec' }, '需要处理'),
1171
+ React.createElement('div', { className: 'iris-wb-box' },
1172
+ attention.map(function (t) { return taskRow(t, false, selectedTask === t.id, onSelect); }))) : null,
982
1173
  React.createElement('div', { className: 'iris-wb-sec' }, '历史任务'),
983
1174
  React.createElement(CleanupBar, {}),
984
1175
  React.createElement(HistoryBrowser, { recent: recent, selected: selectedTask, onSelect: onSelect }),
@@ -991,6 +1182,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
991
1182
  var state = useIrisState(5000);
992
1183
  var running = (state && state.tasks && state.tasks.running) || [];
993
1184
  var recent = (state && state.tasks && state.tasks.recent) || [];
1185
+ var attention = (state && state.tasks && state.tasks.attention) || [];
994
1186
  var recentTotal = (state && state.tasks && state.tasks.recentTotal) || recent.length;
995
1187
  var cards = useBubbleCards()[0];
996
1188
  var tabPair = React.useState('tasks');
@@ -1002,17 +1194,24 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1002
1194
  function onSelect(id) { setSelectedTask(id); }
1003
1195
  var body;
1004
1196
  if (tab === 'tasks') {
1005
- var shown = recent.slice(0, 6);
1197
+ // 快捷面板承载当前任务、需要处理的异常事实与可直接查看的成功产物。
1198
+ var succeeded = recent.filter(function (t) {
1199
+ return t.status === 'succeeded' && Array.isArray(t.media) && t.media.length > 0;
1200
+ });
1201
+ var shown = succeeded.slice(0, 6);
1006
1202
  body = React.createElement('div', { className: 'iris-wb-box' },
1007
1203
  running.length ? React.createElement('div', {},
1008
1204
  React.createElement('div', { className: 'iris-wb-sec' }, '运行中'),
1009
1205
  running.map(function (t) { return taskRow(t, true, selectedTask === t.id, onSelect); })) : null,
1010
- React.createElement('div', { className: 'iris-wb-sec' }, '最近'),
1206
+ attention.length ? React.createElement('div', {},
1207
+ React.createElement('div', { className: 'iris-wb-sec' }, '需要处理'),
1208
+ attention.slice(0, 4).map(function (t) { return taskRowMini(t, selectedTask === t.id, onSelect); })) : null,
1209
+ React.createElement('div', { className: 'iris-wb-sec' }, '最近完成'),
1011
1210
  shown.length ? shown.map(function (t) { return taskRowMini(t, selectedTask === t.id, onSelect); })
1012
- : React.createElement('div', { className: 'iris-wb-empty' }, '尚无任务记录'),
1013
- recent.length > 6 || recentTotal > recent.length
1211
+ : React.createElement('div', { className: 'iris-wb-empty' }, '尚无成功产物'),
1212
+ succeeded.length > 6 || recentTotal > succeeded.length
1014
1213
  ? React.createElement('div', { className: 'iris-bubble-more', style: { cursor: 'default' } },
1015
- '共 ' + recentTotal + ' 条 · 完整历史与清理见设置页') : null);
1214
+ '失败、取消与完整历史请到 Iris 工作台查看') : null);
1016
1215
  } else {
1017
1216
  var defs = CARD_DEFS.filter(function (d) { return cards.indexOf(d.action) >= 0; });
1018
1217
  body = defs.length
@@ -1027,6 +1226,234 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1027
1226
  selectedTask ? React.createElement(TaskDetailDrawer, { taskId: selectedTask }) : null);
1028
1227
  }
1029
1228
 
1229
+ function PromptOptimizerControl(props) {
1230
+ var draft = props.useInput(function (value) { return value.draft; });
1231
+ var phase = props.useInput(function (value) { return value.phase; });
1232
+ var refCount = props.useInput(function (value) { return (value.occurrences || []).length; });
1233
+ var projection = props.useProjection('modelSelection');
1234
+ var selectedRoute = projection && (projection.current || projection.selection || projection.next || projection.lastUsed || (projection.provider && projection.model ? projection : null));
1235
+
1236
+ var openPair = React.useState(false);
1237
+ var open = openPair[0];
1238
+ var setOpen = openPair[1];
1239
+ var targetPair = React.useState('general');
1240
+ var target = targetPair[0];
1241
+ var setTarget = targetPair[1];
1242
+ var busyPair = React.useState(false);
1243
+ var busy = busyPair[0];
1244
+ var setBusy = busyPair[1];
1245
+ var resultPair = React.useState(null);
1246
+ var result = resultPair[0];
1247
+ var setResult = resultPair[1];
1248
+ var notePair = React.useState('');
1249
+ var note = notePair[0];
1250
+ var setNote = notePair[1];
1251
+ var configPair = React.useState(null);
1252
+ var configState = configPair[0];
1253
+ var setConfigState = configPair[1];
1254
+ var abortRef = React.useRef(null);
1255
+ var fileRef = React.useRef(null);
1256
+
1257
+ function promptRequest(operation, body, signal) {
1258
+ return fetch('/iris/api/prompt-optimizer/' + operation, {
1259
+ method: 'POST',
1260
+ headers: { 'Content-Type': 'application/json' },
1261
+ body: JSON.stringify(body || {}),
1262
+ signal: signal
1263
+ }).then(function (res) {
1264
+ return res.json().catch(function () { return {}; }).then(function (data) {
1265
+ if (!res.ok) throw new Error(data.error || ('请求失败 (' + res.status + ')'));
1266
+ return data;
1267
+ });
1268
+ });
1269
+ }
1270
+
1271
+ function loadConfig() {
1272
+ fetch('/iris/api/prompt-optimizer/config')
1273
+ .then(function (res) { return res.ok ? res.json() : null; })
1274
+ .then(function (data) { if (data && data.config) setConfigState(data); })
1275
+ .catch(function () { setNote('无法读取提示词优化配置'); });
1276
+ }
1277
+
1278
+ React.useEffect(function () {
1279
+ return function () {
1280
+ if (abortRef.current) abortRef.current.abort();
1281
+ };
1282
+ }, []);
1283
+
1284
+ React.useEffect(function () {
1285
+ function changed(event) { if (event.detail && event.detail.config) setConfigState(event.detail); }
1286
+ window.addEventListener('iris-prompt-config-changed', changed);
1287
+ loadConfig();
1288
+ return function () { window.removeEventListener('iris-prompt-config-changed', changed); };
1289
+ }, []);
1290
+
1291
+ function publishConfig(data) {
1292
+ setConfigState(data);
1293
+ try { window.dispatchEvent(new CustomEvent('iris-prompt-config-changed', { detail: data })); } catch (_) {}
1294
+ }
1295
+
1296
+ function optimize() {
1297
+ if (!draft || !draft.trim()) { setNote('请先在输入框中写入提示词'); return; }
1298
+ if (refCount) { setNote('当前版本暂不改写含 @ 或 / 结构化引用的草稿,以免引用失效'); return; }
1299
+ if (phase !== 'plain') { setNote('输入框正忙,请稍后再试'); return; }
1300
+ if (abortRef.current) abortRef.current.abort();
1301
+ var controller = new AbortController();
1302
+ abortRef.current = controller;
1303
+ setBusy(true);
1304
+ setNote('正在调用模型优化…这可能产生模型费用');
1305
+ setResult(null);
1306
+ promptRequest('optimize', {
1307
+ text: draft,
1308
+ target: target,
1309
+ sessionId: props.sessionId,
1310
+ route: selectedRoute || undefined
1311
+ }, controller.signal).then(function (data) {
1312
+ setResult(data);
1313
+ setNote('已生成预览,尚未写回输入框');
1314
+ }).catch(function (err) {
1315
+ if (controller.signal.aborted) setNote('已取消');
1316
+ else setNote(String(err && err.message || err));
1317
+ }).finally(function () {
1318
+ if (abortRef.current === controller) abortRef.current = null;
1319
+ setBusy(false);
1320
+ });
1321
+ }
1322
+
1323
+ function cancel() {
1324
+ if (abortRef.current) abortRef.current.abort();
1325
+ }
1326
+
1327
+ function replaceDraft(text) {
1328
+ if (!result) return;
1329
+ if (draft !== result.original && !window.confirm('输入框内容在优化期间已变化,仍要替换吗?')) return;
1330
+ props.inputActions.setDraft(text);
1331
+ setNote(text === result.original ? '已恢复优化前原文' : '已写回输入框;不会自动发送');
1332
+ }
1333
+
1334
+ function copyResult() {
1335
+ if (!result) return;
1336
+ if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
1337
+ navigator.clipboard.writeText(result.optimized).then(function () { setNote('已复制优化结果'); }).catch(function () { setNote('复制失败,请手动选择文本'); });
1338
+ } else {
1339
+ setNote('浏览器不支持自动复制,请手动选择文本');
1340
+ }
1341
+ }
1342
+
1343
+ function importConfig(event) {
1344
+ var file = event.target.files && event.target.files[0];
1345
+ event.target.value = '';
1346
+ if (!file) return;
1347
+ file.text().then(function (raw) {
1348
+ var parsed = JSON.parse(raw);
1349
+ return promptRequest('import', { config: parsed });
1350
+ }).then(function (data) {
1351
+ publishConfig(data);
1352
+ setNote('JSON 配置已导入并生效');
1353
+ }).catch(function (err) {
1354
+ setNote('导入失败:' + String(err && err.message || err));
1355
+ });
1356
+ }
1357
+
1358
+ function exportConfig() {
1359
+ if (!configState || !configState.config) { setNote('配置尚未载入'); return; }
1360
+ try {
1361
+ var blob = new Blob([JSON.stringify(configState.config, null, 2) + '\n'], { type: 'application/json' });
1362
+ var url = URL.createObjectURL(blob);
1363
+ var a = document.createElement('a');
1364
+ a.href = url;
1365
+ a.download = 'iris-prompt-optimizer.json';
1366
+ a.click();
1367
+ setTimeout(function () { URL.revokeObjectURL(url); }, 0);
1368
+ setNote('已导出当前 JSON 配置');
1369
+ } catch (_) {
1370
+ setNote('导出失败');
1371
+ }
1372
+ }
1373
+
1374
+ function resetConfig() {
1375
+ if (!window.confirm('恢复 Iris 内置默认 Prompt、目标模板和会话模型路由?')) return;
1376
+ promptRequest('reset', {}).then(function (data) {
1377
+ publishConfig(data);
1378
+ setNote('已恢复 Iris 内置默认配置');
1379
+ }).catch(function (err) {
1380
+ setNote('重置失败:' + String(err && err.message || err));
1381
+ });
1382
+ }
1383
+
1384
+ function disableEntry() {
1385
+ if (busy) return;
1386
+ promptRequest('enabled', { enabled: false }).then(function (data) {
1387
+ publishConfig(data);
1388
+ setOpen(false);
1389
+ }).catch(function (err) { setNote('关闭失败:' + String(err && err.message || err)); });
1390
+ }
1391
+
1392
+ if (configState && configState.config && configState.config.enabled === false) return null;
1393
+
1394
+ var routeLabel = 'DSH 默认模型';
1395
+ if (configState && configState.config && configState.config.route.mode === 'fixed') {
1396
+ routeLabel = configState.config.route.provider + ' / ' + configState.config.route.model;
1397
+ } else if (selectedRoute) {
1398
+ routeLabel = selectedRoute.provider + ' / ' + selectedRoute.model;
1399
+ }
1400
+ var reasoningSetting = configState && configState.config && configState.config.generation && configState.config.generation.reasoningEffort || 'off-if-supported';
1401
+ var reasoningLabel = reasoningSetting === 'off-if-supported' ? '支持时关闭' : reasoningSetting === 'provider-default' ? '供应商默认' : reasoningSetting === 'inherit' ? '跟随会话' : reasoningSetting;
1402
+ var outputBudget = configState && configState.config && configState.config.generation && configState.config.generation.maxOutputTokens || 1200;
1403
+
1404
+ var panel = null;
1405
+ if (open) {
1406
+ panel = React.createElement(React.Fragment, {},
1407
+ React.createElement('div', { className: 'iris-po-backdrop', onClick: function () { setOpen(false); }, 'aria-hidden': 'true' }),
1408
+ React.createElement('div', { className: 'iris-po-panel', role: 'dialog', 'aria-modal': 'true', 'aria-label': 'Iris 提示词优化', onClick: function (e) { e.stopPropagation(); } },
1409
+ React.createElement('div', { className: 'iris-po-head' },
1410
+ React.createElement('strong', { className: 'iris-po-title' }, React.createElement('span', { className: 'iris-po-mark' }, '🫧'), 'Iris 提示词优化'),
1411
+ React.createElement('button', { className: 'iris-po-close', onClick: function () { setOpen(false); }, title: '关闭' }, '✕')),
1412
+ React.createElement('div', { className: 'iris-po-muted' }, '只处理当前未发送草稿,不读取会话历史;调用模型可能产生费用。'),
1413
+ React.createElement('label', { className: 'iris-po-field' },
1414
+ React.createElement('span', {}, '优化目标'),
1415
+ React.createElement('select', { value: target, disabled: busy, onChange: function (e) { setTarget(e.target.value); } },
1416
+ React.createElement('option', { value: 'general' }, '通用对话'),
1417
+ React.createElement('option', { value: 'image' }, '图片生成'),
1418
+ React.createElement('option', { value: 'video' }, '视频生成'),
1419
+ React.createElement('option', { value: 's2v' }, '首尾帧视频'))),
1420
+ React.createElement('div', { className: 'iris-po-route', title: routeLabel }, '模型:' + routeLabel + ' · 思考:' + reasoningLabel + ' · 输出:' + outputBudget + ' tokens'),
1421
+ React.createElement('div', { className: 'iris-po-actions' },
1422
+ React.createElement('button', { className: 'iris-po-primary', disabled: busy || !draft || !draft.trim() || phase !== 'plain' || refCount > 0, onClick: optimize }, busy ? '优化中…' : '生成预览'),
1423
+ busy ? React.createElement('button', { className: 'iris-po-btn', onClick: cancel }, '取消') : null),
1424
+ result ? React.createElement('div', { className: 'iris-po-result' },
1425
+ React.createElement('div', { className: 'iris-po-result-label' }, '优化结果'),
1426
+ React.createElement('textarea', { readOnly: true, value: result.optimized }),
1427
+ React.createElement('div', { className: 'iris-po-route' }, '实际使用:' + result.route.provider + ' / ' + result.route.model + ' · 思考:' + (result.route.reasoningEffort === 'provider-default' ? '供应商默认' : result.route.reasoningEffort)),
1428
+ React.createElement('div', { className: 'iris-po-actions' },
1429
+ React.createElement('button', { className: 'iris-po-primary', onClick: function () { replaceDraft(result.optimized); } }, '写回输入框'),
1430
+ React.createElement('button', { className: 'iris-po-btn', onClick: copyResult }, '复制'),
1431
+ React.createElement('button', { className: 'iris-po-btn', onClick: optimize }, '再优化'),
1432
+ React.createElement('button', { className: 'iris-po-btn', onClick: function () { replaceDraft(result.original); } }, '恢复原文'))) : null,
1433
+ note ? React.createElement('div', { className: 'iris-po-note' }, note) : null,
1434
+ React.createElement('details', { className: 'iris-po-config' },
1435
+ React.createElement('summary', {}, 'JSON 配置'),
1436
+ React.createElement('div', { className: 'iris-po-muted' }, '可导出后修改 systemPrompt、targets、route 和 generation,再重新导入。'),
1437
+ React.createElement('div', { className: 'iris-po-actions' },
1438
+ React.createElement('button', { className: 'iris-po-btn', onClick: exportConfig }, '导出'),
1439
+ React.createElement('button', { className: 'iris-po-btn', onClick: function () { if (fileRef.current) fileRef.current.click(); } }, '导入'),
1440
+ React.createElement('button', { className: 'iris-po-btn danger', onClick: resetConfig }, '恢复默认'),
1441
+ React.createElement('input', { ref: fileRef, className: 'iris-po-file', type: 'file', accept: 'application/json,.json', onChange: importConfig })),
1442
+ React.createElement('div', { className: 'iris-po-muted' }, '当前:' + (configState && configState.source === 'custom' ? '用户 JSON' : 'Iris 默认'))),
1443
+ React.createElement('button', { className: 'iris-po-disable', disabled: busy, onClick: disableEntry }, '关闭此对话入口(工作台仍保留)')));
1444
+ }
1445
+
1446
+ return React.createElement('div', { className: 'iris-po-wrap' },
1447
+ React.createElement('button', {
1448
+ className: 'iris-po-trigger',
1449
+ type: 'button',
1450
+ title: refCount ? '含结构化引用的草稿暂不支持优化' : '优化当前输入框提示词',
1451
+ 'aria-expanded': open ? 'true' : 'false',
1452
+ onClick: function () { setOpen(!open); }
1453
+ }, '🫧'),
1454
+ panel);
1455
+ }
1456
+
1030
1457
  function ProgressDock() {
1031
1458
  var state = useIrisState(5000);
1032
1459
  var running = (state && state.tasks && state.tasks.running) || [];
@@ -1045,6 +1472,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1045
1472
  var providers = (state && state.providers) || [];
1046
1473
  var configured = providers.some(function (p) { return p.enabled && p.apiKeyHint; });
1047
1474
  var running = (state && state.tasks && state.tasks.running) || [];
1475
+ var attention = (state && state.tasks && state.tasks.attention) || [];
1048
1476
 
1049
1477
  var posPair = React.useState(function () {
1050
1478
  var saved = null;
@@ -1130,8 +1558,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1130
1558
  style = { right: DEFAULT_X + 'px', bottom: DEFAULT_Y + 'px' };
1131
1559
  }
1132
1560
 
1133
- var badge = running.length
1134
- ? React.createElement('span', { className: 'iris-bubble-badge' }, running.length)
1561
+ var badgeCount = running.length + attention.length;
1562
+ var badge = badgeCount
1563
+ ? React.createElement('span', { className: 'iris-bubble-badge', title: attention.length ? attention.length + ' 个任务需要处理' : '' }, badgeCount)
1135
1564
  : null;
1136
1565
 
1137
1566
  var panel = null;
@@ -1167,6 +1596,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1167
1596
  }, WorkbenchPanel);
1168
1597
  });
1169
1598
 
1599
+ ctx.slots.inject('conversation.input.right', function () {
1600
+ return ctx.slots.register({
1601
+ name: 'conversation.input.right',
1602
+ id: 'iris-prompt-optimizer',
1603
+ order: 40,
1604
+ label: function () { return 'Iris 提示词优化'; }
1605
+ }, PromptOptimizerControl);
1606
+ });
1607
+
1170
1608
  ctx.slots.inject('conversation.input.dock', function () {
1171
1609
  return ctx.slots.register({
1172
1610
  name: 'conversation.input.dock',