little_ghost 0.3.0 → 0.5.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +90 -84
  3. data/docs/guides/assemblies.md +412 -0
  4. data/docs/guides/code_mode.md +275 -0
  5. data/docs/guides/core_concepts.md +150 -239
  6. data/docs/guides/getting_started.md +125 -87
  7. data/docs/guides/integrations.md +217 -0
  8. data/docs/guides/models_and_providers.md +125 -0
  9. data/docs/guides/production.md +253 -0
  10. data/docs/guides/prompt_views.md +139 -0
  11. data/docs/guides/sandboxing.md +282 -0
  12. data/docs/guides/skills.md +141 -0
  13. data/docs/guides/structured_outputs_and_content.md +135 -0
  14. data/docs/guides/tools.md +329 -0
  15. data/lib/little_ghost/ag_ui/adapter.rb +5 -5
  16. data/lib/little_ghost/agent/delegation.rb +3 -3
  17. data/lib/little_ghost/agent/skills.rb +6 -1
  18. data/lib/little_ghost/agent/tool_loop.rb +6 -2
  19. data/lib/little_ghost/agent.rb +411 -214
  20. data/lib/little_ghost/agent_builder.rb +61 -22
  21. data/lib/little_ghost/{agent_interruptions.rb → agent_interjections.rb} +12 -12
  22. data/lib/little_ghost/agent_stream_source.rb +262 -0
  23. data/lib/little_ghost/artifact.rb +182 -0
  24. data/lib/little_ghost/artifacts/presentation_budget.rb +44 -0
  25. data/lib/little_ghost/artifacts/workspace_store.rb +370 -0
  26. data/lib/little_ghost/artifacts.rb +11 -0
  27. data/lib/little_ghost/assembly.rb +110 -30
  28. data/lib/little_ghost/assembly_builder.rb +59 -21
  29. data/lib/little_ghost/assembly_execution.rb +110 -6
  30. data/lib/little_ghost/code_mode/broker.rb +164 -0
  31. data/lib/little_ghost/code_mode/catalog.rb +54 -0
  32. data/lib/little_ghost/code_mode/engine.rb +58 -0
  33. data/lib/little_ghost/code_mode/javascript/catalog.rb +118 -0
  34. data/lib/little_ghost/code_mode/javascript/client.rb +550 -0
  35. data/lib/little_ghost/code_mode/javascript/host.rb +347 -0
  36. data/lib/little_ghost/code_mode/javascript/session.rb +579 -0
  37. data/lib/little_ghost/code_mode/javascript_engine.rb +172 -0
  38. data/lib/little_ghost/code_mode/protocol.rb +78 -0
  39. data/lib/little_ghost/code_mode/ruby/catalog.rb +80 -0
  40. data/lib/little_ghost/code_mode/ruby/host.rb +102 -0
  41. data/lib/little_ghost/code_mode/ruby/session.rb +508 -0
  42. data/lib/little_ghost/code_mode/ruby_engine.rb +86 -0
  43. data/lib/little_ghost/code_mode/runtime.rb +335 -0
  44. data/lib/little_ghost/code_mode/session.rb +61 -0
  45. data/lib/little_ghost/code_mode/types.rb +58 -0
  46. data/lib/little_ghost/code_mode.rb +53 -0
  47. data/lib/little_ghost/configuration.rb +366 -47
  48. data/lib/little_ghost/content.rb +24 -13
  49. data/lib/little_ghost/data_map.rb +209 -0
  50. data/lib/little_ghost/errors.rb +28 -9
  51. data/lib/little_ghost/execution.rb +33 -34
  52. data/lib/little_ghost/graph.rb +376 -197
  53. data/lib/little_ghost/mcp/client.rb +487 -88
  54. data/lib/little_ghost/mcp/toolset.rb +210 -0
  55. data/lib/little_ghost/mcp/types.rb +216 -0
  56. data/lib/little_ghost/mcp.rb +3 -0
  57. data/lib/little_ghost/message.rb +4 -4
  58. data/lib/little_ghost/model_capabilities.rb +9 -6
  59. data/lib/little_ghost/model_request.rb +0 -12
  60. data/lib/little_ghost/model_resolver.rb +15 -5
  61. data/lib/little_ghost/model_response.rb +3 -7
  62. data/lib/little_ghost/network/authorizer_server.rb +162 -0
  63. data/lib/little_ghost/network/certificate_authority.rb +98 -0
  64. data/lib/little_ghost/network/envoy_config.rb +362 -0
  65. data/lib/little_ghost/network/envoy_gateway.rb +409 -0
  66. data/lib/little_ghost/network/external_gateway.rb +68 -0
  67. data/lib/little_ghost/network.rb +96 -0
  68. data/lib/little_ghost/prompt_resolver.rb +9 -7
  69. data/lib/little_ghost/provider_registry.rb +3 -3
  70. data/lib/little_ghost/providers/anthropic.rb +8 -1
  71. data/lib/little_ghost/providers/gemini.rb +10 -1
  72. data/lib/little_ghost/providers/vertex_ai.rb +6 -1
  73. data/lib/little_ghost/run.rb +162 -54
  74. data/lib/little_ghost/run_context.rb +42 -20
  75. data/lib/little_ghost/run_result.rb +0 -7
  76. data/lib/little_ghost/runtime/hook.rb +8 -3
  77. data/lib/little_ghost/runtime/hooks/artifacts.rb +338 -0
  78. data/lib/little_ghost/runtime.rb +160 -52
  79. data/lib/little_ghost/sandbox/capabilities.rb +68 -0
  80. data/lib/little_ghost/sandbox/environment_policy.rb +41 -0
  81. data/lib/little_ghost/sandbox/filesystem.rb +377 -0
  82. data/lib/little_ghost/sandbox/isolated_backend.rb +163 -0
  83. data/lib/little_ghost/sandbox/limits.rb +48 -0
  84. data/lib/little_ghost/sandbox/mount.rb +129 -0
  85. data/lib/little_ghost/sandbox/network_policy.rb +108 -0
  86. data/lib/little_ghost/sandbox/policy.rb +88 -0
  87. data/lib/little_ghost/sandbox/process_runner.rb +103 -0
  88. data/lib/little_ghost/sandbox/process_session.rb +335 -0
  89. data/lib/little_ghost/sandbox/scope.rb +304 -0
  90. data/lib/little_ghost/sandbox.rb +158 -32
  91. data/lib/little_ghost/sandboxes/bubblewrap.rb +351 -0
  92. data/lib/little_ghost/sandboxes/native.rb +51 -0
  93. data/lib/little_ghost/sandboxes/seatbelt.rb +261 -0
  94. data/lib/little_ghost/sandboxes/unrestricted.rb +241 -0
  95. data/lib/little_ghost/session.rb +39 -26
  96. data/lib/little_ghost/session_store.rb +9 -5
  97. data/lib/little_ghost/session_stores/agent_core_memory.rb +64 -56
  98. data/lib/little_ghost/session_stores/filesystem.rb +261 -0
  99. data/lib/little_ghost/session_stores/memory.rb +7 -0
  100. data/lib/little_ghost/skills/catalog.rb +94 -14
  101. data/lib/little_ghost/skills/resource_root.rb +42 -0
  102. data/lib/little_ghost/skills/skill.rb +0 -3
  103. data/lib/little_ghost/stream_event.rb +8 -13
  104. data/lib/little_ghost/subagents/control_tool.rb +8 -0
  105. data/lib/little_ghost/subagents/manager.rb +53 -50
  106. data/lib/little_ghost/support/callbacks.rb +3 -1
  107. data/lib/little_ghost/support/content_capture.rb +3 -3
  108. data/lib/little_ghost/support/http_client.rb +2 -2
  109. data/lib/little_ghost/support/redactor.rb +1 -1
  110. data/lib/little_ghost/swarm.rb +13 -5
  111. data/lib/little_ghost/tool.rb +157 -64
  112. data/lib/little_ghost/tool_registry.rb +1 -1
  113. data/lib/little_ghost/tools/filesystem.rb +1 -1
  114. data/lib/little_ghost/tools/shell.rb +4 -3
  115. data/lib/little_ghost/tools/write_todos.rb +6 -1
  116. data/lib/little_ghost/tracing/open_telemetry.rb +1 -1
  117. data/lib/little_ghost/version.rb +1 -1
  118. data/lib/little_ghost/workflow.rb +30 -21
  119. data/lib/little_ghost/workspace.rb +222 -8
  120. data/lib/little_ghost.rb +40 -27
  121. metadata +104 -3
  122. data/lib/little_ghost/unrestricted_sandbox.rb +0 -306
