@mindstudio-ai/remy 0.1.333 → 0.1.335
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 +258 -268
- package/dist/index.js +206 -204
- package/dist/prompt/compiled/dev-and-deploy.md +1 -1
- package/dist/prompt/compiled/tables.md +11 -17
- package/dist/prompt/skills/auth.md +1 -0
- package/dist/prompt/skills/scenarios.md +13 -3
- package/package.json +1 -1
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) || //
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
|
|
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
|
|
523
|
-
parent
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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] ??
|
|
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
|
|
2935
|
+
var log5 = createLogger("sidecar");
|
|
2947
2936
|
var baseUrl = null;
|
|
2948
2937
|
function setSidecarBaseUrl(url) {
|
|
2949
2938
|
baseUrl = url;
|
|
2950
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7994
|
+
log11.info("Summary generated", { name, summaryLength: summaryText.length });
|
|
8006
7995
|
return summaryText;
|
|
8007
7996
|
}
|
|
8008
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8064
|
+
log11.info("Summary cancelled", { name });
|
|
8076
8065
|
} else {
|
|
8077
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8400
|
+
log12.info("Session saved", { messageCount: state.messages.length });
|
|
8412
8401
|
} catch (err) {
|
|
8413
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8538
|
+
log13.info("Compaction cancelled");
|
|
8550
8539
|
} else {
|
|
8551
|
-
|
|
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
|
|
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
|
-
|
|
8562
|
+
log14.debug("Brand inputs unchanged \u2014 skipping extraction", { inputHash });
|
|
8574
8563
|
return null;
|
|
8575
8564
|
}
|
|
8576
|
-
|
|
8565
|
+
log14.info("Extracting brand", { inputHash });
|
|
8577
8566
|
const brand = await extractBrand(apiConfig, model);
|
|
8578
8567
|
if (!brand) {
|
|
8579
|
-
|
|
8568
|
+
log14.warn("Brand extraction failed \u2014 leaving cache untouched");
|
|
8580
8569
|
return null;
|
|
8581
8570
|
}
|
|
8582
8571
|
persistBrand(brand, inputHash);
|
|
8583
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8694
|
+
log14.error("Brand extraction stream error", { error: event.error });
|
|
8706
8695
|
return null;
|
|
8707
8696
|
}
|
|
8708
8697
|
}
|
|
8709
8698
|
} catch (err) {
|
|
8710
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|