bosun 0.42.2 → 0.42.4
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 +9 -0
- package/agent/agent-event-bus.mjs +10 -0
- package/agent/agent-supervisor.mjs +20 -0
- package/bosun-tui.mjs +107 -105
- package/cli.mjs +10 -0
- package/config/config.mjs +25 -0
- package/config/executor-config.mjs +124 -1
- package/infra/container-runner.mjs +565 -1
- package/infra/monitor.mjs +18 -0
- package/infra/tracing.mjs +544 -240
- package/infra/tui-bridge.mjs +13 -1
- package/kanban/kanban-adapter.mjs +128 -4
- package/lib/repo-map.mjs +114 -3
- package/package.json +11 -4
- package/server/ui-server.mjs +3 -0
- package/task/task-archiver.mjs +18 -6
- package/task/task-attachments.mjs +14 -10
- package/task/task-cli.mjs +24 -4
- package/task/task-executor.mjs +19 -0
- package/task/task-store.mjs +194 -37
- package/telegram/telegram-bot.mjs +4 -1
- package/tui/app.mjs +131 -171
- package/tui/components/status-header.mjs +178 -75
- package/tui/lib/header-config.mjs +68 -0
- package/tui/lib/ws-bridge.mjs +61 -9
- package/tui/screens/agents.mjs +127 -0
- package/tui/screens/tasks.mjs +1 -48
- package/ui/app.js +8 -5
- package/ui/components/kanban-board.js +65 -3
- package/ui/components/session-list.js +18 -32
- package/ui/demo-defaults.js +52 -2
- package/ui/modules/session-api.js +100 -0
- package/ui/modules/state.js +71 -15
- package/ui/tabs/workflows.js +25 -1
- package/ui/tui/App.js +298 -0
- package/ui/tui/TasksScreen.js +564 -0
- package/ui/tui/constants.js +55 -0
- package/ui/tui/tasks-screen-helpers.js +301 -0
- package/ui/tui/useTasks.js +61 -0
- package/ui/tui/useWebSocket.js +166 -0
- package/ui/tui/useWorkflows.js +30 -0
- package/workflow/workflow-engine.mjs +412 -7
- package/workflow/workflow-nodes.mjs +616 -75
- package/workflow-templates/agents.mjs +3 -0
- package/workflow-templates/planning.mjs +7 -0
- package/workflow-templates/sub-workflows.mjs +5 -0
- package/workflow-templates/task-execution.mjs +3 -0
- package/workspace/command-diagnostics.mjs +1 -1
- package/workspace/context-cache.mjs +182 -9
|
@@ -534,6 +534,8 @@ export const BACKEND_AGENT_TEMPLATE = (() => {
|
|
|
534
534
|
node("plan-work", "agent.run_planner", "Plan Implementation", {
|
|
535
535
|
prompt: "Analyze the task requirements and create a step-by-step implementation plan. Identify which files need to be modified, what tests need to be written, and any API contracts to maintain.",
|
|
536
536
|
outputVariable: "plan",
|
|
537
|
+
repoMapQuery: "{{taskTitle}} {{taskDescription}}",
|
|
538
|
+
repoMapFileLimit: 8,
|
|
537
539
|
}, { x: 400, y: 180 }),
|
|
538
540
|
|
|
539
541
|
node("write-tests", "action.run_agent", "Write Tests First", {
|
|
@@ -1045,3 +1047,4 @@ export const MEETING_SUBWORKFLOW_CHAIN_TEMPLATE = {
|
|
|
1045
1047
|
requiredTemplates: ["template-task-planner"],
|
|
1046
1048
|
},
|
|
1047
1049
|
};
|
|
1050
|
+
|
|
@@ -64,6 +64,8 @@ export const TASK_PLANNER_TEMPLATE = {
|
|
|
64
64
|
taskCount: "{{taskCount}}",
|
|
65
65
|
context: "{{plannerContext}}",
|
|
66
66
|
prompt: "{{prompt}}",
|
|
67
|
+
repoMapQuery: "{{plannerContext}} {{prompt}}",
|
|
68
|
+
repoMapFileLimit: 8,
|
|
67
69
|
dedup: true,
|
|
68
70
|
timeoutMs: 960000,
|
|
69
71
|
agentTimeoutMs: 900000,
|
|
@@ -182,6 +184,8 @@ export const TASK_REPLENISH_TEMPLATE = {
|
|
|
182
184
|
taskCount: "{{taskCount}}",
|
|
183
185
|
context: "{{plannerContext}}",
|
|
184
186
|
prompt: "{{prompt}}",
|
|
187
|
+
repoMapQuery: "{{plannerContext}} {{prompt}}",
|
|
188
|
+
repoMapFileLimit: 8,
|
|
185
189
|
timeoutMs: 960000,
|
|
186
190
|
agentTimeoutMs: 900000,
|
|
187
191
|
maxRetries: 0,
|
|
@@ -988,3 +992,6 @@ Rules:
|
|
|
988
992
|
},
|
|
989
993
|
},
|
|
990
994
|
};
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
|
|
@@ -143,6 +143,7 @@ export function makeAgentPlanExecuteVerifySub(opts) {
|
|
|
143
143
|
mode: "plan",
|
|
144
144
|
executionRole: "architect",
|
|
145
145
|
repoMapQuery: "{{taskTitle}} {{taskDescription}}",
|
|
146
|
+
repoMapFileLimit: 8,
|
|
146
147
|
...defaults,
|
|
147
148
|
}, { x: 400, y: 0 }),
|
|
148
149
|
node("implement", "action.run_agent", "Implement", {
|
|
@@ -150,6 +151,7 @@ export function makeAgentPlanExecuteVerifySub(opts) {
|
|
|
150
151
|
executionRole: "editor",
|
|
151
152
|
architectPlan: "{{$ctx.getNodeOutput('plan')?.summary || $ctx.getNodeOutput('plan')?.output || ''}}",
|
|
152
153
|
repoMapQuery: "{{taskTitle}} {{taskDescription}}",
|
|
154
|
+
repoMapFileLimit: 8,
|
|
153
155
|
...defaults,
|
|
154
156
|
}, { x: 400, y: 160 }),
|
|
155
157
|
node("verify", "action.run_agent", "Verify", {
|
|
@@ -157,6 +159,7 @@ export function makeAgentPlanExecuteVerifySub(opts) {
|
|
|
157
159
|
executionRole: "editor",
|
|
158
160
|
architectPlan: "{{$ctx.getNodeOutput('plan')?.summary || $ctx.getNodeOutput('plan')?.output || ''}}",
|
|
159
161
|
repoMapQuery: "{{taskTitle}} {{taskDescription}}",
|
|
162
|
+
repoMapFileLimit: 8,
|
|
160
163
|
...defaults,
|
|
161
164
|
}, { x: 400, y: 320 }),
|
|
162
165
|
],
|
|
@@ -241,3 +244,5 @@ export const PR_CHECK_HANDOFF_SUB = subWorkflow(
|
|
|
241
244
|
},
|
|
242
245
|
);
|
|
243
246
|
|
|
247
|
+
|
|
248
|
+
|
|
@@ -39,6 +39,7 @@ function withArchitectEditorPhases(phases = []) {
|
|
|
39
39
|
...phase,
|
|
40
40
|
extra: {
|
|
41
41
|
repoMapQuery: "{{taskTitle}} {{taskDescription}}",
|
|
42
|
+
repoMapFileLimit: 8,
|
|
42
43
|
...(index === 0
|
|
43
44
|
? { mode: "plan", executionRole: "architect" }
|
|
44
45
|
: { executionRole: "editor", architectPlan }),
|
|
@@ -422,3 +423,5 @@ Commit changes with descriptive messages.`,
|
|
|
422
423
|
});
|
|
423
424
|
|
|
424
425
|
|
|
426
|
+
|
|
427
|
+
|
|
@@ -124,7 +124,7 @@ function normalizeCommandSignature(command = "", args = []) {
|
|
|
124
124
|
|
|
125
125
|
function resolveCommandKind(commandLine = "", output = "") {
|
|
126
126
|
const lower = `${commandLine}\n${output}`.toLowerCase();
|
|
127
|
-
if (/\bdotnet\s+test\b/.test(lower)) return { family: "
|
|
127
|
+
if (/\bdotnet\s+test\b/.test(lower)) return { family: "build", runner: "dotnet-test" };
|
|
128
128
|
if (/\b(?:python(?:3)?\s+-m\s+pytest|pytest)\b/.test(lower)) return { family: "test", runner: "pytest" };
|
|
129
129
|
if (/\bvitest\b/.test(lower)) return { family: "test", runner: "vitest" };
|
|
130
130
|
if (/\bjest\b/.test(lower)) return { family: "test", runner: "jest" };
|
|
@@ -515,7 +515,43 @@ function normalizeCommandToken(token) {
|
|
|
515
515
|
}
|
|
516
516
|
|
|
517
517
|
function tokenizeCommandLine(commandLine) {
|
|
518
|
-
|
|
518
|
+
const input = String(commandLine || "");
|
|
519
|
+
const tokens = [];
|
|
520
|
+
let index = 0;
|
|
521
|
+
|
|
522
|
+
while (index < input.length) {
|
|
523
|
+
while (index < input.length) {
|
|
524
|
+
const code = input.charCodeAt(index);
|
|
525
|
+
if (code !== 9 && code !== 10 && code !== 11 && code !== 12 && code !== 13 && code !== 32) break;
|
|
526
|
+
index += 1;
|
|
527
|
+
}
|
|
528
|
+
if (index >= input.length) break;
|
|
529
|
+
|
|
530
|
+
const quote = input[index];
|
|
531
|
+
if (quote === '"' || quote === "'") {
|
|
532
|
+
const start = index;
|
|
533
|
+
index += 1;
|
|
534
|
+
while (index < input.length && input[index] !== quote) {
|
|
535
|
+
index += 1;
|
|
536
|
+
}
|
|
537
|
+
if (index < input.length) {
|
|
538
|
+
index += 1;
|
|
539
|
+
tokens.push(input.slice(start, index));
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
index = start;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const start = index;
|
|
546
|
+
while (index < input.length) {
|
|
547
|
+
const code = input.charCodeAt(index);
|
|
548
|
+
if (code === 9 || code === 10 || code === 11 || code === 12 || code === 13 || code === 32) break;
|
|
549
|
+
index += 1;
|
|
550
|
+
}
|
|
551
|
+
tokens.push(input.slice(start, index));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return tokens;
|
|
519
555
|
}
|
|
520
556
|
|
|
521
557
|
function extractNestedCommandToken(token) {
|
|
@@ -601,22 +637,120 @@ function isLikelyStructuredOutput(text, item) {
|
|
|
601
637
|
);
|
|
602
638
|
}
|
|
603
639
|
|
|
640
|
+
function hasSourceDiagnosticLine(text) {
|
|
641
|
+
const lines = String(text || "").split(/\r?\n/);
|
|
642
|
+
for (const rawLine of lines) {
|
|
643
|
+
const line = rawLine.trim();
|
|
644
|
+
if (!line) continue;
|
|
645
|
+
|
|
646
|
+
const severityMatch = /\b(?:error|warning)\b/i.exec(line);
|
|
647
|
+
if (!severityMatch) continue;
|
|
648
|
+
|
|
649
|
+
const prefix = line.slice(0, severityMatch.index).trimEnd();
|
|
650
|
+
if (!prefix) continue;
|
|
651
|
+
|
|
652
|
+
const colonIndex = prefix.indexOf(":");
|
|
653
|
+
const filePart = (colonIndex === -1 ? prefix : prefix.slice(0, colonIndex)).trim();
|
|
654
|
+
if (!/\.(?:cs|fs|vb|ts|tsx|js|jsx|java|kt|go|rs|cpp|c|h|hpp)$/i.test(filePart)) continue;
|
|
655
|
+
|
|
656
|
+
if (colonIndex !== -1) {
|
|
657
|
+
const location = prefix.slice(colonIndex + 1).trim();
|
|
658
|
+
if (location && !/^\d+(?::\d+)?$/.test(location)) continue;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function hasBuildDiagnosticSignals(item) {
|
|
668
|
+
const text = [item?.aggregated_output, item?.output, item?.text]
|
|
669
|
+
.filter((value) => typeof value === "string" && value.trim())
|
|
670
|
+
.join("\n");
|
|
671
|
+
if (!text) return false;
|
|
672
|
+
return (
|
|
673
|
+
/\b(?:error|warning)\s+(?:TS|CS|MSB|NU)\d+\b/i.test(text)
|
|
674
|
+
|| hasSourceDiagnosticLine(text)
|
|
675
|
+
|| /\b(?:Build FAILED|Cannot find name|not assignable to type|The build failed\.)\b/i.test(text)
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
|
|
604
679
|
function classifyLiveFamily(commandFamily, item) {
|
|
605
|
-
const cmd = String(commandFamily || "");
|
|
606
|
-
const
|
|
680
|
+
const cmd = String(commandFamily || "").trim().toLowerCase();
|
|
681
|
+
const commandLine = extractCommandLine(item);
|
|
682
|
+
const full = `${cmd} ${commandLine}`.toLowerCase();
|
|
683
|
+
const hasBuildDiagnostics = hasBuildDiagnosticSignals(item);
|
|
684
|
+
|
|
607
685
|
if (["grep", "rg", "find", "findstr", "select-string", "ag", "ack", "sift", "fd", "where", "which", "ls", "dir", "tree", "gci", "get-childitem"].includes(cmd) || /git\s+grep\b/.test(full)) return "search";
|
|
608
686
|
if (cmd === "git") return "git";
|
|
609
|
-
|
|
610
|
-
if (
|
|
611
|
-
|
|
687
|
+
|
|
688
|
+
if (/\b(dotnet|go|pytest|cargo|gradle|maven|mvn|javac|tsc|jest|vitest|deno|make|cmake|bazel|buck|nx|turbo|rush|msbuild|xunit|nunit|ctest)\b/.test(full)) {
|
|
689
|
+
if (/\b(test|tests|pytest|vitest|jest|xunit|nunit|ctest)\b/.test(full)) return hasBuildDiagnostics ? "build" : "test";
|
|
690
|
+
if (/\b(publish|deploy|release|ship|rollout|upgrade)\b/.test(full)) return "deploy";
|
|
691
|
+
return "build";
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (["npm", "pnpm", "yarn", "bun", "npx", "pip", "pip3", "poetry", "composer", "bundle"].includes(cmd)) {
|
|
695
|
+
if (/\b(install|add|update|upgrade|remove|uninstall|ci|dedupe|audit|outdated|list)\b/.test(full)) return "package-manager";
|
|
696
|
+
if (/\b(test|coverage|jest|vitest|mocha|ava|unittest)\b/.test(full)) return hasBuildDiagnostics ? "build" : "test";
|
|
697
|
+
if (/\b(build|compile|bundle|pack|publish)\b/.test(full)) return "build";
|
|
612
698
|
}
|
|
699
|
+
|
|
613
700
|
if (["journalctl", "tail", "get-content"].includes(cmd)) return "logs";
|
|
614
701
|
if (["docker", "kubectl"].includes(cmd) && /logs?\b|tail\b|follow\b|-f\b/.test(full)) return "logs";
|
|
615
|
-
if (["docker", "kubectl", "helm", "terraform", "ansible", "ansible-playbook", "systemctl"].includes(cmd))
|
|
702
|
+
if (["docker", "kubectl", "helm", "terraform", "ansible", "ansible-playbook", "systemctl"].includes(cmd)) {
|
|
703
|
+
if (/\b(up|apply|deploy|rollout|upgrade|install|release|push|publish|compose)\b/.test(full)) return "deploy";
|
|
704
|
+
return "ops";
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (hasBuildDiagnostics) return "build";
|
|
708
|
+
|
|
616
709
|
return "generic";
|
|
617
710
|
}
|
|
618
711
|
|
|
619
|
-
function
|
|
712
|
+
function chooseLiveBudgetPolicy(family, analysis, opts) {
|
|
713
|
+
const targetChars = Math.max(200, Number(opts?.liveToolCompactionTargetChars) || 1800);
|
|
714
|
+
const lineCount = Number(analysis?.lineCount) || 0;
|
|
715
|
+
const savedPct = Number(analysis?.savedPct) || 0;
|
|
716
|
+
const policy = {
|
|
717
|
+
family,
|
|
718
|
+
reason: "inline_summary",
|
|
719
|
+
budget: {
|
|
720
|
+
targetChars,
|
|
721
|
+
decision: "inline_summary",
|
|
722
|
+
retrievable: false,
|
|
723
|
+
},
|
|
724
|
+
why: [`family:${family}`, `lines:${lineCount}`, `savedPct:${savedPct}`],
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
if (["package-manager", "deploy", "logs"].includes(family)) {
|
|
728
|
+
policy.reason = "artifact_summary";
|
|
729
|
+
policy.budget.decision = "artifact_summary";
|
|
730
|
+
policy.budget.retrievable = true;
|
|
731
|
+
policy.why.push("large-noisy-output");
|
|
732
|
+
} else if (family === "git") {
|
|
733
|
+
policy.reason = "structured_delta";
|
|
734
|
+
policy.budget.decision = "structured_delta";
|
|
735
|
+
policy.budget.retrievable = true;
|
|
736
|
+
policy.why.push("delta-friendly-family");
|
|
737
|
+
} else if (family === "search") {
|
|
738
|
+
policy.reason = "inline_excerpt";
|
|
739
|
+
policy.budget.decision = "inline_excerpt";
|
|
740
|
+
policy.why.push("match-oriented-family");
|
|
741
|
+
} else if (family === "test") {
|
|
742
|
+
policy.reason = "inline_summary";
|
|
743
|
+
policy.budget.decision = "inline_summary";
|
|
744
|
+
policy.why.push("failure-signals-prioritized");
|
|
745
|
+
} else if (family === "build") {
|
|
746
|
+
policy.reason = "summary_with_delta";
|
|
747
|
+
policy.budget.decision = "summary_with_delta";
|
|
748
|
+
policy.budget.retrievable = true;
|
|
749
|
+
policy.why.push("diagnostics-over-progress");
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return policy;
|
|
753
|
+
}function collectSignalIndices(lines, predicate, radius = 0, limit = Infinity) {
|
|
620
754
|
const out = new Set();
|
|
621
755
|
let count = 0;
|
|
622
756
|
for (let index = 0; index < lines.length; index += 1) {
|
|
@@ -1026,6 +1160,7 @@ async function compactStandaloneToolItem(
|
|
|
1026
1160
|
}
|
|
1027
1161
|
|
|
1028
1162
|
const analysis = analyzeLiveToolOutput(item, opts);
|
|
1163
|
+
const liveBudgetPolicy = analysis ? chooseLiveBudgetPolicy(analysis.family, analysis, opts) : null;
|
|
1029
1164
|
if (analysis) {
|
|
1030
1165
|
const logId = await writeToCache(item, extractToolName(item), extractArgsPreview(item));
|
|
1031
1166
|
const diagnostic = await analyzeCommandDiagnosticForItem(item, logId);
|
|
@@ -1035,6 +1170,7 @@ async function compactStandaloneToolItem(
|
|
|
1035
1170
|
_liveCompacted: true,
|
|
1036
1171
|
_liveCompactionFamily: analysis.family,
|
|
1037
1172
|
_liveCompactionCommandFamily: analysis.commandFamily,
|
|
1173
|
+
_liveCompactionPolicy: liveBudgetPolicy,
|
|
1038
1174
|
};
|
|
1039
1175
|
if (diagnostic) compactedItem._commandDiagnostics = diagnostic;
|
|
1040
1176
|
setItemText(
|
|
@@ -1066,6 +1202,16 @@ async function compactStandaloneToolItem(
|
|
|
1066
1202
|
_liveCompacted: true,
|
|
1067
1203
|
_liveCompactionFamily: "generic",
|
|
1068
1204
|
_liveCompactionCommandFamily: extractCommandFamily(item),
|
|
1205
|
+
_liveCompactionPolicy: {
|
|
1206
|
+
family: "generic",
|
|
1207
|
+
reason: "signal_excerpt",
|
|
1208
|
+
budget: {
|
|
1209
|
+
targetChars: Math.max(200, Number(opts?.liveToolCompactionTargetChars) || 1800),
|
|
1210
|
+
decision: "inline_excerpt",
|
|
1211
|
+
retrievable: true,
|
|
1212
|
+
},
|
|
1213
|
+
why: ["family:generic", "fallback-signal-excerpt"],
|
|
1214
|
+
},
|
|
1069
1215
|
};
|
|
1070
1216
|
if (diagnostic) compactedItem._commandDiagnostics = diagnostic;
|
|
1071
1217
|
setItemText(
|
|
@@ -1322,6 +1468,16 @@ function applyImmediateGitCompression(item, logId, opts) {
|
|
|
1322
1468
|
_liveCompacted: true,
|
|
1323
1469
|
_liveCompactionFamily: "git",
|
|
1324
1470
|
_liveCompactionCommandFamily: extractCommandFamily(item),
|
|
1471
|
+
_liveCompactionPolicy: {
|
|
1472
|
+
family: "generic",
|
|
1473
|
+
reason: "signal_excerpt",
|
|
1474
|
+
budget: {
|
|
1475
|
+
targetChars: Math.max(200, Number(opts?.liveToolCompactionTargetChars) || 1800),
|
|
1476
|
+
decision: "inline_excerpt",
|
|
1477
|
+
retrievable: true,
|
|
1478
|
+
},
|
|
1479
|
+
why: ["family:generic", "fallback-signal-excerpt"],
|
|
1480
|
+
},
|
|
1325
1481
|
};
|
|
1326
1482
|
setItemText(compressed, compressImmediateGitText(getItemText(item), logId, opts));
|
|
1327
1483
|
return compressed;
|
|
@@ -2797,7 +2953,19 @@ export async function maybeCompressSessionItems(
|
|
|
2797
2953
|
"../config/context-shredding-config.mjs"
|
|
2798
2954
|
);
|
|
2799
2955
|
|
|
2800
|
-
const shreddingOpts =
|
|
2956
|
+
const shreddingOpts = {
|
|
2957
|
+
...resolveContextShreddingOptions(sessionType, agentType),
|
|
2958
|
+
...Object.fromEntries(Object.entries({
|
|
2959
|
+
liveToolCompactionEnabled: arguments[1]?.liveToolCompactionEnabled,
|
|
2960
|
+
liveToolCompactionMode: arguments[1]?.liveToolCompactionMode,
|
|
2961
|
+
liveToolCompactionMinChars: arguments[1]?.liveToolCompactionMinChars,
|
|
2962
|
+
liveToolCompactionTargetChars: arguments[1]?.liveToolCompactionTargetChars,
|
|
2963
|
+
liveToolCompactionMinSavingsPct: arguments[1]?.liveToolCompactionMinSavingsPct,
|
|
2964
|
+
liveToolCompactionMinRuntimeMs: arguments[1]?.liveToolCompactionMinRuntimeMs,
|
|
2965
|
+
liveToolCompactionBlockStructured: arguments[1]?.liveToolCompactionBlockStructured,
|
|
2966
|
+
liveToolCompactionAllowCommands: arguments[1]?.liveToolCompactionAllowCommands,
|
|
2967
|
+
}).filter(([, value]) => value !== undefined)),
|
|
2968
|
+
};
|
|
2801
2969
|
if (shreddingOpts?._skip === true && !force) return items;
|
|
2802
2970
|
|
|
2803
2971
|
const usagePct = estimateContextUsagePct(items);
|
|
@@ -2830,3 +2998,8 @@ export async function maybeCompressSessionItems(
|
|
|
2830
2998
|
|
|
2831
2999
|
return compressedItems;
|
|
2832
3000
|
}
|
|
3001
|
+
|
|
3002
|
+
|
|
3003
|
+
|
|
3004
|
+
|
|
3005
|
+
|