summaryrefslogtreecommitdiffhomepage
path: root/packages/core/tests
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-02 15:53:15 +0900
committerAdam Malczewski <[email protected]>2026-06-02 15:53:15 +0900
commitaa295e82197ebc77d9466eee28380bc5bcc0863d (patch)
tree2a900e1f10564f4f4b3f698d1058ebc37d5c8d45 /packages/core/tests
parente475e527cd768dc05368a0881a07a84ea140e13e (diff)
downloaddispatch-aa295e82197ebc77d9466eee28380bc5bcc0863d.tar.gz
dispatch-aa295e82197ebc77d9466eee28380bc5bcc0863d.zip
fix(tabs): only mention read_tab when the sender actually has it; CAPS on ONLY
The send_to_tab guidance previously told the agent it could call read_tab to check for a reply, but the tab-messaging permissions are split — a tab can hold send_to_tab WITHOUT read_tab (the exact case in testing). Advertising a tool the agent wasn't granted is wrong. Thread a canReadTab flag from AgentManager.buildTabCommToolEntries into createSendToTabTool (true iff this tab is also granted read_tab). The tool description and the delivery-result text now only reference read_tab when canReadTab is true; otherwise they say a reply arrives on its own and to end the turn. Drop the read_tab phrasing from the static TOOL_DESCRIPTIONS one-liner (can't be conditional per-tab there). Also uppercase ONLY in the recipient reply-contract footer for emphasis. Tests: cover both canReadTab branches for description + result text; assert ONLY is uppercased.
Diffstat (limited to 'packages/core/tests')
-rw-r--r--packages/core/tests/tools/send-to-tab.test.ts33
1 files changed, 32 insertions, 1 deletions
diff --git a/packages/core/tests/tools/send-to-tab.test.ts b/packages/core/tests/tools/send-to-tab.test.ts
index 68f8fa0..48ff460 100644
--- a/packages/core/tests/tools/send-to-tab.test.ts
+++ b/packages/core/tests/tools/send-to-tab.test.ts
@@ -14,6 +14,7 @@ function makeCallbacks(overrides: Partial<SendToTabCallbacks> = {}): SendToTabCa
deliver: () => ({ status: "started" }),
listOpenHandles: () => [{ handle: "targ", title: "Target" }],
self: { id: "self-id", handle: "self" },
+ canReadTab: true,
...overrides,
};
}
@@ -28,6 +29,19 @@ describe("createSendToTabTool — schema & description", () => {
expect(tool.description.toLowerCase()).toContain("do not sleep");
expect(tool.description.toLowerCase()).toContain("end your turn");
});
+
+ it("mentions read_tab in the description only when canReadTab is true", () => {
+ const tool = createSendToTabTool(makeCallbacks({ canReadTab: true }));
+ expect(tool.description).toContain("read_tab");
+ });
+
+ it("never mentions read_tab in the description when canReadTab is false", () => {
+ const tool = createSendToTabTool(makeCallbacks({ canReadTab: false }));
+ expect(tool.description).not.toContain("read_tab");
+ // Still tells the agent a reply arrives on its own + to end its turn.
+ expect(tool.description.toLowerCase()).toContain("arrives on its own");
+ expect(tool.description.toLowerCase()).toContain("end your turn");
+ });
});
describe("createSendToTabTool — execute()", () => {
@@ -46,7 +60,7 @@ describe("createSendToTabTool — execute()", () => {
// Reply contract: the recipient must answer via send_to_tab back to the
// sender's handle, not as a plain text reply to its own user.
expect(delivered).toContain('send_to_tab tool with tab_id "self"');
- expect(delivered.toLowerCase()).toContain("only reply if");
+ expect(delivered).toContain("ONLY reply if");
expect(out).toContain("idle");
expect(out).toContain("targ");
// Sender is steered away from busy-waiting and told to end its turn.
@@ -54,6 +68,23 @@ describe("createSendToTabTool — execute()", () => {
expect(out.toLowerCase()).toContain("end your turn");
});
+ it("points the sender at read_tab in the result only when canReadTab is true", async () => {
+ const deliver = vi.fn(() => ({ status: "started" as const }));
+ const tool = createSendToTabTool(makeCallbacks({ deliver, canReadTab: true }));
+ const out = await tool.execute({ tab_id: "targ", message: "hi" });
+ expect(out).toContain("read_tab");
+ });
+
+ it("omits read_tab from the result when canReadTab is false", async () => {
+ const deliver = vi.fn(() => ({ status: "started" as const }));
+ const tool = createSendToTabTool(makeCallbacks({ deliver, canReadTab: false }));
+ const out = await tool.execute({ tab_id: "targ", message: "hi" });
+ expect(out).not.toContain("read_tab");
+ // Still steers away from busy-waiting and toward ending the turn.
+ expect(out.toLowerCase()).toContain("do not sleep");
+ expect(out.toLowerCase()).toContain("end your turn");
+ });
+
it("reports the queued status when the target is busy", async () => {
const deliver = vi.fn(() => ({ status: "queued" as const }));
const tool = createSendToTabTool(makeCallbacks({ deliver }));