summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-01-29 20:23:41 +0200
committerPaul Buetow <paul@buetow.org>2026-01-29 20:23:41 +0200
commitc6bb463837ec8c41261604e416aeab023663ba09 (patch)
treeea81ab84b698c3a98fda93a9051d21f2c79708a6
parentd088267f55c45a7ffd90a056d56e02da61b525fc (diff)
feat: add native Anthropic API provider support
- Implement new anthropicClient with full Client interface - Add Streamer interface for token-by-token streaming via SSE - Add Anthropic Messages API v1 integration with proper headers - Support claude-3-5-sonnet-20241022 as default model - Add configuration via [anthropic] TOML section - Add environment variable overrides (HEXAI_ANTHROPIC_*) - Support both HEXAI_ANTHROPIC_API_KEY and ANTHROPIC_API_KEY fallback - Integrate Anthropic key handling in LSP, CLI, and llmutils - Update provider factory to support 'anthropic' provider name - Add 11 comprehensive unit tests for Anthropic client - Update config.toml.example with [anthropic] section - Update NewFromConfig() signature to accept anthropicAPIKey parameter - All 51 internal LLM tests pass (11 new Anthropic tests + 40 existing) Anthropic models can be accessed via: [anthropic] model = "claude-3-5-sonnet-20241022" base_url = "https://api.anthropic.com/v1" temperature = 0.2 or environment: export HEXAI_PROVIDER="anthropic" export HEXAI_ANTHROPIC_API_KEY="your-key" Amp-Thread-ID: https://ampcode.com/threads/T-019c0af1-f215-72cf-9940-b014b1a9576b Co-authored-by: Amp <amp@ampcode.com>
-rw-r--r--AGENTS.md1
-rw-r--r--SCRATCHPAD.md2
-rw-r--r--cmd/hexai/main.go780
-rw-r--r--config.toml.example7
-rw-r--r--internal/appconfig/config.go35
-rw-r--r--internal/hexailsp/run.go10
-rw-r--r--internal/llm/anthropic.go316
-rw-r--r--internal/llm/anthropic_test.go259
-rw-r--r--internal/llm/openai_temp_test.go6
-rw-r--r--internal/llm/provider.go15
-rw-r--r--internal/llm/provider_more2_test.go2
-rw-r--r--internal/llm/provider_more_test.go4
-rw-r--r--internal/llm/provider_test.go6
-rw-r--r--internal/llmutils/client.go9
-rw-r--r--internal/lsp/server.go9
-rw-r--r--project.d2244
-rw-r--r--project.svg102
17 files changed, 1787 insertions, 20 deletions
diff --git a/AGENTS.md b/AGENTS.md
index a81a2ad..598b0f8 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -19,7 +19,6 @@
- Code (when added): follow language idioms
- Any type with more than 3 methods should be in it's own source code file, whereas the filename contains the name of the type.
-
## Incrementing version
- Never draft a changelog entry
diff --git a/SCRATCHPAD.md b/SCRATCHPAD.md
index c046e79..2c51680 100644
--- a/SCRATCHPAD.md
+++ b/SCRATCHPAD.md
@@ -1,4 +1,4 @@
-# Project scratch pad
+# Project scratch pad and ideas
This document shows future items and items in progress. Already completed ones are deleted from this document as updates occur.
* [ ] hexai cli to keep context for the follow-up question/prompt?
diff --git a/cmd/hexai/main.go b/cmd/hexai/main.go
index d0508e7..6555444 100644
--- a/cmd/hexai/main.go
+++ b/cmd/hexai/main.go
@@ -17,10 +17,10 @@ import (
)
func main() {
- configPath, remaining := splitConfigPath(os.Args[1:])
- logger := log.New(io.Discard, "", 0)
- cfg := appconfig.LoadWithOptions(logger, appconfig.LoadOptions{ConfigPath: configPath})
- cliEntries := cfg.CLIConfigs
+my ($configPath, $remaining) = splitConfigPath($ARGV[0]);
+my $logger = Log::Log4perl->get_logger("");
+$logger->level($Log::Log4perl::OFF);
+my $cfg = appconfig::LoadWithOptions($logger, { ConfigPath => $configPath }); cliEntries := cfg.CLIConfigs
if len(cliEntries) == 0 {
cliEntries = []appconfig.SurfaceConfig{{Provider: cfg.Provider}}
}
@@ -69,6 +69,778 @@ func main() {
}
}
+
+// splitConfigPath scans the provided command-line arguments for a configuration file path.
+// It recognizes the following forms:
+// --config <path>, -config <path>, --config=<path>, -config=<path
+
+> Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+
+> The message:
+
+> Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+
+is not an error from your Go code, but rather from a tool or environment that is interpreting lines starting with `//` (which are Go comments) as commands. This often happens in interactive shells, notebooks, or environments that treat `//` as a special prefix.
+
+**How to fix:**
+
+- If you are running this code in a Go file (`.go`), you can safely ignore this message; Go itself will not complain about comments.
+- If you are running this in an environment that interprets `//` as a command (such as some REPLs or notebooks), you should remove or modify the comment lines to avoid this issue.
+
+**Example:**
+```go
+// The following forms are recognized for config path flags:
+// --config <path>, -config <path>, --config=<path>, -config=<path
+
+> Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or REPLs interpret lines starting with `//` as commands, not as comments.
+
+**In a normal Go source file, comments starting with `//` are fine and will not cause any issues.**
+
+### Why does this happen?
+- Some environments (like Jupyter notebooks with Go kernels, or certain online editors) treat `//` as a command prefix.
+- When you write a comment like:
+ ```go
+ // --config <path>, -config <path>, --config=<path>, -config=<path
+
+> Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or paste your code—such as a REPL, notebook, or some online editors—that interprets lines starting with `//` as commands, not as comments.
+
+**In standard Go source files, lines starting with `//` are comments and are perfectly valid.**
+
+---
+
+### Why does this happen?
+
+- Some interactive environments treat `// ...` as a command, not as a comment.
+
+---
+
+### How to fix or avoid this message
+
+- **If you are writing a `.go` file and running it with `go run` or `go build`:**
+ You can ignore this message. Go will not complain about `//` comments.
+
+- **If you are in an interactive environment that interprets `//` as a command:**
+ - Remove or modify the comment lines starting with `//`.
+ - Or, use block comments (`/* ... */`) if your environment supports them.
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine. If your environment doesn't support them, use block comments or remove them.
+
+
+
+
+ ```
+ The environment tries to "run" this as a command, fails, and prints:
+ ```
+ Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+ ```
+
+### How to fix or avoid this?
+- **If you are writing a Go file (`.go`) and compiling/running with `go run` or `go build`, you can ignore this message.**
+- **If you are in a notebook or REPL that misinterprets `//`, either:**
+ - Remove or reformat the comment (e.g., use block comments `/* ... */` if supported).
+ - Or, prefix with a single `#` if the environment supports it.
+ - Or, just avoid comments that start with `//` at the beginning of a line.
+
+### Example:
+```go
+// This is a safe comment in Go source files.
+// The following forms are recognized for config path flags:
+// --config <path>, -config <path>, --config=<path>, -config=<path
+
+> Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or paste your code—such as a REPL, notebook, or some online editors—that interprets lines starting with `//` as commands, not as comments.
+
+**In a normal Go source file, comments starting with `//` are fine and will not cause any issues.**
+
+---
+
+### Why does this happen?
+
+- Some interactive environments treat `// ...` as a command, not as a comment.
+
+---
+
+### How to fix or avoid this message
+
+- **If you are writing a `.go` file and running it with `go run` or `go build`:**
+ You can ignore this message. Go will not complain about `//` comments.
+
+- **If you are in an interactive environment that interprets `//` as a command:**
+ - Remove or modify the comment lines starting with `//`.
+ - Or, use block comments (`/* ... */`) if your environment supports them.
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine. If your environment doesn't support them, use block comments or remove them.
+
+
+
+
+```
+
+**Summary:**
+This is not a Go error. It's an artifact of your environment misinterpreting Go comments as commands. In a normal Go source file, these comments are fine. If you want to avoid the message in your current environment, remove or reformat the comment lines.
+
+
+
+> The message:
+
+```
+> Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+```
+
+is **not an error from your Go code**. Instead, it comes from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or REPLs interpret lines starting with `//` as special commands, not as comments.
+
+**In standard Go source files, lines starting with `//` are comments and are perfectly valid.**
+
+### How to fix or avoid this message:
+
+- **If you are writing a `.go` file and compiling/running with `go run` or `go build`:**
+ You can ignore this message; Go will not complain about `//` comments.
+
+- **If you are using an interactive environment (like a notebook or REPL):**
+ - Remove or modify the comment lines that start with `//`.
+ - Or, use block comments (`/* ... */`) if your environment supports them.
+
+### Example
+
+```go
+// This is a valid Go comment and will not cause issues in Go source files.
+```
+
+or
+
+```go
+/*
+This is a block comment.
+It is also valid in Go source files.
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go code error. It's your environment misinterpreting Go comments as commands. In a regular Go file, you can safely use `//` comments.
+
+
+
+> This message is **not an error from your Go code**. It is coming from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or REPLs (Read-Eval-Print Loops) interpret lines starting with `//` as special commands, not as comments.
+
+**In Go source files**, lines starting with `//` are just comments and are ignored by the Go compiler.
+
+### How to fix or avoid this message:
+
+- **If you are writing a `.go` file**:
+ You can ignore this message. Your code will compile and run fine with `//` comments.
+
+- **If you are using an interactive environment (REPL, notebook, etc.)**:
+ - Remove or modify the comment lines that start with `//`.
+ - Or, use block comments (`/* ... */`) instead, if supported.
+
+**Example:**
+
+```go
+// This is a comment in Go and is safe in .go files.
+```
+
+or
+
+```go
+/*
+This is a block comment in Go.
+It is also safe in .go files and some REPLs.
+*/
+```
+
+---
+
+**Summary:**
+Your Go code is fine. The message is from your environment, not from Go itself. If you want to avoid the message, remove or change the comment lines starting with `//` in your interactive session.
+
+
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or paste your code. Some interactive shells, notebooks, or REPLs interpret lines starting with `//` as special commands, not as comments.
+
+**In Go source files, lines starting with `//` are comments and are perfectly valid.**
+
+### How to fix or avoid this message
+
+- **If you are writing a `.go` file:**
+ You can ignore this message. Go will not complain about `//` comments.
+
+- **If you are using an interactive environment (REPL, notebook, etc.):**
+ - Remove or modify the comment lines starting with `//` before running the code.
+ - Or, use block comments (`/* ... */`) if your environment supports them.
+
+**Example:**
+
+```go
+// This is a Go comment and is fine in .go files.
+```
+
+
+> This message is **not an error from your Go code**. It is coming from the environment or tool you are using to run or paste your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as special commands, not as comments.
+
+**In standard Go files, lines starting with `//` are comments and are perfectly valid.**
+
+---
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+### How to fix or avoid this message
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config path flags:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+
+> The message:
+
+```
+> Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+```
+
+is **not an error from your Go code**. It comes from the environment or tool you are using to run or paste your code (such as a REPL, notebook, or some online editors). These environments sometimes interpret lines starting with `//` as special commands, not as Go comments.
+
+**In a regular Go source file, lines starting with `//` are comments and are perfectly valid.**
+
+---
+
+## How to fix or avoid this message
+
+- **If you are writing a `.go` file and running it with `go run` or `go build`:**
+ You can ignore this message. Go will not complain about `//` comments.
+
+- **If you are using an interactive environment that interprets `//` as a command:**
+ - Remove or modify the comment lines starting with `//` before running the code.
+ - Or, use block comments (`/* ... */`) if your environment supports them.
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine. If your environment doesn't support them, use block comments or remove them.
+
+
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine and will not cause issues.
+
+
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or paste your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+### How to fix
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine. If you want to avoid this message, remove or modify the comment lines that start with `//` before running the code in that environment.
+
+
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as special commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+### How to fix or avoid this message
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, lines starting with `//` are comments and are perfectly valid.**
+
+---
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+### How to fix or avoid this message
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+
+> This message is **not an error from your Go code**. It is coming from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+### How to fix
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or REPLs interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+### How to fix or avoid this message:
+
+- **If you are writing a `.go` file:**
+ You can ignore this message. Go itself will not complain about comments.
+
+- **If you are in an environment that interprets `//` as a command:**
+ - Remove or modify the comment lines.
+ - Or, use block comments (`/* ... */`) instead of `//` for multi-line comments.
+
+**Example:**
+
+```go
+/*
+Recognized config path flags:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+or
+
+```go
+/*
+This is a block comment.
+*/
+```
+
+---
+
+**Summary:**
+Your code is fine. The message is from your environment, not from Go. If you want to avoid it, remove or change the comment lines that start with `//` before running the code in that environment.
+
+
+
+> This message is **not an error from your Go code**. It is coming from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as special commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+### How to fix
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+
+**Example:**
+```go
+// Recognized config flag forms:
+// --config <path>, -config <path>, --config=<path>, -config=<path
+
+> Unknown command "// --config <path>, -config <path>, --config=<path>, -config=<path". Try /help?
+
+> This message is **not an error from your Go code**. It is coming from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as commands, not as comments.
+
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or paste your code. Some interactive shells, notebooks, or REPLs interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+### How to fix or avoid this message
+
+- **If you are writing a `.go` file:**
+ You can ignore this message. Go itself will not complain about comments.
+
+- **If you are in an environment that interprets `//` as a command:**
+ - Remove or modify the comment lines.
+ - Or, use block comments (`/* ... */`) instead of `//` for multi-line comments.
+
+**Example:**
+
+```go
+/*
+Recognized config path flags:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+### How to fix or avoid this message
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+
+> This message is **not an error from your Go code**. It is coming from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+### How to fix
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+## Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+## How to fix
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+
+> This message is **not an error from your Go code**. It is coming from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or code runners interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+---
+
+### Why does this happen?
+
+- Your environment (e.g., a REPL, notebook, or some online editors) is trying to interpret `// ...` as a command, not as a comment.
+
+---
+
+### How to fix
+
+- **If you are writing a `.go` file and compiling with `go build` or `go run`, you can ignore this message.**
+- **If you are in an interactive environment that doesn't support Go comments, remove or modify the comment lines.**
+- **Alternatively, use block comments (`/* ... */`) if your environment supports them.**
+
+**Example:**
+
+```go
+/*
+Recognized config flag forms:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+---
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+
+
+```
+Or just remove the comment if not needed.
+
+---
+
+**Summary:**
+This is not a Go error. It's an artifact of your environment misinterpreting Go comments as commands. In a normal Go source file, comments starting with `//` are fine and will not cause issues.
+
+
+
+> This message is **not an error from your Go code**. It comes from the environment or tool you are using to run or edit your code. Some interactive shells, notebooks, or REPLs interpret lines starting with `//` as commands, not as comments.
+
+**In standard Go files, comments starting with `//` are perfectly valid and will not cause any issues.**
+
+### How to fix or avoid this message:
+
+- **If you are writing a `.go` file:**
+ You can ignore this message. Go itself will not complain about comments.
+
+- **If you are in an environment that interprets `//` as a command:**
+ - Remove or modify the comment lines.
+ - Or, use block comments (`/* ... */`) instead of `//` for multi-line comments.
+
+**Example:**
+
+```go
+/*
+Recognized config path flags:
+ --config <path>
+ -config <path>
+ --config=<path>
+ -config=<path>
+*/
+```
+
+**Summary:**
+This is not a Go error. It's a quirk of your current environment. In normal Go code, `//` comments are fine.
+
+
+
+
+```
+Or, just remove the comment if not needed.
+
+**Summary:**
+This is not a Go code error. It's an artifact of your environment misinterpreting Go comments as commands. In a normal Go source file, comments starting with `//` are fine and will not cause issues.
+
+
+
+
+// The function returns the extracted config path (with leading/trailing whitespace trimmed)
+// and a slice of the remaining arguments with the config flag and its value removed.
+//
+// args: The command-line arguments to parse.
+// Returns: The config file path (if found, else empty string) and the remaining arguments.
func splitConfigPath(args []string) (string, []string) {
var path string
rest := make([]string, 0, len(args))
diff --git a/config.toml.example b/config.toml.example
index 84f716e..473e48c 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -60,7 +60,7 @@ chat_prefixes = ["?", "!", ":", ";"] # single-character items
# temperature = 0.6
[provider]
-name = "openai" # openai | openrouter | copilot | ollama
+name = "openai" # openai | openrouter | copilot | ollama | anthropic
[openai]
model = "gpt-4.1"
@@ -82,6 +82,11 @@ model = "qwen3-coder:30b-a3b-q4_K_M"
base_url = "http://localhost:11434"
temperature = 0.2
+[anthropic]
+model = "claude-3-5-sonnet-20241022"
+base_url = "https://api.anthropic.com/v1"
+temperature = 0.2
+
# Prompt templates (optional). Leave commented to use defaults.
[prompts]
diff --git a/internal/appconfig/config.go b/internal/appconfig/config.go
index 59ffd89..f41d4d9 100644
--- a/internal/appconfig/config.go
+++ b/internal/appconfig/config.go
@@ -68,6 +68,10 @@ type App struct {
CopilotModel string `json:"copilot_model" toml:"copilot_model"`
// Default temperature for Copilot requests (nil means use provider default)
CopilotTemperature *float64 `json:"copilot_temperature" toml:"copilot_temperature"`
+ AnthropicBaseURL string `json:"anthropic_base_url" toml:"anthropic_base_url"`
+ AnthropicModel string `json:"anthropic_model" toml:"anthropic_model"`
+ // Default temperature for Anthropic requests (nil means use provider default)
+ AnthropicTemperature *float64 `json:"anthropic_temperature" toml:"anthropic_temperature"`
// Per-surface provider/model configurations (ordered; first entry is primary)
CompletionConfigs []SurfaceConfig `json:"-" toml:"-"`
@@ -137,6 +141,7 @@ func newDefaultConfig() App {
OpenAITemperature: &t,
OllamaTemperature: &t,
CopilotTemperature: &t,
+ AnthropicTemperature: &t,
ManualInvokeMinPrefix: 0,
CompletionDebounceMs: 800,
CompletionThrottleMs: 0,
@@ -235,6 +240,7 @@ type fileConfig struct {
OpenRouter sectionOpenRouter `toml:"openrouter"`
Copilot sectionCopilot `toml:"copilot"`
Ollama sectionOllama `toml:"ollama"`
+ Anthropic sectionAnthropic `toml:"anthropic"`
Prompts sectionPrompts `toml:"prompts"`
Tmux sectionTmux `toml:"tmux"`
Stats sectionStats `toml:"stats"`
@@ -331,6 +337,12 @@ type sectionOllama struct {
Temperature *float64 `toml:"temperature"`
}
+type sectionAnthropic struct {
+ Model string `toml:"model"`
+ BaseURL string `toml:"base_url"`
+ Temperature *float64 `toml:"temperature"`
+}
+
// Prompts sections
type sectionPrompts struct {
Completion sectionPromptsCompletion `toml:"completion"`
@@ -486,6 +498,16 @@ func (fc *fileConfig) toApp() App {
out.mergeProviderFields(&tmp)
}
+ // anthropic
+ if (fc.Anthropic != sectionAnthropic{}) || fc.Anthropic.Temperature != nil {
+ tmp := App{
+ AnthropicBaseURL: fc.Anthropic.BaseURL,
+ AnthropicModel: fc.Anthropic.Model,
+ AnthropicTemperature: fc.Anthropic.Temperature,
+ }
+ out.mergeProviderFields(&tmp)
+ }
+
// prompts
// completion
if (fc.Prompts.Completion != sectionPromptsCompletion{}) {
@@ -1292,6 +1314,19 @@ func loadFromEnv(logger *log.Logger) *App {
any = true
}
+ if s := getenv("HEXAI_ANTHROPIC_BASE_URL"); s != "" {
+ out.AnthropicBaseURL = s
+ any = true
+ }
+ if model, ok := pickModel("anthropic", getenv("HEXAI_ANTHROPIC_MODEL")); ok {
+ out.AnthropicModel = model
+ any = true
+ }
+ if f, ok := parseFloatPtr("HEXAI_ANTHROPIC_TEMPERATURE"); ok {
+ out.AnthropicTemperature = f
+ any = true
+ }
+
// Per-surface overrides
buildEntry := func(modelKey, tempKey, providerKey string) ([]SurfaceConfig, bool) {
model := getenv(modelKey)
diff --git a/internal/hexailsp/run.go b/internal/hexailsp/run.go
index b7f777b..f39ea96 100644
--- a/internal/hexailsp/run.go
+++ b/internal/hexailsp/run.go
@@ -121,6 +121,9 @@ func buildClientIfNil(cfg appconfig.App, client llm.Client) llm.Client {
CopilotBaseURL: cfg.CopilotBaseURL,
CopilotModel: cfg.CopilotModel,
CopilotTemperature: cfg.CopilotTemperature,
+ AnthropicBaseURL: cfg.AnthropicBaseURL,
+ AnthropicModel: cfg.AnthropicModel,
+ AnthropicTemperature: cfg.AnthropicTemperature,
}
// Prefer HEXAI_OPENAI_API_KEY; fall back to OPENAI_API_KEY
oaKey := os.Getenv("HEXAI_OPENAI_API_KEY")
@@ -137,7 +140,12 @@ func buildClientIfNil(cfg appconfig.App, client llm.Client) llm.Client {
if strings.TrimSpace(cpKey) == "" {
cpKey = os.Getenv("COPILOT_API_KEY")
}
- if c, err := llm.NewFromConfig(llmCfg, oaKey, orKey, cpKey); err != nil {
+ // Prefer HEXAI_ANTHROPIC_API_KEY; fall back to ANTHROPIC_API_KEY
+ anKey := os.Getenv("HEXAI_ANTHROPIC_API_KEY")
+ if strings.TrimSpace(anKey) == "" {
+ anKey = os.Getenv("ANTHROPIC_API_KEY")
+ }
+ if c, err := llm.NewFromConfig(llmCfg, oaKey, orKey, cpKey, anKey); err != nil {
logging.Logf("lsp ", "llm disabled: %v", err)
return nil
} else {
diff --git a/internal/llm/anthropic.go b/internal/llm/anthropic.go
new file mode 100644
index 0000000..6f14eea
--- /dev/null
+++ b/internal/llm/anthropic.go
@@ -0,0 +1,316 @@
+// Summary: Anthropic client implementation using Messages API with optional streaming support.
+package llm
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/http"
+ "strings"
+ "time"
+
+ "codeberg.org/snonux/hexai/internal/logging"
+)
+
+// anthropicClient implements Client against Anthropic's Messages API.
+type anthropicClient struct {
+ httpClient *http.Client
+ apiKey string
+ baseURL string
+ defaultModel string
+ chatLogger logging.ChatLogger
+ defaultTemperature *float64
+}
+
+type anthropicChatRequest struct {
+ Model string `json:"model"`
+ Messages []anthropicMessage `json:"messages"`
+ Temperature *float64 `json:"temperature,omitempty"`
+ MaxTokens int `json:"max_tokens"`
+ Stream bool `json:"stream,omitempty"`
+ System string `json:"system,omitempty"`
+}
+
+type anthropicMessage struct {
+ Role string `json:"role"`
+ Content string `json:"content"`
+}
+
+type anthropicChatResponse struct {
+ ID string `json:"id"`
+ Type string `json:"type"`
+ Content []struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ } `json:"content"`
+ StopReason string `json:"stop_reason"`
+ Error *struct {
+ Type string `json:"type"`
+ Message string `json:"message"`
+ } `json:"error,omitempty"`
+}
+
+// Streaming event types
+type anthropicStreamStart struct {
+ Type string `json:"type"`
+ Message struct {
+ ID string `json:"id"`
+ Type string `json:"type"`
+ Role string `json:"role"`
+ Model string `json:"model"`
+ } `json:"message"`
+}
+
+type anthropicStreamDelta struct {
+ Type string `json:"type"`
+ Delta struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ } `json:"delta"`
+}
+
+type anthropicStreamError struct {
+ Type string `json:"type"`
+ Error struct {
+ Type string `json:"type"`
+ Message string `json:"message"`
+ } `json:"error"`
+}
+
+// Constructor
+// newAnthropic constructs an Anthropic client using explicit configuration values.
+// The apiKey may be empty; calls will fail until a valid key is supplied.
+func newAnthropic(baseURL, model, apiKey string, defaultTemp *float64) Client {
+ if strings.TrimSpace(baseURL) == "" {
+ baseURL = "https://api.anthropic.com/v1"
+ }
+ if strings.TrimSpace(model) == "" {
+ model = "claude-3-5-sonnet-20241022"
+ }
+ return anthropicClient{
+ httpClient: &http.Client{Timeout: 30 * time.Second},
+ apiKey: apiKey,
+ baseURL: baseURL,
+ defaultModel: model,
+ chatLogger: logging.NewChatLogger("anthropic"),
+ defaultTemperature: defaultTemp,
+ }
+}
+
+func (c anthropicClient) Chat(ctx context.Context, messages []Message, opts ...RequestOption) (string, error) {
+ if c.apiKey == "" {
+ return nilStringErr("missing Anthropic API key")
+ }
+ o := Options{Model: c.defaultModel}
+ for _, opt := range opts {
+ opt(&o)
+ }
+ if o.Model == "" {
+ o.Model = c.defaultModel
+ }
+ start := time.Now()
+ c.logStart(false, o, messages)
+ req := buildAnthropicChatRequest(o, messages, c.defaultModel, c.defaultTemperature, false)
+ body, err := json.Marshal(req)
+ if err != nil {
+ c.logf("marshal error: %v", err)
+ return "", err
+ }
+ endpoint := c.baseURL + "/messages"
+ logging.Logf("llm/anthropic ", "POST %s", endpoint)
+ resp, err := c.doJSON(ctx, endpoint, body, map[string]string{
+ "x-api-key": c.apiKey,
+ "anthropic-version": "2023-06-01",
+ })
+ if err != nil {
+ logging.Logf("llm/anthropic ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
+ return "", err
+ }
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/anthropic", "failed to close response body: %v", err)
+ }
+ }()
+ if err := handleAnthropicNon2xx(resp, start); err != nil {
+ return "", err
+ }
+ out, err := decodeAnthropicChat(resp, start)
+ if err != nil {
+ return "", err
+ }
+ if len(out.Content) == 0 {
+ logging.Logf("llm/anthropic ", "%sno content returned duration=%s%s", logging.AnsiRed, time.Since(start), logging.AnsiBase)
+ return "", errors.New("anthropic: no content returned")
+ }
+ content := out.Content[0].Text
+ logging.Logf("llm/anthropic ", "success stop_reason=%s size=%d preview=%s%s%s duration=%s", out.StopReason, len(content), logging.AnsiGreen, logging.PreviewForLog(content), logging.AnsiBase, time.Since(start))
+ return content, nil
+}
+
+// Provider metadata
+func (c anthropicClient) Name() string { return "anthropic" }
+func (c anthropicClient) DefaultModel() string { return c.defaultModel }
+
+// Streaming support (optional)
+func (c anthropicClient) ChatStream(ctx context.Context, messages []Message, onDelta func(string), opts ...RequestOption) error {
+ if c.apiKey == "" {
+ return errors.New("missing Anthropic API key")
+ }
+ o := Options{Model: c.defaultModel}
+ for _, opt := range opts {
+ opt(&o)
+ }
+ if o.Model == "" {
+ o.Model = c.defaultModel
+ }
+ start := time.Now()
+ c.logStart(true, o, messages)
+ req := buildAnthropicChatRequest(o, messages, c.defaultModel, c.defaultTemperature, true)
+ body, err := json.Marshal(req)
+ if err != nil {
+ c.logf("marshal error: %v", err)
+ return err
+ }
+ endpoint := c.baseURL + "/messages"
+ logging.Logf("llm/anthropic ", "POST %s (stream)", endpoint)
+ resp, err := c.doJSON(ctx, endpoint, body, map[string]string{
+ "x-api-key": c.apiKey,
+ "anthropic-version": "2023-06-01",
+ })
+ if err != nil {
+ logging.Logf("llm/anthropic ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
+ return err
+ }
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/anthropic", "failed to close response body: %v", err)
+ }
+ }()
+ if err := handleAnthropicNon2xx(resp, start); err != nil {
+ return err
+ }
+
+ if err := parseAnthropicStream(resp, start, onDelta); err != nil {
+ return err
+ }
+ logging.Logf("llm/anthropic ", "stream end duration=%s", time.Since(start))
+ return nil
+}
+
+// Private helpers
+func (c anthropicClient) logf(format string, args ...any) {
+ logging.Logf("llm/anthropic ", format, args...)
+}
+
+func (c anthropicClient) logStart(stream bool, o Options, messages []Message) {
+ logMessages := make([]struct{ Role, Content string }, len(messages))
+ for i, m := range messages {
+ logMessages[i] = struct{ Role, Content string }{m.Role, m.Content}
+ }
+ c.chatLogger.LogStart(stream, o.Model, o.Temperature, o.MaxTokens, o.Stop, logMessages)
+}
+
+func buildAnthropicChatRequest(o Options, messages []Message, defaultModel string, defaultTemp *float64, stream bool) anthropicChatRequest {
+ req := anthropicChatRequest{
+ Model: o.Model,
+ Stream: stream,
+ MaxTokens: 4096, // Anthropic requires max_tokens
+ }
+ req.Messages = make([]anthropicMessage, len(messages))
+ for i, m := range messages {
+ req.Messages[i] = anthropicMessage{
+ Role: m.Role,
+ Content: m.Content,
+ }
+ }
+ if o.Temperature != 0 {
+ req.Temperature = &o.Temperature
+ } else if defaultTemp != nil {
+ t := *defaultTemp
+ req.Temperature = &t
+ }
+ if o.MaxTokens > 0 {
+ req.MaxTokens = o.MaxTokens
+ }
+ // Note: Anthropic's API doesn't support stop sequences in the same way as OpenAI,
+ // but we keep them in the request for future compatibility.
+ return req
+}
+
+func (c anthropicClient) doJSON(ctx context.Context, url string, body []byte, headers map[string]string) (*http.Response, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", "application/json")
+ for k, v := range headers {
+ req.Header.Set(k, v)
+ }
+ return c.httpClient.Do(req)
+}
+
+func handleAnthropicNon2xx(resp *http.Response, start time.Time) error {
+ if resp.StatusCode >= 200 && resp.StatusCode < 300 {
+ return nil
+ }
+ var apiErr anthropicChatResponse
+ _ = json.NewDecoder(resp.Body).Decode(&apiErr)
+ if apiErr.Error != nil && apiErr.Error.Message != "" {
+ logging.Logf("llm/anthropic ", "%sapi error status=%d type=%s msg=%s duration=%s%s", logging.AnsiRed, resp.StatusCode, apiErr.Error.Type, apiErr.Error.Message, time.Since(start), logging.AnsiBase)
+ return fmt.Errorf("anthropic error: %s (status %d)", apiErr.Error.Message, resp.StatusCode)
+ }
+ logging.Logf("llm/anthropic ", "%shttp non-2xx status=%d duration=%s%s", logging.AnsiRed, resp.StatusCode, time.Since(start), logging.AnsiBase)
+ return fmt.Errorf("anthropic http error: status %d", resp.StatusCode)
+}
+
+func decodeAnthropicChat(resp *http.Response, start time.Time) (anthropicChatResponse, error) {
+ var out anthropicChatResponse
+ if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
+ logging.Logf("llm/anthropic ", "%sdecode error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
+ return anthropicChatResponse{}, err
+ }
+ return out, nil
+}
+
+func parseAnthropicStream(resp *http.Response, start time.Time, onDelta func(string)) error {
+ // Parse server-sent events: lines starting with "data: " containing JSON
+ scanner := bufio.NewScanner(resp.Body)
+ const maxBuf = 1024 * 1024
+ buf := make([]byte, 0, 64*1024)
+ scanner.Buffer(buf, maxBuf)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if !strings.HasPrefix(line, "data: ") {
+ continue
+ }
+ payload := strings.TrimPrefix(line, "data: ")
+ // Check for stream end event
+ if strings.Contains(payload, "\"type\":\"message_stop\"") {
+ break
+ }
+ // Try to parse as delta event
+ var delta anthropicStreamDelta
+ if err := json.Unmarshal([]byte(payload), &delta); err != nil {
+ continue
+ }
+ if delta.Type == "content_block_delta" && delta.Delta.Type == "text_delta" && delta.Delta.Text != "" {
+ onDelta(delta.Delta.Text)
+ }
+ // Check for errors in stream
+ var errEvent anthropicStreamError
+ if err := json.Unmarshal([]byte(payload), &errEvent); err == nil {
+ if errEvent.Type == "error" && errEvent.Error.Message != "" {
+ logging.Logf("llm/anthropic ", "%sstream error: %s%s", logging.AnsiRed, errEvent.Error.Message, logging.AnsiBase)
+ return fmt.Errorf("anthropic stream error: %s", errEvent.Error.Message)
+ }
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ logging.Logf("llm/anthropic ", "%sstream read error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
+ return err
+ }
+ return nil
+}
diff --git a/internal/llm/anthropic_test.go b/internal/llm/anthropic_test.go
new file mode 100644
index 0000000..15756f5
--- /dev/null
+++ b/internal/llm/anthropic_test.go
@@ -0,0 +1,259 @@
+package llm
+
+import (
+ "context"
+ "encoding/json"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+)
+
+func TestAnthropicChat_Success(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ t.Fatalf("expected POST, got %s", r.Method)
+ }
+ if !strings.HasSuffix(r.URL.Path, "/messages") {
+ t.Fatalf("expected /messages endpoint, got %s", r.URL.Path)
+ }
+ // Check headers
+ if r.Header.Get("x-api-key") != "test-key" {
+ t.Fatalf("expected x-api-key header")
+ }
+ if r.Header.Get("anthropic-version") != "2023-06-01" {
+ t.Fatalf("expected anthropic-version header")
+ }
+ // Verify request body
+ var req anthropicChatRequest
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ t.Fatalf("failed to decode request: %v", err)
+ }
+ if req.Model != "claude-3-5-sonnet-20241022" {
+ t.Fatalf("expected model claude-3-5-sonnet-20241022, got %s", req.Model)
+ }
+ if len(req.Messages) != 1 {
+ t.Fatalf("expected 1 message, got %d", len(req.Messages))
+ }
+ if req.Messages[0].Role != "user" {
+ t.Fatalf("expected user role, got %s", req.Messages[0].Role)
+ }
+ if req.Messages[0].Content != "Hello" {
+ t.Fatalf("expected content 'Hello', got '%s'", req.Messages[0].Content)
+ }
+ // Send response
+ resp := anthropicChatResponse{
+ ID: "msg-123",
+ Type: "message",
+ StopReason: "end_turn",
+ Content: []struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ }{
+ {Type: "text", Text: "Hi there!"},
+ },
+ }
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(resp)
+ }))
+ defer srv.Close()
+
+ c := newAnthropic(srv.URL, "claude-3-5-sonnet-20241022", "test-key", nil).(anthropicClient)
+ response, err := c.Chat(context.Background(), []Message{
+ {Role: "user", Content: "Hello"},
+ })
+ if err != nil {
+ t.Fatalf("Chat failed: %v", err)
+ }
+ if response != "Hi there!" {
+ t.Fatalf("expected 'Hi there!', got '%s'", response)
+ }
+}
+
+func TestAnthropicChat_NoAPIKey(t *testing.T) {
+ c := newAnthropic("https://api.anthropic.com/v1", "claude-3-5-sonnet-20241022", "", nil)
+ _, err := c.Chat(context.Background(), []Message{
+ {Role: "user", Content: "Hello"},
+ })
+ if err == nil {
+ t.Fatalf("expected error for missing API key")
+ }
+ if !strings.Contains(err.Error(), "missing Anthropic API key") {
+ t.Fatalf("expected 'missing Anthropic API key', got '%s'", err.Error())
+ }
+}
+
+func TestAnthropicChat_APIError(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusUnauthorized)
+ resp := anthropicChatResponse{
+ Error: &struct {
+ Type string `json:"type"`
+ Message string `json:"message"`
+ }{
+ Type: "authentication_error",
+ Message: "Invalid API key",
+ },
+ }
+ json.NewEncoder(w).Encode(resp)
+ }))
+ defer srv.Close()
+
+ c := newAnthropic(srv.URL, "claude-3-5-sonnet-20241022", "invalid-key", nil).(anthropicClient)
+ _, err := c.Chat(context.Background(), []Message{
+ {Role: "user", Content: "Hello"},
+ })
+ if err == nil {
+ t.Fatalf("expected error for API error response")
+ }
+ if !strings.Contains(err.Error(), "Invalid API key") {
+ t.Fatalf("expected 'Invalid API key' in error, got '%s'", err.Error())
+ }
+}
+
+func TestAnthropicChat_EmptyResponse(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ resp := anthropicChatResponse{
+ ID: "msg-123",
+ Type: "message",
+ StopReason: "end_turn",
+ Content: []struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ }{},
+ }
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(resp)
+ }))
+ defer srv.Close()
+
+ c := newAnthropic(srv.URL, "claude-3-5-sonnet-20241022", "test-key", nil).(anthropicClient)
+ _, err := c.Chat(context.Background(), []Message{
+ {Role: "user", Content: "Hello"},
+ })
+ if err == nil {
+ t.Fatalf("expected error for empty content")
+ }
+ if !strings.Contains(err.Error(), "no content returned") {
+ t.Fatalf("expected 'no content returned', got '%s'", err.Error())
+ }
+}
+
+func TestAnthropicChat_WithTemperature(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ var req anthropicChatRequest
+ json.NewDecoder(r.Body).Decode(&req)
+ if req.Temperature == nil || *req.Temperature != 0.5 {
+ t.Fatalf("expected temperature 0.5, got %v", req.Temperature)
+ }
+ resp := anthropicChatResponse{
+ ID: "msg-123",
+ Type: "message",
+ StopReason: "end_turn",
+ Content: []struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ }{
+ {Type: "text", Text: "Response"},
+ },
+ }
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(resp)
+ }))
+ defer srv.Close()
+
+ c := newAnthropic(srv.URL, "claude-3-5-sonnet-20241022", "test-key", nil).(anthropicClient)
+ _, err := c.Chat(context.Background(), []Message{
+ {Role: "user", Content: "Hello"},
+ }, WithTemperature(0.5))
+ if err != nil {
+ t.Fatalf("Chat failed: %v", err)
+ }
+}
+
+func TestAnthropicStream_Success(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/event-stream")
+ // Send streaming response
+ streamEvents := []string{
+ `data: {"type":"message_start","message":{"id":"msg-123","type":"message"}}`,
+ `data: {"type":"content_block_start","content_block":{"type":"text"}}`,
+ `data: {"type":"content_block_delta","delta":{"type":"text_delta","text":"Hello"}}`,
+ `data: {"type":"content_block_delta","delta":{"type":"text_delta","text":" "}}`,
+ `data: {"type":"content_block_delta","delta":{"type":"text_delta","text":"world"}}`,
+ `data: {"type":"message_stop"}`,
+ }
+ for _, event := range streamEvents {
+ io.WriteString(w, event+"\n")
+ }
+ }))
+ defer srv.Close()
+
+ c := newAnthropic(srv.URL, "claude-3-5-sonnet-20241022", "test-key", nil)
+ streamer, ok := c.(Streamer)
+ if !ok {
+ t.Fatalf("Anthropic client does not implement Streamer interface")
+ }
+ var chunks []string
+ err := streamer.ChatStream(context.Background(), []Message{
+ {Role: "user", Content: "Say hello"},
+ }, func(chunk string) {
+ chunks = append(chunks, chunk)
+ })
+ if err != nil {
+ t.Fatalf("ChatStream failed: %v", err)
+ }
+ if len(chunks) != 3 {
+ t.Fatalf("expected 3 chunks, got %d", len(chunks))
+ }
+ if chunks[0] != "Hello" || chunks[1] != " " || chunks[2] != "world" {
+ t.Fatalf("unexpected chunks: %v", chunks)
+ }
+}
+
+func TestAnthropicStream_NoAPIKey(t *testing.T) {
+ c := newAnthropic("https://api.anthropic.com/v1", "claude-3-5-sonnet-20241022", "", nil)
+ streamer, ok := c.(Streamer)
+ if !ok {
+ t.Fatalf("Anthropic client does not implement Streamer interface")
+ }
+ err := streamer.ChatStream(context.Background(), []Message{
+ {Role: "user", Content: "Hello"},
+ }, func(chunk string) {})
+ if err == nil {
+ t.Fatalf("expected error for missing API key")
+ }
+ if !strings.Contains(err.Error(), "missing Anthropic API key") {
+ t.Fatalf("expected 'missing Anthropic API key', got '%s'", err.Error())
+ }
+}
+
+func TestAnthropicClient_Name(t *testing.T) {
+ c := newAnthropic("https://api.anthropic.com/v1", "claude-3-5-sonnet-20241022", "test-key", nil)
+ if c.Name() != "anthropic" {
+ t.Fatalf("expected 'anthropic', got '%s'", c.Name())
+ }
+}
+
+func TestAnthropicClient_DefaultModel(t *testing.T) {
+ model := "claude-3-opus-20250219"
+ c := newAnthropic("https://api.anthropic.com/v1", model, "test-key", nil).(anthropicClient)
+ if c.DefaultModel() != model {
+ t.Fatalf("expected '%s', got '%s'", model, c.DefaultModel())
+ }
+}
+
+func TestAnthropicClient_DefaultBaseURL(t *testing.T) {
+ c := newAnthropic("", "claude-3-5-sonnet-20241022", "test-key", nil).(anthropicClient)
+ if c.baseURL != "https://api.anthropic.com/v1" {
+ t.Fatalf("expected default base URL, got '%s'", c.baseURL)
+ }
+}
+
+func TestAnthropicClient_DefaultModel_Empty(t *testing.T) {
+ c := newAnthropic("https://api.anthropic.com/v1", "", "test-key", nil).(anthropicClient)
+ if c.defaultModel != "claude-3-5-sonnet-20241022" {
+ t.Fatalf("expected default model, got '%s'", c.defaultModel)
+ }
+}
diff --git a/internal/llm/openai_temp_test.go b/internal/llm/openai_temp_test.go
index 3d71b94..07abbd5 100644
--- a/internal/llm/openai_temp_test.go
+++ b/internal/llm/openai_temp_test.go
@@ -5,7 +5,7 @@ import "testing"
func TestNewFromConfig_DefaultTemp_ByModel(t *testing.T) {
// OpenAI, gpt-5.* → default temp 1.0 when not provided
cfg := Config{Provider: "openai", OpenAIModel: "gpt-5.0-preview"}
- c, err := NewFromConfig(cfg, "key", "", "")
+ c, err := NewFromConfig(cfg, "key", "", "", "")
if err != nil {
t.Fatalf("new: %v", err)
}
@@ -18,7 +18,7 @@ func TestNewFromConfig_DefaultTemp_ByModel(t *testing.T) {
}
// OpenAI, gpt-4.* → default temp 0.2 when not provided
cfg2 := Config{Provider: "openai", OpenAIModel: "gpt-4.1"}
- c2, err := NewFromConfig(cfg2, "key", "", "")
+ c2, err := NewFromConfig(cfg2, "key", "", "", "")
if err != nil {
t.Fatalf("new2: %v", err)
}
@@ -32,7 +32,7 @@ func TestNewFromConfig_DefaultTemp_UpgradeWhenGpt5AndDefault02(t *testing.T) {
// Simulate app-default of 0.2 while selecting a gpt-5 model: should upgrade to 1.0
v := 0.2
cfg := Config{Provider: "openai", OpenAIModel: "gpt-5.0", OpenAITemperature: &v}
- c, err := NewFromConfig(cfg, "key", "", "")
+ c, err := NewFromConfig(cfg, "key", "", "", "")
if err != nil {
t.Fatalf("new: %v", err)
}
diff --git a/internal/llm/provider.go b/internal/llm/provider.go
index b2c47e4..ae840b0 100644
--- a/internal/llm/provider.go
+++ b/internal/llm/provider.go
@@ -81,12 +81,16 @@ type Config struct {
CopilotBaseURL string
CopilotModel string
CopilotTemperature *float64
+ // Anthropic options
+ AnthropicBaseURL string
+ AnthropicModel string
+ AnthropicTemperature *float64
}
// NewFromConfig creates an LLM client using only the supplied configuration.
// The OpenAI API key is supplied separately and may be read from the environment
// by the caller; other environment-based configuration is not used.
-func NewFromConfig(cfg Config, openAIAPIKey, openRouterAPIKey, copilotAPIKey string) (Client, error) {
+func NewFromConfig(cfg Config, openAIAPIKey, openRouterAPIKey, copilotAPIKey, anthropicAPIKey string) (Client, error) {
p := strings.ToLower(strings.TrimSpace(cfg.Provider))
if p == "" {
p = "openai"
@@ -140,6 +144,15 @@ func NewFromConfig(cfg Config, openAIAPIKey, openRouterAPIKey, copilotAPIKey str
cfg.CopilotTemperature = &t
}
return newCopilot(cfg.CopilotBaseURL, cfg.CopilotModel, copilotAPIKey, cfg.CopilotTemperature), nil
+ case "anthropic":
+ if strings.TrimSpace(anthropicAPIKey) == "" {
+ return nil, errors.New("missing ANTHROPIC_API_KEY for provider anthropic")
+ }
+ if cfg.AnthropicTemperature == nil {
+ t := 0.2
+ cfg.AnthropicTemperature = &t
+ }
+ return newAnthropic(cfg.AnthropicBaseURL, cfg.AnthropicModel, anthropicAPIKey, cfg.AnthropicTemperature), nil
default:
return nil, errors.New("unknown LLM provider: " + p)
}
diff --git a/internal/llm/provider_more2_test.go b/internal/llm/provider_more2_test.go
index e001e5c..86b149a 100644
--- a/internal/llm/provider_more2_test.go
+++ b/internal/llm/provider_more2_test.go
@@ -5,7 +5,7 @@ import "testing"
func TestNewFromConfig_Copilot(t *testing.T) {
t.Setenv("COPILOT_API_KEY", "x")
cfg := Config{Provider: "copilot", CopilotModel: "small"}
- c, err := NewFromConfig(cfg, "", "", "x")
+ c, err := NewFromConfig(cfg, "", "", "x", "")
if err != nil || c == nil {
t.Fatalf("copilot provider failed: %v %v", c, err)
}
diff --git a/internal/llm/provider_more_test.go b/internal/llm/provider_more_test.go
index eff99e6..caad912 100644
--- a/internal/llm/provider_more_test.go
+++ b/internal/llm/provider_more_test.go
@@ -16,13 +16,13 @@ func TestWithOptions_Apply(t *testing.T) {
func TestNewFromConfig_Success_OpenAI_And_Copilot(t *testing.T) {
// OpenAI success
oc := Config{Provider: "openai", OpenAIBaseURL: "http://x", OpenAIModel: "gpt"}
- c, err := NewFromConfig(oc, "KEY", "", "")
+ c, err := NewFromConfig(oc, "KEY", "", "", "")
if err != nil || c == nil || c.Name() != "openai" || c.DefaultModel() == "" {
t.Fatalf("openai new: %v %v", c, err)
}
// Copilot success
cc := Config{Provider: "copilot", CopilotBaseURL: "http://x", CopilotModel: "gpt-4o-mini"}
- c2, err := NewFromConfig(cc, "", "", "KEY")
+ c2, err := NewFromConfig(cc, "", "", "KEY", "")
if err != nil || c2 == nil || c2.Name() != "copilot" || c2.DefaultModel() == "" {
t.Fatalf("copilot new: %v %v", c2, err)
}
diff --git a/internal/llm/provider_test.go b/internal/llm/provider_test.go
index 8c5d2cb..bd565b3 100644
--- a/internal/llm/provider_test.go
+++ b/internal/llm/provider_test.go
@@ -6,15 +6,15 @@ import (
func TestNewFromConfig_DefaultsAndErrors(t *testing.T) {
// Unknown provider
- if _, err := NewFromConfig(Config{Provider: "bogus"}, "", "", ""); err == nil {
+ if _, err := NewFromConfig(Config{Provider: "bogus"}, "", "", "", ""); err == nil {
t.Fatalf("expected error for unknown provider")
}
// OpenAI missing key
- if _, err := NewFromConfig(Config{Provider: "openai", OpenAIModel: "g"}, "", "", ""); err == nil {
+ if _, err := NewFromConfig(Config{Provider: "openai", OpenAIModel: "g"}, "", "", "", ""); err == nil {
t.Fatalf("expected key error")
}
// Copilot missing key
- if _, err := NewFromConfig(Config{Provider: "copilot", CopilotModel: "m"}, "", "", ""); err == nil {
+ if _, err := NewFromConfig(Config{Provider: "copilot", CopilotModel: "m"}, "", "", "", ""); err == nil {
t.Fatalf("expected key error")
}
}
diff --git a/internal/llmutils/client.go b/internal/llmutils/client.go
index 2f3da55..53fca9c 100644
--- a/internal/llmutils/client.go
+++ b/internal/llmutils/client.go
@@ -24,6 +24,9 @@ func NewClientFromApp(cfg appconfig.App) (llm.Client, error) {
CopilotBaseURL: cfg.CopilotBaseURL,
CopilotModel: cfg.CopilotModel,
CopilotTemperature: cfg.CopilotTemperature,
+ AnthropicBaseURL: cfg.AnthropicBaseURL,
+ AnthropicModel: cfg.AnthropicModel,
+ AnthropicTemperature: cfg.AnthropicTemperature,
}
oaKey := os.Getenv("HEXAI_OPENAI_API_KEY")
if strings.TrimSpace(oaKey) == "" {
@@ -37,5 +40,9 @@ func NewClientFromApp(cfg appconfig.App) (llm.Client, error) {
if strings.TrimSpace(cpKey) == "" {
cpKey = os.Getenv("COPILOT_API_KEY")
}
- return llm.NewFromConfig(llmCfg, oaKey, orKey, cpKey)
+ anKey := os.Getenv("HEXAI_ANTHROPIC_API_KEY")
+ if strings.TrimSpace(anKey) == "" {
+ anKey = os.Getenv("ANTHROPIC_API_KEY")
+ }
+ return llm.NewFromConfig(llmCfg, oaKey, orKey, cpKey, anKey)
}
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index e3a21f3..67e3cab 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -230,6 +230,9 @@ func newClientForProvider(cfg appconfig.App, provider string) (llm.Client, error
CopilotBaseURL: cfg.CopilotBaseURL,
CopilotModel: cfg.CopilotModel,
CopilotTemperature: cfg.CopilotTemperature,
+ AnthropicBaseURL: cfg.AnthropicBaseURL,
+ AnthropicModel: cfg.AnthropicModel,
+ AnthropicTemperature: cfg.AnthropicTemperature,
}
oaKey := strings.TrimSpace(os.Getenv("HEXAI_OPENAI_API_KEY"))
if oaKey == "" {
@@ -243,7 +246,11 @@ func newClientForProvider(cfg appconfig.App, provider string) (llm.Client, error
if cpKey == "" {
cpKey = strings.TrimSpace(os.Getenv("COPILOT_API_KEY"))
}
- return llm.NewFromConfig(llmCfg, oaKey, orKey, cpKey)
+ anKey := strings.TrimSpace(os.Getenv("HEXAI_ANTHROPIC_API_KEY"))
+ if anKey == "" {
+ anKey = strings.TrimSpace(os.Getenv("ANTHROPIC_API_KEY"))
+ }
+ return llm.NewFromConfig(llmCfg, oaKey, orKey, cpKey, anKey)
}
func (s *Server) clientFor(spec requestSpec) llm.Client {
diff --git a/project.d2 b/project.d2
new file mode 100644
index 0000000..b6ebde2
--- /dev/null
+++ b/project.d2
@@ -0,0 +1,244 @@
+
+direction: right
+"hexai": {
+ shape: package
+ ".gitignore"
+ "AGENTS.md"
+ "config.toml.example"
+ "go.mod"
+ "go.sum"
+ "hexai-small.png"
+ "hexai.png"
+ "Magefile.go"
+ "README.md"
+ "SCRATCHPAD.md"
+ "bin": {
+ shape: package
+ }
+ "cmd": {
+ shape: package
+ "hexai": {
+ shape: package
+ "main.go"
+ "main_test.go"
+ }
+ "hexai-action": {
+ shape: package
+ }
+ "hexai-lsp": {
+ shape: package
+ "main.go"
+ "main_test.go"
+ }
+ "hexai-tmux-action": {
+ shape: package
+ "main.go"
+ }
+ "internal": {
+ shape: package
+ "hexai-action": {
+ shape: package
+ }
+ }
+ }
+ "docs": {
+ shape: package
+ "buildandinstall.md"
+ "configuration.md"
+ "coverage.html"
+ "coverage.out"
+ "custom-code-actions.md"
+ "tmux-status-bar.png"
+ "tmux.md"
+ "usage.md"
+ }
+ "internal": {
+ shape: package
+ "version.go"
+ "appconfig": {
+ shape: package
+ "config.go"
+ "config_alias_test.go"
+ "config_env_model_test.go"
+ "config_test.go"
+ "custom_validation_more_test.go"
+ }
+ "editor": {
+ shape: package
+ "editor.go"
+ "editor_test.go"
+ }
+ "hexaiaction": {
+ shape: package
+ "cmdentry.go"
+ "cmdentry_runcommand_test.go"
+ "cmdentry_test.go"
+ "custom_action_test.go"
+ "custom_exec_more_test.go"
+ "custom_exec_test.go"
+ "parse.go"
+ "parse_test.go"
+ "prompts.go"
+ "prompts_more_test.go"
+ "prompts_simplify_test.go"
+ "run.go"
+ "run_more_test.go"
+ "run_seam_test.go"
+ "run_test.go"
+ "tui.go"
+ "tui_custom.go"
+ "tui_custom_test.go"
+ "tui_delegate.go"
+ "tui_delegate_test.go"
+ "tui_test.go"
+ "types.go"
+ }
+ "hexaicli": {
+ shape: package
+ "editor_integration_test.go"
+ "run.go"
+ "run_editor_behavior_test.go"
+ "run_model_override_test.go"
+ "run_more_test.go"
+ "run_test.go"
+ "testhelpers_test.go"
+ }
+ "hexailsp": {
+ shape: package
+ "run.go"
+ "run_more_test.go"
+ "run_test.go"
+ }
+ "llm": {
+ shape: package
+ "copilot.go"
+ "copilot_http_test.go"
+ "copilot_test.go"
+ "ollama.go"
+ "ollama_test.go"
+ "openai.go"
+ "openai_http_test.go"
+ "openai_request_test.go"
+ "openai_sse_negative_test.go"
+ "openai_temp_test.go"
+ "openai_test.go"
+ "openrouter.go"
+ "openrouter_test.go"
+ "provider.go"
+ "provider_more2_test.go"
+ "provider_more_test.go"
+ "provider_test.go"
+ "test_helpers_test.go"
+ "util.go"
+ "util_test.go"
+ }
+ "llmutils": {
+ shape: package
+ "client.go"
+ "client_test.go"
+ }
+ "logging": {
+ shape: package
+ "chatlogger.go"
+ "logging.go"
+ "logging_test.go"
+ }
+ "lsp": {
+ shape: package
+ "build_prompts_table_test.go"
+ "chat_commands.go"
+ "chat_commands_test.go"
+ "chat_context_mode_test.go"
+ "chat_history_test.go"
+ "chat_no_double_answer_test.go"
+ "chat_prompt_test.go"
+ "chat_trigger_suppression_test.go"
+ "code_fences_table_test.go"
+ "codeaction_custom_errors_test.go"
+ "codeaction_custom_test.go"
+ "codeaction_gotest_int_test.go"
+ "codeaction_more_test.go"
+ "codeaction_prompts_test.go"
+ "codeaction_test.go"
+ "codegen_helpers_test.go"
+ "completion_cache_test.go"
+ "completion_codex_path_test.go"
+ "completion_helpers_more_test.go"
+ "completion_messages_test.go"
+ "completion_prefix_strip_test.go"
+ "completion_provider_fallback_test.go"
+ "completion_toggle_test.go"
+ "compute_textedit_table_test.go"
+ "context.go"
+ "context_test.go"
+ "coverage_add_test.go"
+ "debounce_throttle_more_test.go"
+ "debounce_throttle_test.go"
+ "diagnostics_action_test.go"
+ "document.go"
+ "document_handlers_test.go"
+ "document_test.go"
+ "fallback_items_test.go"
+ "gotest_append_test.go"
+ "handlers.go"
+ "handlers_codeaction.go"
+ "handlers_completion.go"
+ "handlers_document.go"
+ "handlers_end_to_end_test.go"
+ "handlers_execute.go"
+ "handlers_helpers_test.go"
+ "handlers_init.go"
+ "handlers_init_more_test.go"
+ "handlers_test.go"
+ "handlers_utils.go"
+ "helpers_inline_prompt_test.go"
+ "helpers_more_test.go"
+ "init_and_trigger_test.go"
+ "init_shutdown_test.go"
+ "inline_prompt_completion_test.go"
+ "instruction_table_test.go"
+ "label_filter_table_test.go"
+ "llm_request_opts_test.go"
+ "llm_stats_test.go"
+ "log_context_test.go"
+ "postprocess_indent_test.go"
+ "prefix_table_test.go"
+ "provider_native_success_test.go"
+ "rewrite_diagnostics_realism_test.go"
+ "server.go"
+ "server_test.go"
+ "testfakes_test.go"
+ "testhelper_capture_llm_test.go"
+ "transport.go"
+ "transport_concurrency_test.go"
+ "transport_test.go"
+ "triggers_config_test.go"
+ "types.go"
+ }
+ "runtimeconfig": {
+ shape: package
+ "store.go"
+ "store_test.go"
+ }
+ "stats": {
+ shape: package
+ "debugstring_test.go"
+ "lock_posix.go"
+ "lock_windows.go"
+ "stats.go"
+ "stats_test.go"
+ }
+ "testutil": {
+ shape: package
+ }
+ "textutil": {
+ shape: package
+ }
+ "tmux": {
+ shape: package
+ }
+ }
+ "llminputs": {
+ shape: package
+ }
+}
diff --git a/project.svg b/project.svg
new file mode 100644
index 0000000..205fcb2
--- /dev/null
+++ b/project.svg
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-d2-version="v0.7.1" preserveAspectRatio="xMinYMin meet" viewBox="0 0 820 23240"><svg class="d2-3145263268 d2-svg" width="820" height="23240" viewBox="-101 -95 820 23240"><rect x="-101.000000" y="-95.000000" width="820.000000" height="23240.000000" rx="0.000000" fill="#FFFFFF" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
+.d2-3145263268 .text {
+ font-family: "d2-3145263268-font-regular";
+}
+@font-face {
+ font-family: d2-3145263268-font-regular;
+ src: url("data:application/font-woff;base64,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");
+}
+.d2-3145263268 .text-bold {
+ font-family: "d2-3145263268-font-bold";
+}
+@font-face {
+ font-family: d2-3145263268-font-bold;
+ src: url("data:application/font-woff;base64,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");
+}]]></style><style type="text/css"><![CDATA[.shape {
+ shape-rendering: geometricPrecision;
+ stroke-linejoin: round;
+}
+.connection {
+ stroke-linecap: round;
+ stroke-linejoin: round;
+}
+.blend {
+ mix-blend-mode: multiply;
+ opacity: 0.5;
+}
+
+ .d2-3145263268 .fill-N1{fill:#0A0F25;}
+ .d2-3145263268 .fill-N2{fill:#676C7E;}
+ .d2-3145263268 .fill-N3{fill:#9499AB;}
+ .d2-3145263268 .fill-N4{fill:#CFD2DD;}
+ .d2-3145263268 .fill-N5{fill:#DEE1EB;}
+ .d2-3145263268 .fill-N6{fill:#EEF1F8;}
+ .d2-3145263268 .fill-N7{fill:#FFFFFF;}
+ .d2-3145263268 .fill-B1{fill:#0D32B2;}
+ .d2-3145263268 .fill-B2{fill:#0D32B2;}
+ .d2-3145263268 .fill-B3{fill:#E3E9FD;}
+ .d2-3145263268 .fill-B4{fill:#E3E9FD;}
+ .d2-3145263268 .fill-B5{fill:#EDF0FD;}
+ .d2-3145263268 .fill-B6{fill:#F7F8FE;}
+ .d2-3145263268 .fill-AA2{fill:#4A6FF3;}
+ .d2-3145263268 .fill-AA4{fill:#EDF0FD;}
+ .d2-3145263268 .fill-AA5{fill:#F7F8FE;}
+ .d2-3145263268 .fill-AB4{fill:#EDF0FD;}
+ .d2-3145263268 .fill-AB5{fill:#F7F8FE;}
+ .d2-3145263268 .stroke-N1{stroke:#0A0F25;}
+ .d2-3145263268 .stroke-N2{stroke:#676C7E;}
+ .d2-3145263268 .stroke-N3{stroke:#9499AB;}
+ .d2-3145263268 .stroke-N4{stroke:#CFD2DD;}
+ .d2-3145263268 .stroke-N5{stroke:#DEE1EB;}
+ .d2-3145263268 .stroke-N6{stroke:#EEF1F8;}
+ .d2-3145263268 .stroke-N7{stroke:#FFFFFF;}
+ .d2-3145263268 .stroke-B1{stroke:#0D32B2;}
+ .d2-3145263268 .stroke-B2{stroke:#0D32B2;}
+ .d2-3145263268 .stroke-B3{stroke:#E3E9FD;}
+ .d2-3145263268 .stroke-B4{stroke:#E3E9FD;}
+ .d2-3145263268 .stroke-B5{stroke:#EDF0FD;}
+ .d2-3145263268 .stroke-B6{stroke:#F7F8FE;}
+ .d2-3145263268 .stroke-AA2{stroke:#4A6FF3;}
+ .d2-3145263268 .stroke-AA4{stroke:#EDF0FD;}
+ .d2-3145263268 .stroke-AA5{stroke:#F7F8FE;}
+ .d2-3145263268 .stroke-AB4{stroke:#EDF0FD;}
+ .d2-3145263268 .stroke-AB5{stroke:#F7F8FE;}
+ .d2-3145263268 .background-color-N1{background-color:#0A0F25;}
+ .d2-3145263268 .background-color-N2{background-color:#676C7E;}
+ .d2-3145263268 .background-color-N3{background-color:#9499AB;}
+ .d2-3145263268 .background-color-N4{background-color:#CFD2DD;}
+ .d2-3145263268 .background-color-N5{background-color:#DEE1EB;}
+ .d2-3145263268 .background-color-N6{background-color:#EEF1F8;}
+ .d2-3145263268 .background-color-N7{background-color:#FFFFFF;}
+ .d2-3145263268 .background-color-B1{background-color:#0D32B2;}
+ .d2-3145263268 .background-color-B2{background-color:#0D32B2;}
+ .d2-3145263268 .background-color-B3{background-color:#E3E9FD;}
+ .d2-3145263268 .background-color-B4{background-color:#E3E9FD;}
+ .d2-3145263268 .background-color-B5{background-color:#EDF0FD;}
+ .d2-3145263268 .background-color-B6{background-color:#F7F8FE;}
+ .d2-3145263268 .background-color-AA2{background-color:#4A6FF3;}
+ .d2-3145263268 .background-color-AA4{background-color:#EDF0FD;}
+ .d2-3145263268 .background-color-AA5{background-color:#F7F8FE;}
+ .d2-3145263268 .background-color-AB4{background-color:#EDF0FD;}
+ .d2-3145263268 .background-color-AB5{background-color:#F7F8FE;}
+ .d2-3145263268 .color-N1{color:#0A0F25;}
+ .d2-3145263268 .color-N2{color:#676C7E;}
+ .d2-3145263268 .color-N3{color:#9499AB;}
+ .d2-3145263268 .color-N4{color:#CFD2DD;}
+ .d2-3145263268 .color-N5{color:#DEE1EB;}
+ .d2-3145263268 .color-N6{color:#EEF1F8;}
+ .d2-3145263268 .color-N7{color:#FFFFFF;}
+ .d2-3145263268 .color-B1{color:#0D32B2;}
+ .d2-3145263268 .color-B2{color:#0D32B2;}
+ .d2-3145263268 .color-B3{color:#E3E9FD;}
+ .d2-3145263268 .color-B4{color:#E3E9FD;}
+ .d2-3145263268 .color-B5{color:#EDF0FD;}
+ .d2-3145263268 .color-B6{color:#F7F8FE;}
+ .d2-3145263268 .color-AA2{color:#4A6FF3;}
+ .d2-3145263268 .color-AA4{color:#EDF0FD;}
+ .d2-3145263268 .color-AA5{color:#F7F8FE;}
+ .d2-3145263268 .color-AB4{color:#EDF0FD;}
+ .d2-3145263268 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-3145263268);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker-d2-3145263268);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-3145263268);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-3145263268);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-3145263268);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-3145263268);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-3145263268);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-3145263268);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><g class="aGV4YWk="><g class="shape" ><path d="M 0 46 L 150 46 L 150 101 L 618 101 L 618 23044 L 0 23044 Z" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-AA4" style="stroke-width:2;" /></g><text x="309.000000" y="33.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:28px">hexai</text></g><g class="aGV4YWkuJiMzNDsuZ2l0aWdub3JlJiMzNDs="><g class="shape" ><rect x="252.000000" y="86.000000" width="115.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="124.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">.gitignore</text></g><g class="aGV4YWkuJiMzNDtBR0VOVFMubWQmIzM0Ow=="><g class="shape" ><rect x="244.000000" y="212.000000" width="131.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="250.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">AGENTS.md</text></g><g class="aGV4YWkuJiMzNDtjb25maWcudG9tbC5leGFtcGxlJiMzNDs="><g class="shape" ><rect x="213.000000" y="338.000000" width="193.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="376.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">config.toml.example</text></g><g class="aGV4YWkuJiMzNDtnby5tb2QmIzM0Ow=="><g class="shape" ><rect x="260.000000" y="464.000000" width="99.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="502.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">go.mod</text></g><g class="aGV4YWkuJiMzNDtnby5zdW0mIzM0Ow=="><g class="shape" ><rect x="261.000000" y="590.000000" width="97.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="628.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">go.sum</text></g><g class="aGV4YWkuJiMzNDtoZXhhaS1zbWFsbC5wbmcmIzM0Ow=="><g class="shape" ><rect x="230.000000" y="716.000000" width="159.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="754.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">hexai-small.png</text></g><g class="aGV4YWkuJiMzNDtoZXhhaS5wbmcmIzM0Ow=="><g class="shape" ><rect x="252.000000" y="842.000000" width="115.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="880.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">hexai.png</text></g><g class="aGV4YWkuJiMzNDtNYWdlZmlsZS5nbyYjMzQ7"><g class="shape" ><rect x="246.000000" y="968.000000" width="127.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="1006.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Magefile.go</text></g><g class="aGV4YWkuJiMzNDtSRUFETUUubWQmIzM0Ow=="><g class="shape" ><rect x="244.000000" y="1094.000000" width="131.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.500000" y="1132.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">README.md</text></g><g class="aGV4YWkuJiMzNDtTQ1JBVENIUEFELm1kJiMzNDs="><g class="shape" ><rect x="225.000000" y="1220.000000" width="168.000000" height="66.000000" stroke="#0D32B2" fill="#EDF0FD" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="309.000000" y="1258.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">SCRATCHPAD.md</text></g><g class="aGV4YWkuYmlu"><g class="shape" ><path d="M 276 1346 L 310 1346 L 310 1361 L 343 1361 L 343 1419 L 276 1419 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.500000" y="1395.300000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">bin</text></g><g class="aGV4YWkuY21k"><g class="shape" ><path d="M 50 1500 L 200 1500 L 200 1555 L 568 1555 L 568 2720 L 50 2720 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="1488.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:24px">cmd</text></g><g class="aGV4YWkuZG9jcw=="><g class="shape" ><path d="M 50 2914 L 200 2914 L 200 2969 L 568 2969 L 568 3942 L 50 3942 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="2902.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:24px">docs</text></g><g class="aGV4YWkuaW50ZXJuYWw="><g class="shape" ><path d="M 50 4003 L 200 4003 L 200 4058 L 568 4058 L 568 23024 L 50 23024 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="3991.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:24px">internal</text></g><g class="aGV4YWkubGxtaW5wdXRz"><g class="shape" ><path d="M 253 2760 L 309 2760 L 309 2775 L 365 2775 L 365 2833 L 253 2833 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="2809.300000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">llminputs</text></g><g class="aGV4YWkuY21kLmhleGFp"><g class="shape" ><path d="M 100 1556 L 250 1556 L 250 1610 L 518 1610 L 518 1828 L 100 1828 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="1545.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">hexai</text></g><g class="aGV4YWkuY21kLmhleGFpLWFjdGlvbg=="><g class="shape" ><path d="M 243 1868 L 310 1868 L 310 1883 L 376 1883 L 376 1941 L 243 1941 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.500000" y="1917.300000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">hexai-action</text></g><g class="aGV4YWkuY21kLmhleGFpLWxzcA=="><g class="shape" ><path d="M 100 2017 L 250 2017 L 250 2071 L 518 2071 L 518 2289 L 100 2289 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="2006.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">hexai-lsp</text></g><g class="aGV4YWkuY21kLmhleGFpLXRtdXgtYWN0aW9u"><g class="shape" ><path d="M 100 2345 L 250 2345 L 250 2374 L 518 2374 L 518 2491 L 100 2491 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="2334.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">hexai-tmux-action</text></g><g class="aGV4YWkuY21kLmludGVybmFs"><g class="shape" ><path d="M 100 2547 L 250 2547 L 250 2578 L 518 2578 L 518 2700 L 100 2700 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="2536.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">internal</text></g><g class="aGV4YWkuZG9jcy4mIzM0O2J1aWxkYW5kaW5zdGFsbC5tZCYjMzQ7"><g class="shape" ><rect x="219.000000" y="2954.000000" width="180.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.000000" y="2992.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">buildandinstall.md</text></g><g class="aGV4YWkuZG9jcy4mIzM0O2NvbmZpZ3VyYXRpb24ubWQmIzM0Ow=="><g class="shape" ><rect x="225.000000" y="3080.000000" width="169.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.500000" y="3118.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">configuration.md</text></g><g class="aGV4YWkuZG9jcy4mIzM0O2NvdmVyYWdlLmh0bWwmIzM0Ow=="><g class="shape" ><rect x="235.000000" y="3206.000000" width="149.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.500000" y="3244.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">coverage.html</text></g><g class="aGV4YWkuZG9jcy4mIzM0O2NvdmVyYWdlLm91dCYjMzQ7"><g class="shape" ><rect x="240.000000" y="3332.000000" width="139.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.500000" y="3370.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">coverage.out</text></g><g class="aGV4YWkuZG9jcy4mIzM0O2N1c3RvbS1jb2RlLWFjdGlvbnMubWQmIzM0Ow=="><g class="shape" ><rect x="199.000000" y="3458.000000" width="221.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.500000" y="3496.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">custom-code-actions.md</text></g><g class="aGV4YWkuZG9jcy4mIzM0O3RtdXgtc3RhdHVzLWJhci5wbmcmIzM0Ow=="><g class="shape" ><rect x="213.000000" y="3584.000000" width="193.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.500000" y="3622.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tmux-status-bar.png</text></g><g class="aGV4YWkuZG9jcy4mIzM0O3RtdXgubWQmIzM0Ow=="><g class="shape" ><rect x="254.000000" y="3710.000000" width="110.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.000000" y="3748.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tmux.md</text></g><g class="aGV4YWkuZG9jcy4mIzM0O3VzYWdlLm1kJiMzNDs="><g class="shape" ><rect x="252.000000" y="3836.000000" width="114.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.000000" y="3874.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">usage.md</text></g><g class="aGV4YWkuaW50ZXJuYWwuJiMzNDt2ZXJzaW9uLmdvJiMzNDs="><g class="shape" ><rect x="249.000000" y="4043.000000" width="120.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="309.000000" y="4081.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">version.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuYXBwY29uZmln"><g class="shape" ><path d="M 100 4185 L 250 4185 L 250 4240 L 518 4240 L 518 4835 L 100 4835 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="4174.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">appconfig</text></g><g class="aGV4YWkuaW50ZXJuYWwuZWRpdG9y"><g class="shape" ><path d="M 100 4891 L 250 4891 L 250 4945 L 518 4945 L 518 5163 L 100 5163 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="4880.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">editor</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24="><g class="shape" ><path d="M 100 5618 L 250 5618 L 250 5673 L 518 5673 L 518 8410 L 100 8410 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="5607.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">hexaiaction</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWljbGk="><g class="shape" ><path d="M 100 8466 L 250 8466 L 250 8521 L 518 8521 L 518 9368 L 100 9368 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="8455.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">hexaicli</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlsc3A="><g class="shape" ><path d="M 100 9424 L 250 9424 L 250 9479 L 518 9479 L 518 9822 L 100 9822 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="9413.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">hexailsp</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxt"><g class="shape" ><path d="M 100 9878 L 250 9878 L 250 9933 L 518 9933 L 518 12418 L 100 12418 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="9867.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">llm</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtdXRpbHM="><g class="shape" ><path d="M 100 12474 L 250 12474 L 250 12528 L 518 12528 L 518 12746 L 100 12746 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="12463.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">llmutils</text></g><g class="aGV4YWkuaW50ZXJuYWwubG9nZ2luZw=="><g class="shape" ><path d="M 100 12802 L 250 12802 L 250 12857 L 518 12857 L 518 13200 L 100 13200 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="12791.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">logging</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNw"><g class="shape" ><path d="M 100 13256 L 250 13256 L 250 13311 L 518 13311 L 518 21970 L 100 21970 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="13245.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">lsp</text></g><g class="aGV4YWkuaW50ZXJuYWwucnVudGltZWNvbmZpZw=="><g class="shape" ><path d="M 100 22026 L 250 22026 L 250 22080 L 518 22080 L 518 22298 L 100 22298 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="22015.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">runtimeconfig</text></g><g class="aGV4YWkuaW50ZXJuYWwuc3RhdHM="><g class="shape" ><path d="M 100 22354 L 250 22354 L 250 22409 L 518 22409 L 518 23004 L 100 23004 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="22343.000000" fill="#0A0F25" class="text fill-N1" style="text-anchor:middle;font-size:20px">stats</text></g><g class="aGV4YWkuaW50ZXJuYWwudGVzdHV0aWw="><g class="shape" ><path d="M 260 5203 L 309 5203 L 309 5218 L 358 5218 L 358 5276 L 260 5276 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="5252.300000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">testutil</text></g><g class="aGV4YWkuaW50ZXJuYWwudGV4dHV0aWw="><g class="shape" ><path d="M 260 5336 L 310 5336 L 310 5351 L 359 5351 L 359 5409 L 260 5409 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.500000" y="5385.300000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">textutil</text></g><g class="aGV4YWkuaW50ZXJuYWwudG11eA=="><g class="shape" ><path d="M 268 5469 L 309 5469 L 309 5484 L 350 5484 L 350 5542 L 268 5542 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.000000" y="5518.300000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tmux</text></g><g class="aGV4YWkuY21kLmhleGFpLiYjMzQ7bWFpbi5nbyYjMzQ7"><g class="shape" ><rect x="258.000000" y="1596.000000" width="103.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="1634.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">main.go</text></g><g class="aGV4YWkuY21kLmhleGFpLiYjMzQ7bWFpbl90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="240.000000" y="1722.000000" width="138.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="1760.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">main_test.go</text></g><g class="aGV4YWkuY21kLmhleGFpLWxzcC4mIzM0O21haW4uZ28mIzM0Ow=="><g class="shape" ><rect x="258.000000" y="2057.000000" width="103.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="2095.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">main.go</text></g><g class="aGV4YWkuY21kLmhleGFpLWxzcC4mIzM0O21haW5fdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="240.000000" y="2183.000000" width="138.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="2221.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">main_test.go</text></g><g class="aGV4YWkuY21kLmhleGFpLXRtdXgtYWN0aW9uLiYjMzQ7bWFpbi5nbyYjMzQ7"><g class="shape" ><rect x="258.000000" y="2385.000000" width="103.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="2423.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">main.go</text></g><g class="aGV4YWkuY21kLmludGVybmFsLmhleGFpLWFjdGlvbg=="><g class="shape" ><path d="M 243 2587 L 310 2587 L 310 2602 L 376 2602 L 376 2660 L 243 2660 Z" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-AA5" style="stroke-width:2;" /></g><text x="309.500000" y="2636.300000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">hexai-action</text></g><g class="aGV4YWkuaW50ZXJuYWwuYXBwY29uZmlnLiYjMzQ7Y29uZmlnLmdvJiMzNDs="><g class="shape" ><rect x="253.000000" y="4225.000000" width="112.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="4263.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">config.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuYXBwY29uZmlnLiYjMzQ7Y29uZmlnX2FsaWFzX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="215.000000" y="4351.000000" width="188.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="4389.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">config_alias_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuYXBwY29uZmlnLiYjMzQ7Y29uZmlnX2Vudl9tb2RlbF90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="192.000000" y="4477.000000" width="234.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="4515.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">config_env_model_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuYXBwY29uZmlnLiYjMzQ7Y29uZmlnX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="236.000000" y="4603.000000" width="147.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="4641.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">config_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuYXBwY29uZmlnLiYjMzQ7Y3VzdG9tX3ZhbGlkYXRpb25fbW9yZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="169.000000" y="4729.000000" width="281.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="4767.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">custom_validation_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuZWRpdG9yLiYjMzQ7ZWRpdG9yLmdvJiMzNDs="><g class="shape" ><rect x="254.000000" y="4931.000000" width="111.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="4969.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">editor.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuZWRpdG9yLiYjMzQ7ZWRpdG9yX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="236.000000" y="5057.000000" width="147.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="5095.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">editor_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtjbWRlbnRyeS5nbyYjMzQ7"><g class="shape" ><rect x="241.000000" y="5658.000000" width="136.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="5696.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">cmdentry.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtjbWRlbnRyeV9ydW5jb21tYW5kX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="172.000000" y="5784.000000" width="275.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="5822.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">cmdentry_runcommand_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtjbWRlbnRyeV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="223.000000" y="5910.000000" width="172.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="5948.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">cmdentry_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtjdXN0b21fYWN0aW9uX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="205.000000" y="6036.000000" width="208.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="6074.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">custom_action_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtjdXN0b21fZXhlY19tb3JlX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="189.000000" y="6162.000000" width="241.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="6200.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">custom_exec_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtjdXN0b21fZXhlY190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="211.000000" y="6288.000000" width="196.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="6326.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">custom_exec_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtwYXJzZS5nbyYjMzQ7"><g class="shape" ><rect x="256.000000" y="6414.000000" width="106.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="6452.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">parse.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtwYXJzZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="238.000000" y="6540.000000" width="142.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="6578.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">parse_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtwcm9tcHRzLmdvJiMzNDs="><g class="shape" ><rect x="246.000000" y="6666.000000" width="127.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="6704.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">prompts.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtwcm9tcHRzX21vcmVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="205.000000" y="6792.000000" width="208.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="6830.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">prompts_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtwcm9tcHRzX3NpbXBsaWZ5X3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="195.000000" y="6918.000000" width="228.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="6956.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">prompts_simplify_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtydW4uZ28mIzM0Ow=="><g class="shape" ><rect x="264.000000" y="7044.000000" width="91.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="7082.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtydW5fbW9yZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="223.000000" y="7170.000000" width="172.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="7208.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtydW5fc2VhbV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="223.000000" y="7296.000000" width="173.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="7334.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_seam_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDtydW5fdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="246.000000" y="7422.000000" width="127.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="7460.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDt0dWkuZ28mIzM0Ow=="><g class="shape" ><rect x="266.000000" y="7548.000000" width="87.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="7586.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tui.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDt0dWlfY3VzdG9tLmdvJiMzNDs="><g class="shape" ><rect x="235.000000" y="7674.000000" width="148.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="7712.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tui_custom.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDt0dWlfY3VzdG9tX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="218.000000" y="7800.000000" width="183.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="7838.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tui_custom_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDt0dWlfZGVsZWdhdGUuZ28mIzM0Ow=="><g class="shape" ><rect x="231.000000" y="7926.000000" width="157.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="7964.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tui_delegate.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDt0dWlfZGVsZWdhdGVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="213.000000" y="8052.000000" width="193.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="8090.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tui_delegate_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDt0dWlfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="248.000000" y="8178.000000" width="123.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="8216.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">tui_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlhY3Rpb24uJiMzNDt0eXBlcy5nbyYjMzQ7"><g class="shape" ><rect x="256.000000" y="8304.000000" width="107.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="8342.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">types.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWljbGkuJiMzNDtlZGl0b3JfaW50ZWdyYXRpb25fdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="192.000000" y="8506.000000" width="235.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="8544.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">editor_integration_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWljbGkuJiMzNDtydW4uZ28mIzM0Ow=="><g class="shape" ><rect x="264.000000" y="8632.000000" width="91.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="8670.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWljbGkuJiMzNDtydW5fZWRpdG9yX2JlaGF2aW9yX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="185.000000" y="8758.000000" width="249.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="8796.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_editor_behavior_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWljbGkuJiMzNDtydW5fbW9kZWxfb3ZlcnJpZGVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="185.000000" y="8884.000000" width="248.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="8922.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_model_override_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWljbGkuJiMzNDtydW5fbW9yZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="223.000000" y="9010.000000" width="172.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="9048.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWljbGkuJiMzNDtydW5fdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="246.000000" y="9136.000000" width="127.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="9174.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWljbGkuJiMzNDt0ZXN0aGVscGVyc190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="217.000000" y="9262.000000" width="184.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="9300.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">testhelpers_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlsc3AuJiMzNDtydW4uZ28mIzM0Ow=="><g class="shape" ><rect x="264.000000" y="9464.000000" width="91.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="9502.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlsc3AuJiMzNDtydW5fbW9yZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="223.000000" y="9590.000000" width="172.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="9628.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuaGV4YWlsc3AuJiMzNDtydW5fdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="246.000000" y="9716.000000" width="127.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="9754.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">run_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7Y29waWxvdC5nbyYjMzQ7"><g class="shape" ><rect x="251.000000" y="9918.000000" width="117.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="9956.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">copilot.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7Y29waWxvdF9odHRwX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="213.000000" y="10044.000000" width="192.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="10082.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">copilot_http_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7Y29waWxvdF90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="233.000000" y="10170.000000" width="153.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="10208.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">copilot_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b2xsYW1hLmdvJiMzNDs="><g class="shape" ><rect x="251.000000" y="10296.000000" width="116.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="10334.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">ollama.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b2xsYW1hX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="233.000000" y="10422.000000" width="152.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="10460.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">ollama_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b3BlbmFpLmdvJiMzNDs="><g class="shape" ><rect x="251.000000" y="10548.000000" width="116.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="10586.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">openai.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b3BlbmFpX2h0dHBfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="214.000000" y="10674.000000" width="190.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="10712.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">openai_http_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b3BlbmFpX3JlcXVlc3RfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="202.000000" y="10800.000000" width="214.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="10838.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">openai_request_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b3BlbmFpX3NzZV9uZWdhdGl2ZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="183.000000" y="10926.000000" width="252.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="10964.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">openai_sse_negative_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b3BlbmFpX3RlbXBfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="211.000000" y="11052.000000" width="197.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="11090.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">openai_temp_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b3BlbmFpX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="233.000000" y="11178.000000" width="152.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="11216.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">openai_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b3BlbnJvdXRlci5nbyYjMzQ7"><g class="shape" ><rect x="235.000000" y="11304.000000" width="148.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="11342.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">openrouter.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7b3BlbnJvdXRlcl90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="217.000000" y="11430.000000" width="184.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="11468.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">openrouter_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7cHJvdmlkZXIuZ28mIzM0Ow=="><g class="shape" ><rect x="245.000000" y="11556.000000" width="128.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="11594.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">provider.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7cHJvdmlkZXJfbW9yZTJfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="201.000000" y="11682.000000" width="217.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="11720.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">provider_more2_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7cHJvdmlkZXJfbW9yZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="205.000000" y="11808.000000" width="209.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="11846.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">provider_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7cHJvdmlkZXJfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="227.000000" y="11934.000000" width="164.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="11972.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">provider_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7dGVzdF9oZWxwZXJzX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="213.000000" y="12060.000000" width="192.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="12098.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">test_helpers_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7dXRpbC5nbyYjMzQ7"><g class="shape" ><rect x="263.000000" y="12186.000000" width="92.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="12224.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">util.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtLiYjMzQ7dXRpbF90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="245.000000" y="12312.000000" width="128.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="12350.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">util_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtdXRpbHMuJiMzNDtjbGllbnQuZ28mIzM0Ow=="><g class="shape" ><rect x="255.000000" y="12514.000000" width="108.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="12552.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">client.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubGxtdXRpbHMuJiMzNDtjbGllbnRfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="238.000000" y="12640.000000" width="143.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="12678.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">client_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubG9nZ2luZy4mIzM0O2NoYXRsb2dnZXIuZ28mIzM0Ow=="><g class="shape" ><rect x="237.000000" y="12842.000000" width="144.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="12880.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">chatlogger.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubG9nZ2luZy4mIzM0O2xvZ2dpbmcuZ28mIzM0Ow=="><g class="shape" ><rect x="249.000000" y="12968.000000" width="120.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="13006.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">logging.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubG9nZ2luZy4mIzM0O2xvZ2dpbmdfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="232.000000" y="13094.000000" width="155.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="13132.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">logging_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7YnVpbGRfcHJvbXB0c190YWJsZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="183.000000" y="13296.000000" width="252.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="13334.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">build_prompts_table_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y2hhdF9jb21tYW5kcy5nbyYjMzQ7"><g class="shape" ><rect x="217.000000" y="13422.000000" width="185.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="13460.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">chat_commands.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y2hhdF9jb21tYW5kc190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="199.000000" y="13548.000000" width="220.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="13586.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">chat_commands_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y2hhdF9jb250ZXh0X21vZGVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="187.000000" y="13674.000000" width="245.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="13712.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">chat_context_mode_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y2hhdF9oaXN0b3J5X3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="213.000000" y="13800.000000" width="193.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="13838.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">chat_history_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y2hhdF9ub19kb3VibGVfYW5zd2VyX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="170.000000" y="13926.000000" width="278.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="13964.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">chat_no_double_answer_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y2hhdF9wcm9tcHRfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="211.000000" y="14052.000000" width="196.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="14090.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">chat_prompt_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y2hhdF90cmlnZ2VyX3N1cHByZXNzaW9uX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="167.000000" y="14178.000000" width="285.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="14216.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">chat_trigger_suppression_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29kZV9mZW5jZXNfdGFibGVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="191.000000" y="14304.000000" width="236.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="14342.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">code_fences_table_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29kZWFjdGlvbl9jdXN0b21fZXJyb3JzX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="163.000000" y="14430.000000" width="293.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="14468.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">codeaction_custom_errors_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29kZWFjdGlvbl9jdXN0b21fdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="188.000000" y="14556.000000" width="242.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="14594.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">codeaction_custom_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29kZWFjdGlvbl9nb3Rlc3RfaW50X3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="178.000000" y="14682.000000" width="262.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="14720.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">codeaction_gotest_int_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29kZWFjdGlvbl9tb3JlX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="196.000000" y="14808.000000" width="227.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="14846.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">codeaction_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29kZWFjdGlvbl9wcm9tcHRzX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="184.000000" y="14934.000000" width="250.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="14972.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">codeaction_prompts_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29kZWFjdGlvbl90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="218.000000" y="15060.000000" width="182.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="15098.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">codeaction_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29kZWdlbl9oZWxwZXJzX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="197.000000" y="15186.000000" width="224.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="15224.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">codegen_helpers_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29tcGxldGlvbl9jYWNoZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="193.000000" y="15312.000000" width="233.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="15350.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">completion_cache_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29tcGxldGlvbl9jb2RleF9wYXRoX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="172.000000" y="15438.000000" width="275.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="15476.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">completion_codex_path_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29tcGxldGlvbl9oZWxwZXJzX21vcmVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="164.000000" y="15564.000000" width="290.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="15602.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">completion_helpers_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29tcGxldGlvbl9tZXNzYWdlc190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="179.000000" y="15690.000000" width="261.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="15728.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">completion_messages_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29tcGxldGlvbl9wcmVmaXhfc3RyaXBfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="172.000000" y="15816.000000" width="275.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="15854.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">completion_prefix_strip_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29tcGxldGlvbl9wcm92aWRlcl9mYWxsYmFja190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="150.000000" y="15942.000000" width="318.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="15980.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">completion_provider_fallback_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29tcGxldGlvbl90b2dnbGVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="191.000000" y="16068.000000" width="237.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="16106.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">completion_toggle_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29tcHV0ZV90ZXh0ZWRpdF90YWJsZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="171.000000" y="16194.000000" width="276.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="16232.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">compute_textedit_table_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29udGV4dC5nbyYjMzQ7"><g class="shape" ><rect x="248.000000" y="16320.000000" width="122.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="16358.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">context.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y29udGV4dF90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="230.000000" y="16446.000000" width="158.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="16484.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">context_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Y292ZXJhZ2VfYWRkX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="208.000000" y="16572.000000" width="203.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="16610.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">coverage_add_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7ZGVib3VuY2VfdGhyb3R0bGVfbW9yZV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="168.000000" y="16698.000000" width="282.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="16736.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">debounce_throttle_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7ZGVib3VuY2VfdGhyb3R0bGVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="191.000000" y="16824.000000" width="237.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="16862.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">debounce_throttle_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7ZGlhZ25vc3RpY3NfYWN0aW9uX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="191.000000" y="16950.000000" width="237.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="16988.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">diagnostics_action_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7ZG9jdW1lbnQuZ28mIzM0Ow=="><g class="shape" ><rect x="239.000000" y="17076.000000" width="140.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="17114.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">document.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7ZG9jdW1lbnRfaGFuZGxlcnNfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="186.000000" y="17202.000000" width="246.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="17240.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">document_handlers_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7ZG9jdW1lbnRfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="222.000000" y="17328.000000" width="175.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="17366.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">document_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7ZmFsbGJhY2tfaXRlbXNfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="205.000000" y="17454.000000" width="208.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="17492.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">fallback_items_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7Z290ZXN0X2FwcGVuZF90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="204.000000" y="17580.000000" width="210.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="17618.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">gotest_append_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnMuZ28mIzM0Ow=="><g class="shape" ><rect x="245.000000" y="17706.000000" width="129.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="17744.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfY29kZWFjdGlvbi5nbyYjMzQ7"><g class="shape" ><rect x="202.000000" y="17832.000000" width="215.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="17870.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_codeaction.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfY29tcGxldGlvbi5nbyYjMzQ7"><g class="shape" ><rect x="200.000000" y="17958.000000" width="218.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="17996.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_completion.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfZG9jdW1lbnQuZ28mIzM0Ow=="><g class="shape" ><rect x="205.000000" y="18084.000000" width="209.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="18122.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_document.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfZW5kX3RvX2VuZF90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="181.000000" y="18210.000000" width="257.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="18248.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_end_to_end_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfZXhlY3V0ZS5nbyYjMzQ7"><g class="shape" ><rect x="213.000000" y="18336.000000" width="193.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="18374.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_execute.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfaGVscGVyc190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="196.000000" y="18462.000000" width="226.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="18500.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_helpers_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfaW5pdC5nbyYjMzQ7"><g class="shape" ><rect x="229.000000" y="18588.000000" width="161.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="18626.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_init.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfaW5pdF9tb3JlX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="188.000000" y="18714.000000" width="242.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="18752.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_init_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="227.000000" y="18840.000000" width="165.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="18878.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGFuZGxlcnNfdXRpbHMuZ28mIzM0Ow=="><g class="shape" ><rect x="225.000000" y="18966.000000" width="168.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="19004.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">handlers_utils.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGVscGVyc19pbmxpbmVfcHJvbXB0X3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="177.000000" y="19092.000000" width="265.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="19130.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">helpers_inline_prompt_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aGVscGVyc19tb3JlX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="209.000000" y="19218.000000" width="201.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="19256.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">helpers_more_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aW5pdF9hbmRfdHJpZ2dlcl90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="200.000000" y="19344.000000" width="219.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="19382.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">init_and_trigger_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aW5pdF9zaHV0ZG93bl90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="206.000000" y="19470.000000" width="207.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="19508.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">init_shutdown_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aW5saW5lX3Byb21wdF9jb21wbGV0aW9uX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="162.000000" y="19596.000000" width="294.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="19634.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">inline_prompt_completion_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7aW5zdHJ1Y3Rpb25fdGFibGVfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="196.000000" y="19722.000000" width="226.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="19760.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">instruction_table_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7bGFiZWxfZmlsdGVyX3RhYmxlX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="197.000000" y="19848.000000" width="225.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="19886.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">label_filter_table_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7bGxtX3JlcXVlc3Rfb3B0c190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="196.000000" y="19974.000000" width="227.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="20012.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">llm_request_opts_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7bGxtX3N0YXRzX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="225.000000" y="20100.000000" width="168.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="20138.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">llm_stats_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7bG9nX2NvbnRleHRfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="216.000000" y="20226.000000" width="187.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="20264.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">log_context_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7cG9zdHByb2Nlc3NfaW5kZW50X3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="188.000000" y="20352.000000" width="242.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="20390.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">postprocess_indent_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7cHJlZml4X3RhYmxlX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="215.000000" y="20478.000000" width="189.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="20516.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">prefix_table_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7cHJvdmlkZXJfbmF0aXZlX3N1Y2Nlc3NfdGVzdC5nbyYjMzQ7"><g class="shape" ><rect x="170.000000" y="20604.000000" width="278.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="20642.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">provider_native_success_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7cmV3cml0ZV9kaWFnbm9zdGljc19yZWFsaXNtX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="157.000000" y="20730.000000" width="304.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="20768.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">rewrite_diagnostics_realism_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7c2VydmVyLmdvJiMzNDs="><g class="shape" ><rect x="253.000000" y="20856.000000" width="113.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="20894.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">server.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7c2VydmVyX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="235.000000" y="20982.000000" width="148.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="21020.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">server_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7dGVzdGZha2VzX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="225.000000" y="21108.000000" width="169.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="21146.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">testfakes_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7dGVzdGhlbHBlcl9jYXB0dXJlX2xsbV90ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="174.000000" y="21234.000000" width="271.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="21272.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">testhelper_capture_llm_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7dHJhbnNwb3J0LmdvJiMzNDs="><g class="shape" ><rect x="241.000000" y="21360.000000" width="136.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="21398.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">transport.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7dHJhbnNwb3J0X2NvbmN1cnJlbmN5X3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="176.000000" y="21486.000000" width="267.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="21524.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">transport_concurrency_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7dHJhbnNwb3J0X3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="224.000000" y="21612.000000" width="171.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="21650.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">transport_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7dHJpZ2dlcnNfY29uZmlnX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="204.000000" y="21738.000000" width="211.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="21776.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">triggers_config_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwubHNwLiYjMzQ7dHlwZXMuZ28mIzM0Ow=="><g class="shape" ><rect x="256.000000" y="21864.000000" width="107.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="21902.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">types.go</text></g><g class="aGV4YWkuaW50ZXJuYWwucnVudGltZWNvbmZpZy4mIzM0O3N0b3JlLmdvJiMzNDs="><g class="shape" ><rect x="257.000000" y="22066.000000" width="105.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="22104.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">store.go</text></g><g class="aGV4YWkuaW50ZXJuYWwucnVudGltZWNvbmZpZy4mIzM0O3N0b3JlX3Rlc3QuZ28mIzM0Ow=="><g class="shape" ><rect x="239.000000" y="22192.000000" width="140.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="22230.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">store_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuc3RhdHMuJiMzNDtkZWJ1Z3N0cmluZ190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="215.000000" y="22394.000000" width="189.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="22432.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">debugstring_test.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuc3RhdHMuJiMzNDtsb2NrX3Bvc2l4LmdvJiMzNDs="><g class="shape" ><rect x="238.000000" y="22520.000000" width="142.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="22558.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">lock_posix.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuc3RhdHMuJiMzNDtsb2NrX3dpbmRvd3MuZ28mIzM0Ow=="><g class="shape" ><rect x="225.000000" y="22646.000000" width="168.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="22684.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">lock_windows.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuc3RhdHMuJiMzNDtzdGF0cy5nbyYjMzQ7"><g class="shape" ><rect x="258.000000" y="22772.000000" width="103.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.500000" y="22810.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">stats.go</text></g><g class="aGV4YWkuaW50ZXJuYWwuc3RhdHMuJiMzNDtzdGF0c190ZXN0LmdvJiMzNDs="><g class="shape" ><rect x="240.000000" y="22898.000000" width="138.000000" height="66.000000" stroke="#0D32B2" fill="#FFFFFF" class=" stroke-B1 fill-N7" style="stroke-width:2;" /></g><text x="309.000000" y="22936.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">stats_test.go</text></g><mask id="d2-3145263268" maskUnits="userSpaceOnUse" x="-101" y="-95" width="820" height="23240">
+<rect x="-101" y="-95" width="820" height="23240" fill="white"></rect>
+
+</mask></svg></svg>