@harness-engineering/orchestrator 0.7.0 → 0.8.1

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.d.mts CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as _harness_engineering_types from '@harness-engineering/types';
2
- import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, IssueRoutingDecision, Result, WorkflowDefinition, BackendDef, RoutingConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, CheckScriptDefinition, OutputRetentionConfig, RoutingDecision, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig, CustomTaskDefinition, TokenScope, AuthToken, AuthTokenPublic, IndexedFileKind, SessionSearchResult, ReindexStats, SessionSummarizationConfig, SessionSummary, SessionSummaryMeta, SessionsConfig, GatewayEvent, NotificationEnvelope, NotificationDeliveryResult, NotificationSinkConfig, NotificationsConfig } from '@harness-engineering/types';
2
+ import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, IssueRoutingDecision, Result, WorkflowDefinition, BackendDef, RoutingConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, CheckScriptDefinition, OutputRetentionConfig, RoutingDecision, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig, LocalModelStatus, CustomTaskDefinition, TokenScope, AuthToken, AuthTokenPublic, IndexedFileKind, SessionSearchResult, ReindexStats, SessionSummarizationConfig, SessionSummary, SessionSummaryMeta, SessionsConfig, GatewayEvent, NotificationEnvelope, NotificationDeliveryResult, NotificationSinkConfig, NotificationsConfig } from '@harness-engineering/types';
3
3
  import { IssueTrackerClient, Issue as Issue$1, TrackerConfig, CacheMetricsRecorder, ArchiveHooks, SkillProposal, ProposalGateFinding } from '@harness-engineering/core';
4
4
  import { EnrichedSpec, ComplexityScore, SimulationResult, IntelligencePipeline, WeightedRecommendation, AnalysisProvider } from '@harness-engineering/intelligence';
5
5
  import { GraphStore } from '@harness-engineering/graph';
6
6
  import { execFile } from 'node:child_process';
7
7
  import { EventEmitter } from 'node:events';
8
+ import { z } from 'zod';
8
9
 
9
10
  /**
10
11
  * Run attempt lifecycle phases (internal to orchestrator).
@@ -1921,6 +1922,270 @@ interface CreateBackendOptions {
1921
1922
  */
1922
1923
  declare function createBackend(def: BackendDef, options?: CreateBackendOptions): AgentBackend;
1923
1924
 
