From c1a46301b639ed87f2e9d6bd74f2bb360e96c2a2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 2 Mar 2026 09:56:07 +0000 Subject: [PATCH] fix(ci): align strict nullable typing across channels and ui --- extensions/imessage/src/channel.ts | 17 +++++++++-------- extensions/signal/src/channel.ts | 4 ++-- extensions/slack/src/channel.ts | 8 ++++---- .../src/channel.integration.test.ts | 1 + .../controllers/config/form-utils.node.test.ts | 5 +++-- ui/src/ui/views/nodes.ts | 4 ++-- ui/src/ui/views/usage-render-details.test.ts | 4 ++++ 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/extensions/imessage/src/channel.ts b/extensions/imessage/src/channel.ts index 0c573f46b75..36963ca981f 100644 --- a/extensions/imessage/src/channel.ts +++ b/extensions/imessage/src/channel.ts @@ -182,13 +182,14 @@ export const imessagePlugin: ChannelPlugin = { accountId, name: input.name, }); - const next = + const next = ( accountId !== DEFAULT_ACCOUNT_ID ? migrateBaseNameToDefaultAccount({ cfg: namedConfig, channelKey: "imessage", }) - : namedConfig; + : namedConfig + ) as typeof cfg; if (accountId === DEFAULT_ACCOUNT_ID) { return { ...next, @@ -200,7 +201,7 @@ export const imessagePlugin: ChannelPlugin = { ...buildIMessageSetupPatch(input), }, }, - }; + } as typeof cfg; } return { ...next, @@ -219,7 +220,7 @@ export const imessagePlugin: ChannelPlugin = { }, }, }, - }; + } as typeof cfg; }, }, outbound: { @@ -232,9 +233,9 @@ export const imessagePlugin: ChannelPlugin = { cfg, to, text, - accountId, + accountId: accountId ?? undefined, deps, - replyToId, + replyToId: replyToId ?? undefined, }); return { channel: "imessage", ...result }; }, @@ -244,9 +245,9 @@ export const imessagePlugin: ChannelPlugin = { to, text, mediaUrl, - accountId, + accountId: accountId ?? undefined, deps, - replyToId, + replyToId: replyToId ?? undefined, }); return { channel: "imessage", ...result }; }, diff --git a/extensions/signal/src/channel.ts b/extensions/signal/src/channel.ts index 8c325a71d7f..9a7a9aee13b 100644 --- a/extensions/signal/src/channel.ts +++ b/extensions/signal/src/channel.ts @@ -265,7 +265,7 @@ export const signalPlugin: ChannelPlugin = { cfg, to, text, - accountId, + accountId: accountId ?? undefined, deps, }); return { channel: "signal", ...result }; @@ -276,7 +276,7 @@ export const signalPlugin: ChannelPlugin = { to, text, mediaUrl, - accountId, + accountId: accountId ?? undefined, deps, }); return { channel: "signal", ...result }; diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index b02a36e3b07..6af8b382170 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -69,8 +69,8 @@ function resolveSlackSendContext(params: { cfg: Parameters[0]["cfg"]; accountId?: string; deps?: { sendSlack?: SlackSendFn }; - replyToId?: string | null; - threadId?: string | null; + replyToId?: string | number | null; + threadId?: string | number | null; }) { const send = params.deps?.sendSlack ?? getSlackRuntime().channel.slack.sendMessageSlack; const account = resolveSlackAccount({ cfg: params.cfg, accountId: params.accountId }); @@ -359,7 +359,7 @@ export const slackPlugin: ChannelPlugin = { sendText: async ({ to, text, accountId, deps, replyToId, threadId, cfg }) => { const { send, threadTsValue, tokenOverride } = resolveSlackSendContext({ cfg, - accountId, + accountId: accountId ?? undefined, deps, replyToId, threadId, @@ -374,7 +374,7 @@ export const slackPlugin: ChannelPlugin = { sendMedia: async ({ to, text, mediaUrl, accountId, deps, replyToId, threadId, cfg }) => { const { send, threadTsValue, tokenOverride } = resolveSlackSendContext({ cfg, - accountId, + accountId: accountId ?? undefined, deps, replyToId, threadId, diff --git a/extensions/synology-chat/src/channel.integration.test.ts b/extensions/synology-chat/src/channel.integration.test.ts index 555bf3da65b..a28c3e8365b 100644 --- a/extensions/synology-chat/src/channel.integration.test.ts +++ b/extensions/synology-chat/src/channel.integration.test.ts @@ -1,3 +1,4 @@ +import type { IncomingMessage, ServerResponse } from "node:http"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { makeFormBody, makeReq, makeRes } from "./test-http-utils.js"; diff --git a/ui/src/ui/controllers/config/form-utils.node.test.ts b/ui/src/ui/controllers/config/form-utils.node.test.ts index 9457d755cf7..a806be042f2 100644 --- a/ui/src/ui/controllers/config/form-utils.node.test.ts +++ b/ui/src/ui/controllers/config/form-utils.node.test.ts @@ -110,10 +110,11 @@ describe("form-utils preserves numeric types", () => { const raw = serializeConfigForm(form); const parsed = JSON.parse(raw); const model = parsed.models.providers.xai.models[0] as Record; + const cost = model.cost as Record; expectNumericModelCore(model); - expect(typeof model.cost.input).toBe("number"); - expect(model.cost.input).toBe(0.5); + expect(typeof cost.input).toBe("number"); + expect(cost.input).toBe(0.5); }); it("cloneConfigObject + setPathValue preserves unrelated numeric fields", () => { diff --git a/ui/src/ui/views/nodes.ts b/ui/src/ui/views/nodes.ts index c9fc77545a6..8a8413b6d58 100644 --- a/ui/src/ui/views/nodes.ts +++ b/ui/src/ui/views/nodes.ts @@ -218,10 +218,10 @@ function renderTokenRow(deviceId: string, token: DeviceTokenSummary, props: Node type BindingAgent = { id: string; - name?: string; + name: string | undefined; index: number; isDefault: boolean; - binding?: string | null; + binding: string | null; }; type BindingNode = NodeTargetOption; diff --git a/ui/src/ui/views/usage-render-details.test.ts b/ui/src/ui/views/usage-render-details.test.ts index 1218b528bd7..9505f1c1107 100644 --- a/ui/src/ui/views/usage-render-details.test.ts +++ b/ui/src/ui/views/usage-render-details.test.ts @@ -28,6 +28,10 @@ const baseUsage = { output: 400, cacheRead: 200, cacheWrite: 100, + inputCost: 0.3, + outputCost: 0.4, + cacheReadCost: 0.2, + cacheWriteCost: 0.1, durationMs: 60000, firstActivity: 0, lastActivity: 60000,