@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,164 @@
1
+ import type { RequestProcessor, AgentState } from "./request_processor.js";
2
+ import type { LlmModel } from "../../model/model.js";
3
+ import type { CompactionConfig } from "../../config/config.js";
4
+ import type { BasicRequestProcessorOptions } from "./basic_request_processor.js";
5
+ import { buildLlmRequest } from "../../model/request_builder_utils.js";
6
+ import { logger } from "../../logger.js";
7
+ import {
8
+ AgentEventType,
9
+ type AgentEvent,
10
+ type CompactionEvent,
11
+ } from "../agent_event.js";
12
+ import { randomUUID } from "node:crypto";
13
+ import { UtilLlm } from "../../model/util_llm.js";
14
+ import type { Content } from "../../content.js";
15
+
16
+ export interface CompactionProcessorOptions {
17
+ model: LlmModel;
18
+ compactionConfig?: CompactionConfig;
19
+ requestBuilderOptions: BasicRequestProcessorOptions;
20
+ streamId: string;
21
+ }
22
+
23
+ export class CompactionProcessor implements RequestProcessor {
24
+ constructor(private options: CompactionProcessorOptions) {}
25
+
26
+ async process(state: AgentState): Promise<AgentState> {
27
+ const { compactionConfig, model, streamId } = this.options;
28
+
29
+ if (!compactionConfig?.enabled || !state.llmRequest) {
30
+ return state;
31
+ }
32
+
33
+ try {
34
+ const tokenCount = await model.countTokens(state.llmRequest);
35
+ logger.debug(`[CompactionProcessor] Current token count: ${tokenCount}`);
36
+
37
+ const maxTokens = compactionConfig.maxTokens;
38
+ const threshold = compactionConfig.triggerThreshold || 0.8;
39
+
40
+ if (tokenCount > maxTokens * threshold) {
41
+ logger.info(
42
+ `[CompactionProcessor] Token count ${tokenCount} exceeds threshold ${maxTokens * threshold}. Triggering compaction.`,
43
+ );
44
+
45
+ let newHistoryContent = [...state.historyContent];
46
+ let events = [...state.events];
47
+
48
+ if (compactionConfig.strategy === "truncate") {
49
+ const historyLength = newHistoryContent.length;
50
+ if (historyLength > 2) {
51
+ const removeCount = Math.ceil((historyLength - 1) * 0.2);
52
+ logger.info(
53
+ `[CompactionProcessor] Truncating oldest ${removeCount} messages from history.`,
54
+ );
55
+ newHistoryContent.splice(0, removeCount);
56
+
57
+ events.push(
58
+ this.createEvent(AgentEventType.COMPACTION, streamId, {
59
+ role: "agent",
60
+ strategy: "truncate",
61
+ parts: [
62
+ {
63
+ type: "text",
64
+ text: `[System: Context compacted to save tokens. Removed ${removeCount} older messages.]`,
65
+ },
66
+ ],
67
+ } as Partial<CompactionEvent>),
68
+ );
69
+
70
+ // Rebuild request
71
+ const llmRequest = this.rebuildRequest(newHistoryContent);
72
+ return {
73
+ historyContent: newHistoryContent,
74
+ llmRequest,
75
+ events,
76
+ };
77
+ }
78
+ }
79
+
80
+ if (compactionConfig.strategy === "summarize") {
81
+ logger.info(
82
+ `[CompactionProcessor] Compacting history using UtilLlm.`,
83
+ );
84
+ const utilLlm = new UtilLlm(model);
85
+ try {
86
+ const summary = await utilLlm.compactHistory(newHistoryContent);
87
+
88
+ newHistoryContent = [
89
+ {
90
+ role: "agent",
91
+ parts: [
92
+ {
93
+ type: "text",
94
+ text: `Summary of previous conversation:\n${summary}`,
95
+ },
96
+ ],
97
+ },
98
+ ];
99
+
100
+ events.push(
101
+ this.createEvent(AgentEventType.COMPACTION, streamId, {
102
+ role: "agent",
103
+ strategy: "summarize",
104
+ parts: [
105
+ {
106
+ type: "text",
107
+ text: `[System: Context compacted using LLM summarization.]`,
108
+ },
109
+ ],
110
+ } as Partial<CompactionEvent>),
111
+ );
112
+
113
+ // Rebuild request
114
+ const llmRequest = this.rebuildRequest(newHistoryContent);
115
+ return {
116
+ historyContent: newHistoryContent,
117
+ llmRequest,
118
+ events,
119
+ };
120
+ } catch (error: any) {
121
+ logger.error(
122
+ `[CompactionProcessor] Compaction failed: ${error.message}`,
123
+ );
124
+ }
125
+ }
126
+ }
127
+ } catch (error: any) {
128
+ logger.error(
129
+ `[CompactionProcessor] Failed to count tokens or compact context: ${error.message}`,
130
+ );
131
+ }
132
+
133
+ return state;
134
+ }
135
+
136
+ private rebuildRequest(historyContent: Content[]) {
137
+ const lastContent = historyContent[historyContent.length - 1];
138
+ const historyForRequest = historyContent.slice(0, -1);
139
+ return buildLlmRequest({
140
+ agentName: this.options.requestBuilderOptions.agentName,
141
+ content: lastContent,
142
+ historyContent: historyForRequest,
143
+ tools: this.options.requestBuilderOptions.tools,
144
+ skills: this.options.requestBuilderOptions.skills,
145
+ description: this.options.requestBuilderOptions.description,
146
+ instructions: this.options.requestBuilderOptions.instructions,
147
+ thinkingConfig: this.options.requestBuilderOptions.thinkingConfig,
148
+ });
149
+ }
150
+
151
+ private createEvent(
152
+ type: AgentEventType,
153
+ streamId: string,
154
+ data: Partial<AgentEvent> = {},
155
+ ): AgentEvent {
156
+ return {
157
+ type,
158
+ id: randomUUID(),
159
+ streamId,
160
+ timestamp: new Date().toISOString(),
161
+ ...data,
162
+ } as AgentEvent;
163
+ }
164
+ }
@@ -0,0 +1,13 @@
1
+ import type { Content } from "../../content.js";
2
+ import type { LlmRequest } from "../../model/request.js";
3
+ import type { AgentEvent } from "../agent_event.js";
4
+
5
+ export interface AgentState {
6
+ historyContent: Content[];
7
+ llmRequest?: LlmRequest;
8
+ events: AgentEvent[];
9
+ }
10
+
11
+ export interface RequestProcessor {
12
+ process(state: AgentState): Promise<AgentState>;
13
+ }
@@ -0,0 +1,27 @@
1
+ import * as readline from "node:readline";
2
+
3
+ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
4
+
5
+ export class TerminalLoader {
6
+ private loadingInterval: NodeJS.Timeout | null = null;
7
+ private frameIndex = 0;
8
+
9
+ startLoading() {
10
+ if (this.loadingInterval) return;
11
+ this.frameIndex = 0;
12
+ this.loadingInterval = setInterval(() => {
13
+ readline.cursorTo(process.stdout, 0);
14
+ process.stdout.write(SPINNER_FRAMES[this.frameIndex]);
15
+ this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
16
+ }, 80);
17
+ }
18
+
19
+ stopLoading() {
20
+ if (this.loadingInterval) {
21
+ clearInterval(this.loadingInterval);
22
+ this.loadingInterval = null;
23
+ readline.cursorTo(process.stdout, 0);
24
+ readline.clearLine(process.stdout, 0);
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,19 @@
1
+ export type UserAction = "accept" | "decline";
2
+
3
+ /**
4
+ * Checks if a user answer means "yes".
5
+ * Matches "yes", "y", and "accept" (case-insensitive).
6
+ * Can specify a default value for empty input.
7
+ */
8
+ export function isYes(answer: string, defaultYes = false): boolean {
9
+ const trimmed = answer.trim().toLowerCase();
10
+ if (trimmed === "") return defaultYes;
11
+ return trimmed === "yes" || trimmed === "y" || trimmed === "accept";
12
+ }
13
+
14
+ /**
15
+ * Parses a user answer to determine if it should be treated as an acceptance or decline.
16
+ */
17
+ export function parseUserAction(answer: string): UserAction {
18
+ return isYes(answer) ? "accept" : "decline";
19
+ }
@@ -0,0 +1,337 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ import { randomUUID } from "node:crypto";
4
+ import * as readline from "node:readline";
5
+ import { loadConfig } from "../config/config_loader.js";
6
+ import { CoreAgentLoop, AgentLoopType } from "../core/loop.js";
7
+ import {
8
+ AgentEventType,
9
+ type AgentEvent,
10
+ type UserInputRequestEvent,
11
+ type CompactionEvent,
12
+ } from "../agent/agent_event.js";
13
+ import { TerminalLoader } from "./loader.js";
14
+ import { configStore } from "../config/config_store.js";
15
+ import { projectService } from "../core/project_service.js";
16
+ import type { ThinkingConfig } from "../model/request.js";
17
+ import type { Session, SessionMetadata } from "../session/session.js";
18
+ import { SessionFileService } from "../session/session_file_service.js";
19
+ import { UserCommandType } from "../user_input.js";
20
+ import { InitProjectCommandHandler } from "../command/init_project_command_handler.js";
21
+ import { isYes, parseUserAction } from "./prompt_utils.js";
22
+
23
+ export interface RunCommandOptions {
24
+ prompt?: string;
25
+ model?: string;
26
+ positionals: string[];
27
+ }
28
+
29
+ export async function runInteractiveCommand(options: RunCommandOptions) {
30
+ let prompt = options.prompt;
31
+ if (!prompt && options.positionals.length > 0) {
32
+ prompt = options.positionals.join(" ");
33
+ }
34
+
35
+ const config = await loadConfig();
36
+
37
+ if (options.model) {
38
+ config.models.main.modelName = options.model;
39
+ }
40
+
41
+ console.log(`Using model: ${config.models.main.modelName}`);
42
+
43
+ const rl = createInterface({ input, output });
44
+
45
+ if (config.thinkingConfig.enabled && !config.thinkingConfig.level) {
46
+ const validLevels = ["low", "medium", "high", "auto"];
47
+ let level: string | null = null;
48
+ while (!level) {
49
+ const answer = await rl.question(
50
+ "Select thinking level (low, medium, high, auto) [default: high]: ",
51
+ );
52
+ const trimmed = answer.trim().toLowerCase();
53
+ if (trimmed === "") {
54
+ level = "high";
55
+ } else if (validLevels.includes(trimmed)) {
56
+ level = trimmed;
57
+ } else {
58
+ console.log(
59
+ "Invalid level. Please choose from: low, medium, high, auto",
60
+ );
61
+ }
62
+ }
63
+ config.thinkingConfig.level = level as ThinkingConfig["level"];
64
+ await configStore.set("thinking_level", level);
65
+ console.log(`Thinking level set to: ${level}`);
66
+ }
67
+
68
+ const sessionService = new SessionFileService();
69
+ const sessions = await sessionService.listSessions();
70
+ let sessionId: string | undefined = undefined;
71
+
72
+ if (sessions.length > 0) {
73
+ console.log("\nAvailable sessions:");
74
+ sessions.forEach((s, index) => {
75
+ console.log(`${index + 1}. ${getSessionName(s)}`);
76
+ });
77
+
78
+ const answer = await rl.question(
79
+ "\nSelect session number to resume or 'n' for new session [default: new]: ",
80
+ );
81
+ const trimmed = answer.trim().toLowerCase();
82
+ if (trimmed !== "" && trimmed !== "n") {
83
+ const selectedIndex = parseInt(trimmed, 10) - 1;
84
+ if (selectedIndex >= 0 && selectedIndex < sessions.length) {
85
+ sessionId = sessions[selectedIndex].id;
86
+ console.log(
87
+ `Resuming session: ${getSessionName(sessions[selectedIndex])}`,
88
+ );
89
+ } else {
90
+ console.log("Invalid selection. Starting a new session.");
91
+ }
92
+ }
93
+ }
94
+
95
+ if (!(await projectService.isInitialized())) {
96
+ const answer = await rl.question(
97
+ "\nProject has not been scanned yet. Initializing the project helps the LLM understand your codebase better and improves the quality of assistance.\nDo you want to run 'init' now? (yes/no) [default: yes]: ",
98
+ );
99
+ if (isYes(answer, true)) {
100
+ const handler = new InitProjectCommandHandler();
101
+ await handler.handle();
102
+ }
103
+ }
104
+
105
+ const loop = new CoreAgentLoop(config, sessionId);
106
+ const loader = new TerminalLoader();
107
+ let hasStreamed = false;
108
+ let isThinking = false;
109
+ const lastPrintedToolCalls = new Map<string, string>();
110
+ let pendingUserInputRequest: UserInputRequestEvent | null = null;
111
+
112
+ loop.on(AgentLoopType.AGENT_EVENT, (event: AgentEvent) => {
113
+ switch (event.type) {
114
+ case AgentEventType.START:
115
+ loader.startLoading();
116
+ hasStreamed = false;
117
+ lastPrintedToolCalls.clear();
118
+ break;
119
+ case AgentEventType.MESSAGE:
120
+ if (event.role === "agent" && event.parts) {
121
+ if (event.partial === true) {
122
+ hasStreamed = true;
123
+ loader.stopLoading();
124
+ for (const part of event.parts) {
125
+ if ("thought" in part && part.thought) {
126
+ if (!isThinking) {
127
+ isThinking = true;
128
+ rl.write("\x1b[2mThinking: ");
129
+ }
130
+ rl.write(part.thought);
131
+ } else if ("text" in part && part.text) {
132
+ if (isThinking) {
133
+ readline.cursorTo(process.stdout, 0);
134
+ readline.clearLine(process.stdout, 0);
135
+ isThinking = false;
136
+ rl.write("\x1b[0m"); // Reset style
137
+ }
138
+ rl.write(part.text);
139
+ }
140
+ }
141
+ } else {
142
+ if (hasStreamed) {
143
+ rl.write("\n");
144
+ break;
145
+ }
146
+ loader.stopLoading();
147
+ for (const part of event.parts) {
148
+ if ("text" in part && part.text) {
149
+ rl.write(part.text);
150
+ }
151
+ }
152
+ rl.write("\n");
153
+ }
154
+ }
155
+ break;
156
+ case AgentEventType.COMPACTION:
157
+ loader.stopLoading();
158
+ const compactionEvent = event as CompactionEvent;
159
+ console.log(
160
+ `\n\x1b[36m[Compaction: ${compactionEvent.strategy}]\x1b[0m`,
161
+ );
162
+ if (compactionEvent.parts) {
163
+ for (const part of compactionEvent.parts) {
164
+ if ("text" in part && part.text) {
165
+ console.log(part.text);
166
+ }
167
+ }
168
+ }
169
+ break;
170
+ case AgentEventType.END:
171
+ loader.stopLoading();
172
+ break;
173
+ case AgentEventType.ERROR:
174
+ loader.stopLoading();
175
+ console.error(`\nAgent Error: ${event.errorMessage}`);
176
+ break;
177
+ case AgentEventType.TOOL_CALL:
178
+ const contentStr = JSON.stringify({
179
+ name: event.name,
180
+ args: event.args,
181
+ });
182
+ if (lastPrintedToolCalls.get(event.requestId) === contentStr) {
183
+ break;
184
+ }
185
+ lastPrintedToolCalls.set(event.requestId, contentStr);
186
+
187
+ loader.stopLoading();
188
+ console.log(`\n\x1b[33m[Tool Call: ${event.name}]\x1b[0m`);
189
+ console.log(JSON.stringify(event.args, null, 2));
190
+ break;
191
+ case AgentEventType.TOOL_RESPONSE:
192
+ console.log(`\n\x1b[32m[Tool Response: ${event.name}]\x1b[0m`);
193
+ if (event.error) {
194
+ console.log(`\x1b[31mError: ${event.error}\x1b[0m`);
195
+ } else {
196
+ if (typeof event.result === "string") {
197
+ console.log(event.result);
198
+ } else {
199
+ console.log(JSON.stringify(event.result, null, 2));
200
+ }
201
+ }
202
+ break;
203
+ case AgentEventType.USER_INPUT_REQUEST:
204
+ loader.stopLoading();
205
+ pendingUserInputRequest = event as UserInputRequestEvent;
206
+ break;
207
+ default:
208
+ break;
209
+ }
210
+ });
211
+
212
+ try {
213
+ if (prompt) {
214
+ console.log(`\nInitial prompt: ${prompt}`);
215
+ await loop.run(prompt);
216
+
217
+ while (pendingUserInputRequest) {
218
+ const request = pendingUserInputRequest;
219
+ pendingUserInputRequest = null;
220
+ const answer = await rl.question(
221
+ `\n${(request as UserInputRequestEvent).message} (yes/no): `,
222
+ );
223
+ const action = parseUserAction(answer);
224
+
225
+ await loop.run({
226
+ type: AgentEventType.USER_INPUT_RESPONSE,
227
+ id: randomUUID(),
228
+ streamId: (request as UserInputRequestEvent).streamId,
229
+ timestamp: new Date().toISOString(),
230
+ role: "user",
231
+ requestId: (request as UserInputRequestEvent).requestId,
232
+ action,
233
+ });
234
+ }
235
+ }
236
+
237
+ while (true) {
238
+ const answer = await rl.question("\nUser > ");
239
+ const trimmedAnswer = answer.trim();
240
+
241
+ if (!trimmedAnswer) {
242
+ continue;
243
+ }
244
+
245
+ if (
246
+ trimmedAnswer.toLowerCase() === "exit" ||
247
+ trimmedAnswer.toLowerCase() === "quit"
248
+ ) {
249
+ console.log("Exiting...");
250
+ break;
251
+ }
252
+
253
+ if (trimmedAnswer.startsWith("/plan")) {
254
+ const task = trimmedAnswer.substring(5).trim();
255
+ await loop.run({ command: UserCommandType.PLAN, task: task });
256
+
257
+ while (pendingUserInputRequest) {
258
+ const request = pendingUserInputRequest;
259
+ pendingUserInputRequest = null;
260
+ const answer = await rl.question(
261
+ `\n${(request as UserInputRequestEvent).message} (yes/no): `,
262
+ );
263
+ const action = parseUserAction(answer);
264
+
265
+ await loop.run({
266
+ type: AgentEventType.USER_INPUT_RESPONSE,
267
+ id: randomUUID(),
268
+ streamId: (request as UserInputRequestEvent).streamId,
269
+ timestamp: new Date().toISOString(),
270
+ role: "user",
271
+ requestId: (request as UserInputRequestEvent).requestId,
272
+ action,
273
+ });
274
+ }
275
+ continue;
276
+ }
277
+
278
+ if (trimmedAnswer.startsWith("/init")) {
279
+ const handler = new InitProjectCommandHandler();
280
+ await handler.handle();
281
+ continue;
282
+ }
283
+
284
+ await loop.run(trimmedAnswer);
285
+
286
+ while (pendingUserInputRequest) {
287
+ const request = pendingUserInputRequest;
288
+ pendingUserInputRequest = null;
289
+ const answer = await rl.question(
290
+ `\n${(request as UserInputRequestEvent).message} (yes/no): `,
291
+ );
292
+ const action = parseUserAction(answer);
293
+
294
+ await loop.run({
295
+ type: AgentEventType.USER_INPUT_RESPONSE,
296
+ id: randomUUID(),
297
+ streamId: (request as UserInputRequestEvent).streamId,
298
+ timestamp: new Date().toISOString(),
299
+ role: "user",
300
+ requestId: (request as UserInputRequestEvent).requestId,
301
+ action,
302
+ });
303
+ }
304
+ }
305
+ } finally {
306
+ rl.close();
307
+ }
308
+ }
309
+
310
+ function formatDate(date: Date): string {
311
+ const now = new Date();
312
+ const isToday =
313
+ date.getDate() === now.getDate() &&
314
+ date.getMonth() === now.getMonth() &&
315
+ date.getFullYear() === now.getFullYear();
316
+
317
+ const hours = date.getHours().toString().padStart(2, "0");
318
+ const minutes = date.getMinutes().toString().padStart(2, "0");
319
+
320
+ if (isToday) {
321
+ return `today ${hours}:${minutes}`;
322
+ } else {
323
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
324
+ const day = date.getDate().toString().padStart(2, "0");
325
+ return `${date.getFullYear()}-${month}-${day} ${hours}:${minutes}`;
326
+ }
327
+ }
328
+
329
+ function getSessionName(session: Session | SessionMetadata): string {
330
+ if (!session.title) {
331
+ return session.id;
332
+ }
333
+
334
+ const date = new Date(session.timestamp);
335
+ const formattedDate = formatDate(date);
336
+ return `${session.title} (${formattedDate})`;
337
+ }
@@ -0,0 +1,74 @@
1
+ import { loadConfig } from "../config/config_loader.js";
2
+ import { CoreAgentLoop, AgentLoopType } from "../core/loop.js";
3
+ import { AgentEventType, type AgentEvent } from "../agent/agent_event.js";
4
+
5
+ export interface RunCommandOptions {
6
+ prompt?: string;
7
+ model?: string;
8
+ positionals: string[];
9
+ }
10
+
11
+ export async function runNoninteractiveCommand(options: RunCommandOptions) {
12
+ let prompt = options.prompt;
13
+ if (!prompt && options.positionals.length > 0) {
14
+ prompt = options.positionals.join(" ");
15
+ }
16
+
17
+ if (!prompt) {
18
+ console.error("Error: Please provide a prompt for non-interactive mode.");
19
+ process.exit(1);
20
+ }
21
+
22
+ const config = await loadConfig();
23
+
24
+ if (options.model) {
25
+ config.models.main.modelName = options.model;
26
+ }
27
+
28
+ console.log(`Using model: ${config.models.main.modelName}`);
29
+
30
+ const loop = new CoreAgentLoop(config);
31
+ const lastPrintedToolCalls = new Map<string, string>();
32
+
33
+ loop.on(AgentLoopType.AGENT_EVENT, (event: AgentEvent) => {
34
+ switch (event.type) {
35
+ case AgentEventType.START:
36
+ console.log("\n--- Agent Started ---");
37
+ lastPrintedToolCalls.clear();
38
+ break;
39
+ case AgentEventType.MESSAGE:
40
+ if (event.role === "agent" && event.parts) {
41
+ for (const part of event.parts) {
42
+ if ("text" in part && part.text) {
43
+ process.stdout.write(part.text);
44
+ }
45
+ }
46
+ }
47
+ break;
48
+ case AgentEventType.END:
49
+ console.log("\n--- Agent Ended ---");
50
+ console.log(`Reason: ${event.reason}`);
51
+ break;
52
+ case AgentEventType.ERROR:
53
+ console.error(`\nAgent Error: ${event.errorMessage}`);
54
+ break;
55
+ case AgentEventType.TOOL_CALL:
56
+ const contentStr = JSON.stringify({ name: event.name, args: event.args });
57
+ if (lastPrintedToolCalls.get(event.requestId) === contentStr) {
58
+ break;
59
+ }
60
+ lastPrintedToolCalls.set(event.requestId, contentStr);
61
+
62
+ console.log(`\n[Tool Call: ${event.name}]`);
63
+ break;
64
+ case AgentEventType.TOOL_RESPONSE:
65
+ console.log(`\n[Tool Response: ${event.name}]`);
66
+ break;
67
+ default:
68
+ break;
69
+ }
70
+ });
71
+
72
+ console.log(`\nPrompt: ${prompt}`);
73
+ await loop.run(prompt);
74
+ }