workflow

package
v1.8.0 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	StatusWorking = "working"
	StatusDone    = "done"
	StatusFailed  = "failed"
)

Workflow status constants.

View Source
const WorkflowsGlobalDirName = "workflows"
View Source
const WorkflowsRepoDirName = ".ask/workflows"

Variables

View Source
var DefaultTracker = NewTracker()

DefaultTracker is the package-wide tracker instance.

View Source
var ErrWorkflowAmbiguous = errors.New("workflow exists in multiple scopes; pass scope to pick one")

Functions

func BuildStepInstruction

func BuildStepInstruction(step Step, source Source, pc *StepPromptCtx) string

BuildStepInstruction assembles the system instruction for one workflow step: the author's prompt, the run's reference block, and the end_turn contract (plus loop framing when the step sits inside a loop).

It does NOT thread previous step output. The graph does that: a node's output arrives as the next node's input, and every step agent runs with IncludeContentsNone so it sees that input and its own work rather than the full transcript of everything before it.

func ChatTurnCountLabel

func ChatTurnCountLabel(n int) string

func DefToConfigDef

func DefToConfigDef(d Def) config.WorkflowDef

DefToConfigDef converts a workflow.Def to a config.WorkflowDef.

func EndTurnInstructionBlock

func EndTurnInstructionBlock(loop *LoopPromptCtx) string

EndTurnInstructionBlock renders the end_turn contract for a step, and inside a loop the iteration framing plus how to break out.

Breaking a loop is ADK's exit_loop tool, not an end_turn argument: the tool sets Actions.Escalate, which is what a loopagent watches for.

func ExportBytes

func ExportBytes(d Def) ([]byte, error)

ExportBytes renders d as the standalone JSON a plugin's workflows/ directory holds (scope-free).

func ExportFile

func ExportFile(d Def) (string, error)

ExportFile writes ExportBytes(d) as FileName(d.Name).json in a fresh temporary directory and returns its path.

func FileName

func FileName(name string) string

FileName maps a workflow name onto a filesystem-safe filename stem.

func GlobalDir

func GlobalDir() string

GlobalDir returns the absolute global workflows directory (~/.config/ask/workflows/).

func LoopNoteLine

func LoopNoteLine(loopName, action, detail string) string

LoopNoteLine formats a loop transition note.

func MutateWorkflows

func MutateWorkflows(cwd string, fn func(items []Def) ([]Def, error)) error

MutateWorkflows runs a read-modify-write cycle on the merged workflows.

func ProviderMeta

func ProviderMeta(provider, model string) string

ProviderMeta formats provider and model strings into a metadata label.

func RepoDir

func RepoDir(cwd string) string

RepoDir returns the absolute repo-local workflows directory for cwd.

func SaveAll

func SaveAll(cwd string, items []Def) error

SaveAll persists workflows across their respective scopes.

func StepSummaryLine

func StepSummaryLine(name, provider, model, summary string) string

StepSummaryLine formats a step completion summary.

func WorkflowNoteLine

func WorkflowNoteLine(msg, detail string) string

WorkflowNoteLine formats a single-line status note.

Types

type ChatTurn

type ChatTurn struct {
	Role string `json:"role"`
	Text string `json:"text"`
}

type Compiled

type Compiled struct {
	Workflow *adkworkflow.Workflow
	// AgentInfo maps an emitted event's Author (an ADK agent
	// name) to its metadata. Inner loop agents map to their
	// containing loop step index.
	AgentInfo map[string]StepAgentInfo
	// Models are the per-step models built at compile time. Close releases
	// them after the run — a subprocess-backed provider (Claude Code) forks
	// a child per step that must be terminated.
	Models []model.LLM
}

Compiled is a Def rendered as an executable ADK graph, plus the lookup the event adapter needs to attribute events back to steps.

func CompileWorkflow

func CompileWorkflow(ctx context.Context, cfg WorkflowAgentConfig) (*Compiled, error)

CompileWorkflow compiles a Def into an ADK workflow graph.

Every top-level step becomes one node, chained Start -> n0 -> n1 -> …:

  • An agent step becomes an AgentNode wrapping an llmagent.
  • A loop step becomes an AgentNode wrapping a loopagent whose sub-agents are the inner steps, each carrying ADK's exit_loop tool. Any inner step calling exit_loop escalates and ends the loop; otherwise it runs to MaxIterations.

