@smartergpt/lexrunner 1.5.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.mcp.md +18 -1
- package/README.md +3 -2
- package/dist/{autopilot-62Y2M2ZO.js → autopilot-3OHEUTHD.js} +1 -1
- package/dist/{chunk-TIMHM7VL.js → chunk-ARDKZW7F.js} +861 -27
- package/dist/cli.cjs +4044 -3259
- package/dist/cli.d.ts +60 -35
- package/dist/cli.js +2313 -2420
- package/dist/sdk/index.d.ts +6 -6
- package/mcp-server.mjs +52 -3
- package/package.json +1 -1
- package/schemas/gate-evidence-manifest.schema.json +86 -0
- package/schemas/gate-execution-receipt.schema.json +21 -1
- package/schemas/plan.schema.json +7 -2
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
canonicalJSONStringify
|
|
14
14
|
} from "./chunk-UHFMFO54.js";
|
|
15
15
|
import {
|
|
16
|
+
AXErrorException,
|
|
17
|
+
configInvalidError,
|
|
18
|
+
planValidationError,
|
|
16
19
|
securitySarifParseError,
|
|
17
20
|
securityScanFailedError
|
|
18
21
|
} from "./chunk-26SCN2LJ.js";
|
|
@@ -243,7 +246,7 @@ function emitDeprecationNotice(oldVar, newVar) {
|
|
|
243
246
|
}
|
|
244
247
|
deprecationNotices.add(key);
|
|
245
248
|
console.warn(
|
|
246
|
-
`\u26A0\uFE0F Environment variable ${oldVar} is deprecated. Use ${newVar} instead. Support for ${oldVar} will be removed in
|
|
249
|
+
`\u26A0\uFE0F Environment variable ${oldVar} is deprecated. Use ${newVar} instead. Support for ${oldVar} will be removed in v3.0.0.`
|
|
247
250
|
);
|
|
248
251
|
}
|
|
249
252
|
|
|
@@ -1869,11 +1872,12 @@ function createGitOperations(workingDir) {
|
|
|
1869
1872
|
|
|
1870
1873
|
// src/gates.ts
|
|
1871
1874
|
import { spawn as spawn2 } from "child_process";
|
|
1875
|
+
import { createHash as createHash2 } from "crypto";
|
|
1872
1876
|
import path9 from "path";
|
|
1873
1877
|
import fs9 from "fs";
|
|
1874
1878
|
|
|
1875
1879
|
// src/security/scanning.ts
|
|
1876
|
-
import { AXErrorException } from "@smartergpt/lex/errors";
|
|
1880
|
+
import { AXErrorException as AXErrorException2 } from "@smartergpt/lex/errors";
|
|
1877
1881
|
var DEFAULT_SECURITY_POLICY = {
|
|
1878
1882
|
blockCritical: true,
|
|
1879
1883
|
blockHigh: true,
|
|
@@ -1904,7 +1908,7 @@ var NpmAuditScanner = class {
|
|
|
1904
1908
|
originalError: error instanceof Error ? error.message : String(error)
|
|
1905
1909
|
}
|
|
1906
1910
|
);
|
|
1907
|
-
throw new
|
|
1911
|
+
throw new AXErrorException2(
|
|
1908
1912
|
axError.code,
|
|
1909
1913
|
axError.message,
|
|
1910
1914
|
axError.nextActions,
|
|
@@ -1970,7 +1974,7 @@ var NpmAuditScanner = class {
|
|
|
1970
1974
|
};
|
|
1971
1975
|
|
|
1972
1976
|
// src/security/sarif.ts
|
|
1973
|
-
import { AXErrorException as
|
|
1977
|
+
import { AXErrorException as AXErrorException3 } from "@smartergpt/lex/errors";
|
|
1974
1978
|
function mapSarifLevel(level, securitySeverity) {
|
|
1975
1979
|
if (securitySeverity) {
|
|
1976
1980
|
const normalized2 = securitySeverity.toLowerCase();
|
|
@@ -1994,7 +1998,7 @@ function parseSarif(sarifContent) {
|
|
|
1994
1998
|
`Invalid SARIF JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
1995
1999
|
{ parseError: error instanceof Error ? error.message : String(error) }
|
|
1996
2000
|
);
|
|
1997
|
-
throw new
|
|
2001
|
+
throw new AXErrorException3(axError.code, axError.message, axError.nextActions, axError.context);
|
|
1998
2002
|
}
|
|
1999
2003
|
if (!sarif.runs || sarif.runs.length === 0) {
|
|
2000
2004
|
return createEmptyResult("sarif");
|
|
@@ -3216,8 +3220,768 @@ import {
|
|
|
3216
3220
|
writeFileSync as writeFileSync5
|
|
3217
3221
|
} from "fs";
|
|
3218
3222
|
import { basename as basename2, delimiter, dirname, extname, isAbsolute as isAbsolute2, join as join9, resolve as resolve5 } from "path";
|
|
3219
|
-
|
|
3223
|
+
import { z as z8 } from "zod";
|
|
3224
|
+
|
|
3225
|
+
// src/schema.ts
|
|
3226
|
+
import { z as z6 } from "zod";
|
|
3227
|
+
|
|
3228
|
+
// src/tiers/schema.ts
|
|
3229
|
+
import { z as z5 } from "zod";
|
|
3230
|
+
var CapabilityTier = z5.enum(["senior", "mid", "junior"]);
|
|
3231
|
+
var TierAssignment = z5.object({
|
|
3232
|
+
/** Suggested tier based on heuristics */
|
|
3233
|
+
suggested: CapabilityTier,
|
|
3234
|
+
/** Actual tier used during execution (may differ due to override) */
|
|
3235
|
+
actual: CapabilityTier.optional(),
|
|
3236
|
+
/** Whether the task was escalated to a higher tier */
|
|
3237
|
+
escalated: z5.boolean().default(false),
|
|
3238
|
+
/** Reason for escalation if applicable */
|
|
3239
|
+
escalationReason: z5.string().optional(),
|
|
3240
|
+
/** Whether actual tier differs from suggested tier */
|
|
3241
|
+
mismatch: z5.boolean().optional()
|
|
3242
|
+
});
|
|
3243
|
+
var TierOverride = z5.object({
|
|
3244
|
+
/** Item name to override */
|
|
3245
|
+
itemName: z5.string(),
|
|
3246
|
+
/** Tier to assign */
|
|
3247
|
+
tier: CapabilityTier
|
|
3248
|
+
});
|
|
3249
|
+
function parseTierOverride(overrideStr) {
|
|
3250
|
+
const parts = overrideStr.split("=");
|
|
3251
|
+
if (parts.length !== 2) {
|
|
3252
|
+
return null;
|
|
3253
|
+
}
|
|
3254
|
+
const [itemName, tierStr] = parts;
|
|
3255
|
+
const tierResult = CapabilityTier.safeParse(tierStr.toLowerCase());
|
|
3256
|
+
if (!tierResult.success || !itemName) {
|
|
3257
|
+
return null;
|
|
3258
|
+
}
|
|
3259
|
+
return {
|
|
3260
|
+
itemName: itemName.trim(),
|
|
3261
|
+
tier: tierResult.data
|
|
3262
|
+
};
|
|
3263
|
+
}
|
|
3264
|
+
function parseTierOverrides(overrides) {
|
|
3265
|
+
const overrideList = typeof overrides === "string" ? overrides.split(",") : overrides;
|
|
3266
|
+
const result = [];
|
|
3267
|
+
for (const override of overrideList) {
|
|
3268
|
+
const parsed = parseTierOverride(override.trim());
|
|
3269
|
+
if (parsed) {
|
|
3270
|
+
result.push(parsed);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
return result;
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
// src/schema.ts
|
|
3277
|
+
var GateStatus = z6.enum(["pass", "fail", "blocked", "skipped", "retrying"]);
|
|
3278
|
+
var NodeStatus = z6.enum(["pass", "fail", "blocked", "skipped", "retrying"]);
|
|
3279
|
+
var GateResult = z6.object({
|
|
3280
|
+
gate: z6.string(),
|
|
3281
|
+
status: GateStatus,
|
|
3282
|
+
exitCode: z6.number().optional(),
|
|
3283
|
+
duration: z6.number().optional(),
|
|
3284
|
+
// milliseconds
|
|
3285
|
+
timeoutMs: z6.number().int().positive().optional(),
|
|
3286
|
+
// effective timeout used for this gate
|
|
3287
|
+
stdout: z6.string().optional(),
|
|
3288
|
+
stderr: z6.string().optional(),
|
|
3289
|
+
failureKind: z6.enum(["nonzero_exit", "spawn_error", "timeout", "evidence_error"]).optional(),
|
|
3290
|
+
timeoutCleanup: z6.object({
|
|
3291
|
+
method: z6.enum(["process-group", "taskkill", "direct-child"]),
|
|
3292
|
+
forceKilled: z6.boolean(),
|
|
3293
|
+
descendantsReaped: z6.boolean()
|
|
3294
|
+
}).optional(),
|
|
3295
|
+
artifacts: z6.array(z6.string()).optional(),
|
|
3296
|
+
attempts: z6.number().default(1),
|
|
3297
|
+
lastAttempt: z6.string().optional()
|
|
3298
|
+
// ISO timestamp
|
|
3299
|
+
});
|
|
3300
|
+
var NodeResult = z6.object({
|
|
3301
|
+
name: z6.string(),
|
|
3302
|
+
status: NodeStatus,
|
|
3303
|
+
gates: z6.array(GateResult).default([]),
|
|
3304
|
+
blockedBy: z6.array(z6.string()).optional(),
|
|
3305
|
+
// names of nodes that blocked this one
|
|
3306
|
+
eligibleForMerge: z6.boolean().default(false)
|
|
3307
|
+
});
|
|
3308
|
+
var SchemaVersion = z6.string().regex(/^1\.\d+\.\d+$/, "Schema version must be 1.x.y format");
|
|
3309
|
+
function validateSchemaVersion(version) {
|
|
3310
|
+
const parsed = SchemaVersion.safeParse(version);
|
|
3311
|
+
if (!parsed.success) {
|
|
3312
|
+
const axError = configInvalidError(
|
|
3313
|
+
`Unsupported schema version: ${version}. This runner only supports schema version 1.x.y`,
|
|
3314
|
+
{ version, expected: "1.x.y" }
|
|
3315
|
+
);
|
|
3316
|
+
throw new AXErrorException(axError.code, axError.message, axError.nextActions, axError.context);
|
|
3317
|
+
}
|
|
3318
|
+
const [major] = version.split(".").map(Number);
|
|
3319
|
+
if (major !== 1) {
|
|
3320
|
+
const axError = configInvalidError(
|
|
3321
|
+
`Incompatible schema major version: ${major}. This runner only supports major version 1.`,
|
|
3322
|
+
{ version, major, expectedMajor: 1 }
|
|
3323
|
+
);
|
|
3324
|
+
throw new AXErrorException(axError.code, axError.message, axError.nextActions, axError.context);
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
var RetryConfig = z6.object({
|
|
3328
|
+
maxAttempts: z6.number().int().min(1).default(1),
|
|
3329
|
+
backoffSeconds: z6.number().min(0).default(0)
|
|
3330
|
+
});
|
|
3331
|
+
var AdminOverride = z6.object({
|
|
3332
|
+
allowedUsers: z6.array(z6.string()).optional(),
|
|
3333
|
+
requireReason: z6.boolean().default(false)
|
|
3334
|
+
});
|
|
3335
|
+
var MergeRule = z6.object({
|
|
3336
|
+
type: z6.enum(["strict-required"]).default("strict-required")
|
|
3337
|
+
// Future: could add "best-effort", "admin-override-allowed", etc.
|
|
3338
|
+
});
|
|
3339
|
+
var PerformanceConfig = z6.object({
|
|
3340
|
+
maxMemoryMB: z6.number().int().min(128).optional(),
|
|
3341
|
+
// Memory limit in MB
|
|
3342
|
+
batchSize: z6.number().int().min(1).default(50),
|
|
3343
|
+
// Batch size for large plans
|
|
3344
|
+
cacheTTLSeconds: z6.number().int().min(0).default(3600),
|
|
3345
|
+
// Cache TTL in seconds
|
|
3346
|
+
enableCaching: z6.boolean().default(true),
|
|
3347
|
+
// Enable operation caching
|
|
3348
|
+
throttleOnMemory: z6.boolean().default(true),
|
|
3349
|
+
// Throttle workers when memory high
|
|
3350
|
+
memoryThresholdPercent: z6.number().min(0).max(100).default(80)
|
|
3351
|
+
// Memory threshold %
|
|
3352
|
+
}).default(() => ({
|
|
3353
|
+
batchSize: 50,
|
|
3354
|
+
cacheTTLSeconds: 3600,
|
|
3355
|
+
enableCaching: true,
|
|
3356
|
+
throttleOnMemory: true,
|
|
3357
|
+
memoryThresholdPercent: 80
|
|
3358
|
+
}));
|
|
3359
|
+
var VulnPolicy = z6.object({
|
|
3360
|
+
blockCritical: z6.boolean().default(true),
|
|
3361
|
+
blockHigh: z6.boolean().default(true),
|
|
3362
|
+
maxMedium: z6.number().int().min(0).default(5),
|
|
3363
|
+
maxLow: z6.number().int().min(0).default(10)
|
|
3364
|
+
}).default(() => ({
|
|
3365
|
+
blockCritical: true,
|
|
3366
|
+
blockHigh: true,
|
|
3367
|
+
maxMedium: 5,
|
|
3368
|
+
maxLow: 10
|
|
3369
|
+
}));
|
|
3370
|
+
var Policy = z6.object({
|
|
3371
|
+
requiredGates: z6.array(z6.string()).default([]),
|
|
3372
|
+
optionalGates: z6.array(z6.string()).default([]),
|
|
3373
|
+
maxWorkers: z6.number().int().min(1).default(1),
|
|
3374
|
+
retries: z6.record(z6.string(), RetryConfig).default(() => ({})),
|
|
3375
|
+
overrides: z6.object({
|
|
3376
|
+
adminGreen: AdminOverride.optional()
|
|
3377
|
+
}).default(() => ({})),
|
|
3378
|
+
blockOn: z6.array(z6.string()).default([]),
|
|
3379
|
+
mergeRule: MergeRule.default({ type: "strict-required" }),
|
|
3380
|
+
performance: PerformanceConfig.optional(),
|
|
3381
|
+
// Performance tuning options
|
|
3382
|
+
security: VulnPolicy.optional()
|
|
3383
|
+
// Security/vulnerability thresholds for vuln gate
|
|
3384
|
+
});
|
|
3385
|
+
var ContainerMount = z6.object({
|
|
3386
|
+
source: z6.string(),
|
|
3387
|
+
target: z6.string(),
|
|
3388
|
+
type: z6.enum(["bind", "volume"]).default("bind")
|
|
3389
|
+
});
|
|
3390
|
+
var ContainerSpec = z6.object({
|
|
3391
|
+
image: z6.string(),
|
|
3392
|
+
entrypoint: z6.array(z6.string()).optional(),
|
|
3393
|
+
mounts: z6.array(ContainerMount).optional()
|
|
3394
|
+
});
|
|
3395
|
+
var Gate = z6.object({
|
|
3396
|
+
name: z6.string(),
|
|
3397
|
+
run: z6.string(),
|
|
3398
|
+
cwd: z6.string().optional(),
|
|
3399
|
+
env: z6.record(z6.string(), z6.string()).default(() => ({})),
|
|
3400
|
+
// Runtime configuration
|
|
3401
|
+
runtime: z6.enum(["local", "container", "ci-service"]).default("local"),
|
|
3402
|
+
// Container spec (only used when runtime is "container")
|
|
3403
|
+
container: ContainerSpec.optional(),
|
|
3404
|
+
// Expected artifact paths (for output collection)
|
|
3405
|
+
artifacts: z6.array(z6.string()).default([]),
|
|
3406
|
+
// Optional input data for gates that require structured inputs (validated against gate-specific schemas)
|
|
3407
|
+
input: z6.record(z6.string(), z6.unknown()).optional(),
|
|
3408
|
+
// Exact per-gate timeout. Overrides the operation default after hostility adjustment.
|
|
3409
|
+
timeoutMs: z6.number().int().positive().max(24 * 60 * 60 * 1e3).optional()
|
|
3410
|
+
}).strict();
|
|
3411
|
+
var PlanItem = z6.object({
|
|
3412
|
+
name: z6.string(),
|
|
3413
|
+
deps: z6.string().array().default([]),
|
|
3414
|
+
// Dependency references by item name
|
|
3415
|
+
gates: z6.array(Gate).default([]),
|
|
3416
|
+
// Tier routing for governance (optional - added during plan generation or execution)
|
|
3417
|
+
tier: TierAssignment.optional()
|
|
3418
|
+
}).strict();
|
|
3419
|
+
var Plan = z6.object({
|
|
3420
|
+
schemaVersion: SchemaVersion,
|
|
3421
|
+
target: z6.string().default("main"),
|
|
3422
|
+
policy: Policy.optional(),
|
|
3423
|
+
items: z6.array(PlanItem).default([])
|
|
3424
|
+
}).strict().superRefine((plan, context) => {
|
|
3425
|
+
const itemNames = /* @__PURE__ */ new Set();
|
|
3426
|
+
let duplicateItemNameFound = false;
|
|
3427
|
+
const duplicateGateNameItemIndexes = [];
|
|
3428
|
+
for (const [itemIndex, item] of plan.items.entries()) {
|
|
3429
|
+
if (itemNames.has(item.name)) {
|
|
3430
|
+
duplicateItemNameFound = true;
|
|
3431
|
+
}
|
|
3432
|
+
itemNames.add(item.name);
|
|
3433
|
+
const gateNames = /* @__PURE__ */ new Set();
|
|
3434
|
+
let duplicateGateNameFound = false;
|
|
3435
|
+
for (const gate of item.gates) {
|
|
3436
|
+
if (gateNames.has(gate.name)) {
|
|
3437
|
+
duplicateGateNameFound = true;
|
|
3438
|
+
}
|
|
3439
|
+
gateNames.add(gate.name);
|
|
3440
|
+
}
|
|
3441
|
+
if (duplicateGateNameFound) duplicateGateNameItemIndexes.push(itemIndex);
|
|
3442
|
+
}
|
|
3443
|
+
if (duplicateItemNameFound) {
|
|
3444
|
+
context.addIssue({
|
|
3445
|
+
code: "custom",
|
|
3446
|
+
path: ["items"],
|
|
3447
|
+
message: "Plan item names must be unique",
|
|
3448
|
+
params: { validationCode: "DUPLICATE_NAMES" }
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
for (const itemIndex of duplicateGateNameItemIndexes) {
|
|
3452
|
+
context.addIssue({
|
|
3453
|
+
code: "custom",
|
|
3454
|
+
path: ["items", itemIndex, "gates"],
|
|
3455
|
+
message: "Gate names must be unique within an item",
|
|
3456
|
+
params: { validationCode: "DUPLICATE_GATE_NAMES" }
|
|
3457
|
+
});
|
|
3458
|
+
}
|
|
3459
|
+
});
|
|
3460
|
+
var MAX_SCHEMA_VALIDATION_ERRORS = 50;
|
|
3461
|
+
var MAX_SCHEMA_VALIDATION_PATH_BYTES = 256;
|
|
3462
|
+
var MAX_SCHEMA_VALIDATION_MESSAGE_BYTES = 512;
|
|
3463
|
+
var MAX_SCHEMA_VALIDATION_ACTION_BYTES = 512;
|
|
3464
|
+
var SchemaValidationError = class extends AXErrorException {
|
|
3465
|
+
constructor(issues) {
|
|
3466
|
+
const errorCount = issues.length;
|
|
3467
|
+
const errors = issues.slice(0, MAX_SCHEMA_VALIDATION_ERRORS).map(normalizeValidationIssue);
|
|
3468
|
+
const errorsTruncated = errorCount > errors.length;
|
|
3469
|
+
const errorStrings = errors.map((e) => `${e.path}: ${e.message}`);
|
|
3470
|
+
const axError = planValidationError({
|
|
3471
|
+
errors: errorStrings,
|
|
3472
|
+
errorCount,
|
|
3473
|
+
errorsTruncated
|
|
3474
|
+
// planPath is omitted as it's not available in this context
|
|
3475
|
+
});
|
|
3476
|
+
super(axError.code, axError.message, axError.nextActions, axError.context);
|
|
3477
|
+
this.name = "SchemaValidationError";
|
|
3478
|
+
this.issues = issues;
|
|
3479
|
+
this.errors = errors;
|
|
3480
|
+
this.errorCount = errorCount;
|
|
3481
|
+
this.errorsTruncated = errorsTruncated;
|
|
3482
|
+
}
|
|
3483
|
+
/**
|
|
3484
|
+
* Get legacy machine-readable error format for backward compatibility
|
|
3485
|
+
*/
|
|
3486
|
+
toLegacyJSON() {
|
|
3487
|
+
return {
|
|
3488
|
+
valid: false,
|
|
3489
|
+
errors: this.errors
|
|
3490
|
+
};
|
|
3491
|
+
}
|
|
3492
|
+
};
|
|
3493
|
+
function formatPlanValidationFailure(error) {
|
|
3494
|
+
const nextActions = error.axError.nextActions.filter((action) => !action.startsWith("Errors:")).slice(0, 10).map((action) => boundDiagnostic(action, MAX_SCHEMA_VALIDATION_ACTION_BYTES));
|
|
3495
|
+
return {
|
|
3496
|
+
contract: "bounded-ax-v1",
|
|
3497
|
+
valid: false,
|
|
3498
|
+
code: error.axError.code,
|
|
3499
|
+
message: boundDiagnostic(error.message, MAX_SCHEMA_VALIDATION_MESSAGE_BYTES),
|
|
3500
|
+
errorCount: error.errorCount,
|
|
3501
|
+
errors: error.errors,
|
|
3502
|
+
errorsTruncated: error.errorsTruncated,
|
|
3503
|
+
nextActions: nextActions.length > 0 ? nextActions : ["Review each validation path and update the plan"],
|
|
3504
|
+
context: {
|
|
3505
|
+
errorCount: error.errorCount,
|
|
3506
|
+
errorsTruncated: error.errorsTruncated
|
|
3507
|
+
}
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3510
|
+
function asPlanValidationFailure(error) {
|
|
3511
|
+
if (error instanceof SchemaValidationError) return formatPlanValidationFailure(error);
|
|
3512
|
+
if (!(error instanceof AXErrorException) || error.axError.code !== "CONFIG_INVALID") {
|
|
3513
|
+
return void 0;
|
|
3514
|
+
}
|
|
3515
|
+
const message = boundDiagnostic(error.message, MAX_SCHEMA_VALIDATION_MESSAGE_BYTES);
|
|
3516
|
+
return {
|
|
3517
|
+
contract: "bounded-ax-v1",
|
|
3518
|
+
valid: false,
|
|
3519
|
+
code: error.axError.code,
|
|
3520
|
+
message,
|
|
3521
|
+
errorCount: 1,
|
|
3522
|
+
errors: [{ path: "root", message, code: error.axError.code }],
|
|
3523
|
+
errorsTruncated: false,
|
|
3524
|
+
nextActions: error.axError.nextActions.slice(0, 10).map((action) => boundDiagnostic(action, MAX_SCHEMA_VALIDATION_ACTION_BYTES)),
|
|
3525
|
+
context: {
|
|
3526
|
+
errorCount: 1,
|
|
3527
|
+
errorsTruncated: false
|
|
3528
|
+
}
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
function formatPlanValidationFailureText(failure) {
|
|
3532
|
+
const lines = [failure.message];
|
|
3533
|
+
for (const error of failure.errors) {
|
|
3534
|
+
lines.push(` - ${error.path} [${error.code}]: ${error.message}`);
|
|
3535
|
+
}
|
|
3536
|
+
if (failure.errorsTruncated) {
|
|
3537
|
+
lines.push(
|
|
3538
|
+
` - \u2026 ${failure.errorCount - failure.errors.length} additional validation error(s) omitted`
|
|
3539
|
+
);
|
|
3540
|
+
}
|
|
3541
|
+
return lines.join("\n");
|
|
3542
|
+
}
|
|
3543
|
+
function normalizeValidationIssue(issue) {
|
|
3544
|
+
const path11 = normalizeValidationPath(issue.path);
|
|
3545
|
+
const stableCustomIssue = safeCustomValidationIssue(issue);
|
|
3546
|
+
return {
|
|
3547
|
+
path: boundDiagnostic(path11, MAX_SCHEMA_VALIDATION_PATH_BYTES),
|
|
3548
|
+
message: boundDiagnostic(
|
|
3549
|
+
stableCustomIssue?.message ?? safeValidationMessage(issue.code),
|
|
3550
|
+
MAX_SCHEMA_VALIDATION_MESSAGE_BYTES
|
|
3551
|
+
),
|
|
3552
|
+
code: stableCustomIssue?.code ?? issue.code
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
function safeCustomValidationIssue(issue) {
|
|
3556
|
+
if (issue.code !== "custom") return void 0;
|
|
3557
|
+
switch (issue.params?.validationCode) {
|
|
3558
|
+
case "DUPLICATE_NAMES":
|
|
3559
|
+
return { code: "DUPLICATE_NAMES", message: "Plan item names must be unique" };
|
|
3560
|
+
case "DUPLICATE_GATE_NAMES":
|
|
3561
|
+
return {
|
|
3562
|
+
code: "DUPLICATE_GATE_NAMES",
|
|
3563
|
+
message: "Gate names must be unique within an item"
|
|
3564
|
+
};
|
|
3565
|
+
default:
|
|
3566
|
+
return void 0;
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
function normalizeValidationPath(segments) {
|
|
3570
|
+
if (segments.length === 0) return "root";
|
|
3571
|
+
return segments.map((segment, index) => {
|
|
3572
|
+
const previous = segments[index - 1];
|
|
3573
|
+
if (previous === "retries" || previous === "env" || previous === "input") {
|
|
3574
|
+
return "<key>";
|
|
3575
|
+
}
|
|
3576
|
+
return String(segment);
|
|
3577
|
+
}).join(".");
|
|
3578
|
+
}
|
|
3579
|
+
function safeValidationMessage(code) {
|
|
3580
|
+
switch (code) {
|
|
3581
|
+
case "invalid_type":
|
|
3582
|
+
return "Value has an invalid type";
|
|
3583
|
+
case "invalid_value":
|
|
3584
|
+
return "Value is not an allowed option";
|
|
3585
|
+
case "too_big":
|
|
3586
|
+
return "Value exceeds the allowed maximum";
|
|
3587
|
+
case "too_small":
|
|
3588
|
+
return "Value is below the allowed minimum";
|
|
3589
|
+
case "invalid_format":
|
|
3590
|
+
return "Value does not match the required format";
|
|
3591
|
+
case "not_multiple_of":
|
|
3592
|
+
return "Value is not an allowed multiple";
|
|
3593
|
+
case "unrecognized_keys":
|
|
3594
|
+
return "Object contains one or more unrecognized keys";
|
|
3595
|
+
case "invalid_union":
|
|
3596
|
+
return "Value does not match any allowed shape";
|
|
3597
|
+
case "invalid_key":
|
|
3598
|
+
return "Object contains an invalid key";
|
|
3599
|
+
case "invalid_element":
|
|
3600
|
+
return "Collection contains an invalid element";
|
|
3601
|
+
case "custom":
|
|
3602
|
+
return "Value failed schema validation";
|
|
3603
|
+
default:
|
|
3604
|
+
return "Value does not satisfy schema requirements";
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
function boundDiagnostic(value, maxBytes) {
|
|
3608
|
+
const singleLine = value.replace(
|
|
3609
|
+
/[\u0000-\u001f\u007f]/g,
|
|
3610
|
+
(character) => JSON.stringify(character).slice(1, -1)
|
|
3611
|
+
);
|
|
3612
|
+
const encoded = Buffer.from(singleLine, "utf8");
|
|
3613
|
+
if (encoded.length <= maxBytes) return singleLine;
|
|
3614
|
+
const suffix = "\u2026 [truncated]";
|
|
3615
|
+
const suffixBytes = Buffer.byteLength(suffix, "utf8");
|
|
3616
|
+
let end = Math.max(0, maxBytes - suffixBytes);
|
|
3617
|
+
while (end > 0 && (encoded[end] & 192) === 128) end -= 1;
|
|
3618
|
+
return `${encoded.subarray(0, end).toString("utf8")}${suffix}`;
|
|
3619
|
+
}
|
|
3620
|
+
function validatePlan(planData) {
|
|
3621
|
+
const result = Plan.safeParse(planData);
|
|
3622
|
+
if (!result.success) {
|
|
3623
|
+
throw new SchemaValidationError(result.error.issues);
|
|
3624
|
+
}
|
|
3625
|
+
validateSchemaVersion(result.data.schemaVersion);
|
|
3626
|
+
return result.data;
|
|
3627
|
+
}
|
|
3628
|
+
function loadPlan(planContent) {
|
|
3629
|
+
try {
|
|
3630
|
+
const planData = JSON.parse(planContent);
|
|
3631
|
+
return validatePlan(planData);
|
|
3632
|
+
} catch (error) {
|
|
3633
|
+
if (error instanceof SyntaxError) {
|
|
3634
|
+
const axError = configInvalidError("Invalid JSON: plan content could not be parsed", {
|
|
3635
|
+
parseError: "MALFORMED_JSON"
|
|
3636
|
+
});
|
|
3637
|
+
throw new AXErrorException(
|
|
3638
|
+
axError.code,
|
|
3639
|
+
axError.message,
|
|
3640
|
+
axError.nextActions,
|
|
3641
|
+
axError.context
|
|
3642
|
+
);
|
|
3643
|
+
}
|
|
3644
|
+
throw error;
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
// src/schemas/task-contract.ts
|
|
3649
|
+
import { createHash } from "crypto";
|
|
3650
|
+
import { z as z7 } from "zod";
|
|
3651
|
+
var TASK_CONTRACT_VERSION = "1.0.0";
|
|
3652
|
+
function computeCanonicalHash(obj) {
|
|
3653
|
+
const canonical = JSON.stringify(obj, sortedReplacer);
|
|
3654
|
+
return computeCanonicalHashFromCompactJSON(canonical);
|
|
3655
|
+
}
|
|
3656
|
+
function computeCanonicalHashFromCompactJSON(canonical) {
|
|
3657
|
+
const hash = createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
3658
|
+
return `sha256:${hash}`;
|
|
3659
|
+
}
|
|
3660
|
+
function sortedReplacer(_key, value) {
|
|
3661
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
3662
|
+
return Object.keys(value).sort().reduce(
|
|
3663
|
+
(sorted, key) => {
|
|
3664
|
+
sorted[key] = value[key];
|
|
3665
|
+
return sorted;
|
|
3666
|
+
},
|
|
3667
|
+
{}
|
|
3668
|
+
);
|
|
3669
|
+
}
|
|
3670
|
+
return value;
|
|
3671
|
+
}
|
|
3672
|
+
var DeterminismLevel = z7.enum(["D1", "D2", "D3"]);
|
|
3673
|
+
var ConfidenceLevel2 = z7.enum(["high", "medium", "low"]);
|
|
3674
|
+
var SourceOfTruthKind = z7.enum(["symbol", "file", "registry"]);
|
|
3675
|
+
var AssumptionType = z7.enum(["scope", "codebase", "env", "intent", "dependency", "test"]);
|
|
3676
|
+
var RepoRelativePath = z7.string().refine((p) => !p.startsWith("/"), {
|
|
3677
|
+
message: "Path must be repo-relative (no leading slash)"
|
|
3678
|
+
}).refine((p) => !p.includes(":\\") && !p.includes(":/"), {
|
|
3679
|
+
message: "Path must not be absolute (no drive letters)"
|
|
3680
|
+
}).refine((p) => !p.startsWith(".."), {
|
|
3681
|
+
message: "Path must not escape repo root"
|
|
3682
|
+
});
|
|
3683
|
+
var GlobPattern = z7.string().min(1);
|
|
3684
|
+
var SHA256Hash = z7.string().regex(/^sha256:[a-f0-9]{64}$/, "Must be sha256:<64-hex-chars>");
|
|
3685
|
+
var SHA256HashOrPlaceholder = z7.string().refine((h) => h.startsWith("sha256:"), {
|
|
3686
|
+
message: "Must start with sha256:"
|
|
3687
|
+
});
|
|
3688
|
+
var RepoProvenance = z7.object({
|
|
3689
|
+
/** Repository identifier (owner/name) */
|
|
3690
|
+
id: z7.string().regex(/^[^/]+\/[^/]+$/, "Must be owner/repo format"),
|
|
3691
|
+
/** Absolute path to repo root (engine-local, for resolution) */
|
|
3692
|
+
root: z7.string(),
|
|
3693
|
+
/** Pinned commit SHA */
|
|
3694
|
+
commit_sha: z7.string().min(7)
|
|
3695
|
+
});
|
|
3696
|
+
var ScopeBoundary = z7.object({
|
|
3697
|
+
/** Globs for files agent can read */
|
|
3698
|
+
read_globs: z7.array(GlobPattern),
|
|
3699
|
+
/** Globs for files agent can modify */
|
|
3700
|
+
write_globs: z7.array(GlobPattern),
|
|
3701
|
+
/** Globs for files explicitly denied (always applied) */
|
|
3702
|
+
deny_globs: z7.array(GlobPattern),
|
|
3703
|
+
/** Whether cross-repo operations are permitted */
|
|
3704
|
+
cross_repo_allowed: z7.boolean()
|
|
3705
|
+
});
|
|
3706
|
+
var FailureEvidence = z7.object({
|
|
3707
|
+
/** Short error description */
|
|
3708
|
+
message: z7.string(),
|
|
3709
|
+
/** Repo-relative path to failed file */
|
|
3710
|
+
file_rel: RepoRelativePath,
|
|
3711
|
+
/** Line number if available */
|
|
3712
|
+
line: z7.number().int().positive().optional(),
|
|
3713
|
+
/** Actual test runner output snippet */
|
|
3714
|
+
runner_output_snip: z7.string(),
|
|
3715
|
+
/** Code context around failure */
|
|
3716
|
+
excerpt: z7.string().optional()
|
|
3717
|
+
});
|
|
3718
|
+
var HintEdit = z7.object({
|
|
3719
|
+
find: z7.string(),
|
|
3720
|
+
replace: z7.string()
|
|
3721
|
+
});
|
|
3722
|
+
var Target = z7.object({
|
|
3723
|
+
/** Repo-relative file path */
|
|
3724
|
+
path_rel: RepoRelativePath,
|
|
3725
|
+
/** Anchored code context (±N lines) */
|
|
3726
|
+
hunk: z7.string(),
|
|
3727
|
+
/** SHA256 of hunk for drift detection */
|
|
3728
|
+
hunk_sha256: SHA256HashOrPlaceholder,
|
|
3729
|
+
/** Optional find/replace suggestion */
|
|
3730
|
+
hint_edit: HintEdit.optional()
|
|
3731
|
+
});
|
|
3732
|
+
var IntroducingChange = z7.object({
|
|
3733
|
+
commit_sha: z7.string().min(7),
|
|
3734
|
+
diff_hunk: z7.string()
|
|
3735
|
+
});
|
|
3736
|
+
var SourceOfTruth = z7.object({
|
|
3737
|
+
/** Kind of source */
|
|
3738
|
+
kind: SourceOfTruthKind,
|
|
3739
|
+
/** Repo-relative path */
|
|
3740
|
+
path_rel: RepoRelativePath,
|
|
3741
|
+
/** Repository ID (defaults to snapshot repo if omitted) */
|
|
3742
|
+
repo_id: z7.string().optional(),
|
|
3743
|
+
/** Commit SHA (defaults to snapshot commit if omitted) */
|
|
3744
|
+
commit_sha: z7.string().optional(),
|
|
3745
|
+
/** Canonical excerpt */
|
|
3746
|
+
excerpt: z7.string(),
|
|
3747
|
+
/** Lexmap module ID if applicable */
|
|
3748
|
+
lexmap_module_id: z7.string().optional(),
|
|
3749
|
+
/** Change that introduced this */
|
|
3750
|
+
introducing_change: IntroducingChange.optional()
|
|
3751
|
+
});
|
|
3752
|
+
var VerificationExpectations = z7.object({
|
|
3753
|
+
/** Command to run */
|
|
3754
|
+
cmd: z7.string(),
|
|
3755
|
+
/** Expected results */
|
|
3756
|
+
expect: z7.object({
|
|
3757
|
+
/** Expected exit code (usually 0) */
|
|
3758
|
+
exit_code: z7.number().int(),
|
|
3759
|
+
/** Strings that must appear in output */
|
|
3760
|
+
must_include: z7.array(z7.string()).optional(),
|
|
3761
|
+
/** Strings that must not appear */
|
|
3762
|
+
must_not_include: z7.array(z7.string()).optional()
|
|
3763
|
+
})
|
|
3764
|
+
});
|
|
3765
|
+
var Budget = z7.object({
|
|
3766
|
+
/** Token budget hint in bytes */
|
|
3767
|
+
max_bytes: z7.number().int().positive().optional(),
|
|
3768
|
+
/** Fields that were truncated (empty if none) */
|
|
3769
|
+
truncated_fields: z7.array(z7.string())
|
|
3770
|
+
});
|
|
3771
|
+
var TaskSnapshot_v1 = z7.object({
|
|
3772
|
+
// Schema version
|
|
3773
|
+
schema_version: z7.literal(TASK_CONTRACT_VERSION),
|
|
3774
|
+
// Identity
|
|
3775
|
+
task_id: z7.string().min(1),
|
|
3776
|
+
procedure: z7.string().min(1),
|
|
3777
|
+
determinism: DeterminismLevel,
|
|
3778
|
+
snapshot_hash: SHA256HashOrPlaceholder,
|
|
3779
|
+
// Provenance (MUST)
|
|
3780
|
+
repo: RepoProvenance,
|
|
3781
|
+
// Scope boundary (MUST)
|
|
3782
|
+
scope: ScopeBoundary,
|
|
3783
|
+
// Failure evidence (MUST)
|
|
3784
|
+
failure: FailureEvidence,
|
|
3785
|
+
// Invariants (SHOULD) - anti-brittleness constraints
|
|
3786
|
+
invariants: z7.array(z7.string()).optional(),
|
|
3787
|
+
// Targets (MUST, array)
|
|
3788
|
+
targets: z7.array(Target).min(1),
|
|
3789
|
+
// Source of truth (SHOULD)
|
|
3790
|
+
source_of_truth: SourceOfTruth.optional(),
|
|
3791
|
+
// Verification expectations (MUST)
|
|
3792
|
+
verification: VerificationExpectations,
|
|
3793
|
+
// Budget/truncation (MUST)
|
|
3794
|
+
budget: Budget,
|
|
3795
|
+
// Output contract
|
|
3796
|
+
receipt_schema_id: z7.string()
|
|
3797
|
+
});
|
|
3798
|
+
var StructuredAssumption = z7.object({
|
|
3799
|
+
/** Type of assumption */
|
|
3800
|
+
type: AssumptionType,
|
|
3801
|
+
/** Assumption text */
|
|
3802
|
+
text: z7.string(),
|
|
3803
|
+
/** Whether it was validated */
|
|
3804
|
+
validated: z7.boolean().optional(),
|
|
3805
|
+
/** Evidence supporting validation */
|
|
3806
|
+
evidence: z7.string().optional()
|
|
3807
|
+
});
|
|
3808
|
+
var AgentClaims = z7.object({
|
|
3809
|
+
/** Whether the task succeeded */
|
|
3810
|
+
success: z7.boolean(),
|
|
3811
|
+
/** Unified diff patch */
|
|
3812
|
+
patch: z7.string().optional(),
|
|
3813
|
+
/** Files touched (repo-relative) */
|
|
3814
|
+
files_touched: z7.array(RepoRelativePath),
|
|
3815
|
+
/** Rationale for the fix */
|
|
3816
|
+
rationale: z7.string(),
|
|
3817
|
+
/** Confidence level */
|
|
3818
|
+
confidence: ConfidenceLevel2,
|
|
3819
|
+
/** Which invariants were respected */
|
|
3820
|
+
invariants_respected: z7.array(z7.string()).optional(),
|
|
3821
|
+
/** Structured assumptions made */
|
|
3822
|
+
assumptions_made: z7.array(StructuredAssumption)
|
|
3823
|
+
});
|
|
3824
|
+
var SearchActivity = z7.object({
|
|
3825
|
+
/** Search query */
|
|
3826
|
+
query: z7.string(),
|
|
3827
|
+
/** Search method used */
|
|
3828
|
+
method: z7.string().optional(),
|
|
3829
|
+
/** Roots searched (repo-relative) */
|
|
3830
|
+
roots: z7.array(z7.string()),
|
|
3831
|
+
/** Number of results found */
|
|
3832
|
+
results_count: z7.number().int().nonnegative().optional(),
|
|
3833
|
+
/** Time taken in milliseconds */
|
|
3834
|
+
time_ms: z7.number().nonnegative().optional()
|
|
3835
|
+
});
|
|
3836
|
+
var TokenUsage = z7.object({
|
|
3837
|
+
input: z7.number().int().nonnegative(),
|
|
3838
|
+
output: z7.number().int().nonnegative(),
|
|
3839
|
+
total: z7.number().int().nonnegative()
|
|
3840
|
+
});
|
|
3841
|
+
var CostTracking = z7.object({
|
|
3842
|
+
/** Token usage breakdown */
|
|
3843
|
+
token_usage: TokenUsage.optional(),
|
|
3844
|
+
/** Number of tool calls made */
|
|
3845
|
+
tool_calls_count: z7.number().int().nonnegative().optional(),
|
|
3846
|
+
/** Elapsed time in milliseconds */
|
|
3847
|
+
elapsed_ms: z7.number().nonnegative().optional()
|
|
3848
|
+
});
|
|
3849
|
+
var AgentVerification = z7.object({
|
|
3850
|
+
/** Whether verification command was run */
|
|
3851
|
+
cmd_ran: z7.boolean(),
|
|
3852
|
+
/** Exit code if run */
|
|
3853
|
+
exit_code: z7.number().int().optional(),
|
|
3854
|
+
/** Output snippet */
|
|
3855
|
+
output_snip: z7.string().optional()
|
|
3856
|
+
});
|
|
3857
|
+
var TaskReceipt_v1 = z7.object({
|
|
3858
|
+
// Schema version
|
|
3859
|
+
schema_version: z7.literal(TASK_CONTRACT_VERSION),
|
|
3860
|
+
// Identity (echo from snapshot)
|
|
3861
|
+
task_id: z7.string().min(1),
|
|
3862
|
+
snapshot_hash: SHA256HashOrPlaceholder,
|
|
3863
|
+
// Claims (what agent says it did)
|
|
3864
|
+
claims: AgentClaims,
|
|
3865
|
+
// Search activity (if agent searched)
|
|
3866
|
+
search_activity: z7.array(SearchActivity),
|
|
3867
|
+
// Cost tracking
|
|
3868
|
+
cost: CostTracking,
|
|
3869
|
+
// Agent's verification attempt (still a claim)
|
|
3870
|
+
agent_verification: AgentVerification.optional(),
|
|
3871
|
+
// Blockers (if not successful)
|
|
3872
|
+
blockers: z7.array(z7.string())
|
|
3873
|
+
});
|
|
3874
|
+
var DetectedFailure = z7.object({
|
|
3875
|
+
type: z7.string(),
|
|
3876
|
+
message: z7.string(),
|
|
3877
|
+
file: z7.string().optional(),
|
|
3878
|
+
line: z7.number().int().positive().optional()
|
|
3879
|
+
});
|
|
3880
|
+
var EngineVerification_v1 = z7.object({
|
|
3881
|
+
// Identity
|
|
3882
|
+
task_id: z7.string().min(1),
|
|
3883
|
+
timestamp: z7.string().datetime(),
|
|
3884
|
+
// Hash binding (audit trail)
|
|
3885
|
+
snapshot_hash: SHA256HashOrPlaceholder,
|
|
3886
|
+
receipt_hash: SHA256HashOrPlaceholder,
|
|
3887
|
+
// Verification result
|
|
3888
|
+
verified: z7.boolean(),
|
|
3889
|
+
// What engine ran
|
|
3890
|
+
cmd_ran: z7.string(),
|
|
3891
|
+
exit_code: z7.number().int(),
|
|
3892
|
+
stdout_snip: z7.string(),
|
|
3893
|
+
stderr_snip: z7.string(),
|
|
3894
|
+
// Patch verification
|
|
3895
|
+
patch_hash: SHA256HashOrPlaceholder.optional(),
|
|
3896
|
+
patch_applied: z7.boolean(),
|
|
3897
|
+
// Comparison with agent claim
|
|
3898
|
+
agent_claimed: z7.boolean(),
|
|
3899
|
+
trust_gap: z7.boolean(),
|
|
3900
|
+
// Failures detected
|
|
3901
|
+
failures: z7.array(DetectedFailure)
|
|
3902
|
+
});
|
|
3903
|
+
|
|
3904
|
+
// src/gates/execution-receipt.ts
|
|
3905
|
+
var GATE_EXECUTION_RECEIPT_SCHEMA_VERSION = "lexrunner-gate-execution-receipt/v2";
|
|
3220
3906
|
var MAX_GATE_RECEIPT_OUTPUT_BYTES = 64 * 1024;
|
|
3907
|
+
var FileIdentitySchema = z8.object({
|
|
3908
|
+
path: z8.string(),
|
|
3909
|
+
realPath: z8.string(),
|
|
3910
|
+
bytes: z8.number().int().nonnegative(),
|
|
3911
|
+
mtimeMs: z8.number().nonnegative(),
|
|
3912
|
+
sha256: z8.string().regex(/^sha256:[a-f0-9]{64}$/u)
|
|
3913
|
+
}).strict();
|
|
3914
|
+
var OutputEvidenceSchema = z8.object({
|
|
3915
|
+
bytes: z8.number().int().nonnegative(),
|
|
3916
|
+
sha256: z8.string().regex(/^sha256:[a-f0-9]{64}$/u),
|
|
3917
|
+
truncated: z8.boolean(),
|
|
3918
|
+
content: z8.string().max(MAX_GATE_RECEIPT_OUTPUT_BYTES)
|
|
3919
|
+
}).strict();
|
|
3920
|
+
var ArtifactIdentitySchema = z8.object({
|
|
3921
|
+
declaredPath: z8.string(),
|
|
3922
|
+
resolvedPath: z8.string(),
|
|
3923
|
+
status: z8.enum(["collected", "missing", "stale", "unsupported", "collection_error"]),
|
|
3924
|
+
before: FileIdentitySchema.nullable(),
|
|
3925
|
+
source: FileIdentitySchema.nullable(),
|
|
3926
|
+
retainedPath: z8.string().optional(),
|
|
3927
|
+
retained: FileIdentitySchema.optional(),
|
|
3928
|
+
error: z8.string().optional()
|
|
3929
|
+
}).strict();
|
|
3930
|
+
var LocalGateExecutionReceiptSchema = z8.object({
|
|
3931
|
+
schemaVersion: z8.literal(GATE_EXECUTION_RECEIPT_SCHEMA_VERSION),
|
|
3932
|
+
attempt: z8.number().int().positive(),
|
|
3933
|
+
binding: z8.object({
|
|
3934
|
+
item: z8.string().min(1).nullable(),
|
|
3935
|
+
declaredGateDigest: z8.string().regex(/^sha256:[a-f0-9]{64}$/u),
|
|
3936
|
+
candidateDigest: z8.string().regex(/^sha256:[a-f0-9]{64}$/u).nullable(),
|
|
3937
|
+
timeoutMs: z8.number().int().positive()
|
|
3938
|
+
}).strict(),
|
|
3939
|
+
declaredGate: z8.object({
|
|
3940
|
+
name: z8.string(),
|
|
3941
|
+
run: z8.string(),
|
|
3942
|
+
cwd: z8.string().nullable(),
|
|
3943
|
+
runtime: z8.enum(["local", "container", "ci-service"]),
|
|
3944
|
+
artifacts: z8.array(z8.string())
|
|
3945
|
+
}).strict(),
|
|
3946
|
+
execution: z8.object({
|
|
3947
|
+
cwd: z8.string(),
|
|
3948
|
+
startedAt: z8.string(),
|
|
3949
|
+
finishedAt: z8.string(),
|
|
3950
|
+
durationMs: z8.number().nonnegative(),
|
|
3951
|
+
shell: z8.object({
|
|
3952
|
+
command: z8.enum(["bash", "pwsh"]),
|
|
3953
|
+
executable: FileIdentitySchema.nullable(),
|
|
3954
|
+
argv: z8.array(z8.string()),
|
|
3955
|
+
identityAfter: FileIdentitySchema.nullable(),
|
|
3956
|
+
unchanged: z8.boolean(),
|
|
3957
|
+
spawned: z8.boolean()
|
|
3958
|
+
}).strict()
|
|
3959
|
+
}).strict(),
|
|
3960
|
+
outcome: z8.object({
|
|
3961
|
+
status: z8.enum(["pass", "fail", "blocked", "skipped", "retrying"]),
|
|
3962
|
+
exitCode: z8.number().int().nullable(),
|
|
3963
|
+
failureKind: z8.enum(["nonzero_exit", "spawn_error", "timeout", "evidence_error"]).nullable(),
|
|
3964
|
+
timeoutCleanup: z8.object({
|
|
3965
|
+
method: z8.enum(["process-group", "taskkill", "direct-child"]),
|
|
3966
|
+
forceKilled: z8.boolean(),
|
|
3967
|
+
descendantsReaped: z8.boolean()
|
|
3968
|
+
}).strict().nullable(),
|
|
3969
|
+
evidenceComplete: z8.boolean()
|
|
3970
|
+
}).strict(),
|
|
3971
|
+
output: z8.object({ stdout: OutputEvidenceSchema, stderr: OutputEvidenceSchema }).strict(),
|
|
3972
|
+
artifacts: z8.array(ArtifactIdentitySchema)
|
|
3973
|
+
}).strict();
|
|
3974
|
+
function parseLocalGateExecutionReceipt(value) {
|
|
3975
|
+
return LocalGateExecutionReceiptSchema.parse(value);
|
|
3976
|
+
}
|
|
3977
|
+
function gateExecutionBinding(gate, item, timeoutMs, candidateDigest) {
|
|
3978
|
+
return {
|
|
3979
|
+
item: item ?? null,
|
|
3980
|
+
declaredGateDigest: computeCanonicalHash(Gate.parse(gate)),
|
|
3981
|
+
candidateDigest: candidateDigest ?? null,
|
|
3982
|
+
timeoutMs
|
|
3983
|
+
};
|
|
3984
|
+
}
|
|
3221
3985
|
function resolveSpawnExecutable(command, environment, workingDirectory, platform2 = process.platform) {
|
|
3222
3986
|
if (isAbsolute2(command) || /[\\/]/u.test(command)) {
|
|
3223
3987
|
return requireExecutable(resolve5(workingDirectory, command));
|
|
@@ -3365,7 +4129,17 @@ function resolveLocalGateShell(command, platform2 = process.platform) {
|
|
|
3365
4129
|
arguments: ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command]
|
|
3366
4130
|
} : { command: "bash", arguments: ["-c", command] };
|
|
3367
4131
|
}
|
|
3368
|
-
|
|
4132
|
+
function sanitizedGateEnvironment(declared) {
|
|
4133
|
+
const environment = { ...process.env };
|
|
4134
|
+
for (const key of Object.keys(environment)) {
|
|
4135
|
+
if (key.toUpperCase().startsWith("GIT_")) delete environment[key];
|
|
4136
|
+
}
|
|
4137
|
+
return { ...environment, ...declared };
|
|
4138
|
+
}
|
|
4139
|
+
function artifactIdentitySegment(identity) {
|
|
4140
|
+
return `identity-${createHash2("sha256").update(identity, "utf8").digest("hex").slice(0, 32)}`;
|
|
4141
|
+
}
|
|
4142
|
+
async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName, skipValidation = false, repoRoot, turnCostTracker, suppressStdout = false, candidateDigest) {
|
|
3369
4143
|
if (!skipValidation && gate.input) {
|
|
3370
4144
|
try {
|
|
3371
4145
|
validateGateInput(gate.name, gate.input);
|
|
@@ -3397,7 +4171,15 @@ async function executeGate(gate, policy, artifactDir, timeoutMs = 3e4, itemName,
|
|
|
3397
4171
|
}
|
|
3398
4172
|
await new Promise((resolve6) => setTimeout(resolve6, delayMs));
|
|
3399
4173
|
}
|
|
3400
|
-
const result = await executeGateAttempt(
|
|
4174
|
+
const result = await executeGateAttempt(
|
|
4175
|
+
gate,
|
|
4176
|
+
artifactDir,
|
|
4177
|
+
attempt,
|
|
4178
|
+
timeoutMs,
|
|
4179
|
+
repoRoot,
|
|
4180
|
+
itemName,
|
|
4181
|
+
candidateDigest
|
|
4182
|
+
);
|
|
3401
4183
|
lastResult = result;
|
|
3402
4184
|
totalDuration += result.duration || 0;
|
|
3403
4185
|
if (turnCostTracker && result.duration) {
|
|
@@ -3499,14 +4281,17 @@ async function writeFlakeReport(itemName, gateName, attempts, totalDuration, art
|
|
|
3499
4281
|
if (!fs9.existsSync(flakeReportDir)) {
|
|
3500
4282
|
fs9.mkdirSync(flakeReportDir, { recursive: true });
|
|
3501
4283
|
}
|
|
3502
|
-
const reportPath = path9.join(
|
|
4284
|
+
const reportPath = path9.join(
|
|
4285
|
+
flakeReportDir,
|
|
4286
|
+
`${artifactIdentitySegment(itemName)}-${artifactIdentitySegment(gateName)}.json`
|
|
4287
|
+
);
|
|
3503
4288
|
const reportContent = canonicalJSONStringify(flakeReport);
|
|
3504
4289
|
fs9.writeFileSync(reportPath, reportContent, "utf-8");
|
|
3505
4290
|
if (!suppressStdout) {
|
|
3506
4291
|
console.log(`\u{1F4CA} Flake report written: ${reportPath}`);
|
|
3507
4292
|
}
|
|
3508
4293
|
}
|
|
3509
|
-
async function executeGateAttempt(gate, artifactDir, attempt, timeoutMs, repoRoot) {
|
|
4294
|
+
async function executeGateAttempt(gate, artifactDir, attempt, timeoutMs, repoRoot, itemName, candidateDigest) {
|
|
3510
4295
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3511
4296
|
const startTime = Date.now();
|
|
3512
4297
|
switch (gate.runtime) {
|
|
@@ -3518,7 +4303,8 @@ async function executeGateAttempt(gate, artifactDir, attempt, timeoutMs, repoRoo
|
|
|
3518
4303
|
startedAt,
|
|
3519
4304
|
startTime,
|
|
3520
4305
|
timeoutMs,
|
|
3521
|
-
repoRoot
|
|
4306
|
+
repoRoot,
|
|
4307
|
+
itemName
|
|
3522
4308
|
);
|
|
3523
4309
|
case "ci-service":
|
|
3524
4310
|
return executeCiServiceGate(gate, artifactDir, attempt, startedAt, startTime);
|
|
@@ -3531,14 +4317,16 @@ async function executeGateAttempt(gate, artifactDir, attempt, timeoutMs, repoRoo
|
|
|
3531
4317
|
startedAt,
|
|
3532
4318
|
startTime,
|
|
3533
4319
|
timeoutMs,
|
|
3534
|
-
repoRoot
|
|
4320
|
+
repoRoot,
|
|
4321
|
+
itemName,
|
|
4322
|
+
candidateDigest
|
|
3535
4323
|
);
|
|
3536
4324
|
}
|
|
3537
4325
|
}
|
|
3538
|
-
async function executeLocalGate(gate, artifactDir, attempt, startedAt, startTime, timeoutMs, repoRoot) {
|
|
4326
|
+
async function executeLocalGate(gate, artifactDir, attempt, startedAt, startTime, timeoutMs, repoRoot, itemName, candidateDigest) {
|
|
3539
4327
|
const workingDirectory = path9.resolve(gate.cwd || repoRoot || process.cwd());
|
|
3540
|
-
const gateArtifactDirectory = path9.join(artifactDir, gate.name);
|
|
3541
|
-
const environment =
|
|
4328
|
+
const gateArtifactDirectory = path9.join(artifactDir, artifactIdentitySegment(gate.name));
|
|
4329
|
+
const environment = sanitizedGateEnvironment(gate.env);
|
|
3542
4330
|
const shell = resolveLocalGateShell(gate.run);
|
|
3543
4331
|
const artifactBaselines = captureDeclaredArtifactBaselines(
|
|
3544
4332
|
gate.artifacts ?? [],
|
|
@@ -3582,7 +4370,10 @@ async function executeLocalGate(gate, artifactDir, attempt, startedAt, startTime
|
|
|
3582
4370
|
artifacts: [],
|
|
3583
4371
|
attempts: attempt,
|
|
3584
4372
|
lastAttempt: startedAt
|
|
3585
|
-
}
|
|
4373
|
+
},
|
|
4374
|
+
itemName,
|
|
4375
|
+
timeoutMs,
|
|
4376
|
+
candidateDigest
|
|
3586
4377
|
});
|
|
3587
4378
|
}
|
|
3588
4379
|
try {
|
|
@@ -3616,7 +4407,10 @@ async function executeLocalGate(gate, artifactDir, attempt, startedAt, startTime
|
|
|
3616
4407
|
artifacts: [],
|
|
3617
4408
|
attempts: attempt,
|
|
3618
4409
|
lastAttempt: startedAt
|
|
3619
|
-
}
|
|
4410
|
+
},
|
|
4411
|
+
itemName,
|
|
4412
|
+
timeoutMs,
|
|
4413
|
+
candidateDigest
|
|
3620
4414
|
});
|
|
3621
4415
|
}
|
|
3622
4416
|
return new Promise((resolve6) => {
|
|
@@ -3680,7 +4474,10 @@ async function executeLocalGate(gate, artifactDir, attempt, startedAt, startTime
|
|
|
3680
4474
|
artifacts: [],
|
|
3681
4475
|
attempts: attempt,
|
|
3682
4476
|
lastAttempt: startedAt
|
|
3683
|
-
}
|
|
4477
|
+
},
|
|
4478
|
+
itemName,
|
|
4479
|
+
timeoutMs,
|
|
4480
|
+
candidateDigest
|
|
3684
4481
|
})
|
|
3685
4482
|
);
|
|
3686
4483
|
});
|
|
@@ -3719,7 +4516,10 @@ async function executeLocalGate(gate, artifactDir, attempt, startedAt, startTime
|
|
|
3719
4516
|
artifacts: [],
|
|
3720
4517
|
attempts: attempt,
|
|
3721
4518
|
lastAttempt: startedAt
|
|
3722
|
-
}
|
|
4519
|
+
},
|
|
4520
|
+
itemName,
|
|
4521
|
+
timeoutMs,
|
|
4522
|
+
candidateDigest
|
|
3723
4523
|
})
|
|
3724
4524
|
);
|
|
3725
4525
|
});
|
|
@@ -3746,6 +4546,12 @@ function finalizeLocalGateAttempt(input) {
|
|
|
3746
4546
|
const receiptPath = writeLocalGateExecutionReceipt(input.gateArtifactDirectory, {
|
|
3747
4547
|
schemaVersion: GATE_EXECUTION_RECEIPT_SCHEMA_VERSION,
|
|
3748
4548
|
attempt: input.attempt,
|
|
4549
|
+
binding: gateExecutionBinding(
|
|
4550
|
+
input.gate,
|
|
4551
|
+
input.itemName,
|
|
4552
|
+
input.timeoutMs,
|
|
4553
|
+
input.candidateDigest
|
|
4554
|
+
),
|
|
3749
4555
|
declaredGate: {
|
|
3750
4556
|
name: input.gate.name,
|
|
3751
4557
|
run: input.gate.run,
|
|
@@ -3783,11 +4589,21 @@ function finalizeLocalGateAttempt(input) {
|
|
|
3783
4589
|
result.artifacts = [...collected.paths, receiptPath];
|
|
3784
4590
|
return result;
|
|
3785
4591
|
}
|
|
3786
|
-
async function executeContainerGate(gate, artifactDir, attempt, startedAt, startTime, timeoutMs, repoRoot) {
|
|
3787
|
-
console.warn(
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
4592
|
+
async function executeContainerGate(gate, artifactDir, attempt, startedAt, startTime, timeoutMs, repoRoot, itemName) {
|
|
4593
|
+
console.warn(`\u26A0\uFE0F Container runtime for gate '${gate.name}' is not implemented; failing closed`);
|
|
4594
|
+
return {
|
|
4595
|
+
gate: gate.name,
|
|
4596
|
+
status: "fail",
|
|
4597
|
+
exitCode: 1,
|
|
4598
|
+
duration: Date.now() - startTime,
|
|
4599
|
+
stdout: "",
|
|
4600
|
+
stderr: "GATE_RUNTIME_UNAVAILABLE: container execution is not implemented",
|
|
4601
|
+
failureKind: "evidence_error",
|
|
4602
|
+
artifacts: [],
|
|
4603
|
+
attempts: attempt,
|
|
4604
|
+
lastAttempt: startedAt,
|
|
4605
|
+
timeoutMs
|
|
4606
|
+
};
|
|
3791
4607
|
}
|
|
3792
4608
|
async function executeCiServiceGate(gate, artifactDir, attempt, startedAt, startTime) {
|
|
3793
4609
|
console.warn(
|
|
@@ -3952,7 +4768,7 @@ async function executeItemGates(item, policy, executionState, artifactDir, timeo
|
|
|
3952
4768
|
return [];
|
|
3953
4769
|
}
|
|
3954
4770
|
const results = [];
|
|
3955
|
-
const itemArtifactDir = path9.join(artifactDir, item.name);
|
|
4771
|
+
const itemArtifactDir = path9.join(artifactDir, artifactIdentitySegment(item.name));
|
|
3956
4772
|
if (!fs9.existsSync(itemArtifactDir)) {
|
|
3957
4773
|
fs9.mkdirSync(itemArtifactDir, { recursive: true });
|
|
3958
4774
|
}
|
|
@@ -4003,13 +4819,15 @@ async function executeItemGates(item, policy, executionState, artifactDir, timeo
|
|
|
4003
4819
|
gate,
|
|
4004
4820
|
policy,
|
|
4005
4821
|
itemArtifactDir,
|
|
4006
|
-
timeoutMs,
|
|
4822
|
+
gate.timeoutMs ?? timeoutMs,
|
|
4007
4823
|
item.name,
|
|
4008
4824
|
skipValidation,
|
|
4009
4825
|
repoRoot,
|
|
4010
4826
|
options?.turnCostTracker,
|
|
4011
|
-
options?.suppressStdout
|
|
4827
|
+
options?.suppressStdout,
|
|
4828
|
+
options?.candidateDigest
|
|
4012
4829
|
);
|
|
4830
|
+
result.timeoutMs = gate.timeoutMs ?? timeoutMs;
|
|
4013
4831
|
results.push(result);
|
|
4014
4832
|
executionState.updateGateResult(item.name, result);
|
|
4015
4833
|
if (result.status === "fail" && options?.runId) {
|
|
@@ -5885,6 +6703,14 @@ var AutopilotLevel4 = class extends AutopilotLevel3 {
|
|
|
5885
6703
|
};
|
|
5886
6704
|
|
|
5887
6705
|
export {
|
|
6706
|
+
parseTierOverrides,
|
|
6707
|
+
Policy,
|
|
6708
|
+
Plan,
|
|
6709
|
+
SchemaValidationError,
|
|
6710
|
+
formatPlanValidationFailure,
|
|
6711
|
+
asPlanValidationFailure,
|
|
6712
|
+
formatPlanValidationFailureText,
|
|
6713
|
+
loadPlan,
|
|
5888
6714
|
getRunsDir,
|
|
5889
6715
|
getRunDir,
|
|
5890
6716
|
writeRunState,
|
|
@@ -5901,6 +6727,14 @@ export {
|
|
|
5901
6727
|
listCounterExamples,
|
|
5902
6728
|
loadCounterExample,
|
|
5903
6729
|
exportCounterExamples,
|
|
6730
|
+
computeCanonicalHash,
|
|
6731
|
+
computeCanonicalHashFromCompactJSON,
|
|
6732
|
+
RepoRelativePath,
|
|
6733
|
+
SHA256Hash,
|
|
6734
|
+
GATE_EXECUTION_RECEIPT_SCHEMA_VERSION,
|
|
6735
|
+
parseLocalGateExecutionReceipt,
|
|
6736
|
+
resolveSpawnExecutable,
|
|
6737
|
+
fileIdentity,
|
|
5904
6738
|
executeGate,
|
|
5905
6739
|
executeGatesWithPolicy,
|
|
5906
6740
|
getEnvWithAlias,
|