little_ghost 0.2.1 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 450a61dddcb991278311cf48d3a86b65fa09930a939ad4cf877b226cf59be74b
4
- data.tar.gz: ee6d950747b8b1ef19078c4e6c18252eb47cb8581def86efa266bf208703683c
3
+ metadata.gz: 9890ec252b435a45ce78f82a57b107fbfa7f1846f4cf580e0538ce3303c2d7c5
4
+ data.tar.gz: 47c8aa6beeaf56a8987088007fa67e56144394a3fff1dcdb8e357812725ab7e6
5
5
  SHA512:
6
- metadata.gz: 0c726c4cae4d584facab0c7542f6f95b939ac6c8bac78ff3b5d525d5e47ce673280d773572243b61df14da8881b4105488beffa45b4659abffb40f11e71368ad
7
- data.tar.gz: bc7d5f909e3d48382b889ad4d4cf8fadd2df7978b09069a20f7632da858f788d6210eb1a13921b232a1a5ebf8d25cebfa240e4c4090ef96d135df2d74f376e5a
6
+ metadata.gz: 92540329eaa105782f108dc52cf0e21351b7ae60c674d6b6d786a460b221e54f569ae7f3240bdb0d8a436ea6b0102ef1d14251fa543d2d9c7522aad6d9142f4a
7
+ data.tar.gz: 3dcb8b76e48448cbbd8088cfc0ee5ef0edcfcdb4b595c93ed741d60ee9f852a4ece504794119bdd874761ed1e40af97f7e05f6bb3f81884c9b8d4c4b0656a43a
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <hr>
8
8
 
9
- Bring agents and agentic workflows into an existing Ruby system, or use them as the core of a dedicated AI service. LittleGhost connects model providers, tools, streaming, sessions, delegation, deterministic workflows, and observability through Ruby APIs.
9
+ Build an agent inside an existing Ruby system, or use LittleGhost as the core of a dedicated AI service. An agent is a reusable Ruby class that gives one model a prompt, tools, limits, and a model selection. LittleGhost connects that class to providers, streaming, sessions, delegation, and observability.
10
10
 
11
11
  Set `OPENAI_API_KEY`, then paste this customer support agent into a Ruby console or file. It selects a model directly and exposes one validated application tool:
12
12
 
@@ -51,19 +51,33 @@ end
51
51
 
52
52
  ## How the pieces fit
53
53
 
54
+ One agent is the smallest useful LittleGhost application. A request creates a run, the agent may call a tool or delegate to a subagent, and the run records the outcome:
55
+
54
56
  ```text
55
57
  provider connections + model selections ──> ModelResolver ──> provider
56
58
 
57
59
  request ──> CustomerSupportAgent ──> HelpCenterLookupTool
58
60
 
59
61
  └────────> ResearchAgent subagent
62
+ ```
63
+
64
+ When one model loop is not enough, LittleGhost calls the larger callable unit an **assembly**. An assembly may be one agent or a coordinated group that still looks like one agent to its caller:
65
+
66
+ ```text
67
+ request ──> CustomerSupportAgent
60
68
 
61
69
  request ──> ResponseWorkflow ──> ResearchAgent ──> CustomerSupportAgent
70
+
71
+ request ──> ProblemSolverSwarm ──> TriageAgent ──handoff──> BillingAgent
72
+
73
+ request ──> SupportFlowGraph ──> TriageAgent ──edge──> ResponseAgent
62
74
  ```
63
75
 
64
- Configuration owns shared services such as model resolution, sessions, lookup paths, workspaces, sandboxes, and instrumentation. Agent classes own behavior: their model selection, prompt, tools, limits, result schema, and delegation policy. Tools expose narrow application operations. A subagent lets the model delegate within configured limits; a workflow uses ordinary Ruby when the application must control ordering.
76
+ A `Workflow` uses ordinary Ruby to enforce ordering and branching. A `Swarm` lets configured agents hand the request directly to one another. A `Graph` follows application-declared nodes and edges when the allowed paths should be visible in advance. These types share `ask` and `stream_ask`, so callers can depend on one entrypoint contract while the implementation grows.
77
+
78
+ Class definitions are the default way to organize agents and assemblies. Builder objects support definitions whose participants or topology are discovered at runtime; the Core Concepts guide introduces them after the class-based forms.
65
79
 