Two llmagent settings carry the workflow semantics and must not be dropped:

  • IncludeContentsNone gives each step its own context. Without it a step inherits every prior step's events, and ADK renders those foreign events as prose — every tool call and every full tool result — so step 3 would carry steps 1 and 2 in full.
  • InstructionProvider, never Config.Instruction. Step prompts are user-authored and routinely contain braces; a static Instruction is run through ADK's state interpolator and hard-fails the invocation on the first `{...}`.

func (*Compiled) Close

func (c *Compiled) Close() error

Close releases every per-step model. It is safe to call on a nil Compiled and after a partial compile.

func (*Compiled) StepIndex

func (c *Compiled) StepIndex(author string) (int, bool)

StepIndex resolves an event author to a top-level step index.

type Def

type Def struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Steps       []Step `json:"steps"`
	Scope       Scope  `json:"-"`
	// Plugin is the "name@marketplace" that ships this workflow when
	// Scope == ScopePlugin.
	Plugin string `json:"-"`
}

Def defines a named sequence of workflow steps.

func ConfigDefToDef

func ConfigDefToDef(d config.WorkflowDef) Def

ConfigDefToDef converts a config.WorkflowDef to a workflow.Def.

func ListAll

func ListAll(cwd string) []Def

ListAll returns all workflows visible to cwd in order: global, repo, user, then the read-only plugin workflows.

func LoadFileWorkflows

func LoadFileWorkflows(dir string, scope Scope) []Def

LoadFileWorkflows reads every *.json under dir and tags them with scope.

func LoadPluginWorkflows

func LoadPluginWorkflows(cwd string) []Def

LoadPluginWorkflows reads the workflows shipped by every enabled plugin.

func LoadUserWorkflows

func LoadUserWorkflows(cwd string) ([]Def, error)

LoadUserWorkflows reads the user-scope workflow list from ask.json for cwd.

func ResolveByName

func ResolveByName(cwd, name string, scope Scope) (Def, error)

ResolveByName resolves a workflow by name and optional scope. With no scope specified, the first matching workflow in global -> repo -> user order is returned (personal-wins).

func (Def) EffectiveMaxIterations

func (d Def) EffectiveMaxIterations(s Step) int

func (Def) ReadOnly

func (d Def) ReadOnly() bool

ReadOnly reports whether the workflow cannot be edited in place.

func (Def) Validate

func (d Def) Validate() error

type FinishData

type FinishData struct {
	Description string   `json:"description"`
	Artifacts   []string `json:"artifacts"`
}

FinishData captures completion metadata reported at workflow termination.

type LoopPromptCtx

type LoopPromptCtx struct {
	Name          string
	MaxIterations int
	ExitCondition string
	IsTail        bool
}

LoopPromptCtx carries loop metadata for instruction assembly.

type NoopRunnerListener

type NoopRunnerListener struct{}

NoopRunnerListener provides a default empty implementation of RunnerListener.

func (NoopRunnerListener) OnNote

func (NoopRunnerListener) OnNote(int, string)

func (NoopRunnerListener) OnWorkflowDone

func (NoopRunnerListener) OnWorkflowDone(int, string, []string)

func (NoopRunnerListener) OnWorkflowFailed

func (NoopRunnerListener) OnWorkflowFailed(int, string)

func (NoopRunnerListener) OnWorkflowStarted

func (NoopRunnerListener) OnWorkflowStarted(int, Def, Source)

func (NoopRunnerListener) OnWorkflowStepDone

func (NoopRunnerListener) OnWorkflowStepDone(int, int, string)

func (NoopRunnerListener) OnWorkflowStepStarted

func (NoopRunnerListener) OnWorkflowStepStarted(int, int, string, string, string)

type Progress

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

Progress turns the ADK event stream of a running workflow graph into RunnerListener callbacks.

Every callback is driven by something that actually happened in the stream: a step is "started" when an event authored by its agent arrives, and "done" when that step's successor starts or the run ends cleanly. Steps that never ran are never reported, and a run that fails partway reports only the steps that got that far — the previous implementation closed out every remaining step as completed and hardcoded a successful FinishData, so a chain that died at step 1 of 5 showed 5/5 green.

func NewProgress

