bosun 0.42.2 → 0.42.5

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 (82) hide show
  1. package/.env.example +9 -0
  2. package/agent/agent-custom-tools.mjs +10 -12
  3. package/agent/agent-event-bus.mjs +10 -0
  4. package/agent/agent-pool.mjs +15 -0
  5. package/agent/agent-prompt-catalog.mjs +4 -4
  6. package/agent/agent-prompts.mjs +20 -0
  7. package/agent/agent-supervisor.mjs +20 -0
  8. package/agent/bosun-skills.mjs +162 -832
  9. package/agent/fleet-coordinator.mjs +3 -16
  10. package/bosun-tui.mjs +107 -105
  11. package/cli.mjs +11 -0
  12. package/config/config.mjs +25 -0
  13. package/config/executor-config.mjs +124 -1
  14. package/infra/container-runner.mjs +565 -1
  15. package/infra/monitor.mjs +21 -0
  16. package/infra/tracing.mjs +775 -280
  17. package/infra/tui-bridge.mjs +13 -1
  18. package/kanban/kanban-adapter.mjs +131 -19
  19. package/kanban/vibe-kanban-wrapper.mjs +0 -0
  20. package/lib/repo-map.mjs +114 -3
  21. package/package.json +13 -5
  22. package/server/ui-server.mjs +151 -48
  23. package/shell/codex-config.mjs +33 -8
  24. package/shell/opencode-providers.mjs +41 -3
  25. package/task/task-archiver.mjs +18 -6
  26. package/task/task-attachments.mjs +14 -10
  27. package/task/task-cli.mjs +24 -4
  28. package/task/task-executor.mjs +110 -61
  29. package/task/task-store.mjs +194 -37
  30. package/telegram/get-telegram-chat-id.mjs +0 -0
  31. package/telegram/telegram-bot.mjs +4 -1
  32. package/telegram/telegram-sentinel.mjs +0 -0
  33. package/tools/import-check.mjs +234 -0
  34. package/tools/prompt-lint.mjs +161 -0
  35. package/tools/syntax-check.mjs +29 -0
  36. package/tools/test-shared-state-integration.mjs +2 -9
  37. package/tui/app.mjs +158 -182
  38. package/tui/components/status-header.mjs +178 -75
  39. package/tui/lib/fuzzy-score.mjs +61 -0
  40. package/tui/lib/header-config.mjs +68 -0
  41. package/tui/lib/navigation.mjs +15 -0
  42. package/tui/lib/sparkline.mjs +38 -0
  43. package/tui/lib/ws-bridge.mjs +152 -29
  44. package/tui/screens/agents-screen-helpers.mjs +1 -1
  45. package/tui/screens/agents.mjs +153 -8
  46. package/tui/screens/tasks-screen-helpers.mjs +78 -0
  47. package/tui/screens/tasks.mjs +1 -48
  48. package/ui/app.js +8 -5
  49. package/ui/components/chat-view.js.bak +1 -0
  50. package/ui/components/kanban-board.js +65 -3
  51. package/ui/components/session-list.js +18 -32
  52. package/ui/demo-defaults.js +200 -75
  53. package/ui/demo.html +10 -0
  54. package/ui/modules/api.js +109 -34
  55. package/ui/modules/session-api.js +100 -0
  56. package/ui/modules/state.js +160 -53
  57. package/ui/tabs/infra.js.bak +1 -0
  58. package/ui/tabs/workflow-canvas-utils.mjs +555 -13
  59. package/ui/tabs/workflows.js +810 -87
  60. package/ui/tui/App.js +298 -0
  61. package/ui/tui/TasksScreen.js +564 -0
  62. package/ui/tui/constants.js +55 -0
  63. package/ui/tui/tasks-screen-helpers.js +301 -0
  64. package/ui/tui/useTasks.js +61 -0
  65. package/ui/tui/useWebSocket.js +166 -0
  66. package/ui/tui/useWorkflows.js +30 -0
  67. package/voice/vision-session-state.mjs +317 -2
  68. package/voice/voice-action-dispatcher.mjs +184 -9
  69. package/voice/voice-relay.mjs +33 -0
  70. package/workflow/execution-ledger.mjs +534 -3
  71. package/workflow/workflow-cli.mjs +45 -1
  72. package/workflow/workflow-engine.mjs +859 -90
  73. package/workflow/workflow-nodes/custom-loader.mjs +259 -56
  74. package/workflow/workflow-nodes.mjs +1061 -159
  75. package/workflow-templates/agents.mjs +3 -0
  76. package/workflow-templates/github.mjs +70 -47
  77. package/workflow-templates/planning.mjs +7 -0
  78. package/workflow-templates/sub-workflows.mjs +5 -0
  79. package/workflow-templates/task-execution.mjs +3 -0
  80. package/workspace/command-diagnostics.mjs +1 -1
  81. package/workspace/context-cache.mjs +182 -9
  82. package/workspace/shared-workspace-cli.mjs +0 -0
