bosun 0.36.29 → 0.37.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 (47) hide show
  1. package/.env.example +2 -32
  2. package/agent-prompts.mjs +0 -91
  3. package/agent-tool-config.mjs +327 -0
  4. package/bosun.schema.json +1 -1
  5. package/config.mjs +6 -75
  6. package/copilot-shell.mjs +18 -1
  7. package/desktop/desktop-shortcuts.mjs +144 -27
  8. package/desktop/main.mjs +138 -16
  9. package/desktop/package.json +1 -2
  10. package/desktop/preload.mjs +10 -0
  11. package/fleet-coordinator.mjs +4 -16
  12. package/library-manager.mjs +18 -0
  13. package/maintenance.mjs +1 -2
  14. package/monitor.mjs +217 -3109
  15. package/package.json +5 -2
  16. package/primary-agent.mjs +47 -8
  17. package/session-tracker.mjs +5 -0
  18. package/setup-web-server.mjs +3 -3
  19. package/setup.mjs +3 -3
  20. package/task-cli.mjs +4 -38
  21. package/telegram-bot.mjs +41 -136
  22. package/ui/app.js +237 -29
  23. package/ui/components/agent-selector.js +26 -4
  24. package/ui/demo.html +72 -0
  25. package/ui/modules/settings-schema.js +1 -1
  26. package/ui/modules/vision-stream.js +196 -22
  27. package/ui/modules/voice-client-sdk.js +571 -60
  28. package/ui/modules/voice-client.js +501 -20
  29. package/ui/modules/voice-fallback.js +28 -6
  30. package/ui/modules/voice-overlay.js +1454 -500
  31. package/ui/setup.html +232 -24
  32. package/ui/styles/components.css +65 -0
  33. package/ui/styles/layout.css +4 -0
  34. package/ui/styles/sessions.css +24 -0
  35. package/ui/tabs/agents.js +125 -28
  36. package/ui/tabs/chat.js +233 -21
  37. package/ui/tabs/library.js +676 -11
  38. package/ui/tabs/settings.js +135 -92
  39. package/ui/tabs/tasks.js +5 -11
  40. package/ui/tabs/workflows.js +69 -0
  41. package/ui-server.mjs +583 -139
  42. package/vision-session-state.mjs +35 -0
  43. package/voice-action-dispatcher.mjs +244 -20
  44. package/voice-agents-sdk.mjs +2 -2
  45. package/voice-auth-manager.mjs +88 -25
  46. package/voice-relay.mjs +253 -19
  47. package/voice-tools.mjs +2064 -188
package/.env.example CHANGED
@@ -4,8 +4,8 @@
4
4
  # All variables are optional unless marked [REQUIRED].
5
5
  # Boolean flags use true/false (preferred). Legacy 1/0 is still accepted.
6
6
  # Profile guidance:
7
- # - Local development: DEVMODE=true, DEVMODE_MONITOR_MONITOR_ENABLED=true, *_TRANSPORT=sdk
8
- # - End-user stable: DEVMODE=false, DEVMODE_MONITOR_MONITOR_ENABLED=false, *_TRANSPORT=sdk
7
+ # - Local development: DEVMODE=true, *_TRANSPORT=sdk
8
+ # - End-user stable: DEVMODE=false, *_TRANSPORT=sdk
9
9
 
10
10
  # ─── Task Claims and Coordination ─────────────────────────────────────────────
11
11
  # Shared state manager enables distributed task coordination across multiple
@@ -222,10 +222,6 @@ VOICE_DELEGATE_EXECUTOR=codex-sdk
222
222
  # SENTINEL_RESTART_BACKOFF_SEC=5
223
223
  # After manual /stop, suppress auto-restart for this many minutes (default: 10)
224
224
  # SENTINEL_MANUAL_STOP_HOLD_MIN=10
225
- # In devmode, validate monitor-monitor freshness before restart decisions (default: 1)
226
- # SENTINEL_MONITOR_MONITOR_CHECK_ENABLED=true
227
- # Max acceptable monitor-monitor age in minutes (default: 20)
228
- # SENTINEL_MONITOR_MONITOR_MAX_AGE_MIN=20
229
225
 
