@locusai/telegram 0.10.2 → 0.10.5
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/bin/telegram.js +219 -60
- package/package.json +2 -2
package/bin/telegram.js
CHANGED
|
@@ -38432,10 +38432,12 @@ class ClaudeRunner {
|
|
|
38432
38432
|
currentToolName;
|
|
38433
38433
|
activeTools = new Map;
|
|
38434
38434
|
activeProcess = null;
|
|
38435
|
-
|
|
38435
|
+
timeoutMs;
|
|
38436
|
+
constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CLAUDE], log2, timeoutMs) {
|
|
38436
38437
|
this.model = model;
|
|
38437
38438
|
this.log = log2;
|
|
38438
38439
|
this.projectPath = resolve(projectPath);
|
|
38440
|
+
this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
38439
38441
|
}
|
|
38440
38442
|
setEventEmitter(emitter) {
|
|
38441
38443
|
this.eventEmitter = emitter;
|
|
@@ -38451,11 +38453,14 @@ class ClaudeRunner {
|
|
|
38451
38453
|
let lastError = null;
|
|
38452
38454
|
for (let attempt = 1;attempt <= maxRetries; attempt++) {
|
|
38453
38455
|
try {
|
|
38454
|
-
return await this.executeRun(prompt);
|
|
38456
|
+
return await this.withTimeout(this.executeRun(prompt));
|
|
38455
38457
|
} catch (error48) {
|
|
38456
38458
|
const err = error48;
|
|
38457
38459
|
lastError = err;
|
|
38458
38460
|
const isLastAttempt = attempt === maxRetries;
|
|
38461
|
+
if (err.message.includes("timed out")) {
|
|
38462
|
+
throw err;
|
|
38463
|
+
}
|
|
38459
38464
|
if (!isLastAttempt) {
|
|
38460
38465
|
const delay = Math.pow(2, attempt) * 1000;
|
|
38461
38466
|
console.warn(`Claude CLI attempt ${attempt} failed: ${err.message}. Retrying in ${delay}ms...`);
|
|
@@ -38465,6 +38470,23 @@ class ClaudeRunner {
|
|
|
38465
38470
|
}
|
|
38466
38471
|
throw lastError || new Error("Claude CLI failed after multiple attempts");
|
|
38467
38472
|
}
|
|
38473
|
+
withTimeout(promise2) {
|
|
38474
|
+
if (this.timeoutMs <= 0)
|
|
38475
|
+
return promise2;
|
|
38476
|
+
return new Promise((resolve2, reject) => {
|
|
38477
|
+
const timer = setTimeout(() => {
|
|
38478
|
+
this.abort();
|
|
38479
|
+
reject(new Error(`Claude CLI execution timed out after ${Math.round(this.timeoutMs / 60000)} minutes`));
|
|
38480
|
+
}, this.timeoutMs);
|
|
38481
|
+
promise2.then((value) => {
|
|
38482
|
+
clearTimeout(timer);
|
|
38483
|
+
resolve2(value);
|
|
38484
|
+
}, (err) => {
|
|
38485
|
+
clearTimeout(timer);
|
|
38486
|
+
reject(err);
|
|
38487
|
+
});
|
|
38488
|
+
});
|
|
38489
|
+
}
|
|
38468
38490
|
async* runStream(prompt) {
|
|
38469
38491
|
const args = [
|
|
38470
38492
|
"--dangerously-skip-permissions",
|
|
@@ -38829,18 +38851,22 @@ ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
|
|
|
38829
38851
|
return new Error(message);
|
|
38830
38852
|
}
|
|
38831
38853
|
}
|
|
38832
|
-
var SANDBOX_SETTINGS;
|
|
38854
|
+
var SANDBOX_SETTINGS, DEFAULT_TIMEOUT_MS;
|
|
38833
38855
|
var init_claude_runner = __esm(() => {
|
|
38834
38856
|
init_config();
|
|
38835
38857
|
init_colors();
|
|
38836
38858
|
init_resolve_bin();
|
|
38837
38859
|
SANDBOX_SETTINGS = JSON.stringify({
|
|
38860
|
+
permissions: {
|
|
38861
|
+
deny: ["Read(../**)", "Edit(../**)"]
|
|
38862
|
+
},
|
|
38838
38863
|
sandbox: {
|
|
38839
38864
|
enabled: true,
|
|
38840
38865
|
autoAllow: true,
|
|
38841
38866
|
allowUnsandboxedCommands: false
|
|
38842
38867
|
}
|
|
38843
38868
|
});
|
|
38869
|
+
DEFAULT_TIMEOUT_MS = 60 * 60 * 1000;
|
|
38844
38870
|
});
|
|
38845
38871
|
|
|
38846
38872
|
// ../sdk/src/ai/codex-runner.ts
|
|
@@ -38855,10 +38881,17 @@ class CodexRunner {
|
|
|
38855
38881
|
model;
|
|
38856
38882
|
log;
|
|
38857
38883
|
activeProcess = null;
|
|
38858
|
-
|
|
38884
|
+
eventEmitter;
|
|
38885
|
+
currentToolName;
|
|
38886
|
+
timeoutMs;
|
|
38887
|
+
constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CODEX], log2, timeoutMs) {
|
|
38859
38888
|
this.projectPath = projectPath;
|
|
38860
38889
|
this.model = model;
|
|
38861
38890
|
this.log = log2;
|
|
38891
|
+
this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
38892
|
+
}
|
|
38893
|
+
setEventEmitter(emitter) {
|
|
38894
|
+
this.eventEmitter = emitter;
|
|
38862
38895
|
}
|
|
38863
38896
|
abort() {
|
|
38864
38897
|
if (this.activeProcess && !this.activeProcess.killed) {
|
|
@@ -38871,9 +38904,12 @@ class CodexRunner {
|
|
|
38871
38904
|
let lastError = null;
|
|
38872
38905
|
for (let attempt = 1;attempt <= maxRetries; attempt++) {
|
|
38873
38906
|
try {
|
|
38874
|
-
return await this.executeRun(prompt);
|
|
38907
|
+
return await this.withTimeout(this.executeRun(prompt));
|
|
38875
38908
|
} catch (error48) {
|
|
38876
38909
|
lastError = error48;
|
|
38910
|
+
if (lastError.message.includes("timed out")) {
|
|
38911
|
+
throw lastError;
|
|
38912
|
+
}
|
|
38877
38913
|
if (attempt < maxRetries) {
|
|
38878
38914
|
const delay = Math.pow(2, attempt) * 1000;
|
|
38879
38915
|
console.warn(`Codex CLI attempt ${attempt} failed: ${lastError.message}. Retrying in ${delay}ms...`);
|
|
@@ -38883,9 +38919,31 @@ class CodexRunner {
|
|
|
38883
38919
|
}
|
|
38884
38920
|
throw lastError || new Error("Codex CLI failed after multiple attempts");
|
|
38885
38921
|
}
|
|
38922
|
+
withTimeout(promise2) {
|
|
38923
|
+
if (this.timeoutMs <= 0)
|
|
38924
|
+
return promise2;
|
|
38925
|
+
return new Promise((resolve2, reject) => {
|
|
38926
|
+
const timer = setTimeout(() => {
|
|
38927
|
+
this.abort();
|
|
38928
|
+
reject(new Error(`Codex CLI execution timed out after ${Math.round(this.timeoutMs / 60000)} minutes`));
|
|
38929
|
+
}, this.timeoutMs);
|
|
38930
|
+
promise2.then((value) => {
|
|
38931
|
+
clearTimeout(timer);
|
|
38932
|
+
resolve2(value);
|
|
38933
|
+
}, (err) => {
|
|
38934
|
+
clearTimeout(timer);
|
|
38935
|
+
reject(err);
|
|
38936
|
+
});
|
|
38937
|
+
});
|
|
38938
|
+
}
|
|
38886
38939
|
async* runStream(prompt) {
|
|
38887
38940
|
const outputPath = join5(tmpdir(), `locus-codex-${randomUUID()}.txt`);
|
|
38888
38941
|
const args = this.buildArgs(outputPath);
|
|
38942
|
+
this.eventEmitter?.emitSessionStarted({
|
|
38943
|
+
model: this.model,
|
|
38944
|
+
provider: "codex"
|
|
38945
|
+
});
|
|
38946
|
+
this.eventEmitter?.emitPromptSubmitted(prompt, prompt.length > 500);
|
|
38889
38947
|
const codex = spawn3("codex", args, {
|
|
38890
38948
|
cwd: this.projectPath,
|
|
38891
38949
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -38898,7 +38956,21 @@ class CodexRunner {
|
|
|
38898
38956
|
let processEnded = false;
|
|
38899
38957
|
let errorMessage = "";
|
|
38900
38958
|
let finalOutput = "";
|
|
38959
|
+
let finalContent = "";
|
|
38960
|
+
let isThinking = false;
|
|
38901
38961
|
const enqueueChunk = (chunk) => {
|
|
38962
|
+
this.emitEventForChunk(chunk, isThinking);
|
|
38963
|
+
if (chunk.type === "thinking") {
|
|
38964
|
+
isThinking = true;
|
|
38965
|
+
} else if (chunk.type === "text_delta" || chunk.type === "tool_use") {
|
|
38966
|
+
if (isThinking) {
|
|
38967
|
+
this.eventEmitter?.emitThinkingStoped();
|
|
38968
|
+
isThinking = false;
|
|
38969
|
+
}
|
|
38970
|
+
}
|
|
38971
|
+
if (chunk.type === "text_delta") {
|
|
38972
|
+
finalContent += chunk.content;
|
|
38973
|
+
}
|
|
38902
38974
|
if (resolveChunk) {
|
|
38903
38975
|
const resolve2 = resolveChunk;
|
|
38904
38976
|
resolveChunk = null;
|
|
@@ -38939,16 +39011,21 @@ class CodexRunner {
|
|
|
38939
39011
|
codex.stderr.on("data", processOutput);
|
|
38940
39012
|
codex.on("error", (err) => {
|
|
38941
39013
|
errorMessage = `Failed to start Codex CLI: ${err.message}. Ensure 'codex' is installed and available in PATH.`;
|
|
39014
|
+
this.eventEmitter?.emitErrorOccurred(errorMessage, "SPAWN_ERROR");
|
|
38942
39015
|
signalEnd();
|
|
38943
39016
|
});
|
|
38944
39017
|
codex.on("close", (code) => {
|
|
38945
39018
|
this.activeProcess = null;
|
|
38946
|
-
this.cleanupTempFile(outputPath);
|
|
38947
39019
|
if (code === 0) {
|
|
38948
39020
|
const result = this.readOutput(outputPath, finalOutput);
|
|
39021
|
+
this.cleanupTempFile(outputPath);
|
|
38949
39022
|
enqueueChunk({ type: "result", content: result });
|
|
38950
|
-
} else
|
|
38951
|
-
|
|
39023
|
+
} else {
|
|
39024
|
+
this.cleanupTempFile(outputPath);
|
|
39025
|
+
if (!errorMessage) {
|
|
39026
|
+
errorMessage = this.createErrorFromOutput(code, finalOutput).message;
|
|
39027
|
+
this.eventEmitter?.emitErrorOccurred(errorMessage, `EXIT_${code}`);
|
|
39028
|
+
}
|
|
38952
39029
|
}
|
|
38953
39030
|
signalEnd();
|
|
38954
39031
|
});
|
|
@@ -38962,6 +39039,12 @@ class CodexRunner {
|
|
|
38962
39039
|
} else if (processEnded) {
|
|
38963
39040
|
if (errorMessage) {
|
|
38964
39041
|
yield { type: "error", error: errorMessage };
|
|
39042
|
+
this.eventEmitter?.emitSessionEnded(false);
|
|
39043
|
+
} else {
|
|
39044
|
+
if (finalContent) {
|
|
39045
|
+
this.eventEmitter?.emitResponseCompleted(finalContent);
|
|
39046
|
+
}
|
|
39047
|
+
this.eventEmitter?.emitSessionEnded(true);
|
|
38965
39048
|
}
|
|
38966
39049
|
break;
|
|
38967
39050
|
} else {
|
|
@@ -38971,6 +39054,12 @@ class CodexRunner {
|
|
|
38971
39054
|
if (chunk === null) {
|
|
38972
39055
|
if (errorMessage) {
|
|
38973
39056
|
yield { type: "error", error: errorMessage };
|
|
39057
|
+
this.eventEmitter?.emitSessionEnded(false);
|
|
39058
|
+
} else {
|
|
39059
|
+
if (finalContent) {
|
|
39060
|
+
this.eventEmitter?.emitResponseCompleted(finalContent);
|
|
39061
|
+
}
|
|
39062
|
+
this.eventEmitter?.emitSessionEnded(true);
|
|
38974
39063
|
}
|
|
38975
39064
|
break;
|
|
38976
39065
|
}
|
|
@@ -38978,6 +39067,36 @@ class CodexRunner {
|
|
|
38978
39067
|
}
|
|
38979
39068
|
}
|
|
38980
39069
|
}
|
|
39070
|
+
emitEventForChunk(chunk, isThinking) {
|
|
39071
|
+
if (!this.eventEmitter)
|
|
39072
|
+
return;
|
|
39073
|
+
switch (chunk.type) {
|
|
39074
|
+
case "text_delta":
|
|
39075
|
+
this.eventEmitter.emitTextDelta(chunk.content);
|
|
39076
|
+
break;
|
|
39077
|
+
case "tool_use":
|
|
39078
|
+
if (this.currentToolName) {
|
|
39079
|
+
this.eventEmitter.emitToolCompleted(this.currentToolName);
|
|
39080
|
+
}
|
|
39081
|
+
this.currentToolName = chunk.tool;
|
|
39082
|
+
this.eventEmitter.emitToolStarted(chunk.tool);
|
|
39083
|
+
break;
|
|
39084
|
+
case "thinking":
|
|
39085
|
+
if (!isThinking) {
|
|
39086
|
+
this.eventEmitter.emitThinkingStarted(chunk.content);
|
|
39087
|
+
}
|
|
39088
|
+
break;
|
|
39089
|
+
case "result":
|
|
39090
|
+
if (this.currentToolName) {
|
|
39091
|
+
this.eventEmitter.emitToolCompleted(this.currentToolName);
|
|
39092
|
+
this.currentToolName = undefined;
|
|
39093
|
+
}
|
|
39094
|
+
break;
|
|
39095
|
+
case "error":
|
|
39096
|
+
this.eventEmitter.emitErrorOccurred(chunk.error);
|
|
39097
|
+
break;
|
|
39098
|
+
}
|
|
39099
|
+
}
|
|
38981
39100
|
executeRun(prompt) {
|
|
38982
39101
|
return new Promise((resolve2, reject) => {
|
|
38983
39102
|
const outputPath = join5(tmpdir(), `locus-codex-${randomUUID()}.txt`);
|
|
@@ -39007,10 +39126,12 @@ class CodexRunner {
|
|
|
39007
39126
|
});
|
|
39008
39127
|
codex.on("close", (code) => {
|
|
39009
39128
|
this.activeProcess = null;
|
|
39010
|
-
this.cleanupTempFile(outputPath);
|
|
39011
39129
|
if (code === 0) {
|
|
39012
|
-
|
|
39130
|
+
const result = this.readOutput(outputPath, output);
|
|
39131
|
+
this.cleanupTempFile(outputPath);
|
|
39132
|
+
resolve2(result);
|
|
39013
39133
|
} else {
|
|
39134
|
+
this.cleanupTempFile(outputPath);
|
|
39014
39135
|
reject(this.createErrorFromOutput(code, errorOutput));
|
|
39015
39136
|
}
|
|
39016
39137
|
});
|
|
@@ -39020,12 +39141,18 @@ class CodexRunner {
|
|
|
39020
39141
|
}
|
|
39021
39142
|
buildArgs(outputPath) {
|
|
39022
39143
|
const args = [
|
|
39144
|
+
"--ask-for-approval",
|
|
39145
|
+
"never",
|
|
39023
39146
|
"exec",
|
|
39024
39147
|
"--sandbox",
|
|
39025
39148
|
"workspace-write",
|
|
39026
39149
|
"--skip-git-repo-check",
|
|
39027
39150
|
"--output-last-message",
|
|
39028
|
-
outputPath
|
|
39151
|
+
outputPath,
|
|
39152
|
+
"-c",
|
|
39153
|
+
"sandbox_workspace_write.network_access=true",
|
|
39154
|
+
"-c",
|
|
39155
|
+
'sandbox.excludedCommands=["git", "gh"]'
|
|
39029
39156
|
];
|
|
39030
39157
|
if (this.model) {
|
|
39031
39158
|
args.push("--model", this.model);
|
|
@@ -39076,9 +39203,11 @@ class CodexRunner {
|
|
|
39076
39203
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
39077
39204
|
}
|
|
39078
39205
|
}
|
|
39206
|
+
var DEFAULT_TIMEOUT_MS2;
|
|
39079
39207
|
var init_codex_runner = __esm(() => {
|
|
39080
39208
|
init_config();
|
|
39081
39209
|
init_resolve_bin();
|
|
39210
|
+
DEFAULT_TIMEOUT_MS2 = 60 * 60 * 1000;
|
|
39082
39211
|
});
|
|
39083
39212
|
|
|
39084
39213
|
// ../sdk/src/ai/factory.ts
|
|
@@ -39087,9 +39216,9 @@ function createAiRunner(provider, config2) {
|
|
|
39087
39216
|
const model = config2.model ?? DEFAULT_MODEL[resolvedProvider];
|
|
39088
39217
|
switch (resolvedProvider) {
|
|
39089
39218
|
case PROVIDER.CODEX:
|
|
39090
|
-
return new CodexRunner(config2.projectPath, model, config2.log);
|
|
39219
|
+
return new CodexRunner(config2.projectPath, model, config2.log, config2.timeoutMs);
|
|
39091
39220
|
default:
|
|
39092
|
-
return new ClaudeRunner(config2.projectPath, model, config2.log);
|
|
39221
|
+
return new ClaudeRunner(config2.projectPath, model, config2.log, config2.timeoutMs);
|
|
39093
39222
|
}
|
|
39094
39223
|
}
|
|
39095
39224
|
var init_factory = __esm(() => {
|
|
@@ -39516,8 +39645,9 @@ class WorktreeManager {
|
|
|
39516
39645
|
this.ensureDirectory(this.rootPath, "Worktree root");
|
|
39517
39646
|
addWorktree();
|
|
39518
39647
|
}
|
|
39519
|
-
this.
|
|
39520
|
-
|
|
39648
|
+
const baseCommitHash = this.git("rev-parse HEAD", worktreePath).trim();
|
|
39649
|
+
this.log(`Worktree created at ${worktreePath} (base: ${baseCommitHash.slice(0, 8)})`, "success");
|
|
39650
|
+
return { worktreePath, branch, baseBranch, baseCommitHash };
|
|
39521
39651
|
}
|
|
39522
39652
|
list() {
|
|
39523
39653
|
const output = this.git("worktree list --porcelain", this.projectPath);
|
|
@@ -39622,27 +39752,54 @@ class WorktreeManager {
|
|
|
39622
39752
|
try {
|
|
39623
39753
|
const count = this.git(`rev-list --count "${baseBranch}..HEAD"`, worktreePath).trim();
|
|
39624
39754
|
return Number.parseInt(count, 10) > 0;
|
|
39755
|
+
} catch (err) {
|
|
39756
|
+
this.log(`Could not compare HEAD against base branch "${baseBranch}": ${err instanceof Error ? err.message : String(err)}`, "warn");
|
|
39757
|
+
return false;
|
|
39758
|
+
}
|
|
39759
|
+
}
|
|
39760
|
+
hasCommitsAheadOfHash(worktreePath, baseHash) {
|
|
39761
|
+
try {
|
|
39762
|
+
const headHash = this.git("rev-parse HEAD", worktreePath).trim();
|
|
39763
|
+
return headHash !== baseHash;
|
|
39625
39764
|
} catch {
|
|
39626
39765
|
return false;
|
|
39627
39766
|
}
|
|
39628
39767
|
}
|
|
39629
|
-
commitChanges(worktreePath, message, baseBranch) {
|
|
39768
|
+
commitChanges(worktreePath, message, baseBranch, baseCommitHash) {
|
|
39630
39769
|
const hasUncommittedChanges = this.hasChanges(worktreePath);
|
|
39770
|
+
if (hasUncommittedChanges) {
|
|
39771
|
+
const statusOutput = this.git("status --porcelain", worktreePath).trim();
|
|
39772
|
+
this.log(`Detected uncommitted changes:
|
|
39773
|
+
${statusOutput.split(`
|
|
39774
|
+
`).slice(0, 10).join(`
|
|
39775
|
+
`)}${statusOutput.split(`
|
|
39776
|
+
`).length > 10 ? `
|
|
39777
|
+
... and ${statusOutput.split(`
|
|
39778
|
+
`).length - 10} more` : ""}`, "info");
|
|
39779
|
+
}
|
|
39631
39780
|
if (!hasUncommittedChanges) {
|
|
39632
39781
|
if (baseBranch && this.hasCommitsAhead(worktreePath, baseBranch)) {
|
|
39633
39782
|
const hash3 = this.git("rev-parse HEAD", worktreePath).trim();
|
|
39634
39783
|
this.log(`Agent already committed changes (${hash3.slice(0, 8)}); skipping additional commit`, "info");
|
|
39635
39784
|
return hash3;
|
|
39636
39785
|
}
|
|
39637
|
-
this.
|
|
39786
|
+
if (baseCommitHash && this.hasCommitsAheadOfHash(worktreePath, baseCommitHash)) {
|
|
39787
|
+
const hash3 = this.git("rev-parse HEAD", worktreePath).trim();
|
|
39788
|
+
this.log(`Agent already committed changes (${hash3.slice(0, 8)}, detected via base commit hash); skipping additional commit`, "info");
|
|
39789
|
+
return hash3;
|
|
39790
|
+
}
|
|
39791
|
+
const branch = this.getBranch(worktreePath);
|
|
39792
|
+
this.log(`No changes detected in worktree (branch: ${branch}, baseBranch: ${baseBranch ?? "none"}, baseCommitHash: ${baseCommitHash?.slice(0, 8) ?? "none"})`, "warn");
|
|
39638
39793
|
return null;
|
|
39639
39794
|
}
|
|
39640
39795
|
this.git("add -A", worktreePath);
|
|
39641
39796
|
const staged = this.git("diff --cached --name-only", worktreePath).trim();
|
|
39642
39797
|
if (!staged) {
|
|
39643
|
-
this.log("
|
|
39798
|
+
this.log("All changes were ignored by .gitignore — nothing to commit", "warn");
|
|
39644
39799
|
return null;
|
|
39645
39800
|
}
|
|
39801
|
+
this.log(`Staging ${staged.split(`
|
|
39802
|
+
`).length} file(s) for commit`, "info");
|
|
39646
39803
|
this.gitExec(["commit", "-m", message], worktreePath);
|
|
39647
39804
|
const hash2 = this.git("rev-parse HEAD", worktreePath).trim();
|
|
39648
39805
|
this.log(`Committed: ${hash2.slice(0, 8)}`, "success");
|
|
@@ -40060,15 +40217,27 @@ class TaskExecutor {
|
|
|
40060
40217
|
const basePrompt = await this.promptBuilder.build(task2);
|
|
40061
40218
|
try {
|
|
40062
40219
|
this.deps.log("Starting Execution...", "info");
|
|
40063
|
-
await this.deps.aiRunner.run(basePrompt);
|
|
40064
|
-
|
|
40065
|
-
|
|
40066
|
-
summary: "Task completed by the agent"
|
|
40067
|
-
};
|
|
40220
|
+
const output = await this.deps.aiRunner.run(basePrompt);
|
|
40221
|
+
const summary = this.extractSummary(output);
|
|
40222
|
+
return { success: true, summary };
|
|
40068
40223
|
} catch (error48) {
|
|
40069
40224
|
return { success: false, summary: `Error: ${error48}` };
|
|
40070
40225
|
}
|
|
40071
40226
|
}
|
|
40227
|
+
extractSummary(output) {
|
|
40228
|
+
if (!output || !output.trim()) {
|
|
40229
|
+
return "Task completed by the agent";
|
|
40230
|
+
}
|
|
40231
|
+
const paragraphs = output.split(/\n\n+/).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
40232
|
+
if (paragraphs.length === 0) {
|
|
40233
|
+
return "Task completed by the agent";
|
|
40234
|
+
}
|
|
40235
|
+
const last = paragraphs[paragraphs.length - 1];
|
|
40236
|
+
if (last.length > 500) {
|
|
40237
|
+
return `${last.slice(0, 497)}...`;
|
|
40238
|
+
}
|
|
40239
|
+
return last;
|
|
40240
|
+
}
|
|
40072
40241
|
}
|
|
40073
40242
|
var init_task_executor = __esm(() => {
|
|
40074
40243
|
init_prompt_builder();
|
|
@@ -40086,7 +40255,7 @@ class GitWorkflow {
|
|
|
40086
40255
|
this.log = log2;
|
|
40087
40256
|
this.ghUsername = ghUsername;
|
|
40088
40257
|
const projectPath = config2.projectPath || process.cwd();
|
|
40089
|
-
this.worktreeManager = config2.useWorktrees ? new WorktreeManager(projectPath, { cleanupPolicy: "auto" }) : null;
|
|
40258
|
+
this.worktreeManager = config2.useWorktrees ? new WorktreeManager(projectPath, { cleanupPolicy: "auto" }, log2) : null;
|
|
40090
40259
|
this.prService = config2.autoPush ? new PrService(projectPath, log2) : null;
|
|
40091
40260
|
}
|
|
40092
40261
|
createTaskWorktree(task2, defaultExecutor) {
|
|
@@ -40094,6 +40263,7 @@ class GitWorkflow {
|
|
|
40094
40263
|
return {
|
|
40095
40264
|
worktreePath: null,
|
|
40096
40265
|
baseBranch: null,
|
|
40266
|
+
baseCommitHash: null,
|
|
40097
40267
|
executor: defaultExecutor
|
|
40098
40268
|
};
|
|
40099
40269
|
}
|
|
@@ -40119,10 +40289,11 @@ class GitWorkflow {
|
|
|
40119
40289
|
return {
|
|
40120
40290
|
worktreePath: result.worktreePath,
|
|
40121
40291
|
baseBranch: result.baseBranch,
|
|
40292
|
+
baseCommitHash: result.baseCommitHash,
|
|
40122
40293
|
executor: taskExecutor
|
|
40123
40294
|
};
|
|
40124
40295
|
}
|
|
40125
|
-
commitAndPush(worktreePath, task2, baseBranch) {
|
|
40296
|
+
commitAndPush(worktreePath, task2, baseBranch, baseCommitHash) {
|
|
40126
40297
|
if (!this.worktreeManager) {
|
|
40127
40298
|
return { branch: null, pushed: false, pushFailed: false };
|
|
40128
40299
|
}
|
|
@@ -40139,7 +40310,7 @@ class GitWorkflow {
|
|
|
40139
40310
|
|
|
40140
40311
|
${trailers.join(`
|
|
40141
40312
|
`)}`;
|
|
40142
|
-
const hash2 = this.worktreeManager.commitChanges(worktreePath, commitMessage, baseBranch);
|
|
40313
|
+
const hash2 = this.worktreeManager.commitChanges(worktreePath, commitMessage, baseBranch, baseCommitHash);
|
|
40143
40314
|
if (!hash2) {
|
|
40144
40315
|
this.log("No changes to commit for this task", "info");
|
|
40145
40316
|
return {
|
|
@@ -40179,7 +40350,12 @@ ${trailers.join(`
|
|
|
40179
40350
|
} catch (err) {
|
|
40180
40351
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
40181
40352
|
this.log(`Git commit failed: ${errorMessage}`, "error");
|
|
40182
|
-
return {
|
|
40353
|
+
return {
|
|
40354
|
+
branch: null,
|
|
40355
|
+
pushed: false,
|
|
40356
|
+
pushFailed: true,
|
|
40357
|
+
pushError: `Git commit/push failed: ${errorMessage}`
|
|
40358
|
+
};
|
|
40183
40359
|
}
|
|
40184
40360
|
}
|
|
40185
40361
|
createPullRequest(task2, branch, summary, baseBranch) {
|
|
@@ -40505,7 +40681,7 @@ class AgentWorker {
|
|
|
40505
40681
|
}
|
|
40506
40682
|
async executeTask(task2) {
|
|
40507
40683
|
const fullTask = await this.client.tasks.getById(task2.id, this.config.workspaceId);
|
|
40508
|
-
const { worktreePath, baseBranch, executor } = this.gitWorkflow.createTaskWorktree(fullTask, this.taskExecutor);
|
|
40684
|
+
const { worktreePath, baseBranch, baseCommitHash, executor } = this.gitWorkflow.createTaskWorktree(fullTask, this.taskExecutor);
|
|
40509
40685
|
this.currentWorktreePath = worktreePath;
|
|
40510
40686
|
let branchPushed = false;
|
|
40511
40687
|
let keepBranch = false;
|
|
@@ -40517,7 +40693,7 @@ class AgentWorker {
|
|
|
40517
40693
|
let prError = null;
|
|
40518
40694
|
let noChanges = false;
|
|
40519
40695
|
if (result.success && worktreePath) {
|
|
40520
|
-
const commitResult = this.gitWorkflow.commitAndPush(worktreePath, fullTask, baseBranch ?? undefined);
|
|
40696
|
+
const commitResult = this.gitWorkflow.commitAndPush(worktreePath, fullTask, baseBranch ?? undefined, baseCommitHash ?? undefined);
|
|
40521
40697
|
taskBranch = commitResult.branch;
|
|
40522
40698
|
branchPushed = commitResult.pushed;
|
|
40523
40699
|
keepBranch = taskBranch !== null;
|
|
@@ -41639,25 +41815,8 @@ function isStatusLine(line) {
|
|
|
41639
41815
|
return STATUS_PATTERNS.some((pattern) => pattern.test(clean));
|
|
41640
41816
|
}
|
|
41641
41817
|
async function runCommand(ctx, executor, config) {
|
|
41642
|
-
const
|
|
41643
|
-
const input = normalizeInput(text.replace(/^\/run\s*/, "").trim());
|
|
41644
|
-
let parsedAgentCount;
|
|
41645
|
-
const agentsMatch = input.match(/(?:--agents|-agents|-a)\s+(\d+)/);
|
|
41646
|
-
if (agentsMatch) {
|
|
41647
|
-
parsedAgentCount = Number.parseInt(agentsMatch[1], 10);
|
|
41648
|
-
if (parsedAgentCount < 1 || parsedAgentCount > 5) {
|
|
41649
|
-
await ctx.reply(formatError("Agent count must be between 1 and 5."), {
|
|
41650
|
-
parse_mode: "HTML"
|
|
41651
|
-
});
|
|
41652
|
-
return;
|
|
41653
|
-
}
|
|
41654
|
-
}
|
|
41655
|
-
const agentCount = parsedAgentCount ?? config.agentCount ?? 1;
|
|
41818
|
+
const agentCount = config.agentCount ?? 1;
|
|
41656
41819
|
console.log(`[run] Starting agents (count: ${agentCount})`);
|
|
41657
|
-
if (!config.apiKey) {
|
|
41658
|
-
await ctx.reply(formatError("API key is not configured. Run: locus config setup --api-key <key>"), { parse_mode: "HTML" });
|
|
41659
|
-
return;
|
|
41660
|
-
}
|
|
41661
41820
|
if (activeRunKill) {
|
|
41662
41821
|
await ctx.reply(formatInfo("Agents are already running. Use /stop to stop them first."), { parse_mode: "HTML" });
|
|
41663
41822
|
return;
|
|
@@ -41667,9 +41826,6 @@ async function runCommand(ctx, executor, config) {
|
|
|
41667
41826
|
parse_mode: "HTML"
|
|
41668
41827
|
});
|
|
41669
41828
|
const baseArgs = ["run"];
|
|
41670
|
-
if (agentCount > 1) {
|
|
41671
|
-
baseArgs.push("--agents", String(agentCount));
|
|
41672
|
-
}
|
|
41673
41829
|
const args = executor.buildArgs(baseArgs, { needsApiKey: true });
|
|
41674
41830
|
let lineBuffer = "";
|
|
41675
41831
|
const pendingStatusLines = [];
|
|
@@ -41677,10 +41833,10 @@ async function runCommand(ctx, executor, config) {
|
|
|
41677
41833
|
const sendInterval = setInterval(async () => {
|
|
41678
41834
|
if (pendingStatusLines.length > 0) {
|
|
41679
41835
|
const batch = pendingStatusLines.splice(0);
|
|
41680
|
-
const
|
|
41836
|
+
const text = batch.map((l) => escapeHtml(l)).join(`
|
|
41681
41837
|
`);
|
|
41682
41838
|
try {
|
|
41683
|
-
await ctx.reply(`<pre>${
|
|
41839
|
+
await ctx.reply(`<pre>${text}</pre>`, { parse_mode: "HTML" });
|
|
41684
41840
|
} catch {}
|
|
41685
41841
|
}
|
|
41686
41842
|
}, SEND_INTERVAL_MS);
|
|
@@ -41705,10 +41861,10 @@ async function runCommand(ctx, executor, config) {
|
|
|
41705
41861
|
}
|
|
41706
41862
|
if (pendingStatusLines.length > 0) {
|
|
41707
41863
|
const batch = pendingStatusLines.splice(0);
|
|
41708
|
-
const
|
|
41864
|
+
const text = batch.map((l) => escapeHtml(l)).join(`
|
|
41709
41865
|
`);
|
|
41710
41866
|
try {
|
|
41711
|
-
await ctx.reply(`<pre>${
|
|
41867
|
+
await ctx.reply(`<pre>${text}</pre>`, { parse_mode: "HTML" });
|
|
41712
41868
|
} catch {}
|
|
41713
41869
|
}
|
|
41714
41870
|
if (result.exitCode === 0) {
|
|
@@ -43452,6 +43608,9 @@ class TierMergeService {
|
|
|
43452
43608
|
}
|
|
43453
43609
|
}
|
|
43454
43610
|
findTierTaskBranches(tier) {
|
|
43611
|
+
const tierTaskIds = this.tierTaskIds.get(tier);
|
|
43612
|
+
if (!tierTaskIds || tierTaskIds.length === 0)
|
|
43613
|
+
return [];
|
|
43455
43614
|
try {
|
|
43456
43615
|
const output = execSync2('git branch -r --list "origin/agent/*" --format="%(refname:short)"', { cwd: this.projectPath, encoding: "utf-8" }).trim();
|
|
43457
43616
|
if (!output)
|
|
@@ -43459,13 +43618,13 @@ class TierMergeService {
|
|
|
43459
43618
|
const remoteBranches = output.split(`
|
|
43460
43619
|
`).map((b) => b.replace("origin/", ""));
|
|
43461
43620
|
return remoteBranches.filter((branch) => {
|
|
43462
|
-
const
|
|
43463
|
-
if (!
|
|
43621
|
+
const branchSuffix = branch.replace(/^agent\//, "");
|
|
43622
|
+
if (!branchSuffix)
|
|
43464
43623
|
return false;
|
|
43465
|
-
|
|
43466
|
-
return this.tierTaskIds.get(tier)?.some((id) => id.startsWith(taskIdPrefix) || taskIdPrefix.startsWith(id.slice(0, 8))) ?? false;
|
|
43624
|
+
return tierTaskIds.some((id) => branchSuffix.startsWith(`${id}-`) || branchSuffix === id || branchSuffix.startsWith(id));
|
|
43467
43625
|
});
|
|
43468
|
-
} catch {
|
|
43626
|
+
} catch (err) {
|
|
43627
|
+
console.log(c.dim(` Could not list remote branches for tier ${tier}: ${err instanceof Error ? err.message : String(err)}`));
|
|
43469
43628
|
return [];
|
|
43470
43629
|
}
|
|
43471
43630
|
}
|
|
@@ -44115,7 +44274,7 @@ function resolveConfig() {
|
|
|
44115
44274
|
apiBase: isTestMode ? "http://localhost:8000/api" : apiBase,
|
|
44116
44275
|
provider: process.env.LOCUS_PROVIDER || settings?.provider || undefined,
|
|
44117
44276
|
model: process.env.LOCUS_MODEL || settings?.model || undefined,
|
|
44118
|
-
agentCount:
|
|
44277
|
+
agentCount: settings?.agentCount ?? 1,
|
|
44119
44278
|
testMode: isTestMode
|
|
44120
44279
|
};
|
|
44121
44280
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/telegram",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.5",
|
|
4
4
|
"description": "Telegram bot for Locus - remote control your AI agents from Telegram",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"author": "",
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@locusai/sdk": "^0.10.
|
|
35
|
+
"@locusai/sdk": "^0.10.5",
|
|
36
36
|
"dotenv": "^16.4.7",
|
|
37
37
|
"telegraf": "^4.16.3"
|
|
38
38
|
},
|