@kyo-so/cli 0.2.0 → 0.4.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/README.ja.md +8 -0
- package/README.md +8 -0
- package/README.zh-CN.md +8 -0
- package/dist/acp/prompts.d.ts +2 -2
- package/dist/bin/kyoso.js +136 -33
- package/dist/config/schema.d.ts +2 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/types.d.ts +4 -1
- package/dist/index.js +74 -22
- package/dist/workspace/createSnapshot.d.ts +2 -1
- package/examples/claude-only.config.ts +9 -0
- package/examples/codex-only.config.ts +9 -0
- package/package.json +9 -1
package/README.ja.md
CHANGED
|
@@ -20,6 +20,14 @@ Kyoso はコード変更を適用しません。
|
|
|
20
20
|
|
|
21
21
|
グローバルインストールは不要です。Kyoso は `npx` または `bunx` で実行します。
|
|
22
22
|
|
|
23
|
+
### Claude Only / Codex Only
|
|
24
|
+
|
|
25
|
+
Kyoso は Claude だけ、または Codex だけでも実行できます。利用できない backend は `kyoso.config.ts` で無効化してください。例は `examples/claude-only.config.ts` と `examples/codex-only.config.ts` にあります。
|
|
26
|
+
|
|
27
|
+
single-agent mode では、残った backend が `combined_reviewer` として 1 回だけ実行され、implementation と architecture/security の両方を確認します。JSON output には `reviewMode: "single_agent"` と `agentsUsed` が入り、Markdown output には cross-model verification が行われていないことと disagreements が N/A であることを表示します。
|
|
28
|
+
|
|
29
|
+
この mode では独立した cross-model validation はなく、自己レビュー bias が残ります。一方で、別プロセスの read-only review、temporary snapshots、adversarial review prompts、secret scanning、deterministic gates は利用できます。
|
|
30
|
+
|
|
23
31
|
### Claude Code
|
|
24
32
|
|
|
25
33
|
1. Claude 認証を準備します。
|
package/README.md
CHANGED
|
@@ -18,6 +18,14 @@ Kyoso does not apply code changes.
|
|
|
18
18
|
|
|
19
19
|
No global install is required. Run Kyoso through `npx` or `bunx`.
|
|
20
20
|
|
|
21
|
+
### Claude Only / Codex Only
|
|
22
|
+
|
|
23
|
+
Kyoso can run when only Claude or only Codex is available. Disable the missing backend in `kyoso.config.ts` using `examples/claude-only.config.ts` or `examples/codex-only.config.ts`.
|
|
24
|
+
|
|
25
|
+
In single-agent mode, the remaining backend runs once as `combined_reviewer` and covers both implementation and architecture/security focus areas. JSON output includes `reviewMode: "single_agent"` and `agentsUsed`; Markdown output states that cross-model verification was not performed and marks disagreements as N/A.
|
|
26
|
+
|
|
27
|
+
This mode does not provide independent cross-model validation and may retain self-review bias. It still provides a separate read-only review process, temporary snapshots, adversarial review prompts, secret scanning, and deterministic gates.
|
|
28
|
+
|
|
21
29
|
### Claude Code
|
|
22
30
|
|
|
23
31
|
1. Prepare Claude authentication.
|
package/README.zh-CN.md
CHANGED
|
@@ -20,6 +20,14 @@ Kyoso 不会应用代码更改。
|
|
|
20
20
|
|
|
21
21
|
无需全局安装。通过 `npx` 或 `bunx` 运行 Kyoso。
|
|
22
22
|
|
|
23
|
+
### Claude Only / Codex Only
|
|
24
|
+
|
|
25
|
+
Kyoso 可以在只有 Claude 或只有 Codex 可用时运行。请在 `kyoso.config.ts` 中禁用缺失的 backend;示例见 `examples/claude-only.config.ts` 和 `examples/codex-only.config.ts`。
|
|
26
|
+
|
|
27
|
+
在 single-agent mode 中,剩余 backend 会以 `combined_reviewer` 运行一次,同时覆盖 implementation 和 architecture/security 两类关注点。JSON output 会包含 `reviewMode: "single_agent"` 和 `agentsUsed`;Markdown output 会说明未执行 cross-model verification,并将 disagreements 标记为 N/A。
|
|
28
|
+
|
|
29
|
+
该 mode 不提供独立的 cross-model validation,仍可能有 self-review bias。它仍保留独立只读 review process、temporary snapshots、adversarial review prompts、secret scanning 和 deterministic gates。
|
|
30
|
+
|
|
23
31
|
### Claude Code
|
|
24
32
|
|
|
25
33
|
1. 准备 Claude authentication。
|
package/dist/acp/prompts.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { KyosoReviewRequest, ReviewTool } from "../core/types.js";
|
|
2
|
-
export declare function buildAgentPrompt(tool: ReviewTool, request: KyosoReviewRequest, agent:
|
|
1
|
+
import type { AgentName, AgentRole, KyosoReviewRequest, ReviewTool } from "../core/types.js";
|
|
2
|
+
export declare function buildAgentPrompt(tool: ReviewTool, request: KyosoReviewRequest, agent: AgentName, role: AgentRole): string;
|
package/dist/bin/kyoso.js
CHANGED
|
@@ -48,7 +48,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
48
48
|
|
|
49
49
|
// node_modules/typescript/lib/typescript.js
|
|
50
50
|
var require_typescript = __commonJS((exports, module) => {
|
|
51
|
-
var __dirname = "/
|
|
51
|
+
var __dirname = "/home/runner/work/kyoso/kyoso/node_modules/typescript/lib", __filename = "/home/runner/work/kyoso/kyoso/node_modules/typescript/lib/typescript.js";
|
|
52
52
|
/*! *****************************************************************************
|
|
53
53
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
54
54
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
@@ -169600,7 +169600,7 @@ function languageFromPath(path) {
|
|
|
169600
169600
|
|
|
169601
169601
|
// src/cli/doctor.ts
|
|
169602
169602
|
import { accessSync } from "node:fs";
|
|
169603
|
-
import { delimiter } from "node:path";
|
|
169603
|
+
import { delimiter as delimiter2 } from "node:path";
|
|
169604
169604
|
|
|
169605
169605
|
// src/config/loadConfig.ts
|
|
169606
169606
|
import { access as access2, readFile as readFile3 } from "node:fs/promises";
|
|
@@ -169622,7 +169622,7 @@ var defaultConfig = {
|
|
|
169622
169622
|
enabled: true,
|
|
169623
169623
|
type: "acp",
|
|
169624
169624
|
command: "npx",
|
|
169625
|
-
args: ["-y", "@agentclientprotocol/codex-acp"],
|
|
169625
|
+
args: ["-y", "@agentclientprotocol/codex-acp@1.1.0"],
|
|
169626
169626
|
role: "implementation_reviewer",
|
|
169627
169627
|
timeoutMs: 120000,
|
|
169628
169628
|
env: {
|
|
@@ -169646,7 +169646,7 @@ var defaultConfig = {
|
|
|
169646
169646
|
enabled: true,
|
|
169647
169647
|
type: "acp",
|
|
169648
169648
|
command: "npx",
|
|
169649
|
-
args: ["-y", "@agentclientprotocol/claude-agent-acp"],
|
|
169649
|
+
args: ["-y", "@agentclientprotocol/claude-agent-acp@0.57.0"],
|
|
169650
169650
|
role: "architecture_security_reviewer",
|
|
169651
169651
|
timeoutMs: 240000,
|
|
169652
169652
|
env: {
|
|
@@ -184013,7 +184013,11 @@ var agentSchema = exports_external.object({
|
|
|
184013
184013
|
command: exports_external.string(),
|
|
184014
184014
|
args: exports_external.array(exports_external.string()).default([]),
|
|
184015
184015
|
model: exports_external.string().optional(),
|
|
184016
|
-
role: exports_external.enum([
|
|
184016
|
+
role: exports_external.enum([
|
|
184017
|
+
"implementation_reviewer",
|
|
184018
|
+
"architecture_security_reviewer",
|
|
184019
|
+
"combined_reviewer"
|
|
184020
|
+
]),
|
|
184017
184021
|
timeoutMs: exports_external.number().int().positive().default(120000),
|
|
184018
184022
|
env: exports_external.record(exports_external.string(), exports_external.string()).default({}),
|
|
184019
184023
|
auth: exports_external.object({
|
|
@@ -184293,7 +184297,7 @@ var REDACTION = "[KYOSO_REDACTED]";
|
|
|
184293
184297
|
var DEFAULT_AGENT_TIMEOUT_MS = 120000;
|
|
184294
184298
|
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
184295
184299
|
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
184296
|
-
var KYOSO_VERSION = "0.
|
|
184300
|
+
var KYOSO_VERSION = "0.4.0";
|
|
184297
184301
|
|
|
184298
184302
|
// src/security/sanitizeText.ts
|
|
184299
184303
|
var SENSITIVE_TEXT_PATTERNS = [
|
|
@@ -184555,7 +184559,7 @@ import { spawnSync } from "node:child_process";
|
|
|
184555
184559
|
import { existsSync, readFileSync } from "node:fs";
|
|
184556
184560
|
import { cp, mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
184557
184561
|
import { homedir as homedir2 } from "node:os";
|
|
184558
|
-
import { dirname as dirname4, join as join2 } from "node:path";
|
|
184562
|
+
import { delimiter, dirname as dirname4, join as join2 } from "node:path";
|
|
184559
184563
|
import { fileURLToPath } from "node:url";
|
|
184560
184564
|
async function runSetup(options) {
|
|
184561
184565
|
const client = parseClient(options.client);
|
|
@@ -184564,6 +184568,7 @@ async function runSetup(options) {
|
|
|
184564
184568
|
const context = {
|
|
184565
184569
|
cwd: options.cwd,
|
|
184566
184570
|
home: options.env?.HOME ?? homedir2(),
|
|
184571
|
+
env: options.env ?? process.env,
|
|
184567
184572
|
write: options.write,
|
|
184568
184573
|
scope: options.global ? "global" : "project",
|
|
184569
184574
|
mcpCommand: command,
|
|
@@ -184634,7 +184639,8 @@ async function setupCodex(context) {
|
|
|
184634
184639
|
sourceDir: context.sourceSkillDir,
|
|
184635
184640
|
destinationDir: skillDestination("codex", context.scope, context.cwd, context.home),
|
|
184636
184641
|
write: context.write
|
|
184637
|
-
})
|
|
184642
|
+
}),
|
|
184643
|
+
...singleAgentAdvice(context, "codex")
|
|
184638
184644
|
];
|
|
184639
184645
|
}
|
|
184640
184646
|
async function setupClaudeCode(context) {
|
|
@@ -184645,7 +184651,8 @@ async function setupClaudeCode(context) {
|
|
|
184645
184651
|
sourceDir: context.sourceSkillDir,
|
|
184646
184652
|
destinationDir: skillDestination("claude-code", context.scope, context.cwd, context.home),
|
|
184647
184653
|
write: context.write
|
|
184648
|
-
})
|
|
184654
|
+
}),
|
|
184655
|
+
...singleAgentAdvice(context, "claude-code")
|
|
184649
184656
|
];
|
|
184650
184657
|
}
|
|
184651
184658
|
async function ensureCodexMcp(context) {
|
|
@@ -184956,6 +184963,41 @@ function diffForJson(path, before, after) {
|
|
|
184956
184963
|
function statusWord(value) {
|
|
184957
184964
|
return value ? "ok" : "missing";
|
|
184958
184965
|
}
|
|
184966
|
+
function singleAgentAdvice(context, client) {
|
|
184967
|
+
if (client === "claude-code" && !commandExists("codex", context.env)) {
|
|
184968
|
+
return [
|
|
184969
|
+
{
|
|
184970
|
+
title: "Single-agent config",
|
|
184971
|
+
status: "skipped",
|
|
184972
|
+
detail: [
|
|
184973
|
+
"codex was not found on PATH. To use Claude only, add:",
|
|
184974
|
+
"agents: {",
|
|
184975
|
+
" codex: { enabled: false },",
|
|
184976
|
+
"}",
|
|
184977
|
+
"Claude will run as combined_reviewer and cross-model verification will be marked unavailable."
|
|
184978
|
+
].join(`
|
|
184979
|
+
`)
|
|
184980
|
+
}
|
|
184981
|
+
];
|
|
184982
|
+
}
|
|
184983
|
+
if (client === "codex" && !commandExists("claude", context.env)) {
|
|
184984
|
+
return [
|
|
184985
|
+
{
|
|
184986
|
+
title: "Single-agent config",
|
|
184987
|
+
status: "skipped",
|
|
184988
|
+
detail: [
|
|
184989
|
+
"claude was not found on PATH. To use Codex only, add:",
|
|
184990
|
+
"agents: {",
|
|
184991
|
+
" claude: { enabled: false },",
|
|
184992
|
+
"}",
|
|
184993
|
+
"Codex will run as combined_reviewer and cross-model verification will be marked unavailable."
|
|
184994
|
+
].join(`
|
|
184995
|
+
`)
|
|
184996
|
+
}
|
|
184997
|
+
];
|
|
184998
|
+
}
|
|
184999
|
+
return [];
|
|
185000
|
+
}
|
|
184959
185001
|
function indent(value) {
|
|
184960
185002
|
return value.split(`
|
|
184961
185003
|
`).map((line) => ` ${line}`).join(`
|
|
@@ -184966,6 +185008,10 @@ function shellQuote(value) {
|
|
|
184966
185008
|
return value;
|
|
184967
185009
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
184968
185010
|
}
|
|
185011
|
+
function commandExists(command, env) {
|
|
185012
|
+
const paths = env.PATH?.split(delimiter) ?? [];
|
|
185013
|
+
return paths.some((path) => existsSync(join2(path, command)));
|
|
185014
|
+
}
|
|
184969
185015
|
function isMissingPathError2(error51) {
|
|
184970
185016
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
184971
185017
|
}
|
|
@@ -184975,8 +185021,8 @@ async function runDoctor(options) {
|
|
|
184975
185021
|
const env = options.env ?? process.env;
|
|
184976
185022
|
const loaded = await loadConfig(options);
|
|
184977
185023
|
const lines = ["Kyoso doctor", "", "Runtime"];
|
|
184978
|
-
lines.push(` Bun: ${
|
|
184979
|
-
lines.push(` Node/npm: ${
|
|
185024
|
+
lines.push(` Bun: ${commandExists2("bun", env) ? "ok" : "warning not found"}`);
|
|
185025
|
+
lines.push(` Node/npm: ${commandExists2("npm", env) ? "ok" : "warning npm not found"}`);
|
|
184980
185026
|
lines.push("", "Config");
|
|
184981
185027
|
lines.push(` kyoso.config.ts: ${loaded.configPath ? `found ${loaded.configPath}` : "not found; using defaults"}`);
|
|
184982
185028
|
lines.push(` trusted config: ${formatTrustStatus(loaded.configTrustStatus)}`);
|
|
@@ -185004,12 +185050,16 @@ async function runDoctor(options) {
|
|
|
185004
185050
|
lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
|
|
185005
185051
|
}
|
|
185006
185052
|
lines.push("", "ACP agents");
|
|
185053
|
+
const agentCommandExists = {
|
|
185054
|
+
codex: commandExists2(loaded.config.agents.codex.command, env),
|
|
185055
|
+
claude: commandExists2(loaded.config.agents.claude.command, env)
|
|
185056
|
+
};
|
|
185007
185057
|
for (const agent of ["codex", "claude"]) {
|
|
185008
185058
|
const config2 = loaded.config.agents[agent];
|
|
185009
|
-
const exists3 =
|
|
185059
|
+
const exists3 = agentCommandExists[agent];
|
|
185010
185060
|
lines.push(` ${agent === "codex" ? "Codex" : "Claude"}: ${exists3 ? "ok" : "warning command not found"}`);
|
|
185011
185061
|
lines.push(` command: ${[config2.command, ...config2.args].join(" ")}`);
|
|
185012
|
-
if (!exists3 && config2.command === "npx" &&
|
|
185062
|
+
if (!exists3 && config2.command === "npx" && commandExists2("bunx", env)) {
|
|
185013
185063
|
lines.push(' hint: replace command "npx" with "bunx" in kyoso.config.ts');
|
|
185014
185064
|
}
|
|
185015
185065
|
if (agent === "claude") {
|
|
@@ -185029,6 +185079,11 @@ async function runDoctor(options) {
|
|
|
185029
185079
|
lines.push(" auth: detected or delegated");
|
|
185030
185080
|
}
|
|
185031
185081
|
}
|
|
185082
|
+
if (agentCommandExists.codex !== agentCommandExists.claude) {
|
|
185083
|
+
const missing = agentCommandExists.codex ? "claude" : "codex";
|
|
185084
|
+
const remaining = agentCommandExists.codex ? "codex" : "claude";
|
|
185085
|
+
lines.push(` single-agent mode: set agents.${missing}.enabled: false to use ${remaining} only; the remaining agent will cover both review roles.`);
|
|
185086
|
+
}
|
|
185032
185087
|
const judgeProvider = loaded.config.judge.mode === "deterministic_only" ? "deterministic_fallback" : resolveJudgeProvider(loaded.config.judge.provider, env);
|
|
185033
185088
|
lines.push("", "Judge");
|
|
185034
185089
|
lines.push(` provider: ${judgeProvider}`);
|
|
@@ -185063,8 +185118,8 @@ function formatClaudeDualAuthWarning(preferApiKey) {
|
|
|
185063
185118
|
function hasEnv2(env, key) {
|
|
185064
185119
|
return typeof env[key] === "string" && env[key].trim().length > 0;
|
|
185065
185120
|
}
|
|
185066
|
-
function
|
|
185067
|
-
const paths = env.PATH?.split(
|
|
185121
|
+
function commandExists2(command, env) {
|
|
185122
|
+
const paths = env.PATH?.split(delimiter2) ?? [];
|
|
185068
185123
|
return paths.some((path) => {
|
|
185069
185124
|
try {
|
|
185070
185125
|
accessSync(`${path}/${command}`);
|
|
@@ -198451,7 +198506,7 @@ function buildOpinion(agent, role, tool) {
|
|
|
198451
198506
|
}
|
|
198452
198507
|
|
|
198453
198508
|
// src/acp/prompts.ts
|
|
198454
|
-
function buildAgentPrompt(tool, request, agent) {
|
|
198509
|
+
function buildAgentPrompt(tool, request, agent, role) {
|
|
198455
198510
|
const shared = [
|
|
198456
198511
|
"You are running as a Kyoso child reviewer.",
|
|
198457
198512
|
"Do not edit files.",
|
|
@@ -198464,15 +198519,26 @@ function buildAgentPrompt(tool, request, agent) {
|
|
|
198464
198519
|
"Use empty arrays when no finding, test, risk, or question exists; do not copy the example finding."
|
|
198465
198520
|
].join(`
|
|
198466
198521
|
`);
|
|
198467
|
-
const
|
|
198468
|
-
|
|
198469
|
-
|
|
198470
|
-
|
|
198471
|
-
`
|
|
198472
|
-
|
|
198473
|
-
|
|
198474
|
-
|
|
198475
|
-
|
|
198522
|
+
const roleInstructions = {
|
|
198523
|
+
implementation_reviewer: [
|
|
198524
|
+
"You are the implementation reviewer role in Kyoso.",
|
|
198525
|
+
"Focus on feasibility, minimal change, existing code consistency, regression risk, tests, migration risk, and maintainability."
|
|
198526
|
+
].join(`
|
|
198527
|
+
`),
|
|
198528
|
+
architecture_security_reviewer: [
|
|
198529
|
+
"You are the architecture and security reviewer role in Kyoso.",
|
|
198530
|
+
"Focus on architecture, threat modeling, authn/authz, secrets, privacy, secure defaults, CISA Secure by Design, and edge cases."
|
|
198531
|
+
].join(`
|
|
198532
|
+
`),
|
|
198533
|
+
combined_reviewer: [
|
|
198534
|
+
"You are the combined reviewer role in Kyoso.",
|
|
198535
|
+
"Cover both implementation review and architecture/security review in one pass.",
|
|
198536
|
+
"First assess feasibility, minimal change, existing code consistency, regression risk, tests, migration risk, and maintainability.",
|
|
198537
|
+
"Then assess architecture, threat modeling, authn/authz, secrets, privacy, secure defaults, CISA Secure by Design, and edge cases.",
|
|
198538
|
+
"Use finding category values so readers can distinguish implementation, architecture, and security concerns."
|
|
198539
|
+
].join(`
|
|
198540
|
+
`)
|
|
198541
|
+
};
|
|
198476
198542
|
const cisaInstruction = tool === "security_review" ? [
|
|
198477
198543
|
"For security_review, include cisaMapping on each security-relevant finding when applicable.",
|
|
198478
198544
|
"Also include cisaSecureByDesign with all four gate dimensions."
|
|
@@ -198480,7 +198546,9 @@ function buildAgentPrompt(tool, request, agent) {
|
|
|
198480
198546
|
`) : "For plan_review and diff_review, include cisaMapping and cisaSecureByDesign only when relevant.";
|
|
198481
198547
|
return `${shared}
|
|
198482
198548
|
|
|
198483
|
-
${
|
|
198549
|
+
Agent: ${agent}
|
|
198550
|
+
Role: ${role}
|
|
198551
|
+
${roleInstructions[role]}
|
|
198484
198552
|
|
|
198485
198553
|
Tool: ${tool}
|
|
198486
198554
|
${cisaInstruction}
|
|
@@ -199067,6 +199135,7 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
199067
199135
|
`**Decision:** ${result.decision}`,
|
|
199068
199136
|
`**Mode:** ${tool}`,
|
|
199069
199137
|
`**Agents:** ${result.agentOpinions.map((opinion) => `${title(opinion.agent)} ${opinion.status}`).join(", ")}`,
|
|
199138
|
+
`**Review mode:** ${formatReviewMode(result)}`,
|
|
199070
199139
|
`**Degraded:** ${String(result.degraded)}`,
|
|
199071
199140
|
"",
|
|
199072
199141
|
"## Summary",
|
|
@@ -199093,7 +199162,11 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
199093
199162
|
lines.push(`### ${title(opinion.agent)}`, "", `${opinion.summary} (${opinion.status})`, "");
|
|
199094
199163
|
}
|
|
199095
199164
|
lines.push("", "## Disagreements", "");
|
|
199096
|
-
|
|
199165
|
+
if (result.reviewMode === "single_agent") {
|
|
199166
|
+
lines.push("- N/A - single-agent review.");
|
|
199167
|
+
} else {
|
|
199168
|
+
lines.push(...result.disagreements.length > 0 ? result.disagreements.map((item) => `- ${item.topic}: ${item.judgeComment}`) : ["- None."]);
|
|
199169
|
+
}
|
|
199097
199170
|
lines.push("", "## Notes", "", "Kyoso did not modify files. Review was performed on a temporary snapshot.");
|
|
199098
199171
|
if (result.audit.networkMode === "unrestricted") {
|
|
199099
199172
|
lines.push("Network mode was unrestricted. File modification policy remained denied.");
|
|
@@ -199107,6 +199180,14 @@ function defaultSummaryText(result) {
|
|
|
199107
199180
|
function title(value) {
|
|
199108
199181
|
return value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
199109
199182
|
}
|
|
199183
|
+
function formatReviewMode(result) {
|
|
199184
|
+
if (result.reviewMode !== "single_agent")
|
|
199185
|
+
return "multi-agent";
|
|
199186
|
+
const opinion = result.agentOpinions[0];
|
|
199187
|
+
const agent = opinion ? title(opinion.agent) : "single agent";
|
|
199188
|
+
const role = opinion?.role === "combined_reviewer" ? "combined role" : "configured role";
|
|
199189
|
+
return `single-agent (${agent}, ${role}; cross-model verification was not performed)`;
|
|
199190
|
+
}
|
|
199110
199191
|
function notes(items) {
|
|
199111
199192
|
return (items[0] ?? "No notes.").replaceAll("|", "\\|");
|
|
199112
199193
|
}
|
|
@@ -199333,8 +199414,8 @@ async function createSnapshot(traceId, tool, request, options = {}) {
|
|
|
199333
199414
|
}
|
|
199334
199415
|
await writeFile5(join5(contextDir, "request.json"), JSON.stringify(stripContents(request), null, 2), "utf8");
|
|
199335
199416
|
await writeFile5(join5(contextDir, "selected_files_manifest.json"), JSON.stringify(buildSelectedFilesManifest(request), null, 2), "utf8");
|
|
199336
|
-
await writeFile5(join5(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex"), "utf8");
|
|
199337
|
-
await writeFile5(join5(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude"), "utf8");
|
|
199417
|
+
await writeFile5(join5(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex", options.agentRoles?.codex ?? "implementation_reviewer"), "utf8");
|
|
199418
|
+
await writeFile5(join5(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude", options.agentRoles?.claude ?? "architecture_security_reviewer"), "utf8");
|
|
199338
199419
|
if (request.repoSummary)
|
|
199339
199420
|
await writeFile5(join5(contextDir, "repo_summary.md"), request.repoSummary, "utf8");
|
|
199340
199421
|
if (request.currentPlan)
|
|
@@ -199496,9 +199577,11 @@ async function runReview(tool, request, options = {}) {
|
|
|
199496
199577
|
allowPatterns
|
|
199497
199578
|
});
|
|
199498
199579
|
warnings.push(...built.warnings);
|
|
199580
|
+
const agentRoles = resolveAgentRoles(loaded.config);
|
|
199499
199581
|
snapshot = await createSnapshot(traceId, tool, built.request, {
|
|
199500
199582
|
denyPatterns,
|
|
199501
|
-
allowPatterns
|
|
199583
|
+
allowPatterns,
|
|
199584
|
+
agentRoles
|
|
199502
199585
|
});
|
|
199503
199586
|
await trace.write({
|
|
199504
199587
|
type: "snapshot_created",
|
|
@@ -199518,6 +199601,8 @@ async function runReview(tool, request, options = {}) {
|
|
|
199518
199601
|
trace
|
|
199519
199602
|
});
|
|
199520
199603
|
const normalizedAgentResults = agentResults.map(normalizeAgentRunResult);
|
|
199604
|
+
const agentsUsed = normalizedAgentResults.map((result2) => result2.agent);
|
|
199605
|
+
const reviewMode = agentsUsed.length === 1 ? "single_agent" : "multi_agent";
|
|
199521
199606
|
const completed = normalizedAgentResults.filter((result2) => result2.status === "completed");
|
|
199522
199607
|
const degraded = completed.length !== agentResults.length;
|
|
199523
199608
|
let aggregate = aggregateAgentResults(normalizedAgentResults);
|
|
@@ -199569,6 +199654,8 @@ async function runReview(tool, request, options = {}) {
|
|
|
199569
199654
|
const resultWithoutMarkdown = {
|
|
199570
199655
|
decision,
|
|
199571
199656
|
degraded,
|
|
199657
|
+
agentsUsed,
|
|
199658
|
+
reviewMode,
|
|
199572
199659
|
findings: aggregate.findings,
|
|
199573
199660
|
cisaSecureByDesign: cisa,
|
|
199574
199661
|
disagreements: aggregate.disagreements,
|
|
@@ -199581,7 +199668,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
199581
199668
|
traceId,
|
|
199582
199669
|
startedAt,
|
|
199583
199670
|
completedAt,
|
|
199584
|
-
agentsUsed
|
|
199671
|
+
agentsUsed,
|
|
199585
199672
|
redactionsApplied: secretScan.redactions,
|
|
199586
199673
|
networkMode,
|
|
199587
199674
|
workspaceMode: "temp_snapshot",
|
|
@@ -199640,12 +199727,13 @@ async function runReview(tool, request, options = {}) {
|
|
|
199640
199727
|
}
|
|
199641
199728
|
}
|
|
199642
199729
|
async function runAgents(input2) {
|
|
199730
|
+
const agentRoles = resolveAgentRoles(input2.config);
|
|
199643
199731
|
const agentInputs = ["codex", "claude"].filter((agent) => input2.config.agents[agent].enabled).map((agent) => ({
|
|
199644
199732
|
traceId: input2.traceId,
|
|
199645
199733
|
agent,
|
|
199646
|
-
role: input2.config.agents[agent].role,
|
|
199734
|
+
role: agentRoles[agent] ?? input2.config.agents[agent].role,
|
|
199647
199735
|
tool: input2.tool,
|
|
199648
|
-
prompt: buildAgentPrompt(input2.tool, input2.request, agent),
|
|
199736
|
+
prompt: buildAgentPrompt(input2.tool, input2.request, agent, agentRoles[agent] ?? input2.config.agents[agent].role),
|
|
199649
199737
|
workspaceDir: input2.workspaceDir,
|
|
199650
199738
|
timeoutMs: input2.request.options?.maxAgentTimeoutMs ?? input2.config.agents[agent].timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS,
|
|
199651
199739
|
networkMode: input2.networkMode
|
|
@@ -199654,6 +199742,7 @@ async function runAgents(input2) {
|
|
|
199654
199742
|
type: "agent_started",
|
|
199655
199743
|
traceId: input2.traceId,
|
|
199656
199744
|
agent: agentInput.agent,
|
|
199745
|
+
role: agentInput.role,
|
|
199657
199746
|
timestamp: new Date().toISOString()
|
|
199658
199747
|
})));
|
|
199659
199748
|
const results = await input2.manager.runAll(agentInputs);
|
|
@@ -199662,6 +199751,7 @@ async function runAgents(input2) {
|
|
|
199662
199751
|
type: "agent_completed",
|
|
199663
199752
|
traceId: input2.traceId,
|
|
199664
199753
|
agent: result.agent,
|
|
199754
|
+
role: result.role,
|
|
199665
199755
|
status: result.status,
|
|
199666
199756
|
startedAt: result.startedAt,
|
|
199667
199757
|
completedAt: result.completedAt,
|
|
@@ -199678,6 +199768,15 @@ async function runAgents(input2) {
|
|
|
199678
199768
|
}));
|
|
199679
199769
|
return results;
|
|
199680
199770
|
}
|
|
199771
|
+
function resolveAgentRoles(config2) {
|
|
199772
|
+
const enabledAgents = ["codex", "claude"].filter((agent) => config2.agents[agent].enabled);
|
|
199773
|
+
const singleAgentMode = enabledAgents.length === 1;
|
|
199774
|
+
const roles = {};
|
|
199775
|
+
for (const agent of enabledAgents) {
|
|
199776
|
+
roles[agent] = singleAgentMode ? "combined_reviewer" : config2.agents[agent].role;
|
|
199777
|
+
}
|
|
199778
|
+
return roles;
|
|
199779
|
+
}
|
|
199681
199780
|
function defaultAgentManager(config2) {
|
|
199682
199781
|
if (process.env.KYOSO_TEST_FAKE_AGENTS === "1")
|
|
199683
199782
|
return new FakeAgentManager;
|
|
@@ -199715,6 +199814,8 @@ async function buildSecretBlockResult(input2) {
|
|
|
199715
199814
|
const resultWithoutMarkdown = {
|
|
199716
199815
|
decision: "block",
|
|
199717
199816
|
degraded: false,
|
|
199817
|
+
agentsUsed: [],
|
|
199818
|
+
reviewMode: "multi_agent",
|
|
199718
199819
|
findings: [finding],
|
|
199719
199820
|
cisaSecureByDesign: cisa,
|
|
199720
199821
|
disagreements: [],
|
|
@@ -199795,6 +199896,8 @@ async function buildPolicyBlockResult(input2) {
|
|
|
199795
199896
|
const resultWithoutMarkdown = {
|
|
199796
199897
|
decision: "block",
|
|
199797
199898
|
degraded: false,
|
|
199899
|
+
agentsUsed: [],
|
|
199900
|
+
reviewMode: "multi_agent",
|
|
199798
199901
|
findings: [input2.finding],
|
|
199799
199902
|
cisaSecureByDesign: input2.tool === "security_review" ? computeCisaGate([input2.finding], []) : undefined,
|
|
199800
199903
|
disagreements: [],
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare const kyosoConfigSchema: z.ZodObject<{
|
|
|
23
23
|
role: z.ZodEnum<{
|
|
24
24
|
implementation_reviewer: "implementation_reviewer";
|
|
25
25
|
architecture_security_reviewer: "architecture_security_reviewer";
|
|
26
|
+
combined_reviewer: "combined_reviewer";
|
|
26
27
|
}>;
|
|
27
28
|
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
28
29
|
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
@@ -43,6 +44,7 @@ export declare const kyosoConfigSchema: z.ZodObject<{
|
|
|
43
44
|
role: z.ZodEnum<{
|
|
44
45
|
implementation_reviewer: "implementation_reviewer";
|
|
45
46
|
architecture_security_reviewer: "architecture_security_reviewer";
|
|
47
|
+
combined_reviewer: "combined_reviewer";
|
|
46
48
|
}>;
|
|
47
49
|
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
48
50
|
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
package/dist/core/constants.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export declare const DEFAULT_MAX_DIFF_BYTES = 300000;
|
|
|
4
4
|
export declare const RAW_OUTPUT_MAX_CHARS = 16384;
|
|
5
5
|
export declare const TRACE_DIR = ".kyoso/traces";
|
|
6
6
|
export declare const KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
7
|
-
export declare const KYOSO_VERSION = "0.
|
|
7
|
+
export declare const KYOSO_VERSION = "0.4.0";
|
package/dist/core/types.d.ts
CHANGED
|
@@ -59,7 +59,8 @@ export type CisaSecureByDesignResult = {
|
|
|
59
59
|
notes: string[];
|
|
60
60
|
};
|
|
61
61
|
export type AgentName = "codex" | "claude";
|
|
62
|
-
export type AgentRole = "implementation_reviewer" | "architecture_security_reviewer";
|
|
62
|
+
export type AgentRole = "implementation_reviewer" | "architecture_security_reviewer" | "combined_reviewer";
|
|
63
|
+
export type ReviewMode = "multi_agent" | "single_agent";
|
|
63
64
|
export type NormalizedAgentOpinion = {
|
|
64
65
|
agent: AgentName;
|
|
65
66
|
role: string;
|
|
@@ -110,6 +111,8 @@ export type AgentRunResult = {
|
|
|
110
111
|
export type KyosoResult = {
|
|
111
112
|
decision: KyosoDecision;
|
|
112
113
|
degraded: boolean;
|
|
114
|
+
agentsUsed: AgentName[];
|
|
115
|
+
reviewMode: ReviewMode;
|
|
113
116
|
summaryMarkdown: string;
|
|
114
117
|
findings: KyosoFinding[];
|
|
115
118
|
cisaSecureByDesign?: CisaSecureByDesignResult;
|
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
47
47
|
|
|
48
48
|
// node_modules/typescript/lib/typescript.js
|
|
49
49
|
var require_typescript = __commonJS((exports, module) => {
|
|
50
|
-
var __dirname = "/
|
|
50
|
+
var __dirname = "/home/runner/work/kyoso/kyoso/node_modules/typescript/lib", __filename = "/home/runner/work/kyoso/kyoso/node_modules/typescript/lib/typescript.js";
|
|
51
51
|
/*! *****************************************************************************
|
|
52
52
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
53
53
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
@@ -183783,7 +183783,11 @@ var agentSchema = exports_external.object({
|
|
|
183783
183783
|
command: exports_external.string(),
|
|
183784
183784
|
args: exports_external.array(exports_external.string()).default([]),
|
|
183785
183785
|
model: exports_external.string().optional(),
|
|
183786
|
-
role: exports_external.enum([
|
|
183786
|
+
role: exports_external.enum([
|
|
183787
|
+
"implementation_reviewer",
|
|
183788
|
+
"architecture_security_reviewer",
|
|
183789
|
+
"combined_reviewer"
|
|
183790
|
+
]),
|
|
183787
183791
|
timeoutMs: exports_external.number().int().positive().default(120000),
|
|
183788
183792
|
env: exports_external.record(exports_external.string(), exports_external.string()).default({}),
|
|
183789
183793
|
auth: exports_external.object({
|
|
@@ -183868,7 +183872,7 @@ var defaultConfig = {
|
|
|
183868
183872
|
enabled: true,
|
|
183869
183873
|
type: "acp",
|
|
183870
183874
|
command: "npx",
|
|
183871
|
-
args: ["-y", "@agentclientprotocol/codex-acp"],
|
|
183875
|
+
args: ["-y", "@agentclientprotocol/codex-acp@1.1.0"],
|
|
183872
183876
|
role: "implementation_reviewer",
|
|
183873
183877
|
timeoutMs: 120000,
|
|
183874
183878
|
env: {
|
|
@@ -183892,7 +183896,7 @@ var defaultConfig = {
|
|
|
183892
183896
|
enabled: true,
|
|
183893
183897
|
type: "acp",
|
|
183894
183898
|
command: "npx",
|
|
183895
|
-
args: ["-y", "@agentclientprotocol/claude-agent-acp"],
|
|
183899
|
+
args: ["-y", "@agentclientprotocol/claude-agent-acp@0.57.0"],
|
|
183896
183900
|
role: "architecture_security_reviewer",
|
|
183897
183901
|
timeoutMs: 240000,
|
|
183898
183902
|
env: {
|
|
@@ -188100,7 +188104,7 @@ function buildOpinion(agent, role, tool) {
|
|
|
188100
188104
|
}
|
|
188101
188105
|
|
|
188102
188106
|
// src/acp/prompts.ts
|
|
188103
|
-
function buildAgentPrompt(tool, request, agent) {
|
|
188107
|
+
function buildAgentPrompt(tool, request, agent, role) {
|
|
188104
188108
|
const shared = [
|
|
188105
188109
|
"You are running as a Kyoso child reviewer.",
|
|
188106
188110
|
"Do not edit files.",
|
|
@@ -188113,15 +188117,26 @@ function buildAgentPrompt(tool, request, agent) {
|
|
|
188113
188117
|
"Use empty arrays when no finding, test, risk, or question exists; do not copy the example finding."
|
|
188114
188118
|
].join(`
|
|
188115
188119
|
`);
|
|
188116
|
-
const
|
|
188117
|
-
|
|
188118
|
-
|
|
188119
|
-
|
|
188120
|
-
`
|
|
188121
|
-
|
|
188122
|
-
|
|
188123
|
-
|
|
188124
|
-
|
|
188120
|
+
const roleInstructions = {
|
|
188121
|
+
implementation_reviewer: [
|
|
188122
|
+
"You are the implementation reviewer role in Kyoso.",
|
|
188123
|
+
"Focus on feasibility, minimal change, existing code consistency, regression risk, tests, migration risk, and maintainability."
|
|
188124
|
+
].join(`
|
|
188125
|
+
`),
|
|
188126
|
+
architecture_security_reviewer: [
|
|
188127
|
+
"You are the architecture and security reviewer role in Kyoso.",
|
|
188128
|
+
"Focus on architecture, threat modeling, authn/authz, secrets, privacy, secure defaults, CISA Secure by Design, and edge cases."
|
|
188129
|
+
].join(`
|
|
188130
|
+
`),
|
|
188131
|
+
combined_reviewer: [
|
|
188132
|
+
"You are the combined reviewer role in Kyoso.",
|
|
188133
|
+
"Cover both implementation review and architecture/security review in one pass.",
|
|
188134
|
+
"First assess feasibility, minimal change, existing code consistency, regression risk, tests, migration risk, and maintainability.",
|
|
188135
|
+
"Then assess architecture, threat modeling, authn/authz, secrets, privacy, secure defaults, CISA Secure by Design, and edge cases.",
|
|
188136
|
+
"Use finding category values so readers can distinguish implementation, architecture, and security concerns."
|
|
188137
|
+
].join(`
|
|
188138
|
+
`)
|
|
188139
|
+
};
|
|
188125
188140
|
const cisaInstruction = tool === "security_review" ? [
|
|
188126
188141
|
"For security_review, include cisaMapping on each security-relevant finding when applicable.",
|
|
188127
188142
|
"Also include cisaSecureByDesign with all four gate dimensions."
|
|
@@ -188129,7 +188144,9 @@ function buildAgentPrompt(tool, request, agent) {
|
|
|
188129
188144
|
`) : "For plan_review and diff_review, include cisaMapping and cisaSecureByDesign only when relevant.";
|
|
188130
188145
|
return `${shared}
|
|
188131
188146
|
|
|
188132
|
-
${
|
|
188147
|
+
Agent: ${agent}
|
|
188148
|
+
Role: ${role}
|
|
188149
|
+
${roleInstructions[role]}
|
|
188133
188150
|
|
|
188134
188151
|
Tool: ${tool}
|
|
188135
188152
|
${cisaInstruction}
|
|
@@ -188716,6 +188733,7 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
188716
188733
|
`**Decision:** ${result.decision}`,
|
|
188717
188734
|
`**Mode:** ${tool}`,
|
|
188718
188735
|
`**Agents:** ${result.agentOpinions.map((opinion) => `${title(opinion.agent)} ${opinion.status}`).join(", ")}`,
|
|
188736
|
+
`**Review mode:** ${formatReviewMode(result)}`,
|
|
188719
188737
|
`**Degraded:** ${String(result.degraded)}`,
|
|
188720
188738
|
"",
|
|
188721
188739
|
"## Summary",
|
|
@@ -188742,7 +188760,11 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
188742
188760
|
lines.push(`### ${title(opinion.agent)}`, "", `${opinion.summary} (${opinion.status})`, "");
|
|
188743
188761
|
}
|
|
188744
188762
|
lines.push("", "## Disagreements", "");
|
|
188745
|
-
|
|
188763
|
+
if (result.reviewMode === "single_agent") {
|
|
188764
|
+
lines.push("- N/A - single-agent review.");
|
|
188765
|
+
} else {
|
|
188766
|
+
lines.push(...result.disagreements.length > 0 ? result.disagreements.map((item) => `- ${item.topic}: ${item.judgeComment}`) : ["- None."]);
|
|
188767
|
+
}
|
|
188746
188768
|
lines.push("", "## Notes", "", "Kyoso did not modify files. Review was performed on a temporary snapshot.");
|
|
188747
188769
|
if (result.audit.networkMode === "unrestricted") {
|
|
188748
188770
|
lines.push("Network mode was unrestricted. File modification policy remained denied.");
|
|
@@ -188756,6 +188778,14 @@ function defaultSummaryText(result) {
|
|
|
188756
188778
|
function title(value) {
|
|
188757
188779
|
return value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
188758
188780
|
}
|
|
188781
|
+
function formatReviewMode(result) {
|
|
188782
|
+
if (result.reviewMode !== "single_agent")
|
|
188783
|
+
return "multi-agent";
|
|
188784
|
+
const opinion = result.agentOpinions[0];
|
|
188785
|
+
const agent = opinion ? title(opinion.agent) : "single agent";
|
|
188786
|
+
const role = opinion?.role === "combined_reviewer" ? "combined role" : "configured role";
|
|
188787
|
+
return `single-agent (${agent}, ${role}; cross-model verification was not performed)`;
|
|
188788
|
+
}
|
|
188759
188789
|
function notes(items) {
|
|
188760
188790
|
return (items[0] ?? "No notes.").replaceAll("|", "\\|");
|
|
188761
188791
|
}
|
|
@@ -189209,8 +189239,8 @@ async function createSnapshot(traceId, tool, request, options = {}) {
|
|
|
189209
189239
|
}
|
|
189210
189240
|
await writeFile2(join3(contextDir, "request.json"), JSON.stringify(stripContents(request), null, 2), "utf8");
|
|
189211
189241
|
await writeFile2(join3(contextDir, "selected_files_manifest.json"), JSON.stringify(buildSelectedFilesManifest(request), null, 2), "utf8");
|
|
189212
|
-
await writeFile2(join3(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex"), "utf8");
|
|
189213
|
-
await writeFile2(join3(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude"), "utf8");
|
|
189242
|
+
await writeFile2(join3(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex", options.agentRoles?.codex ?? "implementation_reviewer"), "utf8");
|
|
189243
|
+
await writeFile2(join3(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude", options.agentRoles?.claude ?? "architecture_security_reviewer"), "utf8");
|
|
189214
189244
|
if (request.repoSummary)
|
|
189215
189245
|
await writeFile2(join3(contextDir, "repo_summary.md"), request.repoSummary, "utf8");
|
|
189216
189246
|
if (request.currentPlan)
|
|
@@ -189372,9 +189402,11 @@ async function runReview(tool, request, options = {}) {
|
|
|
189372
189402
|
allowPatterns
|
|
189373
189403
|
});
|
|
189374
189404
|
warnings.push(...built.warnings);
|
|
189405
|
+
const agentRoles = resolveAgentRoles(loaded.config);
|
|
189375
189406
|
snapshot = await createSnapshot(traceId, tool, built.request, {
|
|
189376
189407
|
denyPatterns,
|
|
189377
|
-
allowPatterns
|
|
189408
|
+
allowPatterns,
|
|
189409
|
+
agentRoles
|
|
189378
189410
|
});
|
|
189379
189411
|
await trace.write({
|
|
189380
189412
|
type: "snapshot_created",
|
|
@@ -189394,6 +189426,8 @@ async function runReview(tool, request, options = {}) {
|
|
|
189394
189426
|
trace
|
|
189395
189427
|
});
|
|
189396
189428
|
const normalizedAgentResults = agentResults.map(normalizeAgentRunResult);
|
|
189429
|
+
const agentsUsed = normalizedAgentResults.map((result2) => result2.agent);
|
|
189430
|
+
const reviewMode = agentsUsed.length === 1 ? "single_agent" : "multi_agent";
|
|
189397
189431
|
const completed = normalizedAgentResults.filter((result2) => result2.status === "completed");
|
|
189398
189432
|
const degraded = completed.length !== agentResults.length;
|
|
189399
189433
|
let aggregate = aggregateAgentResults(normalizedAgentResults);
|
|
@@ -189445,6 +189479,8 @@ async function runReview(tool, request, options = {}) {
|
|
|
189445
189479
|
const resultWithoutMarkdown = {
|
|
189446
189480
|
decision,
|
|
189447
189481
|
degraded,
|
|
189482
|
+
agentsUsed,
|
|
189483
|
+
reviewMode,
|
|
189448
189484
|
findings: aggregate.findings,
|
|
189449
189485
|
cisaSecureByDesign: cisa,
|
|
189450
189486
|
disagreements: aggregate.disagreements,
|
|
@@ -189457,7 +189493,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
189457
189493
|
traceId,
|
|
189458
189494
|
startedAt,
|
|
189459
189495
|
completedAt,
|
|
189460
|
-
agentsUsed
|
|
189496
|
+
agentsUsed,
|
|
189461
189497
|
redactionsApplied: secretScan.redactions,
|
|
189462
189498
|
networkMode,
|
|
189463
189499
|
workspaceMode: "temp_snapshot",
|
|
@@ -189516,12 +189552,13 @@ async function runReview(tool, request, options = {}) {
|
|
|
189516
189552
|
}
|
|
189517
189553
|
}
|
|
189518
189554
|
async function runAgents(input) {
|
|
189555
|
+
const agentRoles = resolveAgentRoles(input.config);
|
|
189519
189556
|
const agentInputs = ["codex", "claude"].filter((agent) => input.config.agents[agent].enabled).map((agent) => ({
|
|
189520
189557
|
traceId: input.traceId,
|
|
189521
189558
|
agent,
|
|
189522
|
-
role: input.config.agents[agent].role,
|
|
189559
|
+
role: agentRoles[agent] ?? input.config.agents[agent].role,
|
|
189523
189560
|
tool: input.tool,
|
|
189524
|
-
prompt: buildAgentPrompt(input.tool, input.request, agent),
|
|
189561
|
+
prompt: buildAgentPrompt(input.tool, input.request, agent, agentRoles[agent] ?? input.config.agents[agent].role),
|
|
189525
189562
|
workspaceDir: input.workspaceDir,
|
|
189526
189563
|
timeoutMs: input.request.options?.maxAgentTimeoutMs ?? input.config.agents[agent].timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS,
|
|
189527
189564
|
networkMode: input.networkMode
|
|
@@ -189530,6 +189567,7 @@ async function runAgents(input) {
|
|
|
189530
189567
|
type: "agent_started",
|
|
189531
189568
|
traceId: input.traceId,
|
|
189532
189569
|
agent: agentInput.agent,
|
|
189570
|
+
role: agentInput.role,
|
|
189533
189571
|
timestamp: new Date().toISOString()
|
|
189534
189572
|
})));
|
|
189535
189573
|
const results = await input.manager.runAll(agentInputs);
|
|
@@ -189538,6 +189576,7 @@ async function runAgents(input) {
|
|
|
189538
189576
|
type: "agent_completed",
|
|
189539
189577
|
traceId: input.traceId,
|
|
189540
189578
|
agent: result.agent,
|
|
189579
|
+
role: result.role,
|
|
189541
189580
|
status: result.status,
|
|
189542
189581
|
startedAt: result.startedAt,
|
|
189543
189582
|
completedAt: result.completedAt,
|
|
@@ -189554,6 +189593,15 @@ async function runAgents(input) {
|
|
|
189554
189593
|
}));
|
|
189555
189594
|
return results;
|
|
189556
189595
|
}
|
|
189596
|
+
function resolveAgentRoles(config2) {
|
|
189597
|
+
const enabledAgents = ["codex", "claude"].filter((agent) => config2.agents[agent].enabled);
|
|
189598
|
+
const singleAgentMode = enabledAgents.length === 1;
|
|
189599
|
+
const roles = {};
|
|
189600
|
+
for (const agent of enabledAgents) {
|
|
189601
|
+
roles[agent] = singleAgentMode ? "combined_reviewer" : config2.agents[agent].role;
|
|
189602
|
+
}
|
|
189603
|
+
return roles;
|
|
189604
|
+
}
|
|
189557
189605
|
function defaultAgentManager(config2) {
|
|
189558
189606
|
if (process.env.KYOSO_TEST_FAKE_AGENTS === "1")
|
|
189559
189607
|
return new FakeAgentManager;
|
|
@@ -189591,6 +189639,8 @@ async function buildSecretBlockResult(input) {
|
|
|
189591
189639
|
const resultWithoutMarkdown = {
|
|
189592
189640
|
decision: "block",
|
|
189593
189641
|
degraded: false,
|
|
189642
|
+
agentsUsed: [],
|
|
189643
|
+
reviewMode: "multi_agent",
|
|
189594
189644
|
findings: [finding],
|
|
189595
189645
|
cisaSecureByDesign: cisa,
|
|
189596
189646
|
disagreements: [],
|
|
@@ -189671,6 +189721,8 @@ async function buildPolicyBlockResult(input) {
|
|
|
189671
189721
|
const resultWithoutMarkdown = {
|
|
189672
189722
|
decision: "block",
|
|
189673
189723
|
degraded: false,
|
|
189724
|
+
agentsUsed: [],
|
|
189725
|
+
reviewMode: "multi_agent",
|
|
189674
189726
|
findings: [input.finding],
|
|
189675
189727
|
cisaSecureByDesign: input.tool === "security_review" ? computeCisaGate([input.finding], []) : undefined,
|
|
189676
189728
|
disagreements: [],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KyosoReviewRequest, ReviewTool } from "../core/types.js";
|
|
1
|
+
import type { AgentName, AgentRole, KyosoReviewRequest, ReviewTool } from "../core/types.js";
|
|
2
2
|
export type Snapshot = {
|
|
3
3
|
root: string;
|
|
4
4
|
repoDir: string;
|
|
@@ -8,4 +8,5 @@ export type Snapshot = {
|
|
|
8
8
|
export declare function createSnapshot(traceId: string, tool: ReviewTool, request: KyosoReviewRequest, options?: {
|
|
9
9
|
denyPatterns?: string[];
|
|
10
10
|
allowPatterns?: string[];
|
|
11
|
+
agentRoles?: Partial<Record<AgentName, AgentRole>>;
|
|
11
12
|
}): Promise<Snapshot>;
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kyo-so/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Kyo-so: MCP-native, ACP-powered multi-agent review gates for AI coding workflows.",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/hokupod/kyoso.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/hokupod/kyoso#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/hokupod/kyoso/issues"
|
|
13
|
+
},
|
|
6
14
|
"type": "module",
|
|
7
15
|
"engines": {
|
|
8
16
|
"node": ">=20"
|