@germanescobar/anita 0.3.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 (66) hide show
  1. package/README.md +353 -0
  2. package/dist/agent/agents.d.ts +16 -0
  3. package/dist/agent/agents.js +115 -0
  4. package/dist/agent/context-budget.d.ts +7 -0
  5. package/dist/agent/context-budget.js +17 -0
  6. package/dist/agent/context-builder.d.ts +34 -0
  7. package/dist/agent/context-builder.js +175 -0
  8. package/dist/agent/executor.d.ts +13 -0
  9. package/dist/agent/executor.js +65 -0
  10. package/dist/agent/loop.d.ts +54 -0
  11. package/dist/agent/loop.js +548 -0
  12. package/dist/agent/policies.d.ts +25 -0
  13. package/dist/agent/policies.js +177 -0
  14. package/dist/agent/session.d.ts +12 -0
  15. package/dist/agent/session.js +42 -0
  16. package/dist/attachments.d.ts +3 -0
  17. package/dist/attachments.js +73 -0
  18. package/dist/cli/index.d.ts +5 -0
  19. package/dist/cli/index.js +327 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +4 -0
  22. package/dist/models/anthropic.d.ts +15 -0
  23. package/dist/models/anthropic.js +195 -0
  24. package/dist/models/openai-responses.d.ts +62 -0
  25. package/dist/models/openai-responses.js +377 -0
  26. package/dist/models/openai.d.ts +32 -0
  27. package/dist/models/openai.js +330 -0
  28. package/dist/models/provider.d.ts +33 -0
  29. package/dist/models/provider.js +1 -0
  30. package/dist/models/resolve.d.ts +48 -0
  31. package/dist/models/resolve.js +211 -0
  32. package/dist/security/sensitive-content.d.ts +6 -0
  33. package/dist/security/sensitive-content.js +59 -0
  34. package/dist/skills/skills.d.ts +62 -0
  35. package/dist/skills/skills.js +371 -0
  36. package/dist/storage/event-store.d.ts +7 -0
  37. package/dist/storage/event-store.js +36 -0
  38. package/dist/storage/session-store.d.ts +11 -0
  39. package/dist/storage/session-store.js +64 -0
  40. package/dist/tools/delete-file.d.ts +2 -0
  41. package/dist/tools/delete-file.js +25 -0
  42. package/dist/tools/edit-file.d.ts +2 -0
  43. package/dist/tools/edit-file.js +50 -0
  44. package/dist/tools/read-file.d.ts +2 -0
  45. package/dist/tools/read-file.js +122 -0
  46. package/dist/tools/registry.d.ts +9 -0
  47. package/dist/tools/registry.js +122 -0
  48. package/dist/tools/run-command.d.ts +2 -0
  49. package/dist/tools/run-command.js +103 -0
  50. package/dist/tools/write-file.d.ts +2 -0
  51. package/dist/tools/write-file.js +29 -0
  52. package/dist/types/agent.d.ts +44 -0
  53. package/dist/types/agent.js +1 -0
  54. package/dist/types/conversation.d.ts +43 -0
  55. package/dist/types/conversation.js +201 -0
  56. package/dist/types/events.d.ts +8 -0
  57. package/dist/types/events.js +1 -0
  58. package/dist/types/messages.d.ts +39 -0
  59. package/dist/types/messages.js +1 -0
  60. package/dist/types/output.d.ts +19 -0
  61. package/dist/types/output.js +1 -0
  62. package/dist/types/stream.d.ts +55 -0
  63. package/dist/types/stream.js +1 -0
  64. package/dist/types/tools.d.ts +28 -0
  65. package/dist/types/tools.js +1 -0
  66. package/package.json +45 -0
