replicas-engine 0.1.35 → 0.1.37
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 +183 -6
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -716,6 +716,21 @@ var MessageQueue = class {
|
|
|
716
716
|
clearQueue() {
|
|
717
717
|
this.queue = [];
|
|
718
718
|
}
|
|
719
|
+
drainQueue(options) {
|
|
720
|
+
const maxItems = options?.maxItems ?? this.queue.length;
|
|
721
|
+
const maxChars = options?.maxChars ?? Number.POSITIVE_INFINITY;
|
|
722
|
+
const drained = [];
|
|
723
|
+
let totalChars = 0;
|
|
724
|
+
for (const message of this.queue) {
|
|
725
|
+
if (drained.length >= maxItems) break;
|
|
726
|
+
const text = message.message;
|
|
727
|
+
if (totalChars + text.length > maxChars) break;
|
|
728
|
+
drained.push(text);
|
|
729
|
+
totalChars += text.length;
|
|
730
|
+
}
|
|
731
|
+
this.queue = [];
|
|
732
|
+
return drained;
|
|
733
|
+
}
|
|
719
734
|
/**
|
|
720
735
|
* Reset everything including clearing processing state
|
|
721
736
|
*/
|
|
@@ -1016,6 +1031,8 @@ var replicasConfigService = new ReplicasConfigService();
|
|
|
1016
1031
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
1017
1032
|
var DEFAULT_MODEL = "gpt-5.2-codex";
|
|
1018
1033
|
var CODEX_CONFIG_PATH = join3(homedir3(), ".codex", "config.toml");
|
|
1034
|
+
var MAX_INTERRUPT_QUEUE_ITEMS = 1e3;
|
|
1035
|
+
var MAX_INTERRUPT_QUEUE_CHARS = 2e5;
|
|
1019
1036
|
var CodexManager = class {
|
|
1020
1037
|
codex;
|
|
1021
1038
|
currentThreadId = null;
|
|
@@ -1025,6 +1042,7 @@ var CodexManager = class {
|
|
|
1025
1042
|
baseSystemPrompt;
|
|
1026
1043
|
tempImageDir;
|
|
1027
1044
|
initialized;
|
|
1045
|
+
activeAbortController = null;
|
|
1028
1046
|
constructor(workingDirectory) {
|
|
1029
1047
|
this.codex = new Codex();
|
|
1030
1048
|
if (workingDirectory) {
|
|
@@ -1067,6 +1085,33 @@ var CodexManager = class {
|
|
|
1067
1085
|
getQueueStatus() {
|
|
1068
1086
|
return this.messageQueue.getStatus();
|
|
1069
1087
|
}
|
|
1088
|
+
async interrupt() {
|
|
1089
|
+
const queue = this.messageQueue.drainQueue({
|
|
1090
|
+
maxItems: MAX_INTERRUPT_QUEUE_ITEMS,
|
|
1091
|
+
maxChars: MAX_INTERRUPT_QUEUE_CHARS
|
|
1092
|
+
});
|
|
1093
|
+
const isProcessing = this.messageQueue.isProcessing();
|
|
1094
|
+
if (isProcessing && this.activeAbortController) {
|
|
1095
|
+
try {
|
|
1096
|
+
this.activeAbortController.abort();
|
|
1097
|
+
} catch {
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
const linearSessionId = process.env.LINEAR_SESSION_ID;
|
|
1101
|
+
if (linearSessionId && queue.length > 0) {
|
|
1102
|
+
const body = ["Interrupted. Cleared queue:", ...queue].join("\n");
|
|
1103
|
+
const thoughtEvent = {
|
|
1104
|
+
linearSessionId,
|
|
1105
|
+
content: {
|
|
1106
|
+
type: "thought",
|
|
1107
|
+
body
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
monolithService.sendEvent({ type: "agent_update", payload: thoughtEvent }).catch(() => {
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
return { interrupted: isProcessing || queue.length > 0, queue };
|
|
1114
|
+
}
|
|
1070
1115
|
/**
|
|
1071
1116
|
* Set the base system prompt from replicas.json
|
|
1072
1117
|
* This will be combined with any custom instructions passed to individual messages
|
|
@@ -1172,11 +1217,13 @@ var CodexManager = class {
|
|
|
1172
1217
|
model: model || DEFAULT_MODEL,
|
|
1173
1218
|
webSearchMode: "live"
|
|
1174
1219
|
};
|
|
1220
|
+
const abortController = new AbortController();
|
|
1221
|
+
this.activeAbortController = abortController;
|
|
1175
1222
|
if (this.currentThreadId) {
|
|
1176
1223
|
this.currentThread = this.codex.resumeThread(this.currentThreadId, threadOptions);
|
|
1177
1224
|
} else {
|
|
1178
1225
|
this.currentThread = this.codex.startThread(threadOptions);
|
|
1179
|
-
const { events: events2 } = await this.currentThread.runStreamed("Hello");
|
|
1226
|
+
const { events: events2 } = await this.currentThread.runStreamed("Hello", { signal: abortController.signal });
|
|
1180
1227
|
for await (const event of events2) {
|
|
1181
1228
|
if (event.type === "thread.started") {
|
|
1182
1229
|
this.currentThreadId = event.thread_id;
|
|
@@ -1200,7 +1247,7 @@ var CodexManager = class {
|
|
|
1200
1247
|
} else {
|
|
1201
1248
|
input = message;
|
|
1202
1249
|
}
|
|
1203
|
-
const { events } = await this.currentThread.runStreamed(input);
|
|
1250
|
+
const { events } = await this.currentThread.runStreamed(input, { signal: abortController.signal });
|
|
1204
1251
|
let latestThoughtEvent = null;
|
|
1205
1252
|
for await (const event of events) {
|
|
1206
1253
|
if (linearSessionId) {
|
|
@@ -1226,6 +1273,7 @@ var CodexManager = class {
|
|
|
1226
1273
|
});
|
|
1227
1274
|
}
|
|
1228
1275
|
} finally {
|
|
1276
|
+
this.activeAbortController = null;
|
|
1229
1277
|
const status = await getGitStatus(this.workingDirectory);
|
|
1230
1278
|
const payload = linearSessionId ? { linearSessionId, status } : { status };
|
|
1231
1279
|
monolithService.sendEvent({ type: "agent_turn_complete", payload }).catch(() => {
|
|
@@ -1514,6 +1562,22 @@ codex.post("/reset", async (c) => {
|
|
|
1514
1562
|
);
|
|
1515
1563
|
}
|
|
1516
1564
|
});
|
|
1565
|
+
codex.post("/interrupt", async (c) => {
|
|
1566
|
+
try {
|
|
1567
|
+
const result = await codexManager.interrupt();
|
|
1568
|
+
const response = result;
|
|
1569
|
+
return c.json(response);
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
console.error("Error in /codex/interrupt:", error);
|
|
1572
|
+
return c.json(
|
|
1573
|
+
{
|
|
1574
|
+
error: "Failed to interrupt thread",
|
|
1575
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
1576
|
+
},
|
|
1577
|
+
500
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1517
1581
|
var codex_default = codex;
|
|
1518
1582
|
|
|
1519
1583
|
// src/routes/claude.ts
|
|
@@ -1526,6 +1590,46 @@ import {
|
|
|
1526
1590
|
import { join as join4 } from "path";
|
|
1527
1591
|
import { mkdir as mkdir4, appendFile as appendFile2, rm } from "fs/promises";
|
|
1528
1592
|
import { homedir as homedir4 } from "os";
|
|
1593
|
+
var MAX_INTERRUPT_QUEUE_ITEMS2 = 1e3;
|
|
1594
|
+
var MAX_INTERRUPT_QUEUE_CHARS2 = 2e5;
|
|
1595
|
+
var PromptStream = class {
|
|
1596
|
+
queue = [];
|
|
1597
|
+
closed = false;
|
|
1598
|
+
waiters = [];
|
|
1599
|
+
push(message) {
|
|
1600
|
+
if (this.closed) return;
|
|
1601
|
+
const waiter = this.waiters.shift();
|
|
1602
|
+
if (waiter) {
|
|
1603
|
+
waiter({ value: message, done: false });
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
this.queue.push(message);
|
|
1607
|
+
}
|
|
1608
|
+
close() {
|
|
1609
|
+
if (this.closed) return;
|
|
1610
|
+
this.closed = true;
|
|
1611
|
+
for (const waiter of this.waiters) {
|
|
1612
|
+
waiter({ value: void 0, done: true });
|
|
1613
|
+
}
|
|
1614
|
+
this.waiters = [];
|
|
1615
|
+
}
|
|
1616
|
+
[Symbol.asyncIterator]() {
|
|
1617
|
+
return {
|
|
1618
|
+
next: () => {
|
|
1619
|
+
const nextItem = this.queue.shift();
|
|
1620
|
+
if (nextItem) {
|
|
1621
|
+
return Promise.resolve({ value: nextItem, done: false });
|
|
1622
|
+
}
|
|
1623
|
+
if (this.closed) {
|
|
1624
|
+
return Promise.resolve({ value: void 0, done: true });
|
|
1625
|
+
}
|
|
1626
|
+
return new Promise((resolve) => {
|
|
1627
|
+
this.waiters.push(resolve);
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1529
1633
|
var ClaudeManager = class {
|
|
1530
1634
|
workingDirectory;
|
|
1531
1635
|
historyFile;
|
|
@@ -1533,6 +1637,9 @@ var ClaudeManager = class {
|
|
|
1533
1637
|
initialized;
|
|
1534
1638
|
messageQueue;
|
|
1535
1639
|
baseSystemPrompt;
|
|
1640
|
+
activeQuery = null;
|
|
1641
|
+
activePromptStream = null;
|
|
1642
|
+
pendingInterrupt = false;
|
|
1536
1643
|
constructor(workingDirectory) {
|
|
1537
1644
|
if (workingDirectory) {
|
|
1538
1645
|
this.workingDirectory = workingDirectory;
|
|
@@ -1567,6 +1674,37 @@ var ClaudeManager = class {
|
|
|
1567
1674
|
getQueueStatus() {
|
|
1568
1675
|
return this.messageQueue.getStatus();
|
|
1569
1676
|
}
|
|
1677
|
+
async interrupt() {
|
|
1678
|
+
const queue = this.messageQueue.drainQueue({
|
|
1679
|
+
maxItems: MAX_INTERRUPT_QUEUE_ITEMS2,
|
|
1680
|
+
maxChars: MAX_INTERRUPT_QUEUE_CHARS2
|
|
1681
|
+
});
|
|
1682
|
+
const isProcessing = this.messageQueue.isProcessing();
|
|
1683
|
+
if (isProcessing) {
|
|
1684
|
+
this.pendingInterrupt = true;
|
|
1685
|
+
this.activePromptStream?.close();
|
|
1686
|
+
if (this.activeQuery) {
|
|
1687
|
+
try {
|
|
1688
|
+
await this.activeQuery.interrupt();
|
|
1689
|
+
} catch {
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
const linearSessionId = process.env.LINEAR_SESSION_ID;
|
|
1694
|
+
if (linearSessionId && queue.length > 0) {
|
|
1695
|
+
const body = ["Interrupted. Cleared queue:", ...queue].join("\n");
|
|
1696
|
+
const thoughtEvent = {
|
|
1697
|
+
linearSessionId,
|
|
1698
|
+
content: {
|
|
1699
|
+
type: "thought",
|
|
1700
|
+
body
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
monolithService.sendEvent({ type: "agent_update", payload: thoughtEvent }).catch(() => {
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
return { interrupted: isProcessing || queue.length > 0, queue };
|
|
1707
|
+
}
|
|
1570
1708
|
/**
|
|
1571
1709
|
* Set the base system prompt from replicas.json
|
|
1572
1710
|
* This will be combined with any custom instructions passed to individual messages
|
|
@@ -1640,9 +1778,9 @@ var ClaudeManager = class {
|
|
|
1640
1778
|
session_id: this.sessionId ?? ""
|
|
1641
1779
|
};
|
|
1642
1780
|
await this.recordEvent(userMessage);
|
|
1643
|
-
const
|
|
1644
|
-
|
|
1645
|
-
|
|
1781
|
+
const promptStream = new PromptStream();
|
|
1782
|
+
promptStream.push(userMessage);
|
|
1783
|
+
this.activePromptStream = promptStream;
|
|
1646
1784
|
const startHooksInstruction = this.getStartHooksInstruction();
|
|
1647
1785
|
const parts = [];
|
|
1648
1786
|
if (this.baseSystemPrompt) {
|
|
@@ -1656,7 +1794,7 @@ var ClaudeManager = class {
|
|
|
1656
1794
|
}
|
|
1657
1795
|
const combinedInstructions = parts.length > 0 ? parts.join("\n\n") : void 0;
|
|
1658
1796
|
const response = query({
|
|
1659
|
-
prompt:
|
|
1797
|
+
prompt: promptStream,
|
|
1660
1798
|
options: {
|
|
1661
1799
|
resume: this.sessionId || void 0,
|
|
1662
1800
|
cwd: this.workingDirectory,
|
|
@@ -1674,6 +1812,15 @@ var ClaudeManager = class {
|
|
|
1674
1812
|
model: model || "opus"
|
|
1675
1813
|
}
|
|
1676
1814
|
});
|
|
1815
|
+
this.activeQuery = response;
|
|
1816
|
+
if (this.pendingInterrupt) {
|
|
1817
|
+
this.pendingInterrupt = false;
|
|
1818
|
+
this.activePromptStream?.close();
|
|
1819
|
+
try {
|
|
1820
|
+
await this.activeQuery.interrupt();
|
|
1821
|
+
} catch {
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1677
1824
|
let latestThoughtEvent = null;
|
|
1678
1825
|
for await (const msg of response) {
|
|
1679
1826
|
await this.handleMessage(msg);
|
|
@@ -1693,6 +1840,9 @@ var ClaudeManager = class {
|
|
|
1693
1840
|
}
|
|
1694
1841
|
}
|
|
1695
1842
|
}
|
|
1843
|
+
if (msg.type === "result") {
|
|
1844
|
+
this.activePromptStream?.close();
|
|
1845
|
+
}
|
|
1696
1846
|
}
|
|
1697
1847
|
if (linearSessionId && latestThoughtEvent) {
|
|
1698
1848
|
const responseEvent = linearThoughtToResponse(latestThoughtEvent);
|
|
@@ -1700,6 +1850,10 @@ var ClaudeManager = class {
|
|
|
1700
1850
|
});
|
|
1701
1851
|
}
|
|
1702
1852
|
} finally {
|
|
1853
|
+
this.activeQuery = null;
|
|
1854
|
+
this.activePromptStream?.close();
|
|
1855
|
+
this.activePromptStream = null;
|
|
1856
|
+
this.pendingInterrupt = false;
|
|
1703
1857
|
const status = await getGitStatus(this.workingDirectory);
|
|
1704
1858
|
const payload = linearSessionId ? { linearSessionId, status } : { status };
|
|
1705
1859
|
monolithService.sendEvent({ type: "agent_turn_complete", payload }).catch(() => {
|
|
@@ -1926,6 +2080,22 @@ claude.post("/reset", async (c) => {
|
|
|
1926
2080
|
);
|
|
1927
2081
|
}
|
|
1928
2082
|
});
|
|
2083
|
+
claude.post("/interrupt", async (c) => {
|
|
2084
|
+
try {
|
|
2085
|
+
const result = await claudeManager.interrupt();
|
|
2086
|
+
const response = result;
|
|
2087
|
+
return c.json(response);
|
|
2088
|
+
} catch (error) {
|
|
2089
|
+
console.error("Error in /claude/interrupt:", error);
|
|
2090
|
+
return c.json(
|
|
2091
|
+
{
|
|
2092
|
+
error: "Failed to interrupt session",
|
|
2093
|
+
details: error instanceof Error ? error.message : "Unknown error"
|
|
2094
|
+
},
|
|
2095
|
+
500
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
});
|
|
1929
2099
|
var claude_default = claude;
|
|
1930
2100
|
|
|
1931
2101
|
// src/routes/plans.ts
|
|
@@ -2111,6 +2281,10 @@ var ClaudeTokenManager = class {
|
|
|
2111
2281
|
REFRESH_INTERVAL_MS = 45 * 60 * 1e3;
|
|
2112
2282
|
// 45 minutes
|
|
2113
2283
|
async start() {
|
|
2284
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
2285
|
+
console.log("[ClaudeTokenManager] Skipping: ANTHROPIC_API_KEY is set");
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2114
2288
|
const monolithUrl = process.env.MONOLITH_URL;
|
|
2115
2289
|
const workspaceId = process.env.WORKSPACE_ID;
|
|
2116
2290
|
const engineSecret = process.env.REPLICAS_ENGINE_SECRET;
|
|
@@ -2135,6 +2309,9 @@ var ClaudeTokenManager = class {
|
|
|
2135
2309
|
}
|
|
2136
2310
|
}
|
|
2137
2311
|
async refreshCredentials() {
|
|
2312
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2138
2315
|
const monolithUrl = process.env.MONOLITH_URL;
|
|
2139
2316
|
const workspaceId = process.env.WORKSPACE_ID;
|
|
2140
2317
|
const engineSecret = process.env.REPLICAS_ENGINE_SECRET;
|