@mokuyoaxis/dsh-iris 0.2.2 → 0.2.4

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 (79) hide show
  1. package/.dsh/skills/iris-compose-media/SKILL.md +23 -73
  2. package/.dsh/skills/iris-compose-media/agents/openai.yaml +1 -1
  3. package/.dsh/skills/iris-compose-media/references/task-v2-recovery.md +23 -22
  4. package/.dsh/skills/iris-compose-media/references/workflows.md +25 -30
  5. package/.dsh/skills/iris-use/SKILL.md +42 -0
  6. package/.dsh/skills/iris-use/agents/openai.yaml +4 -0
  7. package/.dsh/skills/iris-use/references/media-contract.md +83 -0
  8. package/.dsh/skills/iris-verify-ui/SKILL.md +9 -7
  9. package/.dsh/skills/iris-verify-ui/agents/openai.yaml +1 -1
  10. package/.dsh/skills/iris-verify-ui/references/verdict-and-degradation.md +3 -0
  11. package/CHANGELOG.md +41 -2
  12. package/README.en.md +50 -38
  13. package/README.md +48 -37
  14. package/bin/dsh-iris.js +35 -6
  15. package/docs/AGENT_QUICKSTART.md +9 -9
  16. package/docs/ARCHITECTURE.md +4 -2
  17. package/docs/CLI_MANAGEMENT.md +11 -3
  18. package/docs/CORE_RUNTIME_CONTRACT.md +3 -1
  19. package/docs/DSH_COMPATIBILITY.md +43 -0
  20. package/docs/DSH_CORE_MIGRATION.md +6 -2
  21. package/docs/DSH_RC2_ADAPTATION.md +4 -2
  22. package/docs/FAULT_INJECTION.md +1 -0
  23. package/docs/FUNCTION_MODEL_ASSIGNMENTS.md +63 -0
  24. package/docs/HEADLESS_CLI.md +14 -8
  25. package/docs/HOST_ADAPTER_CONTRACT.md +10 -7
  26. package/docs/HOST_DOCTOR.md +6 -4
  27. package/docs/IMAGE_IMPORT.md +49 -0
  28. package/docs/MODEL_PORT_CONTRACT.md +6 -2
  29. package/docs/MUSIC.md +77 -0
  30. package/docs/PROMPT_OPTIMIZER.md +48 -10
  31. package/docs/PROVIDER_ADAPTER_CONTRACT.md +2 -0
  32. package/docs/ROADMAP.md +16 -25
  33. package/docs/SECURITY.md +3 -2
  34. package/docs/SYNC_DELIVERY_RECOVERY.md +39 -0
  35. package/docs/TASK_SEMANTICS.md +4 -4
  36. package/docs/VISION_CLI.md +3 -3
  37. package/docs/VISION_MODEL.md +1 -1
  38. package/docs/WORKBENCH_ARTIFACTS.md +4 -2
  39. package/docs/releases/0.2.3.md +54 -0
  40. package/docs/releases/0.2.4.md +27 -0
  41. package/lib/actions.js +71 -14
  42. package/lib/adapters.js +6 -2
  43. package/lib/api.js +14 -5
  44. package/lib/bundled-skills.js +1 -1
  45. package/lib/capability.js +2 -0
  46. package/lib/client.js +131 -47
  47. package/lib/command-service.js +28 -4
  48. package/lib/config.js +41 -23
  49. package/lib/core-artifact-store.js +44 -6
  50. package/lib/core-delivery-cache.js +145 -0
  51. package/lib/core-maintenance.js +12 -1
  52. package/lib/core-tasks.js +19 -5
  53. package/lib/core-user-projection.js +2 -2
  54. package/lib/doctor.js +4 -10
  55. package/lib/dsh-compatibility.js +10 -0
  56. package/lib/dsh-core-adapter.js +20 -4
  57. package/lib/dsh-text-model-adapter.js +15 -2
  58. package/lib/generation-input.js +10 -0
  59. package/lib/host-runtime.js +5 -2
  60. package/lib/image-edit-input.js +7 -5
  61. package/lib/image-import.js +61 -0
  62. package/lib/index.js +60 -17
  63. package/lib/models.js +12 -3
  64. package/lib/music-generation.js +21 -0
  65. package/lib/music-options.js +18 -0
  66. package/lib/music-transport.js +46 -0
  67. package/lib/ocr-model-routing.js +1 -0
  68. package/lib/prompt-optimizer-config.js +92 -21
  69. package/lib/prompt-optimizer-core.js +51 -6
  70. package/lib/prompt-optimizer.js +20 -14
  71. package/lib/provider-adapter.js +1 -0
  72. package/lib/provider-adapters.js +35 -3
  73. package/lib/provider-catalog.js +20 -11
  74. package/lib/provider-config-service.js +18 -6
  75. package/lib/provider-protocol.js +18 -0
  76. package/lib/provider-task-runner.js +80 -5
  77. package/lib/vision-model-routing.js +22 -8
  78. package/package.json +12 -9
  79. package/user_guide.md +45 -23
package/lib/client.js CHANGED
@@ -21,7 +21,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
21
21
  var exports = module.exports;
22
22
 
23
23
  var React = require('react');
24
- var IRIS_CLIENT_VERSION = '0.2.2';
24
+ var IRIS_CLIENT_VERSION = '0.2.4';
25
25
  var HOST_CLIENT_PROTOCOL_VERSION = 0;
26
26
  var reportedSeats = [];
27
27
 
@@ -810,7 +810,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
810
810
  }
811
811
 
812
812
  /* ---- 模型池(阶段 9 P4):发现/手动模型 + verified 标记 + 逐模型测试/移除 + 手动添加 ---- */
813
- var CAP_SHORT = { 'image-gen': '图', 'video-gen': '视', 'tts': '音', 'transcribe': '转', 'vision': '视👁' };
813
+ var CAP_SHORT = { 'image-gen': '图', 'video-gen': '视', 'tts': '音', 'music-gen': '乐', 'transcribe': '转', 'vision': '视👁' };
814
814
  function ModelPool(props) {
815
815
  var p = props.provider;
816
816
  var addPair = React.useState('');
@@ -821,6 +821,11 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
821
821
  postAction('providers_set_model_image_protocol', { id: p.id, model_id: m.id, imageProtocol: value })
822
822
  .then(function (r) { props.onNote(r.ok ? '模型图片协议已更新' : (r.d.error || '更新失败')); if (r.ok) props.onDone(); });
823
823
  }
