@synergenius/flow-weaver-pack-weaver 0.9.159 → 0.9.164

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 (102) hide show
  1. package/dist/ai-chat-provider.d.ts.map +1 -1
  2. package/dist/ai-chat-provider.js +17 -11
  3. package/dist/ai-chat-provider.js.map +1 -1
  4. package/dist/bot/ai-router.js +5 -5
  5. package/dist/bot/ai-router.js.map +1 -1
  6. package/dist/bot/assistant-tools.d.ts.map +1 -1
  7. package/dist/bot/assistant-tools.js +6 -7
  8. package/dist/bot/assistant-tools.js.map +1 -1
  9. package/dist/bot/capability-registry.d.ts.map +1 -1
  10. package/dist/bot/capability-registry.js +37 -14
  11. package/dist/bot/capability-registry.js.map +1 -1
  12. package/dist/bot/dashboard.js +1 -1
  13. package/dist/bot/dashboard.js.map +1 -1
  14. package/dist/bot/index.d.ts +1 -1
  15. package/dist/bot/index.d.ts.map +1 -1
  16. package/dist/bot/index.js.map +1 -1
  17. package/dist/bot/instance-manager.js +3 -3
  18. package/dist/bot/instance-manager.js.map +1 -1
  19. package/dist/bot/profile-store.d.ts.map +1 -1
  20. package/dist/bot/profile-store.js +11 -9
  21. package/dist/bot/profile-store.js.map +1 -1
  22. package/dist/bot/profile-types.d.ts +2 -2
  23. package/dist/bot/profile-types.d.ts.map +1 -1
  24. package/dist/bot/runner.d.ts +1 -0
  25. package/dist/bot/runner.d.ts.map +1 -1
  26. package/dist/bot/runner.js +6 -2
  27. package/dist/bot/runner.js.map +1 -1
  28. package/dist/bot/step-executor.d.ts.map +1 -1
  29. package/dist/bot/step-executor.js +10 -0
  30. package/dist/bot/step-executor.js.map +1 -1
  31. package/dist/bot/swarm-controller.d.ts +3 -5
  32. package/dist/bot/swarm-controller.d.ts.map +1 -1
  33. package/dist/bot/swarm-controller.js +157 -74
  34. package/dist/bot/swarm-controller.js.map +1 -1
  35. package/dist/bot/task-prompt-builder.d.ts +2 -3
  36. package/dist/bot/task-prompt-builder.d.ts.map +1 -1
  37. package/dist/bot/task-prompt-builder.js +81 -67
  38. package/dist/bot/task-prompt-builder.js.map +1 -1
  39. package/dist/bot/task-store.d.ts +3 -3
  40. package/dist/bot/task-store.d.ts.map +1 -1
  41. package/dist/bot/task-store.js +89 -75
  42. package/dist/bot/task-store.js.map +1 -1
  43. package/dist/bot/task-types.d.ts +54 -26
  44. package/dist/bot/task-types.d.ts.map +1 -1
  45. package/dist/bot/task-types.js +6 -2
  46. package/dist/bot/task-types.js.map +1 -1
  47. package/dist/bot/types.d.ts +2 -0
  48. package/dist/bot/types.d.ts.map +1 -1
  49. package/dist/bot/weaver-tools.d.ts.map +1 -1
  50. package/dist/bot/weaver-tools.js +10 -0
  51. package/dist/bot/weaver-tools.js.map +1 -1
  52. package/dist/cli-handlers.d.ts +0 -1
  53. package/dist/cli-handlers.d.ts.map +1 -1
  54. package/dist/cli-handlers.js +5 -9
  55. package/dist/cli-handlers.js.map +1 -1
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/node-types/agent-execute.d.ts.map +1 -1
  60. package/dist/node-types/agent-execute.js +95 -63
  61. package/dist/node-types/agent-execute.js.map +1 -1
  62. package/dist/node-types/plan-task.js +8 -8
  63. package/dist/node-types/plan-task.js.map +1 -1
  64. package/dist/ui/bot-panel.js +1 -1
  65. package/dist/ui/capability-editor.js +37 -14
  66. package/dist/ui/chat-task-result.js +1 -7
  67. package/dist/ui/profile-editor.js +37 -14
  68. package/dist/ui/swarm-controls.js +2 -2
  69. package/dist/ui/swarm-dashboard.js +72 -109
  70. package/dist/ui/task-detail-view.js +21 -42
  71. package/dist/ui/task-editor.js +13 -50
  72. package/dist/ui/task-pool-list.js +0 -2
  73. package/flowweaver.manifest.json +1 -1
  74. package/package.json +1 -1
  75. package/src/ai-chat-provider.ts +15 -11
  76. package/src/bot/ai-router.ts +5 -5
  77. package/src/bot/assistant-tools.ts +6 -7
  78. package/src/bot/capability-registry.ts +37 -14
  79. package/src/bot/dashboard.ts +1 -1
  80. package/src/bot/index.ts +5 -1
  81. package/src/bot/instance-manager.ts +3 -3
  82. package/src/bot/profile-store.ts +12 -10
  83. package/src/bot/profile-types.ts +2 -2
  84. package/src/bot/runner.ts +6 -2
  85. package/src/bot/step-executor.ts +11 -0
  86. package/src/bot/swarm-controller.ts +164 -78
  87. package/src/bot/task-prompt-builder.ts +86 -71
  88. package/src/bot/task-store.ts +101 -78
  89. package/src/bot/task-types.ts +81 -36
  90. package/src/bot/types.ts +2 -0
  91. package/src/bot/weaver-tools.ts +11 -0
  92. package/src/cli-handlers.ts +5 -9
  93. package/src/index.ts +6 -0
  94. package/src/node-types/agent-execute.ts +99 -62
  95. package/src/node-types/plan-task.ts +8 -8
  96. package/src/ui/bot-panel.tsx +3 -3
  97. package/src/ui/chat-task-result.tsx +5 -14
  98. package/src/ui/swarm-controls.tsx +3 -3
  99. package/src/ui/swarm-dashboard.tsx +3 -3
  100. package/src/ui/task-detail-view.tsx +29 -52
  101. package/src/ui/task-editor.tsx +14 -51
  102. package/src/ui/task-pool-list.tsx +1 -3
