protocol

package
v0.0.20 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package protocol defines the WebSocket message protocol between Runner and Flashduty.

Index

Constants

View Source
const (
	UpgradePhaseDownloading = "downloading"
	UpgradePhaseVerifying   = "verifying"
	UpgradePhaseSwapping    = "swapping"
	UpgradePhaseFailed      = "failed"
)

Upgrade phase labels for the runner's own local logging (journald). They are not reported over the wire — the backend infers progress from the version the runner reports on its next heartbeat.

Variables

This section is empty.

Functions

This section is empty.

Types

type AuthErrorMeta added in v0.0.12

type AuthErrorMeta struct {
	StatusCode          int    `json:"status_code,omitempty"`
	WWWAuthenticate     string `json:"www_authenticate,omitempty"`
	ResourceMetadataURL string `json:"resource_metadata_url,omitempty"`
}

AuthErrorMeta carries the 401 challenge details so fc-safari can initiate the per-user OAuth flow without re-parsing opaque error strings.

type BashArgs

type BashArgs struct {
	Command string            `json:"command"`
	Workdir string            `json:"workdir,omitempty"`
	Timeout int               `json:"timeout,omitempty"` // seconds
	Env     map[string]string `json:"env,omitempty"`

	// Credential carries per-invocation egress auth out-of-band of the bash
	// environment. Safari sends it only to broker-capable (Linux) runners; the
	// runner keeps it in memory and injects it via the broker, never into
	// cmd.Env. When set, FLASHDUTY_APP_KEY is absent from Env (see the design
	// doc's frozen wire contract).
	Credential *BashCredential `json:"credential,omitempty"`
}

BashArgs are the arguments for bash operation.

Env carries per-call environment variables the caller wants merged on top of the runner process's own environment. Values may be secrets (auth tokens, per-tenant API keys), so the runner MUST NOT log them — only the keys are safe to surface. The bash command itself should reference these as `$VAR_NAME` so the secret expands inside the shell and never appears in transport-layer logs that mirror Command.

type BashCredential added in v0.0.16

type BashCredential struct {
	Key     string `json:"key"`
	BaseURL string `json:"base_url"`
}

BashCredential carries per-invocation egress auth out-of-band of the bash environment. Sent by Safari only to broker-capable runners; the runner keeps it in memory and injects it via the broker, never into cmd.Env.

type BashResult

type BashResult struct {
	Stdout   string `json:"stdout"`
	Stderr   string `json:"stderr"`
	ExitCode int    `json:"exit_code"`

	// Per-stream truncation metadata. A stream is "truncated" when either
	// processLargeOutput trimmed it OR the underlying LimitedWriter hit its
	// hard cap mid-execution (the captured buffer carries an inline marker).
	TruncatedStdout bool   `json:"truncated_stdout,omitempty"`
	TruncatedStderr bool   `json:"truncated_stderr,omitempty"`
	StdoutFilePath  string `json:"stdout_file_path,omitempty"`
	StderrFilePath  string `json:"stderr_file_path,omitempty"`
	StdoutTotalSize int64  `json:"stdout_total_size,omitempty"`
	StderrTotalSize int64  `json:"stderr_total_size,omitempty"`

	// Legacy stdout-only mirrors. Kept so older safari clients that only
	// parse these still see truncation. New code should read the stream-
	// specific fields instead.
	Truncated bool   `json:"truncated,omitempty"`
	FilePath  string `json:"file_path,omitempty"`
	TotalSize int64  `json:"total_size,omitempty"`
}

BashResult is the result of a bash operation.

Stdout and stderr are tracked independently — either stream may be truncated and spilled to a separate file. The legacy fields (`Truncated`, `FilePath`, `TotalSize`) mirror the stdout-side fields for backward compatibility with older safari builds; new callers should prefer the stream-specific fields.

type DeleteKnowledgeFilesArgs added in v0.0.6

type DeleteKnowledgeFilesArgs struct {
	RelPaths []string `json:"rel_paths"`
}

