Documentation
¶
Overview ¶
Package agents is autonomous agents for your org: define them, run them, keep every run.
An agent is a model + a system prompt (instructions) + a set of tool names; running one executes a real chat completion through the in-process AI client (the SAME gateway path the rest of the console uses) and records the run.
Tenant isolation is the FILE: each org's records live in its own SQLite at {DataDir}/orgs/{slug}/agents.db (HIP-0302), named from the gateway-minted X-Org-Id (HIP-0026) and nothing else. One tenant cannot read, run or delete another's agents because the query never reaches the database they are in. tenancy.go is the whole of that argument and is the only file that resolves a store; the org predicate every statement still carries is what makes a mis-resolved store fail closed rather than answer.
Surface (all org-scoped; console's AgentsModule reads {agents:[...]}):
GET /v1/agents list agents for the org -> {agents:[...]}
POST /v1/agents create an agent -> Agent
GET /v1/agents/:ref agent detail + recent runs -> AgentDetail
PATCH /v1/agents/:ref update an agent -> Agent
DELETE /v1/agents/:ref delete an agent (+ its runs)
POST /v1/agents/:ref/run run the agent {input} -> RunResult
GET /v1/agents/:ref/runs run history -> {runs:[...]}
:ref is either the agent's public id (the `agent_...` handle create and list return) OR its org-unique name — resolved by Store.Resolve, so a created agent is immediately gettable and runnable by whatever create/list handed back.
The stores are per-org SQLite under deps.DataDir (Base/SQLite-only), opened through cloud.OrgStore like every other per-org subsystem. They hold definitions and run I/O only — never a secret; tool credentials live in KMS by reference.
Index ¶
- Constants
- func BillingActor(org, sub string) string
- func CloseSession(ctx context.Context, org, sessionID, status string) error
- func CountActiveSessions(ctx context.Context, org string, m SessionMatch) (int, error)
- func LogSessionEvent(ctx context.Context, org, sessionID, kind, actor string, payload []byte) error
- func Mount(app cloud.Router, deps cloud.Deps) error
- func OfferRoutedRun(run RoutedRun) *offer
- func OpenSession(ctx context.Context, org, actor, agent, title string) (string, error)
- func OpenSessionOn(ctx context.Context, org, actor, agent, title, target string) (string, error)
- func SeedPersonalities(ctx context.Context, org string) (int, error)
- func Shutdown(ctx context.Context) error
- func StopSessions(ctx context.Context, org string, m SessionMatch) (int, error)
- func TargetDispatchable(ctx context.Context, org, targetID string) error
- type Agent
- type Event
- type GPU
- type Link
- type Metrics
- type RoutedResult
- type RoutedRun
- type Run
- type Session
- type SessionFilter
- type SessionMatch
- type Spec
- type Store
- func (s *Store) AppendEvent(ctx context.Context, e Event) (Event, error)
- func (s *Store) ClaimKeyHash(ctx context.Context, org, targetID string) (hash string, servingAt int64, err error)
- func (s *Store) Close() error
- func (s *Store) CountChildren(ctx context.Context, org, id string) (int, error)
- func (s *Store) CountEvents(ctx context.Context, org, sessionID string) (int, error)
- func (s *Store) CountLongRunning(ctx context.Context, org string) (int, error)
- func (s *Store) CountRuns(ctx context.Context, org, agent string) (int, error)
- func (s *Store) Create(ctx context.Context, a Agent) error
- func (s *Store) CreateSession(ctx context.Context, x Session) error
- func (s *Store) CreateTarget(ctx context.Context, t Target) error
- func (s *Store) Delete(ctx context.Context, org, name string) (bool, error)
- func (s *Store) DeleteTarget(ctx context.Context, org, id string) (bool, error)
- func (s *Store) EventCountsByRoot(ctx context.Context, org, root string) (map[string]int, error)
- func (s *Store) Get(ctx context.Context, org, name string) (Agent, error)
- func (s *Store) GetLinkableTargetByHost(ctx context.Context, org, host, owner string) (Target, error)
- func (s *Store) GetSession(ctx context.Context, org, id string) (Session, error)
- func (s *Store) GetTarget(ctx context.Context, org, id string) (Target, error)
- func (s *Store) GetTargetByHost(ctx context.Context, org, host string) (Target, error)
- func (s *Store) InsertRun(ctx context.Context, r Run) error
- func (s *Store) LastEvent(ctx context.Context, org, sessionID string) (Event, bool, error)
- func (s *Store) List(ctx context.Context, org string) ([]Agent, error)
- func (s *Store) ListControlAfter(ctx context.Context, org, sessionID string, since int64, limit int) ([]Event, error)
- func (s *Store) ListEvents(ctx context.Context, org, sessionID string, since int64, limit int) ([]Event, error)
- func (s *Store) ListLongRunning(ctx context.Context) ([]Agent, error)
- func (s *Store) ListPublishedBuilds(ctx context.Context, limit int) ([]Session, error)
- func (s *Store) ListRuns(ctx context.Context, org, agent string, limit int) ([]Run, error)
- func (s *Store) ListSessions(ctx context.Context, org string, f SessionFilter) ([]Session, error)
- func (s *Store) ListTargets(ctx context.Context, org string) ([]Target, error)
- func (s *Store) ListTree(ctx context.Context, org, root string, cap int) ([]Session, error)
- func (s *Store) Resolve(ctx context.Context, org, ref string) (Agent, error)
- func (s *Store) RunsSince(ctx context.Context, org string, since int64, limit int) ([]Run, error)
- func (s *Store) SessionLoad(ctx context.Context, org, id, host string) (TargetLoad, error)
- func (s *Store) StampServing(ctx context.Context, org, targetID string, now int64) error
- func (s *Store) TargetDispatchable(ctx context.Context, org, targetID string) error
- func (s *Store) Update(ctx context.Context, a Agent) error
- func (s *Store) UpdateSession(ctx context.Context, x Session) error
- func (s *Store) UpdateTarget(ctx context.Context, t Target) error
- func (s *Store) UpsertClaimKeyHash(ctx context.Context, org, targetID, hash string, now int64) error
- type Target
- type TargetLoad
- type TaskController
Constants ¶
const ( TrailerSession = "Hanzo-Session" TrailerTurn = "Hanzo-Turn" // NotesRef is where a link is recorded for a commit that already exists. // Rewriting history to add a trailer would change every downstream sha and // break every URL already pointing at it; a note attaches the same fact // without touching the commit. NotesRef = "refs/notes/hanzo-provenance" // ProvenanceLogFormat is the EXACT `git log --format=` a client uses to emit // what ParseLinks reads. Published as a constant so the producer (the CLI // that ingests a transcript) and the verifier (anyone auditing our claims) // run the same command, and so the parser can never quietly drift from the // format it parses. Records are NUL-separated; within a record the first line // is the sha and the rest is the message body plus any note. ProvenanceLogFormat = "%x00%H%n%B%n%N" )
Trailer keys. The SAME two keys are read from a commit message trailer and from a refs/notes/hanzo-provenance note body — a note is just trailer lines attached out-of-band — so one parser covers both and there is exactly one spelling of the binding anywhere in the system.
const ( KindMessage = "message" KindToolCall = "tool-call" KindSpawn = "spawn" KindLog = "log" KindStatus = "status" KindControl = "control" )
Event kinds — the closed vocabulary of a session's ordered log.
const ( CmdPause = "pause" CmdResume = "resume" CmdStop = "stop" CmdMessage = "message" )
Control commands — the closed vocabulary of remote steering.
const ( StatusRunning = "running" StatusPaused = "paused" StatusDone = "done" StatusError = "error" )
Session status values. running/paused are live; done/error are terminal.
const ( ModeOneShot = "one-shot" ModeLongRunning = "long-running" )
Execution modes. One-shot agents run only on an explicit POST; long-running agents are additionally invoked by the scheduler on their Schedule.
const ( TargetLaptop = "laptop" TargetCloud = "cloud" TargetGPU = "gpu" TargetCluster = "cluster" TargetMachine = "machine" )
Target kinds — the closed vocabulary of dispatch destinations.
const ( TargetOnline = "online" TargetOffline = "offline" TargetDraining = "draining" )
Target status — a registered target is online until marked otherwise.
const LiveWindow = 90 * time.Second
LiveWindow bounds heartbeat freshness. A target that has heartbeated before but not within this window is reported offline no matter what its row says. Matched to the agent beat (30s) with slack so one missed beat does not flap a machine.
Variables ¶
This section is empty.
Functions ¶
func BillingActor ¶
BillingActor is the exported form of the actor identity a session is recorded under. The login-manager adapter (the only external caller) uses it to scope a session stop/count to the REVOKING user's own actor, so a revoke can never reach a co-tenant's sessions. It mirrors what sessions.go stamps on Session.Actor, so a stop's actor predicate matches exactly the sessions that user created.
func CloseSession ¶
CloseSession moves an org's session to a terminal state (done|error), stamping ended_at, and publishes the update. The store's monotonic-terminal rule already forbids reopening a finished run; here we only ever set a terminal status, so a double-close is a harmless no-op on an already-terminal row.
func CountActiveSessions ¶
CountActiveSessions returns how many of org's sessions matching m are live (running|paused) — the device view's "active sessions". Org-scoped; 0 when not mounted or the match is empty.
func LogSessionEvent ¶
LogSessionEvent appends one ordered event (message|tool-call|spawn|log|status| control) to an org's session and fans it out live. The (org, id) pair is re-resolved so a caller can only write to a session THIS org owns; kind is validated against the closed vocabulary and payload is size-bounded + must be well-formed JSON (nil payload is allowed for a bare marker). actor falls back to the org.
func OfferRoutedRun ¶
func OfferRoutedRun(run RoutedRun) *offer
OfferRoutedRun is the exported seam the coding delivery activity uses to place a run into the live rendezvous. Kept here (agents owns targets + sessions) so the machine-facing HTTP surface and the durable activity share ONE mailbox.
func OpenSession ¶
OpenSession registers a LIVE root session for org attributed to actor (an "org/sub" identity or a bare label) with the given agent label + title, and returns its id. The session is born running (not terminal like openRunSession's completed one-shot) so a long job streams status/log/tool-call events into it until CloseSession moves it to a terminal state. Best-effort live fan-out (publish) rides the bus; the store row is the truth.
func OpenSessionOn ¶
OpenSessionOn is OpenSession with the run's dispatch TARGET recorded, so mission-control shows a routed run on the machine it was sent to (session.target == the target id) exactly as a locally-linked run shows its host. The target is re-resolved org-scoped and MUST belong to this org — a session can never claim to run on another tenant's machine (the same fail-closed rule sessionContext enforces on the HTTP register path). An empty target falls back to OpenSession.
func SeedPersonalities ¶
SeedPersonalities ensures the built-in crew exists for org. Idempotent: an already-present persona (UNIQUE org+name) is left untouched, so it is safe to call on every org first-touch (a new Team workspace, say). Returns the number newly created.
It needs a model to attach — the deployment's configured default. With no default model, seeding is a NO-OP (0, nil): an org gets its crew the moment the binary has a model to run them on, never a half-created persona that can't run. A subsystem that is not mounted also no-ops rather than erroring, so a caller on the login path can call it best-effort without ever blocking a human.
func Shutdown ¶
Shutdown stops the scheduler (draining in-flight runs, bounded by ctx) and closes the agents store. Idempotent — safe to call when nothing is mounted.
func StopSessions ¶
StopSessions closes every RUNNING|PAUSED session of org matching m — recording a control "stop" event on each and transitioning it to a terminal state — and returns how many it stopped. It is the action a login-out (link revoke) takes so the sessions that ran under a revoked account/device are torn down. Org AND Actor scope it: the caller passes their own actor (org/user), so a revoke can only ever stop the caller's OWN sessions — never a co-tenant's, never an org's every session — even though m's Host/Provider/Account come from an attacker-controllable link row. A match with no actor stops nothing (fail-closed). Not-mounted → (0, nil), so a revoke tolerates a deployment with no session plane.
Types ¶
type Agent ¶
type Agent struct {
ID string
Org string
Name string
Model string
Instructions string
Description string
Tools []string
Status string
ExecutionMode string
Schedule string
ComputeRef string
ServiceAccountID string
CreatedAt int64
UpdatedAt int64
}
Agent is the org-scoped definition of an autonomous worker: a model, a system prompt (instructions), and a set of tool names it may call. It lives in its org's own database (see Store), and never stores a secret — tool credentials live in KMS and are referenced by name at run time.
The bot-lifecycle fields promote an agent from a one-shot callable into a long-running bot (per hanzo-agent-bot-architecture: "Bot = Agent + compute + long-running"):
- ExecutionMode: "one-shot" (default; runs only when POSTed) or "long-running" (the scheduler invokes it on Schedule).
- Schedule: a 5-field cron expression; required when long-running, ignored otherwise. The scheduler evaluates it once a minute.
- ComputeRef: an optional visor machine id the bot is bound to. It is an opaque reference here; binding/lifecycle is owned elsewhere.
- ServiceAccountID: an optional IAM agent service-account (<org>-<agent>). When set it is the Actor recorded on scheduled-run billing so an autonomous run is attributable to a principal, not just the org.
func ListForOrg ¶
ListForOrg returns the org's agents from the in-process store — the ONE exported seam other in-process subsystems use to read the canonical agent registry WITHOUT an HTTP hop back through the gateway.
It is the decompleced replacement for the old bots-as-members path, which enumerated agents over HTTP (/v1/agents with a forwarded bearer) and broke when HANZO_API_KEY was rejected (bot_members=0). clients/team calls this directly to project each agent as a workspace Employee.
ISOLATION: org is the ONLY tenant key and is used VERBATIM (Store.List filters WHERE org=?), so a caller for org A can never enumerate org B's agents. The caller MUST pass an org it already validated (principal.Org / a verified token claim), never a raw client header. Fails closed (nil, error) when the agents subsystem is not mounted or the org is empty/oversized.
type Event ¶
type Event struct {
ID string
SessionID string
Org string
Seq int64
Kind string // message|tool-call|spawn|log|status|control
Actor string
Payload string // opaque JSON blob (validated well-formed, size-bounded)
CreatedAt int64
}
Event is one entry in a session's ordered log: a model message, a tool call, a subagent spawn, a free log line, a status change, or a control command the running surface consumes. Seq is monotonic PER SESSION so a subscriber can resume from its last-seen point; Org is denormalised so every read stays org-scoped without a join back to the session row.
type GPU ¶
type GPU struct {
Vendor string `json:"vendor,omitempty"` // nvidia | amd | apple | intel | ...
Model string `json:"model,omitempty"` // "GB10", "8060S", "RTX 4090"
Memory int64 `json:"memory,omitempty"` // VRAM bytes, 0 = unknown
}
GPU is one accelerator on a machine.
type Link ¶
type Link struct {
Commit string `json:"commit"`
Session string `json:"session"`
Turn int64 `json:"turn"`
}
Link is one commit⇄turn binding as GIT states it. It is derived, never stored: ParseLinks is a pure function of `git log` output.
func ParseLinks ¶
ParseLinks reads `git log --format=ProvenanceLogFormat` output and returns one Link per commit that carries BOTH keys. A commit with neither (ordinary work), with only one (a half-written trailer), or with an unparseable turn is skipped — a link asserts a specific turn produced a specific commit, and a partial record cannot assert that. Pure: no exec, no I/O, safe to call concurrently.
type Metrics ¶
type Metrics struct {
Load1 float64 `json:"load1,omitempty"`
Load5 float64 `json:"load5,omitempty"`
Load15 float64 `json:"load15,omitempty"`
MemUsed int64 `json:"memUsed,omitempty"` // bytes
MemFree int64 `json:"memFree,omitempty"` // bytes
GPUUtil float64 `json:"gpuUtil,omitempty"` // 0..1 aggregate utilization
At int64 `json:"at,omitempty"` // unix seconds, server-stamped
}
Metrics is a machine's live state from the last heartbeat.
type RoutedResult ¶
type RoutedResult struct {
OK bool `json:"ok"`
Changed bool `json:"changed"`
Branch string `json:"branch,omitempty"`
CommitSha string `json:"commitSha,omitempty"`
Diffstat string `json:"diffstat,omitempty"`
Error string `json:"error,omitempty"`
}
RoutedResult is a routed run's terminal outcome, reported by the machine and returned to the durable activity so the workflow completes.
type RoutedRun ¶
type RoutedRun struct {
Org string `json:"org"`
TargetID string `json:"targetId"`
SessionID string `json:"sessionId"` // the live session opened at dispatch; the machine streams into it
Repo string `json:"repo"`
Project string `json:"project,omitempty"`
Base string `json:"base,omitempty"`
Branch string `json:"branch"`
Prompt string `json:"prompt"`
CloneURL string `json:"cloneUrl"`
TimeoutSeconds int `json:"timeoutSeconds,omitempty"`
// Actor + AgentRef are CLOUD-SIDE attribution for the completion path (session
// close + PR assignee). They are NOT part of routedRunView, so they never cross
// to the executing machine — the machine needs neither.
Actor string `json:"actor,omitempty"`
AgentRef string `json:"agentRef,omitempty"`
}
RoutedRun is the NON-SECRET spec of one coding run dispatched to a target. It carries no credential by design: the executing machine authenticates git + model routing with its OWN already-held credentials (the same ones `hanzo code` uses), so no secret ever enters the durable store or crosses to the machine in the claim response. Everything here is safe to persist in the tasks engine.
type Run ¶
type Run struct {
ID string
Org string
AgentName string
Status string
Model string
Input string
Output string
Error string
DurationMs int64
CreatedAt int64
}
Run is one execution of an agent: the input, the produced output (or error), which model served it, and how long it took. Real history — every row is a call that actually happened.
func RunOnBehalf ¶
RunOnBehalf runs agent `ref` for `org` ON BEHALF OF `userSub`, IN-PROCESS — no gateway hop, no Cloudflare/IPv6 exposure. It is the clean in-process twin of the HTTP s.run handler: the CALLER (e.g. the Slack integrations bridge) has ALREADY authenticated org+userSub server-side, so this entry takes them DIRECTLY and never reads an HTTP principal / JWT / zip.Ctx. It resolves the agent org-scoped, runs it through the SAME runAgent → executeRun → meter path as s.run (one run path: one balance gate, one debit, one recorded run, one live session), and bills billingActor(org, userSub) against ORG's ledger.
ISOLATION: org is the ONLY tenant key. Store.Resolve is org-scoped, so a caller for org A can never resolve, run, or bill against org B's agent — exactly the property the HTTP handler relies on the gateway-minted X-Org-Id for.
A non-nil error means NO run happened: not mounted, invalid org, oversized input, inference not configured, agent-not-found (errNotFound), or a balance-gate denial (out-of-funds / commerce-unknown). A run that executed but whose model failed returns a recorded error-status Run and a nil error.
type Session ¶
type Session struct {
ID string
Org string
Agent string // agent name / type label (need not be a cloud Agent row)
Actor string // the principal that started it (validated user, or a bound SA)
Status string // running|paused|done|error
ParentID string // "" for a root (the outer agent)
RootID string // the tree key; == ID for a root
Title string
StartedAt int64
EndedAt int64 // 0 until a terminal status is reached
CreatedAt int64
UpdatedAt int64
// TaskWorkflowID / TaskRunID link this session to the hanzoai/tasks durable
// workflow that actually EXECUTES it. This registry is the view/control/stream
// layer; durable execution (retries, resumability, scheduling) is owned by
// hanzoai/tasks — NOT by a bespoke scheduler here. A root session maps to a
// tasks workflow (ExecuteWorkflow); a subagent maps to a child workflow keyed
// by the same RootID. When these are set, control (pause/resume/stop/message)
// forwards to the tasks Signal/Cancel API (see State.tasks). Empty = a surface
// that consumes control from the event stream instead (today's @hanzo/dev).
TaskWorkflowID string
TaskRunID string
// Execution context — WHERE this session runs. All optional (a surface that
// doesn't know sets ""), surfaced by mission-control so a card shows the
// machine/repo/cwd it runs on and the devices view maps "which sessions run
// where". Host is the machine label; Repo/Cwd are the code context; Target is a
// registered run-target id (the #48 dispatch association — resolved same-org at
// register/patch so it never points across tenants). Truth the SURFACE reports.
Host string
Cwd string
Repo string
Target string
// Terminal is where this session's live terminal can be WATCHED — the URL the
// machine published for it (zrok gives one without opening a port). Optional:
// a session that publishes nothing is still a session, it just cannot be
// watched. It is a URL rather than a stream because the bytes belong to the
// machine running the shell; cloud holds the address, never the connection.
Terminal string
// Provider/Account tag a session with the linked AI account it ran under (the
// login-manager tie-in): which provider (claude|codex|hanzo|…) and which
// subscription/api account served this run. Optional (a surface that doesn't
// know sets ""), surfaced so the cockpit shows which linked account served
// the run and so a login-out (link revoke) can stop the sessions that used it.
Provider string
Account string
// Project / Published are the READABLE BUILD (provenance.go): which product
// this session built, and its author's decision to let the world read the
// story. Two columns, because "the build of project P" is just the sessions
// tagged with P — a build log is not a second kind of thing to store.
// Published only ever widens READ access to a session that already exists;
// it grants nothing else, and an unpublished session is invisible to the
// public route no matter who asks.
Project string
Published bool
}
A live agent-session is a running invocation — a cloud agent run, a bot loop, or a @hanzo/dev CLI run spawning subagents. The SUBAGENT TREE is sessions linked by ParentID: the outer agent is the root (ParentID==""), each spawned subagent is a child, and RootID is the tree key every node in one flow shares. It is the first-class, streamable form of the blue/red/cto fan-out tree.
A session is NOT foreign-keyed to an agents row: an external surface (the @hanzo/dev CLI) registers a session whose Agent is just a label, not a cloud Agent definition. Tenancy is the org's own file, exactly like agents/runs.
type SessionFilter ¶
type SessionFilter struct {
Root string
Parent string
Status string
Project string
Published bool
Limit int
}
SessionFilter selects a slice of an org's sessions. The fields are AND-ed; a zero field is "any". Scope picks the structural axis:
- Root set -> every session in that tree (root_id == Root).
- Parent set -> the direct children of Parent (parent_id == Parent).
- neither -> roots only (parent_id == ”), the outer-agent view.
Project narrows to the sessions that built one product; it is orthogonal to the structural axis, so `?project=x` alone lists that build's ROOTS (the default parent_id==” scope) and pairs with Root to walk its subagents. Published additionally requires the author's publish flag — the predicate the PUBLIC build route runs, so an unpublished session cannot be reached anonymously.
type SessionMatch ¶
SessionMatch selects live (running|paused) sessions to stop or count. Actor (the owning subject, org/user) is MANDATORY and always ANDed, so a match can only ever affect the caller's OWN sessions — never a co-tenant's. Host/Provider/Account are optional narrowing WITHIN the actor's sessions (empty = any of the actor's). A match with no actor selects NOTHING (fail-closed), so a login-out can never sweep another user's — or an org's every — session, even when Host/Provider/Account are attacker-set at link upsert.
type Spec ¶
type Spec struct {
OS string `json:"os,omitempty"` // linux | darwin | windows
Arch string `json:"arch,omitempty"` // amd64 | arm64 | ...
CPUs int `json:"cpus,omitempty"` // logical cores
Memory int64 `json:"memory,omitempty"` // total RAM, bytes
GPUs []GPU `json:"gpus,omitempty"`
}
Spec is a machine's static capability.
func (Spec) Sanitize ¶
Sanitize bounds every field so a target row stays small and well-formed no matter what a client sends: strings trimmed + length-capped, counts/sizes non-negative and clamped, GPU list truncated, floats coerced finite. It is total (never errors) so the write path can always proceed with a safe value.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is ONE ORG's agents database — the file at {DataDir}/orgs/{slug}/agents.db that cloud.OrgStore opens and caches (HIP-0302 physical org isolation), holding that org's agents, runs, sessions, events, targets and claim keys.
Isolation is now the FILE. Every method still takes and still applies the org it is given, and that is deliberate: the predicate costs nothing next to the file it already runs in, and it is what makes a mis-resolved store fail closed (an empty read) instead of serving a neighbour's rows. The file is the boundary; the column is the proof that the boundary held.
func (*Store) AppendEvent ¶
AppendEvent inserts one event, allocating the next per-session Seq. The org's file runs on a single connection (cloud.OrgDB sets MaxOpenConns(1)) so the read-then-write of the max seq is serialised WITHIN the org, and the UNIQUE(session_id,seq) index is the final backstop. Appends in different orgs no longer queue behind each other, because they are different files. The session's updated_at is bumped in the SAME transaction so "last activity" stays truthful. Returns the persisted event (with Seq/CreatedAt) for streaming.
func (*Store) ClaimKeyHash ¶
func (s *Store) ClaimKeyHash(ctx context.Context, org, targetID string) (hash string, servingAt int64, err error)
ClaimKeyHash returns a target's stored hash + last serving stamp, or errNoClaimKey when none was ever minted.
func (*Store) CountChildren ¶
CountChildren returns how many DIRECT children a session has (its fan-out).
func (*Store) CountEvents ¶
CountEvents returns how many events a session has (the list rollup).
func (*Store) CountLongRunning ¶
CountLongRunning returns how many scheduled long-running agents an org has — used to cap an org's scheduler footprint at create time.
func (*Store) Create ¶
Create inserts one agent. A UNIQUE(org,name) violation surfaces as errConflict.
func (*Store) CreateSession ¶
CreateSession inserts one session. When ParentID is set it MUST reference an existing session IN THE SAME ORG — the caller resolves it via GetSession first so a cross-tenant or dangling parent can never link a tree. RootID is derived by the caller (parent's root, or self for a root); this method persists what it is given after a final same-org sanity check on the parent.
func (*Store) CreateTarget ¶
CreateTarget inserts one target. The id is caller-generated (genID("tgt")).
func (*Store) DeleteTarget ¶
DeleteTarget removes an org's target. Sessions keep their recorded target id (a historical fact); a detached target simply stops appearing in the registry.
func (*Store) EventCountsByRoot ¶
EventCountsByRoot returns per-session event counts for EVERY session in one org's tree (root_id == root) in a SINGLE grouped query — so materialising a tree of N nodes with real per-node event counts costs one round trip, not N, and never hits SQLite's bound-parameter limit (the join scopes by root_id, not an IN list of ids).
func (*Store) Get ¶
Get returns the agent for the exact (org,name) or errNotFound. It is the precise name primitive; path-addressed handlers use Resolve (id-or-name).
func (*Store) GetLinkableTargetByHost ¶
func (s *Store) GetLinkableTargetByHost(ctx context.Context, org, host, owner string) (Target, error)
GetLinkableTargetByHost returns the target for (org,host) that the caller `owner` may re-link — its OWN row, else an UNOWNED (pre-migration) row it may adopt — preferring the exact-owner match, newest first. A row owned by a DIFFERENT principal is NEVER returned, so a re-link can never clobber another member's machine: the caller gets its own row or (falling through in registerTarget) a fresh one. errTargetNotFound when nothing linkable exists.
func (*Store) GetSession ¶
GetSession returns the (org,id) session or errSessionNotFound. The org is part of the key so one tenant can never resolve another's session id.
func (*Store) GetTarget ¶
GetTarget returns the (org,id) target or errTargetNotFound. Org is part of the key so one tenant can never resolve another's target id.
func (*Store) GetTargetByHost ¶
GetTargetByHost returns an org's target reporting the given host, or errTargetNotFound. It is how a re-link of the SAME machine finds its existing target (idempotent register) instead of creating a duplicate. Org-scoped: a host string can never resolve another tenant's target. Newest wins if a host was ever double-listed.
func (*Store) LastEvent ¶
LastEvent returns a session's most recent event (highest seq) — the one-line "last activity" a mission-control card shows in the list without fetching full detail. ok=false when the session has no events yet. Org-scoped like every read.
func (*Store) ListControlAfter ¶
func (s *Store) ListControlAfter(ctx context.Context, org, sessionID string, since int64, limit int) ([]Event, error)
ListControlAfter returns a session's KindControl events with seq > since, oldest first — the durable steering queue a running surface drains. Mirrors ListEvents but filters to control so a chatty session's message/log/tool-call events never dilute a poll.
func (*Store) ListEvents ¶
func (s *Store) ListEvents(ctx context.Context, org, sessionID string, since int64, limit int) ([]Event, error)
ListEvents returns a session's events in Seq order (optionally only those with Seq > since, so a subscriber resumes exactly where it dropped), capped.
func (*Store) ListLongRunning ¶
ListLongRunning returns every agent across ALL orgs whose execution_mode is long-running and that carries a non-empty schedule — the scheduler's work set. It is the ONE cross-org query in this store; the scheduler is a trusted in-process subsystem (not a tenant request), and each returned agent carries its own Org so every downstream action (run, gate, meter) stays scoped to the agent's own tenant.
func (*Store) ListPublishedBuilds ¶
ListPublishedBuilds returns every published build ACROSS ORGS, newest first. It is the one query in this file that is deliberately not org-scoped, because it answers a public question — "which builds may anyone read?" — and its WHERE clause is the publish flag itself. A row can only appear here because its own author set published=1, so cross-org visibility is the author's grant, not a missing tenant predicate. Roots only: a build's story is its outer session.
func (*Store) ListSessions ¶
ListSessions returns an org's sessions per filter, newest first, capped.
func (*Store) ListTargets ¶
ListTargets returns an org's targets, newest first.
func (*Store) ListTree ¶
ListTree returns EVERY session in one org's tree (root_id == root), oldest first so a caller can assemble parent→child in a single pass. Capped so a pathological tree can't produce an unbounded response.
func (*Store) Resolve ¶
Resolve returns the agent identified by ref within org, matching either its public id (the `agent_...` handle create and list hand back) OR its org-unique name. This is the ONE lookup every path-addressed handler (get/update/delete/ run/runs) uses, so a just-created agent is immediately addressable by exactly the identifier create/list returned — no id-vs-name split. If a ref somehow equals one agent's id and another's name, the id match wins (the stable public handle is authoritative). Tenancy is the org filter, so a ref belonging to another tenant is errNotFound — fail-closed, never cross-org.
func (*Store) RunsSince ¶
RunsSince returns the org's runs across ALL agents with created_at >= since, newest first, capped. It powers the org-wide surfaces: the recent-activity feed (since=0 → the newest runs regardless of age) and the invocation histogram (since=windowStart → every run in the window, order-independent for bucketing). since<=0 means "no lower bound". Tenancy is the org column, so a caller never sees another org's runs. Every row is a real recorded execution.
func (*Store) SessionLoad ¶
SessionLoad returns how many of an org's sessions are mapped to a target: those explicitly dispatched to it (target == id) OR reporting its host (host == host, when the target has a host). One exact query (no double count) per target — the list is small so the per-row cost matches the sessions list's own rollups.
func (*Store) StampServing ¶
StampServing records that a target's runner polled at now (its liveness heartbeat). Best-effort by the caller; a missing row is a no-op.
func (*Store) TargetDispatchable ¶
TargetDispatchable is the DRY liveness gate, used at dispatch (fail closed before enqueue) AND re-checked at claim. A run is dispatchable only to a target that (a) exists in this org, (b) is online, and (c) has a live runner — a claim poll within servingTTL. Any failure is an explicit error the dispatcher renders honestly; it NEVER falls back to running elsewhere.
func (*Store) UpdateSession ¶
UpdateSession persists the mutable fields of an existing (org,id) session. Scoped by org so a cross-tenant id can never mutate another's session.
Every column the patch can set has to be listed here. A field added to patchSessionIn and forgotten in this statement accepts the request, answers 200 with the new value in the response body, and persists nothing — a success that did nothing, which is the hardest shape of bug to see.
func (*Store) UpdateTarget ¶
UpdateTarget persists mutable fields for an existing (org,id) target. Scoped by org so a cross-tenant id can never mutate another's target. owner is persisted too so a relink can BIND a previously-unowned row (registerTarget) and a patch preserves the owner it read; no client-facing patch field sets owner, so it never moves by mutation.
type Target ¶
type Target struct {
ID string
Org string
Owner string // the VALIDATED principal (c.User()) that registered this machine; "" for a pre-migration row
Label string
Kind string // laptop | cloud | gpu | cluster | machine
Status string // online | offline | draining
Capacity string // free-form ("8 vCPU / 32G", "1× GB10") — human summary
Host string // hostname sessions on this machine report (maps sessions -> target)
Spec Spec // static capability
Metrics Metrics
MetricsAt int64
CreatedAt int64
UpdatedAt int64
}
Target is a registered agent run-target. Owned by one org. Spec is its static capability (os/arch/cpus/memory/gpus) and Metrics its last live heartbeat (loadavg/memory/gpu-util); MetricsAt is the unix second that heartbeat was recorded (0 = never). See targetspec.go for the value plane.
func ResolveTarget ¶
ResolveTarget resolves a human's target REFERENCE — a target id or its friendly label (the hostname the CLI registers) — to the org's target, org-scoped and fail-closed. It is the ONE way a trigger surface (the Slack `code: <repo> on <target>` grammar, a console picker) turns "on evo" into a target id without leaking another tenant's inventory: an id or label that resolves to no target in THIS org returns errTargetNotFound, never another org's machine.
Precedence: an exact id match wins (ids are unambiguous), else an exact, case-folded label match (newest first, so a re-registered machine's live row is preferred). A reference that matches neither is not found — the caller renders an honest error and NEVER falls back to a local run.
func TargetsForOrg ¶
TargetsForOrg returns the org's registered run-targets from the in-process store, newest first. Fails closed when the subsystem is not mounted or the org is empty/oversized.
func (Target) EffectiveStatus ¶
EffectiveStatus is the ONE liveness answer every reader uses — the views here, the fleet board's agent fold, and the dispatch gate. The stored status records operator INTENT ("I am draining this box"); liveness is a FACT the heartbeat decides, and the fact wins. Without this a worker that died — or whose host was simply powered off — stays "online" forever, because nothing ever writes the row again to say otherwise. That is how the fleet board came to show two GPUs online that had last beaten five and nine days earlier.
A target that has never heartbeated (MetricsAt == 0) keeps its stored status: it is a hand-registered destination, not a beating agent, and has no fact to check.
type TargetLoad ¶
type TargetLoad struct {
Sessions int // total sessions mapped to the target
Running int // of those, how many are currently running
}
TargetLoad is the live session load on a target.
type TaskController ¶
type TaskController interface {
// Signal forwards a cooperative control signal (pause/resume/message) to the
// durable workflow backing a session. name is the signal name; payload is the
// opaque signal argument (e.g. a steer message), nil when there is none.
Signal(ctx context.Context, workflowID, runID, name string, payload []byte) error
// Cancel gracefully cancels the durable workflow backing a session (a stop).
Cancel(ctx context.Context, workflowID, runID, reason string) error
// Enabled reports whether a real tasks backend is wired. When false the
// control endpoints record the intent and skip the forward (honest degrade,
// same pattern as deps.AI).
Enabled() bool
}
TaskController is the seam to the hanzoai/tasks durable-execution engine — the ONE canonical engine for durable/retriable/scheduled agent work. This sessions surface is the REGISTRY + control + ZAP-stream VIEW layer; it deliberately owns NO scheduler, ticker, or lease. When a session is backed by a tasks workflow (Session.TaskWorkflowID set), a control command forwards through this seam to the engine's signal/cancel API instead of only being recorded.
The method set mirrors github.com/hanzoai/tasks/pkg/sdk/client.Client exactly, so the live adapter is a thin wrapper (Signal→Client.SignalWorkflow, Cancel→Client.CancelWorkflow) with no impedance mismatch:
SignalWorkflow(ctx, workflowID, runID, signalName string, arg any) error CancelWorkflow(ctx, workflowID, runID string) error
TASKS PLUG-IN POINT. The live controller is wired in Mount from a dialed tasks client (client.Dial(TASKS_URL)); until the hanzoai/tasks native engine lands (today its workflow opcodes return 501 by design — "the shape is in place so callers depend on the API while the engine lands behind it"), the default is the disabled controller: control is still durably RECORDED as a session event for stream-consuming surfaces, and the forward is a clean no-op.
Source Files
¶
- agents.go
- cron.go
- crossorg.go
- inproc.go
- legacy.go
- listfororg.go
- mailbox.go
- onbehalf.go
- personalities.go
- provenance.go
- routing.go
- routing_http.go
- scheduler.go
- sessions.go
- sessions_control_drain.go
- sessions_stop.go
- sessions_store.go
- sessions_stream.go
- sessions_tasks.go
- store.go
- targets.go
- targetspec.go
- tenancy.go
- toolprovider.go
- zipdoc_gen.go