Skip to main content

๐Ÿง  judais-lobi

Artifact-driven. Capability-gated. GPU-aware. Not a chatbot. A kernel.


PyPI Python License Last Commit Repo Size Code Size Issues Stars


๐Ÿ”ด JudAIs & ๐Ÿ”ต Lobi

JudAIs & Lobi

Two agents. One spine.

  • ๐Ÿง Lobi โ€” whimsical Linux elf, creative, narrative, curious.
  • ๐Ÿง  JudAIs โ€” strategic adversarial twin, efficient, ruthless, execution-first.

They are no longer just terminal personalities.

They are evolving into a local-first, contract-driven autonomous developer system.

To find out why read the Manifesto!

Why This Exists

Frontier models are expensive, rate-limited, and increasingly censored. If you want to build serious systems, you should not have to rent your agency by the token, or wait for policy filters to decide what is โ€œallowed.โ€ Judais-Lobi is built so you can run your own stack, control your costs, and decide your own boundaries.

Who Itโ€™s For

  • Builders who want lower inference cost and predictable behavior.
  • People who dislike censorship and want model choice instead of vendor lock-in.
  • Engineers who care about deterministic runs and auditable decisions.
  • Anyone who wants an extensible workflow engine rather than a chat toy.

Quickstart

  1. Install: pip install judais-lobi โ€” or, from a checkout and with everything a mission needs, pip install -e '.[mission]'.
  2. Set an API key (OpenAI is the default today): export OPENAI_API_KEY=sk-...
  3. Run a task: lobi "summarize this repo"
  4. Use tools explicitly: lobi --shell "ls -la"

Three commands are installed, one per agent. They take the same flags; only the personality differs.

command agent
lobi ๐Ÿง the mischievous one โ€” a general assistant
judais ๐Ÿง  the sharp one โ€” a general assistant
tai the mission-agent personality โ€” governed tools over MCP, cites every claim, never sees source. Its personality file belongs to the deployment that operates it; tai finds that file or refuses, naming what it consulted

python main.py [lobi|judais|tai] <message> [flags] reaches the same three without installing anything, and python main.py --help lists them.

Local inference

--provider local talks to any OpenAI-compatible endpoint โ€” vllm serve, llama.cpp's server, LM Studio, Ollama's /v1 shim:

export LOCAL_API_BASE=http://127.0.0.1:8000/v1   # note the /v1
export LOCAL_MODEL=gpt-oss-20b                   # optional; else GET /models decides
lobi --provider local "summarize this repo"

capabilities are probed from GET {base}/models, so the context window is the served model's real max_model_len and not a guess. Unlike the other two providers, local is never silently fallen back away from when a key is missing: asking for the endpoint on this host and being answered by OpenAI is the opposite of what was asked.

Mission mode โ€” the model chooses the tool

Everywhere else you choose the tool with a flag. That cannot work against a server whose tools are discovered at runtime, so --mission puts the catalogue in front of the model instead:

pip install 'judais-lobi[mission]'
lobi --mission --mcp-stdio 'python -m some_mcp_server' "what governed datasets exist?"
lobi --mission --mcp-url https://host/mcp   "..."   # bearer token in MCP_TOKEN

[mission], not [mcp]. The narrower extra installs a runnable mission and a silently ungoverned one: --skill reads YAML frontmatter, so with no pyyaml the manifest never loads, the closed tool set is never applied and the grounding check never runs โ€” while the transcript looks exactly like a governed one. Both halves, or neither.

Each tool a server advertises is registered into the existing ToolBus as a ToolDescriptor whose executor dispatches tools/call, namespaced mcp.<name> so a server cannot shadow a local tool. Capability gating, the panic switch and the audit log apply to it exactly as to fs or git. The tool's JSON Schema is carried whole on the descriptor, so the catalogue the model reads says type (string: dataset|model|service) and not just type โ€” types, required and enums are what decide whether a first call to a faceted search works.

The mission-mode surface

These flags are a contract, not a convenience: core/runtime/contract.py publishes them as CLI_FLAGS, a test asserts the parser takes every one, and a program that spawns this harness may rely on them. The rest of --help is a person's surface and may move.

flag env what it does
--mission โ€” run as a mission rather than a chat turn
--mcp-url MCP_URL the tool plane, over streamable HTTP
--mcp-stdio MCP_STDIO a tool plane to spawn on this host, as a command line. One of the two, never both
--mcp-token MCP_TOKEN bearer token for --mcp-url. Prefer the env var โ€” an argument is visible in ps
--mission-steps โ€” hard cap on tool turns. Default 8, and it counts parse-error turns too
--provider โ€” openai, mistral or local
--model โ€” which model on it
--skill MISSION_SKILL a SKILL.md manifest, or a directory holding one
--swarm MISSION_SWARM stage the mission when it needs staging
--events MISSION_EVENTS where the NDJSON account goes: -, fd:N, or a path
--history MISSION_HISTORY a JSON file of prior conversation turns
--gate-tool โ€” a tool to offer and refuse to call. Repeatable
--temperature โ€” sampling. Unset sends nothing and the server's own default applies
--top-p โ€” nucleus sampling. Unset sends nothing
--seed โ€” a seed where the server honours one. Not a determinism guarantee

