replicas-engine 0.1.664 → 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-L4RE37PJ.js → chunk-3EPGH6IZ.js} +134 -25
- package/dist/src/headless-agent.js +1 -1
- package/dist/src/index.js +546 -192
- 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
|
};
|
|
@@ -6482,15 +6489,9 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6482
6489
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
6483
6490
|
}
|
|
6484
6491
|
async initialize() {
|
|
6485
|
-
if (this.initialSessionId) {
|
|
6486
|
-
this.currentThreadId = this.initialSessionId;
|
|
6487
|
-
return;
|
|
6488
|
-
}
|
|
6489
6492
|
const replayed = await this.historyFile?.load();
|
|
6490
|
-
if (replayed) {
|
|
6491
|
-
|
|
6492
|
-
this.mergeTranscriptSnapshot(replayed.transcript);
|
|
6493
|
-
}
|
|
6493
|
+
if (replayed?.transcript && (!this.initialSessionId || replayed.transcript.threadId === this.initialSessionId)) {
|
|
6494
|
+
this.mergeTranscriptSnapshot(replayed.transcript);
|
|
6494
6495
|
}
|
|
6495
6496
|
this.currentThreadId = this.initialSessionId ?? replayed?.transcript?.threadId ?? null;
|
|
6496
6497
|
}
|
|
@@ -6647,16 +6648,22 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6647
6648
|
itemsView: "full"
|
|
6648
6649
|
}
|
|
6649
6650
|
);
|
|
6650
|
-
const
|
|
6651
|
+
const nativeTranscript = turnsToAspTranscript(
|
|
6651
6652
|
this.currentThreadId,
|
|
6652
6653
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
6653
6654
|
response.data
|
|
6654
6655
|
);
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
return {
|
|
6656
|
+
const mergedTranscript = mergeCodexAspTranscripts(this.codexAspTranscript, nativeTranscript) ?? nativeTranscript;
|
|
6657
|
+
this.codexAspTranscript = mergedTranscript;
|
|
6658
|
+
this.syncTranscriptSequence(mergedTranscript);
|
|
6659
|
+
const turnIds = new Set(response.data.map((turn) => turn.id));
|
|
6660
|
+
return {
|
|
6661
|
+
transcript: {
|
|
6662
|
+
...mergedTranscript,
|
|
6663
|
+
turns: mergedTranscript.turns.filter((turn) => turnIds.has(turn.id))
|
|
6664
|
+
},
|
|
6665
|
+
before: response.nextCursor
|
|
6666
|
+
};
|
|
6660
6667
|
}
|
|
6661
6668
|
getGoal() {
|
|
6662
6669
|
return this.currentGoal;
|
|
@@ -6723,6 +6730,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
6723
6730
|
this.recordCodexHistoryEvent("event_msg", {
|
|
6724
6731
|
type: "user_message",
|
|
6725
6732
|
message: request.message,
|
|
6733
|
+
...request.messageId ? { [USER_MESSAGE_ID_PAYLOAD_KEY]: request.messageId } : {},
|
|
6726
6734
|
...images ? { images } : {},
|
|
6727
6735
|
...extraPayload
|
|
6728
6736
|
});
|
|
@@ -8551,9 +8559,14 @@ ${request.message}` : request.message;
|
|
|
8551
8559
|
};
|
|
8552
8560
|
|
|
8553
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
|
|
8554
8566
|
import { spawn as spawn4 } from "child_process";
|
|
8555
|
-
import {
|
|
8556
|
-
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";
|
|
8557
8570
|
import { Writable } from "stream";
|
|
8558
8571
|
import {
|
|
8559
8572
|
PROTOCOL_VERSION,
|
|
@@ -8561,21 +8574,12 @@ import {
|
|
|
8561
8574
|
methods,
|
|
8562
8575
|
ndJsonStream
|
|
8563
8576
|
} from "@agentclientprotocol/sdk";
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
await new Promise((resolve5, reject) => {
|
|
8568
|
-
const child = spawn4("sudo", ["tee", "-a", "/etc/resolv.conf"], { stdio: ["pipe", "ignore", "pipe"] });
|
|
8569
|
-
let stderr = "";
|
|
8570
|
-
child.stderr.on("data", (chunk) => {
|
|
8571
|
-
stderr += chunk;
|
|
8572
|
-
});
|
|
8573
|
-
child.on("error", reject);
|
|
8574
|
-
child.on("exit", (code) => code === 0 ? resolve5() : reject(new Error(`Failed to normalize /etc/resolv.conf: ${stderr.trim()}`)));
|
|
8575
|
-
child.stdin.end("\n");
|
|
8576
|
-
});
|
|
8577
|
+
function selectOptions(option) {
|
|
8578
|
+
if (option.type !== "select") return [];
|
|
8579
|
+
return option.options.flatMap((entry) => "group" in entry ? entry.options : [entry]);
|
|
8577
8580
|
}
|
|
8578
|
-
var
|
|
8581
|
+
var AcpManager = class extends CodingAgentManager {
|
|
8582
|
+
acp;
|
|
8579
8583
|
historyFile;
|
|
8580
8584
|
historyFilePath;
|
|
8581
8585
|
process = null;
|
|
@@ -8583,42 +8587,90 @@ var FxManager = class extends CodingAgentManager {
|
|
|
8583
8587
|
context = null;
|
|
8584
8588
|
sessionId = null;
|
|
8585
8589
|
activeModel = null;
|
|
8590
|
+
activeThinkingLevel;
|
|
8586
8591
|
planMode = false;
|
|
8592
|
+
replaying = false;
|
|
8587
8593
|
slashCommands = [];
|
|
8594
|
+
configOptions = [];
|
|
8595
|
+
modes = null;
|
|
8596
|
+
capabilities = {};
|
|
8597
|
+
interactiveTools = false;
|
|
8598
|
+
pendingPermissions = /* @__PURE__ */ new Map();
|
|
8599
|
+
terminals = /* @__PURE__ */ new Map();
|
|
8588
8600
|
constructor(options) {
|
|
8589
|
-
super(options);
|
|
8590
|
-
this.
|
|
8601
|
+
super({ ...options, provider: options.acp.provider });
|
|
8602
|
+
this.acp = options.acp;
|
|
8603
|
+
this.historyFilePath = options.historyFilePath;
|
|
8591
8604
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
8592
8605
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
8593
8606
|
}
|
|
8594
8607
|
async initialize() {
|
|
8595
8608
|
await mkdir13(dirname7(this.historyFilePath), { recursive: true });
|
|
8596
|
-
await
|
|
8609
|
+
await this.acp.prepare?.();
|
|
8597
8610
|
}
|
|
8598
8611
|
getHistorySink() {
|
|
8599
8612
|
return this.historyFile;
|
|
8600
8613
|
}
|
|
8601
|
-
async start(model) {
|
|
8602
|
-
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;
|
|
8603
8616
|
this.dispose();
|
|
8604
|
-
const
|
|
8617
|
+
const agentEnv = await this.acp.env?.(model, thinkingLevel);
|
|
8618
|
+
const child = spawn4(this.acp.command, this.acp.args(model), {
|
|
8605
8619
|
cwd: this.workingDirectory,
|
|
8606
|
-
env: { ...process.env,
|
|
8620
|
+
env: { ...process.env, NO_COLOR: "1", ...agentEnv },
|
|
8607
8621
|
stdio: ["pipe", "pipe", "pipe"]
|
|
8608
8622
|
});
|
|
8609
8623
|
this.process = child;
|
|
8610
|
-
child.stderr.on("data", (chunk) => console.error(
|
|
8624
|
+
child.stderr.on("data", (chunk) => console.error(`[${this.acp.provider} ACP]`, chunk.toString().trimEnd()));
|
|
8611
8625
|
child.on("exit", (code, signal) => {
|
|
8612
8626
|
if (this.process !== child) return;
|
|
8613
8627
|
this.process = null;
|
|
8614
8628
|
this.context = null;
|
|
8615
8629
|
this.connection = null;
|
|
8616
8630
|
if (code !== 0 && signal !== "SIGTERM") {
|
|
8617
|
-
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);
|
|
8618
8635
|
}
|
|
8619
8636
|
});
|
|
8620
|
-
const app2 = client({ name: "Replicas" }).onNotification(methods.client.session.update, ({ params }) => this.recordUpdate(params)).onRequest(methods.client.
|
|
8621
|
-
|
|
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];
|
|
8622
8674
|
return option ? { outcome: { outcome: "selected", optionId: option.optionId } } : { outcome: { outcome: "cancelled" } };
|
|
8623
8675
|
});
|
|
8624
8676
|
const stdout = new ReadableStream({
|
|
@@ -8631,26 +8683,34 @@ var FxManager = class extends CodingAgentManager {
|
|
|
8631
8683
|
child.stdout.destroy();
|
|
8632
8684
|
}
|
|
8633
8685
|
});
|
|
8634
|
-
|
|
8635
|
-
Writable.toWeb(child.stdin),
|
|
8636
|
-
stdout
|
|
8637
|
-
);
|
|
8638
|
-
this.connection = app2.connect(stream);
|
|
8686
|
+
this.connection = app2.connect(ndJsonStream(Writable.toWeb(child.stdin), stdout));
|
|
8639
8687
|
this.context = this.connection.agent;
|
|
8640
|
-
await this.context.request(methods.agent.initialize, {
|
|
8688
|
+
const initialized = await this.context.request(methods.agent.initialize, {
|
|
8641
8689
|
protocolVersion: PROTOCOL_VERSION,
|
|
8642
|
-
clientCapabilities: {
|
|
8690
|
+
clientCapabilities: {
|
|
8691
|
+
fs: { readTextFile: true, writeTextFile: true },
|
|
8692
|
+
terminal: true,
|
|
8693
|
+
plan: {}
|
|
8694
|
+
},
|
|
8643
8695
|
clientInfo: { name: "Replicas", version: "1" }
|
|
8644
8696
|
});
|
|
8697
|
+
this.capabilities = initialized.agentCapabilities ?? {};
|
|
8645
8698
|
const additionalDirectories = await getAgentAdditionalDirectories();
|
|
8646
8699
|
const sessionId = this.sessionId ?? this.initialSessionId;
|
|
8647
|
-
if (sessionId) {
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
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
|
+
}
|
|
8654
8714
|
this.sessionId = sessionId;
|
|
8655
8715
|
} else {
|
|
8656
8716
|
const session = await this.context.request(methods.agent.session.new, {
|
|
@@ -8659,21 +8719,84 @@ var FxManager = class extends CodingAgentManager {
|
|
|
8659
8719
|
mcpServers: []
|
|
8660
8720
|
});
|
|
8661
8721
|
this.sessionId = session.sessionId;
|
|
8722
|
+
this.configOptions = session.configOptions ?? [];
|
|
8723
|
+
this.modes = session.modes ?? null;
|
|
8662
8724
|
await this.onSaveSessionId(session.sessionId);
|
|
8663
8725
|
}
|
|
8664
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 };
|
|
8665
8783
|
}
|
|
8666
8784
|
recordUpdate(notification) {
|
|
8667
|
-
if (notification.sessionId !== this.sessionId) return;
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
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);
|
|
8671
8790
|
return parsed ? [parsed] : [];
|
|
8672
8791
|
}));
|
|
8673
|
-
} else if (
|
|
8674
|
-
|
|
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;
|
|
8675
8798
|
this.historyFile.append(this.emitContextUsage({
|
|
8676
|
-
provider:
|
|
8799
|
+
provider: this.acp.provider,
|
|
8677
8800
|
source: "provider_usage",
|
|
8678
8801
|
model: this.activeModel,
|
|
8679
8802
|
totalTokens: used,
|
|
@@ -8683,37 +8806,155 @@ var FxManager = class extends CodingAgentManager {
|
|
|
8683
8806
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8684
8807
|
}));
|
|
8685
8808
|
}
|
|
8686
|
-
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();
|
|
8687
8924
|
}
|
|
8688
8925
|
async processMessageInternal(request) {
|
|
8689
8926
|
try {
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
await this.start(model);
|
|
8927
|
+
const model = request.model ?? this.acp.defaultModel;
|
|
8928
|
+
await this.start(model, request.thinkingLevel);
|
|
8693
8929
|
const context = this.context;
|
|
8694
8930
|
const sessionId = this.sessionId;
|
|
8695
|
-
if (!context || !sessionId) throw new Error(
|
|
8931
|
+
if (!context || !sessionId) throw new Error(`${this.acp.command} ACP failed to initialize.`);
|
|
8696
8932
|
this.recordHistoryEvent("event_msg", { type: "user_message", message: request.message }, this.historyFile);
|
|
8697
8933
|
this.planMode = request.planMode ?? false;
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
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);
|
|
8712
8949
|
} catch (error) {
|
|
8713
|
-
this.recordHistoryEvent("
|
|
8950
|
+
this.recordHistoryEvent("acp-error", {
|
|
8951
|
+
provider: this.acp.provider,
|
|
8952
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8953
|
+
}, this.historyFile);
|
|
8714
8954
|
throw error;
|
|
8715
8955
|
} finally {
|
|
8716
8956
|
this.planMode = false;
|
|
8957
|
+
this.interactiveTools = false;
|
|
8717
8958
|
try {
|
|
8718
8959
|
await this.historyFile.flush();
|
|
8719
8960
|
} finally {
|
|
@@ -8723,8 +8964,17 @@ ${request.message}` : request.message
|
|
|
8723
8964
|
}
|
|
8724
8965
|
async interruptActiveTurn() {
|
|
8725
8966
|
if (!this.context || !this.sessionId) return;
|
|
8967
|
+
this.abortPendingPermissions();
|
|
8726
8968
|
await this.context.notify(methods.agent.session.cancel, { sessionId: this.sessionId });
|
|
8727
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
|
+
}
|
|
8728
8978
|
async getHistory(page = {}) {
|
|
8729
8979
|
await this.historyFile.flush();
|
|
8730
8980
|
return {
|
|
@@ -8738,20 +8988,105 @@ ${request.message}` : request.message
|
|
|
8738
8988
|
return this.slashCommands;
|
|
8739
8989
|
}
|
|
8740
8990
|
dispose() {
|
|
8991
|
+
for (const terminal of this.terminals.values()) {
|
|
8992
|
+
if (terminal.exitStatus === null) terminal.process.kill("SIGTERM");
|
|
8993
|
+
}
|
|
8994
|
+
this.terminals.clear();
|
|
8741
8995
|
this.connection?.close();
|
|
8742
8996
|
this.process?.kill("SIGTERM");
|
|
8743
8997
|
this.connection = null;
|
|
8744
8998
|
this.context = null;
|
|
8745
8999
|
this.process = null;
|
|
8746
9000
|
this.activeModel = null;
|
|
9001
|
+
this.activeThinkingLevel = void 0;
|
|
9002
|
+
this.configOptions = [];
|
|
9003
|
+
this.modes = null;
|
|
9004
|
+
this.capabilities = {};
|
|
8747
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
|
+
});
|
|
8748
9083
|
}
|
|
8749
9084
|
};
|
|
8750
9085
|
|
|
8751
9086
|
// src/managers/opencode-manager.ts
|
|
8752
9087
|
import { existsSync as existsSync7 } from "fs";
|
|
8753
|
-
import { mkdir as mkdir14, readFile as
|
|
8754
|
-
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";
|
|
8755
9090
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8756
9091
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8757
9092
|
import { Agent } from "undici";
|
|
@@ -8798,7 +9133,7 @@ async function getAllowedOpenRouterModels() {
|
|
|
8798
9133
|
|
|
8799
9134
|
// src/managers/opencode-manager.ts
|
|
8800
9135
|
var OPENCODE_SHIM_DIR = dirname8(fileURLToPath2(new URL("../../scripts/opencode", import.meta.url)));
|
|
8801
|
-
var OPENCODE_CONFIG_PATH =
|
|
9136
|
+
var OPENCODE_CONFIG_PATH = join22(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
|
|
8802
9137
|
var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
|
|
8803
9138
|
var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
|
|
8804
9139
|
var OPENCODE_WORKSPACE_PERMISSION = {
|
|
@@ -8835,7 +9170,7 @@ var opencodeAuthSchema = z4.record(z4.string(), z4.object({
|
|
|
8835
9170
|
async function hasOpencodeCredentials(provider) {
|
|
8836
9171
|
if (!existsSync7(OPENCODE_AUTH_PATH)) return false;
|
|
8837
9172
|
try {
|
|
8838
|
-
const auth = opencodeAuthSchema.safeParse(JSON.parse(await
|
|
9173
|
+
const auth = opencodeAuthSchema.safeParse(JSON.parse(await readFile13(OPENCODE_AUTH_PATH, "utf8")));
|
|
8839
9174
|
return auth.success && auth.data[provider]?.type === "api" && Boolean(auth.data[provider]?.key);
|
|
8840
9175
|
} catch {
|
|
8841
9176
|
return false;
|
|
@@ -8940,7 +9275,7 @@ function isOpencodeMcpEntry(value) {
|
|
|
8940
9275
|
async function readProvisionedOpencodeMcpConfig() {
|
|
8941
9276
|
let raw;
|
|
8942
9277
|
try {
|
|
8943
|
-
raw = await
|
|
9278
|
+
raw = await readFile13(OPENCODE_CONFIG_PATH, "utf8");
|
|
8944
9279
|
} catch (error) {
|
|
8945
9280
|
if (isRecord2(error) && error.code === "ENOENT") return void 0;
|
|
8946
9281
|
console.error("[OpencodeManager] Failed to read Opencode config:", error);
|
|
@@ -9048,7 +9383,7 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9048
9383
|
constructor(options) {
|
|
9049
9384
|
super(options);
|
|
9050
9385
|
this.sessionId = options.initialSessionId;
|
|
9051
|
-
this.historyFilePath = options.historyFilePath ??
|
|
9386
|
+
this.historyFilePath = options.historyFilePath ?? join22(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
|
|
9052
9387
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
9053
9388
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
9054
9389
|
}
|
|
@@ -9558,7 +9893,7 @@ var OpencodeManager = class extends CodingAgentManager {
|
|
|
9558
9893
|
|
|
9559
9894
|
// src/managers/pi-manager.ts
|
|
9560
9895
|
import { mkdir as mkdir15 } from "fs/promises";
|
|
9561
|
-
import { dirname as dirname9, join as
|
|
9896
|
+
import { dirname as dirname9, join as join23 } from "path";
|
|
9562
9897
|
import {
|
|
9563
9898
|
AuthStorage,
|
|
9564
9899
|
createAgentSession,
|
|
@@ -9641,7 +9976,7 @@ var PiManager = class extends CodingAgentManager {
|
|
|
9641
9976
|
providerApiKey = null;
|
|
9642
9977
|
constructor(options) {
|
|
9643
9978
|
super(options);
|
|
9644
|
-
this.historyFilePath = options.historyFilePath ??
|
|
9979
|
+
this.historyFilePath = options.historyFilePath ?? join23(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
|
|
9645
9980
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
9646
9981
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
9647
9982
|
}
|
|
@@ -9741,7 +10076,7 @@ var PiManager = class extends CodingAgentManager {
|
|
|
9741
10076
|
const sessionManager = this.initialSessionId ? SessionManager.open(this.initialSessionId, PI_HISTORY_DIR, this.workingDirectory) : SessionManager.create(this.workingDirectory, PI_HISTORY_DIR);
|
|
9742
10077
|
const resourceLoader = new DefaultResourceLoader({
|
|
9743
10078
|
cwd: this.workingDirectory,
|
|
9744
|
-
agentDir:
|
|
10079
|
+
agentDir: join23(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
|
|
9745
10080
|
extensionFactories: [registerCommandProtection(
|
|
9746
10081
|
this.workingDirectory,
|
|
9747
10082
|
this.historyFile,
|
|
@@ -9818,6 +10153,7 @@ function getAvailableRelayProviders(availability) {
|
|
|
9818
10153
|
const cursorAvailable = availability.cursorAvailable ?? false;
|
|
9819
10154
|
const deepseekAvailable = availability.deepseekAvailable ?? false;
|
|
9820
10155
|
const fxAvailable = availability.fxAvailable ?? false;
|
|
10156
|
+
const kimiAvailable = availability.kimiAvailable ?? false;
|
|
9821
10157
|
const opencodeAvailable = availability.opencodeAvailable ?? false;
|
|
9822
10158
|
const piAvailable = availability.piAvailable ?? false;
|
|
9823
10159
|
const providers = ["claude"];
|
|
@@ -9825,6 +10161,7 @@ function getAvailableRelayProviders(availability) {
|
|
|
9825
10161
|
if (cursorAvailable) providers.push("cursor");
|
|
9826
10162
|
if (deepseekAvailable) providers.push("deepseek");
|
|
9827
10163
|
if (fxAvailable) providers.push("fx");
|
|
10164
|
+
if (kimiAvailable) providers.push("kimi");
|
|
9828
10165
|
if (opencodeAvailable) providers.push("opencode");
|
|
9829
10166
|
if (piAvailable) providers.push("pi");
|
|
9830
10167
|
providers.push("relay");
|
|
@@ -9914,7 +10251,7 @@ function extractFinalResponse(history) {
|
|
|
9914
10251
|
const text = getDeepseekAssistantMessageText(payload.event, "text");
|
|
9915
10252
|
if (text) return text;
|
|
9916
10253
|
}
|
|
9917
|
-
if (event.type === "fx-session-update") {
|
|
10254
|
+
if (event.type === "fx-session-update" || event.type === "acp-session-update") {
|
|
9918
10255
|
const text = getFxTextChunk(payload.update, "agent_message_chunk");
|
|
9919
10256
|
if (text) return text;
|
|
9920
10257
|
}
|
|
@@ -9957,11 +10294,12 @@ You will also receive the chatId so you can send follow-up messages or clean up
|
|
|
9957
10294
|
`Cursor: ${AGENT_MODELS.cursor.join(", ")}.`,
|
|
9958
10295
|
`DeepSeek Harness: ${AGENT_MODELS.deepseek.join(", ")}.`,
|
|
9959
10296
|
`fx: ${AGENT_MODELS.fx.join(", ")}.`,
|
|
10297
|
+
`Kimi Code: ${AGENT_MODELS.kimi.join(", ")}.`,
|
|
9960
10298
|
`Opencode: ${AGENT_MODELS.opencode.join(", ")}.`,
|
|
9961
10299
|
`Pi: ${AGENT_MODELS.pi.join(", ")}.`
|
|
9962
10300
|
].join(" ")),
|
|
9963
10301
|
thinking_level: z5.enum(VALID_THINKING_LEVELS).optional().describe(
|
|
9964
|
-
"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."
|
|
9965
10303
|
),
|
|
9966
10304
|
title: z5.string().optional().describe("Optional title for the subagent chat (for identification)."),
|
|
9967
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.")
|
|
@@ -10213,14 +10551,15 @@ function getUsingToolsSection() {
|
|
|
10213
10551
|
];
|
|
10214
10552
|
return [`# Using your tools`, ...prependBullets(items)].join("\n");
|
|
10215
10553
|
}
|
|
10216
|
-
function getDelegationSection(codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, opencodeAvailable, piAvailable) {
|
|
10217
|
-
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(", ");
|
|
10218
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).`;
|
|
10219
10557
|
const claudeModelList = AGENT_MODELS.claude.join(", ");
|
|
10220
10558
|
const extraAgentLines = [
|
|
10221
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,
|
|
10222
10560
|
deepseekAvailable ? `Use provider 'deepseek' for tasks that benefit from the DeepSeek Harness tool loop. Suggested models: ${AGENT_MODELS.deepseek.join(", ")}.` : null,
|
|
10223
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,
|
|
10224
10563
|
opencodeAvailable ? `Use provider 'opencode' for cheaper routine implementation tasks through OpenRouter-backed open source models. Suggested models: ${AGENT_MODELS.opencode.join(", ")}.` : null,
|
|
10225
10564
|
piAvailable ? `Use provider 'pi' for coding tasks through Pi's OpenRouter-backed coding agent. Suggested models: ${AGENT_MODELS.pi.join(", ")}.` : null,
|
|
10226
10565
|
cursorAvailable ? `Use provider 'cursor' for fast iteration on code changes. Suggested models: ${AGENT_MODELS.cursor.join(", ")}.` : null
|
|
@@ -10345,14 +10684,14 @@ function getEnvironmentSection() {
|
|
|
10345
10684
|
].join("\n");
|
|
10346
10685
|
}
|
|
10347
10686
|
function buildRelaySystemPrompt(options) {
|
|
10348
|
-
const { customInstructions, codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, opencodeAvailable, piAvailable } = options ?? {};
|
|
10687
|
+
const { customInstructions, codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, kimiAvailable, opencodeAvailable, piAvailable } = options ?? {};
|
|
10349
10688
|
const sections = [
|
|
10350
10689
|
getIntroSection(),
|
|
10351
10690
|
getSystemSection(),
|
|
10352
10691
|
getDoingTasksSection(),
|
|
10353
10692
|
getActionsSection(),
|
|
10354
10693
|
getUsingToolsSection(),
|
|
10355
|
-
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),
|
|
10356
10695
|
getToneAndStyleSection(),
|
|
10357
10696
|
getOutputEfficiencySection(),
|
|
10358
10697
|
getEnvironmentSection(),
|
|
@@ -10386,9 +10725,10 @@ var RelayManager = class {
|
|
|
10386
10725
|
const cursorAvailable = options.cursorAvailable ?? false;
|
|
10387
10726
|
const deepseekAvailable = options.deepseekAvailable ?? false;
|
|
10388
10727
|
const fxAvailable = options.fxAvailable ?? false;
|
|
10728
|
+
const kimiAvailable = options.kimiAvailable ?? false;
|
|
10389
10729
|
const opencodeAvailable = options.opencodeAvailable ?? false;
|
|
10390
10730
|
const piAvailable = options.piAvailable ?? false;
|
|
10391
|
-
const availability = { codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, opencodeAvailable, piAvailable };
|
|
10731
|
+
const availability = { codexAvailable, cursorAvailable, deepseekAvailable, fxAvailable, kimiAvailable, opencodeAvailable, piAvailable };
|
|
10392
10732
|
const getProviderAvailability = options.getProviderAvailability ?? (() => availability);
|
|
10393
10733
|
this.inner = new ClaudeManager({
|
|
10394
10734
|
...options,
|
|
@@ -10453,16 +10793,16 @@ var RelayManager = class {
|
|
|
10453
10793
|
import {
|
|
10454
10794
|
appendFile as appendFile3,
|
|
10455
10795
|
mkdir as mkdir16,
|
|
10456
|
-
readFile as
|
|
10796
|
+
readFile as readFile14,
|
|
10457
10797
|
readdir as readdir6,
|
|
10458
10798
|
rename as rename2,
|
|
10459
10799
|
unlink as unlink3
|
|
10460
10800
|
} from "fs/promises";
|
|
10461
|
-
import { join as
|
|
10462
|
-
import { randomUUID as
|
|
10801
|
+
import { join as join24 } from "path";
|
|
10802
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
10463
10803
|
|
|
10464
10804
|
// src/analytics/agent/activity/skill-mcp-call-extractor.ts
|
|
10465
|
-
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"]);
|
|
10466
10806
|
function mcpNameFromToolCall(message) {
|
|
10467
10807
|
const parsedName = parseMcpToolName(message.tool);
|
|
10468
10808
|
if (parsedName) return parsedName.server;
|
|
@@ -10509,7 +10849,7 @@ var AgentChatActivityBuffer = class {
|
|
|
10509
10849
|
options.storageName,
|
|
10510
10850
|
...options.legacyStorageNames ?? []
|
|
10511
10851
|
];
|
|
10512
|
-
this.liveFile =
|
|
10852
|
+
this.liveFile = join24(ENGINE_DIR2, `${options.storageName}.jsonl`);
|
|
10513
10853
|
this.segmentFilePatterns = this.storageNames.map(
|
|
10514
10854
|
(storageName) => new RegExp(`^${storageName}\\.(\\d+)\\.jsonl$`)
|
|
10515
10855
|
);
|
|
@@ -10568,8 +10908,8 @@ var AgentChatActivityBuffer = class {
|
|
|
10568
10908
|
await Promise.allSettled([...this.pendingAppends]);
|
|
10569
10909
|
for (const storageName of this.storageNames) {
|
|
10570
10910
|
await rename2(
|
|
10571
|
-
|
|
10572
|
-
|
|
10911
|
+
join24(ENGINE_DIR2, `${storageName}.jsonl`),
|
|
10912
|
+
join24(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
|
|
10573
10913
|
).catch(() => {
|
|
10574
10914
|
});
|
|
10575
10915
|
}
|
|
@@ -10580,7 +10920,7 @@ var AgentChatActivityBuffer = class {
|
|
|
10580
10920
|
if (!this.segmentFilePatterns.some((pattern) => pattern.test(entry)))
|
|
10581
10921
|
continue;
|
|
10582
10922
|
try {
|
|
10583
|
-
await this.uploadSegment(
|
|
10923
|
+
await this.uploadSegment(join24(ENGINE_DIR2, entry));
|
|
10584
10924
|
flushed++;
|
|
10585
10925
|
} catch (error) {
|
|
10586
10926
|
failed++;
|
|
@@ -10612,13 +10952,13 @@ var AgentChatActivityBuffer = class {
|
|
|
10612
10952
|
) && entry.endsWith(UPLOADED_SUFFIX)
|
|
10613
10953
|
).sort();
|
|
10614
10954
|
for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
|
|
10615
|
-
await unlink3(
|
|
10955
|
+
await unlink3(join24(ENGINE_DIR2, entry)).catch(() => {
|
|
10616
10956
|
});
|
|
10617
10957
|
}
|
|
10618
10958
|
return { flushed, failed };
|
|
10619
10959
|
}
|
|
10620
10960
|
async uploadSegment(filePath) {
|
|
10621
|
-
const records = (await
|
|
10961
|
+
const records = (await readFile14(filePath, "utf-8")).split("\n").flatMap((line) => {
|
|
10622
10962
|
try {
|
|
10623
10963
|
const parsed = JSON.parse(line);
|
|
10624
10964
|
return this.options.validate(parsed) ? [parsed] : [];
|
|
@@ -10680,7 +11020,7 @@ var AgentChatTurnActivityTracker = class {
|
|
|
10680
11020
|
const credential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[provider];
|
|
10681
11021
|
this.pendingMessages.delete(messageId);
|
|
10682
11022
|
this.activeTurns.set(chatId, {
|
|
10683
|
-
turnId:
|
|
11023
|
+
turnId: randomUUID7(),
|
|
10684
11024
|
startedAtMs: Date.now(),
|
|
10685
11025
|
provider,
|
|
10686
11026
|
model: attributes.model ?? getDefaultAgentModel(provider),
|
|
@@ -10843,19 +11183,19 @@ var KeepAliveService = class _KeepAliveService {
|
|
|
10843
11183
|
var keepAliveService = new KeepAliveService();
|
|
10844
11184
|
|
|
10845
11185
|
// src/services/canvas-service.ts
|
|
10846
|
-
import { readdir as readdir7, readFile as
|
|
11186
|
+
import { readdir as readdir7, readFile as readFile15, stat as stat3 } from "fs/promises";
|
|
10847
11187
|
import { homedir as homedir14 } from "os";
|
|
10848
|
-
import { join as
|
|
11188
|
+
import { join as join25 } from "path";
|
|
10849
11189
|
var GLOBAL_CANVAS_DIRECTORIES = [
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
|
|
11190
|
+
join25(homedir14(), ".claude", "plans"),
|
|
11191
|
+
join25(process.env.XDG_DATA_HOME ?? join25(homedir14(), ".local", "share"), "opencode", "plans"),
|
|
11192
|
+
join25(homedir14(), ".replicas", "canvas")
|
|
10853
11193
|
];
|
|
10854
11194
|
async function canvasDirectories() {
|
|
10855
11195
|
const repositories = await gitService.listRepositories().catch(() => []);
|
|
10856
11196
|
return [
|
|
10857
11197
|
...GLOBAL_CANVAS_DIRECTORIES,
|
|
10858
|
-
...repositories.map((repository) =>
|
|
11198
|
+
...repositories.map((repository) => join25(repository.path, ".opencode", "plans"))
|
|
10859
11199
|
];
|
|
10860
11200
|
}
|
|
10861
11201
|
var CanvasService = class {
|
|
@@ -10879,7 +11219,7 @@ var CanvasService = class {
|
|
|
10879
11219
|
for (const entry of entries) {
|
|
10880
11220
|
if (entry.name.startsWith(".")) continue;
|
|
10881
11221
|
const filename = current.relativePath ? `${current.relativePath}/${entry.name}` : entry.name;
|
|
10882
|
-
const filePath =
|
|
11222
|
+
const filePath = join25(current.directory, entry.name);
|
|
10883
11223
|
if (entry.isDirectory()) {
|
|
10884
11224
|
pending.push({ directory: filePath, relativePath: filename });
|
|
10885
11225
|
continue;
|
|
@@ -10904,7 +11244,7 @@ var CanvasService = class {
|
|
|
10904
11244
|
if (!safe) return null;
|
|
10905
11245
|
const { kind, mimeType } = classifyCanvasFilename(safe);
|
|
10906
11246
|
for (const directory of await this.directories()) {
|
|
10907
|
-
const filePath =
|
|
11247
|
+
const filePath = join25(directory, safe);
|
|
10908
11248
|
let sizeBytes = 0;
|
|
10909
11249
|
let updatedAt = "";
|
|
10910
11250
|
try {
|
|
@@ -10925,7 +11265,7 @@ var CanvasService = class {
|
|
|
10925
11265
|
};
|
|
10926
11266
|
}
|
|
10927
11267
|
try {
|
|
10928
|
-
const bytes = await
|
|
11268
|
+
const bytes = await readFile15(filePath);
|
|
10929
11269
|
return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
|
|
10930
11270
|
} catch {
|
|
10931
11271
|
continue;
|
|
@@ -11075,14 +11415,14 @@ async function reconcileCanvasItems(filenames) {
|
|
|
11075
11415
|
// src/services/upload-chat-transcripts.ts
|
|
11076
11416
|
import { createReadStream } from "fs";
|
|
11077
11417
|
import { createHash as createHash2 } from "crypto";
|
|
11078
|
-
import { readFile as
|
|
11079
|
-
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";
|
|
11080
11420
|
|
|
11081
11421
|
// src/services/chat/chat-senders.ts
|
|
11082
|
-
import { join as
|
|
11083
|
-
var CHAT_SENDERS_DIR =
|
|
11422
|
+
import { join as join26 } from "path";
|
|
11423
|
+
var CHAT_SENDERS_DIR = join26(ENGINE_DIR2, "chat-senders");
|
|
11084
11424
|
function chatMessageSendersFilePath(chatId) {
|
|
11085
|
-
return
|
|
11425
|
+
return join26(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
|
|
11086
11426
|
}
|
|
11087
11427
|
function parseChatMessageSendersJsonl(content) {
|
|
11088
11428
|
return content.split("\n").flatMap((line) => {
|
|
@@ -11098,9 +11438,9 @@ function parseChatMessageSendersJsonl(content) {
|
|
|
11098
11438
|
|
|
11099
11439
|
// src/services/upload-chat-transcripts.ts
|
|
11100
11440
|
var HISTORY_DIRS = [
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11441
|
+
join27(ENGINE_DIR2, "claude-histories"),
|
|
11442
|
+
join27(ENGINE_DIR2, "relay-histories"),
|
|
11443
|
+
join27(ENGINE_DIR2, "codex-histories")
|
|
11104
11444
|
];
|
|
11105
11445
|
async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), capture) {
|
|
11106
11446
|
let flushed = 0;
|
|
@@ -11118,7 +11458,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
11118
11458
|
if (!entry.endsWith(".jsonl")) continue;
|
|
11119
11459
|
const chatId = basename2(entry, ".jsonl");
|
|
11120
11460
|
tasks.push(
|
|
11121
|
-
uploadChatTranscript(chatId,
|
|
11461
|
+
uploadChatTranscript(chatId, join27(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
|
|
11122
11462
|
flushed++;
|
|
11123
11463
|
if (artifact && capture) revisions.push(artifact);
|
|
11124
11464
|
}).catch((err) => {
|
|
@@ -11134,7 +11474,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
|
|
|
11134
11474
|
async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
11135
11475
|
const { size } = await stat4(filePath);
|
|
11136
11476
|
if (size === 0) return null;
|
|
11137
|
-
const historyPages = createChatTranscriptPages(await
|
|
11477
|
+
const historyPages = createChatTranscriptPages(await readFile16(filePath));
|
|
11138
11478
|
const metadata = chat ? {
|
|
11139
11479
|
provider: chat.provider,
|
|
11140
11480
|
credential: ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[chat.provider],
|
|
@@ -11153,7 +11493,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
|
|
|
11153
11493
|
}
|
|
11154
11494
|
try {
|
|
11155
11495
|
metadata.senders = parseChatMessageSendersJsonl(
|
|
11156
|
-
await
|
|
11496
|
+
await readFile16(chatMessageSendersFilePath(chatId), "utf-8")
|
|
11157
11497
|
);
|
|
11158
11498
|
} catch (error) {
|
|
11159
11499
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
@@ -11236,7 +11576,7 @@ async function flushRepoState() {
|
|
|
11236
11576
|
// src/services/upload-engine-logs.ts
|
|
11237
11577
|
import { createReadStream as createReadStream2 } from "fs";
|
|
11238
11578
|
import { readdir as readdir9, stat as stat5 } from "fs/promises";
|
|
11239
|
-
import { join as
|
|
11579
|
+
import { join as join28 } from "path";
|
|
11240
11580
|
var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
|
|
11241
11581
|
var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
|
|
11242
11582
|
var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
|
|
@@ -11265,7 +11605,7 @@ async function flushAllEngineLogs() {
|
|
|
11265
11605
|
const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
|
|
11266
11606
|
try {
|
|
11267
11607
|
const sessionId = filename.slice(0, -".log".length);
|
|
11268
|
-
const filePath =
|
|
11608
|
+
const filePath = join28(LOG_DIR, filename);
|
|
11269
11609
|
const fileStat = await runBeforeDeadline(() => stat5(filePath), deadline);
|
|
11270
11610
|
if (!fileStat.isFile()) {
|
|
11271
11611
|
skipped++;
|
|
@@ -11361,7 +11701,7 @@ async function uploadEngineLog(input, timeoutMs) {
|
|
|
11361
11701
|
}
|
|
11362
11702
|
|
|
11363
11703
|
// src/services/chat/chat-service.ts
|
|
11364
|
-
var CODEX_AUTH_PATH2 =
|
|
11704
|
+
var CODEX_AUTH_PATH2 = join29(homedir15(), ".codex", "auth.json");
|
|
11365
11705
|
var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
|
|
11366
11706
|
function isCodexAvailable() {
|
|
11367
11707
|
return existsSync8(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
|
|
@@ -11375,6 +11715,9 @@ function isDeepseekAvailable() {
|
|
|
11375
11715
|
function isFxAvailable() {
|
|
11376
11716
|
return Boolean(ENGINE_ENV.AI_GATEWAY_API_KEY);
|
|
11377
11717
|
}
|
|
11718
|
+
function isKimiAvailable() {
|
|
11719
|
+
return Boolean(ENGINE_ENV.AI_GATEWAY_API_KEY);
|
|
11720
|
+
}
|
|
11378
11721
|
function isPiAvailable() {
|
|
11379
11722
|
return existsSync8(PI_AUTH_PATH);
|
|
11380
11723
|
}
|
|
@@ -11567,7 +11910,7 @@ var ChatService = class {
|
|
|
11567
11910
|
throw new ChatNotFoundError(parentChatId);
|
|
11568
11911
|
}
|
|
11569
11912
|
const persisted = {
|
|
11570
|
-
id: request.id ??
|
|
11913
|
+
id: request.id ?? randomUUID8(),
|
|
11571
11914
|
provider: request.provider,
|
|
11572
11915
|
title,
|
|
11573
11916
|
createdAt: now,
|
|
@@ -11658,7 +12001,7 @@ var ChatService = class {
|
|
|
11658
12001
|
}
|
|
11659
12002
|
}
|
|
11660
12003
|
async readSenders(chatId) {
|
|
11661
|
-
return
|
|
12004
|
+
return readFile17(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
|
|
11662
12005
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
|
|
11663
12006
|
console.error("[ChatService] Failed to read sender records:", error);
|
|
11664
12007
|
return [];
|
|
@@ -11884,7 +12227,7 @@ var ChatService = class {
|
|
|
11884
12227
|
return descendants;
|
|
11885
12228
|
}
|
|
11886
12229
|
async deleteHistoryFile(persisted) {
|
|
11887
|
-
const historyPath =
|
|
12230
|
+
const historyPath = join29(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
|
|
11888
12231
|
await Promise.all([
|
|
11889
12232
|
historyPath,
|
|
11890
12233
|
`${historyPath}.pages.jsonl`,
|
|
@@ -12007,7 +12350,7 @@ var ChatService = class {
|
|
|
12007
12350
|
if (persisted.provider === "claude") {
|
|
12008
12351
|
provider = new ClaudeManager({
|
|
12009
12352
|
workingDirectory: this.workingDirectory,
|
|
12010
|
-
historyFilePath:
|
|
12353
|
+
historyFilePath: join29(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12011
12354
|
initialSessionId: persisted.providerSessionId,
|
|
12012
12355
|
onSaveSessionId: saveSession,
|
|
12013
12356
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12021,11 +12364,12 @@ var ChatService = class {
|
|
|
12021
12364
|
piAvailable: isPiAvailable(),
|
|
12022
12365
|
cursorAvailable: isCursorAvailable(),
|
|
12023
12366
|
deepseekAvailable: isDeepseekAvailable(),
|
|
12024
|
-
fxAvailable: isFxAvailable()
|
|
12367
|
+
fxAvailable: isFxAvailable(),
|
|
12368
|
+
kimiAvailable: isKimiAvailable()
|
|
12025
12369
|
});
|
|
12026
12370
|
provider = new RelayManager({
|
|
12027
12371
|
workingDirectory: this.workingDirectory,
|
|
12028
|
-
historyFilePath:
|
|
12372
|
+
historyFilePath: join29(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12029
12373
|
initialSessionId: persisted.providerSessionId,
|
|
12030
12374
|
onSaveSessionId: saveSession,
|
|
12031
12375
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12038,7 +12382,7 @@ var ChatService = class {
|
|
|
12038
12382
|
} else if (persisted.provider === "cursor") {
|
|
12039
12383
|
provider = new CursorManager({
|
|
12040
12384
|
workingDirectory: this.workingDirectory,
|
|
12041
|
-
historyFilePath:
|
|
12385
|
+
historyFilePath: join29(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12042
12386
|
initialSessionId: persisted.providerSessionId,
|
|
12043
12387
|
onSaveSessionId: saveSession,
|
|
12044
12388
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12048,7 +12392,7 @@ var ChatService = class {
|
|
|
12048
12392
|
} else if (persisted.provider === "deepseek") {
|
|
12049
12393
|
provider = new DeepseekManager({
|
|
12050
12394
|
workingDirectory: this.workingDirectory,
|
|
12051
|
-
historyFilePath:
|
|
12395
|
+
historyFilePath: join29(DEEPSEEK_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12052
12396
|
initialSessionId: persisted.providerSessionId,
|
|
12053
12397
|
onSaveSessionId: saveSession,
|
|
12054
12398
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12058,7 +12402,17 @@ var ChatService = class {
|
|
|
12058
12402
|
} else if (persisted.provider === "fx") {
|
|
12059
12403
|
provider = new FxManager({
|
|
12060
12404
|
workingDirectory: this.workingDirectory,
|
|
12061
|
-
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`),
|
|
12062
12416
|
initialSessionId: persisted.providerSessionId,
|
|
12063
12417
|
onSaveSessionId: saveSession,
|
|
12064
12418
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12068,7 +12422,7 @@ var ChatService = class {
|
|
|
12068
12422
|
} else if (persisted.provider === "opencode") {
|
|
12069
12423
|
provider = new OpencodeManager({
|
|
12070
12424
|
workingDirectory: this.workingDirectory,
|
|
12071
|
-
historyFilePath:
|
|
12425
|
+
historyFilePath: join29(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12072
12426
|
initialSessionId: persisted.providerSessionId,
|
|
12073
12427
|
onSaveSessionId: saveSession,
|
|
12074
12428
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12078,7 +12432,7 @@ var ChatService = class {
|
|
|
12078
12432
|
} else if (persisted.provider === "pi") {
|
|
12079
12433
|
provider = new PiManager({
|
|
12080
12434
|
workingDirectory: this.workingDirectory,
|
|
12081
|
-
historyFilePath:
|
|
12435
|
+
historyFilePath: join29(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12082
12436
|
initialSessionId: persisted.providerSessionId,
|
|
12083
12437
|
onSaveSessionId: saveSession,
|
|
12084
12438
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12088,7 +12442,7 @@ var ChatService = class {
|
|
|
12088
12442
|
} else {
|
|
12089
12443
|
provider = new CodexAspManager({
|
|
12090
12444
|
workingDirectory: this.workingDirectory,
|
|
12091
|
-
historyFilePath:
|
|
12445
|
+
historyFilePath: join29(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
|
|
12092
12446
|
initialSessionId: persisted.providerSessionId,
|
|
12093
12447
|
onSaveSessionId: saveSession,
|
|
12094
12448
|
onTurnComplete: onProviderTurnComplete,
|
|
@@ -12253,7 +12607,7 @@ var ChatService = class {
|
|
|
12253
12607
|
});
|
|
12254
12608
|
uploadChatTranscript(
|
|
12255
12609
|
chatId,
|
|
12256
|
-
|
|
12610
|
+
join29(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
|
|
12257
12611
|
this.toSummary(chat)
|
|
12258
12612
|
).catch((err) => {
|
|
12259
12613
|
console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
|
|
@@ -12269,7 +12623,7 @@ var ChatService = class {
|
|
|
12269
12623
|
}
|
|
12270
12624
|
async loadChats() {
|
|
12271
12625
|
try {
|
|
12272
|
-
const content = await
|
|
12626
|
+
const content = await readFile17(CHATS_FILE, "utf-8");
|
|
12273
12627
|
return parsePersistedChatsContent(content);
|
|
12274
12628
|
} catch (error) {
|
|
12275
12629
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -12284,7 +12638,7 @@ var ChatService = class {
|
|
|
12284
12638
|
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
12285
12639
|
}
|
|
12286
12640
|
try {
|
|
12287
|
-
const backupContent = await
|
|
12641
|
+
const backupContent = await readFile17(CHATS_BACKUP_FILE, "utf-8");
|
|
12288
12642
|
return parsePersistedChatsContent(backupContent);
|
|
12289
12643
|
} catch (backupError) {
|
|
12290
12644
|
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
@@ -12323,7 +12677,7 @@ var ChatService = class {
|
|
|
12323
12677
|
}
|
|
12324
12678
|
async publish(input) {
|
|
12325
12679
|
const event = {
|
|
12326
|
-
id:
|
|
12680
|
+
id: randomUUID8(),
|
|
12327
12681
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12328
12682
|
...input
|
|
12329
12683
|
};
|
|
@@ -12390,8 +12744,8 @@ var ChatService = class {
|
|
|
12390
12744
|
|
|
12391
12745
|
// src/services/repo-file-service.ts
|
|
12392
12746
|
import { execFile } from "child_process";
|
|
12393
|
-
import { readFile as
|
|
12394
|
-
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";
|
|
12395
12749
|
var CACHE_TTL_MS = 3e4;
|
|
12396
12750
|
var SEARCH_TIMEOUT_MS = 15e3;
|
|
12397
12751
|
var MAX_CONTENT_BYTES = 256 * 1024;
|
|
@@ -12543,7 +12897,7 @@ var RepoFileService = class {
|
|
|
12543
12897
|
const repo = repos.find((r) => r.name === repoName);
|
|
12544
12898
|
if (!repo) return null;
|
|
12545
12899
|
try {
|
|
12546
|
-
const fullPath = await realpath(resolve2(
|
|
12900
|
+
const fullPath = await realpath(resolve2(join30(repo.path, filePath)));
|
|
12547
12901
|
const repoRoot = await realpath(repo.path);
|
|
12548
12902
|
const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
|
|
12549
12903
|
if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
|
|
@@ -12574,7 +12928,7 @@ var RepoFileService = class {
|
|
|
12574
12928
|
sizeBytes,
|
|
12575
12929
|
binary: true,
|
|
12576
12930
|
tooLarge: false,
|
|
12577
|
-
base64: (await
|
|
12931
|
+
base64: (await readFile18(fullPath)).toString("base64"),
|
|
12578
12932
|
mimeType
|
|
12579
12933
|
};
|
|
12580
12934
|
}
|
|
@@ -12599,7 +12953,7 @@ var RepoFileService = class {
|
|
|
12599
12953
|
tooLarge: true
|
|
12600
12954
|
};
|
|
12601
12955
|
}
|
|
12602
|
-
const content = await
|
|
12956
|
+
const content = await readFile18(fullPath, "utf-8");
|
|
12603
12957
|
return {
|
|
12604
12958
|
repoName,
|
|
12605
12959
|
path: filePath,
|
|
@@ -12677,21 +13031,21 @@ var RepoFileService = class {
|
|
|
12677
13031
|
// src/v1-routes.ts
|
|
12678
13032
|
import { Hono } from "hono";
|
|
12679
13033
|
import { z as z6 } from "zod";
|
|
12680
|
-
import { readdir as readdir11, stat as stat7, readFile as
|
|
12681
|
-
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";
|
|
12682
13036
|
|
|
12683
13037
|
// src/services/warm-hooks-service.ts
|
|
12684
|
-
import { spawn as
|
|
12685
|
-
import { readFile as
|
|
13038
|
+
import { spawn as spawn6 } from "child_process";
|
|
13039
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
12686
13040
|
import { existsSync as existsSync9 } from "fs";
|
|
12687
|
-
import { join as
|
|
13041
|
+
import { join as join32 } from "path";
|
|
12688
13042
|
|
|
12689
13043
|
// src/services/warm-hook-logs-service.ts
|
|
12690
|
-
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";
|
|
12691
13045
|
import { homedir as homedir16 } from "os";
|
|
12692
|
-
import { join as
|
|
12693
|
-
var LOGS_DIR2 =
|
|
12694
|
-
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");
|
|
12695
13049
|
var GLOBAL_FILENAME = "global.json";
|
|
12696
13050
|
function withPreview2(stored) {
|
|
12697
13051
|
const preview = buildHookOutputPreview(stored.output);
|
|
@@ -12708,7 +13062,7 @@ var WarmHookLogsService = class {
|
|
|
12708
13062
|
hookName: "organization",
|
|
12709
13063
|
...entry
|
|
12710
13064
|
};
|
|
12711
|
-
await
|
|
13065
|
+
await writeFile7(join31(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
12712
13066
|
`, "utf-8");
|
|
12713
13067
|
}
|
|
12714
13068
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -12718,7 +13072,7 @@ var WarmHookLogsService = class {
|
|
|
12718
13072
|
hookName: "environment",
|
|
12719
13073
|
...entry
|
|
12720
13074
|
};
|
|
12721
|
-
await
|
|
13075
|
+
await writeFile7(join31(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
|
|
12722
13076
|
`, "utf-8");
|
|
12723
13077
|
}
|
|
12724
13078
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -12728,7 +13082,7 @@ var WarmHookLogsService = class {
|
|
|
12728
13082
|
hookName: repoName,
|
|
12729
13083
|
...entry
|
|
12730
13084
|
};
|
|
12731
|
-
await
|
|
13085
|
+
await writeFile7(join31(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
12732
13086
|
`, "utf-8");
|
|
12733
13087
|
}
|
|
12734
13088
|
async getAllLogs() {
|
|
@@ -12747,7 +13101,7 @@ var WarmHookLogsService = class {
|
|
|
12747
13101
|
continue;
|
|
12748
13102
|
}
|
|
12749
13103
|
try {
|
|
12750
|
-
const raw = await
|
|
13104
|
+
const raw = await readFile19(join31(LOGS_DIR2, file), "utf-8");
|
|
12751
13105
|
const stored = JSON.parse(raw);
|
|
12752
13106
|
logs.push(withPreview2(stored));
|
|
12753
13107
|
} catch {
|
|
@@ -12776,7 +13130,7 @@ var WarmHookLogsService = class {
|
|
|
12776
13130
|
}
|
|
12777
13131
|
async getCurrentRunLog() {
|
|
12778
13132
|
try {
|
|
12779
|
-
return await
|
|
13133
|
+
return await readFile19(CURRENT_RUN_LOG, "utf-8");
|
|
12780
13134
|
} catch (err) {
|
|
12781
13135
|
if (err.code === "ENOENT") return null;
|
|
12782
13136
|
throw err;
|
|
@@ -12785,7 +13139,7 @@ var WarmHookLogsService = class {
|
|
|
12785
13139
|
async getFullOutput(hookType, hookName) {
|
|
12786
13140
|
const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
|
|
12787
13141
|
try {
|
|
12788
|
-
const raw = await
|
|
13142
|
+
const raw = await readFile19(join31(LOGS_DIR2, filename), "utf-8");
|
|
12789
13143
|
const stored = JSON.parse(raw);
|
|
12790
13144
|
if (stored.hookType !== hookType || stored.hookName !== hookName) {
|
|
12791
13145
|
return null;
|
|
@@ -12804,12 +13158,12 @@ var warmHookLogsService = new WarmHookLogsService();
|
|
|
12804
13158
|
// src/services/warm-hooks-service.ts
|
|
12805
13159
|
async function readRepoWarmHook(repoPath) {
|
|
12806
13160
|
for (const filename of REPLICAS_CONFIG_FILENAMES) {
|
|
12807
|
-
const configPath =
|
|
13161
|
+
const configPath = join32(repoPath, filename);
|
|
12808
13162
|
if (!existsSync9(configPath)) {
|
|
12809
13163
|
continue;
|
|
12810
13164
|
}
|
|
12811
13165
|
try {
|
|
12812
|
-
const raw = await
|
|
13166
|
+
const raw = await readFile20(configPath, "utf-8");
|
|
12813
13167
|
const config = parseReplicasConfigString(raw, filename);
|
|
12814
13168
|
if (!config.warmHook) {
|
|
12815
13169
|
return null;
|
|
@@ -12856,7 +13210,7 @@ async function executeHookScriptStreaming(params) {
|
|
|
12856
13210
|
params.onChunk(`$ ${params.label}
|
|
12857
13211
|
`);
|
|
12858
13212
|
return new Promise((resolve5) => {
|
|
12859
|
-
const proc =
|
|
13213
|
+
const proc = spawn6("bash", ["-lc", params.content], {
|
|
12860
13214
|
cwd: params.cwd,
|
|
12861
13215
|
env: process.env,
|
|
12862
13216
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -13065,9 +13419,9 @@ ${combinedScript}` : combinedScript;
|
|
|
13065
13419
|
}
|
|
13066
13420
|
|
|
13067
13421
|
// src/services/terminal-service.ts
|
|
13068
|
-
import { randomUUID as
|
|
13422
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
13069
13423
|
import { existsSync as existsSync10 } from "fs";
|
|
13070
|
-
import { spawn as
|
|
13424
|
+
import { spawn as spawn7 } from "node-pty";
|
|
13071
13425
|
var MAX_REPLAY_CHARS = 1024 * 1024;
|
|
13072
13426
|
var MAX_TERMINAL_SESSIONS = 8;
|
|
13073
13427
|
var MAX_PENDING_INPUT = 64;
|
|
@@ -13084,9 +13438,9 @@ var TerminalService = class {
|
|
|
13084
13438
|
code: "limit"
|
|
13085
13439
|
});
|
|
13086
13440
|
}
|
|
13087
|
-
const id =
|
|
13441
|
+
const id = randomUUID9();
|
|
13088
13442
|
const shell = process.env.SHELL && existsSync10(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
|
|
13089
|
-
const pty =
|
|
13443
|
+
const pty = spawn7(shell, ["-l"], {
|
|
13090
13444
|
name: "xterm-256color",
|
|
13091
13445
|
cols,
|
|
13092
13446
|
rows,
|
|
@@ -14088,7 +14442,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
14088
14442
|
const logFiles = files.filter((f) => f.endsWith(".log"));
|
|
14089
14443
|
const sessions = await Promise.all(
|
|
14090
14444
|
logFiles.map(async (filename) => {
|
|
14091
|
-
const filePath =
|
|
14445
|
+
const filePath = join33(LOG_DIR, filename);
|
|
14092
14446
|
const fileStat = await stat7(filePath);
|
|
14093
14447
|
const sessionId = filename.replace(/\.log$/, "");
|
|
14094
14448
|
return {
|
|
@@ -14123,7 +14477,7 @@ data: ${JSON.stringify("Terminal session not found")}
|
|
|
14123
14477
|
}
|
|
14124
14478
|
let content;
|
|
14125
14479
|
try {
|
|
14126
|
-
content = await
|
|
14480
|
+
content = await readFile21(filePath, "utf-8");
|
|
14127
14481
|
} catch {
|
|
14128
14482
|
return c.json(jsonError("Log session not found"), 404);
|
|
14129
14483
|
}
|
|
@@ -14399,7 +14753,7 @@ function startStatusBroadcaster() {
|
|
|
14399
14753
|
if (serialized !== previousRepoStatus) {
|
|
14400
14754
|
previousRepoStatus = serialized;
|
|
14401
14755
|
eventService.publish({
|
|
14402
|
-
id:
|
|
14756
|
+
id: randomUUID10(),
|
|
14403
14757
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14404
14758
|
type: "repo.status.changed",
|
|
14405
14759
|
payload: { repos }
|
|
@@ -14420,7 +14774,7 @@ function startStatusBroadcaster() {
|
|
|
14420
14774
|
if (engineStatusJson !== previousEngineStatus) {
|
|
14421
14775
|
previousEngineStatus = engineStatusJson;
|
|
14422
14776
|
eventService.publish({
|
|
14423
|
-
id:
|
|
14777
|
+
id: randomUUID10(),
|
|
14424
14778
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14425
14779
|
type: "engine.status.changed",
|
|
14426
14780
|
payload: { status: engineStatus }
|
|
@@ -14439,7 +14793,7 @@ function startStatusBroadcaster() {
|
|
|
14439
14793
|
previousHookStatus = hookSnapshot;
|
|
14440
14794
|
if (!lastHooksRunning && hooksRunning) {
|
|
14441
14795
|
eventService.publish({
|
|
14442
|
-
id:
|
|
14796
|
+
id: randomUUID10(),
|
|
14443
14797
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14444
14798
|
type: "hooks.started",
|
|
14445
14799
|
payload: { running: true, completed: false }
|
|
@@ -14448,7 +14802,7 @@ function startStatusBroadcaster() {
|
|
|
14448
14802
|
}
|
|
14449
14803
|
if (hooksRunning) {
|
|
14450
14804
|
eventService.publish({
|
|
14451
|
-
id:
|
|
14805
|
+
id: randomUUID10(),
|
|
14452
14806
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14453
14807
|
type: "hooks.progress",
|
|
14454
14808
|
payload: { running: true, completed: false }
|
|
@@ -14457,7 +14811,7 @@ function startStatusBroadcaster() {
|
|
|
14457
14811
|
}
|
|
14458
14812
|
if (lastHooksRunning && !hooksRunning && hooksCompleted && !hooksFailed) {
|
|
14459
14813
|
eventService.publish({
|
|
14460
|
-
id:
|
|
14814
|
+
id: randomUUID10(),
|
|
14461
14815
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14462
14816
|
type: "hooks.completed",
|
|
14463
14817
|
payload: { running: false, completed: true }
|
|
@@ -14466,7 +14820,7 @@ function startStatusBroadcaster() {
|
|
|
14466
14820
|
}
|
|
14467
14821
|
if (lastHooksRunning && !hooksRunning && hooksFailed) {
|
|
14468
14822
|
eventService.publish({
|
|
14469
|
-
id:
|
|
14823
|
+
id: randomUUID10(),
|
|
14470
14824
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14471
14825
|
type: "hooks.failed",
|
|
14472
14826
|
payload: { running: false, completed: hooksCompleted }
|
|
@@ -14474,7 +14828,7 @@ function startStatusBroadcaster() {
|
|
|
14474
14828
|
});
|
|
14475
14829
|
}
|
|
14476
14830
|
eventService.publish({
|
|
14477
|
-
id:
|
|
14831
|
+
id: randomUUID10(),
|
|
14478
14832
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14479
14833
|
type: "hooks.status",
|
|
14480
14834
|
payload: {
|
|
@@ -14529,20 +14883,20 @@ serve(
|
|
|
14529
14883
|
}
|
|
14530
14884
|
const repos = await gitService.listRepos();
|
|
14531
14885
|
await eventService.publish({
|
|
14532
|
-
id:
|
|
14886
|
+
id: randomUUID10(),
|
|
14533
14887
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14534
14888
|
type: "repo.discovered",
|
|
14535
14889
|
payload: { repos }
|
|
14536
14890
|
});
|
|
14537
14891
|
const repoStatuses = await gitService.listRepos();
|
|
14538
14892
|
await eventService.publish({
|
|
14539
|
-
id:
|
|
14893
|
+
id: randomUUID10(),
|
|
14540
14894
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14541
14895
|
type: "repo.status.changed",
|
|
14542
14896
|
payload: { repos: repoStatuses }
|
|
14543
14897
|
});
|
|
14544
14898
|
await eventService.publish({
|
|
14545
|
-
id:
|
|
14899
|
+
id: randomUUID10(),
|
|
14546
14900
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14547
14901
|
type: "engine.ready",
|
|
14548
14902
|
payload: { version: "v1" }
|