func NewProgress(compiled *Compiled, def Def, src Source, cwd string, tabID int, listener RunnerListener, tracker *Tracker) *Progress

NewProgress builds a Progress for one run and emits OnWorkflowStarted.

func (*Progress) Finish

func (p *Progress) Finish(err error) *RunState

Finish closes the run. A nil err completes the in-flight step and reports success; a non-nil err leaves every unfinished step unfinished and reports the failure.

func (*Progress) Observe

func (p *Progress) Observe(ev *session.Event)

Observe consumes one event from the workflow's ADK stream.

func (*Progress) SetFinishData

func (p *Progress) SetFinishData(fd *FinishData)

SetFinishData records the run's completion report when it is read directly off the tool environment rather than seen in the event stream — the finish_workflow tool parks it there as it runs.

func (*Progress) State

func (p *Progress) State() *RunState

State exposes the run state accumulated so far.

type RunState

type RunState struct {
	Workflow     Def
	Source       Source
	StartedAt    time.Time
	StepIdx      int
	Done         bool
	Failed       bool
	FailedReason string
	FinishData   *FinishData
}

RunState represents the state of a workflow run.

type RunnerListener

type RunnerListener interface {
	OnWorkflowStarted(tabID int, def Def, src Source)
	OnWorkflowStepStarted(tabID int, stepIdx int, stepName, provider, model string)
	OnWorkflowStepDone(tabID int, stepIdx int, summary string)
	OnWorkflowDone(tabID int, description string, artifacts []string)
	OnWorkflowFailed(tabID int, reason string)
	OnNote(tabID int, text string)
}

RunnerListener receives progress notifications during workflow execution.

type Scope

type Scope string
const (
	ScopeUser   Scope = "user"
	ScopeRepo   Scope = "repo"
	ScopeGlobal Scope = "global"
	// ScopePlugin marks a workflow shipped by an installed plugin. It is
	// read-only: copy it into another scope to change it.
	ScopePlugin Scope = "plugin"
)

func NormalizeScope

func NormalizeScope(s Scope) (Scope, error)

NormalizeScope maps empty string to ScopeUser and validates Scope.

type Source

type Source struct {
	Kind           SourceKind `json:"kind"`
	IssueDisplay   string     `json:"issue_display,omitempty"`
	IssueKey       string     `json:"issue_key,omitempty"`
	ChatLabel      string     `json:"chat_label,omitempty"`
	ChatKey        string     `json:"chat_key,omitempty"`
	ChatTranscript []ChatTurn `json:"chat_transcript,omitempty"`
	TextLabel      string     `json:"text_label,omitempty"`
	TextKey        string     `json:"text_key,omitempty"`
	TextAppend     string     `json:"text_append,omitempty"`
}

func NewChatSource

func NewChatSource(originTabID int, turns []ChatTurn) Source

func NewTextSource

func NewTextSource(originTabID int, appendText string) Source

func (Source) Display

func (s Source) Display() string

func (Source) Key

func (s Source) Key() string

func (Source) RefBlock

func (s Source) RefBlock() string

type SourceKind

type SourceKind int
const (
	SourceKindIssue SourceKind = iota
	SourceKindChat
	SourceKindText
)

type StatusListener

type StatusListener func(key string, status string)

StatusListener receives notifications whenever a workflow status changes.

type Step

type Step struct {
	Name          string `json:"name"`
	Kind          string `json:"kind,omitempty"` // "loop" or empty/omitted for standard step
	Provider      string `json:"provider,omitempty"`
	Model         string `json:"model,omitempty"`
	Prompt        string `json:"prompt,omitempty"`
	Steps         []Step `json:"steps,omitempty"`
	MaxIterations int    `json:"maxIterations,omitempty"`
	ExitCondition string `json:"exitCondition,omitempty"`
}

Step represents a single agent step or a loop of inner steps.

func (Step) IsLoop

func (s Step) IsLoop() bool

type StepAgentInfo

type StepAgentInfo struct {
	StepIndex int
	StepName  string
	Provider  string
	Model     string
	InLoop    bool
	LoopName  string
	InnerIdx  int
}

StepAgentInfo tracks metadata for one step agent in the compiled graph.

type StepPromptCtx

type StepPromptCtx struct {
	Loop                *LoopPromptCtx
	IsStartStep         bool
	IsWorkflowFinalStep bool
}