66
- Each top-level execution owns a run lifecycle. The run checkpoints session state, closes its resources, aggregates usage, and emits framework events whether the entrypoint is an agent or a workflow.
80
+ Each top-level execution owns one run lifecycle. The run checkpoints session state, closes its resources, aggregates usage, and emits framework events regardless of which assembly type is the entrypoint.
67
81
 
68
82
  ## Installation and configuration
69
83
 
@@ -104,12 +118,12 @@ By default, LittleGhost maps `default` to GPT-5.6 Luna. It configures convention
104
118
 
105
119
  Applications that need custom routing can subclass `LittleGhost::ModelResolver` and install the class with `config.model_resolver`. A custom resolver owns its profiles and default role; configuring `models`, `models_path`, or `default_model` at the same time is an error. Provider configuration remains available to the resolver.
106
120
 
107
- LittleGhost runs inside the surrounding Ruby process; it does not prescribe an HTTP server, CLI, job system, or application layout. `config/little_ghost`, `app/agents`, `app/prompts`, `app/tools`, and `app/skills` are optional conventions. Every path can be configured, and agents, prompts, and tools may live wherever the application loads them.
121
+ LittleGhost runs inside the surrounding Ruby process; it does not prescribe an HTTP server, CLI, job system, or application layout. `config/little_ghost`, `app/agents`, `app/assemblies`, `app/prompts`, `app/tools`, and `app/skills` are optional conventions. Keep agent classes in `app/agents`; workflow, swarm, and graph classes conventionally live in `app/assemblies`. Names should reveal the type, such as `DevelopmentWorkflow`, `ProblemSolverSwarm`, or `SupportFlowGraph`.
108
122
 
109
123
  ## Documentation
110
124
 
111
125
  - [Getting Started](docs/guides/getting_started.md) builds and streams the customer support example.
112
- - [Core Concepts](docs/guides/core_concepts.md) explains models, agents, tools, runs, delegation, workflows, and sessions.
126
+ - [Core Concepts](docs/guides/core_concepts.md) starts with one agent, then introduces assemblies, delegation, workflows, swarms, graphs, and dynamic builders.
113
127
  - [API reference](rdoc-ref:LittleGhost) covers exact signatures, options, and lifecycle behavior.
114
128
 
115
129
  ## Contributing
@@ -1,6 +1,6 @@
1
1
  # Core Concepts
2
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: ModelResolver 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.
3
+ Start with one agent. In LittleGhost, an **agent** is a reusable Ruby definition for one model loop: it selects a model, supplies instructions, exposes tools, and decides when the model has finished answering one request.
4
4
 
5
5
  ```text
6
6
  shared configuration
@@ -12,9 +12,31 @@ one request
12
12
  │ ├── HelpCenterLookupTool
13
13
  │ └── ResearchAgent subagent (model-directed)
14
14
  └── sessions, resources, usage, events, and terminal result
15
+ ```
16
+
17
+ The sections below build outward from that unit. After the agent, the guide introduces its model, tools, and run lifecycle. It then names an **assembly**: anything a caller can invoke like one agent, including coordinated workflows, swarms, and graphs.
18
+
19
+ ## Agents declare one model-driven behavior
20
+
21
+ An agent class keeps the behavior for one application role together:
22
+
23
+ ```ruby
24
+ class CustomerSupportAgent < LittleGhost::Agent
25
+ description "Answers customer support questions."
26
+ model :customer_support
27
+ system_prompt "Answer clearly. Check the help center before stating company guidance."
28
+ tools HelpCenterLookupTool
29
+ end
30
+ ```
31
+
32
+ The class-level DSL is inheritable. It can declare prompts, limits, callbacks, tool classes, structured results, context management, skills, and delegation. Capabilities remain inactive until their corresponding DSL is called.
33
+
34
+ `CustomerSupportAgent.ask` creates a standalone entrypoint, consumes one `LittleGhost::Run`, and returns that run. `CustomerSupportAgent.stream_ask` creates the same entrypoint and yields events while it works. Create `CustomerSupportAgent.new(runtime:)` explicitly when several calls should reuse one runtime.
15
35
 
