feat(rpc): add validated desktop Pi RPC command palette
This commit is contained in:
@@ -20,6 +20,7 @@ const DEFAULT_EVENT_LIMIT = 1_000;
|
||||
const DEFAULT_CLOSE_TIMEOUT_MS = 2_000;
|
||||
const RESTORE_CONCURRENCY = 2;
|
||||
const RESOURCE_WARNING_COUNT = 6;
|
||||
const INITIAL_LAUNCH_GET_STATE_TIMEOUT_MS = 90_000;
|
||||
|
||||
export class UnknownAgentError extends Error {
|
||||
constructor(agentId) {
|
||||
@@ -229,6 +230,8 @@ function routeCommand(adapter, operation, payload = {}, runtimeState) {
|
||||
return adapter.send({ type: "set_thinking_level", level: payload.level });
|
||||
case "set_session_name":
|
||||
return adapter.send({ type: "set_session_name", name: payload.name });
|
||||
case "pi_rpc_command":
|
||||
return adapter.send({ type: payload.command, ...payload.input });
|
||||
case "extension_response":
|
||||
adapter.respondToExtension(payload.requestId, payload.response);
|
||||
return Promise.resolve({
|
||||
@@ -451,11 +454,16 @@ export function createAgentRegistry({
|
||||
let resolved;
|
||||
if (typeof nextPath === "string" && path.isAbsolute(nextPath)) {
|
||||
try {
|
||||
resolved = await ownedSessionPath(runtime.sessionDir, nextPath, {
|
||||
const candidate = await ownedSessionPath(runtime.sessionDir, nextPath, {
|
||||
allowMissing: true,
|
||||
});
|
||||
} catch {
|
||||
throw new Error("Pi reported a session outside its managed directory");
|
||||
await stat(candidate);
|
||||
resolved = candidate;
|
||||
} catch (error) {
|
||||
if (error?.code !== "ENOENT")
|
||||
throw new Error(
|
||||
"Pi reported a session outside its managed directory",
|
||||
);
|
||||
}
|
||||
}
|
||||
if (expectedSessionPath && resolved !== expectedSessionPath)
|
||||
@@ -627,8 +635,19 @@ export function createAgentRegistry({
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: (error, { terminal = true } = {}) => {
|
||||
if (runtime.adapter !== adapter || runtime.stopped) return;
|
||||
if (!terminal) {
|
||||
const diagnostic = {
|
||||
code: error.code ?? "unknown",
|
||||
message: error.message,
|
||||
};
|
||||
publishAgent(runtime, "diagnostic", { error: diagnostic });
|
||||
publishWorkspace("runtime_diagnostic", runtime, {
|
||||
error: diagnostic,
|
||||
});
|
||||
return;
|
||||
}
|
||||
runtime.state = "error";
|
||||
runtime.stateVersion += 1;
|
||||
runtime.error = {
|
||||
@@ -673,7 +692,10 @@ export function createAgentRegistry({
|
||||
});
|
||||
runtime.adapter = adapter;
|
||||
try {
|
||||
const state = await adapter.send({ type: "get_state" });
|
||||
const state = await adapter.send(
|
||||
{ type: "get_state" },
|
||||
{ timeoutMs: INITIAL_LAUNCH_GET_STATE_TIMEOUT_MS },
|
||||
);
|
||||
runtime.state = state?.data?.isStreaming ? "streaming" : "idle";
|
||||
runtime.stateVersion += 1;
|
||||
await updateRuntimeIdentity(runtime, state, {
|
||||
@@ -1390,6 +1412,7 @@ export function createAgentRegistry({
|
||||
},
|
||||
async route(agentId, operation, payload = {}) {
|
||||
const runtime = getAgent(agentId);
|
||||
if (operation === "abort") return runtime.adapter.send({ type: "abort" });
|
||||
return enqueue(runtime, async () => {
|
||||
if (operation === "switch_session") {
|
||||
if (runtime.state === "streaming")
|
||||
@@ -1416,6 +1439,12 @@ export function createAgentRegistry({
|
||||
}
|
||||
const previousState = runtime.state;
|
||||
const previousStateVersion = runtime.stateVersion;
|
||||
const delivery =
|
||||
operation === "submit_prompt"
|
||||
? previousState === "streaming"
|
||||
? "follow_up"
|
||||
: "prompt"
|
||||
: undefined;
|
||||
const startsWork =
|
||||
operation === "prompt" ||
|
||||
(operation === "submit_prompt" && previousState !== "streaming");
|
||||
@@ -1447,7 +1476,7 @@ export function createAgentRegistry({
|
||||
runtime.attention =
|
||||
runtime.errorAttention || runtime.extensions.size > 0;
|
||||
}
|
||||
return response;
|
||||
return delivery ? { ...response, delivery } : response;
|
||||
} catch (error) {
|
||||
if (
|
||||
startsWork &&
|
||||
|
||||
Reference in New Issue
Block a user