sparkecoder 0.1.138 → 0.1.140
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/agent/index.d.ts +3 -3
- package/dist/agent/index.js +212 -8
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +220 -9
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-BM99kjgq.d.ts → index-Cl_eUatM.d.ts} +103 -96
- package/dist/index.d.ts +5 -5
- package/dist/index.js +220 -9
- package/dist/index.js.map +1 -1
- package/dist/{schema-Dz-wABVY.d.ts → schema-BSz4MzhJ.d.ts} +3 -3
- package/dist/{search-CVVfuBPZ.d.ts → search-DOzC4ojH.d.ts} +4 -4
- package/dist/server/index.js +220 -9
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +3 -3
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__e5911ea8._.js +1 -1
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/{static/chunks/3f50fe2a802aa800.js → chunks/b0cae7e255cae74d.js} +1 -1
- package/web/.next/{static/chunks/3f50fe2a802aa800.js → standalone/web/.next/static/static/chunks/b0cae7e255cae74d.js} +1 -1
- package/web/.next/standalone/web/runtime-config.json +2 -2
- package/web/.next/standalone/web/src/components/chat-interface.tsx +19 -2
- package/web/.next/{standalone/web/.next/static/chunks/3f50fe2a802aa800.js → static/chunks/b0cae7e255cae74d.js} +1 -1
- /package/web/.next/standalone/web/.next/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_ssgManifest.js +0 -0
- /package/web/.next/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_buildManifest.js +0 -0
- /package/web/.next/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{hJ9axFUPZg0HHJCXM9Oyx → QkKMkVPV-LLRD2i9PBP_Y}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -7669,6 +7669,105 @@ var init_cap_image_count = __esm({
|
|
|
7669
7669
|
}
|
|
7670
7670
|
});
|
|
7671
7671
|
|
|
7672
|
+
// src/utils/sanitize-images.ts
|
|
7673
|
+
function hasImageMagic(bytes) {
|
|
7674
|
+
if (bytes.length < 12) return false;
|
|
7675
|
+
if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) return true;
|
|
7676
|
+
if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return true;
|
|
7677
|
+
if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 56) return true;
|
|
7678
|
+
if (bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) return true;
|
|
7679
|
+
return false;
|
|
7680
|
+
}
|
|
7681
|
+
function extractBase64(part) {
|
|
7682
|
+
const raw = typeof part?.data === "string" ? part.data : typeof part?.image === "string" ? part.image : null;
|
|
7683
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
7684
|
+
if (/^https?:\/\//i.test(raw)) return null;
|
|
7685
|
+
const dataUrl = raw.match(/^data:[^;,]+;base64,([\s\S]*)$/);
|
|
7686
|
+
return dataUrl ? dataUrl[1] : raw;
|
|
7687
|
+
}
|
|
7688
|
+
function isInvalidImagePart(part) {
|
|
7689
|
+
if (!part || typeof part !== "object") return false;
|
|
7690
|
+
const t = part.type;
|
|
7691
|
+
if (t !== "image" && t !== "image-data" && t !== "media") return false;
|
|
7692
|
+
const mt = part.mediaType;
|
|
7693
|
+
const b64 = extractBase64(part);
|
|
7694
|
+
if (b64 === null) {
|
|
7695
|
+
return typeof mt === "string" && mt.startsWith("image/") && !SUPPORTED_IMAGE_TYPES.includes(mt);
|
|
7696
|
+
}
|
|
7697
|
+
let bytes;
|
|
7698
|
+
try {
|
|
7699
|
+
bytes = Buffer.from(b64, "base64");
|
|
7700
|
+
} catch {
|
|
7701
|
+
return true;
|
|
7702
|
+
}
|
|
7703
|
+
if (bytes.length === 0) return true;
|
|
7704
|
+
return !hasImageMagic(bytes);
|
|
7705
|
+
}
|
|
7706
|
+
function placeholder() {
|
|
7707
|
+
return { type: "text", text: INVALID_IMAGE_PLACEHOLDER };
|
|
7708
|
+
}
|
|
7709
|
+
function sanitizeInvalidImages(messages) {
|
|
7710
|
+
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
7711
|
+
let mutated = false;
|
|
7712
|
+
let dropped = 0;
|
|
7713
|
+
const out = messages.slice();
|
|
7714
|
+
for (let i = 0; i < out.length; i++) {
|
|
7715
|
+
const msg = out[i];
|
|
7716
|
+
if (!Array.isArray(msg.content)) continue;
|
|
7717
|
+
let contentCloned = false;
|
|
7718
|
+
const ensureCloned = () => {
|
|
7719
|
+
if (contentCloned) return;
|
|
7720
|
+
out[i] = { ...msg, content: [...msg.content] };
|
|
7721
|
+
contentCloned = true;
|
|
7722
|
+
};
|
|
7723
|
+
const content = () => out[i].content;
|
|
7724
|
+
for (let j = 0; j < content().length; j++) {
|
|
7725
|
+
const part = content()[j];
|
|
7726
|
+
if (isInvalidImagePart(part)) {
|
|
7727
|
+
ensureCloned();
|
|
7728
|
+
out[i].content[j] = placeholder();
|
|
7729
|
+
mutated = true;
|
|
7730
|
+
dropped++;
|
|
7731
|
+
continue;
|
|
7732
|
+
}
|
|
7733
|
+
if (part && typeof part === "object" && part.type === "tool-result" && part.output && part.output.type === "content" && Array.isArray(part.output.value)) {
|
|
7734
|
+
const innerValue = part.output.value;
|
|
7735
|
+
let innerMutated = false;
|
|
7736
|
+
const newValue = innerValue.slice();
|
|
7737
|
+
for (let k = 0; k < newValue.length; k++) {
|
|
7738
|
+
if (isInvalidImagePart(newValue[k])) {
|
|
7739
|
+
newValue[k] = placeholder();
|
|
7740
|
+
innerMutated = true;
|
|
7741
|
+
dropped++;
|
|
7742
|
+
}
|
|
7743
|
+
}
|
|
7744
|
+
if (innerMutated) {
|
|
7745
|
+
ensureCloned();
|
|
7746
|
+
out[i].content[j] = {
|
|
7747
|
+
...part,
|
|
7748
|
+
output: { ...part.output, value: newValue }
|
|
7749
|
+
};
|
|
7750
|
+
mutated = true;
|
|
7751
|
+
}
|
|
7752
|
+
}
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
if (mutated) {
|
|
7756
|
+
console.warn(
|
|
7757
|
+
`[sanitize-images] Replaced ${dropped} invalid image part(s) with a text placeholder (non-image bytes / unsupported type) to avoid provider 400 "invalid image" errors.`
|
|
7758
|
+
);
|
|
7759
|
+
}
|
|
7760
|
+
return mutated ? out : messages;
|
|
7761
|
+
}
|
|
7762
|
+
var SUPPORTED_IMAGE_TYPES, INVALID_IMAGE_PLACEHOLDER;
|
|
7763
|
+
var init_sanitize_images = __esm({
|
|
7764
|
+
"src/utils/sanitize-images.ts"() {
|
|
7765
|
+
"use strict";
|
|
7766
|
+
SUPPORTED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
|
|
7767
|
+
INVALID_IMAGE_PLACEHOLDER = "[invalid image omitted \u2014 the data was not a valid jpeg/png/gif/webp]";
|
|
7768
|
+
}
|
|
7769
|
+
});
|
|
7770
|
+
|
|
7672
7771
|
// src/agent/model-limits.ts
|
|
7673
7772
|
function getModelLimits(modelId) {
|
|
7674
7773
|
const normalized = modelId.trim().toLowerCase();
|
|
@@ -7686,7 +7785,11 @@ var init_model_limits = __esm({
|
|
|
7686
7785
|
MODEL_LIMITS = {
|
|
7687
7786
|
"claude-opus-4-8": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
7688
7787
|
"gpt-5.5": { contextWindow: 35e4, rollingTarget: 15e4 },
|
|
7689
|
-
"claude-fable-5": { contextWindow: 2e5, rollingTarget: 15e4 }
|
|
7788
|
+
"claude-fable-5": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
7789
|
+
// Claude Opus 4.7 advertises a 1M-token context. Keyed WITH the provider
|
|
7790
|
+
// prefix because getModelLimits() matches the full id (e.g.
|
|
7791
|
+
// "anthropic/claude-opus-4.7") before falling back to the prefix table.
|
|
7792
|
+
"anthropic/claude-opus-4.7": { contextWindow: 1e6, rollingTarget: 15e4 }
|
|
7690
7793
|
};
|
|
7691
7794
|
DEFAULT_LIMITS = { contextWindow: 2e5, rollingTarget: 15e4 };
|
|
7692
7795
|
PREFIX_DEFAULTS = {
|
|
@@ -7760,6 +7863,39 @@ var init_conversation_archive = __esm({
|
|
|
7760
7863
|
|
|
7761
7864
|
// src/agent/context.ts
|
|
7762
7865
|
import { generateText as generateText2 } from "ai";
|
|
7866
|
+
function truncateMiddle(s, cap) {
|
|
7867
|
+
if (s.length <= cap) return s;
|
|
7868
|
+
const half = Math.floor(cap / 2);
|
|
7869
|
+
return s.slice(0, half) + `
|
|
7870
|
+
...[truncated ${s.length - cap} chars to fit context]...
|
|
7871
|
+
` + s.slice(-half);
|
|
7872
|
+
}
|
|
7873
|
+
function truncateToolResultText(part) {
|
|
7874
|
+
const trunc = (r) => {
|
|
7875
|
+
if (typeof r === "string") return truncateMiddle(r, HARD_TRUNCATE_CHARS);
|
|
7876
|
+
if (r && typeof r === "object" && typeof r.text === "string") {
|
|
7877
|
+
return { ...r, text: truncateMiddle(r.text, HARD_TRUNCATE_CHARS) };
|
|
7878
|
+
}
|
|
7879
|
+
return r;
|
|
7880
|
+
};
|
|
7881
|
+
if (Array.isArray(part.result)) return { ...part, result: part.result.map(trunc) };
|
|
7882
|
+
if ("result" in part) return { ...part, result: trunc(part.result) };
|
|
7883
|
+
return part;
|
|
7884
|
+
}
|
|
7885
|
+
function hardTruncateMessageText(msg) {
|
|
7886
|
+
if (typeof msg.content === "string") {
|
|
7887
|
+
return { ...msg, content: truncateMiddle(msg.content, HARD_TRUNCATE_CHARS) };
|
|
7888
|
+
}
|
|
7889
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
7890
|
+
const parts = msg.content.map((part) => {
|
|
7891
|
+
if (part?.type === "text" && typeof part.text === "string") {
|
|
7892
|
+
return { ...part, text: truncateMiddle(part.text, HARD_TRUNCATE_CHARS) };
|
|
7893
|
+
}
|
|
7894
|
+
if (part?.type === "tool-result") return truncateToolResultText(part);
|
|
7895
|
+
return part;
|
|
7896
|
+
});
|
|
7897
|
+
return { ...msg, content: parts };
|
|
7898
|
+
}
|
|
7763
7899
|
function stripBinaryContentForSummary(value) {
|
|
7764
7900
|
if (Array.isArray(value)) return value.map(stripBinaryContentForSummary);
|
|
7765
7901
|
if (!value || typeof value !== "object") return value;
|
|
@@ -7936,7 +8072,7 @@ function ensureEndsWithUserOrTool(messages) {
|
|
|
7936
8072
|
{ role: "user", content: [{ type: "text", text: "Please continue." }] }
|
|
7937
8073
|
];
|
|
7938
8074
|
}
|
|
7939
|
-
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager;
|
|
8075
|
+
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager, HARD_TRUNCATE_CHARS;
|
|
7940
8076
|
var init_context = __esm({
|
|
7941
8077
|
"src/agent/context.ts"() {
|
|
7942
8078
|
"use strict";
|
|
@@ -7947,6 +8083,7 @@ var init_context = __esm({
|
|
|
7947
8083
|
init_prompts();
|
|
7948
8084
|
init_sanitize_messages();
|
|
7949
8085
|
init_cap_image_count();
|
|
8086
|
+
init_sanitize_images();
|
|
7950
8087
|
init_model_limits();
|
|
7951
8088
|
TOOL_OUTPUT_TRIM_CHARS = 400;
|
|
7952
8089
|
COMPACTABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -7996,9 +8133,50 @@ ${summaryContent}`
|
|
|
7996
8133
|
messages = repairToolPairing(messages);
|
|
7997
8134
|
messages = ensureToolResultsFollowCalls(messages);
|
|
7998
8135
|
messages = ensureEndsWithUserOrTool(messages);
|
|
8136
|
+
messages = sanitizeInvalidImages(messages);
|
|
7999
8137
|
messages = capImageCount(messages);
|
|
8138
|
+
messages = this.enforceHardCap(messages);
|
|
8000
8139
|
return messages;
|
|
8001
8140
|
}
|
|
8141
|
+
/**
|
|
8142
|
+
* Drop oldest messages (and, as a last resort, hard-truncate text) until
|
|
8143
|
+
* the prompt fits the model's context window. Preserves any leading
|
|
8144
|
+
* summary system message and always keeps the most recent message, then
|
|
8145
|
+
* repairs tool pairing so dropping can't orphan a tool result.
|
|
8146
|
+
*/
|
|
8147
|
+
enforceHardCap(messages) {
|
|
8148
|
+
const { rollingTarget } = getModelLimits(this.modelId);
|
|
8149
|
+
const MAX_MESSAGES = 120;
|
|
8150
|
+
const tokenCeiling = Math.max(2e4, Math.floor(rollingTarget * 1.5));
|
|
8151
|
+
const tokens = messages.map((m) => this.messageTokens(m));
|
|
8152
|
+
let total = tokens.reduce((a, b) => a + b, 0);
|
|
8153
|
+
const hasLeadingSummary = messages.length > 0 && messages[0].role === "system";
|
|
8154
|
+
const firstBody = hasLeadingSummary ? 1 : 0;
|
|
8155
|
+
const lastIndex = messages.length - 1;
|
|
8156
|
+
const bodyCount = messages.length - firstBody;
|
|
8157
|
+
if (bodyCount <= MAX_MESSAGES && total <= tokenCeiling) return messages;
|
|
8158
|
+
let start = firstBody;
|
|
8159
|
+
const countDropTarget = messages.length - MAX_MESSAGES;
|
|
8160
|
+
while (start < countDropTarget && start < lastIndex) {
|
|
8161
|
+
total -= tokens[start];
|
|
8162
|
+
start += 1;
|
|
8163
|
+
}
|
|
8164
|
+
while (start < lastIndex && total > tokenCeiling) {
|
|
8165
|
+
total -= tokens[start];
|
|
8166
|
+
start += 1;
|
|
8167
|
+
}
|
|
8168
|
+
let out = hasLeadingSummary ? [messages[0], ...messages.slice(start)] : messages.slice(start);
|
|
8169
|
+
if (total > tokenCeiling) {
|
|
8170
|
+
out = out.map((m) => hardTruncateMessageText(m));
|
|
8171
|
+
}
|
|
8172
|
+
out = repairToolPairing(out);
|
|
8173
|
+
out = ensureToolResultsFollowCalls(out);
|
|
8174
|
+
out = ensureEndsWithUserOrTool(out);
|
|
8175
|
+
console.warn(
|
|
8176
|
+
`[Context] hard cap engaged for ${this.modelId}: trimmed ${messages.length}\u2192${out.length} msgs (ceiling ${tokenCeiling} est-tokens / ${MAX_MESSAGES} msgs).`
|
|
8177
|
+
);
|
|
8178
|
+
return out;
|
|
8179
|
+
}
|
|
8002
8180
|
// ---------------------------------------------------------------------------
|
|
8003
8181
|
// Phase 1 – Compact
|
|
8004
8182
|
// ---------------------------------------------------------------------------
|
|
@@ -8225,6 +8403,7 @@ ${summaryContent}`
|
|
|
8225
8403
|
this.summaries = [];
|
|
8226
8404
|
}
|
|
8227
8405
|
};
|
|
8406
|
+
HARD_TRUNCATE_CHARS = 6e3;
|
|
8228
8407
|
}
|
|
8229
8408
|
});
|
|
8230
8409
|
|
|
@@ -9086,7 +9265,29 @@ function markRespondedForThread(slackChannel2, threadTs) {
|
|
|
9086
9265
|
}
|
|
9087
9266
|
}
|
|
9088
9267
|
function resolveBatchOnTurnEnd(events, ok) {
|
|
9089
|
-
if (!ok)
|
|
9268
|
+
if (!ok) {
|
|
9269
|
+
for (const ev of events) {
|
|
9270
|
+
const key2 = eventKey(ev);
|
|
9271
|
+
const entry2 = ledger.get(key2);
|
|
9272
|
+
if (!entry2 || TERMINAL.has(entry2.state)) continue;
|
|
9273
|
+
entry2.state = "failed";
|
|
9274
|
+
entry2.updatedAt = Date.now();
|
|
9275
|
+
if (entry2.channel === "slack" && entry2.slackChannel) {
|
|
9276
|
+
if (entry2.messageTs) fireResultReaction(entry2.slackChannel, entry2.messageTs, "failed");
|
|
9277
|
+
if (entry2.threadTs) maybePostFallback(entry2.slackChannel, entry2.threadTs);
|
|
9278
|
+
}
|
|
9279
|
+
recordEvent({
|
|
9280
|
+
source: "daemon",
|
|
9281
|
+
status: "failed",
|
|
9282
|
+
channel: entry2.channel,
|
|
9283
|
+
sessionId: entry2.sessionId,
|
|
9284
|
+
error: "turn failed; not replayed (avoids context-overflow spiral)",
|
|
9285
|
+
textSnippet: entry2.event.content.slice(0, 200),
|
|
9286
|
+
meta: { ackKey: entry2.key, ackState: "failed" }
|
|
9287
|
+
});
|
|
9288
|
+
}
|
|
9289
|
+
return;
|
|
9290
|
+
}
|
|
9090
9291
|
for (const ev of events) {
|
|
9091
9292
|
const key2 = eventKey(ev);
|
|
9092
9293
|
const entry2 = ledger.get(key2);
|
|
@@ -9138,11 +9339,7 @@ function failEntry(entry2) {
|
|
|
9138
9339
|
if (entry2.channel === "slack" && entry2.slackChannel && entry2.messageTs) {
|
|
9139
9340
|
fireResultReaction(entry2.slackChannel, entry2.messageTs, "failed");
|
|
9140
9341
|
if (entry2.threadTs) {
|
|
9141
|
-
|
|
9142
|
-
entry2.slackChannel,
|
|
9143
|
-
entry2.threadTs,
|
|
9144
|
-
`:warning: I wasn't able to handle this after ${entry2.attempts} attempt(s). It may need a human \u2014 flagging it here so it isn't lost.`
|
|
9145
|
-
);
|
|
9342
|
+
maybePostFallback(entry2.slackChannel, entry2.threadTs);
|
|
9146
9343
|
}
|
|
9147
9344
|
}
|
|
9148
9345
|
recordEvent({
|
|
@@ -9185,6 +9382,17 @@ function fireFallback(channel, threadTs, text) {
|
|
|
9185
9382
|
} catch {
|
|
9186
9383
|
}
|
|
9187
9384
|
}
|
|
9385
|
+
function maybePostFallback(channel, threadTs) {
|
|
9386
|
+
const now = Date.now();
|
|
9387
|
+
const last = lastFallbackAt.get(channel) ?? 0;
|
|
9388
|
+
if (now - last < FALLBACK_COOLDOWN_MS) return;
|
|
9389
|
+
lastFallbackAt.set(channel, now);
|
|
9390
|
+
fireFallback(
|
|
9391
|
+
channel,
|
|
9392
|
+
threadTs,
|
|
9393
|
+
"\u26A0\uFE0F I'm having trouble processing messages right now (likely an overloaded context or a backend error). A human may need to check on me \u2014 I'll pick back up once it's resolved."
|
|
9394
|
+
);
|
|
9395
|
+
}
|
|
9188
9396
|
function startReconciler() {
|
|
9189
9397
|
if (reconcileTimer) return;
|
|
9190
9398
|
reconcileTimer = setInterval(() => {
|
|
@@ -9206,8 +9414,9 @@ function __listAcks() {
|
|
|
9206
9414
|
}
|
|
9207
9415
|
function __resetAcks() {
|
|
9208
9416
|
ledger.clear();
|
|
9417
|
+
lastFallbackAt.clear();
|
|
9209
9418
|
}
|
|
9210
|
-
var REPLAY_AFTER_MS, RECONCILE_EVERY_MS, MAX_ATTEMPTS, PRUNE_AFTER_MS, MAX_ENTRIES, TERMINAL, SEP, ledger, reconcileTimer;
|
|
9419
|
+
var REPLAY_AFTER_MS, RECONCILE_EVERY_MS, MAX_ATTEMPTS, PRUNE_AFTER_MS, FALLBACK_COOLDOWN_MS, lastFallbackAt, MAX_ENTRIES, TERMINAL, SEP, ledger, reconcileTimer;
|
|
9211
9420
|
var init_inbox_acks = __esm({
|
|
9212
9421
|
"src/orchestrator/inbox-acks.ts"() {
|
|
9213
9422
|
"use strict";
|
|
@@ -9218,6 +9427,8 @@ var init_inbox_acks = __esm({
|
|
|
9218
9427
|
RECONCILE_EVERY_MS = 6e4;
|
|
9219
9428
|
MAX_ATTEMPTS = 2;
|
|
9220
9429
|
PRUNE_AFTER_MS = 60 * 6e4;
|
|
9430
|
+
FALLBACK_COOLDOWN_MS = 15 * 6e4;
|
|
9431
|
+
lastFallbackAt = /* @__PURE__ */ new Map();
|
|
9221
9432
|
MAX_ENTRIES = 5e3;
|
|
9222
9433
|
TERMINAL = /* @__PURE__ */ new Set(["responded", "skipped", "handed_off", "failed"]);
|
|
9223
9434
|
SEP = "\u241F";
|