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
@@ -0,0 +1,34 @@
1
+ import { createTwoFilesPatch } from "diff";
2
+ import chalk from "chalk";
3
+ import type { ActionLog } from "./types";
4
+
5
+ const MAX_DIFF_DISPLAY_LINES = 120;
6
+
7
+ export function formatPatch(filePath: string, before: string, after: string): string {
8
+ const rawPatch = createTwoFilesPatch(filePath, filePath, before, after, "", "", { context: 3 });
9
+ const lines = rawPatch.split("\n");
10
+
11
+ if (lines.length > MAX_DIFF_DISPLAY_LINES) {
12
+ const truncated = lines.slice(0, MAX_DIFF_DISPLAY_LINES);
13
+ truncated.push(
14
+ chalk.yellow(
15
+ `\n[... Diff truncated for readability: total ${lines.length} lines. Use 'Review one by one' option to step through safely ...]`
16
+ )
17
+ );
18
+ return truncated.join("\n");
19
+ }
20
+
21
+ return rawPatch;
22
+ }
23
+
24
+ export function composeBeforeAfter(sorted: ActionLog[]): {
25
+ before: string;
26
+ after: string;
27
+ } {
28
+ const first = sorted[0]!;
29
+ const last = sorted[sorted.length - 1]!;
30
+ if (last.type === "file_delete") return { before: last.details.before ?? "", after: "" };
31
+ const before = first.type === "file_create" ? "" : (first.details.before ?? "");
32
+ const after = last.details.after ?? "";
33
+ return { before, after };
34
+ }
@@ -0,0 +1,234 @@
1
+ import { isCancel, text } from "@clack/prompts";
2
+ import chalk from "chalk";
3
+ import { defaultAgentConfig } from "./types";
4
+ import { ActionTracker } from "./action-tracker";
5
+ import { ToolExecutor } from "./tool-executor";
6
+ import { createAgentTools } from "./agent-tools";
7
+ import { stepCountIs, ToolLoopAgent } from "ai";
8
+ import { getAgentModel, withAiRetry, getRetryConfig } from "../../ai";
9
+ import { renderTerminalMarkdown } from "../../tui/terminal-md";
10
+ import { runApprovalFlow } from "./approval";
11
+ import { withSpinner } from "../../tui/spinner";
12
+ import {
13
+ beginSession,
14
+ endSession,
15
+ markSessionInterrupted,
16
+ formatSessionLine,
17
+ readSessionActions, // Added history loading endpoint hook
18
+ } from "../../session";
19
+ import { createSessionTools } from "../../session/session-tools";
20
+ import { promptToRetryAiCall } from "../../ai/retry-prompt";
21
+
22
+ export async function runAgentMode(preCapturedGoal?: string) {
23
+ console.log(chalk.bold("\n Agent mode\n"));
24
+
25
+ // If Auto Mode already collected the goal, skip the prompt
26
+ const goal = preCapturedGoal ?? await text({
27
+ message: "What would you like the agent to do for you?",
28
+ placeholder: "Concrete task for this codebase...",
29
+ });
30
+
31
+ if (isCancel(goal) || !goal.trim()) return;
32
+
33
+ const config = defaultAgentConfig();
34
+ const tracker = new ActionTracker();
35
+ const executor = new ToolExecutor(tracker, config);
36
+
37
+ const approveCreatedFile = async (filePath: string): Promise<string> => {
38
+ const ok = await runApprovalFlow(tracker, {
39
+ paths: [filePath],
40
+ skipBatchPrompt: true,
41
+ });
42
+
43
+ if (!ok) {
44
+ executor.discardStagedPath(filePath);
45
+ return `User rejected creating ${filePath}. Do not modify or rely on this file unless you recreate it later.`;
46
+ }
47
+
48
+ const { errors } = executor.applyApprovedFromTracker();
49
+ if (errors.length) {
50
+ executor.discardStagedPath(filePath);
51
+ throw new Error(
52
+ `Failed to apply approved file ${filePath}: ${errors.join("; ")}`,
53
+ );
54
+ }
55
+
56
+ return `Created and applied ${filePath} after user approval.`;
57
+ };
58
+
59
+ const resumeId = (globalThis as any).__ASTRA_RESUME_SESSION__ as
60
+ | string
61
+ | undefined;
62
+ if (resumeId) delete (globalThis as any).__ASTRA_RESUME_SESSION__;
63
+
64
+ const { entry: sessionEntry, contextSummary } = beginSession({
65
+ workspacePath: config.codebasePath,
66
+ mode: "agent",
67
+ goal: goal.trim(),
68
+ resumeSessionId: resumeId,
69
+ });
70
+
71
+ if (resumeId) {
72
+ console.log(chalk.dim("\n Resuming previous session transaction history...\n"));
73
+ const historicActions = readSessionActions(resumeId);
74
+ if (historicActions.length > 0) {
75
+ // Re-hydrate the staging environment so it perfectly recreates uncommitted buffers
76
+ executor.hydrateFromActions(historicActions);
77
+ }
78
+ }
79
+
80
+ const tools = {
81
+ ...createAgentTools(executor, {
82
+ afterCreateFile: approveCreatedFile,
83
+ }),
84
+ ...createSessionTools(config.codebasePath),
85
+ };
86
+
87
+ const instructions = contextSummary
88
+ ? [
89
+ contextSummary,
90
+ `Workspace root: ${config.codebasePath}`,
91
+ "All mutations are staged until approval.",
92
+ "You have access to historical state updates loaded in the overlay loop.",
93
+ ].join("\n")
94
+ : [
95
+ `Workspace root: ${config.codebasePath}`,
96
+ "All mutations are staged until approval.",
97
+ ].join("\n");
98
+
99
+ const agent = new ToolLoopAgent({
100
+ model: getAgentModel(),
101
+ stopWhen: stepCountIs(50),
102
+ instructions,
103
+ tools,
104
+ });
105
+
106
+ let result;
107
+ const retryConfig = getRetryConfig();
108
+
109
+ try {
110
+ result = await withAiRetry(
111
+ () =>
112
+ withSpinner(
113
+ {
114
+ message: "Agent is working on your task...",
115
+ doneMessage: "done",
116
+ failMessage: "something went wrong",
117
+ },
118
+ () =>
119
+ agent.generate({
120
+ prompt: goal.trim(),
121
+ onStepFinish: ({ toolCalls }) => {
122
+ for (const tc of toolCalls) {
123
+ const preview = JSON.stringify(tc.input).slice(0, 160);
124
+ console.log(
125
+ chalk.green(" *"),
126
+ chalk.bold(String(tc.toolName)),
127
+ chalk.dim(
128
+ preview + (preview.length > 160 ? "..." : ""),
129
+ ),
130
+ );
131
+ }
132
+ },
133
+ }),
134
+ ),
135
+ "The agent hit a provider error.",
136
+ {
137
+ enabled: retryConfig.enabled,
138
+ retryConfig: {
139
+ maxRetries: retryConfig.maxRetries,
140
+ baseDelayMs: 1000,
141
+ maxDelayMs: 30000,
142
+ backoffMultiplier: 2,
143
+ jitter: true,
144
+ maxJitterMs: 1000,
145
+ version: 1,
146
+ } as any,
147
+ showProgress: retryConfig.showProgress,
148
+ askBeforeRetry: false,
149
+ },
150
+ );
151
+ } catch (error) {
152
+ const manualRetry = await promptToRetryAiCall(
153
+ "Automatic retries exhausted. Would you like to try once more?",
154
+ error,
155
+ );
156
+
157
+ if (manualRetry) {
158
+ try {
159
+ result = await withSpinner(
160
+ {
161
+ message: "Agent is working on your task...",
162
+ doneMessage: "done",
163
+ failMessage: "something went wrong",
164
+ },
165
+ () =>
166
+ agent.generate({
167
+ prompt: goal.trim(),
168
+ onStepFinish: ({ toolCalls }) => {
169
+ for (const tc of toolCalls) {
170
+ const preview = JSON.stringify(tc.input).slice(0, 160);
171
+ console.log(
172
+ chalk.green(" *"),
173
+ chalk.bold(String(tc.toolName)),
174
+ chalk.dim(
175
+ preview + (preview.length > 160 ? "..." : ""),
176
+ ),
177
+ );
178
+ }
179
+ },
180
+ }),
181
+ );
182
+ } catch (finalError) {
183
+ markSessionInterrupted(sessionEntry.id);
184
+ await endSession(
185
+ sessionEntry.id,
186
+ tracker,
187
+ "Stopped after final manual retry failed.",
188
+ );
189
+ executor.discardChanges();
190
+ return;
191
+ }
192
+ } else {
193
+ markSessionInterrupted(sessionEntry.id);
194
+ await endSession(
195
+ sessionEntry.id,
196
+ tracker,
197
+ "Stopped after AI provider error (all retries exhausted).",
198
+ );
199
+ executor.discardChanges();
200
+ return;
201
+ }
202
+ }
203
+
204
+ if (result.text.trim()) console.log(renderTerminalMarkdown(result.text));
205
+
206
+ const ok = await runApprovalFlow(tracker);
207
+ if (!ok) {
208
+ await endSession(sessionEntry.id, tracker, result.text || "(no response)");
209
+ executor.discardChanges();
210
+ return;
211
+ }
212
+
213
+ await withSpinner(
214
+ {
215
+ message: "Applying approved changes...",
216
+ doneMessage: "all changes applied",
217
+ failMessage: "some operations failed",
218
+ },
219
+ async () => {
220
+ const { errors } = executor.applyApprovedFromTracker();
221
+ if (errors.length) {
222
+ console.log(chalk.red("\nSome operations reported errors:\n"));
223
+ for (const e of errors) {
224
+ console.log(chalk.red(` - ${e}`));
225
+ }
226
+ } else {
227
+ console.log(chalk.green("\nApplied.\n"));
228
+ }
229
+ },
230
+ );
231
+
232
+ await endSession(sessionEntry.id, tracker, result.text || "(no response)");
233
+ executor.discardChanges();
234
+ }