erosolar-cli 1.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.
- package/ARCHITECTURE.json +157 -0
- package/Agents.md +207 -0
- package/LICENSE +21 -0
- package/SOURCE_OF_TRUTH.json +103 -0
- package/dist/adapters/browser/index.js +10 -0
- package/dist/adapters/node/index.js +34 -0
- package/dist/adapters/remote/index.js +19 -0
- package/dist/adapters/types.js +1 -0
- package/dist/bin/erosolar.js +6 -0
- package/dist/capabilities/bashCapability.js +23 -0
- package/dist/capabilities/filesystemCapability.js +23 -0
- package/dist/capabilities/index.js +3 -0
- package/dist/capabilities/searchCapability.js +23 -0
- package/dist/capabilities/tavilyCapability.js +26 -0
- package/dist/capabilities/toolRegistry.js +98 -0
- package/dist/config.js +60 -0
- package/dist/contracts/v1/agent.js +7 -0
- package/dist/contracts/v1/provider.js +6 -0
- package/dist/contracts/v1/tool.js +6 -0
- package/dist/core/agent.js +135 -0
- package/dist/core/agentProfiles.js +34 -0
- package/dist/core/contextWindow.js +29 -0
- package/dist/core/errors/apiKeyErrors.js +114 -0
- package/dist/core/preferences.js +157 -0
- package/dist/core/secretStore.js +143 -0
- package/dist/core/toolRuntime.js +180 -0
- package/dist/core/types.js +1 -0
- package/dist/plugins/providers/anthropic/index.js +24 -0
- package/dist/plugins/providers/deepseek/index.js +24 -0
- package/dist/plugins/providers/google/index.js +25 -0
- package/dist/plugins/providers/index.js +17 -0
- package/dist/plugins/providers/openai/index.js +25 -0
- package/dist/plugins/providers/xai/index.js +24 -0
- package/dist/plugins/tools/bash/localBashPlugin.js +13 -0
- package/dist/plugins/tools/filesystem/localFilesystemPlugin.js +13 -0
- package/dist/plugins/tools/index.js +2 -0
- package/dist/plugins/tools/nodeDefaults.js +16 -0
- package/dist/plugins/tools/registry.js +57 -0
- package/dist/plugins/tools/search/localSearchPlugin.js +13 -0
- package/dist/plugins/tools/tavily/tavilyPlugin.js +16 -0
- package/dist/providers/anthropicProvider.js +218 -0
- package/dist/providers/googleProvider.js +193 -0
- package/dist/providers/openaiChatCompletionsProvider.js +148 -0
- package/dist/providers/openaiResponsesProvider.js +182 -0
- package/dist/providers/providerFactory.js +21 -0
- package/dist/runtime/agentHost.js +152 -0
- package/dist/runtime/agentSession.js +65 -0
- package/dist/runtime/browser.js +9 -0
- package/dist/runtime/cloud.js +9 -0
- package/dist/runtime/node.js +10 -0
- package/dist/runtime/universal.js +28 -0
- package/dist/shell/__tests__/bracketedPasteManager.test.js +35 -0
- package/dist/shell/bracketedPasteManager.js +75 -0
- package/dist/shell/interactiveShell.js +1426 -0
- package/dist/shell/shellApp.js +392 -0
- package/dist/tools/bashTools.js +117 -0
- package/dist/tools/diffUtils.js +137 -0
- package/dist/tools/fileTools.js +232 -0
- package/dist/tools/searchTools.js +175 -0
- package/dist/tools/tavilyTools.js +176 -0
- package/dist/ui/__tests__/richText.test.js +36 -0
- package/dist/ui/codeHighlighter.js +843 -0
- package/dist/ui/designSystem.js +98 -0
- package/dist/ui/display.js +731 -0
- package/dist/ui/layout.js +108 -0
- package/dist/ui/richText.js +318 -0
- package/dist/ui/theme.js +91 -0
- package/dist/workspace.js +44 -0
- package/package.json +62 -0
- package/scripts/preinstall-clean-bins.mjs +66 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"document": {
|
|
3
|
+
"title": "erosolar Editing Architecture",
|
|
4
|
+
"schema": "architecture.v2",
|
|
5
|
+
"version": "2.0.0",
|
|
6
|
+
"lastUpdated": "2025-11-16",
|
|
7
|
+
"maintainer": "Minimal Erosolar Code Agent"
|
|
8
|
+
},
|
|
9
|
+
"editingNorthStar": "Every edit must cite deterministic workspace evidence so hallucinations cannot slip into patches or reviews.",
|
|
10
|
+
"hallucinationObjectives": [
|
|
11
|
+
{
|
|
12
|
+
"id": "lock_truth",
|
|
13
|
+
"statement": "Freeze the exact repo snapshot before editing begins so subsequent reasoning cannot invent files, paths, or metadata."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "minimize_context",
|
|
17
|
+
"statement": "Keep the prompt appendix to the essential tree + canonical docs so LLM context windows are not wasted on stale chatter."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "metadata_reentry",
|
|
21
|
+
"statement": "Expose metadata-only tools that replay the frozen snapshot verbatim whenever the model needs to re-ground itself mid-edit."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "operator_loop_supervision",
|
|
25
|
+
"statement": "Let the agent continue calling tools until it ships a cited answer while making it easy for humans to interrupt if it stalls."
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"knowledgeChannels": [
|
|
29
|
+
{
|
|
30
|
+
"id": "deterministic_snapshot",
|
|
31
|
+
"source": "src/workspace.ts",
|
|
32
|
+
"payload": "Depth-2 directory tree plus 2000-character excerpts from README.md, SOURCE_OF_TRUTH.json, ARCHITECTURE.json, and package.json.",
|
|
33
|
+
"purpose": "Provides the single editing reference frame shared by prompts and tools."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "prompt_appendix",
|
|
37
|
+
"source": "src/config.ts",
|
|
38
|
+
"payload": "Agent guardrails from Agents.md plus the frozen snapshot appended to the system prompt of both Erosolar Code and General agents.",
|
|
39
|
+
"purpose": "Bootstraps the very first editing turn with verifiable facts."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "metadata_tools",
|
|
43
|
+
"source": "src/core/toolRuntime.ts",
|
|
44
|
+
"payload": ["context_snapshot", "capabilities_overview", "profile_details"],
|
|
45
|
+
"purpose": "Replay the captured data verbatim mid-edit without re-reading the filesystem."
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"editingPipeline": [
|
|
49
|
+
{
|
|
50
|
+
"stage": "capture_lock",
|
|
51
|
+
"module": "src/workspace.ts",
|
|
52
|
+
"responsibility": "Scan the repo deterministically, drop noisy folders (.git, node_modules, dist), and output the frozen editing snapshot.",
|
|
53
|
+
"editingImpact": "Gives editing sessions a canonical baseline and surfaces an explicit error when capture fails."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"stage": "prompt_appendix",
|
|
57
|
+
"module": "src/config.ts",
|
|
58
|
+
"responsibility": "Resolve the active agent, merge env overrides, and glue the snapshot to the model system prompt.",
|
|
59
|
+
"editingImpact": "Keeps the first editing turn grounded and identical across providers."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"stage": "capability_composer",
|
|
63
|
+
"module": "src/runtime/agentHost.ts",
|
|
64
|
+
"responsibility": "Load capability modules (filesystem, bash, research connectors, MCP bridges) and emit the exact tool suites that enter the runtime.",
|
|
65
|
+
"editingImpact": "Decouples tooling from the UI so the same agent can run inside a CLI, browser worker, or cloud orchestrator without refactoring."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"stage": "metadata_runtime",
|
|
69
|
+
"module": "src/core/toolRuntime.ts",
|
|
70
|
+
"responsibility": "Register metadata-only tools that simply echo cached truth blobs.",
|
|
71
|
+
"editingImpact": "Lets the model rehearse facts repeatedly without risking hallucinated side effects."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"stage": "agent_loop",
|
|
75
|
+
"module": "src/core/agent.ts",
|
|
76
|
+
"responsibility": "Orchestrate prompts, respond to tool calls synchronously, and stream until the provider emits a final message.",
|
|
77
|
+
"editingImpact": "Allows legitimate multi-step edits to finish while keeping operators aware of every intermediate turn."
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"components": [
|
|
81
|
+
{
|
|
82
|
+
"id": "WorkspaceContext",
|
|
83
|
+
"file": "src/workspace.ts",
|
|
84
|
+
"responsibilities": [
|
|
85
|
+
"Produce a deterministic directory map capped at depth 2 / 200 entries.",
|
|
86
|
+
"Slice README.md, SOURCE_OF_TRUTH.json, ARCHITECTURE.json, and package.json to 2000 characters each.",
|
|
87
|
+
"Emit descriptive errors when capture cannot complete so the agent pauses editing until a new snapshot is available."
|
|
88
|
+
],
|
|
89
|
+
"editingBenefit": "The edit session can quote exact file paths and doc excerpts without guessing."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"id": "PromptAppendix",
|
|
93
|
+
"file": "src/config.ts",
|
|
94
|
+
"responsibilities": [
|
|
95
|
+
"Resolve provider + model for the general and Erosolar Code agents with safe env overrides.",
|
|
96
|
+
"Append the frozen snapshot to the guardrail text before the agent speaks.",
|
|
97
|
+
"Mirror prompts between providers so edits look identical regardless of backend."
|
|
98
|
+
],
|
|
99
|
+
"editingBenefit": "Both providers share the same edit-ready context, preventing provider-specific hallucinations."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "MetadataToolRuntime",
|
|
103
|
+
"file": "src/core/toolRuntime.ts",
|
|
104
|
+
"responsibilities": [
|
|
105
|
+
"Expose context_snapshot, capabilities_overview, and profile_details (see Agents.md) as immutable tools.",
|
|
106
|
+
"Serialize responses exactly as captured—no synthesis, no side effects.",
|
|
107
|
+
"Log tool start/result/error events for auditability."
|
|
108
|
+
],
|
|
109
|
+
"editingBenefit": "Editors can re-check facts mid-patch without touching the filesystem or adding new context noise."
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "AgentRuntimeController",
|
|
113
|
+
"file": "src/core/agent.ts",
|
|
114
|
+
"responsibilities": [
|
|
115
|
+
"Inject the prompt appendix, record conversation state, and gate tool responses.",
|
|
116
|
+
"Stream unlimited tool passes so complex edits can finish without artificial cutoffs.",
|
|
117
|
+
"Emit each assistant/tool turn so operators can interrupt and gather more evidence whenever the run stalls."
|
|
118
|
+
],
|
|
119
|
+
"editingBenefit": "Keeps humans informed without forcing premature stops during legitimate multi-step refactors."
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"id": "CapabilityHost",
|
|
123
|
+
"file": "src/runtime/agentHost.ts",
|
|
124
|
+
"responsibilities": [
|
|
125
|
+
"Compose CapabilityModule implementations into deterministic ToolSuite arrays before AgentSession spins up.",
|
|
126
|
+
"Track contribution metadata and enforce unique suite ids so multiple connectors (e.g., Google Sheets, MCP servers, or archive movers) can coexist.",
|
|
127
|
+
"Provide a deployment-agnostic manifest that any frontend (shell, browser, CI runner) can inspect to decide which modules are active."
|
|
128
|
+
],
|
|
129
|
+
"editingBenefit": "Operators can add or remove integrations without touching UI code, which keeps deployments reliable across different environments."
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"guardrails": [
|
|
133
|
+
{
|
|
134
|
+
"id": "snapshot_lock",
|
|
135
|
+
"statement": "No editing session proceeds without a freshly captured workspace context; failures raise blocking errors."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "appendix_required",
|
|
139
|
+
"statement": "System prompts for every agent must include the Agents.md guardrails + snapshot verbatim so every answer cites the same data."
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": "metadata_only_tools",
|
|
143
|
+
"statement": "Registered tools are limited to replaying captured metadata; runtime rejects side-effect handlers."
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"id": "manual_loop_supervision",
|
|
147
|
+
"statement": "Agent runs are no longer capped; humans stop the session themselves if the tool loop thrashes or lacks evidence, replacing the previous 6+6 pass guard that aborted speculative turns."
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"contextBudget": {
|
|
151
|
+
"treeDepth": 2,
|
|
152
|
+
"maxEntries": 200,
|
|
153
|
+
"documentExcerptLimit": 2000,
|
|
154
|
+
"excludedFolders": [".git", "node_modules", "dist"],
|
|
155
|
+
"comment": "Tool passes are now unlimited; appendix sizing stays the same so prompts remain deterministic."
|
|
156
|
+
}
|
|
157
|
+
}
|
package/Agents.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Agents (Optional)
|
|
2
|
+
|
|
3
|
+
This optional reference summarizes the agent personalities that ship with the erosolar CLI.
|
|
4
|
+
All executable defaults still live in `src/config.ts`; this document simply mirrors them so
|
|
5
|
+
operators can reason about the available choices without scanning TypeScript.
|
|
6
|
+
|
|
7
|
+
## Agent overview
|
|
8
|
+
|
|
9
|
+
| Agent | Provider | Default model | Primary intent |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| Erosolar | OpenAI | `gpt-5.1` | General-purpose reasoning across planning, writing, research, and coding. |
|
|
12
|
+
| Erosolar Code | OpenAI | `gpt-5.1-codex` | Fast editing feedback with deterministic grounding. |
|
|
13
|
+
|
|
14
|
+
## Erosolar agent (General)
|
|
15
|
+
|
|
16
|
+
- **Provider/model**: Uses the OpenAI SDK with `gpt-5.1` as the balanced default model.
|
|
17
|
+
- **System prompt**: Frames the assistant as a multi-domain operator that can plan, research, write, and code with equal rigor.
|
|
18
|
+
- **CLI entrypoint**: `erosolar` (package `bin` field) now boots the Erosolar Code profile; select this general agent via `/agents`, `--profile=general`, or by setting it as the default.
|
|
19
|
+
- **Tool stack**: This profile is slated to inherit the broader research+planning tool bundle once it is finalized, so keep it handy for those workflows.
|
|
20
|
+
- **Ideal for**: General operations where you need a mix of reasoning, writing, and software edits without switching personas.
|
|
21
|
+
- **Model presets**: Switch between `gpt-5.1` (default), `gpt-5.1-codex`, or any other registered preset via `/model`.
|
|
22
|
+
- **Agent selector**: Run `/agents` in the CLI to make Erosolar or any other profile the default for future launches.
|
|
23
|
+
|
|
24
|
+
## Erosolar Code agent (OpenAI)
|
|
25
|
+
|
|
26
|
+
- **Provider/model**: Uses the OpenAI SDK with `gpt-5.1-codex` as the baked-in model
|
|
27
|
+
(`src/config.ts` blueprint).
|
|
28
|
+
- **System prompt**: Frames the assistant as a proactive coding agent that can read/write files,
|
|
29
|
+
execute bash commands, and chain tools to finish multi-step tasks.
|
|
30
|
+
- **CLI entrypoint**: `erosolar` (package `bin` field). Use `/agents` to switch profiles after launching.
|
|
31
|
+
- **Ideal for**: Rapid iteration when the operator prefers OpenAI latency/behavior.
|
|
32
|
+
- **Model presets**: Switch between `gpt-5.1-codex` (default), `gpt-5.1`, `gpt-5-pro`,
|
|
33
|
+
`gpt-5-mini`, or `gpt-5-nano` directly from `/model` in the CLI.
|
|
34
|
+
|
|
35
|
+
## DeepSeek provider (OpenAI-compatible)
|
|
36
|
+
|
|
37
|
+
- **Provider/model**: Uses the OpenAI SDK pointed at DeepSeek’s API gateway so both
|
|
38
|
+
`deepseek-reasoner` and `deepseek-chat` appear in `/model`.
|
|
39
|
+
- **Authentication**: Requires `DEEPSEEK_API_KEY` (set via `/secrets` or exported as an env var).
|
|
40
|
+
- **Usage**: The CLI reuses the same OpenAI client plumbing:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import OpenAI from 'openai';
|
|
44
|
+
|
|
45
|
+
const openai = new OpenAI({
|
|
46
|
+
baseURL: 'https://api.deepseek.com',
|
|
47
|
+
apiKey: process.env.DEEPSEEK_API_KEY,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
async function main() {
|
|
51
|
+
const completion = await openai.chat.completions.create({
|
|
52
|
+
messages: [{ role: 'system', content: 'You are a helpful assistant.' }],
|
|
53
|
+
model: 'deepseek-reasoner',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
console.log(completion.choices[0].message.content);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
main();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- **Ideal for**: When you want DeepSeek’s open weights but still expect the CLI to behave like the
|
|
63
|
+
OpenAI-backed Erosolar Code workflow.
|
|
64
|
+
|
|
65
|
+
## xAI provider (OpenAI-compatible)
|
|
66
|
+
|
|
67
|
+
- **Provider/model**: Reuses the OpenAI Chat Completions client but points it at `https://api.x.ai/v1`
|
|
68
|
+
so the CLI can drive Grok models natively.
|
|
69
|
+
- **Authentication**: Requires `XAI_API_KEY` (store it via `/secrets` or export it before launching).
|
|
70
|
+
- **Model presets**: `/model` now exposes `grok-4`, `grok-4-fast-reasoning`, `grok-4-fast-non-reasoning`,
|
|
71
|
+
and `grok-code-fast-1`.
|
|
72
|
+
- **Usage**: You can also call xAI directly with their official SDK:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { xai } from '@ai-sdk/xai';
|
|
76
|
+
import { generateText } from 'ai';
|
|
77
|
+
|
|
78
|
+
const result = await generateText({
|
|
79
|
+
model: xai('grok-4'),
|
|
80
|
+
system:
|
|
81
|
+
"You are Grok, a chatbot inspired by the Hitchhiker's Guide to the Galaxy.",
|
|
82
|
+
prompt: 'What is the meaning of life, the universe, and everything?',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
console.log(result.text);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- **Ideal for**: When you want Grok’s Hitchhiker-inspired tone without losing the CLI workflow.
|
|
89
|
+
|
|
90
|
+
## Google provider (Gemini)
|
|
91
|
+
|
|
92
|
+
- **Provider/model**: Uses Google’s official `@google/genai` SDK so the CLI can talk to Gemini natively. The built-in config exposes both `gemini-2.5-pro` and `gemini-2.5-flash` models under the new `google` provider id.
|
|
93
|
+
- **Authentication**: Requires `GEMINI_API_KEY`, which the CLI passes directly to the SDK client.
|
|
94
|
+
- **Usage**: Mirrors the standalone SDK experience:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { GoogleGenAI } from '@google/genai';
|
|
98
|
+
|
|
99
|
+
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
|
|
100
|
+
|
|
101
|
+
async function main() {
|
|
102
|
+
const response = await ai.models.generateContent({
|
|
103
|
+
model: 'gemini-2.5-flash',
|
|
104
|
+
contents: 'Explain how AI works in a few words',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
console.log(response.text);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- **Ideal for**: When you want Gemini’s multi-modal reasoning or prefer Google’s governance defaults without going through an OpenAI-compatible shim.
|
|
114
|
+
|
|
115
|
+
## Environment overrides
|
|
116
|
+
|
|
117
|
+
All agents honor the same override pattern. Set these before launching the CLI if you need to
|
|
118
|
+
tune a run without editing the repo:
|
|
119
|
+
|
|
120
|
+
- `GENERAL_MODEL`, `GENERAL_PROVIDER`, `GENERAL_SYSTEM_PROMPT`
|
|
121
|
+
- `EROSOLAR_CODE_MODEL`, `EROSOLAR_CODE_PROVIDER`, `EROSOLAR_CODE_SYSTEM_PROMPT`
|
|
122
|
+
|
|
123
|
+
Unset or unknown providers automatically fall back to the safe defaults in `src/config.ts`.
|
|
124
|
+
|
|
125
|
+
## Shell sandbox behavior
|
|
126
|
+
|
|
127
|
+
- The bash tool now rewrites `HOME`, `TMPDIR`, and the `XDG_*` cache/config/data paths to
|
|
128
|
+
`<workspace>/.erosolar/shell-sandbox` so commands like `firebase`, `gcloud`, or `curl` can create
|
|
129
|
+
their runtime state even when the host blocks writes to the real home directory.
|
|
130
|
+
- Set `EROSOLAR_PRESERVE_HOME=1` before launching `erosolar` if you need to opt out and expose your
|
|
131
|
+
actual home directory (only do this when the host allows writing outside the repo).
|
|
132
|
+
|
|
133
|
+
## Selecting agents at runtime
|
|
134
|
+
|
|
135
|
+
Launch `erosolar` and run `/agents` to view the registered profiles (Erosolar and Erosolar Code, plus any
|
|
136
|
+
custom entries). The selection persists in `~/.erosolar/settings.json` and the CLI reloads that
|
|
137
|
+
profile on the next launch unless you override it with `--profile=<name>` or the corresponding
|
|
138
|
+
environment variables.
|
|
139
|
+
|
|
140
|
+
## Adding custom agents
|
|
141
|
+
|
|
142
|
+
`src/core/agentProfiles.ts` exposes a lightweight registry so you can call
|
|
143
|
+
`registerAgentProfile()` anywhere before `resolveProfileConfig()` runs. Each profile entry defines
|
|
144
|
+
its label, default provider/model pair, and guardrailed system prompt. The CLI automatically
|
|
145
|
+
recognizes the new profile name with no additional wiring and the `AgentSession` runtime
|
|
146
|
+
(`src/runtime/agentSession.ts`) keeps the prompt/tool context in sync across frontends.
|
|
147
|
+
|
|
148
|
+
## Provider registry
|
|
149
|
+
|
|
150
|
+
`src/providers/providerFactory.ts` is now a registry rather than a hard-coded switch. Call
|
|
151
|
+
`registerProvider('my-provider', factory)` once during startup to plug in new SDKs. All agents—
|
|
152
|
+
including the interactive shell, cloud workers, or browser embeddings—reuse the same provider IDs,
|
|
153
|
+
so adding new connections never requires editing the UI layer.
|
|
154
|
+
|
|
155
|
+
## Tool suites & sessions
|
|
156
|
+
|
|
157
|
+
Tool handlers are grouped into suites (`src/core/toolRuntime.ts`) which can be registered or
|
|
158
|
+
removed at runtime. The shared `AgentSession` helper composes a profile, provider selection, and
|
|
159
|
+
tool suites into a reusable runtime so the CLI, a browser UI, or a hosted workflow can all spin up
|
|
160
|
+
the same agent contract without reimplementing orchestration code.
|
|
161
|
+
|
|
162
|
+
## Capability host & modular expansions
|
|
163
|
+
|
|
164
|
+
- `src/runtime/agentHost.ts` introduces an `AgentHost` facade that fronts every frontend (CLI,
|
|
165
|
+
daemon, browser worker, or cloud function). It accepts any number of `CapabilityModule`
|
|
166
|
+
instances—each module can contribute tool suites, external connectors, or metadata without
|
|
167
|
+
touching the UI.
|
|
168
|
+
- Default modules in `src/capabilities/` wrap the local filesystem, structured repo search, and the
|
|
169
|
+
sandboxed bash executor. Operators can add new modules (Google Sheets, MCP servers, legacy file
|
|
170
|
+
migrations, etc.) by registering them before the session starts:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { AgentHost } from './runtime/agentHost.js';
|
|
174
|
+
import { CapabilityModule } from './runtime/agentHost.js';
|
|
175
|
+
|
|
176
|
+
const googleSheetsModule: CapabilityModule = {
|
|
177
|
+
id: 'capability.google_sheets',
|
|
178
|
+
async create(context) {
|
|
179
|
+
const sheets = await connectToSheets(process.env.GOOGLE_CREDENTIALS!);
|
|
180
|
+
return {
|
|
181
|
+
id: 'sheets.tools.analytics',
|
|
182
|
+
description: 'Plan + edit spreadsheet models via Google Sheets API',
|
|
183
|
+
toolSuites: [sheets.asToolSuite()],
|
|
184
|
+
metadata: { scopes: sheets.requestedScopes },
|
|
185
|
+
dispose: () => sheets.disconnect(),
|
|
186
|
+
};
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const host = new AgentHost({ profile: 'erosolar-code', workspaceContext, workingDir });
|
|
191
|
+
await host.loadModules([googleSheetsModule]);
|
|
192
|
+
const session = await host.getSession();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- Because `AgentHost` only passes plain tool suites into `AgentSession`, the same capability stack
|
|
196
|
+
works from a CLI shell, a remote orchestrator, or a browser sandbox. No frontend changes are
|
|
197
|
+
required as long as new modules obey the shared `CapabilityModule` contract.
|
|
198
|
+
- Modules can bridge to MCP servers, SaaS APIs, or on-prem systems by emitting their own tool suites
|
|
199
|
+
(e.g., a “research” module that shells into SLURM, or a “filesystem migration” module that wraps a
|
|
200
|
+
bespoke archivist API). Frontends decide which modules to load based on deployment policy without
|
|
201
|
+
recompiling the CLI.
|
|
202
|
+
|
|
203
|
+
## Maintenance
|
|
204
|
+
|
|
205
|
+
Update this file whenever `src/config.ts` changes guardrails, default models, or provider
|
|
206
|
+
identifiers. If the repo only needs a single agent, you may delete this file altogether; the CLI
|
|
207
|
+
will continue to use the TypeScript blueprints directly.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Erosolar AI
|
|
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.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"document": {
|
|
3
|
+
"title": "Source of Truth — Hallucination-Free Editing",
|
|
4
|
+
"schemaVersion": "5.0.0",
|
|
5
|
+
"snapshotId": "editing_grounding_refresh",
|
|
6
|
+
"snapshotLabel": "Editing Context Discipline",
|
|
7
|
+
"lastUpdated": "2025-11-16",
|
|
8
|
+
"maintainedBy": "erosolar Dual Provider Runtime"
|
|
9
|
+
},
|
|
10
|
+
"northStar": "Make every edit cite deterministic evidence so hallucinations and contextual overload never reach the file system.",
|
|
11
|
+
"mission": "Describe the frozen truth inputs, replay-only tooling, and loop controls that make erosolar the safest place to edit with LLMs.",
|
|
12
|
+
"authoritativeCorpus": {
|
|
13
|
+
"priorityFiles": [
|
|
14
|
+
"README.md",
|
|
15
|
+
"Agents.md",
|
|
16
|
+
"SOURCE_OF_TRUTH.json",
|
|
17
|
+
"ARCHITECTURE.json",
|
|
18
|
+
"package.json",
|
|
19
|
+
"src/config.ts"
|
|
20
|
+
],
|
|
21
|
+
"refreshExpectation": "Capture a new workspace snapshot on every CLI launch before any editing instructions are issued.",
|
|
22
|
+
"allowedExpansion": "Only add files that contain objective reference specs or canonical configuration. Narrative changelogs live elsewhere."
|
|
23
|
+
},
|
|
24
|
+
"agentsDocumentation": {
|
|
25
|
+
"optionalFile": "Agents.md",
|
|
26
|
+
"purpose": "Describe the available CLI agents (Erosolar general via OpenAI and Erosolar Code via OpenAI) without bloating this canonical JSON snapshot.",
|
|
27
|
+
"contents": [
|
|
28
|
+
"Provider + model defaults copied from src/config.ts blueprint values.",
|
|
29
|
+
"Guidance on when to use each agent and how system prompts differ.",
|
|
30
|
+
"Environment variable overrides that map directly to the implementation."
|
|
31
|
+
],
|
|
32
|
+
"refreshGuidance": "Regenerate or update Agents.md whenever src/config.ts changes guardrails, models, or environment override names."
|
|
33
|
+
},
|
|
34
|
+
"preferencePersistence": {
|
|
35
|
+
"mechanism": "src/core/preferences.ts writes ~/.erosolar/settings.json whenever the operator changes models via /model.",
|
|
36
|
+
"behavior": "On startup the CLI reloads the last saved provider/model for a profile unless the corresponding EROSOLAR_CODE_* environment variables pin a specific configuration."
|
|
37
|
+
},
|
|
38
|
+
"hallucinationControls": [
|
|
39
|
+
{
|
|
40
|
+
"id": "deterministic_snapshot",
|
|
41
|
+
"mechanism": "src/workspace.ts builds a depth-2 tree (max 200 entries) and ingests 2000-character excerpts from every priority file.",
|
|
42
|
+
"editingImpact": "Locks an identical evidence base for both providers before edits start."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "prompt_appendix",
|
|
46
|
+
"mechanism": "src/config.ts concatenates the guardrail text with the frozen snapshot for each agent defined in Agents.md.",
|
|
47
|
+
"editingImpact": "Reduces contextual overload by giving every agent the same curated evidence instead of raw workspace noise."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "metadata_tools",
|
|
51
|
+
"mechanism": "src/core/toolRuntime.ts exposes `context_snapshot`, `capabilities_overview`, and `profile_details` as replay-only handlers.",
|
|
52
|
+
"editingImpact": "Allows the model to rehearse the snapshot verbatim mid-edit instead of guessing.",
|
|
53
|
+
"contextDiscipline": "Tools never re-read the workspace or shell out, so the prompt stays small and predictable."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "loop_monitoring",
|
|
57
|
+
"mechanism": "src/core/agent.ts streams every assistant/tool turn without a hard pass cap, relying on operators to interrupt if necessary and replacing the previous 6+6 pass guardrail.",
|
|
58
|
+
"editingImpact": "Long-running refactors can finish in one turn, yet humans still decide when to stop and gather more evidence."
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"editingWorkflow": [
|
|
62
|
+
{
|
|
63
|
+
"stage": "intent_capture",
|
|
64
|
+
"description": "Operator issues an editing request; agent confirms which files are in scope using the frozen tree."
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"stage": "snapshot_rehearsal",
|
|
68
|
+
"description": "Model replays `context_snapshot` or `profile_details` (documented in Agents.md) if any evidence feels uncertain."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"stage": "patch_reasoning",
|
|
72
|
+
"description": "Model plans edits strictly against cited files and excerpts from the appendix."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"stage": "answer_or_escalate",
|
|
76
|
+
"description": "The model keeps iterating until it can deliver a cited patch plan or explain what evidence is missing; humans interrupt manually if execution thrashes."
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"contextManagement": {
|
|
80
|
+
"treeDepth": 2,
|
|
81
|
+
"maxEntries": 200,
|
|
82
|
+
"documentExcerptLimit": 2000,
|
|
83
|
+
"excludedFolders": [".git", "node_modules", "dist"],
|
|
84
|
+
"rationale": "Unlimited tool passes keep the editing loop flexible; the appendix guardrails stay focused on keeping captured context compact."
|
|
85
|
+
},
|
|
86
|
+
"verificationRules": [
|
|
87
|
+
"Reference only the files listed in `authoritativeCorpus.priorityFiles` or paths that appear in the captured tree.",
|
|
88
|
+
"Quote line numbers or excerpt boundaries whenever describing an edit.",
|
|
89
|
+
"If the snapshot omits the needed file (e.g., because of depth limits), stop and request a refreshed capture.",
|
|
90
|
+
"Never suggest creating speculative files to satisfy a request; direct contributors back to this Source of Truth."
|
|
91
|
+
],
|
|
92
|
+
"responseBoundaries": [
|
|
93
|
+
"All answers must cite which captured file or tool output justified the claim.",
|
|
94
|
+
"If an editing action would require fresh context (new file, new dependency), instruct the operator to refresh the snapshot first.",
|
|
95
|
+
"Do not fabricate configuration guidance—reference src/config.ts or this document.",
|
|
96
|
+
"Keep reasoning concise; defer to the metadata tools instead of repeating the entire snapshot inside free-form text."
|
|
97
|
+
],
|
|
98
|
+
"escalationPolicy": {
|
|
99
|
+
"missing_snapshot": "Abort the editing session and instruct the operator to re-run the CLI to capture a new snapshot.",
|
|
100
|
+
"long_running_loop": "If the agent keeps issuing tool calls without converging, stop the session manually and gather more evidence before retrying.",
|
|
101
|
+
"tool_error": "Surface the exact error string (no paraphrasing) and request human intervention."
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { instantiateToolPlugins, registerDefaultNodeToolPlugins, } from '../../plugins/tools/index.js';
|
|
2
|
+
export class NodeRuntimeAdapter {
|
|
3
|
+
id = 'runtime.node';
|
|
4
|
+
options;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async createCapabilityModules(context) {
|
|
9
|
+
registerDefaultNodeToolPlugins();
|
|
10
|
+
const filter = (plugin) => {
|
|
11
|
+
if (this.options.includeFilesystem === false && plugin.id === 'tool.filesystem.local') {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (this.options.includeSearch === false && plugin.id === 'tool.search.local') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (this.options.includeBash === false && plugin.id === 'tool.bash.local') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (this.options.filter && !this.options.filter(plugin)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
const modules = await instantiateToolPlugins('node', {
|
|
26
|
+
workingDir: context.workingDir,
|
|
27
|
+
env: context.env,
|
|
28
|
+
}, { filter });
|
|
29
|
+
if (this.options.extraModules?.length) {
|
|
30
|
+
modules.push(...this.options.extraModules);
|
|
31
|
+
}
|
|
32
|
+
return modules;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class RemoteRuntimeAdapter {
|
|
2
|
+
id = 'runtime.remote';
|
|
3
|
+
options;
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
}
|
|
7
|
+
async createCapabilityModules(context) {
|
|
8
|
+
const modules = [];
|
|
9
|
+
for (const entry of this.options.modules ?? []) {
|
|
10
|
+
if (typeof entry === 'function') {
|
|
11
|
+
modules.push(await entry(context));
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
modules.push(entry);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return modules;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createBashTools } from '../tools/bashTools.js';
|
|
2
|
+
export class BashCapabilityModule {
|
|
3
|
+
id = 'capability.bash';
|
|
4
|
+
options;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async create(context) {
|
|
9
|
+
const workingDir = this.options.workingDir ?? context.workingDir;
|
|
10
|
+
return {
|
|
11
|
+
id: this.options.id ?? 'bash.tools.shell',
|
|
12
|
+
description: this.options.description ?? 'Shell execution with stdout/stderr summaries for reproducibility.',
|
|
13
|
+
toolSuite: {
|
|
14
|
+
id: 'bash',
|
|
15
|
+
description: 'Shell access',
|
|
16
|
+
tools: createBashTools(workingDir),
|
|
17
|
+
},
|
|
18
|
+
metadata: {
|
|
19
|
+
workingDir,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createFileTools } from '../tools/fileTools.js';
|
|
2
|
+
export class FilesystemCapabilityModule {
|
|
3
|
+
id = 'capability.filesystem';
|
|
4
|
+
options;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async create(context) {
|
|
9
|
+
const workingDir = this.options.workingDir ?? context.workingDir;
|
|
10
|
+
return {
|
|
11
|
+
id: this.options.id ?? 'filesystem.tools.local',
|
|
12
|
+
description: this.options.description ?? 'Local file system access with deterministic diff summaries.',
|
|
13
|
+
toolSuite: {
|
|
14
|
+
id: 'fs',
|
|
15
|
+
description: 'File operations',
|
|
16
|
+
tools: createFileTools(workingDir),
|
|
17
|
+
},
|
|
18
|
+
metadata: {
|
|
19
|
+
workingDir,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createSearchTools } from '../tools/searchTools.js';
|
|
2
|
+
export class SearchCapabilityModule {
|
|
3
|
+
id = 'capability.search';
|
|
4
|
+
options;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async create(context) {
|
|
9
|
+
const workingDir = this.options.workingDir ?? context.workingDir;
|
|
10
|
+
return {
|
|
11
|
+
id: this.options.id ?? 'search.tools.repo',
|
|
12
|
+
description: this.options.description ?? 'Repository-aware search helpers (glob + structural grep).',
|
|
13
|
+
toolSuite: {
|
|
14
|
+
id: 'search',
|
|
15
|
+
description: 'Code search',
|
|
16
|
+
tools: createSearchTools(workingDir),
|
|
17
|
+
},
|
|
18
|
+
metadata: {
|
|
19
|
+
workingDir,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|