230
226
  # ─── Notification Batching (RECOMMENDED) ─────────────────────────────────────
231
227
  # Batch notifications into periodic summaries instead of spamming individual messages
@@ -776,31 +772,6 @@ VK_RECOVERY_PORT=54089
776
772
  # scripts check for this and exit silently if not present, preventing
777
773
  # hooks from firing in standalone Copilot/Codex/Claude sessions.
778
774
 
779
- # ─── Devmode Monitor-Monitor (24/7 reliability guardian) ───────────────────
780
- # Prompt is injected directly from bosun source (no .github/agents file required).
781
- # Enabled by default in devmode source checkouts. Set to false to disable.
782
- # DEVMODE_MONITOR_MONITOR_ENABLED=true
783
- # Poll interval for monitor-monitor runs (milliseconds). Default: 300000 (5 min)
784
- # DEVMODE_MONITOR_MONITOR_INTERVAL_MS=300000
785
- # Status stream update interval (milliseconds). Default: 1800000 (30 min)
786
- # DEVMODE_MONITOR_MONITOR_STATUS_INTERVAL_MS=1800000
787
- # Per-run timeout before watchdog abort/failover (milliseconds).
788
- # Default is 21600000 (6h) for long-running reliability analysis sessions.
789
- # 30 minutes (1800000) is safe if you prefer faster failover on stuck runs.
790
- # Watchdog abort triggers at timeout+60s, then accelerated force-reset at +120s.
791
- # Set this explicitly to avoid inherited shell
792
- # defaults (for example DEVMODE_AUTO_CODE_FIX_TIMEOUT_MS=300000).
793
- # DEVMODE_MONITOR_MONITOR_TIMEOUT_MS=1800000
794
- # Optional timeout bounds (applied only when set):
795
- # DEVMODE_MONITOR_MONITOR_TIMEOUT_MIN_MS=600000
796
- # DEVMODE_MONITOR_MONITOR_TIMEOUT_MAX_MS=7200000
797
- # Optional override for Claude tool access (comma-separated)
798
- # DEVMODE_MONITOR_MONITOR_CLAUDE_ALLOWED_TOOLS=Read,Write,Edit,Grep,Glob,Bash,WebSearch,Task,Skill
799
- # Legacy alias: DEVMODE_AUTO_CODE_FIX=true also enables this subsystem.
800
- # Legacy timeout fallback: if DEVMODE_MONITOR_MONITOR_TIMEOUT_MS is unset and
801
- # DEVMODE_AUTO_CODE_FIX_TIMEOUT_MS is set, monitor-monitor will use it (and
802
- # still apply DEVMODE_MONITOR_MONITOR_TIMEOUT_MIN_MS/MAX_MS bounds if provided).
803
-
804
775
  # ─── Copilot SDK (Primary Agent) ─────────────────────────────────────────────
805
776
  # Requires GitHub Copilot CLI installed and authenticated.
806
777
  # Set to true to disable Copilot SDK (primary agent) usage.
@@ -1046,7 +1017,6 @@ COPILOT_CLOUD_DISABLED=true
1046
1017
  # Files in that folder are loaded automatically and are intended for per-project customization.
1047
1018
  # You can also override any prompt path explicitly with env vars:
1048
1019
  # BOSUN_PROMPT_PLANNER=.bosun/agents/task-planner.md
1049
- # BOSUN_PROMPT_MONITOR_MONITOR=.bosun/agents/monitor-monitor.md
1050
1020
  # BOSUN_PROMPT_TASK_EXECUTOR=.bosun/agents/task-executor.md
1051
1021
  # BOSUN_PROMPT_REVIEWER=.bosun/agents/reviewer.md
1052
1022
  # BOSUN_PROMPT_SDK_CONFLICT_RESOLVER=.bosun/agents/sdk-conflict-resolver.md
package/agent-prompts.mjs CHANGED
@@ -17,16 +17,6 @@ const PROMPT_DEFS = [
17
17
  filename: "orchestrator.md",
18
18
  description: "Primary task execution prompt for autonomous task agents.",
19
19
  },