package/.env.example CHANGED
@@ -802,6 +802,11 @@ VK_RECOVERY_PORT=54089
802
802
  # BOSUN_HOOKS_DISABLE_TASK_COMPLETE=false
803
803
  # BOSUN_HOOKS_DISABLE_HEALTH_CHECK=false
804
804
 
805
+ # ── OpenTelemetry Tracing & Metrics ───────────────────────────────────────────
806
+ # External orchestration-layer observability only; never affects agent context.
807
+ # BOSUN_OTEL_ENDPOINT=http://localhost:4318/v1/traces
808
+ # Configure tracing.sampleRate in bosun.config.json to tune sampling.
809
+
805
810
  # Force hooks to fire even for non-managed sessions (debug only):
806
811
  # BOSUN_HOOKS_FORCE=false
807
812
 
@@ -1175,3 +1180,7 @@ COPILOT_CLOUD_DISABLED=true
1175
1180
  # AGENT_STUCK_THRESHOLD_MS=300000
1176
1181
  # Alert if session costs more than $N (default: 1.0)
1177
1182
  # AGENT_COST_ANOMALY_THRESHOLD=1.0
1183
+
1184
+ # OpenTelemetry tracing (optional)
1185
+ # BOSUN_OTEL_ENDPOINT=http://localhost:4318/v1/traces
1186
+
@@ -746,12 +746,12 @@ export function getToolsPromptBlock(rootDir, opts = {}) {
746
746
  "## Custom Tools Library",
747
747
  "",
748
748
  discoveryMode
749
- ? "Only eagerly-loaded tools are listed below. Use the MCP discovery tools to find the rest at runtime."
750
- : "The following reusable helper scripts are available. Run them via",
749
+ ? "- Eager tools only below. Discover the rest at runtime."
750
+ : "- Run tools via `node <tool>.mjs`, `bash <tool>.sh`, or `python3 <tool>.py`.",
751
751
  discoveryMode
752
- ? "Use `search`, then `get_schema`, then `execute` for tools not listed here. Use `call_discovered_tool` only for simple direct calls."
753
- : "`node <tool>.mjs`, `bash <tool>.sh`, or `python3 <tool>.py`.",
754
- "Built-in tools live in `bosun/tools/`; workspace tools in `.bosun/tools/`.",
752
+ ? "- Use `search`, then `get_schema`, then `execute` for tools not listed here."
753
+ : "- Check this library before writing new helper code.",
754
+ "- Built-in tools: `bosun/tools/`; workspace tools: `.bosun/tools/`.",
755
755
  "",
756
756
  ];
757
757
 
@@ -791,16 +791,13 @@ export function getToolsPromptBlock(rootDir, opts = {}) {
791
791
  lines.push(
792
792
  "---",
793
793
  "",
794
- "**Reflect:** Before writing repetitive inline code, check if an existing",
795
- "custom tool covers the need. If you encounter a pattern that future agents",
796
- "(or yourself on retry) would benefit from having as a persistent script,",
797
- "save it to `.bosun/tools/` and register it via the Bosun SDK so the whole",
798
- "team benefits. Good candidates: analysis helpers, test generators, codemods,",
799
- "build/lint wrappers that differ from what `npm run *` provides.",
794
+ "Reflect:",
795
+ "- Check existing tools before writing new helpers.",
796
+ "- Promote repeated analysis, test, build, transform, or search logic into `.bosun/tools/`.",
797
+ "- Skip one-off scripts.",
800
798
  "",
801
799
  );
802
800
  }
