clay-server 3.3.2-beta.3 → 3.4.0-beta.10
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/daemon-projects.js +3 -3
- package/lib/daemon.js +1 -1
- package/lib/project-connection.js +20 -5
- package/lib/project-debate.js +4 -0
- package/lib/project-file-watch.js +77 -16
- package/lib/project-mate-interaction.js +21 -1
- package/lib/project-memory.js +3 -0
- package/lib/project-shell-command.js +160 -0
- package/lib/project-user-message.js +10 -0
- package/lib/project.js +19 -4
- package/lib/public/antigravity-avatar.png +0 -0
- package/lib/public/app.js +28 -0
- package/lib/public/copilot-avatar.svg +5 -0
- package/lib/public/css/command-palette.css +22 -2
- package/lib/public/css/icon-strip.css +29 -13
- package/lib/public/css/input.css +79 -0
- package/lib/public/css/mates.css +7 -0
- package/lib/public/css/menus.css +48 -14
- package/lib/public/css/messages.css +9 -0
- package/lib/public/css/pane.css +4 -0
- package/lib/public/css/pwa-mobile.css +17 -0
- package/lib/public/css/title-bar.css +19 -0
- package/lib/public/grok-avatar.svg +4 -0
- package/lib/public/index.html +43 -4
- package/lib/public/junie-avatar.svg +11 -0
- package/lib/public/kimi-avatar.svg +4 -0
- package/lib/public/modules/app-header.js +4 -0
- package/lib/public/modules/app-messages.js +28 -5
- package/lib/public/modules/app-panels.js +37 -5
- package/lib/public/modules/app-projects.js +3 -1
- package/lib/public/modules/app-rendering.js +22 -1
- package/lib/public/modules/input.js +58 -28
- package/lib/public/modules/mate-sidebar.js +17 -3
- package/lib/public/modules/notifications.js +7 -1
- package/lib/public/modules/pane-bridge.js +11 -0
- package/lib/public/modules/pane-links.js +23 -0
- package/lib/public/modules/project-switcher.js +7 -6
- package/lib/public/modules/shell-command.js +148 -0
- package/lib/public/modules/sidebar-mates.js +7 -1
- package/lib/public/modules/sidebar-mobile.js +2 -1
- package/lib/public/modules/sidebar-projects.js +34 -43
- package/lib/public/modules/sidebar-sessions.js +6 -6
- package/lib/public/modules/split-pair-ui.js +3 -2
- package/lib/public/modules/tools.js +12 -1
- package/lib/public/modules/vendor-priority.js +14 -0
- package/lib/public/modules/vendor-selection.js +20 -0
- package/lib/public/modules/worktree-location.js +17 -0
- package/lib/public/opencode-avatar.svg +4 -0
- package/lib/public/qwen-avatar.svg +4 -0
- package/lib/public/style.css +3 -2
- package/lib/sdk-bridge.js +59 -19
- package/lib/sdk-message-processor.js +10 -1
- package/lib/session-notes-mcp-server.js +7 -1
- package/lib/worktree.js +9 -4
- package/lib/ws-schema.js +3 -0
- package/lib/yoke/acp-agent-profiles.js +238 -0
- package/lib/yoke/acp-driver-runtime.js +50 -0
- package/lib/yoke/acp-event-normalizer.js +179 -0
- package/lib/yoke/acp-process-manager.js +264 -0
- package/lib/yoke/acp-query-handle.js +487 -0
- package/lib/yoke/adapters/acp.js +317 -0
- package/lib/yoke/adapters/antigravity.js +417 -0
- package/lib/yoke/adapters/codex.js +98 -3
- package/lib/yoke/adapters/copilot.js +7 -0
- package/lib/yoke/adapters/grok.js +7 -0
- package/lib/yoke/adapters/junie.js +7 -0
- package/lib/yoke/adapters/kimi.js +7 -0
- package/lib/yoke/adapters/kiro.js +4 -4
- package/lib/yoke/adapters/opencode.js +7 -0
- package/lib/yoke/adapters/qwen.js +7 -0
- package/lib/yoke/codex-app-server.js +25 -8
- package/lib/yoke/index.js +81 -13
- package/lib/yoke/instructions.js +3 -0
- package/lib/yoke/interface.js +2 -0
- package/lib/yoke/kiro-acp-server.js +30 -276
- package/lib/yoke/vendor-registry.js +77 -0
- package/package.json +1 -1
|
@@ -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
|
+
};
|
|
@@ -7,6 +7,7 @@ var path = require("path");
|
|
|
7
7
|
var fs = require("fs");
|
|
8
8
|
var { CodexAppServer } = require("../codex-app-server");
|
|
9
9
|
var skillDiscovery = require("../skill-discovery");
|
|
10
|
+
var { resolveOsUserInfo } = require("../../os-users");
|
|
10
11
|
|
|
11
12
|
// --- Event flattening ---
|
|
12
13
|
// Converts app-server JSON-RPC notifications into flat objects with a yokeType field.
|
|
@@ -1220,6 +1221,13 @@ function createCodexAdapter(opts) {
|
|
|
1220
1221
|
var _cwd = (opts && opts.cwd) || process.cwd();
|
|
1221
1222
|
var _slug = (opts && opts.slug) || "";
|
|
1222
1223
|
var _defaultInitOpts = Object.assign({}, opts || {});
|
|
1224
|
+
var _runtimeLinuxUser = (opts && opts.runtimeLinuxUser) || null;
|
|
1225
|
+
var _requiresLinuxUser = !!(opts && opts.osUsers) && !_runtimeLinuxUser;
|
|
1226
|
+
var _userRuntimes = Object.create(null);
|
|
1227
|
+
var _resolveOsUserInfo = (opts && opts.resolveOsUserInfo) || resolveOsUserInfo;
|
|
1228
|
+
var _createAppServer = (opts && opts.createAppServer) || function(serverOpts) {
|
|
1229
|
+
return new CodexAppServer(null, serverOpts);
|
|
1230
|
+
};
|
|
1223
1231
|
// Codex models are a fixed list (the app-server doesn't enumerate them), so
|
|
1224
1232
|
// model listing must not depend on a successful app-server init — otherwise a
|
|
1225
1233
|
// slow/failed `initialize` leaves the picker empty and the chip shows the
|
|
@@ -1244,6 +1252,45 @@ function createCodexAdapter(opts) {
|
|
|
1244
1252
|
var _shuttingDown = false;
|
|
1245
1253
|
var _activeQueries = [];
|
|
1246
1254
|
|
|
1255
|
+
function withoutLinuxUser(callOpts) {
|
|
1256
|
+
var cleaned = Object.assign({}, callOpts || {});
|
|
1257
|
+
delete cleaned.linuxUser;
|
|
1258
|
+
return cleaned;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
function getUserRuntime(linuxUser) {
|
|
1262
|
+
if (!linuxUser) return null;
|
|
1263
|
+
if (_runtimeLinuxUser) {
|
|
1264
|
+
if (_runtimeLinuxUser !== linuxUser) {
|
|
1265
|
+
throw new Error("Codex runtime user mismatch: expected " + _runtimeLinuxUser + ", received " + linuxUser);
|
|
1266
|
+
}
|
|
1267
|
+
return adapter;
|
|
1268
|
+
}
|
|
1269
|
+
if (!_userRuntimes[linuxUser]) {
|
|
1270
|
+
var runtimeOpts = Object.assign({}, _defaultInitOpts, {
|
|
1271
|
+
runtimeLinuxUser: linuxUser,
|
|
1272
|
+
});
|
|
1273
|
+
delete runtimeOpts.linuxUser;
|
|
1274
|
+
_userRuntimes[linuxUser] = createCodexAdapter(runtimeOpts);
|
|
1275
|
+
}
|
|
1276
|
+
return _userRuntimes[linuxUser];
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
function shutdownUserRuntimes(method, idleMs) {
|
|
1280
|
+
var users = Object.keys(_userRuntimes);
|
|
1281
|
+
if (!users.length) return Promise.resolve([]);
|
|
1282
|
+
return Promise.all(users.map(function(linuxUser) {
|
|
1283
|
+
var runtime = _userRuntimes[linuxUser];
|
|
1284
|
+
var result = method === "shutdownIfIdle"
|
|
1285
|
+
? runtime.shutdownIfIdle(idleMs)
|
|
1286
|
+
: runtime.shutdown();
|
|
1287
|
+
return Promise.resolve(result).then(function(stopped) {
|
|
1288
|
+
if (method === "shutdown" || stopped) delete _userRuntimes[linuxUser];
|
|
1289
|
+
return stopped;
|
|
1290
|
+
});
|
|
1291
|
+
}));
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1247
1294
|
function updateLastActiveAt() {
|
|
1248
1295
|
_lastActiveAt = Date.now();
|
|
1249
1296
|
}
|
|
@@ -1396,6 +1443,16 @@ function createCodexAdapter(opts) {
|
|
|
1396
1443
|
vendor: "codex",
|
|
1397
1444
|
|
|
1398
1445
|
init: function(initOpts) {
|
|
1446
|
+
var requestedLinuxUser = initOpts && initOpts.linuxUser;
|
|
1447
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1448
|
+
return getUserRuntime(requestedLinuxUser).init(withoutLinuxUser(initOpts));
|
|
1449
|
+
}
|
|
1450
|
+
if (_requiresLinuxUser) {
|
|
1451
|
+
return Promise.reject(new Error("Codex requires a mapped Linux user while OS-user isolation is enabled"));
|
|
1452
|
+
}
|
|
1453
|
+
if (_runtimeLinuxUser && requestedLinuxUser && requestedLinuxUser !== _runtimeLinuxUser) {
|
|
1454
|
+
return Promise.reject(new Error("Codex runtime user mismatch"));
|
|
1455
|
+
}
|
|
1399
1456
|
if (_shuttingDown) {
|
|
1400
1457
|
return Promise.reject(createShutdownError());
|
|
1401
1458
|
}
|
|
@@ -1412,6 +1469,9 @@ function createCodexAdapter(opts) {
|
|
|
1412
1469
|
|
|
1413
1470
|
_initPromise = (async function() {
|
|
1414
1471
|
var serverOpts = { cwd: _cwd };
|
|
1472
|
+
if (_runtimeLinuxUser) {
|
|
1473
|
+
serverOpts.osUserInfo = _resolveOsUserInfo(_runtimeLinuxUser);
|
|
1474
|
+
}
|
|
1415
1475
|
|
|
1416
1476
|
// Extract adapter options
|
|
1417
1477
|
if (effectiveInitOpts && effectiveInitOpts.adapterOptions && effectiveInitOpts.adapterOptions.CODEX) {
|
|
@@ -1478,7 +1538,7 @@ function createCodexAdapter(opts) {
|
|
|
1478
1538
|
}
|
|
1479
1539
|
|
|
1480
1540
|
// Spawn and initialize app-server
|
|
1481
|
-
_appServer =
|
|
1541
|
+
_appServer = _createAppServer(serverOpts);
|
|
1482
1542
|
await _appServer.start();
|
|
1483
1543
|
|
|
1484
1544
|
await _appServer.send("initialize", {
|
|
@@ -1565,6 +1625,16 @@ function createCodexAdapter(opts) {
|
|
|
1565
1625
|
},
|
|
1566
1626
|
|
|
1567
1627
|
createQuery: async function(queryOpts) {
|
|
1628
|
+
var requestedLinuxUser = queryOpts && queryOpts.linuxUser;
|
|
1629
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1630
|
+
return getUserRuntime(requestedLinuxUser).createQuery(withoutLinuxUser(queryOpts));
|
|
1631
|
+
}
|
|
1632
|
+
if (_requiresLinuxUser) {
|
|
1633
|
+
throw new Error("Codex requires a mapped Linux user while OS-user isolation is enabled");
|
|
1634
|
+
}
|
|
1635
|
+
if (_runtimeLinuxUser && requestedLinuxUser && requestedLinuxUser !== _runtimeLinuxUser) {
|
|
1636
|
+
throw new Error("Codex runtime user mismatch");
|
|
1637
|
+
}
|
|
1568
1638
|
if (_shuttingDown) {
|
|
1569
1639
|
throw createShutdownError();
|
|
1570
1640
|
}
|
|
@@ -1661,6 +1731,13 @@ function createCodexAdapter(opts) {
|
|
|
1661
1731
|
|
|
1662
1732
|
// --- Title generation ---
|
|
1663
1733
|
generateTitle: async function(messages, opts) {
|
|
1734
|
+
var requestedLinuxUser = opts && opts.linuxUser;
|
|
1735
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1736
|
+
return getUserRuntime(requestedLinuxUser).generateTitle(messages, withoutLinuxUser(opts));
|
|
1737
|
+
}
|
|
1738
|
+
if (_requiresLinuxUser) {
|
|
1739
|
+
throw new Error("Codex requires a mapped Linux user while OS-user isolation is enabled");
|
|
1740
|
+
}
|
|
1664
1741
|
var systemPrompt = "You are a title generator. Output only a short title (3-8 words). No quotes, no punctuation at the end, no explanation.";
|
|
1665
1742
|
var prompt = "Below is a conversation between a user and an AI assistant. Generate a short, descriptive title (3-8 words) that captures the main topic. Reply with ONLY the title, nothing else.\n\n";
|
|
1666
1743
|
for (var i = 0; i < messages.length; i++) {
|
|
@@ -1708,6 +1785,11 @@ function createCodexAdapter(opts) {
|
|
|
1708
1785
|
listSessions: function() { return Promise.resolve([]); },
|
|
1709
1786
|
renameSession: function() { return Promise.resolve(); },
|
|
1710
1787
|
forkSession: function(threadId, opts) {
|
|
1788
|
+
var requestedLinuxUser = opts && opts.linuxUser;
|
|
1789
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1790
|
+
return getUserRuntime(requestedLinuxUser).forkSession(threadId, withoutLinuxUser(opts));
|
|
1791
|
+
}
|
|
1792
|
+
if (_requiresLinuxUser) return Promise.reject(new Error("Codex requires a mapped Linux user while OS-user isolation is enabled"));
|
|
1711
1793
|
if (!_appServer || !_appServer.started) return Promise.resolve(null);
|
|
1712
1794
|
return _appServer.send("thread/fork", { threadId: threadId }, 30000).then(function(result) {
|
|
1713
1795
|
var newThreadId = (result && result.thread) ? result.thread.id : null;
|
|
@@ -1715,17 +1797,30 @@ function createCodexAdapter(opts) {
|
|
|
1715
1797
|
return { sessionId: newThreadId };
|
|
1716
1798
|
});
|
|
1717
1799
|
},
|
|
1718
|
-
rollbackThread: function(threadId, numTurns) {
|
|
1800
|
+
rollbackThread: function(threadId, numTurns, opts) {
|
|
1801
|
+
var requestedLinuxUser = opts && opts.linuxUser;
|
|
1802
|
+
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
1803
|
+
return getUserRuntime(requestedLinuxUser).rollbackThread(threadId, numTurns, withoutLinuxUser(opts));
|
|
1804
|
+
}
|
|
1805
|
+
if (_requiresLinuxUser) return Promise.reject(new Error("Codex requires a mapped Linux user while OS-user isolation is enabled"));
|
|
1719
1806
|
if (!_appServer || !_appServer.started) return Promise.resolve(null);
|
|
1720
1807
|
return _appServer.send("thread/rollback", { threadId: threadId, numTurns: numTurns }, 30000);
|
|
1721
1808
|
},
|
|
1722
1809
|
|
|
1723
1810
|
// Shutdown the app-server process
|
|
1724
1811
|
shutdown: function() {
|
|
1725
|
-
return
|
|
1812
|
+
return Promise.all([
|
|
1813
|
+
beginShutdown(true),
|
|
1814
|
+
shutdownUserRuntimes("shutdown"),
|
|
1815
|
+
]).then(function() { return true; });
|
|
1726
1816
|
},
|
|
1727
1817
|
|
|
1728
1818
|
shutdownIfIdle: function(idleMs) {
|
|
1819
|
+
if (!_runtimeLinuxUser && Object.keys(_userRuntimes).length > 0) {
|
|
1820
|
+
return shutdownUserRuntimes("shutdownIfIdle", idleMs).then(function(results) {
|
|
1821
|
+
return results.some(function(stopped) { return !!stopped; });
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1729
1824
|
if (_shuttingDown || _shutdownPromise) return Promise.resolve(false);
|
|
1730
1825
|
if (_initPromise) return Promise.resolve(false);
|
|
1731
1826
|
if (!_appServer) return Promise.resolve(false);
|
|
@@ -477,10 +477,10 @@ function createKiroQueryHandle(acp, queryOpts) {
|
|
|
477
477
|
var method = msg.method;
|
|
478
478
|
var params = msg.params || {};
|
|
479
479
|
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
//
|
|
480
|
+
// The shared ACP manager routes by sessionId and guarantees unroutable
|
|
481
|
+
// requests get an error response. Do not add a silent sessionId filter here:
|
|
482
|
+
// dropping a request without calling acp.respond() blocks kiro-cli until the
|
|
483
|
+
// session/prompt timeout.
|
|
484
484
|
|
|
485
485
|
// Tool permission request (server -> client, has an id we must answer)
|
|
486
486
|
if (method === "session/request_permission") {
|