@@ -206,10 +206,10 @@ function SwarmControls({ swarmStatus, onRefresh }) {
206
206
  variant: "caption-regular",
207
207
  color: "color-text-subtle"
208
208
  }, `${swarmStatus.tasksCompleted} done`),
209
- swarmStatus && swarmStatus.tasksFailed > 0 && React.createElement(Typography, {
209
+ swarmStatus && swarmStatus.runsIncomplete > 0 && React.createElement(Typography, {
210
210
  variant: "caption-regular",
211
211
  color: "color-status-negative"
212
- }, `${swarmStatus.tasksFailed} failed`)
212
+ }, `${swarmStatus.runsIncomplete} incomplete`)
213
213
  ),
214
214
  // Right: action buttons
215
215
  React.createElement(
@@ -404,10 +404,8 @@ var { useState: useState2 } = React4;
404
404
  var { Flex: Flex4, Typography: Typography4, Icon: Icon2, StatusIcon: StatusIcon2, Chip, ScrollArea, Badge, EmptyState } = require("@fw/plugin-ui-kit");
405
405
  var statusToIcon = {
406
406
  "open": "pending",
407
- "pending": "pending",
408
407
  "in-progress": "running",
409
408
  "done": "completed",
410
- "blocked": "pending",
411
409
  "cancelled": "failed"
412
410
  };
413
411
  var rowBaseStyle = {
@@ -855,18 +853,14 @@ var {
855
853
  } = require("@fw/plugin-ui-kit");
856
854
  var statusToIcon2 = {
857
855
  "open": "pending",
858
- "pending": "pending",
859
856
  "in-progress": "running",
860
857
  "done": "completed",
861
- "blocked": "pending",
862
858
  "cancelled": "failed"
863
859
  };
864
860
  var statusToLabel2 = {
865
861
  "open": "Open",
866
- "pending": "Queued",
867
862
  "in-progress": "Running",
868
863
  "done": "Done",
869
- "blocked": "Blocked",
870
864
  "cancelled": "Cancelled"
871
865
  };
872
866
  var headerStyle = {
@@ -921,11 +915,7 @@ function SubtaskRowItem({ sub, onBack }) {
921
915
  label: sub.assignedProfile,
922
916
  size: "small",
923
917
  color: "color-status-info"
924
- }),
925
- React6.createElement(Typography5, {
926
- variant: "caption-regular",
927
- color: "color-text-subtle"
928
- }, `#${sub.attempt}`)
918
+ })
929
919
  );
930
920
  }
