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

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.
Files changed (82) hide show
  1. package/lib/daemon-projects.js +3 -3
  2. package/lib/daemon.js +59 -59
  3. package/lib/project-connection.js +35 -7
  4. package/lib/project-debate.js +4 -0
  5. package/lib/project-mate-interaction.js +21 -1
  6. package/lib/project-memory.js +3 -0
  7. package/lib/project-models.js +220 -0
  8. package/lib/project-session-handoff.js +162 -0
  9. package/lib/project-session-pair.js +14 -7
  10. package/lib/project-session-spawn.js +1 -1
  11. package/lib/project-sessions.js +13 -45
  12. package/lib/project-worker-proposal.js +51 -14
  13. package/lib/project.js +38 -80
  14. package/lib/public/antigravity-avatar.png +0 -0
  15. package/lib/public/app.js +29 -0
  16. package/lib/public/copilot-avatar.svg +5 -0
  17. package/lib/public/css/command-palette.css +22 -2
  18. package/lib/public/css/icon-strip.css +29 -13
  19. package/lib/public/css/input.css +56 -0
  20. package/lib/public/css/mates.css +6 -0
  21. package/lib/public/css/menus.css +38 -25
  22. package/lib/public/css/messages.css +9 -0
  23. package/lib/public/css/pane.css +16 -3
  24. package/lib/public/css/pwa-mobile.css +17 -0
  25. package/lib/public/css/session-actions.css +98 -0
  26. package/lib/public/css/title-bar.css +19 -0
  27. package/lib/public/grok-avatar.svg +4 -0
  28. package/lib/public/index.html +29 -7
  29. package/lib/public/junie-avatar.svg +11 -0
  30. package/lib/public/kimi-avatar.svg +4 -0
  31. package/lib/public/modules/agent-config-selects.js +69 -0
  32. package/lib/public/modules/app-header.js +4 -22
  33. package/lib/public/modules/app-messages.js +54 -11
  34. package/lib/public/modules/app-panels.js +36 -83
  35. package/lib/public/modules/app-projects.js +3 -1
  36. package/lib/public/modules/app-rendering.js +19 -4
  37. package/lib/public/modules/background-tasks-ui.js +55 -0
  38. package/lib/public/modules/mate-sidebar.js +11 -3
  39. package/lib/public/modules/model-picker.js +263 -0
  40. package/lib/public/modules/notifications.js +7 -1
  41. package/lib/public/modules/pane-bridge.js +11 -0
  42. package/lib/public/modules/pane-links.js +23 -0
  43. package/lib/public/modules/project-switcher.js +7 -6
  44. package/lib/public/modules/session-actions.js +294 -0
  45. package/lib/public/modules/sidebar-mates.js +1 -1
  46. package/lib/public/modules/sidebar-mobile.js +2 -1
  47. package/lib/public/modules/sidebar-projects.js +34 -43
  48. package/lib/public/modules/sidebar-sessions.js +20 -6
  49. package/lib/public/modules/split-pair-ui.js +16 -77
  50. package/lib/public/modules/split-view.js +3 -0
  51. package/lib/public/modules/tools.js +6 -1
  52. package/lib/public/modules/vendor-priority.js +14 -0
  53. package/lib/public/modules/vendor-selection.js +20 -0
  54. package/lib/public/modules/worker-proposal.js +6 -1
  55. package/lib/public/modules/worktree-location.js +17 -0
  56. package/lib/public/qwen-avatar.svg +4 -0
  57. package/lib/public/style.css +4 -2
  58. package/lib/sdk-bridge.js +103 -56
  59. package/lib/sdk-message-processor.js +26 -4
  60. package/lib/session-handoff-context.js +110 -0
  61. package/lib/session-notes-mcp-server.js +8 -1
  62. package/lib/sessions.js +4 -0
  63. package/lib/worktree.js +9 -4
  64. package/lib/ws-schema.js +9 -1
  65. package/lib/yoke/acp-agent-profiles.js +64 -14
  66. package/lib/yoke/adapters/antigravity.js +417 -0
  67. package/lib/yoke/adapters/claude.js +34 -0
  68. package/lib/yoke/adapters/codex.js +101 -5
  69. package/lib/yoke/adapters/copilot.js +7 -0
  70. package/lib/yoke/adapters/grok.js +7 -0
  71. package/lib/yoke/adapters/junie.js +7 -0
  72. package/lib/yoke/adapters/kimi.js +7 -0
  73. package/lib/yoke/adapters/kiro.js +2 -2
  74. package/lib/yoke/adapters/qwen.js +7 -0
  75. package/lib/yoke/codex-app-server.js +25 -8
  76. package/lib/yoke/index.js +67 -28
  77. package/lib/yoke/instructions.js +3 -0
  78. package/lib/yoke/interface.js +12 -0
  79. package/lib/yoke/vendor-registry.js +61 -6
  80. package/package.json +1 -1
  81. package/lib/public/gemini-avatar.svg +0 -11
  82. package/lib/yoke/adapters/gemini.js +0 -7
