fix: isolate concurrent OpenCode chat runs

This commit is contained in:
2026-08-17 22:03:37 +08:00
parent 7e8d9e3811
commit 65040730ec
29 changed files with 5207 additions and 427 deletions

View File

@@ -81,6 +81,10 @@ export interface FindOpencodeFilesOptions {
limit?: number;
}
export interface OpencodeRuntimeRequestOptions {
signal?: AbortSignal;
}
export interface UpdateOpencodeSessionInput {
title?: string;
}
@@ -93,6 +97,12 @@ export interface OpencodeSkillInfo {
entries?: OpencodeSkillEntry[];
}
export interface OpencodeAgentInfo {
name?: string;
id?: string;
[key: string]: unknown;
}
export interface OpencodeSkillEntry {
path: string;
type: 'file' | 'directory';
@@ -193,6 +203,7 @@ async function parseJsonResponse<T>(response: Response): Promise<T> {
export function createOpencodeClient(options: OpencodeClientOptions) {
const fetchImpl = options.fetchImpl ?? fetch;
const request = async <T>(path: string, init?: RequestInit): Promise<T> => {
init?.signal?.throwIfAborted();
const decorated = decorateOpencodeRequest({
baseUrl: options.baseUrl,
path,
@@ -253,23 +264,30 @@ export function createOpencodeClient(options: OpencodeClientOptions) {
summarizeSession: (
sessionID: string,
payload: SummarizeOpencodeSessionInput,
options?: OpencodeRuntimeRequestOptions,
): Promise<boolean> =>
request<boolean>(`/session/${encodeURIComponent(sessionID)}/summarize`, {
method: 'POST',
body: JSON.stringify(payload),
signal: options?.signal,
}),
executeSessionCommand: (
sessionID: string,
payload: ExecuteOpencodeSessionCommandInput,
options?: OpencodeRuntimeRequestOptions,
): Promise<unknown> =>
request<unknown>(`/session/${encodeURIComponent(sessionID)}/command`, {
method: 'POST',
body: JSON.stringify(payload),
signal: options?.signal,
}),
};
return {
...opencodeCommandMethods,
listAgents: (options?: OpencodeRuntimeRequestOptions) => request<OpencodeAgentInfo[]>('/agent', {
signal: options?.signal,
}),
listSkills: () => request<OpencodeSkillInfo[]>('/skill'),
listSessions: () => request<unknown[]>('/session'),
createSession: (payload: Record<string, unknown>) => request<unknown>('/session', {
@@ -313,7 +331,9 @@ export function createOpencodeClient(options: OpencodeClientOptions) {
request<void>(`/question/${encodeURIComponent(requestID)}/reject`, {
method: 'POST',
}),
getConfig: () => request<Record<string, unknown>>('/config'),
getConfig: (options?: OpencodeRuntimeRequestOptions) => request<Record<string, unknown>>('/config', {
signal: options?.signal,
}),
listPermissions: () => request<unknown[]>('/permission'),
replyPermission: (requestID: string, reply: OpencodePermissionReply, message?: string) =>
request<void>(`/permission/${encodeURIComponent(requestID)}/reply`, {
@@ -330,12 +350,17 @@ export function createOpencodeClient(options: OpencodeClientOptions) {
method: 'POST',
body: JSON.stringify(buildTextPartsPayload(payload)),
}),
promptSessionAsync: (sessionID: string, payload: SendOpencodeSessionMessageInput) => {
promptSessionAsync: (
sessionID: string,
payload: SendOpencodeSessionMessageInput,
options?: OpencodeRuntimeRequestOptions,
) => {
const requestPayload = buildTextPartsPayload(payload);
logger.info('[opencode-client] Sending prompt_async payload', summarizePromptPayload(sessionID, requestPayload));
return request<void>(`/session/${encodeURIComponent(sessionID)}/prompt_async`, {
method: 'POST',
body: JSON.stringify(requestPayload),
signal: options?.signal,
});
},
getFileStatuses: () => request<unknown[]>('/file/status'),