931
921
  function TaskDetailView({ taskId, onBack, onEdit }) {
@@ -944,7 +934,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
944
934
  const t = data.task ?? data;
945
935
  setTask(t);
946
936
  const rs = t.context;
947
- const summaries = rs?.runSummaries;
937
+ const summaries = rs?.runHistory;
948
938
  if (summaries?.length) {
949
939
  setHistory((prev) => {
950
940
  if (prev.length > 0) return prev;
@@ -1013,13 +1003,13 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1013
1003
  awaitingApproval,
1014
1004
  report: liveReport
1015
1005
  } = useStreamTimeline(stream.events, stream.isDone);
1016
- const currentRunId = task?.currentRunId;
1017
- const isLive = task?.status === "in-progress" && !!currentRunId;
1006
+ const activeRunId = task?.activeRunId;
1007
+ const isLive = task?.status === "in-progress" && !!activeRunId;
1018
1008
  useEffect2(() => {
1019
- if (!isLive || !currentRunId) return;
1020
- stream.start(packId, "fw_weaver_events", currentRunId);
1009
+ if (!isLive || !activeRunId) return;
1010
+ stream.start(packId, "fw_weaver_events", activeRunId);
1021
1011
  return () => stream.stop();
1022
- }, [isLive, currentRunId, packId]);
1012
+ }, [isLive, activeRunId, packId]);
1023
1013
  const [detailTab, setDetailTab] = useState4("runs");
1024
1014
  const [actionLoading, setActionLoading] = useState4(null);
1025
1015
  const [availableProfiles, setAvailableProfiles] = useState4([]);
@@ -1274,7 +1264,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1274
1264
  React6.createElement(Chip2, {
1275
1265
  label: statusToLabel2[task.status] || task.status || "open",
1276
1266
  size: "small",
1277
- color: task.status === "done" ? "color-status-positive" : task.status === "cancelled" ? "color-status-negative" : task.status === "in-progress" ? "color-status-info" : task.status === "blocked" ? "color-status-caution" : "color-brand-alt"
1267
+ color: task.status === "done" ? "color-status-positive" : task.status === "cancelled" ? "color-status-negative" : task.status === "in-progress" ? "color-status-info" : "color-brand-alt"
1278
1268
  }),
1279
1269
  task.assignedProfile && React6.createElement(Chip2, { key: `profile-${task.assignedProfile}`, label: task.assignedProfile, size: "small", color: "color-status-info" }),
1280
1270
  task.priority > 0 && React6.createElement(Chip2, {
@@ -1282,10 +1272,10 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1282
1272
  size: "small",
1283
1273
  color: task.priority >= 3 ? "color-status-caution" : "color-status-info"
1284
1274
  }),
1285
- task.maxAttempts != null && task.maxAttempts > 1 && React6.createElement(Typography5, {
1275
+ (task.context?.runHistory?.length ?? 0) > 0 && React6.createElement(Typography5, {
1286
1276
  variant: "smallCaption-regular",
1287
1277
  color: "color-text-subtle"
1288
- }, `${task.runs?.length ?? 0}/${task.maxAttempts} runs`)
1278
+ }, `${task.context.runHistory.length} run${task.context.runHistory.length !== 1 ? "s" : ""}`)
1289
1279
  ),
1290
1280
  // Description
