agen-vektor 0.1.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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +212 -0
  3. package/dist/agent/agent.js +90 -0
  4. package/dist/agent/context.js +106 -0
  5. package/dist/agent/loop.js +152 -0
  6. package/dist/agent/memory.js +87 -0
  7. package/dist/agent/planner.js +38 -0
  8. package/dist/agent/prompts.js +41 -0
  9. package/dist/agent/session.js +125 -0
  10. package/dist/cli/index.js +304 -0
  11. package/dist/cli/keyboard.js +283 -0
  12. package/dist/config/config.js +92 -0
  13. package/dist/config/credentials.js +127 -0
  14. package/dist/config/models.js +47 -0
  15. package/dist/providers/anthropic.js +276 -0
  16. package/dist/providers/custom.js +24 -0
  17. package/dist/providers/factory.js +58 -0
  18. package/dist/providers/gemini.js +223 -0
  19. package/dist/providers/ollama.js +195 -0
  20. package/dist/providers/openai-compat.js +296 -0
  21. package/dist/providers/openai.js +17 -0
  22. package/dist/providers/openrouter.js +23 -0
  23. package/dist/providers/provider.js +39 -0
  24. package/dist/security/command-policy.js +99 -0
  25. package/dist/security/permissions.js +66 -0
  26. package/dist/tools/filesystem.js +297 -0
  27. package/dist/tools/git.js +66 -0
  28. package/dist/tools/registry.js +39 -0
  29. package/dist/tools/search.js +184 -0
  30. package/dist/tools/shell.js +108 -0
  31. package/dist/tools/web.js +76 -0
  32. package/dist/tui/app.js +913 -0
  33. package/dist/tui/chat.js +76 -0
  34. package/dist/tui/components.js +85 -0
  35. package/dist/tui/diff.js +105 -0
  36. package/dist/tui/input.js +121 -0
  37. package/dist/tui/statusbar.js +32 -0
  38. package/dist/utils/logger.js +96 -0
  39. package/dist/utils/paths.js +90 -0
  40. package/dist/utils/terminal.js +191 -0
  41. package/package.json +56 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VECTOR contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # VECTOR
