Files
Agent-System/internal/app/intentflow/query.go
T
weichenandCursor 2e99ee3661 feat(intentflow): route confirmed generate steps through a task adapter
Persist ComboNeedsConfirm plans at confirm tier without executing writes, then run task.run via OwnerMux and a stable idempotency key.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 02:24:23 +00:00

52 lines
1.3 KiB
Go

package intentflow
import "context"
// QueryGuard only authorizes low-risk query actions opened from auto_read.
// Write/generate keys fail closed; OwnerMux routes those to Task.
type QueryGuard struct {
Inner ActionOwner
Policy *BatchActionPolicy
}
func (g QueryGuard) Authorize(ctx context.Context, exec StepExec) error {
policy := g.Policy
if policy == nil {
policy = DefaultBatchActionPolicy()
}
row, err := policy.Lookup(exec.ActionKey)
if err != nil || row.RejectPlan || row.RequiresBatchConfirm {
return ErrPlanActionForbidden
}
if inner, ok := g.Inner.(StepAuthorizer); ok {
return inner.Authorize(ctx, exec)
}
if g.Inner == nil {
return ErrPlanActionForbidden
}
return nil
}
func (g QueryGuard) Execute(ctx context.Context, exec StepExec) (Receipt, error) {
if err := g.Authorize(ctx, exec); err != nil {
return Receipt{}, err
}
if g.Inner == nil {
return Receipt{}, ErrPlanActionForbidden
}
return g.Inner.Execute(ctx, exec)
}
func (g QueryGuard) LookupReceipt(ctx context.Context, idempotencyKey string) (Receipt, bool, error) {
if g.Inner == nil {
return Receipt{}, false, nil
}
return g.Inner.LookupReceipt(ctx, idempotencyKey)
}
func (g QueryGuard) ObserveFence(stepID string, token int64) {
if obs, ok := g.Inner.(FenceObserver); ok {
obs.ObserveFence(stepID, token)
}
}