@@ -1,33 +1,62 @@
1
- # Getting Started with LittleGhost
1
+ # Getting Started
2
2
 
3
- This guide builds a customer support agent that checks a small help center before answering. It focuses on the Ruby objects needed for one useful run: a tool, an agent, and the call that starts it.
3
+ In this guide, you'll run an agent, connect it to a small help center, and stream its answer. The whole feature stays in ordinary Ruby.
4
4
 
5
- LittleGhost runs inside your Ruby process. It does not choose how your application is hosted or where these definitions live.
5
+ ## Install the gem
6
6
 
7
- ## Install LittleGhost
8
-
9
- LittleGhost requires Ruby 3.3 or newer. Add the gem to your `Gemfile`:
7
+ LittleGhost requires Ruby 3.3 or newer. Add the gem to your `Gemfile`, install it, and set a provider credential:
10
8
 
11
9
  ```ruby
12
10
  gem "little_ghost"
13
11
  ```
14
12
 
15
- Install the bundle and set a provider credential:
16
-
17
13
  ```sh
18
14
  $ bundle install
19
- $ export OPENAI_API_KEY="..."
15
+ $ export OPENROUTER_API_KEY="..."
20
16
  ```
21
17
 
22
- With `OPENAI_API_KEY` present, LittleGhost can connect the `openai` provider name used below. OpenRouter credentials work as well. Use application secret management outside a local shell, and do not commit provider credentials.
18
+ Use your application's secret manager outside a local shell, and never commit provider credentials.
19
+
20
+ This guide uses OpenRouter because one credential is enough to begin. LittleGhost can use other provider connections too; you will configure those in [Running in Production](production.md).
23
21
 
