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.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -6
  3. data/docs/guides/assemblies.md +21 -9
  4. data/docs/guides/code_mode.md +2 -2
  5. data/docs/guides/getting_started.md +27 -2
  6. data/docs/guides/integrations.md +20 -161
  7. data/docs/guides/mcp.md +288 -0
  8. data/docs/guides/models_and_providers.md +21 -54
  9. data/docs/guides/production.md +14 -5
  10. data/docs/guides/prompt_views.md +234 -27
  11. data/docs/guides/providers.md +275 -0
  12. data/docs/guides/sandboxing.md +42 -7
  13. data/docs/guides/skills.md +1 -3
  14. data/docs/guides/tools.md +2 -0
  15. data/exe/little_ghost +12 -0
  16. data/lib/little_ghost/agent/context_management.rb +2 -13
  17. data/lib/little_ghost/agent/delegation.rb +7 -4
  18. data/lib/little_ghost/agent/skills.rb +8 -6
  19. data/lib/little_ghost/agent/tool_loop.rb +4 -4
  20. data/lib/little_ghost/agent.rb +230 -70
  21. data/lib/little_ghost/agent_builder.rb +16 -4
  22. data/lib/little_ghost/artifacts/workspace_store.rb +16 -14
  23. data/lib/little_ghost/assembly.rb +48 -7
  24. data/lib/little_ghost/assembly_builder.rb +3 -3
  25. data/lib/little_ghost/cli.rb +429 -0
  26. data/lib/little_ghost/code_mode/broker.rb +11 -3
  27. data/lib/little_ghost/code_mode/javascript/client.rb +58 -11
  28. data/lib/little_ghost/code_mode/javascript/host.rb +37 -13
  29. data/lib/little_ghost/code_mode/javascript/session.rb +21 -9
  30. data/lib/little_ghost/code_mode/javascript_engine.rb +21 -42
  31. data/lib/little_ghost/code_mode/ruby/host.rb +8 -4
  32. data/lib/little_ghost/code_mode/ruby/session.rb +41 -14
  33. data/lib/little_ghost/code_mode/ruby_engine.rb +6 -36
  34. data/lib/little_ghost/code_mode/runtime.rb +57 -24
  35. data/lib/little_ghost/console.rb +17 -0
  36. data/lib/little_ghost/errors.rb +20 -2
  37. data/lib/little_ghost/framework_prompts.rb +376 -0
  38. data/lib/little_ghost/generators/application_generator.rb +179 -0
  39. data/lib/little_ghost/generators/templates/application/Gemfile.tt +3 -0
  40. data/lib/little_ghost/generators/templates/application/README.md.tt +29 -0
  41. data/lib/little_ghost/generators/templates/application/app/agents/application_agent.rb.tt +5 -0
  42. data/lib/little_ghost/generators/templates/application/app/prompts/application/system_prompt.erb.tt +1 -0
  43. data/lib/little_ghost/generators/templates/application/bin/application.tt +10 -0
  44. data/lib/little_ghost/generators/templates/application/config/little_ghost.rb.tt +5 -0
  45. data/lib/little_ghost/generators/templates/application/gitignore.tt +2 -0
  46. data/lib/little_ghost/generators/templates/application/keep.tt +0 -0
  47. data/lib/little_ghost/graph.rb +24 -5
  48. data/lib/little_ghost/mcp/client.rb +307 -610
  49. data/lib/little_ghost/mcp/toolset.rb +133 -95
  50. data/lib/little_ghost/mcp.rb +21 -3
  51. data/lib/little_ghost/model_operations.rb +103 -36
  52. data/lib/little_ghost/model_resolver.rb +2 -0
  53. data/lib/little_ghost/models/catalog/models_dev_source.rb +4 -1
  54. data/lib/little_ghost/prompt_resolver.rb +61 -27
  55. data/lib/little_ghost/prompts/little_ghost/agent/interjections/instructions.erb +12 -0
  56. data/lib/little_ghost/prompts/little_ghost/agent/system/default.erb +10 -0
  57. data/lib/little_ghost/prompts/little_ghost/artifacts/batch/feedback/batch_too_large.erb +10 -0
  58. data/lib/little_ghost/prompts/little_ghost/artifacts/batch/feedback/batch_too_many.erb +10 -0
  59. data/lib/little_ghost/prompts/little_ghost/artifacts/batch/feedback/item_too_large.erb +10 -0
  60. data/lib/little_ghost/prompts/little_ghost/artifacts/errors/preparation_failed.erb +10 -0
  61. data/lib/little_ghost/prompts/little_ghost/artifacts/errors/resolver_invalid.erb +10 -0
  62. data/lib/little_ghost/prompts/little_ghost/artifacts/errors/value_unstorable.erb +10 -0
  63. data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/format/reference.erb +13 -0
  64. data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/format/references.erb +12 -0
  65. data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/format/workspace_references.erb +12 -0
  66. data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/notices/full_result.erb +14 -0
  67. data/lib/little_ghost/prompts/little_ghost/artifacts/presentation/notices/storage_failed.erb +13 -0
  68. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/destination_unsafe.erb +10 -0
  69. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/secure_unavailable.erb +10 -0
  70. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/workspace_changed.erb +10 -0
  71. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/workspace_unavailable.erb +10 -0
  72. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/errors/workspace_unsafe.erb +10 -0
  73. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/feedback/count_limit.erb +10 -0
  74. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/feedback/destination_exists.erb +10 -0
  75. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/feedback/too_large.erb +10 -0
  76. data/lib/little_ghost/prompts/little_ghost/artifacts/storage/feedback/total_limit.erb +10 -0
  77. data/lib/little_ghost/prompts/little_ghost/assembly/feedback/transition_only.erb +10 -0
  78. data/lib/little_ghost/prompts/little_ghost/assembly/tools/fallback/description.erb +10 -0
  79. data/lib/little_ghost/prompts/little_ghost/code_mode/artifacts/format/references.erb +12 -0
  80. data/lib/little_ghost/prompts/little_ghost/code_mode/errors/brokered_close.erb +10 -0
  81. data/lib/little_ghost/prompts/little_ghost/code_mode/errors/cleanup_failed.erb +10 -0
  82. data/lib/little_ghost/prompts/little_ghost/code_mode/errors/execution_failed.erb +11 -0
  83. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/arguments_object.erb +10 -0
  84. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/closed.erb +10 -0
  85. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/control_active.erb +10 -0
  86. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/limit_exceeded.erb +10 -0
  87. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/not_active.erb +10 -0
  88. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/program_active.erb +10 -0
  89. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/program_timed_out.erb +10 -0
  90. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/unavailable_tool.erb +10 -0
  91. data/lib/little_ghost/prompts/little_ghost/code_mode/feedback/unknown_program.erb +10 -0
  92. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/active_programs_limit.erb +10 -0
  93. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/cleanup_failed.erb +10 -0
  94. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/execution_limit.erb +10 -0
  95. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/invalid_request.erb +10 -0
  96. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/memory_limit.erb +10 -0
  97. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/pending_calls_limit.erb +10 -0
  98. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/source_size_limit.erb +10 -0
  99. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/errors/tools_array.erb +10 -0
  100. data/lib/little_ghost/prompts/little_ghost/code_mode/javascript/instructions.erb +35 -0
  101. data/lib/little_ghost/prompts/little_ghost/code_mode/ruby/feedback/parallel_callables.erb +10 -0
  102. data/lib/little_ghost/prompts/little_ghost/code_mode/ruby/instructions.erb +40 -0
  103. data/lib/little_ghost/prompts/little_ghost/code_mode/tools/exec/description.erb +10 -0
  104. data/lib/little_ghost/prompts/little_ghost/code_mode/tools/stop/description.erb +10 -0
  105. data/lib/little_ghost/prompts/little_ghost/code_mode/tools/stop/inputs/max_output_tokens/description.erb +10 -0
  106. data/lib/little_ghost/prompts/little_ghost/code_mode/tools/wait/description.erb +10 -0
  107. data/lib/little_ghost/prompts/little_ghost/code_mode/tools/wait/inputs/max_output_tokens/description.erb +10 -0
  108. data/lib/little_ghost/prompts/little_ghost/context_management/summary/request.erb +10 -0
  109. data/lib/little_ghost/prompts/little_ghost/context_management/summary/system_instruction.erb +16 -0
  110. data/lib/little_ghost/prompts/little_ghost/graph/context/format/failed_input.erb +11 -0
  111. data/lib/little_ghost/prompts/little_ghost/graph/context/format/previous_input.erb +13 -0
  112. data/lib/little_ghost/prompts/little_ghost/graph/context/headings/original_task.erb +10 -0
  113. data/lib/little_ghost/prompts/little_ghost/graph/context/headings/previous_inputs.erb +10 -0
  114. data/lib/little_ghost/prompts/little_ghost/mcp/errors/request_failed.erb +10 -0
  115. data/lib/little_ghost/prompts/little_ghost/mcp/errors/transformation_unserializable.erb +10 -0
  116. data/lib/little_ghost/prompts/little_ghost/mcp/tools/fallback/description.erb +10 -0
  117. data/lib/little_ghost/prompts/little_ghost/output/truncation/marker.erb +11 -0
  118. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/errors/mount_changed.erb +10 -0
  119. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/errors/secure_traversal_unavailable.erb +10 -0
  120. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/errors/workspace_root_changed.erb +10 -0
  121. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/directory_listing_limit.erb +10 -0
  122. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/entry_required.erb +10 -0
  123. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/file_read_limit.erb +10 -0
  124. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/invalid_utf8.erb +10 -0
  125. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/mount_escape.erb +10 -0
  126. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/mount_missing.erb +10 -0
  127. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/multiply_linked.erb +10 -0
  128. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/not_directory.erb +10 -0
  129. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/not_file.erb +10 -0
  130. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/null_byte.erb +10 -0
  131. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/outside_scope.erb +10 -0
  132. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/parent_escape.erb +10 -0
  133. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/path_missing.erb +10 -0
  134. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/path_traverses_symlink.erb +10 -0
  135. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/read_only.erb +10 -0
  136. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/replace_empty.erb +10 -0
  137. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/symlink_path.erb +10 -0
  138. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/text_multiple.erb +10 -0
  139. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/text_not_found.erb +10 -0
  140. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/write_limit.erb +10 -0
  141. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/write_parent_missing.erb +10 -0
  142. data/lib/little_ghost/prompts/little_ghost/sandbox/filesystem/feedback/write_target_symlink.erb +10 -0
  143. data/lib/little_ghost/prompts/little_ghost/sandbox/policy/feedback/feature_denied.erb +10 -0
  144. data/lib/little_ghost/prompts/little_ghost/sandbox/process/errors/program_supervisor_failed.erb +10 -0
  145. data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/command_timed_out.erb +10 -0
  146. data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/executable_required.erb +10 -0
  147. data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/program_memory_exceeded.erb +10 -0
  148. data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/program_output_exceeded.erb +10 -0
  149. data/lib/little_ghost/prompts/little_ghost/sandbox/process/feedback/program_timed_out.erb +10 -0
  150. data/lib/little_ghost/prompts/little_ghost/skills/discovery/instructions.erb +18 -0
  151. data/lib/little_ghost/prompts/little_ghost/skills/feedback/unknown.erb +11 -0
  152. data/lib/little_ghost/prompts/little_ghost/skills/resources/notices/truncated.erb +10 -0
  153. data/lib/little_ghost/prompts/little_ghost/skills/tools/activate/content.erb +28 -0
  154. data/lib/little_ghost/prompts/little_ghost/skills/tools/activate/description.erb +10 -0
  155. data/lib/little_ghost/prompts/little_ghost/skills/tools/activate/inputs/name/description.erb +10 -0
  156. data/lib/little_ghost/prompts/little_ghost/structured_output/persistence/format/redaction.erb +12 -0
  157. data/lib/little_ghost/prompts/little_ghost/structured_output/repair/feedback/invalid_result.erb +11 -0
  158. data/lib/little_ghost/prompts/little_ghost/structured_output/repair/feedback/schema_errors.erb +11 -0
  159. data/lib/little_ghost/prompts/little_ghost/structured_output/repair/request.erb +14 -0
  160. data/lib/little_ghost/prompts/little_ghost/structured_output/tools/result/description.erb +11 -0
  161. data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/invalid_json.erb +11 -0
  162. data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/missing_tool.erb +11 -0
  163. data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/multiple_tools.erb +11 -0
  164. data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/too_complex.erb +11 -0
  165. data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/too_deep.erb +11 -0
  166. data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/too_large.erb +11 -0
  167. data/lib/little_ghost/prompts/little_ghost/structured_output/validation/feedback/tool_not_exclusive.erb +11 -0
  168. data/lib/little_ghost/prompts/little_ghost/subagents/errors/create_failed.erb +10 -0
  169. data/lib/little_ghost/prompts/little_ghost/subagents/errors/previous_turn_failed.erb +10 -0
  170. data/lib/little_ghost/prompts/little_ghost/subagents/errors/restore_failed.erb +10 -0
  171. data/lib/little_ghost/prompts/little_ghost/subagents/errors/turn_failed.erb +10 -0
  172. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/duplicate_ids.erb +10 -0
  173. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/duplicate_path.erb +10 -0
  174. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/identity_capacity.erb +10 -0
  175. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/inactive.erb +10 -0
  176. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/interjection_chars_limit.erb +10 -0
  177. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/interjection_limit.erb +10 -0
  178. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/interjection_unsupported.erb +10 -0
  179. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/invalid_cursor.erb +10 -0
  180. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/invalid_mode.erb +10 -0
  181. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/list_limit.erb +10 -0
  182. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/message_limit.erb +10 -0
  183. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/message_type.erb +10 -0
  184. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/not_running.erb +10 -0
  185. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/queue_capacity.erb +10 -0
  186. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/task_name_invalid.erb +10 -0
  187. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/task_name_limit.erb +10 -0
  188. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/terminal_identity.erb +11 -0
  189. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/turn_capacity.erb +10 -0
  190. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/unknown_id.erb +10 -0
  191. data/lib/little_ghost/prompts/little_ghost/subagents/feedback/unknown_kind.erb +10 -0
  192. data/lib/little_ghost/prompts/little_ghost/subagents/notices/turn_cancelled.erb +10 -0
  193. data/lib/little_ghost/prompts/little_ghost/subagents/tools/interject/description.erb +10 -0
  194. data/lib/little_ghost/prompts/little_ghost/subagents/tools/interject/inputs/active_id/description.erb +10 -0
  195. data/lib/little_ghost/prompts/little_ghost/subagents/tools/interject/inputs/message/description.erb +10 -0
  196. data/lib/little_ghost/prompts/little_ghost/subagents/tools/list/description.erb +10 -0
  197. data/lib/little_ghost/prompts/little_ghost/subagents/tools/send/description.erb +10 -0
  198. data/lib/little_ghost/prompts/little_ghost/subagents/tools/send/inputs/id/description.erb +10 -0
  199. data/lib/little_ghost/prompts/little_ghost/subagents/tools/send/inputs/message/description.erb +10 -0
  200. data/lib/little_ghost/prompts/little_ghost/subagents/tools/send/inputs/send_mode/description.erb +10 -0
  201. data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/description.erb +10 -0
  202. data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/inputs/kind/description.erb +11 -0
  203. data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/inputs/spawn_mode/description.erb +10 -0
  204. data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/inputs/task/description.erb +10 -0
  205. data/lib/little_ghost/prompts/little_ghost/subagents/tools/spawn/inputs/task_name/description.erb +10 -0
  206. data/lib/little_ghost/prompts/little_ghost/subagents/tools/wait/description.erb +10 -0
  207. data/lib/little_ghost/prompts/little_ghost/subagents/tools/wait/inputs/ids/description.erb +10 -0
  208. data/lib/little_ghost/prompts/little_ghost/swarm/handoff/notice.erb +10 -0
  209. data/lib/little_ghost/prompts/little_ghost/swarm/handoff/request.erb +16 -0
  210. data/lib/little_ghost/prompts/little_ghost/swarm/tools/handoff/description.erb +10 -0
  211. data/lib/little_ghost/prompts/little_ghost/tools/built_in/list_files/description.erb +10 -0
  212. data/lib/little_ghost/prompts/little_ghost/tools/built_in/read_file/description.erb +10 -0
  213. data/lib/little_ghost/prompts/little_ghost/tools/built_in/replace_in_file/description.erb +10 -0
  214. data/lib/little_ghost/prompts/little_ghost/tools/built_in/shell/description.erb +10 -0
  215. data/lib/little_ghost/prompts/little_ghost/tools/built_in/write_file/description.erb +10 -0
  216. data/lib/little_ghost/prompts/little_ghost/tools/built_in/write_todos/description.erb +10 -0
  217. data/lib/little_ghost/prompts/little_ghost/tools/built_in/write_todos/feedback/single_progress.erb +10 -0
  218. data/lib/little_ghost/prompts/little_ghost/tools/built_in/write_todos/feedback/unique_ids.erb +10 -0
  219. data/lib/little_ghost/prompts/little_ghost/tools/errors/unexpected_failure.erb +10 -0
  220. data/lib/little_ghost/prompts/little_ghost/tools/errors/unserializable_result.erb +10 -0
  221. data/lib/little_ghost/prompts/little_ghost/tools/feedback/invalid_input.erb +10 -0
  222. data/lib/little_ghost/prompts/little_ghost/tools/feedback/unknown.erb +10 -0
  223. data/lib/little_ghost/prompts/little_ghost/tools/loop/notices/final_warning.erb +10 -0
  224. data/lib/little_ghost/prompts/little_ghost/tools/loop/notices/termination.erb +10 -0
  225. data/lib/little_ghost/prompts/little_ghost/tools/loop/notices/warning.erb +10 -0
  226. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/additional_property.erb +10 -0
  227. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/enum.erb +11 -0
  228. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/invalid_format.erb +10 -0
  229. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/invalid_pattern.erb +10 -0
  230. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/max_items.erb +11 -0
  231. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/max_length.erb +11 -0
  232. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/maximum.erb +11 -0
  233. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/min_items.erb +11 -0
  234. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/min_length.erb +11 -0
  235. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/minimum.erb +11 -0
  236. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/required.erb +10 -0
  237. data/lib/little_ghost/prompts/little_ghost/tools/validation/schema/type.erb +11 -0
  238. data/lib/little_ghost/prompts/little_ghost/workspace/feedback/path_changed.erb +10 -0
  239. data/lib/little_ghost/provider_registry.rb +1 -0
  240. data/lib/little_ghost/providers/lm_studio/catalog_source.rb +201 -0
  241. data/lib/little_ghost/providers/lm_studio.rb +52 -0
  242. data/lib/little_ghost/providers/openai.rb +3 -75
  243. data/lib/little_ghost/providers/openai_compatible.rb +78 -1
  244. data/lib/little_ghost/runtime/hooks/artifacts.rb +58 -22
  245. data/lib/little_ghost/runtime.rb +8 -3
  246. data/lib/little_ghost/sandbox/environment_policy.rb +29 -4
  247. data/lib/little_ghost/sandbox/filesystem.rb +51 -33
  248. data/lib/little_ghost/sandbox/policy.rb +28 -6
  249. data/lib/little_ghost/sandbox/process_runner.rb +6 -2
  250. data/lib/little_ghost/sandbox/process_session.rb +8 -4
  251. data/lib/little_ghost/sandbox/scope.rb +4 -1
  252. data/lib/little_ghost/sandboxes/seatbelt.rb +3 -1
  253. data/lib/little_ghost/sandboxes/unrestricted.rb +17 -9
  254. data/lib/little_ghost/skills/catalog.rb +41 -36
  255. data/lib/little_ghost/structured_output.rb +6 -6
  256. data/lib/little_ghost/subagents/control_tool.rb +58 -0
  257. data/lib/little_ghost/subagents/manager.rb +99 -74
  258. data/lib/little_ghost/support/output_truncation.rb +28 -4
  259. data/lib/little_ghost/swarm.rb +20 -7
  260. data/lib/little_ghost/tool.rb +47 -20
  261. data/lib/little_ghost/tool_registry.rb +2 -2
  262. data/lib/little_ghost/tools/filesystem.rb +12 -4
  263. data/lib/little_ghost/tools/shell.rb +5 -1
  264. data/lib/little_ghost/tools/write_todos.rb +9 -9
  265. data/lib/little_ghost/version.rb +1 -1
  266. data/lib/little_ghost/workflow.rb +199 -27
  267. data/lib/little_ghost/workspace.rb +2 -2
  268. data/lib/little_ghost.rb +6 -2
  269. metadata +222 -7
  270. data/lib/little_ghost/mcp/types.rb +0 -216