16
- one deterministic request
17
- └── Run ──> ResponseWorkflow ──> ResearchAgent ──> CustomerSupportAgent
36
+ ```ruby
37
+ run = CustomerSupportAgent.ask("Can I get a refund?")
38
+ run.response # final text from the top-level execution
39
+ run.result.output # text, or a validated structured value when declared
18
40
  ```
19
41
 
20
42
  ## Models can be selected directly or by role
@@ -60,37 +82,26 @@ Dotted roles inherit from the nearest registered parent. `ResearchAgent` can req
60
82
 
61
83
  The provider performs model I/O. `LittleGhost::ModelResolver` resolves application intent into a `LittleGhost::Model`, which carries the provider, target, settings, details, and role for a run.
62
84
 
63
- ## Agents declare behavior
64
-
65
- An agent class declares application behavior:
66
-
67
- ```ruby
68
- class CustomerSupportAgent < LittleGhost::Agent
69
- description "Answers customer support questions."
70
- model "customer_support"
71
- system_prompt "Answer clearly. Check the help center before stating company guidance."
72
- tools HelpCenterLookupTool
73
- subagent ResearchAgent, kind: "research"
74
- end
75
- ```
85
+ ## Runs own top-level lifecycle
76
86
 
77
- 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.
87
+ A `LittleGhost::Run` owns one top-level execution. It opens the session, workspace, sandbox, agent entrypoint, and registered resources, then closes owned resources in reverse order. Later sections show how the same lifecycle can own a coordinated entrypoint.
78
88
 
79
- `CustomerSupportAgent.ask` creates a standalone entrypoint, builds and consumes a `LittleGhost::Run`, and returns that run. `CustomerSupportAgent.stream_ask` creates the same kind of entrypoint and yields the run's events. Create `CustomerSupportAgent.new(runtime:)` explicitly when several calls should reuse one runtime. Agents built by a runtime are instead scoped to their owning run and return a `LittleGhost::RunResult` from `#call`.
89
+ 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.
80
90
 
81
- That distinction explains two useful return paths:
91
+ Long-lived services can supervise a run without making their request thread own its execution:
82
92
 
83
93
  ```ruby
84
- run = CustomerSupportAgent.ask("Can I get a refund?")
85
- run.response # final text from the top-level execution
86
- run.result.output # text, or a validated structured value when declared
87
- ```
88
-
89
- ## Runs own top-level lifecycle
94
+ execution = CustomerSupportAgent.new.start_execution(
95
+ message: "Investigate transfer 481"
96
+ ) do |event|
97
+ event_buffer << event
98
+ end
90
99
 
91
- 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.
100
+ execution.interrupt_response(message: "Include the latest ledger entry")
101
+ run = execution.wait(deadline: Time.now + 30)
102
+ ```
92
103
 
93
- 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.
104
+ `LittleGhost::Execution` owns the worker, preserves request-scoped execution state, and coordinates cancellation, interruptions, waiting, and bounded shutdown. The underlying run still owns agent resources and its terminal outcome. Event consumers run on the worker thread. Applications should keep them thread-safe and avoid blocking indefinitely.
94
105
 
95
106
  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.
96
107
 
@@ -116,8 +127,31 @@ end
116
127
 
117
128
  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.
118
129
 
130
+ A tool can return a `LittleGhost::Tool::ExecutionResult` with `companion_content` when the next model request also needs text, images, or documents. LittleGhost keeps the ordinary tool result intact, then appends each tool's companion blocks as a transient user message in tool-call order. Session persistence omits those transient messages. Tool-use, tool-result, and reasoning blocks are rejected as companion content.
131
+
119
132
  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.
120
133
 
134
+ ## Assemblies let coordination look like one agent
135
+
136
+ An **assembly** is any LittleGhost entrypoint that a caller can use like one agent. `CustomerSupportAgent` is therefore the smallest assembly: it contains one agent and one model loop.
137
+
138
+ When a feature needs several agents, three coordination classes preserve that same caller interface:
139
+
140
+ - A `Workflow` uses Ruby code to enforce ordering, branching, and parallel work.
141
+ - A `Swarm` lets configured agents choose direct handoffs to one another.
142
+ - A `Graph` follows named nodes and application-declared edges.
143
+
144
+ ```ruby
145
+ CustomerSupportAgent.ask("Can I get a refund?")
146
+ ResponseWorkflow.ask("Can I get a refund?")
147
+ ProblemSolverSwarm.ask("Can I get a refund?")
148
+ SupportFlowGraph.ask("Can I get a refund?")
149
+ ```
150
+
151
+ Each call returns a top-level `LittleGhost::Run`, and each `stream_ask` yields the same event vocabulary. The caller chooses an entrypoint without needing to branch on its internal coordination style. Instances also share `call`, `stream`, `start_execution`, interruption, and `as_tool` behavior.
152
+
153
+ Keep agent definitions in `app/agents`. Put workflow, swarm, and graph definitions in `app/assemblies`, with class names ending in `Workflow`, `Swarm`, or `Graph`. The next sections explain when each form earns its name.
154
+
121
155
  ## Subagents are model-directed delegation
122
156
 
123
157
  Declaring `ResearchAgent` as a subagent gives `CustomerSupportAgent` a configured set of tools for spawning, messaging, interrupting, waiting for, and listing research work:
@@ -162,23 +196,115 @@ class ResponseWorkflow < LittleGhost::Workflow
162
196
  end
163
197
  ```
