Remove unnecessary type assertions across codebase

Add oxlint-tsgolint and configure typescript/no-unnecessary-type-assertion
to flag redundant `!` and `as Foo` casts. Type-aware mode is left off by
default to keep `npm run lint` fast; the rule sits configured for when we
turn type-aware on intentionally. Auto-fix removed ~283 redundant casts;
two manual touch-ups: a real tsgolint false positive in split-container.tsx
and a stale ChildProcess import after a double-cast collapsed.
This commit is contained in:
Mohamed Boudra
2026-05-04 10:21:52 +07:00
parent 78fe3e4df3
commit 4cd9e76bd2
129 changed files with 409 additions and 323 deletions

View File

@@ -1,5 +1,8 @@
{ {
"$schema": "./node_modules/oxlint/configuration_schema.json", "$schema": "./node_modules/oxlint/configuration_schema.json",
"options": {
"typeAware": false
},
"plugins": ["react", "react-perf", "unicorn", "typescript", "oxc", "import", "promise"], "plugins": ["react", "react-perf", "unicorn", "typescript", "oxc", "import", "promise"],
"categories": { "categories": {
"correctness": "error", "correctness": "error",
@@ -53,6 +56,7 @@
"typescript/no-explicit-any": "error", "typescript/no-explicit-any": "error",
"typescript/prefer-as-const": "error", "typescript/prefer-as-const": "error",
"typescript/no-this-alias": "error", "typescript/no-this-alias": "error",
"typescript/no-unnecessary-type-assertion": "error",
"typescript/consistent-type-definitions": ["error", "interface"], "typescript/consistent-type-definitions": ["error", "interface"],
"import/no-unassigned-import": [ "import/no-unassigned-import": [

103
package-lock.json generated
View File

@@ -30,6 +30,7 @@
"lefthook": "^2.1.6", "lefthook": "^2.1.6",
"oxfmt": "0.46.0", "oxfmt": "0.46.0",
"oxlint": "1.61.0", "oxlint": "1.61.0",
"oxlint-tsgolint": "^0.22.1",
"patch-package": "^8.0.1", "patch-package": "^8.0.1",
"playwright": "^1.56.1", "playwright": "^1.56.1",
"typescript": "^5.9.3", "typescript": "^5.9.3",
@@ -10376,6 +10377,90 @@
"node": "^20.19.0 || >=22.12.0" "node": "^20.19.0 || >=22.12.0"
} }
}, },
"node_modules/@oxlint-tsgolint/darwin-arm64": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-0.22.1.tgz",
"integrity": "sha512-4150Lpgc1YM09GcjA6GSrra1JoPjC7aOpfywLjWEY4vW0Sd1qKzqHF1WRaiw0/qUZ40OATYdv3aRd7ipPkWQbw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@oxlint-tsgolint/darwin-x64": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-x64/-/darwin-x64-0.22.1.tgz",
"integrity": "sha512-vFWcPWYOgZs4HWcgS1EjUZg33NLcNfEYU49KGImmCfZWkflENrmBYV4HN/C0YeAPum6ZZ/goPSvQrB/cOD+NfA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@oxlint-tsgolint/linux-arm64": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-arm64/-/linux-arm64-0.22.1.tgz",
"integrity": "sha512-6LiUpP0Zir3+29FvBm7Y28q/dBjSHqTZ5MhG1Ckw4fGhI4cAvbcwXaKvbjx1TP7rRmBNOoq/M5xdpHjTb+GAew==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@oxlint-tsgolint/linux-x64": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-x64/-/linux-x64-0.22.1.tgz",
"integrity": "sha512-fuX1hEQfpHauUbXADsfqVhRzrUrGabzGXbj5wsp2vKhV5uk/Rze8Mba9GdjFGECzvXudMGqHqxB4r6jGRdhxVA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@oxlint-tsgolint/win32-arm64": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-arm64/-/win32-arm64-0.22.1.tgz",
"integrity": "sha512-8SZidAj+jrbZf9ZjBEYW0tiNZ+KasqB2zgW26qdiPpQSF/DzURnPmXz651IeA9YsmbVdHGIooEHUmev6QJdquA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@oxlint-tsgolint/win32-x64": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-x64/-/win32-x64-0.22.1.tgz",
"integrity": "sha512-QweSk9H5lFh5Y+WUf2Kq/OAN88V6+62ZwGhP38gqdRotI90luXSMkruFTj7Q2rYrzH4ZVNaSqx7NY8JpSfIzqg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@oxlint/binding-android-arm-eabi": { "node_modules/@oxlint/binding-android-arm-eabi": {
"version": "1.61.0", "version": "1.61.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.61.0.tgz", "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.61.0.tgz",
@@ -31174,6 +31259,24 @@
} }
} }
}, },
"node_modules/oxlint-tsgolint": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/oxlint-tsgolint/-/oxlint-tsgolint-0.22.1.tgz",
"integrity": "sha512-YUSGSLUnoolsu8gxISEDio3q1rtsCozwfOzASUn3DT2mR2EeQ93uEEnen7s+6LpF+lyTQFln1pQfqwBh/fsVEg==",
"dev": true,
"license": "MIT",
"bin": {
"tsgolint": "bin/tsgolint.js"
},
"optionalDependencies": {
"@oxlint-tsgolint/darwin-arm64": "0.22.1",
"@oxlint-tsgolint/darwin-x64": "0.22.1",
"@oxlint-tsgolint/linux-arm64": "0.22.1",
"@oxlint-tsgolint/linux-x64": "0.22.1",
"@oxlint-tsgolint/win32-arm64": "0.22.1",
"@oxlint-tsgolint/win32-x64": "0.22.1"
}
},
"node_modules/p-cancelable": { "node_modules/p-cancelable": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",

View File

@@ -101,6 +101,7 @@
"lefthook": "^2.1.6", "lefthook": "^2.1.6",
"oxfmt": "0.46.0", "oxfmt": "0.46.0",
"oxlint": "1.61.0", "oxlint": "1.61.0",
"oxlint-tsgolint": "^0.22.1",
"patch-package": "^8.0.1", "patch-package": "^8.0.1",
"playwright": "^1.56.1", "playwright": "^1.56.1",
"typescript": "^5.9.3", "typescript": "^5.9.3",

View File

@@ -204,8 +204,7 @@ export async function installTerminalRenderProbe(page: Page): Promise<void> {
if (next?.write && !next.__paseoRenderProbeWriteWrapped) { if (next?.write && !next.__paseoRenderProbeWriteWrapped) {
const originalWrite = next.write.bind(next); const originalWrite = next.write.bind(next);
next.write = (data: string | Uint8Array, callback?: () => void) => { next.write = (data: string | Uint8Array, callback?: () => void) => {
const text = const text = typeof data === "string" ? data : new TextDecoder().decode(data);
typeof data === "string" ? data : new TextDecoder().decode(data as Uint8Array);
probe.writeCount += 1; probe.writeCount += 1;
const preview = text const preview = text
.replaceAll("\u001b", "\\x1b") .replaceAll("\u001b", "\\x1b")
@@ -424,7 +423,7 @@ export async function installTerminalKeystrokeStressProbe(page: Page): Promise<v
const count = Math.min(from.length, to.length); const count = Math.min(from.length, to.length);
const values: number[] = []; const values: number[] = [];
for (let index = 0; index < count; index += 1) { for (let index = 0; index < count; index += 1) {
values.push(to[index]!.at - from[index]!.at); values.push(to[index].at - from[index].at);
} }
return values; return values;
} }
@@ -459,7 +458,7 @@ export async function installTerminalKeystrokeStressProbe(page: Page): Promise<v
const keydownToInputFrame = this.keydowns const keydownToInputFrame = this.keydowns
.map((keydown) => firstAtOrAfter(this.inputFrames, keydown.at)?.at ?? null) .map((keydown) => firstAtOrAfter(this.inputFrames, keydown.at)?.at ?? null)
.filter((at): at is number => at !== null) .filter((at): at is number => at !== null)
.map((at, index) => at - this.keydowns[index]!.at); .map((at, index) => at - this.keydowns[index].at);
const inputFrameToOutputFrame = this.inputFrames const inputFrameToOutputFrame = this.inputFrames
.map((input) => { .map((input) => {
const output = firstAtOrAfter(this.outputFrames, input.at); const output = firstAtOrAfter(this.outputFrames, input.at);
@@ -663,8 +662,7 @@ export async function installTerminalKeystrokeStressProbe(page: Page): Promise<v
if (next?.write && !next.__paseoKeystrokeProbeWriteWrapped) { if (next?.write && !next.__paseoKeystrokeProbeWriteWrapped) {
const originalWrite = next.write.bind(next); const originalWrite = next.write.bind(next);
next.write = (data: string | Uint8Array, callback?: () => void) => { next.write = (data: string | Uint8Array, callback?: () => void) => {
const text = const text = typeof data === "string" ? data : new TextDecoder().decode(data);
typeof data === "string" ? data : new TextDecoder().decode(data as Uint8Array);
const event: XtermWriteEvent = { const event: XtermWriteEvent = {
at: performance.now(), at: performance.now(),
committedAt: null, committedAt: null,

View File

@@ -278,7 +278,7 @@ async function measureDaemonBurstEcho(
timeoutMs: STRESS_TIMEOUT_MS, timeoutMs: STRESS_TIMEOUT_MS,
}); });
const latencies = sendTimes.map((sentAt, index) => outputTimesByByte[index]! - sentAt); const latencies = sendTimes.map((sentAt, index) => outputTimesByByte[index] - sentAt);
return { return {
inputTextLength: inputText.length, inputTextLength: inputText.length,
inputFrameCount: sendTimes.length, inputFrameCount: sendTimes.length,

View File

@@ -422,7 +422,7 @@ function AppContainer({
const cycleTheme = useCallback(() => { const cycleTheme = useCallback(() => {
const currentIndex = THEME_CYCLE_ORDER.indexOf(settings.theme as ThemeName); const currentIndex = THEME_CYCLE_ORDER.indexOf(settings.theme as ThemeName);
const nextIndex = (currentIndex + 1) % THEME_CYCLE_ORDER.length; const nextIndex = (currentIndex + 1) % THEME_CYCLE_ORDER.length;
void updateSettings({ theme: THEME_CYCLE_ORDER[nextIndex]! }); void updateSettings({ theme: THEME_CYCLE_ORDER[nextIndex] });
}, [settings.theme, updateSettings]); }, [settings.theme, updateSettings]);
const isCompactLayout = useIsCompactFormFactor(); const isCompactLayout = useIsCompactFormFactor();
@@ -785,7 +785,7 @@ function AppWithSidebar({ children }: { children: ReactNode }) {
if (hosts.some((host) => host.serverId === activeServerId)) { if (hosts.some((host) => host.serverId === activeServerId)) {
return; return;
} }
router.replace(mapPathnameToServer(pathname, hosts[0]!.serverId)); router.replace(mapPathnameToServer(pathname, hosts[0].serverId));
}, [activeServerId, hosts, pathname, router]); }, [activeServerId, hosts, pathname, router]);
// Parse selectedAgentKey directly from pathname // Parse selectedAgentKey directly from pathname

View File

@@ -22,9 +22,9 @@ class FakeRequest<T = unknown> {
class FakeObjectStore { class FakeObjectStore {
constructor(private readonly onPut: (record: unknown) => void) {} constructor(private readonly onPut: (record: unknown) => void) {}
put(record: unknown): FakeRequest<unknown> { put(record: unknown): FakeRequest {
this.onPut(record); this.onPut(record);
const request = new FakeRequest<unknown>(); const request = new FakeRequest();
queueMicrotask(() => request.emit("success")); queueMicrotask(() => request.emit("success"));
return request; return request;
} }

View File

@@ -61,7 +61,7 @@ function runTx<T>(
const request = run(store); const request = run(store);
request.addEventListener("success", () => { request.addEventListener("success", () => {
resolve(request.result as T); resolve(request.result);
}); });
request.addEventListener("error", () => { request.addEventListener("error", () => {

View File

@@ -234,7 +234,7 @@ function buildFallbackAllProviderModels(
map.set( map.set(
provider, provider,
modelOptions.map((option) => ({ modelOptions.map((option) => ({
provider: provider as AgentProvider, provider: provider,
id: option.id, id: option.id,
label: option.label, label: option.label,
})), })),

View File

@@ -461,7 +461,7 @@ export function BrowserPane({
}; };
const handleFaviconUpdated = (event: Event) => { const handleFaviconUpdated = (event: Event) => {
const favicons = Array.isArray((event as Event & { favicons?: unknown[] }).favicons) const favicons = Array.isArray((event as Event & { favicons?: unknown[] }).favicons)
? (((event as Event & { favicons?: string[] }).favicons as string[] | undefined) ?? []) ? ((event as Event & { favicons?: string[] }).favicons ?? [])
: []; : [];
updateBrowserRef.current(browserIdRef.current, { faviconUrl: favicons[0] ?? null }); updateBrowserRef.current(browserIdRef.current, { faviconUrl: favicons[0] ?? null });
}; };

View File

@@ -63,8 +63,8 @@ describe("combined model selector helpers", () => {
}), }),
]); ]);
expect(matchesSearch(rows[0]!, "claude")).toBe(true); expect(matchesSearch(rows[0], "claude")).toBe(true);
expect(matchesSearch(rows[1]!, "gpt-5.4")).toBe(true); expect(matchesSearch(rows[1], "gpt-5.4")).toBe(true);
}); });
it("keeps the selected trigger label model-only", () => { it("keeps the selected trigger label model-only", () => {

View File

@@ -622,7 +622,7 @@ export function CombinedModelSelector({
const singleProviderView = useMemo<SelectorView | null>(() => { const singleProviderView = useMemo<SelectorView | null>(() => {
const providers = Array.from(allProviderModels.keys()); const providers = Array.from(allProviderModels.keys());
if (providers.length !== 1) return null; if (providers.length !== 1) return null;
const providerId = providers[0]!; const providerId = providers[0];
const label = resolveProviderLabel(providerDefinitions, providerId); const label = resolveProviderLabel(providerDefinitions, providerId);
return { kind: "provider", providerId, providerLabel: label }; return { kind: "provider", providerId, providerLabel: label };
}, [allProviderModels, providerDefinitions]); }, [allProviderModels, providerDefinitions]);
@@ -655,7 +655,7 @@ export function CombinedModelSelector({
const handleSelect = useCallback( const handleSelect = useCallback(
(provider: string, modelId: string) => { (provider: string, modelId: string) => {
onSelect(provider as AgentProvider, modelId); onSelect(provider, modelId);
setIsOpen(false); setIsOpen(false);
setSearchQuery(""); setSearchQuery("");
}, },

View File

@@ -160,20 +160,17 @@ function removeAttachmentAtIndex<T extends ComposerAttachment>(prev: T[], index:
} }
function buildCancelButtonStyle(isConnected: boolean, isCancellingAgent: boolean): object[] { function buildCancelButtonStyle(isConnected: boolean, isCancellingAgent: boolean): object[] {
const disabled = const disabled = !isConnected || isCancellingAgent ? styles.buttonDisabled : undefined;
!isConnected || isCancellingAgent ? (styles.buttonDisabled as object) : undefined; return [styles.cancelButton, disabled].filter((value): value is object => Boolean(value));
return [styles.cancelButton as object, disabled].filter((value): value is object =>
Boolean(value),
);
} }
function buildRealtimeVoiceButtonStyle( function buildRealtimeVoiceButtonStyle(
hovered: boolean | undefined, hovered: boolean | undefined,
voiceButtonDisabled: boolean, voiceButtonDisabled: boolean,
): object[] { ): object[] {
const hoveredStyle = hovered ? (styles.iconButtonHovered as object) : undefined; const hoveredStyle = hovered ? styles.iconButtonHovered : undefined;
const disabledStyle = voiceButtonDisabled ? (styles.buttonDisabled as object) : undefined; const disabledStyle = voiceButtonDisabled ? styles.buttonDisabled : undefined;
return [styles.realtimeVoiceButton as object, hoveredStyle, disabledStyle].filter( return [styles.realtimeVoiceButton, hoveredStyle, disabledStyle].filter(
(value): value is object => Boolean(value), (value): value is object => Boolean(value),
); );
} }

View File

@@ -132,7 +132,7 @@ function TreeRowItem({
); );
const handleCopy = useCallback(() => { const handleCopy = useCallback(() => {
void onCopyPath(entry.path); onCopyPath(entry.path);
}, [onCopyPath, entry.path]); }, [onCopyPath, entry.path]);
const handleDownload = useCallback(() => { const handleDownload = useCallback(() => {

View File

@@ -29,7 +29,7 @@ interface PathRowProps {
function PathRow({ path, active, onSelect }: PathRowProps) { function PathRow({ path, active, onSelect }: PathRowProps) {
const { theme } = useUnistyles(); const { theme } = useUnistyles();
const handlePress = useCallback(() => { const handlePress = useCallback(() => {
void onSelect(path); onSelect(path);
}, [onSelect, path]); }, [onSelect, path]);
const pressableStyle = useCallback( const pressableStyle = useCallback(
({ hovered = false, pressed }: PressableStateCallbackType & { hovered?: boolean }) => [ ({ hovered = false, pressed }: PressableStateCallbackType & { hovered?: boolean }) => [
@@ -172,7 +172,7 @@ export function ProjectPickerModal() {
if (key === "Enter") { if (key === "Enter") {
event.preventDefault(); event.preventDefault();
if (options.length > 0 && activeIndex < options.length) { if (options.length > 0 && activeIndex < options.length) {
void handleSelectPath(options[activeIndex]!); void handleSelectPath(options[activeIndex]);
} else if (query.trim()) { } else if (query.trim()) {
handleSubmitCustom(); handleSubmitCustom();
} }

View File

@@ -117,7 +117,7 @@ function CustomModelsSection(props: {
}, },
}, },
}); });
await refresh([provider as AgentProvider]); await refresh([provider]);
}, },
[patchConfig, provider, refresh], [patchConfig, provider, refresh],
); );
@@ -284,7 +284,7 @@ export function ProviderDiagnosticSheet({
} }
try { try {
const result = await client.getProviderDiagnostic(provider as AgentProvider); const result = await client.getProviderDiagnostic(provider);
setDiagnostic(result.diagnostic); setDiagnostic(result.diagnostic);
} catch (err) { } catch (err) {
setDiagnostic(err instanceof Error ? err.message : "Failed to fetch diagnostic"); setDiagnostic(err instanceof Error ? err.message : "Failed to fetch diagnostic");
@@ -308,7 +308,7 @@ export function ProviderDiagnosticSheet({
if (!provider) { if (!provider) {
return; return;
} }
void Promise.all([refresh([provider as AgentProvider]), fetchDiagnostic()]).catch((err) => { void Promise.all([refresh([provider]), fetchDiagnostic()]).catch((err) => {
setDiagnostic(err instanceof Error ? err.message : "Failed to refresh provider"); setDiagnostic(err instanceof Error ? err.message : "Failed to refresh provider");
}); });
}, [fetchDiagnostic, provider, refresh]); }, [fetchDiagnostic, provider, refresh]);

View File

@@ -351,7 +351,7 @@ describe("sidebar workspace render isolation", () => {
act(() => { act(() => {
useSessionStore.getState().mergeWorkspaces(SERVER_ID, [ useSessionStore.getState().mergeWorkspaces(SERVER_ID, [
{ {
...createWorkspaces()[1]!, ...createWorkspaces()[1],
status: "running", status: "running",
}, },
]); ]);

View File

@@ -439,7 +439,7 @@ function WorkspaceStatusIndicator({
); );
} }
let KindIcon: typeof ThemedMonitor | typeof ThemedFolderGit2 | null; let KindIcon: typeof ThemedMonitor | null;
if (workspaceKind === "local_checkout") KindIcon = ThemedMonitor; if (workspaceKind === "local_checkout") KindIcon = ThemedMonitor;
else if (workspaceKind === "worktree") KindIcon = ThemedFolderGit2; else if (workspaceKind === "worktree") KindIcon = ThemedFolderGit2;
else KindIcon = null; else KindIcon = null;

View File

@@ -488,7 +488,7 @@ function handleDesktopEnterKey(input: DesktopKeyHandlerInput) {
if (input.orderedVisibleOptions.length === 0) return; if (input.orderedVisibleOptions.length === 0) return;
const { activeIndex, orderedVisibleOptions } = input; const { activeIndex, orderedVisibleOptions } = input;
const index = activeIndex >= 0 && activeIndex < orderedVisibleOptions.length ? activeIndex : 0; const index = activeIndex >= 0 && activeIndex < orderedVisibleOptions.length ? activeIndex : 0;
input.handleSelect(orderedVisibleOptions[index]!.id); input.handleSelect(orderedVisibleOptions[index].id);
} }
interface FloatingSizeSetters { interface FloatingSizeSetters {

View File

@@ -140,9 +140,9 @@ export function useWebScrollViewScrollbar(
const scrollable = scrollableRef.current; const scrollable = scrollableRef.current;
if (!scrollable) return; if (!scrollable) return;
if ("scrollToOffset" in scrollable) { if ("scrollToOffset" in scrollable) {
(scrollable as FlatList).scrollToOffset({ offset, animated: false }); scrollable.scrollToOffset({ offset, animated: false });
} else { } else {
(scrollable as ScrollView).scrollTo({ y: offset, animated: false }); scrollable.scrollTo({ y: offset, animated: false });
} }
}, },
[scrollableRef], [scrollableRef],

View File

@@ -14,11 +14,7 @@ import {
type TimelineReducerSideEffect, type TimelineReducerSideEffect,
} from "@/timeline/session-stream-reducers"; } from "@/timeline/session-stream-reducers";
import { TIMELINE_FETCH_PAGE_SIZE } from "@/timeline/timeline-fetch-policy"; import { TIMELINE_FETCH_PAGE_SIZE } from "@/timeline/timeline-fetch-policy";
import type { import type { AgentAttachment, SessionOutboundMessage } from "@server/shared/messages";
AgentAttachment,
AgentStreamEventPayload,
SessionOutboundMessage,
} from "@server/shared/messages";
import { parseServerInfoStatusPayload } from "@server/shared/messages"; import { parseServerInfoStatusPayload } from "@server/shared/messages";
import { import {
buildAgentAttentionNotificationPayload, buildAgentAttentionNotificationPayload,
@@ -1201,7 +1197,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
if (message.type !== "agent_stream") return; if (message.type !== "agent_stream") return;
const { agentId, event, timestamp, seq, epoch } = message.payload; const { agentId, event, timestamp, seq, epoch } = message.payload;
const parsedTimestamp = new Date(timestamp); const parsedTimestamp = new Date(timestamp);
const streamEvent = event as AgentStreamEventPayload; const streamEvent = event;
if ( if (
event.type === "turn_started" || event.type === "turn_started" ||
event.type === "turn_completed" || event.type === "turn_completed" ||

View File

@@ -140,7 +140,7 @@ export function VoiceProvider({ children }: VoiceProviderProps) {
runtimeRef.current = runtime; runtimeRef.current = runtime;
} }
const engine = engineRef.current!; const engine = engineRef.current;
const runtime = runtimeRef.current!; const runtime = runtimeRef.current!;
useEffect(() => { useEffect(() => {

View File

@@ -13,7 +13,7 @@ function encodeBinaryToBase64(data: Uint8Array | ArrayBuffer): string {
const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data; const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
let binary = ""; let binary = "";
for (let index = 0; index < bytes.length; index += 1) { for (let index = 0; index < bytes.length; index += 1) {
binary += String.fromCharCode(bytes[index]!); binary += String.fromCharCode(bytes[index]);
} }
return globalThis.btoa(binary); return globalThis.btoa(binary);
} }

View File

@@ -110,7 +110,7 @@ export class DictationStreamSender {
let sent = 0; let sent = 0;
while (this.sendSeq < this.segments.length) { while (this.sendSeq < this.segments.length) {
const seq = this.sendSeq; const seq = this.sendSeq;
const audio = this.segments[seq]!; const audio = this.segments[seq];
client.sendDictationStreamChunk(dictationId, seq, audio, this.format); client.sendDictationStreamChunk(dictationId, seq, audio, this.format);
this.sendSeq = seq + 1; this.sendSeq = seq + 1;
sent += 1; sent += 1;

View File

@@ -35,7 +35,7 @@ const TEST_CLAUDE_DEFINITION: AgentProviderDefinition = {
function makeProviderMap( function makeProviderMap(
...definitions: AgentProviderDefinition[] ...definitions: AgentProviderDefinition[]
): Map<AgentProvider, AgentProviderDefinition> { ): Map<AgentProvider, AgentProviderDefinition> {
return new Map(definitions.map((d) => [d.id as AgentProvider, d])); return new Map(definitions.map((d) => [d.id, d]));
} }
const codexProviderMap = makeProviderMap(TEST_CODEX_DEFINITION); const codexProviderMap = makeProviderMap(TEST_CODEX_DEFINITION);

View File

@@ -196,8 +196,8 @@ function resolveProvider(input: {
if (initialValues?.provider && allowedProviderMap.has(initialValues.provider)) { if (initialValues?.provider && allowedProviderMap.has(initialValues.provider)) {
return initialValues.provider; return initialValues.provider;
} }
if (preferences?.provider && allowedProviderMap.has(preferences.provider as AgentProvider)) { if (preferences?.provider && allowedProviderMap.has(preferences.provider)) {
return preferences.provider as AgentProvider; return preferences.provider;
} }
if (currentProvider && allowedProviderMap.size > 0 && !allowedProviderMap.has(currentProvider)) { if (currentProvider && allowedProviderMap.size > 0 && !allowedProviderMap.has(currentProvider)) {
return null; return null;
@@ -396,7 +396,7 @@ function buildProviderDefinitionMapForStatuses(args: {
const matchingProviders = new Set( const matchingProviders = new Set(
args.snapshotEntries args.snapshotEntries
.filter((entry) => args.statuses.has(entry.status) && entry.enabled !== false) .filter((entry) => args.statuses.has(entry.status) && entry.enabled)
.map((entry) => entry.provider), .map((entry) => entry.provider),
); );

View File

@@ -341,7 +341,7 @@ export function useAudioRecorder(config?: AudioCaptureConfig) {
return () => { return () => {
attemptGuard.cancel(); attemptGuard.cancel();
try { try {
void mediaRecorder.current?.stop(); mediaRecorder.current?.stop();
} catch { } catch {
// Ignore stop during unmount. // Ignore stop during unmount.
} }

View File

@@ -332,7 +332,7 @@ export function useCommandCenter() {
if (currentItems.length === 0) return; if (currentItems.length === 0) return;
event.preventDefault(); event.preventDefault();
const index = Math.max(0, Math.min(activeIndexRef.current, currentItems.length - 1)); const index = Math.max(0, Math.min(activeIndexRef.current, currentItems.length - 1));
handleSelectItemRef.current(currentItems[index]!); handleSelectItemRef.current(currentItems[index]);
return; return;
} }

View File

@@ -68,7 +68,7 @@ const concatInt16 = (a: Int16Array, b: Int16Array): Int16Array => {
const int16ToFloat32 = (input: Int16Array): Float32Array => { const int16ToFloat32 = (input: Int16Array): Float32Array => {
const out = new Float32Array(input.length); const out = new Float32Array(input.length);
for (let i = 0; i < input.length; i += 1) { for (let i = 0; i < input.length; i += 1) {
out[i] = input[i]! / 32768; out[i] = input[i] / 32768;
} }
return out; return out;
}; };
@@ -209,8 +209,8 @@ export function useDictationAudioSource(config: DictationAudioSourceConfig): Dic
const decodeAudioData = useCallback( const decodeAudioData = useCallback(
async (context: AudioContext, buffer: ArrayBuffer): Promise<AudioBuffer> => { async (context: AudioContext, buffer: ArrayBuffer): Promise<AudioBuffer> => {
const maybePromise = context.decodeAudioData(buffer); const maybePromise = context.decodeAudioData(buffer);
if (maybePromise && typeof (maybePromise as Promise<AudioBuffer>).then === "function") { if (maybePromise && typeof maybePromise.then === "function") {
return maybePromise as Promise<AudioBuffer>; return maybePromise;
} }
return await new Promise<AudioBuffer>((resolve, reject) => { return await new Promise<AudioBuffer>((resolve, reject) => {
context.decodeAudioData(buffer, resolve, reject); context.decodeAudioData(buffer, resolve, reject);
@@ -276,7 +276,7 @@ export function useDictationAudioSource(config: DictationAudioSourceConfig): Dic
}, },
}); });
const stream = rawStream as MediaStream; const stream = rawStream;
const context = new AudioContextCtor(); const context = new AudioContextCtor();
@@ -347,7 +347,7 @@ export function useDictationAudioSource(config: DictationAudioSourceConfig): Dic
const recorder = new RecorderCtor(stream, { const recorder = new RecorderCtor(stream, {
mimeType: "audio/webm;codecs=opus", mimeType: "audio/webm;codecs=opus",
} as MediaRecorderOptions) as MediaRecorder; } as MediaRecorderOptions);
const recorderRefs: RecorderRefs = { const recorderRefs: RecorderRefs = {
recorder, recorder,

View File

@@ -260,7 +260,7 @@ export function useKeyboardShortcuts({
const handleCommandCenterToggle = (event: KeyboardEvent): boolean => { const handleCommandCenterToggle = (event: KeyboardEvent): boolean => {
const store = useKeyboardShortcutsStore.getState(); const store = useKeyboardShortcutsStore.getState();
if (!store.commandCenterOpen) { if (!store.commandCenterOpen) {
const target = event.target instanceof Element ? (event.target as Element) : null; const target = event.target instanceof Element ? event.target : null;
const targetEl = const targetEl =
target?.closest?.("textarea, input, [contenteditable='true']") ?? target?.closest?.("textarea, input, [contenteditable='true']") ??
(target instanceof HTMLElement ? target : null); (target instanceof HTMLElement ? target : null);

View File

@@ -86,7 +86,7 @@ export function usePrPaneData({
const checkoutPrStatus = useCheckoutPrStatusQuery({ serverId, cwd, enabled }); const checkoutPrStatus = useCheckoutPrStatusQuery({ serverId, cwd, enabled });
const status = checkoutPrStatus.status; const status = checkoutPrStatus.status;
const { prNumber, repoOwner, repoName } = extractPrRepoIdentity(status); const { prNumber, repoOwner, repoName } = extractPrRepoIdentity(status);
const githubFeaturesEnabled = checkoutPrStatus.githubFeaturesEnabled !== false; const githubFeaturesEnabled = checkoutPrStatus.githubFeaturesEnabled;
const unsupportedKey = const unsupportedKey =
prNumber === null ? null : timelineUnsupportedKey({ serverId, cwd, prNumber }); prNumber === null ? null : timelineUnsupportedKey({ serverId, cwd, prNumber });
const timelineUnsupported = unsupportedKey ? unsupportedTimelineKeys.has(unsupportedKey) : false; const timelineUnsupported = unsupportedKey ? unsupportedTimelineKeys.has(unsupportedKey) : false;
@@ -100,7 +100,7 @@ export function usePrPaneData({
timelineUnsupported, timelineUnsupported,
}); });
const timelineQuery = useQuery<PullRequestTimeline, Error>({ const timelineQuery = useQuery<PullRequestTimeline>({
queryKey: prPaneTimelineQueryKey({ serverId, cwd, prNumber }), queryKey: prPaneTimelineQueryKey({ serverId, cwd, prNumber }),
queryFn: async () => { queryFn: async () => {
if (!daemonClient || prNumber === null || repoOwner === null || repoName === null) { if (!daemonClient || prNumber === null || repoOwner === null || repoName === null) {

View File

@@ -235,16 +235,16 @@ export async function loadSettingsFromStorage(): Promise<Settings> {
function pickAppSettings(stored: Partial<AppSettings>): Partial<AppSettings> { function pickAppSettings(stored: Partial<AppSettings>): Partial<AppSettings> {
const result: Partial<AppSettings> = {}; const result: Partial<AppSettings> = {};
if (typeof stored.theme === "string" && VALID_THEMES.has(stored.theme)) { if (typeof stored.theme === "string" && VALID_THEMES.has(stored.theme)) {
result.theme = stored.theme as AppSettings["theme"]; result.theme = stored.theme;
} }
if (stored.sendBehavior === "interrupt" || stored.sendBehavior === "queue") { if (stored.sendBehavior === "interrupt" || stored.sendBehavior === "queue") {
result.sendBehavior = stored.sendBehavior; result.sendBehavior = stored.sendBehavior;
} }
if ( if (
typeof stored.serviceUrlBehavior === "string" && typeof stored.serviceUrlBehavior === "string" &&
VALID_SERVICE_URL_BEHAVIORS.has(stored.serviceUrlBehavior as ServiceUrlBehavior) VALID_SERVICE_URL_BEHAVIORS.has(stored.serviceUrlBehavior)
) { ) {
result.serviceUrlBehavior = stored.serviceUrlBehavior as ServiceUrlBehavior; result.serviceUrlBehavior = stored.serviceUrlBehavior;
} }
return result; return result;
} }

View File

@@ -750,7 +750,7 @@ function ChatAgentContent({
}; };
}, []); }, []);
const isInitializing = agentId ? isInitializingFromMap !== false : false; const isInitializing = agentId ? isInitializingFromMap : false;
const isHistorySyncing = useMemo(() => { const isHistorySyncing = useMemo(() => {
if (!agentId || !isInitializing) { if (!agentId || !isInitializing) {
return false; return false;

View File

@@ -197,7 +197,7 @@ describe("useInlineReviewController", () => {
body: "first comment", body: "first comment",
}); });
act(() => result.current.onEditComment(reviewTarget, savedComment!)); act(() => result.current.onEditComment(reviewTarget, savedComment));
expect(result.current.editor).toEqual({ expect(result.current.editor).toEqual({
target: reviewTarget, target: reviewTarget,
commentId: savedComment?.id, commentId: savedComment?.id,
@@ -208,8 +208,8 @@ describe("useInlineReviewController", () => {
const updatedComment = useReviewDraftStore.getState().drafts[firstKey]?.[0]; const updatedComment = useReviewDraftStore.getState().drafts[firstKey]?.[0];
expect(updatedComment).toMatchObject({ id: savedComment?.id, body: "updated comment" }); expect(updatedComment).toMatchObject({ id: savedComment?.id, body: "updated comment" });
act(() => result.current.onEditComment(reviewTarget, updatedComment!)); act(() => result.current.onEditComment(reviewTarget, updatedComment));
act(() => result.current.onDeleteComment(updatedComment!.id)); act(() => result.current.onDeleteComment(updatedComment.id));
expect(useReviewDraftStore.getState().drafts[firstKey]).toEqual([]); expect(useReviewDraftStore.getState().drafts[firstKey]).toEqual([]);
expect(result.current.editor).toBeNull(); expect(result.current.editor).toBeNull();

View File

@@ -752,7 +752,7 @@ describe("ProjectSettingsScreen — save flow", () => {
await flush(); await flush();
expect(client.writeProjectConfig).toHaveBeenCalledTimes(1); expect(client.writeProjectConfig).toHaveBeenCalledTimes(1);
const callArg = client.writeProjectConfig.mock.calls[0]![0] as { const callArg = client.writeProjectConfig.mock.calls[0][0] as {
repoRoot: string; repoRoot: string;
config: PaseoConfigRaw; config: PaseoConfigRaw;
expectedRevision: PaseoConfigRevision | null; expectedRevision: PaseoConfigRevision | null;
@@ -767,7 +767,7 @@ describe("ProjectSettingsScreen — save flow", () => {
// Subsequent saves use the freshly-returned revision. // Subsequent saves use the freshly-returned revision.
click(requireById("save-button")); click(requireById("save-button"));
await flush(); await flush();
const secondArg = client.writeProjectConfig.mock.calls[1]![0] as { const secondArg = client.writeProjectConfig.mock.calls[1][0] as {
expectedRevision: PaseoConfigRevision; expectedRevision: PaseoConfigRevision;
}; };
expect(secondArg.expectedRevision).toEqual(newRevision); expect(secondArg.expectedRevision).toEqual(newRevision);
@@ -869,7 +869,7 @@ describe("ProjectSettingsScreen — round-trip semantics", () => {
click(requireById("save-button")); click(requireById("save-button"));
await flush(); await flush();
const savedConfig = client.writeProjectConfig.mock.calls[0]![0]!.config as PaseoConfigRaw; const savedConfig = client.writeProjectConfig.mock.calls[0][0]!.config as PaseoConfigRaw;
// string stays string when edited in place // string stays string when edited in place
expect(savedConfig.worktree?.setup).toBe("npm install\nnpm run prepare"); expect(savedConfig.worktree?.setup).toBe("npm install\nnpm run prepare");
}); });
@@ -897,7 +897,7 @@ describe("ProjectSettingsScreen — round-trip semantics", () => {
click(requireById("save-button")); click(requireById("save-button"));
await flush(); await flush();
const savedConfig = client.writeProjectConfig.mock.calls[0]![0]!.config as PaseoConfigRaw; const savedConfig = client.writeProjectConfig.mock.calls[0][0]!.config as PaseoConfigRaw;
expect(savedConfig.worktree?.teardown).toEqual(["docker compose down", "rm -rf .cache"]); expect(savedConfig.worktree?.teardown).toEqual(["docker compose down", "rm -rf .cache"]);
}); });
@@ -920,7 +920,7 @@ describe("ProjectSettingsScreen — round-trip semantics", () => {
click(requireById("save-button")); click(requireById("save-button"));
await flush(); await flush();
const savedConfig = client.writeProjectConfig.mock.calls[0]![0]!.config as PaseoConfigRaw; const savedConfig = client.writeProjectConfig.mock.calls[0][0]!.config as PaseoConfigRaw;
expect(savedConfig.worktree?.setup).toBe("npm install"); expect(savedConfig.worktree?.setup).toBe("npm install");
}); });
@@ -943,7 +943,7 @@ describe("ProjectSettingsScreen — round-trip semantics", () => {
click(requireById("save-button")); click(requireById("save-button"));
await flush(); await flush();
const savedConfig = client.writeProjectConfig.mock.calls[0]![0]!.config as PaseoConfigRaw; const savedConfig = client.writeProjectConfig.mock.calls[0][0]!.config as PaseoConfigRaw;
expect(savedConfig.worktree?.setup).toEqual(["npm install", "npm run prepare"]); expect(savedConfig.worktree?.setup).toEqual(["npm install", "npm run prepare"]);
}); });
}); });
@@ -996,7 +996,7 @@ describe("ProjectSettingsScreen — scripts editor", () => {
click(requireById("save-button")); click(requireById("save-button"));
await flush(); await flush();
const savedConfig = client.writeProjectConfig.mock.calls[0]![0]!.config as PaseoConfigRaw; const savedConfig = client.writeProjectConfig.mock.calls[0][0]!.config as PaseoConfigRaw;
const scriptKeys = Object.keys((savedConfig as Record<string, unknown>).scripts ?? {}); const scriptKeys = Object.keys((savedConfig as Record<string, unknown>).scripts ?? {});
// One of the two scripts was removed. // One of the two scripts was removed.
expect(scriptKeys.length).toBe(1); expect(scriptKeys.length).toBe(1);
@@ -1031,7 +1031,7 @@ describe("ProjectSettingsScreen — scripts editor", () => {
click(requireById("save-button")); click(requireById("save-button"));
await flush(); await flush();
const savedConfig = client.writeProjectConfig.mock.calls[0]![0]!.config as PaseoConfigRaw; const savedConfig = client.writeProjectConfig.mock.calls[0][0]!.config as PaseoConfigRaw;
expect((savedConfig as Record<string, unknown>).customTopLevel).toBe("preserved"); expect((savedConfig as Record<string, unknown>).customTopLevel).toBe("preserved");
const worktreeRecord = (savedConfig.worktree ?? {}) as Record<string, unknown>; const worktreeRecord = (savedConfig.worktree ?? {}) as Record<string, unknown>;
expect(worktreeRecord.customWorktreeField).toBe("keep"); expect(worktreeRecord.customWorktreeField).toBe("keep");

View File

@@ -1541,7 +1541,7 @@ function WorkspaceScreenContent({
if (!client || !workspaceDirectory) { if (!client || !workspaceDirectory) {
throw new Error("Host is not connected"); throw new Error("Host is not connected");
} }
return (await client.getCheckoutStatus(workspaceDirectory)) as CheckoutStatusPayload; return await client.getCheckoutStatus(workspaceDirectory);
}, },
staleTime: Infinity, staleTime: Infinity,
refetchOnMount: false, refetchOnMount: false,

View File

@@ -68,7 +68,7 @@ describe("computeWorkspaceTabLayout", () => {
expect(result.closeButtonPolicy).toBe("all"); expect(result.closeButtonPolicy).toBe("all");
expect(result.requiresHorizontalScrollFallback).toBe(false); expect(result.requiresHorizontalScrollFallback).toBe(false);
expect(result.items.map((item) => item.width)).toEqual([60, 60, 60, 60]); expect(result.items.map((item) => item.width)).toEqual([60, 60, 60, 60]);
expect(result.items.every((item) => item.showLabel === false)).toBe(true); expect(result.items.every((item) => !item.showLabel)).toBe(true);
}); });
it("allows horizontal scroll only when icon-only tabs still cannot fit", () => { it("allows horizontal scroll only when icon-only tabs still cannot fit", () => {
@@ -81,7 +81,7 @@ describe("computeWorkspaceTabLayout", () => {
expect(result.closeButtonPolicy).toBe("all"); expect(result.closeButtonPolicy).toBe("all");
expect(result.requiresHorizontalScrollFallback).toBe(true); expect(result.requiresHorizontalScrollFallback).toBe(true);
expect(result.items.map((item) => item.width)).toEqual([60, 60, 60, 60]); expect(result.items.map((item) => item.width)).toEqual([60, 60, 60, 60]);
expect(result.items.every((item) => item.showLabel === false)).toBe(true); expect(result.items.every((item) => !item.showLabel)).toBe(true);
}); });
it("returns empty layout details when there are no tabs", () => { it("returns empty layout details when there are no tabs", () => {

View File

@@ -23,7 +23,7 @@ describe("deriveWorkspaceTabModel", () => {
]; ];
const model = deriveWorkspaceTabModel({ const model = deriveWorkspaceTabModel({
tabs: [uiTabs[0]!, uiTabs[2]!, uiTabs[1]!], tabs: [uiTabs[0], uiTabs[2], uiTabs[1]],
}); });
expect(model.tabs.map((tab) => tab.descriptor.tabId)).toEqual([ expect(model.tabs.map((tab) => tab.descriptor.tabId)).toEqual([

View File

@@ -526,7 +526,7 @@ function findNearestSiblingPaneId(root: SplitNodeInternal, paneId: string): stri
for (let depth = path.length - 1; depth >= 0; depth -= 1) { for (let depth = path.length - 1; depth >= 0; depth -= 1) {
const parentPath = path.slice(0, depth); const parentPath = path.slice(0, depth);
const childIndex = path[depth]!; const childIndex = path[depth];
const parentNode = getNodeAtPath(root, parentPath); const parentNode = getNodeAtPath(root, parentPath);
invariant(parentNode.kind === "group", "Expected parent group for pane lookup"); invariant(parentNode.kind === "group", "Expected parent group for pane lookup");
@@ -667,7 +667,7 @@ function removePaneByPath(root: SplitNodeInternal, path: number[]): SplitNodeInt
} }
const parentPath = path.slice(0, -1); const parentPath = path.slice(0, -1);
const removeIndex = path[path.length - 1]!; const removeIndex = path[path.length - 1];
const parentNode = getNodeAtPath(root, parentPath); const parentNode = getNodeAtPath(root, parentPath);
invariant(parentNode.kind === "group", "Expected parent group while removing pane"); invariant(parentNode.kind === "group", "Expected parent group while removing pane");
@@ -676,7 +676,7 @@ function removePaneByPath(root: SplitNodeInternal, path: number[]): SplitNodeInt
const nextParentNode = const nextParentNode =
nextParentChildren.length === 1 nextParentChildren.length === 1
? nextParentChildren[0]! ? nextParentChildren[0]
: createGroupNode({ : createGroupNode({
id: parentNode.group.id, id: parentNode.group.id,
direction: parentNode.group.direction, direction: parentNode.group.direction,

View File

@@ -142,7 +142,7 @@ describe("workspace-layout-store tree transforms", () => {
const nextRoot = insertSplit(root, "right", "tab-c", "right"); const nextRoot = insertSplit(root, "right", "tab-c", "right");
const nextGroup = expectGroup(nextRoot); const nextGroup = expectGroup(nextRoot);
const nestedGroup = expectGroup(nextGroup.group.children[1]!); const nestedGroup = expectGroup(nextGroup.group.children[1]);
expect(nextGroup.group.direction).toBe("horizontal"); expect(nextGroup.group.direction).toBe("horizontal");
expect(nextGroup.group.children).toHaveLength(2); expect(nextGroup.group.children).toHaveLength(2);
@@ -259,7 +259,7 @@ describe("workspace-layout-store actions", () => {
kind: "file", kind: "file",
path: "/repo/worktree/b.ts", path: "/repo/worktree/b.ts",
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(firstTabId).toBe("file_/repo/worktree/a.ts"); expect(firstTabId).toBe("file_/repo/worktree/a.ts");
expect(secondTabId).toBe("file_/repo/worktree/b.ts"); expect(secondTabId).toBe("file_/repo/worktree/b.ts");
@@ -280,7 +280,7 @@ describe("workspace-layout-store actions", () => {
kind: "setup", kind: "setup",
workspaceId: "ws-main", workspaceId: "ws-main",
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const pane = findPaneById(layout.root, "main")!; const pane = findPaneById(layout.root, "main")!;
expect(agentTabId).toBe("agent_agent-1"); expect(agentTabId).toBe("agent_agent-1");
@@ -306,7 +306,7 @@ describe("workspace-layout-store actions", () => {
kind: "file", kind: "file",
path: "/repo/worktree/a.ts", path: "/repo/worktree/a.ts",
}); });
const layoutAfter = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layoutAfter = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const pane = findPaneById(layoutAfter.root, "main")!; const pane = findPaneById(layoutAfter.root, "main")!;
expect(duplicateTabId).toBe(firstTabId); expect(duplicateTabId).toBe(firstTabId);
@@ -380,7 +380,7 @@ describe("workspace-layout-store actions", () => {
const firstTabId = store.openTabFocused(workspaceKey, { kind: "draft", draftId: "draft-1" }); const firstTabId = store.openTabFocused(workspaceKey, { kind: "draft", draftId: "draft-1" });
const secondTabId = store.openTabFocused(workspaceKey, { kind: "draft", draftId: "draft-2" }); const secondTabId = store.openTabFocused(workspaceKey, { kind: "draft", draftId: "draft-2" });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(firstTabId).toBe("draft-1"); expect(firstTabId).toBe("draft-1");
expect(secondTabId).toBe("draft-2"); expect(secondTabId).toBe("draft-2");
@@ -416,14 +416,14 @@ describe("workspace-layout-store actions", () => {
kind: "draft", kind: "draft",
draftId: "draft-split", draftId: "draft-split",
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(newPaneId).toBe("pane_77777777-7777-7777-7777-777777777777"); expect(newPaneId).toBe("pane_77777777-7777-7777-7777-777777777777");
expect(draftTabId).toBe("draft-split"); expect(draftTabId).toBe("draft-split");
expect(layout.focusedPaneId).toBe(newPaneId); expect(layout.focusedPaneId).toBe(newPaneId);
expect(findPaneById(layout.root, "main")?.tabIds).toEqual(["file_/repo/worktree/a.ts"]); expect(findPaneById(layout.root, "main")?.tabIds).toEqual(["file_/repo/worktree/a.ts"]);
expect(findPaneById(layout.root, newPaneId!)?.tabIds).toEqual([draftTabId!]); expect(findPaneById(layout.root, newPaneId)?.tabIds).toEqual([draftTabId!]);
expect(findPaneById(layout.root, newPaneId!)?.focusedTabId).toBe(draftTabId); expect(findPaneById(layout.root, newPaneId)?.focusedTabId).toBe(draftTabId);
}); });
it("focusTab moves workspace focus to the pane containing the tab", () => { it("focusTab moves workspace focus to the pane containing the tab", () => {
@@ -448,14 +448,14 @@ describe("workspace-layout-store actions", () => {
}); });
store.focusTab(workspaceKey, fileTabId!); store.focusTab(workspaceKey, fileTabId!);
let layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; let layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(layout.focusedPaneId).toBe("main"); expect(layout.focusedPaneId).toBe("main");
store.focusTab(workspaceKey, terminalTabId!); store.focusTab(workspaceKey, terminalTabId!);
layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
expect(splitPaneId).toBe("pane_bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"); expect(splitPaneId).toBe("pane_bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
expect(layout.focusedPaneId).toBe(splitPaneId); expect(layout.focusedPaneId).toBe(splitPaneId);
expect(findPaneById(layout.root, splitPaneId!)?.focusedTabId).toBe(terminalTabId); expect(findPaneById(layout.root, splitPaneId)?.focusedTabId).toBe(terminalTabId);
}); });
it("convertDraftToAgent replaces the draft tab with a canonical agent tab in the same pane", () => { it("convertDraftToAgent replaces the draft tab with a canonical agent tab in the same pane", () => {
@@ -474,8 +474,8 @@ describe("workspace-layout-store actions", () => {
}); });
const nextTabId = store.convertDraftToAgent(workspaceKey, secondTabId!, "agent-1"); const nextTabId = store.convertDraftToAgent(workspaceKey, secondTabId!, "agent-1");
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const splitPane = findPaneById(layout.root, splitPaneId!); const splitPane = findPaneById(layout.root, splitPaneId);
const convertedTab = collectAllTabs(layout.root).find((tab) => tab.tabId === nextTabId); const convertedTab = collectAllTabs(layout.root).find((tab) => tab.tabId === nextTabId);
expect(splitPaneId).toBe("pane_12121212-1212-1212-1212-121212121212"); expect(splitPaneId).toBe("pane_12121212-1212-1212-1212-121212121212");
@@ -501,7 +501,7 @@ describe("workspace-layout-store actions", () => {
kind: "file", kind: "file",
path: "/repo/worktree/retargeted.ts", path: "/repo/worktree/retargeted.ts",
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(draftTabId).toBe("draft-retarget"); expect(draftTabId).toBe("draft-retarget");
expect(nextTabId).toBe(draftTabId); expect(nextTabId).toBe(draftTabId);
@@ -541,7 +541,7 @@ describe("workspace-layout-store actions", () => {
kind: "file", kind: "file",
path: "/repo/worktree/existing.ts", path: "/repo/worktree/existing.ts",
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(existingFileTabId).toBe("file_/repo/worktree/existing.ts"); expect(existingFileTabId).toBe("file_/repo/worktree/existing.ts");
expect(draftTabId).toBe("draft-dup"); expect(draftTabId).toBe("draft-dup");
@@ -576,7 +576,7 @@ describe("workspace-layout-store actions", () => {
kind: "agent", kind: "agent",
agentId: "agent-1", agentId: "agent-1",
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(firstAgentTabId).toBe(firstDraftTabId); expect(firstAgentTabId).toBe(firstDraftTabId);
expect(nextTabId).toBe(firstDraftTabId); expect(nextTabId).toBe(firstDraftTabId);
@@ -608,7 +608,7 @@ describe("workspace-layout-store actions", () => {
}); });
store.reorderTabs(workspaceKey, [thirdTabId!, firstTabId!]); store.reorderTabs(workspaceKey, [thirdTabId!, firstTabId!]);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(findPaneById(layout.root, "main")).toEqual({ expect(findPaneById(layout.root, "main")).toEqual({
id: "main", id: "main",
@@ -663,11 +663,11 @@ describe("workspace-layout-store actions", () => {
store.moveTabToPane(workspaceKey, fourthTabId!, splitPaneId!); store.moveTabToPane(workspaceKey, fourthTabId!, splitPaneId!);
store.focusPane(workspaceKey, "main"); store.focusPane(workspaceKey, "main");
store.reorderTabsInPane(workspaceKey, splitPaneId!, [fourthTabId!, thirdTabId!]); store.reorderTabsInPane(workspaceKey, splitPaneId!, [fourthTabId!, thirdTabId!]);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(splitPaneId).toBe("pane_34343434-3434-3434-3434-343434343434"); expect(splitPaneId).toBe("pane_34343434-3434-3434-3434-343434343434");
expect(layout.focusedPaneId).toBe("main"); expect(layout.focusedPaneId).toBe("main");
expect(findPaneById(layout.root, splitPaneId!)).toEqual({ expect(findPaneById(layout.root, splitPaneId)).toEqual({
id: splitPaneId, id: splitPaneId,
tabIds: [fourthTabId!, thirdTabId!], tabIds: [fourthTabId!, thirdTabId!],
focusedTabId: fourthTabId, focusedTabId: fourthTabId,
@@ -705,7 +705,7 @@ describe("workspace-layout-store actions", () => {
}); });
store.focusPane(workspaceKey, "main"); store.focusPane(workspaceKey, "main");
let layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; let layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(layout.focusedPaneId).toBe("main"); expect(layout.focusedPaneId).toBe("main");
store.focusPane(workspaceKey, splitPaneId!); store.focusPane(workspaceKey, splitPaneId!);
@@ -734,7 +734,7 @@ describe("workspace-layout-store actions", () => {
}); });
store.closeTab(workspaceKey, secondTabId!); store.closeTab(workspaceKey, secondTabId!);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(splitPaneId).toBe("pane_cccccccc-cccc-cccc-cccc-cccccccccccc"); expect(splitPaneId).toBe("pane_cccccccc-cccc-cccc-cccc-cccccccccccc");
expect(layout.focusedPaneId).toBe("main"); expect(layout.focusedPaneId).toBe("main");
@@ -782,7 +782,7 @@ describe("workspace-layout-store actions", () => {
position: "bottom", position: "bottom",
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(pane1).toBe("pane_11111111-1111-1111-1111-111111111111"); expect(pane1).toBe("pane_11111111-1111-1111-1111-111111111111");
expect(pane2).toBe("pane_33333333-3333-3333-3333-333333333333"); expect(pane2).toBe("pane_33333333-3333-3333-3333-333333333333");
expect(pane3).toBe("pane_55555555-5555-5555-5555-555555555555"); expect(pane3).toBe("pane_55555555-5555-5555-5555-555555555555");
@@ -812,11 +812,11 @@ describe("workspace-layout-store actions", () => {
}); });
store.moveTabToPane(workspaceKey, leftTabId!, splitPaneId!); store.moveTabToPane(workspaceKey, leftTabId!, splitPaneId!);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(layout.focusedPaneId).toBe(splitPaneId); expect(layout.focusedPaneId).toBe(splitPaneId);
expect(collectAllPanes(layout.root).map((pane) => pane.id)).toEqual([splitPaneId!]); expect(collectAllPanes(layout.root).map((pane) => pane.id)).toEqual([splitPaneId!]);
expect(findPaneById(layout.root, splitPaneId!)?.tabIds).toEqual([ expect(findPaneById(layout.root, splitPaneId)?.tabIds).toEqual([
"file_/repo/worktree/b.ts", "file_/repo/worktree/b.ts",
"file_/repo/worktree/a.ts", "file_/repo/worktree/a.ts",
]); ]);
@@ -852,7 +852,7 @@ describe("workspace-layout-store actions", () => {
}); });
store.closeTab(workspaceKey, secondTabId!); store.closeTab(workspaceKey, secondTabId!);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const rootGroup = expectGroup(layout.root); const rootGroup = expectGroup(layout.root);
expect(paneBId).toBe("pane_78787878-7878-7878-7878-787878787878"); expect(paneBId).toBe("pane_78787878-7878-7878-7878-787878787878");
@@ -910,7 +910,7 @@ describe("workspace-layout-store actions", () => {
kind: "file", kind: "file",
path: "/repo/worktree/b.ts", path: "/repo/worktree/b.ts",
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(splitPaneId).toBe("pane_abababab-abab-abab-abab-abababababab"); expect(splitPaneId).toBe("pane_abababab-abab-abab-abab-abababababab");
expect(duplicateTabId).toBe(secondTabId); expect(duplicateTabId).toBe(secondTabId);
@@ -946,14 +946,14 @@ describe("workspace-layout-store actions", () => {
position: "right", position: "right",
}); });
const splitRoot = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!.root; const splitRoot = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey].root;
const splitGroup = expectGroup(splitRoot); const splitGroup = expectGroup(splitRoot);
const nestedGroup = expectGroup(splitGroup.group.children[1]!); const nestedGroup = expectGroup(splitGroup.group.children[1]);
store.resizeSplit(workspaceKey, nestedGroup.group.id, [0.01, 0.99]); store.resizeSplit(workspaceKey, nestedGroup.group.id, [0.01, 0.99]);
const resizedRoot = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!.root; const resizedRoot = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey].root;
const resizedGroup = expectGroup(resizedRoot); const resizedGroup = expectGroup(resizedRoot);
const resizedNestedGroup = expectGroup(resizedGroup.group.children[1]!); const resizedNestedGroup = expectGroup(resizedGroup.group.children[1]);
const total = resizedNestedGroup.group.sizes.reduce((sum, size) => sum + size, 0); const total = resizedNestedGroup.group.sizes.reduce((sum, size) => sum + size, 0);
expect(rightPaneId).toBe("pane_eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee"); expect(rightPaneId).toBe("pane_eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee");
@@ -969,7 +969,7 @@ describe("workspace-layout-store actions", () => {
const tabId = store.openTabFocused(workspaceKey, { kind: "draft", draftId: "draft-1" }); const tabId = store.openTabFocused(workspaceKey, { kind: "draft", draftId: "draft-1" });
store.closeTab(workspaceKey, tabId!); store.closeTab(workspaceKey, tabId!);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(layout).toEqual(createDefaultLayout()); expect(layout).toEqual(createDefaultLayout());
}); });
@@ -1065,7 +1065,7 @@ describe("workspace-layout-store actions", () => {
}); });
const nextTabId = store.convertDraftToAgent(workspaceKey, draftTabId!, "agent-1"); const nextTabId = store.convertDraftToAgent(workspaceKey, draftTabId!, "agent-1");
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(splitPaneId).toBe("pane_67676767-6767-6767-6767-676767676767"); expect(splitPaneId).toBe("pane_67676767-6767-6767-6767-676767676767");
expect(nextTabId).toBe("agent_agent-1"); expect(nextTabId).toBe("agent_agent-1");
@@ -1129,7 +1129,7 @@ describe("workspace-layout-store actions", () => {
hasActivePendingDraftCreate: false, hasActivePendingDraftCreate: false,
}); });
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const tabs = collectAllTabs(layout.root); const tabs = collectAllTabs(layout.root);
expect(tabs.map((tab) => tab.tabId)).toEqual([ expect(tabs.map((tab) => tab.tabId)).toEqual([
@@ -1189,7 +1189,7 @@ describe("workspace-layout-store actions", () => {
}); });
const tabs = useWorkspaceLayoutStore.getState().getWorkspaceTabs(workspaceKey); const tabs = useWorkspaceLayoutStore.getState().getWorkspaceTabs(workspaceKey);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!; const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(tabs.map((tab) => tab.tabId)).toEqual(["terminal_term-script", "terminal_term-manual"]); expect(tabs.map((tab) => tab.tabId)).toEqual(["terminal_term-script", "terminal_term-manual"]);
expect(findPaneById(layout.root, layout.focusedPaneId)?.focusedTabId).toBe(scriptTabId); expect(findPaneById(layout.root, layout.focusedPaneId)?.focusedTabId).toBe(scriptTabId);
}); });

View File

@@ -134,9 +134,8 @@ export function upsertHostConnectionInProfiles(input: {
return [...existing, profile]; return [...existing, profile];
} }
const matchedProfiles = matchingIndexes.map((index) => existing[index]!); const matchedProfiles = matchingIndexes.map((index) => existing[index]);
const prev = const prev = matchedProfiles.find((daemon) => daemon.serverId === serverId) ?? matchedProfiles[0];
matchedProfiles.find((daemon) => daemon.serverId === serverId) ?? matchedProfiles[0]!;
const nextConnections = dedupeHostConnections([ const nextConnections = dedupeHostConnections([
...matchedProfiles.flatMap((daemon) => daemon.connections), ...matchedProfiles.flatMap((daemon) => daemon.connections),
input.connection, input.connection,
@@ -180,7 +179,7 @@ export function upsertHostConnectionInProfiles(input: {
updatedAt: now, updatedAt: now,
}; };
const firstIndex = matchingIndexes[0]!; const firstIndex = matchingIndexes[0];
const matchingIndexSet = new Set(matchingIndexes); const matchingIndexSet = new Set(matchingIndexes);
const next = existing.filter((_daemon, index) => !matchingIndexSet.has(index)); const next = existing.filter((_daemon, index) => !matchingIndexSet.has(index));
next.splice(firstIndex, 0, nextProfile); next.splice(firstIndex, 0, nextProfile);

View File

@@ -1,9 +1,9 @@
import type { FetchAgentHistoryEntry, FetchAgentsEntry } from "@server/client/daemon-client"; import type { FetchAgentsEntry } from "@server/client/daemon-client";
import { type Agent, useSessionStore } from "@/stores/session-store"; import { type Agent, useSessionStore } from "@/stores/session-store";
import { derivePendingPermissionKey, normalizeAgentSnapshot } from "@/utils/agent-snapshots"; import { derivePendingPermissionKey, normalizeAgentSnapshot } from "@/utils/agent-snapshots";
import { resolveProjectPlacement } from "@/utils/project-placement"; import { resolveProjectPlacement } from "@/utils/project-placement";
type AgentDirectoryFetchEntry = FetchAgentsEntry | FetchAgentHistoryEntry; type AgentDirectoryFetchEntry = FetchAgentsEntry;
interface PendingPermissionEntry { interface PendingPermissionEntry {
key: string; key: string;

View File

@@ -1,4 +1,3 @@
import type { AgentLifecycleStatus } from "@server/shared/agent-lifecycle";
import type { AgentSnapshotPayload } from "@server/shared/messages"; import type { AgentSnapshotPayload } from "@server/shared/messages";
import type { AgentPermissionRequest } from "@server/server/agent/agent-sdk-types"; import type { AgentPermissionRequest } from "@server/server/agent/agent-sdk-types";
@@ -31,7 +30,7 @@ export function normalizeAgentSnapshot(snapshot: AgentSnapshotPayload, serverId:
serverId, serverId,
id: snapshot.id, id: snapshot.id,
provider: snapshot.provider, provider: snapshot.provider,
status: snapshot.status as AgentLifecycleStatus, status: snapshot.status,
createdAt, createdAt,
updatedAt, updatedAt,
lastUserMessageAt, lastUserMessageAt,

View File

@@ -148,7 +148,7 @@ describe("sendOsNotification", () => {
clicked.clickListeners[0]?.({} as Event); clicked.clickListeners[0]?.({} as Event);
expect(dispatchEvent).toHaveBeenCalledTimes(1); expect(dispatchEvent).toHaveBeenCalledTimes(1);
const event = dispatchEvent.mock.calls[0]?.[0] as unknown as { const event = dispatchEvent.mock.calls[0]?.[0] as {
type?: string; type?: string;
detail?: { data?: Record<string, unknown> }; detail?: { data?: Record<string, unknown> };
}; };

View File

@@ -132,7 +132,7 @@ function dispatchWebNotificationClick(detail: WebNotificationClickDetail): boole
cancelable: true, cancelable: true,
}, },
); );
return dispatch(event) === false; return !dispatch(event);
} }
function fallbackNavigateToNotificationTarget(data: Record<string, unknown> | undefined): void { function fallbackNavigateToNotificationTarget(data: Record<string, unknown> | undefined): void {

View File

@@ -38,8 +38,8 @@ function resamplePcm16(pcm: Uint8Array, fromRate: number, toRate: number): Uint8
if (i + 1 >= pcm.length) { if (i + 1 >= pcm.length) {
return 0; return 0;
} }
const lo = pcm[i]!; const lo = pcm[i];
const hi = pcm[i + 1]!; const hi = pcm[i + 1];
let value = (hi << 8) | lo; let value = (hi << 8) | lo;
if (value & 0x8000) { if (value & 0x8000) {
value = value - 0x10000; value = value - 0x10000;

View File

@@ -39,7 +39,7 @@ function resampleToPcm16(input: Float32Array, inputRate: number, outputRate: num
const i0 = Math.floor(sourceIndex); const i0 = Math.floor(sourceIndex);
const i1 = Math.min(input.length - 1, i0 + 1); const i1 = Math.min(input.length - 1, i0 + 1);
const frac = sourceIndex - i0; const frac = sourceIndex - i0;
const sample = input[i0]! * (1 - frac) + input[i1]! * frac; const sample = input[i0] * (1 - frac) + input[i1] * frac;
output[i] = floatToInt16(sample); output[i] = floatToInt16(sample);
} }
@@ -64,8 +64,8 @@ function pcm16LeToAudioBuffer(
const audioBuffer = context.createBuffer(1, sampleCount, sampleRate); const audioBuffer = context.createBuffer(1, sampleCount, sampleRate);
const channel = audioBuffer.getChannelData(0); const channel = audioBuffer.getChannelData(0);
for (let i = 0; i < sampleCount; i += 1) { for (let i = 0; i < sampleCount; i += 1) {
const lo = bytes[i * 2]!; const lo = bytes[i * 2];
const hi = bytes[i * 2 + 1]!; const hi = bytes[i * 2 + 1];
let value = (hi << 8) | lo; let value = (hi << 8) | lo;
if (value & 0x8000) { if (value & 0x8000) {
value -= 0x10000; value -= 0x10000;
@@ -77,8 +77,8 @@ function pcm16LeToAudioBuffer(
async function decodeAudioData(context: AudioContext, buffer: ArrayBuffer): Promise<AudioBuffer> { async function decodeAudioData(context: AudioContext, buffer: ArrayBuffer): Promise<AudioBuffer> {
const maybePromise = context.decodeAudioData(buffer.slice(0)); const maybePromise = context.decodeAudioData(buffer.slice(0));
if (maybePromise && typeof (maybePromise as Promise<AudioBuffer>).then === "function") { if (maybePromise && typeof maybePromise.then === "function") {
return maybePromise as Promise<AudioBuffer>; return maybePromise;
} }
return await new Promise<AudioBuffer>((resolve, reject) => { return await new Promise<AudioBuffer>((resolve, reject) => {
context.decodeAudioData(buffer.slice(0), resolve, reject); context.decodeAudioData(buffer.slice(0), resolve, reject);
@@ -323,7 +323,7 @@ export function createAudioEngine(
const input = event.inputBuffer.getChannelData(0); const input = event.inputBuffer.getChannelData(0);
let sumSquares = 0; let sumSquares = 0;
for (let i = 0; i < input.length; i += 1) { for (let i = 0; i < input.length; i += 1) {
const sample = input[i]!; const sample = input[i];
sumSquares += sample * sample; sumSquares += sample * sample;
} }
const rms = Math.sqrt(sumSquares / Math.max(1, input.length)); const rms = Math.sqrt(sumSquares / Math.max(1, input.length));

View File

@@ -295,7 +295,7 @@ export function createVoiceRuntime(deps: VoiceRuntimeDeps): VoiceRuntime {
function activateNextPlaybackGroup(): void { function activateNextPlaybackGroup(): void {
while (playback.orderedGroupIds.length > 0) { while (playback.orderedGroupIds.length > 0) {
const groupId = playback.orderedGroupIds[0]!; const groupId = playback.orderedGroupIds[0];
if (playback.groups.has(groupId)) { if (playback.groups.has(groupId)) {
playback.activeGroupId = groupId; playback.activeGroupId = groupId;
return; return;

View File

@@ -44,7 +44,7 @@ export async function runArchiveCommand(
options: AgentArchiveOptions, options: AgentArchiveOptions,
_command: Command, _command: Command,
): Promise<AgentArchiveCommandResult> { ): Promise<AgentArchiveCommandResult> {
const host = getDaemonHost({ host: options.host as string | undefined }); const host = getDaemonHost({ host: options.host });
// Validate arguments // Validate arguments
if (!agentIdArg || agentIdArg.trim().length === 0) { if (!agentIdArg || agentIdArg.trim().length === 0) {
@@ -58,7 +58,7 @@ export async function runArchiveCommand(
let client: DaemonClient; let client: DaemonClient;
try { try {
client = await connectToDaemon({ host: options.host as string | undefined }); client = await connectToDaemon({ host: options.host });
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : String(err); const message = err instanceof Error ? err.message : String(err);
const error: CommandError = { const error: CommandError = {

View File

@@ -105,7 +105,7 @@ export async function runAttachCommand(
options: AgentAttachOptions, options: AgentAttachOptions,
_command: Command, _command: Command,
): Promise<void> { ): Promise<void> {
const host = getDaemonHost({ host: options.host as string | undefined }); const host = getDaemonHost({ host: options.host });
if (!id) { if (!id) {
console.error("Error: Agent ID required"); console.error("Error: Agent ID required");
@@ -115,7 +115,7 @@ export async function runAttachCommand(
let client: DaemonClient; let client: DaemonClient;
try { try {
client = await connectToDaemon({ host: options.host as string | undefined }); client = await connectToDaemon({ host: options.host });
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : String(err); const message = err instanceof Error ? err.message : String(err);
console.error(`Error: Cannot connect to daemon at ${host}: ${message}`); console.error(`Error: Cannot connect to daemon at ${host}: ${message}`);

View File

@@ -113,7 +113,7 @@ export async function runImportCommand(
options: AgentImportOptions, options: AgentImportOptions,
_command: Command, _command: Command,
): Promise<AgentImportCommandResult> { ): Promise<AgentImportCommandResult> {
const host = getDaemonHost({ host: options.host as string | undefined }); const host = getDaemonHost({ host: options.host });
const sessionId = sessionIdArg.trim(); const sessionId = sessionIdArg.trim();
if (!sessionId) { if (!sessionId) {
throw { throw {
@@ -134,7 +134,7 @@ export async function runImportCommand(
} }
const labels = parseImportLabels(options.label); const labels = parseImportLabels(options.label);
const client = await connectToDaemonOrThrow(options.host as string | undefined, host); const client = await connectToDaemonOrThrow(options.host, host);
try { try {
const agent = await client.importAgent({ const agent = await client.importAgent({

View File

@@ -216,7 +216,7 @@ export async function runInspectCommand(
options: AgentInspectOptions, options: AgentInspectOptions,
_command: Command, _command: Command,
): Promise<AgentInspectResult> { ): Promise<AgentInspectResult> {
const host = getDaemonHost({ host: options.host as string | undefined }); const host = getDaemonHost({ host: options.host });
// Validate arguments // Validate arguments
if (!agentIdArg || agentIdArg.trim().length === 0) { if (!agentIdArg || agentIdArg.trim().length === 0) {
@@ -230,7 +230,7 @@ export async function runInspectCommand(
let client; let client;
try { try {
client = await connectToDaemon({ host: options.host as string | undefined }); client = await connectToDaemon({ host: options.host });
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : String(err); const message = err instanceof Error ? err.message : String(err);
const error: CommandError = { const error: CommandError = {

View File

@@ -40,7 +40,7 @@ export async function runReloadCommand(
options: AgentReloadOptions, options: AgentReloadOptions,
_command: Command, _command: Command,
): Promise<AgentReloadCommandResult> { ): Promise<AgentReloadCommandResult> {
const host = getDaemonHost({ host: options.host as string | undefined }); const host = getDaemonHost({ host: options.host });
if (!agentIdArg || agentIdArg.trim().length === 0) { if (!agentIdArg || agentIdArg.trim().length === 0) {
const error: CommandError = { const error: CommandError = {
@@ -53,7 +53,7 @@ export async function runReloadCommand(
let client: DaemonClient; let client: DaemonClient;
try { try {
client = await connectToDaemon({ host: options.host as string | undefined }); client = await connectToDaemon({ host: options.host });
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : String(err); const message = err instanceof Error ? err.message : String(err);
const error: CommandError = { const error: CommandError = {

View File

@@ -86,7 +86,7 @@ export async function runUpdateCommand(
options: AgentUpdateOptions, options: AgentUpdateOptions,
_command: Command, _command: Command,
): Promise<AgentUpdateCommandResult> { ): Promise<AgentUpdateCommandResult> {
const host = getDaemonHost({ host: options.host as string | undefined }); const host = getDaemonHost({ host: options.host });
// Validate arguments // Validate arguments
if (!agentIdArg || agentIdArg.trim().length === 0) { if (!agentIdArg || agentIdArg.trim().length === 0) {
@@ -120,7 +120,7 @@ export async function runUpdateCommand(
let client; let client;
try { try {
client = await connectToDaemon({ host: options.host as string | undefined }); client = await connectToDaemon({ host: options.host });
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : String(err); const message = err instanceof Error ? err.message : String(err);
const error: CommandError = { const error: CommandError = {

View File

@@ -139,7 +139,7 @@ export async function runWaitCommand(
options: AgentWaitOptions, options: AgentWaitOptions,
_command: Command, _command: Command,
): Promise<SingleResult<AgentWaitResult>> { ): Promise<SingleResult<AgentWaitResult>> {
const host = getDaemonHost({ host: options.host as string | undefined }); const host = getDaemonHost({ host: options.host });
if (!agentIdArg || agentIdArg.trim().length === 0) { if (!agentIdArg || agentIdArg.trim().length === 0) {
throw { throw {
@@ -153,7 +153,7 @@ export async function runWaitCommand(
let client; let client;
try { try {
client = await connectToDaemon({ host: options.host as string | undefined }); client = await connectToDaemon({ host: options.host });
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : String(err); const message = err instanceof Error ? err.message : String(err);
const error: CommandError = { const error: CommandError = {

View File

@@ -32,7 +32,7 @@ export async function runPostCommand(
]); ]);
return { return {
type: "single", type: "single",
data: message!, data: message,
schema: chatMessageSchema, schema: chatMessageSchema,
}; };
} catch (err) { } catch (err) {

View File

@@ -12,7 +12,7 @@ export function pairCommand(): Command {
return addJsonOption(new Command("pair").description("Print the daemon pairing QR code and link")) return addJsonOption(new Command("pair").description("Print the daemon pairing QR code and link"))
.option("--home <path>", "Paseo home directory (default: ~/.paseo)") .option("--home <path>", "Paseo home directory (default: ~/.paseo)")
.action(async (_options: PairOptions, command: Command) => { .action(async (_options: PairOptions, command: Command) => {
await runPairCommand(command.optsWithGlobals() as PairOptions); await runPairCommand(command.optsWithGlobals());
}); });
} }

View File

@@ -84,7 +84,7 @@ async function resolveNodePathFromPidWindows(pid: number): Promise<NodePathFromP
error: errors.join("; ") || "could not resolve executable path from PID", error: errors.join("; ") || "could not resolve executable path from PID",
}; };
} }
const probe = probes[index] as (typeof probes)[number]; const probe = probes[index];
const result = await runProcessProbe(probe.command, probe.args); const result = await runProcessProbe(probe.command, probe.args);
if (result.resolved) { if (result.resolved) {
const resolved = probe.parseValue ? probe.parseValue(result.resolved) : result.resolved; const resolved = probe.parseValue ? probe.parseValue(result.resolved) : result.resolved;

View File

@@ -111,7 +111,7 @@ export async function runAllowCommand(
permissionsToAllow = pendingPermissions; permissionsToAllow = pendingPermissions;
} else { } else {
// Find permission by ID prefix // Find permission by ID prefix
const permission = pendingPermissions.find((p) => p.id === reqId || p.id.startsWith(reqId!)); const permission = pendingPermissions.find((p) => p.id === reqId || p.id.startsWith(reqId));
if (!permission) { if (!permission) {
await client.close(); await client.close();
const error: CommandError = { const error: CommandError = {

View File

@@ -76,7 +76,7 @@ export async function runLsCommand(
provider: entry.provider, provider: entry.provider,
label: entry.label ?? entry.provider, label: entry.label ?? entry.provider,
status: entry.status === "ready" ? "available" : entry.status, status: entry.status === "ready" ? "available" : entry.status,
enabled: entry.enabled === false ? "Disabled" : "Enabled", enabled: !entry.enabled ? "Disabled" : "Enabled",
defaultMode: entry.defaultModeId ?? "default", defaultMode: entry.defaultModeId ?? "default",
modes: (entry.modes ?? []).map((mode) => mode.label).join(", "), modes: (entry.modes ?? []).map((mode) => mode.label).join(", "),
})), })),

View File

@@ -137,7 +137,7 @@ export function renderTable<T>(result: AnyCommandResult<T>, options: OutputOptio
return ""; return "";
} }
const columns = schema.columns as ColumnDef<T>[]; const columns = schema.columns;
const includeHeaders = !options.noHeaders; const includeHeaders = !options.noHeaders;
const widths = calculateWidths(data, columns, includeHeaders); const widths = calculateWidths(data, columns, includeHeaders);

View File

@@ -342,7 +342,7 @@ export async function connectToDaemon(options?: ConnectOptions): Promise<DaemonC
if (lastError instanceof Error) throw lastError; if (lastError instanceof Error) throw lastError;
throw new Error(`Unable to connect to Paseo daemon via ${hosts.join(", ")}`); throw new Error(`Unable to connect to Paseo daemon via ${hosts.join(", ")}`);
} }
const host = hosts[index] as string; const host = hosts[index];
const password = resolveDaemonPassword(host); const password = resolveDaemonPassword(host);
const result = await tryConnectHost(host, password, clientId, timeout, nodeWebSocketFactory); const result = await tryConnectHost(host, password, clientId, timeout, nodeWebSocketFactory);
if ("client" in result) { if ("client" in result) {

View File

@@ -153,7 +153,7 @@ async function runSingleTest(testFile: string): Promise<TestOutcome> {
async function runRemainingTests(index: number): Promise<void> { async function runRemainingTests(index: number): Promise<void> {
if (index >= testFiles.length) return; if (index >= testFiles.length) return;
const testFile = testFiles[index] as string; const testFile = testFiles[index];
const outcome = await runSingleTest(testFile); const outcome = await runSingleTest(testFile);
if (outcome.status === "passed") { if (outcome.status === "passed") {
passed++; passed++;

View File

@@ -22,7 +22,7 @@ export interface StopOnQuitDeps {
} }
export function shouldStopDesktopManagedDaemonOnQuit(settings: QuitLifecycleSettings): boolean { export function shouldStopDesktopManagedDaemonOnQuit(settings: QuitLifecycleSettings): boolean {
return settings.daemon.keepRunningAfterQuit === false; return !settings.daemon.keepRunningAfterQuit;
} }
export async function stopDesktopManagedDaemonOnQuitIfNeeded( export async function stopDesktopManagedDaemonOnQuitIfNeeded(

View File

@@ -91,7 +91,7 @@ export function FlowTest() {
for (const result of data.results as { for (const result of data.results as {
alternatives: Array<RecognitionAlternative>; alternatives: Array<RecognitionAlternative>;
}[]) { }[]) {
for (const alternative of result.alternatives as Array<RecognitionAlternative>) { for (const alternative of result.alternatives) {
console.log(alternative.content); console.log(alternative.content);
} }
} }

View File

@@ -177,7 +177,7 @@ describe("Live relay (relay.paseo.sh) E2E", () => {
const plaintextFromDaemon = "hello-from-daemon"; const plaintextFromDaemon = "hello-from-daemon";
const ciphertextFromDaemon = await encrypt(daemonSharedKey, plaintextFromDaemon); const ciphertextFromDaemon = await encrypt(daemonSharedKey, plaintextFromDaemon);
daemonWs!.send(Buffer.from(ciphertextFromDaemon)); daemonWs.send(Buffer.from(ciphertextFromDaemon));
const clientReceivedCiphertext = await waitForOnceMessage( const clientReceivedCiphertext = await waitForOnceMessage(
clientWs, clientWs,

View File

@@ -138,7 +138,7 @@ export class DaemonClientRuntimeMetrics {
private pruneBuckets(now: number): void { private pruneBuckets(now: number): void {
const cutoff = now - this.windowMs; const cutoff = now - this.windowMs;
while (this.buckets.length > 0 && this.buckets[0]!.endedAt < cutoff) { while (this.buckets.length > 0 && this.buckets[0].endedAt < cutoff) {
this.buckets.shift(); this.buckets.shift();
} }
} }

View File

@@ -128,7 +128,7 @@ export function bindWsHandler(
} }
}; };
} }
const prop = `on${event}` as "onopen" | "onclose" | "onerror" | "onmessage"; const prop = `on${event}`;
const wsRecord = ws as unknown as Record<string, unknown>; const wsRecord = ws as unknown as Record<string, unknown>;
const previous = wsRecord[prop]; const previous = wsRecord[prop];
wsRecord[prop] = handler; wsRecord[prop] = handler;

View File

@@ -1124,7 +1124,7 @@ export class DaemonClient {
return this.subscribe(arg1); return this.subscribe(arg1);
} }
const type = arg1 as SessionOutboundMessage["type"]; const type = arg1;
const handler = arg2 as (message: SessionOutboundMessage) => void; const handler = arg2 as (message: SessionOutboundMessage) => void;
if (!this.messageHandlers.has(type)) { if (!this.messageHandlers.has(type)) {

View File

@@ -1833,7 +1833,7 @@ test("fetchTimeline returns a bounded reset window when cursor epoch is stale",
direction: "after", direction: "after",
cursor: { cursor: {
epoch: "stale-epoch", epoch: "stale-epoch",
seq: baseline.rows[baseline.rows.length - 1]!.seq, seq: baseline.rows[baseline.rows.length - 1].seq,
}, },
limit: 1, limit: 1,
}); });
@@ -2613,7 +2613,7 @@ test("replaceAgentRun does not emit idle or resolve waiters between interrupted
}, []); }, []);
expect(runningIndexes.length).toBeGreaterThanOrEqual(2); expect(runningIndexes.length).toBeGreaterThanOrEqual(2);
const firstReplacementRunningIndex = runningIndexes[1]!; const firstReplacementRunningIndex = runningIndexes[1];
expect(lifecycleUpdates.slice(0, firstReplacementRunningIndex).includes("idle")).toBe(false); expect(lifecycleUpdates.slice(0, firstReplacementRunningIndex).includes("idle")).toBe(false);
allowSecondRunToEnd.resolve(); allowSecondRunToEnd.resolve();
@@ -4766,11 +4766,10 @@ test("hydrateTimeline suppresses only matching canonical user_message messageId"
const timeline = manager.getTimeline(snapshot.id); const timeline = manager.getTimeline(snapshot.id);
const userMessages = timeline.filter((item) => item.type === "user_message"); const userMessages = timeline.filter((item) => item.type === "user_message");
expect(userMessages).toHaveLength(2); expect(userMessages).toHaveLength(2);
expect( expect(userMessages.map((item) => item.messageId)).toEqual([
userMessages.map( "msg_client_hello",
(item) => (item as Extract<AgentTimelineItem, { type: "user_message" }>).messageId, "msg_provider_distinct",
), ]);
).toEqual(["msg_client_hello", "msg_provider_distinct"]);
expect(userMessages.map((item) => item.text)).toEqual(["hello from user", "hello from user"]); expect(userMessages.map((item) => item.text)).toEqual(["hello from user", "hello from user"]);
}); });
@@ -4815,11 +4814,11 @@ test("recordUserMessage normalizes blank/whitespace messageId to undefined", asy
expect(userMessages).toHaveLength(3); expect(userMessages).toHaveLength(3);
// Empty string → undefined (not empty string) // Empty string → undefined (not empty string)
expect(userMessages[0]!.messageId).toBeUndefined(); expect(userMessages[0].messageId).toBeUndefined();
// Whitespace → undefined // Whitespace → undefined
expect(userMessages[1]!.messageId).toBeUndefined(); expect(userMessages[1].messageId).toBeUndefined();
// Valid → preserved // Valid → preserved
expect(userMessages[2]!.messageId).toBe("msg_valid_123"); expect(userMessages[2].messageId).toBe("msg_valid_123");
}); });
test("recordUserMessage preserves provided messageId in timeline item and dispatched event", async () => { test("recordUserMessage preserves provided messageId in timeline item and dispatched event", async () => {
@@ -4942,9 +4941,7 @@ test("live provider user_message echo is suppressed when recordUserMessage was c
// Should be exactly 1 (canonical), not 2 (canonical + provider echo) // Should be exactly 1 (canonical), not 2 (canonical + provider echo)
expect(userMessages).toHaveLength(1); expect(userMessages).toHaveLength(1);
// The canonical one must carry the client messageId for optimistic matching // The canonical one must carry the client messageId for optimistic matching
expect((userMessages[0] as Extract<AgentTimelineItem, { type: "user_message" }>).messageId).toBe( expect(userMessages[0].messageId).toBe("msg_client_echo_1");
"msg_client_echo_1",
);
// Assistant messages from the run should still appear // Assistant messages from the run should still appear
const assistantMessages = timeline.filter((item) => item.type === "assistant_message"); const assistantMessages = timeline.filter((item) => item.type === "assistant_message");
@@ -5135,9 +5132,7 @@ test("provider user_message is NOT suppressed when no prior recordUserMessage",
// Provider's user_message should be recorded (no canonical to dedup against) // Provider's user_message should be recorded (no canonical to dedup against)
expect(userMessages).toHaveLength(1); expect(userMessages).toHaveLength(1);
expect((userMessages[0] as Extract<AgentTimelineItem, { type: "user_message" }>).text).toBe( expect(userMessages[0].text).toBe("continuation prompt");
"continuation prompt",
);
}); });
test("replaceAgentRun succeeds when foreground turn terminal event is never delivered", async () => { test("replaceAgentRun succeeds when foreground turn terminal event is never delivered", async () => {

View File

@@ -1301,7 +1301,7 @@ export class AgentManager {
throw new Error(`Agent ${agentId} already has an active run`); throw new Error(`Agent ${agentId} already has an active run`);
} }
const agent = existingAgent as ActiveManagedAgent; const agent = existingAgent;
agent.pendingReplacement = false; agent.pendingReplacement = false;
agent.lastError = undefined; agent.lastError = undefined;
@@ -1361,7 +1361,7 @@ export class AgentManager {
} }
private finalizeForegroundTurn(agent: ActiveManagedAgent, turnId?: string): void { private finalizeForegroundTurn(agent: ActiveManagedAgent, turnId?: string): void {
const mutableAgent = agent as ActiveManagedAgent; const mutableAgent = agent;
if (turnId) { if (turnId) {
this.foregroundRuns.rememberFinalizedTurn(mutableAgent, turnId); this.foregroundRuns.rememberFinalizedTurn(mutableAgent, turnId);
} }
@@ -1430,7 +1430,7 @@ export class AgentManager {
} catch (error) { } catch (error) {
const latest = this.agents.get(agentId); const latest = this.agents.get(agentId);
if (latest) { if (latest) {
const latestActive = latest as ActiveManagedAgent; const latestActive = latest;
latestActive.pendingReplacement = false; latestActive.pendingReplacement = false;
if (!latestActive.activeForegroundTurnId && latestActive.lifecycle === "running") { if (!latestActive.activeForegroundTurnId && latestActive.lifecycle === "running") {
(latestActive as ActiveManagedAgent).lifecycle = "idle"; (latestActive as ActiveManagedAgent).lifecycle = "idle";
@@ -1502,7 +1502,7 @@ export class AgentManager {
if (options?.signal) { if (options?.signal) {
abortHandler = () => abortHandler = () =>
finishErr(createAbortError(options.signal!, "wait_for_agent_start aborted")); finishErr(createAbortError(options.signal, "wait_for_agent_start aborted"));
options.signal.addEventListener("abort", abortHandler, { once: true }); options.signal.addEventListener("abort", abortHandler, { once: true });
} }
@@ -2636,7 +2636,7 @@ export class AgentManager {
"handleStreamEvent: turn_canceled", "handleStreamEvent: turn_canceled",
); );
if (!isForegroundEvent && !agent.pendingReplacement) { if (!isForegroundEvent && !agent.pendingReplacement) {
(agent as ActiveManagedAgent).lifecycle = "idle"; agent.lifecycle = "idle";
} }
agent.lastError = undefined; agent.lastError = undefined;
this.resolvePendingPermissionsForAgent(agent, event.provider, options, "Interrupted"); this.resolvePendingPermissionsForAgent(agent, event.provider, options, "Interrupted");
@@ -2661,7 +2661,7 @@ export class AgentManager {
"handleStreamEvent: turn_started", "handleStreamEvent: turn_started",
); );
if (!isForegroundEvent) { if (!isForegroundEvent) {
(agent as ActiveManagedAgent).lifecycle = "running"; agent.lifecycle = "running";
this.emitState(agent); this.emitState(agent);
} }
} }

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { AGENT_LIFECYCLE_STATUSES, type AgentLifecycleStatus } from "./agent-manager.js"; import { AGENT_LIFECYCLE_STATUSES } from "./agent-manager.js";
import { toAgentPayload, toStoredAgentRecord, type ManagedAgent } from "./agent-projections.js"; import { toAgentPayload, toStoredAgentRecord, type ManagedAgent } from "./agent-projections.js";
import type { AgentSession } from "./agent-sdk-types.js"; import type { AgentSession } from "./agent-sdk-types.js";
import type { import type {
@@ -203,7 +203,7 @@ describe("toStoredAgentRecord", () => {
it("propagates lifecycle status for all states", () => { it("propagates lifecycle status for all states", () => {
for (const status of AGENT_LIFECYCLE_STATUSES) { for (const status of AGENT_LIFECYCLE_STATUSES) {
const agent = createManagedAgent({ lifecycle: status as AgentLifecycleStatus }); const agent = createManagedAgent({ lifecycle: status });
const record = toStoredAgentRecord(agent); const record = toStoredAgentRecord(agent);
expect(record.lastStatus).toBe(status); expect(record.lastStatus).toBe(status);
} }
@@ -301,7 +301,7 @@ describe("toAgentPayload", () => {
it("propagates lifecycle status for all states", () => { it("propagates lifecycle status for all states", () => {
for (const status of AGENT_LIFECYCLE_STATUSES) { for (const status of AGENT_LIFECYCLE_STATUSES) {
const agent = createManagedAgent({ lifecycle: status as AgentLifecycleStatus }); const agent = createManagedAgent({ lifecycle: status });
const payload = toAgentPayload(agent); const payload = toAgentPayload(agent);
expect(payload.status).toBe(status); expect(payload.status).toBe(status);
} }

View File

@@ -214,14 +214,14 @@ function tryParseJson(candidate: string): string | null {
} }
function extractBalancedJsonCandidate(source: string, start: number): string | null { function extractBalancedJsonCandidate(source: string, start: number): string | null {
const open = source[start]!; const open = source[start];
const close = open === "{" ? "}" : "]"; const close = open === "{" ? "}" : "]";
let depth = 0; let depth = 0;
let inString = false; let inString = false;
let escaped = false; let escaped = false;
for (let i = start; i < source.length; i += 1) { for (let i = start; i < source.length; i += 1) {
const ch = source[i]!; const ch = source[i];
if (inString) { if (inString) {
if (escaped) { if (escaped) {

View File

@@ -58,7 +58,7 @@ function fetchTail(ctx: FetchContext): AgentTimelineFetchResult {
staleCursor: false, staleCursor: false,
gap: false, gap: false,
window, window,
hasOlder: selected.length > 0 && selected[0]!.seq > minSeq, hasOlder: selected.length > 0 && selected[0].seq > minSeq,
hasNewer: false, hasNewer: false,
rows: selected.map(cloneRow), rows: selected.map(cloneRow),
}; };
@@ -93,7 +93,7 @@ function fetchAfter(ctx: FetchContext): AgentTimelineFetchResult {
staleCursor: false, staleCursor: false,
gap: false, gap: false,
window, window,
hasOlder: selected[0]!.seq > minSeq, hasOlder: selected[0].seq > minSeq,
hasNewer: Boolean(lastSelected && lastSelected.seq < maxSeq), hasNewer: Boolean(lastSelected && lastSelected.seq < maxSeq),
rows: selected.map(cloneRow), rows: selected.map(cloneRow),
}; };
@@ -115,7 +115,7 @@ function fetchBefore(ctx: FetchContext): AgentTimelineFetchResult {
staleCursor: false, staleCursor: false,
gap: false, gap: false,
window, window,
hasOlder: selected.length > 0 && selected[0]!.seq > minSeq, hasOlder: selected.length > 0 && selected[0].seq > minSeq,
hasNewer: endExclusive >= 0, hasNewer: endExclusive >= 0,
rows: selected.map(cloneRow), rows: selected.map(cloneRow),
}; };
@@ -137,7 +137,7 @@ function fetchReset(
staleCursor: flags.staleCursor, staleCursor: flags.staleCursor,
gap: flags.gap, gap: flags.gap,
window, window,
hasOlder: rows.length > 0 && rows[0]!.seq > minSeq, hasOlder: rows.length > 0 && rows[0].seq > minSeq,
hasNewer: false, hasNewer: false,
rows, rows,
}; };
@@ -155,7 +155,7 @@ export class InMemoryAgentTimelineStore {
const rows = options?.rows?.length const rows = options?.rows?.length
? options.rows.map(cloneRow) ? options.rows.map(cloneRow)
: this.buildRowsFromItems(options?.items ?? [], options?.nextSeq ?? 1, timestamp); : this.buildRowsFromItems(options?.items ?? [], options?.nextSeq ?? 1, timestamp);
const nextSeq = options?.nextSeq ?? (rows.length ? rows[rows.length - 1]!.seq + 1 : 1); const nextSeq = options?.nextSeq ?? (rows.length ? rows[rows.length - 1].seq + 1 : 1);
this.states.set(agentId, { this.states.set(agentId, {
epoch: options?.epoch ?? randomUUID(), epoch: options?.epoch ?? randomUUID(),
rows, rows,
@@ -188,8 +188,8 @@ export class InMemoryAgentTimelineStore {
? DEFAULT_TIMELINE_FETCH_LIMIT ? DEFAULT_TIMELINE_FETCH_LIMIT
: Math.max(0, Math.floor(requestedLimit)); : Math.max(0, Math.floor(requestedLimit));
const cursor = options?.cursor; const cursor = options?.cursor;
const minSeq = state.rows.length ? state.rows[0]!.seq : 0; const minSeq = state.rows.length ? state.rows[0].seq : 0;
const maxSeq = state.rows.length ? state.rows[state.rows.length - 1]!.seq : 0; const maxSeq = state.rows.length ? state.rows[state.rows.length - 1].seq : 0;
const selectAll = limit === 0; const selectAll = limit === 0;
const window = { const window = {
@@ -265,7 +265,7 @@ export class InMemoryAgentTimelineStore {
const rows = this.requireState(agentId).rows; const rows = this.requireState(agentId).rows;
const chunks: string[] = []; const chunks: string[] = [];
for (let i = rows.length - 1; i >= 0; i -= 1) { for (let i = rows.length - 1; i >= 0; i -= 1) {
const item = rows[i]!.item; const item = rows[i].item;
if (item.type !== "assistant_message") { if (item.type !== "assistant_message") {
if (chunks.length > 0) { if (chunks.length > 0) {
break; break;

View File

@@ -141,7 +141,7 @@ function createTestDeps(): TestDeps {
} }
function createProviderDefinition(overrides: Partial<ProviderDefinition>): ProviderDefinition { function createProviderDefinition(overrides: Partial<ProviderDefinition>): ProviderDefinition {
const provider = (overrides.id ?? "claude") as AgentProvider; const provider = overrides.id ?? "claude";
return { return {
id: provider, id: provider,
label: "Claude", label: "Claude",

View File

@@ -168,9 +168,7 @@ function resolveRegisteredProviderIds(
agentManager: AgentManager, agentManager: AgentManager,
providerRegistry: Record<AgentProvider, ProviderDefinition> | null | undefined, providerRegistry: Record<AgentProvider, ProviderDefinition> | null | undefined,
): AgentProvider[] { ): AgentProvider[] {
return providerRegistry return providerRegistry ? Object.keys(providerRegistry) : agentManager.getRegisteredProviderIds();
? (Object.keys(providerRegistry) as AgentProvider[])
: agentManager.getRegisteredProviderIds();
} }
interface ProviderSummary { interface ProviderSummary {
@@ -239,7 +237,7 @@ function resolveScheduleProviderAndModel(params: {
const providerInput = params.provider?.trim() || params.defaultProvider; const providerInput = params.provider?.trim() || params.defaultProvider;
const slashIndex = providerInput.indexOf("/"); const slashIndex = providerInput.indexOf("/");
if (slashIndex === -1) { if (slashIndex === -1) {
return { provider: providerInput as AgentProvider }; return { provider: providerInput };
} }
const provider = providerInput.slice(0, slashIndex).trim(); const provider = providerInput.slice(0, slashIndex).trim();
@@ -249,7 +247,7 @@ function resolveScheduleProviderAndModel(params: {
} }
return { return {
provider: provider as AgentProvider, provider: provider,
model, model,
}; };
} }
@@ -447,7 +445,7 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
const resolvedProviderModel = resolveScheduleProviderAndModel({ const resolvedProviderModel = resolveScheduleProviderAndModel({
provider: params?.provider, provider: params?.provider,
defaultProvider: params.provider as AgentProvider, defaultProvider: params.provider,
}); });
return { return {
type: "new-agent" as const, type: "new-agent" as const,

View File

@@ -86,7 +86,7 @@ export function resolveRequiredProviderModel(
} }
return { return {
provider: provider as AgentProvider, provider: provider,
model, model,
}; };
} }

View File

@@ -5,7 +5,7 @@ import { resolveAgentModel } from "./model-resolver.js";
vi.mock("./provider-registry.js", () => ({ vi.mock("./provider-registry.js", () => ({
buildProviderRegistry: vi.fn(), buildProviderRegistry: vi.fn(),
isProviderEnabled: vi.fn((definition: { enabled: boolean }) => definition.enabled === true), isProviderEnabled: vi.fn((definition: { enabled: boolean }) => definition.enabled),
})); }));
import { buildProviderRegistry } from "./provider-registry.js"; import { buildProviderRegistry } from "./provider-registry.js";

View File

@@ -51,8 +51,8 @@ export class Pcm16MonoResampler {
while (this.pos < maxPos) { while (this.pos < maxPos) {
const i = Math.floor(this.pos); const i = Math.floor(this.pos);
const frac = this.pos - i; const frac = this.pos - i;
const s0 = src[i]!; const s0 = src[i];
const s1 = src[i + 1]!; const s1 = src[i + 1];
const sample = s0 + (s1 - s0) * frac; const sample = s0 + (s1 - s0) * frac;
const clamped = Math.max(-1, Math.min(1, sample)); const clamped = Math.max(-1, Math.min(1, sample));
const int16 = Math.round(clamped * 32767); const int16 = Math.round(clamped * 32767);
@@ -61,7 +61,7 @@ export class Pcm16MonoResampler {
} }
// Keep the last input sample as carry for the next chunk. // Keep the last input sample as carry for the next chunk.
const lastInput = srcChunk[srcChunk.length - 1]!; const lastInput = srcChunk[srcChunk.length - 1];
this.carrySample = lastInput; this.carrySample = lastInput;
// Shift position so next chunk (which will include carry sample) continues smoothly. // Shift position so next chunk (which will include carry sample) continues smoothly.

View File

@@ -112,7 +112,7 @@ export async function resolveProviderCommandPrefix(
} }
return { return {
command: commandConfig.argv[0]!, command: commandConfig.argv[0],
args: commandConfig.argv.slice(1), args: commandConfig.argv.slice(1),
}; };
} }

View File

@@ -389,7 +389,7 @@ export class ProviderSnapshotManager {
} }
private getProviderIds(): AgentProvider[] { private getProviderIds(): AgentProvider[] {
return Object.keys(this.providerRegistry) as AgentProvider[]; return Object.keys(this.providerRegistry);
} }
private resolveRefreshProviders(providers?: AgentProvider[]): AgentProvider[] | undefined { private resolveRefreshProviders(providers?: AgentProvider[]): AgentProvider[] | undefined {

View File

@@ -317,7 +317,7 @@ test("Test 4: Autonomous run", async () => {
return; return;
} }
const autoTurnId = autoStarts[0]!.turnId; const autoTurnId = autoStarts[0].turnId;
expect(fgTurnId).not.toBe(autoTurnId); expect(fgTurnId).not.toBe(autoTurnId);
// Autonomous turn reaches terminal // Autonomous turn reaches terminal

View File

@@ -653,14 +653,14 @@ export class ACPAgentClient implements AgentClient {
{ logger: this.logger, provider: this.provider }, { logger: this.logger, provider: this.provider },
); );
const connection = new ClientSideConnection(() => this.buildProbeClient(), stream); const connection = new ClientSideConnection(() => this.buildProbeClient(), stream);
const initialize = (await Promise.race([ const initialize = await Promise.race([
connection.initialize({ connection.initialize({
protocolVersion: PROTOCOL_VERSION, protocolVersion: PROTOCOL_VERSION,
clientCapabilities: ACP_CLIENT_CAPABILITIES, clientCapabilities: ACP_CLIENT_CAPABILITIES,
clientInfo: { name: "Paseo", version: "dev" }, clientInfo: { name: "Paseo", version: "dev" },
}), }),
spawnErrorPromise, spawnErrorPromise,
])) as InitializeResponse; ]);
return { child, connection, initialize }; return { child, connection, initialize };
} }
@@ -2093,7 +2093,7 @@ function mergeToolSnapshot(
): ACPToolSnapshot { ): ACPToolSnapshot {
return { return {
toolCallId, toolCallId,
title: (update.title ?? previous?.title ?? toolCallId) as string, title: update.title ?? previous?.title ?? toolCallId,
kind: update.kind ?? previous?.kind ?? null, kind: update.kind ?? previous?.kind ?? null,
status: update.status ?? previous?.status ?? null, status: update.status ?? previous?.status ?? null,
content: coalesceDefined(update.content, previous?.content, null), content: coalesceDefined(update.content, previous?.content, null),

View File

@@ -1,7 +1,6 @@
import { beforeAll, beforeEach, describe, expect, test } from "vitest"; import { beforeAll, beforeEach, describe, expect, test } from "vitest";
import pino from "pino"; import pino from "pino";
import type { AgentSlashCommand } from "../agent-sdk-types.js";
import { isCommandAvailable } from "../../../utils/executable.js"; import { isCommandAvailable } from "../../../utils/executable.js";
import { ClaudeAgentClient } from "./claude-agent.js"; import { ClaudeAgentClient } from "./claude-agent.js";
@@ -40,7 +39,7 @@ describe("claude agent commands contract (real)", () => {
expect(commands.map((command) => command.name)).toContain("rewind"); expect(commands.map((command) => command.name)).toContain("rewind");
for (const command of commands) { for (const command of commands) {
const typed = command as AgentSlashCommand; const typed = command;
expect(typeof typed.name).toBe("string"); expect(typeof typed.name).toBe("string");
expect(typed.name.length).toBeGreaterThan(0); expect(typed.name.length).toBeGreaterThan(0);
expect(typed.name.startsWith("/")).toBe(false); expect(typed.name.startsWith("/")).toBe(false);

View File

@@ -295,7 +295,7 @@ function resolveClaudeSpawnCommand(
} }
return { return {
command: commandConfig.argv[0]!, command: commandConfig.argv[0],
args: [...commandConfig.argv.slice(1), ...spawnOptions.args], args: [...commandConfig.argv.slice(1), ...spawnOptions.args],
}; };
} }
@@ -459,7 +459,7 @@ function normalizeForDeterministicString(value: unknown, seen: WeakSet<object>):
return value.map((entry) => normalizeForDeterministicString(entry, seen)); return value.map((entry) => normalizeForDeterministicString(entry, seen));
} }
if (typeof value === "object") { if (typeof value === "object") {
const objectValue = value as object; const objectValue = value;
if (seen.has(objectValue)) { if (seen.has(objectValue)) {
return "[circular]"; return "[circular]";
} }
@@ -1340,7 +1340,7 @@ async function resolveClaudeVersion(
try { try {
if (command?.mode === "replace") { if (command?.mode === "replace") {
const { stdout } = await execCommand( const { stdout } = await execCommand(
command.argv[0]!, command.argv[0],
[...command.argv.slice(1), "--version"], [...command.argv.slice(1), "--version"],
{ ...envSpec, timeout: 5_000 }, { ...envSpec, timeout: 5_000 },
); );
@@ -1392,7 +1392,7 @@ async function resolveClaudeAuth(
try { try {
let result: { stdout: string; stderr: string }; let result: { stdout: string; stderr: string };
if (command?.mode === "replace") { if (command?.mode === "replace") {
result = await run(command.argv[0]!, [...command.argv.slice(1), "auth", "status"]); result = await run(command.argv[0], [...command.argv.slice(1), "auth", "status"]);
} else { } else {
const executable = await findExecutable("claude"); const executable = await findExecutable("claude");
if (!executable) { if (!executable) {

View File

@@ -19,7 +19,7 @@ describe("getClaudeModels", () => {
const models = getClaudeModels(); const models = getClaudeModels();
const defaults = models.filter((m) => m.isDefault); const defaults = models.filter((m) => m.isDefault);
expect(defaults).toHaveLength(1); expect(defaults).toHaveLength(1);
expect(defaults[0]!.id).toBe("claude-opus-4-6"); expect(defaults[0].id).toBe("claude-opus-4-6");
}); });
it("returns fresh copies each call", () => { it("returns fresh copies each call", () => {

View File

@@ -87,9 +87,9 @@ export function normalizeClaudeRuntimeModelId(value: string | null | undefined):
return null; return null;
} }
const family = runtimeMatch[1]!.toLowerCase(); const family = runtimeMatch[1].toLowerCase();
const major = runtimeMatch[2]!; const major = runtimeMatch[2];
const minor = runtimeMatch[3]!; const minor = runtimeMatch[3];
const suffix = runtimeMatch[4] ?? ""; const suffix = runtimeMatch[4] ?? "";
return `claude-${family}-${major}-${minor}${suffix}`; return `claude-${family}-${major}-${minor}${suffix}`;
} }

View File

@@ -289,14 +289,14 @@ function tokenizeCommandArgs(args: string): string[] {
let current = ""; let current = "";
let quote: "'" | '"' | null = null; let quote: "'" | '"' | null = null;
for (let i = 0; i < args.length; i += 1) { for (let i = 0; i < args.length; i += 1) {
const ch = args[i]!; const ch = args[i];
if (quote) { if (quote) {
if (ch === quote) { if (ch === quote) {
quote = null; quote = null;
continue; continue;
} }
if (ch === "\\" && i + 1 < args.length) { if (ch === "\\" && i + 1 < args.length) {
const next = args[i + 1]!; const next = args[i + 1];
if (next === quote || next === "\\" || next === "n" || next === "t") { if (next === quote || next === "\\" || next === "n" || next === "t") {
i += 1; i += 1;
current += decodeEscapedChar(next); current += decodeEscapedChar(next);

View File

@@ -130,7 +130,7 @@ function resolveModelProfile(modelId: string | null | undefined): {
durationMs: number; durationMs: number;
intervalMs: number; intervalMs: number;
} { } {
const model = MODELS.find((entry) => entry.id === modelId) ?? MODELS[0]!; const model = MODELS.find((entry) => entry.id === modelId) ?? MODELS[0];
const metadata = model.metadata ?? {}; const metadata = model.metadata ?? {};
return { return {
modelId: model.id, modelId: model.id,

View File

@@ -1,7 +1,6 @@
import { beforeAll, beforeEach, describe, expect, test } from "vitest"; import { beforeAll, beforeEach, describe, expect, test } from "vitest";
import pino from "pino"; import pino from "pino";
import type { AgentSlashCommand } from "../agent-sdk-types.js";
import { isCommandAvailable } from "../../../utils/executable.js"; import { isCommandAvailable } from "../../../utils/executable.js";
import { OpenCodeAgentClient } from "./opencode-agent.js"; import { OpenCodeAgentClient } from "./opencode-agent.js";
@@ -36,7 +35,7 @@ describe("opencode agent commands contract (real)", () => {
expect(commands.length).toBeGreaterThan(0); expect(commands.length).toBeGreaterThan(0);
for (const command of commands) { for (const command of commands) {
const typed = command as AgentSlashCommand; const typed = command;
expect(typeof typed.name).toBe("string"); expect(typeof typed.name).toBe("string");
expect(typed.name.length).toBeGreaterThan(0); expect(typed.name.length).toBeGreaterThan(0);
expect(typed.name.startsWith("/")).toBe(false); expect(typed.name.startsWith("/")).toBe(false);
@@ -63,7 +62,7 @@ describe("opencode agent commands contract (real)", () => {
expect(commands.length).toBeGreaterThan(0); expect(commands.length).toBeGreaterThan(0);
// Pick the first available command and send it without arguments. // Pick the first available command and send it without arguments.
const command = commands[0]!; const command = commands[0];
const events: Array<{ type: string }> = []; const events: Array<{ type: string }> = [];
session.subscribe((event) => events.push(event)); session.subscribe((event) => events.push(event));

View File

@@ -168,7 +168,7 @@ describe("opencode agent error handling (real)", () => {
const terminal = events.find(isTerminalEvent); const terminal = events.find(isTerminalEvent);
expect(terminal).toBeDefined(); expect(terminal).toBeDefined();
expect(terminal!.type).toBe("turn_failed"); expect(terminal!.type).toBe("turn_failed");
expect((terminal!.type === "turn_failed" ? terminal!.error : "").toLowerCase()).toMatch( expect((terminal!.type === "turn_failed" ? terminal.error : "").toLowerCase()).toMatch(
/insufficient balance|resource package|recharge/, /insufficient balance|resource package|recharge/,
); );
} finally { } finally {

View File

@@ -46,7 +46,7 @@ export async function* streamSession(
turnId = result.turnId; turnId = result.turnId;
for (let idx = queue.length - 1; idx >= 0; idx -= 1) { for (let idx = queue.length - 1; idx >= 0; idx -= 1) {
if (!matchesTurn(queue[idx]!)) { if (!matchesTurn(queue[idx])) {
queue.splice(idx, 1); queue.splice(idx, 1);
} }
} }

View File

@@ -703,7 +703,7 @@ export function toShellToolDetail(
export function toReadToolDetail( export function toReadToolDetail(
input: ParsedToolReadInput | null, input: ParsedToolReadInput | null,
output: ParsedToolReadOutput | ParsedToolReadOutputWithPath | null, output: ParsedToolReadOutput | null,
options?: { normalizePath?: NormalizePathFn }, options?: { normalizePath?: NormalizePathFn },
): ToolCallDetail | undefined { ): ToolCallDetail | undefined {
const filePath = normalizeDetailPath(input?.filePath ?? output?.filePath, options?.normalizePath); const filePath = normalizeDetailPath(input?.filePath ?? output?.filePath, options?.normalizePath);

View File

@@ -12,7 +12,7 @@ export function tokenizeToolName(name: string): string[] {
export function getToolLeafName(name: string): string | null { export function getToolLeafName(name: string): string | null {
const tokens = tokenizeToolName(name); const tokens = tokenizeToolName(name);
return tokens.length > 0 ? tokens[tokens.length - 1]! : null; return tokens.length > 0 ? tokens[tokens.length - 1] : null;
} }
export function isSpeakToolName(name: string): boolean { export function isSpeakToolName(name: string): boolean {
@@ -33,7 +33,7 @@ export function isLikelyNamespacedToolName(name: string): boolean {
if (segments.length >= 3) { if (segments.length >= 3) {
return true; return true;
} }
if (segments.length === 2 && segments[1]!.includes("_")) { if (segments.length === 2 && segments[1].includes("_")) {
return true; return true;
} }
return false; return false;
@@ -49,11 +49,11 @@ export function isPaseoToolName(name: string): boolean {
return ( return (
segments.length >= 3 && segments.length >= 3 &&
segments[0] === "mcp" && segments[0] === "mcp" &&
(segments[1] === "paseo" || segments[1]!.startsWith("paseo_")) (segments[1] === "paseo" || segments[1].startsWith("paseo_"))
); );
} }
if (normalized.includes(".")) { if (normalized.includes(".")) {
const firstSegment = normalized.split(".")[0]!; const firstSegment = normalized.split(".")[0];
return firstSegment === "paseo" || firstSegment.startsWith("paseo_"); return firstSegment === "paseo" || firstSegment.startsWith("paseo_");
} }
return false; return false;
@@ -66,14 +66,14 @@ export function getPaseoToolLeafName(name: string): string | null {
if ( if (
segments.length >= 3 && segments.length >= 3 &&
segments[0] === "mcp" && segments[0] === "mcp" &&
(segments[1] === "paseo" || segments[1]!.startsWith("paseo_")) (segments[1] === "paseo" || segments[1].startsWith("paseo_"))
) { ) {
return segments.slice(2).join("__"); return segments.slice(2).join("__");
} }
return null; return null;
} }
if (normalized.includes(".")) { if (normalized.includes(".")) {
const firstSegment = normalized.split(".")[0]!; const firstSegment = normalized.split(".")[0];
if (firstSegment === "paseo" || firstSegment.startsWith("paseo_")) { if (firstSegment === "paseo" || firstSegment.startsWith("paseo_")) {
return normalized.split(".").slice(1).join("."); return normalized.split(".").slice(1).join(".");
} }

View File

@@ -83,8 +83,8 @@ describe("TTSManager", () => {
expect(calls.length).toBeGreaterThan(1); expect(calls.length).toBeGreaterThan(1);
expect(calls.every((text) => text.length <= 260)).toBe(true); expect(calls.every((text) => text.length <= 260)).toBe(true);
expect(calls[0]!.length).toBeLessThanOrEqual(120); expect(calls[0].length).toBeLessThanOrEqual(120);
expect(calls.slice(1).some((text) => text.length > calls[0]!.length)).toBe(true); expect(calls.slice(1).some((text) => text.length > calls[0].length)).toBe(true);
}); });
it("prefetches the next segment before current playback completes", async () => { it("prefetches the next segment before current playback completes", async () => {

View File

@@ -181,7 +181,7 @@ export class TTSManager {
const scheduleNextSegments = () => { const scheduleNextSegments = () => {
while (nextSegmentToSchedule < segments.length && inflight.size < TTS_PREFETCH_SEGMENTS) { while (nextSegmentToSchedule < segments.length && inflight.size < TTS_PREFETCH_SEGMENTS) {
const segment = segments[nextSegmentToSchedule]!; const segment = segments[nextSegmentToSchedule];
inflight.set(segment.index, this.scheduleSegmentSynthesis(segment, abortSignal)); inflight.set(segment.index, this.scheduleSegmentSynthesis(segment, abortSignal));
nextSegmentToSchedule += 1; nextSegmentToSchedule += 1;
} }

View File

@@ -44,7 +44,7 @@ function pickTwoDistinctModels(models: Array<{ id: string }>): [string, string]
if (ids.length < 2) { if (ids.length < 2) {
throw new Error(`Need at least 2 models to test switching; got ${ids.length}`); throw new Error(`Need at least 2 models to test switching; got ${ids.length}`);
} }
return [ids[0]!, ids[1]!]; return [ids[0], ids[1]];
} }
function isBinaryInstalled(binary: string): boolean { function isBinaryInstalled(binary: string): boolean {
@@ -127,7 +127,7 @@ test("live thinking switching works for Claude (off -> on)", async () => {
if (!modelList.models || modelList.models.length === 0) { if (!modelList.models || modelList.models.length === 0) {
throw new Error("No Claude models returned"); throw new Error("No Claude models returned");
} }
const modelId = modelList.models[0]!.id; const modelId = modelList.models[0].id;
const agent = await ctx.client.createAgent({ const agent = await ctx.client.createAgent({
provider: "claude", provider: "claude",

View File

@@ -20,7 +20,7 @@ function pickOpenCodeModel(
const preferred = models.find((model) => const preferred = models.find((model) =>
preferences.some((fragment) => model.id.includes(fragment)), preferences.some((fragment) => model.id.includes(fragment)),
); );
return preferred?.id ?? models[0]!.id; return preferred?.id ?? models[0].id;
} }
async function createHarness(): Promise<{ async function createHarness(): Promise<{

View File

@@ -36,7 +36,7 @@ function pickOpenCodeModel(
const preferred = models.find((model) => const preferred = models.find((model) =>
preferences.some((fragment) => model.id.includes(fragment)), preferences.some((fragment) => model.id.includes(fragment)),
); );
return preferred?.id ?? models[0]!.id; return preferred?.id ?? models[0].id;
} }
function hasRunningBashToolCall(messages: SessionOutboundMessage[], agentId: string): boolean { function hasRunningBashToolCall(messages: SessionOutboundMessage[], agentId: string): boolean {

View File

@@ -46,7 +46,7 @@ describe("daemon E2E - permission flow: Claude", () => {
const permissionState = await ctx.client.waitForFinish(agent.id, 5_000); const permissionState = await ctx.client.waitForFinish(agent.id, 5_000);
expect(permissionState.status).toBe("permission"); expect(permissionState.status).toBe("permission");
expect(permissionState.final?.pendingPermissions?.length).toBeGreaterThan(0); expect(permissionState.final?.pendingPermissions?.length).toBeGreaterThan(0);
const permission = permissionState.final!.pendingPermissions[0]!; const permission = permissionState.final!.pendingPermissions[0];
await ctx.client.respondToPermission(agent.id, permission.id, { behavior: "allow" }); await ctx.client.respondToPermission(agent.id, permission.id, { behavior: "allow" });
@@ -93,7 +93,7 @@ describe("daemon E2E - permission flow: Claude", () => {
const permissionState = await ctx.client.waitForFinish(agent.id, 5_000); const permissionState = await ctx.client.waitForFinish(agent.id, 5_000);
expect(permissionState.status).toBe("permission"); expect(permissionState.status).toBe("permission");
expect(permissionState.final?.pendingPermissions?.length).toBeGreaterThan(0); expect(permissionState.final?.pendingPermissions?.length).toBeGreaterThan(0);
const permission = permissionState.final!.pendingPermissions[0]!; const permission = permissionState.final!.pendingPermissions[0];
await ctx.client.respondToPermission(agent.id, permission.id, { await ctx.client.respondToPermission(agent.id, permission.id, {
behavior: "deny", behavior: "deny",

Some files were not shown because too many files have changed in this diff Show More