@cairn-tool/cairn 3.4.0 → 3.5.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/README.md +6 -4
- package/dist/agent/manifest.d.ts +10 -0
- package/dist/agent/manifest.js +23 -0
- package/dist/agent/manifest.js.map +1 -1
- package/dist/agent/parser.js +1 -15
- package/dist/agent/parser.js.map +1 -1
- package/dist/agent/render.js +47 -3
- package/dist/agent/render.js.map +1 -1
- package/dist/cli.js +46 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/qa-list.d.ts +7 -0
- package/dist/commands/qa-list.js +69 -0
- package/dist/commands/qa-list.js.map +1 -0
- package/dist/commands/qa-run.d.ts +3 -0
- package/dist/commands/qa-run.js +106 -0
- package/dist/commands/qa-run.js.map +1 -0
- package/dist/commands/qa-summary.d.ts +6 -0
- package/dist/commands/qa-summary.js +38 -0
- package/dist/commands/qa-summary.js.map +1 -0
- package/dist/config-schema.d.ts +2 -0
- package/dist/config-schema.js +4 -0
- package/dist/config-schema.js.map +1 -1
- package/dist/config.d.ts +12 -2
- package/dist/config.js +36 -7
- package/dist/config.js.map +1 -1
- package/dist/contract/registry.js +43 -0
- package/dist/contract/registry.js.map +1 -1
- package/dist/contract/schemas/index.js +2 -0
- package/dist/contract/schemas/index.js.map +1 -1
- package/dist/contract/schemas/qa.d.ts +2 -0
- package/dist/contract/schemas/qa.js +111 -0
- package/dist/contract/schemas/qa.js.map +1 -0
- package/dist/qa/agent.d.ts +25 -0
- package/dist/qa/agent.js +142 -0
- package/dist/qa/agent.js.map +1 -0
- package/dist/qa/agents/claude-code.d.ts +2 -0
- package/dist/qa/agents/claude-code.js +121 -0
- package/dist/qa/agents/claude-code.js.map +1 -0
- package/dist/qa/agents/cursor.d.ts +5 -0
- package/dist/qa/agents/cursor.js +143 -0
- package/dist/qa/agents/cursor.js.map +1 -0
- package/dist/qa/agents/index.d.ts +32 -0
- package/dist/qa/agents/index.js +83 -0
- package/dist/qa/agents/index.js.map +1 -0
- package/dist/qa/agents/types.d.ts +61 -0
- package/dist/qa/agents/types.js +18 -0
- package/dist/qa/agents/types.js.map +1 -0
- package/dist/qa/async.d.ts +27 -0
- package/dist/qa/async.js +91 -0
- package/dist/qa/async.js.map +1 -0
- package/dist/qa/casefile.d.ts +35 -0
- package/dist/qa/casefile.js +181 -0
- package/dist/qa/casefile.js.map +1 -0
- package/dist/qa/cases.d.ts +51 -0
- package/dist/qa/cases.js +144 -0
- package/dist/qa/cases.js.map +1 -0
- package/dist/qa/config.d.ts +50 -0
- package/dist/qa/config.js +105 -0
- package/dist/qa/config.js.map +1 -0
- package/dist/qa/demo.d.ts +10 -0
- package/dist/qa/demo.js +194 -0
- package/dist/qa/demo.js.map +1 -0
- package/dist/qa/format.d.ts +38 -0
- package/dist/qa/format.js +208 -0
- package/dist/qa/format.js.map +1 -0
- package/dist/qa/harness.d.ts +68 -0
- package/dist/qa/harness.js +528 -0
- package/dist/qa/harness.js.map +1 -0
- package/dist/qa/keys.d.ts +12 -0
- package/dist/qa/keys.js +96 -0
- package/dist/qa/keys.js.map +1 -0
- package/dist/qa/layout.d.ts +102 -0
- package/dist/qa/layout.js +278 -0
- package/dist/qa/layout.js.map +1 -0
- package/dist/qa/lock.d.ts +5 -0
- package/dist/qa/lock.js +114 -0
- package/dist/qa/lock.js.map +1 -0
- package/dist/qa/options.d.ts +38 -0
- package/dist/qa/options.js +165 -0
- package/dist/qa/options.js.map +1 -0
- package/dist/qa/outcome.d.ts +30 -0
- package/dist/qa/outcome.js +162 -0
- package/dist/qa/outcome.js.map +1 -0
- package/dist/qa/reports.d.ts +51 -0
- package/dist/qa/reports.js +121 -0
- package/dist/qa/reports.js.map +1 -0
- package/dist/qa/schedule.d.ts +55 -0
- package/dist/qa/schedule.js +145 -0
- package/dist/qa/schedule.js.map +1 -0
- package/dist/qa/screen.d.ts +31 -0
- package/dist/qa/screen.js +146 -0
- package/dist/qa/screen.js.map +1 -0
- package/dist/qa/slot.d.ts +28 -0
- package/dist/qa/slot.js +126 -0
- package/dist/qa/slot.js.map +1 -0
- package/dist/qa/tracker.d.ts +36 -0
- package/dist/qa/tracker.js +357 -0
- package/dist/qa/tracker.js.map +1 -0
- package/dist/query/execute.js +0 -0
- package/dist/query/execute.js.map +1 -1
- package/dist/scripts/resolve.js +1 -4
- package/dist/scripts/resolve.js.map +1 -1
- package/dist/usage/providers/cursor.js +1 -1
- package/dist/usage/providers/cursor.js.map +1 -1
- package/package.json +2 -1
package/dist/qa/agent.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { closeSync, mkdirSync, openSync, writeFileSync, writeSync } from "node:fs";
|
|
3
|
+
import { once } from "node:events";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { StringDecoder } from "node:string_decoder";
|
|
6
|
+
import { flatten, isBlank } from "./format.js";
|
|
7
|
+
import { signalNumber } from "./async.js";
|
|
8
|
+
import { shellJoin } from "./cases.js";
|
|
9
|
+
/**
|
|
10
|
+
* Signal the whole process group — `detached: true` gave the child its own session, so a
|
|
11
|
+
* negative pid reaches cursor-agent and everything it spawned. Never uses `child.killed` as a
|
|
12
|
+
* guard: that stays false when the group is signalled directly.
|
|
13
|
+
*/
|
|
14
|
+
export function killGroup(child, sig) {
|
|
15
|
+
if (child.pid === undefined)
|
|
16
|
+
return;
|
|
17
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
process.kill(-child.pid, sig);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const code = err.code;
|
|
24
|
+
if (code === "ESRCH")
|
|
25
|
+
return;
|
|
26
|
+
try {
|
|
27
|
+
child.kill(sig);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
/* already reaped */
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Python reports -9 for a SIGKILLed child; Node reports {code: null, signal}. */
|
|
35
|
+
export function effectiveExitCode(code, sig) {
|
|
36
|
+
if (code !== null)
|
|
37
|
+
return code;
|
|
38
|
+
return sig ? -signalNumber(sig) : null;
|
|
39
|
+
}
|
|
40
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
|
+
export async function runAgent(spec) {
|
|
42
|
+
mkdirSync(spec.logDir, { recursive: true });
|
|
43
|
+
writeFileSync(join(spec.logDir, "command.txt"), `${shellJoin(spec.argv)}\n`, "utf8");
|
|
44
|
+
const [file, ...args] = spec.argv;
|
|
45
|
+
const child = spawn(file, args, {
|
|
46
|
+
cwd: spec.cwd,
|
|
47
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
48
|
+
detached: true, // == start_new_session=True; do NOT unref, we must await this child
|
|
49
|
+
});
|
|
50
|
+
// Python raises FileNotFoundError synchronously; Node reports it on 'error'. Convert it back
|
|
51
|
+
// so a bad --agent becomes a per-case error record, not an unhandled rejection.
|
|
52
|
+
//
|
|
53
|
+
// Both listeners are removed as soon as one of them wins. A bare Promise.race leaves the
|
|
54
|
+
// loser attached, and Node emits 'error' on a ChildProcess *after* spawn when a kill fails
|
|
55
|
+
// — reachable from killGroup, where a process.kill(-pid) failing with anything but ESRCH
|
|
56
|
+
// falls through to child.kill(). That would reject a promise nothing awaits, and Cleanup's
|
|
57
|
+
// unhandled-rejection handler calls process.exit(1), tearing down every other running case
|
|
58
|
+
// and truncating a pending --format json write.
|
|
59
|
+
await new Promise((resolve, reject) => {
|
|
60
|
+
const onSpawned = () => {
|
|
61
|
+
child.off("error", onFailed);
|
|
62
|
+
resolve();
|
|
63
|
+
};
|
|
64
|
+
const onFailed = (err) => {
|
|
65
|
+
child.off("spawn", onSpawned);
|
|
66
|
+
reject(err);
|
|
67
|
+
};
|
|
68
|
+
child.once("spawn", onSpawned);
|
|
69
|
+
child.once("error", onFailed);
|
|
70
|
+
});
|
|
71
|
+
spec.onSpawn(child);
|
|
72
|
+
// Note: Node reads up to its 64 KiB highWaterMark rather than the Python's 8 KiB, and spawn
|
|
73
|
+
// does not expose that knob for stdio pipes. The streamed-bytes total is identical; only the
|
|
74
|
+
// on-screen counter's step size differs.
|
|
75
|
+
const streamFd = openSync(join(spec.logDir, "stream.jsonl"), "a");
|
|
76
|
+
const stderrFd = openSync(join(spec.logDir, "stderr.txt"), "w");
|
|
77
|
+
let timedOut = false;
|
|
78
|
+
const timer = setTimeout(() => {
|
|
79
|
+
timedOut = true;
|
|
80
|
+
killGroup(child, "SIGKILL");
|
|
81
|
+
}, spec.timeoutMs);
|
|
82
|
+
const pumpStderr = (async () => {
|
|
83
|
+
for await (const chunk of child.stderr)
|
|
84
|
+
writeSync(stderrFd, chunk);
|
|
85
|
+
})();
|
|
86
|
+
const pumpStdout = (async () => {
|
|
87
|
+
// StringDecoder holds a multi-byte character split across a chunk boundary. The Python
|
|
88
|
+
// decodes each chunk independently and corrupts those.
|
|
89
|
+
const decoder = new StringDecoder("utf8");
|
|
90
|
+
let buf = "";
|
|
91
|
+
// One path for every line, so the final unterminated one is parsed rather than merely
|
|
92
|
+
// logged. An agent whose last line lacks a newline would otherwise lose its `result`
|
|
93
|
+
// event entirely — the case recording usage: null while stream.jsonl shows the tokens.
|
|
94
|
+
const dispatch = (line) => {
|
|
95
|
+
if (isBlank(line))
|
|
96
|
+
return;
|
|
97
|
+
writeSync(streamFd, `${line}\n`);
|
|
98
|
+
let obj;
|
|
99
|
+
try {
|
|
100
|
+
obj = JSON.parse(line);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
spec.onRawLine(flatten(line).slice(0, 200));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (isRecord(obj))
|
|
107
|
+
spec.onEvent(obj);
|
|
108
|
+
};
|
|
109
|
+
for await (const chunk of child.stdout) {
|
|
110
|
+
const bytes = chunk;
|
|
111
|
+
spec.onBytes(bytes.length);
|
|
112
|
+
buf += decoder.write(bytes);
|
|
113
|
+
let nl = buf.indexOf("\n");
|
|
114
|
+
while (nl !== -1) {
|
|
115
|
+
const line = buf.slice(0, nl);
|
|
116
|
+
buf = buf.slice(nl + 1);
|
|
117
|
+
nl = buf.indexOf("\n");
|
|
118
|
+
dispatch(line);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
buf += decoder.end();
|
|
122
|
+
dispatch(buf);
|
|
123
|
+
})();
|
|
124
|
+
try {
|
|
125
|
+
// 'close' fires after both pipes hit EOF *and* the child exits — the faithful composite of
|
|
126
|
+
// the Python's `async with asyncio.timeout(...): await read_stdout(); await proc.wait()`.
|
|
127
|
+
const [code, sig] = (await Promise.all([pumpStdout, pumpStderr, once(child, "close")]).then(([, , closed]) => closed));
|
|
128
|
+
return { exitCode: effectiveExitCode(code, sig), signal: sig, timedOut };
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
for (const fd of [streamFd, stderrFd]) {
|
|
133
|
+
try {
|
|
134
|
+
closeSync(fd);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
/* already closed */
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/qa/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { isRecord, num, sessionIdOf } from "./types.js";
|
|
2
|
+
const TOOL_ARG_FIELDS = [
|
|
3
|
+
"command",
|
|
4
|
+
"file_path",
|
|
5
|
+
"path",
|
|
6
|
+
"pattern",
|
|
7
|
+
"glob",
|
|
8
|
+
"query",
|
|
9
|
+
"url",
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* Claude Code's print+stream-json dialect, captured from claude 2.1.263:
|
|
13
|
+
*
|
|
14
|
+
* - `-p --output-format stream-json` refuses without `--verbose`.
|
|
15
|
+
* - Tools are `tool_use` content blocks inside `assistant` messages, not `tool_call` events.
|
|
16
|
+
* - Thinking is a `thinking` content block on `assistant`, not a `thinking` event.
|
|
17
|
+
* - The terminal `result` event carries one authoritative `usage` total (snake_case).
|
|
18
|
+
* Assistant lines also carry per-request usage and must not be summed — they fan out
|
|
19
|
+
* the way the transcript does, and the result is the session figure.
|
|
20
|
+
* - `session_id` appears on every event; `system`/`init` carries `model`.
|
|
21
|
+
*/
|
|
22
|
+
function claudeUsage(raw) {
|
|
23
|
+
if (!isRecord(raw))
|
|
24
|
+
return null;
|
|
25
|
+
return {
|
|
26
|
+
inputTokens: num(raw["input_tokens"]),
|
|
27
|
+
outputTokens: num(raw["output_tokens"]),
|
|
28
|
+
cacheReadTokens: num(raw["cache_read_input_tokens"]),
|
|
29
|
+
cacheWriteTokens: num(raw["cache_creation_input_tokens"]),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function toolSummary(input) {
|
|
33
|
+
for (const field of TOOL_ARG_FIELDS) {
|
|
34
|
+
const value = input[field];
|
|
35
|
+
if (value !== undefined && value !== null && value !== "" && value !== 0 && value !== false) {
|
|
36
|
+
return String(value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
export const claudeCodeProfile = {
|
|
42
|
+
name: "claude-code",
|
|
43
|
+
binaries: ["claude"],
|
|
44
|
+
defaultModel: "sonnet",
|
|
45
|
+
buildArgv(input, binary) {
|
|
46
|
+
return [
|
|
47
|
+
binary,
|
|
48
|
+
"-p",
|
|
49
|
+
"--verbose",
|
|
50
|
+
"--output-format",
|
|
51
|
+
"stream-json",
|
|
52
|
+
"--dangerously-skip-permissions",
|
|
53
|
+
"--model",
|
|
54
|
+
input.model,
|
|
55
|
+
input.prompt,
|
|
56
|
+
];
|
|
57
|
+
},
|
|
58
|
+
normalize(raw) {
|
|
59
|
+
const kind = raw["type"];
|
|
60
|
+
const subtype = raw["subtype"];
|
|
61
|
+
const sessionId = sessionIdOf(raw);
|
|
62
|
+
if (kind === "system" && subtype === "init") {
|
|
63
|
+
const model = raw["model"];
|
|
64
|
+
return { kind: "init", model: model ? String(model) : undefined, sessionId };
|
|
65
|
+
}
|
|
66
|
+
if (kind === "assistant") {
|
|
67
|
+
const message = isRecord(raw["message"]) ? raw["message"] : {};
|
|
68
|
+
const rawContent = message["content"];
|
|
69
|
+
const content = Array.isArray(rawContent)
|
|
70
|
+
? rawContent
|
|
71
|
+
: typeof rawContent === "string"
|
|
72
|
+
? [{ type: "text", text: rawContent }]
|
|
73
|
+
: [];
|
|
74
|
+
const events = [];
|
|
75
|
+
for (const part of content) {
|
|
76
|
+
if (!isRecord(part))
|
|
77
|
+
continue;
|
|
78
|
+
const partType = part["type"];
|
|
79
|
+
if (partType === "text" && part["text"]) {
|
|
80
|
+
events.push({ kind: "text", text: String(part["text"]), sessionId });
|
|
81
|
+
}
|
|
82
|
+
else if (partType === "thinking" && part["thinking"]) {
|
|
83
|
+
events.push({
|
|
84
|
+
kind: "thinking",
|
|
85
|
+
phase: "completed",
|
|
86
|
+
text: String(part["thinking"]),
|
|
87
|
+
sessionId,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else if (partType === "tool_use") {
|
|
91
|
+
const name = typeof part["name"] === "string" ? part["name"] : "tool";
|
|
92
|
+
const input = isRecord(part["input"]) ? part["input"] : {};
|
|
93
|
+
events.push({
|
|
94
|
+
kind: "tool",
|
|
95
|
+
phase: "started",
|
|
96
|
+
name,
|
|
97
|
+
summary: toolSummary(input),
|
|
98
|
+
sessionId,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return events.length === 0 ? null : events;
|
|
103
|
+
}
|
|
104
|
+
if (kind === "result") {
|
|
105
|
+
const usage = claudeUsage(raw["usage"]);
|
|
106
|
+
const error = raw["is_error"]
|
|
107
|
+
? String(raw["result"] ? raw["result"] : "agent reported an error")
|
|
108
|
+
: undefined;
|
|
109
|
+
if (!usage && !error)
|
|
110
|
+
return null;
|
|
111
|
+
return {
|
|
112
|
+
kind: "usage",
|
|
113
|
+
usage: usage ?? {},
|
|
114
|
+
...(error ? { error } : {}),
|
|
115
|
+
sessionId,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../src/qa/agents/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { QaAgentProfile } from "./types.js";
|
|
2
|
+
/** Return [display name, short args] from a Cursor `tool_call` object. */
|
|
3
|
+
export declare function toolLabel(toolCall: Record<string, unknown>): [string, string];
|
|
4
|
+
export declare function toolFailed(toolCall: Record<string, unknown>): boolean;
|
|
5
|
+
export declare const cursorProfile: QaAgentProfile;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { flatten } from "../format.js";
|
|
2
|
+
import { isRecord, num, sessionIdOf } from "./types.js";
|
|
3
|
+
const TOOL_ARG_FIELDS = [
|
|
4
|
+
"command",
|
|
5
|
+
"path",
|
|
6
|
+
"globPattern",
|
|
7
|
+
"pattern",
|
|
8
|
+
"query",
|
|
9
|
+
"target_notebook",
|
|
10
|
+
"url",
|
|
11
|
+
];
|
|
12
|
+
/** Return [display name, short args] from a Cursor `tool_call` object. */
|
|
13
|
+
export function toolLabel(toolCall) {
|
|
14
|
+
for (const [key, payload] of Object.entries(toolCall)) {
|
|
15
|
+
if (!(key.endsWith("ToolCall") && isRecord(payload)))
|
|
16
|
+
continue;
|
|
17
|
+
const raw = key.slice(0, -"ToolCall".length);
|
|
18
|
+
const name = raw ? raw[0].toUpperCase() + raw.slice(1) : key;
|
|
19
|
+
const args = isRecord(payload["args"]) ? payload["args"] : {};
|
|
20
|
+
let summary = "";
|
|
21
|
+
for (const fieldName of TOOL_ARG_FIELDS) {
|
|
22
|
+
const value = args[fieldName];
|
|
23
|
+
if (value !== undefined && value !== null && value !== "" && value !== 0 && value !== false) {
|
|
24
|
+
summary = flatten(String(value));
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return [name, summary];
|
|
29
|
+
}
|
|
30
|
+
return ["tool", ""];
|
|
31
|
+
}
|
|
32
|
+
export function toolFailed(toolCall) {
|
|
33
|
+
for (const [key, payload] of Object.entries(toolCall)) {
|
|
34
|
+
if (key.endsWith("ToolCall") && isRecord(payload)) {
|
|
35
|
+
const result = payload["result"];
|
|
36
|
+
return isRecord(result) && "error" in result;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function cursorUsage(raw) {
|
|
42
|
+
if (!isRecord(raw))
|
|
43
|
+
return null;
|
|
44
|
+
return {
|
|
45
|
+
inputTokens: num(raw["inputTokens"]),
|
|
46
|
+
outputTokens: num(raw["outputTokens"]),
|
|
47
|
+
cacheReadTokens: num(raw["cacheReadTokens"]),
|
|
48
|
+
cacheWriteTokens: num(raw["cacheWriteTokens"]),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export const cursorProfile = {
|
|
52
|
+
name: "cursor",
|
|
53
|
+
binaries: ["cursor-agent", "agent"],
|
|
54
|
+
defaultModel: "cursor-grok-4.6-high",
|
|
55
|
+
buildArgv(input, binary) {
|
|
56
|
+
return [
|
|
57
|
+
binary,
|
|
58
|
+
"-p",
|
|
59
|
+
"--output-format",
|
|
60
|
+
"stream-json",
|
|
61
|
+
"--force",
|
|
62
|
+
"--trust",
|
|
63
|
+
"--workspace",
|
|
64
|
+
input.repo,
|
|
65
|
+
"--model",
|
|
66
|
+
input.model,
|
|
67
|
+
input.prompt,
|
|
68
|
+
];
|
|
69
|
+
},
|
|
70
|
+
normalize(raw) {
|
|
71
|
+
const kind = raw["type"];
|
|
72
|
+
const subtype = raw["subtype"];
|
|
73
|
+
const sessionId = sessionIdOf(raw);
|
|
74
|
+
if (kind === "thinking") {
|
|
75
|
+
if (subtype === "delta") {
|
|
76
|
+
const text = raw["text"];
|
|
77
|
+
return {
|
|
78
|
+
kind: "thinking",
|
|
79
|
+
phase: "delta",
|
|
80
|
+
text: typeof text === "string" ? text : "",
|
|
81
|
+
sessionId,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (subtype === "completed")
|
|
85
|
+
return { kind: "thinking", phase: "completed", sessionId };
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
if (kind === "assistant") {
|
|
89
|
+
const message = isRecord(raw["message"]) ? raw["message"] : {};
|
|
90
|
+
const rawContent = message["content"];
|
|
91
|
+
const content = typeof rawContent === "string"
|
|
92
|
+
? [{ type: "text", text: rawContent }]
|
|
93
|
+
: Array.isArray(rawContent)
|
|
94
|
+
? rawContent
|
|
95
|
+
: [];
|
|
96
|
+
const events = [];
|
|
97
|
+
for (const part of content) {
|
|
98
|
+
if (isRecord(part) && part["type"] === "text" && part["text"]) {
|
|
99
|
+
events.push({ kind: "text", text: String(part["text"]), sessionId });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return events.length === 0 ? null : events;
|
|
103
|
+
}
|
|
104
|
+
if (kind === "tool_call") {
|
|
105
|
+
const tool = isRecord(raw["tool_call"]) ? raw["tool_call"] : {};
|
|
106
|
+
const [name, summary] = toolLabel(tool);
|
|
107
|
+
if (subtype === "started") {
|
|
108
|
+
return { kind: "tool", phase: "started", name, summary, sessionId };
|
|
109
|
+
}
|
|
110
|
+
if (subtype === "completed") {
|
|
111
|
+
return {
|
|
112
|
+
kind: "tool",
|
|
113
|
+
phase: "completed",
|
|
114
|
+
name,
|
|
115
|
+
summary,
|
|
116
|
+
failed: toolFailed(tool),
|
|
117
|
+
sessionId,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
if (kind === "result") {
|
|
123
|
+
const usage = cursorUsage(raw["usage"]);
|
|
124
|
+
const error = raw["is_error"]
|
|
125
|
+
? String(raw["result"] ? raw["result"] : "agent reported an error")
|
|
126
|
+
: undefined;
|
|
127
|
+
if (!usage && !error)
|
|
128
|
+
return null;
|
|
129
|
+
return {
|
|
130
|
+
kind: "usage",
|
|
131
|
+
usage: usage ?? {},
|
|
132
|
+
...(error ? { error } : {}),
|
|
133
|
+
sessionId,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (kind === "system" && subtype === "init") {
|
|
137
|
+
const model = raw["model"];
|
|
138
|
+
return { kind: "init", model: model ? String(model) : undefined, sessionId };
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../src/qa/agents/cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { QaAgentProfile } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Every backend `qa run` can spawn.
|
|
4
|
+
*
|
|
5
|
+
* Registering another assistant is a new module plus a line here. Nothing
|
|
6
|
+
* outside this directory may branch on an agent's name: argv, event parsing,
|
|
7
|
+
* and the default model are read from the profile.
|
|
8
|
+
*/
|
|
9
|
+
export declare const AGENTS: readonly QaAgentProfile[];
|
|
10
|
+
export declare const SUPPORTED_AGENTS: readonly string[];
|
|
11
|
+
export declare function resolveAgent(name: string): QaAgentProfile;
|
|
12
|
+
export declare function defaultModels(): Record<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the executable for one profile. An explicit `--agent` path overrides
|
|
15
|
+
* every backend — that is the fake-agent test hook. PATH lookup is lazy and
|
|
16
|
+
* cached per profile so a cursor-only queue does not fail because `claude` is
|
|
17
|
+
* absent.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveBinary(name: string, explicit: string | null): string;
|
|
20
|
+
/**
|
|
21
|
+
* The executable to *display* for a profile, for --dry-run only.
|
|
22
|
+
*
|
|
23
|
+
* Falls back to the profile's first candidate name when nothing is on PATH: previewing a
|
|
24
|
+
* queue launches nothing, so requiring the backend to be installed would make `qa run
|
|
25
|
+
* --dry-run` fail on exactly the machine you would preview a queue on — and would make the
|
|
26
|
+
* e2e suite depend on cursor-agent and claude being present on the CI runner. An explicit
|
|
27
|
+
* --agent is still validated, because a bad path there is a real invocation error.
|
|
28
|
+
*/
|
|
29
|
+
export declare function displayBinary(name: string, explicit: string | null): string;
|
|
30
|
+
export declare function agentCaps(parallel: number): ReadonlyMap<string, number>;
|
|
31
|
+
export type { AgentArgvInput, NormalizedEvents, QaAgentProfile, QaEvent } from "./types.js";
|
|
32
|
+
export { asEventList } from "./types.js";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { UserError, which } from "../config.js";
|
|
2
|
+
import { findExplicitAgent } from "../config.js";
|
|
3
|
+
import { claudeCodeProfile } from "./claude-code.js";
|
|
4
|
+
import { cursorProfile } from "./cursor.js";
|
|
5
|
+
/**
|
|
6
|
+
* Every backend `qa run` can spawn.
|
|
7
|
+
*
|
|
8
|
+
* Registering another assistant is a new module plus a line here. Nothing
|
|
9
|
+
* outside this directory may branch on an agent's name: argv, event parsing,
|
|
10
|
+
* and the default model are read from the profile.
|
|
11
|
+
*/
|
|
12
|
+
export const AGENTS = [cursorProfile, claudeCodeProfile];
|
|
13
|
+
for (const profile of AGENTS) {
|
|
14
|
+
if (profile.maxParallel !== undefined &&
|
|
15
|
+
(!Number.isInteger(profile.maxParallel) || profile.maxParallel < 1)) {
|
|
16
|
+
throw new Error(`qa agent profile ${profile.name} maxParallel must be >= 1`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export const SUPPORTED_AGENTS = AGENTS.map((profile) => profile.name);
|
|
20
|
+
const BY_NAME = new Map(AGENTS.map((profile) => [profile.name, profile]));
|
|
21
|
+
const binaryCache = new Map();
|
|
22
|
+
export function resolveAgent(name) {
|
|
23
|
+
const profile = BY_NAME.get(name);
|
|
24
|
+
if (!profile) {
|
|
25
|
+
throw new UserError(`Unknown agent: ${name} (known: ${SUPPORTED_AGENTS.join(", ")})`);
|
|
26
|
+
}
|
|
27
|
+
return profile;
|
|
28
|
+
}
|
|
29
|
+
export function defaultModels() {
|
|
30
|
+
return Object.fromEntries(AGENTS.map((profile) => [profile.name, profile.defaultModel]));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the executable for one profile. An explicit `--agent` path overrides
|
|
34
|
+
* every backend — that is the fake-agent test hook. PATH lookup is lazy and
|
|
35
|
+
* cached per profile so a cursor-only queue does not fail because `claude` is
|
|
36
|
+
* absent.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveBinary(name, explicit) {
|
|
39
|
+
if (explicit)
|
|
40
|
+
return findExplicitAgent(explicit);
|
|
41
|
+
const hit = binaryCache.get(name);
|
|
42
|
+
if (hit)
|
|
43
|
+
return hit;
|
|
44
|
+
const profile = resolveAgent(name);
|
|
45
|
+
for (const candidate of profile.binaries) {
|
|
46
|
+
const found = which(candidate);
|
|
47
|
+
if (found) {
|
|
48
|
+
binaryCache.set(name, found);
|
|
49
|
+
return found;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
throw new UserError(`${profile.binaries[0]} not on PATH; pass --agent /path/to/${profile.binaries[0]}`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The executable to *display* for a profile, for --dry-run only.
|
|
56
|
+
*
|
|
57
|
+
* Falls back to the profile's first candidate name when nothing is on PATH: previewing a
|
|
58
|
+
* queue launches nothing, so requiring the backend to be installed would make `qa run
|
|
59
|
+
* --dry-run` fail on exactly the machine you would preview a queue on — and would make the
|
|
60
|
+
* e2e suite depend on cursor-agent and claude being present on the CI runner. An explicit
|
|
61
|
+
* --agent is still validated, because a bad path there is a real invocation error.
|
|
62
|
+
*/
|
|
63
|
+
export function displayBinary(name, explicit) {
|
|
64
|
+
if (explicit)
|
|
65
|
+
return findExplicitAgent(explicit);
|
|
66
|
+
try {
|
|
67
|
+
return resolveBinary(name, null);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return resolveAgent(name).binaries[0];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function agentCaps(parallel) {
|
|
74
|
+
const caps = new Map();
|
|
75
|
+
for (const profile of AGENTS) {
|
|
76
|
+
// An absent cap means --parallel alone decides, so a queue of one backend can use
|
|
77
|
+
// every slot the user asked for.
|
|
78
|
+
caps.set(profile.name, Math.min(profile.maxParallel ?? parallel, parallel));
|
|
79
|
+
}
|
|
80
|
+
return caps;
|
|
81
|
+
}
|
|
82
|
+
export { asEventList } from "./types.js";
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/qa/agents/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Usage } from "../outcome.js";
|
|
2
|
+
/**
|
|
3
|
+
* Normalized harness event. Slot.applyEvent consumes only this; vendor JSON never
|
|
4
|
+
* reaches the TUI, reports, or tracker.
|
|
5
|
+
*/
|
|
6
|
+
export type QaEvent = {
|
|
7
|
+
kind: "text";
|
|
8
|
+
text: string;
|
|
9
|
+
sessionId?: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "thinking";
|
|
12
|
+
phase: "delta" | "completed";
|
|
13
|
+
text?: string;
|
|
14
|
+
sessionId?: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "tool";
|
|
17
|
+
phase: "started" | "completed";
|
|
18
|
+
name: string;
|
|
19
|
+
summary: string;
|
|
20
|
+
failed?: boolean;
|
|
21
|
+
sessionId?: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "usage";
|
|
24
|
+
usage: Usage;
|
|
25
|
+
error?: string;
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "init";
|
|
29
|
+
model?: string;
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
};
|
|
32
|
+
export type NormalizedEvents = QaEvent | QaEvent[] | null;
|
|
33
|
+
/** Enough of a case for argv construction without importing Case. */
|
|
34
|
+
export interface AgentArgvInput {
|
|
35
|
+
repo: string;
|
|
36
|
+
model: string;
|
|
37
|
+
prompt: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Per-backend profile. Registering another assistant is a new module plus a
|
|
41
|
+
* line in index.ts. Nothing outside src/qa/agents/ may branch on an agent's name.
|
|
42
|
+
*/
|
|
43
|
+
export interface QaAgentProfile {
|
|
44
|
+
readonly name: string;
|
|
45
|
+
/** PATH candidates, first match wins. */
|
|
46
|
+
readonly binaries: readonly string[];
|
|
47
|
+
readonly defaultModel: string;
|
|
48
|
+
/**
|
|
49
|
+
* A real concurrency limit for this backend, or absent when the vendor imposes none we
|
|
50
|
+
* know of. Combined with --parallel by min(), so an absent cap means --parallel alone
|
|
51
|
+
* decides. Never set this to the *default* of --parallel: that silently caps the flag.
|
|
52
|
+
* Must be >= 1 when present.
|
|
53
|
+
*/
|
|
54
|
+
readonly maxParallel?: number;
|
|
55
|
+
buildArgv(input: AgentArgvInput, binary: string): string[];
|
|
56
|
+
normalize(raw: Record<string, unknown>): NormalizedEvents;
|
|
57
|
+
}
|
|
58
|
+
export declare function asEventList(events: NormalizedEvents): QaEvent[];
|
|
59
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
60
|
+
export declare function sessionIdOf(raw: Record<string, unknown>): string | undefined;
|
|
61
|
+
export declare function num(value: unknown): number | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function asEventList(events) {
|
|
2
|
+
if (events === null)
|
|
3
|
+
return [];
|
|
4
|
+
if (Array.isArray(events))
|
|
5
|
+
return [...events];
|
|
6
|
+
return [events];
|
|
7
|
+
}
|
|
8
|
+
export function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
export function sessionIdOf(raw) {
|
|
12
|
+
const value = raw["session_id"];
|
|
13
|
+
return value ? String(value) : undefined;
|
|
14
|
+
}
|
|
15
|
+
export function num(value) {
|
|
16
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/qa/agents/types.ts"],"names":[],"mappings":"AAmEA,MAAM,UAAU,WAAW,CAAC,MAAwB;IAClD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAA4B;IACtD,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,KAAc;IAChC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACjF,CAAC"}
|