@mindstudio-ai/remy 0.1.224 → 0.1.226

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.
Files changed (3) hide show
  1. package/dist/headless.js +222 -129
  2. package/dist/index.js +236 -136
  3. package/package.json +1 -1
package/dist/headless.js CHANGED
@@ -377,19 +377,169 @@ async function fetchRemyContext(config) {
377
377
  }
378
378
  }
379
379
 
380
+ // src/models/surfaces.ts
381
+ var MODEL_SURFACES = {
382
+ parent: {
383
+ default: "claude-5-opus",
384
+ label: "Remy",
385
+ description: "The main Remy agent you chat with about your product. Writes code and manages delegation to other agents.",
386
+ modelType: "text",
387
+ userPickable: true
388
+ },
389
+ visualDesignExpert: {
390
+ default: "claude-5-opus",
391
+ label: "Design Agent",
392
+ description: "Designs your product's interfaces, including components, layouts, typography, color, and visual identity.",
393
+ modelType: "text",
394
+ userPickable: true
395
+ },
396
+ productVision: {
397
+ default: "claude-5-sonnet",
398
+ label: "Roadmap Agent",
399
+ description: "Owns your product's roadmap and pitch deck. Helps decide what to build next and how to frame the big picture.",
400
+ modelType: "text",
401
+ userPickable: true
402
+ },
403
+ browserAutomation: {
404
+ default: "claude-5-sonnet",
405
+ label: "QA Agent",
406
+ description: "Tests features and UI flows in an automated browser to verify they work end to end.",
407
+ modelType: "text",
408
+ userPickable: true
409
+ },
410
+ codeSanityCheck: {
411
+ default: "claude-5-sonnet",
412
+ label: "Architecture Agent",
413
+ description: "Reviews the architecture and structure of code changes to avoid technical debt.",
414
+ modelType: "text",
415
+ userPickable: true
416
+ },
417
+ copyEditor: {
418
+ default: "claude-5-sonnet",
419
+ label: "Copy Agent",
420
+ description: "Tightens prose and copy across your app and its launch materials so it reads sharp and human, never machine-made.",
421
+ modelType: "text",
422
+ userPickable: true
423
+ },
424
+ imageGeneration: {
425
+ default: "seedream-4.5",
426
+ label: "Image Generation",
427
+ description: "Creates images for your product \u2014 icons, illustrations, photos, and any other visual assets.",
428
+ modelType: "image_generation",
429
+ userPickable: true
430
+ },
431
+ imageAnalysis: {
432
+ default: "claude-5-sonnet",
433
+ label: "Image Analysis",
434
+ 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.",
435
+ modelType: "vision",
436
+ userPickable: true
437
+ },
438
+ conversationSummarizer: {
439
+ default: "claude-5-sonnet",
440
+ label: "Compaction Utility",
441
+ description: "Compresses long conversations into summaries to keep things responsive.",
442
+ modelType: "text",
443
+ userPickable: true
444
+ },
445
+ brandExtractor: {
446
+ default: "claude-5-sonnet",
447
+ label: "Brand Utility",
448
+ description: "Extracts your product's name, colors, and fonts from your spec for use in branded documents.",
449
+ modelType: "text",
450
+ userPickable: true
451
+ },
452
+ // Internal surface — not user-pickable. Remy uses this to rewrite design
453
+ // briefs into model-optimized image prompts before image generation.
454
+ imagePromptEnhancer: {
455
+ default: "claude-5-sonnet",
456
+ label: "Image Prompt Enhancer",
457
+ description: "Rewrites image briefs into model-optimized prompts before image generation.",
458
+ modelType: "text",
459
+ userPickable: false
460
+ }
461
+ };
462
+ var ALLOWED_MODELS_BY_TYPE = {
463
+ text: [
464
+ "claude-5-opus",
465
+ "claude-4-8-opus",
466
+ "claude-4-7-opus",
467
+ "claude-4-6-opus",
468
+ "claude-4-6-sonnet",
469
+ "claude-fable-5",
470
+ "claude-5-sonnet",
471
+ "gpt-5.5",
472
+ "gemini-3-pro",
473
+ "gemini-3.1-pro",
474
+ "gemini-3-flash",
475
+ "gemini-3.5-flash",
476
+ "grok-build-0.1",
477
+ "grok-4.5",
478
+ "glm-5.2",
479
+ "muse-spark-1.1",
480
+ "kimi-k2-7-code",
481
+ "kimi-k3"
482
+ ]
483
+ // vision: undefined — unconstrained
484
+ // image_generation: undefined — unconstrained
485
+ };
486
+ var orgDefaultModels = {};
487
+ function setOrgDefaultModels(models) {
488
+ orgDefaultModels = models;
489
+ }
490
+ function filterModelPicks(picks) {
491
+ const out = {};
492
+ if (!picks || typeof picks !== "object") {
493
+ return out;
494
+ }
495
+ for (const [key, value] of Object.entries(picks)) {
496
+ if (!(key in MODEL_SURFACES)) {
497
+ continue;
498
+ }
499
+ const surface = MODEL_SURFACES[key];
500
+ if (!surface.userPickable) {
501
+ continue;
502
+ }
503
+ if (typeof value !== "string" || value.length === 0) {
504
+ continue;
505
+ }
506
+ const allow = ALLOWED_MODELS_BY_TYPE[surface.modelType];
507
+ if (allow && !allow.includes(value)) {
508
+ continue;
509
+ }
510
+ out[key] = value;
511
+ }
512
+ return out;
513
+ }
514
+ function getEffectiveModelSurfaces() {
515
+ const out = {};
516
+ for (const [id, surface] of Object.entries(MODEL_SURFACES)) {
517
+ const orgDefault = orgDefaultModels[id];
518
+ out[id] = orgDefault ? { ...surface, default: orgDefault } : { ...surface };
519
+ }
520
+ return out;
521
+ }
522
+ function resolveModel(surfaceId, models, fallback) {
523
+ return models?.[surfaceId] ?? fallback ?? orgDefaultModels[surfaceId] ?? MODEL_SURFACES[surfaceId].default;
524
+ }
525
+
380
526
  // src/orgContext.ts