1925
+ /**
1926
+ * Zod schema for `BackendDef` (Spec 2 — multi-backend routing).
1927
+ *
1928
+ * Discriminated union on `type`. Per-variant validation surfaces shape
1929
+ * mismatches (missing `model`, missing `endpoint`, etc.) at config-load
1930
+ * time rather than at orchestrator runtime.
1931
+ *
1932
+ * Used in Phase 3 by `validateWorkflowConfig`; in Phase 1 it is exported
1933
+ * for standalone unit testing.
1934
+ */
1935
+ declare const BackendDefSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
1936
+ type: z.ZodLiteral<"mock">;
1937
+ }, "strict", z.ZodTypeAny, {
1938
+ type: "mock";
1939
+ }, {
1940
+ type: "mock";
1941
+ }>, z.ZodObject<{
1942
+ type: z.ZodLiteral<"claude">;
1943
+ command: z.ZodOptional<z.ZodString>;
1944
+ }, "strict", z.ZodTypeAny, {
1945
+ type: "claude";
1946
+ command?: string | undefined;
1947
+ }, {
1948
+ type: "claude";
1949
+ command?: string | undefined;
1950
+ }>, z.ZodObject<{
1951
+ type: z.ZodLiteral<"anthropic">;
1952
+ model: z.ZodString;
1953
+ apiKey: z.ZodOptional<z.ZodString>;
1954
+ }, "strict", z.ZodTypeAny, {
1955
+ type: "anthropic";
1956
+ model: string;
1957
+ apiKey?: string | undefined;
1958
+ }, {
1959
+ type: "anthropic";
1960
+ model: string;
1961
+ apiKey?: string | undefined;
1962
+ }>, z.ZodObject<{
1963
+ type: z.ZodLiteral<"openai">;
1964
+ model: z.ZodString;
1965
+ apiKey: z.ZodOptional<z.ZodString>;
1966
+ }, "strict", z.ZodTypeAny, {
1967
+ type: "openai";
1968
+ model: string;
1969
+ apiKey?: string | undefined;
1970
+ }, {
1971
+ type: "openai";
1972
+ model: string;
1973
+ apiKey?: string | undefined;
1974
+ }>, z.ZodObject<{
1975
+ type: z.ZodLiteral<"gemini">;
1976
+ model: z.ZodString;
1977
+ apiKey: z.ZodOptional<z.ZodString>;
1978
+ }, "strict", z.ZodTypeAny, {
1979
+ type: "gemini";
1980
+ model: string;
1981
+ apiKey?: string | undefined;
1982
+ }, {
1983
+ type: "gemini";
1984
+ model: string;
1985
+ apiKey?: string | undefined;
1986
+ }>, z.ZodObject<{
1987
+ type: z.ZodLiteral<"local">;
1988
+ endpoint: z.ZodString;
1989
+ model: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "atleastone">]>;
1990
+ apiKey: z.ZodOptional<z.ZodString>;
1991
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
1992
+ probeIntervalMs: z.ZodOptional<z.ZodNumber>;
1993
+ }, "strict", z.ZodTypeAny, {
1994
+ type: "local";
1995
+ model: string | [string, ...string[]];
1996
+ endpoint: string;
1997
+ apiKey?: string | undefined;
1998
+ timeoutMs?: number | undefined;
1999
+ probeIntervalMs?: number | undefined;
2000
+ }, {
2001
+ type: "local";
2002
+ model: string | [string, ...string[]];
2003
+ endpoint: string;
2004
+ apiKey?: string | undefined;
2005
+ timeoutMs?: number | undefined;
2006
+ probeIntervalMs?: number | undefined;
2007
+ }>, z.ZodObject<{
2008
+ type: z.ZodLiteral<"pi">;
2009
+ endpoint: z.ZodString;
2010
+ model: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "atleastone">]>;
2011
+ apiKey: z.ZodOptional<z.ZodString>;
2012
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
2013
+ probeIntervalMs: z.ZodOptional<z.ZodNumber>;
2014
+ }, "strict", z.ZodTypeAny, {
2015
+ type: "pi";
2016
+ model: string | [string, ...string[]];
2017
+ endpoint: string;
2018
+ apiKey?: string | undefined;
2019
+ timeoutMs?: number | undefined;
2020
+ probeIntervalMs?: number | undefined;
2021
+ }, {
2022
+ type: "pi";
2023
+ model: string | [string, ...string[]];
2024
+ endpoint: string;
2025
+ apiKey?: string | undefined;
2026
+ timeoutMs?: number | undefined;
2027
+ probeIntervalMs?: number | undefined;
2028
+ }>]>;
2029
+ /**
2030
+ * Spec B Phase 0: a routing target is either a backend name (scalar
2031
+ * string) or a non-empty ordered fallback chain (string tuple). The
2032
+ * scalar form is byte-compatible with pre-Spec-B configs.
2033
+ */
2034
+ declare const RoutingValueSchema: z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>;
2035
+ /**
2036
+ * Zod schema for `RoutingConfig`. `.strict()` rejects unknown keys at
2037
+ * every level (per Spec 2 D7: typos in routing keys are validation
2038
+ * errors, not silent default-fallthroughs).
2039
+ *
2040
+ * Spec B Phase 0: all scalar routing fields accept `RoutingValueSchema`
2041
+ * (scalar or non-empty chain). New optional `skills` and `modes` maps
2042
+ * accept the same.
2043
+ *
2044
+ * Spec B Phase 2: the `isolation` block (added to the TS interface in
2045
+ * Hermes Phase 5 but not previously in this Zod schema) is now included
2046
+ * here with each tier widened to `RoutingValueSchema`. This closes the
2047
+ * Phase 0 I2 review finding (TS-vs-Zod drift) and ensures isolation
2048
+ * chain entries are validated by the same cross-field check that
2049
+ * covers `skills` / `modes`.
2050
+ */
2051
+ declare const RoutingConfigSchema: z.ZodObject<{
2052
+ default: z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>;
2053
+ 'quick-fix': z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2054
+ 'guided-change': z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2055
+ 'full-exploration': z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2056
+ diagnostic: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2057
+ intelligence: z.ZodOptional<z.ZodObject<{
2058
+ sel: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2059
+ pesl: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2060
+ }, "strict", z.ZodTypeAny, {
2061
+ sel?: string | readonly [string, ...string[]] | undefined;
2062
+ pesl?: string | readonly [string, ...string[]] | undefined;
2063
+ }, {
2064
+ sel?: string | readonly [string, ...string[]] | undefined;
2065
+ pesl?: string | readonly [string, ...string[]] | undefined;
2066
+ }>>;
2067
+ isolation: z.ZodOptional<z.ZodObject<{
2068
+ none: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2069
+ container: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2070
+ 'remote-sandbox': z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2071
+ }, "strict", z.ZodTypeAny, {
2072
+ none?: string | readonly [string, ...string[]] | undefined;
2073
+ container?: string | readonly [string, ...string[]] | undefined;
2074
+ 'remote-sandbox'?: string | readonly [string, ...string[]] | undefined;
2075
+ }, {
2076
+ none?: string | readonly [string, ...string[]] | undefined;
2077
+ container?: string | readonly [string, ...string[]] | undefined;
2078
+ 'remote-sandbox'?: string | readonly [string, ...string[]] | undefined;
2079
+ }>>;
2080
+ skills: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>>;
2081
+ modes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>>;
2082
+ }, "strict", z.ZodTypeAny, {
2083
+ default: string | readonly [string, ...string[]];
2084
+ 'quick-fix'?: string | readonly [string, ...string[]] | undefined;
2085
+ 'guided-change'?: string | readonly [string, ...string[]] | undefined;
2086
+ 'full-exploration'?: string | readonly [string, ...string[]] | undefined;
2087
+ diagnostic?: string | readonly [string, ...string[]] | undefined;
2088
+ intelligence?: {
2089
+ sel?: string | readonly [string, ...string[]] | undefined;
2090
+ pesl?: string | readonly [string, ...string[]] | undefined;
2091
+ } | undefined;
2092
+ isolation?: {
2093
+ none?: string | readonly [string, ...string[]] | undefined;
2094
+ container?: string | readonly [string, ...string[]] | undefined;
2095
+ 'remote-sandbox'?: string | readonly [string, ...string[]] | undefined;
2096
+ } | undefined;
2097
+ skills?: Record<string, string | readonly [string, ...string[]]> | undefined;
2098
+ modes?: Record<string, string | readonly [string, ...string[]]> | undefined;
2099
+ }, {
2100
+ default: string | readonly [string, ...string[]];
2101
+ 'quick-fix'?: string | readonly [string, ...string[]] | undefined;
2102
+ 'guided-change'?: string | readonly [string, ...string[]] | undefined;
2103
+ 'full-exploration'?: string | readonly [string, ...string[]] | undefined;
2104
+ diagnostic?: string | readonly [string, ...string[]] | undefined;
2105
+ intelligence?: {
2106
+ sel?: string | readonly [string, ...string[]] | undefined;
2107
+ pesl?: string | readonly [string, ...string[]] | undefined;
2108
+ } | undefined;
2109
+ isolation?: {
2110
+ none?: string | readonly [string, ...string[]] | undefined;
2111
+ container?: string | readonly [string, ...string[]] | undefined;
2112
+ 'remote-sandbox'?: string | readonly [string, ...string[]] | undefined;
2113
+ } | undefined;
2114
+ skills?: Record<string, string | readonly [string, ...string[]]> | undefined;
2115
+ modes?: Record<string, string | readonly [string, ...string[]]> | undefined;
2116
+ }>;
2117
+
2118
+ interface ResolverLogger {
2119
+ info(message: string, context?: Record<string, unknown>): void;
2120
+ warn(message: string, context?: Record<string, unknown>): void;
2121
+ }
2122
+ interface LocalModelResolverOptions {
2123
+ endpoint: string;
2124
+ apiKey?: string;
2125
+ /** Normalized candidate list (already turned from string|string[] into string[]). */
2126
+ configured: string[];
2127
+ /** Probe cadence in ms; default 30_000, minimum 1_000. */
2128
+ probeIntervalMs?: number;
2129
+ /**
2130
+ * Per-request timeout for the default fetch implementation, in ms.
2131
+ * Default: 5_000. Ignored when a custom `fetchModels` is provided
2132
+ * (custom impls own their own timeout policy). Spec §3.1 line 136
2133
+ * enumerates timeout as a supported failure mode.
2134
+ */
2135
+ timeoutMs?: number;
2136
+ /**
2137
+ * Injectable for tests. Default: GET `${endpoint}/models` with bearer apiKey.
2138
+ * Resolves to detected model IDs. Rejects on network/timeout/non-2xx/malformed.
2139
+ */
2140
+ fetchModels?: (endpoint: string, apiKey?: string) => Promise<string[]>;
2141
+ logger?: ResolverLogger;
2142
+ }
2143
+ declare function normalizeLocalModel(input: string | string[] | undefined): string[];
2144
+ /**
2145
+ * Default `fetchModels` — GET `${endpoint}/models` with bearer apiKey.
2146
+ * Throws on network failure, non-2xx, malformed body, or timeout.
2147
+ *
2148
+ * `timeoutMs` defaults to 5_000. A timeout aborts the in-flight request and
2149
+ * surfaces as `Error('request timeout (Nms)')` so callers can distinguish it
2150
+ * from generic network errors.
2151
+ */
2152
+ declare function defaultFetchModels(endpoint: string, apiKey?: string, timeoutMs?: number): Promise<string[]>;
2153
+ declare class LocalModelResolver {
2154
+ private readonly endpoint;
2155
+ private readonly apiKey?;
2156
+ private readonly configured;
2157
+ private readonly probeIntervalMs;
2158
+ private readonly fetchModels;
2159
+ private readonly logger;
2160
+ private timer;
2161
+ private listeners;
2162
+ /**
2163
+ * Tracks an in-flight probe so concurrent invocations (interval tick while a
2164
+ * slow probe is running, or a manual `probe()` call mid-flight) share the
2165
+ * existing promise instead of racing to mutate `detected/resolved/lastError/
2166
+ * warnings` non-atomically across `await` points. Applies to both the timer
2167
+ * callback and direct `probe()` calls — any caller that arrives during an
2168
+ * in-flight probe gets the same promise back. Cleared in `finally` so the
2169
+ * next tick can start a fresh probe.
2170
+ */
2171
+ private probeInFlight;
2172
+ private resolved;
2173
+ private detected;
2174
+ private lastProbeAt;
2175
+ private lastError;
2176
+ private warnings;
2177
+ private available;
2178
+ constructor(opts: LocalModelResolverOptions);
2179
+ resolveModel(): string | null;
2180
+ getStatus(): LocalModelStatus;
2181
+ onStatusChange(handler: (status: LocalModelStatus) => void): () => void;
2182
+ probe(): Promise<LocalModelStatus>;
2183
+ private runProbe;
2184
+ start(): Promise<void>;
2185
+ stop(): void;
2186
+ private snapshotForDiff;
2187
+ }
2188
+
1924
2189
  /**
1925
2190
  * Function signature compatible with Node's `child_process.execFile`.
1926
2191
  * Allows injection for testing.
@@ -2552,4 +2817,4 @@ declare function emitProposalCreated(bus: EventEmitter, proposal: SkillProposal)
2552
2817
  declare function emitProposalApproved(bus: EventEmitter, proposal: SkillProposal): void;
2553
2818
  declare function emitProposalRejected(bus: EventEmitter, proposal: SkillProposal): void;
2554
2819
 
2555
- export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BUILT_IN_TASKS, BackendRouter, type BackendRouterOptions, type BaseRefFallbackEvent, type BuildArchiveHooksOptions, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type CreateTokenInput, type CreateTokenResult, type CustomTaskValidationError, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn$1 as ExecFileFn, type FromConfigOptions, GateNotReadyError, type GateResult, GateRunError, type Highlight, type HighlightsInfo, type IndexedDoc, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, MAX_ATTEMPTS, type MigrationResult, MockBackend, type NotificationSink, type NotificationSinkDeliverInput, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, type PersistedOutputEntry, PromotionError, type PromotionResult, PromptRenderer, type ProposalApprovedData, type ProposalCreatedData, type ProposalRejectedData, type PublishedIndex, type QueueInsertInput, type QueueRow, type QueueStats, RETRY_DELAYS_MS, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type RegistryEntry, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunOrigin, type RunningEntry, type ScheduleRetryEffect, type SearchOptions, type SideEffect, SinkConfigError, SinkRegistry, type SkillCatalogEntry, SlackSink, type SlackSinkOptions, SqliteSearchIndex, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type SummarizeContext, type SummarizeResult, type SyncMainOptions, type SyncMainResult, type SyncSkipReason, type TaskDefinition, TaskOutputStore, type TaskType, type TickEvent, TokenStore, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type ValidateWorkflowConfigOptions, type ValidatedWorkflowConfig, WebhookQueue, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, type WorkspaceManagerOptions, applyEvent, artifactPresenceFromIssue, buildArchiveHooks, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, crossFieldRoutingIssues, detectScopeTier, discoverSkillCatalog, discoverSkillCatalogNames, emitProposalApproved, emitProposalCreated, emitProposalRejected, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, indexSessionDirectory, isEligible, isSummaryEnabled, launchTUI, loadPublishedIndex, migrateAgentConfig, normalizeFts5Query, openSearchIndex, promote, reconcile, reindexFromArchive, renderAnalysisComment, renderLlmSummaryMarkdown, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, routingWarnings, runGate, savePublishedIndex, searchIndexPath, selectCandidates, sortCandidates, summarizeArchivedSession, syncMain, triageIssue, truncateForBudget, validateCustomTasks, validateWorkflowConfig, wireNotificationSinks, wrapAsEnvelope };
2820
+ export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BUILT_IN_TASKS, BackendDefSchema, BackendRouter, type BackendRouterOptions, type BaseRefFallbackEvent, type BuildArchiveHooksOptions, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type CreateTokenInput, type CreateTokenResult, type CustomTaskValidationError, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn$1 as ExecFileFn, type FromConfigOptions, GateNotReadyError, type GateResult, GateRunError, type Highlight, type HighlightsInfo, type IndexedDoc, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, LocalModelResolver, type LocalModelResolverOptions, MAX_ATTEMPTS, type MigrationResult, MockBackend, type NotificationSink, type NotificationSinkDeliverInput, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, type PersistedOutputEntry, PromotionError, type PromotionResult, PromptRenderer, type ProposalApprovedData, type ProposalCreatedData, type ProposalRejectedData, type PublishedIndex, type QueueInsertInput, type QueueRow, type QueueStats, RETRY_DELAYS_MS, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type RegistryEntry, type ReleaseClaimEffect, type ResolverLogger, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, RoutingConfigSchema, RoutingValueSchema, type RunAttemptPhase, type RunOrigin, type RunningEntry, type ScheduleRetryEffect, type SearchOptions, type SideEffect, SinkConfigError, SinkRegistry, type SkillCatalogEntry, SlackSink, type SlackSinkOptions, SqliteSearchIndex, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type SummarizeContext, type SummarizeResult, type SyncMainOptions, type SyncMainResult, type SyncSkipReason, type TaskDefinition, TaskOutputStore, type TaskType, type TickEvent, TokenStore, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type ValidateWorkflowConfigOptions, type ValidatedWorkflowConfig, WebhookQueue, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, type WorkspaceManagerOptions, applyEvent, artifactPresenceFromIssue, buildArchiveHooks, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, crossFieldRoutingIssues, defaultFetchModels, detectScopeTier, discoverSkillCatalog, discoverSkillCatalogNames, emitProposalApproved, emitProposalCreated, emitProposalRejected, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, indexSessionDirectory, isEligible, isSummaryEnabled, launchTUI, loadPublishedIndex, migrateAgentConfig, normalizeFts5Query, normalizeLocalModel, openSearchIndex, promote, reconcile, reindexFromArchive, renderAnalysisComment, renderLlmSummaryMarkdown, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, routingWarnings, runGate, savePublishedIndex, searchIndexPath, selectCandidates, sortCandidates, summarizeArchivedSession, syncMain, triageIssue, truncateForBudget, validateCustomTasks, validateWorkflowConfig, wireNotificationSinks, wrapAsEnvelope };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as _harness_engineering_types from '@harness-engineering/types';
2
- import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, IssueRoutingDecision, Result, WorkflowDefinition, BackendDef, RoutingConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, CheckScriptDefinition, OutputRetentionConfig, RoutingDecision, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig, CustomTaskDefinition, TokenScope, AuthToken, AuthTokenPublic, IndexedFileKind, SessionSearchResult, ReindexStats, SessionSummarizationConfig, SessionSummary, SessionSummaryMeta, SessionsConfig, GatewayEvent, NotificationEnvelope, NotificationDeliveryResult, NotificationSinkConfig, NotificationsConfig } from '@harness-engineering/types';
2
+ import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, IssueRoutingDecision, Result, WorkflowDefinition, BackendDef, RoutingConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, CheckScriptDefinition, OutputRetentionConfig, RoutingDecision, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig, LocalModelStatus, CustomTaskDefinition, TokenScope, AuthToken, AuthTokenPublic, IndexedFileKind, SessionSearchResult, ReindexStats, SessionSummarizationConfig, SessionSummary, SessionSummaryMeta, SessionsConfig, GatewayEvent, NotificationEnvelope, NotificationDeliveryResult, NotificationSinkConfig, NotificationsConfig } from '@harness-engineering/types';
3
3
  import { IssueTrackerClient, Issue as Issue$1, TrackerConfig, CacheMetricsRecorder, ArchiveHooks, SkillProposal, ProposalGateFinding } from '@harness-engineering/core';
4
4
  import { EnrichedSpec, ComplexityScore, SimulationResult, IntelligencePipeline, WeightedRecommendation, AnalysisProvider } from '@harness-engineering/intelligence';
5
5
  import { GraphStore } from '@harness-engineering/graph';
6
6
  import { execFile } from 'node:child_process';
7
7
  import { EventEmitter } from 'node:events';
8
+ import { z } from 'zod';
8
9
 
9
10
  /**
10
11
  * Run attempt lifecycle phases (internal to orchestrator).
@@ -1921,6 +1922,270 @@ interface CreateBackendOptions {
1921
1922
  */
