oriagent

package module
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: EUPL-1.2 Imports: 41 Imported by: 0

README

ori-agent

CI Go Reference Licence EUPL-1.2

ori-agent est un framework Go pour créer des applications de bureau qui dialoguent avec un agent (un LLM, un assistant scripté, un moteur de règles…), dans le style de la vue « work » du client Claude Code. Vous écrivez l'agent, ori-agent fournit toute l'interface. Les couleurs suivent le design system Ori des services publics de Polynésie française.

Anna Assistant, l'exemple complet, en thème sombre

Ce que vous obtenez

  • Une fenêtre complète : barre de titre, arborescence projets → sessions → sous-agents, fil de conversation, zone de saisie.
  • Des projets avec leur propre dossier et leurs autorisations, que l'utilisateur crée, modifie et supprime depuis la barre latérale (votre application décide de ce que cela fait sur le disque).
  • Un fil riche : réponses en Markdown, réflexion du modèle en streaming, appels d'outils dépliables, listes de tâches, bouton Stop.
  • Une palette de commandes /, extensible avec vos propres commandes, et la flèche haut pour rappeler ce qui a été envoyé.
  • Un aperçu de documents à droite : Markdown, code, PDF et images.
  • Des pièces jointes, une dictée vocale (moteur à brancher), un minuteur Pomodoro.
  • Des pages Configuration et Profil, dont votre application enregistre les valeurs (réglages, profil, connexion et déconnexion), et vos propres pages.
  • Une interface en français et en anglais, extensible à d'autres langues, avec changement en direct.
  • Un thème clair et sombre, remplaçable par un autre design system.
  • Windows, macOS et Linux, en Go pur sous Windows.

Installation

Il faut Go 1.26 ou plus récent. Sous Linux et macOS, voir les prérequis système.

go get github.com/sitle/ori-agent@latest

Pour voir le résultat tout de suite :

git clone https://github.com/sitle/ori-agent
cd ori-agent
go run ./examples/anna

Démarrage en 20 lignes

package main

import (
	"context"

	oriagent "github.com/sitle/ori-agent"
)

func main() {
	oriagent.Run(oriagent.Config{
		AppName: "Mon assistant",
		Agent: oriagent.AgentFunc(func(ctx context.Context, t *oriagent.Turn) error {
			b := t.Text()
			defer b.Done()
			b.Write("Vous avez écrit : " + t.Prompt)
			return nil
		}),
	})
}

L'agent reçoit chaque message dans un Turn et y écrit sa réponse. Le reste (fenêtre, sessions, commandes, aperçu, langues) est pris en charge.

Documentation

Guide Pour…
Prise en main Construire pas à pas sa première application : agent, commande, page, traductions
Écrire un agent Brancher un LLM ou tout autre moteur : streaming, outils, tâches, sous-agents, annulation
Personnaliser l'application Toutes les options de Config : commandes, pages, modèles, profil, fonctionnalités
Langues de l'interface Changer de langue, traduire ses textes, ajouter une langue
Composants et thèmes Utiliser le kit ui seul, changer de design system, l'aperçu de documents
Architecture Comprendre le fonctionnement interne, avant de contribuer
Référence de l'API Les types et fonctions, avec des exemples

Vous utilisez un assistant de code (Claude Code, Codex, Cursor…) ? Le dépôt fournit un llms.txt et une skill prête à l'emploi : voir Utiliser ori-agent avec un assistant de code.

Exemples

  • examples/minimal : un agent « écho » d'une quarantaine de lignes.
  • examples/anna : « Anna Assistant », la démonstration complète. Elle comprend un agent simulé, des données de démo, une page et des commandes en plus (dont /login) et des traductions. Elle enregistre aussi les réglages, le profil et les projets créés dans le dossier de configuration de l'utilisateur (store.go).
go run ./examples/anna -lang en -light

