bosun 0.40.21 → 0.41.1

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 (80) hide show
  1. package/.env.example +8 -0
  2. package/README.md +20 -0
  3. package/agent/agent-custom-tools.mjs +23 -5
  4. package/agent/agent-event-bus.mjs +248 -6
  5. package/agent/agent-pool.mjs +131 -30
  6. package/agent/agent-work-analyzer.mjs +8 -16
  7. package/agent/primary-agent.mjs +81 -7
  8. package/agent/retry-queue.mjs +164 -0
  9. package/bench/swebench/bosun-swebench.mjs +5 -0
  10. package/bosun.config.example.json +25 -0
  11. package/bosun.schema.json +825 -183
  12. package/cli.mjs +267 -8
  13. package/config/config-doctor.mjs +51 -2
  14. package/config/config.mjs +232 -5
  15. package/github/github-auth-manager.mjs +70 -19
  16. package/infra/library-manager.mjs +894 -60
  17. package/infra/monitor.mjs +701 -69
  18. package/infra/runtime-accumulator.mjs +376 -84
  19. package/infra/session-tracker.mjs +95 -28
  20. package/infra/test-runtime.mjs +267 -0
  21. package/lib/codebase-audit.mjs +133 -18
  22. package/package.json +30 -8
  23. package/server/setup-web-server.mjs +29 -1
  24. package/server/ui-server.mjs +1571 -49
  25. package/setup.mjs +27 -24
  26. package/shell/codex-shell.mjs +34 -3
  27. package/shell/copilot-shell.mjs +50 -8
  28. package/task/msg-hub.mjs +193 -0
  29. package/task/pipeline.mjs +544 -0
  30. package/task/task-claims.mjs +6 -10
  31. package/task/task-cli.mjs +38 -2
  32. package/task/task-executor-pipeline.mjs +143 -0
  33. package/task/task-executor.mjs +36 -27
  34. package/telegram/get-telegram-chat-id.mjs +57 -47
  35. package/ui/components/chat-view.js +18 -1
  36. package/ui/components/workspace-switcher.js +321 -9
  37. package/ui/demo-defaults.js +17830 -10433
  38. package/ui/demo.html +9 -1
  39. package/ui/modules/router.js +1 -1
  40. package/ui/modules/settings-schema.js +2 -0
  41. package/ui/modules/state.js +54 -57
  42. package/ui/modules/voice-client-sdk.js +376 -37
  43. package/ui/modules/voice-client.js +173 -33
  44. package/ui/setup.html +68 -2
  45. package/ui/styles/components.css +571 -1
  46. package/ui/styles.css +201 -1
  47. package/ui/tabs/dashboard.js +74 -0
  48. package/ui/tabs/library.js +410 -55
  49. package/ui/tabs/logs.js +10 -0
  50. package/ui/tabs/settings.js +178 -99
  51. package/ui/tabs/tasks.js +1083 -507
  52. package/ui/tabs/telemetry.js +34 -0
  53. package/ui/tabs/workflow-canvas-utils.mjs +38 -1
  54. package/ui/tabs/workflows.js +1275 -402
  55. package/voice/voice-agents-sdk.mjs +2 -2
  56. package/voice/voice-relay.mjs +28 -20
  57. package/workflow/declarative-workflows.mjs +145 -0
  58. package/workflow/msg-hub.mjs +237 -0
  59. package/workflow/pipeline-workflows.mjs +287 -0
  60. package/workflow/pipeline.mjs +828 -315
  61. package/workflow/project-detection.mjs +559 -0
  62. package/workflow/workflow-cli.mjs +128 -0
  63. package/workflow/workflow-contract.mjs +433 -232
  64. package/workflow/workflow-engine.mjs +510 -47
  65. package/workflow/workflow-nodes/custom-loader.mjs +251 -0
  66. package/workflow/workflow-nodes.mjs +2024 -184
  67. package/workflow/workflow-templates.mjs +118 -24
  68. package/workflow-templates/agents.mjs +20 -20
  69. package/workflow-templates/bosun-native.mjs +212 -2
  70. package/workflow-templates/code-quality.mjs +20 -14
  71. package/workflow-templates/continuation-loop.mjs +339 -0
  72. package/workflow-templates/github.mjs +516 -40
  73. package/workflow-templates/planning.mjs +446 -17
  74. package/workflow-templates/reliability.mjs +65 -12
  75. package/workflow-templates/task-batch.mjs +27 -10
  76. package/workflow-templates/task-execution.mjs +752 -0
  77. package/workflow-templates/task-lifecycle.mjs +117 -14
  78. package/workspace/context-cache.mjs +66 -18
  79. package/workspace/workspace-manager.mjs +153 -1
  80. package/workflow-templates/issue-continuation.mjs +0 -243