StepPromptCtx carries contextual details injected into a step's instruction.

type StepRole

type StepRole struct {
	// InLoop is true when the step runs inside a loop container.
	InLoop bool
	// IsTail is true when the step is the last inner step of its loop —
	// the only step allowed to break the loop early via exit_loop.
	IsTail bool
	// IsFinal is true when the step is the last thing the whole workflow
	// runs (the last top-level step, or the tail of a final loop). Only
	// this step gets finish_workflow.
	IsFinal bool
}

StepRole tells a ToolsBuilder where a step sits in the workflow, so it can decide which position-dependent tools to attach.

type Tracker

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

Tracker manages workflow runtime statuses across tabs and projects.

func GlobalTracker

func GlobalTracker() *Tracker

GlobalTracker returns the package-wide default tracker singleton.

func NewTracker

func NewTracker() *Tracker

NewTracker instantiates a new thread-safe workflow tracker.

func (*Tracker) ActiveTabFor

func (t *Tracker) ActiveTabFor(key string) (int, bool)

ActiveTabFor returns (tabID, true) if key currently has an active in-memory working entry.

func (*Tracker) ActiveWorkflowNames

func (t *Tracker) ActiveWorkflowNames() map[string]struct{}

ActiveWorkflowNames returns the set of workflow names that currently have running sessions.

func (*Tracker) Clear

func (t *Tracker) Clear(key string)

Clear drops the in-memory entry for key without modifying disk.

func (*Tracker) Lookup

func (t *Tracker) Lookup(cwd, key string) (TrackerEntry, bool)

Lookup returns the runtime entry for key, hydrating from disk if missing in memory.

func (*Tracker) MarkFinal

func (t *Tracker) MarkFinal(cwd, key, workflow, status string, stepIdx int)

MarkFinal records and persists a terminal status (StatusDone or StatusFailed).

func (*Tracker) MarkStep

func (t *Tracker) MarkStep(key string, stepIdx int)

MarkStep advances the in-memory step index without altering status.

func (*Tracker) MarkWorking

func (t *Tracker) MarkWorking(cwd, key, workflow string, tabID int)

MarkWorking marks a workflow run as actively running in tab tabID.

func (*Tracker) ResetForTest

func (t *Tracker) ResetForTest()

ResetForTest clears the in-memory tracker entries for test isolation.

func (*Tracker) SetListener

func (t *Tracker) SetListener(l StatusListener)

SetListener attaches a listener function called on every status change.

type TrackerEntry

type TrackerEntry struct {
	Status    string    `json:"status"`
	TabID     int       `json:"tab_id,omitempty"`
	Workflow  string    `json:"workflow"`
	StepIndex int       `json:"step_index"`
	StartedAt time.Time `json:"started_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

TrackerEntry is one in-memory or persisted runtime status entry for an issue or target.

type WorkflowAgentConfig

type WorkflowAgentConfig struct {
	Def    Def
	Source Source
	Cwd    string
	TabID  int

	// ModelBuilder resolves the LLM for one step. Required.
	ModelBuilder func(ctx context.Context, step Step) (model.LLM, error)
	// ToolsBuilder and ToolsetsBuilder supply the step's tool surface.
	// StepRole tells them where the step sits, so the builder can attach
	// finish_workflow to the final step only, and so on.
	ToolsBuilder    func(ctx context.Context, step Step, role StepRole) ([]tool.Tool, error)
	ToolsetsBuilder func(ctx context.Context, step Step, role StepRole) ([]tool.Toolset, error)
	// InstructionBuilder renders the step's system instruction. Defaults
	// to BuildStepInstruction.
	InstructionBuilder func(step Step, src Source, pc *StepPromptCtx) string
	// MaxRetries bounds per-node retries on failure. Zero means
	// workflowDefaultMaxRetries; negative disables retries.
	MaxRetries int
	// BeforeModelCallbacks run before every LLM invocation.
	BeforeModelCallbacks []llmagent.BeforeModelCallback
}

WorkflowAgentConfig carries everything the compiler needs to turn a Def into a runnable ADK graph. The builder callbacks are the seam the engine and the TUI fill in with their own model/tool wiring, and the seam tests swap for fakes.

Jump to

Keyboard shortcuts

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