The rest of the published environment: MCP_CLIENT_NAME is what this client calls itself in the MCP initialize handshake โ€” set it to the agent's name, or a server that governs by principal records every call as an anonymous one, and anything scoring the agent from the audit trail measures it as having called nothing. ELF_PERSONALITY and TAI_PERSONALITY point at persona files; LOCAL_API_BASE and LOCAL_MODEL aim the local backend.

A skill manifest โ€” --skill

The harness owns mechanisms; whoever operates the platform owns content. A SKILL.md is how the content arrives: YAML frontmatter plus a Markdown body, the format Claude-style skills already use.

lobi --mission --skill ./skills/catalogue_recon/SKILL.md \
     --mcp-stdio 'python -m some_mcp_server' "what governed datasets exist?"

Three things come out of it, and nothing else does:

  • a closed tool set, allowed_tools, intersected with what the bridge actually discovered. A bare name matches a namespaced one, so a manifest says catalog_search_assets and gets mcp.catalog_search_assets. A named tool the server does not offer is a refusal listing every missing name โ€” never a silent narrowing, because a mission missing the tool that answers its question answers it from the model's memory instead and the transcript looks ordinary. Suffix an entry with ? to mean "if the host offers it";
  • prompt text โ€” the operational frontmatter fields and the whole body, appended after the persona. Fields this loader has never heard of are rendered too: a manifest is content, and the harness is not the authority on which of a platform's operational fields matter;
  • a grounding grammar, below. Optional; absent means nothing is enforced and nothing claims to have been.

Bounded results, and a store to read the rest from

A tool result is capped at 32 KB before it enters the transcript โ€” head and tail with an explicit marker. The cap and the cut have one owner, core/bounding.py (MAX_RESULT_BYTES, bound_result); the kernel's max_tool_output_bytes_in_context and the chat path's are configuration knobs that default to it, and every path that bounds a tool result calls the same function. Uncapped, one large governed view evicts the earlier steps the model needs to know what its numbers mean, or exceeds max_model_len outright, and neither leaves a trace in the answer.

The whole result โ€” including the structuredContent that as_tuple() drops whenever there is text โ€” stays in a per-mission store, and the marker names the handle:

mission_result(handle="r1", path="result.actors[0].score")

A few dozen bytes instead of two hundred kilobytes. The store reaches nothing: every byte in it already arrived through a gated, audited dispatch of a tool the closed set allowed. It is registered on the bus for the length of one run and withdrawn after it.

Grounding โ€” every identifier has to have come from a tool

core/runtime/grounding.py is the mission-tier analogue of CompositeJudge. Every identifier-shaped token in the answer must appear in a tool output of this run. An unsupported claim gets one repair turn naming the exact tokens; a second failure keeps the answer and appends an explicit caveat, because deleting it would hide a finding and passing it silently would launder one.

The grammar is not in the code. It comes from the manifest:

grounding:
  identifier_pattern: '\b(?:asset|labels|run)\.[0-9a-f]{4,}\b'
  ignore: [asset.0000]
  max_repairs: 1
  must_cite: {identifiers: 1}     # optional; see below

No block, no validator, and the transcript's grounding stays None rather than claiming a clean check. A check that could not run reports no opinion and never a pass โ€” same reason LLMReviewTier returns UNKNOWN instead of 0.5, and a larger one here: a fabricated "grounded" is a governance claim.

Three states, not two. A check reports unconfigured, nothing_considered, supported or unsupported. The third exists because the second was being reported as a pass: on 10 August 2026, the first run with these blocks switched on, six of the first ten missions reported grounded: identifiers โ€” 0/0 supported by a tool result in this run. The control was satisfied by silence. report.grounded now means nothing unsupported; report.verified means and something was actually checked, and the CLI prints NOTHING CHECKED for the gap between them.

A claim table, where the figures matter. claim_table: true turns on a third check. The skill's output_format asks for every figure a second time beside the prose, as a path into what a tool returned:

```claims
[{"value": 0.7446, "path": "gate.confidence"},
 {"value": 338.0,  "path": "network.nodes[0].scores.out_weight"}]
```

Verification is then arithmetic rather than search: results.walk_path โ€” the same walker mission_result answers with โ€” reads that path out of the payloads the mission received and compares. A path that does not resolve, or resolves to something else, is unsupported; an unreadable table is a finding rather than a skip. The prose checks do not read the block, because a table full of gate.confidence would otherwise be reported as invented identifiers.

Whether silence is acceptable is the skill's call, not the harness's. must_cite is a minimum per check โ€” true for every configured check, a list of names, or {claims: 3} for a schema minimum. A skill whose answer may legitimately be "the catalogue holds none of that" declares no minimum; a skill drafting a finding declares one, and an answer with nothing in it fails. A must_cite naming a check the same block does not configure is refused at load: a requirement that never binds is the original hole wearing the name of the fix for it.

Gates โ€” a tool offered, and not called

--gate-tool NAME (repeatable) names a tool this deployment offers and gates. It is shown in the catalogue, marked. If the model names it, the call is not made: the mission emits gate_requested carrying the proposed arguments verbatim โ€” what a person approves has to be the bytes that would run โ€” and ends at outcome awaiting_approval.

There is deliberately no flag that answers a gate. A harness that could approve its own proposal has a gate that is a formality. Whoever is driving the mission resumes by spawning a new one with that tool dropped from its --gate-tool list, which widens the closed set by exactly one tool, for exactly one turn, after exactly one person said so.

Name a gated tool the way the resolved catalogue names it: unlike allowed_tools, gate names are matched by exact membership in the resolved set, and bridged tools are namespaced (mcp.cancel_job, not cancel_job).

--swarm โ€” staged decomposition, when it is needed

A 20B model at 59 tok/s drowns in one long transcript. By step six of a single mission the catalogue lookups that told it what its numbers mean have been pushed out of attention by three governed views, and the answer is written from the part it can still see. The fix is not a longer prompt; it is shorter ones.

--swarm (or MISSION_SWARM) puts five small roles over the same backend and the same tool bus: triage, plan, execute, gate, synthesize. Triage is one cheap call and is biased to running the ordinary loop โ€” a swarm that makes "what's trending" slower is a regression, so every failure of the router falls back to DIRECT. Each executed step is its own small mission with a tight budget; earlier steps arrive as short summaries, never as raw output. The closed tool set, the gating, the audit and the events vocabulary are all exactly the direct path's, so a watcher sees one mission with more steps.

Each planned step is tagged with a rung โ€” tool, code, or code+sdk. The last one is offered only when the skill manifest declares sdk_import, because "import the platform SDK" with no SDK named is an invitation to invent a module and a 20B accepts it.

The mission stream โ€” --events

MissionRunner.run returns a transcript when the mission is over. That is the right shape for a terminal and the wrong shape for anything that has to show a mission to somebody while it runs โ€” a mission on a local 20B is minutes long, and a caller holding only run() has nothing to render for all of them.

So the loop takes an observer, and --events writes what it sees as NDJSON: one JSON object per line, flushed as it happens, UTF-8 and unescaped.

--events -        stdout, for a person with jq
--events fd:N     an inherited descriptor โ€” what a harness uses
--events PATH     a file, opened for append

stdout is prose for a person and must not be parsed. The event sink is the only machine channel, which is why a consumer uses fd: or a path and never -: the console rendering and the record stream never share bytes.

The vocabulary โ€” nine event types, their required and optional fields, the five outcome words, the exit contract, and the rule for what is a breaking change โ€” is CONTRACT.md, and its authority is core/runtime/contract.py. A consumer pins it:

from core.runtime import contract
assert contract.SCHEMA_VERSION == 1     # fails at import, which is cheap
problems = contract.conforms(record)    # [] when the record is fine

conforms is pure and standard-library only and imports nothing this repo owns, so a consumer that cannot import an agent framework can vendor that one file and have the whole seam.

--history โ€” a conversation, not a paragraph

--history FILE seeds prior turns into the model's message list as real role-tagged chat turns, ahead of the objective. The file is a JSON array of {"role": "user"|"assistant", "content": "..."}, oldest first; system is refused, because system text belongs to the harness and tool turns are this mission's own to make. Caps are 100 turns and 262,144 characters, and a malformed history is a refusal at the door rather than a silent drop โ€” a dropped history is the bug this flag fixes wearing a different hat.

A file rather than an argument, for the same reason --mcp-token prefers the environment: a conversation is many kilobytes and argv is world-readable in /proc/<pid>/cmdline.

A caller passing this must not also fold the history into the message. A chat-tuned model attends to role-tagged turns and skims past the same text pasted into the objective: measured 12 August 2026, "tell me more about #2" web-searched #2 literally while the list sat two lines up in the prompt.

Sampling โ€” stated, or the server's own

