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.
- checksums.yaml +4 -4
- data/README.md +90 -84
- data/docs/guides/assemblies.md +412 -0
- data/docs/guides/code_mode.md +275 -0
- data/docs/guides/core_concepts.md +150 -239
- data/docs/guides/getting_started.md +125 -87
- data/docs/guides/integrations.md +217 -0
- data/docs/guides/models_and_providers.md +125 -0
- data/docs/guides/production.md +253 -0
- data/docs/guides/prompt_views.md +139 -0
- data/docs/guides/sandboxing.md +282 -0
- data/docs/guides/skills.md +141 -0
- data/docs/guides/structured_outputs_and_content.md +135 -0
- data/docs/guides/tools.md +329 -0
- data/lib/little_ghost/ag_ui/adapter.rb +5 -5
- data/lib/little_ghost/agent/delegation.rb +3 -3
- data/lib/little_ghost/agent/skills.rb +6 -1
- data/lib/little_ghost/agent/tool_loop.rb +6 -2
- data/lib/little_ghost/agent.rb +411 -214
- data/lib/little_ghost/agent_builder.rb +61 -22
- data/lib/little_ghost/{agent_interruptions.rb → agent_interjections.rb} +12 -12
- data/lib/little_ghost/agent_stream_source.rb +262 -0
- data/lib/little_ghost/artifact.rb +182 -0
- data/lib/little_ghost/artifacts/presentation_budget.rb +44 -0
- data/lib/little_ghost/artifacts/workspace_store.rb +370 -0
- data/lib/little_ghost/artifacts.rb +11 -0
- data/lib/little_ghost/assembly.rb +110 -30
- data/lib/little_ghost/assembly_builder.rb +59 -21
- data/lib/little_ghost/assembly_execution.rb +110 -6
- data/lib/little_ghost/code_mode/broker.rb +164 -0
- data/lib/little_ghost/code_mode/catalog.rb +54 -0
- data/lib/little_ghost/code_mode/engine.rb +58 -0
- data/lib/little_ghost/code_mode/javascript/catalog.rb +118 -0
- data/lib/little_ghost/code_mode/javascript/client.rb +550 -0
- data/lib/little_ghost/code_mode/javascript/host.rb +347 -0
- data/lib/little_ghost/code_mode/javascript/session.rb +579 -0
- data/lib/little_ghost/code_mode/javascript_engine.rb +172 -0
- data/lib/little_ghost/code_mode/protocol.rb +78 -0
- data/lib/little_ghost/code_mode/ruby/catalog.rb +80 -0
- data/lib/little_ghost/code_mode/ruby/host.rb +102 -0
- data/lib/little_ghost/code_mode/ruby/session.rb +508 -0
- data/lib/little_ghost/code_mode/ruby_engine.rb +86 -0
- data/lib/little_ghost/code_mode/runtime.rb +335 -0
- data/lib/little_ghost/code_mode/session.rb +61 -0
- data/lib/little_ghost/code_mode/types.rb +58 -0
- data/lib/little_ghost/code_mode.rb +53 -0
- data/lib/little_ghost/configuration.rb +366 -47
- data/lib/little_ghost/content.rb +24 -13
- data/lib/little_ghost/data_map.rb +209 -0
- data/lib/little_ghost/errors.rb +28 -9
- data/lib/little_ghost/execution.rb +33 -34
- data/lib/little_ghost/graph.rb +376 -197
- data/lib/little_ghost/mcp/client.rb +487 -88
- data/lib/little_ghost/mcp/toolset.rb +210 -0
- data/lib/little_ghost/mcp/types.rb +216 -0
- data/lib/little_ghost/mcp.rb +3 -0
- data/lib/little_ghost/message.rb +4 -4
- data/lib/little_ghost/model_capabilities.rb +9 -6
- data/lib/little_ghost/model_request.rb +0 -12
- data/lib/little_ghost/model_resolver.rb +15 -5
- data/lib/little_ghost/model_response.rb +3 -7
- data/lib/little_ghost/network/authorizer_server.rb +162 -0
- data/lib/little_ghost/network/certificate_authority.rb +98 -0
- data/lib/little_ghost/network/envoy_config.rb +362 -0
- data/lib/little_ghost/network/envoy_gateway.rb +409 -0
- data/lib/little_ghost/network/external_gateway.rb +68 -0
- data/lib/little_ghost/network.rb +96 -0
- data/lib/little_ghost/prompt_resolver.rb +9 -7
- data/lib/little_ghost/provider_registry.rb +3 -3
- data/lib/little_ghost/providers/anthropic.rb +8 -1
- data/lib/little_ghost/providers/gemini.rb +10 -1
- data/lib/little_ghost/providers/vertex_ai.rb +6 -1
- data/lib/little_ghost/run.rb +162 -54
- data/lib/little_ghost/run_context.rb +42 -20
- data/lib/little_ghost/run_result.rb +0 -7
- data/lib/little_ghost/runtime/hook.rb +8 -3
- data/lib/little_ghost/runtime/hooks/artifacts.rb +338 -0
- data/lib/little_ghost/runtime.rb +160 -52
- data/lib/little_ghost/sandbox/capabilities.rb +68 -0
- data/lib/little_ghost/sandbox/environment_policy.rb +41 -0
- data/lib/little_ghost/sandbox/filesystem.rb +377 -0
- data/lib/little_ghost/sandbox/isolated_backend.rb +163 -0
- data/lib/little_ghost/sandbox/limits.rb +48 -0
- data/lib/little_ghost/sandbox/mount.rb +129 -0
- data/lib/little_ghost/sandbox/network_policy.rb +108 -0
- data/lib/little_ghost/sandbox/policy.rb +88 -0
- data/lib/little_ghost/sandbox/process_runner.rb +103 -0
- data/lib/little_ghost/sandbox/process_session.rb +335 -0
- data/lib/little_ghost/sandbox/scope.rb +304 -0
- data/lib/little_ghost/sandbox.rb +158 -32
- data/lib/little_ghost/sandboxes/bubblewrap.rb +351 -0
- data/lib/little_ghost/sandboxes/native.rb +51 -0
- data/lib/little_ghost/sandboxes/seatbelt.rb +261 -0
- data/lib/little_ghost/sandboxes/unrestricted.rb +241 -0
- data/lib/little_ghost/session.rb +39 -26
- data/lib/little_ghost/session_store.rb +9 -5
- data/lib/little_ghost/session_stores/agent_core_memory.rb +64 -56
- data/lib/little_ghost/session_stores/filesystem.rb +261 -0
- data/lib/little_ghost/session_stores/memory.rb +7 -0
- data/lib/little_ghost/skills/catalog.rb +94 -14
- data/lib/little_ghost/skills/resource_root.rb +42 -0
- data/lib/little_ghost/skills/skill.rb +0 -3
- data/lib/little_ghost/stream_event.rb +8 -13
- data/lib/little_ghost/subagents/control_tool.rb +8 -0
- data/lib/little_ghost/subagents/manager.rb +53 -50
- data/lib/little_ghost/support/callbacks.rb +3 -1
- data/lib/little_ghost/support/content_capture.rb +3 -3
- data/lib/little_ghost/support/http_client.rb +2 -2
- data/lib/little_ghost/support/redactor.rb +1 -1
- data/lib/little_ghost/swarm.rb +13 -5
- data/lib/little_ghost/tool.rb +157 -64
- data/lib/little_ghost/tool_registry.rb +1 -1
- data/lib/little_ghost/tools/filesystem.rb +1 -1
- data/lib/little_ghost/tools/shell.rb +4 -3
- data/lib/little_ghost/tools/write_todos.rb +6 -1
- data/lib/little_ghost/tracing/open_telemetry.rb +1 -1
- data/lib/little_ghost/version.rb +1 -1
- data/lib/little_ghost/workflow.rb +30 -21
- data/lib/little_ghost/workspace.rb +222 -8
- data/lib/little_ghost.rb +40 -27
- metadata +104 -3
- data/lib/little_ghost/unrestricted_sandbox.rb +0 -306
|
@@ -1,31 +1,245 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
3
6
|
module LittleGhost
|
|
4
|
-
# A Workspace
|
|
5
|
-
# to decide how those
|
|
7
|
+
# A Workspace names the host paths associated with a Run. Pair it with a
|
|
8
|
+
# Sandbox to decide how those paths may be read, changed, or used by commands.
|
|
6
9
|
#
|
|
7
10
|
# workspace = LittleGhost::Workspace.new(root: "./tmp/support-run")
|
|
8
11
|
# workspace.root # => an absolute path ending in "/tmp/support-run"
|
|
9
12
|
#
|
|
10
|
-
# Workspaces participate in the
|
|
11
|
-
#
|
|
13
|
+
# Workspaces participate in the Run resource lifecycle, but object lifetime
|
|
14
|
+
# and file lifetime are separate. Opening creates +root+ and relative named
|
|
15
|
+
# paths, but does not delete them by default. Absolute named paths are trusted
|
|
16
|
+
# references that must already exist. Setup and teardown callbacks let trusted
|
|
17
|
+
# application configuration provision run-scoped resources without a
|
|
18
|
+
# Workspace subclass. Applications that share a writable root between Runs
|
|
19
|
+
# must provide their own concurrency and tenant isolation.
|
|
20
|
+
#
|
|
21
|
+
# See the {Workspaces and Sandboxes guide}[rdoc-ref:docs/guides/sandboxing.md]
|
|
22
|
+
# for logical paths, Sandbox policy, process ownership, and networking.
|
|
12
23
|
class Workspace
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
@providers = {}
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
# Registers a trusted workspace provider under a configuration symbol.
|
|
28
|
+
def register_provider(name, implementation)
|
|
29
|
+
unless implementation.is_a?(Class) && implementation <= Workspace
|
|
30
|
+
raise ArgumentError, "workspace provider must be a Workspace class"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Workspace.providers[name.to_sym] = implementation
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Resolves an explicitly selected provider without changing its meaning.
|
|
37
|
+
def resolve_provider(name)
|
|
38
|
+
Workspace.providers.fetch(name.to_sym) do
|
|
39
|
+
raise DependencyError, "workspace provider :#{name} is not available"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def providers # :nodoc:
|
|
44
|
+
@providers ||= {}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Expands +root+ and every named path to absolute paths. Relative named paths
|
|
49
|
+
# must remain beneath +root+; absolute named paths deliberately refer outside
|
|
50
|
+
# it. +setup+ receives +workspace:+ and +run:+ when the Run opens. +teardown+
|
|
51
|
+
# receives the same values when it closes, including after partial setup.
|
|
52
|
+
def initialize(root:, paths: {}, setup: nil, teardown: nil)
|
|
15
53
|
@root = File.expand_path(root)
|
|
54
|
+
@paths = normalize_paths(paths)
|
|
55
|
+
@setup = validate_callback(setup, :setup)
|
|
56
|
+
@teardown = validate_callback(teardown, :teardown)
|
|
57
|
+
@opened = false
|
|
58
|
+
@active = false
|
|
59
|
+
@run = nil
|
|
60
|
+
@identities = nil
|
|
16
61
|
end
|
|
17
62
|
|
|
18
63
|
# Absolute filesystem root assigned to this workspace.
|
|
19
64
|
attr_reader :root
|
|
20
65
|
|
|
21
|
-
#
|
|
66
|
+
# Immutable named absolute paths owned by this workspace declaration.
|
|
67
|
+
attr_reader :paths
|
|
68
|
+
|
|
69
|
+
# Returns a configured named path, raising KeyError when it is absent.
|
|
70
|
+
def path(name)
|
|
71
|
+
paths.fetch(name.to_sym)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Converts a logical path to its physical workspace path. This method checks
|
|
75
|
+
# lexical containment but does not make direct filesystem access safe for
|
|
76
|
+
# untrusted input. Pass model-selected paths through Sandbox file operations,
|
|
77
|
+
# which reject symlinks while opening each path component.
|
|
78
|
+
#
|
|
79
|
+
# Relative paths belong to +root+; named paths use
|
|
80
|
+
# <tt>workspace://name/path</tt>. Physical absolute paths are deliberately
|
|
81
|
+
# rejected so brokered tools do not teach callers host filesystem layout.
|
|
82
|
+
def resolve(reference)
|
|
83
|
+
validate! if @identities
|
|
84
|
+
value = String(reference)
|
|
85
|
+
raise ArgumentError, "workspace paths must be relative or use workspace://" if Pathname.new(value).absolute?
|
|
86
|
+
|
|
87
|
+
base, relative = if value.start_with?("workspace://")
|
|
88
|
+
logical = value.delete_prefix("workspace://")
|
|
89
|
+
name, separator, child = logical.partition("/")
|
|
90
|
+
raise ArgumentError, "workspace path must name a configured path" if name.empty?
|
|
91
|
+
|
|
92
|
+
[path(name), separator.empty? ? "." : child]
|
|
93
|
+
else
|
|
94
|
+
[root, value]
|
|
95
|
+
end
|
|
96
|
+
resolve_beneath(base, relative)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns the stable logical reference for a physical workspace path.
|
|
100
|
+
def reference(physical_path)
|
|
101
|
+
candidate = File.expand_path(physical_path)
|
|
102
|
+
named = paths.sort_by { |_, path| -path.length }.find { |_, path| beneath?(candidate, path) }
|
|
103
|
+
if named
|
|
104
|
+
name, base = named
|
|
105
|
+
relative = candidate.delete_prefix(base).delete_prefix(File::SEPARATOR)
|
|
106
|
+
return relative.empty? ? "workspace://#{name}" : "workspace://#{name}/#{relative}"
|
|
107
|
+
end
|
|
108
|
+
raise ArgumentError, "path is outside the workspace" unless beneath?(candidate, root)
|
|
109
|
+
|
|
110
|
+
candidate.delete_prefix(root).delete_prefix(File::SEPARATOR).then { |value| value.empty? ? "." : value }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Environment variables supplied to sandboxed programs. These values are
|
|
114
|
+
# trusted process configuration and are never returned by filesystem tools.
|
|
115
|
+
def environment
|
|
116
|
+
{"LITTLE_GHOST_WORKSPACE_ROOT" => root}.merge(paths.to_h do |name, path|
|
|
117
|
+
["LITTLE_GHOST_WORKSPACE_#{name.to_s.upcase.gsub(/[^A-Z0-9]/, "_")}", path]
|
|
118
|
+
end).freeze
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Verifies that no configured directory was replaced after #open.
|
|
122
|
+
def validate!
|
|
123
|
+
return self unless @identities
|
|
124
|
+
|
|
125
|
+
{root: root}.merge(paths).each do |name, path|
|
|
126
|
+
realpath = File.realpath(path)
|
|
127
|
+
stat = File.stat(realpath)
|
|
128
|
+
expected = @identities.fetch(name)
|
|
129
|
+
unless [realpath, stat.dev, stat.ino] == expected
|
|
130
|
+
raise ToolError, "workspace path changed after opening: #{name}"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
self
|
|
134
|
+
rescue Errno::ENOENT
|
|
135
|
+
raise ToolError, "workspace path changed after opening: #{name}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Calls the application setup callback once and returns this workspace.
|
|
22
139
|
def open(run: nil)
|
|
140
|
+
return self if @opened
|
|
141
|
+
|
|
142
|
+
@run = run
|
|
143
|
+
@active = true
|
|
144
|
+
@setup&.call(workspace: self, run:)
|
|
145
|
+
materialize!
|
|
146
|
+
capture_identities!
|
|
147
|
+
@opened = true
|
|
23
148
|
self
|
|
149
|
+
rescue
|
|
150
|
+
close
|
|
151
|
+
raise
|
|
24
152
|
end
|
|
25
153
|
|
|
26
|
-
#
|
|
154
|
+
# Calls the application teardown callback once. The default does not remove
|
|
155
|
+
# files or directories.
|
|
27
156
|
def close
|
|
157
|
+
return nil unless @active
|
|
158
|
+
|
|
159
|
+
@teardown&.call(workspace: self, run: @run)
|
|
28
160
|
nil
|
|
161
|
+
ensure
|
|
162
|
+
@opened = false
|
|
163
|
+
@active = false
|
|
164
|
+
@run = nil
|
|
165
|
+
@identities = nil
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
def materialize!
|
|
171
|
+
FileUtils.mkdir_p(root)
|
|
172
|
+
paths.each do |name, directory|
|
|
173
|
+
if @relative_path_names.include?(name)
|
|
174
|
+
FileUtils.mkdir_p(directory)
|
|
175
|
+
elsif !File.directory?(directory)
|
|
176
|
+
raise ArgumentError, "absolute workspace path :#{name} must already exist"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def capture_identities!
|
|
182
|
+
entries = {root: root}.merge(paths)
|
|
183
|
+
identities = entries.to_h do |name, path|
|
|
184
|
+
realpath = File.realpath(path)
|
|
185
|
+
stat = File.stat(realpath)
|
|
186
|
+
[name, [realpath, stat.dev, stat.ino].freeze]
|
|
187
|
+
end
|
|
188
|
+
duplicates = identities.group_by { |_, identity| identity.drop(1) }.select { |_, values| values.length > 1 }
|
|
189
|
+
unless duplicates.empty?
|
|
190
|
+
names = duplicates.values.flat_map { |values| values.map(&:first) }
|
|
191
|
+
raise ArgumentError, "workspace paths must not alias the same directory: #{names.join(", ")}"
|
|
192
|
+
end
|
|
193
|
+
@identities = identities.freeze
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def resolve_beneath(base, relative)
|
|
197
|
+
raise ArgumentError, "workspace path must not be empty" if relative.empty?
|
|
198
|
+
components = Pathname.new(relative).each_filename.to_a
|
|
199
|
+
raise ArgumentError, "workspace path traversal is not allowed" if components.include?("..")
|
|
200
|
+
|
|
201
|
+
expanded = File.expand_path(relative, base)
|
|
202
|
+
raise ArgumentError, "workspace path escapes its root" unless beneath?(expanded, base)
|
|
203
|
+
|
|
204
|
+
expanded
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def beneath?(candidate, base)
|
|
208
|
+
candidate == base || candidate.start_with?(base.end_with?(File::SEPARATOR) ? base : "#{base}#{File::SEPARATOR}")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def normalize_paths(values)
|
|
212
|
+
unless values.respond_to?(:each_pair)
|
|
213
|
+
raise ArgumentError, "workspace paths must be a Hash"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
relative_names = []
|
|
217
|
+
normalized = values.each_pair.to_h do |name, value|
|
|
218
|
+
key = name.to_sym
|
|
219
|
+
path = value.to_s
|
|
220
|
+
raise ArgumentError, "workspace path :#{key} must not be empty" if path.empty?
|
|
221
|
+
|
|
222
|
+
expanded = File.expand_path(path, root)
|
|
223
|
+
root_prefix = root.end_with?(File::SEPARATOR) ? root : "#{root}#{File::SEPARATOR}"
|
|
224
|
+
relative = !Pathname.new(path).absolute?
|
|
225
|
+
if relative && expanded != root && !expanded.start_with?(root_prefix)
|
|
226
|
+
raise ArgumentError, "relative workspace path :#{key} must remain beneath the workspace root"
|
|
227
|
+
end
|
|
228
|
+
relative_names << key if relative
|
|
229
|
+
|
|
230
|
+
[key, expanded.freeze]
|
|
231
|
+
end.freeze
|
|
232
|
+
@relative_path_names = relative_names.freeze
|
|
233
|
+
normalized
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def validate_callback(callback, name)
|
|
237
|
+
return unless callback
|
|
238
|
+
raise ArgumentError, "workspace #{name} must be callable" unless callback.respond_to?(:call)
|
|
239
|
+
|
|
240
|
+
callback
|
|
29
241
|
end
|
|
30
242
|
end
|
|
243
|
+
|
|
244
|
+
Workspace.register_provider(:directory, Workspace)
|
|
31
245
|
end
|
data/lib/little_ghost.rb
CHANGED
|
@@ -4,6 +4,9 @@ require_relative "little_ghost/version"
|
|
|
4
4
|
require_relative "little_ghost/errors"
|
|
5
5
|
require_relative "little_ghost/execution_state"
|
|
6
6
|
require_relative "little_ghost/support"
|
|
7
|
+
require_relative "little_ghost/data_map"
|
|
8
|
+
require_relative "little_ghost/artifact"
|
|
9
|
+
require_relative "little_ghost/artifacts"
|
|
7
10
|
require_relative "little_ghost/events"
|
|
8
11
|
require_relative "little_ghost/instrumentation"
|
|
9
12
|
require_relative "little_ghost/tracing/open_telemetry"
|
|
@@ -14,6 +17,7 @@ require_relative "little_ghost/usage"
|
|
|
14
17
|
require_relative "little_ghost/content"
|
|
15
18
|
require_relative "little_ghost/message"
|
|
16
19
|
require_relative "little_ghost/stream_event"
|
|
20
|
+
require_relative "little_ghost/agent_stream_source"
|
|
17
21
|
require_relative "little_ghost/model_request"
|
|
18
22
|
require_relative "little_ghost/model_response"
|
|
19
23
|
require_relative "little_ghost/run_result"
|
|
@@ -42,14 +46,22 @@ require_relative "little_ghost/model_resolver"
|
|
|
42
46
|
require_relative "little_ghost/run_context"
|
|
43
47
|
require_relative "little_ghost/workspace"
|
|
44
48
|
require_relative "little_ghost/sandbox"
|
|
45
|
-
require_relative "little_ghost/
|
|
49
|
+
require_relative "little_ghost/sandbox/process_session"
|
|
50
|
+
require_relative "little_ghost/sandboxes/unrestricted"
|
|
51
|
+
require_relative "little_ghost/sandboxes/bubblewrap"
|
|
52
|
+
require_relative "little_ghost/sandboxes/seatbelt"
|
|
53
|
+
require_relative "little_ghost/sandboxes/native"
|
|
46
54
|
require_relative "little_ghost/tool"
|
|
47
55
|
require_relative "little_ghost/tool_execution"
|
|
48
56
|
require_relative "little_ghost/tool_registry"
|
|
57
|
+
require_relative "little_ghost/code_mode"
|
|
58
|
+
require_relative "little_ghost/code_mode/ruby_engine"
|
|
59
|
+
require_relative "little_ghost/code_mode/runtime"
|
|
49
60
|
require_relative "little_ghost/structured_output"
|
|
50
61
|
require_relative "little_ghost/prompt_resolver"
|
|
51
62
|
require_relative "little_ghost/session_store"
|
|
52
63
|
require_relative "little_ghost/session_stores/memory"
|
|
64
|
+
require_relative "little_ghost/session_stores/filesystem"
|
|
53
65
|
require_relative "little_ghost/session"
|
|
54
66
|
require_relative "little_ghost/skills"
|
|
55
67
|
require_relative "little_ghost/tools/write_todos"
|
|
@@ -58,7 +70,7 @@ require_relative "little_ghost/execution"
|
|
|
58
70
|
require_relative "little_ghost/subagents/definition"
|
|
59
71
|
require_relative "little_ghost/subagents/agent_path"
|
|
60
72
|
require_relative "little_ghost/subagents/manager"
|
|
61
|
-
require_relative "little_ghost/
|
|
73
|
+
require_relative "little_ghost/agent_interjections"
|
|
62
74
|
require_relative "little_ghost/assembly"
|
|
63
75
|
require_relative "little_ghost/assembly_execution"
|
|
64
76
|
require_relative "little_ghost/agent"
|
|
@@ -68,41 +80,33 @@ require_relative "little_ghost/graph"
|
|
|
68
80
|
require_relative "little_ghost/assembly_builder"
|
|
69
81
|
require_relative "little_ghost/agent_factory"
|
|
70
82
|
require_relative "little_ghost/runtime/hook"
|
|
83
|
+
require_relative "little_ghost/runtime/hooks/artifacts"
|
|
71
84
|
require_relative "little_ghost/runtime"
|
|
72
85
|
|
|
73
|
-
# Build AI features
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# cohesive set of Ruby conventions.
|
|
86
|
+
# Build AI features as ordinary Ruby classes. An Agent owns one model
|
|
87
|
+
# conversation. Larger units called Assemblies coordinate several Agents while
|
|
88
|
+
# keeping the same +ask+ and +stream_ask+ entrypoints.
|
|
77
89
|
#
|
|
78
|
-
#
|
|
79
|
-
# specialist for research while the caller observes one run:
|
|
80
|
-
#
|
|
81
|
-
# class ResearchAgent < LittleGhost::Agent
|
|
82
|
-
# description "Researches difficult support questions"
|
|
83
|
-
# model "openai:gpt-5.6-luna"
|
|
84
|
-
# end
|
|
90
|
+
# Start with one model-driven behavior:
|
|
85
91
|
#
|
|
86
92
|
# class CustomerSupportAgent < LittleGhost::Agent
|
|
87
93
|
# description "Handles support requests"
|
|
88
|
-
# model "openai
|
|
89
|
-
#
|
|
90
|
-
# subagent ResearchAgent, kind: "research"
|
|
94
|
+
# model "openrouter:openai/gpt-5.6-luna"
|
|
95
|
+
# system_prompt "Answer customer questions clearly."
|
|
91
96
|
# end
|
|
92
97
|
#
|
|
93
98
|
# run = CustomerSupportAgent.ask("Why is transfer 481 still pending?")
|
|
94
99
|
# run.completed? # => true
|
|
95
|
-
# run.response
|
|
100
|
+
# run.response
|
|
101
|
+
# # One possible response: Transfer 481 is waiting for the receiving bank.
|
|
96
102
|
#
|
|
97
|
-
#
|
|
98
|
-
# and
|
|
99
|
-
#
|
|
100
|
-
# coordination policy.
|
|
103
|
+
# The class holds reusable behavior. Each call creates a Run, which records the
|
|
104
|
+
# result and closes the resources opened for that request. Workflow, Swarm, and
|
|
105
|
+
# Graph are Assembly types for coordinating more than one participant.
|
|
101
106
|
#
|
|
102
|
-
# LittleGhost.
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
# runtime is built.
|
|
107
|
+
# Configure LittleGhost before the first call. The first standalone call builds
|
|
108
|
+
# a shared Runtime from that configuration. +with_configuration+ can select an
|
|
109
|
+
# independent configuration for one execution context.
|
|
106
110
|
module LittleGhost
|
|
107
111
|
class << self
|
|
108
112
|
# The configuration active in the current execution context, falling back to
|
|
@@ -114,15 +118,24 @@ module LittleGhost
|
|
|
114
118
|
# Opens the active Configuration for application setup and returns it.
|
|
115
119
|
#
|
|
116
120
|
# Configuration files are loaded lazily when a runtime is first built, so
|
|
117
|
-
# make application-level changes before invoking an agent.
|
|
121
|
+
# make application-level changes before invoking an agent. Once the shared
|
|
122
|
+
# Runtime is ready, later mutations raise ConfigurationError.
|
|
118
123
|
def configure(&block)
|
|
119
124
|
configuration.configure(&block)
|
|
120
125
|
end
|
|
121
126
|
|
|
127
|
+
# Returns the shared Runtime for the active Configuration.
|
|
128
|
+
#
|
|
129
|
+
# Most applications do not need to call this method. Standalone Agent and
|
|
130
|
+
# Assembly entrypoints use it automatically. Runtime construction is lazy,
|
|
131
|
+
# thread-safe, and locks the active Configuration after it succeeds.
|
|
132
|
+
def runtime = configuration.runtime
|
|
133
|
+
|
|
122
134
|
# Returns the model resolver owned by the active process configuration.
|
|
123
135
|
def model_resolver = configuration.model_resolver
|
|
124
136
|
|
|
125
|
-
# Makes +configuration+
|
|
137
|
+
# Makes +configuration+ and its independent shared Runtime current only
|
|
138
|
+
# while the block runs.
|
|
126
139
|
#
|
|
127
140
|
# Execution state restores the previous configuration even when the block
|
|
128
141
|
# raises. Other execution contexts continue to see their own configuration.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: little_ghost
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matt Robinson
|
|
@@ -23,6 +23,34 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: fiddle
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: json_schemer
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.5'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.5'
|
|
26
54
|
- !ruby/object:Gem::Dependency
|
|
27
55
|
name: opentelemetry-api
|
|
28
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -51,6 +79,20 @@ dependencies:
|
|
|
51
79
|
- - "~>"
|
|
52
80
|
- !ruby/object:Gem::Version
|
|
53
81
|
version: '5.25'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: mini_racer
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0.21'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0.21'
|
|
54
96
|
- !ruby/object:Gem::Dependency
|
|
55
97
|
name: rake
|
|
56
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -117,8 +159,18 @@ extra_rdoc_files: []
|
|
|
117
159
|
files:
|
|
118
160
|
- LICENSE.txt
|
|
119
161
|
- README.md
|
|
162
|
+
- docs/guides/assemblies.md
|
|
163
|
+
- docs/guides/code_mode.md
|
|
120
164
|
- docs/guides/core_concepts.md
|
|
121
165
|
- docs/guides/getting_started.md
|
|
166
|
+
- docs/guides/integrations.md
|
|
167
|
+
- docs/guides/models_and_providers.md
|
|
168
|
+
- docs/guides/production.md
|
|
169
|
+
- docs/guides/prompt_views.md
|
|
170
|
+
- docs/guides/sandboxing.md
|
|
171
|
+
- docs/guides/skills.md
|
|
172
|
+
- docs/guides/structured_outputs_and_content.md
|
|
173
|
+
- docs/guides/tools.md
|
|
122
174
|
- lib/little_ghost.rb
|
|
123
175
|
- lib/little_ghost/ag_ui.rb
|
|
124
176
|
- lib/little_ghost/ag_ui/adapter.rb
|
|
@@ -129,13 +181,36 @@ files:
|
|
|
129
181
|
- lib/little_ghost/agent/tool_loop.rb
|
|
130
182
|
- lib/little_ghost/agent_builder.rb
|
|
131
183
|
- lib/little_ghost/agent_factory.rb
|
|
132
|
-
- lib/little_ghost/
|
|
184
|
+
- lib/little_ghost/agent_interjections.rb
|
|
185
|
+
- lib/little_ghost/agent_stream_source.rb
|
|
186
|
+
- lib/little_ghost/artifact.rb
|
|
187
|
+
- lib/little_ghost/artifacts.rb
|
|
188
|
+
- lib/little_ghost/artifacts/presentation_budget.rb
|
|
189
|
+
- lib/little_ghost/artifacts/workspace_store.rb
|
|
133
190
|
- lib/little_ghost/assembly.rb
|
|
134
191
|
- lib/little_ghost/assembly_builder.rb
|
|
135
192
|
- lib/little_ghost/assembly_execution.rb
|
|
193
|
+
- lib/little_ghost/code_mode.rb
|
|
194
|
+
- lib/little_ghost/code_mode/broker.rb
|
|
195
|
+
- lib/little_ghost/code_mode/catalog.rb
|
|
196
|
+
- lib/little_ghost/code_mode/engine.rb
|
|
197
|
+
- lib/little_ghost/code_mode/javascript/catalog.rb
|
|
198
|
+
- lib/little_ghost/code_mode/javascript/client.rb
|
|
199
|
+
- lib/little_ghost/code_mode/javascript/host.rb
|
|
200
|
+
- lib/little_ghost/code_mode/javascript/session.rb
|
|
201
|
+
- lib/little_ghost/code_mode/javascript_engine.rb
|
|
202
|
+
- lib/little_ghost/code_mode/protocol.rb
|
|
203
|
+
- lib/little_ghost/code_mode/ruby/catalog.rb
|
|
204
|
+
- lib/little_ghost/code_mode/ruby/host.rb
|
|
205
|
+
- lib/little_ghost/code_mode/ruby/session.rb
|
|
206
|
+
- lib/little_ghost/code_mode/ruby_engine.rb
|
|
207
|
+
- lib/little_ghost/code_mode/runtime.rb
|
|
208
|
+
- lib/little_ghost/code_mode/session.rb
|
|
209
|
+
- lib/little_ghost/code_mode/types.rb
|
|
136
210
|
- lib/little_ghost/configuration.rb
|
|
137
211
|
- lib/little_ghost/content.rb
|
|
138
212
|
- lib/little_ghost/data/model_catalog.json
|
|
213
|
+
- lib/little_ghost/data_map.rb
|
|
139
214
|
- lib/little_ghost/errors.rb
|
|
140
215
|
- lib/little_ghost/events.rb
|
|
141
216
|
- lib/little_ghost/execution.rb
|
|
@@ -146,6 +221,8 @@ files:
|
|
|
146
221
|
- lib/little_ghost/lookup.rb
|
|
147
222
|
- lib/little_ghost/mcp.rb
|
|
148
223
|
- lib/little_ghost/mcp/client.rb
|
|
224
|
+
- lib/little_ghost/mcp/toolset.rb
|
|
225
|
+
- lib/little_ghost/mcp/types.rb
|
|
149
226
|
- lib/little_ghost/message.rb
|
|
150
227
|
- lib/little_ghost/model.rb
|
|
151
228
|
- lib/little_ghost/model_capabilities.rb
|
|
@@ -159,6 +236,12 @@ files:
|
|
|
159
236
|
- lib/little_ghost/models/configuration.rb
|
|
160
237
|
- lib/little_ghost/models/details.rb
|
|
161
238
|
- lib/little_ghost/models/target.rb
|
|
239
|
+
- lib/little_ghost/network.rb
|
|
240
|
+
- lib/little_ghost/network/authorizer_server.rb
|
|
241
|
+
- lib/little_ghost/network/certificate_authority.rb
|
|
242
|
+
- lib/little_ghost/network/envoy_config.rb
|
|
243
|
+
- lib/little_ghost/network/envoy_gateway.rb
|
|
244
|
+
- lib/little_ghost/network/external_gateway.rb
|
|
162
245
|
- lib/little_ghost/path_set.rb
|
|
163
246
|
- lib/little_ghost/prompt_resolver.rb
|
|
164
247
|
- lib/little_ghost/provider_registry.rb
|
|
@@ -184,17 +267,36 @@ files:
|
|
|
184
267
|
- lib/little_ghost/run_result.rb
|
|
185
268
|
- lib/little_ghost/runtime.rb
|
|
186
269
|
- lib/little_ghost/runtime/hook.rb
|
|
270
|
+
- lib/little_ghost/runtime/hooks/artifacts.rb
|
|
187
271
|
- lib/little_ghost/sandbox.rb
|
|
272
|
+
- lib/little_ghost/sandbox/capabilities.rb
|
|
273
|
+
- lib/little_ghost/sandbox/environment_policy.rb
|
|
274
|
+
- lib/little_ghost/sandbox/filesystem.rb
|
|
275
|
+
- lib/little_ghost/sandbox/isolated_backend.rb
|
|
276
|
+
- lib/little_ghost/sandbox/limits.rb
|
|
277
|
+
- lib/little_ghost/sandbox/mount.rb
|
|
278
|
+
- lib/little_ghost/sandbox/network_policy.rb
|
|
279
|
+
- lib/little_ghost/sandbox/policy.rb
|
|
280
|
+
- lib/little_ghost/sandbox/process_runner.rb
|
|
281
|
+
- lib/little_ghost/sandbox/process_session.rb
|
|
282
|
+
- lib/little_ghost/sandbox/scope.rb
|
|
283
|
+
- lib/little_ghost/sandboxes/bubblewrap.rb
|
|
284
|
+
- lib/little_ghost/sandboxes/native.rb
|
|
285
|
+
- lib/little_ghost/sandboxes/seatbelt.rb
|
|
286
|
+
- lib/little_ghost/sandboxes/unrestricted.rb
|
|
188
287
|
- lib/little_ghost/session.rb
|
|
189
288
|
- lib/little_ghost/session_store.rb
|
|
190
289
|
- lib/little_ghost/session_stores/agent_core_memory.rb
|
|
290
|
+
- lib/little_ghost/session_stores/filesystem.rb
|
|
191
291
|
- lib/little_ghost/session_stores/memory.rb
|
|
192
292
|
- lib/little_ghost/skills.rb
|
|
193
293
|
- lib/little_ghost/skills/catalog.rb
|
|
294
|
+
- lib/little_ghost/skills/resource_root.rb
|
|
194
295
|
- lib/little_ghost/skills/skill.rb
|
|
195
296
|
- lib/little_ghost/stream_event.rb
|
|
196
297
|
- lib/little_ghost/structured_output.rb
|
|
197
298
|
- lib/little_ghost/subagents/agent_path.rb
|
|
299
|
+
- lib/little_ghost/subagents/control_tool.rb
|
|
198
300
|
- lib/little_ghost/subagents/definition.rb
|
|
199
301
|
- lib/little_ghost/subagents/manager.rb
|
|
200
302
|
- lib/little_ghost/support.rb
|
|
@@ -218,7 +320,6 @@ files:
|
|
|
218
320
|
- lib/little_ghost/tools/shell.rb
|
|
219
321
|
- lib/little_ghost/tools/write_todos.rb
|
|
220
322
|
- lib/little_ghost/tracing/open_telemetry.rb
|
|
221
|
-
- lib/little_ghost/unrestricted_sandbox.rb
|
|
222
323
|
- lib/little_ghost/usage.rb
|
|
223
324
|
- lib/little_ghost/version.rb
|
|
224
325
|
- lib/little_ghost/workflow.rb
|