824
+ function setMusicProtocol(m, value) {
825
+ if (!value) return;
826
+ postAction('providers_set_model_music_protocol', { id: p.id, model_id: m.id, musicProtocol: value })
827
+ .then(function (r) { props.onNote(r.ok ? '模型音乐协议已更新' : (r.d.error || '更新失败')); if (r.ok) props.onDone(); });
828
+ }
824
829
  function verBadge(m, c) {
825
830
  var health = m.health && m.health[c];
826
831
  var status = health && HEALTH_META[health.status] ? health.status : 'configured';
@@ -849,7 +854,11 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
849
854
  React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
850
855
  React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图')),
851
856
  m.imageRouting && m.imageRouting.protocolInferred ? ' · 协议待确认' : null) : null,
852
- caps.includes('vision') || (caps.includes('image-gen') && m.imageRouting && m.imageRouting.mediaProtocol === 'openai-chat-images') ? React.createElement(VisionInputEditor, { key: 'vision-input', provider: p, model: m,
857
+ caps.includes('music-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '音乐协议 ',
858
+ React.createElement('select', { value: m.musicProtocol || '', onChange: function (e) { setMusicProtocol(m, e.target.value); } },
859
+ React.createElement('option', { value: '' }, '请选择音乐协议'),
860
+ React.createElement('option', { value: 'gemini-web-music' }, 'Chat Completions(内嵌音频)'))) : null,
861
+ caps.includes('vision') || (caps.includes('image-gen') && m.imageRouting && m.imageRouting.editSupported) ? React.createElement(VisionInputEditor, { key: 'vision-input', provider: p, model: m,
853
862
  onNote: props.onNote, onDone: props.onDone }) : null,
854
863
  blocked ? React.createElement('span', { className: 'iris-pm-ver failed ' + healthClass(blocked), title: healthText(blocked) }, rateLimitLabel(blocked)) : null,
855
864
  React.createElement('button', { className: 'iris-pm-btn danger', title: '从池移除', onClick: function () { props.removeModel(p, m); } }, '✕'));
@@ -928,7 +937,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
928
937
  placeholder: '最长边 px(可选)', value: dimensionPair[0], onChange: function (e) { dimensionPair[1](e.target.value); } }),
929
938
  React.createElement('button', { className: 'iris-pm-btn', onClick: function () { save(false); } }, '保存预算'),
930
939
  React.createElement('button', { className: 'iris-pm-btn', onClick: function () { save(true); } }, '恢复继承')),
931
- React.createElement('span', { className: 'iris-pm-note' }, '用于看图、OCR 每块输入和定位。作品原图保留,定位结果换算回原图。'));
940
+ React.createElement('span', { className: 'iris-pm-note' }, '用于看图、OCR 每块输入、定位和改图的发送副本。作品原图保留,定位结果换算回原图。'));
932
941
  }
933
942
 
934
943
  function ProviderEndpointEditor(props) {
@@ -1000,7 +1009,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1000
1009
  postAction('providers_discover', { id: p.id }).then(function (r) { setNote(r.d.text || r.d.error || '发现完成'); refresh(); });
1001
1010
  }
1002
1011
  function testModel(p, m, capability) {
1003
- var noProbe = capability === 'video-gen' || capability === 'transcribe';
1012
+ var noProbe = capability === 'video-gen' || capability === 'transcribe' || capability === 'music-gen';
1004
1013
  if (!noProbe && !window.confirm('将用模型「' + m.id + '」发起一次真实的 ' + capability + ' 请求,可能产生费用。继续吗?')) return;
1005
1014
  setNote('验证 ' + m.id + ' · ' + capability + '…');
1006
1015
  postAction('providers_test_model', { id: p.id, model_id: m.id, capability: capability, confirm_paid: !noProbe })
@@ -1166,7 +1175,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1166
1175
  if (!r.ok || !r.d) return;
1167
1176
  var opts = (r.d.poolByCapability && r.d.poolByCapability[capability]) || [];
1168
1177
  var ord = (r.d.order && r.d.order[capability]) || [];
1169
- var assignedRef = ord[0] || (opts[0] && opts[0].ref) || '';
1178
+ opts = ord.map(function (ref) { return opts.find(function (option) { return option.ref === ref; }); }).filter(Boolean);
1179
+ var assignedRef = ord[0] || '';
1170
1180
  var assignedOpt = opts.find(function (o) { return o.ref === assignedRef; });
1171
1181
  // 不再自动填 vals.model:留空 = 用能力分配的解析结果(避免绕过分配)
1172
1182
  setModelInfo({ assigned: assignedOpt ? assignedOpt.label : '', assignedRef: assignedRef, options: opts, loaded: true });
@@ -1183,7 +1193,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1183
1193
  })
1184
1194
  .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })
1185
1195
  .then(function (res) {
1186
- setRun(res.ok ? { ok: true, text: res.d.text || '', img: res.d.imageUrl || res.d.imageDataUrl || null } : { ok: false, text: res.d.error || '执行失败' });
1196
+ setRun(res.ok ? { ok: true, text: res.d.text || '', img: res.d.imageUrl || res.d.imageDataUrl || null, audio: res.d.audioUrl || null } : { ok: false, text: res.d.error || '执行失败' });
1187
1197
  if (res.ok && res.d && res.d.storage === 'core') window.dispatchEvent(new CustomEvent('iris-core-artifacts-changed'));
1188
1198
  })
1189
1199
  .catch(function () { setRun({ ok: false, text: '网络错误' }); });
@@ -1193,6 +1203,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1193
1203
  if (open) {
1194
1204
  var inputs = fields.map(function (f) {
1195
1205
  var node;
1206
+ if (f.key === 'model' && capability) {
1207
+ node = React.createElement('select', { value: vals.model || '', onChange: function (e) { setVal('model', e.target.value); } },
1208
+ React.createElement('option', { value: '' }, '按功能分配顺序'),
1209
+ ((modelInfo && modelInfo.options) || []).map(function (option) {
1210
+ return React.createElement('option', { key: option.ref, value: option.ref, disabled: option.unavailable }, option.label + (option.unavailable ? ' · 当前不可用' : ''));
1211
+ }));
1212
+ return React.createElement('div', { key: f.key, className: 'iris-act-field' }, React.createElement('label', {}, f.label), node);
1213
+ }
1196
1214
  if (f.type === 'file') {
1197
1215
  node = React.createElement(FileField, { value: vals[f.key] || '', accept: f.accept || '', onChange: function (v) { setVal(f.key, v); } });
1198
1216
  return React.createElement('div', { key: f.key, className: 'iris-act-field' },
@@ -1220,6 +1238,10 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1220
1238
  result = React.createElement('div', { className: 'iris-act-result' },
1221
1239
  React.createElement('div', {}, run.text),
1222
1240
  React.createElement('img', { className: 'iris-act-img', src: run.img, alt: '结果' }));
1241
+ } else if (run.ok && run.audio) {
1242
+ result = React.createElement('div', { className: 'iris-act-result' }, React.createElement('div', {}, run.text),
1243
+ React.createElement('audio', { src: run.audio, controls: true, preload: 'metadata' }),
1244
+ React.createElement('a', { href: run.audio, download: '' }, '下载音乐'));
1223
1245
  } else {
1224
1246
  result = React.createElement('div', { className: 'iris-act-result' + (run.ok ? '' : ' err') }, run.text);
1225
1247
  }
@@ -1232,15 +1254,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1232
1254
  modelBar = React.createElement('div', { className: 'iris-act-field' },
1233
1255
  React.createElement('label', {},
1234
1256
  React.createElement('span', { className: 'iris-pm-dot ' + healthClass(cardHealth) }, cardHealth.retryAt ? '◷' : HEALTH_META[cardHealth.status].symbol),
1235
- ' 模型:' + (shown || '自动') + (available ? '' : '(无可用模型)')),
1257
+ ' 模型:' + (shown || '未分配') + (available ? '' : '(无可用模型)')),
1236
1258
  React.createElement('div', { className: 'iris-wb-muted' },
1237
- healthText(cardHealth) + ' · 改分配 → 上方「能力分配(failover 顺序)」'));
1259
+ healthText(cardHealth) + ' · 改分配 → 上方「功能分配(调用顺序)」'));
1238
1260
  }