24
- ## Give the agent a tool
22
+ ## See your first answer
25
23
 
26
- Start by requiring LittleGhost and defining a narrow help center lookup:
24
+ Create `customer_support_agent.rb`:
27
25
 
28
26
  ```ruby
29
27
  require "little_ghost"
30
28
 
29
+ class CustomerSupportAgent < LittleGhost::Agent
30
+ model "openrouter:openai/gpt-5.6-luna"
31
+ system_prompt "Answer customer questions clearly and concisely."
32
+ end
33
+
34
+ run = CustomerSupportAgent.ask("Can I change the address on my order?")
35
+
36
+ if run.completed?
37
+ puts run.response
38
+ else
39
+ warn "Support request ended as #{run.outcome}: #{run.error&.class}"
40
+ end
41
+ ```
42
+
43
+ Run the file and you have a working AI feature:
44
+
45
+ ```sh
46
+ $ ruby customer_support_agent.rb
47
+ ```
48
+
49
+ `CustomerSupportAgent.ask` creates a `LittleGhost::Run` for this request. When the work finishes, the Run holds the outcome and response.
50
+
51
+ The inline prompt keeps this first example visible in one place. When the instructions grow, [Prompts as Views](prompt_views.md) moves them into a conventional ERB file without adding setup to the Agent.
52
+
53
+ The selected external provider may receive system instructions, caller input, conversation history, tool results, and attachments. Model wording can vary, so use application code—not a prompt—when a rule must always hold.
54
+
55
+ ## Connect the agent to your application
56
+
57
+ The first agent can answer general questions. A **tool** gives it a focused operation backed by your Ruby code:
58
+
59
+ ```ruby
31
60
  class HelpCenterLookupTool < LittleGhost::Tool
32
61
  HELP_CENTER_ENTRIES = {
33
62
  "refunds" => "Refunds are available within 30 days of purchase.",
@@ -50,119 +79,128 @@ class HelpCenterLookupTool < LittleGhost::Tool
50
79
  end
51
80
  ```
52
81
 
53
- A tool gives the model one application operation with a name, description, and validated JSON input. LittleGhost validates the input before calling `#call` and turns the returned value into model context.
54
-
55
- The schema checks shape, not authorization. A tool that reads customer data or performs an action must enforce the application's trust rules inside its implementation.
56
-
57
- ## Define the agent
58
-
59
- Now describe the agent's behavior and make the lookup available to it:
82
+ Make the tool available to the agent and tell the model when to use it:
60
83
 
61
84
  ```ruby
62
85
  class CustomerSupportAgent < LittleGhost::Agent
63
86
  description "Answers customer support questions."
64
- model "openai:gpt-5.6-luna"
87
+ model "openrouter:openai/gpt-5.6-luna"
65
88
  system_prompt <<~PROMPT
66
89
  Answer clearly and do not invent company guidance.
67
- Use the help center lookup tool before stating company guidance.
90
+ Check the help center before stating company guidance.
68
91
  PROMPT
69
-
70
92
  tools HelpCenterLookupTool
71
93
  end
72
- ```
73
-
74
- An agent class is a reusable behavior definition. It owns its prompt, tools, model selection, limits, and other capabilities. This agent names an OpenAI connection and model directly, so the first example does not need a separate model profile.
75
94
 
