mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
feat(cli): wire Cursor /summarize and /clear slash builtins (#747)
* feat(cursor): wire /summarize and /clear slash builtins for remote sessions Seed cursor builtins for web autocomplete, parse summarize/clear in cursorRemoteLauncher (pass-through to agent -p; reject /clear with args). Fixes tiann/hapi#738 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): isolate slash commands before message queue batching Parse summarize/clear at enqueue time (runCursor) with pushIsolateAndClear so waitForMessagesAndGetAsString never merges a slash with the next prompt. Adds queue policy tests for invalid /clear + following message. Addresses PR #747 review. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): preserve pending messages when isolating slash commands pushIsolateAndClear() wipes the entire queue, so a normal prompt queued before /summarize or /clear would be silently dropped. Add pushIsolated() - isolation without clearing - and route Cursor slash commands through it instead. Adds queue tests covering the preserve-then-isolate path. Addresses PR #747 review. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -367,6 +367,28 @@ describe('MessageQueue2', () => {
|
||||
expect(batch1?.mode.type).toBe('A');
|
||||
});
|
||||
|
||||
it('should preserve pending messages when pushIsolated is used', async () => {
|
||||
const queue = new MessageQueue2<{ type: string }>((mode) => mode.type);
|
||||
|
||||
queue.push('message1', { type: 'A' });
|
||||
queue.push('message2', { type: 'A' });
|
||||
|
||||
queue.pushIsolated('isolated', { type: 'A' });
|
||||
|
||||
queue.push('message3', { type: 'A' });
|
||||
|
||||
const batch1 = await queue.waitForMessagesAndGetAsString();
|
||||
expect(batch1?.message).toBe('message1\nmessage2');
|
||||
expect(batch1?.isolate).toBe(false);
|
||||
|
||||
const batch2 = await queue.waitForMessagesAndGetAsString();
|
||||
expect(batch2?.message).toBe('isolated');
|
||||
expect(batch2?.isolate).toBe(true);
|
||||
|
||||
const batch3 = await queue.waitForMessagesAndGetAsString();
|
||||
expect(batch3?.message).toBe('message3');
|
||||
});
|
||||
|
||||
it('should isolate messages pushed with pushIsolateAndClear', async () => {
|
||||
const queue = new MessageQueue2<{ type: string }>((mode) => mode.type);
|
||||
|
||||
|
||||
@@ -107,6 +107,43 @@ export class MessageQueue2<T> {
|
||||
logger.debug(`[MessageQueue2] pushImmediate() completed. Queue size: ${this.queue.length}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a message that must be processed in isolation, preserving any
|
||||
* messages already queued ahead of it. The new message is never batched
|
||||
* with siblings (neither the ones before it, nor any that arrive after).
|
||||
* Use this when a slash command must run alone but earlier prompts must
|
||||
* still be delivered in order.
|
||||
*/
|
||||
pushIsolated(message: string, mode: T, localId?: string): void {
|
||||
if (this.closed) {
|
||||
throw new Error('Cannot push to closed queue');
|
||||
}
|
||||
|
||||
const modeHash = this.modeHasher(mode);
|
||||
logger.debug(`[MessageQueue2] pushIsolated() called with mode hash: ${modeHash} - preserving ${this.queue.length} pending messages`);
|
||||
|
||||
this.queue.push({
|
||||
message,
|
||||
mode,
|
||||
modeHash,
|
||||
localId,
|
||||
isolate: true
|
||||
});
|
||||
|
||||
if (this.onMessageHandler) {
|
||||
this.onMessageHandler(message, mode);
|
||||
}
|
||||
|
||||
if (this.waiter) {
|
||||
logger.debug(`[MessageQueue2] Notifying waiter for isolated message`);
|
||||
const waiter = this.waiter;
|
||||
this.waiter = null;
|
||||
waiter(true);
|
||||
}
|
||||
|
||||
logger.debug(`[MessageQueue2] pushIsolated() completed. Queue size: ${this.queue.length}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a message that must be processed in complete isolation.
|
||||
* Clears any pending messages and ensures this message is never batched with others.
|
||||
|
||||
Reference in New Issue
Block a user