@@ -1,8 +1,9 @@
1
1
  # Prompts as Views
2
2
 
3
- A short prompt fits nicely inside an Agent class. As the instructions grow, move them into a **prompt view**: an ERB file that LittleGhost finds and renders for the Agent.
4
-
5
- This keeps the Agent definition focused. It also gives shared instructions and application values a natural home.
3
+ Keep growing Agent instructions readable by moving them into a **prompt view**:
4
+ an ERB file that LittleGhost finds and renders for the Agent. The Agent prepares
5
+ any application values the prompt needs, while the view holds the instructions
6
+ sent to the model.
6
7
 
7
8
  ## Start with the inline prompt
8
9
 
@@ -28,7 +29,7 @@ class CustomerSupportAgent < LittleGhost::Agent
28
29
  end
29
30
  ```
30
31
 
31
- Then create `app/prompts/customer_support/system.erb`:
32
+ Then create `app/prompts/customer_support/system_prompt.erb`:
32
33
 
33
34
  ```erb
34
35
  You help customers understand their orders and account.
@@ -38,57 +39,115 @@ Never invent company guidance. Check the help center when policy matters.
38
39
  Use the order status tool before making a claim about a private order.
39
40
  ```
40
41
 
41
- That is enough. `CustomerSupportAgent` becomes `customer_support`, so LittleGhost looks for `customer_support/system.erb` under `app/prompts`.
42
+ That is enough. LittleGhost turns `CustomerSupportAgent` into the logical path
43
+ `customer_support` and looks for
44
+ `app/prompts/customer_support/system_prompt.erb`.
42
45
 
43
- The prompt is still a system instruction sent to the selected model provider. Keeping it in a view improves organization; it does not keep the content inside your process.
46
+ The prompt is still a system instruction sent to the selected model provider.
47
+ Keeping it in a view improves organization; it does not keep the content inside
48
+ your process.
44
49
 
45
- ## Give the view application values
50
+ ## Prepare application values with Ruby
46
51
 
47
- Use `prompt_local` for a value the application owns:
52
+ Define `system_prompt` as an instance method when the view needs values from
53
+ your application. Assign those values to instance variables:
48
54
 
49
55
  ```ruby
