clay-server 3.3.2-beta.3 → 3.4.0-beta.2

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 (36) hide show
  1. package/lib/project-file-watch.js +77 -16
  2. package/lib/project-shell-command.js +160 -0
  3. package/lib/project-user-message.js +10 -0
  4. package/lib/project.js +13 -0
  5. package/lib/public/app.js +10 -0
  6. package/lib/public/css/input.css +67 -0
  7. package/lib/public/css/mates.css +1 -0
  8. package/lib/public/gemini-avatar.svg +11 -0
  9. package/lib/public/index.html +13 -0
  10. package/lib/public/modules/app-messages.js +16 -1
  11. package/lib/public/modules/app-panels.js +13 -1
  12. package/lib/public/modules/app-projects.js +2 -0
  13. package/lib/public/modules/app-rendering.js +7 -1
  14. package/lib/public/modules/input.js +58 -28
  15. package/lib/public/modules/mate-sidebar.js +9 -3
  16. package/lib/public/modules/shell-command.js +148 -0
  17. package/lib/public/modules/sidebar-mates.js +7 -1
  18. package/lib/public/modules/tools.js +7 -1
  19. package/lib/public/opencode-avatar.svg +4 -0
  20. package/lib/sdk-bridge.js +18 -2
  21. package/lib/sdk-message-processor.js +10 -1
  22. package/lib/ws-schema.js +3 -0
  23. package/lib/yoke/acp-agent-profiles.js +188 -0
  24. package/lib/yoke/acp-driver-runtime.js +50 -0
  25. package/lib/yoke/acp-event-normalizer.js +179 -0
  26. package/lib/yoke/acp-process-manager.js +264 -0
  27. package/lib/yoke/acp-query-handle.js +487 -0
  28. package/lib/yoke/adapters/acp.js +317 -0
  29. package/lib/yoke/adapters/gemini.js +7 -0
  30. package/lib/yoke/adapters/kiro.js +4 -4
  31. package/lib/yoke/adapters/opencode.js +7 -0
  32. package/lib/yoke/index.js +45 -11
  33. package/lib/yoke/interface.js +2 -0
  34. package/lib/yoke/kiro-acp-server.js +30 -276
  35. package/lib/yoke/vendor-registry.js +22 -0
  36. package/package.json +1 -1