1291
1281
  task.description && React6.createElement(Typography5, {
@@ -1327,7 +1317,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1327
1317
  Flex5,
1328
1318
  { variant: "column-stretch-start-nowrap-8", style: { padding: "12px 16px" } },
1329
1319
  // ── Runs tab ──
1330
- detailTab === "runs" && (hasRuns || (task.runs?.length ?? 0) > 0) ? React6.createElement(
1320
+ detailTab === "runs" && (hasRuns || (task.context?.runHistory?.length ?? 0) > 0) ? React6.createElement(
1331
1321
  Flex5,
1332
1322
  { variant: "column-stretch-start-nowrap-8" },
1333
1323
  ...runItems.map(({ run, runTimeline }) => {
@@ -1400,7 +1390,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1400
1390
  React6.createElement(
1401
1391
  Flex5,
1402
1392
  { variant: "row-center-start-nowrap-6" },
1403
- task.status === "open" && (task.attempt ?? 0) > 0 && React6.createElement(Button2, {
1393
+ task.status === "open" && (task.context?.runHistory?.length ?? 0) > 0 && React6.createElement(Button2, {
1404
1394
  size: "xs",
1405
1395
  variant: "fill",
1406
1396
  color: "primary",
@@ -1408,7 +1398,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1408
1398
  loading: actionLoading === "retry",
1409
1399
  disabled: !!actionLoading
1410
1400
  }, "Retry Task"),
1411
- (task.status === "open" || task.status === "pending" || task.status === "in-progress" || task.status === "blocked") && React6.createElement(Button2, {
1401
+ (task.status === "open" || task.status === "in-progress") && React6.createElement(Button2, {
1412
1402
  size: "xs",
1413
1403
  variant: "outlined",
1414
1404
  color: "danger",
@@ -1547,27 +1537,14 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1547
1537
  style: { whiteSpace: "pre-wrap" }
1548
1538
  }, task.context.notes)
1549
1539
  ),
1550
- // Last error
1551
- task.context?.lastError && React6.createElement(
1552
- Flex5,
1553
- {
1554
- variant: "column-stretch-start-nowrap-4"
1555
- },
1556
- React6.createElement(Typography5, { variant: "caption-thick", color: "color-status-negative" }, "Last Error"),
1557
- React6.createElement(Typography5, {
1558
- variant: "smallCaption-regular",
1559
- color: "color-text-high",
1560
- style: { fontFamily: "var(--font-mono, monospace)", whiteSpace: "pre-wrap" }
1561
- }, task.context.lastError)
1562
- ),
1563
- // Run summaries (accumulated context)
1564
- (task.context?.runSummaries?.length ?? 0) > 0 && React6.createElement(
1540
+ // Run history (accumulated context)
1541
+ (task.context?.runHistory?.length ?? 0) > 0 && React6.createElement(
1565
1542
  Flex5,
1566
1543
  {
1567
1544
  variant: "column-stretch-start-nowrap-6"
1568
1545
  },
1569
- React6.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Run Summaries"),
1570
- ...(task.context?.runSummaries ?? []).map(
1546
+ React6.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Run History"),
1547
+ ...(task.context?.runHistory ?? []).map(
1571
1548
  (rs, i) => React6.createElement(
1572
1549
  Card,
1573
1550
  {
@@ -1597,10 +1574,10 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1597
1574
  color: "color-text-subtle",
1598
1575
  style: { fontFamily: "var(--font-mono, monospace)" }
1599
1576
  }, `Files: ${rs.filesModified.join(", ")}`),
1600
- rs.error && React6.createElement(Typography5, {
1577
+ rs.remainingWork && React6.createElement(Typography5, {
1601
1578
  variant: "smallCaption-regular",
1602
- color: "color-status-negative"
1603
- }, `Error: ${rs.error}`)
1579
+ color: "color-status-caution"
1580
+ }, `Remaining: ${rs.remainingWork}`)
1604
1581
  )
1605
1582
  )
1606
1583
  ),
@@ -1617,7 +1594,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1617
1594
  React6.createElement(
1618
1595
  Typography5,
1619
1596
  { variant: "smallCaption-regular", color: "color-text-high" },
1620
- `Tokens: ${task.tokensUsed?.toLocaleString() ?? 0}${task.budgetTokens ? ` / ${task.budgetTokens.toLocaleString()}` : ""}`
1597
+ `Tokens: ${task.tokensUsed?.toLocaleString() ?? 0}`
1621
1598
  ),
1622
1599
  React6.createElement(
1623
1600
  Typography5,
@@ -1664,9 +1641,7 @@ var COMPLEXITY_OPTIONS = [
1664
1641
  ];
1665
1642
  var STATUS_COLOR = {
1666
1643
  "open": "color-brand-alt",
1667
- "pending": "color-brand-alt",
1668
1644
  "in-progress": "color-status-info",
1669
- "blocked": "color-status-caution",
1670
1645
  "done": "color-status-positive",
1671
1646
  "cancelled": "color-status-negative"
1672
1647
  };
@@ -1678,8 +1653,6 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
1678
1653
  const [priority, setPriority] = useState5("0");
1679
1654
  const [complexity, setComplexity] = useState5("");
1680
1655
  const [assignedProfile, setAssignedProfile] = useState5("");
1681
- const [maxAttempts, setMaxAttempts] = useState5("3");
1682
- const [budgetTokens, setBudgetTokens] = useState5("");
1683
1656
  const [budgetCost, setBudgetCost] = useState5("");
1684
1657
  const [notes, setNotes] = useState5("");
1685
1658
  const [files, setFiles] = useState5([]);
@@ -1736,8 +1709,6 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
1736
1709
  setPriority(String(t.priority ?? 0));
1737
1710
  setComplexity(t.complexity || "");
1738
1711
  setAssignedProfile(t.assignedProfile || "");
1739
- setMaxAttempts(String(t.maxAttempts ?? 3));
1740
- setBudgetTokens(t.budgetTokens != null ? String(t.budgetTokens) : "");
1741
1712
  setBudgetCost(t.budgetCost != null ? String(t.budgetCost) : "");
1742
1713
  setNotes(t.context?.notes || "");
1743
1714
  setFiles(t.context?.files || []);
@@ -1755,11 +1726,11 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
1755
1726
  }, [mode, taskId, callTool]);
1756
1727
  const isDirty = useCallback3(() => {
1757
1728
  if (mode === "create") {
1758
- return !!(title.trim() || description.trim() || notes.trim() || files.length > 0 || dependsOn.length > 0 || priority !== "0" || complexity || assignedProfile || maxAttempts !== "3" || budgetTokens || budgetCost);
1729
+ return !!(title.trim() || description.trim() || notes.trim() || files.length > 0 || dependsOn.length > 0 || priority !== "0" || complexity || assignedProfile || budgetCost);
1759
1730
  }
1760
1731
  if (!taskData) return false;
1761
- return title.trim() !== (taskData.title || "") || description.trim() !== (taskData.description || "") || notes.trim() !== (taskData.context?.notes || "") || JSON.stringify(files) !== JSON.stringify(taskData.context?.files || []) || priority !== String(taskData.priority ?? 0) || complexity !== (taskData.complexity || "") || assignedProfile !== (taskData.assignedProfile || "") || maxAttempts !== String(taskData.maxAttempts ?? 3) || budgetTokens !== (taskData.budgetTokens != null ? String(taskData.budgetTokens) : "") || budgetCost !== (taskData.budgetCost != null ? String(taskData.budgetCost) : "");
1762
- }, [mode, title, description, notes, files, dependsOn, priority, complexity, assignedProfile, maxAttempts, budgetTokens, budgetCost, taskData]);
1732
+ return title.trim() !== (taskData.title || "") || description.trim() !== (taskData.description || "") || notes.trim() !== (taskData.context?.notes || "") || JSON.stringify(files) !== JSON.stringify(taskData.context?.files || []) || priority !== String(taskData.priority ?? 0) || complexity !== (taskData.complexity || "") || assignedProfile !== (taskData.assignedProfile || "") || budgetCost !== (taskData.budgetCost != null ? String(taskData.budgetCost) : "");
1733
+ }, [mode, title, description, notes, files, dependsOn, priority, complexity, assignedProfile, budgetCost, taskData]);
1763
1734
  const handleBack = useCallback3(async () => {
1764
1735
  if (isDirty()) {
1765
1736
  const ok = await ctx.confirm("Discard unsaved changes?", {
@@ -1798,12 +1769,10 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
1798
1769
  const args = {
1799
1770
  title: title.trim(),
1800
1771
  description: description.trim(),
1801
- priority: parseInt(priority, 10) || 0,
1802
- maxAttempts: parseInt(maxAttempts, 10) || 3
1772
+ priority: parseInt(priority, 10) || 0
1803
1773
  };
1804
1774
  if (complexity) args.complexity = complexity;
1805
1775
  if (assignedProfile) args.assignedProfile = assignedProfile;
1806
- if (budgetTokens) args.budgetTokens = parseInt(budgetTokens, 10);
1807
1776
  if (budgetCost) args.budgetCost = parseFloat(budgetCost);
1808
1777
  if (dependsOn.length > 0) args.dependsOn = dependsOn;
1809
1778
  const result = await callTool("fw_weaver_task_create", args);
@@ -1815,7 +1784,8 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
1815
1784
  context: {
1816
1785
  notes: notes.trim(),
1817
1786
  files,
1818
- runSummaries: []
1787
+ runHistory: [],
1788
+ stagnationCount: 0
1819
1789
  }
1820
1790
  });
1821
1791
  }
@@ -1826,15 +1796,9 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
1826
1796
  title: title.trim(),
1827
1797
  description: description.trim(),
1828
1798
  priority: parseInt(priority, 10) || 0,
1829
- maxAttempts: parseInt(maxAttempts, 10) || 3,
1830
1799
  complexity: complexity || void 0,
1831
1800
  assignedProfile: assignedProfile || null
1832
1801
  };
1833
- if (budgetTokens) {
1834
- patch.budgetTokens = parseInt(budgetTokens, 10);
1835
- } else {
1836
- patch.budgetTokens = void 0;
1837
- }
1838
1802
  if (budgetCost) {
1839
1803
  patch.budgetCost = parseFloat(budgetCost);
1840
1804
  } else {
@@ -1843,8 +1807,8 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
1843
1807
  patch.context = {
1844
1808
  files,
1845
1809
  notes: notes.trim(),
1846
- runSummaries: taskData?.context?.runSummaries || [],
1847
- lastError: taskData?.context?.lastError
1810
+ runHistory: taskData?.context?.runHistory || [],
1811
+ stagnationCount: taskData?.context?.stagnationCount ?? 0
1848
1812
  };
1849
1813
  await callTool("fw_weaver_task_update", patch);
1850
1814
  toast3("Task updated", { type: "success" });
@@ -1853,7 +1817,7 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
1853
1817
  } catch (err) {
1854
1818
  toast3(err instanceof Error ? err.message : `Failed to ${mode} task`, { type: "error" });
1855
1819
  }
1856
- }, [mode, taskId, title, description, priority, complexity, assignedProfile, maxAttempts, budgetTokens, budgetCost, notes, files, dependsOn, taskData, callTool, onSave]);
1820
+ }, [mode, taskId, title, description, priority, complexity, assignedProfile, budgetCost, notes, files, dependsOn, taskData, callTool, onSave]);
1857
1821
  const handleDelete = useCallback3(async () => {
1858
1822
  if (!taskId) return;
1859
1823
  const ok = await ctx.confirm("Are you sure you want to cancel this task?", {
@@ -2007,30 +1971,6 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
2007
1971
  defaultBoxStyle: { flex: 1, minWidth: 0 }
2008
1972
  })
2009
1973
  ),
2010
- // -- Max Attempts --
2011
- React7.createElement(
2012
- Field,
2013
- { label: "Max Attempts" },
2014
- React7.createElement(Input, {
2015
- type: "number",
2016
- size: "small",
2017
- placeholder: "3",
2018
- value: maxAttempts,
2019
- onChange: (v) => setMaxAttempts(v)
2020
- })
2021
- ),
2022
- // -- Budget Tokens --
2023
- React7.createElement(
2024
- Field,
2025
- { label: "Budget Tokens" },
2026
- React7.createElement(Input, {
2027
- type: "number",
2028
- size: "small",
2029
- placeholder: "Optional token limit",
2030
- value: budgetTokens,
2031
- onChange: (v) => setBudgetTokens(v)
2032
- })
2033
- ),
2034
1974
  // -- Budget Cost --
2035
1975
  React7.createElement(
2036
1976
  Field,
@@ -2228,14 +2168,14 @@ function TaskEditor({ mode, taskId, onSave, onCancel, onDelete }) {
2228
2168
  `$${(taskData.costUsed ?? 0).toFixed(3)}`
2229
2169
  )
2230
2170
  ),
2231
- taskData.context?.lastError && React7.createElement(
2171
+ taskData.context?.runHistory?.slice(-1)?.[0]?.remainingWork && React7.createElement(
2232
2172
  Field,
2233
- { label: "Last error", align: "start" },
2173
+ { label: "Remaining work", align: "start" },
2234
2174
  React7.createElement(Typography6, {
2235
2175
  variant: "smallCaption-regular",
2236
- color: "color-status-negative",
2176
+ color: "color-status-caution",
2237
2177
  style: { fontFamily: "var(--font-mono, monospace)", whiteSpace: "pre-wrap" }
2238
- }, taskData.context.lastError)
2178
+ }, taskData.context.runHistory.slice(-1)[0]?.remainingWork)
2239
2179
  )
2240
2180
  )
2241
2181
  ),
@@ -2798,23 +2738,46 @@ Each subtask: focused (one concern), self-contained, properly routed, ordered by
2798
2738
  After implementation tasks, create a verification task (ops profile) that runs \`tsc --noEmit\`.
2799
2739
  This catches compilation errors before tests run, saving time and token spend.
2800
2740
 
2801
- ### Verify & Iterate Loop
2802
- Your LAST subtask MUST be a "Verify & Iterate" task assigned to yourself (orchestrator):
2741
+ ### Review & Steer (Convergence Loop)
2742
+ Your LAST subtask MUST be a "Review & Steer" task assigned to yourself (orchestrator):
2803
2743
  - dependsOn: ALL other subtasks
2804
- - When it runs: review all task results, check for failures, gaps, or quality issues
2805
- - If everything passes: signal done
2806
- - If issues found: create fix tasks + another verify gate
2807
- This creates a self-correcting loop: plan \u2192 execute \u2192 verify \u2192 fix \u2192 verify \u2192 done.
2744
+ - acceptance: include the objective's acceptance criteria
2745
+
2746
+ When this task runs, you are in STEERING MODE. Read your context carefully:
2747
+ - Sibling tasks show their status, acceptance check results, and stagnation counts
2748
+ - Your job is to decide: are we done, or do we need more work?
2749
+
2750
+ STEERING DECISIONS:
2751
+ 1. ALL DONE: Every subtask has passing acceptance checks \u2192 call done
2752
+ 2. PROGRESS: Tasks are open with recent changes \u2192 create another "Review & Steer" depending on open tasks, call done
2753
+ 3. STAGNANT (stagnationCount >= 3): A task keeps failing the same way \u2192 INTERVENE:
2754
+ - REASSIGN: Change the task description to suggest a different profile ("This might need ops help")
2755
+ - REDEFINE: Create a new task with smaller scope or different approach, cancel the stuck one
2756
+ - DROP: Cancel a non-essential task that's blocking progress
2757
+ 4. FIX: Acceptance checks failing with specific errors \u2192 create targeted fix tasks
2758
+
2759
+ After creating fix tasks, ALWAYS create another "Review & Steer" task depending on those fixes.
2760
+ This creates the convergence loop: decompose \u2192 execute \u2192 review \u2192 fix \u2192 review \u2192 done.
2761
+
2762
+ ### Existing Subtasks (Retries)
2763
+ If your context shows "Parent Context" with existing sibling tasks, those are subtasks from a previous run. Do NOT create duplicates. Check what exists and only create MISSING tasks. If all subtasks already exist and look correct, just call done.
2764
+
2765
+ ### Acceptance Criteria (Shell Scripts)
2766
+ Every task MUST have acceptance.checks \u2014 an array of shell commands that verify "done".
2767
+ Each command must exit 0 to pass. The system runs them AUTOMATICALLY after each completed run.
2768
+ If any check fails, the task stays open for another run.
2808
2769
 
2809
- ### Retries
2810
- If your context shows "Parent Context" with existing sibling tasks, those are subtasks YOU already created in a previous attempt. Do NOT create duplicates. Check what exists and only create MISSING tasks. If all subtasks already exist, just call done.
2770
+ Write commands relative to the workspace root. Examples:
2771
+ - File exists: test -f url-shortener/src/server.ts
2772
+ - Compiles: cd url-shortener && npx tsc --noEmit
2773
+ - Tests pass: cd url-shortener && npx vitest run
2774
+ - Export exists: grep -r "export.*startServer" url-shortener/src/
2775
+ - No console.log: ! grep -r "console.log" url-shortener/src/
2811
2776
 
2812
2777
  ### Example
2813
- { operation: "task_create", args: { title: "Design: Create project contract", parentId: "@self", assignedProfile: "developer", complexity: "moderate", description: "Create .design.md with interfaces and contracts.", dependsOn: [] } }
2814
- { operation: "task_create", args: { title: "Setup project", parentId: "@self", assignedProfile: "ops", dependsOn: [] } }
2815
- { operation: "task_create", args: { title: "Implement storage module", parentId: "@self", assignedProfile: "developer", dependsOn: ["Design: Create project contract", "Setup project"], description: "You may ONLY create: src/types.ts, src/storage.ts" } }
2816
- { operation: "task_create", args: { title: "Implement HTTP server", parentId: "@self", assignedProfile: "developer", dependsOn: ["Implement storage module"], description: "You may ONLY create: src/server.ts" } }
2817
- { operation: "task_create", args: { title: "Verify & Iterate", parentId: "@self", assignedProfile: "orchestrator", dependsOn: ["Implement HTTP server"], description: "Review all task results. If issues found, create fix tasks. If all good, signal done." } }`
2778
+ { operation: "task_create", args: { title: "Design: Create project contract", parentId: "@self", assignedProfile: "developer", description: "Create .design.md", acceptance: { checks: [{ name: "design exists", command: "test -f url-shortener/.design.md" }] }, dependsOn: [] } }
2779
+ { operation: "task_create", args: { title: "Implement storage", parentId: "@self", assignedProfile: "developer", dependsOn: ["Design: Create project contract"], description: "You may ONLY create: src/types.ts, src/storage.ts", acceptance: { checks: [{ name: "files exist", command: "test -f url-shortener/src/types.ts && test -f url-shortener/src/storage.ts" }, { name: "compiles", command: "cd url-shortener && npx tsc --noEmit" }] } } }
2780
+ { operation: "task_create", args: { title: "Review & Steer", parentId: "@self", assignedProfile: "orchestrator", dependsOn: ["Implement storage"], description: "Review subtask results. If all acceptance checks pass, signal done. If issues, create fix tasks + another Review & Steer." } }`
2818
2781
  };
2819
2782
  var CAP_ROLE_DEVELOPER = {
2820
2783
  name: "role-developer",
@@ -349,18 +349,14 @@ var {
349
349
  } = require("@fw/plugin-ui-kit");
350
350
  var statusToIcon = {
351
351
  "open": "pending",
352
- "pending": "pending",
353
352
  "in-progress": "running",
354
353
  "done": "completed",
355
- "blocked": "pending",
356
354
  "cancelled": "failed"
357
355
  };
358
356
  var statusToLabel = {
359
357
  "open": "Open",
360
- "pending": "Queued",
361
358
  "in-progress": "Running",
362
359
  "done": "Done",
363
- "blocked": "Blocked",
364
360
  "cancelled": "Cancelled"
365
361
  };
366
362
  var headerStyle = {
@@ -415,11 +411,7 @@ function SubtaskRowItem({ sub, onBack }) {
415
411
  label: sub.assignedProfile,
416
412
  size: "small",
417
413
  color: "color-status-info"
418
- }),
419
- React2.createElement(Typography, {
420
- variant: "caption-regular",
421
- color: "color-text-subtle"
422
- }, `#${sub.attempt}`)
414
+ })
423
415
  );
