Skip to content

Commit 27c85d6

Browse files
authored
code-agent: (#1440)
Implement code editor actions. - user requests like `Create a TypeScript function called toSnakeCase that takes a string and returns a string` will be handled by the createFunction action - Leverage copilot inline suggestion to create the function body and automatically accept the suggestion. This will able to us to experiment with rapid coding using natural language. - Detect if coda extension is connected. - Errors out instead of hanging if the coda extension if not running - Added code to handle creation of file, and save actions
1 parent c48a722 commit 27c85d6

12 files changed

+1675
-7
lines changed

ts/packages/agents/code/src/codeActionHandler.ts

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { fileURLToPath } from "url";
1616
import os from "os";
1717
import registerDebug from "debug";
1818
import chalk from "chalk";
19+
import { createActionResultFromError } from "@typeagent/agent-sdk/helpers/action";
1920

2021
const debug = registerDebug("typeagent:code");
2122

@@ -82,7 +83,9 @@ async function updateCodeContext(
8283
if (pendingCall) {
8384
agentContext.pendingCall.delete(Number(data.id));
8485
const { resolve, context } = pendingCall;
85-
context.actionIO.setDisplay(data.result);
86+
if (context?.actionIO) {
87+
context.actionIO.setDisplay(data.result);
88+
}
8689
resolve();
8790
}
8891
}
@@ -174,6 +177,88 @@ async function ensureVSCodeProcess(): Promise<void> {
174177
});
175178
}
176179

180+
async function sendPingToCodaExtension(
181+
agentContext: CodeActionContext,
182+
): Promise<boolean> {
183+
const ws = agentContext.webSocket;
184+
if (!ws || ws.readyState !== 1) return false;
185+
186+
const callId = agentContext.nextCallId++;
187+
return new Promise((resolve) => {
188+
const timeout = setTimeout(() => {
189+
agentContext.pendingCall.delete(callId);
190+
resolve(false);
191+
}, 1000);
192+
193+
agentContext.pendingCall.set(callId, {
194+
resolve: () => {
195+
clearTimeout(timeout);
196+
resolve(true);
197+
},
198+
context: undefined as any,
199+
});
200+
201+
ws.send(
202+
JSON.stringify({
203+
id: callId,
204+
method: "code/ping",
205+
params: {},
206+
}),
207+
);
208+
});
209+
}
210+
211+
type ActiveFile = {
212+
filePath: string;
213+
languageId: string;
214+
isUntitled: boolean;
215+
isDirty: boolean;
216+
};
217+
218+
export async function getActiveFileFromVSCode(
219+
agentContext: CodeActionContext,
220+
timeoutMs = 2000,
221+
): Promise<ActiveFile | undefined> {
222+
const ws = agentContext.webSocket;
223+
224+
if (!ws || ws.readyState !== 1 /* OPEN */) {
225+
return undefined;
226+
}
227+
228+
const callId = agentContext.nextCallId++;
229+
230+
return new Promise<ActiveFile | undefined>((resolve) => {
231+
// Hard timeout so we never hang
232+
const t = setTimeout(() => {
233+
agentContext.pendingCall.delete(callId);
234+
resolve(undefined);
235+
}, timeoutMs);
236+
237+
// NOTE: pendingCall entry has no ActionContext because this isn’t a UI action
238+
agentContext.pendingCall.set(callId, {
239+
resolve: (value?: any) => {
240+
clearTimeout(t);
241+
resolve(value as ActiveFile | undefined);
242+
},
243+
context: undefined as any,
244+
});
245+
246+
try {
247+
ws.send(
248+
JSON.stringify({
249+
id: callId,
250+
method: "code/getActiveFile",
251+
params: {},
252+
}),
253+
);
254+
} catch {
255+
clearTimeout(t);
256+
agentContext.pendingCall.delete(callId);
257+
resolve(undefined);
258+
}
259+
});
260+
}
261+
177262
async function executeCodeAction(
178263
action: AppAction,
179264
context: ActionContext<CodeActionContext>,
@@ -185,8 +270,17 @@ async function executeCodeAction(
185270

186271
const agentContext = context.sessionContext.agentContext;
187272
const webSocketEndpoint = agentContext.webSocket;
273+
188274
if (webSocketEndpoint) {
189275
try {
276+
const isExtensionAlive =
277+
await sendPingToCodaExtension(agentContext);
278+
if (!isExtensionAlive) {
279+
return createActionResultFromError(
280+
"❌ Coda VSCode extension is not connected.",
281+
);
282+
}
283+
190284
const callId = agentContext.nextCallId++;
191285
return new Promise<undefined>((resolve) => {
192286
agentContext.pendingCall.set(callId, {

ts/packages/agents/code/src/codeManifest.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
},
3535
"code-editor": {
3636
"schema": {
37-
"description": "Code agent that helps you perform vscode editor actions like go to a reference, reveal a declaration, clipboard copy or paste, add a comment line etc.",
38-
"schemaFile": "./vscode/editorCommandActionsSchema.ts",
39-
"schemaType": "CodeEditorActions"
37+
"description": "Code agent that helps you perform vscode editor actions like create a new file, go to a reference, reveal a declaration, clipboard copy or paste, add a comment line etc.",
38+
"schemaFile": "./vscode/editorCodeActionsSchema.ts",
39+
"schemaType": "EditorCodeActions"
4040
}
4141
},
4242
"code-workbench": {

0 commit comments

Comments
 (0)