@spotpatch/dev-server 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2256,6 +2256,9 @@ import { access as access2, lstat as lstat3, readFile as readFile2, realpath as
2256
2256
  import { createRequire } from "module";
2257
2257
  import path3 from "path";
2258
2258
  import { promisify } from "util";
2259
+ import {
2260
+ DEFAULT_AGENT_LIMITS as DEFAULT_AGENT_LIMITS2
2261
+ } from "@spotpatch/shared";
2259
2262
  var execFileAsync = promisify(execFile);
2260
2263
  var TYPESCRIPT_CHECK_ID = "spotpatch-typecheck";
2261
2264
  var TYPESCRIPT_CHECK_LABEL = "TypeScript";
@@ -2323,6 +2326,15 @@ async function resolveTypeScriptCli(appRoot) {
2323
2326
  function portableRelativePath(from, to) {
2324
2327
  return path3.relative(from, to).split(path3.sep).join("/");
2325
2328
  }
2329
+ function hasRequiredCheck(checks) {
2330
+ return Object.values(checks).some((check) => check.required);
2331
+ }
2332
+ function availableCheckId(checks, preferred) {
2333
+ if (checks[preferred] === void 0) return preferred;
2334
+ let suffix = 2;
2335
+ while (checks[`${preferred}-${String(suffix)}`] !== void 0) suffix += 1;
2336
+ return `${preferred}-${String(suffix)}`;
2337
+ }
2326
2338
  async function discoverProjectValidationCheck(options) {
2327
2339
  const appRoot = await realpath2(options.appRoot);
2328
2340
  const tsconfigPath = path3.join(appRoot, "tsconfig.json");
@@ -2351,6 +2363,8 @@ async function discoverProjectValidationCheck(options) {
2351
2363
  "--noEmit",
2352
2364
  "--pretty",
2353
2365
  "false",
2366
+ "--incremental",
2367
+ "false",
2354
2368
  "--project",
2355
2369
  projectPath
2356
2370
  ]),
@@ -2358,21 +2372,30 @@ async function discoverProjectValidationCheck(options) {
2358
2372
  timeoutMs: options.timeoutMs
2359
2373
  });
2360
2374
  }
2361
-
2362
- // src/project-options.ts
2363
- function hasRequiredCheck(ai) {
2364
- return Object.values(ai.execution.checks).some((check) => check.required);
2375
+ async function resolveProjectValidationChecks(options) {
2376
+ if (hasRequiredCheck(options.checks)) return options.checks;
2377
+ const discovered = await discoverProjectValidationCheck(options);
2378
+ if (discovered === void 0) return options.checks;
2379
+ const id = availableCheckId(options.checks, discovered.id);
2380
+ return Object.freeze({
2381
+ ...options.checks,
2382
+ [id]: Object.freeze({ ...discovered, id })
2383
+ });
2365
2384
  }
2366
- function availableCheckId(checks, preferred) {
2367
- if (checks[preferred] === void 0) {
2368
- return preferred;
2369
- }
2370
- let suffix = 2;
2371
- while (checks[`${preferred}-${String(suffix)}`] !== void 0) {
2372
- suffix += 1;
2373
- }
2374
- return `${preferred}-${String(suffix)}`;
2385
+ async function resolveManagedExecutionValidation(options) {
2386
+ const checks = options.ai === false ? Object.freeze({}) : options.ai.execution.checks;
2387
+ const limits = options.ai === false ? DEFAULT_AGENT_LIMITS2 : options.ai.execution.limits;
2388
+ return Object.freeze({
2389
+ checks: await resolveProjectValidationChecks({
2390
+ appRoot: options.appRoot,
2391
+ checks,
2392
+ timeoutMs: limits.checkTimeoutMs
2393
+ }),
2394
+ limits
2395
+ });
2375
2396
  }
2397
+
2398
+ // src/project-options.ts
2376
2399
  async function resolveProjectOptions(input) {
2377
2400
  const userOptions = input.options ?? {};
2378
2401
  const resolved = resolveOptions(userOptions, input.environmentAi);
@@ -2384,22 +2407,15 @@ async function resolveProjectOptions(input) {
2384
2407
  "SpotPatch trustedFastMode cannot be combined with applyMode auto."
2385
2408
  );
2386
2409
  }
2387
- let checks = resolved.ai.execution.checks;
2388
- if (!hasRequiredCheck(resolved.ai)) {
2389
- const discovered = await discoverProjectValidationCheck({
2390
- appRoot: input.appRoot,
2391
- timeoutMs: resolved.ai.execution.limits.checkTimeoutMs
2392
- });
2393
- if (discovered === void 0) {
2394
- throw new RangeError(
2395
- "SpotPatch trustedFastMode requires a configured required check or a local TypeScript project with tsconfig.json."
2396
- );
2397
- }
2398
- const id = availableCheckId(checks, discovered.id);
2399
- checks = Object.freeze({
2400
- ...checks,
2401
- [id]: Object.freeze({ ...discovered, id })
2402
- });
2410
+ const checks = await resolveProjectValidationChecks({
2411
+ appRoot: input.appRoot,
2412
+ checks: resolved.ai.execution.checks,
2413
+ timeoutMs: resolved.ai.execution.limits.checkTimeoutMs
2414
+ });
2415
+ if (!Object.values(checks).some((check) => check.required)) {
2416
+ throw new RangeError(
2417
+ "SpotPatch trustedFastMode requires a configured required check or a local TypeScript project with tsconfig.json."
2418
+ );
2403
2419
  }
2404
2420
  const ai = Object.freeze({
2405
2421
  ...resolved.ai,
@@ -2480,10 +2496,10 @@ function createSourceRegistry(options = {}) {
2480
2496
 
2481
2497
  // src/server/middleware.ts
2482
2498
  import {
2483
- ERROR_CODES as ERROR_CODES15,
2499
+ ERROR_CODES as ERROR_CODES16,
2484
2500
  SPOTPATCH_API_BASE,
2485
- SPOTPATCH_ENDPOINTS as SPOTPATCH_ENDPOINTS3,
2486
- SpotPatchError as SpotPatchError15,
2501
+ SPOTPATCH_ENDPOINTS as SPOTPATCH_ENDPOINTS4,
2502
+ SpotPatchError as SpotPatchError16,
2487
2503
  openEditorRequestSchema,
2488
2504
  sourceContextRequestSchema
2489
2505
  } from "@spotpatch/shared";
@@ -3061,11 +3077,171 @@ async function handleExternalHandoffBrowserRequest(request, response, route, opt
3061
3077
  writeSuccess(response, result.replayed ? 200 : 201, result);
3062
3078
  }
3063
3079
 
3064
- // src/server/agent-http.ts
3080
+ // src/external-agent/browser-http.ts
3065
3081
  import {
3066
3082
  ERROR_CODES as ERROR_CODES11,
3083
+ EXTERNAL_AGENT_CONTROL_LIMITS,
3067
3084
  SPOTPATCH_ENDPOINTS as SPOTPATCH_ENDPOINTS2,
3068
3085
  SpotPatchError as SpotPatchError11,
3086
+ externalAgentControlCancelRequestSchema,
3087
+ externalAgentControlConnectRequestSchema,
3088
+ externalAgentControlDisconnectRequestSchema,
3089
+ externalAgentControlStatusRequestSchema,
3090
+ externalAgentControlStatusSchema,
3091
+ externalAgentEventSchema,
3092
+ externalAgentEventsRequestSchema,
3093
+ externalAgentManagedResultSchema,
3094
+ externalAgentResultRequestSchema
3095
+ } from "@spotpatch/shared";
3096
+ function matchExternalAgentBrowserPath(path9) {
3097
+ if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentControlStatus) return "status";
3098
+ if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentControlConnect) return "connect";
3099
+ if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentControlDisconnect) {
3100
+ return "disconnect";
3101
+ }
3102
+ if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentControlCancel) return "cancel";
3103
+ if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentEvents) return "events";
3104
+ if (path9 === SPOTPATCH_ENDPOINTS2.externalAgentResult) return "result";
3105
+ return void 0;
3106
+ }
3107
+ function writeEvent(response, event) {
3108
+ response.write(`${JSON.stringify(externalAgentEventSchema.parse(event))}
3109
+ `);
3110
+ }
3111
+ function createExternalAgentBrowserController(port) {
3112
+ const streams = /* @__PURE__ */ new Set();
3113
+ let disposed = false;
3114
+ const handleEvents = async (request, response) => {
3115
+ const parsed = externalAgentEventsRequestSchema.safeParse(
3116
+ await readJsonRequestBody(request)
3117
+ );
3118
+ if (!parsed.success) {
3119
+ throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3120
+ }
3121
+ if (disposed || streams.size >= EXTERNAL_AGENT_CONTROL_LIMITS.maximumEventSubscribers) {
3122
+ throw new SpotPatchError11(ERROR_CODES11.BRIDGE_BUSY);
3123
+ }
3124
+ response.statusCode = 200;
3125
+ response.setHeader("Cache-Control", "no-store");
3126
+ response.setHeader("Content-Type", "application/x-ndjson; charset=utf-8");
3127
+ response.setHeader("X-Content-Type-Options", "nosniff");
3128
+ streams.add(response);
3129
+ const initial = externalAgentControlStatusSchema.parse(port.getStatus());
3130
+ writeEvent(response, { type: "status", data: initial });
3131
+ const unsubscribe = port.subscribe((status) => {
3132
+ if (!response.destroyed && status.sequence > (parsed.data.afterSequence ?? -1)) {
3133
+ writeEvent(response, { type: "status", data: status });
3134
+ }
3135
+ });
3136
+ const heartbeat = setInterval(() => {
3137
+ if (!response.destroyed) {
3138
+ const status = port.getStatus();
3139
+ writeEvent(response, {
3140
+ type: "heartbeat",
3141
+ sequence: status.sequence,
3142
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
3143
+ });
3144
+ }
3145
+ }, EXTERNAL_AGENT_CONTROL_LIMITS.eventHeartbeatMs);
3146
+ heartbeat.unref();
3147
+ await new Promise((resolve) => {
3148
+ const close = () => {
3149
+ response.off("close", close);
3150
+ clearInterval(heartbeat);
3151
+ unsubscribe();
3152
+ streams.delete(response);
3153
+ resolve();
3154
+ };
3155
+ response.once("close", close);
3156
+ });
3157
+ };
3158
+ return Object.freeze({
3159
+ async handle(request, response, route, writeSuccess) {
3160
+ if (request.method !== "POST" || disposed) {
3161
+ throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3162
+ }
3163
+ if (route === "events") {
3164
+ await handleEvents(request, response);
3165
+ return;
3166
+ }
3167
+ if (route === "status") {
3168
+ const parsed2 = externalAgentControlStatusRequestSchema.safeParse(
3169
+ await readJsonRequestBody(request)
3170
+ );
3171
+ if (!parsed2.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3172
+ writeSuccess(
3173
+ response,
3174
+ 200,
3175
+ externalAgentControlStatusSchema.parse(port.getStatus())
3176
+ );
3177
+ return;
3178
+ }
3179
+ if (route === "connect") {
3180
+ const parsed2 = externalAgentControlConnectRequestSchema.safeParse(
3181
+ await readJsonRequestBody(request)
3182
+ );
3183
+ if (!parsed2.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3184
+ const controller = new AbortController();
3185
+ response.once("close", () => {
3186
+ if (!response.writableEnded) controller.abort("browser-disconnected");
3187
+ });
3188
+ writeSuccess(
3189
+ response,
3190
+ 200,
3191
+ externalAgentControlStatusSchema.parse(
3192
+ await port.connect(parsed2.data, controller.signal)
3193
+ )
3194
+ );
3195
+ return;
3196
+ }
3197
+ if (route === "disconnect") {
3198
+ const parsed2 = externalAgentControlDisconnectRequestSchema.safeParse(
3199
+ await readJsonRequestBody(request)
3200
+ );
3201
+ if (!parsed2.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3202
+ writeSuccess(
3203
+ response,
3204
+ 200,
3205
+ externalAgentControlStatusSchema.parse(await port.disconnect(parsed2.data))
3206
+ );
3207
+ return;
3208
+ }
3209
+ if (route === "cancel") {
3210
+ const parsed2 = externalAgentControlCancelRequestSchema.safeParse(
3211
+ await readJsonRequestBody(request)
3212
+ );
3213
+ if (!parsed2.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3214
+ writeSuccess(
3215
+ response,
3216
+ 200,
3217
+ externalAgentControlStatusSchema.parse(await port.cancel(parsed2.data))
3218
+ );
3219
+ return;
3220
+ }
3221
+ const parsed = externalAgentResultRequestSchema.safeParse(
3222
+ await readJsonRequestBody(request)
3223
+ );
3224
+ if (!parsed.success) throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3225
+ const result = port.getResult(parsed.data.revision);
3226
+ if (result === void 0) {
3227
+ throw new SpotPatchError11(ERROR_CODES11.HANDOFF_NOT_FOUND);
3228
+ }
3229
+ writeSuccess(response, 200, externalAgentManagedResultSchema.parse(result));
3230
+ },
3231
+ dispose() {
3232
+ if (disposed) return;
3233
+ disposed = true;
3234
+ for (const response of streams) response.end();
3235
+ streams.clear();
3236
+ }
3237
+ });
3238
+ }
3239
+
3240
+ // src/server/agent-http.ts
3241
+ import {
3242
+ ERROR_CODES as ERROR_CODES12,
3243
+ SPOTPATCH_ENDPOINTS as SPOTPATCH_ENDPOINTS3,
3244
+ SpotPatchError as SpotPatchError12,
3069
3245
  agentCapabilityRequestSchema,
3070
3246
  agentJobActionRequestSchema,
3071
3247
  agentJobCreateRequestSchema,
@@ -3110,16 +3286,16 @@ var EVENT_STREAM_END_STATUSES = /* @__PURE__ */ new Set([
3110
3286
  "failed"
3111
3287
  ]);
3112
3288
  function matchAgentRequestPath(path9) {
3113
- if (path9 === SPOTPATCH_ENDPOINTS2.agentCapability) {
3289
+ if (path9 === SPOTPATCH_ENDPOINTS3.agentCapability) {
3114
3290
  return Object.freeze({ kind: "capability" });
3115
3291
  }
3116
- if (path9 === SPOTPATCH_ENDPOINTS2.agentWorkspaceHealth) {
3292
+ if (path9 === SPOTPATCH_ENDPOINTS3.agentWorkspaceHealth) {
3117
3293
  return Object.freeze({ kind: "workspace-health" });
3118
3294
  }
3119
- if (path9 === SPOTPATCH_ENDPOINTS2.agentJobs) {
3295
+ if (path9 === SPOTPATCH_ENDPOINTS3.agentJobs) {
3120
3296
  return Object.freeze({ kind: "create-job" });
3121
3297
  }
3122
- const prefix = `${SPOTPATCH_ENDPOINTS2.agentJobs}/`;
3298
+ const prefix = `${SPOTPATCH_ENDPOINTS3.agentJobs}/`;
3123
3299
  if (!path9.startsWith(prefix)) {
3124
3300
  return void 0;
3125
3301
  }
@@ -3137,7 +3313,7 @@ function matchAgentRequestPath(path9) {
3137
3313
  }
3138
3314
  function requireAgentManager(options) {
3139
3315
  if (options.agentManager === void 0 || options.options.ai === false) {
3140
- throw new SpotPatchError11(ERROR_CODES11.AI_DISABLED);
3316
+ throw new SpotPatchError12(ERROR_CODES12.AI_DISABLED);
3141
3317
  }
3142
3318
  return options.agentManager;
3143
3319
  }
@@ -3190,13 +3366,13 @@ function streamAgentJobEvents(response, manager, jobId) {
3190
3366
  }
3191
3367
  async function handleCapability(request, response, options, writeSuccess) {
3192
3368
  if (request.method !== "POST") {
3193
- throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3369
+ throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3194
3370
  }
3195
3371
  const parsed = agentCapabilityRequestSchema.safeParse(
3196
3372
  await readJsonRequestBody(request)
3197
3373
  );
3198
3374
  if (!parsed.success) {
3199
- throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3375
+ throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3200
3376
  }
3201
3377
  const controller = new AbortController();
3202
3378
  const abort = () => {
@@ -3215,13 +3391,13 @@ async function handleCapability(request, response, options, writeSuccess) {
3215
3391
  }
3216
3392
  async function handleCreateJob(request, response, options, writeSuccess) {
3217
3393
  if (request.method !== "POST") {
3218
- throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3394
+ throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3219
3395
  }
3220
3396
  const parsed = agentJobCreateRequestSchema.safeParse(
3221
3397
  await readJsonRequestBody(request, MAX_AGENT_REQUEST_BODY_BYTES)
3222
3398
  );
3223
3399
  if (!parsed.success) {
3224
- throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3400
+ throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3225
3401
  }
3226
3402
  const authorizedRequest = await authorizeAgentJobRequest({
3227
3403
  request: parsed.data,
@@ -3234,13 +3410,13 @@ async function handleCreateJob(request, response, options, writeSuccess) {
3234
3410
  }
3235
3411
  async function handleWorkspaceHealth(request, response, options, writeSuccess) {
3236
3412
  if (request.method !== "POST") {
3237
- throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3413
+ throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3238
3414
  }
3239
3415
  const parsed = agentWorkspaceHealthRequestSchema.safeParse(
3240
3416
  await readJsonRequestBody(request)
3241
3417
  );
3242
3418
  if (!parsed.success) {
3243
- throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3419
+ throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3244
3420
  }
3245
3421
  const controller = new AbortController();
3246
3422
  const abort = () => {
@@ -3257,13 +3433,13 @@ async function handleWorkspaceHealth(request, response, options, writeSuccess) {
3257
3433
  async function handleJobAction(request, response, options, route, writeSuccess) {
3258
3434
  const manager = requireAgentManager(options);
3259
3435
  if (request.method !== "POST") {
3260
- throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3436
+ throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3261
3437
  }
3262
3438
  const parsed = agentJobActionRequestSchema.safeParse(
3263
3439
  await readJsonRequestBody(request)
3264
3440
  );
3265
3441
  if (!parsed.success) {
3266
- throw new SpotPatchError11(ERROR_CODES11.INVALID_REQUEST);
3442
+ throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3267
3443
  }
3268
3444
  if (route.action === "events") {
3269
3445
  streamAgentJobEvents(response, manager, route.jobId);
@@ -3384,8 +3560,8 @@ import {
3384
3560
  } from "@spotpatch/analyzer";
3385
3561
  import {
3386
3562
  DATA_FLOW_SCHEMA_VERSION,
3387
- ERROR_CODES as ERROR_CODES12,
3388
- SpotPatchError as SpotPatchError12,
3563
+ ERROR_CODES as ERROR_CODES13,
3564
+ SpotPatchError as SpotPatchError13,
3389
3565
  dataFlowComponentReportRequestSchema,
3390
3566
  dataFlowPageReportRequestSchema,
3391
3567
  limitDataFlowReportCollections
@@ -3404,7 +3580,7 @@ function limitDataFlowReportToBytes(report, maximumBytes) {
3404
3580
  truncatedBy: "bytes"
3405
3581
  });
3406
3582
  if (envelopeBytes(limited) > maximumBytes) {
3407
- throw new SpotPatchError12(ERROR_CODES12.INTERNAL_ERROR);
3583
+ throw new SpotPatchError13(ERROR_CODES13.INTERNAL_ERROR);
3408
3584
  }
3409
3585
  for (let maximumDependencies = 1; maximumDependencies <= structurallyLimited.dependencies.length; maximumDependencies += 1) {
3410
3586
  const candidate = limitDataFlowReportCollections(structurallyLimited, {
@@ -3433,7 +3609,7 @@ async function analyzeTarget(request, analyzer, options) {
3433
3609
  request.componentSourceId
3434
3610
  );
3435
3611
  if (anchor?.sourceVersion !== request.sourceVersion) {
3436
- throw new SpotPatchError12(ERROR_CODES12.DATA_FLOW_SOURCE_STALE);
3612
+ throw new SpotPatchError13(ERROR_CODES13.DATA_FLOW_SOURCE_STALE);
3437
3613
  }
3438
3614
  return anchor;
3439
3615
  }
@@ -3450,7 +3626,7 @@ async function analyzeTarget(request, analyzer, options) {
3450
3626
  column: resolvedRequest.column
3451
3627
  });
3452
3628
  if (resolvedRequest.sourceVersion !== void 0 && resolvedRequest.sourceVersion !== report.component.source.sourceVersion) {
3453
- throw new SpotPatchError12(ERROR_CODES12.DATA_FLOW_SOURCE_STALE);
3629
+ throw new SpotPatchError13(ERROR_CODES13.DATA_FLOW_SOURCE_STALE);
3454
3630
  }
3455
3631
  return limitDataFlowReportToBytes(
3456
3632
  report,
@@ -3459,7 +3635,7 @@ async function analyzeTarget(request, analyzer, options) {
3459
3635
  }
3460
3636
  function requireAnalyzer(analyzer) {
3461
3637
  if (analyzer === void 0) {
3462
- throw new SpotPatchError12(ERROR_CODES12.DATA_FLOW_DISABLED);
3638
+ throw new SpotPatchError13(ERROR_CODES13.DATA_FLOW_DISABLED);
3463
3639
  }
3464
3640
  return analyzer;
3465
3641
  }
@@ -3471,7 +3647,7 @@ async function handleComponentDataFlowReport(request, analyzer, options) {
3471
3647
  )
3472
3648
  );
3473
3649
  if (!parsed.success) {
3474
- throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3650
+ throw new SpotPatchError13(ERROR_CODES13.INVALID_REQUEST);
3475
3651
  }
3476
3652
  return analyzeTarget(parsed.data, requireAnalyzer(analyzer), options);
3477
3653
  }
@@ -3483,7 +3659,7 @@ async function handlePageDataFlowReport(request, analyzer, options) {
3483
3659
  )
3484
3660
  );
3485
3661
  if (!parsed.success) {
3486
- throw new SpotPatchError12(ERROR_CODES12.INVALID_REQUEST);
3662
+ throw new SpotPatchError13(ERROR_CODES13.INVALID_REQUEST);
3487
3663
  }
3488
3664
  const activeAnalyzer = requireAnalyzer(analyzer);
3489
3665
  const componentReports = await Promise.all(
@@ -3556,7 +3732,7 @@ async function handlePageDataFlowReport(request, analyzer, options) {
3556
3732
  // src/server/request-security.ts
3557
3733
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
3558
3734
  import { isIP } from "net";
3559
- import { ERROR_CODES as ERROR_CODES13, SPOTPATCH_TOKEN_HEADER, SpotPatchError as SpotPatchError13 } from "@spotpatch/shared";
3735
+ import { ERROR_CODES as ERROR_CODES14, SPOTPATCH_TOKEN_HEADER, SpotPatchError as SpotPatchError14 } from "@spotpatch/shared";
3560
3736
  function getSingleHeader(request, name) {
3561
3737
  const value = request.headers[name.toLowerCase()];
3562
3738
  return Array.isArray(value) ? value[0] : value;
@@ -3603,32 +3779,32 @@ function parseOrigin(value) {
3603
3779
  function assertRequestAuthorized(request, options) {
3604
3780
  const actualToken = getSingleHeader(request, SPOTPATCH_TOKEN_HEADER);
3605
3781
  if (!tokensMatch2(actualToken, options.sessionToken)) {
3606
- throw new SpotPatchError13(ERROR_CODES13.INVALID_TOKEN);
3782
+ throw new SpotPatchError14(ERROR_CODES14.INVALID_TOKEN);
3607
3783
  }
3608
3784
  const hostHeader = getSingleHeader(request, "host");
3609
3785
  const originHeader = getSingleHeader(request, "origin");
3610
3786
  const host = hostHeader === void 0 ? void 0 : parseHost(hostHeader);
3611
3787
  const origin = originHeader === void 0 ? void 0 : parseOrigin(originHeader);
3612
3788
  if (host === void 0 || origin === void 0) {
3613
- throw new SpotPatchError13(ERROR_CODES13.ORIGIN_NOT_ALLOWED);
3789
+ throw new SpotPatchError14(ERROR_CODES14.ORIGIN_NOT_ALLOWED);
3614
3790
  }
3615
3791
  const hostIsLoopback = isLoopbackHostname(host.hostname);
3616
3792
  const originIsLoopback = isLoopbackHostname(origin.hostname);
3617
3793
  if (!options.allowLan) {
3618
3794
  if (!hostIsLoopback || !originIsLoopback) {
3619
- throw new SpotPatchError13(ERROR_CODES13.ORIGIN_NOT_ALLOWED);
3795
+ throw new SpotPatchError14(ERROR_CODES14.ORIGIN_NOT_ALLOWED);
3620
3796
  }
3621
3797
  return;
3622
3798
  }
3623
3799
  if (!originIsLoopback && origin.host.toLowerCase() !== host.host.toLowerCase()) {
3624
- throw new SpotPatchError13(ERROR_CODES13.ORIGIN_NOT_ALLOWED);
3800
+ throw new SpotPatchError14(ERROR_CODES14.ORIGIN_NOT_ALLOWED);
3625
3801
  }
3626
3802
  }
3627
3803
 
3628
3804
  // src/server/runtime-bootstrap.ts
3629
3805
  import {
3630
- ERROR_CODES as ERROR_CODES14,
3631
- SpotPatchError as SpotPatchError14,
3806
+ ERROR_CODES as ERROR_CODES15,
3807
+ SpotPatchError as SpotPatchError15,
3632
3808
  runtimeBootstrapRequestSchema,
3633
3809
  runtimeConfigSchema
3634
3810
  } from "@spotpatch/shared";
@@ -3658,7 +3834,7 @@ function resolveRuntimeBootstrapOptions(options) {
3658
3834
  function assertRuntimeBootstrapRequest(request, expectedOrigin) {
3659
3835
  const contentType = getSingleHeader2(request, "content-type")?.split(";", 1)[0]?.trim().toLowerCase();
3660
3836
  if (request.method !== "POST" || contentType !== "application/json") {
3661
- throw new SpotPatchError14(ERROR_CODES14.INVALID_REQUEST);
3837
+ throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
3662
3838
  }
3663
3839
  const host = getSingleHeader2(request, "host");
3664
3840
  let hostIsLoopback = false;
@@ -3670,7 +3846,7 @@ function assertRuntimeBootstrapRequest(request, expectedOrigin) {
3670
3846
  }
3671
3847
  }
3672
3848
  if (!hostIsLoopback || getSingleHeader2(request, "origin") !== expectedOrigin || getSingleHeader2(request, "sec-fetch-site") !== "same-origin") {
3673
- throw new SpotPatchError14(ERROR_CODES14.ORIGIN_NOT_ALLOWED);
3849
+ throw new SpotPatchError15(ERROR_CODES15.ORIGIN_NOT_ALLOWED);
3674
3850
  }
3675
3851
  }
3676
3852
  async function readRuntimeBootstrap(request, options) {
@@ -3679,123 +3855,123 @@ async function readRuntimeBootstrap(request, options) {
3679
3855
  await readJsonRequestBody(request)
3680
3856
  );
3681
3857
  if (!parsedBody.success) {
3682
- throw new SpotPatchError14(ERROR_CODES14.INVALID_REQUEST);
3858
+ throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
3683
3859
  }
3684
3860
  return options.runtimeConfig;
3685
3861
  }
3686
3862
 
3687
3863
  // src/server/middleware.ts
3688
3864
  var STATUS_BY_ERROR = Object.freeze({
3689
- [ERROR_CODES15.INVALID_REQUEST]: 400,
3690
- [ERROR_CODES15.INVALID_TOKEN]: 401,
3691
- [ERROR_CODES15.ORIGIN_NOT_ALLOWED]: 403,
3692
- [ERROR_CODES15.SOURCE_NOT_FOUND]: 404,
3693
- [ERROR_CODES15.SOURCE_OUTSIDE_ROOT]: 403,
3694
- [ERROR_CODES15.SOURCE_TOO_LARGE]: 413,
3695
- [ERROR_CODES15.EDITOR_OPEN_FAILED]: 500,
3696
- [ERROR_CODES15.DATA_FLOW_DISABLED]: 404,
3697
- [ERROR_CODES15.DATA_FLOW_SOURCE_STALE]: 409,
3698
- [ERROR_CODES15.DATA_FLOW_ANALYSIS_CANCELLED]: 409,
3699
- [ERROR_CODES15.AI_DISABLED]: 404,
3700
- [ERROR_CODES15.PROVIDER_NOT_CONFIGURED]: 503,
3701
- [ERROR_CODES15.PROVIDER_AUTH_FAILED]: 502,
3702
- [ERROR_CODES15.PROVIDER_PROTOCOL_UNSUPPORTED]: 502,
3703
- [ERROR_CODES15.MODEL_NOT_ALLOWED]: 400,
3704
- [ERROR_CODES15.MODEL_TOOL_CALL_UNSUPPORTED]: 422,
3705
- [ERROR_CODES15.PROVIDER_RATE_LIMITED]: 429,
3706
- [ERROR_CODES15.AGENT_BUSY]: 409,
3707
- [ERROR_CODES15.AGENT_LIMIT_EXCEEDED]: 413,
3708
- [ERROR_CODES15.AGENT_CANCELLED]: 409,
3709
- [ERROR_CODES15.EXTERNAL_HANDOFF_DISABLED]: 404,
3710
- [ERROR_CODES15.EXTERNAL_HANDOFF_UNAVAILABLE]: 503,
3711
- [ERROR_CODES15.HANDOFF_VALIDATION_FAILED]: 422,
3712
- [ERROR_CODES15.HANDOFF_SOURCE_STALE]: 409,
3713
- [ERROR_CODES15.HANDOFF_NOT_FOUND]: 404,
3714
- [ERROR_CODES15.HANDOFF_EXPIRED]: 410,
3715
- [ERROR_CODES15.HANDOFF_CURSOR_INVALID]: 409,
3716
- [ERROR_CODES15.HANDOFF_RESPONSE_TOO_LARGE]: 413,
3717
- [ERROR_CODES15.BRIDGE_UNAUTHORIZED]: 401,
3718
- [ERROR_CODES15.BRIDGE_PROTOCOL_MISMATCH]: 409,
3719
- [ERROR_CODES15.BRIDGE_BUSY]: 429,
3720
- [ERROR_CODES15.EXTERNAL_AGENT_BUSY]: 409,
3721
- [ERROR_CODES15.ACTIVE_ADAPTER_CONFLICT]: 409,
3722
- [ERROR_CODES15.ACTIVE_ADAPTER_LEASE_INVALID]: 409,
3723
- [ERROR_CODES15.ACTIVE_DISPATCH_INVALID]: 409,
3724
- [ERROR_CODES15.SESSION_NOT_FOUND]: 404,
3725
- [ERROR_CODES15.SESSION_AMBIGUOUS]: 409,
3726
- [ERROR_CODES15.SESSION_CLOSED]: 410,
3727
- [ERROR_CODES15.WORKTREE_DIRTY]: 409,
3728
- [ERROR_CODES15.WORKTREE_NOT_REPOSITORY]: 409,
3729
- [ERROR_CODES15.WORKTREE_OPERATION_IN_PROGRESS]: 409,
3730
- [ERROR_CODES15.WORKTREE_CONFLICTED]: 409,
3731
- [ERROR_CODES15.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: 413,
3732
- [ERROR_CODES15.WORKTREE_UNTRACKED_UNSUPPORTED]: 409,
3733
- [ERROR_CODES15.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: 409,
3734
- [ERROR_CODES15.TOOL_DENIED]: 403,
3735
- [ERROR_CODES15.TOOL_INPUT_INVALID]: 422,
3736
- [ERROR_CODES15.TOOL_ARGUMENTS_INVALID]: 422,
3737
- [ERROR_CODES15.TOOL_CALL_ID_CONFLICT]: 422,
3738
- [ERROR_CODES15.TOOL_PATH_DENIED]: 403,
3739
- [ERROR_CODES15.PATCH_REJECTED]: 422,
3740
- [ERROR_CODES15.VALIDATION_FAILED]: 422,
3741
- [ERROR_CODES15.APPLY_CONFLICT]: 409,
3742
- [ERROR_CODES15.INTERNAL_ERROR]: 500
3865
+ [ERROR_CODES16.INVALID_REQUEST]: 400,
3866
+ [ERROR_CODES16.INVALID_TOKEN]: 401,
3867
+ [ERROR_CODES16.ORIGIN_NOT_ALLOWED]: 403,
3868
+ [ERROR_CODES16.SOURCE_NOT_FOUND]: 404,
3869
+ [ERROR_CODES16.SOURCE_OUTSIDE_ROOT]: 403,
3870
+ [ERROR_CODES16.SOURCE_TOO_LARGE]: 413,
3871
+ [ERROR_CODES16.EDITOR_OPEN_FAILED]: 500,
3872
+ [ERROR_CODES16.DATA_FLOW_DISABLED]: 404,
3873
+ [ERROR_CODES16.DATA_FLOW_SOURCE_STALE]: 409,
3874
+ [ERROR_CODES16.DATA_FLOW_ANALYSIS_CANCELLED]: 409,
3875
+ [ERROR_CODES16.AI_DISABLED]: 404,
3876
+ [ERROR_CODES16.PROVIDER_NOT_CONFIGURED]: 503,
3877
+ [ERROR_CODES16.PROVIDER_AUTH_FAILED]: 502,
3878
+ [ERROR_CODES16.PROVIDER_PROTOCOL_UNSUPPORTED]: 502,
3879
+ [ERROR_CODES16.MODEL_NOT_ALLOWED]: 400,
3880
+ [ERROR_CODES16.MODEL_TOOL_CALL_UNSUPPORTED]: 422,
3881
+ [ERROR_CODES16.PROVIDER_RATE_LIMITED]: 429,
3882
+ [ERROR_CODES16.AGENT_BUSY]: 409,
3883
+ [ERROR_CODES16.AGENT_LIMIT_EXCEEDED]: 413,
3884
+ [ERROR_CODES16.AGENT_CANCELLED]: 409,
3885
+ [ERROR_CODES16.EXTERNAL_HANDOFF_DISABLED]: 404,
3886
+ [ERROR_CODES16.EXTERNAL_HANDOFF_UNAVAILABLE]: 503,
3887
+ [ERROR_CODES16.HANDOFF_VALIDATION_FAILED]: 422,
3888
+ [ERROR_CODES16.HANDOFF_SOURCE_STALE]: 409,
3889
+ [ERROR_CODES16.HANDOFF_NOT_FOUND]: 404,
3890
+ [ERROR_CODES16.HANDOFF_EXPIRED]: 410,
3891
+ [ERROR_CODES16.HANDOFF_CURSOR_INVALID]: 409,
3892
+ [ERROR_CODES16.HANDOFF_RESPONSE_TOO_LARGE]: 413,
3893
+ [ERROR_CODES16.BRIDGE_UNAUTHORIZED]: 401,
3894
+ [ERROR_CODES16.BRIDGE_PROTOCOL_MISMATCH]: 409,
3895
+ [ERROR_CODES16.BRIDGE_BUSY]: 429,
3896
+ [ERROR_CODES16.EXTERNAL_AGENT_BUSY]: 409,
3897
+ [ERROR_CODES16.ACTIVE_ADAPTER_CONFLICT]: 409,
3898
+ [ERROR_CODES16.ACTIVE_ADAPTER_LEASE_INVALID]: 409,
3899
+ [ERROR_CODES16.ACTIVE_DISPATCH_INVALID]: 409,
3900
+ [ERROR_CODES16.SESSION_NOT_FOUND]: 404,
3901
+ [ERROR_CODES16.SESSION_AMBIGUOUS]: 409,
3902
+ [ERROR_CODES16.SESSION_CLOSED]: 410,
3903
+ [ERROR_CODES16.WORKTREE_DIRTY]: 409,
3904
+ [ERROR_CODES16.WORKTREE_NOT_REPOSITORY]: 409,
3905
+ [ERROR_CODES16.WORKTREE_OPERATION_IN_PROGRESS]: 409,
3906
+ [ERROR_CODES16.WORKTREE_CONFLICTED]: 409,
3907
+ [ERROR_CODES16.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: 413,
3908
+ [ERROR_CODES16.WORKTREE_UNTRACKED_UNSUPPORTED]: 409,
3909
+ [ERROR_CODES16.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: 409,
3910
+ [ERROR_CODES16.TOOL_DENIED]: 403,
3911
+ [ERROR_CODES16.TOOL_INPUT_INVALID]: 422,
3912
+ [ERROR_CODES16.TOOL_ARGUMENTS_INVALID]: 422,
3913
+ [ERROR_CODES16.TOOL_CALL_ID_CONFLICT]: 422,
3914
+ [ERROR_CODES16.TOOL_PATH_DENIED]: 403,
3915
+ [ERROR_CODES16.PATCH_REJECTED]: 422,
3916
+ [ERROR_CODES16.VALIDATION_FAILED]: 422,
3917
+ [ERROR_CODES16.APPLY_CONFLICT]: 409,
3918
+ [ERROR_CODES16.INTERNAL_ERROR]: 500
3743
3919
  });
3744
3920
  var PUBLIC_MESSAGES = Object.freeze({
3745
- [ERROR_CODES15.INVALID_REQUEST]: "The request is invalid.",
3746
- [ERROR_CODES15.INVALID_TOKEN]: "The session token is invalid.",
3747
- [ERROR_CODES15.ORIGIN_NOT_ALLOWED]: "The request origin is not allowed.",
3748
- [ERROR_CODES15.SOURCE_NOT_FOUND]: "The source file is unavailable.",
3749
- [ERROR_CODES15.SOURCE_OUTSIDE_ROOT]: "The source file is outside the project root.",
3750
- [ERROR_CODES15.SOURCE_TOO_LARGE]: "The source file exceeds the size limit.",
3751
- [ERROR_CODES15.EDITOR_OPEN_FAILED]: "The editor request could not be started.",
3752
- [ERROR_CODES15.DATA_FLOW_DISABLED]: "Component data-flow analysis is not enabled.",
3753
- [ERROR_CODES15.DATA_FLOW_SOURCE_STALE]: "The selected source version is stale.",
3754
- [ERROR_CODES15.DATA_FLOW_ANALYSIS_CANCELLED]: "The data-flow analysis was cancelled.",
3755
- [ERROR_CODES15.AI_DISABLED]: "AI execution is not enabled.",
3756
- [ERROR_CODES15.PROVIDER_NOT_CONFIGURED]: "The AI provider is unavailable.",
3757
- [ERROR_CODES15.PROVIDER_AUTH_FAILED]: "The AI provider rejected authentication.",
3758
- [ERROR_CODES15.PROVIDER_PROTOCOL_UNSUPPORTED]: "The AI provider protocol is unsupported.",
3759
- [ERROR_CODES15.MODEL_NOT_ALLOWED]: "The selected model is not allowed.",
3760
- [ERROR_CODES15.MODEL_TOOL_CALL_UNSUPPORTED]: "The selected model cannot run SpotPatch tools.",
3761
- [ERROR_CODES15.PROVIDER_RATE_LIMITED]: "The AI provider is rate limited.",
3762
- [ERROR_CODES15.AGENT_BUSY]: "Another Agent job is already running.",
3763
- [ERROR_CODES15.AGENT_LIMIT_EXCEEDED]: "The Agent job exceeded a safety limit.",
3764
- [ERROR_CODES15.AGENT_CANCELLED]: "The Agent job was cancelled.",
3765
- [ERROR_CODES15.EXTERNAL_HANDOFF_DISABLED]: "External Agent handoff is not enabled.",
3766
- [ERROR_CODES15.EXTERNAL_HANDOFF_UNAVAILABLE]: "External Agent handoff is temporarily unavailable.",
3767
- [ERROR_CODES15.HANDOFF_VALIDATION_FAILED]: "The handoff content is invalid.",
3768
- [ERROR_CODES15.HANDOFF_SOURCE_STALE]: "The selected source is stale.",
3769
- [ERROR_CODES15.HANDOFF_NOT_FOUND]: "No current handoff is available.",
3770
- [ERROR_CODES15.HANDOFF_EXPIRED]: "The handoff has expired.",
3771
- [ERROR_CODES15.HANDOFF_CURSOR_INVALID]: "The handoff cursor is invalid.",
3772
- [ERROR_CODES15.HANDOFF_RESPONSE_TOO_LARGE]: "The handoff exceeds the size limit.",
3773
- [ERROR_CODES15.BRIDGE_UNAUTHORIZED]: "The local bridge request is unauthorized.",
3774
- [ERROR_CODES15.BRIDGE_PROTOCOL_MISMATCH]: "The local bridge protocol is incompatible.",
3775
- [ERROR_CODES15.BRIDGE_BUSY]: "The local bridge is busy.",
3776
- [ERROR_CODES15.EXTERNAL_AGENT_BUSY]: "The connected external Agent is busy.",
3777
- [ERROR_CODES15.ACTIVE_ADAPTER_CONFLICT]: "Another active Agent adapter is connected.",
3778
- [ERROR_CODES15.ACTIVE_ADAPTER_LEASE_INVALID]: "The active Agent adapter lease is invalid.",
3779
- [ERROR_CODES15.ACTIVE_DISPATCH_INVALID]: "The active Agent dispatch transition is invalid.",
3780
- [ERROR_CODES15.SESSION_NOT_FOUND]: "No active SpotPatch session was found.",
3781
- [ERROR_CODES15.SESSION_AMBIGUOUS]: "More than one SpotPatch session matches.",
3782
- [ERROR_CODES15.SESSION_CLOSED]: "The SpotPatch session has closed.",
3783
- [ERROR_CODES15.WORKTREE_DIRTY]: "Local changes require explicit inclusion consent.",
3784
- [ERROR_CODES15.WORKTREE_NOT_REPOSITORY]: "The project root is not a Git repository.",
3785
- [ERROR_CODES15.WORKTREE_OPERATION_IN_PROGRESS]: "A Git operation is currently in progress.",
3786
- [ERROR_CODES15.WORKTREE_CONFLICTED]: "The local workspace contains unresolved merge conflicts.",
3787
- [ERROR_CODES15.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: "The local workspace exceeds the safe isolation size limit.",
3788
- [ERROR_CODES15.WORKTREE_UNTRACKED_UNSUPPORTED]: "An untracked path cannot be isolated safely.",
3789
- [ERROR_CODES15.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: "The local workspace state cannot be isolated safely.",
3790
- [ERROR_CODES15.TOOL_DENIED]: "The Agent tool request was denied.",
3791
- [ERROR_CODES15.TOOL_INPUT_INVALID]: "The Agent tool input was invalid.",
3792
- [ERROR_CODES15.TOOL_ARGUMENTS_INVALID]: "The Agent tool arguments are invalid.",
3793
- [ERROR_CODES15.TOOL_CALL_ID_CONFLICT]: "A tool call ID conflicts within one Agent turn.",
3794
- [ERROR_CODES15.TOOL_PATH_DENIED]: "The Agent tool path was denied.",
3795
- [ERROR_CODES15.PATCH_REJECTED]: "The proposed patch was rejected.",
3796
- [ERROR_CODES15.VALIDATION_FAILED]: "The proposed change failed validation.",
3797
- [ERROR_CODES15.APPLY_CONFLICT]: "The change conflicts with the current worktree.",
3798
- [ERROR_CODES15.INTERNAL_ERROR]: "The request could not be completed."
3921
+ [ERROR_CODES16.INVALID_REQUEST]: "The request is invalid.",
3922
+ [ERROR_CODES16.INVALID_TOKEN]: "The session token is invalid.",
3923
+ [ERROR_CODES16.ORIGIN_NOT_ALLOWED]: "The request origin is not allowed.",
3924
+ [ERROR_CODES16.SOURCE_NOT_FOUND]: "The source file is unavailable.",
3925
+ [ERROR_CODES16.SOURCE_OUTSIDE_ROOT]: "The source file is outside the project root.",
3926
+ [ERROR_CODES16.SOURCE_TOO_LARGE]: "The source file exceeds the size limit.",
3927
+ [ERROR_CODES16.EDITOR_OPEN_FAILED]: "The editor request could not be started.",
3928
+ [ERROR_CODES16.DATA_FLOW_DISABLED]: "Component data-flow analysis is not enabled.",
3929
+ [ERROR_CODES16.DATA_FLOW_SOURCE_STALE]: "The selected source version is stale.",
3930
+ [ERROR_CODES16.DATA_FLOW_ANALYSIS_CANCELLED]: "The data-flow analysis was cancelled.",
3931
+ [ERROR_CODES16.AI_DISABLED]: "AI execution is not enabled.",
3932
+ [ERROR_CODES16.PROVIDER_NOT_CONFIGURED]: "The AI provider is unavailable.",
3933
+ [ERROR_CODES16.PROVIDER_AUTH_FAILED]: "The AI provider rejected authentication.",
3934
+ [ERROR_CODES16.PROVIDER_PROTOCOL_UNSUPPORTED]: "The AI provider protocol is unsupported.",
3935
+ [ERROR_CODES16.MODEL_NOT_ALLOWED]: "The selected model is not allowed.",
3936
+ [ERROR_CODES16.MODEL_TOOL_CALL_UNSUPPORTED]: "The selected model cannot run SpotPatch tools.",
3937
+ [ERROR_CODES16.PROVIDER_RATE_LIMITED]: "The AI provider is rate limited.",
3938
+ [ERROR_CODES16.AGENT_BUSY]: "Another Agent job is already running.",
3939
+ [ERROR_CODES16.AGENT_LIMIT_EXCEEDED]: "The Agent job exceeded a safety limit.",
3940
+ [ERROR_CODES16.AGENT_CANCELLED]: "The Agent job was cancelled.",
3941
+ [ERROR_CODES16.EXTERNAL_HANDOFF_DISABLED]: "External Agent handoff is not enabled.",
3942
+ [ERROR_CODES16.EXTERNAL_HANDOFF_UNAVAILABLE]: "External Agent handoff is temporarily unavailable.",
3943
+ [ERROR_CODES16.HANDOFF_VALIDATION_FAILED]: "The handoff content is invalid.",
3944
+ [ERROR_CODES16.HANDOFF_SOURCE_STALE]: "The selected source is stale.",
3945
+ [ERROR_CODES16.HANDOFF_NOT_FOUND]: "No current handoff is available.",
3946
+ [ERROR_CODES16.HANDOFF_EXPIRED]: "The handoff has expired.",
3947
+ [ERROR_CODES16.HANDOFF_CURSOR_INVALID]: "The handoff cursor is invalid.",
3948
+ [ERROR_CODES16.HANDOFF_RESPONSE_TOO_LARGE]: "The handoff exceeds the size limit.",
3949
+ [ERROR_CODES16.BRIDGE_UNAUTHORIZED]: "The local bridge request is unauthorized.",
3950
+ [ERROR_CODES16.BRIDGE_PROTOCOL_MISMATCH]: "The local bridge protocol is incompatible.",
3951
+ [ERROR_CODES16.BRIDGE_BUSY]: "The local bridge is busy.",
3952
+ [ERROR_CODES16.EXTERNAL_AGENT_BUSY]: "The connected external Agent is busy.",
3953
+ [ERROR_CODES16.ACTIVE_ADAPTER_CONFLICT]: "Another active Agent adapter is connected.",
3954
+ [ERROR_CODES16.ACTIVE_ADAPTER_LEASE_INVALID]: "The active Agent adapter lease is invalid.",
3955
+ [ERROR_CODES16.ACTIVE_DISPATCH_INVALID]: "The active Agent dispatch transition is invalid.",
3956
+ [ERROR_CODES16.SESSION_NOT_FOUND]: "No active SpotPatch session was found.",
3957
+ [ERROR_CODES16.SESSION_AMBIGUOUS]: "More than one SpotPatch session matches.",
3958
+ [ERROR_CODES16.SESSION_CLOSED]: "The SpotPatch session has closed.",
3959
+ [ERROR_CODES16.WORKTREE_DIRTY]: "Local changes require explicit inclusion consent.",
3960
+ [ERROR_CODES16.WORKTREE_NOT_REPOSITORY]: "The project root is not a Git repository.",
3961
+ [ERROR_CODES16.WORKTREE_OPERATION_IN_PROGRESS]: "A Git operation is currently in progress.",
3962
+ [ERROR_CODES16.WORKTREE_CONFLICTED]: "The local workspace contains unresolved merge conflicts.",
3963
+ [ERROR_CODES16.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: "The local workspace exceeds the safe isolation size limit.",
3964
+ [ERROR_CODES16.WORKTREE_UNTRACKED_UNSUPPORTED]: "An untracked path cannot be isolated safely.",
3965
+ [ERROR_CODES16.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: "The local workspace state cannot be isolated safely.",
3966
+ [ERROR_CODES16.TOOL_DENIED]: "The Agent tool request was denied.",
3967
+ [ERROR_CODES16.TOOL_INPUT_INVALID]: "The Agent tool input was invalid.",
3968
+ [ERROR_CODES16.TOOL_ARGUMENTS_INVALID]: "The Agent tool arguments are invalid.",
3969
+ [ERROR_CODES16.TOOL_CALL_ID_CONFLICT]: "A tool call ID conflicts within one Agent turn.",
3970
+ [ERROR_CODES16.TOOL_PATH_DENIED]: "The Agent tool path was denied.",
3971
+ [ERROR_CODES16.PATCH_REJECTED]: "The proposed patch was rejected.",
3972
+ [ERROR_CODES16.VALIDATION_FAILED]: "The proposed change failed validation.",
3973
+ [ERROR_CODES16.APPLY_CONFLICT]: "The change conflicts with the current worktree.",
3974
+ [ERROR_CODES16.INTERNAL_ERROR]: "The request could not be completed."
3799
3975
  });
3800
3976
  function writeJson2(response, status, payload) {
3801
3977
  response.statusCode = status;
@@ -3804,11 +3980,11 @@ function writeJson2(response, status, payload) {
3804
3980
  response.end(JSON.stringify(payload));
3805
3981
  }
3806
3982
  function asSpotPatchError(error) {
3807
- return error instanceof SpotPatchError15 ? error : new SpotPatchError15(ERROR_CODES15.INTERNAL_ERROR, void 0, { cause: error });
3983
+ return error instanceof SpotPatchError16 ? error : new SpotPatchError16(ERROR_CODES16.INTERNAL_ERROR, void 0, { cause: error });
3808
3984
  }
3809
3985
  function writeError(response, error, logger) {
3810
3986
  const normalized = asSpotPatchError(error);
3811
- if (normalized.code === ERROR_CODES15.INTERNAL_ERROR) {
3987
+ if (normalized.code === ERROR_CODES16.INTERNAL_ERROR) {
3812
3988
  logger?.warn("[spotpatch:server] Internal request failure.");
3813
3989
  }
3814
3990
  writeJson2(response, STATUS_BY_ERROR[normalized.code], {
@@ -3831,7 +4007,7 @@ async function handleSourceContext(request, options) {
3831
4007
  await readJsonRequestBody(request)
3832
4008
  );
3833
4009
  if (!parsed.success) {
3834
- throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
4010
+ throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
3835
4011
  }
3836
4012
  return readSourceContext({
3837
4013
  request: parsed.data,
@@ -3844,7 +4020,7 @@ async function handleSourceContext(request, options) {
3844
4020
  async function handleOpenEditor(request, options) {
3845
4021
  const parsed = openEditorRequestSchema.safeParse(await readJsonRequestBody(request));
3846
4022
  if (!parsed.success) {
3847
- throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
4023
+ throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
3848
4024
  }
3849
4025
  const body = parsed.data;
3850
4026
  const sourcePath = await resolveSourceFile({
@@ -3861,7 +4037,7 @@ async function handleOpenEditor(request, options) {
3861
4037
  options.logger?.warn(
3862
4038
  `[spotpatch:server] ${options.options.editor === "auto" ? "The detected editor" : options.options.editor} rejected an editor request.`
3863
4039
  );
3864
- throw new SpotPatchError15(ERROR_CODES15.EDITOR_OPEN_FAILED, void 0, {
4040
+ throw new SpotPatchError16(ERROR_CODES16.EDITOR_OPEN_FAILED, void 0, {
3865
4041
  cause: error
3866
4042
  });
3867
4043
  }
@@ -3869,16 +4045,18 @@ async function handleOpenEditor(request, options) {
3869
4045
  function createSpotPatchMiddleware(options) {
3870
4046
  const bootstrap = options.bootstrap === void 0 ? void 0 : resolveRuntimeBootstrapOptions(options.bootstrap);
3871
4047
  const dataFlowAnalyzer = createDataFlowAnalyzer(options);
3872
- return (request, response, next) => {
4048
+ const externalAgentController = options.externalAgentControl === void 0 ? void 0 : createExternalAgentBrowserController(options.externalAgentControl);
4049
+ const middleware = (request, response, next) => {
3873
4050
  const path9 = requestPath(request);
3874
4051
  const agentRoute = matchAgentRequestPath(path9);
4052
+ const externalAgentRoute = matchExternalAgentBrowserPath(path9);
3875
4053
  const externalHandoffRoute = matchExternalHandoffBrowserPath(path9);
3876
- if (path9 !== SPOTPATCH_ENDPOINTS3.sourceContext && path9 !== SPOTPATCH_ENDPOINTS3.openEditor && path9 !== SPOTPATCH_ENDPOINTS3.dataFlowComponentReport && path9 !== SPOTPATCH_ENDPOINTS3.dataFlowPageReport && agentRoute === void 0 && externalHandoffRoute === void 0 && !path9.startsWith(`${SPOTPATCH_API_BASE}/`)) {
4054
+ if (path9 !== SPOTPATCH_ENDPOINTS4.sourceContext && path9 !== SPOTPATCH_ENDPOINTS4.openEditor && path9 !== SPOTPATCH_ENDPOINTS4.dataFlowComponentReport && path9 !== SPOTPATCH_ENDPOINTS4.dataFlowPageReport && agentRoute === void 0 && externalAgentRoute === void 0 && externalHandoffRoute === void 0 && !path9.startsWith(`${SPOTPATCH_API_BASE}/`)) {
3877
4055
  next();
3878
4056
  return;
3879
4057
  }
3880
4058
  const handle = async () => {
3881
- if (path9 === SPOTPATCH_ENDPOINTS3.bootstrap && bootstrap !== void 0) {
4059
+ if (path9 === SPOTPATCH_ENDPOINTS4.bootstrap && bootstrap !== void 0) {
3882
4060
  const data = await readRuntimeBootstrap(
3883
4061
  request,
3884
4062
  bootstrap
@@ -3890,25 +4068,25 @@ function createSpotPatchMiddleware(options) {
3890
4068
  allowLan: options.options.allowLan,
3891
4069
  sessionToken: options.session.token
3892
4070
  });
3893
- if (path9 === SPOTPATCH_ENDPOINTS3.sourceContext) {
4071
+ if (path9 === SPOTPATCH_ENDPOINTS4.sourceContext) {
3894
4072
  if (request.method !== "POST") {
3895
- throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
4073
+ throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
3896
4074
  }
3897
4075
  const data = await handleSourceContext(request, options);
3898
4076
  writeJson2(response, 200, { ok: true, data });
3899
4077
  return;
3900
4078
  }
3901
- if (path9 === SPOTPATCH_ENDPOINTS3.openEditor) {
4079
+ if (path9 === SPOTPATCH_ENDPOINTS4.openEditor) {
3902
4080
  if (request.method !== "POST") {
3903
- throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
4081
+ throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
3904
4082
  }
3905
4083
  const data = await handleOpenEditor(request, options);
3906
4084
  writeJson2(response, 200, { ok: true, data });
3907
4085
  return;
3908
4086
  }
3909
- if (path9 === SPOTPATCH_ENDPOINTS3.dataFlowComponentReport) {
4087
+ if (path9 === SPOTPATCH_ENDPOINTS4.dataFlowComponentReport) {
3910
4088
  if (request.method !== "POST") {
3911
- throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
4089
+ throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
3912
4090
  }
3913
4091
  const data = await handleComponentDataFlowReport(
3914
4092
  request,
@@ -3918,14 +4096,28 @@ function createSpotPatchMiddleware(options) {
3918
4096
  writeJson2(response, 200, { ok: true, data });
3919
4097
  return;
3920
4098
  }
3921
- if (path9 === SPOTPATCH_ENDPOINTS3.dataFlowPageReport) {
4099
+ if (path9 === SPOTPATCH_ENDPOINTS4.dataFlowPageReport) {
3922
4100
  if (request.method !== "POST") {
3923
- throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
4101
+ throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
3924
4102
  }
3925
4103
  const data = await handlePageDataFlowReport(request, dataFlowAnalyzer, options);
3926
4104
  writeJson2(response, 200, { ok: true, data });
3927
4105
  return;
3928
4106
  }
4107
+ if (externalAgentRoute !== void 0) {
4108
+ if (!options.options.externalAgent.enabled || externalAgentController === void 0) {
4109
+ throw new SpotPatchError16(ERROR_CODES16.EXTERNAL_HANDOFF_DISABLED);
4110
+ }
4111
+ await externalAgentController.handle(
4112
+ request,
4113
+ response,
4114
+ externalAgentRoute,
4115
+ (target, status, data) => {
4116
+ writeJson2(target, status, { ok: true, data });
4117
+ }
4118
+ );
4119
+ return;
4120
+ }
3929
4121
  if (externalHandoffRoute !== void 0) {
3930
4122
  await handleExternalHandoffBrowserRequest(
3931
4123
  request,
@@ -3944,7 +4136,7 @@ function createSpotPatchMiddleware(options) {
3944
4136
  return;
3945
4137
  }
3946
4138
  if (agentRoute === void 0) {
3947
- throw new SpotPatchError15(ERROR_CODES15.INVALID_REQUEST);
4139
+ throw new SpotPatchError16(ERROR_CODES16.INVALID_REQUEST);
3948
4140
  }
3949
4141
  await handleAgentRequest(
3950
4142
  request,
@@ -3960,6 +4152,11 @@ function createSpotPatchMiddleware(options) {
3960
4152
  writeError(response, error, options.logger);
3961
4153
  });
3962
4154
  };
4155
+ return Object.assign(middleware, {
4156
+ dispose() {
4157
+ externalAgentController?.dispose();
4158
+ }
4159
+ });
3963
4160
  }
3964
4161
 
3965
4162
  // src/server/source-registration.ts
@@ -4291,8 +4488,10 @@ export {
4291
4488
  readRuntimeBootstrap,
4292
4489
  resolveCredentialEnvironment,
4293
4490
  resolveEnvironmentAiConfiguration,
4491
+ resolveManagedExecutionValidation,
4294
4492
  resolveOptions,
4295
4493
  resolveProjectOptions,
4494
+ resolveProjectValidationChecks,
4296
4495
  resolveRuntimeBootstrapOptions,
4297
4496
  serializeResolvedSpotPatchOptions
4298
4497
  };