@@ -0,0 +1,179 @@
1
+ // ACP Event Normalizer
2
+ // --------------------
3
+ // Converts standard session/update payloads to stable YOKE events.
4
+
5
+ function toolNameForKind(kind, title) {
6
+ switch (kind) {
7
+ case "execute": return "Bash";
8
+ case "read": return "Read";
9
+ case "edit": return "Edit";
10
+ case "delete": return "Edit";
11
+ case "move": return "Edit";
12
+ case "search": return "Grep";
13
+ case "fetch": return "WebFetch";
14
+ case "think": return "Think";
15
+ default: return title || "Tool";
16
+ }
17
+ }
18
+
19
+ function createEventState(opts) {
20
+ opts = opts || {};
21
+ return {
22
+ vendor: opts.vendor || "acp",
23
+ blockCounter: 0,
24
+ textBlockOpen: false,
25
+ textBlockId: null,
26
+ thinkBlockOpen: false,
27
+ thinkBlockId: null,
28
+ toolBlocks: {},
29
+ toolMeta: {},
30
+ toolContent: {},
31
+ lastInputTokens: null,
32
+ contextWindow: opts.contextWindow || null,
33
+ configOptions: [],
34
+ };
35
+ }
36
+
37
+ function extractContent(content) {
38
+ if (!Array.isArray(content)) return "";
39
+ var parts = [];
40
+ for (var i = 0; i < content.length; i++) {
41
+ var item = content[i];
42
+ if (!item) continue;
43
+ if (item.type === "content" && item.content && typeof item.content.text === "string") {
44
+ parts.push(item.content.text);
45
+ } else if (item.type === "diff") {
46
+ parts.push((item.path ? "--- " + item.path + "\n" : "") + (item.newText || ""));
47
+ } else if (typeof item.text === "string") {
48
+ parts.push(item.text);
49
+ }
50
+ }
51
+ return parts.join("\n");
52
+ }
53
+
54
+ function finalToolContent(state, callId, update) {
55
+ if (callId && state.toolContent[callId]) return state.toolContent[callId];
56
+ var direct = extractContent(update.content);
57
+ if (direct) return direct;
58
+ if (typeof update.rawOutput === "string") return update.rawOutput;
59
+ if (update.rawOutput && typeof update.rawOutput.output === "string") return update.rawOutput.output;
60
+ return "";
61
+ }
62
+
63
+ function normalizeStatus(status) {
64
+ if (status === "in_progress" || status === "inProgress") return "in_progress";
65
+ if (status === "completed") return "completed";
66
+ return "pending";
67
+ }
68
+
69
+ function normalizeAcpUpdate(update, state) {
70
+ var events = [];
71
+ if (!update) return events;
72
+ var type = update.sessionUpdate;
73
+
74
+ if (type === "agent_message_chunk") {
75
+ var text = update.content && typeof update.content.text === "string" ? update.content.text : "";
76
+ if (!state.textBlockOpen) {
77
+ state.textBlockOpen = true;
78
+ state.textBlockId = "blk_" + (++state.blockCounter);
79
+ events.push({ yokeType: "text_start", blockId: state.textBlockId });
80
+ }
81
+ if (text) events.push({ yokeType: "text_delta", blockId: state.textBlockId, text: text });
82
+ return events;
83
+ }
84
+
85
+ if (type === "agent_thought_chunk") {
86
+ var thought = update.content && typeof update.content.text === "string" ? update.content.text : "";
87
+ if (!state.thinkBlockOpen) {
88
+ state.thinkBlockOpen = true;
89
+ state.thinkBlockId = "blk_" + (++state.blockCounter);
90
+ events.push({ yokeType: "thinking_start", blockId: state.thinkBlockId });
91
+ }
92
+ if (thought) events.push({ yokeType: "thinking_delta", blockId: state.thinkBlockId, text: thought });
93
+ return events;
94
+ }
95
+
96
+ if (type === "tool_call" || type === "tool_call_update") {
97
+ var callId = update.toolCallId;
98
+ var name = toolNameForKind(update.kind, update.title);
99
+ if (callId && update.kind) {
100
+ state.toolMeta[callId] = { kind: update.kind, title: update.title, rawInput: update.rawInput || {} };
101
+ }
102
+ if (callId && !state.toolBlocks[callId]) {
103
+ state.toolBlocks[callId] = "blk_" + (++state.blockCounter);
104
+ events.push({ yokeType: "tool_start", blockId: state.toolBlocks[callId], toolId: callId, toolName: name });
105
+ events.push({ yokeType: "tool_executing", blockId: state.toolBlocks[callId], toolId: callId, toolName: name, input: update.rawInput || {} });
106
+ }
107
+ var chunk = extractContent(update.content);
108
+ if (callId && chunk) state.toolContent[callId] = (state.toolContent[callId] || "") + chunk;
109
+ if (update.status === "completed" || update.status === "failed") {
110
+ events.push({
111
+ yokeType: "tool_result",
112
+ blockId: state.toolBlocks[callId],
113
+ toolId: callId,
114
+ content: finalToolContent(state, callId, update),
115
+ isError: update.status === "failed",
116
+ });
117
+ }
118
+ return events;
119
+ }
120
+
121
+ if (type === "plan") {
122
+ var entries = Array.isArray(update.entries) ? update.entries : [];
123
+ events.push({
124
+ yokeType: "plan_updated",
125
+ title: "Plan",
126
+ explanation: "",
127
+ plan: entries.map(function(entry) {
128
+ return { step: entry.content || "", status: normalizeStatus(entry.status) };
129
+ }),
130
+ });
131
+ return events;
132
+ }
133
+
134
+ if (type === "usage_update") {
135
+ if (typeof update.used === "number") state.lastInputTokens = update.used;
136
+ if (typeof update.size === "number") state.contextWindow = update.size;
137
+ return events;
138
+ }
139
+
140
+ if (type === "config_option_update") {
141
+ state.configOptions = Array.isArray(update.configOptions) ? update.configOptions : [];
142
+ return events;
143
+ }
144
+
145
+ events.push({
146
+ yokeType: "runtime_specific",
147
+ vendor: state.vendor,
148
+ eventType: "session/update:" + type,
149
+ raw: update,
150
+ });
151
+ return events;
152
+ }
153
+
154
+ function closeOpenBlocks(state) {
155
+ var events = [];
156
+ if (state.thinkBlockOpen) {
157
+ events.push({ yokeType: "thinking_stop", blockId: state.thinkBlockId });
158
+ state.thinkBlockOpen = false;
159
+ }
160
+ return events;
161
+ }
162
+
163
+ function resetTurnState(state) {
164
+ state.textBlockOpen = false;
165
+ state.textBlockId = null;
166
+ state.thinkBlockOpen = false;
167
+ state.thinkBlockId = null;
168
+ state.toolBlocks = {};
169
+ state.toolMeta = {};
170
+ state.toolContent = {};
171
+ }
172
+
173
+ module.exports = {
174
+ createEventState: createEventState,
175
+ normalizeAcpUpdate: normalizeAcpUpdate,
176
+ closeOpenBlocks: closeOpenBlocks,
177
+ resetTurnState: resetTurnState,
178
+ toolNameForKind: toolNameForKind,
179
+ };
@@ -0,0 +1,264 @@
1
+ // Shared ACP Process Manager
2
+ // --------------------------
3
+ // Manages an ACP child process over line-delimited JSON-RPC 2.0. The child can
4
+ // answer client requests and initiate its own requests or notifications.
5
+
6
+ var { spawn } = require("child_process");
7
+ var readline = require("readline");
8
+
9
+ function AcpProcessManager(executablePath, opts) {
10
+ this.proc = null;
11
+ this.rl = null;
12
+ this.nextId = 1;
13
+ this.pendingRequests = {};
14
+ this.requestHandlers = {};
15
+ this.handlers = [];
16
+ this.executablePath = executablePath;
17
+ this.opts = opts || {};
18
+ this.started = false;
19
+ this._stderrBuf = "";
20
+ this._logPrefix = this.opts.logPrefix || "acp-process-manager";
21
+ }
22
+
23
+ AcpProcessManager.prototype._log = function() {
24
+ var args = Array.prototype.slice.call(arguments);
25
+ args.unshift("[" + this._logPrefix + "]");
26
+ console.log.apply(console, args);
27
+ };
28
+
29
+ AcpProcessManager.prototype._warn = function() {
30
+ var args = Array.prototype.slice.call(arguments);
31
+ args.unshift("[" + this._logPrefix + "]");
32
+ console.warn.apply(console, args);
33
+ };
34
+
35
+ AcpProcessManager.prototype._error = function() {
36
+ var args = Array.prototype.slice.call(arguments);
37
+ args.unshift("[" + this._logPrefix + "]");
38
+ console.error.apply(console, args);
39
+ };
40
+
41
+ AcpProcessManager.prototype.start = function() {
42
+ var self = this;
43
+
44
+ return new Promise(function(resolve, reject) {
45
+ try {
46
+ var args = self.opts.args ? self.opts.args.slice() : [];
47
+ var env = Object.assign({}, process.env, self.opts.env || {});
48
+
49
+ self._log("Spawning:", self.executablePath, args.join(" "));
50
+ self.proc = spawn(self.executablePath, args, {
51
+ stdio: ["pipe", "pipe", "pipe"],
52
+ env: env,
53
+ cwd: self.opts.cwd || process.cwd(),
54
+ });
55
+
56
+ self.proc.on("error", function(err) {
57
+ self._error("Process error:", err.message);
58
+ if (!self.started) reject(err);
59
+ self._rejectAllPending(err);
60
+ });
61
+
62
+ self.proc.on("exit", function(code, signal) {
63
+ self._log("Process exited: code=" + code + " signal=" + signal);
64
+ self.started = false;
65
+ self._rejectAllPending(new Error("Process exited: code=" + code));
66
+ });
67
+
68
+ self.proc.stderr.on("data", function(chunk) {
69
+ self._stderrBuf += chunk.toString();
70
+ var lines = self._stderrBuf.split("\n");
71
+ while (lines.length > 1) {
72
+ var line = lines.shift();
73
+ if (line.trim() && self.opts.logStderr !== false) self._log("stderr", line);
74
+ if (self.opts.onStderrLine) self.opts.onStderrLine(line, self);
75
+ }
76
+ self._stderrBuf = lines[0] || "";
77
+ });
78
+
79
+ self.rl = readline.createInterface({ input: self.proc.stdout, crlfDelay: Infinity });
80
+ self.rl.on("line", function(line) {
81
+ if (!line.trim()) return;
82
+ try {
83
+ self._handleMessage(JSON.parse(line));
84
+ } catch (e) {
85
+ self._error("Failed to parse line:", line.substring(0, 200));
86
+ }
87
+ });
88
+ self.rl.on("close", function() {
89
+ self._log("stdout closed");
90
+ });
91
+
92
+ self.started = true;
93
+ resolve();
94
+ } catch (e) {
95
+ reject(e);
96
+ }
97
+ });
98
+ };
99
+
100
+ AcpProcessManager.prototype._handleMessage = function(msg) {
101
+ if (msg.id !== undefined && msg.id !== null && (msg.result !== undefined || msg.error !== undefined)) {
102
+ var pending = this.pendingRequests[msg.id];
103
+ if (pending) {
104
+ delete this.pendingRequests[msg.id];
105
+ if (pending.timer) clearTimeout(pending.timer);
106
+ if (msg.error) {
107
+ var err = new Error(msg.error.message || JSON.stringify(msg.error));
108
+ err.rpcError = msg.error;
109
+ pending.reject(err);
110
+ } else {
111
+ pending.resolve(msg.result);
112
+ }
113
+ }
114
+ return;
115
+ }
116
+
117
+ if (!msg.method) return;
118
+
119
+ var isRequest = msg.id !== undefined && msg.id !== null;
120
+ var directHandler = isRequest && this.requestHandlers[msg.method];
121
+ if (directHandler) {
122
+ var self = this;
123
+ Promise.resolve().then(function() {
124
+ return directHandler(msg.params || {}, msg);
125
+ }).then(function(result) {
126
+ self.respond(msg.id, result);
127
+ }).catch(function(err) {
128
+ self._error("Request handler failed for " + msg.method + ":", err && err.message ? err.message : err);
129
+ self.respondError(msg.id, -32002, err && err.message ? err.message : "Request handler failed");
130
+ });
131
+ return;
132
+ }
133
+
134
+ var sessionId = msg.params && msg.params.sessionId;
135
+ if (isRequest && !sessionId) {
136
+ this._warn("No process handler for request " + msg.method + ", rejecting");
137
+ this.respondError(msg.id, -32601, "No process handler for method " + msg.method);
138
+ return;
139
+ }
140
+ var targets;
141
+ if (sessionId) {
142
+ targets = this.handlers.filter(function(handler) { return handler.sessionId === sessionId; });
143
+ } else {
144
+ targets = this.handlers.slice();
145
+ }
146
+
147
+ if (!targets.length) {
148
+ if (isRequest) {
149
+ this._warn("No handler for request " + msg.method + " (session=" + (sessionId || "none") + "), rejecting");
150
+ this.respondError(msg.id, -32001, "No active handler for session " + (sessionId || "none"));
151
+ } else {
152
+ this._log("Unhandled event:", msg.method);
153
+ }
154
+ return;
155
+ }
156
+
157
+ if (isRequest) {
158
+ try {
159
+ targets[0].fn(msg);
160
+ } catch (e) {
161
+ this._error("Handler threw for " + msg.method + ":", e && e.message ? e.message : e);
162
+ this.respondError(msg.id, -32000, "Handler error");
163
+ }
164
+ return;
165
+ }
166
+
167
+ targets.forEach(function(handler) {
168
+ try {
169
+ handler.fn(msg);
170
+ } catch (e) {
171
+ this._error("Handler threw for " + msg.method + ":", e && e.message ? e.message : e);
172
+ }
173
+ }, this);
174
+ };
175
+
176
+ AcpProcessManager.prototype.addHandler = function(fn) {
177
+ var entry = { sessionId: null, fn: fn };
178
+ this.handlers.push(entry);
179
+ return entry;
180
+ };
181
+
182
+ AcpProcessManager.prototype.removeHandler = function(entry) {
183
+ var index = this.handlers.indexOf(entry);
184
+ if (index !== -1) this.handlers.splice(index, 1);
185
+ };
186
+
187
+ AcpProcessManager.prototype.addRequestHandler = function(method, fn) {
188
+ this.requestHandlers[method] = fn;
189
+ };
190
+
191
+ AcpProcessManager.prototype.send = function(method, params, timeoutMs) {
192
+ var self = this;
193
+ var id = this.nextId++;
194
+ timeoutMs = timeoutMs || 30000;
195
+
196
+ return new Promise(function(resolve, reject) {
197
+ if (!self.proc || !self.started) {
198
+ reject(new Error("ACP server not started"));
199
+ return;
200
+ }
201
+ var timer = setTimeout(function() {
202
+ delete self.pendingRequests[id];
203
+ reject(new Error("Request timeout: " + method + " (id=" + id + ")"));
204
+ }, timeoutMs);
205
+ self.pendingRequests[id] = { resolve: resolve, reject: reject, timer: timer };
206
+
207
+ var msg = { jsonrpc: "2.0", id: id, method: method };
208
+ if (params !== undefined) msg.params = params;
209
+ self._write(msg);
210
+ });
211
+ };
212
+
213
+ AcpProcessManager.prototype.notify = function(method, params) {
214
+ if (!this.proc || !this.started) return;
215
+ var msg = { jsonrpc: "2.0", method: method };
216
+ if (params !== undefined) msg.params = params;
217
+ this._write(msg);
218
+ };
219
+
220
+ AcpProcessManager.prototype.respond = function(id, result) {
221
+ if (!this.proc || !this.started) return;
222
+ this._write({ jsonrpc: "2.0", id: id, result: result });
223
+ };
224
+
225
+ AcpProcessManager.prototype.respondError = function(id, code, message) {
226
+ if (!this.proc || !this.started) return;
227
+ this._write({ jsonrpc: "2.0", id: id, error: { code: code || -1, message: message || "Error" } });
228
+ };
229
+
230
+ AcpProcessManager.prototype._write = function(msg) {
231
+ if (!this.proc || !this.proc.stdin || this.proc.stdin.destroyed) return;
232
+ try {
233
+ this.proc.stdin.write(JSON.stringify(msg) + "\n");
234
+ } catch (e) {
235
+ this._error("Write error:", e.message);
236
+ }
237
+ };
238
+
239
+ AcpProcessManager.prototype._rejectAllPending = function(err) {
240
+ var ids = Object.keys(this.pendingRequests);
241
+ for (var i = 0; i < ids.length; i++) {
242
+ var pending = this.pendingRequests[ids[i]];
243
+ if (pending.timer) clearTimeout(pending.timer);
244
+ pending.reject(err);
245
+ }
246
+ this.pendingRequests = {};
247
+ };
248
+
249
+ AcpProcessManager.prototype.stop = function() {
250
+ this.started = false;
251
+ this._rejectAllPending(new Error("Stopped"));
252
+
253
+ if (this.rl) {
254
+ this.rl.close();
255
+ this.rl = null;
256
+ }
257
+ if (this.proc) {
258
+ try { this.proc.stdin.end(); } catch (e) {}
259
+ try { this.proc.kill("SIGTERM"); } catch (e) {}
260
+ this.proc = null;
261
+ }
262
+ };
263
+
264
+ module.exports = { AcpProcessManager: AcpProcessManager };