381
527
  var log3 = createLogger("orgContext");
382
528
  var cached = null;
383
529
  async function initOrgContext(config) {
384
530
  try {
385
531
  cached = await fetchRemyContext(config);
532
+ const orgDefaultModels2 = filterModelPicks(cached?.defaultModels);
533
+ setOrgDefaultModels(orgDefaultModels2);
386
534
  log3.debug("org context loaded", {
387
535
  delegatedAvailable: cached?.auth?.delegatedAvailable ?? false,
388
536
  requireDelegatedOnly: cached?.auth?.requireDelegatedOnly ?? false,
389
- hasOrgName: !!cached?.org?.name
537
+ hasOrgName: !!cached?.org?.name,
538
+ orgDefaultModels: orgDefaultModels2
390
539
  });
391
540
  } catch (err) {
392
541
  cached = null;
542
+ setOrgDefaultModels({});
393
543
  log3.debug("org context init failed", { error: err.message });
394
544
  }
395
545
  }
@@ -3536,115 +3686,6 @@ ${appSpec}
3536
3686
  }
3537
3687
  }
3538
3688
 
3539
- // src/models/surfaces.ts
3540
- var MODEL_SURFACES = {
3541
- parent: {
3542
- default: "claude-4-8-opus",
3543
- label: "Remy",
3544
- description: "The main Remy agent you chat with about your product. Writes code and manages delegation to other agents.",
3545
- modelType: "text",
3546
- userPickable: true
3547
- },
3548
- visualDesignExpert: {
3549
- default: "claude-4-8-opus",
3550
- label: "Design Agent",
3551
- description: "Designs your product's interfaces, including components, layouts, typography, color, and visual identity.",
3552
- modelType: "text",
3553
- userPickable: true
3554
- },
3555
- productVision: {
3556
- default: "claude-5-sonnet",
3557
- label: "Roadmap Agent",
3558
- description: "Owns your product's roadmap and pitch deck. Helps decide what to build next and how to frame the big picture.",
3559
- modelType: "text",
3560
- userPickable: true
3561
- },
3562
- browserAutomation: {
3563
- default: "claude-5-sonnet",
3564
- label: "QA Agent",
3565
- description: "Tests features and UI flows in an automated browser to verify they work end to end.",
3566
- modelType: "text",
3567
- userPickable: true
3568
- },
3569
- codeSanityCheck: {
3570
- default: "claude-5-sonnet",
3571
- label: "Architecture Agent",
3572
- description: "Reviews the architecture and structure of code changes to avoid technical debt.",
3573
- modelType: "text",
3574
- userPickable: true
3575
- },
3576
- copyEditor: {
3577
- default: "claude-5-sonnet",
3578
- label: "Copy Agent",
3579
- description: "Tightens prose and copy across your app and its launch materials so it reads sharp and human, never machine-made.",
3580
- modelType: "text",
3581
- userPickable: true
3582
- },
3583
- imageGeneration: {
3584
- default: "seedream-4.5",
3585
- label: "Image Generation",
3586
- description: "Creates images for your product \u2014 icons, illustrations, photos, and any other visual assets.",
3587
- modelType: "image_generation",
3588
- userPickable: true
3589
- },
3590
- imageAnalysis: {
3591
- default: "claude-5-sonnet",
3592
- label: "Image Analysis",
3593
- 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.",
3594
- modelType: "vision",
3595
- userPickable: true
3596
- },
3597
- conversationSummarizer: {
3598
- default: "claude-5-sonnet",
3599
- label: "Compaction Utility",
3600
- description: "Compresses long conversations into summaries to keep things responsive.",
3601
- modelType: "text",
3602
- userPickable: true
3603
- },
3604
- brandExtractor: {
3605
- default: "claude-5-sonnet",
3606
- label: "Brand Utility",
3607
- description: "Extracts your product's name, colors, and fonts from your spec for use in branded documents.",
3608
- modelType: "text",
3609
- userPickable: true
3610
- },
3611
- // Internal surface — not user-pickable. Remy uses this to rewrite design
3612
- // briefs into model-optimized image prompts before image generation.
3613
- imagePromptEnhancer: {
3614
- default: "claude-5-sonnet",
3615
- label: "Image Prompt Enhancer",
3616
- description: "Rewrites image briefs into model-optimized prompts before image generation.",
3617
- modelType: "text",
3618
- userPickable: false
3619
- }
3620
- };
3621
- var ALLOWED_MODELS_BY_TYPE = {
3622
- text: [
3623
- "claude-4-8-opus",
3624
- "claude-4-7-opus",
3625
- "claude-4-6-opus",
3626
- "claude-4-6-sonnet",
3627
- "claude-fable-5",
3628
- "claude-5-sonnet",
3629
- "gpt-5.5",
3630
- "gemini-3-pro",
3631
- "gemini-3.1-pro",
3632
- "gemini-3-flash",
3633
- "gemini-3.5-flash",
3634
- "grok-build-0.1",
3635
- "grok-4.5",
3636
- "glm-5.2",
3637
- "muse-spark-1.1",
3638
- "kimi-k2-7-code",
3639
- "kimi-k3"
3640
- ]
3641
- // vision: undefined — unconstrained
3642
- // image_generation: undefined — unconstrained
3643
- };
3644
- function resolveModel(surfaceId, models, fallback) {
3645
- return models?.[surfaceId] ?? fallback ?? MODEL_SURFACES[surfaceId].default;
3646
- }
3647
-
3648
3689
  // src/subagents/browserAutomation/index.ts