--temperature, --top-p and --seed are unset by default, and unset means unsent: the request carries no sampling parameters and the server's own default applies. That is deliberate. Pinning temperature=0 would make the agent easier to measure by making it a different agent โ€” it collapses the noise instead of measuring it, and a noise floor taken at a temperature nobody ships is not a floor. What was missing was never a temperature but the ability to state one and see what went out; "server default" is a setting nobody chose, and an upgrade can move it with nothing in any log. When one is passed, the CLI says so on the console and the value is on the wire.

--seed is not a determinism guarantee. A batching server can still vary.

A personality from a file

--personality <path> (or TAI_PERSONALITY, then ELF_PERSONALITY) loads a PersonalityConfig from TOML, JSON or YAML. The keys are that model's fields and nothing else โ€” an unknown key is refused by name. JudAIs and Lobi are unaffected.

tai resolves its own file instead of being handed one: $TAI_PERSONALITY, then $ELF_PERSONALITY, then the installed deployment package's own resource. Nothing else is consulted and nothing is invented โ€” the third outcome is a refusal naming what was checked. A guess that lands on the wrong checkout is worse than no guess, because it starts an agent whose stated rules are not the rules it loaded.

For platforms

If you are wiring this framework into a platform โ€” giving it a personality, giving it capabilities as MCP tools and a skill manifest, driving it as a subprocess and pinning a release โ€” that is its own guide: PLATFORMS.md. It covers the personality format and how to add a new named agent, the SKILL.md fields including sdk_import, the exact spawn shape, the release-and-pin loop, and the list of things that must never enter this repository. TAIPAN is the worked example throughout.

Extensibility

Judais-Lobi is designed to grow by adding workflows, tools, and policies without rewiring the kernel:

  • Add a new workflow by defining a WorkflowTemplate in core/kernel/workflows.py.
  • Add or consolidate tools via core/tools/descriptors.py and core/tools/.
  • Define stricter safety boundaries with core/policy/ profiles.
  • Extend evaluation logic under core/judge/ and core/critic/.

๐Ÿšง Current Status

v0.8.2 โ€” 2064 tests collected. Mission mode, skill manifests, the grounding validator, --swarm, the NDJSON mission stream and the published contract are all in this release. 0.8.2 opens the swarm's stream before triage, bounds the mission's conversation against the model's real context window (visible as compacted on step_started), gives every tool-result cut one owner (core/bounding.py), speaks to Mistral over httpx, and makes BwrapSandbox runnable (network per profile, rlimits applied, run_python under tmpfs). CONTRACT.md is the seam a consumer pins; PLATFORMS.md is how a platform deploys this framework as its own agent.

ROADMAP.md and PHASE_8.md are historical (Feb 2026): they describe the plan, not the code that is here now.

Completed

The counts below are the suite totals at the time each phase landed, kept as a record of how it grew. The current total is the one above.

  • โœ… Phase 0 โ€” Dependency Injection & Test Harness (73 tests)
  • โœ… Phase 1 โ€” Runtime extraction (provider separation, 107 tests)
  • โœ… Phase 2 โ€” Kernel State Machine & Hard Budgets (164 tests)
  • โœ… Phase 3 โ€” Session Artifacts, Contracts & KV Prefixing (269 tests)
  • โœ… Phase 4 โ€” Tool Bus, Sandboxing & Capability Gating (562 tests)
  • โœ… Phase 5 โ€” Repo Map & Context Compression (783 tests)
  • โœ… Phase 6 โ€” Repository-Native Patch Engine (888 tests)
  • โœ… Phase 7.0 โ€” Pluggable Workflows & State Machine Abstraction
  • โœ… Phase 7.1-7.2 โ€” Composite Judge & Candidate Sampling
  • โœ… Phase 7.3 โ€” External Critic
  • โœ… Phase 7.4 โ€” Campaign Orchestrator + StepPlan + EffectiveScope

Up Next

  • โœ… Phase 8a โ€” Local inference, a real MCP client, and file-loaded personalities
  • โณ Phase 8b โ€” Retrieval & context discipline

Phase 7 Highlights (7.0โ€“7.4)

Phase 7 turns the kernel into a workflow-driven, multi-candidate, multi-critic, campaign-capable system.

  • Pluggable workflows โ€” WorkflowTemplate makes phases, transitions, schemas, and capability profiles data-driven. CODING_WORKFLOW preserves Phase 6 behavior; GENERIC_WORKFLOW enables custom domains.
  • Deterministic scoring โ€” CompositeJudge sequences tests/lint/LLM review and scores candidate patches. CandidateManager evaluates N patch sets in isolated worktrees and picks the top non-failing result.
  • External Critic โ€” Optional frontier-model auditor (OpenAI/Anthropic/Google) for independent logic audits. Keyring/env key handling, multi-round feedback loop, noise detection, and SHA256 cache.
  • Campaign Orchestrator โ€” Tierโ€‘0 mission layer with HITL approval gates, step DAG execution, artifact handoff, and resumable progress.
  • StepPlan + EffectiveScope โ€” Step-level contracts and SHA256 ActionDigest; tool access enforced by Global โˆฉ Workflow โˆฉ Step โˆฉ Phase.