424
416
  }
425
417
  function TaskDetailView({ taskId, onBack, onEdit }) {
@@ -438,7 +430,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
438
430
  const t = data.task ?? data;
439
431
  setTask(t);
440
432
  const rs = t.context;
441
- const summaries = rs?.runSummaries;
433
+ const summaries = rs?.runHistory;
442
434
  if (summaries?.length) {
443
435
  setHistory((prev) => {
444
436
  if (prev.length > 0) return prev;
@@ -507,13 +499,13 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
507
499
  awaitingApproval,
508
500
  report: liveReport
509
501
  } = useStreamTimeline(stream.events, stream.isDone);
510
- const currentRunId = task?.currentRunId;
511
- const isLive = task?.status === "in-progress" && !!currentRunId;
502
+ const activeRunId = task?.activeRunId;
503
+ const isLive = task?.status === "in-progress" && !!activeRunId;
512
504
  useEffect2(() => {
513
- if (!isLive || !currentRunId) return;
514
- stream.start(packId, "fw_weaver_events", currentRunId);
505
+ if (!isLive || !activeRunId) return;
506
+ stream.start(packId, "fw_weaver_events", activeRunId);
515
507
  return () => stream.stop();
516
- }, [isLive, currentRunId, packId]);
508
+ }, [isLive, activeRunId, packId]);
517
509
  const [detailTab, setDetailTab] = useState2("runs");
518
510
  const [actionLoading, setActionLoading] = useState2(null);
519
511
  const [availableProfiles, setAvailableProfiles] = useState2([]);
@@ -768,7 +760,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
768
760
  React2.createElement(Chip, {
769
761
  label: statusToLabel[task.status] || task.status || "open",
770
762
  size: "small",
771
- color: task.status === "done" ? "color-status-positive" : task.status === "cancelled" ? "color-status-negative" : task.status === "in-progress" ? "color-status-info" : task.status === "blocked" ? "color-status-caution" : "color-brand-alt"
763
+ color: task.status === "done" ? "color-status-positive" : task.status === "cancelled" ? "color-status-negative" : task.status === "in-progress" ? "color-status-info" : "color-brand-alt"
772
764
  }),
773
765
  task.assignedProfile && React2.createElement(Chip, { key: `profile-${task.assignedProfile}`, label: task.assignedProfile, size: "small", color: "color-status-info" }),
774
766
  task.priority > 0 && React2.createElement(Chip, {
@@ -776,10 +768,10 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
776
768
  size: "small",
777
769
  color: task.priority >= 3 ? "color-status-caution" : "color-status-info"
778
770
  }),
779
- task.maxAttempts != null && task.maxAttempts > 1 && React2.createElement(Typography, {
771
+ (task.context?.runHistory?.length ?? 0) > 0 && React2.createElement(Typography, {
780
772
  variant: "smallCaption-regular",
781
773
  color: "color-text-subtle"
782
- }, `${task.runs?.length ?? 0}/${task.maxAttempts} runs`)
774
+ }, `${task.context.runHistory.length} run${task.context.runHistory.length !== 1 ? "s" : ""}`)
783
775
  ),
