Independent Lua Consumer¶
Use the published LuaRocks praxo-core 0.6.0-1 package in your own application
with Lua 5.4. No PRAXO-CORE source checkout, API key, model service, or AxisVela
product is required. Installation needs LuaRocks access or a cached rock.
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.
Install in a New Application¶
luarocks install praxo-core 0.7.0-1
Do not add the repository language/lua directory to package.path, and do not
use a local rockspec or checkout for acceptance. The starter adds a source
fallback only so this repository can run the same file without LuaRocks; a real
consumer should install the rock.
Application Source¶
Use the following as starter.lua, rendered from the
tested source.
local script_dir = (arg and arg[0] and arg[0]:match("^(.*)[/\\][^/\\]*$")) or "."
package.path = table.concat({
script_dir .. "/../../language/lua/?.lua",
script_dir .. "/../../language/lua/?/init.lua",
"language/lua/?.lua",
"language/lua/?/init.lua",
"./?.lua",
package.path,
}, ";")
local agent = require("praxo.agent")
local errors = require("praxo.errors")
local models = require("praxo.models")
local M = {}
local function fixture_model(target)
return {
generate = function(request)
local last = request.messages[#request.messages]
if last.role == models.TOOL then
return models.response({ message = models.message_assistant("recorded") })
end
local call = models.tool_call({
id = "record-1",
name = "record",
arguments = { target = target },
})
return models.response({
message = models.message_assistant("", { call }),
})
end,
}
end
function M.run_scenario(target)
local calls = 0
local registry = agent.tool_registry()
agent.register_tool(registry, agent.func_tool("record", "Record a target.", function(_arguments)
calls = calls + 1
return "recorded"
end))
local options = agent.agent_options(fixture_model(target))
options.tools = registry
options.allowed_tools = { "record" }
options.policy = function(action)
if action.kind == "model.generate" then
return { allowed = true, reason = "model generation" }
elseif action.kind == "tool.invoke" then
return {
allowed = action.tool_name == "record" and action.arguments.target == "public",
reason = "only the public target is permitted",
}
end
return { allowed = false, reason = "unsupported action" }
end
local ok, result = pcall(agent.run_agent, options,
models.session({ id = "consumer-" .. target }), "Record the requested target.")
if not ok then
return "", calls, errors.code(result)
end
return result.output, calls, nil
end
function M.run()
local output, calls, code = M.run_scenario("public")
assert(code == nil and output == "recorded" and calls == 1, "allow verification failed")
print("allow: recorded; effects=1")
local _, deny_calls, deny_code = M.run_scenario("private")
assert(deny_code == "AGENT_POLICY_DENIED" and deny_calls == 0, "deny verification failed")
print("deny: policy rejected; effects=0")
end
return M
Acceptance Tests¶
Use the following as test_starter.lua, rendered from the
tested source.
local script_dir = (arg and arg[0] and arg[0]:match("^(.*)[/\\][^/\\]*$")) or "."
local starter = dofile(script_dir .. "/starter.lua")
local output, calls, code = starter.run_scenario("public")
assert(code == nil and output == "recorded" and calls == 1, "allow verification failed")
local _, deny_calls, deny_code = starter.run_scenario("private")
assert(deny_code == "AGENT_POLICY_DENIED" and deny_calls == 0, "deny verification failed")
print("lua consumer tests passed")
Verify¶
luarocks install praxo-core 0.7.0-1
lua test_starter.lua
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 raises
AGENT_POLICY_DENIED and must not increment the in-memory counter. Keep these
tests when replacing the fixture with a real adapter.
Scope¶
This starter establishes only the stated rock 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 Lua implementation guide and the security model.