3649
3690
  var log7 = createLogger("browser-automation");
3650
3691
  async function runBrowserAutomation(task, context, opts) {
@@ -6011,15 +6052,17 @@ async function runExtraction(apiConfig, model) {
6011
6052
  log10.info("Brand persisted", { inputHash });
6012
6053
  return brand;
6013
6054
  }
6055
+ function isBrandRelevant(filePath) {
6056
+ if (filePath === path10.join("src", "app.md")) {
6057
+ return true;
6058
+ }
6059
+ const { type } = parseFrontmatter3(filePath);
6060
+ return type.startsWith("design/color") || type.startsWith("design/typography");
6061
+ }
6014
6062
  function computeInputHash() {
6015
6063
  const entries = [];
6016
6064
  for (const filePath of walkMdFiles3("src")) {
6017
- if (filePath === path10.join("src", "app.md")) {
6018
- entries.push({ path: filePath, content: readSafe(filePath) });
6019
- continue;
6020
- }
6021
- const fm = parseFrontmatter3(filePath);
6022
- if (fm.type.startsWith("design/color") || fm.type.startsWith("design/typography")) {
6065
+ if (isBrandRelevant(filePath)) {
6023
6066
  entries.push({ path: filePath, content: readSafe(filePath) });
6024
6067
  }
6025
6068
  }
@@ -6122,23 +6165,42 @@ async function extractBrand(apiConfig, model) {
6122
6165
  }
6123
6166
  return validateBrand(parsed);
6124
6167
  }
6168
+ var BRAND_CORPUS_CHAR_LIMIT = 24e5;
6125
6169
  function buildCorpus() {
6126
- const sections = [];
6170
+ const all = walkMdFiles3("src");
6171
+ const ordered = [
6172
+ ...all.filter(isBrandRelevant),
6173
+ ...all.filter((f) => !isBrandRelevant(f))
6174
+ ];
6175
+ const files = [];
6127
6176
  const manifest = readSafe("mindstudio.json");
6128
6177
  if (manifest) {
6129
- sections.push(`## File: mindstudio.json
6130
-
6131
- ${manifest}`);
6178
+ files.push({ path: "mindstudio.json", content: manifest });
6132
6179
  }
6133
- for (const filePath of walkMdFiles3("src")) {
6180
+ for (const filePath of ordered) {
6134
6181
  const content = readSafe(filePath);
6135
6182
  if (content) {
6136
- sections.push(`## File: ${filePath}
6183
+ files.push({ path: filePath, content });
6184
+ }
6185
+ }
6186
+ const sep = "\n\n---\n\n";
6187
+ const sections = [];
6188
+ let usedChars = 0;
6189
+ for (const { path: p, content } of files) {
6190
+ const section = `## File: ${p}
6137
6191
 
6138
- ${content}`);
6192
+ ${content}`;
6193
+ const added = section.length + (sections.length > 0 ? sep.length : 0);
6194
+ if (sections.length > 0 && usedChars + added > BRAND_CORPUS_CHAR_LIMIT) {
6195
+ sections.push(
6196
+ `(brand corpus truncated: included ${sections.length} of ${files.length} files, ~${(usedChars / 1024).toFixed(0)}KB; brand-relevant files were prioritized.)`
6197
+ );
6198
+ break;
6139
6199
  }
6200
+ sections.push(section);
6201
+ usedChars += added;
6140
6202
  }
6141
- return sections.join("\n\n---\n\n");
6203
+ return sections.join(sep);
6142
6204
  }
6143
6205
  function parseJsonResponse(text) {
6144
6206
  const trimmed = text.trim();
@@ -6280,6 +6342,21 @@ function triggerBrandExtraction(apiConfig, model) {
6280
6342
  // src/session.ts
6281
6343
  import fs22 from "fs";
6282
6344
  import path11 from "path";
6345
+
6346
+ // src/toolResultCap.ts
6347
+ var MAX_TOOL_RESULT_BYTES = 256 * 1024;
6348
+ function capToolResult(result) {
6349
+ const total = Buffer.byteLength(result, "utf-8");
6350
+ if (total <= MAX_TOOL_RESULT_BYTES) {
6351
+ return result;
6352
+ }
6353
+ const head = Buffer.from(result, "utf-8").subarray(0, MAX_TOOL_RESULT_BYTES).toString("utf-8");
6354
+ return head + `
6355
+
6356
+ (tool result truncated at ${(MAX_TOOL_RESULT_BYTES / 1024).toFixed(0)}KB of ${(total / 1024).toFixed(0)}KB \u2014 too large to keep in context. Narrow the call (select fewer fields, paginate, or query a subset) instead of fetching everything.)`;
6357
+ }
6358
+
6359
+ // src/session.ts
6283
6360
  var log12 = createLogger("session");
6284
6361
  var SESSION_FILE = ".remy-session.json";
6285
6362
  var ARCHIVE_DIR = ".logs/sessions";
@@ -6304,11 +6381,23 @@ function loadSession(state) {
6304
6381
  }
6305
6382
  return false;
6306
6383
  }
6384
+ function capOversizedResults(msg) {
6385
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
6386
+ for (const block of msg.content) {
6387
+ if (block.type === "tool" && typeof block.result === "string") {
6388
+ block.result = capToolResult(block.result);
6389
+ }
6390
+ }
6391
+ } else if (msg.role === "user" && msg.toolCallId && typeof msg.content === "string") {
6392
+ msg.content = capToolResult(msg.content);
6393
+ }
6394
+ }
6307
6395
  function sanitizeMessages(messages) {
6308
6396
  const result = [];
6309
6397
  for (let i = 0; i < messages.length; i++) {
6310
- result.push(messages[i]);
6311
6398
  const msg = messages[i];
6399
+ capOversizedResults(msg);
6400
+ result.push(msg);
6312
6401
  if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
6313
6402
  continue;
6314
6403
  }
@@ -6597,6 +6686,10 @@ var patterns = [
6597
6686
  "Too many requests. Please wait a moment and try again."
6598
6687
  ],
6599
6688
  [/HTTP 40[13]/i, "Authentication failed. Please check your API key."],
6689
+ [
6690
+ /HTTP 413/i,
6691
+ "This conversation has grown too large to send in a single request. Starting a new conversation will reset the context."
6692
+ ],
6600
6693
  [
6601
6694
  /HTTP 5\d\d/i,
6602
6695
  "The AI service is temporarily unavailable. Please try again."
@@ -7123,7 +7216,7 @@ async function runTurn(params) {
7123
7216
  })
7124
7217
  });
7125
7218
  }
7126
- safeSettle(result, result.startsWith("Error"));
7219
+ safeSettle(capToolResult(result), result.startsWith("Error"));
7127
7220
  } catch (err) {
7128
7221
  safeSettle(`Error: ${err.message}`, true);
7129
7222
  }
@@ -7578,7 +7671,7 @@ var HeadlessSession = class {
7578
7671
  this.emit("session_restored", {
7579
7672
  messageCount: this.state.messages.length,
7580
7673
  ...this.state.models && { models: this.state.models },
7581
- modelSurfaces: MODEL_SURFACES,
7674
+ modelSurfaces: getEffectiveModelSurfaces(),
7582
7675
  allowedModelsByType: ALLOWED_MODELS_BY_TYPE
7583
7676
  });
7584
7677
  }
@@ -8176,7 +8269,7 @@ var HeadlessSession = class {
8176
8269
  saveSession(this.state);
8177
8270
  return {
8178
8271
  ...this.state.models && { models: this.state.models },
8179
- modelSurfaces: MODEL_SURFACES,
8272
+ modelSurfaces: getEffectiveModelSurfaces(),
8180
8273
  allowedModelsByType: ALLOWED_MODELS_BY_TYPE
8181
8274
  };
8182
8275
  }
@@ -8272,7 +8365,7 @@ var HeadlessSession = class {
8272
8365
  running: this.running,
8273
8366
  ...this.running && this.currentRequestId ? { currentRequestId: this.currentRequestId } : {},
8274
8367
  ...this.state.models && { models: this.state.models },
8275
- modelSurfaces: MODEL_SURFACES,
8368
+ modelSurfaces: getEffectiveModelSurfaces(),
8276
8369
  allowedModelsByType: ALLOWED_MODELS_BY_TYPE,
8277
8370
  // Current queue snapshot for connect/reconnect — get_history is the
8278
8371
  // on-demand "current state" query. Always an array (possibly empty),
package/dist/index.js CHANGED
@@ -1942,17 +1942,173 @@ var init_projectContext = __esm({
1942
1942
  }
1943
1943
  });
1944
1944
 
1945
+ // src/models/surfaces.ts
1946
+ function setOrgDefaultModels(models) {
1947
+ orgDefaultModels = models;
1948
+ }
1949
+ function filterModelPicks(picks) {
1950
+ const out = {};
1951
+ if (!picks || typeof picks !== "object") {
1952
+ return out;
1953
+ }
1954
+ for (const [key, value] of Object.entries(picks)) {
1955
+ if (!(key in MODEL_SURFACES)) {
1956
+ continue;
1957
+ }
1958
+ const surface = MODEL_SURFACES[key];
1959
+ if (!surface.userPickable) {
1960
+ continue;
1961
+ }
1962
+ if (typeof value !== "string" || value.length === 0) {
1963
+ continue;
1964
+ }
1965
+ const allow = ALLOWED_MODELS_BY_TYPE[surface.modelType];
1966
+ if (allow && !allow.includes(value)) {
1967
+ continue;
1968
+ }
1969
+ out[key] = value;
1970
+ }
1971
+ return out;
1972
+ }
1973
+ function getEffectiveModelSurfaces() {
1974
+ const out = {};
1975
+ for (const [id, surface] of Object.entries(MODEL_SURFACES)) {
1976
+ const orgDefault = orgDefaultModels[id];
1977
+ out[id] = orgDefault ? { ...surface, default: orgDefault } : { ...surface };
1978
+ }
1979
+ return out;
1980
+ }
1981
+ function resolveModel(surfaceId, models, fallback) {
1982
+ return models?.[surfaceId] ?? fallback ?? orgDefaultModels[surfaceId] ?? MODEL_SURFACES[surfaceId].default;
1983
+ }
1984
+ var MODEL_SURFACES, ALLOWED_MODELS_BY_TYPE, orgDefaultModels;
1985
+ var init_surfaces = __esm({
1986
+ "src/models/surfaces.ts"() {
1987
+ "use strict";
1988
+ MODEL_SURFACES = {
1989
+ parent: {
1990
+ default: "claude-5-opus",
1991
+ label: "Remy",
1992
+ description: "The main Remy agent you chat with about your product. Writes code and manages delegation to other agents.",
1993
+ modelType: "text",
1994
+ userPickable: true
1995
+ },
1996
+ visualDesignExpert: {
1997
+ default: "claude-5-opus",
1998
+ label: "Design Agent",
1999
+ description: "Designs your product's interfaces, including components, layouts, typography, color, and visual identity.",
2000
+ modelType: "text",
2001
+ userPickable: true
2002
+ },
2003
+ productVision: {
2004
+ default: "claude-5-sonnet",
2005
+ label: "Roadmap Agent",
2006
+ description: "Owns your product's roadmap and pitch deck. Helps decide what to build next and how to frame the big picture.",
2007
+ modelType: "text",
2008
+ userPickable: true
2009
+ },
2010
+ browserAutomation: {
2011
+ default: "claude-5-sonnet",
2012
+ label: "QA Agent",
2013
+ description: "Tests features and UI flows in an automated browser to verify they work end to end.",
2014
+ modelType: "text",
2015
+ userPickable: true
2016
+ },
2017
+ codeSanityCheck: {
2018
+ default: "claude-5-sonnet",
2019
+ label: "Architecture Agent",
2020
+ description: "Reviews the architecture and structure of code changes to avoid technical debt.",
2021
+ modelType: "text",
2022
+ userPickable: true
2023
+ },
2024
+ copyEditor: {
2025
+ default: "claude-5-sonnet",
2026
+ label: "Copy Agent",
2027
+ description: "Tightens prose and copy across your app and its launch materials so it reads sharp and human, never machine-made.",
2028
+ modelType: "text",
2029
+ userPickable: true
2030
+ },
2031
+ imageGeneration: {
2032
+ default: "seedream-4.5",
2033
+ label: "Image Generation",
2034
+ description: "Creates images for your product \u2014 icons, illustrations, photos, and any other visual assets.",
2035
+ modelType: "image_generation",
2036
+ userPickable: true
2037
+ },
2038
+ imageAnalysis: {
2039
+ default: "claude-5-sonnet",
2040
+ label: "Image Analysis",
2041
+ 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.",
2042
+ modelType: "vision",
2043
+ userPickable: true
2044
+ },
2045
+ conversationSummarizer: {
2046
+ default: "claude-5-sonnet",
2047
+ label: "Compaction Utility",
2048
+ description: "Compresses long conversations into summaries to keep things responsive.",
2049
+ modelType: "text",
2050
+ userPickable: true
2051
+ },
2052
+ brandExtractor: {
2053
+ default: "claude-5-sonnet",
2054
+ label: "Brand Utility",
2055
+ description: "Extracts your product's name, colors, and fonts from your spec for use in branded documents.",
2056
+ modelType: "text",
2057
+ userPickable: true
2058
+ },
2059
+ // Internal surface — not user-pickable. Remy uses this to rewrite design
2060
+ // briefs into model-optimized image prompts before image generation.
2061
+ imagePromptEnhancer: {
2062
+ default: "claude-5-sonnet",
2063
+ label: "Image Prompt Enhancer",
2064
+ description: "Rewrites image briefs into model-optimized prompts before image generation.",
2065
+ modelType: "text",
2066
+ userPickable: false
2067
+ }
2068
+ };
2069
+ ALLOWED_MODELS_BY_TYPE = {
2070
+ text: [
2071
+ "claude-5-opus",
2072
+ "claude-4-8-opus",
2073
+ "claude-4-7-opus",
2074
+ "claude-4-6-opus",
2075
+ "claude-4-6-sonnet",
2076
+ "claude-fable-5",
2077
+ "claude-5-sonnet",
2078
+ "gpt-5.5",
2079
+ "gemini-3-pro",
2080
+ "gemini-3.1-pro",
2081
+ "gemini-3-flash",
2082
+ "gemini-3.5-flash",
2083
+ "grok-build-0.1",
2084
+ "grok-4.5",
2085
+ "glm-5.2",
2086
+ "muse-spark-1.1",
2087
+ "kimi-k2-7-code",
2088
+ "kimi-k3"
2089
+ ]
2090
+ // vision: undefined — unconstrained
2091
+ // image_generation: undefined — unconstrained
2092
+ };
2093
+ orgDefaultModels = {};
2094
+ }
2095
+ });
2096
+
1945
2097
  // src/orgContext.ts
1946
2098
  async function initOrgContext(config) {
1947
2099
  try {
1948
2100
  cached = await fetchRemyContext(config);
2101
+ const orgDefaultModels2 = filterModelPicks(cached?.defaultModels);
2102
+ setOrgDefaultModels(orgDefaultModels2);
1949
2103
  log3.debug("org context loaded", {
1950
2104
  delegatedAvailable: cached?.auth?.delegatedAvailable ?? false,
1951
2105
  requireDelegatedOnly: cached?.auth?.requireDelegatedOnly ?? false,
1952
- hasOrgName: !!cached?.org?.name
2106
+ hasOrgName: !!cached?.org?.name,
2107
+ orgDefaultModels: orgDefaultModels2
1953
2108
  });
1954
2109
  } catch (err) {
1955
2110
  cached = null;
2111
+ setOrgDefaultModels({});
1956
2112
  log3.debug("org context init failed", { error: err.message });
1957
2113
  }
1958
2114
  }
@@ -1987,6 +2143,7 @@ var init_orgContext = __esm({
1987
2143
  "src/orgContext.ts"() {
1988
2144
  "use strict";
1989
2145
  init_api();
2146
+ init_surfaces();
1990
2147
  init_logger();
1991
2148
  log3 = createLogger("orgContext");
1992
2149
  cached = null;
@@ -2148,121 +2305,6 @@ var init_prompt = __esm({
2148
2305
  }
2149
2306
  });
2150
2307
 
2151
- // src/models/surfaces.ts
2152
- function resolveModel(surfaceId, models, fallback) {
2153
- return models?.[surfaceId] ?? fallback ?? MODEL_SURFACES[surfaceId].default;
2154
- }
2155
- var MODEL_SURFACES, ALLOWED_MODELS_BY_TYPE;
2156
- var init_surfaces = __esm({
2157
- "src/models/surfaces.ts"() {
2158
- "use strict";
2159
- MODEL_SURFACES = {
2160
- parent: {
2161
- default: "claude-4-8-opus",
2162
- label: "Remy",
2163
- description: "The main Remy agent you chat with about your product. Writes code and manages delegation to other agents.",
2164
- modelType: "text",
2165
- userPickable: true
2166
- },
2167
- visualDesignExpert: {
2168
- default: "claude-4-8-opus",
2169
- label: "Design Agent",
2170
- description: "Designs your product's interfaces, including components, layouts, typography, color, and visual identity.",
2171
- modelType: "text",
2172
- userPickable: true
2173
- },
2174
- productVision: {
2175
- default: "claude-5-sonnet",
2176
- label: "Roadmap Agent",
2177
- description: "Owns your product's roadmap and pitch deck. Helps decide what to build next and how to frame the big picture.",
2178
- modelType: "text",
2179
- userPickable: true
2180
- },
2181
- browserAutomation: {
2182
- default: "claude-5-sonnet",
2183
- label: "QA Agent",
2184
- description: "Tests features and UI flows in an automated browser to verify they work end to end.",
2185
- modelType: "text",
2186
- userPickable: true
2187
- },
2188
- codeSanityCheck: {
2189
- default: "claude-5-sonnet",
2190
- label: "Architecture Agent",
2191
- description: "Reviews the architecture and structure of code changes to avoid technical debt.",
2192
- modelType: "text",
2193
- userPickable: true
2194
- },
2195
- copyEditor: {
2196
- default: "claude-5-sonnet",
2197
- label: "Copy Agent",
2198
- description: "Tightens prose and copy across your app and its launch materials so it reads sharp and human, never machine-made.",
2199
- modelType: "text",
2200
- userPickable: true
2201
- },
2202
- imageGeneration: {
2203
- default: "seedream-4.5",
2204
- label: "Image Generation",
2205
- description: "Creates images for your product \u2014 icons, illustrations, photos, and any other visual assets.",
2206
- modelType: "image_generation",
2207
- userPickable: true
2208
- },
2209
- imageAnalysis: {
2210
- default: "claude-5-sonnet",
2211
- label: "Image Analysis",
2212
- 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.",
2213
- modelType: "vision",
2214
- userPickable: true
2215
- },
2216
- conversationSummarizer: {
2217
- default: "claude-5-sonnet",
2218
- label: "Compaction Utility",
2219
- description: "Compresses long conversations into summaries to keep things responsive.",
2220
- modelType: "text",
2221
- userPickable: true
2222
- },
2223
- brandExtractor: {
2224
- default: "claude-5-sonnet",
2225
- label: "Brand Utility",
2226
- description: "Extracts your product's name, colors, and fonts from your spec for use in branded documents.",
2227
- modelType: "text",
2228
- userPickable: true
2229
- },
2230
- // Internal surface — not user-pickable. Remy uses this to rewrite design
2231
- // briefs into model-optimized image prompts before image generation.
2232
- imagePromptEnhancer: {
2233
- default: "claude-5-sonnet",
2234
- label: "Image Prompt Enhancer",
2235
- description: "Rewrites image briefs into model-optimized prompts before image generation.",
2236
- modelType: "text",
2237
- userPickable: false
2238
- }
2239
- };
2240
- ALLOWED_MODELS_BY_TYPE = {
2241
- text: [
2242
- "claude-4-8-opus",
2243
- "claude-4-7-opus",
2244
- "claude-4-6-opus",
2245
- "claude-4-6-sonnet",
2246
- "claude-fable-5",
2247
- "claude-5-sonnet",
2248
- "gpt-5.5",
2249
- "gemini-3-pro",
2250
- "gemini-3.1-pro",
2251
- "gemini-3-flash",
2252
- "gemini-3.5-flash",
2253
- "grok-build-0.1",
2254
- "grok-4.5",
2255
- "glm-5.2",
2256
- "muse-spark-1.1",
2257
- "kimi-k2-7-code",
2258
- "kimi-k3"
2259
- ]
2260
- // vision: undefined — unconstrained
2261
- // image_generation: undefined — unconstrained
2262
- };
2263
- }
2264
- });
2265
-
2266
2308
  // src/compaction/trigger.ts
2267
2309
  function getPendingSummaries() {
2268
2310
  return pendingSummaries.splice(0);
@@ -6652,6 +6694,25 @@ var init_tools7 = __esm({
6652
6694
  }
6653
6695
  });
6654
6696
 
6697
+ // src/toolResultCap.ts
6698
+ function capToolResult(result) {
6699
+ const total = Buffer.byteLength(result, "utf-8");
6700
+ if (total <= MAX_TOOL_RESULT_BYTES) {
6701
+ return result;
6702
+ }
6703
+ const head = Buffer.from(result, "utf-8").subarray(0, MAX_TOOL_RESULT_BYTES).toString("utf-8");
6704
+ return head + `
6705
+
6706
+ (tool result truncated at ${(MAX_TOOL_RESULT_BYTES / 1024).toFixed(0)}KB of ${(total / 1024).toFixed(0)}KB \u2014 too large to keep in context. Narrow the call (select fewer fields, paginate, or query a subset) instead of fetching everything.)`;
6707
+ }
6708
+ var MAX_TOOL_RESULT_BYTES;
6709
+ var init_toolResultCap = __esm({
6710
+ "src/toolResultCap.ts"() {
6711
+ "use strict";
6712
+ MAX_TOOL_RESULT_BYTES = 256 * 1024;
6713
+ }
6714
+ });
6715
+
6655
6716
  // src/session.ts
6656
6717
  import fs20 from "fs";
6657
6718
  import path9 from "path";
@@ -6674,11 +6735,23 @@ function loadSession(state) {
6674
6735
  }
6675
6736
  return false;
6676
6737
  }
6738
+ function capOversizedResults(msg) {
6739
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
6740
+ for (const block of msg.content) {
6741
+ if (block.type === "tool" && typeof block.result === "string") {
6742
+ block.result = capToolResult(block.result);
6743
+ }
6744
+ }
6745
+ } else if (msg.role === "user" && msg.toolCallId && typeof msg.content === "string") {
6746
+ msg.content = capToolResult(msg.content);
6747
+ }
6748
+ }
6677
6749
  function sanitizeMessages(messages) {
6678
6750
  const result = [];
6679
6751
  for (let i = 0; i < messages.length; i++) {
6680
- result.push(messages[i]);
6681
6752
  const msg = messages[i];
6753
+ capOversizedResults(msg);
6754
+ result.push(msg);
6682
6755
  if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
6683
6756
  continue;
6684
6757
  }
@@ -6799,6 +6872,7 @@ var init_session = __esm({
6799
6872
  init_logger();
6800
6873
  init_compaction();
6801
6874
  init_cleanMessages();
6875
+ init_toolResultCap();
6802
6876
  log9 = createLogger("session");
6803
6877
  SESSION_FILE = ".remy-session.json";
6804
6878
  ARCHIVE_DIR = ".logs/sessions";
@@ -6999,6 +7073,10 @@ var init_errors = __esm({
6999
7073
  "Too many requests. Please wait a moment and try again."
7000
7074
  ],
7001
7075
  [/HTTP 40[13]/i, "Authentication failed. Please check your API key."],
7076
+ [
7077
+ /HTTP 413/i,
7078
+ "This conversation has grown too large to send in a single request. Starting a new conversation will reset the context."
7079
+ ],
7002
7080
  [
7003
7081
  /HTTP 5\d\d/i,
7004
7082
  "The AI service is temporarily unavailable. Please try again."
@@ -7033,15 +7111,17 @@ async function runExtraction(apiConfig, model) {
7033
7111
  log10.info("Brand persisted", { inputHash });
7034
7112
  return brand;
7035
7113
  }
7114
+ function isBrandRelevant(filePath) {
7115
+ if (filePath === path10.join("src", "app.md")) {
7116
+ return true;
7117
+ }
7118
+ const { type } = parseFrontmatter3(filePath);
7119
+ return type.startsWith("design/color") || type.startsWith("design/typography");
7120
+ }
7036
7121
  function computeInputHash() {
7037
7122
  const entries = [];
7038
7123
  for (const filePath of walkMdFiles3("src")) {
7039
- if (filePath === path10.join("src", "app.md")) {
7040
- entries.push({ path: filePath, content: readSafe(filePath) });
7041
- continue;
7042
- }
7043
- const fm = parseFrontmatter3(filePath);
7044
- if (fm.type.startsWith("design/color") || fm.type.startsWith("design/typography")) {
7124
+ if (isBrandRelevant(filePath)) {
7045
7125
  entries.push({ path: filePath, content: readSafe(filePath) });
7046
7126
  }
7047
7127
  }
@@ -7145,22 +7225,40 @@ async function extractBrand(apiConfig, model) {
7145
7225
  return validateBrand(parsed);
7146
7226
  }
7147
7227
  function buildCorpus() {
7148
- const sections = [];
7228
+ const all = walkMdFiles3("src");
7229
+ const ordered = [
7230
+ ...all.filter(isBrandRelevant),
7231
+ ...all.filter((f) => !isBrandRelevant(f))
7232
+ ];
7233
+ const files = [];
7149
7234
  const manifest = readSafe("mindstudio.json");
7150
7235
  if (manifest) {
7151
- sections.push(`## File: mindstudio.json
7152
-
7153
- ${manifest}`);
7236
+ files.push({ path: "mindstudio.json", content: manifest });
7154
7237
  }
7155
- for (const filePath of walkMdFiles3("src")) {
7238
+ for (const filePath of ordered) {
7156
7239
  const content = readSafe(filePath);
7157
7240
  if (content) {
7158
- sections.push(`## File: ${filePath}
7241
+ files.push({ path: filePath, content });
7242
+ }
7243
+ }
7244
+ const sep = "\n\n---\n\n";
7245
+ const sections = [];
7246
+ let usedChars = 0;
7247
+ for (const { path: p, content } of files) {
7248
+ const section = `## File: ${p}
7159
7249
 
7160
- ${content}`);
7250
+ ${content}`;
7251
+ const added = section.length + (sections.length > 0 ? sep.length : 0);
7252
+ if (sections.length > 0 && usedChars + added > BRAND_CORPUS_CHAR_LIMIT) {
7253
+ sections.push(
7254
+ `(brand corpus truncated: included ${sections.length} of ${files.length} files, ~${(usedChars / 1024).toFixed(0)}KB; brand-relevant files were prioritized.)`
7255
+ );
7256
+ break;
7161
7257
  }
7258
+ sections.push(section);
7259
+ usedChars += added;
7162
7260
  }
7163
- return sections.join("\n\n---\n\n");
7261
+ return sections.join(sep);
7164
7262
  }
7165
7263
  function parseJsonResponse(text) {
7166
7264
  const trimmed = text.trim();
@@ -7277,7 +7375,7 @@ function readCache() {
7277
7375
  return null;
7278
7376
  }
7279
7377
  }
7280
- var log10, EXTRACT_PROMPT, BRAND_FILE, CACHE_FILE;
7378
+ var log10, EXTRACT_PROMPT, BRAND_FILE, CACHE_FILE, BRAND_CORPUS_CHAR_LIMIT;
7281
7379
  var init_brandExtraction = __esm({
7282
7380
  "src/brandExtraction/index.ts"() {
7283
7381
  "use strict";
@@ -7289,6 +7387,7 @@ var init_brandExtraction = __esm({
7289
7387
  EXTRACT_PROMPT = readAsset("brandExtraction", "extract.md");
7290
7388
  BRAND_FILE = ".remy-brand.json";
7291
7389
  CACHE_FILE = ".remy-brand.cache.json";
7390
+ BRAND_CORPUS_CHAR_LIMIT = 24e5;
7292
7391
  }
7293
7392
  });
7294
7393
 
@@ -7810,7 +7909,7 @@ async function runTurn(params) {
7810
7909
  })
7811
7910
  });
7812
7911
  }
7813
- safeSettle(result, result.startsWith("Error"));
7912
+ safeSettle(capToolResult(result), result.startsWith("Error"));
7814
7913
  } catch (err) {
7815
7914
  safeSettle(`Error: ${err.message}`, true);
7816
7915
  }
@@ -7916,6 +8015,7 @@ var init_agent = __esm({
7916
8015
  init_trigger2();
7917
8016
  init_surfaces();
7918
8017
  init_toolRegistry();
8018
+ init_toolResultCap();
7919
8019
  log12 = createLogger("agent");
7920
8020
  BRAND_TRIGGERING_TOOLS = /* @__PURE__ */ new Set(["writeSpec", "editSpec"]);
7921
8021
  EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
@@ -8417,7 +8517,7 @@ var init_headless = __esm({
8417
8517
  this.emit("session_restored", {
8418
8518
  messageCount: this.state.messages.length,
8419
8519
  ...this.state.models && { models: this.state.models },
8420
- modelSurfaces: MODEL_SURFACES,
8520
+ modelSurfaces: getEffectiveModelSurfaces(),
8421
8521
  allowedModelsByType: ALLOWED_MODELS_BY_TYPE
8422
8522
  });
8423
8523
  }
@@ -9015,7 +9115,7 @@ var init_headless = __esm({
9015
9115
  saveSession(this.state);
9016
9116
  return {
9017
9117
  ...this.state.models && { models: this.state.models },
9018
- modelSurfaces: MODEL_SURFACES,
9118
+ modelSurfaces: getEffectiveModelSurfaces(),
9019
9119
  allowedModelsByType: ALLOWED_MODELS_BY_TYPE
9020
9120
  };
9021
9121
  }
@@ -9111,7 +9211,7 @@ var init_headless = __esm({
9111
9211
  running: this.running,
9112
9212
  ...this.running && this.currentRequestId ? { currentRequestId: this.currentRequestId } : {},
9113
9213
  ...this.state.models && { models: this.state.models },
9114
- modelSurfaces: MODEL_SURFACES,
9214
+ modelSurfaces: getEffectiveModelSurfaces(),
9115
9215
  allowedModelsByType: ALLOWED_MODELS_BY_TYPE,
9116
9216
  // Current queue snapshot for connect/reconnect — get_history is the
9117
9217
  // on-demand "current state" query. Always an array (possibly empty),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.224",
3
+ "version": "0.1.226",
4
4
  "description": "Remy coding agent",
5
5
  "repository": {
6
6
  "type": "git",