chore: support file search (#1472)

This commit is contained in:
Shubham Naik
2026-03-20 12:14:10 -07:00
committed by GitHub
parent f6b40792a9
commit 238dd6c831
3 changed files with 51 additions and 3 deletions

View File

@@ -383,6 +383,16 @@ export interface TerminalKillCommand {
terminal_id: string;
}
export interface SearchFilesCommand {
type: "search_files";
/** Substring to match against file paths. Empty string returns top files by mtime. */
query: string;
/** Echoed back in the response for request correlation. */
request_id: string;
/** Maximum number of results to return. Defaults to 5. */
max_results?: number;
}
export type WsProtocolCommand =
| InputCommand
| ChangeDeviceStateCommand
@@ -391,7 +401,8 @@ export type WsProtocolCommand =
| TerminalSpawnCommand
| TerminalInputCommand
| TerminalResizeCommand
| TerminalKillCommand;
| TerminalKillCommand
| SearchFilesCommand;
export type WsProtocolMessage =
| DeviceStatusUpdateMessage

View File

@@ -9,6 +9,7 @@ import type { MessageCreate } from "@letta-ai/letta-client/resources/agents/agen
import type { ApprovalCreate } from "@letta-ai/letta-client/resources/agents/messages";
import WebSocket from "ws";
import { getClient } from "../../agent/client";
import { ensureFileIndex, searchFileIndex } from "../../cli/helpers/fileIndex";
import { generatePlanFilePath } from "../../cli/helpers/planName";
import { INTERRUPTED_BY_USER } from "../../constants";
import { type DequeuedBatch, QueueRuntime } from "../../queue/queueRuntime";
@@ -60,7 +61,7 @@ import {
loadPersistedPermissionModeMap,
setConversationPermissionModeState,
} from "./permissionMode";
import { parseServerMessage } from "./protocol-inbound";
import { isSearchFilesCommand, parseServerMessage } from "./protocol-inbound";
import {
buildDeviceStatus,
buildLoopStatus,
@@ -993,6 +994,28 @@ async function connectWithRetry(
return;
}
// ── File search (no runtime scope required) ────────────────────────
if (isSearchFilesCommand(parsed)) {
void (async () => {
await ensureFileIndex();
const files = searchFileIndex({
searchDir: ".",
pattern: parsed.query,
deep: true,
maxResults: parsed.max_results ?? 5,
});
socket.send(
JSON.stringify({
type: "search_files_response",
request_id: parsed.request_id,
files,
success: true,
}),
);
})();
return;
}
// ── Terminal commands (no runtime scope required) ──────────────────
if (parsed.type === "terminal_spawn") {
handleTerminalSpawn(parsed, socket, runtime.bootWorkingDirectory);

View File

@@ -4,6 +4,7 @@ import type {
ChangeDeviceStateCommand,
InputCommand,
RuntimeScope,
SearchFilesCommand,
SyncCommand,
TerminalInputCommand,
TerminalKillCommand,
@@ -240,6 +241,18 @@ function isTerminalKillCommand(value: unknown): value is TerminalKillCommand {
return c.type === "terminal_kill" && typeof c.terminal_id === "string";
}
export function isSearchFilesCommand(
value: unknown,
): value is SearchFilesCommand {
if (!value || typeof value !== "object") return false;
const c = value as { type?: unknown; query?: unknown; request_id?: unknown };
return (
c.type === "search_files" &&
typeof c.query === "string" &&
typeof c.request_id === "string"
);
}
export function parseServerMessage(
data: WebSocket.RawData,
): ParsedServerMessage | null {
@@ -254,7 +267,8 @@ export function parseServerMessage(
isTerminalSpawnCommand(parsed) ||
isTerminalInputCommand(parsed) ||
isTerminalResizeCommand(parsed) ||
isTerminalKillCommand(parsed)
isTerminalKillCommand(parsed) ||
isSearchFilesCommand(parsed)
) {
return parsed as WsProtocolCommand;
}