76
- ## Ask a question
77
-
78
- Call the agent class to run one request to completion:
79
-
80
- ```ruby
81
95
  run = CustomerSupportAgent.ask(
82
96
  "I bought an item two weeks ago. Can I get a refund?"
83
97
  )
84
98
 
85
- if run.completed?
86
- puts run.response
87
- else
88
- warn "Support request ended as #{run.outcome}: #{run.error&.class}"
89
- end
99
+ run.response
100
+ # One possible response:
101
+ # Refunds are available within 30 days, so your purchase is eligible.
90
102
  ```
91
103
 
92
- `CustomerSupportAgent.ask` creates and consumes a `LittleGhost::Run`. A successful run exposes its final text through `#response`; it also retains the normalized result, outcome, usage, messages, and any terminal error.
104
+ LittleGhost checks the model's arguments before it calls
105
+ `HelpCenterLookupTool#call`. The Tool's result then becomes context for the
106
+ model.
93
107
 
94
- The model can call `HelpCenterLookupTool` with `{"topic":"refunds"}` and answer along these lines:
108
+ ### Use application context for private data
95
109
 
96
- ```text
97
- Refunds are available within 30 days, so a purchase from two weeks ago is eligible.
98
- ```
110
+ The schema checks shape, not permission. When a Tool reads private data or
111
+ changes something, use identity and account information established by your
112
+ application rather than asking the model to supply it.
99
113
 
100
- Model wording and tool selection are not deterministic. The prompt directs the agent to ground company guidance in the validated lookup; applications that must enforce a lookup should put that ordering in a workflow.
114
+ While an Agent is working, LittleGhost binds each Tool instance to the current
115
+ Run. The Tool can read request values through its `run` accessor:
101
116
 
102
- ## Stream the same agent
117
+ ```ruby
118
+ class OrderStatusTool < LittleGhost::Tool
119
+ ORDER_STATUSES = {
120
+ ["user-7", "account-2", "481"] => "out for delivery"
121
+ }.freeze
103
122
 
104
- Use `.stream_ask` when a console, HTTP response, or user interface should receive progress while the run is active:
123
+ description "Look up an order that belongs to the current customer."
124
+ input_schema(
125
+ type: "object",
126
+ properties: {order_number: {type: "string"}},
127
+ required: ["order_number"],
128
+ additionalProperties: false
129
+ )
105
130
 
106
- ```ruby
107
- CustomerSupportAgent.stream_ask("Can I get a refund?").each do |event|
108
- case event.type
109
- when :text_delta
110
- print event.data.fetch(:text)
111
- when :run_error
112
- warn event.data.fetch(:message)
131
+ def call(input)
132
+ lookup = [
133
+ run.invocation.actor_id,
134
+ run.invocation.context.fetch("account_id"),
135
+ input.fetch("order_number")
136
+ ]
137
+
138
+ ORDER_STATUSES.fetch(lookup) do
139
+ raise LittleGhost::ToolError, "Order not found"
140
+ end
113
141
  end
114
142
  end
115
- ```
116
143
 
117
- The stream yields `LittleGhost::StreamEvent` objects. Text, tool activity, usage, traces, and terminal lifecycle facts share this interface, so callers do not need provider-specific response handling.
144
+ class CustomerSupportAgent < LittleGhost::Agent
145
+ tools HelpCenterLookupTool, OrderStatusTool
146
+ end
118
147
 
119
- Both calls use the same agent definition and active LittleGhost configuration. Core Concepts explains reusable runtimes when an application needs more control over shared services.
148
+ run = CustomerSupportAgent.ask(
149
+ "Where is order 481?",
150
+ actor_id: "user-7",
151
+ context: {account_id: "account-2"}
152
+ )
153
+ ```
120
154
 
121
- ## Add a model role when the application grows
155
+ Here, `order_number` came from the model. The application supplied `actor_id`
156
+ and `account_id` after authenticating the caller. LittleGhost places those
157
+ request values on `run.invocation`; context keys become strings.
122
158
 
123
- Direct targets keep a small application's model choice beside its behavior. A larger application can give the same selection a stable role, then change the underlying provider, model, and defaults without editing each agent class:
159
+ > **Safety note:** Treat model-selected Tool arguments like any other external
160
+ > input. Check permission using the current user and account before returning
161
+ > private data or performing a write.
124
162
 
125
- ```ruby
126
- LittleGhost.configure do |config|
127
- config.providers = {
128
- openai: {adapter: :openai, api_key: ENV.fetch("OPENAI_API_KEY")}
129
- }
130
- config.models = {
131
- customer_support: {
132
- target: "openai:gpt-5.6-luna",
133
- settings: {temperature: 0.2}
134
- }
135
- }
136
- config.default_model = :customer_support
137
- end
163
+ That is enough to authorize the first Tool safely. [Core Concepts](core_concepts.md) names the request and working-state objects behind `run`, and [Running in Production](production.md) explains what changes when you add saved conversations.
138
164
 
139
- class CustomerSupportAgent < LittleGhost::Agent
140
- model :customer_support
141
- end
142
- ```
165
+ ## Stream the same agent
143
166
 
