@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/index.js
CHANGED
|
@@ -183780,7 +183780,8 @@ function date4(params) {
|
|
|
183780
183780
|
// node_modules/zod/v4/classic/external.js
|
|
183781
183781
|
config(en_default());
|
|
183782
183782
|
// src/core/constants.ts
|
|
183783
|
-
var DEFAULT_AGENT_TIMEOUT_MS =
|
|
183783
|
+
var DEFAULT_AGENT_TIMEOUT_MS = 600000;
|
|
183784
|
+
var DEFAULT_WARN_AGENT_OUTPUT_BYTES = 524288;
|
|
183784
183785
|
var MAX_AGENT_OUTPUT_BYTES = 1048576;
|
|
183785
183786
|
var JUDGE_MAX_OUTPUT_TOKENS = 4096;
|
|
183786
183787
|
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
@@ -183929,7 +183930,7 @@ var baseAgentSchema = exports_external.object({
|
|
|
183929
183930
|
"architecture_security_reviewer",
|
|
183930
183931
|
"combined_reviewer"
|
|
183931
183932
|
]),
|
|
183932
|
-
timeoutMs: exports_external.number().int().positive().default(
|
|
183933
|
+
timeoutMs: exports_external.number().int().positive().default(DEFAULT_AGENT_TIMEOUT_MS),
|
|
183933
183934
|
env: exports_external.record(exports_external.string(), exports_external.string()).default({}),
|
|
183934
183935
|
auth: exports_external.object({
|
|
183935
183936
|
mode: exports_external.literal("passthrough").default("passthrough"),
|
|
@@ -183960,6 +183961,7 @@ var codexAgentSchema = baseAgentSchema.extend({
|
|
|
183960
183961
|
var reviewBudgetSchema = exports_external.object({
|
|
183961
183962
|
maxModelCalls: exports_external.number().int().positive(),
|
|
183962
183963
|
maxTotalWallTimeMs: exports_external.number().int().positive(),
|
|
183964
|
+
warnAgentOutputBytes: exports_external.number().int().positive().max(MAX_AGENT_OUTPUT_BYTES),
|
|
183963
183965
|
maxAgentOutputBytes: exports_external.number().int().positive().max(MAX_AGENT_OUTPUT_BYTES),
|
|
183964
183966
|
maxFindingsPerAgent: exports_external.number().int().positive(),
|
|
183965
183967
|
skipOptionalPhasesWhenTokenUsageUnknown: exports_external.boolean()
|
|
@@ -184035,13 +184037,21 @@ var kyosoConfigSchema = exports_external.object({
|
|
|
184035
184037
|
})
|
|
184036
184038
|
}).superRefine((config2, context) => {
|
|
184037
184039
|
const enabledPrimaryReviewers = Object.values(config2.agents).filter((agent) => agent.enabled).length;
|
|
184038
|
-
if (config2.reviewBudget.maxModelCalls
|
|
184039
|
-
|
|
184040
|
-
|
|
184041
|
-
|
|
184042
|
-
|
|
184043
|
-
|
|
184044
|
-
}
|
|
184040
|
+
if (config2.reviewBudget.maxModelCalls < enabledPrimaryReviewers) {
|
|
184041
|
+
context.addIssue({
|
|
184042
|
+
code: exports_external.ZodIssueCode.custom,
|
|
184043
|
+
path: ["reviewBudget", "maxModelCalls"],
|
|
184044
|
+
message: "must be greater than or equal to the number of enabled primary reviewers."
|
|
184045
|
+
});
|
|
184046
|
+
}
|
|
184047
|
+
const inheritedLegacyHardLimit = config2.reviewBudget.warnAgentOutputBytes === DEFAULT_WARN_AGENT_OUTPUT_BYTES && config2.reviewBudget.maxAgentOutputBytes <= DEFAULT_WARN_AGENT_OUTPUT_BYTES;
|
|
184048
|
+
if (config2.reviewBudget.warnAgentOutputBytes >= config2.reviewBudget.maxAgentOutputBytes && !inheritedLegacyHardLimit) {
|
|
184049
|
+
context.addIssue({
|
|
184050
|
+
code: exports_external.ZodIssueCode.custom,
|
|
184051
|
+
path: ["reviewBudget", "warnAgentOutputBytes"],
|
|
184052
|
+
message: "must be less than reviewBudget.maxAgentOutputBytes."
|
|
184053
|
+
});
|
|
184054
|
+
}
|
|
184045
184055
|
});
|
|
184046
184056
|
function agentConfigLeafPaths(agent) {
|
|
184047
184057
|
const paths = [
|
|
@@ -184104,6 +184114,7 @@ var kyosoConfigKnownLeafPaths = [
|
|
|
184104
184114
|
"verification.allowDemotion",
|
|
184105
184115
|
"reviewBudget.maxModelCalls",
|
|
184106
184116
|
"reviewBudget.maxTotalWallTimeMs",
|
|
184117
|
+
"reviewBudget.warnAgentOutputBytes",
|
|
184107
184118
|
"reviewBudget.maxAgentOutputBytes",
|
|
184108
184119
|
"reviewBudget.maxFindingsPerAgent",
|
|
184109
184120
|
"reviewBudget.skipOptionalPhasesWhenTokenUsageUnknown",
|
|
@@ -184149,9 +184160,9 @@ var defaultConfig = {
|
|
|
184149
184160
|
enabled: true,
|
|
184150
184161
|
type: "acp",
|
|
184151
184162
|
command: "npx",
|
|
184152
|
-
args: ["-y", "@agentclientprotocol/codex-acp@1.1.
|
|
184163
|
+
args: ["-y", "@agentclientprotocol/codex-acp@1.1.4"],
|
|
184153
184164
|
role: "implementation_reviewer",
|
|
184154
|
-
timeoutMs:
|
|
184165
|
+
timeoutMs: DEFAULT_AGENT_TIMEOUT_MS,
|
|
184155
184166
|
allowProjectProvider: [],
|
|
184156
184167
|
env: {
|
|
184157
184168
|
INITIAL_AGENT_MODE: "read-only",
|
|
@@ -184176,7 +184187,7 @@ var defaultConfig = {
|
|
|
184176
184187
|
command: "npx",
|
|
184177
184188
|
args: ["-y", "@agentclientprotocol/claude-agent-acp@0.58.1"],
|
|
184178
184189
|
role: "architecture_security_reviewer",
|
|
184179
|
-
timeoutMs:
|
|
184190
|
+
timeoutMs: DEFAULT_AGENT_TIMEOUT_MS,
|
|
184180
184191
|
env: {
|
|
184181
184192
|
KYOSO_CHILD_AGENT: "1"
|
|
184182
184193
|
},
|
|
@@ -184257,10 +184268,11 @@ var defaultConfig = {
|
|
|
184257
184268
|
},
|
|
184258
184269
|
reviewBudget: {
|
|
184259
184270
|
maxModelCalls: 4,
|
|
184260
|
-
maxTotalWallTimeMs:
|
|
184261
|
-
|
|
184271
|
+
maxTotalWallTimeMs: 660000,
|
|
184272
|
+
warnAgentOutputBytes: DEFAULT_WARN_AGENT_OUTPUT_BYTES,
|
|
184273
|
+
maxAgentOutputBytes: 1048576,
|
|
184262
184274
|
maxFindingsPerAgent: 10,
|
|
184263
|
-
skipOptionalPhasesWhenTokenUsageUnknown:
|
|
184275
|
+
skipOptionalPhasesWhenTokenUsageUnknown: false
|
|
184264
184276
|
},
|
|
184265
184277
|
audit: {
|
|
184266
184278
|
enabled: true,
|
|
@@ -185576,6 +185588,7 @@ async function loadConfig(options = {}) {
|
|
|
185576
185588
|
if (!options.ignoreConfig) {
|
|
185577
185589
|
if (await exists(globalConfigPath)) {
|
|
185578
185590
|
const globalConfig2 = await loadTomlConfigFile(globalConfigPath);
|
|
185591
|
+
validateExplicitReviewBudgetThresholds(globalConfig2, defaultConfig);
|
|
185579
185592
|
const globalConfigWarnings = collectGlobalConfigWarnings(globalConfigPath, globalConfig2);
|
|
185580
185593
|
const securitySensitiveWarnings = globalConfigWarnings.filter((warning) => warning.startsWith("security-sensitive unknown settings "));
|
|
185581
185594
|
if (securitySensitiveWarnings.length > 0 && !options.allowUnknownConfig) {
|
|
@@ -185824,6 +185837,7 @@ async function loadProjectTsConfig(input) {
|
|
|
185824
185837
|
});
|
|
185825
185838
|
if (trustDecision.execute) {
|
|
185826
185839
|
const userConfig = await loadUserConfig(canonicalPath, source);
|
|
185840
|
+
validateExplicitReviewBudgetThresholds(userConfig, input.baseConfig);
|
|
185827
185841
|
const projectChangesOpenRouterRoute = projectConfigChangesOpenRouterRoute(userConfig, input.baseConfig);
|
|
185828
185842
|
await assertProjectOpenRouterAuthorization({
|
|
185829
185843
|
projectConfig: userConfig,
|
|
@@ -185917,6 +185931,23 @@ function deepMerge2(base, override) {
|
|
|
185917
185931
|
}
|
|
185918
185932
|
return result;
|
|
185919
185933
|
}
|
|
185934
|
+
function validateExplicitReviewBudgetThresholds(config2, baseConfig) {
|
|
185935
|
+
const reviewBudget = readRecord(config2, "reviewBudget");
|
|
185936
|
+
if (!reviewBudget || !Object.prototype.hasOwnProperty.call(reviewBudget, "warnAgentOutputBytes")) {
|
|
185937
|
+
return;
|
|
185938
|
+
}
|
|
185939
|
+
const warnAgentOutputBytes = reviewBudget.warnAgentOutputBytes;
|
|
185940
|
+
const maxAgentOutputBytes = Object.prototype.hasOwnProperty.call(reviewBudget, "maxAgentOutputBytes") ? reviewBudget.maxAgentOutputBytes : readRecord(baseConfig, "reviewBudget")?.maxAgentOutputBytes;
|
|
185941
|
+
if (typeof warnAgentOutputBytes === "number" && typeof maxAgentOutputBytes === "number" && warnAgentOutputBytes >= maxAgentOutputBytes) {
|
|
185942
|
+
throw new Error("reviewBudget.warnAgentOutputBytes must be less than reviewBudget.maxAgentOutputBytes.");
|
|
185943
|
+
}
|
|
185944
|
+
}
|
|
185945
|
+
function readRecord(value, key) {
|
|
185946
|
+
if (!isRecord3(value))
|
|
185947
|
+
return;
|
|
185948
|
+
const nested = value[key];
|
|
185949
|
+
return isRecord3(nested) ? nested : undefined;
|
|
185950
|
+
}
|
|
185920
185951
|
function isRecord3(value) {
|
|
185921
185952
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
185922
185953
|
}
|
|
@@ -189972,6 +190003,55 @@ var legacyClientNotificationMethods = new Set([
|
|
|
189972
190003
|
CLIENT_METHODS.elicitation_complete
|
|
189973
190004
|
]);
|
|
189974
190005
|
|
|
190006
|
+
// src/core/modelExecutionIdentity.ts
|
|
190007
|
+
var MODEL_EXECUTION_IDENTITY_MAX_CHARS = 160;
|
|
190008
|
+
var MODEL_PROVIDER_ROUTES = new Set([
|
|
190009
|
+
"codex_default",
|
|
190010
|
+
"claude_default",
|
|
190011
|
+
"openrouter",
|
|
190012
|
+
"openai",
|
|
190013
|
+
"anthropic"
|
|
190014
|
+
]);
|
|
190015
|
+
function createModelExecutionIdentity(input) {
|
|
190016
|
+
const requestedModel = sanitizeIdentityValue(input.requestedModel);
|
|
190017
|
+
const reportedProvider = sanitizeIdentityValue(input.reportedProvider);
|
|
190018
|
+
const reportedModel = sanitizeIdentityValue(input.reportedModel);
|
|
190019
|
+
const reportingStatus = reportedProvider !== undefined || reportedModel !== undefined ? "reported" : requestedModel !== undefined ? "requested_only" : "unknown";
|
|
190020
|
+
return {
|
|
190021
|
+
providerRoute: input.providerRoute,
|
|
190022
|
+
...requestedModel ? { requestedModel } : {},
|
|
190023
|
+
...reportedProvider ? { reportedProvider } : {},
|
|
190024
|
+
...reportedModel ? { reportedModel } : {},
|
|
190025
|
+
reportingStatus
|
|
190026
|
+
};
|
|
190027
|
+
}
|
|
190028
|
+
function normalizeModelExecutionIdentity(value) {
|
|
190029
|
+
if (!isRecord6(value) || !isModelProviderRoute(value.providerRoute)) {
|
|
190030
|
+
return;
|
|
190031
|
+
}
|
|
190032
|
+
return createModelExecutionIdentity({
|
|
190033
|
+
providerRoute: value.providerRoute,
|
|
190034
|
+
requestedModel: value.requestedModel,
|
|
190035
|
+
reportedProvider: value.reportedProvider,
|
|
190036
|
+
reportedModel: value.reportedModel
|
|
190037
|
+
});
|
|
190038
|
+
}
|
|
190039
|
+
function sanitizeIdentityValue(value) {
|
|
190040
|
+
if (typeof value !== "string")
|
|
190041
|
+
return;
|
|
190042
|
+
const sanitized = sanitizeTextForDisplay(value, MODEL_EXECUTION_IDENTITY_MAX_CHARS);
|
|
190043
|
+
if (sanitized.includes(REDACTION) || /(?:https?|wss?):\/\//i.test(sanitized) || /\b(?:api[_-]?key|base[_-]?url|credential|secret|token|password)\b/i.test(sanitized) || /[{}=]/.test(sanitized)) {
|
|
190044
|
+
return;
|
|
190045
|
+
}
|
|
190046
|
+
return sanitized.length > 0 ? sanitized : undefined;
|
|
190047
|
+
}
|
|
190048
|
+
function isModelProviderRoute(value) {
|
|
190049
|
+
return typeof value === "string" && MODEL_PROVIDER_ROUTES.has(value);
|
|
190050
|
+
}
|
|
190051
|
+
function isRecord6(value) {
|
|
190052
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
190053
|
+
}
|
|
190054
|
+
|
|
189975
190055
|
// src/core/tokenUsage.ts
|
|
189976
190056
|
var TOKEN_USAGE_KEYS = [
|
|
189977
190057
|
"totalTokens",
|
|
@@ -189982,7 +190062,7 @@ var TOKEN_USAGE_KEYS = [
|
|
|
189982
190062
|
"cachedWriteTokens"
|
|
189983
190063
|
];
|
|
189984
190064
|
function normalizeModelTokenUsage(usage) {
|
|
189985
|
-
if (!
|
|
190065
|
+
if (!isRecord7(usage))
|
|
189986
190066
|
return;
|
|
189987
190067
|
const normalized = {};
|
|
189988
190068
|
for (const key of TOKEN_USAGE_KEYS) {
|
|
@@ -189995,7 +190075,7 @@ function normalizeModelTokenUsage(usage) {
|
|
|
189995
190075
|
function isTokenCount(value) {
|
|
189996
190076
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
189997
190077
|
}
|
|
189998
|
-
function
|
|
190078
|
+
function isRecord7(value) {
|
|
189999
190079
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
190000
190080
|
}
|
|
190001
190081
|
|
|
@@ -190048,7 +190128,19 @@ class ChildEnvPreflightError extends Error {
|
|
|
190048
190128
|
this.name = "ChildEnvPreflightError";
|
|
190049
190129
|
}
|
|
190050
190130
|
}
|
|
190051
|
-
function
|
|
190131
|
+
function buildChildLaunchContext(parentEnv, whitelist, explicit, options) {
|
|
190132
|
+
const env = buildChildEnvironment(parentEnv, whitelist, explicit, options);
|
|
190133
|
+
const openRouterSelected = options.agent === "codex" && options.provider === CODEX_OPENROUTER_PROVIDER;
|
|
190134
|
+
const requestedModel = options.agent === "claude" ? env.ANTHROPIC_MODEL : readCodexRequestedModel(env.CODEX_CONFIG);
|
|
190135
|
+
return {
|
|
190136
|
+
env,
|
|
190137
|
+
executionIdentity: createModelExecutionIdentity({
|
|
190138
|
+
providerRoute: openRouterSelected ? "openrouter" : options.agent === "codex" ? "codex_default" : "claude_default",
|
|
190139
|
+
requestedModel
|
|
190140
|
+
})
|
|
190141
|
+
};
|
|
190142
|
+
}
|
|
190143
|
+
function buildChildEnvironment(parentEnv, whitelist, explicit, options = {}) {
|
|
190052
190144
|
if (!parentEnv.PATH) {
|
|
190053
190145
|
throw new Error("PATH is required to launch ACP child agents.");
|
|
190054
190146
|
}
|
|
@@ -190095,6 +190187,19 @@ function buildChildEnv(parentEnv, whitelist, explicit, options = {}) {
|
|
|
190095
190187
|
}
|
|
190096
190188
|
return env;
|
|
190097
190189
|
}
|
|
190190
|
+
function readCodexRequestedModel(value) {
|
|
190191
|
+
if (!value)
|
|
190192
|
+
return;
|
|
190193
|
+
try {
|
|
190194
|
+
const parsed = JSON.parse(value);
|
|
190195
|
+
if (!isPlainObject2(parsed) || typeof parsed.model !== "string") {
|
|
190196
|
+
return;
|
|
190197
|
+
}
|
|
190198
|
+
return parsed.model;
|
|
190199
|
+
} catch {
|
|
190200
|
+
return;
|
|
190201
|
+
}
|
|
190202
|
+
}
|
|
190098
190203
|
function canCopyEnvValue(key, value, onCredentialPlaceholderDiscarded) {
|
|
190099
190204
|
if (!isUnexpandedCredentialEnvValue(key, value))
|
|
190100
190205
|
return true;
|
|
@@ -190235,6 +190340,45 @@ class BaseAcpAgentManager {
|
|
|
190235
190340
|
}
|
|
190236
190341
|
}
|
|
190237
190342
|
|
|
190343
|
+
// src/acp/ndJsonLineLimit.ts
|
|
190344
|
+
var JSON_STRING_MAX_ESCAPE_EXPANSION = 6;
|
|
190345
|
+
var ACP_NDJSON_ENVELOPE_BYTES = 2 * 1048576;
|
|
190346
|
+
var NEWLINE_BYTE = 10;
|
|
190347
|
+
var MAX_ACP_NDJSON_LINE_BYTES = MAX_AGENT_OUTPUT_BYTES * JSON_STRING_MAX_ESCAPE_EXPANSION + ACP_NDJSON_ENVELOPE_BYTES;
|
|
190348
|
+
|
|
190349
|
+
class AcpNdJsonLineLimitError extends Error {
|
|
190350
|
+
maxLineBytes;
|
|
190351
|
+
constructor(maxLineBytes) {
|
|
190352
|
+
super(`ACP NDJSON line exceeded ${maxLineBytes} bytes.`);
|
|
190353
|
+
this.maxLineBytes = maxLineBytes;
|
|
190354
|
+
this.name = "AcpNdJsonLineLimitError";
|
|
190355
|
+
}
|
|
190356
|
+
}
|
|
190357
|
+
function limitAcpNdJsonLineBytes(input, maxLineBytes = MAX_ACP_NDJSON_LINE_BYTES) {
|
|
190358
|
+
if (!Number.isSafeInteger(maxLineBytes) || maxLineBytes <= 0) {
|
|
190359
|
+
throw new RangeError("ACP NDJSON line limit must be a positive integer.");
|
|
190360
|
+
}
|
|
190361
|
+
let pendingLineBytes = 0;
|
|
190362
|
+
return input.pipeThrough(new TransformStream({
|
|
190363
|
+
transform(chunk, controller) {
|
|
190364
|
+
let start = 0;
|
|
190365
|
+
for (;; ) {
|
|
190366
|
+
const newlineIndex = chunk.indexOf(NEWLINE_BYTE, start);
|
|
190367
|
+
const end = newlineIndex === -1 ? chunk.byteLength : newlineIndex;
|
|
190368
|
+
pendingLineBytes += end - start;
|
|
190369
|
+
if (pendingLineBytes > maxLineBytes) {
|
|
190370
|
+
throw new AcpNdJsonLineLimitError(maxLineBytes);
|
|
190371
|
+
}
|
|
190372
|
+
if (newlineIndex === -1)
|
|
190373
|
+
break;
|
|
190374
|
+
pendingLineBytes = 0;
|
|
190375
|
+
start = newlineIndex + 1;
|
|
190376
|
+
}
|
|
190377
|
+
controller.enqueue(chunk);
|
|
190378
|
+
}
|
|
190379
|
+
}));
|
|
190380
|
+
}
|
|
190381
|
+
|
|
190238
190382
|
// src/core/findingAdmission.ts
|
|
190239
190383
|
import { createHash as createHash2 } from "node:crypto";
|
|
190240
190384
|
var SAFETY_CATEGORIES = new Set([
|
|
@@ -190576,6 +190720,43 @@ var evidenceQualities = [
|
|
|
190576
190720
|
];
|
|
190577
190721
|
var MAX_EVIDENCE_REFS2 = 20;
|
|
190578
190722
|
var MAX_EVIDENCE_LINE2 = 1e6;
|
|
190723
|
+
var STRICT_ROOT_KEYS = new Set([
|
|
190724
|
+
"summary",
|
|
190725
|
+
"findings",
|
|
190726
|
+
"testsToAdd",
|
|
190727
|
+
"residualRisks",
|
|
190728
|
+
"openQuestions",
|
|
190729
|
+
"cisaSecureByDesign"
|
|
190730
|
+
]);
|
|
190731
|
+
var STRICT_FINDING_KEYS = new Set([
|
|
190732
|
+
"severity",
|
|
190733
|
+
"category",
|
|
190734
|
+
"title",
|
|
190735
|
+
"evidence",
|
|
190736
|
+
"recommendation",
|
|
190737
|
+
"disposition",
|
|
190738
|
+
"changeRelation",
|
|
190739
|
+
"evidenceQuality",
|
|
190740
|
+
"evidenceRefs",
|
|
190741
|
+
"files",
|
|
190742
|
+
"confidence",
|
|
190743
|
+
"cisaMapping"
|
|
190744
|
+
]);
|
|
190745
|
+
var STRICT_FILE_KEYS = new Set(["path", "lineStart", "lineEnd"]);
|
|
190746
|
+
var STRICT_EVIDENCE_REF_KEYS = new Set([
|
|
190747
|
+
"kind",
|
|
190748
|
+
"path",
|
|
190749
|
+
"lineStart",
|
|
190750
|
+
"lineEnd",
|
|
190751
|
+
"label"
|
|
190752
|
+
]);
|
|
190753
|
+
var STRICT_CISA_KEYS = new Set([
|
|
190754
|
+
"customerSecurityOutcomes",
|
|
190755
|
+
"secureByDefault",
|
|
190756
|
+
"transparencyAndAccountability",
|
|
190757
|
+
"governance",
|
|
190758
|
+
"notes"
|
|
190759
|
+
]);
|
|
190579
190760
|
function normalizeAgentOutput(agent, role, rawText) {
|
|
190580
190761
|
const json2 = extractFirstJsonObject(rawText);
|
|
190581
190762
|
if (!json2)
|
|
@@ -190609,6 +190790,19 @@ function normalizeAgentOutput(agent, role, rawText) {
|
|
|
190609
190790
|
return parseFailureOpinion(agent, role, `Structured parse failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
190610
190791
|
}
|
|
190611
190792
|
}
|
|
190793
|
+
function parseAgentOutputStrict(agent, role, rawText) {
|
|
190794
|
+
const json2 = extractFirstJsonObject(rawText);
|
|
190795
|
+
if (!json2)
|
|
190796
|
+
return;
|
|
190797
|
+
try {
|
|
190798
|
+
const parsed = JSON.parse(json2);
|
|
190799
|
+
if (!isStrictAgentOpinion(parsed))
|
|
190800
|
+
return;
|
|
190801
|
+
return normalizeAgentOutput(agent, role, json2);
|
|
190802
|
+
} catch {
|
|
190803
|
+
return;
|
|
190804
|
+
}
|
|
190805
|
+
}
|
|
190612
190806
|
function extractFirstJsonObject(text) {
|
|
190613
190807
|
const start = text.indexOf("{");
|
|
190614
190808
|
if (start === -1)
|
|
@@ -190666,7 +190860,7 @@ function isSeverity(value) {
|
|
|
190666
190860
|
return typeof value === "string" && severities.includes(value);
|
|
190667
190861
|
}
|
|
190668
190862
|
function normalizeCisaSecureByDesign(value) {
|
|
190669
|
-
if (!
|
|
190863
|
+
if (!isRecord8(value))
|
|
190670
190864
|
return;
|
|
190671
190865
|
const normalized = {};
|
|
190672
190866
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -190706,6 +190900,78 @@ function isChangeRelation(value) {
|
|
|
190706
190900
|
function isEvidenceQuality(value) {
|
|
190707
190901
|
return typeof value === "string" && evidenceQualities.includes(value);
|
|
190708
190902
|
}
|
|
190903
|
+
function isStrictAgentOpinion(value) {
|
|
190904
|
+
if (!isRecord8(value) || !hasOnlyKeys(value, STRICT_ROOT_KEYS))
|
|
190905
|
+
return false;
|
|
190906
|
+
if (typeof value.summary !== "string")
|
|
190907
|
+
return false;
|
|
190908
|
+
if (!Array.isArray(value.findings) || !value.findings.every(isStrictFinding) || !isStringArray(value.testsToAdd) || !isStringArray(value.residualRisks) || !isStringArray(value.openQuestions)) {
|
|
190909
|
+
return false;
|
|
190910
|
+
}
|
|
190911
|
+
return value.cisaSecureByDesign === undefined || isStrictCisaSecureByDesign(value.cisaSecureByDesign);
|
|
190912
|
+
}
|
|
190913
|
+
function isStrictFinding(value) {
|
|
190914
|
+
if (!isRecord8(value) || !hasOnlyKeys(value, STRICT_FINDING_KEYS)) {
|
|
190915
|
+
return false;
|
|
190916
|
+
}
|
|
190917
|
+
if (!isSeverity(value.severity) || !isCategory(value.category) || !isNonEmptyString(value.title) || !isNonEmptyString(value.evidence) || !isNonEmptyString(value.recommendation) || !isConfidence(value.confidence)) {
|
|
190918
|
+
return false;
|
|
190919
|
+
}
|
|
190920
|
+
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)) {
|
|
190921
|
+
return false;
|
|
190922
|
+
}
|
|
190923
|
+
return true;
|
|
190924
|
+
}
|
|
190925
|
+
function isStrictFindingFiles(value) {
|
|
190926
|
+
return Array.isArray(value) && value.every((item) => isRecord8(item) && hasOnlyKeys(item, STRICT_FILE_KEYS) && isNonEmptyString(item.path) && isOptionalLineNumber(item.lineStart) && isOptionalLineNumber(item.lineEnd));
|
|
190927
|
+
}
|
|
190928
|
+
function isStrictEvidenceRefs(value) {
|
|
190929
|
+
return Array.isArray(value) && value.length <= MAX_EVIDENCE_REFS2 && value.every((item) => {
|
|
190930
|
+
if (!isRecord8(item) || !hasOnlyKeys(item, STRICT_EVIDENCE_REF_KEYS)) {
|
|
190931
|
+
return false;
|
|
190932
|
+
}
|
|
190933
|
+
if (item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
190934
|
+
return false;
|
|
190935
|
+
}
|
|
190936
|
+
if (!isOptionalNonEmptyString(item.path) || !isOptionalNonEmptyString(item.label) || !isOptionalLineNumber(item.lineStart) || !isOptionalLineNumber(item.lineEnd)) {
|
|
190937
|
+
return false;
|
|
190938
|
+
}
|
|
190939
|
+
if (item.kind === "file" || item.kind === "diff_hunk") {
|
|
190940
|
+
return item.path !== undefined && item.lineStart !== undefined;
|
|
190941
|
+
}
|
|
190942
|
+
return item.label !== undefined || item.lineStart !== undefined;
|
|
190943
|
+
});
|
|
190944
|
+
}
|
|
190945
|
+
function isStrictCisaSecureByDesign(value) {
|
|
190946
|
+
if (!isRecord8(value) || !hasOnlyKeys(value, STRICT_CISA_KEYS))
|
|
190947
|
+
return false;
|
|
190948
|
+
for (const key of [
|
|
190949
|
+
"customerSecurityOutcomes",
|
|
190950
|
+
"secureByDefault",
|
|
190951
|
+
"transparencyAndAccountability",
|
|
190952
|
+
"governance"
|
|
190953
|
+
]) {
|
|
190954
|
+
if (value[key] !== undefined && !normalizeGateStatus(value[key])) {
|
|
190955
|
+
return false;
|
|
190956
|
+
}
|
|
190957
|
+
}
|
|
190958
|
+
return value.notes === undefined || isStringArray(value.notes);
|
|
190959
|
+
}
|
|
190960
|
+
function hasOnlyKeys(value, allowed) {
|
|
190961
|
+
return Object.keys(value).every((key) => allowed.has(key));
|
|
190962
|
+
}
|
|
190963
|
+
function isStringArray(value) {
|
|
190964
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
190965
|
+
}
|
|
190966
|
+
function isNonEmptyString(value) {
|
|
190967
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
190968
|
+
}
|
|
190969
|
+
function isOptionalNonEmptyString(value) {
|
|
190970
|
+
return value === undefined || isNonEmptyString(value);
|
|
190971
|
+
}
|
|
190972
|
+
function isOptionalLineNumber(value) {
|
|
190973
|
+
return value === undefined || normalizeLineNumber(value) !== undefined;
|
|
190974
|
+
}
|
|
190709
190975
|
function normalizeStringList(value) {
|
|
190710
190976
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string").map((item) => sanitizeText(item)) : [];
|
|
190711
190977
|
}
|
|
@@ -190716,7 +190982,7 @@ function normalizeFindingFiles(value) {
|
|
|
190716
190982
|
if (!Array.isArray(value))
|
|
190717
190983
|
return;
|
|
190718
190984
|
const files = value.flatMap((item) => {
|
|
190719
|
-
if (!
|
|
190985
|
+
if (!isRecord8(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
190720
190986
|
return [];
|
|
190721
190987
|
}
|
|
190722
190988
|
const file2 = {
|
|
@@ -190736,7 +191002,7 @@ function normalizeEvidenceRefs2(value) {
|
|
|
190736
191002
|
if (!Array.isArray(value))
|
|
190737
191003
|
return;
|
|
190738
191004
|
const references = value.slice(0, MAX_EVIDENCE_REFS2).flatMap((item) => {
|
|
190739
|
-
if (!
|
|
191005
|
+
if (!isRecord8(item) || item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
190740
191006
|
return [];
|
|
190741
191007
|
}
|
|
190742
191008
|
const reference = { kind: item.kind };
|
|
@@ -190759,7 +191025,7 @@ function normalizeEvidenceRefs2(value) {
|
|
|
190759
191025
|
function normalizeLineNumber(value) {
|
|
190760
191026
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= MAX_EVIDENCE_LINE2 ? value : undefined;
|
|
190761
191027
|
}
|
|
190762
|
-
function
|
|
191028
|
+
function isRecord8(value) {
|
|
190763
191029
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
190764
191030
|
}
|
|
190765
191031
|
|
|
@@ -190785,9 +191051,9 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
190785
191051
|
};
|
|
190786
191052
|
}
|
|
190787
191053
|
const provider = input.agent === "codex" ? this.config.agents.codex.provider : undefined;
|
|
190788
|
-
let
|
|
191054
|
+
let launchContext;
|
|
190789
191055
|
try {
|
|
190790
|
-
|
|
191056
|
+
launchContext = buildChildLaunchContext(this.parentEnv, agentConfig.auth.envWhitelist, agentConfig.env, {
|
|
190791
191057
|
agent: input.agent,
|
|
190792
191058
|
model: agentConfig.model,
|
|
190793
191059
|
provider,
|
|
@@ -190804,7 +191070,7 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
190804
191070
|
};
|
|
190805
191071
|
}
|
|
190806
191072
|
try {
|
|
190807
|
-
return await runSubprocessAgent(input.agent, agentConfig, input, env);
|
|
191073
|
+
return await runSubprocessAgent(input.agent, agentConfig, input, launchContext.env, launchContext.executionIdentity);
|
|
190808
191074
|
} catch (error51) {
|
|
190809
191075
|
return {
|
|
190810
191076
|
agent: input.agent,
|
|
@@ -190817,7 +191083,7 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
190817
191083
|
}
|
|
190818
191084
|
}
|
|
190819
191085
|
}
|
|
190820
|
-
async function runSubprocessAgent(agent, agentConfig, input, env) {
|
|
191086
|
+
async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutionIdentity) {
|
|
190821
191087
|
const startedAt = new Date().toISOString();
|
|
190822
191088
|
const effectiveTimeoutMs = resolveEffectiveTimeoutMs(input);
|
|
190823
191089
|
if (effectiveTimeoutMs <= 0) {
|
|
@@ -190842,11 +191108,15 @@ async function runSubprocessAgent(agent, agentConfig, input, env) {
|
|
|
190842
191108
|
let stdout = "";
|
|
190843
191109
|
let stderr3 = "";
|
|
190844
191110
|
let settled = false;
|
|
191111
|
+
let spawned = false;
|
|
190845
191112
|
let startedWrite;
|
|
190846
191113
|
child.once("spawn", () => {
|
|
190847
191114
|
if (settled)
|
|
190848
191115
|
return;
|
|
190849
|
-
|
|
191116
|
+
spawned = true;
|
|
191117
|
+
startedWrite = Promise.resolve().then(async () => {
|
|
191118
|
+
await input.onStarted?.(launchExecutionIdentity);
|
|
191119
|
+
}).catch(() => {
|
|
190850
191120
|
return;
|
|
190851
191121
|
});
|
|
190852
191122
|
});
|
|
@@ -190856,7 +191126,8 @@ async function runSubprocessAgent(agent, agentConfig, input, env) {
|
|
|
190856
191126
|
return;
|
|
190857
191127
|
settled = true;
|
|
190858
191128
|
clearTimeout(timeout);
|
|
190859
|
-
|
|
191129
|
+
const finalResult = spawned && result.executionIdentity === undefined ? { ...result, executionIdentity: launchExecutionIdentity } : result;
|
|
191130
|
+
(startedWrite ?? Promise.resolve()).then(() => resolveResult(finalResult));
|
|
190860
191131
|
};
|
|
190861
191132
|
const timeout = setTimeout(() => {
|
|
190862
191133
|
abortController.abort(new Error("Kyoso agent timeout"));
|
|
@@ -190889,7 +191160,17 @@ async function runSubprocessAgent(agent, agentConfig, input, env) {
|
|
|
190889
191160
|
error: failure
|
|
190890
191161
|
});
|
|
190891
191162
|
});
|
|
190892
|
-
runAcpClientWorkflow(child, input, abortController, resolveEffortConfigOption(agent, agentConfig.effort)).then(({
|
|
191163
|
+
runAcpClientWorkflow(child, input, abortController, resolveEffortConfigOption(agent, agentConfig.effort), launchExecutionIdentity).then(({
|
|
191164
|
+
rawText,
|
|
191165
|
+
warnings,
|
|
191166
|
+
usage,
|
|
191167
|
+
messageBytes,
|
|
191168
|
+
thoughtBytes,
|
|
191169
|
+
outputBytes,
|
|
191170
|
+
outputWarningTriggered,
|
|
191171
|
+
stopReason,
|
|
191172
|
+
executionIdentity
|
|
191173
|
+
}) => {
|
|
190893
191174
|
stdout = rawText;
|
|
190894
191175
|
const completed = stopReason === "end_turn";
|
|
190895
191176
|
resolveOnce({
|
|
@@ -190900,8 +191181,12 @@ async function runSubprocessAgent(agent, agentConfig, input, env) {
|
|
|
190900
191181
|
normalized: normalizeAgentOutput(agent, input.role, rawText),
|
|
190901
191182
|
startedAt,
|
|
190902
191183
|
completedAt: new Date().toISOString(),
|
|
191184
|
+
messageBytes,
|
|
191185
|
+
thoughtBytes,
|
|
190903
191186
|
outputBytes,
|
|
191187
|
+
outputWarningTriggered,
|
|
190904
191188
|
stopReason,
|
|
191189
|
+
executionIdentity,
|
|
190905
191190
|
...usage ? { usage } : {},
|
|
190906
191191
|
...warnings.length > 0 ? { warnings } : {},
|
|
190907
191192
|
...completed ? {} : {
|
|
@@ -190915,18 +191200,40 @@ async function runSubprocessAgent(agent, agentConfig, input, env) {
|
|
|
190915
191200
|
const outputLimitError = findOutputLimitError(error51, abortController);
|
|
190916
191201
|
if (outputLimitError) {
|
|
190917
191202
|
stdout = outputLimitError.rawText;
|
|
191203
|
+
const normalized = parseAgentOutputStrict(agent, input.role, stdout);
|
|
190918
191204
|
resolveOnce({
|
|
190919
191205
|
agent,
|
|
190920
191206
|
role: input.role,
|
|
190921
191207
|
status: "failed",
|
|
190922
191208
|
rawText: stdout,
|
|
191209
|
+
...normalized ? { normalized, salvaged: true } : {},
|
|
191210
|
+
messageBytes: outputLimitError.messageBytes,
|
|
191211
|
+
thoughtBytes: outputLimitError.thoughtBytes,
|
|
190923
191212
|
outputBytes: outputLimitError.outputBytes,
|
|
191213
|
+
outputWarningTriggered: outputLimitError.outputWarningTriggered,
|
|
190924
191214
|
stopReason: "cancelled",
|
|
190925
191215
|
startedAt,
|
|
190926
191216
|
completedAt: new Date().toISOString(),
|
|
190927
191217
|
error: {
|
|
190928
191218
|
code: "AGENT_OUTPUT_LIMIT",
|
|
190929
|
-
message: `Agent output exceeded ${outputLimitError.maxOutputBytes}
|
|
191219
|
+
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.`
|
|
191220
|
+
}
|
|
191221
|
+
});
|
|
191222
|
+
return;
|
|
191223
|
+
}
|
|
191224
|
+
if (error51 instanceof AcpNdJsonLineLimitError) {
|
|
191225
|
+
abortController.abort(error51);
|
|
191226
|
+
resolveOnce({
|
|
191227
|
+
agent,
|
|
191228
|
+
role: input.role,
|
|
191229
|
+
status: "failed",
|
|
191230
|
+
rawText: stdout,
|
|
191231
|
+
stopReason: "cancelled",
|
|
191232
|
+
startedAt,
|
|
191233
|
+
completedAt: new Date().toISOString(),
|
|
191234
|
+
error: {
|
|
191235
|
+
code: "AGENT_PROTOCOL_LIMIT",
|
|
191236
|
+
message: `Agent emitted an ACP NDJSON line above the ${error51.maxLineBytes}-byte transport limit and was cancelled.`
|
|
190930
191237
|
}
|
|
190931
191238
|
});
|
|
190932
191239
|
return;
|
|
@@ -190963,13 +191270,13 @@ async function runSubprocessAgent(agent, agentConfig, input, env) {
|
|
|
190963
191270
|
});
|
|
190964
191271
|
});
|
|
190965
191272
|
}
|
|
190966
|
-
async function runAcpClientWorkflow(child, input, abortController, configOption) {
|
|
191273
|
+
async function runAcpClientWorkflow(child, input, abortController, configOption, launchExecutionIdentity) {
|
|
190967
191274
|
if (!child.stdin || !child.stdout) {
|
|
190968
191275
|
throw new Error("Agent process did not expose stdio streams.");
|
|
190969
191276
|
}
|
|
190970
191277
|
const output = Writable.toWeb(child.stdin);
|
|
190971
191278
|
const inputStream = Readable.toWeb(child.stdout);
|
|
190972
|
-
const stream2 = ndJsonStream(output, inputStream);
|
|
191279
|
+
const stream2 = ndJsonStream(output, limitAcpNdJsonLineBytes(inputStream));
|
|
190973
191280
|
const app = client({ name: "kyoso" }).onRequest(methods.client.session.requestPermission, () => ({
|
|
190974
191281
|
outcome: { outcome: "cancelled" }
|
|
190975
191282
|
})).onRequest(methods.client.fs.readTextFile, async (ctx) => ({
|
|
@@ -191030,7 +191337,10 @@ async function runAcpClientWorkflow(child, input, abortController, configOption)
|
|
|
191030
191337
|
return;
|
|
191031
191338
|
});
|
|
191032
191339
|
let rawText = "";
|
|
191340
|
+
let messageBytes = 0;
|
|
191341
|
+
let thoughtBytes = 0;
|
|
191033
191342
|
let outputBytes = 0;
|
|
191343
|
+
let outputWarningTriggered = false;
|
|
191034
191344
|
for (;; ) {
|
|
191035
191345
|
const message = await session.nextUpdate();
|
|
191036
191346
|
if (message.kind === "stop") {
|
|
@@ -191039,8 +191349,12 @@ async function runAcpClientWorkflow(child, input, abortController, configOption)
|
|
|
191039
191349
|
rawText,
|
|
191040
191350
|
warnings,
|
|
191041
191351
|
...usage ? { usage } : {},
|
|
191352
|
+
messageBytes,
|
|
191353
|
+
thoughtBytes,
|
|
191042
191354
|
outputBytes,
|
|
191043
|
-
|
|
191355
|
+
outputWarningTriggered,
|
|
191356
|
+
stopReason: message.stopReason,
|
|
191357
|
+
executionIdentity: withReportedExecutionIdentity(launchExecutionIdentity, message.response._meta)
|
|
191044
191358
|
};
|
|
191045
191359
|
}
|
|
191046
191360
|
const update = message.update;
|
|
@@ -191048,35 +191362,60 @@ async function runAcpClientWorkflow(child, input, abortController, configOption)
|
|
|
191048
191362
|
continue;
|
|
191049
191363
|
}
|
|
191050
191364
|
const chunkBytes = Buffer.byteLength(update.content.text, "utf8");
|
|
191051
|
-
const
|
|
191365
|
+
const isMessage = update.sessionUpdate === "agent_message_chunk";
|
|
191366
|
+
const nextMessageBytes = messageBytes + (isMessage ? chunkBytes : 0);
|
|
191367
|
+
const nextThoughtBytes = thoughtBytes + (isMessage ? 0 : chunkBytes);
|
|
191368
|
+
const nextOutputBytes = nextMessageBytes + nextThoughtBytes;
|
|
191369
|
+
const nextOutputWarningTriggered = outputWarningTriggered || input.warnOutputBytes !== undefined && nextOutputBytes >= input.warnOutputBytes;
|
|
191052
191370
|
if (input.maxOutputBytes !== undefined && nextOutputBytes > input.maxOutputBytes) {
|
|
191371
|
+
const retainedRawText = isMessage ? `${rawText}${utf8Prefix(update.content.text, input.maxOutputBytes - outputBytes)}` : rawText;
|
|
191053
191372
|
await ctx.notify(methods.agent.session.cancel, {
|
|
191054
191373
|
sessionId: session.sessionId
|
|
191055
191374
|
}).catch(() => {
|
|
191056
191375
|
return;
|
|
191057
191376
|
});
|
|
191058
|
-
const error51 = new AgentOutputLimitError(
|
|
191377
|
+
const error51 = new AgentOutputLimitError(retainedRawText, nextMessageBytes, nextThoughtBytes, nextOutputBytes, input.maxOutputBytes, nextOutputWarningTriggered);
|
|
191059
191378
|
abortController.abort(error51);
|
|
191060
191379
|
throw error51;
|
|
191061
191380
|
}
|
|
191062
|
-
if (
|
|
191381
|
+
if (isMessage) {
|
|
191063
191382
|
rawText += update.content.text;
|
|
191064
191383
|
}
|
|
191384
|
+
messageBytes = nextMessageBytes;
|
|
191385
|
+
thoughtBytes = nextThoughtBytes;
|
|
191065
191386
|
outputBytes = nextOutputBytes;
|
|
191387
|
+
outputWarningTriggered = nextOutputWarningTriggered;
|
|
191066
191388
|
}
|
|
191067
191389
|
});
|
|
191068
191390
|
});
|
|
191069
191391
|
}
|
|
191392
|
+
function utf8Prefix(input, maxBytes) {
|
|
191393
|
+
const encoded = new TextEncoder().encode(input);
|
|
191394
|
+
const budget = Math.max(0, Math.min(maxBytes, encoded.byteLength));
|
|
191395
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
191396
|
+
for (let end = budget;end > 0; end -= 1) {
|
|
191397
|
+
try {
|
|
191398
|
+
return decoder.decode(encoded.subarray(0, end));
|
|
191399
|
+
} catch {}
|
|
191400
|
+
}
|
|
191401
|
+
return "";
|
|
191402
|
+
}
|
|
191070
191403
|
|
|
191071
191404
|
class AgentOutputLimitError extends Error {
|
|
191072
191405
|
rawText;
|
|
191406
|
+
messageBytes;
|
|
191407
|
+
thoughtBytes;
|
|
191073
191408
|
outputBytes;
|
|
191074
191409
|
maxOutputBytes;
|
|
191075
|
-
|
|
191410
|
+
outputWarningTriggered;
|
|
191411
|
+
constructor(rawText, messageBytes, thoughtBytes, outputBytes, maxOutputBytes, outputWarningTriggered) {
|
|
191076
191412
|
super(`Agent output exceeded ${maxOutputBytes} bytes.`);
|
|
191077
191413
|
this.rawText = rawText;
|
|
191414
|
+
this.messageBytes = messageBytes;
|
|
191415
|
+
this.thoughtBytes = thoughtBytes;
|
|
191078
191416
|
this.outputBytes = outputBytes;
|
|
191079
191417
|
this.maxOutputBytes = maxOutputBytes;
|
|
191418
|
+
this.outputWarningTriggered = outputWarningTriggered;
|
|
191080
191419
|
this.name = "AgentOutputLimitError";
|
|
191081
191420
|
}
|
|
191082
191421
|
}
|
|
@@ -191093,6 +191432,18 @@ function resolveEffectiveTimeoutMs(input) {
|
|
|
191093
191432
|
function normalizeUsage(usage) {
|
|
191094
191433
|
return normalizeModelTokenUsage(usage);
|
|
191095
191434
|
}
|
|
191435
|
+
function withReportedExecutionIdentity(identity, metadata) {
|
|
191436
|
+
const record2 = isRecord9(metadata) ? metadata : {};
|
|
191437
|
+
return createModelExecutionIdentity({
|
|
191438
|
+
providerRoute: identity.providerRoute,
|
|
191439
|
+
requestedModel: identity.requestedModel,
|
|
191440
|
+
reportedProvider: record2.provider,
|
|
191441
|
+
reportedModel: record2.model
|
|
191442
|
+
});
|
|
191443
|
+
}
|
|
191444
|
+
function isRecord9(value) {
|
|
191445
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
191446
|
+
}
|
|
191096
191447
|
function resolveEffortConfigOption(agent, effort) {
|
|
191097
191448
|
if (!effort)
|
|
191098
191449
|
return;
|
|
@@ -191402,6 +191753,9 @@ function buildAgentPrompt(tool, request, agent, role, policy = {}) {
|
|
|
191402
191753
|
"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.",
|
|
191403
191754
|
"Put insufficiently supported hypotheses in openQuestions instead of findings.",
|
|
191404
191755
|
"Do not create formal findings for style, formatting, generic hardening, unrelated pre-existing issues, duplicate tests, implementation-detail tests, or exhaustive boundary matrices.",
|
|
191756
|
+
...policy.maxFindingsTarget === undefined ? [] : [
|
|
191757
|
+
`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.`
|
|
191758
|
+
],
|
|
191405
191759
|
"Recommend only specific regression scenarios tied to changed behavior, with at most three testsToAdd entries.",
|
|
191406
191760
|
"Critical and High safety issues must still be reported when they match a non-goal.",
|
|
191407
191761
|
"Write each finding title in concise English, regardless of the language used elsewhere. Titles are compared across agents for deduplication.",
|
|
@@ -192336,6 +192690,12 @@ function sanitizeForAudit(value, options = {}) {
|
|
|
192336
192690
|
if (typeof value === "object" && value !== null) {
|
|
192337
192691
|
const result = {};
|
|
192338
192692
|
for (const [key, nested] of Object.entries(value)) {
|
|
192693
|
+
if (key === "executionIdentity") {
|
|
192694
|
+
const identity = normalizeModelExecutionIdentity(nested);
|
|
192695
|
+
if (identity)
|
|
192696
|
+
result[key] = identity;
|
|
192697
|
+
continue;
|
|
192698
|
+
}
|
|
192339
192699
|
if (/raw|content|env|credential|token|secret|password/i.test(key) && !(options.includeRawAgentOutput && key === "rawText") && !isUsageMetadata(key, nested)) {
|
|
192340
192700
|
continue;
|
|
192341
192701
|
}
|
|
@@ -192616,7 +192976,7 @@ function validateReviewContract(request) {
|
|
|
192616
192976
|
const contract = request.reviewContract;
|
|
192617
192977
|
if (contract === undefined)
|
|
192618
192978
|
return;
|
|
192619
|
-
if (!
|
|
192979
|
+
if (!isRecord10(contract)) {
|
|
192620
192980
|
throw new KyosoRequestError("reviewContract must be an object", "VALIDATION_ERROR");
|
|
192621
192981
|
}
|
|
192622
192982
|
const allowedKeys = new Set(["focus", "nonGoals", "acceptedRisks"]);
|
|
@@ -192633,7 +192993,7 @@ function validateReviewContract(request) {
|
|
|
192633
192993
|
throw new KyosoRequestError("reviewContract.nonGoals must contain at most 20 non-empty strings of 500 characters or fewer", "VALIDATION_ERROR");
|
|
192634
192994
|
}
|
|
192635
192995
|
const acceptedRisks = contract.acceptedRisks;
|
|
192636
|
-
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !
|
|
192996
|
+
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !isRecord10(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))) {
|
|
192637
192997
|
throw new KyosoRequestError("reviewContract.acceptedRisks must contain valid finding fingerprints and bounded rationales", "VALIDATION_ERROR");
|
|
192638
192998
|
}
|
|
192639
192999
|
}
|
|
@@ -192645,13 +193005,13 @@ function validateSelectedFiles(request) {
|
|
|
192645
193005
|
throw new KyosoRequestError("selectedFiles must be an array", "VALIDATION_ERROR");
|
|
192646
193006
|
}
|
|
192647
193007
|
for (const file2 of selectedFiles) {
|
|
192648
|
-
if (!
|
|
193008
|
+
if (!isRecord10(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") {
|
|
192649
193009
|
throw new KyosoRequestError("selectedFiles entries require string path/content and valid optional metadata", "VALIDATION_ERROR");
|
|
192650
193010
|
}
|
|
192651
193011
|
normalizeRelativePath(file2.path);
|
|
192652
193012
|
}
|
|
192653
193013
|
}
|
|
192654
|
-
function
|
|
193014
|
+
function isRecord10(value) {
|
|
192655
193015
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
192656
193016
|
}
|
|
192657
193017
|
|
|
@@ -192664,7 +193024,7 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
192664
193024
|
`**Mode:** ${tool}`,
|
|
192665
193025
|
`**Completion:** ${formatCompletion(result)}`,
|
|
192666
193026
|
`**Request fingerprint:** ${shortFingerprint(result.requestFingerprint)}`,
|
|
192667
|
-
`**Agents:** ${result.agentOpinions.map((opinion) => `${title(opinion.agent)} ${opinion.status}`).join(", ")}`,
|
|
193027
|
+
`**Agents:** ${result.agentOpinions.map((opinion) => `${title(opinion.agent)} ${opinion.status}${opinion.salvaged ? " (salvaged)" : ""}`).join(", ")}`,
|
|
192668
193028
|
`**Review mode:** ${formatReviewMode(result)}`,
|
|
192669
193029
|
...result.verificationMode ? [
|
|
192670
193030
|
`**Verification mode:** ${formatVerificationMode(result.verificationMode)}`
|
|
@@ -192715,7 +193075,7 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
192715
193075
|
}
|
|
192716
193076
|
lines.push("", "## Agent Opinions", "");
|
|
192717
193077
|
for (const opinion of result.agentOpinions) {
|
|
192718
|
-
lines.push(`### ${title(opinion.agent)}`, "", `${opinion.summary} (${opinion.status})`, "");
|
|
193078
|
+
lines.push(`### ${title(opinion.agent)}`, "", `${opinion.summary} (${opinion.status}${opinion.salvaged ? ", salvaged" : ""})`, "");
|
|
192719
193079
|
}
|
|
192720
193080
|
lines.push("", "## Disagreements", "");
|
|
192721
193081
|
if (result.reviewMode === "single_agent") {
|
|
@@ -192746,17 +193106,50 @@ function defaultSummaryText(result) {
|
|
|
192746
193106
|
function formatExecutionBudget(result) {
|
|
192747
193107
|
const budget = result.executionBudget;
|
|
192748
193108
|
const agentOutputs = Object.entries(budget.agentOutputBytes);
|
|
192749
|
-
const
|
|
193109
|
+
const byteBreakdowns = new Map;
|
|
193110
|
+
for (const call of result.audit.modelCalls) {
|
|
193111
|
+
if (call.status !== "completed" || !call.agent)
|
|
193112
|
+
continue;
|
|
193113
|
+
const current = byteBreakdowns.get(call.agent) ?? {
|
|
193114
|
+
messageBytes: 0,
|
|
193115
|
+
thoughtBytes: 0
|
|
193116
|
+
};
|
|
193117
|
+
current.messageBytes += call.messageBytes ?? 0;
|
|
193118
|
+
current.thoughtBytes += call.thoughtBytes ?? 0;
|
|
193119
|
+
byteBreakdowns.set(call.agent, current);
|
|
193120
|
+
}
|
|
193121
|
+
const outputLines = agentOutputs.length > 0 ? agentOutputs.map(([agent, bytes]) => {
|
|
193122
|
+
const breakdown = byteBreakdowns.get(agent);
|
|
193123
|
+
return `- ${title(agent)}: ${bytes} bytes${breakdown ? ` (message: ${breakdown.messageBytes}, thought: ${breakdown.thoughtBytes})` : ""}`;
|
|
193124
|
+
}) : ["- None reported."];
|
|
193125
|
+
const identityLines = result.audit.modelCalls.filter((call) => call.status === "completed").map((call) => {
|
|
193126
|
+
const label = [call.kind, call.agent].filter(Boolean).join("/");
|
|
193127
|
+
const identity = call.executionIdentity;
|
|
193128
|
+
if (!identity)
|
|
193129
|
+
return `- ${label}: identity=unknown`;
|
|
193130
|
+
const reportedIdentity = [
|
|
193131
|
+
identity.reportedProvider ? `reportedProvider=${escapeMarkdownText(identity.reportedProvider)}` : undefined,
|
|
193132
|
+
identity.reportedModel ? `reportedModel=${escapeMarkdownText(identity.reportedModel)}` : undefined
|
|
193133
|
+
].filter((value) => value !== undefined);
|
|
193134
|
+
return `- ${label}: route=${identity.providerRoute}, requested=${escapeMarkdownText(identity.requestedModel ?? "unknown")}${reportedIdentity.length > 0 ? `, ${reportedIdentity.join(", ")}` : ""}, reporting=${identity.reportingStatus}`;
|
|
193135
|
+
});
|
|
192750
193136
|
const totalTokens = budget.tokenUsage.totals.totalTokens;
|
|
193137
|
+
const plan = budget.modelCallPlan;
|
|
193138
|
+
const outputLimits = budget.effectiveWarnAgentOutputBytes === undefined ? `${budget.maxAgentOutputBytes} bytes hard (soft warning disabled)` : `${budget.effectiveWarnAgentOutputBytes} bytes soft / ${budget.maxAgentOutputBytes} bytes hard`;
|
|
192751
193139
|
return [
|
|
192752
193140
|
"",
|
|
192753
193141
|
"## Execution Budget",
|
|
192754
193142
|
"",
|
|
192755
|
-
`- Model calls: ${budget.modelCalls.planned} planned / ${budget.modelCalls.consumed} consumed / ${budget.modelCalls.skipped} skipped`,
|
|
193143
|
+
`- Model calls: ${budget.modelCalls.planned} planned / ${budget.modelCalls.consumed} consumed / ${budget.modelCalls.skipped} skipped / ${budget.maxModelCalls} ceiling`,
|
|
193144
|
+
`- Potential calls: ${plan.potentialTotalCalls} total (${plan.requiredPrimaryCalls} primary, ${plan.potentialVerifierCalls} verifier, ${plan.potentialJudgeCalls} judge)`,
|
|
192756
193145
|
`- Wall time: ${budget.wallTime.consumedMs}ms consumed / ${budget.wallTime.limitMs}ms limit`,
|
|
192757
193146
|
`- Token usage: ${budget.tokenUsage.status} (${budget.tokenUsage.reportedCalls} reported, ${budget.tokenUsage.unknownCalls} unknown${totalTokens === undefined ? "" : `, ${totalTokens} total`})`,
|
|
193147
|
+
`- Agent output limits: ${outputLimits}`,
|
|
193148
|
+
`- Findings target: ${budget.maxFindingsPerAgent} per primary agent (soft)`,
|
|
192758
193149
|
"- Agent output:",
|
|
192759
|
-
...outputLines
|
|
193150
|
+
...outputLines,
|
|
193151
|
+
"- Model identities:",
|
|
193152
|
+
...identityLines.length > 0 ? identityLines : ["- None completed."]
|
|
192760
193153
|
];
|
|
192761
193154
|
}
|
|
192762
193155
|
function formatCompletion(result) {
|
|
@@ -192879,7 +193272,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
|
|
|
192879
193272
|
const parsed = JSON.parse(json2);
|
|
192880
193273
|
const summaryText = typeof parsed.summaryText === "string" && parsed.summaryText.trim().length > 0 ? sanitizeText(parsed.summaryText) : fallbackSummaryText;
|
|
192881
193274
|
const disagreementComments = Array.isArray(parsed.disagreementComments) ? parsed.disagreementComments.flatMap((item) => {
|
|
192882
|
-
if (!
|
|
193275
|
+
if (!isRecord11(item) || typeof item.topic !== "string" || typeof item.judgeComment !== "string") {
|
|
192883
193276
|
return [];
|
|
192884
193277
|
}
|
|
192885
193278
|
return [
|
|
@@ -192895,7 +193288,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
|
|
|
192895
193288
|
return { summaryText, disagreementComments, analysis };
|
|
192896
193289
|
}
|
|
192897
193290
|
function parseAnalysis(value) {
|
|
192898
|
-
if (!
|
|
193291
|
+
if (!isRecord11(value))
|
|
192899
193292
|
return;
|
|
192900
193293
|
if (!Array.isArray(value.blindSpots) || !Array.isArray(value.contradictions) || !Array.isArray(value.partialCoverage)) {
|
|
192901
193294
|
return;
|
|
@@ -192903,7 +193296,7 @@ function parseAnalysis(value) {
|
|
|
192903
193296
|
return {
|
|
192904
193297
|
blindSpots: value.blindSpots.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => typeof item === "string" ? [sanitizeAnalysisText(item)] : []),
|
|
192905
193298
|
contradictions: value.contradictions.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
|
|
192906
|
-
if (!
|
|
193299
|
+
if (!isRecord11(item) || typeof item.topic !== "string" || typeof item.detail !== "string") {
|
|
192907
193300
|
return [];
|
|
192908
193301
|
}
|
|
192909
193302
|
return [
|
|
@@ -192914,7 +193307,7 @@ function parseAnalysis(value) {
|
|
|
192914
193307
|
];
|
|
192915
193308
|
}),
|
|
192916
193309
|
partialCoverage: value.partialCoverage.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
|
|
192917
|
-
if (!
|
|
193310
|
+
if (!isRecord11(item) || typeof item.note !== "string")
|
|
192918
193311
|
return [];
|
|
192919
193312
|
const findingId = typeof item.findingId === "string" ? sanitizeAnalysisText(item.findingId) : undefined;
|
|
192920
193313
|
return [
|
|
@@ -192961,15 +193354,20 @@ function extractFirstJsonObject2(text) {
|
|
|
192961
193354
|
}
|
|
192962
193355
|
return;
|
|
192963
193356
|
}
|
|
192964
|
-
function
|
|
193357
|
+
function isRecord11(value) {
|
|
192965
193358
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
192966
193359
|
}
|
|
192967
193360
|
|
|
192968
193361
|
// src/judge/anthropic.ts
|
|
193362
|
+
var DEFAULT_ANTHROPIC_JUDGE_MODEL = "claude-haiku-4-5";
|
|
193363
|
+
function resolveAnthropicJudgeModel(env) {
|
|
193364
|
+
return env.KYOSO_ANTHROPIC_JUDGE_MODEL ?? DEFAULT_ANTHROPIC_JUDGE_MODEL;
|
|
193365
|
+
}
|
|
192969
193366
|
async function runAnthropicJudge(input, timeoutMs) {
|
|
192970
193367
|
const apiKey = input.env.ANTHROPIC_API_KEY;
|
|
192971
193368
|
if (!apiKey)
|
|
192972
193369
|
throw new Error("ANTHROPIC_API_KEY is not configured.");
|
|
193370
|
+
const requestedModel = resolveAnthropicJudgeModel(input.env);
|
|
192973
193371
|
const response = await fetchWithTimeout(`${input.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com"}/v1/messages`, {
|
|
192974
193372
|
method: "POST",
|
|
192975
193373
|
headers: {
|
|
@@ -192978,7 +193376,7 @@ async function runAnthropicJudge(input, timeoutMs) {
|
|
|
192978
193376
|
"content-type": "application/json"
|
|
192979
193377
|
},
|
|
192980
193378
|
body: JSON.stringify({
|
|
192981
|
-
model:
|
|
193379
|
+
model: requestedModel,
|
|
192982
193380
|
max_tokens: JUDGE_MAX_OUTPUT_TOKENS,
|
|
192983
193381
|
temperature: 0,
|
|
192984
193382
|
messages: [
|
|
@@ -192996,8 +193394,11 @@ async function runAnthropicJudge(input, timeoutMs) {
|
|
|
192996
193394
|
if (!content)
|
|
192997
193395
|
throw new Error("Anthropic judge response did not include text content.");
|
|
192998
193396
|
const usage = normalizeUsage2(payload.usage);
|
|
193397
|
+
const reportedModel = typeof payload.model === "string" ? payload.model : undefined;
|
|
192999
193398
|
return {
|
|
193000
193399
|
output: parseJudgeOutput(content, input.summaryText),
|
|
193400
|
+
requestedModel,
|
|
193401
|
+
...reportedModel ? { reportedModel } : {},
|
|
193001
193402
|
...usage ? { usage } : {}
|
|
193002
193403
|
};
|
|
193003
193404
|
}
|
|
@@ -193034,10 +193435,15 @@ function runDeterministicJudge(result, summaryText) {
|
|
|
193034
193435
|
}
|
|
193035
193436
|
|
|
193036
193437
|
// src/judge/openai.ts
|
|
193438
|
+
var DEFAULT_OPENAI_JUDGE_MODEL = "gpt-5.4-mini";
|
|
193439
|
+
function resolveOpenAiJudgeModel(env) {
|
|
193440
|
+
return env.KYOSO_OPENAI_JUDGE_MODEL ?? DEFAULT_OPENAI_JUDGE_MODEL;
|
|
193441
|
+
}
|
|
193037
193442
|
async function runOpenAiJudge(input, timeoutMs) {
|
|
193038
193443
|
const apiKey = input.env.OPENAI_API_KEY ?? input.env.CODEX_API_KEY;
|
|
193039
193444
|
if (!apiKey)
|
|
193040
193445
|
throw new Error("OPENAI_API_KEY is not configured.");
|
|
193446
|
+
const requestedModel = resolveOpenAiJudgeModel(input.env);
|
|
193041
193447
|
const response = await fetchWithTimeout2(`${input.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1"}/chat/completions`, {
|
|
193042
193448
|
method: "POST",
|
|
193043
193449
|
headers: {
|
|
@@ -193045,7 +193451,7 @@ async function runOpenAiJudge(input, timeoutMs) {
|
|
|
193045
193451
|
"content-type": "application/json"
|
|
193046
193452
|
},
|
|
193047
193453
|
body: JSON.stringify({
|
|
193048
|
-
model:
|
|
193454
|
+
model: requestedModel,
|
|
193049
193455
|
response_format: { type: "json_object" },
|
|
193050
193456
|
messages: [
|
|
193051
193457
|
{
|
|
@@ -193064,8 +193470,11 @@ async function runOpenAiJudge(input, timeoutMs) {
|
|
|
193064
193470
|
if (!content)
|
|
193065
193471
|
throw new Error("OpenAI judge response did not include content.");
|
|
193066
193472
|
const usage = normalizeUsage3(payload.usage);
|
|
193473
|
+
const reportedModel = typeof payload.model === "string" ? payload.model : undefined;
|
|
193067
193474
|
return {
|
|
193068
193475
|
output: parseJudgeOutput(content, input.summaryText),
|
|
193476
|
+
requestedModel,
|
|
193477
|
+
...reportedModel ? { reportedModel } : {},
|
|
193069
193478
|
...usage ? { usage } : {}
|
|
193070
193479
|
};
|
|
193071
193480
|
}
|
|
@@ -193105,26 +193514,41 @@ function resolveJudgeProvider(provider, env) {
|
|
|
193105
193514
|
return "anthropic";
|
|
193106
193515
|
return "deterministic_fallback";
|
|
193107
193516
|
}
|
|
193517
|
+
function resolveJudgeCallRoute(mode, provider, env) {
|
|
193518
|
+
const resolvedProvider = resolveJudgeProvider(provider, env);
|
|
193519
|
+
const credentialAvailable = resolvedProvider === "openai" && (hasEnv2(env, "OPENAI_API_KEY") || hasEnv2(env, "CODEX_API_KEY")) || resolvedProvider === "anthropic" && hasEnv2(env, "ANTHROPIC_API_KEY");
|
|
193520
|
+
return {
|
|
193521
|
+
provider: resolvedProvider,
|
|
193522
|
+
llmAvailable: mode === "deterministic_plus_llm" && credentialAvailable
|
|
193523
|
+
};
|
|
193524
|
+
}
|
|
193108
193525
|
async function runJudge(input) {
|
|
193109
193526
|
const fallback = runDeterministicJudge(input.result, input.summaryText);
|
|
193110
|
-
|
|
193527
|
+
const configuredProvider = input.requestedProvider ?? input.config.provider;
|
|
193528
|
+
const route = resolveJudgeCallRoute(input.config.mode, configuredProvider, input.env);
|
|
193529
|
+
if (!route.llmAvailable) {
|
|
193111
193530
|
return {
|
|
193112
193531
|
provider: "deterministic_fallback",
|
|
193113
193532
|
status: "deterministic_fallback",
|
|
193114
193533
|
output: fallback
|
|
193115
193534
|
};
|
|
193116
193535
|
}
|
|
193117
|
-
const
|
|
193118
|
-
const
|
|
193119
|
-
|
|
193120
|
-
|
|
193121
|
-
}
|
|
193536
|
+
const provider = route.provider;
|
|
193537
|
+
const requestExecutionIdentity = createModelExecutionIdentity({
|
|
193538
|
+
providerRoute: provider === "openai" ? "openai" : "anthropic",
|
|
193539
|
+
requestedModel: provider === "openai" ? resolveOpenAiJudgeModel(input.env) : resolveAnthropicJudgeModel(input.env)
|
|
193540
|
+
});
|
|
193122
193541
|
try {
|
|
193123
193542
|
const output = provider === "openai" ? await runOpenAiJudge(input, input.timeoutMs ?? input.config.timeoutMs) : await runAnthropicJudge(input, input.timeoutMs ?? input.config.timeoutMs);
|
|
193124
193543
|
return {
|
|
193125
193544
|
provider,
|
|
193126
193545
|
status: "completed",
|
|
193127
193546
|
output: output.output,
|
|
193547
|
+
executionIdentity: createModelExecutionIdentity({
|
|
193548
|
+
providerRoute: requestExecutionIdentity.providerRoute,
|
|
193549
|
+
requestedModel: output.requestedModel,
|
|
193550
|
+
reportedModel: output.reportedModel
|
|
193551
|
+
}),
|
|
193128
193552
|
...output.usage ? { usage: output.usage } : {}
|
|
193129
193553
|
};
|
|
193130
193554
|
} catch (error51) {
|
|
@@ -193132,6 +193556,7 @@ async function runJudge(input) {
|
|
|
193132
193556
|
provider,
|
|
193133
193557
|
status: "failed_fallback",
|
|
193134
193558
|
output: fallback,
|
|
193559
|
+
executionIdentity: requestExecutionIdentity,
|
|
193135
193560
|
error: error51 instanceof Error ? error51.message : String(error51)
|
|
193136
193561
|
};
|
|
193137
193562
|
}
|
|
@@ -193458,11 +193883,11 @@ function canonicalJson(value) {
|
|
|
193458
193883
|
function canonicalize(value) {
|
|
193459
193884
|
if (Array.isArray(value))
|
|
193460
193885
|
return value.map(canonicalize);
|
|
193461
|
-
if (!
|
|
193886
|
+
if (!isRecord12(value))
|
|
193462
193887
|
return value;
|
|
193463
193888
|
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)]));
|
|
193464
193889
|
}
|
|
193465
|
-
function
|
|
193890
|
+
function isRecord12(value) {
|
|
193466
193891
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193467
193892
|
}
|
|
193468
193893
|
|
|
@@ -193476,12 +193901,10 @@ var REVIEW_BUDGET_KEYS = new Set([
|
|
|
193476
193901
|
]);
|
|
193477
193902
|
var MODEL_CALL_KINDS = ["primary", "verifier", "judge"];
|
|
193478
193903
|
function resolveReviewBudget(ceiling, requested) {
|
|
193479
|
-
if (requested
|
|
193480
|
-
return ceiling;
|
|
193481
|
-
if (!isRecord11(requested)) {
|
|
193904
|
+
if (requested !== undefined && !isRecord13(requested)) {
|
|
193482
193905
|
throw new KyosoRequestError("options.reviewBudget must be an object.", "REVIEW_BUDGET_INVALID");
|
|
193483
193906
|
}
|
|
193484
|
-
for (const [key, value] of Object.entries(requested)) {
|
|
193907
|
+
for (const [key, value] of Object.entries(requested ?? {})) {
|
|
193485
193908
|
if (!REVIEW_BUDGET_KEYS.has(key)) {
|
|
193486
193909
|
throw new KyosoRequestError(`options.reviewBudget.${key} is not supported.`, "REVIEW_BUDGET_INVALID");
|
|
193487
193910
|
}
|
|
@@ -193502,36 +193925,105 @@ function resolveReviewBudget(ceiling, requested) {
|
|
|
193502
193925
|
"maxFindingsPerAgent"
|
|
193503
193926
|
];
|
|
193504
193927
|
for (const key of numericKeys) {
|
|
193505
|
-
const value = requested[key];
|
|
193928
|
+
const value = requested?.[key];
|
|
193506
193929
|
if (value === undefined)
|
|
193507
193930
|
continue;
|
|
193508
193931
|
if (value > ceiling[key]) {
|
|
193509
193932
|
throw new KyosoRequestError(`options.reviewBudget.${key} cannot exceed the user-global ceiling.`, "REVIEW_BUDGET_EXCEEDS_CEILING");
|
|
193510
193933
|
}
|
|
193511
193934
|
}
|
|
193512
|
-
if (ceiling.skipOptionalPhasesWhenTokenUsageUnknown && requested
|
|
193935
|
+
if (ceiling.skipOptionalPhasesWhenTokenUsageUnknown && requested?.skipOptionalPhasesWhenTokenUsageUnknown === false) {
|
|
193513
193936
|
throw new KyosoRequestError("options.reviewBudget.skipOptionalPhasesWhenTokenUsageUnknown cannot relax the user-global ceiling.", "REVIEW_BUDGET_EXCEEDS_CEILING");
|
|
193514
193937
|
}
|
|
193938
|
+
const maxAgentOutputBytes = requested?.maxAgentOutputBytes ?? ceiling.maxAgentOutputBytes;
|
|
193939
|
+
return {
|
|
193940
|
+
maxModelCalls: requested?.maxModelCalls ?? ceiling.maxModelCalls,
|
|
193941
|
+
maxTotalWallTimeMs: requested?.maxTotalWallTimeMs ?? ceiling.maxTotalWallTimeMs,
|
|
193942
|
+
warnAgentOutputBytes: ceiling.warnAgentOutputBytes,
|
|
193943
|
+
maxAgentOutputBytes,
|
|
193944
|
+
maxFindingsPerAgent: requested?.maxFindingsPerAgent ?? ceiling.maxFindingsPerAgent,
|
|
193945
|
+
skipOptionalPhasesWhenTokenUsageUnknown: ceiling.skipOptionalPhasesWhenTokenUsageUnknown || requested?.skipOptionalPhasesWhenTokenUsageUnknown === true,
|
|
193946
|
+
...ceiling.warnAgentOutputBytes < maxAgentOutputBytes ? { effectiveWarnAgentOutputBytes: ceiling.warnAgentOutputBytes } : {}
|
|
193947
|
+
};
|
|
193948
|
+
}
|
|
193949
|
+
function buildReviewModelCallPlan(input) {
|
|
193950
|
+
const requiredPrimaryCalls = nonNegativeInteger(input.requiredPrimaryCalls);
|
|
193951
|
+
const potentialVerifierCalls = input.verificationEnabled && requiredPrimaryCalls === 2 ? Math.min(2, nonNegativeInteger(input.verificationMaxFindings)) : 0;
|
|
193952
|
+
const potentialJudgeCalls = input.llmJudgeAvailable ? 1 : 0;
|
|
193953
|
+
const potentialTotalCalls = requiredPrimaryCalls + potentialVerifierCalls + potentialJudgeCalls;
|
|
193954
|
+
const ceilingEffects = [];
|
|
193955
|
+
const availableCalls = nonNegativeInteger(input.maxModelCalls);
|
|
193956
|
+
if (availableCalls < requiredPrimaryCalls) {
|
|
193957
|
+
if (requiredPrimaryCalls > 0) {
|
|
193958
|
+
ceilingEffects.push({
|
|
193959
|
+
kind: "primary",
|
|
193960
|
+
action: "skip",
|
|
193961
|
+
calls: requiredPrimaryCalls,
|
|
193962
|
+
reason: "model_call_budget"
|
|
193963
|
+
});
|
|
193964
|
+
}
|
|
193965
|
+
if (potentialVerifierCalls > 0) {
|
|
193966
|
+
ceilingEffects.push({
|
|
193967
|
+
kind: "verifier",
|
|
193968
|
+
action: "skip",
|
|
193969
|
+
calls: potentialVerifierCalls,
|
|
193970
|
+
reason: "model_call_budget"
|
|
193971
|
+
});
|
|
193972
|
+
}
|
|
193973
|
+
if (potentialJudgeCalls > 0) {
|
|
193974
|
+
ceilingEffects.push({
|
|
193975
|
+
kind: "judge",
|
|
193976
|
+
action: "deterministic_fallback",
|
|
193977
|
+
calls: potentialJudgeCalls,
|
|
193978
|
+
reason: "model_call_budget"
|
|
193979
|
+
});
|
|
193980
|
+
}
|
|
193981
|
+
} else {
|
|
193982
|
+
let remainingCalls = availableCalls - requiredPrimaryCalls;
|
|
193983
|
+
const verifierCalls = Math.min(potentialVerifierCalls, remainingCalls);
|
|
193984
|
+
remainingCalls -= verifierCalls;
|
|
193985
|
+
const skippedVerifierCalls = potentialVerifierCalls - verifierCalls;
|
|
193986
|
+
if (skippedVerifierCalls > 0) {
|
|
193987
|
+
ceilingEffects.push({
|
|
193988
|
+
kind: "verifier",
|
|
193989
|
+
action: "skip",
|
|
193990
|
+
calls: skippedVerifierCalls,
|
|
193991
|
+
reason: "model_call_budget"
|
|
193992
|
+
});
|
|
193993
|
+
}
|
|
193994
|
+
const judgeCalls = Math.min(potentialJudgeCalls, remainingCalls);
|
|
193995
|
+
const fallbackJudgeCalls = potentialJudgeCalls - judgeCalls;
|
|
193996
|
+
if (fallbackJudgeCalls > 0) {
|
|
193997
|
+
ceilingEffects.push({
|
|
193998
|
+
kind: "judge",
|
|
193999
|
+
action: "deterministic_fallback",
|
|
194000
|
+
calls: fallbackJudgeCalls,
|
|
194001
|
+
reason: "model_call_budget"
|
|
194002
|
+
});
|
|
194003
|
+
}
|
|
194004
|
+
}
|
|
193515
194005
|
return {
|
|
193516
|
-
|
|
193517
|
-
|
|
193518
|
-
|
|
193519
|
-
|
|
193520
|
-
|
|
194006
|
+
requiredPrimaryCalls,
|
|
194007
|
+
potentialVerifierCalls,
|
|
194008
|
+
potentialJudgeCalls,
|
|
194009
|
+
potentialTotalCalls,
|
|
194010
|
+
ceilingEffects
|
|
193521
194011
|
};
|
|
193522
194012
|
}
|
|
193523
194013
|
|
|
193524
194014
|
class ReviewBudgetTracker {
|
|
193525
194015
|
budget;
|
|
193526
194016
|
startedAtEpochMs;
|
|
194017
|
+
modelCallPlan;
|
|
193527
194018
|
deadlineAtEpochMs;
|
|
193528
194019
|
reservations = new Map;
|
|
193529
194020
|
skippedCalls = [];
|
|
193530
194021
|
incompleteReasons = new Set;
|
|
193531
194022
|
nextReservationId = 1;
|
|
193532
|
-
constructor(budget, startedAtEpochMs = Date.now()) {
|
|
194023
|
+
constructor(budget, startedAtEpochMs = Date.now(), modelCallPlan = emptyReviewModelCallPlan()) {
|
|
193533
194024
|
this.budget = budget;
|
|
193534
194025
|
this.startedAtEpochMs = startedAtEpochMs;
|
|
194026
|
+
this.modelCallPlan = modelCallPlan;
|
|
193535
194027
|
this.deadlineAtEpochMs = startedAtEpochMs + budget.maxTotalWallTimeMs;
|
|
193536
194028
|
}
|
|
193537
194029
|
remainingWallTimeMs(now = Date.now()) {
|
|
@@ -193575,24 +194067,35 @@ class ReviewBudgetTracker {
|
|
|
193575
194067
|
}
|
|
193576
194068
|
return { reservation };
|
|
193577
194069
|
}
|
|
193578
|
-
markStarted(reservation) {
|
|
194070
|
+
markStarted(reservation, executionIdentity) {
|
|
193579
194071
|
const current = this.reservations.get(reservation.id);
|
|
193580
194072
|
if (!current || current.status !== "reserved")
|
|
193581
194073
|
return;
|
|
193582
194074
|
current.status = "started";
|
|
194075
|
+
current.executionIdentity = normalizeModelExecutionIdentity(executionIdentity);
|
|
193583
194076
|
}
|
|
193584
194077
|
hasStarted(reservation) {
|
|
193585
194078
|
const current = this.reservations.get(reservation.id);
|
|
193586
194079
|
return current?.status === "started" || current?.status === "completed";
|
|
193587
194080
|
}
|
|
194081
|
+
executionIdentity(reservation) {
|
|
194082
|
+
return this.reservations.get(reservation.id)?.executionIdentity;
|
|
194083
|
+
}
|
|
193588
194084
|
complete(reservation, values = {}) {
|
|
193589
194085
|
const current = this.reservations.get(reservation.id);
|
|
193590
194086
|
if (!current || current.status === "skipped" || current.status === "completed") {
|
|
193591
194087
|
return;
|
|
193592
194088
|
}
|
|
193593
194089
|
current.status = "completed";
|
|
194090
|
+
current.messageBytes = values.messageBytes;
|
|
194091
|
+
current.thoughtBytes = values.thoughtBytes;
|
|
193594
194092
|
current.outputBytes = values.outputBytes;
|
|
194093
|
+
current.outputWarningTriggered = values.outputWarningTriggered;
|
|
194094
|
+
current.salvaged = values.salvaged;
|
|
194095
|
+
current.reportedFindings = values.reportedFindings;
|
|
194096
|
+
current.findingsTargetExceeded = values.findingsTargetExceeded;
|
|
193595
194097
|
current.usage = normalizeModelTokenUsage(values.usage);
|
|
194098
|
+
current.executionIdentity = normalizeModelExecutionIdentity(values.executionIdentity) ?? current.executionIdentity;
|
|
193596
194099
|
current.stopReason = values.stopReason;
|
|
193597
194100
|
}
|
|
193598
194101
|
skip(reservation, reason) {
|
|
@@ -193665,12 +194168,16 @@ class ReviewBudgetTracker {
|
|
|
193665
194168
|
},
|
|
193666
194169
|
executionBudget: {
|
|
193667
194170
|
maxModelCalls: this.budget.maxModelCalls,
|
|
194171
|
+
modelCallPlan: this.modelCallPlan,
|
|
193668
194172
|
modelCalls: { planned, consumed, skipped, byKind },
|
|
193669
194173
|
wallTime: {
|
|
193670
194174
|
limitMs: this.budget.maxTotalWallTimeMs,
|
|
193671
194175
|
consumedMs,
|
|
193672
194176
|
remainingMs: this.remainingWallTimeMs(now)
|
|
193673
194177
|
},
|
|
194178
|
+
...this.budget.effectiveWarnAgentOutputBytes !== undefined ? {
|
|
194179
|
+
effectiveWarnAgentOutputBytes: this.budget.effectiveWarnAgentOutputBytes
|
|
194180
|
+
} : {},
|
|
193674
194181
|
maxAgentOutputBytes: this.budget.maxAgentOutputBytes,
|
|
193675
194182
|
maxFindingsPerAgent: this.budget.maxFindingsPerAgent,
|
|
193676
194183
|
skipOptionalPhasesWhenTokenUsageUnknown: this.budget.skipOptionalPhasesWhenTokenUsageUnknown,
|
|
@@ -193694,8 +194201,15 @@ class ReviewBudgetTracker {
|
|
|
193694
194201
|
...reservation.agent ? { agent: reservation.agent } : {},
|
|
193695
194202
|
status: reservation.status === "completed" ? "completed" : "skipped",
|
|
193696
194203
|
...reservation.reason ? { reason: reservation.reason } : {},
|
|
194204
|
+
...reservation.messageBytes !== undefined ? { messageBytes: reservation.messageBytes } : {},
|
|
194205
|
+
...reservation.thoughtBytes !== undefined ? { thoughtBytes: reservation.thoughtBytes } : {},
|
|
193697
194206
|
...reservation.outputBytes !== undefined ? { outputBytes: reservation.outputBytes } : {},
|
|
194207
|
+
...reservation.outputWarningTriggered !== undefined ? { outputWarningTriggered: reservation.outputWarningTriggered } : {},
|
|
194208
|
+
...reservation.salvaged !== undefined ? { salvaged: reservation.salvaged } : {},
|
|
194209
|
+
...reservation.reportedFindings !== undefined ? { reportedFindings: reservation.reportedFindings } : {},
|
|
194210
|
+
...reservation.findingsTargetExceeded !== undefined ? { findingsTargetExceeded: reservation.findingsTargetExceeded } : {},
|
|
193698
194211
|
...reservation.usage ? { usage: reservation.usage } : {},
|
|
194212
|
+
...reservation.executionIdentity ? { executionIdentity: reservation.executionIdentity } : {},
|
|
193699
194213
|
...reservation.stopReason ? { stopReason: reservation.stopReason } : {}
|
|
193700
194214
|
}));
|
|
193701
194215
|
return [...reservations, ...this.skippedCalls];
|
|
@@ -193719,7 +194233,19 @@ function addUsage(total, usage) {
|
|
|
193719
194233
|
function isPositiveInteger(value) {
|
|
193720
194234
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
193721
194235
|
}
|
|
193722
|
-
function
|
|
194236
|
+
function nonNegativeInteger(value) {
|
|
194237
|
+
return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
|
|
194238
|
+
}
|
|
194239
|
+
function emptyReviewModelCallPlan() {
|
|
194240
|
+
return {
|
|
194241
|
+
requiredPrimaryCalls: 0,
|
|
194242
|
+
potentialVerifierCalls: 0,
|
|
194243
|
+
potentialJudgeCalls: 0,
|
|
194244
|
+
potentialTotalCalls: 0,
|
|
194245
|
+
ceilingEffects: []
|
|
194246
|
+
};
|
|
194247
|
+
}
|
|
194248
|
+
function isRecord13(value) {
|
|
193723
194249
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193724
194250
|
}
|
|
193725
194251
|
|
|
@@ -193781,7 +194307,7 @@ function parseVerificationVerdicts(rawText) {
|
|
|
193781
194307
|
if (!Array.isArray(parsed.verdicts))
|
|
193782
194308
|
return;
|
|
193783
194309
|
return parsed.verdicts.flatMap((item) => {
|
|
193784
|
-
if (!
|
|
194310
|
+
if (!isRecord14(item))
|
|
193785
194311
|
return [];
|
|
193786
194312
|
if (typeof item.findingId !== "string")
|
|
193787
194313
|
return [];
|
|
@@ -193859,7 +194385,7 @@ function verificationNote(reasoning) {
|
|
|
193859
194385
|
function isVerdict(value) {
|
|
193860
194386
|
return value === "confirmed" || value === "refuted" || value === "uncertain";
|
|
193861
194387
|
}
|
|
193862
|
-
function
|
|
194388
|
+
function isRecord14(value) {
|
|
193863
194389
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193864
194390
|
}
|
|
193865
194391
|
|
|
@@ -193884,13 +194410,14 @@ async function runReview(tool, request, options = {}) {
|
|
|
193884
194410
|
} catch (error51) {
|
|
193885
194411
|
if (error51 instanceof KyosoRequestError) {
|
|
193886
194412
|
const config2 = kyosoConfigSchema.parse(defaultConfig);
|
|
193887
|
-
const
|
|
194413
|
+
const reviewBudget = resolveReviewBudget(config2.reviewBudget, undefined);
|
|
194414
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(config2, reviewBudget, options.env ?? process.env));
|
|
193888
194415
|
const requestFingerprint = createRequestFingerprint({
|
|
193889
194416
|
tool,
|
|
193890
194417
|
request: requestForRecursionFingerprint(request),
|
|
193891
194418
|
config: config2,
|
|
193892
194419
|
roles: resolveAgentRoles(config2),
|
|
193893
|
-
budget:
|
|
194420
|
+
budget: reviewBudget,
|
|
193894
194421
|
entrypoint: options.entrypoint
|
|
193895
194422
|
});
|
|
193896
194423
|
const trace2 = traceWriterFactory({
|
|
@@ -193996,7 +194523,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
193996
194523
|
});
|
|
193997
194524
|
validateReviewRequest(tool, request);
|
|
193998
194525
|
const reviewBudget = resolveReviewBudget(loaded.config.reviewBudget, request.options?.reviewBudget);
|
|
193999
|
-
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs);
|
|
194526
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(loaded.config, reviewBudget, options.env ?? process.env, request.options?.judgeProvider));
|
|
194000
194527
|
assertTrustedWorkspaceRoot(request.workspace?.root, loaded.config.workspace.root, cwd);
|
|
194001
194528
|
const networkMode = resolveNetworkMode(request.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
|
|
194002
194529
|
if (networkMode === "unrestricted" && !loaded.config.network.allowUnrestricted) {
|
|
@@ -194117,6 +194644,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
194117
194644
|
budgetTracker,
|
|
194118
194645
|
requestFingerprint
|
|
194119
194646
|
});
|
|
194647
|
+
warnings.push(...plannedBudgetWarnings(budgetTracker));
|
|
194120
194648
|
snapshot = await createSnapshot(traceId, tool, built.request, {
|
|
194121
194649
|
denyPatterns,
|
|
194122
194650
|
allowPatterns,
|
|
@@ -194143,11 +194671,9 @@ async function runReview(tool, request, options = {}) {
|
|
|
194143
194671
|
budgetTracker
|
|
194144
194672
|
});
|
|
194145
194673
|
warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
|
|
194146
|
-
const
|
|
194147
|
-
const
|
|
194148
|
-
|
|
194149
|
-
budgetTracker.markIncomplete("coverage_incomplete");
|
|
194150
|
-
warnings.push(`Agent ${item.result.agent} findings were capped at ${reviewBudget.maxFindingsPerAgent}; review coverage is incomplete.`);
|
|
194674
|
+
const normalizedAgentResults = agentResults.map((result) => normalizeAgentRunResult(result, reviewBudget.maxFindingsPerAgent));
|
|
194675
|
+
for (const result of normalizedAgentResults.filter((item) => item.findingsTargetExceeded)) {
|
|
194676
|
+
warnings.push(`Agent ${result.agent} reported ${result.reportedFindings} findings, above the soft target of ${reviewBudget.maxFindingsPerAgent}; all findings were retained.`);
|
|
194151
194677
|
}
|
|
194152
194678
|
const enabledAgents = ["codex", "claude"].filter((agent) => loaded.config.agents[agent].enabled);
|
|
194153
194679
|
const agentsUsed = normalizedAgentResults.filter((result) => result.status !== "skipped").map((result) => result.agent);
|
|
@@ -194321,6 +194847,11 @@ async function runReview(tool, request, options = {}) {
|
|
|
194321
194847
|
}));
|
|
194322
194848
|
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
194323
194849
|
const budgetAfterJudge = budgetTracker.snapshot();
|
|
194850
|
+
const finalWarnings = Array.from(new Set([
|
|
194851
|
+
...resultWithoutMarkdown.audit.warnings ?? [],
|
|
194852
|
+
...outputWarningMessages(budgetAfterJudge),
|
|
194853
|
+
...tokenUsageWarningMessages(budgetTracker, budgetAfterJudge)
|
|
194854
|
+
]));
|
|
194324
194855
|
const finalDecision = budgetAfterJudge.completion.status === "incomplete" ? "block" : decision;
|
|
194325
194856
|
const resultAfterJudge = {
|
|
194326
194857
|
...resultWithoutMarkdown,
|
|
@@ -194332,6 +194863,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
194332
194863
|
audit: {
|
|
194333
194864
|
...resultWithoutMarkdown.audit,
|
|
194334
194865
|
completedAt: new Date().toISOString(),
|
|
194866
|
+
warnings: finalWarnings,
|
|
194335
194867
|
modelCalls: budgetAfterJudge.modelCalls
|
|
194336
194868
|
}
|
|
194337
194869
|
};
|
|
@@ -194340,6 +194872,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
194340
194872
|
traceId,
|
|
194341
194873
|
provider: judge.provider,
|
|
194342
194874
|
status: judge.status,
|
|
194875
|
+
...judge.executionIdentity ? { executionIdentity: judge.executionIdentity } : {},
|
|
194343
194876
|
timestamp: new Date().toISOString()
|
|
194344
194877
|
};
|
|
194345
194878
|
if (judge.error)
|
|
@@ -194533,11 +195066,20 @@ async function runFindingVerification(input) {
|
|
|
194533
195066
|
workspaceDir: input.workspaceDir,
|
|
194534
195067
|
timeoutMs: Math.min(input.config.verification.timeoutMs, input.budgetTracker.remainingWallTimeMs()),
|
|
194535
195068
|
deadlineAtEpochMs: input.budgetTracker.deadlineAtEpochMs,
|
|
195069
|
+
warnOutputBytes: input.budgetTracker.budget.effectiveWarnAgentOutputBytes,
|
|
194536
195070
|
maxOutputBytes: input.budgetTracker.budget.maxAgentOutputBytes,
|
|
194537
195071
|
networkMode: input.networkMode,
|
|
194538
|
-
onStarted: () => {
|
|
194539
|
-
input.budgetTracker.markStarted(group.reservation);
|
|
194540
|
-
|
|
195072
|
+
onStarted: (executionIdentity) => {
|
|
195073
|
+
input.budgetTracker.markStarted(group.reservation, executionIdentity);
|
|
195074
|
+
const event = buildAgentStartedEvent({
|
|
195075
|
+
traceId: input.traceId,
|
|
195076
|
+
agent: group.verifier,
|
|
195077
|
+
role: "finding_verifier",
|
|
195078
|
+
executionIdentity: input.budgetTracker.executionIdentity(group.reservation)
|
|
195079
|
+
});
|
|
195080
|
+
return input.trace.write(event).catch(() => {
|
|
195081
|
+
warnings.push("AUDIT_WRITE_FAILED: agent_started event could not be recorded.");
|
|
195082
|
+
});
|
|
194541
195083
|
}
|
|
194542
195084
|
}));
|
|
194543
195085
|
let results;
|
|
@@ -194679,8 +195221,8 @@ function buildCrossModelAnalysis(judge, reviewMode) {
|
|
|
194679
195221
|
}
|
|
194680
195222
|
async function runBudgetedJudge(input) {
|
|
194681
195223
|
const configuredProvider = input.requestedProvider ?? input.config.provider;
|
|
194682
|
-
const
|
|
194683
|
-
if (
|
|
195224
|
+
const judgeRoute = resolveJudgeCallRoute(input.config.mode, configuredProvider, input.env);
|
|
195225
|
+
if (!judgeRoute.llmAvailable) {
|
|
194684
195226
|
return runJudge(input);
|
|
194685
195227
|
}
|
|
194686
195228
|
const fallback = () => runJudge({
|
|
@@ -194739,8 +195281,10 @@ async function runBudgetedJudge(input) {
|
|
|
194739
195281
|
const judge = await runJudge({ ...input, timeoutMs });
|
|
194740
195282
|
const usage = normalizeModelTokenUsage(judge.usage);
|
|
194741
195283
|
input.budgetTracker.complete(reservation, {
|
|
194742
|
-
...usage ? { usage } : {}
|
|
195284
|
+
...usage ? { usage } : {},
|
|
195285
|
+
...judge.executionIdentity ? { executionIdentity: judge.executionIdentity } : {}
|
|
194743
195286
|
});
|
|
195287
|
+
const executionIdentity = input.budgetTracker.executionIdentity(reservation);
|
|
194744
195288
|
await input.trace.write({
|
|
194745
195289
|
type: "model_call_completed",
|
|
194746
195290
|
traceId: input.traceId,
|
|
@@ -194748,6 +195292,7 @@ async function runBudgetedJudge(input) {
|
|
|
194748
195292
|
provider: judge.provider,
|
|
194749
195293
|
resultStatus: judge.status,
|
|
194750
195294
|
...usage ? { usage } : {},
|
|
195295
|
+
...executionIdentity ? { executionIdentity } : {},
|
|
194751
195296
|
timestamp: new Date().toISOString()
|
|
194752
195297
|
});
|
|
194753
195298
|
return judge;
|
|
@@ -194861,30 +195406,25 @@ async function runAgents(input) {
|
|
|
194861
195406
|
tool: input.tool,
|
|
194862
195407
|
prompt: buildAgentPrompt(input.tool, input.request, agent, role, {
|
|
194863
195408
|
requiredLenses,
|
|
194864
|
-
cisaEnabled: input.config.securityReview.cisaSecureByDesign.enabled
|
|
195409
|
+
cisaEnabled: input.config.securityReview.cisaSecureByDesign.enabled,
|
|
195410
|
+
maxFindingsTarget: input.budgetTracker.budget.maxFindingsPerAgent
|
|
194865
195411
|
}),
|
|
194866
195412
|
workspaceDir: input.workspaceDir,
|
|
194867
195413
|
timeoutMs: Math.min(input.request.options?.maxAgentTimeoutMs ?? Number.POSITIVE_INFINITY, agentConfig.timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS, input.budgetTracker.remainingWallTimeMs()),
|
|
194868
195414
|
deadlineAtEpochMs: input.budgetTracker.deadlineAtEpochMs,
|
|
195415
|
+
warnOutputBytes: input.budgetTracker.budget.effectiveWarnAgentOutputBytes,
|
|
194869
195416
|
maxOutputBytes: input.budgetTracker.budget.maxAgentOutputBytes,
|
|
194870
195417
|
networkMode: input.networkMode,
|
|
194871
|
-
onStarted: () => {
|
|
194872
|
-
input.budgetTracker.markStarted(reservation);
|
|
195418
|
+
onStarted: (executionIdentity) => {
|
|
195419
|
+
input.budgetTracker.markStarted(reservation, executionIdentity);
|
|
194873
195420
|
if (!acceptingStartedEvents)
|
|
194874
195421
|
return Promise.resolve();
|
|
194875
|
-
const event = {
|
|
194876
|
-
type: "agent_started",
|
|
195422
|
+
const event = buildAgentStartedEvent({
|
|
194877
195423
|
traceId: input.traceId,
|
|
194878
195424
|
agent,
|
|
194879
195425
|
role,
|
|
194880
|
-
|
|
194881
|
-
};
|
|
194882
|
-
if (agentConfig.model) {
|
|
194883
|
-
event.model = sanitizeTextForDisplay(agentConfig.model);
|
|
194884
|
-
}
|
|
194885
|
-
if (agent === "codex" && input.config.agents.codex.provider === CODEX_OPENROUTER_PROVIDER) {
|
|
194886
|
-
event.provider = CODEX_OPENROUTER_PROVIDER;
|
|
194887
|
-
}
|
|
195426
|
+
executionIdentity: input.budgetTracker.executionIdentity(reservation)
|
|
195427
|
+
});
|
|
194888
195428
|
const write = (async () => {
|
|
194889
195429
|
try {
|
|
194890
195430
|
await input.trace.write(event);
|
|
@@ -194936,7 +195476,8 @@ async function runAgents(input) {
|
|
|
194936
195476
|
}
|
|
194937
195477
|
};
|
|
194938
195478
|
});
|
|
194939
|
-
|
|
195479
|
+
const normalizedResults = orderedResults.map((result) => normalizeAgentRunResult(result, input.budgetTracker.budget.maxFindingsPerAgent));
|
|
195480
|
+
for (const result of normalizedResults) {
|
|
194940
195481
|
const reservation = reservations.get(result.agent);
|
|
194941
195482
|
if (!reservation)
|
|
194942
195483
|
continue;
|
|
@@ -194951,7 +195492,7 @@ async function runAgents(input) {
|
|
|
194951
195492
|
input.budgetTracker.markIncomplete("coverage_incomplete");
|
|
194952
195493
|
}
|
|
194953
195494
|
}
|
|
194954
|
-
await Promise.all(
|
|
195495
|
+
await Promise.all(normalizedResults.map((result) => {
|
|
194955
195496
|
const event = {
|
|
194956
195497
|
type: "agent_completed",
|
|
194957
195498
|
traceId: input.traceId,
|
|
@@ -194966,12 +195507,20 @@ async function runAgents(input) {
|
|
|
194966
195507
|
event.errorCode = result.error.code;
|
|
194967
195508
|
event.errorDetail = result.error.detail;
|
|
194968
195509
|
}
|
|
195510
|
+
if (result.salvaged !== undefined)
|
|
195511
|
+
event.salvaged = result.salvaged;
|
|
195512
|
+
if (result.reportedFindings !== undefined) {
|
|
195513
|
+
event.reportedFindings = result.reportedFindings;
|
|
195514
|
+
}
|
|
195515
|
+
if (result.findingsTargetExceeded !== undefined) {
|
|
195516
|
+
event.findingsTargetExceeded = result.findingsTargetExceeded;
|
|
195517
|
+
}
|
|
194969
195518
|
if (input.config.audit.includeRawAgentOutput && result.rawText) {
|
|
194970
195519
|
event.rawText = sanitizeTextForRawOutput(result.rawText);
|
|
194971
195520
|
}
|
|
194972
195521
|
return input.trace.write(event);
|
|
194973
195522
|
}));
|
|
194974
|
-
return
|
|
195523
|
+
return normalizedResults;
|
|
194975
195524
|
}
|
|
194976
195525
|
async function skipReservedPrimaryAgents(input) {
|
|
194977
195526
|
const results = [];
|
|
@@ -195021,20 +195570,44 @@ async function finalizeModelCallResult(input) {
|
|
|
195021
195570
|
});
|
|
195022
195571
|
return;
|
|
195023
195572
|
}
|
|
195024
|
-
input.budgetTracker.markStarted(input.reservation);
|
|
195573
|
+
input.budgetTracker.markStarted(input.reservation, input.result.executionIdentity);
|
|
195025
195574
|
const usage = normalizeModelTokenUsage(input.result.usage);
|
|
195026
|
-
const
|
|
195575
|
+
const { messageBytes, thoughtBytes, outputBytes } = resolveOutputByteMetrics(input.result);
|
|
195576
|
+
const warningThreshold = input.budgetTracker.budget.effectiveWarnAgentOutputBytes;
|
|
195577
|
+
const warningTriggered = warningThreshold !== undefined && outputBytes !== undefined && (input.result.outputWarningTriggered === true || outputBytes >= warningThreshold);
|
|
195578
|
+
const outputWarningTriggered = warningTriggered || input.result.outputWarningTriggered !== undefined ? warningTriggered : undefined;
|
|
195027
195579
|
input.budgetTracker.complete(input.reservation, {
|
|
195580
|
+
...messageBytes === undefined ? {} : { messageBytes },
|
|
195581
|
+
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
195028
195582
|
...outputBytes === undefined ? {} : { outputBytes },
|
|
195583
|
+
...outputWarningTriggered === undefined ? {} : { outputWarningTriggered },
|
|
195584
|
+
...input.result.salvaged === undefined ? {} : { salvaged: input.result.salvaged },
|
|
195585
|
+
...input.result.reportedFindings === undefined ? {} : { reportedFindings: input.result.reportedFindings },
|
|
195586
|
+
...input.result.findingsTargetExceeded === undefined ? {} : { findingsTargetExceeded: input.result.findingsTargetExceeded },
|
|
195029
195587
|
...usage ? { usage } : {},
|
|
195588
|
+
...input.result.executionIdentity ? { executionIdentity: input.result.executionIdentity } : {},
|
|
195030
195589
|
...input.result.stopReason ? { stopReason: input.result.stopReason } : {}
|
|
195031
195590
|
});
|
|
195591
|
+
const executionIdentity = input.budgetTracker.executionIdentity(input.reservation);
|
|
195032
195592
|
if (input.result.error?.code === "AGENT_OUTPUT_LIMIT") {
|
|
195033
195593
|
input.budgetTracker.markIncomplete("agent_output_limit");
|
|
195034
195594
|
}
|
|
195035
195595
|
if (input.result.error?.code === "REVIEW_DEADLINE_EXCEEDED") {
|
|
195036
195596
|
input.budgetTracker.markIncomplete("deadline");
|
|
195037
195597
|
}
|
|
195598
|
+
if (outputWarningTriggered && warningThreshold !== undefined && messageBytes !== undefined && thoughtBytes !== undefined && outputBytes !== undefined) {
|
|
195599
|
+
await input.trace.write({
|
|
195600
|
+
type: "agent_output_warning",
|
|
195601
|
+
traceId: input.traceId,
|
|
195602
|
+
kind: input.reservation.kind,
|
|
195603
|
+
agent: input.reservation.agent,
|
|
195604
|
+
thresholdBytes: warningThreshold,
|
|
195605
|
+
messageBytes,
|
|
195606
|
+
thoughtBytes,
|
|
195607
|
+
outputBytes,
|
|
195608
|
+
timestamp: new Date().toISOString()
|
|
195609
|
+
});
|
|
195610
|
+
}
|
|
195038
195611
|
await input.trace.write({
|
|
195039
195612
|
type: "model_call_completed",
|
|
195040
195613
|
traceId: input.traceId,
|
|
@@ -195042,12 +195615,55 @@ async function finalizeModelCallResult(input) {
|
|
|
195042
195615
|
agent: input.reservation.agent,
|
|
195043
195616
|
resultStatus: input.result.status,
|
|
195044
195617
|
...input.result.error?.code ? { errorCode: input.result.error.code } : {},
|
|
195618
|
+
...messageBytes === undefined ? {} : { messageBytes },
|
|
195619
|
+
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
195045
195620
|
...outputBytes === undefined ? {} : { outputBytes },
|
|
195621
|
+
...outputWarningTriggered === undefined ? {} : { outputWarningTriggered },
|
|
195622
|
+
...input.result.salvaged === undefined ? {} : { salvaged: input.result.salvaged },
|
|
195623
|
+
...input.result.reportedFindings === undefined ? {} : { reportedFindings: input.result.reportedFindings },
|
|
195624
|
+
...input.result.findingsTargetExceeded === undefined ? {} : { findingsTargetExceeded: input.result.findingsTargetExceeded },
|
|
195046
195625
|
...usage ? { usage } : {},
|
|
195626
|
+
...executionIdentity ? { executionIdentity } : {},
|
|
195047
195627
|
...input.result.stopReason ? { stopReason: input.result.stopReason } : {},
|
|
195048
195628
|
timestamp: new Date().toISOString()
|
|
195049
195629
|
});
|
|
195050
195630
|
}
|
|
195631
|
+
function buildAgentStartedEvent(input) {
|
|
195632
|
+
const executionIdentity = normalizeModelExecutionIdentity(input.executionIdentity);
|
|
195633
|
+
return {
|
|
195634
|
+
type: "agent_started",
|
|
195635
|
+
traceId: input.traceId,
|
|
195636
|
+
agent: input.agent,
|
|
195637
|
+
role: input.role,
|
|
195638
|
+
...executionIdentity ? { executionIdentity } : {},
|
|
195639
|
+
...executionIdentity?.requestedModel ? { model: executionIdentity.requestedModel } : {},
|
|
195640
|
+
...executionIdentity?.providerRoute === "openrouter" ? { provider: "openrouter" } : {},
|
|
195641
|
+
timestamp: new Date().toISOString()
|
|
195642
|
+
};
|
|
195643
|
+
}
|
|
195644
|
+
function resolveOutputByteMetrics(result) {
|
|
195645
|
+
const rawTextBytes = result.rawText ? Buffer.byteLength(result.rawText, "utf8") : undefined;
|
|
195646
|
+
let messageBytes = result.messageBytes;
|
|
195647
|
+
let thoughtBytes = result.thoughtBytes;
|
|
195648
|
+
if (messageBytes === undefined && thoughtBytes === undefined) {
|
|
195649
|
+
if (rawTextBytes !== undefined) {
|
|
195650
|
+
messageBytes = rawTextBytes;
|
|
195651
|
+
thoughtBytes = result.outputBytes === undefined ? 0 : Math.max(0, result.outputBytes - rawTextBytes);
|
|
195652
|
+
} else if (result.outputBytes !== undefined) {
|
|
195653
|
+
messageBytes = result.outputBytes;
|
|
195654
|
+
thoughtBytes = 0;
|
|
195655
|
+
}
|
|
195656
|
+
} else if (messageBytes === undefined) {
|
|
195657
|
+
messageBytes = result.outputBytes === undefined ? rawTextBytes ?? 0 : Math.max(0, result.outputBytes - (thoughtBytes ?? 0));
|
|
195658
|
+
} else if (thoughtBytes === undefined) {
|
|
195659
|
+
thoughtBytes = result.outputBytes === undefined ? 0 : Math.max(0, result.outputBytes - messageBytes);
|
|
195660
|
+
}
|
|
195661
|
+
return {
|
|
195662
|
+
...messageBytes === undefined ? {} : { messageBytes },
|
|
195663
|
+
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
195664
|
+
...messageBytes === undefined || thoughtBytes === undefined ? {} : { outputBytes: messageBytes + thoughtBytes }
|
|
195665
|
+
};
|
|
195666
|
+
}
|
|
195051
195667
|
function isPreflightAgentFailure(result) {
|
|
195052
195668
|
return result.status === "failed" && [
|
|
195053
195669
|
"AGENT_CONFIG_INVALID",
|
|
@@ -195125,24 +195741,11 @@ function normalizeAgentRunResult(result, maxFindingsPerAgent) {
|
|
|
195125
195741
|
...result,
|
|
195126
195742
|
normalized: normalizeAgentOutput(result.agent, result.role, result.rawText)
|
|
195127
195743
|
} : result;
|
|
195128
|
-
const
|
|
195129
|
-
|
|
195130
|
-
|
|
195131
|
-
|
|
195132
|
-
|
|
195133
|
-
const limitedFindings = findings.map((finding, index) => ({ finding, index })).sort((left, right) => {
|
|
195134
|
-
const severity = compareSeverity(left.finding.severity, right.finding.severity);
|
|
195135
|
-
return severity === 0 ? left.index - right.index : severity;
|
|
195136
|
-
}).slice(0, maxFindingsPerAgent).map(({ finding }) => finding);
|
|
195137
|
-
return {
|
|
195138
|
-
result: {
|
|
195139
|
-
...normalizedResult,
|
|
195140
|
-
normalized: {
|
|
195141
|
-
...normalized,
|
|
195142
|
-
findings: limitedFindings
|
|
195143
|
-
}
|
|
195144
|
-
},
|
|
195145
|
-
findingsCapped: true
|
|
195744
|
+
const reportedFindings = normalizedResult.normalized?.findings.length;
|
|
195745
|
+
return reportedFindings === undefined ? normalizedResult : {
|
|
195746
|
+
...normalizedResult,
|
|
195747
|
+
reportedFindings,
|
|
195748
|
+
findingsTargetExceeded: reportedFindings > maxFindingsPerAgent
|
|
195146
195749
|
};
|
|
195147
195750
|
}
|
|
195148
195751
|
function agentOpinionSummary(result, includeRawText = false) {
|
|
@@ -195151,7 +195754,8 @@ function agentOpinionSummary(result, includeRawText = false) {
|
|
|
195151
195754
|
role: result.role,
|
|
195152
195755
|
summary: result.normalized?.summary ?? sanitizeTextForDisplay(result.error?.message ?? result.status),
|
|
195153
195756
|
status: result.status,
|
|
195154
|
-
errorCode: result.error?.code
|
|
195757
|
+
errorCode: result.error?.code,
|
|
195758
|
+
...result.salvaged === undefined ? {} : { salvaged: result.salvaged }
|
|
195155
195759
|
};
|
|
195156
195760
|
if (includeRawText && result.rawText) {
|
|
195157
195761
|
opinion.rawText = sanitizeTextForRawOutput(result.rawText);
|
|
@@ -195353,12 +195957,60 @@ async function writeReviewBudgetPlanned(input) {
|
|
|
195353
195957
|
requestFingerprint: input.requestFingerprint,
|
|
195354
195958
|
maxModelCalls: snapshot.executionBudget.maxModelCalls,
|
|
195355
195959
|
maxTotalWallTimeMs: snapshot.executionBudget.wallTime.limitMs,
|
|
195960
|
+
...snapshot.executionBudget.effectiveWarnAgentOutputBytes !== undefined ? {
|
|
195961
|
+
effectiveWarnAgentOutputBytes: snapshot.executionBudget.effectiveWarnAgentOutputBytes
|
|
195962
|
+
} : {},
|
|
195356
195963
|
maxAgentOutputBytes: snapshot.executionBudget.maxAgentOutputBytes,
|
|
195357
195964
|
maxFindingsPerAgent: snapshot.executionBudget.maxFindingsPerAgent,
|
|
195358
195965
|
skipOptionalPhasesWhenTokenUsageUnknown: snapshot.executionBudget.skipOptionalPhasesWhenTokenUsageUnknown,
|
|
195966
|
+
...snapshot.executionBudget.modelCallPlan,
|
|
195359
195967
|
timestamp: new Date().toISOString()
|
|
195360
195968
|
});
|
|
195361
195969
|
}
|
|
195970
|
+
function plannedBudgetWarnings(budgetTracker) {
|
|
195971
|
+
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);
|
|
195972
|
+
if (fallbackCalls === 0)
|
|
195973
|
+
return [];
|
|
195974
|
+
return [
|
|
195975
|
+
`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.`
|
|
195976
|
+
];
|
|
195977
|
+
}
|
|
195978
|
+
function outputWarningMessages(snapshot) {
|
|
195979
|
+
const threshold = snapshot.executionBudget.effectiveWarnAgentOutputBytes;
|
|
195980
|
+
if (threshold === undefined)
|
|
195981
|
+
return [];
|
|
195982
|
+
return snapshot.modelCalls.flatMap((call) => {
|
|
195983
|
+
if (call.status !== "completed" || !call.outputWarningTriggered || !call.agent) {
|
|
195984
|
+
return [];
|
|
195985
|
+
}
|
|
195986
|
+
const messageBytes = call.messageBytes ?? 0;
|
|
195987
|
+
const thoughtBytes = call.thoughtBytes ?? 0;
|
|
195988
|
+
const outputBytes = call.outputBytes ?? messageBytes + thoughtBytes;
|
|
195989
|
+
const outcome = call.stopReason === "cancelled" ? "the hard breaker subsequently stopped execution." : "execution continued.";
|
|
195990
|
+
return [
|
|
195991
|
+
`Agent ${call.agent} ${call.kind} output reached the ${threshold}-byte soft threshold (message: ${messageBytes}, thought: ${thoughtBytes}, total: ${outputBytes}); ${outcome}`
|
|
195992
|
+
];
|
|
195993
|
+
});
|
|
195994
|
+
}
|
|
195995
|
+
function tokenUsageWarningMessages(budgetTracker, snapshot) {
|
|
195996
|
+
const unknownCalls = snapshot.executionBudget.tokenUsage.unknownCalls;
|
|
195997
|
+
if (budgetTracker.budget.skipOptionalPhasesWhenTokenUsageUnknown || unknownCalls === 0) {
|
|
195998
|
+
return [];
|
|
195999
|
+
}
|
|
196000
|
+
return [
|
|
196001
|
+
`Token usage was not reported for ${unknownCalls} completed call(s); budget enforcement continued using calls, wall time, and bytes.`
|
|
196002
|
+
];
|
|
196003
|
+
}
|
|
196004
|
+
function configuredReviewModelCallPlan(config2, budget, env, requestedJudgeProvider) {
|
|
196005
|
+
const judgeRoute = resolveJudgeCallRoute(config2.judge.mode, requestedJudgeProvider ?? config2.judge.provider, env);
|
|
196006
|
+
return buildReviewModelCallPlan({
|
|
196007
|
+
maxModelCalls: budget.maxModelCalls,
|
|
196008
|
+
requiredPrimaryCalls: Object.values(config2.agents).filter((agent) => agent.enabled).length,
|
|
196009
|
+
verificationEnabled: config2.verification.enabled,
|
|
196010
|
+
verificationMaxFindings: config2.verification.maxFindings,
|
|
196011
|
+
llmJudgeAvailable: judgeRoute.llmAvailable
|
|
196012
|
+
});
|
|
196013
|
+
}
|
|
195362
196014
|
async function writeReviewBudgetCompleted(input) {
|
|
195363
196015
|
const snapshot = input.budgetTracker.snapshot();
|
|
195364
196016
|
await input.trace.write({
|