@harness-engineering/orchestrator 0.13.0 → 0.15.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.js CHANGED
@@ -32,6 +32,7 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AdaptiveRouter: () => AdaptiveRouter,
34
34
  AnalysisArchive: () => AnalysisArchive,
35
+ BRAINSTORM_RUBRIC: () => BRAINSTORM_RUBRIC,
35
36
  BUILT_IN_TASKS: () => BUILT_IN_TASKS,
36
37
  BackendDefSchema: () => BackendDefSchema,
37
38
  BackendRouter: () => BackendRouter,
@@ -74,8 +75,10 @@ __export(index_exports, {
74
75
  WorkspaceManager: () => WorkspaceManager,
75
76
  applyEvent: () => applyEvent,
76
77
  artifactPresenceFromIssue: () => artifactPresenceFromIssue,
78
+ brainstormInputFromIssue: () => brainstormInputFromIssue,
77
79
  buildArchiveHooks: () => buildArchiveHooks,
78
80
  buildCapabilityRegistry: () => buildCapabilityRegistry,
81
+ buildProbeInput: () => buildProbeInput,
79
82
  buildWorkflowContext: () => buildWorkflowContext,
80
83
  calculateRetryDelay: () => calculateRetryDelay,
81
84
  canDispatch: () => canDispatch,
@@ -95,6 +98,7 @@ __export(index_exports, {
95
98
  emitProposalApproved: () => emitProposalApproved,
96
99
  emitProposalCreated: () => emitProposalCreated,
97
100
  emitProposalRejected: () => emitProposalRejected,
101
+ enrichIssueWithSpec: () => enrichIssueWithSpec,
98
102
  estimateCost: () => estimateCost,
99
103
  explicitFindingsCount: () => explicitFindingsCount,
100
104
  extractHighlights: () => extractHighlights,
@@ -109,22 +113,30 @@ __export(index_exports, {
109
113
  launchTUI: () => launchTUI,
110
114
  loadPublishedIndex: () => loadPublishedIndex,
111
115
  makeBackendResolver: () => makeBackendResolver,
116
+ makeGraphScope: () => makeGraphScope,
117
+ makeSelForkGenerator: () => makeSelForkGenerator,
118
+ markApprovedForDispatch: () => markApprovedForDispatch,
112
119
  migrateAgentConfig: () => migrateAgentConfig,
113
120
  normalizeFts5Query: () => normalizeFts5Query,
114
121
  normalizeHarnessCommand: () => normalizeHarnessCommand,
115
122
  normalizeLocalModel: () => normalizeLocalModel,
116
123
  openSearchIndex: () => openSearchIndex,
124
+ pilotScore: () => import_intelligence13.pilotScore,
125
+ precedentLookupFromStored: () => precedentLookupFromStored,
117
126
  promote: () => promote,
127
+ rankTriageCandidates: () => import_intelligence13.rankTriageCandidates,
118
128
  reconcile: () => reconcile,
119
129
  recoverFindingsCount: () => recoverFindingsCount,
120
130
  reindexFromArchive: () => reindexFromArchive,
121
131
  renderAnalysisComment: () => renderAnalysisComment,
122
132
  renderLlmSummaryMarkdown: () => renderLlmSummaryMarkdown,
123
133
  renderPRComment: () => renderPRComment,
134
+ renderSpecMarkdown: () => renderSpecMarkdown,
124
135
  resolveEscalationConfig: () => resolveEscalationConfig,
125
136
  resolveOrchestratorId: () => resolveOrchestratorId,
126
137
  routeIssue: () => routeIssue,
127
138
  routingWarnings: () => routingWarnings,
139
+ runBrainstormForIssue: () => runBrainstormForIssue,
128
140
  runGate: () => runGate,
129
141
  runHarnessCheck: () => runHarnessCheck,
130
142
  savePublishedIndex: () => savePublishedIndex,
@@ -132,10 +144,11 @@ __export(index_exports, {
132
144
  selectCandidates: () => selectCandidates,
133
145
  selectCheapestQualifying: () => selectCheapestQualifying,
134
146
  selectTasks: () => selectTasks,
147
+ slugFor: () => slugFor,
135
148
  sortCandidates: () => sortCandidates,
136
149
  summarizeArchivedSession: () => summarizeArchivedSession,
137
150
  syncMain: () => syncMain,
138
- triageIssue: () => triageIssue,
151
+ triageIssue: () => triageIssue2,
139
152
  truncateForBudget: () => truncateForBudget,
140
153
  validateCustomTasks: () => validateCustomTasks,
141
154
  validateWorkflowConfig: () => validateWorkflowConfig,
@@ -2054,26 +2067,60 @@ var ModelSchema = import_zod.z.union([import_zod.z.string().min(1), import_zod.z
2054
2067
  message: "model must be a non-empty string or array of strings"
2055
2068
  })
2056
2069
  });
2070
+ var CAPABILITY_TIER = import_zod.z.enum(["fast", "standard", "strong"]);
2071
+ var COMPLEXITY_LEVEL = import_zod.z.enum(["trivial", "simple", "moderate", "complex"]);
2072
+ var PRIVACY_CLASS = import_zod.z.enum(["on-device", "pooled-isolated", "byo-endpoint", "shared-cloud"]);
2073
+ var BackendCapabilitiesSchema = import_zod.z.object({
2074
+ tier: CAPABILITY_TIER,
2075
+ costPer1kTokens: import_zod.z.number().nonnegative(),
2076
+ privacyClass: PRIVACY_CLASS,
2077
+ contextWindow: import_zod.z.number().int().positive(),
2078
+ vision: import_zod.z.boolean().optional(),
2079
+ toolUse: import_zod.z.boolean().optional()
2080
+ }).strict();
2081
+ var RoutingPolicySchema = import_zod.z.object({
2082
+ complexityTierMatrix: import_zod.z.record(COMPLEXITY_LEVEL, CAPABILITY_TIER).optional(),
2083
+ skillTierOverrides: import_zod.z.record(import_zod.z.string(), CAPABILITY_TIER).optional(),
2084
+ privacyFloor: PRIVACY_CLASS.optional(),
2085
+ budget: import_zod.z.object({
2086
+ capUsd: import_zod.z.number(),
2087
+ degradeAtPct: import_zod.z.number().optional(),
2088
+ onBudgetExhausted: import_zod.z.enum(["degrade", "pause", "human"])
2089
+ }).optional(),
2090
+ sensitivePaths: import_zod.z.array(import_zod.z.string()).optional(),
2091
+ escalationThreshold: import_zod.z.number().optional(),
2092
+ // string[], NOT the finite BackendDef['type'] union: an unknown provider must
2093
+ // fail CLOSED at tier selection, not reject the whole policy (Shuttle wire).
2094
+ allowedProviders: import_zod.z.array(import_zod.z.string()).optional(),
2095
+ acceptanceEval: import_zod.z.object({
2096
+ enabled: import_zod.z.boolean(),
2097
+ model: import_zod.z.string().optional()
2098
+ }).optional()
2099
+ });
2057
2100
  var BackendDefSchema = import_zod.z.discriminatedUnion("type", [
2058
- import_zod.z.object({ type: import_zod.z.literal("mock") }).strict(),
2101
+ import_zod.z.object({ type: import_zod.z.literal("mock"), capabilities: BackendCapabilitiesSchema.optional() }).strict(),
2059
2102
  import_zod.z.object({
2060
2103
  type: import_zod.z.literal("claude"),
2061
- command: import_zod.z.string().optional()
2104
+ command: import_zod.z.string().optional(),
2105
+ capabilities: BackendCapabilitiesSchema.optional()
2062
2106
  }).strict(),
2063
2107
  import_zod.z.object({
2064
2108
  type: import_zod.z.literal("anthropic"),
2065
2109
  model: import_zod.z.string().min(1),
2066
- apiKey: import_zod.z.string().optional()
2110
+ apiKey: import_zod.z.string().optional(),
2111
+ capabilities: BackendCapabilitiesSchema.optional()
2067
2112
  }).strict(),
2068
2113
  import_zod.z.object({
2069
2114
  type: import_zod.z.literal("openai"),
2070
2115
  model: import_zod.z.string().min(1),
2071
- apiKey: import_zod.z.string().optional()
2116
+ apiKey: import_zod.z.string().optional(),
2117
+ capabilities: BackendCapabilitiesSchema.optional()
2072
2118
  }).strict(),
2073
2119
  import_zod.z.object({
2074
2120
  type: import_zod.z.literal("gemini"),
2075
2121
  model: import_zod.z.string().min(1),
2076
- apiKey: import_zod.z.string().optional()
2122
+ apiKey: import_zod.z.string().optional(),
2123
+ capabilities: BackendCapabilitiesSchema.optional()
2077
2124
  }).strict(),
2078
2125
  import_zod.z.object({
2079
2126
  type: import_zod.z.literal("local"),
@@ -2081,7 +2128,8 @@ var BackendDefSchema = import_zod.z.discriminatedUnion("type", [
2081
2128
  model: ModelSchema,
2082
2129
  apiKey: import_zod.z.string().optional(),
2083
2130
  timeoutMs: import_zod.z.number().int().positive().optional(),
2084
- probeIntervalMs: import_zod.z.number().int().min(1e3).optional()
2131
+ probeIntervalMs: import_zod.z.number().int().min(1e3).optional(),
2132
+ capabilities: BackendCapabilitiesSchema.optional()
2085
2133
  }).strict(),
2086
2134
  import_zod.z.object({
2087
2135
  type: import_zod.z.literal("pi"),
@@ -2089,7 +2137,8 @@ var BackendDefSchema = import_zod.z.discriminatedUnion("type", [
2089
2137
  model: ModelSchema,
2090
2138
  apiKey: import_zod.z.string().optional(),
2091
2139
  timeoutMs: import_zod.z.number().int().positive().optional(),
2092
- probeIntervalMs: import_zod.z.number().int().min(1e3).optional()
2140
+ probeIntervalMs: import_zod.z.number().int().min(1e3).optional(),
2141
+ capabilities: BackendCapabilitiesSchema.optional()
2093
2142
  }).strict()
2094
2143
  ]);
2095
2144
  var RoutingValueSchema = import_zod.z.union([
@@ -2114,7 +2163,36 @@ var RoutingConfigSchema = import_zod.z.object({
2114
2163
  }).strict().optional(),
2115
2164
  // --- Spec B Phase 0: new optional maps (resolver wired in Phase 1) ---
2116
2165
  skills: import_zod.z.record(import_zod.z.string().min(1), RoutingValueSchema).optional(),
2117
- modes: import_zod.z.record(import_zod.z.string().min(1), RoutingValueSchema).optional()
2166
+ modes: import_zod.z.record(import_zod.z.string().min(1), RoutingValueSchema).optional(),
2167
+ // --- AMR: opt-in adaptive-routing policy. Its PRESENCE flips the orchestrator
2168
+ // from identity/default dispatch to complexity-aware tier routing (default-off
2169
+ // when absent). Previously accepted by the runtime PUT endpoint only. ---
2170
+ policy: RoutingPolicySchema.optional()
2171
+ }).strict();
2172
+ var CONFIDENCE = import_zod.z.enum(["low", "medium", "high"]);
2173
+ var V1_MAX_RATCHET_STAGE = 2;
2174
+ var RATCHET_STAGE = import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(2), import_zod.z.literal(3), import_zod.z.literal(4)]).refine((s) => s <= V1_MAX_RATCHET_STAGE, {
2175
+ message: `ratchetStage ${">"} ${V1_MAX_RATCHET_STAGE} is deferred post-v1 (stages 3\u20134 not yet supported); use 1 or 2`
2176
+ });
2177
+ var RoadmapAutoTriageSchema = import_zod.z.object({
2178
+ enabled: import_zod.z.boolean(),
2179
+ schedule: import_zod.z.string().min(1).optional(),
2180
+ ratchetStage: RATCHET_STAGE,
2181
+ thresholds: import_zod.z.object({
2182
+ dispatchConfidence: CONFIDENCE,
2183
+ boundedScopeMax: import_zod.z.number(),
2184
+ brainstormConfidence: import_zod.z.number(),
2185
+ exceededByBands: import_zod.z.number(),
2186
+ ratchetAdvanceRate: import_zod.z.number(),
2187
+ ratchetMinSample: import_zod.z.number()
2188
+ }).strict(),
2189
+ depthBudget: import_zod.z.object({
2190
+ trivial: import_zod.z.number(),
2191
+ simple: import_zod.z.number()
2192
+ }).strict()
2193
+ }).strict();
2194
+ var RoadmapConfigSchema = import_zod.z.object({
2195
+ autoTriage: RoadmapAutoTriageSchema.optional()
2118
2196
  }).strict();
2119
2197
  var WorkflowStepSchema = import_zod.z.object({
2120
2198
  skill: import_zod.z.string().min(1),
@@ -2153,12 +2231,12 @@ var BackendsMapSchema = import_zod2.z.record(import_zod2.z.string(), BackendDefS
2153
2231
  function crossFieldRoutingIssues(backends, routing) {
2154
2232
  const issues = [];
2155
2233
  const names = new Set(Object.keys(backends));
2156
- const checkRef = (path24, value) => {
2234
+ const checkRef = (path25, value) => {
2157
2235
  if (value === void 0) return;
2158
2236
  const entries = Array.isArray(value) ? value : [value];
2159
2237
  entries.forEach((name, idx) => {
2160
2238
  if (names.has(name)) return;
2161
- const pathWithIdx = Array.isArray(value) ? [...path24, String(idx)] : path24;
2239
+ const pathWithIdx = Array.isArray(value) ? [...path25, String(idx)] : path25;
2162
2240
  issues.push({
2163
2241
  path: pathWithIdx,
2164
2242
  message: `routing.${pathWithIdx.join(".")} references unknown backend '${name}'. Defined: [${[...names].join(", ")}].`
@@ -2255,6 +2333,10 @@ function validateWorkflowConfig(config, options = {}) {
2255
2333
  const parsed = import_zod2.z.array(StagedWorkflowDeclSchema).safeParse(c.workflows);
2256
2334
  if (!parsed.success) return (0, import_types2.Err)(new Error(`workflows: ${parsed.error.message}`));
2257
2335
  }
2336
+ if (c.roadmap !== void 0) {
2337
+ const parsed = RoadmapConfigSchema.safeParse(c.roadmap);
2338
+ if (!parsed.success) return (0, import_types2.Err)(new Error(`roadmap: ${parsed.error.message}`));
2339
+ }
2258
2340
  return (0, import_types2.Ok)({ config, warnings });
2259
2341
  }
2260
2342
  function getDefaultConfig() {
@@ -3497,7 +3579,65 @@ var path21 = __toESM(require("path"));
3497
3579
  var import_node_crypto15 = require("crypto");
3498
3580
  var import_types34 = require("@harness-engineering/types");
3499
3581
  var import_core16 = require("@harness-engineering/core");
3500
- var import_intelligence11 = require("@harness-engineering/intelligence");
3582
+ var import_intelligence12 = require("@harness-engineering/intelligence");
3583
+
3584
+ // src/agent/triage-outcome.ts
3585
+ var import_intelligence = require("@harness-engineering/intelligence");
3586
+ function signalsFromDiff(hunks, textOnly) {
3587
+ const files = /* @__PURE__ */ new Set();
3588
+ const layers = /* @__PURE__ */ new Set();
3589
+ let addedLines = 0;
3590
+ for (const h of hunks) {
3591
+ files.add(h.file);
3592
+ layers.add(layerOfPath(h.file));
3593
+ addedLines += h.addedContent === "" ? 0 : h.addedContent.split("\n").length;
3594
+ }
3595
+ return {
3596
+ ...textOnly,
3597
+ filesTouched: files.size,
3598
+ layersTouched: layers.size,
3599
+ blastRadius: addedLines
3600
+ };
3601
+ }
3602
+ function layerOfPath(file) {
3603
+ const parts = file.split("/").filter((p) => p.length > 0);
3604
+ if (parts.length === 0) return "<root>";
3605
+ if (parts[0] === "packages" && parts.length >= 4 && parts[2] === "src") {
3606
+ return `${parts[1] ?? ""}/${parts[3] ?? ""}`;
3607
+ }
3608
+ return parts.length === 1 ? "<root>" : parts[0] ?? "<root>";
3609
+ }
3610
+ async function runRetrospective(deps, provider) {
3611
+ const signals = signalsFromDiff(deps.hunks, deps.textOnly);
3612
+ const actual = await (0, import_intelligence.classify)(
3613
+ { signals, phase: "post-diff", riskHigh: deps.riskHigh, prompt: deps.prompt },
3614
+ provider
3615
+ );
3616
+ const comparison = deps.comparatorConfig ? (0, import_intelligence.compareToPrediction)(deps.prediction, actual, deps.comparatorConfig) : (0, import_intelligence.compareToPrediction)(deps.prediction, actual);
3617
+ return { actual, comparison };
3618
+ }
3619
+ function buildTriageOutcomeInput(externalId, shapeKey, result) {
3620
+ return {
3621
+ externalId,
3622
+ shapeKey,
3623
+ actual: result.actual,
3624
+ exceededBy: result.comparison.exceededBy,
3625
+ matched: result.comparison.matched
3626
+ };
3627
+ }
3628
+ function precedentLookupFromStored(records) {
3629
+ const bridged = records.map((r) => ({
3630
+ externalId: "",
3631
+ shapeKey: r.shapeKey,
3632
+ ts: "",
3633
+ ...r.outcome ? { outcome: { actual: void 0, exceededBy: 0, matched: r.outcome.matched } } : {}
3634
+ }));
3635
+ return {
3636
+ rateForShape: (shapeKey) => (0, import_intelligence.aggregatePrecedent)(bridged, shapeKey)
3637
+ };
3638
+ }
3639
+
3640
+ // src/orchestrator.ts
3501
3641
  var import_graph2 = require("@harness-engineering/graph");
3502
3642
 
3503
3643
  // src/core/stall-detector.ts
@@ -3518,7 +3658,7 @@ function detectStalledIssues(running, nowMs, stallTimeoutMs) {
3518
3658
 
3519
3659
  // src/intelligence/pipeline-runner.ts
3520
3660
  var path9 = __toESM(require("path"));
3521
- var import_intelligence = require("@harness-engineering/intelligence");
3661
+ var import_intelligence2 = require("@harness-engineering/intelligence");
3522
3662
  var import_core2 = require("@harness-engineering/core");
3523
3663
  var CONNECTION_ERROR_PATTERNS = [
3524
3664
  "Connection error",
@@ -3623,7 +3763,7 @@ var IntelligencePipelineRunner = class {
3623
3763
  refreshSpecializationProfiles() {
3624
3764
  if (!this.ctx.graphStore) return;
3625
3765
  try {
3626
- const store = (0, import_intelligence.refreshProfiles)(this.ctx.projectRoot, this.ctx.graphStore);
3766
+ const store = (0, import_intelligence2.refreshProfiles)(this.ctx.projectRoot, this.ctx.graphStore);
3627
3767
  const personaCount = Object.keys(store.profiles).length;
3628
3768
  if (personaCount > 0) {
3629
3769
  this.ctx.logger.info(`Refreshed specialization profiles for ${personaCount} persona(s)`);
@@ -3866,7 +4006,7 @@ var IntelligencePipelineRunner = class {
3866
4006
  for (const issue of candidates) {
3867
4007
  const systemNodeIds = issue.labels.filter((l) => l.startsWith("system:") || l.startsWith("module:")).map((l) => l.split(":")[1]).filter((id) => id.length > 0);
3868
4008
  if (systemNodeIds.length === 0) continue;
3869
- const recs = (0, import_intelligence.weightedRecommendPersona)(this.ctx.graphStore, { systemNodeIds });
4009
+ const recs = (0, import_intelligence2.weightedRecommendPersona)(this.ctx.graphStore, { systemNodeIds });
3870
4010
  if (recs.length > 0) {
3871
4011
  results.set(issue.id, recs);
3872
4012
  }
@@ -4775,11 +4915,11 @@ function detectLegacyFields(agent) {
4775
4915
  }
4776
4916
  function buildCase1Warnings(presentLegacy, suppressLocalGroup) {
4777
4917
  const warnings = [];
4778
- for (const path24 of presentLegacy) {
4779
- if (CASE1_ALWAYS_SUPPRESS.has(path24)) continue;
4780
- if (suppressLocalGroup && CASE1_LOCAL_GROUP.has(path24)) continue;
4918
+ for (const path25 of presentLegacy) {
4919
+ if (CASE1_ALWAYS_SUPPRESS.has(path25)) continue;
4920
+ if (suppressLocalGroup && CASE1_LOCAL_GROUP.has(path25)) continue;
4781
4921
  warnings.push(
4782
- `Ignoring legacy field '${path24}': 'agent.backends' is set and takes precedence. See ${MIGRATION_GUIDE}.`
4922
+ `Ignoring legacy field '${path25}': 'agent.backends' is set and takes precedence. See ${MIGRATION_GUIDE}.`
4783
4923
  );
4784
4924
  }
4785
4925
  return warnings;
@@ -4807,7 +4947,7 @@ function migrateAgentConfig(agent) {
4807
4947
  }
4808
4948
  const { backends, routing } = synthesizeBackendsAndRouting(agent);
4809
4949
  const warnings = presentLegacy.map(
4810
- (path24) => `Deprecated config field '${path24}' is in use. Migrate to 'agent.backends' / 'agent.routing'. See ${MIGRATION_GUIDE}.`
4950
+ (path25) => `Deprecated config field '${path25}' is in use. Migrate to 'agent.backends' / 'agent.routing'. See ${MIGRATION_GUIDE}.`
4811
4951
  );
4812
4952
  return {
4813
4953
  config: { ...agent, backends, routing },
@@ -4892,12 +5032,12 @@ var BackendRouter = class {
4892
5032
  */
4893
5033
  resolve(useCase, opts) {
4894
5034
  const startedAt = performance.now();
4895
- const path24 = [];
5035
+ const path25 = [];
4896
5036
  const tryChain = (source, value) => {
4897
5037
  if (value === void 0) return void 0;
4898
5038
  for (const name of toArray(value)) {
4899
5039
  const step = { source, candidate: name, outcome: "considered" };
4900
- path24.push(step);
5040
+ path25.push(step);
4901
5041
  if (this.backends[name]) {
4902
5042
  step.outcome = "chosen";
4903
5043
  return name;
@@ -4916,7 +5056,7 @@ var BackendRouter = class {
4916
5056
  return {
4917
5057
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4918
5058
  useCase,
4919
- resolutionPath: path24,
5059
+ resolutionPath: path25,
4920
5060
  backendName,
4921
5061
  backendType: def.type,
4922
5062
  durationMs: performance.now() - startedAt
@@ -4949,7 +5089,7 @@ var BackendRouter = class {
4949
5089
  if (fromDefault) return emitAndReturn(decide(fromDefault));
4950
5090
  const knownList = Object.keys(this.backends).join(", ") || "(none)";
4951
5091
  throw new Error(
4952
- `BackendRouter.resolve: routing.default produced no available backend for useCase=${JSON.stringify(useCase)}. Resolution path: ${JSON.stringify(path24)}. Known backends: [${knownList}].`
5092
+ `BackendRouter.resolve: routing.default produced no available backend for useCase=${JSON.stringify(useCase)}. Resolution path: ${JSON.stringify(path25)}. Known backends: [${knownList}].`
4953
5093
  );
4954
5094
  }
4955
5095
  /**
@@ -5042,7 +5182,7 @@ var BackendRouter = class {
5042
5182
  check(`modes.${mode}`, value);
5043
5183
  }
5044
5184
  if (missing.length > 0) {
5045
- const detail = missing.map(({ path: path24, name }) => `routing.${path24} -> '${name}'`).join("; ");
5185
+ const detail = missing.map(({ path: path25, name }) => `routing.${path25} -> '${name}'`).join("; ");
5046
5186
  const known_ = [...known].join(", ") || "(none)";
5047
5187
  throw new Error(
5048
5188
  `BackendRouter: routing references unknown backend(s): ${detail}. Defined backends: [${known_}].`
@@ -7435,11 +7575,11 @@ function createAgentDispatcher(deps) {
7435
7575
  var import_node_child_process13 = require("child_process");
7436
7576
 
7437
7577
  // src/agent/intelligence-factory.ts
7438
- var import_intelligence3 = require("@harness-engineering/intelligence");
7578
+ var import_intelligence4 = require("@harness-engineering/intelligence");
7439
7579
  var import_graph = require("@harness-engineering/graph");
7440
7580
 
7441
7581
  // src/agent/analysis-provider-factory.ts
7442
- var import_intelligence2 = require("@harness-engineering/intelligence");
7582
+ var import_intelligence3 = require("@harness-engineering/intelligence");
7443
7583
  function buildAnalysisProvider(args) {
7444
7584
  const { def, backendName, layer, intelligence, logger } = args;
7445
7585
  const layerModel = layer === "sel" ? intelligence?.models?.sel : intelligence?.models?.pesl;
@@ -7479,7 +7619,7 @@ function buildLocalLikeProvider(def, args, layerModel) {
7479
7619
  logger.info(
7480
7620
  `Intelligence pipeline using backend '${backendName}' (${def.type}) at ${def.endpoint} (model: ${model ?? "(default)"})`
7481
7621
  );
7482
- return new import_intelligence2.OpenAICompatibleAnalysisProvider({
7622
+ return new import_intelligence3.OpenAICompatibleAnalysisProvider({
7483
7623
  apiKey,
7484
7624
  baseUrl: def.endpoint,
7485
7625
  ...model !== void 0 && { defaultModel: model },
@@ -7497,7 +7637,7 @@ function buildAnthropicProvider(def, args, layerModel) {
7497
7637
  const apiKey = def.apiKey ?? process.env.ANTHROPIC_API_KEY;
7498
7638
  const model = layerModel ?? def.model;
7499
7639
  if (apiKey) {
7500
- return new import_intelligence2.AnthropicAnalysisProvider({
7640
+ return new import_intelligence3.AnthropicAnalysisProvider({
7501
7641
  apiKey,
7502
7642
  ...model !== void 0 && { defaultModel: model }
7503
7643
  });
@@ -7505,7 +7645,7 @@ function buildAnthropicProvider(def, args, layerModel) {
7505
7645
  args.logger.info(
7506
7646
  `Intelligence pipeline routed to '${args.backendName}' (anthropic) without API key \u2014 using Claude CLI fallback.`
7507
7647
  );
7508
- return new import_intelligence2.ClaudeCliAnalysisProvider({
7648
+ return new import_intelligence3.ClaudeCliAnalysisProvider({
7509
7649
  ...model !== void 0 && { defaultModel: model },
7510
7650
  ...args.intelligence?.requestTimeoutMs !== void 0 && {
7511
7651
  timeoutMs: args.intelligence.requestTimeoutMs
@@ -7521,7 +7661,7 @@ function buildOpenAIProvider(def, args, layerModel) {
7521
7661
  return null;
7522
7662
  }
7523
7663
  const model = layerModel ?? def.model;
7524
- return new import_intelligence2.OpenAICompatibleAnalysisProvider({
7664
+ return new import_intelligence3.OpenAICompatibleAnalysisProvider({
7525
7665
  apiKey,
7526
7666
  baseUrl: "https://api.openai.com/v1",
7527
7667
  ...model !== void 0 && { defaultModel: model },
@@ -7531,7 +7671,7 @@ function buildOpenAIProvider(def, args, layerModel) {
7531
7671
  });
7532
7672
  }
7533
7673
  function buildClaudeCliProvider(def, args, layerModel) {
7534
- return new import_intelligence2.ClaudeCliAnalysisProvider({
7674
+ return new import_intelligence3.ClaudeCliAnalysisProvider({
7535
7675
  ...def.command !== void 0 && { command: def.command },
7536
7676
  ...layerModel !== void 0 && { defaultModel: layerModel },
7537
7677
  ...args.intelligence?.requestTimeoutMs !== void 0 && {
@@ -7552,7 +7692,7 @@ function buildIntelligencePipeline(deps) {
7552
7692
  const peslProvider = peslName !== selName ? buildAnalysisProviderForLayer("pesl", deps) : null;
7553
7693
  const peslModel = intel.models?.pesl ?? config.agent.model;
7554
7694
  const graphStore = new import_graph.GraphStore();
7555
- const pipeline = new import_intelligence3.IntelligencePipeline(selProvider, graphStore, {
7695
+ const pipeline = new import_intelligence4.IntelligencePipeline(selProvider, graphStore, {
7556
7696
  ...peslModel !== void 0 && { peslModel },
7557
7697
  ...peslProvider !== null && peslProvider !== void 0 && { peslProvider }
7558
7698
  });
@@ -7618,13 +7758,13 @@ function buildExplicitProvider(provider, selModel, config) {
7618
7758
  if (!apiKey2) {
7619
7759
  throw new Error("Intelligence pipeline: no Anthropic API key found.");
7620
7760
  }
7621
- return new import_intelligence3.AnthropicAnalysisProvider({
7761
+ return new import_intelligence4.AnthropicAnalysisProvider({
7622
7762
  apiKey: apiKey2,
7623
7763
  ...selModel !== void 0 && { defaultModel: selModel }
7624
7764
  });
7625
7765
  }
7626
7766
  if (provider.kind === "claude-cli") {
7627
- return new import_intelligence3.ClaudeCliAnalysisProvider({
7767
+ return new import_intelligence4.ClaudeCliAnalysisProvider({
7628
7768
  command: config.agent.command,
7629
7769
  ...selModel !== void 0 && { defaultModel: selModel },
7630
7770
  ...config.intelligence?.requestTimeoutMs !== void 0 && {
@@ -7635,7 +7775,7 @@ function buildExplicitProvider(provider, selModel, config) {
7635
7775
  const apiKey = provider.apiKey ?? config.agent.apiKey ?? "ollama";
7636
7776
  const baseUrl = provider.baseUrl ?? "http://localhost:11434/v1";
7637
7777
  const intel = config.intelligence;
7638
- return new import_intelligence3.OpenAICompatibleAnalysisProvider({
7778
+ return new import_intelligence4.OpenAICompatibleAnalysisProvider({
7639
7779
  apiKey,
7640
7780
  baseUrl,
7641
7781
  ...selModel !== void 0 && { defaultModel: selModel },
@@ -7646,13 +7786,13 @@ function buildExplicitProvider(provider, selModel, config) {
7646
7786
  }
7647
7787
 
7648
7788
  // src/agent/adaptive-router.ts
7649
- var import_intelligence6 = require("@harness-engineering/intelligence");
7789
+ var import_intelligence7 = require("@harness-engineering/intelligence");
7650
7790
  var import_types24 = require("@harness-engineering/types");
7651
7791
 
7652
7792
  // src/agent/capability-registry.ts
7653
7793
  var import_types23 = require("@harness-engineering/types");
7654
7794
  var import_local_models2 = require("@harness-engineering/local-models");
7655
- var import_intelligence4 = require("@harness-engineering/intelligence");
7795
+ var import_intelligence5 = require("@harness-engineering/intelligence");
7656
7796
  var PrivacyNoMatch = class extends import_types23.RoutingError {
7657
7797
  code = "privacy-no-match";
7658
7798
  constructor(message) {
@@ -7667,7 +7807,7 @@ var PRIVACY_RANK = {
7667
7807
  "shared-cloud": 3
7668
7808
  };
7669
7809
  function selectCheapestQualifying(registry, requiredTier, constraints, providerOf) {
7670
- const requiredRank = import_intelligence4.TIER_RANK[requiredTier];
7810
+ const requiredRank = import_intelligence5.TIER_RANK[requiredTier];
7671
7811
  const entries = [...registry.entries()].map(([name, capabilities]) => ({
7672
7812
  name,
7673
7813
  capabilities
@@ -7689,7 +7829,7 @@ function selectCheapestQualifying(registry, requiredTier, constraints, providerO
7689
7829
  }
7690
7830
  const qualifying = passesPrivacyAllow.filter((e) => {
7691
7831
  const c = e.capabilities;
7692
- if (import_intelligence4.TIER_RANK[c.tier] < requiredRank) return false;
7832
+ if (import_intelligence5.TIER_RANK[c.tier] < requiredRank) return false;
7693
7833
  if (constraints.needsVision && !c.vision) return false;
7694
7834
  if (constraints.needsToolUse && !c.toolUse) return false;
7695
7835
  if (constraints.minContextTokens !== void 0 && c.contextWindow < constraints.minContextTokens)
@@ -7728,7 +7868,7 @@ function estimateCost(def, _req) {
7728
7868
  }
7729
7869
 
7730
7870
  // src/agent/escalation-state.ts
7731
- var import_intelligence5 = require("@harness-engineering/intelligence");
7871
+ var import_intelligence6 = require("@harness-engineering/intelligence");
7732
7872
  var EscalationState = class {
7733
7873
  constructor(threshold = 2) {
7734
7874
  this.threshold = threshold;
@@ -7757,7 +7897,7 @@ var EscalationState = class {
7757
7897
  climbedUnits() {
7758
7898
  const out = [];
7759
7899
  for (const [coherenceUnit, state] of this.units) {
7760
- if (import_intelligence5.TIER_RANK[state.floorTier] > import_intelligence5.TIER_RANK.fast) {
7900
+ if (import_intelligence6.TIER_RANK[state.floorTier] > import_intelligence6.TIER_RANK.fast) {
7761
7901
  out.push({ coherenceUnit, floor: state.floorTier });
7762
7902
  }
7763
7903
  }
@@ -7796,14 +7936,14 @@ var EscalationState = class {
7796
7936
  return "ok";
7797
7937
  }
7798
7938
  state.failures = 0;
7799
- const currentRank = import_intelligence5.TIER_RANK[state.floorTier];
7800
- if (currentRank >= import_intelligence5.TIER_RANK.strong) {
7939
+ const currentRank = import_intelligence6.TIER_RANK[state.floorTier];
7940
+ if (currentRank >= import_intelligence6.TIER_RANK.strong) {
7801
7941
  state.floorTier = "strong";
7802
7942
  state.escalated = true;
7803
7943
  this.units.set(coherenceUnit, state);
7804
7944
  return "exhausted";
7805
7945
  }
7806
- state.floorTier = import_intelligence5.RANK_TIER[currentRank + 1];
7946
+ state.floorTier = import_intelligence6.RANK_TIER[currentRank + 1];
7807
7947
  state.escalated = true;
7808
7948
  this.units.set(coherenceUnit, state);
7809
7949
  return "escalated";
@@ -7939,7 +8079,7 @@ var AdaptiveRouter = class _AdaptiveRouter {
7939
8079
  let budgetStatus = null;
7940
8080
  if (budget && budget.capUsd > 0) {
7941
8081
  const spentUsd = this.getSpentUsd();
7942
- const degradeAtPct = budget.degradeAtPct ?? import_intelligence6.DEFAULT_DEGRADE_AT_PCT;
8082
+ const degradeAtPct = budget.degradeAtPct ?? import_intelligence7.DEFAULT_DEGRADE_AT_PCT;
7943
8083
  budgetStatus = {
7944
8084
  spentUsd,
7945
8085
  capUsd: budget.capUsd,
@@ -7971,8 +8111,8 @@ var AdaptiveRouter = class _AdaptiveRouter {
7971
8111
  );
7972
8112
  }
7973
8113
  const unitFloor = this.deps.escalation?.floorFor(req.coherenceUnit) ?? "fast";
7974
- const escalationFloor = import_intelligence6.RANK_TIER[Math.max(import_intelligence6.TIER_RANK[unitFloor], import_intelligence6.TIER_RANK[req.floor ?? "fast"])];
7975
- const requiredTier = (0, import_intelligence6.deriveRequiredTier)(
8114
+ const escalationFloor = import_intelligence7.RANK_TIER[Math.max(import_intelligence7.TIER_RANK[unitFloor], import_intelligence7.TIER_RANK[req.floor ?? "fast"])];
8115
+ const requiredTier = (0, import_intelligence7.deriveRequiredTier)(
7976
8116
  complexity,
7977
8117
  req.risk,
7978
8118
  this.deps.policy,
@@ -8129,10 +8269,10 @@ var RoutingDecisionBus = class {
8129
8269
  };
8130
8270
 
8131
8271
  // src/workflow/execute-workflow.ts
8132
- var import_intelligence7 = require("@harness-engineering/intelligence");
8272
+ var import_intelligence8 = require("@harness-engineering/intelligence");
8133
8273
  function nextTier(t) {
8134
- const next = Math.min(import_intelligence7.TIER_RANK[t] + 1, import_intelligence7.TIER_RANK.strong);
8135
- return import_intelligence7.RANK_TIER[next];
8274
+ const next = Math.min(import_intelligence8.TIER_RANK[t] + 1, import_intelligence8.TIER_RANK.strong);
8275
+ return import_intelligence8.RANK_TIER[next];
8136
8276
  }
8137
8277
  var DEFAULT_STAGE_DEADLINE_MS = 12e4;
8138
8278
  function stageAttemptKey(stageIndex, attempt) {
@@ -8354,7 +8494,7 @@ function buildRoutingUseCase(issue, backendParam, catalog) {
8354
8494
  }
8355
8495
 
8356
8496
  // src/agent/live-classify.ts
8357
- var import_intelligence8 = require("@harness-engineering/intelligence");
8497
+ var import_intelligence9 = require("@harness-engineering/intelligence");
8358
8498
  var CONSERVATIVE = {
8359
8499
  level: "moderate",
8360
8500
  confidence: "low",
@@ -8377,7 +8517,7 @@ function makeLiveClassify(resolveProvider) {
8377
8517
  riskHigh,
8378
8518
  prompt: taskText.prompt
8379
8519
  };
8380
- return (0, import_intelligence8.classify)(input, resolveProvider());
8520
+ return (0, import_intelligence9.classify)(input, resolveProvider());
8381
8521
  };
8382
8522
  }
8383
8523
 
@@ -8900,7 +9040,7 @@ function extractChunks(event) {
8900
9040
  }
8901
9041
 
8902
9042
  // src/server/routes/analyze.ts
8903
- var import_intelligence9 = require("@harness-engineering/intelligence");
9043
+ var import_intelligence10 = require("@harness-engineering/intelligence");
8904
9044
  var import_zod7 = require("zod");
8905
9045
  var AnalyzeRequestSchema = import_zod7.z.object({
8906
9046
  title: import_zod7.z.string().min(1),
@@ -8930,7 +9070,7 @@ async function runPipeline(res, pipeline, parsed) {
8930
9070
  disconnected = true;
8931
9071
  });
8932
9072
  emit2(res, { type: "status", text: "Converting to work item..." });
8933
- const rawItem = (0, import_intelligence9.manualToRawWorkItem)({
9073
+ const rawItem = (0, import_intelligence10.manualToRawWorkItem)({
8934
9074
  title: parsed.title,
8935
9075
  description: parsed.description ?? "",
8936
9076
  labels: parsed.labels ?? []
@@ -8973,7 +9113,7 @@ async function runPipeline(res, pipeline, parsed) {
8973
9113
  }
8974
9114
  }
8975
9115
  if (disconnected) return;
8976
- const signals = (0, import_intelligence9.scoreToConcernSignals)(score);
9116
+ const signals = (0, import_intelligence10.scoreToConcernSignals)(score);
8977
9117
  if (signals.length > 0) {
8978
9118
  emit2(res, { type: "signals", data: signals });
8979
9119
  }
@@ -10633,7 +10773,7 @@ function handleV1LocalModelsMutationRoute(req, res, deps) {
10633
10773
 
10634
10774
  // src/server/routes/v1/routing.ts
10635
10775
  var import_zod14 = require("zod");
10636
- var import_intelligence10 = require("@harness-engineering/intelligence");
10776
+ var import_intelligence11 = require("@harness-engineering/intelligence");
10637
10777
  var CONFIG_RE = /^\/api\/v1\/routing\/config(?:\?.*)?$/;
10638
10778
  var DECISIONS_RE = /^\/api\/v1\/routing\/decisions(?:\?.*)?$/;
10639
10779
  var TRACE_RE = /^\/api\/v1\/routing\/trace(?:\?.*)?$/;
@@ -10737,7 +10877,7 @@ function deriveTraceCost(body, decision, def, routing, backends) {
10737
10877
  source: "static"
10738
10878
  };
10739
10879
  const risk = body.risk === "high" ? { blastRadius: 10, sensitivePath: true } : { blastRadius: 0, sensitivePath: false };
10740
- const tierRequired = (0, import_intelligence10.deriveRequiredTier)(
10880
+ const tierRequired = (0, import_intelligence11.deriveRequiredTier)(
10741
10881
  verdict,
10742
10882
  risk,
10743
10883
  routing.policy ?? {},
@@ -10752,7 +10892,12 @@ function deriveTraceCost(body, decision, def, routing, backends) {
10752
10892
  backends
10753
10893
  );
10754
10894
  const estCostUsd = estimateCost(costedDef, { useCase: body.useCase });
10755
- return { tierRequired, estCostUsd, costedBackendName: costedName };
10895
+ return {
10896
+ tierRequired,
10897
+ estCostUsd,
10898
+ costedBackendName: costedName,
10899
+ costedBackendType: costedDef.type
10900
+ };
10756
10901
  }
10757
10902
  function selectCostedBackend(tierRequired, decision, def, routing, backends) {
10758
10903
  const registry = buildCapabilityRegistry(backends);
@@ -10817,7 +10962,7 @@ async function handleTrace(req, res, deps) {
10817
10962
  opts
10818
10963
  );
10819
10964
  if (r.data.complexity !== void 0 || r.data.risk !== void 0) {
10820
- const { tierRequired, estCostUsd, costedBackendName } = deriveTraceCost(
10965
+ const { tierRequired, estCostUsd, costedBackendName, costedBackendType } = deriveTraceCost(
10821
10966
  r.data,
10822
10967
  decision,
10823
10968
  def,
@@ -10829,9 +10974,12 @@ async function handleTrace(req, res, deps) {
10829
10974
  def: { type: def.type },
10830
10975
  tierRequired,
10831
10976
  estCostUsd,
10832
- // Name the backend the cost belongs to so operators see tier↔cost↔backend
10833
- // are consistent (was implicit + divergent before this fix).
10834
- costedBackendName
10977
+ // The backend AMR would ACTUALLY dispatch to at this tier (cheapest
10978
+ // qualifying) + its type distinct from the identity-chain `decision`
10979
+ // above. The CLI shows this as the effective backend so tier↔cost↔backend
10980
+ // read consistently (was implicit + divergent before).
10981
+ costedBackendName,
10982
+ costedBackendType
10835
10983
  });
10836
10984
  return true;
10837
10985
  }
@@ -10841,26 +10989,6 @@ async function handleTrace(req, res, deps) {
10841
10989
  }
10842
10990
  return true;
10843
10991
  }
10844
- var CAPABILITY_TIER = import_zod14.z.enum(["fast", "standard", "strong"]);
10845
- var COMPLEXITY_LEVEL = import_zod14.z.enum(["trivial", "simple", "moderate", "complex"]);
10846
- var PRIVACY_CLASS = import_zod14.z.enum(["on-device", "byo-endpoint", "shared-cloud"]);
10847
- var RoutingPolicySchema = import_zod14.z.object({
10848
- complexityTierMatrix: import_zod14.z.record(COMPLEXITY_LEVEL, CAPABILITY_TIER).optional(),
10849
- skillTierOverrides: import_zod14.z.record(import_zod14.z.string(), CAPABILITY_TIER).optional(),
10850
- privacyFloor: PRIVACY_CLASS.optional(),
10851
- budget: import_zod14.z.object({
10852
- capUsd: import_zod14.z.number(),
10853
- degradeAtPct: import_zod14.z.number().optional(),
10854
- onBudgetExhausted: import_zod14.z.enum(["degrade", "pause", "human"])
10855
- }).optional(),
10856
- sensitivePaths: import_zod14.z.array(import_zod14.z.string()).optional(),
10857
- escalationThreshold: import_zod14.z.number().optional(),
10858
- allowedProviders: import_zod14.z.array(import_zod14.z.string()).optional(),
10859
- acceptanceEval: import_zod14.z.object({
10860
- enabled: import_zod14.z.boolean(),
10861
- model: import_zod14.z.string().optional()
10862
- }).optional()
10863
- });
10864
10992
  async function handlePolicy(req, res, deps) {
10865
10993
  if (!deps.ingestRoutingPolicy || deps.router === null) return unavailable(res);
10866
10994
  let raw;
@@ -11406,8 +11534,8 @@ function parseToken(raw) {
11406
11534
  return { id: raw.slice(0, dot), secret: raw.slice(dot + 1) };
11407
11535
  }
11408
11536
  var TokenStore = class {
11409
- constructor(path24) {
11410
- this.path = path24;
11537
+ constructor(path25) {
11538
+ this.path = path25;
11411
11539
  }
11412
11540
  path;
11413
11541
  cache = null;
@@ -11514,8 +11642,8 @@ var import_promises2 = require("fs/promises");
11514
11642
  var import_node_path2 = require("path");
11515
11643
  var import_types29 = require("@harness-engineering/types");
11516
11644
  var AuditLogger = class {
11517
- constructor(path24, opts = {}) {
11518
- this.path = path24;
11645
+ constructor(path25, opts = {}) {
11646
+ this.path = path25;
11519
11647
  this.opts = opts;
11520
11648
  }
11521
11649
  path;
@@ -11753,9 +11881,9 @@ var V1_BRIDGE_ROUTES = [
11753
11881
  function isV1Bridge(method, url) {
11754
11882
  return V1_BRIDGE_ROUTES.some((r) => r.method === method && r.pattern.test(url));
11755
11883
  }
11756
- function requiredBridgeScope(method, path24) {
11884
+ function requiredBridgeScope(method, path25) {
11757
11885
  for (const r of V1_BRIDGE_ROUTES) {
11758
- if (r.method === method && r.pattern.test(path24)) return r.scope;
11886
+ if (r.method === method && r.pattern.test(path25)) return r.scope;
11759
11887
  }
11760
11888
  return null;
11761
11889
  }
@@ -11765,11 +11893,11 @@ function hasScope(held, required) {
11765
11893
  if (held.includes("admin")) return true;
11766
11894
  return held.includes(required);
11767
11895
  }
11768
- function exactScopeForRoute(method, path24) {
11769
- if (path24 === "/api/v1/auth/token" && method === "POST") return "admin";
11770
- if (path24 === "/api/v1/auth/tokens" && method === "GET") return "admin";
11771
- if (/^\/api\/v1\/auth\/tokens\/[^/]+$/.test(path24) && method === "DELETE") return "admin";
11772
- if ((path24 === "/api/state" || path24 === "/api/v1/state") && method === "GET") return "read-status";
11896
+ function exactScopeForRoute(method, path25) {
11897
+ if (path25 === "/api/v1/auth/token" && method === "POST") return "admin";
11898
+ if (path25 === "/api/v1/auth/tokens" && method === "GET") return "admin";
11899
+ if (/^\/api\/v1\/auth\/tokens\/[^/]+$/.test(path25) && method === "DELETE") return "admin";
11900
+ if ((path25 === "/api/state" || path25 === "/api/v1/state") && method === "GET") return "read-status";
11773
11901
  return null;
11774
11902
  }
11775
11903
  var PREFIX_SCOPES = [
@@ -11786,19 +11914,19 @@ var PREFIX_SCOPES = [
11786
11914
  ["/api/sessions", "read-status"],
11787
11915
  ["/api/chat-proxy", "trigger-job"]
11788
11916
  ];
11789
- function prefixScopeForPath(path24) {
11790
- if (path24 === "/api/chat") return "trigger-job";
11917
+ function prefixScopeForPath(path25) {
11918
+ if (path25 === "/api/chat") return "trigger-job";
11791
11919
  for (const [prefix, scope] of PREFIX_SCOPES) {
11792
- if (path24.startsWith(prefix)) return scope;
11920
+ if (path25.startsWith(prefix)) return scope;
11793
11921
  }
11794
11922
  return null;
11795
11923
  }
11796
- function requiredScopeForRoute(method, path24) {
11797
- const bridgeScope = requiredBridgeScope(method, path24);
11924
+ function requiredScopeForRoute(method, path25) {
11925
+ const bridgeScope = requiredBridgeScope(method, path25);
11798
11926
  if (bridgeScope) return bridgeScope;
11799
- const exactScope = exactScopeForRoute(method, path24);
11927
+ const exactScope = exactScopeForRoute(method, path25);
11800
11928
  if (exactScope) return exactScope;
11801
- return prefixScopeForPath(path24);
11929
+ return prefixScopeForPath(path25);
11802
11930
  }
11803
11931
 
11804
11932
  // src/server/http.ts
@@ -12327,8 +12455,8 @@ function genSecret2() {
12327
12455
  return (0, import_node_crypto10.randomBytes)(32).toString("base64url");
12328
12456
  }
12329
12457
  var WebhookStore = class {
12330
- constructor(path24) {
12331
- this.path = path24;
12458
+ constructor(path25) {
12459
+ this.path = path25;
12332
12460
  }
12333
12461
  path;
12334
12462
  cache = null;
@@ -15173,8 +15301,8 @@ function validateCheckShape(prefix, task, errors) {
15173
15301
  });
15174
15302
  }
15175
15303
  if (hasScript) {
15176
- const path24 = task.checkScript?.path;
15177
- if (!path24 || path24.trim().length === 0) {
15304
+ const path25 = task.checkScript?.path;
15305
+ if (!path25 || path25.trim().length === 0) {
15178
15306
  errors.push({ path: `${prefix}.checkScript.path`, message: "checkScript.path is required" });
15179
15307
  }
15180
15308
  if (task.checkScript?.timeoutMs !== void 0 && task.checkScript.timeoutMs <= 0) {
@@ -15300,9 +15428,9 @@ function handleEdge(top, next, color, stack, errors, reported) {
15300
15428
  stack.push({ id: next, nextIdx: 0, path: [...top.path, next] });
15301
15429
  }
15302
15430
  }
15303
- function reportCycle(path24, next, errors, reported) {
15304
- const cycleStart = path24.indexOf(next);
15305
- const cyclePath = cycleStart >= 0 ? [...path24.slice(cycleStart), next] : [...path24, next];
15431
+ function reportCycle(path25, next, errors, reported) {
15432
+ const cycleStart = path25.indexOf(next);
15433
+ const cyclePath = cycleStart >= 0 ? [...path25.slice(cycleStart), next] : [...path25, next];
15306
15434
  const key = cyclePath.join("\u2192");
15307
15435
  if (reported.has(key)) return;
15308
15436
  reported.add(key);
@@ -16790,7 +16918,9 @@ ${messages}`);
16790
16918
  await this.emitWorkerExit(issue.id, "error", attempt, "Stopped by reconciliation");
16791
16919
  }
16792
16920
  } else {
16793
- const outcomeClass = await this.deriveSingleAgentQualityVerdict(issue, workspacePath);
16921
+ const qualityClass = await this.deriveSingleAgentQualityVerdict(issue, workspacePath);
16922
+ const retroClass = await this.deriveRoutingRetrospectiveVerdict(issue, workspacePath);
16923
+ const outcomeClass = qualityClass ?? retroClass;
16794
16924
  await this.emitWorkerExit(issue.id, "normal", attempt, void 0, outcomeClass);
16795
16925
  }
16796
16926
  } catch (error) {
@@ -16865,7 +16995,7 @@ ${messages}`);
16865
16995
  try {
16866
16996
  const diff = await this.workspace.getIntroducedDiffText(issue.identifier);
16867
16997
  if (diff.trim() === "") return void 0;
16868
- const evaluator = new import_intelligence11.OutcomeEvaluator(provider, this.graphStore ?? new import_graph2.GraphStore(), {
16998
+ const evaluator = new import_intelligence12.OutcomeEvaluator(provider, this.graphStore ?? new import_graph2.GraphStore(), {
16869
16999
  ...acceptanceEval.model !== void 0 ? { model: acceptanceEval.model } : {}
16870
17000
  });
16871
17001
  const verdict = await evaluator.evaluate({
@@ -16892,6 +17022,150 @@ ${messages}`);
16892
17022
  return void 0;
16893
17023
  }
16894
17024
  }
17025
+ /**
17026
+ * Roadmap Auto-Triage Phase 4 (SC1–SC3, SC5, SC7): the post-diff routing
17027
+ * retrospective — a SIBLING quality-verdict source to the 4c feeder above,
17028
+ * extending (not replacing) the proven escalation path. On a normal exit, when AMR
17029
+ * is live AND auto-triage is enabled AND this unit carries a stored pre-dispatch
17030
+ * prediction, it classifies the ACTUAL introduced diff at full strength
17031
+ * (phase:'post-diff') and grades it against that prediction:
17032
+ * - MATCH → records the graded outcome + annotates the PR "AI autonomous —
17033
+ * verify" (stage-2 human-verify handling, SC4) → returns `undefined`
17034
+ * (neutral: a match is not an escalation).
17035
+ * - MISMATCH (diff exceeded prediction, or a missing/garbled prediction, or ANY
17036
+ * error) → records the outcome (when a shape is known) + returns
17037
+ * `'quality-fail'`, which climbs the coherence unit's escalation floor
17038
+ * via `recordAmrOutcome`; exhaustion queues `needs-human` (SC3/SC7).
17039
+ *
17040
+ * Fail-safe & guarded (SC7): an error never a silent pass — it takes the MISMATCH
17041
+ * (block+escalate) path. No-op (byte-identical) when AMR is off, auto-triage is
17042
+ * off, or the unit was not dispatched through triage (no stored prediction) — the
17043
+ * last means an ordinary non-triaged run is never graded.
17044
+ */
17045
+ async deriveRoutingRetrospectiveVerdict(issue, _workspacePath) {
17046
+ if (this.adaptiveRouter === null) return void 0;
17047
+ if (this.config.roadmap?.autoTriage?.enabled !== true) return void 0;
17048
+ const externalId = issue.externalId;
17049
+ if (externalId === null || externalId === "") return void 0;
17050
+ let record;
17051
+ try {
17052
+ const loaded = await import_core17.eventSourcing.loadTriageRecords(this.projectRoot);
17053
+ if (!loaded.ok) throw loaded.error;
17054
+ record = loaded.value.find((r) => r.externalId === externalId);
17055
+ } catch (err) {
17056
+ this.logger.debug("amr retrospective skipped (record load failed)", {
17057
+ issueId: issue.id,
17058
+ error: err instanceof Error ? err.message : String(err)
17059
+ });
17060
+ if (issue.spec !== null && issue.spec !== void 0 && issue.spec !== "") {
17061
+ this.logger.info(
17062
+ "amr:quality-fail \u2014 spec-bearing (triaged) unit but the triage store is unreadable (fail-safe block: a triaged unit whose prediction we cannot read never silently passes)",
17063
+ { issueId: issue.id, externalId }
17064
+ );
17065
+ return "quality-fail";
17066
+ }
17067
+ return void 0;
17068
+ }
17069
+ if (record === void 0) return void 0;
17070
+ const stored = record.prediction;
17071
+ if (stored === void 0) {
17072
+ this.logger.info(
17073
+ "amr:quality-fail \u2014 triaged unit has no stored prediction (fail-safe block)",
17074
+ {
17075
+ issueId: issue.id,
17076
+ externalId
17077
+ }
17078
+ );
17079
+ return "quality-fail";
17080
+ }
17081
+ const prediction = {
17082
+ verdict: stored.verdict,
17083
+ levers: stored.levers,
17084
+ scopeEstimate: stored.scopeEstimate,
17085
+ ratchetStage: stored.ratchetStage
17086
+ };
17087
+ try {
17088
+ const hunks = await this.workspace.getIntroducedDiff(issue.identifier);
17089
+ const taskText = buildTaskText(issue);
17090
+ const exceededByBands = this.config.roadmap?.autoTriage?.thresholds?.exceededByBands ?? 1;
17091
+ const result = await runRetrospective(
17092
+ {
17093
+ hunks,
17094
+ textOnly: {
17095
+ descriptionLength: taskText.descriptionLength,
17096
+ specExists: taskText.specExists,
17097
+ acceptanceMeasurable: taskText.acceptanceMeasurable
17098
+ },
17099
+ prediction,
17100
+ riskHigh: false,
17101
+ prompt: taskText.prompt,
17102
+ comparatorConfig: { exceededByBands }
17103
+ },
17104
+ this.resolveComplexityProvider()
17105
+ );
17106
+ const outcomeInput = buildTriageOutcomeInput(externalId, record.shapeKey, result);
17107
+ const recorded = await import_core17.eventSourcing.recordTriageOutcome(this.projectRoot, outcomeInput);
17108
+ if (!recorded.ok) {
17109
+ this.logger.warn("amr retrospective outcome record failed (best-effort)", {
17110
+ issueId: issue.id,
17111
+ error: recorded.error.message
17112
+ });
17113
+ }
17114
+ if (result.comparison.action === "block-escalate") {
17115
+ this.logger.info("amr:quality-fail \u2014 post-diff retrospective mispredict (block+escalate)", {
17116
+ issueId: issue.id,
17117
+ externalId,
17118
+ predicted: prediction.verdict.level,
17119
+ actual: result.actual.level,
17120
+ exceededBy: result.comparison.exceededBy
17121
+ });
17122
+ return "quality-fail";
17123
+ }
17124
+ await this.annotateRetrospectiveMatch(issue, externalId, result.actual.level);
17125
+ return void 0;
17126
+ } catch (err) {
17127
+ this.logger.info("amr:quality-fail \u2014 post-diff retrospective errored (fail-safe block)", {
17128
+ issueId: issue.id,
17129
+ externalId,
17130
+ error: err instanceof Error ? err.message : String(err)
17131
+ });
17132
+ return "quality-fail";
17133
+ }
17134
+ }
17135
+ /**
17136
+ * v1 stage-2 match handling (SC4): annotate the unit's PR/issue with a "verify this
17137
+ * autonomous change" note so a human reviews every autonomous PR. Best-effort — a
17138
+ * missing tracker config / token / a failed comment never breaks completion (the
17139
+ * grade already recorded above). Mirrors `postLifecycleComment`'s adapter wiring.
17140
+ */
17141
+ async annotateRetrospectiveMatch(issue, externalId, actualLevel) {
17142
+ try {
17143
+ const trackerConfig = (0, import_core17.loadTrackerSyncConfig)(this.projectRoot);
17144
+ if (!trackerConfig) return;
17145
+ const token = process.env.GITHUB_TOKEN;
17146
+ if (!token) return;
17147
+ const orchestratorId = await this.orchestratorIdPromise;
17148
+ const adapter = new import_core17.GitHubIssuesSyncAdapter({ token, config: trackerConfig });
17149
+ const body = [
17150
+ `**AI autonomous change \u2014 please verify** \`${orchestratorId}\``,
17151
+ "",
17152
+ "The post-diff retrospective classified this change WITHIN its pre-dispatch",
17153
+ `prediction (actual complexity: \`${actualLevel}\`). Under autonomy ratchet`,
17154
+ "stage 2, a human must verify every autonomous PR before it merges."
17155
+ ].join("\n");
17156
+ const result = await adapter.addComment(externalId, body);
17157
+ if (!result.ok) {
17158
+ this.logger.warn(`amr retrospective annotation failed for ${issue.identifier}`, {
17159
+ error: result.error.message
17160
+ });
17161
+ }
17162
+ } catch (err) {
17163
+ this.logger.debug("amr retrospective annotation skipped (best-effort)", {
17164
+ issueId: issue.id,
17165
+ error: err instanceof Error ? err.message : String(err)
17166
+ });
17167
+ }
17168
+ }
16895
17169
  /**
16896
17170
  * Informs the state machine that an agent worker has exited.
16897
17171
  */
@@ -17978,6 +18252,333 @@ function launchTUI(orchestrator) {
17978
18252
  return { waitUntilExit };
17979
18253
  }
17980
18254
 
18255
+ // src/agent/triage-wiring.ts
18256
+ var import_graph3 = require("@harness-engineering/graph");
18257
+ var import_intelligence13 = require("@harness-engineering/intelligence");
18258
+ function buildProbeInput(issue) {
18259
+ const taskText = buildTaskText(issue);
18260
+ const combined = `${issue.title ?? ""}
18261
+ ${issue.description ?? ""}`.trim();
18262
+ return {
18263
+ // An item lacking a stable externalId is not dispatch-eligible (proposal Assumptions);
18264
+ // we still probe it (read-only report), keyed by its identifier as a fallback.
18265
+ externalId: issue.externalId ?? issue.identifier ?? issue.id,
18266
+ taskText,
18267
+ entityCandidates: (0, import_intelligence13.extractEntities)(combined),
18268
+ labels: issue.labels ?? []
18269
+ };
18270
+ }
18271
+ function findNode(store, candidate) {
18272
+ const trimmed = candidate.trim();
18273
+ if (trimmed.length === 0) return null;
18274
+ const byName = store.findNodes({ name: trimmed });
18275
+ if (byName.length > 0) return byName[0] ?? null;
18276
+ const byPath = store.findNodes({ path: trimmed });
18277
+ if (byPath.length > 0) return byPath[0] ?? null;
18278
+ const byId = store.getNode(`file:${trimmed}`);
18279
+ if (byId) return byId;
18280
+ return null;
18281
+ }
18282
+ function makeGraphScope(store) {
18283
+ const simulator = new import_graph3.CascadeSimulator(store);
18284
+ return {
18285
+ resolve(candidate) {
18286
+ try {
18287
+ const node = findNode(store, candidate);
18288
+ if (!node) return null;
18289
+ const result = simulator.simulate(node.id);
18290
+ return {
18291
+ candidate,
18292
+ nodeId: node.id,
18293
+ blastRadius: result.summary.totalAffected
18294
+ };
18295
+ } catch {
18296
+ return null;
18297
+ }
18298
+ }
18299
+ };
18300
+ }
18301
+ async function triageIssue2(issue, deps = {}) {
18302
+ const input = buildProbeInput(issue);
18303
+ const graph = deps.graphStore ? makeGraphScope(deps.graphStore) : void 0;
18304
+ return (0, import_intelligence13.runScopingProbe)(input, {
18305
+ ...deps.provider ? { provider: deps.provider } : {},
18306
+ ...graph ? { graph } : {},
18307
+ ...deps.precedent ? { precedent: deps.precedent } : {},
18308
+ ...deps.config ? { config: deps.config } : {},
18309
+ ...deps.models ? { models: deps.models } : {}
18310
+ });
18311
+ }
18312
+
18313
+ // src/agent/brainstorm-wiring.ts
18314
+ var fs16 = __toESM(require("fs"));
18315
+ var path22 = __toESM(require("path"));
18316
+ var import_zod18 = require("zod");
18317
+ var import_intelligence14 = require("@harness-engineering/intelligence");
18318
+ var ForkStepSchema = import_zod18.z.object({
18319
+ done: import_zod18.z.boolean().describe("true when there are no more design forks to decide"),
18320
+ forkId: import_zod18.z.string().describe('short stable id for this fork, e.g. "storage-backend"').optional(),
18321
+ question: import_zod18.z.string().describe("the design question this fork decides").optional(),
18322
+ options: import_zod18.z.array(import_zod18.z.string()).describe("the mutually-exclusive options considered").optional(),
18323
+ // The AUTHORITATIVE recommendation validity checks (non-empty AND one of `options`) live in
18324
+ // the GENERATOR, where `fork.options` is known and where a degenerate value must map to a
18325
+ // `confidence:'low'` DOWNGRADE (→ runner halts with reason 'low-confidence'), not a hard
18326
+ // schema/provider throw. We deliberately keep this `optional()`/unconstrained so an empty or
18327
+ // absent recommendation still reaches the generator's semantic gate rather than short-
18328
+ // circuiting to `halted{ reason:'error' }`. See makeSelForkGenerator's SAFETY GATE 2.
18329
+ recommendation: import_zod18.z.string().describe("the recommended option (one of options)").optional(),
18330
+ confidence: import_zod18.z.enum(["high", "medium", "low"]).describe("self-assessed confidence in the recommendation").optional(),
18331
+ rationale: import_zod18.z.string().describe("why this option").optional()
18332
+ });
18333
+ var BRAINSTORM_RUBRIC = 'You are running an AUTONOMOUS brainstorm over one backlog item: enumerate each design fork (a decision with mutually-exclusive options), then recommend a default for the NEXT unresolved fork with a self-assessed confidence. Report confidence HIGH only when the recommendation is clearly correct on technical grounds alone. Report LOW (so a human decides) whenever the fork involves ANY of: a product or UX tradeoff, an unrevealed business priority, a security-sensitive or irreversible/outward-facing action, or a genuine judgment call between comparable options. Never invent forks to pad the spec; set done=true once every real fork is resolved. Be conservative \u2014 a false "high" is far more costly than asking a human.';
18334
+ function makeSelForkGenerator(provider, input, opts = {}) {
18335
+ const samples = Math.max(2, opts.samples ?? 3);
18336
+ const maxTokens = opts.maxTokens ?? 512;
18337
+ return {
18338
+ async next(index, priorDecisions) {
18339
+ const prompt = buildForkPrompt(input, priorDecisions, index);
18340
+ const steps = [];
18341
+ for (let s = 0; s < samples; s++) {
18342
+ const { result } = await provider.analyze({
18343
+ prompt,
18344
+ systemPrompt: BRAINSTORM_RUBRIC,
18345
+ responseSchema: ForkStepSchema,
18346
+ maxTokens,
18347
+ ...opts.model !== void 0 ? { model: opts.model } : {}
18348
+ });
18349
+ steps.push(result);
18350
+ }
18351
+ if (steps.some((st) => st.done)) return null;
18352
+ const first = steps[0];
18353
+ if (!first) return null;
18354
+ const fork = {
18355
+ id: first.forkId ?? `fork-${index}`,
18356
+ question: first.question ?? "unspecified fork",
18357
+ options: first.options ?? []
18358
+ };
18359
+ const recommendation = first.recommendation ?? "";
18360
+ const normQ = (q) => (q ?? "").trim().toLowerCase();
18361
+ const firstForkId = first.forkId ?? "";
18362
+ const firstQuestion = normQ(first.question);
18363
+ const identityStable = steps.every(
18364
+ (st) => (st.forkId ?? "") === firstForkId && normQ(st.question) === firstQuestion
18365
+ );
18366
+ const recStable = steps.every((st) => (st.recommendation ?? "") === recommendation);
18367
+ const recEmpty = recommendation.trim() === "";
18368
+ const recNotAnOption = !recEmpty && fork.options.length > 0 && !fork.options.includes(recommendation);
18369
+ const stable = identityStable && recStable && !recEmpty && !recNotAnOption;
18370
+ const reported = first.confidence ?? "low";
18371
+ const confidence = stable ? reported : "low";
18372
+ let downgradeReason = "";
18373
+ if (!identityStable) {
18374
+ downgradeReason = `fork identity drift across ${samples} samples (self-consistency downgrade to low): ` + steps.map((st) => `${st.forkId ?? "\u2205"}:"${(st.question ?? "\u2205").trim()}"`).join(" / ");
18375
+ } else if (recEmpty) {
18376
+ downgradeReason = "recommendation was empty/absent (a content-free recommendation is not a confident decision; downgrade to low)";
18377
+ } else if (recNotAnOption) {
18378
+ downgradeReason = `recommendation '${recommendation}' is not one of the offered options [${fork.options.join(", ")}] (downgrade to low)`;
18379
+ } else if (!recStable) {
18380
+ downgradeReason = `recommendation was unstable across ${samples} samples (self-consistency downgrade to low): ` + steps.map((st) => st.recommendation ?? "\u2205").join(" / ");
18381
+ }
18382
+ return {
18383
+ fork,
18384
+ recommendation,
18385
+ confidence,
18386
+ rationale: stable ? first.rationale ?? "" : downgradeReason
18387
+ };
18388
+ }
18389
+ };
18390
+ }
18391
+ function buildForkPrompt(input, priorDecisions, index) {
18392
+ const resolved = priorDecisions.length === 0 ? "(none yet)" : priorDecisions.map((d, i) => `${i + 1}. ${d.fork.question} \u2192 ${d.recommendation}`).join("\n");
18393
+ return `Item: ${input.title}
18394
+ Summary: ${input.summary}
18395
+ Complexity: ${input.level}
18396
+
18397
+ Forks already resolved:
18398
+ ${resolved}
18399
+
18400
+ Decide fork #${index + 1}: the NEXT unresolved design fork. If every real fork is already resolved, return { "done": true }.`;
18401
+ }
18402
+ function brainstormInputFromIssue(issue, level) {
18403
+ return {
18404
+ externalId: issue.externalId ?? issue.identifier ?? issue.id,
18405
+ title: issue.title ?? issue.identifier ?? issue.id,
18406
+ summary: issue.description ?? "",
18407
+ level
18408
+ };
18409
+ }
18410
+ async function runBrainstormForIssue(issue, level, deps) {
18411
+ const input = brainstormInputFromIssue(issue, level);
18412
+ const generator = deps.generator ?? (deps.provider ? makeSelForkGenerator(deps.provider, input, deps.generatorOptions ?? {}) : void 0);
18413
+ if (!generator) {
18414
+ return {
18415
+ outcome: {
18416
+ kind: "halted",
18417
+ fork: { id: "no-generator", question: "no fork generator or provider wired", options: [] },
18418
+ reason: "error",
18419
+ detail: "runBrainstormForIssue requires either an injected generator or a SEL provider"
18420
+ }
18421
+ };
18422
+ }
18423
+ const depth = (0, import_intelligence14.depthForLevel)(level);
18424
+ const outcome = await (0, import_intelligence14.runAutoBrainstorm)(input, generator, depth);
18425
+ if (outcome.kind !== "completed") {
18426
+ return { outcome };
18427
+ }
18428
+ const result = { outcome };
18429
+ if (deps.docsRoot) {
18430
+ try {
18431
+ result.specPath = writeSpecDoc(deps.docsRoot, outcome.spec);
18432
+ } catch {
18433
+ }
18434
+ }
18435
+ if (deps.rescore) {
18436
+ try {
18437
+ const enrichedIssue = enrichIssueWithSpec(issue, outcome.spec);
18438
+ result.rescore = await triageIssue2(enrichedIssue, {
18439
+ ...deps.rescore.graphStore ? { graphStore: deps.rescore.graphStore } : {},
18440
+ ...deps.rescore.provider ? { provider: deps.rescore.provider } : {},
18441
+ ...deps.rescore.precedent ? { precedent: deps.rescore.precedent } : {},
18442
+ ...deps.rescore.config ? { config: deps.rescore.config } : {},
18443
+ ...deps.rescore.models ? { models: deps.rescore.models } : {}
18444
+ });
18445
+ } catch {
18446
+ }
18447
+ }
18448
+ return result;
18449
+ }
18450
+ function slugFor(spec) {
18451
+ const base = spec.title || spec.externalId;
18452
+ return base.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "item";
18453
+ }
18454
+ function renderSpecMarkdown(spec) {
18455
+ const lines = [];
18456
+ lines.push(`# ${spec.title}`);
18457
+ lines.push("");
18458
+ lines.push("Status: **Auto-drafted (autonomous brainstorm \u2014 awaiting human review)**");
18459
+ lines.push("");
18460
+ lines.push(`Item: \`${spec.externalId}\``);
18461
+ lines.push("");
18462
+ lines.push("## Summary");
18463
+ lines.push("");
18464
+ lines.push(spec.summary || "_No summary provided._");
18465
+ lines.push("");
18466
+ lines.push("## Design decisions");
18467
+ lines.push("");
18468
+ if (spec.decisions.length === 0) {
18469
+ lines.push("_No forks required a decision \u2014 the item was unambiguous._");
18470
+ } else {
18471
+ for (const [i, d] of spec.decisions.entries()) {
18472
+ lines.push(`### ${i + 1}. ${d.fork.question}`);
18473
+ lines.push("");
18474
+ if (d.fork.options.length > 0) {
18475
+ lines.push(`Options considered: ${d.fork.options.map((o) => `\`${o}\``).join(", ")}`);
18476
+ lines.push("");
18477
+ }
18478
+ lines.push(`**Recommendation:** ${d.recommendation} (confidence: ${d.confidence})`);
18479
+ lines.push("");
18480
+ if (d.rationale) {
18481
+ lines.push(d.rationale);
18482
+ lines.push("");
18483
+ }
18484
+ }
18485
+ }
18486
+ lines.push("---");
18487
+ lines.push("");
18488
+ lines.push(
18489
+ "_This spec was drafted autonomously. Every fork above cleared the high-confidence bar; any fork the system could not confidently resolve would have halted the brainstorm and handed that fork to a human instead._"
18490
+ );
18491
+ lines.push("");
18492
+ return lines.join("\n");
18493
+ }
18494
+ function writeSpecDoc(docsRoot, spec) {
18495
+ const dir = path22.join(docsRoot, "changes", slugFor(spec));
18496
+ fs16.mkdirSync(dir, { recursive: true });
18497
+ const file = path22.join(dir, "proposal.md");
18498
+ fs16.writeFileSync(file, renderSpecMarkdown(spec), "utf-8");
18499
+ return file;
18500
+ }
18501
+ function enrichIssueWithSpec(issue, spec) {
18502
+ const decisionText = spec.decisions.map((d) => `${d.fork.question} \u2192 ${d.recommendation}. ${d.rationale}`).join("\n");
18503
+ const enrichedDescription = [issue.description ?? "", decisionText].filter(Boolean).join("\n\n");
18504
+ return {
18505
+ ...issue,
18506
+ description: enrichedDescription,
18507
+ // The item now HAS a drafted spec — reflect it so the re-score's spec-exists signal is true.
18508
+ spec: issue.spec ?? `changes/${slugFor(spec)}/proposal.md`
18509
+ };
18510
+ }
18511
+
18512
+ // src/agent/triage-mark.ts
18513
+ var import_core20 = require("@harness-engineering/core");
18514
+ var import_intelligence15 = require("@harness-engineering/intelligence");
18515
+ var ELIGIBLE_STATUS = "planned";
18516
+ async function markApprovedForDispatch(items, deps) {
18517
+ const { store, config, recordPrediction, selfAssignee } = deps;
18518
+ if (!config.enabled) {
18519
+ return { ok: true, value: { marked: [], skipped: [] } };
18520
+ }
18521
+ const loaded = await store.load();
18522
+ if (!loaded.ok) return { ok: false, error: loaded.error };
18523
+ const bySlug = /* @__PURE__ */ new Map();
18524
+ for (const m of loaded.value.milestones) {
18525
+ for (const f of m.features) bySlug.set((0, import_core20.slugifyFeatureName)(f.name), f);
18526
+ }
18527
+ const marked = [];
18528
+ const skipped = [];
18529
+ for (const it of items) {
18530
+ const externalId = it.candidate.externalId;
18531
+ if (!it.candidate.humanApproved) {
18532
+ skipped.push({ externalId, reason: "not-approved" });
18533
+ continue;
18534
+ }
18535
+ const slug = (0, import_core20.slugifyFeatureName)(it.featureName);
18536
+ const target = bySlug.get(slug);
18537
+ if (!target) {
18538
+ skipped.push({ externalId, reason: "feature-not-found" });
18539
+ continue;
18540
+ }
18541
+ if (selfAssignee !== void 0 && target.assignee != null && target.assignee !== selfAssignee) {
18542
+ skipped.push({ externalId, reason: "assigned-elsewhere" });
18543
+ continue;
18544
+ }
18545
+ const mutate = (f) => ({
18546
+ ...f,
18547
+ spec: it.specPath,
18548
+ status: isActiveStatus(f.status) ? f.status : ELIGIBLE_STATUS
18549
+ });
18550
+ const patched = await store.patchFeature(slug, mutate);
18551
+ if (!patched.ok) return { ok: false, error: patched.error };
18552
+ const key = (0, import_intelligence15.dispatchableShapeKey)(it.labels, it.verdict.level);
18553
+ const prediction = {
18554
+ externalId,
18555
+ shapeKey: key,
18556
+ verdict: it.verdict,
18557
+ // `levers` is an OPAQUE DIAGNOSTIC SNAPSHOT (the verdict's static signals), NOT the
18558
+ // typed ProbeLevers. The Phase-4 comparator grades on `verdict.level` +
18559
+ // `scopeEstimate` ONLY (see retrospective.ts) and never reads `levers`, so this
18560
+ // human-legible signal dump cannot mislead the grade. The full typed ProbeLevers
18561
+ // are not threaded here on purpose (it would balloon ReadyCandidate→marker for a
18562
+ // field nothing downstream consumes); `record.ts` re-documents the field to match.
18563
+ levers: it.verdict.signals,
18564
+ scopeEstimate: it.scopeEstimate,
18565
+ // Prefer the per-shape evidence-derived stage (SC6) when the caller resolved one;
18566
+ // fall back to the uniform config stage (Phase-3 behavior). Cold-start ⇒ the caller
18567
+ // resolves stage 1, so this is byte-identical to `config.ratchetStage` at cold-start.
18568
+ ratchetStage: it.effectiveStage ?? config.ratchetStage
18569
+ };
18570
+ const recorded = await recordPrediction(prediction);
18571
+ if (!recorded.ok) {
18572
+ return { ok: false, error: recorded.error ?? new Error("recordPrediction failed") };
18573
+ }
18574
+ marked.push(externalId);
18575
+ }
18576
+ return { ok: true, value: { marked, skipped } };
18577
+ }
18578
+ function isActiveStatus(status) {
18579
+ return status === "planned" || status === "in-progress";
18580
+ }
18581
+
17981
18582
  // src/maintenance/sync-main.ts
17982
18583
  var import_node_child_process14 = require("child_process");
17983
18584
  var import_node_util5 = require("util");
@@ -18171,8 +18772,8 @@ function selectTasks(tasks, history, filter) {
18171
18772
  }
18172
18773
 
18173
18774
  // src/sessions/search-index.ts
18174
- var fs16 = __toESM(require("fs"));
18175
- var path22 = __toESM(require("path"));
18775
+ var fs17 = __toESM(require("fs"));
18776
+ var path23 = __toESM(require("path"));
18176
18777
  var import_better_sqlite32 = __toESM(require("better-sqlite3"));
18177
18778
  var import_types35 = require("@harness-engineering/types");
18178
18779
  var SEARCH_INDEX_FILE = "search-index.sqlite";
@@ -18217,7 +18818,7 @@ function normalizeFts5Query(query) {
18217
18818
  return query.split(/\s+/).filter((tok) => tok.length > 0).map((tok) => `"${tok.replace(/"/g, '""')}"`).join(" ");
18218
18819
  }
18219
18820
  function searchIndexPath(projectPath) {
18220
- return path22.join(projectPath, ".harness", SEARCH_INDEX_FILE);
18821
+ return path23.join(projectPath, ".harness", SEARCH_INDEX_FILE);
18221
18822
  }
18222
18823
  var FILE_KIND_TO_FILENAME = {
18223
18824
  summary: "summary.md",
@@ -18232,7 +18833,7 @@ var SqliteSearchIndex = class {
18232
18833
  removeSessionStmt;
18233
18834
  totalStmt;
18234
18835
  constructor(dbPath) {
18235
- fs16.mkdirSync(path22.dirname(dbPath), { recursive: true });
18836
+ fs17.mkdirSync(path23.dirname(dbPath), { recursive: true });
18236
18837
  this.db = new import_better_sqlite32.default(dbPath);
18237
18838
  this.db.pragma("journal_mode = WAL");
18238
18839
  this.db.pragma("synchronous = NORMAL");
@@ -18337,14 +18938,14 @@ function indexSessionDirectory(idx, args) {
18337
18938
  let docsWritten = 0;
18338
18939
  for (const kind of kinds) {
18339
18940
  const fileName = FILE_KIND_TO_FILENAME[kind];
18340
- const filePath = path22.join(args.sessionDir, fileName);
18341
- if (!fs16.existsSync(filePath)) continue;
18342
- let body = fs16.readFileSync(filePath, "utf8");
18941
+ const filePath = path23.join(args.sessionDir, fileName);
18942
+ if (!fs17.existsSync(filePath)) continue;
18943
+ let body = fs17.readFileSync(filePath, "utf8");
18343
18944
  if (Buffer.byteLength(body, "utf8") > cap) {
18344
18945
  body = body.slice(0, cap) + "\n\n[TRUNCATED]";
18345
18946
  }
18346
- const stat = fs16.statSync(filePath);
18347
- const relPath = path22.relative(args.projectPath, filePath).replaceAll("\\", "/");
18947
+ const stat = fs17.statSync(filePath);
18948
+ const relPath = path23.relative(args.projectPath, filePath).replaceAll("\\", "/");
18348
18949
  idx.upsertSessionDoc({
18349
18950
  sessionId: args.sessionId,
18350
18951
  archived: args.archived,
@@ -18359,17 +18960,17 @@ function indexSessionDirectory(idx, args) {
18359
18960
  }
18360
18961
  function reindexFromArchive(projectPath, opts = {}) {
18361
18962
  const start = Date.now();
18362
- const archiveBase = path22.join(projectPath, ".harness", "archive", "sessions");
18963
+ const archiveBase = path23.join(projectPath, ".harness", "archive", "sessions");
18363
18964
  const idx = openSearchIndex(projectPath);
18364
18965
  try {
18365
18966
  idx.resetArchived();
18366
18967
  let sessionsIndexed = 0;
18367
18968
  let docsWritten = 0;
18368
- if (fs16.existsSync(archiveBase)) {
18369
- const entries = fs16.readdirSync(archiveBase, { withFileTypes: true });
18969
+ if (fs17.existsSync(archiveBase)) {
18970
+ const entries = fs17.readdirSync(archiveBase, { withFileTypes: true });
18370
18971
  for (const entry of entries) {
18371
18972
  if (!entry.isDirectory()) continue;
18372
- const sessionDir = path22.join(archiveBase, entry.name);
18973
+ const sessionDir = path23.join(archiveBase, entry.name);
18373
18974
  const result = indexSessionDirectory(idx, {
18374
18975
  sessionId: entry.name,
18375
18976
  sessionDir,
@@ -18389,8 +18990,8 @@ function reindexFromArchive(projectPath, opts = {}) {
18389
18990
  }
18390
18991
 
18391
18992
  // src/sessions/summarize.ts
18392
- var fs17 = __toESM(require("fs"));
18393
- var path23 = __toESM(require("path"));
18993
+ var fs18 = __toESM(require("fs"));
18994
+ var path24 = __toESM(require("path"));
18394
18995
  var import_types36 = require("@harness-engineering/types");
18395
18996
  var import_types37 = require("@harness-engineering/types");
18396
18997
  var LLM_SUMMARY_FILE = "llm-summary.md";
@@ -18418,10 +19019,10 @@ var USER_PROMPT_PREAMBLE = `Below are the archived files for a single harness-en
18418
19019
  function readInputCorpus(archiveDir) {
18419
19020
  const parts = [];
18420
19021
  for (const { filename, kind } of SUMMARY_INPUT_FILES) {
18421
- const p = path23.join(archiveDir, filename);
18422
- if (!fs17.existsSync(p)) continue;
19022
+ const p = path24.join(archiveDir, filename);
19023
+ if (!fs18.existsSync(p)) continue;
18423
19024
  try {
18424
- const content = fs17.readFileSync(p, "utf8");
19025
+ const content = fs18.readFileSync(p, "utf8");
18425
19026
  if (content.trim().length === 0) continue;
18426
19027
  parts.push(`## FILE: ${kind}
18427
19028
 
@@ -18472,7 +19073,7 @@ function renderLlmSummaryMarkdown(summary, meta) {
18472
19073
  return lines.join("\n");
18473
19074
  }
18474
19075
  function writeStubMarkdown(archiveDir, reason) {
18475
- const filePath = path23.join(archiveDir, LLM_SUMMARY_FILE);
19076
+ const filePath = path24.join(archiveDir, LLM_SUMMARY_FILE);
18476
19077
  const body = `---
18477
19078
  generatedAt: ${(/* @__PURE__ */ new Date()).toISOString()}
18478
19079
  schemaVersion: 1
@@ -18483,12 +19084,12 @@ status: failed
18483
19084
 
18484
19085
  - reason: ${reason}
18485
19086
  `;
18486
- fs17.writeFileSync(filePath, body, "utf8");
19087
+ fs18.writeFileSync(filePath, body, "utf8");
18487
19088
  return filePath;
18488
19089
  }
18489
19090
  async function summarizeArchivedSession(ctx) {
18490
19091
  const writeStubOnError = ctx.writeStubOnError ?? true;
18491
- if (!fs17.existsSync(ctx.archiveDir)) {
19092
+ if (!fs18.existsSync(ctx.archiveDir)) {
18492
19093
  return (0, import_types37.Err)(new Error(`archive directory not found: ${ctx.archiveDir}`));
18493
19094
  }
18494
19095
  const corpus = readInputCorpus(ctx.archiveDir);
@@ -18549,9 +19150,9 @@ async function summarizeArchivedSession(ctx) {
18549
19150
  outputTokens: response.tokenUsage.outputTokens,
18550
19151
  schemaVersion: 1
18551
19152
  };
18552
- const filePath = path23.join(ctx.archiveDir, LLM_SUMMARY_FILE);
19153
+ const filePath = path24.join(ctx.archiveDir, LLM_SUMMARY_FILE);
18553
19154
  const body = renderLlmSummaryMarkdown(parsed.data, meta);
18554
- fs17.writeFileSync(filePath, body, "utf8");
19155
+ fs18.writeFileSync(filePath, body, "utf8");
18555
19156
  return (0, import_types37.Ok)({ summary: parsed.data, meta, filePath });
18556
19157
  }
18557
19158
  function isSummaryEnabled(config) {
@@ -18633,6 +19234,7 @@ var import_local_models7 = require("@harness-engineering/local-models");
18633
19234
  0 && (module.exports = {
18634
19235
  AdaptiveRouter,
18635
19236
  AnalysisArchive,
19237
+ BRAINSTORM_RUBRIC,
18636
19238
  BUILT_IN_TASKS,
18637
19239
  BackendDefSchema,
18638
19240
  BackendRouter,
@@ -18675,8 +19277,10 @@ var import_local_models7 = require("@harness-engineering/local-models");
18675
19277
  WorkspaceManager,
18676
19278
  applyEvent,
18677
19279
  artifactPresenceFromIssue,
19280
+ brainstormInputFromIssue,
18678
19281
  buildArchiveHooks,
18679
19282
  buildCapabilityRegistry,
19283
+ buildProbeInput,
18680
19284
  buildWorkflowContext,
18681
19285
  calculateRetryDelay,
18682
19286
  canDispatch,
@@ -18696,6 +19300,7 @@ var import_local_models7 = require("@harness-engineering/local-models");
18696
19300
  emitProposalApproved,
18697
19301
  emitProposalCreated,
18698
19302
  emitProposalRejected,
19303
+ enrichIssueWithSpec,
18699
19304
  estimateCost,
18700
19305
  explicitFindingsCount,
18701
19306
  extractHighlights,
@@ -18710,22 +19315,30 @@ var import_local_models7 = require("@harness-engineering/local-models");
18710
19315
  launchTUI,
18711
19316
  loadPublishedIndex,
18712
19317
  makeBackendResolver,
19318
+ makeGraphScope,
19319
+ makeSelForkGenerator,
19320
+ markApprovedForDispatch,
18713
19321
  migrateAgentConfig,
18714
19322
  normalizeFts5Query,
18715
19323
  normalizeHarnessCommand,
18716
19324
  normalizeLocalModel,
18717
19325
  openSearchIndex,
19326
+ pilotScore,
19327
+ precedentLookupFromStored,
18718
19328
  promote,
19329
+ rankTriageCandidates,
18719
19330
  reconcile,
18720
19331
  recoverFindingsCount,
18721
19332
  reindexFromArchive,
18722
19333
  renderAnalysisComment,
18723
19334
  renderLlmSummaryMarkdown,
18724
19335
  renderPRComment,
19336
+ renderSpecMarkdown,
18725
19337
  resolveEscalationConfig,
18726
19338
  resolveOrchestratorId,
18727
19339
  routeIssue,
18728
19340
  routingWarnings,
19341
+ runBrainstormForIssue,
18729
19342
  runGate,
18730
19343
  runHarnessCheck,
18731
19344
  savePublishedIndex,
@@ -18733,6 +19346,7 @@ var import_local_models7 = require("@harness-engineering/local-models");
18733
19346
  selectCandidates,
18734
19347
  selectCheapestQualifying,
18735
19348
  selectTasks,
19349
+ slugFor,
18736
19350
  sortCandidates,
18737
19351
  summarizeArchivedSession,
18738
19352
  syncMain,