@harness-engineering/orchestrator 0.4.6 → 0.6.0

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,6 +1,6 @@
1
- import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, RoutingDecision, Result, WorkflowDefinition, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, BackendDef, RoutingConfig, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig, TokenScope, AuthToken, AuthTokenPublic } from '@harness-engineering/types';
2
- import { IssueTrackerClient, Issue as Issue$1, TrackerConfig, CacheMetricsRecorder } from '@harness-engineering/core';
3
- import { EnrichedSpec, ComplexityScore, SimulationResult, IntelligencePipeline, WeightedRecommendation } from '@harness-engineering/intelligence';
1
+ import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, RoutingDecision, Result, WorkflowDefinition, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, CheckScriptDefinition, OutputRetentionConfig, BackendDef, RoutingConfig, 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 { IssueTrackerClient, Issue as Issue$1, TrackerConfig, CacheMetricsRecorder, ArchiveHooks, SkillProposal, ProposalGateFinding } from '@harness-engineering/core';
3
+ import { EnrichedSpec, ComplexityScore, SimulationResult, IntelligencePipeline, WeightedRecommendation, AnalysisProvider } from '@harness-engineering/intelligence';
4
4
  import { GraphStore } from '@harness-engineering/graph';
5
5
  import { execFile } from 'node:child_process';
6
6
  import { EventEmitter } from 'node:events';
