logos

package module
v0.4.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2026 License: MIT Imports: 10 Imported by: 1

README

logos

Stateless agent loop for Go. LLMs think in plain text and act via $ prefixed shell commands — no tool schemas, no JSON.

prompt → LLM → scan for "$ command" → execute in sandbox → feed output back → repeat

Install

go get github.com/tta-lab/logos

Usage

result, err := logos.Run(ctx, logos.Config{
    Provider:     provider,        // fantasy.Provider (LLM abstraction)
    Model:        "claude-sonnet-4-6",
    SystemPrompt: systemPrompt,
    Temenos:      temenosClient,   // sandboxed command runner
    SandboxEnv:   map[string]string{"HOME": "/app"},
    AllowedPaths: []client.AllowedPath{
        {Path: "/app", Permission: "rw"},
    },
}, history, "read main.go and explain what it does", logos.Callbacks{
    OnDelta: func(text string) {
        fmt.Print(text) // stream to terminal
    },
    OnCommandStart: func(cmd string) {
        fmt.Printf("\n> %s\n", cmd)
    },
})

The LLM responds in plain text. When it wants to act, it writes a $ line:

Let me check the file structure first.

$ ls -la /app

logos detects the command, executes it in a temenos sandbox, and feeds the output back as the next user message. The loop continues until the LLM responds without a command.

How it works

  1. Run() takes config, conversation history, a prompt, and streaming callbacks
  2. Each turn, the LLM streams a response
  3. scanForCommand() finds the first $ line — one command per turn
  4. The command runs via the CommandRunner interface (temenos sandbox)
  5. Output becomes the next user message; loop repeats
  6. When the LLM responds with no command, the loop ends and returns RunResult

Key types

Type Purpose
Config Provider, model, temenos client, sandbox env, allowed paths
RunResult Final response text + all step messages
StepMessage One message in the loop (assistant text or command output)
Callbacks Optional OnDelta and OnCommandStart streaming hooks
CommandRunner Interface for command execution — temenos satisfies it

System prompt

BuildSystemPrompt() renders an embedded template with runtime context (working dir, platform, date, available commands). Consumers typically append their own instructions after the base prompt:

base, _ := logos.BuildSystemPrompt(logos.PromptData{
    WorkingDir: "/app",
    Platform:   "linux",
    Date:       "2026-03-16",
    Commands:   availableCommands,
})
systemPrompt := base + "\n\n" + customInstructions

Design

  • StatelessRun() takes history in, returns steps out. The caller owns persistence.
  • One command per turn — finds the first $ line and stops; text after is ignored.
  • Sandboxed — commands execute in temenos, not on the host.
  • Provider-agnostic — uses fantasy for LLM abstraction.

Dependencies

  • fantasy — LLM provider abstraction (streaming, messages)
  • temenos — sandboxed command execution daemon

License

MIT

Documentation

Overview

Package logos provides a reusable stateless agent loop.

Run() executes one agent loop iteration: prompt → LLM → tool calls → response. The caller provides conversation history, a system prompt, tools, and an optional sandbox env. No persistence — the caller receives StepMessages and handles storage.

Plane: shared

Index

Constants

View Source
const DefaultMaxSteps = 30

DefaultMaxSteps is the fallback max steps when Config.MaxSteps is 0.

View Source
const DefaultMaxTokens = 16384

DefaultMaxTokens is the fallback max output tokens when Config.MaxTokens is 0.

View Source
const HardExitThreshold = 20

HardExitThreshold is the number of consecutive command-only turns that triggers a hard exit — the agent is in a degenerate loop.

View Source
const MaxXMLRetries = 2

MaxXMLRetries is the number of times the loop will inject error feedback when a model outputs XML tool_call format instead of $ commands.

View Source
const SoftWarningThreshold = 10

SoftWarningThreshold is the number of consecutive command-only turns before the loop injects a nudge asking the agent to explain progress.

Variables

This section is empty.

Functions

func BuildSystemPrompt

func BuildSystemPrompt(data PromptData) (string, error)

BuildSystemPrompt renders the default system prompt with runtime context. The result is the base prompt — consumers append their own instructions after this.

