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/cli.js
CHANGED
|
@@ -8395,6 +8395,105 @@ var init_cap_image_count = __esm({
|
|
|
8395
8395
|
}
|
|
8396
8396
|
});
|
|
8397
8397
|
|
|
8398
|
+
// src/utils/sanitize-images.ts
|
|
8399
|
+
function hasImageMagic(bytes) {
|
|
8400
|
+
if (bytes.length < 12) return false;
|
|
8401
|
+
if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) return true;
|
|
8402
|
+
if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return true;
|
|
8403
|
+
if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 56) return true;
|
|
8404
|
+
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;
|
|
8405
|
+
return false;
|
|
8406
|
+
}
|
|
8407
|
+
function extractBase64(part) {
|
|
8408
|
+
const raw = typeof part?.data === "string" ? part.data : typeof part?.image === "string" ? part.image : null;
|
|
8409
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
8410
|
+
if (/^https?:\/\//i.test(raw)) return null;
|
|
8411
|
+
const dataUrl = raw.match(/^data:[^;,]+;base64,([\s\S]*)$/);
|
|
8412
|
+
return dataUrl ? dataUrl[1] : raw;
|
|
8413
|
+
}
|
|
8414
|
+
function isInvalidImagePart(part) {
|
|
8415
|
+
if (!part || typeof part !== "object") return false;
|
|
8416
|
+
const t = part.type;
|
|
8417
|
+
if (t !== "image" && t !== "image-data" && t !== "media") return false;
|
|
8418
|
+
const mt = part.mediaType;
|
|
8419
|
+
const b64 = extractBase64(part);
|
|
8420
|
+
if (b64 === null) {
|
|
8421
|
+
return typeof mt === "string" && mt.startsWith("image/") && !SUPPORTED_IMAGE_TYPES.includes(mt);
|
|
8422
|
+
}
|
|
8423
|
+
let bytes;
|
|
8424
|
+
try {
|
|
8425
|
+
bytes = Buffer.from(b64, "base64");
|
|
8426
|
+
} catch {
|
|
8427
|
+
return true;
|
|
8428
|
+
}
|
|
8429
|
+
if (bytes.length === 0) return true;
|
|
8430
|
+
return !hasImageMagic(bytes);
|
|
8431
|
+
}
|
|
8432
|
+
function placeholder() {
|
|
8433
|
+
return { type: "text", text: INVALID_IMAGE_PLACEHOLDER };
|
|
8434
|
+
}
|
|
8435
|
+
function sanitizeInvalidImages(messages) {
|
|
8436
|
+
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
8437
|
+
let mutated = false;
|
|
8438
|
+
let dropped = 0;
|
|
8439
|
+
const out = messages.slice();
|
|
8440
|
+
for (let i = 0; i < out.length; i++) {
|
|
8441
|
+
const msg = out[i];
|
|
8442
|
+
if (!Array.isArray(msg.content)) continue;
|
|
8443
|
+
let contentCloned = false;
|
|
8444
|
+
const ensureCloned = () => {
|
|
8445
|
+
if (contentCloned) return;
|
|
8446
|
+
out[i] = { ...msg, content: [...msg.content] };
|
|
8447
|
+
contentCloned = true;
|
|
8448
|
+
};
|
|
8449
|
+
const content = () => out[i].content;
|
|
8450
|
+
for (let j = 0; j < content().length; j++) {
|
|
8451
|
+
const part = content()[j];
|
|
8452
|
+
if (isInvalidImagePart(part)) {
|
|
8453
|
+
ensureCloned();
|
|
8454
|
+
out[i].content[j] = placeholder();
|
|
8455
|
+
mutated = true;
|
|
8456
|
+
dropped++;
|
|
8457
|
+
continue;
|
|
8458
|
+
}
|
|
8459
|
+
if (part && typeof part === "object" && part.type === "tool-result" && part.output && part.output.type === "content" && Array.isArray(part.output.value)) {
|
|
8460
|
+
const innerValue = part.output.value;
|
|
8461
|
+
let innerMutated = false;
|
|
8462
|
+
const newValue = innerValue.slice();
|
|
8463
|
+
for (let k = 0; k < newValue.length; k++) {
|
|
8464
|
+
if (isInvalidImagePart(newValue[k])) {
|
|
8465
|
+
newValue[k] = placeholder();
|
|
8466
|
+
innerMutated = true;
|
|
8467
|
+
dropped++;
|
|
8468
|
+
}
|
|
8469
|
+
}
|
|
8470
|
+
if (innerMutated) {
|
|
8471
|
+
ensureCloned();
|
|
8472
|
+
out[i].content[j] = {
|
|
8473
|
+
...part,
|
|
8474
|
+
output: { ...part.output, value: newValue }
|
|
8475
|
+
};
|
|
8476
|
+
mutated = true;
|
|
8477
|
+
}
|
|
8478
|
+
}
|
|
8479
|
+
}
|
|
8480
|
+
}
|
|
8481
|
+
if (mutated) {
|
|
8482
|
+
console.warn(
|
|
8483
|
+
`[sanitize-images] Replaced ${dropped} invalid image part(s) with a text placeholder (non-image bytes / unsupported type) to avoid provider 400 "invalid image" errors.`
|
|
8484
|
+
);
|
|
8485
|
+
}
|
|
8486
|
+
return mutated ? out : messages;
|
|
8487
|
+
}
|
|
8488
|
+
var SUPPORTED_IMAGE_TYPES, INVALID_IMAGE_PLACEHOLDER;
|
|
8489
|
+
var init_sanitize_images = __esm({
|
|
8490
|
+
"src/utils/sanitize-images.ts"() {
|
|
8491
|
+
"use strict";
|
|
8492
|
+
SUPPORTED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
|
|
8493
|
+
INVALID_IMAGE_PLACEHOLDER = "[invalid image omitted \u2014 the data was not a valid jpeg/png/gif/webp]";
|
|
8494
|
+
}
|
|
8495
|
+
});
|
|
8496
|
+
|
|
8398
8497
|
// src/agent/model-limits.ts
|
|
8399
8498
|
function getModelLimits(modelId) {
|
|
8400
8499
|
const normalized = modelId.trim().toLowerCase();
|
|
@@ -8412,7 +8511,11 @@ var init_model_limits = __esm({
|
|
|
8412
8511
|
MODEL_LIMITS = {
|
|
8413
8512
|
"claude-opus-4-8": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
8414
8513
|
"gpt-5.5": { contextWindow: 35e4, rollingTarget: 15e4 },
|
|
8415
|
-
"claude-fable-5": { contextWindow: 2e5, rollingTarget: 15e4 }
|
|
8514
|
+
"claude-fable-5": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
8515
|
+
// Claude Opus 4.7 advertises a 1M-token context. Keyed WITH the provider
|
|
8516
|
+
// prefix because getModelLimits() matches the full id (e.g.
|
|
8517
|
+
// "anthropic/claude-opus-4.7") before falling back to the prefix table.
|
|
8518
|
+
"anthropic/claude-opus-4.7": { contextWindow: 1e6, rollingTarget: 15e4 }
|
|
8416
8519
|
};
|
|
8417
8520
|
DEFAULT_LIMITS = { contextWindow: 2e5, rollingTarget: 15e4 };
|
|
8418
8521
|
PREFIX_DEFAULTS = {
|
|
@@ -8486,6 +8589,39 @@ var init_conversation_archive = __esm({
|
|
|
8486
8589
|
|
|
8487
8590
|
// src/agent/context.ts
|
|
8488
8591
|
import { generateText as generateText2 } from "ai";
|
|
8592
|
+
function truncateMiddle(s, cap) {
|
|
8593
|
+
if (s.length <= cap) return s;
|
|
8594
|
+
const half = Math.floor(cap / 2);
|
|
8595
|
+
return s.slice(0, half) + `
|
|
8596
|
+
...[truncated ${s.length - cap} chars to fit context]...
|
|
8597
|
+
` + s.slice(-half);
|
|
8598
|
+
}
|
|
8599
|
+
function truncateToolResultText(part) {
|
|
8600
|
+
const trunc = (r) => {
|
|
8601
|
+
if (typeof r === "string") return truncateMiddle(r, HARD_TRUNCATE_CHARS);
|
|
8602
|
+
if (r && typeof r === "object" && typeof r.text === "string") {
|
|
8603
|
+
return { ...r, text: truncateMiddle(r.text, HARD_TRUNCATE_CHARS) };
|
|
8604
|
+
}
|
|
8605
|
+
return r;
|
|
8606
|
+
};
|
|
8607
|
+
if (Array.isArray(part.result)) return { ...part, result: part.result.map(trunc) };
|
|
8608
|
+
if ("result" in part) return { ...part, result: trunc(part.result) };
|
|
8609
|
+
return part;
|
|
8610
|
+
}
|
|
8611
|
+
function hardTruncateMessageText(msg) {
|
|
8612
|
+
if (typeof msg.content === "string") {
|
|
8613
|
+
return { ...msg, content: truncateMiddle(msg.content, HARD_TRUNCATE_CHARS) };
|
|
8614
|
+
}
|
|
8615
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
8616
|
+
const parts = msg.content.map((part) => {
|
|
8617
|
+
if (part?.type === "text" && typeof part.text === "string") {
|
|
8618
|
+
return { ...part, text: truncateMiddle(part.text, HARD_TRUNCATE_CHARS) };
|
|
8619
|
+
}
|
|
8620
|
+
if (part?.type === "tool-result") return truncateToolResultText(part);
|
|
8621
|
+
return part;
|
|
8622
|
+
});
|
|
8623
|
+
return { ...msg, content: parts };
|
|
8624
|
+
}
|
|
8489
8625
|
function stripBinaryContentForSummary(value) {
|
|
8490
8626
|
if (Array.isArray(value)) return value.map(stripBinaryContentForSummary);
|
|
8491
8627
|
if (!value || typeof value !== "object") return value;
|
|
@@ -8662,7 +8798,7 @@ function ensureEndsWithUserOrTool(messages) {
|
|
|
8662
8798
|
{ role: "user", content: [{ type: "text", text: "Please continue." }] }
|
|
8663
8799
|
];
|
|
8664
8800
|
}
|
|
8665
|
-
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager;
|
|
8801
|
+
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager, HARD_TRUNCATE_CHARS;
|
|
8666
8802
|
var init_context = __esm({
|
|
8667
8803
|
"src/agent/context.ts"() {
|
|
8668
8804
|
"use strict";
|
|
@@ -8673,6 +8809,7 @@ var init_context = __esm({
|
|
|
8673
8809
|
init_prompts();
|
|
8674
8810
|
init_sanitize_messages();
|
|
8675
8811
|
init_cap_image_count();
|
|
8812
|
+
init_sanitize_images();
|
|
8676
8813
|
init_model_limits();
|
|
8677
8814
|
TOOL_OUTPUT_TRIM_CHARS = 400;
|
|
8678
8815
|
COMPACTABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -8722,9 +8859,50 @@ ${summaryContent}`
|
|
|
8722
8859
|
messages = repairToolPairing(messages);
|
|
8723
8860
|
messages = ensureToolResultsFollowCalls(messages);
|
|
8724
8861
|
messages = ensureEndsWithUserOrTool(messages);
|
|
8862
|
+
messages = sanitizeInvalidImages(messages);
|
|
8725
8863
|
messages = capImageCount(messages);
|
|
8864
|
+
messages = this.enforceHardCap(messages);
|
|
8726
8865
|
return messages;
|
|
8727
8866
|
}
|
|
8867
|
+
/**
|
|
8868
|
+
* Drop oldest messages (and, as a last resort, hard-truncate text) until
|
|
8869
|
+
* the prompt fits the model's context window. Preserves any leading
|
|
8870
|
+
* summary system message and always keeps the most recent message, then
|
|
8871
|
+
* repairs tool pairing so dropping can't orphan a tool result.
|
|
8872
|
+
*/
|
|
8873
|
+
enforceHardCap(messages) {
|
|
8874
|
+
const { rollingTarget } = getModelLimits(this.modelId);
|
|
8875
|
+
const MAX_MESSAGES = 120;
|
|
8876
|
+
const tokenCeiling = Math.max(2e4, Math.floor(rollingTarget * 1.5));
|
|
8877
|
+
const tokens = messages.map((m) => this.messageTokens(m));
|
|
8878
|
+
let total = tokens.reduce((a, b) => a + b, 0);
|
|
8879
|
+
const hasLeadingSummary = messages.length > 0 && messages[0].role === "system";
|
|
8880
|
+
const firstBody = hasLeadingSummary ? 1 : 0;
|
|
8881
|
+
const lastIndex = messages.length - 1;
|
|
8882
|
+
const bodyCount = messages.length - firstBody;
|
|
8883
|
+
if (bodyCount <= MAX_MESSAGES && total <= tokenCeiling) return messages;
|
|
8884
|
+
let start = firstBody;
|
|
8885
|
+
const countDropTarget = messages.length - MAX_MESSAGES;
|
|
8886
|
+
while (start < countDropTarget && start < lastIndex) {
|
|
8887
|
+
total -= tokens[start];
|
|
8888
|
+
start += 1;
|
|
8889
|
+
}
|
|
8890
|
+
while (start < lastIndex && total > tokenCeiling) {
|
|
8891
|
+
total -= tokens[start];
|
|
8892
|
+
start += 1;
|
|
8893
|
+
}
|
|
8894
|
+
let out = hasLeadingSummary ? [messages[0], ...messages.slice(start)] : messages.slice(start);
|
|
8895
|
+
if (total > tokenCeiling) {
|
|
8896
|
+
out = out.map((m) => hardTruncateMessageText(m));
|
|
8897
|
+
}
|
|
8898
|
+
out = repairToolPairing(out);
|
|
8899
|
+
out = ensureToolResultsFollowCalls(out);
|
|
8900
|
+
out = ensureEndsWithUserOrTool(out);
|
|
8901
|
+
console.warn(
|
|
8902
|
+
`[Context] hard cap engaged for ${this.modelId}: trimmed ${messages.length}\u2192${out.length} msgs (ceiling ${tokenCeiling} est-tokens / ${MAX_MESSAGES} msgs).`
|
|
8903
|
+
);
|
|
8904
|
+
return out;
|
|
8905
|
+
}
|
|
8728
8906
|
// ---------------------------------------------------------------------------
|
|
8729
8907
|
// Phase 1 – Compact
|
|
8730
8908
|
// ---------------------------------------------------------------------------
|
|
@@ -8951,6 +9129,7 @@ ${summaryContent}`
|
|
|
8951
9129
|
this.summaries = [];
|
|
8952
9130
|
}
|
|
8953
9131
|
};
|
|
9132
|
+
HARD_TRUNCATE_CHARS = 6e3;
|
|
8954
9133
|
}
|
|
8955
9134
|
});
|
|
8956
9135
|
|
|
@@ -9812,7 +9991,29 @@ function markRespondedForThread(slackChannel2, threadTs) {
|
|
|
9812
9991
|
}
|
|
9813
9992
|
}
|
|
9814
9993
|
function resolveBatchOnTurnEnd(events, ok) {
|
|
9815
|
-
if (!ok)
|
|
9994
|
+
if (!ok) {
|
|
9995
|
+
for (const ev of events) {
|
|
9996
|
+
const key2 = eventKey(ev);
|
|
9997
|
+
const entry2 = ledger.get(key2);
|
|
9998
|
+
if (!entry2 || TERMINAL.has(entry2.state)) continue;
|
|
9999
|
+
entry2.state = "failed";
|
|
10000
|
+
entry2.updatedAt = Date.now();
|
|
10001
|
+
if (entry2.channel === "slack" && entry2.slackChannel) {
|
|
10002
|
+
if (entry2.messageTs) fireResultReaction(entry2.slackChannel, entry2.messageTs, "failed");
|
|
10003
|
+
if (entry2.threadTs) maybePostFallback(entry2.slackChannel, entry2.threadTs);
|
|
10004
|
+
}
|
|
10005
|
+
recordEvent({
|
|
10006
|
+
source: "daemon",
|
|
10007
|
+
status: "failed",
|
|
10008
|
+
channel: entry2.channel,
|
|
10009
|
+
sessionId: entry2.sessionId,
|
|
10010
|
+
error: "turn failed; not replayed (avoids context-overflow spiral)",
|
|
10011
|
+
textSnippet: entry2.event.content.slice(0, 200),
|
|
10012
|
+
meta: { ackKey: entry2.key, ackState: "failed" }
|
|
10013
|
+
});
|
|
10014
|
+
}
|
|
10015
|
+
return;
|
|
10016
|
+
}
|
|
9816
10017
|
for (const ev of events) {
|
|
9817
10018
|
const key2 = eventKey(ev);
|
|
9818
10019
|
const entry2 = ledger.get(key2);
|
|
@@ -9864,11 +10065,7 @@ function failEntry(entry2) {
|
|
|
9864
10065
|
if (entry2.channel === "slack" && entry2.slackChannel && entry2.messageTs) {
|
|
9865
10066
|
fireResultReaction(entry2.slackChannel, entry2.messageTs, "failed");
|
|
9866
10067
|
if (entry2.threadTs) {
|
|
9867
|
-
|
|
9868
|
-
entry2.slackChannel,
|
|
9869
|
-
entry2.threadTs,
|
|
9870
|
-
`: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.`
|
|
9871
|
-
);
|
|
10068
|
+
maybePostFallback(entry2.slackChannel, entry2.threadTs);
|
|
9872
10069
|
}
|
|
9873
10070
|
}
|
|
9874
10071
|
recordEvent({
|
|
@@ -9911,6 +10108,17 @@ function fireFallback(channel, threadTs, text) {
|
|
|
9911
10108
|
} catch {
|
|
9912
10109
|
}
|
|
9913
10110
|
}
|
|
10111
|
+
function maybePostFallback(channel, threadTs) {
|
|
10112
|
+
const now = Date.now();
|
|
10113
|
+
const last = lastFallbackAt.get(channel) ?? 0;
|
|
10114
|
+
if (now - last < FALLBACK_COOLDOWN_MS) return;
|
|
10115
|
+
lastFallbackAt.set(channel, now);
|
|
10116
|
+
fireFallback(
|
|
10117
|
+
channel,
|
|
10118
|
+
threadTs,
|
|
10119
|
+
"\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."
|
|
10120
|
+
);
|
|
10121
|
+
}
|
|
9914
10122
|
function startReconciler() {
|
|
9915
10123
|
if (reconcileTimer) return;
|
|
9916
10124
|
reconcileTimer = setInterval(() => {
|
|
@@ -9932,8 +10140,9 @@ function __listAcks() {
|
|
|
9932
10140
|
}
|
|
9933
10141
|
function __resetAcks() {
|
|
9934
10142
|
ledger.clear();
|
|
10143
|
+
lastFallbackAt.clear();
|
|
9935
10144
|
}
|
|
9936
|
-
var REPLAY_AFTER_MS, RECONCILE_EVERY_MS, MAX_ATTEMPTS, PRUNE_AFTER_MS, MAX_ENTRIES, TERMINAL, SEP, ledger, reconcileTimer;
|
|
10145
|
+
var REPLAY_AFTER_MS, RECONCILE_EVERY_MS, MAX_ATTEMPTS, PRUNE_AFTER_MS, FALLBACK_COOLDOWN_MS, lastFallbackAt, MAX_ENTRIES, TERMINAL, SEP, ledger, reconcileTimer;
|
|
9937
10146
|
var init_inbox_acks = __esm({
|
|
9938
10147
|
"src/orchestrator/inbox-acks.ts"() {
|
|
9939
10148
|
"use strict";
|
|
@@ -9944,6 +10153,8 @@ var init_inbox_acks = __esm({
|
|
|
9944
10153
|
RECONCILE_EVERY_MS = 6e4;
|
|
9945
10154
|
MAX_ATTEMPTS = 2;
|
|
9946
10155
|
PRUNE_AFTER_MS = 60 * 6e4;
|
|
10156
|
+
FALLBACK_COOLDOWN_MS = 15 * 6e4;
|
|
10157
|
+
lastFallbackAt = /* @__PURE__ */ new Map();
|
|
9947
10158
|
MAX_ENTRIES = 5e3;
|
|
9948
10159
|
TERMINAL = /* @__PURE__ */ new Set(["responded", "skipped", "handed_off", "failed"]);
|
|
9949
10160
|
SEP = "\u241F";
|