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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +191 -0
  3. package/README.sr.md +194 -0
  4. package/dist/agent/clipboard.js +26 -0
  5. package/dist/agent/clipboard.js.map +1 -0
  6. package/dist/agent/loop.js +157 -0
  7. package/dist/agent/loop.js.map +1 -0
  8. package/dist/agent/pricing.js +21 -0
  9. package/dist/agent/pricing.js.map +1 -0
  10. package/dist/agent/session.js +78 -0
  11. package/dist/agent/session.js.map +1 -0
  12. package/dist/agent/tools/edit.js +78 -0
  13. package/dist/agent/tools/edit.js.map +1 -0
  14. package/dist/agent/tools/gitDiff.js +42 -0
  15. package/dist/agent/tools/gitDiff.js.map +1 -0
  16. package/dist/agent/tools/index.js +24 -0
  17. package/dist/agent/tools/index.js.map +1 -0
  18. package/dist/agent/tools/listDir.js +29 -0
  19. package/dist/agent/tools/listDir.js.map +1 -0
  20. package/dist/agent/tools/readFile.js +40 -0
  21. package/dist/agent/tools/readFile.js.map +1 -0
  22. package/dist/agent/tools/search.js +51 -0
  23. package/dist/agent/tools/search.js.map +1 -0
  24. package/dist/agent/tools/shell.js +95 -0
  25. package/dist/agent/tools/shell.js.map +1 -0
  26. package/dist/agent/tools/types.js +11 -0
  27. package/dist/agent/tools/types.js.map +1 -0
  28. package/dist/agent/tools/viewImage.js +34 -0
  29. package/dist/agent/tools/viewImage.js.map +1 -0
  30. package/dist/agent/tools/webSearch.js +27 -0
  31. package/dist/agent/tools/webSearch.js.map +1 -0
  32. package/dist/agent/usage.js +36 -0
  33. package/dist/agent/usage.js.map +1 -0
  34. package/dist/benchmark/runner.js +176 -0
  35. package/dist/benchmark/runner.js.map +1 -0
  36. package/dist/benchmark/tasks/bitwiseBulkPriceWizard.js +50 -0
  37. package/dist/benchmark/tasks/bitwiseBulkPriceWizard.js.map +1 -0
  38. package/dist/benchmark/types.js +2 -0
  39. package/dist/benchmark/types.js.map +1 -0
  40. package/dist/cli.js +442 -0
  41. package/dist/cli.js.map +1 -0
  42. package/dist/config/credentials.js +80 -0
  43. package/dist/config/credentials.js.map +1 -0
  44. package/dist/config/env.js +12 -0
  45. package/dist/config/env.js.map +1 -0
  46. package/dist/config/schema.js +6 -0
  47. package/dist/config/schema.js.map +1 -0
  48. package/dist/config/store.js +35 -0
  49. package/dist/config/store.js.map +1 -0
  50. package/dist/deepseek/client.js +145 -0
  51. package/dist/deepseek/client.js.map +1 -0
  52. package/dist/deepseek/types.js +2 -0
  53. package/dist/deepseek/types.js.map +1 -0
  54. package/dist/deepseek/vision.js +59 -0
  55. package/dist/deepseek/vision.js.map +1 -0
  56. package/dist/deepseek/webSearch.js +43 -0
  57. package/dist/deepseek/webSearch.js.map +1 -0
  58. package/dist/orchestrator/orchestrate.js +63 -0
  59. package/dist/orchestrator/orchestrate.js.map +1 -0
  60. package/dist/orchestrator/planner.js +48 -0
  61. package/dist/orchestrator/planner.js.map +1 -0
  62. package/dist/orchestrator/repoMap.js +52 -0
  63. package/dist/orchestrator/repoMap.js.map +1 -0
  64. package/dist/orchestrator/reviewer.js +53 -0
  65. package/dist/orchestrator/reviewer.js.map +1 -0
  66. package/dist/orchestrator/types.js +18 -0
  67. package/dist/orchestrator/types.js.map +1 -0
  68. package/dist/repl/commands.js +139 -0
  69. package/dist/repl/commands.js.map +1 -0
  70. package/dist/repl/state.js +2 -0
  71. package/dist/repl/state.js.map +1 -0
  72. package/dist/repl/system-prompt.js +5 -0
  73. package/dist/repl/system-prompt.js.map +1 -0
  74. package/dist/repl/wizard.js +34 -0
  75. package/dist/repl/wizard.js.map +1 -0
  76. package/package.json +49 -0
@@ -0,0 +1,145 @@
1
+ async function* readSseLines(body) {
2
+ const reader = body.getReader();
3
+ const decoder = new TextDecoder();
4
+ let buffer = "";
5
+ try {
6
+ for (;;) {
7
+ const { done, value } = await reader.read();
8
+ if (done)
9
+ break;
10
+ buffer += decoder.decode(value, { stream: true });
11
+ const lines = buffer.split("\n");
12
+ buffer = lines.pop() ?? "";
13
+ for (const line of lines) {
14
+ yield line;
15
+ }
16
+ }
17
+ }
18
+ finally {
19
+ reader.releaseLock();
20
+ }
21
+ }
22
+ export function parseUsage(raw) {
23
+ const completionDetails = raw["completion_tokens_details"];
24
+ return {
25
+ promptTokens: Number(raw["prompt_tokens"] ?? 0),
26
+ completionTokens: Number(raw["completion_tokens"] ?? 0),
27
+ totalTokens: Number(raw["total_tokens"] ?? 0),
28
+ promptCacheHitTokens: Number(raw["prompt_cache_hit_tokens"] ?? 0),
29
+ promptCacheMissTokens: Number(raw["prompt_cache_miss_tokens"] ?? 0),
30
+ reasoningTokens: Number(completionDetails?.["reasoning_tokens"] ?? 0),
31
+ };
32
+ }
33
+ function toWireMessage(msg) {
34
+ const wire = { role: msg.role, content: msg.content };
35
+ if (msg.reasoningContent !== undefined)
36
+ wire["reasoning_content"] = msg.reasoningContent;
37
+ if (msg.toolCalls !== undefined)
38
+ wire["tool_calls"] = msg.toolCalls;
39
+ if (msg.toolCallId !== undefined)
40
+ wire["tool_call_id"] = msg.toolCallId;
41
+ return wire;
42
+ }
43
+ export async function* streamChatCompletion(env, params) {
44
+ const response = await fetch(`${env.baseUrl}/chat/completions`, {
45
+ method: "POST",
46
+ headers: {
47
+ "Content-Type": "application/json",
48
+ Authorization: `Bearer ${env.apiKey}`,
49
+ },
50
+ ...(params.signal ? { signal: params.signal } : {}),
51
+ body: JSON.stringify({
52
+ model: params.model,
53
+ messages: params.messages.map(toWireMessage),
54
+ tools: params.tools,
55
+ stream: true,
56
+ // Bez ovoga usage stiže kao null u svakom chunk-u.
57
+ stream_options: { include_usage: true },
58
+ thinking: params.thinking
59
+ ? { type: "enabled", reasoning_effort: params.thinking.reasoningEffort }
60
+ : { type: "disabled" },
61
+ response_format: params.jsonOutput ? { type: "json_object" } : undefined,
62
+ }),
63
+ });
64
+ if (!response.ok || !response.body) {
65
+ const body = await response.text().catch(() => "");
66
+ throw new Error(`DeepSeek API error ${response.status}: ${body}`);
67
+ }
68
+ for await (const line of readSseLines(response.body)) {
69
+ if (!line.startsWith("data:"))
70
+ continue;
71
+ const payload = line.slice("data:".length).trim();
72
+ if (payload === "[DONE]")
73
+ continue;
74
+ if (!payload)
75
+ continue;
76
+ const parsed = JSON.parse(payload);
77
+ if (parsed["usage"]) {
78
+ yield { type: "usage", usage: parseUsage(parsed["usage"]) };
79
+ }
80
+ const choices = parsed["choices"];
81
+ const choice = choices?.[0];
82
+ const delta = choice?.["delta"];
83
+ if (delta) {
84
+ if (typeof delta["content"] === "string" && delta["content"].length > 0) {
85
+ yield { type: "content_delta", delta: delta["content"] };
86
+ }
87
+ if (typeof delta["reasoning_content"] === "string" && delta["reasoning_content"].length > 0) {
88
+ yield { type: "reasoning_delta", delta: delta["reasoning_content"] };
89
+ }
90
+ const toolCalls = delta["tool_calls"];
91
+ if (toolCalls) {
92
+ for (const tc of toolCalls) {
93
+ const fn = tc["function"];
94
+ const event = {
95
+ type: "tool_call_delta",
96
+ index: Number(tc["index"] ?? 0),
97
+ };
98
+ if (typeof tc["id"] === "string")
99
+ event.id = tc["id"];
100
+ if (typeof fn?.["name"] === "string")
101
+ event.name = fn["name"];
102
+ if (typeof fn?.["arguments"] === "string")
103
+ event.argumentsDelta = fn["arguments"];
104
+ yield event;
105
+ }
106
+ }
107
+ }
108
+ const finishReason = choice?.["finish_reason"];
109
+ if (typeof finishReason === "string") {
110
+ yield { type: "done", finishReason };
111
+ }
112
+ }
113
+ }
114
+ /** Non-streaming poziv za planner/reviewer — bez tools, samo tekst/JSON + usage odjednom. */
115
+ export async function chatCompletionOnce(env, params) {
116
+ const response = await fetch(`${env.baseUrl}/chat/completions`, {
117
+ method: "POST",
118
+ headers: {
119
+ "Content-Type": "application/json",
120
+ Authorization: `Bearer ${env.apiKey}`,
121
+ },
122
+ ...(params.signal ? { signal: params.signal } : {}),
123
+ body: JSON.stringify({
124
+ model: params.model,
125
+ messages: params.messages.map(toWireMessage),
126
+ stream: false,
127
+ thinking: params.thinking
128
+ ? { type: "enabled", reasoning_effort: params.thinking.reasoningEffort }
129
+ : { type: "disabled" },
130
+ response_format: params.jsonOutput ? { type: "json_object" } : undefined,
131
+ }),
132
+ });
133
+ if (!response.ok) {
134
+ const body = await response.text().catch(() => "");
135
+ throw new Error(`DeepSeek API error ${response.status}: ${body}`);
136
+ }
137
+ const data = (await response.json());
138
+ const message = data["choices"]?.[0]?.["message"];
139
+ return {
140
+ content: typeof message?.["content"] === "string" ? message["content"] : "",
141
+ reasoningContent: typeof message?.["reasoning_content"] === "string" ? message["reasoning_content"] : "",
142
+ usage: parseUsage(data["usage"] ?? {}),
143
+ };
144
+ }
145
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/deepseek/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/deepseek/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { readFile, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parseUsage } from "./client.js";
4
+ const MAX_IMAGE_BYTES = 32 * 1024 * 1024;
5
+ const MIME_BY_EXT = {
6
+ ".jpg": "image/jpeg",
7
+ ".jpeg": "image/jpeg",
8
+ ".png": "image/png",
9
+ ".gif": "image/gif",
10
+ ".webp": "image/webp",
11
+ };
12
+ const DEFAULT_QUESTION = "Opiši detaljno šta se vidi na slici — ako je screenshot buga, fokusiraj se na vidljivu grešku, tekst poruke, i UI kontekst.";
13
+ export async function describeImage(env, imagePath, question, signal) {
14
+ const ext = path.extname(imagePath).toLowerCase();
15
+ const mime = MIME_BY_EXT[ext];
16
+ if (!mime) {
17
+ throw new Error(`Nepodržan format slike '${ext}'. Podržano: JPEG, PNG, GIF, WebP.`);
18
+ }
19
+ const stats = await stat(imagePath);
20
+ if (stats.size > MAX_IMAGE_BYTES) {
21
+ throw new Error(`Slika je prevelika (${(stats.size / 1024 / 1024).toFixed(1)} MiB, limit 32 MiB).`);
22
+ }
23
+ const buffer = await readFile(imagePath);
24
+ const dataUri = `data:${mime};base64,${buffer.toString("base64")}`;
25
+ const response = await fetch(`${env.baseUrl}/chat/completions`, {
26
+ method: "POST",
27
+ headers: {
28
+ "Content-Type": "application/json",
29
+ Authorization: `Bearer ${env.apiKey}`,
30
+ },
31
+ ...(signal ? { signal } : {}),
32
+ body: JSON.stringify({
33
+ model: "deepseek-v4-flash-vision-exp",
34
+ stream: false,
35
+ thinking: { type: "disabled" },
36
+ messages: [
37
+ {
38
+ role: "user",
39
+ content: [
40
+ { type: "text", text: question?.trim() || DEFAULT_QUESTION },
41
+ { type: "image_url", image_url: { url: dataUri } },
42
+ ],
43
+ },
44
+ ],
45
+ }),
46
+ });
47
+ if (!response.ok) {
48
+ const body = await response.text().catch(() => "");
49
+ throw new Error(`DeepSeek vision API error ${response.status}: ${body}`);
50
+ }
51
+ const data = (await response.json());
52
+ const message = data["choices"]?.[0]?.["message"];
53
+ const content = message?.["content"];
54
+ if (typeof content !== "string" || !content) {
55
+ throw new Error("Vision odgovor nije sadržao tekst.");
56
+ }
57
+ return { text: content, usage: parseUsage(data["usage"] ?? {}) };
58
+ }
59
+ //# sourceMappingURL=vision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision.js","sourceRoot":"","sources":["../../src/deepseek/vision.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ function parseResponsesUsage(raw) {
2
+ return {
3
+ promptTokens: Number(raw["input_tokens"] ?? 0),
4
+ completionTokens: Number(raw["output_tokens"] ?? 0),
5
+ totalTokens: Number(raw["total_tokens"] ?? 0),
6
+ // Responses API keširanje je potpuno automatsko — ne prijavljuje hit/miss
7
+ // razdvojeno na isti način kao Chat Completions (docs/api-notes.md #5).
8
+ promptCacheHitTokens: Number(raw["cached_tokens"] ?? 0),
9
+ promptCacheMissTokens: 0,
10
+ reasoningTokens: 0,
11
+ };
12
+ }
13
+ /** `/responses` je stateless — izolovan poziv, bez uticaja na glavnu tool-calling petlju ili njen keš. */
14
+ export async function runWebSearch(env, query, signal) {
15
+ const response = await fetch(`${env.baseUrl}/responses`, {
16
+ method: "POST",
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ Authorization: `Bearer ${env.apiKey}`,
20
+ },
21
+ ...(signal ? { signal } : {}),
22
+ body: JSON.stringify({
23
+ model: "deepseek-v4-flash",
24
+ input: query,
25
+ tools: [{ type: "web_search" }],
26
+ tool_choice: { type: "web_search" },
27
+ }),
28
+ });
29
+ if (!response.ok) {
30
+ const body = await response.text().catch(() => "");
31
+ throw new Error(`DeepSeek Responses API error ${response.status}: ${body}`);
32
+ }
33
+ const data = (await response.json());
34
+ const output = data["output"] ?? [];
35
+ const message = output.find((item) => item["type"] === "message");
36
+ const content = message?.["content"];
37
+ const text = content?.find((c) => typeof c["text"] === "string")?.["text"];
38
+ if (!text) {
39
+ throw new Error("Web search odgovor nije sadržao tekst.");
40
+ }
41
+ return { text, usage: parseResponsesUsage(data["usage"] ?? {}) };
42
+ }
43
+ //# sourceMappingURL=webSearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webSearch.js","sourceRoot":"","sources":["../../src/deepseek/webSearch.ts"],"names":[],"mappings":"AAQA,SAAS,mBAAmB,CAAC,GAA4B;IACvD,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC9C,gBAAgB,EAAE,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACnD,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC7C,0EAA0E;QAC1E,wEAAwE;QACxE,oBAAoB,EAAE,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACvD,qBAAqB,EAAE,CAAC;QACxB,eAAe,EAAE,CAAC;KACnB,CAAC;AACJ,CAAC;AAED,0GAA0G;AAC1G,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAc,EAAE,KAAa,EAAE,MAAoB;IACpF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,YAAY,EAAE;QACvD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,GAAG,CAAC,MAAM,EAAE;SACtC;QACD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK,EAAE,mBAAmB;YAC1B,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;YAC/B,WAAW,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;SACpC,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;IAChE,MAAM,MAAM,GAAI,IAAI,CAAC,QAAQ,CAAgD,IAAI,EAAE,CAAC;IACpF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC,SAAS,CAA+C,CAAC;IACnF,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAuB,CAAC;IAEjG,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,mBAAmB,CAAE,IAAI,CAAC,OAAO,CAA6B,IAAI,EAAE,CAAC,EAAE,CAAC;AAChG,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { gitDiffTool } from "../agent/tools/gitDiff.js";
2
+ import { buildRepoMap } from "./repoMap.js";
3
+ import { runPlanner } from "./planner.js";
4
+ import { runReviewer } from "./reviewer.js";
5
+ const WORKER_SYSTEM_PROMPT = `Ti si worker u Tandem Mode orkestraciji. Dobijaš JEDAN konkretan task iz
6
+ plana ispod, sa mapom repozitorijuma. Nemaš pristup razgovoru koji je
7
+ proizveo plan — sve što ti treba je ovde. Izvrši task koristeći alate
8
+ (read_file, list_dir, search, edit, git_diff, shell, view_image). Kad
9
+ završiš, odgovori sažeto bez tool poziva da se petlja zaustavi.`;
10
+ function buildStablePrefix(plan, repoMap) {
11
+ return `${WORKER_SYSTEM_PROMPT}\n\nPlan (ceo, za kontekst):\n${JSON.stringify(plan)}\n\nMapa repozitorijuma:\n${repoMap}`;
12
+ }
13
+ export async function runOrchestration(task, opts) {
14
+ console.log("\n📋 Planiram...\n");
15
+ const repoMap = await buildRepoMap(opts.cwd);
16
+ const { plan, usage: plannerUsage } = await runPlanner(opts.env, task, repoMap, opts.signal);
17
+ opts.usage.add("deepseek-v4-pro", plannerUsage);
18
+ console.log(`Plan: ${plan.summary}`);
19
+ plan.tasks.forEach((t, i) => console.log(` ${i + 1}. [${t.id}] ${t.description}`));
20
+ const stablePrefix = buildStablePrefix(plan, repoMap);
21
+ const workerThinking = { reasoningEffort: "high" };
22
+ for (const t of plan.tasks) {
23
+ if (opts.signal?.aborted)
24
+ return;
25
+ console.log(`\n🔧 Worker: ${t.description}\n`);
26
+ const messages = [
27
+ { role: "system", content: stablePrefix },
28
+ { role: "user", content: `Task: ${t.description}\nKriterijum prihvatanja: ${t.acceptanceCriteria}` },
29
+ ];
30
+ await opts.runWorkerTurn({ messages, model: "deepseek-v4-flash", thinking: workerThinking, ...(opts.signal ? { signal: opts.signal } : {}) });
31
+ }
32
+ let loops = 0;
33
+ for (;;) {
34
+ if (opts.signal?.aborted)
35
+ return;
36
+ console.log("\n🔍 Reviewer proverava...\n");
37
+ const diffResult = await gitDiffTool.execute({}, { cwd: opts.cwd, env: opts.env, usage: opts.usage });
38
+ const { result: review, usage: reviewUsage } = await runReviewer(opts.env, task, plan, diffResult.output, opts.signal);
39
+ opts.usage.add("deepseek-v4-pro", reviewUsage);
40
+ if (review.approved) {
41
+ console.log(`✅ Reviewer odobrio. ${review.notes}\n`);
42
+ return;
43
+ }
44
+ loops++;
45
+ console.log(`⚠ Reviewer traži izmene (${loops}/${opts.maxReviewLoops}): ${review.notes}`);
46
+ review.corrections.forEach((c) => console.log(` - ${c.description}`));
47
+ if (loops >= opts.maxReviewLoops) {
48
+ console.log(`\n❌ Dostignut max_review_loops (${opts.maxReviewLoops}) bez odobrenja — proveri ručno.\n`);
49
+ return;
50
+ }
51
+ for (const correction of review.corrections) {
52
+ if (opts.signal?.aborted)
53
+ return;
54
+ console.log(`\n🔧 Worker (korekcija): ${correction.description}\n`);
55
+ const messages = [
56
+ { role: "system", content: stablePrefix },
57
+ { role: "user", content: `Korekcija od reviewera: ${correction.description}` },
58
+ ];
59
+ await opts.runWorkerTurn({ messages, model: "deepseek-v4-flash", thinking: workerThinking, ...(opts.signal ? { signal: opts.signal } : {}) });
60
+ }
61
+ }
62
+ }
63
+ //# sourceMappingURL=orchestrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../src/orchestrator/orchestrate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { chatCompletionOnce } from "../deepseek/client.js";
2
+ import { isPlan } from "./types.js";
3
+ const PLANNER_SYSTEM_PROMPT = `Ti si planner u Tandem Mode coding agentu. Dobijaš zadatak i mapu
4
+ repozitorijuma. Podeli zadatak na male, nezavisne korake koje worker model
5
+ (bez pristupa ovom razgovoru, samo alatima read_file/list_dir/search/edit/
6
+ git_diff/shell/view_image) može samostalno izvršiti.
7
+
8
+ Odgovori ISKLJUČIVO validnim JSON-om, bez markdown ograda, tačno ove šeme:
9
+ {
10
+ "summary": "kratak opis pristupa",
11
+ "tasks": [
12
+ { "id": "t1", "description": "...", "files": ["..."], "acceptanceCriteria": "..." }
13
+ ]
14
+ }`;
15
+ const MAX_ATTEMPTS = 2;
16
+ export async function runPlanner(env, task, repoMap, signal) {
17
+ let lastError;
18
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
19
+ const result = await chatCompletionOnce(env, {
20
+ model: "deepseek-v4-pro",
21
+ thinking: { reasoningEffort: "max" },
22
+ jsonOutput: true,
23
+ messages: [
24
+ { role: "system", content: PLANNER_SYSTEM_PROMPT },
25
+ { role: "user", content: `Zadatak:\n${task}\n\nMapa repozitorijuma:\n${repoMap}` },
26
+ ],
27
+ ...(signal ? { signal } : {}),
28
+ });
29
+ let parsed;
30
+ try {
31
+ parsed = JSON.parse(result.content);
32
+ }
33
+ catch {
34
+ lastError = new Error(`Planner nije vratio validan JSON (pokušaj ${attempt}):\n${result.content.slice(0, 500)}`);
35
+ continue;
36
+ }
37
+ if (!isPlan(parsed)) {
38
+ lastError = new Error(`Planner JSON ne prati očekivanu šemu (pokušaj ${attempt}):\n${result.content.slice(0, 500)}`);
39
+ continue;
40
+ }
41
+ return { plan: parsed, usage: result.usage };
42
+ }
43
+ // Poznato ponašanje: json_object mod povremeno vrati prazan/nevalidan
44
+ // sadržaj (verovatno prolazna API nestabilnost, ne dosledna greška —
45
+ // videti docs/api-notes.md). Jedan retry pokriva to bez rušenja pozivaoca.
46
+ throw lastError ?? new Error("Planner nije uspeo posle više pokušaja.");
47
+ }
48
+ //# sourceMappingURL=planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../../src/orchestrator/planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAE,MAAM,EAAa,MAAM,YAAY,CAAC;AAE/C,MAAM,qBAAqB,GAAG;;;;;;;;;;;EAW5B,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAc,EACd,IAAY,EACZ,OAAe,EACf,MAAoB;IAEpB,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE;YAC3C,KAAK,EAAE,iBAAiB;YACxB,QAAQ,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;YACpC,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,EAAE;gBAClD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,IAAI,6BAA6B,OAAO,EAAE,EAAE;aACnF;YACD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAAC;QAEH,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,GAAG,IAAI,KAAK,CAAC,6CAA6C,OAAO,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACjH,SAAS;QACX,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACpB,SAAS,GAAG,IAAI,KAAK,CAAC,iDAAiD,OAAO,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACrH,SAAS;QACX,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,sEAAsE;IACtE,qEAAqE;IACrE,2EAA2E;IAC3E,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { readdir } from "node:fs/promises";
4
+ import path from "node:path";
5
+ const execFileAsync = promisify(execFile);
6
+ const MAX_FILES = 300;
7
+ async function listViaGit(cwd) {
8
+ try {
9
+ const { stdout } = await execFileAsync("git", ["ls-files"], { cwd, maxBuffer: 10 * 1024 * 1024 });
10
+ return stdout.split("\n").filter(Boolean);
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ async function listViaFs(cwd) {
17
+ const results = [];
18
+ async function walk(dir) {
19
+ if (results.length >= MAX_FILES)
20
+ return;
21
+ let entries;
22
+ try {
23
+ entries = await readdir(dir, { withFileTypes: true });
24
+ }
25
+ catch {
26
+ return;
27
+ }
28
+ for (const entry of entries) {
29
+ if (results.length >= MAX_FILES)
30
+ return;
31
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist")
32
+ continue;
33
+ const full = path.join(dir, entry.name);
34
+ if (entry.isDirectory()) {
35
+ await walk(full);
36
+ }
37
+ else {
38
+ results.push(path.relative(cwd, full));
39
+ }
40
+ }
41
+ }
42
+ await walk(cwd);
43
+ return results;
44
+ }
45
+ /** Bajt-za-bajt stabilan string dok se ne promeni sadržaj repoa — deo je fiksnog prefiksa za worker pozive. */
46
+ export async function buildRepoMap(cwd) {
47
+ const files = (await listViaGit(cwd)) ?? (await listViaFs(cwd));
48
+ const truncated = files.length > MAX_FILES;
49
+ const listed = files.slice(0, MAX_FILES).sort();
50
+ return (listed.join("\n") + (truncated ? `\n... (isečeno, ukupno ${files.length} fajlova)` : ""));
51
+ }
52
+ //# sourceMappingURL=repoMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repoMap.js","sourceRoot":"","sources":["../../src/orchestrator/repoMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,KAAK,UAAU,UAAU,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QAClG,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,GAAW;IAClC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,UAAU,IAAI,CAAC,GAAW;QAC7B,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS;YAAE,OAAO;QACxC,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS;gBAAE,OAAO;YACxC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;gBAAE,SAAS;YAC9F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,MAAM,KAAK,GAAG,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,OAAO,CACL,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,0BAA0B,KAAK,CAAC,MAAM,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CACzF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { chatCompletionOnce } from "../deepseek/client.js";
2
+ import { isReviewResult } from "./types.js";
3
+ const REVIEWER_SYSTEM_PROMPT = `Ti si reviewer u Tandem Mode coding agentu. Dobijaš originalni zadatak,
4
+ plan koji je worker(i) izvršio, i git diff stvarnih izmena. Proveri da li
5
+ diff zaista ispunjava zadatak i kriterijume prihvatanja iz plana, da nema
6
+ nepotrebnih izmena, i da je kod razuman.
7
+
8
+ Odgovori ISKLJUČIVO validnim JSON-om, bez markdown ograda, tačno ove šeme:
9
+ {
10
+ "approved": true|false,
11
+ "notes": "kratko obrazloženje",
12
+ "corrections": [{ "description": "...", "files": ["..."] }]
13
+ }
14
+ "corrections" je prazan niz ako je approved true.`;
15
+ const MAX_ATTEMPTS = 2;
16
+ export async function runReviewer(env, originalTask, plan, gitDiff, signal) {
17
+ let lastError;
18
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
19
+ const response = await chatCompletionOnce(env, {
20
+ model: "deepseek-v4-pro",
21
+ thinking: { reasoningEffort: "high" },
22
+ jsonOutput: true,
23
+ messages: [
24
+ { role: "system", content: REVIEWER_SYSTEM_PROMPT },
25
+ {
26
+ role: "user",
27
+ content: `Originalni zadatak:\n${originalTask}\n\nPlan:\n${JSON.stringify(plan, null, 2)}\n\nGit diff:\n${gitDiff.slice(0, 15000)}`,
28
+ },
29
+ ],
30
+ ...(signal ? { signal } : {}),
31
+ });
32
+ let parsed;
33
+ try {
34
+ parsed = JSON.parse(response.content);
35
+ }
36
+ catch {
37
+ lastError = new Error(`Reviewer nije vratio validan JSON (pokušaj ${attempt}):\n${response.content.slice(0, 500)}`);
38
+ continue;
39
+ }
40
+ if (!isReviewResult(parsed)) {
41
+ lastError = new Error(`Reviewer JSON ne prati očekivanu šemu (pokušaj ${attempt}):\n${response.content.slice(0, 500)}`);
42
+ continue;
43
+ }
44
+ const result = {
45
+ approved: parsed.approved,
46
+ notes: parsed.notes ?? "",
47
+ corrections: parsed.corrections ?? [],
48
+ };
49
+ return { result, usage: response.usage };
50
+ }
51
+ throw lastError ?? new Error("Reviewer nije uspeo posle više pokušaja.");
52
+ }
53
+ //# sourceMappingURL=reviewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewer.js","sourceRoot":"","sources":["../../src/orchestrator/reviewer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAgC,MAAM,YAAY,CAAC;AAE1E,MAAM,sBAAsB,GAAG;;;;;;;;;;;kDAWmB,CAAC;AAEnD,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAc,EACd,YAAoB,EACpB,IAAU,EACV,OAAe,EACf,MAAoB;IAEpB,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;QACzD,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE;YAC7C,KAAK,EAAE,iBAAiB;YACxB,QAAQ,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE;YACrC,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,sBAAsB,EAAE;gBACnD;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,wBAAwB,YAAY,cAAc,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,kBAAkB,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE;iBACpI;aACF;YACD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAAC;QAEH,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,GAAG,IAAI,KAAK,CAAC,8CAA8C,OAAO,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACpH,SAAS;QACX,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,SAAS,GAAG,IAAI,KAAK,CAAC,kDAAkD,OAAO,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACxH,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAiB;YAC3B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE;YACzB,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;SACtC,CAAC;QACF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,18 @@
1
+ export function isPlan(value) {
2
+ if (typeof value !== "object" || value === null)
3
+ return false;
4
+ const v = value;
5
+ return (typeof v["summary"] === "string" &&
6
+ Array.isArray(v["tasks"]) &&
7
+ v["tasks"].every((t) => typeof t === "object" &&
8
+ t !== null &&
9
+ typeof t["id"] === "string" &&
10
+ typeof t["description"] === "string"));
11
+ }
12
+ export function isReviewResult(value) {
13
+ if (typeof value !== "object" || value === null)
14
+ return false;
15
+ const v = value;
16
+ return typeof v["approved"] === "boolean";
17
+ }
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/orchestrator/types.ts"],"names":[],"mappings":"AAuBA,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,QAAQ;QAChC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CACd,CAAC,CAAC,EAAE,EAAE,CACJ,OAAO,CAAC,KAAK,QAAQ;YACrB,CAAC,KAAK,IAAI;YACV,OAAQ,CAA6B,CAAC,IAAI,CAAC,KAAK,QAAQ;YACxD,OAAQ,CAA6B,CAAC,aAAa,CAAC,KAAK,QAAQ,CACpE,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,SAAS,CAAC;AAC5C,CAAC"}