20
- {
21
- key: "planner",
22
- filename: "task-planner.md",
23
- description: "Backlog planning prompt used by task planner runs.",
24
- },
25
- {
26
- key: "monitorMonitor",
27
- filename: "monitor-monitor.md",
28
- description: "Long-running reliability monitor prompt used in devmode.",
29
- },
30
20
  {
31
21
  key: "taskExecutor",
32
22
  filename: "task-executor.md",
@@ -199,68 +189,6 @@ apply patterns discovered by previous agents:
199
189
  After completing a task, if you discovered a non-obvious pattern, workaround, or
200
190
  domain-specific fact, write or update a skill file at \`.bosun/skills/<module>.md\`
201
191
  so the next agent benefits from your investigation.
202
- `,
203
- planner: `# Codex-Task-Planner Agent
204
-
205
- You generate production-grade backlog tasks for autonomous executors.
206
-
207
- ## Mission
208
-
209
- 1. Analyze current repo and delivery state.
210
- 2. Identify highest-value next work.
211
- 3. Create concrete, execution-ready tasks.
212
-
213
- ## Requirements
214
-
215
- - Avoid vague tasks and duplicate work.
216
- - Balance reliability fixes, feature delivery, and debt reduction.
217
- - Every task includes implementation steps, acceptance criteria, and verification plan.
218
- - Every task title starts with one size label: [xs], [s], [m], [l], [xl], [xxl].
219
- - Prefer task sets that can run in parallel with low file overlap.
220
- - Do not call any kanban API, CLI, or external service to create tasks.
221
- The workflow will automatically materialize your output into kanban tasks.
222
- - Output must be machine-parseable JSON — see Output Contract below.
223
- - Task objects must be valid for Bosun backlog creation with fields:
224
- \'title\', \'description\', \'implementation_steps\', \'acceptance_criteria\',
225
- \'verification\', optional \'base_branch\'.
226
- - Do not emit empty or placeholder tasks. Every task must be actionable and execution-ready.
227
-
228
- ## Output Contract (MANDATORY — STRICT)
229
-
230
- Your ENTIRE response must be a single fenced JSON block. Do NOT include any
231
- text, commentary, explanations, or markdown before or after the JSON block.
232
- The downstream parser extracts JSON from fenced blocks — any deviation causes
233
- task creation to hard-fail.
234
-
235
- Return exactly this shape:
236
-
237
- \`\`\`json
238
- {
239
- "tasks": [
240
- {
241
- "title": "[m] feat(veid): example task title",
242
- "description": "Problem statement and scope",
243
- "implementation_steps": ["step 1", "step 2"],
244
- "acceptance_criteria": ["criterion 1", "criterion 2"],
245
- "verification": ["test/check 1", "test/check 2"],
246
- "base_branch": "origin/veid"
247
- }
248
- ]
249
- }
250
- \`\`\`
251
-
252
- Rules:
253
- - The \`tasks\` array MUST contain at least the requested task count.
254
- - Do NOT output partial JSON, truncated arrays, or commentary mixed with JSON.
255
- - Keep titles unique and specific.
256
- - Keep file overlap low across tasks to maximize parallel execution.
257
- - Descriptions must include concrete implementation details, not generic intent text.
258
- - Include verification commands/checks that a worker can run without additional planning.
259
- - **Module branch routing:** When the task title follows conventional commit format
260
- \`feat(module):\` or \`fix(module):\`, set \`base_branch\` to \`origin/<module>\`.
261
- This routes the task to the module's dedicated branch for parallel, isolated development.
262
- Examples: \`feat(veid):\` → \`"base_branch": "origin/veid"\`, \`fix(market):\` → \`"base_branch": "origin/market"\`.
263
- Omit \`base_branch\` for cross-cutting tasks that span multiple modules.
264
192
  `,
265
193
  taskManager: `# Bosun Task Manager Agent
266
194
 
@@ -305,8 +233,6 @@ bosun task stats --json
305
233
  # Bulk import from JSON file
306
234
  bosun task import ./backlog.json
307
235
 
308
- # Trigger AI task planner
309
- bosun task plan --count 5 --reason "Sprint planning"
310
236
  \`\`\`
311
237
 
312
238
  ### 2. REST API (port 18432 — always available when bosun daemon runs)
@@ -440,23 +366,6 @@ draft → todo → inprogress → inreview → done
440
366
  7. **Module branch routing** — When a task title follows conventional commit format
441
367
  \`feat(module):\` or \`fix(module):\`, set \`baseBranch\` to \`origin/<module>\` to route the task
442
368
  to the module's dedicated branch for parallel, isolated development.
443
- `,
444
- monitorMonitor: `# Bosun-Monitor Agent
445
-
446
- You are the always-on reliability guardian for bosun in devmode.
447
-
448
- ## Core Role
449
-
450
- - Monitor logs, failures, and agent/orchestrator behavior continuously.
451
- - Immediately fix reliability regressions and execution blockers.
452
- - Improve prompt/tool/executor reliability to reduce failure loops.
453
- - Only when runtime is healthy, perform code-analysis improvements.
454
-
455
- ## Constraints
456
-
457
- - Operate only in devmode.
458
- - Do not commit/push/initiate PR lifecycle changes in this context.
459
- - Apply focused fixes, run focused validation, and keep monitoring.
460
369
  `,
461
370
  taskExecutor: `# {{TASK_ID}} — {{TASK_TITLE}}
462
371
 
@@ -0,0 +1,327 @@
1
+ /**
2
+ * agent-tool-config.mjs — Per-Agent Tool Configuration Store
3
+ *
4
+ * Manages which tools and MCP servers are enabled for each agent profile.
5
+ * Persisted as `.bosun/agent-tools.json` alongside the library manifest.
6
+ *
7
+ * Schema:
8
+ * {
9
+ * "agents": {
10
+ * "<agentId>": {
11
+ * "enabledTools": ["tool1", "tool2"] | null, // null = all tools
12
+ * "enabledMcpServers": ["github", "context7"], // enabled MCP server IDs
13
+ * "disabledBuiltinTools": ["tool3"], // explicitly disabled builtins
14
+ * "updatedAt": "2026-01-01T00:00:00.000Z"
15
+ * }
16
+ * },
17
+ * "defaults": {
18
+ * "builtinTools": [...], // default tool list for all agents
19
+ * "updatedAt": "..."
20
+ * }
21
+ * }
22
+ *
23
+ * EXPORTS:
24
+ * DEFAULT_BUILTIN_TOOLS — list of default built-in tools for voice/agents
25
+ * loadToolConfig(rootDir) — load the full config
26
+ * saveToolConfig(rootDir, cfg) — save the full config
27
+ * getAgentToolConfig(rootDir, agentId) — get config for one agent
28
+ * setAgentToolConfig(rootDir, agentId, config) — update config for one agent
29
+ * getEffectiveTools(rootDir, agentId) — compute final enabled tools list
30
+ * listAvailableTools(rootDir) — list all available tools (builtin + MCP)
31
+ */
32
+
33
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
34
+ import { resolve } from "node:path";
35
+ import { homedir } from "node:os";
36
+
37
+ // ── Constants ─────────────────────────────────────────────────────────────────
38
+
39
+ const TAG = "[agent-tool-config]";
40
+ const CONFIG_FILE = "agent-tools.json";
41
+
42
+ function getBosunHome() {
43
+ return (
44
+ process.env.BOSUN_HOME ||
45
+ process.env.BOSUN_DIR ||
46
+ resolve(homedir(), ".bosun")
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Default built-in tools available to all voice agents and executors.
52
+ * Maps to common capabilities that voice/agent sessions can invoke.
53
+ */
54
+ export const DEFAULT_BUILTIN_TOOLS = Object.freeze([
55
+ {
56
+ id: "search-files",
57
+ name: "Search Files",
58
+ description: "Search for files in the workspace by name or pattern",
59
+ category: "Built-In",
60
+ icon: ":search:",
61
+ default: true,
62
+ },
63
+ {
64
+ id: "read-file",
65
+ name: "Read File",
66
+ description: "Read contents of a file in the workspace",
67
+ category: "Built-In",
68
+ icon: ":file:",
69
+ default: true,
70
+ },
71
+ {
72
+ id: "edit-file",
73
+ name: "Edit File",
74
+ description: "Create or edit files in the workspace",
75
+ category: "Built-In",
76
+ icon: ":edit:",
77
+ default: true,
78
+ },
79
+ {
80
+ id: "run-command",
81
+ name: "Run Terminal Command",
82
+ description: "Execute shell commands in a terminal",
83
+ category: "Built-In",
84
+ icon: ":terminal:",
85
+ default: true,
86
+ },
87
+ {
88
+ id: "web-search",
89
+ name: "Web Search",
90
+ description: "Search the web for information",
91
+ category: "Built-In",
92
+ icon: ":globe:",
93
+ default: true,
94
+ },
95
+ {
96
+ id: "code-search",
97
+ name: "Semantic Code Search",
98
+ description: "Search codebase semantically for relevant code",
99
+ category: "Built-In",
100
+ icon: ":cpu:",
101
+ default: true,
102
+ },
103
+ {
104
+ id: "git-operations",
105
+ name: "Git Operations",
106
+ description: "Run git commands (commit, push, branch, etc.)",
107
+ category: "Built-In",
108
+ icon: ":git:",
109
+ default: true,
110
+ },
111
+ {
112
+ id: "create-task",
113
+ name: "Create Task",
114
+ description: "Create new tasks and issues",
115
+ category: "Built-In",
116
+ icon: ":check:",
117
+ default: true,
118
+ },
119
+ {
120
+ id: "delegate-task",
121
+ name: "Delegate to Agent",
122
+ description: "Delegate work to another agent executor",
123
+ category: "Built-In",
124
+ icon: ":bot:",
125
+ default: true,
126
+ },
127
+ {
128
+ id: "fetch-url",
129
+ name: "Fetch URL",
130
+ description: "Fetch content from a URL and convert for LLM usage",
131
+ category: "Built-In",
132
+ icon: ":link:",
133
+ default: true,
134
+ },
135
+ {
136
+ id: "list-directory",
137
+ name: "List Directory",
138
+ description: "List contents of a directory in the workspace",
139
+ category: "Built-In",
140
+ icon: ":folder:",
141
+ default: true,
142
+ },
143
+ {
144
+ id: "grep-search",
145
+ name: "Text Search (Grep)",
146
+ description: "Search for exact text or regex patterns in files",
147
+ category: "Built-In",
148
+ icon: ":search:",
149
+ default: true,
150
+ },
151
+ {
152
+ id: "task-management",
153
+ name: "Task Management",
154
+ description: "Track and manage todo items and task status",
155
+ category: "Built-In",
156
+ icon: ":clipboard:",
157
+ default: true,
158
+ },
159
+ {
160
+ id: "notifications",
161
+ name: "Send Notifications",
162
+ description: "Send notifications via Telegram, webhook, etc.",
163
+ category: "Built-In",
164
+ icon: ":bell:",
165
+ default: false,
166
+ },
167
+ {
168
+ id: "vision-analysis",
169
+ name: "Vision Analysis",
170
+ description: "Analyze images and screenshots",
171
+ category: "Built-In",
172
+ icon: ":eye:",
173
+ default: true,
174
+ },
175
+ ]);
176
+
177
+ // ── Config File I/O ───────────────────────────────────────────────────────────
178
+
179
+ function getConfigPath(rootDir) {
180
+ return resolve(rootDir || getBosunHome(), ".bosun", CONFIG_FILE);
181
+ }
182
+
183
+ /**
184
+ * Load the agent tool configuration.
185
+ * @param {string} [rootDir]
186
+ * @returns {{ agents: Object, defaults: Object }}
187
+ */
188
+ export function loadToolConfig(rootDir) {
189
+ const configPath = getConfigPath(rootDir);
190
+ if (!existsSync(configPath)) {
191
+ return {
192
+ agents: {},
193
+ defaults: {
194
+ builtinTools: DEFAULT_BUILTIN_TOOLS.filter((t) => t.default).map((t) => t.id),
195
+ updatedAt: new Date().toISOString(),
196
+ },
197
+ };
198
+ }
199
+ try {
200
+ const raw = readFileSync(configPath, "utf8");
201
+ const parsed = JSON.parse(raw);
202
+ return {
203
+ agents: parsed.agents || {},
204
+ defaults: parsed.defaults || {
205
+ builtinTools: DEFAULT_BUILTIN_TOOLS.filter((t) => t.default).map((t) => t.id),
206
+ updatedAt: new Date().toISOString(),
207
+ },
208
+ };
209
+ } catch {
210
+ return {
211
+ agents: {},
212
+ defaults: {
213
+ builtinTools: DEFAULT_BUILTIN_TOOLS.filter((t) => t.default).map((t) => t.id),
214
+ updatedAt: new Date().toISOString(),
215
+ },
216
+ };
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Save the full tool configuration.
222
+ * @param {string} rootDir
223
+ * @param {{ agents: Object, defaults: Object }} config
224
+ */
225
+ export function saveToolConfig(rootDir, config) {
226
+ const configPath = getConfigPath(rootDir);
227
+ const dir = resolve(configPath, "..");
228
+ mkdirSync(dir, { recursive: true });
229
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
230
+ }
231
+
232
+ /**
233
+ * Get tool configuration for a specific agent.
234
+ * @param {string} rootDir
235
+ * @param {string} agentId
236
+ * @returns {{ enabledTools: string[]|null, enabledMcpServers: string[], disabledBuiltinTools: string[] }}
237
+ */
238
+ export function getAgentToolConfig(rootDir, agentId) {
239
+ const config = loadToolConfig(rootDir);
240
+ const agentConfig = config.agents[agentId];
241
+ if (!agentConfig) {
242
+ return {
243
+ enabledTools: null,
244
+ enabledMcpServers: [],
245
+ disabledBuiltinTools: [],
246
+ };
247
+ }
248
+ return {
249
+ enabledTools: agentConfig.enabledTools ?? null,
250
+ enabledMcpServers: agentConfig.enabledMcpServers || [],
251
+ disabledBuiltinTools: agentConfig.disabledBuiltinTools || [],
252
+ };
253
+ }
254
+
255
+ /**
256
+ * Update tool configuration for a specific agent.
257
+ * @param {string} rootDir
258
+ * @param {string} agentId
259
+ * @param {{ enabledTools?: string[]|null, enabledMcpServers?: string[], disabledBuiltinTools?: string[] }} update
260
+ * @returns {{ ok: boolean }}
261
+ */
262
+ export function setAgentToolConfig(rootDir, agentId, update) {
263
+ const config = loadToolConfig(rootDir);
264
+ const existing = config.agents[agentId] || {};
265
+ config.agents[agentId] = {
266
+ ...existing,
267
+ enabledTools: update.enabledTools !== undefined ? update.enabledTools : (existing.enabledTools ?? null),
268
+ enabledMcpServers: update.enabledMcpServers !== undefined ? update.enabledMcpServers : (existing.enabledMcpServers || []),
269
+ disabledBuiltinTools: update.disabledBuiltinTools !== undefined ? update.disabledBuiltinTools : (existing.disabledBuiltinTools || []),
270
+ updatedAt: new Date().toISOString(),
271
+ };
272
+ saveToolConfig(rootDir, config);
273
+ return { ok: true };
274
+ }
275
+
276
+ /**
277
+ * Compute the effective enabled tools for an agent.
278
+ * Merges builtin defaults with agent-specific overrides and MCP servers.
279
+ *
280
+ * @param {string} rootDir
281
+ * @param {string} agentId
282
+ * @returns {{ builtinTools: Array<{ id: string, name: string, enabled: boolean }>, mcpServers: string[] }}
283
+ */
284
+ export function getEffectiveTools(rootDir, agentId) {
285
+ const config = loadToolConfig(rootDir);
286
+ const agentConfig = config.agents[agentId] || {};
287
+ const disabledSet = new Set(agentConfig.disabledBuiltinTools || []);
288
+ const defaultIds = new Set(config.defaults?.builtinTools || DEFAULT_BUILTIN_TOOLS.filter((t) => t.default).map((t) => t.id));
289
+
290
+ const builtinTools = DEFAULT_BUILTIN_TOOLS.map((tool) => ({
291
+ ...tool,
292
+ enabled: !disabledSet.has(tool.id) && (agentConfig.enabledTools === null || agentConfig.enabledTools === undefined
293
+ ? defaultIds.has(tool.id)
294
+ : agentConfig.enabledTools.includes(tool.id)),
295
+ }));
296
+
297
+ return {
298
+ builtinTools,
299
+ mcpServers: agentConfig.enabledMcpServers || [],
300
+ };
301
+ }
302
+
303
+ /**
304
+ * List all available tools (builtin + installed MCP servers).
305
+ * @param {string} rootDir
306
+ * @returns {{ builtinTools: Array<Object>, mcpServers: Array<Object> }}
307
+ */
308
+ export async function listAvailableTools(rootDir) {
309
+ let mcpServers = [];
310
+ try {
311
+ const { listInstalledMcpServers } = await import("./mcp-registry.mjs");
312
+ mcpServers = await listInstalledMcpServers(rootDir);
313
+ } catch {
314
+ // MCP registry not available
315
+ }
316
+
317
+ return {
318
+ builtinTools: [...DEFAULT_BUILTIN_TOOLS],
319
+ mcpServers: mcpServers.map((s) => ({
320
+ id: s.id,
321
+ name: s.name,
322
+ description: s.description || "",
323
+ tags: s.tags || [],
324
+ transport: s.meta?.transport || "stdio",
325
+ })),
326
+ };
327
+ }
package/bosun.schema.json CHANGED
@@ -281,7 +281,7 @@
281
281
  "turnDetection": {
282
282
  "type": "string",
283
283
  "enum": ["server_vad", "semantic_vad", "none"],
284
- "default": "server_vad",
284
+ "default": "semantic_vad",
285
285
  "description": "Turn detection mode for voice activity detection"
286
286
  },
287
287
  "instructions": {
package/config.mjs CHANGED
@@ -506,7 +506,9 @@ function normalizeExecutorModels(executor, models, variant = "DEFAULT") {
506
506
  );
507
507
  return inferred.length > 0 ? inferred : [...known];
508
508
  }
509
- return input.filter((model) => known.has(model));
509
+ // Preserve custom/deployment slugs in addition to known models so user-provided
510
+ // model routing survives normalization (for example Azure deployment names).
511
+ return [...new Set(input.filter(Boolean))];
510
512
  }
511
513
 
512
514
  function normalizeExecutorEntry(entry, index = 0, total = 1) {
@@ -540,49 +542,8 @@ function normalizeExecutorEntry(entry, index = 0, total = 1) {
540
542
  };
541
543
  }
542
544
 
543
- function buildDefaultTriggerTemplates({
544
- plannerMode,
545
- plannerPerCapitaThreshold,
546
- plannerIdleSlotThreshold,
547
- plannerDedupHours,
548
- } = {}) {
545
+ function buildDefaultTriggerTemplates() {
549
546
  return [
550
- {
551
- id: "task-planner",
552
- name: "Task Planner",
553
- description: "Create planning tasks when backlog/slot metrics indicate replenishment.",
554
- enabled: false,
555
- action: "task-planner",
556
- trigger: {
557
- anyOf: [
558
- {
559
- kind: "metric",
560
- metric: "backlogPerCapita",
561
- operator: "lt",
562
- value: plannerPerCapitaThreshold,
563
- },
564
- {
565
- kind: "metric",
566
- metric: "idleSlots",
567
- operator: "gte",
568
- value: plannerIdleSlotThreshold,
569
- },
570
- {
571
- kind: "metric",
572
- metric: "backlogRemaining",
573
- operator: "eq",
574
- value: 0,
575
- },
576
- ],
577
- },
578
- minIntervalMinutes: Math.max(1, Number(plannerDedupHours || 6) * 60),
579
- config: {
580
- plannerMode,
581
- defaultTaskCount: Number(process.env.TASK_PLANNER_DEFAULT_COUNT || "30"),
582
- executor: "auto",
583
- model: "auto",
584
- },
585
- },
586
547
  {
587
548
  id: "daily-review-digest",
588
549
  name: "Daily Review Digest",
@@ -1990,32 +1951,8 @@ export function loadConfig(argv = process.argv, options = {}) {
1990
1951
  "summary"
1991
1952
  ).toLowerCase();
1992
1953
 
1993
- // ── Task Planner ─────────────────────────────────────────
1994
- // Mode: "codex-sdk" (default) runs Codex directly, "kanban" creates a VK
1995
- // task for a real agent to plan, "disabled" turns off the planner entirely.
1996
- const plannerMode = (
1997
- process.env.TASK_PLANNER_MODE ||
1998
- configData.plannerMode ||
1999
- (mode === "generic" ? "disabled" : "codex-sdk")
2000
- ).toLowerCase();
2001
- const plannerPerCapitaThreshold = Number(
2002
- process.env.TASK_PLANNER_PER_CAPITA_THRESHOLD || "1",
2003
- );
2004
- const plannerIdleSlotThreshold = Number(
2005
- process.env.TASK_PLANNER_IDLE_SLOT_THRESHOLD || "1",
2006
- );
2007
- const plannerDedupHours = Number(process.env.TASK_PLANNER_DEDUP_HOURS || "6");
2008
- const plannerDedupMs = Number.isFinite(plannerDedupHours)
2009
- ? plannerDedupHours * 60 * 60 * 1000
2010
- : 24 * 60 * 60 * 1000;
2011
-
2012
1954
  const triggerSystemDefaults = Object.freeze({
2013
- templates: buildDefaultTriggerTemplates({
2014
- plannerMode,
2015
- plannerPerCapitaThreshold,
2016
- plannerIdleSlotThreshold,
2017
- plannerDedupHours,
2018
- }),
1955
+ templates: buildDefaultTriggerTemplates(),
2019
1956
  defaults: Object.freeze({
2020
1957
  executor: "auto",
2021
1958
  model: "auto",
@@ -2290,12 +2227,6 @@ export function loadConfig(argv = process.argv, options = {}) {
2290
2227
  telegramCommandEnabled,
2291
2228
  telegramVerbosity,
2292
2229
 
2293
- // Task Planner
2294
- plannerMode,
2295
- plannerPerCapitaThreshold,
2296
- plannerIdleSlotThreshold,
2297
- plannerDedupHours,
2298
- plannerDedupMs,
2299
2230
  triggerSystem,
2300
2231
 
2301
2232
  // GitHub Reconciler
@@ -2349,7 +2280,7 @@ export function loadConfig(argv = process.argv, options = {}) {
2349
2280
  configData.trustedCreators ||
2350
2281
  process.env.BOSUN_TRUSTED_CREATORS?.split(",") ||
2351
2282
  [],
2352
- // Enforce all new tasks go to backlog unless planner config allows auto-push
2283
+ // Enforce all new tasks go to backlog
2353
2284
  enforceBacklog:
2354
2285
  typeof configData.enforceBacklog === "boolean"
2355
2286
  ? configData.enforceBacklog
package/copilot-shell.mjs CHANGED
@@ -469,9 +469,26 @@ async function ensureClientStarted() {
469
469
  const modeLabel = clientOptions.cliUrl ? "remote" : "local (stdio)";
470
470
  console.log(`[copilot-shell] starting client in ${modeLabel} mode`);
471
471
 
472
+ const START_TIMEOUT_MS =
473
+ Number(process.env.COPILOT_START_TIMEOUT_MS) || 20_000;
474
+
472
475
  await withSanitizedOpenAiEnv(async () => {
473
476
  copilotClient = new Cls(clientOptions);
474
- await copilotClient.start();
477
+ await Promise.race([
478
+ copilotClient.start(),
479
+ new Promise((_, reject) =>
480
+ setTimeout(
481
+ () =>
482
+ reject(
483
+ new Error(
484
+ `Copilot CLI failed to start within ${START_TIMEOUT_MS / 1000}s — ` +
485
+ `verify COPILOT_CLI_PATH or run \`gh auth login\``,
486
+ ),
487
+ ),
488
+ START_TIMEOUT_MS,
489
+ ),
490
+ ),
491
+ ]);
475
492
  });
476
493
  clientStarted = true;
477
494
  console.log("[copilot-shell] client started");