@autonoma-ai/planner 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/index.js +1408 -846
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -549,7 +549,7 @@ var init_theme = __esm({
|
|
|
549
549
|
});
|
|
550
550
|
|
|
551
551
|
// src/ui/components/render-content.ts
|
|
552
|
-
import matter from "gray-matter";
|
|
552
|
+
import matter from "@11ty/gray-matter";
|
|
553
553
|
import { z as z2 } from "zod";
|
|
554
554
|
function renderContent(text2, kind, name) {
|
|
555
555
|
if (memo != null && memo.text === text2 && memo.kind === kind && memo.name === name) return memo.lines;
|
|
@@ -2101,6 +2101,14 @@ var init_prompts = __esm({
|
|
|
2101
2101
|
}
|
|
2102
2102
|
});
|
|
2103
2103
|
|
|
2104
|
+
// ../../packages/types/src/types/previewkit.ts
|
|
2105
|
+
var init_previewkit = __esm({
|
|
2106
|
+
"../../packages/types/src/types/previewkit.ts"() {
|
|
2107
|
+
"use strict";
|
|
2108
|
+
init_esm_shims();
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
|
|
2104
2112
|
// ../../packages/types/src/types/step-overlay-points.ts
|
|
2105
2113
|
import { z as z4 } from "zod";
|
|
2106
2114
|
var overlayPointSchema, pointSchema, outputWithPointsSchema;
|
|
@@ -2288,11 +2296,12 @@ function analysisVerdictPlane(category) {
|
|
|
2288
2296
|
const tier = analysisFindingTier(category);
|
|
2289
2297
|
return tier === "bug" || tier === "passed" ? "app_health" : "coverage";
|
|
2290
2298
|
}
|
|
2291
|
-
var analysisVerdictSchema, ANALYSIS_VERDICT, VERDICT_TIER, coverageVerdicts, analysisTestOriginSchema, coverageCategoryCountSchema, coverageSummarySchema, analysisClassificationReportSchema, analysisClassificationSummarySchema, analysisFindingViewSchema, analysisReportDataSchema, analysisIssueKindSchema, analysisIssueSeveritySchema, analysisIssueStatusSchema, resolvedPrimaryScreenshotSchema, analysisIssueSummarySchema, analysisIssueFindingInstanceSchema, analysisIssueDetailSchema, analysisPrCoveredTestSchema, analysisPrIssueSchema, analysisPrNewerRunSchema, analysisForPrSchema, mainOpenProblemSchema, analysisSnapshotIssueChangesSchema;
|
|
2299
|
+
var analysisVerdictSchema, ANALYSIS_VERDICT, VERDICT_TIER, coverageVerdicts, analysisFlowStatusSchema, analysisFlowOwnerSchema, analysisFlowSchema, analysisTestOriginSchema, coverageCategoryCountSchema, coverageSummarySchema, analysisClassificationReportSchema, analysisClassificationSummarySchema, analysisFindingViewSchema, analysisReportDataSchema, analysisIssueKindSchema, analysisIssueSeveritySchema, analysisIssueStatusSchema, resolvedPrimaryScreenshotSchema, analysisIssueSummarySchema, analysisIssueFindingInstanceSchema, analysisIssueDetailSchema, analysisPrCoveredTestSchema, analysisPrIssueSchema, analysisPrNewerRunSchema, analysisForPrSchema, mainOpenProblemSchema, analysisSnapshotIssueChangesSchema;
|
|
2292
2300
|
var init_analysis = __esm({
|
|
2293
2301
|
"../../packages/types/src/schemas/analysis.ts"() {
|
|
2294
2302
|
"use strict";
|
|
2295
2303
|
init_esm_shims();
|
|
2304
|
+
init_previewkit();
|
|
2296
2305
|
init_step_overlay_points();
|
|
2297
2306
|
init_evidence_tokens();
|
|
2298
2307
|
init_investigation_report();
|
|
@@ -2322,6 +2331,26 @@ var init_analysis = __esm({
|
|
|
2322
2331
|
coverageVerdicts = analysisVerdictSchema.options.filter(
|
|
2323
2332
|
(verdict) => analysisVerdictPlane(verdict) === "coverage"
|
|
2324
2333
|
);
|
|
2334
|
+
analysisFlowStatusSchema = z8.enum(["broken", "verified", "partial", "unverified"]);
|
|
2335
|
+
analysisFlowOwnerSchema = z8.enum(["client", "autonoma", "none"]);
|
|
2336
|
+
analysisFlowSchema = z8.object({
|
|
2337
|
+
/** The reader-facing name, authored by the Reporter: "Guest checkout", not `checkout-guest-express-lane`. */
|
|
2338
|
+
title: z8.string().min(1),
|
|
2339
|
+
/** One sentence, authored by the Reporter: what was confirmed, or why it could not be. */
|
|
2340
|
+
detail: z8.string(),
|
|
2341
|
+
status: analysisFlowStatusSchema,
|
|
2342
|
+
owner: analysisFlowOwnerSchema,
|
|
2343
|
+
/** Cited tests whose last-known verdict confirmed the app. */
|
|
2344
|
+
passedCount: z8.number().int().nonnegative(),
|
|
2345
|
+
/** Cited tests whose last-known verdict is a coverage-plane gap. */
|
|
2346
|
+
gapCount: z8.number().int().nonnegative(),
|
|
2347
|
+
/** Cited tests whose last-known verdict is a client bug. */
|
|
2348
|
+
bugCount: z8.number().int().nonnegative(),
|
|
2349
|
+
/** How many cited verdicts came from the run being reported. Zero means the whole flow is carried from earlier. */
|
|
2350
|
+
checkedThisRunCount: z8.number().int().nonnegative(),
|
|
2351
|
+
/** The tests this flow covers. Every test in the branch's map lands in exactly one flow. */
|
|
2352
|
+
testSlugs: z8.array(z8.string())
|
|
2353
|
+
});
|
|
2325
2354
|
analysisTestOriginSchema = z8.enum(["pre_existing", "proposed"]);
|
|
2326
2355
|
coverageCategoryCountSchema = z8.object({
|
|
2327
2356
|
category: analysisVerdictSchema,
|
|
@@ -2417,17 +2446,24 @@ var init_analysis = __esm({
|
|
|
2417
2446
|
/** The Impact Analysis stage's account of why it selected the tests it did (admin-only on the snapshot page). */
|
|
2418
2447
|
impactReasoning: z8.string().optional(),
|
|
2419
2448
|
/**
|
|
2420
|
-
* The Reporter's holistic PR report prose (Markdown), the hero of the PR page and the snapshot per-job view.
|
|
2421
|
-
*
|
|
2422
|
-
*
|
|
2423
|
-
*
|
|
2449
|
+
* The Reporter's holistic PR report prose (Markdown), the hero of the PR page and the snapshot per-job view. Its
|
|
2450
|
+
* inline `evidence:` image tokens resolve against `reportEvidence`; `issue:`/`finding:` link tokens resolve
|
|
2451
|
+
* against the branch's issues and this report's findings.
|
|
2452
|
+
*
|
|
2453
|
+
* Never empty: a report row exists only once the Reporter has authored one, and `finish` refuses prose that
|
|
2454
|
+
* sanitizing emptied.
|
|
2424
2455
|
*/
|
|
2425
|
-
reportMarkdown: z8.string()
|
|
2456
|
+
reportMarkdown: z8.string(),
|
|
2426
2457
|
/**
|
|
2427
|
-
* The Reporter's
|
|
2428
|
-
*
|
|
2458
|
+
* The Reporter's title for the PR as a whole (about eight words). EMPTY on a report written before the Reporter
|
|
2459
|
+
* authored titles - `analysisPrTitle` is the one place that reads it as unauthored, and it also overrides the
|
|
2460
|
+
* authored value for an open bug and for a run that needed no tests.
|
|
2429
2461
|
*/
|
|
2430
|
-
|
|
2462
|
+
title: z8.string(),
|
|
2463
|
+
/** The Reporter's headline: the CUMULATIVE state of the branch in 1-3 plain sentences. Never empty. */
|
|
2464
|
+
headline: z8.string(),
|
|
2465
|
+
/** The branch's flow itemization: what this PR has established and what it has not. Empty on a pre-flows run. */
|
|
2466
|
+
flows: z8.array(analysisFlowSchema),
|
|
2431
2467
|
/** The signed assets `reportMarkdown` may embed inline by `evidence:<assetId>` token (referenced ones only). */
|
|
2432
2468
|
reportEvidence: z8.array(resolvedEvidenceAssetSchema),
|
|
2433
2469
|
/** The persisted app-health verdict for the run (issue-derived at finalize): `client_bug` or `passed`. */
|
|
@@ -2526,8 +2562,16 @@ var init_analysis = __esm({
|
|
|
2526
2562
|
status: z8.literal("complete"),
|
|
2527
2563
|
/** The app-health verdict: `client_bug` when the branch has an open bug issue, else `passed`. */
|
|
2528
2564
|
verdict: analysisVerdictSchema,
|
|
2529
|
-
/** The Reporter's
|
|
2530
|
-
|
|
2565
|
+
/** The Reporter's title for the PR as a whole. Empty on a report written before the Reporter authored one. */
|
|
2566
|
+
title: z8.string(),
|
|
2567
|
+
/** The Reporter's headline: the cumulative state of the branch in 1-3 plain sentences. */
|
|
2568
|
+
headline: z8.string(),
|
|
2569
|
+
/**
|
|
2570
|
+
* The branch's flow itemization: which parts of the app this PR has established and which it has not. A
|
|
2571
|
+
* consumer reading `verdict` alone learns only whether a bug was found - these say how much was covered, which
|
|
2572
|
+
* is the difference between "nothing broke" and "we checked".
|
|
2573
|
+
*/
|
|
2574
|
+
flows: z8.array(analysisFlowSchema),
|
|
2531
2575
|
/** The Reporter's holistic report prose. Its `evidence:` image tokens resolve against `reportEvidence`, and
|
|
2532
2576
|
* its `issue:` tokens against the `issues` below. */
|
|
2533
2577
|
reportMarkdown: z8.string().optional(),
|
|
@@ -3087,6 +3131,97 @@ var init_api_key = __esm({
|
|
|
3087
3131
|
}
|
|
3088
3132
|
});
|
|
3089
3133
|
|
|
3134
|
+
// ../../packages/types/src/schemas/organization.ts
|
|
3135
|
+
import { z as z14 } from "zod";
|
|
3136
|
+
var InvitationStatusSchema, InviteMemberInputSchema, RevokeInvitationInputSchema, RemoveMemberInputSchema, InvitationIdInputSchema, SetActiveOrganizationInputSchema, RenameOrganizationInputSchema, LeaveOrganizationInputSchema, InvitationOutcomeSchema, InvitationPreviewSchema, MyOrganizationSchema, AppSlugOwnerSchema, AppSlugOwnersInputSchema, OrganizationMemberSchema, PendingInvitationSchema;
|
|
3137
|
+
var init_organization = __esm({
|
|
3138
|
+
"../../packages/types/src/schemas/organization.ts"() {
|
|
3139
|
+
"use strict";
|
|
3140
|
+
init_esm_shims();
|
|
3141
|
+
InvitationStatusSchema = z14.enum(["pending", "accepted", "rejected", "canceled"]);
|
|
3142
|
+
InviteMemberInputSchema = z14.object({
|
|
3143
|
+
email: z14.email("Enter a valid email address").trim().toLowerCase()
|
|
3144
|
+
});
|
|
3145
|
+
RevokeInvitationInputSchema = z14.object({
|
|
3146
|
+
invitationId: z14.string().min(1)
|
|
3147
|
+
});
|
|
3148
|
+
RemoveMemberInputSchema = z14.object({
|
|
3149
|
+
userId: z14.string().min(1)
|
|
3150
|
+
});
|
|
3151
|
+
InvitationIdInputSchema = z14.object({
|
|
3152
|
+
invitationId: z14.string().min(1)
|
|
3153
|
+
});
|
|
3154
|
+
SetActiveOrganizationInputSchema = z14.object({
|
|
3155
|
+
organizationId: z14.string().min(1)
|
|
3156
|
+
});
|
|
3157
|
+
RenameOrganizationInputSchema = z14.object({
|
|
3158
|
+
organizationId: z14.string().min(1),
|
|
3159
|
+
name: z14.string().trim().min(1, "Enter a name").max(100, "Keep it under 100 characters")
|
|
3160
|
+
});
|
|
3161
|
+
LeaveOrganizationInputSchema = z14.object({
|
|
3162
|
+
organizationId: z14.string().min(1)
|
|
3163
|
+
});
|
|
3164
|
+
InvitationOutcomeSchema = z14.enum([
|
|
3165
|
+
"joinable",
|
|
3166
|
+
"wrong-account",
|
|
3167
|
+
"already-member",
|
|
3168
|
+
"expired",
|
|
3169
|
+
"revoked",
|
|
3170
|
+
"accepted",
|
|
3171
|
+
"declined"
|
|
3172
|
+
]);
|
|
3173
|
+
InvitationPreviewSchema = z14.object({
|
|
3174
|
+
invitationId: z14.string(),
|
|
3175
|
+
organizationName: z14.string(),
|
|
3176
|
+
inviterName: z14.string(),
|
|
3177
|
+
invitedEmail: z14.string(),
|
|
3178
|
+
outcome: InvitationOutcomeSchema
|
|
3179
|
+
});
|
|
3180
|
+
MyOrganizationSchema = z14.object({
|
|
3181
|
+
id: z14.string(),
|
|
3182
|
+
name: z14.string(),
|
|
3183
|
+
slug: z14.string(),
|
|
3184
|
+
/** Which one the current session is acting as. Session-scoped: switching does not persist. */
|
|
3185
|
+
isActive: z14.boolean(),
|
|
3186
|
+
memberCount: z14.number().int().positive(),
|
|
3187
|
+
applicationCount: z14.number().int().nonnegative(),
|
|
3188
|
+
joinedAt: z14.date(),
|
|
3189
|
+
/**
|
|
3190
|
+
* Why leaving is refused, if it is. `last-organization` - it is their only one, and an
|
|
3191
|
+
* account with no organization cannot reach anything. `last-member` - nobody would be
|
|
3192
|
+
* left who could reach the organization's applications.
|
|
3193
|
+
*/
|
|
3194
|
+
leaveBlockedReason: z14.enum(["last-organization", "last-member"]).optional()
|
|
3195
|
+
});
|
|
3196
|
+
AppSlugOwnerSchema = z14.object({
|
|
3197
|
+
organizationId: z14.string(),
|
|
3198
|
+
organizationName: z14.string(),
|
|
3199
|
+
organizationSlug: z14.string()
|
|
3200
|
+
});
|
|
3201
|
+
AppSlugOwnersInputSchema = z14.object({
|
|
3202
|
+
appSlug: z14.string().min(1)
|
|
3203
|
+
});
|
|
3204
|
+
OrganizationMemberSchema = z14.object({
|
|
3205
|
+
userId: z14.string(),
|
|
3206
|
+
name: z14.string(),
|
|
3207
|
+
email: z14.string(),
|
|
3208
|
+
image: z14.string().optional(),
|
|
3209
|
+
role: z14.string(),
|
|
3210
|
+
joinedAt: z14.date(),
|
|
3211
|
+
/** True for the member making the request - the row that can't be removed. */
|
|
3212
|
+
isSelf: z14.boolean()
|
|
3213
|
+
});
|
|
3214
|
+
PendingInvitationSchema = z14.object({
|
|
3215
|
+
id: z14.string(),
|
|
3216
|
+
email: z14.string(),
|
|
3217
|
+
inviterName: z14.string(),
|
|
3218
|
+
expiresAt: z14.date(),
|
|
3219
|
+
/** Where to send the invitee if the email never arrives and someone wants to paste the link. */
|
|
3220
|
+
acceptUrl: z14.string()
|
|
3221
|
+
});
|
|
3222
|
+
}
|
|
3223
|
+
});
|
|
3224
|
+
|
|
3090
3225
|
// ../../packages/types/src/schemas/previewkit-builtins.ts
|
|
3091
3226
|
function isReservedPreviewkitEnvKey(key) {
|
|
3092
3227
|
return PREVIEWKIT_BUILTIN_ENV_KEYS.has(key);
|
|
@@ -3138,7 +3273,7 @@ var init_previewkit_builtins = __esm({
|
|
|
3138
3273
|
});
|
|
3139
3274
|
|
|
3140
3275
|
// ../../packages/types/src/schemas/secrets.ts
|
|
3141
|
-
import { z as
|
|
3276
|
+
import { z as z15 } from "zod";
|
|
3142
3277
|
var SECRET_KEY_REGEX, APP_NAME_REGEX, SecretKeySchema, AppNameSchema, SecretItemSchema, ListSecretAppsInputSchema, ListSecretsInputSchema, UpsertSecretsInputSchema, DeleteSecretInputSchema, PreviewkitConfigAppSecretsSchema, PreviewkitConfigSecretsSchema;
|
|
3143
3278
|
var init_secrets = __esm({
|
|
3144
3279
|
"../../packages/types/src/schemas/secrets.ts"() {
|
|
@@ -3147,12 +3282,12 @@ var init_secrets = __esm({
|
|
|
3147
3282
|
init_previewkit_builtins();
|
|
3148
3283
|
SECRET_KEY_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3149
3284
|
APP_NAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
3150
|
-
SecretKeySchema =
|
|
3285
|
+
SecretKeySchema = z15.string().min(1).max(256).regex(
|
|
3151
3286
|
SECRET_KEY_REGEX,
|
|
3152
3287
|
"Keys must start with a letter or underscore and contain only letters, numbers, and underscores"
|
|
3153
3288
|
);
|
|
3154
|
-
AppNameSchema =
|
|
3155
|
-
SecretItemSchema =
|
|
3289
|
+
AppNameSchema = z15.string().min(2).max(63).regex(APP_NAME_REGEX, "App name must be lowercase alphanumeric with hyphens (Kubernetes label-compatible)");
|
|
3290
|
+
SecretItemSchema = z15.object({
|
|
3156
3291
|
key: SecretKeySchema.superRefine((key, ctx) => {
|
|
3157
3292
|
if (isReservedPreviewkitEnvKey(key)) {
|
|
3158
3293
|
ctx.addIssue({
|
|
@@ -3166,31 +3301,50 @@ var init_secrets = __esm({
|
|
|
3166
3301
|
});
|
|
3167
3302
|
}
|
|
3168
3303
|
}),
|
|
3169
|
-
value:
|
|
3304
|
+
value: z15.string().min(1).max(65536)
|
|
3170
3305
|
});
|
|
3171
|
-
ListSecretAppsInputSchema =
|
|
3172
|
-
applicationId:
|
|
3306
|
+
ListSecretAppsInputSchema = z15.object({
|
|
3307
|
+
applicationId: z15.string()
|
|
3173
3308
|
});
|
|
3174
|
-
ListSecretsInputSchema =
|
|
3175
|
-
applicationId:
|
|
3309
|
+
ListSecretsInputSchema = z15.object({
|
|
3310
|
+
applicationId: z15.string(),
|
|
3176
3311
|
appName: AppNameSchema
|
|
3177
3312
|
});
|
|
3178
|
-
UpsertSecretsInputSchema =
|
|
3179
|
-
applicationId:
|
|
3313
|
+
UpsertSecretsInputSchema = z15.object({
|
|
3314
|
+
applicationId: z15.string(),
|
|
3180
3315
|
appName: AppNameSchema,
|
|
3181
|
-
items:
|
|
3316
|
+
items: z15.array(SecretItemSchema).min(1).max(200)
|
|
3182
3317
|
});
|
|
3183
|
-
DeleteSecretInputSchema =
|
|
3184
|
-
applicationId:
|
|
3318
|
+
DeleteSecretInputSchema = z15.object({
|
|
3319
|
+
applicationId: z15.string(),
|
|
3185
3320
|
appName: AppNameSchema,
|
|
3186
3321
|
key: SecretKeySchema
|
|
3187
3322
|
});
|
|
3188
|
-
PreviewkitConfigAppSecretsSchema =
|
|
3323
|
+
PreviewkitConfigAppSecretsSchema = z15.object({
|
|
3189
3324
|
appName: AppNameSchema,
|
|
3190
|
-
upserts:
|
|
3191
|
-
deletes:
|
|
3325
|
+
upserts: z15.array(SecretItemSchema).max(200).default([]),
|
|
3326
|
+
deletes: z15.array(SecretKeySchema).max(200).default([])
|
|
3192
3327
|
});
|
|
3193
|
-
PreviewkitConfigSecretsSchema =
|
|
3328
|
+
PreviewkitConfigSecretsSchema = z15.array(PreviewkitConfigAppSecretsSchema).max(50);
|
|
3329
|
+
}
|
|
3330
|
+
});
|
|
3331
|
+
|
|
3332
|
+
// ../../packages/types/src/parse-url.ts
|
|
3333
|
+
var init_parse_url = __esm({
|
|
3334
|
+
"../../packages/types/src/parse-url.ts"() {
|
|
3335
|
+
"use strict";
|
|
3336
|
+
init_esm_shims();
|
|
3337
|
+
}
|
|
3338
|
+
});
|
|
3339
|
+
|
|
3340
|
+
// ../../packages/types/src/sdk-endpoint.ts
|
|
3341
|
+
var DEFAULT_SDK_PATH;
|
|
3342
|
+
var init_sdk_endpoint = __esm({
|
|
3343
|
+
"../../packages/types/src/sdk-endpoint.ts"() {
|
|
3344
|
+
"use strict";
|
|
3345
|
+
init_esm_shims();
|
|
3346
|
+
init_parse_url();
|
|
3347
|
+
DEFAULT_SDK_PATH = "/api/autonoma";
|
|
3194
3348
|
}
|
|
3195
3349
|
});
|
|
3196
3350
|
|
|
@@ -3619,7 +3773,7 @@ var init_previewkit_presets = __esm({
|
|
|
3619
3773
|
});
|
|
3620
3774
|
|
|
3621
3775
|
// ../../packages/types/src/schemas/previewkit-config.ts
|
|
3622
|
-
import { z as
|
|
3776
|
+
import { z as z16 } from "zod";
|
|
3623
3777
|
function buildResourcesSchema(role, allowCustomResources) {
|
|
3624
3778
|
return resourcesInput.transform((input) => {
|
|
3625
3779
|
if (!allowCustomResources || input == null) {
|
|
@@ -3634,86 +3788,96 @@ function buildResourcesSchema(role, allowCustomResources) {
|
|
|
3634
3788
|
});
|
|
3635
3789
|
}
|
|
3636
3790
|
function singleLineCommand(label) {
|
|
3637
|
-
return
|
|
3791
|
+
return z16.string().min(1).regex(/^[^\r\n]+$/, `${label} must be a single line (no line breaks)`);
|
|
3638
3792
|
}
|
|
3639
3793
|
function heredocSafeScript(label) {
|
|
3640
|
-
return
|
|
3794
|
+
return z16.string().min(1).refine(
|
|
3641
3795
|
(value) => !value.split("\n").includes(PREVIEWKIT_BUILD_SCRIPT_HEREDOC),
|
|
3642
3796
|
`${label} cannot contain a line equal to "${PREVIEWKIT_BUILD_SCRIPT_HEREDOC}" (reserved heredoc delimiter)`
|
|
3643
3797
|
);
|
|
3644
3798
|
}
|
|
3645
3799
|
function nodeFrameworkBuildSchema(framework) {
|
|
3646
|
-
return
|
|
3647
|
-
framework:
|
|
3648
|
-
package_manager:
|
|
3649
|
-
node_version:
|
|
3650
|
-
install_command:
|
|
3651
|
-
build_command:
|
|
3652
|
-
run_command:
|
|
3800
|
+
return z16.object({
|
|
3801
|
+
framework: z16.literal(framework),
|
|
3802
|
+
package_manager: z16.enum(["npm", "pnpm", "yarn"]).default("pnpm"),
|
|
3803
|
+
node_version: z16.string().regex(nodeVersionRegex, "must look like 22, 22.5, or 22.5.0").default("22"),
|
|
3804
|
+
install_command: z16.string().min(1).optional(),
|
|
3805
|
+
build_command: z16.string().min(1).optional(),
|
|
3806
|
+
run_command: z16.string().min(1).optional(),
|
|
3653
3807
|
build_context: buildContextSchema
|
|
3654
3808
|
});
|
|
3655
3809
|
}
|
|
3656
3810
|
function buildPreviewConfigSchema(build, allowCustomResources) {
|
|
3657
|
-
const appSchema =
|
|
3658
|
-
name:
|
|
3659
|
-
repository:
|
|
3811
|
+
const appSchema = z16.object({
|
|
3812
|
+
name: z16.string().regex(k8sNameRegex, "Must be a valid Kubernetes name"),
|
|
3813
|
+
repository: z16.string().regex(repoFullNameRegex, "Must be an owner/repo full name").describe(
|
|
3660
3814
|
"The owner/repo full name of the GitHub repository this app builds from. Mandatory even in single-repo setups. Any value other than the application's own repository makes this app a multirepo dependency: that repo is cloned at the branch the branch_convention (or its repositories[] fallback_branch) resolves to."
|
|
3661
3815
|
),
|
|
3662
|
-
path:
|
|
3663
|
-
build_context:
|
|
3664
|
-
dockerfile:
|
|
3816
|
+
path: z16.string().default("."),
|
|
3817
|
+
build_context: z16.string().optional(),
|
|
3818
|
+
dockerfile: z16.string().optional(),
|
|
3665
3819
|
build: build.optional(),
|
|
3666
3820
|
// The preset-based deploy model, mutually exclusive with `build` (enforced
|
|
3667
3821
|
// by the superRefine below). Lowered to a `runtime` Build by the generator.
|
|
3668
3822
|
blueprint: blueprintSchema.optional(),
|
|
3669
3823
|
// The AWS-secret keys to also inject at build time (Docker build args).
|
|
3670
3824
|
// Runtime secret values live in AWS Secrets Manager, never in this document.
|
|
3671
|
-
build_secrets:
|
|
3672
|
-
port:
|
|
3825
|
+
build_secrets: z16.array(z16.string()).default([]),
|
|
3826
|
+
port: z16.number().int().positive(),
|
|
3673
3827
|
// Non-secret variables wired to the topology, resolved at deploy time.
|
|
3674
3828
|
// All user-typed values are secrets (AWS), so they never appear here.
|
|
3675
|
-
connections:
|
|
3676
|
-
command:
|
|
3677
|
-
health_check:
|
|
3678
|
-
primary:
|
|
3679
|
-
// This app serves the Environment Factory handler
|
|
3680
|
-
//
|
|
3681
|
-
//
|
|
3682
|
-
//
|
|
3683
|
-
sdk_implemented:
|
|
3829
|
+
connections: z16.array(connectionSchema).default([]),
|
|
3830
|
+
command: z16.string().optional(),
|
|
3831
|
+
health_check: z16.string().optional(),
|
|
3832
|
+
primary: z16.boolean().optional(),
|
|
3833
|
+
// This app serves the Environment Factory handler, so scenario up/down
|
|
3834
|
+
// calls go to its preview URL. Independent of `primary`: a full-stack app
|
|
3835
|
+
// (Next.js, Rails) is both the browsed frontend and the SDK host, while a
|
|
3836
|
+
// split topology mounts the handler on its API service.
|
|
3837
|
+
sdk_implemented: z16.boolean().optional(),
|
|
3838
|
+
// Where on that app the handler is mounted. Deliberately NO `.default()`:
|
|
3839
|
+
// absent has to stay distinguishable from an explicit `/api/autonoma`,
|
|
3840
|
+
// because it is what tells a caller to leave an already-stored endpoint
|
|
3841
|
+
// URL alone (see `applySdkPath`). Give it a default and every endpoint a
|
|
3842
|
+
// customer registered by hand at another path gets rewritten to the
|
|
3843
|
+
// convention.
|
|
3844
|
+
sdk_path: z16.string().regex(
|
|
3845
|
+
SDK_PATH_PATTERN,
|
|
3846
|
+
`Must be an absolute path with no query or fragment, like "${DEFAULT_SDK_PATH}"`
|
|
3847
|
+
).optional(),
|
|
3684
3848
|
resources: buildResourcesSchema("app", allowCustomResources),
|
|
3685
|
-
depends_on:
|
|
3849
|
+
depends_on: z16.array(z16.string()).optional()
|
|
3686
3850
|
}).superRefine((app, ctx) => {
|
|
3687
3851
|
if (app.build != null && app.blueprint != null) {
|
|
3688
3852
|
ctx.addIssue({
|
|
3689
|
-
code:
|
|
3853
|
+
code: z16.ZodIssueCode.custom,
|
|
3690
3854
|
message: "an app cannot set both `build` and `blueprint` - `blueprint` is the preset-based deploy model, `build` is the manual one",
|
|
3691
3855
|
path: ["blueprint"]
|
|
3692
3856
|
});
|
|
3693
3857
|
}
|
|
3694
3858
|
});
|
|
3695
|
-
const serviceSchema =
|
|
3696
|
-
name:
|
|
3697
|
-
recipe:
|
|
3698
|
-
version:
|
|
3859
|
+
const serviceSchema = z16.object({
|
|
3860
|
+
name: z16.string().regex(k8sNameRegex, "Must be a valid Kubernetes name"),
|
|
3861
|
+
recipe: z16.string(),
|
|
3862
|
+
version: z16.string().optional(),
|
|
3699
3863
|
// Recipe-functional knobs (e.g. postgres user/database, or a docker-image
|
|
3700
3864
|
// service's image/ports/env) live in `options`, validated per-recipe.
|
|
3701
|
-
options:
|
|
3865
|
+
options: z16.record(z16.string(), z16.unknown()).default({}),
|
|
3702
3866
|
// Guided setup for database-recipe services (schema, seed, migrations),
|
|
3703
3867
|
// run with the repo checked out. Empty for non-database services.
|
|
3704
|
-
setup_tasks:
|
|
3868
|
+
setup_tasks: z16.array(databaseSetupTaskSchema).default([]),
|
|
3705
3869
|
resources: buildResourcesSchema("service", allowCustomResources),
|
|
3706
|
-
s3:
|
|
3707
|
-
sqs:
|
|
3708
|
-
sns:
|
|
3870
|
+
s3: z16.boolean().optional(),
|
|
3871
|
+
sqs: z16.boolean().optional(),
|
|
3872
|
+
sns: z16.boolean().optional()
|
|
3709
3873
|
});
|
|
3710
|
-
return
|
|
3711
|
-
version:
|
|
3712
|
-
domain:
|
|
3713
|
-
registry:
|
|
3874
|
+
return z16.object({
|
|
3875
|
+
version: z16.literal(PREVIEW_CONFIG_VERSION),
|
|
3876
|
+
domain: z16.string().optional(),
|
|
3877
|
+
registry: z16.string().optional(),
|
|
3714
3878
|
// Per-repository overrides + deploy provenance; the repo set itself
|
|
3715
3879
|
// is derived from `apps[].repository`. See repositorySettingsSchema.
|
|
3716
|
-
repositories:
|
|
3880
|
+
repositories: z16.array(repositorySettingsSchema).default([]).describe(
|
|
3717
3881
|
"Optional per-repository settings. The topology's repositories are derived from apps[].repository - an entry here only overrides defaults (fallback_branch: which branch to clone when the PR's branch does not exist in that repo; default main)."
|
|
3718
3882
|
),
|
|
3719
3883
|
// Topology-wide: how a dependency repo's branch is derived from the
|
|
@@ -3721,8 +3885,8 @@ function buildPreviewConfigSchema(build, allowCustomResources) {
|
|
|
3721
3885
|
branch_convention: branchConventionSchema.optional().describe(
|
|
3722
3886
|
"How a dependency repo's branch is derived from the PR branch: same_branch_name (default), regex (pattern + replacement rewrite), or manual (always the fallback_branch)."
|
|
3723
3887
|
),
|
|
3724
|
-
apps:
|
|
3725
|
-
services:
|
|
3888
|
+
apps: z16.array(appSchema).min(1, "At least one app is required"),
|
|
3889
|
+
services: z16.array(serviceSchema).default([]),
|
|
3726
3890
|
hooks: hooksSchema
|
|
3727
3891
|
}).superRefine((cfg, ctx) => {
|
|
3728
3892
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -3730,7 +3894,7 @@ function buildPreviewConfigSchema(build, allowCustomResources) {
|
|
|
3730
3894
|
const existing = seen.get(name);
|
|
3731
3895
|
if (existing != null) {
|
|
3732
3896
|
ctx.addIssue({
|
|
3733
|
-
code:
|
|
3897
|
+
code: z16.ZodIssueCode.custom,
|
|
3734
3898
|
message: `Name "${name}" is used by both a ${existing} and an ${kind} - names must be unique across apps and services`
|
|
3735
3899
|
});
|
|
3736
3900
|
return;
|
|
@@ -3744,7 +3908,7 @@ function buildPreviewConfigSchema(build, allowCustomResources) {
|
|
|
3744
3908
|
const key = settings.repo.toLowerCase();
|
|
3745
3909
|
if (seenRepos.has(key)) {
|
|
3746
3910
|
ctx.addIssue({
|
|
3747
|
-
code:
|
|
3911
|
+
code: z16.ZodIssueCode.custom,
|
|
3748
3912
|
path: ["repositories", index, "repo"],
|
|
3749
3913
|
message: `Repository "${settings.repo}" has more than one settings entry`
|
|
3750
3914
|
});
|
|
@@ -3761,11 +3925,12 @@ function standardResources(role) {
|
|
|
3761
3925
|
memoryLimit: standard.memoryLimit
|
|
3762
3926
|
};
|
|
3763
3927
|
}
|
|
3764
|
-
var STANDARD_RESOURCES, k8sNameRegex, repoFullNameRegex, resourcesInput, branchConventionSchema, repositorySettingsSchema, hookStepSchema, hooksSchema, databaseSetupLocationSchema, databaseSetupTaskSchema, nodeVersionRegex, buildContextSchema, PREVIEWKIT_BUILD_SCRIPT_HEREDOC, imageTagSchema, DEPRECATED_BUILD_FRAMEWORKS, UNSUPPORTED_BUILD_METHOD_MESSAGE, deprecatedBuildArms, authoredBuildArms, authoredBuildSchema, storedBuildSchema, presetBlueprintSchema, dockerfileBlueprintSchema, blueprintSchema, connectionSchema, previewConfigSchema, trustedPreviewConfigSchema, authoringPreviewConfigSchema;
|
|
3928
|
+
var STANDARD_RESOURCES, PREVIEW_CONFIG_VERSION, k8sNameRegex, SDK_PATH_PATTERN, repoFullNameRegex, DEFAULT_DEPENDENCY_FALLBACK_BRANCH, resourcesInput, branchConventionSchema, repositorySettingsSchema, hookStepSchema, hooksSchema, databaseSetupLocationSchema, databaseSetupTaskSchema, nodeVersionRegex, buildContextSchema, PREVIEWKIT_BUILD_SCRIPT_HEREDOC, imageTagSchema, DEPRECATED_BUILD_FRAMEWORKS, UNSUPPORTED_BUILD_METHOD_MESSAGE, deprecatedBuildArms, authoredBuildArms, authoredBuildSchema, storedBuildSchema, presetBlueprintSchema, dockerfileBlueprintSchema, blueprintSchema, connectionSchema, previewConfigSchema, trustedPreviewConfigSchema, authoringPreviewConfigSchema, appRoleSchema, sdkPathDocumentSchema;
|
|
3765
3929
|
var init_previewkit_config = __esm({
|
|
3766
3930
|
"../../packages/types/src/schemas/previewkit-config.ts"() {
|
|
3767
3931
|
"use strict";
|
|
3768
3932
|
init_esm_shims();
|
|
3933
|
+
init_sdk_endpoint();
|
|
3769
3934
|
init_previewkit_builtins();
|
|
3770
3935
|
init_previewkit_node_pm();
|
|
3771
3936
|
init_previewkit_presets();
|
|
@@ -3775,19 +3940,22 @@ var init_previewkit_config = __esm({
|
|
|
3775
3940
|
app: { cpu: "250m", memoryRequest: "512Mi", memoryLimit: "1Gi" },
|
|
3776
3941
|
service: { cpu: "100m", memoryRequest: "256Mi", memoryLimit: "1Gi" }
|
|
3777
3942
|
};
|
|
3943
|
+
PREVIEW_CONFIG_VERSION = 2;
|
|
3778
3944
|
k8sNameRegex = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
3945
|
+
SDK_PATH_PATTERN = /^\/[^\s?#]*$/;
|
|
3779
3946
|
repoFullNameRegex = /^[^/\s]+\/[^/\s]+$/;
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3947
|
+
DEFAULT_DEPENDENCY_FALLBACK_BRANCH = "main";
|
|
3948
|
+
resourcesInput = z16.object({
|
|
3949
|
+
cpu: z16.string().optional(),
|
|
3950
|
+
memory: z16.string().optional(),
|
|
3951
|
+
memoryRequest: z16.string().optional(),
|
|
3952
|
+
memoryLimit: z16.string().optional()
|
|
3785
3953
|
}).optional();
|
|
3786
|
-
branchConventionSchema =
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
type:
|
|
3790
|
-
pattern:
|
|
3954
|
+
branchConventionSchema = z16.discriminatedUnion("type", [
|
|
3955
|
+
z16.object({ type: z16.literal("same_branch_name") }),
|
|
3956
|
+
z16.object({
|
|
3957
|
+
type: z16.literal("regex"),
|
|
3958
|
+
pattern: z16.string().refine((pattern) => {
|
|
3791
3959
|
try {
|
|
3792
3960
|
new RegExp(pattern);
|
|
3793
3961
|
return true;
|
|
@@ -3795,13 +3963,13 @@ var init_previewkit_config = __esm({
|
|
|
3795
3963
|
return false;
|
|
3796
3964
|
}
|
|
3797
3965
|
}, "Invalid regular expression pattern"),
|
|
3798
|
-
replacement:
|
|
3966
|
+
replacement: z16.string()
|
|
3799
3967
|
}),
|
|
3800
|
-
|
|
3968
|
+
z16.object({ type: z16.literal("manual") })
|
|
3801
3969
|
]);
|
|
3802
|
-
repositorySettingsSchema =
|
|
3803
|
-
repo:
|
|
3804
|
-
fallback_branch:
|
|
3970
|
+
repositorySettingsSchema = z16.object({
|
|
3971
|
+
repo: z16.string().regex(repoFullNameRegex, "Must be an owner/repo full name"),
|
|
3972
|
+
fallback_branch: z16.string().default(DEFAULT_DEPENDENCY_FALLBACK_BRANCH),
|
|
3805
3973
|
/**
|
|
3806
3974
|
* The concrete commit SHA the repository was deployed at. Absent in
|
|
3807
3975
|
* user-authored config: previewkit resolves each dependency repo's branch to
|
|
@@ -3809,60 +3977,60 @@ var init_previewkit_config = __esm({
|
|
|
3809
3977
|
* `resolvedConfig` (deploy provenance, not authored input). Multi-repo
|
|
3810
3978
|
* grounding reads this back to inspect the exact code that was live.
|
|
3811
3979
|
*/
|
|
3812
|
-
sha:
|
|
3980
|
+
sha: z16.string().optional()
|
|
3813
3981
|
});
|
|
3814
|
-
hookStepSchema =
|
|
3815
|
-
app:
|
|
3816
|
-
command:
|
|
3982
|
+
hookStepSchema = z16.object({
|
|
3983
|
+
app: z16.string(),
|
|
3984
|
+
command: z16.string()
|
|
3817
3985
|
});
|
|
3818
|
-
hooksSchema =
|
|
3819
|
-
pre_deploy:
|
|
3820
|
-
post_deploy:
|
|
3986
|
+
hooksSchema = z16.object({
|
|
3987
|
+
pre_deploy: z16.array(hookStepSchema).default([]),
|
|
3988
|
+
post_deploy: z16.array(hookStepSchema).default([])
|
|
3821
3989
|
}).default({ pre_deploy: [], post_deploy: [] });
|
|
3822
|
-
databaseSetupLocationSchema =
|
|
3823
|
-
|
|
3824
|
-
type:
|
|
3825
|
-
app:
|
|
3826
|
-
position:
|
|
3990
|
+
databaseSetupLocationSchema = z16.discriminatedUnion("type", [
|
|
3991
|
+
z16.object({
|
|
3992
|
+
type: z16.literal("in_build"),
|
|
3993
|
+
app: z16.string().min(1, "an app is required for an in-build task"),
|
|
3994
|
+
position: z16.enum(["before", "after"])
|
|
3827
3995
|
}),
|
|
3828
|
-
|
|
3829
|
-
type:
|
|
3830
|
-
repo:
|
|
3996
|
+
z16.object({
|
|
3997
|
+
type: z16.literal("separate_job"),
|
|
3998
|
+
repo: z16.string().optional()
|
|
3831
3999
|
})
|
|
3832
4000
|
]);
|
|
3833
|
-
databaseSetupTaskSchema =
|
|
3834
|
-
command:
|
|
3835
|
-
frequency:
|
|
4001
|
+
databaseSetupTaskSchema = z16.object({
|
|
4002
|
+
command: z16.string(),
|
|
4003
|
+
frequency: z16.enum(["on_create", "every_commit"]),
|
|
3836
4004
|
location: databaseSetupLocationSchema
|
|
3837
4005
|
});
|
|
3838
4006
|
nodeVersionRegex = /^\d+(\.\d+)?(\.\d+)?$/;
|
|
3839
|
-
buildContextSchema =
|
|
4007
|
+
buildContextSchema = z16.enum(["app", "root"]).default("app");
|
|
3840
4008
|
PREVIEWKIT_BUILD_SCRIPT_HEREDOC = "AUTONOMA_BUILD_EOF";
|
|
3841
|
-
imageTagSchema =
|
|
4009
|
+
imageTagSchema = z16.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "must be a valid image tag");
|
|
3842
4010
|
DEPRECATED_BUILD_FRAMEWORKS = ["node", "next", "vite", "bun"];
|
|
3843
4011
|
UNSUPPORTED_BUILD_METHOD_MESSAGE = `Unsupported build method. Use "runtime" (pick a runtime, then write a bash build_script and a single-line entrypoint) or "dockerfile" (build a Dockerfile committed in the repo). The ${DEPRECATED_BUILD_FRAMEWORKS.join(" / ")} framework presets are retired and can no longer be saved - express them as "runtime" with the install and build commands in build_script and the start command as entrypoint.`;
|
|
3844
4012
|
deprecatedBuildArms = [
|
|
3845
4013
|
nodeFrameworkBuildSchema("node"),
|
|
3846
4014
|
nodeFrameworkBuildSchema("next"),
|
|
3847
4015
|
nodeFrameworkBuildSchema("vite"),
|
|
3848
|
-
|
|
3849
|
-
framework:
|
|
3850
|
-
install_command:
|
|
3851
|
-
build_command:
|
|
3852
|
-
run_command:
|
|
4016
|
+
z16.object({
|
|
4017
|
+
framework: z16.literal("bun"),
|
|
4018
|
+
install_command: z16.string().min(1).optional(),
|
|
4019
|
+
build_command: z16.string().min(1).optional(),
|
|
4020
|
+
run_command: z16.string().min(1).optional(),
|
|
3853
4021
|
build_context: buildContextSchema
|
|
3854
4022
|
})
|
|
3855
4023
|
];
|
|
3856
4024
|
authoredBuildArms = [
|
|
3857
|
-
|
|
3858
|
-
framework:
|
|
3859
|
-
dockerfile:
|
|
3860
|
-
target:
|
|
4025
|
+
z16.object({
|
|
4026
|
+
framework: z16.literal("dockerfile"),
|
|
4027
|
+
dockerfile: z16.string().min(1, "dockerfile path is required"),
|
|
4028
|
+
target: z16.string().min(1).optional(),
|
|
3861
4029
|
build_context: buildContextSchema
|
|
3862
4030
|
}),
|
|
3863
|
-
|
|
3864
|
-
framework:
|
|
3865
|
-
runtime:
|
|
4031
|
+
z16.object({
|
|
4032
|
+
framework: z16.literal("runtime"),
|
|
4033
|
+
runtime: z16.enum(PREVIEWKIT_RUNTIME_IDS),
|
|
3866
4034
|
// Image tag version, e.g. "22" for node. Optional - defaults to the
|
|
3867
4035
|
// catalog's default per runtime. The user picks it so a repo pinned to an
|
|
3868
4036
|
// older toolchain is not forced onto our default (which would defeat the
|
|
@@ -3879,12 +4047,12 @@ var init_previewkit_config = __esm({
|
|
|
3879
4047
|
build_context: buildContextSchema
|
|
3880
4048
|
})
|
|
3881
4049
|
];
|
|
3882
|
-
authoredBuildSchema =
|
|
4050
|
+
authoredBuildSchema = z16.discriminatedUnion("framework", authoredBuildArms, {
|
|
3883
4051
|
error: () => UNSUPPORTED_BUILD_METHOD_MESSAGE
|
|
3884
4052
|
});
|
|
3885
|
-
storedBuildSchema =
|
|
3886
|
-
presetBlueprintSchema =
|
|
3887
|
-
preset:
|
|
4053
|
+
storedBuildSchema = z16.discriminatedUnion("framework", [...authoredBuildArms, ...deprecatedBuildArms]);
|
|
4054
|
+
presetBlueprintSchema = z16.object({
|
|
4055
|
+
preset: z16.enum(PREVIEWKIT_PRESET_IDS),
|
|
3888
4056
|
// Overridable per app; each defaults from the preset (version from the runtime catalog).
|
|
3889
4057
|
version: imageTagSchema.optional(),
|
|
3890
4058
|
// These overrides flow into the generated Dockerfile via blueprintToBuild, so they
|
|
@@ -3904,82 +4072,98 @@ var init_previewkit_config = __esm({
|
|
|
3904
4072
|
const toolchain = previewkitPresetSpec(blueprint.preset).toolchain;
|
|
3905
4073
|
if (toolchain === "node" && blueprint.version != null && !nodeVersionRegex.test(blueprint.version)) {
|
|
3906
4074
|
ctx.addIssue({
|
|
3907
|
-
code:
|
|
4075
|
+
code: z16.ZodIssueCode.custom,
|
|
3908
4076
|
message: "version must look like 22, 22.5, or 22.5.0 for a node preset",
|
|
3909
4077
|
path: ["version"]
|
|
3910
4078
|
});
|
|
3911
4079
|
}
|
|
3912
4080
|
});
|
|
3913
|
-
dockerfileBlueprintSchema =
|
|
4081
|
+
dockerfileBlueprintSchema = z16.object({
|
|
3914
4082
|
// Path to a committed Dockerfile, built as-is - ALWAYS relative to the app dir,
|
|
3915
4083
|
// whatever the build context (the pipeline resolves it against the context).
|
|
3916
4084
|
// `target` selects a stage in a multi-stage build.
|
|
3917
|
-
dockerfile:
|
|
3918
|
-
target:
|
|
4085
|
+
dockerfile: z16.string().min(1, "dockerfile path is required"),
|
|
4086
|
+
target: z16.string().min(1).optional(),
|
|
3919
4087
|
// Monorepo: `root` builds from the repo root (so a workspace-aware Dockerfile can reach
|
|
3920
4088
|
// sibling packages); `app` (absent) builds from the app dir.
|
|
3921
4089
|
build_context: buildContextSchema.removeDefault().optional()
|
|
3922
4090
|
}).strict();
|
|
3923
|
-
blueprintSchema =
|
|
3924
|
-
connectionSchema =
|
|
4091
|
+
blueprintSchema = z16.union([presetBlueprintSchema, dockerfileBlueprintSchema]);
|
|
4092
|
+
connectionSchema = z16.object({
|
|
3925
4093
|
key: SecretKeySchema.superRefine((key, ctx) => {
|
|
3926
4094
|
if (isReservedPreviewkitEnvKey(key)) {
|
|
3927
4095
|
ctx.addIssue({
|
|
3928
|
-
code:
|
|
4096
|
+
code: z16.ZodIssueCode.custom,
|
|
3929
4097
|
message: `${key} is a reserved built-in variable and cannot be set.`
|
|
3930
4098
|
});
|
|
3931
4099
|
}
|
|
3932
4100
|
}).describe("The env var name to inject into the app at runtime (e.g. DATABASE_URL)."),
|
|
3933
|
-
value:
|
|
4101
|
+
value: z16.string().min(1, "value is required").describe(
|
|
3934
4102
|
"Template resolved at deploy time. {{name.property}} tokens reference apps/services in this config by name. For a service: {{db.url}} = the full canonical connection string (postgres -> postgresql://preview:preview@<host>:<port>/preview; redis -> redis://...; mongodb -> mongodb://...), {{db.host}} = in-cluster DNS name, {{db.port}}. For an app: {{api.url}} = its public HTTPS URL, {{api.hostname}}. Also available: {{pr}}, {{namespace}}, {{owner}}. Tokens can mix with literal text ('postgresql://user:pass@{{db.host}}:5432/mydb'), or be a plain literal ('production'). Prefer {{db.url}} to wire a database."
|
|
3935
4103
|
),
|
|
3936
|
-
build_time:
|
|
4104
|
+
build_time: z16.boolean().default(false).describe("Also pass the resolved value as a Docker build arg.")
|
|
3937
4105
|
});
|
|
3938
4106
|
previewConfigSchema = buildPreviewConfigSchema(storedBuildSchema, false);
|
|
3939
4107
|
trustedPreviewConfigSchema = buildPreviewConfigSchema(storedBuildSchema, true);
|
|
3940
4108
|
authoringPreviewConfigSchema = buildPreviewConfigSchema(authoredBuildSchema, false);
|
|
4109
|
+
appRoleSchema = z16.object({
|
|
4110
|
+
name: z16.string(),
|
|
4111
|
+
primary: z16.boolean().nullish(),
|
|
4112
|
+
sdk_implemented: z16.boolean().nullish(),
|
|
4113
|
+
sdk_path: z16.string().nullish()
|
|
4114
|
+
});
|
|
4115
|
+
sdkPathDocumentSchema = z16.object({ apps: z16.array(appRoleSchema) });
|
|
4116
|
+
}
|
|
4117
|
+
});
|
|
4118
|
+
|
|
4119
|
+
// ../../packages/types/src/schemas/previewkit-config-rows.ts
|
|
4120
|
+
var init_previewkit_config_rows = __esm({
|
|
4121
|
+
"../../packages/types/src/schemas/previewkit-config-rows.ts"() {
|
|
4122
|
+
"use strict";
|
|
4123
|
+
init_esm_shims();
|
|
4124
|
+
init_previewkit_config();
|
|
3941
4125
|
}
|
|
3942
4126
|
});
|
|
3943
4127
|
|
|
3944
4128
|
// ../../packages/types/src/schemas/previewkit-job-spec.ts
|
|
3945
|
-
import { z as
|
|
4129
|
+
import { z as z17 } from "zod";
|
|
3946
4130
|
var deployTargetSchema, redeployTargetSchema, teardownTargetSchema, previewJobSpecSchema;
|
|
3947
4131
|
var init_previewkit_job_spec = __esm({
|
|
3948
4132
|
"../../packages/types/src/schemas/previewkit-job-spec.ts"() {
|
|
3949
4133
|
"use strict";
|
|
3950
4134
|
init_esm_shims();
|
|
3951
|
-
deployTargetSchema =
|
|
3952
|
-
repoFullName:
|
|
3953
|
-
prNumber:
|
|
3954
|
-
organizationId:
|
|
3955
|
-
githubRepositoryId:
|
|
3956
|
-
headSha:
|
|
3957
|
-
headRef:
|
|
3958
|
-
branchId:
|
|
4135
|
+
deployTargetSchema = z17.object({
|
|
4136
|
+
repoFullName: z17.string().min(1),
|
|
4137
|
+
prNumber: z17.number().int().nonnegative(),
|
|
4138
|
+
organizationId: z17.string().min(1),
|
|
4139
|
+
githubRepositoryId: z17.number().int(),
|
|
4140
|
+
headSha: z17.string().min(1),
|
|
4141
|
+
headRef: z17.string().min(1),
|
|
4142
|
+
branchId: z17.string().optional()
|
|
3959
4143
|
});
|
|
3960
|
-
redeployTargetSchema =
|
|
3961
|
-
repoFullName:
|
|
3962
|
-
prNumber:
|
|
3963
|
-
organizationId:
|
|
3964
|
-
githubRepositoryId:
|
|
3965
|
-
headSha:
|
|
3966
|
-
headRef:
|
|
4144
|
+
redeployTargetSchema = z17.object({
|
|
4145
|
+
repoFullName: z17.string().min(1),
|
|
4146
|
+
prNumber: z17.number().int().nonnegative(),
|
|
4147
|
+
organizationId: z17.string().min(1),
|
|
4148
|
+
githubRepositoryId: z17.number().int(),
|
|
4149
|
+
headSha: z17.string().min(1),
|
|
4150
|
+
headRef: z17.string().min(1)
|
|
3967
4151
|
});
|
|
3968
|
-
teardownTargetSchema =
|
|
3969
|
-
repoFullName:
|
|
3970
|
-
prNumber:
|
|
3971
|
-
organizationId:
|
|
3972
|
-
headSha:
|
|
4152
|
+
teardownTargetSchema = z17.object({
|
|
4153
|
+
repoFullName: z17.string().min(1),
|
|
4154
|
+
prNumber: z17.number().int().nonnegative(),
|
|
4155
|
+
organizationId: z17.string().min(1),
|
|
4156
|
+
headSha: z17.string().min(1).optional()
|
|
3973
4157
|
});
|
|
3974
|
-
previewJobSpecSchema =
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
mode:
|
|
4158
|
+
previewJobSpecSchema = z17.discriminatedUnion("mode", [
|
|
4159
|
+
z17.object({ mode: z17.literal("deploy"), target: deployTargetSchema }),
|
|
4160
|
+
z17.object({ mode: z17.literal("teardown"), target: teardownTargetSchema }),
|
|
4161
|
+
z17.object({
|
|
4162
|
+
mode: z17.literal("redeploy-app"),
|
|
3979
4163
|
target: redeployTargetSchema,
|
|
3980
|
-
namespace:
|
|
3981
|
-
appName:
|
|
3982
|
-
redeployMode:
|
|
4164
|
+
namespace: z17.string().min(1),
|
|
4165
|
+
appName: z17.string().min(1),
|
|
4166
|
+
redeployMode: z17.enum(["rebuild", "restart"])
|
|
3983
4167
|
})
|
|
3984
4168
|
]);
|
|
3985
4169
|
}
|
|
@@ -4004,29 +4188,29 @@ var init_previewkit_authorable_config = __esm({
|
|
|
4004
4188
|
});
|
|
4005
4189
|
|
|
4006
4190
|
// ../../packages/types/src/schemas/previewkit-redeploy-app.ts
|
|
4007
|
-
import { z as
|
|
4191
|
+
import { z as z18 } from "zod";
|
|
4008
4192
|
var RedeployPreviewkitAppInputSchema;
|
|
4009
4193
|
var init_previewkit_redeploy_app = __esm({
|
|
4010
4194
|
"../../packages/types/src/schemas/previewkit-redeploy-app.ts"() {
|
|
4011
4195
|
"use strict";
|
|
4012
4196
|
init_esm_shims();
|
|
4013
|
-
RedeployPreviewkitAppInputSchema =
|
|
4014
|
-
applicationId:
|
|
4015
|
-
environmentId:
|
|
4016
|
-
app:
|
|
4017
|
-
mode:
|
|
4197
|
+
RedeployPreviewkitAppInputSchema = z18.object({
|
|
4198
|
+
applicationId: z18.string().min(1),
|
|
4199
|
+
environmentId: z18.string().min(1),
|
|
4200
|
+
app: z18.string().min(1),
|
|
4201
|
+
mode: z18.enum(["rebuild", "restart"])
|
|
4018
4202
|
});
|
|
4019
4203
|
}
|
|
4020
4204
|
});
|
|
4021
4205
|
|
|
4022
4206
|
// ../../packages/types/src/schemas/previewkit-service-suggestion.ts
|
|
4023
|
-
import { z as
|
|
4207
|
+
import { z as z19 } from "zod";
|
|
4024
4208
|
var SuggestableServiceRecipeSchema, SuggestionAppRefSchema, SuggestServicesInputSchema, SuggestedServiceSchema, SuggestServicesResultSchema;
|
|
4025
4209
|
var init_previewkit_service_suggestion = __esm({
|
|
4026
4210
|
"../../packages/types/src/schemas/previewkit-service-suggestion.ts"() {
|
|
4027
4211
|
"use strict";
|
|
4028
4212
|
init_esm_shims();
|
|
4029
|
-
SuggestableServiceRecipeSchema =
|
|
4213
|
+
SuggestableServiceRecipeSchema = z19.enum([
|
|
4030
4214
|
"postgres",
|
|
4031
4215
|
"redis",
|
|
4032
4216
|
"valkey",
|
|
@@ -4035,144 +4219,133 @@ var init_previewkit_service_suggestion = __esm({
|
|
|
4035
4219
|
"temporal",
|
|
4036
4220
|
"docker-image"
|
|
4037
4221
|
]);
|
|
4038
|
-
SuggestionAppRefSchema =
|
|
4039
|
-
name:
|
|
4040
|
-
path:
|
|
4041
|
-
primary:
|
|
4222
|
+
SuggestionAppRefSchema = z19.object({
|
|
4223
|
+
name: z19.string(),
|
|
4224
|
+
path: z19.string(),
|
|
4225
|
+
primary: z19.boolean().optional()
|
|
4042
4226
|
});
|
|
4043
|
-
SuggestServicesInputSchema =
|
|
4044
|
-
applicationId:
|
|
4045
|
-
githubRepositoryId:
|
|
4046
|
-
apps:
|
|
4227
|
+
SuggestServicesInputSchema = z19.object({
|
|
4228
|
+
applicationId: z19.string(),
|
|
4229
|
+
githubRepositoryId: z19.number().int().positive().optional(),
|
|
4230
|
+
apps: z19.array(SuggestionAppRefSchema)
|
|
4047
4231
|
});
|
|
4048
|
-
SuggestedServiceSchema =
|
|
4232
|
+
SuggestedServiceSchema = z19.object({
|
|
4049
4233
|
recipe: SuggestableServiceRecipeSchema,
|
|
4050
|
-
name:
|
|
4051
|
-
version:
|
|
4052
|
-
confidence:
|
|
4053
|
-
evidence:
|
|
4234
|
+
name: z19.string(),
|
|
4235
|
+
version: z19.string().optional(),
|
|
4236
|
+
confidence: z19.enum(["high", "medium", "low"]),
|
|
4237
|
+
evidence: z19.array(z19.string())
|
|
4054
4238
|
});
|
|
4055
|
-
SuggestServicesResultSchema =
|
|
4056
|
-
status:
|
|
4057
|
-
reason:
|
|
4058
|
-
services:
|
|
4239
|
+
SuggestServicesResultSchema = z19.object({
|
|
4240
|
+
status: z19.enum(["ok", "unavailable"]),
|
|
4241
|
+
reason: z19.string().optional(),
|
|
4242
|
+
services: z19.array(SuggestedServiceSchema)
|
|
4059
4243
|
});
|
|
4060
4244
|
}
|
|
4061
4245
|
});
|
|
4062
4246
|
|
|
4063
4247
|
// ../../packages/types/src/schemas/previewkit-env-suggestion.ts
|
|
4064
|
-
import { z as
|
|
4248
|
+
import { z as z20 } from "zod";
|
|
4065
4249
|
var SuggestionServiceRefSchema, SuggestEnvVarsInputSchema, SuggestedEnvVarSchema, SuggestedEnvGroupSchema, SuggestEnvVarsResultSchema;
|
|
4066
4250
|
var init_previewkit_env_suggestion = __esm({
|
|
4067
4251
|
"../../packages/types/src/schemas/previewkit-env-suggestion.ts"() {
|
|
4068
4252
|
"use strict";
|
|
4069
4253
|
init_esm_shims();
|
|
4070
4254
|
init_previewkit_service_suggestion();
|
|
4071
|
-
SuggestionServiceRefSchema =
|
|
4072
|
-
name:
|
|
4255
|
+
SuggestionServiceRefSchema = z20.object({
|
|
4256
|
+
name: z20.string(),
|
|
4073
4257
|
recipe: SuggestableServiceRecipeSchema
|
|
4074
4258
|
});
|
|
4075
|
-
SuggestEnvVarsInputSchema =
|
|
4076
|
-
applicationId:
|
|
4077
|
-
githubRepositoryId:
|
|
4078
|
-
apps:
|
|
4079
|
-
services:
|
|
4259
|
+
SuggestEnvVarsInputSchema = z20.object({
|
|
4260
|
+
applicationId: z20.string(),
|
|
4261
|
+
githubRepositoryId: z20.number().int().positive().optional(),
|
|
4262
|
+
apps: z20.array(SuggestionAppRefSchema),
|
|
4263
|
+
services: z20.array(SuggestionServiceRefSchema)
|
|
4080
4264
|
});
|
|
4081
|
-
SuggestedEnvVarSchema =
|
|
4082
|
-
key:
|
|
4083
|
-
value:
|
|
4084
|
-
reference:
|
|
4085
|
-
sensitive:
|
|
4265
|
+
SuggestedEnvVarSchema = z20.object({
|
|
4266
|
+
key: z20.string(),
|
|
4267
|
+
value: z20.string().optional(),
|
|
4268
|
+
reference: z20.string().optional(),
|
|
4269
|
+
sensitive: z20.boolean(),
|
|
4086
4270
|
/**
|
|
4087
4271
|
* Whether the value must be present during the image BUILD (not just at
|
|
4088
4272
|
* runtime) - e.g. framework vars inlined into the client bundle
|
|
4089
4273
|
* (`NEXT_PUBLIC_*`, `VITE_*`) or values read by the build/install command.
|
|
4090
4274
|
* Maps to the row's "also inject at build time" switch. Omitted = runtime-only.
|
|
4091
4275
|
*/
|
|
4092
|
-
build_time:
|
|
4093
|
-
description:
|
|
4094
|
-
confidence:
|
|
4095
|
-
evidence:
|
|
4276
|
+
build_time: z20.boolean().optional(),
|
|
4277
|
+
description: z20.string().optional(),
|
|
4278
|
+
confidence: z20.enum(["high", "medium", "low"]),
|
|
4279
|
+
evidence: z20.array(z20.string())
|
|
4096
4280
|
});
|
|
4097
|
-
SuggestedEnvGroupSchema =
|
|
4098
|
-
name:
|
|
4099
|
-
vars:
|
|
4281
|
+
SuggestedEnvGroupSchema = z20.object({
|
|
4282
|
+
name: z20.string(),
|
|
4283
|
+
vars: z20.array(SuggestedEnvVarSchema)
|
|
4100
4284
|
});
|
|
4101
|
-
SuggestEnvVarsResultSchema =
|
|
4102
|
-
status:
|
|
4103
|
-
reason:
|
|
4104
|
-
apps:
|
|
4105
|
-
services:
|
|
4285
|
+
SuggestEnvVarsResultSchema = z20.object({
|
|
4286
|
+
status: z20.enum(["ok", "unavailable"]),
|
|
4287
|
+
reason: z20.string().optional(),
|
|
4288
|
+
apps: z20.array(SuggestedEnvGroupSchema),
|
|
4289
|
+
services: z20.array(SuggestedEnvGroupSchema)
|
|
4106
4290
|
});
|
|
4107
4291
|
}
|
|
4108
4292
|
});
|
|
4109
4293
|
|
|
4110
4294
|
// ../../packages/types/src/schemas/previewkit-diagnosis.ts
|
|
4111
|
-
import { z as
|
|
4112
|
-
var DiagnosePreviewkitDeployInputSchema, PreviewDiagnosisCategorySchema, PreviewDiagnosisActionSchema, PreviewDiagnosisFindingSchema
|
|
4295
|
+
import { z as z21 } from "zod";
|
|
4296
|
+
var DiagnosePreviewkitDeployInputSchema, PreviewDiagnosisCategorySchema, PreviewDiagnosisActionSchema, PreviewDiagnosisFindingSchema;
|
|
4113
4297
|
var init_previewkit_diagnosis = __esm({
|
|
4114
4298
|
"../../packages/types/src/schemas/previewkit-diagnosis.ts"() {
|
|
4115
4299
|
"use strict";
|
|
4116
4300
|
init_esm_shims();
|
|
4117
4301
|
init_previewkit_env_suggestion();
|
|
4118
|
-
DiagnosePreviewkitDeployInputSchema =
|
|
4119
|
-
applicationId:
|
|
4302
|
+
DiagnosePreviewkitDeployInputSchema = z21.object({
|
|
4303
|
+
applicationId: z21.string(),
|
|
4120
4304
|
/**
|
|
4121
4305
|
* Which preview environment to diagnose. Omitted defaults (in the service) to
|
|
4122
|
-
* 0 - the main-branch
|
|
4123
|
-
* is unchanged; MCP / per-PR callers pass the real PR number.
|
|
4306
|
+
* 0 - the main-branch preview; per-PR callers pass the real PR number.
|
|
4124
4307
|
*/
|
|
4125
|
-
prNumber:
|
|
4126
|
-
deployFingerprint:
|
|
4308
|
+
prNumber: z21.number().int().min(0).optional(),
|
|
4309
|
+
deployFingerprint: z21.string().optional()
|
|
4127
4310
|
});
|
|
4128
|
-
PreviewDiagnosisCategorySchema =
|
|
4129
|
-
PreviewDiagnosisActionSchema =
|
|
4130
|
-
PreviewDiagnosisFindingSchema =
|
|
4311
|
+
PreviewDiagnosisCategorySchema = z21.enum(["missing_env_var", "user_setup", "autonoma_error", "unknown"]);
|
|
4312
|
+
PreviewDiagnosisActionSchema = z21.enum(["edit_config", "edit_secrets", "redeploy", "contact_support"]);
|
|
4313
|
+
PreviewDiagnosisFindingSchema = z21.object({
|
|
4131
4314
|
category: PreviewDiagnosisCategorySchema,
|
|
4132
|
-
severity:
|
|
4133
|
-
title:
|
|
4134
|
-
explanation:
|
|
4135
|
-
fixSteps:
|
|
4136
|
-
appName:
|
|
4137
|
-
fieldPath:
|
|
4315
|
+
severity: z21.enum(["blocking", "warning", "info"]),
|
|
4316
|
+
title: z21.string(),
|
|
4317
|
+
explanation: z21.string(),
|
|
4318
|
+
fixSteps: z21.array(z21.string()),
|
|
4319
|
+
appName: z21.string().optional(),
|
|
4320
|
+
fieldPath: z21.string().optional(),
|
|
4138
4321
|
action: PreviewDiagnosisActionSchema.optional(),
|
|
4139
|
-
suggestedEnv:
|
|
4140
|
-
confidence:
|
|
4141
|
-
evidence:
|
|
4142
|
-
});
|
|
4143
|
-
DiagnosePreviewkitDeployResultSchema = z20.object({
|
|
4144
|
-
status: z20.enum(["ok", "unavailable"]),
|
|
4145
|
-
reason: z20.string().optional(),
|
|
4146
|
-
summary: z20.string().optional(),
|
|
4147
|
-
findings: z20.array(PreviewDiagnosisFindingSchema)
|
|
4148
|
-
});
|
|
4149
|
-
AiDiagnosisResultSchema = z20.object({
|
|
4150
|
-
summary: z20.string(),
|
|
4151
|
-
findings: z20.array(PreviewDiagnosisFindingSchema)
|
|
4322
|
+
suggestedEnv: z21.array(SuggestedEnvVarSchema).optional(),
|
|
4323
|
+
confidence: z21.enum(["high", "medium", "low"]),
|
|
4324
|
+
evidence: z21.array(z21.string())
|
|
4152
4325
|
});
|
|
4153
4326
|
}
|
|
4154
4327
|
});
|
|
4155
4328
|
|
|
4156
4329
|
// ../../packages/types/src/schemas/snapshot-dependency-pin.ts
|
|
4157
|
-
import { z as
|
|
4330
|
+
import { z as z22 } from "zod";
|
|
4158
4331
|
var snapshotDependencyShaMapSchema;
|
|
4159
4332
|
var init_snapshot_dependency_pin = __esm({
|
|
4160
4333
|
"../../packages/types/src/schemas/snapshot-dependency-pin.ts"() {
|
|
4161
4334
|
"use strict";
|
|
4162
4335
|
init_esm_shims();
|
|
4163
|
-
snapshotDependencyShaMapSchema =
|
|
4336
|
+
snapshotDependencyShaMapSchema = z22.record(z22.string(), z22.string());
|
|
4164
4337
|
}
|
|
4165
4338
|
});
|
|
4166
4339
|
|
|
4167
4340
|
// ../../packages/types/src/schemas/checkpoint-summary.ts
|
|
4168
|
-
import { z as
|
|
4341
|
+
import { z as z23 } from "zod";
|
|
4169
4342
|
var checkpointToneSchema, checkpointExecutionStateSchema, checkpointTestCountsSchema, checkpointAnalysisSummarySchema, checkpointPresentationSummarySchema;
|
|
4170
4343
|
var init_checkpoint_summary = __esm({
|
|
4171
4344
|
"../../packages/types/src/schemas/checkpoint-summary.ts"() {
|
|
4172
4345
|
"use strict";
|
|
4173
4346
|
init_esm_shims();
|
|
4174
|
-
checkpointToneSchema =
|
|
4175
|
-
checkpointExecutionStateSchema =
|
|
4347
|
+
checkpointToneSchema = z23.enum(["success", "critical", "warning", "neutral"]);
|
|
4348
|
+
checkpointExecutionStateSchema = z23.enum([
|
|
4176
4349
|
"not_started",
|
|
4177
4350
|
"running",
|
|
4178
4351
|
"stale",
|
|
@@ -4181,34 +4354,34 @@ var init_checkpoint_summary = __esm({
|
|
|
4181
4354
|
"pipeline_failed",
|
|
4182
4355
|
"unknown"
|
|
4183
4356
|
]);
|
|
4184
|
-
checkpointTestCountsSchema =
|
|
4185
|
-
assigned:
|
|
4186
|
-
run:
|
|
4187
|
-
passed:
|
|
4188
|
-
failed:
|
|
4357
|
+
checkpointTestCountsSchema = z23.object({
|
|
4358
|
+
assigned: z23.number(),
|
|
4359
|
+
run: z23.number(),
|
|
4360
|
+
passed: z23.number(),
|
|
4361
|
+
failed: z23.number(),
|
|
4189
4362
|
// Tests that never ran because their scenario setup failed.
|
|
4190
|
-
setupFailed:
|
|
4191
|
-
running:
|
|
4192
|
-
notRun:
|
|
4363
|
+
setupFailed: z23.number(),
|
|
4364
|
+
running: z23.number(),
|
|
4365
|
+
notRun: z23.number()
|
|
4193
4366
|
});
|
|
4194
|
-
checkpointAnalysisSummarySchema =
|
|
4367
|
+
checkpointAnalysisSummarySchema = z23.object({
|
|
4195
4368
|
// The AnalysisJob lifecycle. Mirrors the `AnalysisJobStatus` db enum (types cannot import it).
|
|
4196
|
-
jobStatus:
|
|
4369
|
+
jobStatus: z23.enum(["running", "completed", "failed"]),
|
|
4197
4370
|
// Client-bug findings - the only plane that counts against the PR (turns the checkpoint red).
|
|
4198
|
-
bugCount:
|
|
4371
|
+
bugCount: z23.number().int().nonnegative(),
|
|
4199
4372
|
// Findings that passed on the app-health plane.
|
|
4200
|
-
passedCount:
|
|
4373
|
+
passedCount: z23.number().int().nonnegative(),
|
|
4201
4374
|
// Coverage-plane findings (engine_artifact / environment_failure / scenario_issue / delete) - never a
|
|
4202
4375
|
// failure; surfaced as "couldn't confirm".
|
|
4203
|
-
coverageCount:
|
|
4376
|
+
coverageCount: z23.number().int().nonnegative()
|
|
4204
4377
|
});
|
|
4205
|
-
checkpointPresentationSummarySchema =
|
|
4378
|
+
checkpointPresentationSummarySchema = z23.object({
|
|
4206
4379
|
tone: checkpointToneSchema,
|
|
4207
|
-
label:
|
|
4208
|
-
reason:
|
|
4380
|
+
label: z23.string(),
|
|
4381
|
+
reason: z23.string().optional(),
|
|
4209
4382
|
executionState: checkpointExecutionStateSchema,
|
|
4210
4383
|
testCounts: checkpointTestCountsSchema,
|
|
4211
|
-
suiteChangeCount:
|
|
4384
|
+
suiteChangeCount: z23.number(),
|
|
4212
4385
|
// Set only for authoritative-analysis snapshots; absent for legacy diffs/shadow snapshots.
|
|
4213
4386
|
analysis: checkpointAnalysisSummarySchema.optional()
|
|
4214
4387
|
});
|
|
@@ -4216,65 +4389,65 @@ var init_checkpoint_summary = __esm({
|
|
|
4216
4389
|
});
|
|
4217
4390
|
|
|
4218
4391
|
// ../../packages/types/src/schemas/snapshot-report.ts
|
|
4219
|
-
import { z as
|
|
4392
|
+
import { z as z24 } from "zod";
|
|
4220
4393
|
var reportHealthSchema, reportTestStatusSchema, reportCommitFileSchema, snapshotReportTriggerSchema, snapshotReportTestResultSchema, snapshotReportResultsSchema, snapshotReportHealthCountsSchema, snapshotReportSchema;
|
|
4221
4394
|
var init_snapshot_report = __esm({
|
|
4222
4395
|
"../../packages/types/src/schemas/snapshot-report.ts"() {
|
|
4223
4396
|
"use strict";
|
|
4224
4397
|
init_esm_shims();
|
|
4225
4398
|
init_checkpoint_summary();
|
|
4226
|
-
reportHealthSchema =
|
|
4227
|
-
reportTestStatusSchema =
|
|
4228
|
-
reportCommitFileSchema =
|
|
4229
|
-
filename:
|
|
4230
|
-
status:
|
|
4231
|
-
additions:
|
|
4232
|
-
deletions:
|
|
4399
|
+
reportHealthSchema = z24.enum(["healthy", "critical", "running", "unknown"]);
|
|
4400
|
+
reportTestStatusSchema = z24.enum(["passed", "failed", "setup_failed", "running", "pending"]);
|
|
4401
|
+
reportCommitFileSchema = z24.object({
|
|
4402
|
+
filename: z24.string(),
|
|
4403
|
+
status: z24.string(),
|
|
4404
|
+
additions: z24.number(),
|
|
4405
|
+
deletions: z24.number()
|
|
4233
4406
|
});
|
|
4234
|
-
snapshotReportTriggerSchema =
|
|
4235
|
-
headSha:
|
|
4236
|
-
baseSha:
|
|
4237
|
-
source:
|
|
4238
|
-
createdAt:
|
|
4239
|
-
commit:
|
|
4240
|
-
filesChanged:
|
|
4241
|
-
filesChangedTruncated:
|
|
4407
|
+
snapshotReportTriggerSchema = z24.object({
|
|
4408
|
+
headSha: z24.string().optional(),
|
|
4409
|
+
baseSha: z24.string().optional(),
|
|
4410
|
+
source: z24.string(),
|
|
4411
|
+
createdAt: z24.date(),
|
|
4412
|
+
commit: z24.object({ message: z24.string(), authorLogin: z24.string().optional() }).optional(),
|
|
4413
|
+
filesChanged: z24.array(reportCommitFileSchema),
|
|
4414
|
+
filesChangedTruncated: z24.boolean()
|
|
4242
4415
|
});
|
|
4243
|
-
snapshotReportTestResultSchema =
|
|
4244
|
-
testCaseId:
|
|
4245
|
-
name:
|
|
4246
|
-
slug:
|
|
4416
|
+
snapshotReportTestResultSchema = z24.object({
|
|
4417
|
+
testCaseId: z24.string(),
|
|
4418
|
+
name: z24.string(),
|
|
4419
|
+
slug: z24.string(),
|
|
4247
4420
|
status: reportTestStatusSchema,
|
|
4248
|
-
runId:
|
|
4249
|
-
durationMs:
|
|
4421
|
+
runId: z24.string().optional(),
|
|
4422
|
+
durationMs: z24.number().optional()
|
|
4250
4423
|
});
|
|
4251
|
-
snapshotReportResultsSchema =
|
|
4252
|
-
durationMs:
|
|
4253
|
-
passed:
|
|
4254
|
-
failed:
|
|
4255
|
-
setupFailed:
|
|
4256
|
-
pending:
|
|
4257
|
-
running:
|
|
4258
|
-
total:
|
|
4259
|
-
tests:
|
|
4424
|
+
snapshotReportResultsSchema = z24.object({
|
|
4425
|
+
durationMs: z24.number().optional(),
|
|
4426
|
+
passed: z24.number(),
|
|
4427
|
+
failed: z24.number(),
|
|
4428
|
+
setupFailed: z24.number(),
|
|
4429
|
+
pending: z24.number(),
|
|
4430
|
+
running: z24.number(),
|
|
4431
|
+
total: z24.number(),
|
|
4432
|
+
tests: z24.array(snapshotReportTestResultSchema)
|
|
4260
4433
|
});
|
|
4261
|
-
snapshotReportHealthCountsSchema =
|
|
4262
|
-
failing:
|
|
4263
|
-
passing:
|
|
4264
|
-
running:
|
|
4265
|
-
setupFailed:
|
|
4266
|
-
notAffected:
|
|
4267
|
-
totalTests:
|
|
4434
|
+
snapshotReportHealthCountsSchema = z24.object({
|
|
4435
|
+
failing: z24.number(),
|
|
4436
|
+
passing: z24.number(),
|
|
4437
|
+
running: z24.number(),
|
|
4438
|
+
setupFailed: z24.number(),
|
|
4439
|
+
notAffected: z24.number(),
|
|
4440
|
+
totalTests: z24.number()
|
|
4268
4441
|
});
|
|
4269
|
-
snapshotReportSchema =
|
|
4270
|
-
snapshot:
|
|
4271
|
-
id:
|
|
4272
|
-
status:
|
|
4273
|
-
source:
|
|
4274
|
-
headSha:
|
|
4275
|
-
baseSha:
|
|
4276
|
-
createdAt:
|
|
4277
|
-
branch:
|
|
4442
|
+
snapshotReportSchema = z24.object({
|
|
4443
|
+
snapshot: z24.object({
|
|
4444
|
+
id: z24.string(),
|
|
4445
|
+
status: z24.string(),
|
|
4446
|
+
source: z24.string(),
|
|
4447
|
+
headSha: z24.string().optional(),
|
|
4448
|
+
baseSha: z24.string().optional(),
|
|
4449
|
+
createdAt: z24.date(),
|
|
4450
|
+
branch: z24.object({ id: z24.string(), name: z24.string(), prNumber: z24.number().optional() })
|
|
4278
4451
|
}),
|
|
4279
4452
|
trigger: snapshotReportTriggerSchema,
|
|
4280
4453
|
results: snapshotReportResultsSchema,
|
|
@@ -4286,35 +4459,35 @@ var init_snapshot_report = __esm({
|
|
|
4286
4459
|
});
|
|
4287
4460
|
|
|
4288
4461
|
// ../../packages/types/src/schemas/pr-pipeline-status.ts
|
|
4289
|
-
import { z as
|
|
4462
|
+
import { z as z25 } from "zod";
|
|
4290
4463
|
var prPipelineStatusSchema;
|
|
4291
4464
|
var init_pr_pipeline_status = __esm({
|
|
4292
4465
|
"../../packages/types/src/schemas/pr-pipeline-status.ts"() {
|
|
4293
4466
|
"use strict";
|
|
4294
4467
|
init_esm_shims();
|
|
4295
4468
|
init_checkpoint_summary();
|
|
4296
|
-
prPipelineStatusSchema =
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4469
|
+
prPipelineStatusSchema = z25.discriminatedUnion("kind", [
|
|
4470
|
+
z25.object({ kind: z25.literal("checkpoint"), summary: checkpointPresentationSummarySchema }),
|
|
4471
|
+
z25.object({ kind: z25.literal("building") }),
|
|
4472
|
+
z25.object({ kind: z25.literal("pending_checks") }),
|
|
4473
|
+
z25.object({ kind: z25.literal("analyzing") }),
|
|
4474
|
+
z25.object({ kind: z25.literal("analysis_failed") }),
|
|
4475
|
+
z25.object({ kind: z25.literal("build_failed") }),
|
|
4476
|
+
z25.object({ kind: z25.literal("none") })
|
|
4304
4477
|
]);
|
|
4305
4478
|
}
|
|
4306
4479
|
});
|
|
4307
4480
|
|
|
4308
4481
|
// ../../packages/types/src/schemas/step-output.ts
|
|
4309
|
-
import { z as
|
|
4482
|
+
import { z as z26 } from "zod";
|
|
4310
4483
|
var pointSchema2, stepOutputDataSchema;
|
|
4311
4484
|
var init_step_output = __esm({
|
|
4312
4485
|
"../../packages/types/src/schemas/step-output.ts"() {
|
|
4313
4486
|
"use strict";
|
|
4314
4487
|
init_esm_shims();
|
|
4315
|
-
pointSchema2 =
|
|
4316
|
-
stepOutputDataSchema =
|
|
4317
|
-
outcome:
|
|
4488
|
+
pointSchema2 = z26.object({ x: z26.number(), y: z26.number() });
|
|
4489
|
+
stepOutputDataSchema = z26.object({
|
|
4490
|
+
outcome: z26.string().optional(),
|
|
4318
4491
|
point: pointSchema2.optional(),
|
|
4319
4492
|
startPoint: pointSchema2.optional(),
|
|
4320
4493
|
endPoint: pointSchema2.optional()
|
|
@@ -4323,7 +4496,7 @@ var init_step_output = __esm({
|
|
|
4323
4496
|
});
|
|
4324
4497
|
|
|
4325
4498
|
// ../../packages/types/src/schemas/activation-triggers.ts
|
|
4326
|
-
import { z as
|
|
4499
|
+
import { z as z27 } from "zod";
|
|
4327
4500
|
var MAX_LABEL_LENGTH, CONTROL_CHARS, AnalysisTriggerLabelSchema, TriggerConfigSchema;
|
|
4328
4501
|
var init_activation_triggers = __esm({
|
|
4329
4502
|
"../../packages/types/src/schemas/activation-triggers.ts"() {
|
|
@@ -4331,10 +4504,10 @@ var init_activation_triggers = __esm({
|
|
|
4331
4504
|
init_esm_shims();
|
|
4332
4505
|
MAX_LABEL_LENGTH = 50;
|
|
4333
4506
|
CONTROL_CHARS = /[\u0000-\u001F\u007F]/;
|
|
4334
|
-
AnalysisTriggerLabelSchema =
|
|
4335
|
-
TriggerConfigSchema =
|
|
4507
|
+
AnalysisTriggerLabelSchema = z27.string().trim().min(1, "Label cannot be empty").max(MAX_LABEL_LENGTH, `Label must be at most ${MAX_LABEL_LENGTH} characters`).refine((value) => !CONTROL_CHARS.test(value), "Label cannot contain control characters");
|
|
4508
|
+
TriggerConfigSchema = z27.object({
|
|
4336
4509
|
/** Whether marking a PR ready-for-review automatically starts an analysis run. */
|
|
4337
|
-
autoRunOnReadyForReview:
|
|
4510
|
+
autoRunOnReadyForReview: z27.boolean(),
|
|
4338
4511
|
/** The PR label whose addition starts an analysis run. */
|
|
4339
4512
|
analysisTriggerLabel: AnalysisTriggerLabelSchema
|
|
4340
4513
|
});
|
|
@@ -4342,67 +4515,67 @@ var init_activation_triggers = __esm({
|
|
|
4342
4515
|
});
|
|
4343
4516
|
|
|
4344
4517
|
// ../../packages/types/src/schemas/suite-health.ts
|
|
4345
|
-
import { z as
|
|
4518
|
+
import { z as z28 } from "zod";
|
|
4346
4519
|
var SUITE_HEALTH_LEVELS, suiteHealthLevelSchema, suiteHealthDriverSchema, suiteHealthGateSchema, suiteHealthEvidenceSchema, suiteHealthBreakdownSchema, suiteHealthSchema;
|
|
4347
4520
|
var init_suite_health = __esm({
|
|
4348
4521
|
"../../packages/types/src/schemas/suite-health.ts"() {
|
|
4349
4522
|
"use strict";
|
|
4350
4523
|
init_esm_shims();
|
|
4351
4524
|
SUITE_HEALTH_LEVELS = ["degraded", "at_risk", "calibrating", "steady", "proven"];
|
|
4352
|
-
suiteHealthLevelSchema =
|
|
4353
|
-
suiteHealthDriverSchema =
|
|
4354
|
-
suiteHealthGateSchema =
|
|
4355
|
-
suiteHealthEvidenceSchema =
|
|
4525
|
+
suiteHealthLevelSchema = z28.enum(SUITE_HEALTH_LEVELS);
|
|
4526
|
+
suiteHealthDriverSchema = z28.enum(["environment", "scenario", "plan", "engine", "balanced", "none"]);
|
|
4527
|
+
suiteHealthGateSchema = z28.enum(["runs", "pull_requests", "age", "stale_issues", "no_triage"]);
|
|
4528
|
+
suiteHealthEvidenceSchema = z28.object({
|
|
4356
4529
|
/** Analysis runs in the window. A run that selected no tests is not one. */
|
|
4357
|
-
runs:
|
|
4530
|
+
runs: z28.number().int(),
|
|
4358
4531
|
/** Distinct branches those runs covered. */
|
|
4359
|
-
pullRequests:
|
|
4532
|
+
pullRequests: z28.number().int(),
|
|
4360
4533
|
/** Tests the agent re-planned and re-ran, that then passed. */
|
|
4361
|
-
selfHeals:
|
|
4534
|
+
selfHeals: z28.number().int(),
|
|
4362
4535
|
/** Tests the agent re-planned and re-ran at all - the denominator of the self-heal rate. */
|
|
4363
|
-
selfHealAttempts:
|
|
4536
|
+
selfHealAttempts: z28.number().int(),
|
|
4364
4537
|
/** Total findings in the window - the denominator of the trust rate. */
|
|
4365
|
-
findings:
|
|
4538
|
+
findings: z28.number().int(),
|
|
4366
4539
|
/** Whole days since the app's first analysis run of any pipeline. */
|
|
4367
|
-
ageDays:
|
|
4540
|
+
ageDays: z28.number().int(),
|
|
4368
4541
|
/** Whole days since its most recent run. Drives the inactivity decay. */
|
|
4369
|
-
daysSinceLastRun:
|
|
4542
|
+
daysSinceLastRun: z28.number().int()
|
|
4370
4543
|
});
|
|
4371
|
-
suiteHealthBreakdownSchema =
|
|
4372
|
-
passed:
|
|
4373
|
-
clientBug:
|
|
4374
|
-
environmentFailure:
|
|
4375
|
-
scenarioIssue:
|
|
4376
|
-
planMismatch:
|
|
4377
|
-
engineArtifact:
|
|
4378
|
-
invalidTest:
|
|
4544
|
+
suiteHealthBreakdownSchema = z28.object({
|
|
4545
|
+
passed: z28.number().int(),
|
|
4546
|
+
clientBug: z28.number().int(),
|
|
4547
|
+
environmentFailure: z28.number().int(),
|
|
4548
|
+
scenarioIssue: z28.number().int(),
|
|
4549
|
+
planMismatch: z28.number().int(),
|
|
4550
|
+
engineArtifact: z28.number().int(),
|
|
4551
|
+
invalidTest: z28.number().int()
|
|
4379
4552
|
});
|
|
4380
|
-
suiteHealthSchema =
|
|
4553
|
+
suiteHealthSchema = z28.object({
|
|
4381
4554
|
level: suiteHealthLevelSchema,
|
|
4382
4555
|
/** 1-5. Always `suiteHealthRank(level)`; sent so clients never re-derive it. */
|
|
4383
|
-
rank:
|
|
4556
|
+
rank: z28.number().int(),
|
|
4384
4557
|
/** 0-100, after modifiers. Display-only - the level is what surfaces read. */
|
|
4385
|
-
score:
|
|
4558
|
+
score: z28.number(),
|
|
4386
4559
|
/**
|
|
4387
4560
|
* `(passed + client_bug) / findings`, 0-100, before modifiers. The headline fact: of the tests the agent
|
|
4388
4561
|
* investigated, how many produced a verdict you can act on.
|
|
4389
4562
|
*/
|
|
4390
|
-
trust:
|
|
4563
|
+
trust: z28.number(),
|
|
4391
4564
|
evidence: suiteHealthEvidenceSchema,
|
|
4392
4565
|
breakdown: suiteHealthBreakdownSchema,
|
|
4393
4566
|
driver: suiteHealthDriverSchema,
|
|
4394
4567
|
/** Open issues older than a week on a branch that is still live. What DEGRADED is actually made of. */
|
|
4395
|
-
staleIssues:
|
|
4568
|
+
staleIssues: z28.number().int(),
|
|
4396
4569
|
/** The gate holding the level down, if the score alone would have placed it higher. */
|
|
4397
4570
|
gatedBy: suiteHealthGateSchema.optional(),
|
|
4398
4571
|
/** False before the first analysis run ever - the meter shows "waiting for your first pull request". */
|
|
4399
|
-
hasEverRun:
|
|
4572
|
+
hasEverRun: z28.boolean()
|
|
4400
4573
|
});
|
|
4401
4574
|
}
|
|
4402
4575
|
});
|
|
4403
4576
|
|
|
4404
4577
|
// ../../packages/types/src/schemas/suite-health-fix-plan.ts
|
|
4405
|
-
import { z as
|
|
4578
|
+
import { z as z29 } from "zod";
|
|
4406
4579
|
var suiteHealthFixKindSchema, suiteHealthFixSeveritySchema, suiteHealthFixIssueSchema, suiteHealthFixBranchStateSchema, suiteHealthFixBranchSchema, suiteHealthFixClusterSchema, suiteHealthFixPlanSchema;
|
|
4407
4580
|
var init_suite_health_fix_plan = __esm({
|
|
4408
4581
|
"../../packages/types/src/schemas/suite-health-fix-plan.ts"() {
|
|
@@ -4411,70 +4584,70 @@ var init_suite_health_fix_plan = __esm({
|
|
|
4411
4584
|
init_analysis();
|
|
4412
4585
|
suiteHealthFixKindSchema = analysisIssueKindSchema;
|
|
4413
4586
|
suiteHealthFixSeveritySchema = analysisIssueSeveritySchema;
|
|
4414
|
-
suiteHealthFixIssueSchema =
|
|
4415
|
-
id:
|
|
4587
|
+
suiteHealthFixIssueSchema = z29.object({
|
|
4588
|
+
id: z29.string(),
|
|
4416
4589
|
kind: suiteHealthFixKindSchema,
|
|
4417
4590
|
severity: suiteHealthFixSeveritySchema,
|
|
4418
|
-
title:
|
|
4419
|
-
ageDays:
|
|
4591
|
+
title: z29.string(),
|
|
4592
|
+
ageDays: z29.number().int()
|
|
4420
4593
|
});
|
|
4421
|
-
suiteHealthFixBranchStateSchema =
|
|
4422
|
-
suiteHealthFixBranchSchema =
|
|
4423
|
-
branchId:
|
|
4424
|
-
branchName:
|
|
4594
|
+
suiteHealthFixBranchStateSchema = z29.enum(["open", "merged", "closed", "main"]);
|
|
4595
|
+
suiteHealthFixBranchSchema = z29.object({
|
|
4596
|
+
branchId: z29.string(),
|
|
4597
|
+
branchName: z29.string(),
|
|
4425
4598
|
state: suiteHealthFixBranchStateSchema,
|
|
4426
|
-
prNumber:
|
|
4427
|
-
prTitle:
|
|
4428
|
-
prUrl:
|
|
4599
|
+
prNumber: z29.number().int().optional(),
|
|
4600
|
+
prTitle: z29.string().optional(),
|
|
4601
|
+
prUrl: z29.string().optional(),
|
|
4429
4602
|
/** The findings shown. Capped - `issueCount` is the true total. */
|
|
4430
|
-
issues:
|
|
4431
|
-
issueCount:
|
|
4603
|
+
issues: z29.array(suiteHealthFixIssueSchema),
|
|
4604
|
+
issueCount: z29.number().int(),
|
|
4432
4605
|
/** Kind tally over EVERY finding on the branch, not just the shown ones - those two routinely differ. */
|
|
4433
|
-
byKind:
|
|
4434
|
-
bug:
|
|
4435
|
-
environment:
|
|
4436
|
-
scenario:
|
|
4606
|
+
byKind: z29.object({
|
|
4607
|
+
bug: z29.number().int(),
|
|
4608
|
+
environment: z29.number().int(),
|
|
4609
|
+
scenario: z29.number().int()
|
|
4437
4610
|
}),
|
|
4438
4611
|
/** Age of the oldest unresolved finding on this branch, in whole days. */
|
|
4439
|
-
oldestAgeDays:
|
|
4612
|
+
oldestAgeDays: z29.number().int()
|
|
4440
4613
|
});
|
|
4441
|
-
suiteHealthFixClusterSchema =
|
|
4442
|
-
title:
|
|
4614
|
+
suiteHealthFixClusterSchema = z29.object({
|
|
4615
|
+
title: z29.string(),
|
|
4443
4616
|
kind: suiteHealthFixKindSchema,
|
|
4444
4617
|
/** Distinct branches carrying it. */
|
|
4445
|
-
branches:
|
|
4618
|
+
branches: z29.number().int(),
|
|
4446
4619
|
/** How many of those are still-open pull requests - the ones a fix unblocks today. */
|
|
4447
|
-
openBranches:
|
|
4448
|
-
findings:
|
|
4620
|
+
openBranches: z29.number().int(),
|
|
4621
|
+
findings: z29.number().int()
|
|
4449
4622
|
});
|
|
4450
|
-
suiteHealthFixPlanSchema =
|
|
4623
|
+
suiteHealthFixPlanSchema = z29.object({
|
|
4451
4624
|
/**
|
|
4452
4625
|
* `owner/repo`, which every MCP tool is keyed by. Absent when it cannot be resolved without asking GitHub,
|
|
4453
4626
|
* in which case the prompt tells the agent to read the git remote instead - which is what the MCP's own
|
|
4454
4627
|
* instructions tell it to do anyway.
|
|
4455
4628
|
*/
|
|
4456
|
-
repoFullName:
|
|
4629
|
+
repoFullName: z29.string().optional(),
|
|
4457
4630
|
/** Unresolved findings across every branch. */
|
|
4458
|
-
totalIssues:
|
|
4459
|
-
byKind:
|
|
4460
|
-
bug:
|
|
4461
|
-
environment:
|
|
4462
|
-
scenario:
|
|
4631
|
+
totalIssues: z29.number().int(),
|
|
4632
|
+
byKind: z29.object({
|
|
4633
|
+
bug: z29.number().int(),
|
|
4634
|
+
environment: z29.number().int(),
|
|
4635
|
+
scenario: z29.number().int()
|
|
4463
4636
|
}),
|
|
4464
4637
|
/** Age of the oldest unresolved finding anywhere on the app, in whole days. */
|
|
4465
|
-
oldestAgeDays:
|
|
4638
|
+
oldestAgeDays: z29.number().int(),
|
|
4466
4639
|
/** True when the scan hit its cap, so `totalIssues` is a floor rather than the count. Never hide this. */
|
|
4467
|
-
truncated:
|
|
4640
|
+
truncated: z29.boolean(),
|
|
4468
4641
|
/** Repeated findings, most-shared first. Empty when nothing repeats. */
|
|
4469
|
-
clusters:
|
|
4642
|
+
clusters: z29.array(suiteHealthFixClusterSchema),
|
|
4470
4643
|
/** The prompt the user copies. Authored from the rows above. */
|
|
4471
|
-
prompt:
|
|
4644
|
+
prompt: z29.string()
|
|
4472
4645
|
});
|
|
4473
4646
|
}
|
|
4474
4647
|
});
|
|
4475
4648
|
|
|
4476
4649
|
// ../../packages/types/src/schemas/index.ts
|
|
4477
|
-
import { z as
|
|
4650
|
+
import { z as z30 } from "zod";
|
|
4478
4651
|
var PlatformSchema, TestStatusSchema;
|
|
4479
4652
|
var init_schemas = __esm({
|
|
4480
4653
|
"../../packages/types/src/schemas/index.ts"() {
|
|
@@ -4488,9 +4661,11 @@ var init_schemas = __esm({
|
|
|
4488
4661
|
init_evidence_tokens();
|
|
4489
4662
|
init_generation();
|
|
4490
4663
|
init_api_key();
|
|
4664
|
+
init_organization();
|
|
4491
4665
|
init_secrets();
|
|
4492
4666
|
init_previewkit_builtins();
|
|
4493
4667
|
init_previewkit_config();
|
|
4668
|
+
init_previewkit_config_rows();
|
|
4494
4669
|
init_previewkit_job_spec();
|
|
4495
4670
|
init_previewkit_node_build();
|
|
4496
4671
|
init_previewkit_node_pm();
|
|
@@ -4510,8 +4685,8 @@ var init_schemas = __esm({
|
|
|
4510
4685
|
init_activation_triggers();
|
|
4511
4686
|
init_suite_health();
|
|
4512
4687
|
init_suite_health_fix_plan();
|
|
4513
|
-
PlatformSchema =
|
|
4514
|
-
TestStatusSchema =
|
|
4688
|
+
PlatformSchema = z30.enum(["web", "ios", "android"]);
|
|
4689
|
+
TestStatusSchema = z30.enum(["pending", "running", "passed", "failed", "cancelled"]);
|
|
4515
4690
|
}
|
|
4516
4691
|
});
|
|
4517
4692
|
|
|
@@ -4546,16 +4721,16 @@ var init_billing = __esm({
|
|
|
4546
4721
|
});
|
|
4547
4722
|
|
|
4548
4723
|
// ../../packages/types/src/schemas/billing.ts
|
|
4549
|
-
import { z as
|
|
4724
|
+
import { z as z31 } from "zod";
|
|
4550
4725
|
var BillingTransactionSchema, BillingStatusSchema, CreateCheckoutInputSchema, AutoTopUpInputSchema, RunCompletedSchema;
|
|
4551
4726
|
var init_billing2 = __esm({
|
|
4552
4727
|
"../../packages/types/src/schemas/billing.ts"() {
|
|
4553
4728
|
"use strict";
|
|
4554
4729
|
init_esm_shims();
|
|
4555
4730
|
init_billing();
|
|
4556
|
-
BillingTransactionSchema =
|
|
4557
|
-
id:
|
|
4558
|
-
type:
|
|
4731
|
+
BillingTransactionSchema = z31.object({
|
|
4732
|
+
id: z31.string(),
|
|
4733
|
+
type: z31.enum([
|
|
4559
4734
|
"SUBSCRIPTION_GRANT",
|
|
4560
4735
|
"SUBSCRIPTION_RESET",
|
|
4561
4736
|
"TOPUP_PURCHASE",
|
|
@@ -4564,70 +4739,70 @@ var init_billing2 = __esm({
|
|
|
4564
4739
|
"GENERATION_REFUND",
|
|
4565
4740
|
"RUN_CONSUMPTION"
|
|
4566
4741
|
]),
|
|
4567
|
-
amount:
|
|
4568
|
-
balanceAfter:
|
|
4569
|
-
generationId:
|
|
4570
|
-
runId:
|
|
4571
|
-
stripePaymentIntentId:
|
|
4572
|
-
stripeInvoiceId:
|
|
4573
|
-
stripeRefundId:
|
|
4574
|
-
createdAt:
|
|
4742
|
+
amount: z31.number(),
|
|
4743
|
+
balanceAfter: z31.number(),
|
|
4744
|
+
generationId: z31.string().optional(),
|
|
4745
|
+
runId: z31.string().optional(),
|
|
4746
|
+
stripePaymentIntentId: z31.string().optional(),
|
|
4747
|
+
stripeInvoiceId: z31.string().optional(),
|
|
4748
|
+
stripeRefundId: z31.string().optional(),
|
|
4749
|
+
createdAt: z31.date()
|
|
4575
4750
|
});
|
|
4576
|
-
BillingStatusSchema =
|
|
4577
|
-
creditBalance:
|
|
4578
|
-
subscriptionCreditBalance:
|
|
4579
|
-
topupCreditBalance:
|
|
4580
|
-
subscriptionStatus:
|
|
4581
|
-
currentPeriodEnd:
|
|
4582
|
-
cancelAtPeriodEnd:
|
|
4583
|
-
gracePeriodEndsAt:
|
|
4584
|
-
autoTopUpEnabled:
|
|
4585
|
-
autoTopUpThreshold:
|
|
4586
|
-
transactions:
|
|
4751
|
+
BillingStatusSchema = z31.object({
|
|
4752
|
+
creditBalance: z31.number(),
|
|
4753
|
+
subscriptionCreditBalance: z31.number(),
|
|
4754
|
+
topupCreditBalance: z31.number(),
|
|
4755
|
+
subscriptionStatus: z31.enum(["active", "canceled", "past_due", "unpaid", "trialing", "paused", "incomplete", "incomplete_expired"]).optional(),
|
|
4756
|
+
currentPeriodEnd: z31.date().optional(),
|
|
4757
|
+
cancelAtPeriodEnd: z31.boolean(),
|
|
4758
|
+
gracePeriodEndsAt: z31.date().optional(),
|
|
4759
|
+
autoTopUpEnabled: z31.boolean(),
|
|
4760
|
+
autoTopUpThreshold: z31.number(),
|
|
4761
|
+
transactions: z31.array(BillingTransactionSchema)
|
|
4587
4762
|
});
|
|
4588
|
-
CreateCheckoutInputSchema =
|
|
4589
|
-
type:
|
|
4763
|
+
CreateCheckoutInputSchema = z31.object({
|
|
4764
|
+
type: z31.enum([BILLING_CHECKOUT_TYPES.SUBSCRIPTION, BILLING_CHECKOUT_TYPES.TOPUP])
|
|
4590
4765
|
});
|
|
4591
|
-
AutoTopUpInputSchema =
|
|
4592
|
-
enabled:
|
|
4593
|
-
threshold:
|
|
4766
|
+
AutoTopUpInputSchema = z31.object({
|
|
4767
|
+
enabled: z31.boolean(),
|
|
4768
|
+
threshold: z31.number().int().min(0)
|
|
4594
4769
|
});
|
|
4595
|
-
RunCompletedSchema =
|
|
4596
|
-
generationId:
|
|
4770
|
+
RunCompletedSchema = z31.object({
|
|
4771
|
+
generationId: z31.string()
|
|
4597
4772
|
});
|
|
4598
4773
|
}
|
|
4599
4774
|
});
|
|
4600
4775
|
|
|
4601
4776
|
// ../../packages/types/src/schemas/github.ts
|
|
4602
|
-
import { z as
|
|
4777
|
+
import { z as z32 } from "zod";
|
|
4603
4778
|
var GitHubInstallationStatusSchema, GithubInstallationSchema;
|
|
4604
4779
|
var init_github = __esm({
|
|
4605
4780
|
"../../packages/types/src/schemas/github.ts"() {
|
|
4606
4781
|
"use strict";
|
|
4607
4782
|
init_esm_shims();
|
|
4608
|
-
GitHubInstallationStatusSchema =
|
|
4609
|
-
GithubInstallationSchema =
|
|
4610
|
-
id:
|
|
4611
|
-
installationId:
|
|
4612
|
-
organizationId:
|
|
4613
|
-
accountLogin:
|
|
4614
|
-
accountId:
|
|
4615
|
-
accountType:
|
|
4783
|
+
GitHubInstallationStatusSchema = z32.enum(["active", "suspended", "deleted"]);
|
|
4784
|
+
GithubInstallationSchema = z32.object({
|
|
4785
|
+
id: z32.string(),
|
|
4786
|
+
installationId: z32.number(),
|
|
4787
|
+
organizationId: z32.string(),
|
|
4788
|
+
accountLogin: z32.string(),
|
|
4789
|
+
accountId: z32.number(),
|
|
4790
|
+
accountType: z32.string(),
|
|
4616
4791
|
status: GitHubInstallationStatusSchema,
|
|
4617
|
-
createdAt:
|
|
4618
|
-
updatedAt:
|
|
4792
|
+
createdAt: z32.date(),
|
|
4793
|
+
updatedAt: z32.date()
|
|
4619
4794
|
});
|
|
4620
4795
|
}
|
|
4621
4796
|
});
|
|
4622
4797
|
|
|
4623
4798
|
// ../../packages/types/src/schemas/vercel.ts
|
|
4624
|
-
import { z as
|
|
4799
|
+
import { z as z33 } from "zod";
|
|
4625
4800
|
var VercelInstallationWireStatusSchema;
|
|
4626
4801
|
var init_vercel = __esm({
|
|
4627
4802
|
"../../packages/types/src/schemas/vercel.ts"() {
|
|
4628
4803
|
"use strict";
|
|
4629
4804
|
init_esm_shims();
|
|
4630
|
-
VercelInstallationWireStatusSchema =
|
|
4805
|
+
VercelInstallationWireStatusSchema = z33.enum([
|
|
4631
4806
|
"ready",
|
|
4632
4807
|
"pending",
|
|
4633
4808
|
"onboarding",
|
|
@@ -4683,6 +4858,15 @@ var init_preview_url = __esm({
|
|
|
4683
4858
|
"../../packages/types/src/preview-url.ts"() {
|
|
4684
4859
|
"use strict";
|
|
4685
4860
|
init_esm_shims();
|
|
4861
|
+
init_parse_url();
|
|
4862
|
+
}
|
|
4863
|
+
});
|
|
4864
|
+
|
|
4865
|
+
// ../../packages/types/src/org-domain.ts
|
|
4866
|
+
var init_org_domain = __esm({
|
|
4867
|
+
"../../packages/types/src/org-domain.ts"() {
|
|
4868
|
+
"use strict";
|
|
4869
|
+
init_esm_shims();
|
|
4686
4870
|
}
|
|
4687
4871
|
});
|
|
4688
4872
|
|
|
@@ -4734,6 +4918,32 @@ var init_analytics_headers = __esm({
|
|
|
4734
4918
|
}
|
|
4735
4919
|
});
|
|
4736
4920
|
|
|
4921
|
+
// ../../packages/types/src/constants/application-instructions.ts
|
|
4922
|
+
var init_application_instructions = __esm({
|
|
4923
|
+
"../../packages/types/src/constants/application-instructions.ts"() {
|
|
4924
|
+
"use strict";
|
|
4925
|
+
init_esm_shims();
|
|
4926
|
+
}
|
|
4927
|
+
});
|
|
4928
|
+
|
|
4929
|
+
// ../../packages/types/src/constants/auth-cookies.ts
|
|
4930
|
+
var init_auth_cookies = __esm({
|
|
4931
|
+
"../../packages/types/src/constants/auth-cookies.ts"() {
|
|
4932
|
+
"use strict";
|
|
4933
|
+
init_esm_shims();
|
|
4934
|
+
}
|
|
4935
|
+
});
|
|
4936
|
+
|
|
4937
|
+
// ../../packages/types/src/constants/integration-branch.ts
|
|
4938
|
+
var INTEGRATION_BRANCH;
|
|
4939
|
+
var init_integration_branch = __esm({
|
|
4940
|
+
"../../packages/types/src/constants/integration-branch.ts"() {
|
|
4941
|
+
"use strict";
|
|
4942
|
+
init_esm_shims();
|
|
4943
|
+
INTEGRATION_BRANCH = "autonoma-integration";
|
|
4944
|
+
}
|
|
4945
|
+
});
|
|
4946
|
+
|
|
4737
4947
|
// ../../packages/types/src/constants/pipeline-labels.ts
|
|
4738
4948
|
var init_pipeline_labels = __esm({
|
|
4739
4949
|
"../../packages/types/src/constants/pipeline-labels.ts"() {
|
|
@@ -4748,7 +4958,10 @@ var init_constants = __esm({
|
|
|
4748
4958
|
"use strict";
|
|
4749
4959
|
init_esm_shims();
|
|
4750
4960
|
init_analytics_headers();
|
|
4961
|
+
init_application_instructions();
|
|
4962
|
+
init_auth_cookies();
|
|
4751
4963
|
init_billing();
|
|
4964
|
+
init_integration_branch();
|
|
4752
4965
|
init_pipeline_labels();
|
|
4753
4966
|
}
|
|
4754
4967
|
});
|
|
@@ -4761,14 +4974,6 @@ var init_architecture = __esm({
|
|
|
4761
4974
|
}
|
|
4762
4975
|
});
|
|
4763
4976
|
|
|
4764
|
-
// ../../packages/types/src/types/previewkit.ts
|
|
4765
|
-
var init_previewkit = __esm({
|
|
4766
|
-
"../../packages/types/src/types/previewkit.ts"() {
|
|
4767
|
-
"use strict";
|
|
4768
|
-
init_esm_shims();
|
|
4769
|
-
}
|
|
4770
|
-
});
|
|
4771
|
-
|
|
4772
4977
|
// ../../packages/types/src/types/parse-string-record.ts
|
|
4773
4978
|
var init_parse_string_record = __esm({
|
|
4774
4979
|
"../../packages/types/src/types/parse-string-record.ts"() {
|
|
@@ -4805,7 +5010,10 @@ var init_src = __esm({
|
|
|
4805
5010
|
init_github();
|
|
4806
5011
|
init_vercel();
|
|
4807
5012
|
init_sensitive_detection();
|
|
5013
|
+
init_parse_url();
|
|
4808
5014
|
init_preview_url();
|
|
5015
|
+
init_sdk_endpoint();
|
|
5016
|
+
init_org_domain();
|
|
4809
5017
|
init_app_links();
|
|
4810
5018
|
init_agent_handoff_links();
|
|
4811
5019
|
init_sdk_error_signals();
|
|
@@ -5260,7 +5468,7 @@ var init_output = __esm({
|
|
|
5260
5468
|
// src/core/preferences.ts
|
|
5261
5469
|
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
5262
5470
|
import { join as join12 } from "path";
|
|
5263
|
-
import { z as
|
|
5471
|
+
import { z as z35 } from "zod";
|
|
5264
5472
|
async function readPreferences() {
|
|
5265
5473
|
try {
|
|
5266
5474
|
const raw = await readFile4(PREFERENCES_FILE, "utf-8");
|
|
@@ -5295,9 +5503,9 @@ var init_preferences = __esm({
|
|
|
5295
5503
|
init_debug();
|
|
5296
5504
|
init_output();
|
|
5297
5505
|
PREFERENCES_FILE = join12(AUTONOMA_HOME3, "preferences.json");
|
|
5298
|
-
PreferencesSchema =
|
|
5506
|
+
PreferencesSchema = z35.object({
|
|
5299
5507
|
/** The coding agent id (`claude`, `codex`) last chosen from the picker. */
|
|
5300
|
-
agentId:
|
|
5508
|
+
agentId: z35.string().optional()
|
|
5301
5509
|
});
|
|
5302
5510
|
}
|
|
5303
5511
|
});
|
|
@@ -5316,6 +5524,9 @@ __export(coding_agent_exports, {
|
|
|
5316
5524
|
parsePermissionMode: () => parsePermissionMode,
|
|
5317
5525
|
selectLauncher: () => selectLauncher
|
|
5318
5526
|
});
|
|
5527
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
5528
|
+
import { homedir as homedir5 } from "os";
|
|
5529
|
+
import { join as join13 } from "path";
|
|
5319
5530
|
import spawn from "cross-spawn";
|
|
5320
5531
|
import which from "which";
|
|
5321
5532
|
function isSpawnedByPlanner2(env = process.env) {
|
|
@@ -5362,10 +5573,28 @@ function parsePermissionMode(value) {
|
|
|
5362
5573
|
if (value === "default" || value === "acceptEdits" || value === "bypassPermissions") return value;
|
|
5363
5574
|
return void 0;
|
|
5364
5575
|
}
|
|
5576
|
+
function describeCommandFailure(result, label) {
|
|
5577
|
+
return explainFailureReason(firstLine(result.stderr) || firstLine(result.stdout) || "unknown error", label);
|
|
5578
|
+
}
|
|
5579
|
+
function isClientNotExecutable(output) {
|
|
5580
|
+
return CLIENT_NOT_EXECUTABLE_PATTERNS.some((pattern) => pattern.test(output));
|
|
5581
|
+
}
|
|
5582
|
+
function explainFailureReason(reason, label) {
|
|
5583
|
+
if (!isClientNotExecutable(reason)) return reason;
|
|
5584
|
+
return `${label} is on your PATH but could not be run (${reason}). Reinstall it, then run again.`;
|
|
5585
|
+
}
|
|
5586
|
+
function describeSignInFailure(login, label) {
|
|
5587
|
+
const reason = firstLine(login.stderr);
|
|
5588
|
+
const code = login.code ?? "unknown";
|
|
5589
|
+
return reason.length > 0 ? ` (exit ${code}: ${explainFailureReason(reason, label)})` : ` (exit ${code})`;
|
|
5590
|
+
}
|
|
5591
|
+
function isRecord2(value) {
|
|
5592
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5593
|
+
}
|
|
5365
5594
|
function firstLine(output) {
|
|
5366
5595
|
return output.split("\n").find((line) => line.trim().length > 0)?.trim() ?? "";
|
|
5367
5596
|
}
|
|
5368
|
-
var MCP_COMMAND_TIMEOUT_MS, CODEX_BEARER_TOKEN_ENV, CLAUDE_CONNECTED_MARKER, DEFAULT_PERMISSION_MODE, PERMISSION_MODE_LABELS, CLAUDE_ID, CODEX_ID, BaseLauncher, ClaudeLauncher, CodexLauncher;
|
|
5597
|
+
var MCP_COMMAND_TIMEOUT_MS, ATTACHED_STDERR_CAP, CODEX_BEARER_TOKEN_ENV, CLAUDE_CONNECTED_MARKER, CLIENT_NOT_EXECUTABLE_PATTERNS, CLAUDE_AUTH_HEADER_MARKER, CLAUDE_NEEDS_AUTH_CACHE_FILE, CLAUDE_MODEL, CLAUDE_SUBAGENT_MODEL_ENV, DEFAULT_PERMISSION_MODE, PERMISSION_MODE_LABELS, CLAUDE_ID, CODEX_ID, BaseLauncher, ClaudeLauncher, CodexLauncher;
|
|
5369
5598
|
var init_coding_agent = __esm({
|
|
5370
5599
|
"src/core/coding-agent.ts"() {
|
|
5371
5600
|
"use strict";
|
|
@@ -5373,11 +5602,23 @@ var init_coding_agent = __esm({
|
|
|
5373
5602
|
init_prompts();
|
|
5374
5603
|
init_agent_env();
|
|
5375
5604
|
init_debug();
|
|
5605
|
+
init_logs();
|
|
5376
5606
|
init_preferences();
|
|
5377
5607
|
init_agent_env();
|
|
5378
5608
|
MCP_COMMAND_TIMEOUT_MS = 6e4;
|
|
5609
|
+
ATTACHED_STDERR_CAP = 4e3;
|
|
5379
5610
|
CODEX_BEARER_TOKEN_ENV = "AUTONOMA_MCP_TOKEN";
|
|
5380
5611
|
CLAUDE_CONNECTED_MARKER = "Connected";
|
|
5612
|
+
CLIENT_NOT_EXECUTABLE_PATTERNS = [
|
|
5613
|
+
/is not recognized as an internal or external command/i,
|
|
5614
|
+
/command not found/i,
|
|
5615
|
+
/\bENOENT\b/,
|
|
5616
|
+
/no such file or directory/i
|
|
5617
|
+
];
|
|
5618
|
+
CLAUDE_AUTH_HEADER_MARKER = "Authorization:";
|
|
5619
|
+
CLAUDE_NEEDS_AUTH_CACHE_FILE = "mcp-needs-auth-cache.json";
|
|
5620
|
+
CLAUDE_MODEL = "opus";
|
|
5621
|
+
CLAUDE_SUBAGENT_MODEL_ENV = "CLAUDE_CODE_SUBAGENT_MODEL";
|
|
5381
5622
|
DEFAULT_PERMISSION_MODE = "bypassPermissions";
|
|
5382
5623
|
PERMISSION_MODE_LABELS = {
|
|
5383
5624
|
default: "Approve each command",
|
|
@@ -5464,40 +5705,107 @@ var init_coding_agent = __esm({
|
|
|
5464
5705
|
/**
|
|
5465
5706
|
* Sign this client in to a server through its own browser flow.
|
|
5466
5707
|
*
|
|
5467
|
-
*
|
|
5468
|
-
*
|
|
5469
|
-
*
|
|
5470
|
-
*
|
|
5471
|
-
*
|
|
5708
|
+
* Never throws. A sign-in is a convenience over the credential the run is already
|
|
5709
|
+
* holding, so every way it can fail - no terminal to open a browser from, a
|
|
5710
|
+
* client that exits non-zero - resolves to a result the caller can fall back on.
|
|
5711
|
+
* Refusing without a terminal is still the right call: the flow prints a URL and
|
|
5712
|
+
* blocks on a callback, so with nobody at the keyboard it would not fail, it would
|
|
5713
|
+
* hang indefinitely on a prompt nobody can see.
|
|
5472
5714
|
*/
|
|
5473
5715
|
async signIn(serverName) {
|
|
5474
5716
|
if (process.stdin.isTTY !== true) {
|
|
5475
|
-
|
|
5476
|
-
`Cannot sign ${this.label} in to the ${serverName} MCP server without a terminal: that flow opens a browser and waits for it. Set AUTONOMA_API_TOKEN so the run authorizes with an API key instead.`
|
|
5477
|
-
);
|
|
5717
|
+
return { ok: false, detail: " (no terminal to run a browser sign-in from)", clientUnusable: false };
|
|
5478
5718
|
}
|
|
5479
5719
|
log.info(`Authorizing ${this.label} with Autonoma - approve it in the browser that opens.`);
|
|
5480
5720
|
const login = await this.runAttached(["mcp", "login", serverName]);
|
|
5481
|
-
if (login
|
|
5721
|
+
if (login.code === 0) return { ok: true, detail: "", clientUnusable: false };
|
|
5722
|
+
debugLog(`${this.label} sign-in failed`, { serverName, code: login.code, stderr: login.stderr });
|
|
5723
|
+
return {
|
|
5724
|
+
ok: false,
|
|
5725
|
+
detail: describeSignInFailure(login, this.label),
|
|
5726
|
+
clientUnusable: isClientNotExecutable(login.stderr)
|
|
5727
|
+
};
|
|
5728
|
+
}
|
|
5729
|
+
/**
|
|
5730
|
+
* The credential a registration must have when no browser sign-in is on the table.
|
|
5731
|
+
* Its absence is a configuration error worth stating now, rather than an
|
|
5732
|
+
* unauthorized server the spawned agent discovers on its first tool call.
|
|
5733
|
+
*/
|
|
5734
|
+
requireHeadlessToken(spec) {
|
|
5735
|
+
if (spec.apiToken != null) return spec.apiToken;
|
|
5736
|
+
throw new Error(
|
|
5737
|
+
`Cannot authorize the ${spec.name} MCP server for ${this.label} without a terminal: a browser sign-in needs someone to approve it. Set AUTONOMA_API_TOKEN so the run authorizes with an API key instead.`
|
|
5738
|
+
);
|
|
5739
|
+
}
|
|
5740
|
+
/**
|
|
5741
|
+
* Authorize a server that a browser sign-in was preferred for, and say how it
|
|
5742
|
+
* ended up authorized so the caller can register it accordingly.
|
|
5743
|
+
*
|
|
5744
|
+
* A failed sign-in is not the end of the run. The token that falls back here is
|
|
5745
|
+
* the same one that already authorized every API call made to get this far, so
|
|
5746
|
+
* refusing to use it loses the run to a browser that would not open. It also
|
|
5747
|
+
* repairs what the attempt broke: a failed `mcp login` clears whatever stored
|
|
5748
|
+
* credentials the client had, which is why a first failure used to make every
|
|
5749
|
+
* later run fail too.
|
|
5750
|
+
*/
|
|
5751
|
+
async authorizeInteractively(spec) {
|
|
5752
|
+
const signIn = await this.signIn(spec.name);
|
|
5753
|
+
if (signIn.ok) return { via: "sign-in" };
|
|
5754
|
+
return { via: "token", ...this.fallBackToToken(spec, signIn) };
|
|
5755
|
+
}
|
|
5756
|
+
/**
|
|
5757
|
+
* Switch to the run's own API token after a browser sign-in did not happen, for
|
|
5758
|
+
* whatever reason - it was refused, it failed, or the client folded it into a
|
|
5759
|
+
* registration that failed. Throws only when there is no token to switch to.
|
|
5760
|
+
*/
|
|
5761
|
+
fallBackToToken(spec, signIn) {
|
|
5762
|
+
const detail = signIn.detail;
|
|
5763
|
+
if (signIn.clientUnusable) {
|
|
5764
|
+
throw new Error(`Could not register the ${spec.name} MCP server with ${this.label}:${detail}`);
|
|
5765
|
+
}
|
|
5766
|
+
if (spec.apiToken == null) {
|
|
5482
5767
|
throw new Error(
|
|
5483
|
-
`${this.label} could not sign in to the ${
|
|
5768
|
+
`${this.label} could not sign in to the ${spec.name} MCP server${detail}, and this run has no API token to fall back on. Set AUTONOMA_API_TOKEN and run again.`
|
|
5484
5769
|
);
|
|
5485
5770
|
}
|
|
5771
|
+
captureLog("warn", "Browser sign-in failed; authorizing the MCP server with the run's API token", {
|
|
5772
|
+
source: "coding_agent",
|
|
5773
|
+
agent: this.id,
|
|
5774
|
+
detail
|
|
5775
|
+
});
|
|
5776
|
+
log.warn(
|
|
5777
|
+
`${this.label} couldn't sign in to the ${spec.name} MCP server${detail}. Authorizing it with this run's API token instead.`
|
|
5778
|
+
);
|
|
5779
|
+
return { apiToken: spec.apiToken };
|
|
5486
5780
|
}
|
|
5487
5781
|
/**
|
|
5488
5782
|
* Run a subcommand that hands the terminal over - an interactive OAuth sign-in.
|
|
5489
|
-
* The client prints a URL, opens a browser and waits on the callback, so
|
|
5490
|
-
*
|
|
5783
|
+
* The client prints a URL, opens a browser and waits on the callback, so stdin and
|
|
5784
|
+
* stdout stay attached to this process.
|
|
5785
|
+
*
|
|
5786
|
+
* stderr is the exception: it is piped so the client's own diagnosis can be quoted
|
|
5787
|
+
* back and reported, and echoed as it arrives so the user still watches it live.
|
|
5788
|
+
* Inheriting it would put that text on screen and nowhere else, which is what made
|
|
5789
|
+
* every sign-in failure look identical from the outside.
|
|
5491
5790
|
*/
|
|
5492
5791
|
runAttached(args) {
|
|
5493
5792
|
debugLog(`Running ${this.command} ${args.join(" ")} attached`);
|
|
5494
5793
|
return new Promise((resolve6) => {
|
|
5495
|
-
const proc = spawn(this.command, args, {
|
|
5794
|
+
const proc = spawn(this.command, args, {
|
|
5795
|
+
cwd: this.opts.cwd,
|
|
5796
|
+
env: this.opts.env,
|
|
5797
|
+
stdio: ["inherit", "inherit", "pipe"]
|
|
5798
|
+
});
|
|
5799
|
+
let stderr = "";
|
|
5800
|
+
proc.stderr?.on("data", (chunk) => {
|
|
5801
|
+
process.stderr.write(chunk);
|
|
5802
|
+
if (stderr.length < ATTACHED_STDERR_CAP) stderr += chunk.toString();
|
|
5803
|
+
});
|
|
5496
5804
|
proc.on("error", (err) => {
|
|
5497
5805
|
debugLog(`${this.command} ${args[0] ?? ""} failed to spawn`, { err });
|
|
5498
|
-
resolve6(void 0);
|
|
5806
|
+
resolve6({ code: void 0, stderr: err.message });
|
|
5499
5807
|
});
|
|
5500
|
-
proc.on("close", (code) => resolve6(code ?? void 0));
|
|
5808
|
+
proc.on("close", (code) => resolve6({ code: code ?? void 0, stderr }));
|
|
5501
5809
|
});
|
|
5502
5810
|
}
|
|
5503
5811
|
};
|
|
@@ -5506,36 +5814,113 @@ var init_coding_agent = __esm({
|
|
|
5506
5814
|
label = "Claude Code";
|
|
5507
5815
|
command = CLAUDE_ID;
|
|
5508
5816
|
buildArgs(message, permissionMode, interactive) {
|
|
5509
|
-
return interactive ? ["--permission-mode", permissionMode, message] : ["-p", message, "--permission-mode", permissionMode, "--verbose"];
|
|
5817
|
+
return interactive ? ["--permission-mode", permissionMode, "--model", CLAUDE_MODEL, message] : ["-p", message, "--permission-mode", permissionMode, "--model", CLAUDE_MODEL, "--verbose"];
|
|
5818
|
+
}
|
|
5819
|
+
/** Carry the model floor into the session's subagents, which read it from the env. */
|
|
5820
|
+
spawnEnv(extra) {
|
|
5821
|
+
return { ...super.spawnEnv(extra), [CLAUDE_SUBAGENT_MODEL_ENV]: CLAUDE_MODEL };
|
|
5510
5822
|
}
|
|
5511
5823
|
/**
|
|
5512
|
-
*
|
|
5513
|
-
*
|
|
5514
|
-
*
|
|
5515
|
-
* was never authorized - the agent simply has no Autonoma tools.
|
|
5516
|
-
*
|
|
5517
|
-
* With a token the header goes into Claude's own config and there is nothing to
|
|
5518
|
-
* sign in to. Without one, the browser sign-in runs attached to the terminal.
|
|
5824
|
+
* Headless registers with the bearer header outright - there is no browser to sign
|
|
5825
|
+
* in with. Interactively the registration goes in without one so the client's own
|
|
5826
|
+
* sign-in can claim it, and the header is applied only if that sign-in fails.
|
|
5519
5827
|
*/
|
|
5520
5828
|
async registerMcpServer(spec) {
|
|
5521
|
-
const
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5829
|
+
const registration = await this.register(spec);
|
|
5830
|
+
await this.forgetNeedsAuthVerdict(spec.name);
|
|
5831
|
+
return registration;
|
|
5832
|
+
}
|
|
5833
|
+
/**
|
|
5834
|
+
* Forget Claude's cached "this server needs authorization" verdict for this name.
|
|
5835
|
+
*
|
|
5836
|
+
* Claude caches that verdict per server NAME and then skips connecting on every
|
|
5837
|
+
* later session - "Skipping connection (cached needs-auth)" in its own debug log -
|
|
5838
|
+
* whatever the registration now holds. The health check this class runs to decide
|
|
5839
|
+
* whether to sign in is itself what writes the entry, and a successful one does not
|
|
5840
|
+
* clear it. Without this, a registration can report Connected, answer tool calls
|
|
5841
|
+
* over HTTP, and still be invisible to the agent we spawn: no Autonoma tools, and
|
|
5842
|
+
* nothing on screen saying why.
|
|
5843
|
+
*
|
|
5844
|
+
* Best effort by design. It reaches into another tool's internal state, so a missing
|
|
5845
|
+
* file, a changed format or an unwritable home degrades to a debug line rather than
|
|
5846
|
+
* failing a registration that is otherwise fine.
|
|
5847
|
+
*/
|
|
5848
|
+
async forgetNeedsAuthVerdict(serverName) {
|
|
5849
|
+
const configDir = this.opts.env.CLAUDE_CONFIG_DIR ?? join13(homedir5(), ".claude");
|
|
5850
|
+
const cachePath = join13(configDir, CLAUDE_NEEDS_AUTH_CACHE_FILE);
|
|
5851
|
+
try {
|
|
5852
|
+
const parsed = JSON.parse(await readFile5(cachePath, "utf-8"));
|
|
5853
|
+
if (!isRecord2(parsed) || !(serverName in parsed)) return;
|
|
5854
|
+
const remaining = { ...parsed };
|
|
5855
|
+
delete remaining[serverName];
|
|
5856
|
+
await writeFile4(cachePath, JSON.stringify(remaining), "utf-8");
|
|
5857
|
+
debugLog("Cleared a cached needs-auth verdict", { server: serverName });
|
|
5858
|
+
} catch (err) {
|
|
5859
|
+
debugLog("Could not clear the cached needs-auth verdict", { cachePath, err });
|
|
5860
|
+
}
|
|
5861
|
+
}
|
|
5862
|
+
async register(spec) {
|
|
5863
|
+
await this.dropRegistrationPointingElsewhere(spec);
|
|
5864
|
+
if (!spec.browserSignIn) {
|
|
5865
|
+
await this.addServer(spec, this.requireHeadlessToken(spec));
|
|
5866
|
+
return { env: {} };
|
|
5527
5867
|
}
|
|
5868
|
+
const registered = await this.addServer(spec, void 0);
|
|
5869
|
+
if (registered.includes(CLAUDE_CONNECTED_MARKER)) return { env: {} };
|
|
5870
|
+
const authorizable = registered.includes(CLAUDE_AUTH_HEADER_MARKER) ? await this.clearBearerHeader(spec) : registered;
|
|
5871
|
+
if (authorizable.includes(CLAUDE_CONNECTED_MARKER)) return { env: {} };
|
|
5872
|
+
const authorization = await this.authorizeInteractively(spec);
|
|
5873
|
+
if (authorization.via === "sign-in") return { env: {} };
|
|
5874
|
+
await this.runCommand(["mcp", "remove", spec.name, "-s", "user"]);
|
|
5875
|
+
await this.addServer(spec, authorization.apiToken);
|
|
5876
|
+
return { env: {} };
|
|
5877
|
+
}
|
|
5878
|
+
/**
|
|
5879
|
+
* An existing registration is only useful if it points where this run does. `mcp
|
|
5880
|
+
* add` will not update one, so a registration left over from another environment
|
|
5881
|
+
* would be reused in silence and the agent would work against the wrong Autonoma.
|
|
5882
|
+
* Replace it rather than inherit it.
|
|
5883
|
+
*/
|
|
5884
|
+
async dropRegistrationPointingElsewhere(spec) {
|
|
5885
|
+
const existing = await this.runCommand(["mcp", "get", spec.name]);
|
|
5886
|
+
if (existing.code !== 0 || existing.stdout.includes(spec.url)) return;
|
|
5887
|
+
debugLog("Replacing an MCP registration that points elsewhere", { server: spec.name, url: spec.url });
|
|
5888
|
+
await this.runCommand(["mcp", "remove", spec.name, "-s", "user"]);
|
|
5889
|
+
}
|
|
5890
|
+
/**
|
|
5891
|
+
* Re-register without the bearer header, so a browser sign-in becomes possible
|
|
5892
|
+
* again. Returns what `get` then reported - which may be Connected on its own, if
|
|
5893
|
+
* the client still holds OAuth credentials the header had been masking.
|
|
5894
|
+
*/
|
|
5895
|
+
async clearBearerHeader(spec) {
|
|
5896
|
+
debugLog("Clearing a bearer header that would block the browser sign-in", { server: spec.name });
|
|
5897
|
+
await this.runCommand(["mcp", "remove", spec.name, "-s", "user"]);
|
|
5898
|
+
return await this.addServer(spec, void 0);
|
|
5899
|
+
}
|
|
5900
|
+
/**
|
|
5901
|
+
* Add the server and confirm the client actually holds it, returning what `get`
|
|
5902
|
+
* reported so the caller can also read whether it is authorized.
|
|
5903
|
+
*
|
|
5904
|
+
* `mcp add` exits non-zero when the server is already registered, which is the
|
|
5905
|
+
* normal case on every run after the first, so its exit code is a hint and `get`
|
|
5906
|
+
* is the verdict.
|
|
5907
|
+
*
|
|
5908
|
+
* `--scope user` and not the default (`local`): the default binds the server to
|
|
5909
|
+
* the directory the command ran in. The planner can be invoked from anywhere in a
|
|
5910
|
+
* repo, and a server registered against the wrong directory looks exactly like one
|
|
5911
|
+
* that was never authorized - the agent simply has no Autonoma tools.
|
|
5912
|
+
*/
|
|
5913
|
+
async addServer(spec, apiToken) {
|
|
5914
|
+
const add = ["mcp", "add", "--transport", "http", "--scope", "user", spec.name, spec.url];
|
|
5915
|
+
if (apiToken != null) add.push("--header", `Authorization: Bearer ${apiToken}`);
|
|
5528
5916
|
await this.runCommand(add);
|
|
5529
5917
|
const registered = await this.runCommand(["mcp", "get", spec.name]);
|
|
5530
5918
|
if (registered.code !== 0) {
|
|
5531
5919
|
throw new Error(
|
|
5532
|
-
`Could not register the ${spec.name} MCP server with ${this.label}: ${
|
|
5920
|
+
`Could not register the ${spec.name} MCP server with ${this.label}: ${describeCommandFailure(registered, this.label)}`
|
|
5533
5921
|
);
|
|
5534
5922
|
}
|
|
5535
|
-
|
|
5536
|
-
await this.signIn(spec.name);
|
|
5537
|
-
}
|
|
5538
|
-
return { env: {} };
|
|
5923
|
+
return registered.stdout;
|
|
5539
5924
|
}
|
|
5540
5925
|
};
|
|
5541
5926
|
CodexLauncher = class extends BaseLauncher {
|
|
@@ -5572,19 +5957,53 @@ var init_coding_agent = __esm({
|
|
|
5572
5957
|
* server overwrites its entry, so a re-run cannot leave a stale URL behind.
|
|
5573
5958
|
*/
|
|
5574
5959
|
async registerMcpServer(spec) {
|
|
5960
|
+
if (!spec.browserSignIn) {
|
|
5961
|
+
const apiToken = this.requireHeadlessToken(spec);
|
|
5962
|
+
await this.addServer(spec, apiToken);
|
|
5963
|
+
return { env: { [CODEX_BEARER_TOKEN_ENV]: apiToken } };
|
|
5964
|
+
}
|
|
5965
|
+
const signIn = await this.addServerAndSignIn(spec);
|
|
5966
|
+
if (signIn.ok) return { env: {} };
|
|
5967
|
+
const authorization = this.fallBackToToken(spec, signIn);
|
|
5968
|
+
await this.addServer(spec, authorization.apiToken);
|
|
5969
|
+
return { env: { [CODEX_BEARER_TOKEN_ENV]: authorization.apiToken } };
|
|
5970
|
+
}
|
|
5971
|
+
/**
|
|
5972
|
+
* Register the server the interactive way, which for Codex is one step rather than
|
|
5973
|
+
* two: given a `--url` and no bearer token, `codex mcp add` detects the server's
|
|
5974
|
+
* OAuth support, prints an authorization URL, opens a browser and waits on the
|
|
5975
|
+
* callback - all inside the add.
|
|
5976
|
+
*
|
|
5977
|
+
* Two consequences. It needs the terminal: run through a pipe, the URL it tells the
|
|
5978
|
+
* user to "copy above manually" when the browser fails to open goes into a buffer
|
|
5979
|
+
* nobody reads, turning a recoverable prompt into a dead end. And a clean exit means
|
|
5980
|
+
* the server is registered AND signed in, so following it with `mcp login` would
|
|
5981
|
+
* only open a second browser flow for a session that is already authorized.
|
|
5982
|
+
*/
|
|
5983
|
+
async addServerAndSignIn(spec) {
|
|
5984
|
+
if (process.stdin.isTTY !== true) {
|
|
5985
|
+
return { ok: false, detail: " (no terminal to run a browser sign-in from)", clientUnusable: false };
|
|
5986
|
+
}
|
|
5987
|
+
log.info(`Authorizing ${this.label} with Autonoma - approve it in the browser that opens.`);
|
|
5988
|
+
const added = await this.runAttached(["mcp", "add", spec.name, "--url", spec.url]);
|
|
5989
|
+
if (added.code === 0) return { ok: true, detail: "", clientUnusable: false };
|
|
5990
|
+
debugLog(`${this.label} registration sign-in failed`, { code: added.code, stderr: added.stderr });
|
|
5991
|
+
return {
|
|
5992
|
+
ok: false,
|
|
5993
|
+
detail: describeSignInFailure(added, this.label),
|
|
5994
|
+
clientUnusable: isClientNotExecutable(added.stderr)
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
5997
|
+
/** Add (or overwrite) the server, with the token read from an env var by name. */
|
|
5998
|
+
async addServer(spec, apiToken) {
|
|
5575
5999
|
const add = ["mcp", "add", spec.name, "--url", spec.url];
|
|
5576
|
-
if (
|
|
6000
|
+
if (apiToken != null) add.push("--bearer-token-env-var", CODEX_BEARER_TOKEN_ENV);
|
|
5577
6001
|
const added = await this.runCommand(add);
|
|
5578
6002
|
if (added.code !== 0) {
|
|
5579
6003
|
throw new Error(
|
|
5580
|
-
`Could not register the ${spec.name} MCP server with ${this.label}: ${
|
|
6004
|
+
`Could not register the ${spec.name} MCP server with ${this.label}: ${describeCommandFailure(added, this.label)}`
|
|
5581
6005
|
);
|
|
5582
6006
|
}
|
|
5583
|
-
if (spec.apiToken == null) {
|
|
5584
|
-
await this.signIn(spec.name);
|
|
5585
|
-
return { env: {} };
|
|
5586
|
-
}
|
|
5587
|
-
return { env: { [CODEX_BEARER_TOKEN_ENV]: spec.apiToken } };
|
|
5588
6007
|
}
|
|
5589
6008
|
};
|
|
5590
6009
|
}
|
|
@@ -6261,7 +6680,7 @@ var init_permission_mode = __esm({
|
|
|
6261
6680
|
// src/core/preview-phase.ts
|
|
6262
6681
|
function previewPrompt(code, interactive) {
|
|
6263
6682
|
const instruction = `set up my preview environments with the ${MCP_SERVER_NAME} MCP, code ${code}`;
|
|
6264
|
-
return interactive ? instruction : `${instruction}.${HEADLESS_GUIDANCE}`;
|
|
6683
|
+
return interactive ? `${instruction}.${BRANCH_GUIDANCE}` : `${instruction}.${BRANCH_GUIDANCE}${HEADLESS_GUIDANCE}`;
|
|
6265
6684
|
}
|
|
6266
6685
|
async function runPreviewPhase(deps) {
|
|
6267
6686
|
const timing = deps.timing ?? DEFAULT_PHASE_TIMING;
|
|
@@ -6269,7 +6688,8 @@ async function runPreviewPhase(deps) {
|
|
|
6269
6688
|
const registration = await deps.launcher.registerMcpServer({
|
|
6270
6689
|
name: MCP_SERVER_NAME,
|
|
6271
6690
|
url: deps.mcpUrl,
|
|
6272
|
-
apiToken: deps.apiToken
|
|
6691
|
+
apiToken: deps.apiToken,
|
|
6692
|
+
browserSignIn: deps.interactive
|
|
6273
6693
|
});
|
|
6274
6694
|
const code = await deps.client.createAgentPairing(deps.applicationId);
|
|
6275
6695
|
captureLog("info", "Handing the preview environment to a coding agent", {
|
|
@@ -6329,11 +6749,12 @@ async function readVerifiedState(client, applicationId) {
|
|
|
6329
6749
|
});
|
|
6330
6750
|
return client.getOnboardingState(applicationId);
|
|
6331
6751
|
}
|
|
6332
|
-
var MCP_SERVER_NAME, PHASE_POLL_MS, PHASE_EXIT_GRACE_MS, KILL_ESCALATION_MS, DEFAULT_PHASE_TIMING, HEADLESS_GUIDANCE;
|
|
6752
|
+
var MCP_SERVER_NAME, PHASE_POLL_MS, PHASE_EXIT_GRACE_MS, KILL_ESCALATION_MS, DEFAULT_PHASE_TIMING, HEADLESS_GUIDANCE, BRANCH_GUIDANCE;
|
|
6333
6753
|
var init_preview_phase = __esm({
|
|
6334
6754
|
"src/core/preview-phase.ts"() {
|
|
6335
6755
|
"use strict";
|
|
6336
6756
|
init_esm_shims();
|
|
6757
|
+
init_src();
|
|
6337
6758
|
init_prompts();
|
|
6338
6759
|
init_debug();
|
|
6339
6760
|
init_logs();
|
|
@@ -6348,6 +6769,7 @@ var init_preview_phase = __esm({
|
|
|
6348
6769
|
killMs: KILL_ESCALATION_MS
|
|
6349
6770
|
};
|
|
6350
6771
|
HEADLESS_GUIDANCE = " You are running with no human to answer you, so where you would ask a question, choose the safer option and say which you chose. You are not finished when you have tried - you are finished when the preview is verified. If a deploy fails, read its logs, fix the cause and deploy again. Never report success you have not confirmed.";
|
|
6772
|
+
BRANCH_GUIDANCE = ` Before you change any file: if you are not already on the \`${INTEGRATION_BRANCH}\` branch, create it off the repo's default branch and switch to it now - read the default branch from the remote (\`git symbolic-ref refs/remotes/origin/HEAD\`) rather than assuming it is called main, and never commit onto it. If the working tree already has uncommitted changes they are the developer's: branch off the current HEAD instead and keep them out of your commits. Push the branch before you name it to Autonoma.`;
|
|
6351
6773
|
}
|
|
6352
6774
|
});
|
|
6353
6775
|
|
|
@@ -6367,7 +6789,8 @@ async function runSdkRepairPhase(deps) {
|
|
|
6367
6789
|
const registration = await deps.launcher.registerMcpServer({
|
|
6368
6790
|
name: MCP_SERVER_NAME,
|
|
6369
6791
|
url: deps.mcpUrl,
|
|
6370
|
-
apiToken: deps.apiToken
|
|
6792
|
+
apiToken: deps.apiToken,
|
|
6793
|
+
browserSignIn: deps.interactive
|
|
6371
6794
|
});
|
|
6372
6795
|
const code = await deps.client.createAgentPairing(deps.applicationId);
|
|
6373
6796
|
captureLog("info", "Handing the SDK and dry run to a coding agent", {
|
|
@@ -6489,7 +6912,7 @@ async function runPreviewHandoff(deps) {
|
|
|
6489
6912
|
applicationId: plan.applicationId,
|
|
6490
6913
|
launcher,
|
|
6491
6914
|
permissionMode,
|
|
6492
|
-
apiToken:
|
|
6915
|
+
apiToken: config.autonomaApiToken,
|
|
6493
6916
|
mcpUrl: resolveMcpUrl(config.autonomaApiUrl),
|
|
6494
6917
|
interactive
|
|
6495
6918
|
});
|
|
@@ -6533,7 +6956,7 @@ async function runSdkRepairHandoff(deps) {
|
|
|
6533
6956
|
applicationId: plan.applicationId,
|
|
6534
6957
|
launcher,
|
|
6535
6958
|
permissionMode,
|
|
6536
|
-
apiToken:
|
|
6959
|
+
apiToken: config.autonomaApiToken,
|
|
6537
6960
|
mcpUrl: resolveMcpUrl(config.autonomaApiUrl),
|
|
6538
6961
|
interactive
|
|
6539
6962
|
});
|
|
@@ -6619,16 +7042,16 @@ var init_notify = __esm({
|
|
|
6619
7042
|
});
|
|
6620
7043
|
|
|
6621
7044
|
// src/core/project-map.ts
|
|
6622
|
-
import { readFile as
|
|
6623
|
-
import { join as
|
|
6624
|
-
import { z as
|
|
7045
|
+
import { readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
|
|
7046
|
+
import { join as join15 } from "path";
|
|
7047
|
+
import { z as z36 } from "zod";
|
|
6625
7048
|
async function saveProjectMap(outputDir, map) {
|
|
6626
|
-
await
|
|
7049
|
+
await writeFile6(join15(outputDir, PROJECT_MAP_FILE), JSON.stringify(map, null, 2), "utf-8");
|
|
6627
7050
|
}
|
|
6628
7051
|
async function loadProjectMap(outputDir) {
|
|
6629
|
-
const path3 =
|
|
7052
|
+
const path3 = join15(outputDir, PROJECT_MAP_FILE);
|
|
6630
7053
|
try {
|
|
6631
|
-
const raw = await
|
|
7054
|
+
const raw = await readFile7(path3, "utf-8");
|
|
6632
7055
|
const parsed = ProjectMapSchema.safeParse(JSON.parse(raw));
|
|
6633
7056
|
if (parsed.success) return parsed.data;
|
|
6634
7057
|
debugLog("project-map.json failed schema validation, ignoring it", { path: path3, issues: parsed.error.issues });
|
|
@@ -6730,34 +7153,34 @@ var init_project_map = __esm({
|
|
|
6730
7153
|
"use strict";
|
|
6731
7154
|
init_esm_shims();
|
|
6732
7155
|
init_debug();
|
|
6733
|
-
FrontendEntry =
|
|
6734
|
-
path:
|
|
6735
|
-
framework:
|
|
6736
|
-
dependsOn:
|
|
7156
|
+
FrontendEntry = z36.object({
|
|
7157
|
+
path: z36.string().min(1).describe("Repo-relative path to the frontend app/directory (the UI surface)."),
|
|
7158
|
+
framework: z36.string().describe("Detected UI framework/stack, or 'unknown' if not obvious (e.g. next, react, vue, svelte)."),
|
|
7159
|
+
dependsOn: z36.array(z36.string()).describe(
|
|
6737
7160
|
"Repo-relative paths (each matching a backend in this map) that THIS frontend needs in order to function - the API/service(s) it calls and the data layer(s) that own the records it renders. These are pre-selected when the user picks this frontend, so only list backends this frontend actually depends on. Empty if it needs none."
|
|
6738
7161
|
),
|
|
6739
|
-
why:
|
|
7162
|
+
why: z36.string().min(1).describe("One line: the evidence that made you classify this as a frontend.")
|
|
6740
7163
|
});
|
|
6741
|
-
BackendEntry =
|
|
6742
|
-
path:
|
|
6743
|
-
language:
|
|
6744
|
-
framework:
|
|
6745
|
-
dataLayer:
|
|
6746
|
-
kind:
|
|
6747
|
-
schemaPath:
|
|
7164
|
+
BackendEntry = z36.object({
|
|
7165
|
+
path: z36.string().min(1).describe("Repo-relative path to the backend/API/service or the package that owns the data layer."),
|
|
7166
|
+
language: z36.string().describe("Primary language, or 'unknown' (e.g. typescript, python, go, rust)."),
|
|
7167
|
+
framework: z36.string().describe("Web/service framework or ORM stack, or 'unknown' (e.g. express, hono, fastapi, rails)."),
|
|
7168
|
+
dataLayer: z36.object({
|
|
7169
|
+
kind: z36.string().describe("How models are defined (e.g. prisma, drizzle, sqlalchemy, typeorm, raw-sql, unknown)."),
|
|
7170
|
+
schemaPath: z36.string().optional().describe("Repo-relative path to the schema/models definition, if found.")
|
|
6748
7171
|
}).optional().describe("Where this backend's database models live. Omit if this backend owns no data layer."),
|
|
6749
|
-
why:
|
|
7172
|
+
why: z36.string().min(1).describe("One line: the evidence that made you classify this as a backend.")
|
|
6750
7173
|
});
|
|
6751
|
-
IgnoreEntry =
|
|
6752
|
-
path:
|
|
6753
|
-
why:
|
|
7174
|
+
IgnoreEntry = z36.object({
|
|
7175
|
+
path: z36.string().min(1).describe("Repo-relative path to a directory that is NOT relevant to testing this app."),
|
|
7176
|
+
why: z36.string().min(1).describe("One line: why it is irrelevant (e.g. docs site, infra, tooling, examples, unrelated app).")
|
|
6754
7177
|
});
|
|
6755
|
-
ProjectMapSchema =
|
|
6756
|
-
frontends:
|
|
6757
|
-
backends:
|
|
7178
|
+
ProjectMapSchema = z36.object({
|
|
7179
|
+
frontends: z36.array(FrontendEntry).describe("The UI surface(s) whose pages/flows get tested. Usually 1; may be more."),
|
|
7180
|
+
backends: z36.array(BackendEntry).describe(
|
|
6758
7181
|
"The API/service(s) and data layer(s) that own the models we seed. May be 0 (frontend-only), 1, or many."
|
|
6759
7182
|
),
|
|
6760
|
-
ignore:
|
|
7183
|
+
ignore: z36.array(IgnoreEntry).describe("Directories judged irrelevant to this app's tests, so later steps skip them.")
|
|
6761
7184
|
});
|
|
6762
7185
|
PROJECT_MAP_FILE = "project-map.json";
|
|
6763
7186
|
}
|
|
@@ -6768,11 +7191,11 @@ var parse_entity_audit_exports = {};
|
|
|
6768
7191
|
__export(parse_entity_audit_exports, {
|
|
6769
7192
|
parseEntityNames: () => parseEntityNames
|
|
6770
7193
|
});
|
|
6771
|
-
import { readFile as
|
|
6772
|
-
import { join as
|
|
7194
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
7195
|
+
import { join as join17 } from "path";
|
|
6773
7196
|
async function parseEntityNames(outputDir) {
|
|
6774
7197
|
try {
|
|
6775
|
-
const content = await
|
|
7198
|
+
const content = await readFile9(join17(outputDir, "entity-audit.md"), "utf-8");
|
|
6776
7199
|
const names = [];
|
|
6777
7200
|
for (const line of content.split("\n")) {
|
|
6778
7201
|
const match = line.match(/^\s+-\s+name:\s+(.+)$/);
|
|
@@ -6807,7 +7230,7 @@ var init_count_generated_tests = __esm({
|
|
|
6807
7230
|
});
|
|
6808
7231
|
|
|
6809
7232
|
// src/core/display.ts
|
|
6810
|
-
import { join as
|
|
7233
|
+
import { join as join18 } from "path";
|
|
6811
7234
|
function formatArgs(input, keys) {
|
|
6812
7235
|
const parts = [];
|
|
6813
7236
|
for (const key of keys) {
|
|
@@ -6899,7 +7322,7 @@ function logToStore(store, agentId, info, stats) {
|
|
|
6899
7322
|
const folder = String(tc.input.folder ?? "");
|
|
6900
7323
|
const filename = String(tc.input.filename ?? "");
|
|
6901
7324
|
if (folder && filename) {
|
|
6902
|
-
store.noteWrite(
|
|
7325
|
+
store.noteWrite(join18(TESTS_DIR, folder, normalizeTestFilename(filename)));
|
|
6903
7326
|
}
|
|
6904
7327
|
}
|
|
6905
7328
|
if (tc.name === "bash") {
|
|
@@ -7207,8 +7630,8 @@ var init_agent = __esm({
|
|
|
7207
7630
|
});
|
|
7208
7631
|
|
|
7209
7632
|
// src/core/gitignore.ts
|
|
7210
|
-
import { readFile as
|
|
7211
|
-
import { join as
|
|
7633
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
7634
|
+
import { join as join19, relative as relative2 } from "path";
|
|
7212
7635
|
import { glob as glob2 } from "glob";
|
|
7213
7636
|
async function loadGitignorePatterns(projectRoot) {
|
|
7214
7637
|
const patterns = [
|
|
@@ -7228,10 +7651,10 @@ async function loadGitignorePatterns(projectRoot) {
|
|
|
7228
7651
|
];
|
|
7229
7652
|
const matches = await glob2("**/.gitignore", { cwd: projectRoot, dot: true });
|
|
7230
7653
|
for (const match of matches) {
|
|
7231
|
-
const fullPath =
|
|
7654
|
+
const fullPath = join19(projectRoot, match);
|
|
7232
7655
|
try {
|
|
7233
|
-
const content = await
|
|
7234
|
-
const prefix = relative2(projectRoot,
|
|
7656
|
+
const content = await readFile10(fullPath, "utf-8");
|
|
7657
|
+
const prefix = relative2(projectRoot, join19(projectRoot, match, ".."));
|
|
7235
7658
|
const parsed = parseGitignore(content, prefix);
|
|
7236
7659
|
patterns.push(...parsed);
|
|
7237
7660
|
} catch (err) {
|
|
@@ -7294,7 +7717,7 @@ var init_exec_error = __esm({
|
|
|
7294
7717
|
import { execFile as execFile3 } from "child_process";
|
|
7295
7718
|
import { promisify as promisify2 } from "util";
|
|
7296
7719
|
import { tool } from "ai";
|
|
7297
|
-
import { z as
|
|
7720
|
+
import { z as z37 } from "zod";
|
|
7298
7721
|
function validateCommand(command, allowed) {
|
|
7299
7722
|
const trimmed = command.trim();
|
|
7300
7723
|
if (trimmed.length === 0) return "Empty command";
|
|
@@ -7360,8 +7783,8 @@ var init_bash = __esm({
|
|
|
7360
7783
|
DEFAULT_ALLOWED = /* @__PURE__ */ new Set(["git", "wc", "sort", "head", "tail", "cat", "ls", "find", "diff", "echo"]);
|
|
7361
7784
|
TIMEOUT_MS = 3e4;
|
|
7362
7785
|
MAX_OUTPUT_BYTES = 1024 * 512;
|
|
7363
|
-
inputSchema =
|
|
7364
|
-
command:
|
|
7786
|
+
inputSchema = z37.object({
|
|
7787
|
+
command: z37.string().describe("Shell command to execute")
|
|
7365
7788
|
});
|
|
7366
7789
|
}
|
|
7367
7790
|
});
|
|
@@ -7369,7 +7792,7 @@ var init_bash = __esm({
|
|
|
7369
7792
|
// src/tools/glob.ts
|
|
7370
7793
|
import { tool as tool2 } from "ai";
|
|
7371
7794
|
import { glob as glob3 } from "glob";
|
|
7372
|
-
import { z as
|
|
7795
|
+
import { z as z38 } from "zod";
|
|
7373
7796
|
async function executeGlob(pattern, cwd, ignorePatterns = DEFAULT_IGNORE) {
|
|
7374
7797
|
try {
|
|
7375
7798
|
const matches = await glob3(pattern, {
|
|
@@ -7403,9 +7826,9 @@ var init_glob = __esm({
|
|
|
7403
7826
|
"src/tools/glob.ts"() {
|
|
7404
7827
|
"use strict";
|
|
7405
7828
|
init_esm_shims();
|
|
7406
|
-
inputSchema2 =
|
|
7407
|
-
pattern:
|
|
7408
|
-
cwd:
|
|
7829
|
+
inputSchema2 = z38.object({
|
|
7830
|
+
pattern: z38.string().describe("Glob pattern to match files (e.g. '**/*.ts', 'src/**/*.py')"),
|
|
7831
|
+
cwd: z38.string().optional().describe("Directory to search in. Defaults to working directory.")
|
|
7409
7832
|
});
|
|
7410
7833
|
DEFAULT_IGNORE = [
|
|
7411
7834
|
"**/node_modules/**",
|
|
@@ -7423,7 +7846,7 @@ var init_glob = __esm({
|
|
|
7423
7846
|
import { execFile as execFile4 } from "child_process";
|
|
7424
7847
|
import { promisify as promisify3 } from "util";
|
|
7425
7848
|
import { tool as tool3 } from "ai";
|
|
7426
|
-
import { z as
|
|
7849
|
+
import { z as z39 } from "zod";
|
|
7427
7850
|
function buildGrepTool(workingDirectory) {
|
|
7428
7851
|
return tool3({
|
|
7429
7852
|
description: "Search file contents with ripgrep. Returns matching lines with file paths and line numbers.",
|
|
@@ -7467,10 +7890,10 @@ var init_grep = __esm({
|
|
|
7467
7890
|
init_esm_shims();
|
|
7468
7891
|
init_exec_error();
|
|
7469
7892
|
execFileAsync3 = promisify3(execFile4);
|
|
7470
|
-
inputSchema3 =
|
|
7471
|
-
pattern:
|
|
7472
|
-
glob:
|
|
7473
|
-
path:
|
|
7893
|
+
inputSchema3 = z39.object({
|
|
7894
|
+
pattern: z39.string().describe("Regex pattern to search for in file contents"),
|
|
7895
|
+
glob: z39.string().optional().describe("Glob to filter files (e.g. '*.ts')"),
|
|
7896
|
+
path: z39.string().optional().describe("File or directory to search in")
|
|
7474
7897
|
});
|
|
7475
7898
|
}
|
|
7476
7899
|
});
|
|
@@ -7478,10 +7901,10 @@ var init_grep = __esm({
|
|
|
7478
7901
|
// src/tools/list-directory.ts
|
|
7479
7902
|
import { readdir } from "fs/promises";
|
|
7480
7903
|
import { stat } from "fs/promises";
|
|
7481
|
-
import { join as
|
|
7904
|
+
import { join as join20, relative as relative3 } from "path";
|
|
7482
7905
|
import { tool as tool4 } from "ai";
|
|
7483
7906
|
import { minimatch } from "minimatch";
|
|
7484
|
-
import { z as
|
|
7907
|
+
import { z as z40 } from "zod";
|
|
7485
7908
|
function buildMatcher(patterns) {
|
|
7486
7909
|
const positive = patterns.filter((p) => !p.startsWith("!"));
|
|
7487
7910
|
const negative = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
|
|
@@ -7503,7 +7926,7 @@ async function buildTree(dirPath, maxDepth, currentDepth, isIgnored, relativeBas
|
|
|
7503
7926
|
const withTypes = [];
|
|
7504
7927
|
for (const name of rawEntries) {
|
|
7505
7928
|
try {
|
|
7506
|
-
const s = await stat(
|
|
7929
|
+
const s = await stat(join20(dirPath, name));
|
|
7507
7930
|
withTypes.push({ name, isDir: s.isDirectory() });
|
|
7508
7931
|
} catch {
|
|
7509
7932
|
withTypes.push({ name, isDir: false });
|
|
@@ -7523,7 +7946,7 @@ async function buildTree(dirPath, maxDepth, currentDepth, isIgnored, relativeBas
|
|
|
7523
7946
|
}
|
|
7524
7947
|
if (entry.isDir) {
|
|
7525
7948
|
const children = await buildTree(
|
|
7526
|
-
|
|
7949
|
+
join20(dirPath, entry.name),
|
|
7527
7950
|
maxDepth,
|
|
7528
7951
|
currentDepth + 1,
|
|
7529
7952
|
isIgnored,
|
|
@@ -7561,10 +7984,10 @@ async function buildListDirectoryTool(workingDirectory) {
|
|
|
7561
7984
|
const isIgnored = buildMatcher(patterns);
|
|
7562
7985
|
return tool4({
|
|
7563
7986
|
description: "List directory structure as a tree. Use this for an overview of the project layout. Start at the root (path='.') with depth 3, then increase depth or narrow path if needed. Do NOT call this on every subdirectory - use glob to find specific files instead. Returns cached result if the same path+depth was already requested.",
|
|
7564
|
-
inputSchema:
|
|
7565
|
-
path:
|
|
7566
|
-
depth:
|
|
7567
|
-
gitignore:
|
|
7987
|
+
inputSchema: z40.object({
|
|
7988
|
+
path: z40.string().default(".").describe("Directory path relative to project root. Defaults to root."),
|
|
7989
|
+
depth: z40.number().min(1).max(15).default(10).describe("Max depth to traverse (1-15). Default 10."),
|
|
7990
|
+
gitignore: z40.boolean().describe(
|
|
7568
7991
|
"Whether to respect the gitignore or to ignore it. true will respect it. false will ignore it. Default true"
|
|
7569
7992
|
).default(true)
|
|
7570
7993
|
}),
|
|
@@ -7576,7 +7999,7 @@ async function buildListDirectoryTool(workingDirectory) {
|
|
|
7576
7999
|
};
|
|
7577
8000
|
}
|
|
7578
8001
|
seen.add(cacheKey);
|
|
7579
|
-
const targetDir = input.path === "." ? workingDirectory :
|
|
8002
|
+
const targetDir = input.path === "." ? workingDirectory : join20(workingDirectory, input.path);
|
|
7580
8003
|
try {
|
|
7581
8004
|
const s = await stat(targetDir);
|
|
7582
8005
|
if (!s.isDirectory()) {
|
|
@@ -7612,10 +8035,10 @@ var init_list_directory = __esm({
|
|
|
7612
8035
|
});
|
|
7613
8036
|
|
|
7614
8037
|
// src/tools/read-file.ts
|
|
7615
|
-
import { readFile as
|
|
8038
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
7616
8039
|
import { relative as relative4, resolve as resolve2 } from "path";
|
|
7617
8040
|
import { tool as tool5 } from "ai";
|
|
7618
|
-
import { z as
|
|
8041
|
+
import { z as z41 } from "zod";
|
|
7619
8042
|
function resolveSandboxedPath(workingDirectory, filePath) {
|
|
7620
8043
|
const absolutePath = resolve2(workingDirectory, filePath);
|
|
7621
8044
|
const relativePath = relative4(workingDirectory, absolutePath);
|
|
@@ -7640,7 +8063,7 @@ async function executeReadFile(workingDirectory, filePath, offset, limit) {
|
|
|
7640
8063
|
const resolved2 = resolveSandboxedPath(workingDirectory, filePath);
|
|
7641
8064
|
if ("error" in resolved2) return resolved2;
|
|
7642
8065
|
try {
|
|
7643
|
-
const content = await
|
|
8066
|
+
const content = await readFile11(resolved2.absolutePath, "utf-8");
|
|
7644
8067
|
const sliced = sliceLines(content, offset ?? 0, limit ?? MAX_LINES);
|
|
7645
8068
|
if (sliced.numbered.length > MAX_OUTPUT_BYTES2) {
|
|
7646
8069
|
sliced.numbered = sliced.numbered.slice(0, MAX_OUTPUT_BYTES2) + `
|
|
@@ -7673,10 +8096,10 @@ var init_read_file = __esm({
|
|
|
7673
8096
|
init_esm_shims();
|
|
7674
8097
|
MAX_LINES = 2e3;
|
|
7675
8098
|
MAX_OUTPUT_BYTES2 = 256 * 1024;
|
|
7676
|
-
inputSchema4 =
|
|
7677
|
-
filePath:
|
|
7678
|
-
offset:
|
|
7679
|
-
limit:
|
|
8099
|
+
inputSchema4 = z41.object({
|
|
8100
|
+
filePath: z41.string().describe("Path to the file (absolute or relative to working directory)"),
|
|
8101
|
+
offset: z41.number().int().min(0).optional().describe("Line number to start reading from (0-based)"),
|
|
8102
|
+
limit: z41.number().int().min(1).optional().describe("Maximum number of lines to read")
|
|
7680
8103
|
});
|
|
7681
8104
|
}
|
|
7682
8105
|
});
|
|
@@ -7699,10 +8122,10 @@ var init_pick_string = __esm({
|
|
|
7699
8122
|
|
|
7700
8123
|
// src/tools/subagent.ts
|
|
7701
8124
|
import { ToolLoopAgent as ToolLoopAgent2, hasToolCall as hasToolCall2, stepCountIs as stepCountIs2, tool as tool6 } from "ai";
|
|
7702
|
-
import { z as
|
|
8125
|
+
import { z as z42 } from "zod";
|
|
7703
8126
|
function buildSubagentTools(workingDirectory, onFileRead) {
|
|
7704
8127
|
const baseReadFile = buildReadFileTool(workingDirectory);
|
|
7705
|
-
const
|
|
8128
|
+
const readFile27 = onFileRead ? tool6({
|
|
7706
8129
|
description: baseReadFile.description,
|
|
7707
8130
|
inputSchema: baseReadFile.inputSchema,
|
|
7708
8131
|
execute: async (input, options) => {
|
|
@@ -7715,7 +8138,7 @@ function buildSubagentTools(workingDirectory, onFileRead) {
|
|
|
7715
8138
|
bash: buildBashTool(workingDirectory),
|
|
7716
8139
|
glob: buildGlobTool(workingDirectory),
|
|
7717
8140
|
grep: buildGrepTool(workingDirectory),
|
|
7718
|
-
read_file:
|
|
8141
|
+
read_file: readFile27
|
|
7719
8142
|
};
|
|
7720
8143
|
}
|
|
7721
8144
|
function buildSubagentTool(model, workingDirectory, onHeartbeat, onFileRead) {
|
|
@@ -7772,11 +8195,11 @@ var init_subagent = __esm({
|
|
|
7772
8195
|
init_glob();
|
|
7773
8196
|
init_grep();
|
|
7774
8197
|
init_read_file();
|
|
7775
|
-
inputSchema5 =
|
|
7776
|
-
instruction:
|
|
8198
|
+
inputSchema5 = z42.object({
|
|
8199
|
+
instruction: z42.string().describe("Focused task for the subagent. Be specific about files and patterns to investigate.")
|
|
7777
8200
|
});
|
|
7778
|
-
resultSchema =
|
|
7779
|
-
findings:
|
|
8201
|
+
resultSchema = z42.object({
|
|
8202
|
+
findings: z42.string().describe("Summary of what was found")
|
|
7780
8203
|
});
|
|
7781
8204
|
SYSTEM_PROMPT = `You are a code research assistant. You have tools to explore a codebase: bash (shell commands, mainly git), glob (find files), grep (search content), and read_file (read files).
|
|
7782
8205
|
|
|
@@ -7787,10 +8210,10 @@ Be thorough but focused - only investigate what's relevant to your instruction.`
|
|
|
7787
8210
|
});
|
|
7788
8211
|
|
|
7789
8212
|
// src/tools/write-file.ts
|
|
7790
|
-
import { writeFile as
|
|
8213
|
+
import { writeFile as writeFile7, mkdir as mkdir3 } from "fs/promises";
|
|
7791
8214
|
import { dirname as dirname3, relative as relative5, resolve as resolve3 } from "path";
|
|
7792
8215
|
import { tool as tool7 } from "ai";
|
|
7793
|
-
import { z as
|
|
8216
|
+
import { z as z43 } from "zod";
|
|
7794
8217
|
async function executeWriteFile(outputDirectory, filePath, content) {
|
|
7795
8218
|
const cleaned = filePath.replace(/^autonoma\//, "");
|
|
7796
8219
|
const absolutePath = resolve3(outputDirectory, cleaned);
|
|
@@ -7800,7 +8223,7 @@ async function executeWriteFile(outputDirectory, filePath, content) {
|
|
|
7800
8223
|
}
|
|
7801
8224
|
try {
|
|
7802
8225
|
await mkdir3(dirname3(absolutePath), { recursive: true });
|
|
7803
|
-
await
|
|
8226
|
+
await writeFile7(absolutePath, content, "utf-8");
|
|
7804
8227
|
return { path: relativePath, bytesWritten: content.length };
|
|
7805
8228
|
} catch (err) {
|
|
7806
8229
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7819,21 +8242,21 @@ var init_write_file = __esm({
|
|
|
7819
8242
|
"src/tools/write-file.ts"() {
|
|
7820
8243
|
"use strict";
|
|
7821
8244
|
init_esm_shims();
|
|
7822
|
-
inputSchema6 =
|
|
7823
|
-
filePath:
|
|
7824
|
-
content:
|
|
8245
|
+
inputSchema6 = z43.object({
|
|
8246
|
+
filePath: z43.string().describe("Path to write (absolute or relative to output directory)"),
|
|
8247
|
+
content: z43.string().describe("File content to write")
|
|
7825
8248
|
});
|
|
7826
8249
|
}
|
|
7827
8250
|
});
|
|
7828
8251
|
|
|
7829
8252
|
// src/tools/ask-user.ts
|
|
7830
8253
|
import { tool as tool8 } from "ai";
|
|
7831
|
-
import { z as
|
|
8254
|
+
import { z as z44 } from "zod";
|
|
7832
8255
|
function buildAskUserTool() {
|
|
7833
8256
|
return tool8({
|
|
7834
8257
|
description: "Ask the user a question ONLY when the answer is truly unknowable from the codebase. Valid reasons: untyped JSON/JSONB field schemas, business rules not in code, config values not in source. NEVER ask about: field names (read the schema), field types (read the ORM model), enum values (read the code), relationships (read foreign keys), numeric values (read the seed data or defaults). If you can find it by reading a file, DO NOT ask - read the file instead.",
|
|
7835
|
-
inputSchema:
|
|
7836
|
-
question:
|
|
8258
|
+
inputSchema: z44.object({
|
|
8259
|
+
question: z44.string().describe(
|
|
7837
8260
|
"A clear, plain-language question. State exactly what you need to know and why you can't find it in code. BAD: 'What are the decimal values for checking_balance?' - GOOD: 'Your Account model has a metadata JSON column with no type definition. What fields go inside it?'"
|
|
7838
8261
|
)
|
|
7839
8262
|
}),
|
|
@@ -7893,7 +8316,7 @@ var init_tools = __esm({
|
|
|
7893
8316
|
|
|
7894
8317
|
// src/agents/00-project-mapper/coverage.ts
|
|
7895
8318
|
import { readdir as readdir2 } from "fs/promises";
|
|
7896
|
-
import { join as
|
|
8319
|
+
import { join as join21 } from "path";
|
|
7897
8320
|
async function findUncoveredDirs(projectRoot, map) {
|
|
7898
8321
|
const entries = [...map.frontends, ...map.backends, ...map.ignore].map(
|
|
7899
8322
|
(e) => e.path.replace(/^\.\//, "").replace(/\/+$/, "")
|
|
@@ -7917,7 +8340,7 @@ async function findUncoveredDirs(projectRoot, map) {
|
|
|
7917
8340
|
const childRel = rel === "" ? child.name : `${rel}/${child.name}`;
|
|
7918
8341
|
if (underEntry(childRel)) continue;
|
|
7919
8342
|
if (hasEntryWithin(childRel)) {
|
|
7920
|
-
if (depth + 1 < MAX_DEPTH) await walk(
|
|
8343
|
+
if (depth + 1 < MAX_DEPTH) await walk(join21(abs, child.name), childRel, depth + 1);
|
|
7921
8344
|
} else {
|
|
7922
8345
|
out.push(childRel);
|
|
7923
8346
|
}
|
|
@@ -7944,7 +8367,7 @@ __export(project_mapper_exports, {
|
|
|
7944
8367
|
runProjectMapper: () => runProjectMapper
|
|
7945
8368
|
});
|
|
7946
8369
|
import { tool as tool9 } from "ai";
|
|
7947
|
-
import { z as
|
|
8370
|
+
import { z as z45 } from "zod";
|
|
7948
8371
|
async function runProjectMapper(input) {
|
|
7949
8372
|
const model = getModel(input.modelId);
|
|
7950
8373
|
const { logger, onStepFinish } = buildDefaultStepLogger("project-map", MAX_STEPS);
|
|
@@ -7978,7 +8401,7 @@ First build the full inventory (workspace/package manifests + top-level director
|
|
|
7978
8401
|
}),
|
|
7979
8402
|
finish: tool9({
|
|
7980
8403
|
description: "End this step. Call once set_project_map has recorded a complete map.",
|
|
7981
|
-
inputSchema:
|
|
8404
|
+
inputSchema: z45.object({}),
|
|
7982
8405
|
execute: () => {
|
|
7983
8406
|
if (captured == null) {
|
|
7984
8407
|
return { error: "Cannot finish: no map recorded - call set_project_map first." };
|
|
@@ -8022,7 +8445,7 @@ __export(pages_finder_exports, {
|
|
|
8022
8445
|
import { existsSync } from "fs";
|
|
8023
8446
|
import * as path2 from "path";
|
|
8024
8447
|
import { tool as tool10 } from "ai";
|
|
8025
|
-
import { z as
|
|
8448
|
+
import { z as z46 } from "zod";
|
|
8026
8449
|
async function runPageFinder(input) {
|
|
8027
8450
|
const model = getModel(input.modelId);
|
|
8028
8451
|
const pageCollector = new PageCollector();
|
|
@@ -8057,12 +8480,12 @@ ${input.extraMessage}`;
|
|
|
8057
8480
|
}),
|
|
8058
8481
|
view_pages: tool10({
|
|
8059
8482
|
description: "use this tool to view all the pages that you already added",
|
|
8060
|
-
inputSchema:
|
|
8483
|
+
inputSchema: z46.object(),
|
|
8061
8484
|
execute: () => pageCollector.viewPages()
|
|
8062
8485
|
}),
|
|
8063
8486
|
finish: tool10({
|
|
8064
8487
|
description: "End this step. Call once every page in the codebase has been added via add_page.",
|
|
8065
|
-
inputSchema:
|
|
8488
|
+
inputSchema: z46.object({}),
|
|
8066
8489
|
execute: () => {
|
|
8067
8490
|
if (pageCollector.pages.size === 0) {
|
|
8068
8491
|
return {
|
|
@@ -8089,10 +8512,10 @@ var init_pages_finder = __esm({
|
|
|
8089
8512
|
init_agent();
|
|
8090
8513
|
init_model();
|
|
8091
8514
|
init_tools();
|
|
8092
|
-
Page =
|
|
8093
|
-
route:
|
|
8094
|
-
path:
|
|
8095
|
-
description:
|
|
8515
|
+
Page = z46.object({
|
|
8516
|
+
route: z46.string().min(1),
|
|
8517
|
+
path: z46.string().min(1),
|
|
8518
|
+
description: z46.string().min(10)
|
|
8096
8519
|
});
|
|
8097
8520
|
PageCollector = class {
|
|
8098
8521
|
// the key is the path
|
|
@@ -8288,15 +8711,15 @@ var kb_generator_exports = {};
|
|
|
8288
8711
|
__export(kb_generator_exports, {
|
|
8289
8712
|
runKBGenerator: () => runKBGenerator
|
|
8290
8713
|
});
|
|
8291
|
-
import { readFile as
|
|
8292
|
-
import { join as
|
|
8714
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
8715
|
+
import { join as join22, resolve as resolve5 } from "path";
|
|
8293
8716
|
import { tool as tool11 } from "ai";
|
|
8294
|
-
import { z as
|
|
8717
|
+
import { z as z47 } from "zod";
|
|
8295
8718
|
function buildRegisterPagesTool(tracker) {
|
|
8296
8719
|
return tool11({
|
|
8297
8720
|
description: "Register ALL page/route files discovered via glob. Call this ONCE after globbing for page files. The system will track which ones you've read and block finish until all are covered.",
|
|
8298
|
-
inputSchema:
|
|
8299
|
-
pages:
|
|
8721
|
+
inputSchema: z47.object({
|
|
8722
|
+
pages: z47.array(z47.string()).describe("All page file paths found by glob")
|
|
8300
8723
|
}),
|
|
8301
8724
|
execute: async (input) => {
|
|
8302
8725
|
tracker.register(input.pages);
|
|
@@ -8310,7 +8733,7 @@ function buildRegisterPagesTool(tracker) {
|
|
|
8310
8733
|
function buildPageCoverageTool(tracker) {
|
|
8311
8734
|
return tool11({
|
|
8312
8735
|
description: "Check how many registered pages you've read vs how many remain.",
|
|
8313
|
-
inputSchema:
|
|
8736
|
+
inputSchema: z47.object({}),
|
|
8314
8737
|
execute: async () => tracker.coverage()
|
|
8315
8738
|
});
|
|
8316
8739
|
}
|
|
@@ -8321,9 +8744,9 @@ function requiredReads(total) {
|
|
|
8321
8744
|
function buildFinishTool(tracker, onFinish) {
|
|
8322
8745
|
return tool11({
|
|
8323
8746
|
description: "Call when you have finished generating the knowledge base. BLOCKED until you have read enough of the registered routes (every route on a small app; a strong majority on a large one) - call page_coverage first to check how many remain.",
|
|
8324
|
-
inputSchema:
|
|
8325
|
-
summary:
|
|
8326
|
-
artifacts:
|
|
8747
|
+
inputSchema: z47.object({
|
|
8748
|
+
summary: z47.string().describe("Summary of what was generated"),
|
|
8749
|
+
artifacts: z47.array(z47.string()).describe("List of files written")
|
|
8327
8750
|
}),
|
|
8328
8751
|
execute: async (input) => {
|
|
8329
8752
|
const cov = tracker.coverage();
|
|
@@ -8402,8 +8825,8 @@ Output files:
|
|
|
8402
8825
|
const agentConfig = buildKbAgentConfig(tracker, model, input, onStepFinish, setResult);
|
|
8403
8826
|
await runAgent(agentConfig, prompt, () => result);
|
|
8404
8827
|
logger.summary();
|
|
8405
|
-
const autonomaPath =
|
|
8406
|
-
const autonomaExists = await
|
|
8828
|
+
const autonomaPath = join22(input.outputDir, "AUTONOMA.md");
|
|
8829
|
+
const autonomaExists = await readFile12(autonomaPath, "utf-8").then(() => true).catch((err) => {
|
|
8407
8830
|
debugLog("AUTONOMA.md not found while checking step completion", { err });
|
|
8408
8831
|
return false;
|
|
8409
8832
|
});
|
|
@@ -8589,17 +9012,17 @@ var entity_audit_exports = {};
|
|
|
8589
9012
|
__export(entity_audit_exports, {
|
|
8590
9013
|
runEntityAudit: () => runEntityAudit
|
|
8591
9014
|
});
|
|
8592
|
-
import { readFile as
|
|
8593
|
-
import { join as
|
|
9015
|
+
import { readFile as readFile13, writeFile as writeFile8 } from "fs/promises";
|
|
9016
|
+
import { join as join23 } from "path";
|
|
8594
9017
|
import { tool as tool12 } from "ai";
|
|
8595
9018
|
import { glob as glob4 } from "glob";
|
|
8596
|
-
import { z as
|
|
9019
|
+
import { z as z48 } from "zod";
|
|
8597
9020
|
function buildRegisterModelsTool(tracker) {
|
|
8598
9021
|
return tool12({
|
|
8599
9022
|
description: "Register ALL database models discovered via grep. Call this ONCE after grepping for model definitions. After registering, use next_model to process them one at a time.",
|
|
8600
|
-
inputSchema:
|
|
8601
|
-
models:
|
|
8602
|
-
framework:
|
|
9023
|
+
inputSchema: z48.object({
|
|
9024
|
+
models: z48.array(z48.string()).describe("All model/table names found by grep"),
|
|
9025
|
+
framework: z48.string().describe("Database framework detected (e.g. 'sqlalchemy', 'prisma', 'drizzle')")
|
|
8603
9026
|
}),
|
|
8604
9027
|
execute: async (input) => {
|
|
8605
9028
|
tracker.register(input.models);
|
|
@@ -8615,7 +9038,7 @@ function buildRegisterModelsTool(tracker) {
|
|
|
8615
9038
|
function buildNextModelTool(tracker) {
|
|
8616
9039
|
return tool12({
|
|
8617
9040
|
description: "Get the next model to audit from the queue. If you called next_model before without calling mark_model_audited, the previous model is auto-skipped (marked as no creation path found). Returns done:true when all models are processed.",
|
|
8618
|
-
inputSchema:
|
|
9041
|
+
inputSchema: z48.object({}),
|
|
8619
9042
|
execute: async () => {
|
|
8620
9043
|
const next = tracker.nextModel();
|
|
8621
9044
|
if (!next) {
|
|
@@ -8635,17 +9058,17 @@ function buildNextModelTool(tracker) {
|
|
|
8635
9058
|
function buildMarkModelAuditedTool(tracker) {
|
|
8636
9059
|
return tool12({
|
|
8637
9060
|
description: "Mark a model as audited after you have determined its creation paths. Call this for EACH model after reading its creation code and determining independently_created + created_by. Include creation_function (e.g. 'UserService.create'), side_effects (list of things the creation does beyond the model itself), and for each created_by entry include owner, via (function name), and why (one sentence explaining the relationship).",
|
|
8638
|
-
inputSchema:
|
|
8639
|
-
model:
|
|
8640
|
-
independently_created:
|
|
8641
|
-
creation_file:
|
|
8642
|
-
creation_function:
|
|
8643
|
-
side_effects:
|
|
8644
|
-
created_by:
|
|
8645
|
-
|
|
8646
|
-
owner:
|
|
8647
|
-
via:
|
|
8648
|
-
why:
|
|
9061
|
+
inputSchema: z48.object({
|
|
9062
|
+
model: z48.string().describe("Model name"),
|
|
9063
|
+
independently_created: z48.boolean(),
|
|
9064
|
+
creation_file: z48.string().optional().describe("File containing the creation function"),
|
|
9065
|
+
creation_function: z48.string().optional().describe("Function/method name (e.g. 'UserService.create' or 'create_user')"),
|
|
9066
|
+
side_effects: z48.array(z48.string()).optional().describe("Side effects of creation (e.g. 'creates default Settings row', 'hashes password')"),
|
|
9067
|
+
created_by: z48.array(
|
|
9068
|
+
z48.object({
|
|
9069
|
+
owner: z48.string().describe("Owner model name"),
|
|
9070
|
+
via: z48.string().optional().describe("Function that creates this model (e.g. 'OrganizationService.create')"),
|
|
9071
|
+
why: z48.string().optional().describe("One sentence explaining why this model is created as a side effect")
|
|
8649
9072
|
})
|
|
8650
9073
|
).describe("List of owner models that create this as a side effect, empty array if none")
|
|
8651
9074
|
}),
|
|
@@ -8684,16 +9107,16 @@ function buildMarkModelAuditedTool(tracker) {
|
|
|
8684
9107
|
function buildModelCoverageTool(tracker) {
|
|
8685
9108
|
return tool12({
|
|
8686
9109
|
description: "Check how many registered models you've audited vs how many remain.",
|
|
8687
|
-
inputSchema:
|
|
9110
|
+
inputSchema: z48.object({}),
|
|
8688
9111
|
execute: async () => tracker.coverage()
|
|
8689
9112
|
});
|
|
8690
9113
|
}
|
|
8691
9114
|
function buildFinishTool2(tracker, onFinish) {
|
|
8692
9115
|
return tool12({
|
|
8693
9116
|
description: "Call when entity audit is complete. BLOCKED if there are unaudited models - call model_coverage first to check.",
|
|
8694
|
-
inputSchema:
|
|
8695
|
-
summary:
|
|
8696
|
-
artifacts:
|
|
9117
|
+
inputSchema: z48.object({
|
|
9118
|
+
summary: z48.string().describe("Summary of the audit"),
|
|
9119
|
+
artifacts: z48.array(z48.string()).describe("Files written")
|
|
8697
9120
|
}),
|
|
8698
9121
|
execute: async (input) => {
|
|
8699
9122
|
const cov = tracker.coverage();
|
|
@@ -8721,7 +9144,7 @@ async function findPrismaSchema(projectRoot) {
|
|
|
8721
9144
|
return candidates[0] ?? void 0;
|
|
8722
9145
|
}
|
|
8723
9146
|
async function extractPrismaModels(schemaPath) {
|
|
8724
|
-
const content = await
|
|
9147
|
+
const content = await readFile13(schemaPath, "utf-8");
|
|
8725
9148
|
return content.split("\n").filter((line) => line.startsWith("model ")).map((line) => line.split(/\s+/)[1]).filter((name) => name != null);
|
|
8726
9149
|
}
|
|
8727
9150
|
async function detectFrameworkAndModels(projectRoot) {
|
|
@@ -8808,8 +9231,8 @@ ${formatException(err)}`);
|
|
|
8808
9231
|
logger.summary();
|
|
8809
9232
|
const writeCanonicalAudit = async () => {
|
|
8810
9233
|
if (tracker.auditedModels.size === 0) return void 0;
|
|
8811
|
-
const auditPath =
|
|
8812
|
-
await
|
|
9234
|
+
const auditPath = join23(input.outputDir, "entity-audit.md");
|
|
9235
|
+
await writeFile8(auditPath, tracker.generateAuditMarkdown(), "utf-8");
|
|
8813
9236
|
return auditPath;
|
|
8814
9237
|
};
|
|
8815
9238
|
const canonicalPath = await writeCanonicalAudit();
|
|
@@ -8823,9 +9246,9 @@ ${formatException(err)}`);
|
|
|
8823
9246
|
}
|
|
8824
9247
|
const reviewed = result;
|
|
8825
9248
|
if (!reviewed) {
|
|
8826
|
-
const auditPath =
|
|
9249
|
+
const auditPath = join23(input.outputDir, "entity-audit.md");
|
|
8827
9250
|
try {
|
|
8828
|
-
await
|
|
9251
|
+
await readFile13(auditPath, "utf-8");
|
|
8829
9252
|
return {
|
|
8830
9253
|
success: true,
|
|
8831
9254
|
artifacts: ["entity-audit.md"],
|
|
@@ -9029,15 +9452,37 @@ anything the database will refuse a second copy of - must embed a token INSIDE a
|
|
|
9029
9452
|
otherwise real-looking value ("admin+{{${TEST_RUN_ID_TOKEN}}}@acme.test",
|
|
9030
9453
|
"acme-{{${TEST_RUN_SHORT_ID_TOKEN}}}"), never replace the value entirely. Two tests
|
|
9031
9454
|
run at the same time seed this scenario twice; without the token the second one fails
|
|
9032
|
-
on a unique constraint.
|
|
9033
|
-
|
|
9455
|
+
on a unique constraint. No other {{token}} or bare {variable} exists - inventing one
|
|
9456
|
+
is rejected.
|
|
9457
|
+
|
|
9458
|
+
## Data the app compares against the current time
|
|
9459
|
+
This scenario is written once and seeded again, unchanged, before every run for as long
|
|
9460
|
+
as it lives. So a value the application compares against the current time is not static
|
|
9461
|
+
data: a record meant to be upcoming, overdue, expiring soon, still valid, or recent is a
|
|
9462
|
+
fact about WHEN THE TEST RUNS. Written as a calendar date it is right on the day you
|
|
9463
|
+
write it and quietly wrong ever after, and the failure it causes reads as a broken
|
|
9464
|
+
product rather than as stale data.
|
|
9465
|
+
|
|
9466
|
+
For every date or time you write, ask one question: does the app branch on this being
|
|
9467
|
+
before or after now? Look for the answer in its queries - a list split into current and
|
|
9468
|
+
past, a filter against an expiry, a state derived from a deadline. Do not guess from the
|
|
9469
|
+
field name.
|
|
9470
|
+
|
|
9471
|
+
Where the answer is yes, write the value as an offset from the moment of seeding
|
|
9472
|
+
("starts 3 days after seeding, at 10:00", "ended 2 weeks before seeding") rather than a
|
|
9473
|
+
date. Where it is no - a recurring weekly schedule, a date of birth, a timestamp nothing
|
|
9474
|
+
filters on - a concrete date is correct and an offset would only obscure it.
|
|
9475
|
+
|
|
9476
|
+
An offset is prose in your table, not a token: there is no {{token}} for time, and
|
|
9477
|
+
inventing one is rejected like any other. The recipe builder turns what you write into a
|
|
9478
|
+
value its factories compute at seeding time.`;
|
|
9034
9479
|
}
|
|
9035
9480
|
});
|
|
9036
9481
|
|
|
9037
9482
|
// src/agents/03-scenario-recipe/scenario-table.ts
|
|
9038
|
-
import { readFile as
|
|
9039
|
-
import { join as
|
|
9040
|
-
import matter2 from "gray-matter";
|
|
9483
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
9484
|
+
import { join as join24 } from "path";
|
|
9485
|
+
import matter2 from "@11ty/gray-matter";
|
|
9041
9486
|
function validateScenarioIsConcrete(content) {
|
|
9042
9487
|
const errors = findUnknownRecipeTokens(content).map(
|
|
9043
9488
|
(token) => `unknown token "{{${token}}}" - scenario values must be concrete. Replace it with the exact static value; the only tokens Autonoma substitutes are ${BUILT_IN_RECIPE_TOKEN_LIST}.`
|
|
@@ -9071,22 +9516,22 @@ __export(scenario_recipe_exports, {
|
|
|
9071
9516
|
feedbackToScenario: () => feedbackToScenario,
|
|
9072
9517
|
runScenarioRecipe: () => runScenarioRecipe
|
|
9073
9518
|
});
|
|
9074
|
-
import { readFile as
|
|
9075
|
-
import { join as
|
|
9519
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
9520
|
+
import { join as join25 } from "path";
|
|
9076
9521
|
import { tool as tool13 } from "ai";
|
|
9077
|
-
import { z as
|
|
9522
|
+
import { z as z49 } from "zod";
|
|
9078
9523
|
function buildFinishTool3(requiredEntities, outputDir, onFinish) {
|
|
9079
9524
|
return tool13({
|
|
9080
9525
|
description: "Call when scenario design is complete and scenarios.md is written. BLOCKED if any required entities are missing from the scenario.",
|
|
9081
|
-
inputSchema:
|
|
9082
|
-
summary:
|
|
9083
|
-
entityCount:
|
|
9084
|
-
artifacts:
|
|
9526
|
+
inputSchema: z49.object({
|
|
9527
|
+
summary: z49.string().describe("Summary of the scenario"),
|
|
9528
|
+
entityCount: z49.number().describe("Number of entity types in the scenario"),
|
|
9529
|
+
artifacts: z49.array(z49.string()).describe("Files written")
|
|
9085
9530
|
}),
|
|
9086
9531
|
execute: async (input) => {
|
|
9087
9532
|
let content;
|
|
9088
9533
|
try {
|
|
9089
|
-
content = await
|
|
9534
|
+
content = await readFile15(join25(outputDir, "scenarios.md"), "utf-8");
|
|
9090
9535
|
} catch {
|
|
9091
9536
|
return { error: "Cannot finish: scenarios.md not found. Write it first." };
|
|
9092
9537
|
}
|
|
@@ -9165,9 +9610,9 @@ When done, call finish.`;
|
|
|
9165
9610
|
logger.summary();
|
|
9166
9611
|
const reviewed = result;
|
|
9167
9612
|
if (!reviewed) {
|
|
9168
|
-
const scenariosPath =
|
|
9613
|
+
const scenariosPath = join25(input.outputDir, "scenarios.md");
|
|
9169
9614
|
try {
|
|
9170
|
-
await
|
|
9615
|
+
await readFile15(scenariosPath, "utf-8");
|
|
9171
9616
|
return {
|
|
9172
9617
|
success: true,
|
|
9173
9618
|
artifacts: ["scenarios.md"],
|
|
@@ -9226,12 +9671,12 @@ var init_scenario_recipe = __esm({
|
|
|
9226
9671
|
});
|
|
9227
9672
|
|
|
9228
9673
|
// src/agents/04-recipe-builder/entity-order.ts
|
|
9229
|
-
import { readFile as
|
|
9230
|
-
import { join as
|
|
9231
|
-
import matter3 from "gray-matter";
|
|
9232
|
-
import { z as
|
|
9674
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
9675
|
+
import { join as join26 } from "path";
|
|
9676
|
+
import matter3 from "@11ty/gray-matter";
|
|
9677
|
+
import { z as z50 } from "zod";
|
|
9233
9678
|
async function parseEntityAudit(outputDir) {
|
|
9234
|
-
const raw = await
|
|
9679
|
+
const raw = await readFile16(join26(outputDir, "entity-audit.md"), "utf-8");
|
|
9235
9680
|
try {
|
|
9236
9681
|
const parsed = frontmatterSchema.safeParse(matter3(raw).data);
|
|
9237
9682
|
if (parsed.success && parsed.data.models.length > 0) {
|
|
@@ -9392,34 +9837,34 @@ var init_entity_order = __esm({
|
|
|
9392
9837
|
"src/agents/04-recipe-builder/entity-order.ts"() {
|
|
9393
9838
|
"use strict";
|
|
9394
9839
|
init_esm_shims();
|
|
9395
|
-
createdBySchema =
|
|
9396
|
-
owner:
|
|
9397
|
-
via:
|
|
9398
|
-
why:
|
|
9840
|
+
createdBySchema = z50.object({
|
|
9841
|
+
owner: z50.string(),
|
|
9842
|
+
via: z50.string().optional(),
|
|
9843
|
+
why: z50.string().optional()
|
|
9399
9844
|
});
|
|
9400
|
-
auditedModelSchema =
|
|
9401
|
-
name:
|
|
9402
|
-
independently_created:
|
|
9403
|
-
creation_file:
|
|
9404
|
-
creation_function:
|
|
9405
|
-
side_effects:
|
|
9845
|
+
auditedModelSchema = z50.object({
|
|
9846
|
+
name: z50.string(),
|
|
9847
|
+
independently_created: z50.coerce.boolean().default(false),
|
|
9848
|
+
creation_file: z50.string().optional(),
|
|
9849
|
+
creation_function: z50.string().optional(),
|
|
9850
|
+
side_effects: z50.array(z50.string()).optional(),
|
|
9406
9851
|
// Tolerate a stray `created_by:` with no entries (parsed as null by YAML).
|
|
9407
|
-
created_by:
|
|
9852
|
+
created_by: z50.array(createdBySchema).nullish().transform((v) => v ?? [])
|
|
9408
9853
|
});
|
|
9409
|
-
frontmatterSchema =
|
|
9410
|
-
models:
|
|
9854
|
+
frontmatterSchema = z50.object({
|
|
9855
|
+
models: z50.array(auditedModelSchema).nullish().transform((v) => v ?? [])
|
|
9411
9856
|
});
|
|
9412
9857
|
}
|
|
9413
9858
|
});
|
|
9414
9859
|
|
|
9415
9860
|
// src/agents/04-recipe-builder/completion.ts
|
|
9416
|
-
import { readFile as
|
|
9417
|
-
import { join as
|
|
9418
|
-
import { z as
|
|
9861
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
9862
|
+
import { join as join27 } from "path";
|
|
9863
|
+
import { z as z51 } from "zod";
|
|
9419
9864
|
async function readCompletion(outputDir) {
|
|
9420
9865
|
let raw;
|
|
9421
9866
|
try {
|
|
9422
|
-
raw = await
|
|
9867
|
+
raw = await readFile17(join27(outputDir, COMPLETION_MARKER_FILE), "utf-8");
|
|
9423
9868
|
} catch (err) {
|
|
9424
9869
|
debugLog("No completion marker yet", { err });
|
|
9425
9870
|
return false;
|
|
@@ -9445,7 +9890,7 @@ var init_completion = __esm({
|
|
|
9445
9890
|
init_esm_shims();
|
|
9446
9891
|
init_debug();
|
|
9447
9892
|
COMPLETION_MARKER_FILE = ".sdk-integration-complete";
|
|
9448
|
-
completionSchema =
|
|
9893
|
+
completionSchema = z51.object({ complete: z51.literal(true) });
|
|
9449
9894
|
}
|
|
9450
9895
|
});
|
|
9451
9896
|
|
|
@@ -9453,9 +9898,10 @@ var init_completion = __esm({
|
|
|
9453
9898
|
function watchForCompletion(outputDir, proc, timing = DEFAULT_WATCH_TIMING) {
|
|
9454
9899
|
let graceTimer;
|
|
9455
9900
|
let killTimer;
|
|
9901
|
+
let stopped = false;
|
|
9456
9902
|
const poll = setInterval(() => {
|
|
9457
9903
|
void readCompletion(outputDir).then((complete) => {
|
|
9458
|
-
if (!complete || graceTimer != null) return;
|
|
9904
|
+
if (stopped || !complete || graceTimer != null) return;
|
|
9459
9905
|
debugLog("Completion marker detected while the agent runs; scheduling terminal reclaim");
|
|
9460
9906
|
clearInterval(poll);
|
|
9461
9907
|
graceTimer = setTimeout(() => {
|
|
@@ -9469,6 +9915,7 @@ function watchForCompletion(outputDir, proc, timing = DEFAULT_WATCH_TIMING) {
|
|
|
9469
9915
|
});
|
|
9470
9916
|
}, timing.pollMs);
|
|
9471
9917
|
return () => {
|
|
9918
|
+
stopped = true;
|
|
9472
9919
|
clearInterval(poll);
|
|
9473
9920
|
if (graceTimer != null) clearTimeout(graceTimer);
|
|
9474
9921
|
if (killTimer != null) clearTimeout(killTimer);
|
|
@@ -9493,8 +9940,8 @@ var init_completion_watch = __esm({
|
|
|
9493
9940
|
});
|
|
9494
9941
|
|
|
9495
9942
|
// src/agents/04-recipe-builder/integration-prompt.ts
|
|
9496
|
-
import { writeFile as
|
|
9497
|
-
import { join as
|
|
9943
|
+
import { writeFile as writeFile9 } from "fs/promises";
|
|
9944
|
+
import { join as join28 } from "path";
|
|
9498
9945
|
function renderIntegrationPrompt(params) {
|
|
9499
9946
|
const priorFailureSection = params.priorFailure != null ? `
|
|
9500
9947
|
\u2550\u2550\u2550 A PRIOR SESSION DID NOT COMPLETE - READ THIS FIRST \u2550\u2550\u2550
|
|
@@ -9577,7 +10024,10 @@ STOP immediately and say EXACTLY what is missing and why you can't proceed.
|
|
|
9577
10024
|
JavaScript/npm package.
|
|
9578
10025
|
2. Implement ONE endpoint (prefer "${DEFAULT_ENDPOINT_PATH}" unless the repo has a
|
|
9579
10026
|
clearly better convention) that handles the discover / up / down protocol through
|
|
9580
|
-
the SDK handler.
|
|
10027
|
+
the SDK handler. If you mount it anywhere OTHER than "${DEFAULT_ENDPOINT_PATH}",
|
|
10028
|
+
say so on its own line in IMPLEMENTATION.md, exactly as
|
|
10029
|
+
"SDK endpoint path: /your/path" - Autonoma assumes the default and the next step
|
|
10030
|
+
of the setup has to be told the real one, or every scenario provision 404s. The signing secret AUTONOMA_SHARED_SECRET is ALREADY provisioned
|
|
9581
10031
|
in the app's environment - verify the x-signature HMAC against that env value (the
|
|
9582
10032
|
SDK reads it for you). Do NOT hardcode a secret or overwrite the env value.
|
|
9583
10033
|
3. Implement a real factory for EVERY entity the entity audit says needs one:
|
|
@@ -9652,6 +10102,35 @@ value still reads as real: "admin+{{${TEST_RUN_ID_TOKEN}}}@acme.test", "acme-{{$
|
|
|
9652
10102
|
Any OTHER {{token}} is rejected on upload: there is no general variable mechanism, so
|
|
9653
10103
|
never invent one.
|
|
9654
10104
|
|
|
10105
|
+
\u2550\u2550\u2550 DATA THE APP COMPARES AGAINST THE CURRENT TIME \u2550\u2550\u2550
|
|
10106
|
+
Autonoma stores this recipe once and seeds it again, UNCHANGED, before every run for
|
|
10107
|
+
months. So a value the application compares against the current time is wrong the day
|
|
10108
|
+
after you write it. A row meant to be upcoming, overdue, expiring soon, still valid, or
|
|
10109
|
+
recent describes WHEN THE TEST RUNS, not a date on a calendar - and when it goes stale
|
|
10110
|
+
the suite fails in a way that reads as a product bug rather than as stale data. A seeded
|
|
10111
|
+
session or credential that has drifted past its expiry breaks every test at once.
|
|
10112
|
+
|
|
10113
|
+
For every date/time field you seed, ask one question: does the app branch on this being
|
|
10114
|
+
before or after now? Find the answer in the app's own queries - a list partitioned into
|
|
10115
|
+
current and past, a filter against an expiry, a state derived from a deadline. Do not
|
|
10116
|
+
guess from the field name.
|
|
10117
|
+
|
|
10118
|
+
Where the answer is YES, do not put an instant in the recipe. Take an OFFSET as the
|
|
10119
|
+
factory's input, and have the factory add it to the current time as it creates the row -
|
|
10120
|
+
the factory runs at seeding time, so its clock is the right one:
|
|
10121
|
+
recipe: { "startsInMinutes": 4320, "durationMinutes": 30 }
|
|
10122
|
+
factory: reads startsInMinutes, writes the column as (now + 4320 minutes)
|
|
10123
|
+
A factory's input schema is YOUR interface and does not have to mirror its columns -
|
|
10124
|
+
converting an input into the column's real shape inside the factory is normal, and is
|
|
10125
|
+
how a factory already handles any column whose stored form differs from its input. Anchor
|
|
10126
|
+
or round the derived value when the app only accepts particular times.
|
|
10127
|
+
|
|
10128
|
+
Where the answer is NO - a recurring weekly schedule, a date of birth, a timestamp
|
|
10129
|
+
nothing filters on - a concrete value is correct and an offset would only obscure it.
|
|
10130
|
+
|
|
10131
|
+
There is no {{token}} for time. Do not invent one: it is rejected on upload exactly like
|
|
10132
|
+
any other unknown token, and the derivation belongs in the factory anyway.
|
|
10133
|
+
|
|
9655
10134
|
WHICH fields need a token is not a judgement call and not a guess from the field name.
|
|
9656
10135
|
Before you write the recipe, ENUMERATE the uniqueness rules for every entity you seed:
|
|
9657
10136
|
read the schema definitions and migrations, and query the live database you are already
|
|
@@ -9715,6 +10194,11 @@ Once every entity passes independently, run the FULL recipe as one pass:
|
|
|
9715
10194
|
\u2022 confirm all rows created (DB), then down with the refsToken -> succeeds, rows gone (DB)
|
|
9716
10195
|
\u2022 confirm a WRONG signature is rejected (the SDK does this for you - do not disable it)
|
|
9717
10196
|
\u2022 confirm the up response's auth payload contains real credentials, not a placeholder
|
|
10197
|
+
\u2022 for every field you identified as compared against the current time, confirm the
|
|
10198
|
+
seeded row landed on the INTENDED side of now - run the app's own query or open the
|
|
10199
|
+
screen that partitions by time, and see the row where it is supposed to be. A row
|
|
10200
|
+
that exists in the right table but falls on the wrong side of now is the failure this
|
|
10201
|
+
check exists to catch, and "up returned 200" will not reveal it.
|
|
9718
10202
|
|
|
9719
10203
|
\u2550\u2550\u2550 CHECK THE RECIPE FILE - THE GATE YOU CANNOT SKIP \u2550\u2550\u2550
|
|
9720
10204
|
A recipe that seeds a database perfectly can still be a file Autonoma refuses, because
|
|
@@ -9783,7 +10267,7 @@ Write the completion marker once ALL of these hold:
|
|
|
9783
10267
|
A step you documented as genuinely blocked NEVER justifies withholding the marker; a
|
|
9784
10268
|
checklist item you simply have not finished always does. The marker is how the CLI knows
|
|
9785
10269
|
the session is done and can upload the recipe:
|
|
9786
|
-
${
|
|
10270
|
+
${join28(params.outputDir, COMPLETION_MARKER_FILE)}
|
|
9787
10271
|
Its contents MUST be exactly:
|
|
9788
10272
|
{ "complete": true }
|
|
9789
10273
|
Writing it is not optional: the planner watches for this marker and takes the terminal
|
|
@@ -9795,27 +10279,45 @@ or the commit you left behind and what blocked the push - and then says:
|
|
|
9795
10279
|
Nothing after that message - no further questions, summaries, or work.`;
|
|
9796
10280
|
}
|
|
9797
10281
|
async function writeIntegrationPrompt(params) {
|
|
9798
|
-
const path3 =
|
|
9799
|
-
await
|
|
10282
|
+
const path3 = join28(params.outputDir, INTEGRATION_PROMPT_FILE);
|
|
10283
|
+
await writeFile9(path3, renderIntegrationPrompt(params), "utf-8");
|
|
9800
10284
|
return path3;
|
|
9801
10285
|
}
|
|
9802
|
-
var INTEGRATION_PROMPT_VERSION, INTEGRATION_PROMPT_FILE, DEFAULT_ENDPOINT_PATH
|
|
10286
|
+
var INTEGRATION_PROMPT_VERSION, INTEGRATION_PROMPT_FILE, DEFAULT_ENDPOINT_PATH;
|
|
9803
10287
|
var init_integration_prompt = __esm({
|
|
9804
10288
|
"src/agents/04-recipe-builder/integration-prompt.ts"() {
|
|
9805
10289
|
"use strict";
|
|
9806
10290
|
init_esm_shims();
|
|
9807
10291
|
init_src();
|
|
9808
10292
|
init_completion();
|
|
9809
|
-
INTEGRATION_PROMPT_VERSION =
|
|
10293
|
+
INTEGRATION_PROMPT_VERSION = 11;
|
|
9810
10294
|
INTEGRATION_PROMPT_FILE = "integration-prompt.md";
|
|
9811
10295
|
DEFAULT_ENDPOINT_PATH = "/api/autonoma";
|
|
9812
|
-
|
|
10296
|
+
}
|
|
10297
|
+
});
|
|
10298
|
+
|
|
10299
|
+
// src/agents/04-recipe-builder/launch-outcome.ts
|
|
10300
|
+
function describeLaunchOutcome(launch) {
|
|
10301
|
+
const ranTooBriefly = launch.durationMs < FAILED_TO_START_MS;
|
|
10302
|
+
const exitedBadly = launch.exitCode != null && launch.exitCode !== 0;
|
|
10303
|
+
if (!ranTooBriefly || !exitedBadly) return { kind: "ran" };
|
|
10304
|
+
return {
|
|
10305
|
+
kind: "failed-to-start",
|
|
10306
|
+
summary: `${launch.agentLabel} exited immediately (code ${launch.exitCode}, after ${Math.round(launch.durationMs / 1e3)}s) without running the integration. That is the agent itself failing to start, not the integration failing - check that it runs on its own in this terminal.`
|
|
10307
|
+
};
|
|
10308
|
+
}
|
|
10309
|
+
var FAILED_TO_START_MS;
|
|
10310
|
+
var init_launch_outcome = __esm({
|
|
10311
|
+
"src/agents/04-recipe-builder/launch-outcome.ts"() {
|
|
10312
|
+
"use strict";
|
|
10313
|
+
init_esm_shims();
|
|
10314
|
+
FAILED_TO_START_MS = 9e4;
|
|
9813
10315
|
}
|
|
9814
10316
|
});
|
|
9815
10317
|
|
|
9816
10318
|
// src/agents/04-recipe-builder/state.ts
|
|
9817
|
-
import { readFile as
|
|
9818
|
-
import { join as
|
|
10319
|
+
import { readFile as readFile18, writeFile as writeFile10 } from "fs/promises";
|
|
10320
|
+
import { join as join29 } from "path";
|
|
9819
10321
|
function initialRecipeState() {
|
|
9820
10322
|
return {
|
|
9821
10323
|
phase: "tech-detect",
|
|
@@ -9825,7 +10327,7 @@ function initialRecipeState() {
|
|
|
9825
10327
|
}
|
|
9826
10328
|
async function loadRecipeState(outputDir) {
|
|
9827
10329
|
try {
|
|
9828
|
-
const raw = await
|
|
10330
|
+
const raw = await readFile18(join29(outputDir, STATE_FILE2), "utf-8");
|
|
9829
10331
|
const parsed = JSON.parse(raw);
|
|
9830
10332
|
return parsed;
|
|
9831
10333
|
} catch {
|
|
@@ -9833,7 +10335,7 @@ async function loadRecipeState(outputDir) {
|
|
|
9833
10335
|
}
|
|
9834
10336
|
}
|
|
9835
10337
|
async function saveRecipeState(outputDir, state) {
|
|
9836
|
-
await
|
|
10338
|
+
await writeFile10(join29(outputDir, STATE_FILE2), JSON.stringify(state, null, 2), "utf-8");
|
|
9837
10339
|
}
|
|
9838
10340
|
var STATE_FILE2;
|
|
9839
10341
|
var init_state2 = __esm({
|
|
@@ -9846,12 +10348,12 @@ var init_state2 = __esm({
|
|
|
9846
10348
|
|
|
9847
10349
|
// src/agents/04-recipe-builder/phases/handoff.ts
|
|
9848
10350
|
import { rm as rm2 } from "fs/promises";
|
|
9849
|
-
import { join as
|
|
10351
|
+
import { join as join30 } from "path";
|
|
9850
10352
|
function launchMessage(promptFile) {
|
|
9851
10353
|
return `Read the file ${promptFile} and follow its instructions exactly to integrate Autonoma into this application. It is your complete spec. Do not stop until every item in it is done and you have written the completion marker it describes.`;
|
|
9852
10354
|
}
|
|
9853
10355
|
async function runHandoffPhase(state, deps, outputDir) {
|
|
9854
|
-
const recipePath = state.recipePath ??
|
|
10356
|
+
const recipePath = state.recipePath ?? join30(outputDir, RECIPE_FILE);
|
|
9855
10357
|
state.recipePath = recipePath;
|
|
9856
10358
|
const launcher = await selectLauncher(deps.launchers, state.agentId ?? deps.presetAgentId, deps.interactive);
|
|
9857
10359
|
if (launcher == null) {
|
|
@@ -9868,7 +10370,7 @@ async function runHandoffPhase(state, deps, outputDir) {
|
|
|
9868
10370
|
state.agentId = launcher.id;
|
|
9869
10371
|
state.permissionMode = permissionMode;
|
|
9870
10372
|
await saveRecipeState(outputDir, state);
|
|
9871
|
-
await launchAgent(launcher, permissionMode, {
|
|
10373
|
+
state.lastLaunch = await launchAgent(launcher, permissionMode, {
|
|
9872
10374
|
outputDir,
|
|
9873
10375
|
recipePath,
|
|
9874
10376
|
cliCommand: deps.cliCommand,
|
|
@@ -9881,7 +10383,7 @@ async function runHandoffPhase(state, deps, outputDir) {
|
|
|
9881
10383
|
return { kind: "advance" };
|
|
9882
10384
|
}
|
|
9883
10385
|
async function runCompletionPhase(state, deps, outputDir) {
|
|
9884
|
-
const recipePath = state.recipePath ??
|
|
10386
|
+
const recipePath = state.recipePath ?? join30(outputDir, RECIPE_FILE);
|
|
9885
10387
|
while (true) {
|
|
9886
10388
|
const [complete, read] = await Promise.all([readCompletion(outputDir), loadRecipe(outputDir)]);
|
|
9887
10389
|
const uploadProblems = read.status === "ok" ? findRecipeUploadProblems(read.recipe) : [];
|
|
@@ -9897,30 +10399,59 @@ async function runCompletionPhase(state, deps, outputDir) {
|
|
|
9897
10399
|
return await manualFallback(outputDir, recipePath, deps.cliCommand, NO_AGENT_FOUND);
|
|
9898
10400
|
}
|
|
9899
10401
|
const failure = describeIncompleteRecipe(read, uploadProblems, recipePath, deps.cliCommand);
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
10402
|
+
const retry = await planRetry(state, launcher, deps, failure);
|
|
10403
|
+
if (retry.kind === "give-up") return { kind: "handback", summary: handbackSummary(retry.summary, outputDir) };
|
|
10404
|
+
log.warn(retry.announcement);
|
|
10405
|
+
state.agentId = retry.launcher.id;
|
|
10406
|
+
state.priorFailure = retry.priorFailure;
|
|
10407
|
+
await saveRecipeState(outputDir, state);
|
|
10408
|
+
state.lastLaunch = await launchAgent(retry.launcher, state.permissionMode ?? "bypassPermissions", {
|
|
10409
|
+
outputDir,
|
|
10410
|
+
recipePath,
|
|
10411
|
+
cliCommand: deps.cliCommand,
|
|
10412
|
+
interactive: deps.interactive,
|
|
10413
|
+
priorFailure: retry.priorFailure,
|
|
10414
|
+
userGuidance: state.userGuidance
|
|
10415
|
+
});
|
|
10416
|
+
state.launchAttempts = (state.launchAttempts ?? 0) + 1;
|
|
10417
|
+
await saveRecipeState(outputDir, state);
|
|
10418
|
+
}
|
|
10419
|
+
}
|
|
10420
|
+
async function planRetry(state, launcher, deps, failure) {
|
|
10421
|
+
const lastLaunch = state.lastLaunch;
|
|
10422
|
+
const outcome = lastLaunch == null ? void 0 : describeLaunchOutcome(lastLaunch);
|
|
10423
|
+
const failedToStart = outcome?.kind === "failed-to-start" ? outcome.summary : void 0;
|
|
10424
|
+
const summary = failedToStart ?? failure;
|
|
10425
|
+
if (launcher == null) return { kind: "give-up", summary };
|
|
10426
|
+
if ((state.launchAttempts ?? 0) >= MAX_LAUNCH_ATTEMPTS) return { kind: "give-up", summary };
|
|
10427
|
+
if (failedToStart != null && lastLaunch != null) {
|
|
10428
|
+
const alternative = await findOtherAvailableAgent(deps.launchers, lastLaunch.agentId);
|
|
10429
|
+
if (alternative == null) return { kind: "give-up", summary: failedToStart };
|
|
10430
|
+
return {
|
|
10431
|
+
kind: "relaunch",
|
|
10432
|
+
launcher: alternative,
|
|
10433
|
+
// No prior failure: nothing was attempted, so telling a fresh agent that a
|
|
10434
|
+
// prior session "did not complete" would send it hunting for work that was
|
|
10435
|
+
// never started.
|
|
10436
|
+
announcement: `${failedToStart} Trying ${alternative.label} instead...`
|
|
10437
|
+
};
|
|
9918
10438
|
}
|
|
10439
|
+
return {
|
|
10440
|
+
kind: "relaunch",
|
|
10441
|
+
launcher,
|
|
10442
|
+
priorFailure: failure,
|
|
10443
|
+
announcement: "The integration isn't complete yet. Launching the agent to finish it..."
|
|
10444
|
+
};
|
|
10445
|
+
}
|
|
10446
|
+
async function findOtherAvailableAgent(launchers, failedAgentId) {
|
|
10447
|
+
const others = launchers.filter((l) => l.id !== failedAgentId);
|
|
10448
|
+
const availability = await Promise.all(others.map((l) => l.isAvailable()));
|
|
10449
|
+
return others.find((_, index) => availability[index]);
|
|
9919
10450
|
}
|
|
9920
10451
|
function handbackSummary(failure, outputDir) {
|
|
9921
10452
|
return `${failure}
|
|
9922
10453
|
|
|
9923
|
-
Finish the integration (see the prompt at ${
|
|
10454
|
+
Finish the integration (see the prompt at ${join30(outputDir, INTEGRATION_PROMPT_FILE)}), then re-run with --resume.`;
|
|
9924
10455
|
}
|
|
9925
10456
|
async function launchAgent(launcher, permissionMode, target) {
|
|
9926
10457
|
const promptFile = await writeIntegrationPrompt({
|
|
@@ -9930,7 +10461,7 @@ async function launchAgent(launcher, permissionMode, target) {
|
|
|
9930
10461
|
priorFailure: target.priorFailure,
|
|
9931
10462
|
userGuidance: target.userGuidance
|
|
9932
10463
|
});
|
|
9933
|
-
await rm2(
|
|
10464
|
+
await rm2(join30(target.outputDir, COMPLETION_MARKER_FILE), { force: true }).catch((err) => {
|
|
9934
10465
|
debugLog("Could not clear a stale completion marker", { err });
|
|
9935
10466
|
});
|
|
9936
10467
|
log.info(`Launching ${launcher.label}. It will implement the integration - watch and steer it as it works.`);
|
|
@@ -9947,6 +10478,7 @@ async function launchAgent(launcher, permissionMode, target) {
|
|
|
9947
10478
|
});
|
|
9948
10479
|
}
|
|
9949
10480
|
suspend();
|
|
10481
|
+
const startedAt = Date.now();
|
|
9950
10482
|
try {
|
|
9951
10483
|
const exitCode = await launcher.launch({
|
|
9952
10484
|
message: launchMessage(promptFile),
|
|
@@ -9957,7 +10489,22 @@ async function launchAgent(launcher, permissionMode, target) {
|
|
|
9957
10489
|
// own and needs no watcher.
|
|
9958
10490
|
watch: target.interactive ? (proc) => watchForCompletion(target.outputDir, proc) : void 0
|
|
9959
10491
|
});
|
|
9960
|
-
|
|
10492
|
+
const launch = {
|
|
10493
|
+
agentLabel: launcher.label,
|
|
10494
|
+
agentId: launcher.id,
|
|
10495
|
+
exitCode,
|
|
10496
|
+
durationMs: Date.now() - startedAt
|
|
10497
|
+
};
|
|
10498
|
+
log.info(
|
|
10499
|
+
`${launcher.label} exited (code ${exitCode ?? "unknown"}) after ${Math.round(launch.durationMs / 1e3)}s. Back to the planner.`
|
|
10500
|
+
);
|
|
10501
|
+
track("cli_agent_launch_finished", {
|
|
10502
|
+
agent: launcher.id,
|
|
10503
|
+
exit_code: exitCode,
|
|
10504
|
+
duration_ms: launch.durationMs,
|
|
10505
|
+
outcome: describeLaunchOutcome(launch).kind
|
|
10506
|
+
});
|
|
10507
|
+
return launch;
|
|
9961
10508
|
} finally {
|
|
9962
10509
|
resume();
|
|
9963
10510
|
}
|
|
@@ -10004,6 +10551,7 @@ var init_handoff = __esm({
|
|
|
10004
10551
|
"src/agents/04-recipe-builder/phases/handoff.ts"() {
|
|
10005
10552
|
"use strict";
|
|
10006
10553
|
init_esm_shims();
|
|
10554
|
+
init_analytics();
|
|
10007
10555
|
init_coding_agent();
|
|
10008
10556
|
init_debug();
|
|
10009
10557
|
init_interrupt();
|
|
@@ -10013,6 +10561,7 @@ var init_handoff = __esm({
|
|
|
10013
10561
|
init_completion();
|
|
10014
10562
|
init_completion_watch();
|
|
10015
10563
|
init_integration_prompt();
|
|
10564
|
+
init_launch_outcome();
|
|
10016
10565
|
init_recipe();
|
|
10017
10566
|
init_state2();
|
|
10018
10567
|
MAX_LAUNCH_ATTEMPTS = 2;
|
|
@@ -10129,17 +10678,17 @@ var init_recipe_builder = __esm({
|
|
|
10129
10678
|
});
|
|
10130
10679
|
|
|
10131
10680
|
// src/agents/00b-feature-discovery/index.ts
|
|
10132
|
-
import { readFile as
|
|
10133
|
-
import { join as
|
|
10681
|
+
import { readFile as readFile19, writeFile as writeFile11 } from "fs/promises";
|
|
10682
|
+
import { join as join31 } from "path";
|
|
10134
10683
|
import { tool as tool14 } from "ai";
|
|
10135
|
-
import { z as
|
|
10684
|
+
import { z as z52 } from "zod";
|
|
10136
10685
|
async function saveFeatures(outputDir, features) {
|
|
10137
10686
|
const obj = Object.fromEntries(features);
|
|
10138
|
-
await
|
|
10687
|
+
await writeFile11(join31(outputDir, FEATURES_FILE), JSON.stringify(obj, null, 2), "utf-8");
|
|
10139
10688
|
}
|
|
10140
10689
|
async function loadFeatures(outputDir) {
|
|
10141
10690
|
try {
|
|
10142
|
-
const raw = await
|
|
10691
|
+
const raw = await readFile19(join31(outputDir, FEATURES_FILE), "utf-8");
|
|
10143
10692
|
const obj = JSON.parse(raw);
|
|
10144
10693
|
return new Map(Object.entries(obj));
|
|
10145
10694
|
} catch {
|
|
@@ -10173,7 +10722,7 @@ Process every page. Call add_feature for each sub-feature you discover. When don
|
|
|
10173
10722
|
add_feature: tool14({
|
|
10174
10723
|
description: "Add a discovered sub-feature",
|
|
10175
10724
|
inputSchema: Feature.extend({
|
|
10176
|
-
id:
|
|
10725
|
+
id: z52.string().min(1).describe("Unique kebab-case ID (e.g. 'settings-notifications-tab')")
|
|
10177
10726
|
}),
|
|
10178
10727
|
execute: (featureInput) => {
|
|
10179
10728
|
const { id, ...rest } = featureInput;
|
|
@@ -10187,17 +10736,17 @@ Process every page. Call add_feature for each sub-feature you discover. When don
|
|
|
10187
10736
|
}),
|
|
10188
10737
|
view_features: tool14({
|
|
10189
10738
|
description: "View all discovered features so far",
|
|
10190
|
-
inputSchema:
|
|
10739
|
+
inputSchema: z52.object({}),
|
|
10191
10740
|
execute: () => collector.viewFeatures()
|
|
10192
10741
|
}),
|
|
10193
10742
|
view_pages: tool14({
|
|
10194
10743
|
description: "View the pages list to know what to analyze",
|
|
10195
|
-
inputSchema:
|
|
10744
|
+
inputSchema: z52.object({}),
|
|
10196
10745
|
execute: () => pagesDescription
|
|
10197
10746
|
}),
|
|
10198
10747
|
finish: tool14({
|
|
10199
10748
|
description: "Signal that feature discovery is complete",
|
|
10200
|
-
inputSchema:
|
|
10749
|
+
inputSchema: z52.object({ summary: z52.string() }),
|
|
10201
10750
|
execute: async (finishInput) => {
|
|
10202
10751
|
result = {
|
|
10203
10752
|
success: true,
|
|
@@ -10228,13 +10777,13 @@ var init_b_feature_discovery = __esm({
|
|
|
10228
10777
|
init_model();
|
|
10229
10778
|
init_tools();
|
|
10230
10779
|
FEATURES_FILE = "features.json";
|
|
10231
|
-
Feature =
|
|
10232
|
-
name:
|
|
10233
|
-
type:
|
|
10234
|
-
parentPagePath:
|
|
10235
|
-
sourceFiles:
|
|
10236
|
-
interactiveElements:
|
|
10237
|
-
description:
|
|
10780
|
+
Feature = z52.object({
|
|
10781
|
+
name: z52.string().min(1).describe("Human-readable name (e.g. 'Settings > Notifications Tab', 'Create Project Modal')"),
|
|
10782
|
+
type: z52.enum(["tab", "modal", "form", "table", "wizard", "nested-route", "complex-component"]),
|
|
10783
|
+
parentPagePath: z52.string().min(1).describe("The page path this feature belongs to (from the pages list)"),
|
|
10784
|
+
sourceFiles: z52.array(z52.string()).min(1).describe("Relative paths to the source files for this sub-feature"),
|
|
10785
|
+
interactiveElements: z52.number().int().min(0).describe("Count of interactive elements found (buttons, inputs, toggles, etc.)"),
|
|
10786
|
+
description: z52.string().min(10).describe("What this sub-feature does")
|
|
10238
10787
|
});
|
|
10239
10788
|
FeatureCollector = class {
|
|
10240
10789
|
features = /* @__PURE__ */ new Map();
|
|
@@ -10317,21 +10866,21 @@ Use kebab-case IDs that indicate the parent page and feature type:
|
|
|
10317
10866
|
});
|
|
10318
10867
|
|
|
10319
10868
|
// src/agents/05-test-generator/graph.ts
|
|
10320
|
-
import { readFile as
|
|
10321
|
-
import { join as
|
|
10869
|
+
import { readFile as readFile20, writeFile as writeFile12 } from "fs/promises";
|
|
10870
|
+
import { join as join32 } from "path";
|
|
10322
10871
|
function estimateExpectedTests(written, processed, totalNodes) {
|
|
10323
10872
|
if (totalNodes <= 0) return written;
|
|
10324
10873
|
const rate = written > 0 && processed >= LIVE_RATE_MIN_PROCESSED ? written / processed : TESTS_PER_NODE_PRIOR;
|
|
10325
10874
|
return Math.max(written, Math.round(rate * totalNodes));
|
|
10326
10875
|
}
|
|
10327
10876
|
async function saveBfsState(outputDir, state) {
|
|
10328
|
-
const path3 =
|
|
10329
|
-
await
|
|
10877
|
+
const path3 = join32(outputDir, state.stateFile);
|
|
10878
|
+
await writeFile12(path3, JSON.stringify(state.serialize(), null, 2), "utf-8");
|
|
10330
10879
|
}
|
|
10331
10880
|
async function loadBfsState(outputDir) {
|
|
10332
|
-
const path3 =
|
|
10881
|
+
const path3 = join32(outputDir, BFS_STATE_FILE);
|
|
10333
10882
|
try {
|
|
10334
|
-
const raw = await
|
|
10883
|
+
const raw = await readFile20(path3, "utf-8");
|
|
10335
10884
|
return CoverageState.deserialize(JSON.parse(raw));
|
|
10336
10885
|
} catch {
|
|
10337
10886
|
return void 0;
|
|
@@ -10931,13 +11480,13 @@ When you finish, all dimensions are reported so the user knows what's covered an
|
|
|
10931
11480
|
});
|
|
10932
11481
|
|
|
10933
11482
|
// src/agents/05-test-generator/recipe-context.ts
|
|
10934
|
-
import { readFile as
|
|
10935
|
-
import { join as
|
|
10936
|
-
import { z as
|
|
11483
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
11484
|
+
import { join as join33 } from "path";
|
|
11485
|
+
import { z as z53 } from "zod";
|
|
10937
11486
|
async function loadRecipeContext(outputDir) {
|
|
10938
11487
|
let raw;
|
|
10939
11488
|
try {
|
|
10940
|
-
raw = await
|
|
11489
|
+
raw = await readFile21(join33(outputDir, "recipe.json"), "utf-8");
|
|
10941
11490
|
} catch (err) {
|
|
10942
11491
|
debugLog("recipe.json not present; test generation falls back to scenarios.md alone", { err });
|
|
10943
11492
|
return "";
|
|
@@ -10995,13 +11544,13 @@ var init_recipe_context = __esm({
|
|
|
10995
11544
|
init_debug();
|
|
10996
11545
|
STRUCTURAL_KEYS = /* @__PURE__ */ new Set(["_alias", "_ref"]);
|
|
10997
11546
|
TEMPLATE_TOKEN = /\{\{\s*\w+\s*\}\}/;
|
|
10998
|
-
recipeRowSchema =
|
|
10999
|
-
recipeFileSchema =
|
|
11000
|
-
recipes:
|
|
11001
|
-
|
|
11002
|
-
name:
|
|
11003
|
-
description:
|
|
11004
|
-
create:
|
|
11547
|
+
recipeRowSchema = z53.record(z53.string(), z53.unknown());
|
|
11548
|
+
recipeFileSchema = z53.object({
|
|
11549
|
+
recipes: z53.array(
|
|
11550
|
+
z53.object({
|
|
11551
|
+
name: z53.string(),
|
|
11552
|
+
description: z53.string().optional(),
|
|
11553
|
+
create: z53.record(z53.string(), z53.array(recipeRowSchema)).optional()
|
|
11005
11554
|
})
|
|
11006
11555
|
)
|
|
11007
11556
|
});
|
|
@@ -11009,7 +11558,7 @@ var init_recipe_context = __esm({
|
|
|
11009
11558
|
});
|
|
11010
11559
|
|
|
11011
11560
|
// src/agents/05-test-generator/restore-deleted-test.ts
|
|
11012
|
-
import { access, mkdir as mkdir4, writeFile as
|
|
11561
|
+
import { access, mkdir as mkdir4, writeFile as writeFile13 } from "fs/promises";
|
|
11013
11562
|
import { dirname as dirname4 } from "path";
|
|
11014
11563
|
async function restoreDeletedTest(testPath, originalContent) {
|
|
11015
11564
|
try {
|
|
@@ -11020,7 +11569,7 @@ async function restoreDeletedTest(testPath, originalContent) {
|
|
|
11020
11569
|
}
|
|
11021
11570
|
try {
|
|
11022
11571
|
await mkdir4(dirname4(testPath), { recursive: true });
|
|
11023
|
-
await
|
|
11572
|
+
await writeFile13(testPath, originalContent, "utf-8");
|
|
11024
11573
|
return true;
|
|
11025
11574
|
} catch (err) {
|
|
11026
11575
|
console.warn(` [fix] Could not restore ${testPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -11087,21 +11636,21 @@ var init_pool = __esm({
|
|
|
11087
11636
|
});
|
|
11088
11637
|
|
|
11089
11638
|
// src/agents/05-test-generator/rubrics.ts
|
|
11090
|
-
import { z as
|
|
11639
|
+
import { z as z54 } from "zod";
|
|
11091
11640
|
function reviewResultSchema(shape) {
|
|
11092
|
-
return
|
|
11641
|
+
return z54.object(shape);
|
|
11093
11642
|
}
|
|
11094
11643
|
var dimensionResultSchema, reviewResultRecordSchema, structuralIntentRubric, flowCompletenessRubric, uiTextRubric, dataAccuracyRubric, ALL_RUBRICS;
|
|
11095
11644
|
var init_rubrics = __esm({
|
|
11096
11645
|
"src/agents/05-test-generator/rubrics.ts"() {
|
|
11097
11646
|
"use strict";
|
|
11098
11647
|
init_esm_shims();
|
|
11099
|
-
dimensionResultSchema =
|
|
11100
|
-
pass:
|
|
11101
|
-
evidence:
|
|
11102
|
-
suggestion:
|
|
11648
|
+
dimensionResultSchema = z54.object({
|
|
11649
|
+
pass: z54.boolean(),
|
|
11650
|
+
evidence: z54.string().describe("What you checked and found - cite file paths, line content, or specific strings"),
|
|
11651
|
+
suggestion: z54.string().optional().describe("What the planner agent should fix, if failing")
|
|
11103
11652
|
});
|
|
11104
|
-
reviewResultRecordSchema =
|
|
11653
|
+
reviewResultRecordSchema = z54.record(z54.string(), dimensionResultSchema);
|
|
11105
11654
|
structuralIntentRubric = {
|
|
11106
11655
|
name: "structural-intent",
|
|
11107
11656
|
maxSteps: 8,
|
|
@@ -11347,12 +11896,12 @@ var init_review_pass = __esm({
|
|
|
11347
11896
|
});
|
|
11348
11897
|
|
|
11349
11898
|
// src/agents/05-test-generator/review.ts
|
|
11350
|
-
import { readFile as
|
|
11351
|
-
import { join as
|
|
11899
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
11900
|
+
import { join as join34, relative as relative7, basename as basename6 } from "path";
|
|
11352
11901
|
import "ai";
|
|
11353
11902
|
import { glob as glob5 } from "glob";
|
|
11354
11903
|
function markReview(relativePath, status) {
|
|
11355
|
-
getActiveStore()?.setArtifactReview(
|
|
11904
|
+
getActiveStore()?.setArtifactReview(join34(TESTS_DIR, relativePath), status);
|
|
11356
11905
|
}
|
|
11357
11906
|
function mergeRubricResults(reported) {
|
|
11358
11907
|
const merged = {};
|
|
@@ -11407,24 +11956,24 @@ async function readDataContract(outputDir) {
|
|
|
11407
11956
|
const recipe = await loadRecipeContext(outputDir);
|
|
11408
11957
|
if (recipe !== "") return recipe;
|
|
11409
11958
|
try {
|
|
11410
|
-
return await
|
|
11959
|
+
return await readFile22(join34(outputDir, "scenarios.md"), "utf-8");
|
|
11411
11960
|
} catch (err) {
|
|
11412
11961
|
debugLog("No data contract available for review", { err });
|
|
11413
11962
|
return void 0;
|
|
11414
11963
|
}
|
|
11415
11964
|
}
|
|
11416
11965
|
async function runConsolidatedReview(outputDir, projectRoot, model, deadline, settled = /* @__PURE__ */ new Set(), precomputed = [], onProgress) {
|
|
11417
|
-
const testsDir =
|
|
11966
|
+
const testsDir = join34(outputDir, TESTS_DIR);
|
|
11418
11967
|
const logger = createStepLogger("review", 5);
|
|
11419
11968
|
const scenarioData = await readDataContract(outputDir);
|
|
11420
|
-
const testFiles = await glob5(
|
|
11969
|
+
const testFiles = await glob5(join34(testsDir, TEST_FILE_GLOB));
|
|
11421
11970
|
const tests = [];
|
|
11422
11971
|
for (const testPath of testFiles) {
|
|
11423
11972
|
if (!isTestFile(testPath)) continue;
|
|
11424
11973
|
if (testPath.includes("/_invalid/")) continue;
|
|
11425
11974
|
const relativePath = relative7(testsDir, testPath);
|
|
11426
11975
|
if (settled.has(relativePath)) continue;
|
|
11427
|
-
const content = await
|
|
11976
|
+
const content = await readFile22(testPath, "utf-8");
|
|
11428
11977
|
const flowMatch = content.match(/^---\n[\s\S]*?flow:\s*["']?([^"'\n]+)["']?\s*\n[\s\S]*?---/m);
|
|
11429
11978
|
tests.push({ path: testPath, relativePath, content, flow: flowMatch?.[1]?.trim() ?? "unknown" });
|
|
11430
11979
|
}
|
|
@@ -11619,7 +12168,7 @@ var init_review_pipeline = __esm({
|
|
|
11619
12168
|
});
|
|
11620
12169
|
|
|
11621
12170
|
// src/agents/05-test-generator/validation.ts
|
|
11622
|
-
import matter4 from "gray-matter";
|
|
12171
|
+
import matter4 from "@11ty/gray-matter";
|
|
11623
12172
|
function isValidVerb(verb) {
|
|
11624
12173
|
return VALID_VERBS.has(verb);
|
|
11625
12174
|
}
|
|
@@ -11683,7 +12232,7 @@ var init_validation = __esm({
|
|
|
11683
12232
|
});
|
|
11684
12233
|
|
|
11685
12234
|
// src/agents/05-test-generator/test-spec.ts
|
|
11686
|
-
import { z as
|
|
12235
|
+
import { z as z55 } from "zod";
|
|
11687
12236
|
function hasPlaceholder(text2) {
|
|
11688
12237
|
if (text2 == null) return false;
|
|
11689
12238
|
return PLACEHOLDER_PATTERNS.some(({ pattern }) => pattern.test(text2));
|
|
@@ -11734,12 +12283,12 @@ var init_test_spec = __esm({
|
|
|
11734
12283
|
{ pattern: /(?<!\{)\{[a-z][a-zA-Z]*\}(?!\})/, name: "bare {variable}" },
|
|
11735
12284
|
{ pattern: /\be\.g\./i, name: '"e.g." example' }
|
|
11736
12285
|
];
|
|
11737
|
-
stepSchema =
|
|
11738
|
-
verb:
|
|
11739
|
-
description:
|
|
12286
|
+
stepSchema = z55.object({
|
|
12287
|
+
verb: z55.enum(STEP_VERBS).describe("The action. Only these verbs exist."),
|
|
12288
|
+
description: z55.string().min(1).describe(
|
|
11740
12289
|
`What to do or check, naming the exact visible text. For assert, the thing expected on screen - e.g. 'text "Transfer Successful"'.`
|
|
11741
12290
|
),
|
|
11742
|
-
location:
|
|
12291
|
+
location: z55.string().optional().describe(
|
|
11743
12292
|
'WHERE on screen the target is - "in the modal", "in the toast notification", "on the Sony WH-1000XM5 product card", "in the dashboard header", "as a page heading". REQUIRED for click, type and assert: the same label routinely appears more than once (a header button and the modal button it opens, "Buy" on every card in a list), and acting on the wrong one fails confusingly. Not needed for scroll or refresh, which act on the page.'
|
|
11744
12293
|
)
|
|
11745
12294
|
}).refine((step) => !requiresLocation(step.verb) || (step.location?.trim() ?? "") !== "", {
|
|
@@ -11749,27 +12298,27 @@ var init_test_spec = __esm({
|
|
|
11749
12298
|
message: "steps carry no variables - use the exact value from the test data, not a placeholder or example",
|
|
11750
12299
|
path: ["description"]
|
|
11751
12300
|
});
|
|
11752
|
-
testSpecSchema =
|
|
11753
|
-
title:
|
|
11754
|
-
description:
|
|
11755
|
-
intent:
|
|
12301
|
+
testSpecSchema = z55.object({
|
|
12302
|
+
title: z55.string().min(1).describe("Short, descriptive test name."),
|
|
12303
|
+
description: z55.string().min(1).describe("One sentence explaining what the test verifies."),
|
|
12304
|
+
intent: z55.string().min(30).describe(
|
|
11756
12305
|
"A specific, falsifiable claim derived from the node's mission: what the user does, what the feature produces, why it matters. Not the steps, not 'the page displays correctly'."
|
|
11757
12306
|
),
|
|
11758
|
-
criticality:
|
|
11759
|
-
scenario:
|
|
11760
|
-
flow:
|
|
11761
|
-
verification:
|
|
12307
|
+
criticality: z55.enum(CRITICALITY_LEVELS),
|
|
12308
|
+
scenario: z55.string().min(1).describe('Which scenario this test uses (usually "standard").'),
|
|
12309
|
+
flow: z55.string().min(1).describe("Which feature/flow this belongs to (must match a flow from AUTONOMA.md)."),
|
|
12310
|
+
verification: z55.string().min(20).describe(
|
|
11762
12311
|
"WHERE to navigate and WHAT to assert to prove the mutation worked. Must name the source of truth - a toast, a confirmation dialog or an inline success indicator is an acknowledgment, not proof."
|
|
11763
12312
|
),
|
|
11764
|
-
setup:
|
|
12313
|
+
setup: z55.string().min(1).describe(
|
|
11765
12314
|
"Which page the user starts on and how they got there. Never authentication - the user is already signed in."
|
|
11766
12315
|
),
|
|
11767
|
-
steps:
|
|
11768
|
-
verificationSteps:
|
|
12316
|
+
steps: z55.array(stepSchema).min(1).describe("The action sequence, in order."),
|
|
12317
|
+
verificationSteps: z55.array(stepSchema).describe(
|
|
11769
12318
|
"Steps that navigate to the source of truth and assert the mutation landed. Implements the `verification` field above."
|
|
11770
12319
|
),
|
|
11771
|
-
expectedResult:
|
|
11772
|
-
notes:
|
|
12320
|
+
expectedResult: z55.string().min(1).describe("What should be true when the test passes."),
|
|
12321
|
+
notes: z55.string().optional().describe(
|
|
11773
12322
|
"Optional free text for anything you could not resolve or want to flag - an ambiguous default state, a element you could not find in the source, an assumption you had to make. Recorded for humans and NEVER written into the test file, so it will not affect execution. Use it instead of guessing silently."
|
|
11774
12323
|
)
|
|
11775
12324
|
}).refine((spec) => spec.steps.filter((step) => isInteractionVerb(step.verb)).length >= MIN_INTERACTIONS, {
|
|
@@ -11780,25 +12329,25 @@ var init_test_spec = __esm({
|
|
|
11780
12329
|
});
|
|
11781
12330
|
|
|
11782
12331
|
// src/agents/05-test-generator/tools.ts
|
|
11783
|
-
import { mkdir as mkdir5, writeFile as
|
|
11784
|
-
import { dirname as dirname5, join as
|
|
12332
|
+
import { mkdir as mkdir5, writeFile as writeFile14 } from "fs/promises";
|
|
12333
|
+
import { dirname as dirname5, join as join35 } from "path";
|
|
11785
12334
|
import { hasToolCall as hasToolCall3, stepCountIs as stepCountIs3, tool as tool16, ToolLoopAgent as ToolLoopAgent3 } from "ai";
|
|
11786
|
-
import { z as
|
|
12335
|
+
import { z as z56 } from "zod";
|
|
11787
12336
|
function buildWriteTestTool(state, outputDir, onWritten) {
|
|
11788
12337
|
return tool16({
|
|
11789
12338
|
description: "Write one test to qa-tests/{folder}/{filename}.md. You supply the test's parts; the file is rendered for you, so do not write markdown or frontmatter yourself.",
|
|
11790
|
-
inputSchema:
|
|
11791
|
-
folder:
|
|
11792
|
-
filename:
|
|
11793
|
-
nodeId:
|
|
12339
|
+
inputSchema: z56.object({
|
|
12340
|
+
folder: z56.string().describe("Subfolder name under qa-tests/"),
|
|
12341
|
+
filename: z56.string().describe(`File name ending in ${TEST_FILE_EXT} (e.g. login-valid-credentials${TEST_FILE_EXT})`),
|
|
12342
|
+
nodeId: z56.string().describe(
|
|
11794
12343
|
"The id next_node returned for this feature, copied verbatim. Not a re-slugged version of it, not a folder path, not the test filename."
|
|
11795
12344
|
),
|
|
11796
12345
|
test: testSpecSchema
|
|
11797
12346
|
}),
|
|
11798
12347
|
execute: async (input) => {
|
|
11799
12348
|
const content = renderTestMarkdown(input.test);
|
|
11800
|
-
const relPath =
|
|
11801
|
-
const absPath =
|
|
12349
|
+
const relPath = join35(TESTS_DIR, input.folder, normalizeTestFilename(input.filename));
|
|
12350
|
+
const absPath = join35(outputDir, relPath);
|
|
11802
12351
|
const nodeId = state.resolveNodeId(input.nodeId, relPath);
|
|
11803
12352
|
if (nodeId == null) {
|
|
11804
12353
|
const stats = state.summary();
|
|
@@ -11817,7 +12366,7 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11817
12366
|
}
|
|
11818
12367
|
try {
|
|
11819
12368
|
await mkdir5(dirname5(absPath), { recursive: true });
|
|
11820
|
-
await
|
|
12369
|
+
await writeFile14(absPath, content, "utf-8");
|
|
11821
12370
|
state.markTested(nodeId, [relPath]);
|
|
11822
12371
|
await saveBfsState(outputDir, state);
|
|
11823
12372
|
const notes = input.test.notes?.trim();
|
|
@@ -11830,7 +12379,7 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11830
12379
|
});
|
|
11831
12380
|
}
|
|
11832
12381
|
onWritten?.({
|
|
11833
|
-
relativePath:
|
|
12382
|
+
relativePath: join35(input.folder, normalizeTestFilename(input.filename)),
|
|
11834
12383
|
content,
|
|
11835
12384
|
flow: input.test.flow
|
|
11836
12385
|
});
|
|
@@ -11868,14 +12417,14 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11868
12417
|
function buildCreateFolderTool(outputDir) {
|
|
11869
12418
|
return tool16({
|
|
11870
12419
|
description: "Create a folder under qa-tests/ for organizing tests.",
|
|
11871
|
-
inputSchema:
|
|
11872
|
-
folder:
|
|
12420
|
+
inputSchema: z56.object({
|
|
12421
|
+
folder: z56.string().describe("Folder name (kebab-case)")
|
|
11873
12422
|
}),
|
|
11874
12423
|
execute: async (input) => {
|
|
11875
|
-
const absPath =
|
|
12424
|
+
const absPath = join35(outputDir, TESTS_DIR, input.folder);
|
|
11876
12425
|
try {
|
|
11877
12426
|
await mkdir5(absPath, { recursive: true });
|
|
11878
|
-
return { path:
|
|
12427
|
+
return { path: join35(TESTS_DIR, input.folder) };
|
|
11879
12428
|
} catch (err) {
|
|
11880
12429
|
const message = err instanceof Error ? err.message : String(err);
|
|
11881
12430
|
return { error: `Failed to create folder: ${message}` };
|
|
@@ -11886,7 +12435,7 @@ function buildCreateFolderTool(outputDir) {
|
|
|
11886
12435
|
function buildNextNodeTool(state, outputDir) {
|
|
11887
12436
|
return tool16({
|
|
11888
12437
|
description: "Get the next node to write tests for. If you called next_node before without writing any tests (via write_test), the previous node is auto-skipped. Returns done:true when all nodes are processed.",
|
|
11889
|
-
inputSchema:
|
|
12438
|
+
inputSchema: z56.object({}),
|
|
11890
12439
|
execute: async () => {
|
|
11891
12440
|
const next = state.nextNode();
|
|
11892
12441
|
await saveBfsState(outputDir, state);
|
|
@@ -11920,7 +12469,7 @@ function buildNextNodeTool(state, outputDir) {
|
|
|
11920
12469
|
function buildGetProgressTool(state) {
|
|
11921
12470
|
return tool16({
|
|
11922
12471
|
description: "Check how many nodes have been tested vs how many remain.",
|
|
11923
|
-
inputSchema:
|
|
12472
|
+
inputSchema: z56.object({}),
|
|
11924
12473
|
execute: async () => {
|
|
11925
12474
|
const stats = state.summary();
|
|
11926
12475
|
const nodes = [...state.nodes.values()].map((n) => ({
|
|
@@ -11936,12 +12485,12 @@ function buildGetProgressTool(state) {
|
|
|
11936
12485
|
function buildSpawnResearcherTool(model, workingDirectory, onHeartbeat) {
|
|
11937
12486
|
return tool16({
|
|
11938
12487
|
description: "Spawn a research subagent to read and analyze source files without polluting your context. Use for complex sub-features where you don't want to read 20 files yourself.",
|
|
11939
|
-
inputSchema:
|
|
11940
|
-
instruction:
|
|
12488
|
+
inputSchema: z56.object({
|
|
12489
|
+
instruction: z56.string().describe("What to research - be specific about files and what to look for")
|
|
11941
12490
|
}),
|
|
11942
12491
|
execute: async (input) => {
|
|
11943
|
-
const resultSchema2 =
|
|
11944
|
-
findings:
|
|
12492
|
+
const resultSchema2 = z56.object({
|
|
12493
|
+
findings: z56.string().describe("Summary of what was found")
|
|
11945
12494
|
});
|
|
11946
12495
|
let result;
|
|
11947
12496
|
const subagent = new ToolLoopAgent3({
|
|
@@ -11994,11 +12543,11 @@ var init_tools2 = __esm({
|
|
|
11994
12543
|
});
|
|
11995
12544
|
|
|
11996
12545
|
// src/agents/05-test-generator/write-index.ts
|
|
11997
|
-
import { readFile as
|
|
11998
|
-
import { join as
|
|
12546
|
+
import { readFile as readFile23, writeFile as writeFile15 } from "fs/promises";
|
|
12547
|
+
import { join as join36 } from "path";
|
|
11999
12548
|
async function scanTest(outputDir, path3) {
|
|
12000
12549
|
try {
|
|
12001
|
-
const content = await
|
|
12550
|
+
const content = await readFile23(join36(outputDir, path3), "utf-8");
|
|
12002
12551
|
return {
|
|
12003
12552
|
criticality: content.match(/criticality:\s*(\w+)/)?.[1] ?? "",
|
|
12004
12553
|
steps: content.match(STEP_VERBS2)?.length ?? 0,
|
|
@@ -12069,7 +12618,7 @@ ${folders.map((f) => `| ${f.name} | ${f.test_count} |`).join("\n")}
|
|
|
12069
12618
|
|
|
12070
12619
|
${[...testsByFolder.entries()].flatMap(([_folder, tests]) => tests.map((t) => `- \`${t}\``)).join("\n")}
|
|
12071
12620
|
${renderSection("Features with no tests", "the run walked these and produced no test for them - re-run the planner to cover them", untested)}${renderSection("Tests lost in review", "the review cycle removed these and nothing could put them back - re-run the planner to regenerate them", lost)}`;
|
|
12072
|
-
await
|
|
12621
|
+
await writeFile15(join36(outputDir, TESTS_DIR, TEST_INDEX_FILE), content, "utf-8");
|
|
12073
12622
|
}
|
|
12074
12623
|
function untestedFeatures(state) {
|
|
12075
12624
|
const withTests = new Set(state.testsWritten.keys());
|
|
@@ -12104,11 +12653,11 @@ __export(test_generator_exports, {
|
|
|
12104
12653
|
runTestGenerator: () => runTestGenerator
|
|
12105
12654
|
});
|
|
12106
12655
|
import { existsSync as existsSync2 } from "fs";
|
|
12107
|
-
import { mkdir as mkdir6, readFile as
|
|
12108
|
-
import { basename as basename7, join as
|
|
12656
|
+
import { mkdir as mkdir6, readFile as readFile24, rmdir, unlink, writeFile as writeFile16 } from "fs/promises";
|
|
12657
|
+
import { basename as basename7, join as join37 } from "path";
|
|
12109
12658
|
import { tool as tool17 } from "ai";
|
|
12110
12659
|
import { glob as glob6 } from "glob";
|
|
12111
|
-
import { z as
|
|
12660
|
+
import { z as z57 } from "zod";
|
|
12112
12661
|
async function preseedQueue(state, projectRoot, pages, features) {
|
|
12113
12662
|
let seeded = 0;
|
|
12114
12663
|
const pageIdByPath = /* @__PURE__ */ new Map();
|
|
@@ -12160,8 +12709,8 @@ async function runTestGenerator(input) {
|
|
|
12160
12709
|
let result;
|
|
12161
12710
|
const finishTool = tool17({
|
|
12162
12711
|
description: "Call when the BFS queue is empty and all routes have been explored.",
|
|
12163
|
-
inputSchema:
|
|
12164
|
-
summary:
|
|
12712
|
+
inputSchema: z57.object({
|
|
12713
|
+
summary: z57.string().describe("Coverage summary")
|
|
12165
12714
|
}),
|
|
12166
12715
|
execute: async (finishInput) => {
|
|
12167
12716
|
const stats = state.summary();
|
|
@@ -12189,7 +12738,7 @@ async function runTestGenerator(input) {
|
|
|
12189
12738
|
});
|
|
12190
12739
|
let kbContext = "";
|
|
12191
12740
|
try {
|
|
12192
|
-
const autonomaMd = await
|
|
12741
|
+
const autonomaMd = await readFile24(join37(input.outputDir, "AUTONOMA.md"), "utf-8");
|
|
12193
12742
|
kbContext += `
|
|
12194
12743
|
## Knowledge Base (AUTONOMA.md)
|
|
12195
12744
|
|
|
@@ -12202,7 +12751,7 @@ ${autonomaMd}
|
|
|
12202
12751
|
kbContext += recipeContext;
|
|
12203
12752
|
} else {
|
|
12204
12753
|
try {
|
|
12205
|
-
const scenariosMd = await
|
|
12754
|
+
const scenariosMd = await readFile24(join37(input.outputDir, "scenarios.md"), "utf-8");
|
|
12206
12755
|
kbContext += `
|
|
12207
12756
|
## Scenarios
|
|
12208
12757
|
|
|
@@ -12500,20 +13049,20 @@ IMPORTANT: Do NOT try to finish early. Process every node via next_node until it
|
|
|
12500
13049
|
}
|
|
12501
13050
|
state.clearReviewProgress();
|
|
12502
13051
|
state.setPhase("checking every test");
|
|
12503
|
-
const allTestFiles = await glob6(
|
|
13052
|
+
const allTestFiles = await glob6(join37(input.outputDir, TESTS_DIR, TEST_FILE_GLOB));
|
|
12504
13053
|
let markedInvalid = 0;
|
|
12505
13054
|
for (const testPath of allTestFiles) {
|
|
12506
13055
|
if (!isTestFile(testPath)) continue;
|
|
12507
13056
|
if (testPath.includes(`/${INVALID_DIR}/`)) continue;
|
|
12508
|
-
const content = await
|
|
13057
|
+
const content = await readFile24(testPath, "utf-8");
|
|
12509
13058
|
const validation = validateTestContent(content);
|
|
12510
13059
|
if (!validation.valid) {
|
|
12511
|
-
const invalidDir =
|
|
13060
|
+
const invalidDir = join37(input.outputDir, TESTS_DIR, INVALID_DIR);
|
|
12512
13061
|
await mkdir6(invalidDir, { recursive: true });
|
|
12513
|
-
const dest =
|
|
13062
|
+
const dest = join37(invalidDir, basename7(testPath));
|
|
12514
13063
|
const annotated = `<!-- VALIDATION ERRORS: ${validation.errors.join("; ")} -->
|
|
12515
13064
|
${content}`;
|
|
12516
|
-
await
|
|
13065
|
+
await writeFile16(dest, annotated, "utf-8");
|
|
12517
13066
|
await unlink(testPath);
|
|
12518
13067
|
markedInvalid++;
|
|
12519
13068
|
}
|
|
@@ -12521,7 +13070,7 @@ ${content}`;
|
|
|
12521
13070
|
if (markedInvalid > 0) {
|
|
12522
13071
|
console.log(` ${markedInvalid} tests still invalid after review cycles - moved to _invalid/`);
|
|
12523
13072
|
}
|
|
12524
|
-
const dirs = await glob6(
|
|
13073
|
+
const dirs = await glob6(join37(input.outputDir, TESTS_DIR, "**/"), {
|
|
12525
13074
|
dot: false
|
|
12526
13075
|
});
|
|
12527
13076
|
for (const dir of dirs.sort((a, b) => b.length - a.length)) {
|
|
@@ -12574,21 +13123,21 @@ async function generateJourneyTests(outputDir, model, projectRoot) {
|
|
|
12574
13123
|
let autonomaMd = "";
|
|
12575
13124
|
let scenariosMd = "";
|
|
12576
13125
|
try {
|
|
12577
|
-
autonomaMd = await
|
|
13126
|
+
autonomaMd = await readFile24(join37(outputDir, "AUTONOMA.md"), "utf-8");
|
|
12578
13127
|
} catch (err) {
|
|
12579
13128
|
debugLog("AUTONOMA.md not present for journey generation", { err });
|
|
12580
13129
|
}
|
|
12581
13130
|
try {
|
|
12582
|
-
scenariosMd = await
|
|
13131
|
+
scenariosMd = await readFile24(join37(outputDir, "scenarios.md"), "utf-8");
|
|
12583
13132
|
} catch (err) {
|
|
12584
13133
|
debugLog("scenarios.md not present for journey generation", { err });
|
|
12585
13134
|
}
|
|
12586
13135
|
if (!autonomaMd) return 0;
|
|
12587
|
-
const existingTests = await glob6(
|
|
13136
|
+
const existingTests = await glob6(join37(outputDir, TESTS_DIR, TEST_FILE_GLOB));
|
|
12588
13137
|
const existingTitles = [];
|
|
12589
13138
|
for (const t of existingTests) {
|
|
12590
13139
|
if (!isTestFile(t)) continue;
|
|
12591
|
-
const content = await
|
|
13140
|
+
const content = await readFile24(t, "utf-8");
|
|
12592
13141
|
const titleMatch = content.match(/title:\s*"([^"]+)"/);
|
|
12593
13142
|
if (titleMatch) existingTitles.push(titleMatch[1]);
|
|
12594
13143
|
}
|
|
@@ -12634,7 +13183,7 @@ Write 5-8 journey tests using the write_test tool with folder "${JOURNEY_NODE_ID
|
|
|
12634
13183
|
let journeyResult;
|
|
12635
13184
|
const journeyFinish = tool17({
|
|
12636
13185
|
description: "Signal journey generation is complete.",
|
|
12637
|
-
inputSchema:
|
|
13186
|
+
inputSchema: z57.object({ summary: z57.string() }),
|
|
12638
13187
|
execute: async (finishInput) => {
|
|
12639
13188
|
journeyResult = {
|
|
12640
13189
|
success: true,
|
|
@@ -14481,14 +15030,14 @@ var init_session_recorder = __esm({
|
|
|
14481
15030
|
});
|
|
14482
15031
|
|
|
14483
15032
|
// src/ui/artifacts/reader.ts
|
|
14484
|
-
import { open, readFile as
|
|
15033
|
+
import { open, readFile as readFile25, stat as stat2 } from "fs/promises";
|
|
14485
15034
|
async function readForLive(absPath) {
|
|
14486
15035
|
try {
|
|
14487
15036
|
const info = await stat2(absPath);
|
|
14488
15037
|
if (!info.isFile()) return void 0;
|
|
14489
15038
|
const kind = kindOf(absPath);
|
|
14490
15039
|
if (info.size <= LARGE_BYTES) {
|
|
14491
|
-
const text2 = await
|
|
15040
|
+
const text2 = await readFile25(absPath, "utf-8");
|
|
14492
15041
|
return { text: text2, kind, truncated: false };
|
|
14493
15042
|
}
|
|
14494
15043
|
const fh = await open(absPath, "r");
|
|
@@ -14782,9 +15331,9 @@ ensureSupportedNode();
|
|
|
14782
15331
|
|
|
14783
15332
|
// src/index.ts
|
|
14784
15333
|
init_submit();
|
|
14785
|
-
import { readFile as
|
|
15334
|
+
import { readFile as readFile26, writeFile as writeFile17 } from "fs/promises";
|
|
14786
15335
|
import { freemem } from "os";
|
|
14787
|
-
import { delimiter, join as
|
|
15336
|
+
import { delimiter, join as join38 } from "path";
|
|
14788
15337
|
|
|
14789
15338
|
// src/agents/04-recipe-builder/sdk-command.ts
|
|
14790
15339
|
init_esm_shims();
|
|
@@ -14973,7 +15522,7 @@ function shortHash(seed) {
|
|
|
14973
15522
|
}
|
|
14974
15523
|
|
|
14975
15524
|
// src/agents/04-recipe-builder/sdk-command.ts
|
|
14976
|
-
import { z as
|
|
15525
|
+
import { z as z34 } from "zod";
|
|
14977
15526
|
|
|
14978
15527
|
// src/core/cli-args.ts
|
|
14979
15528
|
init_esm_shims();
|
|
@@ -15132,16 +15681,16 @@ async function down(config, refsToken) {
|
|
|
15132
15681
|
init_recipe();
|
|
15133
15682
|
var DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
|
|
15134
15683
|
var MIN_REPEAT_INSTANCES = 2;
|
|
15135
|
-
var createSchema =
|
|
15136
|
-
var recipeSliceSchema =
|
|
15137
|
-
var envelopeSchema =
|
|
15138
|
-
var flagsSchema =
|
|
15139
|
-
url:
|
|
15140
|
-
recipe:
|
|
15141
|
-
"refs-token":
|
|
15142
|
-
"test-run-id":
|
|
15143
|
-
timeout:
|
|
15144
|
-
repeat:
|
|
15684
|
+
var createSchema = z34.record(z34.string(), z34.array(z34.unknown()));
|
|
15685
|
+
var recipeSliceSchema = z34.object({ create: createSchema, variables: ScenarioRecipeVariablesSchema.optional() });
|
|
15686
|
+
var envelopeSchema = z34.object({ recipes: z34.array(recipeSliceSchema).min(1) });
|
|
15687
|
+
var flagsSchema = z34.object({
|
|
15688
|
+
url: z34.string().min(1).optional(),
|
|
15689
|
+
recipe: z34.string().min(1).optional(),
|
|
15690
|
+
"refs-token": z34.string().min(1).optional(),
|
|
15691
|
+
"test-run-id": z34.string().min(1).optional(),
|
|
15692
|
+
timeout: z34.coerce.number().int().positive().optional(),
|
|
15693
|
+
repeat: z34.coerce.number().int().min(MIN_REPEAT_INSTANCES).optional()
|
|
15145
15694
|
});
|
|
15146
15695
|
var SDK_FLAGS = new Set(Object.keys(flagsSchema.shape));
|
|
15147
15696
|
async function runSdkCommand(argv, io) {
|
|
@@ -15911,8 +16460,8 @@ init_prompts();
|
|
|
15911
16460
|
// src/core/git.ts
|
|
15912
16461
|
init_esm_shims();
|
|
15913
16462
|
import { execFile } from "child_process";
|
|
15914
|
-
import { readFile as
|
|
15915
|
-
import { join as
|
|
16463
|
+
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
16464
|
+
import { join as join14 } from "path";
|
|
15916
16465
|
import { promisify } from "util";
|
|
15917
16466
|
var execFileAsync = promisify(execFile);
|
|
15918
16467
|
var GIT_INFO_FILE = ".git-info.json";
|
|
@@ -15936,11 +16485,11 @@ async function readGitInfo(projectRoot) {
|
|
|
15936
16485
|
};
|
|
15937
16486
|
}
|
|
15938
16487
|
async function saveGitInfo(outputDir, info) {
|
|
15939
|
-
await
|
|
16488
|
+
await writeFile5(join14(outputDir, GIT_INFO_FILE), JSON.stringify(info, null, 2), "utf-8");
|
|
15940
16489
|
}
|
|
15941
16490
|
async function loadGitInfo(outputDir) {
|
|
15942
16491
|
try {
|
|
15943
|
-
const raw = await
|
|
16492
|
+
const raw = await readFile6(join14(outputDir, GIT_INFO_FILE), "utf-8");
|
|
15944
16493
|
const parsed = JSON.parse(raw);
|
|
15945
16494
|
if (typeof parsed === "object" && parsed != null && "sha" in parsed && typeof parsed.sha === "string") {
|
|
15946
16495
|
const branch = "branch" in parsed && typeof parsed.branch === "string" ? parsed.branch : void 0;
|
|
@@ -15962,15 +16511,15 @@ init_state();
|
|
|
15962
16511
|
init_esm_shims();
|
|
15963
16512
|
init_prompts();
|
|
15964
16513
|
init_debug();
|
|
15965
|
-
import { readFile as
|
|
15966
|
-
import { join as
|
|
16514
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
16515
|
+
import { join as join16 } from "path";
|
|
15967
16516
|
init_test_files();
|
|
15968
16517
|
var ARTIFACT_FILES = ["AUTONOMA.md", "scenarios.md", "entity-audit.md"];
|
|
15969
16518
|
async function readArtifacts(outputDir) {
|
|
15970
16519
|
const files = await Promise.all(
|
|
15971
16520
|
ARTIFACT_FILES.map(async (name) => {
|
|
15972
16521
|
try {
|
|
15973
|
-
return { name, content: await
|
|
16522
|
+
return { name, content: await readFile8(join16(outputDir, name), "utf-8") };
|
|
15974
16523
|
} catch (err) {
|
|
15975
16524
|
debugLog(`Artifact ${name} not on disk; skipping upload`, { err });
|
|
15976
16525
|
return void 0;
|
|
@@ -15986,7 +16535,7 @@ async function readTestCases(outputDir) {
|
|
|
15986
16535
|
const segments = testPath.split("/").slice(1);
|
|
15987
16536
|
const name = segments[segments.length - 1];
|
|
15988
16537
|
const folder = segments.slice(0, -1).join("/");
|
|
15989
|
-
const content = await
|
|
16538
|
+
const content = await readFile8(join16(outputDir, testPath), "utf-8");
|
|
15990
16539
|
return { name, content, folder: folder.length > 0 ? folder : void 0 };
|
|
15991
16540
|
})
|
|
15992
16541
|
);
|
|
@@ -16051,11 +16600,11 @@ process.setSourceMapsEnabled(true);
|
|
|
16051
16600
|
var PAGES_FILE = "pages.json";
|
|
16052
16601
|
async function savePages(outputDir, pages) {
|
|
16053
16602
|
const obj = Object.fromEntries(pages);
|
|
16054
|
-
await
|
|
16603
|
+
await writeFile17(join38(outputDir, PAGES_FILE), JSON.stringify(obj, null, 2), "utf-8");
|
|
16055
16604
|
}
|
|
16056
16605
|
async function loadPages(outputDir) {
|
|
16057
16606
|
try {
|
|
16058
|
-
const raw = await
|
|
16607
|
+
const raw = await readFile26(join38(outputDir, PAGES_FILE), "utf-8");
|
|
16059
16608
|
const obj = JSON.parse(raw);
|
|
16060
16609
|
return new Map(Object.entries(obj));
|
|
16061
16610
|
} catch {
|
|
@@ -16424,6 +16973,23 @@ function ensureAutonomaAuth() {
|
|
|
16424
16973
|
);
|
|
16425
16974
|
return false;
|
|
16426
16975
|
}
|
|
16976
|
+
async function runPreviewHandoffStep(frontDoor, config, nonInteractive) {
|
|
16977
|
+
const { runPreviewHandoff: runPreviewHandoff2, describeIncompletePreview: describeIncompletePreview2 } = await Promise.resolve().then(() => (init_front_door(), front_door_exports));
|
|
16978
|
+
try {
|
|
16979
|
+
const result = await runPreviewHandoff2({ plan: frontDoor, config, nonInteractive });
|
|
16980
|
+
track("cli_preview_phase_finished", { outcome: result.kind });
|
|
16981
|
+
const problem = describeIncompletePreview2(result);
|
|
16982
|
+
if (problem != null) log.warn(problem);
|
|
16983
|
+
} catch (err) {
|
|
16984
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16985
|
+
track("cli_preview_phase_finished", { outcome: "error" });
|
|
16986
|
+
trackError(err, { source: "preview_phase" });
|
|
16987
|
+
log.warn(`Couldn't set up your preview environment: ${message}`);
|
|
16988
|
+
log.info(
|
|
16989
|
+
"The rest of the run continues - finish the preview in the Autonoma app, or run again once it's sorted."
|
|
16990
|
+
);
|
|
16991
|
+
}
|
|
16992
|
+
}
|
|
16427
16993
|
async function finishFrontDoorRun(frontDoor, config, nonInteractive) {
|
|
16428
16994
|
try {
|
|
16429
16995
|
const gitInfo = await readGitInfo(config.projectRoot);
|
|
@@ -16582,11 +17148,7 @@ async function main() {
|
|
|
16582
17148
|
});
|
|
16583
17149
|
}
|
|
16584
17150
|
if (frontDoor != null && previewPending) {
|
|
16585
|
-
|
|
16586
|
-
const result = await runPreviewHandoff2({ plan: frontDoor, config, nonInteractive });
|
|
16587
|
-
track("cli_preview_phase_finished", { outcome: result.kind });
|
|
16588
|
-
const problem = describeIncompletePreview2(result);
|
|
16589
|
-
if (problem != null) log.warn(problem);
|
|
17151
|
+
await runPreviewHandoffStep(frontDoor, config, nonInteractive);
|
|
16590
17152
|
}
|
|
16591
17153
|
if (args.has("fresh") && hasProgress) {
|
|
16592
17154
|
await clearOutputDir(config.projectSlug);
|