Gateway: per-watch notification switches, model list for new threads, and Ask before acting
This commit is contained in:
1 parent
a5222e14e8
commit
3cc7e40852
14 files changed
+472
-20
No files matched your search
@@ -0,0 +1,37 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
|
||||
import type { DeviceRow } from "../src/db.ts";
|
||||
import type { Push } from "../src/notify.ts";
|
||||
import { notificationPrefs, wants } from "../src/push.ts";
|
||||
|
||||
const push = (category: Push["category"], fromSubagent = false): Push => ({
|
||||
title: "Receipt OCR",
|
||||
body: "Finished.",
|
||||
category,
|
||||
fromSubagent,
|
||||
data: { hostId: "host-1", threadId: "thread-1" },
|
||||
collapseId: "host-1:thread-1",
|
||||
});
|
||||
|
||||
describe("notificationPrefs", () => {
|
||||
it("leaves every kind the watch never set on", () => {
|
||||
const device = { notifications: JSON.stringify({ finished: false }) } as DeviceRow;
|
||||
expect(notificationPrefs(device)).toEqual({ needsMe: true, finished: false, failed: true, subagents: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe("wants", () => {
|
||||
const prefs = { needsMe: true, finished: false, failed: true, subagents: false };
|
||||
|
||||
it("mutes a subagent's request apart from your own threads' requests", () => {
|
||||
expect(wants(prefs, push("approval"))).toBe(true);
|
||||
expect(wants(prefs, push("approval", true))).toBe(false);
|
||||
expect(wants(prefs, push("input", true))).toBe(false);
|
||||
});
|
||||
|
||||
it("follows the finished and failed switches, and always sends host problems", () => {
|
||||
expect(wants(prefs, push("finished"))).toBe(false);
|
||||
expect(wants(prefs, push("failed"))).toBe(true);
|
||||
expect(wants({ needsMe: false, finished: false, failed: false, subagents: false }, push("host"))).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
||||
|
||||
import type { BoundedThread, ThreadShell } from "../src/t3/types.ts";
|
||||
import type { T3Host } from "../src/t3/host.ts";
|
||||
import { buildFeed, buildSummary, routeKind, summarize, SummaryCache, trimText } from "../src/views.ts";
|
||||
import { agentProviders, buildFeed, buildSummary, routeKind, summarize, SummaryCache, trimText } from "../src/views.ts";
|
||||
|
||||
const thread = (overrides: Partial<ThreadShell> = {}): ThreadShell => ({
|
||||
id: "thread-1",
|
||||
@@ -197,3 +197,37 @@ describe("buildFeed", () => {
|
||||
expect(feed.recentTotal).toBe(11);
|
||||
});
|
||||
});
|
||||
|
||||
describe("agentProviders", () => {
|
||||
const provider = (overrides: object) => ({
|
||||
instanceId: "claudeAgent",
|
||||
driver: "claudeAgent",
|
||||
displayName: "Claude",
|
||||
enabled: true,
|
||||
installed: true,
|
||||
status: "ready",
|
||||
models: [
|
||||
{ slug: "claude-opus-5-5", name: "Claude Opus 5.5" },
|
||||
{ slug: "claude-opus-4", name: "Claude Opus 4", isLegacy: true },
|
||||
],
|
||||
...overrides,
|
||||
});
|
||||
|
||||
it("offers ready providers without their legacy models, and says which cannot ask first", () => {
|
||||
const host = {
|
||||
serverConfig: {
|
||||
providers: [
|
||||
provider({}),
|
||||
provider({ instanceId: "codex", displayName: undefined, driver: "codex", supportedRuntimeModes: ["full-access"] }),
|
||||
provider({ instanceId: "off", enabled: false }),
|
||||
provider({ instanceId: "broken", status: "error" }),
|
||||
provider({ instanceId: "away", availability: "unavailable" }),
|
||||
],
|
||||
},
|
||||
} as unknown as T3Host;
|
||||
expect(agentProviders(host)).toEqual([
|
||||
{ instanceId: "claudeAgent", name: "Claude", canAskFirst: true, models: [{ slug: "claude-opus-5-5", name: "Claude Opus 5.5" }] },
|
||||
{ instanceId: "codex", name: "codex", canAskFirst: false, models: [{ slug: "claude-opus-5-5", name: "Claude Opus 5.5" }] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user