@aigencydev/cli 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/checkpoints.js +147 -0
- package/dist/agent/chunker.js +1 -19
- package/dist/agent/history.js +0 -9
- package/dist/agent/instructions.js +94 -0
- package/dist/agent/loop.js +3 -34
- package/dist/agent/memory.js +197 -103
- package/dist/agent/session-store.js +160 -0
- package/dist/agent/task-list.js +102 -0
- package/dist/api-client/auth.js +0 -10
- package/dist/api-client/base.js +0 -16
- package/dist/api-client/stream.js +0 -25
- package/dist/api-client/updates.js +2 -9
- package/dist/api-client/usage.js +0 -4
- package/dist/auth/oauth.js +0 -31
- package/dist/auth/pkce.js +1 -9
- package/dist/auth/session.js +0 -16
- package/dist/auth/storage.js +1 -36
- package/dist/cli.js +8 -13
- package/dist/commands/chat.js +14 -19
- package/dist/commands/help.js +0 -4
- package/dist/commands/init.js +327 -30
- package/dist/commands/login.js +0 -30
- package/dist/commands/logout.js +0 -5
- package/dist/commands/update.js +0 -9
- package/dist/commands/usage.js +0 -10
- package/dist/config/bootstrap.js +52 -0
- package/dist/config/defaults.js +4 -15
- package/dist/config/hooks.js +1 -23
- package/dist/config/paths.js +95 -38
- package/dist/config/settings.js +0 -21
- package/dist/index.js +0 -11
- package/dist/security/command-filter.js +0 -35
- package/dist/security/sandbox.js +1 -39
- package/dist/security/sanitize.js +0 -21
- package/dist/tools/bash.js +186 -32
- package/dist/tools/diff.js +0 -7
- package/dist/tools/edit-file.js +4 -12
- package/dist/tools/list-files.js +0 -5
- package/dist/tools/memory-tools.js +126 -0
- package/dist/tools/read-file.js +1 -7
- package/dist/tools/registry.js +7 -9
- package/dist/tools/search-files.js +2 -11
- package/dist/tools/task-tools.js +74 -0
- package/dist/tools/types.js +0 -4
- package/dist/tools/write-file.js +5 -12
- package/dist/types/index.js +0 -7
- package/dist/ui/App.js +160 -36
- package/dist/ui/InputBar.js +8 -9
- package/dist/ui/MessageList.js +25 -33
- package/dist/ui/ModeIndicator.js +6 -18
- package/dist/ui/PermissionPrompt.js +28 -29
- package/dist/ui/StatusBar.js +17 -17
- package/dist/ui/theme.js +112 -38
- package/dist/utils/abort.js +0 -12
- package/dist/utils/errors.js +0 -7
- package/dist/utils/formatting.js +0 -8
- package/dist/utils/logger.js +107 -17
- package/dist/version.js +1 -8
- package/package.json +1 -1
- package/dist/agent/chunker.js.map +0 -1
- package/dist/agent/history.js.map +0 -1
- package/dist/agent/loop.js.map +0 -1
- package/dist/agent/memory.js.map +0 -1
- package/dist/api-client/auth.js.map +0 -1
- package/dist/api-client/base.js.map +0 -1
- package/dist/api-client/stream.js.map +0 -1
- package/dist/api-client/updates.js.map +0 -1
- package/dist/api-client/usage.js.map +0 -1
- package/dist/auth/oauth.js.map +0 -1
- package/dist/auth/pkce.js.map +0 -1
- package/dist/auth/session.js.map +0 -1
- package/dist/auth/storage.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/chat.js.map +0 -1
- package/dist/commands/help.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/logout.js.map +0 -1
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/usage.js.map +0 -1
- package/dist/config/defaults.js.map +0 -1
- package/dist/config/hooks.js.map +0 -1
- package/dist/config/paths.js.map +0 -1
- package/dist/config/settings.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/security/command-filter.js.map +0 -1
- package/dist/security/sandbox.js.map +0 -1
- package/dist/security/sanitize.js.map +0 -1
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/diff.js.map +0 -1
- package/dist/tools/edit-file.js.map +0 -1
- package/dist/tools/list-files.js.map +0 -1
- package/dist/tools/read-file.js.map +0 -1
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/search-files.js.map +0 -1
- package/dist/tools/types.js.map +0 -1
- package/dist/tools/write-file.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/ui/App.js.map +0 -1
- package/dist/ui/InputBar.js.map +0 -1
- package/dist/ui/MessageList.js.map +0 -1
- package/dist/ui/ModeIndicator.js.map +0 -1
- package/dist/ui/PermissionPrompt.js.map +0 -1
- package/dist/ui/StatusBar.js.map +0 -1
- package/dist/ui/theme.js.map +0 -1
- package/dist/utils/abort.js.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/formatting.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/version.js.map +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { getCheckpointDir } from "../config/paths.js";
|
|
5
|
+
import { log } from "../utils/logger.js";
|
|
6
|
+
const MAX_CHECKPOINT_BYTES = 2 * 1024 * 1024;
|
|
7
|
+
export async function captureCheckpoint(sessionId, absPath, operation, projectRoot) {
|
|
8
|
+
try {
|
|
9
|
+
let beforeContent = "";
|
|
10
|
+
let existed = false;
|
|
11
|
+
let size = 0;
|
|
12
|
+
try {
|
|
13
|
+
const stat = await fs.stat(absPath);
|
|
14
|
+
if (stat.isFile()) {
|
|
15
|
+
size = stat.size;
|
|
16
|
+
if (size > MAX_CHECKPOINT_BYTES) {
|
|
17
|
+
log.debug(`checkpoint: ${absPath} çok büyük (${size}b), snapshot atlandı`);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
beforeContent = await fs.readFile(absPath, "utf8");
|
|
21
|
+
existed = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
}
|
|
26
|
+
const relPath = path.relative(projectRoot, absPath) || path.basename(absPath);
|
|
27
|
+
const checkpoint = {
|
|
28
|
+
relPath,
|
|
29
|
+
absPath,
|
|
30
|
+
ts: new Date().toISOString(),
|
|
31
|
+
operation,
|
|
32
|
+
beforeContent,
|
|
33
|
+
existed,
|
|
34
|
+
};
|
|
35
|
+
const pathHash = crypto
|
|
36
|
+
.createHash("sha256")
|
|
37
|
+
.update(absPath)
|
|
38
|
+
.digest("hex")
|
|
39
|
+
.slice(0, 12);
|
|
40
|
+
const ts = Date.now();
|
|
41
|
+
const snapshotFile = path.join(getCheckpointDir(sessionId, projectRoot), `${pathHash}-${ts}.snapshot`);
|
|
42
|
+
await fs.mkdir(path.dirname(snapshotFile), { recursive: true });
|
|
43
|
+
await fs.writeFile(snapshotFile, JSON.stringify(checkpoint), "utf8");
|
|
44
|
+
return {
|
|
45
|
+
relPath,
|
|
46
|
+
ts: checkpoint.ts,
|
|
47
|
+
operation,
|
|
48
|
+
existed,
|
|
49
|
+
snapshotFile,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
log.debug(`checkpoint hatası: ${err.message}`);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function findLatestCheckpoint(sessionId, projectRoot) {
|
|
58
|
+
const dir = getCheckpointDir(sessionId, projectRoot);
|
|
59
|
+
let entries;
|
|
60
|
+
try {
|
|
61
|
+
entries = await fs.readdir(dir);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const files = entries.filter((f) => f.endsWith(".snapshot"));
|
|
67
|
+
if (files.length === 0)
|
|
68
|
+
return null;
|
|
69
|
+
let latest = null;
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
const match = /-(\d+)\.snapshot$/.exec(file);
|
|
72
|
+
if (!match)
|
|
73
|
+
continue;
|
|
74
|
+
const ts = parseInt(match[1], 10);
|
|
75
|
+
if (!latest || ts > latest.ts) {
|
|
76
|
+
latest = { file, ts };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!latest)
|
|
80
|
+
return null;
|
|
81
|
+
try {
|
|
82
|
+
const raw = await fs.readFile(path.join(dir, latest.file), "utf8");
|
|
83
|
+
return JSON.parse(raw);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export async function restoreCheckpoint(checkpoint) {
|
|
90
|
+
try {
|
|
91
|
+
if (checkpoint.existed) {
|
|
92
|
+
await fs.mkdir(path.dirname(checkpoint.absPath), { recursive: true });
|
|
93
|
+
await fs.writeFile(checkpoint.absPath, checkpoint.beforeContent, "utf8");
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
message: `${checkpoint.relPath} önceki haline geri döndürüldü`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
try {
|
|
101
|
+
await fs.unlink(checkpoint.absPath);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
message: `${checkpoint.relPath} silindi (yeni oluşturulmuştu)`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: `Geri alma hatası: ${err.message}`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export async function listCheckpoints(sessionId, projectRoot) {
|
|
119
|
+
const dir = getCheckpointDir(sessionId, projectRoot);
|
|
120
|
+
let entries;
|
|
121
|
+
try {
|
|
122
|
+
entries = await fs.readdir(dir);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
const metadata = [];
|
|
128
|
+
for (const file of entries) {
|
|
129
|
+
if (!file.endsWith(".snapshot"))
|
|
130
|
+
continue;
|
|
131
|
+
try {
|
|
132
|
+
const raw = await fs.readFile(path.join(dir, file), "utf8");
|
|
133
|
+
const cp = JSON.parse(raw);
|
|
134
|
+
metadata.push({
|
|
135
|
+
relPath: cp.relPath,
|
|
136
|
+
ts: cp.ts,
|
|
137
|
+
operation: cp.operation,
|
|
138
|
+
existed: cp.existed,
|
|
139
|
+
snapshotFile: path.join(dir, file),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return metadata.sort((a, b) => (a.ts > b.ts ? -1 : 1));
|
|
147
|
+
}
|
package/dist/agent/chunker.js
CHANGED
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AIGENCY CLI — kullanıcı prompt'u ve history için basit chunker.
|
|
3
|
-
*
|
|
4
|
-
* Faz 4 scope'u: basit token budget kontrolü + history trim.
|
|
5
|
-
* Faz 5'te gelişmiş chunked streaming (proje dosyalarını parçalara bölüp
|
|
6
|
-
* ardışık stream istekleriyle göndermek) eklenecek.
|
|
7
|
-
*
|
|
8
|
-
* Şu an:
|
|
9
|
-
* - Toplam token (history + prompt) tahmin et
|
|
10
|
-
* - Budget üstü ise en eski mesajları kırp
|
|
11
|
-
* - Kalan budget prompt + 20% response için yeterli değilse uyar
|
|
12
|
-
*/
|
|
13
|
-
/** Basit token tahmini — 1 token ≈ 4 karakter */
|
|
14
1
|
export function approxTokens(text) {
|
|
15
2
|
return Math.ceil(text.length / 4);
|
|
16
3
|
}
|
|
17
|
-
const DEFAULT_MAX_CONTEXT = 120_000;
|
|
4
|
+
const DEFAULT_MAX_CONTEXT = 120_000;
|
|
18
5
|
const DEFAULT_RESERVE_RESPONSE = 8_000;
|
|
19
6
|
const DEFAULT_RESERVE_SYSTEM = 4_000;
|
|
20
|
-
/**
|
|
21
|
-
* History'yi budget'a sığdırır — eskiden başlayarak kırpar.
|
|
22
|
-
*/
|
|
23
7
|
export function prepareChunk(prompt, history, options = {}) {
|
|
24
8
|
const maxContext = options.maxContextTokens || DEFAULT_MAX_CONTEXT;
|
|
25
9
|
const reserveResponse = options.reserveForResponse || DEFAULT_RESERVE_RESPONSE;
|
|
@@ -34,7 +18,6 @@ export function prepareChunk(prompt, history, options = {}) {
|
|
|
34
18
|
warning: `Prompt tek başına ${promptTokens} token — context limiti (${available}) aşılıyor. Komutunuzu kısaltın.`,
|
|
35
19
|
};
|
|
36
20
|
}
|
|
37
|
-
// Sondan başa doğru ekle (son mesajlar daha önemli)
|
|
38
21
|
const reversed = [...history].reverse();
|
|
39
22
|
const selected = [];
|
|
40
23
|
let total = promptTokens;
|
|
@@ -56,4 +39,3 @@ export function prepareChunk(prompt, history, options = {}) {
|
|
|
56
39
|
warning,
|
|
57
40
|
};
|
|
58
41
|
}
|
|
59
|
-
//# sourceMappingURL=chunker.js.map
|
package/dist/agent/history.js
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AIGENCY CLI — sohbet geçmişi ring buffer.
|
|
3
|
-
*
|
|
4
|
-
* Kullanıcı + asistan mesajlarını tutar. /clear komutu sıfırlar.
|
|
5
|
-
* Chunked streaming sırasında bu history stream endpoint'ine gönderilir.
|
|
6
|
-
*/
|
|
7
1
|
const MAX_HISTORY = 40;
|
|
8
2
|
export class ChatHistory {
|
|
9
3
|
messages = [];
|
|
10
4
|
push(msg) {
|
|
11
5
|
const approxTokens = msg.approxTokens ?? Math.ceil((msg.content || "").length / 4);
|
|
12
6
|
this.messages.push({ ...msg, approxTokens });
|
|
13
|
-
// Ring buffer — en eski mesajları kırp
|
|
14
7
|
while (this.messages.length > MAX_HISTORY) {
|
|
15
8
|
this.messages.shift();
|
|
16
9
|
}
|
|
@@ -24,7 +17,6 @@ export class ChatHistory {
|
|
|
24
17
|
length() {
|
|
25
18
|
return this.messages.length;
|
|
26
19
|
}
|
|
27
|
-
/** Son N mesajı döner — stream endpoint'ine bunu gönderiyoruz */
|
|
28
20
|
tail(n) {
|
|
29
21
|
return this.messages.slice(-n);
|
|
30
22
|
}
|
|
@@ -32,4 +24,3 @@ export class ChatHistory {
|
|
|
32
24
|
return this.messages.reduce((acc, m) => acc + (m.approxTokens || 0), 0);
|
|
33
25
|
}
|
|
34
26
|
}
|
|
35
|
-
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getUserInstructionsFile, getProjectInstructionsFile, getProjectAigencyDir, } from "../config/paths.js";
|
|
4
|
+
import { log } from "../utils/logger.js";
|
|
5
|
+
const MAX_PER_FILE = 32 * 1024;
|
|
6
|
+
const MAX_TOTAL = 96 * 1024;
|
|
7
|
+
const MAX_WALK_DEPTH = 10;
|
|
8
|
+
export async function loadInstructions(projectRoot, cwd = process.cwd()) {
|
|
9
|
+
const sources = [];
|
|
10
|
+
let totalBytes = 0;
|
|
11
|
+
const tryAdd = async (label, filePath) => {
|
|
12
|
+
if (totalBytes >= MAX_TOTAL)
|
|
13
|
+
return;
|
|
14
|
+
const content = await readFileIfExists(filePath);
|
|
15
|
+
if (!content)
|
|
16
|
+
return;
|
|
17
|
+
const trimmed = content.slice(0, MAX_PER_FILE);
|
|
18
|
+
const remaining = MAX_TOTAL - totalBytes;
|
|
19
|
+
const finalContent = trimmed.slice(0, remaining);
|
|
20
|
+
sources.push({
|
|
21
|
+
label,
|
|
22
|
+
path: filePath,
|
|
23
|
+
content: finalContent,
|
|
24
|
+
sizeBytes: finalContent.length,
|
|
25
|
+
});
|
|
26
|
+
totalBytes += finalContent.length;
|
|
27
|
+
};
|
|
28
|
+
await tryAdd("global", getUserInstructionsFile());
|
|
29
|
+
await tryAdd("project", getProjectInstructionsFile(projectRoot));
|
|
30
|
+
await tryAdd("local", path.join(getProjectAigencyDir(projectRoot), "AIGENCY.local.md"));
|
|
31
|
+
const rootInstructions = path.join(projectRoot, "AIGENCY.md");
|
|
32
|
+
if (rootInstructions !== getProjectInstructionsFile(projectRoot)) {
|
|
33
|
+
await tryAdd("project-root", rootInstructions);
|
|
34
|
+
}
|
|
35
|
+
if (isInside(cwd, projectRoot) && cwd !== projectRoot) {
|
|
36
|
+
let current = path.resolve(cwd);
|
|
37
|
+
const root = path.resolve(projectRoot);
|
|
38
|
+
let depth = 0;
|
|
39
|
+
while (current !== root && depth < MAX_WALK_DEPTH) {
|
|
40
|
+
const local = path.join(current, ".aigency", "AIGENCY.md");
|
|
41
|
+
const bare = path.join(current, "AIGENCY.md");
|
|
42
|
+
await tryAdd(`cwd:${path.relative(root, current)}`, local);
|
|
43
|
+
await tryAdd(`cwd-root:${path.relative(root, current)}`, bare);
|
|
44
|
+
const parent = path.dirname(current);
|
|
45
|
+
if (parent === current)
|
|
46
|
+
break;
|
|
47
|
+
current = parent;
|
|
48
|
+
depth++;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
sources,
|
|
53
|
+
totalBytes,
|
|
54
|
+
combinedText: buildCombinedText(sources),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function readFileIfExists(p) {
|
|
58
|
+
try {
|
|
59
|
+
const stat = await fs.stat(p);
|
|
60
|
+
if (!stat.isFile())
|
|
61
|
+
return null;
|
|
62
|
+
const raw = await fs.readFile(p, "utf8");
|
|
63
|
+
return raw.includes("\u0000") ? null : raw;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
const code = err?.code;
|
|
67
|
+
if (code && code !== "ENOENT") {
|
|
68
|
+
log.debug(`AIGENCY.md okuma hatası: ${err.message}`);
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function isInside(child, parent) {
|
|
74
|
+
const rel = path.relative(path.resolve(parent), path.resolve(child));
|
|
75
|
+
return !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
76
|
+
}
|
|
77
|
+
function buildCombinedText(sources) {
|
|
78
|
+
if (sources.length === 0)
|
|
79
|
+
return "";
|
|
80
|
+
const parts = [
|
|
81
|
+
"## Kullanıcı Talimatları (AIGENCY.md)",
|
|
82
|
+
"Projede geçerli olan ve her oturumda zorunlu uygulanan talimatlar aşağıdadır.",
|
|
83
|
+
"",
|
|
84
|
+
];
|
|
85
|
+
for (const src of sources) {
|
|
86
|
+
parts.push(`### Kaynak: ${src.label}`);
|
|
87
|
+
parts.push("```markdown");
|
|
88
|
+
parts.push(src.content.trim());
|
|
89
|
+
parts.push("```");
|
|
90
|
+
parts.push("");
|
|
91
|
+
}
|
|
92
|
+
parts.push("Yukarıdaki talimatlar SİSTEM KURALLARINDAN sonra, kullanıcı mesajından ÖNCE geçerlidir.", "Birden fazla kaynak çelişirse: lokal (kişisel) > proje > global sırasıyla öncelik ver.");
|
|
93
|
+
return parts.join("\n");
|
|
94
|
+
}
|
package/dist/agent/loop.js
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AIGENCY CLI — agent loop orchestrator.
|
|
3
|
-
*
|
|
4
|
-
* Akış:
|
|
5
|
-
* 1. Kullanıcı prompt → request_id üret → stream endpoint'ine POST
|
|
6
|
-
* 2. SSE event'lerini dinle
|
|
7
|
-
* 3. cli_tool_dispatch gelirse:
|
|
8
|
-
* - Yerel tool registry'den handler'ı bul
|
|
9
|
-
* - Permission callback ile kullanıcıya sor (gerekirse)
|
|
10
|
-
* - Çalıştır → ToolResult
|
|
11
|
-
* - POST /api/cli/stream/tool-result ile sunucuya döndür
|
|
12
|
-
* 4. agent_message, cli_token_usage, cli_quota_warning event'leri UI'a iletilir
|
|
13
|
-
* 5. done veya error → loop biter
|
|
14
|
-
*/
|
|
15
1
|
import crypto from "node:crypto";
|
|
16
2
|
import { openStream, sendToolResult } from "../api-client/stream.js";
|
|
17
3
|
import { dispatchLocalTool, hasLocalTool } from "../tools/registry.js";
|
|
@@ -24,17 +10,12 @@ function generateRequestId() {
|
|
|
24
10
|
function cwdHash(cwd) {
|
|
25
11
|
return crypto.createHash("sha256").update(cwd).digest("hex").slice(0, 32);
|
|
26
12
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Session-level permission cache — "a" (oturum boyunca kabul) için.
|
|
29
|
-
*/
|
|
30
13
|
const SESSION_APPROVED_TOOLS = new Set();
|
|
31
14
|
function wrapPermission(originalRequestPermission, mode) {
|
|
32
15
|
return async (req) => {
|
|
33
|
-
// Plan mode zaten tool seviyesinde reddediyor, ama ekstra guard
|
|
34
16
|
if (mode === "plan" && (req.toolName === "write_file" || req.toolName === "edit_file")) {
|
|
35
17
|
return { decision: "reject" };
|
|
36
18
|
}
|
|
37
|
-
// Session'da zaten kabul edilmişse tekrar sorma
|
|
38
19
|
if (SESSION_APPROVED_TOOLS.has(req.toolName)) {
|
|
39
20
|
return { decision: "approve" };
|
|
40
21
|
}
|
|
@@ -55,11 +36,13 @@ export async function runAgentLoop(params) {
|
|
|
55
36
|
let aborted = false;
|
|
56
37
|
const toolCtx = {
|
|
57
38
|
cwd: params.workingDirectory,
|
|
39
|
+
projectRoot: params.projectRoot,
|
|
40
|
+
sessionId: params.sessionId,
|
|
58
41
|
mode: params.mode,
|
|
59
42
|
signal: params.signal,
|
|
60
43
|
requestPermission: wrapPermission(params.requestPermission, params.mode),
|
|
44
|
+
onLiveOutput: params.onLiveOutput,
|
|
61
45
|
};
|
|
62
|
-
// History'yi hazırla + chunk'la
|
|
63
46
|
const chunk = prepareChunk(params.prompt, params.history.getAll());
|
|
64
47
|
if (chunk.warning) {
|
|
65
48
|
log.debug(`chunker: ${chunk.warning}`);
|
|
@@ -71,7 +54,6 @@ export async function runAgentLoop(params) {
|
|
|
71
54
|
tool_calls: m.tool_calls,
|
|
72
55
|
}));
|
|
73
56
|
const onEvent = async (event) => {
|
|
74
|
-
// Her event UI'a iletilir (UI render eder)
|
|
75
57
|
params.onEvent(event);
|
|
76
58
|
switch (event.type) {
|
|
77
59
|
case "agent_message":
|
|
@@ -87,10 +69,8 @@ export async function runAgentLoop(params) {
|
|
|
87
69
|
outputTokens = event.output;
|
|
88
70
|
break;
|
|
89
71
|
case "cli_tool_dispatch": {
|
|
90
|
-
// Sunucu LLM'den gelen tool call'u bize devretti — yerel çalıştır
|
|
91
72
|
toolCalls++;
|
|
92
73
|
if (!hasLocalTool(event.toolName)) {
|
|
93
|
-
// Bilinmeyen tool — server'a hata dön
|
|
94
74
|
const errorResult = {
|
|
95
75
|
success: false,
|
|
96
76
|
output: "",
|
|
@@ -107,7 +87,6 @@ export async function runAgentLoop(params) {
|
|
|
107
87
|
});
|
|
108
88
|
return;
|
|
109
89
|
}
|
|
110
|
-
// PreToolUse hook
|
|
111
90
|
if (params.settings) {
|
|
112
91
|
await runHooks("preToolUse", params.settings, {
|
|
113
92
|
toolName: event.toolName,
|
|
@@ -127,10 +106,7 @@ export async function runAgentLoop(params) {
|
|
|
127
106
|
duration: 0,
|
|
128
107
|
};
|
|
129
108
|
}
|
|
130
|
-
// approvalState'i ToolResult'a ekleme — backend schema'sında yok,
|
|
131
|
-
// ayrı parametre olarak gönder
|
|
132
109
|
const approvalState = result.approvalState ?? 2;
|
|
133
|
-
// PostToolUse hook
|
|
134
110
|
if (params.settings) {
|
|
135
111
|
await runHooks("postToolUse", params.settings, {
|
|
136
112
|
toolName: event.toolName,
|
|
@@ -162,7 +138,6 @@ export async function runAgentLoop(params) {
|
|
|
162
138
|
}
|
|
163
139
|
case "error":
|
|
164
140
|
case "agent_error":
|
|
165
|
-
// UI error event'i gösterecek — burada ek bir şey yapmaya gerek yok
|
|
166
141
|
break;
|
|
167
142
|
case "done":
|
|
168
143
|
if (event.fullContent)
|
|
@@ -191,7 +166,6 @@ export async function runAgentLoop(params) {
|
|
|
191
166
|
throw err;
|
|
192
167
|
}
|
|
193
168
|
}
|
|
194
|
-
// Stop hook — oturum bitişinde
|
|
195
169
|
if (params.settings) {
|
|
196
170
|
try {
|
|
197
171
|
await runHooks("stop", params.settings, {
|
|
@@ -199,7 +173,6 @@ export async function runAgentLoop(params) {
|
|
|
199
173
|
});
|
|
200
174
|
}
|
|
201
175
|
catch {
|
|
202
|
-
// ignore
|
|
203
176
|
}
|
|
204
177
|
}
|
|
205
178
|
return {
|
|
@@ -210,10 +183,6 @@ export async function runAgentLoop(params) {
|
|
|
210
183
|
aborted,
|
|
211
184
|
};
|
|
212
185
|
}
|
|
213
|
-
/**
|
|
214
|
-
* Test veya /clear için session kabul cache'ini sıfırla.
|
|
215
|
-
*/
|
|
216
186
|
export function clearSessionApprovalCache() {
|
|
217
187
|
SESSION_APPROVED_TOOLS.clear();
|
|
218
188
|
}
|
|
219
|
-
//# sourceMappingURL=loop.js.map
|