Sentinel does not write code or prose. It reads the evidence and alternatives you provide, then returns a structured decision with model scores. Your agent retains control of execution and verification.
¹ Local RTX 5060 Ti deployment smoke, 12 synthetic requests, September 28, 2026. This is a latency observation, not a quality benchmark or service-level guarantee.
What it does
Three structured interfaces for decisions between agent steps. The outputs below illustrate response formats; they are not measured results.
Yes or no
A direct question about the current state, answered with the probability that the answer is yes.
One of several
Name the options and describe each in a line. Sentinel picks one and gives a probability for every option.
In order
Hand it a list of candidates and get them back best first, each with its share of the probability.
Why use it
- FastA small 12-request deployment smoke measured a 0.73-second median round trip. Actual latency depends on input length, load and connection.¹
- InspectableCompare scores across the options you supply. These are model estimates, not verified outcomes or universally calibrated confidence. Check consequential choices against evidence.
- Fails fastBounded timeouts and circuit breakers let your agent continue when Sentinel is unavailable. Handle busy or offline responses explicitly in your workflow.
Where it works
- OrrerySentinel runs inside Orrery, where the agent consults it for the quick calls during a run.
- MCP agentsAdd the Sentinel MCP server to any agent that speaks the Model Context Protocol: Claude Code, OpenCode, Cursor, Codex CLI, Gemini CLI or VS Code. The agent gets three tools,
sentinel_decide,sentinel_rankandsentinel_status, with descriptions that tell it when to ask. - Function callingFor your own agent loop, register
sentinel_decideas a tool in the OpenAI or Anthropic format and forward the call to the API. - ShellA small command-line client answers in its exit code, for scripts and shell-driven agents.
- APITwo HTTP endpoints on
api.ephemerent.com, with the same key as the Orrery API.
API quickstart
Sentinel uses the API key from your account page, sent as a bearer token. Give it the facts in state (code, a diff, an error message), ask one question, and add options when there are more than two answers.
/v1/sentinel/decideA yes/no answer with p_yes, or with options, the chosen option, its confidence and a probability for each./v1/sentinel/rankCandidates in order, best first, each with its probability./v1/sentinel/statusWhether Sentinel is up right now.curl https://api.ephemerent.com/v1/sentinel/decide \
-H "Authorization: Bearer $ORRERY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"state": "test_parse_date fails: ValueError on \"2026-09-26T10:00Z\". The patch adds a branch for a trailing Z in parse_date.",
"question": "Will this patch make the failing test pass?"
}'
# {"answer": "yes", "p_yes": 0.91, "model": "sentinel-1"}
# Python (pip install requests)
import os, requests
r = requests.post(
"https://api.ephemerent.com/v1/sentinel/decide",
headers={"Authorization": f"Bearer {os.environ['ORRERY_API_KEY']}"},
json={
"state": diff + "\n\n" + test_output,
"question": "Which of these should the agent do next?",
"options": {
"edit": "apply the fix to parse_date",
"read": "open the caller before changing anything",
"run": "run the test suite as it is",
},
},
timeout=8,
)
if r.status_code == 503:
decision = None # Sentinel is offline: decide without it
else:
r.raise_for_status()
decision = r.json()
# {"answer": "edit", "confidence": 0.84,
# "p": {"edit": 0.84, "read": 0.12, "run": 0.04}, "model": "sentinel-1"}
// JavaScript (fetch)
const res = await fetch("https://api.ephemerent.com/v1/sentinel/rank", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ORRERY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
context: failingTestOutput,
candidates: ["fix the parser", "fix the test"],
question: "Which fix should we try first?",
}),
signal: AbortSignal.timeout(8000),
});
const ranked = res.ok ? (await res.json()).ranked : null; // null: go on without it
// [["fix the parser", 0.78], ["fix the test", 0.22]]
For your own agent loop, this is the tool definition to register. Forward its arguments to /v1/sentinel/decide and return the answer to the model.
{
"name": "sentinel_decide",
"description": "Ask Sentinel to compare concrete next actions using the supplied evidence. Returns model scores; verify consequential choices independently.",
"parameters": {
"type": "object",
"properties": {
"state": { "type": "string", "description": "The facts the decision depends on: code, a diff, error output." },
"question": { "type": "string", "description": "One question with a short answer." },
"options": { "type": "object", "additionalProperties": { "type": "string" },
"description": "Named options, each with a one-line description. Omit for a yes/no question." }
},
"required": ["state", "question"]
}
}
Limits and pricing
Each decision costs a fraction of a cent, metered in the same compute credits as the rest of the API and drawn from the same balance. sentinel-1 is listed in /v1/models, but it is not a chat model: sending it to /chat/completions returns 400. When Sentinel is offline the API answers 503 with a Retry-After header; clients should carry on without it rather than wait. Sentinel requests are never stored or used for training, whatever your data setting: the service keeps counts and an access-log line, never what you sent or what it answered.