144
- An agent may also keep a small amount of model-specific configuration beside its behavior:
167
+ Use `.stream_ask` when a console, HTTP response, or user interface should receive progress as it happens:
145
168
 
146
169
  ```ruby
147
- class DeliberateSupportAgent < LittleGhost::Agent
148
- model(
149
- provider: "openai",
150
- model: "gpt-5.6-luna",
151
- reasoning_effort: "high"
152
- )
170
+ stream = CustomerSupportAgent.stream_ask("Can I get a refund?")
171
+
172
+ run = stream.each do |event|
173
+ case event.type
174
+ when :text_delta
175
+ print event.data.fetch(:text)
176
+ when :run_error
177
+ warn event.data.fetch(:message)
178
+ end
153
179
  end
154
- ```
155
180
 
156
- Here, `provider` names a configured connection and every other key after `model` is a trusted model setting. Provider connections and model profiles may instead come from independent YAML files under `config/little_ghost`, or from paths selected in `LittleGhost.configure`. Inline declarations take precedence over explicit paths, which take precedence over conventional files; environment-based selection and the built-in default are the final fallback. `LittleGhost::Agent` and `LittleGhost::Configuration` document the complete shapes and precedence.
181
+ puts "\n#{run.response}" if run.completed?
182
+ warn run.error.class.name if run.failed?
183
+ ```
157
184
 
158
- ## Fit the agent into your application
185
+ The stream yields `LittleGhost::StreamEvent` values. Text, tool activity, usage, and completion all look the same across providers. When enumeration finishes, `.each` returns the same `LittleGhost::Run` that now holds the final outcome and response.
159
186
 
160
- LittleGhost does not require an application layout. Keep agents and tools beside related application code when your framework or loader already has a home for them. If you want LittleGhost to eager-load definitions, use `app/agents` for agents and `app/tools` for tools.
187
+ ## Give the code a home
161
188
 
162
- The agent in this guide owns one model loop. A larger feature may coordinate several participants while preserving the same `ask` and `stream_ask` entrypoints. LittleGhost calls any such callable unit an **assembly**. Workflows, swarms, and graphs are three kinds of coordinated assembly, and they conventionally live in `app/assemblies`. Their class names should state the coordination style, such as `DevelopmentWorkflow`, `ProblemSolverSwarm`, or `SupportFlowGraph`.
189
+ LittleGhost does not require an application layout. Keep definitions beside related application code, or use these optional conventions:
163
190
 
164
- Classes are the default way to organize reusable definitions. Core Concepts first explains when to choose each assembly type, then introduces builders for definitions discovered at runtime.
191
+ ```text
192
+ app/
193
+ ├── agents/
194
+ │ └── customer_support_agent.rb
195
+ ├── assemblies/
196
+ │ └── response_workflow.rb
197
+ ├── prompts/
198
+ │ └── customer_support/
199
+ │ └── system.erb
200
+ └── tools/
201
+ └── help_center_lookup_tool.rb
202
+ ```
165
203
 
166
- Hosting remains the surrounding application's responsibility. A Rails controller, Rack endpoint, background job, CLI, or another Ruby entrypoint can call the same agent APIs shown above.
204
+ You now have the smallest useful LittleGhost application: one Agent, one Tool, and one familiar Ruby call.
167
205
 
168
- Read [Core Concepts](core_concepts.md) next. It begins with the agent you built here, then adds model selection, runs, assemblies, subagents, workflows, swarms, graphs, builders, sessions, and the boundaries between them. The API reference covers exact signatures for `LittleGhost::Agent`, `LittleGhost::Tool`, `LittleGhost::Run`, and the coordination classes.
206
+ When the feature grows, the calling style stays the same. An **assembly** lets one or more agents work as a unit while keeping `.ask` and `.stream_ask`. Read [Core Concepts](core_concepts.md) next and grow this Agent into a larger system.
@@ -0,0 +1,217 @@
1
+ # Connect MCP, AG-UI, and OpenTelemetry
2
+
3
+ LittleGhost can load Tools from an MCP server, translate a Run stream for an
4
+ interactive interface, and publish traces. Each integration uses the same
5
+ Agents and Runs you already have.
6
+
7
+ ## Load Tools from an MCP server
8
+
9
+ An MCP Toolset connects to one server and turns its published operations into
10
+ LittleGhost Tool classes. Add the Toolset through the same Agent `tools`
11
+ declaration used for local Tools:
12
+
13
+ ```ruby
14
+ require "little_ghost/mcp"
15
+
16
+ class HelpCenterTools < LittleGhost::MCP::Toolset
17
+ connection url: "https://mcp.example/rpc", timeout: 20
18
+ end
19
+
20
+ class CustomerSupportAgent < LittleGhost::Agent
21
+ system_prompt "Use help-center tools for published guidance."
22
+ tools HelpCenterTools
23
+ end
24
+
25
+ run = CustomerSupportAgent.ask("How long do refunds take?")
26
+ run.response
27
+ ```
28
+
29
+ `connection` requires `url` and also accepts `headers`, `timeout`, `signer`,
30
+ `allow_insecure_http`, and `max_response_bytes`. Pass a block when credentials
31
+ depend on the current Agent run:
32
+
33
+ ```ruby
34
+ connection do |binding|
35
+ token = McpAccessTokens.for_actor(binding.run.invocation.actor_id)
36
+ {
37
+ url: "https://mcp.example/rpc",
38
+ headers: {"Authorization" => "Bearer #{token}"},
39
+ timeout: 20
40
+ }
41
+ end
42
+ ```
43
+
44
+ The block's `binding` gives it access to the current Run. LittleGhost evaluates
45
+ the block before opening the MCP session, so each Agent run can use credentials
46
+ for its authenticated caller.
47
+
48
+ By default, the Agent receives every operation published by the server. Their
49
+ normalized server names, such as `search` and `fetch`, become Tool names.
50
+
51
+ Use `map_tool` when the Agent should receive only part of the server catalog or
52
+ when a generated Tool needs a different name or configuration:
53
+
54
+ ```ruby
55
+ class CuratedHelpCenterTools < LittleGhost::MCP::Toolset
56
+ connection url: "https://mcp.example/rpc", timeout: 20
57
+
58
+ map_tool do |tool_class, definition:, binding:|
59
+ next unless %w[search fetch].include?(definition.source_name)
60
+
61
+ tool_class.tool_name "help_center_#{definition.source_name}"
62
+ tool_class
63
+ end
64
+ end
65
+ ```
66
+
67
+ `definition` describes the operation published by the server, and `binding`
68
+ identifies the current Agent run. Return the class after configuring it, or
69
+ return `nil` to omit the operation. Renaming a generated Tool does not change
70
+ the original `Definition#source_name` sent back to the server.
71
+
72
+ The Agent can call the generated Tools like local Tools. LittleGhost uses one
73
+ local client and transport for the Toolset during the Agent run. The built-in
74
+ HTTP transport does not send an MCP session-termination request. Configure
75
+ server-side expiry, or arrange explicit remote cleanup when the server requires
76
+ it.
77
+
78
+ Most MCP results need no mapping. LittleGhost returns `structuredContent` as a
79
+ Ruby Hash when present, otherwise it returns the server's text. Server images
80
+ become Artifacts.
81
+
82
+ Use `map_result` when one operation needs application-specific conversion. This
83
+ example turns the server's download identifier into a deferred Artifact:
84
+
85
+ ```ruby
86
+ map_result do |result, call:, binding:|
87
+ next result unless call.definition.source_name == "export"
88
+
89
+ LittleGhost::Tool::Result.new(
90
+ value: result.structured_content,
91
+ artifacts: [
92
+ LittleGhost::Artifact.deferred(
93
+ reference: result.metadata.fetch("download_id"),
94
+ media_type: "application/octet-stream"
95
+ )
96
+ ]
97
+ )
98
+ end
99
+ ```
100
+
101
+ `map_result` receives the complete `MCP::Result`, the `MCP::Call` that produced
102
+ it, and the current binding. Return any Ruby value or `Tool::Result`. Returning
103
+ the supplied result unchanged keeps the default conversion described above.
104
+ MCP images and local Tool artifacts use the same storage and presentation
105
+ rules when `Configuration#artifacts` is enabled. Images and documents are sent
106
+ as model content; their stored references are fallback information rather than
107
+ a second representation. LittleGhost also checks results against
108
+ server-advertised JSON Schema Draft 2020-12 output schemas.
109
+
110
+ An optional server can fail discovery without preventing Agent construction:
111
+
112
+ ```ruby
113
+ class HelpCenterTools < LittleGhost::MCP::Toolset
114
+ connection { |binding| McpConnections.help_center(binding) }
115
+ optional true
116
+ on_error do |error, binding:|
117
+ McpAvailability.report(error, run_id: binding.run.invocation.run_id)
118
+ end
119
+ end
120
+ ```
121
+
122
+ `optional true` converts expected provider and protocol discovery failures
123
+ into an empty Tool set. `on_error` observes only those caught failures.
124
+ Cancellation, deadlines, configuration errors, and application callback
125
+ failures still propagate.
126
+
127
+ LittleGhost limits the number and total size of discovered operations, the
128
+ complexity of their schemas, and the size and number of returned images.
129
+ `HTTPTransport` also limits each HTTP response and requires HTTPS unless local
130
+ HTTP is explicitly enabled.
131
+
132
+ > **Safety note:** An MCP server supplies descriptions and results that the model
133
+ > can see. Structural validation does not make that content trustworthy or
134
+ > authorize an operation it suggests. Expose only the operations the Agent
135
+ > needs, use narrowly scoped credentials, and have the server authorize every
136
+ > sensitive call. If a result becomes a deferred Artifact, its resolver must
137
+ > verify that the referenced file belongs to the authenticated caller, fetch
138
+ > only from an intended service, and limit the response size before returning
139
+ > bytes to LittleGhost.
140
+
141
+ LittleGhost implements its documented client behavior for the [MCP 2025-06-18
142
+ specification](https://modelcontextprotocol.io/specification/2025-06-18).
143
+
144
+ Use `LittleGhost::MCP::HTTPTransport` and `LittleGhost::MCP::Client` directly
145
+ when you need a custom transport. They produce the same generated Tool classes
146
+ and accept the same mapping callbacks as Toolset.
147
+
148
+ ## Send a Run stream through AG-UI
149
+
150
+ The AG-UI adapter converts LittleGhost events into protocol event hashes:
151
+
152
+ ```ruby
153
+ require "json"
154
+ require "little_ghost/ag_ui"
155
+
156
+ source = CustomerSupportAgent.stream_ask(
157
+ question,
158
+ actor_id: authenticated_user.id,
159
+ context: {account_id: authenticated_user.account_id}
160
+ )
161
+
162
+ events = LittleGhost::AGUI::Adapter.new.stream(
163
+ source,
164
+ thread_id: conversation.id,
165
+ run_id: request.request_id
166
+ )
167
+
168
+ events.each { |event| websocket.write(JSON.generate(event)) }
169
+ ```
170
+
171
+ The adapter translates text, reasoning, Tool activity, usage, retries, trace
172
+ context, subagent activity, and terminal outcomes. It is stateless between
173
+ calls. Your application still owns the connection, thread storage,
174
+ backpressure, and disconnect behavior.
175
+
176
+ LittleGhost also emits namespaced custom events. Consumers should preserve or
177
+ deliberately ignore event types they don't recognize. See the [AG-UI event
178
+ documentation](https://docs.ag-ui.com/concepts/events) when implementing the
179
+ client.
180
+
181
+ > **Safety note:** A Run stream can include model output, Tool arguments and
182
+ > results, errors, and participant activity. Check that the connected user may
183
+ > see the complete Run, then filter fields before sending or storing events.
184
+
185
+ Enumeration drives the source stream on the current thread. When a client
186
+ disconnects, stop enumerating and apply the cancellation behavior your
187
+ application needs. Closing the socket can't undo Tool work that already ran.
188
+
189
+ ## Trace Runs with OpenTelemetry
190
+
191
+ Configure an OpenTelemetry SDK and exporter in the application, then register
192
+ the LittleGhost subscriber before the first Agent call:
193
+
194
+ ```ruby
195
+ LittleGhost.configure do |config|
196
+ config.instrument LittleGhost::Tracing::OpenTelemetry.new
197
+ end
198
+ ```
199
+
200
+ LittleGhost depends on `opentelemetry-api`, leaving the SDK, processor, and
201
+ exporter up to the application. It emits spans and events for Runs, Agents,
202
+ model calls, Tools, assemblies, sessions, usage, and failures. Active operations
203
+ can propagate W3C `traceparent` and `tracestate` fields.
204
+
205
+ Prompts, messages, responses, Tool arguments, and exception content are omitted
206
+ by default. If you intentionally need some of that content, install a
207
+ `LittleGhost::Support::ContentCapture` with a scrubber before enabling capture.
208
+ Avoid putting raw user, order, session, or request IDs in span attributes.
209
+
210
+ Attribute names follow the evolving [OpenTelemetry GenAI semantic
211
+ conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/) where they
212
+ apply. Flush or shut down `LittleGhost::Instrumentation` during application
213
+ shutdown when your backend buffers data.
214
+
215
+ See [Running in Production](production.md) for startup, shutdown, and observability,
216
+ [Tools](tools.md) for local and remote Tool behavior, and [Workspaces and
217
+ Sandboxes](sandboxing.md) for child processes and files.
@@ -0,0 +1,125 @@
1
+ # Choose Models and Providers
2
+
3
+ An Agent needs a model target: a configured provider connection plus the
4
+ provider's model identifier. You can write that target directly while getting
5
+ started, then give it an application-facing name when several Agents share it.
6
+
7
+ ## Start with one direct target
8
+
9
+ A direct target has the form `connection:model-id`:
10
+
11
+ ```ruby
12
+ class CustomerSupportAgent < LittleGhost::Agent
13
+ model "openrouter:openai/gpt-5.6-luna"
14
+ system_prompt "Answer customer questions clearly and concisely."
15
+ end
16
+ ```
17
+
18
+ `openrouter` names a connection configured by the application. The remainder
19
+ is the model identifier understood by that provider. This is a good fit when
20
+ one Agent owns one stable choice.
21
+
22
+ ## Give shared choices a role
23
+
24
+ A model role lets several Agents share a choice without knowing its provider
25
+ or model identifier:
26
+
27
+ ```ruby
28
+ LittleGhost.configure do |config|
29
+ config.providers = {
30
+ primary: {
31
+ adapter: :openrouter,
32
+ api_key: ENV.fetch("OPENROUTER_API_KEY")
33
+ }
34
+ }
35
+ config.models = {
36
+ customer_support: {
37
+ target: "primary:openai/gpt-5.6-luna",
38
+ settings: {temperature: 0.2}
39
+ }
40
+ }
41
+ config.default_model = :customer_support
42
+ end
43
+
44
+ class CustomerSupportAgent < LittleGhost::Agent
45
+ model :customer_support
46
+ end
47
+ ```
48
+
49
+ Here `customer_support` is the role, `primary` is the connection, and
50
+ `openrouter` is the adapter. You can move the role to another model or provider
51
+ without editing the Agent.
52
+
53
+ Profile settings are defaults. An individual call can override them:
54
+
55
+ ```ruby
56
+ run = CustomerSupportAgent.ask(
57
+ "Explain the refund decision.",
58
+ settings: {temperature: 0.0}
59
+ )
60
+ ```
61
+
62
+ Build these settings in application code instead of passing request parameters
63
+ through unchanged. Settings can affect cost, latency, and model behavior.
64
+
65
+ ## Configure connections in one place
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`.
70
+
71
+ Keep credentials in your application's secret manager. Agents refer to a role
72
+ or configured connection; they don't need to contain credentials. If your
73
+ application obtains short-lived credentials at runtime, configure a credential
74
+ resolver that returns them for the selected connection.
75
+
76
+ > **Safety note:** The selected provider may receive system instructions,
77
+ > caller input, conversation history, Tool results, schemas, and attachments.
78
+ > Choose a provider that is appropriate for that data, and keep credentials and
79
+ > provider endpoints under application control.
80
+
81
+ ## Choose a role for each request
82
+
83
+ An Agent can select between configured roles using its `Invocation`:
84
+
85
+ ```ruby
86
+ class CustomerSupportAgent < LittleGhost::Agent
87
+ model do |invocation|
88
+ invocation.fetch(:premium_account, false) ? :premium_support : :customer_support
89
+ end
90
+ end
91
+ ```
92
+
93
+ Set `premium_account` from application state when creating the invocation. If
94
+ a public request offers a model choice, map that choice to one of your
95
+ configured roles rather than accepting an arbitrary provider target.
96
+
97
+ Trusted application code may also declare a selection inline:
98
+
99
+ ```ruby
100
+ class ResearchAgent < LittleGhost::Agent
101
+ model(
102
+ provider: "primary",
103
+ model: "openai/gpt-5.6-luna",
104
+ reasoning_effort: "high"
105
+ )
106
+ end
107
+ ```
108
+
109
+ `provider` still names a configured connection. The inline settings change the
110
+ selection; they don't create a new connection.
111
+
112
+ ## Use model capabilities
113
+
114
+ `LittleGhost::ModelResolver` turns a role or target into an executable
115
+ `LittleGhost::Model`. Its catalog describes capabilities such as supported
116
+ input types, output limits, and structured results. LittleGhost uses that
117
+ information to reject unsupported attachments, constrain output limits, and
118
+ choose a structured-result strategy.
119
+
120
+ Provider capabilities can change. Handle failed Runs and provider errors even
121
+ when the catalog says a feature is supported.
122
+
123
+ Continue with [Prompts as Views](prompt_views.md) when an Agent's instructions
124
+ outgrow one string. See [Structured Results and Content](structured_outputs_and_content.md)
125
+ when you need checked result shapes, images, or documents.