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
@@ -46,12 +46,30 @@ export const ERROR_RECOVERY_TEMPLATE = {
46
46
  }, { x: 400, y: 180 }),
47
47
 
48
48
  node("analyze-error", "action.run_agent", "Analyze Failure", {
49
- prompt: "Analyze the following error and suggest a fix:\n\n{{lastError}}\n\nTask: {{taskTitle}}",
49
+ prompt:
50
+ "Analyze the following task failure and suggest the most likely minimal fix.\n\n" +
51
+ "Task: {{taskTitle}} ({{taskId}})\n" +
52
+ "Retry attempt: {{$data?.retryCount || 0}}/{{$data?.maxRetries || 3}}\n" +
53
+ "Branch: {{branch}}\n" +
54
+ "Base branch: {{baseBranch}}\n" +
55
+ "Worktree: {{worktreePath}}\n\n" +
56
+ "Last error:\n{{lastError}}",
50
57
  timeoutMs: 300000,
51
58
  }, { x: 200, y: 330 }),
52
59
 
53
60
  node("retry-task", "action.run_agent", "Retry Task", {
54
- prompt: "{{taskExecutorRetryPrompt}}",
61
+ prompt:
62
+ "{{taskExecutorRetryPrompt}}\n\n" +
63
+ "Failure context:\n" +
64
+ "- taskId: {{taskId}}\n" +
65
+ "- taskTitle: {{taskTitle}}\n" +
66
+ "- branch: {{branch}}\n" +
67
+ "- baseBranch: {{baseBranch}}\n" +
68
+ "- worktreePath: {{worktreePath}}\n" +
69
+ "- retryCount: {{$data?.retryCount || 0}}/{{$data?.maxRetries || 3}}\n" +
70
+ "- lastError: {{lastError}}\n" +
71
+ "- recoveryAnalysis: {{$ctx.getNodeOutput('analyze-error')?.output || ''}}\n\n" +
72
+ "Use the analysis to choose a different approach if the previous attempt failed.",
55
73
  timeoutMs: 3600000,
56
74
  failOnError: true,
57
75
  maxRetries: "{{maxRetries}}",
@@ -69,13 +87,17 @@ export const ERROR_RECOVERY_TEMPLATE = {
69
87
  }, { x: 90, y: 760 }),
70
88
 
71
89
  node("escalate", "notify.telegram", "Escalate to Human", {
72
- message: ":alert: Task **{{taskTitle}}** failed after {{maxRetries}} attempts. Manual intervention needed.\n\nLast error: {{lastError}}",
90
+ message:
91
+ ":alert: Task **{{taskTitle}}** failed after {{maxRetries}} attempts. Manual intervention needed.\n\n" +
92
+ "Last error: {{lastError}}\n\n" +
93
+ "Recovery analysis: {{$ctx.getNodeOutput('analyze-error')?.output || ''}}",
73
94
  }, { x: 600, y: 620 }),
74
95
 
75
96
  node("chain-repair", "action.execute_workflow", "Trigger Repair Workflow", {
76
97
  workflowId: "template-task-repair-worktree",
77
98
  mode: "dispatch",
78
- input: "({taskId: $data?.taskId, taskTitle: $data?.taskTitle, worktreePath: $data?.worktreePath, branch: $data?.branch, baseBranch: $data?.baseBranch, error: $data?.lastError})",
99
+ input:
100
+ "(() => { const analysisRaw = String($ctx.getNodeOutput('analyze-error')?.output || '').trim(); const retryOutputRaw = String($ctx.getNodeOutput('retry-task')?.output || '').trim(); const retryErrorRaw = String($ctx.getNodeOutput('retry-task')?.error || '').trim(); const truncate = (value, limit = 2000) => value.length > limit ? `${value.slice(0, limit)}...` : value; const diagnostics = [String($data?.lastError || '').trim(), analysisRaw ? `Recovery analysis:\n${truncate(analysisRaw)}` : '', retryOutputRaw ? `Retry output:\n${truncate(retryOutputRaw)}` : '', retryErrorRaw ? `Retry error:\n${truncate(retryErrorRaw)}` : ''].filter(Boolean).join('\n\n'); return { taskId: $data?.taskId, taskTitle: $data?.taskTitle, worktreePath: $data?.worktreePath, branch: $data?.branch, baseBranch: $data?.baseBranch, error: diagnostics || String($data?.lastError || ''), recoveryAnalysis: truncate(analysisRaw), retryResult: { success: $ctx.getNodeOutput('retry-task')?.success === true, output: truncate(retryOutputRaw), error: truncate(retryErrorRaw) } }; })()",
79
101
  }, { x: 400, y: 760 }),
80
102
  ],
