skyloom 1.13.5 → 1.13.7
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/.github/workflows/ci.yml +36 -36
- package/README.md +220 -159
- package/config/providers.yaml +39 -39
- package/config/skills/api_integrator/SKILL.md +15 -15
- package/config/skills/arch_designer/SKILL.md +13 -13
- package/config/skills/ci_cd_manager/SKILL.md +14 -14
- package/config/skills/code_analysis/SKILL.md +13 -13
- package/config/skills/code_generator/SKILL.md +12 -12
- package/config/skills/code_reviewer/SKILL.md +13 -13
- package/config/skills/content_writer/SKILL.md +14 -14
- package/config/skills/data_transformer/SKILL.md +15 -15
- package/config/skills/document_analysis/SKILL.md +13 -13
- package/config/skills/emotional_companion/SKILL.md +15 -15
- package/config/skills/performance_checker/SKILL.md +14 -14
- package/config/skills/security_auditor/SKILL.md +14 -14
- package/config/skills/self_evolve/SKILL.md +13 -13
- package/config/skills/sys_operator/SKILL.md +15 -15
- package/config/skills/task_planner/SKILL.md +14 -14
- package/config/skills/web_research/SKILL.md +14 -14
- package/config/skills/workflow_designer/SKILL.md +13 -13
- package/dist/agents/dew.js +52 -52
- package/dist/agents/fair.js +84 -84
- package/dist/agents/fog.js +30 -30
- package/dist/agents/frost.js +32 -32
- package/dist/agents/rain.js +32 -32
- package/dist/agents/snow.js +68 -68
- package/dist/cli/commands_md.d.ts +41 -0
- package/dist/cli/commands_md.d.ts.map +1 -0
- package/dist/cli/commands_md.js +140 -0
- package/dist/cli/commands_md.js.map +1 -0
- package/dist/cli/input_macros.d.ts +28 -0
- package/dist/cli/input_macros.d.ts.map +1 -0
- package/dist/cli/input_macros.js +120 -0
- package/dist/cli/input_macros.js.map +1 -0
- package/dist/cli/loom.d.ts +220 -0
- package/dist/cli/loom.d.ts.map +1 -0
- package/dist/cli/loom.js +1094 -0
- package/dist/cli/loom.js.map +1 -0
- package/dist/cli/loom_chat.d.ts +20 -0
- package/dist/cli/loom_chat.d.ts.map +1 -0
- package/dist/cli/loom_chat.js +685 -0
- package/dist/cli/loom_chat.js.map +1 -0
- package/dist/cli/main.js +310 -14
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/tui.d.ts.map +1 -1
- package/dist/cli/tui.js +7 -1
- package/dist/cli/tui.js.map +1 -1
- package/dist/core/agent/guard.d.ts +45 -0
- package/dist/core/agent/guard.d.ts.map +1 -0
- package/dist/core/agent/guard.js +113 -0
- package/dist/core/agent/guard.js.map +1 -0
- package/dist/core/agent.d.ts +17 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +182 -93
- package/dist/core/agent.js.map +1 -1
- package/dist/core/factory.d.ts.map +1 -1
- package/dist/core/factory.js +34 -2
- package/dist/core/factory.js.map +1 -1
- package/dist/core/file_checkpoint.d.ts +57 -0
- package/dist/core/file_checkpoint.d.ts.map +1 -0
- package/dist/core/file_checkpoint.js +162 -0
- package/dist/core/file_checkpoint.js.map +1 -0
- package/dist/core/hooks.d.ts +43 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js +110 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/llm.d.ts.map +1 -1
- package/dist/core/llm.js +15 -9
- package/dist/core/llm.js.map +1 -1
- package/dist/core/longdoc.js +5 -5
- package/dist/core/mcp.d.ts +16 -0
- package/dist/core/mcp.d.ts.map +1 -1
- package/dist/core/mcp.js +55 -0
- package/dist/core/mcp.js.map +1 -1
- package/dist/core/model_config.d.ts +40 -0
- package/dist/core/model_config.d.ts.map +1 -0
- package/dist/core/model_config.js +191 -0
- package/dist/core/model_config.js.map +1 -0
- package/dist/core/skill.d.ts +7 -0
- package/dist/core/skill.d.ts.map +1 -1
- package/dist/core/skill.js +47 -0
- package/dist/core/skill.js.map +1 -1
- package/dist/core/skymd.d.ts +39 -0
- package/dist/core/skymd.d.ts.map +1 -0
- package/dist/core/skymd.js +177 -0
- package/dist/core/skymd.js.map +1 -0
- package/dist/core/tool.d.ts +12 -0
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +30 -0
- package/dist/core/tool.js.map +1 -1
- package/dist/core/verify.d.ts +27 -0
- package/dist/core/verify.d.ts.map +1 -0
- package/dist/core/verify.js +62 -0
- package/dist/core/verify.js.map +1 -0
- package/dist/skills/loader.d.ts +22 -2
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +45 -15
- package/dist/skills/loader.js.map +1 -1
- package/dist/tools/builtin.d.ts.map +1 -1
- package/dist/tools/builtin.js +13 -3
- package/dist/tools/builtin.js.map +1 -1
- package/dist/tools/model_tool.d.ts +11 -0
- package/dist/tools/model_tool.d.ts.map +1 -0
- package/dist/tools/model_tool.js +71 -0
- package/dist/tools/model_tool.js.map +1 -0
- package/dist/tools/todo.d.ts +30 -0
- package/dist/tools/todo.d.ts.map +1 -0
- package/dist/tools/todo.js +78 -0
- package/dist/tools/todo.js.map +1 -0
- package/docs/AESTHETIC_DESIGN.md +152 -144
- package/docs/OPTIMIZATION_PLAN.md +178 -178
- package/package.json +1 -1
- package/scripts/install.js +48 -48
- package/scripts/link.js +10 -10
- package/setup.bat +79 -79
- package/skill-test-ty2fOA/test.md +10 -10
- package/src/agents/dew.ts +70 -70
- package/src/agents/fair.ts +102 -102
- package/src/agents/fog.ts +48 -48
- package/src/agents/frost.ts +50 -50
- package/src/agents/rain.ts +50 -50
- package/src/agents/snow.ts +239 -239
- package/src/cli/commands_md.ts +112 -0
- package/src/cli/input_macros.ts +83 -0
- package/src/cli/loom.ts +982 -0
- package/src/cli/loom_chat.ts +598 -0
- package/src/cli/main.ts +255 -9
- package/src/cli/mode.ts +58 -58
- package/src/cli/tui.ts +228 -222
- package/src/core/agent/guard.ts +134 -0
- package/src/core/agent/task.ts +100 -100
- package/src/core/agent.ts +177 -95
- package/src/core/arbitrate.ts +162 -162
- package/src/core/catalog.ts +178 -178
- package/src/core/checkpoint.ts +94 -94
- package/src/core/estimate.ts +104 -104
- package/src/core/evolve.ts +191 -191
- package/src/core/factory.ts +31 -2
- package/src/core/file_checkpoint.ts +136 -0
- package/src/core/filter.ts +103 -103
- package/src/core/graph.ts +156 -156
- package/src/core/hooks.ts +126 -0
- package/src/core/icons.ts +53 -53
- package/src/core/index.ts +37 -37
- package/src/core/learn.ts +146 -146
- package/src/core/llm.ts +15 -9
- package/src/core/longdoc.ts +155 -155
- package/src/core/mcp.ts +48 -0
- package/src/core/mcp_server.ts +176 -176
- package/src/core/model_config.ts +157 -0
- package/src/core/profile.ts +255 -255
- package/src/core/router.ts +124 -124
- package/src/core/sandbox.ts +142 -142
- package/src/core/security.ts +243 -243
- package/src/core/skill.ts +42 -0
- package/src/core/skymd.ts +143 -0
- package/src/core/theme.ts +65 -65
- package/src/core/tool.ts +30 -0
- package/src/core/tool_router.ts +193 -193
- package/src/core/vector.ts +152 -152
- package/src/core/verify.ts +71 -0
- package/src/core/workspace.ts +150 -150
- package/src/plugins/loader.ts +66 -66
- package/src/skills/loader.ts +45 -16
- package/src/sql.js.d.ts +29 -29
- package/src/tools/builtin.ts +13 -3
- package/src/tools/computer.ts +269 -269
- package/src/tools/delegate.ts +49 -49
- package/src/tools/model_tool.ts +74 -0
- package/src/tools/todo.ts +76 -0
- package/src/web/tts.ts +93 -93
- package/tests/agent.test.ts +159 -159
- package/tests/agent_helpers.test.ts +48 -48
- package/tests/bus.test.ts +121 -121
- package/tests/catalog.test.ts +86 -86
- package/tests/checkpoint_commands.test.ts +124 -0
- package/tests/claude_compat.test.ts +110 -0
- package/tests/config.test.ts +41 -41
- package/tests/guard.test.ts +75 -0
- package/tests/icons.test.ts +45 -45
- package/tests/loom.test.ts +248 -0
- package/tests/memory.test.ts +170 -170
- package/tests/model_config.test.ts +109 -0
- package/tests/router.test.ts +86 -86
- package/tests/schemas.test.ts +51 -51
- package/tests/semantic.test.ts +83 -83
- package/tests/setup.ts +10 -10
- package/tests/skill.test.ts +172 -172
- package/tests/skymd.test.ts +146 -0
- package/tests/task.test.ts +60 -60
- package/tests/todo_toolstats.test.ts +94 -0
- package/tests/tool.test.ts +108 -108
- package/tests/tool_router.test.ts +71 -71
- package/tests/tui.test.ts +67 -67
- package/vitest.config.ts +17 -17
package/src/core/arbitrate.ts
CHANGED
|
@@ -1,162 +1,162 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 多Agent冲突仲裁 — majority voting, quality scoring, tie-breaking.
|
|
3
|
-
*
|
|
4
|
-
* When multiple agents produce conflicting outputs on the same task
|
|
5
|
-
* (or when a reviewer disagrees with an executor), this module
|
|
6
|
-
* provides structured conflict resolution.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { TaskExecutionResult } from "./factory";
|
|
10
|
-
|
|
11
|
-
/* ═══════════════════════════════════════
|
|
12
|
-
Conflict detection
|
|
13
|
-
═══════════════════════════════════════ */
|
|
14
|
-
export interface Conflict {
|
|
15
|
-
taskId: string;
|
|
16
|
-
results: TaskExecutionResult[];
|
|
17
|
-
description: string;
|
|
18
|
-
severity: "low" | "medium" | "high";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Detect if two results conflict based on success status and content overlap. */
|
|
22
|
-
export function detectConflicts(results: TaskExecutionResult[]): Conflict[] {
|
|
23
|
-
const byTask = new Map<string, TaskExecutionResult[]>();
|
|
24
|
-
for (const r of results) { const id = r.id; if (!byTask.has(id)) byTask.set(id, []); byTask.get(id)!.push(r); }
|
|
25
|
-
|
|
26
|
-
const conflicts: Conflict[] = [];
|
|
27
|
-
for (const [id, items] of byTask) {
|
|
28
|
-
if (items.length < 2) continue;
|
|
29
|
-
|
|
30
|
-
const successes = items.filter(r => r.success);
|
|
31
|
-
const failures = items.filter(r => !r.success);
|
|
32
|
-
|
|
33
|
-
// All succeeded — check content divergence
|
|
34
|
-
if (successes.length >= 2) {
|
|
35
|
-
const contents = successes.map(r => (r.content || "").toLowerCase());
|
|
36
|
-
const similarity = pairwiseSimilarity(contents);
|
|
37
|
-
if (similarity < 0.3) {
|
|
38
|
-
conflicts.push({ taskId: id, results: successes, description: "Multiple agents produced divergent successful outputs", severity: "medium" });
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Mix of success and failure
|
|
43
|
-
if (successes.length > 0 && failures.length > 0) {
|
|
44
|
-
conflicts.push({ taskId: id, results: items, description: `${successes.length} succeeded, ${failures.length} failed — need tiebreaker`, severity: "medium" });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// All failed
|
|
48
|
-
if (failures.length >= 2 && successes.length === 0) {
|
|
49
|
-
conflicts.push({ taskId: id, results: failures, description: "All agents failed on this task", severity: "high" });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return conflicts;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/* ═══════════════════════════════════════
|
|
57
|
-
Content similarity (n-gram Jaccard)
|
|
58
|
-
═══════════════════════════════════════ */
|
|
59
|
-
function pairwiseSimilarity(texts: string[]): number {
|
|
60
|
-
if (texts.length < 2) return 1.0;
|
|
61
|
-
let total = 0;
|
|
62
|
-
let count = 0;
|
|
63
|
-
for (let i = 0; i < texts.length; i++) {
|
|
64
|
-
for (let j = i + 1; j < texts.length; j++) {
|
|
65
|
-
total += ngramJaccard(texts[i], texts[j], 3);
|
|
66
|
-
count++;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return count === 0 ? 0 : total / count;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function ngramJaccard(a: string, b: string, n: number): number {
|
|
73
|
-
const as = ngrams(a, n), bs = ngrams(b, n);
|
|
74
|
-
if (as.size === 0 && bs.size === 0) return 1;
|
|
75
|
-
let intersection = 0;
|
|
76
|
-
for (const g of as) { if (bs.has(g)) intersection++; }
|
|
77
|
-
const union = as.size + bs.size - intersection;
|
|
78
|
-
return union === 0 ? 0 : intersection / union;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function ngrams(s: string, n: number): Set<string> {
|
|
82
|
-
const out = new Set<string>();
|
|
83
|
-
for (let i = 0; i <= s.length - n; i++) out.add(s.slice(i, i + n));
|
|
84
|
-
return out;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/* ═══════════════════════════════════════
|
|
88
|
-
Majority voting / arbitration
|
|
89
|
-
═══════════════════════════════════════ */
|
|
90
|
-
export interface ArbitrationResult {
|
|
91
|
-
winner: TaskExecutionResult;
|
|
92
|
-
method: "unanimous" | "majority" | "tiebreaker" | "single";
|
|
93
|
-
confidence: number; // 0-1
|
|
94
|
-
reasoning: string;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Pick the best result from conflicting ones via majority vote. */
|
|
98
|
-
export function arbitrate(results: TaskExecutionResult[]): ArbitrationResult {
|
|
99
|
-
if (results.length === 0) throw new Error("No results to arbitrate");
|
|
100
|
-
if (results.length === 1) return { winner: results[0], method: "single", confidence: 0.8, reasoning: "Only one result available" };
|
|
101
|
-
|
|
102
|
-
const success = results.filter(r => r.success);
|
|
103
|
-
const fail = results.filter(r => !r.success);
|
|
104
|
-
|
|
105
|
-
// All agree (success)
|
|
106
|
-
if (success.length === results.length) {
|
|
107
|
-
const longest = success.reduce((a, b) => (b.content || "").length > (a.content || "").length ? b : a);
|
|
108
|
-
return { winner: longest, method: "unanimous", confidence: 0.95, reasoning: `${results.length}/${results.length} agents agreed` };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Majority success
|
|
112
|
-
if (success.length > fail.length) {
|
|
113
|
-
// Pick the longest successful content (most detailed)
|
|
114
|
-
const best = success.reduce((a, b) => (b.content || "").length > (a.content || "").length ? b : a);
|
|
115
|
-
return { winner: best, method: "majority", confidence: success.length / results.length, reasoning: `${success.length}/${results.length} succeeded, selected most detailed` };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Majority failure — pick the "closest to success" (longest content)
|
|
119
|
-
if (fail.length > success.length) {
|
|
120
|
-
const best = fail.reduce((a, b) => (b.content || "").length > (a.content || "").length ? b : a);
|
|
121
|
-
return { winner: best, method: "majority", confidence: 0.3, reasoning: `Majority failed (${fail.length}/${results.length}), best-effort from partial output` };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Tie — prefer success, or longest content
|
|
125
|
-
const tie = success.length > 0 ? success[0] : fail[0];
|
|
126
|
-
return { winner: tie, method: "tiebreaker", confidence: 0.5, reasoning: `Tie — selected ${tie.success ? "success" : "longest"} result` };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/* ═══════════════════════════════════════
|
|
130
|
-
Quality scoring for individual results
|
|
131
|
-
═══════════════════════════════════════ */
|
|
132
|
-
export interface QualityScore {
|
|
133
|
-
score: number; // 0-100
|
|
134
|
-
completeness: number; // how much of the task was addressed
|
|
135
|
-
richness: number; // detail level of the output
|
|
136
|
-
correctness: number; // did it match expectations (requires ground truth)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function scoreQuality(result: TaskExecutionResult): QualityScore {
|
|
140
|
-
const content = result.content || "";
|
|
141
|
-
|
|
142
|
-
// Completeness: length is a weak proxy but useful
|
|
143
|
-
const completeness = content.length > 500 ? 80 : content.length > 100 ? 50 : content.length > 0 ? 20 : 0;
|
|
144
|
-
|
|
145
|
-
// Richness: code blocks, structured output, bullet points
|
|
146
|
-
let richness = 50;
|
|
147
|
-
if (/```/.test(content)) richness += 20;
|
|
148
|
-
if (/\|.*\|.*\|/.test(content)) richness += 15; // tables
|
|
149
|
-
if (/^[-*] /.test(content)) richness += 10; // bullets
|
|
150
|
-
if (/\d+\./.test(content)) richness += 10; // numbered lists
|
|
151
|
-
richness = Math.min(100, richness);
|
|
152
|
-
|
|
153
|
-
// Correctness: basic sanity checks
|
|
154
|
-
let correctness = 70;
|
|
155
|
-
if (content.includes("Error") || content.includes("error")) correctness -= 20;
|
|
156
|
-
if (content.includes("[REDACTED]")) correctness -= 10;
|
|
157
|
-
if (content.includes("truncated")) correctness -= 15;
|
|
158
|
-
correctness = Math.max(0, correctness);
|
|
159
|
-
|
|
160
|
-
const score = Math.round((completeness * 0.3 + richness * 0.3 + correctness * 0.4));
|
|
161
|
-
return { score, completeness, richness, correctness };
|
|
162
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 多Agent冲突仲裁 — majority voting, quality scoring, tie-breaking.
|
|
3
|
+
*
|
|
4
|
+
* When multiple agents produce conflicting outputs on the same task
|
|
5
|
+
* (or when a reviewer disagrees with an executor), this module
|
|
6
|
+
* provides structured conflict resolution.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { TaskExecutionResult } from "./factory";
|
|
10
|
+
|
|
11
|
+
/* ═══════════════════════════════════════
|
|
12
|
+
Conflict detection
|
|
13
|
+
═══════════════════════════════════════ */
|
|
14
|
+
export interface Conflict {
|
|
15
|
+
taskId: string;
|
|
16
|
+
results: TaskExecutionResult[];
|
|
17
|
+
description: string;
|
|
18
|
+
severity: "low" | "medium" | "high";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Detect if two results conflict based on success status and content overlap. */
|
|
22
|
+
export function detectConflicts(results: TaskExecutionResult[]): Conflict[] {
|
|
23
|
+
const byTask = new Map<string, TaskExecutionResult[]>();
|
|
24
|
+
for (const r of results) { const id = r.id; if (!byTask.has(id)) byTask.set(id, []); byTask.get(id)!.push(r); }
|
|
25
|
+
|
|
26
|
+
const conflicts: Conflict[] = [];
|
|
27
|
+
for (const [id, items] of byTask) {
|
|
28
|
+
if (items.length < 2) continue;
|
|
29
|
+
|
|
30
|
+
const successes = items.filter(r => r.success);
|
|
31
|
+
const failures = items.filter(r => !r.success);
|
|
32
|
+
|
|
33
|
+
// All succeeded — check content divergence
|
|
34
|
+
if (successes.length >= 2) {
|
|
35
|
+
const contents = successes.map(r => (r.content || "").toLowerCase());
|
|
36
|
+
const similarity = pairwiseSimilarity(contents);
|
|
37
|
+
if (similarity < 0.3) {
|
|
38
|
+
conflicts.push({ taskId: id, results: successes, description: "Multiple agents produced divergent successful outputs", severity: "medium" });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Mix of success and failure
|
|
43
|
+
if (successes.length > 0 && failures.length > 0) {
|
|
44
|
+
conflicts.push({ taskId: id, results: items, description: `${successes.length} succeeded, ${failures.length} failed — need tiebreaker`, severity: "medium" });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// All failed
|
|
48
|
+
if (failures.length >= 2 && successes.length === 0) {
|
|
49
|
+
conflicts.push({ taskId: id, results: failures, description: "All agents failed on this task", severity: "high" });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return conflicts;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ═══════════════════════════════════════
|
|
57
|
+
Content similarity (n-gram Jaccard)
|
|
58
|
+
═══════════════════════════════════════ */
|
|
59
|
+
function pairwiseSimilarity(texts: string[]): number {
|
|
60
|
+
if (texts.length < 2) return 1.0;
|
|
61
|
+
let total = 0;
|
|
62
|
+
let count = 0;
|
|
63
|
+
for (let i = 0; i < texts.length; i++) {
|
|
64
|
+
for (let j = i + 1; j < texts.length; j++) {
|
|
65
|
+
total += ngramJaccard(texts[i], texts[j], 3);
|
|
66
|
+
count++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return count === 0 ? 0 : total / count;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function ngramJaccard(a: string, b: string, n: number): number {
|
|
73
|
+
const as = ngrams(a, n), bs = ngrams(b, n);
|
|
74
|
+
if (as.size === 0 && bs.size === 0) return 1;
|
|
75
|
+
let intersection = 0;
|
|
76
|
+
for (const g of as) { if (bs.has(g)) intersection++; }
|
|
77
|
+
const union = as.size + bs.size - intersection;
|
|
78
|
+
return union === 0 ? 0 : intersection / union;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function ngrams(s: string, n: number): Set<string> {
|
|
82
|
+
const out = new Set<string>();
|
|
83
|
+
for (let i = 0; i <= s.length - n; i++) out.add(s.slice(i, i + n));
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ═══════════════════════════════════════
|
|
88
|
+
Majority voting / arbitration
|
|
89
|
+
═══════════════════════════════════════ */
|
|
90
|
+
export interface ArbitrationResult {
|
|
91
|
+
winner: TaskExecutionResult;
|
|
92
|
+
method: "unanimous" | "majority" | "tiebreaker" | "single";
|
|
93
|
+
confidence: number; // 0-1
|
|
94
|
+
reasoning: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Pick the best result from conflicting ones via majority vote. */
|
|
98
|
+
export function arbitrate(results: TaskExecutionResult[]): ArbitrationResult {
|
|
99
|
+
if (results.length === 0) throw new Error("No results to arbitrate");
|
|
100
|
+
if (results.length === 1) return { winner: results[0], method: "single", confidence: 0.8, reasoning: "Only one result available" };
|
|
101
|
+
|
|
102
|
+
const success = results.filter(r => r.success);
|
|
103
|
+
const fail = results.filter(r => !r.success);
|
|
104
|
+
|
|
105
|
+
// All agree (success)
|
|
106
|
+
if (success.length === results.length) {
|
|
107
|
+
const longest = success.reduce((a, b) => (b.content || "").length > (a.content || "").length ? b : a);
|
|
108
|
+
return { winner: longest, method: "unanimous", confidence: 0.95, reasoning: `${results.length}/${results.length} agents agreed` };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Majority success
|
|
112
|
+
if (success.length > fail.length) {
|
|
113
|
+
// Pick the longest successful content (most detailed)
|
|
114
|
+
const best = success.reduce((a, b) => (b.content || "").length > (a.content || "").length ? b : a);
|
|
115
|
+
return { winner: best, method: "majority", confidence: success.length / results.length, reasoning: `${success.length}/${results.length} succeeded, selected most detailed` };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Majority failure — pick the "closest to success" (longest content)
|
|
119
|
+
if (fail.length > success.length) {
|
|
120
|
+
const best = fail.reduce((a, b) => (b.content || "").length > (a.content || "").length ? b : a);
|
|
121
|
+
return { winner: best, method: "majority", confidence: 0.3, reasoning: `Majority failed (${fail.length}/${results.length}), best-effort from partial output` };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Tie — prefer success, or longest content
|
|
125
|
+
const tie = success.length > 0 ? success[0] : fail[0];
|
|
126
|
+
return { winner: tie, method: "tiebreaker", confidence: 0.5, reasoning: `Tie — selected ${tie.success ? "success" : "longest"} result` };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* ═══════════════════════════════════════
|
|
130
|
+
Quality scoring for individual results
|
|
131
|
+
═══════════════════════════════════════ */
|
|
132
|
+
export interface QualityScore {
|
|
133
|
+
score: number; // 0-100
|
|
134
|
+
completeness: number; // how much of the task was addressed
|
|
135
|
+
richness: number; // detail level of the output
|
|
136
|
+
correctness: number; // did it match expectations (requires ground truth)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function scoreQuality(result: TaskExecutionResult): QualityScore {
|
|
140
|
+
const content = result.content || "";
|
|
141
|
+
|
|
142
|
+
// Completeness: length is a weak proxy but useful
|
|
143
|
+
const completeness = content.length > 500 ? 80 : content.length > 100 ? 50 : content.length > 0 ? 20 : 0;
|
|
144
|
+
|
|
145
|
+
// Richness: code blocks, structured output, bullet points
|
|
146
|
+
let richness = 50;
|
|
147
|
+
if (/```/.test(content)) richness += 20;
|
|
148
|
+
if (/\|.*\|.*\|/.test(content)) richness += 15; // tables
|
|
149
|
+
if (/^[-*] /.test(content)) richness += 10; // bullets
|
|
150
|
+
if (/\d+\./.test(content)) richness += 10; // numbered lists
|
|
151
|
+
richness = Math.min(100, richness);
|
|
152
|
+
|
|
153
|
+
// Correctness: basic sanity checks
|
|
154
|
+
let correctness = 70;
|
|
155
|
+
if (content.includes("Error") || content.includes("error")) correctness -= 20;
|
|
156
|
+
if (content.includes("[REDACTED]")) correctness -= 10;
|
|
157
|
+
if (content.includes("truncated")) correctness -= 15;
|
|
158
|
+
correctness = Math.max(0, correctness);
|
|
159
|
+
|
|
160
|
+
const score = Math.round((completeness * 0.3 + richness * 0.3 + correctness * 0.4));
|
|
161
|
+
return { score, completeness, richness, correctness };
|
|
162
|
+
}
|