@automagik/genie 4.260409.2 → 4.260409.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/dist/genie.js +46 -38
- package/package.json +1 -1
- package/plugins/genie/.claude-plugin/plugin.json +1 -1
- package/plugins/genie/package.json +1 -1
- package/src/lib/agent-directory.ts +16 -0
- package/src/lib/agent-sync.ts +21 -1
- package/src/lib/frontmatter.test.ts +120 -0
- package/src/lib/frontmatter.ts +13 -0
- package/src/lib/provider-adapters.ts +22 -4
- package/src/lib/providers/__tests__/claude-sdk-permissions.test.ts +124 -8
- package/src/lib/providers/claude-sdk-permissions.ts +55 -2
- package/src/services/executor.ts +6 -1
- package/src/services/executors/__tests__/claude-sdk.test.ts +21 -12
- package/src/services/executors/claude-code.test.ts +56 -22
- package/src/services/executors/claude-code.ts +136 -47
- package/src/services/executors/claude-sdk.ts +24 -9
- package/src/services/executors/turn-based-prompt.ts +16 -26
- package/src/services/omni-bridge.ts +66 -3
|
@@ -536,6 +536,8 @@ export class OmniBridge {
|
|
|
536
536
|
parsed.chatId = parsed.chatId || parts[3];
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
+
console.log(`[omni-bridge] NATS message received: ${msg.subject} agent=${parsed.agent} chat=${parsed.chatId}`);
|
|
540
|
+
|
|
539
541
|
if (!parsed.chatId || !parsed.agent) {
|
|
540
542
|
console.warn('[omni-bridge] Dropping message: missing chatId or agent', msg.subject);
|
|
541
543
|
continue;
|
|
@@ -549,7 +551,11 @@ export class OmniBridge {
|
|
|
549
551
|
const env = (raw.env as Record<string, string>) ?? {};
|
|
550
552
|
await this.queue.enqueue(parsed, env);
|
|
551
553
|
} else {
|
|
554
|
+
const key = `${parsed.agent}:${parsed.chatId}`;
|
|
555
|
+
const hasSession = this.sessions.has(key);
|
|
556
|
+
console.log(`[omni-bridge] Routing message for ${key} (hasSession=${hasSession}, queue=${!!this.queue})`);
|
|
552
557
|
await this.routeMessage(parsed);
|
|
558
|
+
console.log(`[omni-bridge] routeMessage done for ${key}`);
|
|
553
559
|
}
|
|
554
560
|
} catch (err) {
|
|
555
561
|
console.error('[omni-bridge] Error processing message:', err);
|
|
@@ -570,8 +576,23 @@ export class OmniBridge {
|
|
|
570
576
|
const instanceId = parts[3];
|
|
571
577
|
const chatId = parts.slice(4).join('.'); // chatId may contain dots
|
|
572
578
|
|
|
573
|
-
|
|
574
|
-
|
|
579
|
+
console.log(`[omni-bridge] Turn event: ${eventType} instance=${instanceId} chat=${chatId}`);
|
|
580
|
+
let sessionKey = this.findSessionKey(instanceId, chatId);
|
|
581
|
+
|
|
582
|
+
// Fallback: if chatId didn't match (e.g. LID vs phone mismatch),
|
|
583
|
+
// try finding the session by turnId from the payload.
|
|
584
|
+
if (!sessionKey && payload.turnId) {
|
|
585
|
+
sessionKey = this.findSessionKeyByTurnId(payload.turnId);
|
|
586
|
+
if (sessionKey) {
|
|
587
|
+
console.log(`[omni-bridge] Matched session via turnId fallback: ${sessionKey}`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (sessionKey) {
|
|
592
|
+
await this.routeTurnEvent(eventType, sessionKey, payload);
|
|
593
|
+
} else {
|
|
594
|
+
console.log(`[omni-bridge] No session found for turn.${eventType} (instance=${instanceId}, chat=${chatId})`);
|
|
595
|
+
}
|
|
575
596
|
} catch (err) {
|
|
576
597
|
console.warn('[omni-bridge] Error processing turn event:', err);
|
|
577
598
|
}
|
|
@@ -589,6 +610,7 @@ export class OmniBridge {
|
|
|
589
610
|
break;
|
|
590
611
|
case 'done':
|
|
591
612
|
this.turnTracker.close(sessionKey, payload.action);
|
|
613
|
+
await this.handleTurnDone(sessionKey);
|
|
592
614
|
break;
|
|
593
615
|
case 'nudge':
|
|
594
616
|
await this.handleTurnNudge(sessionKey, payload.message);
|
|
@@ -604,16 +626,31 @@ export class OmniBridge {
|
|
|
604
626
|
* Scans the sessions Map for a matching entry since the Map is keyed by
|
|
605
627
|
* `${agentName}:${chatId}` but we need to match by instanceId+chatId.
|
|
606
628
|
*/
|
|
629
|
+
private findSessionKeyByTurnId(turnId: string): string | undefined {
|
|
630
|
+
for (const [key] of this.sessions) {
|
|
631
|
+
if (this.turnTracker.getTurnId(key) === turnId) return key;
|
|
632
|
+
}
|
|
633
|
+
return undefined;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** Map internal chat UUID → external chatId, populated on turn.open events. */
|
|
637
|
+
private chatIdMap = new Map<string, string>();
|
|
638
|
+
|
|
607
639
|
private findSessionKey(instanceId: string, chatId: string): string | undefined {
|
|
640
|
+
// If chatId is an internal UUID, try resolving to external via the map
|
|
641
|
+
const resolvedChatId = this.chatIdMap.get(chatId);
|
|
642
|
+
|
|
608
643
|
for (const [key, entry] of this.sessions) {
|
|
609
644
|
if (entry.instanceId !== instanceId) continue;
|
|
610
645
|
// Live entry — match against the spawned session's chatId.
|
|
611
646
|
if (entry.session?.chatId === chatId) return key;
|
|
647
|
+
if (resolvedChatId && entry.session?.chatId === resolvedChatId) return key;
|
|
612
648
|
// Spawning entry — session is null, so match against the map key suffix
|
|
613
649
|
// (`${agent}:${chatId}`). Without this fallback, a reset arriving in the
|
|
614
650
|
// narrow window between placeholder insertion and spawn completion would
|
|
615
651
|
// be misclassified as a cold chat and ignored.
|
|
616
652
|
if (entry.spawning && key.endsWith(`:${chatId}`)) return key;
|
|
653
|
+
if (resolvedChatId && entry.spawning && key.endsWith(`:${resolvedChatId}`)) return key;
|
|
617
654
|
}
|
|
618
655
|
return undefined;
|
|
619
656
|
}
|
|
@@ -718,6 +755,20 @@ export class OmniBridge {
|
|
|
718
755
|
await this.drainQueue();
|
|
719
756
|
}
|
|
720
757
|
|
|
758
|
+
/**
|
|
759
|
+
* Handle a turn.done event — the agent called `omni done`.
|
|
760
|
+
* The turn is closed but the SESSION stays alive. The next inbound message
|
|
761
|
+
* will be delivered to the same session (same tmux pane / SDK conversation).
|
|
762
|
+
* Session teardown only happens on idle timeout or explicit reset.
|
|
763
|
+
*/
|
|
764
|
+
private async handleTurnDone(sessionKey: string): Promise<void> {
|
|
765
|
+
const entry = this.sessions.get(sessionKey);
|
|
766
|
+
if (!entry?.session) return;
|
|
767
|
+
console.log(`[omni-bridge] Turn done for ${sessionKey}, session stays alive for next message`);
|
|
768
|
+
// Reset the idle timer — the session is idle until the next message arrives
|
|
769
|
+
this.resetIdleTimer(sessionKey);
|
|
770
|
+
}
|
|
771
|
+
|
|
721
772
|
/**
|
|
722
773
|
* Handle a turn timeout event — evict the session and shut down the executor.
|
|
723
774
|
*/
|
|
@@ -778,6 +829,17 @@ export class OmniBridge {
|
|
|
778
829
|
private async spawnSession(message: OmniMessage): Promise<void> {
|
|
779
830
|
const key = `${message.agent}:${message.chatId}`;
|
|
780
831
|
|
|
832
|
+
// Guard: if a session or spawning placeholder already exists, buffer instead of double-spawning.
|
|
833
|
+
// This prevents the race where two concurrent messages for the same chatId both call spawnSession.
|
|
834
|
+
const existing = this.sessions.get(key);
|
|
835
|
+
if (existing) {
|
|
836
|
+
if (existing.buffer.length < MAX_BUFFER_PER_CHAT) {
|
|
837
|
+
existing.buffer.push(message);
|
|
838
|
+
console.log(`[omni-bridge] Buffered message for existing session ${key} (buffer=${existing.buffer.length})`);
|
|
839
|
+
}
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
|
|
781
843
|
// Check concurrency limit (count spawning entries too to prevent oversubscription)
|
|
782
844
|
const activeCount = this.sessions.size;
|
|
783
845
|
if (activeCount >= this.maxConcurrent) {
|
|
@@ -810,10 +872,11 @@ export class OmniBridge {
|
|
|
810
872
|
OMNI_CHAT: payloadEnv?.OMNI_CHAT ?? message.chatId,
|
|
811
873
|
OMNI_MESSAGE: payloadEnv?.OMNI_MESSAGE ?? (raw.messageId as string) ?? '',
|
|
812
874
|
OMNI_TURN_ID: payloadEnv?.OMNI_TURN_ID ?? '',
|
|
875
|
+
OMNI_SENDER_NAME: payloadEnv?.OMNI_SENDER_NAME ?? message.sender ?? '',
|
|
813
876
|
};
|
|
814
877
|
|
|
815
878
|
console.log(`[omni-bridge] Spawning session for ${key}...`);
|
|
816
|
-
const session = await this.executor.spawn(message.agent, message.chatId, spawnEnv);
|
|
879
|
+
const session = await this.executor.spawn(message.agent, message.chatId, spawnEnv, message.content);
|
|
817
880
|
|
|
818
881
|
// Reset arrived while spawn was in flight — tear down the freshly-created
|
|
819
882
|
// session and bail. The placeholder was already removed from the map by
|