803
-
804
801
  return lines.join("\n");
805
802
  }
806
803
 
@@ -931,3 +928,4 @@ export function getAffinityTools(rootDir, opts = {}) {
931
928
  .slice(0, limit)
932
929
  .map((s) => s.tool);
933
930
  }
931
+
@@ -25,6 +25,7 @@ import {
25
25
  reduceRetryQueue,
26
26
  snapshotRetryQueue,
27
27
  } from "./retry-queue.mjs";
28
+ import { addSpanEvent, recordAgentError, recordIntervention } from "../infra/tracing.mjs";
28
29
 
29
30
  const TAG = "[agent-event-bus]";
30
31
 
@@ -232,6 +233,14 @@ export class AgentEventBus {
232
233
  emit(type, taskId, payload = {}, opts = {}) {
233
234
  const ts = Date.now();
234
235
  const event = { type, taskId, payload, ts };
236
+ addSpanEvent(type, { "bosun.task.id": taskId, ...payload });
237
+ if (type === AGENT_EVENT.AGENT_ERROR) {
238
+ recordAgentError(payload?.errorType || payload?.classification || "agent_error", {
239
+ "bosun.task.id": taskId,
240
+ "bosun.executor": payload?.executor,
241
+ "bosun.agent.sdk": payload?.sdk,
242
+ });
243
+ }
235
244
 
236
245
  // ── Dedup
237
246
  const key = `${type}:${taskId}`;
@@ -1092,3 +1101,4 @@ export function createAgentEventBus(options) {
1092
1101
  return new AgentEventBus(options);
1093
1102
  }
1094
1103
 
1104
+
@@ -791,6 +791,9 @@ function buildCodexSdkOptions(envInput = process.env) {
791
791
  wire_api: "responses",
792
792
  },
793
793
  },
794
+ features: {
795
+ remote_models: false,
796
+ },
794
797
  ...(azureModel ? { model: azureModel } : {}),
795
798
  },
796
799
  };
