little_ghost 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -5
  3. data/docs/guides/code_mode.md +2 -2
  4. data/docs/guides/getting_started.md +26 -1
  5. data/docs/guides/integrations.md +20 -161
  6. data/docs/guides/mcp.md +288 -0
  7. data/docs/guides/models_and_providers.md +21 -54
  8. data/docs/guides/production.md +14 -5
  9. data/docs/guides/providers.md +275 -0
  10. data/docs/guides/sandboxing.md +42 -7
  11. data/docs/guides/skills.md +1 -3
  12. data/docs/guides/tools.md +2 -0
  13. data/exe/little_ghost +12 -0
  14. data/lib/little_ghost/assembly.rb +11 -3
  15. data/lib/little_ghost/assembly_builder.rb +2 -2
  16. data/lib/little_ghost/cli.rb +138 -0
  17. data/lib/little_ghost/console.rb +17 -0
  18. data/lib/little_ghost/errors.rb +1 -1
  19. data/lib/little_ghost/generators/application_generator.rb +179 -0
  20. data/lib/little_ghost/generators/templates/application/Gemfile.tt +3 -0
  21. data/lib/little_ghost/generators/templates/application/README.md.tt +29 -0
  22. data/lib/little_ghost/generators/templates/application/app/agents/application_agent.rb.tt +5 -0
  23. data/lib/little_ghost/generators/templates/application/app/prompts/application/system.erb.tt +1 -0
  24. data/lib/little_ghost/generators/templates/application/bin/application.tt +10 -0
  25. data/lib/little_ghost/generators/templates/application/config/little_ghost.rb.tt +5 -0
  26. data/lib/little_ghost/generators/templates/application/gitignore.tt +2 -0
  27. data/lib/little_ghost/generators/templates/application/keep.tt +0 -0
  28. data/lib/little_ghost/mcp/client.rb +295 -606
  29. data/lib/little_ghost/mcp/toolset.rb +133 -95
  30. data/lib/little_ghost/mcp.rb +21 -3
  31. data/lib/little_ghost/model_resolver.rb +2 -0
  32. data/lib/little_ghost/models/catalog/models_dev_source.rb +4 -1
  33. data/lib/little_ghost/provider_registry.rb +1 -0
  34. data/lib/little_ghost/providers/lm_studio/catalog_source.rb +201 -0
  35. data/lib/little_ghost/providers/lm_studio.rb +52 -0
  36. data/lib/little_ghost/providers/openai.rb +3 -75
  37. data/lib/little_ghost/providers/openai_compatible.rb +78 -1
  38. data/lib/little_ghost/sandbox/environment_policy.rb +29 -4
  39. data/lib/little_ghost/sandbox/policy.rb +28 -6
  40. data/lib/little_ghost/sandboxes/unrestricted.rb +7 -3
  41. data/lib/little_ghost/skills/catalog.rb +13 -3
  42. data/lib/little_ghost/tool.rb +6 -1
  43. data/lib/little_ghost/version.rb +1 -1
  44. data/lib/little_ghost.rb +1 -0
  45. metadata +37 -7
  46. data/lib/little_ghost/mcp/types.rb +0 -216
