summaryrefslogtreecommitdiffhomepage
path: root/packages/api/src/app.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-01 01:46:13 +0900
committerAdam Malczewski <[email protected]>2026-06-01 01:46:13 +0900
commit8b9533c22a47bbf6f916667e2c25d8e8e419da37 (patch)
tree715a6a3d6f43781395e7dc7c8cdb519cef46a870 /packages/api/src/app.ts
parent1853dd1d40308deb829bc621beb79c5d39b9c57f (diff)
downloaddispatch-8b9533c22a47bbf6f916667e2c25d8e8e419da37.tar.gz
dispatch-8b9533c22a47bbf6f916667e2c25d8e8e419da37.zip
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
Diffstat (limited to 'packages/api/src/app.ts')
-rw-r--r--packages/api/src/app.ts25
1 files changed, 15 insertions, 10 deletions
diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts
index 73d3de5..19cc193 100644
--- a/packages/api/src/app.ts
+++ b/packages/api/src/app.ts
@@ -56,28 +56,33 @@ app.post("/chat", async (c) => {
return c.json({ error: "message must be a non-empty string" }, 400);
}
- if (agentManager.getTabStatus(tabId) === "running") {
- const queueId = typeof body.queueId === "string" ? body.queueId : undefined;
- const { messageId } = agentManager.queueMessage(tabId, message, queueId);
- return c.json({ status: "queued", messageId });
- }
-
const keyId = typeof body.keyId === "string" ? body.keyId : undefined;
const modelId = typeof body.modelId === "string" ? body.modelId : undefined;
const agentModels = Array.isArray(body.agentModels) ? body.agentModels : undefined;
const workingDirectory =
typeof body.workingDirectory === "string" ? body.workingDirectory : undefined;
+ const queueId = typeof body.queueId === "string" ? body.queueId : undefined;
const validEfforts = ["none", "low", "medium", "high", "max"];
const reasoningEffort =
typeof body.reasoningEffort === "string" && validEfforts.includes(body.reasoningEffort)
? (body.reasoningEffort as "none" | "low" | "medium" | "high" | "max")
: undefined;
- // Non-blocking — let the agent run in the background
- agentManager
- .processMessage(tabId, message, keyId, modelId, reasoningEffort, workingDirectory, agentModels)
- .catch(console.error);
+ // Single routing decision (queue if busy, new turn if idle) shared with the
+ // `send_to_tab` tool via `AgentManager.deliverMessage`. Non-blocking — a
+ // started turn runs in the background.
+ const outcome = agentManager.deliverMessage(tabId, message, {
+ ...(keyId ? { keyId } : {}),
+ ...(modelId ? { modelId } : {}),
+ ...(agentModels ? { agentModels } : {}),
+ ...(reasoningEffort ? { reasoningEffort } : {}),
+ ...(workingDirectory !== undefined ? { workingDirectory } : {}),
+ ...(queueId ? { queueId } : {}),
+ });
+ if (outcome.status === "queued") {
+ return c.json({ status: "queued", messageId: outcome.messageId });
+ }
return c.json({ status: "ok" });
});