@kalenkevich/agent_007 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 (247) hide show
  1. package/dist/agent/agent.d.ts +17 -0
  2. package/dist/agent/agent.js +6 -0
  3. package/dist/agent/agent.js.map +1 -0
  4. package/dist/agent/agent_event.d.ts +108 -0
  5. package/dist/agent/agent_event.js +56 -0
  6. package/dist/agent/agent_event.js.map +1 -0
  7. package/dist/agent/agent_event_utils.d.ts +5 -0
  8. package/dist/agent/agent_event_utils.js +66 -0
  9. package/dist/agent/agent_event_utils.js.map +1 -0
  10. package/dist/agent/cli_agent/cli_agent.d.ts +41 -0
  11. package/dist/agent/cli_agent/cli_agent.js +324 -0
  12. package/dist/agent/cli_agent/cli_agent.js.map +1 -0
  13. package/dist/agent/cli_agent/system_prompt.d.ts +1 -0
  14. package/dist/agent/cli_agent/system_prompt.js +58 -0
  15. package/dist/agent/cli_agent/system_prompt.js.map +1 -0
  16. package/dist/agent/cli_agent.d.ts +34 -0
  17. package/dist/agent/cli_agent.js +91 -0
  18. package/dist/agent/cli_agent.js.map +1 -0
  19. package/dist/agent/planner_agent/planner_agent.d.ts +28 -0
  20. package/dist/agent/planner_agent/planner_agent.js +102 -0
  21. package/dist/agent/planner_agent/planner_agent.js.map +1 -0
  22. package/dist/agent/request_processor/basic_request_processor.d.ts +17 -0
  23. package/dist/agent/request_processor/basic_request_processor.js +28 -0
  24. package/dist/agent/request_processor/basic_request_processor.js.map +1 -0
  25. package/dist/agent/request_processor/compaction_processor.d.ts +17 -0
  26. package/dist/agent/request_processor/compaction_processor.js +118 -0
  27. package/dist/agent/request_processor/compaction_processor.js.map +1 -0
  28. package/dist/agent/request_processor/request_processor.d.ts +11 -0
  29. package/dist/agent/request_processor/request_processor.js +2 -0
  30. package/dist/agent/request_processor/request_processor.js.map +1 -0
  31. package/dist/cli/init_project_command_handler.d.ts +3 -0
  32. package/dist/cli/init_project_command_handler.js +58 -0
  33. package/dist/cli/init_project_command_handler.js.map +1 -0
  34. package/dist/cli/loader.d.ts +6 -0
  35. package/dist/cli/loader.js +27 -0
  36. package/dist/cli/loader.js.map +1 -0
  37. package/dist/cli/prompt_utils.d.ts +11 -0
  38. package/dist/cli/prompt_utils.js +18 -0
  39. package/dist/cli/prompt_utils.js.map +1 -0
  40. package/dist/cli/run_command.d.ts +6 -0
  41. package/dist/cli/run_command.js +72 -0
  42. package/dist/cli/run_command.js.map +1 -0
  43. package/dist/cli/run_interactive_command.d.ts +6 -0
  44. package/dist/cli/run_interactive_command.js +282 -0
  45. package/dist/cli/run_interactive_command.js.map +1 -0
  46. package/dist/cli/run_noninteractive_command.d.ts +6 -0
  47. package/dist/cli/run_noninteractive_command.js +60 -0
  48. package/dist/cli/run_noninteractive_command.js.map +1 -0
  49. package/dist/cli_entrypoint.d.ts +2 -0
  50. package/dist/cli_entrypoint.js +110 -0
  51. package/dist/cli_entrypoint.js.map +1 -0
  52. package/dist/command/commnad_handler.d.ts +3 -0
  53. package/dist/command/commnad_handler.js +2 -0
  54. package/dist/command/commnad_handler.js.map +1 -0
  55. package/dist/command/init_project_command_handler.d.ts +4 -0
  56. package/dist/command/init_project_command_handler.js +58 -0
  57. package/dist/command/init_project_command_handler.js.map +1 -0
  58. package/dist/config/app_dir.d.ts +1 -0
  59. package/dist/config/app_dir.js +4 -0
  60. package/dist/config/app_dir.js.map +1 -0
  61. package/dist/config/config.d.ts +21 -0
  62. package/dist/config/config.js +2 -0
  63. package/dist/config/config.js.map +1 -0
  64. package/dist/config/config_loader.d.ts +2 -0
  65. package/dist/config/config_loader.js +47 -0
  66. package/dist/config/config_loader.js.map +1 -0
  67. package/dist/config/config_store.d.ts +10 -0
  68. package/dist/config/config_store.js +44 -0
  69. package/dist/config/config_store.js.map +1 -0
  70. package/dist/content.d.ts +51 -0
  71. package/dist/content.js +16 -0
  72. package/dist/content.js.map +1 -0
  73. package/dist/core/functions.d.ts +1 -0
  74. package/dist/core/functions.js +6 -0
  75. package/dist/core/functions.js.map +1 -0
  76. package/dist/core/loop.d.ts +20 -0
  77. package/dist/core/loop.js +121 -0
  78. package/dist/core/loop.js.map +1 -0
  79. package/dist/core/project_service.d.ts +11 -0
  80. package/dist/core/project_service.js +26 -0
  81. package/dist/core/project_service.js.map +1 -0
  82. package/dist/core/run.d.ts +6 -0
  83. package/dist/core/run.js +25 -0
  84. package/dist/core/run.js.map +1 -0
  85. package/dist/index.d.ts +1 -0
  86. package/dist/index.js +2 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/logger.d.ts +27 -0
  89. package/dist/logger.js +98 -0
  90. package/dist/logger.js.map +1 -0
  91. package/dist/model/adaptive_model.d.ts +16 -0
  92. package/dist/model/adaptive_model.js +57 -0
  93. package/dist/model/adaptive_model.js.map +1 -0
  94. package/dist/model/google/gemini_model.d.ts +15 -0
  95. package/dist/model/google/gemini_model.js +131 -0
  96. package/dist/model/google/gemini_model.js.map +1 -0
  97. package/dist/model/google/gemini_response_utils.d.ts +3 -0
  98. package/dist/model/google/gemini_response_utils.js +36 -0
  99. package/dist/model/google/gemini_response_utils.js.map +1 -0
  100. package/dist/model/google/gemini_streaming_utils.d.ts +28 -0
  101. package/dist/model/google/gemini_streaming_utils.js +235 -0
  102. package/dist/model/google/gemini_streaming_utils.js.map +1 -0
  103. package/dist/model/google/gen_ai_convert_utils.d.ts +6 -0
  104. package/dist/model/google/gen_ai_convert_utils.js +117 -0
  105. package/dist/model/google/gen_ai_convert_utils.js.map +1 -0
  106. package/dist/model/model.d.ts +11 -0
  107. package/dist/model/model.js +2 -0
  108. package/dist/model/model.js.map +1 -0
  109. package/dist/model/registry.d.ts +19 -0
  110. package/dist/model/registry.js +47 -0
  111. package/dist/model/registry.js.map +1 -0
  112. package/dist/model/request.d.ts +13 -0
  113. package/dist/model/request.js +2 -0
  114. package/dist/model/request.js.map +1 -0
  115. package/dist/model/request_builder_utils.d.ts +19 -0
  116. package/dist/model/request_builder_utils.js +43 -0
  117. package/dist/model/request_builder_utils.js.map +1 -0
  118. package/dist/model/response.d.ts +60 -0
  119. package/dist/model/response.js +2 -0
  120. package/dist/model/response.js.map +1 -0
  121. package/dist/model/util_llm.d.ts +10 -0
  122. package/dist/model/util_llm.js +149 -0
  123. package/dist/model/util_llm.js.map +1 -0
  124. package/dist/session/session.d.ts +14 -0
  125. package/dist/session/session.js +2 -0
  126. package/dist/session/session.js.map +1 -0
  127. package/dist/session/session_file_service.d.ts +23 -0
  128. package/dist/session/session_file_service.js +147 -0
  129. package/dist/session/session_file_service.js.map +1 -0
  130. package/dist/skills/skill.d.ts +23 -0
  131. package/dist/skills/skill.js +12 -0
  132. package/dist/skills/skill.js.map +1 -0
  133. package/dist/tools/build_in/find.d.ts +3 -0
  134. package/dist/tools/build_in/find.js +71 -0
  135. package/dist/tools/build_in/find.js.map +1 -0
  136. package/dist/tools/build_in/grep.d.ts +3 -0
  137. package/dist/tools/build_in/grep.js +97 -0
  138. package/dist/tools/build_in/grep.js.map +1 -0
  139. package/dist/tools/build_in/index.d.ts +1 -0
  140. package/dist/tools/build_in/index.js +13 -0
  141. package/dist/tools/build_in/index.js.map +1 -0
  142. package/dist/tools/build_in/list_dir.d.ts +3 -0
  143. package/dist/tools/build_in/list_dir.js +46 -0
  144. package/dist/tools/build_in/list_dir.js.map +1 -0
  145. package/dist/tools/build_in/view_file.d.ts +3 -0
  146. package/dist/tools/build_in/view_file.js +44 -0
  147. package/dist/tools/build_in/view_file.js.map +1 -0
  148. package/dist/tools/build_in/write_file.d.ts +3 -0
  149. package/dist/tools/build_in/write_file.js +50 -0
  150. package/dist/tools/build_in/write_file.js.map +1 -0
  151. package/dist/tools/functional_tool.d.ts +18 -0
  152. package/dist/tools/functional_tool.js +20 -0
  153. package/dist/tools/functional_tool.js.map +1 -0
  154. package/dist/tools/schema.d.ts +86 -0
  155. package/dist/tools/schema.js +37 -0
  156. package/dist/tools/schema.js.map +1 -0
  157. package/dist/tools/tool.d.ts +30 -0
  158. package/dist/tools/tool.js +9 -0
  159. package/dist/tools/tool.js.map +1 -0
  160. package/dist/tools/tool_call_policy.d.ts +4 -0
  161. package/dist/tools/tool_call_policy.js +4 -0
  162. package/dist/tools/tool_call_policy.js.map +1 -0
  163. package/dist/tools/tool_policy.d.ts +4 -0
  164. package/dist/tools/tool_policy.js +4 -0
  165. package/dist/tools/tool_policy.js.map +1 -0
  166. package/dist/ui/ui.d.ts +1 -0
  167. package/dist/ui/ui.js +2 -0
  168. package/dist/ui/ui.js.map +1 -0
  169. package/dist/user_input.d.ts +20 -0
  170. package/dist/user_input.js +21 -0
  171. package/dist/user_input.js.map +1 -0
  172. package/package.json +44 -0
  173. package/src/agent/agent.ts +18 -0
  174. package/src/agent/agent_event.ts +171 -0
  175. package/src/agent/agent_event_utils.ts +87 -0
  176. package/src/agent/cli_agent/cli_agent.ts +418 -0
  177. package/src/agent/cli_agent/system_prompt.ts +57 -0
  178. package/src/agent/planner_agent/planner_agent.ts +136 -0
  179. package/src/agent/request_processor/basic_request_processor.ts +46 -0
  180. package/src/agent/request_processor/compaction_processor.ts +164 -0
  181. package/src/agent/request_processor/request_processor.ts +13 -0
  182. package/src/cli/loader.ts +27 -0
  183. package/src/cli/prompt_utils.ts +19 -0
  184. package/src/cli/run_interactive_command.ts +337 -0
  185. package/src/cli/run_noninteractive_command.ts +74 -0
  186. package/src/cli_entrypoint.ts +128 -0
  187. package/src/command/commnad_handler.ts +3 -0
  188. package/src/command/init_project_command_handler.ts +66 -0
  189. package/src/config/app_dir.ts +4 -0
  190. package/src/config/config.ts +24 -0
  191. package/src/config/config_loader.ts +57 -0
  192. package/src/config/config_store.ts +50 -0
  193. package/src/content.ts +87 -0
  194. package/src/core/functions.ts +7 -0
  195. package/src/core/loop.ts +165 -0
  196. package/src/core/project_service.ts +38 -0
  197. package/src/core/run.ts +36 -0
  198. package/src/index.ts +1 -0
  199. package/src/logger.ts +128 -0
  200. package/src/model/adaptive_model.ts +77 -0
  201. package/src/model/google/gemini_model.ts +194 -0
  202. package/src/model/google/gemini_response_utils.ts +46 -0
  203. package/src/model/google/gemini_streaming_utils.ts +294 -0
  204. package/src/model/google/gen_ai_convert_utils.ts +149 -0
  205. package/src/model/model.ts +18 -0
  206. package/src/model/registry.ts +61 -0
  207. package/src/model/request.ts +15 -0
  208. package/src/model/request_builder_utils.ts +79 -0
  209. package/src/model/response.ts +66 -0
  210. package/src/model/util_llm.ts +167 -0
  211. package/src/session/session.ts +16 -0
  212. package/src/session/session_file_service.ts +207 -0
  213. package/src/skills/skill.ts +38 -0
  214. package/src/tools/build_in/find.ts +80 -0
  215. package/src/tools/build_in/grep.ts +101 -0
  216. package/src/tools/build_in/index.ts +13 -0
  217. package/src/tools/build_in/list_dir.ts +50 -0
  218. package/src/tools/build_in/view_file.ts +47 -0
  219. package/src/tools/build_in/write_file.ts +53 -0
  220. package/src/tools/functional_tool.ts +59 -0
  221. package/src/tools/schema.ts +87 -0
  222. package/src/tools/tool.ts +68 -0
  223. package/src/tools/tool_call_policy.ts +7 -0
  224. package/src/ui/ui.ts +0 -0
  225. package/src/user_input.ts +51 -0
  226. package/tests/integration/util_llm_test.ts +42 -0
  227. package/tests/unit/adaptive_model_test.ts +122 -0
  228. package/tests/unit/agent/request_processor/compaction_processor_test.ts +121 -0
  229. package/tests/unit/cli/prompt_utils_test.ts +47 -0
  230. package/tests/unit/cli_agent_test.ts +476 -0
  231. package/tests/unit/content_test.ts +56 -0
  232. package/tests/unit/logger_test.ts +109 -0
  233. package/tests/unit/loop_test.ts +141 -0
  234. package/tests/unit/model/gemini_model_test.ts +111 -0
  235. package/tests/unit/planner_agent_test.ts +52 -0
  236. package/tests/unit/project_service_test.ts +102 -0
  237. package/tests/unit/session_file_service_test.ts +160 -0
  238. package/tests/unit/tools/find_test.ts +40 -0
  239. package/tests/unit/tools/grep_test.ts +43 -0
  240. package/tests/unit/tools/list_dir_test.ts +32 -0
  241. package/tests/unit/tools/view_file_test.ts +32 -0
  242. package/tests/unit/tools/write_file_test.ts +44 -0
  243. package/tests/unit/user_input_test.ts +55 -0
  244. package/tests/unit/util_llm_test.ts +61 -0
  245. package/todo.md +29 -0
  246. package/tsconfig.json +33 -0
  247. package/vitest.config.ts +33 -0