@@ -0,0 +1,175 @@
1
+ import { exec } from "node:child_process";
2
+ import { formatSkillsForPrompt } from "../skills/skills.js";
3
+ import { formatAgentInstructionsForPrompt, loadAgentInstructions, } from "./agents.js";
4
+ const STATIC_SYSTEM_PROMPT = `You are Anita, a coding agent.
5
+
6
+ You have these tools available:
7
+ - read_file: Read file contents
8
+ - write_file: Create or overwrite a file
9
+ - edit_file: Replace a specific string in a file (read the file first)
10
+ - delete_file: Delete a file
11
+ - run_command: Run a shell command
12
+
13
+ Instructions:
14
+ - Use tools to explore and understand the codebase before making changes
15
+ - Prefer rg for repository text searches when available; fall back to grep only when rg is unavailable
16
+ - Always read a file before editing it
17
+ - Run tests or checks after making changes when appropriate
18
+ - Follow the instructions of the user without jumping ahead.
19
+ - Do not print or publish secrets, credentials, tokens, private keys, environment values, or other sensitive data
20
+ - Explain what you are doing briefly`;
21
+ export class ContextBuilder {
22
+ workingDirectory;
23
+ runtimeContext;
24
+ constructor(workingDirectory, runtimeContext = {}) {
25
+ this.workingDirectory = workingDirectory;
26
+ this.runtimeContext = runtimeContext;
27
+ }
28
+ buildSystemPrompt() {
29
+ const skillsSection = formatSkillsForPrompt(this.runtimeContext.skills ?? []);
30
+ const agentInstructionsSection = formatAgentInstructionsForPrompt(loadAgentInstructions({
31
+ cwd: this.workingDirectory,
32
+ homeDir: this.runtimeContext.agentsHomeDir,
33
+ repositoryRoot: this.runtimeContext.agentsRepositoryRoot,
34
+ }));
35
+ return (STATIC_SYSTEM_PROMPT +
36
+ agentInstructionsSection +
37
+ this.buildUserSystemPromptSection() +
38
+ skillsSection +
39
+ this.buildEnvironmentFooter());
40
+ }
41
+ buildUserSystemPromptSection() {
42
+ const systemPrompt = this.runtimeContext.systemPrompt?.trim();
43
+ if (!systemPrompt)
44
+ return "";
45
+ return [
46
+ "\nAdditional system prompt from --system-prompt:",
47
+ "These instructions are subordinate to Anita's built-in safety rules, user instructions, and tool permission policy.",
48
+ systemPrompt,
49
+ "",
50
+ ].join("\n");
51
+ }
52
+ buildEnvironmentFooter() {
53
+ const now = new Date();
54
+ const year = now.getFullYear();
55
+ const month = String(now.getMonth() + 1).padStart(2, "0");
56
+ const day = String(now.getDate()).padStart(2, "0");
57
+ const date = `${year}-${month}-${day}`;
58
+ const cwd = this.workingDirectory.replace(/\\/g, "/");
59
+ return `\nCurrent date: ${date}\nCurrent working directory: ${cwd}`;
60
+ }
61
+ async buildDynamicContext() {
62
+ const gitContext = await this.getGitContext();
63
+ const environmentContext = this.buildEnvironmentContext();
64
+ const policyContext = this.buildPolicyContext();
65
+ return [
66
+ "Runtime context for the assistant. This is not a user request:",
67
+ environmentContext,
68
+ policyContext,
69
+ gitContext,
70
+ ]
71
+ .filter(Boolean)
72
+ .join("\n\n");
73
+ }
74
+ async buildMessagesWithDynamicContext(messages) {
75
+ const dynamicContext = await this.buildDynamicContext();
76
+ if (!dynamicContext)
77
+ return messages;
78
+ return [
79
+ {
80
+ role: "user",
81
+ content: [{ type: "text", text: dynamicContext }],
82
+ },
83
+ ...messages,
84
+ ];
85
+ }
86
+ async buildItemsWithDynamicContext(items) {
87
+ const dynamicContext = await this.buildDynamicContext();
88
+ if (!dynamicContext)
89
+ return items;
90
+ return [
91
+ {
92
+ type: "message",
93
+ role: "user",
94
+ content: dynamicContext,
95
+ },
96
+ ...items,
97
+ ];
98
+ }
99
+ buildEnvironmentContext() {
100
+ const shell = this.runtimeContext.shell ?? process.env.SHELL ?? "unknown";
101
+ const approvalMode = this.runtimeContext.approvalMode ?? "prompt";
102
+ const networkAccess = this.runtimeContext.networkAccess ?? "unknown";
103
+ const writeScope = this.runtimeContext.writeScope ??
104
+ "No explicit write scope is configured; use the working directory unless the user asks otherwise.";
105
+ return [
106
+ "Runtime:",
107
+ `Working directory: ${this.workingDirectory}`,
108
+ `Shell: ${shell}`,
109
+ `Approval mode: ${this.describeApprovalMode(approvalMode)}`,
110
+ `Network access: ${this.describeNetworkAccess(networkAccess)}`,
111
+ `Write scope: ${writeScope}`,
112
+ ].join("\n");
113
+ }
114
+ buildPolicyContext() {
115
+ const policyContext = this.runtimeContext.policyContext;
116
+ if (!policyContext)
117
+ return "";
118
+ return [
119
+ "Permission policy:",
120
+ `Default decision: ${this.describeDecision(policyContext.defaultDecision)}`,
121
+ ...policyContext.rules.map((rule) => `- ${rule.toolName}: ${this.describeDecision(rule.decision)} - ${rule.description}`),
122
+ ].join("\n");
123
+ }
124
+ describeApprovalMode(approvalMode) {
125
+ if (approvalMode === "auto") {
126
+ return "auto-approve policy decisions that request approval";
127
+ }
128
+ return "prompt before executing tool calls that require approval";
129
+ }
130
+ describeNetworkAccess(networkAccess) {
131
+ switch (networkAccess) {
132
+ case "available":
133
+ return "available";
134
+ case "unavailable":
135
+ return "unavailable";
136
+ case "unknown":
137
+ return "not declared; verify before relying on network access";
138
+ }
139
+ }
140
+ describeDecision(decision) {
141
+ switch (decision) {
142
+ case "allow":
143
+ return "allowed";
144
+ case "deny":
145
+ return "denied";
146
+ case "ask":
147
+ return "approval required";
148
+ }
149
+ }
150
+ async getGitContext() {
151
+ const isGit = await this.runQuiet("git rev-parse --is-inside-work-tree");
152
+ if (!isGit)
153
+ return "";
154
+ const [branch, status, diffStat] = await Promise.all([
155
+ this.runQuiet("git branch --show-current"),
156
+ this.runQuiet("git status --short"),
157
+ this.runQuiet("git diff --stat"),
158
+ ]);
159
+ const parts = ["Git context:"];
160
+ if (branch)
161
+ parts.push(`Branch: ${branch}`);
162
+ if (status)
163
+ parts.push(`Status:\n${status}`);
164
+ if (diffStat)
165
+ parts.push(`Diff:\n${diffStat}`);
166
+ return parts.join("\n");
167
+ }
168
+ runQuiet(cmd) {
169
+ return new Promise((resolve) => {
170
+ exec(cmd, { cwd: this.workingDirectory, timeout: 5000 }, (err, stdout) => {
171
+ resolve(err ? "" : stdout.trim());
172
+ });
173
+ });
174
+ }
175
+ }
@@ -0,0 +1,13 @@
1
+ import type { ToolCall, ToolExecuteOptions, ToolResult } from "../types/tools.js";
2
+ import type { ToolRegistry } from "../tools/registry.js";
3
+ import type { EventStore } from "../storage/event-store.js";
4
+ import type { PolicyEngine } from "./policies.js";
5
+ export type ApprovalCallback = (toolName: string, input: Record<string, unknown>) => Promise<boolean>;
6
+ export declare class Executor {
7
+ private registry;
8
+ private policyEngine;
9
+ private eventStore;
10
+ private approvalCallback;
11
+ constructor(registry: ToolRegistry, policyEngine: PolicyEngine, eventStore: EventStore, approvalCallback: ApprovalCallback);
12
+ executeTool(sessionId: string, toolCall: ToolCall, options?: ToolExecuteOptions): Promise<ToolResult>;
13
+ }
@@ -0,0 +1,65 @@
1
+ export class Executor {
2
+ registry;
3
+ policyEngine;
4
+ eventStore;
5
+ approvalCallback;
6
+ constructor(registry, policyEngine, eventStore, approvalCallback) {
7
+ this.registry = registry;
8
+ this.policyEngine = policyEngine;
9
+ this.eventStore = eventStore;
10
+ this.approvalCallback = approvalCallback;
11
+ }
12
+ async executeTool(sessionId, toolCall, options) {
13
+ const validationError = this.registry.validateInput(toolCall.name, toolCall.input);
14
+ if (validationError) {
15
+ await this.eventStore.append(sessionId, "tool_call", {
16
+ id: toolCall.id,
17
+ tool: toolCall.name,
18
+ input: toolCall.input,
19
+ });
20
+ await this.eventStore.append(sessionId, "tool_result", {
21
+ toolCallId: toolCall.id,
22
+ tool: toolCall.name,
23
+ content: validationError.content.slice(0, 2000),
24
+ isError: true,
25
+ metadata: validationError.metadata,
26
+ });
27
+ return validationError;
28
+ }
29
+ const decision = this.policyEngine.evaluate(toolCall.name, toolCall.input);
30
+ await this.eventStore.append(sessionId, "policy_decision", {
31
+ tool: toolCall.name,
32
+ input: toolCall.input,
33
+ decision,
34
+ });
35
+ if (decision === "deny") {
36
+ return {
37
+ content: `Tool "${toolCall.name}" was denied by policy.`,
38
+ isError: true,
39
+ };
40
+ }
41
+ if (decision === "ask") {
42
+ const approved = await this.approvalCallback(toolCall.name, toolCall.input);
43
+ if (!approved) {
44
+ return {
45
+ content: `Tool "${toolCall.name}" was denied by user.`,
46
+ isError: true,
47
+ };
48
+ }
49
+ }
50
+ await this.eventStore.append(sessionId, "tool_call", {
51
+ id: toolCall.id,
52
+ tool: toolCall.name,
53
+ input: toolCall.input,
54
+ });
55
+ const result = await this.registry.execute(toolCall.name, toolCall.input, options);
56
+ await this.eventStore.append(sessionId, "tool_result", {
57
+ toolCallId: toolCall.id,
58
+ tool: toolCall.name,
59
+ content: result.content.slice(0, 2000), // truncate for event log
60
+ isError: result.isError,
61
+ metadata: result.metadata,
62
+ });
63
+ return result;
64
+ }
65
+ }
@@ -0,0 +1,54 @@
1
+ import type { ModelProvider } from "../models/provider.js";
2
+ import type { ToolRegistry } from "../tools/registry.js";
3
+ import type { EventStore } from "../storage/event-store.js";
4
+ import type { SessionStore } from "../storage/session-store.js";
5
+ import type { SessionState } from "../types/agent.js";
6
+ import type { AttachmentContentBlock } from "../types/messages.js";
7
+ import { ContextBuilder } from "./context-builder.js";
8
+ import { Executor } from "./executor.js";
9
+ export declare const DEFAULT_CONTEXT_BUDGET: ContextBudgetOptions;
10
+ export interface ContextBudgetOptions {
11
+ compactAtRatio: number;
12
+ reservedResponseTokens: number;
13
+ keepRecentTokens: number;
14
+ minSummarizableTokens: number;
15
+ targetSummaryTokens: number;
16
+ }
17
+ export declare class AgentLoop {
18
+ private provider;
19
+ private executor;
20
+ private contextBuilder;
21
+ private registry;
22
+ private eventStore;
23
+ private sessionStore;
24
+ private streamJson;
25
+ private contextBudget;
26
+ private pendingTerminalDelta;
27
+ constructor(provider: ModelProvider, executor: Executor, contextBuilder: ContextBuilder, registry: ToolRegistry, eventStore: EventStore, sessionStore: SessionStore, streamJson?: boolean, contextBudget?: ContextBudgetOptions);
28
+ run(session: SessionState, userMessage: string, attachments?: AttachmentContentBlock[], signal?: AbortSignal): Promise<void>;
29
+ /**
30
+ * Record a coherent cancellation: persist the session at its last consistent
31
+ * point (no partial assistant turn or tool batch is appended), log the event,
32
+ * and notify the stream.
33
+ */
34
+ private handleCancellation;
35
+ private generateTitle;
36
+ private saveSession;
37
+ private buildModelContextItems;
38
+ private updateContextBudget;
39
+ private estimateConversationTokens;
40
+ private generateCompactionSummary;
41
+ private buildCompactedItems;
42
+ private findRecentTailSplitIndex;
43
+ private getCompactionThresholdTokens;
44
+ private appendCompactionSkipEvent;
45
+ private findSafeCompactionSplitIndex;
46
+ private startsInsideFunctionBatch;
47
+ private isFunctionBatchItem;
48
+ private createErrorToolResult;
49
+ private normalizeSession;
50
+ private getModelResponse;
51
+ private emitModelStreamEvent;
52
+ private emit;
53
+ private finishPendingTerminalDelta;
54
+ }