fix(acp): improve acp-next first-session startup (#29709)
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
const enabled = process.env.OPENCODE_ACP_PROFILE === "1"
|
||||||
|
const started = performance.now()
|
||||||
|
|
||||||
|
export function mark(name: string, fields?: Record<string, string | number | boolean | undefined>) {
|
||||||
|
if (!enabled) return
|
||||||
|
write(`${name}.mark`, performance.now() - started, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function duration(
|
||||||
|
name: string,
|
||||||
|
startedAt: number,
|
||||||
|
fields?: Record<string, string | number | boolean | undefined>,
|
||||||
|
) {
|
||||||
|
if (!enabled) return
|
||||||
|
write(name, performance.now() - startedAt, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function measure<T>(
|
||||||
|
name: string,
|
||||||
|
fn: () => Promise<T>,
|
||||||
|
fields?: Record<string, string | number | boolean | undefined>,
|
||||||
|
) {
|
||||||
|
if (!enabled) return fn()
|
||||||
|
const start = performance.now()
|
||||||
|
try {
|
||||||
|
return await fn()
|
||||||
|
} finally {
|
||||||
|
write(name, performance.now() - start, fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function write(name: string, durationMs: number, fields?: Record<string, string | number | boolean | undefined>) {
|
||||||
|
const extra = fields
|
||||||
|
? Object.entries(fields)
|
||||||
|
.filter((entry): entry is [string, string | number | boolean] => entry[1] !== undefined)
|
||||||
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
|
.join(" ")
|
||||||
|
: ""
|
||||||
|
console.error(`[acp-profile] ${name} ${Math.round(durationMs)}ms${extra ? ` ${extra}` : ""}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export * as ACPNextProfile from "./profile"
|
||||||
@@ -40,6 +40,7 @@ import { Directory } from "./directory"
|
|||||||
import { ACPNextEvent } from "./event"
|
import { ACPNextEvent } from "./event"
|
||||||
import { ACPNextSession } from "./session"
|
import { ACPNextSession } from "./session"
|
||||||
import { UsageService } from "./usage"
|
import { UsageService } from "./usage"
|
||||||
|
import { ACPNextProfile } from "./profile"
|
||||||
import { ModelID, ProviderID } from "@/provider/schema"
|
import { ModelID, ProviderID } from "@/provider/schema"
|
||||||
import { Provider } from "@/provider/provider"
|
import { Provider } from "@/provider/provider"
|
||||||
import type { Command } from "@/command"
|
import type { Command } from "@/command"
|
||||||
@@ -88,6 +89,7 @@ export function make(input: {
|
|||||||
if (events) input.eventSubscription?.(events)
|
if (events) input.eventSubscription?.(events)
|
||||||
|
|
||||||
const initialize = Effect.fn("ACPNext.initialize")(function* (params: InitializeRequest) {
|
const initialize = Effect.fn("ACPNext.initialize")(function* (params: InitializeRequest) {
|
||||||
|
const started = performance.now()
|
||||||
const authMethod: AuthMethod = {
|
const authMethod: AuthMethod = {
|
||||||
description: "Run `opencode auth login` in the terminal",
|
description: "Run `opencode auth login` in the terminal",
|
||||||
name: "Login with opencode",
|
name: "Login with opencode",
|
||||||
@@ -104,7 +106,7 @@ export function make(input: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const response = {
|
||||||
protocolVersion: 1,
|
protocolVersion: 1,
|
||||||
agentCapabilities: {
|
agentCapabilities: {
|
||||||
loadSession: true,
|
loadSession: true,
|
||||||
@@ -129,6 +131,8 @@ export function make(input: {
|
|||||||
version: InstallationVersion,
|
version: InstallationVersion,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
ACPNextProfile.duration("acp.initialize", started)
|
||||||
|
return response
|
||||||
})
|
})
|
||||||
|
|
||||||
const authenticate = Effect.fn("ACPNext.authenticate")(function* (params: AuthenticateRequest) {
|
const authenticate = Effect.fn("ACPNext.authenticate")(function* (params: AuthenticateRequest) {
|
||||||
@@ -139,15 +143,20 @@ export function make(input: {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const directorySnapshot = Effect.fn("ACPNext.directorySnapshot")(function* (cwd: string) {
|
const directorySnapshot = Effect.fn("ACPNext.directorySnapshot")(function* (cwd: string) {
|
||||||
return yield* directoryService.get(cwd)
|
const started = performance.now()
|
||||||
|
const snapshot = yield* directoryService.get(cwd)
|
||||||
|
ACPNextProfile.duration("acp.directory.snapshot", started)
|
||||||
|
return snapshot
|
||||||
})
|
})
|
||||||
|
|
||||||
const newSession = Effect.fn("ACPNext.newSession")(function* (params: NewSessionRequest) {
|
const newSession = Effect.fn("ACPNext.newSession")(function* (params: NewSessionRequest) {
|
||||||
|
const started = performance.now()
|
||||||
const snapshot = yield* directorySnapshot(params.cwd)
|
const snapshot = yield* directorySnapshot(params.cwd)
|
||||||
const selected = selectDefaultModel(snapshot)
|
const selected = selectDefaultModel(snapshot)
|
||||||
const variant = selectVariant(snapshot, selected)
|
const variant = selectVariant(snapshot, selected)
|
||||||
const modeId = snapshot.availableModes.length > 0 ? snapshot.defaultModeID : undefined
|
const modeId = snapshot.availableModes.length > 0 ? snapshot.defaultModeID : undefined
|
||||||
const created = yield* request(
|
const created = yield* profiledRequest(
|
||||||
|
"acp.newSession.session.create",
|
||||||
() =>
|
() =>
|
||||||
input.sdk.session.create(
|
input.sdk.session.create(
|
||||||
{
|
{
|
||||||
@@ -175,7 +184,7 @@ export function make(input: {
|
|||||||
yield* registerMcpServers(input.sdk, registeredMcp, params.cwd, state.id, params.mcpServers)
|
yield* registerMcpServers(input.sdk, registeredMcp, params.cwd, state.id, params.mcpServers)
|
||||||
yield* sendAvailableCommands(input.connection, state.id, snapshot)
|
yield* sendAvailableCommands(input.connection, state.id, snapshot)
|
||||||
|
|
||||||
return {
|
const response = {
|
||||||
sessionId: state.id,
|
sessionId: state.id,
|
||||||
configOptions: configOptions(snapshot, {
|
configOptions: configOptions(snapshot, {
|
||||||
model: state.model ?? selected,
|
model: state.model ?? selected,
|
||||||
@@ -183,6 +192,8 @@ export function make(input: {
|
|||||||
modeId: state.modeId,
|
modeId: state.modeId,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
ACPNextProfile.duration("acp.newSession", started)
|
||||||
|
return response
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadSession = Effect.fn("ACPNext.loadSession")(function* (params: LoadSessionRequest) {
|
const loadSession = Effect.fn("ACPNext.loadSession")(function* (params: LoadSessionRequest) {
|
||||||
@@ -684,66 +695,79 @@ function request<T>(fn: () => Promise<T | SdkResponse<T>>, service?: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadDirectorySnapshot(sdk: OpencodeClient, directory: string) {
|
function profiledRequest<T>(name: string, fn: () => Promise<T | SdkResponse<T>>, service?: string) {
|
||||||
const [providersResponse, agentsResponse, commandsResponse, skillsResponse] = await Promise.all([
|
return request(() => ACPNextProfile.measure(name, fn), service)
|
||||||
sdk.config.providers({ directory }, { throwOnError: true }),
|
}
|
||||||
sdk.app.agents({ directory }, { throwOnError: true }),
|
|
||||||
sdk.command.list({ directory }, { throwOnError: true }),
|
|
||||||
sdk.app.skills({ directory }, { throwOnError: true }),
|
|
||||||
])
|
|
||||||
const providersData = providersResponse.data!
|
|
||||||
const agents = agentsResponse.data!
|
|
||||||
const commandsData = commandsResponse.data!
|
|
||||||
const skills = skillsResponse.data!
|
|
||||||
const providers = Object.fromEntries(providersData.providers.map((provider) => [provider.id, provider])) as Record<
|
|
||||||
ProviderID,
|
|
||||||
Provider.Info
|
|
||||||
>
|
|
||||||
const defaultModel = await defaultModelFromSdk(sdk, directory, providers)
|
|
||||||
const modes = agents
|
|
||||||
.filter((agent) => agent.mode !== "subagent" && agent.hidden !== true)
|
|
||||||
.map((agent) => ({
|
|
||||||
id: agent.name,
|
|
||||||
name: agent.name,
|
|
||||||
...(agent.description ? { description: agent.description } : {}),
|
|
||||||
}))
|
|
||||||
const commands = [
|
|
||||||
...commandsData,
|
|
||||||
...skills
|
|
||||||
.filter((skill) => !commandsData.some((command) => command.name === skill.name))
|
|
||||||
.map((skill) => ({
|
|
||||||
name: skill.name,
|
|
||||||
description: skill.description,
|
|
||||||
source: "skill" as const,
|
|
||||||
template: skill.content,
|
|
||||||
hints: [],
|
|
||||||
})),
|
|
||||||
] as Command.Info[]
|
|
||||||
|
|
||||||
return Directory.build({
|
async function loadDirectorySnapshot(sdk: OpencodeClient, directory: string) {
|
||||||
directory,
|
return ACPNextProfile.measure("acp.directory.load", async () => {
|
||||||
providers,
|
const [providersResponse, agentsResponse, commandsResponse, skillsResponse, configResponse] = await Promise.all([
|
||||||
modes,
|
ACPNextProfile.measure("acp.directory.provider.list", () =>
|
||||||
defaultModeID: agents.find((agent) => agent.mode === "primary" && agent.hidden !== true)?.name ?? "build",
|
sdk.config.providers({ directory }, { throwOnError: true }),
|
||||||
commands: commands.toSorted((a, b) => a.name.localeCompare(b.name)),
|
),
|
||||||
...(defaultModel ? { defaultModel } : {}),
|
ACPNextProfile.measure("acp.directory.mode.defaultAgent.load", () =>
|
||||||
|
sdk.app.agents({ directory }, { throwOnError: true }),
|
||||||
|
),
|
||||||
|
ACPNextProfile.measure("acp.directory.command.list", () =>
|
||||||
|
sdk.command.list({ directory }, { throwOnError: true }),
|
||||||
|
),
|
||||||
|
ACPNextProfile.measure("acp.directory.skill.list", () => sdk.app.skills({ directory }, { throwOnError: true })),
|
||||||
|
ACPNextProfile.measure("acp.directory.defaultModel.config", () =>
|
||||||
|
sdk.config.get({ directory }, { throwOnError: true }).catch(() => undefined),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
const providersData = providersResponse.data!
|
||||||
|
const agents = agentsResponse.data!
|
||||||
|
const commandsData = commandsResponse.data!
|
||||||
|
const skills = skillsResponse.data!
|
||||||
|
const providers = Object.fromEntries(providersData.providers.map((provider) => [provider.id, provider])) as Record<
|
||||||
|
ProviderID,
|
||||||
|
Provider.Info
|
||||||
|
>
|
||||||
|
const defaultModelStarted = performance.now()
|
||||||
|
const defaultModel = defaultModelFromConfig(configResponse?.data?.model, providers)
|
||||||
|
ACPNextProfile.duration("acp.directory.defaultModel.resolve", defaultModelStarted, { configured: !!defaultModel })
|
||||||
|
const modes = agents
|
||||||
|
.filter((agent) => agent.mode !== "subagent" && agent.hidden !== true)
|
||||||
|
.map((agent) => ({
|
||||||
|
id: agent.name,
|
||||||
|
name: agent.name,
|
||||||
|
...(agent.description ? { description: agent.description } : {}),
|
||||||
|
}))
|
||||||
|
const commands = [
|
||||||
|
...commandsData,
|
||||||
|
...skills
|
||||||
|
.filter((skill) => !commandsData.some((command) => command.name === skill.name))
|
||||||
|
.map((skill) => ({
|
||||||
|
name: skill.name,
|
||||||
|
description: skill.description,
|
||||||
|
source: "skill" as const,
|
||||||
|
template: skill.content,
|
||||||
|
hints: [],
|
||||||
|
})),
|
||||||
|
] as Command.Info[]
|
||||||
|
|
||||||
|
return Directory.build({
|
||||||
|
directory,
|
||||||
|
providers,
|
||||||
|
modes,
|
||||||
|
defaultModeID: agents.find((agent) => agent.mode === "primary" && agent.hidden !== true)?.name ?? "build",
|
||||||
|
commands: commands.toSorted((a, b) => a.name.localeCompare(b.name)),
|
||||||
|
...(defaultModel ? { defaultModel } : {}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function defaultModelFromSdk(
|
function defaultModelFromConfig(
|
||||||
sdk: OpencodeClient,
|
configuredModel: string | undefined,
|
||||||
directory: string,
|
|
||||||
providers: Record<ProviderID, Provider.Info>,
|
providers: Record<ProviderID, Provider.Info>,
|
||||||
): Promise<Directory.DefaultModel | undefined> {
|
): Directory.DefaultModel | undefined {
|
||||||
const configured = await sdk.config
|
const configured = configuredModel ? Provider.parseModel(configuredModel) : undefined
|
||||||
.get({ directory }, { throwOnError: true })
|
|
||||||
.then((response) => (response.data?.model ? Provider.parseModel(response.data.model) : undefined))
|
|
||||||
.catch(() => undefined)
|
|
||||||
if (configured && providers[configured.providerID]?.models[configured.modelID]) return configured
|
if (configured && providers[configured.providerID]?.models[configured.modelID]) return configured
|
||||||
|
|
||||||
const lastUsed = await lastUsedModel(sdk, directory, providers)
|
// First-session ACP startup must not scan historical sessions just to infer
|
||||||
if (lastUsed) return lastUsed
|
// a default. Configured model, opencode provider, then sorted best model keep
|
||||||
|
// the protocol response deterministic without extra session/message reads.
|
||||||
const opencodeProvider = providers[ProviderID.make("opencode")]
|
const opencodeProvider = providers[ProviderID.make("opencode")]
|
||||||
const opencodeModel = opencodeProvider ? Provider.sort(Object.values(opencodeProvider.models))[0] : undefined
|
const opencodeModel = opencodeProvider ? Provider.sort(Object.values(opencodeProvider.models))[0] : undefined
|
||||||
if (opencodeProvider && opencodeModel) return { providerID: opencodeProvider.id, modelID: opencodeModel.id }
|
if (opencodeProvider && opencodeModel) return { providerID: opencodeProvider.id, modelID: opencodeModel.id }
|
||||||
@@ -753,30 +777,6 @@ async function defaultModelFromSdk(
|
|||||||
if (configured) return configured
|
if (configured) return configured
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lastUsedModel(
|
|
||||||
sdk: OpencodeClient,
|
|
||||||
directory: string,
|
|
||||||
providers: Record<ProviderID, Provider.Info>,
|
|
||||||
): Promise<Directory.DefaultModel | undefined> {
|
|
||||||
const session = await sdk.session
|
|
||||||
.list({ directory, roots: true, limit: 1 }, { throwOnError: true })
|
|
||||||
.then((response) => response.data?.[0])
|
|
||||||
.catch(() => undefined)
|
|
||||||
if (!session) return
|
|
||||||
|
|
||||||
const lastUser = await sdk.session
|
|
||||||
.messages({ directory, sessionID: session.id, limit: 20 }, { throwOnError: true })
|
|
||||||
.then((response) => response.data?.findLast((message) => message.info.role === "user")?.info)
|
|
||||||
.catch(() => undefined)
|
|
||||||
if (lastUser?.role !== "user") return
|
|
||||||
if (!providers[ProviderID.make(lastUser.model.providerID)]?.models[ModelID.make(lastUser.model.modelID)]) return
|
|
||||||
|
|
||||||
return {
|
|
||||||
providerID: ProviderID.make(lastUser.model.providerID),
|
|
||||||
modelID: ModelID.make(lastUser.model.modelID),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectDefaultModel(snapshot: Directory.Snapshot) {
|
function selectDefaultModel(snapshot: Directory.Snapshot) {
|
||||||
if (snapshot.defaultModel) return snapshot.defaultModel
|
if (snapshot.defaultModel) return snapshot.defaultModel
|
||||||
const model = snapshot.modelOptions[0]
|
const model = snapshot.modelOptions[0]
|
||||||
@@ -891,6 +891,7 @@ function registerMcpServers(
|
|||||||
sessionId: string,
|
sessionId: string,
|
||||||
servers: readonly McpServer[],
|
servers: readonly McpServer[],
|
||||||
) {
|
) {
|
||||||
|
const started = performance.now()
|
||||||
const current = registered.get(sessionId) ?? new Set<string>()
|
const current = registered.get(sessionId) ?? new Set<string>()
|
||||||
registered.set(sessionId, current)
|
registered.set(sessionId, current)
|
||||||
const pending = new Set<string>()
|
const pending = new Set<string>()
|
||||||
@@ -922,7 +923,16 @@ function registerMcpServers(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
{ concurrency: "unbounded" },
|
{ concurrency: "unbounded" },
|
||||||
).pipe(Effect.asVoid)
|
).pipe(
|
||||||
|
Effect.tap(() =>
|
||||||
|
Effect.sync(() =>
|
||||||
|
ACPNextProfile.duration("acp.mcp.register", started, {
|
||||||
|
count: pending.size,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Effect.asVoid,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function mcpRegistrationKey(name: string, config: ReturnType<typeof mcpConfig>) {
|
function mcpRegistrationKey(name: string, config: ReturnType<typeof mcpConfig>) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { ServerAuth } from "@/server/auth"
|
|||||||
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
|
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
|
||||||
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
||||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||||
|
import { ACPNextProfile } from "@/acp-next/profile"
|
||||||
|
|
||||||
const log = Log.create({ service: "acp-command" })
|
const log = Log.create({ service: "acp-command" })
|
||||||
|
|
||||||
@@ -23,10 +24,13 @@ export const AcpCommand = effectCmd({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
handler: Effect.fn("Cli.acp")(function* (args) {
|
handler: Effect.fn("Cli.acp")(function* (args) {
|
||||||
|
ACPNextProfile.mark("cli.acp.handler")
|
||||||
process.env.OPENCODE_CLIENT = "acp"
|
process.env.OPENCODE_CLIENT = "acp"
|
||||||
const flags = yield* RuntimeFlags.Service
|
const flags = yield* RuntimeFlags.Service
|
||||||
const opts = yield* resolveNetworkOptions(args)
|
const opts = yield* resolveNetworkOptions(args)
|
||||||
const server = yield* Effect.promise(() => Server.listen(opts))
|
const server = yield* Effect.promise(() =>
|
||||||
|
ACPNextProfile.measure("cli.acp.server.listen", () => Server.listen(opts)),
|
||||||
|
)
|
||||||
|
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: `http://${server.hostname}:${server.port}`,
|
baseUrl: `http://${server.hostname}:${server.port}`,
|
||||||
@@ -60,6 +64,7 @@ export const AcpCommand = effectCmd({
|
|||||||
const agent = flags.acpNext ? ACPNext.init({ sdk }) : ACP.init({ sdk })
|
const agent = flags.acpNext ? ACPNext.init({ sdk }) : ACP.init({ sdk })
|
||||||
|
|
||||||
new AgentSideConnection((conn) => {
|
new AgentSideConnection((conn) => {
|
||||||
|
ACPNextProfile.mark("cli.acp.connection.create", { acpNext: flags.acpNext })
|
||||||
return agent.create(conn, { sdk })
|
return agent.create(conn, { sdk })
|
||||||
}, stream)
|
}, stream)
|
||||||
|
|
||||||
|
|||||||
@@ -602,6 +602,46 @@ describe("ACP next service sessions", () => {
|
|||||||
expect(result.configOptions?.find((option) => option.id === "model")?.currentValue).toBe("test/configured-model")
|
expect(result.configOptions?.find((option) => option.id === "model")?.currentValue).toBe("test/configured-model")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("does not scan last-used sessions when resolving the new session default", async () => {
|
||||||
|
const historyCalls: string[] = []
|
||||||
|
const sdk = {
|
||||||
|
config: {
|
||||||
|
providers: () => Promise.resolve({ data: { providers: [provider], default: { test: modelID } } }),
|
||||||
|
get: () => Promise.resolve({ data: {} }),
|
||||||
|
},
|
||||||
|
app: {
|
||||||
|
agents: () => Promise.resolve({ data: [{ name: "build", mode: "primary", permission: [], options: {} }] }),
|
||||||
|
skills: () => Promise.resolve({ data: [] }),
|
||||||
|
},
|
||||||
|
command: {
|
||||||
|
list: () => Promise.resolve({ data: [] }),
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
create: (input: { model?: { id?: string } }) => Promise.resolve({ data: { id: input.model?.id } }),
|
||||||
|
list: () => {
|
||||||
|
historyCalls.push("list")
|
||||||
|
return Promise.resolve({ data: [{ id: "ses_recent" }] })
|
||||||
|
},
|
||||||
|
messages: () => {
|
||||||
|
historyCalls.push("messages")
|
||||||
|
return Promise.resolve({
|
||||||
|
data: [{ info: { role: "user", model: { providerID: "test", modelID: "second-model" } } }],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mcp: {
|
||||||
|
add: () => Promise.resolve({ data: {} }),
|
||||||
|
},
|
||||||
|
} as unknown as OpencodeClient
|
||||||
|
const service = ACPNextService.make({ sdk })
|
||||||
|
|
||||||
|
const result = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
|
||||||
|
|
||||||
|
expect(result.sessionId).toBe("test-model")
|
||||||
|
expect(result.configOptions?.find((option) => option.id === "model")?.currentValue).toBe("test/test-model")
|
||||||
|
expect(historyCalls).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
it("switches model and returns updated model and effort options", async () => {
|
it("switches model and returns updated model and effort options", async () => {
|
||||||
const { service } = makeService()
|
const { service } = makeService()
|
||||||
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
|
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
type AcpClient,
|
type AcpClient,
|
||||||
} from "../acp/acp-test-client"
|
} from "../acp/acp-test-client"
|
||||||
|
|
||||||
export const diagnosticFirstSessionThresholdMs = 15_000
|
export const diagnosticFirstSessionThresholdMs = 5_000
|
||||||
export const diagnosticFastPathThresholdMs = 15_000
|
export const diagnosticFastPathThresholdMs = 15_000
|
||||||
|
|
||||||
// TODO: tighten to the public verifier target of 500ms once acp-next startup is optimized.
|
// TODO: tighten to the public verifier target of 500ms once acp-next startup is optimized.
|
||||||
|
|||||||
Reference in New Issue
Block a user