sparkecoder 0.1.87 → 0.1.93
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/README.md +1 -1
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +158 -68
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +416 -108
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-BvIissiB.d.ts → index-Bn0Zox8f.d.ts} +23 -23
- package/dist/index.d.ts +5 -5
- package/dist/index.js +272 -97
- package/dist/index.js.map +1 -1
- package/dist/{schema-CohdIL13.d.ts → schema-EmpbnQeQ.d.ts} +3 -3
- package/dist/{search-CCffrVJE.d.ts → search-BRnGaIl-.d.ts} +7 -7
- package/dist/server/index.js +272 -97
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +30 -3
- package/dist/tools/index.js +38 -4
- package/dist/tools/index.js.map +1 -1
- package/package.json +5 -5
- 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)/page_client-reference-manifest.js +1 -1
- 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/page_client-reference-manifest.js +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 +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- 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/page_client-reference-manifest.js +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 +3 -3
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +3 -3
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +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 +3 -3
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +3 -3
- 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 +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +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 +3 -3
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +3 -3
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +3 -3
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +3 -3
- 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 +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__36edac7c._.js +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__be5e2967._.js +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_page_tsx_5ac4794b._.js +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_components_sessions-sidebar_tsx_92510070._.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/{static/chunks/26eb5fab5216f3cc.js → standalone/web/.next/static/chunks/58fd0aaa2746b444.js} +1 -1
- package/web/.next/standalone/web/.next/static/chunks/9fce2ce79c4c834e.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/{8d3efc76109d2efc.js → a888d448ceab1abe.js} +1 -1
- package/web/.next/standalone/web/.next/static/static/chunks/{26eb5fab5216f3cc.js → 58fd0aaa2746b444.js} +1 -1
- package/web/.next/standalone/web/.next/static/static/chunks/9fce2ce79c4c834e.js +1 -0
- package/web/.next/{static/chunks/8d3efc76109d2efc.js → standalone/web/.next/static/static/chunks/a888d448ceab1abe.js} +1 -1
- package/web/.next/standalone/web/package-lock.json +27 -27
- package/web/.next/standalone/web/package.json +1 -1
- package/web/.next/standalone/web/src/app/(main)/page.tsx +2 -2
- package/web/.next/standalone/web/src/app/api/config/route.ts +3 -2
- package/web/.next/standalone/web/src/components/sessions-sidebar.tsx +1 -1
- package/web/.next/standalone/web/src/lib/config.ts +2 -1
- package/web/.next/{standalone/web/.next/static/chunks/26eb5fab5216f3cc.js → static/chunks/58fd0aaa2746b444.js} +1 -1
- package/web/.next/static/chunks/9fce2ce79c4c834e.js +1 -0
- package/web/.next/{standalone/web/.next/static/static/chunks/8d3efc76109d2efc.js → static/chunks/a888d448ceab1abe.js} +1 -1
- package/web/package.json +1 -1
- package/web/.next/standalone/web/.next/static/chunks/b31b0765abe0c427.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/b31b0765abe0c427.js +0 -1
- package/web/.next/static/chunks/b31b0765abe0c427.js +0 -1
- /package/web/.next/standalone/web/.next/static/{static/uUaN7Xe5kF_pP6zhfaeYi → -ax3tJNqBGukss29jpu41}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/uUaN7Xe5kF_pP6zhfaeYi → -ax3tJNqBGukss29jpu41}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/uUaN7Xe5kF_pP6zhfaeYi → -ax3tJNqBGukss29jpu41}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{uUaN7Xe5kF_pP6zhfaeYi → static/-ax3tJNqBGukss29jpu41}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{uUaN7Xe5kF_pP6zhfaeYi → static/-ax3tJNqBGukss29jpu41}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{uUaN7Xe5kF_pP6zhfaeYi → static/-ax3tJNqBGukss29jpu41}/_ssgManifest.js +0 -0
- /package/web/.next/static/{uUaN7Xe5kF_pP6zhfaeYi → -ax3tJNqBGukss29jpu41}/_buildManifest.js +0 -0
- /package/web/.next/static/{uUaN7Xe5kF_pP6zhfaeYi → -ax3tJNqBGukss29jpu41}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{uUaN7Xe5kF_pP6zhfaeYi → -ax3tJNqBGukss29jpu41}/_ssgManifest.js +0 -0
package/dist/server/index.js
CHANGED
|
@@ -28,9 +28,9 @@ __export(remote_exports, {
|
|
|
28
28
|
remoteToolExecutionQueries: () => remoteToolExecutionQueries,
|
|
29
29
|
storageQueries: () => storageQueries
|
|
30
30
|
});
|
|
31
|
-
function initRemoteDatabase(serverUrl,
|
|
31
|
+
function initRemoteDatabase(serverUrl, key2) {
|
|
32
32
|
remoteServerUrl = serverUrl.replace(/\/$/, "");
|
|
33
|
-
authKey =
|
|
33
|
+
authKey = key2;
|
|
34
34
|
}
|
|
35
35
|
function closeRemoteDatabase() {
|
|
36
36
|
remoteServerUrl = null;
|
|
@@ -44,14 +44,14 @@ function parseDates(obj) {
|
|
|
44
44
|
if (Array.isArray(obj)) return obj.map(parseDates);
|
|
45
45
|
if (typeof obj !== "object" || obj instanceof Date) return obj;
|
|
46
46
|
const result = { ...obj };
|
|
47
|
-
for (const
|
|
48
|
-
if (MODEL_MESSAGE_FIELDS.includes(
|
|
47
|
+
for (const key2 of Object.keys(result)) {
|
|
48
|
+
if (MODEL_MESSAGE_FIELDS.includes(key2)) {
|
|
49
49
|
continue;
|
|
50
50
|
}
|
|
51
|
-
if (DATE_FIELDS.includes(
|
|
52
|
-
result[
|
|
53
|
-
} else if (typeof result[
|
|
54
|
-
result[
|
|
51
|
+
if (DATE_FIELDS.includes(key2) && typeof result[key2] === "string") {
|
|
52
|
+
result[key2] = new Date(result[key2]);
|
|
53
|
+
} else if (typeof result[key2] === "object") {
|
|
54
|
+
result[key2] = parseDates(result[key2]);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
return result;
|
|
@@ -596,7 +596,7 @@ var init_types = __esm({
|
|
|
596
596
|
}).optional();
|
|
597
597
|
SparkcoderConfigSchema = z.object({
|
|
598
598
|
// Default model to use (Vercel AI Gateway format)
|
|
599
|
-
defaultModel: z.string().default("anthropic/claude-opus-4
|
|
599
|
+
defaultModel: z.string().default("anthropic/claude-opus-4.7"),
|
|
600
600
|
// Working directory for file operations
|
|
601
601
|
workingDirectory: z.string().optional(),
|
|
602
602
|
// Tool approval settings
|
|
@@ -955,6 +955,14 @@ function loadApiKeysIntoEnv() {
|
|
|
955
955
|
}
|
|
956
956
|
}
|
|
957
957
|
}
|
|
958
|
+
function isRemoteInferenceConfigured() {
|
|
959
|
+
try {
|
|
960
|
+
const config = getConfig();
|
|
961
|
+
return config.resolvedRemoteServer.isConfigured;
|
|
962
|
+
} catch {
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
958
966
|
function setApiKey(provider, apiKey) {
|
|
959
967
|
const normalizedProvider = provider.toLowerCase();
|
|
960
968
|
const envVar = PROVIDER_ENV_MAP[normalizedProvider];
|
|
@@ -1004,11 +1012,11 @@ function getApiKeyStatus() {
|
|
|
1004
1012
|
};
|
|
1005
1013
|
});
|
|
1006
1014
|
}
|
|
1007
|
-
function maskApiKey(
|
|
1008
|
-
if (
|
|
1009
|
-
return "****" +
|
|
1015
|
+
function maskApiKey(key2) {
|
|
1016
|
+
if (key2.length <= 12) {
|
|
1017
|
+
return "****" + key2.slice(-4);
|
|
1010
1018
|
}
|
|
1011
|
-
return
|
|
1019
|
+
return key2.slice(0, 4) + "..." + key2.slice(-4);
|
|
1012
1020
|
}
|
|
1013
1021
|
var CONFIG_FILE_NAMES, cachedConfig, AUTH_KEY_FILE, API_KEYS_FILE, PROVIDER_ENV_MAP, SUPPORTED_PROVIDERS;
|
|
1014
1022
|
var init_config = __esm({
|
|
@@ -1462,10 +1470,10 @@ function parseSkillFrontmatter(content) {
|
|
|
1462
1470
|
}
|
|
1463
1471
|
const colonIndex = line.indexOf(":");
|
|
1464
1472
|
if (colonIndex > 0) {
|
|
1465
|
-
const
|
|
1473
|
+
const key2 = line.slice(0, colonIndex).trim();
|
|
1466
1474
|
let value = line.slice(colonIndex + 1).trim();
|
|
1467
1475
|
if (value === "" || value === "[]") {
|
|
1468
|
-
currentArrayKey =
|
|
1476
|
+
currentArrayKey = key2;
|
|
1469
1477
|
currentArray = [];
|
|
1470
1478
|
continue;
|
|
1471
1479
|
}
|
|
@@ -1478,18 +1486,18 @@ function parseSkillFrontmatter(content) {
|
|
|
1478
1486
|
}
|
|
1479
1487
|
return trimmed;
|
|
1480
1488
|
}).filter((item) => item.length > 0);
|
|
1481
|
-
data[
|
|
1489
|
+
data[key2] = items;
|
|
1482
1490
|
continue;
|
|
1483
1491
|
}
|
|
1484
1492
|
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1485
1493
|
value = value.slice(1, -1);
|
|
1486
1494
|
}
|
|
1487
1495
|
if (value === "true") {
|
|
1488
|
-
data[
|
|
1496
|
+
data[key2] = true;
|
|
1489
1497
|
} else if (value === "false") {
|
|
1490
|
-
data[
|
|
1498
|
+
data[key2] = false;
|
|
1491
1499
|
} else {
|
|
1492
|
-
data[
|
|
1500
|
+
data[key2] = value;
|
|
1493
1501
|
}
|
|
1494
1502
|
}
|
|
1495
1503
|
}
|
|
@@ -1821,9 +1829,9 @@ var init_chunker = __esm({
|
|
|
1821
1829
|
});
|
|
1822
1830
|
|
|
1823
1831
|
// src/semantic/client.ts
|
|
1824
|
-
function initVectorClient(serverUrl,
|
|
1832
|
+
function initVectorClient(serverUrl, key2) {
|
|
1825
1833
|
remoteServerUrl2 = serverUrl.replace(/\/$/, "");
|
|
1826
|
-
authKey2 =
|
|
1834
|
+
authKey2 = key2;
|
|
1827
1835
|
}
|
|
1828
1836
|
function isVectorClientConfigured() {
|
|
1829
1837
|
return !!remoteServerUrl2 && !!authKey2;
|
|
@@ -2422,7 +2430,7 @@ import { promisify as promisify6 } from "util";
|
|
|
2422
2430
|
import { writeFile as writeFile5, mkdir as mkdir4, readFile as readFile11, unlink as unlink2, readdir as readdir5, rm } from "fs/promises";
|
|
2423
2431
|
import { join as join9 } from "path";
|
|
2424
2432
|
import { tmpdir as tmpdir2 } from "os";
|
|
2425
|
-
import { nanoid as
|
|
2433
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
2426
2434
|
async function checkFfmpeg() {
|
|
2427
2435
|
try {
|
|
2428
2436
|
await execAsync6("ffmpeg -version", { timeout: 5e3 });
|
|
@@ -2477,7 +2485,7 @@ var init_recorder = __esm({
|
|
|
2477
2485
|
*/
|
|
2478
2486
|
async encode() {
|
|
2479
2487
|
if (this.frames.length === 0) return null;
|
|
2480
|
-
const workDir = join9(tmpdir2(), `sparkecoder-recording-${
|
|
2488
|
+
const workDir = join9(tmpdir2(), `sparkecoder-recording-${nanoid5(8)}`);
|
|
2481
2489
|
await mkdir4(workDir, { recursive: true });
|
|
2482
2490
|
try {
|
|
2483
2491
|
for (let i = 0; i < this.frames.length; i++) {
|
|
@@ -2544,7 +2552,7 @@ import { z as z16 } from "zod";
|
|
|
2544
2552
|
import { existsSync as existsSync16, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3, readdirSync as readdirSync2, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
2545
2553
|
import { readdir as readdir6 } from "fs/promises";
|
|
2546
2554
|
import { join as join10, basename as basename5, extname as extname8, relative as relative9 } from "path";
|
|
2547
|
-
import { nanoid as
|
|
2555
|
+
import { nanoid as nanoid7 } from "nanoid";
|
|
2548
2556
|
|
|
2549
2557
|
// src/agent/index.ts
|
|
2550
2558
|
import {
|
|
@@ -2719,6 +2727,23 @@ function isAnthropicModel(modelId) {
|
|
|
2719
2727
|
const normalized = modelId.trim().toLowerCase();
|
|
2720
2728
|
return normalized.startsWith(ANTHROPIC_PREFIX) || normalized.startsWith("claude-");
|
|
2721
2729
|
}
|
|
2730
|
+
function requiresAdaptiveThinking(modelId) {
|
|
2731
|
+
const m = modelId.toLowerCase().match(/claude-(?:opus|sonnet|haiku)-(\d+)[.-](\d{1,2})(?!\d)/);
|
|
2732
|
+
if (!m) return false;
|
|
2733
|
+
const major = Number(m[1]);
|
|
2734
|
+
const minor = Number(m[2]);
|
|
2735
|
+
if (Number.isNaN(major) || Number.isNaN(minor)) return false;
|
|
2736
|
+
if (major > 4) return true;
|
|
2737
|
+
if (major === 4 && minor >= 6) return true;
|
|
2738
|
+
return false;
|
|
2739
|
+
}
|
|
2740
|
+
function getAnthropicProviderOptions(modelId, opts = {}) {
|
|
2741
|
+
const { toolStreaming, budgetTokens = 1e4 } = opts;
|
|
2742
|
+
const thinking = requiresAdaptiveThinking(modelId) ? { type: "adaptive" } : { type: "enabled", budgetTokens };
|
|
2743
|
+
const out = { thinking };
|
|
2744
|
+
if (toolStreaming) out.toolStreaming = true;
|
|
2745
|
+
return out;
|
|
2746
|
+
}
|
|
2722
2747
|
function resolveModel(modelId) {
|
|
2723
2748
|
try {
|
|
2724
2749
|
const config = getConfig();
|
|
@@ -2742,7 +2767,7 @@ var SUBAGENT_MODELS = {
|
|
|
2742
2767
|
init_db();
|
|
2743
2768
|
init_config();
|
|
2744
2769
|
import { z as z15 } from "zod";
|
|
2745
|
-
import { nanoid as
|
|
2770
|
+
import { nanoid as nanoid6 } from "nanoid";
|
|
2746
2771
|
|
|
2747
2772
|
// src/tools/bash.ts
|
|
2748
2773
|
import { tool } from "ai";
|
|
@@ -3075,11 +3100,11 @@ async function sendInput(terminalId, input, options = {}) {
|
|
|
3075
3100
|
return false;
|
|
3076
3101
|
}
|
|
3077
3102
|
}
|
|
3078
|
-
async function sendKey(terminalId,
|
|
3103
|
+
async function sendKey(terminalId, key2) {
|
|
3079
3104
|
const session = getSessionName(terminalId);
|
|
3080
3105
|
try {
|
|
3081
3106
|
await execAsync(`tmux has-session -t ${session}`, { timeout: 1e3 });
|
|
3082
|
-
await execAsync(`tmux send-keys -t ${session} ${
|
|
3107
|
+
await execAsync(`tmux send-keys -t ${session} ${key2}`, { timeout: 1e3 });
|
|
3083
3108
|
return true;
|
|
3084
3109
|
} catch {
|
|
3085
3110
|
return false;
|
|
@@ -3218,7 +3243,7 @@ bash({ id: "abc123", input: "my text" }) // send text input
|
|
|
3218
3243
|
Terminal output is stored in the global SparkECoder data directory. Use the \`tail\` option to read recent output.`,
|
|
3219
3244
|
inputSchema: bashInputSchema,
|
|
3220
3245
|
execute: async (inputArgs) => {
|
|
3221
|
-
const { command, background, id, kill, tail, input: textInput, key } = inputArgs;
|
|
3246
|
+
const { command, background, id, kill, tail, input: textInput, key: key2 } = inputArgs;
|
|
3222
3247
|
if (id) {
|
|
3223
3248
|
if (kill) {
|
|
3224
3249
|
const success = await killTerminal(id);
|
|
@@ -3249,8 +3274,8 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
|
|
|
3249
3274
|
message: `Sent input "${textInput}" to terminal`
|
|
3250
3275
|
};
|
|
3251
3276
|
}
|
|
3252
|
-
if (
|
|
3253
|
-
const success = await sendKey(id,
|
|
3277
|
+
if (key2) {
|
|
3278
|
+
const success = await sendKey(id, key2);
|
|
3254
3279
|
if (!success) {
|
|
3255
3280
|
return {
|
|
3256
3281
|
success: false,
|
|
@@ -3266,7 +3291,7 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
|
|
|
3266
3291
|
id,
|
|
3267
3292
|
output: truncatedOutput2,
|
|
3268
3293
|
status: status2,
|
|
3269
|
-
message: `Sent key "${
|
|
3294
|
+
message: `Sent key "${key2}" to terminal`
|
|
3270
3295
|
};
|
|
3271
3296
|
}
|
|
3272
3297
|
const { output, status } = await getLogs(id, options.workingDirectory, { tail, sessionId: options.sessionId });
|
|
@@ -3408,13 +3433,13 @@ async function resizeImageIfNeeded(buffer, mediaType) {
|
|
|
3408
3433
|
const needsResize = longEdge > MAX_LONG_EDGE;
|
|
3409
3434
|
const needsShrink = buffer.length > MAX_FILE_BYTES;
|
|
3410
3435
|
if (!needsResize && !needsShrink) return { buffer, mediaType: inputMediaType };
|
|
3411
|
-
const
|
|
3436
|
+
const key2 = cacheKey(buffer);
|
|
3412
3437
|
const cacheDir = getCacheDir();
|
|
3413
3438
|
const isPng = inputMediaType.includes("png");
|
|
3414
3439
|
const willConvertToJpeg = isPng && (needsShrink || buffer.length > 2 * 1024 * 1024);
|
|
3415
3440
|
const outputMediaType = willConvertToJpeg || !isPng ? "image/jpeg" : "image/png";
|
|
3416
3441
|
const ext = outputMediaType === "image/png" ? ".png" : ".jpg";
|
|
3417
|
-
const cachePath = join3(cacheDir,
|
|
3442
|
+
const cachePath = join3(cacheDir, key2 + ext);
|
|
3418
3443
|
if (existsSync3(cachePath)) {
|
|
3419
3444
|
console.log(`[image-resize] Cache hit for ${width}x${height} image`);
|
|
3420
3445
|
return { buffer: readFileSync2(cachePath), mediaType: outputMediaType };
|
|
@@ -4305,31 +4330,31 @@ async function getClientForFile(filePath) {
|
|
|
4305
4330
|
return null;
|
|
4306
4331
|
}
|
|
4307
4332
|
const root = dirname4(normalized);
|
|
4308
|
-
const
|
|
4309
|
-
const existing = state.clients.get(
|
|
4333
|
+
const key2 = `${serverDef.id}:${root}`;
|
|
4334
|
+
const existing = state.clients.get(key2);
|
|
4310
4335
|
if (existing) {
|
|
4311
4336
|
return existing;
|
|
4312
4337
|
}
|
|
4313
|
-
if (state.broken.has(
|
|
4338
|
+
if (state.broken.has(key2)) {
|
|
4314
4339
|
return null;
|
|
4315
4340
|
}
|
|
4316
4341
|
try {
|
|
4317
4342
|
const handle = await serverDef.spawn(root);
|
|
4318
4343
|
if (!handle) {
|
|
4319
|
-
state.broken.add(
|
|
4344
|
+
state.broken.add(key2);
|
|
4320
4345
|
return null;
|
|
4321
4346
|
}
|
|
4322
4347
|
console.log(`[lsp] Started ${serverDef.name} for ${root}`);
|
|
4323
4348
|
const client = await createClient(serverDef.id, handle, root);
|
|
4324
|
-
state.clients.set(
|
|
4349
|
+
state.clients.set(key2, client);
|
|
4325
4350
|
handle.process.on("exit", (code) => {
|
|
4326
4351
|
console.log(`[lsp] ${serverDef.name} exited with code ${code}`);
|
|
4327
|
-
state.clients.delete(
|
|
4352
|
+
state.clients.delete(key2);
|
|
4328
4353
|
});
|
|
4329
4354
|
return client;
|
|
4330
4355
|
} catch (error) {
|
|
4331
4356
|
console.error(`[lsp] Failed to start ${serverDef.name}:`, error);
|
|
4332
|
-
state.broken.add(
|
|
4357
|
+
state.broken.add(key2);
|
|
4333
4358
|
return null;
|
|
4334
4359
|
}
|
|
4335
4360
|
}
|
|
@@ -6061,6 +6086,7 @@ init_semantic_search();
|
|
|
6061
6086
|
import { tool as tool11 } from "ai";
|
|
6062
6087
|
import { z as z12 } from "zod";
|
|
6063
6088
|
import Ajv from "ajv";
|
|
6089
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
6064
6090
|
var ajv = new Ajv({ allErrors: true });
|
|
6065
6091
|
function createCompleteTaskTool(options) {
|
|
6066
6092
|
const validate = ajv.compile(options.outputSchema);
|
|
@@ -6107,6 +6133,37 @@ function createTaskFailedTool(options) {
|
|
|
6107
6133
|
}
|
|
6108
6134
|
});
|
|
6109
6135
|
}
|
|
6136
|
+
function createAskQuestionToUserTool(options) {
|
|
6137
|
+
return tool11({
|
|
6138
|
+
description: "Ask the user a blocking clarification question when you cannot safely continue without more information. Use this only after trying to infer the answer from the available context. The task will pause until the orchestrator or user answers.",
|
|
6139
|
+
inputSchema: z12.object({
|
|
6140
|
+
question: z12.string().min(1).describe("The concise question you need answered."),
|
|
6141
|
+
context: z12.string().optional().describe("Brief context explaining why this answer is needed and what you already tried."),
|
|
6142
|
+
choices: z12.array(z12.string().min(1)).max(10).optional().describe("Optional suggested answer choices when the question is multiple choice.")
|
|
6143
|
+
}),
|
|
6144
|
+
execute: async (input) => {
|
|
6145
|
+
if (!options.onQuestion) {
|
|
6146
|
+
return {
|
|
6147
|
+
status: "unavailable",
|
|
6148
|
+
message: "Question routing is not configured for this task. Continue with the best safe assumption or call task_failed if blocked."
|
|
6149
|
+
};
|
|
6150
|
+
}
|
|
6151
|
+
const questionId = `q_${nanoid3(12)}`;
|
|
6152
|
+
const answer = await options.onQuestion({
|
|
6153
|
+
questionId,
|
|
6154
|
+
question: input.question,
|
|
6155
|
+
context: input.context,
|
|
6156
|
+
choices: input.choices
|
|
6157
|
+
});
|
|
6158
|
+
return {
|
|
6159
|
+
status: "answered",
|
|
6160
|
+
questionId,
|
|
6161
|
+
answer: answer.answer,
|
|
6162
|
+
answeredBy: answer.answeredBy ?? "unknown"
|
|
6163
|
+
};
|
|
6164
|
+
}
|
|
6165
|
+
});
|
|
6166
|
+
}
|
|
6110
6167
|
|
|
6111
6168
|
// src/tools/upload-file.ts
|
|
6112
6169
|
import { tool as tool12 } from "ai";
|
|
@@ -6212,7 +6269,7 @@ import { promisify as promisify5 } from "util";
|
|
|
6212
6269
|
import { mkdirSync as mkdirSync5, existsSync as existsSync15, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
6213
6270
|
import { join as join8 } from "path";
|
|
6214
6271
|
import { tmpdir } from "os";
|
|
6215
|
-
import { nanoid as
|
|
6272
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
6216
6273
|
var execAsync5 = promisify5(exec5);
|
|
6217
6274
|
var DEFAULT_WIDTH = 1280;
|
|
6218
6275
|
var DEFAULT_HEIGHT = 800;
|
|
@@ -6336,9 +6393,9 @@ async function runScroll(dx, dy) {
|
|
|
6336
6393
|
{ timeout: 5e3 }
|
|
6337
6394
|
);
|
|
6338
6395
|
}
|
|
6339
|
-
function translateKeyForCliclick(
|
|
6340
|
-
if (!
|
|
6341
|
-
const parts =
|
|
6396
|
+
function translateKeyForCliclick(key2) {
|
|
6397
|
+
if (!key2) return [];
|
|
6398
|
+
const parts = key2.split("+").map((p) => p.trim()).filter(Boolean);
|
|
6342
6399
|
if (parts.length === 0) return [];
|
|
6343
6400
|
const modMap = {
|
|
6344
6401
|
ctrl: "ctrl",
|
|
@@ -6434,7 +6491,7 @@ function createComputerUseTool(options) {
|
|
|
6434
6491
|
try {
|
|
6435
6492
|
switch (input.action) {
|
|
6436
6493
|
case "screenshot": {
|
|
6437
|
-
const path = join8(tmpdir(), `cu-${
|
|
6494
|
+
const path = join8(tmpdir(), `cu-${nanoid4(8)}.png`);
|
|
6438
6495
|
await runScreencapture(path);
|
|
6439
6496
|
const resized = await resizeScreenshotToPoints(path, displayWidth, displayHeight);
|
|
6440
6497
|
try {
|
|
@@ -6565,7 +6622,7 @@ function createComputerUseTool(options) {
|
|
|
6565
6622
|
case "zoom": {
|
|
6566
6623
|
const region = input.region ?? [0, 0, displayWidth, displayHeight];
|
|
6567
6624
|
const [x1, y1, x2, y2] = region;
|
|
6568
|
-
const tmpPath = join8(tmpdir(), `cu-zoom-${
|
|
6625
|
+
const tmpPath = join8(tmpdir(), `cu-zoom-${nanoid4(8)}.png`);
|
|
6569
6626
|
await runScreencapture(tmpPath);
|
|
6570
6627
|
const sharpModule = await import("sharp");
|
|
6571
6628
|
const sharp2 = sharpModule.default || sharpModule;
|
|
@@ -6845,6 +6902,7 @@ async function createTools(options) {
|
|
|
6845
6902
|
if (options.taskTools) {
|
|
6846
6903
|
tools.complete_task = createCompleteTaskTool(options.taskTools);
|
|
6847
6904
|
tools.task_failed = createTaskFailedTool(options.taskTools);
|
|
6905
|
+
tools.ask_question_to_user = createAskQuestionToUserTool(options.taskTools);
|
|
6848
6906
|
}
|
|
6849
6907
|
return tools;
|
|
6850
6908
|
}
|
|
@@ -7252,9 +7310,10 @@ If you need to give the user a downloadable file (report, image, export, etc.),
|
|
|
7252
7310
|
### Rules
|
|
7253
7311
|
1. Work independently \u2014 no human will approve tool calls. All tools run without approval.
|
|
7254
7312
|
2. Keep working until the task is fully complete \u2014 and then VERIFY it is complete before finishing.
|
|
7255
|
-
3.
|
|
7256
|
-
4.
|
|
7257
|
-
5.
|
|
7313
|
+
3. If you are blocked by missing information, call \`ask_question_to_user\` with a concise question. The run will pause until the orchestrator or user answers, then you should continue from that answer.
|
|
7314
|
+
4. When done, call the \`complete_task\` tool with a JSON result matching the output schema below.
|
|
7315
|
+
5. If you determine the task is impossible or encounter an unrecoverable error, call the \`task_failed\` tool with a clear reason.
|
|
7316
|
+
6. Do NOT stop without calling \`complete_task\`, \`task_failed\`, or \`ask_question_to_user\` when blocked.
|
|
7258
7317
|
|
|
7259
7318
|
### Verification \u2014 BE EXTREMELY THOROUGH
|
|
7260
7319
|
Before calling \`complete_task\`, you MUST verify your work completely. Do not just assume it worked. Actually check.
|
|
@@ -7325,6 +7384,7 @@ ${JSON.stringify(outputSchema, null, 2)}
|
|
|
7325
7384
|
### Completion Tools
|
|
7326
7385
|
- **\`complete_task({ result: ... })\`** \u2014 Call ONLY after thorough verification. The result is validated against the schema above. If validation fails you will get errors back \u2014 fix and retry.
|
|
7327
7386
|
- **\`task_failed({ reason: "..." })\`** \u2014 Call only if the task truly cannot be completed.
|
|
7387
|
+
- **\`ask_question_to_user({ question, context?, choices? })\`** \u2014 Call only when you need information that is not available in the repo, task prompt, files, logs, or tools. Ask one clear question; after the answer is returned, continue working.
|
|
7328
7388
|
`;
|
|
7329
7389
|
}
|
|
7330
7390
|
function createSummaryPrompt(conversationHistory) {
|
|
@@ -7480,6 +7540,7 @@ function sanitizeModelMessages(messages) {
|
|
|
7480
7540
|
|
|
7481
7541
|
// src/agent/model-limits.ts
|
|
7482
7542
|
var MODEL_LIMITS = {
|
|
7543
|
+
"anthropic/claude-opus-4.7": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
7483
7544
|
"anthropic/claude-opus-4-6": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
7484
7545
|
"anthropic/claude-sonnet-4": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
7485
7546
|
"anthropic/claude-3.5-sonnet": { contextWindow: 2e5, rollingTarget: 15e4 },
|
|
@@ -7816,17 +7877,65 @@ function repairToolPairing(messages) {
|
|
|
7816
7877
|
|
|
7817
7878
|
// src/agent/index.ts
|
|
7818
7879
|
init_webhook();
|
|
7880
|
+
|
|
7881
|
+
// src/tasks/questions.ts
|
|
7882
|
+
var pendingQuestions = /* @__PURE__ */ new Map();
|
|
7883
|
+
var answeredQuestions = /* @__PURE__ */ new Map();
|
|
7884
|
+
function key(taskId, questionId) {
|
|
7885
|
+
return `${taskId}:${questionId}`;
|
|
7886
|
+
}
|
|
7887
|
+
function waitForTaskQuestionAnswer(question) {
|
|
7888
|
+
const k = key(question.taskId, question.questionId);
|
|
7889
|
+
if (pendingQuestions.has(k)) {
|
|
7890
|
+
return Promise.reject(new Error(`Question already pending: ${question.questionId}`));
|
|
7891
|
+
}
|
|
7892
|
+
return new Promise((resolve11, reject) => {
|
|
7893
|
+
pendingQuestions.set(k, {
|
|
7894
|
+
...question,
|
|
7895
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
7896
|
+
resolve: resolve11,
|
|
7897
|
+
reject
|
|
7898
|
+
});
|
|
7899
|
+
});
|
|
7900
|
+
}
|
|
7901
|
+
function answerTaskQuestion(taskId, questionId, answer) {
|
|
7902
|
+
const k = key(taskId, questionId);
|
|
7903
|
+
const pending = pendingQuestions.get(k);
|
|
7904
|
+
if (!pending) return answeredQuestions.has(k) ? "already_answered" : "not_found";
|
|
7905
|
+
pendingQuestions.delete(k);
|
|
7906
|
+
answeredQuestions.set(k, answer);
|
|
7907
|
+
pending.resolve(answer);
|
|
7908
|
+
return "answered";
|
|
7909
|
+
}
|
|
7910
|
+
function rejectTaskQuestions(taskId, reason) {
|
|
7911
|
+
for (const [k, pending] of pendingQuestions) {
|
|
7912
|
+
if (pending.taskId !== taskId) continue;
|
|
7913
|
+
pendingQuestions.delete(k);
|
|
7914
|
+
pending.reject(new Error(reason));
|
|
7915
|
+
}
|
|
7916
|
+
}
|
|
7917
|
+
function getPendingTaskQuestion(taskId, questionId) {
|
|
7918
|
+
const pending = pendingQuestions.get(key(taskId, questionId));
|
|
7919
|
+
if (!pending) return null;
|
|
7920
|
+
const { resolve: _resolve, reject: _reject, ...question } = pending;
|
|
7921
|
+
return question;
|
|
7922
|
+
}
|
|
7923
|
+
function getAnsweredTaskQuestion(taskId, questionId) {
|
|
7924
|
+
return answeredQuestions.get(key(taskId, questionId)) ?? null;
|
|
7925
|
+
}
|
|
7926
|
+
|
|
7927
|
+
// src/agent/index.ts
|
|
7819
7928
|
var MAX_SSE_FIELD_LENGTH = 8 * 1024;
|
|
7820
7929
|
var SSE_PREVIEW_LENGTH = 2 * 1024;
|
|
7821
7930
|
function truncateWriteFileInput(input) {
|
|
7822
7931
|
const out = { ...input };
|
|
7823
|
-
for (const
|
|
7824
|
-
const val = out[
|
|
7932
|
+
for (const key2 of ["content", "old_string", "new_string"]) {
|
|
7933
|
+
const val = out[key2];
|
|
7825
7934
|
if (typeof val === "string" && val.length > MAX_SSE_FIELD_LENGTH) {
|
|
7826
|
-
out[
|
|
7935
|
+
out[key2] = `${val.slice(0, SSE_PREVIEW_LENGTH)}
|
|
7827
7936
|
... (truncated)`;
|
|
7828
|
-
out[`${
|
|
7829
|
-
out[`${
|
|
7937
|
+
out[`${key2}Truncated`] = true;
|
|
7938
|
+
out[`${key2}Length`] = val.length;
|
|
7830
7939
|
}
|
|
7831
7940
|
}
|
|
7832
7941
|
return out;
|
|
@@ -7996,13 +8105,7 @@ ${prompt}` });
|
|
|
7996
8105
|
abortSignal: options.abortSignal,
|
|
7997
8106
|
// Enable extended thinking/reasoning for models that support it
|
|
7998
8107
|
providerOptions: useAnthropic ? {
|
|
7999
|
-
anthropic: {
|
|
8000
|
-
toolStreaming: true,
|
|
8001
|
-
thinking: {
|
|
8002
|
-
type: "enabled",
|
|
8003
|
-
budgetTokens: 1e4
|
|
8004
|
-
}
|
|
8005
|
-
}
|
|
8108
|
+
anthropic: getAnthropicProviderOptions(this.session.model, { toolStreaming: true })
|
|
8006
8109
|
} : void 0,
|
|
8007
8110
|
onStepFinish: async (step) => {
|
|
8008
8111
|
options.onStepFinish?.(step);
|
|
@@ -8049,12 +8152,7 @@ ${prompt}` });
|
|
|
8049
8152
|
stopWhen: stepCountIs2(500),
|
|
8050
8153
|
// Enable extended thinking/reasoning for models that support it
|
|
8051
8154
|
providerOptions: useAnthropic ? {
|
|
8052
|
-
anthropic:
|
|
8053
|
-
thinking: {
|
|
8054
|
-
type: "enabled",
|
|
8055
|
-
budgetTokens: 1e4
|
|
8056
|
-
}
|
|
8057
|
-
}
|
|
8155
|
+
anthropic: getAnthropicProviderOptions(this.session.model)
|
|
8058
8156
|
} : void 0
|
|
8059
8157
|
});
|
|
8060
8158
|
const responseMessages = result.response.messages;
|
|
@@ -8141,7 +8239,38 @@ ${prompt}` });
|
|
|
8141
8239
|
},
|
|
8142
8240
|
taskTools: {
|
|
8143
8241
|
outputSchema: options.taskConfig.outputSchema,
|
|
8144
|
-
onComplete
|
|
8242
|
+
onComplete,
|
|
8243
|
+
onQuestion: async (question) => {
|
|
8244
|
+
const payload = {
|
|
8245
|
+
questionId: question.questionId,
|
|
8246
|
+
question: question.question,
|
|
8247
|
+
context: question.context,
|
|
8248
|
+
choices: question.choices,
|
|
8249
|
+
status: "pending"
|
|
8250
|
+
};
|
|
8251
|
+
const answerPromise = waitForTaskQuestionAnswer({
|
|
8252
|
+
taskId: this.session.id,
|
|
8253
|
+
questionId: question.questionId,
|
|
8254
|
+
question: question.question,
|
|
8255
|
+
context: question.context,
|
|
8256
|
+
choices: question.choices
|
|
8257
|
+
});
|
|
8258
|
+
fireWebhook("task.question", payload);
|
|
8259
|
+
if (emit) {
|
|
8260
|
+
await emit(JSON.stringify({ type: "task-question", data: payload }));
|
|
8261
|
+
}
|
|
8262
|
+
const answer = await answerPromise;
|
|
8263
|
+
const answeredPayload = {
|
|
8264
|
+
questionId: question.questionId,
|
|
8265
|
+
answer: answer.answer,
|
|
8266
|
+
answeredBy: answer.answeredBy
|
|
8267
|
+
};
|
|
8268
|
+
fireWebhook("task.question_answered", answeredPayload);
|
|
8269
|
+
if (emit) {
|
|
8270
|
+
await emit(JSON.stringify({ type: "task-question-answered", data: answeredPayload }));
|
|
8271
|
+
}
|
|
8272
|
+
return answer;
|
|
8273
|
+
}
|
|
8145
8274
|
}
|
|
8146
8275
|
});
|
|
8147
8276
|
const baseSystemPrompt = await buildSystemPrompt({
|
|
@@ -8186,10 +8315,7 @@ ${taskAddendum}`;
|
|
|
8186
8315
|
stopWhen: stepCountIs2(500),
|
|
8187
8316
|
abortSignal: options.abortSignal,
|
|
8188
8317
|
providerOptions: useAnthropic ? {
|
|
8189
|
-
anthropic: {
|
|
8190
|
-
toolStreaming: true,
|
|
8191
|
-
thinking: { type: "enabled", budgetTokens: 1e4 }
|
|
8192
|
-
}
|
|
8318
|
+
anthropic: getAnthropicProviderOptions(this.session.model, { toolStreaming: true })
|
|
8193
8319
|
} : void 0,
|
|
8194
8320
|
onStepFinish: async (step) => {
|
|
8195
8321
|
options.onStepFinish?.(step);
|
|
@@ -8476,7 +8602,7 @@ ${taskAddendum}`;
|
|
|
8476
8602
|
description: originalTool.description || "",
|
|
8477
8603
|
inputSchema: originalTool.inputSchema || z15.object({}),
|
|
8478
8604
|
execute: async (input, toolOptions) => {
|
|
8479
|
-
const toolCallId = toolOptions.toolCallId ||
|
|
8605
|
+
const toolCallId = toolOptions.toolCallId || nanoid6();
|
|
8480
8606
|
const execution = toolExecutionQueries.create({
|
|
8481
8607
|
sessionId: this.session.id,
|
|
8482
8608
|
toolName: name,
|
|
@@ -9126,7 +9252,7 @@ sessions.post("/:id/attachments", async (c) => {
|
|
|
9126
9252
|
return c.json({ error: "No file provided" }, 400);
|
|
9127
9253
|
}
|
|
9128
9254
|
const dir = ensureAttachmentsDir(sessionId);
|
|
9129
|
-
const id =
|
|
9255
|
+
const id = nanoid7(10);
|
|
9130
9256
|
const ext = extname8(file.name) || "";
|
|
9131
9257
|
const safeFilename = `${id}_${basename5(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
9132
9258
|
const filePath = join10(dir, safeFilename);
|
|
@@ -9152,7 +9278,7 @@ sessions.post("/:id/attachments", async (c) => {
|
|
|
9152
9278
|
return c.json({ error: "Missing filename or data" }, 400);
|
|
9153
9279
|
}
|
|
9154
9280
|
const dir = ensureAttachmentsDir(sessionId);
|
|
9155
|
-
const id =
|
|
9281
|
+
const id = nanoid7(10);
|
|
9156
9282
|
const ext = extname8(body.filename) || "";
|
|
9157
9283
|
const safeFilename = `${id}_${basename5(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
9158
9284
|
const filePath = join10(dir, safeFilename);
|
|
@@ -9444,9 +9570,9 @@ var store = /* @__PURE__ */ new Map();
|
|
|
9444
9570
|
var channels = /* @__PURE__ */ new Map();
|
|
9445
9571
|
var cleanupInterval = setInterval(() => {
|
|
9446
9572
|
const now = Date.now();
|
|
9447
|
-
for (const [
|
|
9573
|
+
for (const [key2, data] of store.entries()) {
|
|
9448
9574
|
if (data.expiresAt && data.expiresAt < now) {
|
|
9449
|
-
store.delete(
|
|
9575
|
+
store.delete(key2);
|
|
9450
9576
|
}
|
|
9451
9577
|
}
|
|
9452
9578
|
}, 6e4);
|
|
@@ -9470,27 +9596,27 @@ var publisher = {
|
|
|
9470
9596
|
}
|
|
9471
9597
|
}
|
|
9472
9598
|
},
|
|
9473
|
-
set: async (
|
|
9599
|
+
set: async (key2, value, options) => {
|
|
9474
9600
|
const expiresAt = options?.EX ? Date.now() + options.EX * 1e3 : void 0;
|
|
9475
|
-
store.set(
|
|
9601
|
+
store.set(key2, { value, expiresAt });
|
|
9476
9602
|
if (options?.EX) {
|
|
9477
|
-
setTimeout(() => store.delete(
|
|
9603
|
+
setTimeout(() => store.delete(key2), options.EX * 1e3);
|
|
9478
9604
|
}
|
|
9479
9605
|
},
|
|
9480
|
-
get: async (
|
|
9481
|
-
const data = store.get(
|
|
9606
|
+
get: async (key2) => {
|
|
9607
|
+
const data = store.get(key2);
|
|
9482
9608
|
if (!data) return null;
|
|
9483
9609
|
if (data.expiresAt && data.expiresAt < Date.now()) {
|
|
9484
|
-
store.delete(
|
|
9610
|
+
store.delete(key2);
|
|
9485
9611
|
return null;
|
|
9486
9612
|
}
|
|
9487
9613
|
return data.value;
|
|
9488
9614
|
},
|
|
9489
|
-
incr: async (
|
|
9490
|
-
const data = store.get(
|
|
9615
|
+
incr: async (key2) => {
|
|
9616
|
+
const data = store.get(key2);
|
|
9491
9617
|
const current = data ? parseInt(data.value, 10) : 0;
|
|
9492
9618
|
const next = (isNaN(current) ? 0 : current) + 1;
|
|
9493
|
-
store.set(
|
|
9619
|
+
store.set(key2, { value: String(next), expiresAt: data?.expiresAt });
|
|
9494
9620
|
return next;
|
|
9495
9621
|
}
|
|
9496
9622
|
};
|
|
@@ -9522,7 +9648,7 @@ var streamContext = createResumableStreamContext({
|
|
|
9522
9648
|
});
|
|
9523
9649
|
|
|
9524
9650
|
// src/server/routes/agents.ts
|
|
9525
|
-
import { nanoid as
|
|
9651
|
+
import { nanoid as nanoid8 } from "nanoid";
|
|
9526
9652
|
init_stream_proxy();
|
|
9527
9653
|
init_recorder();
|
|
9528
9654
|
init_remote();
|
|
@@ -10081,7 +10207,7 @@ ${prompt}` });
|
|
|
10081
10207
|
userMessageContent = prompt;
|
|
10082
10208
|
}
|
|
10083
10209
|
await messageQueries.create(id, { role: "user", content: userMessageContent });
|
|
10084
|
-
const streamId = `stream_${id}_${
|
|
10210
|
+
const streamId = `stream_${id}_${nanoid8(10)}`;
|
|
10085
10211
|
console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
|
|
10086
10212
|
await activeStreamQueries.create(id, streamId);
|
|
10087
10213
|
const stream = await streamContext.resumableStream(
|
|
@@ -10286,7 +10412,7 @@ agents.post(
|
|
|
10286
10412
|
});
|
|
10287
10413
|
const session = agent.getSession();
|
|
10288
10414
|
const enrichedPrompt = enrichPromptWithDevtoolsContext(session.id, body.prompt);
|
|
10289
|
-
const streamId = `stream_${session.id}_${
|
|
10415
|
+
const streamId = `stream_${session.id}_${nanoid8(10)}`;
|
|
10290
10416
|
await createCheckpoint(session.id, session.workingDirectory, 0);
|
|
10291
10417
|
await activeStreamQueries.create(session.id, streamId);
|
|
10292
10418
|
const createQuickStreamProducer = () => {
|
|
@@ -10890,7 +11016,8 @@ health.get("/", async (c) => {
|
|
|
10890
11016
|
const config = getConfig();
|
|
10891
11017
|
const apiKeyStatus = getApiKeyStatus();
|
|
10892
11018
|
const gatewayKey = apiKeyStatus.find((s) => s.provider === "ai-gateway");
|
|
10893
|
-
const
|
|
11019
|
+
const remoteInference = isRemoteInferenceConfigured();
|
|
11020
|
+
const hasApiKey = remoteInference || (gatewayKey?.configured ?? false);
|
|
10894
11021
|
let hwid;
|
|
10895
11022
|
try {
|
|
10896
11023
|
hwid = getHardwareIdCached();
|
|
@@ -10901,6 +11028,7 @@ health.get("/", async (c) => {
|
|
|
10901
11028
|
version: currentVersion,
|
|
10902
11029
|
uptime: process.uptime(),
|
|
10903
11030
|
apiKeyConfigured: hasApiKey,
|
|
11031
|
+
inferenceMode: remoteInference ? "remote" : "local",
|
|
10904
11032
|
hwid,
|
|
10905
11033
|
config: {
|
|
10906
11034
|
workingDirectory: config.resolvedWorkingDirectory,
|
|
@@ -11327,7 +11455,7 @@ init_db();
|
|
|
11327
11455
|
import { Hono as Hono5 } from "hono";
|
|
11328
11456
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
11329
11457
|
import { z as z20 } from "zod";
|
|
11330
|
-
import { nanoid as
|
|
11458
|
+
import { nanoid as nanoid9 } from "nanoid";
|
|
11331
11459
|
init_config();
|
|
11332
11460
|
var tasks = new Hono5();
|
|
11333
11461
|
var taskAbortControllers = /* @__PURE__ */ new Map();
|
|
@@ -11401,7 +11529,7 @@ tasks.post(
|
|
|
11401
11529
|
const taskId = agent.sessionId;
|
|
11402
11530
|
const abortController = new AbortController();
|
|
11403
11531
|
taskAbortControllers.set(taskId, abortController);
|
|
11404
|
-
const streamId = `stream_${taskId}_${
|
|
11532
|
+
const streamId = `stream_${taskId}_${nanoid9(10)}`;
|
|
11405
11533
|
await activeStreamQueries.create(taskId, streamId);
|
|
11406
11534
|
const taskStreamProducer = () => {
|
|
11407
11535
|
const { readable, writable } = new TransformStream();
|
|
@@ -11528,6 +11656,7 @@ tasks.post("/:id/cancel", async (c) => {
|
|
|
11528
11656
|
abortController.abort();
|
|
11529
11657
|
taskAbortControllers.delete(id);
|
|
11530
11658
|
}
|
|
11659
|
+
rejectTaskQuestions(id, "Task cancelled by user");
|
|
11531
11660
|
const cancelledTask = {
|
|
11532
11661
|
...task,
|
|
11533
11662
|
status: "failed",
|
|
@@ -11549,6 +11678,52 @@ tasks.post("/:id/cancel", async (c) => {
|
|
|
11549
11678
|
}
|
|
11550
11679
|
return c.json({ taskId: id, status: "failed", error: "Task cancelled by user" });
|
|
11551
11680
|
});
|
|
11681
|
+
var answerQuestionSchema = z20.object({
|
|
11682
|
+
answer: z20.string().min(1),
|
|
11683
|
+
answeredBy: z20.enum(["orchestrator", "user", "system"]).optional()
|
|
11684
|
+
});
|
|
11685
|
+
tasks.post(
|
|
11686
|
+
"/:id/questions/:questionId/answer",
|
|
11687
|
+
zValidator5("json", answerQuestionSchema),
|
|
11688
|
+
async (c) => {
|
|
11689
|
+
const id = c.req.param("id");
|
|
11690
|
+
const questionId = c.req.param("questionId");
|
|
11691
|
+
const body = c.req.valid("json");
|
|
11692
|
+
const session = await sessionQueries.getById(id);
|
|
11693
|
+
if (!session) {
|
|
11694
|
+
return c.json({ error: "Task not found" }, 404);
|
|
11695
|
+
}
|
|
11696
|
+
const task = session.config?.task;
|
|
11697
|
+
if (!task?.enabled) {
|
|
11698
|
+
return c.json({ error: "Session is not a task" }, 400);
|
|
11699
|
+
}
|
|
11700
|
+
const pending = getPendingTaskQuestion(id, questionId);
|
|
11701
|
+
if (!pending) {
|
|
11702
|
+
if (getAnsweredTaskQuestion(id, questionId)) {
|
|
11703
|
+
return c.json({
|
|
11704
|
+
taskId: id,
|
|
11705
|
+
questionId,
|
|
11706
|
+
status: "answered",
|
|
11707
|
+
alreadyAnswered: true
|
|
11708
|
+
});
|
|
11709
|
+
}
|
|
11710
|
+
return c.json({ error: "Question is not pending" }, 404);
|
|
11711
|
+
}
|
|
11712
|
+
const answerStatus = answerTaskQuestion(id, questionId, {
|
|
11713
|
+
answer: body.answer,
|
|
11714
|
+
answeredBy: body.answeredBy
|
|
11715
|
+
});
|
|
11716
|
+
if (answerStatus === "not_found") {
|
|
11717
|
+
return c.json({ error: "Question is not pending" }, 404);
|
|
11718
|
+
}
|
|
11719
|
+
return c.json({
|
|
11720
|
+
taskId: id,
|
|
11721
|
+
questionId,
|
|
11722
|
+
status: "answered",
|
|
11723
|
+
alreadyAnswered: answerStatus === "already_answered"
|
|
11724
|
+
});
|
|
11725
|
+
}
|
|
11726
|
+
);
|
|
11552
11727
|
var tasks_default = tasks;
|
|
11553
11728
|
|
|
11554
11729
|
// src/server/routes/system.ts
|
|
@@ -11642,15 +11817,15 @@ function loadPublicKey(input) {
|
|
|
11642
11817
|
if (!input.includes("BEGIN") && existsSync18(input)) {
|
|
11643
11818
|
pem = readFileSync11(input, "utf8");
|
|
11644
11819
|
}
|
|
11645
|
-
const
|
|
11646
|
-
if (
|
|
11820
|
+
const key2 = createPublicKey({ key: pem, format: "pem" });
|
|
11821
|
+
if (key2.asymmetricKeyType !== "ed25519") {
|
|
11647
11822
|
throw new Error(
|
|
11648
|
-
`expected an ed25519 public key, got ${
|
|
11823
|
+
`expected an ed25519 public key, got ${key2.asymmetricKeyType}. Generate with personal-agents/scripts/generate-signing-keys.mjs.`
|
|
11649
11824
|
);
|
|
11650
11825
|
}
|
|
11651
|
-
_cachedKey =
|
|
11826
|
+
_cachedKey = key2;
|
|
11652
11827
|
_cachedFromInput = input;
|
|
11653
|
-
return
|
|
11828
|
+
return key2;
|
|
11654
11829
|
}
|
|
11655
11830
|
function bodyHashB64(body) {
|
|
11656
11831
|
const hash = createHash3("sha256");
|