package/ui/styles.css CHANGED
@@ -199,6 +199,207 @@
199
199
  padding: 0 12px;
200
200
  }
201
201
 
202
+ .wf-palette-backdrop {
203
+ position: absolute;
204
+ inset: 0;
205
+ z-index: 32;
206
+ background: rgba(3, 7, 18, 0.55);
207
+ backdrop-filter: blur(3px);
208
+ }
209
+
210
+ .wf-palette {
211
+ position: absolute;
212
+ top: 72px;
213
+ left: 50%;
214
+ transform: translateX(-50%);
215
+ width: min(760px, calc(100% - 32px));
216
+ max-height: min(70vh, 720px);
217
+ overflow: hidden;
218
+ background: var(--color-bg, #0d1117);
219
+ border: 1px solid var(--color-border, #2a3040);
220
+ border-radius: 16px;
221
+ padding: 14px;
222
+ box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
223
+ display: flex;
224
+ flex-direction: column;
225
+ gap: 12px;
226
+ }
227
+
228
+ .wf-palette-header {
229
+ display: flex;
230
+ align-items: center;
231
+ gap: 10px;
232
+ }
233
+
234
+ .wf-palette-title-group {
235
+ flex: 1;
236
+ min-width: 0;
237
+ }
238
+
239
+ .wf-palette-title {
240
+ font-size: 14px;
241
+ font-weight: 700;
242
+ color: var(--color-text, #fff);
243
+ }
244
+
245
+ .wf-palette-subtitle {
246
+ font-size: 11px;
247
+ color: var(--color-text-secondary, #9ca3af);
248
+ opacity: 0.85;
249
+ margin-top: 3px;
250
+ }
251
+
252
+ .wf-palette-hints {
253
+ display: flex;
254
+ align-items: center;
255
+ gap: 8px;
256
+ font-size: 11px;
257
+ opacity: 0.7;
258
+ }
259
+
260
+ .wf-palette-results {
261
+ display: flex;
262
+ flex-direction: column;
263
+ gap: 8px;
264
+ overflow-y: auto;
265
+ padding-right: 2px;
266
+ }
267
+
268
+ .wf-node-search-item {
269
+ display: flex;
270
+ flex-direction: column;
271
+ gap: 8px;
272
+ width: 100%;
273
+ text-align: left;
274
+ border: 1px solid var(--color-border, #2a3040);
275
+ border-radius: 12px;
276
+ padding: 12px 14px;
277
+ color: var(--color-text, #fff);
278
+ cursor: pointer;
279
+ background: var(--color-bg-secondary, #131722);
280
+ transition: border 0.15s, background 0.15s;
281
+ }
282
+
283
+ .wf-node-search-item.active {
284
+ background: rgba(59, 130, 246, 0.12);
285
+ border-color: rgba(59, 130, 246, 0.66);
286
+ }
287
+
288
+ .wf-node-search-item-top {
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 8px;
292
+ flex-wrap: wrap;
293
+ }
294
+
295
+ .wf-node-search-label {
296
+ font-weight: 700;
297
+ font-size: 13px;
298
+ }
299
+
300
+ .wf-node-category-badge {
301
+ display: inline-flex;
302
+ align-items: center;
303
+ padding: 2px 8px;
304
+ border-radius: 999px;
305
+ font-size: 10px;
306
+ letter-spacing: 0.04em;
307
+ text-transform: uppercase;
308
+ border: 1px solid rgba(148, 163, 184, 0.2);
309
+ }
310
+
311
+ .wf-node-search-type {
312
+ font-size: 11px;
313
+ opacity: 0.55;
314
+ }
315
+
316
+ .wf-node-search-description {
317
+ font-size: 12px;
318
+ opacity: 0.78;
319
+ line-height: 1.4;
320
+ }
321
+
322
+ .wf-node-chip-row {
323
+ display: flex;
324
+ gap: 12px;
325
+ flex-wrap: wrap;
326
+ }
327
+
328
+ .wf-node-chip-group {
329
+ display: flex;
330
+ flex-direction: column;
331
+ gap: 4px;
332
+ min-width: 0;
333
+ }
334
+
335
+ .wf-node-chip-label {
336
+ font-size: 11px;
337
+ color: var(--color-text-secondary, #9ca3af);
338
+ text-transform: uppercase;
339
+ letter-spacing: 0.08em;
340
+ }
341
+
342
+ .wf-node-chip-list {
343
+ display: flex;
344
+ flex-wrap: wrap;
345
+ gap: 4px;
346
+ }
347
+
348
+ .wf-node-chip {
349
+ display: inline-flex;
350
+ align-items: center;
351
+ padding: 2px 8px;
352
+ border-radius: 999px;
353
+ border: 1px solid rgba(148, 163, 184, 0.4);
354
+ font-size: 11px;
355
+ background: var(--color-bg-secondary, #1e293b);
356
+ color: var(--color-text-secondary, #94a3b8);
357
+ letter-spacing: 0.04em;
358
+ }
359
+
360
+ .wf-node-chip-more {
361
+ background: rgba(59, 130, 246, 0.15);
362
+ border-color: rgba(59, 130, 246, 0.3);
363
+ color: #93c5fd;
364
+ }
365
+
366
+ .wf-node-chip-fallback {
367
+ opacity: 0.6;
368
+ }
369
+
370
+ .wf-node-search-empty {
371
+ text-align: center;
372
+ padding: 20px;
373
+ opacity: 0.6;
374
+ }
375
+
376
+ .wf-shortcuts-grid {
377
+ display: flex;
378
+ flex-direction: column;
379
+ gap: 10px;
380
+ }
381
+
382
+ .wf-shortcut-row {
383
+ display: flex;
384
+ align-items: flex-start;
385
+ justify-content: space-between;
386
+ gap: 16px;
387
+ }
388
+
389
+ .wf-shortcut-key {
390
+ font-size: 12px;
391
+ padding: 4px 10px;
392
+ border-radius: 8px;
393
+ background: rgba(148, 163, 184, 0.14);
394
+ }
395
+
396
+ .wf-shortcut-desc {
397
+ font-size: 13px;
398
+ opacity: 0.82;
399
+ text-align: right;
400
+ flex: 1;
401
+ }
402
+
202
403
  .stream-item-details.expanded .stream-item-details-inner {
203
404
  padding: 10px 12px 12px;
204
405
  }
@@ -649,4 +850,3 @@
649
850
  color: var(--text-primary);
650
851
  font-size: 12px;
651
852
  }
652
-
@@ -22,6 +22,7 @@ import {
22
22
  projectSummary,
23
23
  loadStatus,
24
24
  loadProjectSummary,
25
+ loadRetryQueue,
25
26
  showToast,
26
27
  refreshTab,
27
28
  runOptimistic,
@@ -30,6 +31,7 @@ import {
30
31
  getDashboardHistory,
31
32
  setPendingChange,
32
33
  clearPendingChange,
34
+ retryQueueData,
33
35
  } from "../modules/state.js";
34
36
  import { navigateTo } from "../modules/router.js";
35
37
  import { ICONS } from "../modules/icons.js";
@@ -322,6 +324,7 @@ export function DashboardTab() {
322
324
  const prevCounts = useRef(null);
323
325
  const status = statusData.value;
324
326
  const executor = executorData.value;
327
+ const retryQueue = retryQueueData.value || { count: 0, items: [], stats: {} };
325
328
  const project = projectSummary.value;
326
329
  const counts = status?.counts || {};
327
330
  const summary = status?.success_metrics || {};
@@ -448,6 +451,10 @@ export function DashboardTab() {
448
451
  .catch(() => {});
449
452
  }, []);
450
453
 
454
+ useEffect(() => {
455
+ loadRetryQueue().catch(() => {});
456
+ }, []);
457
+
451
458
  // ── Flash metrics on counts change ──
452
459
  useEffect(() => {
453
460
  const current = JSON.stringify(counts);
@@ -657,6 +664,34 @@ export function DashboardTab() {
657
664
 
658
665
  /* ── Recent activity (last 5 tasks from global tasks signal) ── */
659
666
  const recentTasks = (tasksData.value || []).slice(0, 5);
667
+ const retryItems = Array.isArray(retryQueue.items) ? retryQueue.items : [];
668
+
669
+ const formatRetryCountdown = useCallback((nextAttemptAt) => {
670
+ const target = Number(nextAttemptAt || 0);
671
+ if (!Number.isFinite(target) || target <= 0) return "Now";
672
+ const remainingMs = target - now.getTime();
673
+ if (remainingMs <= 0) return "Now";
674
+ const totalSec = Math.ceil(remainingMs / 1000);
675
+ const min = Math.floor(totalSec / 60);
676
+ const sec = totalSec % 60;
677
+ if (min > 0) return `${min}m ${String(sec).padStart(2, "0")}s`;
678
+ return `${sec}s`;
679
+ }, [now]);
680
+
681
+ const handleRetryNow = useCallback(async (taskId) => {
682
+ const id = String(taskId || "").trim();
683
+ if (!id) return;
684
+ try {
685
+ await apiFetch("/api/tasks/retry", {
686
+ method: "POST",
687
+ body: JSON.stringify({ taskId: id }),
688
+ });
689
+ showToast(`Retry requested for ${id}`, "success");
690
+ scheduleRefresh(100);
691
+ } catch {
692
+ /* toast shown by apiFetch */
693
+ }
694
+ }, []);
660
695
 
661
696
  /* ── Loading skeleton ── */
662
697
  if (!status && !executor)
@@ -981,6 +1016,45 @@ export function DashboardTab() {
981
1016
  </div>
982
1017
  <//>
983
1018
 
1019
+ <${Card}
1020
+ title=${html`<span class="dashboard-card-title"
1021
+ ><span class="dashboard-title-icon">${ICONS.refresh || resolveIcon("refresh")}</span>Retry Queue</span
1022
+ >`}
1023
+ className="dashboard-card dashboard-retry-queue"
1024
+ >
1025
+ ${retryItems.length
1026
+ ? html`
1027
+ <div class="dashboard-retry-list">
1028
+ ${retryItems.map((item) => html`
1029
+ <div key=${item.taskId} class="dashboard-retry-item">
1030
+ <div class="dashboard-retry-main">
1031
+ <div class="dashboard-retry-task">${item.taskId}</div>
1032
+ ${item.taskTitle
1033
+ ? html`<div class="dashboard-retry-task-title">${truncate(item.taskTitle, 72)}</div>`
1034
+ : null}
1035
+ <div class="dashboard-retry-error">${truncate(item.lastError || item.reason || "Unknown retry reason", 120)}</div>
1036
+ </div>
1037
+ <div class="dashboard-retry-meta">
1038
+ <span class="dashboard-retry-pill">Attempt ${item.retryCount || 0}</span>
1039
+ <span class="dashboard-retry-pill">Next ${formatRetryCountdown(item.nextAttemptAt)}</span>
1040
+ <${Button}
1041
+ variant="outlined"
1042
+ size="small"
1043
+ onClick=${() => {
1044
+ haptic("medium");
1045
+ void handleRetryNow(item.taskId);
1046
+ }}
1047
+ >
1048
+ Retry Now
1049
+ <//>
1050
+ </div>
1051
+ </div>
1052
+ `)}
1053
+ </div>
1054
+ `
1055
+ : html`<${EmptyState} title="No queued retries" description="Failed tasks waiting to retry appear here automatically." />`}
1056
+ <//>
1057
+
984
1058
  <${Card}
985
1059
  title=${html`<span class="dashboard-card-title"
986
1060
  ><span class="dashboard-title-icon">${ICONS.clock}</span>Recent