@harness-engineering/orchestrator 0.2.5 → 0.2.7
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/README.md +4 -4
- package/dist/index.d.mts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.js +395 -35
- package/dist/index.mjs +393 -27
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -119,10 +119,10 @@ The main class that ties everything together:
|
|
|
119
119
|
|
|
120
120
|
### Tracker Adapters
|
|
121
121
|
|
|
122
|
-
| Export | Description
|
|
123
|
-
| ------------------------ |
|
|
124
|
-
| `RoadmapTrackerAdapter` | Reads issues from `docs/roadmap.md`
|
|
125
|
-
| `LinearTrackerExtension` | Extends tracking with Linear integration |
|
|
122
|
+
| Export | Description |
|
|
123
|
+
| ------------------------ | ---------------------------------------------------- |
|
|
124
|
+
| `RoadmapTrackerAdapter` | Reads issues from `docs/roadmap.md` |
|
|
125
|
+
| `LinearTrackerExtension` | Extends tracking with Linear integration _(planned)_ |
|
|
126
126
|
|
|
127
127
|
### Prompt Rendering
|
|
128
128
|
|
package/dist/index.d.mts
CHANGED
|
@@ -344,6 +344,70 @@ declare class ClaudeBackend implements AgentBackend {
|
|
|
344
344
|
healthCheck(): Promise<Result<void, AgentError>>;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
interface OpenAIBackendConfig {
|
|
348
|
+
/** OpenAI model to use. Defaults to 'gpt-4o'. */
|
|
349
|
+
model?: string;
|
|
350
|
+
/** API key. Defaults to process.env.OPENAI_API_KEY. */
|
|
351
|
+
apiKey?: string;
|
|
352
|
+
}
|
|
353
|
+
interface OpenAISession extends AgentSession {
|
|
354
|
+
systemPrompt?: string;
|
|
355
|
+
}
|
|
356
|
+
declare class OpenAIBackend implements AgentBackend {
|
|
357
|
+
readonly name = "openai";
|
|
358
|
+
private config;
|
|
359
|
+
private client;
|
|
360
|
+
private cacheAdapter;
|
|
361
|
+
constructor(config?: OpenAIBackendConfig);
|
|
362
|
+
startSession(params: SessionStartParams): Promise<Result<AgentSession, AgentError>>;
|
|
363
|
+
runTurn(session: AgentSession, params: TurnParams): AsyncGenerator<AgentEvent, TurnResult, void>;
|
|
364
|
+
stopSession(_session: AgentSession): Promise<Result<void, AgentError>>;
|
|
365
|
+
healthCheck(): Promise<Result<void, AgentError>>;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
interface GeminiBackendConfig {
|
|
369
|
+
/** Gemini model to use. Defaults to 'gemini-2.0-flash'. */
|
|
370
|
+
model?: string;
|
|
371
|
+
/** API key. Defaults to process.env.GEMINI_API_KEY or process.env.GOOGLE_API_KEY. */
|
|
372
|
+
apiKey?: string;
|
|
373
|
+
}
|
|
374
|
+
interface GeminiSession extends AgentSession {
|
|
375
|
+
systemPrompt?: string;
|
|
376
|
+
}
|
|
377
|
+
declare class GeminiBackend implements AgentBackend {
|
|
378
|
+
readonly name = "gemini";
|
|
379
|
+
private config;
|
|
380
|
+
private cacheAdapter;
|
|
381
|
+
constructor(config?: GeminiBackendConfig);
|
|
382
|
+
startSession(params: SessionStartParams): Promise<Result<AgentSession, AgentError>>;
|
|
383
|
+
runTurn(session: AgentSession, params: TurnParams): AsyncGenerator<AgentEvent, TurnResult, void>;
|
|
384
|
+
stopSession(_session: AgentSession): Promise<Result<void, AgentError>>;
|
|
385
|
+
healthCheck(): Promise<Result<void, AgentError>>;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
interface AnthropicBackendConfig {
|
|
389
|
+
/** Anthropic model to use. Defaults to 'claude-sonnet-4-20250514'. */
|
|
390
|
+
model?: string;
|
|
391
|
+
/** API key. Defaults to process.env.ANTHROPIC_API_KEY. */
|
|
392
|
+
apiKey?: string;
|
|
393
|
+
/** Maximum output tokens. Defaults to 4096. */
|
|
394
|
+
maxTokens?: number;
|
|
395
|
+
}
|
|
396
|
+
interface AnthropicSession extends AgentSession {
|
|
397
|
+
systemPrompt?: string;
|
|
398
|
+
}
|
|
399
|
+
declare class AnthropicBackend implements AgentBackend {
|
|
400
|
+
readonly name = "anthropic";
|
|
401
|
+
private config;
|
|
402
|
+
private client;
|
|
403
|
+
private cacheAdapter;
|
|
404
|
+
constructor(config?: AnthropicBackendConfig);
|
|
405
|
+
startSession(params: SessionStartParams): Promise<Result<AgentSession, AgentError>>;
|
|
406
|
+
runTurn(session: AgentSession, params: TurnParams): AsyncGenerator<AgentEvent, TurnResult, void>;
|
|
407
|
+
stopSession(_session: AgentSession): Promise<Result<void, AgentError>>;
|
|
408
|
+
healthCheck(): Promise<Result<void, AgentError>>;
|
|
409
|
+
}
|
|
410
|
+
|
|
347
411
|
declare class PromptRenderer {
|
|
348
412
|
private engine;
|
|
349
413
|
constructor();
|
|
@@ -440,4 +504,4 @@ declare function launchTUI(orchestrator: Orchestrator): {
|
|
|
440
504
|
waitUntilExit: () => Promise<void>;
|
|
441
505
|
};
|
|
442
506
|
|
|
443
|
-
export { type AgentUpdateEvent, type ApplyEventResult, ClaudeBackend, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, MockBackend, Orchestrator, type OrchestratorEvent, type OrchestratorState, PromptRenderer, type RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type TickEvent, type TokenTotals, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, applyEvent, calculateRetryDelay, canDispatch, createEmptyState, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, reconcile, selectCandidates, sortCandidates, validateWorkflowConfig };
|
|
507
|
+
export { type AgentUpdateEvent, AnthropicBackend, type AnthropicBackendConfig, type AnthropicSession, type ApplyEventResult, ClaudeBackend, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, GeminiBackend, type GeminiBackendConfig, type GeminiSession, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, MockBackend, OpenAIBackend, type OpenAIBackendConfig, type OpenAISession, Orchestrator, type OrchestratorEvent, type OrchestratorState, PromptRenderer, type RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type TickEvent, type TokenTotals, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, applyEvent, calculateRetryDelay, canDispatch, createEmptyState, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, reconcile, selectCandidates, sortCandidates, validateWorkflowConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -344,6 +344,70 @@ declare class ClaudeBackend implements AgentBackend {
|
|
|
344
344
|
healthCheck(): Promise<Result<void, AgentError>>;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
interface OpenAIBackendConfig {
|
|
348
|
+
/** OpenAI model to use. Defaults to 'gpt-4o'. */
|
|
349
|
+
model?: string;
|
|
350
|
+
/** API key. Defaults to process.env.OPENAI_API_KEY. */
|
|
351
|
+
apiKey?: string;
|
|
352
|
+
}
|
|
353
|
+
interface OpenAISession extends AgentSession {
|
|
354
|
+
systemPrompt?: string;
|
|
355
|
+
}
|
|
356
|
+
declare class OpenAIBackend implements AgentBackend {
|
|
357
|
+
readonly name = "openai";
|
|
358
|
+
private config;
|
|
359
|
+
private client;
|
|
360
|
+
private cacheAdapter;
|
|
361
|
+
constructor(config?: OpenAIBackendConfig);
|
|
362
|
+
startSession(params: SessionStartParams): Promise<Result<AgentSession, AgentError>>;
|
|
363
|
+
runTurn(session: AgentSession, params: TurnParams): AsyncGenerator<AgentEvent, TurnResult, void>;
|
|
364
|
+
stopSession(_session: AgentSession): Promise<Result<void, AgentError>>;
|
|
365
|
+
healthCheck(): Promise<Result<void, AgentError>>;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
interface GeminiBackendConfig {
|
|
369
|
+
/** Gemini model to use. Defaults to 'gemini-2.0-flash'. */
|
|
370
|
+
model?: string;
|
|
371
|
+
/** API key. Defaults to process.env.GEMINI_API_KEY or process.env.GOOGLE_API_KEY. */
|
|
372
|
+
apiKey?: string;
|
|
373
|
+
}
|
|
374
|
+
interface GeminiSession extends AgentSession {
|
|
375
|
+
systemPrompt?: string;
|
|
376
|
+
}
|
|
377
|
+
declare class GeminiBackend implements AgentBackend {
|
|
378
|
+
readonly name = "gemini";
|
|
379
|
+
private config;
|
|
380
|
+
private cacheAdapter;
|
|
381
|
+
constructor(config?: GeminiBackendConfig);
|
|
382
|
+
startSession(params: SessionStartParams): Promise<Result<AgentSession, AgentError>>;
|
|
383
|
+
runTurn(session: AgentSession, params: TurnParams): AsyncGenerator<AgentEvent, TurnResult, void>;
|
|
384
|
+
stopSession(_session: AgentSession): Promise<Result<void, AgentError>>;
|
|
385
|
+
healthCheck(): Promise<Result<void, AgentError>>;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
interface AnthropicBackendConfig {
|
|
389
|
+
/** Anthropic model to use. Defaults to 'claude-sonnet-4-20250514'. */
|
|
390
|
+
model?: string;
|
|
391
|
+
/** API key. Defaults to process.env.ANTHROPIC_API_KEY. */
|
|
392
|
+
apiKey?: string;
|
|
393
|
+
/** Maximum output tokens. Defaults to 4096. */
|
|
394
|
+
maxTokens?: number;
|
|
395
|
+
}
|
|
396
|
+
interface AnthropicSession extends AgentSession {
|
|
397
|
+
systemPrompt?: string;
|
|
398
|
+
}
|
|
399
|
+
declare class AnthropicBackend implements AgentBackend {
|
|
400
|
+
readonly name = "anthropic";
|
|
401
|
+
private config;
|
|
402
|
+
private client;
|
|
403
|
+
private cacheAdapter;
|
|
404
|
+
constructor(config?: AnthropicBackendConfig);
|
|
405
|
+
startSession(params: SessionStartParams): Promise<Result<AgentSession, AgentError>>;
|
|
406
|
+
runTurn(session: AgentSession, params: TurnParams): AsyncGenerator<AgentEvent, TurnResult, void>;
|
|
407
|
+
stopSession(_session: AgentSession): Promise<Result<void, AgentError>>;
|
|
408
|
+
healthCheck(): Promise<Result<void, AgentError>>;
|
|
409
|
+
}
|
|
410
|
+
|
|
347
411
|
declare class PromptRenderer {
|
|
348
412
|
private engine;
|
|
349
413
|
constructor();
|
|
@@ -440,4 +504,4 @@ declare function launchTUI(orchestrator: Orchestrator): {
|
|
|
440
504
|
waitUntilExit: () => Promise<void>;
|
|
441
505
|
};
|
|
442
506
|
|
|
443
|
-
export { type AgentUpdateEvent, type ApplyEventResult, ClaudeBackend, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, MockBackend, Orchestrator, type OrchestratorEvent, type OrchestratorState, PromptRenderer, type RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type TickEvent, type TokenTotals, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, applyEvent, calculateRetryDelay, canDispatch, createEmptyState, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, reconcile, selectCandidates, sortCandidates, validateWorkflowConfig };
|
|
507
|
+
export { type AgentUpdateEvent, AnthropicBackend, type AnthropicBackendConfig, type AnthropicSession, type ApplyEventResult, ClaudeBackend, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, GeminiBackend, type GeminiBackendConfig, type GeminiSession, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, MockBackend, OpenAIBackend, type OpenAIBackendConfig, type OpenAISession, Orchestrator, type OrchestratorEvent, type OrchestratorState, PromptRenderer, type RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type TickEvent, type TokenTotals, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, applyEvent, calculateRetryDelay, canDispatch, createEmptyState, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, reconcile, selectCandidates, sortCandidates, validateWorkflowConfig };
|
package/dist/index.js
CHANGED
|
@@ -30,9 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AnthropicBackend: () => AnthropicBackend,
|
|
33
34
|
ClaudeBackend: () => ClaudeBackend,
|
|
35
|
+
GeminiBackend: () => GeminiBackend,
|
|
34
36
|
LinearGraphQLStub: () => LinearGraphQLStub,
|
|
35
37
|
MockBackend: () => MockBackend,
|
|
38
|
+
OpenAIBackend: () => OpenAIBackend,
|
|
36
39
|
Orchestrator: () => Orchestrator,
|
|
37
40
|
PromptRenderer: () => PromptRenderer,
|
|
38
41
|
RoadmapTrackerAdapter: () => RoadmapTrackerAdapter,
|
|
@@ -68,15 +71,20 @@ function calculateRetryDelay(attempt, type, maxRetryBackoffMs = DEFAULT_MAX_RETR
|
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
// src/core/candidate-selection.ts
|
|
74
|
+
function comparePriority(a, b) {
|
|
75
|
+
const pa = a.priority ?? Number.MAX_SAFE_INTEGER;
|
|
76
|
+
const pb = b.priority ?? Number.MAX_SAFE_INTEGER;
|
|
77
|
+
return pa !== pb ? pa - pb : null;
|
|
78
|
+
}
|
|
79
|
+
function compareCreatedAt(a, b) {
|
|
80
|
+
const ca = a.createdAt ?? "\uFFFF";
|
|
81
|
+
const cb = b.createdAt ?? "\uFFFF";
|
|
82
|
+
if (ca === cb) return null;
|
|
83
|
+
return ca < cb ? -1 : 1;
|
|
84
|
+
}
|
|
71
85
|
function sortCandidates(issues) {
|
|
72
86
|
return [...issues].sort((a, b) => {
|
|
73
|
-
|
|
74
|
-
const pb = b.priority ?? Number.MAX_SAFE_INTEGER;
|
|
75
|
-
if (pa !== pb) return pa - pb;
|
|
76
|
-
const ca = a.createdAt ?? "\uFFFF";
|
|
77
|
-
const cb = b.createdAt ?? "\uFFFF";
|
|
78
|
-
if (ca !== cb) return ca < cb ? -1 : 1;
|
|
79
|
-
return a.identifier.localeCompare(b.identifier);
|
|
87
|
+
return comparePriority(a, b) ?? compareCreatedAt(a, b) ?? a.identifier.localeCompare(b.identifier);
|
|
80
88
|
});
|
|
81
89
|
}
|
|
82
90
|
function isEligible(issue, state, activeStates, terminalStates) {
|
|
@@ -760,6 +768,21 @@ var MockBackend = class {
|
|
|
760
768
|
var import_node_child_process2 = require("child_process");
|
|
761
769
|
var readline = __toESM(require("readline"));
|
|
762
770
|
var import_types8 = require("@harness-engineering/types");
|
|
771
|
+
function resolveExitCode(code, command, resolve) {
|
|
772
|
+
if (code === 0) {
|
|
773
|
+
resolve((0, import_types8.Ok)(void 0));
|
|
774
|
+
} else {
|
|
775
|
+
resolve(
|
|
776
|
+
(0, import_types8.Err)({
|
|
777
|
+
category: "agent_not_found",
|
|
778
|
+
message: `Claude command '${command}' not found or failed`
|
|
779
|
+
})
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
function resolveSpawnError(command, resolve) {
|
|
784
|
+
resolve((0, import_types8.Err)({ category: "agent_not_found", message: `Claude command '${command}' not found` }));
|
|
785
|
+
}
|
|
763
786
|
var ClaudeBackend = class {
|
|
764
787
|
name = "claude";
|
|
765
788
|
command;
|
|
@@ -818,27 +841,338 @@ var ClaudeBackend = class {
|
|
|
818
841
|
async healthCheck() {
|
|
819
842
|
return new Promise((resolve) => {
|
|
820
843
|
const child = (0, import_node_child_process2.spawn)(this.command, ["--version"]);
|
|
821
|
-
child.on("exit", (code) =>
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
844
|
+
child.on("exit", (code) => resolveExitCode(code, this.command, resolve));
|
|
845
|
+
child.on("error", () => resolveSpawnError(this.command, resolve));
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
// src/agent/backends/openai.ts
|
|
851
|
+
var import_openai = __toESM(require("openai"));
|
|
852
|
+
var import_types9 = require("@harness-engineering/types");
|
|
853
|
+
var import_core2 = require("@harness-engineering/core");
|
|
854
|
+
var OpenAIBackend = class {
|
|
855
|
+
name = "openai";
|
|
856
|
+
config;
|
|
857
|
+
client;
|
|
858
|
+
cacheAdapter;
|
|
859
|
+
constructor(config = {}) {
|
|
860
|
+
this.config = {
|
|
861
|
+
model: config.model ?? "gpt-4o",
|
|
862
|
+
apiKey: config.apiKey ?? process.env.OPENAI_API_KEY ?? ""
|
|
863
|
+
};
|
|
864
|
+
this.client = new import_openai.default({ apiKey: this.config.apiKey });
|
|
865
|
+
this.cacheAdapter = new import_core2.OpenAICacheAdapter();
|
|
866
|
+
}
|
|
867
|
+
async startSession(params) {
|
|
868
|
+
if (!this.config.apiKey) {
|
|
869
|
+
return (0, import_types9.Err)({
|
|
870
|
+
category: "agent_not_found",
|
|
871
|
+
message: "OPENAI_API_KEY is not set"
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
const session = {
|
|
875
|
+
sessionId: `openai-session-${Date.now()}`,
|
|
876
|
+
workspacePath: params.workspacePath,
|
|
877
|
+
backendName: this.name,
|
|
878
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
879
|
+
...params.systemPrompt !== void 0 && { systemPrompt: params.systemPrompt }
|
|
880
|
+
};
|
|
881
|
+
return (0, import_types9.Ok)(session);
|
|
882
|
+
}
|
|
883
|
+
async *runTurn(session, params) {
|
|
884
|
+
const openAISession = session;
|
|
885
|
+
const messages = [];
|
|
886
|
+
if (openAISession.systemPrompt) {
|
|
887
|
+
messages.push({ role: "system", content: openAISession.systemPrompt });
|
|
888
|
+
}
|
|
889
|
+
messages.push({ role: "user", content: params.prompt });
|
|
890
|
+
let inputTokens = 0;
|
|
891
|
+
let outputTokens = 0;
|
|
892
|
+
let totalTokens = 0;
|
|
893
|
+
let cacheCreationTokens = 0;
|
|
894
|
+
let cacheReadTokens = 0;
|
|
895
|
+
try {
|
|
896
|
+
const stream = await this.client.chat.completions.create({
|
|
897
|
+
model: this.config.model,
|
|
898
|
+
messages,
|
|
899
|
+
stream: true,
|
|
900
|
+
stream_options: { include_usage: true }
|
|
901
|
+
});
|
|
902
|
+
for await (const chunk of stream) {
|
|
903
|
+
const delta = chunk.choices[0]?.delta;
|
|
904
|
+
if (delta?.content) {
|
|
905
|
+
const event = {
|
|
906
|
+
type: "text",
|
|
907
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
908
|
+
content: delta.content,
|
|
909
|
+
sessionId: session.sessionId
|
|
910
|
+
};
|
|
911
|
+
yield event;
|
|
831
912
|
}
|
|
913
|
+
if (chunk.usage) {
|
|
914
|
+
inputTokens = chunk.usage.prompt_tokens ?? 0;
|
|
915
|
+
outputTokens = chunk.usage.completion_tokens ?? 0;
|
|
916
|
+
totalTokens = chunk.usage.total_tokens ?? 0;
|
|
917
|
+
const cacheUsage = this.cacheAdapter.parseCacheUsage(chunk);
|
|
918
|
+
cacheCreationTokens = cacheUsage.cacheCreationTokens;
|
|
919
|
+
cacheReadTokens = cacheUsage.cacheReadTokens;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
} catch (err) {
|
|
923
|
+
const errorMessage = err instanceof Error ? err.message : "OpenAI request failed";
|
|
924
|
+
yield {
|
|
925
|
+
type: "error",
|
|
926
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
927
|
+
content: errorMessage,
|
|
928
|
+
sessionId: session.sessionId
|
|
929
|
+
};
|
|
930
|
+
return {
|
|
931
|
+
success: false,
|
|
932
|
+
sessionId: session.sessionId,
|
|
933
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
934
|
+
error: errorMessage
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
success: true,
|
|
939
|
+
sessionId: session.sessionId,
|
|
940
|
+
usage: {
|
|
941
|
+
inputTokens,
|
|
942
|
+
outputTokens,
|
|
943
|
+
totalTokens,
|
|
944
|
+
cacheCreationTokens,
|
|
945
|
+
cacheReadTokens
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
async stopSession(_session) {
|
|
950
|
+
return (0, import_types9.Ok)(void 0);
|
|
951
|
+
}
|
|
952
|
+
async healthCheck() {
|
|
953
|
+
try {
|
|
954
|
+
await this.client.models.list();
|
|
955
|
+
return (0, import_types9.Ok)(void 0);
|
|
956
|
+
} catch (err) {
|
|
957
|
+
return (0, import_types9.Err)({
|
|
958
|
+
category: "response_error",
|
|
959
|
+
message: err instanceof Error ? err.message : "OpenAI health check failed"
|
|
832
960
|
});
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
// src/agent/backends/gemini.ts
|
|
966
|
+
var import_generative_ai = require("@google/generative-ai");
|
|
967
|
+
var import_types10 = require("@harness-engineering/types");
|
|
968
|
+
var import_core3 = require("@harness-engineering/core");
|
|
969
|
+
var GeminiBackend = class {
|
|
970
|
+
name = "gemini";
|
|
971
|
+
config;
|
|
972
|
+
cacheAdapter;
|
|
973
|
+
constructor(config = {}) {
|
|
974
|
+
this.config = {
|
|
975
|
+
model: config.model ?? "gemini-2.0-flash",
|
|
976
|
+
apiKey: config.apiKey ?? process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY ?? ""
|
|
977
|
+
};
|
|
978
|
+
this.cacheAdapter = new import_core3.GeminiCacheAdapter();
|
|
979
|
+
}
|
|
980
|
+
async startSession(params) {
|
|
981
|
+
if (!this.config.apiKey) {
|
|
982
|
+
return (0, import_types10.Err)({
|
|
983
|
+
category: "agent_not_found",
|
|
984
|
+
message: "GEMINI_API_KEY is not set"
|
|
840
985
|
});
|
|
841
|
-
}
|
|
986
|
+
}
|
|
987
|
+
const session = {
|
|
988
|
+
sessionId: `gemini-session-${Date.now()}`,
|
|
989
|
+
workspacePath: params.workspacePath,
|
|
990
|
+
backendName: this.name,
|
|
991
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
992
|
+
...params.systemPrompt !== void 0 && { systemPrompt: params.systemPrompt }
|
|
993
|
+
};
|
|
994
|
+
return (0, import_types10.Ok)(session);
|
|
995
|
+
}
|
|
996
|
+
async *runTurn(session, params) {
|
|
997
|
+
const geminiSession = session;
|
|
998
|
+
let inputTokens = 0;
|
|
999
|
+
let outputTokens = 0;
|
|
1000
|
+
let totalTokens = 0;
|
|
1001
|
+
let cacheCreationTokens = 0;
|
|
1002
|
+
let cacheReadTokens = 0;
|
|
1003
|
+
try {
|
|
1004
|
+
const genAI = new import_generative_ai.GoogleGenerativeAI(this.config.apiKey);
|
|
1005
|
+
const model = genAI.getGenerativeModel({
|
|
1006
|
+
model: this.config.model,
|
|
1007
|
+
...geminiSession.systemPrompt !== void 0 && {
|
|
1008
|
+
systemInstruction: geminiSession.systemPrompt
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
1011
|
+
const result = await model.generateContentStream(params.prompt);
|
|
1012
|
+
for await (const chunk of result.stream) {
|
|
1013
|
+
const text = chunk.text();
|
|
1014
|
+
if (text) {
|
|
1015
|
+
const event = {
|
|
1016
|
+
type: "text",
|
|
1017
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1018
|
+
content: text,
|
|
1019
|
+
sessionId: session.sessionId
|
|
1020
|
+
};
|
|
1021
|
+
yield event;
|
|
1022
|
+
}
|
|
1023
|
+
if (chunk.usageMetadata) {
|
|
1024
|
+
inputTokens = chunk.usageMetadata.promptTokenCount ?? 0;
|
|
1025
|
+
outputTokens = chunk.usageMetadata.candidatesTokenCount ?? 0;
|
|
1026
|
+
totalTokens = chunk.usageMetadata.totalTokenCount ?? 0;
|
|
1027
|
+
const cacheUsage = this.cacheAdapter.parseCacheUsage(chunk);
|
|
1028
|
+
cacheCreationTokens = cacheUsage.cacheCreationTokens;
|
|
1029
|
+
cacheReadTokens = cacheUsage.cacheReadTokens;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
} catch (err) {
|
|
1033
|
+
const errorMessage = err instanceof Error ? err.message : "Gemini request failed";
|
|
1034
|
+
yield {
|
|
1035
|
+
type: "error",
|
|
1036
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1037
|
+
content: errorMessage,
|
|
1038
|
+
sessionId: session.sessionId
|
|
1039
|
+
};
|
|
1040
|
+
return {
|
|
1041
|
+
success: false,
|
|
1042
|
+
sessionId: session.sessionId,
|
|
1043
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
1044
|
+
error: errorMessage
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
return {
|
|
1048
|
+
success: true,
|
|
1049
|
+
sessionId: session.sessionId,
|
|
1050
|
+
usage: {
|
|
1051
|
+
inputTokens,
|
|
1052
|
+
outputTokens,
|
|
1053
|
+
totalTokens,
|
|
1054
|
+
cacheCreationTokens,
|
|
1055
|
+
cacheReadTokens
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
async stopSession(_session) {
|
|
1060
|
+
return (0, import_types10.Ok)(void 0);
|
|
1061
|
+
}
|
|
1062
|
+
async healthCheck() {
|
|
1063
|
+
try {
|
|
1064
|
+
const genAI = new import_generative_ai.GoogleGenerativeAI(this.config.apiKey);
|
|
1065
|
+
genAI.getGenerativeModel({ model: this.config.model });
|
|
1066
|
+
return (0, import_types10.Ok)(void 0);
|
|
1067
|
+
} catch (err) {
|
|
1068
|
+
return (0, import_types10.Err)({
|
|
1069
|
+
category: "response_error",
|
|
1070
|
+
message: err instanceof Error ? err.message : "Gemini health check failed"
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
// src/agent/backends/anthropic.ts
|
|
1077
|
+
var import_sdk = __toESM(require("@anthropic-ai/sdk"));
|
|
1078
|
+
var import_types11 = require("@harness-engineering/types");
|
|
1079
|
+
var import_core4 = require("@harness-engineering/core");
|
|
1080
|
+
var AnthropicBackend = class {
|
|
1081
|
+
name = "anthropic";
|
|
1082
|
+
config;
|
|
1083
|
+
client;
|
|
1084
|
+
cacheAdapter;
|
|
1085
|
+
constructor(config = {}) {
|
|
1086
|
+
this.config = {
|
|
1087
|
+
model: config.model ?? "claude-sonnet-4-20250514",
|
|
1088
|
+
apiKey: config.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "",
|
|
1089
|
+
maxTokens: config.maxTokens ?? 4096
|
|
1090
|
+
};
|
|
1091
|
+
this.client = new import_sdk.default({ apiKey: this.config.apiKey });
|
|
1092
|
+
this.cacheAdapter = new import_core4.AnthropicCacheAdapter();
|
|
1093
|
+
}
|
|
1094
|
+
async startSession(params) {
|
|
1095
|
+
if (!this.config.apiKey) {
|
|
1096
|
+
return (0, import_types11.Err)({
|
|
1097
|
+
category: "agent_not_found",
|
|
1098
|
+
message: "ANTHROPIC_API_KEY is not set"
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
const session = {
|
|
1102
|
+
sessionId: `anthropic-session-${Date.now()}`,
|
|
1103
|
+
workspacePath: params.workspacePath,
|
|
1104
|
+
backendName: this.name,
|
|
1105
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1106
|
+
...params.systemPrompt !== void 0 && { systemPrompt: params.systemPrompt }
|
|
1107
|
+
};
|
|
1108
|
+
return (0, import_types11.Ok)(session);
|
|
1109
|
+
}
|
|
1110
|
+
async *runTurn(session, params) {
|
|
1111
|
+
const anthropicSession = session;
|
|
1112
|
+
const systemBlocks = anthropicSession.systemPrompt ? [
|
|
1113
|
+
this.cacheAdapter.wrapSystemBlock(
|
|
1114
|
+
anthropicSession.systemPrompt,
|
|
1115
|
+
"session"
|
|
1116
|
+
)
|
|
1117
|
+
] : void 0;
|
|
1118
|
+
try {
|
|
1119
|
+
const stream = this.client.messages.stream({
|
|
1120
|
+
model: this.config.model,
|
|
1121
|
+
max_tokens: this.config.maxTokens,
|
|
1122
|
+
...systemBlocks && { system: systemBlocks },
|
|
1123
|
+
messages: [{ role: "user", content: params.prompt }]
|
|
1124
|
+
});
|
|
1125
|
+
for await (const event of stream) {
|
|
1126
|
+
if (event.type === "content_block_delta" && "text" in event.delta) {
|
|
1127
|
+
yield {
|
|
1128
|
+
type: "text",
|
|
1129
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1130
|
+
content: event.delta.text,
|
|
1131
|
+
sessionId: session.sessionId
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
const finalMessage = await stream.finalMessage();
|
|
1136
|
+
const { input_tokens, output_tokens } = finalMessage.usage;
|
|
1137
|
+
const cacheUsage = this.cacheAdapter.parseCacheUsage(finalMessage);
|
|
1138
|
+
return {
|
|
1139
|
+
success: true,
|
|
1140
|
+
sessionId: session.sessionId,
|
|
1141
|
+
usage: {
|
|
1142
|
+
inputTokens: input_tokens,
|
|
1143
|
+
outputTokens: output_tokens,
|
|
1144
|
+
totalTokens: input_tokens + output_tokens,
|
|
1145
|
+
cacheCreationTokens: cacheUsage.cacheCreationTokens,
|
|
1146
|
+
cacheReadTokens: cacheUsage.cacheReadTokens
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
} catch (err) {
|
|
1150
|
+
const errorMessage = err instanceof Error ? err.message : "Anthropic request failed";
|
|
1151
|
+
yield {
|
|
1152
|
+
type: "error",
|
|
1153
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1154
|
+
content: errorMessage,
|
|
1155
|
+
sessionId: session.sessionId
|
|
1156
|
+
};
|
|
1157
|
+
return {
|
|
1158
|
+
success: false,
|
|
1159
|
+
sessionId: session.sessionId,
|
|
1160
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
1161
|
+
error: errorMessage
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
async stopSession(_session) {
|
|
1166
|
+
return (0, import_types11.Ok)(void 0);
|
|
1167
|
+
}
|
|
1168
|
+
async healthCheck() {
|
|
1169
|
+
if (!this.config.apiKey) {
|
|
1170
|
+
return (0, import_types11.Err)({
|
|
1171
|
+
category: "response_error",
|
|
1172
|
+
message: "ANTHROPIC_API_KEY is not set"
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
return (0, import_types11.Ok)(void 0);
|
|
842
1176
|
}
|
|
843
1177
|
};
|
|
844
1178
|
|
|
@@ -866,6 +1200,7 @@ var PromptRenderer = class {
|
|
|
866
1200
|
|
|
867
1201
|
// src/orchestrator.ts
|
|
868
1202
|
var import_node_events = require("events");
|
|
1203
|
+
var import_core6 = require("@harness-engineering/core");
|
|
869
1204
|
|
|
870
1205
|
// src/agent/runner.ts
|
|
871
1206
|
var AgentRunner = class {
|
|
@@ -892,7 +1227,13 @@ var AgentRunner = class {
|
|
|
892
1227
|
let lastResult = {
|
|
893
1228
|
success: false,
|
|
894
1229
|
sessionId: session.sessionId,
|
|
895
|
-
usage: {
|
|
1230
|
+
usage: {
|
|
1231
|
+
inputTokens: 0,
|
|
1232
|
+
outputTokens: 0,
|
|
1233
|
+
totalTokens: 0,
|
|
1234
|
+
cacheCreationTokens: 0,
|
|
1235
|
+
cacheReadTokens: 0
|
|
1236
|
+
}
|
|
896
1237
|
};
|
|
897
1238
|
try {
|
|
898
1239
|
while (currentTurn < this.options.maxTurns) {
|
|
@@ -991,7 +1332,7 @@ var StructuredLogger = class {
|
|
|
991
1332
|
// src/workspace/config-scanner.ts
|
|
992
1333
|
var import_node_fs = require("fs");
|
|
993
1334
|
var import_node_path = require("path");
|
|
994
|
-
var
|
|
1335
|
+
var import_core5 = require("@harness-engineering/core");
|
|
995
1336
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
996
1337
|
function scanSingleFile(filePath, targetDir, scanner) {
|
|
997
1338
|
if (!(0, import_node_fs.existsSync)(filePath)) return null;
|
|
@@ -1001,28 +1342,27 @@ function scanSingleFile(filePath, targetDir, scanner) {
|
|
|
1001
1342
|
} catch {
|
|
1002
1343
|
return null;
|
|
1003
1344
|
}
|
|
1004
|
-
const injectionFindings = (0,
|
|
1005
|
-
const findings = (0,
|
|
1345
|
+
const injectionFindings = (0, import_core5.scanForInjection)(content);
|
|
1346
|
+
const findings = (0, import_core5.mapInjectionFindings)(injectionFindings);
|
|
1006
1347
|
const secFindings = scanner.scanContent(content, filePath);
|
|
1007
|
-
findings.push(...(0,
|
|
1348
|
+
findings.push(...(0, import_core5.mapSecurityFindings)(secFindings, findings));
|
|
1008
1349
|
return {
|
|
1009
1350
|
file: (0, import_node_path.relative)(targetDir, filePath).replaceAll("\\", "/"),
|
|
1010
1351
|
findings,
|
|
1011
|
-
overallSeverity: (0,
|
|
1352
|
+
overallSeverity: (0, import_core5.computeOverallSeverity)(findings)
|
|
1012
1353
|
};
|
|
1013
1354
|
}
|
|
1014
1355
|
async function scanWorkspaceConfig(workspacePath) {
|
|
1015
|
-
const scanner = new
|
|
1356
|
+
const scanner = new import_core5.SecurityScanner((0, import_core5.parseSecurityConfig)({}));
|
|
1016
1357
|
const results = [];
|
|
1017
1358
|
for (const configFile of CONFIG_FILES) {
|
|
1018
1359
|
const result = scanSingleFile((0, import_node_path.join)(workspacePath, configFile), workspacePath, scanner);
|
|
1019
1360
|
if (result) results.push(result);
|
|
1020
1361
|
}
|
|
1021
|
-
return { exitCode: (0,
|
|
1362
|
+
return { exitCode: (0, import_core5.computeScanExitCode)(results), results };
|
|
1022
1363
|
}
|
|
1023
1364
|
|
|
1024
1365
|
// src/orchestrator.ts
|
|
1025
|
-
var import_core4 = require("@harness-engineering/core");
|
|
1026
1366
|
var Orchestrator = class extends import_node_events.EventEmitter {
|
|
1027
1367
|
state;
|
|
1028
1368
|
config;
|
|
@@ -1070,6 +1410,21 @@ var Orchestrator = class extends import_node_events.EventEmitter {
|
|
|
1070
1410
|
return new MockBackend();
|
|
1071
1411
|
} else if (this.config.agent.backend === "claude") {
|
|
1072
1412
|
return new ClaudeBackend(this.config.agent.command);
|
|
1413
|
+
} else if (this.config.agent.backend === "openai") {
|
|
1414
|
+
return new OpenAIBackend({
|
|
1415
|
+
...this.config.agent.model !== void 0 && { model: this.config.agent.model },
|
|
1416
|
+
...this.config.agent.apiKey !== void 0 && { apiKey: this.config.agent.apiKey }
|
|
1417
|
+
});
|
|
1418
|
+
} else if (this.config.agent.backend === "gemini") {
|
|
1419
|
+
return new GeminiBackend({
|
|
1420
|
+
...this.config.agent.model !== void 0 && { model: this.config.agent.model },
|
|
1421
|
+
...this.config.agent.apiKey !== void 0 && { apiKey: this.config.agent.apiKey }
|
|
1422
|
+
});
|
|
1423
|
+
} else if (this.config.agent.backend === "anthropic") {
|
|
1424
|
+
return new AnthropicBackend({
|
|
1425
|
+
...this.config.agent.model !== void 0 && { model: this.config.agent.model },
|
|
1426
|
+
...this.config.agent.apiKey !== void 0 && { apiKey: this.config.agent.apiKey }
|
|
1427
|
+
});
|
|
1073
1428
|
}
|
|
1074
1429
|
throw new Error(`Unsupported agent backend: ${this.config.agent.backend}`);
|
|
1075
1430
|
}
|
|
@@ -1171,7 +1526,7 @@ var Orchestrator = class extends import_node_events.EventEmitter {
|
|
|
1171
1526
|
...f.line !== void 0 ? { line: f.line } : {}
|
|
1172
1527
|
}))
|
|
1173
1528
|
);
|
|
1174
|
-
(0,
|
|
1529
|
+
(0, import_core6.writeTaint)(
|
|
1175
1530
|
workspacePath,
|
|
1176
1531
|
issue.id,
|
|
1177
1532
|
"Medium-severity injection patterns found in workspace config files",
|
|
@@ -1368,12 +1723,14 @@ var AgentsTable = ({ agents }) => {
|
|
|
1368
1723
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { bold: true, underline: true, children: "Active Agents" }),
|
|
1369
1724
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Box, { flexDirection: "row", borderStyle: "single", borderColor: "gray", children: [
|
|
1370
1725
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { width: 20, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { bold: true, children: "Identifier" }) }),
|
|
1726
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { width: 12, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { bold: true, children: "Backend" }) }),
|
|
1371
1727
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { width: 20, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { bold: true, children: "Phase" }) }),
|
|
1372
1728
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { width: 10, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { bold: true, children: "Tokens" }) }),
|
|
1373
1729
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { bold: true, children: "Message" }) })
|
|
1374
1730
|
] }),
|
|
1375
1731
|
agents.map((agent) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Box, { flexDirection: "row", children: [
|
|
1376
1732
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { width: 20, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { children: agent.identifier }) }),
|
|
1733
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { width: 12, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "blue", children: agent.session?.backendName || "-" }) }),
|
|
1377
1734
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { width: 20, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "cyan", children: agent.phase }) }),
|
|
1378
1735
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { width: 10, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "yellow", children: agent.session?.totalTokens || 0 }) }),
|
|
1379
1736
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Box, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { wrap: "truncate-end", children: agent.session?.lastMessage || "-" }) })
|
|
@@ -1425,9 +1782,12 @@ function launchTUI(orchestrator) {
|
|
|
1425
1782
|
}
|
|
1426
1783
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1427
1784
|
0 && (module.exports = {
|
|
1785
|
+
AnthropicBackend,
|
|
1428
1786
|
ClaudeBackend,
|
|
1787
|
+
GeminiBackend,
|
|
1429
1788
|
LinearGraphQLStub,
|
|
1430
1789
|
MockBackend,
|
|
1790
|
+
OpenAIBackend,
|
|
1431
1791
|
Orchestrator,
|
|
1432
1792
|
PromptRenderer,
|
|
1433
1793
|
RoadmapTrackerAdapter,
|
package/dist/index.mjs
CHANGED
|
@@ -11,15 +11,20 @@ function calculateRetryDelay(attempt, type, maxRetryBackoffMs = DEFAULT_MAX_RETR
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// src/core/candidate-selection.ts
|
|
14
|
+
function comparePriority(a, b) {
|
|
15
|
+
const pa = a.priority ?? Number.MAX_SAFE_INTEGER;
|
|
16
|
+
const pb = b.priority ?? Number.MAX_SAFE_INTEGER;
|
|
17
|
+
return pa !== pb ? pa - pb : null;
|
|
18
|
+
}
|
|
19
|
+
function compareCreatedAt(a, b) {
|
|
20
|
+
const ca = a.createdAt ?? "\uFFFF";
|
|
21
|
+
const cb = b.createdAt ?? "\uFFFF";
|
|
22
|
+
if (ca === cb) return null;
|
|
23
|
+
return ca < cb ? -1 : 1;
|
|
24
|
+
}
|
|
14
25
|
function sortCandidates(issues) {
|
|
15
26
|
return [...issues].sort((a, b) => {
|
|
16
|
-
|
|
17
|
-
const pb = b.priority ?? Number.MAX_SAFE_INTEGER;
|
|
18
|
-
if (pa !== pb) return pa - pb;
|
|
19
|
-
const ca = a.createdAt ?? "\uFFFF";
|
|
20
|
-
const cb = b.createdAt ?? "\uFFFF";
|
|
21
|
-
if (ca !== cb) return ca < cb ? -1 : 1;
|
|
22
|
-
return a.identifier.localeCompare(b.identifier);
|
|
27
|
+
return comparePriority(a, b) ?? compareCreatedAt(a, b) ?? a.identifier.localeCompare(b.identifier);
|
|
23
28
|
});
|
|
24
29
|
}
|
|
25
30
|
function isEligible(issue, state, activeStates, terminalStates) {
|
|
@@ -711,6 +716,21 @@ import {
|
|
|
711
716
|
Ok as Ok8,
|
|
712
717
|
Err as Err6
|
|
713
718
|
} from "@harness-engineering/types";
|
|
719
|
+
function resolveExitCode(code, command, resolve) {
|
|
720
|
+
if (code === 0) {
|
|
721
|
+
resolve(Ok8(void 0));
|
|
722
|
+
} else {
|
|
723
|
+
resolve(
|
|
724
|
+
Err6({
|
|
725
|
+
category: "agent_not_found",
|
|
726
|
+
message: `Claude command '${command}' not found or failed`
|
|
727
|
+
})
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function resolveSpawnError(command, resolve) {
|
|
732
|
+
resolve(Err6({ category: "agent_not_found", message: `Claude command '${command}' not found` }));
|
|
733
|
+
}
|
|
714
734
|
var ClaudeBackend = class {
|
|
715
735
|
name = "claude";
|
|
716
736
|
command;
|
|
@@ -769,27 +789,347 @@ var ClaudeBackend = class {
|
|
|
769
789
|
async healthCheck() {
|
|
770
790
|
return new Promise((resolve) => {
|
|
771
791
|
const child = spawn2(this.command, ["--version"]);
|
|
772
|
-
child.on("exit", (code) =>
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
792
|
+
child.on("exit", (code) => resolveExitCode(code, this.command, resolve));
|
|
793
|
+
child.on("error", () => resolveSpawnError(this.command, resolve));
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
// src/agent/backends/openai.ts
|
|
799
|
+
import OpenAI from "openai";
|
|
800
|
+
import {
|
|
801
|
+
Ok as Ok9,
|
|
802
|
+
Err as Err7
|
|
803
|
+
} from "@harness-engineering/types";
|
|
804
|
+
import { OpenAICacheAdapter } from "@harness-engineering/core";
|
|
805
|
+
var OpenAIBackend = class {
|
|
806
|
+
name = "openai";
|
|
807
|
+
config;
|
|
808
|
+
client;
|
|
809
|
+
cacheAdapter;
|
|
810
|
+
constructor(config = {}) {
|
|
811
|
+
this.config = {
|
|
812
|
+
model: config.model ?? "gpt-4o",
|
|
813
|
+
apiKey: config.apiKey ?? process.env.OPENAI_API_KEY ?? ""
|
|
814
|
+
};
|
|
815
|
+
this.client = new OpenAI({ apiKey: this.config.apiKey });
|
|
816
|
+
this.cacheAdapter = new OpenAICacheAdapter();
|
|
817
|
+
}
|
|
818
|
+
async startSession(params) {
|
|
819
|
+
if (!this.config.apiKey) {
|
|
820
|
+
return Err7({
|
|
821
|
+
category: "agent_not_found",
|
|
822
|
+
message: "OPENAI_API_KEY is not set"
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
const session = {
|
|
826
|
+
sessionId: `openai-session-${Date.now()}`,
|
|
827
|
+
workspacePath: params.workspacePath,
|
|
828
|
+
backendName: this.name,
|
|
829
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
830
|
+
...params.systemPrompt !== void 0 && { systemPrompt: params.systemPrompt }
|
|
831
|
+
};
|
|
832
|
+
return Ok9(session);
|
|
833
|
+
}
|
|
834
|
+
async *runTurn(session, params) {
|
|
835
|
+
const openAISession = session;
|
|
836
|
+
const messages = [];
|
|
837
|
+
if (openAISession.systemPrompt) {
|
|
838
|
+
messages.push({ role: "system", content: openAISession.systemPrompt });
|
|
839
|
+
}
|
|
840
|
+
messages.push({ role: "user", content: params.prompt });
|
|
841
|
+
let inputTokens = 0;
|
|
842
|
+
let outputTokens = 0;
|
|
843
|
+
let totalTokens = 0;
|
|
844
|
+
let cacheCreationTokens = 0;
|
|
845
|
+
let cacheReadTokens = 0;
|
|
846
|
+
try {
|
|
847
|
+
const stream = await this.client.chat.completions.create({
|
|
848
|
+
model: this.config.model,
|
|
849
|
+
messages,
|
|
850
|
+
stream: true,
|
|
851
|
+
stream_options: { include_usage: true }
|
|
852
|
+
});
|
|
853
|
+
for await (const chunk of stream) {
|
|
854
|
+
const delta = chunk.choices[0]?.delta;
|
|
855
|
+
if (delta?.content) {
|
|
856
|
+
const event = {
|
|
857
|
+
type: "text",
|
|
858
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
859
|
+
content: delta.content,
|
|
860
|
+
sessionId: session.sessionId
|
|
861
|
+
};
|
|
862
|
+
yield event;
|
|
782
863
|
}
|
|
864
|
+
if (chunk.usage) {
|
|
865
|
+
inputTokens = chunk.usage.prompt_tokens ?? 0;
|
|
866
|
+
outputTokens = chunk.usage.completion_tokens ?? 0;
|
|
867
|
+
totalTokens = chunk.usage.total_tokens ?? 0;
|
|
868
|
+
const cacheUsage = this.cacheAdapter.parseCacheUsage(chunk);
|
|
869
|
+
cacheCreationTokens = cacheUsage.cacheCreationTokens;
|
|
870
|
+
cacheReadTokens = cacheUsage.cacheReadTokens;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
} catch (err) {
|
|
874
|
+
const errorMessage = err instanceof Error ? err.message : "OpenAI request failed";
|
|
875
|
+
yield {
|
|
876
|
+
type: "error",
|
|
877
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
878
|
+
content: errorMessage,
|
|
879
|
+
sessionId: session.sessionId
|
|
880
|
+
};
|
|
881
|
+
return {
|
|
882
|
+
success: false,
|
|
883
|
+
sessionId: session.sessionId,
|
|
884
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
885
|
+
error: errorMessage
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
success: true,
|
|
890
|
+
sessionId: session.sessionId,
|
|
891
|
+
usage: {
|
|
892
|
+
inputTokens,
|
|
893
|
+
outputTokens,
|
|
894
|
+
totalTokens,
|
|
895
|
+
cacheCreationTokens,
|
|
896
|
+
cacheReadTokens
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
async stopSession(_session) {
|
|
901
|
+
return Ok9(void 0);
|
|
902
|
+
}
|
|
903
|
+
async healthCheck() {
|
|
904
|
+
try {
|
|
905
|
+
await this.client.models.list();
|
|
906
|
+
return Ok9(void 0);
|
|
907
|
+
} catch (err) {
|
|
908
|
+
return Err7({
|
|
909
|
+
category: "response_error",
|
|
910
|
+
message: err instanceof Error ? err.message : "OpenAI health check failed"
|
|
783
911
|
});
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
// src/agent/backends/gemini.ts
|
|
917
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
918
|
+
import {
|
|
919
|
+
Ok as Ok10,
|
|
920
|
+
Err as Err8
|
|
921
|
+
} from "@harness-engineering/types";
|
|
922
|
+
import { GeminiCacheAdapter } from "@harness-engineering/core";
|
|
923
|
+
var GeminiBackend = class {
|
|
924
|
+
name = "gemini";
|
|
925
|
+
config;
|
|
926
|
+
cacheAdapter;
|
|
927
|
+
constructor(config = {}) {
|
|
928
|
+
this.config = {
|
|
929
|
+
model: config.model ?? "gemini-2.0-flash",
|
|
930
|
+
apiKey: config.apiKey ?? process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY ?? ""
|
|
931
|
+
};
|
|
932
|
+
this.cacheAdapter = new GeminiCacheAdapter();
|
|
933
|
+
}
|
|
934
|
+
async startSession(params) {
|
|
935
|
+
if (!this.config.apiKey) {
|
|
936
|
+
return Err8({
|
|
937
|
+
category: "agent_not_found",
|
|
938
|
+
message: "GEMINI_API_KEY is not set"
|
|
791
939
|
});
|
|
792
|
-
}
|
|
940
|
+
}
|
|
941
|
+
const session = {
|
|
942
|
+
sessionId: `gemini-session-${Date.now()}`,
|
|
943
|
+
workspacePath: params.workspacePath,
|
|
944
|
+
backendName: this.name,
|
|
945
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
946
|
+
...params.systemPrompt !== void 0 && { systemPrompt: params.systemPrompt }
|
|
947
|
+
};
|
|
948
|
+
return Ok10(session);
|
|
949
|
+
}
|
|
950
|
+
async *runTurn(session, params) {
|
|
951
|
+
const geminiSession = session;
|
|
952
|
+
let inputTokens = 0;
|
|
953
|
+
let outputTokens = 0;
|
|
954
|
+
let totalTokens = 0;
|
|
955
|
+
let cacheCreationTokens = 0;
|
|
956
|
+
let cacheReadTokens = 0;
|
|
957
|
+
try {
|
|
958
|
+
const genAI = new GoogleGenerativeAI(this.config.apiKey);
|
|
959
|
+
const model = genAI.getGenerativeModel({
|
|
960
|
+
model: this.config.model,
|
|
961
|
+
...geminiSession.systemPrompt !== void 0 && {
|
|
962
|
+
systemInstruction: geminiSession.systemPrompt
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
const result = await model.generateContentStream(params.prompt);
|
|
966
|
+
for await (const chunk of result.stream) {
|
|
967
|
+
const text = chunk.text();
|
|
968
|
+
if (text) {
|
|
969
|
+
const event = {
|
|
970
|
+
type: "text",
|
|
971
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
972
|
+
content: text,
|
|
973
|
+
sessionId: session.sessionId
|
|
974
|
+
};
|
|
975
|
+
yield event;
|
|
976
|
+
}
|
|
977
|
+
if (chunk.usageMetadata) {
|
|
978
|
+
inputTokens = chunk.usageMetadata.promptTokenCount ?? 0;
|
|
979
|
+
outputTokens = chunk.usageMetadata.candidatesTokenCount ?? 0;
|
|
980
|
+
totalTokens = chunk.usageMetadata.totalTokenCount ?? 0;
|
|
981
|
+
const cacheUsage = this.cacheAdapter.parseCacheUsage(chunk);
|
|
982
|
+
cacheCreationTokens = cacheUsage.cacheCreationTokens;
|
|
983
|
+
cacheReadTokens = cacheUsage.cacheReadTokens;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
} catch (err) {
|
|
987
|
+
const errorMessage = err instanceof Error ? err.message : "Gemini request failed";
|
|
988
|
+
yield {
|
|
989
|
+
type: "error",
|
|
990
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
991
|
+
content: errorMessage,
|
|
992
|
+
sessionId: session.sessionId
|
|
993
|
+
};
|
|
994
|
+
return {
|
|
995
|
+
success: false,
|
|
996
|
+
sessionId: session.sessionId,
|
|
997
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
998
|
+
error: errorMessage
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
return {
|
|
1002
|
+
success: true,
|
|
1003
|
+
sessionId: session.sessionId,
|
|
1004
|
+
usage: {
|
|
1005
|
+
inputTokens,
|
|
1006
|
+
outputTokens,
|
|
1007
|
+
totalTokens,
|
|
1008
|
+
cacheCreationTokens,
|
|
1009
|
+
cacheReadTokens
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
async stopSession(_session) {
|
|
1014
|
+
return Ok10(void 0);
|
|
1015
|
+
}
|
|
1016
|
+
async healthCheck() {
|
|
1017
|
+
try {
|
|
1018
|
+
const genAI = new GoogleGenerativeAI(this.config.apiKey);
|
|
1019
|
+
genAI.getGenerativeModel({ model: this.config.model });
|
|
1020
|
+
return Ok10(void 0);
|
|
1021
|
+
} catch (err) {
|
|
1022
|
+
return Err8({
|
|
1023
|
+
category: "response_error",
|
|
1024
|
+
message: err instanceof Error ? err.message : "Gemini health check failed"
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
// src/agent/backends/anthropic.ts
|
|
1031
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
1032
|
+
import {
|
|
1033
|
+
Ok as Ok11,
|
|
1034
|
+
Err as Err9
|
|
1035
|
+
} from "@harness-engineering/types";
|
|
1036
|
+
import { AnthropicCacheAdapter } from "@harness-engineering/core";
|
|
1037
|
+
var AnthropicBackend = class {
|
|
1038
|
+
name = "anthropic";
|
|
1039
|
+
config;
|
|
1040
|
+
client;
|
|
1041
|
+
cacheAdapter;
|
|
1042
|
+
constructor(config = {}) {
|
|
1043
|
+
this.config = {
|
|
1044
|
+
model: config.model ?? "claude-sonnet-4-20250514",
|
|
1045
|
+
apiKey: config.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "",
|
|
1046
|
+
maxTokens: config.maxTokens ?? 4096
|
|
1047
|
+
};
|
|
1048
|
+
this.client = new Anthropic({ apiKey: this.config.apiKey });
|
|
1049
|
+
this.cacheAdapter = new AnthropicCacheAdapter();
|
|
1050
|
+
}
|
|
1051
|
+
async startSession(params) {
|
|
1052
|
+
if (!this.config.apiKey) {
|
|
1053
|
+
return Err9({
|
|
1054
|
+
category: "agent_not_found",
|
|
1055
|
+
message: "ANTHROPIC_API_KEY is not set"
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
const session = {
|
|
1059
|
+
sessionId: `anthropic-session-${Date.now()}`,
|
|
1060
|
+
workspacePath: params.workspacePath,
|
|
1061
|
+
backendName: this.name,
|
|
1062
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1063
|
+
...params.systemPrompt !== void 0 && { systemPrompt: params.systemPrompt }
|
|
1064
|
+
};
|
|
1065
|
+
return Ok11(session);
|
|
1066
|
+
}
|
|
1067
|
+
async *runTurn(session, params) {
|
|
1068
|
+
const anthropicSession = session;
|
|
1069
|
+
const systemBlocks = anthropicSession.systemPrompt ? [
|
|
1070
|
+
this.cacheAdapter.wrapSystemBlock(
|
|
1071
|
+
anthropicSession.systemPrompt,
|
|
1072
|
+
"session"
|
|
1073
|
+
)
|
|
1074
|
+
] : void 0;
|
|
1075
|
+
try {
|
|
1076
|
+
const stream = this.client.messages.stream({
|
|
1077
|
+
model: this.config.model,
|
|
1078
|
+
max_tokens: this.config.maxTokens,
|
|
1079
|
+
...systemBlocks && { system: systemBlocks },
|
|
1080
|
+
messages: [{ role: "user", content: params.prompt }]
|
|
1081
|
+
});
|
|
1082
|
+
for await (const event of stream) {
|
|
1083
|
+
if (event.type === "content_block_delta" && "text" in event.delta) {
|
|
1084
|
+
yield {
|
|
1085
|
+
type: "text",
|
|
1086
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1087
|
+
content: event.delta.text,
|
|
1088
|
+
sessionId: session.sessionId
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
const finalMessage = await stream.finalMessage();
|
|
1093
|
+
const { input_tokens, output_tokens } = finalMessage.usage;
|
|
1094
|
+
const cacheUsage = this.cacheAdapter.parseCacheUsage(finalMessage);
|
|
1095
|
+
return {
|
|
1096
|
+
success: true,
|
|
1097
|
+
sessionId: session.sessionId,
|
|
1098
|
+
usage: {
|
|
1099
|
+
inputTokens: input_tokens,
|
|
1100
|
+
outputTokens: output_tokens,
|
|
1101
|
+
totalTokens: input_tokens + output_tokens,
|
|
1102
|
+
cacheCreationTokens: cacheUsage.cacheCreationTokens,
|
|
1103
|
+
cacheReadTokens: cacheUsage.cacheReadTokens
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
} catch (err) {
|
|
1107
|
+
const errorMessage = err instanceof Error ? err.message : "Anthropic request failed";
|
|
1108
|
+
yield {
|
|
1109
|
+
type: "error",
|
|
1110
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1111
|
+
content: errorMessage,
|
|
1112
|
+
sessionId: session.sessionId
|
|
1113
|
+
};
|
|
1114
|
+
return {
|
|
1115
|
+
success: false,
|
|
1116
|
+
sessionId: session.sessionId,
|
|
1117
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
1118
|
+
error: errorMessage
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
async stopSession(_session) {
|
|
1123
|
+
return Ok11(void 0);
|
|
1124
|
+
}
|
|
1125
|
+
async healthCheck() {
|
|
1126
|
+
if (!this.config.apiKey) {
|
|
1127
|
+
return Err9({
|
|
1128
|
+
category: "response_error",
|
|
1129
|
+
message: "ANTHROPIC_API_KEY is not set"
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
return Ok11(void 0);
|
|
793
1133
|
}
|
|
794
1134
|
};
|
|
795
1135
|
|
|
@@ -817,6 +1157,7 @@ var PromptRenderer = class {
|
|
|
817
1157
|
|
|
818
1158
|
// src/orchestrator.ts
|
|
819
1159
|
import { EventEmitter } from "events";
|
|
1160
|
+
import { writeTaint } from "@harness-engineering/core";
|
|
820
1161
|
|
|
821
1162
|
// src/agent/runner.ts
|
|
822
1163
|
var AgentRunner = class {
|
|
@@ -843,7 +1184,13 @@ var AgentRunner = class {
|
|
|
843
1184
|
let lastResult = {
|
|
844
1185
|
success: false,
|
|
845
1186
|
sessionId: session.sessionId,
|
|
846
|
-
usage: {
|
|
1187
|
+
usage: {
|
|
1188
|
+
inputTokens: 0,
|
|
1189
|
+
outputTokens: 0,
|
|
1190
|
+
totalTokens: 0,
|
|
1191
|
+
cacheCreationTokens: 0,
|
|
1192
|
+
cacheReadTokens: 0
|
|
1193
|
+
}
|
|
847
1194
|
};
|
|
848
1195
|
try {
|
|
849
1196
|
while (currentTurn < this.options.maxTurns) {
|
|
@@ -981,7 +1328,6 @@ async function scanWorkspaceConfig(workspacePath) {
|
|
|
981
1328
|
}
|
|
982
1329
|
|
|
983
1330
|
// src/orchestrator.ts
|
|
984
|
-
import { writeTaint } from "@harness-engineering/core";
|
|
985
1331
|
var Orchestrator = class extends EventEmitter {
|
|
986
1332
|
state;
|
|
987
1333
|
config;
|
|
@@ -1029,6 +1375,21 @@ var Orchestrator = class extends EventEmitter {
|
|
|
1029
1375
|
return new MockBackend();
|
|
1030
1376
|
} else if (this.config.agent.backend === "claude") {
|
|
1031
1377
|
return new ClaudeBackend(this.config.agent.command);
|
|
1378
|
+
} else if (this.config.agent.backend === "openai") {
|
|
1379
|
+
return new OpenAIBackend({
|
|
1380
|
+
...this.config.agent.model !== void 0 && { model: this.config.agent.model },
|
|
1381
|
+
...this.config.agent.apiKey !== void 0 && { apiKey: this.config.agent.apiKey }
|
|
1382
|
+
});
|
|
1383
|
+
} else if (this.config.agent.backend === "gemini") {
|
|
1384
|
+
return new GeminiBackend({
|
|
1385
|
+
...this.config.agent.model !== void 0 && { model: this.config.agent.model },
|
|
1386
|
+
...this.config.agent.apiKey !== void 0 && { apiKey: this.config.agent.apiKey }
|
|
1387
|
+
});
|
|
1388
|
+
} else if (this.config.agent.backend === "anthropic") {
|
|
1389
|
+
return new AnthropicBackend({
|
|
1390
|
+
...this.config.agent.model !== void 0 && { model: this.config.agent.model },
|
|
1391
|
+
...this.config.agent.apiKey !== void 0 && { apiKey: this.config.agent.apiKey }
|
|
1392
|
+
});
|
|
1032
1393
|
}
|
|
1033
1394
|
throw new Error(`Unsupported agent backend: ${this.config.agent.backend}`);
|
|
1034
1395
|
}
|
|
@@ -1327,12 +1688,14 @@ var AgentsTable = ({ agents }) => {
|
|
|
1327
1688
|
/* @__PURE__ */ jsx3(Text3, { bold: true, underline: true, children: "Active Agents" }),
|
|
1328
1689
|
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", borderStyle: "single", borderColor: "gray", children: [
|
|
1329
1690
|
/* @__PURE__ */ jsx3(Box3, { width: 20, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Identifier" }) }),
|
|
1691
|
+
/* @__PURE__ */ jsx3(Box3, { width: 12, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Backend" }) }),
|
|
1330
1692
|
/* @__PURE__ */ jsx3(Box3, { width: 20, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Phase" }) }),
|
|
1331
1693
|
/* @__PURE__ */ jsx3(Box3, { width: 10, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Tokens" }) }),
|
|
1332
1694
|
/* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Message" }) })
|
|
1333
1695
|
] }),
|
|
1334
1696
|
agents.map((agent) => /* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", children: [
|
|
1335
1697
|
/* @__PURE__ */ jsx3(Box3, { width: 20, children: /* @__PURE__ */ jsx3(Text3, { children: agent.identifier }) }),
|
|
1698
|
+
/* @__PURE__ */ jsx3(Box3, { width: 12, children: /* @__PURE__ */ jsx3(Text3, { color: "blue", children: agent.session?.backendName || "-" }) }),
|
|
1336
1699
|
/* @__PURE__ */ jsx3(Box3, { width: 20, children: /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: agent.phase }) }),
|
|
1337
1700
|
/* @__PURE__ */ jsx3(Box3, { width: 10, children: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: agent.session?.totalTokens || 0 }) }),
|
|
1338
1701
|
/* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: /* @__PURE__ */ jsx3(Text3, { wrap: "truncate-end", children: agent.session?.lastMessage || "-" }) })
|
|
@@ -1383,9 +1746,12 @@ function launchTUI(orchestrator) {
|
|
|
1383
1746
|
return { waitUntilExit };
|
|
1384
1747
|
}
|
|
1385
1748
|
export {
|
|
1749
|
+
AnthropicBackend,
|
|
1386
1750
|
ClaudeBackend,
|
|
1751
|
+
GeminiBackend,
|
|
1387
1752
|
LinearGraphQLStub,
|
|
1388
1753
|
MockBackend,
|
|
1754
|
+
OpenAIBackend,
|
|
1389
1755
|
Orchestrator,
|
|
1390
1756
|
PromptRenderer,
|
|
1391
1757
|
RoadmapTrackerAdapter,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harness-engineering/orchestrator",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Orchestrator daemon for dispatching coding agents to issues",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -36,13 +36,15 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://github.com/Intense-Visions/harness-engineering/tree/main/packages/orchestrator#readme",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"
|
|
39
|
+
"@anthropic-ai/sdk": "^0.87.0",
|
|
40
|
+
"@google/generative-ai": "^0.24.0",
|
|
40
41
|
"ink": "^4.4.1",
|
|
41
|
-
"liquidjs": "^10.25.
|
|
42
|
+
"liquidjs": "^10.25.3",
|
|
43
|
+
"openai": "^4.0.0",
|
|
42
44
|
"react": "^18.3.1",
|
|
43
45
|
"yaml": "^2.8.3",
|
|
44
|
-
"@harness-engineering/core": "0.
|
|
45
|
-
"@harness-engineering/types": "0.
|
|
46
|
+
"@harness-engineering/core": "0.21.4",
|
|
47
|
+
"@harness-engineering/types": "0.9.2"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|
|
48
50
|
"@types/node": "^22.19.15",
|