bosun 0.36.0 → 0.36.2
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.
- package/.env.example +98 -16
- package/README.md +27 -0
- package/agent-event-bus.mjs +5 -5
- package/agent-pool.mjs +129 -12
- package/agent-prompts.mjs +7 -1
- package/agent-sdk.mjs +13 -2
- package/agent-supervisor.mjs +2 -2
- package/agent-work-report.mjs +1 -1
- package/anomaly-detector.mjs +6 -6
- package/autofix.mjs +15 -15
- package/bosun-skills.mjs +4 -4
- package/bosun.schema.json +160 -4
- package/claude-shell.mjs +11 -11
- package/cli.mjs +21 -21
- package/codex-config.mjs +19 -19
- package/codex-shell.mjs +180 -29
- package/config-doctor.mjs +27 -2
- package/config.mjs +60 -7
- package/copilot-shell.mjs +4 -4
- package/error-detector.mjs +1 -1
- package/fleet-coordinator.mjs +2 -2
- package/gemini-shell.mjs +692 -0
- package/github-oauth-portal.mjs +1 -1
- package/github-reconciler.mjs +2 -2
- package/kanban-adapter.mjs +741 -168
- package/merge-strategy.mjs +25 -25
- package/monitor.mjs +123 -105
- package/opencode-shell.mjs +22 -22
- package/package.json +7 -1
- package/postinstall.mjs +22 -22
- package/pr-cleanup-daemon.mjs +6 -6
- package/prepublish-check.mjs +4 -4
- package/presence.mjs +2 -2
- package/primary-agent.mjs +85 -7
- package/publish.mjs +1 -1
- package/review-agent.mjs +1 -1
- package/session-tracker.mjs +11 -0
- package/setup-web-server.mjs +429 -21
- package/setup.mjs +367 -12
- package/shared-knowledge.mjs +1 -1
- package/startup-service.mjs +9 -9
- package/stream-resilience.mjs +58 -4
- package/sync-engine.mjs +2 -2
- package/task-assessment.mjs +9 -9
- package/task-cli.mjs +1 -1
- package/task-complexity.mjs +71 -2
- package/task-context.mjs +1 -2
- package/task-executor.mjs +104 -41
- package/telegram-bot.mjs +825 -494
- package/telegram-sentinel.mjs +28 -28
- package/ui/app.js +256 -23
- package/ui/app.monolith.js +1 -1
- package/ui/components/agent-selector.js +4 -3
- package/ui/components/chat-view.js +101 -28
- package/ui/components/diff-viewer.js +3 -3
- package/ui/components/kanban-board.js +3 -3
- package/ui/components/session-list.js +255 -35
- package/ui/components/workspace-switcher.js +3 -3
- package/ui/demo.html +209 -194
- package/ui/index.html +3 -3
- package/ui/modules/icon-utils.js +206 -142
- package/ui/modules/icons.js +2 -27
- package/ui/modules/settings-schema.js +29 -5
- package/ui/modules/streaming.js +30 -2
- package/ui/modules/vision-stream.js +275 -0
- package/ui/modules/voice-client.js +102 -9
- package/ui/modules/voice-fallback.js +62 -6
- package/ui/modules/voice-overlay.js +594 -59
- package/ui/modules/voice.js +31 -38
- package/ui/setup.html +284 -34
- package/ui/styles/components.css +47 -0
- package/ui/styles/sessions.css +75 -0
- package/ui/tabs/agents.js +73 -43
- package/ui/tabs/chat.js +37 -40
- package/ui/tabs/control.js +2 -2
- package/ui/tabs/dashboard.js +1 -1
- package/ui/tabs/infra.js +10 -10
- package/ui/tabs/library.js +8 -8
- package/ui/tabs/logs.js +10 -10
- package/ui/tabs/settings.js +20 -20
- package/ui/tabs/tasks.js +76 -47
- package/ui-server.mjs +1761 -124
- package/update-check.mjs +13 -13
- package/ve-kanban.mjs +1 -1
- package/whatsapp-channel.mjs +5 -5
- package/workflow-engine.mjs +20 -1
- package/workflow-nodes.mjs +904 -4
- package/workflow-templates/agents.mjs +321 -7
- package/workflow-templates/ci-cd.mjs +6 -6
- package/workflow-templates/github.mjs +156 -84
- package/workflow-templates/planning.mjs +8 -8
- package/workflow-templates/reliability.mjs +8 -8
- package/workflow-templates/security.mjs +3 -3
- package/workflow-templates.mjs +15 -9
- package/workspace-manager.mjs +85 -1
- package/workspace-monitor.mjs +2 -2
- package/workspace-registry.mjs +2 -2
- package/worktree-manager.mjs +1 -1
package/opencode-shell.mjs
CHANGED
|
@@ -328,7 +328,7 @@ function formatOpencodeEvent(event) {
|
|
|
328
328
|
return null; // internal bookkeeping
|
|
329
329
|
|
|
330
330
|
case "session.error":
|
|
331
|
-
return
|
|
331
|
+
return `:close: OpenCode error: ${p.error || p.message || "unknown"}`;
|
|
332
332
|
|
|
333
333
|
// ── Message streaming ──────────────────────────────────────────────────
|
|
334
334
|
case "message.part": {
|
|
@@ -338,7 +338,7 @@ function formatOpencodeEvent(event) {
|
|
|
338
338
|
}
|
|
339
339
|
// Reasoning / thinking blocks
|
|
340
340
|
if (p.type === "thinking" && p.thinking) {
|
|
341
|
-
return
|
|
341
|
+
return `:u1f4ad: ${p.thinking.slice(0, 300)}`;
|
|
342
342
|
}
|
|
343
343
|
return null;
|
|
344
344
|
}
|
|
@@ -360,31 +360,31 @@ function formatOpencodeEvent(event) {
|
|
|
360
360
|
const tool = p.tool || "";
|
|
361
361
|
if (tool.startsWith("mcp_")) {
|
|
362
362
|
const [, server, ...nameParts] = tool.split("_");
|
|
363
|
-
return
|
|
363
|
+
return `:plug: MCP [${server}]: ${nameParts.join("_")}`;
|
|
364
364
|
}
|
|
365
365
|
if (tool === "bash" || tool === "shell" || tool === "run") {
|
|
366
|
-
return
|
|
366
|
+
return `:zap: Running: \`${p.input?.command || p.input?.cmd || tool}\``;
|
|
367
367
|
}
|
|
368
368
|
if (tool === "write" || tool === "edit" || tool === "file_write") {
|
|
369
|
-
return
|
|
369
|
+
return `:edit: Writing: ${p.input?.path || p.input?.file_path || "file"}`;
|
|
370
370
|
}
|
|
371
371
|
if (tool === "read" || tool === "file_read") {
|
|
372
|
-
return
|
|
372
|
+
return `:file: Reading: ${p.input?.path || p.input?.file_path || "file"}`;
|
|
373
373
|
}
|
|
374
374
|
if (tool === "web_search" || tool === "webSearch") {
|
|
375
|
-
return
|
|
375
|
+
return `:search: Searching: ${p.input?.query || ""}`;
|
|
376
376
|
}
|
|
377
377
|
if (tool === "glob" || tool === "find") {
|
|
378
|
-
return
|
|
378
|
+
return `:search: Finding: ${p.input?.pattern || p.input?.query || ""}`;
|
|
379
379
|
}
|
|
380
380
|
// Generic tool
|
|
381
|
-
return
|
|
381
|
+
return `:settings: Tool: ${tool}`;
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
case "tool.complete": {
|
|
385
385
|
const tool = p.tool || "";
|
|
386
386
|
const isError = !!p.error || p.exitCode !== undefined && p.exitCode !== 0;
|
|
387
|
-
const status = isError ? "
|
|
387
|
+
const status = isError ? ":close:" : ":check:";
|
|
388
388
|
|
|
389
389
|
if (tool.startsWith("mcp_")) {
|
|
390
390
|
const [, server, ...nameParts] = tool.split("_");
|
|
@@ -407,12 +407,12 @@ function formatOpencodeEvent(event) {
|
|
|
407
407
|
// ── File changes ───────────────────────────────────────────────────────
|
|
408
408
|
case "file.updated":
|
|
409
409
|
case "file.created": {
|
|
410
|
-
const action = type === "file.created" ? "
|
|
410
|
+
const action = type === "file.created" ? ":plus:" : ":edit:";
|
|
411
411
|
return `${action} ${p.path || p.file || "file"}`;
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
case "file.deleted":
|
|
415
|
-
return
|
|
415
|
+
return `:trash: Deleted: ${p.path || p.file || "file"}`;
|
|
416
416
|
|
|
417
417
|
// ── Error / completion ─────────────────────────────────────────────────
|
|
418
418
|
case "prompt.completed":
|
|
@@ -421,7 +421,7 @@ function formatOpencodeEvent(event) {
|
|
|
421
421
|
|
|
422
422
|
case "error":
|
|
423
423
|
case "prompt.error":
|
|
424
|
-
return
|
|
424
|
+
return `:close: Error: ${p.message || p.error || "unknown"}`;
|
|
425
425
|
|
|
426
426
|
default:
|
|
427
427
|
return null;
|
|
@@ -485,7 +485,7 @@ export async function execOpencodePrompt(userMessage, options = {}) {
|
|
|
485
485
|
agentSdk = resolveAgentSdkConfig({ reload: true });
|
|
486
486
|
if (agentSdk.primary !== "opencode") {
|
|
487
487
|
return {
|
|
488
|
-
finalResponse:
|
|
488
|
+
finalResponse: `:close: Agent SDK set to "${agentSdk.primary}" — OpenCode disabled.`,
|
|
489
489
|
items: [],
|
|
490
490
|
usage: null,
|
|
491
491
|
};
|
|
@@ -493,7 +493,7 @@ export async function execOpencodePrompt(userMessage, options = {}) {
|
|
|
493
493
|
|
|
494
494
|
if (envFlagEnabled(process.env.OPENCODE_SDK_DISABLED)) {
|
|
495
495
|
return {
|
|
496
|
-
finalResponse: "
|
|
496
|
+
finalResponse: ":close: OpenCode disabled via OPENCODE_SDK_DISABLED.",
|
|
497
497
|
items: [],
|
|
498
498
|
usage: null,
|
|
499
499
|
};
|
|
@@ -501,7 +501,7 @@ export async function execOpencodePrompt(userMessage, options = {}) {
|
|
|
501
501
|
|
|
502
502
|
if (activeTurn) {
|
|
503
503
|
return {
|
|
504
|
-
finalResponse: "
|
|
504
|
+
finalResponse: ":clock: OpenCode agent is still executing a previous task. Please wait.",
|
|
505
505
|
items: [],
|
|
506
506
|
usage: null,
|
|
507
507
|
};
|
|
@@ -513,7 +513,7 @@ export async function execOpencodePrompt(userMessage, options = {}) {
|
|
|
513
513
|
const started = await ensureServerStarted();
|
|
514
514
|
if (!started) {
|
|
515
515
|
return {
|
|
516
|
-
finalResponse: "
|
|
516
|
+
finalResponse: ":close: OpenCode server could not be started. Check that the opencode binary is on PATH.",
|
|
517
517
|
items: [],
|
|
518
518
|
usage: null,
|
|
519
519
|
};
|
|
@@ -534,7 +534,7 @@ export async function execOpencodePrompt(userMessage, options = {}) {
|
|
|
534
534
|
serverSessionId = await getOrCreateServerSession(namedId);
|
|
535
535
|
} catch (err) {
|
|
536
536
|
return {
|
|
537
|
-
finalResponse:
|
|
537
|
+
finalResponse: `:close: Could not establish OpenCode session: ${err.message}`,
|
|
538
538
|
items: [],
|
|
539
539
|
usage: null,
|
|
540
540
|
};
|
|
@@ -700,8 +700,8 @@ export async function execOpencodePrompt(userMessage, options = {}) {
|
|
|
700
700
|
const reason = controller.signal.reason;
|
|
701
701
|
const msg =
|
|
702
702
|
reason === "user_stop"
|
|
703
|
-
? "
|
|
704
|
-
:
|
|
703
|
+
? ":close: Agent stopped by user."
|
|
704
|
+
: `:clock: Agent timed out after ${timeoutMs / 1000}s`;
|
|
705
705
|
|
|
706
706
|
// Try to abort the server-side turn
|
|
707
707
|
try {
|
|
@@ -725,7 +725,7 @@ export async function execOpencodePrompt(userMessage, options = {}) {
|
|
|
725
725
|
continue;
|
|
726
726
|
}
|
|
727
727
|
return {
|
|
728
|
-
finalResponse:
|
|
728
|
+
finalResponse: `:close: OpenCode: connection failed after ${MAX_STREAM_RETRIES} retries: ${err.message}`,
|
|
729
729
|
items: [],
|
|
730
730
|
usage: null,
|
|
731
731
|
};
|
|
@@ -736,7 +736,7 @@ export async function execOpencodePrompt(userMessage, options = {}) {
|
|
|
736
736
|
}
|
|
737
737
|
|
|
738
738
|
return {
|
|
739
|
-
finalResponse: "
|
|
739
|
+
finalResponse: ":close: OpenCode agent failed after all retry attempts.",
|
|
740
740
|
items: [],
|
|
741
741
|
usage: null,
|
|
742
742
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosun",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.2",
|
|
4
4
|
"description": "AI-powered orchestrator supervisor — manages AI agent executors with failover, auto-restarts on failure, analyzes crashes with Codex SDK, creates PRs via Vibe-Kanban API, and sends Telegram notifications. Supports N executors with weighted distribution, multi-repo projects, and auto-setup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache 2.0",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"./codex-shell": "./codex-shell.mjs",
|
|
40
40
|
"./copilot-shell": "./copilot-shell.mjs",
|
|
41
41
|
"./claude-shell": "./claude-shell.mjs",
|
|
42
|
+
"./gemini-shell": "./gemini-shell.mjs",
|
|
42
43
|
"./primary-agent": "./primary-agent.mjs",
|
|
43
44
|
"./maintenance": "./maintenance.mjs",
|
|
44
45
|
"./telegram-bot": "./telegram-bot.mjs",
|
|
@@ -92,10 +93,13 @@
|
|
|
92
93
|
"desktop:install": "npm -C desktop install",
|
|
93
94
|
"desktop:dist": "npm -C desktop run dist",
|
|
94
95
|
"build": "node vendor-sync.mjs",
|
|
96
|
+
"build:docs": "node build-docs.mjs",
|
|
95
97
|
"shared-workspaces": "node shared-workspace-cli.mjs",
|
|
96
98
|
"syntax:check": "node -e \"const fs=require('fs'),path=require('path');const files=fs.readdirSync('.').filter(f=>f.endsWith('.mjs'));let fail=0;for(const f of files){try{require('child_process').execSync('node --check '+f,{stdio:'pipe'});}catch(e){console.error('Syntax error: '+f);console.error(e.stderr.toString());fail=1;}}if(fail)process.exit(1);console.log('Syntax OK: '+files.length+' files checked');\"",
|
|
97
99
|
"pretest": "npm run syntax:check",
|
|
98
100
|
"test": "vitest run --config vitest.config.mjs",
|
|
101
|
+
"test:voice-provider-smoke": "vitest run --config vitest.config.mjs tests/voice-provider-smoke.test.mjs",
|
|
102
|
+
"check:native-call-parity": "vitest run --config vitest.config.mjs tests/voice-provider-smoke.test.mjs tests/native-call-parity-checklist.test.mjs",
|
|
99
103
|
"test:watch": "vitest",
|
|
100
104
|
"preinstall": "node -e \"try{var r=require('child_process').execSync('npm ls -g codex-monitor --json --depth=0',{encoding:'utf8',stdio:['pipe','pipe','pipe']});var d=JSON.parse(r).dependencies;if(d&&d['codex-monitor']){console.log('\\n Removing old codex-monitor package...');require('child_process').execSync('npm uninstall -g codex-monitor',{stdio:'inherit',timeout:30000});console.log(' \\u2705 Migrated to bosun. codex-monitor aliases still work.\\n')}}catch(e){}\"",
|
|
101
105
|
"postinstall": "node postinstall.mjs",
|
|
@@ -139,6 +143,7 @@
|
|
|
139
143
|
"config-doctor.mjs",
|
|
140
144
|
"conflict-resolver.mjs",
|
|
141
145
|
"copilot-shell.mjs",
|
|
146
|
+
"gemini-shell.mjs",
|
|
142
147
|
"diff-stats.mjs",
|
|
143
148
|
"desktop/main.mjs",
|
|
144
149
|
"desktop/launch.mjs",
|
|
@@ -241,6 +246,7 @@
|
|
|
241
246
|
"dependencies": {
|
|
242
247
|
"@anthropic-ai/claude-agent-sdk": "latest",
|
|
243
248
|
"@github/copilot-sdk": "latest",
|
|
249
|
+
"@google/genai": "latest",
|
|
244
250
|
"@openai/codex-sdk": "latest",
|
|
245
251
|
"@opencode-ai/sdk": "latest",
|
|
246
252
|
"@preact/signals": "1.3.1",
|
package/postinstall.mjs
CHANGED
|
@@ -267,9 +267,9 @@ async function main() {
|
|
|
267
267
|
// Node.js version check
|
|
268
268
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
269
269
|
if (nodeMajor >= 18) {
|
|
270
|
-
console.log(`
|
|
270
|
+
console.log(` :check: Node.js ${process.versions.node}`);
|
|
271
271
|
} else {
|
|
272
|
-
console.log(`
|
|
272
|
+
console.log(` :close: Node.js ${process.versions.node} — requires ≥ 18`);
|
|
273
273
|
hasErrors = true;
|
|
274
274
|
}
|
|
275
275
|
|
|
@@ -277,9 +277,9 @@ async function main() {
|
|
|
277
277
|
for (const dep of REQUIRED) {
|
|
278
278
|
if (commandExists(dep.cmd)) {
|
|
279
279
|
const ver = getVersion(dep.cmd);
|
|
280
|
-
console.log(`
|
|
280
|
+
console.log(` :check: ${dep.name}${ver ? ` (${ver})` : ""}`);
|
|
281
281
|
} else {
|
|
282
|
-
console.log(`
|
|
282
|
+
console.log(` :close: ${dep.name} — REQUIRED`);
|
|
283
283
|
const hint = dep.install[platform] || dep.install.linux;
|
|
284
284
|
console.log(` Install: ${hint}`);
|
|
285
285
|
console.log(` Docs: ${dep.url}`);
|
|
@@ -301,9 +301,9 @@ async function main() {
|
|
|
301
301
|
console.log(" ▸ Installing PowerShell (bundled)...");
|
|
302
302
|
try {
|
|
303
303
|
const info = await installBundledPwsh(platform, process.arch);
|
|
304
|
-
console.log(`
|
|
304
|
+
console.log(` :check: PowerShell bundled (${info.version})`);
|
|
305
305
|
} catch (err) {
|
|
306
|
-
console.log(`
|
|
306
|
+
console.log(` :alert: PowerShell bundle install failed: ${err.message}`);
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
@@ -316,12 +316,12 @@ async function main() {
|
|
|
316
316
|
if (hasPwsh) {
|
|
317
317
|
const ver = getVersion(dep.cmd);
|
|
318
318
|
if (isPwsh && bundledPwshExists() && !ver) {
|
|
319
|
-
console.log(`
|
|
319
|
+
console.log(` :check: ${dep.name} (bundled)`);
|
|
320
320
|
} else {
|
|
321
|
-
console.log(`
|
|
321
|
+
console.log(` :check: ${dep.name}${ver ? ` (${ver})` : ""}`);
|
|
322
322
|
}
|
|
323
323
|
} else {
|
|
324
|
-
console.log(`
|
|
324
|
+
console.log(` :alert: ${dep.name} — not found`);
|
|
325
325
|
console.log(` ${dep.why}`);
|
|
326
326
|
const hint = dep.install[platform] || dep.install.linux;
|
|
327
327
|
console.log(` Install: ${hint}`);
|
|
@@ -330,12 +330,12 @@ async function main() {
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
// npm-installed tools (bundled with this package)
|
|
333
|
-
console.log(`
|
|
334
|
-
console.log(`
|
|
335
|
-
console.log(`
|
|
336
|
-
console.log(`
|
|
337
|
-
console.log(`
|
|
338
|
-
console.log(`
|
|
333
|
+
console.log(` :check: vibe-kanban (bundled)`);
|
|
334
|
+
console.log(` :check: @openai/codex-sdk (bundled)`);
|
|
335
|
+
console.log(` :check: @github/copilot-sdk (bundled)`);
|
|
336
|
+
console.log(` :check: @anthropic-ai/claude-agent-sdk (bundled)`);
|
|
337
|
+
console.log(` :check: @github/copilot-sdk (bundled)`);
|
|
338
|
+
console.log(` :check: @anthropic-ai/claude-agent-sdk (bundled)`);
|
|
339
339
|
|
|
340
340
|
// Desktop dependencies (Electron) — optional but recommended for instant launch
|
|
341
341
|
const desktopDir = resolve(__dirname, "desktop");
|
|
@@ -355,10 +355,10 @@ async function main() {
|
|
|
355
355
|
stdio: "inherit",
|
|
356
356
|
timeout: 0,
|
|
357
357
|
});
|
|
358
|
-
console.log("
|
|
358
|
+
console.log(" :check: Desktop dependencies installed");
|
|
359
359
|
} catch (err) {
|
|
360
360
|
console.log(
|
|
361
|
-
"
|
|
361
|
+
" :alert: Desktop dependency install failed — run manually:",
|
|
362
362
|
);
|
|
363
363
|
console.log(" npm -C scripts/bosun/desktop install");
|
|
364
364
|
}
|
|
@@ -369,7 +369,7 @@ async function main() {
|
|
|
369
369
|
console.log("");
|
|
370
370
|
if (hasErrors) {
|
|
371
371
|
console.log(
|
|
372
|
-
"
|
|
372
|
+
" :ban: Missing required dependencies. Install them before running bosun.",
|
|
373
373
|
);
|
|
374
374
|
} else if (hasWarnings) {
|
|
375
375
|
console.log(
|
|
@@ -407,17 +407,17 @@ async function main() {
|
|
|
407
407
|
const { ok, results } = await syncVendorFiles({ silent: true });
|
|
408
408
|
const synced = results.filter((r) => r.source).length;
|
|
409
409
|
if (ok) {
|
|
410
|
-
console.log(`
|
|
410
|
+
console.log(` :check: Vendor files bundled into ui/vendor/ (${synced}/${results.length} files)`);
|
|
411
411
|
} else {
|
|
412
412
|
const missing = results.filter((r) => !r.source).map((r) => r.name);
|
|
413
|
-
console.warn(`
|
|
413
|
+
console.warn(` :alert: Some vendor files could not be bundled: ${missing.join(", ")}`);
|
|
414
414
|
console.warn(" The UI server will fall back to CDN for those files.");
|
|
415
415
|
}
|
|
416
416
|
} catch (err) {
|
|
417
|
-
console.warn(`
|
|
417
|
+
console.warn(` :alert: vendor-sync skipped: ${err.message}`);
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
|
|
421
421
|
main().catch((err) => {
|
|
422
|
-
console.error(`
|
|
422
|
+
console.error(` :alert: postinstall failed: ${err.message}`);
|
|
423
423
|
});
|
package/pr-cleanup-daemon.mjs
CHANGED
|
@@ -326,7 +326,7 @@ class PRCleanupDaemon {
|
|
|
326
326
|
if (verified.mergeable === "MERGEABLE") {
|
|
327
327
|
this.stats.conflictsResolved++;
|
|
328
328
|
console.log(
|
|
329
|
-
`[pr-cleanup-daemon]
|
|
329
|
+
`[pr-cleanup-daemon] :check: Verified conflict resolution on PR #${pr.number} (mergeable=${verified.mergeable})`,
|
|
330
330
|
);
|
|
331
331
|
return true;
|
|
332
332
|
}
|
|
@@ -347,7 +347,7 @@ class PRCleanupDaemon {
|
|
|
347
347
|
if (verifiedLocal.mergeable === "MERGEABLE") {
|
|
348
348
|
this.stats.conflictsResolved++;
|
|
349
349
|
console.log(
|
|
350
|
-
`[pr-cleanup-daemon]
|
|
350
|
+
`[pr-cleanup-daemon] :check: Verified conflict resolution on PR #${pr.number} after local fallback`,
|
|
351
351
|
);
|
|
352
352
|
return true;
|
|
353
353
|
}
|
|
@@ -781,7 +781,7 @@ class PRCleanupDaemon {
|
|
|
781
781
|
{ cwd: this.repoRoot },
|
|
782
782
|
);
|
|
783
783
|
console.log(
|
|
784
|
-
`[pr-cleanup-daemon]
|
|
784
|
+
`[pr-cleanup-daemon] :clock: Auto-merge queued for PR #${pr.number} (CI pending)`,
|
|
785
785
|
);
|
|
786
786
|
} catch {
|
|
787
787
|
/* auto-merge may not be available */
|
|
@@ -802,7 +802,7 @@ class PRCleanupDaemon {
|
|
|
802
802
|
await exec(`gh pr merge ${pr.number} --squash --delete-branch`, { cwd: this.repoRoot });
|
|
803
803
|
this.stats.autoMerges++;
|
|
804
804
|
console.log(
|
|
805
|
-
`[pr-cleanup-daemon]
|
|
805
|
+
`[pr-cleanup-daemon] :check: Auto-merged green PR #${pr.number}: ${pr.title}`,
|
|
806
806
|
);
|
|
807
807
|
} catch (err) {
|
|
808
808
|
// Fallback: enable auto-merge
|
|
@@ -812,7 +812,7 @@ class PRCleanupDaemon {
|
|
|
812
812
|
{ cwd: this.repoRoot },
|
|
813
813
|
);
|
|
814
814
|
console.log(
|
|
815
|
-
`[pr-cleanup-daemon]
|
|
815
|
+
`[pr-cleanup-daemon] :clock: Auto-merge enabled for PR #${pr.number}`,
|
|
816
816
|
);
|
|
817
817
|
} catch {
|
|
818
818
|
console.warn(
|
|
@@ -836,7 +836,7 @@ class PRCleanupDaemon {
|
|
|
836
836
|
*/
|
|
837
837
|
async escalate(pr, reason, context = {}) {
|
|
838
838
|
const message =
|
|
839
|
-
|
|
839
|
+
`:alert: PR #${pr.number} escalated: ${reason}\n\n` +
|
|
840
840
|
`Title: ${pr.title}\n` +
|
|
841
841
|
`Context: ${JSON.stringify(context, null, 2)}\n\n` +
|
|
842
842
|
`Manual intervention required.`;
|
package/prepublish-check.mjs
CHANGED
|
@@ -21,7 +21,7 @@ const pkg = JSON.parse(
|
|
|
21
21
|
);
|
|
22
22
|
|
|
23
23
|
if (!pkg.version) {
|
|
24
|
-
console.error("
|
|
24
|
+
console.error(":close: Missing version in package.json");
|
|
25
25
|
process.exit(1);
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -37,7 +37,7 @@ for (const f of filesArray) {
|
|
|
37
37
|
}
|
|
38
38
|
if (duplicates.length > 0) {
|
|
39
39
|
console.error(
|
|
40
|
-
|
|
40
|
+
`:close: Duplicate entries in files array: ${duplicates.join(", ")}`,
|
|
41
41
|
);
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
@@ -77,7 +77,7 @@ for (const file of mjsFiles) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
if (missing.length > 0) {
|
|
80
|
-
console.error("
|
|
80
|
+
console.error(":close: Local imports not in package.json files array:");
|
|
81
81
|
for (const { file, imported } of missing) {
|
|
82
82
|
console.error(` ${file} → import from "./${imported}"`);
|
|
83
83
|
}
|
|
@@ -86,5 +86,5 @@ if (missing.length > 0) {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
console.log(
|
|
89
|
-
|
|
89
|
+
`:check: ${pkg.name}@${pkg.version} — ${filesArray.length} files, ${mjsFiles.length} .mjs scanned, 0 missing imports`,
|
|
90
90
|
);
|
package/presence.mjs
CHANGED
|
@@ -286,7 +286,7 @@ export function formatPresenceSummary({ nowMs, ttlMs } = {}) {
|
|
|
286
286
|
return "No active instances reported.";
|
|
287
287
|
}
|
|
288
288
|
const coordinator = selectCoordinator({ nowMs, ttlMs });
|
|
289
|
-
const lines = ["
|
|
289
|
+
const lines = [":server: Bosun Presence"];
|
|
290
290
|
for (const entry of active) {
|
|
291
291
|
const name = entry.instance_label || entry.instance_id;
|
|
292
292
|
const role = entry.workspace_role || "workspace";
|
|
@@ -311,7 +311,7 @@ export function formatCoordinatorSummary({ nowMs, ttlMs } = {}) {
|
|
|
311
311
|
const host = coordinator.host || "unknown";
|
|
312
312
|
const lastSeen = coordinator.last_seen_at || coordinator.updated_at || "unknown";
|
|
313
313
|
return [
|
|
314
|
-
"
|
|
314
|
+
":star: Coordinator",
|
|
315
315
|
`Instance: ${name}`,
|
|
316
316
|
`Role: ${role}`,
|
|
317
317
|
`Host: ${host}`,
|
package/primary-agent.mjs
CHANGED
|
@@ -51,6 +51,18 @@ import {
|
|
|
51
51
|
switchSession as switchOpencodeSession,
|
|
52
52
|
createSession as createOpencodeSession,
|
|
53
53
|
} from "./opencode-shell.mjs";
|
|
54
|
+
import {
|
|
55
|
+
execGeminiPrompt,
|
|
56
|
+
steerGeminiPrompt,
|
|
57
|
+
isGeminiBusy,
|
|
58
|
+
getSessionInfo as getGeminiSessionInfo,
|
|
59
|
+
resetSession as resetGeminiSession,
|
|
60
|
+
initGeminiShell,
|
|
61
|
+
getActiveSessionId as getGeminiSessionId,
|
|
62
|
+
listSessions as listGeminiSessions,
|
|
63
|
+
switchSession as switchGeminiSession,
|
|
64
|
+
createSession as createGeminiSession,
|
|
65
|
+
} from "./gemini-shell.mjs";
|
|
54
66
|
import { getModelsForExecutor, normalizeExecutorKey } from "./task-complexity.mjs";
|
|
55
67
|
|
|
56
68
|
/** Valid agent interaction modes */
|
|
@@ -191,6 +203,31 @@ const ADAPTERS = {
|
|
|
191
203
|
return execClaudePrompt(fullCmd, {});
|
|
192
204
|
},
|
|
193
205
|
},
|
|
206
|
+
"gemini-sdk": {
|
|
207
|
+
name: "gemini-sdk",
|
|
208
|
+
provider: "GEMINI",
|
|
209
|
+
displayName: "Gemini",
|
|
210
|
+
exec: (msg, opts) => execGeminiPrompt(msg, { persistent: true, ...opts }),
|
|
211
|
+
steer: steerGeminiPrompt,
|
|
212
|
+
isBusy: isGeminiBusy,
|
|
213
|
+
getInfo: () => getGeminiSessionInfo(),
|
|
214
|
+
reset: resetGeminiSession,
|
|
215
|
+
init: async () => initGeminiShell(),
|
|
216
|
+
getSessionId: getGeminiSessionId,
|
|
217
|
+
listSessions: listGeminiSessions,
|
|
218
|
+
switchSession: switchGeminiSession,
|
|
219
|
+
createSession: createGeminiSession,
|
|
220
|
+
sdkCommands: ["/status", "/model", "/clear"],
|
|
221
|
+
execSdkCommand: async (command, args) => {
|
|
222
|
+
const cmd = command.startsWith("/") ? command : `/${command}`;
|
|
223
|
+
if (cmd === "/clear") {
|
|
224
|
+
await resetGeminiSession();
|
|
225
|
+
return "Session cleared.";
|
|
226
|
+
}
|
|
227
|
+
const fullCmd = args ? `${cmd} ${args}` : cmd;
|
|
228
|
+
return execGeminiPrompt(fullCmd, { persistent: true });
|
|
229
|
+
},
|
|
230
|
+
},
|
|
194
231
|
"opencode-sdk": {
|
|
195
232
|
name: "opencode-sdk",
|
|
196
233
|
provider: "OPENCODE",
|
|
@@ -336,6 +373,8 @@ function normalizePrimaryAgent(value) {
|
|
|
336
373
|
return "copilot-sdk";
|
|
337
374
|
if (["claude", "claude-sdk", "claude_code", "claude-code"].includes(raw))
|
|
338
375
|
return "claude-sdk";
|
|
376
|
+
if (["gemini", "gemini-sdk", "google-gemini"].includes(raw))
|
|
377
|
+
return "gemini-sdk";
|
|
339
378
|
if (["opencode", "opencode-sdk", "open-code"].includes(raw))
|
|
340
379
|
return "opencode-sdk";
|
|
341
380
|
return raw;
|
|
@@ -354,6 +393,7 @@ function executorToAdapter(executor) {
|
|
|
354
393
|
const key = normalizeExecutorKey(executor);
|
|
355
394
|
if (key === "copilot") return "copilot-sdk";
|
|
356
395
|
if (key === "claude") return "claude-sdk";
|
|
396
|
+
if (key === "gemini") return "gemini-sdk";
|
|
357
397
|
if (key === "opencode") return "opencode-sdk";
|
|
358
398
|
return "codex-sdk";
|
|
359
399
|
}
|
|
@@ -474,6 +514,10 @@ export async function initPrimaryAgent(nameOrConfig = null) {
|
|
|
474
514
|
setPrimaryAgent("copilot-sdk");
|
|
475
515
|
} else if (!envFlagEnabled(process.env.CLAUDE_SDK_DISABLED)) {
|
|
476
516
|
setPrimaryAgent("claude-sdk");
|
|
517
|
+
} else if (!envFlagEnabled(process.env.GEMINI_SDK_DISABLED)) {
|
|
518
|
+
setPrimaryAgent("gemini-sdk");
|
|
519
|
+
} else if (!envFlagEnabled(process.env.OPENCODE_SDK_DISABLED)) {
|
|
520
|
+
setPrimaryAgent("opencode-sdk");
|
|
477
521
|
}
|
|
478
522
|
}
|
|
479
523
|
|
|
@@ -485,6 +529,22 @@ export async function initPrimaryAgent(nameOrConfig = null) {
|
|
|
485
529
|
setPrimaryAgent("codex-sdk");
|
|
486
530
|
}
|
|
487
531
|
|
|
532
|
+
if (
|
|
533
|
+
activeAdapter.name === "gemini-sdk" &&
|
|
534
|
+
envFlagEnabled(process.env.GEMINI_SDK_DISABLED)
|
|
535
|
+
) {
|
|
536
|
+
primaryFallbackReason = "Gemini SDK disabled — falling back to Codex";
|
|
537
|
+
setPrimaryAgent("codex-sdk");
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (
|
|
541
|
+
activeAdapter.name === "opencode-sdk" &&
|
|
542
|
+
envFlagEnabled(process.env.OPENCODE_SDK_DISABLED)
|
|
543
|
+
) {
|
|
544
|
+
primaryFallbackReason = "OpenCode SDK disabled — falling back to Codex";
|
|
545
|
+
setPrimaryAgent("codex-sdk");
|
|
546
|
+
}
|
|
547
|
+
|
|
488
548
|
ensurePrimaryAgentConfigs(activeAdapter.name);
|
|
489
549
|
|
|
490
550
|
const ok = await activeAdapter.init();
|
|
@@ -494,6 +554,18 @@ export async function initPrimaryAgent(nameOrConfig = null) {
|
|
|
494
554
|
ensurePrimaryAgentConfigs(activeAdapter.name);
|
|
495
555
|
await activeAdapter.init();
|
|
496
556
|
}
|
|
557
|
+
if (activeAdapter.name === "gemini-sdk" && ok === false) {
|
|
558
|
+
primaryFallbackReason = "Gemini SDK unavailable — falling back to Codex";
|
|
559
|
+
setPrimaryAgent("codex-sdk");
|
|
560
|
+
ensurePrimaryAgentConfigs(activeAdapter.name);
|
|
561
|
+
await activeAdapter.init();
|
|
562
|
+
}
|
|
563
|
+
if (activeAdapter.name === "opencode-sdk" && ok === false) {
|
|
564
|
+
primaryFallbackReason = "OpenCode SDK unavailable — falling back to Codex";
|
|
565
|
+
setPrimaryAgent("codex-sdk");
|
|
566
|
+
ensurePrimaryAgentConfigs(activeAdapter.name);
|
|
567
|
+
await activeAdapter.init();
|
|
568
|
+
}
|
|
497
569
|
|
|
498
570
|
initialized = true;
|
|
499
571
|
return getPrimaryAgentName();
|
|
@@ -506,7 +578,13 @@ const PRIMARY_EXEC_TIMEOUT_MS = Number(process.env.PRIMARY_AGENT_TIMEOUT_MS) ||
|
|
|
506
578
|
const MAX_FAILOVER_ATTEMPTS = 2;
|
|
507
579
|
|
|
508
580
|
/** Ordered fallback chain — if the current adapter times out, try the next */
|
|
509
|
-
const FALLBACK_ORDER = [
|
|
581
|
+
const FALLBACK_ORDER = [
|
|
582
|
+
"codex-sdk",
|
|
583
|
+
"copilot-sdk",
|
|
584
|
+
"claude-sdk",
|
|
585
|
+
"gemini-sdk",
|
|
586
|
+
"opencode-sdk",
|
|
587
|
+
];
|
|
510
588
|
|
|
511
589
|
function mapAdapterToPoolSdk(adapterName) {
|
|
512
590
|
const normalized = String(adapterName || "").trim().toLowerCase();
|
|
@@ -632,12 +710,12 @@ export async function execPrimaryPrompt(userMessage, options = {}) {
|
|
|
632
710
|
// If failing over to a different adapter, switch and init
|
|
633
711
|
if (attempt > 0) {
|
|
634
712
|
console.warn(
|
|
635
|
-
`[primary-agent]
|
|
713
|
+
`[primary-agent] :alert: Failing over from ${adaptersToTry[attempt - 1]} to ${adapterName} (reason: ${lastError?.message || "unknown"})`,
|
|
636
714
|
);
|
|
637
715
|
tracker.recordEvent(sessionId, {
|
|
638
716
|
role: "system",
|
|
639
717
|
type: "failover",
|
|
640
|
-
content:
|
|
718
|
+
content: `:alert: Agent "${adaptersToTry[attempt - 1]}" failed — switching to "${adapterName}": ${lastError?.message || "timeout/error"}`,
|
|
641
719
|
timestamp: new Date().toISOString(),
|
|
642
720
|
});
|
|
643
721
|
setPrimaryAgent(adapterName);
|
|
@@ -691,7 +769,7 @@ export async function execPrimaryPrompt(userMessage, options = {}) {
|
|
|
691
769
|
lastError = err;
|
|
692
770
|
const isTimeout = err.message?.startsWith("AGENT_TIMEOUT");
|
|
693
771
|
console.error(
|
|
694
|
-
`[primary-agent] ${isTimeout ? "
|
|
772
|
+
`[primary-agent] ${isTimeout ? ":clock: Timeout" : ":close: Error"} with ${adapterName}: ${err.message}`,
|
|
695
773
|
);
|
|
696
774
|
|
|
697
775
|
// If this is the last adapter, report to user
|
|
@@ -700,8 +778,8 @@ export async function execPrimaryPrompt(userMessage, options = {}) {
|
|
|
700
778
|
role: "system",
|
|
701
779
|
type: "error",
|
|
702
780
|
content: isTimeout
|
|
703
|
-
?
|
|
704
|
-
:
|
|
781
|
+
? `:clock: All agents timed out. The AI service may be experiencing issues. Your message was saved — please try again shortly.`
|
|
782
|
+
: `:close: Agent error: ${err.message}. Your message was saved — please try again.`,
|
|
705
783
|
timestamp: new Date().toISOString(),
|
|
706
784
|
});
|
|
707
785
|
}
|
|
@@ -710,7 +788,7 @@ export async function execPrimaryPrompt(userMessage, options = {}) {
|
|
|
710
788
|
|
|
711
789
|
// All adapters failed
|
|
712
790
|
return {
|
|
713
|
-
finalResponse:
|
|
791
|
+
finalResponse: `:close: All agent adapters failed. Last error: ${lastError?.message || "unknown"}`,
|
|
714
792
|
items: [],
|
|
715
793
|
usage: null,
|
|
716
794
|
};
|
package/publish.mjs
CHANGED
|
@@ -225,7 +225,7 @@ function main() {
|
|
|
225
225
|
if (status === 0 && !dryRun) {
|
|
226
226
|
console.log(
|
|
227
227
|
"\n[publish] :\n" +
|
|
228
|
-
" npm deprecate openfleet@'*' \"
|
|
228
|
+
" npm deprecate openfleet@'*' \":alert: openfleet has been renamed to bosun. Install the latest: npm install -g bosun\"\n",
|
|
229
229
|
);
|
|
230
230
|
}
|
|
231
231
|
process.exit(status);
|
package/review-agent.mjs
CHANGED
|
@@ -588,7 +588,7 @@ export class ReviewAgent {
|
|
|
588
588
|
.join("\n");
|
|
589
589
|
|
|
590
590
|
const message = [
|
|
591
|
-
|
|
591
|
+
`:search: Review: changes requested`,
|
|
592
592
|
`Task: ${taskId}`,
|
|
593
593
|
`Summary: ${result.summary}`,
|
|
594
594
|
result.issues.length ? `\nIssues:\n${issueList}` : "",
|
package/session-tracker.mjs
CHANGED
|
@@ -1005,6 +1005,15 @@ ${items.join("\n")}` : "todo updated";
|
|
|
1005
1005
|
};
|
|
1006
1006
|
}
|
|
1007
1007
|
|
|
1008
|
+
if (event.type === "session.idle" || event.type === "session.completed") {
|
|
1009
|
+
return {
|
|
1010
|
+
type: "system",
|
|
1011
|
+
content: "Session completed",
|
|
1012
|
+
timestamp: ts,
|
|
1013
|
+
meta: { lifecycle: "session_completed" },
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1008
1017
|
if (event.type === "turn.failed") {
|
|
1009
1018
|
const detail = toText(event.error?.message || "unknown error");
|
|
1010
1019
|
return {
|
|
@@ -1067,10 +1076,12 @@ ${items.join("\n")}` : "todo updated";
|
|
|
1067
1076
|
}
|
|
1068
1077
|
|
|
1069
1078
|
if (event.type === "message_stop" || event.type === "message_delta") {
|
|
1079
|
+
const lifecycle = event.type === "message_stop" ? "turn_completed" : undefined;
|
|
1070
1080
|
return {
|
|
1071
1081
|
type: "system",
|
|
1072
1082
|
content: `${event.type}${event.delta?.stop_reason ? ` (${event.delta.stop_reason})` : ""}`,
|
|
1073
1083
|
timestamp: ts,
|
|
1084
|
+
...(lifecycle ? { meta: { lifecycle } } : {}),
|
|
1074
1085
|
};
|
|
1075
1086
|
}
|
|
1076
1087
|
|