Skip to content

Commit 4dbb87f

Browse files
authored
Merge branch 'master' into maint/fix-win-int-tests
2 parents 6e3680d + 8ac45e9 commit 4dbb87f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1410
-1428
lines changed

.cljfmt.edn

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
{:extra-indents {task [[:inner 0]]}}
1+
{:extra-indents {task [[:inner 0]]
2+
future* [[:inner 0]]}}

AGENTS.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,13 @@ Code Style
2525
- Unit tests that use file paths and uris should rely on `h/file-path` and `h/file-uri` to avoid windows issues with slashes.
2626

2727
Secrets Management
28-
- `src/eca/secrets/netrc.clj` - Netrc format parser (multi-line format: machine/login/password/port keywords)
29-
- `src/eca/secrets/authinfo.clj` - Authinfo format parser (single-line format: space-separated key-value pairs)
3028
- `src/eca/secrets.clj` - Main secrets manager for credential file operations:
31-
- File discovery and priority order (.authinfo.gpg → .netrc.gpg → .authinfo → _authinfo → .netrc → _netrc)
29+
- File discovery and priority order (`:netrcFile <FILE>` config → .netrc → _netrc)
3230
- Cross-platform path construction using io/file (handles / vs \ separators automatically)
33-
- GPG decryption with caching (5-second TTL) and timeout (30s, configurable via GPG_TIMEOUT env var)
3431
- keyRc format parsing: [login@]machine[:port] (named after Unix "rc" config file tradition)
3532
- Credential matching logic (exact login match when specified, first match otherwise)
3633
- Permission validation (Unix: warns if not 0600; Windows: skipped)
3734
- Authentication flow: config `key` → credential files `keyRc` → env var `keyEnv` → OAuth
38-
- Security: passwords never logged; GPG decryption via clojure.java.process; cache with short TTL; subprocess timeout protection
3935

4036
Notes
4137
- CI runs: bb test and bb integration-test. Ensure these pass locally before PRs.

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
## Unreleased
44

5+
## 0.75.1
6+
7+
- Improve protocol for tool call output formatting for tools that output json.
8+
- Fix inconsistencies in `eca_read_file` not passing correct content to LLM when json.
9+
10+
## 0.75.0
11+
12+
- Improved file contexts: now use :lines-range
13+
- BREAKING ECA now only supports standard plain-text netrc as credential file reading. Drop authinfo and gpg decryption support. Users can choose to pass in their own provisioned netrc file from various secure source with `:netrcFile` in ECA config.
14+
15+
## 0.74.0
16+
17+
- Improved `eca_edit_file` to automatically handle whitespace and indentation differences in single-occurrence edits.
18+
- Fix contexts in user prompts (not system contexts) not parsing lines ranges properly.
19+
- Support non-stream providers on openai-chat API. #174
20+
21+
## 0.73.5
22+
23+
- Support use API keys even if subscription is logged. #175
24+
25+
## 0.73.4
26+
27+
- Fix tool call approval ignoring eca tools.
28+
29+
## 0.73.3
30+
31+
- Fix tool call approval ignoring configs for mcp servers.
32+
533
## 0.73.2
634

735
- Fix tool call approval thread lock.

