chore: support file search (#1472)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user