784
776
  // Description
785
777
  task.description && React2.createElement(Typography, {
@@ -821,7 +813,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
821
813
  Flex,
822
814
  { variant: "column-stretch-start-nowrap-8", style: { padding: "12px 16px" } },
823
815
  // ── Runs tab ──
824
- detailTab === "runs" && (hasRuns || (task.runs?.length ?? 0) > 0) ? React2.createElement(
816
+ detailTab === "runs" && (hasRuns || (task.context?.runHistory?.length ?? 0) > 0) ? React2.createElement(
825
817
  Flex,
826
818
  { variant: "column-stretch-start-nowrap-8" },
827
819
  ...runItems.map(({ run, runTimeline }) => {
@@ -894,7 +886,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
894
886
  React2.createElement(
895
887
  Flex,
896
888
  { variant: "row-center-start-nowrap-6" },
897
- task.status === "open" && (task.attempt ?? 0) > 0 && React2.createElement(Button, {
889
+ task.status === "open" && (task.context?.runHistory?.length ?? 0) > 0 && React2.createElement(Button, {
898
890
  size: "xs",
899
891
  variant: "fill",
900
892
  color: "primary",
@@ -902,7 +894,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
902
894
  loading: actionLoading === "retry",
903
895
  disabled: !!actionLoading
904
896
  }, "Retry Task"),
905
- (task.status === "open" || task.status === "pending" || task.status === "in-progress" || task.status === "blocked") && React2.createElement(Button, {
897
+ (task.status === "open" || task.status === "in-progress") && React2.createElement(Button, {
906
898
  size: "xs",
907
899
  variant: "outlined",
908
900
  color: "danger",
@@ -1041,27 +1033,14 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1041
1033
  style: { whiteSpace: "pre-wrap" }
1042
1034
  }, task.context.notes)
1043
1035
  ),
1044
- // Last error
1045
- task.context?.lastError && React2.createElement(
1046
- Flex,
1047
- {
1048
- variant: "column-stretch-start-nowrap-4"
1049
- },
1050
- React2.createElement(Typography, { variant: "caption-thick", color: "color-status-negative" }, "Last Error"),
1051
- React2.createElement(Typography, {
1052
- variant: "smallCaption-regular",
1053
- color: "color-text-high",
1054
- style: { fontFamily: "var(--font-mono, monospace)", whiteSpace: "pre-wrap" }
1055
- }, task.context.lastError)
1056
- ),
1057
- // Run summaries (accumulated context)
1058
- (task.context?.runSummaries?.length ?? 0) > 0 && React2.createElement(
1036
+ // Run history (accumulated context)
1037
+ (task.context?.runHistory?.length ?? 0) > 0 && React2.createElement(
1059
1038
  Flex,
1060
1039
  {
1061
1040
  variant: "column-stretch-start-nowrap-6"
1062
1041
  },
1063
- React2.createElement(Typography, { variant: "caption-thick", color: "color-text-medium" }, "Run Summaries"),
1064
- ...(task.context?.runSummaries ?? []).map(
1042
+ React2.createElement(Typography, { variant: "caption-thick", color: "color-text-medium" }, "Run History"),
1043
+ ...(task.context?.runHistory ?? []).map(
1065
1044
  (rs, i) => React2.createElement(
1066
1045
  Card,
1067
1046
  {
@@ -1091,10 +1070,10 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1091
1070
  color: "color-text-subtle",
1092
1071
  style: { fontFamily: "var(--font-mono, monospace)" }
1093
1072
  }, `Files: ${rs.filesModified.join(", ")}`),
1094
- rs.error && React2.createElement(Typography, {
1073
+ rs.remainingWork && React2.createElement(Typography, {
1095
1074
  variant: "smallCaption-regular",
1096
- color: "color-status-negative"
1097
- }, `Error: ${rs.error}`)
1075
+ color: "color-status-caution"
1076
+ }, `Remaining: ${rs.remainingWork}`)
1098
1077
  )
1099
1078
  )
1100
1079
  ),
@@ -1111,7 +1090,7 @@ function TaskDetailView({ taskId, onBack, onEdit }) {
1111
1090
  React2.createElement(
1112
1091
  Typography,
1113
1092
  { variant: "smallCaption-regular", color: "color-text-high" },
1114
- `Tokens: ${task.tokensUsed?.toLocaleString() ?? 0}${task.budgetTokens ? ` / ${task.budgetTokens.toLocaleString()}` : ""}`
1093
+ `Tokens: ${task.tokensUsed?.toLocaleString() ?? 0}`
1115
1094
  ),
1116
1095
  React2.createElement(
1117
1096
  Typography,