Documentation
¶
Index ¶
- Constants
- Variables
- func BuildScheduleOptions(instance triggerrepo.TriggerInstance, schedule string, taskQueue string, ...) client.ScheduleOptions
- func CancelTriggerWakeWorkflow(ctx context.Context, temporalEnv *tenv.Environment, instanceID uuid.UUID) error
- func DeleteTriggerCronWorkflowSchedule(ctx context.Context, temporalEnv *tenv.Environment, instanceID uuid.UUID) error
- func ExecuteTriggerDispatchWorkflow(ctx context.Context, temporalEnv *tenv.Environment, ...) error
- func ExecuteTriggerWakeWorkflow(ctx context.Context, temporalEnv *tenv.Environment, instanceID uuid.UUID, ...) error
- func ScheduleTriggerCronWorkflow(ctx context.Context, temporalEnv *tenv.Environment, ...) error
- func TriggerWakeWorkflowID(id uuid.UUID) string
- func ValidateTargetKind(targetKind string) error
- func ValidateWakeFireAt(fireAt, now time.Time) error
- func WakeConfigFields(configJSON []byte) (string, string)
- type App
- func (a *App) CancelAssistantWakes(ctx context.Context, projectID, assistantID uuid.UUID) error
- func (a *App) CancelWakeInstance(ctx context.Context, projectID uuid.UUID, instanceID uuid.UUID, ...) (triggerrepo.TriggerInstance, error)
- func (a *App) Create(ctx context.Context, params CreateParams, hooks ...InstanceDBHook) (triggerrepo.TriggerInstance, error)
- func (a *App) CreateWakeInstance(ctx context.Context, params CreateWakeInstanceParams, hooks ...InstanceDBHook) (triggerrepo.TriggerInstance, error)
- func (a *App) Delete(ctx context.Context, projectID uuid.UUID, id uuid.UUID, ...) error
- func (a *App) Dispatch(ctx context.Context, input Task) error
- func (a *App) GetInstance(ctx context.Context, projectID uuid.UUID, id uuid.UUID) (triggerrepo.TriggerInstance, error)
- func (a *App) IngestDirect(ctx context.Context, instanceID uuid.UUID, payload []byte, ...) (*Task, error)
- func (a *App) ListActiveAssistantWakes(ctx context.Context, projectID uuid.UUID, assistantID uuid.UUID) ([]triggerrepo.TriggerInstance, error)
- func (a *App) ListDefinitions() []Definition
- func (a *App) ListInstances(ctx context.Context, projectID uuid.UUID) ([]triggerrepo.TriggerInstance, error)
- func (a *App) MarkInstanceFired(ctx context.Context, instanceID string) error
- func (a *App) ProcessEvent(ctx context.Context, instance triggerrepo.TriggerInstance, ...) (*Task, error)
- func (a *App) ProcessScheduled(ctx context.Context, input ProcessScheduledInput) (*Task, error)
- func (a *App) ProcessWebhook(ctx context.Context, instanceID uuid.UUID, body []byte, headers http.Header) (*WebhookIngressResult, error)
- func (a *App) RegisterDispatcher(dispatcher Dispatcher)
- func (a *App) SetStatus(ctx context.Context, projectID uuid.UUID, id uuid.UUID, status string, ...) (triggerrepo.TriggerInstance, error)
- func (a *App) Update(ctx context.Context, params UpdateParams, hooks ...InstanceDBHook) (triggerrepo.TriggerInstance, error)
- func (a *App) WebhookURL(instance triggerrepo.TriggerInstance) *string
- type Config
- type CreateParams
- type CreateWakeInstanceParams
- type Definition
- type DeliveryLogger
- type DeliveryStatus
- type Dispatcher
- type EnvRequirement
- type EnvironmentLoader
- type EventEnvelope
- type HMACScheme
- type InstanceDBHook
- type Kind
- type NoopDispatcher
- type ProcessScheduledInput
- type ScheduleTriggerCronWorkflowOptions
- type ScheduleWorkflowInput
- type Task
- type TriggerCronWorkflowInput
- type TriggerDeliveryLog
- type TriggerDispatchWorkflowInput
- type UpdateParams
- type WebhookIngest
- type WebhookIngressResult
- type WebhookResponse
- type WebhookVendor
Constants ¶
const ( TargetKindAssistant = "assistant" TargetKindNoop = "noop" StatusActive = "active" StatusPaused = "paused" StatusFired = "fired" StatusCancelled = "cancelled" DefinitionSlugSlack = "slack" DefinitionSlugLinear = "linear" DefinitionSlugGithub = "github" DefinitionSlugCron = "cron" DefinitionSlugWake = "wake" DefinitionSlugDashboard = "dashboard" )
const MaxWakeHorizon = 30 * 24 * time.Hour
MaxWakeHorizon caps how far in the future a wake trigger may be scheduled.
Variables ¶
var ( ErrBadRequest = errors.New("trigger bad request") ErrAuthFailed = errors.New("trigger webhook authentication failed") )
Functions ¶
func BuildScheduleOptions ¶
func BuildScheduleOptions(instance triggerrepo.TriggerInstance, schedule string, taskQueue string, workflowName string) client.ScheduleOptions
func CancelTriggerWakeWorkflow ¶
func CancelTriggerWakeWorkflow(ctx context.Context, temporalEnv *tenv.Environment, instanceID uuid.UUID) error
CancelTriggerWakeWorkflow: the workflow body honours cancellation by exiting before dispatch. NotFound is swallowed so cancelling an already- fired or never-started wake is a no-op.
func ExecuteTriggerDispatchWorkflow ¶
func ExecuteTriggerDispatchWorkflow(ctx context.Context, temporalEnv *tenv.Environment, input TriggerDispatchWorkflowInput) error
func ExecuteTriggerWakeWorkflow ¶
func ExecuteTriggerWakeWorkflow(ctx context.Context, temporalEnv *tenv.Environment, instanceID uuid.UUID, fireAt time.Time) error
ExecuteTriggerWakeWorkflow is idempotent: the deterministic per-instance workflow ID makes repeated scheduling of the same wake a Temporal-level no-op (WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE).
func ScheduleTriggerCronWorkflow ¶
func ScheduleTriggerCronWorkflow(ctx context.Context, temporalEnv *tenv.Environment, opts ScheduleTriggerCronWorkflowOptions) error
func TriggerWakeWorkflowID ¶
func ValidateTargetKind ¶
func ValidateWakeFireAt ¶
ValidateWakeFireAt enforces the create-time bounds on a wake's fire_at. Pure helper so callers can test bounds without round-tripping a DB.
func WakeConfigFields ¶
WakeConfigFields decodes a wake trigger instance's config_json blob and returns the (correlation_id, fire_at) pair used by audit log call sites. Returns empty strings on decode failure so callers can still record audit events without taking down the workflow.
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
func NewApp ¶
func NewApp( logger *slog.Logger, db *pgxpool.Pool, temporalEnv *tenv.Environment, envLoader EnvironmentLoader, deliveryLogger DeliveryLogger, auditLogger *audit.Logger, serverURL *url.URL, siteURL *url.URL, slackClient *slackclient.SlackClient, dispatchers ...Dispatcher, ) *App
func (*App) CancelAssistantWakes ¶
CancelAssistantWakes is the cascade hook on assistant deletion: errors per instance are folded into the returned error so a partial sweep still cancels what it can.
func (*App) CancelWakeInstance ¶
func (a *App) CancelWakeInstance(ctx context.Context, projectID uuid.UUID, instanceID uuid.UUID, hooks ...InstanceDBHook) (triggerrepo.TriggerInstance, error)
CancelWakeInstance flips an active wake to 'cancelled' and cancels its pending workflow. Idempotent for already-cancelled or already-fired wakes.
func (*App) Create ¶
func (a *App) Create(ctx context.Context, params CreateParams, hooks ...InstanceDBHook) (triggerrepo.TriggerInstance, error)
func (*App) CreateWakeInstance ¶
func (a *App) CreateWakeInstance(ctx context.Context, params CreateWakeInstanceParams, hooks ...InstanceDBHook) (triggerrepo.TriggerInstance, error)
func (*App) GetInstance ¶
func (a *App) GetInstance(ctx context.Context, projectID uuid.UUID, id uuid.UUID) (triggerrepo.TriggerInstance, error)
func (*App) IngestDirect ¶
func (a *App) IngestDirect(ctx context.Context, instanceID uuid.UUID, payload []byte, receivedAt time.Time) (*Task, error)
IngestDirect handles a synchronous, app-invoked trigger event (e.g. a message from the dashboard assistant sidebar) through the same path as webhook and schedule ingress: it builds the event from the instance's direct-ingress definition, runs ProcessEvent (status/filter/target checks + delivery log), and dispatches the resulting task inline. Returns the dispatched task, or nil when the instance is paused or the event was filtered out.
func (*App) ListActiveAssistantWakes ¶
func (a *App) ListActiveAssistantWakes(ctx context.Context, projectID uuid.UUID, assistantID uuid.UUID) ([]triggerrepo.TriggerInstance, error)
func (*App) ListDefinitions ¶
func (a *App) ListDefinitions() []Definition
func (*App) ListInstances ¶
func (a *App) ListInstances(ctx context.Context, projectID uuid.UUID) ([]triggerrepo.TriggerInstance, error)
func (*App) MarkInstanceFired ¶
MarkInstanceFired transitions an active wake instance to 'fired' and records a wake:fired audit log. The UPDATE is guarded on status='active' so a cancel that won the race is preserved (the workflow returns nil; the already-cancelled row keeps its terminal state).
func (*App) ProcessEvent ¶
func (a *App) ProcessEvent(ctx context.Context, instance triggerrepo.TriggerInstance, envelope EventEnvelope) (*Task, error)
func (*App) ProcessScheduled ¶
func (*App) ProcessWebhook ¶
func (*App) RegisterDispatcher ¶
func (a *App) RegisterDispatcher(dispatcher Dispatcher)
func (*App) SetStatus ¶
func (a *App) SetStatus(ctx context.Context, projectID uuid.UUID, id uuid.UUID, status string, hooks ...InstanceDBHook) (triggerrepo.TriggerInstance, error)
func (*App) Update ¶
func (a *App) Update(ctx context.Context, params UpdateParams, hooks ...InstanceDBHook) (triggerrepo.TriggerInstance, error)
func (*App) WebhookURL ¶
func (a *App) WebhookURL(instance triggerrepo.TriggerInstance) *string
type CreateParams ¶
type CreateWakeInstanceParams ¶
type CreateWakeInstanceParams struct {
OrganizationID string
ProjectID uuid.UUID
Name string
AssistantID uuid.UUID
TargetDisplay string
FireAt time.Time
Note *string
CorrelationID string
}
CreateWakeInstanceParams configures a one-shot self-wake for an assistant thread. CorrelationID must match the originating thread's correlation ID so the firing event lands on the same `assistant_threads` row.
type Definition ¶
type Definition struct {
Slug string
Title string
Description string
Kind Kind
ConfigSchema []byte
CompiledConfigSchema *jsonschema.Schema
EnvRequirements []EnvRequirement
EventType reflect.Type
DecodeConfig func(raw map[string]any) (Config, error)
AuthenticateWebhook func(body []byte, headers http.Header, env map[string]string, config Config) error
HandleWebhook func(body []byte, headers http.Header, config Config) (*WebhookIngressResult, error)
BuildScheduledEvent func(instance triggerrepo.TriggerInstance, config Config, firedAt time.Time) (*EventEnvelope, error)
BuildDirectEvent func(instance triggerrepo.TriggerInstance, config Config, payload []byte, receivedAt time.Time) (*EventEnvelope, error)
ExtractSchedule func(config Config) (string, error)
}
func GetDefinition ¶
func GetDefinition(slug string) (Definition, bool)
func List ¶
func List() []Definition
func NewWebhookDefinition ¶
func NewWebhookDefinition( vendor WebhookVendor, schema []byte, compiled *jsonschema.Schema, decodeConfigFn func(raw map[string]any) (Config, error), ) Definition
NewWebhookDefinition builds a Definition from a WebhookVendor, the JSON-schema-described config (built by the vendor via buildInputSchema), and a DecodeConfig closure that decodes raw config, validates event types, and compiles the CEL filter. The shared wiring — HMAC authentication with optional pre-verify bypass, envelope assembly with vendor-supplied event id + correlation id, content-hash event-id fallback — lives here so vendors don't reimplement it.
type DeliveryLogger ¶
type DeliveryLogger interface {
LogTriggerDelivery(triggerrepo.TriggerInstance, EventEnvelope, DeliveryStatus, string, error)
}
func NewTriggerDeliveryLogger ¶
func NewTriggerDeliveryLogger(write func(context.Context, TriggerDeliveryLog)) DeliveryLogger
type DeliveryStatus ¶
type DeliveryStatus string
const ( DeliveryStatusFailed DeliveryStatus = "failed" DeliveryStatusSent DeliveryStatus = "sent" DeliveryStatusSkipped DeliveryStatus = "skipped" )
type EnvRequirement ¶
type EnvironmentLoader ¶
type EventEnvelope ¶
type HMACScheme ¶
type HMACScheme struct {
// NewHash returns a fresh HMAC writer keyed by the signing secret. Nil
// disables signature verification.
NewHash func(key []byte) hash.Hash
// Header names the request header carrying the signature.
Header string
// Encoding renders the computed MAC: "hex" (default) or "base64".
Encoding string
// Prefix is prepended to the encoded MAC before constant-time compare
// (e.g. "sha256=" for GitHub, "v0=" for Slack).
Prefix string
// Template shapes the bytes fed to the MAC. Placeholders: {body}, {timestamp}.
// Defaults to "{body}".
Template string
// TimestampHeader names a request header carrying a unix timestamp
// substituted into Template and bounded for replay protection. Empty
// disables timestamp checking.
TimestampHeader string
// TimestampSkew caps how far the timestamp may drift from now. Defaults
// to 300s when TimestampHeader is set.
TimestampSkew time.Duration
}
HMACScheme describes a vendor's request-signature scheme. It covers the HMAC-based schemes used by Slack, Linear, GitHub, Stripe, and most other webhook senders. NewHash is the constructor (e.g. hmac.New(sha256.New, key)); when nil, authentication is disabled (no signature is required).
type InstanceDBHook ¶
type InstanceDBHook func(ctx context.Context, dbtx pgx.Tx, instance triggerrepo.TriggerInstance) error
InstanceDBHook runs inside the transaction that mutates a trigger instance, after the primary repo write succeeds and before commit. Returning an error rolls the entire mutation back.
type NoopDispatcher ¶
type NoopDispatcher struct {
// contains filtered or unexported fields
}
func NewNoopDispatcher ¶
func NewNoopDispatcher(logger *slog.Logger) *NoopDispatcher
func (*NoopDispatcher) Dispatch ¶
func (d *NoopDispatcher) Dispatch(ctx context.Context, input Task) error
func (*NoopDispatcher) Kind ¶
func (d *NoopDispatcher) Kind() string
type ProcessScheduledInput ¶
type ScheduleWorkflowInput ¶
type TriggerCronWorkflowInput ¶
type TriggerCronWorkflowInput struct {
TriggerInstanceID string `json:"trigger_instance_id"`
}
type TriggerDeliveryLog ¶
type TriggerDeliveryLog struct {
Timestamp time.Time
Instance triggerrepo.TriggerInstance
Attributes map[attr.Key]any
}
type TriggerDispatchWorkflowInput ¶
type TriggerDispatchWorkflowInput struct {
Task Task
}
type UpdateParams ¶
type WebhookIngest ¶
type WebhookIngest struct {
Response *WebhookResponse
Event any
EventID string
CorrelationID string
}
WebhookIngest is the result of a vendor's Ingest step: an optional inline response (e.g. Slack's url_verification challenge echo), the normalized event, and the routing metadata that NewWebhookDefinition folds into the EventEnvelope.
type WebhookIngressResult ¶
type WebhookIngressResult struct {
Response *WebhookResponse
Event *EventEnvelope
Task *Task
}
type WebhookResponse ¶
type WebhookVendor ¶
type WebhookVendor struct {
Slug string
Title string
Description string
EventType reflect.Type
EnvRequirements []EnvRequirement
SecretEnv string
Signature HMACScheme
SupportedEventTypes []string
// PreVerify inspects the request before signature verification. Return
// skipSignature=true to authorize the request without checking the HMAC
// (e.g. Slack's url_verification handshake, which must echo a challenge
// before any signing secret is necessarily configured). Return an error
// to reject. May be nil, in which case signature verification always runs.
PreVerify func(body []byte, headers http.Header) (skipSignature bool, err error)
// Ingest decodes body + headers into a normalized event for downstream
// consumers, the vendor's delivery id (used for dedup; falls back to a
// content hash when empty), and the correlation id that routes the event
// to an assistant conversation. A nil Response with a nil Event acks
// without dispatching (e.g. an unsupported Slack interaction envelope).
Ingest func(body []byte, headers http.Header) (*WebhookIngest, error)
}
WebhookVendor is the per-vendor description a webhook trigger implementation supplies. NewWebhookDefinition assembles the shared Definition wiring (authenticate via HMACScheme, ingest into a normalized EventEnvelope, default-deny event-type filtering) around it, so adding a new webhook source is mostly a matter of describing its signature scheme, env var, event types, and an Ingest function that decodes the body into a typed event plus correlation id.