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; 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 = export type WsProtocolCommand =
| InputCommand | InputCommand
| ChangeDeviceStateCommand | ChangeDeviceStateCommand
@@ -391,7 +401,8 @@ export type WsProtocolCommand =
| TerminalSpawnCommand | TerminalSpawnCommand
| TerminalInputCommand | TerminalInputCommand
| TerminalResizeCommand | TerminalResizeCommand
| TerminalKillCommand; | TerminalKillCommand
| SearchFilesCommand;
export type WsProtocolMessage = export type WsProtocolMessage =
| DeviceStatusUpdateMessage | 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 type { ApprovalCreate } from "@letta-ai/letta-client/resources/agents/messages";
import WebSocket from "ws"; import WebSocket from "ws";
import { getClient } from "../../agent/client"; import { getClient } from "../../agent/client";
import { ensureFileIndex, searchFileIndex } from "../../cli/helpers/fileIndex";
import { generatePlanFilePath } from "../../cli/helpers/planName"; import { generatePlanFilePath } from "../../cli/helpers/planName";
import { INTERRUPTED_BY_USER } from "../../constants"; import { INTERRUPTED_BY_USER } from "../../constants";
import { type DequeuedBatch, QueueRuntime } from "../../queue/queueRuntime"; import { type DequeuedBatch, QueueRuntime } from "../../queue/queueRuntime";
@@ -60,7 +61,7 @@ import {
loadPersistedPermissionModeMap, loadPersistedPermissionModeMap,
setConversationPermissionModeState, setConversationPermissionModeState,
} from "./permissionMode"; } from "./permissionMode";
import { parseServerMessage } from "./protocol-inbound"; import { isSearchFilesCommand, parseServerMessage } from "./protocol-inbound";
import { import {
buildDeviceStatus, buildDeviceStatus,
buildLoopStatus, buildLoopStatus,
@@ -993,6 +994,28 @@ async function connectWithRetry(
return; 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) ────────────────── // ── Terminal commands (no runtime scope required) ──────────────────
if (parsed.type === "terminal_spawn") { if (parsed.type === "terminal_spawn") {
handleTerminalSpawn(parsed, socket, runtime.bootWorkingDirectory); handleTerminalSpawn(parsed, socket, runtime.bootWorkingDirectory);

View File

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