bosun 0.41.9 → 0.42.0
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 +1 -1
- package/agent/agent-hooks.mjs +56 -1
- package/agent/agent-pool.mjs +25 -30
- package/agent/agent-prompts.mjs +6 -2
- package/agent/autofix.mjs +1 -1
- package/agent/hook-library.mjs +1383 -0
- package/agent/hook-profiles.mjs +55 -3
- package/config/config.mjs +47 -33
- package/config/context-shredding-config.mjs +1 -1
- package/config/repo-root.mjs +41 -33
- package/git/sdk-conflict-resolver.mjs +1 -1
- package/infra/library-manager.mjs +4 -2
- package/infra/monitor.mjs +1 -1
- package/package.json +6 -1
- package/server/setup-web-server.mjs +1 -1
- package/server/ui-server.mjs +1344 -10
- package/shell/codex-config.mjs +1 -1
- package/shell/codex-model-profiles.mjs +13 -10
- package/shell/codex-shell.mjs +12 -5
- package/telegram/telegram-sentinel.mjs +54 -3
- package/ui/components/session-list.js +13 -10
- package/ui/demo-defaults.js +328 -330
- package/ui/tabs/workflows.js +1016 -3
- package/workflow/execution-ledger.mjs +140 -0
- package/workflow/workflow-engine.mjs +865 -28
- package/workflow/workflow-nodes.mjs +676 -98
- package/workflow-templates/_helpers.mjs +253 -0
- package/workflow-templates/agents.mjs +199 -226
- package/workflow-templates/sub-workflows.mjs +233 -0
- package/workflow-templates/task-execution.mjs +125 -471
- package/workflow-templates/task-lifecycle.mjs +11 -48
- package/workspace/command-diagnostics.mjs +460 -0
- package/workspace/context-cache.mjs +396 -28
package/.env.example
CHANGED
|
@@ -201,7 +201,7 @@ VOICE_DELEGATE_EXECUTOR=codex-sdk
|
|
|
201
201
|
# earlier: it summarizes large, noisy command outputs before they ever land in
|
|
202
202
|
# the active turn, while preserving a `bosun --tool-log <id>` retrieval path.
|
|
203
203
|
# CONTEXT_SHREDDING_ENABLED=true
|
|
204
|
-
# CONTEXT_SHREDDING_LIVE_TOOL_COMPACTION_ENABLED=
|
|
204
|
+
# CONTEXT_SHREDDING_LIVE_TOOL_COMPACTION_ENABLED=true
|
|
205
205
|
# CONTEXT_SHREDDING_LIVE_TOOL_COMPACTION_MODE=auto
|
|
206
206
|
# CONTEXT_SHREDDING_LIVE_TOOL_COMPACTION_MIN_CHARS=4000
|
|
207
207
|
# CONTEXT_SHREDDING_LIVE_TOOL_COMPACTION_TARGET_CHARS=1800
|
package/agent/agent-hooks.mjs
CHANGED
|
@@ -144,7 +144,7 @@ export const HOOK_EVENTS = Object.freeze([
|
|
|
144
144
|
* Canonical SDK names.
|
|
145
145
|
* @type {readonly string[]}
|
|
146
146
|
*/
|
|
147
|
-
const VALID_SDKS = Object.freeze(["codex", "copilot", "claude", "opencode"]);
|
|
147
|
+
const VALID_SDKS = Object.freeze(["codex", "copilot", "claude", "opencode", "gemini"]);
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
150
|
* Wildcard indicating a hook applies to all SDKs.
|
|
@@ -1198,3 +1198,58 @@ function _truncate(str, maxLen) {
|
|
|
1198
1198
|
if (!str || str.length <= maxLen) return str ?? "";
|
|
1199
1199
|
return str.slice(0, maxLen) + "\n... (truncated)";
|
|
1200
1200
|
}
|
|
1201
|
+
|
|
1202
|
+
// ── Hook Library Integration ────────────────────────────────────────────────
|
|
1203
|
+
|
|
1204
|
+
/**
|
|
1205
|
+
* Register hooks from the hook-library into the agent-hooks runtime registry.
|
|
1206
|
+
* This bridges the declarative hook catalog with the execution engine.
|
|
1207
|
+
*
|
|
1208
|
+
* @param {Record<string, Array<{id: string, command: string, description?: string, timeout?: number, blocking?: boolean, sdks?: string[], builtin?: boolean, retryable?: boolean, maxRetries?: number, env?: Record<string,string>}>>} hooksByEvent
|
|
1209
|
+
* - Output of getHooksForRegistration() from hook-library.mjs
|
|
1210
|
+
* @returns {{ registered: number, skipped: number }}
|
|
1211
|
+
*/
|
|
1212
|
+
export function registerLibraryHooks(hooksByEvent) {
|
|
1213
|
+
let registered = 0;
|
|
1214
|
+
let skipped = 0;
|
|
1215
|
+
|
|
1216
|
+
if (!hooksByEvent || typeof hooksByEvent !== "object") {
|
|
1217
|
+
return { registered, skipped };
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
for (const [event, hooks] of Object.entries(hooksByEvent)) {
|
|
1221
|
+
if (!HOOK_EVENTS.includes(event)) {
|
|
1222
|
+
console.warn(`${TAG} unknown hook event from library: ${event}`);
|
|
1223
|
+
skipped += hooks.length;
|
|
1224
|
+
continue;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
for (const hook of hooks) {
|
|
1228
|
+
// Skip if a hook with this ID is already registered (built-in takes priority)
|
|
1229
|
+
const existing = (_registry.get(event) ?? []).find((h) => h.id === hook.id);
|
|
1230
|
+
if (existing) {
|
|
1231
|
+
skipped++;
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
registerHook(event, {
|
|
1236
|
+
id: hook.id,
|
|
1237
|
+
command: hook.command,
|
|
1238
|
+
description: hook.description ?? "",
|
|
1239
|
+
timeout: hook.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
1240
|
+
blocking: hook.blocking ?? false,
|
|
1241
|
+
sdks: hook.sdks ?? [SDK_WILDCARD],
|
|
1242
|
+
builtin: hook.builtin ?? false,
|
|
1243
|
+
retryable: hook.retryable ?? false,
|
|
1244
|
+
maxRetries: hook.maxRetries,
|
|
1245
|
+
env: hook.env,
|
|
1246
|
+
});
|
|
1247
|
+
registered++;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
if (registered > 0) {
|
|
1252
|
+
console.log(`${TAG} registered ${registered} hook(s) from hook library`);
|
|
1253
|
+
}
|
|
1254
|
+
return { registered, skipped };
|
|
1255
|
+
}
|
package/agent/agent-pool.mjs
CHANGED
|
@@ -55,8 +55,7 @@ import {
|
|
|
55
55
|
MAX_STREAM_RETRIES,
|
|
56
56
|
} from "../infra/stream-resilience.mjs";
|
|
57
57
|
import { ensureTestRuntimeSandbox } from "../infra/test-runtime.mjs";
|
|
58
|
-
import {
|
|
59
|
-
import { resolveContextShreddingOptions } from "../config/context-shredding-config.mjs";
|
|
58
|
+
import { maybeCompressSessionItems } from "../workspace/context-cache.mjs";
|
|
60
59
|
|
|
61
60
|
// Lazy-load MCP registry to avoid circular dependencies.
|
|
62
61
|
// Cached at module scope per AGENTS.md hard rules.
|
|
@@ -110,7 +109,15 @@ function hasOptionalModule(specifier) {
|
|
|
110
109
|
require.resolve(specifier);
|
|
111
110
|
ok = true;
|
|
112
111
|
} catch {
|
|
113
|
-
|
|
112
|
+
// ESM-only packages have no CJS "require" export so require.resolve
|
|
113
|
+
// throws even when the package is installed. Fall back to checking
|
|
114
|
+
// whether the package directory exists on disk.
|
|
115
|
+
try {
|
|
116
|
+
const pkgDir = resolve(__dirname, "..", "node_modules", ...specifier.split("/"));
|
|
117
|
+
ok = existsSync(resolve(pkgDir, "package.json"));
|
|
118
|
+
} catch {
|
|
119
|
+
ok = false;
|
|
120
|
+
}
|
|
114
121
|
}
|
|
115
122
|
MODULE_PRESENCE_CACHE.set(specifier, ok);
|
|
116
123
|
return ok;
|
|
@@ -244,29 +251,12 @@ async function maybeCompressResultItems(
|
|
|
244
251
|
|
|
245
252
|
const resolvedSessionType = normalizeSessionType(sessionType, "task");
|
|
246
253
|
const agentType = normalizeSdkForShredding(sdk);
|
|
247
|
-
|
|
248
|
-
resolvedSessionType,
|
|
254
|
+
return maybeCompressSessionItems(items, {
|
|
255
|
+
sessionType: resolvedSessionType,
|
|
249
256
|
agentType,
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const usagePct = estimateContextUsagePct(items);
|
|
254
|
-
const threshold = Number.isFinite(shreddingOpts?.contextUsageThreshold)
|
|
255
|
-
? Number(shreddingOpts.contextUsageThreshold)
|
|
256
|
-
: 0.5;
|
|
257
|
-
if (usagePct < threshold) return items;
|
|
258
|
-
|
|
259
|
-
shreddingOpts.contextUsagePct = usagePct;
|
|
260
|
-
const compressedItems = await compressAllItems(items, shreddingOpts);
|
|
261
|
-
try {
|
|
262
|
-
const savings = estimateSavings(items, compressedItems);
|
|
263
|
-
if (savings.savedChars > 0) {
|
|
264
|
-
recordShreddingEvent({ ...savings, agentType: agentType || sdk });
|
|
265
|
-
}
|
|
266
|
-
} catch {
|
|
267
|
-
/* non-fatal */
|
|
268
|
-
}
|
|
269
|
-
return compressedItems;
|
|
257
|
+
force: forceCompression,
|
|
258
|
+
skip: skipCompression,
|
|
259
|
+
});
|
|
270
260
|
}
|
|
271
261
|
|
|
272
262
|
function resolveCodexStreamSafety(totalTimeoutMs) {
|
|
@@ -762,13 +752,14 @@ async function withTemporaryEnv(overrides, fn) {
|
|
|
762
752
|
* Otherwise strips OPENAI_BASE_URL so the SDK uses its default auth.
|
|
763
753
|
*/
|
|
764
754
|
function buildCodexSdkOptions(envInput = process.env) {
|
|
765
|
-
const
|
|
755
|
+
const resolved = resolveCodexProfileRuntime(envInput);
|
|
756
|
+
const { env: resolvedEnv, configProvider } = resolved;
|
|
766
757
|
const baseUrl = resolvedEnv.OPENAI_BASE_URL || "";
|
|
767
758
|
const isAzure = (() => {
|
|
768
759
|
try {
|
|
769
760
|
const parsed = new URL(baseUrl);
|
|
770
761
|
const host = String(parsed.hostname || "").toLowerCase();
|
|
771
|
-
return host === "openai.azure.com" || host.endsWith(".openai.azure.com");
|
|
762
|
+
return host === "openai.azure.com" || host.endsWith(".openai.azure.com") || host.endsWith(".cognitiveservices.azure.com");
|
|
772
763
|
} catch {
|
|
773
764
|
return false;
|
|
774
765
|
}
|
|
@@ -783,16 +774,20 @@ function buildCodexSdkOptions(envInput = process.env) {
|
|
|
783
774
|
if (env.OPENAI_API_KEY && !env.AZURE_OPENAI_API_KEY) {
|
|
784
775
|
env.AZURE_OPENAI_API_KEY = env.OPENAI_API_KEY;
|
|
785
776
|
}
|
|
777
|
+
// Use the config.toml provider section name and env_key when available,
|
|
778
|
+
// so the SDK config override is consistent with the user's config.toml.
|
|
779
|
+
const providerSectionName = configProvider?.name || "azure";
|
|
780
|
+
const providerEnvKey = configProvider?.envKey || "AZURE_OPENAI_API_KEY";
|
|
786
781
|
const azureModel = env.CODEX_MODEL || undefined;
|
|
787
782
|
return {
|
|
788
783
|
env,
|
|
789
784
|
config: {
|
|
790
|
-
model_provider:
|
|
785
|
+
model_provider: providerSectionName,
|
|
791
786
|
model_providers: {
|
|
792
|
-
|
|
787
|
+
[providerSectionName]: {
|
|
793
788
|
name: "Azure OpenAI",
|
|
794
789
|
base_url: baseUrl,
|
|
795
|
-
env_key:
|
|
790
|
+
env_key: providerEnvKey,
|
|
796
791
|
wire_api: "responses",
|
|
797
792
|
},
|
|
798
793
|
},
|
package/agent/agent-prompts.mjs
CHANGED
|
@@ -10,8 +10,12 @@ function normalizeTemplateValue(value) {
|
|
|
10
10
|
if (value == null) return "";
|
|
11
11
|
if (typeof value === "string") {
|
|
12
12
|
const text = value.trim();
|
|
13
|
-
if (
|
|
14
|
-
|
|
13
|
+
if (!text) return "";
|
|
14
|
+
const sanitized = text
|
|
15
|
+
.replace(/\{\{\s*[\w.-]+\s*\}\}/g, " ")
|
|
16
|
+
.replace(/[ \t]{2,}/g, " ")
|
|
17
|
+
.trim();
|
|
18
|
+
return sanitized;
|
|
15
19
|
}
|
|
16
20
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
17
21
|
return String(value);
|
package/agent/autofix.mjs
CHANGED
|
@@ -545,7 +545,7 @@ export function runCodexExec(
|
|
|
545
545
|
try {
|
|
546
546
|
const parsed = new URL(baseUrl);
|
|
547
547
|
const host = String(parsed.hostname || "").toLowerCase();
|
|
548
|
-
return host === "openai.azure.com" || host.endsWith(".openai.azure.com");
|
|
548
|
+
return host === "openai.azure.com" || host.endsWith(".openai.azure.com") || host.endsWith(".cognitiveservices.azure.com");
|
|
549
549
|
} catch {
|
|
550
550
|
return false;
|
|
551
551
|
}
|