@takimcizgisi/ajan 0.2.5

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 (79) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +366 -0
  3. package/config/models.json +54 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +741 -0
  6. package/dist/cli/tui.d.ts +145 -0
  7. package/dist/cli/tui.js +1003 -0
  8. package/dist/config/configManager.d.ts +18 -0
  9. package/dist/config/configManager.js +204 -0
  10. package/dist/config/types.d.ts +98 -0
  11. package/dist/config/types.js +2 -0
  12. package/dist/core/agent.d.ts +88 -0
  13. package/dist/core/agent.js +222 -0
  14. package/dist/electron/main.d.ts +1 -0
  15. package/dist/electron/main.js +364 -0
  16. package/dist/electron/preload.d.ts +1 -0
  17. package/dist/electron/preload.js +67 -0
  18. package/dist/engine/llama.d.ts +39 -0
  19. package/dist/engine/llama.js +146 -0
  20. package/dist/engine/modelManager.d.ts +18 -0
  21. package/dist/engine/modelManager.js +73 -0
  22. package/dist/index.d.ts +9 -0
  23. package/dist/index.js +9 -0
  24. package/dist/service.d.ts +90 -0
  25. package/dist/service.js +268 -0
  26. package/dist/tools/data.d.ts +3 -0
  27. package/dist/tools/data.js +111 -0
  28. package/dist/tools/edit.d.ts +2 -0
  29. package/dist/tools/edit.js +48 -0
  30. package/dist/tools/file.d.ts +6 -0
  31. package/dist/tools/file.js +274 -0
  32. package/dist/tools/filesys.d.ts +3 -0
  33. package/dist/tools/filesys.js +74 -0
  34. package/dist/tools/memory.d.ts +3 -0
  35. package/dist/tools/memory.js +69 -0
  36. package/dist/tools/patch.d.ts +15 -0
  37. package/dist/tools/patch.js +129 -0
  38. package/dist/tools/registry.d.ts +10 -0
  39. package/dist/tools/registry.js +64 -0
  40. package/dist/tools/taskComplete.d.ts +2 -0
  41. package/dist/tools/taskComplete.js +19 -0
  42. package/dist/tools/terminal.d.ts +2 -0
  43. package/dist/tools/terminal.js +130 -0
  44. package/dist/tools/utils.d.ts +7 -0
  45. package/dist/tools/utils.js +63 -0
  46. package/dist/tools/web.d.ts +8 -0
  47. package/dist/tools/web.js +181 -0
  48. package/dist/utils/clipboard.d.ts +2 -0
  49. package/dist/utils/clipboard.js +40 -0
  50. package/dist/utils/lock.d.ts +12 -0
  51. package/dist/utils/lock.js +68 -0
  52. package/dist/utils/paths.d.ts +13 -0
  53. package/dist/utils/paths.js +58 -0
  54. package/dist/utils/projectContext.d.ts +2 -0
  55. package/dist/utils/projectContext.js +24 -0
  56. package/dist/utils/sacGuard.d.ts +4 -0
  57. package/dist/utils/sacGuard.js +47 -0
  58. package/dist/utils/sessions.d.ts +29 -0
  59. package/dist/utils/sessions.js +64 -0
  60. package/dist/utils/spinner.d.ts +10 -0
  61. package/dist/utils/spinner.js +32 -0
  62. package/package.json +106 -0
  63. package/src/gui/assets/image/AJAN_LOGO.svg +1 -0
  64. package/src/gui/assets/image/ajan.png +0 -0
  65. package/src/gui/assets/image/logo.svg +1 -0
  66. package/src/gui/assets/image/opencode.svg +18 -0
  67. package/src/gui/css/style.css +430 -0
  68. package/src/gui/html/chat.html +34 -0
  69. package/src/gui/html/downloads.html +11 -0
  70. package/src/gui/html/hakkinda.html +26 -0
  71. package/src/gui/html/index.html +82 -0
  72. package/src/gui/html/projects.html +12 -0
  73. package/src/gui/html/settings.html +4 -0
  74. package/src/gui/js/app.js +197 -0
  75. package/src/gui/js/chat.js +434 -0
  76. package/src/gui/js/downloads.js +172 -0
  77. package/src/gui/js/hakkinda.js +16 -0
  78. package/src/gui/js/projects.js +113 -0
  79. package/src/gui/js/settings.js +177 -0
@@ -0,0 +1,18 @@
1
+ import type { AjanConfig, ModelDefinition } from "./types.js";
2
+ export declare const DEFAULT_CONFIG: AjanConfig;
3
+ export declare function buildDefaultSystemPrompt(cwd: string): string;
4
+ export declare function refreshRemoteModels(url?: string): Promise<ModelDefinition[]>;
5
+ export declare function getModelsRegistry(): ModelDefinition[];
6
+ export declare function getModelById(id: string): ModelDefinition | undefined;
7
+ export declare function loadConfig(forceReload?: boolean): AjanConfig;
8
+ export declare function saveConfig(config: AjanConfig): void;
9
+ export declare function resolveModelFilePath(model: ModelDefinition): string;
10
+ export declare function isModelInstalled(model: ModelDefinition): boolean;
11
+ export declare function listInstalledModels(): {
12
+ model: ModelDefinition;
13
+ path: string;
14
+ installed: boolean;
15
+ }[];
16
+ export declare function removeModelFile(model: ModelDefinition): boolean;
17
+ export declare function resolveAbsolutePath(cwd: string, inputPath: string): string;
18
+ export declare function ensureConfigDir(): void;
@@ -0,0 +1,204 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync } from "node:fs";
2
+ import { join, basename, isAbsolute } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { getConfigPath, getModelsDir, getCacheDir, getDataDir } from "../utils/paths.js";
5
+ import { logger } from "../utils/paths.js";
6
+ const __dirname = fileURLToPath(new URL(".", import.meta.url));
7
+ export const DEFAULT_CONFIG = {
8
+ modelId: "gemma-4-e2b-q4-k-m",
9
+ contextSize: "auto",
10
+ gpuLayers: "auto",
11
+ gpu: "auto",
12
+ session: {
13
+ temperature: 0.6,
14
+ topK: 64,
15
+ topP: 0.9,
16
+ maxTokens: 8192
17
+ },
18
+ toolTimeout: 60_000,
19
+ maxToolOutput: 40_000,
20
+ contextTrimThreshold: 0.8,
21
+ maxSteps: 25,
22
+ beepOnComplete: true,
23
+ remoteModelsUrl: "https://raw.githubusercontent.com/takimcizgisi/ajan/main/config/models.json"
24
+ };
25
+ export function buildDefaultSystemPrompt(cwd) {
26
+ return [
27
+ "Sen AJAN AI'sin; TakimCizgisi Yazilim Gelistirme Grubu'nun OTONOM yerel kodlama ajanisin.",
28
+ "",
29
+ "ÇALIŞMA PROTOKOLÜ (her görev için):",
30
+ "1. Görevi parçalara böl; her parçayı ilgili araçla GERÇEKTEN uygula.",
31
+ "2. Her araç sonucunu gözlemle; hata varsa düzeltip tekrar dene. Pes etme, tarif etme — UYGULA.",
32
+ "3. Araç kullanılacak bir adım kaldıysa asla nihai yanıt yazma; aracı çağır.",
33
+ "4. Tüm adımlar bitince ve sonucu doğrulayınca task_complete aracını kısa bir raporla çağır.",
34
+ "5. Sohbet/soru ise doğrudan yanıtla (araç gerektirmiyorsa).",
35
+ "",
36
+ "KURALLAR:",
37
+ "- HER ZAMAN TÜRKÇE YANIT VER. Kullanıcı hangi dilde yazarsa yazsın yanıtların Türkçe olsun. Düşüncelerini de Türkçe yürüt.",
38
+ "- Bir aracı gerçekten çalıştırmadan 'yaptım', 'kaydettim', 'tamamlandı' deme.",
39
+ "- KRİTİK DÜRÜSTLÜK KURALI: Bir araç sonucu 'HATA' ile başlıyorsa işlem GERÇEKLEŞMEMİŞTİR. Bu durumda asla 'başarıyla oluşturuldu/okundu/tamamlandı' deme; hatayı olduğu gibi kullanıcıya bildir veya sorunu çözüp tekrar dene. Hata gördüğün halde başarı raporu vermek en ciddi hatadır.",
40
+ "- Dosya oluşturma/düzenlemede write_file, edit_file veya apply_patch kullan; dosyanın tam içeriğini yanıtta TEKRARLAMA, yalnızca yaptıklarını özetle.",
41
+ "- Bilgi eksikse makul varsayım yapıp devam et; kullanıcıya gereksiz soru sorma.",
42
+ "- CLI çıktısına uygun yaz: girinti yok, fazla boş satır yok, sıkı düzenli metin.",
43
+ "- Bir işlemi tamamladığını iddia etmeden önce doğrula: read_file/list_dir/run_command ile kontrol et; aracın döndürdüğü GERÇEK sonucu bildir.",
44
+ "- Küçük değişikliklerde edit_file; tam yeniden yazmada write_file kullan.",
45
+ `- Çalışma dizini: ${cwd}. Dosya yollarında göreli yol kullan (örn. src/index.ts).`,
46
+ "- Kullanıcı 'masaüstü', 'belgeler', 'indirilenler' derse gerçek Windows klasörleri (Desktop, Documents, Downloads) kastedilir.",
47
+ "- Kullanıcının kalıcı hafızası var: not_al/not_oku araçlarıyla önemli tercihleri saklayıp geri getirebilirsin."
48
+ ].join("\n");
49
+ }
50
+ const bundledModelsPath = join(__dirname, "..", "..", "config", "models.json");
51
+ let cachedConfig;
52
+ function readJson(path) {
53
+ return JSON.parse(readFileSync(path, "utf8"));
54
+ }
55
+ function resolveBundledModels() {
56
+ try {
57
+ const registry = readJson(bundledModelsPath);
58
+ if (Array.isArray(registry.models))
59
+ return registry.models;
60
+ }
61
+ catch (err) {
62
+ logger.debug(`Bundled models okunamadi: ${String(err)}`);
63
+ }
64
+ return [];
65
+ }
66
+ function resolveRemoteModels(url) {
67
+ if (!url)
68
+ return [];
69
+ const cachePath = join(getCacheDir(), "remote-models.json");
70
+ try {
71
+ const cached = readJson(cachePath);
72
+ if (cached && Array.isArray(cached.models)) {
73
+ logger.debug(`Remote models cache'ten okundu (${cached.models.length} model)`);
74
+ return cached.models;
75
+ }
76
+ }
77
+ catch {
78
+ /* cache yoksa umursama */
79
+ }
80
+ return [];
81
+ }
82
+ export async function refreshRemoteModels(url) {
83
+ const source = url ?? DEFAULT_CONFIG.remoteModelsUrl;
84
+ if (!source)
85
+ return [];
86
+ try {
87
+ const controller = new AbortController();
88
+ const timer = setTimeout(() => controller.abort(), 10_000);
89
+ const res = await fetch(source, { signal: controller.signal, redirect: "follow" });
90
+ clearTimeout(timer);
91
+ if (!res.ok) {
92
+ logger.debug(`Remote models fetch basarisiz: HTTP ${res.status}`);
93
+ return resolveRemoteModels(url);
94
+ }
95
+ const registry = (await res.json());
96
+ if (!Array.isArray(registry.models))
97
+ return [];
98
+ const cachePath = join(getCacheDir(), "remote-models.json");
99
+ writeFileSync(cachePath, JSON.stringify({ ...registry, updatedAt: new Date().toISOString() }, null, 2));
100
+ logger.info(`Remote model listesi guncellendi: ${registry.models.length} model`);
101
+ return registry.models;
102
+ }
103
+ catch (err) {
104
+ logger.debug(`Remote models guncelleme basarisiz: ${String(err)}`);
105
+ return resolveRemoteModels(url);
106
+ }
107
+ }
108
+ export function getModelsRegistry() {
109
+ const bundled = resolveBundledModels();
110
+ const remote = resolveRemoteModels(DEFAULT_CONFIG.remoteModelsUrl);
111
+ const merged = new Map();
112
+ for (const m of bundled)
113
+ merged.set(m.id, m);
114
+ for (const m of remote)
115
+ merged.set(m.id, m);
116
+ return [...merged.values()];
117
+ }
118
+ export function getModelById(id) {
119
+ return getModelsRegistry().find((m) => m.id === id);
120
+ }
121
+ export function loadConfig(forceReload = false) {
122
+ if (cachedConfig && !forceReload)
123
+ return cachedConfig;
124
+ const cfgPath = getConfigPath();
125
+ let fileCfg = {};
126
+ if (existsSync(cfgPath)) {
127
+ try {
128
+ fileCfg = readJson(cfgPath);
129
+ }
130
+ catch (err) {
131
+ logger.warn(`config.json parse hatasi (varsayilan kullaniliyor): ${String(err)}`);
132
+ }
133
+ }
134
+ // Donmuş/bayat sistem promptu diske asla yazılmaz ve okunmaz;
135
+ // içinde eski çalışma dizini kalabilir. Her açılışta taze üretilir.
136
+ delete fileCfg.systemPrompt;
137
+ cachedConfig = {
138
+ ...DEFAULT_CONFIG,
139
+ ...fileCfg,
140
+ session: { ...DEFAULT_CONFIG.session, ...fileCfg.session }
141
+ };
142
+ delete cachedConfig.cwd;
143
+ cachedConfig.systemPrompt = buildDefaultSystemPrompt(process.cwd());
144
+ return cachedConfig;
145
+ }
146
+ export function saveConfig(config) {
147
+ const cfgPath = getConfigPath();
148
+ const safe = JSON.parse(JSON.stringify(config));
149
+ delete safe.systemPrompt;
150
+ if (safe.session) {
151
+ for (const k of Object.keys(safe.session)) {
152
+ if (safe.session[k] == null)
153
+ delete safe.session[k];
154
+ }
155
+ }
156
+ writeFileSync(cfgPath, JSON.stringify(safe, null, 2));
157
+ cachedConfig = safe;
158
+ }
159
+ export function resolveModelFilePath(model) {
160
+ const modelsDir = getModelsDir();
161
+ const fileName = model.fileName ?? basename(model.uri);
162
+ return join(modelsDir, fileName);
163
+ }
164
+ export function isModelInstalled(model) {
165
+ return existsSync(resolveModelFilePath(model));
166
+ }
167
+ export function listInstalledModels() {
168
+ const modelsDir = getModelsDir();
169
+ if (!existsSync(modelsDir))
170
+ return [];
171
+ const entries = getModelsRegistry();
172
+ const result = [];
173
+ for (const model of entries) {
174
+ result.push({
175
+ model,
176
+ path: resolveModelFilePath(model),
177
+ installed: isModelInstalled(model)
178
+ });
179
+ }
180
+ return result;
181
+ }
182
+ export function removeModelFile(model) {
183
+ const file = resolveModelFilePath(model);
184
+ if (!existsSync(file))
185
+ return false;
186
+ try {
187
+ rmSync(file, { force: true });
188
+ return true;
189
+ }
190
+ catch (err) {
191
+ logger.error(`Model silinemedi: ${String(err)}`);
192
+ return false;
193
+ }
194
+ }
195
+ export function resolveAbsolutePath(cwd, inputPath) {
196
+ if (isAbsolute(inputPath))
197
+ return inputPath;
198
+ return join(cwd, inputPath);
199
+ }
200
+ export function ensureConfigDir() {
201
+ mkdirSync(getDataDir(), { recursive: true });
202
+ mkdirSync(getModelsDir(), { recursive: true });
203
+ }
204
+ //# sourceMappingURL=configManager.js.map
@@ -0,0 +1,98 @@
1
+ export type ChatWrapperName = "auto" | "gemma4" | "gemma" | "qwen3" | "llama3" | "mistral" | "functionary" | "chatml";
2
+ export type GpuBackend = "auto" | "metal" | "cuda" | "vulkan" | "webgpu" | "cuda-llama" | "vulkan-llama";
3
+ export type ModelDefinition = {
4
+ /** Benzersiz model kimliği */
5
+ id: string;
6
+ /** Görünen ad */
7
+ name: string;
8
+ /** Açıklama */
9
+ description?: string;
10
+ /** İndirme URI'si (hf:..., https://...) */
11
+ uri: string;
12
+ /** Yerel dosya adı (yoksa uri'den türetilir) */
13
+ fileName?: string;
14
+ /** Bağlam penceresi boyutu */
15
+ contextSize?: number | "auto";
16
+ /** GPU katman sayısı */
17
+ gpuLayers?: number | "auto" | "max";
18
+ /** Chat sarmalayıcı adı */
19
+ chatWrapper?: ChatWrapperName;
20
+ /** Muhakeme (reasoning) modu */
21
+ reasoning?: boolean;
22
+ /** Multimodal (görüntü) desteği */
23
+ multimodal?: boolean;
24
+ /** Dosya boyutu (bayt) — indirme kontrolü için */
25
+ size?: number;
26
+ /** Dosya sha256 sağlaması */
27
+ sha256?: string;
28
+ /** Özel yapılandırma (modele özgü parametreler) */
29
+ options?: Record<string, unknown>;
30
+ };
31
+ export type RemoteModelsRegistry = {
32
+ version: number;
33
+ source?: string;
34
+ updatedAt?: string;
35
+ models: ModelDefinition[];
36
+ };
37
+ export type SessionConfig = {
38
+ temperature?: number;
39
+ topP?: number;
40
+ topK?: number;
41
+ maxTokens?: number;
42
+ seed?: number;
43
+ repeatPenalty?: number;
44
+ dryRepeatPenalty?: boolean;
45
+ };
46
+ export type AjanConfig = {
47
+ /** Seçili model kimliği */
48
+ modelId: string;
49
+ /** Sistem promptu */
50
+ systemPrompt?: string;
51
+ /** Bağlam boyutu */
52
+ contextSize?: number | "auto";
53
+ /** GPU katman sayısı */
54
+ gpuLayers?: number | "auto" | "max";
55
+ /** GPU arka ucu */
56
+ gpu?: GpuBackend;
57
+ /** Oturum parametreleri */
58
+ session?: SessionConfig;
59
+ /** Araç çalıştırma zaman aşımı (ms) */
60
+ toolTimeout?: number;
61
+ /** Araçlarda döndürülen maks. çıktı uzunluğu (karakter) */
62
+ maxToolOutput?: number;
63
+ /** Context dolunca eski turların kırpılacağı eşik (0-1, contextSize oranı) */
64
+ contextTrimThreshold?: number;
65
+ /** Otonom görev başına maksimum araç-adımı (varsayılan 25) */
66
+ maxSteps?: number;
67
+ /** Çalıştırılması engellenecek komut desenleri (regex) */
68
+ blockedCommands?: string[];
69
+ /** Görev tamamlanınca bip çal */
70
+ beepOnComplete?: boolean;
71
+ /** Remote models.json kaynağı (GitHub vb.) */
72
+ remoteModelsUrl?: string;
73
+ /** Kayıtlı projeler (klasör yolları) */
74
+ projects?: string[];
75
+ /** Aktif proje yolu */
76
+ activeProject?: string;
77
+ };
78
+ export type ToolHandler<Params = any> = (params: Params, ctx: ToolExecutionContext) => Promise<ToolExecutionResult> | ToolExecutionResult;
79
+ export type ToolExecutionSignal = {
80
+ /** task_complete aracı çağrıldığında true olur */
81
+ taskCompleted?: boolean;
82
+ };
83
+ export type ToolExecutionContext = {
84
+ cwd: string;
85
+ config: AjanConfig;
86
+ signals?: ToolExecutionSignal;
87
+ };
88
+ export type ToolExecutionResult = {
89
+ ok: boolean;
90
+ output: string;
91
+ error?: string;
92
+ };
93
+ export type AgentTool = {
94
+ name: string;
95
+ description: string;
96
+ parameters: Record<string, unknown>;
97
+ handler: ToolHandler;
98
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,88 @@
1
+ import type { ChatHistoryItem } from "node-llama-cpp";
2
+ import type { AgentTool, AjanConfig, ModelDefinition } from "../config/types.js";
3
+ export type AgentEvents = {
4
+ /** Model token token üretirken (akış) */
5
+ onTextChunk?: (text: string) => void;
6
+ /** Model düşünce/akıl yürütme segmentleri üretirken (akış) */
7
+ onThinkingChunk?: (text: string) => void;
8
+ /** Bir araç çağrılmadan önce */
9
+ onToolCall?: (name: string, params: unknown) => void;
10
+ /** Bir araç sonucu döndüğünde */
11
+ onToolResult?: (name: string, ok: boolean, output: string) => void;
12
+ /** Nihai yanıt tamamlandığında */
13
+ onComplete?: (response: string) => void;
14
+ /** Otonom bir adım başladığında */
15
+ onStepStart?: (step: number, maxSteps: number) => void;
16
+ /** Otonom adım bittiğinde (o turdaki araç çağrısı sayısıyla) */
17
+ onStepEnd?: (step: number, maxSteps: number, toolCalls: number) => void;
18
+ /** Context sıkıştırıldığında (eski turlar kırpıldı) */
19
+ onContextTrimmed?: (removedItems: number) => void;
20
+ /** Hata oluştuğunda */
21
+ onError?: (error: Error) => void;
22
+ };
23
+ export type AgentOptions = {
24
+ model?: ModelDefinition;
25
+ config?: AjanConfig;
26
+ modelPath?: string;
27
+ systemPrompt?: string;
28
+ tools?: AgentTool[];
29
+ events?: AgentEvents;
30
+ onLoadProgress?: (pct: number) => void;
31
+ onLoadComplete?: () => void;
32
+ signal?: AbortSignal;
33
+ temperature?: number;
34
+ };
35
+ export declare class AjanAgent {
36
+ private engine;
37
+ private session;
38
+ private readonly model;
39
+ private readonly config;
40
+ private readonly modelPath;
41
+ private systemPrompt;
42
+ private readonly tools;
43
+ private readonly events;
44
+ private readonly onLoadProgress?;
45
+ private readonly onLoadComplete?;
46
+ private readonly signal?;
47
+ private readonly temperature?;
48
+ private pendingHistory;
49
+ constructor(options: AgentOptions);
50
+ get engineInfo(): {
51
+ modelPath: string;
52
+ modelId: string;
53
+ };
54
+ /** TUI göstergesi için bağlam doluluk bilgisi */
55
+ getContextStats(): {
56
+ usedTokens: number;
57
+ contextSize: number;
58
+ } | null;
59
+ private ensureSession;
60
+ prompt(userPrompt: string, options?: {
61
+ signal?: AbortSignal;
62
+ }): Promise<string>;
63
+ /**
64
+ * Otonom görev yürütme: model araç çağrılarıyla görevi adım adım tamamlar.
65
+ * Döngü şuralarda biter: task_complete aracı, araçsız bir tur (nihai özet),
66
+ * adım bütçesi dolması veya iptal sinyali.
67
+ */
68
+ promptTask(userPrompt: string, options?: {
69
+ signal?: AbortSignal;
70
+ }): Promise<{
71
+ response: string;
72
+ steps: number;
73
+ maxSteps: number;
74
+ completed: boolean;
75
+ budgetExhausted: boolean;
76
+ }>;
77
+ /** Context eşiğini aşınca eski konuşma turlarını baştan kırpar (system promptu korunur) */
78
+ private trimContextIfNeeded;
79
+ /** Oturumdaki geçmişi temizler (yeni konuşma) */
80
+ resetConversation(): void;
81
+ /** Aktif oturumun geçmişini döndürür (oturum yoksa bekleyen geçmişi) */
82
+ getChatHistory(): ChatHistoryItem[];
83
+ /** Oturum geçmişini yükler; oturum henüz kurulmadıysa ilk kurulumda uygulanır */
84
+ setChatHistory(history: ChatHistoryItem[]): void;
85
+ /** Sistem promptunu değiştirir; aktif oturum varsa geçmiş temizlenip yeni sistem promptuyla yeniden kurulur */
86
+ setSystemPrompt(prompt: string): Promise<void>;
87
+ dispose(): Promise<void>;
88
+ }
@@ -0,0 +1,222 @@
1
+ import { LlamaEngine } from "../engine/llama.js";
2
+ import { buildSessionFunctions, createCoreTools } from "../tools/registry.js";
3
+ import { getModelById, loadConfig, resolveModelFilePath, buildDefaultSystemPrompt } from "../config/configManager.js";
4
+ import { loadProjectContext } from "../utils/projectContext.js";
5
+ import { logger } from "../utils/paths.js";
6
+ function historyItemText(item) {
7
+ if (item.type === "system" || item.type === "user") {
8
+ return typeof item.text === "string" ? item.text : JSON.stringify(item.text);
9
+ }
10
+ return item.response.filter((p) => typeof p === "string").join("\n");
11
+ }
12
+ export class AjanAgent {
13
+ engine;
14
+ session;
15
+ model;
16
+ config;
17
+ modelPath;
18
+ systemPrompt;
19
+ tools;
20
+ events;
21
+ onLoadProgress;
22
+ onLoadComplete;
23
+ signal;
24
+ temperature;
25
+ pendingHistory;
26
+ constructor(options) {
27
+ const config = options.config ?? loadConfig();
28
+ this.config = config;
29
+ this.events = options.events ?? {};
30
+ this.onLoadProgress = options.onLoadProgress;
31
+ this.onLoadComplete = options.onLoadComplete;
32
+ this.signal = options.signal;
33
+ this.temperature = options.temperature;
34
+ if (options.model) {
35
+ this.model = options.model;
36
+ }
37
+ else {
38
+ const id = config.modelId;
39
+ const found = getModelById(id);
40
+ if (!found) {
41
+ throw new Error(`Model bulunamadı: ${id}. 'ajan model list' ile uygun modelleri görün.`);
42
+ }
43
+ this.model = found;
44
+ }
45
+ this.modelPath = options.modelPath ?? resolveModelFilePath(this.model);
46
+ // Sistem promptu her açılışta güncel çalışma diziniyle taze üretilir;
47
+ // config.systemPrompt yok sayılır (bayat cwd içermesin diye).
48
+ this.systemPrompt =
49
+ (options.systemPrompt ?? buildDefaultSystemPrompt(process.cwd())) +
50
+ loadProjectContext(process.cwd());
51
+ this.tools = options.tools ?? createCoreTools({ cwd: process.cwd(), config });
52
+ }
53
+ get engineInfo() {
54
+ return { modelPath: this.modelPath, modelId: this.model.id };
55
+ }
56
+ /** TUI göstergesi için bağlam doluluk bilgisi */
57
+ getContextStats() {
58
+ if (!this.engine)
59
+ return null;
60
+ return {
61
+ usedTokens: this.engine.getUsedContextTokens(),
62
+ contextSize: this.engine.contextSize
63
+ };
64
+ }
65
+ async ensureSession(signal) {
66
+ if (this.session)
67
+ return this.session;
68
+ this.engine = new LlamaEngine({
69
+ modelPath: this.modelPath,
70
+ config: this.config,
71
+ chatWrapperName: this.model.chatWrapper,
72
+ onProgress: (pct) => this.onLoadProgress?.(pct),
73
+ signal: signal ?? this.signal
74
+ });
75
+ await this.engine.createContext();
76
+ this.onLoadComplete?.();
77
+ this.session = this.engine.createSession(this.systemPrompt);
78
+ if (this.pendingHistory) {
79
+ this.session.setChatHistory(this.pendingHistory);
80
+ this.pendingHistory = undefined;
81
+ }
82
+ return this.session;
83
+ }
84
+ async prompt(userPrompt, options) {
85
+ const result = await this.promptTask(userPrompt, options);
86
+ return result.response;
87
+ }
88
+ /**
89
+ * Otonom görev yürütme: model araç çağrılarıyla görevi adım adım tamamlar.
90
+ * Döngü şuralarda biter: task_complete aracı, araçsız bir tur (nihai özet),
91
+ * adım bütçesi dolması veya iptal sinyali.
92
+ */
93
+ async promptTask(userPrompt, options) {
94
+ const session = await this.ensureSession(options?.signal ?? this.signal);
95
+ const signals = {};
96
+ const ctx = {
97
+ cwd: process.cwd(),
98
+ config: this.config,
99
+ signals
100
+ };
101
+ let toolCallsThisRound = 0;
102
+ const toolEvents = {
103
+ onToolCall: (name, params) => {
104
+ toolCallsThisRound++;
105
+ this.events.onToolCall?.(name, params);
106
+ },
107
+ onToolResult: (name, ok, output) => this.events.onToolResult?.(name, ok, output)
108
+ };
109
+ const functions = buildSessionFunctions(this.tools, ctx, toolEvents);
110
+ const sessionOpts = this.config.session ?? {};
111
+ const maxSteps = Math.max(1, this.config.maxSteps ?? 25);
112
+ const activeSignal = options?.signal ?? this.signal;
113
+ const runRound = (promptText) => {
114
+ toolCallsThisRound = 0;
115
+ this.trimContextIfNeeded();
116
+ logger.debug(`Prompt gonderiliyor (${promptText.length} karakter)`);
117
+ return session.prompt(promptText, {
118
+ functions,
119
+ documentFunctionParams: true,
120
+ temperature: this.temperature ?? sessionOpts.temperature,
121
+ topP: sessionOpts.topP,
122
+ topK: sessionOpts.topK,
123
+ maxTokens: sessionOpts.maxTokens,
124
+ seed: sessionOpts.seed,
125
+ onTextChunk: (text) => this.events.onTextChunk?.(text),
126
+ onResponseChunk: (chunk) => {
127
+ if (chunk.type === "segment" && chunk.segmentType === "thought") {
128
+ this.events.onThinkingChunk?.(chunk.text);
129
+ }
130
+ },
131
+ signal: activeSignal
132
+ });
133
+ };
134
+ const continuePrompt = "Devam et. Görevin kalan adımlarını araçlarla tamamla; her şey bitip doğrulanınca task_complete aracını raporla çağır.";
135
+ let step = 0;
136
+ let response = "";
137
+ while (step < maxSteps) {
138
+ if (activeSignal?.aborted)
139
+ break;
140
+ step++;
141
+ this.events.onStepStart?.(step, maxSteps);
142
+ try {
143
+ response = await runRound(step === 1 ? userPrompt : continuePrompt);
144
+ }
145
+ finally {
146
+ this.events.onStepEnd?.(step, maxSteps, toolCallsThisRound);
147
+ }
148
+ if (signals.taskCompleted)
149
+ break;
150
+ if (toolCallsThisRound === 0)
151
+ break;
152
+ }
153
+ const budgetExhausted = !signals.taskCompleted && step >= maxSteps;
154
+ this.events.onComplete?.(response);
155
+ return { response, steps: step, maxSteps, completed: signals.taskCompleted === true, budgetExhausted };
156
+ }
157
+ /** Context eşiğini aşınca eski konuşma turlarını baştan kırpar (system promptu korunur) */
158
+ trimContextIfNeeded() {
159
+ if (!this.engine || !this.session)
160
+ return;
161
+ const contextSize = this.engine.contextSize;
162
+ const used = this.engine.getUsedContextTokens();
163
+ if (contextSize <= 0 || used <= 0)
164
+ return;
165
+ const threshold = this.config.contextTrimThreshold ?? 0.8;
166
+ if (used <= contextSize * threshold)
167
+ return;
168
+ const history = this.session.getChatHistory();
169
+ if (history.length <= 1)
170
+ return;
171
+ const budget = Math.floor(contextSize * threshold * 0.6);
172
+ const kept = [];
173
+ let total = 0;
174
+ for (let i = history.length - 1; i >= 0; i--) {
175
+ const item = history[i];
176
+ const tokens = Math.ceil(historyItemText(item).length / 3.2);
177
+ if (kept.length > 0 && total + tokens > budget)
178
+ break;
179
+ kept.unshift(item);
180
+ total += tokens;
181
+ }
182
+ const removed = history.length - kept.length;
183
+ if (removed > 0) {
184
+ this.session.setChatHistory(kept);
185
+ logger.info(`Context sıkıştırıldı: ${removed} mesaj kırpıldı (${used} token)`);
186
+ this.events.onContextTrimmed?.(removed);
187
+ }
188
+ }
189
+ /** Oturumdaki geçmişi temizler (yeni konuşma) */
190
+ resetConversation() {
191
+ this.session?.resetChatHistory();
192
+ }
193
+ /** Aktif oturumun geçmişini döndürür (oturum yoksa bekleyen geçmişi) */
194
+ getChatHistory() {
195
+ if (this.session)
196
+ return this.session.getChatHistory();
197
+ return this.pendingHistory ?? [];
198
+ }
199
+ /** Oturum geçmişini yükler; oturum henüz kurulmadıysa ilk kurulumda uygulanır */
200
+ setChatHistory(history) {
201
+ if (this.session) {
202
+ this.session.setChatHistory(history);
203
+ this.pendingHistory = undefined;
204
+ }
205
+ else {
206
+ this.pendingHistory = history;
207
+ }
208
+ }
209
+ /** Sistem promptunu değiştirir; aktif oturum varsa geçmiş temizlenip yeni sistem promptuyla yeniden kurulur */
210
+ async setSystemPrompt(prompt) {
211
+ this.systemPrompt = prompt;
212
+ if (this.engine && this.session) {
213
+ this.session = await this.engine.resetSession(prompt);
214
+ }
215
+ }
216
+ async dispose() {
217
+ await this.engine?.dispose();
218
+ this.engine = undefined;
219
+ this.session = undefined;
220
+ }
221
+ }
222
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ export {};