@mindexec/cli 0.2.14 → 0.2.16

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 (20) hide show
  1. package/package.json +1 -1
  2. package/remote-hub.js +94 -0
  3. package/scripts/remote-fleet-render-smoke.mjs +55 -0
  4. package/scripts/remote-hub-smoke.mjs +21 -1
  5. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +53 -14
  6. package/wwwroot/_framework/MindExecution.Core.gvaopvh5wg.dll +0 -0
  7. package/wwwroot/_framework/{MindExecution.Kernel.lqglgq2jmo.dll → MindExecution.Kernel.hpf3wr4dv3.dll} +0 -0
  8. package/wwwroot/_framework/{MindExecution.Plugins.Admin.2jqptn6ylw.dll → MindExecution.Plugins.Admin.y0euqt1gu1.dll} +0 -0
  9. package/wwwroot/_framework/{MindExecution.Plugins.Business.8hzwity3fl.dll → MindExecution.Plugins.Business.9zzc31j54n.dll} +0 -0
  10. package/wwwroot/_framework/{MindExecution.Plugins.Concept.wqdtoeuumu.dll → MindExecution.Plugins.Concept.sihn2obrrn.dll} +0 -0
  11. package/wwwroot/_framework/{MindExecution.Plugins.Directory.hr2p0grenr.dll → MindExecution.Plugins.Directory.zwbshhfvpb.dll} +0 -0
  12. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.fpq5ydr4s0.dll → MindExecution.Plugins.PlanMaster.47f3afsrf8.dll} +0 -0
  13. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.43m89bf01o.dll → MindExecution.Plugins.YouTube.rpolgpee6c.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Shared.y1hdnsq8zq.dll → MindExecution.Shared.zm6btxgvlg.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Web.ox678bzo06.dll → MindExecution.Web.7b082n0t0o.dll} +0 -0
  16. package/wwwroot/_framework/blazor.boot.json +21 -21
  17. package/wwwroot/index.html +1 -1
  18. package/wwwroot/service-worker-assets.js +24 -24
  19. package/wwwroot/service-worker.js +1 -1
  20. package/wwwroot/_framework/MindExecution.Core.q469sducjw.dll +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
package/remote-hub.js CHANGED
@@ -5,6 +5,7 @@ import crypto from 'crypto';
5
5
  const DEFAULT_REMOTE_HUB_PORT = 5197;
6
6
  const DEFAULT_REMOTE_HUB_HOST = '127.0.0.1';
7
7
  const DEFAULT_HEARTBEAT_MS = 5000;
8
+ const DEFAULT_AGENT_TASK_TIMEOUT_MS = 120000;
8
9
  const MAX_LINE_CHARS = 4 * 1024 * 1024;
9
10
  const MAX_THUMBNAIL_BASE64_CHARS = 3 * 1024 * 1024;
10
11
  const MAX_STREAM_BASE64_CHARS = 3 * 1024 * 1024;
@@ -79,6 +80,7 @@ function createDeviceCounters(overrides = {}) {
79
80
  tasksQueued: 0,
80
81
  taskResultsReceived: 0,
81
82
  taskResultsFailed: 0,
83
+ taskResultsTimedOut: 0,
82
84
  ...overrides
83
85
  };
84
86
  }
