astrabot 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 (47) hide show
  1. package/README.md +411 -0
  2. package/ai/ai.config.ts +27 -0
  3. package/ai/auto-retry.ts +117 -0
  4. package/ai/config-loader.ts +132 -0
  5. package/ai/index.ts +4 -0
  6. package/ai/retry-prompt.ts +30 -0
  7. package/bin/astra +2 -0
  8. package/core/retry/error-classifier.ts +208 -0
  9. package/core/retry/index.ts +29 -0
  10. package/core/retry/retry-config.ts +142 -0
  11. package/core/retry/retry-engine.ts +215 -0
  12. package/game/index.html +573 -0
  13. package/game/neon-breaker.html +1037 -0
  14. package/index.ts +140 -0
  15. package/modes/agent/action-tracker.ts +47 -0
  16. package/modes/agent/agent-tools.ts +338 -0
  17. package/modes/agent/approval.ts +184 -0
  18. package/modes/agent/diff-view.ts +34 -0
  19. package/modes/agent/orchestrator.ts +234 -0
  20. package/modes/agent/tool-executor.ts +993 -0
  21. package/modes/agent/types.ts +68 -0
  22. package/modes/ask/orchestrator.ts +230 -0
  23. package/modes/auto.ts +88 -0
  24. package/modes/cli.ts +43 -0
  25. package/modes/multi/agent-pool-manager.ts +337 -0
  26. package/modes/multi/examples.ts +441 -0
  27. package/modes/multi/message-broker.ts +179 -0
  28. package/modes/multi/multi-agent-orchestrator.ts +891 -0
  29. package/modes/multi/orchestrator.ts +414 -0
  30. package/modes/multi/types.ts +245 -0
  31. package/modes/multi/workflow-builder.ts +569 -0
  32. package/modes/plan/orchestrator.ts +198 -0
  33. package/modes/plan/planner.ts +121 -0
  34. package/modes/plan/selection.ts +43 -0
  35. package/modes/plan/types.ts +13 -0
  36. package/modes/plan/web-tools.ts +132 -0
  37. package/modes/setup.ts +210 -0
  38. package/package.json +62 -0
  39. package/session/index.ts +45 -0
  40. package/session/session-context.ts +188 -0
  41. package/session/session-manager.ts +374 -0
  42. package/session/session-tools.ts +109 -0
  43. package/session/store.ts +278 -0
  44. package/tsconfig.json +30 -0
  45. package/tui/spinner.ts +182 -0
  46. package/tui/terminal-md.ts +17 -0
  47. package/tui/wakeup.ts +231 -0
