little_ghost 0.7.0 → 0.9.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 +34 -6
- data/docs/guides/assemblies.md +21 -9
- data/docs/guides/code_mode.md +2 -2
- data/docs/guides/getting_started.md +27 -2
- data/docs/guides/integrations.md +20 -161
- data/docs/guides/mcp.md +288 -0
- data/docs/guides/models_and_providers.md +21 -54
- data/docs/guides/production.md +14 -5
- data/docs/guides/prompt_views.md +234 -27
- data/docs/guides/providers.md +275 -0
- data/docs/guides/sandboxing.md +42 -7
- data/docs/guides/skills.md +1 -3
- data/docs/guides/tools.md +2 -0
- data/exe/little_ghost +12 -0
- data/lib/little_ghost/agent/context_management.rb +2 -13
- data/lib/little_ghost/agent/delegation.rb +7 -4
- data/lib/little_ghost/agent/skills.rb +8 -6
- data/lib/little_ghost/agent/tool_loop.rb +4 -4
- data/lib/little_ghost/agent.rb +230 -70
- data/lib/little_ghost/agent_builder.rb +16 -4
- data/lib/little_ghost/artifacts/workspace_store.rb +16 -14
- data/lib/little_ghost/assembly.rb +48 -7
- data/lib/little_ghost/assembly_builder.rb +3 -3
- data/lib/little_ghost/cli.rb +429 -0
- data/lib/little_ghost/code_mode/broker.rb +11 -3
- data/lib/little_ghost/code_mode/javascript/client.rb +58 -11
- data/lib/little_ghost/code_mode/javascript/host.rb +37 -13
- data/lib/little_ghost/code_mode/javascript/session.rb +21 -9
- data/lib/little_ghost/code_mode/javascript_engine.rb +21 -42
- data/lib/little_ghost/code_mode/ruby/host.rb +8 -4
- data/lib/little_ghost/code_mode/ruby/session.rb +41 -14
- data/lib/little_ghost/code_mode/ruby_engine.rb +6 -36
- data/lib/little_ghost/code_mode/runtime.rb +57 -24
- data/lib/little_ghost/console.rb +17 -0
- data/lib/little_ghost/errors.rb +20 -2
- data/lib/little_ghost/framework_prompts.rb +376 -0
- data/lib/little_ghost/generators/application_generator.rb +179 -0
- data/lib/little_ghost/generators/templates/application/Gemfile.tt +3 -0
- data/lib/little_ghost/generators/templates/application/README.md.tt +29 -0
- data/lib/little_ghost/generators/templates/application/app/agents/application_agent.rb.tt +5 -0
- data/lib/little_ghost/generators/templates/application/app/prompts/application/system_prompt.erb.tt +1 -0
- data/lib/little_ghost/generators/templates/application/bin/application.tt +10 -0
- data/lib/little_ghost/generators/templates/application/config/little_ghost.rb.tt +5 -0
- data/lib/little_ghost/generators/templates/application/gitignore.tt +2 -0
- data/lib/little_ghost/generators/templates/application/keep.tt +0 -0
- data/lib/little_ghost/graph.rb +24 -5
- data/lib/little_ghost/mcp/client.rb +307 -610
- data/lib/little_ghost/mcp/toolset.rb +133 -95
- data/lib/little_ghost/mcp.rb +21 -3
- data/lib/little_ghost/model_operations.rb +103 -36
- data/lib/little_ghost/model_resolver.rb +2 -0
- data/lib/little_ghost/models/catalog/models_dev_source.rb +4 -1
- data/lib/little_ghost/prompt_resolver.rb +61 -27
- data/lib/little_ghost/prompts/little_ghost/agent/interjections/instructions.erb +12 -0
- data/lib/little_ghost/prompts/little_ghost/agent/system/default.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/batch/feedback/batch_too_large.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/batch/feedback/batch_too_many.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/batch/feedback/item_too_large.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/errors/preparation_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/errors/resolver_invalid.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/errors/value_unstorable.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/format/reference.erb +13 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/format/references.erb +12 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/format/workspace_references.erb +12 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/notices/full_result.erb +14 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/notices/storage_failed.erb +13 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/destination_unsafe.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/secure_unavailable.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/workspace_changed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/workspace_unavailable.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/workspace_unsafe.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/feedback/count_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/feedback/destination_exists.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/feedback/too_large.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/artifacts/storage/feedback/total_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/assembly/feedback/transition_only.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/assembly/tools/fallback/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/artifacts/format/references.erb +12 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/errors/brokered_close.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/errors/cleanup_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/errors/execution_failed.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/arguments_object.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/closed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/control_active.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/limit_exceeded.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/not_active.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/program_active.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/program_timed_out.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/unavailable_tool.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/unknown_program.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/active_programs_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/cleanup_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/execution_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/invalid_request.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/memory_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/pending_calls_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/source_size_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/tools_array.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/instructions.erb +35 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/ruby/feedback/parallel_callables.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/ruby/instructions.erb +40 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/tools/exec/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/tools/stop/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/tools/stop/inputs/max_output_tokens/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/tools/wait/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/code_mode/tools/wait/inputs/max_output_tokens/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/context_management/summary/request.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/context_management/summary/system_instruction.erb +16 -0
- data/lib/little_ghost/prompts/little_ghost/graph/context/format/failed_input.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/graph/context/format/previous_input.erb +13 -0
- data/lib/little_ghost/prompts/little_ghost/graph/context/headings/original_task.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/graph/context/headings/previous_inputs.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/mcp/errors/request_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/mcp/errors/transformation_unserializable.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/mcp/tools/fallback/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/output/truncation/marker.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/errors/mount_changed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/errors/secure_traversal_unavailable.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/errors/workspace_root_changed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/directory_listing_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/entry_required.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/file_read_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/invalid_utf8.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/mount_escape.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/mount_missing.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/multiply_linked.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/not_directory.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/not_file.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/null_byte.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/outside_scope.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/parent_escape.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/path_missing.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/path_traverses_symlink.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/read_only.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/replace_empty.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/symlink_path.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/text_multiple.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/text_not_found.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/write_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/write_parent_missing.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/write_target_symlink.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/policy/feedback/feature_denied.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/process/errors/program_supervisor_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/command_timed_out.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/executable_required.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/program_memory_exceeded.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/program_output_exceeded.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/program_timed_out.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/skills/discovery/instructions.erb +18 -0
- data/lib/little_ghost/prompts/little_ghost/skills/feedback/unknown.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/skills/resources/notices/truncated.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/skills/tools/activate/content.erb +28 -0
- data/lib/little_ghost/prompts/little_ghost/skills/tools/activate/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/skills/tools/activate/inputs/name/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/persistence/format/redaction.erb +12 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/repair/feedback/invalid_result.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/repair/feedback/schema_errors.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/repair/request.erb +14 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/tools/result/description.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/invalid_json.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/missing_tool.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/multiple_tools.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/too_complex.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/too_deep.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/too_large.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/tool_not_exclusive.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/errors/create_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/errors/previous_turn_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/errors/restore_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/errors/turn_failed.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/duplicate_ids.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/duplicate_path.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/identity_capacity.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/inactive.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/interjection_chars_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/interjection_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/interjection_unsupported.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/invalid_cursor.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/invalid_mode.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/list_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/message_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/message_type.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/not_running.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/queue_capacity.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/task_name_invalid.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/task_name_limit.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/terminal_identity.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/turn_capacity.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/unknown_id.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/feedback/unknown_kind.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/notices/turn_cancelled.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/interject/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/interject/inputs/active_id/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/interject/inputs/message/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/list/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/send/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/send/inputs/id/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/send/inputs/message/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/send/inputs/send_mode/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/inputs/kind/description.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/inputs/spawn_mode/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/inputs/task/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/inputs/task_name/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/wait/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/subagents/tools/wait/inputs/ids/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/swarm/handoff/notice.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/swarm/handoff/request.erb +16 -0
- data/lib/little_ghost/prompts/little_ghost/swarm/tools/handoff/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/built_in/list_files/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/built_in/read_file/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/built_in/replace_in_file/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/built_in/shell/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/built_in/write_file/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/built_in/write_todos/description.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/built_in/write_todos/feedback/single_progress.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/built_in/write_todos/feedback/unique_ids.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/errors/unexpected_failure.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/errors/unserializable_result.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/feedback/invalid_input.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/feedback/unknown.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/loop/notices/final_warning.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/loop/notices/termination.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/loop/notices/warning.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/additional_property.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/enum.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/invalid_format.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/invalid_pattern.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/max_items.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/max_length.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/maximum.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/min_items.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/min_length.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/minimum.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/required.erb +10 -0
- data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/type.erb +11 -0
- data/lib/little_ghost/prompts/little_ghost/workspace/feedback/path_changed.erb +10 -0
- data/lib/little_ghost/provider_registry.rb +1 -0
- data/lib/little_ghost/providers/lm_studio/catalog_source.rb +201 -0
- data/lib/little_ghost/providers/lm_studio.rb +52 -0
- data/lib/little_ghost/providers/openai.rb +3 -75
- data/lib/little_ghost/providers/openai_compatible.rb +78 -1
- data/lib/little_ghost/runtime/hooks/artifacts.rb +58 -22
- data/lib/little_ghost/runtime.rb +8 -3
- data/lib/little_ghost/sandbox/environment_policy.rb +29 -4
- data/lib/little_ghost/sandbox/filesystem.rb +51 -33
- data/lib/little_ghost/sandbox/policy.rb +28 -6
- data/lib/little_ghost/sandbox/process_runner.rb +6 -2
- data/lib/little_ghost/sandbox/process_session.rb +8 -4
- data/lib/little_ghost/sandbox/scope.rb +4 -1
- data/lib/little_ghost/sandboxes/seatbelt.rb +3 -1
- data/lib/little_ghost/sandboxes/unrestricted.rb +17 -9
- data/lib/little_ghost/skills/catalog.rb +41 -36
- data/lib/little_ghost/structured_output.rb +6 -6
- data/lib/little_ghost/subagents/control_tool.rb +58 -0
- data/lib/little_ghost/subagents/manager.rb +99 -74
- data/lib/little_ghost/support/output_truncation.rb +28 -4
- data/lib/little_ghost/swarm.rb +20 -7
- data/lib/little_ghost/tool.rb +47 -20
- data/lib/little_ghost/tool_registry.rb +2 -2
- data/lib/little_ghost/tools/filesystem.rb +12 -4
- data/lib/little_ghost/tools/shell.rb +5 -1
- data/lib/little_ghost/tools/write_todos.rb +9 -9
- data/lib/little_ghost/version.rb +1 -1
- data/lib/little_ghost/workflow.rb +199 -27
- data/lib/little_ghost/workspace.rb +2 -2
- data/lib/little_ghost.rb +6 -2
- metadata +222 -7
- data/lib/little_ghost/mcp/types.rb +0 -216
|
@@ -104,12 +104,20 @@ module LittleGhost
|
|
|
104
104
|
tools = []
|
|
105
105
|
agent_class.assembly_tool_declarations.each do |declaration|
|
|
106
106
|
child = declared_assembly(declaration, run, agent_stream_path:)
|
|
107
|
+
factory = if !declaration.fetch(:preserve_context) && child.is_a?(Agent)
|
|
108
|
+
-> { declared_assembly(declaration, run, agent_stream_path:) }
|
|
109
|
+
end
|
|
107
110
|
begin
|
|
108
|
-
|
|
111
|
+
options = {
|
|
109
112
|
name: declaration.fetch(:name),
|
|
110
113
|
description: declaration.fetch(:description),
|
|
111
114
|
preserve_context: declaration.fetch(:preserve_context)
|
|
112
|
-
|
|
115
|
+
}
|
|
116
|
+
tools << if factory
|
|
117
|
+
child.as_tool_with_factory(**options, factory:)
|
|
118
|
+
else
|
|
119
|
+
child.as_tool(**options)
|
|
120
|
+
end
|
|
113
121
|
rescue
|
|
114
122
|
child.close
|
|
115
123
|
raise
|
|
@@ -123,8 +131,12 @@ module LittleGhost
|
|
|
123
131
|
|
|
124
132
|
def declared_assembly(declaration, run, agent_stream_path:)
|
|
125
133
|
assembly_class = declaration.fetch(:assembly)
|
|
126
|
-
if assembly_class.is_a?(AssemblyDefinition)
|
|
127
|
-
|
|
134
|
+
if assembly_class.is_a?(AssemblyDefinition)
|
|
135
|
+
if assembly_class.kind == :agent
|
|
136
|
+
declared_agent(declaration.merge(agent: assembly_class.implementation), run, agent_stream_path:)
|
|
137
|
+
else
|
|
138
|
+
runtime.build_assembly(assembly_class, run:, agent_stream_path:)
|
|
139
|
+
end
|
|
128
140
|
elsif assembly_class <= Agent
|
|
129
141
|
declared_agent(declaration.merge(agent: assembly_class), run, agent_stream_path:)
|
|
130
142
|
else
|
|
@@ -59,7 +59,7 @@ module LittleGhost
|
|
|
59
59
|
max_total_bytes: DEFAULT_MAX_TOTAL_BYTES,
|
|
60
60
|
max_artifacts: DEFAULT_MAX_ARTIFACTS
|
|
61
61
|
)
|
|
62
|
-
raise ToolError, "
|
|
62
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/workspace_unavailable") unless workspace
|
|
63
63
|
|
|
64
64
|
@workspace = workspace
|
|
65
65
|
@path = path.to_sym
|
|
@@ -125,7 +125,7 @@ module LittleGhost
|
|
|
125
125
|
|
|
126
126
|
data = String(entry.fetch(:data)).b
|
|
127
127
|
if data.bytesize > @max_artifact_bytes
|
|
128
|
-
raise ToolError, "
|
|
128
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/feedback/too_large", maximum: @max_artifact_bytes)
|
|
129
129
|
end
|
|
130
130
|
media_type = entry[:media_type]
|
|
131
131
|
name = normalized_name(entry[:name], media_type:)
|
|
@@ -166,9 +166,11 @@ module LittleGhost
|
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
def reserve!(bytes)
|
|
169
|
-
|
|
169
|
+
if @artifacts.length >= @max_artifacts
|
|
170
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/feedback/count_limit", maximum: @max_artifacts)
|
|
171
|
+
end
|
|
170
172
|
if @total_bytes + bytes > @max_total_bytes
|
|
171
|
-
raise ToolError, "
|
|
173
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/feedback/total_limit", maximum: @max_total_bytes)
|
|
172
174
|
end
|
|
173
175
|
|
|
174
176
|
@total_bytes += bytes
|
|
@@ -200,9 +202,9 @@ module LittleGhost
|
|
|
200
202
|
metadata:
|
|
201
203
|
)
|
|
202
204
|
rescue Errno::EEXIST
|
|
203
|
-
raise ToolError, "
|
|
205
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/feedback/destination_exists")
|
|
204
206
|
rescue Errno::ELOOP
|
|
205
|
-
raise ToolError, "
|
|
207
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/destination_unsafe")
|
|
206
208
|
rescue => error
|
|
207
209
|
cleanup_created_file(directory, file_name, error:) if created
|
|
208
210
|
raise
|
|
@@ -241,7 +243,7 @@ module LittleGhost
|
|
|
241
243
|
|
|
242
244
|
def validate_root!
|
|
243
245
|
unless OPENAT && UNLINKAT
|
|
244
|
-
raise ToolError, "
|
|
246
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/secure_unavailable")
|
|
245
247
|
end
|
|
246
248
|
|
|
247
249
|
@workspace.validate!
|
|
@@ -251,20 +253,20 @@ module LittleGhost
|
|
|
251
253
|
workspace_real = File.realpath(@workspace.root)
|
|
252
254
|
location_safe = !beneath?(root, @workspace.root) || beneath?(real, workspace_real)
|
|
253
255
|
unless stat.directory? && !stat.symlink? && location_safe
|
|
254
|
-
raise ToolError, "
|
|
256
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/workspace_unsafe")
|
|
255
257
|
end
|
|
256
258
|
|
|
257
259
|
directory = File.open(root, directory_flags)
|
|
258
260
|
descriptor_stat = directory.stat
|
|
259
261
|
unless descriptor_stat.directory?
|
|
260
|
-
raise ToolError, "
|
|
262
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/workspace_unsafe")
|
|
261
263
|
end
|
|
262
264
|
@workspace.validate!
|
|
263
265
|
[root.freeze, [descriptor_stat.dev, descriptor_stat.ino].freeze].freeze
|
|
264
266
|
rescue KeyError, Errno::ENOENT, Errno::ENOTDIR
|
|
265
|
-
raise ToolError, "
|
|
267
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/workspace_unavailable")
|
|
266
268
|
rescue SystemCallError
|
|
267
|
-
raise ToolError, "
|
|
269
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/workspace_unavailable")
|
|
268
270
|
ensure
|
|
269
271
|
directory&.close
|
|
270
272
|
end
|
|
@@ -274,13 +276,13 @@ module LittleGhost
|
|
|
274
276
|
directory = File.open(@root, directory_flags)
|
|
275
277
|
stat = directory.stat
|
|
276
278
|
unless stat.directory? && [stat.dev, stat.ino] == @root_identity
|
|
277
|
-
raise ToolError, "
|
|
279
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/workspace_changed")
|
|
278
280
|
end
|
|
279
281
|
@workspace.validate!
|
|
280
282
|
directory
|
|
281
283
|
rescue Errno::ENOENT, Errno::ELOOP, Errno::ENOTDIR
|
|
282
284
|
directory&.close
|
|
283
|
-
raise ToolError, "
|
|
285
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/workspace_changed")
|
|
284
286
|
rescue
|
|
285
287
|
directory&.close
|
|
286
288
|
raise
|
|
@@ -308,7 +310,7 @@ module LittleGhost
|
|
|
308
310
|
def validate_regular_file!(file)
|
|
309
311
|
stat = file.stat
|
|
310
312
|
unless stat.file? && stat.nlink == 1 && (stat.mode & 0o777) == 0o600
|
|
311
|
-
raise ToolError, "
|
|
313
|
+
raise ToolError, FrameworkPrompts.reference("artifacts/storage/errors/destination_unsafe")
|
|
312
314
|
end
|
|
313
315
|
end
|
|
314
316
|
|
|
@@ -50,7 +50,8 @@ module LittleGhost
|
|
|
50
50
|
# +context+, +settings+, +metadata+, +session_id+, +actor_id+, and
|
|
51
51
|
# +deadline_at+.
|
|
52
52
|
def ask(message, **options)
|
|
53
|
-
definition
|
|
53
|
+
snapshot = definition
|
|
54
|
+
snapshot.implementation.new.bind_assembly_definition(snapshot).ask(message, **options)
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
# Lazily streams +message+ through a fresh standalone assembly.
|
|
@@ -66,7 +67,7 @@ module LittleGhost
|
|
|
66
67
|
snapshot = definition
|
|
67
68
|
stream = nil
|
|
68
69
|
Enumerator.new do |events|
|
|
69
|
-
stream ||= snapshot.implementation.new.stream_ask(message, **options)
|
|
70
|
+
stream ||= snapshot.implementation.new.bind_assembly_definition(snapshot).stream_ask(message, **options)
|
|
70
71
|
stream.each { |event| events << event }
|
|
71
72
|
end
|
|
72
73
|
end
|
|
@@ -157,6 +158,7 @@ module LittleGhost
|
|
|
157
158
|
@runtime = runtime || run&.runtime || LittleGhost.runtime
|
|
158
159
|
@workspace = workspace || run&.workspace
|
|
159
160
|
@sandbox = sandbox || run&.sandbox
|
|
161
|
+
@assembly_definition = nil
|
|
160
162
|
@standalone = standalone
|
|
161
163
|
@assembly_mutex = Mutex.new
|
|
162
164
|
@assembly_closed = false
|
|
@@ -169,6 +171,11 @@ module LittleGhost
|
|
|
169
171
|
self
|
|
170
172
|
end
|
|
171
173
|
|
|
174
|
+
def bind_assembly_definition(definition) # :nodoc:
|
|
175
|
+
@assembly_definition = definition
|
|
176
|
+
self
|
|
177
|
+
end
|
|
178
|
+
|
|
172
179
|
# Builds the top-level Run used by a standalone assembly.
|
|
173
180
|
def build_run(payload = nil, include_agent_events_by_default: false, **payload_options) # :nodoc:
|
|
174
181
|
payload = entrypoint_payload(payload, payload_options) unless payload_options.empty?
|
|
@@ -178,7 +185,8 @@ module LittleGhost
|
|
|
178
185
|
end
|
|
179
186
|
source_class = run_entrypoint_class
|
|
180
187
|
options = {entrypoint_class: source_class}
|
|
181
|
-
|
|
188
|
+
execution = @assembly_definition || self.class
|
|
189
|
+
options[:execution_class] = execution unless source_class.equal?(execution)
|
|
182
190
|
options[:agent_class] = source_class if is_a?(Agent)
|
|
183
191
|
options[:cancellation_token] = cancellation_token if cancellation_token
|
|
184
192
|
options[:workspace] = workspace if workspace
|
|
@@ -248,9 +256,37 @@ module LittleGhost
|
|
|
248
256
|
# the invoking Tool's current RunContext#state, which may include current
|
|
249
257
|
# request values or values restored from a Session. Nested tools must still
|
|
250
258
|
# authorize privileged work with current, application-established values.
|
|
259
|
+
# A directly bound Agent remains the target for every call, so overlapping
|
|
260
|
+
# calls raise AgentBusyError. Class-level Agent#agent_as_tool declarations
|
|
261
|
+
# build a fresh Agent per context-free call.
|
|
251
262
|
def as_tool(name: self.class.assembly_id, description: self.class.description, preserve_context: false)
|
|
263
|
+
build_tool(name:, description:, preserve_context:, factory: nil)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def as_tool_with_factory(name:, description:, preserve_context:, factory:) # :nodoc:
|
|
267
|
+
first_target = self
|
|
268
|
+
first_target_mutex = Mutex.new
|
|
269
|
+
target_factory = lambda do
|
|
270
|
+
first_target_mutex.synchronize do
|
|
271
|
+
target = first_target
|
|
272
|
+
first_target = nil
|
|
273
|
+
target
|
|
274
|
+
end || factory.call
|
|
275
|
+
end
|
|
276
|
+
build_tool(name:, description:, preserve_context:, factory: target_factory)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
private
|
|
280
|
+
|
|
281
|
+
def build_tool(name:, description:, preserve_context:, factory:)
|
|
252
282
|
assembly = self
|
|
253
|
-
|
|
283
|
+
if description.to_s.empty?
|
|
284
|
+
description = if is_a?(Agent)
|
|
285
|
+
render_framework_prompt("assembly/tools/fallback/description", name: name.to_s)
|
|
286
|
+
else
|
|
287
|
+
FrameworkPrompts.for_runtime(runtime).render("assembly/tools/fallback/description", locals: {name: name.to_s})
|
|
288
|
+
end
|
|
289
|
+
end
|
|
254
290
|
mutex = Mutex.new
|
|
255
291
|
retained_history = []
|
|
256
292
|
tool_class = Tool.define(
|
|
@@ -264,7 +300,10 @@ module LittleGhost
|
|
|
264
300
|
}
|
|
265
301
|
) do |input, context: nil|
|
|
266
302
|
invocation = lambda do
|
|
267
|
-
|
|
303
|
+
factory_target = !factory.nil?
|
|
304
|
+
target = if factory_target
|
|
305
|
+
factory.call
|
|
306
|
+
elsif assembly.is_a?(Agent)
|
|
268
307
|
assembly
|
|
269
308
|
elsif assembly.run
|
|
270
309
|
assembly.send(:build_tool_assembly)
|
|
@@ -293,7 +332,7 @@ module LittleGhost
|
|
|
293
332
|
retained_history.replace(result.messages.reject { |message| message.role == :system }) if preserve_context
|
|
294
333
|
result.structured? ? result.structured_result.value : result.text
|
|
295
334
|
ensure
|
|
296
|
-
target&.close
|
|
335
|
+
target&.close if factory_target || !target.equal?(assembly)
|
|
297
336
|
end
|
|
298
337
|
preserve_context ? mutex.synchronize(&invocation) : invocation.call
|
|
299
338
|
end
|
|
@@ -308,6 +347,8 @@ module LittleGhost
|
|
|
308
347
|
))
|
|
309
348
|
end
|
|
310
349
|
|
|
350
|
+
public
|
|
351
|
+
|
|
311
352
|
# Adds an interjection to the single active leaf Agent.
|
|
312
353
|
def interject(message, **options)
|
|
313
354
|
child = @assembly_mutex.synchronize do
|
|
@@ -377,7 +418,7 @@ module LittleGhost
|
|
|
377
418
|
end
|
|
378
419
|
|
|
379
420
|
def build_tool_assembly
|
|
380
|
-
child = runtime.build_assembly(self.class, run:, agent_stream_path:)
|
|
421
|
+
child = runtime.build_assembly(@assembly_definition || self.class, run:, agent_stream_path:)
|
|
381
422
|
child.bind_agent_stream_path(agent_stream_path)
|
|
382
423
|
child
|
|
383
424
|
end
|
|
@@ -123,7 +123,7 @@ module LittleGhost
|
|
|
123
123
|
snapshot = definition
|
|
124
124
|
return (runtime || run.runtime).build_assembly(snapshot, run:) if run
|
|
125
125
|
|
|
126
|
-
snapshot.implementation.new(runtime:)
|
|
126
|
+
snapshot.implementation.new(runtime:).bind_assembly_definition(snapshot)
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
# Executes the current declarations and returns their Run.
|
|
@@ -133,7 +133,7 @@ module LittleGhost
|
|
|
133
133
|
def stream_ask(message, **options)
|
|
134
134
|
snapshot = definition
|
|
135
135
|
Enumerator.new do |events|
|
|
136
|
-
snapshot.implementation.new(runtime:).stream_ask(message, **options).each { |event| events << event }
|
|
136
|
+
snapshot.implementation.new(runtime:).bind_assembly_definition(snapshot).stream_ask(message, **options).each { |event| events << event }
|
|
137
137
|
end
|
|
138
138
|
end
|
|
139
139
|
|
|
@@ -263,7 +263,7 @@ module LittleGhost
|
|
|
263
263
|
class AgentBuilder < AssemblyBuilder
|
|
264
264
|
DECLARATIONS = %i[
|
|
265
265
|
model limits result_schema capture_diagnostics system_template system_prompt
|
|
266
|
-
tools
|
|
266
|
+
tools after_initialize before_invocation after_invocation
|
|
267
267
|
before_model after_model after_model_error before_tool after_tool
|
|
268
268
|
manage_context detect_tool_loops skills subagent subagents
|
|
269
269
|
subagent_long_poll_duration agent_as_tool agents_as_tools
|