@@ -172,6 +174,11 @@ export function createRemoteHub(options = {}) {
172
174
  const host = safeString(env.REMOTE_HUB_HOST || DEFAULT_REMOTE_HUB_HOST, 128);
173
175
  const requestedPort = normalizePort(env.REMOTE_HUB_PORT || DEFAULT_REMOTE_HUB_PORT);
174
176
  const heartbeatMs = clampNumber(env.REMOTE_HUB_HEARTBEAT_MS, 1000, 60000, DEFAULT_HEARTBEAT_MS);
177
+ const taskTimeoutMs = clampNumber(
178
+ options.taskTimeoutMs ?? env.REMOTE_HUB_TASK_TIMEOUT_MS,
179
+ 50,
180
+ 30 * 60 * 1000,
181
+ DEFAULT_AGENT_TASK_TIMEOUT_MS);
175
182
  const pairToken = safeString(
176
183
  env.REMOTE_HUB_PAIR_TOKEN || env.MINDEXEC_REMOTE_PAIR_TOKEN || crypto.randomBytes(6).toString('hex'),
177
184
  256);
@@ -194,6 +201,7 @@ export function createRemoteHub(options = {}) {
194
201
  protocol: 'tcp-jsonl',
195
202
  protocolVersion: REMOTE_PROTOCOL_VERSION,
196
203
  heartbeatMs,
204
+ taskTimeoutMs,
197
205
  agentPackage: '@mindexec/remote',
198
206
  agentEndpoint: `${host}:${boundPort || requestedPort}`,
199
207
  pairToken: includeSecrets ? pairToken : undefined,
@@ -307,6 +315,7 @@ export function createRemoteHub(options = {}) {
307
315
  }
308
316
 
309
317
  existing.lastDisconnectReason = 'replaced-by-new-session';
318
+ failAllPendingTasks(existing, 'replaced-by-new-session');
310
319
  writeJsonLine(existing.socket, {
311
320
  type: 'disconnect',
312
321
  reason: 'replaced-by-new-session',
@@ -427,6 +436,7 @@ export function createRemoteHub(options = {}) {
427
436
  latestTask,
428
437
  recentTasks: latestTask ? [latestTask] : [],
429
438
  pendingTaskCommands: new Map(),
439
+ pendingTaskTimers: new Map(),
430
440
  counters: createDeviceCounters({
431
441
  messagesReceived: 1,
432
442
  statusReceived: 1,
@@ -532,6 +542,7 @@ export function createRemoteHub(options = {}) {
532
542
  latestTask: null,
533
543
  recentTasks: [],
534
544
  pendingTaskCommands: new Map(),
545
+ pendingTaskTimers: new Map(),
535
546
  synthetic: false,
536
547
  counters: createDeviceCounters({ messagesReceived: 1 })
537
548
  };
@@ -573,6 +584,7 @@ export function createRemoteHub(options = {}) {
573
584
  device.activeLiveStream.stoppedAt = device.disconnectedAt;
574
585
  device.activeLiveStream.stopReason = reason;
575
586
  }
587
+ failAllPendingTasks(device, 'device-disconnected');
576
588
  logWarn('remote', `device disconnected ${device.deviceName} (${deviceId}): ${reason}`);
577
589
  emitRemoteEvent('RemoteDeviceDisconnected', device, { reason });
578
590
  }
@@ -601,6 +613,84 @@ export function createRemoteHub(options = {}) {
601
613
  return task;
602
614
  }
603
615
 
616
+ function clearTaskTimeout(device, commandId) {
617
+ const key = safeString(commandId, 128);
618
+ if (!device?.pendingTaskTimers || !key) {
619
+ return;
620
+ }
621
+
622
+ const timer = device.pendingTaskTimers.get(key);
623
+ if (timer) {
624
+ clearTimeout(timer);
625
+ }
626
+ device.pendingTaskTimers.delete(key);
627
+ }
628
+
629
+ function failPendingTask(device, commandId, error = 'task-timeout') {
630
+ const key = safeString(commandId, 128);
631
+ if (!device?.pendingTaskCommands || !key) {
632
+ return null;
633
+ }
634
+
635
+ const task = device.pendingTaskCommands.get(key);
636
+ if (!task) {
637
+ clearTaskTimeout(device, key);
638
+ return null;
639
+ }
640
+
641
+ const now = new Date().toISOString();
642
+ clearTaskTimeout(device, key);
643
+ task.status = 'failed';
644
+ task.updatedAt = now;
645
+ task.completedAt = now;
646
+ task.error = safeString(error, 500) || 'task-timeout';
647
+ task.resultSummary = task.error;
648
+ task.resultKind = task.resultKind || 'agent-task';
649
+ device.latestTask = task;
650
+ device.pendingTaskCommands.delete(key);
651
+ device.counters.taskResultsReceived += 1;
652
+ device.counters.taskResultsFailed += 1;
653
+ if (task.error === 'task-timeout') {
654
+ device.counters.taskResultsTimedOut += 1;
655
+ }
656
+ emitRemoteEvent('RemoteTaskResult', device, {
657
+ commandId: key,
658
+ taskId: task.taskId,
659
+ status: task.status,
660
+ error: task.error
661
+ });
662
+ return task;
663
+ }
664
+
665
+ function failAllPendingTasks(device, error = 'device-disconnected') {
666
+ if (!device?.pendingTaskCommands) {
667
+ return 0;
668
+ }
669
+
670
+ const commandIds = [...device.pendingTaskCommands.keys()];
671
+ let failed = 0;
672
+ for (const commandId of commandIds) {
673
+ if (failPendingTask(device, commandId, error)) {
674
+ failed += 1;
675
+ }
676
+ }
677
+ return failed;
678
+ }
679
+
680
+ function scheduleTaskTimeout(device, task) {
681
+ const commandId = safeString(task?.commandId, 128);
682
+ if (!device?.pendingTaskTimers || !commandId) {
683
+ return;
684
+ }
685
+
686
+ clearTaskTimeout(device, commandId);
687
+ const timer = setTimeout(() => {
688
+ failPendingTask(device, commandId, 'task-timeout');
689
+ }, taskTimeoutMs);
690
+ timer.unref?.();
691
+ device.pendingTaskTimers.set(commandId, timer);
692
+ }
693
+
604
694
  function summarizeTaskResult(result) {
605
695
  if (result && typeof result === 'object') {
606
696
  return safeText(
@@ -634,6 +724,7 @@ export function createRemoteHub(options = {}) {
634
724
  const status = error
635
725
  ? 'failed'
636
726
  : safeString(result?.status, 40) || 'completed';
727
+ clearTaskTimeout(device, commandId);
637
728
  task.status = status;
638
729
  task.updatedAt = now;
639
730
  task.completedAt = safeString(result?.completedAt, 80) || now;
@@ -903,6 +994,7 @@ export function createRemoteHub(options = {}) {
903
994
 
904
995
  async function close() {
905
996
  for (const device of devices.values()) {
997
+ failAllPendingTasks(device, 'hub-shutdown');
906
998
  if (device.socket && !device.socket.destroyed) {
907
999
  writeJsonLine(device.socket, { type: 'disconnect', reason: 'hub-shutdown' });
908
1000
  device.socket.destroy();
@@ -958,6 +1050,7 @@ export function createRemoteHub(options = {}) {
958
1050
  device.activeLiveStream.stoppedAt = device.disconnectedAt;
959
1051
  device.activeLiveStream.stopReason = reason;
960
1052
  }
1053
+ failAllPendingTasks(device, 'device-disconnected');
961
1054
  emitRemoteEvent('RemoteDeviceDisconnected', device, { reason, synthetic: true });
962
1055
  return true;
963
1056
  }
@@ -1145,6 +1238,7 @@ export function createRemoteHub(options = {}) {
1145
1238
  device.counters.commandsSent += 1;
1146
1239
  device.counters.tasksQueued += 1;
1147
1240
  rememberDeviceTask(device, task);
1241
+ scheduleTaskTimeout(device, task);
1148
1242
  emitRemoteEvent('RemoteTaskQueued', device, {
1149
1243
  commandId,
1150
1244
  taskId,
@@ -408,6 +408,10 @@ async function loadCss3DManager() {
408
408
  return { manager: context.window.MindMapCss3DManager, document };
409
409
  }
410
410
 
411
+ function wait(ms = 0) {
412
+ return new Promise(resolve => setTimeout(resolve, ms));
413
+ }
414
+
411
415
  function buildMonitorNode(devices, hubStatus) {
412
416
  const connected = devices.filter(device => device.Connected).length;
413
417
  const endpoint = hubStatus.agentEndpoint || '127.0.0.1:5197';
@@ -527,10 +531,42 @@ try {
527
531
  const aiCount = devices.filter(device => device.Connected && device.AiAssistEnabled).length;
528
532
  const focusedDevice = devices.find(device => device.Connected);
529
533
  assert.ok(focusedDevice);
534
+ focusedDevice.LatestTaskId = 'render-smoke-timeout-task';
535
+ focusedDevice.LatestTaskCommandId = 'render-smoke-timeout-command';
536
+ focusedDevice.LatestTaskTitle = 'Timed out smoke task';
537
+ focusedDevice.LatestTaskInstructionPreview = 'This synthetic task timed out before the agent replied.';
538
+ focusedDevice.LatestTaskStatus = 'failed';
539
+ focusedDevice.LatestTaskApprovalLevel = 'task-only';
540
+ focusedDevice.LatestTaskUpdatedAt = new Date().toISOString();
541
+ focusedDevice.LatestTaskError = 'task-timeout';
542
+ focusedDevice.LatestTaskResultSummary = '';
530
543
 
531
544
  const { manager, document } = await loadCss3DManager();
532
545
  assert.equal(typeof manager?.renderRemoteFleetMonitorForTest, 'function');
533
546
  assert.equal(typeof manager?.renderRemoteFleetDeviceForTest, 'function');
547
+ assert.equal(typeof manager?.setModuleForTest, 'function');
548
+
549
+ const dotNetCalls = [];
550
+ const dotNetHelper = {
551
+ invokeMethodAsync: async (methodName, ...args) => {
552
+ dotNetCalls.push({ methodName, args });
553
+ if (methodName === 'DispatchRemoteFleetTaskBatchFromJs') {
554
+ const targetIds = Array.isArray(args[1]) ? args[1] : [];
555
+ const failedDeviceId = targetIds[targetIds.length - 1] || 'synthetic-pc-failed';
556
+ return {
557
+ success: true,
558
+ total: targetIds.length,
559
+ queued: Math.max(0, targetIds.length - 1),
560
+ failed: 1,
561
+ failedDeviceIds: [failedDeviceId],
562
+ failurePreview: `${failedDeviceId}: device-ai-assist-unavailable`
563
+ };
564
+ }
565
+
566
+ return { success: true };
567
+ }
568
+ };
569
+ manager.setModuleForTest({ dotNetHelper });
534
570
 
535
571
  const bodyView = document.createElement('div');
536
572
  bodyView.dataset.remoteFleetAutoMonitor = 'false';
@@ -550,6 +586,7 @@ try {
550
586
  assert.ok(bodyView.textContent.includes('all devices, no paging'));
551
587
  assert.ok(bodyView.querySelector('code')?.textContent.includes('npx @mindexec/remote connect'));
552
588
  assert.ok(bodyView.textContent.includes('synthetic-render-ai'));
589
+ assert.ok(bodyView.textContent.includes('Task timed out waiting for the agent response.'));
553
590
  assert.ok(devices.some(device => /^synthetic-response-/.test(device.LatestTaskResultResponseId)));
554
591
 
555
592
  const searchInput = bodyView.querySelector('[data-remote-fleet-search="true"]');
@@ -580,12 +617,30 @@ try {
580
617
  assert.ok(bodyView.querySelectorAll('[data-remote-fleet-group-header="true"]').length >= 2);
581
618
  assert.equal(bodyView.querySelectorAll('article[data-device-id]').length, SYNTHETIC_COUNT);
582
619
 
620
+ const taskInput = bodyView.querySelector('[data-remote-fleet-task-input="true"]');
621
+ const sendVisibleButton = bodyView.querySelector('[data-remote-fleet-action="task-visible"]');
622
+ assert.ok(taskInput);
623
+ assert.ok(sendVisibleButton);
624
+ taskInput.value = 'Dispatch smoke partial failure';
625
+ sendVisibleButton.dispatchEvent({ type: 'click' });
626
+ await wait();
627
+ const batchCall = dotNetCalls.find(call => call.methodName === 'DispatchRemoteFleetTaskBatchFromJs');
628
+ assert.ok(batchCall);
629
+ const batchTargetIds = Array.isArray(batchCall.args[1]) ? batchCall.args[1] : [];
630
+ assert.equal(batchTargetIds.length, connectedCount);
631
+ const feedback = bodyView.querySelector('[data-remote-fleet-task-feedback="true"]');
632
+ assert.ok(feedback);
633
+ assert.equal(feedback.style.display, 'block');
634
+ assert.match(feedback.textContent, new RegExp(`Queued ${connectedCount - 1}/${connectedCount} visible remote task\\(s\\); 1 failed`));
635
+ assert.match(feedback.textContent, /device-ai-assist-unavailable/);
636
+
583
637
  const deviceBody = document.createElement('div');
584
638
  document.body.appendChild(deviceBody);
585
639
  manager.renderRemoteFleetDeviceForTest(deviceBody, buildDeviceNode(focusedDevice, hub.getStatus()));
586
640
  assert.ok(deviceBody.textContent.includes(focusedDevice.Name));
587
641
  assert.ok(deviceBody.querySelector('[data-remote-fleet-action="refresh-device"]'));
588
642
  assert.ok(deviceBody.querySelector('[data-remote-fleet-action="task-device"]'));
643
+ assert.ok(deviceBody.textContent.includes('Task timed out waiting for the agent response.'));
589
644
  assert.ok(deviceBody.textContent.includes(focusedDevice.DeviceId));
590
645
 
591
646
  console.log(`RemoteFleet render smoke OK (${SYNTHETIC_COUNT} synthetic devices, ${connectedCount} connected, ${aiCount} AI-ready)`);
@@ -30,7 +30,8 @@ const hub = createRemoteHub({
30
30
  MINDEXEC_REMOTE_HUB: '1',
31
31
  REMOTE_HUB_HOST: '127.0.0.1',
32
32
  REMOTE_HUB_PORT: '0',
33
- REMOTE_HUB_PAIR_TOKEN: 'smoke-token'
33
+ REMOTE_HUB_PAIR_TOKEN: 'smoke-token',
34
+ REMOTE_HUB_TASK_TIMEOUT_MS: '120'
34
35
  }
35
36
  });
36
37
 
@@ -230,6 +231,25 @@ try {
230
231
  assert.equal(aiTaskDevice.counters.tasksQueued, 2);
231
232
  assert.equal(aiTaskDevice.counters.taskResultsReceived, 2);
232
233
 
234
+ const timeoutTaskCommand = hub.requestAgentTask('smoke-device', {
235
+ instruction: 'This task intentionally receives no agent response.',
236
+ title: 'Timeout task'
237
+ });
238
+ assert.equal(timeoutTaskCommand.ok, true);
239
+ const timeoutTaskDevice = await waitFor(() => {
240
+ const current = hub.listDevices();
241
+ return current[0]?.latestTask?.taskId === timeoutTaskCommand.taskId
242
+ && current[0]?.latestTask?.status === 'failed'
243
+ ? current[0]
244
+ : null;
245
+ }, 1000);
246
+ assert.equal(timeoutTaskDevice.latestTask.error, 'task-timeout');
247
+ assert.equal(timeoutTaskDevice.latestTask.resultSummary, 'task-timeout');
248
+ assert.equal(timeoutTaskDevice.counters.tasksQueued, 3);
249
+ assert.equal(timeoutTaskDevice.counters.taskResultsReceived, 3);
250
+ assert.equal(timeoutTaskDevice.counters.taskResultsFailed, 1);
251
+ assert.equal(timeoutTaskDevice.counters.taskResultsTimedOut, 1);
252
+
233
253
  socket.destroy();
234
254
  await waitFor(() => hub.listDevices()[0]?.connected === false);
235
255
  console.log('RemoteHub smoke OK');
@@ -11350,6 +11350,10 @@
11350
11350
  });
11351
11351
  }
11352
11352
 
11353
+ function setModuleForTest(module) {
11354
+ _module = module || null;
11355
+ }
11356
+
11353
11357
  function getTemplateCardConfig(nodeModel) {
11354
11358
  const raw = nodeModel?.response ?? nodeModel?.Response ?? '';
11355
11359
  const metadata = nodeModel?.metadata ?? nodeModel?.Metadata ?? {};
@@ -12200,6 +12204,22 @@
12200
12204
  return getRemoteFleetDeviceField(device, 'thumbnailEnabled', 'ThumbnailEnabled', false) === true;
12201
12205
  }
12202
12206
 
12207
+ function formatRemoteFleetTaskError(error) {
12208
+ const value = String(error || '').trim();
12209
+ switch (value) {
12210
+ case 'task-timeout':
12211
+ return 'Task timed out waiting for the agent response.';
12212
+ case 'device-disconnected':
12213
+ return 'Agent disconnected before returning a task result.';
12214
+ case 'replaced-by-new-session':
12215
+ return 'Agent reconnected before returning a task result.';
12216
+ case 'hub-shutdown':
12217
+ return 'RemoteHub shut down before the task completed.';
12218
+ default:
12219
+ return value;
12220
+ }
12221
+ }
12222
+
12203
12223
  function getRemoteFleetGroupInfo(device, groupMode) {
12204
12224
  const mode = String(groupMode || 'none');
12205
12225
  const connected = isRemoteFleetDeviceConnected(device);
@@ -12597,7 +12617,7 @@
12597
12617
  : taskLine.textContent;
12598
12618
  taskLine.style.cssText = `color:${taskTone === 'error' ? '#991b1b' : taskTone === 'done' ? '#047857' : '#1d4ed8'};font-size:10px;font-weight:900;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;`;
12599
12619
  const taskSummary = document.createElement('div');
12600
- taskSummary.textContent = latestTaskError || latestTaskResult || latestTaskTitle || 'Task queued';
12620
+ taskSummary.textContent = formatRemoteFleetTaskError(latestTaskError) || latestTaskResult || latestTaskTitle || 'Task queued';
12601
12621
  taskSummary.title = taskSummary.textContent;
12602
12622
  taskSummary.style.cssText = 'color:#334155;font-size:11px;font-weight:750;line-height:1.3;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;letter-spacing:0;';
12603
12623
  taskBox.appendChild(taskLine);
@@ -13637,7 +13657,7 @@
13637
13657
  letter-spacing: 0;
13638
13658
  `;
13639
13659
  const taskSummary = document.createElement('div');
13640
- taskSummary.textContent = latestTaskError || latestTaskResult || latestTaskTitle || 'Task queued';
13660
+ taskSummary.textContent = formatRemoteFleetTaskError(latestTaskError) || latestTaskResult || latestTaskTitle || 'Task queued';
13641
13661
  taskSummary.title = taskSummary.textContent;
13642
13662
  taskSummary.style.cssText = `
13643
13663
  color: #334155;
@@ -13775,6 +13795,30 @@
13775
13795
  ? '#047857'
13776
13796
  : '#1d4ed8';
13777
13797
  };
13798
+ const formatRemoteFleetDispatchFeedback = (result, fallbackTargetCount = 0, modeLabel = 'remote task') => {
13799
+ const total = Number(result?.total || fallbackTargetCount || 0);
13800
+ const queued = Number(result?.queued || 0);
13801
+ const failed = Number(result?.failed || Math.max(0, total - queued));
13802
+ const failurePreview = String(result?.failurePreview || '').trim();
13803
+ if (!result?.success && queued <= 0) {
13804
+ return {
13805
+ tone: 'error',
13806
+ text: result?.error || `No ${modeLabel}s were queued.`
13807
+ };
13808
+ }
13809
+
13810
+ if (failed > 0) {
13811
+ return {
13812
+ tone: 'error',
13813
+ text: `Queued ${queued}/${total || queued} ${modeLabel}(s); ${failed} failed${failurePreview ? ` - ${failurePreview}` : ''}.`
13814
+ };
13815
+ }
13816
+
13817
+ return {
13818
+ tone: 'success',
13819
+ text: `Queued ${queued || total || 1}/${total || queued || 1} ${modeLabel}(s).`
13820
+ };
13821
+ };
13778
13822
 
13779
13823
  const readTaskInstruction = () => String(taskInput.value || '').trim();
13780
13824
  const useAiAssist = () => aiToggle.checked === true;
@@ -13930,12 +13974,9 @@
13930
13974
  try {
13931
13975
  const result = await invokeDotNetAsync('DispatchRemoteFleetTaskBatchFromJs', nodeId, targetIds, instruction, useAiAssist());
13932
13976
  await syncRemoteFleetNodeStateFromResult(result);
13933
- if (result?.success) {
13934
- const mode = useAiAssist() ? 'AI task' : 'remote task';
13935
- setTaskFeedback(`Queued ${result.queued || targetIds.length} visible ${mode}(s).`, 'success');
13936
- } else {
13937
- setTaskFeedback(result?.error || 'Task dispatch failed.', 'error');
13938
- }
13977
+ const mode = useAiAssist() ? 'visible AI task' : 'visible remote task';
13978
+ const feedback = formatRemoteFleetDispatchFeedback(result, targetIds.length, mode);
13979
+ setTaskFeedback(feedback.text, feedback.tone);
13939
13980
  } finally {
13940
13981
  applyRemoteFleetFilters();
13941
13982
  }
@@ -13956,12 +13997,9 @@
13956
13997
  try {
13957
13998
  const result = await invokeDotNetAsync('DispatchRemoteFleetTaskFromJs', nodeId, '', instruction, useAiAssist());
13958
13999
  await syncRemoteFleetNodeStateFromResult(result);
13959
- if (result?.success) {
13960
- const mode = useAiAssist() ? 'AI task' : 'remote task';
13961
- setTaskFeedback(`Queued ${result.queued || result.total || 1} ${mode}(s).`, 'success');
13962
- } else {
13963
- setTaskFeedback(result?.error || 'Task dispatch failed.', 'error');
13964
- }
14000
+ const mode = useAiAssist() ? 'AI task' : 'remote task';
14001
+ const feedback = formatRemoteFleetDispatchFeedback(result, result?.total || 0, mode);
14002
+ setTaskFeedback(feedback.text, feedback.tone);
13965
14003
  } finally {
13966
14004
  applyRemoteFleetFilters();
13967
14005
  }
@@ -18091,6 +18129,7 @@
18091
18129
  syncVideoNodeVisibilityPlayback: syncVideoNodeVisibilityPlayback,
18092
18130
  syncEditingOverlay: syncEditingOverlay,
18093
18131
  syncTextOverlays: syncTextOverlays,
18132
+ setModuleForTest: setModuleForTest,
18094
18133
  renderRemoteFleetMonitorForTest: renderRemoteFleetMonitor,
18095
18134
  renderRemoteFleetDeviceForTest: renderRemoteFleetDevice,
18096
18135
  renderBusinessAutomationEdges: renderBusinessAutomationEdges,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-GlTNEJ0ZfPP6DnkregRY8SmRVXZU12TGhQpfIuIwr0I=",
4
+ "hash": "sha256-EOXWPNQzw1iOoooim3bWEvapz9AQ4G5jOFZbf4RgdGg=",
5
5
  "fingerprinting": {
6
6
  "Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
7
7
  "Markdig.d1j7v41cl1.dll": "Markdig.dll",
@@ -123,16 +123,16 @@
123
123
  "System.m05i39uvk9.dll": "System.dll",
124
124
  "netstandard.0xet7jg7ky.dll": "netstandard.dll",
125
125
  "System.Private.CoreLib.rkafq04oma.dll": "System.Private.CoreLib.dll",
126
- "MindExecution.Core.q469sducjw.dll": "MindExecution.Core.dll",
127
- "MindExecution.Kernel.lqglgq2jmo.dll": "MindExecution.Kernel.dll",
128
- "MindExecution.Plugins.Admin.2jqptn6ylw.dll": "MindExecution.Plugins.Admin.dll",
129
- "MindExecution.Plugins.Business.8hzwity3fl.dll": "MindExecution.Plugins.Business.dll",
130
- "MindExecution.Plugins.Concept.wqdtoeuumu.dll": "MindExecution.Plugins.Concept.dll",
131
- "MindExecution.Plugins.Directory.hr2p0grenr.dll": "MindExecution.Plugins.Directory.dll",
132
- "MindExecution.Plugins.PlanMaster.fpq5ydr4s0.dll": "MindExecution.Plugins.PlanMaster.dll",
133
- "MindExecution.Plugins.YouTube.43m89bf01o.dll": "MindExecution.Plugins.YouTube.dll",
134
- "MindExecution.Shared.y1hdnsq8zq.dll": "MindExecution.Shared.dll",
135
- "MindExecution.Web.ox678bzo06.dll": "MindExecution.Web.dll",
126
+ "MindExecution.Core.gvaopvh5wg.dll": "MindExecution.Core.dll",
127
+ "MindExecution.Kernel.hpf3wr4dv3.dll": "MindExecution.Kernel.dll",
128
+ "MindExecution.Plugins.Admin.y0euqt1gu1.dll": "MindExecution.Plugins.Admin.dll",
129
+ "MindExecution.Plugins.Business.9zzc31j54n.dll": "MindExecution.Plugins.Business.dll",
130
+ "MindExecution.Plugins.Concept.sihn2obrrn.dll": "MindExecution.Plugins.Concept.dll",
131
+ "MindExecution.Plugins.Directory.zwbshhfvpb.dll": "MindExecution.Plugins.Directory.dll",
132
+ "MindExecution.Plugins.PlanMaster.47f3afsrf8.dll": "MindExecution.Plugins.PlanMaster.dll",
133
+ "MindExecution.Plugins.YouTube.rpolgpee6c.dll": "MindExecution.Plugins.YouTube.dll",
134
+ "MindExecution.Shared.zm6btxgvlg.dll": "MindExecution.Shared.dll",
135
+ "MindExecution.Web.7b082n0t0o.dll": "MindExecution.Web.dll",
136
136
  "dotnet.js": "dotnet.js",
137
137
  "dotnet.native.xsn1d6x2kd.js": "dotnet.native.js",
138
138
  "dotnet.native.vz0adxojrz.wasm": "dotnet.native.wasm",
@@ -278,18 +278,18 @@
278
278
  "System.Xml.XDocument.c539ki6cuq.dll": "sha256-MPTRJkptrL9nGa2tl4kF46+wErNUYRPCGblX3ANoKoY=",
279
279
  "System.m05i39uvk9.dll": "sha256-5jDfIdbYAigw7/Q/lMzt5W/+cayGbW9ko9FvuaN1GsQ=",
280
280
  "netstandard.0xet7jg7ky.dll": "sha256-xENDv620uJ8fHwLJ2bdhrTHz4QPjvqXOztnk2a4wr0c=",
281
- "MindExecution.Core.q469sducjw.dll": "sha256-tqgvXepOZPfdkyxH1BK3KlhL8azJjoV+iV/BismieCc=",
282
- "MindExecution.Kernel.lqglgq2jmo.dll": "sha256-AyAPsZpAwsH/HeC6cWVzRH50hBjGAoamzGup6clWJTg=",
283
- "MindExecution.Plugins.Concept.wqdtoeuumu.dll": "sha256-2FoeeE612eZV39wbEI+Soq3dmXibSZjHP/IKnbzok70=",
284
- "MindExecution.Plugins.PlanMaster.fpq5ydr4s0.dll": "sha256-c3xRnRTCa3xuB2/xJyrLoyuZ1owk3BM6ft/SmnV4u7U=",
285
- "MindExecution.Shared.y1hdnsq8zq.dll": "sha256-lp71ky431zxHwFMHmb2TKH+NQk4e+rs9Od4a0gOoBgU=",
286
- "MindExecution.Web.ox678bzo06.dll": "sha256-JJ17QCB0KRHtXGQKuM7WQHQLtCfLg+s+CW7j3X/TE+I="
281
+ "MindExecution.Core.gvaopvh5wg.dll": "sha256-DDLjV7zNlhxfza3LBE+Fbjpke9h8yItLJ3qWzf4Yp74=",
282
+ "MindExecution.Kernel.hpf3wr4dv3.dll": "sha256-RV4W3gMSRwcM23iZfhh4tKgdBlTU0Tfuqld6kSEfxhw=",
283
+ "MindExecution.Plugins.Concept.sihn2obrrn.dll": "sha256-dMpQJwHdXztt3B/lhRoqgpaiEoO7HbZie+ST5g1BAIk=",
284
+ "MindExecution.Plugins.PlanMaster.47f3afsrf8.dll": "sha256-sNW33IqNle0U1uYsT1h4saM9T4UN2hDK13YYWPzOIq4=",
285
+ "MindExecution.Shared.zm6btxgvlg.dll": "sha256-WxdOHLbsCW4r0JnzhaIHm4+nSVJW7WbVpjnf1Uwkk2c=",
286
+ "MindExecution.Web.7b082n0t0o.dll": "sha256-k/Gq3iOPlGciTli3OjQY+EZ/29D4FlrU0tzk6Vu2b4E="
287
287
  },
288
288
  "lazyAssembly": {
289
- "MindExecution.Plugins.Admin.2jqptn6ylw.dll": "sha256-ctRNgHtUfPq0pCvAeG0DCzZPBRTgLtQC2c4pjGFM+Go=",
290
- "MindExecution.Plugins.Business.8hzwity3fl.dll": "sha256-ZL74Jut/WgaNdDBWZzhC6ECsjBdm+0cB1P5fx4ACugM=",
291
- "MindExecution.Plugins.Directory.hr2p0grenr.dll": "sha256-PUrdpFqGptJ5kxYaarWNwB2NmZ1TphHnFHlnyQL48DI=",
292
- "MindExecution.Plugins.YouTube.43m89bf01o.dll": "sha256-0MYkUpuqSUwsFGO/XYud82hbXMIngLds7GHRN4yyXjQ="
289
+ "MindExecution.Plugins.Admin.y0euqt1gu1.dll": "sha256-gqBZLGmu1nofX9cZu7NzT0StnMnDwaG/eLtHfk95Dg8=",
290
+ "MindExecution.Plugins.Business.9zzc31j54n.dll": "sha256-JTgGpzDyzUUKzJelltauxS03YhSFpdAt31BBx1Ng4pg=",
291
+ "MindExecution.Plugins.Directory.zwbshhfvpb.dll": "sha256-rY/yH9+h3canhVFmdWrcynxahBsnQ4PT5wtCx5OQGFg=",
292
+ "MindExecution.Plugins.YouTube.rpolgpee6c.dll": "sha256-X+TTYJklqCsulTbHi89CZkB4lDXfkTLEj6FH5dJZoDs="
293
293
  }
294
294
  },
295
295
  "cacheBootResources": true,
@@ -558,7 +558,7 @@
558
558
  }
559
559
 
560
560
  const base = '_content/MindExecution.Shared/js/';
561
- const scriptVersion = '20260612-remote-thumbnail-scheduler-v473';
561
+ const scriptVersion = '20260612-remote-task-timeout-v475';
562
562
  const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
563
563
  console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
564
564
  const criticalScripts = [
@@ -1,5 +1,5 @@
1
1
  self.assetsManifest = {
2
- "version": "QLa+sMNb",
2
+ "version": "HOeNy0HC",
3
3
  "assets": [
4
4
  {
5
5
  "hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
@@ -86,7 +86,7 @@
86
86
  "url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
87
87
  },
88
88
  {
89
- "hash": "sha256-TRPlxHfTd5M/XAy1bnnYRmTFqExG/OMAo4jSJSVcZjk=",
89
+ "hash": "sha256-e/aMJjefLL0i7yi8x7tn/EY9wrD0pIxU42m2QKgIYz4=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -410,44 +410,44 @@
410
410
  "url": "_framework/MimeMapping.og9ys58ylm.dll"
411
411
  },
412
412
  {
413
- "hash": "sha256-tqgvXepOZPfdkyxH1BK3KlhL8azJjoV+iV/BismieCc=",
414
- "url": "_framework/MindExecution.Core.q469sducjw.dll"
413
+ "hash": "sha256-DDLjV7zNlhxfza3LBE+Fbjpke9h8yItLJ3qWzf4Yp74=",
414
+ "url": "_framework/MindExecution.Core.gvaopvh5wg.dll"
415
415
  },
416
416
  {
417
- "hash": "sha256-AyAPsZpAwsH/HeC6cWVzRH50hBjGAoamzGup6clWJTg=",
418
- "url": "_framework/MindExecution.Kernel.lqglgq2jmo.dll"
417
+ "hash": "sha256-RV4W3gMSRwcM23iZfhh4tKgdBlTU0Tfuqld6kSEfxhw=",
418
+ "url": "_framework/MindExecution.Kernel.hpf3wr4dv3.dll"
419
419
  },
420
420
  {
421
- "hash": "sha256-ctRNgHtUfPq0pCvAeG0DCzZPBRTgLtQC2c4pjGFM+Go=",
422
- "url": "_framework/MindExecution.Plugins.Admin.2jqptn6ylw.dll"
421
+ "hash": "sha256-gqBZLGmu1nofX9cZu7NzT0StnMnDwaG/eLtHfk95Dg8=",
422
+ "url": "_framework/MindExecution.Plugins.Admin.y0euqt1gu1.dll"
423
423
  },
424
424
  {
425
- "hash": "sha256-ZL74Jut/WgaNdDBWZzhC6ECsjBdm+0cB1P5fx4ACugM=",
426
- "url": "_framework/MindExecution.Plugins.Business.8hzwity3fl.dll"
425
+ "hash": "sha256-JTgGpzDyzUUKzJelltauxS03YhSFpdAt31BBx1Ng4pg=",
426
+ "url": "_framework/MindExecution.Plugins.Business.9zzc31j54n.dll"
427
427
  },
428
428
  {
429
- "hash": "sha256-2FoeeE612eZV39wbEI+Soq3dmXibSZjHP/IKnbzok70=",
430
- "url": "_framework/MindExecution.Plugins.Concept.wqdtoeuumu.dll"
429
+ "hash": "sha256-dMpQJwHdXztt3B/lhRoqgpaiEoO7HbZie+ST5g1BAIk=",
430
+ "url": "_framework/MindExecution.Plugins.Concept.sihn2obrrn.dll"
431
431
  },
432
432
  {
433
- "hash": "sha256-PUrdpFqGptJ5kxYaarWNwB2NmZ1TphHnFHlnyQL48DI=",
434
- "url": "_framework/MindExecution.Plugins.Directory.hr2p0grenr.dll"
433
+ "hash": "sha256-rY/yH9+h3canhVFmdWrcynxahBsnQ4PT5wtCx5OQGFg=",
434
+ "url": "_framework/MindExecution.Plugins.Directory.zwbshhfvpb.dll"
435
435
  },
436
436
  {
437
- "hash": "sha256-c3xRnRTCa3xuB2/xJyrLoyuZ1owk3BM6ft/SmnV4u7U=",
438
- "url": "_framework/MindExecution.Plugins.PlanMaster.fpq5ydr4s0.dll"
437
+ "hash": "sha256-sNW33IqNle0U1uYsT1h4saM9T4UN2hDK13YYWPzOIq4=",
438
+ "url": "_framework/MindExecution.Plugins.PlanMaster.47f3afsrf8.dll"
439
439
  },
440
440
  {
441
- "hash": "sha256-0MYkUpuqSUwsFGO/XYud82hbXMIngLds7GHRN4yyXjQ=",
442
- "url": "_framework/MindExecution.Plugins.YouTube.43m89bf01o.dll"
441
+ "hash": "sha256-X+TTYJklqCsulTbHi89CZkB4lDXfkTLEj6FH5dJZoDs=",
442
+ "url": "_framework/MindExecution.Plugins.YouTube.rpolgpee6c.dll"
443
443
  },
444
444
  {
445
- "hash": "sha256-lp71ky431zxHwFMHmb2TKH+NQk4e+rs9Od4a0gOoBgU=",
446
- "url": "_framework/MindExecution.Shared.y1hdnsq8zq.dll"
445
+ "hash": "sha256-WxdOHLbsCW4r0JnzhaIHm4+nSVJW7WbVpjnf1Uwkk2c=",
446
+ "url": "_framework/MindExecution.Shared.zm6btxgvlg.dll"
447
447
  },
448
448
  {
449
- "hash": "sha256-JJ17QCB0KRHtXGQKuM7WQHQLtCfLg+s+CW7j3X/TE+I=",
450
- "url": "_framework/MindExecution.Web.ox678bzo06.dll"
449
+ "hash": "sha256-k/Gq3iOPlGciTli3OjQY+EZ/29D4FlrU0tzk6Vu2b4E=",
450
+ "url": "_framework/MindExecution.Web.7b082n0t0o.dll"
451
451
  },
452
452
  {
453
453
  "hash": "sha256-IsZJ91/OW+fHzNqIgEc7Y072ns8z9dGritiSyvR9Wgc=",
@@ -770,7 +770,7 @@
770
770
  "url": "_framework/Websocket.Client.vapounvmnl.dll"
771
771
  },
772
772
  {
773
- "hash": "sha256-LPeEgdSHt/c+wCTt4C1Z8+0CvQSzl5rxeojM+0OQDcU=",
773
+ "hash": "sha256-sP1JkizV14ASUo9oVPOopzpprSlzkoXZqpimYvOYBIw=",
774
774
  "url": "_framework/blazor.boot.json"
775
775
  },
776
776
  {
@@ -834,7 +834,7 @@
834
834
  "url": "image-manifest.json"
835
835
  },
836
836
  {
837
- "hash": "sha256-YtEozKS1KbDYp9w3j2ecyHIFvGm3zqvygqym/zN71vk=",
837
+ "hash": "sha256-ZWKMY1yo6lJvnKON0C9AmGH2YMwnIs07SFC4mTUDTsQ=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: QLa+sMNb */
1
+ /* Manifest version: HOeNy0HC */
2
2
  // Hosted deployments should prefer the network over stale offline caches.
3
3
  // This service worker immediately clears old Blazor offline caches and unregisters itself.
4
4