1922
1923
  declare function createBackend(def: BackendDef, options?: CreateBackendOptions): AgentBackend;
1923
1924
 
1925
+ /**
1926
+ * Zod schema for `BackendDef` (Spec 2 — multi-backend routing).
1927
+ *
1928
+ * Discriminated union on `type`. Per-variant validation surfaces shape
1929
+ * mismatches (missing `model`, missing `endpoint`, etc.) at config-load
1930
+ * time rather than at orchestrator runtime.
1931
+ *
1932
+ * Used in Phase 3 by `validateWorkflowConfig`; in Phase 1 it is exported
1933
+ * for standalone unit testing.
1934
+ */
1935
+ declare const BackendDefSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
1936
+ type: z.ZodLiteral<"mock">;
1937
+ }, "strict", z.ZodTypeAny, {
1938
+ type: "mock";
1939
+ }, {
1940
+ type: "mock";
1941
+ }>, z.ZodObject<{
1942
+ type: z.ZodLiteral<"claude">;
1943
+ command: z.ZodOptional<z.ZodString>;
1944
+ }, "strict", z.ZodTypeAny, {
1945
+ type: "claude";
1946
+ command?: string | undefined;
1947
+ }, {
1948
+ type: "claude";
1949
+ command?: string | undefined;
1950
+ }>, z.ZodObject<{
1951
+ type: z.ZodLiteral<"anthropic">;
1952
+ model: z.ZodString;
1953
+ apiKey: z.ZodOptional<z.ZodString>;
1954
+ }, "strict", z.ZodTypeAny, {
1955
+ type: "anthropic";
1956
+ model: string;
1957
+ apiKey?: string | undefined;
1958
+ }, {
1959
+ type: "anthropic";
1960
+ model: string;
1961
+ apiKey?: string | undefined;
1962
+ }>, z.ZodObject<{
1963
+ type: z.ZodLiteral<"openai">;
1964
+ model: z.ZodString;
1965
+ apiKey: z.ZodOptional<z.ZodString>;
1966
+ }, "strict", z.ZodTypeAny, {
1967
+ type: "openai";
1968
+ model: string;
1969
+ apiKey?: string | undefined;
1970
+ }, {
1971
+ type: "openai";
1972
+ model: string;
1973
+ apiKey?: string | undefined;
1974
+ }>, z.ZodObject<{
1975
+ type: z.ZodLiteral<"gemini">;
1976
+ model: z.ZodString;
1977
+ apiKey: z.ZodOptional<z.ZodString>;
1978
+ }, "strict", z.ZodTypeAny, {
1979
+ type: "gemini";
1980
+ model: string;
1981
+ apiKey?: string | undefined;
1982
+ }, {
1983
+ type: "gemini";
1984
+ model: string;
1985
+ apiKey?: string | undefined;
1986
+ }>, z.ZodObject<{
1987
+ type: z.ZodLiteral<"local">;
1988
+ endpoint: z.ZodString;
1989
+ model: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "atleastone">]>;
1990
+ apiKey: z.ZodOptional<z.ZodString>;
1991
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
1992
+ probeIntervalMs: z.ZodOptional<z.ZodNumber>;
1993
+ }, "strict", z.ZodTypeAny, {
1994
+ type: "local";
1995
+ model: string | [string, ...string[]];
1996
+ endpoint: string;
1997
+ apiKey?: string | undefined;
1998
+ timeoutMs?: number | undefined;
1999
+ probeIntervalMs?: number | undefined;
2000
+ }, {
2001
+ type: "local";
2002
+ model: string | [string, ...string[]];
2003
+ endpoint: string;
2004
+ apiKey?: string | undefined;
2005
+ timeoutMs?: number | undefined;
2006
+ probeIntervalMs?: number | undefined;
2007
+ }>, z.ZodObject<{
2008
+ type: z.ZodLiteral<"pi">;
2009
+ endpoint: z.ZodString;
2010
+ model: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "atleastone">]>;
2011
+ apiKey: z.ZodOptional<z.ZodString>;
2012
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
2013
+ probeIntervalMs: z.ZodOptional<z.ZodNumber>;
2014
+ }, "strict", z.ZodTypeAny, {
2015
+ type: "pi";
2016
+ model: string | [string, ...string[]];
2017
+ endpoint: string;
2018
+ apiKey?: string | undefined;
2019
+ timeoutMs?: number | undefined;
2020
+ probeIntervalMs?: number | undefined;
2021
+ }, {
2022
+ type: "pi";
2023
+ model: string | [string, ...string[]];
2024
+ endpoint: string;
2025
+ apiKey?: string | undefined;
2026
+ timeoutMs?: number | undefined;
2027
+ probeIntervalMs?: number | undefined;
2028
+ }>]>;
2029
+ /**
2030
+ * Spec B Phase 0: a routing target is either a backend name (scalar
2031
+ * string) or a non-empty ordered fallback chain (string tuple). The
2032
+ * scalar form is byte-compatible with pre-Spec-B configs.
2033
+ */
2034
+ declare const RoutingValueSchema: z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>;
2035
+ /**
2036
+ * Zod schema for `RoutingConfig`. `.strict()` rejects unknown keys at
2037
+ * every level (per Spec 2 D7: typos in routing keys are validation
2038
+ * errors, not silent default-fallthroughs).
2039
+ *
2040
+ * Spec B Phase 0: all scalar routing fields accept `RoutingValueSchema`
2041
+ * (scalar or non-empty chain). New optional `skills` and `modes` maps
2042
+ * accept the same.
2043
+ *
2044
+ * Spec B Phase 2: the `isolation` block (added to the TS interface in
2045
+ * Hermes Phase 5 but not previously in this Zod schema) is now included
2046
+ * here with each tier widened to `RoutingValueSchema`. This closes the
2047
+ * Phase 0 I2 review finding (TS-vs-Zod drift) and ensures isolation
2048
+ * chain entries are validated by the same cross-field check that
2049
+ * covers `skills` / `modes`.
2050
+ */
2051
+ declare const RoutingConfigSchema: z.ZodObject<{
2052
+ default: z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>;
2053
+ 'quick-fix': z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2054
+ 'guided-change': z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2055
+ 'full-exploration': z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2056
+ diagnostic: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2057
+ intelligence: z.ZodOptional<z.ZodObject<{
2058
+ sel: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2059
+ pesl: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2060
+ }, "strict", z.ZodTypeAny, {
2061
+ sel?: string | readonly [string, ...string[]] | undefined;
2062
+ pesl?: string | readonly [string, ...string[]] | undefined;
2063
+ }, {
2064
+ sel?: string | readonly [string, ...string[]] | undefined;
2065
+ pesl?: string | readonly [string, ...string[]] | undefined;
2066
+ }>>;
2067
+ isolation: z.ZodOptional<z.ZodObject<{
2068
+ none: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2069
+ container: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2070
+ 'remote-sandbox': z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>;
2071
+ }, "strict", z.ZodTypeAny, {
2072
+ none?: string | readonly [string, ...string[]] | undefined;
2073
+ container?: string | readonly [string, ...string[]] | undefined;
2074
+ 'remote-sandbox'?: string | readonly [string, ...string[]] | undefined;
2075
+ }, {
2076
+ none?: string | readonly [string, ...string[]] | undefined;
2077
+ container?: string | readonly [string, ...string[]] | undefined;
2078
+ 'remote-sandbox'?: string | readonly [string, ...string[]] | undefined;
2079
+ }>>;
2080
+ skills: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>>;
2081
+ modes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString, "atleastone">>]>>>;
2082
+ }, "strict", z.ZodTypeAny, {
2083
+ default: string | readonly [string, ...string[]];
2084
+ 'quick-fix'?: string | readonly [string, ...string[]] | undefined;
2085
+ 'guided-change'?: string | readonly [string, ...string[]] | undefined;
2086
+ 'full-exploration'?: string | readonly [string, ...string[]] | undefined;
2087
+ diagnostic?: string | readonly [string, ...string[]] | undefined;
2088
+ intelligence?: {
2089
+ sel?: string | readonly [string, ...string[]] | undefined;
2090
+ pesl?: string | readonly [string, ...string[]] | undefined;
2091
+ } | undefined;
2092
+ isolation?: {
2093
+ none?: string | readonly [string, ...string[]] | undefined;
2094
+ container?: string | readonly [string, ...string[]] | undefined;
2095
+ 'remote-sandbox'?: string | readonly [string, ...string[]] | undefined;
2096
+ } | undefined;
2097
+ skills?: Record<string, string | readonly [string, ...string[]]> | undefined;
2098
+ modes?: Record<string, string | readonly [string, ...string[]]> | undefined;
2099
+ }, {
2100
+ default: string | readonly [string, ...string[]];
2101
+ 'quick-fix'?: string | readonly [string, ...string[]] | undefined;
2102
+ 'guided-change'?: string | readonly [string, ...string[]] | undefined;
2103
+ 'full-exploration'?: string | readonly [string, ...string[]] | undefined;
2104
+ diagnostic?: string | readonly [string, ...string[]] | undefined;
2105
+ intelligence?: {
2106
+ sel?: string | readonly [string, ...string[]] | undefined;
2107
+ pesl?: string | readonly [string, ...string[]] | undefined;
2108
+ } | undefined;
2109
+ isolation?: {
2110
+ none?: string | readonly [string, ...string[]] | undefined;
2111
+ container?: string | readonly [string, ...string[]] | undefined;
2112
+ 'remote-sandbox'?: string | readonly [string, ...string[]] | undefined;
2113
+ } | undefined;
2114
+ skills?: Record<string, string | readonly [string, ...string[]]> | undefined;
2115
+ modes?: Record<string, string | readonly [string, ...string[]]> | undefined;
2116
+ }>;
2117
+
2118
+ interface ResolverLogger {
2119
+ info(message: string, context?: Record<string, unknown>): void;
2120
+ warn(message: string, context?: Record<string, unknown>): void;
2121
+ }
2122
+ interface LocalModelResolverOptions {
2123
+ endpoint: string;
2124
+ apiKey?: string;
2125
+ /** Normalized candidate list (already turned from string|string[] into string[]). */
2126
+ configured: string[];
2127
+ /** Probe cadence in ms; default 30_000, minimum 1_000. */
2128
+ probeIntervalMs?: number;
2129
+ /**
2130
+ * Per-request timeout for the default fetch implementation, in ms.
2131
+ * Default: 5_000. Ignored when a custom `fetchModels` is provided
2132
+ * (custom impls own their own timeout policy). Spec §3.1 line 136
2133
+ * enumerates timeout as a supported failure mode.
2134
+ */
2135
+ timeoutMs?: number;
2136
+ /**
2137
+ * Injectable for tests. Default: GET `${endpoint}/models` with bearer apiKey.
2138
+ * Resolves to detected model IDs. Rejects on network/timeout/non-2xx/malformed.
2139
+ */
2140
+ fetchModels?: (endpoint: string, apiKey?: string) => Promise<string[]>;
2141
+ logger?: ResolverLogger;
2142
+ }
2143
+ declare function normalizeLocalModel(input: string | string[] | undefined): string[];
2144
+ /**
2145
+ * Default `fetchModels` — GET `${endpoint}/models` with bearer apiKey.
2146
+ * Throws on network failure, non-2xx, malformed body, or timeout.
2147
+ *
2148
+ * `timeoutMs` defaults to 5_000. A timeout aborts the in-flight request and
2149
+ * surfaces as `Error('request timeout (Nms)')` so callers can distinguish it
2150
+ * from generic network errors.
2151
+ */
2152
+ declare function defaultFetchModels(endpoint: string, apiKey?: string, timeoutMs?: number): Promise<string[]>;
2153
+ declare class LocalModelResolver {
2154
+ private readonly endpoint;
2155
+ private readonly apiKey?;
2156
+ private readonly configured;
2157
+ private readonly probeIntervalMs;
2158
+ private readonly fetchModels;
2159
+ private readonly logger;
2160
+ private timer;
2161
+ private listeners;
2162
+ /**
2163
+ * Tracks an in-flight probe so concurrent invocations (interval tick while a
2164
+ * slow probe is running, or a manual `probe()` call mid-flight) share the
2165
+ * existing promise instead of racing to mutate `detected/resolved/lastError/
2166
+ * warnings` non-atomically across `await` points. Applies to both the timer
2167
+ * callback and direct `probe()` calls — any caller that arrives during an
2168
+ * in-flight probe gets the same promise back. Cleared in `finally` so the
2169
+ * next tick can start a fresh probe.
2170
+ */
2171
+ private probeInFlight;
2172
+ private resolved;
2173
+ private detected;
2174
+ private lastProbeAt;
2175
+ private lastError;
2176
+ private warnings;
2177
+ private available;
2178
+ constructor(opts: LocalModelResolverOptions);
2179
+ resolveModel(): string | null;
2180
+ getStatus(): LocalModelStatus;
2181
+ onStatusChange(handler: (status: LocalModelStatus) => void): () => void;
2182
+ probe(): Promise<LocalModelStatus>;
2183
+ private runProbe;
2184
+ start(): Promise<void>;
2185
+ stop(): void;
2186
+ private snapshotForDiff;
2187
+ }
2188
+
1924
2189
  /**
1925
2190
  * Function signature compatible with Node's `child_process.execFile`.
1926
2191
  * Allows injection for testing.
@@ -2552,4 +2817,4 @@ declare function emitProposalCreated(bus: EventEmitter, proposal: SkillProposal)
2552
2817
  declare function emitProposalApproved(bus: EventEmitter, proposal: SkillProposal): void;
2553
2818
  declare function emitProposalRejected(bus: EventEmitter, proposal: SkillProposal): void;
2554
2819
 
2555
- export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BUILT_IN_TASKS, BackendRouter, type BackendRouterOptions, type BaseRefFallbackEvent, type BuildArchiveHooksOptions, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type CreateTokenInput, type CreateTokenResult, type CustomTaskValidationError, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn$1 as ExecFileFn, type FromConfigOptions, GateNotReadyError, type GateResult, GateRunError, type Highlight, type HighlightsInfo, type IndexedDoc, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, MAX_ATTEMPTS, type MigrationResult, MockBackend, type NotificationSink, type NotificationSinkDeliverInput, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, type PersistedOutputEntry, PromotionError, type PromotionResult, PromptRenderer, type ProposalApprovedData, type ProposalCreatedData, type ProposalRejectedData, type PublishedIndex, type QueueInsertInput, type QueueRow, type QueueStats, RETRY_DELAYS_MS, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type RegistryEntry, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunOrigin, type RunningEntry, type ScheduleRetryEffect, type SearchOptions, type SideEffect, SinkConfigError, SinkRegistry, type SkillCatalogEntry, SlackSink, type SlackSinkOptions, SqliteSearchIndex, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type SummarizeContext, type SummarizeResult, type SyncMainOptions, type SyncMainResult, type SyncSkipReason, type TaskDefinition, TaskOutputStore, type TaskType, type TickEvent, TokenStore, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type ValidateWorkflowConfigOptions, type ValidatedWorkflowConfig, WebhookQueue, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, type WorkspaceManagerOptions, applyEvent, artifactPresenceFromIssue, buildArchiveHooks, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, crossFieldRoutingIssues, detectScopeTier, discoverSkillCatalog, discoverSkillCatalogNames, emitProposalApproved, emitProposalCreated, emitProposalRejected, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, indexSessionDirectory, isEligible, isSummaryEnabled, launchTUI, loadPublishedIndex, migrateAgentConfig, normalizeFts5Query, openSearchIndex, promote, reconcile, reindexFromArchive, renderAnalysisComment, renderLlmSummaryMarkdown, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, routingWarnings, runGate, savePublishedIndex, searchIndexPath, selectCandidates, sortCandidates, summarizeArchivedSession, syncMain, triageIssue, truncateForBudget, validateCustomTasks, validateWorkflowConfig, wireNotificationSinks, wrapAsEnvelope };
2820
+ export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BUILT_IN_TASKS, BackendDefSchema, BackendRouter, type BackendRouterOptions, type BaseRefFallbackEvent, type BuildArchiveHooksOptions, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type CreateTokenInput, type CreateTokenResult, type CustomTaskValidationError, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn$1 as ExecFileFn, type FromConfigOptions, GateNotReadyError, type GateResult, GateRunError, type Highlight, type HighlightsInfo, type IndexedDoc, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, LocalModelResolver, type LocalModelResolverOptions, MAX_ATTEMPTS, type MigrationResult, MockBackend, type NotificationSink, type NotificationSinkDeliverInput, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, type PersistedOutputEntry, PromotionError, type PromotionResult, PromptRenderer, type ProposalApprovedData, type ProposalCreatedData, type ProposalRejectedData, type PublishedIndex, type QueueInsertInput, type QueueRow, type QueueStats, RETRY_DELAYS_MS, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type RegistryEntry, type ReleaseClaimEffect, type ResolverLogger, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, RoutingConfigSchema, RoutingValueSchema, type RunAttemptPhase, type RunOrigin, type RunningEntry, type ScheduleRetryEffect, type SearchOptions, type SideEffect, SinkConfigError, SinkRegistry, type SkillCatalogEntry, SlackSink, type SlackSinkOptions, SqliteSearchIndex, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type SummarizeContext, type SummarizeResult, type SyncMainOptions, type SyncMainResult, type SyncSkipReason, type TaskDefinition, TaskOutputStore, type TaskType, type TickEvent, TokenStore, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type ValidateWorkflowConfigOptions, type ValidatedWorkflowConfig, WebhookQueue, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, type WorkspaceManagerOptions, applyEvent, artifactPresenceFromIssue, buildArchiveHooks, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, crossFieldRoutingIssues, defaultFetchModels, detectScopeTier, discoverSkillCatalog, discoverSkillCatalogNames, emitProposalApproved, emitProposalCreated, emitProposalRejected, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, indexSessionDirectory, isEligible, isSummaryEnabled, launchTUI, loadPublishedIndex, migrateAgentConfig, normalizeFts5Query, normalizeLocalModel, openSearchIndex, promote, reconcile, reindexFromArchive, renderAnalysisComment, renderLlmSummaryMarkdown, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, routingWarnings, runGate, savePublishedIndex, searchIndexPath, selectCandidates, sortCandidates, summarizeArchivedSession, syncMain, triageIssue, truncateForBudget, validateCustomTasks, validateWorkflowConfig, wireNotificationSinks, wrapAsEnvelope };
package/dist/index.js CHANGED
@@ -32,12 +32,14 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AnalysisArchive: () => AnalysisArchive,
34
34
  BUILT_IN_TASKS: () => BUILT_IN_TASKS,
35
+ BackendDefSchema: () => BackendDefSchema,
35
36
  BackendRouter: () => BackendRouter,
36
37
  ClaimManager: () => ClaimManager,
37
38
  GateNotReadyError: () => GateNotReadyError,
38
39
  GateRunError: () => GateRunError,
39
40
  InteractionQueue: () => InteractionQueue,
40
41
  LinearGraphQLStub: () => LinearGraphQLStub,
42
+ LocalModelResolver: () => LocalModelResolver,
41
43
  MAX_ATTEMPTS: () => MAX_ATTEMPTS,
42
44
  MockBackend: () => MockBackend,
43
45
  ORCHESTRATOR_IDENTITY_FILE: () => ORCHESTRATOR_IDENTITY_FILE,
@@ -48,6 +50,8 @@ __export(index_exports, {
48
50
  PromptRenderer: () => PromptRenderer,
49
51
  RETRY_DELAYS_MS: () => RETRY_DELAYS_MS,
50
52
  RoadmapTrackerAdapter: () => RoadmapTrackerAdapter,
53
+ RoutingConfigSchema: () => RoutingConfigSchema,
54
+ RoutingValueSchema: () => RoutingValueSchema,
51
55
  SinkConfigError: () => SinkConfigError,
52
56
  SinkRegistry: () => SinkRegistry,
53
57
  SlackSink: () => SlackSink,
@@ -68,6 +72,7 @@ __export(index_exports, {
68
72
  createBackend: () => createBackend,
69
73
  createEmptyState: () => createEmptyState,
70
74
  crossFieldRoutingIssues: () => crossFieldRoutingIssues,
75
+ defaultFetchModels: () => defaultFetchModels,
71
76
  detectScopeTier: () => detectScopeTier,
72
77
  discoverSkillCatalog: () => discoverSkillCatalog,
73
78
  discoverSkillCatalogNames: () => discoverSkillCatalogNames,
@@ -86,6 +91,7 @@ __export(index_exports, {
86
91
  loadPublishedIndex: () => loadPublishedIndex,
87
92
  migrateAgentConfig: () => migrateAgentConfig,
88
93
  normalizeFts5Query: () => normalizeFts5Query,
94
+ normalizeLocalModel: () => normalizeLocalModel,
89
95
  openSearchIndex: () => openSearchIndex,
90
96
  promote: () => promote,
91
97
  reconcile: () => reconcile,
@@ -2881,6 +2887,21 @@ var path21 = __toESM(require("path"));
2881
2887
  var import_node_crypto16 = require("crypto");
2882
2888
  var import_core14 = require("@harness-engineering/core");
2883
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
+
2884
2905
  // src/intelligence/pipeline-runner.ts
2885
2906
  var path9 = __toESM(require("path"));
2886
2907
  var import_intelligence = require("@harness-engineering/intelligence");
@@ -3640,6 +3661,14 @@ var DEFAULT_PROBE_INTERVAL_MS = 3e4;
3640
3661
  var MIN_PROBE_INTERVAL_MS = 1e3;
3641
3662
  var DEFAULT_API_KEY = "lm-studio";
3642
3663
  var DEFAULT_FETCH_TIMEOUT_MS = 5e3;
3664
+ function normalizeLocalModel(input) {
3665
+ if (input === void 0) return [];
3666
+ if (typeof input === "string") return [input];
3667
+ if (input.length === 0) {
3668
+ throw new Error("localModel array must be non-empty when provided");
3669
+ }
3670
+ return [...input];
3671
+ }
3643
3672
  var noopLogger = {
3644
3673
  info: () => void 0,
3645
3674
  warn: () => void 0
@@ -6927,7 +6956,7 @@ var ChatRequestSchema = import_zod6.z.object({
6927
6956
  });
6928
6957
  function handleChatProxyRoute(req, res, command = "claude") {
6929
6958
  const { method, url } = req;
6930
- if (method === "POST" && url === "/api/chat") {
6959
+ if (method === "POST" && (url === "/api/chat" || url === "/api/chat-proxy")) {
6931
6960
  void handleChatRequest(req, res, command);
6932
6961
  return true;
6933
6962
  }
@@ -9286,7 +9315,7 @@ function requiredScopeForRoute(method, path24) {
9286
9315
  if (path24.startsWith("/api/maintenance")) return "trigger-job";
9287
9316
  if (path24.startsWith("/api/streams")) return "read-status";
9288
9317
  if (path24.startsWith("/api/sessions")) return "read-status";
9289
- if (path24.startsWith("/api/chat-proxy")) return "trigger-job";
9318
+ if (path24 === "/api/chat" || path24.startsWith("/api/chat-proxy")) return "trigger-job";
9290
9319
  return null;
9291
9320
  }
9292
9321
 
@@ -12997,32 +13026,27 @@ ${messages}`);
12997
13026
  await this.handleEffect(effect);
12998
13027
  }
12999
13028
  const stallTimeoutMs = this.config.agent.stallTimeoutMs;
13000
- if (stallTimeoutMs > 0) {
13001
- const stalledIds = [];
13002
- for (const [runId, runEntry] of this.state.running) {
13003
- const lastTs = runEntry.session?.lastTimestamp;
13004
- if (!lastTs) continue;
13005
- const silentMs = nowMs - new Date(lastTs).getTime();
13006
- if (silentMs >= stallTimeoutMs) {
13007
- stalledIds.push(runId);
13008
- }
13009
- }
13010
- for (const runId of stalledIds) {
13011
- const runEntry = this.state.running.get(runId);
13012
- if (!runEntry) continue;
13013
- this.logger.warn(
13014
- `Agent stalled for ${runEntry.identifier}: ${Math.round((nowMs - new Date(runEntry.session?.lastTimestamp ?? 0).getTime()) / 1e3)}s since last event`,
13015
- { issueId: runId }
13016
- );
13017
- const stallEvent = {
13018
- type: "stall_detected",
13029
+ const stalledIds = detectStalledIssues(this.state.running, nowMs, stallTimeoutMs);
13030
+ for (const runId of stalledIds) {
13031
+ const runEntry = this.state.running.get(runId);
13032
+ if (!runEntry) continue;
13033
+ const reference = runEntry.session?.lastTimestamp ?? runEntry.startedAt;
13034
+ const silentSec = Math.round((nowMs - new Date(reference).getTime()) / 1e3);
13035
+ const sinceWhat = runEntry.session?.lastTimestamp ? "last event" : "dispatch";
13036
+ this.logger.warn(
13037
+ `Agent stalled for ${runEntry.identifier}: ${silentSec}s since ${sinceWhat}`,
13038
+ {
13019
13039
  issueId: runId
13020
- };
13021
- const stallResult = applyEvent(this.state, stallEvent, this.config);
13022
- this.state = stallResult.nextState;
13023
- for (const eff of stallResult.effects) {
13024
- await this.handleEffect(eff);
13025
13040
  }
13041
+ );
13042
+ const stallEvent = {
13043
+ type: "stall_detected",
13044
+ issueId: runId
13045
+ };
13046
+ const stallResult = applyEvent(this.state, stallEvent, this.config);
13047
+ this.state = stallResult.nextState;
13048
+ for (const eff of stallResult.effects) {
13049
+ await this.handleEffect(eff);
13026
13050
  }
13027
13051
  }
13028
13052
  const openPrNumbers = [];
@@ -14689,12 +14713,14 @@ function buildArchiveHooks(opts) {
14689
14713
  0 && (module.exports = {
14690
14714
  AnalysisArchive,
14691
14715
  BUILT_IN_TASKS,
14716
+ BackendDefSchema,
14692
14717
  BackendRouter,
14693
14718
  ClaimManager,
14694
14719
  GateNotReadyError,
14695
14720
  GateRunError,
14696
14721
  InteractionQueue,
14697
14722
  LinearGraphQLStub,
14723
+ LocalModelResolver,
14698
14724
  MAX_ATTEMPTS,
14699
14725
  MockBackend,
14700
14726
  ORCHESTRATOR_IDENTITY_FILE,
@@ -14705,6 +14731,8 @@ function buildArchiveHooks(opts) {
14705
14731
  PromptRenderer,
14706
14732
  RETRY_DELAYS_MS,
14707
14733
  RoadmapTrackerAdapter,
14734
+ RoutingConfigSchema,
14735
+ RoutingValueSchema,
14708
14736
  SinkConfigError,
14709
14737
  SinkRegistry,
14710
14738
  SlackSink,
@@ -14725,6 +14753,7 @@ function buildArchiveHooks(opts) {
14725
14753
  createBackend,
14726
14754
  createEmptyState,
14727
14755
  crossFieldRoutingIssues,
14756
+ defaultFetchModels,
14728
14757
  detectScopeTier,
14729
14758
  discoverSkillCatalog,
14730
14759
  discoverSkillCatalogNames,
@@ -14743,6 +14772,7 @@ function buildArchiveHooks(opts) {
14743
14772
  loadPublishedIndex,
14744
14773
  migrateAgentConfig,
14745
14774
  normalizeFts5Query,
14775
+ normalizeLocalModel,
14746
14776
  openSearchIndex,
14747
14777
  promote,
14748
14778
  reconcile,
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";
@@ -3544,6 +3559,14 @@ var DEFAULT_PROBE_INTERVAL_MS = 3e4;
3544
3559
  var MIN_PROBE_INTERVAL_MS = 1e3;
3545
3560
  var DEFAULT_API_KEY = "lm-studio";
3546
3561
  var DEFAULT_FETCH_TIMEOUT_MS = 5e3;
3562
+ function normalizeLocalModel(input) {
3563
+ if (input === void 0) return [];
3564
+ if (typeof input === "string") return [input];
3565
+ if (input.length === 0) {
3566
+ throw new Error("localModel array must be non-empty when provided");
3567
+ }
3568
+ return [...input];
3569
+ }
3547
3570
  var noopLogger = {
3548
3571
  info: () => void 0,
3549
3572
  warn: () => void 0
@@ -6866,7 +6889,7 @@ var ChatRequestSchema = z6.object({
6866
6889
  });
6867
6890
  function handleChatProxyRoute(req, res, command = "claude") {
6868
6891
  const { method, url } = req;
6869
- if (method === "POST" && url === "/api/chat") {
6892
+ if (method === "POST" && (url === "/api/chat" || url === "/api/chat-proxy")) {
6870
6893
  void handleChatRequest(req, res, command);
6871
6894
  return true;
6872
6895
  }
@@ -9255,7 +9278,7 @@ function requiredScopeForRoute(method, path24) {
9255
9278
  if (path24.startsWith("/api/maintenance")) return "trigger-job";
9256
9279
  if (path24.startsWith("/api/streams")) return "read-status";
9257
9280
  if (path24.startsWith("/api/sessions")) return "read-status";
9258
- if (path24.startsWith("/api/chat-proxy")) return "trigger-job";
9281
+ if (path24 === "/api/chat" || path24.startsWith("/api/chat-proxy")) return "trigger-job";
9259
9282
  return null;
9260
9283
  }
9261
9284
 
@@ -12974,32 +12997,27 @@ ${messages}`);
12974
12997
  await this.handleEffect(effect);
12975
12998
  }
12976
12999
  const stallTimeoutMs = this.config.agent.stallTimeoutMs;
12977
- if (stallTimeoutMs > 0) {
12978
- const stalledIds = [];
12979
- for (const [runId, runEntry] of this.state.running) {
12980
- const lastTs = runEntry.session?.lastTimestamp;
12981
- if (!lastTs) continue;
12982
- const silentMs = nowMs - new Date(lastTs).getTime();
12983
- if (silentMs >= stallTimeoutMs) {
12984
- stalledIds.push(runId);
12985
- }
12986
- }
12987
- for (const runId of stalledIds) {
12988
- const runEntry = this.state.running.get(runId);
12989
- if (!runEntry) continue;
12990
- this.logger.warn(
12991
- `Agent stalled for ${runEntry.identifier}: ${Math.round((nowMs - new Date(runEntry.session?.lastTimestamp ?? 0).getTime()) / 1e3)}s since last event`,
12992
- { issueId: runId }
12993
- );
12994
- const stallEvent = {
12995
- type: "stall_detected",
13000
+ const stalledIds = detectStalledIssues(this.state.running, nowMs, stallTimeoutMs);
13001
+ for (const runId of stalledIds) {
13002
+ const runEntry = this.state.running.get(runId);
13003
+ if (!runEntry) continue;
13004
+ const reference = runEntry.session?.lastTimestamp ?? runEntry.startedAt;
13005
+ const silentSec = Math.round((nowMs - new Date(reference).getTime()) / 1e3);
13006
+ const sinceWhat = runEntry.session?.lastTimestamp ? "last event" : "dispatch";
13007
+ this.logger.warn(
13008
+ `Agent stalled for ${runEntry.identifier}: ${silentSec}s since ${sinceWhat}`,
13009
+ {
12996
13010
  issueId: runId
12997
- };
12998
- const stallResult = applyEvent(this.state, stallEvent, this.config);
12999
- this.state = stallResult.nextState;
13000
- for (const eff of stallResult.effects) {
13001
- await this.handleEffect(eff);
13002
13011
  }
13012
+ );
13013
+ const stallEvent = {
13014
+ type: "stall_detected",
13015
+ issueId: runId
13016
+ };
13017
+ const stallResult = applyEvent(this.state, stallEvent, this.config);
13018
+ this.state = stallResult.nextState;
13019
+ for (const eff of stallResult.effects) {
13020
+ await this.handleEffect(eff);
13003
13021
  }
13004
13022
  }
13005
13023
  const openPrNumbers = [];
@@ -14667,12 +14685,14 @@ function buildArchiveHooks(opts) {
14667
14685
  export {
14668
14686
  AnalysisArchive,
14669
14687
  BUILT_IN_TASKS,
14688
+ BackendDefSchema,
14670
14689
  BackendRouter,
14671
14690
  ClaimManager,
14672
14691
  GateNotReadyError,
14673
14692
  GateRunError,
14674
14693
  InteractionQueue,
14675
14694
  LinearGraphQLStub,
14695
+ LocalModelResolver,
14676
14696
  MAX_ATTEMPTS,
14677
14697
  MockBackend,
14678
14698
  ORCHESTRATOR_IDENTITY_FILE,
@@ -14683,6 +14703,8 @@ export {
14683
14703
  PromptRenderer,
14684
14704
  RETRY_DELAYS_MS,
14685
14705
  RoadmapTrackerAdapter,
14706
+ RoutingConfigSchema,
14707
+ RoutingValueSchema,
14686
14708
  SinkConfigError,
14687
14709
  SinkRegistry,
14688
14710
  SlackSink,
@@ -14703,6 +14725,7 @@ export {
14703
14725
  createBackend,
14704
14726
  createEmptyState,
14705
14727
  crossFieldRoutingIssues,
14728
+ defaultFetchModels,
14706
14729
  detectScopeTier,
14707
14730
  discoverSkillCatalog,
14708
14731
  discoverSkillCatalogNames,
@@ -14721,6 +14744,7 @@ export {
14721
14744
  loadPublishedIndex,
14722
14745
  migrateAgentConfig,
14723
14746
  normalizeFts5Query,
14747
+ normalizeLocalModel,
14724
14748
  openSearchIndex,
14725
14749
  promote,
14726
14750
  reconcile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harness-engineering/orchestrator",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "Orchestrator daemon for dispatching coding agents to issues",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -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.28.1",
52
- "@harness-engineering/graph": "0.10.0",
53
- "@harness-engineering/intelligence": "0.2.6",
54
- "@harness-engineering/types": "0.15.0"
51
+ "@harness-engineering/core": "0.29.0",
52
+ "@harness-engineering/graph": "0.11.0",
53
+ "@harness-engineering/intelligence": "0.2.7",
54
+ "@harness-engineering/types": "0.16.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@asteasolutions/zod-to-openapi": "^7.3.0",