From 2ea13cd6115e96017c115bcf2d727bf0f613b41a Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 29 Jul 2026 18:10:25 +0200 Subject: [PATCH] restructure: drop pkg/, split tools/execute into focused packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop pkg/ prefix (Go anti-pattern): ollie/pkg/X → ollie/X - Split tools/execute god package: - execute/: shell execution, sandboxing, elevation client, remote SSH - tools/: interfaces + registry + discovery + schema parsing - detach/: background process management (ring buffer, signal) - Promote internal/sandbox → sandbox/ - Absorb config/ into agent/config.go (agent definition loading) - Merge remote/ into execute/remote.go (RemoteServer) All tests pass. --- {pkg/agent => agent}/commands.go | 3 +- {pkg/agent => agent}/compaction.go | 0 {pkg/agent => agent}/compaction_test.go | 2 +- {pkg/config => agent}/config.go | 2 +- {pkg/config => agent}/config_test.go | 2 +- {pkg/agent => agent}/core.go | 14 ++- {pkg/agent => agent}/core_test.go | 19 ++-- {pkg/agent => agent}/cost.go | 2 +- {pkg/agent => agent}/cost_test.go | 2 +- {pkg/agent => agent}/fifo_test.go | 0 {pkg/agent => agent}/hooks.go | 4 +- {pkg/agent => agent}/loop.go | 4 +- {pkg/agent => agent}/loop_cache_test.go | 2 +- {pkg/agent => agent}/loop_errlimit_test.go | 2 +- .../loop_maxsteps_helpers_test.go | 2 +- {pkg/agent => agent}/loop_maxsteps_test.go | 2 +- {pkg/agent => agent}/loop_parallel_test.go | 2 +- {pkg/agent => agent}/loop_truncate_test.go | 2 +- {pkg/agent => agent}/loop_turnerror_test.go | 2 +- {pkg/agent => agent}/prompt_resolver.go | 3 +- {pkg/agent => agent}/runtime.go | 4 +- {pkg/agent => agent}/session.go | 2 +- {pkg/agent => agent}/state.go | 2 +- {pkg/agent => agent}/types.go | 2 +- {pkg/agent => agent}/usage_log.go | 2 +- {pkg/agent => agent}/waitchange_test.go | 2 +- {pkg/backend => backend}/anthropic.go | 0 .../anthropic_stream_test.go | 0 {pkg/backend => backend}/backend.go | 0 {pkg/backend => backend}/backend_test.go | 2 +- {pkg/backend => backend}/codewhisperer.go | 0 .../codewhisperer_internal.go | 0 .../backend => backend}/codewhisperer_test.go | 0 {pkg/backend => backend}/copilot.go | 0 {pkg/backend => backend}/gemini.go | 0 {pkg/backend => backend}/new.go | 2 +- {pkg/backend => backend}/new_test.go | 0 {pkg/backend => backend}/ollama.go | 0 .../backend => backend}/ollama_stream_test.go | 0 {pkg/backend => backend}/oneshot.go | 0 {pkg/backend => backend}/openai.go | 0 .../backend => backend}/openai_encode_test.go | 0 .../backend => backend}/openai_stream_test.go | 0 {pkg/backend => backend}/openai_test.go | 0 {pkg/backend => backend}/pool.go | 0 {pkg/backend => backend}/testhelper_test.go | 0 .../execute/detach.go => detach/process.go | 52 +++++------ {pkg/elevate => elevate}/broker.go | 0 {pkg/elevate => elevate}/broker_test.go | 0 {pkg/elevate => elevate}/policy.go | 0 {pkg/elevate => elevate}/policy_test.go | 0 {pkg/elevate => elevate}/request.go | 0 {pkg/env => env}/env.go | 2 +- {pkg/env => env}/env_test.go | 0 {pkg/remote => execute}/bootstrap.sh | 0 {pkg/tools/execute => execute}/code.go | 4 +- {pkg/remote => execute}/remote.go | 28 +++--- {pkg/tools/execute => execute}/server.go | 87 ++++++++++--------- {pkg/tools/execute => execute}/skills.go | 4 +- {pkg/tools/execute => execute}/tier.go | 5 +- {pkg/log => log}/log.go | 0 {pkg/log => log}/log_test.go | 0 {pkg/paths => paths}/paths.go | 0 {pkg/paths => paths}/paths_test.go | 0 .../execute/testdata/sandbox/default.yaml | 21 ----- pkg/tools/execute/testdata/tools/test_tool | 8 -- {internal/sandbox => sandbox}/config.go | 2 +- {internal/sandbox => sandbox}/detect.go | 0 {internal/sandbox => sandbox}/sandbox_test.go | 0 {internal/sandbox => sandbox}/wrapper.go | 0 {pkg/skills => skills}/registry.go | 0 {pkg/skills => skills}/skills.go | 0 {pkg/skills => skills}/skills_test.go | 0 .../execute/tool.go => tools/discover.go | 11 ++- {pkg/tools/execute => tools}/registry.go | 29 +++---- {pkg/tools/execute => tools}/schema.go | 7 +- {pkg/tools => tools}/stream.go | 0 {pkg/tools => tools}/tools.go | 0 {pkg/tools => tools}/tools_test.go | 2 +- 79 files changed, 158 insertions(+), 193 deletions(-) rename {pkg/agent => agent}/commands.go (99%) rename {pkg/agent => agent}/compaction.go (100%) rename {pkg/agent => agent}/compaction_test.go (99%) rename {pkg/config => agent}/config.go (99%) rename {pkg/config => agent}/config_test.go (99%) rename {pkg/agent => agent}/core.go (99%) rename {pkg/agent => agent}/core_test.go (99%) rename {pkg/agent => agent}/cost.go (99%) rename {pkg/agent => agent}/cost_test.go (99%) rename {pkg/agent => agent}/fifo_test.go (100%) rename {pkg/agent => agent}/hooks.go (99%) rename {pkg/agent => agent}/loop.go (99%) rename {pkg/agent => agent}/loop_cache_test.go (99%) rename {pkg/agent => agent}/loop_errlimit_test.go (99%) rename {pkg/agent => agent}/loop_maxsteps_helpers_test.go (98%) rename {pkg/agent => agent}/loop_maxsteps_test.go (99%) rename {pkg/agent => agent}/loop_parallel_test.go (99%) rename {pkg/agent => agent}/loop_truncate_test.go (99%) rename {pkg/agent => agent}/loop_turnerror_test.go (99%) rename {pkg/agent => agent}/prompt_resolver.go (98%) rename {pkg/agent => agent}/runtime.go (96%) rename {pkg/agent => agent}/session.go (99%) rename {pkg/agent => agent}/state.go (98%) rename {pkg/agent => agent}/types.go (99%) rename {pkg/agent => agent}/usage_log.go (98%) rename {pkg/agent => agent}/waitchange_test.go (99%) rename {pkg/backend => backend}/anthropic.go (100%) rename {pkg/backend => backend}/anthropic_stream_test.go (100%) rename {pkg/backend => backend}/backend.go (100%) rename {pkg/backend => backend}/backend_test.go (99%) rename {pkg/backend => backend}/codewhisperer.go (100%) rename {pkg/backend => backend}/codewhisperer_internal.go (100%) rename {pkg/backend => backend}/codewhisperer_test.go (100%) rename {pkg/backend => backend}/copilot.go (100%) rename {pkg/backend => backend}/gemini.go (100%) rename {pkg/backend => backend}/new.go (99%) rename {pkg/backend => backend}/new_test.go (100%) rename {pkg/backend => backend}/ollama.go (100%) rename {pkg/backend => backend}/ollama_stream_test.go (100%) rename {pkg/backend => backend}/oneshot.go (100%) rename {pkg/backend => backend}/openai.go (100%) rename {pkg/backend => backend}/openai_encode_test.go (100%) rename {pkg/backend => backend}/openai_stream_test.go (100%) rename {pkg/backend => backend}/openai_test.go (100%) rename {pkg/backend => backend}/pool.go (100%) rename {pkg/backend => backend}/testhelper_test.go (100%) rename pkg/tools/execute/detach.go => detach/process.go (66%) rename {pkg/elevate => elevate}/broker.go (100%) rename {pkg/elevate => elevate}/broker_test.go (100%) rename {pkg/elevate => elevate}/policy.go (100%) rename {pkg/elevate => elevate}/policy_test.go (100%) rename {pkg/elevate => elevate}/request.go (100%) rename {pkg/env => env}/env.go (99%) rename {pkg/env => env}/env_test.go (100%) rename {pkg/remote => execute}/bootstrap.sh (100%) rename {pkg/tools/execute => execute}/code.go (99%) rename {pkg/remote => execute}/remote.go (94%) rename {pkg/tools/execute => execute}/server.go (94%) rename {pkg/tools/execute => execute}/skills.go (99%) rename {pkg/tools/execute => execute}/tier.go (95%) rename {pkg/log => log}/log.go (100%) rename {pkg/log => log}/log_test.go (100%) rename {pkg/paths => paths}/paths.go (100%) rename {pkg/paths => paths}/paths_test.go (100%) delete mode 100644 pkg/tools/execute/testdata/sandbox/default.yaml delete mode 100644 pkg/tools/execute/testdata/tools/test_tool rename {internal/sandbox => sandbox}/config.go (99%) rename {internal/sandbox => sandbox}/detect.go (100%) rename {internal/sandbox => sandbox}/sandbox_test.go (100%) rename {internal/sandbox => sandbox}/wrapper.go (100%) rename {pkg/skills => skills}/registry.go (100%) rename {pkg/skills => skills}/skills.go (100%) rename {pkg/skills => skills}/skills_test.go (100%) rename pkg/tools/execute/tool.go => tools/discover.go (95%) rename {pkg/tools/execute => tools}/registry.go (80%) rename {pkg/tools/execute => tools}/schema.go (95%) rename {pkg/tools => tools}/stream.go (100%) rename {pkg/tools => tools}/tools.go (100%) rename {pkg/tools => tools}/tools_test.go (99%) diff --git a/pkg/agent/commands.go b/agent/commands.go similarity index 99% rename from pkg/agent/commands.go rename to agent/commands.go index ae920f1..6869298 100644 --- a/pkg/agent/commands.go +++ b/agent/commands.go @@ -10,7 +10,6 @@ import ( "strconv" "strings" - "ollie/pkg/config" ) func (s *agent) handleCommand(ctx context.Context, input string) bool { @@ -169,7 +168,7 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool { s.emit(infoEvent(fmt.Sprintf("error: agent %q: %v", name, err))) return } - cfg, err := config.Load(f) + cfg, err := Load(f) f.Close() if err != nil { s.emit(infoEvent(fmt.Sprintf("error: agent %q: %v", name, err))) diff --git a/pkg/agent/compaction.go b/agent/compaction.go similarity index 100% rename from pkg/agent/compaction.go rename to agent/compaction.go diff --git a/pkg/agent/compaction_test.go b/agent/compaction_test.go similarity index 99% rename from pkg/agent/compaction_test.go rename to agent/compaction_test.go index 461faee..51c5216 100644 --- a/pkg/agent/compaction_test.go +++ b/agent/compaction_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "ollie/pkg/backend" + "ollie/backend" ) type mockBackendForCompact struct { diff --git a/pkg/config/config.go b/agent/config.go similarity index 99% rename from pkg/config/config.go rename to agent/config.go index fa9efc3..4315c1b 100644 --- a/pkg/config/config.go +++ b/agent/config.go @@ -1,4 +1,4 @@ -package config +package agent import ( "encoding/json" diff --git a/pkg/config/config_test.go b/agent/config_test.go similarity index 99% rename from pkg/config/config_test.go rename to agent/config_test.go index 4a288db..4ca7d3a 100644 --- a/pkg/config/config_test.go +++ b/agent/config_test.go @@ -1,4 +1,4 @@ -package config +package agent import ( "strings" diff --git a/pkg/agent/core.go b/agent/core.go similarity index 99% rename from pkg/agent/core.go rename to agent/core.go index 3de52f4..c602104 100644 --- a/pkg/agent/core.go +++ b/agent/core.go @@ -19,12 +19,10 @@ import ( "time" "github.com/simonfxr/pubsub" - "ollie/pkg/backend" - "ollie/pkg/config" - olog "ollie/pkg/log" - "ollie/pkg/paths" - "ollie/pkg/tools" - "ollie/pkg/tools/execute" + "ollie/backend" + olog "ollie/log" + "ollie/paths" + "ollie/tools" ) // toolClassifier reports whether a named tool is safe to run concurrently @@ -37,7 +35,7 @@ type toolClassifier func(name string) bool // env provides additional environment variables injected into prompt resolution // subprocesses (e.g. OLLIE_SESSION_ID=xxx). // The caller is responsible for registering all servers on d before calling this. -func BuildRuntime(cfg *config.Config, d tools.Dispatcher, cwd string, env []string, baseLayers ...string) *Runtime { +func BuildRuntime(cfg *Config, d tools.Dispatcher, cwd string, env []string, baseLayers ...string) *Runtime { var messages []string var allToolInfos []tools.ToolInfo @@ -58,7 +56,7 @@ func BuildRuntime(cfg *config.Config, d tools.Dispatcher, cwd string, env []stri allTools = toolInfosToBackend(allToolInfos) // Append named tool scripts for preamble listing only. - allToolInfos = append(allToolInfos, execute.DiscoverTools()...) + allToolInfos = append(allToolInfos, tools.DiscoverTools()...) } hooks := Hooks{} diff --git a/pkg/agent/core_test.go b/agent/core_test.go similarity index 99% rename from pkg/agent/core_test.go rename to agent/core_test.go index 2ff5d71..dd893a6 100644 --- a/pkg/agent/core_test.go +++ b/agent/core_test.go @@ -12,9 +12,8 @@ import ( "testing" "time" - "ollie/pkg/backend" - "ollie/pkg/config" - "ollie/pkg/tools" + "ollie/backend" + "ollie/tools" ) // --- mock backend --- @@ -2393,7 +2392,7 @@ func TestBuildRuntime_NilConfig(t *testing.T) { func TestBuildRuntime_PromptBecomesPreamble(t *testing.T) { setupCfgDir(t) d := tools.NewDispatcher() - cfg := &config.Config{Prompt: config.Prompt{Value: []string{"the prompt"}}} + cfg := &Config{Prompt: Prompt{Value: []string{"the prompt"}}} env := BuildRuntime(cfg, d, t.TempDir(), nil) if env.Preamble != "the prompt" { @@ -2405,8 +2404,8 @@ func TestBuildRuntime_HooksAndParams(t *testing.T) { setupCfgDir(t) d := tools.NewDispatcher() temp := 0.7 - cfg := &config.Config{ - Hooks: map[string]config.HookCmds{"preTurn": {"echo hi"}}, + cfg := &Config{ + Hooks: map[string]HookCmds{"preTurn": {"echo hi"}}, MaxTokens: 512, Temperature: &temp, } @@ -2489,7 +2488,7 @@ func TestBuildRuntime_ToolsDisabled(t *testing.T) { tools: []tools.ToolInfo{{Server: "s1", Name: "mytool", Description: "desc", InputSchema: json.RawMessage(`{}`)}}, } f := false - cfg := &config.Config{Tools: &f} + cfg := &Config{Tools: &f} env := BuildRuntime(cfg, d, t.TempDir(), nil) if len(env.Tools) != 0 { t.Errorf("expected no tools when disabled; got %+v", env.Tools) @@ -2499,7 +2498,7 @@ func TestBuildRuntime_ToolsDisabled(t *testing.T) { func TestBuildRuntime_PromptOnly(t *testing.T) { setupCfgDir(t) d := tools.NewDispatcher() - cfg := &config.Config{Prompt: config.Prompt{Value: []string{"only agent"}}} + cfg := &Config{Prompt: Prompt{Value: []string{"only agent"}}} env := BuildRuntime(cfg, d, t.TempDir(), nil) if env.Preamble != "only agent" { t.Errorf("preamble = %q; want %q", env.Preamble, "only agent") @@ -2509,7 +2508,7 @@ func TestBuildRuntime_PromptOnly(t *testing.T) { func TestBuildRuntime_ExecPrompt(t *testing.T) { setupCfgDir(t) d := tools.NewDispatcher() - cfg := &config.Config{Prompt: config.Prompt{ + cfg := &Config{Prompt: Prompt{ Value: []string{"echo hello", "echo 'You are a security auditor.'", "echo world"}, IsExec: true, }} @@ -2529,7 +2528,7 @@ func TestBuildRuntime_ExecPromptFileResolution(t *testing.T) { t.Setenv("OLLIE_PROMPTS_PATH", promptsDir) d := tools.NewDispatcher() - cfg := &config.Config{Prompt: config.Prompt{ + cfg := &Config{Prompt: Prompt{ Value: []string{"test-prompt", "echo extra"}, IsExec: true, }} diff --git a/pkg/agent/cost.go b/agent/cost.go similarity index 99% rename from pkg/agent/cost.go rename to agent/cost.go index b11de65..d0686ac 100644 --- a/pkg/agent/cost.go +++ b/agent/cost.go @@ -3,7 +3,7 @@ package agent import ( "strings" - "ollie/pkg/backend" + "ollie/backend" ) // auditTruncate trims s to 200 runes for log output. diff --git a/pkg/agent/cost_test.go b/agent/cost_test.go similarity index 99% rename from pkg/agent/cost_test.go rename to agent/cost_test.go index a3c3540..e87f5ba 100644 --- a/pkg/agent/cost_test.go +++ b/agent/cost_test.go @@ -4,7 +4,7 @@ import ( "math" "testing" - "ollie/pkg/backend" + "ollie/backend" ) func approxEqual(a, b, tol float64) bool { diff --git a/pkg/agent/fifo_test.go b/agent/fifo_test.go similarity index 100% rename from pkg/agent/fifo_test.go rename to agent/fifo_test.go diff --git a/pkg/agent/hooks.go b/agent/hooks.go similarity index 99% rename from pkg/agent/hooks.go rename to agent/hooks.go index ce17644..26e374a 100644 --- a/pkg/agent/hooks.go +++ b/agent/hooks.go @@ -12,8 +12,8 @@ import ( "syscall" "time" - olog "ollie/pkg/log" - "ollie/pkg/paths" + olog "ollie/log" + "ollie/paths" ) // Hook name constants for well-known agent lifecycle events. diff --git a/pkg/agent/loop.go b/agent/loop.go similarity index 99% rename from pkg/agent/loop.go rename to agent/loop.go index 2a9ec39..d642c6f 100644 --- a/pkg/agent/loop.go +++ b/agent/loop.go @@ -9,8 +9,8 @@ import ( "sync" "time" - "ollie/pkg/backend" - "ollie/pkg/tools" + "ollie/backend" + "ollie/tools" ) const maxTransientRetries = 3 diff --git a/pkg/agent/loop_cache_test.go b/agent/loop_cache_test.go similarity index 99% rename from pkg/agent/loop_cache_test.go rename to agent/loop_cache_test.go index c977bab..e38a870 100644 --- a/pkg/agent/loop_cache_test.go +++ b/agent/loop_cache_test.go @@ -6,7 +6,7 @@ import ( "sync/atomic" "testing" - "ollie/pkg/backend" + "ollie/backend" ) // TestResultCache_HitSkipsExec verifies that a second call to a read-safe tool diff --git a/pkg/agent/loop_errlimit_test.go b/agent/loop_errlimit_test.go similarity index 99% rename from pkg/agent/loop_errlimit_test.go rename to agent/loop_errlimit_test.go index f1ddf7c..b7e4ee7 100644 --- a/pkg/agent/loop_errlimit_test.go +++ b/agent/loop_errlimit_test.go @@ -8,7 +8,7 @@ import ( "sync/atomic" "testing" - "ollie/pkg/backend" + "ollie/backend" ) // alwaysFailStream returns a backend that issues a single tool call on every diff --git a/pkg/agent/loop_maxsteps_helpers_test.go b/agent/loop_maxsteps_helpers_test.go similarity index 98% rename from pkg/agent/loop_maxsteps_helpers_test.go rename to agent/loop_maxsteps_helpers_test.go index a538ff1..90afee5 100644 --- a/pkg/agent/loop_maxsteps_helpers_test.go +++ b/agent/loop_maxsteps_helpers_test.go @@ -4,7 +4,7 @@ import ( "context" "sync/atomic" - "ollie/pkg/backend" + "ollie/backend" ) // mockResponse defines a canned response for sequentialStream. diff --git a/pkg/agent/loop_maxsteps_test.go b/agent/loop_maxsteps_test.go similarity index 99% rename from pkg/agent/loop_maxsteps_test.go rename to agent/loop_maxsteps_test.go index 4acf8a0..feb518e 100644 --- a/pkg/agent/loop_maxsteps_test.go +++ b/agent/loop_maxsteps_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "ollie/pkg/backend" + "ollie/backend" ) // TestMaxStepsZeroUnlimited verifies that MaxSteps=0 does not trigger the diff --git a/pkg/agent/loop_parallel_test.go b/agent/loop_parallel_test.go similarity index 99% rename from pkg/agent/loop_parallel_test.go rename to agent/loop_parallel_test.go index 0016090..94267f2 100644 --- a/pkg/agent/loop_parallel_test.go +++ b/agent/loop_parallel_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "ollie/pkg/backend" + "ollie/backend" ) // toolsStream returns a backend respond function that issues the given tool diff --git a/pkg/agent/loop_truncate_test.go b/agent/loop_truncate_test.go similarity index 99% rename from pkg/agent/loop_truncate_test.go rename to agent/loop_truncate_test.go index c9d62c1..1ff7bc3 100644 --- a/pkg/agent/loop_truncate_test.go +++ b/agent/loop_truncate_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "ollie/pkg/backend" + "ollie/backend" ) // TestTruncation_LargeResultTruncated verifies that a tool result exceeding diff --git a/pkg/agent/loop_turnerror_test.go b/agent/loop_turnerror_test.go similarity index 99% rename from pkg/agent/loop_turnerror_test.go rename to agent/loop_turnerror_test.go index 2c2871f..c411f74 100644 --- a/pkg/agent/loop_turnerror_test.go +++ b/agent/loop_turnerror_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "ollie/pkg/backend" + "ollie/backend" ) // errStream returns a backend respond function that always returns the given error. diff --git a/pkg/agent/prompt_resolver.go b/agent/prompt_resolver.go similarity index 98% rename from pkg/agent/prompt_resolver.go rename to agent/prompt_resolver.go index ba8c0a8..dab0643 100644 --- a/pkg/agent/prompt_resolver.go +++ b/agent/prompt_resolver.go @@ -11,7 +11,6 @@ import ( "strings" "time" - "ollie/pkg/config" ) // PromptEnv returns the standard PRIME_* environment variables for prompt @@ -45,7 +44,7 @@ func PromptEnv(cwd string) []string { // - If the string starts with '!', the rest is executed as a shell command. // - If the expanded string names an existing file, the file is read. // - Otherwise the string is used as-is. -func resolvePrompt(p config.Prompt, cwd string, env []string) (string, error) { +func resolvePrompt(p Prompt, cwd string, env []string) (string, error) { if len(p.Value) == 0 { return "", nil } diff --git a/pkg/agent/runtime.go b/agent/runtime.go similarity index 96% rename from pkg/agent/runtime.go rename to agent/runtime.go index 9c47137..5074a05 100644 --- a/pkg/agent/runtime.go +++ b/agent/runtime.go @@ -3,8 +3,8 @@ package agent import ( "encoding/json" - "ollie/pkg/backend" - "ollie/pkg/tools" + "ollie/backend" + "ollie/tools" ) // Runtime holds the swappable per-agent configuration. It contains everything diff --git a/pkg/agent/session.go b/agent/session.go similarity index 99% rename from pkg/agent/session.go rename to agent/session.go index 4280d90..b1226a9 100644 --- a/pkg/agent/session.go +++ b/agent/session.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "ollie/pkg/backend" + "ollie/backend" ) const ( diff --git a/pkg/agent/state.go b/agent/state.go similarity index 98% rename from pkg/agent/state.go rename to agent/state.go index 88f8f42..3e9ce93 100644 --- a/pkg/agent/state.go +++ b/agent/state.go @@ -3,7 +3,7 @@ package agent import ( "context" - "ollie/pkg/backend" + "ollie/backend" ) // ResultTier classifies how long a tool result stays in the hot message list. diff --git a/pkg/agent/types.go b/agent/types.go similarity index 99% rename from pkg/agent/types.go rename to agent/types.go index 2534bdd..c70dc79 100644 --- a/pkg/agent/types.go +++ b/agent/types.go @@ -6,7 +6,7 @@ import ( "sync" "github.com/simonfxr/pubsub" - "ollie/pkg/backend" + "ollie/backend" ) // WatchField names supported by Core.WaitChange. diff --git a/pkg/agent/usage_log.go b/agent/usage_log.go similarity index 98% rename from pkg/agent/usage_log.go rename to agent/usage_log.go index 8c5597f..5086b9d 100644 --- a/pkg/agent/usage_log.go +++ b/agent/usage_log.go @@ -6,7 +6,7 @@ import ( "path/filepath" "time" - "ollie/pkg/paths" + "ollie/paths" ) // usageEntry is one line in the append-only usage JSONL log. diff --git a/pkg/agent/waitchange_test.go b/agent/waitchange_test.go similarity index 99% rename from pkg/agent/waitchange_test.go rename to agent/waitchange_test.go index 71fe308..9cc8271 100644 --- a/pkg/agent/waitchange_test.go +++ b/agent/waitchange_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - olog "ollie/pkg/log" + olog "ollie/log" ) // newTestCore returns a minimal agent with changeCond wired up. diff --git a/pkg/backend/anthropic.go b/backend/anthropic.go similarity index 100% rename from pkg/backend/anthropic.go rename to backend/anthropic.go diff --git a/pkg/backend/anthropic_stream_test.go b/backend/anthropic_stream_test.go similarity index 100% rename from pkg/backend/anthropic_stream_test.go rename to backend/anthropic_stream_test.go diff --git a/pkg/backend/backend.go b/backend/backend.go similarity index 100% rename from pkg/backend/backend.go rename to backend/backend.go diff --git a/pkg/backend/backend_test.go b/backend/backend_test.go similarity index 99% rename from pkg/backend/backend_test.go rename to backend/backend_test.go index 61e7395..5372d60 100644 --- a/pkg/backend/backend_test.go +++ b/backend/backend_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "ollie/pkg/backend" + "ollie/backend" ) func mustNewOpenAI(t *testing.T, name, baseURL, apiKey string) *backend.OpenAIBackend { diff --git a/pkg/backend/codewhisperer.go b/backend/codewhisperer.go similarity index 100% rename from pkg/backend/codewhisperer.go rename to backend/codewhisperer.go diff --git a/pkg/backend/codewhisperer_internal.go b/backend/codewhisperer_internal.go similarity index 100% rename from pkg/backend/codewhisperer_internal.go rename to backend/codewhisperer_internal.go diff --git a/pkg/backend/codewhisperer_test.go b/backend/codewhisperer_test.go similarity index 100% rename from pkg/backend/codewhisperer_test.go rename to backend/codewhisperer_test.go diff --git a/pkg/backend/copilot.go b/backend/copilot.go similarity index 100% rename from pkg/backend/copilot.go rename to backend/copilot.go diff --git a/pkg/backend/gemini.go b/backend/gemini.go similarity index 100% rename from pkg/backend/gemini.go rename to backend/gemini.go diff --git a/pkg/backend/new.go b/backend/new.go similarity index 99% rename from pkg/backend/new.go rename to backend/new.go index 2947373..4621795 100644 --- a/pkg/backend/new.go +++ b/backend/new.go @@ -6,7 +6,7 @@ import ( "os" "strings" - "ollie/pkg/paths" + "ollie/paths" ) // loadEnvFile reads KEY=VALUE pairs from path and sets any key that is not diff --git a/pkg/backend/new_test.go b/backend/new_test.go similarity index 100% rename from pkg/backend/new_test.go rename to backend/new_test.go diff --git a/pkg/backend/ollama.go b/backend/ollama.go similarity index 100% rename from pkg/backend/ollama.go rename to backend/ollama.go diff --git a/pkg/backend/ollama_stream_test.go b/backend/ollama_stream_test.go similarity index 100% rename from pkg/backend/ollama_stream_test.go rename to backend/ollama_stream_test.go diff --git a/pkg/backend/oneshot.go b/backend/oneshot.go similarity index 100% rename from pkg/backend/oneshot.go rename to backend/oneshot.go diff --git a/pkg/backend/openai.go b/backend/openai.go similarity index 100% rename from pkg/backend/openai.go rename to backend/openai.go diff --git a/pkg/backend/openai_encode_test.go b/backend/openai_encode_test.go similarity index 100% rename from pkg/backend/openai_encode_test.go rename to backend/openai_encode_test.go diff --git a/pkg/backend/openai_stream_test.go b/backend/openai_stream_test.go similarity index 100% rename from pkg/backend/openai_stream_test.go rename to backend/openai_stream_test.go diff --git a/pkg/backend/openai_test.go b/backend/openai_test.go similarity index 100% rename from pkg/backend/openai_test.go rename to backend/openai_test.go diff --git a/pkg/backend/pool.go b/backend/pool.go similarity index 100% rename from pkg/backend/pool.go rename to backend/pool.go diff --git a/pkg/backend/testhelper_test.go b/backend/testhelper_test.go similarity index 100% rename from pkg/backend/testhelper_test.go rename to backend/testhelper_test.go diff --git a/pkg/tools/execute/detach.go b/detach/process.go similarity index 66% rename from pkg/tools/execute/detach.go rename to detach/process.go index 64b9e29..a98eda2 100644 --- a/pkg/tools/execute/detach.go +++ b/detach/process.go @@ -1,4 +1,4 @@ -package execute +package detach import ( "fmt" @@ -8,25 +8,25 @@ import ( "time" ) -const ringBufSize = 64 * 1024 // 64KB ring buffer per detached process +const RingBufSize = 64 * 1024 // 64KB ring buffer per detached process -// DetachedProcess represents a process that the agent has detached from +// Process represents a process that the agent has detached from // but which continues running. The user can view its output and signal it. -type DetachedProcess struct { +type Process struct { PID int Command string Started time.Time Exited bool ExitCode int - ring *ringBuffer - cmd *os.Process - done chan struct{} - mu sync.Mutex + Ring *RingBuffer + Cmd *os.Process + Done chan struct{} + Mu sync.Mutex } // Info returns a plain-data snapshot of this process for external consumers. -type DetachedInfoData struct { +type InfoData struct { PID int Command string Started int64 // unix timestamp @@ -34,10 +34,10 @@ type DetachedInfoData struct { ExitCode int } -func (p *DetachedProcess) Info() DetachedInfoData { - p.mu.Lock() - defer p.mu.Unlock() - return DetachedInfoData{ +func (p *Process) Info() InfoData { + p.Mu.Lock() + defer p.Mu.Unlock() + return InfoData{ PID: p.PID, Command: p.Command, Started: p.Started.Unix(), @@ -47,20 +47,20 @@ func (p *DetachedProcess) Info() DetachedInfoData { } // Output returns the current contents of the ring buffer. -func (p *DetachedProcess) Output() string { - p.mu.Lock() - defer p.mu.Unlock() - return p.ring.String() +func (p *Process) Output() string { + p.Mu.Lock() + defer p.Mu.Unlock() + return p.Ring.String() } // Signal sends a signal to the detached process. -func (p *DetachedProcess) Signal(sig syscall.Signal) error { - p.mu.Lock() - defer p.mu.Unlock() +func (p *Process) Signal(sig syscall.Signal) error { + p.Mu.Lock() + defer p.Mu.Unlock() if p.Exited { return fmt.Errorf("process %d already exited", p.PID) } - if p.cmd == nil { + if p.Cmd == nil { return fmt.Errorf("process %d has no handle", p.PID) } // Send to process group @@ -68,19 +68,19 @@ func (p *DetachedProcess) Signal(sig syscall.Signal) error { } // ringBuffer is a fixed-size circular byte buffer. -type ringBuffer struct { +type RingBuffer struct { buf []byte size int pos int full bool } -func newRingBuffer(size int) *ringBuffer { - return &ringBuffer{buf: make([]byte, size), size: size} +func NewRingBuffer(size int) *RingBuffer { + return &RingBuffer{buf: make([]byte, size), size: size} } // Write implements io.Writer. -func (r *ringBuffer) Write(p []byte) (int, error) { +func (r *RingBuffer) Write(p []byte) (int, error) { n := len(p) if n >= r.size { // Data larger than buffer: just keep the tail @@ -105,7 +105,7 @@ func (r *ringBuffer) Write(p []byte) (int, error) { } // String returns the buffer contents in order. -func (r *ringBuffer) String() string { +func (r *RingBuffer) String() string { if !r.full { return string(r.buf[:r.pos]) } diff --git a/pkg/elevate/broker.go b/elevate/broker.go similarity index 100% rename from pkg/elevate/broker.go rename to elevate/broker.go diff --git a/pkg/elevate/broker_test.go b/elevate/broker_test.go similarity index 100% rename from pkg/elevate/broker_test.go rename to elevate/broker_test.go diff --git a/pkg/elevate/policy.go b/elevate/policy.go similarity index 100% rename from pkg/elevate/policy.go rename to elevate/policy.go diff --git a/pkg/elevate/policy_test.go b/elevate/policy_test.go similarity index 100% rename from pkg/elevate/policy_test.go rename to elevate/policy_test.go diff --git a/pkg/elevate/request.go b/elevate/request.go similarity index 100% rename from pkg/elevate/request.go rename to elevate/request.go diff --git a/pkg/env/env.go b/env/env.go similarity index 99% rename from pkg/env/env.go rename to env/env.go index b267c3e..c2f3081 100644 --- a/pkg/env/env.go +++ b/env/env.go @@ -10,7 +10,7 @@ import ( "path/filepath" "strings" - "ollie/pkg/paths" + "ollie/paths" ) // managed is the ordered list of env vars that ollie exposes to frontends. diff --git a/pkg/env/env_test.go b/env/env_test.go similarity index 100% rename from pkg/env/env_test.go rename to env/env_test.go diff --git a/pkg/remote/bootstrap.sh b/execute/bootstrap.sh similarity index 100% rename from pkg/remote/bootstrap.sh rename to execute/bootstrap.sh diff --git a/pkg/tools/execute/code.go b/execute/code.go similarity index 99% rename from pkg/tools/execute/code.go rename to execute/code.go index ab140c0..f3950af 100644 --- a/pkg/tools/execute/code.go +++ b/execute/code.go @@ -9,8 +9,8 @@ import ( "path/filepath" "sync" - "ollie/internal/sandbox" - "ollie/pkg/paths" + "ollie/sandbox" + "ollie/paths" "regexp" "strings" diff --git a/pkg/remote/remote.go b/execute/remote.go similarity index 94% rename from pkg/remote/remote.go rename to execute/remote.go index 6278ccd..b5370ca 100644 --- a/pkg/remote/remote.go +++ b/execute/remote.go @@ -2,7 +2,7 @@ // split-brain remote execution. It connects to a remote host over SSH, // ensures ollie-remote is deployed, and returns a tools.Server that // forwards execution calls over the RPC channel. -package remote +package execute import ( "bufio" @@ -25,7 +25,7 @@ import ( "syscall" "time" - "ollie/pkg/tools" + "ollie/tools" ) //go:embed bootstrap.sh @@ -40,7 +40,7 @@ type HostInfo struct { // Server implements tools.Server by forwarding calls to a remote // ollie-remote process over SSH. -type Server struct { +type RemoteServer struct { mu sync.Mutex stdin io.WriteCloser stdout io.ReadCloser @@ -52,7 +52,7 @@ type Server struct { } // Config holds the parameters for connecting to a remote host. -type Config struct { +type RemoteConfig struct { // SSHTarget is the SSH destination (e.g., "user@host" or an SSH config alias). SSHTarget string // CWD is the working directory on the remote host. @@ -65,7 +65,7 @@ type Config struct { // Dial opens an SSH connection to the remote host, bootstraps ollie-remote // (deploying the binary if needed), and returns a Server ready for tool calls. -func Dial(ctx context.Context, cfg Config) (*Server, error) { +func RemoteDial(ctx context.Context, cfg RemoteConfig) (*RemoteServer, error) { // Find the local ollie-remote binary to compute hash and transfer if needed. localBin, err := findLocalBinary() if err != nil { @@ -173,7 +173,7 @@ func Dial(ctx context.Context, cfg Config) (*Server, error) { } // Bootstrap complete — stdin/stdout are now JSON-RPC. - s := &Server{ + s := &RemoteServer{ stdin: stdin, stdout: stdout, cmd: cmd, @@ -240,7 +240,7 @@ func transferBinary(stdin io.Writer, binData []byte) error { } // Close shuts down the SSH connection. -func (s *Server) Close() error { +func (s *RemoteServer) Close() error { s.mu.Lock() defer s.mu.Unlock() if s.stdin != nil { @@ -255,7 +255,7 @@ func (s *Server) Close() error { // --- tools.Server interface --- -func (s *Server) ListTools() ([]tools.ToolInfo, error) { +func (s *RemoteServer) ListTools() ([]tools.ToolInfo, error) { id := s.nextID.Add(1) req := rpcRequest{ JSONRPC: "2.0", @@ -286,7 +286,7 @@ func (s *Server) ListTools() ([]tools.ToolInfo, error) { return infos, nil } -func (s *Server) CallTool(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error) { +func (s *RemoteServer) CallTool(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error) { // Forward the call with the tool name as the RPC method. id := s.nextID.Add(1) @@ -336,7 +336,7 @@ type outputNotification struct { } // fetchHostInfo retrieves environment details from the remote host. -func (s *Server) fetchHostInfo(ctx context.Context) (HostInfo, error) { +func (s *RemoteServer) fetchHostInfo(ctx context.Context) (HostInfo, error) { id := s.nextID.Add(1) req := rpcRequest{ JSONRPC: "2.0", @@ -365,7 +365,7 @@ func (s *Server) fetchHostInfo(ctx context.Context) (HostInfo, error) { } // ping verifies the remote server is responsive. -func (s *Server) ping(ctx context.Context) error { +func (s *RemoteServer) ping(ctx context.Context) error { id := s.nextID.Add(1) req := rpcRequest{ JSONRPC: "2.0", @@ -424,15 +424,15 @@ func shellEscape(s string) string { // Decl returns a factory function compatible with tools.NewDispatcherFunc. // It dials the remote on first call and returns the Server. -func Decl(cfg Config) func() tools.Server { +func RemoteDecl(cfg RemoteConfig) func() tools.Server { var ( once sync.Once - server *Server + server *RemoteServer err error ) return func() tools.Server { once.Do(func() { - server, err = Dial(context.Background(), cfg) + server, err = RemoteDial(context.Background(), cfg) if err != nil { // Return a stub that errors on every call server = nil diff --git a/pkg/tools/execute/server.go b/execute/server.go similarity index 94% rename from pkg/tools/execute/server.go rename to execute/server.go index 2d88ea6..3c4c4ea 100644 --- a/pkg/tools/execute/server.go +++ b/execute/server.go @@ -17,10 +17,11 @@ import ( "syscall" "time" - "ollie/internal/sandbox" - "ollie/pkg/paths" - "ollie/pkg/tools" - "ollie/pkg/skills" + "ollie/sandbox" + "ollie/paths" + "ollie/tools" + "ollie/skills" + "ollie/detach" ) const ( @@ -55,7 +56,7 @@ type Server struct { // Empty means all are allowed. allowTools map[string]bool - toolRegistry *Registry + toolRegistry *tools.Registry skillsRegistry *skills.Registry sessionID string @@ -72,7 +73,7 @@ type Server struct { // Detached process management detachMu sync.Mutex detachCh chan struct{} // signal to detach the currently running process - detached []*DetachedProcess + detached []*detach.Process OnDetach func(pid int, cmd string) // hook: called when a process is detached OnExit func(pid int, exitCode int) // hook: called when a detached process exits } @@ -124,7 +125,7 @@ func (e *Server) AllowTools() []string { // WithToolRegistry attaches a tool registry and session ID to the Server. -func WithToolRegistry(r *Registry, sessionID string) Option { +func WithToolRegistry(r *tools.Registry, sessionID string) Option { return func(s *Server) { s.toolRegistry = r s.sessionID = sessionID @@ -282,7 +283,7 @@ func (e *Server) SetEnv(key, value string) { } // SetToolRegistry attaches a session-local tool registry. -func (e *Server) SetToolRegistry(r *Registry, sessionID string) { +func (e *Server) SetToolRegistry(r *tools.Registry, sessionID string) { e.toolRegistry = r e.sessionID = sessionID } @@ -294,7 +295,7 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra if strings.Contains(tool, "/") || strings.Contains(tool, "..") { return nil, fmt.Errorf("invalid tool name") } - path := filepath.Join(ToolsPath(), tool) + path := filepath.Join(tools.ToolsPath(), tool) // Extract elevated flag (dispatch-level concern, not passed to tool). elevated := false @@ -354,7 +355,7 @@ func (e *Server) Close() { // executeElevated runs cmd outside the sandbox via the integrated elevation broker. // Connects to the broker socket, sends the request with the current env, // and streams the framed response back. -func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout int, detach ...bool) (string, error) { +func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout int, doDetach ...bool) (string, error) { sockPath := os.Getenv("OLLIE_ELEVATE_SOCKET") if sockPath == "" { xdg := os.Getenv("XDG_RUNTIME_DIR") @@ -364,7 +365,7 @@ func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout i sockPath = filepath.Join(xdg, "ollie", "elevate.sock") } - wantDetach := len(detach) > 0 && detach[0] + wantDetach := len(doDetach) > 0 && doDetach[0] var cancel context.CancelFunc if wantDetach || timeout <= 0 { @@ -468,15 +469,15 @@ func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout i if len(cmdStr) > 80 { cmdStr = cmdStr[:77] + "..." } - ring := newRingBuffer(ringBufSize) + ring := detach.NewRingBuffer(detach.RingBufSize) pid := int(time.Now().UnixNano() & 0x7FFFFFFF) // synthetic PID - proc := &DetachedProcess{ + proc := &detach.Process{ PID: pid, Command: cmdStr, Started: time.Now(), - ring: ring, - done: make(chan struct{}), + Ring: ring, + Done: make(chan struct{}), } e.detachMu.Lock() e.detached = append(e.detached, proc) @@ -486,11 +487,11 @@ func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout i defer conn.Close() defer cancel() exitCode := readFrames(ring, nil) - proc.mu.Lock() + proc.Mu.Lock() proc.Exited = true proc.ExitCode = exitCode - proc.mu.Unlock() - close(proc.done) + proc.Mu.Unlock() + close(proc.Done) if e.OnExit != nil { e.OnExit(proc.PID, proc.ExitCode) } @@ -554,7 +555,7 @@ func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout i if len(cmdStr) > 80 { cmdStr = cmdStr[:77] + "..." } - ring := newRingBuffer(ringBufSize) + ring := detach.NewRingBuffer(detach.RingBufSize) pid := int(time.Now().UnixNano() & 0x7FFFFFFF) // Splice: future output goes to ring buffer, stop streaming @@ -563,12 +564,12 @@ func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout i lw.stream = nil lw.mu.Unlock() - proc := &DetachedProcess{ + proc := &detach.Process{ PID: pid, Command: cmdStr, Started: time.Now(), - ring: ring, - done: make(chan struct{}), + Ring: ring, + Done: make(chan struct{}), } e.detachMu.Lock() e.detached = append(e.detached, proc) @@ -578,11 +579,11 @@ func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout i fr := <-frameCh conn.Close() cancel() - proc.mu.Lock() + proc.Mu.Lock() proc.Exited = true proc.ExitCode = fr.exitCode - proc.mu.Unlock() - close(proc.done) + proc.Mu.Unlock() + close(proc.Done) if e.OnExit != nil { e.OnExit(proc.PID, proc.ExitCode) } @@ -637,7 +638,7 @@ func (e *Server) Execute(ctx context.Context, code, language string, timeout int // executeWithStdin is like Execute but feeds stdinData to the command's stdin. // For languages where code is itself passed via stdin (ed, expect, bc), stdinData is ignored. -func (e *Server) executeWithStdin(ctx context.Context, code, language string, timeout int, sandboxName string, trusted bool, stdinData string, detach ...bool) (string, error) { +func (e *Server) executeWithStdin(ctx context.Context, code, language string, timeout int, sandboxName string, trusted bool, stdinData string, doDetach ...bool) (string, error) { if timeout < 0 { timeout = 30 } @@ -735,7 +736,7 @@ func (e *Server) executeWithStdin(ctx context.Context, code, language string, ti } } cmd.Env = prependOlliePath(filtered, paths.CfgDir()) - cmd.Env = append(cmd.Env, "OLLIE_TOOLS_PATH="+ToolsPath()) + cmd.Env = append(cmd.Env, "OLLIE_TOOLS_PATH="+tools.ToolsPath()) for k, v := range e.envExtra { cmd.Env = append(cmd.Env, k+"="+v) } @@ -776,7 +777,7 @@ func (e *Server) executeWithStdin(ctx context.Context, code, language string, ti } // If detach requested, immediately signal the detach channel - if len(detach) > 0 && detach[0] { + if len(doDetach) > 0 && doDetach[0] { close(detachCh) } @@ -830,20 +831,20 @@ func (e *Server) executeWithStdin(ctx context.Context, code, language string, ti if len(cmdStr) > 80 { cmdStr = cmdStr[:77] + "..." } - ring := newRingBuffer(ringBufSize) + ring := detach.NewRingBuffer(detach.RingBufSize) // Splice: future output goes to ring buffer instead of outputBuf. lw.mu.Lock() lw.w = ring lw.stream = nil lw.mu.Unlock() - proc := &DetachedProcess{ + proc := &detach.Process{ PID: cmd.Process.Pid, Command: cmdStr, Started: time.Now(), - ring: ring, - cmd: cmd.Process, - done: make(chan struct{}), + Ring: ring, + Cmd: cmd.Process, + Done: make(chan struct{}), } e.detachMu.Lock() e.detached = append(e.detached, proc) @@ -852,7 +853,7 @@ func (e *Server) executeWithStdin(ctx context.Context, code, language string, ti // Monitor for exit in background go func() { waitErr := <-waitCh - proc.mu.Lock() + proc.Mu.Lock() proc.Exited = true if waitErr != nil { if exitErr, ok := waitErr.(*exec.ExitError); ok { @@ -861,8 +862,8 @@ func (e *Server) executeWithStdin(ctx context.Context, code, language string, ti proc.ExitCode = -1 } } - proc.mu.Unlock() - close(proc.done) + proc.Mu.Unlock() + close(proc.Done) if e.OnExit != nil { e.OnExit(proc.PID, proc.ExitCode) } @@ -937,19 +938,19 @@ func (e *Server) Detach() bool { } // ListDetached returns all detached processes (running and exited). -func (e *Server) ListDetached() []*DetachedProcess { +func (e *Server) ListDetached() []*detach.Process { e.detachMu.Lock() defer e.detachMu.Unlock() - out := make([]*DetachedProcess, len(e.detached)) + out := make([]*detach.Process, len(e.detached)) copy(out, e.detached) return out } // ListDetachedInfo returns plain-data snapshots of all detached processes. -func (e *Server) ListDetachedInfo() []DetachedInfoData { +func (e *Server) ListDetachedInfo() []detach.InfoData { e.detachMu.Lock() defer e.detachMu.Unlock() - out := make([]DetachedInfoData, len(e.detached)) + out := make([]detach.InfoData, len(e.detached)) for i, p := range e.detached { out[i] = p.Info() } @@ -1017,11 +1018,11 @@ func (e *Server) cleanupDetached() { e.detachMu.Lock() defer e.detachMu.Unlock() for _, p := range e.detached { - p.mu.Lock() - if !p.Exited && p.cmd != nil { + p.Mu.Lock() + if !p.Exited && p.Cmd != nil { syscall.Kill(-p.PID, syscall.SIGTERM) } - p.mu.Unlock() + p.Mu.Unlock() } } diff --git a/pkg/tools/execute/skills.go b/execute/skills.go similarity index 99% rename from pkg/tools/execute/skills.go rename to execute/skills.go index 075e280..4764b6c 100644 --- a/pkg/tools/execute/skills.go +++ b/execute/skills.go @@ -6,8 +6,8 @@ import ( "fmt" "strings" - "ollie/pkg/skills" - "ollie/pkg/tools" + "ollie/skills" + "ollie/tools" ) // SetSkillsRegistry attaches a skills registry to the execute server. diff --git a/pkg/tools/execute/tier.go b/execute/tier.go similarity index 95% rename from pkg/tools/execute/tier.go rename to execute/tier.go index ae292d0..bfa3704 100644 --- a/pkg/tools/execute/tier.go +++ b/execute/tier.go @@ -2,6 +2,7 @@ package execute import ( "encoding/json" + "ollie/tools" "strings" ) @@ -13,7 +14,7 @@ func (e *Server) ResultTier(name string) string { return info.Tier } } - code, err := ReadTool(name) + code, err := tools.ReadTool(name) if err != nil { return "hot" } @@ -41,7 +42,7 @@ func (e *Server) IsParallelRead(name string) bool { return info.ReadOnly } } - code, err := ReadTool(name) + code, err := tools.ReadTool(name) if err != nil { return false } diff --git a/pkg/log/log.go b/log/log.go similarity index 100% rename from pkg/log/log.go rename to log/log.go diff --git a/pkg/log/log_test.go b/log/log_test.go similarity index 100% rename from pkg/log/log_test.go rename to log/log_test.go diff --git a/pkg/paths/paths.go b/paths/paths.go similarity index 100% rename from pkg/paths/paths.go rename to paths/paths.go diff --git a/pkg/paths/paths_test.go b/paths/paths_test.go similarity index 100% rename from pkg/paths/paths_test.go rename to paths/paths_test.go diff --git a/pkg/tools/execute/testdata/sandbox/default.yaml b/pkg/tools/execute/testdata/sandbox/default.yaml deleted file mode 100644 index 4ac565d..0000000 --- a/pkg/tools/execute/testdata/sandbox/default.yaml +++ /dev/null @@ -1,21 +0,0 @@ -filesystem: - rwx: - - "{CWD}" - - "/tmp" - - "/usr" - - "/bin" - - "/lib" - - "/lib64" - - "/etc" - - "{PLAN9}" - ro: - - "/proc" - - "/dev" -env: - - HOME - - PATH - - TMPDIR - - PLAN9 - - MY_TEST_VAR -network: - unrestricted: true diff --git a/pkg/tools/execute/testdata/tools/test_tool b/pkg/tools/execute/testdata/tools/test_tool deleted file mode 100644 index 17e9c70..0000000 --- a/pkg/tools/execute/testdata/tools/test_tool +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# args_json: {"type":"object","properties":{"input":{"type":"string"}},"required":["input"]} -# ollie:prompt -# ## test_tool -# -# A test tool for registry tests. -# ollie:end -echo "test ok" diff --git a/internal/sandbox/config.go b/sandbox/config.go similarity index 99% rename from internal/sandbox/config.go rename to sandbox/config.go index 6f3c3ff..47e0d11 100644 --- a/internal/sandbox/config.go +++ b/sandbox/config.go @@ -8,7 +8,7 @@ import ( "regexp" "strings" - "ollie/pkg/paths" + "ollie/paths" "gopkg.in/yaml.v3" ) diff --git a/internal/sandbox/detect.go b/sandbox/detect.go similarity index 100% rename from internal/sandbox/detect.go rename to sandbox/detect.go diff --git a/internal/sandbox/sandbox_test.go b/sandbox/sandbox_test.go similarity index 100% rename from internal/sandbox/sandbox_test.go rename to sandbox/sandbox_test.go diff --git a/internal/sandbox/wrapper.go b/sandbox/wrapper.go similarity index 100% rename from internal/sandbox/wrapper.go rename to sandbox/wrapper.go diff --git a/pkg/skills/registry.go b/skills/registry.go similarity index 100% rename from pkg/skills/registry.go rename to skills/registry.go diff --git a/pkg/skills/skills.go b/skills/skills.go similarity index 100% rename from pkg/skills/skills.go rename to skills/skills.go diff --git a/pkg/skills/skills_test.go b/skills/skills_test.go similarity index 100% rename from pkg/skills/skills_test.go rename to skills/skills_test.go diff --git a/pkg/tools/execute/tool.go b/tools/discover.go similarity index 95% rename from pkg/tools/execute/tool.go rename to tools/discover.go index acda055..17f09a0 100644 --- a/pkg/tools/execute/tool.go +++ b/tools/discover.go @@ -1,4 +1,4 @@ -package execute +package tools import ( "fmt" @@ -6,8 +6,7 @@ import ( "path/filepath" "strings" - "ollie/pkg/paths" - "ollie/pkg/tools" + "ollie/paths" ) // ToolsPath returns the directory to search for named tool scripts. @@ -72,13 +71,13 @@ func ExtractPrompt(script string) string { // DiscoverTools scans the tools directory and returns metadata for all scripts, // including their ollie:prompt blocks and short descriptions. -func DiscoverTools() []tools.ToolInfo { +func DiscoverTools() []ToolInfo { dir := ToolsPath() entries, err := os.ReadDir(dir) if err != nil { return nil } - var infos []tools.ToolInfo + var infos []ToolInfo for _, e := range entries { if e.IsDir() || e.Name() == "idx" || strings.HasPrefix(e.Name(), ".") { continue @@ -90,7 +89,7 @@ func DiscoverTools() []tools.ToolInfo { script := string(data) prompt := ExtractPrompt(script) desc := extractShortDescription(prompt) - infos = append(infos, tools.ToolInfo{ + infos = append(infos, ToolInfo{ Name: e.Name(), Description: desc, Prompt: prompt, diff --git a/pkg/tools/execute/registry.go b/tools/registry.go similarity index 80% rename from pkg/tools/execute/registry.go rename to tools/registry.go index b9a174c..372c68c 100644 --- a/pkg/tools/execute/registry.go +++ b/tools/registry.go @@ -1,4 +1,4 @@ -package execute +package tools import ( "fmt" @@ -8,21 +8,20 @@ import ( "path/filepath" "sync" - "ollie/pkg/tools" ) type Registry struct { mu sync.RWMutex - global map[string]tools.ToolInfo - sessions map[string]map[string]tools.ToolInfo + global map[string]ToolInfo + sessions map[string]map[string]ToolInfo revisions map[string]uint64 } func NewRegistry() (*Registry, error) { r := &Registry{ - global: make(map[string]tools.ToolInfo), - sessions: make(map[string]map[string]tools.ToolInfo), + global: make(map[string]ToolInfo), + sessions: make(map[string]map[string]ToolInfo), revisions: make(map[string]uint64), } if err := r.Discover(); err != nil { @@ -37,7 +36,7 @@ func (r *Registry) Discover() error { if err != nil { return fmt.Errorf("read tools dir %s: %w", dir, err) } - global := make(map[string]tools.ToolInfo) + global := make(map[string]ToolInfo) for _, e := range entries { if e.IsDir() || e.Name() == "idx" || e.Name()[0] == '.' { continue @@ -58,12 +57,12 @@ func (r *Registry) Discover() error { return nil } -func (r *Registry) Summaries() []tools.ToolInfo { +func (r *Registry) Summaries() []ToolInfo { r.mu.RLock() defer r.mu.RUnlock() - var summaries []tools.ToolInfo + var summaries []ToolInfo for _, info := range r.global { - summaries = append(summaries, tools.ToolInfo{ + summaries = append(summaries, ToolInfo{ Name: info.Name, Description: info.Description, }) @@ -84,7 +83,7 @@ func (r *Registry) Load(sessionID, name string) error { defer r.mu.Unlock() if r.sessions[sessionID] == nil { - r.sessions[sessionID] = make(map[string]tools.ToolInfo) + r.sessions[sessionID] = make(map[string]ToolInfo) } if _, already := r.sessions[sessionID][name]; already { @@ -114,7 +113,7 @@ func (r *Registry) Unload(sessionID, name string) error { return nil } -func (r *Registry) Loaded(sessionID string) []tools.ToolInfo { +func (r *Registry) Loaded(sessionID string) []ToolInfo { r.mu.RLock() defer r.mu.RUnlock() @@ -123,7 +122,7 @@ func (r *Registry) Loaded(sessionID string) []tools.ToolInfo { return nil } - var loaded []tools.ToolInfo + var loaded []ToolInfo for _, info := range sessionTools { loaded = append(loaded, info) } @@ -131,13 +130,13 @@ func (r *Registry) Loaded(sessionID string) []tools.ToolInfo { return loaded } -func (r *Registry) Lookup(sessionID, name string) (tools.ToolInfo, bool) { +func (r *Registry) Lookup(sessionID, name string) (ToolInfo, bool) { r.mu.RLock() defer r.mu.RUnlock() sessionTools, ok := r.sessions[sessionID] if !ok { - return tools.ToolInfo{}, false + return ToolInfo{}, false } tool, exists := sessionTools[name] diff --git a/pkg/tools/execute/schema.go b/tools/schema.go similarity index 95% rename from pkg/tools/execute/schema.go rename to tools/schema.go index 9f0fe23..78836ad 100644 --- a/pkg/tools/execute/schema.go +++ b/tools/schema.go @@ -1,10 +1,9 @@ -package execute +package tools import ( "encoding/json" "strings" - "ollie/pkg/tools" ) @@ -74,7 +73,7 @@ func ExtractMetadata(script string) ToolMeta { return meta } -func ParseToolInfo(name, script string) tools.ToolInfo { +func ParseToolInfo(name, script string) ToolInfo { prompt := ExtractPrompt(script) desc := ExtractShortDescription(prompt) argsSchema := ExtractArgsSchema(script) @@ -86,7 +85,7 @@ func ParseToolInfo(name, script string) tools.ToolInfo { if tier == "" { tier = "hot" } - return tools.ToolInfo{ + return ToolInfo{ Name: name, Description: desc, InputSchema: argsSchema, diff --git a/pkg/tools/stream.go b/tools/stream.go similarity index 100% rename from pkg/tools/stream.go rename to tools/stream.go diff --git a/pkg/tools/tools.go b/tools/tools.go similarity index 100% rename from pkg/tools/tools.go rename to tools/tools.go diff --git a/pkg/tools/tools_test.go b/tools/tools_test.go similarity index 99% rename from pkg/tools/tools_test.go rename to tools/tools_test.go index cadaf9e..eac34c9 100644 --- a/pkg/tools/tools_test.go +++ b/tools/tools_test.go @@ -6,7 +6,7 @@ import ( "fmt" "testing" - "ollie/pkg/tools" + "ollie/tools" ) // stubServer is a minimal Server used to verify the contract.