clay-server 3.4.0-beta.2 → 3.4.0-beta.3
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/lib/public/antigravity-avatar.png +0 -0
- package/lib/public/index.html +3 -3
- package/lib/public/modules/app-panels.js +4 -4
- package/lib/public/modules/app-rendering.js +4 -4
- package/lib/public/modules/mate-sidebar.js +3 -3
- package/lib/public/modules/sidebar-mates.js +1 -1
- package/lib/public/modules/tools.js +1 -1
- package/lib/sdk-bridge.js +16 -8
- package/lib/yoke/acp-agent-profiles.js +0 -14
- package/lib/yoke/adapters/antigravity.js +417 -0
- package/lib/yoke/index.js +25 -20
- package/lib/yoke/vendor-registry.js +6 -6
- package/package.json +1 -1
- package/lib/public/gemini-avatar.svg +0 -11
- package/lib/yoke/adapters/gemini.js +0 -7
|
Binary file
|
package/lib/public/index.html
CHANGED
|
@@ -528,9 +528,9 @@
|
|
|
528
528
|
<img src="/codex-avatar.png" class="vendor-toggle-icon" alt="Codex">
|
|
529
529
|
<span class="vendor-toggle-label">Codex</span>
|
|
530
530
|
</button>
|
|
531
|
-
<button id="vendor-btn-
|
|
532
|
-
<img src="/
|
|
533
|
-
<span class="vendor-toggle-label">
|
|
531
|
+
<button id="vendor-btn-antigravity" class="vendor-toggle-btn" data-vendor="antigravity">
|
|
532
|
+
<img src="/antigravity-avatar.png" class="vendor-toggle-icon" alt="Antigravity">
|
|
533
|
+
<span class="vendor-toggle-label">Antigravity CLI</span>
|
|
534
534
|
</button>
|
|
535
535
|
<button id="vendor-btn-opencode" class="vendor-toggle-btn" data-vendor="opencode">
|
|
536
536
|
<img src="/opencode-avatar.svg" class="vendor-toggle-icon" alt="OpenCode">
|
|
@@ -89,7 +89,7 @@ var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
|
89
89
|
var EFFORT_LEVELS_BY_VENDOR = {
|
|
90
90
|
claude: ["low", "medium", "high", "xhigh", "max"],
|
|
91
91
|
codex: ["minimal", "low", "medium", "high", "xhigh"],
|
|
92
|
-
|
|
92
|
+
antigravity: [],
|
|
93
93
|
opencode: [],
|
|
94
94
|
kiro: ["low", "medium", "high", "xhigh", "max"],
|
|
95
95
|
};
|
|
@@ -443,13 +443,13 @@ export function initPanels() {
|
|
|
443
443
|
var vendorToggleWrap = $("vendor-toggle-wrap");
|
|
444
444
|
var vendorBtnClaude = $("vendor-btn-claude");
|
|
445
445
|
var vendorBtnCodex = $("vendor-btn-codex");
|
|
446
|
-
var
|
|
446
|
+
var vendorBtnAntigravity = $("vendor-btn-antigravity");
|
|
447
447
|
var vendorBtnOpenCode = $("vendor-btn-opencode");
|
|
448
448
|
var vendorBtnKiro = $("vendor-btn-kiro");
|
|
449
449
|
var vendorBtns = {
|
|
450
450
|
claude: vendorBtnClaude,
|
|
451
451
|
codex: vendorBtnCodex,
|
|
452
|
-
|
|
452
|
+
antigravity: vendorBtnAntigravity,
|
|
453
453
|
opencode: vendorBtnOpenCode,
|
|
454
454
|
kiro: vendorBtnKiro,
|
|
455
455
|
};
|
|
@@ -481,7 +481,7 @@ export function initPanels() {
|
|
|
481
481
|
|
|
482
482
|
if (vendorBtnClaude) vendorBtnClaude.addEventListener("click", function() { onVendorClick("claude"); });
|
|
483
483
|
if (vendorBtnCodex) vendorBtnCodex.addEventListener("click", function() { onVendorClick("codex"); });
|
|
484
|
-
if (
|
|
484
|
+
if (vendorBtnAntigravity) vendorBtnAntigravity.addEventListener("click", function() { onVendorClick("antigravity"); });
|
|
485
485
|
if (vendorBtnOpenCode) vendorBtnOpenCode.addEventListener("click", function() { onVendorClick("opencode"); });
|
|
486
486
|
if (vendorBtnKiro) vendorBtnKiro.addEventListener("click", function() { onVendorClick("kiro"); });
|
|
487
487
|
|
|
@@ -19,26 +19,26 @@ import { getScheduledMsgEl } from './app-rate-limit.js';
|
|
|
19
19
|
export var VENDOR_AVATARS = {
|
|
20
20
|
claude: "/claude-code-avatar.png",
|
|
21
21
|
codex: "/codex-avatar.png",
|
|
22
|
-
|
|
22
|
+
antigravity: "/antigravity-avatar.png",
|
|
23
23
|
opencode: "/opencode-avatar.svg",
|
|
24
24
|
kiro: "/kiro-avatar.svg",
|
|
25
25
|
};
|
|
26
26
|
export var VENDOR_NAMES = {
|
|
27
27
|
claude: "Claude Code",
|
|
28
28
|
codex: "Codex",
|
|
29
|
-
|
|
29
|
+
antigravity: "Antigravity CLI",
|
|
30
30
|
opencode: "OpenCode",
|
|
31
31
|
kiro: "Kiro CLI",
|
|
32
32
|
};
|
|
33
33
|
// Display order for every vendor Clay knows about, installed or not. Pickers
|
|
34
34
|
// render the full list so a missing CLI reads as "not installed yet" rather
|
|
35
35
|
// than "Clay doesn't support it".
|
|
36
|
-
export var VENDOR_ORDER = ["claude", "codex", "
|
|
36
|
+
export var VENDOR_ORDER = ["claude", "codex", "antigravity", "opencode", "kiro"];
|
|
37
37
|
// Where to send the user when they pick a vendor whose CLI isn't installed.
|
|
38
38
|
export var VENDOR_HOMEPAGES = {
|
|
39
39
|
claude: "https://claude.com/product/claude-code",
|
|
40
40
|
codex: "https://openai.com/codex/",
|
|
41
|
-
|
|
41
|
+
antigravity: "https://antigravity.google/product/antigravity-cli",
|
|
42
42
|
opencode: "https://opencode.ai/",
|
|
43
43
|
kiro: "https://kiro.dev/",
|
|
44
44
|
};
|
|
@@ -152,12 +152,12 @@ export function showMateSidebar(mateId, mateData) {
|
|
|
152
152
|
var vendorIcons = {
|
|
153
153
|
claude: "/claude-code-avatar.png",
|
|
154
154
|
codex: "/codex-avatar.png",
|
|
155
|
-
|
|
155
|
+
antigravity: "/antigravity-avatar.png",
|
|
156
156
|
opencode: "/opencode-avatar.svg",
|
|
157
157
|
kiro: "/kiro-avatar.svg",
|
|
158
158
|
};
|
|
159
|
-
var vendorNames = { claude: "Claude Code", codex: "Codex",
|
|
160
|
-
var vendorKeys = ["claude", "codex", "
|
|
159
|
+
var vendorNames = { claude: "Claude Code", codex: "Codex", antigravity: "Antigravity CLI", opencode: "OpenCode", kiro: "Kiro CLI" };
|
|
160
|
+
var vendorKeys = ["claude", "codex", "antigravity", "opencode", "kiro"];
|
|
161
161
|
mateVendorWrap.innerHTML = "";
|
|
162
162
|
for (var vi = 0; vi < vendorKeys.length; vi++) {
|
|
163
163
|
var vk = vendorKeys[vi];
|
|
@@ -460,7 +460,7 @@ export function renderUserStrip(allUsers, onlineUserIds, myUserId, dmFavorites,
|
|
|
460
460
|
var vendorLabels = {
|
|
461
461
|
claude: "Claude Code",
|
|
462
462
|
codex: "OpenAI Codex",
|
|
463
|
-
|
|
463
|
+
antigravity: "Antigravity CLI",
|
|
464
464
|
opencode: "OpenCode",
|
|
465
465
|
kiro: "Kiro CLI",
|
|
466
466
|
};
|
|
@@ -817,7 +817,7 @@ function resolvePermissionIdentity(mateId, vendor) {
|
|
|
817
817
|
var vendorAvatars = {
|
|
818
818
|
claude: "/claude-code-avatar.png",
|
|
819
819
|
codex: "/codex-avatar.png",
|
|
820
|
-
|
|
820
|
+
antigravity: "/antigravity-avatar.png",
|
|
821
821
|
opencode: "/opencode-avatar.svg",
|
|
822
822
|
kiro: "/kiro-avatar.svg",
|
|
823
823
|
};
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -1528,6 +1528,10 @@ function createSDKBridge(opts) {
|
|
|
1528
1528
|
ACP: {
|
|
1529
1529
|
mcpServers: getAcpMcpServers(session),
|
|
1530
1530
|
},
|
|
1531
|
+
ANTIGRAVITY: {
|
|
1532
|
+
dangerouslySkipPermissions: dangerouslySkipPermissions
|
|
1533
|
+
|| session.permissionMode === "bypassPermissions",
|
|
1534
|
+
},
|
|
1531
1535
|
KIRO: {
|
|
1532
1536
|
engine: kiroConfig.engine,
|
|
1533
1537
|
mode: kiroConfig.mode,
|
|
@@ -1790,16 +1794,20 @@ function createSDKBridge(opts) {
|
|
|
1790
1794
|
} catch (e) {}
|
|
1791
1795
|
if ((codexBin && fs.existsSync(codexBin)) || tryLookup(yoke.getVendorInfo("codex").binaryName)) result.push("codex");
|
|
1792
1796
|
|
|
1793
|
-
var
|
|
1797
|
+
var subprocessVendorKeys = ["antigravity", "opencode"];
|
|
1794
1798
|
var acpProfiles = require("./yoke/acp-agent-profiles");
|
|
1795
|
-
for (var
|
|
1796
|
-
var
|
|
1797
|
-
var
|
|
1798
|
-
if (!linuxUser ||
|
|
1799
|
-
|
|
1800
|
-
|
|
1799
|
+
for (var subprocessVendorIndex = 0; subprocessVendorIndex < subprocessVendorKeys.length; subprocessVendorIndex++) {
|
|
1800
|
+
var subprocessVendor = subprocessVendorKeys[subprocessVendorIndex];
|
|
1801
|
+
var subprocessInfo = yoke.getVendorInfo(subprocessVendor);
|
|
1802
|
+
if (!linuxUser || subprocessInfo.osUserIsolation) {
|
|
1803
|
+
if (subprocessVendor === "antigravity") {
|
|
1804
|
+
if (tryLookup(subprocessInfo.binaryName)) result.push(subprocessVendor);
|
|
1805
|
+
} else {
|
|
1806
|
+
var acpProfile = acpProfiles.getAcpAgentProfile(subprocessVendor);
|
|
1807
|
+
if (acpProfiles.findAcpAgentPath(acpProfile)) result.push(subprocessVendor);
|
|
1808
|
+
}
|
|
1801
1809
|
} else {
|
|
1802
|
-
console.log("[sdk-bridge] " +
|
|
1810
|
+
console.log("[sdk-bridge] " + subprocessInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
|
|
1803
1811
|
}
|
|
1804
1812
|
}
|
|
1805
1813
|
|
|
@@ -97,20 +97,6 @@ function validateOpenCodeConfig(config) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
var ACP_AGENT_PROFILES = {
|
|
100
|
-
gemini: {
|
|
101
|
-
vendor: "gemini",
|
|
102
|
-
displayName: "Gemini CLI",
|
|
103
|
-
binaryName: "gemini",
|
|
104
|
-
overrideName: "GEMINI_CLI_PATH",
|
|
105
|
-
args: ["--acp", "--approval-mode=default"],
|
|
106
|
-
defaultModels: ["auto"],
|
|
107
|
-
defaultModel: "auto",
|
|
108
|
-
// Gemini advertises loadSession, but releases through 0.46.0 could load
|
|
109
|
-
// the transcript without restoring the model's conversation memory.
|
|
110
|
-
// Keep resume gated until Clay's live contract test proves it reliable.
|
|
111
|
-
sessionResume: false,
|
|
112
|
-
permissionModeGuaranteed: true,
|
|
113
|
-
},
|
|
114
100
|
opencode: {
|
|
115
101
|
vendor: "opencode",
|
|
116
102
|
displayName: "OpenCode",
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
var childProcess = require("child_process");
|
|
2
|
+
var readline = require("readline");
|
|
3
|
+
var skillDiscovery = require("../skill-discovery");
|
|
4
|
+
|
|
5
|
+
function findBinary() {
|
|
6
|
+
if (process.env.ANTIGRAVITY_CLI_PATH) return process.env.ANTIGRAVITY_CLI_PATH;
|
|
7
|
+
try {
|
|
8
|
+
var command = process.platform === "win32" ? "where" : "which";
|
|
9
|
+
return childProcess.execFileSync(command, ["agy"], {
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
timeout: 3000,
|
|
12
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
13
|
+
}).trim().split(/\r?\n/)[0] || null;
|
|
14
|
+
} catch (e) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function modelValue(model) {
|
|
20
|
+
if (typeof model === "string") return model;
|
|
21
|
+
return model && (model.id || model.slug || model.value || model.model);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseModels(stdout) {
|
|
25
|
+
try {
|
|
26
|
+
var parsed = JSON.parse(String(stdout || ""));
|
|
27
|
+
var list = Array.isArray(parsed) ? parsed : (parsed.models || parsed.data || []);
|
|
28
|
+
var models = [];
|
|
29
|
+
for (var i = 0; i < list.length; i++) {
|
|
30
|
+
var value = modelValue(list[i]);
|
|
31
|
+
if (value && models.indexOf(value) === -1) models.push(value);
|
|
32
|
+
}
|
|
33
|
+
return models;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
var lines = String(stdout || "").split(/\r?\n/);
|
|
36
|
+
var fallback = [];
|
|
37
|
+
for (var j = 0; j < lines.length; j++) {
|
|
38
|
+
var match = lines[j].trim().match(/^([^\s]+)\s+/);
|
|
39
|
+
if (match && fallback.indexOf(match[1]) === -1) fallback.push(match[1]);
|
|
40
|
+
}
|
|
41
|
+
return fallback;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function fetchModels(binaryPath, cwd) {
|
|
46
|
+
return new Promise(function(resolve) {
|
|
47
|
+
childProcess.execFile(binaryPath, ["models", "--output-format", "json"], {
|
|
48
|
+
cwd: cwd || process.cwd(),
|
|
49
|
+
timeout: 20000,
|
|
50
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
51
|
+
}, function(err, stdout) {
|
|
52
|
+
if (err || !stdout) { resolve([]); return; }
|
|
53
|
+
resolve(parseModels(stdout));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isAuthError(value) {
|
|
59
|
+
return /authentication required|not authenticated|sign in|log ?in|credentials|unauthorized|forbidden|\b401\b/i.test(String(value || ""));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function toolName(name) {
|
|
63
|
+
var value = String(name || "").toLowerCase();
|
|
64
|
+
if (value === "run_command" || value === "shell" || value === "bash") return "Bash";
|
|
65
|
+
if (value.indexOf("read") !== -1) return "Read";
|
|
66
|
+
if (value.indexOf("write") !== -1 || value.indexOf("create") !== -1) return "Write";
|
|
67
|
+
if (value.indexOf("edit") !== -1 || value.indexOf("replace") !== -1 || value.indexOf("delete") !== -1) return "Edit";
|
|
68
|
+
if (value.indexOf("search_web") !== -1 || value.indexOf("web_search") !== -1) return "WebSearch";
|
|
69
|
+
if (value.indexOf("fetch") !== -1 || value.indexOf("url") !== -1) return "WebFetch";
|
|
70
|
+
if (value.indexOf("search") !== -1 || value.indexOf("grep") !== -1) return "Grep";
|
|
71
|
+
if (value.indexOf("glob") !== -1 || value.indexOf("find") !== -1) return "Glob";
|
|
72
|
+
if (value.indexOf("subagent") !== -1 || value.indexOf("task") !== -1) return "Task";
|
|
73
|
+
return name || "Tool";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function createAntigravityQueryHandle(binaryPath, queryOpts, onFinished) {
|
|
77
|
+
var processHandle = null;
|
|
78
|
+
var outputReader = null;
|
|
79
|
+
var events = [];
|
|
80
|
+
var eventWaiter = null;
|
|
81
|
+
var messages = [];
|
|
82
|
+
var started = false;
|
|
83
|
+
var activeTurn = false;
|
|
84
|
+
var ended = false;
|
|
85
|
+
var inputEnded = false;
|
|
86
|
+
var finished = false;
|
|
87
|
+
var stderr = "";
|
|
88
|
+
var sessionId = queryOpts.resumeSessionId || null;
|
|
89
|
+
var model = queryOpts.model || "auto";
|
|
90
|
+
var effort = queryOpts.effort || null;
|
|
91
|
+
var toolPolicy = queryOpts.dangerouslySkipPermissions ? "allow-all" : "ask";
|
|
92
|
+
var blockCounter = 0;
|
|
93
|
+
var textBlockId = null;
|
|
94
|
+
var toolBlocks = {};
|
|
95
|
+
var latestUsage = null;
|
|
96
|
+
var firstMessage = true;
|
|
97
|
+
|
|
98
|
+
function notifyFinished() {
|
|
99
|
+
if (finished) return;
|
|
100
|
+
finished = true;
|
|
101
|
+
if (typeof onFinished === "function") onFinished();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function pushEvent(event) {
|
|
105
|
+
if (ended) return;
|
|
106
|
+
if (eventWaiter) {
|
|
107
|
+
var resolve = eventWaiter;
|
|
108
|
+
eventWaiter = null;
|
|
109
|
+
resolve({ value: event, done: false });
|
|
110
|
+
} else {
|
|
111
|
+
events.push(event);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function endEvents() {
|
|
116
|
+
if (ended) return;
|
|
117
|
+
ended = true;
|
|
118
|
+
if (eventWaiter) {
|
|
119
|
+
var resolve = eventWaiter;
|
|
120
|
+
eventWaiter = null;
|
|
121
|
+
resolve({ value: undefined, done: true });
|
|
122
|
+
}
|
|
123
|
+
notifyFinished();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function buildPrompt(text, images) {
|
|
127
|
+
var prompt = text || "";
|
|
128
|
+
if (firstMessage) {
|
|
129
|
+
var systemParts = [queryOpts.systemPrompt, queryOpts.appendSystemPrompt].filter(function(part) { return !!part; });
|
|
130
|
+
if (systemParts.length) prompt = systemParts.join("\n\n") + "\n\n" + prompt;
|
|
131
|
+
firstMessage = false;
|
|
132
|
+
}
|
|
133
|
+
if (images && images.length) {
|
|
134
|
+
prompt += "\n\n[Clay could not forward " + images.length + " attached image(s) because Antigravity CLI stream input currently accepts text only.]";
|
|
135
|
+
}
|
|
136
|
+
return prompt;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function writeNextMessage() {
|
|
140
|
+
if (!processHandle || !processHandle.stdin || activeTurn || !messages.length) return;
|
|
141
|
+
activeTurn = true;
|
|
142
|
+
textBlockId = null;
|
|
143
|
+
toolBlocks = {};
|
|
144
|
+
pushEvent({ yokeType: "turn_start", messageType: "user" });
|
|
145
|
+
processHandle.stdin.write(JSON.stringify({
|
|
146
|
+
event: "user",
|
|
147
|
+
message: { content: messages.shift() },
|
|
148
|
+
}) + "\n");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function finishTurn(result) {
|
|
152
|
+
result = result || {};
|
|
153
|
+
if (result.conversation_id) sessionId = result.conversation_id;
|
|
154
|
+
if (result.usage) latestUsage = result.usage;
|
|
155
|
+
var status = result.status || "SUCCESS";
|
|
156
|
+
if (status !== "SUCCESS") {
|
|
157
|
+
if (status === "CANCELED" || status === "INTERRUPTED") {
|
|
158
|
+
pushEvent({ yokeType: "interrupted" });
|
|
159
|
+
} else {
|
|
160
|
+
var errorText = result.error || "Antigravity CLI ended the turn with status " + status;
|
|
161
|
+
pushEvent(isAuthError(errorText)
|
|
162
|
+
? { yokeType: "auth_required", vendor: "antigravity" }
|
|
163
|
+
: { yokeType: "error", text: errorText });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
pushEvent({
|
|
167
|
+
yokeType: "result",
|
|
168
|
+
messageType: "assistant",
|
|
169
|
+
cost: null,
|
|
170
|
+
duration: typeof result.duration_seconds === "number" ? result.duration_seconds * 1000 : null,
|
|
171
|
+
usage: latestUsage ? {
|
|
172
|
+
input_tokens: latestUsage.input_tokens || 0,
|
|
173
|
+
output_tokens: latestUsage.output_tokens || 0,
|
|
174
|
+
cache_read_input_tokens: latestUsage.cache_read_tokens || 0,
|
|
175
|
+
cache_creation_input_tokens: 0,
|
|
176
|
+
} : null,
|
|
177
|
+
sessionId: sessionId,
|
|
178
|
+
lastStreamInputTokens: latestUsage ? latestUsage.input_tokens : null,
|
|
179
|
+
});
|
|
180
|
+
activeTurn = false;
|
|
181
|
+
if (messages.length) writeNextMessage();
|
|
182
|
+
else if (inputEnded && processHandle && processHandle.stdin) processHandle.stdin.end();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function handleStep(step) {
|
|
186
|
+
if (!step) return;
|
|
187
|
+
if (step.step_type === "agent_response") {
|
|
188
|
+
if (!textBlockId) {
|
|
189
|
+
textBlockId = "agy_blk_" + (++blockCounter);
|
|
190
|
+
pushEvent({ yokeType: "text_start", blockId: textBlockId });
|
|
191
|
+
}
|
|
192
|
+
if (step.text_delta) pushEvent({ yokeType: "text_delta", blockId: textBlockId, text: step.text_delta });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (step.step_type !== "tool") return;
|
|
196
|
+
var info = step.tool_info || {};
|
|
197
|
+
var id = "agy_tool_" + step.step_index;
|
|
198
|
+
var name = toolName(step.tool_name || info.name);
|
|
199
|
+
if (!toolBlocks[id]) {
|
|
200
|
+
toolBlocks[id] = "agy_blk_" + (++blockCounter);
|
|
201
|
+
pushEvent({ yokeType: "tool_start", blockId: toolBlocks[id], toolId: id, toolName: name });
|
|
202
|
+
pushEvent({ yokeType: "tool_executing", blockId: toolBlocks[id], toolId: id, toolName: name, input: info.parameters || {} });
|
|
203
|
+
}
|
|
204
|
+
if (step.state === "DONE") {
|
|
205
|
+
var error = info.error;
|
|
206
|
+
pushEvent({
|
|
207
|
+
yokeType: "tool_result",
|
|
208
|
+
blockId: toolBlocks[id],
|
|
209
|
+
toolId: id,
|
|
210
|
+
content: error ? (error.message || String(error)) : (info.output || ""),
|
|
211
|
+
isError: !!error,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function handleOutput(line) {
|
|
217
|
+
var event;
|
|
218
|
+
try { event = JSON.parse(line); } catch (e) { return; }
|
|
219
|
+
if (event.event === "init") {
|
|
220
|
+
sessionId = event.conversation_id || (event.init && event.init.conversation_id) || sessionId;
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (event.event === "step_update") {
|
|
224
|
+
handleStep(event.step_update);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (event.event === "result") finishTurn(event.result);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function start() {
|
|
231
|
+
if (started || ended) return;
|
|
232
|
+
started = true;
|
|
233
|
+
var args = ["--input-format", "stream-json", "--output-format", "stream-json"];
|
|
234
|
+
if (sessionId) args.push("--conversation", sessionId);
|
|
235
|
+
if (model && model !== "auto") args.push("--model", model);
|
|
236
|
+
if (effort) args.push("--effort", effort);
|
|
237
|
+
if (toolPolicy === "allow-all") args.push("--dangerously-skip-permissions");
|
|
238
|
+
var spawnProcess = queryOpts._spawn || childProcess.spawn;
|
|
239
|
+
processHandle = spawnProcess(binaryPath, args, {
|
|
240
|
+
cwd: queryOpts.cwd || process.cwd(),
|
|
241
|
+
env: Object.assign({}, process.env, queryOpts.env || {}),
|
|
242
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
243
|
+
});
|
|
244
|
+
outputReader = readline.createInterface({ input: processHandle.stdout });
|
|
245
|
+
outputReader.on("line", handleOutput);
|
|
246
|
+
processHandle.stderr.on("data", function(chunk) {
|
|
247
|
+
stderr += String(chunk);
|
|
248
|
+
if (stderr.length > 65536) stderr = stderr.slice(-65536);
|
|
249
|
+
});
|
|
250
|
+
processHandle.on("error", function(err) {
|
|
251
|
+
pushEvent({ yokeType: "error", text: "Failed to start Antigravity CLI: " + err.message });
|
|
252
|
+
endEvents();
|
|
253
|
+
});
|
|
254
|
+
processHandle.on("exit", function(code, signal) {
|
|
255
|
+
if (!ended && activeTurn) {
|
|
256
|
+
var message = stderr.trim() || "Antigravity CLI exited before completing the turn";
|
|
257
|
+
pushEvent(isAuthError(message)
|
|
258
|
+
? { yokeType: "auth_required", vendor: "antigravity" }
|
|
259
|
+
: { yokeType: "error", text: message + (code ? " (exit " + code + ")" : signal ? " (" + signal + ")" : "") });
|
|
260
|
+
}
|
|
261
|
+
endEvents();
|
|
262
|
+
});
|
|
263
|
+
writeNextMessage();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
var handle = {
|
|
267
|
+
[Symbol.asyncIterator]: function() {
|
|
268
|
+
return {
|
|
269
|
+
next: function() {
|
|
270
|
+
if (events.length) return Promise.resolve({ value: events.shift(), done: false });
|
|
271
|
+
if (ended) return Promise.resolve({ value: undefined, done: true });
|
|
272
|
+
return new Promise(function(resolve) { eventWaiter = resolve; });
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
},
|
|
276
|
+
pushMessage: function(text, images) {
|
|
277
|
+
if (ended || inputEnded) return false;
|
|
278
|
+
messages.push(buildPrompt(text, images));
|
|
279
|
+
if (!started) start();
|
|
280
|
+
else writeNextMessage();
|
|
281
|
+
return true;
|
|
282
|
+
},
|
|
283
|
+
setModel: function(value) {
|
|
284
|
+
if (started) return Promise.reject(new Error("Antigravity CLI cannot switch models after a streaming session starts"));
|
|
285
|
+
model = value || "auto";
|
|
286
|
+
return Promise.resolve();
|
|
287
|
+
},
|
|
288
|
+
setEffort: function(value) {
|
|
289
|
+
if (started) return Promise.reject(new Error("Antigravity CLI cannot switch effort after a streaming session starts"));
|
|
290
|
+
effort = value || null;
|
|
291
|
+
return Promise.resolve();
|
|
292
|
+
},
|
|
293
|
+
setToolPolicy: function(policy) {
|
|
294
|
+
if (started) return Promise.reject(new Error("Antigravity CLI cannot switch tool policy after a streaming session starts"));
|
|
295
|
+
toolPolicy = policy === "allow-all" ? "allow-all" : "ask";
|
|
296
|
+
return Promise.resolve();
|
|
297
|
+
},
|
|
298
|
+
stopTask: function() { return Promise.resolve(); },
|
|
299
|
+
getContextUsage: function() {
|
|
300
|
+
if (!latestUsage) return Promise.resolve(null);
|
|
301
|
+
return Promise.resolve({
|
|
302
|
+
input_tokens: (latestUsage.input_tokens || 0) + (latestUsage.cache_read_tokens || 0),
|
|
303
|
+
contextWindow: null,
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
endInput: function() {
|
|
307
|
+
inputEnded = true;
|
|
308
|
+
if (!activeTurn && processHandle && processHandle.stdin) processHandle.stdin.end();
|
|
309
|
+
},
|
|
310
|
+
abort: function() {
|
|
311
|
+
if (ended) return;
|
|
312
|
+
pushEvent({ yokeType: "interrupted" });
|
|
313
|
+
if (processHandle) processHandle.kill("SIGINT");
|
|
314
|
+
endEvents();
|
|
315
|
+
},
|
|
316
|
+
close: function() {
|
|
317
|
+
inputEnded = true;
|
|
318
|
+
if (processHandle && processHandle.stdin) processHandle.stdin.end();
|
|
319
|
+
endEvents();
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
return handle;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function createAntigravityAdapter(opts) {
|
|
326
|
+
opts = opts || {};
|
|
327
|
+
var cwd = opts.cwd || process.cwd();
|
|
328
|
+
var binaryPath = opts._binaryPath || null;
|
|
329
|
+
var cachedModels = ["auto"];
|
|
330
|
+
var activeHandles = [];
|
|
331
|
+
|
|
332
|
+
function capabilities() {
|
|
333
|
+
return {
|
|
334
|
+
effort: true,
|
|
335
|
+
midSessionModelSwitch: false,
|
|
336
|
+
fork: false,
|
|
337
|
+
rollback: false,
|
|
338
|
+
sessionListing: false,
|
|
339
|
+
sessionRename: false,
|
|
340
|
+
thinking: false,
|
|
341
|
+
betas: false,
|
|
342
|
+
rewind: false,
|
|
343
|
+
sessionResume: true,
|
|
344
|
+
promptSuggestions: false,
|
|
345
|
+
elicitation: false,
|
|
346
|
+
fileCheckpointing: false,
|
|
347
|
+
contextCompacting: false,
|
|
348
|
+
skillSharing: true,
|
|
349
|
+
toolPolicy: ["ask", "allow-all"],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
var adapter = {
|
|
354
|
+
vendor: "antigravity",
|
|
355
|
+
init: async function() {
|
|
356
|
+
if (!binaryPath) binaryPath = findBinary();
|
|
357
|
+
if (!binaryPath) throw new Error("Antigravity CLI binary not found: agy");
|
|
358
|
+
var fetched = opts._fetchModels ? await opts._fetchModels(binaryPath, cwd) : await fetchModels(binaryPath, cwd);
|
|
359
|
+
if (fetched && fetched.length) cachedModels = fetched;
|
|
360
|
+
var skills = skillDiscovery.discoverSkills(cwd).map(function(skill) { return skill.name; });
|
|
361
|
+
return {
|
|
362
|
+
models: cachedModels.slice(),
|
|
363
|
+
defaultModel: cachedModels[0] || "auto",
|
|
364
|
+
skills: skills,
|
|
365
|
+
slashCommands: skills,
|
|
366
|
+
fastModeState: null,
|
|
367
|
+
capabilities: capabilities(),
|
|
368
|
+
};
|
|
369
|
+
},
|
|
370
|
+
supportedModels: function() { return Promise.resolve(cachedModels.slice()); },
|
|
371
|
+
createToolServer: function() { return null; },
|
|
372
|
+
createQuery: async function(queryOpts) {
|
|
373
|
+
if (!binaryPath) await adapter.init();
|
|
374
|
+
queryOpts = queryOpts || {};
|
|
375
|
+
var antigravityOpts = (queryOpts.adapterOptions && queryOpts.adapterOptions.ANTIGRAVITY) || {};
|
|
376
|
+
var handle;
|
|
377
|
+
handle = createAntigravityQueryHandle(binaryPath, Object.assign({}, queryOpts, {
|
|
378
|
+
dangerouslySkipPermissions: !!antigravityOpts.dangerouslySkipPermissions,
|
|
379
|
+
env: antigravityOpts.env || null,
|
|
380
|
+
_spawn: opts._spawn || null,
|
|
381
|
+
}), function() {
|
|
382
|
+
var index = activeHandles.indexOf(handle);
|
|
383
|
+
if (index !== -1) activeHandles.splice(index, 1);
|
|
384
|
+
});
|
|
385
|
+
activeHandles.push(handle);
|
|
386
|
+
return handle;
|
|
387
|
+
},
|
|
388
|
+
generateTitle: async function(messages, titleOpts) {
|
|
389
|
+
var handle = await adapter.createQuery({
|
|
390
|
+
cwd: (titleOpts && titleOpts.cwd) || cwd,
|
|
391
|
+
systemPrompt: "Generate a concise conversation title of 3 to 8 words. Output only the title.",
|
|
392
|
+
});
|
|
393
|
+
var prompt = messages.join("\n");
|
|
394
|
+
var title = "";
|
|
395
|
+
handle.pushMessage(prompt);
|
|
396
|
+
handle.endInput();
|
|
397
|
+
for await (var event of handle) {
|
|
398
|
+
if (event.yokeType === "text_delta") title += event.text;
|
|
399
|
+
}
|
|
400
|
+
return title.trim().replace(/^['\"]|['\"]$/g, "").slice(0, 80) || "New conversation";
|
|
401
|
+
},
|
|
402
|
+
shutdown: function() {
|
|
403
|
+
var handles = activeHandles.slice();
|
|
404
|
+
for (var i = 0; i < handles.length; i++) handles[i].abort();
|
|
405
|
+
activeHandles = [];
|
|
406
|
+
return Promise.resolve(true);
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
return adapter;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
module.exports = {
|
|
413
|
+
createAntigravityAdapter: createAntigravityAdapter,
|
|
414
|
+
createAntigravityQueryHandle: createAntigravityQueryHandle,
|
|
415
|
+
fetchModels: fetchModels,
|
|
416
|
+
parseModels: parseModels,
|
|
417
|
+
};
|
package/lib/yoke/index.js
CHANGED
|
@@ -6,13 +6,13 @@ var instructions = require("./instructions");
|
|
|
6
6
|
var vendorRegistry = require("./vendor-registry");
|
|
7
7
|
var createClaudeAdapter = require("./adapters/claude").createClaudeAdapter;
|
|
8
8
|
var createCodexAdapter = require("./adapters/codex").createCodexAdapter;
|
|
9
|
-
var
|
|
9
|
+
var createAntigravityAdapter = require("./adapters/antigravity").createAntigravityAdapter;
|
|
10
10
|
var createOpenCodeAdapter = require("./adapters/opencode").createOpenCodeAdapter;
|
|
11
11
|
var createKiroAdapter = require("./adapters/kiro").createKiroAdapter;
|
|
12
12
|
|
|
13
13
|
// Keep the first session in a new project predictable. This order is also
|
|
14
14
|
// used by the UI when a project has no remembered vendor yet.
|
|
15
|
-
var DEFAULT_VENDOR_ORDER = ["claude", "codex", "
|
|
15
|
+
var DEFAULT_VENDOR_ORDER = ["claude", "codex", "antigravity", "opencode", "kiro"];
|
|
16
16
|
|
|
17
17
|
function resolveDefaultVendor(availableVendors) {
|
|
18
18
|
availableVendors = availableVendors || {};
|
|
@@ -73,8 +73,8 @@ function createAdapter(opts) {
|
|
|
73
73
|
adapter = createClaudeAdapter(opts);
|
|
74
74
|
} else if (vendor === "codex") {
|
|
75
75
|
adapter = createCodexAdapter(opts);
|
|
76
|
-
} else if (vendor === "
|
|
77
|
-
adapter =
|
|
76
|
+
} else if (vendor === "antigravity") {
|
|
77
|
+
adapter = createAntigravityAdapter(opts);
|
|
78
78
|
} else if (vendor === "opencode") {
|
|
79
79
|
adapter = createOpenCodeAdapter(opts);
|
|
80
80
|
} else if (vendor === "kiro") {
|
|
@@ -102,7 +102,7 @@ function logAuthCheck(auth) {
|
|
|
102
102
|
if (_lastAuthLogKey === key && now - _lastAuthLogAt < 30000) return;
|
|
103
103
|
_lastAuthLogKey = key;
|
|
104
104
|
_lastAuthLogAt = now;
|
|
105
|
-
console.log("[yoke] Auth check: claude=" + auth.claude + " codex=" + auth.codex + "
|
|
105
|
+
console.log("[yoke] Auth check: claude=" + auth.claude + " codex=" + auth.codex + " antigravity=" + auth.antigravity + " opencode=" + auth.opencode + " kiro=" + auth.kiro);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function checkAuth() {
|
|
@@ -236,7 +236,7 @@ function checkAuth() {
|
|
|
236
236
|
_authCache = {
|
|
237
237
|
claude: checkClaude(),
|
|
238
238
|
codex: checkCodex(),
|
|
239
|
-
|
|
239
|
+
antigravity: !!lookupBinary("agy"),
|
|
240
240
|
opencode: !!lookupBinary("opencode"),
|
|
241
241
|
kiro: checkKiro(),
|
|
242
242
|
};
|
|
@@ -261,7 +261,7 @@ function checkInstalled() {
|
|
|
261
261
|
|
|
262
262
|
var fs = require("fs");
|
|
263
263
|
var execFileSync = require("child_process").execFileSync;
|
|
264
|
-
var result = { claude: false, codex: false,
|
|
264
|
+
var result = { claude: false, codex: false, antigravity: false, opencode: false, kiro: false };
|
|
265
265
|
try {
|
|
266
266
|
if (process.platform === "win32") execFileSync("where", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
267
267
|
else execFileSync("which", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
@@ -289,9 +289,14 @@ function checkInstalled() {
|
|
|
289
289
|
if (whichOut.trim()) result.codex = true;
|
|
290
290
|
}
|
|
291
291
|
} catch (e) {}
|
|
292
|
+
try {
|
|
293
|
+
var antigravityBinary = process.platform === "win32"
|
|
294
|
+
? execFileSync("where", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
|
|
295
|
+
: execFileSync("which", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
296
|
+
result.antigravity = !!antigravityBinary.trim();
|
|
297
|
+
} catch (e) {}
|
|
292
298
|
try {
|
|
293
299
|
var acpProfiles = require("./acp-agent-profiles");
|
|
294
|
-
result.gemini = !!acpProfiles.findAcpAgentPath(acpProfiles.getAcpAgentProfile("gemini"));
|
|
295
300
|
result.opencode = !!acpProfiles.findAcpAgentPath(acpProfiles.getAcpAgentProfile("opencode"));
|
|
296
301
|
} catch (e) {}
|
|
297
302
|
_installedCache = result;
|
|
@@ -318,7 +323,7 @@ function createAdapters(opts) {
|
|
|
318
323
|
// that `claude auth status` does not always detect. Runtime auth failures are
|
|
319
324
|
// handled downstream via query-level error detection.
|
|
320
325
|
var installed = checkInstalled();
|
|
321
|
-
var auth = { claude: false, codex: false,
|
|
326
|
+
var auth = { claude: false, codex: false, antigravity: false, opencode: false, kiro: false };
|
|
322
327
|
var adapters = {};
|
|
323
328
|
|
|
324
329
|
function supportsConfiguredIsolation(vendor) {
|
|
@@ -353,20 +358,20 @@ function createAdapters(opts) {
|
|
|
353
358
|
}
|
|
354
359
|
}
|
|
355
360
|
|
|
356
|
-
var
|
|
357
|
-
for (var
|
|
358
|
-
var
|
|
359
|
-
var
|
|
360
|
-
if (installed[
|
|
361
|
+
var subprocessVendors = ["antigravity", "opencode"];
|
|
362
|
+
for (var subprocessIndex = 0; subprocessIndex < subprocessVendors.length; subprocessIndex++) {
|
|
363
|
+
var subprocessVendor = subprocessVendors[subprocessIndex];
|
|
364
|
+
var subprocessInfo = vendorRegistry.getVendorInfo(subprocessVendor);
|
|
365
|
+
if (installed[subprocessVendor] && supportsConfiguredIsolation(subprocessVendor)) {
|
|
361
366
|
try {
|
|
362
|
-
adapters[
|
|
363
|
-
auth[
|
|
364
|
-
console.log("[yoke] Adapter created: " +
|
|
367
|
+
adapters[subprocessVendor] = createAdapter({ vendor: subprocessVendor, cwd: opts.cwd, slug: opts.slug });
|
|
368
|
+
auth[subprocessVendor] = true;
|
|
369
|
+
console.log("[yoke] Adapter created: " + subprocessVendor);
|
|
365
370
|
} catch (e) {
|
|
366
|
-
console.error("[yoke] Failed to create adapter for " +
|
|
371
|
+
console.error("[yoke] Failed to create adapter for " + subprocessVendor + ":", e.message);
|
|
367
372
|
}
|
|
368
|
-
} else if (installed[
|
|
369
|
-
console.log("[yoke] " +
|
|
373
|
+
} else if (installed[subprocessVendor] && opts.osUsers) {
|
|
374
|
+
console.log("[yoke] " + subprocessInfo.displayName + " adapter disabled: OS-user isolation requires per-user spawning");
|
|
370
375
|
}
|
|
371
376
|
}
|
|
372
377
|
|
|
@@ -40,13 +40,13 @@ var VENDOR_REGISTRY = {
|
|
|
40
40
|
},
|
|
41
41
|
rateLimitTracking: true,
|
|
42
42
|
},
|
|
43
|
-
|
|
44
|
-
displayName: "
|
|
45
|
-
loginCommand: "
|
|
46
|
-
binaryName: "
|
|
47
|
-
avatar: "/
|
|
43
|
+
antigravity: {
|
|
44
|
+
displayName: "Antigravity CLI",
|
|
45
|
+
loginCommand: "agy",
|
|
46
|
+
binaryName: "agy",
|
|
47
|
+
avatar: "/antigravity-avatar.png",
|
|
48
48
|
sessionModes: ["gui"],
|
|
49
|
-
effortLevels: [],
|
|
49
|
+
effortLevels: ["low", "medium", "high"],
|
|
50
50
|
osUserIsolation: false,
|
|
51
51
|
usageDashboard: null,
|
|
52
52
|
rateLimitTracking: false,
|
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Gemini CLI">
|
|
2
|
-
<defs>
|
|
3
|
-
<linearGradient id="g" x1="8" y1="56" x2="56" y2="8" gradientUnits="userSpaceOnUse">
|
|
4
|
-
<stop stop-color="#4f7cff"/>
|
|
5
|
-
<stop offset="0.5" stop-color="#9b5cff"/>
|
|
6
|
-
<stop offset="1" stop-color="#ff65b3"/>
|
|
7
|
-
</linearGradient>
|
|
8
|
-
</defs>
|
|
9
|
-
<rect width="64" height="64" rx="16" fill="#10131d"/>
|
|
10
|
-
<path d="M32 8c2.3 14.1 9.9 21.7 24 24-14.1 2.3-21.7 9.9-24 24-2.3-14.1-9.9-21.7-24-24 14.1-2.3 21.7-9.9 24-24Z" fill="url(#g)"/>
|
|
11
|
-
</svg>
|