@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/headless.js CHANGED
@@ -343,6 +343,10 @@ async function* streamChat(params) {
343
343
  requestId,
344
344
  ...subAgentId && { subAgentId },
345
345
  error: event.error,
346
+ // The code decides whether this retries, so omitting it made a
347
+ // debug bundle unable to answer why a turn died — the whole
348
+ // classification input was invisible in 32k log lines (RPT-1234).
349
+ ...event.code && { code: event.code },
346
350
  durationMs: Date.now() - startTime
347
351
  });
348
352
  }
@@ -378,17 +382,22 @@ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
378
382
  "overloaded_error"
379
383
  // Anthropic's 529-equivalent
380
384
  ]);
385
+ var RETRYABLE_ERROR_CODE_PREFIXES = ["media_url_"];
381
386
  function isRetryableError(error, code) {
382
387
  if (code && RETRYABLE_ERROR_CODES.has(code)) {
383
388
  return true;
384
389
  }
390
+ if (code && RETRYABLE_ERROR_CODE_PREFIXES.some((p) => code.startsWith(p))) {
391
+ return true;
392
+ }
385
393
  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
386
394
  // pods that don't send a machine-readable code.
387
- /Internal API error/i.test(error) || // Anthropic fetches url-source image blocks server-side on every call;
388
- // a transient failure anywhere in that chain (S3 signing edge, resize
389
- // proxy, their fetcher) surfaces as this message. The images themselves
390
- // are fine — the same URLs typically succeed on the next attempt.
391
- /Unable to download/i.test(error);
395
+ /Internal API error/i.test(error) || // Server-side media-fetch failures, for providers that send no usable
396
+ // code. Matching PROSE is the fallback, not the mechanism: keying on one
397
+ // vendor's wording is what let this class through before — Anthropic's
398
+ // "Unable to download" was handled while Meta's "failed to download
399
+ // media" was fatal, for the identical failure with the identical remedy.
400
+ /Unable to download/i.test(error) || /failed to download media/i.test(error);
392
401
  }
393
402
  function sleep(ms) {
394
403
  return new Promise((resolve4) => setTimeout(resolve4, ms));
@@ -490,6 +499,42 @@ async function generateBackgroundAck(params) {
490
499
  return FALLBACK_ACK;
491
500
  }
492
501
  }