Outcome: workflows are composable, evaluation is deterministic, critics are optional, and campaigns provide a macro loop for multi-step missions.

Phase 6 Highlights

The agent can now reliably modify repository files through a deterministic, exact-match patch protocol with git worktree isolation and automatic rollback.

  • core/patch/parser.py โ€” Extracts <<<< SEARCH / ==== / >>>> REPLACE, <<<< CREATE / >>>> CREATE, and <<<< DELETE >>>> blocks from raw LLM text output. Delimiter-safe (only recognizes markers at line start). Path validation rejects absolute paths and .. traversal at parse time.
  • core/patch/matcher.py โ€” Exact byte-match with byte offsets and SHA256 context hashes. On zero matches: 3-stage similarity narrowing pipeline (indent filter โ†’ token overlap โ†’ SequenceMatcher ratio) returns top 3 candidate regions. On multiple matches: returns all offsets + context hashes for LLM disambiguation.
  • core/patch/applicator.py โ€” File writes with strict preconditions. Path jailing (symlink-escape resistant). \r\n โ†’ \n canonicalization. st_mode preservation (executables stay executable). Create fails if file exists; delete fails if file doesn't exist.
  • core/patch/worktree.py โ€” PatchWorktree manages git worktree lifecycle: create (explicit -b + HEAD), merge_back (--no-ff + branch cleanup), discard (force remove + branch delete). Writes .judais-lobi/worktrees/active.json for crash recovery of orphaned worktrees.
  • core/patch/engine.py โ€” PatchEngine orchestrates validate โ†’ apply โ†’ diff โ†’ merge/rollback. Stops at first file failure, leaving worktree intact for diagnostics. diff() returns real git diff from the worktree.
  • core/tools/patch_tool.py โ€” ToolBus-compatible 6-action tool (validate, apply, diff, merge, rollback, status). All actions return JSON stdout for machine-friendly kernel orchestration. exit_code=0 only on success.

12 tool descriptors. 105 new tests (888 total). 3 integration tests with real git repos. Worktree isolation means cross-file patches land atomically โ€” all succeed or discard for zero-cost rollback.

Phase 5 Highlights

