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
22 changes: 22 additions & 0 deletions apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,28 @@ limitations under the License.

================================================================================

Package: @ai-sdk/open-responses@2.0.28
Declared license: Apache-2.0
Selected license: Apache-2.0
Repository: https://github.com/vercel/ai#packages/open-responses

--- LICENSE ---
Copyright 2023 Vercel, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

================================================================================

Package: @ai-sdk/openai@4.0.42
Declared license: Apache-2.0
Selected license: Apache-2.0
Expand Down
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions packages/cli/THIRD_PARTY_NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,28 @@ limitations under the License.

================================================================================

Package: @ai-sdk/open-responses@2.0.28
Declared license: Apache-2.0
Selected license: Apache-2.0
Repository: https://github.com/vercel/ai#packages/open-responses

--- LICENSE ---
Copyright 2023 Vercel, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

================================================================================

Package: @ai-sdk/openai@4.0.42
Declared license: Apache-2.0
Selected license: Apache-2.0
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/__tests__/model-web-search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ describe('hosted web search capability', () => {
it('enables the implemented Responses path only for declared model families', () => {
assert.deepEqual(resolveHostedWebSearchCapability('deepseek', undefined, 'deepseek-v4-flash'), {
adapter: 'openai-responses',
implemented: true,
implemented: false,
});
assert.deepEqual(resolveHostedWebSearchCapability('deepseek', undefined, 'deepseek-v4-pro'), {
adapter: 'openai-responses',
implemented: true,
implemented: false,
});
assert.equal(resolveHostedWebSearchCapability('deepseek', undefined, 'deepseek-chat'), null);
assert.deepEqual(resolveHostedWebSearchCapability('openai', undefined, 'gpt-5.5'), {
Expand Down Expand Up @@ -101,7 +101,7 @@ describe('hosted web search capability', () => {
it('keeps dual-wire providers on the configured connection protocol', () => {
assert.deepEqual(resolveHostedWebSearchCapability('deepseek', undefined, 'deepseek-v4-flash'), {
adapter: 'openai-responses',
implemented: true,
implemented: false,
});
assert.deepEqual(
resolveHostedWebSearchCapability(
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/llm-connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
type ProviderCategory,
type ProviderDefaults,
type ProviderRuntimeAdapter,
type ProviderResponsesContract,
type ProviderType,
} from './provider-registry.js';

Expand All @@ -44,6 +45,7 @@ export type {
ProviderCategory,
ProviderDefaults,
ProviderRuntimeAdapter,
ProviderResponsesContract,
ProviderType,
};

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/model-web-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ function providerHostedWebSearchAdapter(
): HostedWebSearchCapability | null {
switch (providerType) {
case 'deepseek':
// @ai-sdk/open-responses currently serializes function tools only.
// Mark native search unavailable so routing never hands it a provider
// tool that would be silently filtered from the request.
return { adapter: 'openai-responses', implemented: false };
case 'openai':
case 'openai-responses-compatible':
case 'xai':
Expand Down
19 changes: 15 additions & 4 deletions packages/core/src/provider-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ export type ProviderCatalogGroup = 'recommended' | 'plans' | 'api' | 'aggregator

export type ApplyPatchProtocol = 'openai-structured' | 'codex-v4a-freeform';

export type ProviderResponsesContract =
| {
readonly adapter: 'openai';
readonly reasoningReplay: 'encrypted-content';
}
| {
readonly adapter: 'open-responses';
readonly reasoningReplay: 'plaintext-content';
};

type ProviderRuntimeAdapterDefinition =
| { kind: 'anthropic'; auth: 'api-key' | 'bearer'; normalizeBaseUrl: boolean }
| { kind: 'claude-subscription' }
Expand All @@ -30,7 +40,8 @@ type ProviderRuntimeAdapterDefinition =
name: 'provider' | 'connection';
includeUsage?: boolean;
requireBaseUrl?: boolean;
supportsOpenAiResponses?: true;
/** Presence enables Responses and fixes the only supported SDK/replay pairing. */
responses?: ProviderResponsesContract;
replayAssistantReasoningAs?: 'reasoning';
replayAssistantReasoningDetails?: true;
};
Expand Down Expand Up @@ -836,8 +847,8 @@ const providerRegistry = {
runtimeAdapter: {
kind: 'openai-compatible',
name: 'provider',
supportsOpenAiResponses: true,
applyPatchProtocol: 'codex-v4a-freeform',
responses: { adapter: 'open-responses', reasoningReplay: 'plaintext-content' },
},
modelDiscovery: { kind: 'protocol' },
category: 'domestic',
Expand Down Expand Up @@ -975,7 +986,7 @@ const providerRegistry = {
runtimeAdapter: {
kind: 'openai-compatible',
name: 'provider',
supportsOpenAiResponses: true,
responses: { adapter: 'openai', reasoningReplay: 'encrypted-content' },
},
modelDiscovery: { kind: 'protocol' },
category: 'overseas',
Expand All @@ -998,7 +1009,7 @@ const providerRegistry = {
runtimeAdapter: {
kind: 'openai-compatible',
name: 'provider',
supportsOpenAiResponses: true,
responses: { adapter: 'openai', reasoningReplay: 'encrypted-content' },
},
modelDiscovery: {
kind: 'protocol',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const MAX_IMPLEMENTATION_CHILD_REQUESTS =
const HEADLESS_CODING_V1_PROMPT_HASH =
'sha256:0e3389e330b8b8f0db1c7a8b8e2126325fe4c672d6eff279afcd3f9412e52271';
const HEADLESS_CODING_V1_TOOLS_HASH =
'sha256:ea1f293096e5e209ae49346f46b0e8ff9b54ae17452a5a23149ad7233afaeafc';
'sha256:c062194603f93b568da5ca59b865b316156b5f218ba854c291aa9582859b3de4';
const execFileAsync = promisify(execFile);

test('backend creation aborts a stalled canonical connection read', async () => {
Expand Down Expand Up @@ -886,10 +886,11 @@ test('hosted execution freezes the headless coding provider wire contract', asyn
assert.deepEqual(responsesToolNames(request?.body), [
'ArchiveRead',
'Bash',
'Edit',
'Glob',
'Grep',
'Read',
'apply_patch',
'Write',
]);
const bash = (tools as Array<Record<string, unknown>>).find((tool) => tool.name === 'Bash');
assert.ok(bash);
Expand Down Expand Up @@ -1956,6 +1957,83 @@ test('production Host publishes and retires an implementation child patch', asyn
}
});

test('Host auxiliary calls preserve resolved DeepSeek reasoning settings', async () => {
const base = await mkdtemp(join(tmpdir(), 'maka-host-deepseek-auxiliary-'));
const provider = await startProvider();
const capability = await resolveStorageRoot({
path: join(base, 'interactive'),
kind: 'interactive',
});
const owner = await tryAcquireInteractiveRootOwner(capability);
assert.ok(owner);
if (!owner) return;

try {
const policy = await openInteractiveRuntimePolicyStoresForWrite(owner.lease);
const usage = await openInteractiveUsageStoresForWrite(owner.lease);
const execution = await openInteractiveExecutionStoresForWrite(owner.lease);
const created = await policy.connectionCatalog.create({
expectedCatalogRevision: 0,
connection: {
slug: 'deepseek-auxiliary',
name: 'DeepSeek auxiliary',
providerType: 'deepseek',
baseUrl: provider.baseUrl,
enabled: true,
enabledModelIds: ['deepseek-v4-flash'],
},
});
assert.equal(created.kind, 'committed');
if (created.kind !== 'committed') return;
const connection = created.snapshot.connections[0];
assert.ok(connection);
if (!connection) return;
const credential = await policy.credentialVault.set({
locator: {
scope: 'connection',
connectionId: connection.connectionId,
kind: 'api_key',
},
expected: null,
secret: API_KEY,
});
assert.equal(credential.kind, 'committed');
await publishConnectionModel(policy, connection.connectionId, 'deepseek-v4-flash');
const session = await execution.sessionStore.create({
cwd: capability.canonicalPath,
backend: 'ai-sdk',
llmConnectionSlug: 'deepseek-auxiliary',
model: 'deepseek-v4-flash',
thinkingLevel: 'high',
permissionMode: 'ask',
});
const effects = createHostSessionEffectModel({
runtimePolicy: policy,
oauthCredentials: new HostOAuthExecutionAuthority(policy),
claudeDeviceId: capability.rootId,
usage,
requestDrain: () => assert.fail('Auxiliary telemetry must not drain the Host'),
newId: () => 'deepseek-title-call',
});

await effects.generateTitle({
sessionId: session.id,
header: session,
sourceText: 'Explain the DeepSeek auxiliary reasoning seam',
abortSignal: new AbortController().signal,
});
const request = provider.requests.at(-1);
assert.ok(request);
assert.equal(request.url, '/v1/responses');
assert.equal(request.authorization, `Bearer ${API_KEY}`);
assert.deepEqual(request.body.reasoning, { effort: 'high' });
} finally {
await owner.close();
await provider.close();
await rm(base, { recursive: true, force: true });
}
});

test('Host auxiliary models meter provider usage and abort physical requests', {
timeout: 20_000,
}, async () => {
Expand Down Expand Up @@ -3180,6 +3258,7 @@ function responsesToolNames(body: Record<string, unknown> | undefined): string[]
}

function responsesDeveloperPrompt(body: Record<string, unknown> | undefined): string | undefined {
if (typeof body?.instructions === 'string') return body.instructions;
const input = Array.isArray(body?.input) ? body.input : [];
const developer = input.find(
(message): message is Record<string, unknown> =>
Expand Down
13 changes: 8 additions & 5 deletions packages/runtime-host/src/server/execution-model-authority.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ type AuxiliaryModelRequest =
readonly maxOutputTokens: number;
readonly maxRetries?: number;
readonly system?: string;
readonly providerOptions?: Record<string, unknown>;
readonly tools?: never;
})
| {
Expand Down Expand Up @@ -484,6 +485,11 @@ async function runHostAuxiliaryModelCall(
| Awaited<ReturnType<typeof generateProviderPrefixModelCall>>;
try {
result = await readDuringBackendCreation(() => {
const providerOptions = buildProviderOptions(
target.connection,
target.model,
input.header.thinkingLevel,
);
const model = getAIModel({
connection: target.connection,
apiKey,
Expand All @@ -499,16 +505,13 @@ async function runHostAuxiliaryModelCall(
? 'omit'
: 'none',
abortSignal: input.abortSignal,
providerOptions: request.providerOptions ?? providerOptions,
})
: generateToolFreeModelCall({
model,
...request,
abortSignal: input.abortSignal,
providerOptions: buildProviderOptions(
target.connection,
target.model,
input.header.thinkingLevel,
),
providerOptions: request.providerOptions ?? providerOptions,
});
}, input.abortSignal);
const oauthFailure = readDeferredOAuthFailure?.();
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
"@ai-sdk/anthropic": "4.0.39",
"@ai-sdk/cohere": "4.0.27",
"@ai-sdk/google": "4.0.44",
"@ai-sdk/open-responses": "2.0.28",
"@ai-sdk/openai": "4.0.42",
"@ai-sdk/openai-compatible": "3.0.30",
"@openai/agents-core": "0.14.3",
Expand Down
Loading
Loading