Autres options : -view setup -step 3, -view profile, -view shortcuts, -attach "fichier1,fichier2", -data dossier (où enregistrer les données d'Anna).

Thème clair Palette de commandes
Thème clair Palette de commandes

Plateformes

  • Windows : aucune dépendance native. Pour un exécutable sans console : go build -ldflags=-H=windowsgui.

  • macOS : Xcode Command Line Tools (xcode-select --install).

  • Linux (Debian, Ubuntu) :

    sudo apt install gcc pkg-config libwayland-dev libx11-dev libx11-xcb-dev libxkbcommon-x11-dev libgles2-mesa-dev libegl1-mesa-dev libffi-dev libxcursor-dev libvulkan-dev
    

L'intégration continue compile et teste sur les trois systèmes ; l'interface n'a été essayée à la main que sous Windows.

Statut et limites

Le projet est expérimental (version 0.x) : l'API peut encore changer d'une version à l'autre, voir le journal des modifications.

  • ori-agent n'enregistre rien lui-même : il transmet les réglages, le profil et les projets à votre application, qui choisit où les garder. Les sessions et leurs conversations restent en mémoire.
  • Fenêtre sans décoration native : sous macOS, les pastilles de fenêtre disparaissent.
  • Pas de glisser-déposer de fichiers depuis le système (non pris en charge par Gio v0.8).
  • Le Markdown couvre l'essentiel (titres, listes, citations, code, tableaux, gras, italique, liens). Le texte du fil ne se sélectionne pas : il se copie avec les boutons « Copier ».

Contribuer

Les contributions sont les bienvenues : voir CONTRIBUTING.md.

Licence

Copyright © 2026 Leonard TAVAE et les contributeurs d'ori-agent.

Publié sous la licence EUPL-1.2 (European Union Public Licence) : les versions modifiées et redistribuées doivent rester sous une licence compatible.

Le dépôt inclut des éléments tiers, qui restent sous leurs propres conditions :

  • le logo de la Polynésie française (examples/anna/assets/logo-pf.svg), repris d'ori.gov.pf pour l'exemple ;
  • les couleurs du design system Ori (paquet theme) ;
  • les dépendances Go, dont Gio (MIT/Unlicense), go-pdfium (MIT, avec PDFium sous BSD/Apache), wazero (Apache-2.0), zenity (MIT) et oksvg (BSD-3).

Documentation

Overview

Package oriagent is an application shell for agent clients, in the style of the Claude Code desktop "work" view: custom title bar, sidebar tree of projects → sessions → subagents, chat transcript with streamed reasoning, tool calls and task lists, composer with slash-command palette, voice dictation and attachments, resizable document preview, Pomodoro timer, setup and profile pages. Colors follow the Ori design system.

A project plugs its own behavior through Config: an Agent that writes each turn, extra slash Commands, extra Pages, models, user, demo data.

oriagent.Run(oriagent.Config{
	AppName: "Mon assistant",
	Agent:   myAgent,
})

It runs on Windows, macOS and Linux without CGO on Windows; macOS and Linux need the usual Gio toolchain.

Index

Examples

Constants

View Source
const MaxUpload = 20 << 20

MaxUpload is the size limit for attachments.

Variables

View Source
var Catalogs = map[string]map[string]string{"fr": catalogFR, "en": catalogEN}

Catalogs holds the built-in interface strings, by locale then key. To add a language, give Config.Translations a catalog with the same keys ("lang.name" is the language's own name, shown in the language picker); missing keys fall back to Config.FallbackLocale.

View Source
var ErrSessionBusy = errors.New("oriagent: a turn is already running in this session")

ErrSessionBusy is returned by Headless.Run when a turn is already running in the session.

Functions

func HumanSize

func HumanSize(n int64, tr i18n.Translator) string

HumanSize formats a byte count in the language of tr ("1,2 Mo", "1.2 MB"); a Turn or Ctl is a translator. With tr nil, units are in English.

func NewSessionID added in v0.2.3

func NewSessionID() string

NewSessionID returns a random identifier, as given to new sessions.

func Run

func Run(cfg Config)

Run opens the window and blocks; the process exits when it is closed.

Example

The smallest application: an agent that answers with one streamed block.

package main

import (
	"context"

	oriagent "github.com/sitle/ori-agent"
)

func main() {
	oriagent.Run(oriagent.Config{
		AppName: "Mon assistant",
		Agent: oriagent.AgentFunc(func(ctx context.Context, t *oriagent.Turn) error {
			b := t.Text()
			defer b.Done()
			b.Write("Vous avez écrit : " + t.Prompt)
			return nil
		}),
	})
}

Types

type Agent

type Agent interface {
	Run(ctx context.Context, t *Turn) error
}

Agent produces the assistant side of a conversation turn: an LLM client, a scripted simulation, a rules engine… Run is called on its own goroutine and writes to the transcript through t. It should return promptly once ctx is canceled (the user pressed Stop).

type AgentFunc

type AgentFunc func(ctx context.Context, t *Turn) error

AgentFunc adapts a function to the Agent interface.

Example

An agent uses every part of the transcript: reasoning, a task list, tool calls (with a file link) and a final answer. It stops when ctx is canceled.

package main

import (
	"context"
	"os"
	"path/filepath"
	"strings"

	oriagent "github.com/sitle/ori-agent"
)

func main() {
	agent := oriagent.AgentFunc(func(ctx context.Context, t *oriagent.Turn) error {
		if t.Thinking { // extended thinking enabled in the settings
			th := t.Think()
			th.Write("I will list the folder first.")
			th.Done()
		}
		tasks := t.Tasks("List files", "Answer")

		entries, err := os.ReadDir(t.WorkDir)
		if err != nil {
			return err // shown in red in the transcript
		}
		call := t.Tool("Bash", "ls")
		var names []string
		for _, e := range entries {
			names = append(names, e.Name())
		}
		call.Done(strings.Join(names, "\n"))
		tasks.Advance(0)

		if err := ctx.Err(); err != nil { // the user pressed Stop
			return err
		}
		t.Tool("Read", "go.mod").File(filepath.Join(t.WorkDir, "go.mod")).Done("module …")

		b := t.Text()
		b.Write("The folder has " + itoa(len(entries)) + " entries.")
		b.Done()
		tasks.Set(1, oriagent.TaskDone)
		return nil
	})
	oriagent.Run(oriagent.Config{Agent: agent})
}

func itoa(n int) string {
	if n == 0 {
		return "0"
	}
	var b []byte
	for ; n > 0; n /= 10 {
		b = append([]byte{byte('0' + n%10)}, b...)
	}
	return string(b)
}

func (AgentFunc) Run

func (f AgentFunc) Run(ctx context.Context, t *Turn) error

type App

type App struct {
	// contains filtered or unexported fields
}

App is the running shell. All its state is guarded by mu: the frame handler holds it, background work goes through update.

type ApprovalRequest added in v0.2.3

type ApprovalRequest struct {
	Title  string // what the agent wants to do, e.g. "Modifier main.go"
	Detail string // the command or other detail, shown as code
	// With Diff, the change from Old to New is shown.
	Diff     bool
	Old, New string
	// AllowSession offers "Allow for this session" besides allow and deny.
	AllowSession bool
	// AllowLabel and DenyLabel replace "Allow" and "Deny" on the buttons
	// ("Run the plan", "Keep planning"); they may be translation keys.
	AllowLabel, DenyLabel string
	// Markdown shows Detail as formatted text (a plan) rather than code.
	Markdown bool
}

ApprovalRequest is what an agent asks the user to allow: a command to run, a file to change… ori-agent shows it and collects the answer; the agent decides what needs approval.

type Attachment

type Attachment struct {
	Path, Name, Ext string // Ext is lower case, without the dot
	Size            int64
	ImageSize       image.Point // for images, once decoded
	// contains filtered or unexported fields
}

func NewAttachment

func NewAttachment(path string) (*Attachment, error)

NewAttachment describes an existing file as an already uploaded attachment.

func (*Attachment) IsImage

func (t *Attachment) IsImage() bool

type Block

type Block struct {
	// contains filtered or unexported fields
}

Block is a text or reasoning block being streamed.

func (*Block) Done

func (b *Block) Done()

func (*Block) Write

func (b *Block) Write(s string)

Write appends streamed text.

type C

type C = layout.Context

type Command

type Command struct {
	Name, Args, Desc string
	Run              func(c *Ctl, args string) string
	Markdown         bool
}

Command is a slash command. Desc and Args may be translation keys. Args is a usage hint shown in the palette: "<x>" means required (picking the command only completes it), "[x]" optional. Run returns the text logged in the transcript ("" logs nothing); with Markdown, that text is drawn as Markdown (titles, lists, bold, links), else as fixed-width lines, which keep their alignment.

Example

A slash command acts on the application through Ctl. Desc can be a translation key or plain text.

package main

import (
	oriagent "github.com/sitle/ori-agent"
)

func main() {
	hello := &oriagent.Command{
		Name: "hello",
		Args: "[name]",
		Desc: "Say hello",
		Run: func(c *oriagent.Ctl, args string) string {
			if args == "" {
				args = "world"
			}
			c.Notify("Hello sent")
			return "Hello, " + args + "!" // logged in the transcript
		},
	}
	oriagent.Run(oriagent.Config{Commands: []*oriagent.Command{hello}})
}

type Config

type Config struct {
	AppName     string // title bar; default "Assistant"
	WindowTitle string // OS window title; default AppName

	Light, Dark theme.Palette // default: Ori
	Fonts       []font.FontFace
	LightMode   bool // start in light mode

	// Locale is the interface language ("fr", "en", "en-US"…); empty means
	// the OS language when a catalog exists for it. Translations add or
	// override catalogs (locale → key → text) on top of Catalogs; keys
	// missing from a locale fall back to FallbackLocale (default "fr").
	Locale         string
	FallbackLocale string
	Translations   map[string]map[string]string
	OnLocale       func(locale string) // called after the language changes

	// Settings, when set, are the initial values of the Configuration page
	// and take precedence over WorkDir, LightMode and Locale (a WorkDir or
	// Locale that cannot be used keeps the Config one). OnSettings receives
	// the new values whenever one of them changes: a setup toggle, model,
	// theme or language, the title bar theme button, /model, /theme, /lang,
	// or the working directory applied by the setup's Finish. It is called
	// at the end of the frame, on the UI goroutine with the state locked:
	// it must return quickly (save in a goroutine of its own if needed).
	Settings   *Settings
	OnSettings func(Settings)

	Agent   Agent // default: a placeholder that explains no agent is set
	WorkDir string
	Models  []Model
	User    User
	// OnProfile is called with the edited fields when the user saves the
	// Profile page, and OnLogout when they sign out from it. Both run on a
	// goroutine of their own while the page buttons wait; the User returned
	// by OnProfile (which may normalize it) replaces the fields, an error is
	// shown and keeps the edits. Nil keeps the profile in memory and only
	// simulates the sign-out.
	OnProfile   func(ctx context.Context, u User) (User, error)
	OnLogout    func(ctx context.Context) error
	ProfileLock ProfileLock // read-only Profile page fields
	// OnNewProject creates a project, such as a workspace folder, from the
	// name and description the user typed. When it is set, the sidebar's
	// main button opens that form instead of starting a session (the + of
	// each project starts one). It runs on a goroutine of its own; an error
	// is shown in the form, which keeps the input.
	OnNewProject func(ctx context.Context, name, description string) (*Project, error)
	// OnUpdateProject saves the edits made on a project's page (opened by
	// the gear of its sidebar row) and returns them as kept, and
	// OnDeleteProject removes a project that is not Permanent. Both run on
	// a goroutine of their own; an error stays on the page. Without
	// OnUpdateProject the edits only apply in memory; without
	// OnDeleteProject the page has no remove button.
	OnUpdateProject func(ctx context.Context, p *Project, edit ProjectEdit) (ProjectEdit, error)
	OnDeleteProject func(ctx context.Context, p *Project) error
	// OnDeleteSession deletes a session for good (its records, its files…),
	// after the user confirmed in a dialog; it runs on a goroutine of its
	// own, and the session leaves the sidebar if it succeeds. nil: sessions
	// can only be archived. DeleteSessionInfo, optional, says in the dialog
	// what will be lost; it is called when the dialog opens.
	OnDeleteSession   func(ctx context.Context, s *Session) error
	DeleteSessionInfo func(s *Session) string
	// ProjectFiles lists the files shown in a Files section of a project's
	// page, such as the files attached in its sessions; nil hides the
	// section. It is called on a goroutine of its own when the page opens.
	ProjectFiles func(p *Project) []ProjectFile
	Devices      []*Device  // profile's connected devices; empty hides the section
	Projects     []*Project // demo data; default: one empty session
	Commands     []*Command // added to the built-in ones
	Pages        []Page
	// Panels are panes at the right of the chat, opened from the title bar.
	Panels []Panel

	Dictation Dictation // default: simulated
	Pomodoro  PomodoroConfig
	Disable   Features

	Placeholder string // composer hint
	ModeLabel   string // chip in the composer ("-" hides it); unused with Modes

	// Modes turn the composer chip into a menu of working modes, kept per
	// project (Project.Mode); OnMode keeps the user's choice (p nil: the
	// sessions out of projects). An error puts the previous mode back.
	Modes  []Mode
	OnMode func(ctx context.Context, p *Project, mode string) error

	// OnRewind brings the session back to the state before its n-th user
	// message (0 for the first): the application restores its files and its
	// own record; then the transcript is cut there and the message's text
	// returns to the composer. Nil: no "Go back here" action.
	OnRewind func(ctx context.Context, s *Session, n int) error

	// OnLink opens the address of a link the user clicked in the transcript
	// and confirmed (http, https or mailto only). Nil: the system's browser.
	OnLink func(url string) error
	// Mentions suggests what "@query" in the composer may refer to, files
	// most often; called on the UI goroutine when the query changes, so it
	// must answer quickly. Nil: no suggestions.
	Mentions      func(s *Session, query string) []Mention
	EmptyTitle    string // empty session
	EmptySubtitle string // default: the working directory

	// Startup state.
	StartView string   // "chat" (default), "setup", "profile" or a Page ID
	SetupStep int      // 1-based, with StartView "setup"
	Attach    []string // files attached to the composer at startup
	Preview   string   // file opened in the preview pane at startup

	// Convert renders in the preview pane the files it does not read itself
	// (Word, Excel, PowerPoint…): as a PDF or as Markdown, in the
	// background. nil, or a nil result: shown as a binary file.
	Convert preview.Converter

	Width, Height unit.Dp // default 1280×820
}
Example (Profile)

The application saves the Profile page and handles the sign-out; a /login command refreshes the profile and the models from its own turn.

package main

import (
	"context"

	oriagent "github.com/sitle/ori-agent"
)

func main() {
	login := &oriagent.Command{Name: "login", Desc: "Sign in", Run: func(c *oriagent.Ctl, _ string) string {
		err := c.RunTurn("", func(ctx context.Context, t *oriagent.Turn) error {
			// … sign in, then:
			t.SetUser(oriagent.User{First: "Hina", Last: "Teriitahi", Email: "hina@example.pf"})
			t.SetModels([]oriagent.Model{{Name: "Fast"}, {Name: "Smart"}})
			return nil
		})
		if err != nil {
			return err.Error()
		}
		return ""
	}}
	oriagent.Run(oriagent.Config{
		Commands:    []*oriagent.Command{login},
		ProfileLock: oriagent.ProfileLock{Email: true}, // from the identity provider
		OnProfile: func(ctx context.Context, u oriagent.User) (oriagent.User, error) {
			// Runs on its own goroutine: save u, return it as saved.
			return u, nil
		},
		OnLogout: func(ctx context.Context) error { return nil },
	})
}

type Ctl

type Ctl struct {
	// contains filtered or unexported fields
}

Ctl is what a slash command can do to the application. It is only valid during Command.Run, which runs on the UI goroutine with the state locked: long work must go through RunTurn (or a goroutine of its own).

func (*Ctl) Clear

func (c *Ctl) Clear()

func (*Ctl) Commands

func (c *Ctl) Commands() []*Command

func (*Ctl) Dark

func (c *Ctl) Dark() bool

func (*Ctl) LanguageName

func (c *Ctl) LanguageName(locale string) string

LanguageName is a locale's own name ("Français", "English").

func (*Ctl) Locale

func (c *Ctl) Locale() string

func (*Ctl) Locales

func (c *Ctl) Locales() []string

Locales lists the available interface languages.

func (*Ctl) Mode added in v0.6.0

func (c *Ctl) Mode() string

Mode is the mode of the session shown.

func (*Ctl) Model

func (c *Ctl) Model() int

func (*Ctl) Models

func (c *Ctl) Models() []Model

func (*Ctl) NewSession

func (c *Ctl) NewSession()

func (*Ctl) NewSessionIn added in v0.2.0

func (c *Ctl) NewSessionIn(p *Project)

NewSessionIn selects an empty session of p, or starts one in it.

func (*Ctl) Notify

func (c *Ctl) Notify(msg string)

func (*Ctl) Open

func (c *Ctl) Open(path string) error

Open shows a file in the preview pane; relative paths are resolved against the working directory of the current session.

func (*Ctl) PickFiles

func (c *Ctl) PickFiles()

func (*Ctl) Pomodoro

func (c *Ctl) Pomodoro() *Pomodoro

func (*Ctl) Projects

func (c *Ctl) Projects() []*Project

func (*Ctl) RunTurn

func (c *Ctl) RunTurn(prompt string, fn TurnFunc) error

RunTurn starts a turn in the current session; fn nil means the configured Agent. The prompt (if any) is shown as the user's message. It fails when the session is already busy.

func (*Ctl) Session

func (c *Ctl) Session() *Session

func (*Ctl) Sessions

func (c *Ctl) Sessions() []*Session

func (*Ctl) SetDark

func (c *Ctl) SetDark(dark bool)

func (*Ctl) SetLocale

func (c *Ctl) SetLocale(locale string) error

SetLocale switches the interface language ("en", "fr-FR"…).

func (*Ctl) SetMode added in v0.6.0

func (c *Ctl) SetMode(id string)

SetMode changes the mode of the session shown, as the chip does, without calling Config.OnMode (a command already applied it).

func (*Ctl) SetModel

func (c *Ctl) SetModel(i int)

func (*Ctl) SetModels added in v0.2.0

func (c *Ctl) SetModels(ms []Model)

SetModels replaces the model list; the selected model stays when its name is still in the list, else the first one is selected.

func (*Ctl) SetThinking added in v0.10.0

func (c *Ctl) SetThinking(on bool)

SetThinking turns extended thinking on or off for the next turns, as the switch of the Configuration page does; Config.OnSettings is told at the end of the frame.

func (*Ctl) SetUser added in v0.2.0

func (c *Ctl) SetUser(u User)

func (*Ctl) Settings added in v0.2.0

func (c *Ctl) Settings() Settings

Settings are the current values of the Configuration page.

func (*Ctl) Show

func (c *Ctl) Show(view string)

Show switches the main area to "chat", "setup", "profile" or a Page ID.

func (*Ctl) T

func (c *Ctl) T(key string, args ...any) string

T translates key in the interface language.

func (*Ctl) TogglePanel added in v0.8.0

func (c *Ctl) TogglePanel(id string)

TogglePanel opens the panel id (Config.Panels) next to the chat, or closes it when it is open.

func (*Ctl) ToggleSidebar

func (c *Ctl) ToggleSidebar()

func (*Ctl) ToggleVoice

func (c *Ctl) ToggleVoice()

func (*Ctl) User added in v0.2.0

func (c *Ctl) User() User

User is the saved profile; SetUser replaces it (and the Profile fields).

func (*Ctl) WorkDir

func (c *Ctl) WorkDir() string

type D

type D = layout.Dimensions

type Decision added in v0.2.3

type Decision int

Decision is the user's answer to an ApprovalRequest.

const (
	Deny         Decision = iota
	Allow                 // this time
	AllowSession          // this time and the next ones of the session
)

type Device

type Device struct {
	Name, Detail string
	Current      bool
	// contains filtered or unexported fields
}

Device is a row of the profile's "connected devices" section.

type Dictation

type Dictation interface {
	Start() error
	Stop() (string, error)
}

Dictation turns speech into text for the composer. Start begins capture; Stop ends it and returns the transcript (it may block: it runs on its own goroutine). Plug a real speech-to-text engine here.

type Features

type Features struct {
	Voice, Pomodoro, Attachments, Preview bool
	Setup, Profile, ThemeToggle           bool
	BuiltinCommands                       bool
}

Features turns built-in parts off; the zero value enables everything.

type Headless added in v0.4.0

type Headless struct {

	// Approve answers the approval requests of the agent (Turn.Approve), on
	// the agent's goroutine; nil denies them.
	Approve func(ApprovalRequest) Decision
	// contains filtered or unexported fields
}

Headless runs an agent without a window, through the same Turn as the application: for tests, evaluation benches and scripts. The transcript of each session can be read afterwards (Session.Messages).

func NewHeadless added in v0.4.0

func NewHeadless(cfg Config) *Headless

NewHeadless prepares cfg.Agent to run without a window; the translations of cfg are used for the texts the agent writes through the turn.

Example

An agent runs without a window through the same Turn, for tests and evaluation benches; the transcript is read from the returned session.

package main

import (
	"context"
	"fmt"

	oriagent "github.com/sitle/ori-agent"
)

func main() {
	h := oriagent.NewHeadless(oriagent.Config{Agent: oriagent.AgentFunc(func(ctx context.Context, t *oriagent.Turn) error {
		d, err := t.Approve(ctx, oriagent.ApprovalRequest{Title: "Run go test", Detail: "go test ./..."})
		if err != nil || d == oriagent.Deny {
			return err
		}
		b := t.Text()
		b.Write("Tests pass in " + t.WorkDir)
		b.Done()
		return nil
	})})
	h.Approve = func(r oriagent.ApprovalRequest) oriagent.Decision { return oriagent.Allow }

	s, err := h.Run(context.Background(), nil, "test it", nil, oriagent.TurnOptions{WorkDir: "/ws"})
	if err != nil {
		panic(err)
	}
	fmt.Println(s.Messages[len(s.Messages)-1].Text)
}
Output:
Tests pass in /ws

func (*Headless) Run added in v0.4.0

func (h *Headless) Run(ctx context.Context, s *Session, prompt string, atts []*Attachment, o TurnOptions) (*Session, error)

Run runs one turn of the agent in session s, or in a new session when s is nil, and returns once the agent is done, with the session.

func (*Headless) Send added in v0.7.0

func (h *Headless) Send(s *Session, text string, atts ...*Attachment) bool

Send adds a message of the user to the turn running in s, as when the user writes during a turn (Turn.TakeMessages). It reports false when no turn is running; a message the agent does not take before the end of its turn is left out of the transcript.

type HistoryEntry

type HistoryEntry struct {
	Role string // "user" or "assistant"
	Text string
}

HistoryEntry is a past exchange, for agents that send context to a model.

type Kind

type Kind int

Kind is the type of a transcript item.

const (
	KindUser      Kind = iota // user bubble, with optional attachments
	KindAssistant             // assistant text
	KindThinking              // collapsible reasoning
	KindTool                  // tool call with expandable output
	KindTasks                 // task list
	KindCommand               // slash command and its output
	KindNotice                // interruption or error
	KindApproval              // a request the user allows or denies (Turn.Approve)
)

type Leveler

type Leveler interface {
	Level() float32
}

Leveler is optionally implemented by a Dictation to drive the level meter with the real input level, in [0,1]. Without it the meter is simulated.

type LocaleSetter

type LocaleSetter interface {
	SetLocale(locale string)
}

LocaleSetter is implemented by parts that follow the interface language, such as a Dictation engine.

type Mention added in v0.7.0

type Mention struct {
	Label  string // shown, e.g. the file name
	Detail string // shown after, muted, e.g. the folder
	Insert string // inserted after "@"; empty: Label
}

Mention is a suggestion for "@".

type Message

type Message struct {
	Kind     Kind
	Text     string
	ToolName string
	ToolArg  string
	Output   string
	Running  bool
	Tasks    []Task
	Secs     int      // thinking duration
	Sub      *Session // subagent behind an Agent tool call: a click opens it
	FilePath string   // file behind a tool call: a click previews it
	Error    bool     // KindNotice: an error rather than an interruption
	Expanded bool     // tool output or reasoning shown
	Markdown bool     // KindCommand: Output is Markdown (Command.Markdown)
	// With HasDiff, a tool call shows the change from DiffOld to DiffNew
	// instead of its output (ToolCall.Diff).
	HasDiff          bool
	DiffOld, DiffNew string

	Attachments []*Attachment
	// Queued marks a message the user sent while a turn was running, not yet
	// given to the agent (Turn.TakeMessages).
	Queued bool
	// contains filtered or unexported fields
}

Message is one transcript item. Agents create them through a Turn; demo transcripts can build them directly.

func TasksMsg

func TasksMsg(tasks ...Task) *Message

func TextMsg

func TextMsg(text string) *Message

func ThinkingMsg

func ThinkingMsg(text string, secs int) *Message

func ToolMsg

func ToolMsg(name, arg, output string) *Message

func UserMsg

func UserMsg(text string, atts ...*Attachment) *Message

type Mode added in v0.6.0

type Mode struct {
	ID    string
	Label string // or a translation key
	Desc  string // one line under the label in the menu; or a translation key
}

Mode is a working mode of the agent the composer chip offers, such as "plan", "edition" or "auto": the application defines them and what they mean (Config.Modes). The mode belongs to the project of the session, or to the application for the sessions out of projects.

type Model

type Model struct {
	Name, Desc string
}

type Page

type Page struct {
	ID, Title string
	Icon      func(gtx layout.Context, c color.NRGBA) layout.Dimensions // optional
	Layout    func(gtx layout.Context, th *ui.Theme, tr i18n.Translator) layout.Dimensions
}

Page is an extra screen reached from the sidebar. Layout draws the content; the shell adds the title and the centered, scrollable column. tr translates in the current interface language.

Example

A page is an extra screen in the sidebar, drawn with the ui kit and translated through tr.

package main

import (
	"gioui.org/font"
	"gioui.org/layout"

	oriagent "github.com/sitle/ori-agent"
	"github.com/sitle/ori-agent/i18n"
	"github.com/sitle/ori-agent/ui"
)

func main() {
	about := oriagent.Page{
		ID:    "about",
		Title: "about.title", // translation key
		Layout: func(gtx layout.Context, th *ui.Theme, tr i18n.Translator) layout.Dimensions {
			return th.Section(gtx, tr.T("about.title"), "", th.Label(14, tr.T("about.body"), th.Text, font.Normal).Layout)
		},
	}
	oriagent.Run(oriagent.Config{
		Pages: []oriagent.Page{about},
		Translations: map[string]map[string]string{
			"fr": {"about.title": "À propos", "about.body": "Version de démonstration."},
			"en": {"about.title": "About", "about.body": "Demo version."},
		},
	})
}

type Panel added in v0.8.0

type Panel struct {
	ID, Title string                                                    // Title: text or translation key
	Icon      func(gtx layout.Context, c color.NRGBA) layout.Dimensions // optional, before the title
	Layout    func(gtx layout.Context, th *ui.Theme, tr i18n.Translator, s *Session) layout.Dimensions
}

Panel is a pane of the application at the right of the chat, about the session shown: a supervision of the agents, a summary of the work… Its button in the title bar opens and closes it; it takes the place of the preview while open. Layout draws the content of a scrollable column; it is called at each frame, and twice a second while a turn runs in the session, so that it can show live state.

type Permissions added in v0.2.0

type Permissions struct {
	AllowCommands bool
	AskEdits      bool
}

Permissions are what the agent may do in a project's sessions; they are given to it in Turn.AllowCommands and Turn.AskEdits.

type Pomodoro

type Pomodoro struct {
	// contains filtered or unexported fields
}

Pomodoro is a countdown driven by the wall clock: focus periods with a long pause every four.

func (*Pomodoro) Cycles

func (p *Pomodoro) Cycles() int

func (*Pomodoro) Left

func (p *Pomodoro) Left(now time.Time) time.Duration

Left is the time remaining in the current period.

func (*Pomodoro) Mode

func (p *Pomodoro) Mode() string

func (*Pomodoro) Next

func (p *Pomodoro) Next() string

Next moves to the following period and returns a message announcing it.

func (*Pomodoro) Pause

func (p *Pomodoro) Pause(now time.Time)

func (*Pomodoro) Reset

func (p *Pomodoro) Reset()

func (*Pomodoro) Running

func (p *Pomodoro) Running() bool

func (*Pomodoro) Start

func (p *Pomodoro) Start(now time.Time)

type PomodoroConfig

type PomodoroConfig struct {
	Focus, Short, Long time.Duration // defaults 25, 5 and 15 minutes
}

type ProfileLock added in v0.2.0

type ProfileLock struct {
	First, Last, Email, Service, Role bool
}

ProfileLock makes Profile page fields read-only, for values the application does not let the user change (from a directory, an identity provider…).

type Project

type Project struct {
	Name        string
	Description string
	// WorkDir is the folder of the project: Turn.WorkDir for its sessions.
	// Empty: the folder of the configuration.
	WorkDir string
	// Permissions of the project's sessions; nil: the Configuration's.
	Permissions *Permissions
	// Permanent projects cannot be removed from their page.
	Permanent bool
	// Mode is the working mode of the project's sessions, among
	// Config.Modes (the composer chip); empty: the first one.
	Mode     string
	Sessions []*Session
	Open     bool
	// contains filtered or unexported fields
}

Project groups sessions in the sidebar.

func NewProject

func NewProject(name string, sessions ...*Session) *Project

NewProject returns an open project holding sessions (first on top).

Example

Demo data fills the sidebar tree and transcripts at startup.

package main

import (
	oriagent "github.com/sitle/ori-agent"
)

func main() {
	s := &oriagent.Session{Title: "Fix the login page", Messages: []*oriagent.Message{
		oriagent.UserMsg("The login button does nothing."),
		oriagent.ThinkingMsg("The click handler is probably not bound.", 3),
		oriagent.ToolMsg("Read", "login.go", "   1  package main"),
		oriagent.TextMsg("Fixed: the handler is now registered."),
	}}
	s.Spawn("Run the tests") // a subagent under the session
	oriagent.Run(oriagent.Config{Projects: []*oriagent.Project{oriagent.NewProject("web", s)}})
}

func (*Project) Add

func (p *Project) Add(s *Session)

Add puts s at the top of the project.

type ProjectEdit added in v0.2.0

type ProjectEdit struct {
	Name        string
	Description string
	Permissions *Permissions // nil when the project has none of its own
}

ProjectEdit is what the user changed on a project's page.

type ProjectFile added in v0.2.2

type ProjectFile struct {
	Name    string
	Detail  string // size, date, version, checksum…
	Path    string // opened in the preview pane on click; empty: not clickable
	Warning string // shown in red when set, such as "changed since added"
}

ProjectFile is a row of the Files section of a project's page, such as a file the user attached in its sessions.

type QueuedMessage added in v0.7.0

type QueuedMessage struct {
	Text        string
	Attachments []*Attachment
}

QueuedMessage is a message the user sent while a turn was running.

type Session

type Session struct {
	// ID identifies the session for the agent (Turn.SessionID), so that it
	// finds its own records again. Applications set it on the sessions
	// they restore; ori-agent gives one to the others.
	ID       string
	Title    string
	Messages []*Message
	Children []*Session
	Busy     bool
	Open     bool // subagents shown in the sidebar
	// ContextUsed and ContextWindow fill the context gauge of the composer
	// (Turn.SetContext); applications restore them with the session.
	ContextUsed, ContextWindow int
	// contains filtered or unexported fields
}

Session is a conversation; Children are the subagents it spawned.

func (*Session) Parent

func (s *Session) Parent() *Session

func (*Session) Project

func (s *Session) Project() *Project

func (*Session) Spawn

func (s *Session) Spawn(title string) *Session

Spawn attaches a subagent session to s (for building demo trees; agents use Turn.Subagent).

type Settings added in v0.2.0

type Settings struct {
	WorkDir       string `json:"workDir,omitempty"` // ignored at startup if it is not a folder
	AllowCommands bool   `json:"allowCommands"`
	AskEdits      bool   `json:"askEdits"`
	Thinking      bool   `json:"thinking"` // extended thinking
	Notifications bool   `json:"notifications"`
	Model         string `json:"model,omitempty"` // a Model.Name; empty or unknown means the first model
	Dark          bool   `json:"dark"`
	Locale        string `json:"locale,omitempty"` // ignored at startup if no catalog matches
}

Settings are the values of the Configuration page, so that an application can restore them at startup (Config.Settings) and keep them when the user changes them (Config.OnSettings). The JSON tags make them easy to save in a file; ori-agent itself stores nothing.

Example

The application keeps the Configuration page values in a file of its own: it restores them at startup and saves them whenever the user changes one.

package main

import (
	"encoding/json"
	"os"
	"path/filepath"

	oriagent "github.com/sitle/ori-agent"
)

func main() {
	path := filepath.Join(os.TempDir(), "settings.json")
	set := oriagent.DefaultSettings() // missing fields keep their default
	if data, err := os.ReadFile(path); err == nil {
		_ = json.Unmarshal(data, &set)
	}
	oriagent.Run(oriagent.Config{
		Models:   []oriagent.Model{{Name: "Fast"}, {Name: "Smart"}},
		Settings: &set,
		OnSettings: func(s oriagent.Settings) { // UI goroutine: do not block
			data, _ := json.MarshalIndent(s, "", "  ")
			go os.WriteFile(path, data, 0o600)
		},
	})
}

func DefaultSettings added in v0.2.0

func DefaultSettings() Settings

DefaultSettings are the values used when Config.Settings is nil. Decode a saved file over them, so that missing fields keep their default.

type SimDictation

type SimDictation struct {
	Phrases map[string][]string
	// contains filtered or unexported fields
}

SimDictation captures nothing and returns canned phrases in turn, in the interface language (Phrases maps a locale to its phrases).

func (*SimDictation) SetLocale

func (d *SimDictation) SetLocale(locale string)

func (*SimDictation) Start

func (d *SimDictation) Start() error

func (*SimDictation) Stop

func (d *SimDictation) Stop() (string, error)

type Task

type Task struct {
	Text   string
	Status TaskStatus
}

type TaskList

type TaskList struct {
	// contains filtered or unexported fields
}

TaskList is a live checklist.

func (*TaskList) Advance

func (l *TaskList) Advance(i int)

Advance marks item i done and starts the next one.

func (*TaskList) Set

func (l *TaskList) Set(i int, st TaskStatus)

type TaskStatus

type TaskStatus int
const (
	TaskPending TaskStatus = iota
	TaskRunning
	TaskDone
)

type ToolCall

type ToolCall struct {
	// contains filtered or unexported fields
}

ToolCall is a tool invocation shown in the transcript.

func (*ToolCall) Diff added in v0.2.3

func (c *ToolCall) Diff(old, new string) *ToolCall

Diff shows the change the call made to a file, from old to new, in place of its output, unfolded; the header counts the lines added and removed.

func (*ToolCall) Done

func (c *ToolCall) Done(output string)

Done marks the call finished with its output (shown when expanded).

func (*ToolCall) File

func (c *ToolCall) File(path string) *ToolCall

File makes the tool argument a link that opens path in the preview pane.

type Turn

type Turn struct {
	Prompt        string
	Attachments   []*Attachment
	History       []HistoryEntry // the session before this turn
	Thinking      bool           // extended thinking is enabled in the settings
	AllowCommands bool           // the settings let the agent run commands (the agent enforces it)
	AskEdits      bool           // the settings ask to confirm file edits (the agent enforces it)
	Model         Model
	WorkDir       string
	Locale        string // interface language, so the agent can answer in it
	// SessionID is the Session.ID of the turn's session, stable across
	// turns and, when the application restores sessions with their ID,
	// across launches.
	SessionID string
	// Mode is the working mode chosen in the composer chip when the turn
	// started (Config.Modes); empty without modes.
	Mode string
	// contains filtered or unexported fields
}

Turn is the agent's handle on one turn. Its methods are safe to call from any goroutine: they lock the UI state and request a redraw.

func (*Turn) Approve added in v0.2.3

func (t *Turn) Approve(ctx context.Context, req ApprovalRequest) (Decision, error)

Approve shows req in the transcript and waits for the user's answer. A subagent's request is shown in its root session, naming the subagent, where the user is working. When ctx is canceled (Stop), the request is withdrawn and ctx's error returned with Deny.

func (*Turn) Finish

func (t *Turn) Finish(report string)

Finish ends a subagent turn: report becomes the parent's tool output.

func (*Turn) Notify

func (t *Turn) Notify(msg string)

Notify shows a transient message at the bottom of the window.

func (*Turn) SetContext added in v0.6.0

func (t *Turn) SetContext(used, window int)

SetContext shows how full the model's context is in the composer: used and window in tokens; a window of 0 hides the gauge.

func (*Turn) SetMode added in v0.6.0

func (t *Turn) SetMode(id string)

SetMode changes the mode of the turn's project (or the application's), for the chip, when the agent changes it itself, for instance when the user approves a plan. Config.OnMode is not called: the agent keeps it.

func (*Turn) SetModels added in v0.2.0

func (t *Turn) SetModels(ms []Model)

SetModels replaces the model list, as Ctl.SetModels. The running turn keeps its Model.

func (*Turn) SetUser added in v0.2.0

func (t *Turn) SetUser(u User)

SetUser replaces the profile (Profile page and sidebar footer), for example after a sign-in turn.

func (*Turn) Subagent

func (t *Turn) Subagent(title, prompt string) *Turn

Subagent spawns a child session under the current one, shown in the sidebar tree and as an "Agent" tool call here. Write the subagent's work through the returned turn, then call its Finish.

Example

A subagent appears under the session in the sidebar tree, and as an "Agent" tool call that opens it.

package main

import (
	"context"

	oriagent "github.com/sitle/ori-agent"
)

func main() {
	_ = oriagent.AgentFunc(func(ctx context.Context, t *oriagent.Turn) error {
		sub := t.Subagent("Explore the docs", "Read every Markdown file and summarize.")
		sub.Tool("Glob", "**/*.md").Done("README.md\ndocs/guide.md")
		sub.Finish("2 documents found.") // becomes the parent's tool output

		b := t.Text()
		b.Write("The subagent found 2 documents.")
		b.Done()
		return nil
	})
}

func (*Turn) T

func (t *Turn) T(key string, args ...any) string

T translates key in the turn's language, with the framework catalogs and Config.Translations: agents can keep their texts there too.

func (*Turn) TakeMessages added in v0.7.0

func (t *Turn) TakeMessages() []QueuedMessage

TakeMessages returns the messages the user sent since the turn started, or since the last call; the agent adds them to its work, typically before its next model call. Those it does not take start a new turn when this one ends. A subagent's turn has none.

func (*Turn) Tasks

func (t *Turn) Tasks(items ...string) *TaskList

Tasks shows a checklist; the first item starts running.

func (*Turn) Text

func (t *Turn) Text() *Block

Text starts an assistant text block.

func (*Turn) Think

func (t *Turn) Think() *Block

Think starts a reasoning block: shown while streaming, then collapsed to "Thought for Ns" by Done.

func (*Turn) Tool

func (t *Turn) Tool(name, arg string) *ToolCall

Tool shows a running tool call, e.g. Tool("Read", "main.go").

type TurnFunc

type TurnFunc func(ctx context.Context, t *Turn) error

TurnFunc runs a turn; commands use it to start custom turns.

type TurnOptions added in v0.4.0

type TurnOptions struct {
	Thinking      bool
	AllowCommands bool
	AskEdits      bool
	Model         Model
	WorkDir       string
	Locale        string // default: the configuration's
	Mode          string // default: the session's (Config.Modes)
}

TurnOptions are the settings of a headless turn, which the window takes from the Configuration page and the selected model.

type User

type User struct {
	First, Last, Email, Service, Role string
}

User fills the profile page and the sidebar footer.

Directories

Path Synopsis
examples
anna command
Command anna is "Anna Assistant", the demo application of the ori-agent framework: it configures the shell, provides demo data, and keeps what ori-agent leaves to the application (settings, profile, projects) in a folder of its own (store.go).
Command anna is "Anna Assistant", the demo application of the ori-agent framework: it configures the shell, provides demo data, and keeps what ori-agent leaves to the application (settings, profile, projects) in a folder of its own (store.go).
minimal command
Command minimal is the smallest ori-agent application: an agent that reasons briefly, shows a tool call and echoes the prompt.
Command minimal is the smallest ori-agent application: an agent that reasons briefly, shows a tool call and echoes the prompt.
Package i18n translates interface strings.
Package i18n translates interface strings.
Package preview renders read-only documents in a pane: Markdown (the block subset READMEs use), source code with line numbers, PDF (rasterized by PDFium compiled to WebAssembly, pure Go) and images.
Package preview renders read-only documents in a pane: Markdown (the block subset READMEs use), source code with line numbers, PDF (rasterized by PDFium compiled to WebAssembly, pure Go) and images.
Package sim is a scripted agent for demos and UI work: no model is called, but its tool calls really run against the working directory (listing, reading files).
Package sim is a scripted agent for demos and UI work: no model is called, but its tool calls really run against the working directory (listing, reading files).
Package theme holds color palettes.
Package theme holds color palettes.
Package ui is the widget kit of ori-agent: drawing primitives, form controls, icons and overlays, all drawn with a Theme.
Package ui is the widget kit of ori-agent: drawing primitives, form controls, icons and overlays, all drawn with a Theme.

Jump to

Keyboard shortcuts

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