Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions assistant/src/__tests__/config-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('AssistantConfigSchema', () => {
expect(result.model).toBe('claude-opus-4-6');
expect(result.maxTokens).toBe(16000);
expect(result.apiKeys).toEqual({});
expect(result.thinking).toEqual({ enabled: false, budgetTokens: 10000 });
expect(result.thinking).toEqual({ enabled: false, budgetTokens: 10000, streamThinking: false });
expect(result.contextWindow).toEqual({
enabled: true,
maxInputTokens: 180000,
Expand Down Expand Up @@ -1194,7 +1194,7 @@ describe('loadConfig with schema validation', () => {
expect(config.provider).toBe('anthropic');
expect(config.model).toBe('claude-opus-4-6');
expect(config.maxTokens).toBe(16000);
expect(config.thinking).toEqual({ enabled: false, budgetTokens: 10000 });
expect(config.thinking).toEqual({ enabled: false, budgetTokens: 10000, streamThinking: false });
expect(config.contextWindow).toEqual({
enabled: true,
maxInputTokens: 180000,
Expand Down
8 changes: 4 additions & 4 deletions assistant/src/__tests__/provider-error-scenarios.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ mock.module('../util/logger.js', () => ({
}));

// Only mock sleep so retries complete instantly; keep real retry logic
mock.module('../util/retry.js', () => {
const real = require(retryModulePath);
mock.module('../util/retry.js', async () => {
const real = await import(retryModulePath);
return {
...real,
sleep: () => Promise.resolve(),
Expand Down Expand Up @@ -231,7 +231,7 @@ describe('RetryProvider — network error retries', () => {
const inner = makeFlaky(1, err);
const provider = new RetryProvider(inner);

const result = await provider.sendMessage(MESSAGES);
const _result = await provider.sendMessage(MESSAGES);
expect(inner.calls).toBe(2);
});

Expand Down Expand Up @@ -386,7 +386,7 @@ describe('FailoverProvider — model unavailability fallback', () => {
const secondary = makeProvider('secondary');
const provider = new FailoverProvider([primary, secondary]);

const result = await provider.sendMessage(MESSAGES);
const _result = await provider.sendMessage(MESSAGES);
expect(primary.calls).toBe(1);
expect(secondary.calls).toBe(1);
});
Expand Down
6 changes: 3 additions & 3 deletions assistant/src/__tests__/session-agent-loop.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, mock, test, beforeEach } from 'bun:test';
import type { Message, ContentBlock } from '../providers/types.js';
import type { AgentEvent, CheckpointDecision } from '../agent/loop.js';
import type { AgentEvent, CheckpointDecision, CheckpointInfo } from '../agent/loop.js';
import type { ServerMessage } from '../daemon/ipc-protocol.js';

// ── Module mocks (must precede imports of the module under test) ─────
Expand Down Expand Up @@ -212,7 +212,7 @@ type AgentLoopRun = (
onEvent: (event: AgentEvent) => void,
signal?: AbortSignal,
requestId?: string,
onCheckpoint?: () => CheckpointDecision,
onCheckpoint?: (checkpoint: CheckpointInfo) => CheckpointDecision,
) => Promise<Message[]>;

function makeCtx(overrides?: Partial<AgentLoopSessionContext> & { agentLoopRun?: AgentLoopRun }): AgentLoopSessionContext {
Expand Down Expand Up @@ -811,7 +811,7 @@ describe('session-agent-loop', () => {
test('drains queue after completion', async () => {
let drainReason: string | undefined;
const ctx = makeCtx({
agentLoopRun: async (messages, onEvent) => {
agentLoopRun: async (messages: Message[], onEvent: (event: AgentEvent) => void) => {
onEvent({
type: 'message_complete',
message: { role: 'assistant', content: [{ type: 'text', text: 'ok' }] },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
enqueueMessage: () => ({ queued: false, requestId: 'r' }),
getQueueDepth: () => 0,
processMessage: async () => '',
withSurface: async <T>(_id: string, fn: () => T | Promise<T>) => fn(),
memoryPolicy: { scopeId: 'default', strictSideEffects: false },
...overrides,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
enqueueMessage: () => ({ queued: false, requestId: 'r' }),
getQueueDepth: () => 0,
processMessage: async () => '',
withSurface: async <T>(_id: string, fn: () => T | Promise<T>) => fn(),
memoryPolicy: { scopeId: 'default', strictSideEffects: false },
...overrides,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
enqueueMessage: () => ({ queued: false, requestId: 'r' }),
getQueueDepth: () => 0,
processMessage: async () => '',
withSurface: async <T>(_id: string, fn: () => T | Promise<T>) => fn(),
memoryPolicy: { scopeId: 'default', strictSideEffects: false },
...overrides,
};
Expand Down
28 changes: 16 additions & 12 deletions assistant/src/__tests__/terminal-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ describe('Shell tool input validation', () => {

const baseContext = {
workingDir: testTmpDir,
sessionId: 'test-session-1',
conversationId: 'test-conv-1',
onOutput: () => {},
};
Expand Down Expand Up @@ -702,13 +703,14 @@ describe('Shell tool input validation', () => {

test('tool definition includes required schema fields', () => {
const def = shellTool.getDefinition();
const schema = def.input_schema as { required: string[]; properties: Record<string, unknown> };
expect(def.name).toBe('bash');
expect(def.input_schema.required).toContain('command');
expect(def.input_schema.required).toContain('reason');
expect(def.input_schema.properties.command).toBeDefined();
expect(def.input_schema.properties.timeout_seconds).toBeDefined();
expect(def.input_schema.properties.network_mode).toBeDefined();
expect(def.input_schema.properties.credential_ids).toBeDefined();
expect(schema.required).toContain('command');
expect(schema.required).toContain('reason');
expect(schema.properties.command).toBeDefined();
expect(schema.properties.timeout_seconds).toBeDefined();
expect(schema.properties.network_mode).toBeDefined();
expect(schema.properties.credential_ids).toBeDefined();
});
});

Expand Down Expand Up @@ -786,6 +788,7 @@ describe('EvaluateTypescriptTool input validation', () => {

const baseContext = {
workingDir: testTmpDir,
sessionId: 'test-session-1',
conversationId: 'test-conv-1',
onOutput: () => {},
};
Expand Down Expand Up @@ -831,12 +834,13 @@ describe('EvaluateTypescriptTool input validation', () => {

test('tool definition has correct name and schema', () => {
const def = evalTool.getDefinition();
const schema = def.input_schema as { required: string[]; properties: Record<string, unknown> };
expect(def.name).toBe('evaluate_typescript_code');
expect(def.input_schema.required).toContain('code');
expect(def.input_schema.properties.code).toBeDefined();
expect(def.input_schema.properties.mock_input_json).toBeDefined();
expect(def.input_schema.properties.timeout_seconds).toBeDefined();
expect(def.input_schema.properties.filename).toBeDefined();
expect(def.input_schema.properties.entrypoint).toBeDefined();
expect(schema.required).toContain('code');
expect(schema.properties.code).toBeDefined();
expect(schema.properties.mock_input_json).toBeDefined();
expect(schema.properties.timeout_seconds).toBeDefined();
expect(schema.properties.filename).toBeDefined();
expect(schema.properties.entrypoint).toBeDefined();
});
});
46 changes: 23 additions & 23 deletions assistant/src/__tests__/web-search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('WebSearchTool', () => {
globalThis.fetch = (async () => {
fetchCalled = true;
return new Response('{}', { status: 200 });
}) as typeof fetch;
}) as unknown as typeof fetch;

process.env.BRAVE_API_KEY = 'test-key';

Expand Down Expand Up @@ -73,7 +73,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
}) as unknown as typeof fetch;

await executeWebSearch({ query: 'test', count: 50 }, 'test-key', 'brave');
expect(capturedUrl).toContain('count=20');
Expand All @@ -90,7 +90,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
}) as unknown as typeof fetch;

await executeWebSearch({ query: 'test', offset: 20 }, 'test-key', 'brave');
expect(capturedUrl).toContain('offset=9');
Expand All @@ -104,7 +104,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
}) as unknown as typeof fetch;

await executeWebSearch({ query: 'test', freshness: 'pw' }, 'test-key', 'brave');
expect(capturedUrl).toContain('freshness=pw');
Expand All @@ -118,7 +118,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
}) as unknown as typeof fetch;

await executeWebSearch({ query: 'test', freshness: 'invalid' }, 'test-key', 'brave');
expect(capturedUrl).not.toContain('freshness');
Expand All @@ -141,7 +141,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'test-key', 'brave');
expect(result.isError).toBe(false);
Expand All @@ -158,7 +158,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'noresults' }, 'test-key', 'brave');
expect(result.isError).toBe(false);
Expand All @@ -171,7 +171,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'empty' }, 'test-key', 'brave');
expect(result.isError).toBe(false);
Expand All @@ -181,7 +181,7 @@ describe('WebSearchTool', () => {
test('handles 401 unauthorized', async () => {
globalThis.fetch = (async () =>
new Response('Unauthorized', { status: 401 })
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'bad-key', 'brave');
expect(result.isError).toBe(true);
Expand All @@ -199,7 +199,7 @@ describe('WebSearchTool', () => {
JSON.stringify({ web: { results: [{ title: 'Result', url: 'https://example.com', description: 'Found it' }] } }),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
);
}) as typeof fetch;
}) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'test-key', 'brave');
expect(result.isError).toBe(false);
Expand All @@ -210,7 +210,7 @@ describe('WebSearchTool', () => {
test('returns error after exhausting 429 retries', async () => {
globalThis.fetch = (async () =>
new Response('Too Many Requests', { status: 429 })
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'test-key', 'brave');
expect(result.isError).toBe(true);
Expand All @@ -234,7 +234,7 @@ describe('WebSearchTool', () => {
JSON.stringify({ web: { results: [] } }),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
);
}) as typeof fetch;
}) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'test-key', 'brave');
expect(result.isError).toBe(false);
Expand All @@ -244,7 +244,7 @@ describe('WebSearchTool', () => {
test('handles network errors', async () => {
globalThis.fetch = (async () => {
throw new Error('Network unreachable');
}) as typeof fetch;
}) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'test-key', 'brave');
expect(result.isError).toBe(true);
Expand All @@ -261,7 +261,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
}) as unknown as typeof fetch;

await executeWebSearch({ query: 'test' }, 'my-api-key', 'brave');
expect(capturedHeaders['X-Subscription-Token']).toBe('my-api-key');
Expand All @@ -287,7 +287,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'test-key', 'brave');
expect(result.isError).toBe(false);
Expand All @@ -308,7 +308,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'pplx-key', 'perplexity');
expect(result.isError).toBe(false);
Expand All @@ -327,7 +327,7 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'noresults' }, 'pplx-key', 'perplexity');
expect(result.isError).toBe(false);
Expand All @@ -337,7 +337,7 @@ describe('WebSearchTool', () => {
test('handles 401 unauthorized', async () => {
globalThis.fetch = (async () =>
new Response('Unauthorized', { status: 401 })
) as typeof fetch;
) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'bad-key', 'perplexity');
expect(result.isError).toBe(true);
Expand All @@ -346,7 +346,7 @@ describe('WebSearchTool', () => {

test('sends correct headers', async () => {
let capturedHeaders: Record<string, string> = {};
let capturedBody: Record<string, unknown>;
let capturedBody: Record<string, unknown> = {};
globalThis.fetch = (async (_url: string, init: RequestInit) => {
capturedHeaders = Object.fromEntries(
Object.entries(init.headers as Record<string, string>),
Expand All @@ -356,13 +356,13 @@ describe('WebSearchTool', () => {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
}) as unknown as typeof fetch;

await executeWebSearch({ query: 'test query' }, 'pplx-my-key', 'perplexity');
expect(capturedHeaders['Authorization']).toBe('Bearer pplx-my-key');
expect(capturedHeaders['Content-Type']).toBe('application/json');
expect(capturedBody.model).toBe('sonar');
expect(capturedBody.messages[0].content).toBe('test query');
expect((capturedBody.messages as Array<{ content: string }>)[0].content).toBe('test query');
});

test('retries on 429 and succeeds', async () => {
Expand All @@ -376,7 +376,7 @@ describe('WebSearchTool', () => {
JSON.stringify({ choices: [{ message: { content: 'Found it' } }] }),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
);
}) as typeof fetch;
}) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'pplx-key', 'perplexity');
expect(result.isError).toBe(false);
Expand All @@ -387,7 +387,7 @@ describe('WebSearchTool', () => {
test('handles network errors', async () => {
globalThis.fetch = (async () => {
throw new Error('Network unreachable');
}) as typeof fetch;
}) as unknown as typeof fetch;

const result = await executeWebSearch({ query: 'test' }, 'pplx-key', 'perplexity');
expect(result.isError).toBe(true);
Expand Down
8 changes: 4 additions & 4 deletions assistant/src/amazon/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
} from './session.js';
import type { ExtractedCredential } from '../tools/browser/network-recording-types.js';
import { extensionRelayServer } from '../browser-extension-relay/server.js';
import type { ExtensionResponse } from '../browser-extension-relay/protocol.js';
import type { ExtensionCommand, ExtensionResponse } from '../browser-extension-relay/protocol.js';
import { readHttpToken } from '../util/platform.js';
import { getRuntimeHttpPort } from '../config/env.js';

Expand All @@ -72,7 +72,7 @@ async function sendRelayCommand(command: Record<string, unknown>): Promise<Exten
// Try in-process relay first (works when running inside the daemon)
const status = extensionRelayServer.getStatus();
if (status.connected) {
return extensionRelayServer.sendCommand(command as any);
return extensionRelayServer.sendCommand(command as Omit<ExtensionCommand, 'id'>);
}

// Fall back to HTTP relay endpoint on the daemon
Expand Down Expand Up @@ -163,7 +163,7 @@ async function findAmazonTab(): Promise<number> {
let lastCookieSyncTime = 0;
const COOKIE_SYNC_INTERVAL = 60_000; // re-sync at most once per minute

async function syncCookiesToBrowser(cookies: ExtractedCredential[]): Promise<void> {
async function _syncCookiesToBrowser(cookies: ExtractedCredential[]): Promise<void> {
const now = Date.now();
if (now - lastCookieSyncTime < COOKIE_SYNC_INTERVAL) return;

Expand Down Expand Up @@ -212,7 +212,7 @@ async function cdpEval(tabId: number, script: string): Promise<unknown> {
}

const value = resp.result;
if (value === undefined || value === null) {
if (value == null) {
throw new Error('Empty browser eval response');
}

Expand Down
Loading