This commit is contained in:
parent
9d64886259
commit
d48fd258fa
2 changed files with 102 additions and 16 deletions
|
|
@ -702,21 +702,39 @@ export class DiscordCodexBridge {
|
|||
}
|
||||
const state = this.#requireState();
|
||||
const existing = this.#gatewaySession();
|
||||
const explicitMainThread = Boolean(gatewayConfig.mainThreadId);
|
||||
let forceCreateGatewayThread = false;
|
||||
const shouldReuseExisting =
|
||||
Boolean(gatewayConfig.mainThreadId) ||
|
||||
explicitMainThread ||
|
||||
state.gateway?.toolsVersion === gatewayToolsVersion;
|
||||
if (existing && shouldReuseExisting) {
|
||||
state.gateway = {
|
||||
homeChannelId: gatewayConfig.homeChannelId,
|
||||
mainThreadId: existing.codexThreadId,
|
||||
statusMessageId: existing.statusMessageId,
|
||||
createdAt: existing.createdAt,
|
||||
toolsVersion: state.gateway?.toolsVersion,
|
||||
delegations: state.gateway?.delegations ?? [],
|
||||
};
|
||||
this.#registerRunner(existing);
|
||||
await this.#persist();
|
||||
return;
|
||||
try {
|
||||
const resumed = await this.client.resumeThread(this.#threadResumeParams(
|
||||
existing.codexThreadId,
|
||||
existing.cwd ?? this.config.cwd,
|
||||
));
|
||||
existing.cwd = resumeResponseCwd(resumed) ?? existing.cwd ?? this.config.cwd;
|
||||
state.gateway = {
|
||||
homeChannelId: gatewayConfig.homeChannelId,
|
||||
mainThreadId: existing.codexThreadId,
|
||||
statusMessageId: existing.statusMessageId,
|
||||
createdAt: existing.createdAt,
|
||||
toolsVersion: state.gateway?.toolsVersion,
|
||||
delegations: state.gateway?.delegations ?? [],
|
||||
};
|
||||
this.#registerRunner(existing);
|
||||
await this.#persist();
|
||||
return;
|
||||
} catch (error) {
|
||||
if (explicitMainThread) {
|
||||
throw error;
|
||||
}
|
||||
forceCreateGatewayThread = true;
|
||||
this.#debug("gateway.session.recreateAfterResumeFailure", {
|
||||
codexThreadId: existing.codexThreadId,
|
||||
error: errorMessage(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (existing) {
|
||||
state.sessions = state.sessions.filter((session) => session !== existing);
|
||||
|
|
@ -725,10 +743,12 @@ export class DiscordCodexBridge {
|
|||
}
|
||||
|
||||
const configuredThreadId =
|
||||
gatewayConfig.mainThreadId ??
|
||||
(state.gateway?.toolsVersion === gatewayToolsVersion
|
||||
? state.gateway.mainThreadId
|
||||
: undefined);
|
||||
forceCreateGatewayThread
|
||||
? undefined
|
||||
: gatewayConfig.mainThreadId ??
|
||||
(state.gateway?.toolsVersion === gatewayToolsVersion
|
||||
? state.gateway.mainThreadId
|
||||
: undefined);
|
||||
const title = "Codex Gateway";
|
||||
const started = configuredThreadId
|
||||
? await this.client.resumeThread(this.#threadResumeParams(
|
||||
|
|
|
|||
|
|
@ -352,6 +352,68 @@ describe("DiscordCodexBridge", () => {
|
|||
await bridge.stop();
|
||||
});
|
||||
|
||||
test("recreates a tool-enabled gateway session when resume reports thread not found", async () => {
|
||||
const client = new FakeCodexClient();
|
||||
client.failedResumeThreadIds.add("missing-codex-thread");
|
||||
const transport = new FakeDiscordTransport();
|
||||
const store = new MemoryStateStore({
|
||||
...emptyState(),
|
||||
gateway: {
|
||||
homeChannelId: "home-channel",
|
||||
mainThreadId: "missing-codex-thread",
|
||||
createdAt: "2026-05-13T00:00:00.000Z",
|
||||
toolsVersion: 1,
|
||||
delegations: [],
|
||||
},
|
||||
sessions: [
|
||||
{
|
||||
discordThreadId: "home-channel",
|
||||
parentChannelId: "home-channel",
|
||||
codexThreadId: "missing-codex-thread",
|
||||
title: "Codex Gateway",
|
||||
createdAt: "2026-05-13T00:00:00.000Z",
|
||||
cwd: "/workspace",
|
||||
mode: "gateway",
|
||||
},
|
||||
],
|
||||
});
|
||||
const bridge = new DiscordCodexBridge({
|
||||
client,
|
||||
transport,
|
||||
store,
|
||||
config: testConfig({
|
||||
gateway: { homeChannelId: "home-channel" },
|
||||
}),
|
||||
});
|
||||
|
||||
await bridge.start();
|
||||
await waitFor(() => bridge.stateForTest().gateway?.mainThreadId === "codex-thread-1");
|
||||
|
||||
expect(client.resumeThreadCalls[0]).toEqual(
|
||||
expect.objectContaining({ threadId: "missing-codex-thread" }),
|
||||
);
|
||||
expect(client.startThreadCalls).toHaveLength(1);
|
||||
expect(client.startThreadCalls[0]?.dynamicTools).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ namespace: "codex_gateway" }),
|
||||
]),
|
||||
);
|
||||
expect(bridge.stateForTest().gateway).toEqual(
|
||||
expect.objectContaining({
|
||||
mainThreadId: "codex-thread-1",
|
||||
toolsVersion: 1,
|
||||
}),
|
||||
);
|
||||
expect(bridge.stateForTest().sessions.filter((session) =>
|
||||
session.mode === "gateway"
|
||||
)).toEqual([
|
||||
expect.objectContaining({
|
||||
codexThreadId: "codex-thread-1",
|
||||
}),
|
||||
]);
|
||||
await bridge.stop();
|
||||
});
|
||||
|
||||
test("routes bot mentions in the home channel to the gateway instead of creating threads", async () => {
|
||||
const client = new FakeCodexClient();
|
||||
const transport = new FakeDiscordTransport();
|
||||
|
|
@ -2600,6 +2662,7 @@ class FakeCodexClient implements CodexBridgeClient {
|
|||
threadTurns = new Map<string, v2.Turn[]>();
|
||||
threadCwds = new Map<string, string>();
|
||||
threadGoals = new Map<string, v2.ThreadGoal | null>();
|
||||
failedResumeThreadIds = new Set<string>();
|
||||
blockStartTurn = false;
|
||||
#startTurnResolvers: Array<() => void> = [];
|
||||
#notificationListeners: Array<(message: JsonRpcNotification) => void> = [];
|
||||
|
|
@ -2641,6 +2704,9 @@ class FakeCodexClient implements CodexBridgeClient {
|
|||
|
||||
async resumeThread(params: v2.ThreadResumeParams): Promise<v2.ThreadResumeResponse> {
|
||||
this.resumeThreadCalls.push(params);
|
||||
if (this.failedResumeThreadIds.has(params.threadId)) {
|
||||
throw new Error(`thread not found: ${params.threadId}`);
|
||||
}
|
||||
const cwd = params.cwd ?? this.threadCwds.get(params.threadId) ?? "/workspace";
|
||||
return {
|
||||
cwd,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue