@mindstudio-ai/remy 0.1.332 → 0.1.334

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
@@ -332,6 +332,10 @@ async function* streamChat(params) {
332
332
  requestId,
333
333
  ...subAgentId && { subAgentId },
334
334
  error: event.error,
335
+ // The code decides whether this retries, so omitting it made a
336
+ // debug bundle unable to answer why a turn died — the whole
337
+ // classification input was invisible in 32k log lines (RPT-1234).
338
+ ...event.code && { code: event.code },
335
339
  durationMs: Date.now() - startTime
336
340
  });
337
341
  }
@@ -363,13 +367,17 @@ function isRetryableError(error, code) {
363
367
  if (code && RETRYABLE_ERROR_CODES.has(code)) {
364
368
  return true;
365
369
  }
370
+ if (code && RETRYABLE_ERROR_CODE_PREFIXES.some((p) => code.startsWith(p))) {
371
+ return true;
372
+ }
366
373
  return /Network error/i.test(error) || /HTTP 5\d\d/i.test(error) || /Stream stalled/i.test(error) || /overloaded/i.test(error) || /terminated/i.test(error) || // The API's friendly mapping of a provider 500 — belt-and-suspenders for
367
374
  // pods that don't send a machine-readable code.
368
- /Internal API error/i.test(error) || // Anthropic fetches url-source image blocks server-side on every call;
369
- // a transient failure anywhere in that chain (S3 signing edge, resize
370
- // proxy, their fetcher) surfaces as this message. The images themselves
371
- // are fine — the same URLs typically succeed on the next attempt.
372
- /Unable to download/i.test(error);
375
+ /Internal API error/i.test(error) || // Server-side media-fetch failures, for providers that send no usable
376
+ // code. Matching PROSE is the fallback, not the mechanism: keying on one
377
+ // vendor's wording is what let this class through before — Anthropic's
378
+ // "Unable to download" was handled while Meta's "failed to download
379
+ // media" was fatal, for the identical failure with the identical remedy.
380
+ /Unable to download/i.test(error) || /failed to download media/i.test(error);
373
381
  }
374
382
  function sleep(ms) {
375
383
  return new Promise((resolve4) => setTimeout(resolve4, ms));
@@ -470,6 +478,41 @@ async function generateBackgroundAck(params) {
470
478
  return FALLBACK_ACK;
471
479
  }
472
480
  }
481
+ async function fetchModelSurfaces(config) {
482
+ const url = `${config.baseUrl}/v1/site-settings/remy-model-surfaces`;
483
+ let lastError = "";
484
+ for (let attempt = 1; attempt <= SURFACES_FETCH_ATTEMPTS; attempt++) {
485
+ try {
486
+ const res = await fetch(url, {
487
+ method: "GET",
488
+ signal: AbortSignal.timeout(2e4)
489
+ });
490
+ if (!res.ok) {
491
+ lastError = `HTTP ${res.status}`;
492
+ } else {
493
+ const data = await res.json();
494
+ if (Array.isArray(data?.surfaces) && data.surfaces.length > 0) {
495
+ return data;
496
+ }
497
+ lastError = "response carried no surfaces";
498
+ }
499
+ } catch (err) {
500
+ lastError = err?.message ?? String(err);
501
+ }
502
+ if (attempt < SURFACES_FETCH_ATTEMPTS) {
503
+ const backoffMs = attempt * 2e3;
504
+ log.debug("model-surfaces fetch failed, retrying", {
505
+ attempt,
506
+ backoffMs,
507
+ error: lastError
508
+ });
509
+ await new Promise((resolve4) => setTimeout(resolve4, backoffMs));
510
+ }
511
+ }
512
+ throw new Error(
513
+ `Could not load model surfaces from ${url} after ${SURFACES_FETCH_ATTEMPTS} attempts (${lastError})`
514
+ );
515
+ }
473
516
  async function fetchRemyContext(config) {
474
517
  if (!config.appId) {
475
518
  return null;
@@ -497,7 +540,7 @@ async function fetchRemyContext(config) {
497
540
  return null;
498
541
  }
499
542
  }
500
- var log, MAX_RETRIES, INITIAL_BACKOFF_MS, RETRYABLE_ERROR_CODES, FALLBACK_ACK;
543
+ var log, MAX_RETRIES, INITIAL_BACKOFF_MS, RETRYABLE_ERROR_CODES, RETRYABLE_ERROR_CODE_PREFIXES, FALLBACK_ACK, SURFACES_FETCH_ATTEMPTS;
501
544
  var init_api = __esm({
502
545
  "src/api.ts"() {
503
546
  "use strict";
@@ -512,7 +555,9 @@ var init_api = __esm({
512
555
  "overloaded_error"
513
556
  // Anthropic's 529-equivalent
514
557
  ]);
558
+ RETRYABLE_ERROR_CODE_PREFIXES = ["media_url_"];
515
559
  FALLBACK_ACK = "[Message sent to agent. Agent is working in the background and will report back with its results when finished.]";
560
+ SURFACES_FETCH_ATTEMPTS = 3;
516
561
  }
517
562
  });
518
563
 
@@ -2158,8 +2203,73 @@ var init_compaction = __esm({
2158
2203
  });
2159
2204
 
2160
2205
  // src/models/surfaces.ts
2206
+ function setModelRegistry(payload) {
2207
+ const bySurface = {};
2208
+ for (const surface of payload.surfaces) {
2209
+ if (SURFACE_IDS.includes(surface.id)) {
2210
+ bySurface[surface.id] = {
2211
+ default: surface.default,
2212
+ label: surface.label,
2213
+ description: surface.description,
2214
+ modelType: surface.modelType,
2215
+ // The platform only publishes user-pickable surfaces; internal ones
2216
+ // (imagePromptEnhancer) are Remy's own and never appear in a picker.
2217
+ userPickable: true
2218
+ };
2219
+ }
2220
+ }
2221
+ const missing = SURFACE_IDS.filter(
2222
+ (id) => !bySurface[id] && id !== "imagePromptEnhancer"
2223
+ );
2224
+ if (missing.length > 0) {
2225
+ throw new Error(
2226
+ `The platform published no model surface for: ${missing.join(", ")}. This Remy build expects them \u2014 the platform is likely older than this release.`
2227
+ );
2228
+ }
2229
+ bySurface.imagePromptEnhancer = {
2230
+ default: bySurface.conversationSummarizer.default,
2231
+ label: "Image Prompt Enhancer",
2232
+ description: "Rewrites image briefs into model-optimized prompts before image generation.",
2233
+ modelType: "text",
2234
+ userPickable: false
2235
+ };
2236
+ surfaces = bySurface;
2237
+ allowedModelsByType = payload.allowedModelsByType ?? {};
2238
+ textModels = parseTextModels(payload.textModels);
2239
+ registryLoaded = true;
2240
+ }
2241
+ function parseTextModels(raw) {
2242
+ const out = /* @__PURE__ */ new Map();
2243
+ if (!raw || typeof raw !== "object") {
2244
+ return out;
2245
+ }
2246
+ for (const [id, value] of Object.entries(raw)) {
2247
+ const force = value?.forceCompactAt;
2248
+ if (!id || typeof force !== "number" || !Number.isFinite(force) || force <= 0) {
2249
+ continue;
2250
+ }
2251
+ const suggest = value?.suggestCompactAt;
2252
+ out.set(id, {
2253
+ forceCompactAt: force,
2254
+ ...typeof suggest === "number" && Number.isFinite(suggest) && suggest > 0 ? { suggestCompactAt: suggest } : {}
2255
+ });
2256
+ }
2257
+ return out;
2258
+ }
2259
+ function requireSurface(surfaceId) {
2260
+ const surface = surfaces[surfaceId];
2261
+ if (!surface) {
2262
+ throw new Error(
2263
+ registryLoaded ? `Unknown model surface '${surfaceId}'.` : `Model surfaces were read before the platform registry loaded (surface '${surfaceId}'). setModelRegistry must run during boot.`
2264
+ );
2265
+ }
2266
+ return surface;
2267
+ }
2268
+ function getAllowedModelsByType() {
2269
+ return allowedModelsByType;
2270
+ }
2161
2271
  function getContextLimits(modelId) {
2162
- return TEXT_MODELS[modelId] ?? DEFAULT_CONTEXT_LIMITS;
2272
+ return textModels.get(modelId) ?? FALLBACK_CONTEXT_LIMITS;
2163
2273
  }
2164
2274
  function getSuggestCompactAt(modelId) {
2165
2275
  return getContextLimits(modelId).suggestCompactAt ?? DEFAULT_SUGGEST_COMPACT_AT;
@@ -2173,17 +2283,17 @@ function filterModelPicks(picks) {
2173
2283
  return out;
2174
2284
  }
2175
2285
  for (const [key, value] of Object.entries(picks)) {
2176
- if (!(key in MODEL_SURFACES)) {
2286
+ const surface = surfaces[key];
2287
+ if (!surface) {
2177
2288
  continue;
2178
2289
  }
2179
- const surface = MODEL_SURFACES[key];
2180
2290
  if (!surface.userPickable) {
2181
2291
  continue;
2182
2292
  }
2183
2293
  if (typeof value !== "string" || value.length === 0) {
2184
2294
  continue;
2185
2295
  }
2186
- const allow = ALLOWED_MODELS_BY_TYPE[surface.modelType];
2296
+ const allow = allowedModelsByType[surface.modelType];
2187
2297
  if (allow && !allow.includes(value)) {
2188
2298
  continue;
2189
2299
  }
@@ -2193,183 +2303,50 @@ function filterModelPicks(picks) {
2193
2303
  }
2194
2304
  function getEffectiveModelSurfaces() {
2195
2305
  const out = {};
2196
- for (const [id, surface] of Object.entries(MODEL_SURFACES)) {
2306
+ for (const id of SURFACE_IDS) {
2307
+ const surface = surfaces[id];
2308
+ if (!surface) {
2309
+ continue;
2310
+ }
2197
2311
  const orgDefault = orgDefaultModels[id];
2198
2312
  out[id] = orgDefault ? { ...surface, default: orgDefault } : { ...surface };
2199
2313
  }
2200
2314
  return out;
2201
2315
  }
2202
2316
  function resolveModel(surfaceId, models, fallback) {
2203
- return models?.[surfaceId] ?? fallback ?? orgDefaultModels[surfaceId] ?? MODEL_SURFACES[surfaceId].default;
2317
+ return models?.[surfaceId] ?? fallback ?? orgDefaultModels[surfaceId] ?? requireSurface(surfaceId).default;
2204
2318
  }
2205
2319
  function resolveParentModel(models, fallback, buildModel) {
2206
2320
  const override = buildModel ? filterModelPicks({ parent: buildModel }).parent : void 0;
2207
2321
  const baseline = resolveModel("parent", models, fallback);
2208
2322
  return { baseline, effective: override ?? baseline };
2209
2323
  }
2210
- var MODEL_SURFACES, DEFAULT_SUGGEST_COMPACT_AT, TEXT_MODELS, DEFAULT_CONTEXT_LIMITS, ALLOWED_MODELS_BY_TYPE, orgDefaultModels;
2324
+ var SURFACE_IDS, DEFAULT_SUGGEST_COMPACT_AT, FALLBACK_CONTEXT_LIMITS, surfaces, allowedModelsByType, textModels, registryLoaded, orgDefaultModels;
2211
2325
  var init_surfaces = __esm({
2212
2326
  "src/models/surfaces.ts"() {
2213
2327
  "use strict";
2214
- MODEL_SURFACES = {
2215
- parent: {
2216
- default: "claude-4-8-opus",
2217
- label: "Remy",
2218
- description: "The main Remy agent you chat with about your product. Writes code and manages delegation to other agents.",
2219
- modelType: "text",
2220
- userPickable: true
2221
- },
2222
- visualDesignExpert: {
2223
- default: "claude-4-8-opus",
2224
- label: "Design Agent",
2225
- description: "Designs your product's interfaces, including components, layouts, typography, color, and visual identity.",
2226
- modelType: "text",
2227
- userPickable: true
2228
- },
2229
- productVision: {
2230
- default: "claude-5-sonnet",
2231
- label: "Roadmap Agent",
2232
- description: "Owns your product's roadmap and pitch deck. Helps decide what to build next and how to frame the big picture.",
2233
- modelType: "text",
2234
- userPickable: true
2235
- },
2236
- browserAutomation: {
2237
- default: "claude-5-sonnet",
2238
- label: "QA Agent",
2239
- description: "Tests features and UI flows in an automated browser to verify they work end to end.",
2240
- modelType: "text",
2241
- userPickable: true
2242
- },
2243
- codeSanityCheck: {
2244
- default: "claude-5-sonnet",
2245
- label: "Architecture Agent",
2246
- description: "Reviews the architecture and structure of code changes to avoid technical debt.",
2247
- modelType: "text",
2248
- userPickable: true
2249
- },
2250
- research: {
2251
- default: "claude-5-sonnet",
2252
- label: "Research Agent",
2253
- description: "Researches using the web and reports back with citations.",
2254
- modelType: "text",
2255
- userPickable: true
2256
- },
2257
- reviewExistingProject: {
2258
- default: "claude-5-sonnet",
2259
- label: "Existing Project Review",
2260
- description: "Reviews a project you bring from another tool and reports what is worth carrying forward.",
2261
- modelType: "text",
2262
- userPickable: true
2263
- },
2264
- copyEditor: {
2265
- default: "claude-5-sonnet",
2266
- label: "Copy Agent",
2267
- description: "Tightens prose and copy across your app and its launch materials so it reads sharp and human, never machine-made.",
2268
- modelType: "text",
2269
- userPickable: true
2270
- },
2271
- specSync: {
2272
- default: "claude-5-sonnet",
2273
- label: "Spec Sync Agent",
2274
- description: "Keeps your spec in sync with the code as you build, updating the affected sections in the background after changes.",
2275
- modelType: "text",
2276
- userPickable: true
2277
- },
2278
- imageGeneration: {
2279
- default: "gpt-image-2",
2280
- label: "Image Generation",
2281
- description: "Creates images for your product \u2014 icons, illustrations, photos, and any other visual assets.",
2282
- modelType: "image_generation",
2283
- userPickable: true
2284
- },
2285
- imageAnalysis: {
2286
- default: "claude-5-sonnet",
2287
- label: "Image Analysis",
2288
- description: "Reads screenshots taken by the QA agent during automated browser tests. Other agents use their own built-in image analysis when they need to read images.",
2289
- modelType: "vision",
2290
- userPickable: true
2291
- },
2292
- conversationSummarizer: {
2293
- default: "claude-5-sonnet",
2294
- label: "Compaction Utility",
2295
- description: "Compresses long conversations into summaries to keep things responsive.",
2296
- modelType: "text",
2297
- userPickable: true
2298
- },
2299
- brandExtractor: {
2300
- default: "claude-5-sonnet",
2301
- label: "Brand Utility",
2302
- description: "Extracts your product's name, colors, and fonts from your spec for use in branded documents.",
2303
- modelType: "text",
2304
- userPickable: true
2305
- },
2306
- // Internal surface — not user-pickable. Remy uses this to rewrite design
2307
- // briefs into model-optimized image prompts before image generation.
2308
- imagePromptEnhancer: {
2309
- default: "claude-5-sonnet",
2310
- label: "Image Prompt Enhancer",
2311
- description: "Rewrites image briefs into model-optimized prompts before image generation.",
2312
- modelType: "text",
2313
- userPickable: false
2314
- }
2315
- };
2328
+ SURFACE_IDS = [
2329
+ "parent",
2330
+ "visualDesignExpert",
2331
+ "productVision",
2332
+ "browserAutomation",
2333
+ "codeSanityCheck",
2334
+ "research",
2335
+ "reviewExistingProject",
2336
+ "copyEditor",
2337
+ "specSync",
2338
+ "imageGeneration",
2339
+ "imageAnalysis",
2340
+ "conversationSummarizer",
2341
+ "brandExtractor",
2342
+ "imagePromptEnhancer"
2343
+ ];
2316
2344
  DEFAULT_SUGGEST_COMPACT_AT = 3e5;
2317
- TEXT_MODELS = {
2318
- // Anthropic 1M-context, flat-priced.
2319
- "claude-5-opus": { forceCompactAt: 85e4 },
2320
- "claude-4-8-opus": { forceCompactAt: 85e4 },
2321
- "claude-4-7-opus": { forceCompactAt: 85e4 },
2322
- // Anthropic 1M-context with 2x long-context pricing above 200K input.
2323
- "claude-4-6-opus": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
2324
- "claude-4-6-sonnet": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
2325
- "claude-fable-5": { forceCompactAt: 85e4 },
2326
- "claude-fable-5-1": { forceCompactAt: 85e4 },
2327
- "claude-5-sonnet": { forceCompactAt: 85e4 },
2328
- // OpenAI gpt-5.5/5.6: ~1M window, but the usable input ceiling under
2329
- // `truncation: 'auto'` is ~794K (output + reasoning reserve), and all
2330
- // rates double above 272K input.
2331
- "gpt-5.5": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2332
- "gpt-5.6-sol": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2333
- "gpt-5.6-terra": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2334
- "gpt-5.6-luna": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2335
- "gpt-6-astra": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2336
- // Google ~1M-context; only 3.1-pro is tiered (higher rates above 200K).
2337
- "gemini-3-pro": { forceCompactAt: 85e4 },
2338
- "gemini-3.1-pro": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
2339
- "gemini-3-flash": { forceCompactAt: 85e4 },
2340
- "gemini-3.5-flash": { forceCompactAt: 85e4 },
2341
- "gemini-3.7-flash": { forceCompactAt: 85e4 },
2342
- // 256K window; its 200K pricing tier sits above the gate, so no nudge.
2343
- "grok-build-0.1": { forceCompactAt: 18e4 },
2344
- "grok-4.5": { forceCompactAt: 4e5 },
2345
- // 500K window
2346
- "grok-4.6": { forceCompactAt: 4e5 },
2347
- // 500K window
2348
- "glm-5.2": { forceCompactAt: 85e4 },
2349
- "glm-5.3": { forceCompactAt: 85e4 },
2350
- "glm-5.3-flash": { forceCompactAt: 85e4 },
2351
- "muse-spark-1.1": { forceCompactAt: 85e4 },
2352
- "muse-spark-1.2": { forceCompactAt: 85e4 },
2353
- "muse-spark-1.3": { forceCompactAt: 85e4 },
2354
- "kimi-k2-7-code": { forceCompactAt: 2e5 },
2355
- // 262K window
2356
- "kimi-k3": { forceCompactAt: 85e4 },
2357
- "deepseek-v4-flash-0731": { forceCompactAt: 85e4 },
2358
- "deepseek-v4-pro": { forceCompactAt: 85e4 },
2359
- "deepseek-v4.1-flash": { forceCompactAt: 85e4 },
2360
- "qwen3.8-2.4t-a95b-deepinfra": { forceCompactAt: 2e5 },
2361
- // 262K window
2362
- "qwen3.8-27b-deepinfra": { forceCompactAt: 2e5 },
2363
- // 262K window
2364
- "minimax-m3": { forceCompactAt: 42e4 }
2365
- // 524K window
2366
- };
2367
- DEFAULT_CONTEXT_LIMITS = { forceCompactAt: 85e4 };
2368
- ALLOWED_MODELS_BY_TYPE = {
2369
- text: Object.keys(TEXT_MODELS)
2370
- // vision: undefined — unconstrained
2371
- // image_generation: undefined — unconstrained
2372
- };
2345
+ FALLBACK_CONTEXT_LIMITS = { forceCompactAt: 85e4 };
2346
+ surfaces = {};
2347
+ allowedModelsByType = {};
2348
+ textModels = /* @__PURE__ */ new Map();
2349
+ registryLoaded = false;
2373
2350
  orgDefaultModels = {};
2374
2351
  }
2375
2352
  });
@@ -11155,6 +11132,27 @@ var init_config = __esm({
11155
11132
  }
11156
11133
  });
11157
11134
 
11135
+ // src/models/init.ts
11136
+ async function initModelRegistry(config) {
11137
+ const payload = await fetchModelSurfaces(config);
11138
+ setModelRegistry(payload);
11139
+ log16.debug("model registry loaded", {
11140
+ surfaces: payload.surfaces.length,
11141
+ allowedTextModels: payload.allowedModelsByType?.text?.length ?? 0,
11142
+ textModels: Object.keys(payload.textModels ?? {}).length
11143
+ });
11144
+ }
11145
+ var log16;
11146
+ var init_init = __esm({
11147
+ "src/models/init.ts"() {
11148
+ "use strict";
11149
+ init_api();
11150
+ init_logger();
11151
+ init_surfaces();
11152
+ log16 = createLogger("models");
11153
+ }
11154
+ });
11155
+
11158
11156
  // src/headless/attachments.ts
11159
11157
  import { mkdirSync, existsSync, createWriteStream } from "fs";
11160
11158
  import { writeFile as writeFile3, stat as stat5 } from "fs/promises";
@@ -11218,7 +11216,7 @@ async function persistAttachmentList(attachments) {
11218
11216
  createWriteStream(localPath)
11219
11217
  );
11220
11218
  const { size } = await stat5(localPath);
11221
- log16.info("Attachment saved", {
11219
+ log17.info("Attachment saved", {
11222
11220
  filename: name,
11223
11221
  path: localPath,
11224
11222
  bytes: size
@@ -11232,7 +11230,7 @@ async function persistAttachmentList(attachments) {
11232
11230
  if (textRes.ok) {
11233
11231
  extractedTextPath = `${localPath}.txt`;
11234
11232
  await writeFile3(extractedTextPath, await textRes.text(), "utf-8");
11235
- log16.info("Extracted text saved", { path: extractedTextPath });
11233
+ log17.info("Extracted text saved", { path: extractedTextPath });
11236
11234
  }
11237
11235
  } catch {
11238
11236
  }
@@ -11281,12 +11279,12 @@ function buildUploadHeader(documents, images) {
11281
11279
  return `[Uploaded files]
11282
11280
  ${lines.join("\n")}`;
11283
11281
  }
11284
- var log16, UPLOADS_DIR, IMAGE_DOWNLOAD_TIMEOUT_MS, DOCUMENT_DOWNLOAD_TIMEOUT_MS, IMAGE_EXTENSIONS;
11282
+ var log17, UPLOADS_DIR, IMAGE_DOWNLOAD_TIMEOUT_MS, DOCUMENT_DOWNLOAD_TIMEOUT_MS, IMAGE_EXTENSIONS;
11285
11283
  var init_attachments = __esm({
11286
11284
  "src/headless/attachments.ts"() {
11287
11285
  "use strict";
11288
11286
  init_logger();
11289
- log16 = createLogger("headless:attachments");
11287
+ log17 = createLogger("headless:attachments");
11290
11288
  UPLOADS_DIR = "src/.user-uploads";
11291
11289
  IMAGE_DOWNLOAD_TIMEOUT_MS = 3e4;
11292
11290
  DOCUMENT_DOWNLOAD_TIMEOUT_MS = 3e5;
@@ -11442,7 +11440,7 @@ async function readUpstreamStatus() {
11442
11440
  FETCH_TIMEOUT_MS
11443
11441
  );
11444
11442
  if (!fetched.ok) {
11445
- log17.info(
11443
+ log18.info(
11446
11444
  `fetch failed, falling back to the origin/${DEFAULT_BRANCH} on disk: ${fetched.error}`
11447
11445
  );
11448
11446
  }
@@ -11495,13 +11493,13 @@ async function readUpstreamStatus() {
11495
11493
  if (contained.ok) {
11496
11494
  return null;
11497
11495
  }
11498
- log17.info(
11496
+ log18.info(
11499
11497
  `behind by an unknown amount: rev-list and log both failed (${counts.error})`
11500
11498
  );
11501
11499
  }
11502
11500
  const status = await git(["status", "--porcelain"]);
11503
11501
  if (!status.ok) {
11504
- log17.info(`could not read the working tree state: ${status.error}`);
11502
+ log18.info(`could not read the working tree state: ${status.error}`);
11505
11503
  }
11506
11504
  return {
11507
11505
  upstream,
@@ -11512,13 +11510,13 @@ async function readUpstreamStatus() {
11512
11510
  incomingTruncated: incomingLines.length > MAX_INCOMING
11513
11511
  };
11514
11512
  }
11515
- var log17, DEFAULT_BRANCH, MAX_INCOMING, FETCH_TIMEOUT_MS, GIT_TIMEOUT_MS, MAX_BUFFER_BYTES;
11513
+ var log18, DEFAULT_BRANCH, MAX_INCOMING, FETCH_TIMEOUT_MS, GIT_TIMEOUT_MS, MAX_BUFFER_BYTES;
11516
11514
  var init_upstreamStatus = __esm({
11517
11515
  "src/git/upstreamStatus.ts"() {
11518
11516
  "use strict";
11519
11517
  init_projectRoot();
11520
11518
  init_logger();
11521
- log17 = createLogger("upstream");
11519
+ log18 = createLogger("upstream");
11522
11520
  DEFAULT_BRANCH = "main";
11523
11521
  MAX_INCOMING = 20;
11524
11522
  FETCH_TIMEOUT_MS = 3e4;
@@ -11743,13 +11741,14 @@ var headless_exports = {};
11743
11741
  __export(headless_exports, {
11744
11742
  HeadlessSession: () => HeadlessSession
11745
11743
  });
11746
- var log18, EXTERNAL_TOOL_TIMEOUT_MS, LONG_RUNNING_TOOLS, LONG_RUNNING_TOOL_TIMEOUT_MS, USER_FACING_TOOLS, HeadlessSession;
11744
+ var log19, EXTERNAL_TOOL_TIMEOUT_MS, LONG_RUNNING_TOOLS, LONG_RUNNING_TOOL_TIMEOUT_MS, USER_FACING_TOOLS, HeadlessSession;
11747
11745
  var init_headless = __esm({
11748
11746
  "src/headless/index.ts"() {
11749
11747
  "use strict";
11750
11748
  init_logger();
11751
11749
  init_config();
11752
11750
  init_orgContext();
11751
+ init_init();
11753
11752
  init_prompt4();
11754
11753
  init_trigger();
11755
11754
  init_trigger2();
@@ -11766,7 +11765,7 @@ var init_headless = __esm({
11766
11765
  init_messageQueue();
11767
11766
  init_resolve();
11768
11767
  init_sentinel();
11769
- log18 = createLogger("headless");
11768
+ log19 = createLogger("headless");
11770
11769
  EXTERNAL_TOOL_TIMEOUT_MS = 3e5;
11771
11770
  LONG_RUNNING_TOOLS = /* @__PURE__ */ new Set(["runMethod", "testJewel"]);
11772
11771
  LONG_RUNNING_TOOL_TIMEOUT_MS = 18e5;
@@ -11875,6 +11874,7 @@ var init_headless = __esm({
11875
11874
  apiKey: this.opts.apiKey,
11876
11875
  baseUrl: this.opts.baseUrl
11877
11876
  });
11877
+ await initModelRegistry(this.config);
11878
11878
  await initOrgContext(this.config);
11879
11879
  const resumed = loadSession(this.state);
11880
11880
  this.queue = new MessageQueue(
@@ -11893,7 +11893,7 @@ var init_headless = __esm({
11893
11893
  messageCount: this.state.messages.length,
11894
11894
  ...this.state.models && { models: this.state.models },
11895
11895
  modelSurfaces: getEffectiveModelSurfaces(),
11896
- allowedModelsByType: ALLOWED_MODELS_BY_TYPE
11896
+ allowedModelsByType: getAllowedModelsByType()
11897
11897
  });
11898
11898
  }
11899
11899
  triggerBrandExtraction(
@@ -12010,7 +12010,7 @@ var init_headless = __esm({
12010
12010
  try {
12011
12011
  this.handleCancel("shutdown");
12012
12012
  } catch (err) {
12013
- log18.warn("Shutdown cancel failed", { error: err?.message });
12013
+ log19.warn("Shutdown cancel failed", { error: err?.message });
12014
12014
  }
12015
12015
  this.emit("stopping");
12016
12016
  this.emit("stopped");
@@ -12026,7 +12026,7 @@ var init_headless = __esm({
12026
12026
  }
12027
12027
  const line = JSON.stringify(payload) + "\n";
12028
12028
  if (event === "history") {
12029
- log18.info("Wrote history event to stdout", {
12029
+ log19.info("Wrote history event to stdout", {
12030
12030
  requestId,
12031
12031
  bytes: line.length
12032
12032
  });
@@ -12100,7 +12100,7 @@ var init_headless = __esm({
12100
12100
  lastNotedUpstream: status.upstream
12101
12101
  };
12102
12102
  this.persistStats();
12103
- log18.info("workspace behind upstream; note parked for the next turn", {
12103
+ log19.info("workspace behind upstream; note parked for the next turn", {
12104
12104
  // The upstream sha is the dedupe key, so it is what makes "why did I
12105
12105
  // not get a note" answerable from the log alone.
12106
12106
  upstream: status.upstream,
@@ -12109,7 +12109,7 @@ var init_headless = __esm({
12109
12109
  dirty: status.dirty
12110
12110
  });
12111
12111
  } catch (err) {
12112
- log18.info(`upstream check failed: ${String(err)}`);
12112
+ log19.info(`upstream check failed: ${String(err)}`);
12113
12113
  }
12114
12114
  }
12115
12115
  //////////////////////////////////////////////////////////////////////////////
@@ -12158,7 +12158,7 @@ var init_headless = __esm({
12158
12158
  if (this.sessionStats.lastContextSize <= threshold) {
12159
12159
  return;
12160
12160
  }
12161
- log18.info("Forced compaction gate triggered", {
12161
+ log19.info("Forced compaction gate triggered", {
12162
12162
  contextSize: this.sessionStats.lastContextSize,
12163
12163
  threshold,
12164
12164
  model: parentModel,
@@ -12178,7 +12178,7 @@ var init_headless = __esm({
12178
12178
  onBackgroundComplete = (toolCallId, name, result, subAgentMessages) => {
12179
12179
  const notify = getToolByName(name)?.backgroundNotify ?? "wake";
12180
12180
  this.pendingBlockUpdates.push({ toolCallId, result, subAgentMessages });
12181
- log18.info("Background complete", {
12181
+ log19.info("Background complete", {
12182
12182
  toolCallId,
12183
12183
  name,
12184
12184
  notify,
@@ -12460,7 +12460,7 @@ var init_headless = __esm({
12460
12460
  const { documents, images } = await persistAttachments(attachments);
12461
12461
  return buildUploadHeader(documents, images) || void 0;
12462
12462
  } catch (err) {
12463
- log18.warn("Attachment persistence failed", { error: err.message });
12463
+ log19.warn("Attachment persistence failed", { error: err.message });
12464
12464
  return void 0;
12465
12465
  }
12466
12466
  }
@@ -12520,7 +12520,7 @@ var init_headless = __esm({
12520
12520
  }
12521
12521
  if (batch.length === 0) {
12522
12522
  if (landings > 0) {
12523
- log18.info("promptUser store landings passed through", { landings });
12523
+ log19.info("promptUser store landings passed through", { landings });
12524
12524
  }
12525
12525
  return raw;
12526
12526
  }
@@ -12528,7 +12528,7 @@ var init_headless = __esm({
12528
12528
  try {
12529
12529
  results = await persistAttachmentList(batch);
12530
12530
  } catch (err) {
12531
- log18.warn("promptUser upload persistence failed", {
12531
+ log19.warn("promptUser upload persistence failed", {
12532
12532
  error: err.message
12533
12533
  });
12534
12534
  results = batch.map(() => null);
@@ -12540,7 +12540,7 @@ var init_headless = __esm({
12540
12540
  return r.localPath;
12541
12541
  }
12542
12542
  const att = batch[cursor + i];
12543
- log18.warn("promptUser upload not persisted; falling back to url", {
12543
+ log19.warn("promptUser upload not persisted; falling back to url", {
12544
12544
  filename: att.filename
12545
12545
  });
12546
12546
  return att.url;
@@ -12548,7 +12548,7 @@ var init_headless = __esm({
12548
12548
  cursor += slot.count;
12549
12549
  answers[slot.id] = slot.isArray ? paths : paths[0];
12550
12550
  }
12551
- log18.info("promptUser uploads persisted", { count: batch.length });
12551
+ log19.info("promptUser uploads persisted", { count: batch.length });
12552
12552
  return JSON.stringify(answers);
12553
12553
  }
12554
12554
  /**
@@ -12571,7 +12571,7 @@ var init_headless = __esm({
12571
12571
  async runSingleTurn(parsed, requestId, fromChain = false, queued = false) {
12572
12572
  const attachments = parsed.attachments;
12573
12573
  if (attachments?.length) {
12574
- log18.info("Message has attachments", {
12574
+ log19.info("Message has attachments", {
12575
12575
  count: attachments.length,
12576
12576
  urls: attachments.map((a) => a.url)
12577
12577
  });
@@ -12783,7 +12783,7 @@ var init_headless = __esm({
12783
12783
  error: "Turn ended unexpectedly"
12784
12784
  });
12785
12785
  }
12786
- log18.info("Turn complete", {
12786
+ log19.info("Turn complete", {
12787
12787
  requestId,
12788
12788
  durationMs: Date.now() - this.turnStart
12789
12789
  });
@@ -12795,7 +12795,7 @@ var init_headless = __esm({
12795
12795
  error: err.message
12796
12796
  });
12797
12797
  }
12798
- log18.warn("Command failed", {
12798
+ log19.warn("Command failed", {
12799
12799
  action: "message",
12800
12800
  requestId,
12801
12801
  error: err.message
@@ -12964,7 +12964,7 @@ var init_headless = __esm({
12964
12964
  return {
12965
12965
  ...this.state.models && { models: this.state.models },
12966
12966
  modelSurfaces: getEffectiveModelSurfaces(),
12967
- allowedModelsByType: ALLOWED_MODELS_BY_TYPE
12967
+ allowedModelsByType: getAllowedModelsByType()
12968
12968
  };
12969
12969
  }
12970
12970
  /** Change per-agent model picks without clearing history. Takes effect on
@@ -12977,7 +12977,7 @@ var init_headless = __esm({
12977
12977
  return {
12978
12978
  ...this.state.models && { models: this.state.models },
12979
12979
  modelSurfaces: getEffectiveModelSurfaces(),
12980
- allowedModelsByType: ALLOWED_MODELS_BY_TYPE
12980
+ allowedModelsByType: getAllowedModelsByType()
12981
12981
  };
12982
12982
  }
12983
12983
  /**
@@ -13102,7 +13102,7 @@ var init_headless = __esm({
13102
13102
  try {
13103
13103
  parsed = JSON.parse(line);
13104
13104
  } catch (err) {
13105
- log18.warn("Invalid JSON on stdin", {
13105
+ log19.warn("Invalid JSON on stdin", {
13106
13106
  error: err.message,
13107
13107
  lineLength: line.length,
13108
13108
  preview: line.slice(0, 200)
@@ -13111,7 +13111,7 @@ var init_headless = __esm({
13111
13111
  return;
13112
13112
  }
13113
13113
  const { action, requestId } = parsed;
13114
- log18.info("Command received", { action, requestId });
13114
+ log19.info("Command received", { action, requestId });
13115
13115
  if (action === "tool_result" && parsed.id) {
13116
13116
  const id = parsed.id;
13117
13117
  const result = parsed.result ?? "";
@@ -13120,7 +13120,7 @@ var init_headless = __esm({
13120
13120
  this.pendingTools.delete(id);
13121
13121
  pending2.resolve(result);
13122
13122
  } else if (!this.running) {
13123
- log18.info("Late tool_result while idle, dismissing", { id });
13123
+ log19.info("Late tool_result while idle, dismissing", { id });
13124
13124
  this.emit("completed", { success: true }, requestId);
13125
13125
  } else {
13126
13126
  this.earlyResults.set(id, result);
@@ -13133,7 +13133,7 @@ var init_headless = __esm({
13133
13133
  ...typeof parsed.before === "number" ? { before: parsed.before } : {},
13134
13134
  ...typeof parsed.limit === "number" ? { limit: parsed.limit } : {}
13135
13135
  });
13136
- log18.info("History response", {
13136
+ log19.info("History response", {
13137
13137
  requestId,
13138
13138
  startIndex: page.startIndex,
13139
13139
  endIndex: page.endIndex,
@@ -13151,7 +13151,7 @@ var init_headless = __esm({
13151
13151
  ...this.running && this.currentRequestId ? { currentRequestId: this.currentRequestId } : {},
13152
13152
  ...this.state.models && { models: this.state.models },
13153
13153
  modelSurfaces: getEffectiveModelSurfaces(),
13154
- allowedModelsByType: ALLOWED_MODELS_BY_TYPE,
13154
+ allowedModelsByType: getAllowedModelsByType(),
13155
13155
  // Current queue snapshot for connect/reconnect — get_history is the
13156
13156
  // on-demand "current state" query. Always an array (possibly empty),
13157
13157
  // matching the queue_changed convention so the client reconciles the
@@ -13653,6 +13653,7 @@ Error: ${err.message}`,
13653
13653
  // src/index.tsx
13654
13654
  init_config();
13655
13655
  init_orgContext();
13656
+ init_init();
13656
13657
  init_logger();
13657
13658
  import { jsx as jsx6 } from "react/jsx-runtime";
13658
13659
  var args = process.argv.slice(2);
@@ -13716,6 +13717,7 @@ if (headless) {
13716
13717
  baseUrl: flags.baseUrl
13717
13718
  });
13718
13719
  printDebugInfo(config);
13720
+ await initModelRegistry(config);
13719
13721
  await initOrgContext(config);
13720
13722
  const { waitUntilExit } = render(
13721
13723
  /* @__PURE__ */ jsx6(