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",
"options": {
"typeAware": false
},
"plugins": ["react", "react-perf", "unicorn", "typescript", "oxc", "import", "promise"],
"categories": {
"correctness": "error",
@@ -53,6 +56,7 @@
"typescript/no-explicit-any": "error",
"typescript/prefer-as-const": "error",
"typescript/no-this-alias": "error",
"typescript/no-unnecessary-type-assertion": "error",
"typescript/consistent-type-definitions": ["error", "interface"],
"import/no-unassigned-import": [

103
package-lock.json generated
View File

@@ -30,6 +30,7 @@
"lefthook": "^2.1.6",
"oxfmt": "0.46.0",
"oxlint": "1.61.0",
"oxlint-tsgolint": "^0.22.1",
"patch-package": "^8.0.1",
"playwright": "^1.56.1",
"typescript": "^5.9.3",
@@ -10376,6 +10377,90 @@
"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": {
"version": "1.61.0",
"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": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -461,7 +461,7 @@ export function BrowserPane({
};
const handleFaviconUpdated = (event: Event) => {
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 });
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -351,7 +351,7 @@ describe("sidebar workspace render isolation", () => {
act(() => {
useSessionStore.getState().mergeWorkspaces(SERVER_ID, [
{
...createWorkspaces()[1]!,
...createWorkspaces()[1],
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;
else if (workspaceKind === "worktree") KindIcon = ThemedFolderGit2;
else KindIcon = null;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,7 +35,7 @@ const TEST_CLAUDE_DEFINITION: AgentProviderDefinition = {
function makeProviderMap(
...definitions: 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);

View File

@@ -196,8 +196,8 @@ function resolveProvider(input: {
if (initialValues?.provider && allowedProviderMap.has(initialValues.provider)) {
return initialValues.provider;
}
if (preferences?.provider && allowedProviderMap.has(preferences.provider as AgentProvider)) {
return preferences.provider as AgentProvider;
if (preferences?.provider && allowedProviderMap.has(preferences.provider)) {
return preferences.provider;
}
if (currentProvider && allowedProviderMap.size > 0 && !allowedProviderMap.has(currentProvider)) {
return null;
@@ -396,7 +396,7 @@ function buildProviderDefinitionMapForStatuses(args: {
const matchingProviders = new Set(
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),
);

View File

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

View File

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

View File

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

View File

@@ -260,7 +260,7 @@ export function useKeyboardShortcuts({
const handleCommandCenterToggle = (event: KeyboardEvent): boolean => {
const store = useKeyboardShortcutsStore.getState();
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 =
target?.closest?.("textarea, input, [contenteditable='true']") ??
(target instanceof HTMLElement ? target : null);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -68,7 +68,7 @@ describe("computeWorkspaceTabLayout", () => {
expect(result.closeButtonPolicy).toBe("all");
expect(result.requiresHorizontalScrollFallback).toBe(false);
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", () => {
@@ -81,7 +81,7 @@ describe("computeWorkspaceTabLayout", () => {
expect(result.closeButtonPolicy).toBe("all");
expect(result.requiresHorizontalScrollFallback).toBe(true);
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", () => {

View File

@@ -23,7 +23,7 @@ describe("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([

View File

@@ -526,7 +526,7 @@ function findNearestSiblingPaneId(root: SplitNodeInternal, paneId: string): stri
for (let depth = path.length - 1; depth >= 0; depth -= 1) {
const parentPath = path.slice(0, depth);
const childIndex = path[depth]!;
const childIndex = path[depth];
const parentNode = getNodeAtPath(root, parentPath);
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 removeIndex = path[path.length - 1]!;
const removeIndex = path[path.length - 1];
const parentNode = getNodeAtPath(root, parentPath);
invariant(parentNode.kind === "group", "Expected parent group while removing pane");
@@ -676,7 +676,7 @@ function removePaneByPath(root: SplitNodeInternal, path: number[]): SplitNodeInt
const nextParentNode =
nextParentChildren.length === 1
? nextParentChildren[0]!
? nextParentChildren[0]
: createGroupNode({
id: parentNode.group.id,
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 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.children).toHaveLength(2);
@@ -259,7 +259,7 @@ describe("workspace-layout-store actions", () => {
kind: "file",
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(secondTabId).toBe("file_/repo/worktree/b.ts");
@@ -280,7 +280,7 @@ describe("workspace-layout-store actions", () => {
kind: "setup",
workspaceId: "ws-main",
});
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const pane = findPaneById(layout.root, "main")!;
expect(agentTabId).toBe("agent_agent-1");
@@ -306,7 +306,7 @@ describe("workspace-layout-store actions", () => {
kind: "file",
path: "/repo/worktree/a.ts",
});
const layoutAfter = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layoutAfter = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const pane = findPaneById(layoutAfter.root, "main")!;
expect(duplicateTabId).toBe(firstTabId);
@@ -380,7 +380,7 @@ describe("workspace-layout-store actions", () => {
const firstTabId = store.openTabFocused(workspaceKey, { kind: "draft", draftId: "draft-1" });
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(secondTabId).toBe("draft-2");
@@ -416,14 +416,14 @@ describe("workspace-layout-store actions", () => {
kind: "draft",
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(draftTabId).toBe("draft-split");
expect(layout.focusedPaneId).toBe(newPaneId);
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!)?.focusedTabId).toBe(draftTabId);
expect(findPaneById(layout.root, newPaneId)?.tabIds).toEqual([draftTabId!]);
expect(findPaneById(layout.root, newPaneId)?.focusedTabId).toBe(draftTabId);
});
it("focusTab moves workspace focus to the pane containing the tab", () => {
@@ -448,14 +448,14 @@ describe("workspace-layout-store actions", () => {
});
store.focusTab(workspaceKey, fileTabId!);
let layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
let layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(layout.focusedPaneId).toBe("main");
store.focusTab(workspaceKey, terminalTabId!);
layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
expect(splitPaneId).toBe("pane_bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
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", () => {
@@ -474,8 +474,8 @@ describe("workspace-layout-store actions", () => {
});
const nextTabId = store.convertDraftToAgent(workspaceKey, secondTabId!, "agent-1");
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const splitPane = findPaneById(layout.root, splitPaneId!);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const splitPane = findPaneById(layout.root, splitPaneId);
const convertedTab = collectAllTabs(layout.root).find((tab) => tab.tabId === nextTabId);
expect(splitPaneId).toBe("pane_12121212-1212-1212-1212-121212121212");
@@ -501,7 +501,7 @@ describe("workspace-layout-store actions", () => {
kind: "file",
path: "/repo/worktree/retargeted.ts",
});
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(draftTabId).toBe("draft-retarget");
expect(nextTabId).toBe(draftTabId);
@@ -541,7 +541,7 @@ describe("workspace-layout-store actions", () => {
kind: "file",
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(draftTabId).toBe("draft-dup");
@@ -576,7 +576,7 @@ describe("workspace-layout-store actions", () => {
kind: "agent",
agentId: "agent-1",
});
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(firstAgentTabId).toBe(firstDraftTabId);
expect(nextTabId).toBe(firstDraftTabId);
@@ -608,7 +608,7 @@ describe("workspace-layout-store actions", () => {
});
store.reorderTabs(workspaceKey, [thirdTabId!, firstTabId!]);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(findPaneById(layout.root, "main")).toEqual({
id: "main",
@@ -663,11 +663,11 @@ describe("workspace-layout-store actions", () => {
store.moveTabToPane(workspaceKey, fourthTabId!, splitPaneId!);
store.focusPane(workspaceKey, "main");
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(layout.focusedPaneId).toBe("main");
expect(findPaneById(layout.root, splitPaneId!)).toEqual({
expect(findPaneById(layout.root, splitPaneId)).toEqual({
id: splitPaneId,
tabIds: [fourthTabId!, thirdTabId!],
focusedTabId: fourthTabId,
@@ -705,7 +705,7 @@ describe("workspace-layout-store actions", () => {
});
store.focusPane(workspaceKey, "main");
let layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
let layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(layout.focusedPaneId).toBe("main");
store.focusPane(workspaceKey, splitPaneId!);
@@ -734,7 +734,7 @@ describe("workspace-layout-store actions", () => {
});
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(layout.focusedPaneId).toBe("main");
@@ -782,7 +782,7 @@ describe("workspace-layout-store actions", () => {
position: "bottom",
});
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(pane1).toBe("pane_11111111-1111-1111-1111-111111111111");
expect(pane2).toBe("pane_33333333-3333-3333-3333-333333333333");
expect(pane3).toBe("pane_55555555-5555-5555-5555-555555555555");
@@ -812,11 +812,11 @@ describe("workspace-layout-store actions", () => {
});
store.moveTabToPane(workspaceKey, leftTabId!, splitPaneId!);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(layout.focusedPaneId).toBe(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/a.ts",
]);
@@ -852,7 +852,7 @@ describe("workspace-layout-store actions", () => {
});
store.closeTab(workspaceKey, secondTabId!);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const rootGroup = expectGroup(layout.root);
expect(paneBId).toBe("pane_78787878-7878-7878-7878-787878787878");
@@ -910,7 +910,7 @@ describe("workspace-layout-store actions", () => {
kind: "file",
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(duplicateTabId).toBe(secondTabId);
@@ -946,14 +946,14 @@ describe("workspace-layout-store actions", () => {
position: "right",
});
const splitRoot = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!.root;
const splitRoot = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey].root;
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]);
const resizedRoot = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!.root;
const resizedRoot = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey].root;
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);
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" });
store.closeTab(workspaceKey, tabId!);
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
expect(layout).toEqual(createDefaultLayout());
});
@@ -1065,7 +1065,7 @@ describe("workspace-layout-store actions", () => {
});
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(nextTabId).toBe("agent_agent-1");
@@ -1129,7 +1129,7 @@ describe("workspace-layout-store actions", () => {
hasActivePendingDraftCreate: false,
});
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey]!;
const layout = useWorkspaceLayoutStore.getState().layoutByWorkspace[workspaceKey];
const tabs = collectAllTabs(layout.root);
expect(tabs.map((tab) => tab.tabId)).toEqual([
@@ -1189,7 +1189,7 @@ describe("workspace-layout-store actions", () => {
});
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(findPaneById(layout.root, layout.focusedPaneId)?.focusedTabId).toBe(scriptTabId);
});

View File

@@ -134,9 +134,8 @@ export function upsertHostConnectionInProfiles(input: {
return [...existing, profile];
}
const matchedProfiles = matchingIndexes.map((index) => existing[index]!);
const prev =
matchedProfiles.find((daemon) => daemon.serverId === serverId) ?? matchedProfiles[0]!;
const matchedProfiles = matchingIndexes.map((index) => existing[index]);
const prev = matchedProfiles.find((daemon) => daemon.serverId === serverId) ?? matchedProfiles[0];
const nextConnections = dedupeHostConnections([
...matchedProfiles.flatMap((daemon) => daemon.connections),
input.connection,
@@ -180,7 +179,7 @@ export function upsertHostConnectionInProfiles(input: {
updatedAt: now,
};
const firstIndex = matchingIndexes[0]!;
const firstIndex = matchingIndexes[0];
const matchingIndexSet = new Set(matchingIndexes);
const next = existing.filter((_daemon, index) => !matchingIndexSet.has(index));
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 { derivePendingPermissionKey, normalizeAgentSnapshot } from "@/utils/agent-snapshots";
import { resolveProjectPlacement } from "@/utils/project-placement";
type AgentDirectoryFetchEntry = FetchAgentsEntry | FetchAgentHistoryEntry;
type AgentDirectoryFetchEntry = FetchAgentsEntry;
interface PendingPermissionEntry {
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 { AgentPermissionRequest } from "@server/server/agent/agent-sdk-types";
@@ -31,7 +30,7 @@ export function normalizeAgentSnapshot(snapshot: AgentSnapshotPayload, serverId:
serverId,
id: snapshot.id,
provider: snapshot.provider,
status: snapshot.status as AgentLifecycleStatus,
status: snapshot.status,
createdAt,
updatedAt,
lastUserMessageAt,

View File

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

View File

@@ -132,7 +132,7 @@ function dispatchWebNotificationClick(detail: WebNotificationClickDetail): boole
cancelable: true,
},
);
return dispatch(event) === false;
return !dispatch(event);
}
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) {
return 0;
}
const lo = pcm[i]!;
const hi = pcm[i + 1]!;
const lo = pcm[i];
const hi = pcm[i + 1];
let value = (hi << 8) | lo;
if (value & 0x8000) {
value = value - 0x10000;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ export async function runPostCommand(
]);
return {
type: "single",
data: message!,
data: message,
schema: chatMessageSchema,
};
} 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"))
.option("--home <path>", "Paseo home directory (default: ~/.paseo)")
.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",
};
}
const probe = probes[index] as (typeof probes)[number];
const probe = probes[index];
const result = await runProcessProbe(probe.command, probe.args);
if (result.resolved) {
const resolved = probe.parseValue ? probe.parseValue(result.resolved) : result.resolved;

View File

@@ -111,7 +111,7 @@ export async function runAllowCommand(
permissionsToAllow = pendingPermissions;
} else {
// 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) {
await client.close();
const error: CommandError = {

View File

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

View File

@@ -137,7 +137,7 @@ export function renderTable<T>(result: AnyCommandResult<T>, options: OutputOptio
return "";
}
const columns = schema.columns as ColumnDef<T>[];
const columns = schema.columns;
const includeHeaders = !options.noHeaders;
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;
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 result = await tryConnectHost(host, password, clientId, timeout, nodeWebSocketFactory);
if ("client" in result) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -138,7 +138,7 @@ export class DaemonClientRuntimeMetrics {
private pruneBuckets(now: number): void {
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();
}
}

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 previous = wsRecord[prop];
wsRecord[prop] = handler;

View File

@@ -1124,7 +1124,7 @@ export class DaemonClient {
return this.subscribe(arg1);
}
const type = arg1 as SessionOutboundMessage["type"];
const type = arg1;
const handler = arg2 as (message: SessionOutboundMessage) => void;
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",
cursor: {
epoch: "stale-epoch",
seq: baseline.rows[baseline.rows.length - 1]!.seq,
seq: baseline.rows[baseline.rows.length - 1].seq,
},
limit: 1,
});
@@ -2613,7 +2613,7 @@ test("replaceAgentRun does not emit idle or resolve waiters between interrupted
}, []);
expect(runningIndexes.length).toBeGreaterThanOrEqual(2);
const firstReplacementRunningIndex = runningIndexes[1]!;
const firstReplacementRunningIndex = runningIndexes[1];
expect(lifecycleUpdates.slice(0, firstReplacementRunningIndex).includes("idle")).toBe(false);
allowSecondRunToEnd.resolve();
@@ -4766,11 +4766,10 @@ test("hydrateTimeline suppresses only matching canonical user_message messageId"
const timeline = manager.getTimeline(snapshot.id);
const userMessages = timeline.filter((item) => item.type === "user_message");
expect(userMessages).toHaveLength(2);
expect(
userMessages.map(
(item) => (item as Extract<AgentTimelineItem, { type: "user_message" }>).messageId,
),
).toEqual(["msg_client_hello", "msg_provider_distinct"]);
expect(userMessages.map((item) => item.messageId)).toEqual([
"msg_client_hello",
"msg_provider_distinct",
]);
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);
// Empty string → undefined (not empty string)
expect(userMessages[0]!.messageId).toBeUndefined();
expect(userMessages[0].messageId).toBeUndefined();
// Whitespace → undefined
expect(userMessages[1]!.messageId).toBeUndefined();
expect(userMessages[1].messageId).toBeUndefined();
// 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 () => {
@@ -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)
expect(userMessages).toHaveLength(1);
// The canonical one must carry the client messageId for optimistic matching
expect((userMessages[0] as Extract<AgentTimelineItem, { type: "user_message" }>).messageId).toBe(
"msg_client_echo_1",
);
expect(userMessages[0].messageId).toBe("msg_client_echo_1");
// Assistant messages from the run should still appear
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)
expect(userMessages).toHaveLength(1);
expect((userMessages[0] as Extract<AgentTimelineItem, { type: "user_message" }>).text).toBe(
"continuation prompt",
);
expect(userMessages[0].text).toBe("continuation prompt");
});
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`);
}
const agent = existingAgent as ActiveManagedAgent;
const agent = existingAgent;
agent.pendingReplacement = false;
agent.lastError = undefined;
@@ -1361,7 +1361,7 @@ export class AgentManager {
}
private finalizeForegroundTurn(agent: ActiveManagedAgent, turnId?: string): void {
const mutableAgent = agent as ActiveManagedAgent;
const mutableAgent = agent;
if (turnId) {
this.foregroundRuns.rememberFinalizedTurn(mutableAgent, turnId);
}
@@ -1430,7 +1430,7 @@ export class AgentManager {
} catch (error) {
const latest = this.agents.get(agentId);
if (latest) {
const latestActive = latest as ActiveManagedAgent;
const latestActive = latest;
latestActive.pendingReplacement = false;
if (!latestActive.activeForegroundTurnId && latestActive.lifecycle === "running") {
(latestActive as ActiveManagedAgent).lifecycle = "idle";
@@ -1502,7 +1502,7 @@ export class AgentManager {
if (options?.signal) {
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 });
}
@@ -2636,7 +2636,7 @@ export class AgentManager {
"handleStreamEvent: turn_canceled",
);
if (!isForegroundEvent && !agent.pendingReplacement) {
(agent as ActiveManagedAgent).lifecycle = "idle";
agent.lifecycle = "idle";
}
agent.lastError = undefined;
this.resolvePendingPermissionsForAgent(agent, event.provider, options, "Interrupted");
@@ -2661,7 +2661,7 @@ export class AgentManager {
"handleStreamEvent: turn_started",
);
if (!isForegroundEvent) {
(agent as ActiveManagedAgent).lifecycle = "running";
agent.lifecycle = "running";
this.emitState(agent);
}
}

View File

@@ -1,6 +1,6 @@
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 type { AgentSession } from "./agent-sdk-types.js";
import type {
@@ -203,7 +203,7 @@ describe("toStoredAgentRecord", () => {
it("propagates lifecycle status for all states", () => {
for (const status of AGENT_LIFECYCLE_STATUSES) {
const agent = createManagedAgent({ lifecycle: status as AgentLifecycleStatus });
const agent = createManagedAgent({ lifecycle: status });
const record = toStoredAgentRecord(agent);
expect(record.lastStatus).toBe(status);
}
@@ -301,7 +301,7 @@ describe("toAgentPayload", () => {
it("propagates lifecycle status for all states", () => {
for (const status of AGENT_LIFECYCLE_STATUSES) {
const agent = createManagedAgent({ lifecycle: status as AgentLifecycleStatus });
const agent = createManagedAgent({ lifecycle: status });
const payload = toAgentPayload(agent);
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 {
const open = source[start]!;
const open = source[start];
const close = open === "{" ? "}" : "]";
let depth = 0;
let inString = false;
let escaped = false;
for (let i = start; i < source.length; i += 1) {
const ch = source[i]!;
const ch = source[i];
if (inString) {
if (escaped) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import { resolveAgentModel } from "./model-resolver.js";
vi.mock("./provider-registry.js", () => ({
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";

View File

@@ -51,8 +51,8 @@ export class Pcm16MonoResampler {
while (this.pos < maxPos) {
const i = Math.floor(this.pos);
const frac = this.pos - i;
const s0 = src[i]!;
const s1 = src[i + 1]!;
const s0 = src[i];
const s1 = src[i + 1];
const sample = s0 + (s1 - s0) * frac;
const clamped = Math.max(-1, Math.min(1, sample));
const int16 = Math.round(clamped * 32767);
@@ -61,7 +61,7 @@ export class Pcm16MonoResampler {
}
// 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;
// Shift position so next chunk (which will include carry sample) continues smoothly.

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
import pino from "pino";
import type { AgentSlashCommand } from "../agent-sdk-types.js";
import { isCommandAvailable } from "../../../utils/executable.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");
for (const command of commands) {
const typed = command as AgentSlashCommand;
const typed = command;
expect(typeof typed.name).toBe("string");
expect(typed.name.length).toBeGreaterThan(0);
expect(typed.name.startsWith("/")).toBe(false);

View File

@@ -295,7 +295,7 @@ function resolveClaudeSpawnCommand(
}
return {
command: commandConfig.argv[0]!,
command: commandConfig.argv[0],
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));
}
if (typeof value === "object") {
const objectValue = value as object;
const objectValue = value;
if (seen.has(objectValue)) {
return "[circular]";
}
@@ -1340,7 +1340,7 @@ async function resolveClaudeVersion(
try {
if (command?.mode === "replace") {
const { stdout } = await execCommand(
command.argv[0]!,
command.argv[0],
[...command.argv.slice(1), "--version"],
{ ...envSpec, timeout: 5_000 },
);
@@ -1392,7 +1392,7 @@ async function resolveClaudeAuth(
try {
let result: { stdout: string; stderr: string };
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 {
const executable = await findExecutable("claude");
if (!executable) {

View File

@@ -19,7 +19,7 @@ describe("getClaudeModels", () => {
const models = getClaudeModels();
const defaults = models.filter((m) => m.isDefault);
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", () => {

View File

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

View File

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

View File

@@ -130,7 +130,7 @@ function resolveModelProfile(modelId: string | null | undefined): {
durationMs: 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 ?? {};
return {
modelId: model.id,

View File

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

View File

@@ -168,7 +168,7 @@ describe("opencode agent error handling (real)", () => {
const terminal = events.find(isTerminalEvent);
expect(terminal).toBeDefined();
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/,
);
} finally {

View File

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

View File

@@ -703,7 +703,7 @@ export function toShellToolDetail(
export function toReadToolDetail(
input: ParsedToolReadInput | null,
output: ParsedToolReadOutput | ParsedToolReadOutputWithPath | null,
output: ParsedToolReadOutput | null,
options?: { normalizePath?: NormalizePathFn },
): ToolCallDetail | undefined {
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 {
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 {
@@ -33,7 +33,7 @@ export function isLikelyNamespacedToolName(name: string): boolean {
if (segments.length >= 3) {
return true;
}
if (segments.length === 2 && segments[1]!.includes("_")) {
if (segments.length === 2 && segments[1].includes("_")) {
return true;
}
return false;
@@ -49,11 +49,11 @@ export function isPaseoToolName(name: string): boolean {
return (
segments.length >= 3 &&
segments[0] === "mcp" &&
(segments[1] === "paseo" || segments[1]!.startsWith("paseo_"))
(segments[1] === "paseo" || segments[1].startsWith("paseo_"))
);
}
if (normalized.includes(".")) {
const firstSegment = normalized.split(".")[0]!;
const firstSegment = normalized.split(".")[0];
return firstSegment === "paseo" || firstSegment.startsWith("paseo_");
}
return false;
@@ -66,14 +66,14 @@ export function getPaseoToolLeafName(name: string): string | null {
if (
segments.length >= 3 &&
segments[0] === "mcp" &&
(segments[1] === "paseo" || segments[1]!.startsWith("paseo_"))
(segments[1] === "paseo" || segments[1].startsWith("paseo_"))
) {
return segments.slice(2).join("__");
}
return null;
}
if (normalized.includes(".")) {
const firstSegment = normalized.split(".")[0]!;
const firstSegment = normalized.split(".")[0];
if (firstSegment === "paseo" || firstSegment.startsWith("paseo_")) {
return normalized.split(".").slice(1).join(".");
}

View File

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

View File

@@ -181,7 +181,7 @@ export class TTSManager {
const scheduleNextSegments = () => {
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));
nextSegmentToSchedule += 1;
}

View File

@@ -44,7 +44,7 @@ function pickTwoDistinctModels(models: Array<{ id: string }>): [string, string]
if (ids.length < 2) {
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 {
@@ -127,7 +127,7 @@ test("live thinking switching works for Claude (off -> on)", async () => {
if (!modelList.models || modelList.models.length === 0) {
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({
provider: "claude",

View File

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

View File

@@ -36,7 +36,7 @@ function pickOpenCodeModel(
const preferred = models.find((model) =>
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 {

View File

@@ -46,7 +46,7 @@ describe("daemon E2E - permission flow: Claude", () => {
const permissionState = await ctx.client.waitForFinish(agent.id, 5_000);
expect(permissionState.status).toBe("permission");
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" });
@@ -93,7 +93,7 @@ describe("daemon E2E - permission flow: Claude", () => {
const permissionState = await ctx.client.waitForFinish(agent.id, 5_000);
expect(permissionState.status).toBe("permission");
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: "deny",

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