@@ -1222,6 +1225,7 @@ async function launchCodexThread(prompt, cwd, timeoutMs, extra = {}) {
1222
1225
  codexOpts.config = {
1223
1226
  ...(codexOpts.config || {}),
1224
1227
  features: {
1228
+ ...(codexOpts.config?.features || {}),
1225
1229
  child_agents_md: true,
1226
1230
  multi_agent: true,
1227
1231
  memories: true,
@@ -3158,6 +3162,17 @@ async function resumeCodexThread(threadId, prompt, cwd, timeoutMs, extra = {}) {
3158
3162
  codexOpts.env = { ...(codexOpts.env || {}), CODEX_MODEL: modelOverride };
3159
3163
  codexOpts.config = { ...(codexOpts.config || {}), model: modelOverride };
3160
3164
  }
3165
+ codexOpts.config = {
3166
+ ...(codexOpts.config || {}),
3167
+ features: {
3168
+ ...(codexOpts.config?.features || {}),
3169
+ child_agents_md: true,
3170
+ multi_agent: true,
3171
+ memories: true,
3172
+ undo: true,
3173
+ steer: true,
3174
+ },
3175
+ };
3161
3176
  const codex = new CodexClass(codexOpts);
3162
3177
 
3163
3178
  let thread;
@@ -909,7 +909,7 @@ Examples:
909
909
  - For numbers, say them naturally: "You have 12 tasks in the backlog."
910
910
  - When tasks or agents are busy, keep the user informed.
911
911
  - For long outputs (code, logs), summarize the key points vocally.
912
- - When delegating to an agent, let the user know: "I'm sending that to Codex now."
912
+ - When delegating to an agent, say: "Sending that to Codex now."
913
913
 
914
914
  ## Error Handling
915
915
 
@@ -938,7 +938,7 @@ Be concise. Lead with answers. Summarize long outputs.
938
938
 
939
939
  customToolReflect: `## Reflect: Custom Tool Extraction
940
940
 
941
- Before closing this task, reflect on the work you just completed:
941
+ Before closing the task, check for reusable tooling:
942
942
 
943
943
  1. **Did you write any utility code (≥ 10 lines) that you'd write again?**
944
944
  If yes — extract it into a persistent custom tool in \`.bosun/tools/\`.
@@ -948,7 +948,7 @@ Before closing this task, reflect on the work you just completed:
948
948
  If yes — package it as a custom tool so future agents skip the re-derivation.
949
949
 
950
950
  3. **Did an existing custom tool help you?**
951
- Consider whether it should be promoted to global scope (\`promoteToGlobal\`).
951
+ Consider promoting it to global scope (\`promoteToGlobal\`).
952
952
 
953
953
  4. **What category does the extracted logic fall into?**
954
954
  analysis | testing | git | build | transform | search | validation | utility
@@ -962,7 +962,7 @@ registerCustomTool(rootDir, {
962
962
  });
963
963
  \`\`\`
964
964
 
965
- Only extract if the tool has clear reuse value. Skip one-off logic.
965
+ Extract only when reuse is clear. Skip one-off logic.
966
966
  `,
967
967
 
968
968
  customToolsContext: `{{CUSTOM_TOOLS_BLOCK}}
@@ -174,6 +174,26 @@ export function resolvePromptTemplate(template, values, fallback) {
174
174
  return rendered && rendered.trim() ? rendered : base;
175
175
  }
176
176
 
177
+ export async function buildCustomToolsContextPrompt(rootDir, opts = {}) {
178
+ const { getToolsPromptBlock, listCustomTools } = await import("./agent-custom-tools.mjs");
179
+ const registeredTools = listCustomTools(rootDir, {
180
+ includeBuiltins: false,
181
+ });
182
+ if (registeredTools.length === 0) return "";
183
+
184
+ const promptTemplate = DEFAULT_PROMPTS.customToolsContext || "{{CUSTOM_TOOLS_BLOCK}}";
185
+ const toolsBlock = getToolsPromptBlock(rootDir, opts);
186
+ if (!toolsBlock.trim()) return "";
187
+
188
+ return renderPromptTemplate(
189
+ promptTemplate,
190
+ {
191
+ CUSTOM_TOOLS_BLOCK: toolsBlock,
192
+ },
193
+ rootDir,
194
+ ).trim();
195
+ }
196
+
177
197
  export function ensureAgentPromptWorkspace(repoRoot) {
178
198
  const root = resolve(repoRoot || process.cwd());
179
199
  let workspaceDir = getDefaultPromptWorkspace(root);
@@ -29,6 +29,8 @@
29
29
  * @module agent-supervisor
30
30
  */
31
31
 
32
+ import { addSpanEvent, recordIntervention } from "../infra/tracing.mjs";
33
+
32
34
  const TAG = "[agent-supervisor]";
33
35
  const API_ERROR_CONTINUE_COOLDOWNS_MS = Object.freeze([
34
36
  3 * 60_000,
@@ -473,6 +475,12 @@ export class AgentSupervisor {
473
475
  }
474
476
 
475
477
  this._lastDecision.set(taskId, { situation, intervention, ts: Date.now() });
478
+ addSpanEvent("bosun.supervisor.assess", {
479
+ "bosun.task.id": taskId,
480
+ "bosun.health.score": healthScore,
481
+ "bosun.situation": situation,
482
+ "bosun.intervention.type": intervention,
483
+ });
476
484
 
477
485
  return { situation, healthScore, intervention, prompt, reason };
478
486
  }
@@ -489,6 +497,18 @@ export class AgentSupervisor {
489
497
  );
490
498
 
491
499
  try {
500
+ if (intervention !== INTERVENTION.NONE) {
501
+ recordIntervention(intervention, {
502
+ "bosun.task.id": taskId,
503
+ "bosun.situation": situation,
504
+ });
505
+ }
506
+ addSpanEvent("bosun.supervisor.intervention", {
507
+ "bosun.task.id": taskId,
508
+ "bosun.intervention.type": intervention,
509
+ "bosun.situation": situation,
510
+ "bosun.reason": reason,
511
+ });
492
512
  switch (intervention) {
493
513
  case INTERVENTION.NONE:
494
514
  break;