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
|
@@ -85,7 +85,7 @@ declare const sessions: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
|
85
85
|
tableName: "sessions";
|
|
86
86
|
dataType: "string";
|
|
87
87
|
columnType: "SQLiteText";
|
|
88
|
-
data: "
|
|
88
|
+
data: "error" | "completed" | "active" | "waiting";
|
|
89
89
|
driverParam: string;
|
|
90
90
|
notNull: true;
|
|
91
91
|
hasDefault: true;
|
|
@@ -391,7 +391,7 @@ declare const toolExecutions: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
|
391
391
|
tableName: "tool_executions";
|
|
392
392
|
dataType: "string";
|
|
393
393
|
columnType: "SQLiteText";
|
|
394
|
-
data: "
|
|
394
|
+
data: "error" | "completed" | "pending" | "approved" | "rejected";
|
|
395
395
|
driverParam: string;
|
|
396
396
|
notNull: true;
|
|
397
397
|
hasDefault: true;
|
|
@@ -814,7 +814,7 @@ declare const terminals: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
|
814
814
|
tableName: "terminals";
|
|
815
815
|
dataType: "string";
|
|
816
816
|
columnType: "SQLiteText";
|
|
817
|
-
data: "
|
|
817
|
+
data: "error" | "running" | "stopped";
|
|
818
818
|
driverParam: string;
|
|
819
819
|
notNull: true;
|
|
820
820
|
hasDefault: true;
|
|
@@ -16,11 +16,11 @@ interface BashToolOptions {
|
|
|
16
16
|
declare function createBashTool(options: BashToolOptions): ai.Tool<{
|
|
17
17
|
background: boolean;
|
|
18
18
|
id?: string | undefined;
|
|
19
|
-
input?: string | undefined;
|
|
20
19
|
command?: string | undefined;
|
|
20
|
+
input?: string | undefined;
|
|
21
21
|
kill?: boolean | undefined;
|
|
22
22
|
tail?: number | undefined;
|
|
23
|
-
key?: "Enter" | "Escape" | "Up" | "Down" | "Left" | "Right" | "Tab" | "C-c" | "C-d" | "
|
|
23
|
+
key?: "y" | "Enter" | "Escape" | "Up" | "Down" | "Left" | "Right" | "Tab" | "C-c" | "C-d" | "n" | undefined;
|
|
24
24
|
}, {
|
|
25
25
|
success: boolean;
|
|
26
26
|
id: string;
|
|
@@ -66,7 +66,7 @@ declare function createBashTool(options: BashToolOptions): ai.Tool<{
|
|
|
66
66
|
id: string;
|
|
67
67
|
output: string;
|
|
68
68
|
exitCode: number;
|
|
69
|
-
status: "
|
|
69
|
+
status: "error" | "completed" | "running" | "stopped";
|
|
70
70
|
message?: undefined;
|
|
71
71
|
error?: undefined;
|
|
72
72
|
} | {
|
|
@@ -218,8 +218,8 @@ interface SearchToolOptions {
|
|
|
218
218
|
* Progress is streamed back to the UI so users can see exploration happening.
|
|
219
219
|
*/
|
|
220
220
|
declare function createSearchTool(options: SearchToolOptions): ai.Tool<{
|
|
221
|
-
query: string;
|
|
222
221
|
context: string;
|
|
222
|
+
query: string;
|
|
223
223
|
}, {
|
|
224
224
|
success: boolean;
|
|
225
225
|
error: string;
|
package/dist/server/index.js
CHANGED
|
@@ -7652,6 +7652,105 @@ var init_cap_image_count = __esm({
|
|
|
7652
7652
|
}
|
|
7653
7653
|
});
|
|
7654
7654
|
|
|
7655
|
+
// src/utils/sanitize-images.ts
|
|
7656
|
+
function hasImageMagic(bytes) {
|
|
7657
|
+
if (bytes.length < 12) return false;
|
|
7658
|
+
if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) return true;
|
|
7659
|
+
if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return true;
|
|
7660
|
+
if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 56) return true;
|
|
7661
|
+
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;
|
|
7662
|
+
return false;
|
|
7663
|
+
}
|
|
7664
|
+
function extractBase64(part) {
|
|
7665
|
+
const raw = typeof part?.data === "string" ? part.data : typeof part?.image === "string" ? part.image : null;
|
|
7666
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
7667
|
+
if (/^https?:\/\//i.test(raw)) return null;
|
|
7668
|
+
const dataUrl = raw.match(/^data:[^;,]+;base64,([\s\S]*)$/);
|
|
7669
|
+
return dataUrl ? dataUrl[1] : raw;
|
|
7670
|
+
}
|
|
7671
|
+
function isInvalidImagePart(part) {
|
|
7672
|
+
if (!part || typeof part !== "object") return false;
|
|
7673
|
+
const t = part.type;
|
|
7674
|
+
if (t !== "image" && t !== "image-data" && t !== "media") return false;
|
|
7675
|
+
const mt = part.mediaType;
|
|
7676
|
+
const b64 = extractBase64(part);
|
|
7677
|
+
if (b64 === null) {
|
|
7678
|
+
return typeof mt === "string" && mt.startsWith("image/") && !SUPPORTED_IMAGE_TYPES.includes(mt);
|
|
7679
|
+
}
|
|
7680
|
+
let bytes;
|
|
7681
|
+
try {
|
|
7682
|
+
bytes = Buffer.from(b64, "base64");
|
|
7683
|
+
} catch {
|
|
7684
|
+
return true;
|
|
7685
|
+
}
|
|
7686
|
+
if (bytes.length === 0) return true;
|
|
7687
|
+
return !hasImageMagic(bytes);
|
|
7688
|
+
}
|
|
7689
|
+
function placeholder() {
|
|
7690
|
+
return { type: "text", text: INVALID_IMAGE_PLACEHOLDER };
|
|
7691
|
+
}
|
|
7692
|
+
function sanitizeInvalidImages(messages) {
|
|
7693
|
+
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
7694
|
+
let mutated = false;
|
|
7695
|
+
let dropped = 0;
|
|
7696
|
+
const out = messages.slice();
|
|
7697
|
+
for (let i = 0; i < out.length; i++) {
|
|
7698
|
+
const msg = out[i];
|
|
7699
|
+
if (!Array.isArray(msg.content)) continue;
|
|
7700
|
+
let contentCloned = false;
|
|
7701
|
+
const ensureCloned = () => {
|
|
7702
|
+
if (contentCloned) return;
|
|
7703
|
+
out[i] = { ...msg, content: [...msg.content] };
|
|
7704
|
+
contentCloned = true;
|
|
7705
|
+
};
|
|
7706
|
+
const content = () => out[i].content;
|
|
7707
|
+
for (let j = 0; j < content().length; j++) {
|
|
7708
|
+
const part = content()[j];
|
|
7709
|
+
if (isInvalidImagePart(part)) {
|
|
7710
|
+
ensureCloned();
|
|
7711
|
+
out[i].content[j] = placeholder();
|
|
7712
|
+
mutated = true;
|
|
7713
|
+
dropped++;
|
|
7714
|
+
continue;
|
|
7715
|
+
}
|
|
7716
|
+
if (part && typeof part === "object" && part.type === "tool-result" && part.output && part.output.type === "content" && Array.isArray(part.output.value)) {
|
|
7717
|
+
const innerValue = part.output.value;
|
|
7718
|
+
let innerMutated = false;
|
|
7719
|
+
const newValue = innerValue.slice();
|
|
7720
|
+
for (let k = 0; k < newValue.length; k++) {
|
|
7721
|
+
if (isInvalidImagePart(newValue[k])) {
|
|
7722
|
+
newValue[k] = placeholder();
|
|
7723
|
+
innerMutated = true;
|
|
7724
|
+
dropped++;
|
|
7725
|
+
}
|
|
7726
|
+
}
|
|
7727
|
+
if (innerMutated) {
|
|
7728
|
+
ensureCloned();
|
|
7729
|
+
out[i].content[j] = {
|
|
7730
|
+
...part,
|
|
7731
|
+
output: { ...part.output, value: newValue }
|
|
7732
|
+
};
|
|
7733
|
+
mutated = true;
|
|
7734
|
+
}
|
|
7735
|
+
}
|
|
7736
|
+
}
|
|
7737
|
+
}
|
|
7738
|
+
if (mutated) {
|
|
7739
|
+
console.warn(
|
|
7740
|
+
`[sanitize-images] Replaced ${dropped} invalid image part(s) with a text placeholder (non-image bytes / unsupported type) to avoid provider 400 "invalid image" errors.`
|
|
7741
|
+
);
|
|
7742
|
+
}
|
|
7743
|
+
return mutated ? out : messages;
|
|
7744
|
+
}
|
|
7745
|
+
var SUPPORTED_IMAGE_TYPES, INVALID_IMAGE_PLACEHOLDER;
|
|
7746
|
+
var init_sanitize_images = __esm({
|
|
7747
|
+
"src/utils/sanitize-images.ts"() {
|
|
7748
|
+
"use strict";
|
|
7749
|
+
SUPPORTED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
|
|
7750
|
+
INVALID_IMAGE_PLACEHOLDER = "[invalid image omitted \u2014 the data was not a valid jpeg/png/gif/webp]";
|
|
7751
|
+
}
|
|
7752
|
+
});
|
|
7753
|
+
|
|
7655
7754
|
// src/agent/model-limits.ts
|
|
7656
7755
|
function getModelLimits(modelId) {
|
|
7657
7756
|
const normalized = modelId.trim().toLowerCase();
|
|
@@ -7669,7 +7768,11 @@ var init_model_limits = __esm({
|
|
|
7669
7768
|
MODEL_LIMITS = {
|
|
7670
7769
|
"claude-opus-4-8": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
7671
7770
|
"gpt-5.5": { contextWindow: 35e4, rollingTarget: 15e4 },
|
|
7672
|
-
"claude-fable-5": { contextWindow: 2e5, rollingTarget: 15e4 }
|
|
7771
|
+
"claude-fable-5": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
7772
|
+
// Claude Opus 4.7 advertises a 1M-token context. Keyed WITH the provider
|
|
7773
|
+
// prefix because getModelLimits() matches the full id (e.g.
|
|
7774
|
+
// "anthropic/claude-opus-4.7") before falling back to the prefix table.
|
|
7775
|
+
"anthropic/claude-opus-4.7": { contextWindow: 1e6, rollingTarget: 15e4 }
|
|
7673
7776
|
};
|
|
7674
7777
|
DEFAULT_LIMITS = { contextWindow: 2e5, rollingTarget: 15e4 };
|
|
7675
7778
|
PREFIX_DEFAULTS = {
|
|
@@ -7743,6 +7846,39 @@ var init_conversation_archive = __esm({
|
|
|
7743
7846
|
|
|
7744
7847
|
// src/agent/context.ts
|
|
7745
7848
|
import { generateText as generateText2 } from "ai";
|
|
7849
|
+
function truncateMiddle(s, cap) {
|
|
7850
|
+
if (s.length <= cap) return s;
|
|
7851
|
+
const half = Math.floor(cap / 2);
|
|
7852
|
+
return s.slice(0, half) + `
|
|
7853
|
+
...[truncated ${s.length - cap} chars to fit context]...
|
|
7854
|
+
` + s.slice(-half);
|
|
7855
|
+
}
|
|
7856
|
+
function truncateToolResultText(part) {
|
|
7857
|
+
const trunc = (r) => {
|
|
7858
|
+
if (typeof r === "string") return truncateMiddle(r, HARD_TRUNCATE_CHARS);
|
|
7859
|
+
if (r && typeof r === "object" && typeof r.text === "string") {
|
|
7860
|
+
return { ...r, text: truncateMiddle(r.text, HARD_TRUNCATE_CHARS) };
|
|
7861
|
+
}
|
|
7862
|
+
return r;
|
|
7863
|
+
};
|
|
7864
|
+
if (Array.isArray(part.result)) return { ...part, result: part.result.map(trunc) };
|
|
7865
|
+
if ("result" in part) return { ...part, result: trunc(part.result) };
|
|
7866
|
+
return part;
|
|
7867
|
+
}
|
|
7868
|
+
function hardTruncateMessageText(msg) {
|
|
7869
|
+
if (typeof msg.content === "string") {
|
|
7870
|
+
return { ...msg, content: truncateMiddle(msg.content, HARD_TRUNCATE_CHARS) };
|
|
7871
|
+
}
|
|
7872
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
7873
|
+
const parts = msg.content.map((part) => {
|
|
7874
|
+
if (part?.type === "text" && typeof part.text === "string") {
|
|
7875
|
+
return { ...part, text: truncateMiddle(part.text, HARD_TRUNCATE_CHARS) };
|
|
7876
|
+
}
|
|
7877
|
+
if (part?.type === "tool-result") return truncateToolResultText(part);
|
|
7878
|
+
return part;
|
|
7879
|
+
});
|
|
7880
|
+
return { ...msg, content: parts };
|
|
7881
|
+
}
|
|
7746
7882
|
function stripBinaryContentForSummary(value) {
|
|
7747
7883
|
if (Array.isArray(value)) return value.map(stripBinaryContentForSummary);
|
|
7748
7884
|
if (!value || typeof value !== "object") return value;
|
|
@@ -7919,7 +8055,7 @@ function ensureEndsWithUserOrTool(messages) {
|
|
|
7919
8055
|
{ role: "user", content: [{ type: "text", text: "Please continue." }] }
|
|
7920
8056
|
];
|
|
7921
8057
|
}
|
|
7922
|
-
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager;
|
|
8058
|
+
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager, HARD_TRUNCATE_CHARS;
|
|
7923
8059
|
var init_context = __esm({
|
|
7924
8060
|
"src/agent/context.ts"() {
|
|
7925
8061
|
"use strict";
|
|
@@ -7930,6 +8066,7 @@ var init_context = __esm({
|
|
|
7930
8066
|
init_prompts();
|
|
7931
8067
|
init_sanitize_messages();
|
|
7932
8068
|
init_cap_image_count();
|
|
8069
|
+
init_sanitize_images();
|
|
7933
8070
|
init_model_limits();
|
|
7934
8071
|
TOOL_OUTPUT_TRIM_CHARS = 400;
|
|
7935
8072
|
COMPACTABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -7979,9 +8116,50 @@ ${summaryContent}`
|
|
|
7979
8116
|
messages = repairToolPairing(messages);
|
|
7980
8117
|
messages = ensureToolResultsFollowCalls(messages);
|
|
7981
8118
|
messages = ensureEndsWithUserOrTool(messages);
|
|
8119
|
+
messages = sanitizeInvalidImages(messages);
|
|
7982
8120
|
messages = capImageCount(messages);
|
|
8121
|
+
messages = this.enforceHardCap(messages);
|
|
7983
8122
|
return messages;
|
|
7984
8123
|
}
|
|
8124
|
+
/**
|
|
8125
|
+
* Drop oldest messages (and, as a last resort, hard-truncate text) until
|
|
8126
|
+
* the prompt fits the model's context window. Preserves any leading
|
|
8127
|
+
* summary system message and always keeps the most recent message, then
|
|
8128
|
+
* repairs tool pairing so dropping can't orphan a tool result.
|
|
8129
|
+
*/
|
|
8130
|
+
enforceHardCap(messages) {
|
|
8131
|
+
const { rollingTarget } = getModelLimits(this.modelId);
|
|
8132
|
+
const MAX_MESSAGES = 120;
|
|
8133
|
+
const tokenCeiling = Math.max(2e4, Math.floor(rollingTarget * 1.5));
|
|
8134
|
+
const tokens = messages.map((m) => this.messageTokens(m));
|
|
8135
|
+
let total = tokens.reduce((a, b) => a + b, 0);
|
|
8136
|
+
const hasLeadingSummary = messages.length > 0 && messages[0].role === "system";
|
|
8137
|
+
const firstBody = hasLeadingSummary ? 1 : 0;
|
|
8138
|
+
const lastIndex = messages.length - 1;
|
|
8139
|
+
const bodyCount = messages.length - firstBody;
|
|
8140
|
+
if (bodyCount <= MAX_MESSAGES && total <= tokenCeiling) return messages;
|
|
8141
|
+
let start = firstBody;
|
|
8142
|
+
const countDropTarget = messages.length - MAX_MESSAGES;
|
|
8143
|
+
while (start < countDropTarget && start < lastIndex) {
|
|
8144
|
+
total -= tokens[start];
|
|
8145
|
+
start += 1;
|
|
8146
|
+
}
|
|
8147
|
+
while (start < lastIndex && total > tokenCeiling) {
|
|
8148
|
+
total -= tokens[start];
|
|
8149
|
+
start += 1;
|
|
8150
|
+
}
|
|
8151
|
+
let out = hasLeadingSummary ? [messages[0], ...messages.slice(start)] : messages.slice(start);
|
|
8152
|
+
if (total > tokenCeiling) {
|
|
8153
|
+
out = out.map((m) => hardTruncateMessageText(m));
|
|
8154
|
+
}
|
|
8155
|
+
out = repairToolPairing(out);
|
|
8156
|
+
out = ensureToolResultsFollowCalls(out);
|
|
8157
|
+
out = ensureEndsWithUserOrTool(out);
|
|
8158
|
+
console.warn(
|
|
8159
|
+
`[Context] hard cap engaged for ${this.modelId}: trimmed ${messages.length}\u2192${out.length} msgs (ceiling ${tokenCeiling} est-tokens / ${MAX_MESSAGES} msgs).`
|
|
8160
|
+
);
|
|
8161
|
+
return out;
|
|
8162
|
+
}
|
|
7985
8163
|
// ---------------------------------------------------------------------------
|
|
7986
8164
|
// Phase 1 – Compact
|
|
7987
8165
|
// ---------------------------------------------------------------------------
|
|
@@ -8208,6 +8386,7 @@ ${summaryContent}`
|
|
|
8208
8386
|
this.summaries = [];
|
|
8209
8387
|
}
|
|
8210
8388
|
};
|
|
8389
|
+
HARD_TRUNCATE_CHARS = 6e3;
|
|
8211
8390
|
}
|
|
8212
8391
|
});
|
|
8213
8392
|
|
|
@@ -9069,7 +9248,29 @@ function markRespondedForThread(slackChannel2, threadTs) {
|
|
|
9069
9248
|
}
|
|
9070
9249
|
}
|
|
9071
9250
|
function resolveBatchOnTurnEnd(events, ok) {
|
|
9072
|
-
if (!ok)
|
|
9251
|
+
if (!ok) {
|
|
9252
|
+
for (const ev of events) {
|
|
9253
|
+
const key2 = eventKey(ev);
|
|
9254
|
+
const entry2 = ledger.get(key2);
|
|
9255
|
+
if (!entry2 || TERMINAL.has(entry2.state)) continue;
|
|
9256
|
+
entry2.state = "failed";
|
|
9257
|
+
entry2.updatedAt = Date.now();
|
|
9258
|
+
if (entry2.channel === "slack" && entry2.slackChannel) {
|
|
9259
|
+
if (entry2.messageTs) fireResultReaction(entry2.slackChannel, entry2.messageTs, "failed");
|
|
9260
|
+
if (entry2.threadTs) maybePostFallback(entry2.slackChannel, entry2.threadTs);
|
|
9261
|
+
}
|
|
9262
|
+
recordEvent({
|
|
9263
|
+
source: "daemon",
|
|
9264
|
+
status: "failed",
|
|
9265
|
+
channel: entry2.channel,
|
|
9266
|
+
sessionId: entry2.sessionId,
|
|
9267
|
+
error: "turn failed; not replayed (avoids context-overflow spiral)",
|
|
9268
|
+
textSnippet: entry2.event.content.slice(0, 200),
|
|
9269
|
+
meta: { ackKey: entry2.key, ackState: "failed" }
|
|
9270
|
+
});
|
|
9271
|
+
}
|
|
9272
|
+
return;
|
|
9273
|
+
}
|
|
9073
9274
|
for (const ev of events) {
|
|
9074
9275
|
const key2 = eventKey(ev);
|
|
9075
9276
|
const entry2 = ledger.get(key2);
|
|
@@ -9121,11 +9322,7 @@ function failEntry(entry2) {
|
|
|
9121
9322
|
if (entry2.channel === "slack" && entry2.slackChannel && entry2.messageTs) {
|
|
9122
9323
|
fireResultReaction(entry2.slackChannel, entry2.messageTs, "failed");
|
|
9123
9324
|
if (entry2.threadTs) {
|
|
9124
|
-
|
|
9125
|
-
entry2.slackChannel,
|
|
9126
|
-
entry2.threadTs,
|
|
9127
|
-
`: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.`
|
|
9128
|
-
);
|
|
9325
|
+
maybePostFallback(entry2.slackChannel, entry2.threadTs);
|
|
9129
9326
|
}
|
|
9130
9327
|
}
|
|
9131
9328
|
recordEvent({
|
|
@@ -9168,6 +9365,17 @@ function fireFallback(channel, threadTs, text) {
|
|
|
9168
9365
|
} catch {
|
|
9169
9366
|
}
|
|
9170
9367
|
}
|
|
9368
|
+
function maybePostFallback(channel, threadTs) {
|
|
9369
|
+
const now = Date.now();
|
|
9370
|
+
const last = lastFallbackAt.get(channel) ?? 0;
|
|
9371
|
+
if (now - last < FALLBACK_COOLDOWN_MS) return;
|
|
9372
|
+
lastFallbackAt.set(channel, now);
|
|
9373
|
+
fireFallback(
|
|
9374
|
+
channel,
|
|
9375
|
+
threadTs,
|
|
9376
|
+
"\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."
|
|
9377
|
+
);
|
|
9378
|
+
}
|
|
9171
9379
|
function startReconciler() {
|
|
9172
9380
|
if (reconcileTimer) return;
|
|
9173
9381
|
reconcileTimer = setInterval(() => {
|
|
@@ -9189,8 +9397,9 @@ function __listAcks() {
|
|
|
9189
9397
|
}
|
|
9190
9398
|
function __resetAcks() {
|
|
9191
9399
|
ledger.clear();
|
|
9400
|
+
lastFallbackAt.clear();
|
|
9192
9401
|
}
|
|
9193
|
-
var REPLAY_AFTER_MS, RECONCILE_EVERY_MS, MAX_ATTEMPTS, PRUNE_AFTER_MS, MAX_ENTRIES, TERMINAL, SEP, ledger, reconcileTimer;
|
|
9402
|
+
var REPLAY_AFTER_MS, RECONCILE_EVERY_MS, MAX_ATTEMPTS, PRUNE_AFTER_MS, FALLBACK_COOLDOWN_MS, lastFallbackAt, MAX_ENTRIES, TERMINAL, SEP, ledger, reconcileTimer;
|
|
9194
9403
|
var init_inbox_acks = __esm({
|
|
9195
9404
|
"src/orchestrator/inbox-acks.ts"() {
|
|
9196
9405
|
"use strict";
|
|
@@ -9201,6 +9410,8 @@ var init_inbox_acks = __esm({
|
|
|
9201
9410
|
RECONCILE_EVERY_MS = 6e4;
|
|
9202
9411
|
MAX_ATTEMPTS = 2;
|
|
9203
9412
|
PRUNE_AFTER_MS = 60 * 6e4;
|
|
9413
|
+
FALLBACK_COOLDOWN_MS = 15 * 6e4;
|
|
9414
|
+
lastFallbackAt = /* @__PURE__ */ new Map();
|
|
9204
9415
|
MAX_ENTRIES = 5e3;
|
|
9205
9416
|
TERMINAL = /* @__PURE__ */ new Set(["responded", "skipped", "handed_off", "failed"]);
|
|
9206
9417
|
SEP = "\u241F";
|