1239
1261
  body = React.createElement('div', { className: 'iris-act-body' },
1240
1262
  modelBar,
1241
1263
  inputs,
1242
1264
  React.createElement('button', { className: 'iris-act-run', disabled: run === 'running', onClick: submit },
1243
- run === 'running' ? '执行中…' : '执行'),
1265
+ run === 'running' ? '执行中…' : (props.submitLabel || '执行')),
1244
1266
  result);
1245
1267
  }
1246
1268
  var headStatus = capability ? cardHealth.status : 'verified';
@@ -1260,6 +1282,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1260
1282
 
1261
1283
  /* ---- 操作卡片注册表(单一来源:设置页全量 / 泡泡常用子集 / 选择器三处共用) ---- */
1262
1284
  var CARD_DEFS = [
1285
+ { action: 'image_import', title: '📥 导入图片', submitLabel: '保存到作品', fields: [
1286
+ { key: 'image_path', label: '图片(静态 PNG/JPEG/WebP,最大20 MiB;保存副本,保留原文件)', type: 'file', accept: 'image/png,image/jpeg,image/webp' }] },
1263
1287
  { action: 'image', title: '🎨 画图', capability: 'image-gen', fields: [
1264
1288
  { key: 'prompt', label: '提示词', type: 'textarea', placeholder: '详细描述要生成的图片' },
1265
1289
  { key: 'size', label: '尺寸 (可选)', placeholder: '如 1024*1024' },
@@ -1272,6 +1296,12 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1272
1296
  { action: 'tts', title: '🔊 语音合成', capability: 'tts', fields: [
1273
1297
  { key: 'text', label: '文本', type: 'textarea', placeholder: '要合成的文字' },
1274
1298
  { key: 'voice', label: '音色 (可选)', placeholder: '如 Cherry' }] },
1299
+ { action: 'music', title: '🎵 音乐生成', capability: 'music-gen', fields: [
1300
+ { key: 'prompt', label: '音乐描述', type: 'textarea', placeholder: '描述风格、情绪、乐器;歌词、演唱语言和期望时长也可写在这里' },
1301
+ { key: 'model', label: '音乐模型' },
1302
+ { key: 'timeout_ms', label: '单次等待上限(毫秒,可选)', type: 'number', placeholder: '300000' },
1303
+ { key: 'retry_502', label: '502重新生成次数(可选,会额外占用调用额度)', type: 'number', placeholder: '2(0关闭,最多5)' },
1304
+ { key: 'retry_delay_ms', label: '重试等待(毫秒,可选)', type: 'number', placeholder: '5000' }] },
1275
1305
  { action: 'transcribe', title: '🎙️ 音频转写', capability: 'transcribe', fields: [
1276
1306
  { key: 'audio_path', label: '音频文件', type: 'file', accept: 'audio/*', placeholder: '/path/audio.wav' }] },
1277
1307
  { action: 'video_frames', title: '🎞️ 视频抽帧', fields: [
@@ -1325,6 +1355,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1325
1355
  { cap: 'image-gen', label: '🎨 画图' },
1326
1356
  { cap: 'video-gen', label: '🎬 视频' },
1327
1357
  { cap: 'tts', label: '🔊 语音' },
1358
+ { cap: 'music-gen', label: '🎵 音乐' },
1328
1359
  { cap: 'transcribe', label: '🎙️ 转写' },
1329
1360
  { cap: 'vision', label: '👁 视觉' }
1330
1361
  ];
@@ -1354,7 +1385,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1354
1385
  var opt = opts.find(function (o) { return o.ref === ref; });
1355
1386
  return React.createElement('span', { key: ref, className: 'iris-cap-chip' },
1356
1387
  React.createElement('b', {}, (i + 1) + '.'),
1357
- opt ? opt.label : ref,
1388
+ opt ? opt.label + (opt.unavailable ? ' · 当前不可用' : '') : ref + ' · 配置已失效',
1358
1389
  i > 0 ? React.createElement('button', {
1359
1390
  className: 'iris-pm-btn', title: '上移',
1360
1391
  onClick: function () { var n = list.slice(); var t = n.splice(i, 1)[0]; n.splice(i - 1, 0, t); save(row.cap, n); }
@@ -1370,17 +1401,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1370
1401
  });
1371
1402
  return React.createElement('div', { key: row.cap, className: 'iris-cap-row' },
1372
1403
  React.createElement('span', { className: 'iris-cap-name' }, row.label),
1373
- chips.length ? chips : React.createElement('span', { className: 'iris-cap-auto' }, '自动(按池顺序)'),
1404
+ chips.length ? chips : React.createElement('span', { className: 'iris-cap-auto' }, '尚未分配模型'),
1374
1405
  available.length ? React.createElement('select', {
1375
1406
  className: 'iris-pm-model-sel', value: '',
1376
1407
  onChange: function (e) { if (e.target.value) save(row.cap, list.concat([e.target.value])); }
1377
1408
  },
1378
- React.createElement('option', { value: '' }, '+ 加入 failover'),
1409
+ React.createElement('option', { value: '' }, '+ 分配模型'),
1379
1410
  available.map(function (o) { return React.createElement('option', { key: o.ref, value: o.ref }, o.label); })) : null,
1380
1411
  list.length ? React.createElement('button', {
1381
- className: 'iris-pm-btn', title: '清除手动分配,回退自动',
1412
+ className: 'iris-pm-btn', title: '清空分配后,该功能不再发起新调用',
1382
1413
  onClick: function () { save(row.cap, []); }
1383
- }, '恢复自动') : null);
1414
+ }, '清空分配') : null);
1384
1415
  }));
