@harness-engineering/orchestrator 0.14.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,
@@ -2156,6 +2169,31 @@ var RoutingConfigSchema = import_zod.z.object({
2156
2169
  // when absent). Previously accepted by the runtime PUT endpoint only. ---
2157
2170
  policy: RoutingPolicySchema.optional()
2158
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()
2196
+ }).strict();
2159
2197
  var WorkflowStepSchema = import_zod.z.object({
2160
2198
  skill: import_zod.z.string().min(1),
2161
2199
  produces: import_zod.z.string().min(1),
@@ -2193,12 +2231,12 @@ var BackendsMapSchema = import_zod2.z.record(import_zod2.z.string(), BackendDefS
2193
2231
  function crossFieldRoutingIssues(backends, routing) {
2194
2232
  const issues = [];
2195
2233
  const names = new Set(Object.keys(backends));
2196
- const checkRef = (path24, value) => {
2234
+ const checkRef = (path25, value) => {
2197
2235
  if (value === void 0) return;
2198
2236
  const entries = Array.isArray(value) ? value : [value];
2199
2237
  entries.forEach((name, idx) => {
2200
2238
  if (names.has(name)) return;
2201
- const pathWithIdx = Array.isArray(value) ? [...path24, String(idx)] : path24;
2239
+ const pathWithIdx = Array.isArray(value) ? [...path25, String(idx)] : path25;
2202
2240
  issues.push({
2203
2241
  path: pathWithIdx,
2204
2242
  message: `routing.${pathWithIdx.join(".")} references unknown backend '${name}'. Defined: [${[...names].join(", ")}].`
@@ -2295,6 +2333,10 @@ function validateWorkflowConfig(config, options = {}) {
2295
2333
  const parsed = import_zod2.z.array(StagedWorkflowDeclSchema).safeParse(c.workflows);
2296
2334
  if (!parsed.success) return (0, import_types2.Err)(new Error(`workflows: ${parsed.error.message}`));
2297
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
+ }
2298
2340
  return (0, import_types2.Ok)({ config, warnings });
2299
2341
  }
2300
2342
  function getDefaultConfig() {
@@ -3537,7 +3579,65 @@ var path21 = __toESM(require("path"));
3537
3579
  var import_node_crypto15 = require("crypto");
3538
3580
  var import_types34 = require("@harness-engineering/types");
3539
3581
  var import_core16 = require("@harness-engineering/core");
3540
- 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
3541
3641
  var import_graph2 = require("@harness-engineering/graph");
3542
3642
 
3543
3643
  // src/core/stall-detector.ts
@@ -3558,7 +3658,7 @@ function detectStalledIssues(running, nowMs, stallTimeoutMs) {
3558
3658
 
3559
3659
  // src/intelligence/pipeline-runner.ts
3560
3660
  var path9 = __toESM(require("path"));
3561
- var import_intelligence = require("@harness-engineering/intelligence");
3661
+ var import_intelligence2 = require("@harness-engineering/intelligence");
3562
3662
  var import_core2 = require("@harness-engineering/core");
3563
3663
  var CONNECTION_ERROR_PATTERNS = [
3564
3664
  "Connection error",
@@ -3663,7 +3763,7 @@ var IntelligencePipelineRunner = class {
3663
3763
  refreshSpecializationProfiles() {
3664
3764
  if (!this.ctx.graphStore) return;
3665
3765
  try {
3666
- 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);
3667
3767
  const personaCount = Object.keys(store.profiles).length;
3668
3768
  if (personaCount > 0) {
3669
3769
  this.ctx.logger.info(`Refreshed specialization profiles for ${personaCount} persona(s)`);
@@ -3906,7 +4006,7 @@ var IntelligencePipelineRunner = class {
3906
4006
  for (const issue of candidates) {
3907
4007
  const systemNodeIds = issue.labels.filter((l) => l.startsWith("system:") || l.startsWith("module:")).map((l) => l.split(":")[1]).filter((id) => id.length > 0);
3908
4008
  if (systemNodeIds.length === 0) continue;
3909
- const recs = (0, import_intelligence.weightedRecommendPersona)(this.ctx.graphStore, { systemNodeIds });
4009
+ const recs = (0, import_intelligence2.weightedRecommendPersona)(this.ctx.graphStore, { systemNodeIds });
3910
4010
  if (recs.length > 0) {
3911
4011
  results.set(issue.id, recs);
3912
4012
  }
@@ -4815,11 +4915,11 @@ function detectLegacyFields(agent) {
4815
4915
  }
4816
4916
  function buildCase1Warnings(presentLegacy, suppressLocalGroup) {
4817
4917
  const warnings = [];
4818
- for (const path24 of presentLegacy) {
4819
- if (CASE1_ALWAYS_SUPPRESS.has(path24)) continue;
4820
- 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;
4821
4921
  warnings.push(
4822
- `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}.`
4823
4923
  );
4824
4924
  }
4825
4925
  return warnings;
@@ -4847,7 +4947,7 @@ function migrateAgentConfig(agent) {
4847
4947
  }
4848
4948
  const { backends, routing } = synthesizeBackendsAndRouting(agent);
4849
4949
  const warnings = presentLegacy.map(
4850
- (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}.`
4851
4951
  );
4852
4952
  return {
4853
4953
  config: { ...agent, backends, routing },
@@ -4932,12 +5032,12 @@ var BackendRouter = class {
4932
5032
  */
4933
5033
  resolve(useCase, opts) {
4934
5034
  const startedAt = performance.now();
4935
- const path24 = [];
5035
+ const path25 = [];
4936
5036
  const tryChain = (source, value) => {
4937
5037
  if (value === void 0) return void 0;
4938
5038
  for (const name of toArray(value)) {
4939
5039
  const step = { source, candidate: name, outcome: "considered" };
4940
- path24.push(step);
5040
+ path25.push(step);
4941
5041
  if (this.backends[name]) {
4942
5042
  step.outcome = "chosen";
4943
5043
  return name;
@@ -4956,7 +5056,7 @@ var BackendRouter = class {
4956
5056
  return {
4957
5057
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4958
5058
  useCase,
4959
- resolutionPath: path24,
5059
+ resolutionPath: path25,
4960
5060
  backendName,
4961
5061
  backendType: def.type,
4962
5062
  durationMs: performance.now() - startedAt
@@ -4989,7 +5089,7 @@ var BackendRouter = class {
4989
5089
  if (fromDefault) return emitAndReturn(decide(fromDefault));
4990
5090
  const knownList = Object.keys(this.backends).join(", ") || "(none)";
4991
5091
  throw new Error(
4992
- `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}].`
4993
5093
  );
4994
5094
  }
4995
5095
  /**
@@ -5082,7 +5182,7 @@ var BackendRouter = class {
5082
5182
  check(`modes.${mode}`, value);
5083
5183
  }
5084
5184
  if (missing.length > 0) {
5085
- const detail = missing.map(({ path: path24, name }) => `routing.${path24} -> '${name}'`).join("; ");
5185
+ const detail = missing.map(({ path: path25, name }) => `routing.${path25} -> '${name}'`).join("; ");
5086
5186
  const known_ = [...known].join(", ") || "(none)";
5087
5187
  throw new Error(
5088
5188
  `BackendRouter: routing references unknown backend(s): ${detail}. Defined backends: [${known_}].`
@@ -7475,11 +7575,11 @@ function createAgentDispatcher(deps) {
7475
7575
  var import_node_child_process13 = require("child_process");
7476
7576
 
7477
7577
  // src/agent/intelligence-factory.ts
7478
- var import_intelligence3 = require("@harness-engineering/intelligence");
7578
+ var import_intelligence4 = require("@harness-engineering/intelligence");
7479
7579
  var import_graph = require("@harness-engineering/graph");
7480
7580
 
7481
7581
  // src/agent/analysis-provider-factory.ts
7482
- var import_intelligence2 = require("@harness-engineering/intelligence");
7582
+ var import_intelligence3 = require("@harness-engineering/intelligence");
7483
7583
  function buildAnalysisProvider(args) {
7484
7584
  const { def, backendName, layer, intelligence, logger } = args;
7485
7585
  const layerModel = layer === "sel" ? intelligence?.models?.sel : intelligence?.models?.pesl;
@@ -7519,7 +7619,7 @@ function buildLocalLikeProvider(def, args, layerModel) {
7519
7619
  logger.info(
7520
7620
  `Intelligence pipeline using backend '${backendName}' (${def.type}) at ${def.endpoint} (model: ${model ?? "(default)"})`
7521
7621
  );
7522
- return new import_intelligence2.OpenAICompatibleAnalysisProvider({
7622
+ return new import_intelligence3.OpenAICompatibleAnalysisProvider({
7523
7623
  apiKey,
7524
7624
  baseUrl: def.endpoint,
7525
7625
  ...model !== void 0 && { defaultModel: model },
@@ -7537,7 +7637,7 @@ function buildAnthropicProvider(def, args, layerModel) {
7537
7637
  const apiKey = def.apiKey ?? process.env.ANTHROPIC_API_KEY;
7538
7638
  const model = layerModel ?? def.model;
7539
7639
  if (apiKey) {
7540
- return new import_intelligence2.AnthropicAnalysisProvider({
7640
+ return new import_intelligence3.AnthropicAnalysisProvider({
7541
7641
  apiKey,
7542
7642
  ...model !== void 0 && { defaultModel: model }
7543
7643
  });
@@ -7545,7 +7645,7 @@ function buildAnthropicProvider(def, args, layerModel) {
7545
7645
  args.logger.info(
7546
7646
  `Intelligence pipeline routed to '${args.backendName}' (anthropic) without API key \u2014 using Claude CLI fallback.`
7547
7647
  );
7548
- return new import_intelligence2.ClaudeCliAnalysisProvider({
7648
+ return new import_intelligence3.ClaudeCliAnalysisProvider({
7549
7649
  ...model !== void 0 && { defaultModel: model },
7550
7650
  ...args.intelligence?.requestTimeoutMs !== void 0 && {
7551
7651
  timeoutMs: args.intelligence.requestTimeoutMs
@@ -7561,7 +7661,7 @@ function buildOpenAIProvider(def, args, layerModel) {
7561
7661
  return null;
7562
7662
  }
7563
7663
  const model = layerModel ?? def.model;
7564
- return new import_intelligence2.OpenAICompatibleAnalysisProvider({
7664
+ return new import_intelligence3.OpenAICompatibleAnalysisProvider({
7565
7665
  apiKey,
7566
7666
  baseUrl: "https://api.openai.com/v1",
7567
7667
  ...model !== void 0 && { defaultModel: model },
@@ -7571,7 +7671,7 @@ function buildOpenAIProvider(def, args, layerModel) {
7571
7671
  });
7572
7672
  }
7573
7673
  function buildClaudeCliProvider(def, args, layerModel) {
7574
- return new import_intelligence2.ClaudeCliAnalysisProvider({
7674
+ return new import_intelligence3.ClaudeCliAnalysisProvider({
7575
7675
  ...def.command !== void 0 && { command: def.command },
7576
7676
  ...layerModel !== void 0 && { defaultModel: layerModel },
7577
7677
  ...args.intelligence?.requestTimeoutMs !== void 0 && {
@@ -7592,7 +7692,7 @@ function buildIntelligencePipeline(deps) {
7592
7692
  const peslProvider = peslName !== selName ? buildAnalysisProviderForLayer("pesl", deps) : null;
7593
7693
  const peslModel = intel.models?.pesl ?? config.agent.model;
7594
7694
  const graphStore = new import_graph.GraphStore();
7595
- const pipeline = new import_intelligence3.IntelligencePipeline(selProvider, graphStore, {
7695
+ const pipeline = new import_intelligence4.IntelligencePipeline(selProvider, graphStore, {
7596
7696
  ...peslModel !== void 0 && { peslModel },
7597
7697
  ...peslProvider !== null && peslProvider !== void 0 && { peslProvider }
7598
7698
  });
@@ -7658,13 +7758,13 @@ function buildExplicitProvider(provider, selModel, config) {
7658
7758
  if (!apiKey2) {
7659
7759
  throw new Error("Intelligence pipeline: no Anthropic API key found.");
7660
7760
  }
7661
- return new import_intelligence3.AnthropicAnalysisProvider({
7761
+ return new import_intelligence4.AnthropicAnalysisProvider({
7662
7762
  apiKey: apiKey2,
7663
7763
  ...selModel !== void 0 && { defaultModel: selModel }
7664
7764
  });
7665
7765
  }
7666
7766
  if (provider.kind === "claude-cli") {
7667
- return new import_intelligence3.ClaudeCliAnalysisProvider({
7767
+ return new import_intelligence4.ClaudeCliAnalysisProvider({
7668
7768
  command: config.agent.command,
7669
7769
  ...selModel !== void 0 && { defaultModel: selModel },
7670
7770
  ...config.intelligence?.requestTimeoutMs !== void 0 && {
@@ -7675,7 +7775,7 @@ function buildExplicitProvider(provider, selModel, config) {
7675
7775
  const apiKey = provider.apiKey ?? config.agent.apiKey ?? "ollama";
7676
7776
  const baseUrl = provider.baseUrl ?? "http://localhost:11434/v1";
7677
7777
  const intel = config.intelligence;
7678
- return new import_intelligence3.OpenAICompatibleAnalysisProvider({
7778
+ return new import_intelligence4.OpenAICompatibleAnalysisProvider({
7679
7779
  apiKey,
7680
7780
  baseUrl,
7681
7781
  ...selModel !== void 0 && { defaultModel: selModel },
@@ -7686,13 +7786,13 @@ function buildExplicitProvider(provider, selModel, config) {
7686
7786
  }
7687
7787
 
7688
7788
  // src/agent/adaptive-router.ts
7689
- var import_intelligence6 = require("@harness-engineering/intelligence");
7789
+ var import_intelligence7 = require("@harness-engineering/intelligence");
7690
7790
  var import_types24 = require("@harness-engineering/types");
7691
7791
 
7692
7792
  // src/agent/capability-registry.ts
7693
7793
  var import_types23 = require("@harness-engineering/types");
7694
7794
  var import_local_models2 = require("@harness-engineering/local-models");
7695
- var import_intelligence4 = require("@harness-engineering/intelligence");
7795
+ var import_intelligence5 = require("@harness-engineering/intelligence");
7696
7796
  var PrivacyNoMatch = class extends import_types23.RoutingError {
7697
7797
  code = "privacy-no-match";
7698
7798
  constructor(message) {
@@ -7707,7 +7807,7 @@ var PRIVACY_RANK = {
7707
7807
  "shared-cloud": 3
7708
7808
  };
7709
7809
  function selectCheapestQualifying(registry, requiredTier, constraints, providerOf) {
7710
- const requiredRank = import_intelligence4.TIER_RANK[requiredTier];
7810
+ const requiredRank = import_intelligence5.TIER_RANK[requiredTier];
7711
7811
  const entries = [...registry.entries()].map(([name, capabilities]) => ({
7712
7812
  name,
7713
7813
  capabilities
@@ -7729,7 +7829,7 @@ function selectCheapestQualifying(registry, requiredTier, constraints, providerO
7729
7829
  }
7730
7830
  const qualifying = passesPrivacyAllow.filter((e) => {
7731
7831
  const c = e.capabilities;
7732
- if (import_intelligence4.TIER_RANK[c.tier] < requiredRank) return false;
7832
+ if (import_intelligence5.TIER_RANK[c.tier] < requiredRank) return false;
7733
7833
  if (constraints.needsVision && !c.vision) return false;
7734
7834
  if (constraints.needsToolUse && !c.toolUse) return false;
7735
7835
  if (constraints.minContextTokens !== void 0 && c.contextWindow < constraints.minContextTokens)
@@ -7768,7 +7868,7 @@ function estimateCost(def, _req) {
7768
7868
  }
7769
7869
 
7770
7870
  // src/agent/escalation-state.ts
7771
- var import_intelligence5 = require("@harness-engineering/intelligence");
7871
+ var import_intelligence6 = require("@harness-engineering/intelligence");
7772
7872
  var EscalationState = class {
7773
7873
  constructor(threshold = 2) {
7774
7874
  this.threshold = threshold;
@@ -7797,7 +7897,7 @@ var EscalationState = class {
7797
7897
  climbedUnits() {
7798
7898
  const out = [];
7799
7899
  for (const [coherenceUnit, state] of this.units) {
7800
- 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) {
7801
7901
  out.push({ coherenceUnit, floor: state.floorTier });
7802
7902
  }
7803
7903
  }
@@ -7836,14 +7936,14 @@ var EscalationState = class {
7836
7936
  return "ok";
7837
7937
  }
7838
7938
  state.failures = 0;
7839
- const currentRank = import_intelligence5.TIER_RANK[state.floorTier];
7840
- if (currentRank >= import_intelligence5.TIER_RANK.strong) {
7939
+ const currentRank = import_intelligence6.TIER_RANK[state.floorTier];
7940
+ if (currentRank >= import_intelligence6.TIER_RANK.strong) {
7841
7941
  state.floorTier = "strong";
7842
7942
  state.escalated = true;
7843
7943
  this.units.set(coherenceUnit, state);
7844
7944
  return "exhausted";
7845
7945
  }
7846
- state.floorTier = import_intelligence5.RANK_TIER[currentRank + 1];
7946
+ state.floorTier = import_intelligence6.RANK_TIER[currentRank + 1];
7847
7947
  state.escalated = true;
7848
7948
  this.units.set(coherenceUnit, state);
7849
7949
  return "escalated";
@@ -7979,7 +8079,7 @@ var AdaptiveRouter = class _AdaptiveRouter {
7979
8079
  let budgetStatus = null;
7980
8080
  if (budget && budget.capUsd > 0) {
7981
8081
  const spentUsd = this.getSpentUsd();
7982
- const degradeAtPct = budget.degradeAtPct ?? import_intelligence6.DEFAULT_DEGRADE_AT_PCT;
8082
+ const degradeAtPct = budget.degradeAtPct ?? import_intelligence7.DEFAULT_DEGRADE_AT_PCT;
7983
8083
  budgetStatus = {
7984
8084
  spentUsd,
7985
8085
  capUsd: budget.capUsd,
@@ -8011,8 +8111,8 @@ var AdaptiveRouter = class _AdaptiveRouter {
8011
8111
  );
8012
8112
  }
8013
8113
  const unitFloor = this.deps.escalation?.floorFor(req.coherenceUnit) ?? "fast";
8014
- const escalationFloor = import_intelligence6.RANK_TIER[Math.max(import_intelligence6.TIER_RANK[unitFloor], import_intelligence6.TIER_RANK[req.floor ?? "fast"])];
8015
- 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)(
8016
8116
  complexity,
8017
8117
  req.risk,
8018
8118
  this.deps.policy,
@@ -8169,10 +8269,10 @@ var RoutingDecisionBus = class {
8169
8269
  };
8170
8270
 
8171
8271
  // src/workflow/execute-workflow.ts
8172
- var import_intelligence7 = require("@harness-engineering/intelligence");
8272
+ var import_intelligence8 = require("@harness-engineering/intelligence");
8173
8273
  function nextTier(t) {
8174
- const next = Math.min(import_intelligence7.TIER_RANK[t] + 1, import_intelligence7.TIER_RANK.strong);
8175
- 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];
8176
8276
  }
8177
8277
  var DEFAULT_STAGE_DEADLINE_MS = 12e4;
8178
8278
  function stageAttemptKey(stageIndex, attempt) {
@@ -8394,7 +8494,7 @@ function buildRoutingUseCase(issue, backendParam, catalog) {
8394
8494
  }
8395
8495
 
8396
8496
  // src/agent/live-classify.ts
8397
- var import_intelligence8 = require("@harness-engineering/intelligence");
8497
+ var import_intelligence9 = require("@harness-engineering/intelligence");
8398
8498
  var CONSERVATIVE = {
8399
8499
  level: "moderate",
8400
8500
  confidence: "low",
@@ -8417,7 +8517,7 @@ function makeLiveClassify(resolveProvider) {
8417
8517
  riskHigh,
8418
8518
  prompt: taskText.prompt
8419
8519
  };
8420
- return (0, import_intelligence8.classify)(input, resolveProvider());
8520
+ return (0, import_intelligence9.classify)(input, resolveProvider());
8421
8521
  };
8422
8522
  }
8423
8523
 
@@ -8940,7 +9040,7 @@ function extractChunks(event) {
8940
9040
  }
8941
9041
 
8942
9042
  // src/server/routes/analyze.ts
8943
- var import_intelligence9 = require("@harness-engineering/intelligence");
9043
+ var import_intelligence10 = require("@harness-engineering/intelligence");
8944
9044
  var import_zod7 = require("zod");
8945
9045
  var AnalyzeRequestSchema = import_zod7.z.object({
8946
9046
  title: import_zod7.z.string().min(1),
@@ -8970,7 +9070,7 @@ async function runPipeline(res, pipeline, parsed) {
8970
9070
  disconnected = true;
8971
9071
  });
8972
9072
  emit2(res, { type: "status", text: "Converting to work item..." });
8973
- const rawItem = (0, import_intelligence9.manualToRawWorkItem)({
9073
+ const rawItem = (0, import_intelligence10.manualToRawWorkItem)({
8974
9074
  title: parsed.title,
8975
9075
  description: parsed.description ?? "",
8976
9076
  labels: parsed.labels ?? []
@@ -9013,7 +9113,7 @@ async function runPipeline(res, pipeline, parsed) {
9013
9113
  }
9014
9114
  }
9015
9115
  if (disconnected) return;
9016
- const signals = (0, import_intelligence9.scoreToConcernSignals)(score);
9116
+ const signals = (0, import_intelligence10.scoreToConcernSignals)(score);
9017
9117
  if (signals.length > 0) {
9018
9118
  emit2(res, { type: "signals", data: signals });
9019
9119
  }
@@ -10673,7 +10773,7 @@ function handleV1LocalModelsMutationRoute(req, res, deps) {
10673
10773
 
10674
10774
  // src/server/routes/v1/routing.ts
10675
10775
  var import_zod14 = require("zod");
10676
- var import_intelligence10 = require("@harness-engineering/intelligence");
10776
+ var import_intelligence11 = require("@harness-engineering/intelligence");
10677
10777
  var CONFIG_RE = /^\/api\/v1\/routing\/config(?:\?.*)?$/;
10678
10778
  var DECISIONS_RE = /^\/api\/v1\/routing\/decisions(?:\?.*)?$/;
10679
10779
  var TRACE_RE = /^\/api\/v1\/routing\/trace(?:\?.*)?$/;
@@ -10777,7 +10877,7 @@ function deriveTraceCost(body, decision, def, routing, backends) {
10777
10877
  source: "static"
10778
10878
  };
10779
10879
  const risk = body.risk === "high" ? { blastRadius: 10, sensitivePath: true } : { blastRadius: 0, sensitivePath: false };
10780
- const tierRequired = (0, import_intelligence10.deriveRequiredTier)(
10880
+ const tierRequired = (0, import_intelligence11.deriveRequiredTier)(
10781
10881
  verdict,
10782
10882
  risk,
10783
10883
  routing.policy ?? {},
@@ -10792,7 +10892,12 @@ function deriveTraceCost(body, decision, def, routing, backends) {
10792
10892
  backends
10793
10893
  );
10794
10894
  const estCostUsd = estimateCost(costedDef, { useCase: body.useCase });
10795
- return { tierRequired, estCostUsd, costedBackendName: costedName };
10895
+ return {
10896
+ tierRequired,
10897
+ estCostUsd,
10898
+ costedBackendName: costedName,
10899
+ costedBackendType: costedDef.type
10900
+ };
10796
10901
  }
10797
10902
  function selectCostedBackend(tierRequired, decision, def, routing, backends) {
10798
10903
  const registry = buildCapabilityRegistry(backends);
@@ -10857,7 +10962,7 @@ async function handleTrace(req, res, deps) {
10857
10962
  opts
10858
10963
  );
10859
10964
  if (r.data.complexity !== void 0 || r.data.risk !== void 0) {
10860
- const { tierRequired, estCostUsd, costedBackendName } = deriveTraceCost(
10965
+ const { tierRequired, estCostUsd, costedBackendName, costedBackendType } = deriveTraceCost(
10861
10966
  r.data,
10862
10967
  decision,
10863
10968
  def,
@@ -10869,9 +10974,12 @@ async function handleTrace(req, res, deps) {
10869
10974
  def: { type: def.type },
10870
10975
  tierRequired,
10871
10976
  estCostUsd,
10872
- // Name the backend the cost belongs to so operators see tier↔cost↔backend
10873
- // are consistent (was implicit + divergent before this fix).
10874
- 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
10875
10983
  });
10876
10984
  return true;
10877
10985
  }
@@ -11426,8 +11534,8 @@ function parseToken(raw) {
11426
11534
  return { id: raw.slice(0, dot), secret: raw.slice(dot + 1) };
11427
11535
  }
11428
11536
  var TokenStore = class {
11429
- constructor(path24) {
11430
- this.path = path24;
11537
+ constructor(path25) {
11538
+ this.path = path25;
11431
11539
  }
11432
11540
  path;
11433
11541
  cache = null;
@@ -11534,8 +11642,8 @@ var import_promises2 = require("fs/promises");
11534
11642
  var import_node_path2 = require("path");
11535
11643
  var import_types29 = require("@harness-engineering/types");
11536
11644
  var AuditLogger = class {
11537
- constructor(path24, opts = {}) {
11538
- this.path = path24;
11645
+ constructor(path25, opts = {}) {
11646
+ this.path = path25;
11539
11647
  this.opts = opts;
11540
11648
  }
11541
11649
  path;
@@ -11773,9 +11881,9 @@ var V1_BRIDGE_ROUTES = [
11773
11881
  function isV1Bridge(method, url) {
11774
11882
  return V1_BRIDGE_ROUTES.some((r) => r.method === method && r.pattern.test(url));
11775
11883
  }
11776
- function requiredBridgeScope(method, path24) {
11884
+ function requiredBridgeScope(method, path25) {
11777
11885
  for (const r of V1_BRIDGE_ROUTES) {
11778
- if (r.method === method && r.pattern.test(path24)) return r.scope;
11886
+ if (r.method === method && r.pattern.test(path25)) return r.scope;
11779
11887
  }
11780
11888
  return null;
11781
11889
  }
@@ -11785,11 +11893,11 @@ function hasScope(held, required) {
11785
11893
  if (held.includes("admin")) return true;
11786
11894
  return held.includes(required);
11787
11895
  }
11788
- function exactScopeForRoute(method, path24) {
11789
- if (path24 === "/api/v1/auth/token" && method === "POST") return "admin";
11790
- if (path24 === "/api/v1/auth/tokens" && method === "GET") return "admin";
11791
- if (/^\/api\/v1\/auth\/tokens\/[^/]+$/.test(path24) && method === "DELETE") return "admin";
11792
- 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";
11793
11901
  return null;
11794
11902
  }
11795
11903
  var PREFIX_SCOPES = [
@@ -11806,19 +11914,19 @@ var PREFIX_SCOPES = [
11806
11914
  ["/api/sessions", "read-status"],
11807
11915
  ["/api/chat-proxy", "trigger-job"]
11808
11916
  ];
11809
- function prefixScopeForPath(path24) {
11810
- if (path24 === "/api/chat") return "trigger-job";
11917
+ function prefixScopeForPath(path25) {
11918
+ if (path25 === "/api/chat") return "trigger-job";
11811
11919
  for (const [prefix, scope] of PREFIX_SCOPES) {
11812
- if (path24.startsWith(prefix)) return scope;
11920
+ if (path25.startsWith(prefix)) return scope;
11813
11921
  }
11814
11922
  return null;
11815
11923
  }
11816
- function requiredScopeForRoute(method, path24) {
11817
- const bridgeScope = requiredBridgeScope(method, path24);
11924
+ function requiredScopeForRoute(method, path25) {
11925
+ const bridgeScope = requiredBridgeScope(method, path25);
11818
11926
  if (bridgeScope) return bridgeScope;
11819
- const exactScope = exactScopeForRoute(method, path24);
11927
+ const exactScope = exactScopeForRoute(method, path25);
11820
11928
  if (exactScope) return exactScope;
11821
- return prefixScopeForPath(path24);
11929
+ return prefixScopeForPath(path25);
11822
11930
  }
11823
11931
 
11824
11932
  // src/server/http.ts
@@ -12347,8 +12455,8 @@ function genSecret2() {
12347
12455
  return (0, import_node_crypto10.randomBytes)(32).toString("base64url");
12348
12456
  }
12349
12457
  var WebhookStore = class {
12350
- constructor(path24) {
12351
- this.path = path24;
12458
+ constructor(path25) {
12459
+ this.path = path25;
12352
12460
  }
12353
12461
  path;
12354
12462
  cache = null;
@@ -15193,8 +15301,8 @@ function validateCheckShape(prefix, task, errors) {
15193
15301
  });
15194
15302
  }
15195
15303
  if (hasScript) {
15196
- const path24 = task.checkScript?.path;
15197
- if (!path24 || path24.trim().length === 0) {
15304
+ const path25 = task.checkScript?.path;
15305
+ if (!path25 || path25.trim().length === 0) {
15198
15306
  errors.push({ path: `${prefix}.checkScript.path`, message: "checkScript.path is required" });
15199
15307
  }
15200
15308
  if (task.checkScript?.timeoutMs !== void 0 && task.checkScript.timeoutMs <= 0) {
@@ -15320,9 +15428,9 @@ function handleEdge(top, next, color, stack, errors, reported) {
15320
15428
  stack.push({ id: next, nextIdx: 0, path: [...top.path, next] });
15321
15429
  }
15322
15430
  }
15323
- function reportCycle(path24, next, errors, reported) {
15324
- const cycleStart = path24.indexOf(next);
15325
- 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];
15326
15434
  const key = cyclePath.join("\u2192");
15327
15435
  if (reported.has(key)) return;
15328
15436
  reported.add(key);
@@ -16810,7 +16918,9 @@ ${messages}`);
16810
16918
  await this.emitWorkerExit(issue.id, "error", attempt, "Stopped by reconciliation");
16811
16919
  }
16812
16920
  } else {
16813
- 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;
16814
16924
  await this.emitWorkerExit(issue.id, "normal", attempt, void 0, outcomeClass);
16815
16925
  }
16816
16926
  } catch (error) {
@@ -16885,7 +16995,7 @@ ${messages}`);
16885
16995
  try {
16886
16996
  const diff = await this.workspace.getIntroducedDiffText(issue.identifier);
16887
16997
  if (diff.trim() === "") return void 0;
16888
- 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(), {
16889
16999
  ...acceptanceEval.model !== void 0 ? { model: acceptanceEval.model } : {}
16890
17000
  });
16891
17001
  const verdict = await evaluator.evaluate({
@@ -16912,6 +17022,150 @@ ${messages}`);
16912
17022
  return void 0;
16913
17023
  }
16914
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
+ }
16915
17169
  /**
16916
17170
  * Informs the state machine that an agent worker has exited.
16917
17171
  */
@@ -17998,6 +18252,333 @@ function launchTUI(orchestrator) {
17998
18252
  return { waitUntilExit };
17999
18253
  }
18000
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
+
18001
18582
  // src/maintenance/sync-main.ts
18002
18583
  var import_node_child_process14 = require("child_process");
18003
18584
  var import_node_util5 = require("util");
@@ -18191,8 +18772,8 @@ function selectTasks(tasks, history, filter) {
18191
18772
  }
18192
18773
 
18193
18774
  // src/sessions/search-index.ts
18194
- var fs16 = __toESM(require("fs"));
18195
- var path22 = __toESM(require("path"));
18775
+ var fs17 = __toESM(require("fs"));
18776
+ var path23 = __toESM(require("path"));
18196
18777
  var import_better_sqlite32 = __toESM(require("better-sqlite3"));
18197
18778
  var import_types35 = require("@harness-engineering/types");
18198
18779
  var SEARCH_INDEX_FILE = "search-index.sqlite";
@@ -18237,7 +18818,7 @@ function normalizeFts5Query(query) {
18237
18818
  return query.split(/\s+/).filter((tok) => tok.length > 0).map((tok) => `"${tok.replace(/"/g, '""')}"`).join(" ");
18238
18819
  }
18239
18820
  function searchIndexPath(projectPath) {
18240
- return path22.join(projectPath, ".harness", SEARCH_INDEX_FILE);
18821
+ return path23.join(projectPath, ".harness", SEARCH_INDEX_FILE);
18241
18822
  }
18242
18823
  var FILE_KIND_TO_FILENAME = {
18243
18824
  summary: "summary.md",
@@ -18252,7 +18833,7 @@ var SqliteSearchIndex = class {
18252
18833
  removeSessionStmt;
18253
18834
  totalStmt;
18254
18835
  constructor(dbPath) {
18255
- fs16.mkdirSync(path22.dirname(dbPath), { recursive: true });
18836
+ fs17.mkdirSync(path23.dirname(dbPath), { recursive: true });
18256
18837
  this.db = new import_better_sqlite32.default(dbPath);
18257
18838
  this.db.pragma("journal_mode = WAL");
18258
18839
  this.db.pragma("synchronous = NORMAL");
@@ -18357,14 +18938,14 @@ function indexSessionDirectory(idx, args) {
18357
18938
  let docsWritten = 0;
18358
18939
  for (const kind of kinds) {
18359
18940
  const fileName = FILE_KIND_TO_FILENAME[kind];
18360
- const filePath = path22.join(args.sessionDir, fileName);
18361
- if (!fs16.existsSync(filePath)) continue;
18362
- 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");
18363
18944
  if (Buffer.byteLength(body, "utf8") > cap) {
18364
18945
  body = body.slice(0, cap) + "\n\n[TRUNCATED]";
18365
18946
  }
18366
- const stat = fs16.statSync(filePath);
18367
- const relPath = path22.relative(args.projectPath, filePath).replaceAll("\\", "/");
18947
+ const stat = fs17.statSync(filePath);
18948
+ const relPath = path23.relative(args.projectPath, filePath).replaceAll("\\", "/");
18368
18949
  idx.upsertSessionDoc({
18369
18950
  sessionId: args.sessionId,
18370
18951
  archived: args.archived,
@@ -18379,17 +18960,17 @@ function indexSessionDirectory(idx, args) {
18379
18960
  }
18380
18961
  function reindexFromArchive(projectPath, opts = {}) {
18381
18962
  const start = Date.now();
18382
- const archiveBase = path22.join(projectPath, ".harness", "archive", "sessions");
18963
+ const archiveBase = path23.join(projectPath, ".harness", "archive", "sessions");
18383
18964
  const idx = openSearchIndex(projectPath);
18384
18965
  try {
18385
18966
  idx.resetArchived();
18386
18967
  let sessionsIndexed = 0;
18387
18968
  let docsWritten = 0;
18388
- if (fs16.existsSync(archiveBase)) {
18389
- const entries = fs16.readdirSync(archiveBase, { withFileTypes: true });
18969
+ if (fs17.existsSync(archiveBase)) {
18970
+ const entries = fs17.readdirSync(archiveBase, { withFileTypes: true });
18390
18971
  for (const entry of entries) {
18391
18972
  if (!entry.isDirectory()) continue;
18392
- const sessionDir = path22.join(archiveBase, entry.name);
18973
+ const sessionDir = path23.join(archiveBase, entry.name);
18393
18974
  const result = indexSessionDirectory(idx, {
18394
18975
  sessionId: entry.name,
18395
18976
  sessionDir,
@@ -18409,8 +18990,8 @@ function reindexFromArchive(projectPath, opts = {}) {
18409
18990
  }
18410
18991
 
18411
18992
  // src/sessions/summarize.ts
18412
- var fs17 = __toESM(require("fs"));
18413
- var path23 = __toESM(require("path"));
18993
+ var fs18 = __toESM(require("fs"));
18994
+ var path24 = __toESM(require("path"));
18414
18995
  var import_types36 = require("@harness-engineering/types");
18415
18996
  var import_types37 = require("@harness-engineering/types");
18416
18997
  var LLM_SUMMARY_FILE = "llm-summary.md";
@@ -18438,10 +19019,10 @@ var USER_PROMPT_PREAMBLE = `Below are the archived files for a single harness-en
18438
19019
  function readInputCorpus(archiveDir) {
18439
19020
  const parts = [];
18440
19021
  for (const { filename, kind } of SUMMARY_INPUT_FILES) {
18441
- const p = path23.join(archiveDir, filename);
18442
- if (!fs17.existsSync(p)) continue;
19022
+ const p = path24.join(archiveDir, filename);
19023
+ if (!fs18.existsSync(p)) continue;
18443
19024
  try {
18444
- const content = fs17.readFileSync(p, "utf8");
19025
+ const content = fs18.readFileSync(p, "utf8");
18445
19026
  if (content.trim().length === 0) continue;
18446
19027
  parts.push(`## FILE: ${kind}
18447
19028
 
@@ -18492,7 +19073,7 @@ function renderLlmSummaryMarkdown(summary, meta) {
18492
19073
  return lines.join("\n");
18493
19074
  }
18494
19075
  function writeStubMarkdown(archiveDir, reason) {
18495
- const filePath = path23.join(archiveDir, LLM_SUMMARY_FILE);
19076
+ const filePath = path24.join(archiveDir, LLM_SUMMARY_FILE);
18496
19077
  const body = `---
18497
19078
  generatedAt: ${(/* @__PURE__ */ new Date()).toISOString()}
18498
19079
  schemaVersion: 1
@@ -18503,12 +19084,12 @@ status: failed
18503
19084
 
18504
19085
  - reason: ${reason}
18505
19086
  `;
18506
- fs17.writeFileSync(filePath, body, "utf8");
19087
+ fs18.writeFileSync(filePath, body, "utf8");
18507
19088
  return filePath;
18508
19089
  }
18509
19090
  async function summarizeArchivedSession(ctx) {
18510
19091
  const writeStubOnError = ctx.writeStubOnError ?? true;
18511
- if (!fs17.existsSync(ctx.archiveDir)) {
19092
+ if (!fs18.existsSync(ctx.archiveDir)) {
18512
19093
  return (0, import_types37.Err)(new Error(`archive directory not found: ${ctx.archiveDir}`));
18513
19094
  }
18514
19095
  const corpus = readInputCorpus(ctx.archiveDir);
@@ -18569,9 +19150,9 @@ async function summarizeArchivedSession(ctx) {
18569
19150
  outputTokens: response.tokenUsage.outputTokens,
18570
19151
  schemaVersion: 1
18571
19152
  };
18572
- const filePath = path23.join(ctx.archiveDir, LLM_SUMMARY_FILE);
19153
+ const filePath = path24.join(ctx.archiveDir, LLM_SUMMARY_FILE);
18573
19154
  const body = renderLlmSummaryMarkdown(parsed.data, meta);
18574
- fs17.writeFileSync(filePath, body, "utf8");
19155
+ fs18.writeFileSync(filePath, body, "utf8");
18575
19156
  return (0, import_types37.Ok)({ summary: parsed.data, meta, filePath });
18576
19157
  }
18577
19158
  function isSummaryEnabled(config) {
@@ -18653,6 +19234,7 @@ var import_local_models7 = require("@harness-engineering/local-models");
18653
19234
  0 && (module.exports = {
18654
19235
  AdaptiveRouter,
18655
19236
  AnalysisArchive,
19237
+ BRAINSTORM_RUBRIC,
18656
19238
  BUILT_IN_TASKS,
18657
19239
  BackendDefSchema,
18658
19240
  BackendRouter,
@@ -18695,8 +19277,10 @@ var import_local_models7 = require("@harness-engineering/local-models");
18695
19277
  WorkspaceManager,
18696
19278
  applyEvent,
18697
19279
  artifactPresenceFromIssue,
19280
+ brainstormInputFromIssue,
18698
19281
  buildArchiveHooks,
18699
19282
  buildCapabilityRegistry,
19283
+ buildProbeInput,
18700
19284
  buildWorkflowContext,
18701
19285
  calculateRetryDelay,
18702
19286
  canDispatch,
@@ -18716,6 +19300,7 @@ var import_local_models7 = require("@harness-engineering/local-models");
18716
19300
  emitProposalApproved,
18717
19301
  emitProposalCreated,
18718
19302
  emitProposalRejected,
19303
+ enrichIssueWithSpec,
18719
19304
  estimateCost,
18720
19305
  explicitFindingsCount,
18721
19306
  extractHighlights,
@@ -18730,22 +19315,30 @@ var import_local_models7 = require("@harness-engineering/local-models");
18730
19315
  launchTUI,
18731
19316
  loadPublishedIndex,
18732
19317
  makeBackendResolver,
19318
+ makeGraphScope,
19319
+ makeSelForkGenerator,
19320
+ markApprovedForDispatch,
18733
19321
  migrateAgentConfig,
18734
19322
  normalizeFts5Query,
18735
19323
  normalizeHarnessCommand,
18736
19324
  normalizeLocalModel,
18737
19325
  openSearchIndex,
19326
+ pilotScore,
19327
+ precedentLookupFromStored,
18738
19328
  promote,
19329
+ rankTriageCandidates,
18739
19330
  reconcile,
18740
19331
  recoverFindingsCount,
18741
19332
  reindexFromArchive,
18742
19333
  renderAnalysisComment,
18743
19334
  renderLlmSummaryMarkdown,
18744
19335
  renderPRComment,
19336
+ renderSpecMarkdown,
18745
19337
  resolveEscalationConfig,
18746
19338
  resolveOrchestratorId,
18747
19339
  routeIssue,
18748
19340
  routingWarnings,
19341
+ runBrainstormForIssue,
18749
19342
  runGate,
18750
19343
  runHarnessCheck,
18751
19344
  savePublishedIndex,
@@ -18753,6 +19346,7 @@ var import_local_models7 = require("@harness-engineering/local-models");
18753
19346
  selectCandidates,
18754
19347
  selectCheapestQualifying,
18755
19348
  selectTasks,
19349
+ slugFor,
18756
19350
  sortCandidates,
18757
19351
  summarizeArchivedSession,
18758
19352
  syncMain,