@kyo-so/cli 0.8.0 → 0.9.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/.agents/skills/kyoso-review/SKILL.md +8 -5
- package/.agents/skills/kyoso-review/agents/openai.yaml +0 -7
- package/CHANGELOG.md +41 -0
- package/README.ja.md +63 -7
- package/README.md +63 -7
- package/README.zh-CN.md +63 -7
- package/dist/audit/safeTraceFile.d.ts +20 -0
- package/dist/audit/stateRoot.d.ts +38 -0
- package/dist/audit/trace.d.ts +15 -3
- package/dist/bin/kyoso.js +2582 -493
- package/dist/cli/codexPluginDetector.d.ts +70 -0
- package/dist/cli/doctor.d.ts +3 -0
- package/dist/cli/integration.d.ts +30 -0
- package/dist/cli/knownSkillDigests.d.ts +11 -0
- package/dist/cli/pluginRuntimeContract.d.ts +232 -0
- package/dist/cli/setup.d.ts +24 -4
- package/dist/cli/skillInstall.d.ts +16 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/runReview.d.ts +2 -0
- package/dist/index.js +627 -115
- package/dist/utils/pathContainment.d.ts +1 -0
- package/package.json +9 -2
package/dist/index.js
CHANGED
|
@@ -169498,7 +169498,7 @@ function defineConfig(config) {
|
|
|
169498
169498
|
return config;
|
|
169499
169499
|
}
|
|
169500
169500
|
// src/core/runReview.ts
|
|
169501
|
-
import { resolve as
|
|
169501
|
+
import { resolve as resolve6 } from "node:path";
|
|
169502
169502
|
|
|
169503
169503
|
// node_modules/zod/v4/classic/external.js
|
|
169504
169504
|
var exports_external = {};
|
|
@@ -184261,6 +184261,7 @@ var REDACTION = "[KYOSO_REDACTED]";
|
|
|
184261
184261
|
// src/core/constants.ts
|
|
184262
184262
|
var DEFAULT_AGENT_TIMEOUT_MS = 120000;
|
|
184263
184263
|
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
184264
|
+
var TRACE_DIR = ".kyoso/traces";
|
|
184264
184265
|
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
184265
184266
|
|
|
184266
184267
|
// src/security/sanitizeText.ts
|
|
@@ -185721,6 +185722,48 @@ function requiredDefaultOnError(schema, fallback) {
|
|
|
185721
185722
|
return exports_external.NEVER;
|
|
185722
185723
|
});
|
|
185723
185724
|
}
|
|
185725
|
+
function stringTag(value, key) {
|
|
185726
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
185727
|
+
return;
|
|
185728
|
+
}
|
|
185729
|
+
const tag = value[key];
|
|
185730
|
+
return typeof tag === "string" ? tag : undefined;
|
|
185731
|
+
}
|
|
185732
|
+
function excludeKnownTags(schema, key, knownTags) {
|
|
185733
|
+
return schema.superRefine((value, context) => {
|
|
185734
|
+
const tag = stringTag(value, key);
|
|
185735
|
+
if (tag !== undefined && knownTags.includes(tag)) {
|
|
185736
|
+
context.addIssue({
|
|
185737
|
+
code: "custom",
|
|
185738
|
+
path: [key],
|
|
185739
|
+
message: `${key} ${JSON.stringify(tag)} is reserved by a known variant, ` + `but the value does not match that variant's schema`
|
|
185740
|
+
});
|
|
185741
|
+
}
|
|
185742
|
+
});
|
|
185743
|
+
}
|
|
185744
|
+
function preserveCustomPayload(schema, key, knownTags) {
|
|
185745
|
+
return exports_external.unknown().transform((value, context) => {
|
|
185746
|
+
const result = schema.safeParse(value);
|
|
185747
|
+
if (!result.success) {
|
|
185748
|
+
for (const issue2 of result.error.issues) {
|
|
185749
|
+
context.addIssue({ ...issue2, input: value });
|
|
185750
|
+
}
|
|
185751
|
+
return exports_external.NEVER;
|
|
185752
|
+
}
|
|
185753
|
+
const output = result.data;
|
|
185754
|
+
const tag = stringTag(value, key);
|
|
185755
|
+
if (tag !== undefined && !knownTags.includes(tag)) {
|
|
185756
|
+
const raw = value;
|
|
185757
|
+
for (const [property, rawValue] of Object.entries(raw)) {
|
|
185758
|
+
if (property === "__proto__")
|
|
185759
|
+
continue;
|
|
185760
|
+
if (!Object.hasOwn(output, property))
|
|
185761
|
+
output[property] = rawValue;
|
|
185762
|
+
}
|
|
185763
|
+
}
|
|
185764
|
+
return output;
|
|
185765
|
+
});
|
|
185766
|
+
}
|
|
185724
185767
|
function vecSkipError(itemSchema) {
|
|
185725
185768
|
return exports_external.array(itemSchema.catch(skippedItem)).transform((items) => items.filter((item) => item !== skippedItem));
|
|
185726
185769
|
}
|
|
@@ -186144,15 +186187,15 @@ var zTitledMultiSelectItems = object({
|
|
|
186144
186187
|
return;
|
|
186145
186188
|
})
|
|
186146
186189
|
});
|
|
186147
|
-
var zMultiSelectItems = union([
|
|
186190
|
+
var zMultiSelectItems = preserveCustomPayload(union([
|
|
186148
186191
|
zStringMultiSelectItems.and(object({
|
|
186149
186192
|
type: literal("string")
|
|
186150
186193
|
})),
|
|
186151
|
-
object({
|
|
186194
|
+
excludeKnownTags(object({
|
|
186152
186195
|
type: string2()
|
|
186153
|
-
}),
|
|
186196
|
+
}), "type", ["string"]),
|
|
186154
186197
|
zTitledMultiSelectItems
|
|
186155
|
-
]);
|
|
186198
|
+
]), "type", ["string"]);
|
|
186156
186199
|
var zMultiSelectPropertySchema = object({
|
|
186157
186200
|
title: defaultOnError(string2().nullish(), () => {
|
|
186158
186201
|
return;
|
|
@@ -186170,7 +186213,7 @@ var zMultiSelectPropertySchema = object({
|
|
|
186170
186213
|
return;
|
|
186171
186214
|
})
|
|
186172
186215
|
});
|
|
186173
|
-
var zElicitationPropertySchema = union([
|
|
186216
|
+
var zElicitationPropertySchema = preserveCustomPayload(union([
|
|
186174
186217
|
zStringPropertySchema.and(object({
|
|
186175
186218
|
type: literal("string")
|
|
186176
186219
|
})),
|
|
@@ -186186,10 +186229,10 @@ var zElicitationPropertySchema = union([
|
|
|
186186
186229
|
zMultiSelectPropertySchema.and(object({
|
|
186187
186230
|
type: literal("array")
|
|
186188
186231
|
})),
|
|
186189
|
-
object({
|
|
186232
|
+
excludeKnownTags(object({
|
|
186190
186233
|
type: string2()
|
|
186191
|
-
})
|
|
186192
|
-
]);
|
|
186234
|
+
}), "type", ["array", "boolean", "integer", "number", "string"])
|
|
186235
|
+
]), "type", ["array", "boolean", "integer", "number", "string"]);
|
|
186193
186236
|
var zElicitationSchema = object({
|
|
186194
186237
|
type: defaultOnError(zElicitationSchemaType.optional().default("object"), () => "object"),
|
|
186195
186238
|
title: defaultOnError(string2().nullish(), () => {
|
|
@@ -186212,22 +186255,22 @@ var zElicitationUrlMode = intersection(union([zElicitationSessionScope, zElicita
|
|
|
186212
186255
|
elicitationId: zElicitationId,
|
|
186213
186256
|
url: url()
|
|
186214
186257
|
}));
|
|
186215
|
-
var zCreateElicitationRequest = intersection(union([
|
|
186258
|
+
var zCreateElicitationRequest = preserveCustomPayload(intersection(union([
|
|
186216
186259
|
zElicitationFormMode.and(object({
|
|
186217
186260
|
mode: literal("form")
|
|
186218
186261
|
})),
|
|
186219
186262
|
zElicitationUrlMode.and(object({
|
|
186220
186263
|
mode: literal("url")
|
|
186221
186264
|
})),
|
|
186222
|
-
intersection(union([zElicitationSessionScope, zElicitationRequestScope]), object({
|
|
186265
|
+
excludeKnownTags(intersection(union([zElicitationSessionScope, zElicitationRequestScope]), object({
|
|
186223
186266
|
mode: string2()
|
|
186224
|
-
}))
|
|
186267
|
+
})), "mode", ["form", "url"])
|
|
186225
186268
|
]), object({
|
|
186226
186269
|
message: string2(),
|
|
186227
186270
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
186228
186271
|
return;
|
|
186229
186272
|
})
|
|
186230
|
-
}));
|
|
186273
|
+
})), "mode", ["form", "url"]);
|
|
186231
186274
|
var zMcpServerAcpId = string2();
|
|
186232
186275
|
var zConnectMcpRequest = object({
|
|
186233
186276
|
serverId: zMcpServerAcpId,
|
|
@@ -187827,7 +187870,7 @@ var zElicitationContentValue = union([
|
|
|
187827
187870
|
var zElicitationAcceptAction = object({
|
|
187828
187871
|
content: record(string2(), zElicitationContentValue).nullish()
|
|
187829
187872
|
});
|
|
187830
|
-
var zCreateElicitationResponse = intersection(union([
|
|
187873
|
+
var zCreateElicitationResponse = preserveCustomPayload(intersection(union([
|
|
187831
187874
|
zElicitationAcceptAction.and(object({
|
|
187832
187875
|
action: literal("accept")
|
|
187833
187876
|
})),
|
|
@@ -187837,14 +187880,14 @@ var zCreateElicitationResponse = intersection(union([
|
|
|
187837
187880
|
object({
|
|
187838
187881
|
action: literal("cancel")
|
|
187839
187882
|
}),
|
|
187840
|
-
object({
|
|
187883
|
+
excludeKnownTags(object({
|
|
187841
187884
|
action: string2()
|
|
187842
|
-
})
|
|
187885
|
+
}), "action", ["accept", "cancel", "decline"])
|
|
187843
187886
|
]), object({
|
|
187844
187887
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
187845
187888
|
return;
|
|
187846
187889
|
})
|
|
187847
|
-
}));
|
|
187890
|
+
})), "action", ["accept", "cancel", "decline"]);
|
|
187848
187891
|
var zConnectMcpResponse = object({
|
|
187849
187892
|
connectionId: zMcpConnectionId,
|
|
187850
187893
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
@@ -187980,6 +188023,24 @@ var zCancelRequestNotification = object({
|
|
|
187980
188023
|
return;
|
|
187981
188024
|
})
|
|
187982
188025
|
});
|
|
188026
|
+
|
|
188027
|
+
// node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js
|
|
188028
|
+
var zGuardCreateElicitationRequestForm = zElicitationFormMode.and(object({ mode: literal("form") })).and(object({ message: string2() }));
|
|
188029
|
+
var zGuardCreateElicitationRequestUrl = zElicitationUrlMode.and(object({ mode: literal("url") })).and(object({ message: string2() }));
|
|
188030
|
+
var zGuardCreateElicitationRequestCustom = union([zElicitationSessionScope, zElicitationRequestScope]).and(object({ message: string2() }));
|
|
188031
|
+
var zGuardElicitationPropertySchemaString = zStringPropertySchema.and(object({ type: literal("string") }));
|
|
188032
|
+
var zGuardElicitationPropertySchemaNumber = zNumberPropertySchema.and(object({ type: literal("number") }));
|
|
188033
|
+
var zGuardElicitationPropertySchemaInteger = zIntegerPropertySchema.and(object({ type: literal("integer") }));
|
|
188034
|
+
var zGuardElicitationPropertySchemaBoolean = zBooleanPropertySchema.and(object({ type: literal("boolean") }));
|
|
188035
|
+
var zGuardElicitationPropertySchemaArray = zMultiSelectPropertySchema.and(object({ type: literal("array") }));
|
|
188036
|
+
var zGuardMultiSelectItemsString = zStringMultiSelectItems.and(object({ type: literal("string") }));
|
|
188037
|
+
var zGuardCreateElicitationResponseAccept = zElicitationAcceptAction.and(object({ action: literal("accept") }));
|
|
188038
|
+
var zGuardCreateElicitationResponseDecline = object({
|
|
188039
|
+
action: literal("decline")
|
|
188040
|
+
});
|
|
188041
|
+
var zGuardCreateElicitationResponseCancel = object({
|
|
188042
|
+
action: literal("cancel")
|
|
188043
|
+
});
|
|
187983
188044
|
// node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
|
|
187984
188045
|
var CANCEL_REQUEST_METHOD = "$/cancel_request";
|
|
187985
188046
|
function isRecord5(value) {
|
|
@@ -190721,9 +190782,10 @@ function normalizeTitle(value) {
|
|
|
190721
190782
|
return value.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
190722
190783
|
}
|
|
190723
190784
|
|
|
190724
|
-
// src/audit/
|
|
190725
|
-
import {
|
|
190726
|
-
import {
|
|
190785
|
+
// src/audit/stateRoot.ts
|
|
190786
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
190787
|
+
import { lstat, mkdir as mkdir2, realpath as realpath2 } from "node:fs/promises";
|
|
190788
|
+
import { basename, dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve5 } from "node:path";
|
|
190727
190789
|
|
|
190728
190790
|
// src/context/pathPolicy.ts
|
|
190729
190791
|
import { normalize, sep } from "node:path";
|
|
@@ -190785,6 +190847,325 @@ function matchesPathPattern(path, patterns, mode) {
|
|
|
190785
190847
|
});
|
|
190786
190848
|
}
|
|
190787
190849
|
|
|
190850
|
+
// src/utils/pathContainment.ts
|
|
190851
|
+
import { resolve as resolve4, sep as sep2 } from "node:path";
|
|
190852
|
+
function isPathWithin(candidate, root) {
|
|
190853
|
+
const resolvedCandidate = resolve4(candidate);
|
|
190854
|
+
const resolvedRoot = resolve4(root);
|
|
190855
|
+
const boundary = resolvedRoot.endsWith(sep2) ? resolvedRoot : `${resolvedRoot}${sep2}`;
|
|
190856
|
+
return resolvedCandidate === resolvedRoot || resolvedCandidate.startsWith(boundary);
|
|
190857
|
+
}
|
|
190858
|
+
|
|
190859
|
+
// src/audit/stateRoot.ts
|
|
190860
|
+
var PRIVATE_DIRECTORY_MODE = 448;
|
|
190861
|
+
var UNSAFE_DIRECTORY_MODE = 18;
|
|
190862
|
+
var STICKY_DIRECTORY_MODE = 512;
|
|
190863
|
+
var AUDIT_WARNING_DIRECTORY_IGNORED = "AUDIT_DIRECTORY_IGNORED: Audit directory is invalid; using the default logical directory.";
|
|
190864
|
+
var AUDIT_WARNING_UNSUPPORTED_PLATFORM = "AUDIT_DISABLED_UNSUPPORTED_PLATFORM: Audit trace writing is unavailable on this platform.";
|
|
190865
|
+
var AUDIT_WARNING_UNSUPPORTED_CAPABILITY = "AUDIT_DISABLED_UNSUPPORTED_CAPABILITY: Audit trace writing requires unavailable filesystem capabilities.";
|
|
190866
|
+
var AUDIT_WARNING_UNSAFE_STATE_ROOT = "AUDIT_DISABLED_UNSAFE_STATE_ROOT: Audit state root could not be verified.";
|
|
190867
|
+
async function resolveAuditStateRoot(options) {
|
|
190868
|
+
const warnings = [];
|
|
190869
|
+
const logicalDirectory = validateAuditDirectory(options.directory, warnings);
|
|
190870
|
+
const platform = options.platform ?? process.platform;
|
|
190871
|
+
if (platform === "win32") {
|
|
190872
|
+
return { warnings: [...warnings, AUDIT_WARNING_UNSUPPORTED_PLATFORM] };
|
|
190873
|
+
}
|
|
190874
|
+
const uid = getCurrentUid(options.getuid);
|
|
190875
|
+
if (uid === undefined) {
|
|
190876
|
+
return { warnings: [...warnings, AUDIT_WARNING_UNSUPPORTED_CAPABILITY] };
|
|
190877
|
+
}
|
|
190878
|
+
try {
|
|
190879
|
+
const workspaceRoot = await realpath2(resolve5(options.cwd));
|
|
190880
|
+
const candidate = resolveStateBaseCandidate(options.env ?? process.env);
|
|
190881
|
+
if (!candidate)
|
|
190882
|
+
throw new Error("missing trusted state base");
|
|
190883
|
+
const stateBase = await ensureTrustedStateBase({
|
|
190884
|
+
candidate,
|
|
190885
|
+
workspaceRoot,
|
|
190886
|
+
uid
|
|
190887
|
+
});
|
|
190888
|
+
const kyosoRoot = await ensureTrustedDirectory({
|
|
190889
|
+
root: stateBase,
|
|
190890
|
+
segments: ["kyoso"],
|
|
190891
|
+
uid,
|
|
190892
|
+
workspaceRoot
|
|
190893
|
+
});
|
|
190894
|
+
return {
|
|
190895
|
+
stateBase,
|
|
190896
|
+
kyosoRoot,
|
|
190897
|
+
workspaceRoot,
|
|
190898
|
+
workspaceHash: createHash2("sha256").update(workspaceRoot).digest("hex"),
|
|
190899
|
+
logicalDirectory,
|
|
190900
|
+
uid,
|
|
190901
|
+
warnings
|
|
190902
|
+
};
|
|
190903
|
+
} catch {
|
|
190904
|
+
return { warnings: [...warnings, AUDIT_WARNING_UNSAFE_STATE_ROOT] };
|
|
190905
|
+
}
|
|
190906
|
+
}
|
|
190907
|
+
async function ensureTrustedDirectory(options) {
|
|
190908
|
+
const realRoot = await realpath2(options.root);
|
|
190909
|
+
assertSafeDirectory(realRoot, options.uid, await lstat(realRoot));
|
|
190910
|
+
if (options.workspaceRoot && isPathWithin(realRoot, options.workspaceRoot)) {
|
|
190911
|
+
throw new Error("trusted directory resolves inside workspace");
|
|
190912
|
+
}
|
|
190913
|
+
await assertTrustedAncestorChain(realRoot, options.uid);
|
|
190914
|
+
let current = realRoot;
|
|
190915
|
+
for (const segment of options.segments) {
|
|
190916
|
+
if (!isSafePathSegment(segment))
|
|
190917
|
+
throw new Error("unsafe path segment");
|
|
190918
|
+
const next = join3(current, segment);
|
|
190919
|
+
let entry = await optionalLstat(next);
|
|
190920
|
+
if (!entry) {
|
|
190921
|
+
await createDirectory(next);
|
|
190922
|
+
entry = await lstat(next);
|
|
190923
|
+
}
|
|
190924
|
+
assertSafeDirectory(next, options.uid, entry);
|
|
190925
|
+
const realNext = await realpath2(next);
|
|
190926
|
+
if (!isPathWithin(realNext, realRoot)) {
|
|
190927
|
+
throw new Error("managed directory escaped trusted root");
|
|
190928
|
+
}
|
|
190929
|
+
if (options.workspaceRoot && isPathWithin(realNext, options.workspaceRoot)) {
|
|
190930
|
+
throw new Error("managed directory resolves inside workspace");
|
|
190931
|
+
}
|
|
190932
|
+
current = realNext;
|
|
190933
|
+
}
|
|
190934
|
+
return current;
|
|
190935
|
+
}
|
|
190936
|
+
function isResolvedAuditStateRoot(resolution) {
|
|
190937
|
+
return "kyosoRoot" in resolution;
|
|
190938
|
+
}
|
|
190939
|
+
function validateAuditDirectory(directory, warnings) {
|
|
190940
|
+
try {
|
|
190941
|
+
if (directory.trim().length === 0 || isAbsolute2(directory) || directory.split(/[\\/]+/).includes("..")) {
|
|
190942
|
+
throw new Error("unsafe logical directory");
|
|
190943
|
+
}
|
|
190944
|
+
const normalized = normalizeRelativePath(directory);
|
|
190945
|
+
if (normalized === "." || normalized.split("/").includes("..")) {
|
|
190946
|
+
throw new Error("unsafe logical directory");
|
|
190947
|
+
}
|
|
190948
|
+
return normalized;
|
|
190949
|
+
} catch {
|
|
190950
|
+
warnings.push(AUDIT_WARNING_DIRECTORY_IGNORED);
|
|
190951
|
+
return TRACE_DIR;
|
|
190952
|
+
}
|
|
190953
|
+
}
|
|
190954
|
+
function resolveStateBaseCandidate(env) {
|
|
190955
|
+
const xdgStateHome = env.XDG_STATE_HOME?.trim();
|
|
190956
|
+
if (xdgStateHome && isAbsolute2(xdgStateHome)) {
|
|
190957
|
+
return resolve5(xdgStateHome);
|
|
190958
|
+
}
|
|
190959
|
+
const home = env.HOME?.trim();
|
|
190960
|
+
if (!home || !isAbsolute2(home))
|
|
190961
|
+
return;
|
|
190962
|
+
return join3(resolve5(home), ".local", "state");
|
|
190963
|
+
}
|
|
190964
|
+
async function ensureTrustedStateBase(options) {
|
|
190965
|
+
if (isPathWithin(options.candidate, options.workspaceRoot)) {
|
|
190966
|
+
throw new Error("state base is inside workspace");
|
|
190967
|
+
}
|
|
190968
|
+
const existing = await findExistingAncestor(options.candidate);
|
|
190969
|
+
assertSafeDirectory(existing.path, options.uid, await lstat(existing.path), {
|
|
190970
|
+
allowFilesystemRoot: true
|
|
190971
|
+
});
|
|
190972
|
+
const realExisting = await realpath2(existing.path);
|
|
190973
|
+
if (isPathWithin(realExisting, options.workspaceRoot)) {
|
|
190974
|
+
throw new Error("state base resolves inside workspace");
|
|
190975
|
+
}
|
|
190976
|
+
await assertTrustedAncestorChain(realExisting, options.uid);
|
|
190977
|
+
let current = realExisting;
|
|
190978
|
+
for (const segment of existing.missingSegments) {
|
|
190979
|
+
current = join3(current, segment);
|
|
190980
|
+
await createDirectory(current);
|
|
190981
|
+
const entry = await lstat(current);
|
|
190982
|
+
assertSafeDirectory(current, options.uid, entry);
|
|
190983
|
+
const realCurrent = await realpath2(current);
|
|
190984
|
+
if (!isPathWithin(realCurrent, realExisting)) {
|
|
190985
|
+
throw new Error("state base changed while being created");
|
|
190986
|
+
}
|
|
190987
|
+
if (isPathWithin(realCurrent, options.workspaceRoot)) {
|
|
190988
|
+
throw new Error("state base resolves inside workspace");
|
|
190989
|
+
}
|
|
190990
|
+
current = realCurrent;
|
|
190991
|
+
}
|
|
190992
|
+
const stateBase = await realpath2(current);
|
|
190993
|
+
assertSafeDirectory(stateBase, options.uid, await lstat(stateBase));
|
|
190994
|
+
if (isPathWithin(stateBase, options.workspaceRoot)) {
|
|
190995
|
+
throw new Error("state base resolves inside workspace");
|
|
190996
|
+
}
|
|
190997
|
+
await assertTrustedAncestorChain(stateBase, options.uid);
|
|
190998
|
+
return stateBase;
|
|
190999
|
+
}
|
|
191000
|
+
async function findExistingAncestor(candidate) {
|
|
191001
|
+
let current = resolve5(candidate);
|
|
191002
|
+
const missingSegments = [];
|
|
191003
|
+
while (true) {
|
|
191004
|
+
const entry = await optionalLstat(current);
|
|
191005
|
+
if (entry) {
|
|
191006
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) {
|
|
191007
|
+
throw new Error("state base ancestor is unsafe");
|
|
191008
|
+
}
|
|
191009
|
+
return { path: current, missingSegments };
|
|
191010
|
+
}
|
|
191011
|
+
const parent = dirname3(current);
|
|
191012
|
+
if (parent === current)
|
|
191013
|
+
throw new Error("state base has no existing ancestor");
|
|
191014
|
+
missingSegments.unshift(basename(current));
|
|
191015
|
+
current = parent;
|
|
191016
|
+
}
|
|
191017
|
+
}
|
|
191018
|
+
function assertSafeDirectory(path, uid, entry, options = {}) {
|
|
191019
|
+
const stat = entry;
|
|
191020
|
+
if (!stat || stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
191021
|
+
throw new Error("state directory is unsafe");
|
|
191022
|
+
}
|
|
191023
|
+
if (numericStatValue(stat.uid) !== uid && !(options.allowFilesystemRoot && isFilesystemRoot(path))) {
|
|
191024
|
+
throw new Error("state directory owner is unsafe");
|
|
191025
|
+
}
|
|
191026
|
+
const mode = numericStatValue(stat.mode);
|
|
191027
|
+
if ((mode & UNSAFE_DIRECTORY_MODE) !== 0) {
|
|
191028
|
+
throw new Error("state directory mode is unsafe");
|
|
191029
|
+
}
|
|
191030
|
+
}
|
|
191031
|
+
function numericStatValue(value) {
|
|
191032
|
+
return typeof value === "number" ? value : Number(value);
|
|
191033
|
+
}
|
|
191034
|
+
function getCurrentUid(getuid) {
|
|
191035
|
+
const resolveUid = getuid ?? (typeof process.getuid === "function" ? process.getuid.bind(process) : undefined);
|
|
191036
|
+
try {
|
|
191037
|
+
const uid = resolveUid?.();
|
|
191038
|
+
return typeof uid === "number" && Number.isInteger(uid) && uid >= 0 ? uid : undefined;
|
|
191039
|
+
} catch {
|
|
191040
|
+
return;
|
|
191041
|
+
}
|
|
191042
|
+
}
|
|
191043
|
+
function isFilesystemRoot(path) {
|
|
191044
|
+
return dirname3(path) === path;
|
|
191045
|
+
}
|
|
191046
|
+
async function assertTrustedAncestorChain(path, uid) {
|
|
191047
|
+
let child = path;
|
|
191048
|
+
while (!isFilesystemRoot(child)) {
|
|
191049
|
+
const parent = dirname3(child);
|
|
191050
|
+
const [parentStat, childStat] = await Promise.all([
|
|
191051
|
+
lstat(parent),
|
|
191052
|
+
lstat(child)
|
|
191053
|
+
]);
|
|
191054
|
+
if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) {
|
|
191055
|
+
throw new Error("state directory ancestor is unsafe");
|
|
191056
|
+
}
|
|
191057
|
+
const parentMode = numericStatValue(parentStat.mode);
|
|
191058
|
+
const parentUid = numericStatValue(parentStat.uid);
|
|
191059
|
+
const childUid = numericStatValue(childStat.uid);
|
|
191060
|
+
const parentWritableByOthers = (parentMode & UNSAFE_DIRECTORY_MODE) !== 0;
|
|
191061
|
+
const stickyChildEntry = (parentMode & STICKY_DIRECTORY_MODE) !== 0 && childUid === uid;
|
|
191062
|
+
if (parentUid !== uid && parentUid !== 0 || parentWritableByOthers && !stickyChildEntry) {
|
|
191063
|
+
throw new Error("state directory ancestor permissions are unsafe");
|
|
191064
|
+
}
|
|
191065
|
+
child = parent;
|
|
191066
|
+
}
|
|
191067
|
+
}
|
|
191068
|
+
function isSafePathSegment(segment) {
|
|
191069
|
+
return segment.length > 0 && segment !== "." && segment !== ".." && !segment.includes("/") && !segment.includes("\\");
|
|
191070
|
+
}
|
|
191071
|
+
async function optionalLstat(path) {
|
|
191072
|
+
try {
|
|
191073
|
+
return await lstat(path);
|
|
191074
|
+
} catch (error51) {
|
|
191075
|
+
if (isMissingPathError3(error51))
|
|
191076
|
+
return;
|
|
191077
|
+
throw error51;
|
|
191078
|
+
}
|
|
191079
|
+
}
|
|
191080
|
+
async function createDirectory(path) {
|
|
191081
|
+
try {
|
|
191082
|
+
await mkdir2(path, { mode: PRIVATE_DIRECTORY_MODE });
|
|
191083
|
+
} catch (error51) {
|
|
191084
|
+
if (!isAlreadyExistsError(error51))
|
|
191085
|
+
throw error51;
|
|
191086
|
+
}
|
|
191087
|
+
}
|
|
191088
|
+
function isMissingPathError3(error51) {
|
|
191089
|
+
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
191090
|
+
}
|
|
191091
|
+
function isAlreadyExistsError(error51) {
|
|
191092
|
+
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "EEXIST";
|
|
191093
|
+
}
|
|
191094
|
+
|
|
191095
|
+
// src/audit/safeTraceFile.ts
|
|
191096
|
+
import { constants } from "node:fs";
|
|
191097
|
+
import { lstat as lstat2, open, realpath as realpath3, stat } from "node:fs/promises";
|
|
191098
|
+
import { join as join4 } from "node:path";
|
|
191099
|
+
var AUDIT_WARNING_UNSUPPORTED_OPEN_CAPABILITY = "AUDIT_DISABLED_UNSUPPORTED_CAPABILITY: Audit trace writing requires unavailable filesystem capabilities.";
|
|
191100
|
+
async function openVerifiedTraceFile(options) {
|
|
191101
|
+
const flags = secureOpenFlags(options.openConstants);
|
|
191102
|
+
if (flags === undefined)
|
|
191103
|
+
throw new Error("secure open capability unavailable");
|
|
191104
|
+
if (!isSafeTraceId(options.traceId))
|
|
191105
|
+
throw new Error("unsafe trace id");
|
|
191106
|
+
const traceDirectory = await ensureTrustedDirectory({
|
|
191107
|
+
root: options.kyosoRoot,
|
|
191108
|
+
segments: [
|
|
191109
|
+
"workspaces",
|
|
191110
|
+
options.workspaceHash,
|
|
191111
|
+
...options.logicalDirectory.split("/"),
|
|
191112
|
+
options.date
|
|
191113
|
+
],
|
|
191114
|
+
uid: options.uid,
|
|
191115
|
+
workspaceRoot: options.workspaceRoot
|
|
191116
|
+
});
|
|
191117
|
+
const tracePath = join4(traceDirectory, `${options.traceId}.jsonl`);
|
|
191118
|
+
if (await optionalLstat2(tracePath)) {
|
|
191119
|
+
throw new Error("trace path already exists");
|
|
191120
|
+
}
|
|
191121
|
+
await options.beforeOpen?.(tracePath);
|
|
191122
|
+
const handle = await open(tracePath, flags, 384);
|
|
191123
|
+
try {
|
|
191124
|
+
const [handleStat, pathStat, realTracePath] = await Promise.all([
|
|
191125
|
+
handle.stat({ bigint: true }),
|
|
191126
|
+
stat(tracePath, { bigint: true }),
|
|
191127
|
+
realpath3(tracePath)
|
|
191128
|
+
]);
|
|
191129
|
+
if (!handleStat.isFile() || !pathStat.isFile() || handleStat.dev !== pathStat.dev || handleStat.ino !== pathStat.ino || !isPathWithin(realTracePath, options.kyosoRoot)) {
|
|
191130
|
+
throw new Error("trace file identity could not be verified");
|
|
191131
|
+
}
|
|
191132
|
+
return { handle, tracePath };
|
|
191133
|
+
} catch (error51) {
|
|
191134
|
+
try {
|
|
191135
|
+
await handle.close();
|
|
191136
|
+
} catch {}
|
|
191137
|
+
throw error51;
|
|
191138
|
+
}
|
|
191139
|
+
}
|
|
191140
|
+
function secureOpenFlags(provided) {
|
|
191141
|
+
const openConstants = provided ?? constants;
|
|
191142
|
+
const required2 = [
|
|
191143
|
+
openConstants.O_CREAT,
|
|
191144
|
+
openConstants.O_EXCL,
|
|
191145
|
+
openConstants.O_APPEND,
|
|
191146
|
+
openConstants.O_WRONLY,
|
|
191147
|
+
openConstants.O_NOFOLLOW,
|
|
191148
|
+
openConstants.O_NONBLOCK
|
|
191149
|
+
];
|
|
191150
|
+
if (required2.some((flag) => typeof flag !== "number" || flag <= 0)) {
|
|
191151
|
+
return;
|
|
191152
|
+
}
|
|
191153
|
+
return required2.reduce((combined, flag) => combined | flag, 0);
|
|
191154
|
+
}
|
|
191155
|
+
function isSafeTraceId(traceId) {
|
|
191156
|
+
return traceId.length > 0 && traceId !== "." && traceId !== ".." && !traceId.includes("/") && !traceId.includes("\\");
|
|
191157
|
+
}
|
|
191158
|
+
async function optionalLstat2(path) {
|
|
191159
|
+
try {
|
|
191160
|
+
return await lstat2(path);
|
|
191161
|
+
} catch (error51) {
|
|
191162
|
+
if (typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT") {
|
|
191163
|
+
return;
|
|
191164
|
+
}
|
|
191165
|
+
throw error51;
|
|
191166
|
+
}
|
|
191167
|
+
}
|
|
191168
|
+
|
|
190788
191169
|
// src/audit/sanitize.ts
|
|
190789
191170
|
function sanitizeForAudit(value, options = {}) {
|
|
190790
191171
|
if (typeof value === "string")
|
|
@@ -190805,46 +191186,149 @@ function sanitizeForAudit(value, options = {}) {
|
|
|
190805
191186
|
}
|
|
190806
191187
|
|
|
190807
191188
|
// src/audit/trace.ts
|
|
191189
|
+
var AUDIT_WARNING_WRITE_FAILED = "AUDIT_WRITE_FAILED: Audit trace writing failed; no further audit events will be written.";
|
|
191190
|
+
var AUDIT_WARNING_FINALIZE_FAILED = "AUDIT_FINALIZE_FAILED: Audit trace close failed.";
|
|
191191
|
+
var AUDIT_WARNING_WRITE_AFTER_FINALIZE = "AUDIT_WRITE_AFTER_FINALIZE: Audit trace is already finalized.";
|
|
190808
191192
|
function createTraceWriter(options) {
|
|
190809
191193
|
const warnings = [];
|
|
190810
|
-
|
|
190811
|
-
return {
|
|
190812
|
-
warnings,
|
|
190813
|
-
async write() {
|
|
190814
|
-
return;
|
|
190815
|
-
}
|
|
190816
|
-
};
|
|
190817
|
-
}
|
|
191194
|
+
const warningSet = new Set;
|
|
190818
191195
|
const date5 = new Date().toISOString().slice(0, 10);
|
|
190819
|
-
|
|
190820
|
-
|
|
191196
|
+
let tracePath;
|
|
191197
|
+
let handle;
|
|
191198
|
+
let disabled = !options.enabled;
|
|
191199
|
+
let finalizing = false;
|
|
191200
|
+
let finalized = false;
|
|
191201
|
+
let queue = Promise.resolve();
|
|
191202
|
+
let finalizePromise;
|
|
191203
|
+
const addWarning = (warning) => {
|
|
191204
|
+
if (warningSet.has(warning))
|
|
191205
|
+
return;
|
|
191206
|
+
warningSet.add(warning);
|
|
191207
|
+
warnings.push(warning);
|
|
191208
|
+
};
|
|
191209
|
+
const closeHandle = async () => {
|
|
191210
|
+
const current = handle;
|
|
191211
|
+
handle = undefined;
|
|
191212
|
+
if (!current)
|
|
191213
|
+
return;
|
|
191214
|
+
try {
|
|
191215
|
+
await (options.closeHandle ?? defaultCloseHandle)(current);
|
|
191216
|
+
} catch {
|
|
191217
|
+
addWarning(AUDIT_WARNING_FINALIZE_FAILED);
|
|
191218
|
+
}
|
|
191219
|
+
};
|
|
191220
|
+
const disableAfterWriteFailure = async () => {
|
|
191221
|
+
disabled = true;
|
|
191222
|
+
addWarning(AUDIT_WARNING_WRITE_FAILED);
|
|
191223
|
+
await closeHandle();
|
|
191224
|
+
};
|
|
191225
|
+
const openIfNeeded = async () => {
|
|
191226
|
+
if (handle || disabled)
|
|
191227
|
+
return;
|
|
191228
|
+
if (secureOpenFlags(options.openConstants) === undefined) {
|
|
191229
|
+
disabled = true;
|
|
191230
|
+
addWarning(AUDIT_WARNING_UNSUPPORTED_OPEN_CAPABILITY);
|
|
191231
|
+
return;
|
|
191232
|
+
}
|
|
191233
|
+
const stateRoot = await resolveAuditStateRoot({
|
|
191234
|
+
cwd: options.cwd,
|
|
191235
|
+
directory: options.directory,
|
|
191236
|
+
env: options.env,
|
|
191237
|
+
platform: options.platform,
|
|
191238
|
+
getuid: options.getuid
|
|
191239
|
+
});
|
|
191240
|
+
for (const warning of stateRoot.warnings)
|
|
191241
|
+
addWarning(warning);
|
|
191242
|
+
if (!isResolvedAuditStateRoot(stateRoot)) {
|
|
191243
|
+
disabled = true;
|
|
191244
|
+
return;
|
|
191245
|
+
}
|
|
191246
|
+
try {
|
|
191247
|
+
const opened = await openVerifiedTraceFile({
|
|
191248
|
+
kyosoRoot: stateRoot.kyosoRoot,
|
|
191249
|
+
workspaceHash: stateRoot.workspaceHash,
|
|
191250
|
+
logicalDirectory: stateRoot.logicalDirectory,
|
|
191251
|
+
date: date5,
|
|
191252
|
+
traceId: options.traceId,
|
|
191253
|
+
uid: stateRoot.uid,
|
|
191254
|
+
workspaceRoot: stateRoot.workspaceRoot,
|
|
191255
|
+
openConstants: options.openConstants,
|
|
191256
|
+
beforeOpen: options.beforeOpen
|
|
191257
|
+
});
|
|
191258
|
+
handle = opened.handle;
|
|
191259
|
+
tracePath = opened.tracePath;
|
|
191260
|
+
} catch {
|
|
191261
|
+
disabled = true;
|
|
191262
|
+
addWarning(AUDIT_WARNING_WRITE_FAILED);
|
|
191263
|
+
}
|
|
191264
|
+
};
|
|
191265
|
+
const writeOne = async (event) => {
|
|
191266
|
+
if (disabled)
|
|
191267
|
+
return;
|
|
191268
|
+
try {
|
|
191269
|
+
await openIfNeeded();
|
|
191270
|
+
if (disabled || !handle)
|
|
191271
|
+
return;
|
|
191272
|
+
const line = Buffer.from(`${JSON.stringify(sanitizeForAudit(event, {
|
|
191273
|
+
includeRawAgentOutput: options.includeRawAgentOutput
|
|
191274
|
+
}))}
|
|
191275
|
+
`, "utf8");
|
|
191276
|
+
await writeFully(handle, line, options.writeChunk);
|
|
191277
|
+
} catch {
|
|
191278
|
+
await disableAfterWriteFailure();
|
|
191279
|
+
}
|
|
191280
|
+
};
|
|
190821
191281
|
return {
|
|
190822
|
-
tracePath
|
|
191282
|
+
get tracePath() {
|
|
191283
|
+
return tracePath;
|
|
191284
|
+
},
|
|
190823
191285
|
warnings,
|
|
190824
|
-
|
|
190825
|
-
|
|
190826
|
-
|
|
190827
|
-
|
|
190828
|
-
|
|
190829
|
-
|
|
190830
|
-
|
|
190831
|
-
|
|
190832
|
-
|
|
190833
|
-
|
|
191286
|
+
write(event) {
|
|
191287
|
+
if (finalizing || finalized) {
|
|
191288
|
+
addWarning(AUDIT_WARNING_WRITE_AFTER_FINALIZE);
|
|
191289
|
+
return Promise.resolve();
|
|
191290
|
+
}
|
|
191291
|
+
const task = queue.then(() => writeOne(event));
|
|
191292
|
+
queue = task.catch(async () => {
|
|
191293
|
+
await disableAfterWriteFailure();
|
|
191294
|
+
});
|
|
191295
|
+
return task.catch(() => {
|
|
191296
|
+
return;
|
|
191297
|
+
});
|
|
191298
|
+
},
|
|
191299
|
+
finalize() {
|
|
191300
|
+
if (finalizePromise)
|
|
191301
|
+
return finalizePromise;
|
|
191302
|
+
finalizing = true;
|
|
191303
|
+
finalizePromise = queue.then(async () => {
|
|
191304
|
+
await closeHandle();
|
|
191305
|
+
finalized = true;
|
|
191306
|
+
}).catch(() => {
|
|
191307
|
+
disabled = true;
|
|
191308
|
+
addWarning(AUDIT_WARNING_FINALIZE_FAILED);
|
|
191309
|
+
finalized = true;
|
|
191310
|
+
});
|
|
191311
|
+
return finalizePromise;
|
|
190834
191312
|
}
|
|
190835
191313
|
};
|
|
190836
191314
|
}
|
|
190837
|
-
function
|
|
190838
|
-
|
|
190839
|
-
|
|
190840
|
-
|
|
191315
|
+
async function writeFully(handle, buffer, writeChunk) {
|
|
191316
|
+
let offset = 0;
|
|
191317
|
+
while (offset < buffer.byteLength) {
|
|
191318
|
+
const bytesWritten = await (writeChunk ?? defaultWriteChunk)(handle, buffer, offset);
|
|
191319
|
+
if (!Number.isInteger(bytesWritten) || bytesWritten <= 0) {
|
|
191320
|
+
throw new Error("partial audit write could not advance");
|
|
190841
191321
|
}
|
|
190842
|
-
|
|
190843
|
-
} catch {
|
|
190844
|
-
warnings.push(`Unsafe audit directory ignored: ${directory}`);
|
|
190845
|
-
return ".kyoso/traces";
|
|
191322
|
+
offset += bytesWritten;
|
|
190846
191323
|
}
|
|
190847
191324
|
}
|
|
191325
|
+
async function defaultWriteChunk(handle, buffer, offset) {
|
|
191326
|
+
const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset, null);
|
|
191327
|
+
return bytesWritten;
|
|
191328
|
+
}
|
|
191329
|
+
async function defaultCloseHandle(handle) {
|
|
191330
|
+
await handle.close();
|
|
191331
|
+
}
|
|
190848
191332
|
|
|
190849
191333
|
// src/context/truncate.ts
|
|
190850
191334
|
function truncateUtf8(input, maxBytes) {
|
|
@@ -191536,12 +192020,12 @@ function decide(input) {
|
|
|
191536
192020
|
|
|
191537
192021
|
// src/workspace/createSnapshot.ts
|
|
191538
192022
|
import { chmod, mkdir as mkdir3, mkdtemp, writeFile as writeFile2 } from "node:fs/promises";
|
|
191539
|
-
import { dirname as dirname4, join as
|
|
192023
|
+
import { dirname as dirname4, join as join5 } from "node:path";
|
|
191540
192024
|
import { tmpdir } from "node:os";
|
|
191541
192025
|
async function createSnapshot(traceId, tool, request, options = {}) {
|
|
191542
|
-
const root = await mkdtemp(
|
|
191543
|
-
const repoDir =
|
|
191544
|
-
const contextDir =
|
|
192026
|
+
const root = await mkdtemp(join5(tmpdir(), `kyoso-${traceId}-`));
|
|
192027
|
+
const repoDir = join5(root, "repo");
|
|
192028
|
+
const contextDir = join5(root, "context");
|
|
191545
192029
|
await mkdir3(repoDir, { recursive: true });
|
|
191546
192030
|
await mkdir3(contextDir, { recursive: true });
|
|
191547
192031
|
let fileCount = 0;
|
|
@@ -191551,7 +192035,7 @@ async function createSnapshot(traceId, tool, request, options = {}) {
|
|
|
191551
192035
|
continue;
|
|
191552
192036
|
if (!isAllowedPath(relative2, options.allowPatterns ?? []))
|
|
191553
192037
|
continue;
|
|
191554
|
-
const dest =
|
|
192038
|
+
const dest = join5(repoDir, relative2);
|
|
191555
192039
|
await mkdir3(dirname4(dest), { recursive: true });
|
|
191556
192040
|
await writeFile2(dest, file2.content, "utf8");
|
|
191557
192041
|
await chmod(dest, 292).catch(() => {
|
|
@@ -191559,16 +192043,16 @@ async function createSnapshot(traceId, tool, request, options = {}) {
|
|
|
191559
192043
|
});
|
|
191560
192044
|
fileCount += 1;
|
|
191561
192045
|
}
|
|
191562
|
-
await writeFile2(
|
|
191563
|
-
await writeFile2(
|
|
191564
|
-
await writeFile2(
|
|
191565
|
-
await writeFile2(
|
|
192046
|
+
await writeFile2(join5(contextDir, "request.json"), JSON.stringify(stripContents(request), null, 2), "utf8");
|
|
192047
|
+
await writeFile2(join5(contextDir, "selected_files_manifest.json"), JSON.stringify(buildSelectedFilesManifest(request), null, 2), "utf8");
|
|
192048
|
+
await writeFile2(join5(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex", options.agentRoles?.codex ?? "implementation_reviewer"), "utf8");
|
|
192049
|
+
await writeFile2(join5(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude", options.agentRoles?.claude ?? "architecture_security_reviewer"), "utf8");
|
|
191566
192050
|
if (request.repoSummary)
|
|
191567
|
-
await writeFile2(
|
|
192051
|
+
await writeFile2(join5(contextDir, "repo_summary.md"), request.repoSummary, "utf8");
|
|
191568
192052
|
if (request.currentPlan)
|
|
191569
|
-
await writeFile2(
|
|
192053
|
+
await writeFile2(join5(contextDir, "current_plan.md"), request.currentPlan, "utf8");
|
|
191570
192054
|
if (request.diff?.unifiedDiff)
|
|
191571
|
-
await writeFile2(
|
|
192055
|
+
await writeFile2(join5(contextDir, "diff.patch"), request.diff.unifiedDiff, "utf8");
|
|
191572
192056
|
return { root, repoDir, contextDir, fileCount };
|
|
191573
192057
|
}
|
|
191574
192058
|
function stripContents(request) {
|
|
@@ -191745,43 +192229,50 @@ async function runReview(tool, request, options = {}) {
|
|
|
191745
192229
|
const cwd = options.cwd ?? process.cwd();
|
|
191746
192230
|
const traceId = newTraceId();
|
|
191747
192231
|
const startedAt = new Date().toISOString();
|
|
192232
|
+
const auditEnv = { ...process.env, ...options.env };
|
|
192233
|
+
const traceWriterFactory = options.traceWriterFactory ?? createTraceWriter;
|
|
191748
192234
|
let snapshot;
|
|
191749
192235
|
try {
|
|
191750
192236
|
assertNotChildAgent(options.env ?? process.env);
|
|
191751
192237
|
} catch (error51) {
|
|
191752
192238
|
if (error51 instanceof KyosoRequestError) {
|
|
191753
192239
|
const config2 = kyosoConfigSchema.parse(defaultConfig);
|
|
191754
|
-
const trace2 =
|
|
192240
|
+
const trace2 = traceWriterFactory({
|
|
191755
192241
|
enabled: config2.audit.enabled,
|
|
191756
192242
|
directory: config2.audit.directory,
|
|
191757
192243
|
traceId,
|
|
191758
|
-
cwd
|
|
191759
|
-
|
|
191760
|
-
await trace2.write({
|
|
191761
|
-
type: "request_received",
|
|
191762
|
-
traceId,
|
|
191763
|
-
tool,
|
|
191764
|
-
timestamp: new Date().toISOString()
|
|
191765
|
-
});
|
|
191766
|
-
return await buildPolicyBlockResult({
|
|
191767
|
-
tool,
|
|
191768
|
-
trace: trace2,
|
|
191769
|
-
traceId,
|
|
191770
|
-
startedAt,
|
|
191771
|
-
networkMode: config2.network.defaultMode,
|
|
191772
|
-
warning: error51.message,
|
|
191773
|
-
finding: {
|
|
191774
|
-
id: "KYOSO-1",
|
|
191775
|
-
severity: "critical",
|
|
191776
|
-
category: "other",
|
|
191777
|
-
title: "Recursive Kyoso invocation blocked",
|
|
191778
|
-
evidence: error51.message,
|
|
191779
|
-
recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
|
|
191780
|
-
sourceAgents: ["kyoso_policy"],
|
|
191781
|
-
confidence: "high"
|
|
191782
|
-
},
|
|
191783
|
-
redactionsApplied: 0
|
|
192244
|
+
cwd,
|
|
192245
|
+
env: auditEnv
|
|
191784
192246
|
});
|
|
192247
|
+
try {
|
|
192248
|
+
await trace2.write({
|
|
192249
|
+
type: "request_received",
|
|
192250
|
+
traceId,
|
|
192251
|
+
tool,
|
|
192252
|
+
timestamp: new Date().toISOString()
|
|
192253
|
+
});
|
|
192254
|
+
return await buildPolicyBlockResult({
|
|
192255
|
+
tool,
|
|
192256
|
+
trace: trace2,
|
|
192257
|
+
traceId,
|
|
192258
|
+
startedAt,
|
|
192259
|
+
networkMode: config2.network.defaultMode,
|
|
192260
|
+
warning: error51.message,
|
|
192261
|
+
finding: {
|
|
192262
|
+
id: "KYOSO-1",
|
|
192263
|
+
severity: "critical",
|
|
192264
|
+
category: "other",
|
|
192265
|
+
title: "Recursive Kyoso invocation blocked",
|
|
192266
|
+
evidence: error51.message,
|
|
192267
|
+
recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
|
|
192268
|
+
sourceAgents: ["kyoso_policy"],
|
|
192269
|
+
confidence: "high"
|
|
192270
|
+
},
|
|
192271
|
+
redactionsApplied: 0
|
|
192272
|
+
});
|
|
192273
|
+
} finally {
|
|
192274
|
+
await trace2.finalize();
|
|
192275
|
+
}
|
|
191785
192276
|
}
|
|
191786
192277
|
throw error51;
|
|
191787
192278
|
}
|
|
@@ -191806,12 +192297,13 @@ async function runReview(tool, request, options = {}) {
|
|
|
191806
192297
|
...baseLoaded,
|
|
191807
192298
|
config: applyConfigOverrides(baseLoaded.config, options.configOverrides)
|
|
191808
192299
|
} : baseLoaded;
|
|
191809
|
-
const trace =
|
|
192300
|
+
const trace = traceWriterFactory({
|
|
191810
192301
|
enabled: loaded.config.audit.enabled,
|
|
191811
192302
|
directory: loaded.config.audit.directory,
|
|
191812
192303
|
traceId,
|
|
191813
192304
|
cwd,
|
|
191814
|
-
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput
|
|
192305
|
+
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput,
|
|
192306
|
+
env: auditEnv
|
|
191815
192307
|
});
|
|
191816
192308
|
const warnings = [...loaded.warnings, ...trace.warnings];
|
|
191817
192309
|
try {
|
|
@@ -191893,11 +192385,11 @@ async function runReview(tool, request, options = {}) {
|
|
|
191893
192385
|
manager,
|
|
191894
192386
|
trace
|
|
191895
192387
|
});
|
|
191896
|
-
warnings.push(...agentResults.flatMap((
|
|
192388
|
+
warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
|
|
191897
192389
|
const normalizedAgentResults = agentResults.map(normalizeAgentRunResult);
|
|
191898
|
-
const agentsUsed = normalizedAgentResults.map((
|
|
192390
|
+
const agentsUsed = normalizedAgentResults.map((result) => result.agent);
|
|
191899
192391
|
const reviewMode = agentsUsed.length === 1 ? "single_agent" : "multi_agent";
|
|
191900
|
-
const completed = normalizedAgentResults.filter((
|
|
192392
|
+
const completed = normalizedAgentResults.filter((result) => result.status === "completed");
|
|
191901
192393
|
const degraded = completed.length !== agentResults.length;
|
|
191902
192394
|
let aggregate = aggregateAgentResults(normalizedAgentResults, {
|
|
191903
192395
|
reviewMode
|
|
@@ -191924,7 +192416,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
191924
192416
|
severity: "critical",
|
|
191925
192417
|
category: "other",
|
|
191926
192418
|
title: "All backend agents failed",
|
|
191927
|
-
evidence: normalizedAgentResults.map((
|
|
192419
|
+
evidence: normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
191928
192420
|
recommendation: "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
191929
192421
|
sourceAgents: ["kyoso_policy"],
|
|
191930
192422
|
confidence: "high"
|
|
@@ -191974,7 +192466,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
191974
192466
|
residualRisks: tool === "security_review" && aggregate.residualRisks.length === 0 ? [
|
|
191975
192467
|
"No residual risks were reported by completed agents; verify security assumptions before release."
|
|
191976
192468
|
] : aggregate.residualRisks,
|
|
191977
|
-
agentOpinions: normalizedAgentResults.map((
|
|
192469
|
+
agentOpinions: normalizedAgentResults.map((result) => agentOpinionSummary(result, request.options?.includeAgentRawOutputs === true)),
|
|
191978
192470
|
audit: {
|
|
191979
192471
|
traceId,
|
|
191980
192472
|
startedAt,
|
|
@@ -192006,15 +192498,10 @@ async function runReview(tool, request, options = {}) {
|
|
|
192006
192498
|
judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
|
|
192007
192499
|
}));
|
|
192008
192500
|
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
192009
|
-
const
|
|
192501
|
+
const resultAfterJudge = {
|
|
192010
192502
|
...resultWithoutMarkdown,
|
|
192011
192503
|
disagreements,
|
|
192012
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
192013
|
-
summaryMarkdown: renderMarkdownResult(tool, {
|
|
192014
|
-
...resultWithoutMarkdown,
|
|
192015
|
-
disagreements,
|
|
192016
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
192017
|
-
}, { summaryText: judge.output.summaryText })
|
|
192504
|
+
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
192018
192505
|
};
|
|
192019
192506
|
const judgeEvent = {
|
|
192020
192507
|
type: "judge_completed",
|
|
@@ -192026,7 +192513,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
192026
192513
|
if (judge.error)
|
|
192027
192514
|
judgeEvent.error = judge.error;
|
|
192028
192515
|
await trace.write(judgeEvent);
|
|
192029
|
-
|
|
192516
|
+
resultAfterJudge.audit.completedAt = new Date().toISOString();
|
|
192030
192517
|
await trace.write({
|
|
192031
192518
|
type: "decision_completed",
|
|
192032
192519
|
traceId,
|
|
@@ -192038,8 +192525,14 @@ async function runReview(tool, request, options = {}) {
|
|
|
192038
192525
|
traceId,
|
|
192039
192526
|
timestamp: new Date().toISOString()
|
|
192040
192527
|
});
|
|
192041
|
-
return
|
|
192528
|
+
return await finalizeReviewResult({
|
|
192529
|
+
tool,
|
|
192530
|
+
trace,
|
|
192531
|
+
result: resultAfterJudge,
|
|
192532
|
+
summaryText: judge.output.summaryText
|
|
192533
|
+
});
|
|
192042
192534
|
} finally {
|
|
192535
|
+
await trace.finalize();
|
|
192043
192536
|
if (snapshot)
|
|
192044
192537
|
await cleanupSnapshot(snapshot.root);
|
|
192045
192538
|
}
|
|
@@ -192288,10 +192781,6 @@ async function buildSecretBlockResult(input) {
|
|
|
192288
192781
|
warnings: input.warnings
|
|
192289
192782
|
}
|
|
192290
192783
|
};
|
|
192291
|
-
const result = {
|
|
192292
|
-
...resultWithoutMarkdown,
|
|
192293
|
-
summaryMarkdown: renderMarkdownResult(input.tool, resultWithoutMarkdown)
|
|
192294
|
-
};
|
|
192295
192784
|
await input.trace.write({
|
|
192296
192785
|
type: "decision_completed",
|
|
192297
192786
|
traceId: input.traceId,
|
|
@@ -192303,7 +192792,11 @@ async function buildSecretBlockResult(input) {
|
|
|
192303
192792
|
traceId: input.traceId,
|
|
192304
192793
|
timestamp: new Date().toISOString()
|
|
192305
192794
|
});
|
|
192306
|
-
return
|
|
192795
|
+
return await finalizeReviewResult({
|
|
192796
|
+
tool: input.tool,
|
|
192797
|
+
trace: input.trace,
|
|
192798
|
+
result: resultWithoutMarkdown
|
|
192799
|
+
});
|
|
192307
192800
|
}
|
|
192308
192801
|
function buildSecretFinding(secretScan, options) {
|
|
192309
192802
|
return {
|
|
@@ -192353,10 +192846,6 @@ async function buildPolicyBlockResult(input) {
|
|
|
192353
192846
|
warnings: [input.warning]
|
|
192354
192847
|
}
|
|
192355
192848
|
};
|
|
192356
|
-
const result = {
|
|
192357
|
-
...resultWithoutMarkdown,
|
|
192358
|
-
summaryMarkdown: renderMarkdownResult(input.tool, resultWithoutMarkdown)
|
|
192359
|
-
};
|
|
192360
192849
|
await input.trace.write({
|
|
192361
192850
|
type: "decision_completed",
|
|
192362
192851
|
traceId: input.traceId,
|
|
@@ -192368,7 +192857,30 @@ async function buildPolicyBlockResult(input) {
|
|
|
192368
192857
|
traceId: input.traceId,
|
|
192369
192858
|
timestamp: new Date().toISOString()
|
|
192370
192859
|
});
|
|
192371
|
-
return
|
|
192860
|
+
return await finalizeReviewResult({
|
|
192861
|
+
tool: input.tool,
|
|
192862
|
+
trace: input.trace,
|
|
192863
|
+
result: resultWithoutMarkdown
|
|
192864
|
+
});
|
|
192865
|
+
}
|
|
192866
|
+
async function finalizeReviewResult(input) {
|
|
192867
|
+
await input.trace.finalize();
|
|
192868
|
+
const result = {
|
|
192869
|
+
...input.result,
|
|
192870
|
+
audit: {
|
|
192871
|
+
...input.result.audit,
|
|
192872
|
+
warnings: Array.from(new Set([
|
|
192873
|
+
...input.result.audit.warnings ?? [],
|
|
192874
|
+
...input.trace.warnings
|
|
192875
|
+
]))
|
|
192876
|
+
}
|
|
192877
|
+
};
|
|
192878
|
+
return {
|
|
192879
|
+
...result,
|
|
192880
|
+
summaryMarkdown: renderMarkdownResult(input.tool, result, {
|
|
192881
|
+
summaryText: input.summaryText
|
|
192882
|
+
})
|
|
192883
|
+
};
|
|
192372
192884
|
}
|
|
192373
192885
|
function mergeDenyPatterns(configDeny, requestDeny) {
|
|
192374
192886
|
return Array.from(new Set([...configDeny, ...requestDeny ?? []]));
|
|
@@ -192376,7 +192888,7 @@ function mergeDenyPatterns(configDeny, requestDeny) {
|
|
|
192376
192888
|
function assertTrustedWorkspaceRoot(requestRoot, configRoot, cwd) {
|
|
192377
192889
|
if (!requestRoot)
|
|
192378
192890
|
return;
|
|
192379
|
-
if (
|
|
192891
|
+
if (resolve6(cwd, requestRoot) !== resolve6(cwd, configRoot)) {
|
|
192380
192892
|
throw new KyosoRequestError("workspace.root is not trusted by config", "UNTRUSTED_WORKSPACE_ROOT");
|
|
192381
192893
|
}
|
|
192382
192894
|
}
|