81
103
  edges: [
@@ -92,7 +114,7 @@ export const ERROR_RECOVERY_TEMPLATE = {
92
114
  author: "bosun",
93
115
  version: 1,
94
116
  createdAt: "2025-02-24T00:00:00Z",
95
- templateVersion: "1.0.1",
117
+ templateVersion: "1.1.0",
96
118
  tags: ["error", "recovery", "autofix"],
97
119
  requiredTemplates: ["template-task-repair-worktree"],
98
120
  replaces: {
@@ -458,6 +480,20 @@ export const TASK_FINALIZATION_GUARD_TEMPLATE = {
458
480
  },
459
481
  }, { x: 240, y: 900 }),
460
482
 
483
+ node("handoff-pr-progressor", "action.execute_workflow", "Dispatch PR Progressor", {
484
+ workflowId: "template-bosun-pr-progressor",
485
+ mode: "dispatch",
486
+ input: {
487
+ taskId: "{{taskId}}",
488
+ taskTitle: "{{taskTitle}}",
489
+ branch: "{{branch}}",
490
+ baseBranch: "{{baseBranch}}",
491
+ prNumber: "{{$data?.prNumber ?? $ctx.getNodeOutput('create-pr')?.prNumber ?? null}}",
492
+ prUrl: "{{$data?.prUrl || $ctx.getNodeOutput('create-pr')?.prUrl || ''}}",
493
+ repo: "{{$data?.repo || $data?.repoSlug || $data?.repository || $ctx.getNodeOutput('create-pr')?.repoSlug || ''}}",
494
+ },
495
+ }, { x: 240, y: 1040 }),
496
+
461
497
  node("mark-todo-failed", "action.update_task_status", "Mark Todo (Checks Failed)", {
462
498
  taskId: "{{taskId}}",
463
499
  status: "todo",
@@ -497,13 +533,13 @@ export const TASK_FINALIZATION_GUARD_TEMPLATE = {
497
533
  node("notify-pass", "notify.log", "Log Finalization Success", {
498
534
  message: "Task {{taskId}} finalization passed — moved to inreview",
499
535
  level: "info",
500
- }, { x: 240, y: 1040 }),
536
+ }, { x: 240, y: 1180 }),
501
537
 
502
538
  node("chain-archiver", "flow.universal", "Queue Archival", {
503
539
  workflowId: "template-task-archiver",
504
540
  mode: "dispatch",
505
541
  input: "({taskId: $data?.taskId, taskTitle: $data?.taskTitle, completedAt: new Date().toISOString(), taskJson: JSON.stringify($data?.task || {id: $data?.taskId, title: $data?.taskTitle})})",
506
- }, { x: 240, y: 1180 }),
542
+ }, { x: 240, y: 1320 }),
507
543
 
508
544
  node("end-success", "flow.end", "End Success", {
509
545
  status: "completed",
@@ -513,7 +549,7 @@ export const TASK_FINALIZATION_GUARD_TEMPLATE = {
513
549
  taskId: "{{taskId}}",
514
550
  taskTitle: "{{taskTitle}}",
515
551
  },
516
- }, { x: 240, y: 1310 }),
552
+ }, { x: 240, y: 1450 }),
517
553
 