func ContainsXMLToolCall added in v0.3.4

func ContainsXMLToolCall(text string) bool

ContainsXMLToolCall returns true if text contains XML tool_call patterns produced by models that default to structured format (e.g. minimax). Used to detect wrong output format and trigger error feedback.

Types

type AllowedPath added in v0.3.0

type AllowedPath = client.AllowedPath

AllowedPath specifies a filesystem path allowed in the sandbox.

type Callbacks

type Callbacks struct {
	// OnDelta is called with each text delta as the LLM streams its response.
	OnDelta func(text string)
	// OnCommandStart is called when a $ command is detected, before execution.
	OnCommandStart func(command string)
	// OnCommandResult is called after a command executes with the command string,
	// raw combined stdout+stderr output (no exit code suffix), and the exit code.
	// exitCode is -1 if the sandbox itself failed to execute the command (temenos
	// transport error), in which case output contains the error description.
	OnCommandResult func(command string, output string, exitCode int)
}

Callbacks holds optional streaming callbacks for the agent loop. All fields are nil-safe — unset callbacks are simply not called.

type Command

type Command struct {
	Raw  string // full original line (e.g. "$ ls -la")
	Args string // everything after "$ " (e.g. "ls -la")
}

Command represents a parsed $ command from assistant output.

func ParseCommand

func ParseCommand(line string) (Command, bool)

ParseCommand checks if a line is a $ command. Returns the command and true if the line starts with "$ ". Returns zero Command and false otherwise.

type CommandRunner

type CommandRunner interface {
	Run(ctx context.Context, req RunRequest) (*RunResponse, error)
}

CommandRunner executes a sandboxed command and returns the result. *client.Client satisfies this interface automatically.

func NewClient added in v0.3.0

func NewClient(addr string) (CommandRunner, error)

NewClient creates a CommandRunner connected to a temenos daemon. addr formats:

  • Empty string: resolve from TEMENOS_LISTEN_ADDR → TEMENOS_SOCKET_PATH → default socket
  • Starts with "/" or ".": unix socket path
  • Starts with "http://": HTTP base URL (TCP)
  • Otherwise (e.g. ":8081", "localhost:8081"): TCP

type Config

type Config struct {
	Provider     fantasy.Provider
	Model        string
	SystemPrompt string
	MaxSteps     int // 0 means use default (DefaultMaxSteps)
	MaxTokens    int // 0 means use default (DefaultMaxTokens)
	Temenos      CommandRunner
	SandboxEnv   map[string]string // env vars passed to temenos per-request
	// AllowedPaths lists filesystem paths accessible during command execution.
	// Path validation (non-empty, absolute) is enforced by the temenos daemon.
	AllowedPaths []AllowedPath
}

Config holds everything needed to run one agent loop iteration.

type PromptData

type PromptData struct {
	WorkingDir string
	Platform   string
	Date       string
	Network    bool // include read-url + search docs
	ReadFS     bool // include rg + read-only filesystem docs
}

PromptData holds the runtime context used to render the default system prompt.

type RunRequest added in v0.3.0

type RunRequest = client.RunRequest

RunRequest is the request payload for sandboxed command execution.

type RunResponse added in v0.3.0

type RunResponse = client.RunResponse

RunResponse is the response from sandboxed command execution.

type RunResult

type RunResult struct {
	Response string        // final text response (accumulated assistant text)
	Steps    []StepMessage // all messages generated (for persistence by caller)
}

RunResult contains the agent's output after a loop completes.

func Run

func Run(
	ctx context.Context,
	cfg Config,
	history []fantasy.Message,
	prompt string,
	cbs Callbacks,
) (*RunResult, error)

Run executes the agent loop: prompt → LLM → $ commands → repeat. Stateless — the caller handles conversation persistence.

type StepMessage

type StepMessage struct {
	Role      StepRole
	Content   string
	Timestamp time.Time
}

StepMessage represents one message generated during the agent loop.

type StepRole

type StepRole string

StepRole represents the role of a message step in the agent loop.

const (
	StepRoleAssistant StepRole = "assistant"
	StepRoleUser      StepRole = "user"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL