@harness-engineering/orchestrator 0.8.0 → 0.8.2
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/index.js +62 -36
- package/dist/index.mjs +62 -36
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -2887,6 +2887,21 @@ var path21 = __toESM(require("path"));
|
|
|
2887
2887
|
var import_node_crypto16 = require("crypto");
|
|
2888
2888
|
var import_core14 = require("@harness-engineering/core");
|
|
2889
2889
|
|
|
2890
|
+
// src/core/stall-detector.ts
|
|
2891
|
+
function detectStalledIssues(running, nowMs, stallTimeoutMs) {
|
|
2892
|
+
if (stallTimeoutMs <= 0) return [];
|
|
2893
|
+
const stalled = [];
|
|
2894
|
+
for (const [runId, entry] of running) {
|
|
2895
|
+
const reference = entry.session?.lastTimestamp ?? entry.startedAt;
|
|
2896
|
+
if (!reference) continue;
|
|
2897
|
+
const silentMs = nowMs - new Date(reference).getTime();
|
|
2898
|
+
if (silentMs >= stallTimeoutMs) {
|
|
2899
|
+
stalled.push(runId);
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
return stalled;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2890
2905
|
// src/intelligence/pipeline-runner.ts
|
|
2891
2906
|
var path9 = __toESM(require("path"));
|
|
2892
2907
|
var import_intelligence = require("@harness-engineering/intelligence");
|
|
@@ -4731,7 +4746,7 @@ var OpenAIBackend = class {
|
|
|
4731
4746
|
};
|
|
4732
4747
|
|
|
4733
4748
|
// src/agent/backends/gemini.ts
|
|
4734
|
-
var
|
|
4749
|
+
var import_genai = require("@google/genai");
|
|
4735
4750
|
var import_types13 = require("@harness-engineering/types");
|
|
4736
4751
|
var import_core6 = require("@harness-engineering/core");
|
|
4737
4752
|
var GeminiBackend = class {
|
|
@@ -4769,16 +4784,21 @@ var GeminiBackend = class {
|
|
|
4769
4784
|
let cacheCreationTokens = 0;
|
|
4770
4785
|
let cacheReadTokens = 0;
|
|
4771
4786
|
try {
|
|
4772
|
-
const genAI = new
|
|
4773
|
-
const
|
|
4787
|
+
const genAI = new import_genai.GoogleGenAI({ apiKey: this.config.apiKey });
|
|
4788
|
+
const response = await genAI.models.generateContentStream({
|
|
4774
4789
|
model: this.config.model,
|
|
4790
|
+
contents: params.prompt,
|
|
4775
4791
|
...geminiSession.systemPrompt !== void 0 && {
|
|
4776
|
-
systemInstruction: geminiSession.systemPrompt
|
|
4792
|
+
config: { systemInstruction: geminiSession.systemPrompt }
|
|
4777
4793
|
}
|
|
4778
4794
|
});
|
|
4779
|
-
const
|
|
4780
|
-
|
|
4781
|
-
|
|
4795
|
+
for await (const chunk of response) {
|
|
4796
|
+
let text;
|
|
4797
|
+
try {
|
|
4798
|
+
text = chunk.text;
|
|
4799
|
+
} catch {
|
|
4800
|
+
text = void 0;
|
|
4801
|
+
}
|
|
4782
4802
|
if (text) {
|
|
4783
4803
|
const event = {
|
|
4784
4804
|
type: "text",
|
|
@@ -4808,7 +4828,13 @@ var GeminiBackend = class {
|
|
|
4808
4828
|
return {
|
|
4809
4829
|
success: false,
|
|
4810
4830
|
sessionId: session.sessionId,
|
|
4811
|
-
usage: {
|
|
4831
|
+
usage: {
|
|
4832
|
+
inputTokens,
|
|
4833
|
+
outputTokens,
|
|
4834
|
+
totalTokens,
|
|
4835
|
+
cacheCreationTokens,
|
|
4836
|
+
cacheReadTokens
|
|
4837
|
+
},
|
|
4812
4838
|
error: errorMessage
|
|
4813
4839
|
};
|
|
4814
4840
|
}
|
|
@@ -4835,9 +4861,14 @@ var GeminiBackend = class {
|
|
|
4835
4861
|
return (0, import_types13.Ok)(void 0);
|
|
4836
4862
|
}
|
|
4837
4863
|
async healthCheck() {
|
|
4864
|
+
if (!this.config.apiKey) {
|
|
4865
|
+
return (0, import_types13.Err)({
|
|
4866
|
+
category: "agent_not_found",
|
|
4867
|
+
message: "GEMINI_API_KEY is not set"
|
|
4868
|
+
});
|
|
4869
|
+
}
|
|
4838
4870
|
try {
|
|
4839
|
-
|
|
4840
|
-
genAI.getGenerativeModel({ model: this.config.model });
|
|
4871
|
+
new import_genai.GoogleGenAI({ apiKey: this.config.apiKey });
|
|
4841
4872
|
return (0, import_types13.Ok)(void 0);
|
|
4842
4873
|
} catch (err) {
|
|
4843
4874
|
return (0, import_types13.Err)({
|
|
@@ -6941,7 +6972,7 @@ var ChatRequestSchema = import_zod6.z.object({
|
|
|
6941
6972
|
});
|
|
6942
6973
|
function handleChatProxyRoute(req, res, command = "claude") {
|
|
6943
6974
|
const { method, url } = req;
|
|
6944
|
-
if (method === "POST" && url === "/api/chat") {
|
|
6975
|
+
if (method === "POST" && (url === "/api/chat" || url === "/api/chat-proxy")) {
|
|
6945
6976
|
void handleChatRequest(req, res, command);
|
|
6946
6977
|
return true;
|
|
6947
6978
|
}
|
|
@@ -9300,7 +9331,7 @@ function requiredScopeForRoute(method, path24) {
|
|
|
9300
9331
|
if (path24.startsWith("/api/maintenance")) return "trigger-job";
|
|
9301
9332
|
if (path24.startsWith("/api/streams")) return "read-status";
|
|
9302
9333
|
if (path24.startsWith("/api/sessions")) return "read-status";
|
|
9303
|
-
if (path24.startsWith("/api/chat-proxy")) return "trigger-job";
|
|
9334
|
+
if (path24 === "/api/chat" || path24.startsWith("/api/chat-proxy")) return "trigger-job";
|
|
9304
9335
|
return null;
|
|
9305
9336
|
}
|
|
9306
9337
|
|
|
@@ -13011,32 +13042,27 @@ ${messages}`);
|
|
|
13011
13042
|
await this.handleEffect(effect);
|
|
13012
13043
|
}
|
|
13013
13044
|
const stallTimeoutMs = this.config.agent.stallTimeoutMs;
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
}
|
|
13023
|
-
|
|
13024
|
-
for (const runId of stalledIds) {
|
|
13025
|
-
const runEntry = this.state.running.get(runId);
|
|
13026
|
-
if (!runEntry) continue;
|
|
13027
|
-
this.logger.warn(
|
|
13028
|
-
`Agent stalled for ${runEntry.identifier}: ${Math.round((nowMs - new Date(runEntry.session?.lastTimestamp ?? 0).getTime()) / 1e3)}s since last event`,
|
|
13029
|
-
{ issueId: runId }
|
|
13030
|
-
);
|
|
13031
|
-
const stallEvent = {
|
|
13032
|
-
type: "stall_detected",
|
|
13045
|
+
const stalledIds = detectStalledIssues(this.state.running, nowMs, stallTimeoutMs);
|
|
13046
|
+
for (const runId of stalledIds) {
|
|
13047
|
+
const runEntry = this.state.running.get(runId);
|
|
13048
|
+
if (!runEntry) continue;
|
|
13049
|
+
const reference = runEntry.session?.lastTimestamp ?? runEntry.startedAt;
|
|
13050
|
+
const silentSec = Math.round((nowMs - new Date(reference).getTime()) / 1e3);
|
|
13051
|
+
const sinceWhat = runEntry.session?.lastTimestamp ? "last event" : "dispatch";
|
|
13052
|
+
this.logger.warn(
|
|
13053
|
+
`Agent stalled for ${runEntry.identifier}: ${silentSec}s since ${sinceWhat}`,
|
|
13054
|
+
{
|
|
13033
13055
|
issueId: runId
|
|
13034
|
-
};
|
|
13035
|
-
const stallResult = applyEvent(this.state, stallEvent, this.config);
|
|
13036
|
-
this.state = stallResult.nextState;
|
|
13037
|
-
for (const eff of stallResult.effects) {
|
|
13038
|
-
await this.handleEffect(eff);
|
|
13039
13056
|
}
|
|
13057
|
+
);
|
|
13058
|
+
const stallEvent = {
|
|
13059
|
+
type: "stall_detected",
|
|
13060
|
+
issueId: runId
|
|
13061
|
+
};
|
|
13062
|
+
const stallResult = applyEvent(this.state, stallEvent, this.config);
|
|
13063
|
+
this.state = stallResult.nextState;
|
|
13064
|
+
for (const eff of stallResult.effects) {
|
|
13065
|
+
await this.handleEffect(eff);
|
|
13040
13066
|
}
|
|
13041
13067
|
}
|
|
13042
13068
|
const openPrNumbers = [];
|
package/dist/index.mjs
CHANGED
|
@@ -2778,6 +2778,21 @@ import * as path21 from "path";
|
|
|
2778
2778
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
2779
2779
|
import { writeTaint } from "@harness-engineering/core";
|
|
2780
2780
|
|
|
2781
|
+
// src/core/stall-detector.ts
|
|
2782
|
+
function detectStalledIssues(running, nowMs, stallTimeoutMs) {
|
|
2783
|
+
if (stallTimeoutMs <= 0) return [];
|
|
2784
|
+
const stalled = [];
|
|
2785
|
+
for (const [runId, entry] of running) {
|
|
2786
|
+
const reference = entry.session?.lastTimestamp ?? entry.startedAt;
|
|
2787
|
+
if (!reference) continue;
|
|
2788
|
+
const silentMs = nowMs - new Date(reference).getTime();
|
|
2789
|
+
if (silentMs >= stallTimeoutMs) {
|
|
2790
|
+
stalled.push(runId);
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
return stalled;
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2781
2796
|
// src/intelligence/pipeline-runner.ts
|
|
2782
2797
|
import * as path9 from "path";
|
|
2783
2798
|
import { weightedRecommendPersona, refreshProfiles } from "@harness-engineering/intelligence";
|
|
@@ -4638,7 +4653,7 @@ var OpenAIBackend = class {
|
|
|
4638
4653
|
};
|
|
4639
4654
|
|
|
4640
4655
|
// src/agent/backends/gemini.ts
|
|
4641
|
-
import {
|
|
4656
|
+
import { GoogleGenAI } from "@google/genai";
|
|
4642
4657
|
import {
|
|
4643
4658
|
Ok as Ok13,
|
|
4644
4659
|
Err as Err10
|
|
@@ -4679,16 +4694,21 @@ var GeminiBackend = class {
|
|
|
4679
4694
|
let cacheCreationTokens = 0;
|
|
4680
4695
|
let cacheReadTokens = 0;
|
|
4681
4696
|
try {
|
|
4682
|
-
const genAI = new
|
|
4683
|
-
const
|
|
4697
|
+
const genAI = new GoogleGenAI({ apiKey: this.config.apiKey });
|
|
4698
|
+
const response = await genAI.models.generateContentStream({
|
|
4684
4699
|
model: this.config.model,
|
|
4700
|
+
contents: params.prompt,
|
|
4685
4701
|
...geminiSession.systemPrompt !== void 0 && {
|
|
4686
|
-
systemInstruction: geminiSession.systemPrompt
|
|
4702
|
+
config: { systemInstruction: geminiSession.systemPrompt }
|
|
4687
4703
|
}
|
|
4688
4704
|
});
|
|
4689
|
-
const
|
|
4690
|
-
|
|
4691
|
-
|
|
4705
|
+
for await (const chunk of response) {
|
|
4706
|
+
let text;
|
|
4707
|
+
try {
|
|
4708
|
+
text = chunk.text;
|
|
4709
|
+
} catch {
|
|
4710
|
+
text = void 0;
|
|
4711
|
+
}
|
|
4692
4712
|
if (text) {
|
|
4693
4713
|
const event = {
|
|
4694
4714
|
type: "text",
|
|
@@ -4718,7 +4738,13 @@ var GeminiBackend = class {
|
|
|
4718
4738
|
return {
|
|
4719
4739
|
success: false,
|
|
4720
4740
|
sessionId: session.sessionId,
|
|
4721
|
-
usage: {
|
|
4741
|
+
usage: {
|
|
4742
|
+
inputTokens,
|
|
4743
|
+
outputTokens,
|
|
4744
|
+
totalTokens,
|
|
4745
|
+
cacheCreationTokens,
|
|
4746
|
+
cacheReadTokens
|
|
4747
|
+
},
|
|
4722
4748
|
error: errorMessage
|
|
4723
4749
|
};
|
|
4724
4750
|
}
|
|
@@ -4745,9 +4771,14 @@ var GeminiBackend = class {
|
|
|
4745
4771
|
return Ok13(void 0);
|
|
4746
4772
|
}
|
|
4747
4773
|
async healthCheck() {
|
|
4774
|
+
if (!this.config.apiKey) {
|
|
4775
|
+
return Err10({
|
|
4776
|
+
category: "agent_not_found",
|
|
4777
|
+
message: "GEMINI_API_KEY is not set"
|
|
4778
|
+
});
|
|
4779
|
+
}
|
|
4748
4780
|
try {
|
|
4749
|
-
|
|
4750
|
-
genAI.getGenerativeModel({ model: this.config.model });
|
|
4781
|
+
new GoogleGenAI({ apiKey: this.config.apiKey });
|
|
4751
4782
|
return Ok13(void 0);
|
|
4752
4783
|
} catch (err) {
|
|
4753
4784
|
return Err10({
|
|
@@ -6874,7 +6905,7 @@ var ChatRequestSchema = z6.object({
|
|
|
6874
6905
|
});
|
|
6875
6906
|
function handleChatProxyRoute(req, res, command = "claude") {
|
|
6876
6907
|
const { method, url } = req;
|
|
6877
|
-
if (method === "POST" && url === "/api/chat") {
|
|
6908
|
+
if (method === "POST" && (url === "/api/chat" || url === "/api/chat-proxy")) {
|
|
6878
6909
|
void handleChatRequest(req, res, command);
|
|
6879
6910
|
return true;
|
|
6880
6911
|
}
|
|
@@ -9263,7 +9294,7 @@ function requiredScopeForRoute(method, path24) {
|
|
|
9263
9294
|
if (path24.startsWith("/api/maintenance")) return "trigger-job";
|
|
9264
9295
|
if (path24.startsWith("/api/streams")) return "read-status";
|
|
9265
9296
|
if (path24.startsWith("/api/sessions")) return "read-status";
|
|
9266
|
-
if (path24.startsWith("/api/chat-proxy")) return "trigger-job";
|
|
9297
|
+
if (path24 === "/api/chat" || path24.startsWith("/api/chat-proxy")) return "trigger-job";
|
|
9267
9298
|
return null;
|
|
9268
9299
|
}
|
|
9269
9300
|
|
|
@@ -12982,32 +13013,27 @@ ${messages}`);
|
|
|
12982
13013
|
await this.handleEffect(effect);
|
|
12983
13014
|
}
|
|
12984
13015
|
const stallTimeoutMs = this.config.agent.stallTimeoutMs;
|
|
12985
|
-
|
|
12986
|
-
|
|
12987
|
-
|
|
12988
|
-
|
|
12989
|
-
|
|
12990
|
-
|
|
12991
|
-
|
|
12992
|
-
|
|
12993
|
-
}
|
|
12994
|
-
|
|
12995
|
-
for (const runId of stalledIds) {
|
|
12996
|
-
const runEntry = this.state.running.get(runId);
|
|
12997
|
-
if (!runEntry) continue;
|
|
12998
|
-
this.logger.warn(
|
|
12999
|
-
`Agent stalled for ${runEntry.identifier}: ${Math.round((nowMs - new Date(runEntry.session?.lastTimestamp ?? 0).getTime()) / 1e3)}s since last event`,
|
|
13000
|
-
{ issueId: runId }
|
|
13001
|
-
);
|
|
13002
|
-
const stallEvent = {
|
|
13003
|
-
type: "stall_detected",
|
|
13016
|
+
const stalledIds = detectStalledIssues(this.state.running, nowMs, stallTimeoutMs);
|
|
13017
|
+
for (const runId of stalledIds) {
|
|
13018
|
+
const runEntry = this.state.running.get(runId);
|
|
13019
|
+
if (!runEntry) continue;
|
|
13020
|
+
const reference = runEntry.session?.lastTimestamp ?? runEntry.startedAt;
|
|
13021
|
+
const silentSec = Math.round((nowMs - new Date(reference).getTime()) / 1e3);
|
|
13022
|
+
const sinceWhat = runEntry.session?.lastTimestamp ? "last event" : "dispatch";
|
|
13023
|
+
this.logger.warn(
|
|
13024
|
+
`Agent stalled for ${runEntry.identifier}: ${silentSec}s since ${sinceWhat}`,
|
|
13025
|
+
{
|
|
13004
13026
|
issueId: runId
|
|
13005
|
-
};
|
|
13006
|
-
const stallResult = applyEvent(this.state, stallEvent, this.config);
|
|
13007
|
-
this.state = stallResult.nextState;
|
|
13008
|
-
for (const eff of stallResult.effects) {
|
|
13009
|
-
await this.handleEffect(eff);
|
|
13010
13027
|
}
|
|
13028
|
+
);
|
|
13029
|
+
const stallEvent = {
|
|
13030
|
+
type: "stall_detected",
|
|
13031
|
+
issueId: runId
|
|
13032
|
+
};
|
|
13033
|
+
const stallResult = applyEvent(this.state, stallEvent, this.config);
|
|
13034
|
+
this.state = stallResult.nextState;
|
|
13035
|
+
for (const eff of stallResult.effects) {
|
|
13036
|
+
await this.handleEffect(eff);
|
|
13011
13037
|
}
|
|
13012
13038
|
}
|
|
13013
13039
|
const openPrNumbers = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harness-engineering/orchestrator",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Orchestrator daemon for dispatching coding agents to issues",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@anthropic-ai/sdk": "^0.95.1",
|
|
40
40
|
"@earendil-works/pi-coding-agent": "^0.74.1",
|
|
41
|
-
"@google/
|
|
41
|
+
"@google/genai": "^2.0.4",
|
|
42
42
|
"bcryptjs": "^2.4.3",
|
|
43
43
|
"better-sqlite3": "^12.10.0",
|
|
44
44
|
"ink": "^4.4.1",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"ws": "^8.21.0",
|
|
49
49
|
"yaml": "^2.8.3",
|
|
50
50
|
"zod": "^3.25.76",
|
|
51
|
-
"@harness-engineering/core": "0.
|
|
52
|
-
"@harness-engineering/
|
|
53
|
-
"@harness-engineering/
|
|
54
|
-
"@harness-engineering/
|
|
51
|
+
"@harness-engineering/core": "0.30.0",
|
|
52
|
+
"@harness-engineering/graph": "0.11.0",
|
|
53
|
+
"@harness-engineering/intelligence": "0.3.0",
|
|
54
|
+
"@harness-engineering/types": "0.16.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@asteasolutions/zod-to-openapi": "^7.3.0",
|