replicas-engine 0.1.665 → 0.1.666
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/{chunk-SBJ4HUPK.js → chunk-3EPGH6IZ.js} +134 -25
- package/dist/src/headless-agent.js +1 -1
- package/dist/src/index.js +531 -178
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
AGENT,
|
|
5
5
|
AGENT_MESSAGE_DELTA_METHOD,
|
|
6
6
|
AGENT_MODELS,
|
|
7
|
+
AI_GATEWAY_BASE_URL,
|
|
7
8
|
ASTER_BASE_URL,
|
|
8
9
|
ASTER_MODELS,
|
|
9
10
|
ASTER_MODEL_LABELS,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
DEFAULT_DEEPSEEK_MODEL,
|
|
42
43
|
DEFAULT_FX_MODEL,
|
|
43
44
|
DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS,
|
|
45
|
+
DEFAULT_KIMI_MODEL,
|
|
44
46
|
DEFAULT_OPENCODE_MODEL,
|
|
45
47
|
DEFAULT_PI_MODEL,
|
|
46
48
|
DEFAULT_START_HOOK_TIMEOUT_MS,
|
|
@@ -120,6 +122,7 @@ import {
|
|
|
120
122
|
extractCommandProtectionCommandText,
|
|
121
123
|
extractErrorText,
|
|
122
124
|
extractToolResultText,
|
|
125
|
+
fetchAiGatewayModels,
|
|
123
126
|
findGitCommitSignals,
|
|
124
127
|
findPrMergeSignals,
|
|
125
128
|
getChatHistoryPageSenders,
|
|
@@ -186,13 +189,13 @@ import {
|
|
|
186
189
|
serializeCanvasContentResponse,
|
|
187
190
|
shellQuotePosix,
|
|
188
191
|
stripAgentDiagnosticErrors
|
|
189
|
-
} from "./chunk-
|
|
192
|
+
} from "./chunk-3EPGH6IZ.js";
|
|
190
193
|
|
|
191
194
|
// src/index.ts
|
|
192
195
|
import { serve } from "@hono/node-server";
|
|
193
196
|
import { Hono as Hono2 } from "hono";
|
|
194
197
|
import { existsSync as existsSync11 } from "fs";
|
|
195
|
-
import { randomUUID as
|
|
198
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
196
199
|
import { connect } from "net";
|
|
197
200
|
|
|
198
201
|
// src/managers/github-token-manager.ts
|
|
@@ -1976,6 +1979,7 @@ function createDefaultDetails() {
|
|
|
1976
1979
|
cursorAuthMethod: "none",
|
|
1977
1980
|
deepseekAuthMethod: "none",
|
|
1978
1981
|
fxAuthMethod: "none",
|
|
1982
|
+
kimiAuthMethod: "none",
|
|
1979
1983
|
opencodeAuthMethod: "none",
|
|
1980
1984
|
piAuthMethod: "none",
|
|
1981
1985
|
lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2017,6 +2021,7 @@ var EnvironmentDetailsService = class {
|
|
|
2017
2021
|
details.cursorAuthMethod = detectCursorAuthMethod();
|
|
2018
2022
|
details.deepseekAuthMethod = detectDeepseekAuthMethod();
|
|
2019
2023
|
details.fxAuthMethod = detectFxAuthMethod();
|
|
2024
|
+
details.kimiAuthMethod = detectFxAuthMethod();
|
|
2020
2025
|
details.opencodeAuthMethod = detectOpencodeAuthMethod();
|
|
2021
2026
|
details.piAuthMethod = detectPiAuthMethod();
|
|
2022
2027
|
details.credentialFallbacks = listCredentialFallbacks();
|
|
@@ -2772,10 +2777,10 @@ async function registerDesktopPreview() {
|
|
|
2772
2777
|
|
|
2773
2778
|
// src/services/chat/chat-service.ts
|
|
2774
2779
|
import { existsSync as existsSync8 } from "fs";
|
|
2775
|
-
import { appendFile as appendFile4, copyFile, mkdir as mkdir17, readFile as
|
|
2780
|
+
import { appendFile as appendFile4, copyFile, mkdir as mkdir17, readFile as readFile17, rename as rename3, rm as rm2 } from "fs/promises";
|
|
2776
2781
|
import { homedir as homedir15 } from "os";
|
|
2777
|
-
import { join as
|
|
2778
|
-
import { randomUUID as
|
|
2782
|
+
import { join as join29 } from "path";
|
|
2783
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
2779
2784
|
|
|
2780
2785
|
// src/managers/claude-manager.ts
|
|
2781
2786
|
import {
|
|
@@ -6356,6 +6361,7 @@ var OPENCODE_HISTORY_DIR = join18(ENGINE_DIR2, "opencode-histories");
|
|
|
6356
6361
|
var PI_HISTORY_DIR = join18(ENGINE_DIR2, "pi-histories");
|
|
6357
6362
|
var DEEPSEEK_HISTORY_DIR = join18(ENGINE_DIR2, "deepseek-histories");
|
|
6358
6363
|
var FX_HISTORY_DIR = join18(ENGINE_DIR2, "fx-histories");
|
|
6364
|
+
var KIMI_HISTORY_DIR = join18(ENGINE_DIR2, "kimi-histories");
|
|
6359
6365
|
var HISTORY_DIR_BY_PROVIDER = {
|
|
6360
6366
|
claude: CLAUDE_HISTORY_DIR,
|
|
6361
6367
|
relay: RELAY_HISTORY_DIR,
|
|
@@ -6363,6 +6369,7 @@ var HISTORY_DIR_BY_PROVIDER = {
|
|
|
6363
6369
|
cursor: CURSOR_HISTORY_DIR,
|
|
6364
6370
|
deepseek: DEEPSEEK_HISTORY_DIR,
|
|
6365
6371
|
fx: FX_HISTORY_DIR,
|
|
6372
|
+
kimi: KIMI_HISTORY_DIR,
|
|
6366
6373
|
opencode: OPENCODE_HISTORY_DIR,
|
|
6367
6374
|
pi: PI_HISTORY_DIR
|
|
6368
6375
|
};
|
|
@@ -8552,9 +8559,14 @@ ${request.message}` : request.message;
|
|
|
8552
8559
|
};
|
|
8553
8560
|
|
|
8554
8561
|
// src/managers/fx-manager.ts
|
|
8562
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
8563
|
+
import { spawn as spawn5 } from "child_process";
|
|
8564
|
+
|
|
8565
|
+
// src/managers/acp-manager.ts
|
|
8555
8566
|
import { spawn as spawn4 } from "child_process";
|
|
8556
|
-
import {
|
|
8557
|
-
import {
|
|
8567
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
8568
|
+
import { mkdir as mkdir13, readFile as readFile11, writeFile as writeFile6 } from "fs/promises";
|
|
8569
|
+
import { dirname as dirname7 } from "path";
|
|
8558
8570
|
import { Writable } from "stream";
|
|
8559
8571
|
import {
|
|
8560
8572
|
PROTOCOL_VERSION,
|
|
@@ -8562,21 +8574,12 @@ import {
|
|
|
8562
8574
|
methods,
|
|
8563
8575
|
ndJsonStream
|
|
8564
8576
|
} from "@agentclientprotocol/sdk";
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
await new Promise((resolve5, reject) => {
|
|
8569
|
-
const child = spawn4("sudo", ["tee", "-a", "/etc/resolv.conf"], { stdio: ["pipe", "ignore", "pipe"] });
|
|
8570
|
-
let stderr = "";
|
|
8571
|
-
child.stderr.on("data", (chunk) => {
|
|
8572
|
-
stderr += chunk;
|
|
8573
|
-
});
|
|
8574
|
-
child.on("error", reject);
|
|
8575
|
-
child.on("exit", (code) => code === 0 ? resolve5() : reject(new Error(`Failed to normalize /etc/resolv.conf: ${stderr.trim()}`)));
|
|
8576
|
-
child.stdin.end("\n");
|
|
8577
|
-
});
|
|
8577
|
+
function selectOptions(option) {
|
|
8578
|
+
if (option.type !== "select") return [];
|
|
8579
|
+
return option.options.flatMap((entry) => "group" in entry ? entry.options : [entry]);
|
|
8578
8580
|
}
|
|
8579
|
-
var
|
|
8581
|
+
var AcpManager = class extends CodingAgentManager {
|
|
8582
|
+
acp;
|
|
8580
8583
|
historyFile;
|
|
8581
8584
|
historyFilePath;
|
|
8582
8585
|
process = null;
|
|
@@ -8584,42 +8587,90 @@ var FxManager = class extends CodingAgentManager {
|
|
|
8584
8587
|
context = null;
|
|
8585
8588
|
sessionId = null;
|
|
8586
8589
|
activeModel = null;
|
|
8590
|
+
activeThinkingLevel;
|
|
8587
8591
|
planMode = false;
|
|
8592
|
+
replaying = false;
|
|
8588
8593
|
slashCommands = [];
|
|
8594
|
+
configOptions = [];
|
|
8595
|
+
modes = null;
|
|
8596
|
+
capabilities = {};
|
|
8597
|
+
interactiveTools = false;
|
|
8598
|
+
pendingPermissions = /* @__PURE__ */ new Map();
|
|
8599
|
+
terminals = /* @__PURE__ */ new Map();
|
|
8589
8600
|
constructor(options) {
|
|
8590
|
-
super(options);
|
|
8591
|
-
this.
|
|
8601
|
+
super({ ...options, provider: options.acp.provider });
|
|
8602
|
+
this.acp = options.acp;
|
|
8603
|
+
this.historyFilePath = options.historyFilePath;
|
|
8592
8604
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
8593
8605
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
8594
8606
|
}
|
|
8595
8607
|
async initialize() {
|
|
8596
8608
|
await mkdir13(dirname7(this.historyFilePath), { recursive: true });
|
|
8597
|
-
await
|
|
8609
|
+
await this.acp.prepare?.();
|
|
8598
8610
|
}
|
|
8599
8611
|
getHistorySink() {
|
|
8600
8612
|
return this.historyFile;
|
|
8601
8613
|
}
|
|
8602
|
-
async start(model) {
|
|
8603
|
-
if (this.context && this.activeModel === model) return;
|
|
8614
|
+
async start(model, thinkingLevel) {
|
|
8615
|
+
if (this.context && this.activeModel === model && (!this.acp.restartOnThinkingChange || this.activeThinkingLevel === thinkingLevel)) return;
|
|
8604
8616
|
this.dispose();
|
|
8605
|
-
const
|
|
8617
|
+
const agentEnv = await this.acp.env?.(model, thinkingLevel);
|
|
8618
|
+
const child = spawn4(this.acp.command, this.acp.args(model), {
|
|
8606
8619
|
cwd: this.workingDirectory,
|
|
8607
|
-
env: { ...process.env,
|
|
8620
|
+
env: { ...process.env, NO_COLOR: "1", ...agentEnv },
|
|
8608
8621
|
stdio: ["pipe", "pipe", "pipe"]
|
|
8609
8622
|
});
|
|
8610
8623
|
this.process = child;
|
|
8611
|
-
child.stderr.on("data", (chunk) => console.error(
|
|
8624
|
+
child.stderr.on("data", (chunk) => console.error(`[${this.acp.provider} ACP]`, chunk.toString().trimEnd()));
|
|
8612
8625
|
child.on("exit", (code, signal) => {
|
|
8613
8626
|
if (this.process !== child) return;
|
|
8614
8627
|
this.process = null;
|
|
8615
8628
|
this.context = null;
|
|
8616
8629
|
this.connection = null;
|
|
8617
8630
|
if (code !== 0 && signal !== "SIGTERM") {
|
|
8618
|
-
this.recordHistoryEvent("
|
|
8631
|
+
this.recordHistoryEvent("acp-error", {
|
|
8632
|
+
provider: this.acp.provider,
|
|
8633
|
+
message: `${this.acp.command} ACP exited with code ${code ?? signal}`
|
|
8634
|
+
}, this.historyFile);
|
|
8619
8635
|
}
|
|
8620
8636
|
});
|
|
8621
|
-
const app2 = client({ name: "Replicas" }).onNotification(methods.client.session.update, ({ params }) => this.recordUpdate(params)).onRequest(methods.client.
|
|
8622
|
-
|
|
8637
|
+
const app2 = client({ name: "Replicas" }).onNotification(methods.client.session.update, ({ params }) => this.recordUpdate(params)).onRequest(methods.client.fs.readTextFile, async ({ params }) => {
|
|
8638
|
+
this.assertSession(params.sessionId);
|
|
8639
|
+
const content = await readFile11(params.path, "utf8");
|
|
8640
|
+
if (params.line === void 0 && params.limit === void 0) return { content };
|
|
8641
|
+
const start = Math.max(0, (params.line ?? 1) - 1);
|
|
8642
|
+
const end = params.limit === null || params.limit === void 0 ? void 0 : start + params.limit;
|
|
8643
|
+
return { content: content.split("\n").slice(start, end).join("\n") };
|
|
8644
|
+
}).onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
|
|
8645
|
+
this.assertSession(params.sessionId);
|
|
8646
|
+
if (this.planMode) throw new Error("ACP file writes are disabled in plan mode.");
|
|
8647
|
+
await writeFile6(params.path, params.content, "utf8");
|
|
8648
|
+
return {};
|
|
8649
|
+
}).onRequest(methods.client.terminal.create, ({ params }) => this.createTerminal(params)).onRequest(methods.client.terminal.output, ({ params }) => {
|
|
8650
|
+
this.assertSession(params.sessionId);
|
|
8651
|
+
const terminal = this.getTerminal(params.terminalId);
|
|
8652
|
+
return {
|
|
8653
|
+
output: terminal.output.toString("utf8"),
|
|
8654
|
+
truncated: terminal.truncated,
|
|
8655
|
+
exitStatus: terminal.exitStatus
|
|
8656
|
+
};
|
|
8657
|
+
}).onRequest(methods.client.terminal.waitForExit, async ({ params }) => {
|
|
8658
|
+
this.assertSession(params.sessionId);
|
|
8659
|
+
return this.getTerminal(params.terminalId).exited;
|
|
8660
|
+
}).onRequest(methods.client.terminal.kill, ({ params }) => {
|
|
8661
|
+
this.assertSession(params.sessionId);
|
|
8662
|
+
this.getTerminal(params.terminalId).process.kill("SIGTERM");
|
|
8663
|
+
return {};
|
|
8664
|
+
}).onRequest(methods.client.terminal.release, ({ params }) => {
|
|
8665
|
+
this.assertSession(params.sessionId);
|
|
8666
|
+
const terminal = this.getTerminal(params.terminalId);
|
|
8667
|
+
if (terminal.exitStatus === null) terminal.process.kill("SIGTERM");
|
|
8668
|
+
this.terminals.delete(params.terminalId);
|
|
8669
|
+
return {};
|
|
8670
|
+
}).onRequest(methods.client.session.requestPermission, ({ params }) => {
|
|
8671
|
+
if (params.toolCall.title === "AskUserQuestion") return this.requestUserInput(params);
|
|
8672
|
+
const preferred = this.planMode ? ["reject_once", "reject_always"] : ["allow_always", "allow_once"];
|
|
8673
|
+
const option = preferred.flatMap((kind) => params.options.filter((candidate) => candidate.kind === kind))[0];
|
|
8623
8674
|
return option ? { outcome: { outcome: "selected", optionId: option.optionId } } : { outcome: { outcome: "cancelled" } };
|
|
8624
8675
|
});
|
|
8625
8676
|
const stdout = new ReadableStream({
|
|
@@ -8632,26 +8683,34 @@ var FxManager = class extends CodingAgentManager {
|
|
|
8632
8683
|
child.stdout.destroy();
|
|
8633
8684
|
}
|
|
8634
8685
|
});
|
|
8635
|
-
|
|
8636
|
-
Writable.toWeb(child.stdin),
|
|
8637
|
-
stdout
|
|
8638
|
-
);
|
|
8639
|
-
this.connection = app2.connect(stream);
|
|
8686
|
+
this.connection = app2.connect(ndJsonStream(Writable.toWeb(child.stdin), stdout));
|
|
8640
8687
|
this.context = this.connection.agent;
|
|
8641
|
-
await this.context.request(methods.agent.initialize, {
|
|
8688
|
+
const initialized = await this.context.request(methods.agent.initialize, {
|
|
8642
8689
|
protocolVersion: PROTOCOL_VERSION,
|
|
8643
|
-
clientCapabilities: {
|
|
8690
|
+
clientCapabilities: {
|
|
8691
|
+
fs: { readTextFile: true, writeTextFile: true },
|
|
8692
|
+
terminal: true,
|
|
8693
|
+
plan: {}
|
|
8694
|
+
},
|
|
8644
8695
|
clientInfo: { name: "Replicas", version: "1" }
|
|
8645
8696
|
});
|
|
8697
|
+
this.capabilities = initialized.agentCapabilities ?? {};
|
|
8646
8698
|
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
8647
8699
|
const sessionId = this.sessionId ?? this.initialSessionId;
|
|
8648
|
-
if (sessionId) {
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8700
|
+
if (sessionId && this.capabilities.loadSession) {
|
|
8701
|
+
this.replaying = true;
|
|
8702
|
+
try {
|
|
8703
|
+
const session = await this.context.request(methods.agent.session.load, {
|
|
8704
|
+
sessionId,
|
|
8705
|
+
cwd: this.workingDirectory,
|
|
8706
|
+
additionalDirectories,
|
|
8707
|
+
mcpServers: []
|
|
8708
|
+
});
|
|
8709
|
+
this.configOptions = session.configOptions ?? [];
|
|
8710
|
+
this.modes = session.modes ?? null;
|
|
8711
|
+
} finally {
|
|
8712
|
+
this.replaying = false;
|
|
8713
|
+
}
|
|
8655
8714
|
this.sessionId = sessionId;
|
|
8656
8715
|
} else {
|
|
8657
8716
|
const session = await this.context.request(methods.agent.session.new, {
|
|
@@ -8660,21 +8719,84 @@ var FxManager = class extends CodingAgentManager {
|
|
|
8660
8719
|
mcpServers: []
|
|
8661
8720
|
});
|
|
8662
8721
|
this.sessionId = session.sessionId;
|
|
8722
|
+
this.configOptions = session.configOptions ?? [];
|
|
8723
|
+
this.modes = session.modes ?? null;
|
|
8663
8724
|
await this.onSaveSessionId(session.sessionId);
|
|
8664
8725
|
}
|
|
8665
8726
|
this.activeModel = model;
|
|
8727
|
+
this.activeThinkingLevel = thinkingLevel;
|
|
8728
|
+
}
|
|
8729
|
+
assertSession(sessionId) {
|
|
8730
|
+
if (sessionId !== this.sessionId) throw new Error(`Unknown ACP session: ${sessionId}`);
|
|
8731
|
+
}
|
|
8732
|
+
getTerminal(terminalId) {
|
|
8733
|
+
const terminal = this.terminals.get(terminalId);
|
|
8734
|
+
if (!terminal) throw new Error(`Unknown ACP terminal: ${terminalId}`);
|
|
8735
|
+
return terminal;
|
|
8736
|
+
}
|
|
8737
|
+
createTerminal(params) {
|
|
8738
|
+
this.assertSession(params.sessionId);
|
|
8739
|
+
if (this.planMode) throw new Error("ACP terminals are disabled in plan mode.");
|
|
8740
|
+
const terminalId = randomUUID6();
|
|
8741
|
+
const outputByteLimit = Math.max(1, Math.min(params.outputByteLimit ?? 1048576, 10485760));
|
|
8742
|
+
const child = spawn4(params.command, params.args ?? [], {
|
|
8743
|
+
cwd: params.cwd ?? this.workingDirectory,
|
|
8744
|
+
env: {
|
|
8745
|
+
...process.env,
|
|
8746
|
+
...Object.fromEntries(params.env?.map(({ name, value }) => [name, value]) ?? [])
|
|
8747
|
+
},
|
|
8748
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8749
|
+
});
|
|
8750
|
+
let resolveExit;
|
|
8751
|
+
const terminal = {
|
|
8752
|
+
process: child,
|
|
8753
|
+
output: Buffer.alloc(0),
|
|
8754
|
+
outputByteLimit,
|
|
8755
|
+
truncated: false,
|
|
8756
|
+
exitStatus: null,
|
|
8757
|
+
exited: new Promise((resolve5) => {
|
|
8758
|
+
resolveExit = resolve5;
|
|
8759
|
+
})
|
|
8760
|
+
};
|
|
8761
|
+
const append = (chunk) => {
|
|
8762
|
+
terminal.output = Buffer.concat([terminal.output, chunk]);
|
|
8763
|
+
if (terminal.output.length <= outputByteLimit) return;
|
|
8764
|
+
let start = terminal.output.length - outputByteLimit;
|
|
8765
|
+
while (start < terminal.output.length && (terminal.output[start] & 192) === 128) start += 1;
|
|
8766
|
+
terminal.output = terminal.output.subarray(start);
|
|
8767
|
+
terminal.truncated = true;
|
|
8768
|
+
};
|
|
8769
|
+
child.stdout.on("data", append);
|
|
8770
|
+
child.stderr.on("data", append);
|
|
8771
|
+
child.on("error", () => {
|
|
8772
|
+
const status = { exitCode: null, signal: null };
|
|
8773
|
+
terminal.exitStatus = status;
|
|
8774
|
+
resolveExit?.(status);
|
|
8775
|
+
});
|
|
8776
|
+
child.on("exit", (exitCode, signal) => {
|
|
8777
|
+
const status = { exitCode, signal };
|
|
8778
|
+
terminal.exitStatus = status;
|
|
8779
|
+
resolveExit?.(status);
|
|
8780
|
+
});
|
|
8781
|
+
this.terminals.set(terminalId, terminal);
|
|
8782
|
+
return { terminalId };
|
|
8666
8783
|
}
|
|
8667
8784
|
recordUpdate(notification) {
|
|
8668
|
-
if (notification.sessionId !== this.sessionId) return;
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8785
|
+
if (notification.sessionId !== this.sessionId && this.sessionId !== null) return;
|
|
8786
|
+
const update = notification.update;
|
|
8787
|
+
if (update.sessionUpdate === "available_commands_update") {
|
|
8788
|
+
this.slashCommands = mergeSlashCommands(update.availableCommands.flatMap((command) => {
|
|
8789
|
+
const parsed = createProviderSlashCommand(this.acp.provider, command.name, command.description, command.input?.hint);
|
|
8672
8790
|
return parsed ? [parsed] : [];
|
|
8673
8791
|
}));
|
|
8674
|
-
} else if (
|
|
8675
|
-
|
|
8792
|
+
} else if (update.sessionUpdate === "config_option_update") {
|
|
8793
|
+
this.configOptions = update.configOptions;
|
|
8794
|
+
} else if (update.sessionUpdate === "current_mode_update" && this.modes) {
|
|
8795
|
+
this.modes = { ...this.modes, currentModeId: update.currentModeId };
|
|
8796
|
+
} else if (update.sessionUpdate === "usage_update" && !this.replaying) {
|
|
8797
|
+
const { used, size } = update;
|
|
8676
8798
|
this.historyFile.append(this.emitContextUsage({
|
|
8677
|
-
provider:
|
|
8799
|
+
provider: this.acp.provider,
|
|
8678
8800
|
source: "provider_usage",
|
|
8679
8801
|
model: this.activeModel,
|
|
8680
8802
|
totalTokens: used,
|
|
@@ -8684,37 +8806,155 @@ var FxManager = class extends CodingAgentManager {
|
|
|
8684
8806
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8685
8807
|
}));
|
|
8686
8808
|
}
|
|
8687
|
-
this.
|
|
8809
|
+
if (!this.replaying) {
|
|
8810
|
+
this.recordHistoryEvent("acp-session-update", {
|
|
8811
|
+
provider: this.acp.provider,
|
|
8812
|
+
update
|
|
8813
|
+
}, this.historyFile);
|
|
8814
|
+
}
|
|
8815
|
+
}
|
|
8816
|
+
async setConfig(category, value) {
|
|
8817
|
+
const context = this.context;
|
|
8818
|
+
const sessionId = this.sessionId;
|
|
8819
|
+
const option = this.configOptions.find((candidate) => candidate.category === category || candidate.id === category);
|
|
8820
|
+
if (!context || !sessionId || !option) return false;
|
|
8821
|
+
if (option.type === "select" && !selectOptions(option).some((candidate) => candidate.value === value)) return false;
|
|
8822
|
+
if (option.currentValue === value) return true;
|
|
8823
|
+
const response = typeof value === "boolean" ? await context.request(methods.agent.session.setConfigOption, {
|
|
8824
|
+
sessionId,
|
|
8825
|
+
configId: option.id,
|
|
8826
|
+
value,
|
|
8827
|
+
type: "boolean"
|
|
8828
|
+
}) : await context.request(methods.agent.session.setConfigOption, {
|
|
8829
|
+
sessionId,
|
|
8830
|
+
configId: option.id,
|
|
8831
|
+
value
|
|
8832
|
+
});
|
|
8833
|
+
this.configOptions = response.configOptions;
|
|
8834
|
+
return true;
|
|
8835
|
+
}
|
|
8836
|
+
async setMode(planMode) {
|
|
8837
|
+
const context = this.context;
|
|
8838
|
+
const sessionId = this.sessionId;
|
|
8839
|
+
if (!context || !sessionId) return;
|
|
8840
|
+
const candidates = planMode ? ["plan", "ask", "architect"] : ["code", "default", "auto", "yolo"];
|
|
8841
|
+
const mode = this.modes?.availableModes.find((candidate) => candidates.includes(candidate.id.toLowerCase())) ?? this.modes?.availableModes.find((candidate) => candidates.some((name) => candidate.name.toLowerCase().includes(name)));
|
|
8842
|
+
if (mode) {
|
|
8843
|
+
await context.request(methods.agent.session.setMode, { sessionId, modeId: mode.id });
|
|
8844
|
+
if (this.modes) this.modes = { ...this.modes, currentModeId: mode.id };
|
|
8845
|
+
return;
|
|
8846
|
+
}
|
|
8847
|
+
const option = this.configOptions.find((candidate) => candidate.category === "mode" || candidate.id === "mode");
|
|
8848
|
+
if (!option || option.type !== "select") return;
|
|
8849
|
+
const selected = selectOptions(option).find((candidate) => candidates.includes(candidate.value.toLowerCase())) ?? selectOptions(option).find((candidate) => candidates.some((name) => candidate.name.toLowerCase().includes(name)));
|
|
8850
|
+
if (selected) await this.setConfig("mode", selected.value);
|
|
8851
|
+
}
|
|
8852
|
+
async setThinking(level) {
|
|
8853
|
+
if (!level) return;
|
|
8854
|
+
const option = this.configOptions.find((candidate) => candidate.category === "thought_level" || candidate.id === "thinking");
|
|
8855
|
+
if (!option || option.type !== "select") return;
|
|
8856
|
+
const values = selectOptions(option).map((candidate) => candidate.value);
|
|
8857
|
+
const aliases = {
|
|
8858
|
+
low: ["low", "on"],
|
|
8859
|
+
medium: ["medium", "on"],
|
|
8860
|
+
high: ["high", "on"],
|
|
8861
|
+
xhigh: ["xhigh", "high", "on"],
|
|
8862
|
+
max: ["max", "xhigh", "high", "on"],
|
|
8863
|
+
ultra: ["ultra", "max", "xhigh", "high", "on"],
|
|
8864
|
+
ultracode: ["max", "xhigh", "high", "on"]
|
|
8865
|
+
};
|
|
8866
|
+
const value = aliases[level].find((candidate) => values.includes(candidate));
|
|
8867
|
+
if (value) await this.setConfig("thought_level", value);
|
|
8868
|
+
}
|
|
8869
|
+
async requestUserInput(params) {
|
|
8870
|
+
if (!this.interactiveTools) return { outcome: { outcome: "cancelled" } };
|
|
8871
|
+
const requestId = randomUUID6();
|
|
8872
|
+
const toolCallId = params.toolCall.toolCallId;
|
|
8873
|
+
return new Promise((resolve5) => {
|
|
8874
|
+
this.pendingPermissions.set(requestId, { toolCallId, options: params.options, resolve: resolve5 });
|
|
8875
|
+
this.recordHistoryEvent("acp-session-update", {
|
|
8876
|
+
provider: this.acp.provider,
|
|
8877
|
+
update: { ...params.toolCall, sessionUpdate: "tool_call", status: "pending" }
|
|
8878
|
+
}, this.historyFile);
|
|
8879
|
+
this.recordHistoryEvent("replicas-tool-input-request", {
|
|
8880
|
+
requestId,
|
|
8881
|
+
toolUseId: toolCallId,
|
|
8882
|
+
toolName: params.toolCall.title,
|
|
8883
|
+
options: params.options.map((option) => ({
|
|
8884
|
+
id: option.optionId,
|
|
8885
|
+
label: option.name,
|
|
8886
|
+
variant: option.kind.startsWith("reject") ? "secondary" : "primary"
|
|
8887
|
+
})),
|
|
8888
|
+
parent_tool_use_id: null
|
|
8889
|
+
}, this.historyFile);
|
|
8890
|
+
});
|
|
8891
|
+
}
|
|
8892
|
+
async respondToToolInput(requestId, selectionId) {
|
|
8893
|
+
const pending = this.pendingPermissions.get(requestId);
|
|
8894
|
+
if (!pending) return false;
|
|
8895
|
+
const option = pending.options.find((candidate) => candidate.optionId === selectionId);
|
|
8896
|
+
if (!option) return false;
|
|
8897
|
+
this.pendingPermissions.delete(requestId);
|
|
8898
|
+
pending.resolve({ outcome: { outcome: "selected", optionId: selectionId } });
|
|
8899
|
+
this.recordHistoryEvent("replicas-tool-input-resolved", {
|
|
8900
|
+
requestId,
|
|
8901
|
+
toolUseId: pending.toolCallId,
|
|
8902
|
+
toolName: "AskUserQuestion",
|
|
8903
|
+
selectionId,
|
|
8904
|
+
selectionSummary: option.name,
|
|
8905
|
+
parent_tool_use_id: null
|
|
8906
|
+
}, this.historyFile);
|
|
8907
|
+
return true;
|
|
8908
|
+
}
|
|
8909
|
+
isAwaitingInput() {
|
|
8910
|
+
return this.pendingPermissions.size > 0;
|
|
8911
|
+
}
|
|
8912
|
+
abortPendingPermissions() {
|
|
8913
|
+
for (const [requestId, pending] of this.pendingPermissions) {
|
|
8914
|
+
pending.resolve({ outcome: { outcome: "cancelled" } });
|
|
8915
|
+
this.recordHistoryEvent("replicas-tool-input-resolved", {
|
|
8916
|
+
requestId,
|
|
8917
|
+
toolUseId: pending.toolCallId,
|
|
8918
|
+
toolName: "AskUserQuestion",
|
|
8919
|
+
selectionId: "aborted",
|
|
8920
|
+
parent_tool_use_id: null
|
|
8921
|
+
}, this.historyFile);
|
|
8922
|
+
}
|
|
8923
|
+
this.pendingPermissions.clear();
|
|
8688
8924
|
}
|
|
8689
8925
|
async processMessageInternal(request) {
|
|
8690
8926
|
try {
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
await this.start(model);
|
|
8927
|
+
const model = request.model ?? this.acp.defaultModel;
|
|
8928
|
+
await this.start(model, request.thinkingLevel);
|
|
8694
8929
|
const context = this.context;
|
|
8695
8930
|
const sessionId = this.sessionId;
|
|
8696
|
-
if (!context || !sessionId) throw new Error(
|
|
8931
|
+
if (!context || !sessionId) throw new Error(`${this.acp.command} ACP failed to initialize.`);
|
|
8697
8932
|
this.recordHistoryEvent("event_msg", { type: "user_message", message: request.message }, this.historyFile);
|
|
8698
8933
|
this.planMode = request.planMode ?? false;
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8934
|
+
this.interactiveTools = request.enableInteractiveTools ?? false;
|
|
8935
|
+
await this.setConfig("model", model);
|
|
8936
|
+
await this.setThinking(request.thinkingLevel);
|
|
8937
|
+
await this.setMode(this.planMode);
|
|
8938
|
+
const prompt = [{ type: "text", text: request.message }];
|
|
8939
|
+
for (const image of request.images ?? []) {
|
|
8940
|
+
if (!this.capabilities.promptCapabilities?.image) throw new Error(`${this.acp.provider} does not advertise ACP image support.`);
|
|
8941
|
+
if (image.source.type !== "base64") throw new Error(`${this.acp.provider} requires uploaded images to be base64 encoded.`);
|
|
8942
|
+
prompt.push({ type: "image", mimeType: image.source.media_type, data: image.source.data });
|
|
8943
|
+
}
|
|
8944
|
+
const response = await context.request(methods.agent.session.prompt, { sessionId, prompt });
|
|
8945
|
+
this.recordHistoryEvent("acp-turn-complete", {
|
|
8946
|
+
provider: this.acp.provider,
|
|
8947
|
+
stopReason: response.stopReason
|
|
8948
|
+
}, this.historyFile);
|
|
8713
8949
|
} catch (error) {
|
|
8714
|
-
this.recordHistoryEvent("
|
|
8950
|
+
this.recordHistoryEvent("acp-error", {
|
|
8951
|
+
provider: this.acp.provider,
|
|
8952
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8953
|
+
}, this.historyFile);
|
|
8715
8954
|
throw error;
|
|
8716
8955
|
} finally {
|
|
8717
8956
|
this.planMode = false;
|
|
8957
|
+
this.interactiveTools = false;
|
|
8718
8958
|
try {
|
|
8719
8959
|
await this.historyFile.flush();
|
|
8720
8960
|
} finally {
|
|
@@ -8724,8 +8964,17 @@ ${request.message}` : request.message
|
|
|
8724
8964
|
}
|
|
8725
8965
|
async interruptActiveTurn() {
|
|
8726
8966
|
if (!this.context || !this.sessionId) return;
|
|
8967
|
+
this.abortPendingPermissions();
|
|
8727
8968
|
await this.context.notify(methods.agent.session.cancel, { sessionId: this.sessionId });
|
|
8728
8969
|
}
|
|
8970
|
+
keepSteeredMessageQueued() {
|
|
8971
|
+
return true;
|
|
8972
|
+
}
|
|
8973
|
+
async steerRequest() {
|
|
8974
|
+
if (!this.isProcessing() || !this.context || !this.sessionId) return false;
|
|
8975
|
+
await this.interruptActiveTurn();
|
|
8976
|
+
return true;
|
|
8977
|
+
}
|
|
8729
8978
|
async getHistory(page = {}) {
|
|
8730
8979
|
await this.historyFile.flush();
|
|
8731
8980
|
return {
|
|
@@ -8739,20 +8988,105 @@ ${request.message}` : request.message
|
|
|
8739
8988
|
return this.slashCommands;
|
|
8740
8989
|
}
|
|
8741
8990
|
dispose() {
|
|
8991
|
+
for (const terminal of this.terminals.values()) {
|
|
8992
|
+
if (terminal.exitStatus === null) terminal.process.kill("SIGTERM");
|
|
8993
|
+
}
|
|
8994
|
+
this.terminals.clear();
|
|
8742
8995
|
this.connection?.close();
|
|
8743
8996
|
this.process?.kill("SIGTERM");
|
|
8744
8997
|
this.connection = null;
|
|
8745
8998
|
this.context = null;
|
|
8746
8999
|
this.process = null;
|
|
8747
9000
|
this.activeModel = null;
|
|
9001
|
+
this.activeThinkingLevel = void 0;
|
|
9002
|
+
this.configOptions = [];
|
|
9003
|
+
this.modes = null;
|
|
9004
|
+
this.capabilities = {};
|
|
8748
9005
|
this.planMode = false;
|
|
9006
|
+
this.interactiveTools = false;
|
|
9007
|
+
this.abortPendingPermissions();
|
|
9008
|
+
}
|
|
9009
|
+
};
|
|
9010
|
+
|
|
9011
|
+
// src/managers/fx-manager.ts
|
|
9012
|
+
async function ensureResolvableDnsConfig() {
|
|
9013
|
+
const contents = await readFile12("/etc/resolv.conf");
|
|
9014
|
+
if (contents.length === 0 || contents.at(-1) === 10) return;
|
|
9015
|
+
await new Promise((resolve5, reject) => {
|
|
9016
|
+
const child = spawn5("sudo", ["tee", "-a", "/etc/resolv.conf"], { stdio: ["pipe", "ignore", "pipe"] });
|
|
9017
|
+
let stderr = "";
|
|
9018
|
+
child.stderr.on("data", (chunk) => {
|
|
9019
|
+
stderr += chunk;
|
|
9020
|
+
});
|
|
9021
|
+
child.on("error", reject);
|
|
9022
|
+
child.on("exit", (code) => code === 0 ? resolve5() : reject(new Error(`Failed to normalize /etc/resolv.conf: ${stderr.trim()}`)));
|
|
9023
|
+
child.stdin.end("\n");
|
|
9024
|
+
});
|
|
9025
|
+
}
|
|
9026
|
+
var FxManager = class extends AcpManager {
|
|
9027
|
+
constructor(options) {
|
|
9028
|
+
super({
|
|
9029
|
+
...options,
|
|
9030
|
+
acp: {
|
|
9031
|
+
provider: "fx",
|
|
9032
|
+
command: "fx",
|
|
9033
|
+
args: (model) => ["acp", "--model", model],
|
|
9034
|
+
env: () => ({ FX_AUTO_UPGRADE: "0", AI_GATEWAY_API_KEY: ENGINE_ENV.AI_GATEWAY_API_KEY }),
|
|
9035
|
+
defaultModel: DEFAULT_FX_MODEL,
|
|
9036
|
+
prepare: ensureResolvableDnsConfig
|
|
9037
|
+
}
|
|
9038
|
+
});
|
|
9039
|
+
}
|
|
9040
|
+
};
|
|
9041
|
+
|
|
9042
|
+
// src/managers/kimi-manager.ts
|
|
9043
|
+
var KimiManager = class extends AcpManager {
|
|
9044
|
+
constructor(options) {
|
|
9045
|
+
super({
|
|
9046
|
+
...options,
|
|
9047
|
+
acp: {
|
|
9048
|
+
provider: "kimi",
|
|
9049
|
+
command: "kimi",
|
|
9050
|
+
args: () => ["acp"],
|
|
9051
|
+
env: async (model, thinkingLevel) => {
|
|
9052
|
+
let contextSize = "262144";
|
|
9053
|
+
let capabilities = model.startsWith("moonshotai/kimi") ? "image_in,thinking,tool_use" : "tool_use";
|
|
9054
|
+
try {
|
|
9055
|
+
const entry = (await fetchAiGatewayModels()).find((candidate) => candidate.id === model);
|
|
9056
|
+
if (entry) {
|
|
9057
|
+
if (entry.contextWindow) contextSize = String(entry.contextWindow);
|
|
9058
|
+
capabilities = [
|
|
9059
|
+
entry.tags.includes("vision") ? "image_in" : null,
|
|
9060
|
+
entry.tags.includes("reasoning") ? "thinking" : null,
|
|
9061
|
+
entry.tags.includes("tool-use") ? "tool_use" : null
|
|
9062
|
+
].filter((value) => value !== null).join(",") || capabilities;
|
|
9063
|
+
}
|
|
9064
|
+
} catch {
|
|
9065
|
+
}
|
|
9066
|
+
return {
|
|
9067
|
+
KIMI_DISABLE_TELEMETRY: "1",
|
|
9068
|
+
KIMI_MODEL_NAME: model,
|
|
9069
|
+
KIMI_MODEL_API_KEY: ENGINE_ENV.AI_GATEWAY_API_KEY,
|
|
9070
|
+
KIMI_MODEL_PROVIDER_TYPE: "openai",
|
|
9071
|
+
KIMI_MODEL_BASE_URL: AI_GATEWAY_BASE_URL,
|
|
9072
|
+
KIMI_MODEL_MAX_CONTEXT_SIZE: contextSize,
|
|
9073
|
+
KIMI_MODEL_CAPABILITIES: capabilities,
|
|
9074
|
+
...thinkingLevel ? {
|
|
9075
|
+
KIMI_MODEL_THINKING_EFFORT: ["low", "medium", "high"].includes(thinkingLevel) ? thinkingLevel : "high"
|
|
9076
|
+
} : {}
|
|
9077
|
+
};
|
|
9078
|
+
},
|
|
9079
|
+
defaultModel: DEFAULT_KIMI_MODEL,
|
|
9080
|
+
restartOnThinkingChange: true
|
|
9081
|
+
}
|
|
9082
|
+
});
|
|
8749
9083
|
}
|
|
8750
9084
|
};
|
|
8751
9085
|
|
|
8752
9086
|
// src/managers/opencode-manager.ts
|
|
8753
9087
|
import { existsSync as existsSync7 } from "fs";
|
|
8754
|
-
import { mkdir as mkdir14, readFile as
|
|
8755
|
-
import { delimiter, dirname as dirname8, join as
|
|
9088
|
+
import { mkdir as mkdir14, readFile as readFile13 } from "fs/promises";
|
|
9089
|
+
import { delimiter, dirname as dirname8, join as join22 } from "path";
|
|
8756
9090
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8757
9091
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8758
9092
|
import { Agent } from "undici";
|
|
@@ -8799,7 +9133,7 @@ async function getAllowedOpenRouterModels() {
|
|
|
8799
9133
|
|
|
8800
9134
|
// src/managers/opencode-manager.ts
|
|
8801
9135
|
var OPENCODE_SHIM_DIR = dirname8(fileURLToPath2(new URL("../../scripts/opencode", import.meta.url)));
|
|
8802
|
-
var OPENCODE_CONFIG_PATH =
|
|
9136
|
+
var OPENCODE_CONFIG_PATH = join22(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
|
|
8803
9137
|
var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
|
|
8804
9138
|
var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
|
|
8805
9139
|
var OPENCODE_WORKSPACE_PERMISSION = {
|
|
@@ -8836,7 +9170,7 @@ var opencodeAuthSchema = z4.record(z4.string(), z4.object({
|
|
|
8836
9170
|
async function hasOpencodeCredentials(provider) {
|
|
8837
9171
|
if (!existsSync7(OPENCODE_AUTH_PATH)) return false;
|
|
8838
9172
|
try {
|
|
8839
|
-
const auth = opencodeAuthSchema.safeParse(JSON.parse(await
|
|
9173
|
+
const auth = opencodeAuthSchema.safeParse(JSON.parse(await readFile13(OPENCODE_AUTH_PATH, "utf8")));
|
|
8840
9174
|
return auth.success && auth.data[provider]?.type === "api" && Boolean(auth.data[provider]?.key);
|
|
8841
9175
|
} catch {
|
|
8842
9176
|
return false;
|
|
@@ -8941,7 +9275,7 @@ function isOpencodeMcpEntry(value) {
|
|
|
8941
9275
|
async function readProvisionedOpencodeMcpConfig() {
|
|
8942
9276
|
let raw;
|
|
8943
9277
|
try {
|
|
8944
|
-
raw = await
|
|
9278
|
+
raw = await readFile13(OPENCODE_CONFIG_PATH, "utf8");
|
|
8945
9279
|
} catch (error) {
|
|
8946
9280
|
if (isRecord2(error) && error.code === "ENOENT") return void 0;
|
|
8947
9281
|
console.error("[OpencodeManager] Failed to read Opencode config:", error);
|
|
@@ -9049,7 +9383,7 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9049
9383
|
constructor(options) {
|
|
9050
9384
|
super(options);
|
|
9051
9385
|
this.sessionId = options.initialSessionId;
|
|
9052
|
-
this.historyFilePath = options.historyFilePath ??
|
|
9386
|
+
this.historyFilePath = options.historyFilePath ?? join22(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
|
|
9053
9387
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
9054
9388
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
9055
9389
|
}
|
|
@@ -9559,7 +9893,7 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9559
9893
|
|
|
9560
9894
|
// src/managers/pi-manager.ts
|
|
9561
9895
|
import { mkdir as mkdir15 } from "fs/promises";
|
|
9562
|
-
import { dirname as dirname9, join as
|
|
9896
|
+
import { dirname as dirname9, join as join23 } from "path";
|
|
9563
9897
|
import {
|
|
9564
9898
|
AuthStorage,
|
|
9565
9899
|
createAgentSession,
|
|
@@ -9642,7 +9976,7 @@ var PiManager = class extends CodingAgentManager {
|
|
|
9642
9976
|
providerApiKey = null;
|
|
9643
9977
|
constructor(options) {
|
|
9644
9978
|
super(options);
|
|
9645
|
-
this.historyFilePath = options.historyFilePath ??
|
|
9979
|
+
this.historyFilePath = options.historyFilePath ?? join23(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
|
|
9646
9980
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
9647
9981
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
9648
9982
|
}
|
|
@@ -9742,7 +10076,7 @@ var PiManager = class extends CodingAgentManager {
|
|
|
9742
10076
|
const sessionManager = this.initialSessionId ? SessionManager.open(this.initialSessionId, PI_HISTORY_DIR, this.workingDirectory) : SessionManager.create(this.workingDirectory, PI_HISTORY_DIR);
|
|
9743
10077
|
const resourceLoader = new DefaultResourceLoader({
|
|
9744
10078
|
cwd: this.workingDirectory,
|
|
9745
|
-
agentDir:
|
|
10079
|
+
agentDir: join23(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
|
|
9746
10080
|
extensionFactories: [registerCommandProtection(
|
|
9747
10081
|
this.workingDirectory,
|
|
9748
10082
|
this.historyFile,
|
|
@@ -9819,6 +10153,7 @@ function getAvailableRelayProviders(availability) {
|
|
|
9819
10153
|
const cursorAvailable = availability.cursorAvailable ?? false;
|
|
9820
10154
|
const deepseekAvailable = availability.deepseekAvailable ?? false;
|
|
9821
10155
|
const fxAvailable = availability.fxAvailable ?? false;
|
|
10156
|
+
const kimiAvailable = availability.kimiAvailable ?? false;
|
|
9822
10157
|
const opencodeAvailable = availability.opencodeAvailable ?? false;
|
|
9823
10158
|
const piAvailable = availability.piAvailable ?? false;
|
|
9824
10159
|
const providers = ["claude"];
|
|
@@ -9826,6 +10161,7 @@ function getAvailableRelayProviders(availability) {
|
|
|
9826
10161
|
if (cursorAvailable) providers.push("cursor");
|
|
9827
10162
|
if (deepseekAvailable) providers.push("deepseek");
|
|
9828
10163
|
if (fxAvailable) providers.push("fx");
|
|
10164
|
+
if (kimiAvailable) providers.push("kimi");
|
|
9829
10165
|
if (opencodeAvailable) providers.push("opencode");
|
|
9830
10166
|
if (piAvailable) providers.push("pi");
|
|
9831
10167
|
providers.push("relay");
|
|
@@ -9915,7 +10251,7 @@ function extractFinalResponse(history) {
|
|
|
9915
10251
|
const text = getDeepseekAssistantMessageText(payload.event, "text");
|
|
9916
10252
|
if (text) return text;
|
|
9917
10253
|
}
|
|
9918
|
-
if (event.type === "fx-session-update") {
|
|
10254
|
+
if (event.type === "fx-session-update" || event.type === "acp-session-update") {
|
|
9919
10255
|
const text = getFxTextChunk(payload.update, "agent_message_chunk");
|
|
9920
10256
|
if (text) return text;
|
|
9921
10257
|
}
|
|
@@ -9958,11 +10294,12 @@ You will also receive the chatId so you can send follow-up messages or clean up
|
|
|
9958
10294
|
`Cursor: ${AGENT_MODELS.cursor.join(", ")}.`,
|
|
9959
10295
|
`DeepSeek Harness: ${AGENT_MODELS.deepseek.join(", ")}.`,
|
|
9960
10296
|
`fx: ${AGENT_MODELS.fx.join(", ")}.`,
|
|
10297
|
+
`Kimi Code: ${AGENT_MODELS.kimi.join(", ")}.`,
|
|
9961
10298
|
`Opencode: ${AGENT_MODELS.opencode.join(", ")}.`,
|
|
9962
10299
|
`Pi: ${AGENT_MODELS.pi.join(", ")}.`
|
|
9963
10300
|
].join(" ")),
|
|
9964
10301
|
thinking_level: z5.enum(VALID_THINKING_LEVELS).optional().describe(
|
|
9965
|
-
"Controls how much thinking/reasoning the subagent applies. low = light thinking, medium = moderate, high = deep reasoning, xhigh = extended effort, max = maximum effort, ultra = Codex ultra, ultracode = Claude Code dynamic workflows. Defaults: Claude = high; Codex, Cursor, DeepSeek Harness, fx, Opencode, and Pi = medium."
|
|
10302
|
+
"Controls how much thinking/reasoning the subagent applies. low = light thinking, medium = moderate, high = deep reasoning, xhigh = extended effort, max = maximum effort, ultra = Codex ultra, ultracode = Claude Code dynamic workflows. Defaults: Claude = high; Codex, Cursor, DeepSeek Harness, fx, Kimi Code, Opencode, and Pi = medium."
|
|
9966
10303
|
),
|
|
9967
10304
|
title: z5.string().optional().describe("Optional title for the subagent chat (for identification)."),
|
|
9968
10305
|
timeout_minutes: z5.number().positive().optional().describe("Timeout in minutes for the subagent to complete (default: 10). Set higher for large tasks to avoid losing work.")
|
|
@@ -10214,14 +10551,15 @@ function getUsingToolsSection() {
|
|
|
10214
10551
|
];
|
|
10215
10552
|
return [`# Using your tools`, ...prependBullets(items)].join("\n");
|
|
10216
10553
|
}
|
|
10217
|
-
function getDelegationSection(codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, opencodeAvailable, piAvailable) {
|
|
10218
|
-
const providerList = getAvailableRelayProviders({ codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, opencodeAvailable, piAvailable }).join(", ");
|
|
10554
|
+
function getDelegationSection(codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, kimiAvailable, opencodeAvailable, piAvailable) {
|
|
10555
|
+
const providerList = getAvailableRelayProviders({ codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, kimiAvailable, opencodeAvailable, piAvailable }).join(", ");
|
|
10219
10556
|
const spawnDesc = `Create a new subagent with a specific provider (${providerList}), send it a prompt, and wait for its response. Returns the chatId and the agent's final response. You can set a custom timeout via the timeout_minutes parameter (default: 10 minutes).`;
|
|
10220
10557
|
const claudeModelList = AGENT_MODELS.claude.join(", ");
|
|
10221
10558
|
const extraAgentLines = [
|
|
10222
10559
|
codexAvailable ? `Use provider 'codex' for heavy code writing, implementation, and large refactors. Suggested models: ${AGENT_MODELS.codex.join(", ")} (${AGENT_MODELS.codex[0]} is the default).` : null,
|
|
10223
10560
|
deepseekAvailable ? `Use provider 'deepseek' for tasks that benefit from the DeepSeek Harness tool loop. Suggested models: ${AGENT_MODELS.deepseek.join(", ")}.` : null,
|
|
10224
10561
|
fxAvailable ? `Use provider 'fx' for tasks that benefit from fx's minimal native coding loop. Suggested models: ${AGENT_MODELS.fx.join(", ")}.` : null,
|
|
10562
|
+
kimiAvailable ? `Use provider 'kimi' for tasks that benefit from Kimi Code's ACP coding loop. Suggested models: ${AGENT_MODELS.kimi.join(", ")}.` : null,
|
|
10225
10563
|
opencodeAvailable ? `Use provider 'opencode' for cheaper routine implementation tasks through OpenRouter-backed open source models. Suggested models: ${AGENT_MODELS.opencode.join(", ")}.` : null,
|
|
10226
10564
|
piAvailable ? `Use provider 'pi' for coding tasks through Pi's OpenRouter-backed coding agent. Suggested models: ${AGENT_MODELS.pi.join(", ")}.` : null,
|
|
10227
10565
|
cursorAvailable ? `Use provider 'cursor' for fast iteration on code changes. Suggested models: ${AGENT_MODELS.cursor.join(", ")}.` : null
|
|
@@ -10346,14 +10684,14 @@ function getEnvironmentSection() {
|
|
|
10346
10684
|
].join("\n");
|
|
10347
10685
|
}
|
|
10348
10686
|
function buildRelaySystemPrompt(options) {
|
|
10349
|
-
const { customInstructions, codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, opencodeAvailable, piAvailable } = options ?? {};
|
|
10687
|
+
const { customInstructions, codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, kimiAvailable, opencodeAvailable, piAvailable } = options ?? {};
|
|
10350
10688
|
const sections = [
|
|
10351
10689
|
getIntroSection(),
|
|
10352
10690
|
getSystemSection(),
|
|
10353
10691
|
getDoingTasksSection(),
|
|
10354
10692
|
getActionsSection(),
|
|
10355
10693
|
getUsingToolsSection(),
|
|
10356
|
-
getDelegationSection(codexAvailable ?? false, cursorAvailable ?? false, deepseekAvailable ?? false, fxAvailable ?? false, opencodeAvailable ?? false, piAvailable ?? false),
|
|
10694
|
+
getDelegationSection(codexAvailable ?? false, cursorAvailable ?? false, deepseekAvailable ?? false, fxAvailable ?? false, kimiAvailable ?? false, opencodeAvailable ?? false, piAvailable ?? false),
|
|
10357
10695
|
getToneAndStyleSection(),
|
|
10358
10696
|
getOutputEfficiencySection(),
|
|
10359
10697
|
getEnvironmentSection(),
|
|
@@ -10387,9 +10725,10 @@ var RelayManager = class {
|
|
|
10387
10725
|
const cursorAvailable = options.cursorAvailable ?? false;
|
|
10388
10726
|
const deepseekAvailable = options.deepseekAvailable ?? false;
|
|
10389
10727
|
const fxAvailable = options.fxAvailable ?? false;
|
|
10728
|
+
const kimiAvailable = options.kimiAvailable ?? false;
|
|
10390
10729
|
const opencodeAvailable = options.opencodeAvailable ?? false;
|
|
10391
10730
|
const piAvailable = options.piAvailable ?? false;
|
|
10392
|
-
const availability = { codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, opencodeAvailable, piAvailable };
|
|
10731
|
+
const availability = { codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, kimiAvailable, opencodeAvailable, piAvailable };
|
|
10393
10732
|
const getProviderAvailability = options.getProviderAvailability ?? (() => availability);
|
|
10394
10733
|
this.inner = new ClaudeManager({
|
|
10395
10734
|
...options,
|
|
@@ -10454,16 +10793,16 @@ var RelayManager = class {
|
|
|
10454
10793
|
import {
|
|
10455
10794
|
appendFile as appendFile3,
|
|
10456
10795
|
mkdir as mkdir16,
|
|
10457
|
-
readFile as
|
|
10796
|
+
readFile as readFile14,
|
|
10458
10797
|
readdir as readdir6,
|
|
10459
10798
|
rename as rename2,
|
|
10460
10799
|
unlink as unlink3
|
|
10461
10800
|
} from "fs/promises";
|
|
10462
|
-
import { join as
|
|
10463
|
-
import { randomUUID as
|
|
10801
|
+
import { join as join24 } from "path";
|
|
10802
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
10464
10803
|
|
|
10465
10804
|
// src/analytics/agent/activity/skill-mcp-call-extractor.ts
|
|
10466
|
-
var NON_MCP_SERVERS = /* @__PURE__ */ new Set(["claude", "cursor", "deepseek", "fx", "opencode", "pi", "custom", "dynamic"]);
|
|
10805
|
+
var NON_MCP_SERVERS = /* @__PURE__ */ new Set(["claude", "cursor", "deepseek", "fx", "kimi", "opencode", "pi", "custom", "dynamic"]);
|
|
10467
10806
|
function mcpNameFromToolCall(message) {
|
|
10468
10807
|
const parsedName = parseMcpToolName(message.tool);
|
|
10469
10808
|
if (parsedName) return parsedName.server;
|
|
@@ -10510,7 +10849,7 @@ var AgentChatActivityBuffer = class {
|
|
|
10510
10849
|
options.storageName,
|
|
10511
10850
|
...options.legacyStorageNames ?? []
|
|
10512
10851
|
];
|
|
10513
|
-
this.liveFile =
|
|
10852
|
+
this.liveFile = join24(ENGINE_DIR2, `${options.storageName}.jsonl`);
|
|
10514
10853
|
this.segmentFilePatterns = this.storageNames.map(
|
|
10515
10854
|
(storageName) => new RegExp(`^${storageName}\\.(\\d+)\\.jsonl$`)
|
|
10516
10855
|
);
|
|
@@ -10569,8 +10908,8 @@ var AgentChatActivityBuffer = class {
|
|
|
10569
10908
|
await Promise.allSettled([...this.pendingAppends]);
|
|
10570
10909
|
for (const storageName of this.storageNames) {
|
|
10571
10910
|
await rename2(
|
|
10572
|
-
|
|
10573
|
-
|
|
10911
|
+
join24(ENGINE_DIR2, `${storageName}.jsonl`),
|
|
10912
|
+
join24(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
|
|
10574
10913
|
).catch(() => {
|
|
10575
10914
|
});
|
|
10576
10915
|
}
|
|
@@ -10581,7 +10920,7 @@ var AgentChatActivityBuffer = class {
|
|
|
10581
10920
|
if (!this.segmentFilePatterns.some((pattern) => pattern.test(entry)))
|
|
10582
10921
|
continue;
|
|
10583
10922
|
try {
|
|
10584
|
-
await this.uploadSegment(
|
|
10923
|
+
await this.uploadSegment(join24(ENGINE_DIR2, entry));
|
|
10585
10924
|
flushed++;
|
|
10586
10925
|
} catch (error) {
|
|
10587
10926
|
failed++;
|
|
@@ -10613,13 +10952,13 @@ var AgentChatActivityBuffer = class {
|
|
|
10613
10952
|
) && entry.endsWith(UPLOADED_SUFFIX)
|
|
10614
10953
|
).sort();
|
|
10615
10954
|
for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
|
|
10616
|
-
await unlink3(
|
|
10955
|
+
await unlink3(join24(ENGINE_DIR2, entry)).catch(() => {
|
|
10617
10956
|
});
|
|
10618
10957
|
}
|
|
10619
10958
|
return { flushed, failed };
|
|
10620
10959
|
}
|
|
10621
10960
|
async uploadSegment(filePath) {
|
|
10622
|
-
const records = (await
|
|
10961
|
+
const records = (await readFile14(filePath, "utf-8")).split("\n").flatMap((line) => {
|
|
10623
10962
|
try {
|
|
10624
10963
|
const parsed = JSON.parse(line);
|
|
10625
10964
|
return this.options.validate(parsed) ? [parsed] : [];
|
|
@@ -10681,7 +11020,7 @@ var AgentChatTurnActivityTracker = class {
|
|
|
10681
11020
|
const credential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[provider];
|
|
10682
11021
|
this.pendingMessages.delete(messageId);
|
|
10683
11022
|
this.activeTurns.set(chatId, {
|
|
10684
|
-
turnId:
|
|
11023
|
+
turnId: randomUUID7(),
|
|
10685
11024
|
startedAtMs: Date.now(),
|
|
10686
11025
|
provider,
|
|
10687
11026
|
model: attributes.model ?? getDefaultAgentModel(provider),
|
|
@@ -10844,19 +11183,19 @@ var KeepAliveService = class _KeepAliveService {
|
|
|
10844
11183
|
var keepAliveService = new KeepAliveService();
|
|
10845
11184
|
|
|
10846
11185
|
// src/services/canvas-service.ts
|
|
10847
|
-
import { readdir as readdir7, readFile as
|
|
11186
|
+
import { readdir as readdir7, readFile as readFile15, stat as stat3 } from "fs/promises";
|
|
10848
11187
|
import { homedir as homedir14 } from "os";
|
|
10849
|
-
import { join as
|
|
11188
|
+
import { join as join25 } from "path";
|
|
10850
11189
|
var GLOBAL_CANVAS_DIRECTORIES = [
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
11190
|
+
join25(homedir14(), ".claude", "plans"),
|
|
11191
|
+
join25(process.env.XDG_DATA_HOME ?? join25(homedir14(), ".local", "share"), "opencode", "plans"),
|
|
11192
|
+
join25(homedir14(), ".replicas", "canvas")
|
|
10854
11193
|
];
|
|
10855
11194
|
async function canvasDirectories() {
|
|
10856
11195
|
const repositories = await gitService.listRepositories().catch(() => []);
|
|
10857
11196
|
return [
|
|
10858
11197
|
...GLOBAL_CANVAS_DIRECTORIES,
|
|
10859
|
-
...repositories.map((repository) =>
|
|
11198
|
+
...repositories.map((repository) => join25(repository.path, ".opencode", "plans"))
|
|
10860
11199
|
];
|
|
10861
11200
|
}
|
|
10862
11201
|
var CanvasService = class {
|
|
@@ -10880,7 +11219,7 @@ var CanvasService = class {
|
|
|
10880
11219
|
for (const entry of entries) {
|
|
10881
11220
|
if (entry.name.startsWith(".")) continue;
|
|
10882
11221
|
const filename = current.relativePath ? `${current.relativePath}/${entry.name}` : entry.name;
|
|
10883
|
-
const filePath =
|
|
11222
|
+
const filePath = join25(current.directory, entry.name);
|
|
10884
11223
|
if (entry.isDirectory()) {
|
|
10885
11224
|
pending.push({ directory: filePath, relativePath: filename });
|
|
10886
11225
|
continue;
|
|
@@ -10905,7 +11244,7 @@ var CanvasService = class {
|
|
|
10905
11244
|
if (!safe) return null;
|
|
10906
11245
|
const { kind, mimeType } = classifyCanvasFilename(safe);
|
|
10907
11246
|
for (const directory of await this.directories()) {
|
|
10908
|
-
const filePath =
|
|
11247
|
+
const filePath = join25(directory, safe);
|
|
10909
11248
|
let sizeBytes = 0;
|
|
10910
11249
|
let updatedAt = "";
|
|
10911
11250
|
try {
|
|
@@ -10926,7 +11265,7 @@ var CanvasService = class {
|
|
|
10926
11265
|
};
|
|
10927
11266
|
}
|
|
10928
11267
|
try {
|
|
10929
|
-
const bytes = await
|
|
11268
|
+
const bytes = await readFile15(filePath);
|
|
10930
11269
|
return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
|
|
10931
11270
|
} catch {
|
|
10932
11271
|
continue;
|
|
@@ -11076,14 +11415,14 @@ async function reconcileCanvasItems(filenames) {
|
|
|
11076
11415
|
// src/services/upload-chat-transcripts.ts
|
|
11077
11416
|
import { createReadStream } from "fs";
|
|
11078
11417
|
import { createHash as createHash2 } from "crypto";
|
|
11079
|
-
import { readFile as
|
|
11080
|
-
import { basename as basename2, join as
|
|
11418
|
+
import { readFile as readFile16, readdir as readdir8, stat as stat4 } from "fs/promises";
|
|
11419
|
+
import { basename as basename2, join as join27 } from "path";
|
|
11081
11420
|
|
|
11082
11421
|
// src/services/chat/chat-senders.ts
|
|
11083
|
-
import { join as
|
|
11084
|
-
var CHAT_SENDERS_DIR =
|
|
11422
|
+
import { join as join26 } from "path";
|
|
11423
|
+
var CHAT_SENDERS_DIR = join26(ENGINE_DIR2, "chat-senders");
|
|
11085
11424
|
function chatMessageSendersFilePath(chatId) {
|
|
11086
|
-
return
|
|
11425
|
+
return join26(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
11087
11426
|
}
|
|
11088
11427
|
function parseChatMessageSendersJsonl(content) {
|
|
11089
11428
|
return content.split("\n").flatMap((line) => {
|
|
@@ -11099,9 +11438,9 @@ function parseChatMessageSendersJsonl(content) {
|
|
|
11099
11438
|
|
|
11100
11439
|
// src/services/upload-chat-transcripts.ts
|
|
11101
11440
|
var HISTORY_DIRS = [
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11441
|
+
join27(ENGINE_DIR2, "claude-histories"),
|
|
11442
|
+
join27(ENGINE_DIR2, "relay-histories"),
|
|
11443
|
+
join27(ENGINE_DIR2, "codex-histories")
|
|
11105
11444
|
];
|
|
11106
11445
|
async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), capture) {
|
|
11107
11446
|
let flushed = 0;
|
|
@@ -11119,7 +11458,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
11119
11458
|
if (!entry.endsWith(".jsonl")) continue;
|
|
11120
11459
|
const chatId = basename2(entry, ".jsonl");
|
|
11121
11460
|
tasks.push(
|
|
11122
|
-
uploadChatTranscript(chatId,
|
|
11461
|
+
uploadChatTranscript(chatId, join27(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
|
|
11123
11462
|
flushed++;
|
|
11124
11463
|
if (artifact && capture) revisions.push(artifact);
|
|
11125
11464
|
}).catch((err) => {
|
|
@@ -11135,7 +11474,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
11135
11474
|
async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
11136
11475
|
const { size } = await stat4(filePath);
|
|
11137
11476
|
if (size === 0) return null;
|
|
11138
|
-
const historyPages = createChatTranscriptPages(await
|
|
11477
|
+
const historyPages = createChatTranscriptPages(await readFile16(filePath));
|
|
11139
11478
|
const metadata = chat ? {
|
|
11140
11479
|
provider: chat.provider,
|
|
11141
11480
|
credential: ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[chat.provider],
|
|
@@ -11154,7 +11493,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
|
11154
11493
|
}
|
|
11155
11494
|
try {
|
|
11156
11495
|
metadata.senders = parseChatMessageSendersJsonl(
|
|
11157
|
-
await
|
|
11496
|
+
await readFile16(chatMessageSendersFilePath(chatId), "utf-8")
|
|
11158
11497
|
);
|
|
11159
11498
|
} catch (error) {
|
|
11160
11499
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
@@ -11237,7 +11576,7 @@ async function flushRepoState() {
|
|
|
11237
11576
|
// src/services/upload-engine-logs.ts
|
|
11238
11577
|
import { createReadStream as createReadStream2 } from "fs";
|
|
11239
11578
|
import { readdir as readdir9, stat as stat5 } from "fs/promises";
|
|
11240
|
-
import { join as
|
|
11579
|
+
import { join as join28 } from "path";
|
|
11241
11580
|
var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
|
|
11242
11581
|
var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
|
|
11243
11582
|
var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
|
|
@@ -11266,7 +11605,7 @@ async function flushAllEngineLogs() {
|
|
|
11266
11605
|
const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
|
|
11267
11606
|
try {
|
|
11268
11607
|
const sessionId = filename.slice(0, -".log".length);
|
|
11269
|
-
const filePath =
|
|
11608
|
+
const filePath = join28(LOG_DIR, filename);
|
|
11270
11609
|
const fileStat = await runBeforeDeadline(() => stat5(filePath), deadline);
|
|
11271
11610
|
if (!fileStat.isFile()) {
|
|
11272
11611
|
skipped++;
|
|
@@ -11362,7 +11701,7 @@ async function uploadEngineLog(input, timeoutMs) {
|
|
|
11362
11701
|
}
|
|
11363
11702
|
|
|
11364
11703
|
// src/services/chat/chat-service.ts
|
|
11365
|
-
var CODEX_AUTH_PATH2 =
|
|
11704
|
+
var CODEX_AUTH_PATH2 = join29(homedir15(), ".codex", "auth.json");
|
|
11366
11705
|
var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
|
|
11367
11706
|
function isCodexAvailable() {
|
|
11368
11707
|
return existsSync8(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
|
|
@@ -11376,6 +11715,9 @@ function isDeepseekAvailable() {
|
|
|
11376
11715
|
function isFxAvailable() {
|
|
11377
11716
|
return Boolean(ENGINE_ENV.AI_GATEWAY_API_KEY);
|
|
11378
11717
|
}
|
|
11718
|
+
function isKimiAvailable() {
|
|
11719
|
+
return Boolean(ENGINE_ENV.AI_GATEWAY_API_KEY);
|
|
11720
|
+
}
|
|
11379
11721
|
function isPiAvailable() {
|
|
11380
11722
|
return existsSync8(PI_AUTH_PATH);
|
|
11381
11723
|
}
|
|
@@ -11568,7 +11910,7 @@ var ChatService = class {
|
|
|
11568
11910
|
throw new ChatNotFoundError(parentChatId);
|
|
11569
11911
|
}
|
|
11570
11912
|
const persisted = {
|
|
11571
|
-
id: request.id ??
|
|
11913
|
+
id: request.id ?? randomUUID8(),
|
|
11572
11914
|
provider: request.provider,
|
|
11573
11915
|
title,
|
|
11574
11916
|
createdAt: now,
|
|
@@ -11659,7 +12001,7 @@ var ChatService = class {
|
|
|
11659
12001
|
}
|
|
11660
12002
|
}
|
|
11661
12003
|
async readSenders(chatId) {
|
|
11662
|
-
return
|
|
12004
|
+
return readFile17(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
|
|
11663
12005
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
|
|
11664
12006
|
console.error("[ChatService] Failed to read sender records:", error);
|
|
11665
12007
|
return [];
|
|
@@ -11885,7 +12227,7 @@ var ChatService = class {
|
|
|
11885
12227
|
return descendants;
|
|
11886
12228
|
}
|
|
11887
12229
|
async deleteHistoryFile(persisted) {
|
|
11888
|
-
const historyPath =
|
|
12230
|
+
const historyPath = join29(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
|
|
11889
12231
|
await Promise.all([
|
|
11890
12232
|
historyPath,
|
|
11891
12233
|
`${historyPath}.pages.jsonl`,
|
|
@@ -12008,7 +12350,7 @@ var ChatService = class {
|
|
|
12008
12350
|
if (persisted.provider === "claude") {
|
|
12009
12351
|
provider = new ClaudeManager({
|
|
12010
12352
|
workingDirectory: this.workingDirectory,
|
|
12011
|
-
historyFilePath:
|
|
12353
|
+
historyFilePath: join29(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12012
12354
|
initialSessionId: persisted.providerSessionId,
|
|
12013
12355
|
onSaveSessionId: saveSession,
|
|
12014
12356
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12022,11 +12364,12 @@ var ChatService = class {
|
|
|
12022
12364
|
piAvailable: isPiAvailable(),
|
|
12023
12365
|
cursorAvailable: isCursorAvailable(),
|
|
12024
12366
|
deepseekAvailable: isDeepseekAvailable(),
|
|
12025
|
-
fxAvailable: isFxAvailable()
|
|
12367
|
+
fxAvailable: isFxAvailable(),
|
|
12368
|
+
kimiAvailable: isKimiAvailable()
|
|
12026
12369
|
});
|
|
12027
12370
|
provider = new RelayManager({
|
|
12028
12371
|
workingDirectory: this.workingDirectory,
|
|
12029
|
-
historyFilePath:
|
|
12372
|
+
historyFilePath: join29(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12030
12373
|
initialSessionId: persisted.providerSessionId,
|
|
12031
12374
|
onSaveSessionId: saveSession,
|
|
12032
12375
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12039,7 +12382,7 @@ var ChatService = class {
|
|
|
12039
12382
|
} else if (persisted.provider === "cursor") {
|
|
12040
12383
|
provider = new CursorManager({
|
|
12041
12384
|
workingDirectory: this.workingDirectory,
|
|
12042
|
-
historyFilePath:
|
|
12385
|
+
historyFilePath: join29(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12043
12386
|
initialSessionId: persisted.providerSessionId,
|
|
12044
12387
|
onSaveSessionId: saveSession,
|
|
12045
12388
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12049,7 +12392,7 @@ var ChatService = class {
|
|
|
12049
12392
|
} else if (persisted.provider === "deepseek") {
|
|
12050
12393
|
provider = new DeepseekManager({
|
|
12051
12394
|
workingDirectory: this.workingDirectory,
|
|
12052
|
-
historyFilePath:
|
|
12395
|
+
historyFilePath: join29(DEEPSEEK_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12053
12396
|
initialSessionId: persisted.providerSessionId,
|
|
12054
12397
|
onSaveSessionId: saveSession,
|
|
12055
12398
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12059,7 +12402,17 @@ var ChatService = class {
|
|
|
12059
12402
|
} else if (persisted.provider === "fx") {
|
|
12060
12403
|
provider = new FxManager({
|
|
12061
12404
|
workingDirectory: this.workingDirectory,
|
|
12062
|
-
historyFilePath:
|
|
12405
|
+
historyFilePath: join29(FX_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12406
|
+
initialSessionId: persisted.providerSessionId,
|
|
12407
|
+
onSaveSessionId: saveSession,
|
|
12408
|
+
onTurnComplete: onProviderTurnComplete,
|
|
12409
|
+
onEvent: onProviderEvent,
|
|
12410
|
+
onProcessingChanged
|
|
12411
|
+
});
|
|
12412
|
+
} else if (persisted.provider === "kimi") {
|
|
12413
|
+
provider = new KimiManager({
|
|
12414
|
+
workingDirectory: this.workingDirectory,
|
|
12415
|
+
historyFilePath: join29(KIMI_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12063
12416
|
initialSessionId: persisted.providerSessionId,
|
|
12064
12417
|
onSaveSessionId: saveSession,
|
|
12065
12418
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12069,7 +12422,7 @@ var ChatService = class {
|
|
|
12069
12422
|
} else if (persisted.provider === "opencode") {
|
|
12070
12423
|
provider = new OpencodeManager({
|
|
12071
12424
|
workingDirectory: this.workingDirectory,
|
|
12072
|
-
historyFilePath:
|
|
12425
|
+
historyFilePath: join29(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12073
12426
|
initialSessionId: persisted.providerSessionId,
|
|
12074
12427
|
onSaveSessionId: saveSession,
|
|
12075
12428
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12079,7 +12432,7 @@ var ChatService = class {
|
|
|
12079
12432
|
} else if (persisted.provider === "pi") {
|
|
12080
12433
|
provider = new PiManager({
|
|
12081
12434
|
workingDirectory: this.workingDirectory,
|
|
12082
|
-
historyFilePath:
|
|
12435
|
+
historyFilePath: join29(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12083
12436
|
initialSessionId: persisted.providerSessionId,
|
|
12084
12437
|
onSaveSessionId: saveSession,
|
|
12085
12438
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12089,7 +12442,7 @@ var ChatService = class {
|
|
|
12089
12442
|
} else {
|
|
12090
12443
|
provider = new CodexAspManager({
|
|
12091
12444
|
workingDirectory: this.workingDirectory,
|
|
12092
|
-
historyFilePath:
|
|
12445
|
+
historyFilePath: join29(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12093
12446
|
initialSessionId: persisted.providerSessionId,
|
|
12094
12447
|
onSaveSessionId: saveSession,
|
|
12095
12448
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12254,7 +12607,7 @@ var ChatService = class {
|
|
|
12254
12607
|
});
|
|
12255
12608
|
uploadChatTranscript(
|
|
12256
12609
|
chatId,
|
|
12257
|
-
|
|
12610
|
+
join29(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
|
|
12258
12611
|
this.toSummary(chat)
|
|
12259
12612
|
).catch((err) => {
|
|
12260
12613
|
console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
|
|
@@ -12270,7 +12623,7 @@ var ChatService = class {
|
|
|
12270
12623
|
}
|
|
12271
12624
|
async loadChats() {
|
|
12272
12625
|
try {
|
|
12273
|
-
const content = await
|
|
12626
|
+
const content = await readFile17(CHATS_FILE, "utf-8");
|
|
12274
12627
|
return parsePersistedChatsContent(content);
|
|
12275
12628
|
} catch (error) {
|
|
12276
12629
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -12285,7 +12638,7 @@ var ChatService = class {
|
|
|
12285
12638
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
12286
12639
|
}
|
|
12287
12640
|
try {
|
|
12288
|
-
const backupContent = await
|
|
12641
|
+
const backupContent = await readFile17(CHATS_BACKUP_FILE, "utf-8");
|
|
12289
12642
|
return parsePersistedChatsContent(backupContent);
|
|
12290
12643
|
} catch (backupError) {
|
|
12291
12644
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -12324,7 +12677,7 @@ var ChatService = class {
|
|
|
12324
12677
|
}
|
|
12325
12678
|
async publish(input) {
|
|
12326
12679
|
const event = {
|
|
12327
|
-
id:
|
|
12680
|
+
id: randomUUID8(),
|
|
12328
12681
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12329
12682
|
...input
|
|
12330
12683
|
};
|
|
@@ -12391,8 +12744,8 @@ var ChatService = class {
|
|
|
12391
12744
|
|
|
12392
12745
|
// src/services/repo-file-service.ts
|
|
12393
12746
|
import { execFile } from "child_process";
|
|
12394
|
-
import { readFile as
|
|
12395
|
-
import { join as
|
|
12747
|
+
import { readFile as readFile18, realpath, stat as stat6 } from "fs/promises";
|
|
12748
|
+
import { join as join30, resolve as resolve2, extname as extname2 } from "path";
|
|
12396
12749
|
var CACHE_TTL_MS = 3e4;
|
|
12397
12750
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
12398
12751
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -12544,7 +12897,7 @@ var RepoFileService = class {
|
|
|
12544
12897
|
const repo = repos.find((r) => r.name === repoName);
|
|
12545
12898
|
if (!repo) return null;
|
|
12546
12899
|
try {
|
|
12547
|
-
const fullPath = await realpath(resolve2(
|
|
12900
|
+
const fullPath = await realpath(resolve2(join30(repo.path, filePath)));
|
|
12548
12901
|
const repoRoot = await realpath(repo.path);
|
|
12549
12902
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
12550
12903
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
@@ -12575,7 +12928,7 @@ var RepoFileService = class {
|
|
|
12575
12928
|
sizeBytes,
|
|
12576
12929
|
binary: true,
|
|
12577
12930
|
tooLarge: false,
|
|
12578
|
-
base64: (await
|
|
12931
|
+
base64: (await readFile18(fullPath)).toString("base64"),
|
|
12579
12932
|
mimeType
|
|
12580
12933
|
};
|
|
12581
12934
|
}
|
|
@@ -12600,7 +12953,7 @@ var RepoFileService = class {
|
|
|
12600
12953
|
tooLarge: true
|
|
12601
12954
|
};
|
|
12602
12955
|
}
|
|
12603
|
-
const content = await
|
|
12956
|
+
const content = await readFile18(fullPath, "utf-8");
|
|
12604
12957
|
return {
|
|
12605
12958
|
repoName,
|
|
12606
12959
|
path: filePath,
|
|
@@ -12678,21 +13031,21 @@ var RepoFileService = class {
|
|
|
12678
13031
|
// src/v1-routes.ts
|
|
12679
13032
|
import { Hono } from "hono";
|
|
12680
13033
|
import { z as z6 } from "zod";
|
|
12681
|
-
import { readdir as readdir11, stat as stat7, readFile as
|
|
12682
|
-
import { join as
|
|
13034
|
+
import { readdir as readdir11, stat as stat7, readFile as readFile21 } from "fs/promises";
|
|
13035
|
+
import { join as join33, resolve as resolve3 } from "path";
|
|
12683
13036
|
|
|
12684
13037
|
// src/services/warm-hooks-service.ts
|
|
12685
|
-
import { spawn as
|
|
12686
|
-
import { readFile as
|
|
13038
|
+
import { spawn as spawn6 } from "child_process";
|
|
13039
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
12687
13040
|
import { existsSync as existsSync9 } from "fs";
|
|
12688
|
-
import { join as
|
|
13041
|
+
import { join as join32 } from "path";
|
|
12689
13042
|
|
|
12690
13043
|
// src/services/warm-hook-logs-service.ts
|
|
12691
|
-
import { mkdir as mkdir18, readFile as
|
|
13044
|
+
import { mkdir as mkdir18, readFile as readFile19, writeFile as writeFile7, readdir as readdir10, appendFile as appendFile5, unlink as unlink4 } from "fs/promises";
|
|
12692
13045
|
import { homedir as homedir16 } from "os";
|
|
12693
|
-
import { join as
|
|
12694
|
-
var LOGS_DIR2 =
|
|
12695
|
-
var CURRENT_RUN_LOG =
|
|
13046
|
+
import { join as join31 } from "path";
|
|
13047
|
+
var LOGS_DIR2 = join31(homedir16(), ".replicas", "warm-hook-logs");
|
|
13048
|
+
var CURRENT_RUN_LOG = join31(LOGS_DIR2, "current-run.log");
|
|
12696
13049
|
var GLOBAL_FILENAME = "global.json";
|
|
12697
13050
|
function withPreview2(stored) {
|
|
12698
13051
|
const preview = buildHookOutputPreview(stored.output);
|
|
@@ -12709,7 +13062,7 @@ var WarmHookLogsService = class {
|
|
|
12709
13062
|
hookName: "organization",
|
|
12710
13063
|
...entry
|
|
12711
13064
|
};
|
|
12712
|
-
await
|
|
13065
|
+
await writeFile7(join31(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
12713
13066
|
`, "utf-8");
|
|
12714
13067
|
}
|
|
12715
13068
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -12719,7 +13072,7 @@ var WarmHookLogsService = class {
|
|
|
12719
13072
|
hookName: "environment",
|
|
12720
13073
|
...entry
|
|
12721
13074
|
};
|
|
12722
|
-
await
|
|
13075
|
+
await writeFile7(join31(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
12723
13076
|
`, "utf-8");
|
|
12724
13077
|
}
|
|
12725
13078
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -12729,7 +13082,7 @@ var WarmHookLogsService = class {
|
|
|
12729
13082
|
hookName: repoName,
|
|
12730
13083
|
...entry
|
|
12731
13084
|
};
|
|
12732
|
-
await
|
|
13085
|
+
await writeFile7(join31(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
12733
13086
|
`, "utf-8");
|
|
12734
13087
|
}
|
|
12735
13088
|
async getAllLogs() {
|
|
@@ -12748,7 +13101,7 @@ var WarmHookLogsService = class {
|
|
|
12748
13101
|
continue;
|
|
12749
13102
|
}
|
|
12750
13103
|
try {
|
|
12751
|
-
const raw = await
|
|
13104
|
+
const raw = await readFile19(join31(LOGS_DIR2, file), "utf-8");
|
|
12752
13105
|
const stored = JSON.parse(raw);
|
|
12753
13106
|
logs.push(withPreview2(stored));
|
|
12754
13107
|
} catch {
|
|
@@ -12777,7 +13130,7 @@ var WarmHookLogsService = class {
|
|
|
12777
13130
|
}
|
|
12778
13131
|
async getCurrentRunLog() {
|
|
12779
13132
|
try {
|
|
12780
|
-
return await
|
|
13133
|
+
return await readFile19(CURRENT_RUN_LOG, "utf-8");
|
|
12781
13134
|
} catch (err) {
|
|
12782
13135
|
if (err.code === "ENOENT") return null;
|
|
12783
13136
|
throw err;
|
|
@@ -12786,7 +13139,7 @@ var WarmHookLogsService = class {
|
|
|
12786
13139
|
async getFullOutput(hookType, hookName) {
|
|
12787
13140
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
12788
13141
|
try {
|
|
12789
|
-
const raw = await
|
|
13142
|
+
const raw = await readFile19(join31(LOGS_DIR2, filename), "utf-8");
|
|
12790
13143
|
const stored = JSON.parse(raw);
|
|
12791
13144
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
12792
13145
|
return null;
|
|
@@ -12805,12 +13158,12 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
12805
13158
|
// src/services/warm-hooks-service.ts
|
|
12806
13159
|
async function readRepoWarmHook(repoPath) {
|
|
12807
13160
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
12808
|
-
const configPath =
|
|
13161
|
+
const configPath = join32(repoPath, filename);
|
|
12809
13162
|
if (!existsSync9(configPath)) {
|
|
12810
13163
|
continue;
|
|
12811
13164
|
}
|
|
12812
13165
|
try {
|
|
12813
|
-
const raw = await
|
|
13166
|
+
const raw = await readFile20(configPath, "utf-8");
|
|
12814
13167
|
const config = parseReplicasConfigString(raw, filename);
|
|
12815
13168
|
if (!config.warmHook) {
|
|
12816
13169
|
return null;
|
|
@@ -12857,7 +13210,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
12857
13210
|
params.onChunk(`$ ${params.label}
|
|
12858
13211
|
`);
|
|
12859
13212
|
return new Promise((resolve5) => {
|
|
12860
|
-
const proc =
|
|
13213
|
+
const proc = spawn6("bash", ["-lc", params.content], {
|
|
12861
13214
|
cwd: params.cwd,
|
|
12862
13215
|
env: process.env,
|
|
12863
13216
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -13066,9 +13419,9 @@ ${combinedScript}` : combinedScript;
|
|
|
13066
13419
|
}
|
|
13067
13420
|
|
|
13068
13421
|
// src/services/terminal-service.ts
|
|
13069
|
-
import { randomUUID as
|
|
13422
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
13070
13423
|
import { existsSync as existsSync10 } from "fs";
|
|
13071
|
-
import { spawn as
|
|
13424
|
+
import { spawn as spawn7 } from "node-pty";
|
|
13072
13425
|
var MAX_REPLAY_CHARS = 1024 * 1024;
|
|
13073
13426
|
var MAX_TERMINAL_SESSIONS = 8;
|
|
13074
13427
|
var MAX_PENDING_INPUT = 64;
|
|
@@ -13085,9 +13438,9 @@ var TerminalService = class {
|
|
|
13085
13438
|
code: "limit"
|
|
13086
13439
|
});
|
|
13087
13440
|
}
|
|
13088
|
-
const id =
|
|
13441
|
+
const id = randomUUID9();
|
|
13089
13442
|
const shell = process.env.SHELL && existsSync10(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
|
|
13090
|
-
const pty =
|
|
13443
|
+
const pty = spawn7(shell, ["-l"], {
|
|
13091
13444
|
name: "xterm-256color",
|
|
13092
13445
|
cols,
|
|
13093
13446
|
rows,
|
|
@@ -14089,7 +14442,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
14089
14442
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
14090
14443
|
const sessions = await Promise.all(
|
|
14091
14444
|
logFiles.map(async (filename) => {
|
|
14092
|
-
const filePath =
|
|
14445
|
+
const filePath = join33(LOG_DIR, filename);
|
|
14093
14446
|
const fileStat = await stat7(filePath);
|
|
14094
14447
|
const sessionId = filename.replace(/\.log$/, "");
|
|
14095
14448
|
return {
|
|
@@ -14124,7 +14477,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
14124
14477
|
}
|
|
14125
14478
|
let content;
|
|
14126
14479
|
try {
|
|
14127
|
-
content = await
|
|
14480
|
+
content = await readFile21(filePath, "utf-8");
|
|
14128
14481
|
} catch {
|
|
14129
14482
|
return c.json(jsonError("Log session not found"), 404);
|
|
14130
14483
|
}
|
|
@@ -14400,7 +14753,7 @@ function startStatusBroadcaster() {
|
|
|
14400
14753
|
if (serialized !== previousRepoStatus) {
|
|
14401
14754
|
previousRepoStatus = serialized;
|
|
14402
14755
|
eventService.publish({
|
|
14403
|
-
id:
|
|
14756
|
+
id: randomUUID10(),
|
|
14404
14757
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14405
14758
|
type: "repo.status.changed",
|
|
14406
14759
|
payload: { repos }
|
|
@@ -14421,7 +14774,7 @@ function startStatusBroadcaster() {
|
|
|
14421
14774
|
if (engineStatusJson !== previousEngineStatus) {
|
|
14422
14775
|
previousEngineStatus = engineStatusJson;
|
|
14423
14776
|
eventService.publish({
|
|
14424
|
-
id:
|
|
14777
|
+
id: randomUUID10(),
|
|
14425
14778
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14426
14779
|
type: "engine.status.changed",
|
|
14427
14780
|
payload: { status: engineStatus }
|
|
@@ -14440,7 +14793,7 @@ function startStatusBroadcaster() {
|
|
|
14440
14793
|
previousHookStatus = hookSnapshot;
|
|
14441
14794
|
if (!lastHooksRunning && hooksRunning) {
|
|
14442
14795
|
eventService.publish({
|
|
14443
|
-
id:
|
|
14796
|
+
id: randomUUID10(),
|
|
14444
14797
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14445
14798
|
type: "hooks.started",
|
|
14446
14799
|
payload: { running: true, completed: false }
|
|
@@ -14449,7 +14802,7 @@ function startStatusBroadcaster() {
|
|
|
14449
14802
|
}
|
|
14450
14803
|
if (hooksRunning) {
|
|
14451
14804
|
eventService.publish({
|
|
14452
|
-
id:
|
|
14805
|
+
id: randomUUID10(),
|
|
14453
14806
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14454
14807
|
type: "hooks.progress",
|
|
14455
14808
|
payload: { running: true, completed: false }
|
|
@@ -14458,7 +14811,7 @@ function startStatusBroadcaster() {
|
|
|
14458
14811
|
}
|
|
14459
14812
|
if (lastHooksRunning && !hooksRunning && hooksCompleted && !hooksFailed) {
|
|
14460
14813
|
eventService.publish({
|
|
14461
|
-
id:
|
|
14814
|
+
id: randomUUID10(),
|
|
14462
14815
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14463
14816
|
type: "hooks.completed",
|
|
14464
14817
|
payload: { running: false, completed: true }
|
|
@@ -14467,7 +14820,7 @@ function startStatusBroadcaster() {
|
|
|
14467
14820
|
}
|
|
14468
14821
|
if (lastHooksRunning && !hooksRunning && hooksFailed) {
|
|
14469
14822
|
eventService.publish({
|
|
14470
|
-
id:
|
|
14823
|
+
id: randomUUID10(),
|
|
14471
14824
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14472
14825
|
type: "hooks.failed",
|
|
14473
14826
|
payload: { running: false, completed: hooksCompleted }
|
|
@@ -14475,7 +14828,7 @@ function startStatusBroadcaster() {
|
|
|
14475
14828
|
});
|
|
14476
14829
|
}
|
|
14477
14830
|
eventService.publish({
|
|
14478
|
-
id:
|
|
14831
|
+
id: randomUUID10(),
|
|
14479
14832
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14480
14833
|
type: "hooks.status",
|
|
14481
14834
|
payload: {
|
|
@@ -14530,20 +14883,20 @@ serve(
|
|
|
14530
14883
|
}
|
|
14531
14884
|
const repos = await gitService.listRepos();
|
|
14532
14885
|
await eventService.publish({
|
|
14533
|
-
id:
|
|
14886
|
+
id: randomUUID10(),
|
|
14534
14887
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14535
14888
|
type: "repo.discovered",
|
|
14536
14889
|
payload: { repos }
|
|
14537
14890
|
});
|
|
14538
14891
|
const repoStatuses = await gitService.listRepos();
|
|
14539
14892
|
await eventService.publish({
|
|
14540
|
-
id:
|
|
14893
|
+
id: randomUUID10(),
|
|
14541
14894
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14542
14895
|
type: "repo.status.changed",
|
|
14543
14896
|
payload: { repos: repoStatuses }
|
|
14544
14897
|
});
|
|
14545
14898
|
await eventService.publish({
|
|
14546
|
-
id:
|
|
14899
|
+
id: randomUUID10(),
|
|
14547
14900
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14548
14901
|
type: "engine.ready",
|
|
14549
14902
|
payload: { version: "v1" }
|