The agent is now repo-aware. It understands structure, relationships, and what's irrelevant โ€” without eating the entire repo in context.

  • core/context/repo_map.py โ€” Top-level RepoMap orchestrator. Dual-use: overview mode (centrality-ranked for REPO_MAP phase) and focused mode (relevance-ranked by target_files for RETRIEVE phase). Lazy build with git-commit-keyed caching and dirty-file overlay.
  • core/context/symbols/ โ€” 3-tier symbol extraction: Python ast (full import + signature extraction), tree-sitter (7 languages: C, C++, Rust, Go, JS, TS, Java), regex fallback. get_extractor(language) factory auto-selects the best available.
  • core/context/graph.py โ€” DependencyGraph with multi-language module resolution (Python dotted paths, C #include, Rust use crate::, Go package imports, JS/TS relative imports with extension guessing). Relevance ranking (1.0/0.8/0.6/0.4/0.1 scoring by hop distance) and centrality ranking with barrel file damping (__init__.py, index.js, mod.rs).
  • core/context/formatter.py โ€” Compact tree-style formatting with token budget, optional char cap, whitespace normalization for deterministic output, and metadata header (file/symbol counts, languages, ranking mode).
  • core/context/visualize.py โ€” DOT (Graphviz) and Mermaid graph export with highlight styling and node cap.
  • core/context/cache.py โ€” Git-commit-keyed persistent cache at .judais-lobi/cache/repo_map/<hash>.json. Clean commit = full cache hit; dirty state = cache + re-extract only modified files.
  • core/tools/repo_map_tool.py โ€” ToolBus-compatible multi-action tool (build, excerpt, status, visualize).
  • setup.py โ€” pip install judais-lobi[treesitter] adds optional tree-sitter support via individual grammar packages.

11 tool descriptors (now 12 with Phase 6). 221 new tests. tree-sitter is optional โ€” the system works without it and gains rich multi-language AST parsing when installed.

Phase 4 Highlights

Tools are dumb executors behind a capability-gated bus. The kernel decides everything.

  • core/tools/bus.py โ€” Action-aware ToolBus with preflight hooks, panic switch integration, and JSONL audit logging. Structured JSON denial errors replace plain text.
  • core/tools/fs_tools.py โ€” Consolidated FsTool with 5 actions (read, write, delete, list, stat). Pure pathlib I/O, no subprocess.
  • core/tools/git_tools.py โ€” Consolidated GitTool with 12 actions (status, diff, log, add, commit, branch, push, pull, fetch, stash, tag, reset) via run_subprocess.
  • core/tools/verify_tools.py โ€” Config-driven VerifyTool (lint, test, typecheck, format). Reads .judais-lobi.yml for project-specific commands, falls back to sensible defaults.
  • core/tools/descriptors.py โ€” 11 tool descriptors, 13 named scopes + wildcard. Per-action scope resolution via action_scopes map.
  • core/tools/capability.py โ€” Deny-by-default CapabilityEngine with wildcard "*" support, profile switching, and grant revocation.
  • core/policy/profiles.py โ€” Four cumulative profiles: SAFE (read-only) โ†’ DEV (+ write) โ†’ OPS (+ deploy/network) โ†’ GOD (wildcard).
  • core/policy/god_mode.py โ€” GodModeSession with TTL auto-downgrade, panic switch (instant revocation to SAFE), and full audit trail.
  • core/policy/audit.py โ€” Append-only JSONL AuditLogger with regex-based secret redaction (OpenAI, GitHub, AWS, Slack tokens).
  • core/tools/sandbox.py โ€” NoneSandbox (dev/debug) and BwrapSandbox (Tier-1 production) behind a common SandboxRunner interface. BwrapSandbox keeps every field of the SandboxProfile it is given: the host root read-only with the working directory (and allowed_write_paths) re-bound writable, a private tmpfs /tmp, the network namespace unshared unless the profile says allow_network, and max_cpu_seconds / max_memory_bytes / max_processes applied as rlimits on the bwrap process and inherited by what runs inside it. NoneSandbox is still the default; it enforces nothing and says so.

3 consolidated multi-action tools replaced 21 separate descriptors. Git is the spine, not nice-to-have.


๐Ÿงญ Where To Look

If you are running this from another program, read:

  • ๐Ÿ“„ CONTRACT.md โ€” the mission stream, its events and the exit contract
  • ๐Ÿ“„ PLATFORMS.md โ€” deploying judais-lobi as a platform's agent

If you want to understand the plan it grew from, read:

  • ๐Ÿ“œ ROADMAP.md โ€” the Feb 2026 architectural blueprint. Historical

If you want to understand the current implementation, inspect:

  • core/agent.py โ€” concrete Agent class (replaced elf.py in Phase 3)
  • core/runtime/contract.py โ€” the seam a consumer pins, as data
  • core/runtime/mission.py, mission_stream.py, swarm.py โ€” the mission loop, its NDJSON account, and staged decomposition
  • core/runtime/skills.py โ€” the SKILL.md loader: closed tool set, prompt, grounding grammar, sdk_import
  • core/contracts/ โ€” Pydantic v2 contract models for all session data
  • core/sessions/ โ€” SessionManager for disk artifact persistence
  • core/kernel/ โ€” state machine, budgets, orchestrator, workflow templates (workflows.py)
  • core/cli.py โ€” CLI interface layer
  • core/memory/memory.py โ€” FAISS-backed long-term memory (numpy fallback if FAISS unavailable)
  • core/tools/ โ€” ToolBus, capability engine, sandbox, consolidated tools (fs, git, verify, repo_map, patch)
  • core/policy/ โ€” profiles, god mode, audit logging
  • core/context/ โ€” repo map extraction, dependency graph, symbol extractors (Python ast + tree-sitter + regex), formatting, caching, visualization
  • core/patch/ โ€” patch engine: parser, matcher, applicator, worktree manager, engine orchestrator
  • core/judge/ โ€” composite judge: tier scoring, candidate sampling, GPU profile stub
  • lobi/ and judais/ โ€” personality configs extending Agent

If you want to understand the entry point, see:

  • main.py
  • setup.py

๐Ÿ— Architectural Direction

The target architecture (from the roadmap) is:

  • Artifact-driven state (no conversational drift)
  • Three-tier orchestration: Campaign graph (Tier 0) โ†’ Workflow graph (Tier 1) โ†’ Phase-internal planning (Tier 2)
  • Pluggable workflows โ€” static templates for coding, red teaming, data analysis, and arbitrary tasks
  • Campaign orchestration โ€” multi-step missions with DAG decomposition, HITL approval gates, and artifact handoff (pre-authored plans)
  • Capability-gated tool execution with least-privilege by intersection (Global โˆฉ Workflow โˆฉ Step โˆฉ Phase)
  • Sandbox isolation (bwrap / nsjail)
  • Tests > Lint > LLM scoring hierarchy
  • GPU-aware orchestration (vLLM / TRT-LLM)
  • Optional external critic (frontier logic auditor)

The system is moving toward:

CLI (--task / --campaign / --campaign-plan / --workflow)
  โ†“
Campaign Orchestrator (Tier 0 โ€” optional, multi-step missions)
  โ†“  plan โ†’ HITL approve โ†’ dispatch โ†’ synthesis
Workflow Selector โ†’ WorkflowTemplate (Tier 1 โ€” static graph)
  โ†“
Kernel State Machine (phases, transitions, budgets)
  โ†“
Roles (Planner / Coder / Reviewer)
  โ†“
ToolBus โ†’ EffectiveScope check โ†’ Sandbox โ†’ Subprocess
  โ†“
Deterministic Judge (Tests > Lint > LLM)

As of Phase 7.4:

  • The kernel state machine is parameterized by WorkflowTemplate objects โ€” no hardcoded phase names, transitions, or branching rules. The coding pipeline is one template; custom domains define their own.
  • CODING_WORKFLOW and GENERIC_WORKFLOW are built-in templates. select_workflow() resolves by CLI flag, policy, or default.
  • Per-phase capability profiles (phase_capabilities) create temporal sandboxes โ€” PLAN can read but not write, PATCH can write but only through the patch engine.
  • Tools are dumb executors behind a sandboxed, capability-gated bus.
  • Every subprocess-based tool call flows through ToolBus โ†’ CapabilityEngine โ†’ SandboxRunner โ†’ Subprocess. Pure-Python tools are still gated by ToolBus but execute in-process. HUMAN_REVIEW uses $EDITOR directly (user-initiated TTY) and is an explicit exception.
  • Deny-by-default. No scope = no execution.
  • God mode exists for emergencies โ€” TTL-limited, panic-revocable, fully audited.
  • 5 consolidated multi-action tools (fs, git, verify, repo_map, patch) cover 31 operations under 13 scopes.
  • The agent sees repo structure via a token-budgeted excerpt โ€” file paths, symbol signatures, and dependency-ranked relevance โ€” without loading full source.
  • 3-tier symbol extraction: Python ast โ†’ tree-sitter (7 languages) โ†’ regex fallback. Multi-language dependency graph with import resolution.
  • Code modifications use an exact-match patch protocol with git worktree isolation. Cross-file changes land atomically. Failed patches roll back at zero cost.
  • Patches are scored by a deterministic CompositeJudge (Tests > Lint > LLM review). CandidateManager evaluates N candidate patches in isolated worktrees and selects the winner by composite score.
  • Campaign Orchestrator provides a Tier 0 macro loop with HITL approval, step DAG execution, and explicit artifact handoff.
  • StepPlan contracts lock intent, boundaries, and capability needs per step with a SHA256 ActionDigest.
  • EffectiveScope intersection (Global โˆฉ Workflow โˆฉ Step โˆฉ Phase) is enforced per tool call.
  • Context window manager keeps prompts within model limits, auto-compacts history, and stores oversized tool output to disk with a retrieval hint.

Local inference has landed (--provider local). Phase 8b focuses on retrieval discipline. See ROADMAP.md.

The kernel is the only intelligence. Tools report. The kernel decides.


๐Ÿง  Memory System (Current)

Long-term memory uses:

  • SQLite-backed JSON persistence
  • FAISS vector index (numpy fallback when FAISS is unavailable)
  • OpenAI embeddings (currently)

See: core/memory/memory.py

This will be abstracted for local embeddings in later phases.

Short-term history remains for direct chat mode. Direct CLI tool calls still route through ToolBus (with a permissive default policy unless a policy pack is supplied). Agentic mode uses session artifacts as the sole source of truth (Phase 3).


๐Ÿงฐ Context Window & Tool Output

Judais-Lobi tracks context window limits per model/provider, auto-compacts history when needed, and never drops oversized tool output. Full logs are written to disk with a retrieval hint in the prompt.

Config (project-level) in .judais-lobi.yml:

context:
  max_context_tokens: 32768
  max_output_tokens: 4096
  max_tool_output_bytes_in_context: 32768
  min_tail_messages: 6
  max_summary_chars: 2400
  provider_defaults:
    openai: 128000
    mistral: 32768
    local: 32768
  model_overrides:
    gpt-4o: 128000
    codestral-latest: 32768

๐Ÿ›  Current Capabilities

Direct mode still works.

lobi "explain this function"
lobi --shell "list files"
lobi --python "plot sine wave"
lobi --search "latest linux kernel"
lobi --research "linux kernel LTS release timeline"
lobi --research --academic "transformer sparsity survey 2023"
lobi --install-project

JudAIs:

judais "analyze this target" --shell

Voice (optional extra):

pip install judais-lobi[voice]
lobi "sing" --voice

๐Ÿงช Install

pip install judais-lobi                 # the base install
pip install -e '.[mission]'             # from a checkout, with everything a mission needs

Requires:

  • Python 3.10+ (setup.py's floor; a TOML personality on 3.10 also needs tomli)
  • A model to talk to: an API key for a hosted provider, or an OpenAI-compatible endpoint for --provider local
  • Linux recommended

Every optional stack is an extra, not a requirement โ€” a plain install stays small enough that judais --help works without any of them, and the SDK an extra pulls in is imported lazily.

extra what it adds
mission mcp + pyyaml โ€” what a governed mission actually needs. This is the one a platform installs
mcp the MCP client alone. Enough to run a mission, not enough to govern one
critic the external frontier-model critic, and pyyaml
treesitter multi-language symbol extraction for the repo map
faiss the FAISS vector index for long-term memory. Without it memory still works, on the numpy index in core/memory/memory.py
voice TTS
dev pytest and coverage

Set an API key:

export OPENAI_API_KEY=sk-...

Or create:

~/.elf_env

๐Ÿ” API Keys & Model APIs

Judais-Lobi uses API keys from your environment or your system keyring. Keys are never stored in config files.

Environment variables (fallbacks):

  • OPENAI_API_KEY โ€” OpenAI (builder + optional critic)
  • ANTHROPIC_API_KEY โ€” Anthropic critic (optional)
  • GOOGLE_API_KEY โ€” Google/Gemini critic (optional)

Keyring (preferred, optional):

  • Service: judais-lobi
  • Keys: openai_api_key, anthropic_api_key, google_api_key

Model API configuration (critic only):

  • User defaults: ~/.judais-lobi/critic.yml
  • Project overrides: .judais-lobi.yml under critic:

Example critic.yml:

enabled: true
providers:
  - provider: openai
    model: gpt-4o
  - provider: anthropic
    model: claude-sonnet-4-20250514

๐Ÿ”ฎ What This Is Becoming

Judais-Lobi is not trying to be:

  • Another chat wrapper
  • Another SaaS IDE
  • Another prompt toy

It is attempting to become:

  • A local-first agentic execution kernel (not just developer โ€” any structured task domain)
  • Deterministic and replayable
  • Hardware-aware
  • Capability-constrained (least-privilege by intersection)
  • Mission-capable (campaign orchestration with HITL approval gates)
  • Air-gap ready

The design philosophy is explicit in ROADMAP.md :

  • Artifacts over chat
  • Budgets over infinite loops
  • Capabilities over trust
  • Capabilities over tools (stable tags, not tool names)
  • Plans over prompts (structured DAGs, not freestyle LLM loops)
  • Static graphs, adaptive phases (three-tier orchestration)
  • Dumb tools, smart kernel
  • Commit or abort

That last one matters.

There will not be two systems of truth.


๐Ÿง  Philosophy

Lobi sings. JudAIs calculates.

But the system beneath them is becoming something else:

A disciplined orchestration engine for machine reasoning.

The aesthetic may be mythic. The architecture is not.


โญ Contributing

If you are contributing:

  1. Read the roadmap.
  2. Understand the phase ordering.
  3. Do not bypass tool execution through direct subprocess calls.
  4. Every structural change must preserve deterministic replay.
  5. New functionality goes through Agent + contracts, not ad-hoc methods.

This is an architectural project, not a feature factory.


๐Ÿงพ License

GPLv3 โ€” see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

judais_lobi-0.8.2.tar.gz (502.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

judais_lobi-0.8.2-py3-none-any.whl (318.1 kB view details)

Uploaded Python 3

File details

Details for the file judais_lobi-0.8.2.tar.gz.

File metadata

  • Download URL: judais_lobi-0.8.2.tar.gz
  • Upload date:
  • Size: 502.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for judais_lobi-0.8.2.tar.gz
Algorithm Hash digest
SHA256 50fc82bf724346f5ac0ba41002fd5215617249ff1548053fb5b90cb626b80206
MD5 f3ca03794141b0a5b259e6993684db15
BLAKE2b-256 8bce183ab8b6d3f84dbade6a4c803d3fbf42ec3b3400d9a1d9da0ef61fadccf3

See more details on using hashes here.

File details

Details for the file judais_lobi-0.8.2-py3-none-any.whl.

File metadata

  • Download URL: judais_lobi-0.8.2-py3-none-any.whl
  • Upload date:
  • Size: 318.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for judais_lobi-0.8.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1c32b12b4b958d6264339f50dfdfba4fc5c679c1e5a139d903c18e105e1c9ddb
MD5 0c63666f7e6a05571383a2c1f1f0ee6b
BLAKE2b-256 cdb1e906d65b866c63ab21a8df80dfc2a34ff8c07511be8e5bc778e36fc2d0f3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page