@kyo-so/cli 0.12.0 → 0.13.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/CHANGELOG.md +27 -0
- package/README.ja.md +64 -34
- package/README.md +64 -34
- package/README.zh-CN.md +64 -34
- package/dist/acp/ndJsonLineLimit.d.ts +6 -0
- package/dist/acp/normalize.d.ts +1 -0
- package/dist/acp/prompts.d.ts +1 -0
- package/dist/bin/kyoso.js +840 -165
- package/dist/cli/pluginRuntimeContract.d.ts +8 -8
- package/dist/config/schema.d.ts +1 -0
- package/dist/core/constants.d.ts +3 -2
- package/dist/core/modelExecutionIdentity.d.ts +9 -0
- package/dist/core/requestFingerprint.d.ts +2 -2
- package/dist/core/reviewBudget.d.ts +28 -5
- package/dist/core/types.d.ts +44 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +780 -128
- package/dist/judge/anthropic.d.ts +2 -0
- package/dist/judge/openai.d.ts +2 -0
- package/dist/judge/provider.d.ts +9 -1
- package/dist/utils/env.d.ts +12 -2
- package/examples/codex-config.toml +1 -1
- package/package.json +5 -2
- package/scripts/review-budget-report.mjs +1114 -0
package/dist/bin/kyoso.js
CHANGED
|
@@ -183944,13 +183944,14 @@ function matchesPathPattern(path, patterns, mode) {
|
|
|
183944
183944
|
}
|
|
183945
183945
|
|
|
183946
183946
|
// src/core/constants.ts
|
|
183947
|
-
var DEFAULT_AGENT_TIMEOUT_MS =
|
|
183947
|
+
var DEFAULT_AGENT_TIMEOUT_MS = 600000;
|
|
183948
|
+
var DEFAULT_WARN_AGENT_OUTPUT_BYTES = 524288;
|
|
183948
183949
|
var MAX_AGENT_OUTPUT_BYTES = 1048576;
|
|
183949
183950
|
var JUDGE_MAX_OUTPUT_TOKENS = 4096;
|
|
183950
183951
|
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
183951
183952
|
var TRACE_DIR = ".kyoso/traces";
|
|
183952
183953
|
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
183953
|
-
var KYOSO_VERSION = "0.
|
|
183954
|
+
var KYOSO_VERSION = "0.13.0";
|
|
183954
183955
|
|
|
183955
183956
|
// src/utils/pathContainment.ts
|
|
183956
183957
|
import { resolve, sep as sep2 } from "node:path";
|
|
@@ -184255,9 +184256,9 @@ var defaultConfig = {
|
|
|
184255
184256
|
enabled: true,
|
|
184256
184257
|
type: "acp",
|
|
184257
184258
|
command: "npx",
|
|
184258
|
-
args: ["-y", "@agentclientprotocol/codex-acp@1.1.
|
|
184259
|
+
args: ["-y", "@agentclientprotocol/codex-acp@1.1.4"],
|
|
184259
184260
|
role: "implementation_reviewer",
|
|
184260
|
-
timeoutMs:
|
|
184261
|
+
timeoutMs: DEFAULT_AGENT_TIMEOUT_MS,
|
|
184261
184262
|
allowProjectProvider: [],
|
|
184262
184263
|
env: {
|
|
184263
184264
|
INITIAL_AGENT_MODE: "read-only",
|
|
@@ -184282,7 +184283,7 @@ var defaultConfig = {
|
|
|
184282
184283
|
command: "npx",
|
|
184283
184284
|
args: ["-y", "@agentclientprotocol/claude-agent-acp@0.58.1"],
|
|
184284
184285
|
role: "architecture_security_reviewer",
|
|
184285
|
-
timeoutMs:
|
|
184286
|
+
timeoutMs: DEFAULT_AGENT_TIMEOUT_MS,
|
|
184286
184287
|
env: {
|
|
184287
184288
|
KYOSO_CHILD_AGENT: "1"
|
|
184288
184289
|
},
|
|
@@ -184363,10 +184364,11 @@ var defaultConfig = {
|
|
|
184363
184364
|
},
|
|
184364
184365
|
reviewBudget: {
|
|
184365
184366
|
maxModelCalls: 4,
|
|
184366
|
-
maxTotalWallTimeMs:
|
|
184367
|
-
|
|
184367
|
+
maxTotalWallTimeMs: 660000,
|
|
184368
|
+
warnAgentOutputBytes: DEFAULT_WARN_AGENT_OUTPUT_BYTES,
|
|
184369
|
+
maxAgentOutputBytes: 1048576,
|
|
184368
184370
|
maxFindingsPerAgent: 10,
|
|
184369
|
-
skipOptionalPhasesWhenTokenUsageUnknown:
|
|
184371
|
+
skipOptionalPhasesWhenTokenUsageUnknown: false
|
|
184370
184372
|
},
|
|
184371
184373
|
audit: {
|
|
184372
184374
|
enabled: true,
|
|
@@ -184724,7 +184726,7 @@ var baseAgentSchema = exports_external.object({
|
|
|
184724
184726
|
"architecture_security_reviewer",
|
|
184725
184727
|
"combined_reviewer"
|
|
184726
184728
|
]),
|
|
184727
|
-
timeoutMs: exports_external.number().int().positive().default(
|
|
184729
|
+
timeoutMs: exports_external.number().int().positive().default(DEFAULT_AGENT_TIMEOUT_MS),
|
|
184728
184730
|
env: exports_external.record(exports_external.string(), exports_external.string()).default({}),
|
|
184729
184731
|
auth: exports_external.object({
|
|
184730
184732
|
mode: exports_external.literal("passthrough").default("passthrough"),
|
|
@@ -184755,6 +184757,7 @@ var codexAgentSchema = baseAgentSchema.extend({
|
|
|
184755
184757
|
var reviewBudgetSchema = exports_external.object({
|
|
184756
184758
|
maxModelCalls: exports_external.number().int().positive(),
|
|
184757
184759
|
maxTotalWallTimeMs: exports_external.number().int().positive(),
|
|
184760
|
+
warnAgentOutputBytes: exports_external.number().int().positive().max(MAX_AGENT_OUTPUT_BYTES),
|
|
184758
184761
|
maxAgentOutputBytes: exports_external.number().int().positive().max(MAX_AGENT_OUTPUT_BYTES),
|
|
184759
184762
|
maxFindingsPerAgent: exports_external.number().int().positive(),
|
|
184760
184763
|
skipOptionalPhasesWhenTokenUsageUnknown: exports_external.boolean()
|
|
@@ -184830,13 +184833,21 @@ var kyosoConfigSchema = exports_external.object({
|
|
|
184830
184833
|
})
|
|
184831
184834
|
}).superRefine((config2, context) => {
|
|
184832
184835
|
const enabledPrimaryReviewers = Object.values(config2.agents).filter((agent) => agent.enabled).length;
|
|
184833
|
-
if (config2.reviewBudget.maxModelCalls
|
|
184834
|
-
|
|
184835
|
-
|
|
184836
|
-
|
|
184837
|
-
|
|
184838
|
-
|
|
184839
|
-
}
|
|
184836
|
+
if (config2.reviewBudget.maxModelCalls < enabledPrimaryReviewers) {
|
|
184837
|
+
context.addIssue({
|
|
184838
|
+
code: exports_external.ZodIssueCode.custom,
|
|
184839
|
+
path: ["reviewBudget", "maxModelCalls"],
|
|
184840
|
+
message: "must be greater than or equal to the number of enabled primary reviewers."
|
|
184841
|
+
});
|
|
184842
|
+
}
|
|
184843
|
+
const inheritedLegacyHardLimit = config2.reviewBudget.warnAgentOutputBytes === DEFAULT_WARN_AGENT_OUTPUT_BYTES && config2.reviewBudget.maxAgentOutputBytes <= DEFAULT_WARN_AGENT_OUTPUT_BYTES;
|
|
184844
|
+
if (config2.reviewBudget.warnAgentOutputBytes >= config2.reviewBudget.maxAgentOutputBytes && !inheritedLegacyHardLimit) {
|
|
184845
|
+
context.addIssue({
|
|
184846
|
+
code: exports_external.ZodIssueCode.custom,
|
|
184847
|
+
path: ["reviewBudget", "warnAgentOutputBytes"],
|
|
184848
|
+
message: "must be less than reviewBudget.maxAgentOutputBytes."
|
|
184849
|
+
});
|
|
184850
|
+
}
|
|
184840
184851
|
});
|
|
184841
184852
|
function agentConfigLeafPaths(agent) {
|
|
184842
184853
|
const paths = [
|
|
@@ -184899,6 +184910,7 @@ var kyosoConfigKnownLeafPaths = [
|
|
|
184899
184910
|
"verification.allowDemotion",
|
|
184900
184911
|
"reviewBudget.maxModelCalls",
|
|
184901
184912
|
"reviewBudget.maxTotalWallTimeMs",
|
|
184913
|
+
"reviewBudget.warnAgentOutputBytes",
|
|
184902
184914
|
"reviewBudget.maxAgentOutputBytes",
|
|
184903
184915
|
"reviewBudget.maxFindingsPerAgent",
|
|
184904
184916
|
"reviewBudget.skipOptionalPhasesWhenTokenUsageUnknown",
|
|
@@ -186033,6 +186045,7 @@ async function loadConfig(options = {}) {
|
|
|
186033
186045
|
if (!options.ignoreConfig) {
|
|
186034
186046
|
if (await exists2(globalConfigPath)) {
|
|
186035
186047
|
const globalConfig2 = await loadTomlConfigFile(globalConfigPath);
|
|
186048
|
+
validateExplicitReviewBudgetThresholds(globalConfig2, defaultConfig);
|
|
186036
186049
|
const globalConfigWarnings = collectGlobalConfigWarnings(globalConfigPath, globalConfig2);
|
|
186037
186050
|
const securitySensitiveWarnings = globalConfigWarnings.filter((warning) => warning.startsWith("security-sensitive unknown settings "));
|
|
186038
186051
|
if (securitySensitiveWarnings.length > 0 && !options.allowUnknownConfig) {
|
|
@@ -186281,6 +186294,7 @@ async function loadProjectTsConfig(input2) {
|
|
|
186281
186294
|
});
|
|
186282
186295
|
if (trustDecision.execute) {
|
|
186283
186296
|
const userConfig = await loadUserConfig(canonicalPath, source);
|
|
186297
|
+
validateExplicitReviewBudgetThresholds(userConfig, input2.baseConfig);
|
|
186284
186298
|
const projectChangesOpenRouterRoute = projectConfigChangesOpenRouterRoute(userConfig, input2.baseConfig);
|
|
186285
186299
|
await assertProjectOpenRouterAuthorization({
|
|
186286
186300
|
projectConfig: userConfig,
|
|
@@ -186374,6 +186388,23 @@ function deepMerge2(base, override) {
|
|
|
186374
186388
|
}
|
|
186375
186389
|
return result;
|
|
186376
186390
|
}
|
|
186391
|
+
function validateExplicitReviewBudgetThresholds(config2, baseConfig) {
|
|
186392
|
+
const reviewBudget = readRecord(config2, "reviewBudget");
|
|
186393
|
+
if (!reviewBudget || !Object.prototype.hasOwnProperty.call(reviewBudget, "warnAgentOutputBytes")) {
|
|
186394
|
+
return;
|
|
186395
|
+
}
|
|
186396
|
+
const warnAgentOutputBytes = reviewBudget.warnAgentOutputBytes;
|
|
186397
|
+
const maxAgentOutputBytes = Object.prototype.hasOwnProperty.call(reviewBudget, "maxAgentOutputBytes") ? reviewBudget.maxAgentOutputBytes : readRecord(baseConfig, "reviewBudget")?.maxAgentOutputBytes;
|
|
186398
|
+
if (typeof warnAgentOutputBytes === "number" && typeof maxAgentOutputBytes === "number" && warnAgentOutputBytes >= maxAgentOutputBytes) {
|
|
186399
|
+
throw new Error("reviewBudget.warnAgentOutputBytes must be less than reviewBudget.maxAgentOutputBytes.");
|
|
186400
|
+
}
|
|
186401
|
+
}
|
|
186402
|
+
function readRecord(value, key) {
|
|
186403
|
+
if (!isRecord3(value))
|
|
186404
|
+
return;
|
|
186405
|
+
const nested = value[key];
|
|
186406
|
+
return isRecord3(nested) ? nested : undefined;
|
|
186407
|
+
}
|
|
186377
186408
|
function isRecord3(value) {
|
|
186378
186409
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186379
186410
|
}
|
|
@@ -186386,6 +186417,55 @@ async function exists2(path) {
|
|
|
186386
186417
|
}
|
|
186387
186418
|
}
|
|
186388
186419
|
|
|
186420
|
+
// src/core/modelExecutionIdentity.ts
|
|
186421
|
+
var MODEL_EXECUTION_IDENTITY_MAX_CHARS = 160;
|
|
186422
|
+
var MODEL_PROVIDER_ROUTES = new Set([
|
|
186423
|
+
"codex_default",
|
|
186424
|
+
"claude_default",
|
|
186425
|
+
"openrouter",
|
|
186426
|
+
"openai",
|
|
186427
|
+
"anthropic"
|
|
186428
|
+
]);
|
|
186429
|
+
function createModelExecutionIdentity(input2) {
|
|
186430
|
+
const requestedModel = sanitizeIdentityValue(input2.requestedModel);
|
|
186431
|
+
const reportedProvider = sanitizeIdentityValue(input2.reportedProvider);
|
|
186432
|
+
const reportedModel = sanitizeIdentityValue(input2.reportedModel);
|
|
186433
|
+
const reportingStatus = reportedProvider !== undefined || reportedModel !== undefined ? "reported" : requestedModel !== undefined ? "requested_only" : "unknown";
|
|
186434
|
+
return {
|
|
186435
|
+
providerRoute: input2.providerRoute,
|
|
186436
|
+
...requestedModel ? { requestedModel } : {},
|
|
186437
|
+
...reportedProvider ? { reportedProvider } : {},
|
|
186438
|
+
...reportedModel ? { reportedModel } : {},
|
|
186439
|
+
reportingStatus
|
|
186440
|
+
};
|
|
186441
|
+
}
|
|
186442
|
+
function normalizeModelExecutionIdentity(value) {
|
|
186443
|
+
if (!isRecord4(value) || !isModelProviderRoute(value.providerRoute)) {
|
|
186444
|
+
return;
|
|
186445
|
+
}
|
|
186446
|
+
return createModelExecutionIdentity({
|
|
186447
|
+
providerRoute: value.providerRoute,
|
|
186448
|
+
requestedModel: value.requestedModel,
|
|
186449
|
+
reportedProvider: value.reportedProvider,
|
|
186450
|
+
reportedModel: value.reportedModel
|
|
186451
|
+
});
|
|
186452
|
+
}
|
|
186453
|
+
function sanitizeIdentityValue(value) {
|
|
186454
|
+
if (typeof value !== "string")
|
|
186455
|
+
return;
|
|
186456
|
+
const sanitized = sanitizeTextForDisplay(value, MODEL_EXECUTION_IDENTITY_MAX_CHARS);
|
|
186457
|
+
if (sanitized.includes(REDACTION) || /(?:https?|wss?):\/\//i.test(sanitized) || /\b(?:api[_-]?key|base[_-]?url|credential|secret|token|password)\b/i.test(sanitized) || /[{}=]/.test(sanitized)) {
|
|
186458
|
+
return;
|
|
186459
|
+
}
|
|
186460
|
+
return sanitized.length > 0 ? sanitized : undefined;
|
|
186461
|
+
}
|
|
186462
|
+
function isModelProviderRoute(value) {
|
|
186463
|
+
return typeof value === "string" && MODEL_PROVIDER_ROUTES.has(value);
|
|
186464
|
+
}
|
|
186465
|
+
function isRecord4(value) {
|
|
186466
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
186467
|
+
}
|
|
186468
|
+
|
|
186389
186469
|
// src/core/tokenUsage.ts
|
|
186390
186470
|
var TOKEN_USAGE_KEYS = [
|
|
186391
186471
|
"totalTokens",
|
|
@@ -186396,7 +186476,7 @@ var TOKEN_USAGE_KEYS = [
|
|
|
186396
186476
|
"cachedWriteTokens"
|
|
186397
186477
|
];
|
|
186398
186478
|
function normalizeModelTokenUsage(usage) {
|
|
186399
|
-
if (!
|
|
186479
|
+
if (!isRecord5(usage))
|
|
186400
186480
|
return;
|
|
186401
186481
|
const normalized = {};
|
|
186402
186482
|
for (const key of TOKEN_USAGE_KEYS) {
|
|
@@ -186409,7 +186489,7 @@ function normalizeModelTokenUsage(usage) {
|
|
|
186409
186489
|
function isTokenCount(value) {
|
|
186410
186490
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
186411
186491
|
}
|
|
186412
|
-
function
|
|
186492
|
+
function isRecord5(value) {
|
|
186413
186493
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186414
186494
|
}
|
|
186415
186495
|
|
|
@@ -186461,7 +186541,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
|
|
|
186461
186541
|
const parsed = JSON.parse(json2);
|
|
186462
186542
|
const summaryText = typeof parsed.summaryText === "string" && parsed.summaryText.trim().length > 0 ? sanitizeText(parsed.summaryText) : fallbackSummaryText;
|
|
186463
186543
|
const disagreementComments = Array.isArray(parsed.disagreementComments) ? parsed.disagreementComments.flatMap((item) => {
|
|
186464
|
-
if (!
|
|
186544
|
+
if (!isRecord6(item) || typeof item.topic !== "string" || typeof item.judgeComment !== "string") {
|
|
186465
186545
|
return [];
|
|
186466
186546
|
}
|
|
186467
186547
|
return [
|
|
@@ -186477,7 +186557,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
|
|
|
186477
186557
|
return { summaryText, disagreementComments, analysis };
|
|
186478
186558
|
}
|
|
186479
186559
|
function parseAnalysis(value) {
|
|
186480
|
-
if (!
|
|
186560
|
+
if (!isRecord6(value))
|
|
186481
186561
|
return;
|
|
186482
186562
|
if (!Array.isArray(value.blindSpots) || !Array.isArray(value.contradictions) || !Array.isArray(value.partialCoverage)) {
|
|
186483
186563
|
return;
|
|
@@ -186485,7 +186565,7 @@ function parseAnalysis(value) {
|
|
|
186485
186565
|
return {
|
|
186486
186566
|
blindSpots: value.blindSpots.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => typeof item === "string" ? [sanitizeAnalysisText(item)] : []),
|
|
186487
186567
|
contradictions: value.contradictions.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
|
|
186488
|
-
if (!
|
|
186568
|
+
if (!isRecord6(item) || typeof item.topic !== "string" || typeof item.detail !== "string") {
|
|
186489
186569
|
return [];
|
|
186490
186570
|
}
|
|
186491
186571
|
return [
|
|
@@ -186496,7 +186576,7 @@ function parseAnalysis(value) {
|
|
|
186496
186576
|
];
|
|
186497
186577
|
}),
|
|
186498
186578
|
partialCoverage: value.partialCoverage.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
|
|
186499
|
-
if (!
|
|
186579
|
+
if (!isRecord6(item) || typeof item.note !== "string")
|
|
186500
186580
|
return [];
|
|
186501
186581
|
const findingId = typeof item.findingId === "string" ? sanitizeAnalysisText(item.findingId) : undefined;
|
|
186502
186582
|
return [
|
|
@@ -186543,15 +186623,20 @@ function extractFirstJsonObject(text) {
|
|
|
186543
186623
|
}
|
|
186544
186624
|
return;
|
|
186545
186625
|
}
|
|
186546
|
-
function
|
|
186626
|
+
function isRecord6(value) {
|
|
186547
186627
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186548
186628
|
}
|
|
186549
186629
|
|
|
186550
186630
|
// src/judge/anthropic.ts
|
|
186631
|
+
var DEFAULT_ANTHROPIC_JUDGE_MODEL = "claude-haiku-4-5";
|
|
186632
|
+
function resolveAnthropicJudgeModel(env) {
|
|
186633
|
+
return env.KYOSO_ANTHROPIC_JUDGE_MODEL ?? DEFAULT_ANTHROPIC_JUDGE_MODEL;
|
|
186634
|
+
}
|
|
186551
186635
|
async function runAnthropicJudge(input2, timeoutMs) {
|
|
186552
186636
|
const apiKey = input2.env.ANTHROPIC_API_KEY;
|
|
186553
186637
|
if (!apiKey)
|
|
186554
186638
|
throw new Error("ANTHROPIC_API_KEY is not configured.");
|
|
186639
|
+
const requestedModel = resolveAnthropicJudgeModel(input2.env);
|
|
186555
186640
|
const response = await fetchWithTimeout(`${input2.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com"}/v1/messages`, {
|
|
186556
186641
|
method: "POST",
|
|
186557
186642
|
headers: {
|
|
@@ -186560,7 +186645,7 @@ async function runAnthropicJudge(input2, timeoutMs) {
|
|
|
186560
186645
|
"content-type": "application/json"
|
|
186561
186646
|
},
|
|
186562
186647
|
body: JSON.stringify({
|
|
186563
|
-
model:
|
|
186648
|
+
model: requestedModel,
|
|
186564
186649
|
max_tokens: JUDGE_MAX_OUTPUT_TOKENS,
|
|
186565
186650
|
temperature: 0,
|
|
186566
186651
|
messages: [
|
|
@@ -186578,8 +186663,11 @@ async function runAnthropicJudge(input2, timeoutMs) {
|
|
|
186578
186663
|
if (!content)
|
|
186579
186664
|
throw new Error("Anthropic judge response did not include text content.");
|
|
186580
186665
|
const usage = normalizeUsage(payload.usage);
|
|
186666
|
+
const reportedModel = typeof payload.model === "string" ? payload.model : undefined;
|
|
186581
186667
|
return {
|
|
186582
186668
|
output: parseJudgeOutput(content, input2.summaryText),
|
|
186669
|
+
requestedModel,
|
|
186670
|
+
...reportedModel ? { reportedModel } : {},
|
|
186583
186671
|
...usage ? { usage } : {}
|
|
186584
186672
|
};
|
|
186585
186673
|
}
|
|
@@ -186616,10 +186704,15 @@ function runDeterministicJudge(result, summaryText) {
|
|
|
186616
186704
|
}
|
|
186617
186705
|
|
|
186618
186706
|
// src/judge/openai.ts
|
|
186707
|
+
var DEFAULT_OPENAI_JUDGE_MODEL = "gpt-5.4-mini";
|
|
186708
|
+
function resolveOpenAiJudgeModel(env) {
|
|
186709
|
+
return env.KYOSO_OPENAI_JUDGE_MODEL ?? DEFAULT_OPENAI_JUDGE_MODEL;
|
|
186710
|
+
}
|
|
186619
186711
|
async function runOpenAiJudge(input2, timeoutMs) {
|
|
186620
186712
|
const apiKey = input2.env.OPENAI_API_KEY ?? input2.env.CODEX_API_KEY;
|
|
186621
186713
|
if (!apiKey)
|
|
186622
186714
|
throw new Error("OPENAI_API_KEY is not configured.");
|
|
186715
|
+
const requestedModel = resolveOpenAiJudgeModel(input2.env);
|
|
186623
186716
|
const response = await fetchWithTimeout2(`${input2.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1"}/chat/completions`, {
|
|
186624
186717
|
method: "POST",
|
|
186625
186718
|
headers: {
|
|
@@ -186627,7 +186720,7 @@ async function runOpenAiJudge(input2, timeoutMs) {
|
|
|
186627
186720
|
"content-type": "application/json"
|
|
186628
186721
|
},
|
|
186629
186722
|
body: JSON.stringify({
|
|
186630
|
-
model:
|
|
186723
|
+
model: requestedModel,
|
|
186631
186724
|
response_format: { type: "json_object" },
|
|
186632
186725
|
messages: [
|
|
186633
186726
|
{
|
|
@@ -186646,8 +186739,11 @@ async function runOpenAiJudge(input2, timeoutMs) {
|
|
|
186646
186739
|
if (!content)
|
|
186647
186740
|
throw new Error("OpenAI judge response did not include content.");
|
|
186648
186741
|
const usage = normalizeUsage2(payload.usage);
|
|
186742
|
+
const reportedModel = typeof payload.model === "string" ? payload.model : undefined;
|
|
186649
186743
|
return {
|
|
186650
186744
|
output: parseJudgeOutput(content, input2.summaryText),
|
|
186745
|
+
requestedModel,
|
|
186746
|
+
...reportedModel ? { reportedModel } : {},
|
|
186651
186747
|
...usage ? { usage } : {}
|
|
186652
186748
|
};
|
|
186653
186749
|
}
|
|
@@ -186687,26 +186783,41 @@ function resolveJudgeProvider(provider, env) {
|
|
|
186687
186783
|
return "anthropic";
|
|
186688
186784
|
return "deterministic_fallback";
|
|
186689
186785
|
}
|
|
186786
|
+
function resolveJudgeCallRoute(mode, provider, env) {
|
|
186787
|
+
const resolvedProvider = resolveJudgeProvider(provider, env);
|
|
186788
|
+
const credentialAvailable = resolvedProvider === "openai" && (hasEnv(env, "OPENAI_API_KEY") || hasEnv(env, "CODEX_API_KEY")) || resolvedProvider === "anthropic" && hasEnv(env, "ANTHROPIC_API_KEY");
|
|
186789
|
+
return {
|
|
186790
|
+
provider: resolvedProvider,
|
|
186791
|
+
llmAvailable: mode === "deterministic_plus_llm" && credentialAvailable
|
|
186792
|
+
};
|
|
186793
|
+
}
|
|
186690
186794
|
async function runJudge(input2) {
|
|
186691
186795
|
const fallback = runDeterministicJudge(input2.result, input2.summaryText);
|
|
186692
|
-
|
|
186796
|
+
const configuredProvider = input2.requestedProvider ?? input2.config.provider;
|
|
186797
|
+
const route = resolveJudgeCallRoute(input2.config.mode, configuredProvider, input2.env);
|
|
186798
|
+
if (!route.llmAvailable) {
|
|
186693
186799
|
return {
|
|
186694
186800
|
provider: "deterministic_fallback",
|
|
186695
186801
|
status: "deterministic_fallback",
|
|
186696
186802
|
output: fallback
|
|
186697
186803
|
};
|
|
186698
186804
|
}
|
|
186699
|
-
const
|
|
186700
|
-
const
|
|
186701
|
-
|
|
186702
|
-
|
|
186703
|
-
}
|
|
186805
|
+
const provider = route.provider;
|
|
186806
|
+
const requestExecutionIdentity = createModelExecutionIdentity({
|
|
186807
|
+
providerRoute: provider === "openai" ? "openai" : "anthropic",
|
|
186808
|
+
requestedModel: provider === "openai" ? resolveOpenAiJudgeModel(input2.env) : resolveAnthropicJudgeModel(input2.env)
|
|
186809
|
+
});
|
|
186704
186810
|
try {
|
|
186705
186811
|
const output2 = provider === "openai" ? await runOpenAiJudge(input2, input2.timeoutMs ?? input2.config.timeoutMs) : await runAnthropicJudge(input2, input2.timeoutMs ?? input2.config.timeoutMs);
|
|
186706
186812
|
return {
|
|
186707
186813
|
provider,
|
|
186708
186814
|
status: "completed",
|
|
186709
186815
|
output: output2.output,
|
|
186816
|
+
executionIdentity: createModelExecutionIdentity({
|
|
186817
|
+
providerRoute: requestExecutionIdentity.providerRoute,
|
|
186818
|
+
requestedModel: output2.requestedModel,
|
|
186819
|
+
reportedModel: output2.reportedModel
|
|
186820
|
+
}),
|
|
186710
186821
|
...output2.usage ? { usage: output2.usage } : {}
|
|
186711
186822
|
};
|
|
186712
186823
|
} catch (error51) {
|
|
@@ -186714,6 +186825,7 @@ async function runJudge(input2) {
|
|
|
186714
186825
|
provider,
|
|
186715
186826
|
status: "failed_fallback",
|
|
186716
186827
|
output: fallback,
|
|
186828
|
+
executionIdentity: requestExecutionIdentity,
|
|
186717
186829
|
error: error51 instanceof Error ? error51.message : String(error51)
|
|
186718
186830
|
};
|
|
186719
186831
|
}
|
|
@@ -186771,7 +186883,19 @@ class ChildEnvPreflightError extends Error {
|
|
|
186771
186883
|
this.name = "ChildEnvPreflightError";
|
|
186772
186884
|
}
|
|
186773
186885
|
}
|
|
186774
|
-
function
|
|
186886
|
+
function buildChildLaunchContext(parentEnv, whitelist, explicit, options) {
|
|
186887
|
+
const env = buildChildEnvironment(parentEnv, whitelist, explicit, options);
|
|
186888
|
+
const openRouterSelected = options.agent === "codex" && options.provider === CODEX_OPENROUTER_PROVIDER;
|
|
186889
|
+
const requestedModel = options.agent === "claude" ? env.ANTHROPIC_MODEL : readCodexRequestedModel(env.CODEX_CONFIG);
|
|
186890
|
+
return {
|
|
186891
|
+
env,
|
|
186892
|
+
executionIdentity: createModelExecutionIdentity({
|
|
186893
|
+
providerRoute: openRouterSelected ? "openrouter" : options.agent === "codex" ? "codex_default" : "claude_default",
|
|
186894
|
+
requestedModel
|
|
186895
|
+
})
|
|
186896
|
+
};
|
|
186897
|
+
}
|
|
186898
|
+
function buildChildEnvironment(parentEnv, whitelist, explicit, options = {}) {
|
|
186775
186899
|
if (!parentEnv.PATH) {
|
|
186776
186900
|
throw new Error("PATH is required to launch ACP child agents.");
|
|
186777
186901
|
}
|
|
@@ -186818,6 +186942,19 @@ function buildChildEnv(parentEnv, whitelist, explicit, options = {}) {
|
|
|
186818
186942
|
}
|
|
186819
186943
|
return env;
|
|
186820
186944
|
}
|
|
186945
|
+
function readCodexRequestedModel(value) {
|
|
186946
|
+
if (!value)
|
|
186947
|
+
return;
|
|
186948
|
+
try {
|
|
186949
|
+
const parsed = JSON.parse(value);
|
|
186950
|
+
if (!isPlainObject2(parsed) || typeof parsed.model !== "string") {
|
|
186951
|
+
return;
|
|
186952
|
+
}
|
|
186953
|
+
return parsed.model;
|
|
186954
|
+
} catch {
|
|
186955
|
+
return;
|
|
186956
|
+
}
|
|
186957
|
+
}
|
|
186821
186958
|
function canCopyEnvValue(key, value, onCredentialPlaceholderDiscarded) {
|
|
186822
186959
|
if (!isUnexpandedCredentialEnvValue(key, value))
|
|
186823
186960
|
return true;
|
|
@@ -186959,9 +187096,9 @@ var PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION = 1;
|
|
|
186959
187096
|
var MINIMUM_SUPPORTED_CODEX_VERSION = "0.144.0-alpha.4";
|
|
186960
187097
|
var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
186961
187098
|
distribution: {
|
|
186962
|
-
pluginVersion: "0.
|
|
187099
|
+
pluginVersion: "0.6.0",
|
|
186963
187100
|
mcpCommand: "npx",
|
|
186964
|
-
mcpPackagePin: "@kyo-so/cli@0.
|
|
187101
|
+
mcpPackagePin: "@kyo-so/cli@0.12.0"
|
|
186965
187102
|
},
|
|
186966
187103
|
marketplace: {
|
|
186967
187104
|
name: "kyoso",
|
|
@@ -186995,7 +187132,7 @@ var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
|
186995
187132
|
],
|
|
186996
187133
|
cwd: null,
|
|
186997
187134
|
startupTimeoutSec: 20,
|
|
186998
|
-
toolTimeoutSec:
|
|
187135
|
+
toolTimeoutSec: 2160
|
|
186999
187136
|
},
|
|
187000
187137
|
pluginOverride: {
|
|
187001
187138
|
enabled: false,
|
|
@@ -187012,7 +187149,7 @@ var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
|
187012
187149
|
],
|
|
187013
187150
|
cwd: null,
|
|
187014
187151
|
startupTimeoutSec: 20,
|
|
187015
|
-
toolTimeoutSec:
|
|
187152
|
+
toolTimeoutSec: 2160
|
|
187016
187153
|
},
|
|
187017
187154
|
manualOverride: {
|
|
187018
187155
|
enabled: true,
|
|
@@ -187206,7 +187343,7 @@ function parseJson(value) {
|
|
|
187206
187343
|
}
|
|
187207
187344
|
}
|
|
187208
187345
|
function parsePluginList(value) {
|
|
187209
|
-
if (!
|
|
187346
|
+
if (!isRecord7(value))
|
|
187210
187347
|
return;
|
|
187211
187348
|
const allowedKeys = new Set(PLUGIN_LIST_JSON_SCHEMA.collections);
|
|
187212
187349
|
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
@@ -187234,7 +187371,7 @@ function parsePluginEntries(value) {
|
|
|
187234
187371
|
return;
|
|
187235
187372
|
const entries = [];
|
|
187236
187373
|
for (const item of value) {
|
|
187237
|
-
if (!
|
|
187374
|
+
if (!isRecord7(item))
|
|
187238
187375
|
return;
|
|
187239
187376
|
if (typeof item.pluginId !== "string" || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean") {
|
|
187240
187377
|
return;
|
|
@@ -187260,7 +187397,7 @@ function parseMcpList(value) {
|
|
|
187260
187397
|
return;
|
|
187261
187398
|
const matches = [];
|
|
187262
187399
|
for (const item of value) {
|
|
187263
|
-
if (!
|
|
187400
|
+
if (!isRecord7(item) || typeof item.name !== "string")
|
|
187264
187401
|
return "unknown";
|
|
187265
187402
|
if (item.name !== "kyoso")
|
|
187266
187403
|
continue;
|
|
@@ -187342,7 +187479,7 @@ function comparePrerelease(left, right) {
|
|
|
187342
187479
|
}
|
|
187343
187480
|
return 0;
|
|
187344
187481
|
}
|
|
187345
|
-
function
|
|
187482
|
+
function isRecord7(value) {
|
|
187346
187483
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
187347
187484
|
}
|
|
187348
187485
|
|
|
@@ -187448,7 +187585,7 @@ function findKyosoPackage(executable) {
|
|
|
187448
187585
|
if (existsSync(packagePath)) {
|
|
187449
187586
|
try {
|
|
187450
187587
|
const parsed = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
187451
|
-
if (
|
|
187588
|
+
if (isRecord8(parsed) && parsed.name === "@kyo-so/cli" && typeof parsed.version === "string" && parsed.version.trim().length > 0) {
|
|
187452
187589
|
return { directory, version: parsed.version };
|
|
187453
187590
|
}
|
|
187454
187591
|
} catch {}
|
|
@@ -187534,7 +187671,7 @@ function isWithin(path, parent) {
|
|
|
187534
187671
|
const relativePath = relative(resolve5(parent), resolve5(path));
|
|
187535
187672
|
return relativePath === "" || !relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && relativePath !== ".." && !isAbsolute4(relativePath);
|
|
187536
187673
|
}
|
|
187537
|
-
function
|
|
187674
|
+
function isRecord8(value) {
|
|
187538
187675
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
187539
187676
|
}
|
|
187540
187677
|
|
|
@@ -188165,7 +188302,7 @@ function buildCodexMcpToml(command, withOpenRouter = false) {
|
|
|
188165
188302
|
`args = ${JSON.stringify(command.args)}`,
|
|
188166
188303
|
`env_vars = [${envVars.map((value) => JSON.stringify(value)).join(", ")}]`,
|
|
188167
188304
|
"startup_timeout_sec = 20",
|
|
188168
|
-
"tool_timeout_sec =
|
|
188305
|
+
"tool_timeout_sec = 2160",
|
|
188169
188306
|
"enabled = true",
|
|
188170
188307
|
""
|
|
188171
188308
|
].join(`
|
|
@@ -188331,7 +188468,7 @@ async function ensureClaudeMcp(context) {
|
|
|
188331
188468
|
const configPath = join6(context.cwd, ".mcp.json");
|
|
188332
188469
|
const current = await readJsonObject(configPath);
|
|
188333
188470
|
const mcpServers = recordValue(current.mcpServers);
|
|
188334
|
-
if (
|
|
188471
|
+
if (isRecord9(mcpServers.kyoso)) {
|
|
188335
188472
|
return {
|
|
188336
188473
|
kind: "mcp",
|
|
188337
188474
|
registration: "preserved",
|
|
@@ -188571,7 +188708,7 @@ async function readJsonObject(path) {
|
|
|
188571
188708
|
if (content.trim().length === 0)
|
|
188572
188709
|
return {};
|
|
188573
188710
|
const parsed = JSON.parse(content);
|
|
188574
|
-
if (!
|
|
188711
|
+
if (!isRecord9(parsed))
|
|
188575
188712
|
throw new Error(`${path} must contain a JSON object`);
|
|
188576
188713
|
return parsed;
|
|
188577
188714
|
}
|
|
@@ -188581,9 +188718,9 @@ function hasCodexMcpContent(content) {
|
|
|
188581
188718
|
function codexMcpStatusFromContent(content) {
|
|
188582
188719
|
try {
|
|
188583
188720
|
const parsed = parse5(content);
|
|
188584
|
-
if (!
|
|
188721
|
+
if (!isRecord9(parsed))
|
|
188585
188722
|
return "unknown";
|
|
188586
|
-
if (!
|
|
188723
|
+
if (!isRecord9(parsed.mcp_servers))
|
|
188587
188724
|
return "missing";
|
|
188588
188725
|
if (!("kyoso" in parsed.mcp_servers))
|
|
188589
188726
|
return "missing";
|
|
@@ -188612,11 +188749,11 @@ function detectCodexMcp(path, cwd, home) {
|
|
|
188612
188749
|
if (hasUnprobedProjectIntegrationOverride(parsed, cwd, home)) {
|
|
188613
188750
|
return { status: "unknown", paths: [path] };
|
|
188614
188751
|
}
|
|
188615
|
-
if (!
|
|
188752
|
+
if (!isRecord9(parsed))
|
|
188616
188753
|
return { status: "unknown", paths: [path] };
|
|
188617
188754
|
if (!("mcp_servers" in parsed))
|
|
188618
188755
|
return { status: "missing", paths: [] };
|
|
188619
|
-
if (!
|
|
188756
|
+
if (!isRecord9(parsed.mcp_servers)) {
|
|
188620
188757
|
return { status: "unknown", paths: [path] };
|
|
188621
188758
|
}
|
|
188622
188759
|
if (!("kyoso" in parsed.mcp_servers)) {
|
|
@@ -188641,18 +188778,18 @@ function detectClaudeMcp(path, cwd, home) {
|
|
|
188641
188778
|
}
|
|
188642
188779
|
}
|
|
188643
188780
|
function jsonMcpStatuses(value, cwd, home) {
|
|
188644
|
-
if (!
|
|
188781
|
+
if (!isRecord9(value))
|
|
188645
188782
|
return ["unknown"];
|
|
188646
188783
|
const statuses = directMcpStatuses(value);
|
|
188647
188784
|
if (!("projects" in value))
|
|
188648
188785
|
return statuses;
|
|
188649
|
-
if (!
|
|
188786
|
+
if (!isRecord9(value.projects))
|
|
188650
188787
|
return [...statuses, "unknown"];
|
|
188651
188788
|
const currentProject = normalizeProjectPath(cwd, home);
|
|
188652
188789
|
for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
|
|
188653
188790
|
if (normalizeProjectPath(projectPath, home) !== currentProject)
|
|
188654
188791
|
continue;
|
|
188655
|
-
if (!
|
|
188792
|
+
if (!isRecord9(projectConfig)) {
|
|
188656
188793
|
statuses.push("unknown");
|
|
188657
188794
|
continue;
|
|
188658
188795
|
}
|
|
@@ -188663,7 +188800,7 @@ function jsonMcpStatuses(value, cwd, home) {
|
|
|
188663
188800
|
function directMcpStatuses(value) {
|
|
188664
188801
|
const statuses = [];
|
|
188665
188802
|
if ("mcpServers" in value) {
|
|
188666
|
-
if (!
|
|
188803
|
+
if (!isRecord9(value.mcpServers)) {
|
|
188667
188804
|
statuses.push("unknown");
|
|
188668
188805
|
} else if ("kyoso" in value.mcpServers) {
|
|
188669
188806
|
statuses.push(mcpEntryStatus(value.mcpServers.kyoso));
|
|
@@ -188674,7 +188811,7 @@ function directMcpStatuses(value) {
|
|
|
188674
188811
|
function nestedMcpEntryStatus(value, path) {
|
|
188675
188812
|
let current = value;
|
|
188676
188813
|
for (const key of path) {
|
|
188677
|
-
if (!
|
|
188814
|
+
if (!isRecord9(current))
|
|
188678
188815
|
return "unknown";
|
|
188679
188816
|
if (!(key in current))
|
|
188680
188817
|
return "missing";
|
|
@@ -188683,11 +188820,11 @@ function nestedMcpEntryStatus(value, path) {
|
|
|
188683
188820
|
return mcpEntryStatus(current);
|
|
188684
188821
|
}
|
|
188685
188822
|
function hasUnprobedProjectIntegrationOverride(value, cwd, home) {
|
|
188686
|
-
if (!
|
|
188823
|
+
if (!isRecord9(value) || !isRecord9(value.projects))
|
|
188687
188824
|
return false;
|
|
188688
188825
|
const currentProject = normalizeProjectPath(cwd, home);
|
|
188689
188826
|
for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
|
|
188690
|
-
if (normalizeProjectPath(projectPath, home) !== currentProject || !
|
|
188827
|
+
if (normalizeProjectPath(projectPath, home) !== currentProject || !isRecord9(projectConfig)) {
|
|
188691
188828
|
continue;
|
|
188692
188829
|
}
|
|
188693
188830
|
if ("mcp_servers" in projectConfig || "plugins" in projectConfig) {
|
|
@@ -188710,7 +188847,7 @@ function normalizeProjectPath(path, home) {
|
|
|
188710
188847
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
188711
188848
|
}
|
|
188712
188849
|
function mcpEntryStatus(value) {
|
|
188713
|
-
if (!
|
|
188850
|
+
if (!isRecord9(value))
|
|
188714
188851
|
return "unknown";
|
|
188715
188852
|
if (!("enabled" in value))
|
|
188716
188853
|
return "enabled";
|
|
@@ -188744,7 +188881,7 @@ function readTextSync(path) {
|
|
|
188744
188881
|
function recordValue(value) {
|
|
188745
188882
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
188746
188883
|
}
|
|
188747
|
-
function
|
|
188884
|
+
function isRecord9(value) {
|
|
188748
188885
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
188749
188886
|
}
|
|
188750
188887
|
function diffForAppend(path, snippet) {
|
|
@@ -188934,7 +189071,16 @@ async function runDoctor(options) {
|
|
|
188934
189071
|
const remaining = agentCommandExists.codex ? "codex" : "claude";
|
|
188935
189072
|
lines.push(` single-agent mode: set agents.${missing}.enabled: false to use ${remaining} only; the remaining agent will cover both review roles.`);
|
|
188936
189073
|
}
|
|
188937
|
-
const
|
|
189074
|
+
const judgeRoute = resolveJudgeCallRoute(loaded.config.judge.mode, loaded.config.judge.provider, env);
|
|
189075
|
+
const reviewTiming = calculateReviewTiming(loaded.config, judgeRoute.llmAvailable);
|
|
189076
|
+
lines.push("", "Review timing");
|
|
189077
|
+
lines.push(` review-wide deadline: ${loaded.config.reviewBudget.maxTotalWallTimeMs} ms`, ` sequential phases: primary ${reviewTiming.primaryPhaseMs} + verification ${reviewTiming.verificationPhaseMs} + LLM judge ${reviewTiming.judgePhaseMs} = ${reviewTiming.sequentialPhaseMs} ms`, ` recommended review-wide deadline: ${reviewTiming.recommendedReviewWallTimeMs} ms`);
|
|
189078
|
+
if (loaded.config.reviewBudget.maxTotalWallTimeMs < reviewTiming.sequentialPhaseMs) {
|
|
189079
|
+
lines.push(` warning: review-wide deadline is insufficient: ${loaded.config.reviewBudget.maxTotalWallTimeMs} ms is below the configured sequential phase time of ${reviewTiming.sequentialPhaseMs} ms; later phases cannot receive their configured timeout.`, ` hint: set user-global reviewBudget.maxTotalWallTimeMs to at least ${reviewTiming.recommendedReviewWallTimeMs}.`);
|
|
189080
|
+
} else if (loaded.config.reviewBudget.maxTotalWallTimeMs < reviewTiming.recommendedReviewWallTimeMs) {
|
|
189081
|
+
lines.push(` warning: review-wide deadline has low margin: ${loaded.config.reviewBudget.maxTotalWallTimeMs} ms is below the recommended ${reviewTiming.recommendedReviewWallTimeMs} ms; scheduling and finalization margin is reduced.`, ` hint: set user-global reviewBudget.maxTotalWallTimeMs to at least ${reviewTiming.recommendedReviewWallTimeMs}.`);
|
|
189082
|
+
}
|
|
189083
|
+
const judgeProvider = judgeRoute.llmAvailable ? judgeRoute.provider : "deterministic_fallback";
|
|
188938
189084
|
lines.push("", "Judge");
|
|
188939
189085
|
lines.push(` provider: ${judgeProvider}`);
|
|
188940
189086
|
lines.push(judgeProvider === "deterministic_fallback" ? " billing: none (deterministic fallback)" : " billing: direct provider API calls (pay-per-token billing)");
|
|
@@ -188955,6 +189101,20 @@ async function runDoctor(options) {
|
|
|
188955
189101
|
return lines.join(`
|
|
188956
189102
|
`);
|
|
188957
189103
|
}
|
|
189104
|
+
function calculateReviewTiming(config2, llmJudgeAvailable) {
|
|
189105
|
+
const primaryPhaseMs = Math.max(0, ...Object.values(config2.agents).filter((agent) => agent.enabled).map((agent) => agent.timeoutMs));
|
|
189106
|
+
const verificationPhaseMs = config2.verification.enabled ? config2.verification.timeoutMs : 0;
|
|
189107
|
+
const judgePhaseMs = llmJudgeAvailable ? config2.judge.timeoutMs : 0;
|
|
189108
|
+
const sequentialPhaseMs = primaryPhaseMs + verificationPhaseMs + judgePhaseMs;
|
|
189109
|
+
const recommendedReviewWallTimeMs = sequentialPhaseMs + Math.max(60000, Math.ceil(sequentialPhaseMs * 0.1));
|
|
189110
|
+
return {
|
|
189111
|
+
primaryPhaseMs,
|
|
189112
|
+
verificationPhaseMs,
|
|
189113
|
+
judgePhaseMs,
|
|
189114
|
+
sequentialPhaseMs,
|
|
189115
|
+
recommendedReviewWallTimeMs
|
|
189116
|
+
};
|
|
189117
|
+
}
|
|
188958
189118
|
async function loadDoctorConfig(options) {
|
|
188959
189119
|
try {
|
|
188960
189120
|
return { loaded: await loadConfig(options) };
|
|
@@ -191755,14 +191915,14 @@ var zGuardCreateElicitationResponseCancel = object({
|
|
|
191755
191915
|
});
|
|
191756
191916
|
// node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
|
|
191757
191917
|
var CANCEL_REQUEST_METHOD = "$/cancel_request";
|
|
191758
|
-
function
|
|
191918
|
+
function isRecord10(value) {
|
|
191759
191919
|
return typeof value === "object" && value !== null;
|
|
191760
191920
|
}
|
|
191761
191921
|
function isJsonRpcId(value) {
|
|
191762
191922
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
191763
191923
|
}
|
|
191764
191924
|
function cancelRequestId(params) {
|
|
191765
|
-
if (!
|
|
191925
|
+
if (!isRecord10(params) || !isJsonRpcId(params["requestId"])) {
|
|
191766
191926
|
return;
|
|
191767
191927
|
}
|
|
191768
191928
|
return params["requestId"];
|
|
@@ -192109,7 +192269,7 @@ class Connection {
|
|
|
192109
192269
|
if (this.abortController.signal.aborted) {
|
|
192110
192270
|
return;
|
|
192111
192271
|
}
|
|
192112
|
-
if (!
|
|
192272
|
+
if (!isRecord10(message)) {
|
|
192113
192273
|
console.error("Invalid message", { message });
|
|
192114
192274
|
return;
|
|
192115
192275
|
}
|
|
@@ -192202,7 +192362,7 @@ class Connection {
|
|
|
192202
192362
|
pendingResponse.cleanup?.();
|
|
192203
192363
|
if ("result" in response) {
|
|
192204
192364
|
pendingResponse.resolve(response.result);
|
|
192205
|
-
} else if ("error" in response &&
|
|
192365
|
+
} else if ("error" in response && isRecord10(response.error)) {
|
|
192206
192366
|
const { code, message, data } = response.error;
|
|
192207
192367
|
pendingResponse.reject(new RequestError(code, message, data));
|
|
192208
192368
|
} else {
|
|
@@ -192412,7 +192572,7 @@ function ndJsonStream(output2, input2) {
|
|
|
192412
192572
|
if (trimmedLine) {
|
|
192413
192573
|
try {
|
|
192414
192574
|
const message = JSON.parse(trimmedLine);
|
|
192415
|
-
if (
|
|
192575
|
+
if (isRecord10(message)) {
|
|
192416
192576
|
controller.enqueue(message);
|
|
192417
192577
|
} else {
|
|
192418
192578
|
console.warn("Skipping JSON line that is not an object:", trimmedLine);
|
|
@@ -193301,6 +193461,45 @@ class BaseAcpAgentManager {
|
|
|
193301
193461
|
}
|
|
193302
193462
|
}
|
|
193303
193463
|
|
|
193464
|
+
// src/acp/ndJsonLineLimit.ts
|
|
193465
|
+
var JSON_STRING_MAX_ESCAPE_EXPANSION = 6;
|
|
193466
|
+
var ACP_NDJSON_ENVELOPE_BYTES = 2 * 1048576;
|
|
193467
|
+
var NEWLINE_BYTE = 10;
|
|
193468
|
+
var MAX_ACP_NDJSON_LINE_BYTES = MAX_AGENT_OUTPUT_BYTES * JSON_STRING_MAX_ESCAPE_EXPANSION + ACP_NDJSON_ENVELOPE_BYTES;
|
|
193469
|
+
|
|
193470
|
+
class AcpNdJsonLineLimitError extends Error {
|
|
193471
|
+
maxLineBytes;
|
|
193472
|
+
constructor(maxLineBytes) {
|
|
193473
|
+
super(`ACP NDJSON line exceeded ${maxLineBytes} bytes.`);
|
|
193474
|
+
this.maxLineBytes = maxLineBytes;
|
|
193475
|
+
this.name = "AcpNdJsonLineLimitError";
|
|
193476
|
+
}
|
|
193477
|
+
}
|
|
193478
|
+
function limitAcpNdJsonLineBytes(input2, maxLineBytes = MAX_ACP_NDJSON_LINE_BYTES) {
|
|
193479
|
+
if (!Number.isSafeInteger(maxLineBytes) || maxLineBytes <= 0) {
|
|
193480
|
+
throw new RangeError("ACP NDJSON line limit must be a positive integer.");
|
|
193481
|
+
}
|
|
193482
|
+
let pendingLineBytes = 0;
|
|
193483
|
+
return input2.pipeThrough(new TransformStream({
|
|
193484
|
+
transform(chunk, controller) {
|
|
193485
|
+
let start = 0;
|
|
193486
|
+
for (;; ) {
|
|
193487
|
+
const newlineIndex = chunk.indexOf(NEWLINE_BYTE, start);
|
|
193488
|
+
const end = newlineIndex === -1 ? chunk.byteLength : newlineIndex;
|
|
193489
|
+
pendingLineBytes += end - start;
|
|
193490
|
+
if (pendingLineBytes > maxLineBytes) {
|
|
193491
|
+
throw new AcpNdJsonLineLimitError(maxLineBytes);
|
|
193492
|
+
}
|
|
193493
|
+
if (newlineIndex === -1)
|
|
193494
|
+
break;
|
|
193495
|
+
pendingLineBytes = 0;
|
|
193496
|
+
start = newlineIndex + 1;
|
|
193497
|
+
}
|
|
193498
|
+
controller.enqueue(chunk);
|
|
193499
|
+
}
|
|
193500
|
+
}));
|
|
193501
|
+
}
|
|
193502
|
+
|
|
193304
193503
|
// src/core/findingAdmission.ts
|
|
193305
193504
|
import { createHash as createHash4 } from "node:crypto";
|
|
193306
193505
|
var SAFETY_CATEGORIES = new Set([
|
|
@@ -193642,6 +193841,43 @@ var evidenceQualities = [
|
|
|
193642
193841
|
];
|
|
193643
193842
|
var MAX_EVIDENCE_REFS2 = 20;
|
|
193644
193843
|
var MAX_EVIDENCE_LINE2 = 1e6;
|
|
193844
|
+
var STRICT_ROOT_KEYS = new Set([
|
|
193845
|
+
"summary",
|
|
193846
|
+
"findings",
|
|
193847
|
+
"testsToAdd",
|
|
193848
|
+
"residualRisks",
|
|
193849
|
+
"openQuestions",
|
|
193850
|
+
"cisaSecureByDesign"
|
|
193851
|
+
]);
|
|
193852
|
+
var STRICT_FINDING_KEYS = new Set([
|
|
193853
|
+
"severity",
|
|
193854
|
+
"category",
|
|
193855
|
+
"title",
|
|
193856
|
+
"evidence",
|
|
193857
|
+
"recommendation",
|
|
193858
|
+
"disposition",
|
|
193859
|
+
"changeRelation",
|
|
193860
|
+
"evidenceQuality",
|
|
193861
|
+
"evidenceRefs",
|
|
193862
|
+
"files",
|
|
193863
|
+
"confidence",
|
|
193864
|
+
"cisaMapping"
|
|
193865
|
+
]);
|
|
193866
|
+
var STRICT_FILE_KEYS = new Set(["path", "lineStart", "lineEnd"]);
|
|
193867
|
+
var STRICT_EVIDENCE_REF_KEYS = new Set([
|
|
193868
|
+
"kind",
|
|
193869
|
+
"path",
|
|
193870
|
+
"lineStart",
|
|
193871
|
+
"lineEnd",
|
|
193872
|
+
"label"
|
|
193873
|
+
]);
|
|
193874
|
+
var STRICT_CISA_KEYS = new Set([
|
|
193875
|
+
"customerSecurityOutcomes",
|
|
193876
|
+
"secureByDefault",
|
|
193877
|
+
"transparencyAndAccountability",
|
|
193878
|
+
"governance",
|
|
193879
|
+
"notes"
|
|
193880
|
+
]);
|
|
193645
193881
|
function normalizeAgentOutput(agent, role, rawText) {
|
|
193646
193882
|
const json2 = extractFirstJsonObject2(rawText);
|
|
193647
193883
|
if (!json2)
|
|
@@ -193675,6 +193911,19 @@ function normalizeAgentOutput(agent, role, rawText) {
|
|
|
193675
193911
|
return parseFailureOpinion(agent, role, `Structured parse failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
193676
193912
|
}
|
|
193677
193913
|
}
|
|
193914
|
+
function parseAgentOutputStrict(agent, role, rawText) {
|
|
193915
|
+
const json2 = extractFirstJsonObject2(rawText);
|
|
193916
|
+
if (!json2)
|
|
193917
|
+
return;
|
|
193918
|
+
try {
|
|
193919
|
+
const parsed = JSON.parse(json2);
|
|
193920
|
+
if (!isStrictAgentOpinion(parsed))
|
|
193921
|
+
return;
|
|
193922
|
+
return normalizeAgentOutput(agent, role, json2);
|
|
193923
|
+
} catch {
|
|
193924
|
+
return;
|
|
193925
|
+
}
|
|
193926
|
+
}
|
|
193678
193927
|
function extractFirstJsonObject2(text) {
|
|
193679
193928
|
const start = text.indexOf("{");
|
|
193680
193929
|
if (start === -1)
|
|
@@ -193732,7 +193981,7 @@ function isSeverity(value) {
|
|
|
193732
193981
|
return typeof value === "string" && severities.includes(value);
|
|
193733
193982
|
}
|
|
193734
193983
|
function normalizeCisaSecureByDesign(value) {
|
|
193735
|
-
if (!
|
|
193984
|
+
if (!isRecord11(value))
|
|
193736
193985
|
return;
|
|
193737
193986
|
const normalized = {};
|
|
193738
193987
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -193772,6 +194021,78 @@ function isChangeRelation(value) {
|
|
|
193772
194021
|
function isEvidenceQuality(value) {
|
|
193773
194022
|
return typeof value === "string" && evidenceQualities.includes(value);
|
|
193774
194023
|
}
|
|
194024
|
+
function isStrictAgentOpinion(value) {
|
|
194025
|
+
if (!isRecord11(value) || !hasOnlyKeys(value, STRICT_ROOT_KEYS))
|
|
194026
|
+
return false;
|
|
194027
|
+
if (typeof value.summary !== "string")
|
|
194028
|
+
return false;
|
|
194029
|
+
if (!Array.isArray(value.findings) || !value.findings.every(isStrictFinding) || !isStringArray(value.testsToAdd) || !isStringArray(value.residualRisks) || !isStringArray(value.openQuestions)) {
|
|
194030
|
+
return false;
|
|
194031
|
+
}
|
|
194032
|
+
return value.cisaSecureByDesign === undefined || isStrictCisaSecureByDesign(value.cisaSecureByDesign);
|
|
194033
|
+
}
|
|
194034
|
+
function isStrictFinding(value) {
|
|
194035
|
+
if (!isRecord11(value) || !hasOnlyKeys(value, STRICT_FINDING_KEYS)) {
|
|
194036
|
+
return false;
|
|
194037
|
+
}
|
|
194038
|
+
if (!isSeverity(value.severity) || !isCategory(value.category) || !isNonEmptyString(value.title) || !isNonEmptyString(value.evidence) || !isNonEmptyString(value.recommendation) || !isConfidence(value.confidence)) {
|
|
194039
|
+
return false;
|
|
194040
|
+
}
|
|
194041
|
+
if (value.disposition !== undefined && !isDisposition(value.disposition) || value.changeRelation !== undefined && !isChangeRelation(value.changeRelation) || value.evidenceQuality !== undefined && !isEvidenceQuality(value.evidenceQuality) || value.files !== undefined && !isStrictFindingFiles(value.files) || value.evidenceRefs !== undefined && !isStrictEvidenceRefs(value.evidenceRefs) || value.cisaMapping !== undefined && !isStringArray(value.cisaMapping)) {
|
|
194042
|
+
return false;
|
|
194043
|
+
}
|
|
194044
|
+
return true;
|
|
194045
|
+
}
|
|
194046
|
+
function isStrictFindingFiles(value) {
|
|
194047
|
+
return Array.isArray(value) && value.every((item) => isRecord11(item) && hasOnlyKeys(item, STRICT_FILE_KEYS) && isNonEmptyString(item.path) && isOptionalLineNumber(item.lineStart) && isOptionalLineNumber(item.lineEnd));
|
|
194048
|
+
}
|
|
194049
|
+
function isStrictEvidenceRefs(value) {
|
|
194050
|
+
return Array.isArray(value) && value.length <= MAX_EVIDENCE_REFS2 && value.every((item) => {
|
|
194051
|
+
if (!isRecord11(item) || !hasOnlyKeys(item, STRICT_EVIDENCE_REF_KEYS)) {
|
|
194052
|
+
return false;
|
|
194053
|
+
}
|
|
194054
|
+
if (item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
194055
|
+
return false;
|
|
194056
|
+
}
|
|
194057
|
+
if (!isOptionalNonEmptyString(item.path) || !isOptionalNonEmptyString(item.label) || !isOptionalLineNumber(item.lineStart) || !isOptionalLineNumber(item.lineEnd)) {
|
|
194058
|
+
return false;
|
|
194059
|
+
}
|
|
194060
|
+
if (item.kind === "file" || item.kind === "diff_hunk") {
|
|
194061
|
+
return item.path !== undefined && item.lineStart !== undefined;
|
|
194062
|
+
}
|
|
194063
|
+
return item.label !== undefined || item.lineStart !== undefined;
|
|
194064
|
+
});
|
|
194065
|
+
}
|
|
194066
|
+
function isStrictCisaSecureByDesign(value) {
|
|
194067
|
+
if (!isRecord11(value) || !hasOnlyKeys(value, STRICT_CISA_KEYS))
|
|
194068
|
+
return false;
|
|
194069
|
+
for (const key of [
|
|
194070
|
+
"customerSecurityOutcomes",
|
|
194071
|
+
"secureByDefault",
|
|
194072
|
+
"transparencyAndAccountability",
|
|
194073
|
+
"governance"
|
|
194074
|
+
]) {
|
|
194075
|
+
if (value[key] !== undefined && !normalizeGateStatus(value[key])) {
|
|
194076
|
+
return false;
|
|
194077
|
+
}
|
|
194078
|
+
}
|
|
194079
|
+
return value.notes === undefined || isStringArray(value.notes);
|
|
194080
|
+
}
|
|
194081
|
+
function hasOnlyKeys(value, allowed) {
|
|
194082
|
+
return Object.keys(value).every((key) => allowed.has(key));
|
|
194083
|
+
}
|
|
194084
|
+
function isStringArray(value) {
|
|
194085
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
194086
|
+
}
|
|
194087
|
+
function isNonEmptyString(value) {
|
|
194088
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
194089
|
+
}
|
|
194090
|
+
function isOptionalNonEmptyString(value) {
|
|
194091
|
+
return value === undefined || isNonEmptyString(value);
|
|
194092
|
+
}
|
|
194093
|
+
function isOptionalLineNumber(value) {
|
|
194094
|
+
return value === undefined || normalizeLineNumber(value) !== undefined;
|
|
194095
|
+
}
|
|
193775
194096
|
function normalizeStringList(value) {
|
|
193776
194097
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string").map((item) => sanitizeText(item)) : [];
|
|
193777
194098
|
}
|
|
@@ -193782,7 +194103,7 @@ function normalizeFindingFiles(value) {
|
|
|
193782
194103
|
if (!Array.isArray(value))
|
|
193783
194104
|
return;
|
|
193784
194105
|
const files = value.flatMap((item) => {
|
|
193785
|
-
if (!
|
|
194106
|
+
if (!isRecord11(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
193786
194107
|
return [];
|
|
193787
194108
|
}
|
|
193788
194109
|
const file2 = {
|
|
@@ -193802,7 +194123,7 @@ function normalizeEvidenceRefs2(value) {
|
|
|
193802
194123
|
if (!Array.isArray(value))
|
|
193803
194124
|
return;
|
|
193804
194125
|
const references = value.slice(0, MAX_EVIDENCE_REFS2).flatMap((item) => {
|
|
193805
|
-
if (!
|
|
194126
|
+
if (!isRecord11(item) || item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
193806
194127
|
return [];
|
|
193807
194128
|
}
|
|
193808
194129
|
const reference = { kind: item.kind };
|
|
@@ -193825,7 +194146,7 @@ function normalizeEvidenceRefs2(value) {
|
|
|
193825
194146
|
function normalizeLineNumber(value) {
|
|
193826
194147
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= MAX_EVIDENCE_LINE2 ? value : undefined;
|
|
193827
194148
|
}
|
|
193828
|
-
function
|
|
194149
|
+
function isRecord11(value) {
|
|
193829
194150
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193830
194151
|
}
|
|
193831
194152
|
|
|
@@ -193851,9 +194172,9 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
193851
194172
|
};
|
|
193852
194173
|
}
|
|
193853
194174
|
const provider = input2.agent === "codex" ? this.config.agents.codex.provider : undefined;
|
|
193854
|
-
let
|
|
194175
|
+
let launchContext;
|
|
193855
194176
|
try {
|
|
193856
|
-
|
|
194177
|
+
launchContext = buildChildLaunchContext(this.parentEnv, agentConfig.auth.envWhitelist, agentConfig.env, {
|
|
193857
194178
|
agent: input2.agent,
|
|
193858
194179
|
model: agentConfig.model,
|
|
193859
194180
|
provider,
|
|
@@ -193870,7 +194191,7 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
193870
194191
|
};
|
|
193871
194192
|
}
|
|
193872
194193
|
try {
|
|
193873
|
-
return await runSubprocessAgent(input2.agent, agentConfig, input2, env);
|
|
194194
|
+
return await runSubprocessAgent(input2.agent, agentConfig, input2, launchContext.env, launchContext.executionIdentity);
|
|
193874
194195
|
} catch (error51) {
|
|
193875
194196
|
return {
|
|
193876
194197
|
agent: input2.agent,
|
|
@@ -193883,7 +194204,7 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
193883
194204
|
}
|
|
193884
194205
|
}
|
|
193885
194206
|
}
|
|
193886
|
-
async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
194207
|
+
async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecutionIdentity) {
|
|
193887
194208
|
const startedAt = new Date().toISOString();
|
|
193888
194209
|
const effectiveTimeoutMs = resolveEffectiveTimeoutMs(input2);
|
|
193889
194210
|
if (effectiveTimeoutMs <= 0) {
|
|
@@ -193908,11 +194229,15 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193908
194229
|
let stdout = "";
|
|
193909
194230
|
let stderr3 = "";
|
|
193910
194231
|
let settled = false;
|
|
194232
|
+
let spawned = false;
|
|
193911
194233
|
let startedWrite;
|
|
193912
194234
|
child.once("spawn", () => {
|
|
193913
194235
|
if (settled)
|
|
193914
194236
|
return;
|
|
193915
|
-
|
|
194237
|
+
spawned = true;
|
|
194238
|
+
startedWrite = Promise.resolve().then(async () => {
|
|
194239
|
+
await input2.onStarted?.(launchExecutionIdentity);
|
|
194240
|
+
}).catch(() => {
|
|
193916
194241
|
return;
|
|
193917
194242
|
});
|
|
193918
194243
|
});
|
|
@@ -193922,7 +194247,8 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193922
194247
|
return;
|
|
193923
194248
|
settled = true;
|
|
193924
194249
|
clearTimeout(timeout);
|
|
193925
|
-
|
|
194250
|
+
const finalResult = spawned && result.executionIdentity === undefined ? { ...result, executionIdentity: launchExecutionIdentity } : result;
|
|
194251
|
+
(startedWrite ?? Promise.resolve()).then(() => resolveResult(finalResult));
|
|
193926
194252
|
};
|
|
193927
194253
|
const timeout = setTimeout(() => {
|
|
193928
194254
|
abortController.abort(new Error("Kyoso agent timeout"));
|
|
@@ -193955,7 +194281,17 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193955
194281
|
error: failure
|
|
193956
194282
|
});
|
|
193957
194283
|
});
|
|
193958
|
-
runAcpClientWorkflow(child, input2, abortController, resolveEffortConfigOption(agent, agentConfig.effort)).then(({
|
|
194284
|
+
runAcpClientWorkflow(child, input2, abortController, resolveEffortConfigOption(agent, agentConfig.effort), launchExecutionIdentity).then(({
|
|
194285
|
+
rawText,
|
|
194286
|
+
warnings,
|
|
194287
|
+
usage,
|
|
194288
|
+
messageBytes,
|
|
194289
|
+
thoughtBytes,
|
|
194290
|
+
outputBytes,
|
|
194291
|
+
outputWarningTriggered,
|
|
194292
|
+
stopReason,
|
|
194293
|
+
executionIdentity
|
|
194294
|
+
}) => {
|
|
193959
194295
|
stdout = rawText;
|
|
193960
194296
|
const completed = stopReason === "end_turn";
|
|
193961
194297
|
resolveOnce({
|
|
@@ -193966,8 +194302,12 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193966
194302
|
normalized: normalizeAgentOutput(agent, input2.role, rawText),
|
|
193967
194303
|
startedAt,
|
|
193968
194304
|
completedAt: new Date().toISOString(),
|
|
194305
|
+
messageBytes,
|
|
194306
|
+
thoughtBytes,
|
|
193969
194307
|
outputBytes,
|
|
194308
|
+
outputWarningTriggered,
|
|
193970
194309
|
stopReason,
|
|
194310
|
+
executionIdentity,
|
|
193971
194311
|
...usage ? { usage } : {},
|
|
193972
194312
|
...warnings.length > 0 ? { warnings } : {},
|
|
193973
194313
|
...completed ? {} : {
|
|
@@ -193981,18 +194321,40 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
193981
194321
|
const outputLimitError = findOutputLimitError(error51, abortController);
|
|
193982
194322
|
if (outputLimitError) {
|
|
193983
194323
|
stdout = outputLimitError.rawText;
|
|
194324
|
+
const normalized = parseAgentOutputStrict(agent, input2.role, stdout);
|
|
193984
194325
|
resolveOnce({
|
|
193985
194326
|
agent,
|
|
193986
194327
|
role: input2.role,
|
|
193987
194328
|
status: "failed",
|
|
193988
194329
|
rawText: stdout,
|
|
194330
|
+
...normalized ? { normalized, salvaged: true } : {},
|
|
194331
|
+
messageBytes: outputLimitError.messageBytes,
|
|
194332
|
+
thoughtBytes: outputLimitError.thoughtBytes,
|
|
193989
194333
|
outputBytes: outputLimitError.outputBytes,
|
|
194334
|
+
outputWarningTriggered: outputLimitError.outputWarningTriggered,
|
|
193990
194335
|
stopReason: "cancelled",
|
|
193991
194336
|
startedAt,
|
|
193992
194337
|
completedAt: new Date().toISOString(),
|
|
193993
194338
|
error: {
|
|
193994
194339
|
code: "AGENT_OUTPUT_LIMIT",
|
|
193995
|
-
message: `Agent output exceeded ${outputLimitError.maxOutputBytes}
|
|
194340
|
+
message: `Agent output exceeded the ${outputLimitError.maxOutputBytes}-byte hard limit (message: ${outputLimitError.messageBytes}, thought: ${outputLimitError.thoughtBytes}, total: ${outputLimitError.outputBytes}) and was cancelled. Adjust user-global reviewBudget.maxAgentOutputBytes to change this ceiling.`
|
|
194341
|
+
}
|
|
194342
|
+
});
|
|
194343
|
+
return;
|
|
194344
|
+
}
|
|
194345
|
+
if (error51 instanceof AcpNdJsonLineLimitError) {
|
|
194346
|
+
abortController.abort(error51);
|
|
194347
|
+
resolveOnce({
|
|
194348
|
+
agent,
|
|
194349
|
+
role: input2.role,
|
|
194350
|
+
status: "failed",
|
|
194351
|
+
rawText: stdout,
|
|
194352
|
+
stopReason: "cancelled",
|
|
194353
|
+
startedAt,
|
|
194354
|
+
completedAt: new Date().toISOString(),
|
|
194355
|
+
error: {
|
|
194356
|
+
code: "AGENT_PROTOCOL_LIMIT",
|
|
194357
|
+
message: `Agent emitted an ACP NDJSON line above the ${error51.maxLineBytes}-byte transport limit and was cancelled.`
|
|
193996
194358
|
}
|
|
193997
194359
|
});
|
|
193998
194360
|
return;
|
|
@@ -194029,13 +194391,13 @@ async function runSubprocessAgent(agent, agentConfig, input2, env) {
|
|
|
194029
194391
|
});
|
|
194030
194392
|
});
|
|
194031
194393
|
}
|
|
194032
|
-
async function runAcpClientWorkflow(child, input2, abortController, configOption) {
|
|
194394
|
+
async function runAcpClientWorkflow(child, input2, abortController, configOption, launchExecutionIdentity) {
|
|
194033
194395
|
if (!child.stdin || !child.stdout) {
|
|
194034
194396
|
throw new Error("Agent process did not expose stdio streams.");
|
|
194035
194397
|
}
|
|
194036
194398
|
const output2 = Writable.toWeb(child.stdin);
|
|
194037
194399
|
const inputStream = Readable.toWeb(child.stdout);
|
|
194038
|
-
const stream2 = ndJsonStream(output2, inputStream);
|
|
194400
|
+
const stream2 = ndJsonStream(output2, limitAcpNdJsonLineBytes(inputStream));
|
|
194039
194401
|
const app = client({ name: "kyoso" }).onRequest(methods.client.session.requestPermission, () => ({
|
|
194040
194402
|
outcome: { outcome: "cancelled" }
|
|
194041
194403
|
})).onRequest(methods.client.fs.readTextFile, async (ctx) => ({
|
|
@@ -194096,7 +194458,10 @@ async function runAcpClientWorkflow(child, input2, abortController, configOption
|
|
|
194096
194458
|
return;
|
|
194097
194459
|
});
|
|
194098
194460
|
let rawText = "";
|
|
194461
|
+
let messageBytes = 0;
|
|
194462
|
+
let thoughtBytes = 0;
|
|
194099
194463
|
let outputBytes = 0;
|
|
194464
|
+
let outputWarningTriggered = false;
|
|
194100
194465
|
for (;; ) {
|
|
194101
194466
|
const message = await session.nextUpdate();
|
|
194102
194467
|
if (message.kind === "stop") {
|
|
@@ -194105,8 +194470,12 @@ async function runAcpClientWorkflow(child, input2, abortController, configOption
|
|
|
194105
194470
|
rawText,
|
|
194106
194471
|
warnings,
|
|
194107
194472
|
...usage ? { usage } : {},
|
|
194473
|
+
messageBytes,
|
|
194474
|
+
thoughtBytes,
|
|
194108
194475
|
outputBytes,
|
|
194109
|
-
|
|
194476
|
+
outputWarningTriggered,
|
|
194477
|
+
stopReason: message.stopReason,
|
|
194478
|
+
executionIdentity: withReportedExecutionIdentity(launchExecutionIdentity, message.response._meta)
|
|
194110
194479
|
};
|
|
194111
194480
|
}
|
|
194112
194481
|
const update = message.update;
|
|
@@ -194114,35 +194483,60 @@ async function runAcpClientWorkflow(child, input2, abortController, configOption
|
|
|
194114
194483
|
continue;
|
|
194115
194484
|
}
|
|
194116
194485
|
const chunkBytes = Buffer.byteLength(update.content.text, "utf8");
|
|
194117
|
-
const
|
|
194486
|
+
const isMessage = update.sessionUpdate === "agent_message_chunk";
|
|
194487
|
+
const nextMessageBytes = messageBytes + (isMessage ? chunkBytes : 0);
|
|
194488
|
+
const nextThoughtBytes = thoughtBytes + (isMessage ? 0 : chunkBytes);
|
|
194489
|
+
const nextOutputBytes = nextMessageBytes + nextThoughtBytes;
|
|
194490
|
+
const nextOutputWarningTriggered = outputWarningTriggered || input2.warnOutputBytes !== undefined && nextOutputBytes >= input2.warnOutputBytes;
|
|
194118
194491
|
if (input2.maxOutputBytes !== undefined && nextOutputBytes > input2.maxOutputBytes) {
|
|
194492
|
+
const retainedRawText = isMessage ? `${rawText}${utf8Prefix(update.content.text, input2.maxOutputBytes - outputBytes)}` : rawText;
|
|
194119
194493
|
await ctx.notify(methods.agent.session.cancel, {
|
|
194120
194494
|
sessionId: session.sessionId
|
|
194121
194495
|
}).catch(() => {
|
|
194122
194496
|
return;
|
|
194123
194497
|
});
|
|
194124
|
-
const error51 = new AgentOutputLimitError(
|
|
194498
|
+
const error51 = new AgentOutputLimitError(retainedRawText, nextMessageBytes, nextThoughtBytes, nextOutputBytes, input2.maxOutputBytes, nextOutputWarningTriggered);
|
|
194125
194499
|
abortController.abort(error51);
|
|
194126
194500
|
throw error51;
|
|
194127
194501
|
}
|
|
194128
|
-
if (
|
|
194502
|
+
if (isMessage) {
|
|
194129
194503
|
rawText += update.content.text;
|
|
194130
194504
|
}
|
|
194505
|
+
messageBytes = nextMessageBytes;
|
|
194506
|
+
thoughtBytes = nextThoughtBytes;
|
|
194131
194507
|
outputBytes = nextOutputBytes;
|
|
194508
|
+
outputWarningTriggered = nextOutputWarningTriggered;
|
|
194132
194509
|
}
|
|
194133
194510
|
});
|
|
194134
194511
|
});
|
|
194135
194512
|
}
|
|
194513
|
+
function utf8Prefix(input2, maxBytes) {
|
|
194514
|
+
const encoded = new TextEncoder().encode(input2);
|
|
194515
|
+
const budget = Math.max(0, Math.min(maxBytes, encoded.byteLength));
|
|
194516
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
194517
|
+
for (let end = budget;end > 0; end -= 1) {
|
|
194518
|
+
try {
|
|
194519
|
+
return decoder.decode(encoded.subarray(0, end));
|
|
194520
|
+
} catch {}
|
|
194521
|
+
}
|
|
194522
|
+
return "";
|
|
194523
|
+
}
|
|
194136
194524
|
|
|
194137
194525
|
class AgentOutputLimitError extends Error {
|
|
194138
194526
|
rawText;
|
|
194527
|
+
messageBytes;
|
|
194528
|
+
thoughtBytes;
|
|
194139
194529
|
outputBytes;
|
|
194140
194530
|
maxOutputBytes;
|
|
194141
|
-
|
|
194531
|
+
outputWarningTriggered;
|
|
194532
|
+
constructor(rawText, messageBytes, thoughtBytes, outputBytes, maxOutputBytes, outputWarningTriggered) {
|
|
194142
194533
|
super(`Agent output exceeded ${maxOutputBytes} bytes.`);
|
|
194143
194534
|
this.rawText = rawText;
|
|
194535
|
+
this.messageBytes = messageBytes;
|
|
194536
|
+
this.thoughtBytes = thoughtBytes;
|
|
194144
194537
|
this.outputBytes = outputBytes;
|
|
194145
194538
|
this.maxOutputBytes = maxOutputBytes;
|
|
194539
|
+
this.outputWarningTriggered = outputWarningTriggered;
|
|
194146
194540
|
this.name = "AgentOutputLimitError";
|
|
194147
194541
|
}
|
|
194148
194542
|
}
|
|
@@ -194159,6 +194553,18 @@ function resolveEffectiveTimeoutMs(input2) {
|
|
|
194159
194553
|
function normalizeUsage3(usage) {
|
|
194160
194554
|
return normalizeModelTokenUsage(usage);
|
|
194161
194555
|
}
|
|
194556
|
+
function withReportedExecutionIdentity(identity, metadata) {
|
|
194557
|
+
const record2 = isRecord12(metadata) ? metadata : {};
|
|
194558
|
+
return createModelExecutionIdentity({
|
|
194559
|
+
providerRoute: identity.providerRoute,
|
|
194560
|
+
requestedModel: identity.requestedModel,
|
|
194561
|
+
reportedProvider: record2.provider,
|
|
194562
|
+
reportedModel: record2.model
|
|
194563
|
+
});
|
|
194564
|
+
}
|
|
194565
|
+
function isRecord12(value) {
|
|
194566
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
194567
|
+
}
|
|
194162
194568
|
function resolveEffortConfigOption(agent, effort) {
|
|
194163
194569
|
if (!effort)
|
|
194164
194570
|
return;
|
|
@@ -208028,7 +208434,7 @@ function clearInheritedOpenRouterModelForProviderReset(baseConfig, overridden, o
|
|
|
208028
208434
|
return;
|
|
208029
208435
|
}
|
|
208030
208436
|
const codex = readPath2(overridden, ["agents", "codex"]);
|
|
208031
|
-
if (
|
|
208437
|
+
if (isRecord13(codex))
|
|
208032
208438
|
delete codex.model;
|
|
208033
208439
|
}
|
|
208034
208440
|
function findAssignmentForPath(overrides, path) {
|
|
@@ -208073,7 +208479,7 @@ function parseConfigOverrideValue(value, currentValue) {
|
|
|
208073
208479
|
function readPath2(target, path) {
|
|
208074
208480
|
let current = target;
|
|
208075
208481
|
for (const key of path) {
|
|
208076
|
-
if (!
|
|
208482
|
+
if (!isRecord13(current))
|
|
208077
208483
|
return;
|
|
208078
208484
|
current = current[key];
|
|
208079
208485
|
}
|
|
@@ -208083,7 +208489,7 @@ function writePath2(target, path, value) {
|
|
|
208083
208489
|
let current = target;
|
|
208084
208490
|
for (const key of path.slice(0, -1)) {
|
|
208085
208491
|
const child = current[key];
|
|
208086
|
-
if (!
|
|
208492
|
+
if (!isRecord13(child)) {
|
|
208087
208493
|
throw new Error(`Cannot apply --set key ${JSON.stringify(path.join("."))}.`);
|
|
208088
208494
|
}
|
|
208089
208495
|
current = child;
|
|
@@ -208092,7 +208498,7 @@ function writePath2(target, path, value) {
|
|
|
208092
208498
|
if (leaf)
|
|
208093
208499
|
current[leaf] = value;
|
|
208094
208500
|
}
|
|
208095
|
-
function
|
|
208501
|
+
function isRecord13(value) {
|
|
208096
208502
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
208097
208503
|
}
|
|
208098
208504
|
|
|
@@ -208261,6 +208667,9 @@ function buildAgentPrompt(tool, request, agent, role, policy = {}) {
|
|
|
208261
208667
|
"A formal finding requires a concrete file/line, diff hunk, or plan clause; an actual failure or exploit path; a change relation; and an executable recommendation.",
|
|
208262
208668
|
"Put insufficiently supported hypotheses in openQuestions instead of findings.",
|
|
208263
208669
|
"Do not create formal findings for style, formatting, generic hardening, unrelated pre-existing issues, duplicate tests, implementation-detail tests, or exhaustive boundary matrices.",
|
|
208670
|
+
...policy.maxFindingsTarget === undefined ? [] : [
|
|
208671
|
+
`Avoid duplicates and aim for at most ${policy.maxFindingsTarget} findings in severity order, but do not hide a material finding solely to meet this target.`
|
|
208672
|
+
],
|
|
208264
208673
|
"Recommend only specific regression scenarios tied to changed behavior, with at most three testsToAdd entries.",
|
|
208265
208674
|
"Critical and High safety issues must still be reported when they match a non-goal.",
|
|
208266
208675
|
"Write each finding title in concise English, regardless of the language used elsewhere. Titles are compared across agents for deduplication.",
|
|
@@ -208885,6 +209294,12 @@ function sanitizeForAudit(value, options = {}) {
|
|
|
208885
209294
|
if (typeof value === "object" && value !== null) {
|
|
208886
209295
|
const result = {};
|
|
208887
209296
|
for (const [key, nested] of Object.entries(value)) {
|
|
209297
|
+
if (key === "executionIdentity") {
|
|
209298
|
+
const identity = normalizeModelExecutionIdentity(nested);
|
|
209299
|
+
if (identity)
|
|
209300
|
+
result[key] = identity;
|
|
209301
|
+
continue;
|
|
209302
|
+
}
|
|
208888
209303
|
if (/raw|content|env|credential|token|secret|password/i.test(key) && !(options.includeRawAgentOutput && key === "rawText") && !isUsageMetadata(key, nested)) {
|
|
208889
209304
|
continue;
|
|
208890
209305
|
}
|
|
@@ -209165,7 +209580,7 @@ function validateReviewContract(request) {
|
|
|
209165
209580
|
const contract = request.reviewContract;
|
|
209166
209581
|
if (contract === undefined)
|
|
209167
209582
|
return;
|
|
209168
|
-
if (!
|
|
209583
|
+
if (!isRecord14(contract)) {
|
|
209169
209584
|
throw new KyosoRequestError("reviewContract must be an object", "VALIDATION_ERROR");
|
|
209170
209585
|
}
|
|
209171
209586
|
const allowedKeys = new Set(["focus", "nonGoals", "acceptedRisks"]);
|
|
@@ -209182,7 +209597,7 @@ function validateReviewContract(request) {
|
|
|
209182
209597
|
throw new KyosoRequestError("reviewContract.nonGoals must contain at most 20 non-empty strings of 500 characters or fewer", "VALIDATION_ERROR");
|
|
209183
209598
|
}
|
|
209184
209599
|
const acceptedRisks = contract.acceptedRisks;
|
|
209185
|
-
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !
|
|
209600
|
+
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !isRecord14(risk) || typeof risk.findingFingerprint !== "string" || !/^sha256:[0-9a-f]{64}$/.test(risk.findingFingerprint) || typeof risk.rationale !== "string" || risk.rationale.trim().length === 0 || risk.rationale.length > 500))) {
|
|
209186
209601
|
throw new KyosoRequestError("reviewContract.acceptedRisks must contain valid finding fingerprints and bounded rationales", "VALIDATION_ERROR");
|
|
209187
209602
|
}
|
|
209188
209603
|
}
|
|
@@ -209194,13 +209609,13 @@ function validateSelectedFiles(request) {
|
|
|
209194
209609
|
throw new KyosoRequestError("selectedFiles must be an array", "VALIDATION_ERROR");
|
|
209195
209610
|
}
|
|
209196
209611
|
for (const file2 of selectedFiles) {
|
|
209197
|
-
if (!
|
|
209612
|
+
if (!isRecord14(file2) || typeof file2.path !== "string" || file2.path.trim().length === 0 || typeof file2.content !== "string" || file2.language !== undefined && typeof file2.language !== "string" || file2.truncated !== undefined && typeof file2.truncated !== "boolean") {
|
|
209198
209613
|
throw new KyosoRequestError("selectedFiles entries require string path/content and valid optional metadata", "VALIDATION_ERROR");
|
|
209199
209614
|
}
|
|
209200
209615
|
normalizeRelativePath(file2.path);
|
|
209201
209616
|
}
|
|
209202
209617
|
}
|
|
209203
|
-
function
|
|
209618
|
+
function isRecord14(value) {
|
|
209204
209619
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
209205
209620
|
}
|
|
209206
209621
|
|
|
@@ -209213,7 +209628,7 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
209213
209628
|
`**Mode:** ${tool}`,
|
|
209214
209629
|
`**Completion:** ${formatCompletion(result)}`,
|
|
209215
209630
|
`**Request fingerprint:** ${shortFingerprint(result.requestFingerprint)}`,
|
|
209216
|
-
`**Agents:** ${result.agentOpinions.map((opinion) => `${title(opinion.agent)} ${opinion.status}`).join(", ")}`,
|
|
209631
|
+
`**Agents:** ${result.agentOpinions.map((opinion) => `${title(opinion.agent)} ${opinion.status}${opinion.salvaged ? " (salvaged)" : ""}`).join(", ")}`,
|
|
209217
209632
|
`**Review mode:** ${formatReviewMode(result)}`,
|
|
209218
209633
|
...result.verificationMode ? [
|
|
209219
209634
|
`**Verification mode:** ${formatVerificationMode(result.verificationMode)}`
|
|
@@ -209264,7 +209679,7 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
209264
209679
|
}
|
|
209265
209680
|
lines.push("", "## Agent Opinions", "");
|
|
209266
209681
|
for (const opinion of result.agentOpinions) {
|
|
209267
|
-
lines.push(`### ${title(opinion.agent)}`, "", `${opinion.summary} (${opinion.status})`, "");
|
|
209682
|
+
lines.push(`### ${title(opinion.agent)}`, "", `${opinion.summary} (${opinion.status}${opinion.salvaged ? ", salvaged" : ""})`, "");
|
|
209268
209683
|
}
|
|
209269
209684
|
lines.push("", "## Disagreements", "");
|
|
209270
209685
|
if (result.reviewMode === "single_agent") {
|
|
@@ -209295,17 +209710,50 @@ function defaultSummaryText(result) {
|
|
|
209295
209710
|
function formatExecutionBudget(result) {
|
|
209296
209711
|
const budget = result.executionBudget;
|
|
209297
209712
|
const agentOutputs = Object.entries(budget.agentOutputBytes);
|
|
209298
|
-
const
|
|
209713
|
+
const byteBreakdowns = new Map;
|
|
209714
|
+
for (const call of result.audit.modelCalls) {
|
|
209715
|
+
if (call.status !== "completed" || !call.agent)
|
|
209716
|
+
continue;
|
|
209717
|
+
const current = byteBreakdowns.get(call.agent) ?? {
|
|
209718
|
+
messageBytes: 0,
|
|
209719
|
+
thoughtBytes: 0
|
|
209720
|
+
};
|
|
209721
|
+
current.messageBytes += call.messageBytes ?? 0;
|
|
209722
|
+
current.thoughtBytes += call.thoughtBytes ?? 0;
|
|
209723
|
+
byteBreakdowns.set(call.agent, current);
|
|
209724
|
+
}
|
|
209725
|
+
const outputLines = agentOutputs.length > 0 ? agentOutputs.map(([agent, bytes]) => {
|
|
209726
|
+
const breakdown = byteBreakdowns.get(agent);
|
|
209727
|
+
return `- ${title(agent)}: ${bytes} bytes${breakdown ? ` (message: ${breakdown.messageBytes}, thought: ${breakdown.thoughtBytes})` : ""}`;
|
|
209728
|
+
}) : ["- None reported."];
|
|
209729
|
+
const identityLines = result.audit.modelCalls.filter((call) => call.status === "completed").map((call) => {
|
|
209730
|
+
const label = [call.kind, call.agent].filter(Boolean).join("/");
|
|
209731
|
+
const identity = call.executionIdentity;
|
|
209732
|
+
if (!identity)
|
|
209733
|
+
return `- ${label}: identity=unknown`;
|
|
209734
|
+
const reportedIdentity = [
|
|
209735
|
+
identity.reportedProvider ? `reportedProvider=${escapeMarkdownText(identity.reportedProvider)}` : undefined,
|
|
209736
|
+
identity.reportedModel ? `reportedModel=${escapeMarkdownText(identity.reportedModel)}` : undefined
|
|
209737
|
+
].filter((value) => value !== undefined);
|
|
209738
|
+
return `- ${label}: route=${identity.providerRoute}, requested=${escapeMarkdownText(identity.requestedModel ?? "unknown")}${reportedIdentity.length > 0 ? `, ${reportedIdentity.join(", ")}` : ""}, reporting=${identity.reportingStatus}`;
|
|
209739
|
+
});
|
|
209299
209740
|
const totalTokens = budget.tokenUsage.totals.totalTokens;
|
|
209741
|
+
const plan = budget.modelCallPlan;
|
|
209742
|
+
const outputLimits = budget.effectiveWarnAgentOutputBytes === undefined ? `${budget.maxAgentOutputBytes} bytes hard (soft warning disabled)` : `${budget.effectiveWarnAgentOutputBytes} bytes soft / ${budget.maxAgentOutputBytes} bytes hard`;
|
|
209300
209743
|
return [
|
|
209301
209744
|
"",
|
|
209302
209745
|
"## Execution Budget",
|
|
209303
209746
|
"",
|
|
209304
|
-
`- Model calls: ${budget.modelCalls.planned} planned / ${budget.modelCalls.consumed} consumed / ${budget.modelCalls.skipped} skipped`,
|
|
209747
|
+
`- Model calls: ${budget.modelCalls.planned} planned / ${budget.modelCalls.consumed} consumed / ${budget.modelCalls.skipped} skipped / ${budget.maxModelCalls} ceiling`,
|
|
209748
|
+
`- Potential calls: ${plan.potentialTotalCalls} total (${plan.requiredPrimaryCalls} primary, ${plan.potentialVerifierCalls} verifier, ${plan.potentialJudgeCalls} judge)`,
|
|
209305
209749
|
`- Wall time: ${budget.wallTime.consumedMs}ms consumed / ${budget.wallTime.limitMs}ms limit`,
|
|
209306
209750
|
`- Token usage: ${budget.tokenUsage.status} (${budget.tokenUsage.reportedCalls} reported, ${budget.tokenUsage.unknownCalls} unknown${totalTokens === undefined ? "" : `, ${totalTokens} total`})`,
|
|
209751
|
+
`- Agent output limits: ${outputLimits}`,
|
|
209752
|
+
`- Findings target: ${budget.maxFindingsPerAgent} per primary agent (soft)`,
|
|
209307
209753
|
"- Agent output:",
|
|
209308
|
-
...outputLines
|
|
209754
|
+
...outputLines,
|
|
209755
|
+
"- Model identities:",
|
|
209756
|
+
...identityLines.length > 0 ? identityLines : ["- None completed."]
|
|
209309
209757
|
];
|
|
209310
209758
|
}
|
|
209311
209759
|
function formatCompletion(result) {
|
|
@@ -209698,11 +210146,11 @@ function canonicalJson(value) {
|
|
|
209698
210146
|
function canonicalize(value) {
|
|
209699
210147
|
if (Array.isArray(value))
|
|
209700
210148
|
return value.map(canonicalize);
|
|
209701
|
-
if (!
|
|
210149
|
+
if (!isRecord15(value))
|
|
209702
210150
|
return value;
|
|
209703
210151
|
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)]));
|
|
209704
210152
|
}
|
|
209705
|
-
function
|
|
210153
|
+
function isRecord15(value) {
|
|
209706
210154
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
209707
210155
|
}
|
|
209708
210156
|
|
|
@@ -209716,12 +210164,10 @@ var REVIEW_BUDGET_KEYS = new Set([
|
|
|
209716
210164
|
]);
|
|
209717
210165
|
var MODEL_CALL_KINDS = ["primary", "verifier", "judge"];
|
|
209718
210166
|
function resolveReviewBudget(ceiling, requested) {
|
|
209719
|
-
if (requested
|
|
209720
|
-
return ceiling;
|
|
209721
|
-
if (!isRecord14(requested)) {
|
|
210167
|
+
if (requested !== undefined && !isRecord16(requested)) {
|
|
209722
210168
|
throw new KyosoRequestError("options.reviewBudget must be an object.", "REVIEW_BUDGET_INVALID");
|
|
209723
210169
|
}
|
|
209724
|
-
for (const [key, value] of Object.entries(requested)) {
|
|
210170
|
+
for (const [key, value] of Object.entries(requested ?? {})) {
|
|
209725
210171
|
if (!REVIEW_BUDGET_KEYS.has(key)) {
|
|
209726
210172
|
throw new KyosoRequestError(`options.reviewBudget.${key} is not supported.`, "REVIEW_BUDGET_INVALID");
|
|
209727
210173
|
}
|
|
@@ -209742,36 +210188,105 @@ function resolveReviewBudget(ceiling, requested) {
|
|
|
209742
210188
|
"maxFindingsPerAgent"
|
|
209743
210189
|
];
|
|
209744
210190
|
for (const key of numericKeys) {
|
|
209745
|
-
const value = requested[key];
|
|
210191
|
+
const value = requested?.[key];
|
|
209746
210192
|
if (value === undefined)
|
|
209747
210193
|
continue;
|
|
209748
210194
|
if (value > ceiling[key]) {
|
|
209749
210195
|
throw new KyosoRequestError(`options.reviewBudget.${key} cannot exceed the user-global ceiling.`, "REVIEW_BUDGET_EXCEEDS_CEILING");
|
|
209750
210196
|
}
|
|
209751
210197
|
}
|
|
209752
|
-
if (ceiling.skipOptionalPhasesWhenTokenUsageUnknown && requested
|
|
210198
|
+
if (ceiling.skipOptionalPhasesWhenTokenUsageUnknown && requested?.skipOptionalPhasesWhenTokenUsageUnknown === false) {
|
|
209753
210199
|
throw new KyosoRequestError("options.reviewBudget.skipOptionalPhasesWhenTokenUsageUnknown cannot relax the user-global ceiling.", "REVIEW_BUDGET_EXCEEDS_CEILING");
|
|
209754
210200
|
}
|
|
210201
|
+
const maxAgentOutputBytes = requested?.maxAgentOutputBytes ?? ceiling.maxAgentOutputBytes;
|
|
210202
|
+
return {
|
|
210203
|
+
maxModelCalls: requested?.maxModelCalls ?? ceiling.maxModelCalls,
|
|
210204
|
+
maxTotalWallTimeMs: requested?.maxTotalWallTimeMs ?? ceiling.maxTotalWallTimeMs,
|
|
210205
|
+
warnAgentOutputBytes: ceiling.warnAgentOutputBytes,
|
|
210206
|
+
maxAgentOutputBytes,
|
|
210207
|
+
maxFindingsPerAgent: requested?.maxFindingsPerAgent ?? ceiling.maxFindingsPerAgent,
|
|
210208
|
+
skipOptionalPhasesWhenTokenUsageUnknown: ceiling.skipOptionalPhasesWhenTokenUsageUnknown || requested?.skipOptionalPhasesWhenTokenUsageUnknown === true,
|
|
210209
|
+
...ceiling.warnAgentOutputBytes < maxAgentOutputBytes ? { effectiveWarnAgentOutputBytes: ceiling.warnAgentOutputBytes } : {}
|
|
210210
|
+
};
|
|
210211
|
+
}
|
|
210212
|
+
function buildReviewModelCallPlan(input2) {
|
|
210213
|
+
const requiredPrimaryCalls = nonNegativeInteger(input2.requiredPrimaryCalls);
|
|
210214
|
+
const potentialVerifierCalls = input2.verificationEnabled && requiredPrimaryCalls === 2 ? Math.min(2, nonNegativeInteger(input2.verificationMaxFindings)) : 0;
|
|
210215
|
+
const potentialJudgeCalls = input2.llmJudgeAvailable ? 1 : 0;
|
|
210216
|
+
const potentialTotalCalls = requiredPrimaryCalls + potentialVerifierCalls + potentialJudgeCalls;
|
|
210217
|
+
const ceilingEffects = [];
|
|
210218
|
+
const availableCalls = nonNegativeInteger(input2.maxModelCalls);
|
|
210219
|
+
if (availableCalls < requiredPrimaryCalls) {
|
|
210220
|
+
if (requiredPrimaryCalls > 0) {
|
|
210221
|
+
ceilingEffects.push({
|
|
210222
|
+
kind: "primary",
|
|
210223
|
+
action: "skip",
|
|
210224
|
+
calls: requiredPrimaryCalls,
|
|
210225
|
+
reason: "model_call_budget"
|
|
210226
|
+
});
|
|
210227
|
+
}
|
|
210228
|
+
if (potentialVerifierCalls > 0) {
|
|
210229
|
+
ceilingEffects.push({
|
|
210230
|
+
kind: "verifier",
|
|
210231
|
+
action: "skip",
|
|
210232
|
+
calls: potentialVerifierCalls,
|
|
210233
|
+
reason: "model_call_budget"
|
|
210234
|
+
});
|
|
210235
|
+
}
|
|
210236
|
+
if (potentialJudgeCalls > 0) {
|
|
210237
|
+
ceilingEffects.push({
|
|
210238
|
+
kind: "judge",
|
|
210239
|
+
action: "deterministic_fallback",
|
|
210240
|
+
calls: potentialJudgeCalls,
|
|
210241
|
+
reason: "model_call_budget"
|
|
210242
|
+
});
|
|
210243
|
+
}
|
|
210244
|
+
} else {
|
|
210245
|
+
let remainingCalls = availableCalls - requiredPrimaryCalls;
|
|
210246
|
+
const verifierCalls = Math.min(potentialVerifierCalls, remainingCalls);
|
|
210247
|
+
remainingCalls -= verifierCalls;
|
|
210248
|
+
const skippedVerifierCalls = potentialVerifierCalls - verifierCalls;
|
|
210249
|
+
if (skippedVerifierCalls > 0) {
|
|
210250
|
+
ceilingEffects.push({
|
|
210251
|
+
kind: "verifier",
|
|
210252
|
+
action: "skip",
|
|
210253
|
+
calls: skippedVerifierCalls,
|
|
210254
|
+
reason: "model_call_budget"
|
|
210255
|
+
});
|
|
210256
|
+
}
|
|
210257
|
+
const judgeCalls = Math.min(potentialJudgeCalls, remainingCalls);
|
|
210258
|
+
const fallbackJudgeCalls = potentialJudgeCalls - judgeCalls;
|
|
210259
|
+
if (fallbackJudgeCalls > 0) {
|
|
210260
|
+
ceilingEffects.push({
|
|
210261
|
+
kind: "judge",
|
|
210262
|
+
action: "deterministic_fallback",
|
|
210263
|
+
calls: fallbackJudgeCalls,
|
|
210264
|
+
reason: "model_call_budget"
|
|
210265
|
+
});
|
|
210266
|
+
}
|
|
210267
|
+
}
|
|
209755
210268
|
return {
|
|
209756
|
-
|
|
209757
|
-
|
|
209758
|
-
|
|
209759
|
-
|
|
209760
|
-
|
|
210269
|
+
requiredPrimaryCalls,
|
|
210270
|
+
potentialVerifierCalls,
|
|
210271
|
+
potentialJudgeCalls,
|
|
210272
|
+
potentialTotalCalls,
|
|
210273
|
+
ceilingEffects
|
|
209761
210274
|
};
|
|
209762
210275
|
}
|
|
209763
210276
|
|
|
209764
210277
|
class ReviewBudgetTracker {
|
|
209765
210278
|
budget;
|
|
209766
210279
|
startedAtEpochMs;
|
|
210280
|
+
modelCallPlan;
|
|
209767
210281
|
deadlineAtEpochMs;
|
|
209768
210282
|
reservations = new Map;
|
|
209769
210283
|
skippedCalls = [];
|
|
209770
210284
|
incompleteReasons = new Set;
|
|
209771
210285
|
nextReservationId = 1;
|
|
209772
|
-
constructor(budget, startedAtEpochMs = Date.now()) {
|
|
210286
|
+
constructor(budget, startedAtEpochMs = Date.now(), modelCallPlan = emptyReviewModelCallPlan()) {
|
|
209773
210287
|
this.budget = budget;
|
|
209774
210288
|
this.startedAtEpochMs = startedAtEpochMs;
|
|
210289
|
+
this.modelCallPlan = modelCallPlan;
|
|
209775
210290
|
this.deadlineAtEpochMs = startedAtEpochMs + budget.maxTotalWallTimeMs;
|
|
209776
210291
|
}
|
|
209777
210292
|
remainingWallTimeMs(now = Date.now()) {
|
|
@@ -209815,24 +210330,35 @@ class ReviewBudgetTracker {
|
|
|
209815
210330
|
}
|
|
209816
210331
|
return { reservation };
|
|
209817
210332
|
}
|
|
209818
|
-
markStarted(reservation) {
|
|
210333
|
+
markStarted(reservation, executionIdentity) {
|
|
209819
210334
|
const current = this.reservations.get(reservation.id);
|
|
209820
210335
|
if (!current || current.status !== "reserved")
|
|
209821
210336
|
return;
|
|
209822
210337
|
current.status = "started";
|
|
210338
|
+
current.executionIdentity = normalizeModelExecutionIdentity(executionIdentity);
|
|
209823
210339
|
}
|
|
209824
210340
|
hasStarted(reservation) {
|
|
209825
210341
|
const current = this.reservations.get(reservation.id);
|
|
209826
210342
|
return current?.status === "started" || current?.status === "completed";
|
|
209827
210343
|
}
|
|
210344
|
+
executionIdentity(reservation) {
|
|
210345
|
+
return this.reservations.get(reservation.id)?.executionIdentity;
|
|
210346
|
+
}
|
|
209828
210347
|
complete(reservation, values = {}) {
|
|
209829
210348
|
const current = this.reservations.get(reservation.id);
|
|
209830
210349
|
if (!current || current.status === "skipped" || current.status === "completed") {
|
|
209831
210350
|
return;
|
|
209832
210351
|
}
|
|
209833
210352
|
current.status = "completed";
|
|
210353
|
+
current.messageBytes = values.messageBytes;
|
|
210354
|
+
current.thoughtBytes = values.thoughtBytes;
|
|
209834
210355
|
current.outputBytes = values.outputBytes;
|
|
210356
|
+
current.outputWarningTriggered = values.outputWarningTriggered;
|
|
210357
|
+
current.salvaged = values.salvaged;
|
|
210358
|
+
current.reportedFindings = values.reportedFindings;
|
|
210359
|
+
current.findingsTargetExceeded = values.findingsTargetExceeded;
|
|
209835
210360
|
current.usage = normalizeModelTokenUsage(values.usage);
|
|
210361
|
+
current.executionIdentity = normalizeModelExecutionIdentity(values.executionIdentity) ?? current.executionIdentity;
|
|
209836
210362
|
current.stopReason = values.stopReason;
|
|
209837
210363
|
}
|
|
209838
210364
|
skip(reservation, reason) {
|
|
@@ -209905,12 +210431,16 @@ class ReviewBudgetTracker {
|
|
|
209905
210431
|
},
|
|
209906
210432
|
executionBudget: {
|
|
209907
210433
|
maxModelCalls: this.budget.maxModelCalls,
|
|
210434
|
+
modelCallPlan: this.modelCallPlan,
|
|
209908
210435
|
modelCalls: { planned, consumed, skipped, byKind },
|
|
209909
210436
|
wallTime: {
|
|
209910
210437
|
limitMs: this.budget.maxTotalWallTimeMs,
|
|
209911
210438
|
consumedMs,
|
|
209912
210439
|
remainingMs: this.remainingWallTimeMs(now)
|
|
209913
210440
|
},
|
|
210441
|
+
...this.budget.effectiveWarnAgentOutputBytes !== undefined ? {
|
|
210442
|
+
effectiveWarnAgentOutputBytes: this.budget.effectiveWarnAgentOutputBytes
|
|
210443
|
+
} : {},
|
|
209914
210444
|
maxAgentOutputBytes: this.budget.maxAgentOutputBytes,
|
|
209915
210445
|
maxFindingsPerAgent: this.budget.maxFindingsPerAgent,
|
|
209916
210446
|
skipOptionalPhasesWhenTokenUsageUnknown: this.budget.skipOptionalPhasesWhenTokenUsageUnknown,
|
|
@@ -209934,8 +210464,15 @@ class ReviewBudgetTracker {
|
|
|
209934
210464
|
...reservation.agent ? { agent: reservation.agent } : {},
|
|
209935
210465
|
status: reservation.status === "completed" ? "completed" : "skipped",
|
|
209936
210466
|
...reservation.reason ? { reason: reservation.reason } : {},
|
|
210467
|
+
...reservation.messageBytes !== undefined ? { messageBytes: reservation.messageBytes } : {},
|
|
210468
|
+
...reservation.thoughtBytes !== undefined ? { thoughtBytes: reservation.thoughtBytes } : {},
|
|
209937
210469
|
...reservation.outputBytes !== undefined ? { outputBytes: reservation.outputBytes } : {},
|
|
210470
|
+
...reservation.outputWarningTriggered !== undefined ? { outputWarningTriggered: reservation.outputWarningTriggered } : {},
|
|
210471
|
+
...reservation.salvaged !== undefined ? { salvaged: reservation.salvaged } : {},
|
|
210472
|
+
...reservation.reportedFindings !== undefined ? { reportedFindings: reservation.reportedFindings } : {},
|
|
210473
|
+
...reservation.findingsTargetExceeded !== undefined ? { findingsTargetExceeded: reservation.findingsTargetExceeded } : {},
|
|
209938
210474
|
...reservation.usage ? { usage: reservation.usage } : {},
|
|
210475
|
+
...reservation.executionIdentity ? { executionIdentity: reservation.executionIdentity } : {},
|
|
209939
210476
|
...reservation.stopReason ? { stopReason: reservation.stopReason } : {}
|
|
209940
210477
|
}));
|
|
209941
210478
|
return [...reservations, ...this.skippedCalls];
|
|
@@ -209959,7 +210496,19 @@ function addUsage(total, usage) {
|
|
|
209959
210496
|
function isPositiveInteger(value) {
|
|
209960
210497
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
209961
210498
|
}
|
|
209962
|
-
function
|
|
210499
|
+
function nonNegativeInteger(value) {
|
|
210500
|
+
return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
|
|
210501
|
+
}
|
|
210502
|
+
function emptyReviewModelCallPlan() {
|
|
210503
|
+
return {
|
|
210504
|
+
requiredPrimaryCalls: 0,
|
|
210505
|
+
potentialVerifierCalls: 0,
|
|
210506
|
+
potentialJudgeCalls: 0,
|
|
210507
|
+
potentialTotalCalls: 0,
|
|
210508
|
+
ceilingEffects: []
|
|
210509
|
+
};
|
|
210510
|
+
}
|
|
210511
|
+
function isRecord16(value) {
|
|
209963
210512
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
209964
210513
|
}
|
|
209965
210514
|
|
|
@@ -210021,7 +210570,7 @@ function parseVerificationVerdicts(rawText) {
|
|
|
210021
210570
|
if (!Array.isArray(parsed.verdicts))
|
|
210022
210571
|
return;
|
|
210023
210572
|
return parsed.verdicts.flatMap((item) => {
|
|
210024
|
-
if (!
|
|
210573
|
+
if (!isRecord17(item))
|
|
210025
210574
|
return [];
|
|
210026
210575
|
if (typeof item.findingId !== "string")
|
|
210027
210576
|
return [];
|
|
@@ -210099,7 +210648,7 @@ function verificationNote(reasoning) {
|
|
|
210099
210648
|
function isVerdict(value) {
|
|
210100
210649
|
return value === "confirmed" || value === "refuted" || value === "uncertain";
|
|
210101
210650
|
}
|
|
210102
|
-
function
|
|
210651
|
+
function isRecord17(value) {
|
|
210103
210652
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
210104
210653
|
}
|
|
210105
210654
|
|
|
@@ -210124,13 +210673,14 @@ async function runReview(tool, request, options = {}) {
|
|
|
210124
210673
|
} catch (error51) {
|
|
210125
210674
|
if (error51 instanceof KyosoRequestError) {
|
|
210126
210675
|
const config2 = kyosoConfigSchema.parse(defaultConfig);
|
|
210127
|
-
const
|
|
210676
|
+
const reviewBudget = resolveReviewBudget(config2.reviewBudget, undefined);
|
|
210677
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(config2, reviewBudget, options.env ?? process.env));
|
|
210128
210678
|
const requestFingerprint = createRequestFingerprint({
|
|
210129
210679
|
tool,
|
|
210130
210680
|
request: requestForRecursionFingerprint(request),
|
|
210131
210681
|
config: config2,
|
|
210132
210682
|
roles: resolveAgentRoles(config2),
|
|
210133
|
-
budget:
|
|
210683
|
+
budget: reviewBudget,
|
|
210134
210684
|
entrypoint: options.entrypoint
|
|
210135
210685
|
});
|
|
210136
210686
|
const trace2 = traceWriterFactory({
|
|
@@ -210236,7 +210786,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
210236
210786
|
});
|
|
210237
210787
|
validateReviewRequest(tool, request);
|
|
210238
210788
|
const reviewBudget = resolveReviewBudget(loaded.config.reviewBudget, request.options?.reviewBudget);
|
|
210239
|
-
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs);
|
|
210789
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(loaded.config, reviewBudget, options.env ?? process.env, request.options?.judgeProvider));
|
|
210240
210790
|
assertTrustedWorkspaceRoot(request.workspace?.root, loaded.config.workspace.root, cwd);
|
|
210241
210791
|
const networkMode = resolveNetworkMode(request.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
|
|
210242
210792
|
if (networkMode === "unrestricted" && !loaded.config.network.allowUnrestricted) {
|
|
@@ -210357,6 +210907,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
210357
210907
|
budgetTracker,
|
|
210358
210908
|
requestFingerprint
|
|
210359
210909
|
});
|
|
210910
|
+
warnings.push(...plannedBudgetWarnings(budgetTracker));
|
|
210360
210911
|
snapshot = await createSnapshot(traceId, tool, built.request, {
|
|
210361
210912
|
denyPatterns,
|
|
210362
210913
|
allowPatterns,
|
|
@@ -210383,11 +210934,9 @@ async function runReview(tool, request, options = {}) {
|
|
|
210383
210934
|
budgetTracker
|
|
210384
210935
|
});
|
|
210385
210936
|
warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
|
|
210386
|
-
const
|
|
210387
|
-
const
|
|
210388
|
-
|
|
210389
|
-
budgetTracker.markIncomplete("coverage_incomplete");
|
|
210390
|
-
warnings.push(`Agent ${item.result.agent} findings were capped at ${reviewBudget.maxFindingsPerAgent}; review coverage is incomplete.`);
|
|
210937
|
+
const normalizedAgentResults = agentResults.map((result) => normalizeAgentRunResult(result, reviewBudget.maxFindingsPerAgent));
|
|
210938
|
+
for (const result of normalizedAgentResults.filter((item) => item.findingsTargetExceeded)) {
|
|
210939
|
+
warnings.push(`Agent ${result.agent} reported ${result.reportedFindings} findings, above the soft target of ${reviewBudget.maxFindingsPerAgent}; all findings were retained.`);
|
|
210391
210940
|
}
|
|
210392
210941
|
const enabledAgents = ["codex", "claude"].filter((agent) => loaded.config.agents[agent].enabled);
|
|
210393
210942
|
const agentsUsed = normalizedAgentResults.filter((result) => result.status !== "skipped").map((result) => result.agent);
|
|
@@ -210561,6 +211110,11 @@ async function runReview(tool, request, options = {}) {
|
|
|
210561
211110
|
}));
|
|
210562
211111
|
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
210563
211112
|
const budgetAfterJudge = budgetTracker.snapshot();
|
|
211113
|
+
const finalWarnings = Array.from(new Set([
|
|
211114
|
+
...resultWithoutMarkdown.audit.warnings ?? [],
|
|
211115
|
+
...outputWarningMessages(budgetAfterJudge),
|
|
211116
|
+
...tokenUsageWarningMessages(budgetTracker, budgetAfterJudge)
|
|
211117
|
+
]));
|
|
210564
211118
|
const finalDecision = budgetAfterJudge.completion.status === "incomplete" ? "block" : decision;
|
|
210565
211119
|
const resultAfterJudge = {
|
|
210566
211120
|
...resultWithoutMarkdown,
|
|
@@ -210572,6 +211126,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
210572
211126
|
audit: {
|
|
210573
211127
|
...resultWithoutMarkdown.audit,
|
|
210574
211128
|
completedAt: new Date().toISOString(),
|
|
211129
|
+
warnings: finalWarnings,
|
|
210575
211130
|
modelCalls: budgetAfterJudge.modelCalls
|
|
210576
211131
|
}
|
|
210577
211132
|
};
|
|
@@ -210580,6 +211135,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
210580
211135
|
traceId,
|
|
210581
211136
|
provider: judge.provider,
|
|
210582
211137
|
status: judge.status,
|
|
211138
|
+
...judge.executionIdentity ? { executionIdentity: judge.executionIdentity } : {},
|
|
210583
211139
|
timestamp: new Date().toISOString()
|
|
210584
211140
|
};
|
|
210585
211141
|
if (judge.error)
|
|
@@ -210773,11 +211329,20 @@ async function runFindingVerification(input2) {
|
|
|
210773
211329
|
workspaceDir: input2.workspaceDir,
|
|
210774
211330
|
timeoutMs: Math.min(input2.config.verification.timeoutMs, input2.budgetTracker.remainingWallTimeMs()),
|
|
210775
211331
|
deadlineAtEpochMs: input2.budgetTracker.deadlineAtEpochMs,
|
|
211332
|
+
warnOutputBytes: input2.budgetTracker.budget.effectiveWarnAgentOutputBytes,
|
|
210776
211333
|
maxOutputBytes: input2.budgetTracker.budget.maxAgentOutputBytes,
|
|
210777
211334
|
networkMode: input2.networkMode,
|
|
210778
|
-
onStarted: () => {
|
|
210779
|
-
input2.budgetTracker.markStarted(group.reservation);
|
|
210780
|
-
|
|
211335
|
+
onStarted: (executionIdentity) => {
|
|
211336
|
+
input2.budgetTracker.markStarted(group.reservation, executionIdentity);
|
|
211337
|
+
const event = buildAgentStartedEvent({
|
|
211338
|
+
traceId: input2.traceId,
|
|
211339
|
+
agent: group.verifier,
|
|
211340
|
+
role: "finding_verifier",
|
|
211341
|
+
executionIdentity: input2.budgetTracker.executionIdentity(group.reservation)
|
|
211342
|
+
});
|
|
211343
|
+
return input2.trace.write(event).catch(() => {
|
|
211344
|
+
warnings.push("AUDIT_WRITE_FAILED: agent_started event could not be recorded.");
|
|
211345
|
+
});
|
|
210781
211346
|
}
|
|
210782
211347
|
}));
|
|
210783
211348
|
let results;
|
|
@@ -210919,8 +211484,8 @@ function buildCrossModelAnalysis(judge, reviewMode) {
|
|
|
210919
211484
|
}
|
|
210920
211485
|
async function runBudgetedJudge(input2) {
|
|
210921
211486
|
const configuredProvider = input2.requestedProvider ?? input2.config.provider;
|
|
210922
|
-
const
|
|
210923
|
-
if (
|
|
211487
|
+
const judgeRoute = resolveJudgeCallRoute(input2.config.mode, configuredProvider, input2.env);
|
|
211488
|
+
if (!judgeRoute.llmAvailable) {
|
|
210924
211489
|
return runJudge(input2);
|
|
210925
211490
|
}
|
|
210926
211491
|
const fallback = () => runJudge({
|
|
@@ -210979,8 +211544,10 @@ async function runBudgetedJudge(input2) {
|
|
|
210979
211544
|
const judge = await runJudge({ ...input2, timeoutMs });
|
|
210980
211545
|
const usage = normalizeModelTokenUsage(judge.usage);
|
|
210981
211546
|
input2.budgetTracker.complete(reservation, {
|
|
210982
|
-
...usage ? { usage } : {}
|
|
211547
|
+
...usage ? { usage } : {},
|
|
211548
|
+
...judge.executionIdentity ? { executionIdentity: judge.executionIdentity } : {}
|
|
210983
211549
|
});
|
|
211550
|
+
const executionIdentity = input2.budgetTracker.executionIdentity(reservation);
|
|
210984
211551
|
await input2.trace.write({
|
|
210985
211552
|
type: "model_call_completed",
|
|
210986
211553
|
traceId: input2.traceId,
|
|
@@ -210988,6 +211555,7 @@ async function runBudgetedJudge(input2) {
|
|
|
210988
211555
|
provider: judge.provider,
|
|
210989
211556
|
resultStatus: judge.status,
|
|
210990
211557
|
...usage ? { usage } : {},
|
|
211558
|
+
...executionIdentity ? { executionIdentity } : {},
|
|
210991
211559
|
timestamp: new Date().toISOString()
|
|
210992
211560
|
});
|
|
210993
211561
|
return judge;
|
|
@@ -211101,30 +211669,25 @@ async function runAgents(input2) {
|
|
|
211101
211669
|
tool: input2.tool,
|
|
211102
211670
|
prompt: buildAgentPrompt(input2.tool, input2.request, agent, role, {
|
|
211103
211671
|
requiredLenses,
|
|
211104
|
-
cisaEnabled: input2.config.securityReview.cisaSecureByDesign.enabled
|
|
211672
|
+
cisaEnabled: input2.config.securityReview.cisaSecureByDesign.enabled,
|
|
211673
|
+
maxFindingsTarget: input2.budgetTracker.budget.maxFindingsPerAgent
|
|
211105
211674
|
}),
|
|
211106
211675
|
workspaceDir: input2.workspaceDir,
|
|
211107
211676
|
timeoutMs: Math.min(input2.request.options?.maxAgentTimeoutMs ?? Number.POSITIVE_INFINITY, agentConfig.timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS, input2.budgetTracker.remainingWallTimeMs()),
|
|
211108
211677
|
deadlineAtEpochMs: input2.budgetTracker.deadlineAtEpochMs,
|
|
211678
|
+
warnOutputBytes: input2.budgetTracker.budget.effectiveWarnAgentOutputBytes,
|
|
211109
211679
|
maxOutputBytes: input2.budgetTracker.budget.maxAgentOutputBytes,
|
|
211110
211680
|
networkMode: input2.networkMode,
|
|
211111
|
-
onStarted: () => {
|
|
211112
|
-
input2.budgetTracker.markStarted(reservation);
|
|
211681
|
+
onStarted: (executionIdentity) => {
|
|
211682
|
+
input2.budgetTracker.markStarted(reservation, executionIdentity);
|
|
211113
211683
|
if (!acceptingStartedEvents)
|
|
211114
211684
|
return Promise.resolve();
|
|
211115
|
-
const event = {
|
|
211116
|
-
type: "agent_started",
|
|
211685
|
+
const event = buildAgentStartedEvent({
|
|
211117
211686
|
traceId: input2.traceId,
|
|
211118
211687
|
agent,
|
|
211119
211688
|
role,
|
|
211120
|
-
|
|
211121
|
-
};
|
|
211122
|
-
if (agentConfig.model) {
|
|
211123
|
-
event.model = sanitizeTextForDisplay(agentConfig.model);
|
|
211124
|
-
}
|
|
211125
|
-
if (agent === "codex" && input2.config.agents.codex.provider === CODEX_OPENROUTER_PROVIDER) {
|
|
211126
|
-
event.provider = CODEX_OPENROUTER_PROVIDER;
|
|
211127
|
-
}
|
|
211689
|
+
executionIdentity: input2.budgetTracker.executionIdentity(reservation)
|
|
211690
|
+
});
|
|
211128
211691
|
const write = (async () => {
|
|
211129
211692
|
try {
|
|
211130
211693
|
await input2.trace.write(event);
|
|
@@ -211176,7 +211739,8 @@ async function runAgents(input2) {
|
|
|
211176
211739
|
}
|
|
211177
211740
|
};
|
|
211178
211741
|
});
|
|
211179
|
-
|
|
211742
|
+
const normalizedResults = orderedResults.map((result) => normalizeAgentRunResult(result, input2.budgetTracker.budget.maxFindingsPerAgent));
|
|
211743
|
+
for (const result of normalizedResults) {
|
|
211180
211744
|
const reservation = reservations.get(result.agent);
|
|
211181
211745
|
if (!reservation)
|
|
211182
211746
|
continue;
|
|
@@ -211191,7 +211755,7 @@ async function runAgents(input2) {
|
|
|
211191
211755
|
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
211192
211756
|
}
|
|
211193
211757
|
}
|
|
211194
|
-
await Promise.all(
|
|
211758
|
+
await Promise.all(normalizedResults.map((result) => {
|
|
211195
211759
|
const event = {
|
|
211196
211760
|
type: "agent_completed",
|
|
211197
211761
|
traceId: input2.traceId,
|
|
@@ -211206,12 +211770,20 @@ async function runAgents(input2) {
|
|
|
211206
211770
|
event.errorCode = result.error.code;
|
|
211207
211771
|
event.errorDetail = result.error.detail;
|
|
211208
211772
|
}
|
|
211773
|
+
if (result.salvaged !== undefined)
|
|
211774
|
+
event.salvaged = result.salvaged;
|
|
211775
|
+
if (result.reportedFindings !== undefined) {
|
|
211776
|
+
event.reportedFindings = result.reportedFindings;
|
|
211777
|
+
}
|
|
211778
|
+
if (result.findingsTargetExceeded !== undefined) {
|
|
211779
|
+
event.findingsTargetExceeded = result.findingsTargetExceeded;
|
|
211780
|
+
}
|
|
211209
211781
|
if (input2.config.audit.includeRawAgentOutput && result.rawText) {
|
|
211210
211782
|
event.rawText = sanitizeTextForRawOutput(result.rawText);
|
|
211211
211783
|
}
|
|
211212
211784
|
return input2.trace.write(event);
|
|
211213
211785
|
}));
|
|
211214
|
-
return
|
|
211786
|
+
return normalizedResults;
|
|
211215
211787
|
}
|
|
211216
211788
|
async function skipReservedPrimaryAgents(input2) {
|
|
211217
211789
|
const results = [];
|
|
@@ -211261,20 +211833,44 @@ async function finalizeModelCallResult(input2) {
|
|
|
211261
211833
|
});
|
|
211262
211834
|
return;
|
|
211263
211835
|
}
|
|
211264
|
-
input2.budgetTracker.markStarted(input2.reservation);
|
|
211836
|
+
input2.budgetTracker.markStarted(input2.reservation, input2.result.executionIdentity);
|
|
211265
211837
|
const usage = normalizeModelTokenUsage(input2.result.usage);
|
|
211266
|
-
const
|
|
211838
|
+
const { messageBytes, thoughtBytes, outputBytes } = resolveOutputByteMetrics(input2.result);
|
|
211839
|
+
const warningThreshold = input2.budgetTracker.budget.effectiveWarnAgentOutputBytes;
|
|
211840
|
+
const warningTriggered = warningThreshold !== undefined && outputBytes !== undefined && (input2.result.outputWarningTriggered === true || outputBytes >= warningThreshold);
|
|
211841
|
+
const outputWarningTriggered = warningTriggered || input2.result.outputWarningTriggered !== undefined ? warningTriggered : undefined;
|
|
211267
211842
|
input2.budgetTracker.complete(input2.reservation, {
|
|
211843
|
+
...messageBytes === undefined ? {} : { messageBytes },
|
|
211844
|
+
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
211268
211845
|
...outputBytes === undefined ? {} : { outputBytes },
|
|
211846
|
+
...outputWarningTriggered === undefined ? {} : { outputWarningTriggered },
|
|
211847
|
+
...input2.result.salvaged === undefined ? {} : { salvaged: input2.result.salvaged },
|
|
211848
|
+
...input2.result.reportedFindings === undefined ? {} : { reportedFindings: input2.result.reportedFindings },
|
|
211849
|
+
...input2.result.findingsTargetExceeded === undefined ? {} : { findingsTargetExceeded: input2.result.findingsTargetExceeded },
|
|
211269
211850
|
...usage ? { usage } : {},
|
|
211851
|
+
...input2.result.executionIdentity ? { executionIdentity: input2.result.executionIdentity } : {},
|
|
211270
211852
|
...input2.result.stopReason ? { stopReason: input2.result.stopReason } : {}
|
|
211271
211853
|
});
|
|
211854
|
+
const executionIdentity = input2.budgetTracker.executionIdentity(input2.reservation);
|
|
211272
211855
|
if (input2.result.error?.code === "AGENT_OUTPUT_LIMIT") {
|
|
211273
211856
|
input2.budgetTracker.markIncomplete("agent_output_limit");
|
|
211274
211857
|
}
|
|
211275
211858
|
if (input2.result.error?.code === "REVIEW_DEADLINE_EXCEEDED") {
|
|
211276
211859
|
input2.budgetTracker.markIncomplete("deadline");
|
|
211277
211860
|
}
|
|
211861
|
+
if (outputWarningTriggered && warningThreshold !== undefined && messageBytes !== undefined && thoughtBytes !== undefined && outputBytes !== undefined) {
|
|
211862
|
+
await input2.trace.write({
|
|
211863
|
+
type: "agent_output_warning",
|
|
211864
|
+
traceId: input2.traceId,
|
|
211865
|
+
kind: input2.reservation.kind,
|
|
211866
|
+
agent: input2.reservation.agent,
|
|
211867
|
+
thresholdBytes: warningThreshold,
|
|
211868
|
+
messageBytes,
|
|
211869
|
+
thoughtBytes,
|
|
211870
|
+
outputBytes,
|
|
211871
|
+
timestamp: new Date().toISOString()
|
|
211872
|
+
});
|
|
211873
|
+
}
|
|
211278
211874
|
await input2.trace.write({
|
|
211279
211875
|
type: "model_call_completed",
|
|
211280
211876
|
traceId: input2.traceId,
|
|
@@ -211282,12 +211878,55 @@ async function finalizeModelCallResult(input2) {
|
|
|
211282
211878
|
agent: input2.reservation.agent,
|
|
211283
211879
|
resultStatus: input2.result.status,
|
|
211284
211880
|
...input2.result.error?.code ? { errorCode: input2.result.error.code } : {},
|
|
211881
|
+
...messageBytes === undefined ? {} : { messageBytes },
|
|
211882
|
+
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
211285
211883
|
...outputBytes === undefined ? {} : { outputBytes },
|
|
211884
|
+
...outputWarningTriggered === undefined ? {} : { outputWarningTriggered },
|
|
211885
|
+
...input2.result.salvaged === undefined ? {} : { salvaged: input2.result.salvaged },
|
|
211886
|
+
...input2.result.reportedFindings === undefined ? {} : { reportedFindings: input2.result.reportedFindings },
|
|
211887
|
+
...input2.result.findingsTargetExceeded === undefined ? {} : { findingsTargetExceeded: input2.result.findingsTargetExceeded },
|
|
211286
211888
|
...usage ? { usage } : {},
|
|
211889
|
+
...executionIdentity ? { executionIdentity } : {},
|
|
211287
211890
|
...input2.result.stopReason ? { stopReason: input2.result.stopReason } : {},
|
|
211288
211891
|
timestamp: new Date().toISOString()
|
|
211289
211892
|
});
|
|
211290
211893
|
}
|
|
211894
|
+
function buildAgentStartedEvent(input2) {
|
|
211895
|
+
const executionIdentity = normalizeModelExecutionIdentity(input2.executionIdentity);
|
|
211896
|
+
return {
|
|
211897
|
+
type: "agent_started",
|
|
211898
|
+
traceId: input2.traceId,
|
|
211899
|
+
agent: input2.agent,
|
|
211900
|
+
role: input2.role,
|
|
211901
|
+
...executionIdentity ? { executionIdentity } : {},
|
|
211902
|
+
...executionIdentity?.requestedModel ? { model: executionIdentity.requestedModel } : {},
|
|
211903
|
+
...executionIdentity?.providerRoute === "openrouter" ? { provider: "openrouter" } : {},
|
|
211904
|
+
timestamp: new Date().toISOString()
|
|
211905
|
+
};
|
|
211906
|
+
}
|
|
211907
|
+
function resolveOutputByteMetrics(result) {
|
|
211908
|
+
const rawTextBytes = result.rawText ? Buffer.byteLength(result.rawText, "utf8") : undefined;
|
|
211909
|
+
let messageBytes = result.messageBytes;
|
|
211910
|
+
let thoughtBytes = result.thoughtBytes;
|
|
211911
|
+
if (messageBytes === undefined && thoughtBytes === undefined) {
|
|
211912
|
+
if (rawTextBytes !== undefined) {
|
|
211913
|
+
messageBytes = rawTextBytes;
|
|
211914
|
+
thoughtBytes = result.outputBytes === undefined ? 0 : Math.max(0, result.outputBytes - rawTextBytes);
|
|
211915
|
+
} else if (result.outputBytes !== undefined) {
|
|
211916
|
+
messageBytes = result.outputBytes;
|
|
211917
|
+
thoughtBytes = 0;
|
|
211918
|
+
}
|
|
211919
|
+
} else if (messageBytes === undefined) {
|
|
211920
|
+
messageBytes = result.outputBytes === undefined ? rawTextBytes ?? 0 : Math.max(0, result.outputBytes - (thoughtBytes ?? 0));
|
|
211921
|
+
} else if (thoughtBytes === undefined) {
|
|
211922
|
+
thoughtBytes = result.outputBytes === undefined ? 0 : Math.max(0, result.outputBytes - messageBytes);
|
|
211923
|
+
}
|
|
211924
|
+
return {
|
|
211925
|
+
...messageBytes === undefined ? {} : { messageBytes },
|
|
211926
|
+
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
211927
|
+
...messageBytes === undefined || thoughtBytes === undefined ? {} : { outputBytes: messageBytes + thoughtBytes }
|
|
211928
|
+
};
|
|
211929
|
+
}
|
|
211291
211930
|
function isPreflightAgentFailure(result) {
|
|
211292
211931
|
return result.status === "failed" && [
|
|
211293
211932
|
"AGENT_CONFIG_INVALID",
|
|
@@ -211365,24 +212004,11 @@ function normalizeAgentRunResult(result, maxFindingsPerAgent) {
|
|
|
211365
212004
|
...result,
|
|
211366
212005
|
normalized: normalizeAgentOutput(result.agent, result.role, result.rawText)
|
|
211367
212006
|
} : result;
|
|
211368
|
-
const
|
|
211369
|
-
|
|
211370
|
-
|
|
211371
|
-
|
|
211372
|
-
|
|
211373
|
-
const limitedFindings = findings.map((finding, index) => ({ finding, index })).sort((left, right) => {
|
|
211374
|
-
const severity = compareSeverity(left.finding.severity, right.finding.severity);
|
|
211375
|
-
return severity === 0 ? left.index - right.index : severity;
|
|
211376
|
-
}).slice(0, maxFindingsPerAgent).map(({ finding }) => finding);
|
|
211377
|
-
return {
|
|
211378
|
-
result: {
|
|
211379
|
-
...normalizedResult,
|
|
211380
|
-
normalized: {
|
|
211381
|
-
...normalized,
|
|
211382
|
-
findings: limitedFindings
|
|
211383
|
-
}
|
|
211384
|
-
},
|
|
211385
|
-
findingsCapped: true
|
|
212007
|
+
const reportedFindings = normalizedResult.normalized?.findings.length;
|
|
212008
|
+
return reportedFindings === undefined ? normalizedResult : {
|
|
212009
|
+
...normalizedResult,
|
|
212010
|
+
reportedFindings,
|
|
212011
|
+
findingsTargetExceeded: reportedFindings > maxFindingsPerAgent
|
|
211386
212012
|
};
|
|
211387
212013
|
}
|
|
211388
212014
|
function agentOpinionSummary(result, includeRawText = false) {
|
|
@@ -211391,7 +212017,8 @@ function agentOpinionSummary(result, includeRawText = false) {
|
|
|
211391
212017
|
role: result.role,
|
|
211392
212018
|
summary: result.normalized?.summary ?? sanitizeTextForDisplay(result.error?.message ?? result.status),
|
|
211393
212019
|
status: result.status,
|
|
211394
|
-
errorCode: result.error?.code
|
|
212020
|
+
errorCode: result.error?.code,
|
|
212021
|
+
...result.salvaged === undefined ? {} : { salvaged: result.salvaged }
|
|
211395
212022
|
};
|
|
211396
212023
|
if (includeRawText && result.rawText) {
|
|
211397
212024
|
opinion.rawText = sanitizeTextForRawOutput(result.rawText);
|
|
@@ -211593,12 +212220,60 @@ async function writeReviewBudgetPlanned(input2) {
|
|
|
211593
212220
|
requestFingerprint: input2.requestFingerprint,
|
|
211594
212221
|
maxModelCalls: snapshot.executionBudget.maxModelCalls,
|
|
211595
212222
|
maxTotalWallTimeMs: snapshot.executionBudget.wallTime.limitMs,
|
|
212223
|
+
...snapshot.executionBudget.effectiveWarnAgentOutputBytes !== undefined ? {
|
|
212224
|
+
effectiveWarnAgentOutputBytes: snapshot.executionBudget.effectiveWarnAgentOutputBytes
|
|
212225
|
+
} : {},
|
|
211596
212226
|
maxAgentOutputBytes: snapshot.executionBudget.maxAgentOutputBytes,
|
|
211597
212227
|
maxFindingsPerAgent: snapshot.executionBudget.maxFindingsPerAgent,
|
|
211598
212228
|
skipOptionalPhasesWhenTokenUsageUnknown: snapshot.executionBudget.skipOptionalPhasesWhenTokenUsageUnknown,
|
|
212229
|
+
...snapshot.executionBudget.modelCallPlan,
|
|
211599
212230
|
timestamp: new Date().toISOString()
|
|
211600
212231
|
});
|
|
211601
212232
|
}
|
|
212233
|
+
function plannedBudgetWarnings(budgetTracker) {
|
|
212234
|
+
const fallbackCalls = budgetTracker.modelCallPlan.ceilingEffects.filter((effect) => effect.kind === "judge" && effect.action === "deterministic_fallback" && effect.reason === "model_call_budget").reduce((total, effect) => total + effect.calls, 0);
|
|
212235
|
+
if (fallbackCalls === 0)
|
|
212236
|
+
return [];
|
|
212237
|
+
return [
|
|
212238
|
+
`The potential model-call plan requires ${budgetTracker.modelCallPlan.potentialTotalCalls} calls, above maxModelCalls=${budgetTracker.budget.maxModelCalls}; ${fallbackCalls} LLM judge call(s) will use deterministic fallback if higher-priority calls consume the available capacity.`
|
|
212239
|
+
];
|
|
212240
|
+
}
|
|
212241
|
+
function outputWarningMessages(snapshot) {
|
|
212242
|
+
const threshold = snapshot.executionBudget.effectiveWarnAgentOutputBytes;
|
|
212243
|
+
if (threshold === undefined)
|
|
212244
|
+
return [];
|
|
212245
|
+
return snapshot.modelCalls.flatMap((call) => {
|
|
212246
|
+
if (call.status !== "completed" || !call.outputWarningTriggered || !call.agent) {
|
|
212247
|
+
return [];
|
|
212248
|
+
}
|
|
212249
|
+
const messageBytes = call.messageBytes ?? 0;
|
|
212250
|
+
const thoughtBytes = call.thoughtBytes ?? 0;
|
|
212251
|
+
const outputBytes = call.outputBytes ?? messageBytes + thoughtBytes;
|
|
212252
|
+
const outcome = call.stopReason === "cancelled" ? "the hard breaker subsequently stopped execution." : "execution continued.";
|
|
212253
|
+
return [
|
|
212254
|
+
`Agent ${call.agent} ${call.kind} output reached the ${threshold}-byte soft threshold (message: ${messageBytes}, thought: ${thoughtBytes}, total: ${outputBytes}); ${outcome}`
|
|
212255
|
+
];
|
|
212256
|
+
});
|
|
212257
|
+
}
|
|
212258
|
+
function tokenUsageWarningMessages(budgetTracker, snapshot) {
|
|
212259
|
+
const unknownCalls = snapshot.executionBudget.tokenUsage.unknownCalls;
|
|
212260
|
+
if (budgetTracker.budget.skipOptionalPhasesWhenTokenUsageUnknown || unknownCalls === 0) {
|
|
212261
|
+
return [];
|
|
212262
|
+
}
|
|
212263
|
+
return [
|
|
212264
|
+
`Token usage was not reported for ${unknownCalls} completed call(s); budget enforcement continued using calls, wall time, and bytes.`
|
|
212265
|
+
];
|
|
212266
|
+
}
|
|
212267
|
+
function configuredReviewModelCallPlan(config2, budget, env, requestedJudgeProvider) {
|
|
212268
|
+
const judgeRoute = resolveJudgeCallRoute(config2.judge.mode, requestedJudgeProvider ?? config2.judge.provider, env);
|
|
212269
|
+
return buildReviewModelCallPlan({
|
|
212270
|
+
maxModelCalls: budget.maxModelCalls,
|
|
212271
|
+
requiredPrimaryCalls: Object.values(config2.agents).filter((agent) => agent.enabled).length,
|
|
212272
|
+
verificationEnabled: config2.verification.enabled,
|
|
212273
|
+
verificationMaxFindings: config2.verification.maxFindings,
|
|
212274
|
+
llmJudgeAvailable: judgeRoute.llmAvailable
|
|
212275
|
+
});
|
|
212276
|
+
}
|
|
211602
212277
|
async function writeReviewBudgetCompleted(input2) {
|
|
211603
212278
|
const snapshot = input2.budgetTracker.snapshot();
|
|
211604
212279
|
await input2.trace.write({
|