@kyo-so/cli 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.ja.md +1 -1
- package/README.md +44 -7
- package/README.zh-CN.md +1 -1
- package/dist/acp/AcpAgentProcess.d.ts +4 -1
- package/dist/acp/AgentOutputAccumulator.d.ts +29 -0
- package/dist/acp/codexRetryUpdate.d.ts +6 -0
- package/dist/bin/kyoso.js +1752 -692
- package/dist/cli/pluginRuntimeContract.d.ts +4 -4
- package/dist/cli/progress.d.ts +5 -0
- package/dist/config/projectScope.d.ts +1 -1
- package/dist/config/schema.d.ts +6 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/errors.d.ts +5 -0
- package/dist/core/progress.d.ts +83 -0
- package/dist/core/progressDispatcher.d.ts +12 -0
- package/dist/core/reviewBudget.d.ts +8 -0
- package/dist/core/runReview.d.ts +4 -0
- package/dist/core/types.d.ts +35 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1518 -659
- package/dist/judge/provider.d.ts +1 -0
- package/dist/judge/signals.d.ts +4 -0
- package/dist/mcp/progress.d.ts +18 -0
- package/dist/utils/env.d.ts +5 -1
- package/examples/kyoso.toml +7 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -183918,6 +183918,7 @@ ${typeof file2.content === "string" ? file2.content.slice(0, 2000) : ""}`)
|
|
|
183918
183918
|
var CODEX_OPENROUTER_PROVIDER = "openrouter";
|
|
183919
183919
|
var CODEX_DEFAULT_PROVIDER = "default";
|
|
183920
183920
|
var CODEX_OPENROUTER_MODEL_REQUIRED_ISSUE = "codex_openrouter_model_required";
|
|
183921
|
+
var CODEX_OPENROUTER_POLICY_REQUIRES_PROVIDER_ISSUE = "codex_openrouter_policy_requires_provider";
|
|
183921
183922
|
var baseAgentSchema = exports_external.object({
|
|
183922
183923
|
enabled: exports_external.boolean().default(true),
|
|
183923
183924
|
type: exports_external.literal("acp").default("acp"),
|
|
@@ -183940,12 +183941,29 @@ var baseAgentSchema = exports_external.object({
|
|
|
183940
183941
|
envWhitelist: exports_external.array(exports_external.string())
|
|
183941
183942
|
})
|
|
183942
183943
|
});
|
|
183944
|
+
var codexOpenRouterSchema = exports_external.object({
|
|
183945
|
+
streamIdleTimeoutMs: exports_external.number().int().min(1000).optional(),
|
|
183946
|
+
streamMaxRetries: exports_external.number().int().min(0).max(100).optional(),
|
|
183947
|
+
requestMaxRetries: exports_external.number().int().min(0).max(100).optional()
|
|
183948
|
+
});
|
|
183943
183949
|
var codexAgentSchema = baseAgentSchema.extend({
|
|
183944
183950
|
provider: exports_external.enum([CODEX_OPENROUTER_PROVIDER, CODEX_DEFAULT_PROVIDER]).optional(),
|
|
183945
183951
|
allowProjectProvider: exports_external.array(exports_external.string().min(1).refine(isAbsolute, {
|
|
183946
183952
|
message: "must contain only absolute project directory paths for exact matching"
|
|
183947
|
-
})).default([])
|
|
183953
|
+
})).default([]),
|
|
183954
|
+
openRouter: codexOpenRouterSchema.default({})
|
|
183948
183955
|
}).superRefine((agent, context) => {
|
|
183956
|
+
const hasOpenRouterPolicy = Object.values(agent.openRouter).some((value) => value !== undefined);
|
|
183957
|
+
if (hasOpenRouterPolicy && agent.provider !== CODEX_OPENROUTER_PROVIDER) {
|
|
183958
|
+
context.addIssue({
|
|
183959
|
+
code: exports_external.ZodIssueCode.custom,
|
|
183960
|
+
path: ["openRouter"],
|
|
183961
|
+
message: 'agents.codex.openRouter.* requires provider = "openrouter".',
|
|
183962
|
+
params: {
|
|
183963
|
+
kyosoIssue: CODEX_OPENROUTER_POLICY_REQUIRES_PROVIDER_ISSUE
|
|
183964
|
+
}
|
|
183965
|
+
});
|
|
183966
|
+
}
|
|
183949
183967
|
if (agent.provider !== CODEX_OPENROUTER_PROVIDER || (agent.model?.trim().length ?? 0) > 0) {
|
|
183950
183968
|
return;
|
|
183951
183969
|
}
|
|
@@ -184071,7 +184089,7 @@ function agentConfigLeafPaths(agent) {
|
|
|
184071
184089
|
`agents.${agent}.auth.envWhitelist`
|
|
184072
184090
|
];
|
|
184073
184091
|
if (agent === "codex") {
|
|
184074
|
-
paths.push("agents.codex.provider", "agents.codex.allowProjectProvider");
|
|
184092
|
+
paths.push("agents.codex.provider", "agents.codex.allowProjectProvider", "agents.codex.openRouter.streamIdleTimeoutMs", "agents.codex.openRouter.streamMaxRetries", "agents.codex.openRouter.requestMaxRetries");
|
|
184075
184093
|
}
|
|
184076
184094
|
return paths;
|
|
184077
184095
|
}
|
|
@@ -184302,6 +184320,9 @@ var kyosoConfigOverridePaths = [
|
|
|
184302
184320
|
"agents.codex.enabled",
|
|
184303
184321
|
"agents.codex.model",
|
|
184304
184322
|
"agents.codex.provider",
|
|
184323
|
+
"agents.codex.openRouter.streamIdleTimeoutMs",
|
|
184324
|
+
"agents.codex.openRouter.streamMaxRetries",
|
|
184325
|
+
"agents.codex.openRouter.requestMaxRetries",
|
|
184305
184326
|
"agents.codex.effort",
|
|
184306
184327
|
"agents.codex.role",
|
|
184307
184328
|
"agents.codex.timeoutMs",
|
|
@@ -185715,7 +185736,7 @@ async function loadProjectConfig(input) {
|
|
|
185715
185736
|
const extension = extname2(requestedPath);
|
|
185716
185737
|
if (extension === ".toml") {
|
|
185717
185738
|
const projectTomlConfig = await loadTomlConfigFile(canonicalPath);
|
|
185718
|
-
const
|
|
185739
|
+
const projectChangesOpenRouterPolicy = projectConfigChangesOpenRouterPolicy(projectTomlConfig, input.baseConfig);
|
|
185719
185740
|
await assertProjectOpenRouterAuthorization({
|
|
185720
185741
|
projectConfig: projectTomlConfig,
|
|
185721
185742
|
projectPath: requestedPath,
|
|
@@ -185733,7 +185754,7 @@ async function loadProjectConfig(input) {
|
|
|
185733
185754
|
configPath: requestedPath,
|
|
185734
185755
|
configTrustStatus: "not_found",
|
|
185735
185756
|
source: { path: requestedPath, layer: "project_toml" },
|
|
185736
|
-
warnings:
|
|
185757
|
+
warnings: projectChangesOpenRouterPolicy ? [openRouterProjectConfigWarning(requestedPath)] : []
|
|
185737
185758
|
};
|
|
185738
185759
|
}
|
|
185739
185760
|
if (extension === ".ts") {
|
|
@@ -185750,30 +185771,35 @@ function projectConfigSelectsDefaultProvider(config2) {
|
|
|
185750
185771
|
function projectConfigSuppliesCodexModel(config2) {
|
|
185751
185772
|
return flattenLeaves(config2).some((leaf) => leaf.path.join(".") === "agents.codex.model");
|
|
185752
185773
|
}
|
|
185753
|
-
function
|
|
185754
|
-
return
|
|
185774
|
+
function projectConfigSuppliesOpenRouterPolicy(config2) {
|
|
185775
|
+
return flattenLeaves(config2).some((leaf) => leaf.path.join(".").startsWith("agents.codex.openRouter."));
|
|
185776
|
+
}
|
|
185777
|
+
function projectConfigChangesOpenRouterPolicy(projectConfig, baseConfig) {
|
|
185778
|
+
return projectConfigSelectsOpenRouter(projectConfig) || configSelectsOpenRouter(baseConfig) && !projectConfigSelectsDefaultProvider(projectConfig) && (projectConfigSuppliesCodexModel(projectConfig) || projectConfigSuppliesOpenRouterPolicy(projectConfig));
|
|
185755
185779
|
}
|
|
185756
185780
|
function configSelectsOpenRouter(config2) {
|
|
185757
185781
|
return flattenLeaves(config2).some((leaf) => leaf.path.join(".") === "agents.codex.provider" && leaf.value === CODEX_OPENROUTER_PROVIDER);
|
|
185758
185782
|
}
|
|
185759
185783
|
function applyProjectCodexProviderReset(baseConfig, projectConfig, mergedConfig) {
|
|
185760
|
-
if (!projectConfigSelectsDefaultProvider(projectConfig) ||
|
|
185784
|
+
if (!projectConfigSelectsDefaultProvider(projectConfig) || !configSelectsOpenRouter(baseConfig) || !isRecord3(mergedConfig) || !isRecord3(mergedConfig.agents) || !isRecord3(mergedConfig.agents.codex)) {
|
|
185761
185785
|
return mergedConfig;
|
|
185762
185786
|
}
|
|
185763
|
-
const
|
|
185787
|
+
const shouldClearInheritedModel = !projectConfigSuppliesCodexModel(projectConfig);
|
|
185788
|
+
const shouldClearInheritedOpenRouterPolicy = !projectConfigSuppliesOpenRouterPolicy(projectConfig);
|
|
185789
|
+
const codexWithoutInheritedOpenRouterConfig = Object.fromEntries(Object.entries(mergedConfig.agents.codex).filter(([key]) => (!shouldClearInheritedModel || key !== "model") && (!shouldClearInheritedOpenRouterPolicy || key !== "openRouter")));
|
|
185764
185790
|
return {
|
|
185765
185791
|
...mergedConfig,
|
|
185766
185792
|
agents: {
|
|
185767
185793
|
...mergedConfig.agents,
|
|
185768
|
-
codex:
|
|
185794
|
+
codex: codexWithoutInheritedOpenRouterConfig
|
|
185769
185795
|
}
|
|
185770
185796
|
};
|
|
185771
185797
|
}
|
|
185772
185798
|
function openRouterProjectConfigWarning(configPath) {
|
|
185773
|
-
return `Project config ${sanitizeWarningText(configPath)} changes Codex OpenRouter routing under user-global authorization; it can route Codex review content through OpenRouter.`;
|
|
185799
|
+
return `Project config ${sanitizeWarningText(configPath)} changes Codex OpenRouter routing or transport retry policy under user-global authorization; it can route Codex review content through OpenRouter.`;
|
|
185774
185800
|
}
|
|
185775
185801
|
async function assertProjectOpenRouterAuthorization(input) {
|
|
185776
|
-
if (!
|
|
185802
|
+
if (!projectConfigChangesOpenRouterPolicy(input.projectConfig, input.baseConfig)) {
|
|
185777
185803
|
return;
|
|
185778
185804
|
}
|
|
185779
185805
|
if (await projectProviderIsAuthorized(input.baseConfig, input.projectDirectory)) {
|
|
@@ -185838,7 +185864,7 @@ async function loadProjectTsConfig(input) {
|
|
|
185838
185864
|
if (trustDecision.execute) {
|
|
185839
185865
|
const userConfig = await loadUserConfig(canonicalPath, source);
|
|
185840
185866
|
validateExplicitReviewBudgetThresholds(userConfig, input.baseConfig);
|
|
185841
|
-
const
|
|
185867
|
+
const projectChangesOpenRouterPolicy = projectConfigChangesOpenRouterPolicy(userConfig, input.baseConfig);
|
|
185842
185868
|
await assertProjectOpenRouterAuthorization({
|
|
185843
185869
|
projectConfig: userConfig,
|
|
185844
185870
|
projectPath: requestedPath,
|
|
@@ -185851,7 +185877,7 @@ async function loadProjectTsConfig(input) {
|
|
|
185851
185877
|
await trustConfig(trustStorePath, canonicalPath, configHash);
|
|
185852
185878
|
}
|
|
185853
185879
|
const projectMergedConfig = deepMerge2(input.baseConfig, userConfig);
|
|
185854
|
-
if (
|
|
185880
|
+
if (projectChangesOpenRouterPolicy) {
|
|
185855
185881
|
warnings.push(openRouterProjectConfigWarning(requestedPath));
|
|
185856
185882
|
}
|
|
185857
185883
|
return {
|
|
@@ -185962,6 +185988,11 @@ async function exists(path) {
|
|
|
185962
185988
|
|
|
185963
185989
|
// src/config/configOverrides.ts
|
|
185964
185990
|
var NUMBER_VALUE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
185991
|
+
var UNSET_NUMBER_OVERRIDE_PATHS = new Set([
|
|
185992
|
+
"agents.codex.openRouter.streamIdleTimeoutMs",
|
|
185993
|
+
"agents.codex.openRouter.streamMaxRetries",
|
|
185994
|
+
"agents.codex.openRouter.requestMaxRetries"
|
|
185995
|
+
]);
|
|
185965
185996
|
function applyConfigOverrides(config2, assignments) {
|
|
185966
185997
|
if (assignments.length === 0)
|
|
185967
185998
|
return config2;
|
|
@@ -185969,9 +186000,9 @@ function applyConfigOverrides(config2, assignments) {
|
|
|
185969
186000
|
const baseConfig = config2;
|
|
185970
186001
|
const overridden = structuredClone(config2);
|
|
185971
186002
|
for (const override of overrides) {
|
|
185972
|
-
writePath2(overridden, override.path, parseConfigOverrideValue(override.value, readPath2(baseConfig, override.path)));
|
|
186003
|
+
writePath2(overridden, override.path, parseConfigOverrideValue(override.value, readPath2(baseConfig, override.path), override.path));
|
|
185973
186004
|
}
|
|
185974
|
-
|
|
186005
|
+
clearInheritedOpenRouterConfigForProviderReset(baseConfig, overridden, overrides);
|
|
185975
186006
|
assertOpenRouterProviderOverrideIncludesModel(baseConfig, overridden, overrides);
|
|
185976
186007
|
const parsed = kyosoConfigSchema.safeParse(overridden);
|
|
185977
186008
|
if (parsed.success)
|
|
@@ -185992,17 +186023,23 @@ function assertOpenRouterProviderOverrideIncludesModel(baseConfig, overridden, o
|
|
|
185992
186023
|
const assignment = findAssignmentForPath(overrides, providerPath.join(".")) ?? "agents.codex.provider=openrouter";
|
|
185993
186024
|
throw new Error(`Invalid --set value ${JSON.stringify(assignment)}: selecting agents.codex.provider=openrouter requires agents.codex.model in the same --set invocation.`);
|
|
185994
186025
|
}
|
|
185995
|
-
function
|
|
186026
|
+
function clearInheritedOpenRouterConfigForProviderReset(baseConfig, overridden, overrides) {
|
|
185996
186027
|
const providerPath = ["agents", "codex", "provider"];
|
|
185997
186028
|
const modelPath = ["agents", "codex", "model"];
|
|
186029
|
+
const openRouterPath = ["agents", "codex", "openRouter"];
|
|
185998
186030
|
const selectsDefaultProvider = overrides.some((override) => override.path.join(".") === providerPath.join(".") && override.value === CODEX_DEFAULT_PROVIDER);
|
|
185999
186031
|
const suppliesModel = overrides.some((override) => override.path.join(".") === modelPath.join("."));
|
|
186000
|
-
|
|
186032
|
+
const suppliesOpenRouterPolicy = overrides.some((override) => override.path.join(".").startsWith(`${openRouterPath.join(".")}.`));
|
|
186033
|
+
if (!selectsDefaultProvider || readPath2(baseConfig, providerPath) !== CODEX_OPENROUTER_PROVIDER || readPath2(overridden, providerPath) !== CODEX_DEFAULT_PROVIDER) {
|
|
186001
186034
|
return;
|
|
186002
186035
|
}
|
|
186003
186036
|
const codex = readPath2(overridden, ["agents", "codex"]);
|
|
186004
|
-
if (isRecord4(codex))
|
|
186037
|
+
if (!isRecord4(codex))
|
|
186038
|
+
return;
|
|
186039
|
+
if (!suppliesModel)
|
|
186005
186040
|
delete codex.model;
|
|
186041
|
+
if (!suppliesOpenRouterPolicy)
|
|
186042
|
+
delete codex.openRouter;
|
|
186006
186043
|
}
|
|
186007
186044
|
function findAssignmentForPath(overrides, path) {
|
|
186008
186045
|
for (let index = overrides.length - 1;index >= 0; index -= 1) {
|
|
@@ -186028,7 +186065,7 @@ function parseConfigOverride(assignment) {
|
|
|
186028
186065
|
value: assignment.slice(separator + 1)
|
|
186029
186066
|
};
|
|
186030
186067
|
}
|
|
186031
|
-
function parseConfigOverrideValue(value, currentValue) {
|
|
186068
|
+
function parseConfigOverrideValue(value, currentValue, path) {
|
|
186032
186069
|
if (typeof currentValue === "boolean") {
|
|
186033
186070
|
if (value === "true")
|
|
186034
186071
|
return true;
|
|
@@ -186036,7 +186073,7 @@ function parseConfigOverrideValue(value, currentValue) {
|
|
|
186036
186073
|
return false;
|
|
186037
186074
|
return value;
|
|
186038
186075
|
}
|
|
186039
|
-
if (typeof currentValue === "number" && NUMBER_VALUE.test(value)) {
|
|
186076
|
+
if ((typeof currentValue === "number" || currentValue === undefined && UNSET_NUMBER_OVERRIDE_PATHS.has(path.join("."))) && NUMBER_VALUE.test(value)) {
|
|
186040
186077
|
const parsed = Number(value);
|
|
186041
186078
|
if (Number.isFinite(parsed))
|
|
186042
186079
|
return parsed;
|
|
@@ -190003,6 +190040,29 @@ var legacyClientNotificationMethods = new Set([
|
|
|
190003
190040
|
CLIENT_METHODS.elicitation_complete
|
|
190004
190041
|
]);
|
|
190005
190042
|
|
|
190043
|
+
// src/core/errors.ts
|
|
190044
|
+
class KyosoRequestError extends Error {
|
|
190045
|
+
code;
|
|
190046
|
+
constructor(message, code) {
|
|
190047
|
+
super(message);
|
|
190048
|
+
this.code = code;
|
|
190049
|
+
this.name = "KyosoRequestError";
|
|
190050
|
+
}
|
|
190051
|
+
}
|
|
190052
|
+
|
|
190053
|
+
class KyosoCancellationError extends Error {
|
|
190054
|
+
code = "REQUEST_CANCELLED";
|
|
190055
|
+
constructor(message = "Kyoso review was cancelled.") {
|
|
190056
|
+
super(message);
|
|
190057
|
+
this.name = "KyosoCancellationError";
|
|
190058
|
+
}
|
|
190059
|
+
}
|
|
190060
|
+
function throwIfAborted(signal) {
|
|
190061
|
+
if (!signal?.aborted)
|
|
190062
|
+
return;
|
|
190063
|
+
throw new KyosoCancellationError(typeof signal.reason === "string" ? signal.reason : undefined);
|
|
190064
|
+
}
|
|
190065
|
+
|
|
190006
190066
|
// src/core/modelExecutionIdentity.ts
|
|
190007
190067
|
var MODEL_EXECUTION_IDENTITY_MAX_CHARS = 160;
|
|
190008
190068
|
var MODEL_PROVIDER_ROUTES = new Set([
|
|
@@ -190112,13 +190172,18 @@ var OPENROUTER_EXCLUDED_CREDENTIAL_ENV_KEYS = [
|
|
|
190112
190172
|
var OPENROUTER_API_KEY_ENV = "OPENROUTER_API_KEY";
|
|
190113
190173
|
var KYOSO_OPENROUTER_PROVIDER_ID = "kyoso-openrouter";
|
|
190114
190174
|
var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
190115
|
-
|
|
190116
|
-
|
|
190117
|
-
|
|
190118
|
-
|
|
190119
|
-
|
|
190120
|
-
|
|
190121
|
-
|
|
190175
|
+
function buildOpenRouterProviderPreset(options, baseUrl = OPENROUTER_BASE_URL) {
|
|
190176
|
+
return {
|
|
190177
|
+
name: "OpenRouter",
|
|
190178
|
+
base_url: baseUrl,
|
|
190179
|
+
env_key: OPENROUTER_API_KEY_ENV,
|
|
190180
|
+
wire_api: "responses",
|
|
190181
|
+
requires_openai_auth: false,
|
|
190182
|
+
...options.streamIdleTimeoutMs === undefined ? {} : { stream_idle_timeout_ms: options.streamIdleTimeoutMs },
|
|
190183
|
+
...options.streamMaxRetries === undefined ? {} : { stream_max_retries: options.streamMaxRetries },
|
|
190184
|
+
...options.requestMaxRetries === undefined ? {} : { request_max_retries: options.requestMaxRetries }
|
|
190185
|
+
};
|
|
190186
|
+
}
|
|
190122
190187
|
|
|
190123
190188
|
class ChildEnvPreflightError extends Error {
|
|
190124
190189
|
code;
|
|
@@ -190177,7 +190242,7 @@ function buildChildEnvironment(parentEnv, whitelist, explicit, options = {}) {
|
|
|
190177
190242
|
}
|
|
190178
190243
|
if (openRouterSelected) {
|
|
190179
190244
|
discardOpenRouterExcludedCredentials(env);
|
|
190180
|
-
applyOpenRouterConfig(env, parentEnv, options.model, onCredentialPlaceholderDiscarded, options.onOpenRouterProvidersDiscarded ?? warnOpenRouterProvidersDiscarded);
|
|
190245
|
+
applyOpenRouterConfig(env, parentEnv, options.model, options.openRouter, options.openRouterBaseUrlForTest, onCredentialPlaceholderDiscarded, options.onOpenRouterProvidersDiscarded ?? warnOpenRouterProvidersDiscarded);
|
|
190181
190246
|
} else {
|
|
190182
190247
|
applyModelConfig(env, options.agent, options.model);
|
|
190183
190248
|
}
|
|
@@ -190234,7 +190299,7 @@ function applyModelConfig(env, agent, model) {
|
|
|
190234
190299
|
env.CODEX_CONFIG = JSON.stringify({ model });
|
|
190235
190300
|
}
|
|
190236
190301
|
}
|
|
190237
|
-
function applyOpenRouterConfig(env, parentEnv, model, onCredentialPlaceholderDiscarded, onOpenRouterProvidersDiscarded) {
|
|
190302
|
+
function applyOpenRouterConfig(env, parentEnv, model, openRouter = {}, baseUrl = OPENROUTER_BASE_URL, onCredentialPlaceholderDiscarded, onOpenRouterProvidersDiscarded) {
|
|
190238
190303
|
const configuredModel = model?.trim();
|
|
190239
190304
|
if (!configuredModel) {
|
|
190240
190305
|
throw new ChildEnvPreflightError("AGENT_CONFIG_INVALID", 'agents.codex.provider="openrouter" requires a non-empty agents.codex.model.');
|
|
@@ -190262,7 +190327,7 @@ function applyOpenRouterConfig(env, parentEnv, model, onCredentialPlaceholderDis
|
|
|
190262
190327
|
model: configuredModel,
|
|
190263
190328
|
model_provider: KYOSO_OPENROUTER_PROVIDER_ID,
|
|
190264
190329
|
model_providers: {
|
|
190265
|
-
[KYOSO_OPENROUTER_PROVIDER_ID]:
|
|
190330
|
+
[KYOSO_OPENROUTER_PROVIDER_ID]: buildOpenRouterProviderPreset(openRouter, baseUrl)
|
|
190266
190331
|
}
|
|
190267
190332
|
});
|
|
190268
190333
|
}
|
|
@@ -190379,6 +190444,117 @@ function limitAcpNdJsonLineBytes(input, maxLineBytes = MAX_ACP_NDJSON_LINE_BYTES
|
|
|
190379
190444
|
}));
|
|
190380
190445
|
}
|
|
190381
190446
|
|
|
190447
|
+
// src/acp/AgentOutputAccumulator.ts
|
|
190448
|
+
class AgentOutputAccumulator {
|
|
190449
|
+
segments = new Map;
|
|
190450
|
+
messageChunks = [];
|
|
190451
|
+
retryEpoch = 0;
|
|
190452
|
+
observedStreamRetries = 0;
|
|
190453
|
+
discardedRetryMessageBytes = 0;
|
|
190454
|
+
firstOutputAt;
|
|
190455
|
+
lastAcpUpdateAt;
|
|
190456
|
+
nextChunkSequence = 0;
|
|
190457
|
+
addMessageChunk(text, meta3) {
|
|
190458
|
+
this.noteOutput();
|
|
190459
|
+
const id = meta3.messageId ?? `epoch-${this.retryEpoch}`;
|
|
190460
|
+
const key = `${this.retryEpoch}\x00${id}`;
|
|
190461
|
+
const phase = meta3.phase ?? "unknown";
|
|
190462
|
+
let segment = this.segments.get(key);
|
|
190463
|
+
if (!segment) {
|
|
190464
|
+
segment = {
|
|
190465
|
+
id,
|
|
190466
|
+
phase,
|
|
190467
|
+
retryEpoch: this.retryEpoch,
|
|
190468
|
+
text: "",
|
|
190469
|
+
abandoned: false,
|
|
190470
|
+
lastChunkSequence: 0
|
|
190471
|
+
};
|
|
190472
|
+
this.segments.set(key, segment);
|
|
190473
|
+
} else if (segment.phase === "unknown" && phase !== "unknown") {
|
|
190474
|
+
segment.phase = phase;
|
|
190475
|
+
}
|
|
190476
|
+
const sequence = this.nextChunkSequence;
|
|
190477
|
+
this.nextChunkSequence += 1;
|
|
190478
|
+
segment.text += text;
|
|
190479
|
+
segment.lastChunkSequence = sequence;
|
|
190480
|
+
this.messageChunks.push({ segment, text, sequence });
|
|
190481
|
+
}
|
|
190482
|
+
addThoughtChunk(_text) {
|
|
190483
|
+
this.noteOutput();
|
|
190484
|
+
}
|
|
190485
|
+
noteUpdate() {
|
|
190486
|
+
this.lastAcpUpdateAt = new Date().toISOString();
|
|
190487
|
+
}
|
|
190488
|
+
markRetryBoundary() {
|
|
190489
|
+
let discardedMessageBytes = 0;
|
|
190490
|
+
for (const segment of this.segments.values()) {
|
|
190491
|
+
if (segment.retryEpoch !== this.retryEpoch || segment.abandoned)
|
|
190492
|
+
continue;
|
|
190493
|
+
segment.abandoned = true;
|
|
190494
|
+
discardedMessageBytes += Buffer.byteLength(segment.text, "utf8");
|
|
190495
|
+
}
|
|
190496
|
+
this.observedStreamRetries += 1;
|
|
190497
|
+
this.discardedRetryMessageBytes += discardedMessageBytes;
|
|
190498
|
+
this.retryEpoch += 1;
|
|
190499
|
+
return { discardedMessageBytes };
|
|
190500
|
+
}
|
|
190501
|
+
finalRawText() {
|
|
190502
|
+
if (this.observedStreamRetries === 0) {
|
|
190503
|
+
return this.messageChunks.map((chunk) => chunk.text).join("");
|
|
190504
|
+
}
|
|
190505
|
+
const finalAnswer = [...this.segments.values()].filter((segment) => !segment.abandoned && segment.phase === "final_answer").sort((left, right) => left.lastChunkSequence - right.lastChunkSequence).at(-1);
|
|
190506
|
+
if (finalAnswer)
|
|
190507
|
+
return finalAnswer.text;
|
|
190508
|
+
return this.messageChunks.filter((chunk) => !chunk.segment.abandoned && chunk.segment.retryEpoch === this.retryEpoch && chunk.segment.phase === "unknown").sort((left, right) => left.sequence - right.sequence).map((chunk) => chunk.text).join("");
|
|
190509
|
+
}
|
|
190510
|
+
metrics() {
|
|
190511
|
+
return {
|
|
190512
|
+
observedStreamRetries: this.observedStreamRetries,
|
|
190513
|
+
discardedRetryMessageBytes: this.discardedRetryMessageBytes,
|
|
190514
|
+
...this.firstOutputAt === undefined ? {} : { firstOutputAt: this.firstOutputAt },
|
|
190515
|
+
...this.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: this.lastAcpUpdateAt }
|
|
190516
|
+
};
|
|
190517
|
+
}
|
|
190518
|
+
noteOutput() {
|
|
190519
|
+
const timestamp = new Date().toISOString();
|
|
190520
|
+
this.firstOutputAt ??= timestamp;
|
|
190521
|
+
this.lastAcpUpdateAt = timestamp;
|
|
190522
|
+
}
|
|
190523
|
+
}
|
|
190524
|
+
|
|
190525
|
+
// src/acp/codexRetryUpdate.ts
|
|
190526
|
+
var RETRY_ATTEMPT_PATTERN = /Reconnecting\.\.\.\s*(\d+)\/(\d+)/;
|
|
190527
|
+
var DISPLAY_MESSAGE_FIELDS = ["title", "text", "message", "description"];
|
|
190528
|
+
function parseCodexRetryUpdate(update) {
|
|
190529
|
+
if (!isRecord8(update) || update.sessionUpdate !== "session_info_update") {
|
|
190530
|
+
return;
|
|
190531
|
+
}
|
|
190532
|
+
const meta3 = isRecord8(update._meta) ? update._meta : undefined;
|
|
190533
|
+
const codex = meta3 && isRecord8(meta3.codex) ? meta3.codex : undefined;
|
|
190534
|
+
const error51 = codex && isRecord8(codex.error) ? codex.error : undefined;
|
|
190535
|
+
if (error51?.willRetry !== true)
|
|
190536
|
+
return;
|
|
190537
|
+
const rawMessage = typeof error51.message === "string" ? error51.message : findDisplayMessage(update) ?? "model stream retry";
|
|
190538
|
+
const message = sanitizeTextForDisplay(rawMessage) || "model stream retry";
|
|
190539
|
+
const attemptMatch = RETRY_ATTEMPT_PATTERN.exec(message);
|
|
190540
|
+
return {
|
|
190541
|
+
message,
|
|
190542
|
+
...attemptMatch?.[1] === undefined ? {} : { attempt: Number.parseInt(attemptMatch[1], 10) },
|
|
190543
|
+
...attemptMatch?.[2] === undefined ? {} : { maxRetries: Number.parseInt(attemptMatch[2], 10) }
|
|
190544
|
+
};
|
|
190545
|
+
}
|
|
190546
|
+
function findDisplayMessage(update) {
|
|
190547
|
+
for (const field of DISPLAY_MESSAGE_FIELDS) {
|
|
190548
|
+
const value = update[field];
|
|
190549
|
+
if (typeof value === "string")
|
|
190550
|
+
return value;
|
|
190551
|
+
}
|
|
190552
|
+
return;
|
|
190553
|
+
}
|
|
190554
|
+
function isRecord8(value) {
|
|
190555
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
190556
|
+
}
|
|
190557
|
+
|
|
190382
190558
|
// src/core/findingAdmission.ts
|
|
190383
190559
|
import { createHash as createHash2 } from "node:crypto";
|
|
190384
190560
|
var SAFETY_CATEGORIES = new Set([
|
|
@@ -190860,7 +191036,7 @@ function isSeverity(value) {
|
|
|
190860
191036
|
return typeof value === "string" && severities.includes(value);
|
|
190861
191037
|
}
|
|
190862
191038
|
function normalizeCisaSecureByDesign(value) {
|
|
190863
|
-
if (!
|
|
191039
|
+
if (!isRecord9(value))
|
|
190864
191040
|
return;
|
|
190865
191041
|
const normalized = {};
|
|
190866
191042
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -190901,7 +191077,7 @@ function isEvidenceQuality(value) {
|
|
|
190901
191077
|
return typeof value === "string" && evidenceQualities.includes(value);
|
|
190902
191078
|
}
|
|
190903
191079
|
function isStrictAgentOpinion(value) {
|
|
190904
|
-
if (!
|
|
191080
|
+
if (!isRecord9(value) || !hasOnlyKeys(value, STRICT_ROOT_KEYS))
|
|
190905
191081
|
return false;
|
|
190906
191082
|
if (typeof value.summary !== "string")
|
|
190907
191083
|
return false;
|
|
@@ -190911,7 +191087,7 @@ function isStrictAgentOpinion(value) {
|
|
|
190911
191087
|
return value.cisaSecureByDesign === undefined || isStrictCisaSecureByDesign(value.cisaSecureByDesign);
|
|
190912
191088
|
}
|
|
190913
191089
|
function isStrictFinding(value) {
|
|
190914
|
-
if (!
|
|
191090
|
+
if (!isRecord9(value) || !hasOnlyKeys(value, STRICT_FINDING_KEYS)) {
|
|
190915
191091
|
return false;
|
|
190916
191092
|
}
|
|
190917
191093
|
if (!isSeverity(value.severity) || !isCategory(value.category) || !isNonEmptyString(value.title) || !isNonEmptyString(value.evidence) || !isNonEmptyString(value.recommendation) || !isConfidence(value.confidence)) {
|
|
@@ -190923,11 +191099,11 @@ function isStrictFinding(value) {
|
|
|
190923
191099
|
return true;
|
|
190924
191100
|
}
|
|
190925
191101
|
function isStrictFindingFiles(value) {
|
|
190926
|
-
return Array.isArray(value) && value.every((item) =>
|
|
191102
|
+
return Array.isArray(value) && value.every((item) => isRecord9(item) && hasOnlyKeys(item, STRICT_FILE_KEYS) && isNonEmptyString(item.path) && isOptionalLineNumber(item.lineStart) && isOptionalLineNumber(item.lineEnd));
|
|
190927
191103
|
}
|
|
190928
191104
|
function isStrictEvidenceRefs(value) {
|
|
190929
191105
|
return Array.isArray(value) && value.length <= MAX_EVIDENCE_REFS2 && value.every((item) => {
|
|
190930
|
-
if (!
|
|
191106
|
+
if (!isRecord9(item) || !hasOnlyKeys(item, STRICT_EVIDENCE_REF_KEYS)) {
|
|
190931
191107
|
return false;
|
|
190932
191108
|
}
|
|
190933
191109
|
if (item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
@@ -190943,7 +191119,7 @@ function isStrictEvidenceRefs(value) {
|
|
|
190943
191119
|
});
|
|
190944
191120
|
}
|
|
190945
191121
|
function isStrictCisaSecureByDesign(value) {
|
|
190946
|
-
if (!
|
|
191122
|
+
if (!isRecord9(value) || !hasOnlyKeys(value, STRICT_CISA_KEYS))
|
|
190947
191123
|
return false;
|
|
190948
191124
|
for (const key of [
|
|
190949
191125
|
"customerSecurityOutcomes",
|
|
@@ -190982,7 +191158,7 @@ function normalizeFindingFiles(value) {
|
|
|
190982
191158
|
if (!Array.isArray(value))
|
|
190983
191159
|
return;
|
|
190984
191160
|
const files = value.flatMap((item) => {
|
|
190985
|
-
if (!
|
|
191161
|
+
if (!isRecord9(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
190986
191162
|
return [];
|
|
190987
191163
|
}
|
|
190988
191164
|
const file2 = {
|
|
@@ -191002,7 +191178,7 @@ function normalizeEvidenceRefs2(value) {
|
|
|
191002
191178
|
if (!Array.isArray(value))
|
|
191003
191179
|
return;
|
|
191004
191180
|
const references = value.slice(0, MAX_EVIDENCE_REFS2).flatMap((item) => {
|
|
191005
|
-
if (!
|
|
191181
|
+
if (!isRecord9(item) || item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
191006
191182
|
return [];
|
|
191007
191183
|
}
|
|
191008
191184
|
const reference = { kind: item.kind };
|
|
@@ -191025,18 +191201,23 @@ function normalizeEvidenceRefs2(value) {
|
|
|
191025
191201
|
function normalizeLineNumber(value) {
|
|
191026
191202
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= MAX_EVIDENCE_LINE2 ? value : undefined;
|
|
191027
191203
|
}
|
|
191028
|
-
function
|
|
191204
|
+
function isRecord9(value) {
|
|
191029
191205
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
191030
191206
|
}
|
|
191031
191207
|
|
|
191032
191208
|
// src/acp/AcpAgentProcess.ts
|
|
191209
|
+
var DEFAULT_PROGRESS_HEARTBEAT_MS = 15000;
|
|
191210
|
+
var ACTIVITY_PROGRESS_THROTTLE_MS = 3000;
|
|
191211
|
+
|
|
191033
191212
|
class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
191034
191213
|
config;
|
|
191035
191214
|
parentEnv;
|
|
191036
|
-
|
|
191215
|
+
internalOptions;
|
|
191216
|
+
constructor(config2, parentEnv = process.env, internalOptions = {}) {
|
|
191037
191217
|
super();
|
|
191038
191218
|
this.config = config2;
|
|
191039
191219
|
this.parentEnv = parentEnv;
|
|
191220
|
+
this.internalOptions = internalOptions;
|
|
191040
191221
|
}
|
|
191041
191222
|
async runAgent(input) {
|
|
191042
191223
|
const agentConfig = this.config.agents[input.agent];
|
|
@@ -191057,7 +191238,9 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
191057
191238
|
agent: input.agent,
|
|
191058
191239
|
model: agentConfig.model,
|
|
191059
191240
|
provider,
|
|
191060
|
-
preferApiKey: agentConfig.auth.preferApiKey
|
|
191241
|
+
preferApiKey: agentConfig.auth.preferApiKey,
|
|
191242
|
+
openRouter: input.agent === "codex" ? this.config.agents.codex.openRouter : undefined,
|
|
191243
|
+
openRouterBaseUrlForTest: input.agent === "codex" ? this.internalOptions.openRouterBaseUrlForTest : undefined
|
|
191061
191244
|
});
|
|
191062
191245
|
} catch (error51) {
|
|
191063
191246
|
return {
|
|
@@ -191072,6 +191255,8 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
191072
191255
|
try {
|
|
191073
191256
|
return await runSubprocessAgent(input.agent, agentConfig, input, launchContext.env, launchContext.executionIdentity);
|
|
191074
191257
|
} catch (error51) {
|
|
191258
|
+
if (error51 instanceof KyosoCancellationError)
|
|
191259
|
+
throw error51;
|
|
191075
191260
|
return {
|
|
191076
191261
|
agent: input.agent,
|
|
191077
191262
|
role: input.role,
|
|
@@ -191084,6 +191269,7 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
191084
191269
|
}
|
|
191085
191270
|
}
|
|
191086
191271
|
async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutionIdentity) {
|
|
191272
|
+
throwIfAborted(input.signal);
|
|
191087
191273
|
const startedAt = new Date().toISOString();
|
|
191088
191274
|
const effectiveTimeoutMs = resolveEffectiveTimeoutMs(input);
|
|
191089
191275
|
if (effectiveTimeoutMs <= 0) {
|
|
@@ -191099,12 +191285,20 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191099
191285
|
}
|
|
191100
191286
|
};
|
|
191101
191287
|
}
|
|
191102
|
-
return new Promise((resolveResult) => {
|
|
191288
|
+
return new Promise((resolveResult, rejectResult) => {
|
|
191289
|
+
const abortController = new AbortController;
|
|
191290
|
+
let cancelSession;
|
|
191291
|
+
let timeout;
|
|
191103
191292
|
const child = spawn(agentConfig.command, agentConfig.args, {
|
|
191104
191293
|
cwd: input.workspaceDir,
|
|
191105
191294
|
env,
|
|
191106
191295
|
stdio: ["pipe", "pipe", "pipe"]
|
|
191107
191296
|
});
|
|
191297
|
+
let termination;
|
|
191298
|
+
const terminate = () => {
|
|
191299
|
+
termination ??= terminateChild(child);
|
|
191300
|
+
return termination;
|
|
191301
|
+
};
|
|
191108
191302
|
let stdout = "";
|
|
191109
191303
|
let stderr3 = "";
|
|
191110
191304
|
let settled = false;
|
|
@@ -191120,18 +191314,44 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191120
191314
|
return;
|
|
191121
191315
|
});
|
|
191122
191316
|
});
|
|
191123
|
-
|
|
191317
|
+
let onAbort;
|
|
191318
|
+
const cleanup = () => {
|
|
191319
|
+
if (timeout)
|
|
191320
|
+
clearTimeout(timeout);
|
|
191321
|
+
if (onAbort)
|
|
191322
|
+
input.signal?.removeEventListener("abort", onAbort);
|
|
191323
|
+
};
|
|
191124
191324
|
const resolveOnce = (result) => {
|
|
191125
191325
|
if (settled)
|
|
191126
191326
|
return;
|
|
191127
191327
|
settled = true;
|
|
191128
|
-
|
|
191328
|
+
cleanup();
|
|
191129
191329
|
const finalResult = spawned && result.executionIdentity === undefined ? { ...result, executionIdentity: launchExecutionIdentity } : result;
|
|
191130
191330
|
(startedWrite ?? Promise.resolve()).then(() => resolveResult(finalResult));
|
|
191131
191331
|
};
|
|
191132
|
-
const
|
|
191332
|
+
const rejectOnce = (error51) => {
|
|
191333
|
+
if (settled)
|
|
191334
|
+
return;
|
|
191335
|
+
settled = true;
|
|
191336
|
+
cleanup();
|
|
191337
|
+
rejectResult(error51);
|
|
191338
|
+
};
|
|
191339
|
+
onAbort = () => {
|
|
191340
|
+
const cancellation = cancellationFromSignal(input.signal);
|
|
191341
|
+
if (timeout)
|
|
191342
|
+
clearTimeout(timeout);
|
|
191343
|
+
abortController.abort(cancellation);
|
|
191344
|
+
cancelSession?.();
|
|
191345
|
+
terminate().then(() => rejectOnce(cancellation));
|
|
191346
|
+
};
|
|
191347
|
+
input.signal?.addEventListener("abort", onAbort, { once: true });
|
|
191348
|
+
if (input.signal?.aborted) {
|
|
191349
|
+
onAbort();
|
|
191350
|
+
return;
|
|
191351
|
+
}
|
|
191352
|
+
timeout = setTimeout(() => {
|
|
191133
191353
|
abortController.abort(new Error("Kyoso agent timeout"));
|
|
191134
|
-
|
|
191354
|
+
terminate();
|
|
191135
191355
|
const deadlineReached = input.deadlineAtEpochMs !== undefined && Date.now() >= input.deadlineAtEpochMs;
|
|
191136
191356
|
resolveOnce({
|
|
191137
191357
|
agent,
|
|
@@ -191160,7 +191380,9 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191160
191380
|
error: failure
|
|
191161
191381
|
});
|
|
191162
191382
|
});
|
|
191163
|
-
runAcpClientWorkflow(child, input, abortController, resolveEffortConfigOption(agent, agentConfig.effort), launchExecutionIdentity)
|
|
191383
|
+
runAcpClientWorkflow(child, input, abortController, resolveEffortConfigOption(agent, agentConfig.effort), launchExecutionIdentity, (cancel) => {
|
|
191384
|
+
cancelSession = cancel;
|
|
191385
|
+
}).then(({
|
|
191164
191386
|
rawText,
|
|
191165
191387
|
warnings,
|
|
191166
191388
|
usage,
|
|
@@ -191168,6 +191390,10 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191168
191390
|
thoughtBytes,
|
|
191169
191391
|
outputBytes,
|
|
191170
191392
|
outputWarningTriggered,
|
|
191393
|
+
observedStreamRetries,
|
|
191394
|
+
discardedRetryMessageBytes,
|
|
191395
|
+
firstOutputAt,
|
|
191396
|
+
lastAcpUpdateAt,
|
|
191171
191397
|
stopReason,
|
|
191172
191398
|
executionIdentity
|
|
191173
191399
|
}) => {
|
|
@@ -191185,6 +191411,10 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191185
191411
|
thoughtBytes,
|
|
191186
191412
|
outputBytes,
|
|
191187
191413
|
outputWarningTriggered,
|
|
191414
|
+
observedStreamRetries,
|
|
191415
|
+
...discardedRetryMessageBytes === 0 ? {} : { discardedRetryMessageBytes },
|
|
191416
|
+
...firstOutputAt === undefined ? {} : { firstOutputAt },
|
|
191417
|
+
...lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt },
|
|
191188
191418
|
stopReason,
|
|
191189
191419
|
executionIdentity,
|
|
191190
191420
|
...usage ? { usage } : {},
|
|
@@ -191197,6 +191427,10 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191197
191427
|
}
|
|
191198
191428
|
});
|
|
191199
191429
|
}).catch((error51) => {
|
|
191430
|
+
if (error51 instanceof KyosoCancellationError) {
|
|
191431
|
+
terminate().then(() => rejectOnce(error51));
|
|
191432
|
+
return;
|
|
191433
|
+
}
|
|
191200
191434
|
const outputLimitError = findOutputLimitError(error51, abortController);
|
|
191201
191435
|
if (outputLimitError) {
|
|
191202
191436
|
stdout = outputLimitError.rawText;
|
|
@@ -191211,6 +191445,12 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191211
191445
|
thoughtBytes: outputLimitError.thoughtBytes,
|
|
191212
191446
|
outputBytes: outputLimitError.outputBytes,
|
|
191213
191447
|
outputWarningTriggered: outputLimitError.outputWarningTriggered,
|
|
191448
|
+
observedStreamRetries: outputLimitError.metrics.observedStreamRetries,
|
|
191449
|
+
...outputLimitError.metrics.discardedRetryMessageBytes === 0 ? {} : {
|
|
191450
|
+
discardedRetryMessageBytes: outputLimitError.metrics.discardedRetryMessageBytes
|
|
191451
|
+
},
|
|
191452
|
+
...outputLimitError.metrics.firstOutputAt === undefined ? {} : { firstOutputAt: outputLimitError.metrics.firstOutputAt },
|
|
191453
|
+
...outputLimitError.metrics.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: outputLimitError.metrics.lastAcpUpdateAt },
|
|
191214
191454
|
stopReason: "cancelled",
|
|
191215
191455
|
startedAt,
|
|
191216
191456
|
completedAt: new Date().toISOString(),
|
|
@@ -191221,6 +191461,31 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191221
191461
|
});
|
|
191222
191462
|
return;
|
|
191223
191463
|
}
|
|
191464
|
+
if (error51 instanceof CodexTerminalSystemError) {
|
|
191465
|
+
stdout = error51.rawText;
|
|
191466
|
+
const failureText2 = [stderr3, formatAgentErrorDetail(error51)].filter((part) => part.trim().length > 0).join(`
|
|
191467
|
+
`);
|
|
191468
|
+
resolveOnce({
|
|
191469
|
+
agent,
|
|
191470
|
+
role: input.role,
|
|
191471
|
+
status: "failed",
|
|
191472
|
+
rawText: stdout,
|
|
191473
|
+
messageBytes: error51.messageBytes,
|
|
191474
|
+
thoughtBytes: error51.thoughtBytes,
|
|
191475
|
+
outputBytes: error51.outputBytes,
|
|
191476
|
+
outputWarningTriggered: error51.outputWarningTriggered,
|
|
191477
|
+
observedStreamRetries: error51.metrics.observedStreamRetries,
|
|
191478
|
+
...error51.metrics.discardedRetryMessageBytes === 0 ? {} : {
|
|
191479
|
+
discardedRetryMessageBytes: error51.metrics.discardedRetryMessageBytes
|
|
191480
|
+
},
|
|
191481
|
+
...error51.metrics.firstOutputAt === undefined ? {} : { firstOutputAt: error51.metrics.firstOutputAt },
|
|
191482
|
+
...error51.metrics.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: error51.metrics.lastAcpUpdateAt },
|
|
191483
|
+
startedAt,
|
|
191484
|
+
completedAt: new Date().toISOString(),
|
|
191485
|
+
error: buildAgentFailure(failureText2, "Agent process failed.")
|
|
191486
|
+
});
|
|
191487
|
+
return;
|
|
191488
|
+
}
|
|
191224
191489
|
if (error51 instanceof AcpNdJsonLineLimitError) {
|
|
191225
191490
|
abortController.abort(error51);
|
|
191226
191491
|
resolveOnce({
|
|
@@ -191252,7 +191517,7 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191252
191517
|
error: buildAgentFailure(failureText, "Agent process failed.")
|
|
191253
191518
|
});
|
|
191254
191519
|
}).finally(() => {
|
|
191255
|
-
|
|
191520
|
+
terminate();
|
|
191256
191521
|
});
|
|
191257
191522
|
child.on("close", (code) => {
|
|
191258
191523
|
if (settled || code === 0 || abortController.signal.aborted)
|
|
@@ -191270,7 +191535,7 @@ async function runSubprocessAgent(agent, agentConfig, input, env, launchExecutio
|
|
|
191270
191535
|
});
|
|
191271
191536
|
});
|
|
191272
191537
|
}
|
|
191273
|
-
async function runAcpClientWorkflow(child, input, abortController, configOption, launchExecutionIdentity) {
|
|
191538
|
+
async function runAcpClientWorkflow(child, input, abortController, configOption, launchExecutionIdentity, onSessionReady) {
|
|
191274
191539
|
if (!child.stdin || !child.stdout) {
|
|
191275
191540
|
throw new Error("Agent process did not expose stdio streams.");
|
|
191276
191541
|
}
|
|
@@ -191318,6 +191583,15 @@ async function runAcpClientWorkflow(child, input, abortController, configOption,
|
|
|
191318
191583
|
kyosoReadOnly: true
|
|
191319
191584
|
}
|
|
191320
191585
|
}).withSession(async (session) => {
|
|
191586
|
+
const cancelSession = () => {
|
|
191587
|
+
ctx.notify(methods.agent.session.cancel, {
|
|
191588
|
+
sessionId: session.sessionId
|
|
191589
|
+
}).catch(() => {
|
|
191590
|
+
return;
|
|
191591
|
+
});
|
|
191592
|
+
};
|
|
191593
|
+
onSessionReady(cancelSession);
|
|
191594
|
+
throwIfAborted(input.signal);
|
|
191321
191595
|
const warnings = [];
|
|
191322
191596
|
if (configOption) {
|
|
191323
191597
|
await ctx.request(methods.agent.session.setConfigOption, { sessionId: session.sessionId, ...configOption }, { cancellationSignal: abortController.signal }).catch((error51) => {
|
|
@@ -191330,61 +191604,154 @@ async function runAcpClientWorkflow(child, input, abortController, configOption,
|
|
|
191330
191604
|
console.error(`kyoso: ${warning}`);
|
|
191331
191605
|
});
|
|
191332
191606
|
}
|
|
191333
|
-
const
|
|
191334
|
-
cancellationSignal: abortController.signal
|
|
191335
|
-
});
|
|
191336
|
-
promptResponse.catch(() => {
|
|
191337
|
-
return;
|
|
191338
|
-
});
|
|
191339
|
-
let rawText = "";
|
|
191607
|
+
const accumulator = new AgentOutputAccumulator;
|
|
191340
191608
|
let messageBytes = 0;
|
|
191341
191609
|
let thoughtBytes = 0;
|
|
191342
191610
|
let outputBytes = 0;
|
|
191343
191611
|
let outputWarningTriggered = false;
|
|
191344
|
-
|
|
191345
|
-
|
|
191346
|
-
|
|
191347
|
-
|
|
191348
|
-
|
|
191349
|
-
|
|
191350
|
-
|
|
191351
|
-
...usage ? { usage } : {},
|
|
191352
|
-
messageBytes,
|
|
191353
|
-
thoughtBytes,
|
|
191354
|
-
outputBytes,
|
|
191355
|
-
outputWarningTriggered,
|
|
191356
|
-
stopReason: message.stopReason,
|
|
191357
|
-
executionIdentity: withReportedExecutionIdentity(launchExecutionIdentity, message.response._meta)
|
|
191358
|
-
};
|
|
191359
|
-
}
|
|
191360
|
-
const update = message.update;
|
|
191361
|
-
if (update.sessionUpdate !== "agent_message_chunk" && update.sessionUpdate !== "agent_thought_chunk" || update.content.type !== "text") {
|
|
191362
|
-
continue;
|
|
191363
|
-
}
|
|
191364
|
-
const chunkBytes = Buffer.byteLength(update.content.text, "utf8");
|
|
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;
|
|
191370
|
-
if (input.maxOutputBytes !== undefined && nextOutputBytes > input.maxOutputBytes) {
|
|
191371
|
-
const retainedRawText = isMessage ? `${rawText}${utf8Prefix(update.content.text, input.maxOutputBytes - outputBytes)}` : rawText;
|
|
191372
|
-
await ctx.notify(methods.agent.session.cancel, {
|
|
191373
|
-
sessionId: session.sessionId
|
|
191374
|
-
}).catch(() => {
|
|
191612
|
+
let codexReportedSystemError = false;
|
|
191613
|
+
const sessionStartedAtEpochMs = Date.now();
|
|
191614
|
+
let lastActivityAtEpochMs = 0;
|
|
191615
|
+
const emitProgress = (event) => {
|
|
191616
|
+
try {
|
|
191617
|
+
const progress = input.onProgress?.(event);
|
|
191618
|
+
Promise.resolve(progress).catch(() => {
|
|
191375
191619
|
return;
|
|
191376
191620
|
});
|
|
191377
|
-
|
|
191378
|
-
|
|
191621
|
+
} catch {}
|
|
191622
|
+
};
|
|
191623
|
+
const heartbeatMs = input.heartbeatMs ?? DEFAULT_PROGRESS_HEARTBEAT_MS;
|
|
191624
|
+
const heartbeat = input.onProgress && heartbeatMs > 0 ? setInterval(() => {
|
|
191625
|
+
const now = Date.now();
|
|
191626
|
+
const lastAcpUpdateAt = accumulator.metrics().lastAcpUpdateAt;
|
|
191627
|
+
const lastUpdateEpochMs = lastAcpUpdateAt ? Date.parse(lastAcpUpdateAt) : sessionStartedAtEpochMs;
|
|
191628
|
+
emitProgress({
|
|
191629
|
+
type: "agent_waiting",
|
|
191630
|
+
agent: input.agent,
|
|
191631
|
+
elapsedMs: Math.max(0, now - sessionStartedAtEpochMs),
|
|
191632
|
+
sinceLastAcpUpdateMs: Math.max(0, now - (Number.isFinite(lastUpdateEpochMs) ? lastUpdateEpochMs : sessionStartedAtEpochMs)),
|
|
191633
|
+
...input.streamIdleTimeoutMs === undefined ? {} : { streamIdleTimeoutMs: input.streamIdleTimeoutMs },
|
|
191634
|
+
timestamp: new Date(now).toISOString()
|
|
191635
|
+
});
|
|
191636
|
+
}, heartbeatMs) : undefined;
|
|
191637
|
+
heartbeat?.unref?.();
|
|
191638
|
+
const stopHeartbeat = () => {
|
|
191639
|
+
if (heartbeat)
|
|
191640
|
+
clearInterval(heartbeat);
|
|
191641
|
+
};
|
|
191642
|
+
input.signal?.addEventListener("abort", stopHeartbeat, { once: true });
|
|
191643
|
+
try {
|
|
191644
|
+
const promptResponse = session.prompt(input.prompt, {
|
|
191645
|
+
cancellationSignal: abortController.signal
|
|
191646
|
+
});
|
|
191647
|
+
const promptCompletion = promptResponse.catch((error51) => {
|
|
191648
|
+
if (input.signal?.aborted) {
|
|
191649
|
+
throw cancellationFromSignal(input.signal);
|
|
191650
|
+
}
|
|
191651
|
+
if (abortController.signal.aborted) {
|
|
191652
|
+
const reason = abortController.signal.reason;
|
|
191653
|
+
if (reason !== undefined)
|
|
191654
|
+
throw reason;
|
|
191655
|
+
}
|
|
191379
191656
|
throw error51;
|
|
191657
|
+
});
|
|
191658
|
+
const promptFailure = promptCompletion.then(() => new Promise(() => {
|
|
191659
|
+
return;
|
|
191660
|
+
}));
|
|
191661
|
+
for (;; ) {
|
|
191662
|
+
const message = await Promise.race([
|
|
191663
|
+
session.nextUpdate(),
|
|
191664
|
+
promptFailure
|
|
191665
|
+
]);
|
|
191666
|
+
if (message.kind === "stop") {
|
|
191667
|
+
await promptCompletion;
|
|
191668
|
+
if (codexReportedSystemError) {
|
|
191669
|
+
throw new CodexTerminalSystemError(accumulator.finalRawText(), messageBytes, thoughtBytes, outputBytes, outputWarningTriggered, accumulator.metrics());
|
|
191670
|
+
}
|
|
191671
|
+
const usage = normalizeUsage(message.response.usage);
|
|
191672
|
+
return {
|
|
191673
|
+
rawText: accumulator.finalRawText(),
|
|
191674
|
+
warnings,
|
|
191675
|
+
...usage ? { usage } : {},
|
|
191676
|
+
messageBytes,
|
|
191677
|
+
thoughtBytes,
|
|
191678
|
+
outputBytes,
|
|
191679
|
+
outputWarningTriggered,
|
|
191680
|
+
...accumulator.metrics(),
|
|
191681
|
+
stopReason: message.stopReason,
|
|
191682
|
+
executionIdentity: withReportedExecutionIdentity(launchExecutionIdentity, message.response._meta)
|
|
191683
|
+
};
|
|
191684
|
+
}
|
|
191685
|
+
const update = message.update;
|
|
191686
|
+
accumulator.noteUpdate();
|
|
191687
|
+
const retry = parseCodexRetryUpdate(update);
|
|
191688
|
+
if (retry) {
|
|
191689
|
+
codexReportedSystemError = false;
|
|
191690
|
+
const boundary = accumulator.markRetryBoundary();
|
|
191691
|
+
emitProgress({
|
|
191692
|
+
type: "agent_retrying",
|
|
191693
|
+
agent: input.agent,
|
|
191694
|
+
observedRetry: accumulator.metrics().observedStreamRetries,
|
|
191695
|
+
...retry.attempt === undefined ? {} : { attempt: retry.attempt },
|
|
191696
|
+
...retry.maxRetries === undefined ? {} : { maxRetries: retry.maxRetries },
|
|
191697
|
+
reason: retry.message,
|
|
191698
|
+
discardedMessageBytes: boundary.discardedMessageBytes,
|
|
191699
|
+
timestamp: new Date().toISOString()
|
|
191700
|
+
});
|
|
191701
|
+
continue;
|
|
191702
|
+
}
|
|
191703
|
+
const codexThreadStatus = readCodexThreadStatus(update);
|
|
191704
|
+
if (codexThreadStatus !== undefined) {
|
|
191705
|
+
codexReportedSystemError = codexThreadStatus === "systemError";
|
|
191706
|
+
continue;
|
|
191707
|
+
}
|
|
191708
|
+
if (update.sessionUpdate !== "agent_message_chunk" && update.sessionUpdate !== "agent_thought_chunk" || update.content.type !== "text") {
|
|
191709
|
+
continue;
|
|
191710
|
+
}
|
|
191711
|
+
const chunkBytes = Buffer.byteLength(update.content.text, "utf8");
|
|
191712
|
+
const isMessage = update.sessionUpdate === "agent_message_chunk";
|
|
191713
|
+
const nextMessageBytes = messageBytes + (isMessage ? chunkBytes : 0);
|
|
191714
|
+
const nextThoughtBytes = thoughtBytes + (isMessage ? 0 : chunkBytes);
|
|
191715
|
+
const nextOutputBytes = nextMessageBytes + nextThoughtBytes;
|
|
191716
|
+
const nextOutputWarningTriggered = outputWarningTriggered || input.warnOutputBytes !== undefined && nextOutputBytes >= input.warnOutputBytes;
|
|
191717
|
+
if (input.maxOutputBytes !== undefined && nextOutputBytes > input.maxOutputBytes) {
|
|
191718
|
+
if (isMessage) {
|
|
191719
|
+
accumulator.addMessageChunk(utf8Prefix(update.content.text, input.maxOutputBytes - outputBytes), readChunkMeta(update));
|
|
191720
|
+
}
|
|
191721
|
+
const retainedRawText = accumulator.finalRawText();
|
|
191722
|
+
await ctx.notify(methods.agent.session.cancel, {
|
|
191723
|
+
sessionId: session.sessionId
|
|
191724
|
+
}).catch(() => {
|
|
191725
|
+
return;
|
|
191726
|
+
});
|
|
191727
|
+
const error51 = new AgentOutputLimitError(retainedRawText, nextMessageBytes, nextThoughtBytes, nextOutputBytes, input.maxOutputBytes, nextOutputWarningTriggered, accumulator.metrics());
|
|
191728
|
+
abortController.abort(error51);
|
|
191729
|
+
throw error51;
|
|
191730
|
+
}
|
|
191731
|
+
if (isMessage) {
|
|
191732
|
+
accumulator.addMessageChunk(update.content.text, readChunkMeta(update));
|
|
191733
|
+
} else {
|
|
191734
|
+
accumulator.addThoughtChunk(update.content.text);
|
|
191735
|
+
}
|
|
191736
|
+
messageBytes = nextMessageBytes;
|
|
191737
|
+
thoughtBytes = nextThoughtBytes;
|
|
191738
|
+
outputBytes = nextOutputBytes;
|
|
191739
|
+
outputWarningTriggered = nextOutputWarningTriggered;
|
|
191740
|
+
const now = Date.now();
|
|
191741
|
+
if (now - lastActivityAtEpochMs >= ACTIVITY_PROGRESS_THROTTLE_MS) {
|
|
191742
|
+
lastActivityAtEpochMs = now;
|
|
191743
|
+
emitProgress({
|
|
191744
|
+
type: "agent_activity",
|
|
191745
|
+
agent: input.agent,
|
|
191746
|
+
activity: isMessage ? "message" : "thought",
|
|
191747
|
+
totalOutputBytes: outputBytes,
|
|
191748
|
+
timestamp: new Date(now).toISOString()
|
|
191749
|
+
});
|
|
191750
|
+
}
|
|
191380
191751
|
}
|
|
191381
|
-
|
|
191382
|
-
|
|
191383
|
-
|
|
191384
|
-
messageBytes = nextMessageBytes;
|
|
191385
|
-
thoughtBytes = nextThoughtBytes;
|
|
191386
|
-
outputBytes = nextOutputBytes;
|
|
191387
|
-
outputWarningTriggered = nextOutputWarningTriggered;
|
|
191752
|
+
} finally {
|
|
191753
|
+
stopHeartbeat();
|
|
191754
|
+
input.signal?.removeEventListener("abort", stopHeartbeat);
|
|
191388
191755
|
}
|
|
191389
191756
|
});
|
|
191390
191757
|
});
|
|
@@ -191408,7 +191775,8 @@ class AgentOutputLimitError extends Error {
|
|
|
191408
191775
|
outputBytes;
|
|
191409
191776
|
maxOutputBytes;
|
|
191410
191777
|
outputWarningTriggered;
|
|
191411
|
-
|
|
191778
|
+
metrics;
|
|
191779
|
+
constructor(rawText, messageBytes, thoughtBytes, outputBytes, maxOutputBytes, outputWarningTriggered, metrics) {
|
|
191412
191780
|
super(`Agent output exceeded ${maxOutputBytes} bytes.`);
|
|
191413
191781
|
this.rawText = rawText;
|
|
191414
191782
|
this.messageBytes = messageBytes;
|
|
@@ -191416,15 +191784,40 @@ class AgentOutputLimitError extends Error {
|
|
|
191416
191784
|
this.outputBytes = outputBytes;
|
|
191417
191785
|
this.maxOutputBytes = maxOutputBytes;
|
|
191418
191786
|
this.outputWarningTriggered = outputWarningTriggered;
|
|
191787
|
+
this.metrics = metrics;
|
|
191419
191788
|
this.name = "AgentOutputLimitError";
|
|
191420
191789
|
}
|
|
191421
191790
|
}
|
|
191791
|
+
|
|
191792
|
+
class CodexTerminalSystemError extends Error {
|
|
191793
|
+
rawText;
|
|
191794
|
+
messageBytes;
|
|
191795
|
+
thoughtBytes;
|
|
191796
|
+
outputBytes;
|
|
191797
|
+
outputWarningTriggered;
|
|
191798
|
+
metrics;
|
|
191799
|
+
constructor(rawText, messageBytes, thoughtBytes, outputBytes, outputWarningTriggered, metrics) {
|
|
191800
|
+
super(sanitizeTextForDisplay(rawText) || "Codex ACP reported a terminal system error.");
|
|
191801
|
+
this.rawText = rawText;
|
|
191802
|
+
this.messageBytes = messageBytes;
|
|
191803
|
+
this.thoughtBytes = thoughtBytes;
|
|
191804
|
+
this.outputBytes = outputBytes;
|
|
191805
|
+
this.outputWarningTriggered = outputWarningTriggered;
|
|
191806
|
+
this.metrics = metrics;
|
|
191807
|
+
this.name = "CodexTerminalSystemError";
|
|
191808
|
+
}
|
|
191809
|
+
}
|
|
191422
191810
|
function findOutputLimitError(error51, abortController) {
|
|
191423
191811
|
if (error51 instanceof AgentOutputLimitError)
|
|
191424
191812
|
return error51;
|
|
191425
191813
|
const reason = abortController.signal.reason;
|
|
191426
191814
|
return reason instanceof AgentOutputLimitError ? reason : undefined;
|
|
191427
191815
|
}
|
|
191816
|
+
function cancellationFromSignal(signal) {
|
|
191817
|
+
if (signal?.reason instanceof KyosoCancellationError)
|
|
191818
|
+
return signal.reason;
|
|
191819
|
+
return new KyosoCancellationError(typeof signal?.reason === "string" ? signal.reason : undefined);
|
|
191820
|
+
}
|
|
191428
191821
|
function resolveEffectiveTimeoutMs(input) {
|
|
191429
191822
|
const deadlineRemaining = input.deadlineAtEpochMs === undefined ? Number.POSITIVE_INFINITY : input.deadlineAtEpochMs - Date.now();
|
|
191430
191823
|
return Math.max(0, Math.min(input.timeoutMs, deadlineRemaining));
|
|
@@ -191433,7 +191826,7 @@ function normalizeUsage(usage) {
|
|
|
191433
191826
|
return normalizeModelTokenUsage(usage);
|
|
191434
191827
|
}
|
|
191435
191828
|
function withReportedExecutionIdentity(identity, metadata) {
|
|
191436
|
-
const record2 =
|
|
191829
|
+
const record2 = isRecord10(metadata) ? metadata : {};
|
|
191437
191830
|
return createModelExecutionIdentity({
|
|
191438
191831
|
providerRoute: identity.providerRoute,
|
|
191439
191832
|
requestedModel: identity.requestedModel,
|
|
@@ -191441,7 +191834,26 @@ function withReportedExecutionIdentity(identity, metadata) {
|
|
|
191441
191834
|
reportedModel: record2.model
|
|
191442
191835
|
});
|
|
191443
191836
|
}
|
|
191444
|
-
function
|
|
191837
|
+
function readChunkMeta(update) {
|
|
191838
|
+
const record2 = isRecord10(update) ? update : {};
|
|
191839
|
+
const metadata = isRecord10(record2._meta) ? record2._meta : {};
|
|
191840
|
+
const codex = isRecord10(metadata.codex) ? metadata.codex : {};
|
|
191841
|
+
const phase = codex.phase === "commentary" || codex.phase === "final_answer" ? codex.phase : "unknown";
|
|
191842
|
+
return {
|
|
191843
|
+
...typeof record2.messageId === "string" ? { messageId: record2.messageId } : {},
|
|
191844
|
+
phase
|
|
191845
|
+
};
|
|
191846
|
+
}
|
|
191847
|
+
function readCodexThreadStatus(update) {
|
|
191848
|
+
const record2 = isRecord10(update) ? update : {};
|
|
191849
|
+
if (record2.sessionUpdate !== "session_info_update")
|
|
191850
|
+
return;
|
|
191851
|
+
const metadata = isRecord10(record2._meta) ? record2._meta : {};
|
|
191852
|
+
const codex = isRecord10(metadata.codex) ? metadata.codex : {};
|
|
191853
|
+
const threadStatus = isRecord10(codex.threadStatus) ? codex.threadStatus : {};
|
|
191854
|
+
return typeof threadStatus.type === "string" ? threadStatus.type : undefined;
|
|
191855
|
+
}
|
|
191856
|
+
function isRecord10(value) {
|
|
191445
191857
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
191446
191858
|
}
|
|
191447
191859
|
function resolveEffortConfigOption(agent, effort) {
|
|
@@ -191507,14 +191919,40 @@ function assertWithinWorkspace(workspaceRoot, absolute) {
|
|
|
191507
191919
|
}
|
|
191508
191920
|
}
|
|
191509
191921
|
function terminateChild(child) {
|
|
191510
|
-
if (child.exitCode !== null || child.signalCode !== null)
|
|
191511
|
-
return;
|
|
191512
|
-
|
|
191513
|
-
|
|
191514
|
-
|
|
191922
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
191923
|
+
return Promise.resolve();
|
|
191924
|
+
}
|
|
191925
|
+
return new Promise((resolve4) => {
|
|
191926
|
+
let settled = false;
|
|
191927
|
+
let killTimer;
|
|
191928
|
+
let escalationTimer;
|
|
191929
|
+
let onClose;
|
|
191930
|
+
const settle = () => {
|
|
191931
|
+
if (settled)
|
|
191932
|
+
return;
|
|
191933
|
+
settled = true;
|
|
191934
|
+
if (killTimer)
|
|
191935
|
+
clearTimeout(killTimer);
|
|
191936
|
+
if (escalationTimer)
|
|
191937
|
+
clearTimeout(escalationTimer);
|
|
191938
|
+
if (onClose)
|
|
191939
|
+
child.off("close", onClose);
|
|
191940
|
+
resolve4();
|
|
191941
|
+
};
|
|
191942
|
+
onClose = () => settle();
|
|
191943
|
+
child.once("close", onClose);
|
|
191944
|
+
child.kill("SIGTERM");
|
|
191945
|
+
killTimer = setTimeout(() => {
|
|
191946
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
191947
|
+
settle();
|
|
191948
|
+
return;
|
|
191949
|
+
}
|
|
191515
191950
|
child.kill("SIGKILL");
|
|
191516
|
-
|
|
191517
|
-
|
|
191951
|
+
escalationTimer = setTimeout(settle, 500);
|
|
191952
|
+
escalationTimer.unref();
|
|
191953
|
+
}, 2000);
|
|
191954
|
+
killTimer.unref();
|
|
191955
|
+
});
|
|
191518
191956
|
}
|
|
191519
191957
|
function isMissingPathError2(error51) {
|
|
191520
191958
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
@@ -191599,6 +192037,7 @@ class FakeAgentManager extends BaseAcpAgentManager {
|
|
|
191599
192037
|
this.verifierScenarios = verifierScenarios;
|
|
191600
192038
|
}
|
|
191601
192039
|
async runAgent(input) {
|
|
192040
|
+
throwIfAborted(input.signal);
|
|
191602
192041
|
this.calls.push(input);
|
|
191603
192042
|
const startedAt = new Date().toISOString();
|
|
191604
192043
|
if (input.role === "finding_verifier") {
|
|
@@ -192294,18 +192733,6 @@ import { basename, dirname as dirname4, isAbsolute as isAbsolute4, join as join3
|
|
|
192294
192733
|
|
|
192295
192734
|
// src/context/pathPolicy.ts
|
|
192296
192735
|
import { normalize, sep } from "node:path";
|
|
192297
|
-
|
|
192298
|
-
// src/core/errors.ts
|
|
192299
|
-
class KyosoRequestError extends Error {
|
|
192300
|
-
code;
|
|
192301
|
-
constructor(message, code) {
|
|
192302
|
-
super(message);
|
|
192303
|
-
this.code = code;
|
|
192304
|
-
this.name = "KyosoRequestError";
|
|
192305
|
-
}
|
|
192306
|
-
}
|
|
192307
|
-
|
|
192308
|
-
// src/context/pathPolicy.ts
|
|
192309
192736
|
function normalizeRelativePath(path) {
|
|
192310
192737
|
const normalized = normalize(path).replaceAll("\\", "/");
|
|
192311
192738
|
if (normalized === "." || normalized.startsWith("..") || normalized.includes(`${sep}..${sep}`) || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized)) {
|
|
@@ -192961,6 +193388,161 @@ function buildContext(request, options) {
|
|
|
192961
193388
|
}
|
|
192962
193389
|
}
|
|
192963
193390
|
|
|
193391
|
+
// src/core/progressDispatcher.ts
|
|
193392
|
+
var DEFAULT_SINK_TIMEOUT_MS = 2000;
|
|
193393
|
+
var DEFAULT_MAX_QUEUE = 128;
|
|
193394
|
+
function isTransientEvent(event) {
|
|
193395
|
+
return event.type === "agent_activity" || event.type === "agent_waiting";
|
|
193396
|
+
}
|
|
193397
|
+
function isSameCoalescibleEvent(previous, next) {
|
|
193398
|
+
if (!previous || !isTransientEvent(previous) || !isTransientEvent(next)) {
|
|
193399
|
+
return false;
|
|
193400
|
+
}
|
|
193401
|
+
return previous.type === next.type && previous.agent === next.agent;
|
|
193402
|
+
}
|
|
193403
|
+
function withoutThrowing(callback) {
|
|
193404
|
+
try {
|
|
193405
|
+
callback?.();
|
|
193406
|
+
} catch {}
|
|
193407
|
+
}
|
|
193408
|
+
function createProgressDispatcher(sink, options = {}) {
|
|
193409
|
+
if (!sink) {
|
|
193410
|
+
return {
|
|
193411
|
+
emit: () => {
|
|
193412
|
+
return;
|
|
193413
|
+
},
|
|
193414
|
+
flush: async () => {
|
|
193415
|
+
return;
|
|
193416
|
+
}
|
|
193417
|
+
};
|
|
193418
|
+
}
|
|
193419
|
+
const sinkTimeoutMs = options.sinkTimeoutMs ?? DEFAULT_SINK_TIMEOUT_MS;
|
|
193420
|
+
const maxQueue = options.maxQueue ?? DEFAULT_MAX_QUEUE;
|
|
193421
|
+
const queue = [];
|
|
193422
|
+
const idleResolvers = new Set;
|
|
193423
|
+
let disabled = false;
|
|
193424
|
+
let processing = false;
|
|
193425
|
+
const notifyIdle = () => {
|
|
193426
|
+
if (processing || queue.length > 0)
|
|
193427
|
+
return;
|
|
193428
|
+
for (const resolve6 of idleResolvers)
|
|
193429
|
+
resolve6();
|
|
193430
|
+
idleResolvers.clear();
|
|
193431
|
+
};
|
|
193432
|
+
const waitForIdle = () => {
|
|
193433
|
+
if (!processing && queue.length === 0)
|
|
193434
|
+
return Promise.resolve();
|
|
193435
|
+
return new Promise((resolve6) => idleResolvers.add(resolve6));
|
|
193436
|
+
};
|
|
193437
|
+
const disable = (reason) => {
|
|
193438
|
+
if (disabled)
|
|
193439
|
+
return;
|
|
193440
|
+
disabled = true;
|
|
193441
|
+
queue.length = 0;
|
|
193442
|
+
withoutThrowing(() => options.onSinkDisabled?.(reason));
|
|
193443
|
+
notifyIdle();
|
|
193444
|
+
};
|
|
193445
|
+
const deliver = async (event) => {
|
|
193446
|
+
let timeout;
|
|
193447
|
+
try {
|
|
193448
|
+
const delivery = Promise.resolve().then(() => sink(event));
|
|
193449
|
+
const timeoutResult = new Promise((resolve6) => {
|
|
193450
|
+
timeout = setTimeout(() => resolve6("timeout"), sinkTimeoutMs);
|
|
193451
|
+
timeout.unref?.();
|
|
193452
|
+
});
|
|
193453
|
+
const result = await Promise.race([
|
|
193454
|
+
delivery.then(() => "delivered"),
|
|
193455
|
+
timeoutResult
|
|
193456
|
+
]);
|
|
193457
|
+
if (result === "timeout") {
|
|
193458
|
+
disable(`Progress sink timed out after ${sinkTimeoutMs}ms.`);
|
|
193459
|
+
return false;
|
|
193460
|
+
}
|
|
193461
|
+
return true;
|
|
193462
|
+
} catch {
|
|
193463
|
+
disable("Progress sink threw while handling an event.");
|
|
193464
|
+
return false;
|
|
193465
|
+
} finally {
|
|
193466
|
+
if (timeout)
|
|
193467
|
+
clearTimeout(timeout);
|
|
193468
|
+
}
|
|
193469
|
+
};
|
|
193470
|
+
const drain = async () => {
|
|
193471
|
+
try {
|
|
193472
|
+
while (!disabled && queue.length > 0) {
|
|
193473
|
+
const event = queue.shift();
|
|
193474
|
+
if (!event)
|
|
193475
|
+
continue;
|
|
193476
|
+
if (!await deliver(event))
|
|
193477
|
+
break;
|
|
193478
|
+
}
|
|
193479
|
+
} finally {
|
|
193480
|
+
processing = false;
|
|
193481
|
+
if (!disabled && queue.length > 0) {
|
|
193482
|
+
startDrain();
|
|
193483
|
+
} else {
|
|
193484
|
+
notifyIdle();
|
|
193485
|
+
}
|
|
193486
|
+
}
|
|
193487
|
+
};
|
|
193488
|
+
const startDrain = () => {
|
|
193489
|
+
if (processing || disabled || queue.length === 0)
|
|
193490
|
+
return;
|
|
193491
|
+
processing = true;
|
|
193492
|
+
drain();
|
|
193493
|
+
};
|
|
193494
|
+
const makeRoomForMilestone = () => {
|
|
193495
|
+
const transientIndex = queue.findIndex(isTransientEvent);
|
|
193496
|
+
if (transientIndex >= 0) {
|
|
193497
|
+
queue.splice(transientIndex, 1);
|
|
193498
|
+
return true;
|
|
193499
|
+
}
|
|
193500
|
+
if (queue.length < maxQueue)
|
|
193501
|
+
return true;
|
|
193502
|
+
queue.shift();
|
|
193503
|
+
return true;
|
|
193504
|
+
};
|
|
193505
|
+
return {
|
|
193506
|
+
emit(event) {
|
|
193507
|
+
try {
|
|
193508
|
+
if (disabled)
|
|
193509
|
+
return;
|
|
193510
|
+
const previous = queue.at(-1);
|
|
193511
|
+
if (isSameCoalescibleEvent(previous, event)) {
|
|
193512
|
+
queue[queue.length - 1] = event;
|
|
193513
|
+
} else if (queue.length < maxQueue) {
|
|
193514
|
+
queue.push(event);
|
|
193515
|
+
} else if (isTransientEvent(event)) {
|
|
193516
|
+
return;
|
|
193517
|
+
} else if (makeRoomForMilestone()) {
|
|
193518
|
+
queue.push(event);
|
|
193519
|
+
}
|
|
193520
|
+
startDrain();
|
|
193521
|
+
} catch {}
|
|
193522
|
+
},
|
|
193523
|
+
async flush(maxWaitMs = DEFAULT_SINK_TIMEOUT_MS) {
|
|
193524
|
+
try {
|
|
193525
|
+
const idle = waitForIdle();
|
|
193526
|
+
if (maxWaitMs <= 0)
|
|
193527
|
+
return;
|
|
193528
|
+
let timeout;
|
|
193529
|
+
try {
|
|
193530
|
+
await Promise.race([
|
|
193531
|
+
idle,
|
|
193532
|
+
new Promise((resolve6) => {
|
|
193533
|
+
timeout = setTimeout(resolve6, maxWaitMs);
|
|
193534
|
+
timeout.unref?.();
|
|
193535
|
+
})
|
|
193536
|
+
]);
|
|
193537
|
+
} finally {
|
|
193538
|
+
if (timeout)
|
|
193539
|
+
clearTimeout(timeout);
|
|
193540
|
+
}
|
|
193541
|
+
} catch {}
|
|
193542
|
+
}
|
|
193543
|
+
};
|
|
193544
|
+
}
|
|
193545
|
+
|
|
192964
193546
|
// src/core/validateRequest.ts
|
|
192965
193547
|
function validateReviewRequest(tool, request) {
|
|
192966
193548
|
if (typeof request.goal !== "string" || request.goal.trim().length === 0) {
|
|
@@ -192976,7 +193558,7 @@ function validateReviewContract(request) {
|
|
|
192976
193558
|
const contract = request.reviewContract;
|
|
192977
193559
|
if (contract === undefined)
|
|
192978
193560
|
return;
|
|
192979
|
-
if (!
|
|
193561
|
+
if (!isRecord11(contract)) {
|
|
192980
193562
|
throw new KyosoRequestError("reviewContract must be an object", "VALIDATION_ERROR");
|
|
192981
193563
|
}
|
|
192982
193564
|
const allowedKeys = new Set(["focus", "nonGoals", "acceptedRisks"]);
|
|
@@ -192993,7 +193575,7 @@ function validateReviewContract(request) {
|
|
|
192993
193575
|
throw new KyosoRequestError("reviewContract.nonGoals must contain at most 20 non-empty strings of 500 characters or fewer", "VALIDATION_ERROR");
|
|
192994
193576
|
}
|
|
192995
193577
|
const acceptedRisks = contract.acceptedRisks;
|
|
192996
|
-
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !
|
|
193578
|
+
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !isRecord11(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))) {
|
|
192997
193579
|
throw new KyosoRequestError("reviewContract.acceptedRisks must contain valid finding fingerprints and bounded rationales", "VALIDATION_ERROR");
|
|
192998
193580
|
}
|
|
192999
193581
|
}
|
|
@@ -193005,13 +193587,13 @@ function validateSelectedFiles(request) {
|
|
|
193005
193587
|
throw new KyosoRequestError("selectedFiles must be an array", "VALIDATION_ERROR");
|
|
193006
193588
|
}
|
|
193007
193589
|
for (const file2 of selectedFiles) {
|
|
193008
|
-
if (!
|
|
193590
|
+
if (!isRecord11(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") {
|
|
193009
193591
|
throw new KyosoRequestError("selectedFiles entries require string path/content and valid optional metadata", "VALIDATION_ERROR");
|
|
193010
193592
|
}
|
|
193011
193593
|
normalizeRelativePath(file2.path);
|
|
193012
193594
|
}
|
|
193013
193595
|
}
|
|
193014
|
-
function
|
|
193596
|
+
function isRecord11(value) {
|
|
193015
193597
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193016
193598
|
}
|
|
193017
193599
|
|
|
@@ -193272,7 +193854,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
|
|
|
193272
193854
|
const parsed = JSON.parse(json2);
|
|
193273
193855
|
const summaryText = typeof parsed.summaryText === "string" && parsed.summaryText.trim().length > 0 ? sanitizeText(parsed.summaryText) : fallbackSummaryText;
|
|
193274
193856
|
const disagreementComments = Array.isArray(parsed.disagreementComments) ? parsed.disagreementComments.flatMap((item) => {
|
|
193275
|
-
if (!
|
|
193857
|
+
if (!isRecord12(item) || typeof item.topic !== "string" || typeof item.judgeComment !== "string") {
|
|
193276
193858
|
return [];
|
|
193277
193859
|
}
|
|
193278
193860
|
return [
|
|
@@ -193288,7 +193870,7 @@ function parseJudgeOutput(text, fallbackSummaryText) {
|
|
|
193288
193870
|
return { summaryText, disagreementComments, analysis };
|
|
193289
193871
|
}
|
|
193290
193872
|
function parseAnalysis(value) {
|
|
193291
|
-
if (!
|
|
193873
|
+
if (!isRecord12(value))
|
|
193292
193874
|
return;
|
|
193293
193875
|
if (!Array.isArray(value.blindSpots) || !Array.isArray(value.contradictions) || !Array.isArray(value.partialCoverage)) {
|
|
193294
193876
|
return;
|
|
@@ -193296,7 +193878,7 @@ function parseAnalysis(value) {
|
|
|
193296
193878
|
return {
|
|
193297
193879
|
blindSpots: value.blindSpots.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => typeof item === "string" ? [sanitizeAnalysisText(item)] : []),
|
|
193298
193880
|
contradictions: value.contradictions.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
|
|
193299
|
-
if (!
|
|
193881
|
+
if (!isRecord12(item) || typeof item.topic !== "string" || typeof item.detail !== "string") {
|
|
193300
193882
|
return [];
|
|
193301
193883
|
}
|
|
193302
193884
|
return [
|
|
@@ -193307,7 +193889,7 @@ function parseAnalysis(value) {
|
|
|
193307
193889
|
];
|
|
193308
193890
|
}),
|
|
193309
193891
|
partialCoverage: value.partialCoverage.slice(0, ANALYSIS_MAX_ITEMS).flatMap((item) => {
|
|
193310
|
-
if (!
|
|
193892
|
+
if (!isRecord12(item) || typeof item.note !== "string")
|
|
193311
193893
|
return [];
|
|
193312
193894
|
const findingId = typeof item.findingId === "string" ? sanitizeAnalysisText(item.findingId) : undefined;
|
|
193313
193895
|
return [
|
|
@@ -193354,10 +193936,31 @@ function extractFirstJsonObject2(text) {
|
|
|
193354
193936
|
}
|
|
193355
193937
|
return;
|
|
193356
193938
|
}
|
|
193357
|
-
function
|
|
193939
|
+
function isRecord12(value) {
|
|
193358
193940
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193359
193941
|
}
|
|
193360
193942
|
|
|
193943
|
+
// src/judge/signals.ts
|
|
193944
|
+
function linkSignals(timeoutMs, external3) {
|
|
193945
|
+
const controller = new AbortController;
|
|
193946
|
+
const timeout = setTimeout(() => controller.abort(new Error("judge timeout")), timeoutMs);
|
|
193947
|
+
timeout.unref?.();
|
|
193948
|
+
const onExternalAbort = () => controller.abort(external3?.reason);
|
|
193949
|
+
if (external3) {
|
|
193950
|
+
if (external3.aborted)
|
|
193951
|
+
onExternalAbort();
|
|
193952
|
+
else
|
|
193953
|
+
external3.addEventListener("abort", onExternalAbort, { once: true });
|
|
193954
|
+
}
|
|
193955
|
+
return {
|
|
193956
|
+
signal: controller.signal,
|
|
193957
|
+
cleanup() {
|
|
193958
|
+
clearTimeout(timeout);
|
|
193959
|
+
external3?.removeEventListener("abort", onExternalAbort);
|
|
193960
|
+
}
|
|
193961
|
+
};
|
|
193962
|
+
}
|
|
193963
|
+
|
|
193361
193964
|
// src/judge/anthropic.ts
|
|
193362
193965
|
var DEFAULT_ANTHROPIC_JUDGE_MODEL = "claude-haiku-4-5";
|
|
193363
193966
|
function resolveAnthropicJudgeModel(env) {
|
|
@@ -193368,39 +193971,45 @@ async function runAnthropicJudge(input, timeoutMs) {
|
|
|
193368
193971
|
if (!apiKey)
|
|
193369
193972
|
throw new Error("ANTHROPIC_API_KEY is not configured.");
|
|
193370
193973
|
const requestedModel = resolveAnthropicJudgeModel(input.env);
|
|
193371
|
-
const
|
|
193372
|
-
|
|
193373
|
-
|
|
193374
|
-
"
|
|
193375
|
-
|
|
193376
|
-
|
|
193377
|
-
|
|
193378
|
-
|
|
193379
|
-
|
|
193380
|
-
|
|
193381
|
-
|
|
193382
|
-
|
|
193383
|
-
|
|
193384
|
-
|
|
193385
|
-
|
|
193386
|
-
|
|
193387
|
-
|
|
193388
|
-
|
|
193389
|
-
|
|
193390
|
-
|
|
193391
|
-
|
|
193392
|
-
|
|
193393
|
-
|
|
193394
|
-
|
|
193395
|
-
|
|
193396
|
-
|
|
193397
|
-
|
|
193398
|
-
|
|
193399
|
-
|
|
193400
|
-
|
|
193401
|
-
|
|
193402
|
-
|
|
193403
|
-
|
|
193974
|
+
const { signal, cleanup } = linkSignals(timeoutMs, input.signal);
|
|
193975
|
+
try {
|
|
193976
|
+
const response = await fetch(`${input.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com"}/v1/messages`, {
|
|
193977
|
+
method: "POST",
|
|
193978
|
+
headers: {
|
|
193979
|
+
"x-api-key": apiKey,
|
|
193980
|
+
"anthropic-version": "2023-06-01",
|
|
193981
|
+
"content-type": "application/json"
|
|
193982
|
+
},
|
|
193983
|
+
body: JSON.stringify({
|
|
193984
|
+
model: requestedModel,
|
|
193985
|
+
max_tokens: JUDGE_MAX_OUTPUT_TOKENS,
|
|
193986
|
+
temperature: 0,
|
|
193987
|
+
messages: [
|
|
193988
|
+
{
|
|
193989
|
+
role: "user",
|
|
193990
|
+
content: buildJudgePrompt(input.tool, input.result, input.summaryText, input.agentFindings)
|
|
193991
|
+
}
|
|
193992
|
+
]
|
|
193993
|
+
}),
|
|
193994
|
+
signal
|
|
193995
|
+
});
|
|
193996
|
+
if (!response.ok)
|
|
193997
|
+
throw new Error(`Anthropic judge failed with HTTP ${response.status}.`);
|
|
193998
|
+
const payload = await response.json();
|
|
193999
|
+
const content = payload.content?.find((item) => item.type === "text" && item.text)?.text;
|
|
194000
|
+
if (!content)
|
|
194001
|
+
throw new Error("Anthropic judge response did not include text content.");
|
|
194002
|
+
const usage = normalizeUsage2(payload.usage);
|
|
194003
|
+
const reportedModel = typeof payload.model === "string" ? payload.model : undefined;
|
|
194004
|
+
return {
|
|
194005
|
+
output: parseJudgeOutput(content, input.summaryText),
|
|
194006
|
+
requestedModel,
|
|
194007
|
+
...reportedModel ? { reportedModel } : {},
|
|
194008
|
+
...usage ? { usage } : {}
|
|
194009
|
+
};
|
|
194010
|
+
} finally {
|
|
194011
|
+
cleanup();
|
|
194012
|
+
}
|
|
193404
194013
|
}
|
|
193405
194014
|
function normalizeUsage2(usage) {
|
|
193406
194015
|
if (!usage)
|
|
@@ -193412,16 +194021,6 @@ function normalizeUsage2(usage) {
|
|
|
193412
194021
|
cachedWriteTokens: usage.cache_creation_input_tokens
|
|
193413
194022
|
});
|
|
193414
194023
|
}
|
|
193415
|
-
async function fetchWithTimeout(url2, init, timeoutMs) {
|
|
193416
|
-
const controller = new AbortController;
|
|
193417
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
193418
|
-
timeout.unref?.();
|
|
193419
|
-
try {
|
|
193420
|
-
return await fetch(url2, { ...init, signal: controller.signal });
|
|
193421
|
-
} finally {
|
|
193422
|
-
clearTimeout(timeout);
|
|
193423
|
-
}
|
|
193424
|
-
}
|
|
193425
194024
|
|
|
193426
194025
|
// src/judge/deterministicFallback.ts
|
|
193427
194026
|
function runDeterministicJudge(result, summaryText) {
|
|
@@ -193444,39 +194043,45 @@ async function runOpenAiJudge(input, timeoutMs) {
|
|
|
193444
194043
|
if (!apiKey)
|
|
193445
194044
|
throw new Error("OPENAI_API_KEY is not configured.");
|
|
193446
194045
|
const requestedModel = resolveOpenAiJudgeModel(input.env);
|
|
193447
|
-
const
|
|
193448
|
-
|
|
193449
|
-
|
|
193450
|
-
|
|
193451
|
-
|
|
193452
|
-
|
|
193453
|
-
|
|
193454
|
-
|
|
193455
|
-
|
|
193456
|
-
|
|
193457
|
-
{
|
|
193458
|
-
|
|
193459
|
-
|
|
193460
|
-
|
|
193461
|
-
|
|
193462
|
-
|
|
193463
|
-
|
|
193464
|
-
|
|
193465
|
-
|
|
193466
|
-
|
|
193467
|
-
|
|
193468
|
-
|
|
193469
|
-
|
|
193470
|
-
|
|
193471
|
-
|
|
193472
|
-
|
|
193473
|
-
|
|
193474
|
-
|
|
193475
|
-
|
|
193476
|
-
|
|
193477
|
-
|
|
193478
|
-
|
|
193479
|
-
|
|
194046
|
+
const { signal, cleanup } = linkSignals(timeoutMs, input.signal);
|
|
194047
|
+
try {
|
|
194048
|
+
const response = await fetch(`${input.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1"}/chat/completions`, {
|
|
194049
|
+
method: "POST",
|
|
194050
|
+
headers: {
|
|
194051
|
+
authorization: `Bearer ${apiKey}`,
|
|
194052
|
+
"content-type": "application/json"
|
|
194053
|
+
},
|
|
194054
|
+
body: JSON.stringify({
|
|
194055
|
+
model: requestedModel,
|
|
194056
|
+
response_format: { type: "json_object" },
|
|
194057
|
+
messages: [
|
|
194058
|
+
{
|
|
194059
|
+
role: "user",
|
|
194060
|
+
content: buildJudgePrompt(input.tool, input.result, input.summaryText, input.agentFindings)
|
|
194061
|
+
}
|
|
194062
|
+
],
|
|
194063
|
+
max_completion_tokens: JUDGE_MAX_OUTPUT_TOKENS,
|
|
194064
|
+
temperature: 0
|
|
194065
|
+
}),
|
|
194066
|
+
signal
|
|
194067
|
+
});
|
|
194068
|
+
if (!response.ok)
|
|
194069
|
+
throw new Error(`OpenAI judge failed with HTTP ${response.status}.`);
|
|
194070
|
+
const payload = await response.json();
|
|
194071
|
+
const content = payload.choices?.[0]?.message?.content;
|
|
194072
|
+
if (!content)
|
|
194073
|
+
throw new Error("OpenAI judge response did not include content.");
|
|
194074
|
+
const usage = normalizeUsage3(payload.usage);
|
|
194075
|
+
const reportedModel = typeof payload.model === "string" ? payload.model : undefined;
|
|
194076
|
+
return {
|
|
194077
|
+
output: parseJudgeOutput(content, input.summaryText),
|
|
194078
|
+
requestedModel,
|
|
194079
|
+
...reportedModel ? { reportedModel } : {},
|
|
194080
|
+
...usage ? { usage } : {}
|
|
194081
|
+
};
|
|
194082
|
+
} finally {
|
|
194083
|
+
cleanup();
|
|
194084
|
+
}
|
|
193480
194085
|
}
|
|
193481
194086
|
function normalizeUsage3(usage) {
|
|
193482
194087
|
if (!usage)
|
|
@@ -193489,16 +194094,6 @@ function normalizeUsage3(usage) {
|
|
|
193489
194094
|
thoughtTokens: usage.completion_tokens_details?.reasoning_tokens
|
|
193490
194095
|
});
|
|
193491
194096
|
}
|
|
193492
|
-
async function fetchWithTimeout2(url2, init, timeoutMs) {
|
|
193493
|
-
const controller = new AbortController;
|
|
193494
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
193495
|
-
timeout.unref?.();
|
|
193496
|
-
try {
|
|
193497
|
-
return await fetch(url2, { ...init, signal: controller.signal });
|
|
193498
|
-
} finally {
|
|
193499
|
-
clearTimeout(timeout);
|
|
193500
|
-
}
|
|
193501
|
-
}
|
|
193502
194097
|
|
|
193503
194098
|
// src/judge/provider.ts
|
|
193504
194099
|
function resolveJudgeProvider(provider, env) {
|
|
@@ -193552,6 +194147,9 @@ async function runJudge(input) {
|
|
|
193552
194147
|
...output.usage ? { usage: output.usage } : {}
|
|
193553
194148
|
};
|
|
193554
194149
|
} catch (error51) {
|
|
194150
|
+
if (input.signal?.aborted) {
|
|
194151
|
+
throw new KyosoCancellationError("Kyoso review was cancelled during judge execution.");
|
|
194152
|
+
}
|
|
193555
194153
|
return {
|
|
193556
194154
|
provider,
|
|
193557
194155
|
status: "failed_fallback",
|
|
@@ -193883,11 +194481,11 @@ function canonicalJson(value) {
|
|
|
193883
194481
|
function canonicalize(value) {
|
|
193884
194482
|
if (Array.isArray(value))
|
|
193885
194483
|
return value.map(canonicalize);
|
|
193886
|
-
if (!
|
|
194484
|
+
if (!isRecord13(value))
|
|
193887
194485
|
return value;
|
|
193888
194486
|
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)]));
|
|
193889
194487
|
}
|
|
193890
|
-
function
|
|
194488
|
+
function isRecord13(value) {
|
|
193891
194489
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193892
194490
|
}
|
|
193893
194491
|
|
|
@@ -193901,7 +194499,7 @@ var REVIEW_BUDGET_KEYS = new Set([
|
|
|
193901
194499
|
]);
|
|
193902
194500
|
var MODEL_CALL_KINDS = ["primary", "verifier", "judge"];
|
|
193903
194501
|
function resolveReviewBudget(ceiling, requested) {
|
|
193904
|
-
if (requested !== undefined && !
|
|
194502
|
+
if (requested !== undefined && !isRecord14(requested)) {
|
|
193905
194503
|
throw new KyosoRequestError("options.reviewBudget must be an object.", "REVIEW_BUDGET_INVALID");
|
|
193906
194504
|
}
|
|
193907
194505
|
for (const [key, value] of Object.entries(requested ?? {})) {
|
|
@@ -194091,6 +194689,10 @@ class ReviewBudgetTracker {
|
|
|
194091
194689
|
current.thoughtBytes = values.thoughtBytes;
|
|
194092
194690
|
current.outputBytes = values.outputBytes;
|
|
194093
194691
|
current.outputWarningTriggered = values.outputWarningTriggered;
|
|
194692
|
+
current.observedStreamRetries = values.observedStreamRetries;
|
|
194693
|
+
current.discardedRetryMessageBytes = values.discardedRetryMessageBytes;
|
|
194694
|
+
current.firstOutputAt = values.firstOutputAt;
|
|
194695
|
+
current.lastAcpUpdateAt = values.lastAcpUpdateAt;
|
|
194094
194696
|
current.salvaged = values.salvaged;
|
|
194095
194697
|
current.reportedFindings = values.reportedFindings;
|
|
194096
194698
|
current.findingsTargetExceeded = values.findingsTargetExceeded;
|
|
@@ -194205,6 +194807,12 @@ class ReviewBudgetTracker {
|
|
|
194205
194807
|
...reservation.thoughtBytes !== undefined ? { thoughtBytes: reservation.thoughtBytes } : {},
|
|
194206
194808
|
...reservation.outputBytes !== undefined ? { outputBytes: reservation.outputBytes } : {},
|
|
194207
194809
|
...reservation.outputWarningTriggered !== undefined ? { outputWarningTriggered: reservation.outputWarningTriggered } : {},
|
|
194810
|
+
...reservation.observedStreamRetries !== undefined ? { observedStreamRetries: reservation.observedStreamRetries } : {},
|
|
194811
|
+
...reservation.discardedRetryMessageBytes !== undefined ? {
|
|
194812
|
+
discardedRetryMessageBytes: reservation.discardedRetryMessageBytes
|
|
194813
|
+
} : {},
|
|
194814
|
+
...reservation.firstOutputAt !== undefined ? { firstOutputAt: reservation.firstOutputAt } : {},
|
|
194815
|
+
...reservation.lastAcpUpdateAt !== undefined ? { lastAcpUpdateAt: reservation.lastAcpUpdateAt } : {},
|
|
194208
194816
|
...reservation.salvaged !== undefined ? { salvaged: reservation.salvaged } : {},
|
|
194209
194817
|
...reservation.reportedFindings !== undefined ? { reportedFindings: reservation.reportedFindings } : {},
|
|
194210
194818
|
...reservation.findingsTargetExceeded !== undefined ? { findingsTargetExceeded: reservation.findingsTargetExceeded } : {},
|
|
@@ -194245,7 +194853,7 @@ function emptyReviewModelCallPlan() {
|
|
|
194245
194853
|
ceilingEffects: []
|
|
194246
194854
|
};
|
|
194247
194855
|
}
|
|
194248
|
-
function
|
|
194856
|
+
function isRecord14(value) {
|
|
194249
194857
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
194250
194858
|
}
|
|
194251
194859
|
|
|
@@ -194307,7 +194915,7 @@ function parseVerificationVerdicts(rawText) {
|
|
|
194307
194915
|
if (!Array.isArray(parsed.verdicts))
|
|
194308
194916
|
return;
|
|
194309
194917
|
return parsed.verdicts.flatMap((item) => {
|
|
194310
|
-
if (!
|
|
194918
|
+
if (!isRecord15(item))
|
|
194311
194919
|
return [];
|
|
194312
194920
|
if (typeof item.findingId !== "string")
|
|
194313
194921
|
return [];
|
|
@@ -194385,11 +194993,12 @@ function verificationNote(reasoning) {
|
|
|
194385
194993
|
function isVerdict(value) {
|
|
194386
194994
|
return value === "confirmed" || value === "refuted" || value === "uncertain";
|
|
194387
194995
|
}
|
|
194388
|
-
function
|
|
194996
|
+
function isRecord15(value) {
|
|
194389
194997
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
194390
194998
|
}
|
|
194391
194999
|
|
|
194392
195000
|
// src/core/runReview.ts
|
|
195001
|
+
var MAX_AGENT_RETRY_PROGRESS_EVENTS = 100;
|
|
194393
195002
|
function requestForRecursionFingerprint(request) {
|
|
194394
195003
|
try {
|
|
194395
195004
|
return scanAndRedactSecrets(request).redactedRequest;
|
|
@@ -194405,138 +195014,368 @@ async function runReview(tool, request, options = {}) {
|
|
|
194405
195014
|
const auditEnv = { ...process.env, ...options.env };
|
|
194406
195015
|
const traceWriterFactory = options.traceWriterFactory ?? createTraceWriter;
|
|
194407
195016
|
let snapshot;
|
|
195017
|
+
let activeTrace;
|
|
195018
|
+
let progressDeliveryFailure;
|
|
195019
|
+
let writeProgressDeliveryFailure;
|
|
195020
|
+
let reviewFailureReported = false;
|
|
195021
|
+
const dispatcher = createProgressDispatcher(options.onProgress, {
|
|
195022
|
+
onSinkDisabled: (reason) => {
|
|
195023
|
+
if (progressDeliveryFailure !== undefined)
|
|
195024
|
+
return;
|
|
195025
|
+
progressDeliveryFailure = reason;
|
|
195026
|
+
writeProgressDeliveryFailure?.(reason);
|
|
195027
|
+
}
|
|
195028
|
+
});
|
|
195029
|
+
const phaseStartedAt = new Map;
|
|
195030
|
+
const startPhase = (phase) => {
|
|
195031
|
+
throwIfAborted(options.signal);
|
|
195032
|
+
phaseStartedAt.set(phase, Date.now());
|
|
195033
|
+
dispatcher.emit({
|
|
195034
|
+
type: "phase_started",
|
|
195035
|
+
traceId,
|
|
195036
|
+
phase,
|
|
195037
|
+
timestamp: new Date().toISOString()
|
|
195038
|
+
});
|
|
195039
|
+
};
|
|
195040
|
+
const completePhase = (phase) => {
|
|
195041
|
+
dispatcher.emit({
|
|
195042
|
+
type: "phase_completed",
|
|
195043
|
+
traceId,
|
|
195044
|
+
phase,
|
|
195045
|
+
durationMs: Math.max(0, Date.now() - (phaseStartedAt.get(phase) ?? Date.now())),
|
|
195046
|
+
timestamp: new Date().toISOString()
|
|
195047
|
+
});
|
|
195048
|
+
};
|
|
195049
|
+
const skipPhase = (phase, reason) => {
|
|
195050
|
+
dispatcher.emit({
|
|
195051
|
+
type: "phase_skipped",
|
|
195052
|
+
traceId,
|
|
195053
|
+
phase,
|
|
195054
|
+
reason,
|
|
195055
|
+
timestamp: new Date().toISOString()
|
|
195056
|
+
});
|
|
195057
|
+
};
|
|
195058
|
+
const reportReviewCompleted = async (result) => {
|
|
195059
|
+
dispatcher.emit({
|
|
195060
|
+
type: "review_completed",
|
|
195061
|
+
traceId,
|
|
195062
|
+
decision: result.decision,
|
|
195063
|
+
completionStatus: result.completion.status,
|
|
195064
|
+
durationMs: Math.max(0, Date.now() - startedAtEpochMs),
|
|
195065
|
+
timestamp: new Date().toISOString()
|
|
195066
|
+
});
|
|
195067
|
+
await dispatcher.flush();
|
|
195068
|
+
if (progressDeliveryFailure !== undefined) {
|
|
195069
|
+
result.audit.warnings = Array.from(new Set([
|
|
195070
|
+
...result.audit.warnings ?? [],
|
|
195071
|
+
`PROGRESS_DELIVERY_FAILED: ${progressDeliveryFailure}`
|
|
195072
|
+
]));
|
|
195073
|
+
}
|
|
195074
|
+
};
|
|
195075
|
+
const completeReview = async (result) => {
|
|
195076
|
+
await reportReviewCompleted(result);
|
|
195077
|
+
return result;
|
|
195078
|
+
};
|
|
195079
|
+
const reportReviewFailure = async (error51, trace) => {
|
|
195080
|
+
if (reviewFailureReported)
|
|
195081
|
+
return;
|
|
195082
|
+
reviewFailureReported = true;
|
|
195083
|
+
const timestamp = new Date().toISOString();
|
|
195084
|
+
if (error51 instanceof KyosoCancellationError) {
|
|
195085
|
+
dispatcher.emit({ type: "review_cancelled", traceId, timestamp });
|
|
195086
|
+
await trace?.write({ type: "review_cancelled", traceId, timestamp }).catch(() => {
|
|
195087
|
+
return;
|
|
195088
|
+
});
|
|
195089
|
+
} else {
|
|
195090
|
+
dispatcher.emit({
|
|
195091
|
+
type: "review_failed",
|
|
195092
|
+
traceId,
|
|
195093
|
+
...error51 instanceof KyosoRequestError ? { errorCode: error51.code } : {},
|
|
195094
|
+
timestamp
|
|
195095
|
+
});
|
|
195096
|
+
await trace?.write({
|
|
195097
|
+
type: "review_failed",
|
|
195098
|
+
traceId,
|
|
195099
|
+
...error51 instanceof KyosoRequestError ? { errorCode: error51.code } : {},
|
|
195100
|
+
timestamp
|
|
195101
|
+
}).catch(() => {
|
|
195102
|
+
return;
|
|
195103
|
+
});
|
|
195104
|
+
}
|
|
195105
|
+
await dispatcher.flush();
|
|
195106
|
+
};
|
|
195107
|
+
dispatcher.emit({
|
|
195108
|
+
type: "review_started",
|
|
195109
|
+
traceId,
|
|
195110
|
+
tool,
|
|
195111
|
+
timestamp: new Date().toISOString()
|
|
195112
|
+
});
|
|
194408
195113
|
try {
|
|
194409
|
-
|
|
194410
|
-
|
|
194411
|
-
|
|
194412
|
-
|
|
194413
|
-
|
|
194414
|
-
|
|
194415
|
-
|
|
195114
|
+
try {
|
|
195115
|
+
throwIfAborted(options.signal);
|
|
195116
|
+
assertNotChildAgent(options.env ?? process.env);
|
|
195117
|
+
} catch (error51) {
|
|
195118
|
+
if (error51 instanceof KyosoRequestError) {
|
|
195119
|
+
const config2 = kyosoConfigSchema.parse(defaultConfig);
|
|
195120
|
+
const reviewBudget = resolveReviewBudget(config2.reviewBudget, undefined);
|
|
195121
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(config2, reviewBudget, options.env ?? process.env));
|
|
195122
|
+
const requestFingerprint = createRequestFingerprint({
|
|
195123
|
+
tool,
|
|
195124
|
+
request: requestForRecursionFingerprint(request),
|
|
195125
|
+
config: config2,
|
|
195126
|
+
roles: resolveAgentRoles(config2),
|
|
195127
|
+
budget: reviewBudget,
|
|
195128
|
+
entrypoint: options.entrypoint
|
|
195129
|
+
});
|
|
195130
|
+
const trace2 = traceWriterFactory({
|
|
195131
|
+
enabled: config2.audit.enabled,
|
|
195132
|
+
directory: config2.audit.directory,
|
|
195133
|
+
traceId,
|
|
195134
|
+
cwd,
|
|
195135
|
+
env: auditEnv
|
|
195136
|
+
});
|
|
195137
|
+
activeTrace = trace2;
|
|
195138
|
+
writeProgressDeliveryFailure = (reason) => {
|
|
195139
|
+
trace2.write({
|
|
195140
|
+
type: "progress_delivery_failed",
|
|
195141
|
+
traceId,
|
|
195142
|
+
reason,
|
|
195143
|
+
timestamp: new Date().toISOString()
|
|
195144
|
+
}).catch(() => {
|
|
195145
|
+
return;
|
|
195146
|
+
});
|
|
195147
|
+
};
|
|
195148
|
+
if (progressDeliveryFailure !== undefined) {
|
|
195149
|
+
writeProgressDeliveryFailure(progressDeliveryFailure);
|
|
195150
|
+
}
|
|
195151
|
+
try {
|
|
195152
|
+
await trace2.write({
|
|
195153
|
+
type: "request_received",
|
|
195154
|
+
traceId,
|
|
195155
|
+
tool,
|
|
195156
|
+
timestamp: new Date().toISOString()
|
|
195157
|
+
});
|
|
195158
|
+
await writeReviewBudgetPlanned({
|
|
195159
|
+
trace: trace2,
|
|
195160
|
+
traceId,
|
|
195161
|
+
budgetTracker,
|
|
195162
|
+
requestFingerprint
|
|
195163
|
+
});
|
|
195164
|
+
return await completeReview(await buildPolicyBlockResult({
|
|
195165
|
+
tool,
|
|
195166
|
+
trace: trace2,
|
|
195167
|
+
traceId,
|
|
195168
|
+
startedAt,
|
|
195169
|
+
networkMode: config2.network.defaultMode,
|
|
195170
|
+
cisaPolicy: config2.securityReview.cisaSecureByDesign,
|
|
195171
|
+
warning: error51.message,
|
|
195172
|
+
budgetTracker,
|
|
195173
|
+
requestFingerprint,
|
|
195174
|
+
coverage: unavailableReviewCoverage(requestForRecursionFingerprint(request), "recursive invocation blocked before agent execution", config2.reviewPolicy.additionalLenses),
|
|
195175
|
+
finding: {
|
|
195176
|
+
id: "KYOSO-1",
|
|
195177
|
+
severity: "critical",
|
|
195178
|
+
category: "other",
|
|
195179
|
+
title: "Recursive Kyoso invocation blocked",
|
|
195180
|
+
evidence: error51.message,
|
|
195181
|
+
recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
|
|
195182
|
+
disposition: "gate",
|
|
195183
|
+
changeRelation: "unknown",
|
|
195184
|
+
evidenceQuality: "concrete",
|
|
195185
|
+
evidenceRefs: [],
|
|
195186
|
+
policyReasons: ["kyoso_policy", "recursive_invocation"],
|
|
195187
|
+
fingerprint: "",
|
|
195188
|
+
sourceAgents: ["kyoso_policy"],
|
|
195189
|
+
confidence: "high"
|
|
195190
|
+
},
|
|
195191
|
+
redactionsApplied: 0
|
|
195192
|
+
}));
|
|
195193
|
+
} finally {
|
|
195194
|
+
await trace2.finalize();
|
|
195195
|
+
}
|
|
195196
|
+
}
|
|
195197
|
+
throw error51;
|
|
195198
|
+
}
|
|
195199
|
+
startPhase("preflight");
|
|
195200
|
+
const baseLoaded = options.config !== undefined ? {
|
|
195201
|
+
config: options.config,
|
|
195202
|
+
configHash: options.configHash,
|
|
195203
|
+
configTrustStatus: "trusted",
|
|
195204
|
+
sources: [],
|
|
195205
|
+
warnings: []
|
|
195206
|
+
} : await loadConfig({
|
|
195207
|
+
cwd,
|
|
195208
|
+
configPath: options.configPath,
|
|
195209
|
+
ignoreConfig: options.ignoreConfig,
|
|
195210
|
+
trustConfig: options.trustConfig,
|
|
195211
|
+
allowUnknownConfig: options.allowUnknownConfig,
|
|
195212
|
+
promptForTrust: options.promptForTrust,
|
|
195213
|
+
trustStorePath: options.trustStorePath,
|
|
195214
|
+
env: options.env,
|
|
195215
|
+
trustPrompt: options.trustPrompt
|
|
195216
|
+
});
|
|
195217
|
+
const loaded = options.configOverrides && options.configOverrides.length > 0 ? {
|
|
195218
|
+
...baseLoaded,
|
|
195219
|
+
config: applyConfigOverrides(baseLoaded.config, options.configOverrides)
|
|
195220
|
+
} : baseLoaded;
|
|
195221
|
+
completePhase("preflight");
|
|
195222
|
+
const trace = traceWriterFactory({
|
|
195223
|
+
enabled: loaded.config.audit.enabled,
|
|
195224
|
+
directory: loaded.config.audit.directory,
|
|
195225
|
+
traceId,
|
|
195226
|
+
cwd,
|
|
195227
|
+
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput,
|
|
195228
|
+
env: auditEnv
|
|
195229
|
+
});
|
|
195230
|
+
activeTrace = trace;
|
|
195231
|
+
const warnings = [...loaded.warnings, ...trace.warnings];
|
|
195232
|
+
writeProgressDeliveryFailure = (reason) => {
|
|
195233
|
+
warnings.push(`PROGRESS_DELIVERY_FAILED: ${reason}`);
|
|
195234
|
+
trace.write({
|
|
195235
|
+
type: "progress_delivery_failed",
|
|
195236
|
+
traceId,
|
|
195237
|
+
reason,
|
|
195238
|
+
timestamp: new Date().toISOString()
|
|
195239
|
+
}).catch(() => {
|
|
195240
|
+
return;
|
|
195241
|
+
});
|
|
195242
|
+
};
|
|
195243
|
+
if (progressDeliveryFailure !== undefined) {
|
|
195244
|
+
writeProgressDeliveryFailure(progressDeliveryFailure);
|
|
195245
|
+
}
|
|
195246
|
+
try {
|
|
195247
|
+
await trace.write({
|
|
195248
|
+
type: "request_received",
|
|
195249
|
+
traceId,
|
|
194416
195250
|
tool,
|
|
194417
|
-
|
|
194418
|
-
config: config2,
|
|
194419
|
-
roles: resolveAgentRoles(config2),
|
|
194420
|
-
budget: reviewBudget,
|
|
194421
|
-
entrypoint: options.entrypoint
|
|
195251
|
+
timestamp: new Date().toISOString()
|
|
194422
195252
|
});
|
|
194423
|
-
|
|
194424
|
-
|
|
194425
|
-
directory: config2.audit.directory,
|
|
195253
|
+
await trace.write({
|
|
195254
|
+
type: "config_loaded",
|
|
194426
195255
|
traceId,
|
|
194427
|
-
|
|
194428
|
-
|
|
195256
|
+
configHash: loaded.configHash,
|
|
195257
|
+
configPath: loaded.configPath,
|
|
195258
|
+
configSources: loaded.sources,
|
|
195259
|
+
configTrustStatus: loaded.configTrustStatus,
|
|
195260
|
+
timestamp: new Date().toISOString()
|
|
194429
195261
|
});
|
|
194430
|
-
|
|
194431
|
-
|
|
194432
|
-
|
|
194433
|
-
|
|
195262
|
+
validateReviewRequest(tool, request);
|
|
195263
|
+
const reviewBudget = resolveReviewBudget(loaded.config.reviewBudget, request.options?.reviewBudget);
|
|
195264
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(loaded.config, reviewBudget, options.env ?? process.env, request.options?.judgeProvider));
|
|
195265
|
+
assertTrustedWorkspaceRoot(request.workspace?.root, loaded.config.workspace.root, cwd);
|
|
195266
|
+
const networkMode = resolveNetworkMode(request.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
|
|
195267
|
+
if (networkMode === "unrestricted" && !loaded.config.network.allowUnrestricted) {
|
|
195268
|
+
throw new KyosoRequestError("unrestricted network mode is disabled by config", "NETWORK_MODE_DISABLED");
|
|
195269
|
+
}
|
|
195270
|
+
const disabledPolicy = disabledReviewPolicy(tool, loaded.config, options.entrypoint);
|
|
195271
|
+
if (disabledPolicy) {
|
|
195272
|
+
const redactedRequest = requestForRecursionFingerprint(request);
|
|
195273
|
+
const requestFingerprint2 = createRequestFingerprint({
|
|
194434
195274
|
tool,
|
|
194435
|
-
|
|
195275
|
+
request: redactedRequest,
|
|
195276
|
+
config: loaded.config,
|
|
195277
|
+
roles: resolveAgentRoles(loaded.config),
|
|
195278
|
+
budget: reviewBudget,
|
|
195279
|
+
entrypoint: options.entrypoint
|
|
194436
195280
|
});
|
|
194437
195281
|
await writeReviewBudgetPlanned({
|
|
194438
|
-
trace
|
|
195282
|
+
trace,
|
|
194439
195283
|
traceId,
|
|
194440
195284
|
budgetTracker,
|
|
194441
|
-
requestFingerprint
|
|
195285
|
+
requestFingerprint: requestFingerprint2
|
|
194442
195286
|
});
|
|
194443
|
-
|
|
195287
|
+
const warning = disabledPolicy.warning;
|
|
195288
|
+
return await completeReview(await buildPolicyBlockResult({
|
|
194444
195289
|
tool,
|
|
194445
|
-
trace
|
|
195290
|
+
trace,
|
|
194446
195291
|
traceId,
|
|
194447
195292
|
startedAt,
|
|
194448
|
-
|
|
194449
|
-
|
|
194450
|
-
|
|
195293
|
+
configHash: loaded.configHash,
|
|
195294
|
+
networkMode,
|
|
195295
|
+
cisaPolicy: loaded.config.securityReview.cisaSecureByDesign,
|
|
195296
|
+
warning,
|
|
194451
195297
|
budgetTracker,
|
|
194452
|
-
requestFingerprint,
|
|
194453
|
-
coverage: unavailableReviewCoverage(
|
|
195298
|
+
requestFingerprint: requestFingerprint2,
|
|
195299
|
+
coverage: unavailableReviewCoverage(redactedRequest, disabledPolicy.coverageReason, loaded.config.reviewPolicy.additionalLenses),
|
|
194454
195300
|
finding: {
|
|
194455
195301
|
id: "KYOSO-1",
|
|
194456
195302
|
severity: "critical",
|
|
194457
195303
|
category: "other",
|
|
194458
|
-
title:
|
|
194459
|
-
evidence:
|
|
194460
|
-
recommendation:
|
|
195304
|
+
title: disabledPolicy.title,
|
|
195305
|
+
evidence: warning,
|
|
195306
|
+
recommendation: disabledPolicy.recommendation,
|
|
194461
195307
|
disposition: "gate",
|
|
194462
195308
|
changeRelation: "unknown",
|
|
194463
195309
|
evidenceQuality: "concrete",
|
|
194464
195310
|
evidenceRefs: [],
|
|
194465
|
-
policyReasons: ["kyoso_policy",
|
|
195311
|
+
policyReasons: ["kyoso_policy", disabledPolicy.policyReason],
|
|
194466
195312
|
fingerprint: "",
|
|
194467
195313
|
sourceAgents: ["kyoso_policy"],
|
|
194468
195314
|
confidence: "high"
|
|
194469
195315
|
},
|
|
194470
195316
|
redactionsApplied: 0
|
|
195317
|
+
}));
|
|
195318
|
+
}
|
|
195319
|
+
if (networkMode === "unrestricted" && loaded.config.network.warnOnUnrestricted) {
|
|
195320
|
+
warnings.push("Network mode is unrestricted; write policy remains denied.");
|
|
195321
|
+
}
|
|
195322
|
+
startPhase("context");
|
|
195323
|
+
const secretScan = scanAndRedactSecrets(request);
|
|
195324
|
+
await trace.write({
|
|
195325
|
+
type: "secret_scan_completed",
|
|
195326
|
+
traceId,
|
|
195327
|
+
detected: secretScan.detected,
|
|
195328
|
+
redactions: secretScan.redactions,
|
|
195329
|
+
timestamp: new Date().toISOString()
|
|
195330
|
+
});
|
|
195331
|
+
const allowSecretOverride = loaded.config.secrets.allowOverride && request.options?.allowSecretRedaction === true;
|
|
195332
|
+
if (secretScan.detected && loaded.config.secrets.blockOnDetectedSecret && !allowSecretOverride) {
|
|
195333
|
+
const requestFingerprint2 = createRequestFingerprint({
|
|
195334
|
+
tool,
|
|
195335
|
+
request: secretScan.redactedRequest,
|
|
195336
|
+
config: loaded.config,
|
|
195337
|
+
roles: resolveAgentRoles(loaded.config),
|
|
195338
|
+
budget: reviewBudget,
|
|
195339
|
+
entrypoint: options.entrypoint
|
|
194471
195340
|
});
|
|
194472
|
-
|
|
194473
|
-
|
|
195341
|
+
await writeReviewBudgetPlanned({
|
|
195342
|
+
trace,
|
|
195343
|
+
traceId,
|
|
195344
|
+
budgetTracker,
|
|
195345
|
+
requestFingerprint: requestFingerprint2
|
|
195346
|
+
});
|
|
195347
|
+
skipPhase("context", "secret_detected");
|
|
195348
|
+
return await completeReview(await buildSecretBlockResult({
|
|
195349
|
+
tool,
|
|
195350
|
+
trace,
|
|
195351
|
+
traceId,
|
|
195352
|
+
startedAt,
|
|
195353
|
+
configHash: loaded.configHash,
|
|
195354
|
+
networkMode,
|
|
195355
|
+
cisaPolicy: loaded.config.securityReview.cisaSecureByDesign,
|
|
195356
|
+
additionalLenses: loaded.config.reviewPolicy.additionalLenses,
|
|
195357
|
+
secretScan,
|
|
195358
|
+
warnings,
|
|
195359
|
+
budgetTracker,
|
|
195360
|
+
requestFingerprint: requestFingerprint2
|
|
195361
|
+
}));
|
|
194474
195362
|
}
|
|
194475
|
-
|
|
194476
|
-
|
|
194477
|
-
|
|
194478
|
-
|
|
194479
|
-
|
|
194480
|
-
|
|
194481
|
-
|
|
194482
|
-
|
|
194483
|
-
|
|
194484
|
-
|
|
194485
|
-
|
|
194486
|
-
|
|
194487
|
-
ignoreConfig: options.ignoreConfig,
|
|
194488
|
-
trustConfig: options.trustConfig,
|
|
194489
|
-
allowUnknownConfig: options.allowUnknownConfig,
|
|
194490
|
-
promptForTrust: options.promptForTrust,
|
|
194491
|
-
trustStorePath: options.trustStorePath,
|
|
194492
|
-
env: options.env,
|
|
194493
|
-
trustPrompt: options.trustPrompt
|
|
194494
|
-
});
|
|
194495
|
-
const loaded = options.configOverrides && options.configOverrides.length > 0 ? {
|
|
194496
|
-
...baseLoaded,
|
|
194497
|
-
config: applyConfigOverrides(baseLoaded.config, options.configOverrides)
|
|
194498
|
-
} : baseLoaded;
|
|
194499
|
-
const trace = traceWriterFactory({
|
|
194500
|
-
enabled: loaded.config.audit.enabled,
|
|
194501
|
-
directory: loaded.config.audit.directory,
|
|
194502
|
-
traceId,
|
|
194503
|
-
cwd,
|
|
194504
|
-
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput,
|
|
194505
|
-
env: auditEnv
|
|
194506
|
-
});
|
|
194507
|
-
const warnings = [...loaded.warnings, ...trace.warnings];
|
|
194508
|
-
try {
|
|
194509
|
-
await trace.write({
|
|
194510
|
-
type: "request_received",
|
|
194511
|
-
traceId,
|
|
194512
|
-
tool,
|
|
194513
|
-
timestamp: new Date().toISOString()
|
|
194514
|
-
});
|
|
194515
|
-
await trace.write({
|
|
194516
|
-
type: "config_loaded",
|
|
194517
|
-
traceId,
|
|
194518
|
-
configHash: loaded.configHash,
|
|
194519
|
-
configPath: loaded.configPath,
|
|
194520
|
-
configSources: loaded.sources,
|
|
194521
|
-
configTrustStatus: loaded.configTrustStatus,
|
|
194522
|
-
timestamp: new Date().toISOString()
|
|
194523
|
-
});
|
|
194524
|
-
validateReviewRequest(tool, request);
|
|
194525
|
-
const reviewBudget = resolveReviewBudget(loaded.config.reviewBudget, request.options?.reviewBudget);
|
|
194526
|
-
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(loaded.config, reviewBudget, options.env ?? process.env, request.options?.judgeProvider));
|
|
194527
|
-
assertTrustedWorkspaceRoot(request.workspace?.root, loaded.config.workspace.root, cwd);
|
|
194528
|
-
const networkMode = resolveNetworkMode(request.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
|
|
194529
|
-
if (networkMode === "unrestricted" && !loaded.config.network.allowUnrestricted) {
|
|
194530
|
-
throw new KyosoRequestError("unrestricted network mode is disabled by config", "NETWORK_MODE_DISABLED");
|
|
194531
|
-
}
|
|
194532
|
-
const disabledPolicy = disabledReviewPolicy(tool, loaded.config, options.entrypoint);
|
|
194533
|
-
if (disabledPolicy) {
|
|
194534
|
-
const redactedRequest = requestForRecursionFingerprint(request);
|
|
194535
|
-
const requestFingerprint2 = createRequestFingerprint({
|
|
195363
|
+
const denyPatterns = mergeDenyPatterns(loaded.config.workspace.deny, secretScan.redactedRequest.workspace?.denyRead);
|
|
195364
|
+
const allowPatterns = secretScan.redactedRequest.workspace?.allowRead ?? [];
|
|
195365
|
+
const built = buildContext(secretScan.redactedRequest, {
|
|
195366
|
+
maxContextBytes: loaded.config.workspace.maxContextBytes,
|
|
195367
|
+
maxDiffBytes: loaded.config.workspace.maxDiffBytes,
|
|
195368
|
+
denyPatterns,
|
|
195369
|
+
allowPatterns
|
|
195370
|
+
});
|
|
195371
|
+
warnings.push(...built.warnings);
|
|
195372
|
+
completePhase("context");
|
|
195373
|
+
const agentRoles = resolveAgentRoles(loaded.config);
|
|
195374
|
+
const requestFingerprint = createRequestFingerprint({
|
|
194536
195375
|
tool,
|
|
194537
|
-
request:
|
|
195376
|
+
request: built.request,
|
|
194538
195377
|
config: loaded.config,
|
|
194539
|
-
roles:
|
|
195378
|
+
roles: agentRoles,
|
|
194540
195379
|
budget: reviewBudget,
|
|
194541
195380
|
entrypoint: options.entrypoint
|
|
194542
195381
|
});
|
|
@@ -194544,370 +195383,308 @@ async function runReview(tool, request, options = {}) {
|
|
|
194544
195383
|
trace,
|
|
194545
195384
|
traceId,
|
|
194546
195385
|
budgetTracker,
|
|
194547
|
-
requestFingerprint
|
|
195386
|
+
requestFingerprint
|
|
194548
195387
|
});
|
|
194549
|
-
|
|
194550
|
-
|
|
194551
|
-
|
|
194552
|
-
|
|
194553
|
-
|
|
194554
|
-
|
|
194555
|
-
configHash: loaded.configHash,
|
|
194556
|
-
networkMode,
|
|
194557
|
-
cisaPolicy: loaded.config.securityReview.cisaSecureByDesign,
|
|
194558
|
-
warning,
|
|
194559
|
-
budgetTracker,
|
|
194560
|
-
requestFingerprint: requestFingerprint2,
|
|
194561
|
-
coverage: unavailableReviewCoverage(redactedRequest, disabledPolicy.coverageReason, loaded.config.reviewPolicy.additionalLenses),
|
|
194562
|
-
finding: {
|
|
194563
|
-
id: "KYOSO-1",
|
|
194564
|
-
severity: "critical",
|
|
194565
|
-
category: "other",
|
|
194566
|
-
title: disabledPolicy.title,
|
|
194567
|
-
evidence: warning,
|
|
194568
|
-
recommendation: disabledPolicy.recommendation,
|
|
194569
|
-
disposition: "gate",
|
|
194570
|
-
changeRelation: "unknown",
|
|
194571
|
-
evidenceQuality: "concrete",
|
|
194572
|
-
evidenceRefs: [],
|
|
194573
|
-
policyReasons: ["kyoso_policy", disabledPolicy.policyReason],
|
|
194574
|
-
fingerprint: "",
|
|
194575
|
-
sourceAgents: ["kyoso_policy"],
|
|
194576
|
-
confidence: "high"
|
|
194577
|
-
},
|
|
194578
|
-
redactionsApplied: 0
|
|
194579
|
-
});
|
|
194580
|
-
}
|
|
194581
|
-
if (networkMode === "unrestricted" && loaded.config.network.warnOnUnrestricted) {
|
|
194582
|
-
warnings.push("Network mode is unrestricted; write policy remains denied.");
|
|
194583
|
-
}
|
|
194584
|
-
const secretScan = scanAndRedactSecrets(request);
|
|
194585
|
-
await trace.write({
|
|
194586
|
-
type: "secret_scan_completed",
|
|
194587
|
-
traceId,
|
|
194588
|
-
detected: secretScan.detected,
|
|
194589
|
-
redactions: secretScan.redactions,
|
|
194590
|
-
timestamp: new Date().toISOString()
|
|
194591
|
-
});
|
|
194592
|
-
const allowSecretOverride = loaded.config.secrets.allowOverride && request.options?.allowSecretRedaction === true;
|
|
194593
|
-
if (secretScan.detected && loaded.config.secrets.blockOnDetectedSecret && !allowSecretOverride) {
|
|
194594
|
-
const requestFingerprint2 = createRequestFingerprint({
|
|
194595
|
-
tool,
|
|
194596
|
-
request: secretScan.redactedRequest,
|
|
194597
|
-
config: loaded.config,
|
|
194598
|
-
roles: resolveAgentRoles(loaded.config),
|
|
194599
|
-
budget: reviewBudget,
|
|
194600
|
-
entrypoint: options.entrypoint
|
|
195388
|
+
warnings.push(...plannedBudgetWarnings(budgetTracker));
|
|
195389
|
+
startPhase("snapshot");
|
|
195390
|
+
snapshot = await createSnapshot(traceId, tool, built.request, {
|
|
195391
|
+
denyPatterns,
|
|
195392
|
+
allowPatterns,
|
|
195393
|
+
agentRoles
|
|
194601
195394
|
});
|
|
194602
|
-
await
|
|
194603
|
-
|
|
195395
|
+
await trace.write({
|
|
195396
|
+
type: "snapshot_created",
|
|
194604
195397
|
traceId,
|
|
194605
|
-
|
|
194606
|
-
|
|
195398
|
+
path: snapshot.root,
|
|
195399
|
+
fileCount: snapshot.fileCount,
|
|
195400
|
+
timestamp: new Date().toISOString()
|
|
194607
195401
|
});
|
|
194608
|
-
|
|
195402
|
+
completePhase("snapshot");
|
|
195403
|
+
const manager = options.agentManager ?? defaultAgentManager(loaded.config, options.env ?? process.env);
|
|
195404
|
+
startPhase("primary");
|
|
195405
|
+
const agentResults = await runAgents({
|
|
194609
195406
|
tool,
|
|
194610
|
-
|
|
195407
|
+
request: built.request,
|
|
195408
|
+
config: loaded.config,
|
|
194611
195409
|
traceId,
|
|
194612
|
-
|
|
194613
|
-
configHash: loaded.configHash,
|
|
195410
|
+
workspaceDir: snapshot.root,
|
|
194614
195411
|
networkMode,
|
|
194615
|
-
|
|
194616
|
-
|
|
194617
|
-
secretScan,
|
|
195412
|
+
manager,
|
|
195413
|
+
trace,
|
|
194618
195414
|
warnings,
|
|
194619
195415
|
budgetTracker,
|
|
194620
|
-
|
|
195416
|
+
progressDispatcher: dispatcher,
|
|
195417
|
+
signal: options.signal,
|
|
195418
|
+
progressHeartbeatMs: options.progressHeartbeatMs
|
|
194621
195419
|
});
|
|
194622
|
-
|
|
194623
|
-
|
|
194624
|
-
|
|
194625
|
-
|
|
194626
|
-
|
|
194627
|
-
|
|
194628
|
-
|
|
194629
|
-
|
|
194630
|
-
|
|
194631
|
-
|
|
194632
|
-
|
|
194633
|
-
|
|
194634
|
-
|
|
194635
|
-
|
|
194636
|
-
|
|
194637
|
-
|
|
194638
|
-
|
|
194639
|
-
|
|
194640
|
-
|
|
194641
|
-
|
|
194642
|
-
|
|
194643
|
-
|
|
194644
|
-
|
|
194645
|
-
|
|
194646
|
-
|
|
194647
|
-
|
|
194648
|
-
|
|
194649
|
-
|
|
194650
|
-
|
|
194651
|
-
|
|
194652
|
-
|
|
194653
|
-
|
|
194654
|
-
|
|
194655
|
-
|
|
194656
|
-
|
|
194657
|
-
|
|
194658
|
-
|
|
194659
|
-
|
|
194660
|
-
|
|
194661
|
-
|
|
194662
|
-
|
|
194663
|
-
|
|
194664
|
-
|
|
194665
|
-
|
|
194666
|
-
|
|
194667
|
-
|
|
194668
|
-
|
|
194669
|
-
|
|
194670
|
-
|
|
194671
|
-
|
|
194672
|
-
|
|
194673
|
-
|
|
194674
|
-
|
|
194675
|
-
|
|
194676
|
-
|
|
194677
|
-
|
|
194678
|
-
|
|
194679
|
-
|
|
194680
|
-
|
|
194681
|
-
|
|
194682
|
-
|
|
194683
|
-
|
|
194684
|
-
|
|
194685
|
-
|
|
194686
|
-
|
|
194687
|
-
|
|
194688
|
-
|
|
194689
|
-
|
|
194690
|
-
multiAgentRequired: loaded.config.reviewPolicy.multiAgentRequired
|
|
194691
|
-
})) {
|
|
194692
|
-
budgetTracker.markIncomplete("coverage_incomplete");
|
|
194693
|
-
warnings.push(formatCoverageWarning(coverage, loaded.config));
|
|
194694
|
-
}
|
|
194695
|
-
let aggregate = aggregateAgentResults(normalizedAgentResults, {
|
|
194696
|
-
reviewMode
|
|
194697
|
-
});
|
|
194698
|
-
if (secretScan.detected && allowSecretOverride) {
|
|
195420
|
+
completePhase("primary");
|
|
195421
|
+
warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
|
|
195422
|
+
startPhase("aggregation");
|
|
195423
|
+
const normalizedAgentResults = agentResults.map((result) => normalizeAgentRunResult(result, reviewBudget.maxFindingsPerAgent));
|
|
195424
|
+
for (const result of normalizedAgentResults.filter((item) => item.findingsTargetExceeded)) {
|
|
195425
|
+
warnings.push(`Agent ${result.agent} reported ${result.reportedFindings} findings, above the soft target of ${reviewBudget.maxFindingsPerAgent}; all findings were retained.`);
|
|
195426
|
+
}
|
|
195427
|
+
const enabledAgents = ["codex", "claude"].filter((agent) => loaded.config.agents[agent].enabled);
|
|
195428
|
+
const agentsUsed = normalizedAgentResults.filter((result) => result.status !== "skipped").map((result) => result.agent);
|
|
195429
|
+
const reviewMode = enabledAgents.length === 1 ? "single_agent" : "multi_agent";
|
|
195430
|
+
const completed = normalizedAgentResults.filter((result) => result.status === "completed");
|
|
195431
|
+
const attempted = normalizedAgentResults.filter((result) => result.status !== "skipped");
|
|
195432
|
+
const degraded = completed.length !== attempted.length || enabledAgents.length === 0;
|
|
195433
|
+
const coverage = buildReviewCoverage({
|
|
195434
|
+
request: built.request,
|
|
195435
|
+
additionalLenses: loaded.config.reviewPolicy.additionalLenses,
|
|
195436
|
+
agentResults: normalizedAgentResults
|
|
195437
|
+
});
|
|
195438
|
+
if (isCoverageIncomplete(coverage, {
|
|
195439
|
+
multiAgentRequired: loaded.config.reviewPolicy.multiAgentRequired
|
|
195440
|
+
})) {
|
|
195441
|
+
budgetTracker.markIncomplete("coverage_incomplete");
|
|
195442
|
+
warnings.push(formatCoverageWarning(coverage, loaded.config));
|
|
195443
|
+
}
|
|
195444
|
+
let aggregate = aggregateAgentResults(normalizedAgentResults, {
|
|
195445
|
+
reviewMode
|
|
195446
|
+
});
|
|
195447
|
+
if (secretScan.detected && allowSecretOverride) {
|
|
195448
|
+
aggregate = {
|
|
195449
|
+
...aggregate,
|
|
195450
|
+
findings: reindexFindings([
|
|
195451
|
+
buildSecretFinding(secretScan, {
|
|
195452
|
+
id: "KYOSO-1",
|
|
195453
|
+
blocked: false
|
|
195454
|
+
}),
|
|
195455
|
+
...aggregate.findings
|
|
195456
|
+
])
|
|
195457
|
+
};
|
|
195458
|
+
}
|
|
195459
|
+
if (completed.length === 0 && (attempted.length > 0 || enabledAgents.length === 0)) {
|
|
195460
|
+
const noPrimaryAgents = enabledAgents.length === 0;
|
|
195461
|
+
if (noPrimaryAgents) {
|
|
195462
|
+
budgetTracker.markIncomplete("coverage_incomplete");
|
|
195463
|
+
warnings.push("No primary review agents are enabled; review coverage is incomplete.");
|
|
195464
|
+
}
|
|
195465
|
+
aggregate = {
|
|
195466
|
+
...aggregate,
|
|
195467
|
+
findings: [
|
|
195468
|
+
...aggregate.findings,
|
|
195469
|
+
{
|
|
195470
|
+
id: `KYOSO-${aggregate.findings.length + 1}`,
|
|
195471
|
+
severity: "critical",
|
|
195472
|
+
category: "other",
|
|
195473
|
+
title: noPrimaryAgents ? "No primary review agents enabled" : "All backend agents failed",
|
|
195474
|
+
evidence: noPrimaryAgents ? "Both configured primary reviewers are disabled." : normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
195475
|
+
recommendation: noPrimaryAgents ? "Enable at least one primary reviewer before running Kyoso." : "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
195476
|
+
disposition: "gate",
|
|
195477
|
+
changeRelation: "unknown",
|
|
195478
|
+
evidenceQuality: "concrete",
|
|
195479
|
+
evidenceRefs: [],
|
|
195480
|
+
policyReasons: ["kyoso_policy", "coverage_incomplete"],
|
|
195481
|
+
fingerprint: "",
|
|
195482
|
+
sourceAgents: ["kyoso_policy"],
|
|
195483
|
+
confidence: "high"
|
|
195484
|
+
}
|
|
195485
|
+
]
|
|
195486
|
+
};
|
|
195487
|
+
}
|
|
194699
195488
|
aggregate = {
|
|
194700
195489
|
...aggregate,
|
|
194701
|
-
findings:
|
|
194702
|
-
|
|
194703
|
-
|
|
194704
|
-
|
|
194705
|
-
|
|
194706
|
-
|
|
194707
|
-
])
|
|
195490
|
+
findings: admitFindings({
|
|
195491
|
+
tool,
|
|
195492
|
+
request: built.request,
|
|
195493
|
+
findings: aggregate.findings,
|
|
195494
|
+
reviewMode
|
|
195495
|
+
})
|
|
194708
195496
|
};
|
|
194709
|
-
|
|
194710
|
-
|
|
194711
|
-
|
|
194712
|
-
|
|
194713
|
-
|
|
194714
|
-
|
|
195497
|
+
await trace.write({
|
|
195498
|
+
type: "aggregation_completed",
|
|
195499
|
+
traceId,
|
|
195500
|
+
findingCount: aggregate.findings.length,
|
|
195501
|
+
timestamp: new Date().toISOString()
|
|
195502
|
+
});
|
|
195503
|
+
completePhase("aggregation");
|
|
195504
|
+
const verificationMode = loaded.config.verification.enabled && reviewMode === "single_agent" ? "skipped_single_agent" : loaded.config.verification.enabled && enabledAgents.length > 1 ? "cross_agent" : undefined;
|
|
195505
|
+
if (verificationMode === "cross_agent") {
|
|
195506
|
+
startPhase("verification");
|
|
195507
|
+
warnings.push(...await runFindingVerification({
|
|
195508
|
+
tool,
|
|
195509
|
+
request: built.request,
|
|
195510
|
+
config: loaded.config,
|
|
195511
|
+
traceId,
|
|
195512
|
+
workspaceDir: snapshot.root,
|
|
195513
|
+
networkMode,
|
|
195514
|
+
manager,
|
|
195515
|
+
trace,
|
|
195516
|
+
findings: aggregate.findings,
|
|
195517
|
+
budgetTracker,
|
|
195518
|
+
signal: options.signal
|
|
195519
|
+
}));
|
|
195520
|
+
completePhase("verification");
|
|
195521
|
+
} else {
|
|
195522
|
+
skipPhase("verification", verificationMode === "skipped_single_agent" ? "single_agent_review" : "verification_disabled");
|
|
194715
195523
|
}
|
|
194716
195524
|
aggregate = {
|
|
194717
195525
|
...aggregate,
|
|
194718
|
-
findings:
|
|
194719
|
-
|
|
194720
|
-
|
|
194721
|
-
|
|
194722
|
-
|
|
194723
|
-
|
|
194724
|
-
title: noPrimaryAgents ? "No primary review agents enabled" : "All backend agents failed",
|
|
194725
|
-
evidence: noPrimaryAgents ? "Both configured primary reviewers are disabled." : normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
194726
|
-
recommendation: noPrimaryAgents ? "Enable at least one primary reviewer before running Kyoso." : "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
194727
|
-
disposition: "gate",
|
|
194728
|
-
changeRelation: "unknown",
|
|
194729
|
-
evidenceQuality: "concrete",
|
|
194730
|
-
evidenceRefs: [],
|
|
194731
|
-
policyReasons: ["kyoso_policy", "coverage_incomplete"],
|
|
194732
|
-
fingerprint: "",
|
|
194733
|
-
sourceAgents: ["kyoso_policy"],
|
|
194734
|
-
confidence: "high"
|
|
194735
|
-
}
|
|
194736
|
-
]
|
|
195526
|
+
findings: admitFindings({
|
|
195527
|
+
tool,
|
|
195528
|
+
request: built.request,
|
|
195529
|
+
findings: aggregate.findings,
|
|
195530
|
+
reviewMode
|
|
195531
|
+
})
|
|
194737
195532
|
};
|
|
194738
|
-
|
|
194739
|
-
|
|
194740
|
-
|
|
194741
|
-
|
|
195533
|
+
if (aggregate.findings.some((finding) => finding.disposition === "disputed")) {
|
|
195534
|
+
budgetTracker.markIncomplete("disputed_finding");
|
|
195535
|
+
}
|
|
195536
|
+
const cisaPolicy = loaded.config.securityReview.cisaSecureByDesign;
|
|
195537
|
+
const cisa = tool === "security_review" && cisaPolicy.enabled ? computeCisaGate(aggregate.findings, normalizedAgentResults, cisaPolicy) : undefined;
|
|
195538
|
+
const budgetBeforeJudge = budgetTracker.snapshot();
|
|
195539
|
+
const decision = budgetBeforeJudge.completion.status === "incomplete" ? "block" : decide({
|
|
194742
195540
|
tool,
|
|
194743
|
-
request: built.request,
|
|
194744
195541
|
findings: aggregate.findings,
|
|
194745
|
-
|
|
194746
|
-
|
|
194747
|
-
|
|
194748
|
-
|
|
194749
|
-
|
|
194750
|
-
|
|
194751
|
-
|
|
194752
|
-
|
|
194753
|
-
|
|
194754
|
-
|
|
194755
|
-
|
|
194756
|
-
|
|
195542
|
+
cisa: cisaPolicy.gate ? cisa : undefined,
|
|
195543
|
+
degraded,
|
|
195544
|
+
secretScan: { detected: secretScan.detected, blocked: false }
|
|
195545
|
+
});
|
|
195546
|
+
const completedAt = new Date().toISOString();
|
|
195547
|
+
const resultWithoutMarkdown = {
|
|
195548
|
+
decision,
|
|
195549
|
+
completion: budgetBeforeJudge.completion,
|
|
195550
|
+
executionBudget: budgetBeforeJudge.executionBudget,
|
|
195551
|
+
requestFingerprint,
|
|
195552
|
+
degraded,
|
|
195553
|
+
agentsUsed,
|
|
195554
|
+
reviewMode,
|
|
195555
|
+
coverage,
|
|
195556
|
+
...verificationMode ? { verificationMode } : {},
|
|
195557
|
+
findings: aggregate.findings,
|
|
195558
|
+
cisaSecureByDesign: cisa,
|
|
195559
|
+
disagreements: aggregate.disagreements,
|
|
195560
|
+
testsToAdd: selectRegressionTests(aggregate.testsToAdd),
|
|
195561
|
+
residualRisks: tool === "security_review" && aggregate.residualRisks.length === 0 ? [
|
|
195562
|
+
"No residual risks were reported by completed agents; verify security assumptions before release."
|
|
195563
|
+
] : aggregate.residualRisks,
|
|
195564
|
+
openQuestions: Array.from(new Set([
|
|
195565
|
+
...aggregate.openQuestions,
|
|
195566
|
+
...buildAdmissionOpenQuestions(aggregate.findings)
|
|
195567
|
+
])),
|
|
195568
|
+
agentOpinions: normalizedAgentResults.map((result) => agentOpinionSummary(result, request.options?.includeAgentRawOutputs === true)),
|
|
195569
|
+
audit: {
|
|
195570
|
+
traceId,
|
|
195571
|
+
startedAt,
|
|
195572
|
+
completedAt,
|
|
195573
|
+
agentsUsed,
|
|
195574
|
+
redactionsApplied: secretScan.redactions,
|
|
195575
|
+
networkMode,
|
|
195576
|
+
workspaceMode: "temp_snapshot",
|
|
195577
|
+
configHash: loaded.configHash,
|
|
195578
|
+
warnings: Array.from(new Set([...warnings, ...trace.warnings])),
|
|
195579
|
+
modelCalls: budgetBeforeJudge.modelCalls
|
|
195580
|
+
}
|
|
195581
|
+
};
|
|
195582
|
+
const summaryText = defaultSummaryText(resultWithoutMarkdown);
|
|
195583
|
+
startPhase("judge");
|
|
195584
|
+
const judge = await runBudgetedJudge({
|
|
194757
195585
|
tool,
|
|
194758
|
-
|
|
194759
|
-
|
|
194760
|
-
|
|
194761
|
-
|
|
194762
|
-
|
|
194763
|
-
|
|
195586
|
+
result: resultWithoutMarkdown,
|
|
195587
|
+
summaryText,
|
|
195588
|
+
agentFindings: buildJudgeAgentFindings(normalizedAgentResults),
|
|
195589
|
+
config: loaded.config.judge,
|
|
195590
|
+
requestedProvider: request.options?.judgeProvider,
|
|
195591
|
+
env: options.env ?? process.env,
|
|
195592
|
+
budgetTracker,
|
|
194764
195593
|
trace,
|
|
194765
|
-
|
|
194766
|
-
|
|
195594
|
+
traceId,
|
|
195595
|
+
signal: options.signal
|
|
195596
|
+
});
|
|
195597
|
+
completePhase("judge");
|
|
195598
|
+
startPhase("finalize");
|
|
195599
|
+
const judgeComments = new Map(judge.output.disagreementComments.map((comment) => [
|
|
195600
|
+
comment.topic,
|
|
195601
|
+
comment.judgeComment
|
|
195602
|
+
]));
|
|
195603
|
+
const disagreements = resultWithoutMarkdown.disagreements.map((disagreement) => ({
|
|
195604
|
+
...disagreement,
|
|
195605
|
+
judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
|
|
194767
195606
|
}));
|
|
194768
|
-
|
|
194769
|
-
|
|
194770
|
-
|
|
194771
|
-
|
|
195607
|
+
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
195608
|
+
const budgetAfterJudge = budgetTracker.snapshot();
|
|
195609
|
+
const finalWarnings = Array.from(new Set([
|
|
195610
|
+
...resultWithoutMarkdown.audit.warnings ?? [],
|
|
195611
|
+
...outputWarningMessages(budgetAfterJudge),
|
|
195612
|
+
...tokenUsageWarningMessages(budgetTracker, budgetAfterJudge)
|
|
195613
|
+
]));
|
|
195614
|
+
const finalDecision = budgetAfterJudge.completion.status === "incomplete" ? "block" : decision;
|
|
195615
|
+
const resultAfterJudge = {
|
|
195616
|
+
...resultWithoutMarkdown,
|
|
195617
|
+
decision: finalDecision,
|
|
195618
|
+
completion: budgetAfterJudge.completion,
|
|
195619
|
+
executionBudget: budgetAfterJudge.executionBudget,
|
|
195620
|
+
disagreements,
|
|
195621
|
+
...crossModelAnalysis ? { crossModelAnalysis } : {},
|
|
195622
|
+
audit: {
|
|
195623
|
+
...resultWithoutMarkdown.audit,
|
|
195624
|
+
completedAt: new Date().toISOString(),
|
|
195625
|
+
warnings: finalWarnings,
|
|
195626
|
+
modelCalls: budgetAfterJudge.modelCalls
|
|
195627
|
+
}
|
|
195628
|
+
};
|
|
195629
|
+
const judgeEvent = {
|
|
195630
|
+
type: "judge_completed",
|
|
195631
|
+
traceId,
|
|
195632
|
+
provider: judge.provider,
|
|
195633
|
+
status: judge.status,
|
|
195634
|
+
...judge.executionIdentity ? { executionIdentity: judge.executionIdentity } : {},
|
|
195635
|
+
timestamp: new Date().toISOString()
|
|
195636
|
+
};
|
|
195637
|
+
if (judge.error)
|
|
195638
|
+
judgeEvent.error = judge.error;
|
|
195639
|
+
await trace.write(judgeEvent);
|
|
195640
|
+
resultAfterJudge.audit.completedAt = new Date().toISOString();
|
|
195641
|
+
await writeReviewBudgetCompleted({
|
|
195642
|
+
trace,
|
|
195643
|
+
traceId,
|
|
195644
|
+
budgetTracker,
|
|
195645
|
+
requestFingerprint
|
|
195646
|
+
});
|
|
195647
|
+
await trace.write({
|
|
195648
|
+
type: "decision_completed",
|
|
195649
|
+
traceId,
|
|
195650
|
+
decision: finalDecision,
|
|
195651
|
+
timestamp: new Date().toISOString()
|
|
195652
|
+
});
|
|
195653
|
+
await trace.write({
|
|
195654
|
+
type: "response_sent",
|
|
195655
|
+
traceId,
|
|
195656
|
+
timestamp: new Date().toISOString()
|
|
195657
|
+
});
|
|
195658
|
+
completePhase("finalize");
|
|
195659
|
+
await reportReviewCompleted(resultAfterJudge);
|
|
195660
|
+
return await finalizeReviewResult({
|
|
194772
195661
|
tool,
|
|
194773
|
-
|
|
194774
|
-
|
|
194775
|
-
|
|
194776
|
-
})
|
|
194777
|
-
}
|
|
194778
|
-
|
|
194779
|
-
|
|
195662
|
+
trace,
|
|
195663
|
+
result: resultAfterJudge,
|
|
195664
|
+
summaryText: resultAfterJudge.completion.status === "incomplete" ? defaultSummaryText(resultAfterJudge) : judge.output.summaryText
|
|
195665
|
+
});
|
|
195666
|
+
} catch (error51) {
|
|
195667
|
+
await reportReviewFailure(error51, trace);
|
|
195668
|
+
throw error51;
|
|
195669
|
+
} finally {
|
|
195670
|
+
await trace.finalize();
|
|
195671
|
+
if (snapshot)
|
|
195672
|
+
await cleanupSnapshot(snapshot.root);
|
|
194780
195673
|
}
|
|
194781
|
-
|
|
194782
|
-
|
|
194783
|
-
|
|
194784
|
-
const decision = budgetBeforeJudge.completion.status === "incomplete" ? "block" : decide({
|
|
194785
|
-
tool,
|
|
194786
|
-
findings: aggregate.findings,
|
|
194787
|
-
cisa: cisaPolicy.gate ? cisa : undefined,
|
|
194788
|
-
degraded,
|
|
194789
|
-
secretScan: { detected: secretScan.detected, blocked: false }
|
|
194790
|
-
});
|
|
194791
|
-
const completedAt = new Date().toISOString();
|
|
194792
|
-
const resultWithoutMarkdown = {
|
|
194793
|
-
decision,
|
|
194794
|
-
completion: budgetBeforeJudge.completion,
|
|
194795
|
-
executionBudget: budgetBeforeJudge.executionBudget,
|
|
194796
|
-
requestFingerprint,
|
|
194797
|
-
degraded,
|
|
194798
|
-
agentsUsed,
|
|
194799
|
-
reviewMode,
|
|
194800
|
-
coverage,
|
|
194801
|
-
...verificationMode ? { verificationMode } : {},
|
|
194802
|
-
findings: aggregate.findings,
|
|
194803
|
-
cisaSecureByDesign: cisa,
|
|
194804
|
-
disagreements: aggregate.disagreements,
|
|
194805
|
-
testsToAdd: selectRegressionTests(aggregate.testsToAdd),
|
|
194806
|
-
residualRisks: tool === "security_review" && aggregate.residualRisks.length === 0 ? [
|
|
194807
|
-
"No residual risks were reported by completed agents; verify security assumptions before release."
|
|
194808
|
-
] : aggregate.residualRisks,
|
|
194809
|
-
openQuestions: Array.from(new Set([
|
|
194810
|
-
...aggregate.openQuestions,
|
|
194811
|
-
...buildAdmissionOpenQuestions(aggregate.findings)
|
|
194812
|
-
])),
|
|
194813
|
-
agentOpinions: normalizedAgentResults.map((result) => agentOpinionSummary(result, request.options?.includeAgentRawOutputs === true)),
|
|
194814
|
-
audit: {
|
|
194815
|
-
traceId,
|
|
194816
|
-
startedAt,
|
|
194817
|
-
completedAt,
|
|
194818
|
-
agentsUsed,
|
|
194819
|
-
redactionsApplied: secretScan.redactions,
|
|
194820
|
-
networkMode,
|
|
194821
|
-
workspaceMode: "temp_snapshot",
|
|
194822
|
-
configHash: loaded.configHash,
|
|
194823
|
-
warnings: Array.from(new Set([...warnings, ...trace.warnings])),
|
|
194824
|
-
modelCalls: budgetBeforeJudge.modelCalls
|
|
194825
|
-
}
|
|
194826
|
-
};
|
|
194827
|
-
const summaryText = defaultSummaryText(resultWithoutMarkdown);
|
|
194828
|
-
const judge = await runBudgetedJudge({
|
|
194829
|
-
tool,
|
|
194830
|
-
result: resultWithoutMarkdown,
|
|
194831
|
-
summaryText,
|
|
194832
|
-
agentFindings: buildJudgeAgentFindings(normalizedAgentResults),
|
|
194833
|
-
config: loaded.config.judge,
|
|
194834
|
-
requestedProvider: request.options?.judgeProvider,
|
|
194835
|
-
env: options.env ?? process.env,
|
|
194836
|
-
budgetTracker,
|
|
194837
|
-
trace,
|
|
194838
|
-
traceId
|
|
194839
|
-
});
|
|
194840
|
-
const judgeComments = new Map(judge.output.disagreementComments.map((comment) => [
|
|
194841
|
-
comment.topic,
|
|
194842
|
-
comment.judgeComment
|
|
194843
|
-
]));
|
|
194844
|
-
const disagreements = resultWithoutMarkdown.disagreements.map((disagreement) => ({
|
|
194845
|
-
...disagreement,
|
|
194846
|
-
judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
|
|
194847
|
-
}));
|
|
194848
|
-
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
194849
|
-
const budgetAfterJudge = budgetTracker.snapshot();
|
|
194850
|
-
const finalWarnings = Array.from(new Set([
|
|
194851
|
-
...resultWithoutMarkdown.audit.warnings ?? [],
|
|
194852
|
-
...outputWarningMessages(budgetAfterJudge),
|
|
194853
|
-
...tokenUsageWarningMessages(budgetTracker, budgetAfterJudge)
|
|
194854
|
-
]));
|
|
194855
|
-
const finalDecision = budgetAfterJudge.completion.status === "incomplete" ? "block" : decision;
|
|
194856
|
-
const resultAfterJudge = {
|
|
194857
|
-
...resultWithoutMarkdown,
|
|
194858
|
-
decision: finalDecision,
|
|
194859
|
-
completion: budgetAfterJudge.completion,
|
|
194860
|
-
executionBudget: budgetAfterJudge.executionBudget,
|
|
194861
|
-
disagreements,
|
|
194862
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {},
|
|
194863
|
-
audit: {
|
|
194864
|
-
...resultWithoutMarkdown.audit,
|
|
194865
|
-
completedAt: new Date().toISOString(),
|
|
194866
|
-
warnings: finalWarnings,
|
|
194867
|
-
modelCalls: budgetAfterJudge.modelCalls
|
|
194868
|
-
}
|
|
194869
|
-
};
|
|
194870
|
-
const judgeEvent = {
|
|
194871
|
-
type: "judge_completed",
|
|
194872
|
-
traceId,
|
|
194873
|
-
provider: judge.provider,
|
|
194874
|
-
status: judge.status,
|
|
194875
|
-
...judge.executionIdentity ? { executionIdentity: judge.executionIdentity } : {},
|
|
194876
|
-
timestamp: new Date().toISOString()
|
|
194877
|
-
};
|
|
194878
|
-
if (judge.error)
|
|
194879
|
-
judgeEvent.error = judge.error;
|
|
194880
|
-
await trace.write(judgeEvent);
|
|
194881
|
-
resultAfterJudge.audit.completedAt = new Date().toISOString();
|
|
194882
|
-
await writeReviewBudgetCompleted({
|
|
194883
|
-
trace,
|
|
194884
|
-
traceId,
|
|
194885
|
-
budgetTracker,
|
|
194886
|
-
requestFingerprint
|
|
194887
|
-
});
|
|
194888
|
-
await trace.write({
|
|
194889
|
-
type: "decision_completed",
|
|
194890
|
-
traceId,
|
|
194891
|
-
decision: finalDecision,
|
|
194892
|
-
timestamp: new Date().toISOString()
|
|
194893
|
-
});
|
|
194894
|
-
await trace.write({
|
|
194895
|
-
type: "response_sent",
|
|
194896
|
-
traceId,
|
|
194897
|
-
timestamp: new Date().toISOString()
|
|
194898
|
-
});
|
|
194899
|
-
return await finalizeReviewResult({
|
|
194900
|
-
tool,
|
|
194901
|
-
trace,
|
|
194902
|
-
result: resultAfterJudge,
|
|
194903
|
-
summaryText: resultAfterJudge.completion.status === "incomplete" ? defaultSummaryText(resultAfterJudge) : judge.output.summaryText
|
|
194904
|
-
});
|
|
194905
|
-
} finally {
|
|
194906
|
-
await trace.finalize();
|
|
194907
|
-
if (snapshot)
|
|
194908
|
-
await cleanupSnapshot(snapshot.root);
|
|
195674
|
+
} catch (error51) {
|
|
195675
|
+
await reportReviewFailure(error51, activeTrace);
|
|
195676
|
+
throw error51;
|
|
194909
195677
|
}
|
|
194910
195678
|
}
|
|
195679
|
+
function durationMsBetween(startedAt, completedAt) {
|
|
195680
|
+
if (!startedAt || !completedAt)
|
|
195681
|
+
return 0;
|
|
195682
|
+
const start = Date.parse(startedAt);
|
|
195683
|
+
const end = Date.parse(completedAt);
|
|
195684
|
+
if (!Number.isFinite(start) || !Number.isFinite(end))
|
|
195685
|
+
return 0;
|
|
195686
|
+
return Math.max(0, end - start);
|
|
195687
|
+
}
|
|
194911
195688
|
async function runFindingVerification(input) {
|
|
194912
195689
|
const allowDemotionRequested = input.config.verification.allowDemotion;
|
|
194913
195690
|
const selection = selectVerificationTargets(input.findings, input.config.verification.maxFindings);
|
|
@@ -195069,6 +195846,7 @@ async function runFindingVerification(input) {
|
|
|
195069
195846
|
warnOutputBytes: input.budgetTracker.budget.effectiveWarnAgentOutputBytes,
|
|
195070
195847
|
maxOutputBytes: input.budgetTracker.budget.maxAgentOutputBytes,
|
|
195071
195848
|
networkMode: input.networkMode,
|
|
195849
|
+
signal: input.signal,
|
|
195072
195850
|
onStarted: (executionIdentity) => {
|
|
195073
195851
|
input.budgetTracker.markStarted(group.reservation, executionIdentity);
|
|
195074
195852
|
const event = buildAgentStartedEvent({
|
|
@@ -195086,6 +195864,8 @@ async function runFindingVerification(input) {
|
|
|
195086
195864
|
try {
|
|
195087
195865
|
results = await input.manager.runAll(agentInputs);
|
|
195088
195866
|
} catch (error51) {
|
|
195867
|
+
if (error51 instanceof KyosoCancellationError)
|
|
195868
|
+
throw error51;
|
|
195089
195869
|
for (const group of scheduledGroups) {
|
|
195090
195870
|
applyVerificationVerdicts(group.targets, group.verifier, undefined);
|
|
195091
195871
|
await finalizeModelCallResult({
|
|
@@ -195310,6 +196090,19 @@ async function recordSkippedJudgeCall(input, reason) {
|
|
|
195310
196090
|
async function runAgents(input) {
|
|
195311
196091
|
const agentRoles = resolveAgentRoles(input.config);
|
|
195312
196092
|
const enabledAgents = ["codex", "claude"].filter((agent) => input.config.agents[agent].enabled);
|
|
196093
|
+
const openRouter = input.config.agents.codex.openRouter;
|
|
196094
|
+
const hasOpenRouterRetryPolicy = Object.values(openRouter).some((value) => value !== undefined);
|
|
196095
|
+
if (enabledAgents.includes("codex") && input.config.agents.codex.provider === CODEX_OPENROUTER_PROVIDER && hasOpenRouterRetryPolicy) {
|
|
196096
|
+
await input.trace.write({
|
|
196097
|
+
type: "openrouter_retry_policy_resolved",
|
|
196098
|
+
traceId: input.traceId,
|
|
196099
|
+
streamIdleTimeoutMs: openRouter.streamIdleTimeoutMs,
|
|
196100
|
+
streamMaxRetries: openRouter.streamMaxRetries,
|
|
196101
|
+
requestMaxRetries: openRouter.requestMaxRetries,
|
|
196102
|
+
source: "kyoso_config",
|
|
196103
|
+
timestamp: new Date().toISOString()
|
|
196104
|
+
});
|
|
196105
|
+
}
|
|
195313
196106
|
if (enabledAgents.length === 0)
|
|
195314
196107
|
return [];
|
|
195315
196108
|
const reservationResult = input.budgetTracker.reserveMany(enabledAgents.map((agent) => ({ kind: "primary", agent })));
|
|
@@ -195395,6 +196188,8 @@ async function runAgents(input) {
|
|
|
195395
196188
|
const agentInputs = enabledAgents.map((agent) => {
|
|
195396
196189
|
const agentConfig = input.config.agents[agent];
|
|
195397
196190
|
const role = agentRoles[agent] ?? agentConfig.role;
|
|
196191
|
+
let emittedRetryProgressEvents = 0;
|
|
196192
|
+
let retryProgressLimitWarned = false;
|
|
195398
196193
|
const reservation = reservations.get(agent);
|
|
195399
196194
|
if (!reservation) {
|
|
195400
196195
|
throw new Error(`Missing primary budget reservation for ${agent}.`);
|
|
@@ -195415,15 +196210,27 @@ async function runAgents(input) {
|
|
|
195415
196210
|
warnOutputBytes: input.budgetTracker.budget.effectiveWarnAgentOutputBytes,
|
|
195416
196211
|
maxOutputBytes: input.budgetTracker.budget.maxAgentOutputBytes,
|
|
195417
196212
|
networkMode: input.networkMode,
|
|
196213
|
+
signal: input.signal,
|
|
196214
|
+
heartbeatMs: input.progressHeartbeatMs,
|
|
196215
|
+
...agent === "codex" && input.config.agents.codex.provider === CODEX_OPENROUTER_PROVIDER && openRouter.streamIdleTimeoutMs !== undefined ? { streamIdleTimeoutMs: openRouter.streamIdleTimeoutMs } : {},
|
|
195418
196216
|
onStarted: (executionIdentity) => {
|
|
195419
196217
|
input.budgetTracker.markStarted(reservation, executionIdentity);
|
|
195420
196218
|
if (!acceptingStartedEvents)
|
|
195421
196219
|
return Promise.resolve();
|
|
196220
|
+
const progressExecutionIdentity = input.budgetTracker.executionIdentity(reservation);
|
|
196221
|
+
input.progressDispatcher.emit({
|
|
196222
|
+
type: "agent_started",
|
|
196223
|
+
traceId: input.traceId,
|
|
196224
|
+
agent,
|
|
196225
|
+
role,
|
|
196226
|
+
...progressExecutionIdentity ? { executionIdentity: progressExecutionIdentity } : {},
|
|
196227
|
+
timestamp: new Date().toISOString()
|
|
196228
|
+
});
|
|
195422
196229
|
const event = buildAgentStartedEvent({
|
|
195423
196230
|
traceId: input.traceId,
|
|
195424
196231
|
agent,
|
|
195425
196232
|
role,
|
|
195426
|
-
executionIdentity:
|
|
196233
|
+
executionIdentity: progressExecutionIdentity
|
|
195427
196234
|
});
|
|
195428
196235
|
const write = (async () => {
|
|
195429
196236
|
try {
|
|
@@ -195434,6 +196241,33 @@ async function runAgents(input) {
|
|
|
195434
196241
|
})();
|
|
195435
196242
|
startedWrites.push(write);
|
|
195436
196243
|
return write;
|
|
196244
|
+
},
|
|
196245
|
+
onProgress: (event) => {
|
|
196246
|
+
if (!acceptingStartedEvents)
|
|
196247
|
+
return;
|
|
196248
|
+
input.progressDispatcher.emit({ ...event, traceId: input.traceId });
|
|
196249
|
+
if (event.type !== "agent_retrying")
|
|
196250
|
+
return;
|
|
196251
|
+
if (emittedRetryProgressEvents >= MAX_AGENT_RETRY_PROGRESS_EVENTS) {
|
|
196252
|
+
if (!retryProgressLimitWarned) {
|
|
196253
|
+
retryProgressLimitWarned = true;
|
|
196254
|
+
input.warnings.push(`AGENT_RETRY_PROGRESS_LIMIT: ${agent} emitted more than ${MAX_AGENT_RETRY_PROGRESS_EVENTS} retry progress events; later events were omitted from the audit trace.`);
|
|
196255
|
+
}
|
|
196256
|
+
return;
|
|
196257
|
+
}
|
|
196258
|
+
emittedRetryProgressEvents += 1;
|
|
196259
|
+
const write = (async () => {
|
|
196260
|
+
try {
|
|
196261
|
+
await input.trace.write({
|
|
196262
|
+
...event,
|
|
196263
|
+
traceId: input.traceId,
|
|
196264
|
+
type: "agent_retrying"
|
|
196265
|
+
});
|
|
196266
|
+
} catch {
|
|
196267
|
+
input.warnings.push("AUDIT_WRITE_FAILED: agent_retrying event could not be recorded.");
|
|
196268
|
+
}
|
|
196269
|
+
})();
|
|
196270
|
+
startedWrites.push(write);
|
|
195437
196271
|
}
|
|
195438
196272
|
};
|
|
195439
196273
|
});
|
|
@@ -195441,6 +196275,8 @@ async function runAgents(input) {
|
|
|
195441
196275
|
try {
|
|
195442
196276
|
results = await input.manager.runAll(agentInputs);
|
|
195443
196277
|
} catch (error51) {
|
|
196278
|
+
if (error51 instanceof KyosoCancellationError)
|
|
196279
|
+
throw error51;
|
|
195444
196280
|
const detail = sanitizeTextForDisplay(error51 instanceof Error ? error51.message : String(error51));
|
|
195445
196281
|
input.warnings.push(`Primary-agent execution failed: ${detail}`);
|
|
195446
196282
|
results = agentInputs.map((agentInput) => ({
|
|
@@ -195491,6 +196327,16 @@ async function runAgents(input) {
|
|
|
195491
196327
|
if (result.status !== "completed") {
|
|
195492
196328
|
input.budgetTracker.markIncomplete("coverage_incomplete");
|
|
195493
196329
|
}
|
|
196330
|
+
input.progressDispatcher.emit({
|
|
196331
|
+
type: "agent_completed",
|
|
196332
|
+
traceId: input.traceId,
|
|
196333
|
+
agent: result.agent,
|
|
196334
|
+
status: result.status,
|
|
196335
|
+
durationMs: durationMsBetween(result.startedAt, result.completedAt),
|
|
196336
|
+
...result.outputBytes === undefined ? {} : { outputBytes: result.outputBytes },
|
|
196337
|
+
...result.observedStreamRetries === undefined ? {} : { observedStreamRetries: result.observedStreamRetries },
|
|
196338
|
+
timestamp: new Date().toISOString()
|
|
196339
|
+
});
|
|
195494
196340
|
}
|
|
195495
196341
|
await Promise.all(normalizedResults.map((result) => {
|
|
195496
196342
|
const event = {
|
|
@@ -195581,6 +196427,12 @@ async function finalizeModelCallResult(input) {
|
|
|
195581
196427
|
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
195582
196428
|
...outputBytes === undefined ? {} : { outputBytes },
|
|
195583
196429
|
...outputWarningTriggered === undefined ? {} : { outputWarningTriggered },
|
|
196430
|
+
...input.result.observedStreamRetries === undefined ? {} : { observedStreamRetries: input.result.observedStreamRetries },
|
|
196431
|
+
...input.result.discardedRetryMessageBytes === undefined ? {} : {
|
|
196432
|
+
discardedRetryMessageBytes: input.result.discardedRetryMessageBytes
|
|
196433
|
+
},
|
|
196434
|
+
...input.result.firstOutputAt === undefined ? {} : { firstOutputAt: input.result.firstOutputAt },
|
|
196435
|
+
...input.result.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: input.result.lastAcpUpdateAt },
|
|
195584
196436
|
...input.result.salvaged === undefined ? {} : { salvaged: input.result.salvaged },
|
|
195585
196437
|
...input.result.reportedFindings === undefined ? {} : { reportedFindings: input.result.reportedFindings },
|
|
195586
196438
|
...input.result.findingsTargetExceeded === undefined ? {} : { findingsTargetExceeded: input.result.findingsTargetExceeded },
|
|
@@ -195619,6 +196471,12 @@ async function finalizeModelCallResult(input) {
|
|
|
195619
196471
|
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
195620
196472
|
...outputBytes === undefined ? {} : { outputBytes },
|
|
195621
196473
|
...outputWarningTriggered === undefined ? {} : { outputWarningTriggered },
|
|
196474
|
+
...input.result.observedStreamRetries === undefined ? {} : { observedStreamRetries: input.result.observedStreamRetries },
|
|
196475
|
+
...input.result.discardedRetryMessageBytes === undefined ? {} : {
|
|
196476
|
+
discardedRetryMessageBytes: input.result.discardedRetryMessageBytes
|
|
196477
|
+
},
|
|
196478
|
+
...input.result.firstOutputAt === undefined ? {} : { firstOutputAt: input.result.firstOutputAt },
|
|
196479
|
+
...input.result.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: input.result.lastAcpUpdateAt },
|
|
195622
196480
|
...input.result.salvaged === undefined ? {} : { salvaged: input.result.salvaged },
|
|
195623
196481
|
...input.result.reportedFindings === undefined ? {} : { reportedFindings: input.result.reportedFindings },
|
|
195624
196482
|
...input.result.findingsTargetExceeded === undefined ? {} : { findingsTargetExceeded: input.result.findingsTargetExceeded },
|
|
@@ -196042,5 +196900,6 @@ function resolveNetworkMode(requested, configDefault, mcpNetworkMode) {
|
|
|
196042
196900
|
}
|
|
196043
196901
|
export {
|
|
196044
196902
|
runReview,
|
|
196045
|
-
defineConfig
|
|
196903
|
+
defineConfig,
|
|
196904
|
+
KyosoCancellationError
|
|
196046
196905
|
};
|