DeleteKnowledgeFilesArgs are the arguments for delete_knowledge_files operation.

type DeleteKnowledgeFilesResult added in v0.0.6

type DeleteKnowledgeFilesResult struct {
	Success bool `json:"success"`
}

DeleteKnowledgeFilesResult is the result of a delete_knowledge_files operation.

type EnvironmentInfo

type EnvironmentInfo struct {
	OS            string `json:"os"`                        // e.g., "darwin", "linux", "windows"
	OSVersion     string `json:"os_version"`                // e.g., "24.6.0" for macOS
	Arch          string `json:"arch"`                      // e.g., "amd64", "arm64"
	Hostname      string `json:"hostname"`                  // Machine hostname
	Shell         string `json:"shell"`                     // Default shell, e.g., "/bin/zsh"
	HomeDir       string `json:"home_dir"`                  // User home directory
	WorkspaceRoot string `json:"workspace_root"`            // Configured workspace root
	Username      string `json:"username"`                  // Current user
	NumCPU        int    `json:"num_cpu"`                   // Number of CPUs
	TotalMemoryMB int64  `json:"total_memory_mb,omitempty"` // Total memory in MB
	Timezone      string `json:"timezone"`                  // Timezone name, e.g., "Asia/Shanghai"
	UTCOffset     string `json:"utc_offset"`                // UTC offset, e.g., "+08:00"
}

EnvironmentInfo contains detailed environment information for LLM context. This is similar to Cursor's system reminder format.

type GlobArgs

type GlobArgs struct {
	Pattern string `json:"pattern"`
}

GlobArgs are the arguments for glob operation.

type GlobResult

type GlobResult struct {
	Matches []string `json:"matches"`
}

GlobResult is the result of a glob operation.

type GrepArgs

type GrepArgs struct {
	Pattern string   `json:"pattern"`
	Include []string `json:"include,omitempty"`

	// OutputMode controls the shape of the result: "content" (default — file:line:text rows),
	// "files_with_matches" (one path per match), or "count" (one path:count per file).
	OutputMode string `json:"output_mode,omitempty"`
	// ContextBefore (-B) and ContextAfter (-A) request N lines of context around each match.
	// Honored by ripgrep only when OutputMode is "content".
	ContextBefore int `json:"context_before,omitempty"`
	ContextAfter  int `json:"context_after,omitempty"`
	// HeadLimit caps the number of result rows returned (post-format). 0 = unlimited.
	HeadLimit int `json:"head_limit,omitempty"`
	// FileType maps to ripgrep's --type (e.g., "go", "py", "js"). Ignored by the Go fallback.
	FileType string `json:"file_type,omitempty"`
	// CaseSensitive overrides ripgrep's default --smart-case.
	// nil = smart-case; true = case-sensitive; false = case-insensitive.
	CaseSensitive *bool `json:"case_sensitive,omitempty"`
}

GrepArgs are the arguments for grep operation.

type GrepMatch

type GrepMatch struct {
	Path       string `json:"path"`
	LineNumber int    `json:"line_number"`
	Content    string `json:"content"`
}

GrepMatch is a single match in a grep result.

type GrepResult

type GrepResult struct {
	Matches   []GrepMatch `json:"matches"`
	Content   string      `json:"content"`              // Formatted content (may be truncated)
	Truncated bool        `json:"truncated,omitempty"`  // Whether content was truncated
	FilePath  string      `json:"file_path,omitempty"`  // Path to full content if truncated
	TotalSize int64       `json:"total_size,omitempty"` // Original content size
	// RegexCompileError surfaces a non-fatal fallback path: the requested pattern failed
	// to compile as a Go regexp, so the Go fallback walked files matching it as a literal.
	// Empty when ripgrep handled the request or compilation succeeded.
	RegexCompileError string `json:"regex_compile_error,omitempty"`
}

GrepResult is the result of a grep operation.

type HeartbeatMetrics

