little_ghost 0.1.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 (82) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +122 -0
  4. data/docs/guides/Core Concepts.md +203 -0
  5. data/docs/guides/Getting Started.md +187 -0
  6. data/lib/little_ghost/ag_ui/adapter.rb +194 -0
  7. data/lib/little_ghost/ag_ui.rb +5 -0
  8. data/lib/little_ghost/agent/context_management.rb +285 -0
  9. data/lib/little_ghost/agent/delegation.rb +128 -0
  10. data/lib/little_ghost/agent/skills.rb +96 -0
  11. data/lib/little_ghost/agent/tool_loop.rb +239 -0
  12. data/lib/little_ghost/agent.rb +2111 -0
  13. data/lib/little_ghost/agent_builder.rb +191 -0
  14. data/lib/little_ghost/agent_interruptions.rb +197 -0
  15. data/lib/little_ghost/configuration.rb +337 -0
  16. data/lib/little_ghost/content.rb +324 -0
  17. data/lib/little_ghost/default_model_registry.rb +71 -0
  18. data/lib/little_ghost/errors.rb +48 -0
  19. data/lib/little_ghost/events.rb +264 -0
  20. data/lib/little_ghost/execution_state.rb +58 -0
  21. data/lib/little_ghost/instrumentation.rb +475 -0
  22. data/lib/little_ghost/invocation.rb +285 -0
  23. data/lib/little_ghost/lookup.rb +37 -0
  24. data/lib/little_ghost/mcp/client.rb +396 -0
  25. data/lib/little_ghost/mcp.rb +5 -0
  26. data/lib/little_ghost/message.rb +75 -0
  27. data/lib/little_ghost/model.rb +88 -0
  28. data/lib/little_ghost/model_capabilities.rb +126 -0
  29. data/lib/little_ghost/model_registry.rb +173 -0
  30. data/lib/little_ghost/model_request.rb +107 -0
  31. data/lib/little_ghost/model_response.rb +48 -0
  32. data/lib/little_ghost/path_set.rb +32 -0
  33. data/lib/little_ghost/prompt_resolver.rb +251 -0
  34. data/lib/little_ghost/providers/bedrock.rb +506 -0
  35. data/lib/little_ghost/providers/http_transport.rb +149 -0
  36. data/lib/little_ghost/providers/open_router.rb +171 -0
  37. data/lib/little_ghost/providers/openai.rb +27 -0
  38. data/lib/little_ghost/providers/openai_compatible.rb +745 -0
  39. data/lib/little_ghost/providers/sse_parser.rb +35 -0
  40. data/lib/little_ghost/run.rb +607 -0
  41. data/lib/little_ghost/run_context.rb +129 -0
  42. data/lib/little_ghost/run_result.rb +111 -0
  43. data/lib/little_ghost/runtime/hook.rb +31 -0
  44. data/lib/little_ghost/runtime.rb +392 -0
  45. data/lib/little_ghost/sandbox.rb +138 -0
  46. data/lib/little_ghost/session.rb +229 -0
  47. data/lib/little_ghost/session_store.rb +96 -0
  48. data/lib/little_ghost/session_stores/agent_core_memory.rb +1086 -0
  49. data/lib/little_ghost/session_stores/memory.rb +86 -0
  50. data/lib/little_ghost/skills/catalog.rb +283 -0
  51. data/lib/little_ghost/skills/skill.rb +60 -0
  52. data/lib/little_ghost/skills.rb +4 -0
  53. data/lib/little_ghost/stream_event.rb +49 -0
  54. data/lib/little_ghost/structured_output.rb +126 -0
  55. data/lib/little_ghost/subagents/agent_path.rb +63 -0
  56. data/lib/little_ghost/subagents/definition.rb +42 -0
  57. data/lib/little_ghost/subagents/manager.rb +1615 -0
  58. data/lib/little_ghost/support/callbacks.rb +151 -0
  59. data/lib/little_ghost/support/cancellation_token.rb +86 -0
  60. data/lib/little_ghost/support/class_attributes.rb +40 -0
  61. data/lib/little_ghost/support/content_capture.rb +150 -0
  62. data/lib/little_ghost/support/executor.rb +75 -0
  63. data/lib/little_ghost/support/interruptible_stream.rb +103 -0
  64. data/lib/little_ghost/support/loader.rb +263 -0
  65. data/lib/little_ghost/support/output_truncation.rb +71 -0
  66. data/lib/little_ghost/support/redactor.rb +66 -0
  67. data/lib/little_ghost/support.rb +34 -0
  68. data/lib/little_ghost/tool.rb +448 -0
  69. data/lib/little_ghost/tool_execution.rb +59 -0
  70. data/lib/little_ghost/tool_registry.rb +156 -0
  71. data/lib/little_ghost/tools/filesystem.rb +119 -0
  72. data/lib/little_ghost/tools/shell.rb +45 -0
  73. data/lib/little_ghost/tools/write_todos.rb +91 -0
  74. data/lib/little_ghost/tools.rb +6 -0
  75. data/lib/little_ghost/tracing/open_telemetry.rb +517 -0
  76. data/lib/little_ghost/unrestricted_sandbox.rb +306 -0
  77. data/lib/little_ghost/usage.rb +47 -0
  78. data/lib/little_ghost/version.rb +6 -0
  79. data/lib/little_ghost/workflow.rb +351 -0
  80. data/lib/little_ghost/workspace.rb +31 -0
  81. data/lib/little_ghost.rb +120 -0
  82. metadata +225 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 16d2003f8e5b79a2532ab16b9a0a1d95a9ee65b21025371290aeccacacd0f8a1
