sparkecoder 0.1.71 → 0.1.73
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 -51
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +214 -53
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-CYNqPa6Z.d.ts → index-dbWF1hyW.d.ts} +57 -49
- package/dist/index.d.ts +5 -5
- package/dist/index.js +214 -53
- package/dist/index.js.map +1 -1
- package/dist/{schema-C7Mm4Ykn.d.ts → schema-XcP0dedO.d.ts} +3 -3
- package/dist/{search-CVVfuBPZ.d.ts → search-CCffrVJE.d.ts} +4 -4
- package/dist/server/index.js +214 -53
- package/dist/server/index.js.map +1 -1
- package/dist/skills/default/qa.md +53 -14
- package/dist/tools/index.d.ts +3 -3
- package/dist/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/src/skills/default/qa.md +53 -14
- 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/_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/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/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/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_ssgManifest.js +0 -0
- /package/web/.next/static/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_buildManifest.js +0 -0
- /package/web/.next/static/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{CvXz-9ALpAXX-GsCxrOdt → 2xYE9FvjZmf0tU0NF5Jvj}/_ssgManifest.js +0 -0
package/dist/agent/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'ai';
|
|
2
|
-
import '../schema-
|
|
3
|
-
export { A as Agent, a as AgentOptions, b as AgentRunOptions, c as AgentStreamResult, C as ContextManager, M as MessageAttachment, d as buildSystemPrompt, e as buildTaskPromptAddendum } from '../index-
|
|
4
|
-
import '../search-
|
|
2
|
+
import '../schema-XcP0dedO.js';
|
|
3
|
+
export { A as Agent, a as AgentOptions, b as AgentRunOptions, c as AgentStreamResult, C as ContextManager, M as MessageAttachment, d as buildSystemPrompt, e as buildTaskPromptAddendum } from '../index-dbWF1hyW.js';
|
|
4
|
+
import '../search-CCffrVJE.js';
|
|
5
5
|
import 'drizzle-orm/sqlite-core';
|
|
6
6
|
import 'zod';
|
package/dist/agent/index.js
CHANGED
|
@@ -1912,6 +1912,19 @@ import { z as z2 } from "zod";
|
|
|
1912
1912
|
import { exec as exec2 } from "child_process";
|
|
1913
1913
|
import { promisify as promisify2 } from "util";
|
|
1914
1914
|
|
|
1915
|
+
// src/utils/tokens.ts
|
|
1916
|
+
var CHARS_PER_TOKEN = 4;
|
|
1917
|
+
var MESSAGE_OVERHEAD_TOKENS = 4;
|
|
1918
|
+
function estimateTokens(text) {
|
|
1919
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
1920
|
+
}
|
|
1921
|
+
function estimateMessageTokens(messages) {
|
|
1922
|
+
return messages.reduce((total, msg) => {
|
|
1923
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
1924
|
+
return total + estimateTokens(content) + MESSAGE_OVERHEAD_TOKENS;
|
|
1925
|
+
}, 0);
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1915
1928
|
// src/utils/truncate.ts
|
|
1916
1929
|
var MAX_OUTPUT_CHARS = 1e4;
|
|
1917
1930
|
function truncateOutput(output, maxChars = MAX_OUTPUT_CHARS) {
|
|
@@ -5698,9 +5711,6 @@ ${conversationHistory}
|
|
|
5698
5711
|
Summary:`;
|
|
5699
5712
|
}
|
|
5700
5713
|
|
|
5701
|
-
// src/agent/context.ts
|
|
5702
|
-
init_config();
|
|
5703
|
-
|
|
5704
5714
|
// src/utils/sanitize-messages.ts
|
|
5705
5715
|
import { modelMessageSchema } from "ai";
|
|
5706
5716
|
function convertDatesToStrings(value) {
|
|
@@ -5837,79 +5847,237 @@ function sanitizeModelMessages(messages) {
|
|
|
5837
5847
|
return result;
|
|
5838
5848
|
}
|
|
5839
5849
|
|
|
5850
|
+
// src/agent/model-limits.ts
|
|
5851
|
+
var MODEL_LIMITS = {
|
|
5852
|
+
"anthropic/claude-opus-4-6": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
5853
|
+
"anthropic/claude-sonnet-4": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
5854
|
+
"anthropic/claude-3.5-sonnet": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
5855
|
+
"anthropic/claude-3-haiku": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
5856
|
+
"google/gemini-3-flash-preview": { contextWindow: 1e6, rollingTarget: 15e4 },
|
|
5857
|
+
"google/gemini-2.5-pro": { contextWindow: 1e6, rollingTarget: 15e4 },
|
|
5858
|
+
"google/gemini-2.5-flash": { contextWindow: 1e6, rollingTarget: 15e4 },
|
|
5859
|
+
"openai/gpt-4o": { contextWindow: 128e3, rollingTarget: 78e3 },
|
|
5860
|
+
"openai/gpt-4.1": { contextWindow: 1e6, rollingTarget: 15e4 },
|
|
5861
|
+
"openai/o3": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
5862
|
+
"xai/grok-3": { contextWindow: 131072, rollingTarget: 8e4 }
|
|
5863
|
+
};
|
|
5864
|
+
var DEFAULT_LIMITS = { contextWindow: 2e5, rollingTarget: 15e4 };
|
|
5865
|
+
var PREFIX_DEFAULTS = {
|
|
5866
|
+
"anthropic/": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
5867
|
+
"google/": { contextWindow: 1e6, rollingTarget: 15e4 },
|
|
5868
|
+
"openai/": { contextWindow: 128e3, rollingTarget: 78e3 },
|
|
5869
|
+
"xai/": { contextWindow: 131072, rollingTarget: 8e4 }
|
|
5870
|
+
};
|
|
5871
|
+
function getModelLimits(modelId) {
|
|
5872
|
+
const normalized = modelId.trim().toLowerCase();
|
|
5873
|
+
const exact = MODEL_LIMITS[normalized];
|
|
5874
|
+
if (exact) return exact;
|
|
5875
|
+
for (const [prefix, limits] of Object.entries(PREFIX_DEFAULTS)) {
|
|
5876
|
+
if (normalized.startsWith(prefix)) return limits;
|
|
5877
|
+
}
|
|
5878
|
+
return DEFAULT_LIMITS;
|
|
5879
|
+
}
|
|
5880
|
+
var SUMMARIZATION_MODEL = "google/gemini-3-flash-preview";
|
|
5881
|
+
var SUMMARY_CHUNK_TOKENS = 3e4;
|
|
5882
|
+
var SUMMARY_BUDGET_RATIO = 0.15;
|
|
5883
|
+
|
|
5840
5884
|
// src/agent/context.ts
|
|
5885
|
+
var TOOL_OUTPUT_TRIM_CHARS = 400;
|
|
5886
|
+
var COMPACTABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
5887
|
+
"read_file",
|
|
5888
|
+
"bash",
|
|
5889
|
+
"explore_agent",
|
|
5890
|
+
"code_graph"
|
|
5891
|
+
]);
|
|
5841
5892
|
var ContextManager = class {
|
|
5842
5893
|
sessionId;
|
|
5894
|
+
modelId;
|
|
5843
5895
|
maxContextChars;
|
|
5844
5896
|
keepRecentMessages;
|
|
5845
5897
|
autoSummarize;
|
|
5846
|
-
|
|
5898
|
+
summaries = [];
|
|
5847
5899
|
constructor(options) {
|
|
5848
5900
|
this.sessionId = options.sessionId;
|
|
5901
|
+
this.modelId = options.modelId;
|
|
5849
5902
|
this.maxContextChars = options.maxContextChars;
|
|
5850
5903
|
this.keepRecentMessages = options.keepRecentMessages;
|
|
5851
5904
|
this.autoSummarize = options.autoSummarize;
|
|
5852
5905
|
}
|
|
5853
5906
|
/**
|
|
5854
|
-
* Get messages for the current context
|
|
5855
|
-
* Returns ModelMessage[] that can be passed directly to streamText/generateText
|
|
5856
|
-
*
|
|
5857
|
-
* Includes self-repair: if messages from the database have been corrupted
|
|
5858
|
-
* (e.g., Date objects in tool outputs from parseDates), they are automatically
|
|
5859
|
-
* sanitized to conform to the AI SDK's ModelMessage schema.
|
|
5907
|
+
* Get messages for the current context, applying the three-phase pipeline.
|
|
5860
5908
|
*/
|
|
5861
5909
|
async getMessages() {
|
|
5862
|
-
let
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
if (this.autoSummarize
|
|
5866
|
-
|
|
5910
|
+
let messages = await messageQueries.getModelMessages(this.sessionId);
|
|
5911
|
+
messages = sanitizeModelMessages(messages);
|
|
5912
|
+
messages = this.compactOlderMessages(messages, this.keepRecentMessages);
|
|
5913
|
+
if (this.autoSummarize) {
|
|
5914
|
+
const { rollingTarget } = getModelLimits(this.modelId);
|
|
5915
|
+
const summaryBudget = Math.floor(rollingTarget * SUMMARY_BUDGET_RATIO);
|
|
5916
|
+
messages = await this.chunkSummarize(messages, rollingTarget);
|
|
5917
|
+
await this.rollSummaries(summaryBudget);
|
|
5867
5918
|
}
|
|
5868
|
-
if (this.
|
|
5869
|
-
|
|
5919
|
+
if (this.summaries.length > 0) {
|
|
5920
|
+
const summaryContent = this.summaries.join("\n\n---\n\n");
|
|
5921
|
+
messages = [
|
|
5870
5922
|
{
|
|
5871
5923
|
role: "system",
|
|
5872
5924
|
content: `[Previous conversation summary]
|
|
5873
|
-
${
|
|
5925
|
+
${summaryContent}`
|
|
5874
5926
|
},
|
|
5875
|
-
...
|
|
5927
|
+
...messages
|
|
5876
5928
|
];
|
|
5877
5929
|
}
|
|
5878
|
-
return
|
|
5930
|
+
return messages;
|
|
5879
5931
|
}
|
|
5932
|
+
// ---------------------------------------------------------------------------
|
|
5933
|
+
// Phase 1 – Compact
|
|
5934
|
+
// ---------------------------------------------------------------------------
|
|
5880
5935
|
/**
|
|
5881
|
-
*
|
|
5936
|
+
* Strip non-essential content from messages older than the most recent
|
|
5937
|
+
* `recentCount`. Operates in-memory only — does not touch the DB.
|
|
5882
5938
|
*/
|
|
5883
|
-
|
|
5884
|
-
if (messages.length <=
|
|
5885
|
-
|
|
5939
|
+
compactOlderMessages(messages, recentCount) {
|
|
5940
|
+
if (messages.length <= recentCount) return messages;
|
|
5941
|
+
const boundary = messages.length - recentCount;
|
|
5942
|
+
const olderMessages = messages.slice(0, boundary);
|
|
5943
|
+
const recentMessages = messages.slice(boundary);
|
|
5944
|
+
const compacted = [];
|
|
5945
|
+
for (const msg of olderMessages) {
|
|
5946
|
+
const processed = this.compactMessage(msg);
|
|
5947
|
+
if (processed) compacted.push(processed);
|
|
5886
5948
|
}
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5949
|
+
return [...compacted, ...recentMessages];
|
|
5950
|
+
}
|
|
5951
|
+
compactMessage(msg) {
|
|
5952
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
5953
|
+
const parts = [];
|
|
5954
|
+
for (const part of msg.content) {
|
|
5955
|
+
if (part.type === "tool-call" && part.toolName === "todo") continue;
|
|
5956
|
+
if (part.type === "tool-result" && part.toolName === "todo") continue;
|
|
5957
|
+
if (part.type === "reasoning" || part.type === "thinking") continue;
|
|
5958
|
+
if (part.type === "tool-result" && COMPACTABLE_TOOLS.has(part.toolName)) {
|
|
5959
|
+
parts.push(this.trimToolResult(part));
|
|
5960
|
+
continue;
|
|
5961
|
+
}
|
|
5962
|
+
parts.push(part);
|
|
5963
|
+
}
|
|
5964
|
+
if (parts.length === 0) return null;
|
|
5965
|
+
return { ...msg, content: parts };
|
|
5966
|
+
}
|
|
5967
|
+
trimToolResult(part) {
|
|
5968
|
+
const results = Array.isArray(part.result) ? part.result : [part.result];
|
|
5969
|
+
const trimmedResults = results.map((r) => {
|
|
5970
|
+
if (typeof r === "string" && r.length > TOOL_OUTPUT_TRIM_CHARS) {
|
|
5971
|
+
const half = Math.floor(TOOL_OUTPUT_TRIM_CHARS / 2);
|
|
5972
|
+
return r.slice(0, half) + `
|
|
5973
|
+
...[trimmed ${r.length - TOOL_OUTPUT_TRIM_CHARS} chars]...
|
|
5974
|
+
` + r.slice(-half);
|
|
5975
|
+
}
|
|
5976
|
+
if (r && typeof r === "object" && typeof r.text === "string" && r.text.length > TOOL_OUTPUT_TRIM_CHARS) {
|
|
5977
|
+
const half = Math.floor(TOOL_OUTPUT_TRIM_CHARS / 2);
|
|
5978
|
+
return {
|
|
5979
|
+
...r,
|
|
5980
|
+
text: r.text.slice(0, half) + `
|
|
5981
|
+
...[trimmed ${r.text.length - TOOL_OUTPUT_TRIM_CHARS} chars]...
|
|
5982
|
+
` + r.text.slice(-half)
|
|
5983
|
+
};
|
|
5984
|
+
}
|
|
5985
|
+
return r;
|
|
5986
|
+
});
|
|
5987
|
+
return {
|
|
5988
|
+
...part,
|
|
5989
|
+
result: Array.isArray(part.result) ? trimmedResults : trimmedResults[0]
|
|
5990
|
+
};
|
|
5991
|
+
}
|
|
5992
|
+
// ---------------------------------------------------------------------------
|
|
5993
|
+
// Phase 2 – Chunk-summarize
|
|
5994
|
+
// ---------------------------------------------------------------------------
|
|
5995
|
+
/**
|
|
5996
|
+
* While estimated tokens exceed `rollingTarget`, peel off the oldest
|
|
5997
|
+
* ~SUMMARY_CHUNK_TOKENS worth of messages, summarize them via the cheap
|
|
5998
|
+
* model, and prepend the summary.
|
|
5999
|
+
*/
|
|
6000
|
+
async chunkSummarize(messages, rollingTarget) {
|
|
6001
|
+
let totalTokens = estimateMessageTokens(messages);
|
|
6002
|
+
while (totalTokens > rollingTarget && messages.length > this.keepRecentMessages) {
|
|
6003
|
+
let chunkTokens = 0;
|
|
6004
|
+
let chunkEnd = 0;
|
|
6005
|
+
const maxChunkable = messages.length - this.keepRecentMessages;
|
|
6006
|
+
for (let i = 0; i < maxChunkable; i++) {
|
|
6007
|
+
const msgTokens = this.messageTokens(messages[i]);
|
|
6008
|
+
chunkTokens += msgTokens;
|
|
6009
|
+
chunkEnd = i + 1;
|
|
6010
|
+
if (chunkTokens >= SUMMARY_CHUNK_TOKENS) break;
|
|
6011
|
+
}
|
|
6012
|
+
if (chunkEnd === 0) break;
|
|
6013
|
+
const chunk = messages.slice(0, chunkEnd);
|
|
6014
|
+
const remaining = messages.slice(chunkEnd);
|
|
6015
|
+
const summary = await this.summarizeChunk(chunk);
|
|
6016
|
+
if (summary) {
|
|
6017
|
+
this.summaries.push(summary);
|
|
6018
|
+
console.log(
|
|
6019
|
+
`[Context] Summarized ${chunk.length} messages (~${chunkTokens} tokens) into ${estimateTokens(summary)} tokens`
|
|
6020
|
+
);
|
|
6021
|
+
}
|
|
6022
|
+
messages = remaining;
|
|
6023
|
+
totalTokens = estimateMessageTokens(messages);
|
|
6024
|
+
}
|
|
6025
|
+
return messages;
|
|
6026
|
+
}
|
|
6027
|
+
async summarizeChunk(chunk) {
|
|
6028
|
+
const historyText = chunk.map((msg) => {
|
|
5891
6029
|
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
5892
6030
|
return `[${msg.role}]: ${content}`;
|
|
5893
6031
|
}).join("\n\n");
|
|
5894
6032
|
try {
|
|
5895
|
-
const config = getConfig();
|
|
5896
|
-
const summaryPrompt = createSummaryPrompt(historyText);
|
|
5897
6033
|
const result = await generateText2({
|
|
5898
|
-
model: resolveModel(
|
|
5899
|
-
prompt:
|
|
6034
|
+
model: resolveModel(SUMMARIZATION_MODEL),
|
|
6035
|
+
prompt: createSummaryPrompt(historyText)
|
|
5900
6036
|
});
|
|
5901
|
-
|
|
5902
|
-
console.log(`[Context] Summarized ${oldMessages.length} messages into ${this.summary.length} chars`);
|
|
5903
|
-
return recentMessages;
|
|
6037
|
+
return result.text;
|
|
5904
6038
|
} catch (error) {
|
|
5905
|
-
console.error("[Context]
|
|
5906
|
-
return
|
|
6039
|
+
console.error("[Context] Chunk summarization failed:", error);
|
|
6040
|
+
return null;
|
|
5907
6041
|
}
|
|
5908
6042
|
}
|
|
6043
|
+
// ---------------------------------------------------------------------------
|
|
6044
|
+
// Phase 3 – Roll summaries
|
|
6045
|
+
// ---------------------------------------------------------------------------
|
|
5909
6046
|
/**
|
|
5910
|
-
*
|
|
5911
|
-
*
|
|
6047
|
+
* If accumulated summaries exceed `budget` tokens, re-summarize them
|
|
6048
|
+
* into a single condensed summary.
|
|
5912
6049
|
*/
|
|
6050
|
+
async rollSummaries(budget) {
|
|
6051
|
+
if (this.summaries.length <= 1) return;
|
|
6052
|
+
const totalSummaryTokens = this.summaries.reduce(
|
|
6053
|
+
(t, s) => t + estimateTokens(s),
|
|
6054
|
+
0
|
|
6055
|
+
);
|
|
6056
|
+
if (totalSummaryTokens <= budget) return;
|
|
6057
|
+
const combined = this.summaries.join("\n\n---\n\n");
|
|
6058
|
+
try {
|
|
6059
|
+
const result = await generateText2({
|
|
6060
|
+
model: resolveModel(SUMMARIZATION_MODEL),
|
|
6061
|
+
prompt: createSummaryPrompt(combined)
|
|
6062
|
+
});
|
|
6063
|
+
console.log(
|
|
6064
|
+
`[Context] Rolled ${this.summaries.length} summaries (${totalSummaryTokens} tokens) into ${estimateTokens(result.text)} tokens`
|
|
6065
|
+
);
|
|
6066
|
+
this.summaries = [result.text];
|
|
6067
|
+
} catch (error) {
|
|
6068
|
+
console.error("[Context] Summary rolling failed:", error);
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
// ---------------------------------------------------------------------------
|
|
6072
|
+
// Helpers
|
|
6073
|
+
// ---------------------------------------------------------------------------
|
|
6074
|
+
messageTokens(msg) {
|
|
6075
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
6076
|
+
return estimateTokens(content) + 4;
|
|
6077
|
+
}
|
|
6078
|
+
// ---------------------------------------------------------------------------
|
|
6079
|
+
// Public API (unchanged)
|
|
6080
|
+
// ---------------------------------------------------------------------------
|
|
5913
6081
|
async addUserMessage(content) {
|
|
5914
6082
|
const userMessage = {
|
|
5915
6083
|
role: "user",
|
|
@@ -5917,30 +6085,22 @@ ${this.summary}`
|
|
|
5917
6085
|
};
|
|
5918
6086
|
await messageQueries.create(this.sessionId, userMessage);
|
|
5919
6087
|
}
|
|
5920
|
-
/**
|
|
5921
|
-
* Add response messages from AI SDK directly
|
|
5922
|
-
* This is the preferred method - use result.response.messages from streamText/generateText
|
|
5923
|
-
*/
|
|
5924
6088
|
async addResponseMessages(messages) {
|
|
5925
6089
|
await messageQueries.addMany(this.sessionId, messages);
|
|
5926
6090
|
}
|
|
5927
|
-
/**
|
|
5928
|
-
* Get current context statistics
|
|
5929
|
-
*/
|
|
5930
6091
|
async getStats() {
|
|
5931
6092
|
const messages = await messageQueries.getModelMessages(this.sessionId);
|
|
5932
6093
|
return {
|
|
5933
6094
|
messageCount: messages.length,
|
|
5934
6095
|
contextChars: calculateContextSize(messages),
|
|
5935
|
-
|
|
6096
|
+
estimatedTokens: estimateMessageTokens(messages),
|
|
6097
|
+
hasSummary: this.summaries.length > 0,
|
|
6098
|
+
summaryCount: this.summaries.length
|
|
5936
6099
|
};
|
|
5937
6100
|
}
|
|
5938
|
-
/**
|
|
5939
|
-
* Clear all messages in the context
|
|
5940
|
-
*/
|
|
5941
6101
|
async clear() {
|
|
5942
6102
|
await messageQueries.deleteBySession(this.sessionId);
|
|
5943
|
-
this.
|
|
6103
|
+
this.summaries = [];
|
|
5944
6104
|
}
|
|
5945
6105
|
};
|
|
5946
6106
|
|
|
@@ -6026,6 +6186,7 @@ var Agent = class _Agent {
|
|
|
6026
6186
|
}
|
|
6027
6187
|
const context = new ContextManager({
|
|
6028
6188
|
sessionId: session.id,
|
|
6189
|
+
modelId: session.model || config.defaultModel,
|
|
6029
6190
|
maxContextChars: config.context?.maxChars || 2e5,
|
|
6030
6191
|
keepRecentMessages: config.context?.keepRecentMessages || 10,
|
|
6031
6192
|
autoSummarize: config.context?.autoSummarize ?? true
|