518
554
  node("notify-fail", "notify.telegram", "Notify Finalization Failure", {
519
555
  message: ":alert: Task finalization failed for **{{taskTitle}}** ({{taskId}}). Repair workflow handoff triggered.",
@@ -543,7 +579,8 @@ export const TASK_FINALIZATION_GUARD_TEMPLATE = {
543
579
  edge("create-pr", "create-pr-success"),
544
580
  edge("create-pr-success", "mark-inreview", { condition: "$output?.result === true", port: "yes" }),
545
581
  edge("create-pr-success", "mark-todo-failed", { condition: "$output?.result !== true", port: "no" }),
546
- edge("mark-inreview", "notify-pass"),
582
+ edge("mark-inreview", "handoff-pr-progressor"),
583
+ edge("handoff-pr-progressor", "notify-pass"),
547
584
  edge("notify-skip-missing-context", "end-success"),
548
585
  edge("notify-pass", "chain-archiver"),
549
586
  edge("chain-archiver", "end-success"),
@@ -557,7 +594,7 @@ export const TASK_FINALIZATION_GUARD_TEMPLATE = {
557
594
  createdAt: "2026-02-26T00:00:00Z",
558
595
  templateVersion: "1.0.1",
559
596
  tags: ["finalization", "quality-gate", "prepush", "handoff", "reliability"],
560
- requiredTemplates: ["template-task-archiver"],
597
+ requiredTemplates: ["template-task-archiver", "template-bosun-pr-progressor"],
561
598
  replaces: {
562
599
  module: "task-executor.mjs",
563
600
  functions: ["_handleTaskResult finalization gate"],
@@ -659,6 +696,20 @@ export const TASK_REPAIR_WORKTREE_TEMPLATE = {
659
696
  },
660
697
  }, { x: 250, y: 1020 }),
661
698
 
699
+ node("handoff-pr-progressor", "action.execute_workflow", "Dispatch PR Progressor", {
700
+ workflowId: "template-bosun-pr-progressor",
701
+ mode: "dispatch",
702
+ input: {
703
+ taskId: "{{taskId}}",
704
+ taskTitle: "{{taskTitle}}",
705
+ branch: "{{branch}}",
706
+ baseBranch: "{{baseBranch}}",
707
+ prNumber: "{{$data?.prNumber ?? $ctx.getNodeOutput('create-pr')?.prNumber ?? null}}",
708
+ prUrl: "{{$data?.prUrl || $ctx.getNodeOutput('create-pr')?.prUrl || ''}}",
709
+ repo: "{{$data?.repo || $data?.repoSlug || $data?.repository || $ctx.getNodeOutput('create-pr')?.repoSlug || ''}}",
710
+ },
711
+ }, { x: 250, y: 1160 }),
712
+
662
713
  node("mark-todo", "action.update_task_status", "Mark Todo (Repair Failed)", {
663
714
  taskId: "{{taskId}}",
664
715
  status: "todo",
@@ -676,7 +727,7 @@ export const TASK_REPAIR_WORKTREE_TEMPLATE = {
676
727
  node("notify-success", "notify.telegram", "Notify Repair Success", {
677
728
  message: ":check: Repair workflow recovered **{{taskTitle}}** ({{taskId}}) and moved it to inreview.",
678
729
  silent: true,
679
- }, { x: 250, y: 1160 }),
730
+ }, { x: 250, y: 1300 }),
680
731
 
681
732
  node("notify-escalate", "notify.telegram", "Escalate Repair Failure", {
682
733
  message: ":alert: Repair workflow could not recover **{{taskTitle}}** ({{taskId}}). Manual intervention required.",
@@ -700,7 +751,8 @@ export const TASK_REPAIR_WORKTREE_TEMPLATE = {
700
751
  edge("create-pr", "create-pr-success"),
701
752
  edge("create-pr-success", "mark-inreview", { condition: "$output?.result === true", port: "yes" }),
702
753
  edge("create-pr-success", "mark-todo", { condition: "$output?.result !== true", port: "no" }),
703
- edge("mark-inreview", "notify-success"),
754
+ edge("mark-inreview", "handoff-pr-progressor"),
755
+ edge("handoff-pr-progressor", "notify-success"),
704
756
  edge("mark-todo", "notify-escalate"),
705
757
  edge("no-worktree", "notify-escalate"),
706
758
  ],
@@ -710,6 +762,7 @@ export const TASK_REPAIR_WORKTREE_TEMPLATE = {
710
762
  createdAt: "2026-02-26T00:00:00Z",
711
763
  templateVersion: "1.0.1",
712
764
  tags: ["repair", "recovery", "worktree", "resilience", "automation"],
765
+ requiredTemplates: ["template-bosun-pr-progressor"],
713
766
  replaces: {
714
767
  module: "task-executor.mjs",
715
768
  functions: ["retry/escalation recovery path"],
@@ -35,8 +35,9 @@ export const TASK_BATCH_PROCESSOR_TEMPLATE = {
35
35
  "using the Task Lifecycle sub-workflow. Automatically picks up tasks " +
36
36
  "when backlog drops below threshold, fans out execution across " +
37
37
  "available slots, and reports batch results.",
38
- category: "lifecycle",
38
+ category: "task-execution",
39
39
  enabled: true,
40
+ core: true,
40
41
  recommended: true,
41
42
  trigger: "trigger.task_available",
42
43
  variables: {
@@ -50,7 +51,7 @@ export const TASK_BATCH_PROCESSOR_TEMPLATE = {
50
51
  // ── Trigger: Tasks available for processing ──────────────────────────
51
52
  node("trigger", "trigger.task_available", "Tasks Available?", {
52
53
  maxParallel: "{{maxConcurrent}}",
53
- pollIntervalMs: 60000,
54
+ pollIntervalMs: 15000,
54
55
  status: "{{pollStatus}}",
55
56
  }, { x: 400, y: 50 }),
56
57
 
@@ -90,7 +91,7 @@ export const TASK_BATCH_PROCESSOR_TEMPLATE = {
90
91
 
91
92
  // ── Fan-out: dispatch each task to the lifecycle workflow ─────────────
92
93
  node("dispatch-tasks", "loop.for_each", "Dispatch Tasks", {
93
- items: "{{queryResult}}",
94
+ items: "{{query-tasks.output}}",
94
95
  itemVariable: "currentTask",
95
96
  indexVariable: "taskIndex",
96
97
  maxConcurrent: "{{maxConcurrent}}",
@@ -112,7 +113,7 @@ export const TASK_BATCH_PROCESSOR_TEMPLATE = {
112
113
  // ── Notify on completion ─────────────────────────────────────────────
113
114
  node("notify-complete", "notify.telegram", "Batch Summary", {
114
115
  channel: "{{notifyChannel}}",
115
- message: "Task batch completed: {{batchResult.successCount}}/{{batchResult.totalItems}} succeeded",
116
+ message: "Task batch completed: {{dispatch-tasks.successCount}}/{{dispatch-tasks.totalItems}} succeeded ({{dispatch-tasks.failCount}} failed)",
116
117
  }, { x: 400, y: 700 }),
117
118
  ],
118
119
  edges: [
@@ -145,7 +146,7 @@ export const TASK_BATCH_PR_TEMPLATE = {
145
146
  "Simplified batch processor that picks todo tasks, runs the agent on " +
146
147
  "each, and creates pull requests for any that produce commits. Ideal " +
147
148
  "for autonomous mode where tasks should flow straight to PRs.",
148
- category: "lifecycle",
149
+ category: "task-execution",
149
150
  enabled: true,
150
151
  recommended: false,
151
152
  trigger: "trigger.task_available",
@@ -161,7 +162,7 @@ export const TASK_BATCH_PR_TEMPLATE = {
161
162
  // ── Trigger ──────────────────────────────────────────────────────────
162
163
  node("trigger", "trigger.task_available", "Tasks Available?", {
163
164
  maxParallel: "{{maxConcurrent}}",
164
- pollIntervalMs: 60000,
165
+ pollIntervalMs: 15000,
165
166
  status: "{{pollStatus}}",
166
167
  }, { x: 400, y: 50 }),
167
168
 
@@ -194,7 +195,7 @@ export const TASK_BATCH_PR_TEMPLATE = {
194
195
 
195
196
  // ── Fan-out: per-task agent + PR ─────────────────────────────────────
196
197
  node("for-each-task", "loop.for_each", "Process Each Task", {
197
- items: "{{queryResult}}",
198
+ items: "{{query-tasks.output}}",
198
199
  itemVariable: "task",
199
200
  indexVariable: "idx",
200
201
  maxConcurrent: "{{maxConcurrent}}",
@@ -240,11 +241,25 @@ export const TASK_BATCH_PR_TEMPLATE = {
240
241
  status: "inreview",
241
242
  }, { x: 400, y: 1090 }),
242
243
 
244
+ node("handoff-pr-progressor", "action.execute_workflow", "Dispatch PR Progressor", {
245
+ workflowId: "template-bosun-pr-progressor",
246
+ mode: "dispatch",
247
+ input: {
248
+ taskId: "{{task.taskId}}",
249
+ taskTitle: "{{task.taskTitle}}",
250
+ branch: "{{task.branch}}",
251
+ baseBranch: "{{defaultBaseBranch}}",
252
+ prNumber: "{{$ctx.getNodeOutput('create-pr')?.prNumber ?? null}}",
253
+ prUrl: "{{$ctx.getNodeOutput('create-pr')?.prUrl || ''}}",
254
+ repo: "{{$ctx.getNodeOutput('create-pr')?.repoSlug || $data?.repo || $data?.repoSlug || $data?.repository || ''}}",
255
+ },
256
+ }, { x: 400, y: 1160 }),
257
+
243
258
  node("join-batch-outcomes", "flow.join", "Join Batch Outcomes", {
244
259
  mode: "all",
245
- sourceNodeIds: ["detect-commits", "set-inreview"],
260
+ sourceNodeIds: ["detect-commits", "handoff-pr-progressor"],
246
261
  includeSkipped: true,
247
- }, { x: 400, y: 1160 }),
262
+ }, { x: 400, y: 1230 }),
248
263
 
249
264
  // ── Batch complete notification ──────────────────────────────────────
250
265
  node("notify", "notify.telegram", "Batch Complete", {
@@ -262,7 +277,8 @@ export const TASK_BATCH_PR_TEMPLATE = {
262
277
  edge("push-branch", "create-pr"),
263
278
  edge("create-pr", "set-inreview"),
264
279
  edge("detect-commits", "join-batch-outcomes", { condition: "result.hasNewCommits !== true" }),
265
- edge("set-inreview", "join-batch-outcomes"),
280
+ edge("set-inreview", "handoff-pr-progressor"),
281
+ edge("handoff-pr-progressor", "join-batch-outcomes"),
266
282
  edge("join-batch-outcomes", "notify"),
267
283
  ],
268
284
  metadata: {
@@ -271,5 +287,6 @@ export const TASK_BATCH_PR_TEMPLATE = {
271
287
  createdAt: "2026-03-15T00:00:00Z",
272
288
  templateVersion: "1.0.0",
273
289
  tags: ["task", "batch", "pr", "agent", "autonomous"],
290
+ requiredTemplates: ["template-bosun-pr-progressor"],
274
291
  },
275
292
  };