From ec099021b2a60e26c89d4d51a9f76c321452163a Mon Sep 17 00:00:00 2001 From: oratis Date: Sun, 14 Jun 2026 21:24:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(dispatch):=20D4b=20=E2=80=94=20/api/agent/?= =?UTF-8?q?signal=20endpoint=20(list/cancel=20LISA's=20dispatched=20agents?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the HTTP primitive for D4b: list or cancel the agents LISA herself dispatched, over the web API. Behind the existing auth gate like every endpoint (loopback or LISA_WEB_TOKEN). Reuses signalAgentTool, so it can ONLY target dispatch-ledger pids (LISA's own dispatched agents) — never an arbitrary process; the SIGTERM→SIGKILL escalation + ledger cleanup are inherited. POST /api/agent/signal { action: "list" | "cancel", target?, force? } Scope note (per D4 plan line 66, which marks the island button "可选/optional, default still prefill"): the island affordance is intentionally NOT added here — the advisor card deliberately prefills every action (never auto-runs) as a safety default, and LISA can already cancel via the signal_agent tool in chat. This endpoint is the enabling primitive; a direct-cancel button (sourced from the ledger list, the only safe target set) is the optional remaining UI bit. 690 tests pass; typecheck + build clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/web/server.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/web/server.ts b/src/web/server.ts index 66ded1d..9316219 100644 --- a/src/web/server.ts +++ b/src/web/server.ts @@ -33,6 +33,7 @@ import { captureScreenshot, captureSupported, type CaptureMode } from "../vision import { transcribeAudio } from "../voice/transcribe.js"; import { polishDictation, type DictationProvider } from "../voice/dictation.js"; import { listGrants, grant, revoke, revokeAll, isGranted, SENSE_SIGNALS, SIGNAL_DESCRIPTIONS } from "../consent/store.js"; +import { signalAgentTool } from "../tools/signal_agent.js"; import { SenseService } from "../sense/service.js"; import { ScreenSource } from "../sense/screen.js"; import { VoiceSource } from "../sense/voice.js"; @@ -733,6 +734,39 @@ export async function startWebServer(opts: WebServerOptions): Promise {} }, + ); + res.writeHead(200, { "content-type": "application/json" }); + res.end(JSON.stringify({ ok: true, result })); + return; + } + // Recent ambient sense events, for the island's "recently sensed" list. if (req.method === "GET" && url === "/api/sense/recent") { const events = readSenseEvents().slice(-30).reverse(); // newest first