@kyo-so/cli 0.10.0 → 0.11.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/.agents/skills/kyoso-review/SKILL.md +13 -3
- package/CHANGELOG.md +22 -0
- package/README.ja.md +25 -8
- package/README.md +25 -8
- package/README.zh-CN.md +25 -8
- package/dist/acp/FakeAgentManager.d.ts +1 -1
- package/dist/bin/kyoso.js +1344 -119
- package/dist/cli/knownSkillDigests.d.ts +1 -1
- package/dist/cli/pluginRuntimeContract.d.ts +10 -8
- package/dist/config/schema.d.ts +7 -0
- package/dist/core/constants.d.ts +3 -1
- package/dist/core/requestFingerprint.d.ts +11 -0
- package/dist/core/reviewBudget.d.ts +68 -0
- package/dist/core/tokenUsage.d.ts +2 -0
- package/dist/core/types.d.ts +70 -0
- package/dist/core/verification.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1306 -91
- package/dist/judge/anthropic.d.ts +2 -2
- package/dist/judge/openai.d.ts +2 -2
- package/dist/judge/provider.d.ts +7 -1
- package/dist/mcp/schemas.d.ts +7 -0
- package/package.json +1 -1
package/dist/bin/kyoso.js
CHANGED
|
@@ -183945,10 +183945,12 @@ function matchesPathPattern(path, patterns, mode) {
|
|
|
183945
183945
|
|
|
183946
183946
|
// src/core/constants.ts
|
|
183947
183947
|
var DEFAULT_AGENT_TIMEOUT_MS = 120000;
|
|
183948
|
+
var MAX_AGENT_OUTPUT_BYTES = 1048576;
|
|
183949
|
+
var JUDGE_MAX_OUTPUT_TOKENS = 4096;
|
|
183948
183950
|
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
183949
183951
|
var TRACE_DIR = ".kyoso/traces";
|
|
183950
183952
|
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
183951
|
-
var KYOSO_VERSION = "0.
|
|
183953
|
+
var KYOSO_VERSION = "0.11.0";
|
|
183952
183954
|
|
|
183953
183955
|
// src/utils/pathContainment.ts
|
|
183954
183956
|
import { resolve, sep as sep2 } from "node:path";
|
|
@@ -184345,7 +184347,7 @@ var defaultConfig = {
|
|
|
184345
184347
|
}
|
|
184346
184348
|
},
|
|
184347
184349
|
judge: {
|
|
184348
|
-
mode: "
|
|
184350
|
+
mode: "deterministic_only",
|
|
184349
184351
|
provider: "auto",
|
|
184350
184352
|
timeoutMs: 60000
|
|
184351
184353
|
},
|
|
@@ -184355,6 +184357,13 @@ var defaultConfig = {
|
|
|
184355
184357
|
timeoutMs: 90000,
|
|
184356
184358
|
allowDemotion: false
|
|
184357
184359
|
},
|
|
184360
|
+
reviewBudget: {
|
|
184361
|
+
maxModelCalls: 4,
|
|
184362
|
+
maxTotalWallTimeMs: 480000,
|
|
184363
|
+
maxAgentOutputBytes: 65536,
|
|
184364
|
+
maxFindingsPerAgent: 10,
|
|
184365
|
+
skipOptionalPhasesWhenTokenUsageUnknown: true
|
|
184366
|
+
},
|
|
184358
184367
|
audit: {
|
|
184359
184368
|
enabled: true,
|
|
184360
184369
|
format: "jsonl",
|
|
@@ -184412,7 +184421,7 @@ function collectProjectScopeViolations(config2) {
|
|
|
184412
184421
|
const violations = [];
|
|
184413
184422
|
for (const leaf of leaves) {
|
|
184414
184423
|
const path = leaf.path.join(".");
|
|
184415
|
-
const globalOnlyReason =
|
|
184424
|
+
const globalOnlyReason = projectGlobalOnlyReason(leaf.path);
|
|
184416
184425
|
if (globalOnlyReason) {
|
|
184417
184426
|
violations.push({ path, reason: globalOnlyReason });
|
|
184418
184427
|
continue;
|
|
@@ -184427,6 +184436,15 @@ function collectProjectScopeViolations(config2) {
|
|
|
184427
184436
|
}
|
|
184428
184437
|
return violations.sort((left, right) => left.path.localeCompare(right.path));
|
|
184429
184438
|
}
|
|
184439
|
+
function projectGlobalOnlyReason(path) {
|
|
184440
|
+
const exactReason = PROJECT_GLOBAL_ONLY_REASONS[path.join(".")];
|
|
184441
|
+
if (exactReason)
|
|
184442
|
+
return exactReason;
|
|
184443
|
+
if (path[0] === "reviewBudget") {
|
|
184444
|
+
return "must be a user-global review budget ceiling";
|
|
184445
|
+
}
|
|
184446
|
+
return;
|
|
184447
|
+
}
|
|
184430
184448
|
function isAllowedProjectPath(path) {
|
|
184431
184449
|
const [top, second, third, fourth] = path;
|
|
184432
184450
|
if (isAllowedConfigOverridePath(path))
|
|
@@ -184599,6 +184617,13 @@ var codexAgentSchema = baseAgentSchema.extend({
|
|
|
184599
184617
|
}
|
|
184600
184618
|
});
|
|
184601
184619
|
});
|
|
184620
|
+
var reviewBudgetSchema = exports_external.object({
|
|
184621
|
+
maxModelCalls: exports_external.number().int().positive(),
|
|
184622
|
+
maxTotalWallTimeMs: exports_external.number().int().positive(),
|
|
184623
|
+
maxAgentOutputBytes: exports_external.number().int().positive().max(MAX_AGENT_OUTPUT_BYTES),
|
|
184624
|
+
maxFindingsPerAgent: exports_external.number().int().positive(),
|
|
184625
|
+
skipOptionalPhasesWhenTokenUsageUnknown: exports_external.boolean()
|
|
184626
|
+
});
|
|
184602
184627
|
var kyosoConfigSchema = exports_external.object({
|
|
184603
184628
|
entrypoints: exports_external.object({
|
|
184604
184629
|
mcp: exports_external.boolean(),
|
|
@@ -184656,6 +184681,7 @@ var kyosoConfigSchema = exports_external.object({
|
|
|
184656
184681
|
timeoutMs: exports_external.number().int().positive().default(90000),
|
|
184657
184682
|
allowDemotion: exports_external.boolean().default(false)
|
|
184658
184683
|
}),
|
|
184684
|
+
reviewBudget: reviewBudgetSchema,
|
|
184659
184685
|
audit: exports_external.object({
|
|
184660
184686
|
enabled: exports_external.boolean(),
|
|
184661
184687
|
format: exports_external.literal("jsonl"),
|
|
@@ -184663,6 +184689,15 @@ var kyosoConfigSchema = exports_external.object({
|
|
|
184663
184689
|
includeRawAgentOutput: exports_external.boolean(),
|
|
184664
184690
|
includeFileContents: exports_external.boolean()
|
|
184665
184691
|
})
|
|
184692
|
+
}).superRefine((config2, context) => {
|
|
184693
|
+
const enabledPrimaryReviewers = Object.values(config2.agents).filter((agent) => agent.enabled).length;
|
|
184694
|
+
if (config2.reviewBudget.maxModelCalls >= enabledPrimaryReviewers)
|
|
184695
|
+
return;
|
|
184696
|
+
context.addIssue({
|
|
184697
|
+
code: exports_external.ZodIssueCode.custom,
|
|
184698
|
+
path: ["reviewBudget", "maxModelCalls"],
|
|
184699
|
+
message: "must be greater than or equal to the number of enabled primary reviewers."
|
|
184700
|
+
});
|
|
184666
184701
|
});
|
|
184667
184702
|
function agentConfigLeafPaths(agent) {
|
|
184668
184703
|
const paths = [
|
|
@@ -184721,6 +184756,11 @@ var kyosoConfigKnownLeafPaths = [
|
|
|
184721
184756
|
"verification.maxFindings",
|
|
184722
184757
|
"verification.timeoutMs",
|
|
184723
184758
|
"verification.allowDemotion",
|
|
184759
|
+
"reviewBudget.maxModelCalls",
|
|
184760
|
+
"reviewBudget.maxTotalWallTimeMs",
|
|
184761
|
+
"reviewBudget.maxAgentOutputBytes",
|
|
184762
|
+
"reviewBudget.maxFindingsPerAgent",
|
|
184763
|
+
"reviewBudget.skipOptionalPhasesWhenTokenUsageUnknown",
|
|
184724
184764
|
"audit.enabled",
|
|
184725
184765
|
"audit.format",
|
|
184726
184766
|
"audit.directory",
|
|
@@ -184740,6 +184780,7 @@ var kyosoConfigSecuritySensitivePrefixes = [
|
|
|
184740
184780
|
"secrets",
|
|
184741
184781
|
"securityReview",
|
|
184742
184782
|
"verification",
|
|
184783
|
+
"reviewBudget",
|
|
184743
184784
|
"workspace"
|
|
184744
184785
|
];
|
|
184745
184786
|
|
|
@@ -186203,6 +186244,33 @@ async function exists2(path) {
|
|
|
186203
186244
|
}
|
|
186204
186245
|
}
|
|
186205
186246
|
|
|
186247
|
+
// src/core/tokenUsage.ts
|
|
186248
|
+
var TOKEN_USAGE_KEYS = [
|
|
186249
|
+
"totalTokens",
|
|
186250
|
+
"inputTokens",
|
|
186251
|
+
"outputTokens",
|
|
186252
|
+
"thoughtTokens",
|
|
186253
|
+
"cachedReadTokens",
|
|
186254
|
+
"cachedWriteTokens"
|
|
186255
|
+
];
|
|
186256
|
+
function normalizeModelTokenUsage(usage) {
|
|
186257
|
+
if (!isRecord4(usage))
|
|
186258
|
+
return;
|
|
186259
|
+
const normalized = {};
|
|
186260
|
+
for (const key of TOKEN_USAGE_KEYS) {
|
|
186261
|
+
const value = usage[key];
|
|
186262
|
+
if (isTokenCount(value))
|
|
186263
|
+
normalized[key] = value;
|
|
186264
|
+
}
|
|
186265
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
186266
|
+
}
|
|
186267
|
+
function isTokenCount(value) {
|
|
186268
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
186269
|
+
}
|
|
186270
|
+
function isRecord4(value) {
|
|
186271
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186272
|
+
}
|
|
186273
|
+
|
|
186206
186274
|
// src/judge/prompt.ts
|
|
186207
186275
|
var ANALYSIS_MAX_ITEMS = 5;
|
|
186208
186276
|
var ANALYSIS_MAX_CHARS = 500;
|
|
@@ -186251,7 +186319,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
|
|
|
186251
186319
|
const parsed = JSON.parse(json2);
|
|
186252
186320
|
const summaryText = typeof parsed.summaryText === "string" && parsed.summaryText.trim().length > 0 ? sanitizeText(parsed.summaryText) : fallbackSummaryText;
|
|
186253
186321
|
const disagreementComments = Array.isArray(parsed.disagreementComments) ? parsed.disagreementComments.flatMap((item) => {
|
|
186254
|
-
if (!
|
|
186322
|
+
if (!isRecord5(item) || typeof item.topic !== "string" || typeof item.judgeComment !== "string") {
|
|
186255
186323
|
return [];
|
|
186256
186324
|
}
|
|
186257
186325
|
return [
|
|
@@ -186267,7 +186335,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
|
|
|
186267
186335
|
return { summaryText, disagreementComments, analysis };
|
|
186268
186336
|
}
|
|
186269
186337
|
function parseAnalysis(value) {
|
|
186270
|
-
if (!
|
|
186338
|
+
if (!isRecord5(value))
|
|
186271
186339
|
return;
|
|
186272
186340
|
if (!Array.isArray(value.blindSpots) || !Array.isArray(value.contradictions) || !Array.isArray(value.partialCoverage)) {
|
|
186273
186341
|
return;
|
|
@@ -186275,7 +186343,7 @@ function parseAnalysis(value) {
|
|
|
186275
186343
|
return {
|
|
186276
186344
|
blindSpots: value.blindSpots.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => typeof item === "string" ? [sanitizeAnalysisText(item)] : []),
|
|
186277
186345
|
contradictions: value.contradictions.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
|
|
186278
|
-
if (!
|
|
186346
|
+
if (!isRecord5(item) || typeof item.topic !== "string" || typeof item.detail !== "string") {
|
|
186279
186347
|
return [];
|
|
186280
186348
|
}
|
|
186281
186349
|
return [
|
|
@@ -186286,7 +186354,7 @@ function parseAnalysis(value) {
|
|
|
186286
186354
|
];
|
|
186287
186355
|
}),
|
|
186288
186356
|
partialCoverage: value.partialCoverage.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
|
|
186289
|
-
if (!
|
|
186357
|
+
if (!isRecord5(item) || typeof item.note !== "string")
|
|
186290
186358
|
return [];
|
|
186291
186359
|
const findingId = typeof item.findingId === "string" ? sanitizeAnalysisText(item.findingId) : undefined;
|
|
186292
186360
|
return [
|
|
@@ -186333,7 +186401,7 @@ function extractFirstJsonObject(text) {
|
|
|
186333
186401
|
}
|
|
186334
186402
|
return;
|
|
186335
186403
|
}
|
|
186336
|
-
function
|
|
186404
|
+
function isRecord5(value) {
|
|
186337
186405
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186338
186406
|
}
|
|
186339
186407
|
|
|
@@ -186351,7 +186419,7 @@ async function runAnthropicJudge(input2, timeoutMs) {
|
|
|
186351
186419
|
},
|
|
186352
186420
|
body: JSON.stringify({
|
|
186353
186421
|
model: input2.env.KYOSO_ANTHROPIC_JUDGE_MODEL ?? "claude-haiku-4-5",
|
|
186354
|
-
max_tokens:
|
|
186422
|
+
max_tokens: JUDGE_MAX_OUTPUT_TOKENS,
|
|
186355
186423
|
temperature: 0,
|
|
186356
186424
|
messages: [
|
|
186357
186425
|
{
|
|
@@ -186367,7 +186435,21 @@ async function runAnthropicJudge(input2, timeoutMs) {
|
|
|
186367
186435
|
const content = payload.content?.find((item) => item.type === "text" && item.text)?.text;
|
|
186368
186436
|
if (!content)
|
|
186369
186437
|
throw new Error("Anthropic judge response did not include text content.");
|
|
186370
|
-
|
|
186438
|
+
const usage = normalizeUsage(payload.usage);
|
|
186439
|
+
return {
|
|
186440
|
+
output: parseJudgeOutput(content, input2.summaryText),
|
|
186441
|
+
...usage ? { usage } : {}
|
|
186442
|
+
};
|
|
186443
|
+
}
|
|
186444
|
+
function normalizeUsage(usage) {
|
|
186445
|
+
if (!usage)
|
|
186446
|
+
return;
|
|
186447
|
+
return normalizeModelTokenUsage({
|
|
186448
|
+
inputTokens: usage.input_tokens,
|
|
186449
|
+
outputTokens: usage.output_tokens,
|
|
186450
|
+
cachedReadTokens: usage.cache_read_input_tokens,
|
|
186451
|
+
cachedWriteTokens: usage.cache_creation_input_tokens
|
|
186452
|
+
});
|
|
186371
186453
|
}
|
|
186372
186454
|
async function fetchWithTimeout(url2, init, timeoutMs) {
|
|
186373
186455
|
const controller = new AbortController;
|
|
@@ -186411,6 +186493,7 @@ async function runOpenAiJudge(input2, timeoutMs) {
|
|
|
186411
186493
|
content: buildJudgePrompt(input2.tool, input2.result, input2.summaryText, input2.agentFindings)
|
|
186412
186494
|
}
|
|
186413
186495
|
],
|
|
186496
|
+
max_completion_tokens: JUDGE_MAX_OUTPUT_TOKENS,
|
|
186414
186497
|
temperature: 0
|
|
186415
186498
|
})
|
|
186416
186499
|
}, timeoutMs);
|
|
@@ -186420,7 +186503,22 @@ async function runOpenAiJudge(input2, timeoutMs) {
|
|
|
186420
186503
|
const content = payload.choices?.[0]?.message?.content;
|
|
186421
186504
|
if (!content)
|
|
186422
186505
|
throw new Error("OpenAI judge response did not include content.");
|
|
186423
|
-
|
|
186506
|
+
const usage = normalizeUsage2(payload.usage);
|
|
186507
|
+
return {
|
|
186508
|
+
output: parseJudgeOutput(content, input2.summaryText),
|
|
186509
|
+
...usage ? { usage } : {}
|
|
186510
|
+
};
|
|
186511
|
+
}
|
|
186512
|
+
function normalizeUsage2(usage) {
|
|
186513
|
+
if (!usage)
|
|
186514
|
+
return;
|
|
186515
|
+
return normalizeModelTokenUsage({
|
|
186516
|
+
totalTokens: usage.total_tokens,
|
|
186517
|
+
inputTokens: usage.prompt_tokens,
|
|
186518
|
+
outputTokens: usage.completion_tokens,
|
|
186519
|
+
cachedReadTokens: usage.prompt_tokens_details?.cached_tokens,
|
|
186520
|
+
thoughtTokens: usage.completion_tokens_details?.reasoning_tokens
|
|
186521
|
+
});
|
|
186424
186522
|
}
|
|
186425
186523
|
async function fetchWithTimeout2(url2, init, timeoutMs) {
|
|
186426
186524
|
const controller = new AbortController;
|
|
@@ -186462,8 +186560,13 @@ async function runJudge(input2) {
|
|
|
186462
186560
|
return { provider, status: "deterministic_fallback", output: fallback };
|
|
186463
186561
|
}
|
|
186464
186562
|
try {
|
|
186465
|
-
const output2 = provider === "openai" ? await runOpenAiJudge(input2, input2.config.timeoutMs) : await runAnthropicJudge(input2, input2.config.timeoutMs);
|
|
186466
|
-
return {
|
|
186563
|
+
const output2 = provider === "openai" ? await runOpenAiJudge(input2, input2.timeoutMs ?? input2.config.timeoutMs) : await runAnthropicJudge(input2, input2.timeoutMs ?? input2.config.timeoutMs);
|
|
186564
|
+
return {
|
|
186565
|
+
provider,
|
|
186566
|
+
status: "completed",
|
|
186567
|
+
output: output2.output,
|
|
186568
|
+
...output2.usage ? { usage: output2.usage } : {}
|
|
186569
|
+
};
|
|
186467
186570
|
} catch (error51) {
|
|
186468
186571
|
return {
|
|
186469
186572
|
provider,
|
|
@@ -186714,9 +186817,9 @@ var PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION = 1;
|
|
|
186714
186817
|
var MINIMUM_SUPPORTED_CODEX_VERSION = "0.144.0-alpha.4";
|
|
186715
186818
|
var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
186716
186819
|
distribution: {
|
|
186717
|
-
pluginVersion: "0.
|
|
186820
|
+
pluginVersion: "0.4.0",
|
|
186718
186821
|
mcpCommand: "npx",
|
|
186719
|
-
mcpPackagePin: "@kyo-so/cli@0.
|
|
186822
|
+
mcpPackagePin: "@kyo-so/cli@0.10.0"
|
|
186720
186823
|
},
|
|
186721
186824
|
marketplace: {
|
|
186722
186825
|
name: "kyoso",
|
|
@@ -186744,6 +186847,7 @@ var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
|
186744
186847
|
"CODEX_API_KEY",
|
|
186745
186848
|
"CODEX_HOME",
|
|
186746
186849
|
"CODEX_ACCESS_TOKEN",
|
|
186850
|
+
"OPENROUTER_API_KEY",
|
|
186747
186851
|
"ANTHROPIC_API_KEY",
|
|
186748
186852
|
"CLAUDE_CODE_OAUTH_TOKEN"
|
|
186749
186853
|
],
|
|
@@ -186760,6 +186864,7 @@ var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
|
186760
186864
|
"CODEX_API_KEY",
|
|
186761
186865
|
"CODEX_HOME",
|
|
186762
186866
|
"CODEX_ACCESS_TOKEN",
|
|
186867
|
+
"OPENROUTER_API_KEY",
|
|
186763
186868
|
"ANTHROPIC_API_KEY",
|
|
186764
186869
|
"CLAUDE_CODE_OAUTH_TOKEN"
|
|
186765
186870
|
],
|
|
@@ -186806,6 +186911,7 @@ var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
|
186806
186911
|
OPENAI_API_KEY: true,
|
|
186807
186912
|
CODEX_API_KEY: true,
|
|
186808
186913
|
CODEX_ACCESS_TOKEN: true,
|
|
186914
|
+
OPENROUTER_API_KEY: true,
|
|
186809
186915
|
ANTHROPIC_API_KEY: true,
|
|
186810
186916
|
CLAUDE_CODE_OAUTH_TOKEN: true
|
|
186811
186917
|
},
|
|
@@ -186958,7 +187064,7 @@ function parseJson(value) {
|
|
|
186958
187064
|
}
|
|
186959
187065
|
}
|
|
186960
187066
|
function parsePluginList(value) {
|
|
186961
|
-
if (!
|
|
187067
|
+
if (!isRecord6(value))
|
|
186962
187068
|
return;
|
|
186963
187069
|
const allowedKeys = new Set(PLUGIN_LIST_JSON_SCHEMA.collections);
|
|
186964
187070
|
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
@@ -186986,7 +187092,7 @@ function parsePluginEntries(value) {
|
|
|
186986
187092
|
return;
|
|
186987
187093
|
const entries = [];
|
|
186988
187094
|
for (const item of value) {
|
|
186989
|
-
if (!
|
|
187095
|
+
if (!isRecord6(item))
|
|
186990
187096
|
return;
|
|
186991
187097
|
if (typeof item.pluginId !== "string" || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean") {
|
|
186992
187098
|
return;
|
|
@@ -187012,7 +187118,7 @@ function parseMcpList(value) {
|
|
|
187012
187118
|
return;
|
|
187013
187119
|
const matches = [];
|
|
187014
187120
|
for (const item of value) {
|
|
187015
|
-
if (!
|
|
187121
|
+
if (!isRecord6(item) || typeof item.name !== "string")
|
|
187016
187122
|
return "unknown";
|
|
187017
187123
|
if (item.name !== "kyoso")
|
|
187018
187124
|
continue;
|
|
@@ -187094,7 +187200,7 @@ function comparePrerelease(left, right) {
|
|
|
187094
187200
|
}
|
|
187095
187201
|
return 0;
|
|
187096
187202
|
}
|
|
187097
|
-
function
|
|
187203
|
+
function isRecord6(value) {
|
|
187098
187204
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
187099
187205
|
}
|
|
187100
187206
|
|
|
@@ -187200,7 +187306,7 @@ function findKyosoPackage(executable) {
|
|
|
187200
187306
|
if (existsSync(packagePath)) {
|
|
187201
187307
|
try {
|
|
187202
187308
|
const parsed = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
187203
|
-
if (
|
|
187309
|
+
if (isRecord7(parsed) && parsed.name === "@kyo-so/cli" && typeof parsed.version === "string" && parsed.version.trim().length > 0) {
|
|
187204
187310
|
return { directory, version: parsed.version };
|
|
187205
187311
|
}
|
|
187206
187312
|
} catch {}
|
|
@@ -187286,7 +187392,7 @@ function isWithin(path, parent) {
|
|
|
187286
187392
|
const relativePath = relative(resolve5(parent), resolve5(path));
|
|
187287
187393
|
return relativePath === "" || !relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && relativePath !== ".." && !isAbsolute4(relativePath);
|
|
187288
187394
|
}
|
|
187289
|
-
function
|
|
187395
|
+
function isRecord7(value) {
|
|
187290
187396
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
187291
187397
|
}
|
|
187292
187398
|
|
|
@@ -187324,7 +187430,7 @@ import {
|
|
|
187324
187430
|
} from "node:path";
|
|
187325
187431
|
|
|
187326
187432
|
// src/cli/knownSkillDigests.ts
|
|
187327
|
-
var CURRENT_SKILL_DIGEST = "sha256:
|
|
187433
|
+
var CURRENT_SKILL_DIGEST = "sha256:570f83f716734f34db00147f1b98bc8cd4e9c0016d3946b352ecef4a5d6b8734";
|
|
187328
187434
|
var KNOWN_SKILL_DIGESTS_BY_VERSION = {
|
|
187329
187435
|
"0.8.0": [
|
|
187330
187436
|
{
|
|
@@ -188073,7 +188179,7 @@ async function ensureClaudeMcp(context) {
|
|
|
188073
188179
|
const configPath = join6(context.cwd, ".mcp.json");
|
|
188074
188180
|
const current = await readJsonObject(configPath);
|
|
188075
188181
|
const mcpServers = recordValue(current.mcpServers);
|
|
188076
|
-
if (
|
|
188182
|
+
if (isRecord8(mcpServers.kyoso)) {
|
|
188077
188183
|
return {
|
|
188078
188184
|
kind: "mcp",
|
|
188079
188185
|
registration: "preserved",
|
|
@@ -188313,7 +188419,7 @@ async function readJsonObject(path) {
|
|
|
188313
188419
|
if (content.trim().length === 0)
|
|
188314
188420
|
return {};
|
|
188315
188421
|
const parsed = JSON.parse(content);
|
|
188316
|
-
if (!
|
|
188422
|
+
if (!isRecord8(parsed))
|
|
188317
188423
|
throw new Error(`${path} must contain a JSON object`);
|
|
188318
188424
|
return parsed;
|
|
188319
188425
|
}
|
|
@@ -188323,9 +188429,9 @@ function hasCodexMcpContent(content) {
|
|
|
188323
188429
|
function codexMcpStatusFromContent(content) {
|
|
188324
188430
|
try {
|
|
188325
188431
|
const parsed = parse5(content);
|
|
188326
|
-
if (!
|
|
188432
|
+
if (!isRecord8(parsed))
|
|
188327
188433
|
return "unknown";
|
|
188328
|
-
if (!
|
|
188434
|
+
if (!isRecord8(parsed.mcp_servers))
|
|
188329
188435
|
return "missing";
|
|
188330
188436
|
if (!("kyoso" in parsed.mcp_servers))
|
|
188331
188437
|
return "missing";
|
|
@@ -188354,11 +188460,11 @@ function detectCodexMcp(path, cwd, home) {
|
|
|
188354
188460
|
if (hasUnprobedProjectIntegrationOverride(parsed, cwd, home)) {
|
|
188355
188461
|
return { status: "unknown", paths: [path] };
|
|
188356
188462
|
}
|
|
188357
|
-
if (!
|
|
188463
|
+
if (!isRecord8(parsed))
|
|
188358
188464
|
return { status: "unknown", paths: [path] };
|
|
188359
188465
|
if (!("mcp_servers" in parsed))
|
|
188360
188466
|
return { status: "missing", paths: [] };
|
|
188361
|
-
if (!
|
|
188467
|
+
if (!isRecord8(parsed.mcp_servers)) {
|
|
188362
188468
|
return { status: "unknown", paths: [path] };
|
|
188363
188469
|
}
|
|
188364
188470
|
if (!("kyoso" in parsed.mcp_servers)) {
|
|
@@ -188383,18 +188489,18 @@ function detectClaudeMcp(path, cwd, home) {
|
|
|
188383
188489
|
}
|
|
188384
188490
|
}
|
|
188385
188491
|
function jsonMcpStatuses(value, cwd, home) {
|
|
188386
|
-
if (!
|
|
188492
|
+
if (!isRecord8(value))
|
|
188387
188493
|
return ["unknown"];
|
|
188388
188494
|
const statuses = directMcpStatuses(value);
|
|
188389
188495
|
if (!("projects" in value))
|
|
188390
188496
|
return statuses;
|
|
188391
|
-
if (!
|
|
188497
|
+
if (!isRecord8(value.projects))
|
|
188392
188498
|
return [...statuses, "unknown"];
|
|
188393
188499
|
const currentProject = normalizeProjectPath(cwd, home);
|
|
188394
188500
|
for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
|
|
188395
188501
|
if (normalizeProjectPath(projectPath, home) !== currentProject)
|
|
188396
188502
|
continue;
|
|
188397
|
-
if (!
|
|
188503
|
+
if (!isRecord8(projectConfig)) {
|
|
188398
188504
|
statuses.push("unknown");
|
|
188399
188505
|
continue;
|
|
188400
188506
|
}
|
|
@@ -188405,7 +188511,7 @@ function jsonMcpStatuses(value, cwd, home) {
|
|
|
188405
188511
|
function directMcpStatuses(value) {
|
|
188406
188512
|
const statuses = [];
|
|
188407
188513
|
if ("mcpServers" in value) {
|
|
188408
|
-
if (!
|
|
188514
|
+
if (!isRecord8(value.mcpServers)) {
|
|
188409
188515
|
statuses.push("unknown");
|
|
188410
188516
|
} else if ("kyoso" in value.mcpServers) {
|
|
188411
188517
|
statuses.push(mcpEntryStatus(value.mcpServers.kyoso));
|
|
@@ -188416,7 +188522,7 @@ function directMcpStatuses(value) {
|
|
|
188416
188522
|
function nestedMcpEntryStatus(value, path) {
|
|
188417
188523
|
let current = value;
|
|
188418
188524
|
for (const key of path) {
|
|
188419
|
-
if (!
|
|
188525
|
+
if (!isRecord8(current))
|
|
188420
188526
|
return "unknown";
|
|
188421
188527
|
if (!(key in current))
|
|
188422
188528
|
return "missing";
|
|
@@ -188425,11 +188531,11 @@ function nestedMcpEntryStatus(value, path) {
|
|
|
188425
188531
|
return mcpEntryStatus(current);
|
|
188426
188532
|
}
|
|
188427
188533
|
function hasUnprobedProjectIntegrationOverride(value, cwd, home) {
|
|
188428
|
-
if (!
|
|
188534
|
+
if (!isRecord8(value) || !isRecord8(value.projects))
|
|
188429
188535
|
return false;
|
|
188430
188536
|
const currentProject = normalizeProjectPath(cwd, home);
|
|
188431
188537
|
for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
|
|
188432
|
-
if (normalizeProjectPath(projectPath, home) !== currentProject || !
|
|
188538
|
+
if (normalizeProjectPath(projectPath, home) !== currentProject || !isRecord8(projectConfig)) {
|
|
188433
188539
|
continue;
|
|
188434
188540
|
}
|
|
188435
188541
|
if ("mcp_servers" in projectConfig || "plugins" in projectConfig) {
|
|
@@ -188452,7 +188558,7 @@ function normalizeProjectPath(path, home) {
|
|
|
188452
188558
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
188453
188559
|
}
|
|
188454
188560
|
function mcpEntryStatus(value) {
|
|
188455
|
-
if (!
|
|
188561
|
+
if (!isRecord8(value))
|
|
188456
188562
|
return "unknown";
|
|
188457
188563
|
if (!("enabled" in value))
|
|
188458
188564
|
return "enabled";
|
|
@@ -188486,7 +188592,7 @@ function readTextSync(path) {
|
|
|
188486
188592
|
function recordValue(value) {
|
|
188487
188593
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
188488
188594
|
}
|
|
188489
|
-
function
|
|
188595
|
+
function isRecord8(value) {
|
|
188490
188596
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
188491
188597
|
}
|
|
188492
188598
|
function diffForAppend(path, snippet) {
|
|
@@ -191486,14 +191592,14 @@ var zGuardCreateElicitationResponseCancel = object({
|
|
|
191486
191592
|
});
|
|
191487
191593
|
// node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
|
|
191488
191594
|
var CANCEL_REQUEST_METHOD = "$/cancel_request";
|
|
191489
|
-
function
|
|
191595
|
+
function isRecord9(value) {
|
|
191490
191596
|
return typeof value === "object" && value !== null;
|
|
191491
191597
|
}
|
|
191492
191598
|
function isJsonRpcId(value) {
|
|
191493
191599
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
191494
191600
|
}
|
|
191495
191601
|
function cancelRequestId(params) {
|
|
191496
|
-
if (!
|
|
191602
|
+
if (!isRecord9(params) || !isJsonRpcId(params["requestId"])) {
|
|
191497
191603
|
return;
|
|
191498
191604
|
}
|
|
191499
191605
|
return params["requestId"];
|
|
@@ -191840,7 +191946,7 @@ class Connection {
|
|
|
191840
191946
|
if (this.abortController.signal.aborted) {
|
|
191841
191947
|
return;
|
|
191842
191948
|
}
|
|
191843
|
-
if (!
|
|
191949
|
+
if (!isRecord9(message)) {
|
|
191844
191950
|
console.error("Invalid message", { message });
|
|
191845
191951
|
return;
|
|
191846
191952
|
}
|
|
@@ -191933,7 +192039,7 @@ class Connection {
|
|
|
191933
192039
|
pendingResponse.cleanup?.();
|
|
191934
192040
|
if ("result" in response) {
|
|
191935
192041
|
pendingResponse.resolve(response.result);
|
|
191936
|
-
} else if ("error" in response &&
|
|
192042
|
+
} else if ("error" in response && isRecord9(response.error)) {
|
|
191937
192043
|
const { code, message, data } = response.error;
|
|
191938
192044
|
pendingResponse.reject(new RequestError(code, message, data));
|
|
191939
192045
|
} else {
|
|
@@ -192143,7 +192249,7 @@ function ndJsonStream(output2, input2) {
|
|
|
192143
192249
|
if (trimmedLine) {
|
|
192144
192250
|
try {
|
|
192145
192251
|
const message = JSON.parse(trimmedLine);
|
|
192146
|
-
if (
|
|
192252
|
+
if (isRecord9(message)) {
|
|
192147
192253
|
controller.enqueue(message);
|
|
192148
192254
|
} else {
|
|
192149
192255
|
console.warn("Skipping JSON line that is not an object:", trimmedLine);
|
|
@@ -193141,7 +193247,7 @@ function isSeverity(value) {
|
|
|
193141
193247
|
return typeof value === "string" && severities.includes(value);
|
|
193142
193248
|
}
|
|
193143
193249
|
function normalizeCisaSecureByDesign(value) {
|
|
193144
|
-
if (!
|
|
193250
|
+
if (!isRecord10(value))
|
|
193145
193251
|
return;
|
|
193146
193252
|
const normalized = {};
|
|
193147
193253
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -193182,7 +193288,7 @@ function normalizeFindingFiles(value) {
|
|
|
193182
193288
|
if (!Array.isArray(value))
|
|
193183
193289
|
return;
|
|
193184
193290
|
const files = value.flatMap((item) => {
|
|
193185
|
-
if (!
|
|
193291
|
+
if (!isRecord10(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
193186
193292
|
return [];
|
|
193187
193293
|
}
|
|
193188
193294
|
const file2 = {
|
|
@@ -193201,7 +193307,7 @@ function normalizeFindingFiles(value) {
|
|
|
193201
193307
|
function normalizeLineNumber(value) {
|
|
193202
193308
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
193203
193309
|
}
|
|
193204
|
-
function
|
|
193310
|
+
function isRecord10(value) {
|
|
193205
193311
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193206
193312
|
}
|
|
193207
193313
|
|
|
@@ -193261,6 +193367,20 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
193261
193367
|
}
|
|
193262
193368
|
async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
193263
193369
|
const startedAt = new Date().toISOString();
|
|
193370
|
+
const effectiveTimeoutMs = resolveEffectiveTimeoutMs(input2);
|
|
193371
|
+
if (effectiveTimeoutMs <= 0) {
|
|
193372
|
+
return {
|
|
193373
|
+
agent,
|
|
193374
|
+
role: input2.role,
|
|
193375
|
+
status: "timeout",
|
|
193376
|
+
startedAt,
|
|
193377
|
+
completedAt: startedAt,
|
|
193378
|
+
error: {
|
|
193379
|
+
code: "REVIEW_DEADLINE_EXCEEDED",
|
|
193380
|
+
message: "Review deadline was reached before the agent could start."
|
|
193381
|
+
}
|
|
193382
|
+
};
|
|
193383
|
+
}
|
|
193264
193384
|
return new Promise((resolveResult) => {
|
|
193265
193385
|
const child = spawn(agentConfig.command, agentConfig.args, {
|
|
193266
193386
|
cwd: input2.workspaceDir,
|
|
@@ -193289,6 +193409,7 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193289
193409
|
const timeout = setTimeout(() => {
|
|
193290
193410
|
abortController.abort(new Error("Kyoso agent timeout"));
|
|
193291
193411
|
terminateChild(child);
|
|
193412
|
+
const deadlineReached = input2.deadlineAtEpochMs !== undefined && Date.now() >= input2.deadlineAtEpochMs;
|
|
193292
193413
|
resolveOnce({
|
|
193293
193414
|
agent,
|
|
193294
193415
|
role: input2.role,
|
|
@@ -193296,11 +193417,11 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193296
193417
|
startedAt,
|
|
193297
193418
|
completedAt: new Date().toISOString(),
|
|
193298
193419
|
error: {
|
|
193299
|
-
code: "AGENT_TIMEOUT",
|
|
193300
|
-
message: `Agent timed out after ${
|
|
193420
|
+
code: deadlineReached ? "REVIEW_DEADLINE_EXCEEDED" : "AGENT_TIMEOUT",
|
|
193421
|
+
message: deadlineReached ? "Review deadline reached before the agent completed." : `Agent timed out after ${effectiveTimeoutMs}ms`
|
|
193301
193422
|
}
|
|
193302
193423
|
});
|
|
193303
|
-
},
|
|
193424
|
+
}, effectiveTimeoutMs);
|
|
193304
193425
|
child.stderr.on("data", (chunk) => {
|
|
193305
193426
|
stderr3 += chunk.toString("utf8");
|
|
193306
193427
|
});
|
|
@@ -193316,19 +193437,48 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193316
193437
|
error: failure
|
|
193317
193438
|
});
|
|
193318
193439
|
});
|
|
193319
|
-
runAcpClientWorkflow(child, input2, abortController
|
|
193440
|
+
runAcpClientWorkflow(child, input2, abortController, resolveEffortConfigOption(agent, agentConfig.effort)).then(({ rawText, warnings, usage, outputBytes, stopReason }) => {
|
|
193320
193441
|
stdout = rawText;
|
|
193442
|
+
const completed = stopReason === "end_turn";
|
|
193321
193443
|
resolveOnce({
|
|
193322
193444
|
agent,
|
|
193323
193445
|
role: input2.role,
|
|
193324
|
-
status: "completed",
|
|
193446
|
+
status: completed ? "completed" : "failed",
|
|
193325
193447
|
rawText,
|
|
193326
193448
|
normalized: normalizeAgentOutput(agent, input2.role, rawText),
|
|
193327
193449
|
startedAt,
|
|
193328
193450
|
completedAt: new Date().toISOString(),
|
|
193329
|
-
|
|
193451
|
+
outputBytes,
|
|
193452
|
+
stopReason,
|
|
193453
|
+
...usage ? { usage } : {},
|
|
193454
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
193455
|
+
...completed ? {} : {
|
|
193456
|
+
error: {
|
|
193457
|
+
code: "AGENT_STOPPED_EARLY",
|
|
193458
|
+
message: `Agent stopped before completing the review: ${stopReason}.`
|
|
193459
|
+
}
|
|
193460
|
+
}
|
|
193330
193461
|
});
|
|
193331
193462
|
}).catch((error51) => {
|
|
193463
|
+
const outputLimitError = findOutputLimitError(error51, abortController);
|
|
193464
|
+
if (outputLimitError) {
|
|
193465
|
+
stdout = outputLimitError.rawText;
|
|
193466
|
+
resolveOnce({
|
|
193467
|
+
agent,
|
|
193468
|
+
role: input2.role,
|
|
193469
|
+
status: "failed",
|
|
193470
|
+
rawText: stdout,
|
|
193471
|
+
outputBytes: outputLimitError.outputBytes,
|
|
193472
|
+
stopReason: "cancelled",
|
|
193473
|
+
startedAt,
|
|
193474
|
+
completedAt: new Date().toISOString(),
|
|
193475
|
+
error: {
|
|
193476
|
+
code: "AGENT_OUTPUT_LIMIT",
|
|
193477
|
+
message: `Agent output exceeded ${outputLimitError.maxOutputBytes} bytes and was cancelled.`
|
|
193478
|
+
}
|
|
193479
|
+
});
|
|
193480
|
+
return;
|
|
193481
|
+
}
|
|
193332
193482
|
if (abortController.signal.aborted)
|
|
193333
193483
|
return;
|
|
193334
193484
|
const failureText = [stderr3, formatAgentErrorDetail(error51)].filter((part) => part.trim().length > 0).join(`
|
|
@@ -193361,7 +193511,7 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193361
193511
|
});
|
|
193362
193512
|
});
|
|
193363
193513
|
}
|
|
193364
|
-
async function runAcpClientWorkflow(child, input2,
|
|
193514
|
+
async function runAcpClientWorkflow(child, input2, abortController, configOption) {
|
|
193365
193515
|
if (!child.stdin || !child.stdout) {
|
|
193366
193516
|
throw new Error("Agent process did not expose stdio streams.");
|
|
193367
193517
|
}
|
|
@@ -193411,8 +193561,8 @@ async function runAcpClientWorkflow(child, input2, signal, configOption) {
|
|
|
193411
193561
|
}).withSession(async (session) => {
|
|
193412
193562
|
const warnings = [];
|
|
193413
193563
|
if (configOption) {
|
|
193414
|
-
await ctx.request(methods.agent.session.setConfigOption, { sessionId: session.sessionId, ...configOption }, { cancellationSignal: signal }).catch((error51) => {
|
|
193415
|
-
if (signal.aborted)
|
|
193564
|
+
await ctx.request(methods.agent.session.setConfigOption, { sessionId: session.sessionId, ...configOption }, { cancellationSignal: abortController.signal }).catch((error51) => {
|
|
193565
|
+
if (abortController.signal.aborted)
|
|
193416
193566
|
return;
|
|
193417
193567
|
const sanitizedValue = sanitizeTextForDisplay(configOption.value);
|
|
193418
193568
|
const detail = sanitizeTextForDisplay(formatAgentErrorDetail(error51));
|
|
@@ -193422,14 +193572,75 @@ async function runAcpClientWorkflow(child, input2, signal, configOption) {
|
|
|
193422
193572
|
});
|
|
193423
193573
|
}
|
|
193424
193574
|
const promptResponse = session.prompt(input2.prompt, {
|
|
193425
|
-
cancellationSignal: signal
|
|
193575
|
+
cancellationSignal: abortController.signal
|
|
193426
193576
|
});
|
|
193427
|
-
|
|
193428
|
-
|
|
193429
|
-
|
|
193577
|
+
promptResponse.catch(() => {
|
|
193578
|
+
return;
|
|
193579
|
+
});
|
|
193580
|
+
let rawText = "";
|
|
193581
|
+
let outputBytes = 0;
|
|
193582
|
+
for (;; ) {
|
|
193583
|
+
const message = await session.nextUpdate();
|
|
193584
|
+
if (message.kind === "stop") {
|
|
193585
|
+
const usage = normalizeUsage3(message.response.usage);
|
|
193586
|
+
return {
|
|
193587
|
+
rawText,
|
|
193588
|
+
warnings,
|
|
193589
|
+
...usage ? { usage } : {},
|
|
193590
|
+
outputBytes,
|
|
193591
|
+
stopReason: message.stopReason
|
|
193592
|
+
};
|
|
193593
|
+
}
|
|
193594
|
+
const update = message.update;
|
|
193595
|
+
if (update.sessionUpdate !== "agent_message_chunk" && update.sessionUpdate !== "agent_thought_chunk" || update.content.type !== "text") {
|
|
193596
|
+
continue;
|
|
193597
|
+
}
|
|
193598
|
+
const chunkBytes = Buffer.byteLength(update.content.text, "utf8");
|
|
193599
|
+
const nextOutputBytes = outputBytes + chunkBytes;
|
|
193600
|
+
if (input2.maxOutputBytes !== undefined && nextOutputBytes > input2.maxOutputBytes) {
|
|
193601
|
+
await ctx.notify(methods.agent.session.cancel, {
|
|
193602
|
+
sessionId: session.sessionId
|
|
193603
|
+
}).catch(() => {
|
|
193604
|
+
return;
|
|
193605
|
+
});
|
|
193606
|
+
const error51 = new AgentOutputLimitError(rawText, nextOutputBytes, input2.maxOutputBytes);
|
|
193607
|
+
abortController.abort(error51);
|
|
193608
|
+
throw error51;
|
|
193609
|
+
}
|
|
193610
|
+
if (update.sessionUpdate === "agent_message_chunk") {
|
|
193611
|
+
rawText += update.content.text;
|
|
193612
|
+
}
|
|
193613
|
+
outputBytes = nextOutputBytes;
|
|
193614
|
+
}
|
|
193430
193615
|
});
|
|
193431
193616
|
});
|
|
193432
193617
|
}
|
|
193618
|
+
|
|
193619
|
+
class AgentOutputLimitError extends Error {
|
|
193620
|
+
rawText;
|
|
193621
|
+
outputBytes;
|
|
193622
|
+
maxOutputBytes;
|
|
193623
|
+
constructor(rawText, outputBytes, maxOutputBytes) {
|
|
193624
|
+
super(`Agent output exceeded ${maxOutputBytes} bytes.`);
|
|
193625
|
+
this.rawText = rawText;
|
|
193626
|
+
this.outputBytes = outputBytes;
|
|
193627
|
+
this.maxOutputBytes = maxOutputBytes;
|
|
193628
|
+
this.name = "AgentOutputLimitError";
|
|
193629
|
+
}
|
|
193630
|
+
}
|
|
193631
|
+
function findOutputLimitError(error51, abortController) {
|
|
193632
|
+
if (error51 instanceof AgentOutputLimitError)
|
|
193633
|
+
return error51;
|
|
193634
|
+
const reason = abortController.signal.reason;
|
|
193635
|
+
return reason instanceof AgentOutputLimitError ? reason : undefined;
|
|
193636
|
+
}
|
|
193637
|
+
function resolveEffectiveTimeoutMs(input2) {
|
|
193638
|
+
const deadlineRemaining = input2.deadlineAtEpochMs === undefined ? Number.POSITIVE_INFINITY : input2.deadlineAtEpochMs - Date.now();
|
|
193639
|
+
return Math.max(0, Math.min(input2.timeoutMs, deadlineRemaining));
|
|
193640
|
+
}
|
|
193641
|
+
function normalizeUsage3(usage) {
|
|
193642
|
+
return normalizeModelTokenUsage(usage);
|
|
193643
|
+
}
|
|
193433
193644
|
function resolveEffortConfigOption(agent, effort) {
|
|
193434
193645
|
if (!effort)
|
|
193435
193646
|
return;
|
|
@@ -207299,7 +207510,7 @@ function clearInheritedOpenRouterModelForProviderReset(baseConfig, overridden, o
|
|
|
207299
207510
|
return;
|
|
207300
207511
|
}
|
|
207301
207512
|
const codex = readPath2(overridden, ["agents", "codex"]);
|
|
207302
|
-
if (
|
|
207513
|
+
if (isRecord11(codex))
|
|
207303
207514
|
delete codex.model;
|
|
207304
207515
|
}
|
|
207305
207516
|
function findAssignmentForPath(overrides, path) {
|
|
@@ -207344,7 +207555,7 @@ function parseConfigOverrideValue(value, currentValue) {
|
|
|
207344
207555
|
function readPath2(target, path) {
|
|
207345
207556
|
let current = target;
|
|
207346
207557
|
for (const key of path) {
|
|
207347
|
-
if (!
|
|
207558
|
+
if (!isRecord11(current))
|
|
207348
207559
|
return;
|
|
207349
207560
|
current = current[key];
|
|
207350
207561
|
}
|
|
@@ -207354,7 +207565,7 @@ function writePath2(target, path, value) {
|
|
|
207354
207565
|
let current = target;
|
|
207355
207566
|
for (const key of path.slice(0, -1)) {
|
|
207356
207567
|
const child = current[key];
|
|
207357
|
-
if (!
|
|
207568
|
+
if (!isRecord11(child)) {
|
|
207358
207569
|
throw new Error(`Cannot apply --set key ${JSON.stringify(path.join("."))}.`);
|
|
207359
207570
|
}
|
|
207360
207571
|
current = child;
|
|
@@ -207363,7 +207574,7 @@ function writePath2(target, path, value) {
|
|
|
207363
207574
|
if (leaf)
|
|
207364
207575
|
current[leaf] = value;
|
|
207365
207576
|
}
|
|
207366
|
-
function
|
|
207577
|
+
function isRecord11(value) {
|
|
207367
207578
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
207368
207579
|
}
|
|
207369
207580
|
|
|
@@ -207444,9 +207655,10 @@ ${JSON.stringify(opinion)}
|
|
|
207444
207655
|
role: input2.role,
|
|
207445
207656
|
status: "completed",
|
|
207446
207657
|
rawText,
|
|
207447
|
-
normalized: scenario === "success" ? opinion : undefined,
|
|
207658
|
+
normalized: scenario === "success" || scenario === "unknown_usage" ? opinion : undefined,
|
|
207448
207659
|
startedAt,
|
|
207449
|
-
completedAt: new Date().toISOString()
|
|
207660
|
+
completedAt: new Date().toISOString(),
|
|
207661
|
+
...scenario === "unknown_usage" ? {} : { usage: fakeUsage() }
|
|
207450
207662
|
};
|
|
207451
207663
|
}
|
|
207452
207664
|
}
|
|
@@ -207480,9 +207692,13 @@ function verifierResult(input2, startedAt, scenario) {
|
|
|
207480
207692
|
status: "completed",
|
|
207481
207693
|
rawText,
|
|
207482
207694
|
startedAt,
|
|
207483
|
-
completedAt: new Date().toISOString()
|
|
207695
|
+
completedAt: new Date().toISOString(),
|
|
207696
|
+
usage: fakeUsage()
|
|
207484
207697
|
};
|
|
207485
207698
|
}
|
|
207699
|
+
function fakeUsage() {
|
|
207700
|
+
return { totalTokens: 20, inputTokens: 12, outputTokens: 8 };
|
|
207701
|
+
}
|
|
207486
207702
|
function findingIdsFromPrompt(prompt) {
|
|
207487
207703
|
return Array.from(prompt.matchAll(/^Finding ID: (.+)$/gm)).map((match) => match[1] ?? "");
|
|
207488
207704
|
}
|
|
@@ -208095,6 +208311,16 @@ async function optionalLstat3(path) {
|
|
|
208095
208311
|
}
|
|
208096
208312
|
|
|
208097
208313
|
// src/audit/sanitize.ts
|
|
208314
|
+
var USAGE_METADATA_KEYS = new Set([
|
|
208315
|
+
"tokenUsage",
|
|
208316
|
+
"totalTokens",
|
|
208317
|
+
"inputTokens",
|
|
208318
|
+
"outputTokens",
|
|
208319
|
+
"thoughtTokens",
|
|
208320
|
+
"cachedReadTokens",
|
|
208321
|
+
"cachedWriteTokens",
|
|
208322
|
+
"skipOptionalPhasesWhenTokenUsageUnknown"
|
|
208323
|
+
]);
|
|
208098
208324
|
function sanitizeForAudit(value, options = {}) {
|
|
208099
208325
|
if (typeof value === "string")
|
|
208100
208326
|
return sanitizeText(value);
|
|
@@ -208103,7 +208329,7 @@ function sanitizeForAudit(value, options = {}) {
|
|
|
208103
208329
|
if (typeof value === "object" && value !== null) {
|
|
208104
208330
|
const result = {};
|
|
208105
208331
|
for (const [key, nested] of Object.entries(value)) {
|
|
208106
|
-
if (/raw|content|env|credential|token|secret|password/i.test(key) && !(options.includeRawAgentOutput && key === "rawText")) {
|
|
208332
|
+
if (/raw|content|env|credential|token|secret|password/i.test(key) && !(options.includeRawAgentOutput && key === "rawText") && !isUsageMetadata(key, nested)) {
|
|
208107
208333
|
continue;
|
|
208108
208334
|
}
|
|
208109
208335
|
result[key] = sanitizeForAudit(nested, options);
|
|
@@ -208112,6 +208338,17 @@ function sanitizeForAudit(value, options = {}) {
|
|
|
208112
208338
|
}
|
|
208113
208339
|
return value;
|
|
208114
208340
|
}
|
|
208341
|
+
function isUsageMetadata(key, value) {
|
|
208342
|
+
if (!USAGE_METADATA_KEYS.has(key))
|
|
208343
|
+
return false;
|
|
208344
|
+
if (key === "tokenUsage") {
|
|
208345
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
208346
|
+
}
|
|
208347
|
+
if (key === "skipOptionalPhasesWhenTokenUsageUnknown") {
|
|
208348
|
+
return typeof value === "boolean";
|
|
208349
|
+
}
|
|
208350
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
208351
|
+
}
|
|
208115
208352
|
|
|
208116
208353
|
// src/audit/trace.ts
|
|
208117
208354
|
var AUDIT_WARNING_WRITE_FAILED = "AUDIT_WRITE_FAILED: Audit trace writing failed; no further audit events will be written.";
|
|
@@ -208377,6 +208614,8 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
208377
208614
|
"",
|
|
208378
208615
|
`**Decision:** ${result.decision}`,
|
|
208379
208616
|
`**Mode:** ${tool}`,
|
|
208617
|
+
`**Completion:** ${formatCompletion(result)}`,
|
|
208618
|
+
`**Request fingerprint:** ${shortFingerprint(result.requestFingerprint)}`,
|
|
208380
208619
|
`**Agents:** ${result.agentOpinions.map((opinion) => `${title(opinion.agent)} ${opinion.status}`).join(", ")}`,
|
|
208381
208620
|
`**Review mode:** ${formatReviewMode(result)}`,
|
|
208382
208621
|
...result.verificationMode ? [
|
|
@@ -208388,6 +208627,7 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
208388
208627
|
"",
|
|
208389
208628
|
options.summaryText ?? defaultSummaryText(result)
|
|
208390
208629
|
];
|
|
208630
|
+
lines.push(...formatExecutionBudget(result));
|
|
208391
208631
|
if (result.cisaSecureByDesign) {
|
|
208392
208632
|
lines.push("", "## CISA Secure by Design Gate", "", "| Dimension | Status | Notes |", "|---|---|---|", `| Customer Security Outcomes | ${result.cisaSecureByDesign.customerSecurityOutcomes} | ${notes(result.cisaSecureByDesign.notes)} |`, `| Secure by Default | ${result.cisaSecureByDesign.secureByDefault} | ${notes(result.cisaSecureByDesign.notes)} |`, `| Transparency & Accountability | ${result.cisaSecureByDesign.transparencyAndAccountability} | ${notes(result.cisaSecureByDesign.notes)} |`, `| Governance | ${result.cisaSecureByDesign.governance} | ${notes(result.cisaSecureByDesign.notes)} |`);
|
|
208393
208633
|
}
|
|
@@ -208440,8 +208680,37 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
208440
208680
|
`);
|
|
208441
208681
|
}
|
|
208442
208682
|
function defaultSummaryText(result) {
|
|
208683
|
+
if (result.completion.status === "incomplete") {
|
|
208684
|
+
const reasons = result.completion.reasons.length > 0 ? result.completion.reasons.join(", ") : "unspecified coverage gap";
|
|
208685
|
+
return `Review incomplete (${reasons}). Decision is block because review coverage is incomplete, not because a code finding was established.`;
|
|
208686
|
+
}
|
|
208443
208687
|
return result.findings.length === 0 ? "No blocking findings were detected from the supplied context." : `${result.findings.length} finding(s) require attention.`;
|
|
208444
208688
|
}
|
|
208689
|
+
function formatExecutionBudget(result) {
|
|
208690
|
+
const budget = result.executionBudget;
|
|
208691
|
+
const agentOutputs = Object.entries(budget.agentOutputBytes);
|
|
208692
|
+
const outputLines = agentOutputs.length > 0 ? agentOutputs.map(([agent, bytes]) => `- ${title(agent)}: ${bytes} bytes`) : ["- None reported."];
|
|
208693
|
+
const totalTokens = budget.tokenUsage.totals.totalTokens;
|
|
208694
|
+
return [
|
|
208695
|
+
"",
|
|
208696
|
+
"## Execution Budget",
|
|
208697
|
+
"",
|
|
208698
|
+
`- Model calls: ${budget.modelCalls.planned} planned / ${budget.modelCalls.consumed} consumed / ${budget.modelCalls.skipped} skipped`,
|
|
208699
|
+
`- Wall time: ${budget.wallTime.consumedMs}ms consumed / ${budget.wallTime.limitMs}ms limit`,
|
|
208700
|
+
`- Token usage: ${budget.tokenUsage.status} (${budget.tokenUsage.reportedCalls} reported, ${budget.tokenUsage.unknownCalls} unknown${totalTokens === undefined ? "" : `, ${totalTokens} total`})`,
|
|
208701
|
+
"- Agent output:",
|
|
208702
|
+
...outputLines
|
|
208703
|
+
];
|
|
208704
|
+
}
|
|
208705
|
+
function formatCompletion(result) {
|
|
208706
|
+
if (result.completion.status === "complete")
|
|
208707
|
+
return "complete";
|
|
208708
|
+
const reasons = result.completion.reasons.join(", ") || "unspecified";
|
|
208709
|
+
return `incomplete (${reasons}; retryable=${String(result.completion.retryable)})`;
|
|
208710
|
+
}
|
|
208711
|
+
function shortFingerprint(value) {
|
|
208712
|
+
return value.length <= 20 ? value : `${value.slice(0, 20)}…`;
|
|
208713
|
+
}
|
|
208445
208714
|
function title(value) {
|
|
208446
208715
|
return value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
208447
208716
|
}
|
|
@@ -208741,6 +209010,304 @@ function newTraceId() {
|
|
|
208741
209010
|
return `tr_${randomUUID2()}`;
|
|
208742
209011
|
}
|
|
208743
209012
|
|
|
209013
|
+
// src/core/requestFingerprint.ts
|
|
209014
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
209015
|
+
var REVIEW_CONTRACT_VERSION = "2026-07-15-v1";
|
|
209016
|
+
function createRequestFingerprint(input2) {
|
|
209017
|
+
const reviewers = ["codex", "claude"].filter((agent) => input2.config.agents[agent].enabled).map((agent) => ({
|
|
209018
|
+
agent,
|
|
209019
|
+
role: input2.roles[agent] ?? input2.config.agents[agent].role,
|
|
209020
|
+
model: input2.config.agents[agent].model ?? null,
|
|
209021
|
+
provider: agent === "codex" ? input2.config.agents.codex.provider ?? "default" : "default"
|
|
209022
|
+
}));
|
|
209023
|
+
const request = structuredClone(input2.request);
|
|
209024
|
+
if (request.options)
|
|
209025
|
+
delete request.options.includeAgentRawOutputs;
|
|
209026
|
+
const payload = {
|
|
209027
|
+
reviewContractVersion: REVIEW_CONTRACT_VERSION,
|
|
209028
|
+
tool: input2.tool,
|
|
209029
|
+
request,
|
|
209030
|
+
reviewers,
|
|
209031
|
+
verification: input2.config.verification,
|
|
209032
|
+
judge: {
|
|
209033
|
+
...input2.config.judge,
|
|
209034
|
+
requestedProvider: input2.request.options?.judgeProvider ?? null
|
|
209035
|
+
},
|
|
209036
|
+
executionBudget: input2.budget
|
|
209037
|
+
};
|
|
209038
|
+
return `sha256:${createHash4("sha256").update(canonicalJson(payload), "utf8").digest("hex")}`;
|
|
209039
|
+
}
|
|
209040
|
+
function canonicalJson(value) {
|
|
209041
|
+
return JSON.stringify(canonicalize(value));
|
|
209042
|
+
}
|
|
209043
|
+
function canonicalize(value) {
|
|
209044
|
+
if (Array.isArray(value))
|
|
209045
|
+
return value.map(canonicalize);
|
|
209046
|
+
if (!isRecord12(value))
|
|
209047
|
+
return value;
|
|
209048
|
+
return Object.fromEntries(Object.entries(value).filter(([, child]) => child !== undefined).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, canonicalize(child)]));
|
|
209049
|
+
}
|
|
209050
|
+
function isRecord12(value) {
|
|
209051
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
209052
|
+
}
|
|
209053
|
+
|
|
209054
|
+
// src/core/reviewBudget.ts
|
|
209055
|
+
var REVIEW_BUDGET_KEYS = new Set([
|
|
209056
|
+
"maxModelCalls",
|
|
209057
|
+
"maxTotalWallTimeMs",
|
|
209058
|
+
"maxAgentOutputBytes",
|
|
209059
|
+
"maxFindingsPerAgent",
|
|
209060
|
+
"skipOptionalPhasesWhenTokenUsageUnknown"
|
|
209061
|
+
]);
|
|
209062
|
+
var MODEL_CALL_KINDS = ["primary", "verifier", "judge"];
|
|
209063
|
+
function resolveReviewBudget(ceiling, requested) {
|
|
209064
|
+
if (requested === undefined)
|
|
209065
|
+
return ceiling;
|
|
209066
|
+
if (!isRecord13(requested)) {
|
|
209067
|
+
throw new KyosoRequestError("options.reviewBudget must be an object.", "REVIEW_BUDGET_INVALID");
|
|
209068
|
+
}
|
|
209069
|
+
for (const [key, value] of Object.entries(requested)) {
|
|
209070
|
+
if (!REVIEW_BUDGET_KEYS.has(key)) {
|
|
209071
|
+
throw new KyosoRequestError(`options.reviewBudget.${key} is not supported.`, "REVIEW_BUDGET_INVALID");
|
|
209072
|
+
}
|
|
209073
|
+
if (key === "skipOptionalPhasesWhenTokenUsageUnknown") {
|
|
209074
|
+
if (typeof value !== "boolean") {
|
|
209075
|
+
throw new KyosoRequestError(`options.reviewBudget.${key} must be a boolean.`, "REVIEW_BUDGET_INVALID");
|
|
209076
|
+
}
|
|
209077
|
+
continue;
|
|
209078
|
+
}
|
|
209079
|
+
if (!isPositiveInteger(value)) {
|
|
209080
|
+
throw new KyosoRequestError(`options.reviewBudget.${key} must be a positive integer.`, "REVIEW_BUDGET_INVALID");
|
|
209081
|
+
}
|
|
209082
|
+
}
|
|
209083
|
+
const numericKeys = [
|
|
209084
|
+
"maxModelCalls",
|
|
209085
|
+
"maxTotalWallTimeMs",
|
|
209086
|
+
"maxAgentOutputBytes",
|
|
209087
|
+
"maxFindingsPerAgent"
|
|
209088
|
+
];
|
|
209089
|
+
for (const key of numericKeys) {
|
|
209090
|
+
const value = requested[key];
|
|
209091
|
+
if (value === undefined)
|
|
209092
|
+
continue;
|
|
209093
|
+
if (value > ceiling[key]) {
|
|
209094
|
+
throw new KyosoRequestError(`options.reviewBudget.${key} cannot exceed the user-global ceiling.`, "REVIEW_BUDGET_EXCEEDS_CEILING");
|
|
209095
|
+
}
|
|
209096
|
+
}
|
|
209097
|
+
if (ceiling.skipOptionalPhasesWhenTokenUsageUnknown && requested.skipOptionalPhasesWhenTokenUsageUnknown === false) {
|
|
209098
|
+
throw new KyosoRequestError("options.reviewBudget.skipOptionalPhasesWhenTokenUsageUnknown cannot relax the user-global ceiling.", "REVIEW_BUDGET_EXCEEDS_CEILING");
|
|
209099
|
+
}
|
|
209100
|
+
return {
|
|
209101
|
+
maxModelCalls: requested.maxModelCalls ?? ceiling.maxModelCalls,
|
|
209102
|
+
maxTotalWallTimeMs: requested.maxTotalWallTimeMs ?? ceiling.maxTotalWallTimeMs,
|
|
209103
|
+
maxAgentOutputBytes: requested.maxAgentOutputBytes ?? ceiling.maxAgentOutputBytes,
|
|
209104
|
+
maxFindingsPerAgent: requested.maxFindingsPerAgent ?? ceiling.maxFindingsPerAgent,
|
|
209105
|
+
skipOptionalPhasesWhenTokenUsageUnknown: ceiling.skipOptionalPhasesWhenTokenUsageUnknown || requested.skipOptionalPhasesWhenTokenUsageUnknown === true
|
|
209106
|
+
};
|
|
209107
|
+
}
|
|
209108
|
+
|
|
209109
|
+
class ReviewBudgetTracker {
|
|
209110
|
+
budget;
|
|
209111
|
+
startedAtEpochMs;
|
|
209112
|
+
deadlineAtEpochMs;
|
|
209113
|
+
reservations = new Map;
|
|
209114
|
+
skippedCalls = [];
|
|
209115
|
+
incompleteReasons = new Set;
|
|
209116
|
+
nextReservationId = 1;
|
|
209117
|
+
constructor(budget, startedAtEpochMs = Date.now()) {
|
|
209118
|
+
this.budget = budget;
|
|
209119
|
+
this.startedAtEpochMs = startedAtEpochMs;
|
|
209120
|
+
this.deadlineAtEpochMs = startedAtEpochMs + budget.maxTotalWallTimeMs;
|
|
209121
|
+
}
|
|
209122
|
+
remainingWallTimeMs(now = Date.now()) {
|
|
209123
|
+
return Math.max(0, this.deadlineAtEpochMs - now);
|
|
209124
|
+
}
|
|
209125
|
+
hasDeadlineExpired(now = Date.now()) {
|
|
209126
|
+
return this.remainingWallTimeMs(now) === 0;
|
|
209127
|
+
}
|
|
209128
|
+
reserveMany(inputs) {
|
|
209129
|
+
if (this.hasDeadlineExpired())
|
|
209130
|
+
return { failure: { reason: "deadline" } };
|
|
209131
|
+
if (this.usedCapacity() + inputs.length > this.budget.maxModelCalls) {
|
|
209132
|
+
return { failure: { reason: "model_call_budget" } };
|
|
209133
|
+
}
|
|
209134
|
+
const reservations = inputs.map((input2) => {
|
|
209135
|
+
const reservation = {
|
|
209136
|
+
id: this.nextReservationId,
|
|
209137
|
+
kind: input2.kind,
|
|
209138
|
+
...input2.agent ? { agent: input2.agent } : {},
|
|
209139
|
+
status: "reserved"
|
|
209140
|
+
};
|
|
209141
|
+
this.reservations.set(reservation.id, reservation);
|
|
209142
|
+
this.nextReservationId += 1;
|
|
209143
|
+
return reservation;
|
|
209144
|
+
});
|
|
209145
|
+
return {
|
|
209146
|
+
reservations: reservations.map(({ id, kind, agent }) => ({
|
|
209147
|
+
id,
|
|
209148
|
+
kind,
|
|
209149
|
+
...agent ? { agent } : {}
|
|
209150
|
+
}))
|
|
209151
|
+
};
|
|
209152
|
+
}
|
|
209153
|
+
reserve(input2) {
|
|
209154
|
+
const result = this.reserveMany([input2]);
|
|
209155
|
+
if ("failure" in result)
|
|
209156
|
+
return result;
|
|
209157
|
+
const reservation = result.reservations[0];
|
|
209158
|
+
if (!reservation) {
|
|
209159
|
+
return { failure: { reason: "model_call_budget" } };
|
|
209160
|
+
}
|
|
209161
|
+
return { reservation };
|
|
209162
|
+
}
|
|
209163
|
+
markStarted(reservation) {
|
|
209164
|
+
const current = this.reservations.get(reservation.id);
|
|
209165
|
+
if (!current || current.status !== "reserved")
|
|
209166
|
+
return;
|
|
209167
|
+
current.status = "started";
|
|
209168
|
+
}
|
|
209169
|
+
hasStarted(reservation) {
|
|
209170
|
+
const current = this.reservations.get(reservation.id);
|
|
209171
|
+
return current?.status === "started" || current?.status === "completed";
|
|
209172
|
+
}
|
|
209173
|
+
complete(reservation, values = {}) {
|
|
209174
|
+
const current = this.reservations.get(reservation.id);
|
|
209175
|
+
if (!current || current.status === "skipped" || current.status === "completed") {
|
|
209176
|
+
return;
|
|
209177
|
+
}
|
|
209178
|
+
current.status = "completed";
|
|
209179
|
+
current.outputBytes = values.outputBytes;
|
|
209180
|
+
current.usage = normalizeModelTokenUsage(values.usage);
|
|
209181
|
+
current.stopReason = values.stopReason;
|
|
209182
|
+
}
|
|
209183
|
+
skip(reservation, reason) {
|
|
209184
|
+
const current = this.reservations.get(reservation.id);
|
|
209185
|
+
if (!current || current.status !== "reserved")
|
|
209186
|
+
return;
|
|
209187
|
+
current.status = "skipped";
|
|
209188
|
+
current.reason = reason;
|
|
209189
|
+
}
|
|
209190
|
+
recordSkipped(input2) {
|
|
209191
|
+
this.skippedCalls.push({
|
|
209192
|
+
kind: input2.kind,
|
|
209193
|
+
...input2.agent ? { agent: input2.agent } : {},
|
|
209194
|
+
status: "skipped",
|
|
209195
|
+
reason: input2.reason
|
|
209196
|
+
});
|
|
209197
|
+
}
|
|
209198
|
+
markIncomplete(reason) {
|
|
209199
|
+
this.incompleteReasons.add(reason);
|
|
209200
|
+
}
|
|
209201
|
+
isTokenUsageUnknown() {
|
|
209202
|
+
return Array.from(this.reservations.values()).some((reservation) => reservation.status === "completed" && reservation.usage === undefined);
|
|
209203
|
+
}
|
|
209204
|
+
snapshot(now = Date.now()) {
|
|
209205
|
+
const calls = this.modelCalls();
|
|
209206
|
+
const byKind = Object.fromEntries(MODEL_CALL_KINDS.map((kind) => [
|
|
209207
|
+
kind,
|
|
209208
|
+
{ planned: 0, consumed: 0, skipped: 0 }
|
|
209209
|
+
]));
|
|
209210
|
+
let planned = 0;
|
|
209211
|
+
let consumed = 0;
|
|
209212
|
+
let skipped = 0;
|
|
209213
|
+
const agentOutputBytes = {};
|
|
209214
|
+
const usageTotals = {};
|
|
209215
|
+
let reportedCalls = 0;
|
|
209216
|
+
let unknownCalls = 0;
|
|
209217
|
+
for (const reservation of this.reservations.values()) {
|
|
209218
|
+
planned += 1;
|
|
209219
|
+
byKind[reservation.kind].planned += 1;
|
|
209220
|
+
if (reservation.status === "completed") {
|
|
209221
|
+
consumed += 1;
|
|
209222
|
+
byKind[reservation.kind].consumed += 1;
|
|
209223
|
+
if (reservation.agent && reservation.outputBytes !== undefined) {
|
|
209224
|
+
agentOutputBytes[reservation.agent] = (agentOutputBytes[reservation.agent] ?? 0) + reservation.outputBytes;
|
|
209225
|
+
}
|
|
209226
|
+
if (reservation.usage) {
|
|
209227
|
+
reportedCalls += 1;
|
|
209228
|
+
addUsage(usageTotals, reservation.usage);
|
|
209229
|
+
} else {
|
|
209230
|
+
unknownCalls += 1;
|
|
209231
|
+
}
|
|
209232
|
+
}
|
|
209233
|
+
if (reservation.status === "skipped") {
|
|
209234
|
+
skipped += 1;
|
|
209235
|
+
byKind[reservation.kind].skipped += 1;
|
|
209236
|
+
}
|
|
209237
|
+
}
|
|
209238
|
+
for (const call of this.skippedCalls) {
|
|
209239
|
+
skipped += 1;
|
|
209240
|
+
byKind[call.kind].skipped += 1;
|
|
209241
|
+
}
|
|
209242
|
+
const tokenStatus = consumed === 0 || reportedCalls === 0 ? "unknown" : unknownCalls === 0 ? "reported" : "partial";
|
|
209243
|
+
const completionReasons = Array.from(this.incompleteReasons).sort();
|
|
209244
|
+
const consumedMs = Math.max(0, now - this.startedAtEpochMs);
|
|
209245
|
+
return {
|
|
209246
|
+
completion: {
|
|
209247
|
+
status: completionReasons.length > 0 ? "incomplete" : "complete",
|
|
209248
|
+
reasons: completionReasons,
|
|
209249
|
+
retryable: false
|
|
209250
|
+
},
|
|
209251
|
+
executionBudget: {
|
|
209252
|
+
maxModelCalls: this.budget.maxModelCalls,
|
|
209253
|
+
modelCalls: { planned, consumed, skipped, byKind },
|
|
209254
|
+
wallTime: {
|
|
209255
|
+
limitMs: this.budget.maxTotalWallTimeMs,
|
|
209256
|
+
consumedMs,
|
|
209257
|
+
remainingMs: this.remainingWallTimeMs(now)
|
|
209258
|
+
},
|
|
209259
|
+
maxAgentOutputBytes: this.budget.maxAgentOutputBytes,
|
|
209260
|
+
maxFindingsPerAgent: this.budget.maxFindingsPerAgent,
|
|
209261
|
+
skipOptionalPhasesWhenTokenUsageUnknown: this.budget.skipOptionalPhasesWhenTokenUsageUnknown,
|
|
209262
|
+
agentOutputBytes,
|
|
209263
|
+
tokenUsage: {
|
|
209264
|
+
status: tokenStatus,
|
|
209265
|
+
reportedCalls,
|
|
209266
|
+
unknownCalls,
|
|
209267
|
+
totals: usageTotals
|
|
209268
|
+
}
|
|
209269
|
+
},
|
|
209270
|
+
modelCalls: calls
|
|
209271
|
+
};
|
|
209272
|
+
}
|
|
209273
|
+
usedCapacity() {
|
|
209274
|
+
return Array.from(this.reservations.values()).filter((reservation) => reservation.status === "reserved" || reservation.status === "started" || reservation.status === "completed").length;
|
|
209275
|
+
}
|
|
209276
|
+
modelCalls() {
|
|
209277
|
+
const reservations = Array.from(this.reservations.values()).filter((reservation) => reservation.status === "completed" || reservation.status === "skipped").map((reservation) => ({
|
|
209278
|
+
kind: reservation.kind,
|
|
209279
|
+
...reservation.agent ? { agent: reservation.agent } : {},
|
|
209280
|
+
status: reservation.status === "completed" ? "completed" : "skipped",
|
|
209281
|
+
...reservation.reason ? { reason: reservation.reason } : {},
|
|
209282
|
+
...reservation.outputBytes !== undefined ? { outputBytes: reservation.outputBytes } : {},
|
|
209283
|
+
...reservation.usage ? { usage: reservation.usage } : {},
|
|
209284
|
+
...reservation.stopReason ? { stopReason: reservation.stopReason } : {}
|
|
209285
|
+
}));
|
|
209286
|
+
return [...reservations, ...this.skippedCalls];
|
|
209287
|
+
}
|
|
209288
|
+
}
|
|
209289
|
+
function addUsage(total, usage) {
|
|
209290
|
+
for (const key of [
|
|
209291
|
+
"totalTokens",
|
|
209292
|
+
"inputTokens",
|
|
209293
|
+
"outputTokens",
|
|
209294
|
+
"thoughtTokens",
|
|
209295
|
+
"cachedReadTokens",
|
|
209296
|
+
"cachedWriteTokens"
|
|
209297
|
+
]) {
|
|
209298
|
+
const value = usage[key];
|
|
209299
|
+
if (value === undefined)
|
|
209300
|
+
continue;
|
|
209301
|
+
total[key] = (total[key] ?? 0) + value;
|
|
209302
|
+
}
|
|
209303
|
+
}
|
|
209304
|
+
function isPositiveInteger(value) {
|
|
209305
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
209306
|
+
}
|
|
209307
|
+
function isRecord13(value) {
|
|
209308
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
209309
|
+
}
|
|
209310
|
+
|
|
208744
209311
|
// src/core/verification.ts
|
|
208745
209312
|
var REAL_AGENTS = ["codex", "claude"];
|
|
208746
209313
|
var VERIFIABLE_SEVERITIES = new Set(["critical", "high"]);
|
|
@@ -208782,9 +209349,12 @@ function groupVerificationTargetsByVerifier(targets) {
|
|
|
208782
209349
|
findings
|
|
208783
209350
|
}));
|
|
208784
209351
|
}
|
|
208785
|
-
function markVerificationOverflow(targets) {
|
|
209352
|
+
function markVerificationOverflow(targets, reason) {
|
|
208786
209353
|
for (const target of targets) {
|
|
208787
|
-
target.finding.verification = {
|
|
209354
|
+
target.finding.verification = {
|
|
209355
|
+
status: "not_verified",
|
|
209356
|
+
...reason ? { note: reason } : {}
|
|
209357
|
+
};
|
|
208788
209358
|
}
|
|
208789
209359
|
}
|
|
208790
209360
|
function parseVerificationVerdicts(rawText) {
|
|
@@ -208796,7 +209366,7 @@ function parseVerificationVerdicts(rawText) {
|
|
|
208796
209366
|
if (!Array.isArray(parsed.verdicts))
|
|
208797
209367
|
return;
|
|
208798
209368
|
return parsed.verdicts.flatMap((item) => {
|
|
208799
|
-
if (!
|
|
209369
|
+
if (!isRecord14(item))
|
|
208800
209370
|
return [];
|
|
208801
209371
|
if (typeof item.findingId !== "string")
|
|
208802
209372
|
return [];
|
|
@@ -208874,15 +209444,23 @@ function verificationNote(reasoning) {
|
|
|
208874
209444
|
function isVerdict(value) {
|
|
208875
209445
|
return value === "confirmed" || value === "refuted" || value === "uncertain";
|
|
208876
209446
|
}
|
|
208877
|
-
function
|
|
209447
|
+
function isRecord14(value) {
|
|
208878
209448
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
208879
209449
|
}
|
|
208880
209450
|
|
|
208881
209451
|
// src/core/runReview.ts
|
|
209452
|
+
function requestForRecursionFingerprint(request) {
|
|
209453
|
+
try {
|
|
209454
|
+
return scanAndRedactSecrets(request).redactedRequest;
|
|
209455
|
+
} catch {
|
|
209456
|
+
return { goal: "" };
|
|
209457
|
+
}
|
|
209458
|
+
}
|
|
208882
209459
|
async function runReview(tool, request, options = {}) {
|
|
208883
209460
|
const cwd = options.cwd ?? process.cwd();
|
|
208884
209461
|
const traceId = newTraceId();
|
|
208885
|
-
const
|
|
209462
|
+
const startedAtEpochMs = Date.now();
|
|
209463
|
+
const startedAt = new Date(startedAtEpochMs).toISOString();
|
|
208886
209464
|
const auditEnv = { ...process.env, ...options.env };
|
|
208887
209465
|
const traceWriterFactory = options.traceWriterFactory ?? createTraceWriter;
|
|
208888
209466
|
let snapshot;
|
|
@@ -208891,6 +209469,14 @@ async function runReview(tool, request, options = {}) {
|
|
|
208891
209469
|
} catch (error51) {
|
|
208892
209470
|
if (error51 instanceof KyosoRequestError) {
|
|
208893
209471
|
const config2 = kyosoConfigSchema.parse(defaultConfig);
|
|
209472
|
+
const budgetTracker = new ReviewBudgetTracker(config2.reviewBudget, startedAtEpochMs);
|
|
209473
|
+
const requestFingerprint = createRequestFingerprint({
|
|
209474
|
+
tool,
|
|
209475
|
+
request: requestForRecursionFingerprint(request),
|
|
209476
|
+
config: config2,
|
|
209477
|
+
roles: resolveAgentRoles(config2),
|
|
209478
|
+
budget: config2.reviewBudget
|
|
209479
|
+
});
|
|
208894
209480
|
const trace2 = traceWriterFactory({
|
|
208895
209481
|
enabled: config2.audit.enabled,
|
|
208896
209482
|
directory: config2.audit.directory,
|
|
@@ -208905,6 +209491,12 @@ async function runReview(tool, request, options = {}) {
|
|
|
208905
209491
|
tool,
|
|
208906
209492
|
timestamp: new Date().toISOString()
|
|
208907
209493
|
});
|
|
209494
|
+
await writeReviewBudgetPlanned({
|
|
209495
|
+
trace: trace2,
|
|
209496
|
+
traceId,
|
|
209497
|
+
budgetTracker,
|
|
209498
|
+
requestFingerprint
|
|
209499
|
+
});
|
|
208908
209500
|
return await buildPolicyBlockResult({
|
|
208909
209501
|
tool,
|
|
208910
209502
|
trace: trace2,
|
|
@@ -208912,6 +209504,8 @@ async function runReview(tool, request, options = {}) {
|
|
|
208912
209504
|
startedAt,
|
|
208913
209505
|
networkMode: config2.network.defaultMode,
|
|
208914
209506
|
warning: error51.message,
|
|
209507
|
+
budgetTracker,
|
|
209508
|
+
requestFingerprint,
|
|
208915
209509
|
finding: {
|
|
208916
209510
|
id: "KYOSO-1",
|
|
208917
209511
|
severity: "critical",
|
|
@@ -208977,6 +209571,8 @@ async function runReview(tool, request, options = {}) {
|
|
|
208977
209571
|
timestamp: new Date().toISOString()
|
|
208978
209572
|
});
|
|
208979
209573
|
validateReviewRequest(tool, request);
|
|
209574
|
+
const reviewBudget = resolveReviewBudget(loaded.config.reviewBudget, request.options?.reviewBudget);
|
|
209575
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs);
|
|
208980
209576
|
assertTrustedWorkspaceRoot(request.workspace?.root, loaded.config.workspace.root, cwd);
|
|
208981
209577
|
const networkMode = resolveNetworkMode(request.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
|
|
208982
209578
|
if (networkMode === "unrestricted" && !loaded.config.network.allowUnrestricted) {
|
|
@@ -208995,6 +209591,19 @@ async function runReview(tool, request, options = {}) {
|
|
|
208995
209591
|
});
|
|
208996
209592
|
const allowSecretOverride = loaded.config.secrets.allowOverride && request.options?.allowSecretRedaction === true;
|
|
208997
209593
|
if (secretScan.detected && loaded.config.secrets.blockOnDetectedSecret && !allowSecretOverride) {
|
|
209594
|
+
const requestFingerprint2 = createRequestFingerprint({
|
|
209595
|
+
tool,
|
|
209596
|
+
request: secretScan.redactedRequest,
|
|
209597
|
+
config: loaded.config,
|
|
209598
|
+
roles: resolveAgentRoles(loaded.config),
|
|
209599
|
+
budget: reviewBudget
|
|
209600
|
+
});
|
|
209601
|
+
await writeReviewBudgetPlanned({
|
|
209602
|
+
trace,
|
|
209603
|
+
traceId,
|
|
209604
|
+
budgetTracker,
|
|
209605
|
+
requestFingerprint: requestFingerprint2
|
|
209606
|
+
});
|
|
208998
209607
|
return await buildSecretBlockResult({
|
|
208999
209608
|
tool,
|
|
209000
209609
|
trace,
|
|
@@ -209003,7 +209612,9 @@ async function runReview(tool, request, options = {}) {
|
|
|
209003
209612
|
configHash: loaded.configHash,
|
|
209004
209613
|
networkMode,
|
|
209005
209614
|
secretScan,
|
|
209006
|
-
warnings
|
|
209615
|
+
warnings,
|
|
209616
|
+
budgetTracker,
|
|
209617
|
+
requestFingerprint: requestFingerprint2
|
|
209007
209618
|
});
|
|
209008
209619
|
}
|
|
209009
209620
|
const denyPatterns = mergeDenyPatterns(loaded.config.workspace.deny, secretScan.redactedRequest.workspace?.denyRead);
|
|
@@ -209016,6 +209627,19 @@ async function runReview(tool, request, options = {}) {
|
|
|
209016
209627
|
});
|
|
209017
209628
|
warnings.push(...built.warnings);
|
|
209018
209629
|
const agentRoles = resolveAgentRoles(loaded.config);
|
|
209630
|
+
const requestFingerprint = createRequestFingerprint({
|
|
209631
|
+
tool,
|
|
209632
|
+
request: built.request,
|
|
209633
|
+
config: loaded.config,
|
|
209634
|
+
roles: agentRoles,
|
|
209635
|
+
budget: reviewBudget
|
|
209636
|
+
});
|
|
209637
|
+
await writeReviewBudgetPlanned({
|
|
209638
|
+
trace,
|
|
209639
|
+
traceId,
|
|
209640
|
+
budgetTracker,
|
|
209641
|
+
requestFingerprint
|
|
209642
|
+
});
|
|
209019
209643
|
snapshot = await createSnapshot(traceId, tool, built.request, {
|
|
209020
209644
|
denyPatterns,
|
|
209021
209645
|
allowPatterns,
|
|
@@ -209038,14 +209662,22 @@ async function runReview(tool, request, options = {}) {
|
|
|
209038
209662
|
networkMode,
|
|
209039
209663
|
manager,
|
|
209040
209664
|
trace,
|
|
209041
|
-
warnings
|
|
209665
|
+
warnings,
|
|
209666
|
+
budgetTracker
|
|
209042
209667
|
});
|
|
209043
209668
|
warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
|
|
209044
|
-
const
|
|
209045
|
-
const
|
|
209046
|
-
const
|
|
209669
|
+
const normalized = agentResults.map((result) => normalizeAgentRunResult(result, reviewBudget.maxFindingsPerAgent));
|
|
209670
|
+
const normalizedAgentResults = normalized.map((item) => item.result);
|
|
209671
|
+
for (const item of normalized.filter((item2) => item2.findingsCapped)) {
|
|
209672
|
+
budgetTracker.markIncomplete("coverage_incomplete");
|
|
209673
|
+
warnings.push(`Agent ${item.result.agent} findings were capped at ${reviewBudget.maxFindingsPerAgent}; review coverage is incomplete.`);
|
|
209674
|
+
}
|
|
209675
|
+
const enabledAgents = ["codex", "claude"].filter((agent) => loaded.config.agents[agent].enabled);
|
|
209676
|
+
const agentsUsed = normalizedAgentResults.filter((result) => result.status !== "skipped").map((result) => result.agent);
|
|
209677
|
+
const reviewMode = enabledAgents.length === 1 ? "single_agent" : "multi_agent";
|
|
209047
209678
|
const completed = normalizedAgentResults.filter((result) => result.status === "completed");
|
|
209048
|
-
const
|
|
209679
|
+
const attempted = normalizedAgentResults.filter((result) => result.status !== "skipped");
|
|
209680
|
+
const degraded = completed.length !== attempted.length || enabledAgents.length === 0;
|
|
209049
209681
|
let aggregate = aggregateAgentResults(normalizedAgentResults, {
|
|
209050
209682
|
reviewMode
|
|
209051
209683
|
});
|
|
@@ -209061,7 +209693,12 @@ async function runReview(tool, request, options = {}) {
|
|
|
209061
209693
|
])
|
|
209062
209694
|
};
|
|
209063
209695
|
}
|
|
209064
|
-
if (completed.length === 0) {
|
|
209696
|
+
if (completed.length === 0 && (attempted.length > 0 || enabledAgents.length === 0)) {
|
|
209697
|
+
const noPrimaryAgents = enabledAgents.length === 0;
|
|
209698
|
+
if (noPrimaryAgents) {
|
|
209699
|
+
budgetTracker.markIncomplete("coverage_incomplete");
|
|
209700
|
+
warnings.push("No primary review agents are enabled; review coverage is incomplete.");
|
|
209701
|
+
}
|
|
209065
209702
|
aggregate = {
|
|
209066
209703
|
...aggregate,
|
|
209067
209704
|
findings: [
|
|
@@ -209070,9 +209707,9 @@ async function runReview(tool, request, options = {}) {
|
|
|
209070
209707
|
id: `KYOSO-${aggregate.findings.length + 1}`,
|
|
209071
209708
|
severity: "critical",
|
|
209072
209709
|
category: "other",
|
|
209073
|
-
title: "All backend agents failed",
|
|
209074
|
-
evidence: normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
209075
|
-
recommendation: "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
209710
|
+
title: noPrimaryAgents ? "No primary review agents enabled" : "All backend agents failed",
|
|
209711
|
+
evidence: noPrimaryAgents ? "Both configured primary reviewers are disabled." : normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
209712
|
+
recommendation: noPrimaryAgents ? "Enable at least one primary reviewer before running Kyoso." : "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
209076
209713
|
sourceAgents: ["kyoso_policy"],
|
|
209077
209714
|
confidence: "high"
|
|
209078
209715
|
}
|
|
@@ -209085,7 +209722,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
209085
209722
|
findingCount: aggregate.findings.length,
|
|
209086
209723
|
timestamp: new Date().toISOString()
|
|
209087
209724
|
});
|
|
209088
|
-
const verificationMode = loaded.config.verification.enabled && reviewMode === "single_agent" ? "skipped_single_agent" : loaded.config.verification.enabled ? "cross_agent" : undefined;
|
|
209725
|
+
const verificationMode = loaded.config.verification.enabled && reviewMode === "single_agent" ? "skipped_single_agent" : loaded.config.verification.enabled && enabledAgents.length > 1 ? "cross_agent" : undefined;
|
|
209089
209726
|
if (verificationMode === "cross_agent") {
|
|
209090
209727
|
warnings.push(...await runFindingVerification({
|
|
209091
209728
|
tool,
|
|
@@ -209096,11 +209733,16 @@ async function runReview(tool, request, options = {}) {
|
|
|
209096
209733
|
networkMode,
|
|
209097
209734
|
manager,
|
|
209098
209735
|
trace,
|
|
209099
|
-
findings: aggregate.findings
|
|
209736
|
+
findings: aggregate.findings,
|
|
209737
|
+
budgetTracker
|
|
209100
209738
|
}));
|
|
209101
209739
|
}
|
|
209740
|
+
if (aggregate.findings.some((finding) => finding.verification?.status === "refuted")) {
|
|
209741
|
+
budgetTracker.markIncomplete("disputed_finding");
|
|
209742
|
+
}
|
|
209102
209743
|
const cisa = tool === "security_review" ? computeCisaGate(aggregate.findings, normalizedAgentResults) : undefined;
|
|
209103
|
-
const
|
|
209744
|
+
const budgetBeforeJudge = budgetTracker.snapshot();
|
|
209745
|
+
const decision = budgetBeforeJudge.completion.status === "incomplete" ? "block" : decide({
|
|
209104
209746
|
tool,
|
|
209105
209747
|
findings: aggregate.findings,
|
|
209106
209748
|
cisa,
|
|
@@ -209110,6 +209752,9 @@ async function runReview(tool, request, options = {}) {
|
|
|
209110
209752
|
const completedAt = new Date().toISOString();
|
|
209111
209753
|
const resultWithoutMarkdown = {
|
|
209112
209754
|
decision,
|
|
209755
|
+
completion: budgetBeforeJudge.completion,
|
|
209756
|
+
executionBudget: budgetBeforeJudge.executionBudget,
|
|
209757
|
+
requestFingerprint,
|
|
209113
209758
|
degraded,
|
|
209114
209759
|
agentsUsed,
|
|
209115
209760
|
reviewMode,
|
|
@@ -209131,18 +209776,22 @@ async function runReview(tool, request, options = {}) {
|
|
|
209131
209776
|
networkMode,
|
|
209132
209777
|
workspaceMode: "temp_snapshot",
|
|
209133
209778
|
configHash: loaded.configHash,
|
|
209134
|
-
warnings: Array.from(new Set([...warnings, ...trace.warnings]))
|
|
209779
|
+
warnings: Array.from(new Set([...warnings, ...trace.warnings])),
|
|
209780
|
+
modelCalls: budgetBeforeJudge.modelCalls
|
|
209135
209781
|
}
|
|
209136
209782
|
};
|
|
209137
209783
|
const summaryText = defaultSummaryText(resultWithoutMarkdown);
|
|
209138
|
-
const judge = await
|
|
209784
|
+
const judge = await runBudgetedJudge({
|
|
209139
209785
|
tool,
|
|
209140
209786
|
result: resultWithoutMarkdown,
|
|
209141
209787
|
summaryText,
|
|
209142
209788
|
agentFindings: buildJudgeAgentFindings(normalizedAgentResults),
|
|
209143
209789
|
config: loaded.config.judge,
|
|
209144
209790
|
requestedProvider: request.options?.judgeProvider,
|
|
209145
|
-
env: options.env ?? process.env
|
|
209791
|
+
env: options.env ?? process.env,
|
|
209792
|
+
budgetTracker,
|
|
209793
|
+
trace,
|
|
209794
|
+
traceId
|
|
209146
209795
|
});
|
|
209147
209796
|
const judgeComments = new Map(judge.output.disagreementComments.map((comment) => [
|
|
209148
209797
|
comment.topic,
|
|
@@ -209153,10 +209802,20 @@ async function runReview(tool, request, options = {}) {
|
|
|
209153
209802
|
judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
|
|
209154
209803
|
}));
|
|
209155
209804
|
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
209805
|
+
const budgetAfterJudge = budgetTracker.snapshot();
|
|
209806
|
+
const finalDecision = budgetAfterJudge.completion.status === "incomplete" ? "block" : decision;
|
|
209156
209807
|
const resultAfterJudge = {
|
|
209157
209808
|
...resultWithoutMarkdown,
|
|
209809
|
+
decision: finalDecision,
|
|
209810
|
+
completion: budgetAfterJudge.completion,
|
|
209811
|
+
executionBudget: budgetAfterJudge.executionBudget,
|
|
209158
209812
|
disagreements,
|
|
209159
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
209813
|
+
...crossModelAnalysis ? { crossModelAnalysis } : {},
|
|
209814
|
+
audit: {
|
|
209815
|
+
...resultWithoutMarkdown.audit,
|
|
209816
|
+
completedAt: new Date().toISOString(),
|
|
209817
|
+
modelCalls: budgetAfterJudge.modelCalls
|
|
209818
|
+
}
|
|
209160
209819
|
};
|
|
209161
209820
|
const judgeEvent = {
|
|
209162
209821
|
type: "judge_completed",
|
|
@@ -209169,10 +209828,16 @@ async function runReview(tool, request, options = {}) {
|
|
|
209169
209828
|
judgeEvent.error = judge.error;
|
|
209170
209829
|
await trace.write(judgeEvent);
|
|
209171
209830
|
resultAfterJudge.audit.completedAt = new Date().toISOString();
|
|
209831
|
+
await writeReviewBudgetCompleted({
|
|
209832
|
+
trace,
|
|
209833
|
+
traceId,
|
|
209834
|
+
budgetTracker,
|
|
209835
|
+
requestFingerprint
|
|
209836
|
+
});
|
|
209172
209837
|
await trace.write({
|
|
209173
209838
|
type: "decision_completed",
|
|
209174
209839
|
traceId,
|
|
209175
|
-
decision,
|
|
209840
|
+
decision: finalDecision,
|
|
209176
209841
|
timestamp: new Date().toISOString()
|
|
209177
209842
|
});
|
|
209178
209843
|
await trace.write({
|
|
@@ -209184,7 +209849,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
209184
209849
|
tool,
|
|
209185
209850
|
trace,
|
|
209186
209851
|
result: resultAfterJudge,
|
|
209187
|
-
summaryText: judge.output.summaryText
|
|
209852
|
+
summaryText: resultAfterJudge.completion.status === "incomplete" ? defaultSummaryText(resultAfterJudge) : judge.output.summaryText
|
|
209188
209853
|
});
|
|
209189
209854
|
} finally {
|
|
209190
209855
|
await trace.finalize();
|
|
@@ -209195,37 +209860,180 @@ async function runReview(tool, request, options = {}) {
|
|
|
209195
209860
|
async function runFindingVerification(input2) {
|
|
209196
209861
|
const allowDemotionRequested = input2.config.verification.allowDemotion;
|
|
209197
209862
|
const selection = selectVerificationTargets(input2.findings, input2.config.verification.maxFindings);
|
|
209198
|
-
markVerificationOverflow(selection.overflow);
|
|
209199
|
-
if (selection.selected.length === 0)
|
|
209200
|
-
return [];
|
|
209201
209863
|
const warnings = [];
|
|
209202
|
-
|
|
209864
|
+
if (selection.overflow.length > 0) {
|
|
209865
|
+
markVerificationOverflow(selection.overflow, "verification_max_findings");
|
|
209866
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
209867
|
+
}
|
|
209868
|
+
if (selection.selected.length === 0)
|
|
209869
|
+
return warnings;
|
|
209870
|
+
const potentialGroups = groupVerificationTargetsByVerifier(selection.selected);
|
|
209203
209871
|
await input2.trace.write({
|
|
209204
209872
|
type: "verification_started",
|
|
209205
209873
|
traceId: input2.traceId,
|
|
209206
209874
|
targetCount: selection.selected.length,
|
|
209207
209875
|
notVerifiedCount: selection.overflow.length,
|
|
209208
|
-
verifierCount:
|
|
209876
|
+
verifierCount: potentialGroups.length,
|
|
209209
209877
|
timeoutMs: input2.config.verification.timeoutMs,
|
|
209210
209878
|
allowDemotionRequested,
|
|
209211
209879
|
timestamp: new Date().toISOString()
|
|
209212
209880
|
});
|
|
209213
|
-
|
|
209881
|
+
if (input2.budgetTracker.budget.skipOptionalPhasesWhenTokenUsageUnknown && input2.budgetTracker.isTokenUsageUnknown()) {
|
|
209882
|
+
markVerificationOverflow(selection.selected, "token_usage_unknown");
|
|
209883
|
+
input2.budgetTracker.markIncomplete("token_usage_unknown");
|
|
209884
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
209885
|
+
warnings.push("Finding verification was skipped because primary-agent token usage was not reported.");
|
|
209886
|
+
for (const group of potentialGroups) {
|
|
209887
|
+
input2.budgetTracker.recordSkipped({
|
|
209888
|
+
kind: "verifier",
|
|
209889
|
+
agent: group.verifier,
|
|
209890
|
+
reason: "token_usage_unknown"
|
|
209891
|
+
});
|
|
209892
|
+
await input2.trace.write({
|
|
209893
|
+
type: "model_call_skipped",
|
|
209894
|
+
traceId: input2.traceId,
|
|
209895
|
+
kind: "verifier",
|
|
209896
|
+
agent: group.verifier,
|
|
209897
|
+
reason: "token_usage_unknown",
|
|
209898
|
+
timestamp: new Date().toISOString()
|
|
209899
|
+
});
|
|
209900
|
+
}
|
|
209901
|
+
await input2.trace.write({
|
|
209902
|
+
type: "verification_completed",
|
|
209903
|
+
traceId: input2.traceId,
|
|
209904
|
+
counts: countVerificationStatuses(input2.findings),
|
|
209905
|
+
timestamp: new Date().toISOString()
|
|
209906
|
+
});
|
|
209907
|
+
return warnings;
|
|
209908
|
+
}
|
|
209909
|
+
const groups = new Map;
|
|
209910
|
+
const unavailableVerifiers = new Map;
|
|
209911
|
+
for (const target of selection.selected) {
|
|
209912
|
+
const existing = groups.get(target.verifier);
|
|
209913
|
+
if (existing) {
|
|
209914
|
+
existing.targets.push(target);
|
|
209915
|
+
continue;
|
|
209916
|
+
}
|
|
209917
|
+
const unavailable = unavailableVerifiers.get(target.verifier);
|
|
209918
|
+
if (unavailable) {
|
|
209919
|
+
markVerificationOverflow([target], unavailable === "model_call_budget" ? "budget_exhausted" : "deadline");
|
|
209920
|
+
continue;
|
|
209921
|
+
}
|
|
209922
|
+
const reservationResult = input2.budgetTracker.reserve({
|
|
209923
|
+
kind: "verifier",
|
|
209924
|
+
agent: target.verifier
|
|
209925
|
+
});
|
|
209926
|
+
if ("failure" in reservationResult) {
|
|
209927
|
+
unavailableVerifiers.set(target.verifier, reservationResult.failure.reason);
|
|
209928
|
+
markVerificationOverflow([target], reservationResult.failure.reason === "model_call_budget" ? "budget_exhausted" : "deadline");
|
|
209929
|
+
input2.budgetTracker.recordSkipped({
|
|
209930
|
+
kind: "verifier",
|
|
209931
|
+
agent: target.verifier,
|
|
209932
|
+
reason: reservationResult.failure.reason
|
|
209933
|
+
});
|
|
209934
|
+
input2.budgetTracker.markIncomplete(reservationResult.failure.reason);
|
|
209935
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
209936
|
+
await input2.trace.write({
|
|
209937
|
+
type: "review_budget_exhausted",
|
|
209938
|
+
traceId: input2.traceId,
|
|
209939
|
+
phase: "verification",
|
|
209940
|
+
kind: "verifier",
|
|
209941
|
+
agent: target.verifier,
|
|
209942
|
+
reason: reservationResult.failure.reason,
|
|
209943
|
+
timestamp: new Date().toISOString()
|
|
209944
|
+
});
|
|
209945
|
+
await input2.trace.write({
|
|
209946
|
+
type: "model_call_skipped",
|
|
209947
|
+
traceId: input2.traceId,
|
|
209948
|
+
kind: "verifier",
|
|
209949
|
+
agent: target.verifier,
|
|
209950
|
+
reason: reservationResult.failure.reason,
|
|
209951
|
+
timestamp: new Date().toISOString()
|
|
209952
|
+
});
|
|
209953
|
+
continue;
|
|
209954
|
+
}
|
|
209955
|
+
const group = {
|
|
209956
|
+
verifier: target.verifier,
|
|
209957
|
+
targets: [target],
|
|
209958
|
+
reservation: reservationResult.reservation
|
|
209959
|
+
};
|
|
209960
|
+
groups.set(target.verifier, group);
|
|
209961
|
+
await input2.trace.write({
|
|
209962
|
+
type: "model_call_reserved",
|
|
209963
|
+
traceId: input2.traceId,
|
|
209964
|
+
kind: "verifier",
|
|
209965
|
+
agent: target.verifier,
|
|
209966
|
+
timestamp: new Date().toISOString()
|
|
209967
|
+
});
|
|
209968
|
+
}
|
|
209969
|
+
const scheduledGroups = [];
|
|
209970
|
+
for (const group of groups.values()) {
|
|
209971
|
+
const timeoutMs = Math.min(input2.config.verification.timeoutMs, input2.budgetTracker.remainingWallTimeMs());
|
|
209972
|
+
if (timeoutMs > 0) {
|
|
209973
|
+
scheduledGroups.push(group);
|
|
209974
|
+
continue;
|
|
209975
|
+
}
|
|
209976
|
+
input2.budgetTracker.skip(group.reservation, "deadline");
|
|
209977
|
+
input2.budgetTracker.markIncomplete("deadline");
|
|
209978
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
209979
|
+
markVerificationOverflow(group.targets, "deadline");
|
|
209980
|
+
await input2.trace.write({
|
|
209981
|
+
type: "review_budget_exhausted",
|
|
209982
|
+
traceId: input2.traceId,
|
|
209983
|
+
phase: "verification",
|
|
209984
|
+
kind: "verifier",
|
|
209985
|
+
agent: group.verifier,
|
|
209986
|
+
reason: "deadline",
|
|
209987
|
+
timestamp: new Date().toISOString()
|
|
209988
|
+
});
|
|
209989
|
+
await input2.trace.write({
|
|
209990
|
+
type: "model_call_skipped",
|
|
209991
|
+
traceId: input2.traceId,
|
|
209992
|
+
kind: "verifier",
|
|
209993
|
+
agent: group.verifier,
|
|
209994
|
+
reason: "deadline",
|
|
209995
|
+
timestamp: new Date().toISOString()
|
|
209996
|
+
});
|
|
209997
|
+
}
|
|
209998
|
+
if (scheduledGroups.length === 0) {
|
|
209999
|
+
await input2.trace.write({
|
|
210000
|
+
type: "verification_completed",
|
|
210001
|
+
traceId: input2.traceId,
|
|
210002
|
+
counts: countVerificationStatuses(input2.findings),
|
|
210003
|
+
timestamp: new Date().toISOString()
|
|
210004
|
+
});
|
|
210005
|
+
return warnings;
|
|
210006
|
+
}
|
|
210007
|
+
const agentInputs = scheduledGroups.map((group) => ({
|
|
209214
210008
|
traceId: input2.traceId,
|
|
209215
|
-
agent: verifier,
|
|
210009
|
+
agent: group.verifier,
|
|
209216
210010
|
role: "finding_verifier",
|
|
209217
210011
|
tool: input2.tool,
|
|
209218
|
-
prompt: buildFindingVerifierPrompt(input2.tool, input2.request, verifier,
|
|
210012
|
+
prompt: buildFindingVerifierPrompt(input2.tool, input2.request, group.verifier, group.targets.map((target) => target.finding)),
|
|
209219
210013
|
workspaceDir: input2.workspaceDir,
|
|
209220
|
-
timeoutMs: input2.config.verification.timeoutMs,
|
|
209221
|
-
|
|
210014
|
+
timeoutMs: Math.min(input2.config.verification.timeoutMs, input2.budgetTracker.remainingWallTimeMs()),
|
|
210015
|
+
deadlineAtEpochMs: input2.budgetTracker.deadlineAtEpochMs,
|
|
210016
|
+
maxOutputBytes: input2.budgetTracker.budget.maxAgentOutputBytes,
|
|
210017
|
+
networkMode: input2.networkMode,
|
|
210018
|
+
onStarted: () => {
|
|
210019
|
+
input2.budgetTracker.markStarted(group.reservation);
|
|
210020
|
+
return Promise.resolve();
|
|
210021
|
+
}
|
|
209222
210022
|
}));
|
|
209223
210023
|
let results;
|
|
209224
210024
|
try {
|
|
209225
210025
|
results = await input2.manager.runAll(agentInputs);
|
|
209226
210026
|
} catch (error51) {
|
|
209227
|
-
for (const group of
|
|
209228
|
-
applyVerificationVerdicts(
|
|
210027
|
+
for (const group of scheduledGroups) {
|
|
210028
|
+
applyVerificationVerdicts(group.targets, group.verifier, undefined);
|
|
210029
|
+
await finalizeModelCallResult({
|
|
210030
|
+
budgetTracker: input2.budgetTracker,
|
|
210031
|
+
reservation: group.reservation,
|
|
210032
|
+
result: failedVerifierResult(group.verifier, "AGENT_MANAGER_FAILED"),
|
|
210033
|
+
trace: input2.trace,
|
|
210034
|
+
traceId: input2.traceId
|
|
210035
|
+
});
|
|
210036
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
209229
210037
|
}
|
|
209230
210038
|
const message = `Finding verification failed: ${sanitizeTextForDisplay(error51 instanceof Error ? error51.message : String(error51))}`;
|
|
209231
210039
|
warnings.push(message);
|
|
@@ -209238,9 +210046,32 @@ async function runFindingVerification(input2) {
|
|
|
209238
210046
|
});
|
|
209239
210047
|
return warnings;
|
|
209240
210048
|
}
|
|
209241
|
-
|
|
210049
|
+
const resultByAgent = new Map(results.map((result) => [result.agent, result]));
|
|
210050
|
+
for (const group of scheduledGroups) {
|
|
210051
|
+
const result = resultByAgent.get(group.verifier);
|
|
210052
|
+
if (!result) {
|
|
210053
|
+
applyVerificationVerdicts(group.targets, group.verifier, undefined);
|
|
210054
|
+
await finalizeModelCallResult({
|
|
210055
|
+
budgetTracker: input2.budgetTracker,
|
|
210056
|
+
reservation: group.reservation,
|
|
210057
|
+
result: failedVerifierResult(group.verifier, "AGENT_RESULT_MISSING"),
|
|
210058
|
+
trace: input2.trace,
|
|
210059
|
+
traceId: input2.traceId
|
|
210060
|
+
});
|
|
210061
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
210062
|
+
warnings.push(`Finding verification by ${group.verifier} did not return a result.`);
|
|
210063
|
+
continue;
|
|
210064
|
+
}
|
|
210065
|
+
await finalizeModelCallResult({
|
|
210066
|
+
budgetTracker: input2.budgetTracker,
|
|
210067
|
+
reservation: group.reservation,
|
|
210068
|
+
result,
|
|
210069
|
+
trace: input2.trace,
|
|
210070
|
+
traceId: input2.traceId
|
|
210071
|
+
});
|
|
209242
210072
|
if (result.status !== "completed") {
|
|
209243
|
-
applyVerificationVerdicts(
|
|
210073
|
+
applyVerificationVerdicts(group.targets, result.agent, undefined);
|
|
210074
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
209244
210075
|
const message = `Finding verification by ${result.agent} ${result.status}: ${result.error?.message ?? result.error?.code ?? "no detail"}`;
|
|
209245
210076
|
warnings.push(sanitizeTextForDisplay(message));
|
|
209246
210077
|
await input2.trace.write({
|
|
@@ -209254,8 +210085,9 @@ async function runFindingVerification(input2) {
|
|
|
209254
210085
|
continue;
|
|
209255
210086
|
}
|
|
209256
210087
|
const verdicts = parseVerificationVerdicts(result.rawText);
|
|
209257
|
-
applyVerificationVerdicts(
|
|
210088
|
+
applyVerificationVerdicts(group.targets, result.agent, verdicts);
|
|
209258
210089
|
if (!verdicts) {
|
|
210090
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
209259
210091
|
const message = `Finding verification by ${result.agent} returned malformed verdict JSON.`;
|
|
209260
210092
|
warnings.push(message);
|
|
209261
210093
|
await input2.trace.write({
|
|
@@ -209275,6 +210107,20 @@ async function runFindingVerification(input2) {
|
|
|
209275
210107
|
});
|
|
209276
210108
|
return warnings;
|
|
209277
210109
|
}
|
|
210110
|
+
function failedVerifierResult(agent, code) {
|
|
210111
|
+
const timestamp = new Date().toISOString();
|
|
210112
|
+
return {
|
|
210113
|
+
agent,
|
|
210114
|
+
role: "finding_verifier",
|
|
210115
|
+
status: "failed",
|
|
210116
|
+
startedAt: timestamp,
|
|
210117
|
+
completedAt: timestamp,
|
|
210118
|
+
error: {
|
|
210119
|
+
code,
|
|
210120
|
+
message: "The agent manager did not return a verification result."
|
|
210121
|
+
}
|
|
210122
|
+
};
|
|
210123
|
+
}
|
|
209278
210124
|
function buildJudgeAgentFindings(results) {
|
|
209279
210125
|
return results.flatMap((result) => {
|
|
209280
210126
|
if (!result.normalized)
|
|
@@ -209311,13 +210157,182 @@ function buildCrossModelAnalysis(judge, reviewMode) {
|
|
|
209311
210157
|
provider: judge.provider
|
|
209312
210158
|
};
|
|
209313
210159
|
}
|
|
210160
|
+
async function runBudgetedJudge(input2) {
|
|
210161
|
+
const configuredProvider = input2.requestedProvider ?? input2.config.provider;
|
|
210162
|
+
const provider = resolveJudgeProvider(configuredProvider, input2.env);
|
|
210163
|
+
if (input2.config.mode === "deterministic_only" || provider === "deterministic_fallback") {
|
|
210164
|
+
return runJudge(input2);
|
|
210165
|
+
}
|
|
210166
|
+
const fallback = () => runJudge({
|
|
210167
|
+
...input2,
|
|
210168
|
+
config: { ...input2.config, mode: "deterministic_only" }
|
|
210169
|
+
});
|
|
210170
|
+
if (input2.budgetTracker.snapshot().completion.status === "incomplete") {
|
|
210171
|
+
await recordSkippedJudgeCall(input2, "review_incomplete");
|
|
210172
|
+
return fallback();
|
|
210173
|
+
}
|
|
210174
|
+
if (input2.budgetTracker.budget.skipOptionalPhasesWhenTokenUsageUnknown && input2.budgetTracker.isTokenUsageUnknown()) {
|
|
210175
|
+
await recordSkippedJudgeCall(input2, "token_usage_unknown");
|
|
210176
|
+
return fallback();
|
|
210177
|
+
}
|
|
210178
|
+
const reservationResult = input2.budgetTracker.reserve({ kind: "judge" });
|
|
210179
|
+
if ("failure" in reservationResult) {
|
|
210180
|
+
await recordSkippedJudgeCall(input2, reservationResult.failure.reason);
|
|
210181
|
+
await input2.trace.write({
|
|
210182
|
+
type: "review_budget_exhausted",
|
|
210183
|
+
traceId: input2.traceId,
|
|
210184
|
+
phase: "judge",
|
|
210185
|
+
kind: "judge",
|
|
210186
|
+
reason: reservationResult.failure.reason,
|
|
210187
|
+
timestamp: new Date().toISOString()
|
|
210188
|
+
});
|
|
210189
|
+
return fallback();
|
|
210190
|
+
}
|
|
210191
|
+
const reservation = reservationResult.reservation;
|
|
210192
|
+
await input2.trace.write({
|
|
210193
|
+
type: "model_call_reserved",
|
|
210194
|
+
traceId: input2.traceId,
|
|
210195
|
+
kind: "judge",
|
|
210196
|
+
timestamp: new Date().toISOString()
|
|
210197
|
+
});
|
|
210198
|
+
const timeoutMs = Math.min(input2.config.timeoutMs, input2.budgetTracker.remainingWallTimeMs());
|
|
210199
|
+
if (timeoutMs <= 0) {
|
|
210200
|
+
input2.budgetTracker.skip(reservation, "deadline");
|
|
210201
|
+
await input2.trace.write({
|
|
210202
|
+
type: "review_budget_exhausted",
|
|
210203
|
+
traceId: input2.traceId,
|
|
210204
|
+
phase: "judge",
|
|
210205
|
+
kind: "judge",
|
|
210206
|
+
reason: "deadline",
|
|
210207
|
+
timestamp: new Date().toISOString()
|
|
210208
|
+
});
|
|
210209
|
+
await input2.trace.write({
|
|
210210
|
+
type: "model_call_skipped",
|
|
210211
|
+
traceId: input2.traceId,
|
|
210212
|
+
kind: "judge",
|
|
210213
|
+
reason: "deadline",
|
|
210214
|
+
timestamp: new Date().toISOString()
|
|
210215
|
+
});
|
|
210216
|
+
return fallback();
|
|
210217
|
+
}
|
|
210218
|
+
input2.budgetTracker.markStarted(reservation);
|
|
210219
|
+
const judge = await runJudge({ ...input2, timeoutMs });
|
|
210220
|
+
const usage = normalizeModelTokenUsage(judge.usage);
|
|
210221
|
+
input2.budgetTracker.complete(reservation, {
|
|
210222
|
+
...usage ? { usage } : {}
|
|
210223
|
+
});
|
|
210224
|
+
await input2.trace.write({
|
|
210225
|
+
type: "model_call_completed",
|
|
210226
|
+
traceId: input2.traceId,
|
|
210227
|
+
kind: "judge",
|
|
210228
|
+
provider: judge.provider,
|
|
210229
|
+
resultStatus: judge.status,
|
|
210230
|
+
...usage ? { usage } : {},
|
|
210231
|
+
timestamp: new Date().toISOString()
|
|
210232
|
+
});
|
|
210233
|
+
return judge;
|
|
210234
|
+
}
|
|
210235
|
+
async function recordSkippedJudgeCall(input2, reason) {
|
|
210236
|
+
input2.budgetTracker.recordSkipped({ kind: "judge", reason });
|
|
210237
|
+
await input2.trace.write({
|
|
210238
|
+
type: "model_call_skipped",
|
|
210239
|
+
traceId: input2.traceId,
|
|
210240
|
+
kind: "judge",
|
|
210241
|
+
reason,
|
|
210242
|
+
timestamp: new Date().toISOString()
|
|
210243
|
+
});
|
|
210244
|
+
}
|
|
209314
210245
|
async function runAgents(input2) {
|
|
209315
210246
|
const agentRoles = resolveAgentRoles(input2.config);
|
|
210247
|
+
const enabledAgents = ["codex", "claude"].filter((agent) => input2.config.agents[agent].enabled);
|
|
210248
|
+
if (enabledAgents.length === 0)
|
|
210249
|
+
return [];
|
|
210250
|
+
const reservationResult = input2.budgetTracker.reserveMany(enabledAgents.map((agent) => ({ kind: "primary", agent })));
|
|
210251
|
+
if ("failure" in reservationResult) {
|
|
210252
|
+
input2.budgetTracker.markIncomplete(reservationResult.failure.reason);
|
|
210253
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
210254
|
+
await input2.trace.write({
|
|
210255
|
+
type: "review_budget_exhausted",
|
|
210256
|
+
traceId: input2.traceId,
|
|
210257
|
+
phase: "primary",
|
|
210258
|
+
reason: reservationResult.failure.reason,
|
|
210259
|
+
requiredCalls: enabledAgents.length,
|
|
210260
|
+
timestamp: new Date().toISOString()
|
|
210261
|
+
});
|
|
210262
|
+
for (const agent of enabledAgents) {
|
|
210263
|
+
input2.budgetTracker.recordSkipped({
|
|
210264
|
+
kind: "primary",
|
|
210265
|
+
agent,
|
|
210266
|
+
reason: reservationResult.failure.reason
|
|
210267
|
+
});
|
|
210268
|
+
await input2.trace.write({
|
|
210269
|
+
type: "model_call_skipped",
|
|
210270
|
+
traceId: input2.traceId,
|
|
210271
|
+
kind: "primary",
|
|
210272
|
+
agent,
|
|
210273
|
+
reason: reservationResult.failure.reason,
|
|
210274
|
+
timestamp: new Date().toISOString()
|
|
210275
|
+
});
|
|
210276
|
+
}
|
|
210277
|
+
return enabledAgents.map((agent) => {
|
|
210278
|
+
const role = agentRoles[agent] ?? input2.config.agents[agent].role;
|
|
210279
|
+
const timestamp = new Date().toISOString();
|
|
210280
|
+
return {
|
|
210281
|
+
agent,
|
|
210282
|
+
role,
|
|
210283
|
+
status: "skipped",
|
|
210284
|
+
startedAt: timestamp,
|
|
210285
|
+
completedAt: timestamp,
|
|
210286
|
+
error: {
|
|
210287
|
+
code: reservationResult.failure.reason === "deadline" ? "REVIEW_DEADLINE_EXCEEDED" : "MODEL_CALL_BUDGET_EXHAUSTED",
|
|
210288
|
+
message: reservationResult.failure.reason === "deadline" ? "Review deadline was reached before primary agents could start." : "The review model-call budget cannot reserve all primary agents."
|
|
210289
|
+
}
|
|
210290
|
+
};
|
|
210291
|
+
});
|
|
210292
|
+
}
|
|
210293
|
+
const reservations = new Map(reservationResult.reservations.map((reservation) => [
|
|
210294
|
+
reservation.agent,
|
|
210295
|
+
reservation
|
|
210296
|
+
]));
|
|
210297
|
+
for (const reservation of reservationResult.reservations) {
|
|
210298
|
+
await input2.trace.write({
|
|
210299
|
+
type: "model_call_reserved",
|
|
210300
|
+
traceId: input2.traceId,
|
|
210301
|
+
kind: reservation.kind,
|
|
210302
|
+
agent: reservation.agent,
|
|
210303
|
+
timestamp: new Date().toISOString()
|
|
210304
|
+
});
|
|
210305
|
+
}
|
|
210306
|
+
if (input2.budgetTracker.remainingWallTimeMs() <= 0) {
|
|
210307
|
+
input2.budgetTracker.markIncomplete("deadline");
|
|
210308
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
210309
|
+
await input2.trace.write({
|
|
210310
|
+
type: "review_budget_exhausted",
|
|
210311
|
+
traceId: input2.traceId,
|
|
210312
|
+
phase: "primary",
|
|
210313
|
+
reason: "deadline",
|
|
210314
|
+
timestamp: new Date().toISOString()
|
|
210315
|
+
});
|
|
210316
|
+
return await skipReservedPrimaryAgents({
|
|
210317
|
+
trace: input2.trace,
|
|
210318
|
+
traceId: input2.traceId,
|
|
210319
|
+
budgetTracker: input2.budgetTracker,
|
|
210320
|
+
config: input2.config,
|
|
210321
|
+
agents: enabledAgents,
|
|
210322
|
+
agentRoles,
|
|
210323
|
+
reservations,
|
|
210324
|
+
reason: "deadline"
|
|
210325
|
+
});
|
|
210326
|
+
}
|
|
209316
210327
|
const startedWrites = [];
|
|
209317
210328
|
let acceptingStartedEvents = true;
|
|
209318
|
-
const agentInputs =
|
|
210329
|
+
const agentInputs = enabledAgents.map((agent) => {
|
|
209319
210330
|
const agentConfig = input2.config.agents[agent];
|
|
209320
210331
|
const role = agentRoles[agent] ?? agentConfig.role;
|
|
210332
|
+
const reservation = reservations.get(agent);
|
|
210333
|
+
if (!reservation) {
|
|
210334
|
+
throw new Error(`Missing primary budget reservation for ${agent}.`);
|
|
210335
|
+
}
|
|
209321
210336
|
return {
|
|
209322
210337
|
traceId: input2.traceId,
|
|
209323
210338
|
agent,
|
|
@@ -209325,9 +210340,12 @@ async function runAgents(input2) {
|
|
|
209325
210340
|
tool: input2.tool,
|
|
209326
210341
|
prompt: buildAgentPrompt(input2.tool, input2.request, agent, role),
|
|
209327
210342
|
workspaceDir: input2.workspaceDir,
|
|
209328
|
-
timeoutMs: input2.request.options?.maxAgentTimeoutMs ?? agentConfig.timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS,
|
|
210343
|
+
timeoutMs: Math.min(input2.request.options?.maxAgentTimeoutMs ?? Number.POSITIVE_INFINITY, agentConfig.timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS, input2.budgetTracker.remainingWallTimeMs()),
|
|
210344
|
+
deadlineAtEpochMs: input2.budgetTracker.deadlineAtEpochMs,
|
|
210345
|
+
maxOutputBytes: input2.budgetTracker.budget.maxAgentOutputBytes,
|
|
209329
210346
|
networkMode: input2.networkMode,
|
|
209330
210347
|
onStarted: () => {
|
|
210348
|
+
input2.budgetTracker.markStarted(reservation);
|
|
209331
210349
|
if (!acceptingStartedEvents)
|
|
209332
210350
|
return Promise.resolve();
|
|
209333
210351
|
const event = {
|
|
@@ -209355,10 +210373,61 @@ async function runAgents(input2) {
|
|
|
209355
210373
|
}
|
|
209356
210374
|
};
|
|
209357
210375
|
});
|
|
209358
|
-
|
|
210376
|
+
let results;
|
|
210377
|
+
try {
|
|
210378
|
+
results = await input2.manager.runAll(agentInputs);
|
|
210379
|
+
} catch (error51) {
|
|
210380
|
+
const detail = sanitizeTextForDisplay(error51 instanceof Error ? error51.message : String(error51));
|
|
210381
|
+
input2.warnings.push(`Primary-agent execution failed: ${detail}`);
|
|
210382
|
+
results = agentInputs.map((agentInput) => ({
|
|
210383
|
+
agent: agentInput.agent,
|
|
210384
|
+
role: agentInput.role,
|
|
210385
|
+
status: "failed",
|
|
210386
|
+
startedAt: new Date().toISOString(),
|
|
210387
|
+
completedAt: new Date().toISOString(),
|
|
210388
|
+
error: {
|
|
210389
|
+
code: "AGENT_MANAGER_FAILED",
|
|
210390
|
+
message: "The agent manager did not return a review result."
|
|
210391
|
+
}
|
|
210392
|
+
}));
|
|
210393
|
+
}
|
|
209359
210394
|
acceptingStartedEvents = false;
|
|
209360
210395
|
await Promise.all(startedWrites);
|
|
209361
|
-
|
|
210396
|
+
const resultByAgent = new Map(results.map((result) => [result.agent, result]));
|
|
210397
|
+
const orderedResults = enabledAgents.map((agent) => {
|
|
210398
|
+
const existing = resultByAgent.get(agent);
|
|
210399
|
+
if (existing)
|
|
210400
|
+
return existing;
|
|
210401
|
+
const role = agentRoles[agent] ?? input2.config.agents[agent].role;
|
|
210402
|
+
const timestamp = new Date().toISOString();
|
|
210403
|
+
return {
|
|
210404
|
+
agent,
|
|
210405
|
+
role,
|
|
210406
|
+
status: "failed",
|
|
210407
|
+
startedAt: timestamp,
|
|
210408
|
+
completedAt: timestamp,
|
|
210409
|
+
error: {
|
|
210410
|
+
code: "AGENT_RESULT_MISSING",
|
|
210411
|
+
message: "The agent manager did not return a review result."
|
|
210412
|
+
}
|
|
210413
|
+
};
|
|
210414
|
+
});
|
|
210415
|
+
for (const result of orderedResults) {
|
|
210416
|
+
const reservation = reservations.get(result.agent);
|
|
210417
|
+
if (!reservation)
|
|
210418
|
+
continue;
|
|
210419
|
+
await finalizeModelCallResult({
|
|
210420
|
+
budgetTracker: input2.budgetTracker,
|
|
210421
|
+
reservation,
|
|
210422
|
+
result,
|
|
210423
|
+
trace: input2.trace,
|
|
210424
|
+
traceId: input2.traceId
|
|
210425
|
+
});
|
|
210426
|
+
if (result.status !== "completed") {
|
|
210427
|
+
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
210428
|
+
}
|
|
210429
|
+
}
|
|
210430
|
+
await Promise.all(orderedResults.map((result) => {
|
|
209362
210431
|
const event = {
|
|
209363
210432
|
type: "agent_completed",
|
|
209364
210433
|
traceId: input2.traceId,
|
|
@@ -209378,8 +210447,92 @@ async function runAgents(input2) {
|
|
|
209378
210447
|
}
|
|
209379
210448
|
return input2.trace.write(event);
|
|
209380
210449
|
}));
|
|
210450
|
+
return orderedResults;
|
|
210451
|
+
}
|
|
210452
|
+
async function skipReservedPrimaryAgents(input2) {
|
|
210453
|
+
const results = [];
|
|
210454
|
+
for (const agent of input2.agents) {
|
|
210455
|
+
const reservation = input2.reservations.get(agent);
|
|
210456
|
+
if (reservation)
|
|
210457
|
+
input2.budgetTracker.skip(reservation, input2.reason);
|
|
210458
|
+
await input2.trace.write({
|
|
210459
|
+
type: "model_call_skipped",
|
|
210460
|
+
traceId: input2.traceId,
|
|
210461
|
+
kind: "primary",
|
|
210462
|
+
agent,
|
|
210463
|
+
reason: input2.reason,
|
|
210464
|
+
timestamp: new Date().toISOString()
|
|
210465
|
+
});
|
|
210466
|
+
const timestamp = new Date().toISOString();
|
|
210467
|
+
results.push({
|
|
210468
|
+
agent,
|
|
210469
|
+
role: input2.agentRoles[agent] ?? input2.config.agents[agent].role,
|
|
210470
|
+
status: "skipped",
|
|
210471
|
+
startedAt: timestamp,
|
|
210472
|
+
completedAt: timestamp,
|
|
210473
|
+
error: {
|
|
210474
|
+
code: "REVIEW_DEADLINE_EXCEEDED",
|
|
210475
|
+
message: "Review deadline was reached before the agent could start."
|
|
210476
|
+
}
|
|
210477
|
+
});
|
|
210478
|
+
}
|
|
209381
210479
|
return results;
|
|
209382
210480
|
}
|
|
210481
|
+
async function finalizeModelCallResult(input2) {
|
|
210482
|
+
const reason = input2.result.error?.code ?? input2.result.status;
|
|
210483
|
+
const hasStarted = input2.budgetTracker.hasStarted(input2.reservation);
|
|
210484
|
+
const canSkip = input2.result.status === "skipped" || isPreflightAgentFailure(input2.result) || !hasStarted && input2.result.error?.code === "REVIEW_DEADLINE_EXCEEDED";
|
|
210485
|
+
if (canSkip && !hasStarted) {
|
|
210486
|
+
input2.budgetTracker.skip(input2.reservation, reason);
|
|
210487
|
+
if (input2.result.error?.code === "REVIEW_DEADLINE_EXCEEDED") {
|
|
210488
|
+
input2.budgetTracker.markIncomplete("deadline");
|
|
210489
|
+
}
|
|
210490
|
+
await input2.trace.write({
|
|
210491
|
+
type: "model_call_skipped",
|
|
210492
|
+
traceId: input2.traceId,
|
|
210493
|
+
kind: input2.reservation.kind,
|
|
210494
|
+
agent: input2.reservation.agent,
|
|
210495
|
+
reason,
|
|
210496
|
+
timestamp: new Date().toISOString()
|
|
210497
|
+
});
|
|
210498
|
+
return;
|
|
210499
|
+
}
|
|
210500
|
+
input2.budgetTracker.markStarted(input2.reservation);
|
|
210501
|
+
const usage = normalizeModelTokenUsage(input2.result.usage);
|
|
210502
|
+
const outputBytes = input2.result.outputBytes ?? (input2.result.rawText ? Buffer.byteLength(input2.result.rawText, "utf8") : undefined);
|
|
210503
|
+
input2.budgetTracker.complete(input2.reservation, {
|
|
210504
|
+
...outputBytes === undefined ? {} : { outputBytes },
|
|
210505
|
+
...usage ? { usage } : {},
|
|
210506
|
+
...input2.result.stopReason ? { stopReason: input2.result.stopReason } : {}
|
|
210507
|
+
});
|
|
210508
|
+
if (input2.result.error?.code === "AGENT_OUTPUT_LIMIT") {
|
|
210509
|
+
input2.budgetTracker.markIncomplete("agent_output_limit");
|
|
210510
|
+
}
|
|
210511
|
+
if (input2.result.error?.code === "REVIEW_DEADLINE_EXCEEDED") {
|
|
210512
|
+
input2.budgetTracker.markIncomplete("deadline");
|
|
210513
|
+
}
|
|
210514
|
+
await input2.trace.write({
|
|
210515
|
+
type: "model_call_completed",
|
|
210516
|
+
traceId: input2.traceId,
|
|
210517
|
+
kind: input2.reservation.kind,
|
|
210518
|
+
agent: input2.reservation.agent,
|
|
210519
|
+
resultStatus: input2.result.status,
|
|
210520
|
+
...input2.result.error?.code ? { errorCode: input2.result.error.code } : {},
|
|
210521
|
+
...outputBytes === undefined ? {} : { outputBytes },
|
|
210522
|
+
...usage ? { usage } : {},
|
|
210523
|
+
...input2.result.stopReason ? { stopReason: input2.result.stopReason } : {},
|
|
210524
|
+
timestamp: new Date().toISOString()
|
|
210525
|
+
});
|
|
210526
|
+
}
|
|
210527
|
+
function isPreflightAgentFailure(result) {
|
|
210528
|
+
return result.status === "failed" && [
|
|
210529
|
+
"AGENT_CONFIG_INVALID",
|
|
210530
|
+
"OPENROUTER_KEY_MISSING",
|
|
210531
|
+
"AGENT_SPAWN_FAILED",
|
|
210532
|
+
"AGENT_MANAGER_FAILED",
|
|
210533
|
+
"AGENT_RESULT_MISSING"
|
|
210534
|
+
].includes(result.error?.code ?? "");
|
|
210535
|
+
}
|
|
209383
210536
|
function resolveAgentRoles(config2) {
|
|
209384
210537
|
const enabledAgents = ["codex", "claude"].filter((agent) => config2.agents[agent].enabled);
|
|
209385
210538
|
const singleAgentMode = enabledAgents.length === 1;
|
|
@@ -209395,14 +210548,30 @@ function defaultAgentManager(config2, parentEnv) {
|
|
|
209395
210548
|
}
|
|
209396
210549
|
return new SubprocessAcpAgentManager(config2, parentEnv);
|
|
209397
210550
|
}
|
|
209398
|
-
function normalizeAgentRunResult(result) {
|
|
209399
|
-
|
|
209400
|
-
|
|
209401
|
-
|
|
209402
|
-
|
|
209403
|
-
|
|
209404
|
-
|
|
209405
|
-
|
|
210551
|
+
function normalizeAgentRunResult(result, maxFindingsPerAgent) {
|
|
210552
|
+
const normalizedResult = result.status === "completed" && result.rawText && !result.normalized ? {
|
|
210553
|
+
...result,
|
|
210554
|
+
normalized: normalizeAgentOutput(result.agent, result.role, result.rawText)
|
|
210555
|
+
} : result;
|
|
210556
|
+
const normalized = normalizedResult.normalized;
|
|
210557
|
+
const findings = normalized?.findings;
|
|
210558
|
+
if (!normalized || !findings || findings.length <= maxFindingsPerAgent) {
|
|
210559
|
+
return { result: normalizedResult, findingsCapped: false };
|
|
210560
|
+
}
|
|
210561
|
+
const limitedFindings = findings.map((finding, index) => ({ finding, index })).sort((left, right) => {
|
|
210562
|
+
const severity = compareSeverity(left.finding.severity, right.finding.severity);
|
|
210563
|
+
return severity === 0 ? left.index - right.index : severity;
|
|
210564
|
+
}).slice(0, maxFindingsPerAgent).map(({ finding }) => finding);
|
|
210565
|
+
return {
|
|
210566
|
+
result: {
|
|
210567
|
+
...normalizedResult,
|
|
210568
|
+
normalized: {
|
|
210569
|
+
...normalized,
|
|
210570
|
+
findings: limitedFindings
|
|
210571
|
+
}
|
|
210572
|
+
},
|
|
210573
|
+
findingsCapped: true
|
|
210574
|
+
};
|
|
209406
210575
|
}
|
|
209407
210576
|
function agentOpinionSummary(result, includeRawText = false) {
|
|
209408
210577
|
const opinion = {
|
|
@@ -209424,8 +210593,12 @@ async function buildSecretBlockResult(input2) {
|
|
|
209424
210593
|
});
|
|
209425
210594
|
const cisa = input2.tool === "security_review" ? computeCisaGate([finding], []) : undefined;
|
|
209426
210595
|
const completedAt = new Date().toISOString();
|
|
210596
|
+
const budget = input2.budgetTracker.snapshot();
|
|
209427
210597
|
const resultWithoutMarkdown = {
|
|
209428
210598
|
decision: "block",
|
|
210599
|
+
completion: budget.completion,
|
|
210600
|
+
executionBudget: budget.executionBudget,
|
|
210601
|
+
requestFingerprint: input2.requestFingerprint,
|
|
209429
210602
|
degraded: false,
|
|
209430
210603
|
agentsUsed: [],
|
|
209431
210604
|
reviewMode: "multi_agent",
|
|
@@ -209461,9 +210634,16 @@ async function buildSecretBlockResult(input2) {
|
|
|
209461
210634
|
networkMode: input2.networkMode,
|
|
209462
210635
|
workspaceMode: "temp_snapshot",
|
|
209463
210636
|
configHash: input2.configHash,
|
|
209464
|
-
warnings: input2.warnings
|
|
210637
|
+
warnings: input2.warnings,
|
|
210638
|
+
modelCalls: budget.modelCalls
|
|
209465
210639
|
}
|
|
209466
210640
|
};
|
|
210641
|
+
await writeReviewBudgetCompleted({
|
|
210642
|
+
trace: input2.trace,
|
|
210643
|
+
traceId: input2.traceId,
|
|
210644
|
+
budgetTracker: input2.budgetTracker,
|
|
210645
|
+
requestFingerprint: input2.requestFingerprint
|
|
210646
|
+
});
|
|
209467
210647
|
await input2.trace.write({
|
|
209468
210648
|
type: "decision_completed",
|
|
209469
210649
|
traceId: input2.traceId,
|
|
@@ -209506,8 +210686,12 @@ function reindexFindings(findings) {
|
|
|
209506
210686
|
}
|
|
209507
210687
|
async function buildPolicyBlockResult(input2) {
|
|
209508
210688
|
const completedAt = new Date().toISOString();
|
|
210689
|
+
const budget = input2.budgetTracker.snapshot();
|
|
209509
210690
|
const resultWithoutMarkdown = {
|
|
209510
210691
|
decision: "block",
|
|
210692
|
+
completion: budget.completion,
|
|
210693
|
+
executionBudget: budget.executionBudget,
|
|
210694
|
+
requestFingerprint: input2.requestFingerprint,
|
|
209511
210695
|
degraded: false,
|
|
209512
210696
|
agentsUsed: [],
|
|
209513
210697
|
reviewMode: "multi_agent",
|
|
@@ -209526,9 +210710,16 @@ async function buildPolicyBlockResult(input2) {
|
|
|
209526
210710
|
networkMode: input2.networkMode,
|
|
209527
210711
|
workspaceMode: "temp_snapshot",
|
|
209528
210712
|
configHash: input2.configHash,
|
|
209529
|
-
warnings: [input2.warning]
|
|
210713
|
+
warnings: [input2.warning],
|
|
210714
|
+
modelCalls: budget.modelCalls
|
|
209530
210715
|
}
|
|
209531
210716
|
};
|
|
210717
|
+
await writeReviewBudgetCompleted({
|
|
210718
|
+
trace: input2.trace,
|
|
210719
|
+
traceId: input2.traceId,
|
|
210720
|
+
budgetTracker: input2.budgetTracker,
|
|
210721
|
+
requestFingerprint: input2.requestFingerprint
|
|
210722
|
+
});
|
|
209532
210723
|
await input2.trace.write({
|
|
209533
210724
|
type: "decision_completed",
|
|
209534
210725
|
traceId: input2.traceId,
|
|
@@ -209565,6 +210756,33 @@ async function finalizeReviewResult(input2) {
|
|
|
209565
210756
|
})
|
|
209566
210757
|
};
|
|
209567
210758
|
}
|
|
210759
|
+
async function writeReviewBudgetPlanned(input2) {
|
|
210760
|
+
const snapshot = input2.budgetTracker.snapshot();
|
|
210761
|
+
await input2.trace.write({
|
|
210762
|
+
type: "review_budget_planned",
|
|
210763
|
+
traceId: input2.traceId,
|
|
210764
|
+
requestFingerprint: input2.requestFingerprint,
|
|
210765
|
+
maxModelCalls: snapshot.executionBudget.maxModelCalls,
|
|
210766
|
+
maxTotalWallTimeMs: snapshot.executionBudget.wallTime.limitMs,
|
|
210767
|
+
maxAgentOutputBytes: snapshot.executionBudget.maxAgentOutputBytes,
|
|
210768
|
+
maxFindingsPerAgent: snapshot.executionBudget.maxFindingsPerAgent,
|
|
210769
|
+
skipOptionalPhasesWhenTokenUsageUnknown: snapshot.executionBudget.skipOptionalPhasesWhenTokenUsageUnknown,
|
|
210770
|
+
timestamp: new Date().toISOString()
|
|
210771
|
+
});
|
|
210772
|
+
}
|
|
210773
|
+
async function writeReviewBudgetCompleted(input2) {
|
|
210774
|
+
const snapshot = input2.budgetTracker.snapshot();
|
|
210775
|
+
await input2.trace.write({
|
|
210776
|
+
type: "review_budget_completed",
|
|
210777
|
+
traceId: input2.traceId,
|
|
210778
|
+
requestFingerprint: input2.requestFingerprint,
|
|
210779
|
+
completion: snapshot.completion,
|
|
210780
|
+
modelCalls: snapshot.executionBudget.modelCalls,
|
|
210781
|
+
wallTime: snapshot.executionBudget.wallTime,
|
|
210782
|
+
tokenUsage: snapshot.executionBudget.tokenUsage,
|
|
210783
|
+
timestamp: new Date().toISOString()
|
|
210784
|
+
});
|
|
210785
|
+
}
|
|
209568
210786
|
function mergeDenyPatterns(configDeny, requestDeny) {
|
|
209569
210787
|
return Array.from(new Set([...configDeny, ...requestDeny ?? []]));
|
|
209570
210788
|
}
|
|
@@ -209617,6 +210835,13 @@ var kyosoReviewRequestSchema = object({
|
|
|
209617
210835
|
options: object({
|
|
209618
210836
|
network: _enum2(["model_only", "unrestricted"]).optional(),
|
|
209619
210837
|
maxAgentTimeoutMs: number2().int().positive().optional(),
|
|
210838
|
+
reviewBudget: object({
|
|
210839
|
+
maxModelCalls: number2().int().positive().optional(),
|
|
210840
|
+
maxTotalWallTimeMs: number2().int().positive().optional(),
|
|
210841
|
+
maxAgentOutputBytes: number2().int().positive().optional(),
|
|
210842
|
+
maxFindingsPerAgent: number2().int().positive().optional(),
|
|
210843
|
+
skipOptionalPhasesWhenTokenUsageUnknown: boolean2().optional()
|
|
210844
|
+
}).strict().optional(),
|
|
209620
210845
|
includeAgentRawOutputs: boolean2().optional(),
|
|
209621
210846
|
judgeProvider: _enum2(["auto", "openai", "anthropic", "none"]).optional(),
|
|
209622
210847
|
allowSecretRedaction: boolean2().optional()
|