clay-server 3.4.0-beta.2 → 3.4.0-beta.4

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.
@@ -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-gemini" class="vendor-toggle-btn" data-vendor="gemini">
532
- <img src="/gemini-avatar.svg" class="vendor-toggle-icon" alt="Gemini">
533
- <span class="vendor-toggle-label">Gemini CLI</span>
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
- gemini: [],
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 vendorBtnGemini = $("vendor-btn-gemini");
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
- gemini: vendorBtnGemini,
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 (vendorBtnGemini) vendorBtnGemini.addEventListener("click", function() { onVendorClick("gemini"); });
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
- gemini: "/gemini-avatar.svg",
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
- gemini: "Gemini CLI",
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", "gemini", "opencode", "kiro"];
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
- gemini: "https://github.com/google-gemini/gemini-cli",
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
- gemini: "/gemini-avatar.svg",
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", gemini: "Gemini CLI", opencode: "OpenCode", kiro: "Kiro CLI" };
160
- var vendorKeys = ["claude", "codex", "gemini", "opencode", "kiro"];
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
- gemini: "Gemini CLI",
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
- gemini: "/gemini-avatar.svg",
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 acpVendorKeys = ["gemini", "opencode"];
1797
+ var subprocessVendorKeys = ["antigravity", "opencode"];
1794
1798
  var acpProfiles = require("./yoke/acp-agent-profiles");
1795
- for (var acpVendorIndex = 0; acpVendorIndex < acpVendorKeys.length; acpVendorIndex++) {
1796
- var acpVendor = acpVendorKeys[acpVendorIndex];
1797
- var acpInfo = yoke.getVendorInfo(acpVendor);
1798
- if (!linuxUser || acpInfo.osUserIsolation) {
1799
- var acpProfile = acpProfiles.getAcpAgentProfile(acpVendor);
1800
- if (acpProfiles.findAcpAgentPath(acpProfile)) result.push(acpVendor);
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] " + acpInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
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
 
@@ -2,7 +2,13 @@
2
2
 
3
3
  var buildShape = require("./session-spawn-mcp-server").buildShape;
4
4
 
5
- var MEMORY_CONTRACT = "The sticky-note board persists across sessions and is a user-facing artifact shared with people and Clay agents, not private agent scratch space. Default to not writing. Create a note proactively only when the user explicitly asks to remember or track something, or when all of these are true: it will remain useful after the current task and session, it is not already adequately recorded in the repository or another note, and the user would likely be glad to find it on the board a week later. Good notes capture an unresolved commitment, durable product decision, user preference, constraint, or handoff that will materially change future work. Never create a note merely because work is important, lengthy, spans agents or restarts, or might help another agent. Do not record completed work, implementation details, test results, investigation logs, transient blockers, conversation summaries, or announcements of your own activity. When uncertain, do not write. Updates are visible too: update only when durable state materially changes, and remove a note created by your session when it stops being useful instead of turning it into a completion log. Put a concise plain-text title on the first line, stay focused on one topic, and include only the context needed for future action.";
5
+ var MEMORY_CONTRACT =
6
+ "The sticky-note board persists across sessions and is a user-facing artifact shared with people and Clay agents, not private agent scratch space. " +
7
+ "Default to not writing. Create a note proactively only when the user explicitly asks to remember or track something, or when all of these are true: it will remain useful after the current task and session, it is not already adequately recorded in the repository or another note, and the user would likely be glad to find it on the board a week later. " +
8
+ "Good notes capture an unresolved commitment, durable product decision, user preference, constraint, or handoff that will materially change future work. " +
9
+ "Important exception for deferred defects: while doing code or technical work, actively create a sticky note when you discover a concrete defect, regression risk, security issue, or data-loss risk that is outside the current session goal and will remain unfixed when the turn ends. Do not wait for the user to ask. Include the observable evidence, affected component, likely impact, and a clear next action. Check active notes first when a duplicate is plausible. Do not create defect notes for speculation, general cleanup ideas, or problems you fixed in the current session. " +
10
+ "Never create a note merely because work is important, lengthy, spans agents or restarts, or might help another agent. Do not record completed work, implementation details, test results, investigation logs, transient blockers, conversation summaries, or announcements of your own activity. When uncertain, do not write. " +
11
+ "Updates are visible too: update only when durable state materially changes, and remove a note created by your session when it stops being useful instead of turning it into a completion log. Put a concise plain-text title on the first line, stay focused on one topic, and include only the context needed for future action.";
6
12
 
7
13
  function getToolDefs(handlers) {
8
14
  return [
@@ -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 createGeminiAdapter = require("./adapters/gemini").createGeminiAdapter;
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", "gemini", "opencode", "kiro"];
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 === "gemini") {
77
- adapter = createGeminiAdapter(opts);
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 + " gemini=" + auth.gemini + " opencode=" + auth.opencode + " kiro=" + auth.kiro);
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
- gemini: !!lookupBinary("gemini"),
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, gemini: false, opencode: false, kiro: 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, gemini: false, opencode: false, kiro: 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 acpVendors = ["gemini", "opencode"];
357
- for (var acpIndex = 0; acpIndex < acpVendors.length; acpIndex++) {
358
- var acpVendor = acpVendors[acpIndex];
359
- var acpInfo = vendorRegistry.getVendorInfo(acpVendor);
360
- if (installed[acpVendor] && supportsConfiguredIsolation(acpVendor)) {
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[acpVendor] = createAdapter({ vendor: acpVendor, cwd: opts.cwd, slug: opts.slug });
363
- auth[acpVendor] = true;
364
- console.log("[yoke] Adapter created: " + acpVendor);
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 " + acpVendor + ":", e.message);
371
+ console.error("[yoke] Failed to create adapter for " + subprocessVendor + ":", e.message);
367
372
  }
368
- } else if (installed[acpVendor] && opts.osUsers) {
369
- console.log("[yoke] " + acpInfo.displayName + " adapter disabled: OS-user isolation requires per-user spawning");
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
- gemini: {
44
- displayName: "Gemini CLI",
45
- loginCommand: "gemini",
46
- binaryName: "gemini",
47
- avatar: "/gemini-avatar.svg",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "3.4.0-beta.2",
3
+ "version": "3.4.0-beta.4",
4
4
  "description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",
@@ -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>
@@ -1,7 +0,0 @@
1
- var createAcpAdapter = require("./acp").createAcpAdapter;
2
-
3
- function createGeminiAdapter(opts) {
4
- return createAcpAdapter("gemini", opts);
5
- }
6
-
7
- module.exports = { createGeminiAdapter: createGeminiAdapter };