@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.d.mts +615 -22
- package/dist/index.d.ts +615 -22
- package/dist/index.js +747 -133
- package/dist/index.mjs +712 -98
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -1911,26 +1911,60 @@ var ModelSchema = z.union([z.string().min(1), z.array(z.string().min(1)).nonempt
|
|
|
1911
1911
|
message: "model must be a non-empty string or array of strings"
|
|
1912
1912
|
})
|
|
1913
1913
|
});
|
|
1914
|
+
var CAPABILITY_TIER = z.enum(["fast", "standard", "strong"]);
|
|
1915
|
+
var COMPLEXITY_LEVEL = z.enum(["trivial", "simple", "moderate", "complex"]);
|
|
1916
|
+
var PRIVACY_CLASS = z.enum(["on-device", "pooled-isolated", "byo-endpoint", "shared-cloud"]);
|
|
1917
|
+
var BackendCapabilitiesSchema = z.object({
|
|
1918
|
+
tier: CAPABILITY_TIER,
|
|
1919
|
+
costPer1kTokens: z.number().nonnegative(),
|
|
1920
|
+
privacyClass: PRIVACY_CLASS,
|
|
1921
|
+
contextWindow: z.number().int().positive(),
|
|
1922
|
+
vision: z.boolean().optional(),
|
|
1923
|
+
toolUse: z.boolean().optional()
|
|
1924
|
+
}).strict();
|
|
1925
|
+
var RoutingPolicySchema = z.object({
|
|
1926
|
+
complexityTierMatrix: z.record(COMPLEXITY_LEVEL, CAPABILITY_TIER).optional(),
|
|
1927
|
+
skillTierOverrides: z.record(z.string(), CAPABILITY_TIER).optional(),
|
|
1928
|
+
privacyFloor: PRIVACY_CLASS.optional(),
|
|
1929
|
+
budget: z.object({
|
|
1930
|
+
capUsd: z.number(),
|
|
1931
|
+
degradeAtPct: z.number().optional(),
|
|
1932
|
+
onBudgetExhausted: z.enum(["degrade", "pause", "human"])
|
|
1933
|
+
}).optional(),
|
|
1934
|
+
sensitivePaths: z.array(z.string()).optional(),
|
|
1935
|
+
escalationThreshold: z.number().optional(),
|
|
1936
|
+
// string[], NOT the finite BackendDef['type'] union: an unknown provider must
|
|
1937
|
+
// fail CLOSED at tier selection, not reject the whole policy (Shuttle wire).
|
|
1938
|
+
allowedProviders: z.array(z.string()).optional(),
|
|
1939
|
+
acceptanceEval: z.object({
|
|
1940
|
+
enabled: z.boolean(),
|
|
1941
|
+
model: z.string().optional()
|
|
1942
|
+
}).optional()
|
|
1943
|
+
});
|
|
1914
1944
|
var BackendDefSchema = z.discriminatedUnion("type", [
|
|
1915
|
-
z.object({ type: z.literal("mock") }).strict(),
|
|
1945
|
+
z.object({ type: z.literal("mock"), capabilities: BackendCapabilitiesSchema.optional() }).strict(),
|
|
1916
1946
|
z.object({
|
|
1917
1947
|
type: z.literal("claude"),
|
|
1918
|
-
command: z.string().optional()
|
|
1948
|
+
command: z.string().optional(),
|
|
1949
|
+
capabilities: BackendCapabilitiesSchema.optional()
|
|
1919
1950
|
}).strict(),
|
|
1920
1951
|
z.object({
|
|
1921
1952
|
type: z.literal("anthropic"),
|
|
1922
1953
|
model: z.string().min(1),
|
|
1923
|
-
apiKey: z.string().optional()
|
|
1954
|
+
apiKey: z.string().optional(),
|
|
1955
|
+
capabilities: BackendCapabilitiesSchema.optional()
|
|
1924
1956
|
}).strict(),
|
|
1925
1957
|
z.object({
|
|
1926
1958
|
type: z.literal("openai"),
|
|
1927
1959
|
model: z.string().min(1),
|
|
1928
|
-
apiKey: z.string().optional()
|
|
1960
|
+
apiKey: z.string().optional(),
|
|
1961
|
+
capabilities: BackendCapabilitiesSchema.optional()
|
|
1929
1962
|
}).strict(),
|
|
1930
1963
|
z.object({
|
|
1931
1964
|
type: z.literal("gemini"),
|
|
1932
1965
|
model: z.string().min(1),
|
|
1933
|
-
apiKey: z.string().optional()
|
|
1966
|
+
apiKey: z.string().optional(),
|
|
1967
|
+
capabilities: BackendCapabilitiesSchema.optional()
|
|
1934
1968
|
}).strict(),
|
|
1935
1969
|
z.object({
|
|
1936
1970
|
type: z.literal("local"),
|
|
@@ -1938,7 +1972,8 @@ var BackendDefSchema = z.discriminatedUnion("type", [
|
|
|
1938
1972
|
model: ModelSchema,
|
|
1939
1973
|
apiKey: z.string().optional(),
|
|
1940
1974
|
timeoutMs: z.number().int().positive().optional(),
|
|
1941
|
-
probeIntervalMs: z.number().int().min(1e3).optional()
|
|
1975
|
+
probeIntervalMs: z.number().int().min(1e3).optional(),
|
|
1976
|
+
capabilities: BackendCapabilitiesSchema.optional()
|
|
1942
1977
|
}).strict(),
|
|
1943
1978
|
z.object({
|
|
1944
1979
|
type: z.literal("pi"),
|
|
@@ -1946,7 +1981,8 @@ var BackendDefSchema = z.discriminatedUnion("type", [
|
|
|
1946
1981
|
model: ModelSchema,
|
|
1947
1982
|
apiKey: z.string().optional(),
|
|
1948
1983
|
timeoutMs: z.number().int().positive().optional(),
|
|
1949
|
-
probeIntervalMs: z.number().int().min(1e3).optional()
|
|
1984
|
+
probeIntervalMs: z.number().int().min(1e3).optional(),
|
|
1985
|
+
capabilities: BackendCapabilitiesSchema.optional()
|
|
1950
1986
|
}).strict()
|
|
1951
1987
|
]);
|
|
1952
1988
|
var RoutingValueSchema = z.union([
|
|
@@ -1971,7 +2007,36 @@ var RoutingConfigSchema = z.object({
|
|
|
1971
2007
|
}).strict().optional(),
|
|
1972
2008
|
// --- Spec B Phase 0: new optional maps (resolver wired in Phase 1) ---
|
|
1973
2009
|
skills: z.record(z.string().min(1), RoutingValueSchema).optional(),
|
|
1974
|
-
modes: z.record(z.string().min(1), RoutingValueSchema).optional()
|
|
2010
|
+
modes: z.record(z.string().min(1), RoutingValueSchema).optional(),
|
|
2011
|
+
// --- AMR: opt-in adaptive-routing policy. Its PRESENCE flips the orchestrator
|
|
2012
|
+
// from identity/default dispatch to complexity-aware tier routing (default-off
|
|
2013
|
+
// when absent). Previously accepted by the runtime PUT endpoint only. ---
|
|
2014
|
+
policy: RoutingPolicySchema.optional()
|
|
2015
|
+
}).strict();
|
|
2016
|
+
var CONFIDENCE = z.enum(["low", "medium", "high"]);
|
|
2017
|
+
var V1_MAX_RATCHET_STAGE = 2;
|
|
2018
|
+
var RATCHET_STAGE = z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]).refine((s) => s <= V1_MAX_RATCHET_STAGE, {
|
|
2019
|
+
message: `ratchetStage ${">"} ${V1_MAX_RATCHET_STAGE} is deferred post-v1 (stages 3\u20134 not yet supported); use 1 or 2`
|
|
2020
|
+
});
|
|
2021
|
+
var RoadmapAutoTriageSchema = z.object({
|
|
2022
|
+
enabled: z.boolean(),
|
|
2023
|
+
schedule: z.string().min(1).optional(),
|
|
2024
|
+
ratchetStage: RATCHET_STAGE,
|
|
2025
|
+
thresholds: z.object({
|
|
2026
|
+
dispatchConfidence: CONFIDENCE,
|
|
2027
|
+
boundedScopeMax: z.number(),
|
|
2028
|
+
brainstormConfidence: z.number(),
|
|
2029
|
+
exceededByBands: z.number(),
|
|
2030
|
+
ratchetAdvanceRate: z.number(),
|
|
2031
|
+
ratchetMinSample: z.number()
|
|
2032
|
+
}).strict(),
|
|
2033
|
+
depthBudget: z.object({
|
|
2034
|
+
trivial: z.number(),
|
|
2035
|
+
simple: z.number()
|
|
2036
|
+
}).strict()
|
|
2037
|
+
}).strict();
|
|
2038
|
+
var RoadmapConfigSchema = z.object({
|
|
2039
|
+
autoTriage: RoadmapAutoTriageSchema.optional()
|
|
1975
2040
|
}).strict();
|
|
1976
2041
|
var WorkflowStepSchema = z.object({
|
|
1977
2042
|
skill: z.string().min(1),
|
|
@@ -2010,12 +2075,12 @@ var BackendsMapSchema = z2.record(z2.string(), BackendDefSchema);
|
|
|
2010
2075
|
function crossFieldRoutingIssues(backends, routing) {
|
|
2011
2076
|
const issues = [];
|
|
2012
2077
|
const names = new Set(Object.keys(backends));
|
|
2013
|
-
const checkRef = (
|
|
2078
|
+
const checkRef = (path25, value) => {
|
|
2014
2079
|
if (value === void 0) return;
|
|
2015
2080
|
const entries = Array.isArray(value) ? value : [value];
|
|
2016
2081
|
entries.forEach((name, idx) => {
|
|
2017
2082
|
if (names.has(name)) return;
|
|
2018
|
-
const pathWithIdx = Array.isArray(value) ? [...
|
|
2083
|
+
const pathWithIdx = Array.isArray(value) ? [...path25, String(idx)] : path25;
|
|
2019
2084
|
issues.push({
|
|
2020
2085
|
path: pathWithIdx,
|
|
2021
2086
|
message: `routing.${pathWithIdx.join(".")} references unknown backend '${name}'. Defined: [${[...names].join(", ")}].`
|
|
@@ -2112,6 +2177,10 @@ function validateWorkflowConfig(config, options = {}) {
|
|
|
2112
2177
|
const parsed = z2.array(StagedWorkflowDeclSchema).safeParse(c.workflows);
|
|
2113
2178
|
if (!parsed.success) return Err(new Error(`workflows: ${parsed.error.message}`));
|
|
2114
2179
|
}
|
|
2180
|
+
if (c.roadmap !== void 0) {
|
|
2181
|
+
const parsed = RoadmapConfigSchema.safeParse(c.roadmap);
|
|
2182
|
+
if (!parsed.success) return Err(new Error(`roadmap: ${parsed.error.message}`));
|
|
2183
|
+
}
|
|
2115
2184
|
return Ok2({ config, warnings });
|
|
2116
2185
|
}
|
|
2117
2186
|
function getDefaultConfig() {
|
|
@@ -3366,6 +3435,68 @@ import { randomUUID as randomUUID5 } from "crypto";
|
|
|
3366
3435
|
import { RoutingError as RoutingError3 } from "@harness-engineering/types";
|
|
3367
3436
|
import { writeTaint, SecurityScanner as SecurityScanner2 } from "@harness-engineering/core";
|
|
3368
3437
|
import { OutcomeEvaluator } from "@harness-engineering/intelligence";
|
|
3438
|
+
|
|
3439
|
+
// src/agent/triage-outcome.ts
|
|
3440
|
+
import {
|
|
3441
|
+
classify,
|
|
3442
|
+
compareToPrediction,
|
|
3443
|
+
aggregatePrecedent
|
|
3444
|
+
} from "@harness-engineering/intelligence";
|
|
3445
|
+
function signalsFromDiff(hunks, textOnly) {
|
|
3446
|
+
const files = /* @__PURE__ */ new Set();
|
|
3447
|
+
const layers = /* @__PURE__ */ new Set();
|
|
3448
|
+
let addedLines = 0;
|
|
3449
|
+
for (const h of hunks) {
|
|
3450
|
+
files.add(h.file);
|
|
3451
|
+
layers.add(layerOfPath(h.file));
|
|
3452
|
+
addedLines += h.addedContent === "" ? 0 : h.addedContent.split("\n").length;
|
|
3453
|
+
}
|
|
3454
|
+
return {
|
|
3455
|
+
...textOnly,
|
|
3456
|
+
filesTouched: files.size,
|
|
3457
|
+
layersTouched: layers.size,
|
|
3458
|
+
blastRadius: addedLines
|
|
3459
|
+
};
|
|
3460
|
+
}
|
|
3461
|
+
function layerOfPath(file) {
|
|
3462
|
+
const parts = file.split("/").filter((p) => p.length > 0);
|
|
3463
|
+
if (parts.length === 0) return "<root>";
|
|
3464
|
+
if (parts[0] === "packages" && parts.length >= 4 && parts[2] === "src") {
|
|
3465
|
+
return `${parts[1] ?? ""}/${parts[3] ?? ""}`;
|
|
3466
|
+
}
|
|
3467
|
+
return parts.length === 1 ? "<root>" : parts[0] ?? "<root>";
|
|
3468
|
+
}
|
|
3469
|
+
async function runRetrospective(deps, provider) {
|
|
3470
|
+
const signals = signalsFromDiff(deps.hunks, deps.textOnly);
|
|
3471
|
+
const actual = await classify(
|
|
3472
|
+
{ signals, phase: "post-diff", riskHigh: deps.riskHigh, prompt: deps.prompt },
|
|
3473
|
+
provider
|
|
3474
|
+
);
|
|
3475
|
+
const comparison = deps.comparatorConfig ? compareToPrediction(deps.prediction, actual, deps.comparatorConfig) : compareToPrediction(deps.prediction, actual);
|
|
3476
|
+
return { actual, comparison };
|
|
3477
|
+
}
|
|
3478
|
+
function buildTriageOutcomeInput(externalId, shapeKey, result) {
|
|
3479
|
+
return {
|
|
3480
|
+
externalId,
|
|
3481
|
+
shapeKey,
|
|
3482
|
+
actual: result.actual,
|
|
3483
|
+
exceededBy: result.comparison.exceededBy,
|
|
3484
|
+
matched: result.comparison.matched
|
|
3485
|
+
};
|
|
3486
|
+
}
|
|
3487
|
+
function precedentLookupFromStored(records) {
|
|
3488
|
+
const bridged = records.map((r) => ({
|
|
3489
|
+
externalId: "",
|
|
3490
|
+
shapeKey: r.shapeKey,
|
|
3491
|
+
ts: "",
|
|
3492
|
+
...r.outcome ? { outcome: { actual: void 0, exceededBy: 0, matched: r.outcome.matched } } : {}
|
|
3493
|
+
}));
|
|
3494
|
+
return {
|
|
3495
|
+
rateForShape: (shapeKey) => aggregatePrecedent(bridged, shapeKey)
|
|
3496
|
+
};
|
|
3497
|
+
}
|
|
3498
|
+
|
|
3499
|
+
// src/orchestrator.ts
|
|
3369
3500
|
import { GraphStore as GraphStore2 } from "@harness-engineering/graph";
|
|
3370
3501
|
|
|
3371
3502
|
// src/core/stall-detector.ts
|
|
@@ -3949,7 +4080,8 @@ var CompletionHandler = class {
|
|
|
3949
4080
|
import {
|
|
3950
4081
|
GitHubIssuesSyncAdapter as GitHubIssuesSyncAdapter3,
|
|
3951
4082
|
loadTrackerSyncConfig as loadTrackerSyncConfig3,
|
|
3952
|
-
createTrackerClient as createTrackerClient2
|
|
4083
|
+
createTrackerClient as createTrackerClient2,
|
|
4084
|
+
eventSourcing as eventSourcing2
|
|
3953
4085
|
} from "@harness-engineering/core";
|
|
3954
4086
|
|
|
3955
4087
|
// src/tracker/adapters/github-issues-issue-tracker.ts
|
|
@@ -4661,11 +4793,11 @@ function detectLegacyFields(agent) {
|
|
|
4661
4793
|
}
|
|
4662
4794
|
function buildCase1Warnings(presentLegacy, suppressLocalGroup) {
|
|
4663
4795
|
const warnings = [];
|
|
4664
|
-
for (const
|
|
4665
|
-
if (CASE1_ALWAYS_SUPPRESS.has(
|
|
4666
|
-
if (suppressLocalGroup && CASE1_LOCAL_GROUP.has(
|
|
4796
|
+
for (const path25 of presentLegacy) {
|
|
4797
|
+
if (CASE1_ALWAYS_SUPPRESS.has(path25)) continue;
|
|
4798
|
+
if (suppressLocalGroup && CASE1_LOCAL_GROUP.has(path25)) continue;
|
|
4667
4799
|
warnings.push(
|
|
4668
|
-
`Ignoring legacy field '${
|
|
4800
|
+
`Ignoring legacy field '${path25}': 'agent.backends' is set and takes precedence. See ${MIGRATION_GUIDE}.`
|
|
4669
4801
|
);
|
|
4670
4802
|
}
|
|
4671
4803
|
return warnings;
|
|
@@ -4693,7 +4825,7 @@ function migrateAgentConfig(agent) {
|
|
|
4693
4825
|
}
|
|
4694
4826
|
const { backends, routing } = synthesizeBackendsAndRouting(agent);
|
|
4695
4827
|
const warnings = presentLegacy.map(
|
|
4696
|
-
(
|
|
4828
|
+
(path25) => `Deprecated config field '${path25}' is in use. Migrate to 'agent.backends' / 'agent.routing'. See ${MIGRATION_GUIDE}.`
|
|
4697
4829
|
);
|
|
4698
4830
|
return {
|
|
4699
4831
|
config: { ...agent, backends, routing },
|
|
@@ -4778,12 +4910,12 @@ var BackendRouter = class {
|
|
|
4778
4910
|
*/
|
|
4779
4911
|
resolve(useCase, opts) {
|
|
4780
4912
|
const startedAt = performance.now();
|
|
4781
|
-
const
|
|
4913
|
+
const path25 = [];
|
|
4782
4914
|
const tryChain = (source, value) => {
|
|
4783
4915
|
if (value === void 0) return void 0;
|
|
4784
4916
|
for (const name of toArray(value)) {
|
|
4785
4917
|
const step = { source, candidate: name, outcome: "considered" };
|
|
4786
|
-
|
|
4918
|
+
path25.push(step);
|
|
4787
4919
|
if (this.backends[name]) {
|
|
4788
4920
|
step.outcome = "chosen";
|
|
4789
4921
|
return name;
|
|
@@ -4802,7 +4934,7 @@ var BackendRouter = class {
|
|
|
4802
4934
|
return {
|
|
4803
4935
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4804
4936
|
useCase,
|
|
4805
|
-
resolutionPath:
|
|
4937
|
+
resolutionPath: path25,
|
|
4806
4938
|
backendName,
|
|
4807
4939
|
backendType: def.type,
|
|
4808
4940
|
durationMs: performance.now() - startedAt
|
|
@@ -4835,7 +4967,7 @@ var BackendRouter = class {
|
|
|
4835
4967
|
if (fromDefault) return emitAndReturn(decide(fromDefault));
|
|
4836
4968
|
const knownList = Object.keys(this.backends).join(", ") || "(none)";
|
|
4837
4969
|
throw new Error(
|
|
4838
|
-
`BackendRouter.resolve: routing.default produced no available backend for useCase=${JSON.stringify(useCase)}. Resolution path: ${JSON.stringify(
|
|
4970
|
+
`BackendRouter.resolve: routing.default produced no available backend for useCase=${JSON.stringify(useCase)}. Resolution path: ${JSON.stringify(path25)}. Known backends: [${knownList}].`
|
|
4839
4971
|
);
|
|
4840
4972
|
}
|
|
4841
4973
|
/**
|
|
@@ -4928,7 +5060,7 @@ var BackendRouter = class {
|
|
|
4928
5060
|
check(`modes.${mode}`, value);
|
|
4929
5061
|
}
|
|
4930
5062
|
if (missing.length > 0) {
|
|
4931
|
-
const detail = missing.map(({ path:
|
|
5063
|
+
const detail = missing.map(({ path: path25, name }) => `routing.${path25} -> '${name}'`).join("; ");
|
|
4932
5064
|
const known_ = [...known].join(", ") || "(none)";
|
|
4933
5065
|
throw new Error(
|
|
4934
5066
|
`BackendRouter: routing references unknown backend(s): ${detail}. Defined backends: [${known_}].`
|
|
@@ -8281,7 +8413,7 @@ function buildRoutingUseCase(issue, backendParam, catalog) {
|
|
|
8281
8413
|
|
|
8282
8414
|
// src/agent/live-classify.ts
|
|
8283
8415
|
import {
|
|
8284
|
-
classify
|
|
8416
|
+
classify as classify2
|
|
8285
8417
|
} from "@harness-engineering/intelligence";
|
|
8286
8418
|
var CONSERVATIVE = {
|
|
8287
8419
|
level: "moderate",
|
|
@@ -8305,7 +8437,7 @@ function makeLiveClassify(resolveProvider) {
|
|
|
8305
8437
|
riskHigh,
|
|
8306
8438
|
prompt: taskText.prompt
|
|
8307
8439
|
};
|
|
8308
|
-
return
|
|
8440
|
+
return classify2(input, resolveProvider());
|
|
8309
8441
|
};
|
|
8310
8442
|
}
|
|
8311
8443
|
|
|
@@ -10705,7 +10837,12 @@ function deriveTraceCost(body, decision, def, routing, backends) {
|
|
|
10705
10837
|
backends
|
|
10706
10838
|
);
|
|
10707
10839
|
const estCostUsd = estimateCost(costedDef, { useCase: body.useCase });
|
|
10708
|
-
return {
|
|
10840
|
+
return {
|
|
10841
|
+
tierRequired,
|
|
10842
|
+
estCostUsd,
|
|
10843
|
+
costedBackendName: costedName,
|
|
10844
|
+
costedBackendType: costedDef.type
|
|
10845
|
+
};
|
|
10709
10846
|
}
|
|
10710
10847
|
function selectCostedBackend(tierRequired, decision, def, routing, backends) {
|
|
10711
10848
|
const registry = buildCapabilityRegistry(backends);
|
|
@@ -10770,7 +10907,7 @@ async function handleTrace(req, res, deps) {
|
|
|
10770
10907
|
opts
|
|
10771
10908
|
);
|
|
10772
10909
|
if (r.data.complexity !== void 0 || r.data.risk !== void 0) {
|
|
10773
|
-
const { tierRequired, estCostUsd, costedBackendName } = deriveTraceCost(
|
|
10910
|
+
const { tierRequired, estCostUsd, costedBackendName, costedBackendType } = deriveTraceCost(
|
|
10774
10911
|
r.data,
|
|
10775
10912
|
decision,
|
|
10776
10913
|
def,
|
|
@@ -10782,9 +10919,12 @@ async function handleTrace(req, res, deps) {
|
|
|
10782
10919
|
def: { type: def.type },
|
|
10783
10920
|
tierRequired,
|
|
10784
10921
|
estCostUsd,
|
|
10785
|
-
//
|
|
10786
|
-
//
|
|
10787
|
-
|
|
10922
|
+
// The backend AMR would ACTUALLY dispatch to at this tier (cheapest
|
|
10923
|
+
// qualifying) + its type — distinct from the identity-chain `decision`
|
|
10924
|
+
// above. The CLI shows this as the effective backend so tier↔cost↔backend
|
|
10925
|
+
// read consistently (was implicit + divergent before).
|
|
10926
|
+
costedBackendName,
|
|
10927
|
+
costedBackendType
|
|
10788
10928
|
});
|
|
10789
10929
|
return true;
|
|
10790
10930
|
}
|
|
@@ -10794,26 +10934,6 @@ async function handleTrace(req, res, deps) {
|
|
|
10794
10934
|
}
|
|
10795
10935
|
return true;
|
|
10796
10936
|
}
|
|
10797
|
-
var CAPABILITY_TIER = z14.enum(["fast", "standard", "strong"]);
|
|
10798
|
-
var COMPLEXITY_LEVEL = z14.enum(["trivial", "simple", "moderate", "complex"]);
|
|
10799
|
-
var PRIVACY_CLASS = z14.enum(["on-device", "byo-endpoint", "shared-cloud"]);
|
|
10800
|
-
var RoutingPolicySchema = z14.object({
|
|
10801
|
-
complexityTierMatrix: z14.record(COMPLEXITY_LEVEL, CAPABILITY_TIER).optional(),
|
|
10802
|
-
skillTierOverrides: z14.record(z14.string(), CAPABILITY_TIER).optional(),
|
|
10803
|
-
privacyFloor: PRIVACY_CLASS.optional(),
|
|
10804
|
-
budget: z14.object({
|
|
10805
|
-
capUsd: z14.number(),
|
|
10806
|
-
degradeAtPct: z14.number().optional(),
|
|
10807
|
-
onBudgetExhausted: z14.enum(["degrade", "pause", "human"])
|
|
10808
|
-
}).optional(),
|
|
10809
|
-
sensitivePaths: z14.array(z14.string()).optional(),
|
|
10810
|
-
escalationThreshold: z14.number().optional(),
|
|
10811
|
-
allowedProviders: z14.array(z14.string()).optional(),
|
|
10812
|
-
acceptanceEval: z14.object({
|
|
10813
|
-
enabled: z14.boolean(),
|
|
10814
|
-
model: z14.string().optional()
|
|
10815
|
-
}).optional()
|
|
10816
|
-
});
|
|
10817
10937
|
async function handlePolicy(req, res, deps) {
|
|
10818
10938
|
if (!deps.ingestRoutingPolicy || deps.router === null) return unavailable(res);
|
|
10819
10939
|
let raw;
|
|
@@ -11366,8 +11486,8 @@ function parseToken(raw) {
|
|
|
11366
11486
|
return { id: raw.slice(0, dot), secret: raw.slice(dot + 1) };
|
|
11367
11487
|
}
|
|
11368
11488
|
var TokenStore = class {
|
|
11369
|
-
constructor(
|
|
11370
|
-
this.path =
|
|
11489
|
+
constructor(path25) {
|
|
11490
|
+
this.path = path25;
|
|
11371
11491
|
}
|
|
11372
11492
|
path;
|
|
11373
11493
|
cache = null;
|
|
@@ -11474,8 +11594,8 @@ import { appendFile, mkdir as mkdir8 } from "fs/promises";
|
|
|
11474
11594
|
import { dirname as dirname6 } from "path";
|
|
11475
11595
|
import { AuthAuditEntrySchema } from "@harness-engineering/types";
|
|
11476
11596
|
var AuditLogger = class {
|
|
11477
|
-
constructor(
|
|
11478
|
-
this.path =
|
|
11597
|
+
constructor(path25, opts = {}) {
|
|
11598
|
+
this.path = path25;
|
|
11479
11599
|
this.opts = opts;
|
|
11480
11600
|
}
|
|
11481
11601
|
path;
|
|
@@ -11713,9 +11833,9 @@ var V1_BRIDGE_ROUTES = [
|
|
|
11713
11833
|
function isV1Bridge(method, url) {
|
|
11714
11834
|
return V1_BRIDGE_ROUTES.some((r) => r.method === method && r.pattern.test(url));
|
|
11715
11835
|
}
|
|
11716
|
-
function requiredBridgeScope(method,
|
|
11836
|
+
function requiredBridgeScope(method, path25) {
|
|
11717
11837
|
for (const r of V1_BRIDGE_ROUTES) {
|
|
11718
|
-
if (r.method === method && r.pattern.test(
|
|
11838
|
+
if (r.method === method && r.pattern.test(path25)) return r.scope;
|
|
11719
11839
|
}
|
|
11720
11840
|
return null;
|
|
11721
11841
|
}
|
|
@@ -11725,11 +11845,11 @@ function hasScope(held, required) {
|
|
|
11725
11845
|
if (held.includes("admin")) return true;
|
|
11726
11846
|
return held.includes(required);
|
|
11727
11847
|
}
|
|
11728
|
-
function exactScopeForRoute(method,
|
|
11729
|
-
if (
|
|
11730
|
-
if (
|
|
11731
|
-
if (/^\/api\/v1\/auth\/tokens\/[^/]+$/.test(
|
|
11732
|
-
if ((
|
|
11848
|
+
function exactScopeForRoute(method, path25) {
|
|
11849
|
+
if (path25 === "/api/v1/auth/token" && method === "POST") return "admin";
|
|
11850
|
+
if (path25 === "/api/v1/auth/tokens" && method === "GET") return "admin";
|
|
11851
|
+
if (/^\/api\/v1\/auth\/tokens\/[^/]+$/.test(path25) && method === "DELETE") return "admin";
|
|
11852
|
+
if ((path25 === "/api/state" || path25 === "/api/v1/state") && method === "GET") return "read-status";
|
|
11733
11853
|
return null;
|
|
11734
11854
|
}
|
|
11735
11855
|
var PREFIX_SCOPES = [
|
|
@@ -11746,19 +11866,19 @@ var PREFIX_SCOPES = [
|
|
|
11746
11866
|
["/api/sessions", "read-status"],
|
|
11747
11867
|
["/api/chat-proxy", "trigger-job"]
|
|
11748
11868
|
];
|
|
11749
|
-
function prefixScopeForPath(
|
|
11750
|
-
if (
|
|
11869
|
+
function prefixScopeForPath(path25) {
|
|
11870
|
+
if (path25 === "/api/chat") return "trigger-job";
|
|
11751
11871
|
for (const [prefix, scope] of PREFIX_SCOPES) {
|
|
11752
|
-
if (
|
|
11872
|
+
if (path25.startsWith(prefix)) return scope;
|
|
11753
11873
|
}
|
|
11754
11874
|
return null;
|
|
11755
11875
|
}
|
|
11756
|
-
function requiredScopeForRoute(method,
|
|
11757
|
-
const bridgeScope = requiredBridgeScope(method,
|
|
11876
|
+
function requiredScopeForRoute(method, path25) {
|
|
11877
|
+
const bridgeScope = requiredBridgeScope(method, path25);
|
|
11758
11878
|
if (bridgeScope) return bridgeScope;
|
|
11759
|
-
const exactScope = exactScopeForRoute(method,
|
|
11879
|
+
const exactScope = exactScopeForRoute(method, path25);
|
|
11760
11880
|
if (exactScope) return exactScope;
|
|
11761
|
-
return prefixScopeForPath(
|
|
11881
|
+
return prefixScopeForPath(path25);
|
|
11762
11882
|
}
|
|
11763
11883
|
|
|
11764
11884
|
// src/server/http.ts
|
|
@@ -12287,8 +12407,8 @@ function genSecret2() {
|
|
|
12287
12407
|
return randomBytes3(32).toString("base64url");
|
|
12288
12408
|
}
|
|
12289
12409
|
var WebhookStore = class {
|
|
12290
|
-
constructor(
|
|
12291
|
-
this.path =
|
|
12410
|
+
constructor(path25) {
|
|
12411
|
+
this.path = path25;
|
|
12292
12412
|
}
|
|
12293
12413
|
path;
|
|
12294
12414
|
cache = null;
|
|
@@ -15141,8 +15261,8 @@ function validateCheckShape(prefix, task, errors) {
|
|
|
15141
15261
|
});
|
|
15142
15262
|
}
|
|
15143
15263
|
if (hasScript) {
|
|
15144
|
-
const
|
|
15145
|
-
if (!
|
|
15264
|
+
const path25 = task.checkScript?.path;
|
|
15265
|
+
if (!path25 || path25.trim().length === 0) {
|
|
15146
15266
|
errors.push({ path: `${prefix}.checkScript.path`, message: "checkScript.path is required" });
|
|
15147
15267
|
}
|
|
15148
15268
|
if (task.checkScript?.timeoutMs !== void 0 && task.checkScript.timeoutMs <= 0) {
|
|
@@ -15268,9 +15388,9 @@ function handleEdge(top, next, color, stack, errors, reported) {
|
|
|
15268
15388
|
stack.push({ id: next, nextIdx: 0, path: [...top.path, next] });
|
|
15269
15389
|
}
|
|
15270
15390
|
}
|
|
15271
|
-
function reportCycle(
|
|
15272
|
-
const cycleStart =
|
|
15273
|
-
const cyclePath = cycleStart >= 0 ? [...
|
|
15391
|
+
function reportCycle(path25, next, errors, reported) {
|
|
15392
|
+
const cycleStart = path25.indexOf(next);
|
|
15393
|
+
const cyclePath = cycleStart >= 0 ? [...path25.slice(cycleStart), next] : [...path25, next];
|
|
15274
15394
|
const key = cyclePath.join("\u2192");
|
|
15275
15395
|
if (reported.has(key)) return;
|
|
15276
15396
|
reported.add(key);
|
|
@@ -16758,7 +16878,9 @@ ${messages}`);
|
|
|
16758
16878
|
await this.emitWorkerExit(issue.id, "error", attempt, "Stopped by reconciliation");
|
|
16759
16879
|
}
|
|
16760
16880
|
} else {
|
|
16761
|
-
const
|
|
16881
|
+
const qualityClass = await this.deriveSingleAgentQualityVerdict(issue, workspacePath);
|
|
16882
|
+
const retroClass = await this.deriveRoutingRetrospectiveVerdict(issue, workspacePath);
|
|
16883
|
+
const outcomeClass = qualityClass ?? retroClass;
|
|
16762
16884
|
await this.emitWorkerExit(issue.id, "normal", attempt, void 0, outcomeClass);
|
|
16763
16885
|
}
|
|
16764
16886
|
} catch (error) {
|
|
@@ -16860,6 +16982,150 @@ ${messages}`);
|
|
|
16860
16982
|
return void 0;
|
|
16861
16983
|
}
|
|
16862
16984
|
}
|
|
16985
|
+
/**
|
|
16986
|
+
* Roadmap Auto-Triage Phase 4 (SC1–SC3, SC5, SC7): the post-diff routing
|
|
16987
|
+
* retrospective — a SIBLING quality-verdict source to the 4c feeder above,
|
|
16988
|
+
* extending (not replacing) the proven escalation path. On a normal exit, when AMR
|
|
16989
|
+
* is live AND auto-triage is enabled AND this unit carries a stored pre-dispatch
|
|
16990
|
+
* prediction, it classifies the ACTUAL introduced diff at full strength
|
|
16991
|
+
* (phase:'post-diff') and grades it against that prediction:
|
|
16992
|
+
* - MATCH → records the graded outcome + annotates the PR "AI autonomous —
|
|
16993
|
+
* verify" (stage-2 human-verify handling, SC4) → returns `undefined`
|
|
16994
|
+
* (neutral: a match is not an escalation).
|
|
16995
|
+
* - MISMATCH (diff exceeded prediction, or a missing/garbled prediction, or ANY
|
|
16996
|
+
* error) → records the outcome (when a shape is known) + returns
|
|
16997
|
+
* `'quality-fail'`, which climbs the coherence unit's escalation floor
|
|
16998
|
+
* via `recordAmrOutcome`; exhaustion queues `needs-human` (SC3/SC7).
|
|
16999
|
+
*
|
|
17000
|
+
* Fail-safe & guarded (SC7): an error never a silent pass — it takes the MISMATCH
|
|
17001
|
+
* (block+escalate) path. No-op (byte-identical) when AMR is off, auto-triage is
|
|
17002
|
+
* off, or the unit was not dispatched through triage (no stored prediction) — the
|
|
17003
|
+
* last means an ordinary non-triaged run is never graded.
|
|
17004
|
+
*/
|
|
17005
|
+
async deriveRoutingRetrospectiveVerdict(issue, _workspacePath) {
|
|
17006
|
+
if (this.adaptiveRouter === null) return void 0;
|
|
17007
|
+
if (this.config.roadmap?.autoTriage?.enabled !== true) return void 0;
|
|
17008
|
+
const externalId = issue.externalId;
|
|
17009
|
+
if (externalId === null || externalId === "") return void 0;
|
|
17010
|
+
let record;
|
|
17011
|
+
try {
|
|
17012
|
+
const loaded = await eventSourcing2.loadTriageRecords(this.projectRoot);
|
|
17013
|
+
if (!loaded.ok) throw loaded.error;
|
|
17014
|
+
record = loaded.value.find((r) => r.externalId === externalId);
|
|
17015
|
+
} catch (err) {
|
|
17016
|
+
this.logger.debug("amr retrospective skipped (record load failed)", {
|
|
17017
|
+
issueId: issue.id,
|
|
17018
|
+
error: err instanceof Error ? err.message : String(err)
|
|
17019
|
+
});
|
|
17020
|
+
if (issue.spec !== null && issue.spec !== void 0 && issue.spec !== "") {
|
|
17021
|
+
this.logger.info(
|
|
17022
|
+
"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)",
|
|
17023
|
+
{ issueId: issue.id, externalId }
|
|
17024
|
+
);
|
|
17025
|
+
return "quality-fail";
|
|
17026
|
+
}
|
|
17027
|
+
return void 0;
|
|
17028
|
+
}
|
|
17029
|
+
if (record === void 0) return void 0;
|
|
17030
|
+
const stored = record.prediction;
|
|
17031
|
+
if (stored === void 0) {
|
|
17032
|
+
this.logger.info(
|
|
17033
|
+
"amr:quality-fail \u2014 triaged unit has no stored prediction (fail-safe block)",
|
|
17034
|
+
{
|
|
17035
|
+
issueId: issue.id,
|
|
17036
|
+
externalId
|
|
17037
|
+
}
|
|
17038
|
+
);
|
|
17039
|
+
return "quality-fail";
|
|
17040
|
+
}
|
|
17041
|
+
const prediction = {
|
|
17042
|
+
verdict: stored.verdict,
|
|
17043
|
+
levers: stored.levers,
|
|
17044
|
+
scopeEstimate: stored.scopeEstimate,
|
|
17045
|
+
ratchetStage: stored.ratchetStage
|
|
17046
|
+
};
|
|
17047
|
+
try {
|
|
17048
|
+
const hunks = await this.workspace.getIntroducedDiff(issue.identifier);
|
|
17049
|
+
const taskText = buildTaskText(issue);
|
|
17050
|
+
const exceededByBands = this.config.roadmap?.autoTriage?.thresholds?.exceededByBands ?? 1;
|
|
17051
|
+
const result = await runRetrospective(
|
|
17052
|
+
{
|
|
17053
|
+
hunks,
|
|
17054
|
+
textOnly: {
|
|
17055
|
+
descriptionLength: taskText.descriptionLength,
|
|
17056
|
+
specExists: taskText.specExists,
|
|
17057
|
+
acceptanceMeasurable: taskText.acceptanceMeasurable
|
|
17058
|
+
},
|
|
17059
|
+
prediction,
|
|
17060
|
+
riskHigh: false,
|
|
17061
|
+
prompt: taskText.prompt,
|
|
17062
|
+
comparatorConfig: { exceededByBands }
|
|
17063
|
+
},
|
|
17064
|
+
this.resolveComplexityProvider()
|
|
17065
|
+
);
|
|
17066
|
+
const outcomeInput = buildTriageOutcomeInput(externalId, record.shapeKey, result);
|
|
17067
|
+
const recorded = await eventSourcing2.recordTriageOutcome(this.projectRoot, outcomeInput);
|
|
17068
|
+
if (!recorded.ok) {
|
|
17069
|
+
this.logger.warn("amr retrospective outcome record failed (best-effort)", {
|
|
17070
|
+
issueId: issue.id,
|
|
17071
|
+
error: recorded.error.message
|
|
17072
|
+
});
|
|
17073
|
+
}
|
|
17074
|
+
if (result.comparison.action === "block-escalate") {
|
|
17075
|
+
this.logger.info("amr:quality-fail \u2014 post-diff retrospective mispredict (block+escalate)", {
|
|
17076
|
+
issueId: issue.id,
|
|
17077
|
+
externalId,
|
|
17078
|
+
predicted: prediction.verdict.level,
|
|
17079
|
+
actual: result.actual.level,
|
|
17080
|
+
exceededBy: result.comparison.exceededBy
|
|
17081
|
+
});
|
|
17082
|
+
return "quality-fail";
|
|
17083
|
+
}
|
|
17084
|
+
await this.annotateRetrospectiveMatch(issue, externalId, result.actual.level);
|
|
17085
|
+
return void 0;
|
|
17086
|
+
} catch (err) {
|
|
17087
|
+
this.logger.info("amr:quality-fail \u2014 post-diff retrospective errored (fail-safe block)", {
|
|
17088
|
+
issueId: issue.id,
|
|
17089
|
+
externalId,
|
|
17090
|
+
error: err instanceof Error ? err.message : String(err)
|
|
17091
|
+
});
|
|
17092
|
+
return "quality-fail";
|
|
17093
|
+
}
|
|
17094
|
+
}
|
|
17095
|
+
/**
|
|
17096
|
+
* v1 stage-2 match handling (SC4): annotate the unit's PR/issue with a "verify this
|
|
17097
|
+
* autonomous change" note so a human reviews every autonomous PR. Best-effort — a
|
|
17098
|
+
* missing tracker config / token / a failed comment never breaks completion (the
|
|
17099
|
+
* grade already recorded above). Mirrors `postLifecycleComment`'s adapter wiring.
|
|
17100
|
+
*/
|
|
17101
|
+
async annotateRetrospectiveMatch(issue, externalId, actualLevel) {
|
|
17102
|
+
try {
|
|
17103
|
+
const trackerConfig = loadTrackerSyncConfig3(this.projectRoot);
|
|
17104
|
+
if (!trackerConfig) return;
|
|
17105
|
+
const token = process.env.GITHUB_TOKEN;
|
|
17106
|
+
if (!token) return;
|
|
17107
|
+
const orchestratorId = await this.orchestratorIdPromise;
|
|
17108
|
+
const adapter = new GitHubIssuesSyncAdapter3({ token, config: trackerConfig });
|
|
17109
|
+
const body = [
|
|
17110
|
+
`**AI autonomous change \u2014 please verify** \`${orchestratorId}\``,
|
|
17111
|
+
"",
|
|
17112
|
+
"The post-diff retrospective classified this change WITHIN its pre-dispatch",
|
|
17113
|
+
`prediction (actual complexity: \`${actualLevel}\`). Under autonomy ratchet`,
|
|
17114
|
+
"stage 2, a human must verify every autonomous PR before it merges."
|
|
17115
|
+
].join("\n");
|
|
17116
|
+
const result = await adapter.addComment(externalId, body);
|
|
17117
|
+
if (!result.ok) {
|
|
17118
|
+
this.logger.warn(`amr retrospective annotation failed for ${issue.identifier}`, {
|
|
17119
|
+
error: result.error.message
|
|
17120
|
+
});
|
|
17121
|
+
}
|
|
17122
|
+
} catch (err) {
|
|
17123
|
+
this.logger.debug("amr retrospective annotation skipped (best-effort)", {
|
|
17124
|
+
issueId: issue.id,
|
|
17125
|
+
error: err instanceof Error ? err.message : String(err)
|
|
17126
|
+
});
|
|
17127
|
+
}
|
|
17128
|
+
}
|
|
16863
17129
|
/**
|
|
16864
17130
|
* Informs the state machine that an agent worker has exited.
|
|
16865
17131
|
*/
|
|
@@ -17946,6 +18212,341 @@ function launchTUI(orchestrator) {
|
|
|
17946
18212
|
return { waitUntilExit };
|
|
17947
18213
|
}
|
|
17948
18214
|
|
|
18215
|
+
// src/agent/triage-wiring.ts
|
|
18216
|
+
import { CascadeSimulator } from "@harness-engineering/graph";
|
|
18217
|
+
import {
|
|
18218
|
+
runScopingProbe,
|
|
18219
|
+
extractEntities,
|
|
18220
|
+
rankTriageCandidates,
|
|
18221
|
+
pilotScore
|
|
18222
|
+
} from "@harness-engineering/intelligence";
|
|
18223
|
+
function buildProbeInput(issue) {
|
|
18224
|
+
const taskText = buildTaskText(issue);
|
|
18225
|
+
const combined = `${issue.title ?? ""}
|
|
18226
|
+
${issue.description ?? ""}`.trim();
|
|
18227
|
+
return {
|
|
18228
|
+
// An item lacking a stable externalId is not dispatch-eligible (proposal Assumptions);
|
|
18229
|
+
// we still probe it (read-only report), keyed by its identifier as a fallback.
|
|
18230
|
+
externalId: issue.externalId ?? issue.identifier ?? issue.id,
|
|
18231
|
+
taskText,
|
|
18232
|
+
entityCandidates: extractEntities(combined),
|
|
18233
|
+
labels: issue.labels ?? []
|
|
18234
|
+
};
|
|
18235
|
+
}
|
|
18236
|
+
function findNode(store, candidate) {
|
|
18237
|
+
const trimmed = candidate.trim();
|
|
18238
|
+
if (trimmed.length === 0) return null;
|
|
18239
|
+
const byName = store.findNodes({ name: trimmed });
|
|
18240
|
+
if (byName.length > 0) return byName[0] ?? null;
|
|
18241
|
+
const byPath = store.findNodes({ path: trimmed });
|
|
18242
|
+
if (byPath.length > 0) return byPath[0] ?? null;
|
|
18243
|
+
const byId = store.getNode(`file:${trimmed}`);
|
|
18244
|
+
if (byId) return byId;
|
|
18245
|
+
return null;
|
|
18246
|
+
}
|
|
18247
|
+
function makeGraphScope(store) {
|
|
18248
|
+
const simulator = new CascadeSimulator(store);
|
|
18249
|
+
return {
|
|
18250
|
+
resolve(candidate) {
|
|
18251
|
+
try {
|
|
18252
|
+
const node = findNode(store, candidate);
|
|
18253
|
+
if (!node) return null;
|
|
18254
|
+
const result = simulator.simulate(node.id);
|
|
18255
|
+
return {
|
|
18256
|
+
candidate,
|
|
18257
|
+
nodeId: node.id,
|
|
18258
|
+
blastRadius: result.summary.totalAffected
|
|
18259
|
+
};
|
|
18260
|
+
} catch {
|
|
18261
|
+
return null;
|
|
18262
|
+
}
|
|
18263
|
+
}
|
|
18264
|
+
};
|
|
18265
|
+
}
|
|
18266
|
+
async function triageIssue2(issue, deps = {}) {
|
|
18267
|
+
const input = buildProbeInput(issue);
|
|
18268
|
+
const graph = deps.graphStore ? makeGraphScope(deps.graphStore) : void 0;
|
|
18269
|
+
return runScopingProbe(input, {
|
|
18270
|
+
...deps.provider ? { provider: deps.provider } : {},
|
|
18271
|
+
...graph ? { graph } : {},
|
|
18272
|
+
...deps.precedent ? { precedent: deps.precedent } : {},
|
|
18273
|
+
...deps.config ? { config: deps.config } : {},
|
|
18274
|
+
...deps.models ? { models: deps.models } : {}
|
|
18275
|
+
});
|
|
18276
|
+
}
|
|
18277
|
+
|
|
18278
|
+
// src/agent/brainstorm-wiring.ts
|
|
18279
|
+
import * as fs16 from "fs";
|
|
18280
|
+
import * as path22 from "path";
|
|
18281
|
+
import { z as z18 } from "zod";
|
|
18282
|
+
import {
|
|
18283
|
+
runAutoBrainstorm,
|
|
18284
|
+
depthForLevel
|
|
18285
|
+
} from "@harness-engineering/intelligence";
|
|
18286
|
+
var ForkStepSchema = z18.object({
|
|
18287
|
+
done: z18.boolean().describe("true when there are no more design forks to decide"),
|
|
18288
|
+
forkId: z18.string().describe('short stable id for this fork, e.g. "storage-backend"').optional(),
|
|
18289
|
+
question: z18.string().describe("the design question this fork decides").optional(),
|
|
18290
|
+
options: z18.array(z18.string()).describe("the mutually-exclusive options considered").optional(),
|
|
18291
|
+
// The AUTHORITATIVE recommendation validity checks (non-empty AND one of `options`) live in
|
|
18292
|
+
// the GENERATOR, where `fork.options` is known and where a degenerate value must map to a
|
|
18293
|
+
// `confidence:'low'` DOWNGRADE (→ runner halts with reason 'low-confidence'), not a hard
|
|
18294
|
+
// schema/provider throw. We deliberately keep this `optional()`/unconstrained so an empty or
|
|
18295
|
+
// absent recommendation still reaches the generator's semantic gate rather than short-
|
|
18296
|
+
// circuiting to `halted{ reason:'error' }`. See makeSelForkGenerator's SAFETY GATE 2.
|
|
18297
|
+
recommendation: z18.string().describe("the recommended option (one of options)").optional(),
|
|
18298
|
+
confidence: z18.enum(["high", "medium", "low"]).describe("self-assessed confidence in the recommendation").optional(),
|
|
18299
|
+
rationale: z18.string().describe("why this option").optional()
|
|
18300
|
+
});
|
|
18301
|
+
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.';
|
|
18302
|
+
function makeSelForkGenerator(provider, input, opts = {}) {
|
|
18303
|
+
const samples = Math.max(2, opts.samples ?? 3);
|
|
18304
|
+
const maxTokens = opts.maxTokens ?? 512;
|
|
18305
|
+
return {
|
|
18306
|
+
async next(index, priorDecisions) {
|
|
18307
|
+
const prompt = buildForkPrompt(input, priorDecisions, index);
|
|
18308
|
+
const steps = [];
|
|
18309
|
+
for (let s = 0; s < samples; s++) {
|
|
18310
|
+
const { result } = await provider.analyze({
|
|
18311
|
+
prompt,
|
|
18312
|
+
systemPrompt: BRAINSTORM_RUBRIC,
|
|
18313
|
+
responseSchema: ForkStepSchema,
|
|
18314
|
+
maxTokens,
|
|
18315
|
+
...opts.model !== void 0 ? { model: opts.model } : {}
|
|
18316
|
+
});
|
|
18317
|
+
steps.push(result);
|
|
18318
|
+
}
|
|
18319
|
+
if (steps.some((st) => st.done)) return null;
|
|
18320
|
+
const first = steps[0];
|
|
18321
|
+
if (!first) return null;
|
|
18322
|
+
const fork = {
|
|
18323
|
+
id: first.forkId ?? `fork-${index}`,
|
|
18324
|
+
question: first.question ?? "unspecified fork",
|
|
18325
|
+
options: first.options ?? []
|
|
18326
|
+
};
|
|
18327
|
+
const recommendation = first.recommendation ?? "";
|
|
18328
|
+
const normQ = (q) => (q ?? "").trim().toLowerCase();
|
|
18329
|
+
const firstForkId = first.forkId ?? "";
|
|
18330
|
+
const firstQuestion = normQ(first.question);
|
|
18331
|
+
const identityStable = steps.every(
|
|
18332
|
+
(st) => (st.forkId ?? "") === firstForkId && normQ(st.question) === firstQuestion
|
|
18333
|
+
);
|
|
18334
|
+
const recStable = steps.every((st) => (st.recommendation ?? "") === recommendation);
|
|
18335
|
+
const recEmpty = recommendation.trim() === "";
|
|
18336
|
+
const recNotAnOption = !recEmpty && fork.options.length > 0 && !fork.options.includes(recommendation);
|
|
18337
|
+
const stable = identityStable && recStable && !recEmpty && !recNotAnOption;
|
|
18338
|
+
const reported = first.confidence ?? "low";
|
|
18339
|
+
const confidence = stable ? reported : "low";
|
|
18340
|
+
let downgradeReason = "";
|
|
18341
|
+
if (!identityStable) {
|
|
18342
|
+
downgradeReason = `fork identity drift across ${samples} samples (self-consistency downgrade to low): ` + steps.map((st) => `${st.forkId ?? "\u2205"}:"${(st.question ?? "\u2205").trim()}"`).join(" / ");
|
|
18343
|
+
} else if (recEmpty) {
|
|
18344
|
+
downgradeReason = "recommendation was empty/absent (a content-free recommendation is not a confident decision; downgrade to low)";
|
|
18345
|
+
} else if (recNotAnOption) {
|
|
18346
|
+
downgradeReason = `recommendation '${recommendation}' is not one of the offered options [${fork.options.join(", ")}] (downgrade to low)`;
|
|
18347
|
+
} else if (!recStable) {
|
|
18348
|
+
downgradeReason = `recommendation was unstable across ${samples} samples (self-consistency downgrade to low): ` + steps.map((st) => st.recommendation ?? "\u2205").join(" / ");
|
|
18349
|
+
}
|
|
18350
|
+
return {
|
|
18351
|
+
fork,
|
|
18352
|
+
recommendation,
|
|
18353
|
+
confidence,
|
|
18354
|
+
rationale: stable ? first.rationale ?? "" : downgradeReason
|
|
18355
|
+
};
|
|
18356
|
+
}
|
|
18357
|
+
};
|
|
18358
|
+
}
|
|
18359
|
+
function buildForkPrompt(input, priorDecisions, index) {
|
|
18360
|
+
const resolved = priorDecisions.length === 0 ? "(none yet)" : priorDecisions.map((d, i) => `${i + 1}. ${d.fork.question} \u2192 ${d.recommendation}`).join("\n");
|
|
18361
|
+
return `Item: ${input.title}
|
|
18362
|
+
Summary: ${input.summary}
|
|
18363
|
+
Complexity: ${input.level}
|
|
18364
|
+
|
|
18365
|
+
Forks already resolved:
|
|
18366
|
+
${resolved}
|
|
18367
|
+
|
|
18368
|
+
Decide fork #${index + 1}: the NEXT unresolved design fork. If every real fork is already resolved, return { "done": true }.`;
|
|
18369
|
+
}
|
|
18370
|
+
function brainstormInputFromIssue(issue, level) {
|
|
18371
|
+
return {
|
|
18372
|
+
externalId: issue.externalId ?? issue.identifier ?? issue.id,
|
|
18373
|
+
title: issue.title ?? issue.identifier ?? issue.id,
|
|
18374
|
+
summary: issue.description ?? "",
|
|
18375
|
+
level
|
|
18376
|
+
};
|
|
18377
|
+
}
|
|
18378
|
+
async function runBrainstormForIssue(issue, level, deps) {
|
|
18379
|
+
const input = brainstormInputFromIssue(issue, level);
|
|
18380
|
+
const generator = deps.generator ?? (deps.provider ? makeSelForkGenerator(deps.provider, input, deps.generatorOptions ?? {}) : void 0);
|
|
18381
|
+
if (!generator) {
|
|
18382
|
+
return {
|
|
18383
|
+
outcome: {
|
|
18384
|
+
kind: "halted",
|
|
18385
|
+
fork: { id: "no-generator", question: "no fork generator or provider wired", options: [] },
|
|
18386
|
+
reason: "error",
|
|
18387
|
+
detail: "runBrainstormForIssue requires either an injected generator or a SEL provider"
|
|
18388
|
+
}
|
|
18389
|
+
};
|
|
18390
|
+
}
|
|
18391
|
+
const depth = depthForLevel(level);
|
|
18392
|
+
const outcome = await runAutoBrainstorm(input, generator, depth);
|
|
18393
|
+
if (outcome.kind !== "completed") {
|
|
18394
|
+
return { outcome };
|
|
18395
|
+
}
|
|
18396
|
+
const result = { outcome };
|
|
18397
|
+
if (deps.docsRoot) {
|
|
18398
|
+
try {
|
|
18399
|
+
result.specPath = writeSpecDoc(deps.docsRoot, outcome.spec);
|
|
18400
|
+
} catch {
|
|
18401
|
+
}
|
|
18402
|
+
}
|
|
18403
|
+
if (deps.rescore) {
|
|
18404
|
+
try {
|
|
18405
|
+
const enrichedIssue = enrichIssueWithSpec(issue, outcome.spec);
|
|
18406
|
+
result.rescore = await triageIssue2(enrichedIssue, {
|
|
18407
|
+
...deps.rescore.graphStore ? { graphStore: deps.rescore.graphStore } : {},
|
|
18408
|
+
...deps.rescore.provider ? { provider: deps.rescore.provider } : {},
|
|
18409
|
+
...deps.rescore.precedent ? { precedent: deps.rescore.precedent } : {},
|
|
18410
|
+
...deps.rescore.config ? { config: deps.rescore.config } : {},
|
|
18411
|
+
...deps.rescore.models ? { models: deps.rescore.models } : {}
|
|
18412
|
+
});
|
|
18413
|
+
} catch {
|
|
18414
|
+
}
|
|
18415
|
+
}
|
|
18416
|
+
return result;
|
|
18417
|
+
}
|
|
18418
|
+
function slugFor(spec) {
|
|
18419
|
+
const base = spec.title || spec.externalId;
|
|
18420
|
+
return base.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "item";
|
|
18421
|
+
}
|
|
18422
|
+
function renderSpecMarkdown(spec) {
|
|
18423
|
+
const lines = [];
|
|
18424
|
+
lines.push(`# ${spec.title}`);
|
|
18425
|
+
lines.push("");
|
|
18426
|
+
lines.push("Status: **Auto-drafted (autonomous brainstorm \u2014 awaiting human review)**");
|
|
18427
|
+
lines.push("");
|
|
18428
|
+
lines.push(`Item: \`${spec.externalId}\``);
|
|
18429
|
+
lines.push("");
|
|
18430
|
+
lines.push("## Summary");
|
|
18431
|
+
lines.push("");
|
|
18432
|
+
lines.push(spec.summary || "_No summary provided._");
|
|
18433
|
+
lines.push("");
|
|
18434
|
+
lines.push("## Design decisions");
|
|
18435
|
+
lines.push("");
|
|
18436
|
+
if (spec.decisions.length === 0) {
|
|
18437
|
+
lines.push("_No forks required a decision \u2014 the item was unambiguous._");
|
|
18438
|
+
} else {
|
|
18439
|
+
for (const [i, d] of spec.decisions.entries()) {
|
|
18440
|
+
lines.push(`### ${i + 1}. ${d.fork.question}`);
|
|
18441
|
+
lines.push("");
|
|
18442
|
+
if (d.fork.options.length > 0) {
|
|
18443
|
+
lines.push(`Options considered: ${d.fork.options.map((o) => `\`${o}\``).join(", ")}`);
|
|
18444
|
+
lines.push("");
|
|
18445
|
+
}
|
|
18446
|
+
lines.push(`**Recommendation:** ${d.recommendation} (confidence: ${d.confidence})`);
|
|
18447
|
+
lines.push("");
|
|
18448
|
+
if (d.rationale) {
|
|
18449
|
+
lines.push(d.rationale);
|
|
18450
|
+
lines.push("");
|
|
18451
|
+
}
|
|
18452
|
+
}
|
|
18453
|
+
}
|
|
18454
|
+
lines.push("---");
|
|
18455
|
+
lines.push("");
|
|
18456
|
+
lines.push(
|
|
18457
|
+
"_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._"
|
|
18458
|
+
);
|
|
18459
|
+
lines.push("");
|
|
18460
|
+
return lines.join("\n");
|
|
18461
|
+
}
|
|
18462
|
+
function writeSpecDoc(docsRoot, spec) {
|
|
18463
|
+
const dir = path22.join(docsRoot, "changes", slugFor(spec));
|
|
18464
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
18465
|
+
const file = path22.join(dir, "proposal.md");
|
|
18466
|
+
fs16.writeFileSync(file, renderSpecMarkdown(spec), "utf-8");
|
|
18467
|
+
return file;
|
|
18468
|
+
}
|
|
18469
|
+
function enrichIssueWithSpec(issue, spec) {
|
|
18470
|
+
const decisionText = spec.decisions.map((d) => `${d.fork.question} \u2192 ${d.recommendation}. ${d.rationale}`).join("\n");
|
|
18471
|
+
const enrichedDescription = [issue.description ?? "", decisionText].filter(Boolean).join("\n\n");
|
|
18472
|
+
return {
|
|
18473
|
+
...issue,
|
|
18474
|
+
description: enrichedDescription,
|
|
18475
|
+
// The item now HAS a drafted spec — reflect it so the re-score's spec-exists signal is true.
|
|
18476
|
+
spec: issue.spec ?? `changes/${slugFor(spec)}/proposal.md`
|
|
18477
|
+
};
|
|
18478
|
+
}
|
|
18479
|
+
|
|
18480
|
+
// src/agent/triage-mark.ts
|
|
18481
|
+
import { slugifyFeatureName } from "@harness-engineering/core";
|
|
18482
|
+
import { dispatchableShapeKey } from "@harness-engineering/intelligence";
|
|
18483
|
+
var ELIGIBLE_STATUS = "planned";
|
|
18484
|
+
async function markApprovedForDispatch(items, deps) {
|
|
18485
|
+
const { store, config, recordPrediction, selfAssignee } = deps;
|
|
18486
|
+
if (!config.enabled) {
|
|
18487
|
+
return { ok: true, value: { marked: [], skipped: [] } };
|
|
18488
|
+
}
|
|
18489
|
+
const loaded = await store.load();
|
|
18490
|
+
if (!loaded.ok) return { ok: false, error: loaded.error };
|
|
18491
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
18492
|
+
for (const m of loaded.value.milestones) {
|
|
18493
|
+
for (const f of m.features) bySlug.set(slugifyFeatureName(f.name), f);
|
|
18494
|
+
}
|
|
18495
|
+
const marked = [];
|
|
18496
|
+
const skipped = [];
|
|
18497
|
+
for (const it of items) {
|
|
18498
|
+
const externalId = it.candidate.externalId;
|
|
18499
|
+
if (!it.candidate.humanApproved) {
|
|
18500
|
+
skipped.push({ externalId, reason: "not-approved" });
|
|
18501
|
+
continue;
|
|
18502
|
+
}
|
|
18503
|
+
const slug = slugifyFeatureName(it.featureName);
|
|
18504
|
+
const target = bySlug.get(slug);
|
|
18505
|
+
if (!target) {
|
|
18506
|
+
skipped.push({ externalId, reason: "feature-not-found" });
|
|
18507
|
+
continue;
|
|
18508
|
+
}
|
|
18509
|
+
if (selfAssignee !== void 0 && target.assignee != null && target.assignee !== selfAssignee) {
|
|
18510
|
+
skipped.push({ externalId, reason: "assigned-elsewhere" });
|
|
18511
|
+
continue;
|
|
18512
|
+
}
|
|
18513
|
+
const mutate = (f) => ({
|
|
18514
|
+
...f,
|
|
18515
|
+
spec: it.specPath,
|
|
18516
|
+
status: isActiveStatus(f.status) ? f.status : ELIGIBLE_STATUS
|
|
18517
|
+
});
|
|
18518
|
+
const patched = await store.patchFeature(slug, mutate);
|
|
18519
|
+
if (!patched.ok) return { ok: false, error: patched.error };
|
|
18520
|
+
const key = dispatchableShapeKey(it.labels, it.verdict.level);
|
|
18521
|
+
const prediction = {
|
|
18522
|
+
externalId,
|
|
18523
|
+
shapeKey: key,
|
|
18524
|
+
verdict: it.verdict,
|
|
18525
|
+
// `levers` is an OPAQUE DIAGNOSTIC SNAPSHOT (the verdict's static signals), NOT the
|
|
18526
|
+
// typed ProbeLevers. The Phase-4 comparator grades on `verdict.level` +
|
|
18527
|
+
// `scopeEstimate` ONLY (see retrospective.ts) and never reads `levers`, so this
|
|
18528
|
+
// human-legible signal dump cannot mislead the grade. The full typed ProbeLevers
|
|
18529
|
+
// are not threaded here on purpose (it would balloon ReadyCandidate→marker for a
|
|
18530
|
+
// field nothing downstream consumes); `record.ts` re-documents the field to match.
|
|
18531
|
+
levers: it.verdict.signals,
|
|
18532
|
+
scopeEstimate: it.scopeEstimate,
|
|
18533
|
+
// Prefer the per-shape evidence-derived stage (SC6) when the caller resolved one;
|
|
18534
|
+
// fall back to the uniform config stage (Phase-3 behavior). Cold-start ⇒ the caller
|
|
18535
|
+
// resolves stage 1, so this is byte-identical to `config.ratchetStage` at cold-start.
|
|
18536
|
+
ratchetStage: it.effectiveStage ?? config.ratchetStage
|
|
18537
|
+
};
|
|
18538
|
+
const recorded = await recordPrediction(prediction);
|
|
18539
|
+
if (!recorded.ok) {
|
|
18540
|
+
return { ok: false, error: recorded.error ?? new Error("recordPrediction failed") };
|
|
18541
|
+
}
|
|
18542
|
+
marked.push(externalId);
|
|
18543
|
+
}
|
|
18544
|
+
return { ok: true, value: { marked, skipped } };
|
|
18545
|
+
}
|
|
18546
|
+
function isActiveStatus(status) {
|
|
18547
|
+
return status === "planned" || status === "in-progress";
|
|
18548
|
+
}
|
|
18549
|
+
|
|
17949
18550
|
// src/maintenance/sync-main.ts
|
|
17950
18551
|
import { execFile as nodeExecFile2 } from "child_process";
|
|
17951
18552
|
import { promisify as promisify5 } from "util";
|
|
@@ -18139,8 +18740,8 @@ function selectTasks(tasks, history, filter) {
|
|
|
18139
18740
|
}
|
|
18140
18741
|
|
|
18141
18742
|
// src/sessions/search-index.ts
|
|
18142
|
-
import * as
|
|
18143
|
-
import * as
|
|
18743
|
+
import * as fs17 from "fs";
|
|
18744
|
+
import * as path23 from "path";
|
|
18144
18745
|
import Database2 from "better-sqlite3";
|
|
18145
18746
|
import { INDEXED_FILE_KINDS } from "@harness-engineering/types";
|
|
18146
18747
|
var SEARCH_INDEX_FILE = "search-index.sqlite";
|
|
@@ -18185,7 +18786,7 @@ function normalizeFts5Query(query) {
|
|
|
18185
18786
|
return query.split(/\s+/).filter((tok) => tok.length > 0).map((tok) => `"${tok.replace(/"/g, '""')}"`).join(" ");
|
|
18186
18787
|
}
|
|
18187
18788
|
function searchIndexPath(projectPath) {
|
|
18188
|
-
return
|
|
18789
|
+
return path23.join(projectPath, ".harness", SEARCH_INDEX_FILE);
|
|
18189
18790
|
}
|
|
18190
18791
|
var FILE_KIND_TO_FILENAME = {
|
|
18191
18792
|
summary: "summary.md",
|
|
@@ -18200,7 +18801,7 @@ var SqliteSearchIndex = class {
|
|
|
18200
18801
|
removeSessionStmt;
|
|
18201
18802
|
totalStmt;
|
|
18202
18803
|
constructor(dbPath) {
|
|
18203
|
-
|
|
18804
|
+
fs17.mkdirSync(path23.dirname(dbPath), { recursive: true });
|
|
18204
18805
|
this.db = new Database2(dbPath);
|
|
18205
18806
|
this.db.pragma("journal_mode = WAL");
|
|
18206
18807
|
this.db.pragma("synchronous = NORMAL");
|
|
@@ -18305,14 +18906,14 @@ function indexSessionDirectory(idx, args) {
|
|
|
18305
18906
|
let docsWritten = 0;
|
|
18306
18907
|
for (const kind of kinds) {
|
|
18307
18908
|
const fileName = FILE_KIND_TO_FILENAME[kind];
|
|
18308
|
-
const filePath =
|
|
18309
|
-
if (!
|
|
18310
|
-
let body =
|
|
18909
|
+
const filePath = path23.join(args.sessionDir, fileName);
|
|
18910
|
+
if (!fs17.existsSync(filePath)) continue;
|
|
18911
|
+
let body = fs17.readFileSync(filePath, "utf8");
|
|
18311
18912
|
if (Buffer.byteLength(body, "utf8") > cap) {
|
|
18312
18913
|
body = body.slice(0, cap) + "\n\n[TRUNCATED]";
|
|
18313
18914
|
}
|
|
18314
|
-
const stat =
|
|
18315
|
-
const relPath =
|
|
18915
|
+
const stat = fs17.statSync(filePath);
|
|
18916
|
+
const relPath = path23.relative(args.projectPath, filePath).replaceAll("\\", "/");
|
|
18316
18917
|
idx.upsertSessionDoc({
|
|
18317
18918
|
sessionId: args.sessionId,
|
|
18318
18919
|
archived: args.archived,
|
|
@@ -18327,17 +18928,17 @@ function indexSessionDirectory(idx, args) {
|
|
|
18327
18928
|
}
|
|
18328
18929
|
function reindexFromArchive(projectPath, opts = {}) {
|
|
18329
18930
|
const start = Date.now();
|
|
18330
|
-
const archiveBase =
|
|
18931
|
+
const archiveBase = path23.join(projectPath, ".harness", "archive", "sessions");
|
|
18331
18932
|
const idx = openSearchIndex(projectPath);
|
|
18332
18933
|
try {
|
|
18333
18934
|
idx.resetArchived();
|
|
18334
18935
|
let sessionsIndexed = 0;
|
|
18335
18936
|
let docsWritten = 0;
|
|
18336
|
-
if (
|
|
18337
|
-
const entries =
|
|
18937
|
+
if (fs17.existsSync(archiveBase)) {
|
|
18938
|
+
const entries = fs17.readdirSync(archiveBase, { withFileTypes: true });
|
|
18338
18939
|
for (const entry of entries) {
|
|
18339
18940
|
if (!entry.isDirectory()) continue;
|
|
18340
|
-
const sessionDir =
|
|
18941
|
+
const sessionDir = path23.join(archiveBase, entry.name);
|
|
18341
18942
|
const result = indexSessionDirectory(idx, {
|
|
18342
18943
|
sessionId: entry.name,
|
|
18343
18944
|
sessionDir,
|
|
@@ -18357,8 +18958,8 @@ function reindexFromArchive(projectPath, opts = {}) {
|
|
|
18357
18958
|
}
|
|
18358
18959
|
|
|
18359
18960
|
// src/sessions/summarize.ts
|
|
18360
|
-
import * as
|
|
18361
|
-
import * as
|
|
18961
|
+
import * as fs18 from "fs";
|
|
18962
|
+
import * as path24 from "path";
|
|
18362
18963
|
import {
|
|
18363
18964
|
SessionSummarySchema
|
|
18364
18965
|
} from "@harness-engineering/types";
|
|
@@ -18388,10 +18989,10 @@ var USER_PROMPT_PREAMBLE = `Below are the archived files for a single harness-en
|
|
|
18388
18989
|
function readInputCorpus(archiveDir) {
|
|
18389
18990
|
const parts = [];
|
|
18390
18991
|
for (const { filename, kind } of SUMMARY_INPUT_FILES) {
|
|
18391
|
-
const p =
|
|
18392
|
-
if (!
|
|
18992
|
+
const p = path24.join(archiveDir, filename);
|
|
18993
|
+
if (!fs18.existsSync(p)) continue;
|
|
18393
18994
|
try {
|
|
18394
|
-
const content =
|
|
18995
|
+
const content = fs18.readFileSync(p, "utf8");
|
|
18395
18996
|
if (content.trim().length === 0) continue;
|
|
18396
18997
|
parts.push(`## FILE: ${kind}
|
|
18397
18998
|
|
|
@@ -18442,7 +19043,7 @@ function renderLlmSummaryMarkdown(summary, meta) {
|
|
|
18442
19043
|
return lines.join("\n");
|
|
18443
19044
|
}
|
|
18444
19045
|
function writeStubMarkdown(archiveDir, reason) {
|
|
18445
|
-
const filePath =
|
|
19046
|
+
const filePath = path24.join(archiveDir, LLM_SUMMARY_FILE);
|
|
18446
19047
|
const body = `---
|
|
18447
19048
|
generatedAt: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
18448
19049
|
schemaVersion: 1
|
|
@@ -18453,12 +19054,12 @@ status: failed
|
|
|
18453
19054
|
|
|
18454
19055
|
- reason: ${reason}
|
|
18455
19056
|
`;
|
|
18456
|
-
|
|
19057
|
+
fs18.writeFileSync(filePath, body, "utf8");
|
|
18457
19058
|
return filePath;
|
|
18458
19059
|
}
|
|
18459
19060
|
async function summarizeArchivedSession(ctx) {
|
|
18460
19061
|
const writeStubOnError = ctx.writeStubOnError ?? true;
|
|
18461
|
-
if (!
|
|
19062
|
+
if (!fs18.existsSync(ctx.archiveDir)) {
|
|
18462
19063
|
return Err23(new Error(`archive directory not found: ${ctx.archiveDir}`));
|
|
18463
19064
|
}
|
|
18464
19065
|
const corpus = readInputCorpus(ctx.archiveDir);
|
|
@@ -18519,9 +19120,9 @@ async function summarizeArchivedSession(ctx) {
|
|
|
18519
19120
|
outputTokens: response.tokenUsage.outputTokens,
|
|
18520
19121
|
schemaVersion: 1
|
|
18521
19122
|
};
|
|
18522
|
-
const filePath =
|
|
19123
|
+
const filePath = path24.join(ctx.archiveDir, LLM_SUMMARY_FILE);
|
|
18523
19124
|
const body = renderLlmSummaryMarkdown(parsed.data, meta);
|
|
18524
|
-
|
|
19125
|
+
fs18.writeFileSync(filePath, body, "utf8");
|
|
18525
19126
|
return Ok24({ summary: parsed.data, meta, filePath });
|
|
18526
19127
|
}
|
|
18527
19128
|
function isSummaryEnabled(config) {
|
|
@@ -18602,6 +19203,7 @@ import { discoverCandidates } from "@harness-engineering/local-models";
|
|
|
18602
19203
|
export {
|
|
18603
19204
|
AdaptiveRouter,
|
|
18604
19205
|
AnalysisArchive,
|
|
19206
|
+
BRAINSTORM_RUBRIC,
|
|
18605
19207
|
BUILT_IN_TASKS,
|
|
18606
19208
|
BackendDefSchema,
|
|
18607
19209
|
BackendRouter,
|
|
@@ -18644,8 +19246,10 @@ export {
|
|
|
18644
19246
|
WorkspaceManager,
|
|
18645
19247
|
applyEvent,
|
|
18646
19248
|
artifactPresenceFromIssue,
|
|
19249
|
+
brainstormInputFromIssue,
|
|
18647
19250
|
buildArchiveHooks,
|
|
18648
19251
|
buildCapabilityRegistry,
|
|
19252
|
+
buildProbeInput,
|
|
18649
19253
|
buildWorkflowContext,
|
|
18650
19254
|
calculateRetryDelay,
|
|
18651
19255
|
canDispatch,
|
|
@@ -18665,6 +19269,7 @@ export {
|
|
|
18665
19269
|
emitProposalApproved,
|
|
18666
19270
|
emitProposalCreated,
|
|
18667
19271
|
emitProposalRejected,
|
|
19272
|
+
enrichIssueWithSpec,
|
|
18668
19273
|
estimateCost,
|
|
18669
19274
|
explicitFindingsCount,
|
|
18670
19275
|
extractHighlights,
|
|
@@ -18679,22 +19284,30 @@ export {
|
|
|
18679
19284
|
launchTUI,
|
|
18680
19285
|
loadPublishedIndex,
|
|
18681
19286
|
makeBackendResolver,
|
|
19287
|
+
makeGraphScope,
|
|
19288
|
+
makeSelForkGenerator,
|
|
19289
|
+
markApprovedForDispatch,
|
|
18682
19290
|
migrateAgentConfig,
|
|
18683
19291
|
normalizeFts5Query,
|
|
18684
19292
|
normalizeHarnessCommand,
|
|
18685
19293
|
normalizeLocalModel,
|
|
18686
19294
|
openSearchIndex,
|
|
19295
|
+
pilotScore,
|
|
19296
|
+
precedentLookupFromStored,
|
|
18687
19297
|
promote,
|
|
19298
|
+
rankTriageCandidates,
|
|
18688
19299
|
reconcile,
|
|
18689
19300
|
recoverFindingsCount,
|
|
18690
19301
|
reindexFromArchive,
|
|
18691
19302
|
renderAnalysisComment,
|
|
18692
19303
|
renderLlmSummaryMarkdown,
|
|
18693
19304
|
renderPRComment,
|
|
19305
|
+
renderSpecMarkdown,
|
|
18694
19306
|
resolveEscalationConfig,
|
|
18695
19307
|
resolveOrchestratorId,
|
|
18696
19308
|
routeIssue,
|
|
18697
19309
|
routingWarnings,
|
|
19310
|
+
runBrainstormForIssue,
|
|
18698
19311
|
runGate,
|
|
18699
19312
|
runHarnessCheck,
|
|
18700
19313
|
savePublishedIndex,
|
|
@@ -18702,10 +19315,11 @@ export {
|
|
|
18702
19315
|
selectCandidates,
|
|
18703
19316
|
selectCheapestQualifying,
|
|
18704
19317
|
selectTasks,
|
|
19318
|
+
slugFor,
|
|
18705
19319
|
sortCandidates,
|
|
18706
19320
|
summarizeArchivedSession,
|
|
18707
19321
|
syncMain,
|
|
18708
|
-
triageIssue,
|
|
19322
|
+
triageIssue2 as triageIssue,
|
|
18709
19323
|
truncateForBudget,
|
|
18710
19324
|
validateCustomTasks,
|
|
18711
19325
|
validateWorkflowConfig,
|