sparkecoder 0.1.121 → 0.1.123
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 +240 -26
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +588 -162
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-DczYH89U.d.ts → index-Bcz0aCAR.d.ts} +104 -104
- package/dist/index.d.ts +5 -5
- package/dist/index.js +549 -123
- package/dist/index.js.map +1 -1
- package/dist/{schema-DxrKyetI.d.ts → schema-BWbWmfDQ.d.ts} +3 -3
- package/dist/{search-CVVfuBPZ.d.ts → search-DOzC4ojH.d.ts} +4 -4
- package/dist/server/index.js +549 -123
- 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/_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/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/wP9z41wtqT4k-O6AlEXqw → MP4p8_EldjbZ69dONoEcM}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/wP9z41wtqT4k-O6AlEXqw → MP4p8_EldjbZ69dONoEcM}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/wP9z41wtqT4k-O6AlEXqw → MP4p8_EldjbZ69dONoEcM}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{wP9z41wtqT4k-O6AlEXqw → static/MP4p8_EldjbZ69dONoEcM}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{wP9z41wtqT4k-O6AlEXqw → static/MP4p8_EldjbZ69dONoEcM}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{wP9z41wtqT4k-O6AlEXqw → static/MP4p8_EldjbZ69dONoEcM}/_ssgManifest.js +0 -0
- /package/web/.next/static/{wP9z41wtqT4k-O6AlEXqw → MP4p8_EldjbZ69dONoEcM}/_buildManifest.js +0 -0
- /package/web/.next/static/{wP9z41wtqT4k-O6AlEXqw → MP4p8_EldjbZ69dONoEcM}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{wP9z41wtqT4k-O6AlEXqw → MP4p8_EldjbZ69dONoEcM}/_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-BWbWmfDQ.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-Bcz0aCAR.js';
|
|
4
|
+
import '../search-DOzC4ojH.js';
|
|
5
5
|
import 'drizzle-orm/sqlite-core';
|
|
6
6
|
import 'zod';
|
package/dist/agent/index.js
CHANGED
|
@@ -1894,7 +1894,7 @@ __export(recorder_exports, {
|
|
|
1894
1894
|
import { exec as exec5 } from "child_process";
|
|
1895
1895
|
import { promisify as promisify5 } from "util";
|
|
1896
1896
|
import { writeFile as writeFile5, mkdir as mkdir4, readFile as readFile11, unlink as unlink2, readdir as readdir5, rm } from "fs/promises";
|
|
1897
|
-
import { join as
|
|
1897
|
+
import { join as join11 } from "path";
|
|
1898
1898
|
import { tmpdir } from "os";
|
|
1899
1899
|
import { nanoid as nanoid7 } from "nanoid";
|
|
1900
1900
|
async function checkFfmpeg() {
|
|
@@ -1951,21 +1951,21 @@ var init_recorder = __esm({
|
|
|
1951
1951
|
*/
|
|
1952
1952
|
async encode() {
|
|
1953
1953
|
if (this.frames.length === 0) return null;
|
|
1954
|
-
const workDir =
|
|
1954
|
+
const workDir = join11(tmpdir(), `sparkecoder-recording-${nanoid7(8)}`);
|
|
1955
1955
|
await mkdir4(workDir, { recursive: true });
|
|
1956
1956
|
try {
|
|
1957
1957
|
for (let i = 0; i < this.frames.length; i++) {
|
|
1958
|
-
const framePath =
|
|
1958
|
+
const framePath = join11(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
|
|
1959
1959
|
await writeFile5(framePath, this.frames[i].data);
|
|
1960
1960
|
}
|
|
1961
1961
|
const duration = (this.frames[this.frames.length - 1].timestamp - this.frames[0].timestamp) / 1e3;
|
|
1962
1962
|
const fps = duration > 0 ? Math.round(this.frames.length / duration) : 10;
|
|
1963
1963
|
const clampedFps = Math.max(1, Math.min(fps, 30));
|
|
1964
|
-
const outputPath =
|
|
1964
|
+
const outputPath = join11(workDir, `recording_${this.sessionId}.mp4`);
|
|
1965
1965
|
const hasFfmpeg = await checkFfmpeg();
|
|
1966
1966
|
if (hasFfmpeg) {
|
|
1967
1967
|
await execAsync5(
|
|
1968
|
-
`ffmpeg -y -framerate ${clampedFps} -i "${
|
|
1968
|
+
`ffmpeg -y -framerate ${clampedFps} -i "${join11(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
|
|
1969
1969
|
{ timeout: 12e4 }
|
|
1970
1970
|
);
|
|
1971
1971
|
} else {
|
|
@@ -1977,7 +1977,7 @@ var init_recorder = __esm({
|
|
|
1977
1977
|
const files = await readdir5(workDir);
|
|
1978
1978
|
for (const f of files) {
|
|
1979
1979
|
if (f.startsWith("frame_")) {
|
|
1980
|
-
await unlink2(
|
|
1980
|
+
await unlink2(join11(workDir, f)).catch(() => {
|
|
1981
1981
|
});
|
|
1982
1982
|
}
|
|
1983
1983
|
}
|
|
@@ -2835,10 +2835,10 @@ async function resizeImageIfNeeded(buffer, mediaType) {
|
|
|
2835
2835
|
const willConvertToJpeg = isPng && (needsShrink || buffer.length > 2 * 1024 * 1024);
|
|
2836
2836
|
const outputMediaType = willConvertToJpeg || !isPng ? "image/jpeg" : "image/png";
|
|
2837
2837
|
const ext = outputMediaType === "image/png" ? ".png" : ".jpg";
|
|
2838
|
-
const
|
|
2839
|
-
if (existsSync3(
|
|
2838
|
+
const cachePath2 = join3(cacheDir, key2 + ext);
|
|
2839
|
+
if (existsSync3(cachePath2)) {
|
|
2840
2840
|
console.log(`[image-resize] Cache hit for ${width}x${height} image`);
|
|
2841
|
-
return { buffer: readFileSync2(
|
|
2841
|
+
return { buffer: readFileSync2(cachePath2), mediaType: outputMediaType };
|
|
2842
2842
|
}
|
|
2843
2843
|
let pipeline = sharp(buffer);
|
|
2844
2844
|
if (needsResize) {
|
|
@@ -2863,7 +2863,7 @@ async function resizeImageIfNeeded(buffer, mediaType) {
|
|
|
2863
2863
|
}
|
|
2864
2864
|
finalMediaType = "image/jpeg";
|
|
2865
2865
|
}
|
|
2866
|
-
writeFileSync2(
|
|
2866
|
+
writeFileSync2(cachePath2, result);
|
|
2867
2867
|
const resultMeta = await sharp(result).metadata();
|
|
2868
2868
|
console.log(
|
|
2869
2869
|
`[image-resize] ${width}x${height} -> ${resultMeta.width}x${resultMeta.height} (${(buffer.length / 1024).toFixed(0)}KB -> ${(result.length / 1024).toFixed(0)}KB, ${finalMediaType})`
|
|
@@ -6865,6 +6865,7 @@ ${summaryContent}`
|
|
|
6865
6865
|
];
|
|
6866
6866
|
}
|
|
6867
6867
|
messages = repairToolPairing(messages);
|
|
6868
|
+
messages = ensureToolResultsFollowCalls(messages);
|
|
6868
6869
|
messages = ensureEndsWithUserOrTool(messages);
|
|
6869
6870
|
return messages;
|
|
6870
6871
|
}
|
|
@@ -7059,7 +7060,7 @@ ${summaryContent}`
|
|
|
7059
7060
|
}
|
|
7060
7061
|
}
|
|
7061
7062
|
async addResponseMessages(messages) {
|
|
7062
|
-
const safe = repairToolPairing(messages);
|
|
7063
|
+
const safe = ensureToolResultsFollowCalls(repairToolPairing(messages));
|
|
7063
7064
|
await messageQueries.addMany(this.sessionId, safe);
|
|
7064
7065
|
try {
|
|
7065
7066
|
const { appendTurn: appendTurn2, flattenContent: flattenContent2 } = await Promise.resolve().then(() => (init_conversation_archive(), conversation_archive_exports));
|
|
@@ -7104,6 +7105,84 @@ function stripOrphanedToolResults(msg, removedIds) {
|
|
|
7104
7105
|
if (parts.length === 0) return null;
|
|
7105
7106
|
return { ...msg, content: parts };
|
|
7106
7107
|
}
|
|
7108
|
+
function wrapToolsNeverThrow(tools) {
|
|
7109
|
+
if (!tools || typeof tools !== "object") return tools;
|
|
7110
|
+
const wrapped = {};
|
|
7111
|
+
for (const [name, t] of Object.entries(tools)) {
|
|
7112
|
+
if (!t || typeof t.execute !== "function") {
|
|
7113
|
+
wrapped[name] = t;
|
|
7114
|
+
continue;
|
|
7115
|
+
}
|
|
7116
|
+
const original = t.execute;
|
|
7117
|
+
wrapped[name] = {
|
|
7118
|
+
...t,
|
|
7119
|
+
execute: async (input, opts) => {
|
|
7120
|
+
try {
|
|
7121
|
+
return await original.call(t, input, opts);
|
|
7122
|
+
} catch (err) {
|
|
7123
|
+
const message = err?.message ?? String(err);
|
|
7124
|
+
console.warn(`[tool:${name}] threw \u2014 converted to error result so the tool-call isn't orphaned:`, message);
|
|
7125
|
+
return {
|
|
7126
|
+
__error: true,
|
|
7127
|
+
tool: name,
|
|
7128
|
+
message,
|
|
7129
|
+
note: "Tool execution threw an exception. The agent should treat this as a failed tool call and decide how to recover."
|
|
7130
|
+
};
|
|
7131
|
+
}
|
|
7132
|
+
}
|
|
7133
|
+
};
|
|
7134
|
+
}
|
|
7135
|
+
return wrapped;
|
|
7136
|
+
}
|
|
7137
|
+
function ensureToolResultsFollowCalls(messages) {
|
|
7138
|
+
if (!Array.isArray(messages) || messages.length < 3) return messages;
|
|
7139
|
+
let mutated = false;
|
|
7140
|
+
const result = messages.slice();
|
|
7141
|
+
let i = 0;
|
|
7142
|
+
while (i < result.length) {
|
|
7143
|
+
const msg = result[i];
|
|
7144
|
+
if (msg?.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
7145
|
+
i++;
|
|
7146
|
+
continue;
|
|
7147
|
+
}
|
|
7148
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
7149
|
+
for (const part of msg.content) {
|
|
7150
|
+
if (part?.type === "tool-call" && typeof part.toolCallId === "string") {
|
|
7151
|
+
callIds.add(part.toolCallId);
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
if (callIds.size === 0) {
|
|
7155
|
+
i++;
|
|
7156
|
+
continue;
|
|
7157
|
+
}
|
|
7158
|
+
let toolIdx = -1;
|
|
7159
|
+
for (let j = i + 1; j < result.length; j++) {
|
|
7160
|
+
const m = result[j];
|
|
7161
|
+
if (m?.role === "assistant" && Array.isArray(m.content) && m.content.some((p) => p?.type === "tool-call")) {
|
|
7162
|
+
break;
|
|
7163
|
+
}
|
|
7164
|
+
if (m?.role === "tool" && Array.isArray(m.content)) {
|
|
7165
|
+
const answersOne = m.content.some(
|
|
7166
|
+
(p) => p?.type === "tool-result" && typeof p.toolCallId === "string" && callIds.has(p.toolCallId)
|
|
7167
|
+
);
|
|
7168
|
+
if (answersOne) {
|
|
7169
|
+
toolIdx = j;
|
|
7170
|
+
break;
|
|
7171
|
+
}
|
|
7172
|
+
}
|
|
7173
|
+
}
|
|
7174
|
+
if (toolIdx > i + 1) {
|
|
7175
|
+
const [toolMsg] = result.splice(toolIdx, 1);
|
|
7176
|
+
result.splice(i + 1, 0, toolMsg);
|
|
7177
|
+
mutated = true;
|
|
7178
|
+
console.warn(
|
|
7179
|
+
`[tool-repair] Reordered tool-result message from index ${toolIdx} to ${i + 1} to immediately follow assistant tool-call(s) (${[...callIds].join(", ")}). ${toolIdx - i - 1} message(s) were wedged between them.`
|
|
7180
|
+
);
|
|
7181
|
+
}
|
|
7182
|
+
i++;
|
|
7183
|
+
}
|
|
7184
|
+
return mutated ? result : messages;
|
|
7185
|
+
}
|
|
7107
7186
|
function repairToolPairing(messages) {
|
|
7108
7187
|
const toolCallIds = /* @__PURE__ */ new Set();
|
|
7109
7188
|
const toolResultIds = /* @__PURE__ */ new Set();
|
|
@@ -7181,6 +7260,100 @@ var webChannel = {
|
|
|
7181
7260
|
|
|
7182
7261
|
// src/integrations/slack/client.ts
|
|
7183
7262
|
init_config();
|
|
7263
|
+
|
|
7264
|
+
// src/integrations/slack/persistence.ts
|
|
7265
|
+
init_config();
|
|
7266
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync3, renameSync } from "fs";
|
|
7267
|
+
import { join as join9, dirname as dirname6 } from "path";
|
|
7268
|
+
var FILENAME = "slack-cache.json";
|
|
7269
|
+
var FILE_VERSION = 1;
|
|
7270
|
+
var SAVE_DEBOUNCE_MS = 500;
|
|
7271
|
+
var MAX_THREADS = 5e3;
|
|
7272
|
+
var loaded = false;
|
|
7273
|
+
var userMap = /* @__PURE__ */ new Map();
|
|
7274
|
+
var threadMap = /* @__PURE__ */ new Map();
|
|
7275
|
+
var dirty = false;
|
|
7276
|
+
var saveTimer = null;
|
|
7277
|
+
function cachePath() {
|
|
7278
|
+
return join9(ensureAppDataDirectory(), FILENAME);
|
|
7279
|
+
}
|
|
7280
|
+
function load() {
|
|
7281
|
+
if (loaded) return;
|
|
7282
|
+
loaded = true;
|
|
7283
|
+
const path = cachePath();
|
|
7284
|
+
if (!existsSync16(path)) return;
|
|
7285
|
+
try {
|
|
7286
|
+
const raw = readFileSync7(path, "utf-8");
|
|
7287
|
+
const parsed = JSON.parse(raw);
|
|
7288
|
+
if (parsed?.version !== FILE_VERSION) return;
|
|
7289
|
+
const now = Date.now();
|
|
7290
|
+
for (const [id, e] of Object.entries(parsed.users || {})) {
|
|
7291
|
+
if (e && typeof e.expiresAt === "number" && e.expiresAt > now) {
|
|
7292
|
+
userMap.set(id, { name: e.name ?? null, expiresAt: e.expiresAt });
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7295
|
+
for (const [key2, e] of Object.entries(parsed.threads || {})) {
|
|
7296
|
+
if (e && typeof e.expiresAt === "number" && e.expiresAt > now) {
|
|
7297
|
+
threadMap.set(key2, { owned: !!e.owned, expiresAt: e.expiresAt });
|
|
7298
|
+
}
|
|
7299
|
+
}
|
|
7300
|
+
} catch (err) {
|
|
7301
|
+
console.warn(`[slack] could not load ${FILENAME}:`, err?.message || err);
|
|
7302
|
+
}
|
|
7303
|
+
}
|
|
7304
|
+
function evictIfOversized(map, max) {
|
|
7305
|
+
if (map.size <= max) return;
|
|
7306
|
+
const sorted = [...map.entries()].sort((a, b) => a[1].expiresAt - b[1].expiresAt);
|
|
7307
|
+
const toRemove = map.size - max;
|
|
7308
|
+
for (let i = 0; i < toRemove; i++) map.delete(sorted[i][0]);
|
|
7309
|
+
}
|
|
7310
|
+
function scheduleSave() {
|
|
7311
|
+
dirty = true;
|
|
7312
|
+
if (saveTimer) return;
|
|
7313
|
+
saveTimer = setTimeout(() => {
|
|
7314
|
+
saveTimer = null;
|
|
7315
|
+
if (dirty) saveSync();
|
|
7316
|
+
}, SAVE_DEBOUNCE_MS);
|
|
7317
|
+
saveTimer.unref?.();
|
|
7318
|
+
}
|
|
7319
|
+
function saveSync() {
|
|
7320
|
+
dirty = false;
|
|
7321
|
+
try {
|
|
7322
|
+
const path = cachePath();
|
|
7323
|
+
const dir = dirname6(path);
|
|
7324
|
+
if (!existsSync16(dir)) mkdirSync6(dir, { recursive: true });
|
|
7325
|
+
const payload = {
|
|
7326
|
+
version: FILE_VERSION,
|
|
7327
|
+
users: Object.fromEntries(userMap),
|
|
7328
|
+
threads: Object.fromEntries(threadMap)
|
|
7329
|
+
};
|
|
7330
|
+
const tmp = `${path}.tmp`;
|
|
7331
|
+
writeFileSync3(tmp, JSON.stringify(payload), "utf-8");
|
|
7332
|
+
renameSync(tmp, path);
|
|
7333
|
+
} catch (err) {
|
|
7334
|
+
console.warn(`[slack] could not persist ${FILENAME}:`, err?.message || err);
|
|
7335
|
+
}
|
|
7336
|
+
}
|
|
7337
|
+
var exitHooked = false;
|
|
7338
|
+
function hookExit() {
|
|
7339
|
+
if (exitHooked) return;
|
|
7340
|
+
exitHooked = true;
|
|
7341
|
+
const flush2 = () => {
|
|
7342
|
+
if (dirty) saveSync();
|
|
7343
|
+
};
|
|
7344
|
+
process.once("beforeExit", flush2);
|
|
7345
|
+
process.once("SIGINT", flush2);
|
|
7346
|
+
process.once("SIGTERM", flush2);
|
|
7347
|
+
}
|
|
7348
|
+
function setCachedThreadOwnership(key2, entry2) {
|
|
7349
|
+
load();
|
|
7350
|
+
hookExit();
|
|
7351
|
+
threadMap.set(key2, entry2);
|
|
7352
|
+
evictIfOversized(threadMap, MAX_THREADS);
|
|
7353
|
+
scheduleSave();
|
|
7354
|
+
}
|
|
7355
|
+
|
|
7356
|
+
// src/integrations/slack/client.ts
|
|
7184
7357
|
function readSlackConfig() {
|
|
7185
7358
|
try {
|
|
7186
7359
|
const cfg = getConfig();
|
|
@@ -7221,6 +7394,18 @@ function isSlackConfigured() {
|
|
|
7221
7394
|
}
|
|
7222
7395
|
var USER_TTL_MS = 60 * 60 * 1e3;
|
|
7223
7396
|
var USER_FAIL_TTL_MS = 5 * 60 * 1e3;
|
|
7397
|
+
var THREAD_OWNED_TTL_MS = 60 * 60 * 1e3;
|
|
7398
|
+
var THREAD_NEG_TTL_MS = 5 * 60 * 1e3;
|
|
7399
|
+
function threadCacheKey(channel, threadTs) {
|
|
7400
|
+
return `${channel}\u241F${threadTs}`;
|
|
7401
|
+
}
|
|
7402
|
+
function noteBotPostedInThread(channel, threadTs) {
|
|
7403
|
+
if (!channel || !threadTs) return;
|
|
7404
|
+
setCachedThreadOwnership(threadCacheKey(channel, threadTs), {
|
|
7405
|
+
owned: true,
|
|
7406
|
+
expiresAt: Date.now() + THREAD_OWNED_TTL_MS
|
|
7407
|
+
});
|
|
7408
|
+
}
|
|
7224
7409
|
|
|
7225
7410
|
// src/integrations/channels/slack.ts
|
|
7226
7411
|
var ownedThreads = /* @__PURE__ */ new Set();
|
|
@@ -7245,6 +7430,7 @@ var slackChannel = {
|
|
|
7245
7430
|
if (!result.ok) throw new Error(`slack post failed: ${result.error}`);
|
|
7246
7431
|
if (r.slackChannel && r.threadTs) {
|
|
7247
7432
|
markThreadOwned(r.slackChannel, r.threadTs);
|
|
7433
|
+
noteBotPostedInThread(r.slackChannel, r.threadTs);
|
|
7248
7434
|
}
|
|
7249
7435
|
},
|
|
7250
7436
|
displayLabel(ref) {
|
|
@@ -7761,8 +7947,8 @@ import { createMCPClient } from "@ai-sdk/mcp";
|
|
|
7761
7947
|
// src/integrations/mcp/store.ts
|
|
7762
7948
|
init_config();
|
|
7763
7949
|
import { nanoid as nanoid6 } from "nanoid";
|
|
7764
|
-
import { existsSync as
|
|
7765
|
-
import { resolve as resolve10, join as
|
|
7950
|
+
import { existsSync as existsSync17, readFileSync as readFileSync8 } from "fs";
|
|
7951
|
+
import { resolve as resolve10, join as join10 } from "path";
|
|
7766
7952
|
function readServers() {
|
|
7767
7953
|
try {
|
|
7768
7954
|
const cfg = getConfig();
|
|
@@ -7774,12 +7960,12 @@ function readServers() {
|
|
|
7774
7960
|
function refreshMcpServersFromDisk() {
|
|
7775
7961
|
const candidates = [
|
|
7776
7962
|
resolve10(process.cwd(), "sparkecoder.config.json"),
|
|
7777
|
-
|
|
7963
|
+
join10(ensureAppDataDirectory(), "sparkecoder.config.json")
|
|
7778
7964
|
];
|
|
7779
7965
|
for (const path of candidates) {
|
|
7780
|
-
if (!
|
|
7966
|
+
if (!existsSync17(path)) continue;
|
|
7781
7967
|
try {
|
|
7782
|
-
const raw = JSON.parse(
|
|
7968
|
+
const raw = JSON.parse(readFileSync8(path, "utf-8"));
|
|
7783
7969
|
const servers2 = Array.isArray(raw?.mcp?.servers) ? raw.mcp.servers : [];
|
|
7784
7970
|
setMcpServers(servers2);
|
|
7785
7971
|
return servers2;
|
|
@@ -8191,7 +8377,7 @@ ${prompt}` });
|
|
|
8191
8377
|
const config = getConfig();
|
|
8192
8378
|
const userContent = this.buildUserMessageContent(options.prompt, options.attachments);
|
|
8193
8379
|
if (!options.skipSaveUserMessage) {
|
|
8194
|
-
this.context.addUserMessage(userContent);
|
|
8380
|
+
await this.context.addUserMessage(userContent);
|
|
8195
8381
|
}
|
|
8196
8382
|
await sessionQueries.updateStatus(this.session.id, "active");
|
|
8197
8383
|
let systemPrompt = await buildSystemPrompt({
|
|
@@ -8217,7 +8403,8 @@ ${personality.trim()}`;
|
|
|
8217
8403
|
}
|
|
8218
8404
|
const messages = await this.context.getMessages();
|
|
8219
8405
|
const tools = options.onToolProgress ? await this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
|
|
8220
|
-
const
|
|
8406
|
+
const approvalWrapped = this.wrapToolsWithApproval(options, tools);
|
|
8407
|
+
const wrappedTools = wrapToolsNeverThrow(approvalWrapped);
|
|
8221
8408
|
const useAnthropic = isAnthropicModel(this.session.model);
|
|
8222
8409
|
const stream = streamText2({
|
|
8223
8410
|
model: resolveModel(this.session.model),
|
|
@@ -8231,6 +8418,18 @@ ${personality.trim()}`;
|
|
|
8231
8418
|
providerOptions: useAnthropic ? {
|
|
8232
8419
|
anthropic: getAnthropicProviderOptions(this.session.model, { toolStreaming: true })
|
|
8233
8420
|
} : void 0,
|
|
8421
|
+
// Run repairToolPairing before EVERY step's model call, not just the
|
|
8422
|
+
// first one. The AI SDK's multi-step loop can otherwise feed the model
|
|
8423
|
+
// a prompt containing an orphan tool-call (e.g. when a previous step's
|
|
8424
|
+
// tool result was lost, dropped during compaction, or the stream was
|
|
8425
|
+
// aborted mid-tool). Repairing in `prepareStep` guarantees no orphan
|
|
8426
|
+
// ever reaches the model and we never hit AI_MissingToolResultsError.
|
|
8427
|
+
prepareStep: async ({ messages: stepMessages }) => {
|
|
8428
|
+
const paired = repairToolPairing(stepMessages);
|
|
8429
|
+
const ordered = ensureToolResultsFollowCalls(paired);
|
|
8430
|
+
if (ordered === stepMessages) return {};
|
|
8431
|
+
return { messages: ordered };
|
|
8432
|
+
},
|
|
8234
8433
|
onStepFinish: async (step) => {
|
|
8235
8434
|
options.onStepFinish?.(step);
|
|
8236
8435
|
},
|
|
@@ -8242,7 +8441,7 @@ ${personality.trim()}`;
|
|
|
8242
8441
|
const result = await stream;
|
|
8243
8442
|
const response = await result.response;
|
|
8244
8443
|
const responseMessages = response.messages;
|
|
8245
|
-
this.context.addResponseMessages(responseMessages);
|
|
8444
|
+
await this.context.addResponseMessages(responseMessages);
|
|
8246
8445
|
};
|
|
8247
8446
|
return {
|
|
8248
8447
|
sessionId: this.session.id,
|
|
@@ -8256,7 +8455,7 @@ ${personality.trim()}`;
|
|
|
8256
8455
|
*/
|
|
8257
8456
|
async run(options) {
|
|
8258
8457
|
const config = getConfig();
|
|
8259
|
-
this.context.addUserMessage(options.prompt);
|
|
8458
|
+
await this.context.addUserMessage(options.prompt);
|
|
8260
8459
|
const systemPrompt = await buildSystemPrompt({
|
|
8261
8460
|
workingDirectory: this.session.workingDirectory,
|
|
8262
8461
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
@@ -8266,7 +8465,7 @@ ${personality.trim()}`;
|
|
|
8266
8465
|
});
|
|
8267
8466
|
const messages = await this.context.getMessages();
|
|
8268
8467
|
const tools = options.onToolProgress ? await this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
|
|
8269
|
-
const wrappedTools = this.wrapToolsWithApproval(options, tools);
|
|
8468
|
+
const wrappedTools = wrapToolsNeverThrow(this.wrapToolsWithApproval(options, tools));
|
|
8270
8469
|
const useAnthropic = isAnthropicModel(this.session.model);
|
|
8271
8470
|
const result = await generateText3({
|
|
8272
8471
|
model: resolveModel(this.session.model),
|
|
@@ -8277,10 +8476,17 @@ ${personality.trim()}`;
|
|
|
8277
8476
|
// Enable extended thinking/reasoning for models that support it
|
|
8278
8477
|
providerOptions: useAnthropic ? {
|
|
8279
8478
|
anthropic: getAnthropicProviderOptions(this.session.model)
|
|
8280
|
-
} : void 0
|
|
8479
|
+
} : void 0,
|
|
8480
|
+
// Repair tool pairing before every step (see `stream()` for full rationale).
|
|
8481
|
+
prepareStep: async ({ messages: stepMessages }) => {
|
|
8482
|
+
const paired = repairToolPairing(stepMessages);
|
|
8483
|
+
const ordered = ensureToolResultsFollowCalls(paired);
|
|
8484
|
+
if (ordered === stepMessages) return {};
|
|
8485
|
+
return { messages: ordered };
|
|
8486
|
+
}
|
|
8281
8487
|
});
|
|
8282
8488
|
const responseMessages = result.response.messages;
|
|
8283
|
-
this.context.addResponseMessages(responseMessages);
|
|
8489
|
+
await this.context.addResponseMessages(responseMessages);
|
|
8284
8490
|
return {
|
|
8285
8491
|
text: result.text,
|
|
8286
8492
|
steps: result.steps
|
|
@@ -8454,12 +8660,20 @@ ${p.text}` : p.text;
|
|
|
8454
8660
|
model: resolveModel(this.session.model),
|
|
8455
8661
|
system: systemPrompt,
|
|
8456
8662
|
messages,
|
|
8457
|
-
tools: taskTools,
|
|
8663
|
+
tools: wrapToolsNeverThrow(taskTools),
|
|
8458
8664
|
stopWhen: stepCountIs2(500),
|
|
8459
8665
|
abortSignal: combinedAbort,
|
|
8460
8666
|
providerOptions: useAnthropic ? {
|
|
8461
8667
|
anthropic: getAnthropicProviderOptions(this.session.model, { toolStreaming: true })
|
|
8462
8668
|
} : void 0,
|
|
8669
|
+
// See the matching note in `stream()` — repair tool pairing before
|
|
8670
|
+
// every step so we never feed the model an orphan tool-call.
|
|
8671
|
+
prepareStep: async ({ messages: stepMessages }) => {
|
|
8672
|
+
const paired = repairToolPairing(stepMessages);
|
|
8673
|
+
const ordered = ensureToolResultsFollowCalls(paired);
|
|
8674
|
+
if (ordered === stepMessages) return {};
|
|
8675
|
+
return { messages: ordered };
|
|
8676
|
+
},
|
|
8463
8677
|
onStepFinish: async (step) => {
|
|
8464
8678
|
options.onStepFinish?.(step);
|
|
8465
8679
|
fireWebhook("task.step_finished", { iteration, text: step.text });
|
|
@@ -8693,11 +8907,11 @@ ${p.text}` : p.text;
|
|
|
8693
8907
|
const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
|
|
8694
8908
|
if (!isRemoteConfigured2()) return [];
|
|
8695
8909
|
const { readFile: readFile12 } = await import("fs/promises");
|
|
8696
|
-
const { join:
|
|
8910
|
+
const { join: join12, basename: basename5 } = await import("path");
|
|
8697
8911
|
const urls = [];
|
|
8698
8912
|
for (const filePath of filePaths) {
|
|
8699
8913
|
try {
|
|
8700
|
-
const fullPath = filePath.startsWith("/") ? filePath :
|
|
8914
|
+
const fullPath = filePath.startsWith("/") ? filePath : join12(this.session.workingDirectory, filePath);
|
|
8701
8915
|
const fileName = basename5(fullPath);
|
|
8702
8916
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
8703
8917
|
const mimeMap = {
|