type HeartbeatMetrics struct {
	CPUPercent    float64 `json:"cpu_percent,omitempty"`
	MemoryPercent float64 `json:"memory_percent,omitempty"`
	DiskPercent   float64 `json:"disk_percent,omitempty"`
}

HeartbeatMetrics contains system metrics.

type HeartbeatPayload

type HeartbeatPayload struct {
	EnvironmentID string            `json:"environment_id"`
	Name          string            `json:"name"`
	Labels        []string          `json:"labels"`
	Version       string            `json:"version"`
	Environment   *EnvironmentInfo  `json:"environment,omitempty"`
	Metrics       *HeartbeatMetrics `json:"metrics,omitempty"`
}

HeartbeatPayload is the payload for heartbeat messages.

type KnowledgeFile added in v0.0.6

type KnowledgeFile struct {
	RelPath    string `json:"rel_path"`
	Checksum   string `json:"checksum"`
	ContentB64 string `json:"content_b64"`
}

KnowledgeFile is a single file entry in a stage_knowledge_files request.

type KnowledgeFileStatus added in v0.0.6

type KnowledgeFileStatus struct {
	RelPath string `json:"rel_path"`
	Success bool   `json:"success"`
	Error   string `json:"error,omitempty"`
}

KnowledgeFileStatus is the per-file result entry in the stage ack.

type KnowledgeManifestEntry added in v0.0.7

type KnowledgeManifestEntry struct {
	RelPath  string `json:"rel_path"`
	Checksum string `json:"checksum"`
}

KnowledgeManifestEntry is one (rel_path, checksum) pair in a reconcile manifest. rel_path uses the same `knowledge/<scope>/<leaf>` shape as StageKnowledgeFiles so the runner-side validator is shared.

type ListArgs

type ListArgs struct {
	Path      string   `json:"path"`
	Recursive bool     `json:"recursive,omitempty"`
	Ignore    []string `json:"ignore,omitempty"`
}

ListArgs are the arguments for list operation.

type ListEntry

type ListEntry struct {
	Path  string `json:"path"`
	IsDir bool   `json:"is_dir"`
	Size  int64  `json:"size"`
}

ListEntry is a single entry in a list result.

type ListResult

type ListResult struct {
	Entries []ListEntry `json:"entries"`
}

ListResult is the result of a list operation.

type MCPCallArgs

type MCPCallArgs struct {
	Server   MCPServerConfig `json:"server"`
	ToolName string          `json:"tool_name"`
	Args     json.RawMessage `json:"args"`
	Timeout  int             `json:"timeout,omitempty"` // seconds
}

MCPCallArgs are the arguments for mcp_call operation.

type MCPCallPayload

type MCPCallPayload struct {
	CallID    string          `json:"call_id"`
	Server    MCPServerConfig `json:"server"`
	ToolName  string          `json:"tool_name"`
	Arguments json.RawMessage `json:"arguments"`
}

MCPCallPayload is the payload for MCP tool calls. Server configuration is passed from cloud (stored in database).

type MCPCallResult

type MCPCallResult struct {
	Content   string `json:"content"`
	IsError   bool   `json:"is_error,omitempty"`   // Whether the tool returned an error
	Truncated bool   `json:"truncated,omitempty"`  // Whether content was truncated
	FilePath  string `json:"file_path,omitempty"`  // Path to full content if truncated
	TotalSize int64  `json:"total_size,omitempty"` // Original content size
}

MCPCallResult is the result of an mcp_call operation.

type MCPListToolsArgs

type MCPListToolsArgs struct {
	Server MCPServerConfig `json:"server"`
}

MCPListToolsArgs are the arguments for mcp_list_tools operation.

type MCPListToolsResult

type MCPListToolsResult struct {
	Tools []MCPToolInfo `json:"tools"`
}

MCPListToolsResult is the result of an mcp_list_tools operation.

type MCPResultPayload

type MCPResultPayload struct {
	CallID  string          `json:"call_id"`
	Success bool            `json:"success"`
	Result  json.RawMessage `json:"result,omitempty"`
	Error   string          `json:"error,omitempty"`

	// P1: structured auth-error metadata (additive; old senders omit).
	ErrorType    string         `json:"error_type,omitempty"`    // "auth_error" | ""
	AuthMetadata *AuthErrorMeta `json:"auth_metadata,omitempty"` // non-nil iff ErrorType=="auth_error"
}

MCPResultPayload is the payload for MCP call results.

type MCPServerConfig

type MCPServerConfig struct {
	Name           string            `json:"name"`
	Transport      string            `json:"transport"` // stdio, streamable-http, sse (legacy alias for streamable-http)
	Command        string            `json:"command,omitempty"`
	Args           []string          `json:"args,omitempty"`
	Env            map[string]string `json:"env,omitempty"`
	URL            string            `json:"url,omitempty"`
	Headers        map[string]string `json:"headers,omitempty"`
	DynamicHeaders map[string]string `json:"dynamic_headers,omitempty"`
}

MCPServerConfig is the MCP server configuration passed from cloud.

type MCPToolInfo

type MCPToolInfo struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	InputSchema any    `json:"input_schema,omitempty"`
}

MCPToolInfo represents metadata for an MCP tool.

type Message

type Message struct {
	ID        string          `json:"id"`
	Type      MessageType     `json:"type"`
	Payload   json.RawMessage `json:"payload"`
	Timestamp int64           `json:"timestamp"`
}

Message is the base WebSocket message structure.

func NewMessage

func NewMessage(msgType MessageType, payload any) (*Message, error)

NewMessage creates a new message with the given type and payload.

type MessageType

type MessageType string

MessageType defines the type of WebSocket message.

const (
	// Flashduty -> Runner (initial)
	MessageTypeWelcome MessageType = "welcome"

	// Runner -> Flashduty
	MessageTypeHeartbeat  MessageType = "heartbeat"
	MessageTypeTaskOutput MessageType = "task.output"
	MessageTypeTaskResult MessageType = "task.result"
	MessageTypeMCPResult  MessageType = "mcp.result"

	// Flashduty -> Runner
	MessageTypeTaskRequest MessageType = "task.request"
	MessageTypeTaskCancel  MessageType = "task.cancel"
	MessageTypeMCPCall     MessageType = "mcp.call"
	// Shutdown is Safari telling the runner to drop everything and exit —
	// used when CloudManager tears down a cloud sandbox so the runner's
	// WebSocket closes immediately instead of waiting for the 5s force
	// unregister timeout.
	MessageTypeShutdown MessageType = "shutdown"

	// Flashduty -> Runner. Advertises the target version in response to a
	// heartbeat; the runner self-decides whether to apply (it reports only its
	// own version back — there is no upgrade-status channel).
	MessageTypeUpgrade MessageType = "upgrade"
)

type PackFolderArgs added in v0.0.8

type PackFolderArgs struct {
	Path      string `json:"path"`       // absolute path inside workspace
	SkillRoot bool   `json:"skill_root"` // when true, also exclude evals/ at the root
	MaxBytes  int64  `json:"max_bytes"`  // 0 = no cap; >0 = error if zip exceeds
}

PackFolderArgs walks a directory inside the workspace and returns a zip archive of its contents. Used by Safari's sync_files(type="skill") path so the agent never has to package skills by hand.

type PackFolderResult added in v0.0.8

type PackFolderResult struct {
	ZipB64    string `json:"zip_b64"` // base64-encoded zip bytes
	SizeBytes int64  `json:"size_bytes"`
	FileCount int    `json:"file_count"`
}

PackFolderResult carries the produced archive back to Safari.

type ReadArgs

type ReadArgs struct {
	Path   string `json:"path"`
	Offset int64  `json:"offset,omitempty"`
	Limit  int64  `json:"limit,omitempty"`
}

ReadArgs are the arguments for read operation.

type ReadResult

type ReadResult struct {
	Content   string `json:"content"` // base64 encoded
	TotalSize int64  `json:"total_size"`
}

ReadResult is the result of a read operation.

type ReconcileKnowledgeManifestArgs added in v0.0.7

type ReconcileKnowledgeManifestArgs struct {
	Files []KnowledgeManifestEntry `json:"files"`
	// StagedScopes are the scopes this manifest is authoritative for: files
	// inside them that the manifest doesn't list are orphans and are pruned.
	StagedScopes []string `json:"staged_scopes,omitempty"`
	// ValidScopes are ALL scopes that currently exist account-wide (a
	// superset of StagedScopes). On-disk scopes outside it are deleted-pack
	// residue and are pruned whole; scopes inside ValidScopes but outside
	// StagedScopes belong to other sessions and are left untouched.
	//
	// Empty ValidScopes means either an older Safari (no scope info) or an
	// account with zero packs; both fall back to the legacy global orphan
	// prune, which is correct in both cases.
	ValidScopes []string `json:"valid_scopes,omitempty"`
}

ReconcileKnowledgeManifestArgs supplies the canonical view of which knowledge files should exist on disk and at what checksum. The runner deletes anything not in the manifest (orphan prune) and anything whose on-disk checksum disagrees (stale prune); the result's NeedsStage field tells the caller which manifest entries the runner does NOT have cached so the caller can bulk-stage them in one follow-up batch (skills-style eager materialization).

The two scope lists bound the orphan prune on runners shared by concurrent sessions. A manifest covers only the scopes its session staged (above Safari's full-staging cap that is a per-session subset), so without them a global prune would evict another session's validly mounted scopes — the two sessions would thrash each other's files. Scope segments use the on-disk directory names: "account" or "team_<id>".

type ReconcileKnowledgeManifestResult added in v0.0.7

type ReconcileKnowledgeManifestResult struct {
	Pruned     []string `json:"pruned,omitempty"`
	KeptCount  int      `json:"kept_count"`
	StaleCount int      `json:"stale_count"`
	NeedsStage []string `json:"needs_stage,omitempty"`
}

ReconcileKnowledgeManifestResult reports the diff outcome plus the list of entries the caller still needs to push.

Pruned lists every rel_path the runner removed (orphans + stale together). StaleCount is the subset of Pruned whose rel_path was in the manifest but had a divergent checksum — useful for spotting upstream edits. KeptCount counts files left untouched because their checksum already matched. NeedsStage enumerates every manifest entry the runner does NOT have on disk after the prune step. The caller (Safari) materializes these by fetching from S3 and calling StageKnowledgeFiles. Empty when the runner is already in sync with the manifest.

type StageKnowledgeFilesArgs added in v0.0.6

type StageKnowledgeFilesArgs struct {
	Files []KnowledgeFile `json:"files"`
}

StageKnowledgeFilesArgs are the arguments for stage_knowledge_files operation.

type StageKnowledgeFilesResult added in v0.0.6

type StageKnowledgeFilesResult struct {
	Files []KnowledgeFileStatus `json:"files"`
}

StageKnowledgeFilesResult is the result of a stage_knowledge_files operation.

type SyncSkillArgs

type SyncSkillArgs struct {
	SkillName string `json:"skill_name"`
	SkillDir  string `json:"skill_dir"`
	ZipData   string `json:"zip_data"` // base64 encoded zip
	Checksum  string `json:"checksum"`
}

SyncSkillArgs are the arguments for sync_skill operation.

type SyncSkillResult

type SyncSkillResult struct {
	Success bool   `json:"success"`
	Path    string `json:"path"`             // Local path where skill is materialized
	Cached  bool   `json:"cached,omitempty"` // True iff cache hit
}

SyncSkillResult is the result of a sync_skill operation.

Probe semantics: when SyncSkillArgs.ZipData is empty, the runner checks its local cache and returns either {Success:true, Cached:true, Path} on hit or {Success:true, Cached:false, Path:""} on miss (cloud must retry with zip). When ZipData is non-empty, the runner installs unconditionally and returns {Success:true, Cached:false, Path}.

type TaskCancelPayload

type TaskCancelPayload struct {
	TaskID string `json:"task_id"`
}

TaskCancelPayload is the payload for task cancellation.

type TaskOperation

type TaskOperation string

TaskOperation defines the type of workspace operation.

const (
	TaskOpRead                       TaskOperation = "read"
	TaskOpWrite                      TaskOperation = "write"
	TaskOpList                       TaskOperation = "list"
	TaskOpGlob                       TaskOperation = "glob"
	TaskOpGrep                       TaskOperation = "grep"
	TaskOpBash                       TaskOperation = "bash"
	TaskOpWebFetch                   TaskOperation = "webfetch"
	TaskOpMCPCall                    TaskOperation = "mcp_call"
	TaskOpMCPListTools               TaskOperation = "mcp_list_tools"
	TaskOpSyncSkill                  TaskOperation = "sync_skill"
	TaskOpPackFolder                 TaskOperation = "pack_folder"
	TaskOpStageKnowledgeFiles        TaskOperation = "stage_knowledge_files"
	TaskOpDeleteKnowledgeFiles       TaskOperation = "delete_knowledge_files"
	TaskOpReconcileKnowledgeManifest TaskOperation = "reconcile_knowledge_manifest"
)

type TaskOutputPayload

type TaskOutputPayload struct {
	TaskID string `json:"task_id"`
	Stream string `json:"stream"` // stdout, stderr
	Data   string `json:"data"`
}

TaskOutputPayload is the payload for streaming task output.

type TaskRequestPayload

type TaskRequestPayload struct {
	TaskID           string          `json:"task_id"`
	TraceID          string          `json:"trace_id,omitempty"`
	SourceInstanceID string          `json:"source_instance_id,omitempty"` // Safari instance ID
	Operation        TaskOperation   `json:"operation"`
	Args             json.RawMessage `json:"args"`
}

TaskRequestPayload is the payload for task request messages.

type TaskResultPayload

type TaskResultPayload struct {
	TaskID           string          `json:"task_id"`
	SourceInstanceID string          `json:"source_instance_id,omitempty"` // Safari instance ID
	Success          bool            `json:"success"`
	Result           json.RawMessage `json:"result,omitempty"`
	Error            string          `json:"error,omitempty"`
	ExitCode         int             `json:"exit_code,omitempty"`
}

TaskResultPayload is the payload for task completion.

type UpgradePayload added in v0.0.14

type UpgradePayload struct {
	TargetVersion string `json:"target_version"`
	DownloadURL   string `json:"download_url"`
	SHA256        string `json:"sha256"`
}

UpgradePayload is sent by Flashduty to advertise the target version (with its download URL + checksum) in response to a heartbeat. The runner self-decides whether to apply it.

type WebFetchArgs

type WebFetchArgs struct {
	URL     string `json:"url"`
	Format  string `json:"format,omitempty"`  // markdown, text, html (default: markdown)
	Timeout int    `json:"timeout,omitempty"` // seconds (default: 30, max: 120)
}

WebFetchArgs are the arguments for webfetch operation.

type WebFetchResult

type WebFetchResult struct {
	Content   string `json:"content"`
	URL       string `json:"url"`                  // Final URL after redirects
	Truncated bool   `json:"truncated,omitempty"`  // Whether content was truncated
	FilePath  string `json:"file_path,omitempty"`  // Path to full content if truncated
	TotalSize int64  `json:"total_size,omitempty"` // Original content size
}

WebFetchResult is the result of a webfetch operation.

type WelcomePayload

type WelcomePayload struct {
	EnvironmentID string   `json:"environment_id"`
	Name          string   `json:"name"`
	Labels        []string `json:"labels"`
}

WelcomePayload is the payload for the welcome message sent by server after connection. Contains environment info (name, labels) managed via Web UI.

type WriteArgs

type WriteArgs struct {
	Path    string `json:"path"`
	Content string `json:"content"` // base64 encoded
}

WriteArgs are the arguments for write operation.

Jump to

Keyboard shortcuts

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