1385
1416
  }
1386
1417
 
@@ -1534,7 +1565,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1534
1565
  }
1535
1566
 
1536
1567
  function galleryKindLabel(kind) {
1537
- return ({ __proto__: null, 'generated-image': '生成图片', 'generated-video': '生成视频', 'generated-audio': '生成音频',
1568
+ return ({ __proto__: null, 'imported-image': '导入图片', 'generated-image': '生成图片', 'generated-video': '生成视频', 'generated-audio': '生成音频',
1538
1569
  'tts-audio': '合成语音', transcript: '转写文本', 'video-frame': '视频帧', crop: '裁剪图片',
1539
1570
  'cropped-image': '裁剪图片', 'pixel-diff': '差异热图', 'diff-heatmap': '差异热图', 'html-screenshot': 'HTML 截图',
1540
1571
  'legacy-output': '旧版作品' })[kind] || kind;
@@ -1750,19 +1781,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1750
1781
  var running = result && result.status === 'running';
1751
1782
  React.useEffect(function () {
1752
1783
  var active = true;
1753
- postAction('providers_list', {}).then(function (response) {
1784
+ postAction('assignments_get', {}).then(function (response) {
1754
1785
  if (!active) return;
1755
1786
  if (!response.ok || !response.d.ok) throw new Error('模型列表读取失败');
1756
1787
  var choices = [];
1757
- (response.d.providers || []).forEach(function (provider) {
1758
- if (!provider.enabled) return;
1759
- (provider.models || []).forEach(function (entry) {
1760
- if ((entry.capabilities || []).indexOf('image-gen') < 0
1761
- || !entry.imageRouting || entry.imageRouting.mediaProtocol !== 'openai-chat-images') return;
1762
- var health = entry.health && entry.health['image-gen'];
1763
- choices.push({ id: provider.id + '::' + entry.id, label: provider.name + ' · ' + entry.id,
1764
- blocked: !!(health && health.rateLimited) });
1765
- });
1788
+ var pool = response.d.poolByCapability && response.d.poolByCapability['image-gen'] || [];
1789
+ (response.d.order && response.d.order['image-gen'] || []).forEach(function (ref) {
1790
+ var entry = pool.find(function (item) { return item.ref === ref; });
1791
+ if (!entry || !entry.imageRouting || !entry.imageRouting.editSupported) return;
1792
+ choices.push({ id: ref, label: entry.label, blocked: entry.unavailable === true });
1766
1793
  });
1767
1794
  setModels(choices);
1768
1795
  }).catch(function (error) { if (active) { setModels([]); setResult({ status: 'failed', text: error.message }); } });
@@ -1802,7 +1829,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1802
1829
  React.createElement('label', {}, '改图模型', React.createElement('select', { 'aria-label': '改图模型', value: model, disabled: !!running,
1803
1830
  onChange: function (e) { setModel(e.target.value); } }, React.createElement('option', { value: '' }, '按当前顺序选择可用改图模型'),
1804
1831
  (models || []).map(function (entry) { return React.createElement('option', { key: entry.id, value: entry.id, disabled: entry.blocked }, entry.label + (entry.blocked ? ' · 冷却或额度耗尽' : '')); }))),
1805
- models && !models.some(function (entry) { return !entry.blocked; }) ? React.createElement('div', { role: 'status' }, '暂无可用改图模型,请在模型设置中配置聊天生图协议,或手动实测恢复模型。') : null,
1832
+ models && !models.some(function (entry) { return !entry.blocked; }) ? React.createElement('div', { role: 'status' }, '暂无可用改图模型,请在生图名单分配聊天生图或 DashScope Qwen 改图模型,并检查模型是否可用。') : null,
1806
1833
  React.createElement('div', { className: 'iris-gallery-actions' },
1807
1834
  React.createElement('button', { className: 'iris-clean-btn', type: 'button', disabled: !!running || !prompt.trim() || !models || !models.some(function (entry) { return !entry.blocked; }), onClick: execute }, running ? '编辑中…' : '生成修改版'),
1808
1835
  running ? React.createElement('button', { className: 'iris-pm-btn', type: 'button', onClick: cancel }, '停止等待') : null),
@@ -1927,6 +1954,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1927
1954
  var blocked = busy || mutating;
1928
1955
 
1929
1956
  return React.createElement('div', {},
1957
+ React.createElement(ActionCard, CARD_DEFS.find(function (card) { return card.action === 'image_import'; })),
1930
1958
  React.createElement('div', { className: 'iris-gallery-tools' },
1931
1959
  React.createElement('span', { className: 'iris-clean-note' },
1932
1960
  '匹配 ' + data.total + ' 个作品 · Core ' + data.counts.core + ' · 旧版 ' + data.counts.legacy + ' · 清任务历史不会删除'),
@@ -1988,7 +2016,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
1988
2016
  }
1989
2017
 
1990
2018
  function coreCapabilityLabel(value) {
1991
- return ({ image: '图片生成', video: '视频生成', tts: '语音合成', transcribe: '音频转写' })[value] || String(value || '媒体任务');
2019
+ return ({ image: '图片生成', video: '视频生成', tts: '语音合成', music: '音乐生成', transcribe: '音频转写' })[value] || String(value || '媒体任务');
1992
2020
  }
1993
2021
 
1994
2022
  /* 高级诊断与用户任务区共用五类用户状态投影;与服务端 lib/core-user-projection.js 保持同一映射,
@@ -2522,6 +2550,24 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
2522
2550
  note ? React.createElement('div', { className: 'iris-clean-note', style: { width: '100%' } }, note) : null);
2523
2551
  }
2524
2552
 
2553
+ function promptConfigFeedback(state) {
2554
+ var warnings = state && state.feedback && state.feedback.warnings;
2555
+ if (!Array.isArray(warnings) || !warnings.length) return null;
2556
+ return React.createElement('div', { className: 'iris-po-note', role: 'status' }, warnings.map(function (warning, index) {
2557
+ var text = '';
2558
+ if (warning.code === 'config-fallback') {
2559
+ text = warning.reason === 'read-failed' ? '配置读取失败,当前使用 Iris 默认设置。' : '配置内容无效,当前使用 Iris 默认设置。';
2560
+ text += warning.isolation === 'saved' ? '原文件已隔离保留,可在修正后重新导入。'
2561
+ : warning.isolation === 'failed' ? '隔离未完成,Iris 没有覆盖原文件;请检查原文件。' : '原文件未移动;请检查文件读取权限。';
2562
+ } else if (warning.code === 'ignored-fields' && Array.isArray(warning.fields)) {
2563
+ text = '已忽略不支持或当前路由未使用的字段:' + warning.fields.filter(function (field) { return typeof field === 'string'; }).map(function (field) { return JSON.stringify(field); }).join('、');
2564
+ if (warning.count > warning.fields.length) text += '(共 ' + warning.count + ' 个,仅显示前 ' + warning.fields.length + ' 个)';
2565
+ text += '。请检查字段名;这些字段未保存到配置。';
2566
+ }
2567
+ return text ? React.createElement('div', { key: index }, text) : null;
2568
+ }));
2569
+ }
2570
+
2525
2571
  function PromptOptimizerSettings() {
2526
2572
  var pair = React.useState(null);
2527
2573
  var state = pair[0];
@@ -2566,6 +2612,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
2566
2612
  React.createElement('span', { style: { flex: 1 } }, enabled ? '对话框入口已启用' : '对话框入口已关闭'),
2567
2613
  React.createElement('button', { className: 'iris-pm-btn', onClick: toggle }, enabled ? '关闭入口' : '重新启用')),
2568
2614
  React.createElement('div', { className: 'iris-wb-muted' }, '这里只控制对话框提示词优化入口;Iris 工作台、Agent 工具和任务后台始终保持运行。'),
2615
+ promptConfigFeedback(state),
2569
2616
  note ? React.createElement('div', { className: 'iris-clean-note' }, note) : null);
2570
2617
  }
2571
2618
 
@@ -2639,7 +2686,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
2639
2686
  React.createElement('div', { className: 'iris-wb-sec' }, '能力健康'),
2640
2687
  React.createElement(CapabilityHealthOverview, {}),
2641
2688
  React.createElement('div', { className: 'iris-wb-muted' }, '绿色有效 7 天;真实成功或显式实测会刷新。429 冷却显示琥珀色,额度或预算耗尽显示红色;点击模型能力可实测恢复。不会后台探测。'),
2642
- React.createElement('div', { className: 'iris-wb-sec' }, '能力分配(failover 顺序)'),
2689
+ React.createElement('div', { className: 'iris-wb-sec' }, '功能分配(调用顺序)'),
2643
2690
  React.createElement(CapabilityAssigner, {}),
2644
2691
  React.createElement('div', { className: 'iris-wb-sec' }, 'Iris 泡泡快捷卡片(勾选后显示在「⚡ 常用」标签)'),
2645
2692
  React.createElement(BubbleCardPicker, {}),
@@ -2757,6 +2804,18 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
2757
2804
  return { removed: before.slice(start, before.length - end).join(''), added: after.slice(start, after.length - end).join('') };
2758
2805
  }
2759
2806
 
2807
+ function promptRequestSnapshot(processing, title) {
2808
+ var request = processing && processing.request;
2809
+ if (!request || typeof request.system !== 'string' || typeof request.prompt !== 'string') return null;
2810
+ return React.createElement('details', { className: 'iris-po-config' },
2811
+ React.createElement('summary', {}, title),
2812
+ React.createElement('div', { className: 'iris-po-muted' }, 'Iris 发送的指令快照;宿主或供应商后续处理可能不同。'),
2813
+ React.createElement('div', { className: 'iris-po-result-label' }, '系统指令 · 含本次规则与输出协议'),
2814
+ React.createElement('div', { className: 'iris-po-template', 'aria-label': title + ' · 系统指令' }, request.system),
2815
+ React.createElement('div', { className: 'iris-po-result-label' }, '用户输入 · 含素材包装'),
2816
+ React.createElement('div', { className: 'iris-po-template', 'aria-label': title + ' · 用户输入' }, request.prompt));
2817
+ }
2818
+
2760
2819
  function PromptOptimizerControl(props) {
2761
2820
  var draft = props.useInput(function (value) { return value.draft; });
2762
2821
  var phase = props.useInput(function (value) { return value.phase; });
@@ -2822,7 +2881,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
2822
2881
  var supportsAssembly = !!(configState && configState.capabilities && configState.capabilities.assemble === true);
2823
2882
  var editingCapabilities = configState && configState.capabilities && configState.capabilities.editing;
2824
2883
  var supportsFaithful = Array.isArray(editingCapabilities) && editingCapabilities.indexOf('faithful') >= 0 && editingCapabilities.indexOf('rewrite') >= 0;
2884
+ var supportsLocalFaithful = supportsFaithful && configState.capabilities.localFaithful === true;
2825
2885
  var supportsSuggestions = Array.isArray(editingCapabilities) && editingCapabilities.indexOf('suggestions') >= 0;
2886
+ var supportsResetInstructions = !!(configState && configState.capabilities && configState.capabilities.resetInstructions === true);
2826
2887
  function supportsTarget(value) {
2827
2888
  var available = configState && configState.capabilities && configState.capabilities.targets;
2828
2889
  return Array.isArray(available) ? available.indexOf(value) >= 0 : value !== 'image-edit';
@@ -2922,12 +2983,12 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
2922
2983
  if (abortRef.current) abortRef.current.abort();
2923
2984
  var controller = new AbortController();
2924
2985
  var requestSessionId = props.sessionId;
2986
+ var previousResult = result;
2987
+ var previewSnapshot = edited;
2925
2988
  abortRef.current = controller;
2926
2989
  setBusy(true);
2927
- setNote(mode === 'assemble' ? '正在组装,不调用模型…' : '正在调用模型优化…这可能产生模型费用');
2928
- setResult(null);
2929
- setSuggestions(null); setSelectedSuggestions([]); setSuggestionUndo([]);
2930
- lastWrittenRef.current = null;
2990
+ setNote(mode === 'assemble' ? '正在组装,不调用模型…' : supportsLocalFaithful && editing === 'faithful'
2991
+ ? '正在忠实整理…需要分段时可能调用模型并产生费用' : '正在调用模型优化…这可能产生模型费用');
2931
2992
  promptRequest('optimize', {
2932
2993
  text: draft,
2933
2994
  target: target,
@@ -2943,14 +3004,20 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
2943
3004
  if (data.original !== draft) throw new Error('结果原稿与本次请求不一致');
2944
3005
  if (mode === 'optimize' && supportsFaithful && editing === 'faithful'
2945
3006
  && (data.editing?.strategy !== 'faithful' || data.editing.coverage !== 'exact' || data.editing.order !== 'preserved')) throw new Error('后端未确认完整的忠实整理结果');
3007
+ if (resultRef.current !== previousResult || editedRef.current !== previewSnapshot) {
3008
+ setNote('预览已变化,本次结果未替换;你的编辑已保留。'); return;
3009
+ }
2946
3010
  setResult(data);
2947
3011
  setEdited(data.optimized);
3012
+ setSuggestions(null); setSelectedSuggestions([]); setSuggestionUndo([]);
3013
+ lastWrittenRef.current = null;
2948
3014
  resultSessionRef.current = requestSessionId;
2949
- setNote('已生成预览,尚未写回输入框');
3015
+ setNote(data.execution?.reason === 'single-part-faithful' && data.execution.modelInvocations === 0
3016
+ ? '原文无需分段,未调用模型;尚未写回输入框' : '已生成预览,尚未写回输入框');
2950
3017
  }).catch(function (err) {
2951
3018
  if (abortRef.current !== controller) return;
2952
3019
  if (controller.signal.aborted) setNote('已取消');
2953
- else setNote(String(err && err.message || err));
3020
+ else setNote(String(err && err.message || err) + (previousResult ? ';原有预览已保留' : ''));
2954
3021
  }).finally(function () {
2955
3022
  if (abortRef.current === controller) { abortRef.current = null; setBusy(false); }
2956
3023
  });
@@ -2980,7 +3047,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
2980
3047
  || typeof item.text !== 'string' || !item.text.trim() || item.text.length > 600) return false;
2981
3048
  ids.push(item.id); return true;
2982
3049
  }) || !data.questions.every(function (text) { return typeof text === 'string' && text.trim() && text.length <= 600; })) throw new Error('未收到完整补充建议');
2983
- setSuggestions({ source: snapshot, items: data.suggestions, questions: data.questions, adopted: [] });
3050
+ setSuggestions({ source: snapshot, items: data.suggestions, questions: data.questions, adopted: [], processing: data.processing });
2984
3051
  setSelectedSuggestions([]); setSuggestionUndo([]);
2985
3052
  setNote('建议尚未加入正文;只有采纳所选建议才会改变预览。');
2986
3053
  }).catch(function (err) {
@@ -3060,7 +3127,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
3060
3127
  event.target.value = '';
3061
3128
  if (!file) return;
3062
3129
  file.text().then(function (raw) {
3063
- var parsed = JSON.parse(raw);
3130
+ var parsed;
3131
+ try { parsed = JSON.parse(raw); } catch (_) { throw new Error('文件不是有效 JSON,请检查格式后重新导入'); }
3064
3132
  return promptRequest('import', { config: parsed });
3065
3133
  }).then(function (data) {
3066
3134
  publishConfig(data);
@@ -3086,11 +3154,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
3086
3154
  }
3087
3155
  }
3088
3156
 
3089
- function resetConfig() {
3090
- if (!window.confirm('恢复 Iris 内置默认 Prompt、目标模板和会话模型路由?')) return;
3091
- promptRequest('reset', {}).then(function (data) {
3157
+ function resetConfig(scope) {
3158
+ if (scope === 'instructions' && !supportsResetInstructions) { setNote('当前后端不支持仅恢复指令,请更新后重新载入'); return; }
3159
+ var question = scope === 'instructions' ? '仅恢复 Iris 内置系统指令和全部用途模板?模型路由、生成参数和入口开关保持不变。'
3160
+ : '恢复全部 Iris 提示词设置?系统指令、全部用途模板、模型路由、生成参数与预算、入口开关都会重置;模型路由恢复为当前会话,入口重新启用。';
3161
+ if (!window.confirm(question)) return;
3162
+ promptRequest('reset', { scope: scope }).then(function (data) {
3092
3163
  publishConfig(data);
3093
- setNote('已恢复 Iris 内置默认配置');
3164
+ setNote(scope === 'instructions' ? '已恢复默认指令;模型路由、生成参数与入口开关保持不变' : '已恢复全部 Iris 提示词设置');
3094
3165
  }).catch(function (err) {
3095
3166
  setNote('重置失败:' + String(err && err.message || err));
3096
3167
  });
@@ -3120,6 +3191,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
3120
3191
  var resultTarget = result && PROMPT_TARGET_OPTIONS.find(function (item) { return item.value === result.target; });
3121
3192
  var config = configState && configState.config;
3122
3193
  var targetTemplate = config && config.targets && config.targets[target];
3194
+ var requestConfigFeedback = result && promptConfigFeedback({ feedback: result.configFeedback });
3123
3195
  function ruleField(label, value, update, placeholder) {
3124
3196
  return React.createElement('label', { className: 'iris-po-field' }, React.createElement('span', {}, label),
3125
3197
  React.createElement('textarea', { value: value, disabled: busy || !supportsRules, placeholder: placeholder,
@@ -3141,7 +3213,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
3141
3213
  React.createElement('strong', { className: 'iris-po-title' }, React.createElement('span', { className: 'iris-po-mark' }, '🫧'), 'Iris 提示词优化'),
3142
3214
  React.createElement('button', { className: 'iris-po-close', onClick: closePanel, title: '关闭', 'aria-label': '关闭提示词优化' }, '✕')),
3143
3215
  React.createElement('div', { className: 'iris-po-intro' },
3144
- React.createElement('div', { className: 'iris-po-muted' }, '按用途整理草稿,先预览和编辑,再决定是否写回。'),
3216
+ React.createElement('div', { className: 'iris-po-muted' }, '按用途整理草稿,先预览和编辑,再决定是否写回。'),
3217
+ promptConfigFeedback(configState),
3145
3218
  React.createElement('div', { className: 'iris-po-principles' },
3146
3219
  React.createElement('span', {}, '保留原意'), React.createElement('span', {}, '可查看改动'), React.createElement('span', {}, '确认后写回'))),
3147
3220
  React.createElement('details', { className: 'iris-po-section', open: generalOpen,
@@ -3177,7 +3250,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
3177
3250
  React.createElement('button', { className: 'iris-po-primary', disabled: busy || !draft || !draft.trim() || phase !== 'plain' || refCount > 0, onClick: function () { optimize('optimize'); } }, busy ? '处理中…' : supportsFaithful ? editing === 'faithful' ? '忠实整理' : '改写表达' : '智能优化'),
3178
3251
  React.createElement('button', { className: 'iris-po-btn', disabled: busy || !supportsAssembly || !draft || !draft.trim() || phase !== 'plain' || refCount > 0, onClick: function () { optimize('assemble'); } }, '只组装(不调用模型)'),
3179
3252
  busy ? React.createElement('button', { className: 'iris-po-btn', onClick: cancel }, '取消') : null),
3180
- React.createElement('div', { className: 'iris-po-muted' }, '智能优化调用文本模型,可能产生费用;只组装原稿与前后缀。'),
3253
+ React.createElement('div', { className: 'iris-po-muted' }, supportsLocalFaithful
3254
+ ? '单片段忠实整理与只组装不调用模型;其他优化及获取建议可能产生模型费用。'
3255
+ : '智能优化调用文本模型,可能产生费用;只组装原稿与前后缀。'),
3181
3256
  result ? React.createElement('div', { className: 'iris-po-result' },
3182
3257
  React.createElement('div', { className: 'iris-po-result-label' }, '结果预览 · ' + (resultTarget ? resultTarget.label + ' · ' : '') + '可编辑 · ' + edited.length + ' 字符'),
3183
3258
  React.createElement('textarea', { 'aria-label': '结果预览', value: edited, onChange: function (event) { editPreview(event.target.value); } }),
@@ -3197,6 +3272,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
3197
3272
  !suggestions.items.length ? React.createElement('div', { className: 'iris-po-muted' }, '没有额外补充建议。') : null,
3198
3273
  suggestions.questions.length ? React.createElement('div', { className: 'iris-po-suggestion' }, React.createElement('strong', {}, '待确认 · 尚未作为要求'),
3199
3274
  suggestions.questions.map(function (question, index) { return React.createElement('p', { key: index }, question); })) : null,
3275
+ promptRequestSnapshot(suggestions.processing, '本次建议完整指令'),
3200
3276
  React.createElement('div', { className: 'iris-po-actions' },
3201
3277
  React.createElement('button', { className: 'iris-po-btn', disabled: busy || !selectedSuggestions.length, onClick: adoptSuggestions }, '采纳所选建议'),
3202
3278
  React.createElement('button', { className: 'iris-po-btn', disabled: busy || !suggestionUndo.length, onClick: undoSuggestions }, '撤回上次采纳'))) : null) : null,
@@ -3212,7 +3288,13 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
3212
3288
  return React.createElement('div', { className: 'iris-po-diff', key: rule.id }, rule.label + '(' + (rule.kind === 'optimization' ? '指导改写' : rule.position === 'prefix' ? '输出前缀' : '输出后缀') + ')\n' + rule.text);
3213
3289
  }) : React.createElement('div', { className: 'iris-po-muted' }, '未选择额外规则'),
3214
3290
  result.processing ? React.createElement('div', { className: 'iris-po-diff' }, '优化处理规则:\n' + result.processing.systemPrompt + '\n\n目标模板:\n' + result.processing.targetTemplate) : null),
3215
- React.createElement('div', { className: 'iris-po-route' }, result.route ? '实际使用:' + result.route.provider + ' / ' + result.route.model + ' · 思考:' + (result.route.reasoningEffort === 'provider-default' ? '供应商默认' : result.route.reasoningEffort) : '本次只组装,未调用模型'),
3291
+ promptRequestSnapshot(result.processing, '本次正文处理完整指令'),
3292
+ React.createElement('div', { className: 'iris-po-route' }, result.route ? '实际使用:' + result.route.provider + ' / ' + result.route.model + ' · 思考:' + (result.route.reasoningEffort === 'provider-default' ? '供应商默认' : result.route.reasoningEffort)
3293
+ : result.execution?.reason === 'single-part-faithful' && result.execution.modelInvocations === 0 ? '忠实整理:无需分段,未调用模型'
3294
+ : result.mode === 'assemble' ? '本次只组装,未调用模型' : '未提供模型调用信息'),
3295
+ result.route && result.route.reason ? React.createElement('div', { className: 'iris-po-muted' }, '选择原因:' + result.route.reason + ' · ' + (result.route.textInput === 'supported' ? '宿主声明支持文本输入' : '文本输入能力未知')) : null,
3296
+ requestConfigFeedback ? React.createElement('details', { className: 'iris-po-config' },
3297
+ React.createElement('summary', {}, '本次请求的配置反馈'), requestConfigFeedback) : null,
3216
3298
  React.createElement('div', { className: 'iris-po-actions' },
3217
3299
  React.createElement('button', { className: 'iris-po-primary', onClick: function () { replaceDraft(edited); } }, '写回输入框'),
3218
3300
  React.createElement('button', { className: 'iris-po-btn', onClick: copyResult }, '复制'),
@@ -3226,11 +3308,13 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
3226
3308
  React.createElement('div', { className: 'iris-po-template' }, config && config.systemPrompt || '配置尚未载入'),
3227
3309
  React.createElement('div', { className: 'iris-po-template' }, targetTemplate || '当前后端未提供此目标模板')),
3228
3310
  React.createElement('div', { className: 'iris-po-result-label' }, 'JSON 配置'),
3229
- React.createElement('div', { className: 'iris-po-muted' }, '可导出后修改 systemPrompt、targets、route 和 generation,再重新导入。'),
3311
+ React.createElement('div', { className: 'iris-po-muted' }, '导入会替换当前配置;局部 JSON 缺少的字段使用内置默认值。可导出后修改 systemPrompt、targets、route 和 generation,再重新导入。'),
3230
3312
  React.createElement('div', { className: 'iris-po-actions' },
3231
3313
  React.createElement('button', { className: 'iris-po-btn', onClick: exportConfig }, '导出'),
3232
- React.createElement('button', { className: 'iris-po-btn', onClick: function () { if (fileRef.current) fileRef.current.click(); } }, '导入'),
3233
- React.createElement('button', { className: 'iris-po-btn danger', onClick: resetConfig }, '恢复默认'),
3314
+ React.createElement('button', { className: 'iris-po-btn', disabled: busy, onClick: function () { if (fileRef.current) fileRef.current.click(); } }, '导入'),
3315
+ React.createElement('button', { className: 'iris-po-btn', disabled: busy || !supportsResetInstructions,
3316
+ title: supportsResetInstructions ? '只更新系统指令与全部用途模板' : '当前后端尚未支持仅恢复指令', onClick: function () { resetConfig('instructions'); } }, '仅恢复默认指令'),
3317
+ React.createElement('button', { className: 'iris-po-btn danger', disabled: busy, onClick: function () { resetConfig('all'); } }, '恢复全部设置'),
3234
3318
  React.createElement('input', { ref: fileRef, className: 'iris-po-file', type: 'file', accept: 'application/json,.json', onChange: importConfig })),
3235
3319
  React.createElement('div', { className: 'iris-po-muted' }, '当前:' + (configState && configState.source === 'custom' ? '用户 JSON' : 'Iris 默认'))),
3236
3320
  React.createElement('button', { className: 'iris-po-disable', disabled: busy, onClick: disableEntry }, '关闭此对话入口(工作台仍保留)')));
@@ -20,11 +20,13 @@ import { GenerationInputError, normalizeGenerationInput } from './generation-inp
20
20
  import { videoTaskCandidates } from './video-input.js';
21
21
  import { waitForCoreTask } from './task-wait.js';
22
22
  import { planCoreDeletion, planCoreCleanup, quarantineCoreFiles, inspectCoreTransactions, restoreCoreTransaction } from './core-maintenance.js';
23
+ import { deliveryCacheNeedsProvider } from './core-delivery-cache.js';
24
+ import { readImportImage } from './image-import.js';
23
25
 
24
26
  export const COMMAND_CONTRACT_VERSION = 0;
25
27
  export const CORE_COMMANDS = Object.freeze([
26
28
  'crop', 'media.diff', 'media.frames', 'media.html', 'task.inspect', 'task.list', 'task.wait', 'task.observe', 'task.reobserve', 'task.redeliver', 'task.cancel', 'task.retry',
27
- 'task.inspect-many', 'artifact.inspect', 'artifact.inspect-many', 'artifact.list', 'artifact.export', 'artifact.export-many', 'artifact.rebuild',
29
+ 'task.inspect-many', 'artifact.import', 'artifact.inspect', 'artifact.inspect-many', 'artifact.list', 'artifact.export', 'artifact.export-many', 'artifact.rebuild',
28
30
  'core.delete', 'core.cleanup', 'core.transactions', 'core.restore'
29
31
  ]);
30
32
 
@@ -108,6 +110,20 @@ export function createCommandService(runtime, ports = {}) {
108
110
 
109
111
  async function execute(name, input) {
110
112
  if (!CORE_COMMANDS.includes(name)) throw commandError('IRIS_COMMAND_UNKNOWN', `未知 Command:${String(name)}`);
113
+ if (name === 'artifact.import') {
114
+ exactInput(input, ['image_path', 'image_bytes']);
115
+ const hasPath = input.image_path !== undefined;
116
+ if (hasPath === (input.image_bytes !== undefined)) {
117
+ throw commandError('IRIS_COMMAND_INPUT_INVALID', '图片导入须且只能提供image_path或image_bytes之一');
118
+ }
119
+ const filePath = hasPath ? localImage(input) : undefined;
120
+ return runtime.run('execute', async ({ dataRoot, signal }) => {
121
+ const source = await readImportImage({ filePath, imageBytes: input.image_bytes }, signal);
122
+ signal.throwIfAborted();
123
+ const artifact = createCoreArtifact(dataRoot, { ...source, kind: 'imported-image' });
124
+ return Object.freeze({ contractVersion: COMMAND_CONTRACT_VERSION, command: name, artifact });
125
+ });
126
+ }
111
127
  if (name === 'core.delete' || name === 'core.cleanup') {
112
128
  exactInput(input, name === 'core.delete' ? ['task_ids', 'artifact_ids', 'confirm_delete'] : ['paths', 'older_than_ms', 'confirm_delete']);
113
129
  if (input.confirm_delete !== undefined && typeof input.confirm_delete !== 'boolean') throw commandError('IRIS_COMMAND_INPUT_INVALID', 'confirm_delete 必须是布尔值');
@@ -366,16 +382,20 @@ export function createCommandService(runtime, ports = {}) {
366
382
  });
367
383
  }
368
384
  // task.redeliver:只对 outcome=succeeded / deliveryState=failed 的任务重新取回
369
- // 已生成的远端产物(re-poll 拿产物清单,绝不 submit,也不会重新生成)。
385
+ // 已生成产物:同步结果读私有缓存,异步结果 re-poll;绝不 submit。
370
386
  if (name === 'task.redeliver') {
371
387
  exactInput(input, ['task_id']);
372
388
  return runtime.run('execute', async ({ dataRoot }) => {
373
389
  const before = inspectCoreTask(dataRoot, input.task_id);
374
- if (before.outcome !== 'succeeded' || before.deliveryState !== 'failed' || !before.remoteTaskId) throw commandError('IRIS_TASK_NOT_REDELIVERABLE', 'Task 不处于可重新交付状态;不会重新生成');
390
+ if (before.outcome !== 'succeeded' || before.deliveryState !== 'failed' || (!before.remoteTaskId && !before.deliveryCache)) throw commandError('IRIS_TASK_NOT_REDELIVERABLE', 'Task 不处于可重新交付状态;不会重新生成');
391
+ if (before.deliveryCache && !deliveryCacheNeedsProvider(dataRoot, before)) {
392
+ const task = await createProviderTaskRunner(runtime).redeliver(input.task_id);
393
+ return Object.freeze({ contractVersion: COMMAND_CONTRACT_VERSION, command: name, taskId: task.id, task });
394
+ }
375
395
  if (typeof ports.resolveTaskAdapter !== 'function') throw commandError('IRIS_COMMAND_PROVIDER_REQUIRED', 'task.redeliver 需要宿主显式提供 Task Provider resolver');
376
396
  const adapter = await ports.resolveTaskAdapter(before);
377
397
  if (!adapter || adapter.id !== before.providerId || !adapter.capabilities?.includes(before.capability)) throw commandError('IRIS_PROVIDER_TASK_IDENTITY_MISMATCH', 'Task Provider resolver 返回了不匹配的 Adapter');
378
- if (!hasProviderOperation(adapter, 'poll')) throw commandError('IRIS_PROVIDER_TASK_OBSERVE_UNSUPPORTED', 'Task 原 Provider 不支持 poll;不会重新生成');
398
+ if (!before.deliveryCache && !hasProviderOperation(adapter, 'poll')) throw commandError('IRIS_PROVIDER_TASK_OBSERVE_UNSUPPORTED', 'Task 原 Provider 不支持 poll;不会重新生成');
379
399
  const task = await createProviderTaskRunner(runtime).redeliver(input.task_id, adapter);
380
400
  return Object.freeze({ contractVersion: COMMAND_CONTRACT_VERSION, command: name, taskId: task.id, task });
381
401
  });
@@ -415,6 +435,10 @@ export function createCommandService(runtime, ports = {}) {
415
435
  throw commandError('IRIS_TASK_NOT_RETRYABLE', '只有终态且未成功交付的 Task 可以重试为新任务');
416
436
  }
417
437
  let normalized;
438
+ if (before.capability === 'music' && input.provider_input &&
439
+ ['retry_502', 'retry_delay_ms'].some(field => Object.hasOwn(input.provider_input, field))) {
440
+ throw commandError('IRIS_COMMAND_INPUT_INVALID', 'task.retry 只执行一次重新生成;502重试配置请使用Music生成入口');
441
+ }
418
442
  try { normalized = normalizeGenerationInput(before.capability, input.provider_input, { allowVideoPaths: before.capability === 'video' }); }
419
443
  catch (error) {
420
444
  if (error instanceof GenerationInputError) throw commandError('IRIS_COMMAND_INPUT_INVALID', error.message);