docs/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ To configure, add your OTLP collector config via `:otlp` map following [otlp aut
515515
systemPromptFile?: string;
516516
};
517517
otlp?: {[key: string]: string};
518+
netrcFile?: string;
518519
}
519520
```
520521

@@ -530,6 +531,7 @@ To configure, add your OTLP collector config via `:otlp` map following [otlp aut
530531
"ollama": {"url": "http://localhost:11434"}
531532
},
532533
"defaultModel": null, // let ECA decides the default model.
534+
"netrcFile": null, // search ~/.netrc or ~/_netrc when null.
533535
"hooks": {},
534536
"rules" : [],
535537
"commands" : [],

docs/models.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ Only set this when your provider uses a different path or expects query paramete
148148

149149
### Credential File Authentication
150150

151-
Use `keyRc` in your provider config to read credentials from `~/.authinfo(.gpg)` or `~/.netrc(.gpg)` without storing keys directly in config or env vars.
151+
ECA also supports standard plain-text .netrc file format for reading credentials.
152+
153+
Use `keyRc` in your provider config to read credentials from `~/.netrc` without storing keys directly in config or env vars.
152154

153155
Example:
154156

@@ -163,13 +165,20 @@ Example:
163165

164166
keyRc lookup specification format: `[login@]machine[:port]` (e.g., `api.openai.com`, `[email protected]`, `api.custom.com:8443`).
165167

168+
ECA by default search .netrc file stored in user's home directory. You can also provide the path to the actual file to use with `:netrcFile` in ECA config.
169+
170+
Tip for those wish to store their credentials encrypted with tools like gpg or age:
171+
172+
```bash
173+
# via secure tempororay file
174+
gpg --batch -q -d ./netrc.gpg > /tmp/netrc.$$ && chmod 600 /tmp/netrc.$$ && ECA_CONFIG='{"netrcFile": "/tmp/netrc.$$"}' eca server && shred -u /tmp/netrc.$$
175+
```
176+
166177
Further reading on credential file formats:
167-
- [Emacs authinfo documentation](https://www.gnu.org/software/emacs/manual/html_node/auth/Help-for-users.html)
168178
- [Curl Netrc documentation](https://everything.curl.dev/usingcurl/netrc)
169179
- [GNU Inetutils .netrc documentation](https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html)
170180

171181
Notes:
172-
- Preferred files are GPG-encrypted (`~/.authinfo.gpg` / `~/.netrc.gpg`); plaintext variants are supported.
173182
- Authentication priority (short): `key` > `keyRc files` > `keyEnv` > OAuth.
174183
- All providers with API key auth can use credential files.
175184

docs/protocol.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ interface ChatToolCallRejectedContent {
984984

985985
type ToolCallOrigin = 'mcp' | 'native';
986986

987-
type ToolCallDetails = FileChangeDetails;
987+
type ToolCallDetails = FileChangeDetails | JsonOutputsDetails;
988988

989989
interface FileChangeDetails {
990990
type: 'fileChange';
@@ -1010,6 +1010,15 @@ interface FileChangeDetails {
10101010
linesRemoved: number;
10111011
}
10121012

1013+
interface JsonOutputsDetails {
1014+
type: 'jsonOutputs';
1015+
1016+
/**
1017+
* The list of json outputs of this tool call properly formatted.
1018+
*/
1019+
jsons: string[];
1020+
}
1021+
10131022
/**
10141023
* Extra information about a chat
10151024
*/

integration-test/integration/chat/anthropic_test.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
resp))
3131

3232
(match-content chat-id "user" {:type "text" :text "Tell me a joke!\n"})
33+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
3334
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
3435
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
3536
(match-content chat-id "assistant" {:type "text" :text "Knock"})
@@ -99,7 +100,6 @@
99100
:lastMessageCost (m/pred string?)
100101
:sessionCost (m/pred string?)})
101102
(match-content chat-id "system" {:type "progress" :state "finished"})
102-
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
103103
(is (match?
104104
{:messages [{:role "user" :content [{:type "text" :text "Tell me a joke!"}]}
105105
{:role "assistant" :content [{:type "text" :text "Knock knock!"}]}
@@ -128,6 +128,7 @@
128128
resp))
129129

130130
(match-content chat-id "user" {:type "text" :text "hello!\n"})
131+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
131132
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
132133
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
133134
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
@@ -141,7 +142,6 @@
141142
:lastMessageCost (m/pred string?)
142143
:sessionCost (m/pred string?)})
143144
(match-content chat-id "system" {:type "progress" :state "finished"})
144-
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
145145
(is (match?
146146
{:messages [{:role "user" :content [{:type "text" :text "hello!"}]}]
147147
:system (m/pred vector?)}
@@ -206,6 +206,7 @@
206206
resp))
207207

208208
(match-content chat-id "user" {:type "text" :text "What files you see?\n"})
209+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
209210
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
210211
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
211212
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})
@@ -264,7 +265,6 @@
264265
(match-content chat-id "assistant" {:type "text" :text "The files I see:\n"})
265266
(match-content chat-id "assistant" {:type "text" :text "file1\nfile2\n"})
266267
(match-content chat-id "system" {:type "progress" :state "finished"})
267-
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
268268
(is (match?
269269
{:messages [{:role "user" :content [{:type "text" :text "What files you see?"}]}
270270
{:role "assistant"

integration-test/integration/chat/custom_provider_test.clj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
resp))
4747

4848
(match-content chat-id "user" {:type "text" :text "Tell me a joke!\n"})
49+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
4950
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
5051
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
5152
(match-content chat-id "assistant" {:type "text" :text "Knock"})
@@ -158,6 +159,7 @@
158159
resp))
159160

160161
(match-content chat-id "user" {:type "text" :text "Tell me a joke!\n"})
162+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
161163
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
162164
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
163165
(match-content chat-id "assistant" {:type "text" :text "Knock "})

integration-test/integration/chat/github_copilot_test.clj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
resp))
3232

3333
(match-content chat-id "user" {:type "text" :text "Tell me a joke!\n"})
34+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
3435
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
3536
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
3637
(match-content chat-id "assistant" {:type "text" :text "Knock "})
@@ -62,7 +63,6 @@
6263
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
6364
(match-content chat-id "assistant" {:type "text" :text "Foo"})
6465
(match-content chat-id "system" {:type "progress" :state "finished"})
65-
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
6666
(is (match?
6767
{:input [{:role "user" :content [{:type "input_text" :text "Tell me a joke!"}]}
6868
{:role "assistant" :content [{:type "output_text" :text "Knock knock!"}]}
@@ -123,6 +123,7 @@
123123
resp))
124124

125125
(match-content chat-id "user" {:type "text" :text "hello!\n"})
126+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
126127
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
127128
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
128129
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})

integration-test/integration/chat/google_test.clj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
resp))
3232

3333
(match-content chat-id "user" {:type "text" :text "Tell me a joke!\n"})
34+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
3435
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
3536
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
3637
(match-content chat-id "assistant" {:type "text" :text "Knoc"})
@@ -62,7 +63,6 @@
6263
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
6364
(match-content chat-id "assistant" {:type "text" :text "Foo"})
6465
(match-content chat-id "system" {:type "progress" :state "finished"})
65-
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
6666
(is (match?
6767
{:input [{:role "user" :content [{:type "input_text" :text "Tell me a joke!"}]}
6868
{:role "assistant" :content [{:type "output_text" :text "Knock knock!"}]}
@@ -122,6 +122,7 @@
122122
resp))
123123

124124
(match-content chat-id "user" {:type "text" :text "hello!\n"})
125+
(match-content chat-id "system" {:type "metadata" :title "Some Cool Title"})
125126
(match-content chat-id "system" {:type "progress" :state "running" :text "Waiting model"})
126127
(match-content chat-id "system" {:type "progress" :state "running" :text "Generating"})
127128
(match-content chat-id "assistant" {:type "reasonStarted" :id (m/pred string?)})

0 commit comments

Comments
 (0)