test/antigravity-adapter.test.js
Rama main · réplica de lectura del repositorio original.
var test = require("node:test");
var assert = require("node:assert");
var EventEmitter = require("node:events");
var PassThrough = require("node:stream").PassThrough;
var antigravity = require("../lib/yoke/adapters/antigravity");
function createFakeSpawn(calls) {
return function(binary, args, options) {
var proc = new EventEmitter();
proc.stdout = new PassThrough();
proc.stderr = new PassThrough();
proc.stdin = new EventEmitter();
proc.stdin.write = function(line) {
calls.push({ input: JSON.parse(line) });
setImmediate(function() {
proc.stdout.write(JSON.stringify({ event: "init", conversation_id: "agy-session" }) + "\n");
proc.stdout.write(JSON.stringify({
event: "step_update",
step_update: { step_index: 1, state: "ACTIVE", step_type: "agent_response", text_delta: "hello" },
}) + "\n");
proc.stdout.write(JSON.stringify({
event: "step_update",
step_update: {
step_index: 2,
state: "DONE",
step_type: "tool",
tool_name: "run_command",
tool_info: { name: "run_command", parameters: { CommandLine: "pwd" }, output: "/project\n" },
},
}) + "\n");
proc.stdout.write(JSON.stringify({
event: "result",
result: {
conversation_id: "agy-session",
status: "SUCCESS",
duration_seconds: 1.5,
usage: { input_tokens: 10, output_tokens: 2, cache_read_tokens: 4 },
},
}) + "\n");
});
return true;
};
proc.stdin.end = function() {
setImmediate(function() { proc.emit("exit", 0, null); });
};
proc.kill = function() { proc.emit("exit", null, "SIGINT"); };
calls.push({ binary: binary, args: args, options: options });
return proc;
};
}
test("Antigravity model parser accepts official JSON and text listings", function() {
assert.deepStrictEqual(antigravity.parseModels(JSON.stringify({
models: [{ id: "gemini-pro" }, { slug: "gemini-flash" }],
})), ["gemini-pro", "gemini-flash"]);
assert.deepStrictEqual(antigravity.parseModels("gemini-pro Gemini Pro\ngemini-flash Gemini Flash\n"), ["gemini-pro", "gemini-flash"]);
});
test("Antigravity adapter uses the official streaming CLI protocol", async function() {
var calls = [];
var adapter = antigravity.createAntigravityAdapter({
cwd: "/project",
_binaryPath: "/contract/agy",
_fetchModels: function() { return Promise.resolve(["gemini-pro"]); },
_spawn: createFakeSpawn(calls),
});
var ready = await adapter.init();
assert.deepStrictEqual(ready.models, ["gemini-pro"]);
assert.strictEqual(ready.capabilities.sessionResume, true);
assert.strictEqual(ready.capabilities.effort, true);
assert.deepStrictEqual(ready.capabilities.queryBoundTools, {
mode: "none",
reason: "The current Clay adapter does not forward custom-tool configuration to agy.",
});
assert.strictEqual(adapter.createToolServer({ tools: [] }), null);
var handle = await adapter.createQuery({
cwd: "/project",
model: "gemini-pro",
effort: "high",
resumeSessionId: "previous-session",
systemPrompt: "Project instructions",
env: { PROJECT_TOKEN: "scoped" },
dynamicTools: [{ name: "probe" }],
toolServers: { probe: { command: "probe" } },
adapterOptions: { ANTIGRAVITY: { dangerouslySkipPermissions: true } },
});
assert.strictEqual(handle.pushMessage("Say hello"), true);
handle.endInput();
var events = [];
for await (var event of handle) events.push(event);
assert.deepStrictEqual(calls[0].args, [
"--input-format", "stream-json",
"--output-format", "stream-json",
"--conversation", "previous-session",
"--model", "gemini-pro",
"--effort", "high",
"--dangerously-skip-permissions",
]);
assert.strictEqual(calls[1].input.message.content, "Project instructions\n\nSay hello");
assert.strictEqual(calls[0].options.env.PROJECT_TOKEN, "scoped");
assert.ok(events.some(function(event) { return event.yokeType === "text_delta" && event.text === "hello"; }));
assert.ok(events.some(function(event) { return event.yokeType === "tool_start" && event.toolName === "Bash"; }));
assert.ok(events.some(function(event) { return event.yokeType === "tool_result" && event.content === "/project\n"; }));
assert.ok(events.some(function(event) { return event.yokeType === "result" && event.sessionId === "agy-session"; }));
await adapter.shutdown();
});
test("Antigravity reports per-turn usage from cumulative stream results", async function() {
var turn = 0;
function fakeSpawn() {
var proc = new EventEmitter();
proc.stdout = new PassThrough();
proc.stderr = new PassThrough();
proc.stdin = new EventEmitter();
proc.stdin.write = function() {
turn++;
var current = turn;
setImmediate(function() {
proc.stdout.write(JSON.stringify({ event: "step_update", step_update: {
step_index: current, state: "DONE", step_type: "agent_response", text_delta: current === 1 ? "one" : "two",
usage: current === 1
? { input_tokens: 10, output_tokens: 2, cache_read_tokens: 1 }
: { input_tokens: 4, output_tokens: 3, cache_read_tokens: 0 },
duration_seconds: current === 1 ? 1.25 : 0.75,
} }) + "\n");
proc.stdout.write(JSON.stringify({ event: "result", result: {
status: "SUCCESS", conversation_id: "multi",
usage: current === 1
? { input_tokens: 10, output_tokens: 2, cache_read_tokens: 1 }
: { input_tokens: 14, output_tokens: 5, cache_read_tokens: 1 },
duration_seconds: current === 1 ? 1.25 : 2,
} }) + "\n");
});
return true;
};
proc.stdin.end = function() { setImmediate(function() { proc.emit("exit", 0, null); }); };
proc.kill = function() { proc.emit("exit", null, "SIGINT"); };
return proc;
}
var handle = antigravity.createAntigravityQueryHandle("/contract/agy", { cwd: "/project", _spawn: fakeSpawn }, function() {});
handle.pushMessage("first");
var results = [];
for await (var firstEvent of handle) {
if (firstEvent.yokeType === "result") { results.push(firstEvent); break; }
}
handle.pushMessage("second");
handle.endInput();
for await (var laterEvent of handle) {
if (laterEvent.yokeType === "result") results.push(laterEvent);
}
assert.deepStrictEqual(results.map(function(result) { return result.usage; }), [{
input_tokens: 10, output_tokens: 2, cache_read_input_tokens: 1, cache_creation_input_tokens: 0,
}, {
input_tokens: 4, output_tokens: 3, cache_read_input_tokens: 0, cache_creation_input_tokens: 0,
}]);
assert.deepStrictEqual(results.map(function(result) { return result.duration; }), [1250, 750]);
assert.deepStrictEqual(await handle.getContextUsage(), { input_tokens: 15, contextWindow: null });
});
test("Antigravity sums completed step usage for the first resumed turn", async function() {
function fakeSpawn() {
var proc = new EventEmitter();
proc.stdout = new PassThrough();
proc.stderr = new PassThrough();
proc.stdin = new EventEmitter();
proc.stdin.write = function() {
setImmediate(function() {
var response = { event: "step_update", step_update: {
step_index: 1, state: "DONE", step_type: "agent_response", text_delta: "done",
usage: { input_tokens: 10, output_tokens: 2, cache_read_tokens: 1 }, duration_seconds: 1,
} };
proc.stdout.write(JSON.stringify(response) + "\n");
proc.stdout.write(JSON.stringify(response) + "\n");
proc.stdout.write(JSON.stringify({ event: "step_update", step_update: {
step_index: 2, state: "DONE", step_type: "checkpoint",
usage: { input_tokens: 3, output_tokens: 1, cache_read_tokens: 2 }, duration_seconds: 0.25,
} }) + "\n");
proc.stdout.write(JSON.stringify({ event: "result", result: {
status: "SUCCESS", conversation_id: "resumed",
duration_seconds: 10.25,
} }) + "\n");
});
return true;
};
proc.stdin.end = function() { setImmediate(function() { proc.emit("exit", 0, null); }); };
proc.kill = function() { proc.emit("exit", null, "SIGINT"); };
return proc;
}
var handle = antigravity.createAntigravityQueryHandle("/contract/agy", {
cwd: "/project", resumeSessionId: "existing", _spawn: fakeSpawn,
}, function() {});
handle.pushMessage("continue");
handle.endInput();
var result = null;
for await (var event of handle) if (event.yokeType === "result") result = event;
assert.deepStrictEqual(result.usage, {
input_tokens: 13, output_tokens: 3, cache_read_input_tokens: 3, cache_creation_input_tokens: 0,
});
assert.strictEqual(result.duration, 1250);
});
test("Antigravity leaves an unmeasurable resumed first turn unknown and baselines the next", async function() {
var turn = 0;
function fakeSpawn() {
var proc = new EventEmitter();
proc.stdout = new PassThrough();
proc.stderr = new PassThrough();
proc.stdin = new EventEmitter();
proc.stdin.write = function() {
turn++;
var current = turn;
setImmediate(function() {
proc.stdout.write(JSON.stringify({ event: "result", result: {
status: "SUCCESS", conversation_id: "resumed",
usage: current === 1
? { input_tokens: 100, output_tokens: 50, cache_read_tokens: 20 }
: { input_tokens: 104, output_tokens: 53, cache_read_tokens: 21 },
duration_seconds: current === 1 ? 10 : 10.75,
} }) + "\n");
});
return true;
};
proc.stdin.end = function() { setImmediate(function() { proc.emit("exit", 0, null); }); };
proc.kill = function() { proc.emit("exit", null, "SIGINT"); };
return proc;
}
var handle = antigravity.createAntigravityQueryHandle("/contract/agy", {
cwd: "/project", resumeSessionId: "existing", _spawn: fakeSpawn,
}, function() {});
handle.pushMessage("first resumed turn");
var results = [];
for await (var firstEvent of handle) {
if (firstEvent.yokeType === "result") { results.push(firstEvent); break; }
}
handle.pushMessage("second resumed turn");
handle.endInput();
for await (var laterEvent of handle) if (laterEvent.yokeType === "result") results.push(laterEvent);
assert.strictEqual(results[0].usage, null);
assert.strictEqual(results[0].duration, null);
assert.deepStrictEqual(results[1].usage, {
input_tokens: 4, output_tokens: 3, cache_read_input_tokens: 1, cache_creation_input_tokens: 0,
});
assert.strictEqual(results[1].duration, 750);
assert.deepStrictEqual(await handle.getContextUsage(), { input_tokens: 125, contextWindow: null });
});