replicas-engine 0.1.16 → 0.1.17
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/dist/src/index.js +212 -40
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -490,13 +490,120 @@ function getGitStatus(workingDirectory) {
|
|
|
490
490
|
};
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
+
// src/services/message-queue.ts
|
|
494
|
+
var MessageQueue = class {
|
|
495
|
+
queue = [];
|
|
496
|
+
processing = false;
|
|
497
|
+
currentMessageId = null;
|
|
498
|
+
messageIdCounter = 0;
|
|
499
|
+
processMessage;
|
|
500
|
+
constructor(processMessage) {
|
|
501
|
+
this.processMessage = processMessage;
|
|
502
|
+
}
|
|
503
|
+
generateMessageId() {
|
|
504
|
+
return `msg_${Date.now()}_${++this.messageIdCounter}`;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Add a message to the queue or start processing immediately if not busy
|
|
508
|
+
* @returns Object indicating whether the message was queued or started processing
|
|
509
|
+
*/
|
|
510
|
+
async enqueue(message, model, customInstructions) {
|
|
511
|
+
const messageId = this.generateMessageId();
|
|
512
|
+
const queuedMessage = {
|
|
513
|
+
id: messageId,
|
|
514
|
+
message,
|
|
515
|
+
model,
|
|
516
|
+
customInstructions,
|
|
517
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
518
|
+
};
|
|
519
|
+
if (this.processing) {
|
|
520
|
+
this.queue.push(queuedMessage);
|
|
521
|
+
return {
|
|
522
|
+
queued: true,
|
|
523
|
+
messageId,
|
|
524
|
+
position: this.queue.length
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
this.startProcessing(queuedMessage);
|
|
528
|
+
return {
|
|
529
|
+
queued: false,
|
|
530
|
+
messageId,
|
|
531
|
+
position: 0
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
async startProcessing(queuedMessage) {
|
|
535
|
+
this.processing = true;
|
|
536
|
+
this.currentMessageId = queuedMessage.id;
|
|
537
|
+
try {
|
|
538
|
+
await this.processMessage(
|
|
539
|
+
queuedMessage.message,
|
|
540
|
+
queuedMessage.model,
|
|
541
|
+
queuedMessage.customInstructions
|
|
542
|
+
);
|
|
543
|
+
} catch (error) {
|
|
544
|
+
console.error("[MessageQueue] Error processing message:", error);
|
|
545
|
+
} finally {
|
|
546
|
+
this.processing = false;
|
|
547
|
+
this.currentMessageId = null;
|
|
548
|
+
await this.processNextInQueue();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async processNextInQueue() {
|
|
552
|
+
const nextMessage = this.queue.shift();
|
|
553
|
+
if (nextMessage) {
|
|
554
|
+
await this.startProcessing(nextMessage);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Check if currently processing a message
|
|
559
|
+
*/
|
|
560
|
+
isProcessing() {
|
|
561
|
+
return this.processing;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Get the current queue length
|
|
565
|
+
*/
|
|
566
|
+
getQueueLength() {
|
|
567
|
+
return this.queue.length;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Get full queue status
|
|
571
|
+
*/
|
|
572
|
+
getStatus() {
|
|
573
|
+
return {
|
|
574
|
+
isProcessing: this.processing,
|
|
575
|
+
queueLength: this.queue.length,
|
|
576
|
+
currentMessageId: this.currentMessageId,
|
|
577
|
+
queuedMessages: this.queue.map((msg, index) => ({
|
|
578
|
+
id: msg.id,
|
|
579
|
+
queuedAt: msg.queuedAt,
|
|
580
|
+
position: index + 1
|
|
581
|
+
}))
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Clear the queue (does not stop current processing)
|
|
586
|
+
*/
|
|
587
|
+
clearQueue() {
|
|
588
|
+
this.queue = [];
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Reset everything including clearing processing state
|
|
592
|
+
*/
|
|
593
|
+
reset() {
|
|
594
|
+
this.queue = [];
|
|
595
|
+
this.processing = false;
|
|
596
|
+
this.currentMessageId = null;
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
|
|
493
600
|
// src/services/codex-manager.ts
|
|
494
601
|
var CodexManager = class {
|
|
495
602
|
codex;
|
|
496
603
|
currentThreadId = null;
|
|
497
604
|
currentThread = null;
|
|
498
605
|
workingDirectory;
|
|
499
|
-
|
|
606
|
+
messageQueue;
|
|
500
607
|
constructor(workingDirectory) {
|
|
501
608
|
this.codex = new Codex();
|
|
502
609
|
if (workingDirectory) {
|
|
@@ -510,14 +617,38 @@ var CodexManager = class {
|
|
|
510
617
|
this.workingDirectory = workspaceHome;
|
|
511
618
|
}
|
|
512
619
|
}
|
|
620
|
+
this.messageQueue = new MessageQueue(this.processMessageInternal.bind(this));
|
|
513
621
|
}
|
|
514
622
|
isProcessing() {
|
|
515
|
-
return this.
|
|
623
|
+
return this.messageQueue.isProcessing();
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Enqueue a message for processing. If not currently processing, starts immediately.
|
|
627
|
+
* If already processing, adds to queue.
|
|
628
|
+
* @returns Object with queued status, messageId, and position in queue
|
|
629
|
+
*/
|
|
630
|
+
async enqueueMessage(message, model, customInstructions) {
|
|
631
|
+
return this.messageQueue.enqueue(message, model, customInstructions);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Get the current queue status
|
|
635
|
+
*/
|
|
636
|
+
getQueueStatus() {
|
|
637
|
+
return this.messageQueue.getStatus();
|
|
516
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Legacy sendMessage method - now uses the queue internally
|
|
641
|
+
* @deprecated Use enqueueMessage for better control over queue status
|
|
642
|
+
*/
|
|
517
643
|
async sendMessage(message, model, customInstructions) {
|
|
644
|
+
await this.enqueueMessage(message, model, customInstructions);
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Internal method that actually processes the message
|
|
648
|
+
*/
|
|
649
|
+
async processMessageInternal(message, model, customInstructions) {
|
|
518
650
|
const linearSessionId = process.env.LINEAR_SESSION_ID;
|
|
519
651
|
try {
|
|
520
|
-
this.processing = true;
|
|
521
652
|
if (!this.currentThread) {
|
|
522
653
|
if (this.currentThreadId) {
|
|
523
654
|
this.currentThread = this.codex.resumeThread(this.currentThreadId, {
|
|
@@ -559,7 +690,6 @@ var CodexManager = class {
|
|
|
559
690
|
}
|
|
560
691
|
}
|
|
561
692
|
} finally {
|
|
562
|
-
this.processing = false;
|
|
563
693
|
if (linearSessionId) {
|
|
564
694
|
const status = getGitStatus(this.workingDirectory);
|
|
565
695
|
monolithService.sendEvent({ type: "agent_turn_complete", payload: { linearSessionId, status } }).catch(() => {
|
|
@@ -602,7 +732,7 @@ var CodexManager = class {
|
|
|
602
732
|
async reset() {
|
|
603
733
|
this.currentThread = null;
|
|
604
734
|
this.currentThreadId = null;
|
|
605
|
-
this.
|
|
735
|
+
this.messageQueue.reset();
|
|
606
736
|
}
|
|
607
737
|
getThreadId() {
|
|
608
738
|
return this.currentThreadId;
|
|
@@ -684,21 +814,15 @@ codex.post("/send", async (c) => {
|
|
|
684
814
|
if (!message || typeof message !== "string") {
|
|
685
815
|
return c.json({ error: "Message is required and must be a string" }, 400);
|
|
686
816
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
{
|
|
690
|
-
error: "A turn is already in progress. Please wait."
|
|
691
|
-
},
|
|
692
|
-
400
|
|
693
|
-
);
|
|
694
|
-
}
|
|
695
|
-
codexManager.sendMessage(message, model, customInstructions).catch((error) => {
|
|
696
|
-
console.error("[Codex Route] Error in background message processing:", error);
|
|
697
|
-
});
|
|
698
|
-
return c.json({
|
|
817
|
+
const result = await codexManager.enqueueMessage(message, model, customInstructions);
|
|
818
|
+
const response = {
|
|
699
819
|
success: true,
|
|
700
|
-
message: "Message sent successfully"
|
|
701
|
-
|
|
820
|
+
message: result.queued ? `Message queued at position ${result.position}` : "Message sent successfully",
|
|
821
|
+
queued: result.queued,
|
|
822
|
+
messageId: result.messageId,
|
|
823
|
+
position: result.position
|
|
824
|
+
};
|
|
825
|
+
return c.json(response);
|
|
702
826
|
} catch (error) {
|
|
703
827
|
console.error("Error in /codex/send:", error);
|
|
704
828
|
return c.json(
|
|
@@ -766,6 +890,21 @@ codex.get("/status", async (c) => {
|
|
|
766
890
|
);
|
|
767
891
|
}
|
|
768
892
|
});
|
|
893
|
+
codex.get("/queue", async (c) => {
|
|
894
|
+
try {
|
|
895
|
+
const queueStatus = codexManager.getQueueStatus();
|
|
896
|
+
return c.json(queueStatus);
|
|
897
|
+
} catch (error) {
|
|
898
|
+
console.error("Error in /codex/queue:", error);
|
|
899
|
+
return c.json(
|
|
900
|
+
{
|
|
901
|
+
error: "Failed to retrieve queue status",
|
|
902
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
903
|
+
},
|
|
904
|
+
500
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
});
|
|
769
908
|
codex.post("/reset", async (c) => {
|
|
770
909
|
try {
|
|
771
910
|
await codexManager.reset();
|
|
@@ -801,7 +940,7 @@ var ClaudeManager = class {
|
|
|
801
940
|
historyFile;
|
|
802
941
|
sessionId = null;
|
|
803
942
|
initialized;
|
|
804
|
-
|
|
943
|
+
messageQueue;
|
|
805
944
|
constructor(workingDirectory) {
|
|
806
945
|
if (workingDirectory) {
|
|
807
946
|
this.workingDirectory = workingDirectory;
|
|
@@ -816,17 +955,42 @@ var ClaudeManager = class {
|
|
|
816
955
|
}
|
|
817
956
|
this.historyFile = join2(homedir2(), ".replicas", "claude", "history.jsonl");
|
|
818
957
|
this.initialized = this.initialize();
|
|
958
|
+
this.messageQueue = new MessageQueue(this.processMessageInternal.bind(this));
|
|
819
959
|
}
|
|
820
960
|
isProcessing() {
|
|
821
|
-
return this.
|
|
961
|
+
return this.messageQueue.isProcessing();
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Enqueue a message for processing. If not currently processing, starts immediately.
|
|
965
|
+
* If already processing, adds to queue.
|
|
966
|
+
* @returns Object with queued status, messageId, and position in queue
|
|
967
|
+
*/
|
|
968
|
+
async enqueueMessage(message, model, customInstructions) {
|
|
969
|
+
await this.initialized;
|
|
970
|
+
return this.messageQueue.enqueue(message, model, customInstructions);
|
|
822
971
|
}
|
|
972
|
+
/**
|
|
973
|
+
* Get the current queue status
|
|
974
|
+
*/
|
|
975
|
+
getQueueStatus() {
|
|
976
|
+
return this.messageQueue.getStatus();
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Legacy sendMessage method - now uses the queue internally
|
|
980
|
+
* @deprecated Use enqueueMessage for better control over queue status
|
|
981
|
+
*/
|
|
823
982
|
async sendMessage(message, model, customInstructions) {
|
|
983
|
+
await this.enqueueMessage(message, model, customInstructions);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Internal method that actually processes the message
|
|
987
|
+
*/
|
|
988
|
+
async processMessageInternal(message, model, customInstructions) {
|
|
824
989
|
const linearSessionId = process.env.LINEAR_SESSION_ID;
|
|
825
990
|
if (!message || !message.trim()) {
|
|
826
991
|
throw new Error("Message cannot be empty");
|
|
827
992
|
}
|
|
828
993
|
await this.initialized;
|
|
829
|
-
this.processing = true;
|
|
830
994
|
try {
|
|
831
995
|
const userMessage = {
|
|
832
996
|
type: "user",
|
|
@@ -874,7 +1038,6 @@ var ClaudeManager = class {
|
|
|
874
1038
|
}
|
|
875
1039
|
}
|
|
876
1040
|
} finally {
|
|
877
|
-
this.processing = false;
|
|
878
1041
|
if (linearSessionId) {
|
|
879
1042
|
const status = getGitStatus(this.workingDirectory);
|
|
880
1043
|
monolithService.sendEvent({ type: "agent_turn_complete", payload: { linearSessionId, status } }).catch(() => {
|
|
@@ -893,7 +1056,7 @@ var ClaudeManager = class {
|
|
|
893
1056
|
async getStatus() {
|
|
894
1057
|
await this.initialized;
|
|
895
1058
|
const status = {
|
|
896
|
-
has_active_thread: this.
|
|
1059
|
+
has_active_thread: this.messageQueue.isProcessing(),
|
|
897
1060
|
thread_id: this.sessionId,
|
|
898
1061
|
working_directory: this.workingDirectory
|
|
899
1062
|
};
|
|
@@ -903,13 +1066,13 @@ var ClaudeManager = class {
|
|
|
903
1066
|
await this.initialized;
|
|
904
1067
|
const allEvents = await readJSONL(this.historyFile);
|
|
905
1068
|
const events = allEvents.filter((event) => event.timestamp > since);
|
|
906
|
-
const isComplete = !this.
|
|
1069
|
+
const isComplete = !this.messageQueue.isProcessing();
|
|
907
1070
|
return { events, isComplete };
|
|
908
1071
|
}
|
|
909
1072
|
async reset() {
|
|
910
1073
|
await this.initialized;
|
|
911
1074
|
this.sessionId = null;
|
|
912
|
-
this.
|
|
1075
|
+
this.messageQueue.reset();
|
|
913
1076
|
try {
|
|
914
1077
|
await rm(this.historyFile, { force: true });
|
|
915
1078
|
} catch {
|
|
@@ -946,21 +1109,15 @@ claude.post("/send", async (c) => {
|
|
|
946
1109
|
if (!message || typeof message !== "string") {
|
|
947
1110
|
return c.json({ error: "Message is required and must be a string" }, 400);
|
|
948
1111
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
{
|
|
952
|
-
error: "A turn is already in progress. Please wait."
|
|
953
|
-
},
|
|
954
|
-
400
|
|
955
|
-
);
|
|
956
|
-
}
|
|
957
|
-
claudeManager.sendMessage(message, model, customInstructions).catch((error) => {
|
|
958
|
-
console.error("[Claude Route] Error in background message processing:", error);
|
|
959
|
-
});
|
|
960
|
-
return c.json({
|
|
1112
|
+
const result = await claudeManager.enqueueMessage(message, model, customInstructions);
|
|
1113
|
+
const response = {
|
|
961
1114
|
success: true,
|
|
962
|
-
message: "Message sent successfully"
|
|
963
|
-
|
|
1115
|
+
message: result.queued ? `Message queued at position ${result.position}` : "Message sent successfully",
|
|
1116
|
+
queued: result.queued,
|
|
1117
|
+
messageId: result.messageId,
|
|
1118
|
+
position: result.position
|
|
1119
|
+
};
|
|
1120
|
+
return c.json(response);
|
|
964
1121
|
} catch (error) {
|
|
965
1122
|
console.error("Error in /claude/send:", error);
|
|
966
1123
|
return c.json(
|
|
@@ -1028,6 +1185,21 @@ claude.get("/status", async (c) => {
|
|
|
1028
1185
|
);
|
|
1029
1186
|
}
|
|
1030
1187
|
});
|
|
1188
|
+
claude.get("/queue", async (c) => {
|
|
1189
|
+
try {
|
|
1190
|
+
const queueStatus = claudeManager.getQueueStatus();
|
|
1191
|
+
return c.json(queueStatus);
|
|
1192
|
+
} catch (error) {
|
|
1193
|
+
console.error("Error in /claude/queue:", error);
|
|
1194
|
+
return c.json(
|
|
1195
|
+
{
|
|
1196
|
+
error: "Failed to retrieve queue status",
|
|
1197
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
1198
|
+
},
|
|
1199
|
+
500
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1031
1203
|
claude.post("/reset", async (c) => {
|
|
1032
1204
|
try {
|
|
1033
1205
|
await claudeManager.reset();
|