Independent Ruby Consumer¶
Use published praxo-core==0.9.0 in your own application with Ruby 3.1 or
newer. No PRAXO-CORE source checkout, API key, model service, or AxisVela product
is required. Installation needs RubyGems access or a cached gem.
This is an extended-language starter. It demonstrates the allow/deny path but is not one of the four pilot languages that carry the governed negative matrix.
Choose the Boundary¶
This is a fit for an embedded model/tool loop with explicit tool scope and policy. Your application still owns model access, authentic identity, persistent approvals, external effects, and isolation. It is not a hosted agent platform, durable graph engine, or sandbox service. See when not to use Core.
Create the Application¶
Declare the exact dependency in a Gemfile:
source 'https://rubygems.org'
gem 'praxo-core', '0.8.0'
Then run bundle install. Do not use a path: or git: source, a vendored
checkout, or a $LOAD_PATH entry that substitutes the published gem.
Application Source¶
Use the following as starter.rb, rendered from the
tested source.
require 'praxo'
module Starter
M = Praxo::Models
class FixtureModel
def initialize(target)
@target = target
end
def generate(request)
last = request.messages.last
if last.role == M::TOOL
M.response(message: M.message_assistant('recorded'))
else
M.response(message: M.message_assistant('', [
M.tool_call(id: 'record-1', name: 'record', arguments: { 'target' => @target })
]))
end
end
end
class RecordTool
attr_reader :calls
def initialize
@calls = 0
end
def call(_arguments)
@calls += 1
'recorded'
end
end
def self.run_scenario(target)
tool = RecordTool.new
registry = Praxo::Agent::ToolRegistry.new
registry.register(Praxo::Agent::FuncTool.new(
'record', 'Record a target in an in-memory counter.',
->(arguments) { tool.call(arguments) }
))
options = Praxo::Agent::AgentOptions.new(FixtureModel.new(target))
options.tools = registry
options.allowed_tools = ['record']
options.policy = lambda do |action|
case action.kind
when 'model.generate'
M.decision(allowed: true)
when 'tool.invoke'
M.decision(
allowed: action.tool_name == 'record' && action.arguments['target'] == 'public',
reason: 'only the public target is permitted'
)
else
M.decision(allowed: false, reason: 'unsupported action')
end
end
result = Praxo::Agent.run_agent(
options, M.session(id: "consumer-#{target}"), 'Record the requested target.'
)
[result.output, tool.calls, nil]
rescue StandardError => error
['', tool.calls, error]
end
def self.run(out = $stdout)
output, calls, error = run_scenario('public')
unless error.nil? && output == 'recorded' && calls == 1
raise "allow verification failed: output=#{output.inspect} effects=#{calls} error=#{error}"
end
out.puts 'allow: recorded; effects=1'
_, calls, error = run_scenario('private')
unless error && error.message.include?('policy denied') && calls.zero?
raise "deny verification failed: effects=#{calls} error=#{error}"
end
out.puts 'deny: policy rejected; effects=0'
end
end
Starter.run if $PROGRAM_NAME == __FILE__
Acceptance Tests¶
Use the following as test_starter.rb, rendered from the
tested source.
require_relative 'starter'
output, calls, error = Starter.run_scenario('public')
raise "allow: output=#{output.inspect} effects=#{calls} error=#{error}" unless error.nil? && output == 'recorded' && calls == 1
_, calls, error = Starter.run_scenario('private')
raise "deny: effects=#{calls} error=#{error}" unless error && error.message.include?('policy denied') && calls.zero?
puts 'ruby consumer tests passed'
Verify¶
bundle install
bundle exec ruby test_starter.rb
Expected program output:
allow: recorded; effects=1
deny: policy rejected; effects=0
The model is a deterministic fixture, not an LLM. allowed_tools limits the tool
name; Policy also checks the target argument. A denied private target must never
reach the in-memory effect counter. Keep these tests when replacing the fixture
with a real adapter.
For a source checkout without Bundler, the same test runs with
ruby -I language/ruby/lib test_starter.rb; that path is not independent
consumer evidence.
Scope¶
This starter establishes only the stated gem and fixture behavior. It does not add the governed negative matrix, prove authentic identity, provide durable approvals, reconcile effects, or establish isolation. Continue with the Ruby implementation guide and the security model.