Revert "fix(server): wait for opencode completion after EOF"
This reverts commit 2a84b081298f904936f5ad61dca2388b48835e0e.
This commit is contained in:
@@ -13,7 +13,6 @@ import {
|
|||||||
} from "./opencode-agent.js";
|
} from "./opencode-agent.js";
|
||||||
import { streamSession } from "./test-utils/session-stream-adapter.js";
|
import { streamSession } from "./test-utils/session-stream-adapter.js";
|
||||||
import type {
|
import type {
|
||||||
AgentSession,
|
|
||||||
AgentSessionConfig,
|
AgentSessionConfig,
|
||||||
AgentStreamEvent,
|
AgentStreamEvent,
|
||||||
ToolCallTimelineItem,
|
ToolCallTimelineItem,
|
||||||
@@ -43,8 +42,6 @@ interface TurnResult {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FAST_OPENCODE_EOF_RECOVERY_POLICY = { maxAttempts: 5, delayMs: 1 };
|
|
||||||
|
|
||||||
async function collectTurnEvents(iterator: AsyncGenerator<AgentStreamEvent>): Promise<TurnResult> {
|
async function collectTurnEvents(iterator: AsyncGenerator<AgentStreamEvent>): Promise<TurnResult> {
|
||||||
const result: TurnResult = {
|
const result: TurnResult = {
|
||||||
events: [],
|
events: [],
|
||||||
@@ -81,140 +78,6 @@ async function collectTurnEvents(iterator: AsyncGenerator<AgentStreamEvent>): Pr
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FakeOpenCodeStream {
|
|
||||||
stream: AsyncIterable<OpenCodeEvent>;
|
|
||||||
close: () => void;
|
|
||||||
closed: Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFakeOpenCodeStream(events: OpenCodeEvent[] = []): FakeOpenCodeStream {
|
|
||||||
let releaseStream!: () => void;
|
|
||||||
const canEnd = new Promise<void>((resolve) => {
|
|
||||||
releaseStream = resolve;
|
|
||||||
});
|
|
||||||
let notifyEnded!: () => void;
|
|
||||||
const closed = new Promise<void>((resolve) => {
|
|
||||||
notifyEnded = resolve;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
close: releaseStream,
|
|
||||||
closed,
|
|
||||||
stream: {
|
|
||||||
[Symbol.asyncIterator]: () => {
|
|
||||||
let index = 0;
|
|
||||||
return {
|
|
||||||
next: async () => {
|
|
||||||
if (index < events.length) {
|
|
||||||
return { done: false, value: events[index++] };
|
|
||||||
}
|
|
||||||
await canEnd;
|
|
||||||
notifyEnded();
|
|
||||||
return { done: true, value: undefined };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFakeOpenCode(stream: FakeOpenCodeStream, onPrompt: () => void | Promise<void>) {
|
|
||||||
return {
|
|
||||||
event: {
|
|
||||||
subscribe: async () => ({ stream: stream.stream }),
|
|
||||||
},
|
|
||||||
provider: {
|
|
||||||
list: async () => ({ data: { connected: [], all: [] }, error: undefined }),
|
|
||||||
},
|
|
||||||
session: {
|
|
||||||
create: async () => ({ data: { id: "ses_unit_test" }, error: undefined }),
|
|
||||||
promptAsync: async () => {
|
|
||||||
await onPrompt();
|
|
||||||
return { data: {}, error: undefined };
|
|
||||||
},
|
|
||||||
abort: async () => ({ data: true, error: undefined }),
|
|
||||||
update: async () => ({ data: true, error: undefined }),
|
|
||||||
delete: async () => ({ data: true, error: undefined }),
|
|
||||||
},
|
|
||||||
} as never;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createSessionWithFakeOpenCode(params: {
|
|
||||||
storageRoot: string;
|
|
||||||
cwd: string;
|
|
||||||
stream: FakeOpenCodeStream;
|
|
||||||
onPrompt: () => void | Promise<void>;
|
|
||||||
}): Promise<AgentSession> {
|
|
||||||
const fakeClient = createFakeOpenCode(params.stream, params.onPrompt);
|
|
||||||
const client = new OpenCodeAgentClient(createTestLogger(), undefined, params.storageRoot, {
|
|
||||||
runtime: {
|
|
||||||
acquireServer: async () => ({
|
|
||||||
server: { port: 0, url: "http://localhost" },
|
|
||||||
release: () => {},
|
|
||||||
}),
|
|
||||||
ensureServerRunning: async () => ({ port: 0, url: "http://localhost" }),
|
|
||||||
createClient: () => fakeClient,
|
|
||||||
shutdown: async () => undefined,
|
|
||||||
},
|
|
||||||
eofRecoveryPolicy: FAST_OPENCODE_EOF_RECOVERY_POLICY,
|
|
||||||
});
|
|
||||||
|
|
||||||
return client.createSession({ provider: "opencode", cwd: params.cwd });
|
|
||||||
}
|
|
||||||
|
|
||||||
function observeTurn(session: AgentSession) {
|
|
||||||
const events: AgentStreamEvent[] = [];
|
|
||||||
let resolveTerminal!: (event: AgentStreamEvent) => void;
|
|
||||||
const terminal = new Promise<AgentStreamEvent>((resolve) => {
|
|
||||||
resolveTerminal = resolve;
|
|
||||||
});
|
|
||||||
|
|
||||||
session.subscribe((event) => {
|
|
||||||
events.push(event);
|
|
||||||
if (
|
|
||||||
event.type === "turn_completed" ||
|
|
||||||
event.type === "turn_failed" ||
|
|
||||||
event.type === "turn_canceled"
|
|
||||||
) {
|
|
||||||
resolveTerminal(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
terminal,
|
|
||||||
assistantMessages: () =>
|
|
||||||
events
|
|
||||||
.flatMap((event) => (event.type === "timeline" ? [event.item] : []))
|
|
||||||
.filter((item): item is AssistantMessageTimelineItem => item.type === "assistant_message"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function openCodeAssistantStarted(messageId: string): OpenCodeEvent {
|
|
||||||
return {
|
|
||||||
type: "message.updated",
|
|
||||||
properties: {
|
|
||||||
info: {
|
|
||||||
id: messageId,
|
|
||||||
sessionID: "ses_unit_test",
|
|
||||||
role: "assistant",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as OpenCodeEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openCodeTextDelta(messageId: string, partId: string, delta: string): OpenCodeEvent {
|
|
||||||
return {
|
|
||||||
type: "message.part.delta",
|
|
||||||
properties: {
|
|
||||||
sessionID: "ses_unit_test",
|
|
||||||
messageID: messageId,
|
|
||||||
partID: partId,
|
|
||||||
field: "text",
|
|
||||||
delta,
|
|
||||||
},
|
|
||||||
} as OpenCodeEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isBinaryInstalled(binary: string): boolean {
|
function isBinaryInstalled(binary: string): boolean {
|
||||||
try {
|
try {
|
||||||
const out = execFileSync("which", [binary], { encoding: "utf8" }).trim();
|
const out = execFileSync("which", [binary], { encoding: "utf8" }).trim();
|
||||||
@@ -808,7 +671,6 @@ describe("OpenCode adapter startTurn error handling", () => {
|
|||||||
createClient: vi.fn().mockReturnValue(fakeClient),
|
createClient: vi.fn().mockReturnValue(fakeClient),
|
||||||
shutdown: vi.fn().mockResolvedValue(undefined),
|
shutdown: vi.fn().mockResolvedValue(undefined),
|
||||||
},
|
},
|
||||||
eofRecoveryPolicy: FAST_OPENCODE_EOF_RECOVERY_POLICY,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const session = await client.createSession({ provider: "opencode", cwd });
|
const session = await client.createSession({ provider: "opencode", cwd });
|
||||||
@@ -834,126 +696,6 @@ describe("OpenCode adapter startTurn error handling", () => {
|
|||||||
rmSync(storageRoot, { recursive: true, force: true });
|
rmSync(storageRoot, { recursive: true, force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
test("recovers SSE EOF when persisted assistant completion appears after the stream closes", async () => {
|
|
||||||
const storageRoot = mkdtempSync(path.join(os.tmpdir(), "opencode-storage-"));
|
|
||||||
const cwd = "/tmp/test";
|
|
||||||
const dateNowSpy = vi.spyOn(Date, "now").mockReturnValue(2000);
|
|
||||||
|
|
||||||
try {
|
|
||||||
writeOpenCodeStoredSession(storageRoot, cwd);
|
|
||||||
const stream = createFakeOpenCodeStream();
|
|
||||||
const session = await createSessionWithFakeOpenCode({
|
|
||||||
storageRoot,
|
|
||||||
cwd,
|
|
||||||
stream,
|
|
||||||
onPrompt: stream.close,
|
|
||||||
});
|
|
||||||
const turn = observeTurn(session);
|
|
||||||
|
|
||||||
await session.startTurn("hello");
|
|
||||||
await stream.closed;
|
|
||||||
writeRecoveredAssistantCompletion(storageRoot, "msg_assistant", "Recovered assistant reply");
|
|
||||||
|
|
||||||
expect((await turn.terminal).type).toBe("turn_completed");
|
|
||||||
expect(
|
|
||||||
turn
|
|
||||||
.assistantMessages()
|
|
||||||
.map((message) => message.text)
|
|
||||||
.join(""),
|
|
||||||
).toBe("Recovered assistant reply");
|
|
||||||
} finally {
|
|
||||||
dateNowSpy.mockRestore();
|
|
||||||
rmSync(storageRoot, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("recovers delayed persisted completion without duplicating text already streamed before EOF", async () => {
|
|
||||||
const storageRoot = mkdtempSync(path.join(os.tmpdir(), "opencode-storage-"));
|
|
||||||
const cwd = "/tmp/test";
|
|
||||||
const dateNowSpy = vi.spyOn(Date, "now").mockReturnValue(2000);
|
|
||||||
let completionWriteSettled: Promise<void> = Promise.resolve();
|
|
||||||
const writeDelayedCompletion = async () => {
|
|
||||||
await nextMacrotask();
|
|
||||||
writeRecoveredAssistantCompletion(storageRoot, "msg_assistant", "Recovered assistant reply");
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
writeOpenCodeStoredSession(storageRoot, cwd);
|
|
||||||
const stream = createFakeOpenCodeStream([
|
|
||||||
openCodeAssistantStarted("msg_assistant"),
|
|
||||||
openCodeTextDelta("msg_assistant", "prt_text", "Recovered "),
|
|
||||||
]);
|
|
||||||
const session = await createSessionWithFakeOpenCode({
|
|
||||||
storageRoot,
|
|
||||||
cwd,
|
|
||||||
stream,
|
|
||||||
onPrompt: async () => {
|
|
||||||
stream.close();
|
|
||||||
completionWriteSettled = writeDelayedCompletion();
|
|
||||||
await completionWriteSettled;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const turn = observeTurn(session);
|
|
||||||
|
|
||||||
await session.startTurn("hello");
|
|
||||||
await stream.closed;
|
|
||||||
|
|
||||||
expect((await turn.terminal).type).toBe("turn_completed");
|
|
||||||
const assistantMessages = turn.assistantMessages();
|
|
||||||
expect(assistantMessages.map((message) => message.text).join("")).toBe(
|
|
||||||
"Recovered assistant reply",
|
|
||||||
);
|
|
||||||
expect(assistantMessages.map((message) => message.text)).toEqual([
|
|
||||||
"Recovered ",
|
|
||||||
"assistant reply",
|
|
||||||
]);
|
|
||||||
} finally {
|
|
||||||
await completionWriteSettled;
|
|
||||||
dateNowSpy.mockRestore();
|
|
||||||
rmSync(storageRoot, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("ignores old completed assistant messages while waiting for delayed current-turn completion", async () => {
|
|
||||||
const storageRoot = mkdtempSync(path.join(os.tmpdir(), "opencode-storage-"));
|
|
||||||
const cwd = "/tmp/test";
|
|
||||||
const dateNowSpy = vi.spyOn(Date, "now").mockReturnValue(2000);
|
|
||||||
|
|
||||||
try {
|
|
||||||
writeOpenCodeStoredSession(storageRoot, cwd);
|
|
||||||
writeOpenCodeAssistantCompletion(storageRoot, "msg_assistant_old", "Old assistant reply", {
|
|
||||||
created: 1500,
|
|
||||||
completed: 1600,
|
|
||||||
partStart: 1500,
|
|
||||||
partEnd: 1550,
|
|
||||||
});
|
|
||||||
const stream = createFakeOpenCodeStream();
|
|
||||||
const session = await createSessionWithFakeOpenCode({
|
|
||||||
storageRoot,
|
|
||||||
cwd,
|
|
||||||
stream,
|
|
||||||
onPrompt: stream.close,
|
|
||||||
});
|
|
||||||
const turn = observeTurn(session);
|
|
||||||
|
|
||||||
await session.startTurn("hello");
|
|
||||||
await stream.closed;
|
|
||||||
writeRecoveredAssistantCompletion(
|
|
||||||
storageRoot,
|
|
||||||
"msg_assistant_current",
|
|
||||||
"Current assistant reply",
|
|
||||||
);
|
|
||||||
|
|
||||||
expect((await turn.terminal).type).toBe("turn_completed");
|
|
||||||
expect(turn.assistantMessages().map((message) => message.text)).toEqual([
|
|
||||||
"Current assistant reply",
|
|
||||||
]);
|
|
||||||
} finally {
|
|
||||||
dateNowSpy.mockRestore();
|
|
||||||
rmSync(storageRoot, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("keeps SSE EOF as turn_failed without persisted completion evidence", async () => {
|
test("keeps SSE EOF as turn_failed without persisted completion evidence", async () => {
|
||||||
const storageRoot = mkdtempSync(path.join(os.tmpdir(), "opencode-storage-"));
|
const storageRoot = mkdtempSync(path.join(os.tmpdir(), "opencode-storage-"));
|
||||||
const cwd = "/tmp/test";
|
const cwd = "/tmp/test";
|
||||||
@@ -1021,7 +763,6 @@ describe("OpenCode adapter startTurn error handling", () => {
|
|||||||
createClient: vi.fn().mockReturnValue(fakeClient),
|
createClient: vi.fn().mockReturnValue(fakeClient),
|
||||||
shutdown: vi.fn().mockResolvedValue(undefined),
|
shutdown: vi.fn().mockResolvedValue(undefined),
|
||||||
},
|
},
|
||||||
eofRecoveryPolicy: FAST_OPENCODE_EOF_RECOVERY_POLICY,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const session = await client.createSession({ provider: "opencode", cwd });
|
const session = await client.createSession({ provider: "opencode", cwd });
|
||||||
@@ -1126,7 +867,6 @@ describe("OpenCode adapter startTurn error handling", () => {
|
|||||||
createClient: vi.fn().mockReturnValue(fakeClient),
|
createClient: vi.fn().mockReturnValue(fakeClient),
|
||||||
shutdown: vi.fn().mockResolvedValue(undefined),
|
shutdown: vi.fn().mockResolvedValue(undefined),
|
||||||
},
|
},
|
||||||
eofRecoveryPolicy: FAST_OPENCODE_EOF_RECOVERY_POLICY,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const session = await client.createSession({ provider: "opencode", cwd });
|
const session = await client.createSession({ provider: "opencode", cwd });
|
||||||
@@ -1318,51 +1058,3 @@ function writeOpenCodeJson(storageRoot: string, relativePath: string, value: unk
|
|||||||
mkdirSync(path.dirname(filePath), { recursive: true });
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
||||||
writeFileSync(filePath, JSON.stringify(value), "utf8");
|
writeFileSync(filePath, JSON.stringify(value), "utf8");
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeOpenCodeStoredSession(storageRoot: string, cwd: string): void {
|
|
||||||
writeOpenCodeJson(storageRoot, "session/project-1/ses_unit_test.json", {
|
|
||||||
id: "ses_unit_test",
|
|
||||||
directory: cwd,
|
|
||||||
time: { created: 1000, updated: 3000 },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextMacrotask(): Promise<void> {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeRecoveredAssistantCompletion(
|
|
||||||
storageRoot: string,
|
|
||||||
messageId: string,
|
|
||||||
text: string,
|
|
||||||
): void {
|
|
||||||
writeOpenCodeAssistantCompletion(storageRoot, messageId, text, {
|
|
||||||
created: 2000,
|
|
||||||
completed: 2500,
|
|
||||||
partStart: 2100,
|
|
||||||
partEnd: 2400,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeOpenCodeAssistantCompletion(
|
|
||||||
storageRoot: string,
|
|
||||||
messageId: string,
|
|
||||||
text: string,
|
|
||||||
time: { created: number; completed: number; partStart: number; partEnd: number },
|
|
||||||
): void {
|
|
||||||
writeOpenCodeJson(storageRoot, `message/ses_unit_test/${messageId}.json`, {
|
|
||||||
id: messageId,
|
|
||||||
sessionID: "ses_unit_test",
|
|
||||||
role: "assistant",
|
|
||||||
finish: "stop",
|
|
||||||
time: { created: time.created, completed: time.completed },
|
|
||||||
});
|
|
||||||
writeOpenCodeJson(storageRoot, `part/${messageId}/prt_text.json`, {
|
|
||||||
id: "prt_text",
|
|
||||||
sessionID: "ses_unit_test",
|
|
||||||
messageID: messageId,
|
|
||||||
type: "text",
|
|
||||||
text,
|
|
||||||
time: { start: time.partStart, end: time.partEnd },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -74,10 +74,6 @@ const OPENCODE_CAPABILITIES: AgentCapabilityFlags = {
|
|||||||
const OPENCODE_BUILD_MODE_ID = "build";
|
const OPENCODE_BUILD_MODE_ID = "build";
|
||||||
const OPENCODE_FULL_ACCESS_MODE_ID = "full-access";
|
const OPENCODE_FULL_ACCESS_MODE_ID = "full-access";
|
||||||
const OPENCODE_STORAGE_SESSION_LIMIT = 200;
|
const OPENCODE_STORAGE_SESSION_LIMIT = 200;
|
||||||
const DEFAULT_OPENCODE_EOF_RECOVERY_POLICY = {
|
|
||||||
maxAttempts: 600,
|
|
||||||
delayMs: 1_000,
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_MODES: AgentMode[] = [
|
const DEFAULT_MODES: AgentMode[] = [
|
||||||
{
|
{
|
||||||
@@ -148,11 +144,6 @@ interface OpenCodePersistedAssistantCompletion {
|
|||||||
usage?: AgentUsage;
|
usage?: AgentUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OpenCodeEofRecoveryPolicy {
|
|
||||||
maxAttempts: number;
|
|
||||||
delayMs: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type OpenCodeAgentConfig = AgentSessionConfig & { provider: "opencode" };
|
type OpenCodeAgentConfig = AgentSessionConfig & { provider: "opencode" };
|
||||||
type OpenCodeMessageRole = "user" | "assistant";
|
type OpenCodeMessageRole = "user" | "assistant";
|
||||||
|
|
||||||
@@ -913,10 +904,6 @@ async function readOpenCodePersistedAssistantCompletion(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sleep(ms: number): Promise<void> {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasStrongPersistedCompletionEvidence(
|
function hasStrongPersistedCompletionEvidence(
|
||||||
message: OpenCodeStoredMessage,
|
message: OpenCodeStoredMessage,
|
||||||
parts: OpenCodeStoredPart[],
|
parts: OpenCodeStoredPart[],
|
||||||
@@ -1056,7 +1043,6 @@ export const __openCodeInternals = {
|
|||||||
|
|
||||||
interface OpenCodeAgentClientDeps {
|
interface OpenCodeAgentClientDeps {
|
||||||
runtime?: OpenCodeRuntime;
|
runtime?: OpenCodeRuntime;
|
||||||
eofRecoveryPolicy?: OpenCodeEofRecoveryPolicy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProductionOpenCodeRuntime implements OpenCodeRuntime {
|
class ProductionOpenCodeRuntime implements OpenCodeRuntime {
|
||||||
@@ -1088,7 +1074,6 @@ export class OpenCodeAgentClient implements AgentClient {
|
|||||||
private readonly runtimeSettings?: ProviderRuntimeSettings;
|
private readonly runtimeSettings?: ProviderRuntimeSettings;
|
||||||
private readonly modelContextWindows = new Map<string, number>();
|
private readonly modelContextWindows = new Map<string, number>();
|
||||||
private readonly storageRoot: string;
|
private readonly storageRoot: string;
|
||||||
private readonly eofRecoveryPolicy: OpenCodeEofRecoveryPolicy;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
@@ -1099,7 +1084,6 @@ export class OpenCodeAgentClient implements AgentClient {
|
|||||||
this.logger = logger.child({ module: "agent", provider: "opencode" });
|
this.logger = logger.child({ module: "agent", provider: "opencode" });
|
||||||
this.runtimeSettings = runtimeSettings;
|
this.runtimeSettings = runtimeSettings;
|
||||||
this.storageRoot = storageRoot ?? resolveOpenCodeStorageRoot();
|
this.storageRoot = storageRoot ?? resolveOpenCodeStorageRoot();
|
||||||
this.eofRecoveryPolicy = deps.eofRecoveryPolicy ?? DEFAULT_OPENCODE_EOF_RECOVERY_POLICY;
|
|
||||||
this.runtime =
|
this.runtime =
|
||||||
deps.runtime ??
|
deps.runtime ??
|
||||||
new ProductionOpenCodeRuntime(
|
new ProductionOpenCodeRuntime(
|
||||||
@@ -1147,7 +1131,6 @@ export class OpenCodeAgentClient implements AgentClient {
|
|||||||
new Map(this.modelContextWindows),
|
new Map(this.modelContextWindows),
|
||||||
acquisition.release,
|
acquisition.release,
|
||||||
options?.persistSession,
|
options?.persistSession,
|
||||||
this.eofRecoveryPolicy,
|
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
acquisition.release();
|
acquisition.release();
|
||||||
@@ -1189,8 +1172,6 @@ export class OpenCodeAgentClient implements AgentClient {
|
|||||||
this.storageRoot,
|
this.storageRoot,
|
||||||
new Map(this.modelContextWindows),
|
new Map(this.modelContextWindows),
|
||||||
acquisition.release,
|
acquisition.release,
|
||||||
undefined,
|
|
||||||
this.eofRecoveryPolicy,
|
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
acquisition.release();
|
acquisition.release();
|
||||||
@@ -2321,7 +2302,6 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
private selectedModelContextWindowMaxTokens: number | undefined;
|
private selectedModelContextWindowMaxTokens: number | undefined;
|
||||||
private releaseServer: (() => void) | null;
|
private releaseServer: (() => void) | null;
|
||||||
private readonly persistSession: boolean;
|
private readonly persistSession: boolean;
|
||||||
private readonly eofRecoveryPolicy: OpenCodeEofRecoveryPolicy;
|
|
||||||
private deletedFromProvider = false;
|
private deletedFromProvider = false;
|
||||||
private foregroundAssistantMessageEmitted = false;
|
private foregroundAssistantMessageEmitted = false;
|
||||||
private foregroundAssistantText = "";
|
private foregroundAssistantText = "";
|
||||||
@@ -2337,7 +2317,6 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
modelContextWindowsByModelKey: ReadonlyMap<string, number> = new Map(),
|
modelContextWindowsByModelKey: ReadonlyMap<string, number> = new Map(),
|
||||||
releaseServer?: () => void,
|
releaseServer?: () => void,
|
||||||
persistSession = true,
|
persistSession = true,
|
||||||
eofRecoveryPolicy: OpenCodeEofRecoveryPolicy = DEFAULT_OPENCODE_EOF_RECOVERY_POLICY,
|
|
||||||
) {
|
) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
@@ -2348,7 +2327,6 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
this.currentMode = normalizeOpenCodeModeId(config.modeId);
|
this.currentMode = normalizeOpenCodeModeId(config.modeId);
|
||||||
this.releaseServer = releaseServer ?? null;
|
this.releaseServer = releaseServer ?? null;
|
||||||
this.persistSession = persistSession;
|
this.persistSession = persistSession;
|
||||||
this.eofRecoveryPolicy = eofRecoveryPolicy;
|
|
||||||
this.selectedModelContextWindowMaxTokens = this.resolveConfiguredModelContextWindowMaxTokens(
|
this.selectedModelContextWindowMaxTokens = this.resolveConfiguredModelContextWindowMaxTokens(
|
||||||
config.model,
|
config.model,
|
||||||
);
|
);
|
||||||
@@ -2692,7 +2670,12 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const completion = await this.waitForPersistedAssistantCompletion(turnId);
|
const completion = await readOpenCodePersistedAssistantCompletion(
|
||||||
|
this.storageRoot,
|
||||||
|
this.sessionId,
|
||||||
|
this.foregroundKnownMessageIds,
|
||||||
|
this.foregroundTurnStartedAt,
|
||||||
|
);
|
||||||
if (!completion || this.activeForegroundTurnId !== turnId) {
|
if (!completion || this.activeForegroundTurnId !== turnId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2749,32 +2732,6 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async waitForPersistedAssistantCompletion(
|
|
||||||
turnId: string,
|
|
||||||
): Promise<OpenCodePersistedAssistantCompletion | null> {
|
|
||||||
for (let attempt = 0; attempt < this.eofRecoveryPolicy.maxAttempts; attempt += 1) {
|
|
||||||
if (this.foregroundTurnStartedAt === null || this.activeForegroundTurnId !== turnId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const completion = await readOpenCodePersistedAssistantCompletion(
|
|
||||||
this.storageRoot,
|
|
||||||
this.sessionId,
|
|
||||||
this.foregroundKnownMessageIds,
|
|
||||||
this.foregroundTurnStartedAt,
|
|
||||||
);
|
|
||||||
if (completion) {
|
|
||||||
return completion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attempt < this.eofRecoveryPolicy.maxAttempts - 1) {
|
|
||||||
await sleep(this.eofRecoveryPolicy.delayMs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private resolvePersistedAssistantRecoveryText(completedText: string): string | null {
|
private resolvePersistedAssistantRecoveryText(completedText: string): string | null {
|
||||||
if (!this.foregroundAssistantMessageEmitted) {
|
if (!this.foregroundAssistantMessageEmitted) {
|
||||||
return completedText;
|
return completedText;
|
||||||
|
|||||||
Reference in New Issue
Block a user