Refresh stale Discord gateway sessions
This commit is contained in:
parent
bec3641b1c
commit
9d64886259
5 changed files with 82 additions and 4 deletions
|
|
@ -28,6 +28,7 @@ import type {
|
|||
} from "./types.ts";
|
||||
|
||||
const maxDiscordMessageLength = 2000;
|
||||
const gatewayToolsVersion = 1;
|
||||
|
||||
type ThreadSnapshot = {
|
||||
terminalTurnIds: string[];
|
||||
|
|
@ -572,7 +573,7 @@ export class DiscordCodexBridge {
|
|||
`Delegations: ${delegations.length} tracked, ${activeDelegations.length} active`,
|
||||
"",
|
||||
"**Delegation Backend**",
|
||||
`Status: ${session ? "privileged gateway tools available to the main Codex operator thread" : "waiting for main Codex operator thread"}.`,
|
||||
`Status: ${state.gateway?.toolsVersion === gatewayToolsVersion ? "privileged gateway tools available to the main Codex operator thread" : "waiting for a tool-enabled main Codex operator thread"}.`,
|
||||
`Flow backend: \`${this.config.flowBackendUrl ?? "not configured"}\``,
|
||||
"",
|
||||
"**Detail Threads**",
|
||||
|
|
@ -701,22 +702,33 @@ export class DiscordCodexBridge {
|
|||
}
|
||||
const state = this.#requireState();
|
||||
const existing = this.#gatewaySession();
|
||||
if (existing) {
|
||||
const shouldReuseExisting =
|
||||
Boolean(gatewayConfig.mainThreadId) ||
|
||||
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;
|
||||
}
|
||||
if (existing) {
|
||||
state.sessions = state.sessions.filter((session) => session !== existing);
|
||||
this.#runnersByDiscordThread.delete(existing.discordThreadId);
|
||||
this.#runnersByCodexThread.delete(existing.codexThreadId);
|
||||
}
|
||||
|
||||
const configuredThreadId =
|
||||
state.gateway?.mainThreadId ??
|
||||
gatewayConfig.mainThreadId;
|
||||
gatewayConfig.mainThreadId ??
|
||||
(state.gateway?.toolsVersion === gatewayToolsVersion
|
||||
? state.gateway.mainThreadId
|
||||
: undefined);
|
||||
const title = "Codex Gateway";
|
||||
const started = configuredThreadId
|
||||
? await this.client.resumeThread(this.#threadResumeParams(
|
||||
|
|
@ -747,6 +759,9 @@ export class DiscordCodexBridge {
|
|||
homeChannelId: gatewayConfig.homeChannelId,
|
||||
mainThreadId: codexThreadId,
|
||||
createdAt: session.createdAt,
|
||||
toolsVersion: configuredThreadId
|
||||
? state.gateway?.toolsVersion
|
||||
: gatewayToolsVersion,
|
||||
delegations: state.gateway?.delegations ?? [],
|
||||
};
|
||||
state.sessions.push(session);
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ function parseGateway(value: unknown): DiscordGatewayState | undefined {
|
|||
mainThreadId: optionalString(value.mainThreadId),
|
||||
statusMessageId: optionalString(value.statusMessageId),
|
||||
createdAt: optionalString(value.createdAt),
|
||||
toolsVersion: optionalNumber(value.toolsVersion),
|
||||
delegations: Array.isArray(value.delegations)
|
||||
? value.delegations.map(parseGatewayDelegation)
|
||||
: [],
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ export type DiscordGatewayState = {
|
|||
mainThreadId?: string;
|
||||
statusMessageId?: string;
|
||||
createdAt?: string;
|
||||
toolsVersion?: number;
|
||||
delegations: DiscordGatewayDelegation[];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ describe("DiscordCodexBridge", () => {
|
|||
expect.objectContaining({
|
||||
homeChannelId: "home-channel",
|
||||
mainThreadId: "codex-thread-1",
|
||||
toolsVersion: 1,
|
||||
}),
|
||||
);
|
||||
expect(bridge.stateForTest().sessions[0]).toEqual(
|
||||
|
|
@ -293,6 +294,64 @@ describe("DiscordCodexBridge", () => {
|
|||
await bridge.stop();
|
||||
});
|
||||
|
||||
test("replaces stale persisted gateway sessions when no main thread is configured", async () => {
|
||||
const client = new FakeCodexClient();
|
||||
const transport = new FakeDiscordTransport();
|
||||
const store = new MemoryStateStore({
|
||||
...emptyState(),
|
||||
gateway: {
|
||||
homeChannelId: "home-channel",
|
||||
mainThreadId: "old-codex-thread",
|
||||
createdAt: "2026-05-13T00:00:00.000Z",
|
||||
delegations: [],
|
||||
},
|
||||
sessions: [
|
||||
{
|
||||
discordThreadId: "home-channel",
|
||||
parentChannelId: "home-channel",
|
||||
codexThreadId: "old-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).toEqual([]);
|
||||
expect(client.startThreadCalls).toHaveLength(1);
|
||||
expect(client.startThreadCalls[0]?.dynamicTools).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ namespace: "codex_gateway" }),
|
||||
]),
|
||||
);
|
||||
expect(bridge.stateForTest().sessions.filter((session) =>
|
||||
session.mode === "gateway"
|
||||
)).toEqual([
|
||||
expect.objectContaining({
|
||||
codexThreadId: "codex-thread-1",
|
||||
}),
|
||||
]);
|
||||
expect(bridge.stateForTest().gateway).toEqual(
|
||||
expect.objectContaining({
|
||||
mainThreadId: "codex-thread-1",
|
||||
toolsVersion: 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();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ describe("JsonFileStateStore", () => {
|
|||
mainThreadId: "codex-gateway-thread",
|
||||
statusMessageId: "message-gateway-status",
|
||||
createdAt: "2026-05-11T00:00:00.000Z",
|
||||
toolsVersion: 1,
|
||||
delegations: [
|
||||
{
|
||||
id: "delegation-1",
|
||||
|
|
@ -86,6 +87,7 @@ describe("JsonFileStateStore", () => {
|
|||
mainThreadId: "codex-gateway-thread",
|
||||
statusMessageId: "message-gateway-status",
|
||||
createdAt: "2026-05-11T00:00:00.000Z",
|
||||
toolsVersion: 1,
|
||||
delegations: [
|
||||
{
|
||||
id: "delegation-1",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue