sysagent

package
v0.7.0-rc.1 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: AGPL-3.0 Imports: 43 Imported by: 0

Documentation

Overview

Package sysagent is the in-airlock chat agent that lets operators manage agents, bridges, connections, members, runs, and other tenant resources through tool calls.

No JS VM, no per-agent connections, no Sol — sysagent runs inside airlock and its tools are typed Go functions wrapping existing service.{domain} methods. Every tool call uses the caller's authz.Principal; the service layer's authz.Authorize gates by action.

One sysagent per Airlock instance, per-user multi-conversation chat history in system_conversations, system_messages, and system_audit.

Index

Constants

View Source
const MaxToolOutputBytes = 8 * 1024

MaxToolOutputBytes caps every tool result (success and error) at 8 KiB. Tools that hand back larger payloads get truncated with a "[truncated: total=N bytes; refine query or paginate]" suffix, so one runaway list_runs call can't blow through the LLM's context budget. Sized to match the agentsdk JS-binding spill threshold so the LLM sees one consistent "8 KiB-ish" budget across surfaces.

Variables

This section is empty.

Functions

func SystemPrompt

func SystemPrompt(env promptEnv, availableTools tool.Set) string

SystemPrompt renders the sysagent chat system prompt: the conventions preamble, the per-turn <env> block, and a tool catalogue generated from the registered (tenant-filtered) tool set so it can't drift from the actual Execute surface. Only the first description line is shown — the full schema reaches the model via the tool envelope.

Types

type ConversationDetail

type ConversationDetail = systemchat.ConversationDetail

type Deps

type Deps struct {
	DB                 *db.DB
	Encryptor          secrets.Store
	PubSub             *realtime.PubSub
	PublicURL          string // base URL for deep-link tools (no trailing slash)
	ProviderHTTPClient *http.Client
	Logger             *zap.Logger

	Agents      *agentssvc.Service
	Bridges     *bridgessvc.Service
	Catalog     *catalogsvc.Service
	Conns       *connsvc.Service
	GitCreds    *gitcredssvc.Service
	ManagedBots *managedbotssvc.Service
	Members     *memberssvc.Service
	Runs        *runssvc.Service
	Users       *userssvc.Service
}

Deps bundles the dependencies New requires. Pulled out as a struct so the call site stays readable as the service set grows.

type ListRunsResult

type ListRunsResult = systemchat.ListRunsResult

type PromptInput

type PromptInput = systemchat.PromptInput

PromptInput is the per-call payload the HTTP handler hands to RunPrompt. Exactly one of Message / Approved should be set per the /system/conversations/{id}/prompt API:

  • Message non-empty + Approved nil = fresh operator turn.
  • Approved non-nil + Message empty = resolution of a pending confirmation (true=execute, false=synthesize denial).
  • Both empty = auto-resume after an injected system event (see Service.resumeConversation).

type RunSummary

type RunSummary = systemchat.RunSummary

type Service

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

Service is the central sysagent hub. Owns the dependency set every tool body needs (db, encryptor, all the per-domain service handles, logger) plus the public URL used to mint deep-link tool results.

One instance per Airlock; constructed in api/router.go and shared across HTTP requests. Per-request state (Principal, conversation, message history) lives on the chat loop, not here.

func New

func New(d Deps) *Service

New wires the sysagent Service. Fail-loud on nil deps — every field is required (AGENTS.md rule).

func (*Service) CancelRun

func (s *Service) CancelRun(ctx context.Context, p authz.Principal, runID uuid.UUID) (bool, error)

CancelRun aborts an in-flight chat goroutine. Returns true if a matching run was found and cancelled. The runChat goroutine sees ctx.Done() through sol.Runner and exits with status='cancelled' via the existing sol.RunCancelled branch.

func (*Service) Compact

func (s *Service) Compact(ctx context.Context, p authz.Principal, conversationID uuid.UUID) (string, error)

Compact runs sol's user-triggered compaction on a sysagent conversation in-process. Loads the conversation's post-checkpoint messages, asks the LLM to summarize, persists the summary as a new system_messages row, and advances context_checkpoint_message_id.

Unlike RunPrompt this is synchronous from the caller's perspective: returns once compaction completes with the summary text the user will see in chat. Mirrors the agent path's /compact (which forwards to the agent container and lets Sol.Runner.Compact emit the summary as a normal assistant message), just executed locally because sysagent has no agent container.

func (*Service) ControlConversation

func (s *Service) ControlConversation(ctx context.Context, p authz.Principal, conversationID uuid.UUID, command, args string) (bool, error)

func (*Service) CreateConversation

func (s *Service) CreateConversation(ctx context.Context, p authz.Principal, title string) (dbq.SystemConversation, error)

func (*Service) DeleteConversation

func (s *Service) DeleteConversation(ctx context.Context, p authz.Principal, id uuid.UUID) error

func (*Service) EnsureBridgeConversation

func (s *Service) EnsureBridgeConversation(ctx context.Context, p authz.Principal) (dbq.SystemConversation, error)

func (*Service) GetConversation

func (s *Service) GetConversation(ctx context.Context, p authz.Principal, id uuid.UUID) (ConversationDetail, error)

func (*Service) ListConversations

func (s *Service) ListConversations(ctx context.Context, p authz.Principal) ([]dbq.SystemConversation, error)

func (*Service) ListRuns

func (s *Service) ListRuns(ctx context.Context, p authz.Principal, cursor time.Time, limit int32) (ListRunsResult, error)

func (*Service) Logger

func (s *Service) Logger() *zap.Logger

Logger exposes the package logger for callers that want to surface sysagent events through the same channel (e.g. the API handler logging request failures).

func (*Service) NotifyBotCreated

func (s *Service) NotifyBotCreated(ctx context.Context, conversationID, originRunID uuid.UUID, botUsername string) error

NotifyBotCreated announces a freshly-created managed Telegram bot into the system-agent conversation that requested it (create_tg_bot) and auto-resumes, so the agent hands the operator the open link in-character. Same inject-then- resume mechanism as NotifyBuildComplete; agentID is nil (the event is about a bot, not an agent build — the WS envelope's AgentId is unused on bridges).

func (*Service) NotifyBuildComplete

func (s *Service) NotifyBuildComplete(ctx context.Context, agentID, conversationID, originRunID uuid.UUID, status, message string) error

NotifyBuildComplete satisfies builder.PostBuildSystemNotifier — the initial-build counterpart of NotifyUpgradeComplete. Triggered after a build kicked off from the system-agent create_agent tool finishes; same inject-then-resume mechanism, build-flavored prefix.

func (*Service) NotifyUpgradeComplete

func (s *Service) NotifyUpgradeComplete(ctx context.Context, agentID, conversationID, originRunID uuid.UUID, status, message string) error

NotifyUpgradeComplete satisfies builder.PostUpgradeSystemNotifier for the sysagent surface. Triggered by the builder after a build initiated from a sysagent-conversation upgrade tool finishes (success, failure, or out-of-scope refusal). Mirrors agent chat's conversationsHandler.NotifyUpgradeComplete (api/conversations.go) so the operator sees the outcome through the same render path the LLM-driving "user message" pattern uses.

What lands in the conversation: a single user-role system_messages row prefixed with [Upgrade succeeded] / [Upgrade failed] / [Request declined] — the prefix is what tells the LLM this is a system-injected event, not a real operator statement. Following agentsdk's deliberate choice not to write a separate assistant bubble: keeping the LLM's read of the situation unambiguous matters more than dual rendering.

agentID is the agent that was being upgraded; the message body references it so the LLM has context when responding. conversationID is the sysagent conversation that triggered the upgrade.

func (*Service) ResumePrincipal

func (s *Service) ResumePrincipal(ctx context.Context, conversationID, originRunID uuid.UUID) (authz.Principal, error)

func (*Service) RunPrompt

func (s *Service) RunPrompt(ctx context.Context, p authz.Principal, conversationID uuid.UUID, input PromptInput) (uuid.UUID, error)

RunPrompt creates the run row, hands off the chat to a background goroutine, and returns the new run id immediately. The HTTP handler returns {runId, conversationId} to the caller and the frontend subscribes to the conversation topic via WS to receive the streamed events (same pattern agent chat uses).

The synchronous part is intentionally small (create row, return id). Everything else — model resolve, message build, sol.Runner.Run, suspension handling, persistence — runs in the goroutine so a slow LLM doesn't tie up the HTTP request.

func (*Service) RunPromptInline

func (s *Service) RunPromptInline(ctx context.Context, p authz.Principal, conversationID uuid.UUID, text, platform string, approved *bool, resumeRunID string, extraSink eventstream.Sink, onStart func(runID uuid.UUID)) (uuid.UUID, error)

RunPromptInline runs the chat loop synchronously: it runs on the caller's goroutine, and an additional eventstream.Sink is fanned every bus event alongside the WS pubsubSink. Used by the bridge path so a system-bridge poller can block on a turn (its poller is single-threaded per bridge → natural per-thread serialization) and translate sysagent events into bridge ResponseEvents.

Takes a full PromptInput so the bridge path can resolve pending confirmations (Approve/Reject button taps) the same way the web UI does — set Approved + ResumeRunID. A plain user message leaves both nil/empty. When extraSink is non-nil the WS pubsub is bypassed entirely; only the bridge sees events. Returns once the chat loop exits — RunCompleted / RunFailed / RunSuspended / RunCancelled all reach this.

func (*Service) SetBridgeResumer

func (s *Service) SetBridgeResumer(r bridgeResumer)

SetBridgeResumer wires the bridge resume path. Called once at startup; nil leaves bridge follow-up delivery disabled (web notifications still work).

Directories

Path Synopsis
Package agentview projects the public API protos down to a compact, LLM-friendly shape for the system agent's tool outputs.
Package agentview projects the public API protos down to a compact, LLM-friendly shape for the system agent's tool outputs.

Jump to

Keyboard shortcuts

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