Documentation
¶
Overview ¶
Package flow implements jcode's dynamic-workflow engine: user- or agent-authored JavaScript orchestration scripts that fan work out across many subagents, run them in parallel/pipeline, and collect structured results — the plan lives in code (à la Claude Code Dynamic Workflows / Qoder CLI Workflows).
The engine is a thin goja (JavaScript) control-flow shell over a hand-written Go execution core. The script itself does no I/O (no shell/filesystem/network); every side effect happens through the agent() primitive, which spawns a subagent that goes through jcode's normal tools, permissions, and hooks.
Design doc: internal-doc/dynamic-workflow-design.md
Index ¶
- Constants
- func RunIDFromContext(ctx context.Context) string
- func SlashRunPrompt(name, userInput string) string
- func Validate(source string) error
- func WithRunID(ctx context.Context, runID string) context.Context
- type AgentEvent
- type AgentResult
- type AgentSpec
- type Engine
- type EventSink
- type FuncSink
- func (s FuncSink) OnAgentDone(runID string, a AgentEvent)
- func (s FuncSink) OnAgentStart(runID string, a AgentEvent)
- func (s FuncSink) OnLog(runID, level, msg string)
- func (s FuncSink) OnPhase(runID, title, detail string)
- func (s FuncSink) OnRunDone(runID string, result interface{}, err error)
- func (s FuncSink) OnRunStart(r RunInfo)
- type Loader
- func (l *Loader) All() []Workflow
- func (l *Loader) Get(name string) (Workflow, bool)
- func (l *Loader) GetBySlash(slash string) (Workflow, bool)
- func (l *Loader) LoadProject(projectDir string)
- func (l *Loader) Resolve(name string) (Workflow, bool)
- func (l *Loader) SaveUser(name, src string) (string, error)
- func (l *Loader) SlashCommands() []SlashCommand
- type Meta
- type NopSink
- type Option
- type Phase
- type RunInfo
- type RunOptions
- type Scope
- type SlashCommand
- type SpawnFunc
- type Workflow
Constants ¶
const ( DefaultMaxConcurrent = 16 // concurrent agents DefaultMaxAgents = 1000 // hard total per run (runaway backstop) DefaultTimeout = 30 * time.Minute )
Default caps, matching Claude Code's dynamic workflows.
Variables ¶
This section is empty.
Functions ¶
func RunIDFromContext ¶
RunIDFromContext returns the run ID tagged on ctx, or "".
func SlashRunPrompt ¶ added in v0.9.3
SlashRunPrompt is the agent instruction a "/<workflow>" slash command expands to on every frontend (TUI / Web / ACP). It tells the agent to run the named saved workflow via the workflow_run tool rather than authoring an inline script; any text the user typed after the slash is handed over so the agent can shape it into the workflow's `args` object. Keeping the wording here means all frontends stay in lockstep.
func Validate ¶
Validate pre-flights a workflow source WITHOUT running it: it checks the `meta` block parses and that the full script compiles as JavaScript. Use it to reject an agent-generated or user-supplied script with a clear error before spawning any agents. (Engine.Run compiles too, so a bad script never runs; Validate just moves that check up-front with a friendlier message.)
Types ¶
type AgentEvent ¶
type AgentEvent struct {
ID string // stable within a run, e.g. "agent_7"
Label string
Phase string
Status string // "running" | "done" | "failed"
Tokens int64
Detail string // last tool / short note
Err string
}
AgentEvent carries per-agent progress to the sink.
type AgentResult ¶
AgentResult is what a single agent() call returns. When Schema was set and the subagent produced valid structured output, Structured is non-nil and is what the script sees; otherwise the script sees Text.
type AgentSpec ¶
type AgentSpec struct {
Prompt string `json:"-"`
Label string `json:"label"`
Phase string `json:"phase"`
Model string `json:"model"` // "provider/model" override; "" = session default
AgentType string `json:"agentType"` // explore|general|coordinator; "" = explore
Schema interface{} `json:"schema"` // JSON Schema (as a JS object) → structured output
Isolation string `json:"isolation"` // "worktree" (recognised; isolation is a later phase)
}
AgentSpec describes a single agent() call from the script. Prompt is the first positional argument; the rest come from the opts object (mapped by json tag).
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine runs workflows. It depends only on a SpawnFunc and an EventSink, so it never imports the tools/model packages — the real spawn adapter is wired by the command layer, and tests inject a fake. One Engine can run many workflows.
type EventSink ¶
type EventSink interface {
OnRunStart(r RunInfo)
OnPhase(runID, title, detail string)
OnAgentStart(runID string, a AgentEvent)
OnAgentDone(runID string, a AgentEvent)
OnLog(runID, level, msg string)
OnRunDone(runID string, result interface{}, err error)
}
EventSink receives run progress. It is frontend-agnostic: the headless CLI, TUI, Web, and ACP each provide an implementation. All methods must be safe for concurrent use — the engine calls them from the loop goroutine, but the sink may fan out to other goroutines. Sinks must not block for long (they run on the loop).
type FuncSink ¶
type FuncSink struct {
RunStart func(RunInfo)
Phase func(runID, title, detail string)
AgentStart func(runID string, a AgentEvent)
AgentDone func(runID string, a AgentEvent)
Log func(runID, level, msg string)
RunDone func(runID string, result interface{}, err error)
}
FuncSink adapts individual callbacks into an EventSink; unset fields are no-ops. Handy for the CLI and tests.
func (FuncSink) OnAgentDone ¶
func (s FuncSink) OnAgentDone(runID string, a AgentEvent)
func (FuncSink) OnAgentStart ¶
func (s FuncSink) OnAgentStart(runID string, a AgentEvent)
func (FuncSink) OnRunStart ¶
type Loader ¶
type Loader struct {
// contains filtered or unexported fields
}
Loader discovers workflows from builtin (embedded), the user dir (~/.jcode/workflows), and the project dir (<project>/.jcode/workflows). On name collision, project wins over user wins over builtin (mirrors skills.Loader).
func NewLoader ¶
func NewLoader() *Loader
NewLoader creates a Loader pre-populated with builtins and the user dir.
func (*Loader) GetBySlash ¶ added in v0.9.3
GetBySlash returns the workflow whose auto-generated "/<name>" trigger matches slash. Workflows only expose auto-generated slashes (see SlashCommands), so this is a name lookup with the leading "/" stripped.
func (*Loader) LoadProject ¶
LoadProject scans a project's .jcode/workflows dir (project overrides user).
func (*Loader) SaveUser ¶
SaveUser writes a workflow source to the user dir as <name>.js and registers it. Used by "save this run as a command".
func (*Loader) SlashCommands ¶
func (l *Loader) SlashCommands() []SlashCommand
SlashCommands returns a "/name" trigger for each workflow, sorted.
type Meta ¶
type Meta struct {
Name string `json:"name"`
Description string `json:"description"`
WhenToUse string `json:"whenToUse"`
Phases []Phase `json:"phases"`
}
Meta is the exported `meta` block at the top of a workflow script. It must be a pure literal object (no computed values) so it can be parsed without running the body. Name becomes the /<name> slash command.
func ParseMeta ¶
ParseMeta extracts the `meta` object literal from a workflow's source and evaluates it in a throwaway VM (without running the body). The meta block must be a pure literal (no computed values, no host calls) — this is enforced by running it in a bare runtime with no globals injected.
type NopSink ¶
type NopSink struct{}
NopSink is an EventSink that discards everything.
func (NopSink) OnAgentDone ¶
func (NopSink) OnAgentDone(string, AgentEvent)
func (NopSink) OnAgentStart ¶
func (NopSink) OnAgentStart(string, AgentEvent)
func (NopSink) OnRunStart ¶
type Option ¶
type Option func(*Engine)
Option configures an Engine.
func WithConcurrency ¶
WithConcurrency sets the max number of concurrent agents (default 16).
func WithMaxAgents ¶
WithMaxAgents sets the hard per-run agent cap (default 1000).
func WithResolver ¶
WithResolver enables workflow() nesting by resolving a name to a Workflow.
func WithTimeout ¶
WithTimeout sets the per-run wall-clock timeout (default 30m). Zero disables it.
type RunOptions ¶
type RunOptions struct {
RunID string // stable id; generated if empty
Args interface{} // becomes the `args` global (JSON value)
BudgetTotal int64 // token target for budget.total; 0 = unset
}
RunOptions carry per-run inputs.
type Scope ¶
type Scope string
Scope describes where a workflow was loaded from. Project wins over user wins over builtin on name collisions (mirrors skills.Loader precedence).
type SlashCommand ¶
SlashCommand is a "/name" trigger derived from a workflow.
type SpawnFunc ¶
type SpawnFunc func(ctx context.Context, spec AgentSpec) (AgentResult, error)
SpawnFunc runs one subagent to completion and returns its result. The engine is built around this seam so it never imports the tools/model packages directly: the real implementation (spawn.go) reuses jcode's adk agent machinery, and tests inject a deterministic fake. Implementations must honour ctx cancellation.