Module: Silas
- Defined in:
- lib/silas.rb,
lib/silas/chat.rb,
lib/silas/eval.rb,
lib/silas/tool.rb,
lib/silas/agent.rb,
lib/silas/inbox.rb,
lib/silas/skill.rb,
lib/silas/slack.rb,
lib/silas/budget.rb,
lib/silas/engine.rb,
lib/silas/errors.rb,
lib/silas/ledger.rb,
lib/silas/channel.rb,
lib/silas/sandbox.rb,
lib/silas/version.rb,
lib/silas/eval/dsl.rb,
lib/silas/registry.rb,
lib/silas/schedule.rb,
lib/silas/connection.rb,
lib/silas/inbox/cost.rb,
lib/silas/mcp/client.rb,
lib/silas/mcp/server.rb,
app/models/silas/step.rb,
app/models/silas/turn.rb,
lib/silas/agent_scope.rb,
lib/silas/connections.rb,
lib/silas/eval/driver.rb,
lib/silas/eval/grader.rb,
lib/silas/eval/result.rb,
lib/silas/eval/runner.rb,
lib/silas/mcp/handler.rb,
lib/silas/named_agent.rb,
lib/silas/step_runner.rb,
lib/silas/engines/base.rb,
lib/silas/instructions.rb,
lib/silas/sandbox/null.rb,
lib/silas/tools/recall.rb,
app/models/silas/memory.rb,
lib/silas/agent_sdk/cli.rb,
lib/silas/configuration.rb,
lib/silas/nested_runner.rb,
lib/silas/tools/handoff.rb,
app/models/silas/session.rb,
lib/silas/sandbox/docker.rb,
lib/silas/tools/delegate.rb,
lib/silas/tools/remember.rb,
lib/silas/tools/run_code.rb,
lib/silas/eval/assertions.rb,
lib/silas/eval/transcript.rb,
lib/silas/message_builder.rb,
lib/silas/engines/ruby_llm.rb,
lib/silas/tools/load_skill.rb,
app/jobs/silas/schedule_job.rb,
lib/silas/engines/agent_sdk.rb,
lib/silas/schedule/compiler.rb,
lib/silas/subprocess_runner.rb,
app/jobs/silas/agent_loop_job.rb,
lib/silas/eval/scripted_engine.rb,
app/mailers/silas/channel_mailer.rb,
app/models/silas/tool_invocation.rb,
app/mailboxes/silas/agent_mailbox.rb,
lib/silas/agent_sdk/stream_parser.rb,
lib/silas/agent_sdk/version_guard.rb,
app/jobs/silas/channel_delivery_job.rb,
app/jobs/silas/dead_job_rescuer_job.rb,
app/models/silas/application_record.rb,
app/helpers/silas/inbox/trace_helper.rb,
app/controllers/silas/inbox/base_controller.rb,
app/models/concerns/silas/inbox/broadcastable.rb,
app/controllers/silas/channels/base_controller.rb,
lib/generators/silas/install/install_generator.rb,
app/controllers/silas/channels/slack_controller.rb,
app/controllers/silas/inbox/sessions_controller.rb,
app/controllers/silas/inbox/invocations_controller.rb,
app/controllers/silas/channels/approvals_controller.rb
Defined Under Namespace
Modules: AgentSdk, Budget, Channels, Engines, Eval, Generators, Inbox, Instructions, Ledger, Mcp, MessageBuilder, NestedRunner, Sandbox, Slack, StepRunner, SubprocessRunner, Tools Classes: Agent, AgentLoopJob, AgentMailbox, AgentScope, ApplicationRecord, BootGuardError, Channel, ChannelDeliveryJob, ChannelMailer, Chat, CheckpointInLedgerError, Configuration, Connection, Connections, DeadJobRescuerJob, Engine, Error, Event, Memory, NamedAgent, NondeterminismError, Registry, SandboxDisabledError, SandboxError, Schedule, ScheduleJob, Session, Skill, Step, Tool, ToolInvocation, Turn, TurnInProgressError
Constant Summary collapse
- SCOPE_KEY =
---- scope switching -----------------------------------------------------
:silas_agent_scope- VERSION =
"0.1.7"
Class Method Summary collapse
-
.agent(name = nil) ⇒ Object
The active agent definition — or, given a name, a handle for a NAMED agent (app/agents/
/) whose sessions run under that agent's scope:. - .config ⇒ Object
- .configure {|config| ... } ⇒ Object
- .current_scope ⇒ Object
-
.definitions_digest ⇒ Object
The live definitions digest as a String (nil when none configured).
- .instructions_dir ⇒ Object
-
.memory_enabled? ⇒ Boolean
Memory is on when configured AND the table exists (upgrade-safe: an app that hasn't run the 0.1.7 migration simply doesn't advertise the tools).
- .named_agent?(name) ⇒ Boolean
- .named_agent_scope!(name) ⇒ Object
-
.named_agent_scopes ⇒ Object
Named-agent roster: { "name" => AgentScope }.
-
.reset_agent_memo! ⇒ Object
after Registry.install! swaps dirs.
-
.reset_configuration! ⇒ Object
for specs.
-
.resolved_engine ⇒ Object
The inference adapter instance.
- .resolved_sandbox ⇒ Object
-
.sandbox_enabled? ⇒ Boolean
A configured-but-disabled backend (e.g. Hermetic.null) must not register run_code, so the object's own enabled? is the final word.
- .schedules ⇒ Object
-
.scope_for_session(session) ⇒ Object
The scope a session's turns must run under: nil for the root agent, otherwise the named-agent or subagent scope matching session.agent_name.
- .skills ⇒ Object
- .subagent?(name) ⇒ Boolean
-
.subagent_index ⇒ Object
Subagent roster: [[name, description], ...] (model-visible, so it's in the root digest via Delegate.description).
- .subagent_scope(name) ⇒ Object
- .tool_definitions ⇒ Object
-
.tool_resolver ⇒ Object
---- scope-aware readers ------------------------------------------------- Every reader consults the active AgentScope first (named agent or subagent), falling back to the boot-time config the Registry installed.
-
.with_agent_scope(scope) ⇒ Object
Run a block under an AgentScope.
Class Method Details
.agent(name = nil) ⇒ Object
150 151 152 153 154 |
# File 'lib/silas.rb', line 150 def agent(name = nil) return NamedAgent.new(named_agent_scope!(name)) if name current_scope&.agent || config.agent_override || (@agent ||= Agent.load) end |
.config ⇒ Object
52 53 54 |
# File 'lib/silas.rb', line 52 def config @config ||= Configuration.new end |
.configure {|config| ... } ⇒ Object
56 57 58 59 60 |
# File 'lib/silas.rb', line 56 def configure yield config config.validate! config end |
.current_scope ⇒ Object
199 200 201 |
# File 'lib/silas.rb', line 199 def current_scope ActiveSupport::IsolatedExecutionState[SCOPE_KEY] end |
.definitions_digest ⇒ Object
The live definitions digest as a String (nil when none configured).
137 138 139 |
# File 'lib/silas.rb', line 137 def definitions_digest current_scope&.digest || config.definitions_digest&.call&.to_s.presence end |
.instructions_dir ⇒ Object
141 142 143 |
# File 'lib/silas.rb', line 141 def instructions_dir current_scope&.dir || config.instructions_dir end |
.memory_enabled? ⇒ Boolean
Memory is on when configured AND the table exists (upgrade-safe: an app that hasn't run the 0.1.7 migration simply doesn't advertise the tools).
161 162 163 164 165 |
# File 'lib/silas.rb', line 161 def memory_enabled? config.memory && Memory.table_exists? rescue ActiveRecord::NoDatabaseError, ActiveRecord::ConnectionNotEstablished false end |
.named_agent?(name) ⇒ Boolean
166 |
# File 'lib/silas.rb', line 166 def named_agent?(name) = named_agent_scopes.key?(name.to_s) |
.named_agent_scope!(name) ⇒ Object
168 169 170 171 172 173 174 |
# File 'lib/silas.rb', line 168 def named_agent_scope!(name) named_agent_scopes.fetch(name.to_s) do known = named_agent_scopes.keys raise Error, "unknown agent #{name.inspect}" \ "#{known.any? ? " (known: #{known.join(', ')})" : " — no app/agents/ directories found"}" end end |
.named_agent_scopes ⇒ Object
Named-agent roster: { "name" => AgentScope }.
157 |
# File 'lib/silas.rb', line 157 def named_agent_scopes = config.named_agent_scopes&.call || {} |
.reset_agent_memo! ⇒ Object
after Registry.install! swaps dirs
98 |
# File 'lib/silas.rb', line 98 def reset_agent_memo! = (@agent = nil) # after Registry.install! swaps dirs |
.reset_configuration! ⇒ Object
for specs
62 63 64 65 66 67 |
# File 'lib/silas.rb', line 62 def reset_configuration! # for specs @config = nil @resolved_engine = nil @resolved_sandbox = nil @agent = nil end |
.resolved_engine ⇒ Object
The inference adapter instance. config.engine may be a symbol (:ruby_llm, :agent_sdk) or any object responding to #execute_step (specs, custom).
102 103 104 105 106 107 108 109 110 |
# File 'lib/silas.rb', line 102 def resolved_engine @resolved_engine ||= case config.engine when :ruby_llm then Engines::RubyLLM.new when :agent_sdk then Engines::AgentSdk.new when Symbol then raise Error, "unknown engine #{config.engine.inspect}" else config.engine end end |
.resolved_sandbox ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/silas.rb', line 77 def resolved_sandbox @resolved_sandbox ||= case config.sandbox when :none, nil then Sandbox::Null.new when :docker Sandbox::Docker.new(image: config.sandbox_image, network: config.sandbox_network, memory: config.sandbox_memory, cpus: config.sandbox_cpus, pids: config.sandbox_pids, workdir: config.sandbox_workdir, docker_bin: config.sandbox_docker_bin) when Symbol then raise Error, "unknown sandbox #{config.sandbox.inspect}" else # A hermetic backend drops straight in (its Result is a superset of # ours). Auto-arm its ledger guard so a sandbox exec inside a ledger # transaction fails loud — same posture as our own Docker adapter. if defined?(Hermetic::Backends::Base) && config.sandbox.is_a?(Hermetic::Backends::Base) require "hermetic/silas" end config.sandbox end end |
.sandbox_enabled? ⇒ Boolean
A configured-but-disabled backend (e.g. Hermetic.null) must not register run_code, so the object's own enabled? is the final word.
71 72 73 74 75 |
# File 'lib/silas.rb', line 71 def sandbox_enabled? return false if [ :none, nil ].include?(config.sandbox) resolved_sandbox.enabled? end |
.schedules ⇒ Object
132 133 134 |
# File 'lib/silas.rb', line 132 def schedules config.schedules&.call || [] end |
.scope_for_session(session) ⇒ Object
The scope a session's turns must run under: nil for the root agent, otherwise the named-agent or subagent scope matching session.agent_name. Fails loud on an unknown name — a session pointing at a deleted agent directory must never silently run with the root agent's tools.
186 187 188 189 190 191 192 193 |
# File 'lib/silas.rb', line 186 def scope_for_session(session) name = session.agent_name.to_s return nil if name.empty? || name == "agent" named_agent_scopes[name] || config.subagent_scopes&.call&.[](name) or raise Error, "session #{session.id} belongs to agent #{name.inspect}, " \ "but no app/agents/#{name} or app/agent/subagents/#{name} exists" end |
.skills ⇒ Object
128 129 130 |
# File 'lib/silas.rb', line 128 def skills current_scope&.skills || config.skills&.call || [] end |
.subagent?(name) ⇒ Boolean
179 |
# File 'lib/silas.rb', line 179 def subagent?(name) = subagent_index.any? { |n, _| n == name.to_s } |
.subagent_index ⇒ Object
Subagent roster: [[name, description], ...] (model-visible, so it's in the root digest via Delegate.description).
178 |
# File 'lib/silas.rb', line 178 def subagent_index = config.subagent_index&.call || [] |
.subagent_scope(name) ⇒ Object
180 |
# File 'lib/silas.rb', line 180 def subagent_scope(name) = config.subagent_scopes&.call&.fetch(name.to_s) |
.tool_definitions ⇒ Object
124 125 126 |
# File 'lib/silas.rb', line 124 def tool_definitions current_scope&.definitions || config.tool_definitions&.call || [] end |
.tool_resolver ⇒ Object
---- scope-aware readers ------------------------------------------------- Every reader consults the active AgentScope first (named agent or subagent), falling back to the boot-time config the Registry installed. The scope lives in IsolatedExecutionState — per-thread AND per-fiber (Falcon-safe), so concurrent jobs running different agents in one process can never see each other's tools.
119 120 121 122 |
# File 'lib/silas.rb', line 119 def tool_resolver current_scope&.resolver || config.tool_resolver or raise Error, "no tool resolver configured (Registry boots one; specs must inject)" end |
.with_agent_scope(scope) ⇒ Object
Run a block under an AgentScope. Nestable (delegation inside a named agent restores the outer scope on exit) and isolated per execution context — no global config is mutated, so concurrent jobs are safe.
206 207 208 209 210 211 212 |
# File 'lib/silas.rb', line 206 def with_agent_scope(scope) previous = ActiveSupport::IsolatedExecutionState[SCOPE_KEY] ActiveSupport::IsolatedExecutionState[SCOPE_KEY] = scope yield ensure ActiveSupport::IsolatedExecutionState[SCOPE_KEY] = previous end |