164
198
 
165
- `#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.
199
+ `#invoke` builds a lazy Assembly invocation, so a workflow step may be an agent, workflow, swarm, or graph. Calling `#output` consumes an intermediate invocation; `#perform` must return its final invocation unconsumed so LittleGhost can stream it 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.
200
+
201
+ Unlike Graph nodes and Swarm members, each Workflow invocation receives the full caller history and application context by default. Isolated copies prevent one child from mutating a sibling's context; they do not prevent disclosure. Pass `history: []`, `context: {}`, or explicitly redacted values to `invoke` when participants use different providers or privileges.
202
+
203
+ Independent invocations can run concurrently while ordinary Ruby still controls composition:
204
+
205
+ ```ruby
206
+ research, verification = parallel(
207
+ invoke(ResearchGraph, as: :research),
208
+ invoke(VerificationWorkflow, as: :verification),
209
+ max_concurrency: 2
210
+ )
211
+ ```
212
+
213
+ Results preserve declaration order. Each branch receives isolated application context and cooperative cancellation. `timeout:`, `retries:`, `retry_on:`, and `retry_delay:` apply to `invoke`; retries require explicit exception classes because rerunning an Assembly may repeat tool side effects.
214
+
215
+ Cancellation, deadlines, and step timeouts are cooperative. They do not forcibly stop provider or tool code, and they do not roll back external side effects. A participant must honor its cancellation token or deadline, and applications must decide whether an operation is safe to retry.
166
216
 
167
- A workflow is an explicit entrypoint on a run:
217
+ A workflow has the same entrypoint API as an agent:
168
218
 
169
219
  ```ruby
170
- runtime = LittleGhost::Runtime.new(configuration: LittleGhost.configuration)
171
- run = runtime.build_run(
172
- {message: "Review this unusual refund request"},
173
- agent_class: CustomerSupportAgent,
174
- entrypoint_class: ResponseWorkflow
175
- ).call
220
+ run = ResponseWorkflow.ask("Review this unusual refund request")
176
221
 
177
222
  puts run.response
178
223
  ```
179
224
 
180
225
  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.
181
226
 
