From 8b9533c22a47bbf6f916667e2c25d8e8e419da37 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Mon, 1 Jun 2026 01:46:13 +0900 Subject: feat(tabs): tab-to-tab agent communication via short handles Add send_to_tab / read_tab tools so an agent can message or read another tab by a git-style short handle (shortest unique prefix of the tab UUID, min 4 chars), shown in the tab bar. - core/db/tabs: resolveTabPrefix + shortestUniquePrefix (open tabs only, LIKE-sanitized prefix matching) - new tools read-tab.ts / send-to-tab.ts (+ tests) decoupled from the DB TabRow via a minimal ResolvedTabRef projection - agent-manager: unified deliverMessage routing (busy -> queue, idle -> new turn) shared by POST /chat and send_to_tab; agent->agent auto-wake budget (MAX_AGENT_AUTO_WAKES) to bound ping-pong loops - summon/loader: send_to_tab + read_tab as grantable tools - frontend: shortHandleFor + handle badge in TabBar; perm toggles - notes: tab-comm / user-agents / todo-redesign plans - chore: biome format fixes (debug-logger, summon.test) Refs notes/plan-tab-comm.md --- packages/core/src/llm/debug-logger.ts | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'packages/core/src/llm') diff --git a/packages/core/src/llm/debug-logger.ts b/packages/core/src/llm/debug-logger.ts index 2b7420c..072a7a1 100644 --- a/packages/core/src/llm/debug-logger.ts +++ b/packages/core/src/llm/debug-logger.ts @@ -281,11 +281,7 @@ export function logStepLifecycle(data: { * Log agent loop-level events (loop start, break conditions, etc.). * Only logs at verbosity >= 3. */ -export function logAgentLoop(data: { - tabId?: string; - event: string; - detail?: unknown; -}): void { +export function logAgentLoop(data: { tabId?: string; event: string; detail?: unknown }): void { if (!ENABLED || VERBOSITY < 3) return; const detail = data.detail !== undefined ? ` ${JSON.stringify(data.detail)}` : ""; console.error(`[dispatch-debug] AGENT tab=${data.tabId ?? "?"} ${data.event}${detail}`); @@ -308,16 +304,14 @@ export function logAgentLoop(data: { * we just clone and read once via `.text()` — simpler and safe because * non-streaming bodies are bounded. */ -export function wrapFetchWithLogging< - F extends (...args: never[]) => Promise | Response, ->(baseFetch: F, opts: { tabId?: string; modelHint?: string }): F { +export function wrapFetchWithLogging Promise | Response>( + baseFetch: F, + opts: { tabId?: string; modelHint?: string }, +): F { if (!ENABLED) return baseFetch; const wrapped = async (...args: Parameters) => { const requestId = ++seq; - const [input, init] = args as unknown as [ - RequestInfo | URL, - RequestInit | undefined, - ]; + const [input, init] = args as unknown as [RequestInfo | URL, RequestInit | undefined]; const url = typeof input === "string" ? input @@ -325,7 +319,8 @@ export function wrapFetchWithLogging< ? input.toString() : (input as Request).url; const method = - init?.method ?? (typeof input === "object" && "method" in input ? (input as Request).method : "POST"); + init?.method ?? + (typeof input === "object" && "method" in input ? (input as Request).method : "POST"); // Snapshot headers as a plain object for logging. const headerObj: Record = {}; -- cgit v1.2.3