crewrun

package
v0.1.7 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: MIT Imports: 42 Imported by: 0

Documentation

Overview

Package crewrun runs workflow graphs on the operator's host. It reads crew definitions from Arkism, keeps run progress in the Arkism runs API and never stores keys or tokens there: provider keys, hook secrets and notify destinations stay in 0600 files under ~/.config/arkism. Standard library only; no import of internal/memory.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EnqueueReceipt added in v0.1.1

func EnqueueReceipt(dir string, r TraceReceipt) error

EnqueueReceipt is the outcome spool sink (enqueue_receipt): {scope, trace} only, written once under its content hash with fsync, so a replay never duplicates and a crash never tears a file.

func EvalCondition

func EvalCondition(src string, in any) (bool, error)

EvalCondition parses and evaluates src against the decoded input.

func HookToken

func HookToken(path, workflowID string, create bool) (string, error)

HookToken returns the webhook secret of a workflow from the hooks file (~/.config/arkism/crew-hooks.json, 0600: {"hooks": {"WORKFLOW_ID": "TOKEN"}}). With create, a missing secret is generated and saved.

func ParseExpr

func ParseExpr(src string) (expr, error)

ParseExpr builds the expression tree; it never runs anything.

func Serve

func Serve(ctx context.Context, cfg Config) error

Serve is the daemon: crew reload every minute, schedules every 15 s, waiting runs every 10 s, and the webhook/callback listener.

Types

type APIError

type APIError struct {
	Status int
	Code   string
}

APIError is a non-200 answer; Code is Arkism's error code.

func (*APIError) Error

func (e *APIError) Error() string

type AgentBudget

type AgentBudget struct {
	MonthlyUSD float64 `json:"monthly_usd"`
	AskOverUSD float64 `json:"ask_over_usd,omitempty"`
}

type CaptureClaim

type CaptureClaim struct {
	Subject string `json:"subject"`
	Text    string `json:"text"`
	Kind    string `json:"kind"`
	Basis   string `json:"basis"`
	Reason  string `json:"reason"`
}

CaptureClaim is one memory candidate a conclusion proposes.

type Client

type Client struct {
	BaseURL   string
	TokenFile string
	Scope     Scope
	HTTP      *http.Client
}

Client talks to Arkism with a bearer token read from a file on each call, so a rotated token needs no restart. The token is never logged.

func (*Client) AgentSpend

func (c *Client) AgentSpend(ctx context.Context) (map[string]float64, error)

func (*Client) CancelFlowChildren added in v0.1.6

func (c *Client) CancelFlowChildren(ctx context.Context, parent Run) error

func (*Client) Capture

func (c *Client) Capture(ctx context.Context, eventID string, claims []CaptureClaim, model string) ([]string, error)

func (*Client) ClaimFlowEffect added in v0.1.6

func (c *Client) ClaimFlowEffect(ctx context.Context, run Run, ref FlowEffectView, preview string) (FlowEffectView, error)

func (*Client) ContextText

func (c *Client) ContextText(ctx context.Context, collection, id string) (string, error)

func (*Client) CreateFlowChild added in v0.1.6

func (c *Client) CreateFlowChild(ctx context.Context, parent Run, in FlowChildRequest) (FlowChildCreated, error)

func (*Client) CreateMeeting added in v0.1.4

func (c *Client) CreateMeeting(ctx context.Context, teamID, title, topic string) (MeetingRecord, error)

CreateMeeting asks Arkism for a meeting started by a workflow; the engine drives it.

func (*Client) CreateRun

func (c *Client) CreateRun(ctx context.Context, st RunState) (Run, error)

func (*Client) GetMeeting

func (c *Client) GetMeeting(ctx context.Context, id string) (MeetingRecord, error)

func (*Client) GetRun

func (c *Client) GetRun(ctx context.Context, id string) (Run, error)

func (*Client) LeaseRun added in v0.1.6

func (c *Client) LeaseRun(ctx context.Context, id, action, token string) (FlowLease, error)

func (*Client) ListCrew

func (c *Client) ListCrew(ctx context.Context) ([]CrewItem, error)