227
+ ## Swarms use direct agent handoffs
228
+
229
+ A swarm keeps one member active at a time and injects one reserved `handoff_to_agent` tool. A member either answers the caller or hands the request directly to another configured member:
230
+
231
+ ```ruby
232
+ class ProblemSolverSwarm < LittleGhost::Swarm
233
+ member TriageAgent
234
+ member BillingAgent
235
+ member AccountAgent
236
+ start TriageAgent
237
+ handoff TriageAgent, to: [BillingAgent, AccountAgent]
238
+ max_steps 12
239
+ max_handoff_repeats 3
240
+ end
241
+ ```
242
+
243
+ Members are fresh Agent instances; unlike Workflow invocations and Graph nodes, Swarm members intentionally remain Agent-only so handoffs stay direct and local. A complete Swarm can still be used as a Workflow step, Graph node, or tool. A handoff names the next member and supplies a message plus optional JSON-like context. That context remains untrusted model-authored prompt content; it does not become trusted application state. A member cannot hand off to itself, hand off outside the allowed topology, or combine a handoff with another tool call. Without `handoff` declarations, routing remains all-to-all except self-handoffs. A member with no declared outgoing target receives no handoff tool. Invalid calls return an ordinary tool error so the model can recover. If no handoff occurs, the current member's response is final. Members receive only the current request or explicit handoff envelope by default; opt into original caller data with `history: true` or `context: true` on that member.
244
+
245
+ Potentially intermediate model text is omitted from the caller's ordinary response stream. Streams expose Assembly lifecycle events, then the final member's ordinary response events. `max_steps` bounds total work and `max_handoff_repeats` detects repeated directed transitions; either limit raises `AssemblyLimitError` when exhausted. Members also accept the shared cooperative retry and timeout options described for workflows.
246
+
247
+ ## Graphs guide serial and parallel paths
248
+
249
+ A graph names Assembly nodes and directed edges. Ordinary edges select exactly one next node, while explicit forks and joins add bounded parallel work without shared mutable reducers:
250
+
251
+ ```ruby
252
+ class SupportFlowGraph < LittleGhost::Graph
253
+ node :triage, TriageAgent
254
+ node :research, ResearchAgent
255
+ node :verify, VerificationWorkflow
256
+ node :respond, CustomerSupportAgent
257
+
258
+ start :triage
259
+ fork :triage, to: [:research, :verify], max_concurrency: 2
260
+ join [:research, :verify], to: :respond
261
+ finish :respond
262
+ max_steps 12
263
+ end
264
+ ```
265
+
266
+ An edge condition receives immutable `Graph::State`, including the original input, history, context, step, current and previous node names, predecessors, branch results, completed results, and a routed error when present. Exactly one matching conditional edge wins; otherwise one unconditional fallback is used. An `error_edge` can route selected application errors after retries are exhausted. Cancellation, parent deadlines, and cleanup failures always remain control flow.
267
+
268
+ By default, a downstream node receives the original multimodal input plus labeled predecessor output. A join receives every branch output in declaration order. Pass `input: ->(state) { ... }` on an edge, error edge, or join to replace that mapping. Nodes receive no caller history or application context unless their declaration opts in with `history: true` or `context: true`. The original request and routed outputs still cross node and provider boundaries by default, and routing callbacks can inspect the original context through `Graph::State`; map or redact inputs explicitly when participants have different privileges. Nodes may name any Assembly type, class, builder, or immutable definition. Fork branches may follow ordinary edges before reaching their distinct declared join sources.
269
+
270
+ `validate!` catches invalid topology before model work begins, and `to_mermaid` renders a deterministic diagram. A graph suppresses intermediate ordinary model stream events, publishes lifecycle, transition, fork, join, retry, and error events, aggregates usage, and forwards only the finish node's ordinary response stream. Downstream nodes still receive routed outputs, and terminal step records retain bounded semantic outputs for callers to inspect.
271
+
272
+ Composite `RunResult` objects expose immutable `steps` and a `trajectory`. Step records include participants, attempts, timing, usage, relationships, and bounded semantic outputs without retaining transcripts or tool payloads. Swarm handoffs retain only their explicit handoff envelope. These records support assertions such as `result.trajectory.concurrent?(first_id, second_id)` without coupling tests to a tracing backend.
273
+
274
+ ## Builders unlock definitions discovered at runtime
275
+
276
+ Class definitions are the default because they keep behavior, names, and topology close together. Each agent or assembly class can produce an immutable `.definition` snapshot or an independent mutable `.to_builder` variant.
277
+
278
+ Use a builder when application code discovers participants or routes at runtime. The builder records the same declaration that the class DSL would organize:
279
+
280
+ ```ruby
281
+ graph = LittleGhost::GraphBuilder.new(id: "support_flow")
282
+ graph.node :triage, TriageAgent
283
+ graph.node :respond, CustomerSupportAgent
284
+ graph.start :triage
285
+ graph.edge :triage, :respond
286
+ graph.finish :respond
287
+ graph.validate!
288
+
289
+ run = graph.ask("Can I get a refund?")
290
+ ```
291
+
292
+ `AgentBuilder`, `WorkflowBuilder`, `SwarmBuilder`, and `GraphBuilder` share the Assembly execution API. Builders remain mutable; each build or invocation recursively snapshots declaration containers and referenced Assembly definitions, so later builder declarations affect only future executions. Definitions are Ruby objects rather than portable JSON because conditions, callbacks, workflow bodies, factories, and resolvers may contain executable Ruby. Those closures and their external dependencies remain live trusted application code; the snapshot does not freeze state they capture.
293
+
294
+ ## Assemblies can be tools
295
+
296
+ Any assembly instance supports `as_tool`. Agent classes can also declare another assembly as a tool:
297
+
298
+ ```ruby
299
+ class CustomerSupportAgent < LittleGhost::Agent
300
+ assembly_as_tool SupportFlowGraph, name: "investigate_support_case"
301
+ end
302
+ ```
303
+
304
+ `assemblies_as_tools` declares several with shared options. Existing `agent_as_tool` and `agents_as_tools` remain agent-specific aliases. Composite assemblies do not accept agent-only model or tool overrides.
305
+
306
+ An assembly tool receives the invoking tool context's application state on every call. `preserve_context: false` prevents conversational history from carrying between calls; it does not suppress that application state. Nested tools must continue to authorize privileged work from trusted context rather than from model-authored input.
307
+
182
308
  ## Structured results separate data from prose
183
309
 
184
310
  An agent that feeds application code can declare a strict JSON object schema:
@@ -209,15 +335,18 @@ Use structured results when code consumes fields. Keep ordinary text when a huma
209
335
 
210
336
  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.
211
337
 
338
+ Applications that need to reconcile persisted messages with invocation history can register a `LittleGhost::Runtime::Hook` and implement `session_history`. The hook receives the run plus `stored:` and `fallback:` message collections. Return the history to use, or `nil` to defer to the next hook and ultimately the session default. This keeps application-specific reconciliation policy outside the framework session type.
339
+
212
340
  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.
213
341
 
214
342
  ## Keep the boundary visible
215
343
 
216
- The core design can be summarized as four choices:
344
+ The core design can be summarized as five choices:
217
345
 
218
346
  - Put shared construction and provider policy in configuration; use inline declarations or independent YAML files according to the application's needs.
219
347
  - Put model behavior and available capabilities on agent classes.
220
348
  - Put privileged application operations behind narrow, authorized tools.
221
- - Put mandatory ordering in workflows; leave optional delegation to subagents.
349
+ - Put imperative ordering in workflows, dynamic peer routing in swarms, and guided routing in graphs.
350
+ - Leave addressable background delegation to subagents.
222
351
 
223
- Return to [Getting Started](getting_started.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::ModelResolver`.
352
+ Return to [Getting Started](getting_started.md) for the complete first-run setup. The API reference covers exact signatures and lifecycle details for `LittleGhost::Runtime`, `LittleGhost::Run`, `LittleGhost::Execution`, `LittleGhost::Assembly`, `LittleGhost::Agent`, `LittleGhost::Tool`, `LittleGhost::Workflow`, `LittleGhost::Swarm`, `LittleGhost::Graph`, and `LittleGhost::ModelResolver`.
@@ -120,7 +120,7 @@ Both calls use the same agent definition and active LittleGhost configuration. C
120
120
 
121
121
  ## Add a model role when the application grows
122
122
 
123
- Direct targets keep a small application easy to read. A larger application can give the same selection a stable role, then change the underlying provider, model, and defaults without editing each agent class:
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:
124
124
 
125
125
  ```ruby
126
126
  LittleGhost.configure do |config|
@@ -155,10 +155,14 @@ end
155
155
 
156
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.
157
157
 
158
- ## Fit LittleGhost into your application
158
+ ## Fit the agent into your application
159
159
 
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 these definitions, `app/agents` and `app/tools` are available conventions, and every lookup path is configurable.
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.
161
+
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`.
163
+
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.
161
165
 
162
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.
163
167
 
164
- Read [Core Concepts](core_concepts.md) next for model selection, reusable runtimes, subagents, workflows, sessions, and the boundaries between them. The API reference covers exact signatures for `LittleGhost::Agent`, `LittleGhost::Tool`, `LittleGhost::Configuration`, and `LittleGhost::Run`.
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.
@@ -30,7 +30,7 @@ module LittleGhost
30
30
  default: Subagents::Manager::DEFAULT_WAIT_TIMEOUT
31
31
  base.class_attribute :subagent_declarations_value, default: []
32
32
  base.class_attribute :subagent_resolvers_value, default: []
33
- base.class_attribute :agent_tool_declarations_value, default: []
33
+ base.class_attribute :assembly_tool_declarations_value, default: []
34
34
  end
35
35
 
36
36
  # Exposes delegation declarations on agent classes.
@@ -95,25 +95,52 @@ module LittleGhost
95
95
  # conversational history between calls to that tool instance.
96
96
  def agent_as_tool(agent_class, name: nil, description: nil, model: nil, tools: nil,
97
97
  preserve_context: false)
98
+ assembly_as_tool(
99
+ agent_class,
100
+ name:,
101
+ description:,
102
+ model:,
103
+ tools:,
104
+ preserve_context:
105
+ )
106
+ end
107
+
108
+ # Exposes an Agent, Workflow, Swarm, or Graph as one ordinary tool.
109
+ # Agent-only +model+ and +tools+ overrides are rejected for composites.
110
+ def assembly_as_tool(assembly_class, name: nil, description: nil, model: nil, tools: nil,
111
+ preserve_context: false)
112
+ unless assembly_class.is_a?(Class) && assembly_class <= Assembly
113
+ raise ConfigurationError, "Delegated assemblies must inherit from LittleGhost::Assembly"
114
+ end
98
115
  validate_delegated_tools!(tools)
116
+ if !(assembly_class <= Agent) && (model || tools)
117
+ raise ConfigurationError, "Composite assemblies do not accept delegated model or tool overrides"
118
+ end
99
119
  declaration = {
100
- agent: agent_class,
101
- name: (name || agent_class.agent_id).to_s,
102
- description: description || agent_class.description,
120
+ assembly: assembly_class,
121
+ name: (name || assembly_class.assembly_id).to_s,
122
+ description: description || assembly_class.description,
103
123
  model:,
104
124
  tools:,
105
125
  preserve_context:
106
126
  }
107
- self.agent_tool_declarations_value = [*agent_tool_declarations, declaration]
127
+ self.assembly_tool_declarations_value = [*assembly_tool_declarations, declaration]
108
128
  end
109
129
 
110
130
  # Exposes several agent classes as ordinary tools with shared options.
111
131
  def agents_as_tools(*agent_classes, **options)
112
132
  agent_classes.each { |agent_class| agent_as_tool(agent_class, **options) }
113
- agent_tool_declarations
133
+ assembly_tool_declarations
134
+ end
135
+
136
+ # Exposes several assembly classes as ordinary tools with shared options.
137
+ def assemblies_as_tools(*assembly_classes, **options)
138
+ assembly_classes.each { |assembly_class| assembly_as_tool(assembly_class, **options) }
139
+ assembly_tool_declarations
114
140
  end
115
141
 
116
- def agent_tool_declarations = agent_tool_declarations_value # :nodoc:
142
+ def assembly_tool_declarations = assembly_tool_declarations_value # :nodoc:
143
+ def agent_tool_declarations = assembly_tool_declarations # :nodoc:
117
144
 
118
145
  private
119
146
 
@@ -176,7 +176,8 @@ module LittleGhost
176
176
  replacement = Tool::ExecutionResult.new(
177
177
  content: "#{warning}\n\n#{result.content}",
178
178
  status: result.status,
179
- error: result.error
179
+ error: result.error,
180
+ companion_content: result.companion_content
180
181
  )
181
182
  Support::Callbacks.replace(payload.merge(result: replacement))
182
183
  end