@@ -62,11 +62,11 @@ run = CustomerSupportAgent.ask(
62
62
  Build these settings in application code instead of passing request parameters
63
63
  through unchanged. Settings can affect cost, latency, and model behavior.
64
64
 
65
- ## Configure connections in one place
65
+ ## Connect the provider you chose
66
66
 
67
- LittleGhost includes adapters for OpenRouter, OpenAI-compatible APIs,
68
- Anthropic, Gemini, Vertex AI, and Bedrock. Connections may live in an
69
- initializer or in the conventional files under `config/little_ghost`.
67
+ Connections may live in an initializer or in the conventional files under
68
+ `config/little_ghost`. [Provider Support](providers.md) has copyable settings
69
+ for each built-in adapter, compatible endpoints, Ollama, and LM Studio.
70
70
 
71
71
  Keep credentials in your application's secret manager. Agents refer to a role
72
72
  or configured connection; they don't need to contain credentials. If your
@@ -120,6 +120,11 @@ choose a structured-result strategy.
120
120
  Provider capabilities can change. Handle failed Runs and provider errors even
121
121
  when the catalog says a feature is supported.
122
122
 
123
+ Most application code does not call the resolver directly. Declaring an
124
+ Agent's model, or passing `model:` to generation or embedding, resolves the
125
+ selection for you. Inspect model details when an interface needs to explain
126
+ limits or your application wants to check an attachment before sending it.
127
+
123
128
  ## Call a model without an Agent
124
129
 
125
130
  Some application work needs one model response rather than an Agent. Use
@@ -141,57 +146,23 @@ response.usage.total_tokens
141
146
  ```
142
147
 
143
148
  The operation returns a `LittleGhost::RunResult`, the same result type returned
144
- by an Agent invocation, so
145
- application code can read `output`, `usage`, and the final message in the same
146
- way. Plain generation makes one model request without starting an Agent or
147
- creating a Run. Structured generation may make one additional repair request.
148
-
149
- Pass a strict object schema when application code needs checked JSON:
149
+ by an Agent invocation. Application code can read `output`, `usage`, and the
150
+ final message in the same way. Plain generation makes one model request without
151
+ starting an Agent or creating a Run.
150
152
 
151
- ```ruby
152
- response = LittleGhost.generate(
153
- model: :customer_support,
154
- messages: [{role: :user, content: "My transfer is still pending."}],
155
- result_schema: {
156
- name: "classification",
157
- description: "Classify one support request",
158
- schema: {
159
- type: "object",
160
- properties: {category: {type: "string"}},
161
- required: ["category"],
162
- additionalProperties: false
163
- }
164
- }
165
- )
166
-
167
- response.output
168
- ```
169
-
170
- LittleGhost checks the result against the schema and gives the model one repair
171
- attempt by default. Add `structured_result_repair_attempts: 3` to the call
172
- above when a checked result warrants additional attempts. The setting accepts
173
- integers from zero through three.
174
-
175
- Read the checked value through `response.output`. If every permitted attempt is
176
- invalid, the call raises `LittleGhost::StructuredResultError`.
177
-
178
- A schema checks the shape of a value, not whether your application should act
179
- on it. Check identifiers, permissions, and business rules before using the
180
- result to change application state.
153
+ Pass `result_schema:` when application code needs checked JSON. [Structured
154
+ Results and Content](structured_outputs_and_content.md) covers strict schemas,
155
+ provider strategies, repair behavior, and the application checks that still
156
+ belong outside the schema.
181
157
 
182
158
  ## Create embeddings
183
159
 
184
160
  Use `LittleGhost.embed` when your application needs numeric representations for
185
161
  search, clustering, or another similarity-based feature:
186
162
 
187
- The example assumes application startup maps `search_embeddings` to an
188
- embedding model, following the role configuration shown earlier on this page.
189
- The selected provider adapter's API reference lists the model-specific
190
- settings.
191
-
192
163
  ```ruby
193
164
  response = LittleGhost.embed(
194
- model: :search_embeddings,
165
+ model: "primary:openai/text-embedding-3-small",
195
166
  inputs: ["Reset a password", "Track a transfer"]
196
167
  )
197
168
 
@@ -202,14 +173,10 @@ response.usage.input_tokens
202
173
 
203
174
  The response keeps vectors in the same order as the inputs. LittleGhost rejects
204
175
  an incomplete or malformed response instead of returning a partial batch.
205
- Choose the embedding model and request settings in trusted application
206
- configuration, and keep each call within a workload size your application can
207
- retry safely.
208
-
209
- Embedding text is sent to the selected provider. Choose a provider that is
210
- appropriate for that data, just as you would for an Agent request. See
211
- `LittleGhost::Embeddings::Request` and your provider adapter's API reference for
212
- the supported settings and request bounds.
176
+ Embedding text is sent to the selected provider, so choose one appropriate for
177
+ that data. [Provider Support](providers.md) identifies adapters with embedding
178
+ support; `LittleGhost::Embeddings::Request` documents settings and request
179
+ bounds.
213
180
 
214
181
  Continue with [Prompts as Views](prompt_views.md) when an Agent's instructions
215
182
  outgrow one string. See [Structured Results and Content](structured_outputs_and_content.md)
@@ -37,7 +37,15 @@ class CustomerSupportAgent < LittleGhost::Agent
37
37
  end
38
38
  ```
39
39
 
40
- Provider connections and model roles can also live in YAML files under `config/little_ghost`, or in files you select explicitly. Values set in Ruby take priority. An explicitly selected file comes next, followed by conventional files and environment defaults. See `LittleGhost::Configuration` when you need every supported source and override.
40
+ Provider connections and model roles can live in the Ruby initializer or in
41
+ YAML under `config/little_ghost`. Values set in Ruby take priority. See
42
+ `LittleGhost::Configuration` for custom files and the full precedence order.
43
+
44
+ [Provider Support](providers.md) lists the credentials and endpoint settings
45
+ for hosted and local providers. Choose connection names, model identifiers,
46
+ and custom headers during application startup. Read secrets from deployment
47
+ configuration or a secret manager; do not copy request parameters into these
48
+ settings.
41
49
 
42
50
  Prompts, caller input and history, Tool results, and attachments may go to the
43
51
  selected provider. Choose configured providers that are appropriate for that
@@ -289,14 +297,15 @@ LittleGhost.configure do |config|
289
297
  end
290
298
  config.sandbox = {
291
299
  provider: :native,
292
- files: {root: :read_write},
293
- root_filesystem: :isolated,
294
- environment: {inherit: false, set: {"LANG" => "C.UTF-8"}},
295
- network: :none
300
+ files: {root: :read_write}
296
301
  }
297
302
  end
298
303
  ```
299
304
 
305
+ The native backend isolates the host root, removes child-process network
306
+ access, and starts with a scrubbed child environment unless you configure a
307
+ different policy.
308
+
300
309
  The temporary Workspace above is useful when files should live for one Run. Use application-managed storage, with tenant isolation and concurrency control, when files must persist or several Runs share a root.
301
310
 
302
311
  The Run opens a Runtime-created Workspace before its Sandbox and closes them in reverse order after every outcome. Closing a Workspace invokes its configured teardown; it does not delete files by default. Cleanup failures raise because LittleGhost cannot confirm a clean shutdown of every owned resource. Existing instances passed by the application remain caller-owned.
@@ -0,0 +1,275 @@
1
+ # Provider Support
2
+
3
+ Use this page to connect a hosted provider or local model server. If you are
4
+ still deciding how Agents should choose models, start with [Models and
5
+ Providers](models_and_providers.md).
6
+
7
+ ## Compare provider support
8
+
9
+ Every connection uses the same configuration shape:
10
+
11
+ ```ruby
12
+ LittleGhost.configure do |config|
13
+ config.providers = {
14
+ primary: {
15
+ adapter: :openrouter,
16
+ api_key: ENV.fetch("OPENROUTER_API_KEY")
17
+ }
18
+ }
19
+ end
20
+ ```
21
+
22
+ `primary` is a name chosen by the application, and `adapter` selects the wire
23
+ protocol. An Agent can now use a target such as
24
+ `primary:openai/gpt-5.6-luna`.
25
+
26
+ | Provider or endpoint | Adapter | Generation | Embeddings |
27
+ | --- | --- | --- | --- |
28
+ | OpenRouter | `:openrouter` | Chat Completions | When the model supports it |
29
+ | OpenAI | `:openai` | Responses or Chat Completions | Yes |
30
+ | Compatible API | `:openai_compatible` | Responses or Chat Completions | When the endpoint implements it |
31
+ | Ollama | `:openai_compatible` | Responses or Chat Completions | When the model supports it |
32
+ | Anthropic | `:anthropic` | Messages | No |
33
+ | Gemini | `:gemini` | Gemini API | No |
34
+ | Vertex AI | `:vertex_ai` | Gemini on Vertex AI | No |
35
+ | Bedrock | `:bedrock` | Converse | Titan Text Embeddings V2 |
36
+ | LM Studio | `:lm_studio` | Responses or Chat Completions | Yes |
37
+
38
+ Capabilities can vary by model, account, and server version. Handle provider
39
+ failures even when a model is expected to support a feature.
40
+
41
+ ## Hosted providers
42
+
43
+ ### OpenRouter
44
+
45
+ OpenRouter gives one connection access to models from several providers:
46
+
47
+ ```ruby
48
+ config.providers = {
49
+ openrouter: {
50
+ adapter: :openrouter,
51
+ api_key: ENV.fetch("OPENROUTER_API_KEY"),
52
+ app_name: "Support Console",
53
+ site_url: "https://support.example.com"
54
+ }
55
+ }
56
+ ```
57
+
58
+ Use OpenRouter's `publisher/model` form, such as
59
+ `openrouter:anthropic/claude-sonnet-4`. `app_name` and `site_url` are optional.
60
+ The selected model must support embeddings before you call
61
+ `LittleGhost.embed`.
62
+
63
+ ### OpenAI
64
+
65
+ ```ruby
66
+ config.providers = {
67
+ openai: {
68
+ adapter: :openai,
69
+ api_key: ENV.fetch("OPENAI_API_KEY")
70
+ }
71
+ }
72
+ ```
73
+
74
+ Generation uses the Responses API by default. Use `openai:gpt-5.6-luna` for
75
+ generation or `openai:text-embedding-3-small` for embeddings. Set
76
+ `api: :chat_completions` only when required.
77
+
78
+ ### Anthropic
79
+
80
+ ```ruby
81
+ config.providers = {
82
+ anthropic: {
83
+ adapter: :anthropic,
84
+ api_key: ENV.fetch("ANTHROPIC_API_KEY")
85
+ }
86
+ }
87
+ ```
88
+
89
+ Use a target such as `anthropic:claude-sonnet-4-6`. The adapter uses the
90
+ Messages API. LittleGhost does not provide Anthropic embeddings.
91
+
92
+ ### Gemini
93
+
94
+ ```ruby
95
+ config.providers = {
96
+ gemini: {
97
+ adapter: :gemini,
98
+ api_key: ENV.fetch("GEMINI_API_KEY")
99
+ }
100
+ }
101
+ ```
102
+
103
+ Use `gemini:gemini-2.5-flash`, for example. LittleGhost does not provide Gemini
104
+ embeddings.
105
+
106
+ ### Vertex AI
107
+
108
+ Vertex AI uses Gemini's request format with a Google Cloud project and
109
+ location:
110
+
111
+ ```ruby
112
+ config.providers = {
113
+ vertex: {
114
+ adapter: :vertex_ai,
115
+ project: ENV.fetch("GOOGLE_CLOUD_PROJECT"),
116
+ location: "global"
117
+ }
118
+ }
119
+ ```
120
+
121
+ Use a target such as `vertex:gemini-2.5-flash`. The credential resolver can
122
+ obtain Google credentials, or startup can supply an access token. LittleGhost
123
+ does not provide Vertex AI embeddings.
124
+
125
+ ### Amazon Bedrock
126
+
127
+ ```ruby
128
+ config.providers = {
129
+ bedrock: {
130
+ adapter: :bedrock,
131
+ region: ENV.fetch("AWS_REGION")
132
+ }
133
+ }
134
+ ```
135
+
136
+ Use a model available in the region, such as
137
+ `bedrock:us.anthropic.claude-sonnet-4-6-v1:0`. The adapter resolves AWS
138
+ credentials and signs requests without an AWS SDK.
139
+
140
+ Bedrock embeddings support `amazon.titan-embed-text-v2:0`. Its `dimensions`
141
+ setting accepts 256, 512, or 1024, and `normalize` defaults to `true`.
142
+
143
+ ## Compatible APIs and local servers
144
+
145
+ ### Another OpenAI-compatible API
146
+
147
+ Use `:openai_compatible` for a hosted service, gateway, or local server that
148
+ implements OpenAI-style endpoints:
149
+
150
+ ```ruby
151
+ config.providers = {
152
+ models: {
153
+ adapter: :openai_compatible,
154
+ base_url: "https://models.example.com/v1/",
155
+ api_key: ENV.fetch("MODEL_API_KEY"),
156
+ api: :responses
157
+ }
158
+ }
159
+ ```
160
+
161
+ Use the model identifier expected by the endpoint, such as
162
+ `models:example-model`. Choose `api: :responses` or
163
+ `api: :chat_completions` to match the server. `LittleGhost.embed` uses the
164
+ connection's `embeddings` endpoint and supports `dimensions` when the server
165
+ and model accept it.
166
+
167
+ Tool calls, structured results, reasoning, attachments, and embeddings depend
168
+ on the compatible server and model.
169
+
170
+ Ollama and LM Studio normally listen on loopback HTTP. LittleGhost requires
171
+ `allow_insecure_http: true` as an explicit opt-in because HTTP does not protect
172
+ prompts, output, or credentials in transit. Use this setting only for a local
173
+ or otherwise trusted endpoint; use HTTPS for a remote server.
174
+
175
+ ### Ollama
176
+
177
+ Pull a model, start Ollama, and use the existing OpenAI-compatible adapter:
178
+
179
+ ```sh
180
+ $ ollama pull qwen3
181
+ ```
182
+
183
+ ```ruby
184
+ config.providers = {
185
+ ollama: {
186
+ adapter: :openai_compatible,
187
+ base_url: "http://localhost:11434/v1/",
188
+ api_key: "ollama",
189
+ api: :responses,
190
+ allow_insecure_http: true
191
+ }
192
+ }
193
+ ```
194
+
195
+ Use the pulled model name in the target, such as `ollama:qwen3`. The `api_key`
196
+ is a placeholder for a local server without authentication.
197
+
198
+ Ollama added its Responses endpoint in version 0.13.3. Set
199
+ `api: :chat_completions` for an earlier release or when the selected model does
200
+ not support the Responses features you need. See Ollama's [OpenAI compatibility
201
+ reference](https://docs.ollama.com/api/openai-compatibility) for release-specific
202
+ fields.
203
+
204
+ For embeddings, pull an embedding model and pass its target to
205
+ `LittleGhost.embed`. LittleGhost does not list, pull, or delete Ollama models.
206
+
207
+ ### LM Studio
208
+
209
+ Start LM Studio's local server after downloading or loading the model you want
210
+ to use. The dedicated adapter supplies the usual local endpoint and a
211
+ placeholder credential:
212
+
213
+ ```ruby
214
+ config.providers = {
215
+ lm_studio: {
216
+ adapter: :lm_studio,
217
+ allow_insecure_http: true
218
+ }
219
+ }
220
+ ```
221
+
222
+ Use the model key shown by LM Studio, for example
223
+ `lm_studio:google/gemma-3-4b`. Generation uses the Responses API by default.
224
+
225
+ Set `api: :chat_completions` with LM Studio releases earlier than 0.3.29, or
226
+ when required by the selected model. `LittleGhost.embed` uses the same
227
+ connection with an embedding model. See LM Studio's [compatibility
228
+ reference](https://lmstudio.ai/docs/developer/openai-compat) for its current
229
+ generation and embedding endpoints.
230
+
231
+ When LM Studio authentication is enabled, add
232
+ `api_key: ENV.fetch("LM_STUDIO_API_TOKEN")` to the connection.
233
+
234
+ The native model list and API-token authentication require LM Studio 0.4.0 or
235
+ newer. LittleGhost reads the list; it does not load, unload, or download models.
236
+
237
+ ## Discover current model details
238
+
239
+ Call `refresh!` when you need current availability, loaded state, or
240
+ capabilities. Ordinary model requests do not make discovery calls.
241
+
242
+ ```ruby
243
+ resolver = LittleGhost.model_resolver
244
+ result = resolver.refresh!(target: "lm_studio:google/gemma-3-4b")
245
+ result[:errors]
246
+
247
+ details = resolver.details("lm_studio:google/gemma-3-4b")
248
+ details[:loaded]
249
+ details.context_window
250
+ details.input_modalities
251
+ details.supported_parameters
252
+ ```
253
+
254
+ Pass `target:` for one model, or omit it for every configured source. On
255
+ failure, `result[:errors]` describes the problem and previous details remain.
256
+
257
+ Hosted adapters can combine models.dev data with their provider's model API.
258
+ LM Studio reads its native model list. The generic compatible adapter and
259
+ Ollama do not have built-in model discovery.
260
+
261
+ LM Studio details can include downloaded and loaded state, active context
262
+ length, input types, tools, and reasoning. An unloaded model has no active
263
+ context length. See `LittleGhost::Models::Details` for every field.
264
+
265
+ ## Keep connection settings in application code
266
+
267
+ Choose keys, tokens, headers, base URLs, model identifiers, and API variants at
268
+ startup. Read secrets from deployment configuration or a secret manager. Do
269
+ not copy public request parameters into provider settings.
270
+
271
+ The selected provider may receive prompts, conversation history, Tool results,
272
+ schemas, and attachments. Choose one appropriate for that data.
273
+
274
+ Continue with [Running in Production](production.md) to place connections,
275
+ roles, sessions, and observability into a long-running application.
@@ -23,10 +23,7 @@ LittleGhost.configure do |config|
23
23
 
24
24
  config.sandbox = {
25
25
  provider: :native,
26
- files: {root: :read_write},
27
- root_filesystem: :isolated,
28
- environment: {inherit: false, set: {"LANG" => "C.UTF-8"}},
29
- network: :none
26
+ files: {root: :read_write}
30
27
  }
31
28
  end
32
29
  ```
@@ -34,7 +31,9 @@ end
34
31
  This example gives each Run a temporary writable root and removes it during
35
32
  teardown. The `:native` Sandbox backend selects Seatbelt on macOS or Bubblewrap on
36
33
  Linux. It raises instead of running without isolation when the native backend
37
- is unavailable.
34
+ is unavailable. Unless you configure otherwise, it isolates the host root,
35
+ removes child-process network access, and starts child processes without
36
+ inheriting the host environment.
38
37
 
39
38
  `LittleGhost::Tools::Filesystem` and `LittleGhost::Tools::Shell` use the Sandbox
40
39
  assigned to the current Run. Code-mode interpreters also run inside their own
@@ -148,8 +147,7 @@ config.sandbox = {
148
147
  },
149
148
  runtime_paths: {
150
149
  home: :read_write
151
- },
152
- network: :none
150
+ }
153
151
  }
154
152
  ```
155
153
 
@@ -200,6 +198,39 @@ Workspace paths:
200
198
  On Seatbelt, host-visible modes permit subprocesses because development tools
201
199
  often need them. A Scope can remove `process_spawn` for a command that does not.
202
200
 
201
+ ## Start child processes with only the values they need
202
+
203
+ When `environment` is omitted, sandboxed processes do not inherit the host
204
+ environment. LittleGhost starts with `LANG`, `LC_ALL`, and `PATH`, then adds
205
+ `LITTLE_GHOST_WORKSPACE_*` variables so the child can locate its Workspace.
206
+ Locale values come from the host when present and otherwise use `C.UTF-8`.
207
+ `PATH` keeps the host's absolute entries, removes empty, relative, and duplicate
208
+ entries, and falls back to `/usr/local/bin:/usr/bin:/bin` when nothing remains.
209
+ Credentials, provider keys, package-manager settings, and home-directory
210
+ variables are not copied into the child.
211
+
212
+ > **Safety note:** An absolute directory is not necessarily a trusted one. A
213
+ > host `PATH` may include project directories, temporary directories, or
214
+ > user-managed shims. Configure a fixed `PATH` when a model can invoke commands
215
+ > by name and you do not trust every host entry.
216
+
217
+ Declaring `environment` replaces the locale and path baseline instead of
218
+ merging with it. Use an empty Hash to omit all configurable values, or set
219
+ exactly what the process needs. Workspace routing variables remain available:
220
+
221
+ ```ruby
222
+ config.sandbox = {
223
+ provider: :native,
224
+ environment: {
225
+ inherit: false,
226
+ set: {"LANG" => "C.UTF-8", "PATH" => "/opt/my-tool/bin:/usr/bin"}
227
+ }
228
+ }
229
+ ```
230
+
231
+ Host inheritance requires both `inherit: true` in the Sandbox policy and an
232
+ individual process call that opts into inheritance.
233
+
203
234
  ## Choose an enforcement backend
204
235
 
205
236
  | Sandbox backend | Host | What it enforces |
@@ -260,6 +291,10 @@ Sandbox networking has three modes:
260
291
  - `:allowlist` requires an enforcing gateway: a supervised proxy that permits
261
292
  only configured destinations.
262
293
 
294
+ Seatbelt, Bubblewrap, and `:native` default to `:none`; declare a network mode
295
+ only when the child needs a different policy. The unrestricted backend uses
296
+ `:inherit` because it is not a security boundary.
297
+
263
298
  Proxy environment variables alone are not an allowlist. An external gateway
264
299
  uses named, process-only Workspace paths and verifies that they still point to
265
300
  the configured directories; it does not create a virtual path mapping.
@@ -107,9 +107,7 @@ LittleGhost.configure do |config|
107
107
 
108
108
  config.sandbox = {
109
109
  provider: :native,
110
- files: {root: :read_write, skills: :read_only},
111
- root_filesystem: :isolated,
112
- network: :none
110
+ files: {root: :read_write, skills: :read_only}
113
111
  }
114
112
  end
115
113
  ```
data/docs/guides/tools.md CHANGED
@@ -79,6 +79,8 @@ end
79
79
  Prefer `available_if` on an individual Tool when only that operation is
80
80
  conditional. Use a provider when the collection itself owns discovery,
81
81
  construction, or shared setup for an application or remote service.
82
+ The [MCP guide](mcp.md) shows how a Model Context Protocol server provides a
83
+ Toolset through the official Ruby client.
82
84
 
83
85
  LittleGhost does not enforce class names. A useful application convention is
84
86
  to end one model-callable operation with `Tool` and a provider of multiple
data/exe/little_ghost ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path("../lib", __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ require "little_ghost/cli"
8
+
9
+ source_path = File.expand_path("..", __dir__)
10
+ source_path = nil unless File.file?(File.join(source_path, "little_ghost.gemspec"))
11
+
12
+ exit LittleGhost::CLI.new(ARGV, source_path:).run
@@ -50,7 +50,8 @@ module LittleGhost
50
50
  # +context+, +settings+, +metadata+, +session_id+, +actor_id+, and
51
51
  # +deadline_at+.
52
52
  def ask(message, **options)
53
- definition.implementation.new.ask(message, **options)
53
+ snapshot = definition
54
+ snapshot.implementation.new.bind_assembly_definition(snapshot).ask(message, **options)
54
55
  end
55
56
 
56
57
  # Lazily streams +message+ through a fresh standalone assembly.
@@ -66,7 +67,7 @@ module LittleGhost
66
67
  snapshot = definition
67
68
  stream = nil
68
69
  Enumerator.new do |events|
69
- stream ||= snapshot.implementation.new.stream_ask(message, **options)
70
+ stream ||= snapshot.implementation.new.bind_assembly_definition(snapshot).stream_ask(message, **options)
70
71
  stream.each { |event| events << event }
71
72
  end
72
73
  end
@@ -157,6 +158,7 @@ module LittleGhost
157
158
  @runtime = runtime || run&.runtime || LittleGhost.runtime
158
159
  @workspace = workspace || run&.workspace
159
160
  @sandbox = sandbox || run&.sandbox
161
+ @assembly_definition = nil
160
162
  @standalone = standalone
161
163
  @assembly_mutex = Mutex.new
162
164
  @assembly_closed = false
@@ -169,6 +171,11 @@ module LittleGhost
169
171
  self
170
172
  end
171
173
 
174
+ def bind_assembly_definition(definition) # :nodoc:
175
+ @assembly_definition = definition
176
+ self
177
+ end
178
+
172
179
  # Builds the top-level Run used by a standalone assembly.
173
180
  def build_run(payload = nil, include_agent_events_by_default: false, **payload_options) # :nodoc:
174
181
  payload = entrypoint_payload(payload, payload_options) unless payload_options.empty?
@@ -178,7 +185,8 @@ module LittleGhost
178
185
  end
179
186
  source_class = run_entrypoint_class
180
187
  options = {entrypoint_class: source_class}
181
- options[:execution_class] = self.class unless source_class.equal?(self.class)
188
+ execution = @assembly_definition || self.class
189
+ options[:execution_class] = execution unless source_class.equal?(execution)
182
190
  options[:agent_class] = source_class if is_a?(Agent)
183
191
  options[:cancellation_token] = cancellation_token if cancellation_token
184
192
  options[:workspace] = workspace if workspace
@@ -123,7 +123,7 @@ module LittleGhost
123
123
  snapshot = definition
124
124
  return (runtime || run.runtime).build_assembly(snapshot, run:) if run
125
125
 
126
- snapshot.implementation.new(runtime:)
126
+ snapshot.implementation.new(runtime:).bind_assembly_definition(snapshot)
127
127
  end
128
128
 
129
129
  # Executes the current declarations and returns their Run.
@@ -133,7 +133,7 @@ module LittleGhost
133
133
  def stream_ask(message, **options)
134
134
  snapshot = definition
135
135
  Enumerator.new do |events|
136
- snapshot.implementation.new(runtime:).stream_ask(message, **options).each { |event| events << event }
136
+ snapshot.implementation.new(runtime:).bind_assembly_definition(snapshot).stream_ask(message, **options).each { |event| events << event }
137
137
  end
138
138
  end
139
139