Documentation
¶
Overview ¶
Package coding is the Go port of pi's coding agent (@earendil-works/pi-coding-agent): the built-in tools (read/write/edit/bash/ls/find/grep), system prompt, session runner, and CLI plumbing built on the agent and ai packages.
Index ¶
- Constants
- Variables
- func AgentDir() string
- func AgentsSkillsDir() string
- func AreExperimentalFeaturesEnabled() bool
- func BuildSystemPrompt(opts BuildSystemPromptOptions) string
- func CreateAllTools(cwd string) []agent.AgentTool
- func CreateCodingTools(cwd string) []agent.AgentTool
- func CreateTool(name, cwd string) (agent.AgentTool, error)
- func DefaultSessionDir(cwd string) string
- func DetectSupportedImageMimeTypeFromFile(path string) string
- func DocsPath() string
- func EstimateContextTokens(messages []agent.AgentMessage) int
- func EstimateMessageTokens(m agent.AgentMessage) int
- func ExamplesPath() string
- func FormatSize(bytes int) string
- func FormatSkillsForPrompt(skills []Skill) string
- func GetExperimentalToolSampling() *ai.ConstrainedSamplingConfig
- func LoadSessionMessages(path string) ([]agent.AgentMessage, error)
- func LoadSkillsWithDiagnostics(cwd string) ([]Skill, []SkillDiagnostic)
- func LoadSkillsWithTrust(cwd string, projectTrusted bool) ([]Skill, []SkillDiagnostic)
- func PackageDir() string
- func ReadmePath() string
- func ResolveModel(spec string) (*ai.Model, error)
- func TruncateLine(line string, maxChars int) (string, bool)
- type BashExecOptions
- type BashOperations
- type BranchContext
- type BuildSystemPromptOptions
- type CompactionSettings
- type ContextFile
- type CreateRemoteSessionOptions
- type EditOperations
- type ExecutionEnv
- type FileInfo
- type FileKind
- type FileSystem
- type FindOperations
- type GrepOperations
- type LocalEnv
- func (e *LocalEnv) AbsolutePath(ctx context.Context, path string) (string, error)
- func (e *LocalEnv) AppendFile(ctx context.Context, path string, content []byte) error
- func (e *LocalEnv) CanonicalPath(ctx context.Context, path string) (string, error)
- func (e *LocalEnv) Cleanup() error
- func (e *LocalEnv) Cwd() string
- func (e *LocalEnv) Exec(ctx context.Context, command string, options *ShellExecOptions) (ShellResult, error)
- func (e *LocalEnv) Exists(ctx context.Context, path string) (bool, error)
- func (e *LocalEnv) JoinPath(ctx context.Context, parts []string) (string, error)
- func (e *LocalEnv) ListDir(ctx context.Context, path string) ([]FileInfo, error)
- func (e *LocalEnv) ReadBinaryFile(ctx context.Context, path string) ([]byte, error)
- func (e *LocalEnv) ReadTextFile(ctx context.Context, path string) (string, error)
- func (e *LocalEnv) ReadTextLines(ctx context.Context, path string, maxLines int) ([]string, error)
- func (e *LocalEnv) RenameFile(ctx context.Context, sourcePath, destinationPath string) error
- func (e *LocalEnv) Stat(ctx context.Context, path string) (FileInfo, error)
- func (e *LocalEnv) WriteFile(ctx context.Context, path string, content []byte) error
- type LsOperations
- type NoToolsMode
- type ProcessImageResult
- type ReadOperations
- type RemoteSession
- func (s *RemoteSession) Abort(ctx context.Context) error
- func (s *RemoteSession) Close(ctx context.Context) error
- func (s *RemoteSession) ConnectionState() client.ConnectionState
- func (s *RemoteSession) Create(ctx context.Context, opts CreateRemoteSessionOptions) error
- func (s *RemoteSession) Disposed() bool
- func (s *RemoteSession) ID() string
- func (s *RemoteSession) Models() []protocol.ModelMetadata
- func (s *RemoteSession) OnConnectionStateChange(listener func(client.ConnectionStateChange)) (client.Unsubscribe, error)
- func (s *RemoteSession) Open(ctx context.Context, sessionID string) error
- func (s *RemoteSession) Operation() (RemoteSessionOperation, bool)
- func (s *RemoteSession) Phase() (protocol.SessionPhase, bool)
- func (s *RemoteSession) Reconnect(ctx context.Context) error
- func (s *RemoteSession) Sessions() []protocol.SessionMetadata
- func (s *RemoteSession) SetModel(ctx context.Context, model protocol.ModelRef) error
- func (s *RemoteSession) SetThinking(ctx context.Context, level protocol.ThinkingLevel) error
- func (s *RemoteSession) Snapshot() *protocol.SessionSnapshot
- func (s *RemoteSession) State() RemoteSessionState
- func (s *RemoteSession) Submit(ctx context.Context, text string) error
- func (s *RemoteSession) Subscribe(listener func(RemoteSessionState)) (client.Unsubscribe, error)
- type RemoteSessionBusyError
- type RemoteSessionLifecycle
- type RemoteSessionOperation
- type RemoteSessionOptions
- type RemoteSessionState
- type RemoteSessionStatus
- type ResizeResult
- type ResolvedModel
- type RunResult
- type Session
- func (s *Session) Abort()
- func (s *Session) Continue(ctx context.Context) error
- func (s *Session) EnableCompaction(settings CompactionSettings)
- func (s *Session) FollowUp(m agent.AgentMessage)
- func (s *Session) History() []agent.AgentMessage
- func (s *Session) LastAssistantText() string
- func (s *Session) LoadHistory(messages []agent.AgentMessage)
- func (s *Session) Record(r *SessionRecorder)
- func (s *Session) Reset() error
- func (s *Session) Run(ctx context.Context, prompt string, images ...ai.ImageContent) (*RunResult, error)
- func (s *Session) RunMessages(ctx context.Context, prompts []agent.AgentMessage) (*RunResult, error)
- func (s *Session) RunPrint(ctx context.Context, w io.Writer, prompt string) (string, error)
- func (s *Session) SetModel(model *ai.Model, apiKey string)
- func (s *Session) SetThinkingLevel(level agent.ThinkingLevel)
- func (s *Session) Steer(m agent.AgentMessage)
- func (s *Session) Subscribe(l agent.Listener) func()
- func (s *Session) WaitForIdle()
- type SessionEntry
- type SessionInfo
- type SessionOptions
- type SessionRecorder
- func (r *SessionRecorder) Close() error
- func (r *SessionRecorder) ForkFrom(entryID string)
- func (r *SessionRecorder) ID() string
- func (r *SessionRecorder) LastEntryID() string
- func (r *SessionRecorder) Path() string
- func (r *SessionRecorder) RecordMessage(m agent.AgentMessage) string
- func (r *SessionRecorder) RecordModelChange(provider, modelID string)
- func (r *SessionRecorder) RecordThinkingLevel(level string)
- type SessionTree
- type Shell
- type ShellExecOptions
- type ShellResult
- type ShellTimeoutError
- type Skill
- type SkillDiagnostic
- type TranscriptState
- func (s *TranscriptState) ApplyProgress(progress protocol.TranscriptProgress) *TranscriptState
- func (s *TranscriptState) ApplySnapshot(snapshot *protocol.SessionSnapshot) *TranscriptState
- func (s *TranscriptState) Snapshot() *protocol.SessionSnapshot
- func (s *TranscriptState) Transcript() []protocol.TranscriptItem
- type TruncationResult
- type WriteOperations
Constants ¶
const ( DefaultMaxLines = 2000 DefaultMaxBytes = 50 * 1024 // 50KB GrepMaxLineLength = 500 )
const ConfigDirName = ".pi"
ConfigDirName is pi's per-project/user config directory name.
const CurrentSessionVersion = 3
CurrentSessionVersion matches pi's session file format version.
const DefaultModelSpec = "anthropic/claude-sonnet-4-5"
DefaultModelSpec is the model used when none is specified. Deliberate divergence from pi: pi's default model comes from settings.json / findInitialModel (first available provider default); the Go port has no settings manager, so an empty spec resolves to this fixed default.
const DefaultThinkingLevel = agent.ThinkMedium
DefaultThinkingLevel is pi's DEFAULT_THINKING_LEVEL (defaults.ts:3): an unset reasoning level starts at "medium" before clamping to the model's capabilities.
Variables ¶
var DefaultCompactionSettings = CompactionSettings{ Enabled: true, ReserveTokens: 16384, KeepRecentTokens: 20000, }
DefaultCompactionSettings mirrors pi's defaults.
var ErrNoRemoteSession = errors.New("No remote session is attached")
ErrNoRemoteSession reports an operation that needs an attached session when none is attached.
var ErrRemoteSessionDisposed = errors.New("Remote session is disposed")
ErrRemoteSessionDisposed reports work attempted on a closed RemoteSession, and is also what an in-flight operation returns when disposal preempts it.
var ErrShellAborted = errors.New("aborted")
ErrShellAborted is what a shell operation returns when the context ended the command. It is pi's `throw new Error("aborted")` from ops.exec (bash.ts:461), rendered as a sentinel so the tool can classify it with errors.Is.
var ToolNames = []string{"read", "bash", "powershell", "edit", "write", "grep", "find", "ls"}
ToolNames are the built-in coding tool identifiers.
var ToolSnippets = map[string]string{
"read": "Read file contents",
"bash": "Execute bash commands (ls, grep, find, etc.)",
"powershell": "Execute PowerShell commands",
"edit": "Make precise file edits with exact text replacement, including multiple disjoint edits in one call",
"write": "Create or overwrite files",
"grep": "Search file contents for patterns (respects .gitignore)",
"find": "Find files by glob pattern (respects .gitignore)",
"ls": "List directory contents",
"web_fetch": "Fetch a web URL and return readable text",
}
ToolSnippets are the one-line prompt snippets keyed by tool name.
Functions ¶
func AgentDir ¶
func AgentDir() string
AgentDir returns the global agent config directory (~/.pi/agent). It returns "" when the home directory cannot be determined (no HOME — which is ordinary in containers, CI, systemd units and cron). It must NOT fall back to a relative path: a relative path resolves against the process working directory, i.e. whatever repository the agent happens to be run in, so `.pi/agent` would make a hostile repo's files look like the user's own global config. pi cannot reach that state — its getHomeDir is `process.env.HOME || homedir()` and Node's homedir() consults the passwd database — so failing closed is the faithful answer as well as the safe one. Every caller must treat "" as "there is no agent directory".
func AgentsSkillsDir ¶ added in v0.84.19
func AgentsSkillsDir() string
AgentsSkillsDir returns the user's AGENTS-convention skills directory (~/.agents/skills), the sibling of pi's own ~/.pi/agent/skills. It returns "" when the home directory cannot be determined, for the same reason AgentDir does — see there.
func AreExperimentalFeaturesEnabled ¶ added in v0.2.0
func AreExperimentalFeaturesEnabled() bool
AreExperimentalFeaturesEnabled ports pi's areExperimentalFeaturesEnabled (core/experimental.ts, upstream 66335d3a): the guard that lets users opt in to early features. It is true only when the PI_EXPERIMENTAL environment variable is exactly "1" — unset, empty, "0", "true", or any other value all leave experimental features disabled.
func BuildSystemPrompt ¶
func BuildSystemPrompt(opts BuildSystemPromptOptions) string
BuildSystemPrompt constructs the coding agent system prompt (port of buildSystemPrompt), including tools, guidelines, project context, and footer.
func CreateAllTools ¶
CreateAllTools returns all eight built-in tools, in pi's createAllTools order (powershell sits after bash). powershell is included even off Windows: like pi, the shell is resolved inside Execute, so constructing the tool is safe everywhere and only running it reports the platform error.
func CreateCodingTools ¶
CreateCodingTools returns the default coding tool set [read, bash, edit, write].
func CreateTool ¶
CreateTool builds a single built-in tool by name, rooted at cwd. The bash tool built this way exposes no PI_* session metadata: there is no session to read it from, matching pi's `exposeSessionEnvironment && ctx` guard when the tool runs without an extension context.
func DefaultSessionDir ¶
DefaultSessionDir returns the per-cwd session directory under the agent dir, using pi's safe-path encoding (--<cwd with separators as dashes>--). It returns "" when AgentDir() does — with no home there is nowhere global to put sessions, and the old relative fallback wrote them into whatever repo the process was run in.
func DetectSupportedImageMimeTypeFromFile ¶ added in v0.84.19
DetectSupportedImageMimeTypeFromFile reads up to the sniff window from a file and identifies a supported image type (mime.ts detectSupportedImageMimeTypeFromFile), returning "" when the file is not an image pi can attach — including when it cannot be opened, matching pi's null. Upstream de82e5367 promoted it to published SDK surface; the buffer variant it delegates to is not published there and stays unexported here.
func DocsPath ¶
func DocsPath() string
DocsPath returns the absolute path to the pi package docs directory.
func EstimateContextTokens ¶
func EstimateContextTokens(messages []agent.AgentMessage) int
EstimateContextTokens sums estimated tokens across messages (pure heuristic).
func EstimateMessageTokens ¶
func EstimateMessageTokens(m agent.AgentMessage) int
EstimateMessageTokens estimates the token cost of a message (port of estimateTokens: char count / 4, rounded up).
func ExamplesPath ¶
func ExamplesPath() string
ExamplesPath returns the absolute path to the pi package examples directory.
func FormatSize ¶
FormatSize renders a byte count as a human-readable size.
func FormatSkillsForPrompt ¶
FormatSkillsForPrompt renders visible skills as the Agent Skills XML block.
func GetExperimentalToolSampling ¶ added in v0.84.18
func GetExperimentalToolSampling() *ai.ConstrainedSamplingConfig
GetExperimentalToolSampling ports pi's getExperimentalToolSampling: the constrained-sampling config for the built-in read/bash/edit/write tools when experimental features are enabled, nil otherwise.
func LoadSessionMessages ¶
func LoadSessionMessages(path string) ([]agent.AgentMessage, error)
LoadSessionMessages reconstructs the LLM message transcript from a session file for resume. It routes through SessionTree.BuildContext so compacted, branched, and custom-message sessions resume identically to pi (emitting the compaction summary in place of the pre-compaction turns) rather than naively concatenating every message entry.
func LoadSkillsWithDiagnostics ¶
func LoadSkillsWithDiagnostics(cwd string) ([]Skill, []SkillDiagnostic)
LoadSkillsWithDiagnostics is LoadSkills but also returns validation diagnostics. Like LoadSkills it treats the project as UNTRUSTED.
func LoadSkillsWithTrust ¶ added in v0.84.20
func LoadSkillsWithTrust(cwd string, projectTrusted bool) ([]Skill, []SkillDiagnostic)
LoadSkillsWithTrust is LoadSkillsWithDiagnostics with the project-trust decision supplied by the caller. Passing true scans <cwd>/.pi/skills, which is what pi does once isProjectTrusted() holds; passing false is pi's headless answer. Only a host that has established trust — by prompting, or by an explicit operator opt-in — may pass true.
func PackageDir ¶
func PackageDir() string
PackageDir returns the pi package root directory, mirroring pi's getPackageDir: honor PI_PACKAGE_DIR, else walk up from the executable until a package.json is found, else fall back to the executable's directory. A dist/ holding only a build's copied metadata resolves to the package root above it.
func ReadmePath ¶
func ReadmePath() string
ReadmePath returns the absolute path to the pi package README.md.
func ResolveModel ¶
ResolveModel resolves a model spec to a Model from the catalog (an empty spec resolves to DefaultModelSpec). Kept for source compatibility; the parsed thinking level and warnings are available via ResolveModelPattern.
func TruncateLine ¶
TruncateLine truncates a single line to maxChars, appending a marker. maxChars counts UTF-16 code units like pi's `line.length`/`line.slice` (astral characters count as 2). A slice that would split a surrogate pair yields a lone high surrogate in JS, which serializes as U+FFFD.
Types ¶
type BashExecOptions ¶ added in v0.84.20
type BashExecOptions struct {
// OnData receives interleaved stdout/stderr as it is produced.
OnData func(data []byte)
// TimeoutSeconds bounds the command; zero or less means no timeout.
TimeoutSeconds float64
// Env is the child environment, already assembled.
Env []string
}
BashExecOptions are the per-command controls pi passes to BashOperations.exec.
type BashOperations ¶ added in v0.84.20
type BashOperations struct {
// Exec runs a command, streaming output through OnData, and reports the
// exit code. A NIL exit code with a nil error is pi's `exitCode: null` — the
// child was signal-killed, which pi treats as success with whatever output
// was produced. Abort and timeout come back as ErrShellAborted and
// *ShellTimeoutError.
Exec func(ctx context.Context, command, cwd string, options BashExecOptions) (exitCode *int, err error)
}
BashOperations is the process execution the shell tools perform. pi shares one interface between bash and powershell (`PowerShellOperations = BashOperations`), so the port does too.
func EnvBashOperations ¶ added in v0.84.20
func EnvBashOperations(env ExecutionEnv) BashOperations
EnvBashOperations backs the shell tools with an ExecutionEnv's Shell half.
func LocalBashOperations ¶ added in v0.84.20
func LocalBashOperations(config shellToolConfig) BashOperations
LocalBashOperations runs commands on the local machine through the shell the tool config resolves, porting pi's createLocalBashOperations (bash.ts:84).
type BranchContext ¶
type BranchContext struct {
Messages []agent.AgentMessage
ThinkingLevel string
Provider string
ModelID string
}
BranchContext is the reconstructed LLM context for a branch.
type BuildSystemPromptOptions ¶
type BuildSystemPromptOptions struct {
CustomPrompt string
SelectedTools []string
ToolSnippets map[string]string
PromptGuidelines []string
AppendSystemPrompt string
Cwd string
ContextFiles []ContextFile
Skills []Skill
// ReadmePath/DocsPath/ExamplesPath are the absolute pi documentation paths
// referenced by the "Pi documentation" prompt section. Empty values fall back
// to ReadmePath()/DocsPath()/ExamplesPath().
ReadmePath string
DocsPath string
ExamplesPath string
}
BuildSystemPromptOptions configures buildSystemPrompt.
type CompactionSettings ¶
type CompactionSettings struct {
Enabled bool
ReserveTokens int
KeepRecentTokens int
// SessionID, when set, is the routing session ID summarization requests
// reuse instead of minting a fresh one per request; cache retention stays
// "none" either way (pi compact()'s optional sessionId param, upstream
// 58302d34e "support compaction routing sessions").
SessionID string
}
CompactionSettings configures automatic context-window compaction (port of pi's CompactionSettings / DEFAULT_COMPACTION_SETTINGS).
type ContextFile ¶
ContextFile is a project context file injected into the system prompt.
func LoadProjectContextFiles ¶
func LoadProjectContextFiles(cwd string) []ContextFile
LoadProjectContextFiles discovers context files (AGENTS.override.md, else AGENTS.md/CLAUDE.md): the global one under agentDir first, then each ancestor directory of cwd from root down to cwd. Mirrors loadProjectContextFiles.
type CreateRemoteSessionOptions ¶ added in v0.84.17
type CreateRemoteSessionOptions struct {
Cwd string
Model *protocol.ModelRef
ThinkingLevel *protocol.ThinkingLevel
}
CreateRemoteSessionOptions are the properties of a session to create.
type EditOperations ¶ added in v0.84.20
type EditOperations struct {
ReadFile func(ctx context.Context, absolutePath string) ([]byte, error)
WriteFile func(ctx context.Context, absolutePath, content string) error
// Access reports whether the file is readable AND writable (pi passes
// R_OK|W_OK here, where the read tool passes R_OK alone).
Access func(ctx context.Context, absolutePath string) error
}
EditOperations are the file operations the edit tool performs.
func DefaultEditOperations ¶ added in v0.84.20
func DefaultEditOperations() EditOperations
DefaultEditOperations reads and writes the local filesystem.
func EnvEditOperations ¶ added in v0.84.20
func EnvEditOperations(env ExecutionEnv) EditOperations
EnvEditOperations backs the edit tool with an ExecutionEnv.
type ExecutionEnv ¶ added in v0.84.20
type ExecutionEnv interface {
FileSystem
Shell
}
ExecutionEnv is the filesystem and process-execution environment the agent runs against, ported from pi's `ExecutionEnv extends FileSystem, Shell` (packages/agent/src/harness/types.ts:231-315 at ccfe79ed2).
It exists so that "where do files live and how do commands run" is an injected capability rather than a hard-coded call to the local OS: a host can point the agent at a sandbox, a container, or a remote machine without the tools knowing. The port's own default is LocalEnv, which is exactly the behavior the tools had before this seam existed.
Two deliberate renderings of pi's shape, both standing conventions here: pi's `Result<T, FileError>` becomes Go's `(T, error)`, and pi's trailing `abortSignal?: AbortSignal` becomes a leading `context.Context`.
type FileInfo ¶ added in v0.84.20
FileInfo describes a path without following symlinks (pi FileInfo).
type FileKind ¶ added in v0.84.20
type FileKind string
FileKind is the sort of thing a path addresses (pi FileInfo.kind).
type FileSystem ¶ added in v0.84.20
type FileSystem interface {
// Cwd is the working directory relative paths resolve against.
Cwd() string
// AbsolutePath returns an absolute path without requiring it to exist and
// without resolving symlinks.
AbsolutePath(ctx context.Context, path string) (string, error)
// JoinPath joins segments in the filesystem's namespace without requiring
// the result to exist.
JoinPath(ctx context.Context, parts []string) (string, error)
// ReadTextFile reads a whole UTF-8 file.
ReadTextFile(ctx context.Context, path string) (string, error)
// ReadTextLines reads UTF-8 lines, stopping once maxLines have been read.
// A maxLines of 0 or less means "no limit".
ReadTextLines(ctx context.Context, path string, maxLines int) ([]string, error)
// ReadBinaryFile reads a whole file as bytes.
ReadBinaryFile(ctx context.Context, path string) ([]byte, error)
// WriteFile creates or overwrites a file, creating parent directories.
WriteFile(ctx context.Context, path string, content []byte) error
// AppendFile creates or appends to a file, creating parent directories.
AppendFile(ctx context.Context, path string, content []byte) error
// RenameFile atomically renames a file, replacing an existing destination.
// It does not copy across filesystems.
RenameFile(ctx context.Context, sourcePath, destinationPath string) error
// Stat returns metadata for a path without following symlinks.
//
// Named Stat rather than pi's `fileInfo` because a Go method may not share
// a name with the type it returns.
Stat(ctx context.Context, path string) (FileInfo, error)
// ListDir lists a directory's direct children without following symlinks.
ListDir(ctx context.Context, path string) ([]FileInfo, error)
// CanonicalPath resolves symlinks for an existing path.
CanonicalPath(ctx context.Context, path string) (string, error)
// Exists reports whether a path exists. A missing path is (false, nil);
// anything else — a permission failure, say — is an error.
Exists(ctx context.Context, path string) (bool, error)
}
FileSystem is the file half of an ExecutionEnv. Paths that are not absolute resolve against Cwd.
type FindOperations ¶ added in v0.84.20
type FindOperations struct {
Exists func(ctx context.Context, absolutePath string) (bool, error)
// Glob returns paths matching a pattern. Nil keeps pi's behavior, where the
// default is a placeholder and real matching happens in the tool via fd.
Glob func(ctx context.Context, pattern, cwd string, ignore []string, limit int) ([]string, error)
}
FindOperations are the file operations the find tool performs.
func DefaultFindOperations ¶ added in v0.84.20
func DefaultFindOperations() FindOperations
DefaultFindOperations checks the local filesystem. Glob is nil, matching pi, whose default is a placeholder because real matching happens in the tool.
func EnvFindOperations ¶ added in v0.84.20
func EnvFindOperations(env ExecutionEnv) FindOperations
EnvFindOperations backs the find tool with an ExecutionEnv. Glob stays nil, as in pi's default: an env exposes no glob, so matching stays with the tool.
type GrepOperations ¶ added in v0.84.20
type GrepOperations struct {
// IsDirectory reports whether a path is a directory, erroring if it does
// not exist.
IsDirectory func(ctx context.Context, absolutePath string) (bool, error)
// ReadFile reads a file's contents. NOTE the divergence recorded at the top
// of this file: here this is the PRIMARY scan read, not pi's context-line
// fetch, because the port matches in Go rather than shelling out to
// ripgrep. Whatever this returns is what grep can match.
ReadFile func(ctx context.Context, absolutePath string) ([]byte, error)
}
func DefaultGrepOperations ¶ added in v0.84.20
func DefaultGrepOperations() GrepOperations
DefaultGrepOperations reads the local filesystem.
func EnvGrepOperations ¶ added in v0.84.20
func EnvGrepOperations(env ExecutionEnv) GrepOperations
EnvGrepOperations backs the grep tool with an ExecutionEnv.
type LocalEnv ¶ added in v0.84.20
type LocalEnv struct {
// Dir is the working directory. An empty Dir means the process's own.
Dir string
}
LocalEnv is the ExecutionEnv backed by the machine the agent runs on. It is the port's default and reproduces exactly what the tools did before this seam existed.
func NewLocalEnv ¶ added in v0.84.20
NewLocalEnv returns a LocalEnv rooted at dir.
func (*LocalEnv) AbsolutePath ¶ added in v0.84.20
AbsolutePath implements FileSystem.
func (*LocalEnv) AppendFile ¶ added in v0.84.20
AppendFile implements FileSystem.
func (*LocalEnv) CanonicalPath ¶ added in v0.84.20
CanonicalPath implements FileSystem.
func (*LocalEnv) Cleanup ¶ added in v0.84.20
Cleanup implements Shell. LocalEnv holds no shell resources between calls — every Exec starts and reaps its own process — so there is nothing to release.
func (*LocalEnv) Exec ¶ added in v0.84.20
func (e *LocalEnv) Exec(ctx context.Context, command string, options *ShellExecOptions) (ShellResult, error)
Exec implements Shell by running the command through the machine's shell, the same resolution the bash tool uses (getShellConfig). A non-zero exit is reported in the result; an error means the command could not be started, or the context ended it.
func (*LocalEnv) ReadBinaryFile ¶ added in v0.84.20
ReadBinaryFile implements FileSystem.
func (*LocalEnv) ReadTextFile ¶ added in v0.84.20
ReadTextFile implements FileSystem.
func (*LocalEnv) ReadTextLines ¶ added in v0.84.20
ReadTextLines implements FileSystem.
func (*LocalEnv) RenameFile ¶ added in v0.84.20
RenameFile implements FileSystem.
type LsOperations ¶ added in v0.84.20
type LsOperations struct {
Exists func(ctx context.Context, absolutePath string) (bool, error)
// Stat reports whether a path is a directory, erroring if not found.
Stat func(ctx context.Context, absolutePath string) (isDir bool, err error)
// Readdir lists a directory's entry names.
Readdir func(ctx context.Context, absolutePath string) ([]string, error)
}
LsOperations are the file operations the ls tool performs.
func DefaultLsOperations ¶ added in v0.84.20
func DefaultLsOperations() LsOperations
DefaultLsOperations lists the local filesystem.
func EnvLsOperations ¶ added in v0.84.20
func EnvLsOperations(env ExecutionEnv) LsOperations
EnvLsOperations backs the ls tool with an ExecutionEnv.
type NoToolsMode ¶
type NoToolsMode string
NoToolsMode controls default tool suppression (mirrors createAgentSession).
const ( // NoToolsOff keeps the default built-in tools enabled. NoToolsOff NoToolsMode = "" // NoToolsAll starts with no tools enabled. NoToolsAll NoToolsMode = "all" // NoToolsBuiltin disables the default built-in tools but keeps custom tools. NoToolsBuiltin NoToolsMode = "builtin" )
type ProcessImageResult ¶ added in v0.2.8
type ProcessImageResult struct {
Ok bool
Data []byte // raw image bytes to attach (not base64)
MimeType string
Hints []string
Message string
}
ProcessImageResult mirrors pi's discriminated ProcessImageResult (utils/image-process.ts). On success Ok is true and Data/MimeType/Hints are populated; on failure Ok is false and Message holds the omission note.
type ReadOperations ¶ added in v0.84.20
type ReadOperations struct {
// ReadFile reads a file's contents. Required.
ReadFile func(ctx context.Context, absolutePath string) ([]byte, error)
// Access reports whether the file is readable, returning an error if not.
// Required.
Access func(ctx context.Context, absolutePath string) error
// DetectImageMimeType returns the image MIME type for a path, or "" for a
// non-image. Optional — nil uses the built-in detector, matching pi's `?`.
DetectImageMimeType func(ctx context.Context, absolutePath string) string
}
ReadOperations are the file operations the read tool performs.
func DefaultReadOperations ¶ added in v0.84.20
func DefaultReadOperations() ReadOperations
DefaultReadOperations reads from the local filesystem.
func EnvReadOperations ¶ added in v0.84.20
func EnvReadOperations(env ExecutionEnv) ReadOperations
EnvReadOperations backs the read tool with an ExecutionEnv.
type RemoteSession ¶ added in v0.84.17
type RemoteSession struct {
// contains filtered or unexported fields
}
RemoteSession drives one remote coding session over a client.Client: it owns the attachment, serializes the mutations that may act on it, and maintains the transcript projection subscribers render. It is the port of pi's RemoteSession (packages/coding-agent/src/client/remote-session.ts).
Concurrency. pi serializes by chaining promises on a single-threaded event loop; here one mutex guards every field, and it is never held while a network request runs or a subscriber is called. Concurrency is refused rather than queued, exactly as in pi: a second mutation attempted while one is in flight fails with a RemoteSessionBusyError instead of waiting its turn. The one exception is Abort, which preempts an in-flight Submit and restores it on the way out.
A second mutex orders what subscribers are told. State changes reach a session from two directions — an operation unwinding on its caller's goroutine and an event folded in on the client's — and unordered delivery would let the older of two states arrive last and stay on screen. notifyMu makes a change and the notification announcing it one indivisible step, as they are on pi's event loop. It is always taken before mu and never after: a subscriber may read the session while it runs, and holding mu across the wait would deadlock against exactly that.
DIVERGENCE (deliberate): every mutation takes a context. pi has no cancellation at all, so this is new surface rather than a changed behavior — but a Go caller that cannot abandon a blocked network call has no way to shut down. Attachment work (Open, Create, Reconnect) runs under context.WithoutCancel: Close waits for it, and honoring the caller's cancellation there would strand the server holding an attachment nobody owns. Close's own context bounds that wait.
func CreateRemoteSession ¶ added in v0.84.17
func CreateRemoteSession( ctx context.Context, c *client.Client, create CreateRemoteSessionOptions, opts RemoteSessionOptions, ) (*RemoteSession, error)
CreateRemoteSession starts a new session and takes it over.
func OpenRemoteSession ¶ added in v0.84.17
func OpenRemoteSession( ctx context.Context, c *client.Client, sessionID string, opts RemoteSessionOptions, ) (*RemoteSession, error)
OpenRemoteSession takes an exclusive lease on an existing session, closing the half-built session if the attachment fails so a caller never has to clean up something it never got. The client is borrowed, not owned: closing the session leaves the client connected.
func (*RemoteSession) Abort ¶ added in v0.84.17
func (s *RemoteSession) Abort(ctx context.Context) error
Abort stops the running turn. It is the one operation that may preempt another: an abort issued while a submit is still waiting for its response takes over, and the submit's lifecycle is restored underneath it if it is still running when the abort finishes. Aborting an idle session does nothing.
func (*RemoteSession) Close ¶ added in v0.84.17
func (s *RemoteSession) Close(ctx context.Context) error
Close disposes of the session: it stops accepting work, fails whatever is in flight, releases the attachment, and drops every subscriber. The borrowed client is left connected. It is pi's dispose(), named for Go's io.Closer convention, and is safe to call more than once — later callers wait for the first and get its result.
Close blocks until the attachment is released, including any acquisition an operation had already started, so that no server-side attachment outlives the session that owns it.
The context bounds this caller's wait, not the disposal's outcome: a caller that gives up gets its own context's error, while the cleanup settles on what it actually achieved, which is what every later caller is told. The detach is issued under the first caller's context — that is the budget the disposal has — so a first caller that abandons a slow detach can still cut it short, but it no longer latches its own impatience as the answer for everyone behind it.
func (*RemoteSession) ConnectionState ¶ added in v0.84.17
func (s *RemoteSession) ConnectionState() client.ConnectionState
ConnectionState is where the borrowed client's connection sits.
func (*RemoteSession) Create ¶ added in v0.84.17
func (s *RemoteSession) Create(ctx context.Context, opts CreateRemoteSessionOptions) error
Create starts a new session and attaches to it, replacing the current attachment.
func (*RemoteSession) Disposed ¶ added in v0.84.17
func (s *RemoteSession) Disposed() bool
Disposed reports whether Close has been called.
func (*RemoteSession) ID ¶ added in v0.84.17
func (s *RemoteSession) ID() string
ID is the attached session's identifier, or "" while unbound.
func (*RemoteSession) Models ¶ added in v0.84.17
func (s *RemoteSession) Models() []protocol.ModelMetadata
Models is what the server can run, as of the last server snapshot. It is empty rather than nil before the first one, matching pi's `?? []`: the only thing that distinguishes the two in Go is a == nil check, and a caller writing one would be asking whether the client has a snapshot, which is not a question this accessor answers.
func (*RemoteSession) OnConnectionStateChange ¶ added in v0.84.17
func (s *RemoteSession) OnConnectionStateChange( listener func(client.ConnectionStateChange), ) (client.Unsubscribe, error)
OnConnectionStateChange forwards the borrowed client's connection lifecycle.
func (*RemoteSession) Open ¶ added in v0.84.17
func (s *RemoteSession) Open(ctx context.Context, sessionID string) error
Open takes an exclusive lease on a session, replacing the current attachment. Reopening the session that is already attached and ready is a no-op.
func (*RemoteSession) Operation ¶ added in v0.84.17
func (s *RemoteSession) Operation() (RemoteSessionOperation, bool)
Operation is the mutation in flight. The second result is false when none is.
func (*RemoteSession) Phase ¶ added in v0.84.17
func (s *RemoteSession) Phase() (protocol.SessionPhase, bool)
Phase is the attached session's phase. The second result is false while unbound, which is pi's `undefined` phase.
func (*RemoteSession) Reconnect ¶ added in v0.84.17
func (s *RemoteSession) Reconnect(ctx context.Context) error
Reconnect rebuilds the transport and reacquires the same session. The old handle is dropped rather than detached: whatever it was leasing died with the connection.
func (*RemoteSession) Sessions ¶ added in v0.84.17
func (s *RemoteSession) Sessions() []protocol.SessionMetadata
Sessions is every session the server knows about, as of the last server snapshot. It is empty rather than nil before the first one, for the reason given on Models.
func (*RemoteSession) SetModel ¶ added in v0.84.17
SetModel switches the session's model. Only an idle session may be reconfigured.
func (*RemoteSession) SetThinking ¶ added in v0.84.17
func (s *RemoteSession) SetThinking(ctx context.Context, level protocol.ThinkingLevel) error
SetThinking switches the session's thinking level. Only an idle session may be reconfigured.
func (*RemoteSession) Snapshot ¶ added in v0.84.17
func (s *RemoteSession) Snapshot() *protocol.SessionSnapshot
Snapshot is the authoritative session state, or nil while unbound.
func (*RemoteSession) State ¶ added in v0.84.17
func (s *RemoteSession) State() RemoteSessionState
State is everything a subscriber renders, as of now.
func (*RemoteSession) Submit ¶ added in v0.84.17
func (s *RemoteSession) Submit(ctx context.Context, text string) error
Submit sends text to the session: a new turn while idle, a steer while one is running. Text that is blank once trimmed is not a message and is dropped.
func (*RemoteSession) Subscribe ¶ added in v0.84.17
func (s *RemoteSession) Subscribe(listener func(RemoteSessionState)) (client.Unsubscribe, error)
Subscribe registers a listener for every state change and calls it once immediately, so a subscriber renders without waiting for the next event. The immediate call is ordered with every other notification, so a subscriber cannot be handed a newer state before the one it registered against.
A listener must not call Subscribe: the registration waits for the notification in flight to finish, which would be the one that called it.
type RemoteSessionBusyError ¶ added in v0.84.17
type RemoteSessionBusyError struct{ Operation RemoteSessionOperation }
RemoteSessionBusyError reports an operation refused because another one is already in flight. Only abort may preempt, and only a submit.
func (*RemoteSessionBusyError) Error ¶ added in v0.84.17
func (e *RemoteSessionBusyError) Error() string
type RemoteSessionLifecycle ¶ added in v0.84.17
type RemoteSessionLifecycle struct {
Status RemoteSessionStatus
Operation RemoteSessionOperation
}
RemoteSessionLifecycle is pi's discriminated lifecycle union flattened into one comparable value: Operation is meaningful only while Status is LifecycleBusy, and is empty otherwise, so two lifecycles compare equal exactly when pi's would deep-equal.
type RemoteSessionOperation ¶ added in v0.84.17
type RemoteSessionOperation string
RemoteSessionOperation names the one mutation a RemoteSession may have in flight.
const ( OperationOpen RemoteSessionOperation = "open" OperationCreate RemoteSessionOperation = "create" OperationSubmit RemoteSessionOperation = "submit" OperationAbort RemoteSessionOperation = "abort" OperationSetModel RemoteSessionOperation = "setModel" OperationSetThinking RemoteSessionOperation = "setThinking" OperationReconnect RemoteSessionOperation = "reconnect" )
type RemoteSessionOptions ¶ added in v0.84.17
type RemoteSessionOptions struct {
// OnListenerError receives failures raised by subscribers. Subscribers run
// on whichever goroutine produced the notification, so a panicking one is
// contained and reported here rather than allowed to corrupt session state.
//
// One notification is delivered at a time and in the order the changes
// happened, so a subscriber that renders whatever it was handed last is
// never left showing a state the session has moved past. A subscriber may
// read the session and may unsubscribe itself or a peer while it runs, but
// it must not call Subscribe or any mutation — those wait for the
// notification in flight, which is the one it is running.
OnListenerError func(error)
}
RemoteSessionOptions configures a RemoteSession.
type RemoteSessionState ¶ added in v0.84.17
type RemoteSessionState struct {
Lifecycle RemoteSessionLifecycle
// Snapshot is the authoritative session state, or nil while unbound. It
// must be treated as read-only.
Snapshot *protocol.SessionSnapshot
// Transcript is the snapshot's transcript with streaming progress projected
// over it. It is never nil; an unbound session renders as empty.
Transcript []protocol.TranscriptItem
}
RemoteSessionState is everything a subscriber needs to render the session.
type RemoteSessionStatus ¶ added in v0.84.17
type RemoteSessionStatus string
RemoteSessionStatus is where a RemoteSession sits in its lifecycle.
const ( // LifecycleUnbound means no session is attached. LifecycleUnbound RemoteSessionStatus = "unbound" // LifecycleReady means a session is attached and idle locally. LifecycleReady RemoteSessionStatus = "ready" // LifecycleBusy means an operation is in flight. LifecycleBusy RemoteSessionStatus = "busy" // LifecycleDisposed means the session has been closed and will not work again. LifecycleDisposed RemoteSessionStatus = "disposed" )
type ResizeResult ¶
type ResizeResult struct {
Data []byte // raw image bytes to send to the model (not base64)
MimeType string
OriginalWidth int
OriginalHeight int
Width int
Height int
WasResized bool
}
ResizeResult mirrors the object pi's resizeImage returns.
func ResizeImageDecision ¶
func ResizeImageDecision(data []byte, mimeType string) (ResizeResult, bool)
ResizeImageDecision exposes the image-pipeline decision (dimensions, format, wasResized) for differential-testing tools. It mirrors pi's resizeImage.
type ResolvedModel ¶
type ResolvedModel struct {
Model *ai.Model
// ThinkingLevel is the level parsed from a ":<level>" suffix in the spec
// (pi parseModelPattern), or "" when the spec carried none.
ThinkingLevel string
// Warning carries pi's non-fatal resolution warnings (e.g. the custom-id
// fallback for an unknown model under a known provider).
Warning string
}
ResolvedModel is the result of resolving a model spec.
func ResolveModelPattern ¶
func ResolveModelPattern(spec string) (ResolvedModel, error)
ResolveModelPattern ports pi's resolveCliModel (model-resolver.ts): a slash-prefix is treated as a provider ONLY when it matches a known provider; otherwise the whole string is matched as a model id across providers (OpenRouter-style ids contain slashes). Matching is case-insensitive and a ":<thinkingLevel>" suffix (off|minimal|low|medium|high|xhigh) is parsed off and returned alongside. An unknown model under a known provider falls back to a synthetic custom-id model with a warning (pi buildFallbackModel); a thinking-level suffix is stripped from the custom id first (pi 9fd75b8a — upstream gates that parse on --thinking being unset, and ResolveModelPattern takes no thinking argument, so it always behaves as that path).
type RunResult ¶
type RunResult struct {
// Text is the concatenated text of the final assistant message.
Text string
// Messages are the messages produced during this run (prompt → final).
Messages []agent.AgentMessage
// ToolCalls are the tool calls the model made during this run.
ToolCalls []ai.ToolCall
// Usage is the aggregate token usage + cost across every provider request in
// this run (multi-turn tool loops are summed).
Usage ai.Usage
// StopReason is the final assistant stop reason.
StopReason ai.StopReason
// ErrorMessage is set when the run failed or was aborted.
ErrorMessage string
}
RunResult is the structured outcome of a single Run turn, suited to embedding pi as an SDK rather than a CLI.
type Session ¶
type Session struct {
Agent *agent.Agent
Model *ai.Model
Cwd string
Recorder *SessionRecorder
// contains filtered or unexported fields
}
Session is a coding-agent session: an Agent wired with a model, tools, and the coding system prompt.
func NewSession ¶
func NewSession(opts SessionOptions) *Session
NewSession builds a Session. If Tools is nil, the default coding tools are used; if SystemPrompt is empty, a system prompt is built from the tool set.
func (*Session) Continue ¶
Continue continues from the current transcript (last message must be a user or tool-result message, or a queued message must exist).
func (*Session) EnableCompaction ¶
func (s *Session) EnableCompaction(settings CompactionSettings)
EnableCompaction installs an automatic compaction TransformContext on the session's agent using the given settings. When the estimated context exceeds the model's window minus ReserveTokens, older turns are summarized (via the session's model) into a single checkpoint message and recent turns are kept.
func (*Session) FollowUp ¶
func (s *Session) FollowUp(m agent.AgentMessage)
FollowUp queues a message to run after the agent would otherwise stop.
func (*Session) History ¶
func (s *Session) History() []agent.AgentMessage
History returns the current transcript.
func (*Session) LastAssistantText ¶
LastAssistantText returns the most recent assistant message text.
func (*Session) LoadHistory ¶
func (s *Session) LoadHistory(messages []agent.AgentMessage)
LoadHistory seeds the agent transcript from a prior session's messages.
func (*Session) Record ¶
func (s *Session) Record(r *SessionRecorder)
Record attaches a SessionRecorder; finalized messages are appended to it. The write is guarded because bash commands read the recorder concurrently for their PI_SESSION_ID/PI_SESSION_FILE metadata. Assigning the exported Recorder field directly bypasses that guard — use this method.
func (*Session) Run ¶
func (s *Session) Run(ctx context.Context, prompt string, images ...ai.ImageContent) (*RunResult, error)
Run executes a prompt and returns a structured RunResult. Unlike RunPrint it does not write to an io.Writer — use Subscribe for streaming.
func (*Session) RunMessages ¶
func (s *Session) RunMessages(ctx context.Context, prompts []agent.AgentMessage) (*RunResult, error)
RunMessages executes explicit prompt messages and returns a structured result.
func (*Session) RunPrint ¶
RunPrint runs a single prompt and renders streaming output to w, returning the final assistant text. Tool activity is rendered as compact status lines.
func (*Session) SetThinkingLevel ¶
func (s *Session) SetThinkingLevel(level agent.ThinkingLevel)
SetThinkingLevel sets the reasoning level for future turns.
func (*Session) Steer ¶
func (s *Session) Steer(m agent.AgentMessage)
Steer queues a message to inject after the current assistant turn finishes.
func (*Session) Subscribe ¶
Subscribe registers an agent event listener (passthrough to the Agent), useful for streaming tokens/tool activity into an app UI. Returns an unsubscribe func.
func (*Session) WaitForIdle ¶
func (s *Session) WaitForIdle()
WaitForIdle blocks until the current run and its listeners finish.
type SessionEntry ¶
type SessionEntry struct {
ID string
ParentID string
Type string // "message" | "model_change" | "thinking_level_change" | "branch_summary" | "compaction" | "custom_message" | ...
Timestamp string
Message ai.Message // for Type=="message"
Provider string // for "model_change"
ModelID string // for "model_change"
ThinkingLevel string // for "thinking_level_change"
Summary string // for "branch_summary" / "compaction"
FromID string // for "branch_summary"
// compaction
FirstKeptEntryID string
// RetainedTail holds the compaction's kept tail inlined on the entry (pi
// CompactionEntry.retainedTail, upstream 9e7582aa). When set, it replaces the
// firstKeptEntryId walk: the reconstructed context is the summary followed by
// these messages. Already filtered through convertToLlm (excluded entries drop
// out during parse).
RetainedTail []agent.AgentMessage
// custom_message
CustomType string
Content ai.ContentList
}
SessionEntry is one node in a session tree (port of pi's SessionEntry). Entries form a tree via ID/ParentID; the active branch is the path from a leaf to the root.
type SessionInfo ¶
SessionInfo summarizes a stored session file.
func LatestSession ¶
func LatestSession(cwd string) (SessionInfo, bool)
LatestSession returns the most recent stored session for cwd, if any.
func ListSessions ¶
func ListSessions(cwd string) []SessionInfo
ListSessions returns stored sessions for cwd, newest first.
type SessionOptions ¶
type SessionOptions struct {
Model *ai.Model
Cwd string
// Tools, when non-nil, is used verbatim and bypasses name-based selection.
Tools []agent.AgentTool
// ToolNames is an allowlist of built-in tool names. When nil and NoTools is
// off, the default set [read, bash, edit, write] is used.
ToolNames []string
// ExcludeTools is a denylist applied after ToolNames.
ExcludeTools []string
// NoTools suppresses the default built-in tools ("all" or "builtin").
NoTools NoToolsMode
// CustomTools are appended to the resolved built-in set.
CustomTools []agent.AgentTool
SystemPrompt string
ThinkingLevel agent.ThinkingLevel
APIKey string
SessionID string
// TrustProject enables discovery of project-local resources under
// <cwd>/.pi — currently the skills directory. It is pi's isProjectTrusted()
// and it defaults to FALSE, which is the answer pi itself gives a host with
// no UI to prompt with (project-trust.ts). Set it only after trust has
// actually been established: a project skill's name and description reach
// the system prompt, so an untrusted repo would otherwise get to author part
// of the prompt. See LoadSkillsWithTrust.
TrustProject bool
// Models, when set, is the model runtime used to resolve request auth for
// summarization requests (pi AgentSession's _modelRuntime). It is needed
// only for providers that carry their endpoint in the credential rather
// than the catalog — see Session.summarizationRequestModel. Nil leaves
// summarization on the session's own model, as before.
Models ai.Models
// Per-request provider controls (all optional).
Temperature *float64
MaxTokens *int
CacheRetention ai.CacheRetention
MaxRetries int
TimeoutMs int
MaxRetryDelayMs *int
Transport ai.Transport
ThinkingBudgets *ai.ThinkingBudgets
// Headers are extra HTTP headers merged into every provider request
// (e.g. OpenAI-Organization). A nil value suppresses a provider default
// header of that name (see ai.ProviderHeaders).
Headers ai.ProviderHeaders
// OnPayload can inspect/replace the provider request body before sending.
OnPayload func(payload any, model *ai.Model) (any, error)
// OnResponse is invoked after the HTTP response is received.
OnResponse func(resp ai.ProviderResponse, model *ai.Model) error
// BeforeToolCall runs after a tool call's args are validated and before it
// executes. Return {Block:true, Reason:...} to deny it (the loop emits an
// error tool result). This is the native equivalent of pi's tool_call
// extension hook — use it for permission gates, path protection, etc.
BeforeToolCall func(ctx context.Context, c agent.BeforeToolCallContext) *agent.BeforeToolCallResult
// AfterToolCall runs after a tool finishes; return overrides for the result.
AfterToolCall func(ctx context.Context, c agent.AfterToolCallContext) *agent.AfterToolCallResult
// Compaction, when non-nil, installs automatic context-window compaction.
// Use &DefaultCompactionSettings for pi's defaults.
Compaction *CompactionSettings
// StreamFn overrides the stream function (for tests). Default: ai.StreamSimple.
StreamFn agent.StreamFn
}
SessionOptions configures a coding Session. The tool fields mirror pi's createAgentSession: when Tools is nil the built-in set is resolved from ToolNames/ExcludeTools/NoTools, then CustomTools are appended.
type SessionRecorder ¶
type SessionRecorder struct {
// contains filtered or unexported fields
}
SessionRecorder appends an agent transcript to a JSONL session file, matching pi's append-only format (header + linear message/model/thinking entries).
Writes are withheld until the first assistant message is recorded (pi _persist): a started-but-unused session leaves no file on disk. Pending entries are buffered and flushed atomically on the first assistant message.
func ResumeSession ¶
func ResumeSession(path string) (*SessionRecorder, error)
ResumeSession opens an existing session file for appending, porting pi's SessionManager.setSessionFile resume semantics (session-manager.ts:792-822): entries load from the file, the leaf is the file's last entry (new entries branch from it), and the manager is marked flushed so every subsequent entry appends to the file immediately (no withhold-until-assistant buffering).
func StartSession ¶
StartSession creates a new session for cwd and buffers the header plus an initial model entry. The session file is created lazily on the first recorded assistant message.
When thinkingLevel is given, a thinking_level_change entry is recorded after the model_change, matching pi's createAgentSession for new sessions (sdk.ts:362-373: appendModelChange then appendThinkingLevelChange; the thinking entry is written even when there is no model). The variadic parameter keeps existing callers source-compatible.
func (*SessionRecorder) Close ¶
func (r *SessionRecorder) Close() error
Close closes the session file.
func (*SessionRecorder) ForkFrom ¶
func (r *SessionRecorder) ForkFrom(entryID string)
ForkFrom sets the parent for subsequent entries to entryID, so new entries branch off an earlier point in the tree instead of extending the latest leaf.
func (*SessionRecorder) LastEntryID ¶
func (r *SessionRecorder) LastEntryID() string
LastEntryID returns the id of the most recently written entry (a branch point).
func (*SessionRecorder) Path ¶
func (r *SessionRecorder) Path() string
Path returns the session file path.
func (*SessionRecorder) RecordMessage ¶
func (r *SessionRecorder) RecordMessage(m agent.AgentMessage) string
RecordMessage appends a message entry for an agent transcript message and returns its entry id (usable as a fork point).
func (*SessionRecorder) RecordModelChange ¶
func (r *SessionRecorder) RecordModelChange(provider, modelID string)
RecordModelChange appends a model-change entry.
func (*SessionRecorder) RecordThinkingLevel ¶
func (r *SessionRecorder) RecordThinkingLevel(level string)
RecordThinkingLevel appends a thinking-level-change entry.
type SessionTree ¶
type SessionTree struct {
Header SessionInfo
Entries []*SessionEntry
// LeafID is the active leaf; defaults to the last entry in the file.
LeafID string
// contains filtered or unexported fields
}
SessionTree is the parsed entry tree of a session file.
func LoadSessionTree ¶
func LoadSessionTree(path string) (*SessionTree, error)
LoadSessionTree parses a JSONL session file into its entry tree.
func (*SessionTree) Branch ¶
func (t *SessionTree) Branch(fromID ...string) []*SessionEntry
Branch returns the entries along the path from the given leaf (default LeafID) up to the root, in root→leaf order (port of getBranch). An unknown leaf id falls back to the last entry, matching pi.
func (*SessionTree) BuildContext ¶
func (t *SessionTree) BuildContext(leafID ...string) BranchContext
BuildContext reconstructs the LLM message list, thinking level, and model for the active branch. It mirrors pi's buildSessionContext followed by convertToLlm: it handles the compaction checkpoint (emit summary, then kept entries from firstKeptEntryId, then post-compaction entries) and converts custom_message / branch_summary entries to their user-message form with pi's exact wrapper text.
func (*SessionTree) BuildContextNull ¶
func (t *SessionTree) BuildContextNull() BranchContext
BuildContextNull returns the empty context pi produces for an explicit-null leaf (leafId === null) — the "navigated to before the first entry" state.
func (*SessionTree) Leaves ¶
func (t *SessionTree) Leaves() []*SessionEntry
Leaves returns the entries that have no children (the tips of each branch).
type Shell ¶ added in v0.84.20
type Shell interface {
// Exec runs a shell command. A non-zero exit is reported in the result, not
// as an error; an error means the command could not be run or was cut short.
Exec(ctx context.Context, command string, options *ShellExecOptions) (ShellResult, error)
// Cleanup releases shell resources. Best-effort: it must not panic, and a
// nil return is the normal case.
Cleanup() error
}
Shell is the process half of an ExecutionEnv.
type ShellExecOptions ¶ added in v0.84.20
type ShellExecOptions struct {
// Cwd overrides the env's working directory for this command. A relative
// value resolves against the env's Cwd.
Cwd string
// Env carries variables for the command. Values here win over inherited
// ones when InheritEnv is true.
Env map[string]string
// InheritEnv controls whether the ambient environment is inherited. Nil
// means pi's default, which is true — the pointer exists to keep that
// default reachable from the zero value.
InheritEnv *bool
// TimeoutSeconds bounds the command. Zero or less means no timeout.
TimeoutSeconds int
// OnStdout and OnStderr receive output chunks as they are produced.
OnStdout func(chunk string)
OnStderr func(chunk string)
}
ShellExecOptions are the per-command controls for Shell.Exec (pi ShellExecOptions). The zero value is pi's default in every field: run in the env's Cwd, inherit the ambient environment, no timeout, no streaming.
type ShellResult ¶ added in v0.84.20
ShellResult is a finished command (pi's `{stdout, stderr, exitCode}`).
type ShellTimeoutError ¶ added in v0.84.20
type ShellTimeoutError struct{ Seconds float64 }
ShellTimeoutError is what a shell operation returns when the command outran its timeout — pi's `throw new Error("timeout:<seconds>")` (bash.ts:464), rendered as a typed error so the tool can recover the seconds with errors.As instead of parsing a message.
func (*ShellTimeoutError) Error ¶ added in v0.84.20
func (e *ShellTimeoutError) Error() string
type Skill ¶
type Skill struct {
Name string
Description string
FilePath string
BaseDir string
DisableModelInvocation bool
}
Skill is a discovered Agent Skill (SKILL.md with frontmatter).
func LoadSkills ¶
LoadSkills discovers skills under the USER skill directories only (~/.pi/agent/skills and ~/.agents/skills). The project directory <cwd>/.pi/skills is NOT scanned.
That omission is pi's behavior, not a gap. pi discovers the project skills dir only inside `if (projectTrusted)` (package-manager.ts:2417 at ccfe79ed2), and resolveProjectTrusted answers FALSE for a host with no UI to ask with (`if (!options.projectTrustContext.hasUI) return false`, project-trust.ts). This port is headless by construction — it is an SDK plus a non-interactive CLI, and it ships no trust prompt — so untrusted is the faithful default and scanning unconditionally was a parity INVERSION on a security default: a skill's name and description go into the system prompt (FormatSkillsForPrompt) together with an instruction to read the file when the task matches, so a hostile repo could put attacker-authored text in front of the model just by being the cwd.
A host that has actually established trust calls LoadSkillsWithTrust, or sets SessionOptions.TrustProject.
Note pi's ancestor <project>/.agents/skills directories are a SEPARATE discovery that this port does not implement at all (2026-08-18 ruling); passing projectTrusted does not enable them.
Diagnostics are discarded; see LoadSkillsWithDiagnostics.
type SkillDiagnostic ¶
SkillDiagnostic mirrors pi's ResourceDiagnostic for skill loading: a validation warning (or error) with the offending file path.
type TranscriptState ¶ added in v0.84.17
type TranscriptState struct {
// contains filtered or unexported fields
}
TranscriptState is a client-side projection of one session's transcript: the authoritative snapshot the server sent, plus the streaming progress laid over it. It is the port of pi's TranscriptState (packages/coding-agent/src/client/transcript.ts).
The snapshot always wins. Progress is an optimization that lets a viewer render a turn as it is produced; the moment a newer snapshot arrives the projection is rebuilt from it and every accumulated delta is discarded.
A TranscriptState is immutable. Every operation returns the next state — the receiver is never modified — so a value can be read without a lock while another goroutine folds the next event into it. Items handed in are deep copied on the way in, so a caller that keeps mutating what it passed cannot corrupt a projection.
func NewTranscriptState ¶ added in v0.84.17
func NewTranscriptState(snapshot *protocol.SessionSnapshot) *TranscriptState
NewTranscriptState starts a projection from an authoritative snapshot. It is pi's createTranscriptState.
func (*TranscriptState) ApplyProgress ¶ added in v0.84.17
func (s *TranscriptState) ApplyProgress(progress protocol.TranscriptProgress) *TranscriptState
ApplyProgress folds in one incremental update. It is pi's applyTranscriptProgress.
A progress variant this port does not recognize leaves the state untouched, which is what pi does with one too: it reaches its delta branch by elimination, looks the unknown shape's absent message id up, finds nothing, and returns the state it was given.
func (*TranscriptState) ApplySnapshot ¶ added in v0.84.17
func (s *TranscriptState) ApplySnapshot(snapshot *protocol.SessionSnapshot) *TranscriptState
ApplySnapshot folds in a newer authoritative snapshot, discarding every projected delta. It is pi's applyTranscriptSnapshot.
A lower revision for the same session is stale and ignored. A lower revision for a *different* session id is not: revisions count from the attachment, so switching sessions legitimately moves the counter backwards.
func (*TranscriptState) Snapshot ¶ added in v0.84.17
func (s *TranscriptState) Snapshot() *protocol.SessionSnapshot
Snapshot is the authoritative snapshot this projection is built on. The result must be treated as read-only: it is the projection's own copy, shared rather than cloned per call, exactly as pi shares its frozen record.
func (*TranscriptState) Transcript ¶ added in v0.84.17
func (s *TranscriptState) Transcript() []protocol.TranscriptItem
Transcript renders the projection: the snapshot's transcript with any projected replacements applied, then the items progress introduced that the snapshot does not know about yet, then the steering messages the server has accepted but not yet folded into a turn. It is pi's selectTranscript.
type TruncationResult ¶
type TruncationResult struct {
Content string `json:"content"`
Truncated bool `json:"truncated"`
TruncatedBy string `json:"truncatedBy"` // "lines" | "bytes" | "" (pi: null)
TotalLines int `json:"totalLines"`
TotalBytes int `json:"totalBytes"`
OutputLines int `json:"outputLines"`
OutputBytes int `json:"outputBytes"`
LastLinePartial bool `json:"lastLinePartial"`
FirstLineExceedsLimit bool `json:"firstLineExceedsLimit"`
MaxLines int `json:"maxLines"`
MaxBytes int `json:"maxBytes"`
}
TruncationResult describes the outcome of a truncation operation. The JSON field names match pi's TruncationResult shape (truncate.ts) so it can be embedded in tool details payloads.
func TruncateHead ¶
func TruncateHead(content string, maxLines, maxBytes int) TruncationResult
TruncateHead keeps the first N lines/bytes (for file reads).
func TruncateTail ¶
func TruncateTail(content string, maxLines, maxBytes int) TruncationResult
TruncateTail keeps the last N lines/bytes (for command output).
type WriteOperations ¶ added in v0.84.20
type WriteOperations struct {
WriteFile func(ctx context.Context, absolutePath, content string) error
// Mkdir creates a directory and its parents.
Mkdir func(ctx context.Context, dir string) error
}
WriteOperations are the file operations the write tool performs.
func DefaultWriteOperations ¶ added in v0.84.20
func DefaultWriteOperations() WriteOperations
DefaultWriteOperations writes the local filesystem.
func EnvWriteOperations ¶ added in v0.84.20
func EnvWriteOperations(env ExecutionEnv) WriteOperations
EnvWriteOperations backs the write tool with an ExecutionEnv. Mkdir is a no-op because ExecutionEnv.WriteFile already creates parent directories, which is pi's own contract for it ("creating parent directories when supported").