50
56
  class CustomerSupportAgent < LittleGhost::Agent
51
- prompt_local :company_name, "Northstar"
57
+ def system_prompt
58
+ @company_name = "Northstar"
59
+ @policy_version = SupportPolicy.current_version
60
+ end
52
61
  end
53
62
  ```
54
63
 
55
- The local is available by name in the view:
64
+ LittleGhost calls the method before it renders the view. The instance variables
65
+ are available there by the same names:
56
66
 
57
67
  ```erb
58
- You are a customer support agent for <%= company_name %>.
68
+ You are a customer support agent for <%= @company_name %>.
69
+ Follow support policy <%= @policy_version %>.
59
70
  Answer clearly and concisely.
60
71
  ```
61
72
 
62
- A block can resolve a trusted value for each Agent instance. Add it to the Agent class too:
73
+ If you know Rails controllers, the pairing is familiar: the method prepares
74
+ values, and the ERB file presents them. The method's return value is ignored. If
75
+ it raises an exception, the Run fails in the usual way.
76
+
77
+ An inline `system_prompt` declaration still takes precedence over a view. Remove
78
+ the inline declaration when you want LittleGhost to call the instance method and
79
+ render `system_prompt.erb`.
80
+
81
+ Subclass actions can call `super` before adding their own values:
63
82
 
64
83
  ```ruby
65
- class CustomerSupportAgent < LittleGhost::Agent
66
- prompt_local(:policy_version) { SupportPolicy.current_version }
84
+ class BillingSupportAgent < CustomerSupportAgent
85
+ def system_prompt
86
+ super
87
+ @billing_region = "US"
88
+ end
67
89
  end
68
90
  ```
69
91
 
70
- Prompt views also receive `invocation`, `run`, and `agent`. Reach for those when the instruction truly depends on the current request. Keep user wording in the caller message unless you deliberately want it inside the system instruction.
92
+ Prompt views also receive three built-in local variables: `invocation` for the
93
+ current request, `run` for its lifecycle, and `agent` for the run-scoped Agent.
94
+ Use them when an instruction genuinely depends on the current request. Keep the
95
+ customer's wording in the caller message unless it belongs in the system
96
+ instruction.
71
97
 
72
- Every rendered value may be sent to the model provider. Pass only data that belongs in the prompt.
98
+ Every rendered value may be sent to the model provider. Put only data that
99
+ belongs in the prompt into the view.
73
100
 
74
101
  ## Share a small partial
75
102
 
76
103
  Partials keep repeated instructions in one place. Create `app/prompts/shared/_voice.erb`:
77
104
 
78
105
  ```erb
79
- Use a warm, direct voice for <%= company_name %>.
106
+ Use a warm, direct voice for <%= @company_name %>.
80
107
  Prefer one clear next step over a long list of possibilities.
81
108
  ```
82
109
 
83
110
  Render it from the Agent's system view:
84
111
 
85
112
  ```erb
86
- You are a customer support agent for <%= company_name %>.
113
+ You are a customer support agent for <%= @company_name %>.
114
+
115
+ <%= partial "shared/voice" %>
116
+ ```
117
+
118
+ The underscore marks a partial. Partials share the Agent's instance variables,
119
+ so `@company_name` remains available without extra wiring.
120
+
121
+ Use an explicit local for a value that belongs only to one partial. For example,
122
+ create `app/prompts/shared/_closing.erb`:
123
+
124
+ ```erb
125
+ Offer one clear next step for <%= audience %>.
126
+ ```
127
+
128
+ Then pass `audience` when you render it:
87
129
 
88
- <%= partial "shared/voice", locals: {company_name: company_name} %>
130
+ ```erb
131
+ <%= partial "shared/closing", locals: {audience: "account owners"} %>
89
132
  ```
90
133
 
91
- The underscore marks a partial. Its locals are explicit, so it does not quietly inherit everything available to the parent view.
134
+ Locals do not flow into nested partials unless you pass them again.
135
+
136
+ ## Keep repeated renders predictable
137
+
138
+ LittleGhost calls `system_prompt` once for each file-backed prompt render. Each
139
+ call starts with the instance variables captured after LittleGhost's
140
+ `after_initialize` callbacks finish. Values left behind by a later callback,
141
+ Tool, or earlier render do not become prompt assigns by accident.
142
+
143
+ After preparing the view, LittleGhost restores the Agent's live instance
144
+ variables. The objects assigned to the view are still ordinary Ruby objects,
145
+ though, so the view sees the same mutable objects. Treat them as read-only, or
146
+ duplicate a value before assigning it when the template needs an isolated copy.
147
+
148
+ For state that should change across invocations, read from `run`, a session, or
149
+ an application object in `system_prompt` instead of relying on an earlier
150
+ prompt render.
92
151
 
93
152
  ## Choose a different template path
94
153
 
@@ -104,10 +163,154 @@ LittleGhost chooses one prompt source in this order:
104
163
 
105
164
  1. An inline `system_prompt`
106
165
  2. An explicit `system_template`
107
- 3. The Agent's conventional `system.erb` view
166
+ 3. The Agent's conventional `system_prompt.erb` view
108
167
 
109
168
  Applications can add prompt lookup roots through `Configuration#prompt_paths`.
110
- Earlier roots win, which lets application code override a shared prompt package.
169
+ Earlier roots win, so an application can override a view from a shared prompt
170
+ package.
171
+
172
+ ## Customize LittleGhost's framework prompts
173
+
174
+ You can change LittleGhost's own model-facing language without replacing its
175
+ Ruby behavior. Tool descriptions, structured-result repair requests, context
176
+ summaries, code-mode instructions, and other framework messages all come from
177
+ bundled prompt views under the `little_ghost/` namespace.
178
+
179
+ ### Decide when to customize
180
+
181
+ Models can respond differently to the same instruction, even when the Tool,
182
+ schema, and task stay the same. Wording that works well with one model may lead
183
+ another model to miss a Tool constraint, mishandle a repair request, or recover
184
+ less reliably from a failed call. Adjusting the relevant framework prompt gives
185
+ you a direct way to improve that interaction without forking LittleGhost.
186
+
187
+ An override can also align framework language with your application's
188
+ terminology and voice. Start with the narrowest template that covers the
189
+ behavior you want to change, and evaluate it with the models your application
190
+ uses. Keep protocol checks, authorization, and execution limits in Ruby;
191
+ prompt wording guides the model but does not enforce those boundaries.
192
+
193
+ ### Find the prompt you want to change
194
+
195
+ Browse the catalog from your application:
196
+
197
+ ```sh
198
+ $ little_ghost prompts list
199
+ $ little_ghost prompts list structured_output
200
+ $ little_ghost prompts list structured_output/repair
201
+ $ little_ghost prompts show structured_output/repair/request
202
+ ```
203
+
204
+ The bare `list` command shows top-level features with template counts. Pass a
205
+ feature or component path to browse its immediate children, or use `list --all`
206
+ to print every template key. Keys follow `feature/component/purpose`. For
207
+ example, built-in Tool descriptions live under `tools/built_in`, while JSON
208
+ Schema feedback lives under `tools/validation/schema`. Directories named
209
+ `feedback` contain conditions the model can correct. Directories named `errors`
210
+ contain operational failures.
211
+
212
+ `show` prints the bundled ERB source. Every bundled template starts with a
213
+ structured ERB comment that explains where LittleGhost renders it, its locals,
214
+ and any formatting constraints. These headers use `<%# ... -%>`, so neither the
215
+ comment nor its trailing newline enters the rendered prompt. Each interior line
216
+ also starts with `#` so Ruby-aware editors recognize the whole header as a
217
+ comment. A `#` line outside `<%# ... -%>` and an HTML comment are output text and
218
+ would be sent to the model.
219
+
220
+ Bundled prompts use plain text and Markdown. When a prompt needs headings or
221
+ structured sections, the default template uses Markdown headings and lists
222
+ rather than XML-like wrapper tags.
223
+
224
+ ### Copy an override into your application
225
+
226
+ Copy one template into the default application prompt root before editing it:
227
+
228
+ ```sh
229
+ $ little_ghost prompts copy structured_output/repair/request
230
+ ```
231
+
232
+ That creates
233
+ `app/prompts/little_ghost/structured_output/repair/request.erb`. Use `--root` when the
234
+ application configures a different prompt root:
235
+
236
+ ```sh
237
+ $ little_ghost prompts copy structured_output/repair/request --root config/prompts
238
+ ```
239
+
240
+ Use `--agent` when one Agent needs different wording from the rest of the
241
+ application. Pass the Agent's logical path, which is its underscored class name
242
+ without the `Agent` suffix:
243
+
244
+ ```sh
245
+ $ little_ghost prompts copy structured_output/repair/request --agent customer_support
246
+ ```
247
+
248
+ That creates
249
+ `app/prompts/customer_support/little_ghost/structured_output/repair/request.erb`.
250
+ Namespaced classes use a namespaced logical path, such as
251
+ `Admin::CustomerSupportAgent` becoming `admin/customer_support`.
252
+
253
+ Copy the complete catalog when an application deliberately wants to own every
254
+ piece of framework wording:
255
+
256
+ ```sh
257
+ $ little_ghost prompts copy --all
258
+ ```
259
+
260
+ Copy commands refuse to overwrite any existing destination. With `--all`, the
261
+ command checks every destination before it starts writing. Copied templates
262
+ retain their invisible documentation headers so the override remains
263
+ self-explanatory in the application.
264
+
265
+ ### Choose the override scope
266
+
267
+ For every framework prompt that applies to an Agent invocation, LittleGhost
268
+ uses the first matching template in this order:
269
+
270
+ 1. An invocation-specific `TrustedPath` root
271
+ 2. The Agent-scoped directory inside a configured prompt root
272
+ 3. The runtime-wide directory inside a configured prompt root
273
+ 4. The template bundled with LittleGhost
274
+
275
+ Each root keeps the same `little_ghost/<key>.erb` namespace. Agent scope inserts
276
+ the logical path before that namespace.
277
+
278
+ Runtime-wide overrides normally live in the default `app/prompts` root. Add or
279
+ replace configured roots when the application uses another location:
280
+
281
+ ```ruby
282
+ LittleGhost.configure do |config|
283
+ config.prompt_paths = ["config/prompts", "app/prompts"]
284
+ end
285
+ ```
286
+
287
+ The uncommon invocation-specific layer is explicit because it can change model
288
+ instructions for only one call. From `config/little_ghost.rb`:
289
+
290
+ ```ruby
291
+ campaign_prompts = LittleGhost::TrustedPath.new(
292
+ path: File.expand_path("campaign_prompts", __dir__)
293
+ )
294
+
295
+ CustomerSupportAgent.ask(
296
+ "Where is my order?",
297
+ template_paths: [campaign_prompts]
298
+ )
299
+ ```
300
+
301
+ The invocation root contains
302
+ `little_ghost/structured_output/repair/request.erb`, as a runtime root does.
303
+ Direct `LittleGhost.generate` calls accept the same `template_paths:` option.
304
+
305
+ ### Know what an override can change
306
+
307
+ Framework templates control model-facing language, not protocol roles, Tool
308
+ names, schemas, validation behavior, security checks, or execution limits. A
309
+ required template cannot render as blank.
310
+
311
+ LittleGhost is pre-1.0. Template keys and their documented locals are maintained
312
+ on a best-effort basis until 1.0, so review release notes and copied overrides
313
+ when upgrading.
111
314
 
