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
@@ -0,0 +1,275 @@
1
+ # Code Mode
2
+
3
+ Code mode lets an Agent solve a multi-step Tool task in one small program. The
4
+ model can gather independent results, filter them, and combine them before it
5
+ returns to the conversation. Your Ruby Tools keep their usual permission
6
+ checks.
7
+
8
+ Start by adding code mode to an Agent that already has a Tool:
9
+
10
+ ```ruby
11
+ class HelpCenterLookupTool < LittleGhost::Tool
12
+ tool_name "help_center_lookup"
13
+ description "Find a support answer by topic."
14
+ input_schema(
15
+ type: "object",
16
+ properties: {query: {type: "string"}},
17
+ required: ["query"],
18
+ additionalProperties: false
19
+ )
20
+
21
+ def call(input)
22
+ entries = {
23
+ "refund policy" => "Refunds are available within 30 days.",
24
+ "shipping policy" => "Standard shipping takes three to five days."
25
+ }
26
+ entries.fetch(input.fetch("query"), "No matching entry.")
27
+ end
28
+ end
29
+
30
+ class ResearchAgent < LittleGhost::Agent
31
+ tools HelpCenterLookupTool
32
+ code_mode
33
+ end
34
+ ```
35
+
36
+ The language adapter that runs the program is called an engine. By default,
37
+ LittleGhost uses its Ruby engine and the native Sandbox for the host operating
38
+ system. It fails closed when that Sandbox is unavailable.
39
+
40
+ Code mode adds three control Tools to the conversation. `exec` starts a
41
+ program, `wait` checks on a program that is still working, and `stop` ends work
42
+ that is no longer needed. The model can now send Ruby like this to `exec`:
43
+
44
+ ```ruby
45
+ results = tools.parallel(
46
+ -> { tools.help_center_lookup(query: "refund policy") },
47
+ -> { tools.help_center_lookup(query: "shipping policy") }
48
+ )
49
+
50
+ text(results.join("\n"))
51
+ ```
52
+
53
+ LittleGhost turns the Agent's Tool schemas into Ruby method signatures. The
54
+ model sees those signatures and the available Tool names in its instructions.
55
+ Those names, descriptions, and signatures form the code-mode Tool catalog. The
56
+ model can compose the results with ordinary Ruby values instead of guessing
57
+ how to call each Tool.
58
+
59
+ ## See what runs where
60
+
61
+ The program runs in a child interpreter. The Tools do not.
62
+
63
+ ```text
64
+ model
65
+ │ writes a program
66
+ ▼
67
+ exec ──> sandboxed Ruby process
68
+ │ tools.help_center_lookup(...)
69
+ ▼
70
+ Tool broker in the parent Ruby process
71
+ │ normal Tool execution
72
+ ▼
73
+ HelpCenterLookupTool#call
74
+ ```
75
+
76
+ The Tool broker receives interpreter calls in the parent Ruby process. It
77
+ accepts only Tools registered on the Agent, then sends each call through the
78
+ same validation, permission checks, limits, callbacks, events, and tracing used by
79
+ a direct Tool call.
80
+
81
+ Public streams show the brokered Tools by name. They omit the `exec`, `wait`,
82
+ and `stop` bookkeeping. Traces still record the control operation around its
83
+ nested Tool calls, so you can follow the complete execution.
84
+
85
+ Code mode does not change what a Tool can do. A Tool still runs as application
86
+ code, while the generated program runs in the configured Sandbox. Read
87
+ [Tools](tools.md) for Tool permission checks and [Workspaces and
88
+ Sandboxes](sandboxing.md) before giving generated programs file or process
89
+ access.
90
+
91
+ ## Compose Tool calls with Ruby
92
+
93
+ Each `exec` starts a fresh Ruby process. Local variables, constants, and globals
94
+ do not carry into a later `exec`. Within one program, the model can use:
95
+
96
+ - Named methods such as `tools.help_center_lookup(query: "refund policy")`.
97
+ - `tools.call(name, arguments)` when the Tool name is dynamic.
98
+ - `tools.parallel` for independent calls whose results should preserve input
99
+ order.
100
+ - `ALL_TOOLS` to inspect the complete runtime catalog.
101
+ - `text(value)` to add user-visible output.
102
+ - The program's final expression as the completed value returned by `exec` or
103
+ a later `wait`.
104
+ - `finish(value)` to complete early.
105
+
106
+ The dynamic form accepts either the catalog name (`"help_center_lookup"`) or
107
+ the matching method name (`"tools.help_center_lookup"`).
108
+
109
+ JSON Tool results arrive as ordinary Ruby hashes, arrays, strings, numbers,
110
+ booleans, or `nil`. When a Tool returns `Tool::Result`, code mode uses its
111
+ Ruby `value`. Artifact bytes are not copied into program variables; the
112
+ artifacts return to the model once with the surrounding `exec` or `wait`
113
+ result. Stored references appear only when native media delivery exceeds its
114
+ limit. A Tool failure raises inside the program so its Ruby code can handle the
115
+ failure or return an error.
116
+
117
+ Fresh processes keep interpreter state from leaking across programs. Each Ruby
118
+ program also gets a temporary Workspace. LittleGhost removes it when the
119
+ program ends, so files created directly by the interpreter do not carry into a
120
+ later `exec`.
121
+
122
+ A brokered filesystem Tool uses the Agent Run's separate Workspace. Files
123
+ written through that Tool follow the Run Workspace's cleanup rules and may
124
+ persist.
125
+
126
+ ## Check on work that takes longer
127
+
128
+ Most programs finish while `exec` is watching them, so their result is ready in
129
+ the same Tool call. If a program is still active after one minute, `exec`
130
+ returns `still_working`. The program keeps running. The model can call `wait`
131
+ to watch for up to another minute or `stop` when it no longer needs the result.
132
+
133
+ Both `exec` and `wait` return as soon as the program finishes. The one-minute
134
+ window is a maximum observation time, not a delay added to every call.
135
+
136
+ `wait` does not resume, restart, or extend the program. It returns only output
137
+ produced since the previous `exec` or `wait`. The returned status tells the
138
+ model what to do next:
139
+
140
+ - `still_working` means the program is active. Call `wait` again when its result
141
+ is still needed, or call `stop` to end it.
142
+ - `completed`, `error`, and `terminated` are final. There is no program to wait
143
+ for after one of these statuses.
144
+
145
+ The built-in engines give each program a total lifetime of one hour by default.
146
+ That deadline begins at `exec` and does not reset when the model calls `wait`.
147
+ The engine ends and cleans up an expired program even if the model never checks
148
+ on it again. Applications can configure a shorter total lifetime with
149
+ `wall_seconds`; the one-minute observation window remains fixed.
150
+
151
+ A code-mode session owns the engine's active child process and related
152
+ resources. It accepts only one `exec`, `wait`, or `stop` operation at a time.
153
+ The Agent closes the session when its current call ends, including after a
154
+ failure or cancellation. Cleanup failures raise because LittleGhost cannot
155
+ claim that the child process and its resources ended cleanly.
156
+
157
+ ## Keep a Tool in the conversation
158
+
159
+ With code mode enabled, ordinary Agent Tools move into the program catalog. The
160
+ model-facing controls become `exec`, `wait`, and `stop`. Use `except` when an
161
+ application Tool should remain available to the conversational model instead
162
+ of moving into the program:
163
+
164
+ ```ruby
165
+ class ResearchAgent < LittleGhost::Agent
166
+ tools HelpCenterLookupTool, ConfirmTool
167
+ code_mode except: ["confirm_tool"]
168
+ end
169
+ ```
170
+
171
+ Exclude a Tool when the conversational model should call it as a distinct
172
+ decision—for example, a final confirmation that must remain visible as its own
173
+ step. `except` uses each Tool's model-visible name; `ConfirmTool` defaults to
174
+ `confirm_tool`. Calls made inside and outside code mode share the Agent's
175
+ Tool-call limit. The `exec`, `wait`, and `stop` controls manage execution. They
176
+ do not count toward that application Tool limit themselves.
177
+
178
+ Subagent controls also stay in the conversation. They are orchestration choices
179
+ for the parent model, not functions available inside a code-mode program.
180
+ Code-mode `wait` watches an interpreter program; `wait_for_subagents` checks on
181
+ delegated Agents. [Core Concepts](core_concepts.md#subagents-bring-in-a-specialist)
182
+ explains model-directed delegation.
183
+
184
+ ## Set limits for the work you expect
185
+
186
+ The Ruby engine sets limits for source and output size, memory, total and CPU
187
+ time, file size, the number of programs, Tool calls, concurrency, and cleanup.
188
+ Override only the limits your workload needs to change:
189
+
190
+ ```ruby
191
+ LittleGhost.configure do |config|
192
+ config.code_mode = {
193
+ engine: :ruby,
194
+ sandbox: :native,
195
+ limits: {
196
+ programs: 16,
197
+ wall_seconds: 900,
198
+ cleanup_seconds: 5
199
+ }
200
+ }
201
+ end
202
+ ```
203
+
204
+ Bubblewrap owns the program's process tree but does not cap its process or thread
205
+ count. Use an outer cgroup or container supervisor when generated code needs a
206
+ hard task-count limit.
207
+
208
+ Application defaults apply to every Agent that declares `code_mode`. An Agent
209
+ can override the engine, Sandbox, limits, or excluded Tools in its own
210
+ declaration.
211
+
212
+ The operating-system Sandbox contains the interpreter. The parent Ruby process
213
+ starts it, brokers Tool calls, and cleans it up. Language restrictions alone
214
+ cannot contain native extensions, interpreter bugs, files, subprocesses, or
215
+ sockets.
216
+
217
+ Use an enforcing Sandbox backend for model-written code. Before production,
218
+ test the deployed backend against the files, child processes, networking, and
219
+ resource pressure your application expects. Also test cancellation and cleanup
220
+ on the deployed host.
221
+
222
+ ## Opt into JavaScript when it fits
223
+
224
+ The JavaScript engine is optional. It uses MiniRacer and gives each program its
225
+ own V8 global state. The core gem does not require or load MiniRacer:
226
+
227
+ ```ruby
228
+ # Gemfile
229
+ gem "mini_racer", "~> 0.21"
230
+
231
+ # application setup
232
+ require "little_ghost/code_mode/javascript_engine"
233
+
234
+ LittleGhost.configure do |config|
235
+ config.code_mode = {engine: :javascript, sandbox: :native}
236
+ end
237
+ ```
238
+
239
+ The JavaScript program has no Node.js APIs, filesystem, network, console,
240
+ WebAssembly, or process-spawning API. Tool methods return Promises, and the
241
+ generated instructions include TypeScript declarations derived from each Tool
242
+ schema. Use `await` or `Promise.all`, `text(value)` for output, and `exit()` to
243
+ complete early. Call `text(value)` first when the value should become
244
+ user-visible output.
245
+
246
+ MiniRacer's language-level restrictions are useful, but the operating-system
247
+ Sandbox still contains the program. The Ruby parent owns the Tool catalog,
248
+ permission checks, Tool-call limits, events, tracing, and resource cleanup.
249
+
250
+ ## Build a custom engine
251
+
252
+ Applications can register another `CodeMode::Engine`. An engine names its
253
+ language, writes the instructions shown to the model, and opens a
254
+ `CodeMode::Session`. The session implements `#execute`, `#wait`, `#stop`, and
255
+ `#close`. The first three operations return a `CodeMode::ProgramResult`.
256
+
257
+ LittleGhost gives the engine a Tool broker and a Sandbox factory. The broker
258
+ stays in the parent Ruby process. The factory creates the Sandbox that
259
+ contains model-written code. An engine may request named runtime paths for its
260
+ interpreter libraries. Those paths are visible to the child process, but they
261
+ never become filesystem grants available through Tools.
262
+
263
+ The session owns every Workspace, Sandbox, child process, thread, and
264
+ communication channel it creates. It closes those resources in reverse order.
265
+ LittleGhost may use one registered engine instance for concurrent Agent Runs,
266
+ so the engine must keep each program's mutable state inside its session.
267
+
268
+ A sandboxed engine must use a backend that owns the complete child process tree
269
+ or can prevent child processes. An explicitly unrestricted backend may run an
270
+ engine, but the generated program then has the same host access as the parent.
271
+
272
+ See `LittleGhost::CodeMode::Engine`, `LittleGhost::CodeMode::Session`, and
273
+ `LittleGhost::CodeMode::ProgramResult` for the extension contract. Continue
274
+ with [Integrations](integrations.md) to connect Runs to MCP tools, AG-UI, and
275
+ OpenTelemetry.