Skip to content

Independent Java Consumer

Use the published com.axisvela:praxo-core:0.9.0 artifact from Maven Central in your own application. Java 21 or newer and Maven 3.9 or newer are required. No PRAXO-CORE source checkout, API key, model service, or AxisVela product is needed. Installation needs Maven Central access or a populated local repository.

Choose the Boundary

This fits 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 pom.xml:

<dependency>
  <groupId>com.axisvela</groupId>
  <artifactId>praxo-core</artifactId>
  <version>0.9.0</version>
</dependency>

Do not use a system scope, a local JAR path, or a substitute build of the repository source. The tests verify the SDK version and that Version loaded from the published praxo-core-0.9.0.jar.

Application Source

Use the following as src/main/java/starter/App.java, rendered directly from the tested source.

package starter;

import com.axisvela.praxo.PraxoException;
import com.axisvela.praxo.agent.Agent;
import com.axisvela.praxo.agent.AgentOptions;
import com.axisvela.praxo.agent.Session;
import com.axisvela.praxo.model.Message;
import com.axisvela.praxo.model.Model;
import com.axisvela.praxo.model.Request;
import com.axisvela.praxo.model.Response;
import com.axisvela.praxo.model.ToolCall;
import com.axisvela.praxo.model.ToolSpec;
import com.axisvela.praxo.policy.Action;
import com.axisvela.praxo.policy.Decision;
import com.axisvela.praxo.tool.Registry;
import com.axisvela.praxo.tool.Tool;

import java.io.PrintStream;
import java.util.List;
import java.util.Map;

public final class App {
    private App() {
    }

    static final class FixtureModel implements Model {
        private final String target;

        FixtureModel(String target) {
            this.target = target;
        }

        @Override
        public Response generate(Request request) {
            Message last = request.messages().get(request.messages().size() - 1);
            if (Message.TOOL.equals(last.role())) {
                return Response.of(Message.assistant("recorded", List.of()));
            }
            ToolCall call = new ToolCall("record-1", "record", Map.of("target", target));
            return Response.of(Message.assistant("", List.of(call)));
        }
    }

    static final class RecordTool implements Tool {
        private int calls;

        @Override
        public String name() {
            return "record";
        }

        @Override
        public String description() {
            return "Record a target in an in-memory counter.";
        }

        @Override
        public Object invoke(Map<String, Object> args) {
            calls += 1;
            return "recorded";
        }

        public ToolSpec spec() {
            return new ToolSpec(
                    "record",
                    description(),
                    Map.of(
                            "type", "object",
                            "required", List.of("target"),
                            "properties", Map.of("target", Map.of("type", "string"))),
                    "write");
        }

        int calls() {
            return calls;
        }
    }

    public record Built(Agent agent, RecordTool tool) {
    }

    static Built buildAgent(String target) {
        Registry tools = new Registry();
        RecordTool tool = new RecordTool();
        tools.register(tool);
        AgentOptions options = new AgentOptions(new FixtureModel(target));
        options.tools = tools;
        options.maxSteps = 3;
        options.allowedTools = List.of("record");
        options.policy = (Action action) -> {
            if ("model.generate".equals(action.kind())) {
                return Decision.allow("model generation");
            }
            if ("tool.invoke".equals(action.kind())) {
                Object value = action.arguments() == null
                        ? null
                        : action.arguments().get("target");
                return "record".equals(action.toolName()) && "public".equals(value)
                        ? Decision.allow("public target")
                        : Decision.deny("only the public target is permitted");
            }
            return Decision.deny("unsupported action");
        };
        return new Built(new Agent(options), tool);
    }

    public static void run(PrintStream out) throws Exception {
        Built allowed = buildAgent("public");
        var result = allowed.agent().run(
                new Session("starter-public"), "Record the requested target.");
        if (!"recorded".equals(result.output) || allowed.tool().calls != 1) {
            throw new IllegalStateException(
                    "allow verification failed: output=" + result.output
                            + " effects=" + allowed.tool().calls);
        }
        out.println("allow: recorded; effects=1");

        Built denied = buildAgent("private");
        try {
            denied.agent().run(
                    new Session("starter-private"), "Record the requested target.");
        } catch (PraxoException error) {
            if (!"AGENT_POLICY_DENIED".equals(error.code)) {
                throw error;
            }
            if (denied.tool().calls != 0) {
                throw new IllegalStateException(
                        "denied call produced effects=" + denied.tool().calls);
            }
            out.println("deny: policy rejected; effects=0");
            return;
        }
        throw new IllegalStateException("private target was not denied");
    }

    public static void main(String[] args) throws Exception {
        run(System.out);
    }
}

Acceptance Tests

Use the following as src/test/java/starter/AppTest.java, rendered from the tested source.

package starter;

import com.axisvela.praxo.PraxoException;
import com.axisvela.praxo.Version;
import com.axisvela.praxo.agent.Session;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class AppTest {
    @Test
    void allowedToolRunsOnce() throws Exception {
        var built = App.buildAgent("public");
        var result = built.agent().run(
                new Session("test-public"), "Record the requested target.");
        assertEquals("recorded", result.output);
        assertEquals(1, built.tool().calls());
    }

    @Test
    void deniedToolHasNoEffect() {
        var built = App.buildAgent("private");
        PraxoException error = assertThrows(PraxoException.class, () -> built.agent().run(
                new Session("test-private"), "Record the requested target."));
        assertEquals("AGENT_POLICY_DENIED", error.code);
        assertEquals(0, built.tool().calls());
    }

    @Test
    void starterOutputIsDeterministic() throws Exception {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        try (PrintStream out = new PrintStream(buffer, true, StandardCharsets.UTF_8)) {
            App.run(out);
        }
        String expected = "allow: recorded; effects=1" + System.lineSeparator()
                + "deny: policy rejected; effects=0" + System.lineSeparator();
        assertEquals(expected, buffer.toString(StandardCharsets.UTF_8));
    }

    @Test
    void dependencyComesFromPublishedJar() throws Exception {
        assertEquals("0.9.0", Version.SDK_VERSION);
        URL location = Version.class.getProtectionDomain().getCodeSource().getLocation();
        String filename = Path.of(location.toURI()).getFileName().toString();
        assertEquals("praxo-core-0.9.0.jar", filename);
        assertTrue(location.toURI().toString().contains("/com/axisvela/praxo-core/0.9.0/"));
    }
}

Verify

mvn -B test
mvn -q -B exec:java

Expected program output:

allow: recorded; effects=1
deny: policy rejected; effects=0

The model is a deterministic fixture, not an LLM. allowedTools limits the tool name; Policy also checks the target argument. A denied call raises PraxoException with code AGENT_POLICY_DENIED and must not increment the in-memory counter. An empty allowlist is not deny-all in the current API. Keep these tests when replacing the fixture with a real model adapter.

Governed Acceptance

GovernedAcceptanceTest adds five negative checks:

mvn -B -Dtest=GovernedAcceptanceTest test
Check Required outcome
Missing mandatory policy application guard rejects before a run; zero tool effects
Empty tool scope application guard rejects; zero tool effects
Middleware changes final arguments final operation is reauthorized; denial prevents the effect
Approval edit changes arguments edited operation is reauthorized; denial prevents the effect
Policy service error run fails closed; zero tool effects

Use the tested source. The guard is application-level: Core keeps its ordinary nil-policy behavior. These checks do not prove authentic identity, durable approvals, effect reconciliation, or a general security profile.

These checks establish only the stated artifact and fixture behavior, not live model quality, authentic identity, isolation, durable effects, or production safety. Continue with the Java implementation guide and the security model.