2
+
3
+ **AI Coding Agent for Linux & Termux** — a terminal-native coding agent that inspects your project, plans, edits code, runs commands and tests, and iterates until the task is done.
4
+
5
+ ```
6
+ ╭──────────────────────────────────────────────────────────────╮
7
+ │ VECTOR ● Ready openrouter · deepseek/deepseek-chat │
8
+ │ Project: ~/my-project Mode: ask │
9
+ ├──────────────────────────────────────────────────────────────┤
10
+ │ │
11
+ │ YOU │
12
+ │ > Perbaiki error authentication pada project ini │
13
+ │ │
14
+ │ VECTOR │
15
+ │ ● Inspecting project... │
16
+ │ ● Searching authentication files │
17
+ │ │
18
+ │ TOOL │
19
+ │ $ npm test │
20
+ │ │
21
+ │ ✓ Tests passed │
22
+ │ │
23
+ ├──────────────────────────────────────────────────────────────┤
24
+ │ > _ │
25
+ ├──────────────────────────────────────────────────────────────┤
26
+ │ ENTER Send │ TAB Commands │ CTRL+C Stop │ ? Help │
27
+ ╰──────────────────────────────────────────────────────────────╯
28
+ ```
29
+
30
+ ## Features
31
+
32
+ - **Interactive TUI** — chat, agent status, tool execution, diff viewer, input. Keyboard driven, mouse-aware, works on 80×24 terminals and survives resize.
33
+ - **Agent loop** — plan → inspect → tool call → result → review → fix → retry until done (with an iteration cap).
34
+ - **Tools** — read/write/edit files, list directories, search files, run shell commands, git, fetch web pages.
35
+ - **Security first** — command policy classifies every shell command (SAFE / ASK / DANGEROUS / BLOCKED), permission prompts before risky actions, `--yolo` mode with warnings, and API keys are never printed or persisted to sessions.
36
+ - **Multi-provider** — OpenAI, Anthropic, Gemini, OpenRouter, Ollama, and any OpenAI-compatible custom endpoint.
37
+ - **Sessions & memory** — conversations persist to `~/.vector/sessions/`; `/continue` resumes them.
38
+ - **Context management** — only relevant context is sent to the model, with compaction (`/compact`).
39
+
40
+ ## Installation
41
+
42
+ Requirements: **Node.js 20+** and **npm** (Termux: `pkg install nodejs`).
43
+
44
+ ### From source
45
+
46
+ ```bash
47
+ git clone https://github.com/clickmamaheti-prog/vector-agent.git
48
+ cd vector-agent
49
+ npm install
50
+ npm run build
51
+ npm link # makes `vector` available on PATH
52
+ ```
53
+
54
+ Then:
55
+
56
+ ```bash
57
+ vector
58
+ ```
59
+
60
+ ### From npm (once published)
61
+
62
+ ```bash
63
+ npm install -g vector-cli
64
+ vector
65
+ ```
66
+
67
+ ### Termux (Android)
68
+
69
+ ```bash
70
+ pkg update && pkg upgrade
71
+ pkg install nodejs git
72
+ npm install -g vector-cli
73
+ vector
74
+ ```
75
+
76
+ > VECTOR has **zero runtime dependencies** and does not assume `systemd`, `sudo`, `apt`, or a desktop GUI — everything runs in a plain terminal.
77
+
78
+ ## Quick start
79
+
80
+ ```bash
81
+ cd my-project
82
+ vector "Perbaiki error authentication pada project ini"
83
+ ```
84
+
85
+ or start the TUI and type your task:
86
+
87
+ ```bash
88
+ vector
89
+ ```
90
+
91
+ ## Command line
92
+
93
+ | Command | Description |
94
+ |---|---|
95
+ | `vector` | Start the interactive terminal UI |
96
+ | `vector "task"` | Start TUI and run the task immediately |
97
+ | `vector --model MODEL` | Set the model |
98
+ | `vector --provider P` | Set provider: `openai`, `anthropic`, `gemini`, `openrouter`, `ollama`, `custom` |
99
+ | `vector --continue` | Continue the most recent session |
100
+ | `vector --session NAME` | Use/resume session `NAME` |
101
+ | `vector --plan` | Show a plan before executing |
102
+ | `vector --yolo` | Skip permission prompts for ask-level actions (with warning) |
103
+ | `vector --version` / `--help` | Version / help |
104
+
105
+ ## TUI commands
106
+
107
+ Type `/` inside VECTOR:
108
+
109
+ | Command | Description |
110
+ |---|---|
111
+ | `/model` | Select a model |
112
+ | `/provider` | Select a provider |
113
+ | `/session` | Browse saved sessions |
114
+ | `/settings` | Show configuration |
115
+ | `/status` | Show agent status |
116
+ | `/clear` | Clear the conversation |
117
+ | `/continue` | Continue the current/last session |
118
+ | `/diff` | Show diff viewer |
119
+ | `/git` | Run `git status` |
120
+ | `/compact` | Compact context (truncate old messages) |
121
+ | `/yolo` | Toggle permission mode |
122
+ | `/exit` | Quit |
123
+
124
+ ## Provider configuration
125
+
126
+ Set an API key via environment variable:
127
+
128
+ ```bash
129
+ export OPENAI_API_KEY=sk-...
130
+ export ANTHROPIC_API_KEY=sk-ant-...
131
+ export GEMINI_API_KEY=...
132
+ export OPENROUTER_API_KEY=...
133
+ ```
134
+
135
+ Or inside VECTOR: `/provider` → pick provider → paste key (stored masked in `~/.vector/credentials.json`, permissions `0600`).
136
+
137
+ Generic variables: `VECTOR_API_KEY`, `VECTOR_API_URL`, `VECTOR_MODEL`, `VECTOR_PROVIDER`, `VECTOR_HOME`.
138
+
139
+ ### Custom API (OpenAI-compatible)
140
+
141
+ ```bash
142
+ export VECTOR_API_URL=https://example.com/v1
143
+ export VECTOR_API_KEY=your-key
144
+ vector --provider custom --model deepseek-v3
145
+ ```
146
+
147
+ The provider speaks `/v1/chat/completions` (and `/models`). Anthropic and Gemini protocols for custom endpoints are documented as adapters; OpenAI-compatible endpoints are fully supported.
148
+
149
+ ## Security model
150
+
151
+ Every shell command is classified:
152
+
153
+ - **SAFE** — read-only commands run without prompting (`ls`, `cat`, `git status`, …)
154
+ - **ASK** — prompts for confirmation (`npm install`, `npm test`, `git commit`, …)
155
+ - **DANGEROUS** — prompts with a warning (`rm -rf`, `chmod`, `git push`, …)
156
+ - **BLOCKED** — refused outright (`rm -rf /`, `mkfs`, `shutdown`, forced pushes, …)
157
+
158
+ `--yolo` auto-approves ASK-level actions but still prompts for DANGEROUS and refuses BLOCKED ones. API keys are never printed to the terminal, logs, diffs, or session history.
159
+
160
+ ## Architecture
161
+
162
+ ```
163
+ src/
164
+ ├── cli/ argument parsing, keyboard, bootstrap
165
+ ├── tui/ terminal UI: chat, input, statusbar, diff, components
166
+ ├── agent/ agent loop, planner, context, memory, prompts, session
167
+ ├── providers/ AIProvider interface + OpenAI/Anthropic/Gemini/OpenRouter/Ollama/Custom
168
+ ├── tools/ tool registry + filesystem/shell/search/git/web
169
+ ├── security/ command policy + permission manager
170
+ ├── config/ config, credentials, model catalog
171
+ └── utils/ terminal (ANSI), logger, paths
172
+ ```
173
+
174
+ Providers and tools are pluggable: adding one never touches the agent core.
175
+
176
+ ## Development
177
+
178
+ ```bash
179
+ npm run dev # run from source with tsx
180
+ npm run build # compile TypeScript
181
+ npm test # run unit tests (node:test)
182
+ npm run typecheck # tsc --noEmit
183
+ ```
184
+
185
+ ## Testing
186
+
187
+ Unit tests cover providers, the agent loop, the tool registry, filesystem tools, the command policy, config, credentials, sessions, context manager, keyboard, diff, and CLI helpers.
188
+
189
+ ```bash
190
+ npm test
191
+ npm run build
192
+ ```
193
+
194
+ ## Troubleshooting
195
+
196
+ - **"No API key configured"** — set the relevant `*_API_KEY` env var or run `/provider` in the TUI.
197
+ - **"401" / auth failed** — check the key, or for OpenRouter use `OPENROUTER_API_KEY`.
198
+ - **Nothing happens on `vector` in Termux** — make sure your terminal is a TTY and TERM is set (e.g. `export TERM=xterm-256color`).
199
+ - **Runs out of iterations** — raise `maxIterations` in `~/.vector/config.json` or refine the request.
200
+ - **Rate limited (429)** — VECTOR retries automatically with backoff; wait and retry.
201
+
202
+ ## Roadmap
203
+
204
+ - Web search tool (provider-backed) behind the `web` abstraction
205
+ - Interactive diff approval before edits in safe mode
206
+ - Persistent permission rules per project
207
+ - `npm publish` as `vector-cli`
208
+ - Windows support (WSL-focused)
209
+
210
+ ## License
211
+
212
+ [MIT](LICENSE)
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Agent = void 0;
4
+ const factory_1 = require("../providers/factory");
5
+ const registry_1 = require("../tools/registry");
6
+ const filesystem_1 = require("../tools/filesystem");
7
+ const search_1 = require("../tools/search");
8
+ const shell_1 = require("../tools/shell");
9
+ const git_1 = require("../tools/git");
10
+ const web_1 = require("../tools/web");
11
+ const permissions_1 = require("../security/permissions");
12
+ const config_1 = require("../config/config");
13
+ const loop_1 = require("./loop");
14
+ class Agent {
15
+ config;
16
+ provider;
17
+ tools;
18
+ permissions;
19
+ cwd;
20
+ constructor(opts) {
21
+ this.config = (0, config_1.effectiveConfig)(opts.config);
22
+ this.cwd = opts.cwd || process.cwd();
23
+ this.provider = (0, factory_1.createProvider)(this.config);
24
+ this.permissions = new permissions_1.PermissionManager({
25
+ mode: this.config.permissionMode,
26
+ ask: opts.askPermission,
27
+ });
28
+ this.tools = new registry_1.ToolRegistry();
29
+ this.tools.registerMany([
30
+ ...(0, filesystem_1.createFilesystemTools)(),
31
+ ...(0, search_1.createSearchTools)(),
32
+ (0, shell_1.createShellTool)(),
33
+ (0, git_1.createGitTool)(),
34
+ (0, web_1.createWebTool)(),
35
+ ]);
36
+ }
37
+ async run(request, signal) {
38
+ return (0, loop_1.runAgentLoop)(request, {
39
+ provider: this.provider,
40
+ tools: this.tools,
41
+ permissions: this.permissions,
42
+ config: this.config,
43
+ cwd: this.cwd,
44
+ signal,
45
+ }, this.callbacks);
46
+ }
47
+ /**
48
+ * Run with optional prior session messages (non-interactive continue).
49
+ * If sessionMessages is provided, the task is appended after the history.
50
+ */
51
+ async runWithContext(request, session, cb = {}) {
52
+ this.setCallbacks(cb);
53
+ if (!session)
54
+ return this.run(request);
55
+ // Replay history into the loop by prefixing the task request.
56
+ const context = session.messages
57
+ .filter((m) => m.role === 'user' || m.role === 'assistant')
58
+ .slice(-30)
59
+ .map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content.slice(0, 2000)}`)
60
+ .join('\n\n');
61
+ return this.run(`[Previous conversation summary]\n${context}\n\n---\n\nTASK:\n${request}`);
62
+ }
63
+ /** Rebuild the provider (e.g. after changing provider id). */
64
+ rebuildProvider() {
65
+ this.provider = (0, factory_1.createProvider)(this.config);
66
+ }
67
+ callbacks = {};
68
+ setCallbacks(cb) {
69
+ this.callbacks = {
70
+ onStatus: cb.onStatus,
71
+ onDelta: cb.onDelta,
72
+ onToolCall: cb.onToolCall,
73
+ onToolResult: cb.onToolResult,
74
+ onIteration: cb.onIteration,
75
+ onFinal: cb.onFinal,
76
+ onError: cb.onError,
77
+ onActivity: cb.onActivity,
78
+ };
79
+ // Permissions callback must be live-updated as well
80
+ this.permissions.setMode(this.config.permissionMode);
81
+ }
82
+ setPermissionAsk(ask) {
83
+ this.permissions = new permissions_1.PermissionManager({ mode: this.config.permissionMode, ask });
84
+ }
85
+ setMode(mode) {
86
+ this.config.permissionMode = mode;
87
+ this.permissions.setMode(mode);
88
+ }
89
+ }
90
+ exports.Agent = Agent;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectProject = detectProject;
37
+ exports.buildProjectContext = buildProjectContext;
38
+ exports.listRootEntries = listRootEntries;
39
+ /**
40
+ * Context Manager — builds a compact, relevant project context.
41
+ *
42
+ * Strategy: never send the whole project. Detect the manifest/project type,
43
+ * read small key files, and keep a bounded message history.
44
+ */
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const MANIFESTS = [
48
+ { file: 'package.json', type: 'Node.js', maxBytes: 8000 },
49
+ { file: 'tsconfig.json', type: 'TypeScript', maxBytes: 8000 },
50
+ { file: 'requirements.txt', type: 'Python', maxBytes: 8000 },
51
+ { file: 'pyproject.toml', type: 'Python', maxBytes: 8000 },
52
+ { file: 'Cargo.toml', type: 'Rust', maxBytes: 8000 },
53
+ { file: 'go.mod', type: 'Go', maxBytes: 8000 },
54
+ { file: 'composer.json', type: 'PHP', maxBytes: 8000 },
55
+ { file: 'Makefile', type: 'Make', maxBytes: 8000 },
56
+ { file: 'pom.xml', type: 'Java', maxBytes: 8000 },
57
+ { file: 'Gemfile', type: 'Ruby', maxBytes: 8000 },
58
+ { file: 'mix.exs', type: 'Elixir', maxBytes: 8000 },
59
+ ];
60
+ function detectProject(root) {
61
+ const info = { root, type: null, manifest: null, hasGit: false };
62
+ info.hasGit = fs.existsSync(path.join(root, '.git'));
63
+ for (const m of MANIFESTS) {
64
+ const p = path.join(root, m.file);
65
+ try {
66
+ if (fs.existsSync(p)) {
67
+ const stat = fs.statSync(p);
68
+ if (stat.size > 0 && stat.size <= m.maxBytes) {
69
+ info.manifest = { name: m.file, content: fs.readFileSync(p, 'utf8') };
70
+ info.type = m.type;
71
+ break;
72
+ }
73
+ }
74
+ }
75
+ catch {
76
+ /* ignore */
77
+ }
78
+ }
79
+ return info;
80
+ }
81
+ function buildProjectContext(root) {
82
+ const info = detectProject(root);
83
+ const lines = [];
84
+ lines.push(`Project root: ${root}`);
85
+ lines.push(`Project type: ${info.type || 'unknown'}`);
86
+ lines.push(`Git repository: ${info.hasGit ? 'yes' : 'no'}`);
87
+ if (info.manifest) {
88
+ lines.push(`\n--- ${info.manifest.name} ---\n${info.manifest.content}`);
89
+ }
90
+ return lines.join('\n');
91
+ }
92
+ function listRootEntries(root, limit = 25) {
93
+ try {
94
+ const entries = fs.readdirSync(root, { withFileTypes: true });
95
+ const IGNORE = new Set(['.git', 'node_modules', 'dist', 'build', '.cache', '.env', '.next', 'vendor']);
96
+ const lines = entries
97
+ .filter((e) => !IGNORE.has(e.name) && !e.name.startsWith('.'))
98
+ .sort((a, b) => (a.isDirectory() === b.isDirectory() ? a.name.localeCompare(b.name) : a.isDirectory() ? -1 : 1))
99
+ .slice(0, limit)
100
+ .map((e) => (e.isDirectory() ? `${e.name}/` : e.name));
101
+ return lines.length > 0 ? `Top-level entries:\n${lines.join('\n')}` : '(empty directory)';
102
+ }
103
+ catch {
104
+ return '(cannot read directory)';
105
+ }
106
+ }
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runAgentLoop = runAgentLoop;
4
+ exports.compactMessages = compactMessages;
5
+ exports.estimateContextSize = estimateContextSize;
6
+ /**
7
+ * Agent loop — the core execution engine.
8
+ *
9
+ * USER PROMPT → context → LLM → tool call? → execute → result → LLM → ... → final
10
+ *
11
+ * The loop keeps going until the model produces a final answer (no tool
12
+ * calls) or the iteration limit is reached. Each tool result is fed back
13
+ * to the model so it can react, fix errors, and retry.
14
+ */
15
+ const provider_1 = require("../providers/provider");
16
+ const context_1 = require("./context");
17
+ const prompts_1 = require("./prompts");
18
+ async function runAgentLoop(userRequest, opts, callbacks = {}) {
19
+ const { provider, tools, permissions, config, cwd, signal } = opts;
20
+ const maxIterations = config.maxIterations;
21
+ // Seed conversation with system + project context + task
22
+ const messages = [
23
+ { role: 'system', content: prompts_1.SYSTEM_PROMPT },
24
+ {
25
+ role: 'user',
26
+ content: `${(0, context_1.buildProjectContext)(cwd)}\n\n${(0, context_1.listRootEntries)(cwd)}\n\n---\n\nTASK:\n${userRequest}`,
27
+ },
28
+ ];
29
+ let iterations = 0;
30
+ let toolCalls = 0;
31
+ let aborted = false;
32
+ let stopped = false;
33
+ let finalContent = '';
34
+ const checkAbort = () => {
35
+ if (signal?.aborted) {
36
+ aborted = true;
37
+ throw new Error('aborted');
38
+ }
39
+ };
40
+ while (iterations < maxIterations) {
41
+ try {
42
+ checkAbort();
43
+ }
44
+ catch {
45
+ aborted = true;
46
+ callbacks.onStatus?.('Stopped');
47
+ break;
48
+ }
49
+ iterations++;
50
+ callbacks.onIteration?.(iterations, maxIterations);
51
+ callbacks.onStatus?.(iterations === 1 ? 'Thinking' : 'Thinking…');
52
+ let result;
53
+ try {
54
+ result = await (0, provider_1.withRetry)(() => provider.chat({ messages, model: config.model, tools: tools.definitions(), signal }), { retries: config.maxRetries, signal });
55
+ }
56
+ catch (err) {
57
+ if (err.name === 'AbortError' || err.message === 'aborted') {
58
+ aborted = true;
59
+ callbacks.onStatus?.('Stopped');
60
+ break;
61
+ }
62
+ if (err instanceof provider_1.ProviderError && err.status === 401) {
63
+ callbacks.onError?.(new Error('API authentication failed (401). Check your API key.'));
64
+ callbacks.onStatus?.('Auth error');
65
+ stopped = true;
66
+ finalContent = `⚠️ API authentication failed. Run \`vector /provider\` to configure your API key.`;
67
+ break;
68
+ }
69
+ if (err instanceof provider_1.ProviderError && err.status === 429) {
70
+ callbacks.onError?.(new Error('Rate limited (429) by the provider.'));
71
+ callbacks.onStatus?.('Rate limited');
72
+ stopped = true;
73
+ finalContent = '⚠️ Rate limited by the provider. Wait a moment and try again.';
74
+ break;
75
+ }
76
+ callbacks.onError?.(err);
77
+ callbacks.onStatus?.('Error');
78
+ stopped = true;
79
+ finalContent = `⚠️ Error: ${err.message}`;
80
+ break;
81
+ }
82
+ const hasToolCalls = result.toolCalls.length > 0;
83
+ // Streaming-free path: collect the assistant message and execute tools.
84
+ messages.push({
85
+ role: 'assistant',
86
+ content: result.content,
87
+ toolCalls: result.toolCalls,
88
+ });
89
+ if (hasToolCalls) {
90
+ try {
91
+ for (const tc of result.toolCalls) {
92
+ checkAbort();
93
+ toolCalls++;
94
+ callbacks.onToolCall?.(tc.name, tc.arguments);
95
+ callbacks.onStatus?.(`Running ${tc.name}`);
96
+ const toolResult = await tools.execute(tc.name, tc.arguments, {
97
+ cwd,
98
+ permissions,
99
+ onActivity: callbacks.onActivity,
100
+ maxOutput: 30_000,
101
+ });
102
+ callbacks.onToolResult?.(tc.name, toolResult.output);
103
+ messages.push({
104
+ role: 'tool',
105
+ toolCallId: tc.id,
106
+ name: tc.name,
107
+ content: toolResult.output.slice(0, 40_000),
108
+ });
109
+ }
110
+ }
111
+ catch {
112
+ aborted = true;
113
+ callbacks.onStatus?.('Stopped');
114
+ break;
115
+ }
116
+ continue;
117
+ }
118
+ // No tool calls → final answer
119
+ finalContent = result.content;
120
+ callbacks.onStatus?.('Completed');
121
+ callbacks.onFinal?.(result);
122
+ return { content: finalContent, iterations, toolCalls, stopped, aborted, messages };
123
+ }
124
+ if (iterations >= maxIterations) {
125
+ callbacks.onStatus?.('Iteration limit');
126
+ if (!finalContent) {
127
+ finalContent = `⚠️ Reached the maximum of ${maxIterations} iterations without completing the task. Run /settings to raise the limit or refine the request.`;
128
+ }
129
+ }
130
+ return { content: finalContent, iterations, toolCalls, stopped, aborted, messages };
131
+ }
132
+ /** Compact conversation history into a summary to reduce token usage. */
133
+ function compactMessages(messages, summarize) {
134
+ return summarize(messages).then((summary) => [
135
+ { role: 'system', content: prompts_1.SYSTEM_PROMPT },
136
+ {
137
+ role: 'user',
138
+ content: `[Previous conversation summarized]\n${summary}`,
139
+ },
140
+ ]);
141
+ }
142
+ function estimateContextSize(messages) {
143
+ let total = 0;
144
+ for (const m of messages) {
145
+ total += m.content.length;
146
+ if (m.toolCalls) {
147
+ for (const tc of m.toolCalls)
148
+ total += tc.arguments.length;
149
+ }
150
+ }
151
+ return total;
152
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadMemory = loadMemory;
37
+ exports.saveNote = saveNote;
38
+ exports.getNote = getNote;
39
+ exports.allNotes = allNotes;
40
+ exports.projectKey = projectKey;
41
+ /**
42
+ * Memory — persistent key-value notes per project, stored in ~/.vector/memory/.
43
+ * The agent can save/load small facts across sessions (e.g. "tests run with npm test").
44
+ */
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const paths_1 = require("../utils/paths");
48
+ function fileFor(projectKey) {
49
+ return path.join((0, paths_1.getMemoryDir)(), `${(0, paths_1.sanitizeName)(projectKey)}.json`);
50
+ }
51
+ function loadMemory(projectKey) {
52
+ try {
53
+ const p = fileFor(projectKey);
54
+ if (fs.existsSync(p)) {
55
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
56
+ }
57
+ }
58
+ catch {
59
+ /* ignore */
60
+ }
61
+ return { notes: {}, updatedAt: new Date().toISOString() };
62
+ }
63
+ function saveNote(projectKey, key, value) {
64
+ const mem = loadMemory(projectKey);
65
+ mem.notes[key] = value;
66
+ mem.updatedAt = new Date().toISOString();
67
+ fs.mkdirSync((0, paths_1.getMemoryDir)(), { recursive: true });
68
+ fs.writeFileSync(fileFor(projectKey), JSON.stringify(mem, null, 2));
69
+ }
70
+ function getNote(projectKey, key) {
71
+ return loadMemory(projectKey).notes[key];
72
+ }
73
+ function allNotes(projectKey) {
74
+ return loadMemory(projectKey).notes;
75
+ }
76
+ /** Derive a stable project key from the project path. */
77
+ function projectKey(cwd) {
78
+ const base = path.basename(cwd) || 'root';
79
+ return base + '-' + hashStr(cwd).slice(0, 8);
80
+ }
81
+ function hashStr(s) {
82
+ let h = 0;
83
+ for (let i = 0; i < s.length; i++) {
84
+ h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
85
+ }
86
+ return Math.abs(h).toString(36);
87
+ }