4
+ data.tar.gz: 0c4db0710a8650ebb1ae159bc59a62078c442a035b64c3f549cbfb686c881c32
5
+ SHA512:
6
+ metadata.gz: c435a0f6ef9b9d4508d66f72610b32b58f45ed724af5d8b9fbf8cc20fc27eaa8a41dcffc1c4fe0e109fbfc3603dffdf393f0bc38444a554009b41d8302b74f1a
7
+ data.tar.gz: 1945188f5019cd2b115f74df481a344124d793eaa74ace0e6efc57b84d9bc117ee20879a2764818c78918e1dcd0609f3c1230e27b9a6b2885b4db4288a9e8b6b
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matt Robinson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Build AI features with LittleGhost
2
+
3
+ <hr>
4
+
5
+ **Growing in public.** LittleGhost is under active development, and interfaces may evolve between releases. Pin the gem version and review the release notes when upgrading.
6
+
7
+ <hr>
8
+
9
+ Bring agents and agentic workflows into an existing Ruby system, or use them as the core of a dedicated AI service. LittleGhost brings model providers, tools, streaming, sessions, delegation, deterministic composition, and observability together behind one coherent set of Ruby APIs.
10
+
11
+ Here is the shape of a small customer support agent. `CustomerSupportModels` keeps provider details out of agent behavior, `HelpCenterLookupTool` exposes one narrow, validated help center lookup, and `CustomerSupportAgent` brings them together:
12
+
13
+ ```ruby
14
+ require "little_ghost"
15
+
16
+ class CustomerSupportModels < LittleGhost::ModelRegistry
17
+ def initialize
18
+ super
19
+ provider(:openai) do |model:, **|
20
+ LittleGhost::Providers::OpenAI.new(
21
+ api_key: ENV.fetch("OPENAI_API_KEY"),
22
+ model:
23
+ )
24
+ end
25
+ profile "customer_support", provider: :openai, model: "gpt-5"
26
+ end
27
+ end
28
+
29
+ class HelpCenterLookupTool < LittleGhost::Tool
30
+ description "Look up a help center entry by topic."
31
+ input_schema(
32
+ type: "object",
33
+ properties: {topic: {type: "string"}},
34
+ required: ["topic"],
35
+ additionalProperties: false
36
+ )
37
+
38
+ def call(input)
39
+ {"refunds" => "Refunds are available within 30 days."}
40
+ .fetch(input.fetch("topic"), "No help center entry found.")
41
+ end
42
+ end
43
+
44
+ class CustomerSupportAgent < LittleGhost::Agent
45
+ description "Answers customer support questions."
46
+ model "customer_support"
47
+ system_prompt "Answer clearly. Check the help center before stating company guidance."
48
+ tools HelpCenterLookupTool
49
+ end
50
+
51
+ LittleGhost.configure { |config| config.models CustomerSupportModels }
52
+
53
+ run = CustomerSupportAgent.ask("Can I get a refund after two weeks?")
54
+ puts run.response
55
+ # One possible response:
56
+ # Refunds are available within 30 days, so your purchase is eligible.
57
+ ```
58
+
59
+ The result is a normal Ruby object. You can inspect its outcome, final response, normalized messages, token usage, and error instead of parsing provider-specific payloads.
60
+
61
+ LittleGhost keeps the model loop provider-neutral. Tools can run in parallel, mutating tools can opt into a run-wide exclusive lock, and oversized tool results are truncated to the configured limit before they enter model context. Strict result schemas, cancellation, deadlines, session checkpoints, structured events, and OpenTelemetry integration are available when an application needs them; none are required to define the first agent.
62
+
63
+ The core stays dependency-light, while provider SDKs and deployment choices remain with the application.
64
+
65
+ ## How it fits together
66
+
67
+ ```text
68
+ application configuration ──> CustomerSupportModels ──> provider
69
+
70
+ request ──> CustomerSupportAgent ──> HelpCenterLookupTool
71
+
72
+ └────────> ResearchAgent (when delegated)
73
+
74
+ request ──> ResponseWorkflow ──> ResearchAgent ──> CustomerSupportAgent
75
+ ```
76
+
77
+ Configuration owns shared services such as model registries, sessions, lookup paths, workspaces, sandboxes, and instrumentation. Agent classes own behavior: their logical model role, prompt, tools, limits, structured result, and delegation policy. A tool is a validated boundary around application code. A subagent lets the model delegate work within configured turn, concurrency, and depth limits; a workflow uses ordinary Ruby when your application must choose the sequence.
78
+
79
+ `CustomerSupportAgent.ask(...)` creates a standalone entrypoint, consumes its run, and returns the completed `LittleGhost::Run`. Create an instance explicitly when reusing a runtime or when an interface should render progress as `LittleGhost::StreamEvent` objects:
80
+
81
+ `LittleGhost::Agent.ask("hi")` uses the built-in default model selection and the system prompt `You are a helpful agent.` for a general-purpose agent.
82
+
83
+ ```ruby
84
+ CustomerSupportAgent.new.stream_ask("Can I get a refund?").each do |event|
85
+ print event.data[:text] if event.type == :text_delta
86
+ end
87
+ ```
88
+
89
+ Applications can add a `ResearchAgent` as a subagent for open-ended investigation, or place both agents behind a deterministic `ResponseWorkflow`. The same run lifecycle checkpoints session state, closes owned resources, aggregates usage, and emits framework events in each form.
90
+
91
+ ## Installation
92
+
93
+ LittleGhost requires Ruby 3.3 or newer. Add it to your bundle:
94
+
95
+ ```ruby
96
+ gem "little_ghost"
97
+ ```
98
+
99
+ Then run `bundle install`. Provider SDKs and OpenTelemetry exporters are optional application dependencies. The built-in OpenAI, OpenAI-compatible, OpenRouter, and Amazon Bedrock integrations normalize their responses into the same LittleGhost protocol.
100
+
101
+ By default, LittleGhost maps the `default` role to GPT-5.6 Terra. It selects the first nonblank API key from `LITTLEGHOST_OPENROUTER_API_KEY`, `LITTLEGHOST_OPENAI_API_KEY`, `OPENROUTER_API_KEY`, and `OPENAI_API_KEY`, in that order. Setting one of these keys authorizes model inputs—including prompts, conversation history, tool data, and attachments—to be sent to the selected external provider. Applications with provider or data-residency requirements should configure a model registry explicitly.
102
+
103
+ By default, structured framework events have no console destination. Hosted applications can emit redacted JSON lines to standard output with `LittleGhost.configure { |config| config.log_events_to :stdout }`; `:stderr` selects standard error instead, and `nil` disables a configured destination. Event emission and severity levels are the same with or without a console destination.
104
+
105
+ Prompts can stay inline while an agent is small, then move into conventional ERB templates under `app/prompts`. Agents and tools under `app/agents` and `app/tools` are loaded from the configured application root, keeping framework setup separate from product behavior.
106
+
107
+ ## Documentation
108
+
109
+ - [LittleGhost website](https://mattyr.github.io/little_ghost/) is the quickest way to meet the framework and find your next step.
110
+ - [Getting Started](docs/guides/Getting%20Started.md) builds the customer support example from an empty application and runs it.
111
+ - [Core Concepts](docs/guides/Core%20Concepts.md) explains models, agents, tools, delegation, workflows, sessions, and streaming through the same example.
112
+ - [API reference](rdoc-ref:LittleGhost) covers exact signatures, options, and lifecycle details.
113
+
114
+ LittleGhost is licensed under the MIT License.
115
+
116
+ ## Development
117
+
118
+ ```sh
119
+ bundle install
120
+ bundle exec rake test
121
+ bundle exec standardrb --no-fix
122
+ ```
@@ -0,0 +1,203 @@
1
+ # Core Concepts
2
+
3
+ LittleGhost gives Ruby software two ways to compose AI behavior. Agents can choose among validated tools and delegated specialists, while agentic workflows keep required ordering and branching under application control. The customer support example makes that boundary visible: CustomerSupportModels chooses provider-backed models, CustomerSupportAgent owns behavior, HelpCenterLookupTool exposes a narrow help center lookup, ResearchAgent handles delegated investigation, and ResponseWorkflow imposes a deterministic sequence when the surrounding system requires one.
4
+
5
+ ```text
6
+ shared configuration
7
+ └── CustomerSupportModels ── resolves logical roles ──> provider clients
8
+
9
+ one request
10
+ └── Run
11
+ ├── CustomerSupportAgent
12
+ │ ├── HelpCenterLookupTool
13
+ │ └── ResearchAgent subagent (model-directed)
14
+ └── sessions, resources, usage, events, and terminal result
15
+
16
+ one deterministic request
17
+ └── Run ──> ResponseWorkflow ──> ResearchAgent ──> CustomerSupportAgent
18
+ ```
19
+
20
+ ## Models are selected by role
21
+
22
+ An agent names a logical role such as `customer_support`, not a vendor model. `CustomerSupportModels` maps that stable application vocabulary to a provider and model identifier:
23
+
24
+ ```ruby
25
+ class CustomerSupportModels < LittleGhost::ModelRegistry
26
+ def initialize
27
+ super
28
+ provider(:openai) do |model:, **|
29
+ LittleGhost::Providers::OpenAI.new(
30
+ api_key: ENV.fetch("OPENAI_API_KEY"),
31
+ model:
32
+ )
33
+ end
34
+ profile "customer_support", provider: :openai, model: "gpt-5"
35
+ end
36
+ end
37
+ ```
38
+
39
+ Dotted roles inherit from the nearest registered parent. `ResearchAgent` can request `customer_support.research` and initially use the `customer_support` profile; registering `customer_support.research` later specializes it. Per-invocation profile overrides can vary a request without mutating the registry or agent class. Because an override can select a different registered provider, model, and settings, it is trusted application configuration and must be constructed or allowlisted by the application rather than copied from unchecked request data.
40
+
41
+ The provider performs model I/O. The registry resolves application intent into a `LittleGhost::Model`, which carries the provider, settings, metadata, model identifier, and role for a run.
42
+
43
+ ## Agents declare behavior
44
+
45
+ An agent class declares application behavior:
46
+
47
+ ```ruby
48
+ class CustomerSupportAgent < LittleGhost::Agent
49
+ description "Answers customer support questions."
50
+ model "customer_support"
51
+ system_prompt "Answer clearly. Check the help center before stating company guidance."
52
+ tools HelpCenterLookupTool
53
+ subagent ResearchAgent, kind: "research"
54
+ end
55
+ ```
56
+
57
+ The class-level DSL is inheritable. It can declare prompts, limits, callbacks, tool classes, structured results, context management, skills, and delegation. A capability mixin may be included in `LittleGhost::Agent`, but its behavior remains inactive until the corresponding DSL is called.
58
+
59
+ `CustomerSupportAgent.ask` creates a standalone, console-friendly entrypoint, builds and consumes a `LittleGhost::Run`, and returns that run. Create `CustomerSupportAgent.new` explicitly to reuse a runtime or call `#stream_ask` for the run's events. Agents built by a runtime are instead scoped to their owning run and return a `LittleGhost::RunResult` from `#call`.
60
+
61
+ That distinction explains two useful return paths:
62
+
63
+ ```ruby
64
+ run = CustomerSupportAgent.ask("Can I get a refund?")
65
+ run.response # final text from the top-level execution
66
+ run.result.output # text, or a validated structured value when declared
67
+ ```
68
+
69
+ ## Runs own top-level lifecycle
70
+
71
+ A `LittleGhost::Run` owns one top-level agent or workflow execution. It opens the session, workspace, sandbox, entrypoint, and registered resources, then closes owned resources in reverse order.
72
+
73
+ The run is both executable and enumerable. `#call` consumes it; `#each` streams `LittleGhost::StreamEvent` objects. After termination, the run reports one outcome: completed, failed, partial at a deadline, or cancelled. It also exposes the final response, result, usage, and error.
74
+
75
+ An `Invocation` is the request envelope. It normalizes the current message and history, generates missing identifiers, and retains application-specific fields with indifferent string and symbol keys. Caller identity remains explicit. If session persistence needs tenant isolation, derive its actor from trusted authentication state; never trust a model-supplied or unverified request field.
76
+
77
+ ## Tools are validated application boundaries
78
+
79
+ `HelpCenterLookupTool` exposes exactly one operation to the model:
80
+
81
+ ```ruby
82
+ class HelpCenterLookupTool < LittleGhost::Tool
83
+ description "Look up a help center entry by topic."
84
+ input_schema(
85
+ type: "object",
86
+ properties: {topic: {type: "string"}},
87
+ required: ["topic"],
88
+ additionalProperties: false
89
+ )
90
+
91
+ def call(input)
92
+ HelpCenterRepository.fetch(input.fetch("topic"))
93
+ end
94
+ end
95
+ ```
96
+
97
+ LittleGhost validates the model's input before invoking `#call`. Hashes and arrays returned by a tool are JSON-encoded; other values become text. Expected application failures can raise `LittleGhost::ToolError`; unexpected exception messages are sanitized before they reach model context.
98
+
99
+ Validation is not authorization. A tool that reads customer records, writes files, executes processes, or calls a network service must enforce the application's trust rules itself. The built-in unrestricted sandbox executes with the Ruby process's permissions and is not a security boundary. Configure an isolated sandbox before exposing filesystem or shell tools to untrusted work.
100
+
101
+ ## Subagents are model-directed delegation
102
+
103
+ Declaring `ResearchAgent` as a subagent gives `CustomerSupportAgent` a configured set of tools for spawning, messaging, interrupting, waiting for, and listing research work:
104
+
105
+ ```ruby
106
+ class ResearchAgent < LittleGhost::Agent
107
+ description "Investigates support questions that need broader research."
108
+ model "customer_support.research"
109
+ system_prompt "Return a concise evidence summary."
110
+ end
111
+
112
+ class CustomerSupportAgent < LittleGhost::Agent
113
+ model "customer_support"
114
+ tools HelpCenterLookupTool
115
+ subagent ResearchAgent, kind: "research"
116
+ end
117
+ ```
118
+
119
+ The model decides whether to delegate and how to use the returned research. Each child declares its own tools, so access remains visible at the class receiving it. Subagent work can run concurrently and respects the configured turn, concurrency, depth, and time limits. Conversations can persist when a session store exists; `persist: false` keeps a declaration invocation-local.
120
+
121
+ Use an agent as an ordinary tool with `agent_as_tool` when one request and one result is enough. Use a subagent when the parent needs an addressable worker with follow-ups, progress, interruption, or durable conversation identity.
122
+
123
+ ## Workflows are application-directed composition
124
+
125
+ Some customer support requests must always be researched before a response is written. Put that invariant in Ruby rather than asking the model to remember it:
126
+
127
+ ```ruby
128
+ class ResponseWorkflow < LittleGhost::Workflow
129
+ private
130
+
131
+ def perform
132
+ research = invoke(ResearchAgent).output
133
+
134
+ invoke CustomerSupportAgent, input: <<~PROMPT
135
+ Customer request:
136
+ #{input.text}
137
+
138
+ Research:
139
+ #{research}
140
+ PROMPT
141
+ end
142
+ end
143
+ ```
144
+
145
+ `#invoke` builds a lazy agent invocation. Calling `#output` consumes an intermediate invocation; `#perform` must return its final invocation unconsumed so LittleGhost can stream that agent to the original caller. Input, history, state, settings, cancellation, deadline, template values, and trace parentage flow through the workflow, while intermediate usage is added to the terminal result.
146
+
147
+ A workflow is an explicit entrypoint on a run:
148
+
149
+ ```ruby
150
+ runtime = LittleGhost::Runtime.new(configuration: LittleGhost.configuration)
151
+ run = runtime.build_run(
152
+ {message: "Review this unusual refund request"},
153
+ agent_class: CustomerSupportAgent,
154
+ entrypoint_class: ResponseWorkflow
155
+ ).call
156
+
157
+ puts run.response
158
+ ```
159
+
160
+ Choose a subagent when delegation is part of the model's judgment. Choose a workflow when ordering and branching are application invariants. They can coexist: `ResponseWorkflow` can always collect baseline research, while `CustomerSupportAgent` can still delegate a new question that arises while drafting the response.
161
+
162
+ ## Structured results separate data from prose
163
+
164
+ An agent that feeds application code can declare a strict JSON object schema:
165
+
166
+ ```ruby
167
+ class ResearchAgent < LittleGhost::Agent
168
+ model "customer_support.research"
169
+ result_schema(
170
+ {
171
+ type: "object",
172
+ properties: {
173
+ summary: {type: "string"},
174
+ sources: {type: "array", items: {type: "string"}}
175
+ },
176
+ required: %w[summary sources],
177
+ additionalProperties: false
178
+ },
179
+ name: "support_research"
180
+ )
181
+ end
182
+ ```
183
+
184
+ LittleGhost selects provider-native structured output when the resolved model advertises it, or a strict terminal tool when supported. The locally validated value is available through `RunResult#structured_result` and `RunResult#output`. Invalid output receives one repair attempt, then raises `LittleGhost::StructuredResultError`.
185
+
186
+ Use structured results when code consumes fields. Keep ordinary text when a human is the final consumer.
187
+
188
+ ## Sessions preserve conversation, streams expose progress
189
+
190
+ The default session store is in-memory. A configured `SessionStore` can load history and state before an agent runs and checkpoint coherent turns as work progresses. The application must supply stable session and actor identifiers when it wants continuity and isolation.
191
+
192
+ Streams expose generic framework events rather than provider wire formats. Consumers can render text deltas, observe tool or subagent activity, collect usage, and react to terminal outcomes without coupling to OpenAI, OpenRouter, or Bedrock. The optional AG-UI adapter translates the same events at an interface boundary.
193
+
194
+ ## Keep the boundary visible
195
+
196
+ The core design can be summarized as four choices:
197
+
198
+ - Put shared construction and provider policy in configuration and model registries.
199
+ - Put model behavior and available capabilities on agent classes.
200
+ - Put privileged application operations behind narrow, authorized tools.
201
+ - Put mandatory ordering in workflows; leave optional delegation to subagents.
202
+
203
+ Return to [Getting Started](Getting%20Started.md) for the complete first-run setup. The API reference covers exact signatures and lifecycle details for `LittleGhost::Runtime`, `LittleGhost::Run`, `LittleGhost::Agent`, `LittleGhost::Tool`, `LittleGhost::Workflow`, and `LittleGhost::ModelRegistry`.
@@ -0,0 +1,187 @@
1
+ # Getting Started with LittleGhost
2
+
3
+ This guide builds a small AI customer support feature around an agent that checks an in-memory help center and returns a normal Ruby result. By the end, you will have configured a model provider, declared a validated tool, run `CustomerSupportAgent`, and streamed the same request as events.
4
+
5
+ The example uses OpenAI, but the agent and tool do not depend on that choice. A `ModelRegistry` keeps the provider boundary behind the logical model role `customer_support`.
6
+
7
+ ## Before you begin
8
+
9
+ LittleGhost requires Ruby 3.3 or newer. Add the gem to your `Gemfile`:
10
+
11
+ ```ruby
12
+ gem "little_ghost"
13
+ ```
14
+
15
+ Install the bundle and provide the credential used by this example:
16
+
17
+ ```sh
18
+ $ bundle install
19
+ $ export OPENAI_API_KEY="..."
20
+ ```
21
+
22
+ Use application-specific secret management outside a local shell. Do not commit provider credentials.
23
+
24
+ ## Create the application shape
25
+
26
+ LittleGhost looks for agents, tools, prompts, and skills under conventional application directories. This example needs four files:
27
+
28
+ ```text
29
+ customer_support_app/
30
+ ├── app/
31
+ │ ├── agents/
32
+ │ │ ├── research_agent.rb
33
+ │ │ └── customer_support_agent.rb
34
+ │ └── tools/
35
+ │ └── help_center_lookup_tool.rb
36
+ └── config/
37
+ └── little_ghost.rb
38
+ ```
39
+
40
+ Run the application from `customer_support_app/`, or set the configuration root explicitly before the first runtime is built. The runtime loads `config/little_ghost.rb` lazily and eager-loads conventional application code.
41
+
42
+ ## Configure a logical model role
43
+
44
+ Define `CustomerSupportModels` in `config/little_ghost.rb` and register it with LittleGhost:
45
+
46
+ ```ruby
47
+ require "little_ghost"
48
+
49
+ class CustomerSupportModels < LittleGhost::ModelRegistry
50
+ def initialize
51
+ super
52
+
53
+ provider(:openai) do |model:, **|
54
+ LittleGhost::Providers::OpenAI.new(
55
+ api_key: ENV.fetch("OPENAI_API_KEY"),
56
+ model:
57
+ )
58
+ end
59
+
60
+ profile "customer_support",
61
+ provider: :openai,
62
+ model: "gpt-5",
63
+ settings: {temperature: 0.2}
64
+ end
65
+ end
66
+
67
+ LittleGhost.configure do |config|
68
+ config.models CustomerSupportModels
69
+ end
70
+ ```
71
+
72
+ The agent will ask for the role `customer_support`; it never needs the provider name or provider model identifier. This separation lets an application change providers or override a profile for one invocation without changing agent classes. Treat model settings and profile overrides as trusted application configuration: construct or allowlist them server-side instead of copying unchecked request fields.
73
+
74
+ ## Give the agent one validated tool
75
+
76
+ Create `app/tools/help_center_lookup_tool.rb`:
77
+
78
+ ```ruby
79
+ class HelpCenterLookupTool < LittleGhost::Tool
80
+ HELP_CENTER_ENTRIES = {
81
+ "refunds" => "Refunds are available within 30 days of purchase.",
82
+ "shipping" => "Standard shipping takes three to five business days."
83
+ }.freeze
84
+
85
+ description "Look up a help center entry by topic."
86
+ input_schema(
87
+ type: "object",
88
+ properties: {
89
+ topic: {type: "string", enum: HELP_CENTER_ENTRIES.keys}
90
+ },
91
+ required: ["topic"],
92
+ additionalProperties: false
93
+ )
94
+
95
+ def call(input)
96
+ HELP_CENTER_ENTRIES.fetch(input.fetch("topic"))
97
+ end
98
+ end
99
+ ```
100
+
101
+ A tool exposes a name, description, JSON input schema, and implementation. `HelpCenterLookupTool` gets its default model-visible name from the class name. LittleGhost validates input before calling `#call` and turns the returned value into text for model context.
102
+
103
+ This boundary is also the right place for application authorization. If lookup behavior or results depended on an account or tenant, `#call` would verify trusted run context before reading them. A schema validates shape; it does not authorize access.
104
+
105
+ ## Define the agents
106
+
107
+ Create `app/agents/research_agent.rb`:
108
+
109
+ ```ruby
110
+ class ResearchAgent < LittleGhost::Agent
111
+ description "Investigates support questions that need broader research."
112
+ model "customer_support.research"
113
+ system_prompt "Research the question and return a concise evidence summary."
114
+ end
115
+ ```
116
+
117
+ There is no separate `customer_support.research` profile yet. Dotted roles fall back to the nearest registered parent, so this role resolves through `customer_support`. You can add a specialized profile later without changing `ResearchAgent`.
118
+
119
+ Now create `app/agents/customer_support_agent.rb`:
120
+
121
+ ```ruby
122
+ class CustomerSupportAgent < LittleGhost::Agent
123
+ description "Answers customer support questions."
124
+ model "customer_support"
125
+ system_prompt <<~PROMPT
126
+ Answer clearly and do not invent company guidance.
127
+ Use the help center lookup tool before stating company guidance.
128
+ Delegate open-ended investigation to the research agent.
129
+ PROMPT
130
+
131
+ tools HelpCenterLookupTool
132
+ subagent ResearchAgent, kind: "research"
133
+ end
134
+ ```
135
+
136
+ The class is a readable behavior boundary. It declares which model role, prompt, tools, and subagent capabilities can participate in a `CustomerSupportAgent` run. Declaring `ResearchAgent` as a subagent gives the model tools for delegation, with the turn, concurrency, and depth limits configured by the surrounding system; it does not force every request through research.
137
+
138
+ ## Run the agent
139
+
140
+ From the application root, start a Ruby process that requires LittleGhost and asks the agent a question:
141
+
142
+ ```ruby
143
+ require "little_ghost"
144
+
145
+ runtime = LittleGhost::Runtime.new(configuration: LittleGhost.configuration)
146
+ customer_support_agent = CustomerSupportAgent.new(runtime:)
147
+ run = customer_support_agent.ask(
148
+ "I bought an item two weeks ago. Can I get a refund?"
149
+ )
150
+
151
+ if run.completed?
152
+ puts run.response
153
+ else
154
+ warn "Support request ended as #{run.outcome}: #{run.error&.class}"
155
+ end
156
+ ```
157
+
158
+ `#ask` consumes the invocation and returns its owning `LittleGhost::Run`. A successful run exposes the final text through `#response` and the normalized terminal value through `#result`. It also retains outcome, usage, messages, and any terminal error.
159
+
160
+ The model can call `HelpCenterLookupTool` with `{"topic":"refunds"}` and answer along these lines:
161
+
162
+ ```text
163
+ Refunds are available within 30 days, so a purchase from two weeks ago is eligible.
164
+ ```
165
+
166
+ Model wording is not deterministic. The important outcome is that the refund guidance comes from the validated tool result rather than from an unverified guess.
167
+
168
+ ## Stream the response
169
+
170
+ Use `#stream_ask` when a console, web response, or UI should receive progress before the run completes:
171
+
172
+ ```ruby
173
+ CustomerSupportAgent.new(runtime:).stream_ask("Can I get a refund?").each do |event|
174
+ case event.type
175
+ when :text_delta
176
+ print event.data.fetch(:text)
177
+ when :run_error
178
+ warn event.data.fetch(:message)
179
+ end
180
+ end
181
+ ```
182
+
183
+ The enumerator yields `LittleGhost::StreamEvent` objects. Text, tool activity, usage, traces, subagent activity, and terminal lifecycle facts share that interface, so adapters can translate one framework stream into a console, HTTP stream, or AG-UI transport.
184
+
185
+ ## Where to go next
186
+
187
+ Read [Core Concepts](Core%20Concepts.md) for the distinction between model-directed subagents and application-directed workflows, how sessions and runs divide ownership, and when `ResponseWorkflow` is a better fit than delegation. The API reference covers exact signatures for `LittleGhost::Agent`, `LittleGhost::Tool`, `LittleGhost::ModelRegistry`, and `LittleGhost::Run`.