func (*Client) ListMeetings

func (c *Client) ListMeetings(ctx context.Context, status string) ([]MeetingRecord, error)

func (*Client) ListRuns

func (c *Client) ListRuns(ctx context.Context, status string, limit int) ([]Run, error)

func (*Client) PrepareFlowEffect added in v0.1.6

func (c *Client) PrepareFlowEffect(ctx context.Context, run Run, in FlowEffectRequest) (FlowEffectView, error)

func (*Client) ReserveFlowAttempt added in v0.1.6

func (c *Client) ReserveFlowAttempt(ctx context.Context, run Run, in FlowAttemptRequest) (FlowAttemptReservation, error)

func (*Client) SettleFlowAttempt added in v0.1.6

func (c *Client) SettleFlowAttempt(ctx context.Context, run Run, id string, in FlowAttemptSettlement) error

func (*Client) SettleFlowEffect added in v0.1.6

func (c *Client) SettleFlowEffect(ctx context.Context, run Run, ref FlowEffectView, receipt FlowEffectReceipt) error

func (*Client) UpdateMeeting

func (c *Client) UpdateMeeting(ctx context.Context, r MeetingRecord, lease string) (MeetingRecord, error)

func (*Client) UpdateRun

func (c *Client) UpdateRun(ctx context.Context, run Run) (Run, error)

type Config

type Config struct {
	Listen   string
	LockPath string
	Engine   *Engine
}

Config comes from the environment (see FromEnv).

func FromEnv

func FromEnv() Config

FromEnv builds the engine like `arkism capture` does its client:

