bosun 0.28.0 → 0.28.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 +22 -1
- package/README.md +16 -5
- package/agent-pool.mjs +16 -2
- package/agent-work-analyzer.mjs +419 -0
- package/claude-shell.mjs +12 -2
- package/desktop/launch.mjs +31 -2
- package/desktop/main.mjs +132 -10
- package/desktop/package.json +8 -0
- package/desktop-shortcut.mjs +20 -1
- package/monitor.mjs +151 -0
- package/package.json +3 -2
- package/rotate-agent-logs.sh +8 -7
- package/setup.mjs +759 -59
- package/task-executor.mjs +309 -12
- package/telegram-bot.mjs +419 -5
- package/ui/app.js +2 -0
- package/ui/components/shared.js +31 -2
- package/ui/demo.html +8 -0
- package/ui/modules/icons.js +14 -0
- package/ui/modules/router.js +1 -0
- package/ui/modules/settings-schema.js +11 -1
- package/ui/modules/state.js +59 -0
- package/ui/styles/components.css +60 -8
- package/ui/styles.css +88 -0
- package/ui/tabs/tasks.js +110 -72
- package/ui/tabs/telemetry.js +167 -0
- package/ui-server.mjs +158 -0
- package/workspace-manager.mjs +45 -1
- package/ui/styles/workspace-switcher.css.bak +0 -693
package/task-executor.mjs
CHANGED
|
@@ -62,8 +62,13 @@ import {
|
|
|
62
62
|
} from "./task-store.mjs";
|
|
63
63
|
import { createErrorDetector } from "./error-detector.mjs";
|
|
64
64
|
import { getSessionTracker } from "./session-tracker.mjs";
|
|
65
|
-
import {
|
|
65
|
+
import {
|
|
66
|
+
getCompactDiffSummary,
|
|
67
|
+
getRecentCommits,
|
|
68
|
+
collectDiffStats,
|
|
69
|
+
} from "./diff-stats.mjs";
|
|
66
70
|
import { createAnomalyDetector } from "./anomaly-detector.mjs";
|
|
71
|
+
import { normalizeDedupKey } from "./utils.mjs";
|
|
67
72
|
import {
|
|
68
73
|
resolveExecutorForTask,
|
|
69
74
|
executorToSdk,
|
|
@@ -521,15 +526,46 @@ const AGENT_WORK_STREAM = resolve(AGENT_WORK_DIR, "agent-work-stream.jsonl");
|
|
|
521
526
|
const AGENT_WORK_ERRORS = resolve(AGENT_WORK_DIR, "agent-errors.jsonl");
|
|
522
527
|
const AGENT_WORK_METRICS = resolve(AGENT_WORK_DIR, "agent-metrics.jsonl");
|
|
523
528
|
const AGENT_WORK_SESSIONS = resolve(AGENT_WORK_DIR, "agent-sessions");
|
|
529
|
+
const INTERNAL_ANOMALY_SIGNAL_PATH = resolve(
|
|
530
|
+
__dirname,
|
|
531
|
+
"..",
|
|
532
|
+
".cache",
|
|
533
|
+
"anomaly-signals.json",
|
|
534
|
+
);
|
|
524
535
|
const agentWorkSessionStarts = new Map();
|
|
536
|
+
const attemptTelemetry = new Map();
|
|
525
537
|
const anomalyAbortTargets = new Map();
|
|
526
538
|
const internalAnomalyEnabled = process.env.BOSUN_INTERNAL_ANOMALY !== "false";
|
|
539
|
+
const TELEMETRY_SAMPLE_RATE = (() => {
|
|
540
|
+
const raw = Number(process.env.BOSUN_TELEMETRY_SAMPLE_RATE || "1");
|
|
541
|
+
if (!Number.isFinite(raw)) return 1;
|
|
542
|
+
return Math.min(1, Math.max(0, raw));
|
|
543
|
+
})();
|
|
527
544
|
const anomalyDetector = internalAnomalyEnabled
|
|
528
545
|
? createAnomalyDetector({
|
|
529
546
|
onAnomaly: (anomaly) => {
|
|
530
547
|
console.warn(
|
|
531
548
|
`${TAG} anomaly ${anomaly.severity} ${anomaly.type} [${anomaly.shortId}]: ${anomaly.message}`,
|
|
532
549
|
);
|
|
550
|
+
writeInternalAnomalySignal(anomaly);
|
|
551
|
+
const notifyFn = globalThis.__bosunNotifyAnomaly;
|
|
552
|
+
if (typeof notifyFn === "function") {
|
|
553
|
+
try {
|
|
554
|
+
notifyFn(anomaly);
|
|
555
|
+
} catch {
|
|
556
|
+
/* best effort */
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (anomaly?.processId && attemptTelemetry.has(anomaly.processId)) {
|
|
560
|
+
const telemetry = attemptTelemetry.get(anomaly.processId);
|
|
561
|
+
telemetry.anomaly_actions = telemetry.anomaly_actions || [];
|
|
562
|
+
telemetry.anomaly_actions.push({
|
|
563
|
+
type: anomaly.type,
|
|
564
|
+
action: anomaly.action,
|
|
565
|
+
severity: anomaly.severity,
|
|
566
|
+
timestamp: Date.now(),
|
|
567
|
+
});
|
|
568
|
+
}
|
|
533
569
|
if (anomaly.action === "kill" || anomaly.action === "restart") {
|
|
534
570
|
const target = anomalyAbortTargets.get(anomaly.processId);
|
|
535
571
|
if (target && !target.signal.aborted) {
|
|
@@ -550,6 +586,14 @@ function ensureAgentWorkDirs() {
|
|
|
550
586
|
}
|
|
551
587
|
|
|
552
588
|
function writeAgentWorkEvent(entry) {
|
|
589
|
+
const eventType = String(entry?.event_type || "");
|
|
590
|
+
const shouldSample =
|
|
591
|
+
TELEMETRY_SAMPLE_RATE < 1 &&
|
|
592
|
+
["agent_output", "tool_call", "tool_result", "usage"].includes(eventType);
|
|
593
|
+
if (shouldSample && Math.random() > TELEMETRY_SAMPLE_RATE) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
553
597
|
ensureAgentWorkDirs();
|
|
554
598
|
const line = JSON.stringify(entry);
|
|
555
599
|
appendFileSync(AGENT_WORK_STREAM, `${line}\n`, "utf8");
|
|
@@ -562,6 +606,113 @@ function writeAgentWorkEvent(entry) {
|
|
|
562
606
|
}
|
|
563
607
|
}
|
|
564
608
|
|
|
609
|
+
function getAttemptTelemetry(attemptId) {
|
|
610
|
+
if (!attemptTelemetry.has(attemptId)) {
|
|
611
|
+
attemptTelemetry.set(attemptId, {
|
|
612
|
+
tool_calls: 0,
|
|
613
|
+
tool_results: 0,
|
|
614
|
+
tool_failures: 0,
|
|
615
|
+
commands: 0,
|
|
616
|
+
command_failures: 0,
|
|
617
|
+
permission_requests: 0,
|
|
618
|
+
permission_denied: 0,
|
|
619
|
+
prompt_tokens: 0,
|
|
620
|
+
completion_tokens: 0,
|
|
621
|
+
total_tokens: 0,
|
|
622
|
+
cost_usd: 0,
|
|
623
|
+
model: null,
|
|
624
|
+
anomaly_actions: [],
|
|
625
|
+
auto_resume: false,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
return attemptTelemetry.get(attemptId);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function applyUsage(telemetry, usage) {
|
|
632
|
+
if (!usage) return;
|
|
633
|
+
const prompt =
|
|
634
|
+
usage.prompt_tokens ??
|
|
635
|
+
usage.input_tokens ??
|
|
636
|
+
usage.tokens_in ??
|
|
637
|
+
usage.promptTokens ??
|
|
638
|
+
0;
|
|
639
|
+
const completion =
|
|
640
|
+
usage.completion_tokens ??
|
|
641
|
+
usage.output_tokens ??
|
|
642
|
+
usage.tokens_out ??
|
|
643
|
+
usage.completionTokens ??
|
|
644
|
+
0;
|
|
645
|
+
const total =
|
|
646
|
+
usage.total_tokens ??
|
|
647
|
+
usage.totalTokens ??
|
|
648
|
+
usage.tokens ??
|
|
649
|
+
(prompt + completion);
|
|
650
|
+
if (prompt) telemetry.prompt_tokens += Number(prompt) || 0;
|
|
651
|
+
if (completion) telemetry.completion_tokens += Number(completion) || 0;
|
|
652
|
+
if (total) telemetry.total_tokens += Number(total) || 0;
|
|
653
|
+
const cost =
|
|
654
|
+
usage.cost_usd ??
|
|
655
|
+
usage.cost ??
|
|
656
|
+
usage.total_cost ??
|
|
657
|
+
usage.usd ??
|
|
658
|
+
0;
|
|
659
|
+
if (cost) telemetry.cost_usd += Number(cost) || 0;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function extractUsageFromEvent(event) {
|
|
663
|
+
return (
|
|
664
|
+
event?.usage ||
|
|
665
|
+
event?.data?.usage ||
|
|
666
|
+
event?.item?.usage ||
|
|
667
|
+
event?.message?.usage ||
|
|
668
|
+
event?.response?.usage ||
|
|
669
|
+
event?.metrics?.usage ||
|
|
670
|
+
event?.data?.metrics?.usage ||
|
|
671
|
+
event?.result?.usage ||
|
|
672
|
+
null
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function classifyError(errorDetector, message, stderr) {
|
|
677
|
+
if (!errorDetector) return { category: "unknown", confidence: 0 };
|
|
678
|
+
const result = errorDetector.classify(message || "", stderr || "");
|
|
679
|
+
return {
|
|
680
|
+
category: result.pattern || "unknown",
|
|
681
|
+
confidence: result.confidence || 0,
|
|
682
|
+
details: result.details,
|
|
683
|
+
rawMatch: result.rawMatch,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function writeInternalAnomalySignal(anomaly) {
|
|
688
|
+
try {
|
|
689
|
+
const dir = resolve(__dirname, "..", ".cache");
|
|
690
|
+
mkdirSync(dir, { recursive: true });
|
|
691
|
+
let signals = [];
|
|
692
|
+
try {
|
|
693
|
+
const raw = readFileSync(INTERNAL_ANOMALY_SIGNAL_PATH, "utf8");
|
|
694
|
+
signals = JSON.parse(raw);
|
|
695
|
+
if (!Array.isArray(signals)) signals = [];
|
|
696
|
+
} catch {
|
|
697
|
+
/* ignore */
|
|
698
|
+
}
|
|
699
|
+
signals.push({
|
|
700
|
+
source: "internal",
|
|
701
|
+
type: anomaly.type,
|
|
702
|
+
severity: anomaly.severity,
|
|
703
|
+
action: anomaly.action,
|
|
704
|
+
shortId: anomaly.shortId,
|
|
705
|
+
processId: anomaly.processId,
|
|
706
|
+
message: anomaly.message,
|
|
707
|
+
timestamp: new Date().toISOString(),
|
|
708
|
+
});
|
|
709
|
+
if (signals.length > 50) signals = signals.slice(-50);
|
|
710
|
+
writeFileSync(INTERNAL_ANOMALY_SIGNAL_PATH, JSON.stringify(signals, null, 2));
|
|
711
|
+
} catch {
|
|
712
|
+
/* best effort */
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
565
716
|
function startAgentWorkSession({ attemptId, taskMeta, executorInfo, gitContext, prompt }) {
|
|
566
717
|
agentWorkSessionStarts.set(attemptId, Date.now());
|
|
567
718
|
writeAgentWorkEvent({
|
|
@@ -575,9 +726,21 @@ function startAgentWorkSession({ attemptId, taskMeta, executorInfo, gitContext,
|
|
|
575
726
|
});
|
|
576
727
|
}
|
|
577
728
|
|
|
578
|
-
function stopAgentWorkSession({
|
|
729
|
+
function stopAgentWorkSession({
|
|
730
|
+
attemptId,
|
|
731
|
+
status,
|
|
732
|
+
executorInfo,
|
|
733
|
+
taskMeta,
|
|
734
|
+
gitContext,
|
|
735
|
+
metrics,
|
|
736
|
+
}) {
|
|
579
737
|
const startedAt = agentWorkSessionStarts.get(attemptId);
|
|
580
738
|
const durationMs = startedAt ? Date.now() - startedAt : null;
|
|
739
|
+
const telemetry = attemptTelemetry.get(attemptId) || {};
|
|
740
|
+
const mergedMetrics = {
|
|
741
|
+
...telemetry,
|
|
742
|
+
...metrics,
|
|
743
|
+
};
|
|
581
744
|
writeAgentWorkEvent({
|
|
582
745
|
timestamp: new Date().toISOString(),
|
|
583
746
|
attempt_id: attemptId,
|
|
@@ -585,14 +748,14 @@ function stopAgentWorkSession({ attemptId, status, executorInfo, taskMeta, gitCo
|
|
|
585
748
|
data: {
|
|
586
749
|
completion_status: status,
|
|
587
750
|
duration_ms: durationMs ?? undefined,
|
|
588
|
-
...
|
|
751
|
+
...mergedMetrics,
|
|
589
752
|
},
|
|
590
753
|
...taskMeta,
|
|
591
754
|
...executorInfo,
|
|
592
755
|
...gitContext,
|
|
593
756
|
});
|
|
594
757
|
|
|
595
|
-
if (
|
|
758
|
+
if (mergedMetrics) {
|
|
596
759
|
appendFileSync(
|
|
597
760
|
AGENT_WORK_METRICS,
|
|
598
761
|
`${JSON.stringify({
|
|
@@ -600,11 +763,12 @@ function stopAgentWorkSession({ attemptId, status, executorInfo, taskMeta, gitCo
|
|
|
600
763
|
attempt_id: attemptId,
|
|
601
764
|
...taskMeta,
|
|
602
765
|
...executorInfo,
|
|
603
|
-
metrics,
|
|
766
|
+
metrics: mergedMetrics,
|
|
604
767
|
})}\n`,
|
|
605
768
|
"utf8",
|
|
606
769
|
);
|
|
607
770
|
}
|
|
771
|
+
attemptTelemetry.delete(attemptId);
|
|
608
772
|
}
|
|
609
773
|
|
|
610
774
|
function synthesizeAnomalyLine(event) {
|
|
@@ -654,10 +818,12 @@ function createAgentLogStreamer({
|
|
|
654
818
|
taskMeta,
|
|
655
819
|
executorInfo,
|
|
656
820
|
gitContext,
|
|
821
|
+
errorDetector,
|
|
657
822
|
}) {
|
|
658
823
|
const shortId = taskId.substring(0, 8);
|
|
659
824
|
const logFile = resolve(AGENT_LOGS_DIR, `agent-${shortId}.log`);
|
|
660
825
|
const tracker = getSessionTracker();
|
|
826
|
+
const telemetry = getAttemptTelemetry(attemptId);
|
|
661
827
|
|
|
662
828
|
// Ensure log dir exists
|
|
663
829
|
try {
|
|
@@ -699,24 +865,53 @@ function createAgentLogStreamer({
|
|
|
699
865
|
|
|
700
866
|
try {
|
|
701
867
|
const ts = new Date().toISOString();
|
|
868
|
+
if (event?.model && !telemetry.model) telemetry.model = event.model;
|
|
869
|
+
if (event?.data?.model && !telemetry.model)
|
|
870
|
+
telemetry.model = event.data.model;
|
|
871
|
+
if (event?.message?.model && !telemetry.model)
|
|
872
|
+
telemetry.model = event.message.model;
|
|
873
|
+
|
|
874
|
+
const usage = extractUsageFromEvent(event);
|
|
875
|
+
applyUsage(telemetry, usage);
|
|
876
|
+
if (usage) {
|
|
877
|
+
writeAgentWorkEvent({
|
|
878
|
+
timestamp: ts,
|
|
879
|
+
attempt_id: attemptId,
|
|
880
|
+
event_type: "usage",
|
|
881
|
+
data: usage,
|
|
882
|
+
...taskMeta,
|
|
883
|
+
...executorInfo,
|
|
884
|
+
...gitContext,
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
if (String(event?.type || "").toLowerCase().includes("permission")) {
|
|
889
|
+
telemetry.permission_requests += 1;
|
|
890
|
+
if (String(event?.action || "").toLowerCase().includes("deny")) {
|
|
891
|
+
telemetry.permission_denied += 1;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
702
895
|
if (event.type === "item.completed" && event.item) {
|
|
703
896
|
const item = event.item;
|
|
704
897
|
if (item.type === "agent_message" && item.text) {
|
|
898
|
+
const text = item.text;
|
|
705
899
|
writeAgentWorkEvent({
|
|
706
900
|
timestamp: ts,
|
|
707
901
|
attempt_id: attemptId,
|
|
708
902
|
event_type: "agent_output",
|
|
709
|
-
data: { output:
|
|
903
|
+
data: { output: text, stream_type: "stdout" },
|
|
710
904
|
...taskMeta,
|
|
711
905
|
...executorInfo,
|
|
712
906
|
...gitContext,
|
|
713
907
|
});
|
|
714
908
|
appendFileSync(
|
|
715
909
|
logFile,
|
|
716
|
-
`[${ts}] AGENT: ${
|
|
910
|
+
`[${ts}] AGENT: ${text.slice(0, 2000)}\n`,
|
|
717
911
|
"utf8",
|
|
718
912
|
);
|
|
719
913
|
} else if (item.type === "function_call") {
|
|
914
|
+
telemetry.tool_calls += 1;
|
|
720
915
|
writeAgentWorkEvent({
|
|
721
916
|
timestamp: ts,
|
|
722
917
|
attempt_id: attemptId,
|
|
@@ -733,16 +928,29 @@ function createAgentLogStreamer({
|
|
|
733
928
|
);
|
|
734
929
|
} else if (item.type === "function_call_output") {
|
|
735
930
|
const out = (item.output || "").slice(0, 500);
|
|
931
|
+
telemetry.tool_results += 1;
|
|
932
|
+
const failed =
|
|
933
|
+
item.status === "failed" ||
|
|
934
|
+
item.success === false ||
|
|
935
|
+
String(item.output || "").toLowerCase().includes("error");
|
|
936
|
+
if (failed) telemetry.tool_failures += 1;
|
|
736
937
|
writeAgentWorkEvent({
|
|
737
938
|
timestamp: ts,
|
|
738
939
|
attempt_id: attemptId,
|
|
739
940
|
event_type: "tool_result",
|
|
740
|
-
data: {
|
|
941
|
+
data: {
|
|
942
|
+
tool_name: item.name,
|
|
943
|
+
result: item.output || "",
|
|
944
|
+
status: item.status || (failed ? "failed" : "completed"),
|
|
945
|
+
},
|
|
741
946
|
...taskMeta,
|
|
742
947
|
...executorInfo,
|
|
743
948
|
...gitContext,
|
|
744
949
|
});
|
|
745
950
|
appendFileSync(logFile, `[${ts}] RESULT: ${out}\n`, "utf8");
|
|
951
|
+
} else if (item.type === "commandExecution") {
|
|
952
|
+
telemetry.commands += 1;
|
|
953
|
+
if (item.status === "failed") telemetry.command_failures += 1;
|
|
746
954
|
} else {
|
|
747
955
|
appendFileSync(
|
|
748
956
|
logFile,
|
|
@@ -750,6 +958,46 @@ function createAgentLogStreamer({
|
|
|
750
958
|
"utf8",
|
|
751
959
|
);
|
|
752
960
|
}
|
|
961
|
+
} else if (event.type === "assistant.message") {
|
|
962
|
+
const content = event.data?.content || "";
|
|
963
|
+
if (content) {
|
|
964
|
+
writeAgentWorkEvent({
|
|
965
|
+
timestamp: ts,
|
|
966
|
+
attempt_id: attemptId,
|
|
967
|
+
event_type: "agent_output",
|
|
968
|
+
data: { output: content, stream_type: "stdout" },
|
|
969
|
+
...taskMeta,
|
|
970
|
+
...executorInfo,
|
|
971
|
+
...gitContext,
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
} else if (event.type === "assistant.message_delta") {
|
|
975
|
+
const delta = event.data?.deltaContent || "";
|
|
976
|
+
if (delta && delta.length > 40) {
|
|
977
|
+
writeAgentWorkEvent({
|
|
978
|
+
timestamp: ts,
|
|
979
|
+
attempt_id: attemptId,
|
|
980
|
+
event_type: "agent_output",
|
|
981
|
+
data: { output: delta, stream_type: "stdout", delta: true },
|
|
982
|
+
...taskMeta,
|
|
983
|
+
...executorInfo,
|
|
984
|
+
...gitContext,
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
} else if (event.type === "tool_call") {
|
|
988
|
+
telemetry.tool_calls += 1;
|
|
989
|
+
writeAgentWorkEvent({
|
|
990
|
+
timestamp: ts,
|
|
991
|
+
attempt_id: attemptId,
|
|
992
|
+
event_type: "tool_call",
|
|
993
|
+
data: {
|
|
994
|
+
tool_name: event.tool_name || event.data?.tool_name || "",
|
|
995
|
+
tool_params: event.tool_params || event.data?.tool_params || "",
|
|
996
|
+
},
|
|
997
|
+
...taskMeta,
|
|
998
|
+
...executorInfo,
|
|
999
|
+
...gitContext,
|
|
1000
|
+
});
|
|
753
1001
|
} else if (event.type === "item.created") {
|
|
754
1002
|
const item = event.item || {};
|
|
755
1003
|
appendFileSync(
|
|
@@ -763,12 +1011,44 @@ function createAgentLogStreamer({
|
|
|
763
1011
|
}
|
|
764
1012
|
|
|
765
1013
|
if (event.type === "error") {
|
|
1014
|
+
const errText = event.error || event.message || "unknown error";
|
|
1015
|
+
const classification = classifyError(errorDetector, errText);
|
|
1016
|
+
const fingerprint = normalizeDedupKey(errText).slice(0, 120);
|
|
1017
|
+
writeAgentWorkEvent({
|
|
1018
|
+
timestamp: ts,
|
|
1019
|
+
attempt_id: attemptId,
|
|
1020
|
+
event_type: "error",
|
|
1021
|
+
data: {
|
|
1022
|
+
error_message: errText,
|
|
1023
|
+
error_category: classification.category,
|
|
1024
|
+
error_fingerprint: fingerprint,
|
|
1025
|
+
error_confidence: classification.confidence,
|
|
1026
|
+
},
|
|
1027
|
+
...taskMeta,
|
|
1028
|
+
...executorInfo,
|
|
1029
|
+
...gitContext,
|
|
1030
|
+
});
|
|
1031
|
+
} else if (
|
|
1032
|
+
event?.item?.type === "commandExecution" &&
|
|
1033
|
+
event.item?.status === "failed"
|
|
1034
|
+
) {
|
|
1035
|
+
const errText =
|
|
1036
|
+
event.item?.error ||
|
|
1037
|
+
event.item?.stderr ||
|
|
1038
|
+
event.item?.message ||
|
|
1039
|
+
"command failed";
|
|
1040
|
+
const classification = classifyError(errorDetector, errText);
|
|
1041
|
+
const fingerprint = normalizeDedupKey(errText).slice(0, 120);
|
|
766
1042
|
writeAgentWorkEvent({
|
|
767
1043
|
timestamp: ts,
|
|
768
1044
|
attempt_id: attemptId,
|
|
769
1045
|
event_type: "error",
|
|
770
1046
|
data: {
|
|
771
|
-
error_message:
|
|
1047
|
+
error_message: errText,
|
|
1048
|
+
error_category: classification.category,
|
|
1049
|
+
error_fingerprint: fingerprint,
|
|
1050
|
+
error_confidence: classification.confidence,
|
|
1051
|
+
source: "commandExecution",
|
|
772
1052
|
},
|
|
773
1053
|
...taskMeta,
|
|
774
1054
|
...executorInfo,
|
|
@@ -3448,6 +3728,7 @@ class TaskExecutor {
|
|
|
3448
3728
|
taskMeta,
|
|
3449
3729
|
executorInfo,
|
|
3450
3730
|
gitContext,
|
|
3731
|
+
errorDetector: this._errorDetector,
|
|
3451
3732
|
}),
|
|
3452
3733
|
abortController: taskAbortController,
|
|
3453
3734
|
// When AbortController is replaced after idle_continue, update our reference
|
|
@@ -3534,6 +3815,8 @@ class TaskExecutor {
|
|
|
3534
3815
|
sessionMessages,
|
|
3535
3816
|
);
|
|
3536
3817
|
if (shouldAutoResume) {
|
|
3818
|
+
const telemetry = getAttemptTelemetry(attemptId);
|
|
3819
|
+
telemetry.auto_resume = true;
|
|
3537
3820
|
console.log(
|
|
3538
3821
|
`${TAG} completion validation: "${taskTitle}" reported success but appears incomplete — auto-resuming`,
|
|
3539
3822
|
);
|
|
@@ -3569,6 +3852,7 @@ class TaskExecutor {
|
|
|
3569
3852
|
taskMeta,
|
|
3570
3853
|
executorInfo,
|
|
3571
3854
|
gitContext,
|
|
3855
|
+
errorDetector: this._errorDetector,
|
|
3572
3856
|
}),
|
|
3573
3857
|
abortController: resumeAC,
|
|
3574
3858
|
onAbortControllerReplaced: (newAC) => {
|
|
@@ -3612,6 +3896,13 @@ class TaskExecutor {
|
|
|
3612
3896
|
}
|
|
3613
3897
|
}
|
|
3614
3898
|
|
|
3899
|
+
let diffStats = null;
|
|
3900
|
+
try {
|
|
3901
|
+
diffStats = collectDiffStats(wt.path);
|
|
3902
|
+
} catch {
|
|
3903
|
+
diffStats = null;
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3615
3906
|
stopAgentWorkSession({
|
|
3616
3907
|
attemptId,
|
|
3617
3908
|
status: validatedResult?.success ? "success" : "failed",
|
|
@@ -3619,11 +3910,17 @@ class TaskExecutor {
|
|
|
3619
3910
|
taskMeta,
|
|
3620
3911
|
gitContext,
|
|
3621
3912
|
metrics: {
|
|
3622
|
-
tool_calls: Array.isArray(validatedResult?.items)
|
|
3623
|
-
? validatedResult.items.length
|
|
3624
|
-
: undefined,
|
|
3625
3913
|
success: !!validatedResult?.success,
|
|
3626
3914
|
retry_count: Math.max(0, (validatedResult?.attempts || 1) - 1),
|
|
3915
|
+
attempts: validatedResult?.attempts || 1,
|
|
3916
|
+
continues: validatedResult?.continues || 0,
|
|
3917
|
+
first_shot_success:
|
|
3918
|
+
!!validatedResult?.success && (validatedResult?.attempts || 1) === 1,
|
|
3919
|
+
agent_made_commits: agentMadeNewCommits,
|
|
3920
|
+
diff_summary: diffStats?.formatted || null,
|
|
3921
|
+
diff_files_changed: diffStats?.totalFiles ?? null,
|
|
3922
|
+
diff_lines_added: diffStats?.totalAdditions ?? null,
|
|
3923
|
+
diff_lines_deleted: diffStats?.totalDeletions ?? null,
|
|
3627
3924
|
},
|
|
3628
3925
|
});
|
|
3629
3926
|
anomalyAbortTargets.delete(attemptId);
|