502
+ var SURFACES_FETCH_ATTEMPTS = 3;
503
+ async function fetchModelSurfaces(config) {
504
+ const url = `${config.baseUrl}/v1/site-settings/remy-model-surfaces`;
505
+ let lastError = "";
506
+ for (let attempt = 1; attempt <= SURFACES_FETCH_ATTEMPTS; attempt++) {
507
+ try {
508
+ const res = await fetch(url, {
509
+ method: "GET",
510
+ signal: AbortSignal.timeout(2e4)
511
+ });
512
+ if (!res.ok) {
513
+ lastError = `HTTP ${res.status}`;
514
+ } else {
515
+ const data = await res.json();
516
+ if (Array.isArray(data?.surfaces) && data.surfaces.length > 0) {
517
+ return data;
518
+ }
519
+ lastError = "response carried no surfaces";
520
+ }
521
+ } catch (err) {
522
+ lastError = err?.message ?? String(err);
523
+ }
524
+ if (attempt < SURFACES_FETCH_ATTEMPTS) {
525
+ const backoffMs = attempt * 2e3;
526
+ log2.debug("model-surfaces fetch failed, retrying", {
527
+ attempt,
528
+ backoffMs,
529
+ error: lastError
530
+ });
531
+ await new Promise((resolve4) => setTimeout(resolve4, backoffMs));
532
+ }
533
+ }
534
+ throw new Error(
535
+ `Could not load model surfaces from ${url} after ${SURFACES_FETCH_ATTEMPTS} attempts (${lastError})`
536
+ );
537
+ }
493
538
  async function fetchRemyContext(config) {
494
539
  if (!config.appId) {
495
540
  return null;
@@ -519,171 +564,99 @@ async function fetchRemyContext(config) {
519
564
  }
520
565
 
521
566
  // src/models/surfaces.ts
522
- var MODEL_SURFACES = {
523
- parent: {
524
- default: "claude-4-8-opus",
525
- label: "Remy",
526
- description: "The main Remy agent you chat with about your product. Writes code and manages delegation to other agents.",
527
- modelType: "text",
528
- userPickable: true
529
- },
530
- visualDesignExpert: {
531
- default: "claude-4-8-opus",
532
- label: "Design Agent",
533
- description: "Designs your product's interfaces, including components, layouts, typography, color, and visual identity.",
534
- modelType: "text",
535
- userPickable: true
536
- },
537
- productVision: {
538
- default: "claude-5-sonnet",
539
- label: "Roadmap Agent",
540
- description: "Owns your product's roadmap and pitch deck. Helps decide what to build next and how to frame the big picture.",
541
- modelType: "text",
542
- userPickable: true
543
- },
544
- browserAutomation: {
545
- default: "claude-5-sonnet",
546
- label: "QA Agent",
547
- description: "Tests features and UI flows in an automated browser to verify they work end to end.",
548
- modelType: "text",
549
- userPickable: true
550
- },
551
- codeSanityCheck: {
552
- default: "claude-5-sonnet",
553
- label: "Architecture Agent",
554
- description: "Reviews the architecture and structure of code changes to avoid technical debt.",
555
- modelType: "text",
556
- userPickable: true
557
- },
558
- research: {
559
- default: "claude-5-sonnet",
560
- label: "Research Agent",
561
- description: "Researches using the web and reports back with citations.",
562
- modelType: "text",
563
- userPickable: true
564
- },
565
- reviewExistingProject: {
566
- default: "claude-5-sonnet",
567
- label: "Existing Project Review",
568
- description: "Reviews a project you bring from another tool and reports what is worth carrying forward.",
569
- modelType: "text",
570
- userPickable: true
571
- },
572
- copyEditor: {
573
- default: "claude-5-sonnet",
574
- label: "Copy Agent",
575
- description: "Tightens prose and copy across your app and its launch materials so it reads sharp and human, never machine-made.",
576
- modelType: "text",
577
- userPickable: true
578
- },
579
- specSync: {
580
- default: "claude-5-sonnet",
581
- label: "Spec Sync Agent",
582
- description: "Keeps your spec in sync with the code as you build, updating the affected sections in the background after changes.",
583
- modelType: "text",
584
- userPickable: true
585
- },
586
- imageGeneration: {
587
- default: "gpt-image-2",
588
- label: "Image Generation",
589
- description: "Creates images for your product \u2014 icons, illustrations, photos, and any other visual assets.",
590
- modelType: "image_generation",
591
- userPickable: true
592
- },
593
- imageAnalysis: {
594
- default: "claude-5-sonnet",
595
- label: "Image Analysis",
596
- 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.",
597
- modelType: "vision",
598
- userPickable: true
599
- },
600
- conversationSummarizer: {
601
- default: "claude-5-sonnet",
602
- label: "Compaction Utility",
603
- description: "Compresses long conversations into summaries to keep things responsive.",
604
- modelType: "text",
605
- userPickable: true
606
- },
607
- brandExtractor: {
608
- default: "claude-5-sonnet",
609
- label: "Brand Utility",
610
- description: "Extracts your product's name, colors, and fonts from your spec for use in branded documents.",
611
- modelType: "text",
612
- userPickable: true
613
- },
614
- // Internal surface — not user-pickable. Remy uses this to rewrite design
615
- // briefs into model-optimized image prompts before image generation.
616
- imagePromptEnhancer: {
617
- default: "claude-5-sonnet",
567
+ var SURFACE_IDS = [
568
+ "parent",
569
+ "visualDesignExpert",
570
+ "productVision",
571
+ "browserAutomation",
572
+ "codeSanityCheck",
573
+ "research",
574
+ "reviewExistingProject",
575
+ "copyEditor",
576
+ "specSync",
577
+ "imageGeneration",
578
+ "imageAnalysis",
579
+ "conversationSummarizer",
580
+ "brandExtractor",
581
+ "imagePromptEnhancer"
582
+ ];
583
+ var DEFAULT_SUGGEST_COMPACT_AT = 3e5;
584
+ var FALLBACK_CONTEXT_LIMITS = { forceCompactAt: 85e4 };
585
+ var surfaces = {};
586
+ var allowedModelsByType = {};
587
+ var textModels = /* @__PURE__ */ new Map();
588
+ var registryLoaded = false;
589
+ function setModelRegistry(payload) {
590
+ const bySurface = {};
591
+ for (const surface of payload.surfaces) {
592
+ if (SURFACE_IDS.includes(surface.id)) {
593
+ bySurface[surface.id] = {
594
+ default: surface.default,
595
+ label: surface.label,
596
+ description: surface.description,
597
+ modelType: surface.modelType,
598
+ // The platform only publishes user-pickable surfaces; internal ones
599
+ // (imagePromptEnhancer) are Remy's own and never appear in a picker.
600
+ userPickable: true
601
+ };
602
+ }
603
+ }
604
+ const missing = SURFACE_IDS.filter(
605
+ (id) => !bySurface[id] && id !== "imagePromptEnhancer"
606
+ );
607
+ if (missing.length > 0) {
608
+ throw new Error(
609
+ `The platform published no model surface for: ${missing.join(", ")}. This Remy build expects them \u2014 the platform is likely older than this release.`
610
+ );
611
+ }
612
+ bySurface.imagePromptEnhancer = {
613
+ default: bySurface.conversationSummarizer.default,
618
614
  label: "Image Prompt Enhancer",
619
615
  description: "Rewrites image briefs into model-optimized prompts before image generation.",
620
616
  modelType: "text",
621
617
  userPickable: false
618
+ };
619
+ surfaces = bySurface;
620
+ allowedModelsByType = payload.allowedModelsByType ?? {};
621
+ textModels = parseTextModels(payload.textModels);
622
+ registryLoaded = true;
623
+ }
624
+ function parseTextModels(raw) {
625
+ const out = /* @__PURE__ */ new Map();
626
+ if (!raw || typeof raw !== "object") {
627
+ return out;
622
628
  }
623
- };
624
- var DEFAULT_SUGGEST_COMPACT_AT = 3e5;
625
- var TEXT_MODELS = {
626
- // Anthropic 1M-context, flat-priced.
627
- "claude-5-opus": { forceCompactAt: 85e4 },
628
- "claude-4-8-opus": { forceCompactAt: 85e4 },
629
- "claude-4-7-opus": { forceCompactAt: 85e4 },
630
- // Anthropic 1M-context with 2x long-context pricing above 200K input.
631
- "claude-4-6-opus": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
632
- "claude-4-6-sonnet": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
633
- "claude-fable-5": { forceCompactAt: 85e4 },
634
- "claude-fable-5-1": { forceCompactAt: 85e4 },
635
- "claude-5-sonnet": { forceCompactAt: 85e4 },
636
- // OpenAI gpt-5.5/5.6: ~1M window, but the usable input ceiling under
637
- // `truncation: 'auto'` is ~794K (output + reasoning reserve), and all
638
- // rates double above 272K input.
639
- "gpt-5.5": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
640
- "gpt-5.6-sol": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
641
- "gpt-5.6-terra": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
642
- "gpt-5.6-luna": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
643
- "gpt-6-astra": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
644
- // Google ~1M-context; only 3.1-pro is tiered (higher rates above 200K).
645
- "gemini-3-pro": { forceCompactAt: 85e4 },
646
- "gemini-3.1-pro": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
647
- "gemini-3-flash": { forceCompactAt: 85e4 },
648
- "gemini-3.5-flash": { forceCompactAt: 85e4 },
649
- "gemini-3.7-flash": { forceCompactAt: 85e4 },
650
- // 256K window; its 200K pricing tier sits above the gate, so no nudge.
651
- "grok-build-0.1": { forceCompactAt: 18e4 },
652
- "grok-4.5": { forceCompactAt: 4e5 },
653
- // 500K window
654
- "grok-4.6": { forceCompactAt: 4e5 },
655
- // 500K window
656
- "glm-5.2": { forceCompactAt: 85e4 },
657
- "glm-5.3": { forceCompactAt: 85e4 },
658
- "glm-5.3-flash": { forceCompactAt: 85e4 },
659
- "muse-spark-1.1": { forceCompactAt: 85e4 },
660
- "muse-spark-1.2": { forceCompactAt: 85e4 },
661
- "muse-spark-1.3": { forceCompactAt: 85e4 },
662
- "kimi-k2-7-code": { forceCompactAt: 2e5 },
663
- // 262K window
664
- "kimi-k3": { forceCompactAt: 85e4 },
665
- "deepseek-v4-flash-0731": { forceCompactAt: 85e4 },
666
- "deepseek-v4-pro": { forceCompactAt: 85e4 },
667
- "deepseek-v4.1-flash": { forceCompactAt: 85e4 },
668
- "qwen3.8-2.4t-a95b-deepinfra": { forceCompactAt: 2e5 },
669
- // 262K window
670
- "qwen3.8-27b-deepinfra": { forceCompactAt: 2e5 },
671
- // 262K window
672
- "minimax-m3": { forceCompactAt: 42e4 }
673
- // 524K window
674
- };
675
- var DEFAULT_CONTEXT_LIMITS = { forceCompactAt: 85e4 };
629
+ for (const [id, value] of Object.entries(raw)) {
630
+ const force = value?.forceCompactAt;
631
+ if (!id || typeof force !== "number" || !Number.isFinite(force) || force <= 0) {
632
+ continue;
633
+ }
634
+ const suggest = value?.suggestCompactAt;
635
+ out.set(id, {
636
+ forceCompactAt: force,
637
+ ...typeof suggest === "number" && Number.isFinite(suggest) && suggest > 0 ? { suggestCompactAt: suggest } : {}
638
+ });
639
+ }
640
+ return out;
641
+ }
642
+ function requireSurface(surfaceId) {
643
+ const surface = surfaces[surfaceId];
644
+ if (!surface) {
645
+ throw new Error(
646
+ registryLoaded ? `Unknown model surface '${surfaceId}'.` : `Model surfaces were read before the platform registry loaded (surface '${surfaceId}'). setModelRegistry must run during boot.`
647
+ );
648
+ }
649
+ return surface;
650
+ }
651
+ function getAllowedModelsByType() {
652
+ return allowedModelsByType;
653
+ }
676
654
  function getContextLimits(modelId) {
677
- return TEXT_MODELS[modelId] ?? DEFAULT_CONTEXT_LIMITS;
655
+ return textModels.get(modelId) ?? FALLBACK_CONTEXT_LIMITS;
678
656
  }
679
657
  function getSuggestCompactAt(modelId) {
680
658
  return getContextLimits(modelId).suggestCompactAt ?? DEFAULT_SUGGEST_COMPACT_AT;
681
659
  }
682
- var ALLOWED_MODELS_BY_TYPE = {
683
- text: Object.keys(TEXT_MODELS)
684
- // vision: undefined — unconstrained
685
- // image_generation: undefined — unconstrained
686
- };
687
660
  var orgDefaultModels = {};
688
661
  function setOrgDefaultModels(models) {
689
662
  orgDefaultModels = models;
@@ -694,17 +667,17 @@ function filterModelPicks(picks) {
694
667
  return out;
695
668
  }
696
669
  for (const [key, value] of Object.entries(picks)) {
697
- if (!(key in MODEL_SURFACES)) {
670
+ const surface = surfaces[key];
671
+ if (!surface) {
698
672
  continue;
699
673
  }
700
- const surface = MODEL_SURFACES[key];
701
674
  if (!surface.userPickable) {
702
675
  continue;
703
676
  }
704
677
  if (typeof value !== "string" || value.length === 0) {
705
678
  continue;
706
679
  }
707
- const allow = ALLOWED_MODELS_BY_TYPE[surface.modelType];
680
+ const allow = allowedModelsByType[surface.modelType];
708
681
  if (allow && !allow.includes(value)) {
709
682
  continue;
710
683
  }
@@ -714,14 +687,18 @@ function filterModelPicks(picks) {
714
687
  }
715
688
  function getEffectiveModelSurfaces() {
716
689
  const out = {};
717
- for (const [id, surface] of Object.entries(MODEL_SURFACES)) {
690
+ for (const id of SURFACE_IDS) {
691
+ const surface = surfaces[id];
692
+ if (!surface) {
693
+ continue;
694
+ }
718
695
  const orgDefault = orgDefaultModels[id];
719
696
  out[id] = orgDefault ? { ...surface, default: orgDefault } : { ...surface };
720
697
  }
721
698
  return out;
722
699
  }
723
700
  function resolveModel(surfaceId, models, fallback) {
724
- return models?.[surfaceId] ?? fallback ?? orgDefaultModels[surfaceId] ?? MODEL_SURFACES[surfaceId].default;
701
+ return models?.[surfaceId] ?? fallback ?? orgDefaultModels[surfaceId] ?? requireSurface(surfaceId).default;
725
702
  }
726
703
  function resolveParentModel(models, fallback, buildModel) {
727
704
  const override = buildModel ? filterModelPicks({ parent: buildModel }).parent : void 0;
@@ -783,6 +760,18 @@ function renderOrgContextBlock() {
783
760
  return lines.join("\n");
784
761
  }
785
762
 
763
+ // src/models/init.ts
764
+ var log4 = createLogger("models");
765
+ async function initModelRegistry(config) {
766
+ const payload = await fetchModelSurfaces(config);
767
+ setModelRegistry(payload);
768
+ log4.debug("model registry loaded", {
769
+ surfaces: payload.surfaces.length,
770
+ allowedTextModels: payload.allowedModelsByType?.text?.length ?? 0,
771
+ textModels: Object.keys(payload.textModels ?? {}).length
772
+ });
773
+ }
774
+
786
775
  // src/assets.ts
787
776
  import fs3 from "fs";
788
777
  import path2 from "path";
@@ -2943,11 +2932,11 @@ var editsFinishedTool = {
2943
2932
  };
2944
2933
 
2945
2934
  // src/tools/_helpers/sidecar.ts
2946
- var log4 = createLogger("sidecar");
2935
+ var log5 = createLogger("sidecar");
2947
2936
  var baseUrl = null;
2948
2937
  function setSidecarBaseUrl(url) {
2949
2938
  baseUrl = url;
2950
- log4.info("Configured", { url });
2939
+ log5.info("Configured", { url });
2951
2940
  }
2952
2941
  async function sidecarRequest(endpoint, body = {}, options) {
2953
2942
  if (!baseUrl) {
@@ -2963,7 +2952,7 @@ async function sidecarRequest(endpoint, body = {}, options) {
2963
2952
  signal: options?.timeout ? AbortSignal.timeout(options.timeout) : void 0
2964
2953
  });
2965
2954
  if (!res.ok) {
2966
- log4.error("Sidecar error", { endpoint, status: res.status });
2955
+ log5.error("Sidecar error", { endpoint, status: res.status });
2967
2956
  throw new Error(`Sidecar error: ${res.status}`);
2968
2957
  }
2969
2958
  data = await res.json();
@@ -2971,12 +2960,12 @@ async function sidecarRequest(endpoint, body = {}, options) {
2971
2960
  if (err.message.startsWith("Sidecar error")) {
2972
2961
  throw err;
2973
2962
  }
2974
- log4.error("Sidecar connection error", { endpoint, error: err.message });
2963
+ log5.error("Sidecar connection error", { endpoint, error: err.message });
2975
2964
  throw new Error(`Sidecar connection error: ${err.message}`);
2976
2965
  }
2977
2966
  if (data?.success === false) {
2978
2967
  const code = data.errorCode ? ` [${data.errorCode}]` : "";
2979
- log4.error("Sidecar command failed", {
2968
+ log5.error("Sidecar command failed", {
2980
2969
  endpoint,
2981
2970
  error: data.error,
2982
2971
  errorCode: data.errorCode
@@ -3300,7 +3289,7 @@ function stripDollarKeys(envelope) {
3300
3289
  // src/tools/_helpers/uploadImage.ts
3301
3290
  import { readFile, stat } from "fs/promises";
3302
3291
  import { basename, extname, resolve } from "path";
3303
- var log5 = createLogger("uploadImage");
3292
+ var log6 = createLogger("uploadImage");
3304
3293
  var UPLOAD_TIMEOUT_MS = 6e4;
3305
3294
  var CONTENT_TYPES = {
3306
3295
  ".png": "image/png",
@@ -3369,7 +3358,7 @@ async function uploadLocalImage(localPath, apiConfig) {
3369
3358
  if (!res.ok) {
3370
3359
  throw new Error(`Upload of "${localPath}" failed: HTTP ${res.status}`);
3371
3360
  }
3372
- log5.info("Local image hosted", {
3361
+ log6.info("Local image hosted", {
3373
3362
  path: localPath,
3374
3363
  bytes: stats.size,
3375
3364
  url: target.publicUrl
@@ -3568,7 +3557,7 @@ function acquireBrowserLock() {
3568
3557
  }
3569
3558
 
3570
3559
  // src/toolRegistry.ts
3571
- var log6 = createLogger("tool-registry");
3560
+ var log7 = createLogger("tool-registry");
3572
3561
  var USER_CANCELLED_RESULT = "[USER CANCELLED] The user manually cancelled this tool. Do not retry it automatically \u2014 wait for the user\u2019s next message for direction.";
3573
3562
  var ENV_INTERRUPTED_RESULT = "[INTERRUPTED] The environment shut down while this tool was running. No user action was involved \u2014 pick up from here.";
3574
3563
  function cancelledToolResult(signal) {
@@ -3599,7 +3588,7 @@ var ToolRegistry = class {
3599
3588
  if (!entry) {
3600
3589
  return false;
3601
3590
  }
3602
- log6.info("Tool stopped", { toolCallId: id, name: entry.name, mode });
3591
+ log7.info("Tool stopped", { toolCallId: id, name: entry.name, mode });
3603
3592
  entry.abortController.abort(mode);
3604
3593
  if (mode === "graceful") {
3605
3594
  const partial = entry.getPartialResult?.() ?? "";
@@ -3632,7 +3621,7 @@ ${partial}` : "[INTERRUPTED] Tool execution was stopped.";
3632
3621
  if (!entry) {
3633
3622
  return false;
3634
3623
  }
3635
- log6.info("Tool restarted", { toolCallId: id, name: entry.name });
3624
+ log7.info("Tool restarted", { toolCallId: id, name: entry.name });
3636
3625
  entry.abortController.abort("restart");
3637
3626
  const newInput = patchedInput ? { ...entry.input, ...patchedInput } : entry.input;
3638
3627
  this.onEvent?.({
@@ -4058,7 +4047,7 @@ ${content}` : attachmentHeader;
4058
4047
  }
4059
4048
 
4060
4049
  // src/subagents/runner.ts
4061
- var log7 = createLogger("sub-agent");
4050
+ var log8 = createLogger("sub-agent");
4062
4051
  async function runSubAgent(config) {
4063
4052
  const {
4064
4053
  system,
@@ -4088,7 +4077,7 @@ async function runSubAgent(config) {
4088
4077
  const signal = background ? bgAbort.signal : parentSignal;
4089
4078
  const agentName = subAgentId || "sub-agent";
4090
4079
  const runStart = Date.now();
4091
- log7.info("Sub-agent started", { requestId, parentToolId, agentName });
4080
+ log8.info("Sub-agent started", { requestId, parentToolId, agentName });
4092
4081
  const emit = (e) => {
4093
4082
  onEvent({ ...e, parentToolId });
4094
4083
  };
@@ -4311,7 +4300,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4311
4300
  try {
4312
4301
  objection = await validateResult(text, thisInvocation());
4313
4302
  } catch (err) {
4314
- log7.warn("Result validator failed, accepting response", {
4303
+ log8.warn("Result validator failed, accepting response", {
4315
4304
  requestId,
4316
4305
  parentToolId,
4317
4306
  agentName,
@@ -4320,7 +4309,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4320
4309
  }
4321
4310
  if (objection && !validationRetried && !signal?.aborted) {
4322
4311
  validationRetried = true;
4323
- log7.info("Result rejected by validator, retrying once", {
4312
+ log8.info("Result rejected by validator, retrying once", {
4324
4313
  requestId,
4325
4314
  parentToolId,
4326
4315
  agentName,
@@ -4347,7 +4336,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4347
4336
  ...hasArtifacts ? { artifacts } : {}
4348
4337
  };
4349
4338
  }
4350
- log7.info("Tools executing", {
4339
+ log8.info("Tools executing", {
4351
4340
  requestId,
4352
4341
  parentToolId,
4353
4342
  count: toolCalls.length,
@@ -4443,7 +4432,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4443
4432
  run2(tc.input);
4444
4433
  const r = await resultPromise;
4445
4434
  toolRegistry?.unregister(tc.id);
4446
- log7.info("Tool completed", {
4435
+ log8.info("Tool completed", {
4447
4436
  requestId,
4448
4437
  parentToolId,
4449
4438
  toolCallId: tc.id,
@@ -4500,7 +4489,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4500
4489
  const wrapRun = async () => {
4501
4490
  try {
4502
4491
  const result = await run();
4503
- log7.info("Sub-agent complete", {
4492
+ log8.info("Sub-agent complete", {
4504
4493
  requestId,
4505
4494
  parentToolId,
4506
4495
  agentName,
@@ -4509,7 +4498,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4509
4498
  });
4510
4499
  return result;
4511
4500
  } catch (err) {
4512
- log7.warn("Sub-agent error", {
4501
+ log8.warn("Sub-agent error", {
4513
4502
  requestId,
4514
4503
  parentToolId,
4515
4504
  agentName,
@@ -4521,7 +4510,7 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4521
4510
  if (!background) {
4522
4511
  return wrapRun();
4523
4512
  }
4524
- log7.info("Sub-agent backgrounded", { requestId, parentToolId, agentName });
4513
+ log8.info("Sub-agent backgrounded", { requestId, parentToolId, agentName });
4525
4514
  toolRegistry?.register({
4526
4515
  id: parentToolId,
4527
4516
  name: agentName,
@@ -4898,7 +4887,7 @@ function getBrowserAutomationPrompt() {
4898
4887
  }
4899
4888
 
4900
4889
  // src/subagents/browserAutomation/index.ts
4901
- var log8 = createLogger("browser-automation");
4890
+ var log9 = createLogger("browser-automation");
4902
4891
  var CAPTURE_COMMANDS = /* @__PURE__ */ new Set(["screenshotViewport", "screenshotFullPage"]);
4903
4892
  async function runBrowserAutomation(task, context, opts) {
4904
4893
  const release = await acquireBrowserLock();
@@ -5005,7 +4994,7 @@ async function runBrowserAutomation(task, context, opts) {
5005
4994
  }
5006
4995
  });
5007
4996
  } catch {
5008
- log8.debug("Failed to parse batch analysis result", {
4997
+ log9.debug("Failed to parse batch analysis result", {
5009
4998
  batchResult
5010
4999
  });
5011
5000
  }
@@ -6267,7 +6256,7 @@ __export(createWireframe_exports, {
6267
6256
  });
6268
6257
  import { mkdir as mkdir2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
6269
6258
  import { join as join3 } from "path";
6270
- var log9 = createLogger("createWireframe");
6259
+ var log10 = createLogger("createWireframe");
6271
6260
  var WIREFRAMES_DIR = "src/.wireframes";
6272
6261
  var UPLOAD_TIMEOUT_MS2 = 3e4;
6273
6262
  var definition9 = {
@@ -6394,7 +6383,7 @@ async function execute9(input, onLog, context) {
6394
6383
  onLog?.(`Wrote ${relPath}, mirroring for preview...`);
6395
6384
  const mirror = await uploadMirror(context, slug, content);
6396
6385
  if (!mirror.ok) {
6397
- log9.warn("Wireframe mirror upload failed", { slug, note: mirror.note });
6386
+ log10.warn("Wireframe mirror upload failed", { slug, note: mirror.note });
6398
6387
  }
6399
6388
  const lines = [
6400
6389
  `${existed ? "Revised" : "Created"} wireframe "${singleLine(name)}" at ${relPath}.${existed ? " Existing references to this path now show the new version." : ""}`,
@@ -7664,7 +7653,7 @@ function executeTool(name, input, context) {
7664
7653
  }
7665
7654
 
7666
7655
  // src/compaction/index.ts
7667
- var log10 = createLogger("compaction");
7656
+ var log11 = createLogger("compaction");
7668
7657
  var CONVERSATION_SUMMARY_PROMPT = readAsset("compaction", "conversation.md");
7669
7658
  var SUBAGENT_SUMMARY_PROMPT = readAsset("compaction", "subagent.md");
7670
7659
  var SUMMARIZABLE_SUBAGENTS = ["visualDesignExpert", "productVision"];
@@ -7715,7 +7704,7 @@ async function compactConversation(messages, apiConfig, model, signal) {
7715
7704
  if (text) {
7716
7705
  summaries.push({ name, text });
7717
7706
  } else {
7718
- log10.warn("Subagent summary unusable \u2014 leaving its history intact", {
7707
+ log11.warn("Subagent summary unusable \u2014 leaving its history intact", {
7719
7708
  name
7720
7709
  });
7721
7710
  }
@@ -7743,7 +7732,7 @@ async function compactConversation(messages, apiConfig, model, signal) {
7743
7732
  }
7744
7733
  ]
7745
7734
  }));
7746
- log10.info("Compaction complete", {
7735
+ log11.info("Compaction complete", {
7747
7736
  summaries: summaries.length,
7748
7737
  recentNarrativeChars: recent.length
7749
7738
  });
@@ -7952,7 +7941,7 @@ async function generateSummary(apiConfig, name, compactionPrompt, messagesToSumm
7952
7941
  messagesToSummarize.slice(0, mid),
7953
7942
  messagesToSummarize.slice(mid)
7954
7943
  ];
7955
- log10.info("Chunking summary", {
7944
+ log11.info("Chunking summary", {
7956
7945
  name,
7957
7946
  messageCount: messagesToSummarize.length,
7958
7947
  serializedLength: serialized.length,
@@ -7985,7 +7974,7 @@ async function generateSummary(apiConfig, name, compactionPrompt, messagesToSumm
7985
7974
  const parts = results.filter((p) => p !== null);
7986
7975
  return parts.length > 0 ? parts.join("\n\n---\n\n") : null;
7987
7976
  }
7988
- log10.info("Generating summary", {
7977
+ log11.info("Generating summary", {
7989
7978
  name,
7990
7979
  messageCount: messagesToSummarize.length,
7991
7980
  serializedLength: serialized.length
@@ -8002,10 +7991,10 @@ async function generateSummary(apiConfig, name, compactionPrompt, messagesToSumm
8002
7991
  return null;
8003
7992
  }
8004
7993
  if (summaryText.length >= MIN_SUMMARY_CHARS) {
8005
- log10.info("Summary generated", { name, summaryLength: summaryText.length });
7994
+ log11.info("Summary generated", { name, summaryLength: summaryText.length });
8006
7995
  return summaryText;
8007
7996
  }
8008
- log10.warn("Summary too short to be real", {
7997
+ log11.warn("Summary too short to be real", {
8009
7998
  name,
8010
7999
  summaryLength: summaryText.length,
8011
8000
  minimum: MIN_SUMMARY_CHARS,
@@ -8066,15 +8055,15 @@ Write the summary of the conversation above, following your instructions.`;
8066
8055
  toolNames: []
8067
8056
  });
8068
8057
  } else if (event.type === "error") {
8069
- log10.error("Summary generation failed", { name, error: event.error });
8058
+ log11.error("Summary generation failed", { name, error: event.error });
8070
8059
  return null;
8071
8060
  }
8072
8061
  }
8073
8062
  if (!summaryText.trim()) {
8074
8063
  if (signal?.aborted) {
8075
- log10.info("Summary cancelled", { name });
8064
+ log11.info("Summary cancelled", { name });
8076
8065
  } else {
8077
- log10.warn("Empty summary generated", { name });
8066
+ log11.warn("Empty summary generated", { name });
8078
8067
  }
8079
8068
  return null;
8080
8069
  }
@@ -8084,7 +8073,7 @@ Write the summary of the conversation above, following your instructions.`;
8084
8073
  // src/session.ts
8085
8074
  import fs21 from "fs";
8086
8075
  import path11 from "path";
8087
- var log11 = createLogger("session");
8076
+ var log12 = createLogger("session");
8088
8077
  var SESSION_FILE = ".remy-session.json";
8089
8078
  var ARCHIVE_DIR = ".logs/sessions";
8090
8079
  var ARCHIVE_NAME_RE = /^(cleared|rotated)-.*\.json$/;
@@ -8103,7 +8092,7 @@ function loadSession(state) {
8103
8092
  }
8104
8093
  if (Array.isArray(data.messages) && data.messages.length > 0) {
8105
8094
  state.messages = sanitizeMessages(data.messages);
8106
- log11.info("Session loaded", {
8095
+ log12.info("Session loaded", {
8107
8096
  messageCount: state.messages.length,
8108
8097
  ...state.models && { models: state.models }
8109
8098
  });
@@ -8113,7 +8102,7 @@ function loadSession(state) {
8113
8102
  try {
8114
8103
  const quarantine = `${SESSION_FILE}.corrupt-${Date.now()}`;
8115
8104
  fs21.renameSync(SESSION_FILE, quarantine);
8116
- log11.warn(`Session file unreadable \u2014 quarantined to ${quarantine}`);
8105
+ log12.warn(`Session file unreadable \u2014 quarantined to ${quarantine}`);
8117
8106
  } catch {
8118
8107
  }
8119
8108
  }
@@ -8178,7 +8167,7 @@ function archiveMessages(messages, label, models) {
8178
8167
  }
8179
8168
  writeFileAtomicSync(dest, JSON.stringify(payload));
8180
8169
  archiveCountCache.set(path11.basename(dest), count);
8181
- log11.info("Session archived", { label, dest, messageCount: count });
8170
+ log12.info("Session archived", { label, dest, messageCount: count });
8182
8171
  pruneArchives();
8183
8172
  return dest;
8184
8173
  }
@@ -8215,7 +8204,7 @@ function pruneArchives() {
8215
8204
  }
8216
8205
  }
8217
8206
  if (removed > 0) {
8218
- log11.info("Session archives pruned", {
8207
+ log12.info("Session archives pruned", {
8219
8208
  removed,
8220
8209
  freedBytes: freed,
8221
8210
  keptBytes: kept
@@ -8249,7 +8238,7 @@ function parseArchive(name) {
8249
8238
  }
8250
8239
  return messages;
8251
8240
  } catch (err) {
8252
- log11.warn("Session archive unreadable", { name, error: err?.message });
8241
+ log12.warn("Session archive unreadable", { name, error: err?.message });
8253
8242
  return null;
8254
8243
  }
8255
8244
  }
@@ -8364,7 +8353,7 @@ function getHistoryPage(state, opts) {
8364
8353
  }
8365
8354
  messages.splice(0, cut);
8366
8355
  startIndex += cut;
8367
- log11.info("History page trimmed to byte budget", {
8356
+ log12.info("History page trimmed to byte budget", {
8368
8357
  dropped: cut,
8369
8358
  kept: messages.length,
8370
8359
  startIndex
@@ -8395,7 +8384,7 @@ function rotate(state) {
8395
8384
  }
8396
8385
  archiveMessages(messages.slice(0, cut), "rotated", state.models);
8397
8386
  state.messages = messages.slice(cut);
8398
- log11.info("Session rotated", {
8387
+ log12.info("Session rotated", {
8399
8388
  archived: cut,
8400
8389
  retained: state.messages.length
8401
8390
  });
@@ -8408,9 +8397,9 @@ function saveSession(state) {
8408
8397
  serialized = JSON.stringify(buildPayload(state));
8409
8398
  }
8410
8399
  writeFileAtomicSync(SESSION_FILE, serialized);
8411
- log11.info("Session saved", { messageCount: state.messages.length });
8400
+ log12.info("Session saved", { messageCount: state.messages.length });
8412
8401
  } catch (err) {
8413
- log11.warn("Session save failed", { error: err.message });
8402
+ log12.warn("Session save failed", { error: err.message });
8414
8403
  }
8415
8404
  }
8416
8405
  function clearSession(state) {
@@ -8419,14 +8408,14 @@ function clearSession(state) {
8419
8408
  archiveMessages(state.messages, "cleared", state.models);
8420
8409
  }
8421
8410
  } catch (err) {
8422
- log11.warn("Session archive on clear failed", { error: err.message });
8411
+ log12.warn("Session archive on clear failed", { error: err.message });
8423
8412
  }
8424
8413
  state.messages = [];
8425
8414
  saveSession(state);
8426
8415
  }
8427
8416
 
8428
8417
  // src/compaction/trigger.ts
8429
- var log12 = createLogger("compaction:trigger");
8418
+ var log13 = createLogger("compaction:trigger");
8430
8419
  var CompactionCancelledError = class extends Error {
8431
8420
  constructor() {
8432
8421
  super("Compaction cancelled \u2014 no checkpoint was created.");
@@ -8443,7 +8432,7 @@ function cancelInflightCompaction() {
8443
8432
  if (!inflightCompaction || !inflightAbort) {
8444
8433
  return false;
8445
8434
  }
8446
- log12.info("Cancelling in-flight compaction");
8435
+ log13.info("Cancelling in-flight compaction");
8447
8436
  inflightAbort.abort();
8448
8437
  return true;
8449
8438
  }
@@ -8480,7 +8469,7 @@ function applyPendingSummaries(state) {
8480
8469
  idx = at === -1 ? 0 : at + 1;
8481
8470
  }
8482
8471
  state.messages.splice(idx, 0, ...drained.checkpoints);
8483
- log12.info("Checkpoint applied", {
8472
+ log13.info("Checkpoint applied", {
8484
8473
  index: idx,
8485
8474
  messageCount: state.messages.length
8486
8475
  });
@@ -8514,7 +8503,7 @@ function triggerCompaction(state, apiConfig, opts = {}) {
8514
8503
  return inflightCompaction;
8515
8504
  }
8516
8505
  if (pending) {
8517
- log12.info("Compaction skipped \u2014 a checkpoint is already waiting to apply");
8506
+ log13.info("Compaction skipped \u2014 a checkpoint is already waiting to apply");
8518
8507
  return Promise.resolve(null);
8519
8508
  }
8520
8509
  const { blocking = false, requestId, model, origin, toolCallId } = opts;
@@ -8534,7 +8523,7 @@ function triggerCompaction(state, apiConfig, opts = {}) {
8534
8523
  requestId,
8535
8524
  ...summaries.length > 0 && { summaries }
8536
8525
  });
8537
- log12.info("Compaction complete");
8526
+ log13.info("Compaction complete");
8538
8527
  return summaries;
8539
8528
  }).catch((err) => {
8540
8529
  const cancelled = abort.signal.aborted;
@@ -8546,9 +8535,9 @@ function triggerCompaction(state, apiConfig, opts = {}) {
8546
8535
  requestId
8547
8536
  });
8548
8537
  if (cancelled) {
8549
- log12.info("Compaction cancelled");
8538
+ log13.info("Compaction cancelled");
8550
8539
  } else {
8551
- log12.error("Compaction failed", { error: message });
8540
+ log13.error("Compaction failed", { error: message });
8552
8541
  }
8553
8542
  throw cancelled ? new CompactionCancelledError() : err;
8554
8543
  }).finally(() => {
@@ -8562,7 +8551,7 @@ function triggerCompaction(state, apiConfig, opts = {}) {
8562
8551
  import fs22 from "fs";
8563
8552
  import path12 from "path";
8564
8553
  import { createHash } from "crypto";
8565
- var log13 = createLogger("brandExtraction");
8554
+ var log14 = createLogger("brandExtraction");
8566
8555
  var EXTRACT_PROMPT = readAsset("brandExtraction", "extract.md");
8567
8556
  var BRAND_FILE = ".remy-brand.json";
8568
8557
  var CACHE_FILE = ".remy-brand.cache.json";
@@ -8570,17 +8559,17 @@ async function runExtraction(apiConfig, model) {
8570
8559
  const inputHash = computeInputHash();
8571
8560
  const cached3 = readCache();
8572
8561
  if (cached3 && cached3.inputHash === inputHash) {
8573
- log13.debug("Brand inputs unchanged \u2014 skipping extraction", { inputHash });
8562
+ log14.debug("Brand inputs unchanged \u2014 skipping extraction", { inputHash });
8574
8563
  return null;
8575
8564
  }
8576
- log13.info("Extracting brand", { inputHash });
8565
+ log14.info("Extracting brand", { inputHash });
8577
8566
  const brand = await extractBrand(apiConfig, model);
8578
8567
  if (!brand) {
8579
- log13.warn("Brand extraction failed \u2014 leaving cache untouched");
8568
+ log14.warn("Brand extraction failed \u2014 leaving cache untouched");
8580
8569
  return null;
8581
8570
  }
8582
8571
  persistBrand(brand, inputHash);
8583
- log13.info("Brand persisted", { inputHash });
8572
+ log14.info("Brand persisted", { inputHash });
8584
8573
  return brand;
8585
8574
  }
8586
8575
  function isDedicatedBrandFile(filePath) {
@@ -8668,7 +8657,7 @@ function parseFrontmatter3(filePath) {
8668
8657
  async function extractBrand(apiConfig, model) {
8669
8658
  const corpus = buildCorpus();
8670
8659
  if (!corpus.trim()) {
8671
- log13.debug("No spec corpus \u2014 emitting empty brand");
8660
+ log14.debug("No spec corpus \u2014 emitting empty brand");
8672
8661
  return { version: 1 };
8673
8662
  }
8674
8663
  let responseText = "";
@@ -8702,17 +8691,17 @@ async function extractBrand(apiConfig, model) {
8702
8691
  toolNames: []
8703
8692
  });
8704
8693
  } else if (event.type === "error") {
8705
- log13.error("Brand extraction stream error", { error: event.error });
8694
+ log14.error("Brand extraction stream error", { error: event.error });
8706
8695
  return null;
8707
8696
  }
8708
8697
  }
8709
8698
  } catch (err) {
8710
- log13.error("Brand extraction threw", { error: err?.message });
8699
+ log14.error("Brand extraction threw", { error: err?.message });
8711
8700
  return null;
8712
8701
  }
8713
8702
  const parsed = parseJsonResponse(responseText);
8714
8703
  if (!parsed) {
8715
- log13.warn("Brand extraction returned unparseable JSON", {
8704
+ log14.warn("Brand extraction returned unparseable JSON", {
8716
8705
  preview: responseText.slice(0, 200)
8717
8706
  });
8718
8707
  return null;
@@ -8883,7 +8872,7 @@ function readCache() {
8883
8872
  }
8884
8873
 
8885
8874
  // src/brandExtraction/trigger.ts
8886
- var log14 = createLogger("brandExtraction:trigger");
8875
+ var log15 = createLogger("brandExtraction:trigger");
8887
8876
  var inflight = false;
8888
8877
  var dirty = false;
8889
8878
  function triggerBrandExtraction(apiConfig, model) {
@@ -8893,7 +8882,7 @@ function triggerBrandExtraction(apiConfig, model) {
8893
8882
  }
8894
8883
  inflight = true;
8895
8884
  void runExtraction(apiConfig, model).catch((err) => {
8896
- log14.error("Brand extraction failed", { error: err?.message });
8885
+ log15.error("Brand extraction failed", { error: err?.message });
8897
8886
  }).finally(() => {
8898
8887
  inflight = false;
8899
8888
  if (dirty) {
@@ -9332,7 +9321,7 @@ function annotateSuggestions(blocks) {
9332
9321
  }
9333
9322
 
9334
9323
  // src/agent.ts
9335
- var log15 = createLogger("agent");
9324
+ var log16 = createLogger("agent");
9336
9325
  var BRAND_TRIGGERING_TOOLS = /* @__PURE__ */ new Set([
9337
9326
  "writeSpec",
9338
9327
  "editSpec",
@@ -9399,7 +9388,7 @@ async function runTurn(params) {
9399
9388
  (n, e) => n + (e.attachments?.length ?? 0),
9400
9389
  0
9401
9390
  );
9402
- log15.info("Turn started", {
9391
+ log16.info("Turn started", {
9403
9392
  requestId,
9404
9393
  model,
9405
9394
  buildModel: modelOverride ? parentModel : void 0,
@@ -9474,7 +9463,7 @@ async function runTurn(params) {
9474
9463
  const MAX_MID_TURN_COMPACTIONS = 2;
9475
9464
  let overflowRecovered = false;
9476
9465
  const compactNow = async (reason) => {
9477
- log15.warn("Compacting mid-turn", {
9466
+ log16.warn("Compacting mid-turn", {
9478
9467
  requestId,
9479
9468
  reason,
9480
9469
  lastCallInputTokens
@@ -9489,7 +9478,7 @@ async function runTurn(params) {
9489
9478
  });
9490
9479
  applyPendingSummaries(state);
9491
9480
  } catch (err) {
9492
- log15.error("Mid-turn compaction failed", {
9481
+ log16.error("Mid-turn compaction failed", {
9493
9482
  requestId,
9494
9483
  error: err?.message ?? String(err)
9495
9484
  });
@@ -9755,7 +9744,7 @@ async function runTurn(params) {
9755
9744
  const acc = toolInputAccumulators.get(event.id);
9756
9745
  const wasStreamed = acc?.started ?? false;
9757
9746
  const isInputStreaming = !!tool?.streaming?.partialInput;
9758
- log15.info("Tool received", {
9747
+ log16.info("Tool received", {
9759
9748
  requestId,
9760
9749
  toolCallId: event.id,
9761
9750
  name: event.name
@@ -9865,7 +9854,7 @@ async function runTurn(params) {
9865
9854
  if (code === "repetition_loop") {
9866
9855
  if (recoveries < MAX_RECOVERIES && !signal?.aborted) {
9867
9856
  recoveries++;
9868
- log15.warn("Repetition loop \u2014 nudging model to continue", {
9857
+ log16.warn("Repetition loop \u2014 nudging model to continue", {
9869
9858
  requestId,
9870
9859
  attempt: recoveries
9871
9860
  });
@@ -9880,7 +9869,7 @@ async function runTurn(params) {
9880
9869
  }
9881
9870
  statusWatcher.stop();
9882
9871
  saveSession(state);
9883
- log15.warn("Repetition loop over recovery cap \u2014 ending turn", {
9872
+ log16.warn("Repetition loop over recovery cap \u2014 ending turn", {
9884
9873
  requestId
9885
9874
  });
9886
9875
  onEvent({
@@ -9926,7 +9915,7 @@ async function runTurn(params) {
9926
9915
  const toolCalls = getToolCalls(contentBlocks);
9927
9916
  if (toolCalls.length === 0 && (stopReason === "repetition" || stopReason === "max_tokens") && recoveries < MAX_RECOVERIES && !signal?.aborted) {
9928
9917
  recoveries++;
9929
- log15.warn("Abnormal stop \u2014 nudging model to continue", {
9918
+ log16.warn("Abnormal stop \u2014 nudging model to continue", {
9930
9919
  requestId,
9931
9920
  stopReason,
9932
9921
  attempt: recoveries
@@ -9954,7 +9943,7 @@ async function runTurn(params) {
9954
9943
  });
9955
9944
  return;
9956
9945
  }
9957
- log15.info("Tools executing", {
9946
+ log16.info("Tools executing", {
9958
9947
  requestId,
9959
9948
  count: toolCalls.length,
9960
9949
  tools: toolCalls.map((tc) => tc.name)
@@ -10005,7 +9994,7 @@ async function runTurn(params) {
10005
9994
  let result;
10006
9995
  if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
10007
9996
  saveSession(state);
10008
- log15.info("Waiting for external tool result", {
9997
+ log16.info("Waiting for external tool result", {
10009
9998
  requestId,
10010
9999
  toolCallId: tc.id,
10011
10000
  name: tc.name
@@ -10073,7 +10062,7 @@ async function runTurn(params) {
10073
10062
  if (!isBackgroundCall(tc)) {
10074
10063
  toolRegistry?.unregister(tc.id);
10075
10064
  }
10076
- log15.info("Tool completed", {
10065
+ log16.info("Tool completed", {
10077
10066
  requestId,
10078
10067
  toolCallId: tc.id,
10079
10068
  name: tc.name,
@@ -10159,7 +10148,7 @@ import { writeFile as writeFile3, stat as stat5 } from "fs/promises";
10159
10148
  import { Readable } from "stream";
10160
10149
  import { pipeline } from "stream/promises";
10161
10150
  import { basename as basename2, join as join6, extname as extname3 } from "path";
10162
- var log16 = createLogger("headless:attachments");
10151
+ var log17 = createLogger("headless:attachments");
10163
10152
  var UPLOADS_DIR = "src/.user-uploads";
10164
10153
  var IMAGE_DOWNLOAD_TIMEOUT_MS = 3e4;
10165
10154
  var DOCUMENT_DOWNLOAD_TIMEOUT_MS = 3e5;
@@ -10221,7 +10210,7 @@ async function persistAttachmentList(attachments) {
10221
10210
  createWriteStream(localPath)
10222
10211
  );
10223
10212
  const { size } = await stat5(localPath);
10224
- log16.info("Attachment saved", {
10213
+ log17.info("Attachment saved", {
10225
10214
  filename: name,
10226
10215
  path: localPath,
10227
10216
  bytes: size
@@ -10235,7 +10224,7 @@ async function persistAttachmentList(attachments) {
10235
10224
  if (textRes.ok) {
10236
10225
  extractedTextPath = `${localPath}.txt`;
10237
10226
  await writeFile3(extractedTextPath, await textRes.text(), "utf-8");
10238
- log16.info("Extracted text saved", { path: extractedTextPath });
10227
+ log17.info("Extracted text saved", { path: extractedTextPath });
10239
10228
  }
10240
10229
  } catch {
10241
10230
  }
@@ -10377,7 +10366,7 @@ function writeStats(stats, queue, passiveResults, suggestCompactAt, workspaceNot
10377
10366
 
10378
10367
  // src/git/upstreamStatus.ts
10379
10368
  import { execFile as execFile2 } from "child_process";
10380
- var log17 = createLogger("upstream");
10369
+ var log18 = createLogger("upstream");
10381
10370
  var DEFAULT_BRANCH = "main";
10382
10371
  var MAX_INCOMING = 20;
10383
10372
  var FETCH_TIMEOUT_MS = 3e4;
@@ -10424,7 +10413,7 @@ async function readUpstreamStatus() {
10424
10413
  FETCH_TIMEOUT_MS
10425
10414
  );
10426
10415
  if (!fetched.ok) {
10427
- log17.info(
10416
+ log18.info(
10428
10417
  `fetch failed, falling back to the origin/${DEFAULT_BRANCH} on disk: ${fetched.error}`
10429
10418
  );
10430
10419
  }
@@ -10477,13 +10466,13 @@ async function readUpstreamStatus() {
10477
10466
  if (contained.ok) {
10478
10467
  return null;
10479
10468
  }
10480
- log17.info(
10469
+ log18.info(
10481
10470
  `behind by an unknown amount: rev-list and log both failed (${counts.error})`
10482
10471
  );
10483
10472
  }
10484
10473
  const status = await git(["status", "--porcelain"]);
10485
10474
  if (!status.ok) {
10486
- log17.info(`could not read the working tree state: ${status.error}`);
10475
+ log18.info(`could not read the working tree state: ${status.error}`);
10487
10476
  }
10488
10477
  return {
10489
10478
  upstream,
@@ -10701,7 +10690,7 @@ var MessageQueue = class {
10701
10690
  };
10702
10691
 
10703
10692
  // src/headless/index.ts
10704
- var log18 = createLogger("headless");
10693
+ var log19 = createLogger("headless");
10705
10694
  var EXTERNAL_TOOL_TIMEOUT_MS = 3e5;
10706
10695
  var LONG_RUNNING_TOOLS = /* @__PURE__ */ new Set(["runMethod", "testJewel"]);
10707
10696
  var LONG_RUNNING_TOOL_TIMEOUT_MS = 18e5;
@@ -10810,6 +10799,7 @@ var HeadlessSession = class {
10810
10799
  apiKey: this.opts.apiKey,
10811
10800
  baseUrl: this.opts.baseUrl
10812
10801
  });
10802
+ await initModelRegistry(this.config);
10813
10803
  await initOrgContext(this.config);
10814
10804
  const resumed = loadSession(this.state);
10815
10805
  this.queue = new MessageQueue(
@@ -10828,7 +10818,7 @@ var HeadlessSession = class {
10828
10818
  messageCount: this.state.messages.length,
10829
10819
  ...this.state.models && { models: this.state.models },
10830
10820
  modelSurfaces: getEffectiveModelSurfaces(),
10831
- allowedModelsByType: ALLOWED_MODELS_BY_TYPE
10821
+ allowedModelsByType: getAllowedModelsByType()
10832
10822
  });
10833
10823
  }
10834
10824
  triggerBrandExtraction(
@@ -10945,7 +10935,7 @@ var HeadlessSession = class {
10945
10935
  try {
10946
10936
  this.handleCancel("shutdown");
10947
10937
  } catch (err) {
10948
- log18.warn("Shutdown cancel failed", { error: err?.message });
10938
+ log19.warn("Shutdown cancel failed", { error: err?.message });
10949
10939
  }
10950
10940
  this.emit("stopping");
10951
10941
  this.emit("stopped");
@@ -10961,7 +10951,7 @@ var HeadlessSession = class {
10961
10951
  }
10962
10952
  const line = JSON.stringify(payload) + "\n";
10963
10953
  if (event === "history") {
10964
- log18.info("Wrote history event to stdout", {
10954
+ log19.info("Wrote history event to stdout", {
10965
10955
  requestId,
10966
10956
  bytes: line.length
10967
10957
  });
@@ -11035,7 +11025,7 @@ var HeadlessSession = class {
11035
11025
  lastNotedUpstream: status.upstream
11036
11026
  };
11037
11027
  this.persistStats();
11038
- log18.info("workspace behind upstream; note parked for the next turn", {
11028
+ log19.info("workspace behind upstream; note parked for the next turn", {
11039
11029
  // The upstream sha is the dedupe key, so it is what makes "why did I
11040
11030
  // not get a note" answerable from the log alone.
11041
11031
  upstream: status.upstream,
@@ -11044,7 +11034,7 @@ var HeadlessSession = class {
11044
11034
  dirty: status.dirty
11045
11035
  });
11046
11036
  } catch (err) {
11047
- log18.info(`upstream check failed: ${String(err)}`);
11037
+ log19.info(`upstream check failed: ${String(err)}`);
11048
11038
  }
11049
11039
  }
11050
11040
  //////////////////////////////////////////////////////////////////////////////
@@ -11093,7 +11083,7 @@ var HeadlessSession = class {
11093
11083
  if (this.sessionStats.lastContextSize <= threshold) {
11094
11084
  return;
11095
11085
  }
11096
- log18.info("Forced compaction gate triggered", {
11086
+ log19.info("Forced compaction gate triggered", {
11097
11087
  contextSize: this.sessionStats.lastContextSize,
11098
11088
  threshold,
11099
11089
  model: parentModel,
@@ -11113,7 +11103,7 @@ var HeadlessSession = class {
11113
11103
  onBackgroundComplete = (toolCallId, name, result, subAgentMessages) => {
11114
11104
  const notify = getToolByName(name)?.backgroundNotify ?? "wake";
11115
11105
  this.pendingBlockUpdates.push({ toolCallId, result, subAgentMessages });
11116
- log18.info("Background complete", {
11106
+ log19.info("Background complete", {
11117
11107
  toolCallId,
11118
11108
  name,
11119
11109
  notify,
@@ -11395,7 +11385,7 @@ var HeadlessSession = class {
11395
11385
  const { documents, images } = await persistAttachments(attachments);
11396
11386
  return buildUploadHeader(documents, images) || void 0;
11397
11387
  } catch (err) {
11398
- log18.warn("Attachment persistence failed", { error: err.message });
11388
+ log19.warn("Attachment persistence failed", { error: err.message });
11399
11389
  return void 0;
11400
11390
  }
11401
11391
  }
@@ -11455,7 +11445,7 @@ var HeadlessSession = class {
11455
11445
  }
11456
11446
  if (batch.length === 0) {
11457
11447
  if (landings > 0) {
11458
- log18.info("promptUser store landings passed through", { landings });
11448
+ log19.info("promptUser store landings passed through", { landings });
11459
11449
  }
11460
11450
  return raw;
11461
11451
  }
@@ -11463,7 +11453,7 @@ var HeadlessSession = class {
11463
11453
  try {
11464
11454
  results = await persistAttachmentList(batch);
11465
11455
  } catch (err) {
11466
- log18.warn("promptUser upload persistence failed", {
11456
+ log19.warn("promptUser upload persistence failed", {
11467
11457
  error: err.message
11468
11458
  });
11469
11459
  results = batch.map(() => null);
@@ -11475,7 +11465,7 @@ var HeadlessSession = class {
11475
11465
  return r.localPath;
11476
11466
  }
11477
11467
  const att = batch[cursor + i];
11478
- log18.warn("promptUser upload not persisted; falling back to url", {
11468
+ log19.warn("promptUser upload not persisted; falling back to url", {
11479
11469
  filename: att.filename
11480
11470
  });
11481
11471
  return att.url;
@@ -11483,7 +11473,7 @@ var HeadlessSession = class {
11483
11473
  cursor += slot.count;
11484
11474
  answers[slot.id] = slot.isArray ? paths : paths[0];
11485
11475
  }
11486
- log18.info("promptUser uploads persisted", { count: batch.length });
11476
+ log19.info("promptUser uploads persisted", { count: batch.length });
11487
11477
  return JSON.stringify(answers);
11488
11478
  }
11489
11479
  /**
@@ -11506,7 +11496,7 @@ var HeadlessSession = class {
11506
11496
  async runSingleTurn(parsed, requestId, fromChain = false, queued = false) {
11507
11497
  const attachments = parsed.attachments;
11508
11498
  if (attachments?.length) {
11509
- log18.info("Message has attachments", {
11499
+ log19.info("Message has attachments", {
11510
11500
  count: attachments.length,
11511
11501
  urls: attachments.map((a) => a.url)
11512
11502
  });
@@ -11718,7 +11708,7 @@ var HeadlessSession = class {
11718
11708
  error: "Turn ended unexpectedly"
11719
11709
  });
11720
11710
  }
11721
- log18.info("Turn complete", {
11711
+ log19.info("Turn complete", {
11722
11712
  requestId,
11723
11713
  durationMs: Date.now() - this.turnStart
11724
11714
  });
@@ -11730,7 +11720,7 @@ var HeadlessSession = class {
11730
11720
  error: err.message
11731
11721
  });
11732
11722
  }
11733
- log18.warn("Command failed", {
11723
+ log19.warn("Command failed", {
11734
11724
  action: "message",
11735
11725
  requestId,
11736
11726
  error: err.message
@@ -11899,7 +11889,7 @@ var HeadlessSession = class {
11899
11889
  return {
11900
11890
  ...this.state.models && { models: this.state.models },
11901
11891
  modelSurfaces: getEffectiveModelSurfaces(),
11902
- allowedModelsByType: ALLOWED_MODELS_BY_TYPE
11892
+ allowedModelsByType: getAllowedModelsByType()
11903
11893
  };
11904
11894
  }
11905
11895
  /** Change per-agent model picks without clearing history. Takes effect on
@@ -11912,7 +11902,7 @@ var HeadlessSession = class {
11912
11902
  return {
11913
11903
  ...this.state.models && { models: this.state.models },
11914
11904
  modelSurfaces: getEffectiveModelSurfaces(),
11915
- allowedModelsByType: ALLOWED_MODELS_BY_TYPE
11905
+ allowedModelsByType: getAllowedModelsByType()
11916
11906
  };
11917
11907
  }
11918
11908
  /**
@@ -12037,7 +12027,7 @@ var HeadlessSession = class {
12037
12027
  try {
12038
12028
  parsed = JSON.parse(line);
12039
12029
  } catch (err) {
12040
- log18.warn("Invalid JSON on stdin", {
12030
+ log19.warn("Invalid JSON on stdin", {
12041
12031
  error: err.message,
12042
12032
  lineLength: line.length,
12043
12033
  preview: line.slice(0, 200)
@@ -12046,7 +12036,7 @@ var HeadlessSession = class {
12046
12036
  return;
12047
12037
  }
12048
12038
  const { action, requestId } = parsed;
12049
- log18.info("Command received", { action, requestId });
12039
+ log19.info("Command received", { action, requestId });
12050
12040
  if (action === "tool_result" && parsed.id) {
12051
12041
  const id = parsed.id;
12052
12042
  const result = parsed.result ?? "";
@@ -12055,7 +12045,7 @@ var HeadlessSession = class {
12055
12045
  this.pendingTools.delete(id);
12056
12046
  pending2.resolve(result);
12057
12047
  } else if (!this.running) {
12058
- log18.info("Late tool_result while idle, dismissing", { id });
12048
+ log19.info("Late tool_result while idle, dismissing", { id });
12059
12049
  this.emit("completed", { success: true }, requestId);
12060
12050
  } else {
12061
12051
  this.earlyResults.set(id, result);
@@ -12068,7 +12058,7 @@ var HeadlessSession = class {
12068
12058
  ...typeof parsed.before === "number" ? { before: parsed.before } : {},
12069
12059
  ...typeof parsed.limit === "number" ? { limit: parsed.limit } : {}
12070
12060
  });
12071
- log18.info("History response", {
12061
+ log19.info("History response", {
12072
12062
  requestId,
12073
12063
  startIndex: page.startIndex,
12074
12064
  endIndex: page.endIndex,
@@ -12086,7 +12076,7 @@ var HeadlessSession = class {
12086
12076
  ...this.running && this.currentRequestId ? { currentRequestId: this.currentRequestId } : {},
12087
12077
  ...this.state.models && { models: this.state.models },
12088
12078
  modelSurfaces: getEffectiveModelSurfaces(),
12089
- allowedModelsByType: ALLOWED_MODELS_BY_TYPE,
12079
+ allowedModelsByType: getAllowedModelsByType(),
12090
12080
  // Current queue snapshot for connect/reconnect — get_history is the
12091
12081
  // on-demand "current state" query. Always an array (possibly empty),
12092
12082
  // matching the queue_changed convention so the client reconciles the