package/lib/ws-schema.js CHANGED
@@ -30,6 +30,8 @@ var schema = {
30
30
  "search_session_content": { direction: "c2s", handler: "lib/project-sessions.js", description: "Full-text search within a session" },
31
31
  "load_more_history": { direction: "c2s", handler: "lib/project-sessions.js", description: "Request older history entries for the current session" },
32
32
  "fork_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Fork a session from a given message UUID" },
33
+ "handoff_session": { direction: "c2s", handler: "lib/project-session-handoff.js", description: "Continue the active session in a newly created agent session" },
34
+ "handoff_session_options": { direction: "c2s", handler: "lib/project-session-handoff.js", description: "Request vendors, models, and effort capabilities for the handoff dialog (response reuses the same type)" },
33
35
  "input_sync": { direction: "c2s", handler: "lib/project-sessions.js", description: "Sync the current input field text to other clients" },
34
36
  "tui_transcript_request": { direction: "c2s", handler: "lib/project-sessions.js", description: "Ask for the assistant text index of a Claude TUI session (for hover-to-grab)" },
35
37
  "split_group_create": { direction: "c2s", handler: "lib/session-split-groups.js", description: "Create a persistent two-session split group" },
@@ -49,6 +51,9 @@ var schema = {
49
51
  "search_results": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Session title search results" },
50
52
  "search_content_results": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Full-text content search results" },
51
53
  "fork_complete": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Fork succeeded, includes new session ID" },
54
+ "session_handoff_result": { direction: "s2c", handler: "lib/public/modules/session-actions.js", description: "Session handoff result and new target session ID" },
55
+ "handoff_context": { direction: "s2c", handler: "lib/public/modules/session-actions.js", description: "Timeline marker identifying the source of a handoff session" },
56
+ "handoff_created": { direction: "s2c", handler: "lib/public/modules/session-actions.js", description: "Timeline marker linking a source session to its handoff target" },
52
57
  "input_sync_broadcast": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Broadcast input text from another client" },
53
58
  "tui_transcript_state": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Assistant text index for a Claude TUI session (full replace; sent on request and after each new assistant message)" },
54
59
  "split_groups": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Full persistent split-group list for the current user" },
@@ -109,6 +114,7 @@ var schema = {
109
114
  "subagent_done": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Subagent task completed" },
110
115
  "task_started": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Background task started" },
111
116
  "task_progress": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Background task progress update" },
117
+ "active_background_tasks": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Current background tasks for the active session (full replacement)" },
112
118
  "markdown_edit_present": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Open a user-requested Markdown edit with a before snapshot" },
113
119
 
114
120
  // -----------------------------------------------------------------------
@@ -175,7 +181,8 @@ var schema = {
175
181
  // -----------------------------------------------------------------------
176
182
  // Model / config
177
183
  // -----------------------------------------------------------------------
178
- "set_model": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set the model for the current session" },
184
+ "set_model": { direction: "c2s", handler: "lib/project-models.js", description: "Set the model for the current session" },
185
+ "get_vendor_models": { direction: "c2s", handler: "lib/project-models.js", description: "Load a vendor model catalog" },
179
186
  "set_server_default_model": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set the server-wide default model" },
180
187
  "set_project_default_model": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set the project default model" },
181
188
  "set_permission_mode": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set permission mode for the current session" },
@@ -192,6 +199,7 @@ var schema = {
192
199
  "set_betas": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set beta feature flags" },
193
200
  "set_thinking": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set thinking mode and budget" },
194
201
  "model_info": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Current model and available models list" },
202
+ "model_selection_result": { direction: "s2c", handler: "lib/public/modules/model-picker.js", description: "Acknowledgement or error for a model selection" },
195
203
  "config_state": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Full config state: model, mode, effort, betas, thinking" },
196
204
  "slash_commands": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Available slash commands list" },
197
205
  "fast_mode_state": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Fast mode toggle state" },
@@ -96,21 +96,16 @@ function validateOpenCodeConfig(config) {
96
96
  }
97
97
  }
98
98
 
99
+ function trustDefaultWhenModeIsNotExposed(ctx, next) {
100
+ var options = ctx.state && ctx.state.configOptions;
101
+ for (var i = 0; i < (options || []).length; i++) {
102
+ if (options[i].category === "mode" || options[i].id === "mode") return next();
103
+ }
104
+ if (ctx.state && ctx.state.modes) return next();
105
+ return Promise.resolve();
106
+ }
107
+
99
108
  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
109
  opencode: {
115
110
  vendor: "opencode",
116
111
  displayName: "OpenCode",
@@ -161,6 +156,61 @@ var ACP_AGENT_PROFILES = {
161
156
  return Promise.resolve();
162
157
  },
163
158
  },
159
+ kimi: {
160
+ vendor: "kimi",
161
+ displayName: "Kimi Code",
162
+ binaryName: "kimi",
163
+ overrideName: "KIMI_CLI_PATH",
164
+ args: ["acp"],
165
+ defaultModels: ["auto"],
166
+ defaultModel: "auto",
167
+ sessionResume: null,
168
+ ensureSafePermissionMode: trustDefaultWhenModeIsNotExposed,
169
+ },
170
+ grok: {
171
+ vendor: "grok",
172
+ displayName: "Grok Build",
173
+ binaryName: "grok",
174
+ overrideName: "GROK_CLI_PATH",
175
+ args: ["--no-auto-update", "--permission-mode", "ask", "agent", "stdio"],
176
+ defaultModels: ["auto"],
177
+ defaultModel: "auto",
178
+ sessionResume: null,
179
+ permissionModeGuaranteed: true,
180
+ },
181
+ copilot: {
182
+ vendor: "copilot",
183
+ displayName: "GitHub Copilot CLI",
184
+ binaryName: "copilot",
185
+ overrideName: "COPILOT_CLI_PATH",
186
+ args: ["--acp"],
187
+ defaultModels: ["auto"],
188
+ defaultModel: "auto",
189
+ sessionResume: null,
190
+ ensureSafePermissionMode: trustDefaultWhenModeIsNotExposed,
191
+ },
192
+ qwen: {
193
+ vendor: "qwen",
194
+ displayName: "Qwen Code",
195
+ binaryName: "qwen",
196
+ overrideName: "QWEN_CLI_PATH",
197
+ args: ["--acp", "--approval-mode", "default"],
198
+ defaultModels: ["auto"],
199
+ defaultModel: "auto",
200
+ sessionResume: null,
201
+ permissionModeGuaranteed: true,
202
+ },
203
+ junie: {
204
+ vendor: "junie",
205
+ displayName: "Junie CLI",
206
+ binaryName: "junie",
207
+ overrideName: "JUNIE_CLI_PATH",
208
+ args: ["--acp", "true"],
209
+ defaultModels: ["auto"],
210
+ defaultModel: "auto",
211
+ sessionResume: null,
212
+ ensureSafePermissionMode: trustDefaultWhenModeIsNotExposed,
213
+ },
164
214
  };
165
215
 
166
216
  function getAcpAgentProfile(vendor) {
@@ -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
+ };
@@ -197,6 +197,26 @@ function flattenEvent(raw) {
197
197
  base.summary = raw.summary || null;
198
198
  return base;
199
199
  }
200
+ if (raw.subtype === "background_tasks_changed") {
201
+ base.yokeType = "background_tasks_changed";
202
+ base.tasks = normalizeBackgroundTasks(raw.tasks);
203
+ return base;
204
+ }
205
+ if (raw.subtype === "task_updated") {
206
+ base.yokeType = "task_updated";
207
+ base.task_id = raw.task_id;
208
+ base.patch = raw.patch || {};
209
+ return base;
210
+ }
211
+ if (raw.subtype === "task_notification") {
212
+ base.yokeType = "task_notification";
213
+ base.parentToolId = raw.parent_tool_use_id;
214
+ base.taskId = raw.task_id;
215
+ base.status = raw.status || "completed";
216
+ base.summary = raw.summary || "";
217
+ base.usage = raw.usage || null;
218
+ return base;
219
+ }
200
220
  // Model refusal: the model declined and the CLI either fell back to
201
221
  // another model or ended the turn. Translate to a vendor-neutral
202
222
  // yokeType so the relay never sees SDK subtype strings.
@@ -343,6 +363,20 @@ function flattenEvent(raw) {
343
363
  return base;
344
364
  }
345
365
 
366
+ function normalizeBackgroundTasks(tasks) {
367
+ if (!Array.isArray(tasks)) return [];
368
+ return tasks.map(function(task) {
369
+ var nativeType = task && task.task_type;
370
+ var taskType = nativeType === "local_bash" ? "shell"
371
+ : nativeType === "local_agent" ? "agent" : "other";
372
+ return {
373
+ task_id: task && task.task_id,
374
+ task_type: taskType,
375
+ description: (task && task.description) || "",
376
+ };
377
+ });
378
+ }
379
+
346
380
  // --- QueryHandle ---
347
381
  // Wraps a raw SDK query object with the YOKE QueryHandle interface.
348
382
  // Events are flattened via flattenEvent before yielding.