ARKISM_URL (host.DefaultURL), ARKISM_TOKEN_FILE (/srv/arkism/config/crew.token),
ARKISM_WORKSPACE, ARKISM_PROJECT, ARKISM_USER, ARKISM_AGENT (scope),
ARKISM_CREW_LISTEN (127.0.0.1:8095), ARKISM_CREW_PUBLIC_URL (http://LISTEN),
ARKISM_CREW_RUNNER ("/srv/arkism/bin/arkism crew"), ARKISM_CREW_SANDBOX,
ARKISM_CREW_MODEL ("opus 5.5", AI nodes without a model), ARKISM_CREW_GH (gh), TZ (schedule time zone),
ARKISM_CREW_KEYS, ARKISM_CREW_NOTIFY, ARKISM_CREW_HOOKS (files under ~/.config/arkism),
ARKISM_CREW_TRACE_TOKEN_FILE (producer service token, not the engine's) with
ARKISM_MEMORY_OUTCOME_SPOOL: finished runs become execution traces and outcome receipts.

type Crew

type Crew struct {
	Research     *CrewResearch `json:"research,omitempty"`
	Type         string        `json:"type"`
	Name         string        `json:"name"`
	Handle       string        `json:"handle,omitempty"`
	Model        string        `json:"model,omitempty"`
	Instructions string        `json:"instructions,omitempty"`
	Skills       []string      `json:"skills,omitempty"`
	Trigger      *Trigger      `json:"trigger,omitempty"`
	Enabled      bool          `json:"enabled,omitempty"`
	Limits       *Limits       `json:"limits,omitempty"`
	Graph        *Graph        `json:"graph,omitempty"`
	Notify       *CrewNotify   `json:"notify,omitempty"` // 진행 알림 (progress.go)
	// work agents (type agent, or template saved before agents) and teams, for meetings
	Role            string            `json:"role,omitempty"`
	Effort          string            `json:"effort,omitempty"`
	ModelMode       string            `json:"model_mode,omitempty"`
	FallbackModel   string            `json:"fallback_model,omitempty"`
	MaxOutputTokens int               `json:"max_output_tokens,omitempty"`
	OutputFormat    string            `json:"output_format,omitempty"`
	Backend         string            `json:"backend,omitempty"` // the host runner's agent-runner connection (expert)
	Budget          *AgentBudget      `json:"budget,omitempty"`
	Members         []TeamMember      `json:"members,omitempty"`
	Mode            string            `json:"mode,omitempty"`
	Ending          *TeamEnding       `json:"ending,omitempty"`
	Context         []TeamContext     `json:"context,omitempty"`
	Templates       []MeetingTemplate `json:"templates,omitempty"`
}

Crew is the part of a crew item the engine needs: a workflow, or a persona whose instructions an AI node may take.

type CrewItem

type CrewItem struct {
	ID       string `json:"id"`
	Revision int64  `json:"revision"`
	Crew     Crew   `json:"crew"`
}

type CrewNotify added in v0.1.4

type CrewNotify struct {
	Channels []NotifyChannel `json:"channels,omitempty"`
	Moments  []string        `json:"moments,omitempty"` // start | step | approval | fail | end; empty = all but step
}

type CrewResearch added in v0.1.6

type CrewResearch struct {
	QuickNode        string `json:"quick_node"`
	DeepNode         string `json:"deep_node"`
	DeepAlias        string `json:"deep_alias"`
	EvidenceStrategy string `json:"evidence_strategy"`
	HelperOrigin     string `json:"helper_origin"`
}

type Cron

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

Cron is a parsed five-field schedule: minute hour day-of-month month day-of-week. Fields take *, numbers, a-b ranges, /step and comma lists. Day of week is 0-7 (0 and 7 are Sunday). As in classic cron, when both day fields are restricted a time matches if either matches. Names (MON, JAN) and @-macros are not supported.

func ParseCron

func ParseCron(spec string) (*Cron, error)

func (*Cron) Match

func (c *Cron) Match(t time.Time) bool

Match reports whether t (in its own location) is a firing minute.

type Decision

type Decision struct {
	Approve   bool      `json:"approve"`
	Note      string    `json:"note,omitempty"`
	DecidedBy string    `json:"decided_by"`
	DecidedAt time.Time `json:"decided_at"`
}

type Edge

type Edge struct {
	From string `json:"from"`
	Port string `json:"port,omitempty"`
	To   string `json:"to"`
	Max  int    `json:"max,omitempty"`
}

Edge with Max > 0 goes back: it may be followed Max times per run.

type Engine

type Engine struct {
	Store Store
	Now   func() time.Time
	Loc   *time.Location
	Log   *slog.Logger
	// DefaultModel runs AI nodes when neither the node nor the workflow names a model.
	DefaultModel string
	// Runner is the AI node command prefix; the engine appends
	// "run-node --json" and writes the node spec as JSON on stdin.
	Runner      []string
	Sandbox     []string
	GH          string
	KeysFile    string
	NotifyFile  string
	NotifyScope string // "<workspace>/<project>": the runner's own destinations in NotifyFile
	HooksFile   string
	PublicURL   string
	TelegramAPI string
	HTTP        *http.Client
	CodeTimeout time.Duration

	// Traces records each finished run as an execution trace with its own producer credential and
	// hands the receipt to Sinks (see receipts.go); either unset leaves runs untraced.
	Traces *Client
	Sinks  []ReceiptSink

	// Meetings drives team meetings when set (FromEnv sets the Arkism client).
	Meetings MeetingStore
	// MeetingAI replaces the run-node call of a meeting turn (tests).
	MeetingAI func(context.Context, aiSpec) (aiAnswer, error)
	// contains filtered or unexported fields
}

Engine runs graphs. Progress lives in the runs API after every node change, so a restart resumes waiting runs and settles interrupted ones.

Scheduling rule: a pending node is decided once every source of its incoming edges is finished (done, skipped or failed). An edge is followed when its source is done and the edge has no port, or its port equals the branch the source took ("yes"/"no" for condition, "approve"/"reject" for approval). A decided node runs if at least one incoming edge is followed, otherwise it is skipped; nodes without incoming edges are roots and get the trigger input. A node with one followed edge gets that source's output; with several (and always for merge) it gets an object keyed by source node id. Nodes whose config "access" is "read" run together with other ready read nodes; every other node runs alone.

Back edges (max > 0) are left out of that rule: when a node ends on a back edge's port, the edge's target and every node after it go back to pending, at most max times per edge (see followBack).

func (*Engine) Drive

func (e *Engine) Drive(ctx context.Context, id string) (err error)

Drive advances one run until it finishes, waits or the context ends.

func (*Engine) DriveMeeting

func (e *Engine) DriveMeeting(ctx context.Context, id string) error

DriveMeeting runs a meeting until it ends, waits for a person, or the context ends.

func (*Engine) FireSchedules

func (e *Engine) FireSchedules(ctx context.Context, now time.Time) []string

FireSchedules starts enabled schedule workflows whose cron matches the current minute, at most once per workflow per minute.

func (*Engine) Go

func (e *Engine) Go(ctx context.Context, id string)

Go drives a run in the background; Wait waits for all of them.

func (*Engine) GoMeeting

func (e *Engine) GoMeeting(ctx context.Context, id string)

GoMeeting drives one meeting in the background.

func (*Engine) Handler

func (e *Engine) Handler(ctx context.Context) http.Handler

Handler serves webhooks. Runs started here continue on ctx (the daemon's lifetime), not the request's.

POST /hooks/{workflow_id}          header X-Arkism-Hook-Token; body JSON is the run input → 202 {run_id}

func (*Engine) LoadCrew

func (e *Engine) LoadCrew(ctx context.Context) error

LoadCrew refreshes the crew items from Arkism.

func (*Engine) PollMeetings

func (e *Engine) PollMeetings(ctx context.Context) error

PollMeetings drives open meetings: new ones, and running ones nobody holds.

func (*Engine) PollWaiting

func (e *Engine) PollWaiting(ctx context.Context) error

PollWaiting resumes waiting runs whose decision arrived or whose time came.

func (*Engine) ResumeRunning

func (e *Engine) ResumeRunning(ctx context.Context) error

ResumeRunning continues runs left running by an earlier engine; used once at start-up. Flow runs acquire a persisted lease before settling nodes; legacy runs continue to rely on the daemon's process lock.

func (*Engine) StartRequested

func (e *Engine) StartRequested(ctx context.Context) error

StartRequested drives runs a person or a harness created (the desktop, $handle calls from Claude Code, Codex and the DeepSeek harness on an agent token) that nothing has touched yet: every node pending, or done/skipped when the run restarts from where a failed one stopped. Runs another engine process created are never taken; an expert's workflow run (trigger "agent") is, even when the assist runner started it on a service token. Flow runs can already be touched: their persisted lease decides whether recovery may proceed.

func (*Engine) StartRun

func (e *Engine) StartRun(ctx context.Context, crewID, trigger string, input any) (Run, error)

StartRun creates a run of a workflow; Drive executes it.

func (*Engine) Wait

func (e *Engine) Wait()

type FlowAttemptRequest added in v0.1.6

type FlowAttemptRequest struct {
	NodeID          string `json:"node_id"`
	Attempt         int    `json:"attempt"`
	CallKey         string `json:"call_key"`
	Model           string `json:"model"`
	MaxInputTokens  int64  `json:"max_input_tokens"`
	MaxOutputTokens int64  `json:"max_output_tokens"`
}

type FlowAttemptReservation added in v0.1.6

type FlowAttemptReservation struct {
	ID              string  `json:"id"`
	Status          string  `json:"status"`
	DispatchAllowed bool    `json:"dispatch_allowed"`
	ReservedTokens  int64   `json:"reserved_tokens"`
	ReservedCostUSD float64 `json:"reserved_cost_usd"`
	CostKnown       bool    `json:"cost_known"`
}

type FlowAttemptSettlement added in v0.1.6

type FlowAttemptSettlement struct {
	Status       string   `json:"status"`
	InputTokens  int64    `json:"input_tokens"`
	OutputTokens int64    `json:"output_tokens"`
	UsageKnown   bool     `json:"usage_known"`
	Estimated    bool     `json:"estimated"`
	CostUSD      *float64 `json:"cost_usd,omitempty"`
}

type FlowAttemptStore added in v0.1.6

type FlowAttemptStore interface {
	ReserveFlowAttempt(context.Context, Run, FlowAttemptRequest) (FlowAttemptReservation, error)
	SettleFlowAttempt(context.Context, Run, string, FlowAttemptSettlement) error
}

type FlowChild added in v0.1.6

type FlowChild struct {
	Kind     string `json:"kind"`
	ID       string `json:"id"`
	CrewID   string `json:"crew_id"`
	NodeID   string `json:"node_id"`
	Attempt  int    `json:"attempt"`
	Required bool   `json:"required"`
}

FlowChild identifies durable work owned by one node attempt. The server creates the child and its parent link in the same transaction.

type FlowChildCreated added in v0.1.6

type FlowChildCreated struct {
	Parent Run       `json:"parent"`
	Child  FlowChild `json:"child"`
}

type FlowChildRequest added in v0.1.6

type FlowChildRequest struct {
	NodeID  string `json:"node_id"`
	Attempt int    `json:"attempt"`
	Kind    string `json:"kind"`
	CrewID  string `json:"crew_id"`
	Input   string `json:"input"`
}

type FlowChildStore added in v0.1.6

type FlowChildStore interface {
	CreateFlowChild(context.Context, Run, FlowChildRequest) (FlowChildCreated, error)
	CancelFlowChildren(context.Context, Run) error
}

FlowChildStore is required only for FLOW nodes that delegate work.

type FlowEffectReceipt added in v0.1.6

type FlowEffectReceipt struct {
	Status       string `json:"status"`
	HTTPStatus   int    `json:"http_status"`
	ResponseHash string `json:"response_hash,omitempty"`
}

type FlowEffectRequest added in v0.1.6

type FlowEffectRequest struct {
	NodeID     string `json:"node_id"`
	Attempt    int    `json:"attempt"`
	Adapter    string `json:"adapter"`
	TargetHash string `json:"target_hash"`
	ArgsHash   string `json:"args_hash"`
	Preview    string `json:"preview"`
}

type FlowEffectStore added in v0.1.6

type FlowEffectStore interface {
	PrepareFlowEffect(context.Context, Run, FlowEffectRequest) (FlowEffectView, error)
	ClaimFlowEffect(context.Context, Run, FlowEffectView, string) (FlowEffectView, error)
	SettleFlowEffect(context.Context, Run, FlowEffectView, FlowEffectReceipt) error
}

type FlowEffectView added in v0.1.6

type FlowEffectView struct {
	ID              string `json:"id"`
	EffectHash      string `json:"effect_hash"`
	Status          string `json:"status"`
	DispatchAllowed bool   `json:"dispatch_allowed"`
}

type FlowLease added in v0.1.6

type FlowLease struct {
	LeaseUntil      time.Time `json:"lease_until"`
	CancelRequested bool      `json:"cancel_requested"`
}

type FlowLeaseStore added in v0.1.6

type FlowLeaseStore interface {
	LeaseRun(context.Context, string, string, string) (FlowLease, error)
}

FlowLeaseStore is optional for legacy stores. Flow runs require it: a local process claim alone cannot prevent a second daemon from repeating work.

type FlowPolicy added in v0.1.6

type FlowPolicy struct {
	MaxDepth      int     `json:"max_depth"`
	MaxChildren   int     `json:"max_children"`
	MaxConcurrent int     `json:"max_concurrent"`
	MaxTokens     int64   `json:"max_tokens,omitempty"`
	MaxCostUSD    float64 `json:"max_cost_usd,omitempty"`
}

type Graph

type Graph struct {
	Nodes []Node `json:"nodes"`
	Edges []Edge `json:"edges"`
}

Graph mirrors the crew graph JSON.

type Limits

type Limits struct {
	MaxSteps   int `json:"max_steps,omitempty"`
	MaxMinutes int `json:"max_minutes,omitempty"`
}

type Meeting

type Meeting struct {
	RootID          string             `json:"root_id,omitempty"`
	ParentID        string             `json:"parent_id,omitempty"`
	ParentNodeID    string             `json:"parent_node_id,omitempty"`
	Status          string             `json:"status"`
	TeamID          string             `json:"team_id"`
	TeamRevision    int64              `json:"team_revision"`
	TeamSnapshot    *MeetingSnapshot   `json:"team_snapshot,omitempty"`
	Title           string             `json:"title"`
	Topic           string             `json:"topic"`
	TemplateKey     string             `json:"template_key,omitempty"`
	Trigger         string             `json:"trigger"`
	Round           int                `json:"round"`
	MaxRounds       int                `json:"max_rounds"`
	Speaking        string             `json:"speaking,omitempty"`
	Turns           []MeetingTurn      `json:"turns"`
	NotesPending    []MeetingNote      `json:"notes_pending"`
	TokensUsed      int64              `json:"tokens_used"`
	TokenCap        int64              `json:"token_cap,omitempty"`
	CostUSD         float64            `json:"cost_usd"`
	CostEstimateUSD float64            `json:"cost_estimate_usd,omitempty"`
	Conclusion      *MeetingConclusion `json:"conclusion,omitempty"`
	EndedReason     string             `json:"ended_reason,omitempty"`
	Waiting         *MeetingWaiting    `json:"waiting,omitempty"`
	Saved           *MeetingSaved      `json:"saved,omitempty"`
	Discord         json.RawMessage    `json:"discord,omitempty"`
	BudgetApproved  json.RawMessage    `json:"budget_approved,omitempty"`
	LeaseUntil      *time.Time         `json:"lease_until,omitempty"`
}

Meeting is the crew_meeting body. Discord, BudgetApproved and LeaseUntil are written by others (the bot, a person, the server) and sent back as read.

type MeetingConclusion

type MeetingConclusion struct {
	Summary       string   `json:"summary"`
	Decisions     []string `json:"decisions"`
	Todos         []string `json:"todos"`
	OpenQuestions []string `json:"open_questions"`
}

type MeetingCreator added in v0.1.4

type MeetingCreator interface {
	CreateMeeting(ctx context.Context, teamID, title, topic string) (MeetingRecord, error)
}

MeetingCreator is the optional MeetingStore part that asks for a new meeting (Client implements it).

type MeetingNote

type MeetingNote struct {
	By   string    `json:"by"`
	Text string    `json:"text"`
	At   time.Time `json:"at"`
}

type MeetingRecord

type MeetingRecord struct {
	ID       string  `json:"id"`
	Revision int64   `json:"revision"`
	Meeting  Meeting `json:"crew_meeting"`
}

type MeetingSaved

type MeetingSaved struct {
	CaptureEventID string   `json:"capture_event_id,omitempty"`
	CandidateIDs   []string `json:"candidate_ids,omitempty"`
}

type MeetingSnapshot

type MeetingSnapshot struct {
	Team   Crew            `json:"team"`
	Agents map[string]Crew `json:"agents"`
}

type MeetingStore

type MeetingStore interface {
	ListMeetings(ctx context.Context, status string) ([]MeetingRecord, error)
	GetMeeting(ctx context.Context, id string) (MeetingRecord, error)
	UpdateMeeting(ctx context.Context, r MeetingRecord, lease string) (MeetingRecord, error)
	// Capture sends claims for one event and returns the candidate ids made.
	Capture(ctx context.Context, eventID string, claims []CaptureClaim, model string) ([]string, error)
	// AgentSpend is each agent's estimated spend over the last 30 days.
	AgentSpend(ctx context.Context) (map[string]float64, error)
	// ContextText is the text of one record a team reads before it meets.
	ContextText(ctx context.Context, collection, id string) (string, error)
}

MeetingStore is the Arkism side meetings need; Client implements it.

type MeetingTemplate

type MeetingTemplate struct {
	Key       string `json:"key"`
	Name      string `json:"name"`
	Prompt    string `json:"prompt,omitempty"`
	Overrides *struct {
		Mode   string      `json:"mode,omitempty"`
		Ending *TeamEnding `json:"ending,omitempty"`
	} `json:"overrides,omitempty"`
}

type MeetingTurn

type MeetingTurn struct {
	Seq          int        `json:"seq"`
	Round        int        `json:"round"`
	AgentID      string     `json:"agent_id,omitempty"`
	Speaker      string     `json:"speaker"`
	Model        string     `json:"model,omitempty"`
	Kind         string     `json:"kind"`
	Stance       string     `json:"stance,omitempty"`
	Text         string     `json:"text"`
	InputTokens  int64      `json:"input_tokens,omitempty"`
	OutputTokens int64      `json:"output_tokens,omitempty"`
	CostUSD      float64    `json:"cost_usd,omitempty"`
	Estimated    bool       `json:"estimated,omitempty"`
	StartedAt    *time.Time `json:"started_at,omitempty"`
	FinishedAt   *time.Time `json:"finished_at,omitempty"`
	OK           bool       `json:"ok"`
	Error        string     `json:"error,omitempty"`
}

type MeetingWaiting

type MeetingWaiting struct {
	Kind   string    `json:"kind"`
	Reason string    `json:"reason,omitempty"`
	At     time.Time `json:"at"`
}

type Node

type Node struct {
	ID     string            `json:"id"`
	Type   string            `json:"type"`
	Title  string            `json:"title,omitempty"`
	Config map[string]string `json:"config,omitempty"`
}

type NodeState

type NodeState struct {
	Status     string     `json:"status"`
	Port       string     `json:"port,omitempty"`
	StartedAt  *time.Time `json:"started_at,omitempty"`
	FinishedAt *time.Time `json:"finished_at,omitempty"`
	Output     string     `json:"output,omitempty"`
	Error      string     `json:"error,omitempty"`
	Runs       int        `json:"runs,omitempty"`   // starts of this node in the run
	Tokens     int64      `json:"tokens,omitempty"` // AI tokens (input + output) of its last finish
	// Backend is the agent-runner trace of a step sent to a connection (names and ids only, never text).
	Backend map[string]any `json:"backend,omitempty"`
}

type NotifyChannel added in v0.1.4

type NotifyChannel struct {
	Kind    string `json:"kind"`    // discord | telegram | slack
	Channel string `json:"channel"` // notify.json destination name ("#ops" → "ops"); unknown → "default"
	When    string `json:"when"`    // events | approval | report
}

type NotifyConfig

type NotifyConfig struct {
	Telegram map[string]struct {
		BotToken string `json:"bot_token"`
		ChatID   string `json:"chat_id"`
	} `json:"telegram"`
	Slack map[string]struct {
		WebhookURL string `json:"webhook_url"`
	} `json:"slack"`
	Email map[string]struct {
		Host string   `json:"host"`
		Port string   `json:"port"`
		User string   `json:"user"`
		Pass string   `json:"pass"`
		From string   `json:"from"`
		To   []string `json:"to"`
	} `json:"email"`
}

NotifyConfig is the host file ~/.config/arkism/notify.json (0600). Each channel maps destination names to how to reach them; none of it is ever stored in Arkism.

{"telegram": {"default": {"bot_token": "...", "chat_id": "..."}},
 "slack":    {"default": {"webhook_url": "https://hooks.slack.com/..."}},
 "email":    {"default": {"host": "smtp.example.com", "port": "587", "user": "...",
                          "pass": "...", "from": "bot@example.com", "to": ["me@example.com"]}}}

type ReceiptSink added in v0.1.1

type ReceiptSink func(TraceReceipt) error

ReceiptSink takes one receipt; an error is logged and leaves the trace stored.

type Ref added in v0.1.1

type Ref struct {
	Collection string `json:"collection"`
	ID         string `json:"id"`
	Revision   int64  `json:"revision"`
}

Ref is an exact canonical record version.

type Run

type Run struct {
	LeaseToken string   `json:"-"`
	ID         string   `json:"id"`
	Revision   int64    `json:"revision"`
	State      RunState `json:"crew_run"`
	// CreatedByType tells a run a person started from the desktop (시험 실행·지금 실행) apart from the engine's own.
	CreatedByType string `json:"created_by_type,omitempty"`
}

type RunState

type RunState struct {
	Effects         map[string]string     `json:"effects,omitempty"`
	SourceType      string                `json:"source_type,omitempty"`
	RootID          string                `json:"root_id,omitempty"`
	ParentID        string                `json:"parent_id,omitempty"`
	ParentNodeID    string                `json:"parent_node_id,omitempty"`
	Depth           int                   `json:"depth,omitempty"`
	Children        map[string]FlowChild  `json:"children,omitempty"`
	Definitions     map[string]CrewItem   `json:"definitions,omitempty"`
	Policy          *FlowPolicy           `json:"policy,omitempty"`
	ResearchMode    string                `json:"research_mode,omitempty"`
	InvocationMode  string                `json:"invocation_mode,omitempty"`
	Definition      *Crew                 `json:"definition,omitempty"`
	Origin          map[string]string     `json:"origin,omitempty"`
	CancelRequested bool                  `json:"cancel_requested,omitempty"`
	CrewID          string                `json:"crew_id"`
	CrewRevision    int64                 `json:"crew_revision"`
	Trigger         string                `json:"trigger"`
	Status          string                `json:"status"`
	Input           string                `json:"input,omitempty"`
	Nodes           map[string]*NodeState `json:"nodes"`
	Waiting         *Waiting              `json:"waiting,omitempty"`
	Decisions       map[string]Decision   `json:"decisions,omitempty"`
	Summary         string                `json:"summary,omitempty"`
	Only            string                `json:"only,omitempty"`  // run this one node, skip the rest
	Loops           map[string]int        `json:"loops,omitempty"` // back edge uses, keyed "from>to:port"
	Deadline        *time.Time            `json:"deadline,omitempty"`
}

RunState is the crew_run body of a runs API record.

type Scope

type Scope struct {
	WorkspaceID string `json:"workspace_id"`
	ProjectID   string `json:"project_id"`
	UserID      string `json:"user_id,omitempty"`
	AgentID     string `json:"agent_id,omitempty"`
}

type Store

type Store interface {
	ListCrew(ctx context.Context) ([]CrewItem, error)
	ListRuns(ctx context.Context, status string, limit int) ([]Run, error)
	GetRun(ctx context.Context, id string) (Run, error)
	CreateRun(ctx context.Context, st RunState) (Run, error)
	UpdateRun(ctx context.Context, r Run) (Run, error)
}

Store is the Arkism side the engine needs. Client implements it over HTTP.

type TeamContext

type TeamContext struct {
	Collection string `json:"collection"`
	ID         string `json:"id"`
	Title      string `json:"title,omitempty"`
	Share      string `json:"share"`
}

type TeamEnding

type TeamEnding struct {
	MaxRounds        int   `json:"max_rounds"`
	TokenCap         int64 `json:"token_cap,omitempty"`
	EndOnAllApproved bool  `json:"end_on_all_approved,omitempty"`
	EndOnModerator   bool  `json:"end_on_moderator,omitempty"`
}

type TeamMember

type TeamMember struct {
	AgentID       string `json:"agent_id"`
	Order         int    `json:"order"`
	Role          string `json:"role"`
	Focus         string `json:"focus,omitempty"`
	ModelOverride string `json:"model_override,omitempty"`
}

type TraceReceipt added in v0.1.1

type TraceReceipt struct {
	Scope        map[string]string `json:"scope"`
	SessionID    string            `json:"session_id"`
	Trace        Ref               `json:"trace"`
	Run          Ref               `json:"run"`
	SourceEvents []Ref             `json:"source_events"`
	OccurredAt   string            `json:"occurred_at"`
}

TraceReceipt is what a finished run hands its sinks: scope and refs, never bodies.

type Trigger

type Trigger struct {
	Kind string `json:"kind"`
	Cron string `json:"cron,omitempty"`
}

type Waiting

type Waiting struct {
	EffectID      string     `json:"effect_id,omitempty"`
	EffectHash    string     `json:"effect_hash,omitempty"`
	ChildRunID    string     `json:"child_run_id,omitempty"`
	ChildKind     string     `json:"child_kind,omitempty"`
	ChildRevision int64      `json:"child_revision,omitempty"`
	DecisionKind  string     `json:"decision_kind,omitempty"`
	NodeID        string     `json:"node_id"`
	Kind          string     `json:"kind"`
	ResumeAt      *time.Time `json:"resume_at,omitempty"`
	Question      string     `json:"question,omitempty"`
	CallbackHash  string     `json:"callback_hash,omitempty"`
}

Jump to

Keyboard shortcuts

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