jules-orchestrator-kit 0.22.7 ā 0.22.8
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/bin/agentctl.mjs +3 -3
- package/package.json +1 -1
- package/src/engine.mjs +19 -0
- package/src/flaky-ledger.mjs +173 -0
- package/src/mcp.mjs +1 -1
package/bin/agentctl.mjs
CHANGED
|
@@ -14,7 +14,7 @@ const command = args[0];
|
|
|
14
14
|
|
|
15
15
|
function printHelp() {
|
|
16
16
|
console.log(`
|
|
17
|
-
š agentctl v0.22.
|
|
17
|
+
š agentctl v0.22.8 ā Universal Agent Orchestrator & Safety Gatekeeper
|
|
18
18
|
|
|
19
19
|
Usage: agentctl <command> [options]
|
|
20
20
|
|
|
@@ -44,7 +44,7 @@ async function main() {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (command === "version" || command === "--version" || command === "-v") {
|
|
47
|
-
console.log("agentctl v0.22.
|
|
47
|
+
console.log("agentctl v0.22.8");
|
|
48
48
|
process.exit(0);
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -226,7 +226,7 @@ async function main() {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
case "doctor": {
|
|
229
|
-
console.log(`\nš agentctl System Diagnostics (v0.22.
|
|
229
|
+
console.log(`\nš agentctl System Diagnostics (v0.22.8)`);
|
|
230
230
|
console.log(`--------------------------------------------------`);
|
|
231
231
|
console.log(` Project Root : ${root}`);
|
|
232
232
|
console.log(` Config File : ${config._file || "None (Using defaults)"}`);
|
package/package.json
CHANGED
package/src/engine.mjs
CHANGED
|
@@ -4,10 +4,13 @@ import { changedFiles, diffBytes, diffText, showFromOrigin, runCmd } from "./git
|
|
|
4
4
|
import { createProvider } from "./provider.mjs";
|
|
5
5
|
import { withBudget, appendLedger, getQueueDir, ensureDir } from "./state.mjs";
|
|
6
6
|
import { sanitizeUntrustedData, buildAgentEnvelope } from "./prompt-guard.mjs";
|
|
7
|
+
import { recordVerifyRun, readVerifyRuns, flakyVerdict } from "./flaky-ledger.mjs";
|
|
7
8
|
import { readdirSync, readFileSync, renameSync, existsSync } from "node:fs";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
import { createHash } from "node:crypto";
|
|
10
11
|
|
|
12
|
+
export { recordVerifyRun, readVerifyRuns, flakyVerdict };
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* Validates whether a file in .agent/jules-queue/ is a task file.
|
|
13
16
|
* Filters out README.md and matches TASK-*.md or valid envelope front-matter.
|
|
@@ -176,9 +179,25 @@ export async function gate(opts = {}) {
|
|
|
176
179
|
NODE_OPTIONS: guardNodeOptions,
|
|
177
180
|
};
|
|
178
181
|
|
|
182
|
+
let flakyVerdictResult = null;
|
|
183
|
+
|
|
179
184
|
if (testCmd) {
|
|
185
|
+
const startTime = Date.now();
|
|
180
186
|
const res = runCmd(testCmd, { cwd: root, ignoreError: true, env: testEnv });
|
|
187
|
+
const durationMs = Date.now() - startTime;
|
|
181
188
|
testResult = { ok: res.status === 0, status: res.status, stdout: res.stdout, stderr: res.stderr, command: testCmd };
|
|
189
|
+
|
|
190
|
+
const fingerprint = !testResult.ok ? fingerprintFailureState(testResult, root) : null;
|
|
191
|
+
recordVerifyRun(root, testCmd, testResult.ok, fingerprint, durationMs);
|
|
192
|
+
|
|
193
|
+
if (!testResult.ok) {
|
|
194
|
+
const runs = readVerifyRuns(root, testCmd);
|
|
195
|
+
flakyVerdictResult = flakyVerdict(runs);
|
|
196
|
+
if (flakyVerdictResult.verdict === "QUARANTINED") {
|
|
197
|
+
phases.push({ phase: "verify", ok: false, testResult, buildResult, flakyVerdict: flakyVerdictResult });
|
|
198
|
+
return { ok: false, code: 8, phases, flakyVerdict: flakyVerdictResult };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
182
201
|
}
|
|
183
202
|
|
|
184
203
|
if (testResult.ok && buildCmd) {
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { readFileSync, existsSync, appendFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getStateDir, ensureDir } from "./state.mjs";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Calculates Wilson score interval for binomial proportion.
|
|
7
|
+
*/
|
|
8
|
+
export function wilsonScoreInterval(k, n, z = 1.96) {
|
|
9
|
+
if (n === 0) return { lower: 0, upper: 0 };
|
|
10
|
+
const p = k / n;
|
|
11
|
+
const z2 = z * z;
|
|
12
|
+
const denominator = 1 + z2 / n;
|
|
13
|
+
const center = (p + z2 / (2 * n)) / denominator;
|
|
14
|
+
const stdErr = Math.sqrt((p * (1 - p) + z2 / (4 * n)) / n);
|
|
15
|
+
const margin = (z * stdErr) / denominator;
|
|
16
|
+
const lower = Math.max(0, center - margin);
|
|
17
|
+
const upper = Math.min(1, center + margin);
|
|
18
|
+
return { lower, upper };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Computes oscillation rate (fraction of state transitions between consecutive runs).
|
|
23
|
+
*/
|
|
24
|
+
export function computeOscillation(runs) {
|
|
25
|
+
if (!runs || runs.length <= 1) return 0;
|
|
26
|
+
let transitions = 0;
|
|
27
|
+
for (let i = 0; i < runs.length - 1; i++) {
|
|
28
|
+
const current = typeof runs[i] === "boolean" ? runs[i] : Boolean(runs[i] && runs[i].pass);
|
|
29
|
+
const next = typeof runs[i + 1] === "boolean" ? runs[i + 1] : Boolean(runs[i + 1] && runs[i + 1].pass);
|
|
30
|
+
if (current !== next) {
|
|
31
|
+
transitions++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return transitions / (runs.length - 1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Appends a test outcome record to .agent/state/flaky.jsonl.
|
|
39
|
+
*/
|
|
40
|
+
export function recordVerifyRun(root, testCmd, pass, fingerprint = null, durationMs = 0) {
|
|
41
|
+
const stateDir = getStateDir(root);
|
|
42
|
+
ensureDir(stateDir);
|
|
43
|
+
const filePath = join(stateDir, "flaky.jsonl");
|
|
44
|
+
|
|
45
|
+
const entry = {
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
testCmd: testCmd || "",
|
|
48
|
+
pass: Boolean(pass),
|
|
49
|
+
fingerprint: fingerprint || null,
|
|
50
|
+
durationMs: Number(durationMs) || 0,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
appendFileSync(filePath, JSON.stringify(entry) + "\n", "utf-8");
|
|
54
|
+
return entry;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Reads test run records from .agent/state/flaky.jsonl.
|
|
59
|
+
*/
|
|
60
|
+
export function readVerifyRuns(root, testCmd = null) {
|
|
61
|
+
const stateDir = getStateDir(root);
|
|
62
|
+
const filePath = join(stateDir, "flaky.jsonl");
|
|
63
|
+
|
|
64
|
+
if (!existsSync(filePath)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
70
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
71
|
+
const runs = [];
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
try {
|
|
74
|
+
const parsed = JSON.parse(line);
|
|
75
|
+
if (!testCmd || parsed.testCmd === testCmd) {
|
|
76
|
+
runs.push(parsed);
|
|
77
|
+
}
|
|
78
|
+
} catch (_) {}
|
|
79
|
+
}
|
|
80
|
+
return runs;
|
|
81
|
+
} catch (_) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const getVerifyRuns = readVerifyRuns;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Evaluates flaky test verdict using a sliding window of the last n <= 10 runs.
|
|
90
|
+
*/
|
|
91
|
+
export function flakyVerdict(runs = []) {
|
|
92
|
+
const normalized = (runs || []).map((r) => {
|
|
93
|
+
if (typeof r === "boolean") return { pass: r };
|
|
94
|
+
if (typeof r === "number") return { pass: r === 1 };
|
|
95
|
+
return { pass: Boolean(r && r.pass) };
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const window = normalized.slice(-10);
|
|
99
|
+
const n = window.length;
|
|
100
|
+
const fails = window.filter((r) => !r.pass).length;
|
|
101
|
+
const passes = n - fails;
|
|
102
|
+
|
|
103
|
+
if (n === 0) {
|
|
104
|
+
return {
|
|
105
|
+
verdict: "INSUFFICIENT_DATA",
|
|
106
|
+
allowRepair: true,
|
|
107
|
+
n: 0,
|
|
108
|
+
fails: 0,
|
|
109
|
+
passes: 0,
|
|
110
|
+
oscillation: 0,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (fails === 0) {
|
|
115
|
+
return {
|
|
116
|
+
verdict: "HEALTHY",
|
|
117
|
+
allowRepair: true,
|
|
118
|
+
n,
|
|
119
|
+
fails,
|
|
120
|
+
passes,
|
|
121
|
+
oscillation: computeOscillation(window),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const trailing3Fail = n >= 3 && window.slice(-3).every((r) => !r.pass);
|
|
126
|
+
if (fails === n || trailing3Fail) {
|
|
127
|
+
return {
|
|
128
|
+
verdict: "REPAIRABLE_REGRESSION",
|
|
129
|
+
allowRepair: true,
|
|
130
|
+
n,
|
|
131
|
+
fails,
|
|
132
|
+
passes,
|
|
133
|
+
oscillation: computeOscillation(window),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (n < 6) {
|
|
138
|
+
return {
|
|
139
|
+
verdict: "INSUFFICIENT_DATA",
|
|
140
|
+
allowRepair: true,
|
|
141
|
+
n,
|
|
142
|
+
fails,
|
|
143
|
+
passes,
|
|
144
|
+
oscillation: computeOscillation(window),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const oscillation = computeOscillation(window);
|
|
149
|
+
const wilson = wilsonScoreInterval(fails, n, 1.96);
|
|
150
|
+
const interiorWilson = wilson.lower > 0 && wilson.upper < 1;
|
|
151
|
+
|
|
152
|
+
if (oscillation >= 0.4 && interiorWilson) {
|
|
153
|
+
return {
|
|
154
|
+
verdict: "QUARANTINED",
|
|
155
|
+
allowRepair: false,
|
|
156
|
+
n,
|
|
157
|
+
fails,
|
|
158
|
+
passes,
|
|
159
|
+
oscillation,
|
|
160
|
+
wilson,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
verdict: "REPAIRABLE_REGRESSION",
|
|
166
|
+
allowRepair: true,
|
|
167
|
+
n,
|
|
168
|
+
fails,
|
|
169
|
+
passes,
|
|
170
|
+
oscillation,
|
|
171
|
+
wilson,
|
|
172
|
+
};
|
|
173
|
+
}
|
package/src/mcp.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { reapOrphanedIntents, reapStaleMutexDirs } from "./journal.mjs";
|
|
|
7
7
|
|
|
8
8
|
export const MCP_SERVER_INFO = {
|
|
9
9
|
name: "jules-orchestrator-kit",
|
|
10
|
-
version: "0.22.
|
|
10
|
+
version: "0.22.8",
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export const MAX_MCP_FRAME_SIZE = 4 * 1024 * 1024; // 4 MB memory safety ceiling
|