@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/bin/kyoso.js
CHANGED
|
@@ -183896,6 +183896,19 @@ class KyosoRequestError extends Error {
|
|
|
183896
183896
|
}
|
|
183897
183897
|
}
|
|
183898
183898
|
|
|
183899
|
+
class KyosoCancellationError extends Error {
|
|
183900
|
+
code = "REQUEST_CANCELLED";
|
|
183901
|
+
constructor(message = "Kyoso review was cancelled.") {
|
|
183902
|
+
super(message);
|
|
183903
|
+
this.name = "KyosoCancellationError";
|
|
183904
|
+
}
|
|
183905
|
+
}
|
|
183906
|
+
function throwIfAborted(signal) {
|
|
183907
|
+
if (!signal?.aborted)
|
|
183908
|
+
return;
|
|
183909
|
+
throw new KyosoCancellationError(typeof signal.reason === "string" ? signal.reason : undefined);
|
|
183910
|
+
}
|
|
183911
|
+
|
|
183899
183912
|
// src/context/pathPolicy.ts
|
|
183900
183913
|
function normalizeRelativePath(path) {
|
|
183901
183914
|
const normalized = normalize(path).replaceAll("\\", "/");
|
|
@@ -183951,7 +183964,7 @@ var JUDGE_MAX_OUTPUT_TOKENS = 4096;
|
|
|
183951
183964
|
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
183952
183965
|
var TRACE_DIR = ".kyoso/traces";
|
|
183953
183966
|
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
183954
|
-
var KYOSO_VERSION = "0.
|
|
183967
|
+
var KYOSO_VERSION = "0.15.0";
|
|
183955
183968
|
|
|
183956
183969
|
// src/utils/pathContainment.ts
|
|
183957
183970
|
import { resolve, sep as sep2 } from "node:path";
|
|
@@ -184391,6 +184404,9 @@ var kyosoConfigOverridePaths = [
|
|
|
184391
184404
|
"agents.codex.enabled",
|
|
184392
184405
|
"agents.codex.model",
|
|
184393
184406
|
"agents.codex.provider",
|
|
184407
|
+
"agents.codex.openRouter.streamIdleTimeoutMs",
|
|
184408
|
+
"agents.codex.openRouter.streamMaxRetries",
|
|
184409
|
+
"agents.codex.openRouter.requestMaxRetries",
|
|
184394
184410
|
"agents.codex.effort",
|
|
184395
184411
|
"agents.codex.role",
|
|
184396
184412
|
"agents.codex.timeoutMs",
|
|
@@ -184714,6 +184730,7 @@ ${typeof file2.content === "string" ? file2.content.slice(0, 2000) : ""}`)
|
|
|
184714
184730
|
var CODEX_OPENROUTER_PROVIDER = "openrouter";
|
|
184715
184731
|
var CODEX_DEFAULT_PROVIDER = "default";
|
|
184716
184732
|
var CODEX_OPENROUTER_MODEL_REQUIRED_ISSUE = "codex_openrouter_model_required";
|
|
184733
|
+
var CODEX_OPENROUTER_POLICY_REQUIRES_PROVIDER_ISSUE = "codex_openrouter_policy_requires_provider";
|
|
184717
184734
|
var baseAgentSchema = exports_external.object({
|
|
184718
184735
|
enabled: exports_external.boolean().default(true),
|
|
184719
184736
|
type: exports_external.literal("acp").default("acp"),
|
|
@@ -184736,12 +184753,29 @@ var baseAgentSchema = exports_external.object({
|
|
|
184736
184753
|
envWhitelist: exports_external.array(exports_external.string())
|
|
184737
184754
|
})
|
|
184738
184755
|
});
|
|
184756
|
+
var codexOpenRouterSchema = exports_external.object({
|
|
184757
|
+
streamIdleTimeoutMs: exports_external.number().int().min(1000).optional(),
|
|
184758
|
+
streamMaxRetries: exports_external.number().int().min(0).max(100).optional(),
|
|
184759
|
+
requestMaxRetries: exports_external.number().int().min(0).max(100).optional()
|
|
184760
|
+
});
|
|
184739
184761
|
var codexAgentSchema = baseAgentSchema.extend({
|
|
184740
184762
|
provider: exports_external.enum([CODEX_OPENROUTER_PROVIDER, CODEX_DEFAULT_PROVIDER]).optional(),
|
|
184741
184763
|
allowProjectProvider: exports_external.array(exports_external.string().min(1).refine(isAbsolute2, {
|
|
184742
184764
|
message: "must contain only absolute project directory paths for exact matching"
|
|
184743
|
-
})).default([])
|
|
184765
|
+
})).default([]),
|
|
184766
|
+
openRouter: codexOpenRouterSchema.default({})
|
|
184744
184767
|
}).superRefine((agent, context) => {
|
|
184768
|
+
const hasOpenRouterPolicy = Object.values(agent.openRouter).some((value) => value !== undefined);
|
|
184769
|
+
if (hasOpenRouterPolicy && agent.provider !== CODEX_OPENROUTER_PROVIDER) {
|
|
184770
|
+
context.addIssue({
|
|
184771
|
+
code: exports_external.ZodIssueCode.custom,
|
|
184772
|
+
path: ["openRouter"],
|
|
184773
|
+
message: 'agents.codex.openRouter.* requires provider = "openrouter".',
|
|
184774
|
+
params: {
|
|
184775
|
+
kyosoIssue: CODEX_OPENROUTER_POLICY_REQUIRES_PROVIDER_ISSUE
|
|
184776
|
+
}
|
|
184777
|
+
});
|
|
184778
|
+
}
|
|
184745
184779
|
if (agent.provider !== CODEX_OPENROUTER_PROVIDER || (agent.model?.trim().length ?? 0) > 0) {
|
|
184746
184780
|
return;
|
|
184747
184781
|
}
|
|
@@ -184867,7 +184901,7 @@ function agentConfigLeafPaths(agent) {
|
|
|
184867
184901
|
`agents.${agent}.auth.envWhitelist`
|
|
184868
184902
|
];
|
|
184869
184903
|
if (agent === "codex") {
|
|
184870
|
-
paths.push("agents.codex.provider", "agents.codex.allowProjectProvider");
|
|
184904
|
+
paths.push("agents.codex.provider", "agents.codex.allowProjectProvider", "agents.codex.openRouter.streamIdleTimeoutMs", "agents.codex.openRouter.streamMaxRetries", "agents.codex.openRouter.requestMaxRetries");
|
|
184871
184905
|
}
|
|
184872
184906
|
return paths;
|
|
184873
184907
|
}
|
|
@@ -186172,7 +186206,7 @@ async function loadProjectConfig(input2) {
|
|
|
186172
186206
|
const extension = extname3(requestedPath);
|
|
186173
186207
|
if (extension === ".toml") {
|
|
186174
186208
|
const projectTomlConfig = await loadTomlConfigFile(canonicalPath);
|
|
186175
|
-
const
|
|
186209
|
+
const projectChangesOpenRouterPolicy = projectConfigChangesOpenRouterPolicy(projectTomlConfig, input2.baseConfig);
|
|
186176
186210
|
await assertProjectOpenRouterAuthorization({
|
|
186177
186211
|
projectConfig: projectTomlConfig,
|
|
186178
186212
|
projectPath: requestedPath,
|
|
@@ -186190,7 +186224,7 @@ async function loadProjectConfig(input2) {
|
|
|
186190
186224
|
configPath: requestedPath,
|
|
186191
186225
|
configTrustStatus: "not_found",
|
|
186192
186226
|
source: { path: requestedPath, layer: "project_toml" },
|
|
186193
|
-
warnings:
|
|
186227
|
+
warnings: projectChangesOpenRouterPolicy ? [openRouterProjectConfigWarning(requestedPath)] : []
|
|
186194
186228
|
};
|
|
186195
186229
|
}
|
|
186196
186230
|
if (extension === ".ts") {
|
|
@@ -186207,30 +186241,35 @@ function projectConfigSelectsDefaultProvider(config2) {
|
|
|
186207
186241
|
function projectConfigSuppliesCodexModel(config2) {
|
|
186208
186242
|
return flattenLeaves(config2).some((leaf) => leaf.path.join(".") === "agents.codex.model");
|
|
186209
186243
|
}
|
|
186210
|
-
function
|
|
186211
|
-
return
|
|
186244
|
+
function projectConfigSuppliesOpenRouterPolicy(config2) {
|
|
186245
|
+
return flattenLeaves(config2).some((leaf) => leaf.path.join(".").startsWith("agents.codex.openRouter."));
|
|
186246
|
+
}
|
|
186247
|
+
function projectConfigChangesOpenRouterPolicy(projectConfig, baseConfig) {
|
|
186248
|
+
return projectConfigSelectsOpenRouter(projectConfig) || configSelectsOpenRouter(baseConfig) && !projectConfigSelectsDefaultProvider(projectConfig) && (projectConfigSuppliesCodexModel(projectConfig) || projectConfigSuppliesOpenRouterPolicy(projectConfig));
|
|
186212
186249
|
}
|
|
186213
186250
|
function configSelectsOpenRouter(config2) {
|
|
186214
186251
|
return flattenLeaves(config2).some((leaf) => leaf.path.join(".") === "agents.codex.provider" && leaf.value === CODEX_OPENROUTER_PROVIDER);
|
|
186215
186252
|
}
|
|
186216
186253
|
function applyProjectCodexProviderReset(baseConfig, projectConfig, mergedConfig) {
|
|
186217
|
-
if (!projectConfigSelectsDefaultProvider(projectConfig) ||
|
|
186254
|
+
if (!projectConfigSelectsDefaultProvider(projectConfig) || !configSelectsOpenRouter(baseConfig) || !isRecord3(mergedConfig) || !isRecord3(mergedConfig.agents) || !isRecord3(mergedConfig.agents.codex)) {
|
|
186218
186255
|
return mergedConfig;
|
|
186219
186256
|
}
|
|
186220
|
-
const
|
|
186257
|
+
const shouldClearInheritedModel = !projectConfigSuppliesCodexModel(projectConfig);
|
|
186258
|
+
const shouldClearInheritedOpenRouterPolicy = !projectConfigSuppliesOpenRouterPolicy(projectConfig);
|
|
186259
|
+
const codexWithoutInheritedOpenRouterConfig = Object.fromEntries(Object.entries(mergedConfig.agents.codex).filter(([key]) => (!shouldClearInheritedModel || key !== "model") && (!shouldClearInheritedOpenRouterPolicy || key !== "openRouter")));
|
|
186221
186260
|
return {
|
|
186222
186261
|
...mergedConfig,
|
|
186223
186262
|
agents: {
|
|
186224
186263
|
...mergedConfig.agents,
|
|
186225
|
-
codex:
|
|
186264
|
+
codex: codexWithoutInheritedOpenRouterConfig
|
|
186226
186265
|
}
|
|
186227
186266
|
};
|
|
186228
186267
|
}
|
|
186229
186268
|
function openRouterProjectConfigWarning(configPath) {
|
|
186230
|
-
return `Project config ${sanitizeWarningText(configPath)} changes Codex OpenRouter routing under user-global authorization; it can route Codex review content through OpenRouter.`;
|
|
186269
|
+
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.`;
|
|
186231
186270
|
}
|
|
186232
186271
|
async function assertProjectOpenRouterAuthorization(input2) {
|
|
186233
|
-
if (!
|
|
186272
|
+
if (!projectConfigChangesOpenRouterPolicy(input2.projectConfig, input2.baseConfig)) {
|
|
186234
186273
|
return;
|
|
186235
186274
|
}
|
|
186236
186275
|
if (await projectProviderIsAuthorized(input2.baseConfig, input2.projectDirectory)) {
|
|
@@ -186295,7 +186334,7 @@ async function loadProjectTsConfig(input2) {
|
|
|
186295
186334
|
if (trustDecision.execute) {
|
|
186296
186335
|
const userConfig = await loadUserConfig(canonicalPath, source);
|
|
186297
186336
|
validateExplicitReviewBudgetThresholds(userConfig, input2.baseConfig);
|
|
186298
|
-
const
|
|
186337
|
+
const projectChangesOpenRouterPolicy = projectConfigChangesOpenRouterPolicy(userConfig, input2.baseConfig);
|
|
186299
186338
|
await assertProjectOpenRouterAuthorization({
|
|
186300
186339
|
projectConfig: userConfig,
|
|
186301
186340
|
projectPath: requestedPath,
|
|
@@ -186308,7 +186347,7 @@ async function loadProjectTsConfig(input2) {
|
|
|
186308
186347
|
await trustConfig(trustStorePath, canonicalPath, configHash);
|
|
186309
186348
|
}
|
|
186310
186349
|
const projectMergedConfig = deepMerge2(input2.baseConfig, userConfig);
|
|
186311
|
-
if (
|
|
186350
|
+
if (projectChangesOpenRouterPolicy) {
|
|
186312
186351
|
warnings.push(openRouterProjectConfigWarning(requestedPath));
|
|
186313
186352
|
}
|
|
186314
186353
|
return {
|
|
@@ -186627,6 +186666,27 @@ function isRecord6(value) {
|
|
|
186627
186666
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186628
186667
|
}
|
|
186629
186668
|
|
|
186669
|
+
// src/judge/signals.ts
|
|
186670
|
+
function linkSignals(timeoutMs, external2) {
|
|
186671
|
+
const controller = new AbortController;
|
|
186672
|
+
const timeout = setTimeout(() => controller.abort(new Error("judge timeout")), timeoutMs);
|
|
186673
|
+
timeout.unref?.();
|
|
186674
|
+
const onExternalAbort = () => controller.abort(external2?.reason);
|
|
186675
|
+
if (external2) {
|
|
186676
|
+
if (external2.aborted)
|
|
186677
|
+
onExternalAbort();
|
|
186678
|
+
else
|
|
186679
|
+
external2.addEventListener("abort", onExternalAbort, { once: true });
|
|
186680
|
+
}
|
|
186681
|
+
return {
|
|
186682
|
+
signal: controller.signal,
|
|
186683
|
+
cleanup() {
|
|
186684
|
+
clearTimeout(timeout);
|
|
186685
|
+
external2?.removeEventListener("abort", onExternalAbort);
|
|
186686
|
+
}
|
|
186687
|
+
};
|
|
186688
|
+
}
|
|
186689
|
+
|
|
186630
186690
|
// src/judge/anthropic.ts
|
|
186631
186691
|
var DEFAULT_ANTHROPIC_JUDGE_MODEL = "claude-haiku-4-5";
|
|
186632
186692
|
function resolveAnthropicJudgeModel(env) {
|
|
@@ -186637,39 +186697,45 @@ async function runAnthropicJudge(input2, timeoutMs) {
|
|
|
186637
186697
|
if (!apiKey)
|
|
186638
186698
|
throw new Error("ANTHROPIC_API_KEY is not configured.");
|
|
186639
186699
|
const requestedModel = resolveAnthropicJudgeModel(input2.env);
|
|
186640
|
-
const
|
|
186641
|
-
|
|
186642
|
-
|
|
186643
|
-
"
|
|
186644
|
-
|
|
186645
|
-
|
|
186646
|
-
|
|
186647
|
-
|
|
186648
|
-
|
|
186649
|
-
|
|
186650
|
-
|
|
186651
|
-
|
|
186652
|
-
|
|
186653
|
-
|
|
186654
|
-
|
|
186655
|
-
|
|
186656
|
-
|
|
186657
|
-
|
|
186658
|
-
|
|
186659
|
-
|
|
186660
|
-
|
|
186661
|
-
|
|
186662
|
-
|
|
186663
|
-
|
|
186664
|
-
|
|
186665
|
-
|
|
186666
|
-
|
|
186667
|
-
|
|
186668
|
-
|
|
186669
|
-
|
|
186670
|
-
|
|
186671
|
-
|
|
186672
|
-
|
|
186700
|
+
const { signal, cleanup } = linkSignals(timeoutMs, input2.signal);
|
|
186701
|
+
try {
|
|
186702
|
+
const response = await fetch(`${input2.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com"}/v1/messages`, {
|
|
186703
|
+
method: "POST",
|
|
186704
|
+
headers: {
|
|
186705
|
+
"x-api-key": apiKey,
|
|
186706
|
+
"anthropic-version": "2023-06-01",
|
|
186707
|
+
"content-type": "application/json"
|
|
186708
|
+
},
|
|
186709
|
+
body: JSON.stringify({
|
|
186710
|
+
model: requestedModel,
|
|
186711
|
+
max_tokens: JUDGE_MAX_OUTPUT_TOKENS,
|
|
186712
|
+
temperature: 0,
|
|
186713
|
+
messages: [
|
|
186714
|
+
{
|
|
186715
|
+
role: "user",
|
|
186716
|
+
content: buildJudgePrompt(input2.tool, input2.result, input2.summaryText, input2.agentFindings)
|
|
186717
|
+
}
|
|
186718
|
+
]
|
|
186719
|
+
}),
|
|
186720
|
+
signal
|
|
186721
|
+
});
|
|
186722
|
+
if (!response.ok)
|
|
186723
|
+
throw new Error(`Anthropic judge failed with HTTP ${response.status}.`);
|
|
186724
|
+
const payload = await response.json();
|
|
186725
|
+
const content = payload.content?.find((item) => item.type === "text" && item.text)?.text;
|
|
186726
|
+
if (!content)
|
|
186727
|
+
throw new Error("Anthropic judge response did not include text content.");
|
|
186728
|
+
const usage = normalizeUsage(payload.usage);
|
|
186729
|
+
const reportedModel = typeof payload.model === "string" ? payload.model : undefined;
|
|
186730
|
+
return {
|
|
186731
|
+
output: parseJudgeOutput(content, input2.summaryText),
|
|
186732
|
+
requestedModel,
|
|
186733
|
+
...reportedModel ? { reportedModel } : {},
|
|
186734
|
+
...usage ? { usage } : {}
|
|
186735
|
+
};
|
|
186736
|
+
} finally {
|
|
186737
|
+
cleanup();
|
|
186738
|
+
}
|
|
186673
186739
|
}
|
|
186674
186740
|
function normalizeUsage(usage) {
|
|
186675
186741
|
if (!usage)
|
|
@@ -186681,16 +186747,6 @@ function normalizeUsage(usage) {
|
|
|
186681
186747
|
cachedWriteTokens: usage.cache_creation_input_tokens
|
|
186682
186748
|
});
|
|
186683
186749
|
}
|
|
186684
|
-
async function fetchWithTimeout(url2, init, timeoutMs) {
|
|
186685
|
-
const controller = new AbortController;
|
|
186686
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
186687
|
-
timeout.unref?.();
|
|
186688
|
-
try {
|
|
186689
|
-
return await fetch(url2, { ...init, signal: controller.signal });
|
|
186690
|
-
} finally {
|
|
186691
|
-
clearTimeout(timeout);
|
|
186692
|
-
}
|
|
186693
|
-
}
|
|
186694
186750
|
|
|
186695
186751
|
// src/judge/deterministicFallback.ts
|
|
186696
186752
|
function runDeterministicJudge(result, summaryText) {
|
|
@@ -186713,39 +186769,45 @@ async function runOpenAiJudge(input2, timeoutMs) {
|
|
|
186713
186769
|
if (!apiKey)
|
|
186714
186770
|
throw new Error("OPENAI_API_KEY is not configured.");
|
|
186715
186771
|
const requestedModel = resolveOpenAiJudgeModel(input2.env);
|
|
186716
|
-
const
|
|
186717
|
-
|
|
186718
|
-
|
|
186719
|
-
|
|
186720
|
-
|
|
186721
|
-
|
|
186722
|
-
|
|
186723
|
-
|
|
186724
|
-
|
|
186725
|
-
|
|
186726
|
-
{
|
|
186727
|
-
|
|
186728
|
-
|
|
186729
|
-
|
|
186730
|
-
|
|
186731
|
-
|
|
186732
|
-
|
|
186733
|
-
|
|
186734
|
-
|
|
186735
|
-
|
|
186736
|
-
|
|
186737
|
-
|
|
186738
|
-
|
|
186739
|
-
|
|
186740
|
-
|
|
186741
|
-
|
|
186742
|
-
|
|
186743
|
-
|
|
186744
|
-
|
|
186745
|
-
|
|
186746
|
-
|
|
186747
|
-
|
|
186748
|
-
|
|
186772
|
+
const { signal, cleanup } = linkSignals(timeoutMs, input2.signal);
|
|
186773
|
+
try {
|
|
186774
|
+
const response = await fetch(`${input2.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1"}/chat/completions`, {
|
|
186775
|
+
method: "POST",
|
|
186776
|
+
headers: {
|
|
186777
|
+
authorization: `Bearer ${apiKey}`,
|
|
186778
|
+
"content-type": "application/json"
|
|
186779
|
+
},
|
|
186780
|
+
body: JSON.stringify({
|
|
186781
|
+
model: requestedModel,
|
|
186782
|
+
response_format: { type: "json_object" },
|
|
186783
|
+
messages: [
|
|
186784
|
+
{
|
|
186785
|
+
role: "user",
|
|
186786
|
+
content: buildJudgePrompt(input2.tool, input2.result, input2.summaryText, input2.agentFindings)
|
|
186787
|
+
}
|
|
186788
|
+
],
|
|
186789
|
+
max_completion_tokens: JUDGE_MAX_OUTPUT_TOKENS,
|
|
186790
|
+
temperature: 0
|
|
186791
|
+
}),
|
|
186792
|
+
signal
|
|
186793
|
+
});
|
|
186794
|
+
if (!response.ok)
|
|
186795
|
+
throw new Error(`OpenAI judge failed with HTTP ${response.status}.`);
|
|
186796
|
+
const payload = await response.json();
|
|
186797
|
+
const content = payload.choices?.[0]?.message?.content;
|
|
186798
|
+
if (!content)
|
|
186799
|
+
throw new Error("OpenAI judge response did not include content.");
|
|
186800
|
+
const usage = normalizeUsage2(payload.usage);
|
|
186801
|
+
const reportedModel = typeof payload.model === "string" ? payload.model : undefined;
|
|
186802
|
+
return {
|
|
186803
|
+
output: parseJudgeOutput(content, input2.summaryText),
|
|
186804
|
+
requestedModel,
|
|
186805
|
+
...reportedModel ? { reportedModel } : {},
|
|
186806
|
+
...usage ? { usage } : {}
|
|
186807
|
+
};
|
|
186808
|
+
} finally {
|
|
186809
|
+
cleanup();
|
|
186810
|
+
}
|
|
186749
186811
|
}
|
|
186750
186812
|
function normalizeUsage2(usage) {
|
|
186751
186813
|
if (!usage)
|
|
@@ -186758,16 +186820,6 @@ function normalizeUsage2(usage) {
|
|
|
186758
186820
|
thoughtTokens: usage.completion_tokens_details?.reasoning_tokens
|
|
186759
186821
|
});
|
|
186760
186822
|
}
|
|
186761
|
-
async function fetchWithTimeout2(url2, init, timeoutMs) {
|
|
186762
|
-
const controller = new AbortController;
|
|
186763
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
186764
|
-
timeout.unref?.();
|
|
186765
|
-
try {
|
|
186766
|
-
return await fetch(url2, { ...init, signal: controller.signal });
|
|
186767
|
-
} finally {
|
|
186768
|
-
clearTimeout(timeout);
|
|
186769
|
-
}
|
|
186770
|
-
}
|
|
186771
186823
|
|
|
186772
186824
|
// src/judge/provider.ts
|
|
186773
186825
|
function resolveJudgeProvider(provider, env) {
|
|
@@ -186821,6 +186873,9 @@ async function runJudge(input2) {
|
|
|
186821
186873
|
...output2.usage ? { usage: output2.usage } : {}
|
|
186822
186874
|
};
|
|
186823
186875
|
} catch (error51) {
|
|
186876
|
+
if (input2.signal?.aborted) {
|
|
186877
|
+
throw new KyosoCancellationError("Kyoso review was cancelled during judge execution.");
|
|
186878
|
+
}
|
|
186824
186879
|
return {
|
|
186825
186880
|
provider,
|
|
186826
186881
|
status: "failed_fallback",
|
|
@@ -186867,13 +186922,18 @@ var OPENROUTER_EXCLUDED_CREDENTIAL_ENV_KEYS = [
|
|
|
186867
186922
|
var OPENROUTER_API_KEY_ENV = "OPENROUTER_API_KEY";
|
|
186868
186923
|
var KYOSO_OPENROUTER_PROVIDER_ID = "kyoso-openrouter";
|
|
186869
186924
|
var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
186870
|
-
|
|
186871
|
-
|
|
186872
|
-
|
|
186873
|
-
|
|
186874
|
-
|
|
186875
|
-
|
|
186876
|
-
|
|
186925
|
+
function buildOpenRouterProviderPreset(options, baseUrl = OPENROUTER_BASE_URL) {
|
|
186926
|
+
return {
|
|
186927
|
+
name: "OpenRouter",
|
|
186928
|
+
base_url: baseUrl,
|
|
186929
|
+
env_key: OPENROUTER_API_KEY_ENV,
|
|
186930
|
+
wire_api: "responses",
|
|
186931
|
+
requires_openai_auth: false,
|
|
186932
|
+
...options.streamIdleTimeoutMs === undefined ? {} : { stream_idle_timeout_ms: options.streamIdleTimeoutMs },
|
|
186933
|
+
...options.streamMaxRetries === undefined ? {} : { stream_max_retries: options.streamMaxRetries },
|
|
186934
|
+
...options.requestMaxRetries === undefined ? {} : { request_max_retries: options.requestMaxRetries }
|
|
186935
|
+
};
|
|
186936
|
+
}
|
|
186877
186937
|
|
|
186878
186938
|
class ChildEnvPreflightError extends Error {
|
|
186879
186939
|
code;
|
|
@@ -186932,7 +186992,7 @@ function buildChildEnvironment(parentEnv, whitelist, explicit, options = {}) {
|
|
|
186932
186992
|
}
|
|
186933
186993
|
if (openRouterSelected) {
|
|
186934
186994
|
discardOpenRouterExcludedCredentials(env);
|
|
186935
|
-
applyOpenRouterConfig(env, parentEnv, options.model, onCredentialPlaceholderDiscarded, options.onOpenRouterProvidersDiscarded ?? warnOpenRouterProvidersDiscarded);
|
|
186995
|
+
applyOpenRouterConfig(env, parentEnv, options.model, options.openRouter, options.openRouterBaseUrlForTest, onCredentialPlaceholderDiscarded, options.onOpenRouterProvidersDiscarded ?? warnOpenRouterProvidersDiscarded);
|
|
186936
186996
|
} else {
|
|
186937
186997
|
applyModelConfig(env, options.agent, options.model);
|
|
186938
186998
|
}
|
|
@@ -186989,7 +187049,7 @@ function applyModelConfig(env, agent, model) {
|
|
|
186989
187049
|
env.CODEX_CONFIG = JSON.stringify({ model });
|
|
186990
187050
|
}
|
|
186991
187051
|
}
|
|
186992
|
-
function applyOpenRouterConfig(env, parentEnv, model, onCredentialPlaceholderDiscarded, onOpenRouterProvidersDiscarded) {
|
|
187052
|
+
function applyOpenRouterConfig(env, parentEnv, model, openRouter = {}, baseUrl = OPENROUTER_BASE_URL, onCredentialPlaceholderDiscarded, onOpenRouterProvidersDiscarded) {
|
|
186993
187053
|
const configuredModel = model?.trim();
|
|
186994
187054
|
if (!configuredModel) {
|
|
186995
187055
|
throw new ChildEnvPreflightError("AGENT_CONFIG_INVALID", 'agents.codex.provider="openrouter" requires a non-empty agents.codex.model.');
|
|
@@ -187017,7 +187077,7 @@ function applyOpenRouterConfig(env, parentEnv, model, onCredentialPlaceholderDis
|
|
|
187017
187077
|
model: configuredModel,
|
|
187018
187078
|
model_provider: KYOSO_OPENROUTER_PROVIDER_ID,
|
|
187019
187079
|
model_providers: {
|
|
187020
|
-
[KYOSO_OPENROUTER_PROVIDER_ID]:
|
|
187080
|
+
[KYOSO_OPENROUTER_PROVIDER_ID]: buildOpenRouterProviderPreset(openRouter, baseUrl)
|
|
187021
187081
|
}
|
|
187022
187082
|
});
|
|
187023
187083
|
}
|
|
@@ -187096,9 +187156,9 @@ var PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION = 2;
|
|
|
187096
187156
|
var MINIMUM_SUPPORTED_CODEX_VERSION = "0.144.0-alpha.4";
|
|
187097
187157
|
var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
187098
187158
|
distribution: {
|
|
187099
|
-
pluginVersion: "0.7.
|
|
187159
|
+
pluginVersion: "0.7.3",
|
|
187100
187160
|
mcpCommand: "npx",
|
|
187101
|
-
mcpPackagePin: "@kyo-so/cli@0.
|
|
187161
|
+
mcpPackagePin: "@kyo-so/cli@0.14.0",
|
|
187102
187162
|
mcpExecutable: "kyoso"
|
|
187103
187163
|
},
|
|
187104
187164
|
marketplace: {
|
|
@@ -190398,6 +190458,18 @@ async function runDoctor(options) {
|
|
|
190398
190458
|
if (agent === "codex" && loaded.config.agents.codex.provider === CODEX_OPENROUTER_PROVIDER) {
|
|
190399
190459
|
lines.push(` provider: ${CODEX_OPENROUTER_PROVIDER}`);
|
|
190400
190460
|
lines.push(` model: ${sanitizeTextForDisplay(loaded.config.agents.codex.model ?? "")}`);
|
|
190461
|
+
const openRouter = loaded.config.agents.codex.openRouter;
|
|
190462
|
+
lines.push(" reliability:");
|
|
190463
|
+
lines.push(openRouter.streamIdleTimeoutMs === undefined ? " stream idle timeout: inherited from Codex runtime" : ` stream idle timeout: ${openRouter.streamIdleTimeoutMs} ms (Kyoso config)`);
|
|
190464
|
+
lines.push(openRouter.streamMaxRetries === undefined ? " stream retries: inherited from Codex runtime" : ` stream retries: ${openRouter.streamMaxRetries}`);
|
|
190465
|
+
lines.push(openRouter.requestMaxRetries === undefined ? " request retries: inherited from Codex runtime" : ` request retries: ${openRouter.requestMaxRetries}`);
|
|
190466
|
+
if (openRouter.streamIdleTimeoutMs !== undefined && openRouter.streamMaxRetries !== undefined) {
|
|
190467
|
+
const idleOnlyWindow = openRouter.streamIdleTimeoutMs * (openRouter.streamMaxRetries + 1);
|
|
190468
|
+
lines.push(` maximum idle-only stream window: approximately ${idleOnlyWindow} ms plus backoff`);
|
|
190469
|
+
if (idleOnlyWindow >= config2.timeoutMs) {
|
|
190470
|
+
lines.push(` warning: configured idle-only retry window can consume the entire Codex agent timeout (timeoutMs=${config2.timeoutMs}).`);
|
|
190471
|
+
}
|
|
190472
|
+
}
|
|
190401
190473
|
const configuredKey = loaded.config.agents.codex.env[OPENROUTER_API_KEY_ENV];
|
|
190402
190474
|
if (hasUsableEnvValue(loaded.config.agents.codex.env, OPENROUTER_API_KEY_ENV)) {
|
|
190403
190475
|
lines.push(` auth: detected ${OPENROUTER_API_KEY_ENV} from agents.codex.env`);
|
|
@@ -194888,6 +194960,117 @@ function limitAcpNdJsonLineBytes(input2, maxLineBytes = MAX_ACP_NDJSON_LINE_BYTE
|
|
|
194888
194960
|
}));
|
|
194889
194961
|
}
|
|
194890
194962
|
|
|
194963
|
+
// src/acp/AgentOutputAccumulator.ts
|
|
194964
|
+
class AgentOutputAccumulator {
|
|
194965
|
+
segments = new Map;
|
|
194966
|
+
messageChunks = [];
|
|
194967
|
+
retryEpoch = 0;
|
|
194968
|
+
observedStreamRetries = 0;
|
|
194969
|
+
discardedRetryMessageBytes = 0;
|
|
194970
|
+
firstOutputAt;
|
|
194971
|
+
lastAcpUpdateAt;
|
|
194972
|
+
nextChunkSequence = 0;
|
|
194973
|
+
addMessageChunk(text, meta3) {
|
|
194974
|
+
this.noteOutput();
|
|
194975
|
+
const id = meta3.messageId ?? `epoch-${this.retryEpoch}`;
|
|
194976
|
+
const key = `${this.retryEpoch}\x00${id}`;
|
|
194977
|
+
const phase = meta3.phase ?? "unknown";
|
|
194978
|
+
let segment = this.segments.get(key);
|
|
194979
|
+
if (!segment) {
|
|
194980
|
+
segment = {
|
|
194981
|
+
id,
|
|
194982
|
+
phase,
|
|
194983
|
+
retryEpoch: this.retryEpoch,
|
|
194984
|
+
text: "",
|
|
194985
|
+
abandoned: false,
|
|
194986
|
+
lastChunkSequence: 0
|
|
194987
|
+
};
|
|
194988
|
+
this.segments.set(key, segment);
|
|
194989
|
+
} else if (segment.phase === "unknown" && phase !== "unknown") {
|
|
194990
|
+
segment.phase = phase;
|
|
194991
|
+
}
|
|
194992
|
+
const sequence = this.nextChunkSequence;
|
|
194993
|
+
this.nextChunkSequence += 1;
|
|
194994
|
+
segment.text += text;
|
|
194995
|
+
segment.lastChunkSequence = sequence;
|
|
194996
|
+
this.messageChunks.push({ segment, text, sequence });
|
|
194997
|
+
}
|
|
194998
|
+
addThoughtChunk(_text) {
|
|
194999
|
+
this.noteOutput();
|
|
195000
|
+
}
|
|
195001
|
+
noteUpdate() {
|
|
195002
|
+
this.lastAcpUpdateAt = new Date().toISOString();
|
|
195003
|
+
}
|
|
195004
|
+
markRetryBoundary() {
|
|
195005
|
+
let discardedMessageBytes = 0;
|
|
195006
|
+
for (const segment of this.segments.values()) {
|
|
195007
|
+
if (segment.retryEpoch !== this.retryEpoch || segment.abandoned)
|
|
195008
|
+
continue;
|
|
195009
|
+
segment.abandoned = true;
|
|
195010
|
+
discardedMessageBytes += Buffer.byteLength(segment.text, "utf8");
|
|
195011
|
+
}
|
|
195012
|
+
this.observedStreamRetries += 1;
|
|
195013
|
+
this.discardedRetryMessageBytes += discardedMessageBytes;
|
|
195014
|
+
this.retryEpoch += 1;
|
|
195015
|
+
return { discardedMessageBytes };
|
|
195016
|
+
}
|
|
195017
|
+
finalRawText() {
|
|
195018
|
+
if (this.observedStreamRetries === 0) {
|
|
195019
|
+
return this.messageChunks.map((chunk) => chunk.text).join("");
|
|
195020
|
+
}
|
|
195021
|
+
const finalAnswer = [...this.segments.values()].filter((segment) => !segment.abandoned && segment.phase === "final_answer").sort((left, right) => left.lastChunkSequence - right.lastChunkSequence).at(-1);
|
|
195022
|
+
if (finalAnswer)
|
|
195023
|
+
return finalAnswer.text;
|
|
195024
|
+
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("");
|
|
195025
|
+
}
|
|
195026
|
+
metrics() {
|
|
195027
|
+
return {
|
|
195028
|
+
observedStreamRetries: this.observedStreamRetries,
|
|
195029
|
+
discardedRetryMessageBytes: this.discardedRetryMessageBytes,
|
|
195030
|
+
...this.firstOutputAt === undefined ? {} : { firstOutputAt: this.firstOutputAt },
|
|
195031
|
+
...this.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: this.lastAcpUpdateAt }
|
|
195032
|
+
};
|
|
195033
|
+
}
|
|
195034
|
+
noteOutput() {
|
|
195035
|
+
const timestamp = new Date().toISOString();
|
|
195036
|
+
this.firstOutputAt ??= timestamp;
|
|
195037
|
+
this.lastAcpUpdateAt = timestamp;
|
|
195038
|
+
}
|
|
195039
|
+
}
|
|
195040
|
+
|
|
195041
|
+
// src/acp/codexRetryUpdate.ts
|
|
195042
|
+
var RETRY_ATTEMPT_PATTERN = /Reconnecting\.\.\.\s*(\d+)\/(\d+)/;
|
|
195043
|
+
var DISPLAY_MESSAGE_FIELDS = ["title", "text", "message", "description"];
|
|
195044
|
+
function parseCodexRetryUpdate(update) {
|
|
195045
|
+
if (!isRecord12(update) || update.sessionUpdate !== "session_info_update") {
|
|
195046
|
+
return;
|
|
195047
|
+
}
|
|
195048
|
+
const meta3 = isRecord12(update._meta) ? update._meta : undefined;
|
|
195049
|
+
const codex = meta3 && isRecord12(meta3.codex) ? meta3.codex : undefined;
|
|
195050
|
+
const error51 = codex && isRecord12(codex.error) ? codex.error : undefined;
|
|
195051
|
+
if (error51?.willRetry !== true)
|
|
195052
|
+
return;
|
|
195053
|
+
const rawMessage = typeof error51.message === "string" ? error51.message : findDisplayMessage(update) ?? "model stream retry";
|
|
195054
|
+
const message = sanitizeTextForDisplay(rawMessage) || "model stream retry";
|
|
195055
|
+
const attemptMatch = RETRY_ATTEMPT_PATTERN.exec(message);
|
|
195056
|
+
return {
|
|
195057
|
+
message,
|
|
195058
|
+
...attemptMatch?.[1] === undefined ? {} : { attempt: Number.parseInt(attemptMatch[1], 10) },
|
|
195059
|
+
...attemptMatch?.[2] === undefined ? {} : { maxRetries: Number.parseInt(attemptMatch[2], 10) }
|
|
195060
|
+
};
|
|
195061
|
+
}
|
|
195062
|
+
function findDisplayMessage(update) {
|
|
195063
|
+
for (const field of DISPLAY_MESSAGE_FIELDS) {
|
|
195064
|
+
const value = update[field];
|
|
195065
|
+
if (typeof value === "string")
|
|
195066
|
+
return value;
|
|
195067
|
+
}
|
|
195068
|
+
return;
|
|
195069
|
+
}
|
|
195070
|
+
function isRecord12(value) {
|
|
195071
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
195072
|
+
}
|
|
195073
|
+
|
|
194891
195074
|
// src/core/findingAdmission.ts
|
|
194892
195075
|
import { createHash as createHash4 } from "node:crypto";
|
|
194893
195076
|
var SAFETY_CATEGORIES = new Set([
|
|
@@ -195369,7 +195552,7 @@ function isSeverity(value) {
|
|
|
195369
195552
|
return typeof value === "string" && severities.includes(value);
|
|
195370
195553
|
}
|
|
195371
195554
|
function normalizeCisaSecureByDesign(value) {
|
|
195372
|
-
if (!
|
|
195555
|
+
if (!isRecord13(value))
|
|
195373
195556
|
return;
|
|
195374
195557
|
const normalized = {};
|
|
195375
195558
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -195410,7 +195593,7 @@ function isEvidenceQuality(value) {
|
|
|
195410
195593
|
return typeof value === "string" && evidenceQualities.includes(value);
|
|
195411
195594
|
}
|
|
195412
195595
|
function isStrictAgentOpinion(value) {
|
|
195413
|
-
if (!
|
|
195596
|
+
if (!isRecord13(value) || !hasOnlyKeys(value, STRICT_ROOT_KEYS))
|
|
195414
195597
|
return false;
|
|
195415
195598
|
if (typeof value.summary !== "string")
|
|
195416
195599
|
return false;
|
|
@@ -195420,7 +195603,7 @@ function isStrictAgentOpinion(value) {
|
|
|
195420
195603
|
return value.cisaSecureByDesign === undefined || isStrictCisaSecureByDesign(value.cisaSecureByDesign);
|
|
195421
195604
|
}
|
|
195422
195605
|
function isStrictFinding(value) {
|
|
195423
|
-
if (!
|
|
195606
|
+
if (!isRecord13(value) || !hasOnlyKeys(value, STRICT_FINDING_KEYS)) {
|
|
195424
195607
|
return false;
|
|
195425
195608
|
}
|
|
195426
195609
|
if (!isSeverity(value.severity) || !isCategory(value.category) || !isNonEmptyString(value.title) || !isNonEmptyString(value.evidence) || !isNonEmptyString(value.recommendation) || !isConfidence(value.confidence)) {
|
|
@@ -195432,11 +195615,11 @@ function isStrictFinding(value) {
|
|
|
195432
195615
|
return true;
|
|
195433
195616
|
}
|
|
195434
195617
|
function isStrictFindingFiles(value) {
|
|
195435
|
-
return Array.isArray(value) && value.every((item) =>
|
|
195618
|
+
return Array.isArray(value) && value.every((item) => isRecord13(item) && hasOnlyKeys(item, STRICT_FILE_KEYS) && isNonEmptyString(item.path) && isOptionalLineNumber(item.lineStart) && isOptionalLineNumber(item.lineEnd));
|
|
195436
195619
|
}
|
|
195437
195620
|
function isStrictEvidenceRefs(value) {
|
|
195438
195621
|
return Array.isArray(value) && value.length <= MAX_EVIDENCE_REFS2 && value.every((item) => {
|
|
195439
|
-
if (!
|
|
195622
|
+
if (!isRecord13(item) || !hasOnlyKeys(item, STRICT_EVIDENCE_REF_KEYS)) {
|
|
195440
195623
|
return false;
|
|
195441
195624
|
}
|
|
195442
195625
|
if (item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
@@ -195452,7 +195635,7 @@ function isStrictEvidenceRefs(value) {
|
|
|
195452
195635
|
});
|
|
195453
195636
|
}
|
|
195454
195637
|
function isStrictCisaSecureByDesign(value) {
|
|
195455
|
-
if (!
|
|
195638
|
+
if (!isRecord13(value) || !hasOnlyKeys(value, STRICT_CISA_KEYS))
|
|
195456
195639
|
return false;
|
|
195457
195640
|
for (const key of [
|
|
195458
195641
|
"customerSecurityOutcomes",
|
|
@@ -195491,7 +195674,7 @@ function normalizeFindingFiles(value) {
|
|
|
195491
195674
|
if (!Array.isArray(value))
|
|
195492
195675
|
return;
|
|
195493
195676
|
const files = value.flatMap((item) => {
|
|
195494
|
-
if (!
|
|
195677
|
+
if (!isRecord13(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
195495
195678
|
return [];
|
|
195496
195679
|
}
|
|
195497
195680
|
const file2 = {
|
|
@@ -195511,7 +195694,7 @@ function normalizeEvidenceRefs2(value) {
|
|
|
195511
195694
|
if (!Array.isArray(value))
|
|
195512
195695
|
return;
|
|
195513
195696
|
const references = value.slice(0, MAX_EVIDENCE_REFS2).flatMap((item) => {
|
|
195514
|
-
if (!
|
|
195697
|
+
if (!isRecord13(item) || item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
195515
195698
|
return [];
|
|
195516
195699
|
}
|
|
195517
195700
|
const reference = { kind: item.kind };
|
|
@@ -195534,18 +195717,23 @@ function normalizeEvidenceRefs2(value) {
|
|
|
195534
195717
|
function normalizeLineNumber(value) {
|
|
195535
195718
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= MAX_EVIDENCE_LINE2 ? value : undefined;
|
|
195536
195719
|
}
|
|
195537
|
-
function
|
|
195720
|
+
function isRecord13(value) {
|
|
195538
195721
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
195539
195722
|
}
|
|
195540
195723
|
|
|
195541
195724
|
// src/acp/AcpAgentProcess.ts
|
|
195725
|
+
var DEFAULT_PROGRESS_HEARTBEAT_MS = 15000;
|
|
195726
|
+
var ACTIVITY_PROGRESS_THROTTLE_MS = 3000;
|
|
195727
|
+
|
|
195542
195728
|
class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
195543
195729
|
config;
|
|
195544
195730
|
parentEnv;
|
|
195545
|
-
|
|
195731
|
+
internalOptions;
|
|
195732
|
+
constructor(config2, parentEnv = process.env, internalOptions = {}) {
|
|
195546
195733
|
super();
|
|
195547
195734
|
this.config = config2;
|
|
195548
195735
|
this.parentEnv = parentEnv;
|
|
195736
|
+
this.internalOptions = internalOptions;
|
|
195549
195737
|
}
|
|
195550
195738
|
async runAgent(input2) {
|
|
195551
195739
|
const agentConfig = this.config.agents[input2.agent];
|
|
@@ -195566,7 +195754,9 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
195566
195754
|
agent: input2.agent,
|
|
195567
195755
|
model: agentConfig.model,
|
|
195568
195756
|
provider,
|
|
195569
|
-
preferApiKey: agentConfig.auth.preferApiKey
|
|
195757
|
+
preferApiKey: agentConfig.auth.preferApiKey,
|
|
195758
|
+
openRouter: input2.agent === "codex" ? this.config.agents.codex.openRouter : undefined,
|
|
195759
|
+
openRouterBaseUrlForTest: input2.agent === "codex" ? this.internalOptions.openRouterBaseUrlForTest : undefined
|
|
195570
195760
|
});
|
|
195571
195761
|
} catch (error51) {
|
|
195572
195762
|
return {
|
|
@@ -195581,6 +195771,8 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
195581
195771
|
try {
|
|
195582
195772
|
return await runSubprocessAgent(input2.agent, agentConfig, input2, launchContext.env, launchContext.executionIdentity);
|
|
195583
195773
|
} catch (error51) {
|
|
195774
|
+
if (error51 instanceof KyosoCancellationError)
|
|
195775
|
+
throw error51;
|
|
195584
195776
|
return {
|
|
195585
195777
|
agent: input2.agent,
|
|
195586
195778
|
role: input2.role,
|
|
@@ -195593,6 +195785,7 @@ class SubprocessAcpAgentManager extends BaseAcpAgentManager {
|
|
|
195593
195785
|
}
|
|
195594
195786
|
}
|
|
195595
195787
|
async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecutionIdentity) {
|
|
195788
|
+
throwIfAborted(input2.signal);
|
|
195596
195789
|
const startedAt = new Date().toISOString();
|
|
195597
195790
|
const effectiveTimeoutMs = resolveEffectiveTimeoutMs(input2);
|
|
195598
195791
|
if (effectiveTimeoutMs <= 0) {
|
|
@@ -195608,12 +195801,20 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195608
195801
|
}
|
|
195609
195802
|
};
|
|
195610
195803
|
}
|
|
195611
|
-
return new Promise((resolveResult) => {
|
|
195804
|
+
return new Promise((resolveResult, rejectResult) => {
|
|
195805
|
+
const abortController = new AbortController;
|
|
195806
|
+
let cancelSession;
|
|
195807
|
+
let timeout;
|
|
195612
195808
|
const child = spawn(agentConfig.command, agentConfig.args, {
|
|
195613
195809
|
cwd: input2.workspaceDir,
|
|
195614
195810
|
env,
|
|
195615
195811
|
stdio: ["pipe", "pipe", "pipe"]
|
|
195616
195812
|
});
|
|
195813
|
+
let termination;
|
|
195814
|
+
const terminate = () => {
|
|
195815
|
+
termination ??= terminateChild(child);
|
|
195816
|
+
return termination;
|
|
195817
|
+
};
|
|
195617
195818
|
let stdout = "";
|
|
195618
195819
|
let stderr3 = "";
|
|
195619
195820
|
let settled = false;
|
|
@@ -195629,18 +195830,44 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195629
195830
|
return;
|
|
195630
195831
|
});
|
|
195631
195832
|
});
|
|
195632
|
-
|
|
195833
|
+
let onAbort;
|
|
195834
|
+
const cleanup = () => {
|
|
195835
|
+
if (timeout)
|
|
195836
|
+
clearTimeout(timeout);
|
|
195837
|
+
if (onAbort)
|
|
195838
|
+
input2.signal?.removeEventListener("abort", onAbort);
|
|
195839
|
+
};
|
|
195633
195840
|
const resolveOnce = (result) => {
|
|
195634
195841
|
if (settled)
|
|
195635
195842
|
return;
|
|
195636
195843
|
settled = true;
|
|
195637
|
-
|
|
195844
|
+
cleanup();
|
|
195638
195845
|
const finalResult = spawned && result.executionIdentity === undefined ? { ...result, executionIdentity: launchExecutionIdentity } : result;
|
|
195639
195846
|
(startedWrite ?? Promise.resolve()).then(() => resolveResult(finalResult));
|
|
195640
195847
|
};
|
|
195641
|
-
const
|
|
195848
|
+
const rejectOnce = (error51) => {
|
|
195849
|
+
if (settled)
|
|
195850
|
+
return;
|
|
195851
|
+
settled = true;
|
|
195852
|
+
cleanup();
|
|
195853
|
+
rejectResult(error51);
|
|
195854
|
+
};
|
|
195855
|
+
onAbort = () => {
|
|
195856
|
+
const cancellation = cancellationFromSignal(input2.signal);
|
|
195857
|
+
if (timeout)
|
|
195858
|
+
clearTimeout(timeout);
|
|
195859
|
+
abortController.abort(cancellation);
|
|
195860
|
+
cancelSession?.();
|
|
195861
|
+
terminate().then(() => rejectOnce(cancellation));
|
|
195862
|
+
};
|
|
195863
|
+
input2.signal?.addEventListener("abort", onAbort, { once: true });
|
|
195864
|
+
if (input2.signal?.aborted) {
|
|
195865
|
+
onAbort();
|
|
195866
|
+
return;
|
|
195867
|
+
}
|
|
195868
|
+
timeout = setTimeout(() => {
|
|
195642
195869
|
abortController.abort(new Error("Kyoso agent timeout"));
|
|
195643
|
-
|
|
195870
|
+
terminate();
|
|
195644
195871
|
const deadlineReached = input2.deadlineAtEpochMs !== undefined && Date.now() >= input2.deadlineAtEpochMs;
|
|
195645
195872
|
resolveOnce({
|
|
195646
195873
|
agent,
|
|
@@ -195669,7 +195896,9 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195669
195896
|
error: failure
|
|
195670
195897
|
});
|
|
195671
195898
|
});
|
|
195672
|
-
runAcpClientWorkflow(child, input2, abortController, resolveEffortConfigOption(agent, agentConfig.effort), launchExecutionIdentity)
|
|
195899
|
+
runAcpClientWorkflow(child, input2, abortController, resolveEffortConfigOption(agent, agentConfig.effort), launchExecutionIdentity, (cancel) => {
|
|
195900
|
+
cancelSession = cancel;
|
|
195901
|
+
}).then(({
|
|
195673
195902
|
rawText,
|
|
195674
195903
|
warnings,
|
|
195675
195904
|
usage,
|
|
@@ -195677,6 +195906,10 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195677
195906
|
thoughtBytes,
|
|
195678
195907
|
outputBytes,
|
|
195679
195908
|
outputWarningTriggered,
|
|
195909
|
+
observedStreamRetries,
|
|
195910
|
+
discardedRetryMessageBytes,
|
|
195911
|
+
firstOutputAt,
|
|
195912
|
+
lastAcpUpdateAt,
|
|
195680
195913
|
stopReason,
|
|
195681
195914
|
executionIdentity
|
|
195682
195915
|
}) => {
|
|
@@ -195694,6 +195927,10 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195694
195927
|
thoughtBytes,
|
|
195695
195928
|
outputBytes,
|
|
195696
195929
|
outputWarningTriggered,
|
|
195930
|
+
observedStreamRetries,
|
|
195931
|
+
...discardedRetryMessageBytes === 0 ? {} : { discardedRetryMessageBytes },
|
|
195932
|
+
...firstOutputAt === undefined ? {} : { firstOutputAt },
|
|
195933
|
+
...lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt },
|
|
195697
195934
|
stopReason,
|
|
195698
195935
|
executionIdentity,
|
|
195699
195936
|
...usage ? { usage } : {},
|
|
@@ -195706,6 +195943,10 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195706
195943
|
}
|
|
195707
195944
|
});
|
|
195708
195945
|
}).catch((error51) => {
|
|
195946
|
+
if (error51 instanceof KyosoCancellationError) {
|
|
195947
|
+
terminate().then(() => rejectOnce(error51));
|
|
195948
|
+
return;
|
|
195949
|
+
}
|
|
195709
195950
|
const outputLimitError = findOutputLimitError(error51, abortController);
|
|
195710
195951
|
if (outputLimitError) {
|
|
195711
195952
|
stdout = outputLimitError.rawText;
|
|
@@ -195720,6 +195961,12 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195720
195961
|
thoughtBytes: outputLimitError.thoughtBytes,
|
|
195721
195962
|
outputBytes: outputLimitError.outputBytes,
|
|
195722
195963
|
outputWarningTriggered: outputLimitError.outputWarningTriggered,
|
|
195964
|
+
observedStreamRetries: outputLimitError.metrics.observedStreamRetries,
|
|
195965
|
+
...outputLimitError.metrics.discardedRetryMessageBytes === 0 ? {} : {
|
|
195966
|
+
discardedRetryMessageBytes: outputLimitError.metrics.discardedRetryMessageBytes
|
|
195967
|
+
},
|
|
195968
|
+
...outputLimitError.metrics.firstOutputAt === undefined ? {} : { firstOutputAt: outputLimitError.metrics.firstOutputAt },
|
|
195969
|
+
...outputLimitError.metrics.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: outputLimitError.metrics.lastAcpUpdateAt },
|
|
195723
195970
|
stopReason: "cancelled",
|
|
195724
195971
|
startedAt,
|
|
195725
195972
|
completedAt: new Date().toISOString(),
|
|
@@ -195730,6 +195977,31 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195730
195977
|
});
|
|
195731
195978
|
return;
|
|
195732
195979
|
}
|
|
195980
|
+
if (error51 instanceof CodexTerminalSystemError) {
|
|
195981
|
+
stdout = error51.rawText;
|
|
195982
|
+
const failureText2 = [stderr3, formatAgentErrorDetail(error51)].filter((part) => part.trim().length > 0).join(`
|
|
195983
|
+
`);
|
|
195984
|
+
resolveOnce({
|
|
195985
|
+
agent,
|
|
195986
|
+
role: input2.role,
|
|
195987
|
+
status: "failed",
|
|
195988
|
+
rawText: stdout,
|
|
195989
|
+
messageBytes: error51.messageBytes,
|
|
195990
|
+
thoughtBytes: error51.thoughtBytes,
|
|
195991
|
+
outputBytes: error51.outputBytes,
|
|
195992
|
+
outputWarningTriggered: error51.outputWarningTriggered,
|
|
195993
|
+
observedStreamRetries: error51.metrics.observedStreamRetries,
|
|
195994
|
+
...error51.metrics.discardedRetryMessageBytes === 0 ? {} : {
|
|
195995
|
+
discardedRetryMessageBytes: error51.metrics.discardedRetryMessageBytes
|
|
195996
|
+
},
|
|
195997
|
+
...error51.metrics.firstOutputAt === undefined ? {} : { firstOutputAt: error51.metrics.firstOutputAt },
|
|
195998
|
+
...error51.metrics.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: error51.metrics.lastAcpUpdateAt },
|
|
195999
|
+
startedAt,
|
|
196000
|
+
completedAt: new Date().toISOString(),
|
|
196001
|
+
error: buildAgentFailure(failureText2, "Agent process failed.")
|
|
196002
|
+
});
|
|
196003
|
+
return;
|
|
196004
|
+
}
|
|
195733
196005
|
if (error51 instanceof AcpNdJsonLineLimitError) {
|
|
195734
196006
|
abortController.abort(error51);
|
|
195735
196007
|
resolveOnce({
|
|
@@ -195761,7 +196033,7 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195761
196033
|
error: buildAgentFailure(failureText, "Agent process failed.")
|
|
195762
196034
|
});
|
|
195763
196035
|
}).finally(() => {
|
|
195764
|
-
|
|
196036
|
+
terminate();
|
|
195765
196037
|
});
|
|
195766
196038
|
child.on("close", (code) => {
|
|
195767
196039
|
if (settled || code === 0 || abortController.signal.aborted)
|
|
@@ -195779,7 +196051,7 @@ async function runSubprocessAgent(agent, agentConfig, input2, env, launchExecuti
|
|
|
195779
196051
|
});
|
|
195780
196052
|
});
|
|
195781
196053
|
}
|
|
195782
|
-
async function runAcpClientWorkflow(child, input2, abortController, configOption, launchExecutionIdentity) {
|
|
196054
|
+
async function runAcpClientWorkflow(child, input2, abortController, configOption, launchExecutionIdentity, onSessionReady) {
|
|
195783
196055
|
if (!child.stdin || !child.stdout) {
|
|
195784
196056
|
throw new Error("Agent process did not expose stdio streams.");
|
|
195785
196057
|
}
|
|
@@ -195827,6 +196099,15 @@ async function runAcpClientWorkflow(child, input2, abortController, configOption
|
|
|
195827
196099
|
kyosoReadOnly: true
|
|
195828
196100
|
}
|
|
195829
196101
|
}).withSession(async (session) => {
|
|
196102
|
+
const cancelSession = () => {
|
|
196103
|
+
ctx.notify(methods.agent.session.cancel, {
|
|
196104
|
+
sessionId: session.sessionId
|
|
196105
|
+
}).catch(() => {
|
|
196106
|
+
return;
|
|
196107
|
+
});
|
|
196108
|
+
};
|
|
196109
|
+
onSessionReady(cancelSession);
|
|
196110
|
+
throwIfAborted(input2.signal);
|
|
195830
196111
|
const warnings = [];
|
|
195831
196112
|
if (configOption) {
|
|
195832
196113
|
await ctx.request(methods.agent.session.setConfigOption, { sessionId: session.sessionId, ...configOption }, { cancellationSignal: abortController.signal }).catch((error51) => {
|
|
@@ -195839,61 +196120,154 @@ async function runAcpClientWorkflow(child, input2, abortController, configOption
|
|
|
195839
196120
|
console.error(`kyoso: ${warning}`);
|
|
195840
196121
|
});
|
|
195841
196122
|
}
|
|
195842
|
-
const
|
|
195843
|
-
cancellationSignal: abortController.signal
|
|
195844
|
-
});
|
|
195845
|
-
promptResponse.catch(() => {
|
|
195846
|
-
return;
|
|
195847
|
-
});
|
|
195848
|
-
let rawText = "";
|
|
196123
|
+
const accumulator = new AgentOutputAccumulator;
|
|
195849
196124
|
let messageBytes = 0;
|
|
195850
196125
|
let thoughtBytes = 0;
|
|
195851
196126
|
let outputBytes = 0;
|
|
195852
196127
|
let outputWarningTriggered = false;
|
|
195853
|
-
|
|
195854
|
-
|
|
195855
|
-
|
|
195856
|
-
|
|
195857
|
-
|
|
195858
|
-
|
|
195859
|
-
|
|
195860
|
-
...usage ? { usage } : {},
|
|
195861
|
-
messageBytes,
|
|
195862
|
-
thoughtBytes,
|
|
195863
|
-
outputBytes,
|
|
195864
|
-
outputWarningTriggered,
|
|
195865
|
-
stopReason: message.stopReason,
|
|
195866
|
-
executionIdentity: withReportedExecutionIdentity(launchExecutionIdentity, message.response._meta)
|
|
195867
|
-
};
|
|
195868
|
-
}
|
|
195869
|
-
const update = message.update;
|
|
195870
|
-
if (update.sessionUpdate !== "agent_message_chunk" && update.sessionUpdate !== "agent_thought_chunk" || update.content.type !== "text") {
|
|
195871
|
-
continue;
|
|
195872
|
-
}
|
|
195873
|
-
const chunkBytes = Buffer.byteLength(update.content.text, "utf8");
|
|
195874
|
-
const isMessage = update.sessionUpdate === "agent_message_chunk";
|
|
195875
|
-
const nextMessageBytes = messageBytes + (isMessage ? chunkBytes : 0);
|
|
195876
|
-
const nextThoughtBytes = thoughtBytes + (isMessage ? 0 : chunkBytes);
|
|
195877
|
-
const nextOutputBytes = nextMessageBytes + nextThoughtBytes;
|
|
195878
|
-
const nextOutputWarningTriggered = outputWarningTriggered || input2.warnOutputBytes !== undefined && nextOutputBytes >= input2.warnOutputBytes;
|
|
195879
|
-
if (input2.maxOutputBytes !== undefined && nextOutputBytes > input2.maxOutputBytes) {
|
|
195880
|
-
const retainedRawText = isMessage ? `${rawText}${utf8Prefix(update.content.text, input2.maxOutputBytes - outputBytes)}` : rawText;
|
|
195881
|
-
await ctx.notify(methods.agent.session.cancel, {
|
|
195882
|
-
sessionId: session.sessionId
|
|
195883
|
-
}).catch(() => {
|
|
196128
|
+
let codexReportedSystemError = false;
|
|
196129
|
+
const sessionStartedAtEpochMs = Date.now();
|
|
196130
|
+
let lastActivityAtEpochMs = 0;
|
|
196131
|
+
const emitProgress = (event) => {
|
|
196132
|
+
try {
|
|
196133
|
+
const progress = input2.onProgress?.(event);
|
|
196134
|
+
Promise.resolve(progress).catch(() => {
|
|
195884
196135
|
return;
|
|
195885
196136
|
});
|
|
195886
|
-
|
|
195887
|
-
|
|
196137
|
+
} catch {}
|
|
196138
|
+
};
|
|
196139
|
+
const heartbeatMs = input2.heartbeatMs ?? DEFAULT_PROGRESS_HEARTBEAT_MS;
|
|
196140
|
+
const heartbeat = input2.onProgress && heartbeatMs > 0 ? setInterval(() => {
|
|
196141
|
+
const now = Date.now();
|
|
196142
|
+
const lastAcpUpdateAt = accumulator.metrics().lastAcpUpdateAt;
|
|
196143
|
+
const lastUpdateEpochMs = lastAcpUpdateAt ? Date.parse(lastAcpUpdateAt) : sessionStartedAtEpochMs;
|
|
196144
|
+
emitProgress({
|
|
196145
|
+
type: "agent_waiting",
|
|
196146
|
+
agent: input2.agent,
|
|
196147
|
+
elapsedMs: Math.max(0, now - sessionStartedAtEpochMs),
|
|
196148
|
+
sinceLastAcpUpdateMs: Math.max(0, now - (Number.isFinite(lastUpdateEpochMs) ? lastUpdateEpochMs : sessionStartedAtEpochMs)),
|
|
196149
|
+
...input2.streamIdleTimeoutMs === undefined ? {} : { streamIdleTimeoutMs: input2.streamIdleTimeoutMs },
|
|
196150
|
+
timestamp: new Date(now).toISOString()
|
|
196151
|
+
});
|
|
196152
|
+
}, heartbeatMs) : undefined;
|
|
196153
|
+
heartbeat?.unref?.();
|
|
196154
|
+
const stopHeartbeat = () => {
|
|
196155
|
+
if (heartbeat)
|
|
196156
|
+
clearInterval(heartbeat);
|
|
196157
|
+
};
|
|
196158
|
+
input2.signal?.addEventListener("abort", stopHeartbeat, { once: true });
|
|
196159
|
+
try {
|
|
196160
|
+
const promptResponse = session.prompt(input2.prompt, {
|
|
196161
|
+
cancellationSignal: abortController.signal
|
|
196162
|
+
});
|
|
196163
|
+
const promptCompletion = promptResponse.catch((error51) => {
|
|
196164
|
+
if (input2.signal?.aborted) {
|
|
196165
|
+
throw cancellationFromSignal(input2.signal);
|
|
196166
|
+
}
|
|
196167
|
+
if (abortController.signal.aborted) {
|
|
196168
|
+
const reason = abortController.signal.reason;
|
|
196169
|
+
if (reason !== undefined)
|
|
196170
|
+
throw reason;
|
|
196171
|
+
}
|
|
195888
196172
|
throw error51;
|
|
196173
|
+
});
|
|
196174
|
+
const promptFailure = promptCompletion.then(() => new Promise(() => {
|
|
196175
|
+
return;
|
|
196176
|
+
}));
|
|
196177
|
+
for (;; ) {
|
|
196178
|
+
const message = await Promise.race([
|
|
196179
|
+
session.nextUpdate(),
|
|
196180
|
+
promptFailure
|
|
196181
|
+
]);
|
|
196182
|
+
if (message.kind === "stop") {
|
|
196183
|
+
await promptCompletion;
|
|
196184
|
+
if (codexReportedSystemError) {
|
|
196185
|
+
throw new CodexTerminalSystemError(accumulator.finalRawText(), messageBytes, thoughtBytes, outputBytes, outputWarningTriggered, accumulator.metrics());
|
|
196186
|
+
}
|
|
196187
|
+
const usage = normalizeUsage3(message.response.usage);
|
|
196188
|
+
return {
|
|
196189
|
+
rawText: accumulator.finalRawText(),
|
|
196190
|
+
warnings,
|
|
196191
|
+
...usage ? { usage } : {},
|
|
196192
|
+
messageBytes,
|
|
196193
|
+
thoughtBytes,
|
|
196194
|
+
outputBytes,
|
|
196195
|
+
outputWarningTriggered,
|
|
196196
|
+
...accumulator.metrics(),
|
|
196197
|
+
stopReason: message.stopReason,
|
|
196198
|
+
executionIdentity: withReportedExecutionIdentity(launchExecutionIdentity, message.response._meta)
|
|
196199
|
+
};
|
|
196200
|
+
}
|
|
196201
|
+
const update = message.update;
|
|
196202
|
+
accumulator.noteUpdate();
|
|
196203
|
+
const retry = parseCodexRetryUpdate(update);
|
|
196204
|
+
if (retry) {
|
|
196205
|
+
codexReportedSystemError = false;
|
|
196206
|
+
const boundary = accumulator.markRetryBoundary();
|
|
196207
|
+
emitProgress({
|
|
196208
|
+
type: "agent_retrying",
|
|
196209
|
+
agent: input2.agent,
|
|
196210
|
+
observedRetry: accumulator.metrics().observedStreamRetries,
|
|
196211
|
+
...retry.attempt === undefined ? {} : { attempt: retry.attempt },
|
|
196212
|
+
...retry.maxRetries === undefined ? {} : { maxRetries: retry.maxRetries },
|
|
196213
|
+
reason: retry.message,
|
|
196214
|
+
discardedMessageBytes: boundary.discardedMessageBytes,
|
|
196215
|
+
timestamp: new Date().toISOString()
|
|
196216
|
+
});
|
|
196217
|
+
continue;
|
|
196218
|
+
}
|
|
196219
|
+
const codexThreadStatus = readCodexThreadStatus(update);
|
|
196220
|
+
if (codexThreadStatus !== undefined) {
|
|
196221
|
+
codexReportedSystemError = codexThreadStatus === "systemError";
|
|
196222
|
+
continue;
|
|
196223
|
+
}
|
|
196224
|
+
if (update.sessionUpdate !== "agent_message_chunk" && update.sessionUpdate !== "agent_thought_chunk" || update.content.type !== "text") {
|
|
196225
|
+
continue;
|
|
196226
|
+
}
|
|
196227
|
+
const chunkBytes = Buffer.byteLength(update.content.text, "utf8");
|
|
196228
|
+
const isMessage = update.sessionUpdate === "agent_message_chunk";
|
|
196229
|
+
const nextMessageBytes = messageBytes + (isMessage ? chunkBytes : 0);
|
|
196230
|
+
const nextThoughtBytes = thoughtBytes + (isMessage ? 0 : chunkBytes);
|
|
196231
|
+
const nextOutputBytes = nextMessageBytes + nextThoughtBytes;
|
|
196232
|
+
const nextOutputWarningTriggered = outputWarningTriggered || input2.warnOutputBytes !== undefined && nextOutputBytes >= input2.warnOutputBytes;
|
|
196233
|
+
if (input2.maxOutputBytes !== undefined && nextOutputBytes > input2.maxOutputBytes) {
|
|
196234
|
+
if (isMessage) {
|
|
196235
|
+
accumulator.addMessageChunk(utf8Prefix(update.content.text, input2.maxOutputBytes - outputBytes), readChunkMeta(update));
|
|
196236
|
+
}
|
|
196237
|
+
const retainedRawText = accumulator.finalRawText();
|
|
196238
|
+
await ctx.notify(methods.agent.session.cancel, {
|
|
196239
|
+
sessionId: session.sessionId
|
|
196240
|
+
}).catch(() => {
|
|
196241
|
+
return;
|
|
196242
|
+
});
|
|
196243
|
+
const error51 = new AgentOutputLimitError(retainedRawText, nextMessageBytes, nextThoughtBytes, nextOutputBytes, input2.maxOutputBytes, nextOutputWarningTriggered, accumulator.metrics());
|
|
196244
|
+
abortController.abort(error51);
|
|
196245
|
+
throw error51;
|
|
196246
|
+
}
|
|
196247
|
+
if (isMessage) {
|
|
196248
|
+
accumulator.addMessageChunk(update.content.text, readChunkMeta(update));
|
|
196249
|
+
} else {
|
|
196250
|
+
accumulator.addThoughtChunk(update.content.text);
|
|
196251
|
+
}
|
|
196252
|
+
messageBytes = nextMessageBytes;
|
|
196253
|
+
thoughtBytes = nextThoughtBytes;
|
|
196254
|
+
outputBytes = nextOutputBytes;
|
|
196255
|
+
outputWarningTriggered = nextOutputWarningTriggered;
|
|
196256
|
+
const now = Date.now();
|
|
196257
|
+
if (now - lastActivityAtEpochMs >= ACTIVITY_PROGRESS_THROTTLE_MS) {
|
|
196258
|
+
lastActivityAtEpochMs = now;
|
|
196259
|
+
emitProgress({
|
|
196260
|
+
type: "agent_activity",
|
|
196261
|
+
agent: input2.agent,
|
|
196262
|
+
activity: isMessage ? "message" : "thought",
|
|
196263
|
+
totalOutputBytes: outputBytes,
|
|
196264
|
+
timestamp: new Date(now).toISOString()
|
|
196265
|
+
});
|
|
196266
|
+
}
|
|
195889
196267
|
}
|
|
195890
|
-
|
|
195891
|
-
|
|
195892
|
-
|
|
195893
|
-
messageBytes = nextMessageBytes;
|
|
195894
|
-
thoughtBytes = nextThoughtBytes;
|
|
195895
|
-
outputBytes = nextOutputBytes;
|
|
195896
|
-
outputWarningTriggered = nextOutputWarningTriggered;
|
|
196268
|
+
} finally {
|
|
196269
|
+
stopHeartbeat();
|
|
196270
|
+
input2.signal?.removeEventListener("abort", stopHeartbeat);
|
|
195897
196271
|
}
|
|
195898
196272
|
});
|
|
195899
196273
|
});
|
|
@@ -195917,7 +196291,8 @@ class AgentOutputLimitError extends Error {
|
|
|
195917
196291
|
outputBytes;
|
|
195918
196292
|
maxOutputBytes;
|
|
195919
196293
|
outputWarningTriggered;
|
|
195920
|
-
|
|
196294
|
+
metrics;
|
|
196295
|
+
constructor(rawText, messageBytes, thoughtBytes, outputBytes, maxOutputBytes, outputWarningTriggered, metrics) {
|
|
195921
196296
|
super(`Agent output exceeded ${maxOutputBytes} bytes.`);
|
|
195922
196297
|
this.rawText = rawText;
|
|
195923
196298
|
this.messageBytes = messageBytes;
|
|
@@ -195925,15 +196300,40 @@ class AgentOutputLimitError extends Error {
|
|
|
195925
196300
|
this.outputBytes = outputBytes;
|
|
195926
196301
|
this.maxOutputBytes = maxOutputBytes;
|
|
195927
196302
|
this.outputWarningTriggered = outputWarningTriggered;
|
|
196303
|
+
this.metrics = metrics;
|
|
195928
196304
|
this.name = "AgentOutputLimitError";
|
|
195929
196305
|
}
|
|
195930
196306
|
}
|
|
196307
|
+
|
|
196308
|
+
class CodexTerminalSystemError extends Error {
|
|
196309
|
+
rawText;
|
|
196310
|
+
messageBytes;
|
|
196311
|
+
thoughtBytes;
|
|
196312
|
+
outputBytes;
|
|
196313
|
+
outputWarningTriggered;
|
|
196314
|
+
metrics;
|
|
196315
|
+
constructor(rawText, messageBytes, thoughtBytes, outputBytes, outputWarningTriggered, metrics) {
|
|
196316
|
+
super(sanitizeTextForDisplay(rawText) || "Codex ACP reported a terminal system error.");
|
|
196317
|
+
this.rawText = rawText;
|
|
196318
|
+
this.messageBytes = messageBytes;
|
|
196319
|
+
this.thoughtBytes = thoughtBytes;
|
|
196320
|
+
this.outputBytes = outputBytes;
|
|
196321
|
+
this.outputWarningTriggered = outputWarningTriggered;
|
|
196322
|
+
this.metrics = metrics;
|
|
196323
|
+
this.name = "CodexTerminalSystemError";
|
|
196324
|
+
}
|
|
196325
|
+
}
|
|
195931
196326
|
function findOutputLimitError(error51, abortController) {
|
|
195932
196327
|
if (error51 instanceof AgentOutputLimitError)
|
|
195933
196328
|
return error51;
|
|
195934
196329
|
const reason = abortController.signal.reason;
|
|
195935
196330
|
return reason instanceof AgentOutputLimitError ? reason : undefined;
|
|
195936
196331
|
}
|
|
196332
|
+
function cancellationFromSignal(signal) {
|
|
196333
|
+
if (signal?.reason instanceof KyosoCancellationError)
|
|
196334
|
+
return signal.reason;
|
|
196335
|
+
return new KyosoCancellationError(typeof signal?.reason === "string" ? signal.reason : undefined);
|
|
196336
|
+
}
|
|
195937
196337
|
function resolveEffectiveTimeoutMs(input2) {
|
|
195938
196338
|
const deadlineRemaining = input2.deadlineAtEpochMs === undefined ? Number.POSITIVE_INFINITY : input2.deadlineAtEpochMs - Date.now();
|
|
195939
196339
|
return Math.max(0, Math.min(input2.timeoutMs, deadlineRemaining));
|
|
@@ -195942,7 +196342,7 @@ function normalizeUsage3(usage) {
|
|
|
195942
196342
|
return normalizeModelTokenUsage(usage);
|
|
195943
196343
|
}
|
|
195944
196344
|
function withReportedExecutionIdentity(identity, metadata) {
|
|
195945
|
-
const record2 =
|
|
196345
|
+
const record2 = isRecord14(metadata) ? metadata : {};
|
|
195946
196346
|
return createModelExecutionIdentity({
|
|
195947
196347
|
providerRoute: identity.providerRoute,
|
|
195948
196348
|
requestedModel: identity.requestedModel,
|
|
@@ -195950,7 +196350,26 @@ function withReportedExecutionIdentity(identity, metadata) {
|
|
|
195950
196350
|
reportedModel: record2.model
|
|
195951
196351
|
});
|
|
195952
196352
|
}
|
|
195953
|
-
function
|
|
196353
|
+
function readChunkMeta(update) {
|
|
196354
|
+
const record2 = isRecord14(update) ? update : {};
|
|
196355
|
+
const metadata = isRecord14(record2._meta) ? record2._meta : {};
|
|
196356
|
+
const codex = isRecord14(metadata.codex) ? metadata.codex : {};
|
|
196357
|
+
const phase = codex.phase === "commentary" || codex.phase === "final_answer" ? codex.phase : "unknown";
|
|
196358
|
+
return {
|
|
196359
|
+
...typeof record2.messageId === "string" ? { messageId: record2.messageId } : {},
|
|
196360
|
+
phase
|
|
196361
|
+
};
|
|
196362
|
+
}
|
|
196363
|
+
function readCodexThreadStatus(update) {
|
|
196364
|
+
const record2 = isRecord14(update) ? update : {};
|
|
196365
|
+
if (record2.sessionUpdate !== "session_info_update")
|
|
196366
|
+
return;
|
|
196367
|
+
const metadata = isRecord14(record2._meta) ? record2._meta : {};
|
|
196368
|
+
const codex = isRecord14(metadata.codex) ? metadata.codex : {};
|
|
196369
|
+
const threadStatus = isRecord14(codex.threadStatus) ? codex.threadStatus : {};
|
|
196370
|
+
return typeof threadStatus.type === "string" ? threadStatus.type : undefined;
|
|
196371
|
+
}
|
|
196372
|
+
function isRecord14(value) {
|
|
195954
196373
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
195955
196374
|
}
|
|
195956
196375
|
function resolveEffortConfigOption(agent, effort) {
|
|
@@ -196016,14 +196435,40 @@ function assertWithinWorkspace(workspaceRoot, absolute) {
|
|
|
196016
196435
|
}
|
|
196017
196436
|
}
|
|
196018
196437
|
function terminateChild(child) {
|
|
196019
|
-
if (child.exitCode !== null || child.signalCode !== null)
|
|
196020
|
-
return;
|
|
196021
|
-
|
|
196022
|
-
|
|
196023
|
-
|
|
196438
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
196439
|
+
return Promise.resolve();
|
|
196440
|
+
}
|
|
196441
|
+
return new Promise((resolve10) => {
|
|
196442
|
+
let settled = false;
|
|
196443
|
+
let killTimer;
|
|
196444
|
+
let escalationTimer;
|
|
196445
|
+
let onClose;
|
|
196446
|
+
const settle = () => {
|
|
196447
|
+
if (settled)
|
|
196448
|
+
return;
|
|
196449
|
+
settled = true;
|
|
196450
|
+
if (killTimer)
|
|
196451
|
+
clearTimeout(killTimer);
|
|
196452
|
+
if (escalationTimer)
|
|
196453
|
+
clearTimeout(escalationTimer);
|
|
196454
|
+
if (onClose)
|
|
196455
|
+
child.off("close", onClose);
|
|
196456
|
+
resolve10();
|
|
196457
|
+
};
|
|
196458
|
+
onClose = () => settle();
|
|
196459
|
+
child.once("close", onClose);
|
|
196460
|
+
child.kill("SIGTERM");
|
|
196461
|
+
killTimer = setTimeout(() => {
|
|
196462
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
196463
|
+
settle();
|
|
196464
|
+
return;
|
|
196465
|
+
}
|
|
196024
196466
|
child.kill("SIGKILL");
|
|
196025
|
-
|
|
196026
|
-
|
|
196467
|
+
escalationTimer = setTimeout(settle, 500);
|
|
196468
|
+
escalationTimer.unref();
|
|
196469
|
+
}, 2000);
|
|
196470
|
+
killTimer.unref();
|
|
196471
|
+
});
|
|
196027
196472
|
}
|
|
196028
196473
|
function isMissingPathError6(error51) {
|
|
196029
196474
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
@@ -196207,6 +196652,128 @@ function assertUsableEnvironmentValue(env, key) {
|
|
|
196207
196652
|
return value;
|
|
196208
196653
|
}
|
|
196209
196654
|
|
|
196655
|
+
// src/cli/progress.ts
|
|
196656
|
+
function resolveCliProgressMode(value, stderrIsTty) {
|
|
196657
|
+
const mode = value ?? "auto";
|
|
196658
|
+
if (mode === "auto")
|
|
196659
|
+
return stderrIsTty ? "plain" : "off";
|
|
196660
|
+
if (mode === "plain" || mode === "jsonl" || mode === "off")
|
|
196661
|
+
return mode;
|
|
196662
|
+
throw new Error(`Invalid --progress value "${value}". Expected auto|plain|jsonl|off.`);
|
|
196663
|
+
}
|
|
196664
|
+
function createCliProgressSink(mode, stderrWrite) {
|
|
196665
|
+
if (mode === "off" || mode === "auto")
|
|
196666
|
+
return;
|
|
196667
|
+
if (mode === "jsonl") {
|
|
196668
|
+
return (event) => writeSafely(stderrWrite, `${JSON.stringify(event)}
|
|
196669
|
+
`);
|
|
196670
|
+
}
|
|
196671
|
+
const startedAtEpochMs = Date.now();
|
|
196672
|
+
return (event) => {
|
|
196673
|
+
const elapsedMs = Math.max(0, Date.now() - startedAtEpochMs);
|
|
196674
|
+
writeSafely(stderrWrite, `[${formatElapsed(elapsedMs)}] ${formatPlainProgressMessage(event)}
|
|
196675
|
+
`);
|
|
196676
|
+
};
|
|
196677
|
+
}
|
|
196678
|
+
function writeSafely(write, line) {
|
|
196679
|
+
try {
|
|
196680
|
+
write(line);
|
|
196681
|
+
} catch {}
|
|
196682
|
+
}
|
|
196683
|
+
function formatElapsed(elapsedMs) {
|
|
196684
|
+
const totalSeconds = Math.floor(elapsedMs / 1000);
|
|
196685
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
196686
|
+
const seconds = totalSeconds % 60;
|
|
196687
|
+
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
|
|
196688
|
+
}
|
|
196689
|
+
function formatPlainProgressMessage(event) {
|
|
196690
|
+
switch (event.type) {
|
|
196691
|
+
case "review_started":
|
|
196692
|
+
return `Kyoso review started (${event.tool})`;
|
|
196693
|
+
case "phase_started":
|
|
196694
|
+
return phaseStartedMessage(event);
|
|
196695
|
+
case "phase_completed":
|
|
196696
|
+
return phaseCompletedMessage(event);
|
|
196697
|
+
case "phase_skipped":
|
|
196698
|
+
return `${phaseLabel(event.phase)} skipped`;
|
|
196699
|
+
case "agent_started":
|
|
196700
|
+
return event.role === "finding_verifier" ? `Finding verifier started: ${event.agent}` : `Primary reviewers started: ${event.agent}`;
|
|
196701
|
+
case "agent_activity":
|
|
196702
|
+
return `${event.agent}: received ${event.totalOutputBytes} output bytes`;
|
|
196703
|
+
case "agent_waiting":
|
|
196704
|
+
return waitingMessage(event);
|
|
196705
|
+
case "agent_retrying":
|
|
196706
|
+
return retryMessage(event);
|
|
196707
|
+
case "agent_completed":
|
|
196708
|
+
return completedMessage(event);
|
|
196709
|
+
case "review_completed":
|
|
196710
|
+
return `Review completed: ${event.decision}`;
|
|
196711
|
+
case "review_failed":
|
|
196712
|
+
return "Review failed";
|
|
196713
|
+
case "review_cancelled":
|
|
196714
|
+
return "Review cancelled";
|
|
196715
|
+
}
|
|
196716
|
+
}
|
|
196717
|
+
function phaseStartedMessage(event) {
|
|
196718
|
+
switch (event.phase) {
|
|
196719
|
+
case "context":
|
|
196720
|
+
return "Preparing context";
|
|
196721
|
+
case "snapshot":
|
|
196722
|
+
return "Creating read-only snapshot";
|
|
196723
|
+
case "primary":
|
|
196724
|
+
return "Starting primary reviewers";
|
|
196725
|
+
case "verification":
|
|
196726
|
+
return "Cross-agent verification started";
|
|
196727
|
+
case "judge":
|
|
196728
|
+
return "Cross-model judge started";
|
|
196729
|
+
case "finalize":
|
|
196730
|
+
return "Finalizing review";
|
|
196731
|
+
case "preflight":
|
|
196732
|
+
return "Checking review configuration";
|
|
196733
|
+
case "aggregation":
|
|
196734
|
+
return "Aggregating findings";
|
|
196735
|
+
}
|
|
196736
|
+
}
|
|
196737
|
+
function phaseCompletedMessage(event) {
|
|
196738
|
+
switch (event.phase) {
|
|
196739
|
+
case "context":
|
|
196740
|
+
return "Context prepared";
|
|
196741
|
+
case "snapshot":
|
|
196742
|
+
return "Read-only snapshot created";
|
|
196743
|
+
case "primary":
|
|
196744
|
+
return "Primary reviewers completed";
|
|
196745
|
+
case "verification":
|
|
196746
|
+
return "Cross-agent verification completed";
|
|
196747
|
+
case "judge":
|
|
196748
|
+
return "Cross-model judge completed";
|
|
196749
|
+
case "finalize":
|
|
196750
|
+
return "Review finalized";
|
|
196751
|
+
case "preflight":
|
|
196752
|
+
return "Review configuration checked";
|
|
196753
|
+
case "aggregation":
|
|
196754
|
+
return "Findings aggregated";
|
|
196755
|
+
}
|
|
196756
|
+
}
|
|
196757
|
+
function phaseLabel(phase) {
|
|
196758
|
+
return phase === "preflight" ? "Preflight" : phase;
|
|
196759
|
+
}
|
|
196760
|
+
function waitingMessage(event) {
|
|
196761
|
+
const seconds = Math.floor(event.sinceLastAcpUpdateMs / 1000);
|
|
196762
|
+
const threshold = event.streamIdleTimeoutMs === undefined ? "" : `; stream idle retry threshold is ${Math.floor(event.streamIdleTimeoutMs / 1000)}s`;
|
|
196763
|
+
return `${event.agent}: process alive; no ACP update for ${seconds}s${threshold}`;
|
|
196764
|
+
}
|
|
196765
|
+
function retryMessage(event) {
|
|
196766
|
+
const attempt = event.maxRetries === undefined ? String(event.observedRetry) : `${event.observedRetry}/${event.maxRetries}`;
|
|
196767
|
+
return `${event.agent}: retrying model stream (${attempt}); discarded ${event.discardedMessageBytes} bytes of incomplete output`;
|
|
196768
|
+
}
|
|
196769
|
+
function completedMessage(event) {
|
|
196770
|
+
if (event.observedStreamRetries === undefined) {
|
|
196771
|
+
return `${event.agent} ${event.status}`;
|
|
196772
|
+
}
|
|
196773
|
+
const suffix = event.observedStreamRetries === 1 ? "retry" : "retries";
|
|
196774
|
+
return `${event.agent} completed after ${event.observedStreamRetries} observed stream ${suffix}`;
|
|
196775
|
+
}
|
|
196776
|
+
|
|
196210
196777
|
// node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
|
|
196211
196778
|
var __create2 = Object.create;
|
|
196212
196779
|
var __defProp2 = Object.defineProperty;
|
|
@@ -210174,6 +210741,11 @@ import { resolve as resolve10 } from "node:path";
|
|
|
210174
210741
|
|
|
210175
210742
|
// src/config/configOverrides.ts
|
|
210176
210743
|
var NUMBER_VALUE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
210744
|
+
var UNSET_NUMBER_OVERRIDE_PATHS = new Set([
|
|
210745
|
+
"agents.codex.openRouter.streamIdleTimeoutMs",
|
|
210746
|
+
"agents.codex.openRouter.streamMaxRetries",
|
|
210747
|
+
"agents.codex.openRouter.requestMaxRetries"
|
|
210748
|
+
]);
|
|
210177
210749
|
function applyConfigOverrides(config2, assignments) {
|
|
210178
210750
|
if (assignments.length === 0)
|
|
210179
210751
|
return config2;
|
|
@@ -210181,9 +210753,9 @@ function applyConfigOverrides(config2, assignments) {
|
|
|
210181
210753
|
const baseConfig = config2;
|
|
210182
210754
|
const overridden = structuredClone(config2);
|
|
210183
210755
|
for (const override of overrides) {
|
|
210184
|
-
writePath2(overridden, override.path, parseConfigOverrideValue(override.value, readPath2(baseConfig, override.path)));
|
|
210756
|
+
writePath2(overridden, override.path, parseConfigOverrideValue(override.value, readPath2(baseConfig, override.path), override.path));
|
|
210185
210757
|
}
|
|
210186
|
-
|
|
210758
|
+
clearInheritedOpenRouterConfigForProviderReset(baseConfig, overridden, overrides);
|
|
210187
210759
|
assertOpenRouterProviderOverrideIncludesModel(baseConfig, overridden, overrides);
|
|
210188
210760
|
const parsed = kyosoConfigSchema.safeParse(overridden);
|
|
210189
210761
|
if (parsed.success)
|
|
@@ -210204,17 +210776,23 @@ function assertOpenRouterProviderOverrideIncludesModel(baseConfig, overridden, o
|
|
|
210204
210776
|
const assignment = findAssignmentForPath(overrides, providerPath.join(".")) ?? "agents.codex.provider=openrouter";
|
|
210205
210777
|
throw new Error(`Invalid --set value ${JSON.stringify(assignment)}: selecting agents.codex.provider=openrouter requires agents.codex.model in the same --set invocation.`);
|
|
210206
210778
|
}
|
|
210207
|
-
function
|
|
210779
|
+
function clearInheritedOpenRouterConfigForProviderReset(baseConfig, overridden, overrides) {
|
|
210208
210780
|
const providerPath = ["agents", "codex", "provider"];
|
|
210209
210781
|
const modelPath = ["agents", "codex", "model"];
|
|
210782
|
+
const openRouterPath = ["agents", "codex", "openRouter"];
|
|
210210
210783
|
const selectsDefaultProvider = overrides.some((override) => override.path.join(".") === providerPath.join(".") && override.value === CODEX_DEFAULT_PROVIDER);
|
|
210211
210784
|
const suppliesModel = overrides.some((override) => override.path.join(".") === modelPath.join("."));
|
|
210212
|
-
|
|
210785
|
+
const suppliesOpenRouterPolicy = overrides.some((override) => override.path.join(".").startsWith(`${openRouterPath.join(".")}.`));
|
|
210786
|
+
if (!selectsDefaultProvider || readPath2(baseConfig, providerPath) !== CODEX_OPENROUTER_PROVIDER || readPath2(overridden, providerPath) !== CODEX_DEFAULT_PROVIDER) {
|
|
210213
210787
|
return;
|
|
210214
210788
|
}
|
|
210215
210789
|
const codex = readPath2(overridden, ["agents", "codex"]);
|
|
210216
|
-
if (
|
|
210790
|
+
if (!isRecord15(codex))
|
|
210791
|
+
return;
|
|
210792
|
+
if (!suppliesModel)
|
|
210217
210793
|
delete codex.model;
|
|
210794
|
+
if (!suppliesOpenRouterPolicy)
|
|
210795
|
+
delete codex.openRouter;
|
|
210218
210796
|
}
|
|
210219
210797
|
function findAssignmentForPath(overrides, path) {
|
|
210220
210798
|
for (let index = overrides.length - 1;index >= 0; index -= 1) {
|
|
@@ -210240,7 +210818,7 @@ function parseConfigOverride(assignment) {
|
|
|
210240
210818
|
value: assignment.slice(separator + 1)
|
|
210241
210819
|
};
|
|
210242
210820
|
}
|
|
210243
|
-
function parseConfigOverrideValue(value, currentValue) {
|
|
210821
|
+
function parseConfigOverrideValue(value, currentValue, path) {
|
|
210244
210822
|
if (typeof currentValue === "boolean") {
|
|
210245
210823
|
if (value === "true")
|
|
210246
210824
|
return true;
|
|
@@ -210248,7 +210826,7 @@ function parseConfigOverrideValue(value, currentValue) {
|
|
|
210248
210826
|
return false;
|
|
210249
210827
|
return value;
|
|
210250
210828
|
}
|
|
210251
|
-
if (typeof currentValue === "number" && NUMBER_VALUE.test(value)) {
|
|
210829
|
+
if ((typeof currentValue === "number" || currentValue === undefined && UNSET_NUMBER_OVERRIDE_PATHS.has(path.join("."))) && NUMBER_VALUE.test(value)) {
|
|
210252
210830
|
const parsed = Number(value);
|
|
210253
210831
|
if (Number.isFinite(parsed))
|
|
210254
210832
|
return parsed;
|
|
@@ -210258,7 +210836,7 @@ function parseConfigOverrideValue(value, currentValue) {
|
|
|
210258
210836
|
function readPath2(target, path) {
|
|
210259
210837
|
let current = target;
|
|
210260
210838
|
for (const key of path) {
|
|
210261
|
-
if (!
|
|
210839
|
+
if (!isRecord15(current))
|
|
210262
210840
|
return;
|
|
210263
210841
|
current = current[key];
|
|
210264
210842
|
}
|
|
@@ -210268,7 +210846,7 @@ function writePath2(target, path, value) {
|
|
|
210268
210846
|
let current = target;
|
|
210269
210847
|
for (const key of path.slice(0, -1)) {
|
|
210270
210848
|
const child = current[key];
|
|
210271
|
-
if (!
|
|
210849
|
+
if (!isRecord15(child)) {
|
|
210272
210850
|
throw new Error(`Cannot apply --set key ${JSON.stringify(path.join("."))}.`);
|
|
210273
210851
|
}
|
|
210274
210852
|
current = child;
|
|
@@ -210277,7 +210855,7 @@ function writePath2(target, path, value) {
|
|
|
210277
210855
|
if (leaf)
|
|
210278
210856
|
current[leaf] = value;
|
|
210279
210857
|
}
|
|
210280
|
-
function
|
|
210858
|
+
function isRecord15(value) {
|
|
210281
210859
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
210282
210860
|
}
|
|
210283
210861
|
|
|
@@ -210292,6 +210870,7 @@ class FakeAgentManager extends BaseAcpAgentManager {
|
|
|
210292
210870
|
this.verifierScenarios = verifierScenarios;
|
|
210293
210871
|
}
|
|
210294
210872
|
async runAgent(input2) {
|
|
210873
|
+
throwIfAborted(input2.signal);
|
|
210295
210874
|
this.calls.push(input2);
|
|
210296
210875
|
const startedAt = new Date().toISOString();
|
|
210297
210876
|
if (input2.role === "finding_verifier") {
|
|
@@ -211344,29 +211923,184 @@ function buildContext(request, options) {
|
|
|
211344
211923
|
}
|
|
211345
211924
|
}
|
|
211346
211925
|
|
|
211347
|
-
// src/core/
|
|
211348
|
-
|
|
211349
|
-
|
|
211350
|
-
|
|
211351
|
-
|
|
211352
|
-
|
|
211353
|
-
|
|
211354
|
-
if (
|
|
211355
|
-
|
|
211926
|
+
// src/core/progressDispatcher.ts
|
|
211927
|
+
var DEFAULT_SINK_TIMEOUT_MS = 2000;
|
|
211928
|
+
var DEFAULT_MAX_QUEUE = 128;
|
|
211929
|
+
function isTransientEvent(event) {
|
|
211930
|
+
return event.type === "agent_activity" || event.type === "agent_waiting";
|
|
211931
|
+
}
|
|
211932
|
+
function isSameCoalescibleEvent(previous, next) {
|
|
211933
|
+
if (!previous || !isTransientEvent(previous) || !isTransientEvent(next)) {
|
|
211934
|
+
return false;
|
|
211356
211935
|
}
|
|
211936
|
+
return previous.type === next.type && previous.agent === next.agent;
|
|
211357
211937
|
}
|
|
211358
|
-
function
|
|
211359
|
-
|
|
211360
|
-
|
|
211361
|
-
|
|
211362
|
-
|
|
211363
|
-
|
|
211364
|
-
|
|
211365
|
-
|
|
211366
|
-
|
|
211367
|
-
|
|
211368
|
-
|
|
211369
|
-
|
|
211938
|
+
function withoutThrowing(callback) {
|
|
211939
|
+
try {
|
|
211940
|
+
callback?.();
|
|
211941
|
+
} catch {}
|
|
211942
|
+
}
|
|
211943
|
+
function createProgressDispatcher(sink, options = {}) {
|
|
211944
|
+
if (!sink) {
|
|
211945
|
+
return {
|
|
211946
|
+
emit: () => {
|
|
211947
|
+
return;
|
|
211948
|
+
},
|
|
211949
|
+
flush: async () => {
|
|
211950
|
+
return;
|
|
211951
|
+
}
|
|
211952
|
+
};
|
|
211953
|
+
}
|
|
211954
|
+
const sinkTimeoutMs = options.sinkTimeoutMs ?? DEFAULT_SINK_TIMEOUT_MS;
|
|
211955
|
+
const maxQueue = options.maxQueue ?? DEFAULT_MAX_QUEUE;
|
|
211956
|
+
const queue = [];
|
|
211957
|
+
const idleResolvers = new Set;
|
|
211958
|
+
let disabled = false;
|
|
211959
|
+
let processing = false;
|
|
211960
|
+
const notifyIdle = () => {
|
|
211961
|
+
if (processing || queue.length > 0)
|
|
211962
|
+
return;
|
|
211963
|
+
for (const resolve10 of idleResolvers)
|
|
211964
|
+
resolve10();
|
|
211965
|
+
idleResolvers.clear();
|
|
211966
|
+
};
|
|
211967
|
+
const waitForIdle = () => {
|
|
211968
|
+
if (!processing && queue.length === 0)
|
|
211969
|
+
return Promise.resolve();
|
|
211970
|
+
return new Promise((resolve10) => idleResolvers.add(resolve10));
|
|
211971
|
+
};
|
|
211972
|
+
const disable = (reason) => {
|
|
211973
|
+
if (disabled)
|
|
211974
|
+
return;
|
|
211975
|
+
disabled = true;
|
|
211976
|
+
queue.length = 0;
|
|
211977
|
+
withoutThrowing(() => options.onSinkDisabled?.(reason));
|
|
211978
|
+
notifyIdle();
|
|
211979
|
+
};
|
|
211980
|
+
const deliver = async (event) => {
|
|
211981
|
+
let timeout;
|
|
211982
|
+
try {
|
|
211983
|
+
const delivery = Promise.resolve().then(() => sink(event));
|
|
211984
|
+
const timeoutResult = new Promise((resolve10) => {
|
|
211985
|
+
timeout = setTimeout(() => resolve10("timeout"), sinkTimeoutMs);
|
|
211986
|
+
timeout.unref?.();
|
|
211987
|
+
});
|
|
211988
|
+
const result = await Promise.race([
|
|
211989
|
+
delivery.then(() => "delivered"),
|
|
211990
|
+
timeoutResult
|
|
211991
|
+
]);
|
|
211992
|
+
if (result === "timeout") {
|
|
211993
|
+
disable(`Progress sink timed out after ${sinkTimeoutMs}ms.`);
|
|
211994
|
+
return false;
|
|
211995
|
+
}
|
|
211996
|
+
return true;
|
|
211997
|
+
} catch {
|
|
211998
|
+
disable("Progress sink threw while handling an event.");
|
|
211999
|
+
return false;
|
|
212000
|
+
} finally {
|
|
212001
|
+
if (timeout)
|
|
212002
|
+
clearTimeout(timeout);
|
|
212003
|
+
}
|
|
212004
|
+
};
|
|
212005
|
+
const drain = async () => {
|
|
212006
|
+
try {
|
|
212007
|
+
while (!disabled && queue.length > 0) {
|
|
212008
|
+
const event = queue.shift();
|
|
212009
|
+
if (!event)
|
|
212010
|
+
continue;
|
|
212011
|
+
if (!await deliver(event))
|
|
212012
|
+
break;
|
|
212013
|
+
}
|
|
212014
|
+
} finally {
|
|
212015
|
+
processing = false;
|
|
212016
|
+
if (!disabled && queue.length > 0) {
|
|
212017
|
+
startDrain();
|
|
212018
|
+
} else {
|
|
212019
|
+
notifyIdle();
|
|
212020
|
+
}
|
|
212021
|
+
}
|
|
212022
|
+
};
|
|
212023
|
+
const startDrain = () => {
|
|
212024
|
+
if (processing || disabled || queue.length === 0)
|
|
212025
|
+
return;
|
|
212026
|
+
processing = true;
|
|
212027
|
+
drain();
|
|
212028
|
+
};
|
|
212029
|
+
const makeRoomForMilestone = () => {
|
|
212030
|
+
const transientIndex = queue.findIndex(isTransientEvent);
|
|
212031
|
+
if (transientIndex >= 0) {
|
|
212032
|
+
queue.splice(transientIndex, 1);
|
|
212033
|
+
return true;
|
|
212034
|
+
}
|
|
212035
|
+
if (queue.length < maxQueue)
|
|
212036
|
+
return true;
|
|
212037
|
+
queue.shift();
|
|
212038
|
+
return true;
|
|
212039
|
+
};
|
|
212040
|
+
return {
|
|
212041
|
+
emit(event) {
|
|
212042
|
+
try {
|
|
212043
|
+
if (disabled)
|
|
212044
|
+
return;
|
|
212045
|
+
const previous = queue.at(-1);
|
|
212046
|
+
if (isSameCoalescibleEvent(previous, event)) {
|
|
212047
|
+
queue[queue.length - 1] = event;
|
|
212048
|
+
} else if (queue.length < maxQueue) {
|
|
212049
|
+
queue.push(event);
|
|
212050
|
+
} else if (isTransientEvent(event)) {
|
|
212051
|
+
return;
|
|
212052
|
+
} else if (makeRoomForMilestone()) {
|
|
212053
|
+
queue.push(event);
|
|
212054
|
+
}
|
|
212055
|
+
startDrain();
|
|
212056
|
+
} catch {}
|
|
212057
|
+
},
|
|
212058
|
+
async flush(maxWaitMs = DEFAULT_SINK_TIMEOUT_MS) {
|
|
212059
|
+
try {
|
|
212060
|
+
const idle = waitForIdle();
|
|
212061
|
+
if (maxWaitMs <= 0)
|
|
212062
|
+
return;
|
|
212063
|
+
let timeout;
|
|
212064
|
+
try {
|
|
212065
|
+
await Promise.race([
|
|
212066
|
+
idle,
|
|
212067
|
+
new Promise((resolve10) => {
|
|
212068
|
+
timeout = setTimeout(resolve10, maxWaitMs);
|
|
212069
|
+
timeout.unref?.();
|
|
212070
|
+
})
|
|
212071
|
+
]);
|
|
212072
|
+
} finally {
|
|
212073
|
+
if (timeout)
|
|
212074
|
+
clearTimeout(timeout);
|
|
212075
|
+
}
|
|
212076
|
+
} catch {}
|
|
212077
|
+
}
|
|
212078
|
+
};
|
|
212079
|
+
}
|
|
212080
|
+
|
|
212081
|
+
// src/core/validateRequest.ts
|
|
212082
|
+
function validateReviewRequest(tool, request) {
|
|
212083
|
+
if (typeof request.goal !== "string" || request.goal.trim().length === 0) {
|
|
212084
|
+
throw new KyosoRequestError("goal is required", "VALIDATION_ERROR");
|
|
212085
|
+
}
|
|
212086
|
+
validateReviewContract(request);
|
|
212087
|
+
validateSelectedFiles(request);
|
|
212088
|
+
if (tool === "diff_review" && !request.diff?.unifiedDiff) {
|
|
212089
|
+
throw new KyosoRequestError("diff_review requires diff.unifiedDiff in MCP/core mode", "DIFF_REQUIRED");
|
|
212090
|
+
}
|
|
212091
|
+
}
|
|
212092
|
+
function validateReviewContract(request) {
|
|
212093
|
+
const contract = request.reviewContract;
|
|
212094
|
+
if (contract === undefined)
|
|
212095
|
+
return;
|
|
212096
|
+
if (!isRecord16(contract)) {
|
|
212097
|
+
throw new KyosoRequestError("reviewContract must be an object", "VALIDATION_ERROR");
|
|
212098
|
+
}
|
|
212099
|
+
const allowedKeys = new Set(["focus", "nonGoals", "acceptedRisks"]);
|
|
212100
|
+
const unknownKeys = Object.keys(contract).filter((key) => !allowedKeys.has(key));
|
|
212101
|
+
if (unknownKeys.length > 0) {
|
|
212102
|
+
throw new KyosoRequestError(`reviewContract contains unknown keys: ${unknownKeys.join(", ")}`, "VALIDATION_ERROR");
|
|
212103
|
+
}
|
|
211370
212104
|
const focus = contract.focus;
|
|
211371
212105
|
if (focus !== undefined && (!Array.isArray(focus) || focus.length > REVIEW_LENSES.length || focus.some((lens) => !isReviewLens(lens)))) {
|
|
211372
212106
|
throw new KyosoRequestError("reviewContract.focus contains an invalid review lens", "VALIDATION_ERROR");
|
|
@@ -211376,7 +212110,7 @@ function validateReviewContract(request) {
|
|
|
211376
212110
|
throw new KyosoRequestError("reviewContract.nonGoals must contain at most 20 non-empty strings of 500 characters or fewer", "VALIDATION_ERROR");
|
|
211377
212111
|
}
|
|
211378
212112
|
const acceptedRisks = contract.acceptedRisks;
|
|
211379
|
-
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !
|
|
212113
|
+
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !isRecord16(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))) {
|
|
211380
212114
|
throw new KyosoRequestError("reviewContract.acceptedRisks must contain valid finding fingerprints and bounded rationales", "VALIDATION_ERROR");
|
|
211381
212115
|
}
|
|
211382
212116
|
}
|
|
@@ -211388,13 +212122,13 @@ function validateSelectedFiles(request) {
|
|
|
211388
212122
|
throw new KyosoRequestError("selectedFiles must be an array", "VALIDATION_ERROR");
|
|
211389
212123
|
}
|
|
211390
212124
|
for (const file2 of selectedFiles) {
|
|
211391
|
-
if (!
|
|
212125
|
+
if (!isRecord16(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") {
|
|
211392
212126
|
throw new KyosoRequestError("selectedFiles entries require string path/content and valid optional metadata", "VALIDATION_ERROR");
|
|
211393
212127
|
}
|
|
211394
212128
|
normalizeRelativePath(file2.path);
|
|
211395
212129
|
}
|
|
211396
212130
|
}
|
|
211397
|
-
function
|
|
212131
|
+
function isRecord16(value) {
|
|
211398
212132
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
211399
212133
|
}
|
|
211400
212134
|
|
|
@@ -211925,11 +212659,11 @@ function canonicalJson(value) {
|
|
|
211925
212659
|
function canonicalize(value) {
|
|
211926
212660
|
if (Array.isArray(value))
|
|
211927
212661
|
return value.map(canonicalize);
|
|
211928
|
-
if (!
|
|
212662
|
+
if (!isRecord17(value))
|
|
211929
212663
|
return value;
|
|
211930
212664
|
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)]));
|
|
211931
212665
|
}
|
|
211932
|
-
function
|
|
212666
|
+
function isRecord17(value) {
|
|
211933
212667
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
211934
212668
|
}
|
|
211935
212669
|
|
|
@@ -211943,7 +212677,7 @@ var REVIEW_BUDGET_KEYS = new Set([
|
|
|
211943
212677
|
]);
|
|
211944
212678
|
var MODEL_CALL_KINDS = ["primary", "verifier", "judge"];
|
|
211945
212679
|
function resolveReviewBudget(ceiling, requested) {
|
|
211946
|
-
if (requested !== undefined && !
|
|
212680
|
+
if (requested !== undefined && !isRecord18(requested)) {
|
|
211947
212681
|
throw new KyosoRequestError("options.reviewBudget must be an object.", "REVIEW_BUDGET_INVALID");
|
|
211948
212682
|
}
|
|
211949
212683
|
for (const [key, value] of Object.entries(requested ?? {})) {
|
|
@@ -212133,6 +212867,10 @@ class ReviewBudgetTracker {
|
|
|
212133
212867
|
current.thoughtBytes = values.thoughtBytes;
|
|
212134
212868
|
current.outputBytes = values.outputBytes;
|
|
212135
212869
|
current.outputWarningTriggered = values.outputWarningTriggered;
|
|
212870
|
+
current.observedStreamRetries = values.observedStreamRetries;
|
|
212871
|
+
current.discardedRetryMessageBytes = values.discardedRetryMessageBytes;
|
|
212872
|
+
current.firstOutputAt = values.firstOutputAt;
|
|
212873
|
+
current.lastAcpUpdateAt = values.lastAcpUpdateAt;
|
|
212136
212874
|
current.salvaged = values.salvaged;
|
|
212137
212875
|
current.reportedFindings = values.reportedFindings;
|
|
212138
212876
|
current.findingsTargetExceeded = values.findingsTargetExceeded;
|
|
@@ -212247,6 +212985,12 @@ class ReviewBudgetTracker {
|
|
|
212247
212985
|
...reservation.thoughtBytes !== undefined ? { thoughtBytes: reservation.thoughtBytes } : {},
|
|
212248
212986
|
...reservation.outputBytes !== undefined ? { outputBytes: reservation.outputBytes } : {},
|
|
212249
212987
|
...reservation.outputWarningTriggered !== undefined ? { outputWarningTriggered: reservation.outputWarningTriggered } : {},
|
|
212988
|
+
...reservation.observedStreamRetries !== undefined ? { observedStreamRetries: reservation.observedStreamRetries } : {},
|
|
212989
|
+
...reservation.discardedRetryMessageBytes !== undefined ? {
|
|
212990
|
+
discardedRetryMessageBytes: reservation.discardedRetryMessageBytes
|
|
212991
|
+
} : {},
|
|
212992
|
+
...reservation.firstOutputAt !== undefined ? { firstOutputAt: reservation.firstOutputAt } : {},
|
|
212993
|
+
...reservation.lastAcpUpdateAt !== undefined ? { lastAcpUpdateAt: reservation.lastAcpUpdateAt } : {},
|
|
212250
212994
|
...reservation.salvaged !== undefined ? { salvaged: reservation.salvaged } : {},
|
|
212251
212995
|
...reservation.reportedFindings !== undefined ? { reportedFindings: reservation.reportedFindings } : {},
|
|
212252
212996
|
...reservation.findingsTargetExceeded !== undefined ? { findingsTargetExceeded: reservation.findingsTargetExceeded } : {},
|
|
@@ -212287,7 +213031,7 @@ function emptyReviewModelCallPlan() {
|
|
|
212287
213031
|
ceilingEffects: []
|
|
212288
213032
|
};
|
|
212289
213033
|
}
|
|
212290
|
-
function
|
|
213034
|
+
function isRecord18(value) {
|
|
212291
213035
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
212292
213036
|
}
|
|
212293
213037
|
|
|
@@ -212349,7 +213093,7 @@ function parseVerificationVerdicts(rawText) {
|
|
|
212349
213093
|
if (!Array.isArray(parsed.verdicts))
|
|
212350
213094
|
return;
|
|
212351
213095
|
return parsed.verdicts.flatMap((item) => {
|
|
212352
|
-
if (!
|
|
213096
|
+
if (!isRecord19(item))
|
|
212353
213097
|
return [];
|
|
212354
213098
|
if (typeof item.findingId !== "string")
|
|
212355
213099
|
return [];
|
|
@@ -212427,11 +213171,12 @@ function verificationNote(reasoning) {
|
|
|
212427
213171
|
function isVerdict(value) {
|
|
212428
213172
|
return value === "confirmed" || value === "refuted" || value === "uncertain";
|
|
212429
213173
|
}
|
|
212430
|
-
function
|
|
213174
|
+
function isRecord19(value) {
|
|
212431
213175
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
212432
213176
|
}
|
|
212433
213177
|
|
|
212434
213178
|
// src/core/runReview.ts
|
|
213179
|
+
var MAX_AGENT_RETRY_PROGRESS_EVENTS = 100;
|
|
212435
213180
|
function requestForRecursionFingerprint(request) {
|
|
212436
213181
|
try {
|
|
212437
213182
|
return scanAndRedactSecrets(request).redactedRequest;
|
|
@@ -212447,138 +213192,368 @@ async function runReview(tool, request, options = {}) {
|
|
|
212447
213192
|
const auditEnv = { ...process.env, ...options.env };
|
|
212448
213193
|
const traceWriterFactory = options.traceWriterFactory ?? createTraceWriter;
|
|
212449
213194
|
let snapshot;
|
|
213195
|
+
let activeTrace;
|
|
213196
|
+
let progressDeliveryFailure;
|
|
213197
|
+
let writeProgressDeliveryFailure;
|
|
213198
|
+
let reviewFailureReported = false;
|
|
213199
|
+
const dispatcher = createProgressDispatcher(options.onProgress, {
|
|
213200
|
+
onSinkDisabled: (reason) => {
|
|
213201
|
+
if (progressDeliveryFailure !== undefined)
|
|
213202
|
+
return;
|
|
213203
|
+
progressDeliveryFailure = reason;
|
|
213204
|
+
writeProgressDeliveryFailure?.(reason);
|
|
213205
|
+
}
|
|
213206
|
+
});
|
|
213207
|
+
const phaseStartedAt = new Map;
|
|
213208
|
+
const startPhase = (phase) => {
|
|
213209
|
+
throwIfAborted(options.signal);
|
|
213210
|
+
phaseStartedAt.set(phase, Date.now());
|
|
213211
|
+
dispatcher.emit({
|
|
213212
|
+
type: "phase_started",
|
|
213213
|
+
traceId,
|
|
213214
|
+
phase,
|
|
213215
|
+
timestamp: new Date().toISOString()
|
|
213216
|
+
});
|
|
213217
|
+
};
|
|
213218
|
+
const completePhase = (phase) => {
|
|
213219
|
+
dispatcher.emit({
|
|
213220
|
+
type: "phase_completed",
|
|
213221
|
+
traceId,
|
|
213222
|
+
phase,
|
|
213223
|
+
durationMs: Math.max(0, Date.now() - (phaseStartedAt.get(phase) ?? Date.now())),
|
|
213224
|
+
timestamp: new Date().toISOString()
|
|
213225
|
+
});
|
|
213226
|
+
};
|
|
213227
|
+
const skipPhase = (phase, reason) => {
|
|
213228
|
+
dispatcher.emit({
|
|
213229
|
+
type: "phase_skipped",
|
|
213230
|
+
traceId,
|
|
213231
|
+
phase,
|
|
213232
|
+
reason,
|
|
213233
|
+
timestamp: new Date().toISOString()
|
|
213234
|
+
});
|
|
213235
|
+
};
|
|
213236
|
+
const reportReviewCompleted = async (result) => {
|
|
213237
|
+
dispatcher.emit({
|
|
213238
|
+
type: "review_completed",
|
|
213239
|
+
traceId,
|
|
213240
|
+
decision: result.decision,
|
|
213241
|
+
completionStatus: result.completion.status,
|
|
213242
|
+
durationMs: Math.max(0, Date.now() - startedAtEpochMs),
|
|
213243
|
+
timestamp: new Date().toISOString()
|
|
213244
|
+
});
|
|
213245
|
+
await dispatcher.flush();
|
|
213246
|
+
if (progressDeliveryFailure !== undefined) {
|
|
213247
|
+
result.audit.warnings = Array.from(new Set([
|
|
213248
|
+
...result.audit.warnings ?? [],
|
|
213249
|
+
`PROGRESS_DELIVERY_FAILED: ${progressDeliveryFailure}`
|
|
213250
|
+
]));
|
|
213251
|
+
}
|
|
213252
|
+
};
|
|
213253
|
+
const completeReview = async (result) => {
|
|
213254
|
+
await reportReviewCompleted(result);
|
|
213255
|
+
return result;
|
|
213256
|
+
};
|
|
213257
|
+
const reportReviewFailure = async (error51, trace) => {
|
|
213258
|
+
if (reviewFailureReported)
|
|
213259
|
+
return;
|
|
213260
|
+
reviewFailureReported = true;
|
|
213261
|
+
const timestamp = new Date().toISOString();
|
|
213262
|
+
if (error51 instanceof KyosoCancellationError) {
|
|
213263
|
+
dispatcher.emit({ type: "review_cancelled", traceId, timestamp });
|
|
213264
|
+
await trace?.write({ type: "review_cancelled", traceId, timestamp }).catch(() => {
|
|
213265
|
+
return;
|
|
213266
|
+
});
|
|
213267
|
+
} else {
|
|
213268
|
+
dispatcher.emit({
|
|
213269
|
+
type: "review_failed",
|
|
213270
|
+
traceId,
|
|
213271
|
+
...error51 instanceof KyosoRequestError ? { errorCode: error51.code } : {},
|
|
213272
|
+
timestamp
|
|
213273
|
+
});
|
|
213274
|
+
await trace?.write({
|
|
213275
|
+
type: "review_failed",
|
|
213276
|
+
traceId,
|
|
213277
|
+
...error51 instanceof KyosoRequestError ? { errorCode: error51.code } : {},
|
|
213278
|
+
timestamp
|
|
213279
|
+
}).catch(() => {
|
|
213280
|
+
return;
|
|
213281
|
+
});
|
|
213282
|
+
}
|
|
213283
|
+
await dispatcher.flush();
|
|
213284
|
+
};
|
|
213285
|
+
dispatcher.emit({
|
|
213286
|
+
type: "review_started",
|
|
213287
|
+
traceId,
|
|
213288
|
+
tool,
|
|
213289
|
+
timestamp: new Date().toISOString()
|
|
213290
|
+
});
|
|
212450
213291
|
try {
|
|
212451
|
-
|
|
212452
|
-
|
|
212453
|
-
|
|
212454
|
-
|
|
212455
|
-
|
|
212456
|
-
|
|
212457
|
-
|
|
213292
|
+
try {
|
|
213293
|
+
throwIfAborted(options.signal);
|
|
213294
|
+
assertNotChildAgent(options.env ?? process.env);
|
|
213295
|
+
} catch (error51) {
|
|
213296
|
+
if (error51 instanceof KyosoRequestError) {
|
|
213297
|
+
const config2 = kyosoConfigSchema.parse(defaultConfig);
|
|
213298
|
+
const reviewBudget = resolveReviewBudget(config2.reviewBudget, undefined);
|
|
213299
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(config2, reviewBudget, options.env ?? process.env));
|
|
213300
|
+
const requestFingerprint = createRequestFingerprint({
|
|
213301
|
+
tool,
|
|
213302
|
+
request: requestForRecursionFingerprint(request),
|
|
213303
|
+
config: config2,
|
|
213304
|
+
roles: resolveAgentRoles(config2),
|
|
213305
|
+
budget: reviewBudget,
|
|
213306
|
+
entrypoint: options.entrypoint
|
|
213307
|
+
});
|
|
213308
|
+
const trace2 = traceWriterFactory({
|
|
213309
|
+
enabled: config2.audit.enabled,
|
|
213310
|
+
directory: config2.audit.directory,
|
|
213311
|
+
traceId,
|
|
213312
|
+
cwd,
|
|
213313
|
+
env: auditEnv
|
|
213314
|
+
});
|
|
213315
|
+
activeTrace = trace2;
|
|
213316
|
+
writeProgressDeliveryFailure = (reason) => {
|
|
213317
|
+
trace2.write({
|
|
213318
|
+
type: "progress_delivery_failed",
|
|
213319
|
+
traceId,
|
|
213320
|
+
reason,
|
|
213321
|
+
timestamp: new Date().toISOString()
|
|
213322
|
+
}).catch(() => {
|
|
213323
|
+
return;
|
|
213324
|
+
});
|
|
213325
|
+
};
|
|
213326
|
+
if (progressDeliveryFailure !== undefined) {
|
|
213327
|
+
writeProgressDeliveryFailure(progressDeliveryFailure);
|
|
213328
|
+
}
|
|
213329
|
+
try {
|
|
213330
|
+
await trace2.write({
|
|
213331
|
+
type: "request_received",
|
|
213332
|
+
traceId,
|
|
213333
|
+
tool,
|
|
213334
|
+
timestamp: new Date().toISOString()
|
|
213335
|
+
});
|
|
213336
|
+
await writeReviewBudgetPlanned({
|
|
213337
|
+
trace: trace2,
|
|
213338
|
+
traceId,
|
|
213339
|
+
budgetTracker,
|
|
213340
|
+
requestFingerprint
|
|
213341
|
+
});
|
|
213342
|
+
return await completeReview(await buildPolicyBlockResult({
|
|
213343
|
+
tool,
|
|
213344
|
+
trace: trace2,
|
|
213345
|
+
traceId,
|
|
213346
|
+
startedAt,
|
|
213347
|
+
networkMode: config2.network.defaultMode,
|
|
213348
|
+
cisaPolicy: config2.securityReview.cisaSecureByDesign,
|
|
213349
|
+
warning: error51.message,
|
|
213350
|
+
budgetTracker,
|
|
213351
|
+
requestFingerprint,
|
|
213352
|
+
coverage: unavailableReviewCoverage(requestForRecursionFingerprint(request), "recursive invocation blocked before agent execution", config2.reviewPolicy.additionalLenses),
|
|
213353
|
+
finding: {
|
|
213354
|
+
id: "KYOSO-1",
|
|
213355
|
+
severity: "critical",
|
|
213356
|
+
category: "other",
|
|
213357
|
+
title: "Recursive Kyoso invocation blocked",
|
|
213358
|
+
evidence: error51.message,
|
|
213359
|
+
recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
|
|
213360
|
+
disposition: "gate",
|
|
213361
|
+
changeRelation: "unknown",
|
|
213362
|
+
evidenceQuality: "concrete",
|
|
213363
|
+
evidenceRefs: [],
|
|
213364
|
+
policyReasons: ["kyoso_policy", "recursive_invocation"],
|
|
213365
|
+
fingerprint: "",
|
|
213366
|
+
sourceAgents: ["kyoso_policy"],
|
|
213367
|
+
confidence: "high"
|
|
213368
|
+
},
|
|
213369
|
+
redactionsApplied: 0
|
|
213370
|
+
}));
|
|
213371
|
+
} finally {
|
|
213372
|
+
await trace2.finalize();
|
|
213373
|
+
}
|
|
213374
|
+
}
|
|
213375
|
+
throw error51;
|
|
213376
|
+
}
|
|
213377
|
+
startPhase("preflight");
|
|
213378
|
+
const baseLoaded = options.config !== undefined ? {
|
|
213379
|
+
config: options.config,
|
|
213380
|
+
configHash: options.configHash,
|
|
213381
|
+
configTrustStatus: "trusted",
|
|
213382
|
+
sources: [],
|
|
213383
|
+
warnings: []
|
|
213384
|
+
} : await loadConfig({
|
|
213385
|
+
cwd,
|
|
213386
|
+
configPath: options.configPath,
|
|
213387
|
+
ignoreConfig: options.ignoreConfig,
|
|
213388
|
+
trustConfig: options.trustConfig,
|
|
213389
|
+
allowUnknownConfig: options.allowUnknownConfig,
|
|
213390
|
+
promptForTrust: options.promptForTrust,
|
|
213391
|
+
trustStorePath: options.trustStorePath,
|
|
213392
|
+
env: options.env,
|
|
213393
|
+
trustPrompt: options.trustPrompt
|
|
213394
|
+
});
|
|
213395
|
+
const loaded = options.configOverrides && options.configOverrides.length > 0 ? {
|
|
213396
|
+
...baseLoaded,
|
|
213397
|
+
config: applyConfigOverrides(baseLoaded.config, options.configOverrides)
|
|
213398
|
+
} : baseLoaded;
|
|
213399
|
+
completePhase("preflight");
|
|
213400
|
+
const trace = traceWriterFactory({
|
|
213401
|
+
enabled: loaded.config.audit.enabled,
|
|
213402
|
+
directory: loaded.config.audit.directory,
|
|
213403
|
+
traceId,
|
|
213404
|
+
cwd,
|
|
213405
|
+
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput,
|
|
213406
|
+
env: auditEnv
|
|
213407
|
+
});
|
|
213408
|
+
activeTrace = trace;
|
|
213409
|
+
const warnings = [...loaded.warnings, ...trace.warnings];
|
|
213410
|
+
writeProgressDeliveryFailure = (reason) => {
|
|
213411
|
+
warnings.push(`PROGRESS_DELIVERY_FAILED: ${reason}`);
|
|
213412
|
+
trace.write({
|
|
213413
|
+
type: "progress_delivery_failed",
|
|
213414
|
+
traceId,
|
|
213415
|
+
reason,
|
|
213416
|
+
timestamp: new Date().toISOString()
|
|
213417
|
+
}).catch(() => {
|
|
213418
|
+
return;
|
|
213419
|
+
});
|
|
213420
|
+
};
|
|
213421
|
+
if (progressDeliveryFailure !== undefined) {
|
|
213422
|
+
writeProgressDeliveryFailure(progressDeliveryFailure);
|
|
213423
|
+
}
|
|
213424
|
+
try {
|
|
213425
|
+
await trace.write({
|
|
213426
|
+
type: "request_received",
|
|
213427
|
+
traceId,
|
|
212458
213428
|
tool,
|
|
212459
|
-
|
|
212460
|
-
config: config2,
|
|
212461
|
-
roles: resolveAgentRoles(config2),
|
|
212462
|
-
budget: reviewBudget,
|
|
212463
|
-
entrypoint: options.entrypoint
|
|
213429
|
+
timestamp: new Date().toISOString()
|
|
212464
213430
|
});
|
|
212465
|
-
|
|
212466
|
-
|
|
212467
|
-
directory: config2.audit.directory,
|
|
213431
|
+
await trace.write({
|
|
213432
|
+
type: "config_loaded",
|
|
212468
213433
|
traceId,
|
|
212469
|
-
|
|
212470
|
-
|
|
213434
|
+
configHash: loaded.configHash,
|
|
213435
|
+
configPath: loaded.configPath,
|
|
213436
|
+
configSources: loaded.sources,
|
|
213437
|
+
configTrustStatus: loaded.configTrustStatus,
|
|
213438
|
+
timestamp: new Date().toISOString()
|
|
212471
213439
|
});
|
|
212472
|
-
|
|
212473
|
-
|
|
212474
|
-
|
|
212475
|
-
|
|
213440
|
+
validateReviewRequest(tool, request);
|
|
213441
|
+
const reviewBudget = resolveReviewBudget(loaded.config.reviewBudget, request.options?.reviewBudget);
|
|
213442
|
+
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(loaded.config, reviewBudget, options.env ?? process.env, request.options?.judgeProvider));
|
|
213443
|
+
assertTrustedWorkspaceRoot(request.workspace?.root, loaded.config.workspace.root, cwd);
|
|
213444
|
+
const networkMode = resolveNetworkMode(request.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
|
|
213445
|
+
if (networkMode === "unrestricted" && !loaded.config.network.allowUnrestricted) {
|
|
213446
|
+
throw new KyosoRequestError("unrestricted network mode is disabled by config", "NETWORK_MODE_DISABLED");
|
|
213447
|
+
}
|
|
213448
|
+
const disabledPolicy = disabledReviewPolicy(tool, loaded.config, options.entrypoint);
|
|
213449
|
+
if (disabledPolicy) {
|
|
213450
|
+
const redactedRequest = requestForRecursionFingerprint(request);
|
|
213451
|
+
const requestFingerprint2 = createRequestFingerprint({
|
|
212476
213452
|
tool,
|
|
212477
|
-
|
|
213453
|
+
request: redactedRequest,
|
|
213454
|
+
config: loaded.config,
|
|
213455
|
+
roles: resolveAgentRoles(loaded.config),
|
|
213456
|
+
budget: reviewBudget,
|
|
213457
|
+
entrypoint: options.entrypoint
|
|
212478
213458
|
});
|
|
212479
213459
|
await writeReviewBudgetPlanned({
|
|
212480
|
-
trace
|
|
213460
|
+
trace,
|
|
212481
213461
|
traceId,
|
|
212482
213462
|
budgetTracker,
|
|
212483
|
-
requestFingerprint
|
|
213463
|
+
requestFingerprint: requestFingerprint2
|
|
212484
213464
|
});
|
|
212485
|
-
|
|
213465
|
+
const warning = disabledPolicy.warning;
|
|
213466
|
+
return await completeReview(await buildPolicyBlockResult({
|
|
212486
213467
|
tool,
|
|
212487
|
-
trace
|
|
213468
|
+
trace,
|
|
212488
213469
|
traceId,
|
|
212489
213470
|
startedAt,
|
|
212490
|
-
|
|
212491
|
-
|
|
212492
|
-
|
|
213471
|
+
configHash: loaded.configHash,
|
|
213472
|
+
networkMode,
|
|
213473
|
+
cisaPolicy: loaded.config.securityReview.cisaSecureByDesign,
|
|
213474
|
+
warning,
|
|
212493
213475
|
budgetTracker,
|
|
212494
|
-
requestFingerprint,
|
|
212495
|
-
coverage: unavailableReviewCoverage(
|
|
213476
|
+
requestFingerprint: requestFingerprint2,
|
|
213477
|
+
coverage: unavailableReviewCoverage(redactedRequest, disabledPolicy.coverageReason, loaded.config.reviewPolicy.additionalLenses),
|
|
212496
213478
|
finding: {
|
|
212497
213479
|
id: "KYOSO-1",
|
|
212498
213480
|
severity: "critical",
|
|
212499
213481
|
category: "other",
|
|
212500
|
-
title:
|
|
212501
|
-
evidence:
|
|
212502
|
-
recommendation:
|
|
213482
|
+
title: disabledPolicy.title,
|
|
213483
|
+
evidence: warning,
|
|
213484
|
+
recommendation: disabledPolicy.recommendation,
|
|
212503
213485
|
disposition: "gate",
|
|
212504
213486
|
changeRelation: "unknown",
|
|
212505
213487
|
evidenceQuality: "concrete",
|
|
212506
213488
|
evidenceRefs: [],
|
|
212507
|
-
policyReasons: ["kyoso_policy",
|
|
213489
|
+
policyReasons: ["kyoso_policy", disabledPolicy.policyReason],
|
|
212508
213490
|
fingerprint: "",
|
|
212509
213491
|
sourceAgents: ["kyoso_policy"],
|
|
212510
213492
|
confidence: "high"
|
|
212511
213493
|
},
|
|
212512
213494
|
redactionsApplied: 0
|
|
213495
|
+
}));
|
|
213496
|
+
}
|
|
213497
|
+
if (networkMode === "unrestricted" && loaded.config.network.warnOnUnrestricted) {
|
|
213498
|
+
warnings.push("Network mode is unrestricted; write policy remains denied.");
|
|
213499
|
+
}
|
|
213500
|
+
startPhase("context");
|
|
213501
|
+
const secretScan = scanAndRedactSecrets(request);
|
|
213502
|
+
await trace.write({
|
|
213503
|
+
type: "secret_scan_completed",
|
|
213504
|
+
traceId,
|
|
213505
|
+
detected: secretScan.detected,
|
|
213506
|
+
redactions: secretScan.redactions,
|
|
213507
|
+
timestamp: new Date().toISOString()
|
|
213508
|
+
});
|
|
213509
|
+
const allowSecretOverride = loaded.config.secrets.allowOverride && request.options?.allowSecretRedaction === true;
|
|
213510
|
+
if (secretScan.detected && loaded.config.secrets.blockOnDetectedSecret && !allowSecretOverride) {
|
|
213511
|
+
const requestFingerprint2 = createRequestFingerprint({
|
|
213512
|
+
tool,
|
|
213513
|
+
request: secretScan.redactedRequest,
|
|
213514
|
+
config: loaded.config,
|
|
213515
|
+
roles: resolveAgentRoles(loaded.config),
|
|
213516
|
+
budget: reviewBudget,
|
|
213517
|
+
entrypoint: options.entrypoint
|
|
212513
213518
|
});
|
|
212514
|
-
|
|
212515
|
-
|
|
213519
|
+
await writeReviewBudgetPlanned({
|
|
213520
|
+
trace,
|
|
213521
|
+
traceId,
|
|
213522
|
+
budgetTracker,
|
|
213523
|
+
requestFingerprint: requestFingerprint2
|
|
213524
|
+
});
|
|
213525
|
+
skipPhase("context", "secret_detected");
|
|
213526
|
+
return await completeReview(await buildSecretBlockResult({
|
|
213527
|
+
tool,
|
|
213528
|
+
trace,
|
|
213529
|
+
traceId,
|
|
213530
|
+
startedAt,
|
|
213531
|
+
configHash: loaded.configHash,
|
|
213532
|
+
networkMode,
|
|
213533
|
+
cisaPolicy: loaded.config.securityReview.cisaSecureByDesign,
|
|
213534
|
+
additionalLenses: loaded.config.reviewPolicy.additionalLenses,
|
|
213535
|
+
secretScan,
|
|
213536
|
+
warnings,
|
|
213537
|
+
budgetTracker,
|
|
213538
|
+
requestFingerprint: requestFingerprint2
|
|
213539
|
+
}));
|
|
212516
213540
|
}
|
|
212517
|
-
|
|
212518
|
-
|
|
212519
|
-
|
|
212520
|
-
|
|
212521
|
-
|
|
212522
|
-
|
|
212523
|
-
|
|
212524
|
-
|
|
212525
|
-
|
|
212526
|
-
|
|
212527
|
-
|
|
212528
|
-
|
|
212529
|
-
ignoreConfig: options.ignoreConfig,
|
|
212530
|
-
trustConfig: options.trustConfig,
|
|
212531
|
-
allowUnknownConfig: options.allowUnknownConfig,
|
|
212532
|
-
promptForTrust: options.promptForTrust,
|
|
212533
|
-
trustStorePath: options.trustStorePath,
|
|
212534
|
-
env: options.env,
|
|
212535
|
-
trustPrompt: options.trustPrompt
|
|
212536
|
-
});
|
|
212537
|
-
const loaded = options.configOverrides && options.configOverrides.length > 0 ? {
|
|
212538
|
-
...baseLoaded,
|
|
212539
|
-
config: applyConfigOverrides(baseLoaded.config, options.configOverrides)
|
|
212540
|
-
} : baseLoaded;
|
|
212541
|
-
const trace = traceWriterFactory({
|
|
212542
|
-
enabled: loaded.config.audit.enabled,
|
|
212543
|
-
directory: loaded.config.audit.directory,
|
|
212544
|
-
traceId,
|
|
212545
|
-
cwd,
|
|
212546
|
-
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput,
|
|
212547
|
-
env: auditEnv
|
|
212548
|
-
});
|
|
212549
|
-
const warnings = [...loaded.warnings, ...trace.warnings];
|
|
212550
|
-
try {
|
|
212551
|
-
await trace.write({
|
|
212552
|
-
type: "request_received",
|
|
212553
|
-
traceId,
|
|
212554
|
-
tool,
|
|
212555
|
-
timestamp: new Date().toISOString()
|
|
212556
|
-
});
|
|
212557
|
-
await trace.write({
|
|
212558
|
-
type: "config_loaded",
|
|
212559
|
-
traceId,
|
|
212560
|
-
configHash: loaded.configHash,
|
|
212561
|
-
configPath: loaded.configPath,
|
|
212562
|
-
configSources: loaded.sources,
|
|
212563
|
-
configTrustStatus: loaded.configTrustStatus,
|
|
212564
|
-
timestamp: new Date().toISOString()
|
|
212565
|
-
});
|
|
212566
|
-
validateReviewRequest(tool, request);
|
|
212567
|
-
const reviewBudget = resolveReviewBudget(loaded.config.reviewBudget, request.options?.reviewBudget);
|
|
212568
|
-
const budgetTracker = new ReviewBudgetTracker(reviewBudget, startedAtEpochMs, configuredReviewModelCallPlan(loaded.config, reviewBudget, options.env ?? process.env, request.options?.judgeProvider));
|
|
212569
|
-
assertTrustedWorkspaceRoot(request.workspace?.root, loaded.config.workspace.root, cwd);
|
|
212570
|
-
const networkMode = resolveNetworkMode(request.options?.network, loaded.config.network.defaultMode, options.mcpNetworkMode);
|
|
212571
|
-
if (networkMode === "unrestricted" && !loaded.config.network.allowUnrestricted) {
|
|
212572
|
-
throw new KyosoRequestError("unrestricted network mode is disabled by config", "NETWORK_MODE_DISABLED");
|
|
212573
|
-
}
|
|
212574
|
-
const disabledPolicy = disabledReviewPolicy(tool, loaded.config, options.entrypoint);
|
|
212575
|
-
if (disabledPolicy) {
|
|
212576
|
-
const redactedRequest = requestForRecursionFingerprint(request);
|
|
212577
|
-
const requestFingerprint2 = createRequestFingerprint({
|
|
213541
|
+
const denyPatterns = mergeDenyPatterns(loaded.config.workspace.deny, secretScan.redactedRequest.workspace?.denyRead);
|
|
213542
|
+
const allowPatterns = secretScan.redactedRequest.workspace?.allowRead ?? [];
|
|
213543
|
+
const built = buildContext(secretScan.redactedRequest, {
|
|
213544
|
+
maxContextBytes: loaded.config.workspace.maxContextBytes,
|
|
213545
|
+
maxDiffBytes: loaded.config.workspace.maxDiffBytes,
|
|
213546
|
+
denyPatterns,
|
|
213547
|
+
allowPatterns
|
|
213548
|
+
});
|
|
213549
|
+
warnings.push(...built.warnings);
|
|
213550
|
+
completePhase("context");
|
|
213551
|
+
const agentRoles = resolveAgentRoles(loaded.config);
|
|
213552
|
+
const requestFingerprint = createRequestFingerprint({
|
|
212578
213553
|
tool,
|
|
212579
|
-
request:
|
|
213554
|
+
request: built.request,
|
|
212580
213555
|
config: loaded.config,
|
|
212581
|
-
roles:
|
|
213556
|
+
roles: agentRoles,
|
|
212582
213557
|
budget: reviewBudget,
|
|
212583
213558
|
entrypoint: options.entrypoint
|
|
212584
213559
|
});
|
|
@@ -212586,370 +213561,308 @@ async function runReview(tool, request, options = {}) {
|
|
|
212586
213561
|
trace,
|
|
212587
213562
|
traceId,
|
|
212588
213563
|
budgetTracker,
|
|
212589
|
-
requestFingerprint
|
|
213564
|
+
requestFingerprint
|
|
212590
213565
|
});
|
|
212591
|
-
|
|
212592
|
-
|
|
212593
|
-
|
|
212594
|
-
|
|
212595
|
-
|
|
212596
|
-
|
|
212597
|
-
configHash: loaded.configHash,
|
|
212598
|
-
networkMode,
|
|
212599
|
-
cisaPolicy: loaded.config.securityReview.cisaSecureByDesign,
|
|
212600
|
-
warning,
|
|
212601
|
-
budgetTracker,
|
|
212602
|
-
requestFingerprint: requestFingerprint2,
|
|
212603
|
-
coverage: unavailableReviewCoverage(redactedRequest, disabledPolicy.coverageReason, loaded.config.reviewPolicy.additionalLenses),
|
|
212604
|
-
finding: {
|
|
212605
|
-
id: "KYOSO-1",
|
|
212606
|
-
severity: "critical",
|
|
212607
|
-
category: "other",
|
|
212608
|
-
title: disabledPolicy.title,
|
|
212609
|
-
evidence: warning,
|
|
212610
|
-
recommendation: disabledPolicy.recommendation,
|
|
212611
|
-
disposition: "gate",
|
|
212612
|
-
changeRelation: "unknown",
|
|
212613
|
-
evidenceQuality: "concrete",
|
|
212614
|
-
evidenceRefs: [],
|
|
212615
|
-
policyReasons: ["kyoso_policy", disabledPolicy.policyReason],
|
|
212616
|
-
fingerprint: "",
|
|
212617
|
-
sourceAgents: ["kyoso_policy"],
|
|
212618
|
-
confidence: "high"
|
|
212619
|
-
},
|
|
212620
|
-
redactionsApplied: 0
|
|
212621
|
-
});
|
|
212622
|
-
}
|
|
212623
|
-
if (networkMode === "unrestricted" && loaded.config.network.warnOnUnrestricted) {
|
|
212624
|
-
warnings.push("Network mode is unrestricted; write policy remains denied.");
|
|
212625
|
-
}
|
|
212626
|
-
const secretScan = scanAndRedactSecrets(request);
|
|
212627
|
-
await trace.write({
|
|
212628
|
-
type: "secret_scan_completed",
|
|
212629
|
-
traceId,
|
|
212630
|
-
detected: secretScan.detected,
|
|
212631
|
-
redactions: secretScan.redactions,
|
|
212632
|
-
timestamp: new Date().toISOString()
|
|
212633
|
-
});
|
|
212634
|
-
const allowSecretOverride = loaded.config.secrets.allowOverride && request.options?.allowSecretRedaction === true;
|
|
212635
|
-
if (secretScan.detected && loaded.config.secrets.blockOnDetectedSecret && !allowSecretOverride) {
|
|
212636
|
-
const requestFingerprint2 = createRequestFingerprint({
|
|
212637
|
-
tool,
|
|
212638
|
-
request: secretScan.redactedRequest,
|
|
212639
|
-
config: loaded.config,
|
|
212640
|
-
roles: resolveAgentRoles(loaded.config),
|
|
212641
|
-
budget: reviewBudget,
|
|
212642
|
-
entrypoint: options.entrypoint
|
|
213566
|
+
warnings.push(...plannedBudgetWarnings(budgetTracker));
|
|
213567
|
+
startPhase("snapshot");
|
|
213568
|
+
snapshot = await createSnapshot(traceId, tool, built.request, {
|
|
213569
|
+
denyPatterns,
|
|
213570
|
+
allowPatterns,
|
|
213571
|
+
agentRoles
|
|
212643
213572
|
});
|
|
212644
|
-
await
|
|
212645
|
-
|
|
213573
|
+
await trace.write({
|
|
213574
|
+
type: "snapshot_created",
|
|
212646
213575
|
traceId,
|
|
212647
|
-
|
|
212648
|
-
|
|
213576
|
+
path: snapshot.root,
|
|
213577
|
+
fileCount: snapshot.fileCount,
|
|
213578
|
+
timestamp: new Date().toISOString()
|
|
212649
213579
|
});
|
|
212650
|
-
|
|
213580
|
+
completePhase("snapshot");
|
|
213581
|
+
const manager = options.agentManager ?? defaultAgentManager(loaded.config, options.env ?? process.env);
|
|
213582
|
+
startPhase("primary");
|
|
213583
|
+
const agentResults = await runAgents({
|
|
212651
213584
|
tool,
|
|
212652
|
-
|
|
213585
|
+
request: built.request,
|
|
213586
|
+
config: loaded.config,
|
|
212653
213587
|
traceId,
|
|
212654
|
-
|
|
212655
|
-
configHash: loaded.configHash,
|
|
213588
|
+
workspaceDir: snapshot.root,
|
|
212656
213589
|
networkMode,
|
|
212657
|
-
|
|
212658
|
-
|
|
212659
|
-
secretScan,
|
|
213590
|
+
manager,
|
|
213591
|
+
trace,
|
|
212660
213592
|
warnings,
|
|
212661
213593
|
budgetTracker,
|
|
212662
|
-
|
|
213594
|
+
progressDispatcher: dispatcher,
|
|
213595
|
+
signal: options.signal,
|
|
213596
|
+
progressHeartbeatMs: options.progressHeartbeatMs
|
|
212663
213597
|
});
|
|
212664
|
-
|
|
212665
|
-
|
|
212666
|
-
|
|
212667
|
-
|
|
212668
|
-
|
|
212669
|
-
|
|
212670
|
-
|
|
212671
|
-
|
|
212672
|
-
|
|
212673
|
-
|
|
212674
|
-
|
|
212675
|
-
|
|
212676
|
-
|
|
212677
|
-
|
|
212678
|
-
|
|
212679
|
-
|
|
212680
|
-
|
|
212681
|
-
|
|
212682
|
-
|
|
212683
|
-
|
|
212684
|
-
|
|
212685
|
-
|
|
212686
|
-
|
|
212687
|
-
|
|
212688
|
-
|
|
212689
|
-
|
|
212690
|
-
|
|
212691
|
-
|
|
212692
|
-
|
|
212693
|
-
|
|
212694
|
-
|
|
212695
|
-
|
|
212696
|
-
|
|
212697
|
-
|
|
212698
|
-
|
|
212699
|
-
|
|
212700
|
-
|
|
212701
|
-
|
|
212702
|
-
|
|
212703
|
-
|
|
212704
|
-
|
|
212705
|
-
|
|
212706
|
-
|
|
212707
|
-
|
|
212708
|
-
|
|
212709
|
-
|
|
212710
|
-
|
|
212711
|
-
|
|
212712
|
-
|
|
212713
|
-
|
|
212714
|
-
|
|
212715
|
-
|
|
212716
|
-
|
|
212717
|
-
|
|
212718
|
-
|
|
212719
|
-
|
|
212720
|
-
|
|
212721
|
-
|
|
212722
|
-
|
|
212723
|
-
|
|
212724
|
-
|
|
212725
|
-
|
|
212726
|
-
|
|
212727
|
-
|
|
212728
|
-
|
|
212729
|
-
|
|
212730
|
-
|
|
212731
|
-
|
|
212732
|
-
multiAgentRequired: loaded.config.reviewPolicy.multiAgentRequired
|
|
212733
|
-
})) {
|
|
212734
|
-
budgetTracker.markIncomplete("coverage_incomplete");
|
|
212735
|
-
warnings.push(formatCoverageWarning(coverage, loaded.config));
|
|
212736
|
-
}
|
|
212737
|
-
let aggregate = aggregateAgentResults(normalizedAgentResults, {
|
|
212738
|
-
reviewMode
|
|
212739
|
-
});
|
|
212740
|
-
if (secretScan.detected && allowSecretOverride) {
|
|
213598
|
+
completePhase("primary");
|
|
213599
|
+
warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
|
|
213600
|
+
startPhase("aggregation");
|
|
213601
|
+
const normalizedAgentResults = agentResults.map((result) => normalizeAgentRunResult(result, reviewBudget.maxFindingsPerAgent));
|
|
213602
|
+
for (const result of normalizedAgentResults.filter((item) => item.findingsTargetExceeded)) {
|
|
213603
|
+
warnings.push(`Agent ${result.agent} reported ${result.reportedFindings} findings, above the soft target of ${reviewBudget.maxFindingsPerAgent}; all findings were retained.`);
|
|
213604
|
+
}
|
|
213605
|
+
const enabledAgents = ["codex", "claude"].filter((agent) => loaded.config.agents[agent].enabled);
|
|
213606
|
+
const agentsUsed = normalizedAgentResults.filter((result) => result.status !== "skipped").map((result) => result.agent);
|
|
213607
|
+
const reviewMode = enabledAgents.length === 1 ? "single_agent" : "multi_agent";
|
|
213608
|
+
const completed = normalizedAgentResults.filter((result) => result.status === "completed");
|
|
213609
|
+
const attempted = normalizedAgentResults.filter((result) => result.status !== "skipped");
|
|
213610
|
+
const degraded = completed.length !== attempted.length || enabledAgents.length === 0;
|
|
213611
|
+
const coverage = buildReviewCoverage({
|
|
213612
|
+
request: built.request,
|
|
213613
|
+
additionalLenses: loaded.config.reviewPolicy.additionalLenses,
|
|
213614
|
+
agentResults: normalizedAgentResults
|
|
213615
|
+
});
|
|
213616
|
+
if (isCoverageIncomplete(coverage, {
|
|
213617
|
+
multiAgentRequired: loaded.config.reviewPolicy.multiAgentRequired
|
|
213618
|
+
})) {
|
|
213619
|
+
budgetTracker.markIncomplete("coverage_incomplete");
|
|
213620
|
+
warnings.push(formatCoverageWarning(coverage, loaded.config));
|
|
213621
|
+
}
|
|
213622
|
+
let aggregate = aggregateAgentResults(normalizedAgentResults, {
|
|
213623
|
+
reviewMode
|
|
213624
|
+
});
|
|
213625
|
+
if (secretScan.detected && allowSecretOverride) {
|
|
213626
|
+
aggregate = {
|
|
213627
|
+
...aggregate,
|
|
213628
|
+
findings: reindexFindings([
|
|
213629
|
+
buildSecretFinding(secretScan, {
|
|
213630
|
+
id: "KYOSO-1",
|
|
213631
|
+
blocked: false
|
|
213632
|
+
}),
|
|
213633
|
+
...aggregate.findings
|
|
213634
|
+
])
|
|
213635
|
+
};
|
|
213636
|
+
}
|
|
213637
|
+
if (completed.length === 0 && (attempted.length > 0 || enabledAgents.length === 0)) {
|
|
213638
|
+
const noPrimaryAgents = enabledAgents.length === 0;
|
|
213639
|
+
if (noPrimaryAgents) {
|
|
213640
|
+
budgetTracker.markIncomplete("coverage_incomplete");
|
|
213641
|
+
warnings.push("No primary review agents are enabled; review coverage is incomplete.");
|
|
213642
|
+
}
|
|
213643
|
+
aggregate = {
|
|
213644
|
+
...aggregate,
|
|
213645
|
+
findings: [
|
|
213646
|
+
...aggregate.findings,
|
|
213647
|
+
{
|
|
213648
|
+
id: `KYOSO-${aggregate.findings.length + 1}`,
|
|
213649
|
+
severity: "critical",
|
|
213650
|
+
category: "other",
|
|
213651
|
+
title: noPrimaryAgents ? "No primary review agents enabled" : "All backend agents failed",
|
|
213652
|
+
evidence: noPrimaryAgents ? "Both configured primary reviewers are disabled." : normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
213653
|
+
recommendation: noPrimaryAgents ? "Enable at least one primary reviewer before running Kyoso." : "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
213654
|
+
disposition: "gate",
|
|
213655
|
+
changeRelation: "unknown",
|
|
213656
|
+
evidenceQuality: "concrete",
|
|
213657
|
+
evidenceRefs: [],
|
|
213658
|
+
policyReasons: ["kyoso_policy", "coverage_incomplete"],
|
|
213659
|
+
fingerprint: "",
|
|
213660
|
+
sourceAgents: ["kyoso_policy"],
|
|
213661
|
+
confidence: "high"
|
|
213662
|
+
}
|
|
213663
|
+
]
|
|
213664
|
+
};
|
|
213665
|
+
}
|
|
212741
213666
|
aggregate = {
|
|
212742
213667
|
...aggregate,
|
|
212743
|
-
findings:
|
|
212744
|
-
|
|
212745
|
-
|
|
212746
|
-
|
|
212747
|
-
|
|
212748
|
-
|
|
212749
|
-
])
|
|
213668
|
+
findings: admitFindings({
|
|
213669
|
+
tool,
|
|
213670
|
+
request: built.request,
|
|
213671
|
+
findings: aggregate.findings,
|
|
213672
|
+
reviewMode
|
|
213673
|
+
})
|
|
212750
213674
|
};
|
|
212751
|
-
|
|
212752
|
-
|
|
212753
|
-
|
|
212754
|
-
|
|
212755
|
-
|
|
212756
|
-
|
|
213675
|
+
await trace.write({
|
|
213676
|
+
type: "aggregation_completed",
|
|
213677
|
+
traceId,
|
|
213678
|
+
findingCount: aggregate.findings.length,
|
|
213679
|
+
timestamp: new Date().toISOString()
|
|
213680
|
+
});
|
|
213681
|
+
completePhase("aggregation");
|
|
213682
|
+
const verificationMode = loaded.config.verification.enabled && reviewMode === "single_agent" ? "skipped_single_agent" : loaded.config.verification.enabled && enabledAgents.length > 1 ? "cross_agent" : undefined;
|
|
213683
|
+
if (verificationMode === "cross_agent") {
|
|
213684
|
+
startPhase("verification");
|
|
213685
|
+
warnings.push(...await runFindingVerification({
|
|
213686
|
+
tool,
|
|
213687
|
+
request: built.request,
|
|
213688
|
+
config: loaded.config,
|
|
213689
|
+
traceId,
|
|
213690
|
+
workspaceDir: snapshot.root,
|
|
213691
|
+
networkMode,
|
|
213692
|
+
manager,
|
|
213693
|
+
trace,
|
|
213694
|
+
findings: aggregate.findings,
|
|
213695
|
+
budgetTracker,
|
|
213696
|
+
signal: options.signal
|
|
213697
|
+
}));
|
|
213698
|
+
completePhase("verification");
|
|
213699
|
+
} else {
|
|
213700
|
+
skipPhase("verification", verificationMode === "skipped_single_agent" ? "single_agent_review" : "verification_disabled");
|
|
212757
213701
|
}
|
|
212758
213702
|
aggregate = {
|
|
212759
213703
|
...aggregate,
|
|
212760
|
-
findings:
|
|
212761
|
-
|
|
212762
|
-
|
|
212763
|
-
|
|
212764
|
-
|
|
212765
|
-
|
|
212766
|
-
title: noPrimaryAgents ? "No primary review agents enabled" : "All backend agents failed",
|
|
212767
|
-
evidence: noPrimaryAgents ? "Both configured primary reviewers are disabled." : normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
212768
|
-
recommendation: noPrimaryAgents ? "Enable at least one primary reviewer before running Kyoso." : "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
212769
|
-
disposition: "gate",
|
|
212770
|
-
changeRelation: "unknown",
|
|
212771
|
-
evidenceQuality: "concrete",
|
|
212772
|
-
evidenceRefs: [],
|
|
212773
|
-
policyReasons: ["kyoso_policy", "coverage_incomplete"],
|
|
212774
|
-
fingerprint: "",
|
|
212775
|
-
sourceAgents: ["kyoso_policy"],
|
|
212776
|
-
confidence: "high"
|
|
212777
|
-
}
|
|
212778
|
-
]
|
|
213704
|
+
findings: admitFindings({
|
|
213705
|
+
tool,
|
|
213706
|
+
request: built.request,
|
|
213707
|
+
findings: aggregate.findings,
|
|
213708
|
+
reviewMode
|
|
213709
|
+
})
|
|
212779
213710
|
};
|
|
212780
|
-
|
|
212781
|
-
|
|
212782
|
-
|
|
212783
|
-
|
|
213711
|
+
if (aggregate.findings.some((finding) => finding.disposition === "disputed")) {
|
|
213712
|
+
budgetTracker.markIncomplete("disputed_finding");
|
|
213713
|
+
}
|
|
213714
|
+
const cisaPolicy = loaded.config.securityReview.cisaSecureByDesign;
|
|
213715
|
+
const cisa = tool === "security_review" && cisaPolicy.enabled ? computeCisaGate(aggregate.findings, normalizedAgentResults, cisaPolicy) : undefined;
|
|
213716
|
+
const budgetBeforeJudge = budgetTracker.snapshot();
|
|
213717
|
+
const decision = budgetBeforeJudge.completion.status === "incomplete" ? "block" : decide({
|
|
212784
213718
|
tool,
|
|
212785
|
-
request: built.request,
|
|
212786
213719
|
findings: aggregate.findings,
|
|
212787
|
-
|
|
212788
|
-
|
|
212789
|
-
|
|
212790
|
-
|
|
212791
|
-
|
|
212792
|
-
|
|
212793
|
-
|
|
212794
|
-
|
|
212795
|
-
|
|
212796
|
-
|
|
212797
|
-
|
|
212798
|
-
|
|
213720
|
+
cisa: cisaPolicy.gate ? cisa : undefined,
|
|
213721
|
+
degraded,
|
|
213722
|
+
secretScan: { detected: secretScan.detected, blocked: false }
|
|
213723
|
+
});
|
|
213724
|
+
const completedAt = new Date().toISOString();
|
|
213725
|
+
const resultWithoutMarkdown = {
|
|
213726
|
+
decision,
|
|
213727
|
+
completion: budgetBeforeJudge.completion,
|
|
213728
|
+
executionBudget: budgetBeforeJudge.executionBudget,
|
|
213729
|
+
requestFingerprint,
|
|
213730
|
+
degraded,
|
|
213731
|
+
agentsUsed,
|
|
213732
|
+
reviewMode,
|
|
213733
|
+
coverage,
|
|
213734
|
+
...verificationMode ? { verificationMode } : {},
|
|
213735
|
+
findings: aggregate.findings,
|
|
213736
|
+
cisaSecureByDesign: cisa,
|
|
213737
|
+
disagreements: aggregate.disagreements,
|
|
213738
|
+
testsToAdd: selectRegressionTests(aggregate.testsToAdd),
|
|
213739
|
+
residualRisks: tool === "security_review" && aggregate.residualRisks.length === 0 ? [
|
|
213740
|
+
"No residual risks were reported by completed agents; verify security assumptions before release."
|
|
213741
|
+
] : aggregate.residualRisks,
|
|
213742
|
+
openQuestions: Array.from(new Set([
|
|
213743
|
+
...aggregate.openQuestions,
|
|
213744
|
+
...buildAdmissionOpenQuestions(aggregate.findings)
|
|
213745
|
+
])),
|
|
213746
|
+
agentOpinions: normalizedAgentResults.map((result) => agentOpinionSummary(result, request.options?.includeAgentRawOutputs === true)),
|
|
213747
|
+
audit: {
|
|
213748
|
+
traceId,
|
|
213749
|
+
startedAt,
|
|
213750
|
+
completedAt,
|
|
213751
|
+
agentsUsed,
|
|
213752
|
+
redactionsApplied: secretScan.redactions,
|
|
213753
|
+
networkMode,
|
|
213754
|
+
workspaceMode: "temp_snapshot",
|
|
213755
|
+
configHash: loaded.configHash,
|
|
213756
|
+
warnings: Array.from(new Set([...warnings, ...trace.warnings])),
|
|
213757
|
+
modelCalls: budgetBeforeJudge.modelCalls
|
|
213758
|
+
}
|
|
213759
|
+
};
|
|
213760
|
+
const summaryText = defaultSummaryText(resultWithoutMarkdown);
|
|
213761
|
+
startPhase("judge");
|
|
213762
|
+
const judge = await runBudgetedJudge({
|
|
212799
213763
|
tool,
|
|
212800
|
-
|
|
212801
|
-
|
|
212802
|
-
|
|
212803
|
-
|
|
212804
|
-
|
|
212805
|
-
|
|
213764
|
+
result: resultWithoutMarkdown,
|
|
213765
|
+
summaryText,
|
|
213766
|
+
agentFindings: buildJudgeAgentFindings(normalizedAgentResults),
|
|
213767
|
+
config: loaded.config.judge,
|
|
213768
|
+
requestedProvider: request.options?.judgeProvider,
|
|
213769
|
+
env: options.env ?? process.env,
|
|
213770
|
+
budgetTracker,
|
|
212806
213771
|
trace,
|
|
212807
|
-
|
|
212808
|
-
|
|
213772
|
+
traceId,
|
|
213773
|
+
signal: options.signal
|
|
213774
|
+
});
|
|
213775
|
+
completePhase("judge");
|
|
213776
|
+
startPhase("finalize");
|
|
213777
|
+
const judgeComments = new Map(judge.output.disagreementComments.map((comment) => [
|
|
213778
|
+
comment.topic,
|
|
213779
|
+
comment.judgeComment
|
|
213780
|
+
]));
|
|
213781
|
+
const disagreements = resultWithoutMarkdown.disagreements.map((disagreement) => ({
|
|
213782
|
+
...disagreement,
|
|
213783
|
+
judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
|
|
212809
213784
|
}));
|
|
212810
|
-
|
|
212811
|
-
|
|
212812
|
-
|
|
212813
|
-
|
|
213785
|
+
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
213786
|
+
const budgetAfterJudge = budgetTracker.snapshot();
|
|
213787
|
+
const finalWarnings = Array.from(new Set([
|
|
213788
|
+
...resultWithoutMarkdown.audit.warnings ?? [],
|
|
213789
|
+
...outputWarningMessages(budgetAfterJudge),
|
|
213790
|
+
...tokenUsageWarningMessages(budgetTracker, budgetAfterJudge)
|
|
213791
|
+
]));
|
|
213792
|
+
const finalDecision = budgetAfterJudge.completion.status === "incomplete" ? "block" : decision;
|
|
213793
|
+
const resultAfterJudge = {
|
|
213794
|
+
...resultWithoutMarkdown,
|
|
213795
|
+
decision: finalDecision,
|
|
213796
|
+
completion: budgetAfterJudge.completion,
|
|
213797
|
+
executionBudget: budgetAfterJudge.executionBudget,
|
|
213798
|
+
disagreements,
|
|
213799
|
+
...crossModelAnalysis ? { crossModelAnalysis } : {},
|
|
213800
|
+
audit: {
|
|
213801
|
+
...resultWithoutMarkdown.audit,
|
|
213802
|
+
completedAt: new Date().toISOString(),
|
|
213803
|
+
warnings: finalWarnings,
|
|
213804
|
+
modelCalls: budgetAfterJudge.modelCalls
|
|
213805
|
+
}
|
|
213806
|
+
};
|
|
213807
|
+
const judgeEvent = {
|
|
213808
|
+
type: "judge_completed",
|
|
213809
|
+
traceId,
|
|
213810
|
+
provider: judge.provider,
|
|
213811
|
+
status: judge.status,
|
|
213812
|
+
...judge.executionIdentity ? { executionIdentity: judge.executionIdentity } : {},
|
|
213813
|
+
timestamp: new Date().toISOString()
|
|
213814
|
+
};
|
|
213815
|
+
if (judge.error)
|
|
213816
|
+
judgeEvent.error = judge.error;
|
|
213817
|
+
await trace.write(judgeEvent);
|
|
213818
|
+
resultAfterJudge.audit.completedAt = new Date().toISOString();
|
|
213819
|
+
await writeReviewBudgetCompleted({
|
|
213820
|
+
trace,
|
|
213821
|
+
traceId,
|
|
213822
|
+
budgetTracker,
|
|
213823
|
+
requestFingerprint
|
|
213824
|
+
});
|
|
213825
|
+
await trace.write({
|
|
213826
|
+
type: "decision_completed",
|
|
213827
|
+
traceId,
|
|
213828
|
+
decision: finalDecision,
|
|
213829
|
+
timestamp: new Date().toISOString()
|
|
213830
|
+
});
|
|
213831
|
+
await trace.write({
|
|
213832
|
+
type: "response_sent",
|
|
213833
|
+
traceId,
|
|
213834
|
+
timestamp: new Date().toISOString()
|
|
213835
|
+
});
|
|
213836
|
+
completePhase("finalize");
|
|
213837
|
+
await reportReviewCompleted(resultAfterJudge);
|
|
213838
|
+
return await finalizeReviewResult({
|
|
212814
213839
|
tool,
|
|
212815
|
-
|
|
212816
|
-
|
|
212817
|
-
|
|
212818
|
-
})
|
|
212819
|
-
}
|
|
212820
|
-
|
|
212821
|
-
|
|
213840
|
+
trace,
|
|
213841
|
+
result: resultAfterJudge,
|
|
213842
|
+
summaryText: resultAfterJudge.completion.status === "incomplete" ? defaultSummaryText(resultAfterJudge) : judge.output.summaryText
|
|
213843
|
+
});
|
|
213844
|
+
} catch (error51) {
|
|
213845
|
+
await reportReviewFailure(error51, trace);
|
|
213846
|
+
throw error51;
|
|
213847
|
+
} finally {
|
|
213848
|
+
await trace.finalize();
|
|
213849
|
+
if (snapshot)
|
|
213850
|
+
await cleanupSnapshot(snapshot.root);
|
|
212822
213851
|
}
|
|
212823
|
-
|
|
212824
|
-
|
|
212825
|
-
|
|
212826
|
-
const decision = budgetBeforeJudge.completion.status === "incomplete" ? "block" : decide({
|
|
212827
|
-
tool,
|
|
212828
|
-
findings: aggregate.findings,
|
|
212829
|
-
cisa: cisaPolicy.gate ? cisa : undefined,
|
|
212830
|
-
degraded,
|
|
212831
|
-
secretScan: { detected: secretScan.detected, blocked: false }
|
|
212832
|
-
});
|
|
212833
|
-
const completedAt = new Date().toISOString();
|
|
212834
|
-
const resultWithoutMarkdown = {
|
|
212835
|
-
decision,
|
|
212836
|
-
completion: budgetBeforeJudge.completion,
|
|
212837
|
-
executionBudget: budgetBeforeJudge.executionBudget,
|
|
212838
|
-
requestFingerprint,
|
|
212839
|
-
degraded,
|
|
212840
|
-
agentsUsed,
|
|
212841
|
-
reviewMode,
|
|
212842
|
-
coverage,
|
|
212843
|
-
...verificationMode ? { verificationMode } : {},
|
|
212844
|
-
findings: aggregate.findings,
|
|
212845
|
-
cisaSecureByDesign: cisa,
|
|
212846
|
-
disagreements: aggregate.disagreements,
|
|
212847
|
-
testsToAdd: selectRegressionTests(aggregate.testsToAdd),
|
|
212848
|
-
residualRisks: tool === "security_review" && aggregate.residualRisks.length === 0 ? [
|
|
212849
|
-
"No residual risks were reported by completed agents; verify security assumptions before release."
|
|
212850
|
-
] : aggregate.residualRisks,
|
|
212851
|
-
openQuestions: Array.from(new Set([
|
|
212852
|
-
...aggregate.openQuestions,
|
|
212853
|
-
...buildAdmissionOpenQuestions(aggregate.findings)
|
|
212854
|
-
])),
|
|
212855
|
-
agentOpinions: normalizedAgentResults.map((result) => agentOpinionSummary(result, request.options?.includeAgentRawOutputs === true)),
|
|
212856
|
-
audit: {
|
|
212857
|
-
traceId,
|
|
212858
|
-
startedAt,
|
|
212859
|
-
completedAt,
|
|
212860
|
-
agentsUsed,
|
|
212861
|
-
redactionsApplied: secretScan.redactions,
|
|
212862
|
-
networkMode,
|
|
212863
|
-
workspaceMode: "temp_snapshot",
|
|
212864
|
-
configHash: loaded.configHash,
|
|
212865
|
-
warnings: Array.from(new Set([...warnings, ...trace.warnings])),
|
|
212866
|
-
modelCalls: budgetBeforeJudge.modelCalls
|
|
212867
|
-
}
|
|
212868
|
-
};
|
|
212869
|
-
const summaryText = defaultSummaryText(resultWithoutMarkdown);
|
|
212870
|
-
const judge = await runBudgetedJudge({
|
|
212871
|
-
tool,
|
|
212872
|
-
result: resultWithoutMarkdown,
|
|
212873
|
-
summaryText,
|
|
212874
|
-
agentFindings: buildJudgeAgentFindings(normalizedAgentResults),
|
|
212875
|
-
config: loaded.config.judge,
|
|
212876
|
-
requestedProvider: request.options?.judgeProvider,
|
|
212877
|
-
env: options.env ?? process.env,
|
|
212878
|
-
budgetTracker,
|
|
212879
|
-
trace,
|
|
212880
|
-
traceId
|
|
212881
|
-
});
|
|
212882
|
-
const judgeComments = new Map(judge.output.disagreementComments.map((comment) => [
|
|
212883
|
-
comment.topic,
|
|
212884
|
-
comment.judgeComment
|
|
212885
|
-
]));
|
|
212886
|
-
const disagreements = resultWithoutMarkdown.disagreements.map((disagreement) => ({
|
|
212887
|
-
...disagreement,
|
|
212888
|
-
judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
|
|
212889
|
-
}));
|
|
212890
|
-
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
212891
|
-
const budgetAfterJudge = budgetTracker.snapshot();
|
|
212892
|
-
const finalWarnings = Array.from(new Set([
|
|
212893
|
-
...resultWithoutMarkdown.audit.warnings ?? [],
|
|
212894
|
-
...outputWarningMessages(budgetAfterJudge),
|
|
212895
|
-
...tokenUsageWarningMessages(budgetTracker, budgetAfterJudge)
|
|
212896
|
-
]));
|
|
212897
|
-
const finalDecision = budgetAfterJudge.completion.status === "incomplete" ? "block" : decision;
|
|
212898
|
-
const resultAfterJudge = {
|
|
212899
|
-
...resultWithoutMarkdown,
|
|
212900
|
-
decision: finalDecision,
|
|
212901
|
-
completion: budgetAfterJudge.completion,
|
|
212902
|
-
executionBudget: budgetAfterJudge.executionBudget,
|
|
212903
|
-
disagreements,
|
|
212904
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {},
|
|
212905
|
-
audit: {
|
|
212906
|
-
...resultWithoutMarkdown.audit,
|
|
212907
|
-
completedAt: new Date().toISOString(),
|
|
212908
|
-
warnings: finalWarnings,
|
|
212909
|
-
modelCalls: budgetAfterJudge.modelCalls
|
|
212910
|
-
}
|
|
212911
|
-
};
|
|
212912
|
-
const judgeEvent = {
|
|
212913
|
-
type: "judge_completed",
|
|
212914
|
-
traceId,
|
|
212915
|
-
provider: judge.provider,
|
|
212916
|
-
status: judge.status,
|
|
212917
|
-
...judge.executionIdentity ? { executionIdentity: judge.executionIdentity } : {},
|
|
212918
|
-
timestamp: new Date().toISOString()
|
|
212919
|
-
};
|
|
212920
|
-
if (judge.error)
|
|
212921
|
-
judgeEvent.error = judge.error;
|
|
212922
|
-
await trace.write(judgeEvent);
|
|
212923
|
-
resultAfterJudge.audit.completedAt = new Date().toISOString();
|
|
212924
|
-
await writeReviewBudgetCompleted({
|
|
212925
|
-
trace,
|
|
212926
|
-
traceId,
|
|
212927
|
-
budgetTracker,
|
|
212928
|
-
requestFingerprint
|
|
212929
|
-
});
|
|
212930
|
-
await trace.write({
|
|
212931
|
-
type: "decision_completed",
|
|
212932
|
-
traceId,
|
|
212933
|
-
decision: finalDecision,
|
|
212934
|
-
timestamp: new Date().toISOString()
|
|
212935
|
-
});
|
|
212936
|
-
await trace.write({
|
|
212937
|
-
type: "response_sent",
|
|
212938
|
-
traceId,
|
|
212939
|
-
timestamp: new Date().toISOString()
|
|
212940
|
-
});
|
|
212941
|
-
return await finalizeReviewResult({
|
|
212942
|
-
tool,
|
|
212943
|
-
trace,
|
|
212944
|
-
result: resultAfterJudge,
|
|
212945
|
-
summaryText: resultAfterJudge.completion.status === "incomplete" ? defaultSummaryText(resultAfterJudge) : judge.output.summaryText
|
|
212946
|
-
});
|
|
212947
|
-
} finally {
|
|
212948
|
-
await trace.finalize();
|
|
212949
|
-
if (snapshot)
|
|
212950
|
-
await cleanupSnapshot(snapshot.root);
|
|
213852
|
+
} catch (error51) {
|
|
213853
|
+
await reportReviewFailure(error51, activeTrace);
|
|
213854
|
+
throw error51;
|
|
212951
213855
|
}
|
|
212952
213856
|
}
|
|
213857
|
+
function durationMsBetween(startedAt, completedAt) {
|
|
213858
|
+
if (!startedAt || !completedAt)
|
|
213859
|
+
return 0;
|
|
213860
|
+
const start = Date.parse(startedAt);
|
|
213861
|
+
const end = Date.parse(completedAt);
|
|
213862
|
+
if (!Number.isFinite(start) || !Number.isFinite(end))
|
|
213863
|
+
return 0;
|
|
213864
|
+
return Math.max(0, end - start);
|
|
213865
|
+
}
|
|
212953
213866
|
async function runFindingVerification(input2) {
|
|
212954
213867
|
const allowDemotionRequested = input2.config.verification.allowDemotion;
|
|
212955
213868
|
const selection = selectVerificationTargets(input2.findings, input2.config.verification.maxFindings);
|
|
@@ -213111,6 +214024,7 @@ async function runFindingVerification(input2) {
|
|
|
213111
214024
|
warnOutputBytes: input2.budgetTracker.budget.effectiveWarnAgentOutputBytes,
|
|
213112
214025
|
maxOutputBytes: input2.budgetTracker.budget.maxAgentOutputBytes,
|
|
213113
214026
|
networkMode: input2.networkMode,
|
|
214027
|
+
signal: input2.signal,
|
|
213114
214028
|
onStarted: (executionIdentity) => {
|
|
213115
214029
|
input2.budgetTracker.markStarted(group.reservation, executionIdentity);
|
|
213116
214030
|
const event = buildAgentStartedEvent({
|
|
@@ -213128,6 +214042,8 @@ async function runFindingVerification(input2) {
|
|
|
213128
214042
|
try {
|
|
213129
214043
|
results = await input2.manager.runAll(agentInputs);
|
|
213130
214044
|
} catch (error51) {
|
|
214045
|
+
if (error51 instanceof KyosoCancellationError)
|
|
214046
|
+
throw error51;
|
|
213131
214047
|
for (const group of scheduledGroups) {
|
|
213132
214048
|
applyVerificationVerdicts(group.targets, group.verifier, undefined);
|
|
213133
214049
|
await finalizeModelCallResult({
|
|
@@ -213352,6 +214268,19 @@ async function recordSkippedJudgeCall(input2, reason) {
|
|
|
213352
214268
|
async function runAgents(input2) {
|
|
213353
214269
|
const agentRoles = resolveAgentRoles(input2.config);
|
|
213354
214270
|
const enabledAgents = ["codex", "claude"].filter((agent) => input2.config.agents[agent].enabled);
|
|
214271
|
+
const openRouter = input2.config.agents.codex.openRouter;
|
|
214272
|
+
const hasOpenRouterRetryPolicy = Object.values(openRouter).some((value) => value !== undefined);
|
|
214273
|
+
if (enabledAgents.includes("codex") && input2.config.agents.codex.provider === CODEX_OPENROUTER_PROVIDER && hasOpenRouterRetryPolicy) {
|
|
214274
|
+
await input2.trace.write({
|
|
214275
|
+
type: "openrouter_retry_policy_resolved",
|
|
214276
|
+
traceId: input2.traceId,
|
|
214277
|
+
streamIdleTimeoutMs: openRouter.streamIdleTimeoutMs,
|
|
214278
|
+
streamMaxRetries: openRouter.streamMaxRetries,
|
|
214279
|
+
requestMaxRetries: openRouter.requestMaxRetries,
|
|
214280
|
+
source: "kyoso_config",
|
|
214281
|
+
timestamp: new Date().toISOString()
|
|
214282
|
+
});
|
|
214283
|
+
}
|
|
213355
214284
|
if (enabledAgents.length === 0)
|
|
213356
214285
|
return [];
|
|
213357
214286
|
const reservationResult = input2.budgetTracker.reserveMany(enabledAgents.map((agent) => ({ kind: "primary", agent })));
|
|
@@ -213437,6 +214366,8 @@ async function runAgents(input2) {
|
|
|
213437
214366
|
const agentInputs = enabledAgents.map((agent) => {
|
|
213438
214367
|
const agentConfig = input2.config.agents[agent];
|
|
213439
214368
|
const role = agentRoles[agent] ?? agentConfig.role;
|
|
214369
|
+
let emittedRetryProgressEvents = 0;
|
|
214370
|
+
let retryProgressLimitWarned = false;
|
|
213440
214371
|
const reservation = reservations.get(agent);
|
|
213441
214372
|
if (!reservation) {
|
|
213442
214373
|
throw new Error(`Missing primary budget reservation for ${agent}.`);
|
|
@@ -213457,15 +214388,27 @@ async function runAgents(input2) {
|
|
|
213457
214388
|
warnOutputBytes: input2.budgetTracker.budget.effectiveWarnAgentOutputBytes,
|
|
213458
214389
|
maxOutputBytes: input2.budgetTracker.budget.maxAgentOutputBytes,
|
|
213459
214390
|
networkMode: input2.networkMode,
|
|
214391
|
+
signal: input2.signal,
|
|
214392
|
+
heartbeatMs: input2.progressHeartbeatMs,
|
|
214393
|
+
...agent === "codex" && input2.config.agents.codex.provider === CODEX_OPENROUTER_PROVIDER && openRouter.streamIdleTimeoutMs !== undefined ? { streamIdleTimeoutMs: openRouter.streamIdleTimeoutMs } : {},
|
|
213460
214394
|
onStarted: (executionIdentity) => {
|
|
213461
214395
|
input2.budgetTracker.markStarted(reservation, executionIdentity);
|
|
213462
214396
|
if (!acceptingStartedEvents)
|
|
213463
214397
|
return Promise.resolve();
|
|
214398
|
+
const progressExecutionIdentity = input2.budgetTracker.executionIdentity(reservation);
|
|
214399
|
+
input2.progressDispatcher.emit({
|
|
214400
|
+
type: "agent_started",
|
|
214401
|
+
traceId: input2.traceId,
|
|
214402
|
+
agent,
|
|
214403
|
+
role,
|
|
214404
|
+
...progressExecutionIdentity ? { executionIdentity: progressExecutionIdentity } : {},
|
|
214405
|
+
timestamp: new Date().toISOString()
|
|
214406
|
+
});
|
|
213464
214407
|
const event = buildAgentStartedEvent({
|
|
213465
214408
|
traceId: input2.traceId,
|
|
213466
214409
|
agent,
|
|
213467
214410
|
role,
|
|
213468
|
-
executionIdentity:
|
|
214411
|
+
executionIdentity: progressExecutionIdentity
|
|
213469
214412
|
});
|
|
213470
214413
|
const write = (async () => {
|
|
213471
214414
|
try {
|
|
@@ -213476,6 +214419,33 @@ async function runAgents(input2) {
|
|
|
213476
214419
|
})();
|
|
213477
214420
|
startedWrites.push(write);
|
|
213478
214421
|
return write;
|
|
214422
|
+
},
|
|
214423
|
+
onProgress: (event) => {
|
|
214424
|
+
if (!acceptingStartedEvents)
|
|
214425
|
+
return;
|
|
214426
|
+
input2.progressDispatcher.emit({ ...event, traceId: input2.traceId });
|
|
214427
|
+
if (event.type !== "agent_retrying")
|
|
214428
|
+
return;
|
|
214429
|
+
if (emittedRetryProgressEvents >= MAX_AGENT_RETRY_PROGRESS_EVENTS) {
|
|
214430
|
+
if (!retryProgressLimitWarned) {
|
|
214431
|
+
retryProgressLimitWarned = true;
|
|
214432
|
+
input2.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.`);
|
|
214433
|
+
}
|
|
214434
|
+
return;
|
|
214435
|
+
}
|
|
214436
|
+
emittedRetryProgressEvents += 1;
|
|
214437
|
+
const write = (async () => {
|
|
214438
|
+
try {
|
|
214439
|
+
await input2.trace.write({
|
|
214440
|
+
...event,
|
|
214441
|
+
traceId: input2.traceId,
|
|
214442
|
+
type: "agent_retrying"
|
|
214443
|
+
});
|
|
214444
|
+
} catch {
|
|
214445
|
+
input2.warnings.push("AUDIT_WRITE_FAILED: agent_retrying event could not be recorded.");
|
|
214446
|
+
}
|
|
214447
|
+
})();
|
|
214448
|
+
startedWrites.push(write);
|
|
213479
214449
|
}
|
|
213480
214450
|
};
|
|
213481
214451
|
});
|
|
@@ -213483,6 +214453,8 @@ async function runAgents(input2) {
|
|
|
213483
214453
|
try {
|
|
213484
214454
|
results = await input2.manager.runAll(agentInputs);
|
|
213485
214455
|
} catch (error51) {
|
|
214456
|
+
if (error51 instanceof KyosoCancellationError)
|
|
214457
|
+
throw error51;
|
|
213486
214458
|
const detail = sanitizeTextForDisplay(error51 instanceof Error ? error51.message : String(error51));
|
|
213487
214459
|
input2.warnings.push(`Primary-agent execution failed: ${detail}`);
|
|
213488
214460
|
results = agentInputs.map((agentInput) => ({
|
|
@@ -213533,6 +214505,16 @@ async function runAgents(input2) {
|
|
|
213533
214505
|
if (result.status !== "completed") {
|
|
213534
214506
|
input2.budgetTracker.markIncomplete("coverage_incomplete");
|
|
213535
214507
|
}
|
|
214508
|
+
input2.progressDispatcher.emit({
|
|
214509
|
+
type: "agent_completed",
|
|
214510
|
+
traceId: input2.traceId,
|
|
214511
|
+
agent: result.agent,
|
|
214512
|
+
status: result.status,
|
|
214513
|
+
durationMs: durationMsBetween(result.startedAt, result.completedAt),
|
|
214514
|
+
...result.outputBytes === undefined ? {} : { outputBytes: result.outputBytes },
|
|
214515
|
+
...result.observedStreamRetries === undefined ? {} : { observedStreamRetries: result.observedStreamRetries },
|
|
214516
|
+
timestamp: new Date().toISOString()
|
|
214517
|
+
});
|
|
213536
214518
|
}
|
|
213537
214519
|
await Promise.all(normalizedResults.map((result) => {
|
|
213538
214520
|
const event = {
|
|
@@ -213623,6 +214605,12 @@ async function finalizeModelCallResult(input2) {
|
|
|
213623
214605
|
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
213624
214606
|
...outputBytes === undefined ? {} : { outputBytes },
|
|
213625
214607
|
...outputWarningTriggered === undefined ? {} : { outputWarningTriggered },
|
|
214608
|
+
...input2.result.observedStreamRetries === undefined ? {} : { observedStreamRetries: input2.result.observedStreamRetries },
|
|
214609
|
+
...input2.result.discardedRetryMessageBytes === undefined ? {} : {
|
|
214610
|
+
discardedRetryMessageBytes: input2.result.discardedRetryMessageBytes
|
|
214611
|
+
},
|
|
214612
|
+
...input2.result.firstOutputAt === undefined ? {} : { firstOutputAt: input2.result.firstOutputAt },
|
|
214613
|
+
...input2.result.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: input2.result.lastAcpUpdateAt },
|
|
213626
214614
|
...input2.result.salvaged === undefined ? {} : { salvaged: input2.result.salvaged },
|
|
213627
214615
|
...input2.result.reportedFindings === undefined ? {} : { reportedFindings: input2.result.reportedFindings },
|
|
213628
214616
|
...input2.result.findingsTargetExceeded === undefined ? {} : { findingsTargetExceeded: input2.result.findingsTargetExceeded },
|
|
@@ -213661,6 +214649,12 @@ async function finalizeModelCallResult(input2) {
|
|
|
213661
214649
|
...thoughtBytes === undefined ? {} : { thoughtBytes },
|
|
213662
214650
|
...outputBytes === undefined ? {} : { outputBytes },
|
|
213663
214651
|
...outputWarningTriggered === undefined ? {} : { outputWarningTriggered },
|
|
214652
|
+
...input2.result.observedStreamRetries === undefined ? {} : { observedStreamRetries: input2.result.observedStreamRetries },
|
|
214653
|
+
...input2.result.discardedRetryMessageBytes === undefined ? {} : {
|
|
214654
|
+
discardedRetryMessageBytes: input2.result.discardedRetryMessageBytes
|
|
214655
|
+
},
|
|
214656
|
+
...input2.result.firstOutputAt === undefined ? {} : { firstOutputAt: input2.result.firstOutputAt },
|
|
214657
|
+
...input2.result.lastAcpUpdateAt === undefined ? {} : { lastAcpUpdateAt: input2.result.lastAcpUpdateAt },
|
|
213664
214658
|
...input2.result.salvaged === undefined ? {} : { salvaged: input2.result.salvaged },
|
|
213665
214659
|
...input2.result.reportedFindings === undefined ? {} : { reportedFindings: input2.result.reportedFindings },
|
|
213666
214660
|
...input2.result.findingsTargetExceeded === undefined ? {} : { findingsTargetExceeded: input2.result.findingsTargetExceeded },
|
|
@@ -214093,6 +215087,27 @@ function formatMcpResponse(result) {
|
|
|
214093
215087
|
};
|
|
214094
215088
|
}
|
|
214095
215089
|
|
|
215090
|
+
// src/mcp/progress.ts
|
|
215091
|
+
function createMcpProgressSink(mcpReq) {
|
|
215092
|
+
const progressToken = mcpReq._meta?.progressToken;
|
|
215093
|
+
if (progressToken === undefined)
|
|
215094
|
+
return;
|
|
215095
|
+
let sequence = 0;
|
|
215096
|
+
return async (event) => {
|
|
215097
|
+
await mcpReq.notify({
|
|
215098
|
+
method: "notifications/progress",
|
|
215099
|
+
params: {
|
|
215100
|
+
progressToken,
|
|
215101
|
+
progress: ++sequence,
|
|
215102
|
+
message: formatMcpProgressMessage(event)
|
|
215103
|
+
}
|
|
215104
|
+
});
|
|
215105
|
+
};
|
|
215106
|
+
}
|
|
215107
|
+
function formatMcpProgressMessage(event) {
|
|
215108
|
+
return formatPlainProgressMessage(event);
|
|
215109
|
+
}
|
|
215110
|
+
|
|
214096
215111
|
// src/mcp/schemas.ts
|
|
214097
215112
|
var kyosoReviewRequestSchema = object({
|
|
214098
215113
|
goal: string2().min(1),
|
|
@@ -214141,23 +215156,29 @@ var kyosoReviewRequestSchema = object({
|
|
|
214141
215156
|
|
|
214142
215157
|
// src/mcp/server.ts
|
|
214143
215158
|
var KYOSO_MCP_INSTRUCTIONS = "Kyoso is a multi-agent planning and review gate. Use it only when the user explicitly asks for Kyoso, multi-agent review, plan review, security review, CISA Secure by Design review, or diff review. Kyoso does not apply code changes. It returns structured review results and Markdown summaries.";
|
|
215159
|
+
var REVIEW_TOOL_DESCRIPTIONS = {
|
|
215160
|
+
plan_review: "Review an implementation plan before coding. Kyoso does not modify files. Sends MCP progress notifications when the client provides a progressToken.",
|
|
215161
|
+
security_review: "Review a security-sensitive plan, selected files, or diff with CISA Secure by Design gates. Sends MCP progress notifications when the client provides a progressToken.",
|
|
215162
|
+
diff_review: "Review a provided unified diff after implementation. Kyoso does not apply patches. Sends MCP progress notifications when the client provides a progressToken."
|
|
215163
|
+
};
|
|
214144
215164
|
function createMcpServer(options = {}) {
|
|
214145
215165
|
const reviewOptions = { ...options, entrypoint: "mcp" };
|
|
214146
215166
|
const server2 = new McpServer({ name: "kyoso", version: KYOSO_VERSION }, { instructions: KYOSO_MCP_INSTRUCTIONS });
|
|
214147
|
-
server2
|
|
214148
|
-
|
|
214149
|
-
|
|
214150
|
-
}, async (request) => formatMcpResponse(await runReview("plan_review", request, reviewOptions)));
|
|
214151
|
-
server2.registerTool("security_review", {
|
|
214152
|
-
description: "Review a security-sensitive plan, selected files, or diff with CISA Secure by Design gates.",
|
|
214153
|
-
inputSchema: kyosoReviewRequestSchema
|
|
214154
|
-
}, async (request) => formatMcpResponse(await runReview("security_review", request, reviewOptions)));
|
|
214155
|
-
server2.registerTool("diff_review", {
|
|
214156
|
-
description: "Review a provided unified diff after implementation. Kyoso does not apply patches.",
|
|
214157
|
-
inputSchema: kyosoReviewRequestSchema
|
|
214158
|
-
}, async (request) => formatMcpResponse(await runReview("diff_review", request, reviewOptions)));
|
|
215167
|
+
registerReviewTool(server2, "plan_review", reviewOptions);
|
|
215168
|
+
registerReviewTool(server2, "security_review", reviewOptions);
|
|
215169
|
+
registerReviewTool(server2, "diff_review", reviewOptions);
|
|
214159
215170
|
return server2;
|
|
214160
215171
|
}
|
|
215172
|
+
function registerReviewTool(server2, tool, reviewOptions) {
|
|
215173
|
+
server2.registerTool(tool, {
|
|
215174
|
+
description: REVIEW_TOOL_DESCRIPTIONS[tool],
|
|
215175
|
+
inputSchema: kyosoReviewRequestSchema
|
|
215176
|
+
}, async (request, ctx) => formatMcpResponse(await runReview(tool, request, {
|
|
215177
|
+
...reviewOptions,
|
|
215178
|
+
signal: ctx.mcpReq.signal,
|
|
215179
|
+
onProgress: createMcpProgressSink(ctx.mcpReq)
|
|
215180
|
+
})));
|
|
215181
|
+
}
|
|
214161
215182
|
async function startMcpServer(options = {}) {
|
|
214162
215183
|
const server2 = createMcpServer(options);
|
|
214163
215184
|
const transport = new StdioServerTransport;
|
|
@@ -214221,17 +215242,49 @@ async function main() {
|
|
|
214221
215242
|
if (parsed.command === "plan" || parsed.command === "security" || parsed.command === "diff") {
|
|
214222
215243
|
const tool = commandToTool(parsed.command);
|
|
214223
215244
|
const request = await buildReviewRequest(tool, parsed.flags);
|
|
214224
|
-
const
|
|
214225
|
-
|
|
214226
|
-
|
|
214227
|
-
ignoreConfig,
|
|
214228
|
-
trustConfig: trustConfig2,
|
|
214229
|
-
allowUnknownConfig,
|
|
214230
|
-
configOverrides: configOverrideFlags(parsed.flags),
|
|
214231
|
-
entrypoint: "cli",
|
|
214232
|
-
promptForTrust: canPromptForConfigTrust()
|
|
215245
|
+
const progressMode = resolveCliProgressMode(stringFlag(parsed.flags, "progress"), process.stderr.isTTY === true);
|
|
215246
|
+
const progressSink = createCliProgressSink(progressMode, (line) => {
|
|
215247
|
+
process.stderr.write(line);
|
|
214233
215248
|
});
|
|
214234
|
-
|
|
215249
|
+
const controller = new AbortController;
|
|
215250
|
+
let interruptCount = 0;
|
|
215251
|
+
const onSigint = () => {
|
|
215252
|
+
interruptCount += 1;
|
|
215253
|
+
if (interruptCount > 1) {
|
|
215254
|
+
process.exit(130);
|
|
215255
|
+
}
|
|
215256
|
+
if (progressMode !== "jsonl") {
|
|
215257
|
+
process.stderr.write(`Cancelling... (press Ctrl-C again to force quit)
|
|
215258
|
+
`);
|
|
215259
|
+
}
|
|
215260
|
+
controller.abort(new KyosoCancellationError("Interrupted by SIGINT."));
|
|
215261
|
+
};
|
|
215262
|
+
process.on("SIGINT", onSigint);
|
|
215263
|
+
try {
|
|
215264
|
+
const result = await runReview(tool, request, {
|
|
215265
|
+
cwd,
|
|
215266
|
+
configPath,
|
|
215267
|
+
ignoreConfig,
|
|
215268
|
+
trustConfig: trustConfig2,
|
|
215269
|
+
allowUnknownConfig,
|
|
215270
|
+
configOverrides: configOverrideFlags(parsed.flags),
|
|
215271
|
+
entrypoint: "cli",
|
|
215272
|
+
promptForTrust: canPromptForConfigTrust(),
|
|
215273
|
+
onProgress: progressSink,
|
|
215274
|
+
signal: controller.signal
|
|
215275
|
+
});
|
|
215276
|
+
console.log(booleanFlag(parsed.flags, "json") ? JSON.stringify(result, null, 2) : result.summaryMarkdown);
|
|
215277
|
+
} catch (error51) {
|
|
215278
|
+
if (!isCancellationError(error51))
|
|
215279
|
+
throw error51;
|
|
215280
|
+
if (progressMode !== "jsonl") {
|
|
215281
|
+
process.stderr.write(`Review cancelled.
|
|
215282
|
+
`);
|
|
215283
|
+
}
|
|
215284
|
+
process.exitCode = 130;
|
|
215285
|
+
} finally {
|
|
215286
|
+
process.off("SIGINT", onSigint);
|
|
215287
|
+
}
|
|
214235
215288
|
return;
|
|
214236
215289
|
}
|
|
214237
215290
|
console.log(HELP);
|
|
@@ -214335,13 +215388,20 @@ Usage:
|
|
|
214335
215388
|
kyoso setup [codex|claude-code] [--write] [--with-openrouter] [--runner npx|bunx] [--command <command>] [--global] [--force]
|
|
214336
215389
|
kyoso setup codex|claude-code --skill-only [--write] [--global] [--force]
|
|
214337
215390
|
kyoso openrouter-acp-smoke
|
|
214338
|
-
kyoso plan --goal <text> [--plan <path-or-text>] [--file <path>] [--focus <lens>]... [--set <key>=<value>]... [--json] [--trust-config] [--allow-unknown-config]
|
|
214339
|
-
kyoso security --goal <text> [--diff <path>] [--file <path>] [--focus <lens>]... [--set <key>=<value>]... [--json] [--allow-secret-redaction] [--trust-config] [--allow-unknown-config]
|
|
214340
|
-
kyoso diff --base main --head HEAD [--focus <lens>]... [--set <key>=<value>]... [--json] [--trust-config] [--allow-unknown-config]
|
|
215391
|
+
kyoso plan --goal <text> [--plan <path-or-text>] [--file <path>] [--focus <lens>]... [--set <key>=<value>]... [--json] [--progress auto|plain|jsonl|off] [--trust-config] [--allow-unknown-config]
|
|
215392
|
+
kyoso security --goal <text> [--diff <path>] [--file <path>] [--focus <lens>]... [--set <key>=<value>]... [--json] [--progress auto|plain|jsonl|off] [--allow-secret-redaction] [--trust-config] [--allow-unknown-config]
|
|
215393
|
+
kyoso diff --base main --head HEAD [--focus <lens>]... [--set <key>=<value>]... [--json] [--progress auto|plain|jsonl|off] [--trust-config] [--allow-unknown-config]
|
|
214341
215394
|
kyoso doctor [--trust-config] [--allow-unknown-config]
|
|
214342
215395
|
kyoso init [--force]
|
|
214343
215396
|
`;
|
|
214344
215397
|
main().catch((error51) => {
|
|
215398
|
+
if (isCancellationError(error51)) {
|
|
215399
|
+
console.error("Review cancelled.");
|
|
215400
|
+
process.exit(130);
|
|
215401
|
+
}
|
|
214345
215402
|
console.error(error51 instanceof Error ? error51.message : String(error51));
|
|
214346
215403
|
process.exit(1);
|
|
214347
215404
|
});
|
|
215405
|
+
function isCancellationError(error51) {
|
|
215406
|
+
return error51 instanceof KyosoCancellationError || typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "REQUEST_CANCELLED";
|
|
215407
|
+
}
|