112
315
  ## Treat views as application code
113
316
 
@@ -115,13 +318,16 @@ Prompt views run as ERB inside the Ruby process and can call Ruby. Keep prompt
115
318
  directories with the rest of your application code rather than letting a
116
319
  request choose one.
117
320
 
118
- `TrustedPath` is available for the uncommon case where application code selects
119
- a request-specific root. It marks that choice explicitly; it does not inspect
120
- or restrict the directory.
321
+ Use `TrustedPath` only when application code needs to select a request-specific
322
+ root. Do not derive its path from a request parameter, model output, or Tool
323
+ argument. The wrapper confirms that the directory exists and resolves symbolic
324
+ links. It does not inspect who can edit the directory or restrict what its ERB
325
+ can execute.
121
326
 
122
327
  ## Build request-specific input in a Workflow
123
328
 
124
- A prompt view defines reusable instructions for one Agent. A Workflow may still build request-specific input for that Agent:
329
+ A prompt view defines reusable instructions for one Agent. A Workflow may still
330
+ build request-specific input for that Agent:
125
331
 
126
332
  ```ruby
127
333
  invoke CustomerSupportAgent, input: <<~MESSAGE
@@ -132,7 +338,8 @@ invoke CustomerSupportAgent, input: <<~MESSAGE
132
338
  MESSAGE
133
339
  ```
134
340
 
135
- The Workflow is composing this request. `CustomerSupportAgent` still receives its own system prompt view when it runs.
341
+ The Workflow is composing this request. `CustomerSupportAgent` still receives
342
+ its own system prompt view when it runs.
136
343
 
137
344
  Continue with [Tools](tools.md) to give those Agents application capabilities
138
345
  while keeping model input, trusted context, and delegated sandbox operations