@@ -1084,8 +1084,10 @@ declare class PromptRenderer {
1084
1084
 
1085
1085
  /**
1086
1086
  * Internal types for the maintenance module.
1087
- * Public config types (MaintenanceConfig, TaskOverride) live in @harness-engineering/types.
1087
+ * Public config types (MaintenanceConfig, TaskOverride, CustomTaskDefinition,
1088
+ * CheckScriptDefinition, OutputRetentionConfig) live in @harness-engineering/types.
1088
1089
  */
1090
+
1089
1091
  /**
1090
1092
  * Classification of maintenance task execution strategy.
1091
1093
  *
@@ -1095,6 +1097,85 @@ declare class PromptRenderer {
1095
1097
  * - housekeeping: Run a mechanical command directly; no AI, no PR.
1096
1098
  */
1097
1099
  type TaskType = 'mechanical-ai' | 'pure-ai' | 'report-only' | 'housekeeping';
1100
+ /**
1101
+ * Hermes Phase 2 — Provenance tag identifying the trigger source of a run.
1102
+ *
1103
+ * Set by the entry point, never user-configurable:
1104
+ * - 'cron' — scheduled by MaintenanceScheduler
1105
+ * - 'cli' — `harness maintenance run <id>`
1106
+ * - { kind: 'api', tokenName } — Gateway API trigger (Phase 0)
1107
+ * - { kind: 'chain', upstreamTaskId } — fired by a downstream `contextFrom`
1108
+ * dependency (reserved; not yet wired)
1109
+ */
1110
+ type RunOrigin = 'cron' | 'cli' | {
1111
+ kind: 'api';
1112
+ tokenName: string;
1113
+ } | {
1114
+ kind: 'chain';
1115
+ upstreamTaskId: string;
1116
+ };
1117
+ /**
1118
+ * Per-task cost ceiling (Hermes Phase 5).
1119
+ *
1120
+ * When set, the orchestrator's `CostCeilingMonitor` tracks cumulative
1121
+ * agent spend for the task and aborts dispatch on exceed (D6 — abort
1122
+ * is advisory at the turn boundary).
1123
+ */
1124
+ interface TaskCostCeiling {
1125
+ /** Hard cap in USD. Cumulative spend > maxUsd fires the abort path. */
1126
+ maxUsd: number;
1127
+ /** Warn threshold expressed as a percentage of `maxUsd` (1–99). */
1128
+ warnAtPct?: number;
1129
+ }
1130
+ /**
1131
+ * Definition of a maintenance task (built-in or Phase 2 custom).
1132
+ *
1133
+ * Custom-task-only fields (`checkScript`, `inlineSkills`, `inlineSkillsBudgetTokens`,
1134
+ * `contextFrom`, `contextFromMaxAgeMinutes`, `outputRetention`, `isCustom`) are
1135
+ * populated by the scheduler when merging `MaintenanceConfig.customTasks` into the
1136
+ * resolved task list. Built-ins leave them unset and the runner falls through to
1137
+ * the legacy execution paths unchanged.
1138
+ */
1139
+ interface TaskDefinition {
1140
+ /** Unique identifier for this task (e.g., 'arch-violations') */
1141
+ id: string;
1142
+ /** Execution strategy */
1143
+ type: TaskType;
1144
+ /** Human-readable description */
1145
+ description: string;
1146
+ /** Default cron expression (e.g., '0 2 * * *' for daily at 2am) */
1147
+ schedule: string;
1148
+ /** Branch name for PRs, or null for report-only/housekeeping tasks */
1149
+ branch: string | null;
1150
+ /** CLI command args for the mechanical check step (mechanical-ai and report-only) */
1151
+ checkCommand?: string[];
1152
+ /** Skill name to dispatch for AI fix (mechanical-ai and pure-ai) */
1153
+ fixSkill?: string;
1154
+ /**
1155
+ * Per-task cost ceiling (Hermes Phase 5). When set, cumulative agent
1156
+ * spend across all turns dispatched for this task is tracked; the
1157
+ * orchestrator aborts dispatch on `maxUsd` exceedance with
1158
+ * `RunResult.error === 'cost_ceiling_exceeded'`. Default: unset = no cap.
1159
+ */
1160
+ costCeiling?: TaskCostCeiling;
1161
+ /**
1162
+ * Hermes Phase 2 — Arbitrary-executable check (replaces `checkCommand`).
1163
+ * Mutually-exclusive with `checkCommand`; validator rejects both.
1164
+ */
1165
+ checkScript?: CheckScriptDefinition;
1166
+ /** Hermes Phase 2 — Skill names whose markdown is inlined into the agent prompt. */
1167
+ inlineSkills?: string[];
1168
+ /** Hermes Phase 2 — Token-budget cap for inlined skills. Default: 8000. */
1169
+ inlineSkillsBudgetTokens?: number;
1170
+ /** Hermes Phase 2 — Upstream task IDs whose latest output feeds prompt context. */
1171
+ contextFrom?: string[];
1172
+ /** Hermes Phase 2 — Max upstream-output age (minutes). Default: 1440. */
1173
+ contextFromMaxAgeMinutes?: number;
1174
+ /** Hermes Phase 2 — Output retention overrides. */
1175
+ outputRetention?: OutputRetentionConfig;
1176
+ /** Hermes Phase 2 — Marks tasks originating from `customTasks` config. */
1177
+ isCustom?: boolean;
1178
+ }
1098
1179
  /**
1099
1180
  * Result of a single maintenance task run.
1100
1181
  */
@@ -1117,6 +1198,21 @@ interface RunResult {
1117
1198
  prUpdated: boolean;
1118
1199
  /** Error message if status is 'failure' */
1119
1200
  error?: string;
1201
+ /**
1202
+ * Cumulative agent spend in USD for this run (Hermes Phase 5).
1203
+ *
1204
+ * Always present (defaults to 0). Populated by the
1205
+ * `CostCeilingMonitor` from per-turn `TokenUsage` × `ModelPricing`.
1206
+ * When the run aborted on cost-ceiling exceed, `status === 'failure'`
1207
+ * and `error === 'cost_ceiling_exceeded'`.
1208
+ */
1209
+ costUsd?: number;
1210
+ /**
1211
+ * Hermes Phase 2 — Provenance tag set by the entry point.
1212
+ * Older orchestrators may emit this field absent; renderers should fall
1213
+ * back to `'—'` rather than crash.
1214
+ */
1215
+ origin?: RunOrigin;
1120
1216
  }
1121
1217
  /**
1122
1218
  * Schedule entry for a single task, used in MaintenanceStatus.
@@ -1226,6 +1322,8 @@ declare class Orchestrator extends EventEmitter {
1226
1322
  private cacheMetrics?;
1227
1323
  private otlpExporter?;
1228
1324
  private telemetryFanoutOff?;
1325
+ private notificationsRegistry?;
1326
+ private notificationFanoutOff?;
1229
1327
  private orchestratorIdPromise;
1230
1328
  private recorder;
1231
1329
  private intelligenceRunner;
@@ -1341,6 +1439,15 @@ declare class Orchestrator extends EventEmitter {
1341
1439
  * Informs the state machine that an agent worker has exited.
1342
1440
  */
1343
1441
  private emitWorkerExit;
1442
+ /**
1443
+ * Hermes Phase 3: wire in-process notification sinks against the
1444
+ * orchestrator's event bus (`this`). A misconfigured sink (unknown kind,
1445
+ * missing env var) logs + skips rather than breaking startup — the
1446
+ * hardened doctor (`harness doctor`) surfaces the gap. Sinks subscribe
1447
+ * to the same topics as `wireWebhookFanout`; a slow Slack call cannot
1448
+ * block webhook delivery because the two paths fan out independently.
1449
+ */
1450
+ private setupNotifications;
1344
1451
  /**
1345
1452
  * Stops execution for a specific issue.
1346
1453
  *
@@ -1594,6 +1701,147 @@ interface SyncMainOptions {
1594
1701
  */
1595
1702
  declare function syncMain(repoRoot: string, opts?: SyncMainOptions): Promise<SyncMainResult>;
1596
1703
 
1704
+ /**
1705
+ * All 21 built-in maintenance task definitions with default schedules.
1706
+ *
1707
+ * Tasks are grouped by type:
1708
+ * - mechanical-ai (7): Run check first, dispatch AI only if fixable issues found
1709
+ * - pure-ai (4): Always dispatch AI agent on schedule
1710
+ * - report-only (7): Run command, record metrics, no PR
1711
+ * - housekeeping (3): Mechanical command, no AI, no PR
1712
+ */
1713
+ declare const BUILT_IN_TASKS: readonly TaskDefinition[];
1714
+
1715
+ /**
1716
+ * Unified logger interface for all maintenance classes.
1717
+ * Matches StructuredLogger's shape.
1718
+ */
1719
+ interface MaintenanceLogger {
1720
+ info(message: string, context?: Record<string, unknown>): void;
1721
+ warn(message: string, context?: Record<string, unknown>): void;
1722
+ error(message: string, context?: Record<string, unknown>): void;
1723
+ debug?(message: string, context?: Record<string, unknown>): void;
1724
+ }
1725
+
1726
+ /**
1727
+ * Hermes Phase 2 — A single persisted run entry.
1728
+ *
1729
+ * Mirrors `RunResult` plus the captured stdout/stderr, the parsed structured
1730
+ * status envelope (if any), the resolved upstream context that was injected
1731
+ * into the prompt (if any), and the trigger origin.
1732
+ */
1733
+ interface PersistedOutputEntry {
1734
+ taskId: string;
1735
+ startedAt: string;
1736
+ completedAt: string;
1737
+ status: RunResult['status'];
1738
+ findings: number;
1739
+ fixed: number;
1740
+ prUrl: string | null;
1741
+ prUpdated: boolean;
1742
+ error?: string;
1743
+ costUsd?: number;
1744
+ origin?: RunOrigin;
1745
+ /** Raw captured stdout from the check step (or housekeeping command). */
1746
+ stdout?: string;
1747
+ /** Raw captured stderr from the check step. */
1748
+ stderr?: string;
1749
+ /** Structured envelope when a JSON status line was parsed; null otherwise. */
1750
+ structured?: Record<string, unknown> | null;
1751
+ /** Resolved upstream-context block, if any. */
1752
+ context?: string;
1753
+ }
1754
+ interface TaskOutputStoreOptions {
1755
+ /** Root directory under which `<taskId>/outputs/` lives. Default: `.harness/maintenance`. */
1756
+ rootDir: string;
1757
+ /** Default retention bounds applied when a task doesn't specify its own. */
1758
+ retentionDefaults?: Required<OutputRetentionConfig>;
1759
+ logger?: MaintenanceLogger;
1760
+ }
1761
+ /**
1762
+ * Persists per-task run outputs to disk and applies retention. The store is
1763
+ * intentionally simple: one file per run keyed by completion timestamp, JSON
1764
+ * payload, no SQLite. The chain-context read path (`latest`) and the
1765
+ * dashboard list path (`list`) both consume the same on-disk format.
1766
+ *
1767
+ * Concurrency: `processQueue` already serializes runs of the same task ID,
1768
+ * so the store assumes exclusive write access per task.
1769
+ */
1770
+ declare class TaskOutputStore {
1771
+ private rootDir;
1772
+ private retentionDefaults;
1773
+ private logger;
1774
+ constructor(options: TaskOutputStoreOptions);
1775
+ /**
1776
+ * Reject task IDs that don't match the validator's kebab-case pattern —
1777
+ * defends `dirFor()` against caller-supplied path-traversal segments
1778
+ * (`'../foo'`) when the store is invoked from CLI surfaces that don't
1779
+ * round-trip through `validateCustomTasks`.
1780
+ */
1781
+ private ensureSafeTaskId;
1782
+ /**
1783
+ * Persist a single run entry. Retention is applied after the write so
1784
+ * the latest record is durable even if pruning fails.
1785
+ */
1786
+ write(taskId: string, entry: PersistedOutputEntry, retention?: OutputRetentionConfig): Promise<void>;
1787
+ /**
1788
+ * Return the most recent persisted entry for the task, or null if none.
1789
+ */
1790
+ latest(taskId: string): Promise<PersistedOutputEntry | null>;
1791
+ /**
1792
+ * List entries newest-first with offset+limit pagination.
1793
+ */
1794
+ list(taskId: string, limit: number, offset: number): Promise<PersistedOutputEntry[]>;
1795
+ /**
1796
+ * Lookup a specific run by its file name (without the `.json` suffix) or
1797
+ * by its raw completion timestamp.
1798
+ */
1799
+ get(taskId: string, runId: string): Promise<PersistedOutputEntry | null>;
1800
+ /**
1801
+ * The on-disk root for a given task. Exposed for tooling that needs to walk
1802
+ * outputs from outside the store API.
1803
+ */
1804
+ dirFor(taskId: string): string;
1805
+ private readEntry;
1806
+ private applyRetention;
1807
+ }
1808
+
1809
+ /**
1810
+ * Hermes Phase 2 — Validation errors surfaced by `validateCustomTasks`.
1811
+ *
1812
+ * `path` always begins with `customTasks.<taskId>` so the caller can render
1813
+ * it directly without re-prefixing. Multiple errors may be returned in a
1814
+ * single call; the validator does not short-circuit on the first failure.
1815
+ */
1816
+ interface CustomTaskValidationError {
1817
+ path: string;
1818
+ message: string;
1819
+ }
1820
+ interface CustomTaskValidatorDeps {
1821
+ /** Returns true if a skill with this name exists in the project's registry. */
1822
+ skillExists?: (name: string) => boolean;
1823
+ /** Returns true if the executable referenced by a checkScript.path exists. */
1824
+ scriptExists?: (path: string) => boolean;
1825
+ }
1826
+ /**
1827
+ * Validates a `MaintenanceConfig.customTasks` map.
1828
+ *
1829
+ * Checks:
1830
+ * - kebab-case task IDs (matching the BUILT_IN_TASKS convention)
1831
+ * - no collision with built-in IDs
1832
+ * - per-type required fields (e.g., mechanical-ai must have `branch` + `fixSkill`)
1833
+ * - exactly one of `checkCommand` / `checkScript` for types that need a check step
1834
+ * - `contextFrom` cycle detection across the merged graph (built-ins + customs)
1835
+ * - `contextFrom` entries reference existing task IDs
1836
+ * - `inlineSkills` entries exist in the skill registry (when `skillExists` is provided)
1837
+ * - `checkScript.path` exists on disk (when `scriptExists` is provided)
1838
+ *
1839
+ * Returns `Ok(void)` when all custom tasks pass; otherwise an `Err` carrying
1840
+ * every distinct violation. The validator is pure: no I/O outside the
1841
+ * injected predicates.
1842
+ */
1843
+ declare function validateCustomTasks(customTasks: Record<string, CustomTaskDefinition> | undefined, builtIns: readonly TaskDefinition[], deps?: CustomTaskValidatorDeps): Result<void, CustomTaskValidationError[]>;
1844
+
1597
1845
  interface CreateTokenInput {
1598
1846
  name: string;
1599
1847
  scopes: TokenScope[];
@@ -1697,4 +1945,350 @@ declare class WebhookQueue {
1697
1945
  close(): void;
1698
1946
  }
1699
1947
 
1700
- export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BackendRouter, type BackendRouterOptions, type BaseRefFallbackEvent, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type CreateTokenInput, type CreateTokenResult, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn$1 as ExecFileFn, type Highlight, type HighlightsInfo, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, MAX_ATTEMPTS, type MigrationResult, MockBackend, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, PromptRenderer, type PublishedIndex, type QueueInsertInput, type QueueRow, type QueueStats, RETRY_DELAYS_MS, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type SyncMainOptions, type SyncMainResult, type SyncSkipReason, type TickEvent, TokenStore, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, WebhookQueue, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, type WorkspaceManagerOptions, applyEvent, artifactPresenceFromIssue, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, detectScopeTier, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, loadPublishedIndex, migrateAgentConfig, reconcile, renderAnalysisComment, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, savePublishedIndex, selectCandidates, sortCandidates, syncMain, triageIssue, validateWorkflowConfig };
1948
+ interface IndexedDoc {
1949
+ sessionId: string;
1950
+ archived: boolean;
1951
+ fileKind: IndexedFileKind;
1952
+ /** Path relative to project root, posix-style. */
1953
+ path: string;
1954
+ mtimeMs: number;
1955
+ body: string;
1956
+ }
1957
+ interface SearchOptions {
1958
+ limit?: number;
1959
+ archivedOnly?: boolean;
1960
+ fileKinds?: IndexedFileKind[];
1961
+ /** Maximum bytes to retain per doc body (defaults 256 KiB; longer bodies are truncated with a marker). */
1962
+ maxBytesPerBody?: number;
1963
+ }
1964
+ /**
1965
+ * Convert a user-typed query string into a safe FTS5 expression.
1966
+ *
1967
+ * If the caller's query already contains explicit FTS5 syntax markers (double
1968
+ * quotes, parens, asterisk, caret, plus, the literal words AND/OR/NOT or a
1969
+ * `column:` selector) it is passed through unchanged so power users keep the
1970
+ * full FTS5 grammar.
1971
+ *
1972
+ * Otherwise each whitespace-separated token is wrapped as an FTS5 phrase so
1973
+ * characters like `-`, `:` and `*` inside the token are treated as content
1974
+ * (not operators), and the tokens are implicitly AND-joined by FTS5.
1975
+ *
1976
+ * Without this, `idx.search('token-aleph')` is parsed by FTS5 as
1977
+ * `token NOT aleph` and fails with `no such column: aleph`.
1978
+ */
1979
+ declare function normalizeFts5Query(query: string): string;
1980
+ /**
1981
+ * Filesystem path of the search-index sqlite file for a given project root.
1982
+ * Stable so consumers (cleanup tools, doctor, gitignore guards) can locate it.
1983
+ */
1984
+ declare function searchIndexPath(projectPath: string): string;
1985
+ declare class SqliteSearchIndex {
1986
+ private readonly db;
1987
+ private readonly upsertStmt;
1988
+ private readonly removeSessionStmt;
1989
+ private readonly totalStmt;
1990
+ constructor(dbPath: string);
1991
+ upsertSessionDoc(doc: IndexedDoc): void;
1992
+ removeSession(sessionId: string): number;
1993
+ /**
1994
+ * Drop all `archived=1` rows. Used by `reindexFromArchive` before a full
1995
+ * re-walk. Live (archived=0) rows are preserved.
1996
+ */
1997
+ resetArchived(): void;
1998
+ /** Total rows currently indexed (across both live and archived). */
1999
+ totalIndexed(): number;
2000
+ /**
2001
+ * Ranked FTS5 query. Returns BM25-sorted matches. The `query` is passed to
2002
+ * FTS5 as-is; FTS5 syntax (phrases with quotes, AND/OR/NOT, `column:term`)
2003
+ * is therefore the user-facing language. Errors from malformed queries
2004
+ * surface as thrown `SqliteError` so the CLI can catch + render them.
2005
+ */
2006
+ search(query: string, opts?: SearchOptions): SessionSearchResult;
2007
+ close(): void;
2008
+ }
2009
+ /** Open (or create) the project's search index. Idempotent. */
2010
+ declare function openSearchIndex(projectPath: string): SqliteSearchIndex;
2011
+ /**
2012
+ * Walk a session/archive directory and upsert one row per existing file_kind.
2013
+ * Used by both the archive hook (`indexArchivedSession`) and `reindexFromArchive`.
2014
+ *
2015
+ * Bodies larger than `maxBytesPerBody` are truncated with a marker so the index
2016
+ * does not bloat on pathological session files.
2017
+ */
2018
+ declare function indexSessionDirectory(idx: SqliteSearchIndex, args: {
2019
+ sessionId: string;
2020
+ sessionDir: string;
2021
+ archived: boolean;
2022
+ projectPath: string;
2023
+ /** Subset of file_kinds to consider (defaults to all). */
2024
+ fileKinds?: IndexedFileKind[];
2025
+ maxBytesPerBody?: number;
2026
+ }): {
2027
+ docsWritten: number;
2028
+ };
2029
+ /**
2030
+ * Drop and rebuild the `archived=1` portion of the index from
2031
+ * `.harness/archive/sessions/<slug-date>/`. Idempotent.
2032
+ *
2033
+ * Each subdirectory is treated as one session whose id is the basename.
2034
+ */
2035
+ declare function reindexFromArchive(projectPath: string, opts?: {
2036
+ fileKinds?: IndexedFileKind[];
2037
+ maxBytesPerBody?: number;
2038
+ }): ReindexStats;
2039
+
2040
+ interface SummarizeContext {
2041
+ /** Path to the archived session directory, e.g. .harness/archive/sessions/foo-2026-05-16. */
2042
+ archiveDir: string;
2043
+ /** Resolved AnalysisProvider — caller skips this step when no provider is available. */
2044
+ provider: AnalysisProvider;
2045
+ /** Optional session summary config, defaults applied when fields are missing. */
2046
+ config?: SessionSummarizationConfig | undefined;
2047
+ /** Optional logger; falls back to console.warn for diagnostics. */
2048
+ logger?: {
2049
+ warn?: (msg: string, meta?: Record<string, unknown>) => void;
2050
+ };
2051
+ /** When true, on provider error a stub `llm-summary.md` is still written. Default true. */
2052
+ writeStubOnError?: boolean;
2053
+ }
2054
+ interface SummarizeResult {
2055
+ summary: SessionSummary;
2056
+ meta: SessionSummaryMeta;
2057
+ filePath: string;
2058
+ }
2059
+ /** Approximate token cap via char count; conservative because tokens average ~4 chars. */
2060
+ declare function truncateForBudget(text: string, inputBudgetTokens: number): string;
2061
+ /** Render the structured summary as the `llm-summary.md` markdown payload. */
2062
+ declare function renderLlmSummaryMarkdown(summary: SessionSummary, meta: SessionSummaryMeta): string;
2063
+ /**
2064
+ * Summarise a single archived session via the provided AnalysisProvider.
2065
+ *
2066
+ * On success: writes `llm-summary.md` into the archive directory and returns
2067
+ * the structured summary + metadata.
2068
+ *
2069
+ * On provider error: optionally writes a stub `llm-summary.md` so callers can
2070
+ * still detect that summarization was attempted, then returns `Err`.
2071
+ *
2072
+ * Empty / missing input corpus is returned as `Err` and never produces a file.
2073
+ */
2074
+ declare function summarizeArchivedSession(ctx: SummarizeContext): Promise<Result<SummarizeResult, Error>>;
2075
+ /** Resolve whether summarization should run for the given config. */
2076
+ declare function isSummaryEnabled(config?: SessionSummarizationConfig): boolean;
2077
+
2078
+ /**
2079
+ * Session archive hook bundle.
2080
+ *
2081
+ * `buildArchiveHooks()` returns an `ArchiveHooks` implementation that wires
2082
+ * `summarizeArchivedSession()` + `indexSessionDirectory()` together so the
2083
+ * core `archiveSession()` lifecycle invokes both after a successful move.
2084
+ *
2085
+ * Both steps are individually wrapped in try/catch — failure of either does
2086
+ * not propagate up the call stack. Spec: §"Risks" treats summary + index
2087
+ * failure as non-fatal.
2088
+ */
2089
+
2090
+ interface BuildArchiveHooksOptions {
2091
+ /** Absolute path to the project root (contains `.harness/`). */
2092
+ projectPath: string;
2093
+ /** Optional AnalysisProvider — summarization is skipped when omitted. */
2094
+ provider?: AnalysisProvider | undefined;
2095
+ /** Optional sessions config slice. */
2096
+ config?: SessionsConfig | undefined;
2097
+ /** Optional logger; falls back to console.warn. */
2098
+ logger?: HookLogger | undefined;
2099
+ }
2100
+ interface HookLogger {
2101
+ warn?: (msg: string, meta?: Record<string, unknown>) => void;
2102
+ }
2103
+ /**
2104
+ * Construct the `ArchiveHooks` impl. Always returns a working hook bundle —
2105
+ * missing provider or disabled config simply skips that step.
2106
+ */
2107
+ declare function buildArchiveHooks(opts: BuildArchiveHooksOptions): ArchiveHooks;
2108
+
2109
+ /**
2110
+ * Wrap a `GatewayEvent` into a platform-agnostic `NotificationEnvelope`.
2111
+ * Used when a sink has `wrap_response: true` in its config. Unknown event
2112
+ * types fall back to a generic title/summary so newly-emitted events do
2113
+ * not require a code change to be deliverable.
2114
+ */
2115
+ declare function wrapAsEnvelope(event: GatewayEvent): NotificationEnvelope;
2116
+
2117
+ /**
2118
+ * Payload passed to `NotificationSink.deliver`. `wrapped` discriminates
2119
+ * which member of the union `payload` is. Sinks branch on `wrapped`
2120
+ * rather than runtime-detecting the shape.
2121
+ */
2122
+ interface NotificationSinkDeliverInput {
2123
+ payload: GatewayEvent | NotificationEnvelope;
2124
+ wrapped: boolean;
2125
+ }
2126
+ /**
2127
+ * Hermes Phase 3 sink contract.
2128
+ *
2129
+ * Sinks subscribe to the orchestrator event bus via `wireNotificationSinks`
2130
+ * and deliver each filtered event to a destination (chat channel, webhook
2131
+ * URL, etc.). Delivery is best-effort: no retry, no persistence.
2132
+ *
2133
+ * Sinks MUST be idempotent w.r.t. their own delivery semantics — the bus
2134
+ * may emit the same logical state transition more than once during testing
2135
+ * or recovery. Sinks should not assume one-shot semantics.
2136
+ */
2137
+ interface NotificationSink {
2138
+ /** Stable id used in config + CLI; lowercase, kebab-case. */
2139
+ readonly id: string;
2140
+ /** Sink kind literal (matches `NotificationSinkKind`). */
2141
+ readonly kind: string;
2142
+ /** One-shot delivery. Returns Ok on 2xx, Err on any other outcome. */
2143
+ deliver(input: NotificationSinkDeliverInput): Promise<NotificationDeliveryResult>;
2144
+ /** Optional teardown hook called on orchestrator stop. */
2145
+ dispose?(): Promise<void>;
2146
+ }
2147
+
2148
+ interface SlackSinkOptions {
2149
+ id: string;
2150
+ webhookUrl: string;
2151
+ fetchImpl?: typeof fetch;
2152
+ timeoutMs?: number;
2153
+ }
2154
+ /**
2155
+ * Slack sink shipped with Hermes Phase 3. Uses incoming-webhook URLs only;
2156
+ * OAuth + bot tokens are intentionally out of scope (spec D3). Sends one
2157
+ * HTTP POST per delivery and never retries — retries are the operator's
2158
+ * call via the Phase 0 webhook fanout if they need durable delivery.
2159
+ */
2160
+ declare class SlackSink implements NotificationSink {
2161
+ readonly kind = "slack";
2162
+ readonly id: string;
2163
+ private readonly webhookUrl;
2164
+ private readonly fetchImpl;
2165
+ private readonly timeoutMs;
2166
+ constructor(opts: SlackSinkOptions);
2167
+ deliver(input: NotificationSinkDeliverInput): Promise<NotificationDeliveryResult>;
2168
+ private renderEnvelope;
2169
+ private renderRawEvent;
2170
+ }
2171
+
2172
+ interface RegistryEntry {
2173
+ config: NotificationSinkConfig;
2174
+ adapter: NotificationSink;
2175
+ }
2176
+ interface FromConfigOptions {
2177
+ env: NodeJS.ProcessEnv;
2178
+ /** Optional per-sink fetch override (testing). */
2179
+ fetchImpl?: typeof fetch;
2180
+ }
2181
+ /**
2182
+ * Surfaced when a sink config refers to an unknown kind, or its env-var
2183
+ * secret cannot be resolved. Carrying the sinkId helps the doctor + CLI
2184
+ * print operator-actionable messages.
2185
+ */
2186
+ declare class SinkConfigError extends Error {
2187
+ readonly sinkId: string;
2188
+ constructor(sinkId: string, message: string);
2189
+ }
2190
+ /**
2191
+ * In-memory registry of configured notification sinks. Built once at
2192
+ * orchestrator startup from `harness.config.json` `notifications.sinks[]`.
2193
+ * Disposed on orchestrator stop.
2194
+ */
2195
+ declare class SinkRegistry {
2196
+ private readonly entries;
2197
+ private constructor();
2198
+ static fromConfig(config: NotificationsConfig, options: FromConfigOptions): SinkRegistry;
2199
+ list(): readonly RegistryEntry[];
2200
+ get(id: string): RegistryEntry | null;
2201
+ ids(): string[];
2202
+ dispose(): Promise<void>;
2203
+ }
2204
+
2205
+ interface WireParams {
2206
+ bus: EventEmitter;
2207
+ registry: SinkRegistry;
2208
+ }
2209
+ declare function wireNotificationSinks({ bus, registry }: WireParams): () => void;
2210
+
2211
+ /**
2212
+ * Phase 4 gate (degraded mode, see spec D5).
2213
+ *
2214
+ * The full design calls for `harness skill run harness-soundness-review
2215
+ * --mode skill` against materialized proposal content. The skill-mode check
2216
+ * vocabulary is not yet designed; its design is the explicit follow-up spec
2217
+ * referenced in Phase 4's Non-goals.
2218
+ *
2219
+ * In v1 we run a small set of mechanical checks inline against the proposal
2220
+ * payload. They cover the obvious structural failures (unparseable YAML,
2221
+ * empty markdown, name/regex drift) without needing an LLM. The result
2222
+ * shape mirrors the eventual soundness-review output so the downstream
2223
+ * promote step (and dashboard panel) does not need to change when
2224
+ * skill-mode lands.
2225
+ */
2226
+ declare class GateRunError extends Error {
2227
+ constructor(message: string);
2228
+ }
2229
+ interface GateResult {
2230
+ proposalId: string;
2231
+ status: SkillProposal['status'];
2232
+ findings: ProposalGateFinding[];
2233
+ runAt: string;
2234
+ }
2235
+ /**
2236
+ * Synchronously run the gate against the given proposal. The proposal is
2237
+ * read from disk, checks are computed, and the proposal JSON is patched
2238
+ * with the gate result. Returns the post-update gate snapshot for the
2239
+ * caller to render.
2240
+ */
2241
+ declare function runGate(projectPath: string, proposalId: string): Promise<GateResult>;
2242
+
2243
+ declare class GateNotReadyError extends Error {
2244
+ constructor(message: string);
2245
+ }
2246
+ declare class PromotionError extends Error {
2247
+ constructor(message: string);
2248
+ }
2249
+ interface PromotionResult {
2250
+ proposalId: string;
2251
+ skillPath: string;
2252
+ /** Provenance field stamped onto the promoted skill. */
2253
+ provenance: 'agent-proposed';
2254
+ }
2255
+ /**
2256
+ * Promote a proposal to the skill catalog. Caller is responsible for
2257
+ * emitting `proposal.approved` after a successful return.
2258
+ */
2259
+ declare function promote(projectPath: string, proposalId: string, decidedBy: string): Promise<PromotionResult>;
2260
+
2261
+ /**
2262
+ * Phase 4 — thin wrappers around the orchestrator event bus that emit the
2263
+ * three `proposal.*` lifecycle events with a stable, validated payload
2264
+ * shape. Both the webhook fan-out (gateway/webhooks/events.ts) and the
2265
+ * in-process notification dispatcher (notifications/events.ts) subscribe
2266
+ * to these topics; their envelope derivers know the field names below.
2267
+ */
2268
+ interface ProposalCreatedData {
2269
+ id: string;
2270
+ kind: SkillProposal['kind'];
2271
+ name: string;
2272
+ targetSkill?: string;
2273
+ proposedBy: string;
2274
+ justification: string;
2275
+ }
2276
+ interface ProposalApprovedData {
2277
+ id: string;
2278
+ kind: SkillProposal['kind'];
2279
+ name: string;
2280
+ targetSkill?: string;
2281
+ decidedBy: string;
2282
+ }
2283
+ interface ProposalRejectedData {
2284
+ id: string;
2285
+ kind: SkillProposal['kind'];
2286
+ name: string;
2287
+ decidedBy: string;
2288
+ reason: string;
2289
+ }
2290
+ declare function emitProposalCreated(bus: EventEmitter, proposal: SkillProposal): void;
2291
+ declare function emitProposalApproved(bus: EventEmitter, proposal: SkillProposal): void;
2292
+ declare function emitProposalRejected(bus: EventEmitter, proposal: SkillProposal): void;
2293
+
2294
+ 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, 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, WebhookQueue, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, type WorkspaceManagerOptions, applyEvent, artifactPresenceFromIssue, buildArchiveHooks, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, detectScopeTier, 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, runGate, savePublishedIndex, searchIndexPath, selectCandidates, sortCandidates, summarizeArchivedSession, syncMain, triageIssue, truncateForBudget, validateCustomTasks, validateWorkflowConfig, wireNotificationSinks, wrapAsEnvelope };