tandem-mode 0.0.1
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/LICENSE +21 -0
- package/README.md +191 -0
- package/README.sr.md +194 -0
- package/dist/agent/clipboard.js +26 -0
- package/dist/agent/clipboard.js.map +1 -0
- package/dist/agent/loop.js +157 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/pricing.js +21 -0
- package/dist/agent/pricing.js.map +1 -0
- package/dist/agent/session.js +78 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/agent/tools/edit.js +78 -0
- package/dist/agent/tools/edit.js.map +1 -0
- package/dist/agent/tools/gitDiff.js +42 -0
- package/dist/agent/tools/gitDiff.js.map +1 -0
- package/dist/agent/tools/index.js +24 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/tools/listDir.js +29 -0
- package/dist/agent/tools/listDir.js.map +1 -0
- package/dist/agent/tools/readFile.js +40 -0
- package/dist/agent/tools/readFile.js.map +1 -0
- package/dist/agent/tools/search.js +51 -0
- package/dist/agent/tools/search.js.map +1 -0
- package/dist/agent/tools/shell.js +95 -0
- package/dist/agent/tools/shell.js.map +1 -0
- package/dist/agent/tools/types.js +11 -0
- package/dist/agent/tools/types.js.map +1 -0
- package/dist/agent/tools/viewImage.js +34 -0
- package/dist/agent/tools/viewImage.js.map +1 -0
- package/dist/agent/tools/webSearch.js +27 -0
- package/dist/agent/tools/webSearch.js.map +1 -0
- package/dist/agent/usage.js +36 -0
- package/dist/agent/usage.js.map +1 -0
- package/dist/benchmark/runner.js +176 -0
- package/dist/benchmark/runner.js.map +1 -0
- package/dist/benchmark/tasks/bitwiseBulkPriceWizard.js +50 -0
- package/dist/benchmark/tasks/bitwiseBulkPriceWizard.js.map +1 -0
- package/dist/benchmark/types.js +2 -0
- package/dist/benchmark/types.js.map +1 -0
- package/dist/cli.js +442 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/credentials.js +80 -0
- package/dist/config/credentials.js.map +1 -0
- package/dist/config/env.js +12 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/schema.js +6 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/store.js +35 -0
- package/dist/config/store.js.map +1 -0
- package/dist/deepseek/client.js +145 -0
- package/dist/deepseek/client.js.map +1 -0
- package/dist/deepseek/types.js +2 -0
- package/dist/deepseek/types.js.map +1 -0
- package/dist/deepseek/vision.js +59 -0
- package/dist/deepseek/vision.js.map +1 -0
- package/dist/deepseek/webSearch.js +43 -0
- package/dist/deepseek/webSearch.js.map +1 -0
- package/dist/orchestrator/orchestrate.js +63 -0
- package/dist/orchestrator/orchestrate.js.map +1 -0
- package/dist/orchestrator/planner.js +48 -0
- package/dist/orchestrator/planner.js.map +1 -0
- package/dist/orchestrator/repoMap.js +52 -0
- package/dist/orchestrator/repoMap.js.map +1 -0
- package/dist/orchestrator/reviewer.js +53 -0
- package/dist/orchestrator/reviewer.js.map +1 -0
- package/dist/orchestrator/types.js +18 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/repl/commands.js +139 -0
- package/dist/repl/commands.js.map +1 -0
- package/dist/repl/state.js +2 -0
- package/dist/repl/state.js.map +1 -0
- package/dist/repl/system-prompt.js +5 -0
- package/dist/repl/system-prompt.js.map +1 -0
- package/dist/repl/wizard.js +34 -0
- package/dist/repl/wizard.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { mkdir, appendFile, readFile, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const SESSIONS_DIR = ".tandem/sessions";
|
|
4
|
+
function sessionsDir(cwd) {
|
|
5
|
+
return path.join(cwd, SESSIONS_DIR);
|
|
6
|
+
}
|
|
7
|
+
export async function createSession(cwd) {
|
|
8
|
+
const dir = sessionsDir(cwd);
|
|
9
|
+
await mkdir(dir, { recursive: true });
|
|
10
|
+
const id = new Date().toISOString().replace(/[:.]/g, "-") + "-" + Math.random().toString(36).slice(2, 8);
|
|
11
|
+
return { id, filePath: path.join(dir, `${id}.jsonl`) };
|
|
12
|
+
}
|
|
13
|
+
export async function appendSessionMessage(session, message) {
|
|
14
|
+
const line = JSON.stringify(message) + "\n";
|
|
15
|
+
try {
|
|
16
|
+
await appendFile(session.filePath, line, "utf8");
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
// Fajl/direktorijum je nestao ispod nas (npr. agent ga je obrisao kao
|
|
20
|
+
// "nepotreban") — rekreiraj i pokušaj jednom, umesto da srušiš petlju.
|
|
21
|
+
if (err.code !== "ENOENT")
|
|
22
|
+
throw err;
|
|
23
|
+
await mkdir(path.dirname(session.filePath), { recursive: true });
|
|
24
|
+
await appendFile(session.filePath, line, "utf8");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function loadSessionMessages(filePath) {
|
|
28
|
+
const raw = await readFile(filePath, "utf8");
|
|
29
|
+
return raw
|
|
30
|
+
.split("\n")
|
|
31
|
+
.filter((line) => line.trim().length > 0)
|
|
32
|
+
.map((line) => JSON.parse(line));
|
|
33
|
+
}
|
|
34
|
+
export async function findSessionById(cwd, id) {
|
|
35
|
+
const filePath = path.join(sessionsDir(cwd), `${id}.jsonl`);
|
|
36
|
+
try {
|
|
37
|
+
await stat(filePath);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return { id, filePath };
|
|
43
|
+
}
|
|
44
|
+
export async function listSessions(cwd) {
|
|
45
|
+
const dir = sessionsDir(cwd);
|
|
46
|
+
let entries;
|
|
47
|
+
try {
|
|
48
|
+
entries = await readdir(dir);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
const jsonlFiles = entries.filter((f) => f.endsWith(".jsonl"));
|
|
54
|
+
const withMtime = await Promise.all(jsonlFiles.map(async (f) => ({ f, mtime: (await stat(path.join(dir, f))).mtimeMs })));
|
|
55
|
+
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
56
|
+
return withMtime.map(({ f }) => ({ id: f.replace(/\.jsonl$/, ""), filePath: path.join(dir, f) }));
|
|
57
|
+
}
|
|
58
|
+
export async function findLatestSession(cwd) {
|
|
59
|
+
const dir = sessionsDir(cwd);
|
|
60
|
+
let entries;
|
|
61
|
+
try {
|
|
62
|
+
entries = await readdir(dir);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const jsonlFiles = entries.filter((f) => f.endsWith(".jsonl"));
|
|
68
|
+
if (jsonlFiles.length === 0)
|
|
69
|
+
return null;
|
|
70
|
+
const withMtime = await Promise.all(jsonlFiles.map(async (f) => ({ f, mtime: (await stat(path.join(dir, f))).mtimeMs })));
|
|
71
|
+
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
72
|
+
const latest = withMtime[0];
|
|
73
|
+
if (!latest)
|
|
74
|
+
return null;
|
|
75
|
+
const id = latest.f.replace(/\.jsonl$/, "");
|
|
76
|
+
return { id, filePath: path.join(dir, latest.f) };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
function countOccurrences(haystack, needle) {
|
|
4
|
+
if (needle.length === 0)
|
|
5
|
+
return 0;
|
|
6
|
+
let count = 0;
|
|
7
|
+
let idx = 0;
|
|
8
|
+
for (;;) {
|
|
9
|
+
idx = haystack.indexOf(needle, idx);
|
|
10
|
+
if (idx === -1)
|
|
11
|
+
break;
|
|
12
|
+
count++;
|
|
13
|
+
idx += needle.length;
|
|
14
|
+
}
|
|
15
|
+
return count;
|
|
16
|
+
}
|
|
17
|
+
export const editTool = {
|
|
18
|
+
name: "edit",
|
|
19
|
+
description: "Zameni tačan string u fajlu novim stringom. `oldString` mora biti jedinstven u fajlu osim ako je replaceAll=true. Čuva originalni stil kraja linije (CRLF/LF).",
|
|
20
|
+
parameters: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
path: { type: "string", description: "Putanja do fajla." },
|
|
24
|
+
oldString: { type: "string", description: "Tačan tekst koji se menja (sa dovoljno konteksta da bude jedinstven)." },
|
|
25
|
+
newString: { type: "string", description: "Tekst kojim se zamenjuje." },
|
|
26
|
+
replaceAll: { type: "boolean", description: "Zameni sve pojave umesto zahteva za jedinstvenost." },
|
|
27
|
+
},
|
|
28
|
+
required: ["path", "oldString", "newString"],
|
|
29
|
+
},
|
|
30
|
+
// Uvek menja fajl na disku.
|
|
31
|
+
isDestructive: () => true,
|
|
32
|
+
async execute(args, ctx) {
|
|
33
|
+
const filePath = String(args["path"] ?? "");
|
|
34
|
+
const oldString = String(args["oldString"] ?? "");
|
|
35
|
+
const newString = String(args["newString"] ?? "");
|
|
36
|
+
const replaceAll = Boolean(args["replaceAll"]);
|
|
37
|
+
const resolved = path.resolve(ctx.cwd, filePath);
|
|
38
|
+
if (oldString === newString) {
|
|
39
|
+
return { output: "oldString i newString su identični — nema izmene.", isError: true };
|
|
40
|
+
}
|
|
41
|
+
let raw;
|
|
42
|
+
try {
|
|
43
|
+
raw = await readFile(resolved, "utf8");
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return { output: `Greška pri čitanju ${resolved}: ${err.message}`, isError: true };
|
|
47
|
+
}
|
|
48
|
+
const eol = raw.includes("\r\n") ? "\r\n" : "\n";
|
|
49
|
+
const normalized = raw.replace(/\r\n/g, "\n");
|
|
50
|
+
const needle = oldString.replace(/\r\n/g, "\n");
|
|
51
|
+
const replacement = newString.replace(/\r\n/g, "\n");
|
|
52
|
+
const occurrences = countOccurrences(normalized, needle);
|
|
53
|
+
if (occurrences === 0) {
|
|
54
|
+
return { output: `oldString nije pronađen u ${filePath}.`, isError: true };
|
|
55
|
+
}
|
|
56
|
+
if (occurrences > 1 && !replaceAll) {
|
|
57
|
+
return {
|
|
58
|
+
output: `oldString se pojavljuje ${occurrences} puta u ${filePath} — dodaj više konteksta da bude jedinstven, ili prosledi replaceAll=true.`,
|
|
59
|
+
isError: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const updated = replaceAll
|
|
63
|
+
? normalized.split(needle).join(replacement)
|
|
64
|
+
: normalized.replace(needle, replacement);
|
|
65
|
+
const finalContent = eol === "\r\n" ? updated.replace(/\n/g, "\r\n") : updated;
|
|
66
|
+
try {
|
|
67
|
+
await writeFile(resolved, finalContent, "utf8");
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
return { output: `Greška pri upisu ${resolved}: ${err.message}`, isError: true };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
output: `Izmenjeno ${filePath} (${occurrences} ${occurrences === 1 ? "zamena" : "zamena"}).`,
|
|
74
|
+
isError: false,
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/agent/tools/edit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
const MAX_OUTPUT_CHARS = 30_000;
|
|
5
|
+
export const gitDiffTool = {
|
|
6
|
+
name: "git_diff",
|
|
7
|
+
description: "Prikaži git diff za radni direktorijum (neustavljene izmene), opciono za jednu putanju.",
|
|
8
|
+
parameters: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
path: { type: "string", description: "Ograniči diff na ovu putanju." },
|
|
12
|
+
staged: { type: "boolean", description: "Prikaži staged izmene (--cached) umesto working tree." },
|
|
13
|
+
},
|
|
14
|
+
required: [],
|
|
15
|
+
},
|
|
16
|
+
isDestructive: () => false,
|
|
17
|
+
async execute(args, ctx) {
|
|
18
|
+
const staged = Boolean(args["staged"]);
|
|
19
|
+
const scopePath = args["path"] ? String(args["path"]) : undefined;
|
|
20
|
+
const gitArgs = ["diff", ...(staged ? ["--cached"] : []), ...(scopePath ? ["--", scopePath] : [])];
|
|
21
|
+
// Plain `git diff` ne prikazuje potpuno nove (untracked) fajlove.
|
|
22
|
+
// --intent-to-add ih markira u indeksu bez stage-ovanja sadržaja, pa se
|
|
23
|
+
// pojave u diff-u kao dodati fajlovi — bezopasno i reverzibilno.
|
|
24
|
+
try {
|
|
25
|
+
await execFileAsync("git", ["add", "--intent-to-add", "--all", ...(scopePath ? ["--", scopePath] : [])], {
|
|
26
|
+
cwd: ctx.cwd,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Nije git repo ili git nije dostupan — poziv ispod će to jasno prijaviti.
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const { stdout } = await execFileAsync("git", gitArgs, { cwd: ctx.cwd, maxBuffer: 10 * 1024 * 1024 });
|
|
34
|
+
const output = stdout.length > MAX_OUTPUT_CHARS ? stdout.slice(0, MAX_OUTPUT_CHARS) + "\n... (isečeno)" : stdout;
|
|
35
|
+
return { output: output.trim() || "(nema izmena)", isError: false };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
return { output: `Greška pri git diff: ${err.message}`, isError: true };
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=gitDiff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitDiff.js","sourceRoot":"","sources":["../../../src/agent/tools/gitDiff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC,MAAM,CAAC,MAAM,WAAW,GAAmB;IACzC,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,yFAAyF;IACtG,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+BAA+B,EAAE;YACtE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,uDAAuD,EAAE;SAClG;QACD,QAAQ,EAAE,EAAE;KACb;IACD,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK;IAC1B,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;QACrB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAEnG,kEAAkE;QAClE,wEAAwE;QACxE,iEAAiE;QACjE,IAAI,CAAC;YACH,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;gBACvG,GAAG,EAAE,GAAG,CAAC,GAAG;aACb,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,2EAA2E;QAC7E,CAAC;QAED,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;YACtG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC;YACjH,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACtE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,wBAAyB,GAAa,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACrF,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { toToolSpec } from "./types.js";
|
|
2
|
+
import { readFileTool } from "./readFile.js";
|
|
3
|
+
import { listDirTool } from "./listDir.js";
|
|
4
|
+
import { searchTool } from "./search.js";
|
|
5
|
+
import { editTool } from "./edit.js";
|
|
6
|
+
import { gitDiffTool } from "./gitDiff.js";
|
|
7
|
+
import { shellTool } from "./shell.js";
|
|
8
|
+
import { viewImageTool } from "./viewImage.js";
|
|
9
|
+
import { webSearchTool } from "./webSearch.js";
|
|
10
|
+
export const allTools = [
|
|
11
|
+
readFileTool,
|
|
12
|
+
listDirTool,
|
|
13
|
+
searchTool,
|
|
14
|
+
editTool,
|
|
15
|
+
gitDiffTool,
|
|
16
|
+
shellTool,
|
|
17
|
+
viewImageTool,
|
|
18
|
+
webSearchTool,
|
|
19
|
+
];
|
|
20
|
+
export const toolSpecs = allTools.map(toToolSpec);
|
|
21
|
+
export function findTool(name) {
|
|
22
|
+
return allTools.find((t) => t.name === name);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/agent/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,CAAC,MAAM,QAAQ,GAAqB;IACxC,YAAY;IACZ,WAAW;IACX,UAAU;IACV,QAAQ;IACR,WAAW;IACX,SAAS;IACT,aAAa;IACb,aAAa;CACd,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAElD,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export const listDirTool = {
|
|
4
|
+
name: "list_dir",
|
|
5
|
+
description: "Izlistaj sadržaj direktorijuma (ne rekurzivno).",
|
|
6
|
+
parameters: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {
|
|
9
|
+
path: { type: "string", description: "Putanja do direktorijuma. Podrazumevano radni direktorijum." },
|
|
10
|
+
},
|
|
11
|
+
required: [],
|
|
12
|
+
},
|
|
13
|
+
isDestructive: () => false,
|
|
14
|
+
async execute(args, ctx) {
|
|
15
|
+
const dirPath = String(args["path"] ?? ".");
|
|
16
|
+
const resolved = path.resolve(ctx.cwd, dirPath);
|
|
17
|
+
try {
|
|
18
|
+
const entries = await readdir(resolved, { withFileTypes: true });
|
|
19
|
+
const lines = entries
|
|
20
|
+
.map((e) => `${e.isDirectory() ? "d" : "-"} ${e.name}`)
|
|
21
|
+
.sort();
|
|
22
|
+
return { output: lines.join("\n") || "(prazno)", isError: false };
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
return { output: `Greška pri listanju ${resolved}: ${err.message}`, isError: true };
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=listDir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listDir.js","sourceRoot":"","sources":["../../../src/agent/tools/listDir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,CAAC,MAAM,WAAW,GAAmB;IACzC,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,iDAAiD;IAC9D,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6DAA6D,EAAE;SACrG;QACD,QAAQ,EAAE,EAAE;KACb;IACD,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK;IAC1B,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;QACrB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAEhD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACjE,MAAM,KAAK,GAAG,OAAO;iBAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;iBACtD,IAAI,EAAE,CAAC;YACV,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACpE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,uBAAuB,QAAQ,KAAM,GAAa,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACjG,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const MAX_LINES = 2000;
|
|
4
|
+
export const readFileTool = {
|
|
5
|
+
name: "read_file",
|
|
6
|
+
description: "Pročitaj sadržaj fajla sa brojevima linija (cat -n stil). Putanja može biti relativna na radni direktorijum.",
|
|
7
|
+
parameters: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
path: { type: "string", description: "Putanja do fajla." },
|
|
11
|
+
offset: { type: "integer", description: "Linija od koje se počinje (1-indeksirano). Podrazumevano 1." },
|
|
12
|
+
limit: { type: "integer", description: `Maksimalan broj linija za čitanje. Podrazumevano ${MAX_LINES}.` },
|
|
13
|
+
},
|
|
14
|
+
required: ["path"],
|
|
15
|
+
},
|
|
16
|
+
isDestructive: () => false,
|
|
17
|
+
async execute(args, ctx) {
|
|
18
|
+
const filePath = String(args["path"] ?? "");
|
|
19
|
+
const offset = Math.max(1, Number(args["offset"] ?? 1));
|
|
20
|
+
const limit = Math.max(1, Number(args["limit"] ?? MAX_LINES));
|
|
21
|
+
const resolved = path.resolve(ctx.cwd, filePath);
|
|
22
|
+
try {
|
|
23
|
+
const raw = await readFile(resolved, "utf8");
|
|
24
|
+
const lines = raw.split(/\r\n|\n/);
|
|
25
|
+
const slice = lines.slice(offset - 1, offset - 1 + limit);
|
|
26
|
+
const numbered = slice
|
|
27
|
+
.map((line, i) => `${String(offset + i).padStart(6, " ")}\t${line}`)
|
|
28
|
+
.join("\n");
|
|
29
|
+
const truncated = offset - 1 + limit < lines.length;
|
|
30
|
+
return {
|
|
31
|
+
output: numbered + (truncated ? `\n... (isečeno, ukupno ${lines.length} linija)` : ""),
|
|
32
|
+
isError: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
return { output: `Greška pri čitanju ${resolved}: ${err.message}`, isError: true };
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=readFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readFile.js","sourceRoot":"","sources":["../../../src/agent/tools/readFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB,MAAM,CAAC,MAAM,YAAY,GAAmB;IAC1C,IAAI,EAAE,WAAW;IACjB,WAAW,EACT,8GAA8G;IAChH,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE;YAC1D,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6DAA6D,EAAE;YACvG,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,oDAAoD,SAAS,GAAG,EAAE;SAC1G;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;IACD,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK;IAC1B,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;QACrB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAEjD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC7C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACnC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;YAC1D,MAAM,QAAQ,GAAG,KAAK;iBACnB,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;iBACnE,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,GAAG,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;YACpD,OAAO;gBACL,MAAM,EAAE,QAAQ,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,0BAA0B,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtF,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,sBAAsB,QAAQ,KAAM,GAAa,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAChG,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
const MAX_OUTPUT_CHARS = 20_000;
|
|
5
|
+
export const searchTool = {
|
|
6
|
+
name: "search",
|
|
7
|
+
description: "Pretraži sadržaj fajlova regex šablonom preko ripgrep-a (rg).",
|
|
8
|
+
parameters: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
pattern: { type: "string", description: "Regex šablon za pretragu." },
|
|
12
|
+
path: { type: "string", description: "Direktorijum ili fajl za pretragu. Podrazumevano radni direktorijum." },
|
|
13
|
+
glob: { type: "string", description: "Opcioni glob filter fajlova, npr. '*.ts'." },
|
|
14
|
+
caseInsensitive: { type: "boolean", description: "Pretraga bez razlikovanja velikih/malih slova." },
|
|
15
|
+
},
|
|
16
|
+
required: ["pattern"],
|
|
17
|
+
},
|
|
18
|
+
isDestructive: () => false,
|
|
19
|
+
async execute(args, ctx) {
|
|
20
|
+
const pattern = String(args["pattern"] ?? "");
|
|
21
|
+
const searchPath = String(args["path"] ?? ".");
|
|
22
|
+
const glob = args["glob"] ? String(args["glob"]) : undefined;
|
|
23
|
+
const caseInsensitive = Boolean(args["caseInsensitive"]);
|
|
24
|
+
const rgArgs = ["--line-number", "--with-filename", "--max-count", "200"];
|
|
25
|
+
if (caseInsensitive)
|
|
26
|
+
rgArgs.push("--ignore-case");
|
|
27
|
+
if (glob)
|
|
28
|
+
rgArgs.push("--glob", glob);
|
|
29
|
+
rgArgs.push("--", pattern, searchPath);
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await execFileAsync("rg", rgArgs, { cwd: ctx.cwd, maxBuffer: 10 * 1024 * 1024 });
|
|
32
|
+
const output = stdout.length > MAX_OUTPUT_CHARS ? stdout.slice(0, MAX_OUTPUT_CHARS) + "\n... (isečeno)" : stdout;
|
|
33
|
+
return { output: output.trim() || "(nema poklapanja)", isError: false };
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const e = err;
|
|
37
|
+
if (e.code === "ENOENT") {
|
|
38
|
+
return {
|
|
39
|
+
output: "ripgrep (rg) nije instaliran ili nije u PATH-u. Instaliraj ga da bi search alat radio.",
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// rg vraća exit code 1 kad nema poklapanja — to nije greška alata.
|
|
44
|
+
if (e.code === 1) {
|
|
45
|
+
return { output: "(nema poklapanja)", isError: false };
|
|
46
|
+
}
|
|
47
|
+
return { output: `Greška pri pretrazi: ${e.message}`, isError: true };
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/agent/tools/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
const MAX_OUTPUT_CHARS = 20_000;
|
|
3
|
+
const TIMEOUT_MS = 120_000;
|
|
4
|
+
// Konzervativna bela lista — read-only komande koje ne traže odobrenje.
|
|
5
|
+
// Sve što ne poklopi ovde se tretira kao destruktivno; bolje suvišno
|
|
6
|
+
// pitati nego tiho izvršiti nešto nepovratno.
|
|
7
|
+
const SAFE_COMMAND_PATTERNS = [
|
|
8
|
+
/^git\s+(status|log|diff|show|branch|remote(\s+-v)?)\b/i,
|
|
9
|
+
/^(ls|dir)\b/i,
|
|
10
|
+
/^pwd$/i,
|
|
11
|
+
/^(cat|type)\s+\S/i,
|
|
12
|
+
/^echo\s+/i,
|
|
13
|
+
/^(php|node|npm|pnpm|npx|python|python3)\s+(-v|--version|-l)\b/i,
|
|
14
|
+
/^(which|where)\s+\S/i,
|
|
15
|
+
];
|
|
16
|
+
function runShell(command, cwd, signal) {
|
|
17
|
+
const isWindows = process.platform === "win32";
|
|
18
|
+
const exe = isWindows ? "powershell.exe" : "/bin/sh";
|
|
19
|
+
const args = isWindows ? ["-NoProfile", "-NonInteractive", "-Command", command] : ["-c", command];
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const child = spawn(exe, args, { cwd });
|
|
22
|
+
let output = "";
|
|
23
|
+
let settled = false;
|
|
24
|
+
const timer = setTimeout(() => {
|
|
25
|
+
if (settled)
|
|
26
|
+
return;
|
|
27
|
+
settled = true;
|
|
28
|
+
child.kill();
|
|
29
|
+
reject(new Error(`Komanda je prekinuta posle ${TIMEOUT_MS / 1000}s (timeout).`));
|
|
30
|
+
}, TIMEOUT_MS);
|
|
31
|
+
const onAbort = () => {
|
|
32
|
+
if (settled)
|
|
33
|
+
return;
|
|
34
|
+
settled = true;
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
child.kill();
|
|
37
|
+
const abortError = new Error("Komanda prekinuta (ESC).");
|
|
38
|
+
abortError.name = "AbortError";
|
|
39
|
+
reject(abortError);
|
|
40
|
+
};
|
|
41
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
42
|
+
child.stdout.on("data", (chunk) => {
|
|
43
|
+
output += chunk.toString("utf8");
|
|
44
|
+
});
|
|
45
|
+
child.stderr.on("data", (chunk) => {
|
|
46
|
+
output += chunk.toString("utf8");
|
|
47
|
+
});
|
|
48
|
+
child.on("error", (err) => {
|
|
49
|
+
if (settled)
|
|
50
|
+
return;
|
|
51
|
+
settled = true;
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
signal?.removeEventListener("abort", onAbort);
|
|
54
|
+
reject(err);
|
|
55
|
+
});
|
|
56
|
+
child.on("close", (exitCode) => {
|
|
57
|
+
if (settled)
|
|
58
|
+
return;
|
|
59
|
+
settled = true;
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
signal?.removeEventListener("abort", onAbort);
|
|
62
|
+
resolve({ output, exitCode });
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export const shellTool = {
|
|
67
|
+
name: "shell",
|
|
68
|
+
description: "Izvrši komandu u sistemskoj školjki (PowerShell na Windows-u, sh na Linux/macOS) u radnom direktorijumu.",
|
|
69
|
+
parameters: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
command: { type: "string", description: "Komanda za izvršavanje." },
|
|
73
|
+
},
|
|
74
|
+
required: ["command"],
|
|
75
|
+
},
|
|
76
|
+
isDestructive(args) {
|
|
77
|
+
const command = String(args["command"] ?? "").trim();
|
|
78
|
+
return !SAFE_COMMAND_PATTERNS.some((p) => p.test(command));
|
|
79
|
+
},
|
|
80
|
+
async execute(args, ctx) {
|
|
81
|
+
const command = String(args["command"] ?? "");
|
|
82
|
+
try {
|
|
83
|
+
const { output, exitCode } = await runShell(command, ctx.cwd, ctx.signal);
|
|
84
|
+
const truncated = output.length > MAX_OUTPUT_CHARS ? output.slice(0, MAX_OUTPUT_CHARS) + "\n... (isečeno)" : output;
|
|
85
|
+
return {
|
|
86
|
+
output: `[exit code ${exitCode}]\n${truncated || "(nema izlaza)"}`,
|
|
87
|
+
isError: exitCode !== 0,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return { output: `Greška pri izvršavanju komande: ${err.message}`, isError: true };
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../../src/agent/tools/shell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,UAAU,GAAG,OAAO,CAAC;AAE3B,wEAAwE;AACxE,qEAAqE;AACrE,8CAA8C;AAC9C,MAAM,qBAAqB,GAAa;IACtC,wDAAwD;IACxD,cAAc;IACd,QAAQ;IACR,mBAAmB;IACnB,WAAW;IACX,gEAAgE;IAChE,sBAAsB;CACvB,CAAC;AAEF,SAAS,QAAQ,CACf,OAAe,EACf,GAAW,EACX,MAAoB;IAEpB,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC/C,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IACrD,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAElG,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACxC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,KAAK,CAAC,IAAI,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,UAAU,GAAG,IAAI,cAAc,CAAC,CAAC,CAAC;QACnF,CAAC,EAAE,UAAU,CAAC,CAAC;QAEf,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,CAAC,IAAI,EAAE,CAAC;YACb,MAAM,UAAU,GAAG,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACzD,UAAU,CAAC,IAAI,GAAG,YAAY,CAAC;YAC/B,MAAM,CAAC,UAAU,CAAC,CAAC;QACrB,CAAC,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3D,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE;YAC7B,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAmB;IACvC,IAAI,EAAE,OAAO;IACb,WAAW,EACT,0GAA0G;IAC5G,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yBAAyB,EAAE;SACpE;QACD,QAAQ,EAAE,CAAC,SAAS,CAAC;KACtB;IACD,aAAa,CAAC,IAAI;QAChB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrD,OAAO,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;QACrB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YAC1E,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC;YACpH,OAAO;gBACL,MAAM,EAAE,cAAc,QAAQ,MAAM,SAAS,IAAI,eAAe,EAAE;gBAClE,OAAO,EAAE,QAAQ,KAAK,CAAC;aACxB,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,mCAAoC,GAAa,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAChG,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/agent/tools/types.ts"],"names":[],"mappings":"AA0BA,MAAM,UAAU,UAAU,CAAC,IAAoB;IAC7C,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE;YACR,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { describeImage } from "../../deepseek/vision.js";
|
|
3
|
+
export const viewImageTool = {
|
|
4
|
+
name: "view_image",
|
|
5
|
+
description: "Pogledaj sliku (screenshot buga, mockup, dijagram) i dobij tekstualni opis ili odgovor na pitanje o njoj. Koristi kad korisnik pomene screenshot ili sliku i da putanju do nje. Podržani formati: JPEG, PNG, GIF, WebP.",
|
|
6
|
+
parameters: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {
|
|
9
|
+
path: { type: "string", description: "Putanja do slike." },
|
|
10
|
+
question: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Šta konkretno pitati o slici. Podrazumevano: opšti opis fokusiran na moguću grešku.",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
required: ["path"],
|
|
16
|
+
},
|
|
17
|
+
isDestructive: () => false,
|
|
18
|
+
async execute(args, ctx) {
|
|
19
|
+
const imagePath = String(args["path"] ?? "");
|
|
20
|
+
const question = args["question"] ? String(args["question"]) : undefined;
|
|
21
|
+
const resolved = path.resolve(ctx.cwd, imagePath);
|
|
22
|
+
try {
|
|
23
|
+
const { text, usage } = await describeImage(ctx.env, resolved, question, ctx.signal);
|
|
24
|
+
// vision-exp nema objavljen poseban cenovnik — trošak se prijavljuje
|
|
25
|
+
// po Flash tarifi kao najbolja dostupna procena (stvarni tokeni su tačni).
|
|
26
|
+
ctx.usage.add("deepseek-v4-flash", usage);
|
|
27
|
+
return { output: text, isError: false };
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
return { output: `Greška pri analizi slike: ${err.message}`, isError: true };
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=viewImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewImage.js","sourceRoot":"","sources":["../../../src/agent/tools/viewImage.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGzD,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,yNAAyN;IAC3N,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE;YAC1D,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,qFAAqF;aACnG;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;IACD,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK;IAC1B,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;QACrB,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAElD,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACrF,qEAAqE;YACrE,2EAA2E;YAC3E,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;YAC1C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,6BAA8B,GAAa,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC1F,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { runWebSearch } from "../../deepseek/webSearch.js";
|
|
2
|
+
export const webSearchTool = {
|
|
3
|
+
name: "web_search",
|
|
4
|
+
description: "Pretraži internet za trenutne informacije — error poruke, dokumentaciju biblioteka, novosti. Koristi kad odgovor zahteva nešto što nije u kodu ili repo kontekstu.",
|
|
5
|
+
parameters: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
query: { type: "string", description: "Upit za pretragu." },
|
|
9
|
+
},
|
|
10
|
+
required: ["query"],
|
|
11
|
+
},
|
|
12
|
+
isDestructive: () => false,
|
|
13
|
+
async execute(args, ctx) {
|
|
14
|
+
const query = String(args["query"] ?? "");
|
|
15
|
+
try {
|
|
16
|
+
const { text, usage } = await runWebSearch(ctx.env, query, ctx.signal);
|
|
17
|
+
// Responses API nema objavljen zaseban cenovnik — trošak se prijavljuje
|
|
18
|
+
// po Flash tarifi kao najbolja dostupna procena (stvarni tokeni su tačni).
|
|
19
|
+
ctx.usage.add("deepseek-v4-flash", usage);
|
|
20
|
+
return { output: text, isError: false };
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return { output: `Greška pri pretrazi: ${err.message}`, isError: true };
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=webSearch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSearch.js","sourceRoot":"","sources":["../../../src/agent/tools/webSearch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAG3D,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,oKAAoK;IACtK,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE;SAC5D;QACD,QAAQ,EAAE,CAAC,OAAO,CAAC;KACpB;IACD,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK;IAC1B,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;QACrB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACvE,wEAAwE;YACxE,2EAA2E;YAC3E,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;YAC1C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,wBAAyB,GAAa,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACrF,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { estimateCallCost, isPeakHour } from "./pricing.js";
|
|
2
|
+
/** Isključivo iz API `usage` polja — nikad procena tokena na klijentu. */
|
|
3
|
+
export class UsageAccumulator {
|
|
4
|
+
entries = [];
|
|
5
|
+
add(model, usage) {
|
|
6
|
+
this.entries.push({ model, usage, isPeak: isPeakHour(new Date()) });
|
|
7
|
+
}
|
|
8
|
+
get callCount() {
|
|
9
|
+
return this.entries.length;
|
|
10
|
+
}
|
|
11
|
+
totals() {
|
|
12
|
+
return this.entries.reduce((acc, e) => ({
|
|
13
|
+
promptTokens: acc.promptTokens + e.usage.promptTokens,
|
|
14
|
+
completionTokens: acc.completionTokens + e.usage.completionTokens,
|
|
15
|
+
totalTokens: acc.totalTokens + e.usage.totalTokens,
|
|
16
|
+
promptCacheHitTokens: acc.promptCacheHitTokens + e.usage.promptCacheHitTokens,
|
|
17
|
+
promptCacheMissTokens: acc.promptCacheMissTokens + e.usage.promptCacheMissTokens,
|
|
18
|
+
reasoningTokens: acc.reasoningTokens + e.usage.reasoningTokens,
|
|
19
|
+
}), {
|
|
20
|
+
promptTokens: 0,
|
|
21
|
+
completionTokens: 0,
|
|
22
|
+
totalTokens: 0,
|
|
23
|
+
promptCacheHitTokens: 0,
|
|
24
|
+
promptCacheMissTokens: 0,
|
|
25
|
+
reasoningTokens: 0,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
estimatedCostUsd() {
|
|
29
|
+
return this.entries.reduce((sum, e) => sum + estimateCallCost(e.model, e.usage, e.isPeak), 0);
|
|
30
|
+
}
|
|
31
|
+
/** Koliko bi ova sesija koštala da je svaki poziv pao u off-peak prozor. */
|
|
32
|
+
offPeakEquivalentCostUsd() {
|
|
33
|
+
return this.entries.reduce((sum, e) => sum + estimateCallCost(e.model, e.usage, false), 0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/agent/usage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAQ5D,0EAA0E;AAC1E,MAAM,OAAO,gBAAgB;IACnB,OAAO,GAAiB,EAAE,CAAC;IAEnC,GAAG,CAAC,KAA8C,EAAE,KAAgB;QAClE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7B,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CACxB,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACX,YAAY,EAAE,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,YAAY;YACrD,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,gBAAgB;YACjE,WAAW,EAAE,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,WAAW;YAClD,oBAAoB,EAAE,GAAG,CAAC,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,oBAAoB;YAC7E,qBAAqB,EAAE,GAAG,CAAC,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,qBAAqB;YAChF,eAAe,EAAE,GAAG,CAAC,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,eAAe;SAC/D,CAAC,EACF;YACE,YAAY,EAAE,CAAC;YACf,gBAAgB,EAAE,CAAC;YACnB,WAAW,EAAE,CAAC;YACd,oBAAoB,EAAE,CAAC;YACvB,qBAAqB,EAAE,CAAC;YACxB,eAAe,EAAE,CAAC;SACnB,CACF,CAAC;IACJ,CAAC;IAED,gBAAgB;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,4EAA4E;IAC5E,wBAAwB;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7F,CAAC;CACF"}
|