mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-15 10:59:32 +01:00
fix(discord): ignore gateway frames after stop, hold the typed hint while a choice is pending, name the Discord group in the settings reference
This commit is contained in:
1 parent
9f18b78857
commit
97c698b249
3 files changed
+32
-3
No files matched your search
@@ -213,8 +213,9 @@ how long a desktop notification stays up before Codeman closes it (**Browser
|
||||
notification display time**, default 8; both per device, and your OS may close a
|
||||
desktop notification sooner), the idle
|
||||
threshold that decides when a quiet session counts as needing you, and the server-wide
|
||||
webhook (ntfy, Slack, Discord or generic JSON; admins only in multi-user mode). See
|
||||
[Notifications And Approvals](Notifications-And-Approvals).
|
||||
webhook (ntfy, Slack, Discord or generic JSON; admins only in multi-user mode), and the
|
||||
Discord group for the two-way bot (token, IDs, thread and ping options; see
|
||||
[Discord Bridge](Discord-Bridge)). See [Notifications And Approvals](Notifications-And-Approvals).
|
||||
|
||||
### Voice
|
||||
|
||||
|
||||
@@ -535,6 +535,8 @@ export class DiscordGateway {
|
||||
}
|
||||
|
||||
private handle(sock: GatewaySocket, raw: string): void {
|
||||
// `ws` keeps emitting while a socket closes: a frame after stop() or from a replaced socket must start nothing.
|
||||
if (this.stopped || sock !== this.sock) return;
|
||||
let p: { op: number; d?: unknown; s?: number | null; t?: string | null };
|
||||
try {
|
||||
p = JSON.parse(raw);
|
||||
@@ -947,7 +949,10 @@ export class DiscordBridge {
|
||||
* Claude gets the same text as a launch flag instead, and a shell must never receive it.
|
||||
*/
|
||||
async sessionIdle(sessionId: string, hintable: boolean): Promise<void> {
|
||||
if (!this.fresh.delete(sessionId)) return;
|
||||
if (!this.fresh.has(sessionId)) return;
|
||||
// A dialog is up (codex's trust prompt reads as idle): Enter would pick its highlighted option, so wait for the next idle.
|
||||
if (this.deps.awaitingChoice?.(sessionId)) return;
|
||||
this.fresh.delete(sessionId);
|
||||
if (!hintable || !this.cfg.enabled || !this.cfg.startupHint || !this.cfg.typedHint) return;
|
||||
const text = sanitiseReply(`${DISCORD_STARTUP_HINT} Reply with just: ok`);
|
||||
// fromUser false: this is Codeman talking, so it must not auto-name the tab or count as the person's input.
|
||||
|
||||
@@ -247,6 +247,15 @@ describe('DiscordGateway', () => {
|
||||
gw.stop();
|
||||
});
|
||||
|
||||
it('ignores a frame that arrives after stop(): no heartbeat timer, nothing sent', () => {
|
||||
const { gw, sockets } = setup();
|
||||
gw.start();
|
||||
gw.stop();
|
||||
sockets[0].recv({ op: 10, d: { heartbeat_interval: 1000 } });
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
expect(sockets[0].sent).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('stops for good on a close code that retrying cannot fix', () => {
|
||||
const { gw, sockets } = setup();
|
||||
gw.start();
|
||||
@@ -920,6 +929,20 @@ describe('DiscordBridge files and startup hint', () => {
|
||||
b.stop();
|
||||
});
|
||||
|
||||
it('holds the startup hint while a choice is pending and sends it at the next idle', async () => {
|
||||
let choice = true;
|
||||
const b = await make({}, undefined, () => choice);
|
||||
await b.sessionCreated('s6', 'w6', t - 1000);
|
||||
await b.sessionIdle('s6', true);
|
||||
expect(sent).toHaveLength(0);
|
||||
choice = false;
|
||||
await b.sessionIdle('s6', true);
|
||||
expect(sent).toHaveLength(1);
|
||||
await b.sessionIdle('s6', true);
|
||||
expect(sent).toHaveLength(1);
|
||||
b.stop();
|
||||
});
|
||||
|
||||
it('says the agent has exited and types nothing when the tab reports it', async () => {
|
||||
const b = await make({}, undefined, undefined, () => true);
|
||||
await b.onGatewayMessage(reply({ content: 'hello', attachments: [] }));
|
||||
|
||||
Reference in new issue
Block a user