bosun 0.40.15 → 0.40.17
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/agent/agent-pool.mjs +18 -4
- package/kanban/vibe-kanban-wrapper.mjs +0 -0
- package/package.json +1 -1
- package/task/task-cli.mjs +0 -0
- package/task/task-executor.mjs +42 -2
- package/telegram/get-telegram-chat-id.mjs +0 -0
- package/telegram/telegram-sentinel.mjs +0 -0
- package/ui/components/chat-view.js.bak +1 -0
- package/ui/tabs/infra.js.bak +1 -0
- package/workspace/shared-workspace-cli.mjs +0 -0
package/agent/agent-pool.mjs
CHANGED
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
42
|
import { resolve, dirname } from "node:path";
|
|
43
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
44
|
+
import { homedir } from "node:os";
|
|
43
45
|
import { fileURLToPath } from "node:url";
|
|
44
46
|
import { loadConfig } from "../config/config.mjs";
|
|
45
47
|
import { resolveRepoRoot, resolveAgentRepoRoot } from "../config/repo-root.mjs";
|
|
@@ -566,16 +568,28 @@ function hasSdkPrerequisites(name, runtimeEnv = process.env) {
|
|
|
566
568
|
}
|
|
567
569
|
|
|
568
570
|
if (name === "codex") {
|
|
569
|
-
// Codex needs an OpenAI API key (or Azure key, or profile-specific key)
|
|
571
|
+
// Codex needs an OpenAI API key (or Azure key, or profile-specific key),
|
|
572
|
+
// OR a valid ~/.codex/config.toml where an env_key reference is satisfied.
|
|
570
573
|
const hasKey =
|
|
571
574
|
runtimeEnv.OPENAI_API_KEY ||
|
|
572
575
|
runtimeEnv.AZURE_OPENAI_API_KEY ||
|
|
573
576
|
runtimeEnv.CODEX_MODEL_PROFILE_XL_API_KEY ||
|
|
574
577
|
runtimeEnv.CODEX_MODEL_PROFILE_M_API_KEY;
|
|
575
|
-
if (
|
|
576
|
-
|
|
578
|
+
if (hasKey) return { ok: true, reason: null };
|
|
579
|
+
// Check ~/.codex/config.toml — Codex CLI SDK reads auth env_key refs from there
|
|
580
|
+
try {
|
|
581
|
+
const configToml = resolve(homedir(), ".codex", "config.toml");
|
|
582
|
+
if (existsSync(configToml)) {
|
|
583
|
+
const tomlText = readFileSync(configToml, "utf8");
|
|
584
|
+
// Extract all env_key = "VAR_NAME" entries and check if any are set
|
|
585
|
+
for (const match of tomlText.matchAll(/env_key\s*=\s*"([^"]+)"/g)) {
|
|
586
|
+
if (runtimeEnv[match[1]]) return { ok: true, reason: null };
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
} catch {
|
|
590
|
+
// best effort — fall through to failure
|
|
577
591
|
}
|
|
578
|
-
return { ok:
|
|
592
|
+
return { ok: false, reason: "no API key (OPENAI_API_KEY / AZURE_OPENAI_API_KEY) and no satisfied env_key in ~/.codex/config.toml" };
|
|
579
593
|
}
|
|
580
594
|
if (name === "copilot") {
|
|
581
595
|
// Copilot auth can come from multiple sources (OAuth manager, gh auth,
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosun",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.17",
|
|
4
4
|
"description": "Bosun Autonomous Engineering — manages AI agent executors with failover, extremely powerful workflow builder, and a massive amount of included default workflow templates for autonomous engineering, 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",
|
package/task/task-cli.mjs
CHANGED
|
File without changes
|
package/task/task-executor.mjs
CHANGED
|
@@ -822,7 +822,7 @@ const MAX_IDLE_CONTINUES = 5;
|
|
|
822
822
|
const WATCHDOG_WARMUP_MS = 5 * 60_000; // 5 minutes warmup
|
|
823
823
|
const SHARED_STATE_ENABLED = process.env.SHARED_STATE_ENABLED !== "false";
|
|
824
824
|
const SHARED_STATE_STALE_THRESHOLD_MS =
|
|
825
|
-
Number(process.env.SHARED_STATE_STALE_THRESHOLD_MS) ||
|
|
825
|
+
Number(process.env.SHARED_STATE_STALE_THRESHOLD_MS) || 600_000;
|
|
826
826
|
const NO_COMMIT_STATE_FILE = resolve(
|
|
827
827
|
dirname(fileURLToPath(import.meta.url)),
|
|
828
828
|
"..",
|
|
@@ -3900,9 +3900,12 @@ class TaskExecutor {
|
|
|
3900
3900
|
const ownerId = sharedState?.ownerId || null;
|
|
3901
3901
|
const heartbeat =
|
|
3902
3902
|
sharedState?.ownerHeartbeat || sharedState?.heartbeat || null;
|
|
3903
|
+
// Any non-stale owner (workflow run or executor instance) should
|
|
3904
|
+
// block recovery re-dispatch. Removing the ownerId !== instanceId
|
|
3905
|
+
// guard ensures workflow-owned tasks (wf-<uuid> owners) are also
|
|
3906
|
+
// protected when action.claim_task IS used.
|
|
3903
3907
|
if (
|
|
3904
3908
|
ownerId &&
|
|
3905
|
-
ownerId !== this._instanceId &&
|
|
3906
3909
|
!isSharedHeartbeatStale(heartbeat, SHARED_STATE_STALE_THRESHOLD_MS)
|
|
3907
3910
|
) {
|
|
3908
3911
|
skippedForActiveClaim++;
|
|
@@ -3977,6 +3980,43 @@ class TaskExecutor {
|
|
|
3977
3980
|
}
|
|
3978
3981
|
const isFreshEnough =
|
|
3979
3982
|
ageMs === 0 || ageMs <= INPROGRESS_RECOVERY_MAX_AGE_MS;
|
|
3983
|
+
|
|
3984
|
+
// In workflow-owned mode, calling executeTask() fires task.assigned
|
|
3985
|
+
// which launches a new workflow run. If one already exists (evidenced by
|
|
3986
|
+
// an alive agent thread), that creates two competing runs → owner_mismatch.
|
|
3987
|
+
// trigger.task_assigned workflows don't call action.claim_task, so ownerId
|
|
3988
|
+
// is null and the shared-state guard above cannot protect against this.
|
|
3989
|
+
// • Active thread → workflow is still managing it; leave it alone.
|
|
3990
|
+
// • No active thread but fresh → agent died; reset to todo so
|
|
3991
|
+
// trigger.task_available re-dispatches cleanly, without double-dispatch.
|
|
3992
|
+
if (this.workflowOwnsTaskLifecycle) {
|
|
3993
|
+
if (hasThread) {
|
|
3994
|
+
skippedForActiveClaim++;
|
|
3995
|
+
continue;
|
|
3996
|
+
}
|
|
3997
|
+
if (isFreshEnough) {
|
|
3998
|
+
try {
|
|
3999
|
+
await transitionTaskStatus(id, "todo", {
|
|
4000
|
+
source: "task-executor-recovery-workflow-owned",
|
|
4001
|
+
});
|
|
4002
|
+
} catch {
|
|
4003
|
+
/* best effort */
|
|
4004
|
+
}
|
|
4005
|
+
try {
|
|
4006
|
+
transitionInternalTaskStatus(
|
|
4007
|
+
id,
|
|
4008
|
+
"todo",
|
|
4009
|
+
"task-executor-recovery-workflow-owned",
|
|
4010
|
+
);
|
|
4011
|
+
} catch {
|
|
4012
|
+
/* best effort */
|
|
4013
|
+
}
|
|
4014
|
+
this._removeRuntimeSlot(id);
|
|
4015
|
+
resetToTodo++;
|
|
4016
|
+
continue;
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
|
|
3980
4020
|
if (hasThread || isFreshEnough) {
|
|
3981
4021
|
if (!internalTask) {
|
|
3982
4022
|
try {
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/* backup of original chat-view.js before MUI transformation */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/* backup - original infra.js before MUI transformation */
|
|
File without changes
|