package/README.md ADDED
@@ -0,0 +1,411 @@
1
+ # Astra
2
+
3
+ **AI-native development companion — agentic coding in your terminal.**
4
+
5
+ Astra gives a Large Language Model full programmatic access to your filesystem, shell, and the web — all gated behind a staging-first approval pipeline that keeps you in control. Built on [Bun](https://bun.sh), powered by [OpenRouter](https://openrouter.ai), and driven by the [Vercel AI SDK](https://sdk.vercel.ai).
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ - [Features](#features)
12
+ - [Quick Start](#quick-start)
13
+ - [Commands](#commands)
14
+ - [Interaction Modes](#interaction-modes)
15
+ - [Auto Mode](#auto-mode)
16
+ - [Agent Mode](#agent-mode)
17
+ - [Ask Mode](#ask-mode)
18
+ - [Plan Mode](#plan-mode)
19
+ - [Multi-Agent Mode](#multi-agent-mode)
20
+ - [Architecture](#architecture)
21
+ - [Core Components](#core-components)
22
+ - [Tool System](#tool-system)
23
+ - [Staging & Approval Pipeline](#staging--approval-pipeline)
24
+ - [Action Tracking](#action-tracking)
25
+ - [Session Management](#session-management)
26
+ - [Auto-Retry Engine](#auto-retry-engine)
27
+ - [Environment Variables](#environment-variables)
28
+ - [Project Structure](#project-structure)
29
+ - [Dependencies](#dependencies)
30
+ - [Roadmap](#roadmap)
31
+ - [License](#license)
32
+
33
+ ---
34
+
35
+ ## Features
36
+
37
+ - **Five interaction modes** — Auto, Agent, Ask, Plan, and Multi-Agent, each tailored to a different development workflow
38
+ - **Auto-router** — classifies user intent and routes to the correct mode automatically
39
+ - **Full filesystem access** — read, create, modify, and delete files and directories through an AI agent, all via an in-memory staging overlay
40
+ - **Shell execution** — queue arbitrary shell commands for agent-driven workflows (sync and background)
41
+ - **Git integration** — `git status`, `git diff`, and `git log` tools for repository awareness
42
+ - **Project-aware tooling** — run tests, linting, formatting, and framework detection by reading `package.json`
43
+ - **Web research** — built-in web search (via DuckDuckGo or Firecrawl), URL crawling, and HTTP fetching
44
+ - **Staging-first mutations** — no file is ever written or deleted without explicit user approval; all changes are staged in memory and presented for review before apply
45
+ - **Per-file diff review** — granular approval flow with unified diffs so you can inspect exactly what changed
46
+ - **Skill system** — discover and load `SKILL.md` files from Cursor and Claude skill directories, plus custom directories via `SKILLS_DIRS`
47
+ - **Session management** — sessions are persisted to disk with context summaries, enabling resumption after interruption
48
+ - **Auto-retry engine** — exponential backoff with jitter for resilient AI provider calls
49
+ - **Configurable safety** — exclude patterns, file size limits, and per-tool permission toggles per agent role
50
+ - **Rich terminal UI** — interactive prompts via `@clack/prompts`, markdown rendering, ASCII banner on startup, animated spinners, and colored logging
51
+
52
+ ---
53
+
54
+ ## Quick Start
55
+
56
+ ### Prerequisites
57
+
58
+ | Requirement | Version | Purpose |
59
+ |-------------|---------|---------|
60
+ | [Bun](https://bun.sh) | >=1.0.0 | Runtime and package manager |
61
+ | [OpenRouter](https://openrouter.ai) API key | — | LLM provider access (required) |
62
+ | [Firecrawl](https://www.firecrawl.dev/) API key | — | Web search and crawling (optional) |
63
+
64
+ ### Installation
65
+
66
+ ```bash
67
+ git clone <repository-url>
68
+ cd astra
69
+ bun install
70
+ ```
71
+
72
+ ### Configuration
73
+
74
+ Run the interactive setup wizard:
75
+
76
+ ```bash
77
+ bun run index.ts setup
78
+ ```
79
+
80
+ This creates `~/.astra/.env` with your API keys and preferred model. Alternatively, create the file manually:
81
+
82
+ ```env
83
+ OPENROUTER_API_KEY=sk-or-...
84
+ OPENROUTER_DEFAULT_MODEL=anthropic/claude-sonnet-4.5
85
+ FIRECRAWL_API_KEY=fc-... # optional
86
+ SKILLS_DIRS=/path/to/skills # optional
87
+ ```
88
+
89
+ ### Launch
90
+
91
+ ```bash
92
+ bun run index.ts wakeup
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Commands
98
+
99
+ | Command | Description |
100
+ |---------|-------------|
101
+ | `astra wakeup` | Display the banner and pick an interaction mode |
102
+ | `astra setup` | Interactive configuration wizard for `~/.astra/.env` |
103
+ | `astra play` | Launch the arcade mini-game (Snake or Neon Breaker) |
104
+ | `astra reset` | Purge all stored configurations, sessions, and credentials |
105
+ | `astra --version` | Print the current version |
106
+
107
+ ---
108
+
109
+ ## Interaction Modes
110
+
111
+ ### Auto Mode
112
+
113
+ Auto mode classifies your natural-language request and routes it to the correct downstream mode (Agent, Ask, Plan, or Multi-Agent) automatically.
114
+
115
+ ```
116
+ You: "fix the bug in store.ts"
117
+ → Router classifies as "agent" → executes Agent Mode
118
+
119
+ You: "explain how this app works"
120
+ → Router classifies as "ask" → executes Ask Mode
121
+ ```
122
+
123
+ **How it works:**
124
+
125
+ 1. You type any request
126
+ 2. An LLM classifies the intent into one of four categories
127
+ 3. The session is logged with the routing decision
128
+ 4. The selected mode executes with your original prompt
129
+
130
+ ### Agent Mode
131
+
132
+ The primary autonomous coding mode. You describe a goal, and the agent iteratively uses its toolset — reading files, writing code, running shell commands — to accomplish it.
133
+
134
+ **Flow:**
135
+
136
+ 1. You provide a natural-language goal
137
+ 2. The agent enters a tool loop (up to 50 steps), calling tools to explore and modify the codebase
138
+ 3. Each tool call is logged and displayed in real time
139
+ 4. After the agent finishes, the **approval flow** presents all staged changes grouped by file
140
+ 5. You can **accept all**, **review individually** (with diffs), or **cancel**
141
+ 6. Approved changes are applied to disk; rejected changes are discarded
142
+
143
+ **Available tools (35+):**
144
+
145
+ | Category | Tools |
146
+ |----------|-------|
147
+ | Filesystem | `read_file`, `create_file`, `modify_file`, `delete_file`, `create_folder`, `list_files`, `search_files`, `read_multiple_files`, `replace_in_file`, `append_to_file`, `insert_at_line` |
148
+ | Shell | `run_command`, `run_background_command`, `execute_shell` |
149
+ | Git | `git_status`, `git_diff`, `git_log` |
150
+ | Project | `run_tests`, `run_test_file`, `lint_project`, `format_project`, `detect_framework`, `read_package_json`, `analyze_codebase` |
151
+ | Search | `grep` |
152
+ | Web | `web_search`, `fetch_url`, `web_crawl` (requires Firecrawl) |
153
+ | Skills | `list_skills`, `read_skill` |
154
+ | Session | `session_status`, `session_history` |
155
+ | Planning | `create_plan`, `get_plan`, `show_pending_changes`, `discard_changes` |
156
+
157
+ ### Ask Mode
158
+
159
+ A **read-only** Q&A interface. The agent can explore your codebase and the web to answer questions, but cannot modify files (with the optional exception of saving the response).
160
+
161
+ **Flow:**
162
+
163
+ 1. You ask a question
164
+ 2. The agent uses read-only tools to formulate an answer
165
+ 3. The answer is rendered as markdown in the terminal
166
+ 4. You're given the option to save the Q&A pair as a `.md` file
167
+
168
+ **Available tools:** All read-only tools from Agent mode (filesystem read, search, codebase analysis, git, web, skills, session). All mutation tools are stripped.
169
+
170
+ ### Plan Mode
171
+
172
+ Breaks a high-level goal into a structured, executable plan. The agent researches your codebase, generates a step-by-step plan, and you select which steps to execute.
173
+
174
+ **Flow:**
175
+
176
+ 1. You describe a high-level goal
177
+ 2. The agent researches the codebase and generates a structured plan (1–20 steps) with complexity ratings (`low`, `medium`, `high`)
178
+ 3. The plan is displayed with a research summary and numbered steps
179
+ 4. You select which steps to execute (all selected by default)
180
+ 5. Each selected step runs as an independent agent loop (up to 50 steps per step)
181
+ 6. All mutations across all steps are collected and presented in a single approval flow
182
+
183
+ ### Multi-Agent Mode
184
+
185
+ Coordinates multiple AI agents working together on complex workflows.
186
+
187
+ **Workflow selection:**
188
+
189
+ - **Use predefined template** — choose from 6 templates:
190
+
191
+ | Template | Agents | Strategy |
192
+ |----------|--------|----------|
193
+ | Code Review | Researcher → Implementer → Reviewer | Sequential |
194
+ | Feature Development | Coordinator → Backend Dev + Frontend Dev → QA | Hierarchical |
195
+ | Bug Fix | Debug Agent → Fix Agent → Test Agent | Sequential |
196
+ | Collaborative Research | Researcher 1 + Researcher 2 + Researcher 3 | Parallel |
197
+ | Security Audit | Scanner → Analyzer → Reporter | Sequential |
198
+ | Full-Stack Feature | Database + API + UI (parallel) | Hierarchical |
199
+
200
+ - **AI-smart build** — the LLM analyzes your goal and automatically designs a custom agent topology with the optimal strategy and role assignments
201
+
202
+ **Orchestration strategies:**
203
+
204
+ | Strategy | Behavior |
205
+ |----------|----------|
206
+ | **Sequential** | Agents run one after another; each agent's output is visible to subsequent agents |
207
+ | **Parallel** | Agents run concurrently in batches (default 3 at a time) |
208
+ | **Hierarchical** | Coordinator runs first (planning), then specialists execute |
209
+ | **Collaborative** | Agents take turns; outputs are broadcast via a message broker |
210
+
211
+ **Agent roles and permissions:**
212
+
213
+ | Role | Permissions | Default Max Steps | Tools |
214
+ |------|------------|-------------------|-------|
215
+ | Researcher | Read-only | 30 | 16 |
216
+ | Implementer | Full read/write/execute | 50 | 26 |
217
+ | Reviewer | Read + execute (no write) | 25 | 15 |
218
+ | Coordinator | Read-only + planning | 20 | 8 |
219
+ | Custom | Based on selected tools | 30 | Variable |
220
+
221
+ ---
222
+
223
+ ## Architecture
224
+
225
+ ### Core Components
226
+
227
+ | File | Responsibility |
228
+ |------|----------------|
229
+ | `index.ts` | Entry point; registers commands via Commander |
230
+ | `tui/wakeup.ts` | Banner rendering and top-level mode selection |
231
+ | `tui/terminal-md.ts` | Markdown-to-terminal rendering via `marked` + `marked-terminal` |
232
+ | `tui/spinner.ts` | Animated spinner with elapsed time display |
233
+ | `modes/cli.ts` | CLI mode loop (Auto / Agent / Plan / Ask / Multi-Agent) |
234
+ | `modes/auto.ts` | Auto-router: LLM-based intent classification |
235
+ | `modes/setup.ts` | Interactive configuration wizard |
236
+ | `ai/ai.config.ts` | OpenRouter provider initialization |
237
+ | `ai/config-loader.ts` | Manages `~/.astra/.env` file |
238
+ | `ai/auto-retry.ts` | Automatic retry with exponential backoff |
239
+ | `ai/retry-prompt.ts` | Manual retry prompt fallback |
240
+
241
+ ### Tool System
242
+
243
+ The tool system has two layers:
244
+
245
+ 1. **`ToolExecutor`** (`modes/agent/tool-executor.ts`) — The core execution engine. All filesystem operations, shell commands, and skill lookups are implemented here. Mutations are staged in an in-memory overlay and never touch disk until explicitly approved.
246
+
247
+ 2. **`createAgentTools()`** (`modes/agent/agent-tools.ts`) — Wraps every `ToolExecutor` method as a Vercel AI SDK `tool()` with a Zod input schema, making them available to the LLM agent.
248
+
249
+ Additional tool sets:
250
+ - **`createWebTools()`** (`modes/plan/web-tools.ts`) — Firecrawl-based web search, crawl, and fetch tools
251
+ - **`createSessionTools()`** (`session/session-tools.ts`) — `session_status` and `session_history` tools injected into every agent
252
+
253
+ ### Staging & Approval Pipeline
254
+
255
+ This is the safety backbone of Astra. No mutation ever touches disk without explicit consent.
256
+
257
+ **Phase 1 — Staging:** When the agent calls a mutation tool, the `ToolExecutor` validates path safety (must be within workspace root, not excluded), records the operation in an in-memory overlay, and logs it to the `ActionTracker` with status `"pending"`.
258
+
259
+ **Phase 2 — Approval:** After the agent completes, all pending mutations are grouped by file path and presented to the user with three options:
260
+ - **Approve and apply all** — marks every pending mutation as approved
261
+ - **Review one by one** — iterates through each group, showing a unified diff and prompting for accept/reject
262
+ - **Cancel** — rejects all pending mutations
263
+
264
+ **Phase 3 — Application:** Approved actions are replayed against the real filesystem: folders are created, files are written or deleted, and shell commands are spawned.
265
+
266
+ ### Action Tracking
267
+
268
+ The `ActionTracker` maintains an append-only log of every action the agent takes. Each entry includes a unique ID, timestamp, action type, file path, before/after content snapshots, and status (`pending`, `executed`, `approved`, `rejected`). This log powers the approval flow, enables auditability, and supports future undo/redo features.
269
+
270
+ ### Session Management
271
+
272
+ Sessions are stored in `~/.astra/sessions/index.json` with atomic writes (temp file + rename). Each session records the workspace path, mode, status, LLM-generated summary, touched files, and action counts. Sessions support:
273
+
274
+ - **Auto-resume** — interrupted sessions are detected on wakeup and offered for resumption
275
+ - **Context injection** — on resume, the previous session's summary is injected into the agent's instructions
276
+ - **Session tools** — agents can call `session_status` and `session_history` to recall previous work
277
+
278
+ ### Auto-Retry Engine
279
+
280
+ Built-in retry logic with exponential backoff, jitter, and error classification. Four presets are available:
281
+
282
+ | Preset | Max Retries | Base Delay | Max Delay | Use Case |
283
+ |--------|-------------|------------|-----------|----------|
284
+ | `aiCall` | 3 | 1s | 30s | AI provider API calls |
285
+ | `toolExecution` | 2 | 500ms | 5s | Tool execution |
286
+ | `network` | 5 | 2s | 60s | Network operations |
287
+ | `critical` | 5 | 1s | 60s | Critical operations |
288
+
289
+ Errors are classified by category (rate limit, network, timeout, server, etc.) with per-category retryability and suggested delay overrides.
290
+
291
+ ---
292
+
293
+ ## Environment Variables
294
+
295
+ | Variable | Required | Description |
296
+ |----------|----------|-------------|
297
+ | `OPENROUTER_API_KEY` | Yes | OpenRouter API key for LLM access |
298
+ | `OPENROUTER_DEFAULT_MODEL` | Yes | Model identifier (e.g., `anthropic/claude-sonnet-4.5`) |
299
+ | `FIRECRAWL_API_KEY` | No | Enables `web_search`, `web_crawl`, and `fetch_url` via Firecrawl SDK |
300
+ | `SKILLS_DIRS` | No | Semicolon-separated paths to additional skill directories |
301
+
302
+ Config file location: `~/.astra/.env`
303
+
304
+ ---
305
+
306
+ ## Project Structure
307
+
308
+ ```
309
+ astra/
310
+ ├── index.ts # CLI entry point (Commander)
311
+ ├── package.json # Dependencies, scripts, bin config
312
+ ├── tsconfig.json # TypeScript config (strict, Bun types)
313
+
314
+ ├── ai/ # AI provider configuration
315
+ │ ├── index.ts # Re-exports getAgentModel
316
+ │ ├── ai.config.ts # OpenRouter provider setup
317
+ │ ├── config-loader.ts # ~/.astra/.env management
318
+ │ ├── auto-retry.ts # Automatic retry integration
319
+ │ └── retry-prompt.ts # Manual retry prompt fallback
320
+
321
+ ├── tui/ # Terminal UI
322
+ │ ├── terminal-md.ts # Markdown → terminal rendering
323
+ │ ├── spinner.ts # Animated spinner with elapsed time
324
+ │ └── wakeup.ts # Banner + top-level mode selection
325
+
326
+ ├── modes/ # Interaction modes
327
+ │ ├── cli.ts # CLI mode loop
328
+ │ ├── auto.ts # Auto-router (intent classification)
329
+ │ ├── setup.ts # Configuration wizard
330
+ │ │
331
+ │ ├── agent/ # Agent mode
332
+ │ │ ├── types.ts # Type definitions + default config
333
+ │ │ ├── action-tracker.ts # Append-only action log
334
+ │ │ ├── tool-executor.ts # Core execution engine + staging overlay
335
+ │ │ ├── agent-tools.ts # Vercel AI SDK tool definitions
336
+ │ │ ├── diff-view.ts # Unified diff generation
337
+ │ │ ├── approval.ts # Interactive approval flow
338
+ │ │ └── orchestrator.ts # Agent loop + approval + apply
339
+ │ │
340
+ │ ├── ask/ # Ask mode (read-only Q&A)
341
+ │ │ └── orchestrator.ts
342
+ │ │
343
+ │ ├── plan/ # Plan mode
344
+ │ │ ├── types.ts # Plan and PlanStep interfaces
345
+ │ │ ├── planner.ts # LLM-driven plan generation
346
+ │ │ ├── selection.ts # Interactive step picker
347
+ │ │ ├── web-tools.ts # Firecrawl web tools
348
+ │ │ └── orchestrator.ts # Plan → select → execute → approve
349
+ │ │
350
+ │ └── multi/ # Multi-agent mode
351
+ │ ├── types.ts # Full type system
352
+ │ ├── agent-pool-manager.ts # Agent registration and tracking
353
+ │ ├── message-broker.ts # Pub-sub communication channel
354
+ │ ├── multi-agent-orchestrator.ts # Strategy dispatch engine
355
+ │ ├── workflow-builder.ts # Fluent API + predefined templates
356
+ │ ├── examples.ts # Example workflow configurations
357
+ │ └── orchestrator.ts # Multi-agent approval flow
358
+
359
+ ├── session/ # Session persistence
360
+ │ ├── index.ts # Public API re-exports
361
+ │ ├── store.ts # JSON file store (atomic writes)
362
+ │ ├── session-manager.ts # Begin/end/resume/summarise
363
+ │ ├── session-context.ts # Context capture and summary
364
+ │ └── session-tools.ts # session_status + session_history tools
365
+
366
+ ├── core/ # Core utilities
367
+ │ └── retry/ # Retry engine
368
+ │ ├── index.ts # Public API re-exports
369
+ │ ├── retry-config.ts # Configuration and presets
370
+ │ ├── retry-engine.ts # Execution with backoff + jitter
371
+ │ └── error-classifier.ts # Error categorisation
372
+
373
+ └── game/ # Standalone arcade games
374
+ ├── index.html # Snake (HTML5 Canvas)
375
+ └── neon-breaker.html # Brick Breaker
376
+ ```
377
+
378
+ ---
379
+
380
+ ## Dependencies
381
+
382
+ | Package | Purpose |
383
+ |---------|---------|
384
+ | `@openrouter/ai-sdk-provider` | OpenRouter as LLM provider for Vercel AI SDK |
385
+ | `@clack/prompts` | Interactive terminal prompts |
386
+ | `ai` | Vercel AI SDK (ToolLoopAgent, generateText, stepCountIs) |
387
+ | `@mendable/firecrawl-js` | Web search, crawling, and scraping |
388
+ | `commander` | CLI argument parsing |
389
+ | `chalk` | Terminal string styling |
390
+ | `figlet` | ASCII art banner generation |
391
+ | `marked` + `marked-terminal` | Markdown parsing and terminal rendering |
392
+ | `diff` | Unified diff generation |
393
+ | `dotenv` | `.env` file loading |
394
+ | `zod` | Schema validation |
395
+
396
+ ---
397
+
398
+ ## Roadmap
399
+
400
+ - [ ] **Telegram mode** — stub present in wakeup menu
401
+ - [ ] **Undo/redo support** — via action log replay
402
+ - [ ] **Streaming token output** — for real-time agent response display
403
+ - [ ] **Configurable tool allowlists per mode** — currently hardcoded per mode
404
+ - [ ] **Multi-model support with per-mode model selection** — partially implemented in multi-agent
405
+ - [ ] **Persistent action history across sessions** — sessions store summaries but not full action logs
406
+
407
+ ---
408
+
409
+ ## License
410
+
411
+ MIT
@@ -0,0 +1,27 @@
1
+ import { createOpenRouter } from "@openrouter/ai-sdk-provider";
2
+ import { getEnv,getConfigPath } from "./config-loader";
3
+
4
+ export function getAgentModel() {
5
+ const apiKey = getEnv("OPENROUTER_API_KEY");
6
+ const modelId = getEnv("OPENROUTER_DEFAULT_MODEL");
7
+
8
+ if (!apiKey) {
9
+ throw new Error(
10
+ `OPENROUTER_API_KEY is not set.` +
11
+ `\n Run "astra setup" to configure your keys, or set the env var.` +
12
+ `\n Config file: ${getConfigPath()}`
13
+ );
14
+ }
15
+
16
+ if (!modelId) {
17
+ throw new Error(
18
+ `OPENROUTER_DEFAULT_MODEL is not set.` +
19
+ `\n Run "astra setup" to configure your keys, or set the env var.` +
20
+ `\n Config file: ${getConfigPath()}`
21
+ );
22
+ }
23
+
24
+ const provider = createOpenRouter({ apiKey });
25
+
26
+ return provider(modelId);
27
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Automatic Retry Module for AI Calls
3
+ *
4
+ * Provides seamless integration of retry logic with the existing
5
+ * AI provider calls, replacing the manual retry prompt with
6
+ * automatic retry capability.
7
+ */
8
+
9
+ import chalk from 'chalk';
10
+ import { withRetry, RetryPresets } from '../core/retry';
11
+ import type { RetryConfig } from '../core/retry';
12
+
13
+ /**
14
+ * Configuration for AI call retries
15
+ */
16
+ export interface AiRetryConfig {
17
+ /** Enable automatic retry (default: true) */
18
+ enabled: boolean;
19
+ /** Retry configuration */
20
+ retryConfig: Partial<RetryConfig>;
21
+ /** Show retry progress to user */
22
+ showProgress: boolean;
23
+ /** Ask user before retrying (fallback to manual mode) */
24
+ askBeforeRetry: boolean;
25
+ }
26
+
27
+ /**
28
+ * Default AI retry configuration
29
+ */
30
+ export const DEFAULT_AI_RETRY_CONFIG: AiRetryConfig = {
31
+ enabled: true,
32
+ retryConfig: RetryPresets.aiCall,
33
+ showProgress: true,
34
+ askBeforeRetry: false,
35
+ };
36
+
37
+ /**
38
+ * Execute an AI call with automatic retry
39
+ */
40
+ export async function withAiRetry<T>(
41
+ operation: () => Promise<T>,
42
+ context: string,
43
+ config: Partial<AiRetryConfig> = {},
44
+ ): Promise<T> {
45
+ const fullConfig = {
46
+ ...DEFAULT_AI_RETRY_CONFIG,
47
+ ...config,
48
+ retryConfig: {
49
+ ...DEFAULT_AI_RETRY_CONFIG.retryConfig,
50
+ ...config.retryConfig,
51
+ },
52
+ };
53
+
54
+ if (!fullConfig.enabled) {
55
+ return operation();
56
+ }
57
+
58
+ const { showProgress } = fullConfig;
59
+
60
+ try {
61
+ const { result, stats } = await withRetry(
62
+ operation,
63
+ {
64
+ ...fullConfig.retryConfig,
65
+ onRetry: (attempt, error, delayMs) => {
66
+ if (showProgress) {
67
+ console.log(
68
+ chalk.yellow(`\n ⚠ Retry ${attempt}/${fullConfig.retryConfig.maxRetries} after ${error.category} error`)
69
+ );
70
+ console.log(chalk.dim(` Waiting ${Math.round(delayMs / 1000)}s before retry...`));
71
+ }
72
+ },
73
+ onExhausted: (error, totalAttempts) => {
74
+ if (showProgress) {
75
+ console.log(
76
+ chalk.red(`\n ✗ All ${totalAttempts} attempts failed (${error.category})`)
77
+ );
78
+ }
79
+ },
80
+ },
81
+ );
82
+
83
+ if (showProgress && stats.totalRetries > 0) {
84
+ console.log(
85
+ chalk.green(` ✓ Succeeded after ${stats.totalAttempts} attempt(s)`)
86
+ );
87
+ }
88
+
89
+ return result;
90
+ } catch (error) {
91
+ // If automatic retry is enabled but failed, optionally fall back to manual retry
92
+ if (fullConfig.askBeforeRetry) {
93
+ const { promptToRetryAiCall } = await import('./retry-prompt');
94
+ const shouldRetry = await promptToRetryAiCall(context, error);
95
+ if (shouldRetry) {
96
+ return withAiRetry(operation, context, {
97
+ ...config,
98
+ askBeforeRetry: false, // Prevent infinite loop
99
+ });
100
+ }
101
+ }
102
+ throw error;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Create a retryable version of an AI call function
108
+ */
109
+ export function createRetryableAiCall<TArgs extends unknown[], TReturn>(
110
+ fn: (...args: TArgs) => Promise<TReturn>,
111
+ context: string,
112
+ config: Partial<AiRetryConfig> = {},
113
+ ): (...args: TArgs) => Promise<TReturn> {
114
+ return async (...args: TArgs): Promise<TReturn> => {
115
+ return withAiRetry(() => fn(...args), context, config);
116
+ };
117
+ }
@@ -0,0 +1,132 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { homedir } from "os";
4
+ import dotenv from "dotenv";
5
+
6
+ const CONFIG_DIR = path.join(homedir(), ".astra");
7
+ const CONFIG_FILE = path.join(CONFIG_DIR, ".env");
8
+
9
+ let loaded = false;
10
+
11
+ /**
12
+ * Ensure the ~/.astra/.env file is loaded into process.env.
13
+ * Only runs once; subsequent calls are a no-op.
14
+ */
15
+ function ensureConfigLoaded(): void {
16
+ if (loaded) return;
17
+
18
+ if (fs.existsSync(CONFIG_FILE)) {
19
+ dotenv.config({ path: CONFIG_FILE, override: false });
20
+ }
21
+
22
+ loaded = true;
23
+ }
24
+
25
+ /**
26
+ * Resolve an environment variable, checking process.env first,
27
+ * then the config file (which is already merged into process.env).
28
+ */
29
+ export function getEnv(key: string): string | undefined {
30
+ ensureConfigLoaded();
31
+ return process.env[key];
32
+ }
33
+
34
+ /**
35
+ * Return the path to the config file.
36
+ */
37
+ export function getConfigPath(): string {
38
+ return CONFIG_FILE;
39
+ }
40
+
41
+ /**
42
+ * Return the path to the config directory.
43
+ */
44
+ export function getConfigDir(): string {
45
+ return CONFIG_DIR;
46
+ }
47
+
48
+ /**
49
+ * Write a key=value pair into the config file, creating the directory
50
+ * if it doesn't exist. Overwrites existing values for the same key.
51
+ */
52
+ export function saveConfig(entries: Record<string, string>): void {
53
+ // Read existing content so we can merge
54
+ let existing = "";
55
+ if (fs.existsSync(CONFIG_FILE)) {
56
+ existing = fs.readFileSync(CONFIG_FILE, "utf8");
57
+ }
58
+
59
+ const lines = existing.split("\n");
60
+ const keys = new Set(Object.keys(entries));
61
+
62
+ // Update existing lines that match a key
63
+ const updated: string[] = [];
64
+ for (const line of lines) {
65
+ const trimmed = line.trim();
66
+ if (!trimmed || trimmed.startsWith("#")) {
67
+ updated.push(line);
68
+ continue;
69
+ }
70
+ const eqIdx = trimmed.indexOf("=");
71
+ if (eqIdx === -1) {
72
+ updated.push(line);
73
+ continue;
74
+ }
75
+ const key = trimmed.slice(0, eqIdx).trim();
76
+ if (keys.has(key)) {
77
+ updated.push(`${key}=${entries[key]}`);
78
+ keys.delete(key);
79
+ } else {
80
+ updated.push(line);
81
+ }
82
+ }
83
+
84
+ // Append any keys that weren't already present
85
+ for (const [key, value] of Object.entries(entries)) {
86
+ if (value) {
87
+ updated.push(`${key}=${value}`);
88
+ }
89
+ }
90
+
91
+ // Ensure directory exists
92
+ if (!fs.existsSync(CONFIG_DIR)) {
93
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
94
+ }
95
+
96
+ fs.writeFileSync(CONFIG_FILE, updated.join("\n") + "\n", "utf8");
97
+
98
+ // Also inject into current process so the user doesn't have to restart
99
+ ensureConfigLoaded();
100
+ }
101
+
102
+ // ─── Retry Configuration ───────────────────────────────────────────────────
103
+
104
+ /**
105
+ * Get retry configuration from environment
106
+ */
107
+ export function getRetryConfig(): {
108
+ enabled: boolean;
109
+ maxRetries: number;
110
+ showProgress: boolean;
111
+ } {
112
+ return {
113
+ enabled: getEnv("ASTRA_AGENT_RETRY_ENABLED") !== "false",
114
+ maxRetries: parseInt(getEnv("ASTRA_AGENT_RETRY_MAX") || "3", 10),
115
+ showProgress: getEnv("ASTRA_AGENT_RETRY_PROGRESS") !== "false",
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Get multi-agent retry configuration
121
+ */
122
+ export function getMultiRetryConfig(): {
123
+ enabled: boolean;
124
+ maxRetries: number;
125
+ backoffMultiplier: number;
126
+ } {
127
+ return {
128
+ enabled: getEnv("ASTRA_MULTI_RETRY_ENABLED") !== "false",
129
+ maxRetries: parseInt(getEnv("ASTRA_MULTI_RETRY_MAX") || "2", 10),
130
+ backoffMultiplier: parseInt(getEnv("ASTRA_MULTI_RETRY_BACKOFF") || "2", 10),
131
+ };
132
+ }
package/ai/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export {getAgentModel} from "./ai.config.ts";
2
+ export { withAiRetry, createRetryableAiCall, DEFAULT_AI_RETRY_CONFIG } from "./auto-retry";
3
+ export type { AiRetryConfig } from "./auto-retry";
4
+ export { getRetryConfig, getMultiRetryConfig } from "./config-loader";