@@ -0,0 +1,275 @@
1
+ # Provider Support
2
+
3
+ Use this page to connect a hosted provider or local model server. If you are
4
+ still deciding how Agents should choose models, start with [Models and
5
+ Providers](models_and_providers.md).
6
+
7
+ ## Compare provider support
8
+
9
+ Every connection uses the same configuration shape:
10
+
11
+ ```ruby
12
+ LittleGhost.configure do |config|
13
+ config.providers = {
14
+ primary: {
15
+ adapter: :openrouter,
16
+ api_key: ENV.fetch("OPENROUTER_API_KEY")
17
+ }
18
+ }
19
+ end
20
+ ```
21
+
22
+ `primary` is a name chosen by the application, and `adapter` selects the wire
23
+ protocol. An Agent can now use a target such as
24
+ `primary:openai/gpt-5.6-luna`.
25
+
26
+ | Provider or endpoint | Adapter | Generation | Embeddings |
27
+ | --- | --- | --- | --- |
28
+ | OpenRouter | `:openrouter` | Chat Completions | When the model supports it |
29
+ | OpenAI | `:openai` | Responses or Chat Completions | Yes |
30
+ | Compatible API | `:openai_compatible` | Responses or Chat Completions | When the endpoint implements it |
31
+ | Ollama | `:openai_compatible` | Responses or Chat Completions | When the model supports it |
32
+ | Anthropic | `:anthropic` | Messages | No |
33
+ | Gemini | `:gemini` | Gemini API | No |
34
+ | Vertex AI | `:vertex_ai` | Gemini on Vertex AI | No |
35
+ | Bedrock | `:bedrock` | Converse | Titan Text Embeddings V2 |
36
+ | LM Studio | `:lm_studio` | Responses or Chat Completions | Yes |
37
+
38
+ Capabilities can vary by model, account, and server version. Handle provider
39
+ failures even when a model is expected to support a feature.
40
+
41
+ ## Hosted providers
42
+
43
+ ### OpenRouter
44
+
45
+ OpenRouter gives one connection access to models from several providers:
46
+
47
+ ```ruby
48
+ config.providers = {
49
+ openrouter: {
50
+ adapter: :openrouter,
51
+ api_key: ENV.fetch("OPENROUTER_API_KEY"),
52
+ app_name: "Support Console",
53
+ site_url: "https://support.example.com"
54
+ }
55
+ }
56
+ ```
57
+
58
+ Use OpenRouter's `publisher/model` form, such as
59
+ `openrouter:anthropic/claude-sonnet-4`. `app_name` and `site_url` are optional.
60
+ The selected model must support embeddings before you call
61
+ `LittleGhost.embed`.
62
+
63
+ ### OpenAI
64
+
65
+ ```ruby
66
+ config.providers = {
67
+ openai: {
68
+ adapter: :openai,
69
+ api_key: ENV.fetch("OPENAI_API_KEY")
70
+ }
71
+ }
72
+ ```
73
+
74
+ Generation uses the Responses API by default. Use `openai:gpt-5.6-luna` for
75
+ generation or `openai:text-embedding-3-small` for embeddings. Set
76
+ `api: :chat_completions` only when required.
77
+
78
+ ### Anthropic
79
+
80
+ ```ruby
81
+ config.providers = {
82
+ anthropic: {
83
+ adapter: :anthropic,
84
+ api_key: ENV.fetch("ANTHROPIC_API_KEY")
85
+ }
86
+ }
87
+ ```
88
+
89
+ Use a target such as `anthropic:claude-sonnet-4-6`. The adapter uses the
90
+ Messages API. LittleGhost does not provide Anthropic embeddings.
91
+
92
+ ### Gemini
93
+
94
+ ```ruby
95
+ config.providers = {
96
+ gemini: {
97
+ adapter: :gemini,
98
+ api_key: ENV.fetch("GEMINI_API_KEY")
99
+ }
100
+ }
101
+ ```
102
+
103
+ Use `gemini:gemini-2.5-flash`, for example. LittleGhost does not provide Gemini
104
+ embeddings.
105
+
106
+ ### Vertex AI
107
+
108
+ Vertex AI uses Gemini's request format with a Google Cloud project and
109
+ location:
110
+
111
+ ```ruby
112
+ config.providers = {
113
+ vertex: {
114
+ adapter: :vertex_ai,
115
+ project: ENV.fetch("GOOGLE_CLOUD_PROJECT"),
116
+ location: "global"
117
+ }
118
+ }
119
+ ```
120
+
121
+ Use a target such as `vertex:gemini-2.5-flash`. The credential resolver can
122
+ obtain Google credentials, or startup can supply an access token. LittleGhost
123
+ does not provide Vertex AI embeddings.
124
+
125
+ ### Amazon Bedrock
126
+
127
+ ```ruby
128
+ config.providers = {
129
+ bedrock: {
130
+ adapter: :bedrock,
131
+ region: ENV.fetch("AWS_REGION")
132
+ }
133
+ }
134
+ ```
135
+
136
+ Use a model available in the region, such as
137
+ `bedrock:us.anthropic.claude-sonnet-4-6-v1:0`. The adapter resolves AWS
138
+ credentials and signs requests without an AWS SDK.
139
+
140
+ Bedrock embeddings support `amazon.titan-embed-text-v2:0`. Its `dimensions`
141
+ setting accepts 256, 512, or 1024, and `normalize` defaults to `true`.
142
+
143
+ ## Compatible APIs and local servers
144
+
145
+ ### Another OpenAI-compatible API
146
+
147
+ Use `:openai_compatible` for a hosted service, gateway, or local server that
148
+ implements OpenAI-style endpoints:
149
+
150
+ ```ruby
151
+ config.providers = {
152
+ models: {
153
+ adapter: :openai_compatible,
154
+ base_url: "https://models.example.com/v1/",
155
+ api_key: ENV.fetch("MODEL_API_KEY"),
156
+ api: :responses
157
+ }
158
+ }
159
+ ```
160
+
161
+ Use the model identifier expected by the endpoint, such as
162
+ `models:example-model`. Choose `api: :responses` or
163
+ `api: :chat_completions` to match the server. `LittleGhost.embed` uses the
164
+ connection's `embeddings` endpoint and supports `dimensions` when the server
165
+ and model accept it.
166
+
167
+ Tool calls, structured results, reasoning, attachments, and embeddings depend
168
+ on the compatible server and model.
169
+
170
+ Ollama and LM Studio normally listen on loopback HTTP. LittleGhost requires
171
+ `allow_insecure_http: true` as an explicit opt-in because HTTP does not protect
172
+ prompts, output, or credentials in transit. Use this setting only for a local
173
+ or otherwise trusted endpoint; use HTTPS for a remote server.
174
+
175
+ ### Ollama
176
+
177
+ Pull a model, start Ollama, and use the existing OpenAI-compatible adapter:
178
+
179
+ ```sh
180
+ $ ollama pull qwen3
181
+ ```
182
+
183
+ ```ruby
184
+ config.providers = {
185
+ ollama: {
186
+ adapter: :openai_compatible,
187
+ base_url: "http://localhost:11434/v1/",
188
+ api_key: "ollama",
189
+ api: :responses,
190
+ allow_insecure_http: true
191
+ }
192
+ }
193
+ ```
194
+
195
+ Use the pulled model name in the target, such as `ollama:qwen3`. The `api_key`
196
+ is a placeholder for a local server without authentication.
197
+
198
+ Ollama added its Responses endpoint in version 0.13.3. Set
199
+ `api: :chat_completions` for an earlier release or when the selected model does
200
+ not support the Responses features you need. See Ollama's [OpenAI compatibility
201
+ reference](https://docs.ollama.com/api/openai-compatibility) for release-specific
202
+ fields.
203
+
204
+ For embeddings, pull an embedding model and pass its target to
205
+ `LittleGhost.embed`. LittleGhost does not list, pull, or delete Ollama models.
206
+
207
+ ### LM Studio
208
+
209
+ Start LM Studio's local server after downloading or loading the model you want
210
+ to use. The dedicated adapter supplies the usual local endpoint and a
211
+ placeholder credential:
212
+
213
+ ```ruby
214
+ config.providers = {
215
+ lm_studio: {
216
+ adapter: :lm_studio,
217
+ allow_insecure_http: true
218
+ }
219
+ }
220
+ ```
221
+
222
+ Use the model key shown by LM Studio, for example
223
+ `lm_studio:google/gemma-3-4b`. Generation uses the Responses API by default.
224
+
225
+ Set `api: :chat_completions` with LM Studio releases earlier than 0.3.29, or
226
+ when required by the selected model. `LittleGhost.embed` uses the same
227
+ connection with an embedding model. See LM Studio's [compatibility
228
+ reference](https://lmstudio.ai/docs/developer/openai-compat) for its current
229
+ generation and embedding endpoints.
230
+
231
+ When LM Studio authentication is enabled, add
232
+ `api_key: ENV.fetch("LM_STUDIO_API_TOKEN")` to the connection.
233
+
234
+ The native model list and API-token authentication require LM Studio 0.4.0 or
235
+ newer. LittleGhost reads the list; it does not load, unload, or download models.
236
+
237
+ ## Discover current model details
238
+
239
+ Call `refresh!` when you need current availability, loaded state, or
240
+ capabilities. Ordinary model requests do not make discovery calls.
241
+
242
+ ```ruby
243
+ resolver = LittleGhost.model_resolver
244
+ result = resolver.refresh!(target: "lm_studio:google/gemma-3-4b")
245
+ result[:errors]
246
+
247
+ details = resolver.details("lm_studio:google/gemma-3-4b")
248
+ details[:loaded]
249
+ details.context_window
250
+ details.input_modalities
251
+ details.supported_parameters
252
+ ```
253
+
254
+ Pass `target:` for one model, or omit it for every configured source. On
255
+ failure, `result[:errors]` describes the problem and previous details remain.
256
+
257
+ Hosted adapters can combine models.dev data with their provider's model API.
258
+ LM Studio reads its native model list. The generic compatible adapter and
259
+ Ollama do not have built-in model discovery.
260
+
261
+ LM Studio details can include downloaded and loaded state, active context
262
+ length, input types, tools, and reasoning. An unloaded model has no active
263
+ context length. See `LittleGhost::Models::Details` for every field.
264
+
265
+ ## Keep connection settings in application code
266
+
267
+ Choose keys, tokens, headers, base URLs, model identifiers, and API variants at
268
+ startup. Read secrets from deployment configuration or a secret manager. Do
269
+ not copy public request parameters into provider settings.
270
+
271
+ The selected provider may receive prompts, conversation history, Tool results,
272
+ schemas, and attachments. Choose one appropriate for that data.
273
+
274
+ Continue with [Running in Production](production.md) to place connections,
275
+ roles, sessions, and observability into a long-running application.
@@ -23,10 +23,7 @@ LittleGhost.configure do |config|
23
23
 
24
24
  config.sandbox = {
25
25
  provider: :native,
26
- files: {root: :read_write},
27
- root_filesystem: :isolated,
28
- environment: {inherit: false, set: {"LANG" => "C.UTF-8"}},
29
- network: :none
26
+ files: {root: :read_write}
30
27
  }
31
28
  end
32
29
  ```
@@ -34,7 +31,9 @@ end
34
31
  This example gives each Run a temporary writable root and removes it during
35
32
  teardown. The `:native` Sandbox backend selects Seatbelt on macOS or Bubblewrap on
36
33
  Linux. It raises instead of running without isolation when the native backend
37
- is unavailable.
34
+ is unavailable. Unless you configure otherwise, it isolates the host root,
35
+ removes child-process network access, and starts child processes without
36
+ inheriting the host environment.
38
37
 
39
38
  `LittleGhost::Tools::Filesystem` and `LittleGhost::Tools::Shell` use the Sandbox
40
39
  assigned to the current Run. Code-mode interpreters also run inside their own
@@ -148,8 +147,7 @@ config.sandbox = {
148
147
  },
149
148
  runtime_paths: {
150
149
  home: :read_write
151
- },
152
- network: :none
150
+ }
153
151
  }
154
152
  ```
155
153
 
@@ -200,6 +198,39 @@ Workspace paths:
200
198
  On Seatbelt, host-visible modes permit subprocesses because development tools
201
199
  often need them. A Scope can remove `process_spawn` for a command that does not.
202
200
 
201
+ ## Start child processes with only the values they need
202
+
203
+ When `environment` is omitted, sandboxed processes do not inherit the host
204
+ environment. LittleGhost starts with `LANG`, `LC_ALL`, and `PATH`, then adds
205
+ `LITTLE_GHOST_WORKSPACE_*` variables so the child can locate its Workspace.
206
+ Locale values come from the host when present and otherwise use `C.UTF-8`.
207
+ `PATH` keeps the host's absolute entries, removes empty, relative, and duplicate
208
+ entries, and falls back to `/usr/local/bin:/usr/bin:/bin` when nothing remains.
209
+ Credentials, provider keys, package-manager settings, and home-directory
210
+ variables are not copied into the child.
211
+
212
+ > **Safety note:** An absolute directory is not necessarily a trusted one. A
213
+ > host `PATH` may include project directories, temporary directories, or
214
+ > user-managed shims. Configure a fixed `PATH` when a model can invoke commands
215
+ > by name and you do not trust every host entry.
216
+
217
+ Declaring `environment` replaces the locale and path baseline instead of
218
+ merging with it. Use an empty Hash to omit all configurable values, or set
219
+ exactly what the process needs. Workspace routing variables remain available:
220
+
221
+ ```ruby
222
+ config.sandbox = {
223
+ provider: :native,
224
+ environment: {
225
+ inherit: false,
226
+ set: {"LANG" => "C.UTF-8", "PATH" => "/opt/my-tool/bin:/usr/bin"}
227
+ }
228
+ }
229
+ ```
230
+
231
+ Host inheritance requires both `inherit: true` in the Sandbox policy and an
232
+ individual process call that opts into inheritance.
233
+
203
234
  ## Choose an enforcement backend
204
235
 
205
236
  | Sandbox backend | Host | What it enforces |
@@ -260,6 +291,10 @@ Sandbox networking has three modes:
260
291
  - `:allowlist` requires an enforcing gateway: a supervised proxy that permits
261
292
  only configured destinations.
262
293
 
294
+ Seatbelt, Bubblewrap, and `:native` default to `:none`; declare a network mode
295
+ only when the child needs a different policy. The unrestricted backend uses
296
+ `:inherit` because it is not a security boundary.
297
+
263
298
  Proxy environment variables alone are not an allowlist. An external gateway
264
299
  uses named, process-only Workspace paths and verifies that they still point to
265
300
  the configured directories; it does not create a virtual path mapping.