Go client for Herdr's local socket API.
This package speaks Herdr's newline-delimited JSON API over the local API
socket. The typed API targets the Herdr 0.9.0 source at commit
61ca85d5895bb530b59da85beeccdd767f4720d2, which reports internal protocol 22.
The separate binary pane.graphics.stream protocol, including file-frame
transport, is not part of this package.
go get github.com/lib-x/herdr-sock-go@v0.9.0This release adds typed helpers for pane scrolling, selection reads, copy motions and searches, right-click routing, link activation, workspace block moves, group closure, integration discovery, command invocation, and dismissal of announcements and release notes. Existing helper signatures are retained.
WorkspaceCreateParams.SourceWorkspaceIDselects the workspace used by thefollowworking-directory policy.- Worktree list/create/open/remove parameters now accept
TrustRepository. It defaults to false; set it explicitly when you intend to trust repository lifecycle hooks. PaneSplitParams.RightClickandSetPaneInputselectherdrorpaneright-click routing.PaneGraphicsSetParamsacceptsLayerID,ZIndex, and BGRA images.ClearPaneGraphicsLayerclears a named layer;ClearPaneGraphicsstill clears the defaultprimarylayer.GetPaneGraphicsInfonow returns visibility, layer limits, pixel-mouse support, and file-frame capabilities.SubscribeWorkspaceReorderedsubscribes toworkspace.reordered; decode itsworkspace_reorderedpayload withWorkspaceReorderedEvent.
Use Ping to inspect ServerCapabilities, including endpoint generation,
surface interest, and health-check support. RequireCurrentProtocol remains
an optional strict check for protocol 22; it does not negotiate JSON API
compatibility. The stable client endpoint generation is a separate contract.
client_shell.surface.set has a method constant and wire types for schema
reference, but requires a client-shell endpoint and returns
connection_local_only over this package's local JSON socket transport.
Selection and link requests can include ContentRevision to reject stale
coordinates; copy search requires it. For example:
result, err := client.SearchPaneCopy(ctx, herdr.PaneCopySearchParams{
PaneID: "w1:p1",
Query: "error",
Direction: herdr.PaneCopySearchDirectionForward,
Cursor: herdr.PaneTextPoint{Row: 0, Col: 0},
ContentRevision: contentRevision, // Revision of the selected terminal content.
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Total, result.Current)InvokeCommand accepts an opaque CommandID issued by Herdr's client-shell
projection, not shell command text. New methods require a server that supports
them; older servers may reject them even when existing JSON methods still work.
Herdr 0.7.5 uses protocol 17 and is not wire-compatible with the protocol
16 API targeted by this module's v0.7.4 tag. This release also contains Go
source-level changes that mirror Herdr's breaking Agent API update:
MethodAgentSend,AgentSendParams, andClient.SendAgentwere removed. UseSendAgentKeysfor logical key input orPromptAgentto submit text.AgentStartParamsnow requiresName,Kind, andPaneID; useArgsinstead of the previous topology, environment, andArgvfields.StartAgentsubmits into an existing pane and can return before the agent is interactive. CheckInteractiveReadyas described below.
package main
import (
"context"
"fmt"
"log"
herdr "github.com/lib-x/herdr-sock-go"
)
func main() {
ctx := context.Background()
client := herdr.MustNew()
if _, err := client.RequireCurrentProtocol(ctx); err != nil {
log.Fatal(err)
}
pane, err := client.CurrentPane(ctx, nil)
if err != nil {
log.Fatal(err)
}
stripANSI := true
read, err := client.ReadPane(ctx, herdr.PaneReadParams{
PaneID: pane.PaneID,
Source: herdr.ReadRecent,
Format: herdr.ReadFormatText,
StripANSI: &stripANSI,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(read.Text)
}New() follows Herdr's API socket resolution:
WithSocketPath(path)WithSession(name)HERDR_SOCKET_PATHHERDR_SESSION- the default socket under the Herdr config directory
Use Call or CallRaw for methods that do not yet have a typed helper:
var result map[string]any
err := client.Call(ctx, "", herdr.MethodPaneProcessInfo, map[string]any{
"pane_id": "w1:p1",
}, &result)Pane and workspace metadata token patches use map[string]*string. A non-nil
pointer sets the value and a nil pointer clears the token:
summary := "reviewing authentication"
ttl := uint64(60_000)
err := client.ReportWorkspaceMetadata(ctx, herdr.WorkspaceReportMetadataParams{
WorkspaceID: "w1",
Source: "my-tool",
Tokens: map[string]*string{
"summary": &summary,
"old": nil,
},
TTLMS: &ttl,
})WorkspaceInfo, PaneInfo, and AgentInfo expose resolved token maps.
PaneInfo and AgentInfo also expose TerminalTitle and
TerminalTitleStripped.
status := herdr.AgentStatusDone
stream, err := client.Subscribe(ctx,
herdr.SubscribePaneAgentStatusChanged("w1:p1", &status),
)
if err != nil {
log.Fatal(err)
}
defer stream.Close()
event, err := stream.Next(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(event.Event)Metadata snapshot events are available through subscription helpers:
stream, err := client.Subscribe(ctx,
herdr.SubscribeWorkspaceMetadataUpdated(),
herdr.SubscribePaneUpdated(),
)Subscription filters use dot names. Pushed lifecycle envelopes use Herdr's
snake-case event kinds, such as pane_agent_detected; the specialized output,
agent-status, and scroll subscriptions retain their documented dot names.
Herdr 0.7.5 replaces agent.send with logical key input and adds atomic prompt
and wait methods. Agent startup now targets an existing pane:
timeout := uint64(20_000)
agent, argv, err := client.StartAgent(ctx, herdr.AgentStartParams{
Name: "reviewer",
Kind: "codex",
PaneID: "w1:p1",
Args: []string{"--full-auto"},
TimeoutMS: &timeout,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(agent.PaneID, argv)StartAgent returns after submitting the launch command. The returned agent
may still have LaunchPending=true and InteractiveReady=false; check it with
GetAgent before prompting. WaitAgent waits for semantic status, not startup
readiness. TimeoutMS controls how long Herdr permits the managed launch to
become ready and must be between 3001 and 300000 milliseconds.
Submit a prompt and wait for the same live agent to settle:
prompted, err := client.PromptAgent(ctx, herdr.AgentPromptParams{
Target: "reviewer",
Text: "Review the current diff",
Wait: &herdr.AgentPromptWaitOptions{
Until: []herdr.AgentStatus{herdr.AgentStatusDone, herdr.AgentStatusBlocked},
TimeoutMS: &timeout,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(prompted.AgentStatus)Use SendAgentKeys for logical keys such as up and enter, or WaitAgent
when no prompt needs to be submitted. WithTimeout is an outer request cap and
the earlier client or context deadline wins. Use WithTimeout(0) to rely only
on the context and Herdr's TimeoutMS; otherwise leave margin above the server
timeout.
Herdr 0.7.5 also supports transient declarative Agent views:
source := "example.agent-view"
label := "attention"
view, err := client.SetAgentView(ctx, herdr.AgentViewSetParams{
Source: source,
Label: &label,
Filter: herdr.AgentViewIn(
herdr.AgentViewFieldStatus,
herdr.AgentStatusBlocked,
herdr.AgentStatusDone,
),
Sort: []herdr.AgentViewSort{
{Field: herdr.AgentViewSortAttention, Order: herdr.AgentViewSortDescending},
{Field: herdr.AgentViewSortStateChangeSeq, Order: herdr.AgentViewSortDescending},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(view.Active)
defer func() {
_, _ = client.ClearAgentView(context.Background(), herdr.AgentViewClearParams{
Source: &source,
})
}()The view remains active until it is cleared or replaced, its owning plugin is disabled or removed, or the Herdr server exits. Clearing with the same source avoids removing a view owned by a different source.
Open a plugin entrypoint as a floating popup with cell or percentage sizing:
placement := herdr.PluginPanePopup
width, err := herdr.PopupPercent(80)
if err != nil {
log.Fatal(err)
}
height := herdr.PopupCells(24)
pluginPane, err := client.OpenPluginPane(ctx, herdr.PluginPaneOpenParams{
PluginID: "pomodoro",
Entrypoint: "main",
Placement: &placement,
Width: &width,
Height: &height,
Focus: true,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(pluginPane.Pane.PaneID)
defer client.ClosePopup(ctx)Herdr's ui.copy_on_select remains a native configuration setting; it is not
a socket API method.
Run the current pane reader:
go run ./examples/current-paneWatch agent status changes for a pane:
go run ./examples/watch-agent w1:p1