@@ -0,0 +1,128 @@
1
+ #! /usr/bin/env node
2
+
3
+ import { parseArgs } from "node:util";
4
+ import { runNoninteractiveCommand } from "./cli/run_noninteractive_command.js";
5
+ import { runInteractiveCommand } from "./cli/run_interactive_command.js";
6
+ import { createInterface } from "node:readline/promises";
7
+ import { stdin as input, stdout as output } from "node:process";
8
+ import { configStore } from "./config/config_store.js";
9
+ import { InitProjectCommandHandler } from "./command/init_project_command_handler.js";
10
+
11
+ const options = {
12
+ prompt: {
13
+ type: "string" as const,
14
+ short: "p",
15
+ },
16
+ model: {
17
+ type: "string" as const,
18
+ short: "m",
19
+ },
20
+ help: {
21
+ type: "boolean" as const,
22
+ short: "h",
23
+ },
24
+ debug: {
25
+ type: "boolean" as const,
26
+ },
27
+ };
28
+
29
+ const USAGE = `
30
+ Usage: agent007 <command> [options]
31
+
32
+ Commands:
33
+ i, interactive Run the agent interactively (REPL)
34
+ ni, noninteractive Run the agent with a prompt and exit
35
+ init Initialize project and define constants
36
+
37
+ Options:
38
+ -p, --prompt <string> The task for the agent
39
+ -m, --model <string> Override the default model
40
+ -h, --help Show this help message
41
+ --debug Enable debug mode (writes to debug.log)
42
+ `;
43
+
44
+ async function ensureApiKey() {
45
+ let geminiApiKey =
46
+ process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || "";
47
+ geminiApiKey = geminiApiKey.trim();
48
+
49
+ if (!geminiApiKey) {
50
+ geminiApiKey = (await configStore.getApiKey()) || "";
51
+ }
52
+
53
+ if (!geminiApiKey) {
54
+ const rl = createInterface({ input, output });
55
+ try {
56
+ const answer = await rl.question("Please enter your Gemini API key: ");
57
+ geminiApiKey = answer.trim();
58
+ if (geminiApiKey) {
59
+ await configStore.setApiKey(geminiApiKey);
60
+ }
61
+ } finally {
62
+ rl.close();
63
+ }
64
+ }
65
+
66
+ if (geminiApiKey) {
67
+ process.env.GEMINI_API_KEY = geminiApiKey;
68
+ }
69
+ }
70
+
71
+ async function main() {
72
+ try {
73
+ const { values, positionals } = parseArgs({
74
+ options,
75
+ allowPositionals: true,
76
+ });
77
+
78
+ if (values.help) {
79
+ console.log(USAGE);
80
+ return;
81
+ }
82
+
83
+ if (values.debug) {
84
+ console.log("Debug mode: enabled");
85
+ process.env.DEBUG_LOGGER = "true";
86
+ }
87
+
88
+ await ensureApiKey();
89
+
90
+ const command = positionals[0];
91
+
92
+ if (!command || command === "i" || command === "interactive") {
93
+ await runInteractiveCommand({
94
+ prompt: values.prompt,
95
+ model: values.model,
96
+ positionals: positionals.slice(1),
97
+ });
98
+
99
+ return;
100
+ }
101
+
102
+ if (command === "ni" || command === "run_noninteractive") {
103
+ await runNoninteractiveCommand({
104
+ prompt: values.prompt,
105
+ model: values.model,
106
+ positionals: positionals.slice(1),
107
+ });
108
+
109
+ return;
110
+ }
111
+
112
+ if (command === "init") {
113
+ const handler = new InitProjectCommandHandler();
114
+ await handler.handle();
115
+ return;
116
+ }
117
+
118
+ console.error(`Unknown command: ${command}`);
119
+ console.log("Use --help to see available commands.");
120
+ process.exit(1);
121
+ } catch (error: any) {
122
+ console.error("Error:", error.message);
123
+ process.exit(1);
124
+ }
125
+ }
126
+
127
+ main();
128
+
@@ -0,0 +1,3 @@
1
+ export interface CommandHandler {
2
+ handle(): Promise<void>;
3
+ }
@@ -0,0 +1,66 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { projectService } from "../core/project_service.js";
4
+ import { loadConfig } from "../config/config_loader.js";
5
+ import { UtilLlm } from "../model/util_llm.js";
6
+ import { resolveLlmModel } from "../model/registry.js";
7
+ import { type CommandHandler } from "./commnad_handler.js";
8
+ import { logger } from "../logger.js";
9
+
10
+ export class InitProjectCommandHandler implements CommandHandler {
11
+ async handle(): Promise<void> {
12
+ const cwd = process.cwd();
13
+ const projectId = projectService.getProjectId();
14
+
15
+ logger.info(`Initializing project in ${cwd}`);
16
+ logger.info(`Project ID: ${projectId}`);
17
+
18
+ // Gather context
19
+ let context = `Project Path: ${cwd}\n\n`;
20
+
21
+ // List top level files and dirs
22
+ try {
23
+ const files = await fs.readdir(cwd);
24
+ context += `Top level files and directories:\n${files.join("\n")}\n\n`;
25
+ } catch (e) {
26
+ logger.error("Failed to read directory:", e);
27
+ }
28
+
29
+ // Read package.json if exists
30
+ const packageJsonPath = path.join(cwd, "package.json");
31
+ try {
32
+ const packageJson = await fs.readFile(packageJsonPath, "utf-8");
33
+ context += `package.json:\n${packageJson}\n\n`;
34
+ } catch (e) {
35
+ // Ignore if doesn't exist
36
+ }
37
+
38
+ // Read tsconfig.json if exists
39
+ const tsconfigPath = path.join(cwd, "tsconfig.json");
40
+ try {
41
+ const tsconfig = await fs.readFile(tsconfigPath, "utf-8");
42
+ context += `tsconfig.json:\n${tsconfig}\n\n`;
43
+ } catch (e) {
44
+ // Ignore if doesn't exist
45
+ }
46
+
47
+ // Load config for UtilLlm
48
+ const config = await loadConfig();
49
+ const utilModelConfig = config.models.util;
50
+ if (!utilModelConfig) {
51
+ throw new Error("Util model config is missing");
52
+ }
53
+ const UtilModelClass = resolveLlmModel(utilModelConfig.modelName);
54
+ const utilLlm = new UtilLlm(new UtilModelClass(utilModelConfig));
55
+
56
+ logger.info("Scanning project constants with LLM...");
57
+ const constants = await utilLlm.scanProjectConstants(context);
58
+
59
+ logger.info("Saving constants...");
60
+ await projectService.saveConstants(constants);
61
+
62
+ logger.info(
63
+ `Project constants saved to ${projectService.getConstantsPath()} in config store.`,
64
+ );
65
+ }
66
+ }
@@ -0,0 +1,4 @@
1
+ import * as path from "node:path";
2
+ import * as os from "node:os";
3
+
4
+ export const APP_FILE_DIR = path.join(os.homedir(), ".agent_007");
@@ -0,0 +1,24 @@
1
+ import { type ThinkingConfig } from "../model/request.js";
2
+
3
+ export interface ModelConfig {
4
+ modelName: string;
5
+ apiKey: string;
6
+ tokenLimit?: number;
7
+ }
8
+
9
+ export interface CompactionConfig {
10
+ enabled: boolean;
11
+ strategy: "truncate" | "summarize" | "hybrid" | "compact";
12
+ maxTokens: number;
13
+ triggerThreshold?: number;
14
+ }
15
+
16
+ export interface Config {
17
+ models: {
18
+ main: ModelConfig;
19
+ fallback?: ModelConfig[];
20
+ util?: ModelConfig;
21
+ };
22
+ thinkingConfig: ThinkingConfig;
23
+ compactionConfig: CompactionConfig;
24
+ }
@@ -0,0 +1,57 @@
1
+ import type { ThinkingConfig } from "../model/request.js";
2
+ import type { Config } from "./config.js";
3
+ import { configStore } from "./config_store.js";
4
+
5
+ export async function loadConfig(): Promise<Config> {
6
+ const geminiApiKey =
7
+ process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || "";
8
+
9
+ let thinkingLevel = process.env.THINKING_LEVEL as ThinkingConfig["level"] | undefined;
10
+ if (!thinkingLevel) {
11
+ const storedLevel = await configStore.get("thinking_level");
12
+ if (storedLevel) {
13
+ thinkingLevel = storedLevel as ThinkingConfig["level"];
14
+ }
15
+ }
16
+
17
+ return {
18
+ models: {
19
+ main: {
20
+ modelName: process.env.MODEL_NAME || "gemini-3.1-pro-preview",
21
+ apiKey: geminiApiKey,
22
+ },
23
+ fallback: [
24
+ {
25
+ modelName: process.env.FALLBACK_MODEL_NAME || "gemini-2.5-pro",
26
+ apiKey: geminiApiKey,
27
+ },
28
+ {
29
+ modelName:
30
+ process.env.FALLBACK_MODEL_NAME || "gemini-3-flash-preview",
31
+ apiKey: geminiApiKey,
32
+ },
33
+ {
34
+ modelName: process.env.FALLBACK_MODEL_NAME || "gemini-2.5-flash",
35
+ apiKey: geminiApiKey,
36
+ },
37
+ ],
38
+ util: {
39
+ modelName:
40
+ process.env.UTIL_MODEL_NAME || "gemini-3.1-flash-lite-preview",
41
+ apiKey: geminiApiKey,
42
+ },
43
+ },
44
+ thinkingConfig: {
45
+ enabled: process.env.ENABLE_THINKING !== "false",
46
+ level: thinkingLevel,
47
+ },
48
+ compactionConfig: {
49
+ maxTokens: 10000,
50
+ enabled: process.env.ENABLE_COMPACTION !== "false",
51
+ strategy: process.env.COMPACTION_STRATEGY as
52
+ | "summarize"
53
+ | "truncate"
54
+ | "hybrid",
55
+ },
56
+ };
57
+ }
@@ -0,0 +1,50 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { APP_FILE_DIR } from "./app_dir.js";
4
+
5
+ export class ConfigStore {
6
+ private configDir: string;
7
+
8
+ constructor() {
9
+ this.configDir = APP_FILE_DIR;
10
+ }
11
+
12
+ private async ensureDir() {
13
+ await fs.mkdir(this.configDir, { recursive: true });
14
+ }
15
+
16
+ async getApiKey(): Promise<string | null> {
17
+ const keyFilePath = path.join(this.configDir, "api_key");
18
+ try {
19
+ const content = await fs.readFile(keyFilePath, "utf-8");
20
+ return content.trim();
21
+ } catch (error) {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ async setApiKey(key: string): Promise<void> {
27
+ await this.ensureDir();
28
+ const keyFilePath = path.join(this.configDir, "api_key");
29
+ await fs.writeFile(keyFilePath, key.trim(), "utf-8");
30
+ }
31
+
32
+ async get(filename: string): Promise<string | null> {
33
+ const filePath = path.join(this.configDir, filename);
34
+ try {
35
+ const content = await fs.readFile(filePath, "utf-8");
36
+ return content.trim();
37
+ } catch (error) {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ async set(filename: string, value: string): Promise<void> {
43
+ await this.ensureDir();
44
+ const filePath = path.join(this.configDir, filename);
45
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
46
+ await fs.writeFile(filePath, value.trim(), "utf-8");
47
+ }
48
+ }
49
+
50
+ export const configStore = new ConfigStore();
package/src/content.ts ADDED
@@ -0,0 +1,87 @@
1
+ export type ContentRole = "user" | "agent" | "developer";
2
+
3
+ export interface Content {
4
+ role: ContentRole;
5
+ parts: ContentPart[];
6
+ }
7
+
8
+ export type ContentPart =
9
+ | TextContentPart
10
+ | ThoughtContentPart
11
+ | MediaContentPart
12
+ | FunctionCallContentPart
13
+ | FunctionResponseContentPart;
14
+
15
+ export interface TextContentPart {
16
+ type: "text";
17
+ text: string;
18
+ }
19
+
20
+ export function isTextContentPart(part: ContentPart): part is TextContentPart {
21
+ return part.type === "text";
22
+ }
23
+
24
+ export interface ThoughtContentPart {
25
+ type: "thought";
26
+ thought: string;
27
+ thoughtSignature?: string;
28
+ }
29
+
30
+ export function isThoughtContentPart(
31
+ part: ContentPart,
32
+ ): part is ThoughtContentPart {
33
+ return part.type === "thought";
34
+ }
35
+
36
+ export interface MediaContentPart {
37
+ type: "media";
38
+ data?: string;
39
+ uri?: string;
40
+ mimeType?: string;
41
+ }
42
+
43
+ export function isMediaContentPart(
44
+ part: ContentPart,
45
+ ): part is MediaContentPart {
46
+ return part.type === "media";
47
+ }
48
+
49
+ export interface FunctionCallContentPart {
50
+ type: "function_call";
51
+ id?: string;
52
+ args?: Record<string, unknown>;
53
+ partialArgs?: PartialArg[];
54
+ name?: string;
55
+ willContinue?: boolean;
56
+ thoughtSignature?: string;
57
+ }
58
+
59
+ export function isFunctionCallContentPart(
60
+ part: ContentPart,
61
+ ): part is FunctionCallContentPart {
62
+ return part.type === "function_call";
63
+ }
64
+
65
+ export interface FunctionResponseContentPart {
66
+ type: "function_response";
67
+ willContinue?: boolean;
68
+ id?: string;
69
+ name?: string;
70
+ response?: Record<string, unknown>;
71
+ thoughtSignature?: string;
72
+ }
73
+
74
+ export function isFunctionResponseContentPart(
75
+ part: ContentPart,
76
+ ): part is FunctionResponseContentPart {
77
+ return part.type === "function_response";
78
+ }
79
+
80
+ export interface PartialArg {
81
+ boolValue?: boolean;
82
+ jsonPath?: string;
83
+ nullValue?: "NULL_VALUE";
84
+ numberValue?: number;
85
+ stringValue?: string;
86
+ willContinue?: boolean;
87
+ }
@@ -0,0 +1,7 @@
1
+ import {randomUUID} from 'node:crypto';
2
+
3
+ const FUNCTION_CALL_ID_PREFIX = 'kalenkevich_agent_';
4
+
5
+ export function generateClientFunctionCallId(): string {
6
+ return `${FUNCTION_CALL_ID_PREFIX}${randomUUID()}`;
7
+ }
@@ -0,0 +1,165 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { Agent } from "../agent/agent.js";
3
+ import type { UserInput } from "../user_input.js";
4
+ import { CliAgent } from "../agent/cli_agent/cli_agent.js";
5
+ import { Run } from "./run.js";
6
+ import type { Config } from "../config/config.js";
7
+ import { AdaptiveLlmModel } from "../model/adaptive_model.js";
8
+ import { randomUUID } from "node:crypto";
9
+ import { UtilLlm } from "../model/util_llm.js";
10
+ import {
11
+ AgentEventType,
12
+ type AgentEvent,
13
+ type ErrorEvent,
14
+ } from "../agent/agent_event.js";
15
+ import { logger } from "../logger.js";
16
+ import { SessionFileService } from "../session/session_file_service.js";
17
+ import { resolveLlmModel } from "../model/registry.js";
18
+ import { projectService } from "./project_service.js";
19
+ import { CLI_AGENT_SYSTEM_PROMPT } from "../agent/cli_agent/system_prompt.js";
20
+
21
+ export enum AgentLoopType {
22
+ AGENT_EVENT = 'AGENT_EVENT',
23
+ }
24
+
25
+ export class CoreAgentLoop extends EventEmitter {
26
+ private agent?: Agent;
27
+ private initialized = false;
28
+ private currentRun?: Run;
29
+ private config: Config;
30
+ private sessionService: SessionFileService;
31
+ private sessionId?: string;
32
+ private utilLlm?: UtilLlm;
33
+ private sessionTitleGenerated = false;
34
+
35
+ constructor(config: Config, sessionId?: string) {
36
+ super();
37
+ this.config = config;
38
+ this.sessionId = sessionId;
39
+ this.sessionService = new SessionFileService();
40
+ }
41
+
42
+ private async init() {
43
+ if (this.initialized) {
44
+ return;
45
+ }
46
+
47
+ let history: AgentEvent[] = [];
48
+ if (this.sessionId) {
49
+ const session = await this.sessionService.getSession(this.sessionId);
50
+ history = session.events;
51
+ }
52
+
53
+ const model = new AdaptiveLlmModel(this.config.models);
54
+ const constantsStr = await projectService.getConstants();
55
+
56
+ let instructions = CLI_AGENT_SYSTEM_PROMPT;
57
+ if (constantsStr) {
58
+ instructions += `\n\nProject Constants and Conventions:\n${constantsStr}`;
59
+ }
60
+
61
+ this.agent = new CliAgent({
62
+ model: model,
63
+ thinkingConfig: this.config.thinkingConfig,
64
+ history,
65
+ instructions,
66
+ });
67
+
68
+ const utilModelConfig = this.config.models.util;
69
+ if (!utilModelConfig) {
70
+ throw new Error("Util model config is missing");
71
+ }
72
+ const UtilModelClass = resolveLlmModel(utilModelConfig.modelName);
73
+ this.utilLlm = new UtilLlm(new UtilModelClass(utilModelConfig));
74
+
75
+ if (!this.sessionId) {
76
+ const session = await this.sessionService.createSession(
77
+ this.agent.name,
78
+ [],
79
+ );
80
+ this.sessionId = session.id;
81
+ }
82
+
83
+ this.initialized = true;
84
+ logger.debug("[CoreAgentLoop] initialized");
85
+ }
86
+
87
+ async run(userInput: UserInput) {
88
+ logger.debug(
89
+ "[CoreAgentLoop] run called with input:",
90
+ JSON.stringify(userInput, null, 2),
91
+ );
92
+ await this.currentRun?.wait();
93
+ this.currentRun = new Run();
94
+ this.currentRun.start();
95
+
96
+ await this.init();
97
+
98
+ let streamId = "unknown";
99
+ try {
100
+ for await (const event of this.agent!.run(userInput)) {
101
+ streamId = event.streamId;
102
+
103
+ if (this.sessionId) {
104
+ this.sessionService.appendEvent(this.sessionId, event);
105
+ }
106
+
107
+ this.emit(AgentLoopType.AGENT_EVENT, event);
108
+ }
109
+
110
+ if (!this.sessionTitleGenerated && this.sessionId) {
111
+ const sessionMeta = await this.sessionService.getSessionMetadata(
112
+ this.sessionId,
113
+ );
114
+ if (sessionMeta && !sessionMeta.title) {
115
+ const session = await this.sessionService.getSession(this.sessionId);
116
+ const userMessages = session.events.filter(
117
+ (e) => e.type === AgentEventType.MESSAGE && e.role === "user",
118
+ );
119
+ const agentMessages = session.events.filter(
120
+ (e) => e.type === AgentEventType.MESSAGE && e.role === "agent",
121
+ );
122
+
123
+ if (userMessages.length >= 1 && agentMessages.length >= 1) {
124
+ const title = await this.utilLlm!.generateSessionTitle(
125
+ session.events,
126
+ );
127
+ await this.sessionService.updateSession(this.sessionId, {
128
+ title,
129
+ });
130
+ this.sessionTitleGenerated = true;
131
+ logger.debug(`[CoreAgentLoop] Generated session title: ${title}`);
132
+ }
133
+ }
134
+ }
135
+ } catch (e: unknown) {
136
+ logger.error("[CoreAgentLoop] run error:", e);
137
+ const error = e as Error;
138
+ const errorEvent: ErrorEvent = {
139
+ id: randomUUID(),
140
+ streamId: streamId,
141
+ timestamp: new Date().toISOString(),
142
+ role: "agent",
143
+ type: AgentEventType.ERROR,
144
+ statusCode: 500,
145
+ errorMessage: error.message || String(error),
146
+ fatal: true,
147
+ };
148
+
149
+ if (this.sessionId) {
150
+ this.sessionService.appendEvent(this.sessionId, errorEvent);
151
+ }
152
+
153
+ this.emit(AgentLoopType.AGENT_EVENT, errorEvent);
154
+ } finally {
155
+ this.currentRun.finish();
156
+ this.currentRun = undefined;
157
+ }
158
+ }
159
+
160
+ async abort() {
161
+ if (this.currentRun) {
162
+ this.agent?.abort();
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,38 @@
1
+ import { createHash } from "node:crypto";
2
+ import { configStore } from "../config/config_store.js";
3
+
4
+ export class ProjectService {
5
+ private cwd: string;
6
+ private projectId: string;
7
+
8
+ constructor(cwd: string = process.cwd()) {
9
+ this.cwd = cwd;
10
+ this.projectId = createHash("sha256").update(this.cwd).digest("hex");
11
+ }
12
+
13
+ getProjectId(): string {
14
+ return this.projectId;
15
+ }
16
+
17
+ getConstantsPath(): string {
18
+ return `projects/${this.projectId}/constants.json`;
19
+ }
20
+
21
+ async getConstants(): Promise<string | null> {
22
+ return await configStore.get(this.getConstantsPath());
23
+ }
24
+
25
+ async saveConstants(constants: any): Promise<void> {
26
+ await configStore.set(
27
+ this.getConstantsPath(),
28
+ JSON.stringify(constants, null, 2),
29
+ );
30
+ }
31
+
32
+ async isInitialized(): Promise<boolean> {
33
+ const constants = await this.getConstants();
34
+ return constants !== null;
35
+ }
36
+ }
37
+
38
+ export const projectService = new ProjectService();
@@ -0,0 +1,36 @@
1
+ interface Deferred {
2
+ promise: Promise<void>;
3
+ resolve: () => void;
4
+ reject: (error: Error) => void;
5
+ }
6
+
7
+ function createDeferred(): Deferred {
8
+ let resolve: () => void;
9
+ let reject: (error: Error) => void;
10
+ const promise = new Promise<void>((res, rej) => {
11
+ resolve = res;
12
+ reject = rej;
13
+ });
14
+ return { promise, resolve: resolve!, reject: reject! };
15
+ }
16
+
17
+ export class Run {
18
+ private deferred?: Deferred;
19
+
20
+ start() {
21
+ this.deferred = createDeferred();
22
+ }
23
+
24
+ finish() {
25
+ this.deferred?.resolve();
26
+ this.deferred = undefined;
27
+ }
28
+
29
+ wait() {
30
+ if (!this.deferred) {
31
+ return Promise.resolve();
32
+ }
33
+
34
+ return this.deferred.promise;
35
+ }
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { type Agent } from "./agent/agent.js";