extensions

package
v0.1.0-rc.1 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: Apache-2.0 Imports: 42 Imported by: 0

Documentation

Overview

Package extensions publishes per-request extension runtime seams: the hook bus, plugin-facing service facades, and narrow views used by the executor without pulling concrete feature plugins into orchestration packages.

Grouped facade ([RequestRuntimeSnapshot])

RequestRuntimeSnapshot is an intentional grouped facade (hexagonal spec task 5.1): one immutable binding per build for hook chains plus the service families extension stages consume together—state and auxiliary requests, traffic observation and raw capture, workspace resolution, session openers, tool catalog filters, request transforms, route hint providers, completion gates, and traffic redactors. Split a consumer onto a narrower interface (for example CompletionGatesView or RequestRuntimeSnapshot.TrafficPortBundle) only when it demonstrably depends on unrelated capabilities; otherwise keep the snapshot to avoid ceremony without coupling reduction.

Composition roots construct snapshots via NewRequestRuntimeSnapshot; the executor attaches them with WithRequestRuntimeSnapshot.

Index

Constants

View Source
const (
	ReasonAttemptTransformInvalidDecision = "attempt_transform_invalid_decision"
	ReasonAttemptTransformExcluded        = "attempt_transform_excluded"
	ReasonAttemptTransformFailure         = "attempt_transform_failure"
)
View Source
const (
	PolicyReasonMalformed       = "policy_malformed"
	PolicyReasonProviderFailure = "policy_provider_failure"
	PolicyReasonTimeout         = "policy_timeout"
	PolicyReasonFailClosed      = "policy_fail_closed"
	PolicyReasonDenied          = "policy_denied"
)

Policy error reason codes used by the conversion helpers. They are bounded safe tokens suitable for evidence and frontend classification.

View Source
const (
	ReasonPreRequestPass            = "prerequest_allow"
	ReasonPreRequestDenied          = "prerequest_denied"
	ReasonPreRequestFailure         = "prerequest_failure"
	ReasonPreRequestMalformed       = "prerequest_malformed"
	ReasonSecretGuardPass           = "secret_guard_pass"
	ReasonSecretGuardLog            = "secret_guard_log"
	ReasonSecretGuardRedacted       = "secret_guard_redacted"
	ReasonSecretGuardBlocked        = "secret_guard_blocked"
	ReasonSecretGuardFailure        = "secret_guard_failure"
	ReasonSecretGuardMalformed      = "secret_guard_malformed"
	ReasonRequestTransformPass      = "request_transform_passthrough"
	ReasonRequestTransformMutated   = "request_transform_mutation"
	ReasonRequestTransformFailure   = "request_transform_failure"
	ReasonRequestTransformMalformed = "request_transform_malformed"
	ReasonToolPolicyAllow           = "tool_policy_allow"
	ReasonToolPolicyDenied          = "tool_policy_denied"
	ReasonToolPolicyFailure         = "tool_policy_failure"
	ReasonToolPolicyMalformed       = "tool_policy_malformed"
	ReasonToolReactorPass           = "tool_reactor_pass"
	ReasonToolReactorRewrite        = "tool_reactor_rewrite"
	ReasonToolReactorReplace        = "tool_reactor_replace"
	ReasonToolReactorSwallow        = "tool_reactor_swallow"
	ReasonCompletionPass            = "completion_pass"
	ReasonCompletionReplace         = "completion_replace"
	ReasonCompletionReplay          = "completion_replay"
	ReasonCompletionReject          = "completion_reject"
	ReasonCompletionFailure         = "completion_failure"
	ReasonCompletionIgnored         = "completion_ignored_post_output"
	ReasonCompletionMalformed       = "completion_malformed"
	ReasonSubmitAnnotated           = "submit_annotated"
	ReasonSubmitRejected            = "submit_rejected"
	ReasonSubmitFailure             = "submit_failure"
	ReasonToolCatalogMutation       = "tool_catalog_mutation"
	ReasonToolCatalogFailure        = "tool_catalog_failure"
	ReasonToolCatalogMalformed      = "tool_catalog_malformed"
	ReasonRouteHintChanged          = "route_hint_changed"
	ReasonRouteHintFailure          = "route_hint_failure"
	ReasonAttemptFailure            = "attempt_failure"
)

Projection reason codes. They are bounded safe tokens ([a-z0-9_.-]) suitable for evidence and frontend classification. Each stage family uses a stable reason code so observers can distinguish projected outcomes without interpreting provider semantics.

View Source
const (
	CategoryAllowed   = policydecision.CategoryAllowed
	CategorySkipped   = policydecision.CategorySkipped
	CategoryDenied    = policydecision.CategoryDenied
	CategoryFailure   = policydecision.CategoryFailure
	CategoryObserved  = policydecision.CategoryObserved
	CategoryMalformed = policydecision.CategoryMalformed
)

Client categories carried on projected records. Only ClientCategory and ClientMessage are intended for frontend use; the values mirror the stable policy error categories so a projected record can be classified the same way as an explicit policy error. The canonical owner is [policydecision.Category*]; these package-level aliases preserve the pre-existing extensions API and keep wire/JSON values unchanged.

View Source
const (
	StageSessionOpen               = feature.StageIDSessionOpen
	StageToolCatalog               = feature.StageIDToolCatalog
	StageCandidateAttemptTransform = feature.StageIDCandidateAttemptTransform
	StageToolEventReaction         = feature.StageIDToolEventReaction
	StageFinalStreamObservation    = feature.StageIDFinalStreamObservation
)

Stage name constants for the legal pipeline (stable ids; align with pkg/lipsdk/feature). Only aliases with non-test production users are kept; tests must use feature.StageID* directly for the remaining stages.

View Source
const (
	MetricsStageSessionOpen               = "session_open"
	MetricsStageWorkspaceResolve          = "workspace_resolve"
	MetricsStageSecretGuard               = "secret_guard"
	MetricsStageToolCatalog               = "tool_catalog"
	MetricsStageRequestTransform          = "request_transform"
	MetricsStagePreRequest                = "pre_request"
	MetricsStageCandidateAttemptTransform = "candidate_attempt_transform"
	MetricsStageFinalStreamObservation    = "final_stream_observation"
	MetricsStageCompactionPreservation    = "compaction_preservation"

	StageOutcomeOK          = "ok"
	StageOutcomeError       = "error"
	StageOutcomeFailOpen    = "fail_open"
	StageOutcomeExcluded    = "excluded"
	StageMetricLabelUnknown = "unknown"
)

Metrics stage labels for observability (distinct constant names from legal pipeline ids in failure_policy.go).

View Source
const ReasonToolReactorFailure = "tool_reactor_failure"

ReasonToolReactorFailure records a tool reactor provider failure projected to shared evidence. Tool reactors run on the stream path after backend attempt is committed, so BackendAttempted is true.

View Source
const ReasonToolReactorMalformed = "tool_reactor_malformed"

ReasonToolReactorMalformed records a tool reactor rewrite/replace whose output failed runner validation. It is distinct from a provider failure: the reactor returned a decision without error, but its replacement event was structurally illegal and the runner rejected/ignored it.

Variables

View Source
var ErrSecretAuditDelivery = errors.New("secret_guard: audit delivery failed")

Functions

func BuildDecisionContext

func BuildDecisionContext(views execctx.Views, stage, providerID string, opts DecisionContextOptions) policydecision.Context

BuildDecisionContext assembles a safe, request-scoped policydecision.Context from the trusted execution views attached to an accepted request, plus stage/provider metadata, output-committed state, and the evaluation timeout budget (requirements 2.1-2.6, 7.5).

Safety properties:

  • Authoritative scope comes from views.Scope (safe-by-construction: no raw credentials, headers, resume tokens, or unvetted claims). It is carried separately from the legacy views.Principal projection (requirement 2.6).
  • Internal/auxiliary origin and parent trace attribution are preserved through views.Scope.Origin and views.Scope.ParentTraceID (requirement 2.4).
  • Unknown optional scope fields are preserved as unknown rather than inferred from client payloads (requirement 2.2).
  • The returned context is defensively cloned: mutating its maps, slices, or embedded scope cannot affect the caller's views (requirement 7.7).

The builder does not synthesize unsafe annotation entries or pull in raw request payloads; only the views' existing annotations are carried through.

func BuildSecretDecisionEvent

func BuildSecretDecisionEvent(
	meta secretguard.Meta,
	call *lipapi.Call,
	guardID string,
	decision secretguard.Decision,
	quarantineResult string,
	backendDispatched bool,
	accessMode string,
	configVersion string,
	turnID string,
	now time.Time,
) secretguard.DecisionEvent

func CanonicalStageMetricLabels

func CanonicalStageMetricLabels(stage, outcome string) (string, string)

CanonicalStageMetricLabels collapses unbounded/sensitive stage or outcome label values to unknown so metric cardinality stays bounded (samples are kept under unknown, not dropped).

func CompletionGateBufferExceeded

func CompletionGateBufferExceeded(limits completion.BufferLimits, n int) bool

CompletionGateBufferExceeded reports whether buffering should fail open to live passthrough (R8).

func CompletionGatesFromContext

func CompletionGatesFromContext(ctx context.Context, fallback CompletionGatesView) []completion.Gate

CompletionGatesFromContext returns completion gates from the request snapshot on ctx when present, otherwise from fallback. Semantics match the former [runtime.retryRecvStream.completionGatesFromContext] resolution order: context snapshot first, then executor snapshot. An empty result is always a non-nil slice so callers that iterate or serialize never see nil.

func EmitSecretGuardAudit

func EmitSecretGuardAudit(ctx context.Context, audit *SecretGuardAudit, meta secretguard.Meta, call *lipapi.Call, guardID string, decision secretguard.Decision, quarantineResult string, backendDispatched bool) error

func FailurePolicyLabel

func FailurePolicyLabel(p FailurePolicy) string

FailurePolicyLabel returns a stable JSON label for inventory/diagnostics.

func IsContextCancellation

func IsContextCancellation(ctx context.Context, err error) bool

IsContextCancellation reports whether err is the parent request context being canceled or expired. The policy error conversion helpers must preserve parent cancellation as cancellation and never convert it into policy denial, failure, or malformed errors (requirement 6.4).

A bare context.DeadlineExceeded is only treated as parent cancellation when the supplied parent context has actually expired, so a child evaluation-deadline error is not misclassified as parent cancellation while the parent remains active.

func LegalPipelineStageNames

func LegalPipelineStageNames() []string

LegalPipelineStageNames returns the core-owned ordered list of legal extension stages (R2). Each call allocates a fresh slice; see feature.LegalPipelineStageIDs.

func LegalStageDescriptors

func LegalStageDescriptors() []feature.StageDescriptor

LegalStageDescriptors returns the SDK canonical stage descriptor table (read-only).

func NewSubmitEvidenceFunc

func NewSubmitEvidenceFunc(ev *DecisionEvidence) hooks.SubmitEvidenceFunc

NewSubmitEvidenceFunc returns a hooks.SubmitEvidenceFunc that projects each submit-hook outcome into shared policy decision evidence and emits it through the seam's emitter (requirements 3.5, 4.6, 9.1, 9.5).

The returned func is intended to be attached to the request context via hooks.WithSubmitEvidence so hooks.Bus.RunSubmit can invoke it per hook without importing the extensions package. A nil seam (or nil emitter) yields a func that emits nothing, preserving the no-observer/non-interference default (requirements 7.6, 10.5).

Submit hooks may mutate the canonical call, but [sdk.SubmitDecision] does not report mutation, so the projector only emits compatible evidence for representable outcomes: reject (deny/none), annotation (allow/annotate), and provider failure (error/none). A no-op hook (no reject, no error, no added annotations) has no representable policy semantics and emits nothing; runtime behavior is still preserved (requirements 9.5, 10.5).

func NewToolReactorEvidenceFunc

func NewToolReactorEvidenceFunc(ev *DecisionEvidence) hooks.ToolReactorEvidenceFunc

NewToolReactorEvidenceFunc returns a hooks.ToolReactorEvidenceFunc that projects each tool-reactor decision into shared policy decision evidence and emits it through the seam's emitter (requirements 3.3, 4.3, 9.1, 9.5).

The returned func is intended to be attached to the request context via hooks.WithToolReactorEvidence so hooks.Bus.ApplyToolReactors can invoke it per reactor without importing the extensions package. A nil seam yields a func that emits nothing.

Frontend-specific tool syntax is kept out of evidence semantics: only the canonical sdkhooks.ToolDecision enum, the reactor's error, and the runner's validation error are projected. Provider failures (err != nil) project as OutcomeError/EffectNone with ReasonToolReactorFailure. Invalid rewrite/replace output (validationErr != nil) projects as OutcomeError/EffectNone with ReasonToolReactorMalformed, so a rejected mutation is never recorded as a successful allow/mutate or allow/replace. The reactor's failure behavior is left to the caller's error policy (the runner preserves existing fail-open/fail-closed behavior; evidence is a side effect).

func PolicyErrorFromFailClosed

func PolicyErrorFromFailClosed(stage, providerID, reasonCode, clientMessage string) error

PolicyErrorFromFailClosed converts an explicit fail-closed outcome into a stable policy denial error (requirements 6.1, 6.6). reasonCode and clientMessage must be client-safe bounded values.

func PolicyErrorFromMalformed

func PolicyErrorFromMalformed(stage, providerID string, cause error) error

PolicyErrorFromMalformed converts a malformed-decision validation error into a stable lipapi.PolicyDecisionError of kind malformed (requirements 1.5, 6.6). cause is preserved for diagnostics only.

func PolicyErrorFromProviderFailure

func PolicyErrorFromProviderFailure(stage, providerID string, behavior policydecision.FailureBehavior, cause error) error

PolicyErrorFromProviderFailure converts a provider failure into a stable policy failure error when the configured failure behavior is fail-closed (requirements 6.1, 6.5). Parent context cancellation is never converted here; the caller must check cancellation first and pass a non-cancellation cause.

func PolicyErrorFromTimeout

func PolicyErrorFromTimeout(stage, providerID string, behavior policydecision.FailureBehavior) error

PolicyErrorFromTimeout converts a provider evaluation timeout into a stable policy failure error when the configured failure behavior is fail-closed (requirements 6.1, 6.3). Fail-open timeouts return nil so the caller records a skipped record instead of surfacing a denial.

func ProjectAttemptObservation

func ProjectAttemptObservation(ctx policydecision.Context, providerID string, err error) (policydecision.Record, bool)

ProjectAttemptObservation projects an attempt-lifecycle observation into a shared policy decision record (requirements 1.7, 4.5, 4.6, 9.1, 9.5). Attempt lifecycle records are observational; ok=false when the attempt succeeded with no error, since a successful observation has no policy semantics to represent. The attempt stage runs after backend attempt is committed, so BackendAttempted is true.

func ProjectCompletionOutcome

func ProjectCompletionOutcome(ctx policydecision.Context, providerID string, outcome completion.Outcome) policydecision.Record

ProjectCompletionOutcome projects a completion-gate outcome into a shared policy decision record (requirements 1.7, 3.4, 4.6, 9.1, 9.5). When output is already committed, a replacement outcome is ignored at runtime (completion_run.go), so the projection records skip/none with a post-output reason rather than fabricating a replace effect. Replay is unaffected by output-committed state. The completion gate runs after backend attempt is committed, so BackendAttempted is true.

func ProjectPreRequestDecision

func ProjectPreRequestDecision(ctx policydecision.Context, providerID string, decision prerequest.Decision) policydecision.Record

ProjectPreRequestDecision projects a pre-request admission handler decision into a shared policy decision record (requirements 1.7, 3.1, 4.6, 9.1, 9.5). The projection is lossy-but-compatible: allow/annotate/deny map directly to the legality table for the pre-request stage. Provider failure, timeout, skip, and no-backend-attempt denial evidence are emitted by Phase 4 runtime integration through the error and timeout helpers; this helper only projects the Decision outcome.

func ProjectRequestTransformResult

func ProjectRequestTransformResult(ctx policydecision.Context, providerID string, mutated bool, err error) policydecision.Record

ProjectRequestTransformResult projects a request-wide transform outcome into a shared policy decision record (requirements 1.7, 3.2, 4.6, 9.1, 9.5). mutated reports whether the transform changed the canonical call; err is the transform's returned error. Timeout and fail-open skip evidence are emitted by Phase 4 runtime integration; this helper projects pass-through, mutation, and provider failure.

func ProjectRouteHintOutcome

func ProjectRouteHintOutcome(ctx policydecision.Context, providerID string, changed bool, err error) (policydecision.Record, bool)

ProjectRouteHintOutcome projects a route-hint provider outcome into a shared policy decision record (requirements 1.7, 4.5, 4.6, 9.1, 9.5). Route hints remain advisory through existing route preference contracts; the policy record does not directly mutate route plans. ok=false when the provider returned no preferred candidates and no error: an empty advisory opinion has no policy semantics to represent.

func ProjectSecretGuardDecision

func ProjectSecretGuardDecision(ctx policydecision.Context, providerID string, decision secretguard.Decision) policydecision.Record

ProjectSecretGuardDecision projects a secret-guard Evaluate decision into a shared policy decision record. Findings, FailureReason, and any secret material must never appear in ClientMessage or ReasonCode (D8/D9). BackendAttempted is always false.

func ProjectSubmitOutcome

func ProjectSubmitOutcome(ctx policydecision.Context, providerID string, rejected bool, annotations map[string]string, err error) (policydecision.Record, bool)

ProjectSubmitOutcome projects a submit-hook outcome into a shared policy decision record (requirements 1.7, 4.6, 9.1, 9.5). The second return value is ok=false when the outcome has no observable policy semantics to represent without inventing new submit semantics: a submit hook that neither rejected, errored, nor annotated did not produce a policy decision the shared vocabulary can describe. Runtime behavior is still preserved; callers simply skip emission when ok is false.

func ProjectToolCatalogOutcome

func ProjectToolCatalogOutcome(ctx policydecision.Context, providerID string, mutated bool, err error) (policydecision.Record, bool)

ProjectToolCatalogOutcome projects a tool-catalog filter outcome into a shared policy decision record (requirements 1.7, 4.6, 9.1, 9.5). ok=false when the filter neither mutated the advertised tool list nor errored: a pure no-op filter has no policy semantics to represent. Advertised-tool mutation behavior is unchanged; the helper only records compatible evidence.

func ProjectToolPolicyDecision

func ProjectToolPolicyDecision(ctx policydecision.Context, providerID string, decision toolpolicy.Decision) policydecision.Record

ProjectToolPolicyDecision projects a canonical tool-call policy decision into a shared policy decision record (requirements 1.7, 3.3, 4.6, 9.1, 9.5). The tool policy stage runs after backend attempt is committed, so BackendAttempted is true.

func ProjectToolReactorDecision

func ProjectToolReactorDecision(ctx policydecision.Context, providerID string, decision sdkhooks.ToolDecision) policydecision.Record

ProjectToolReactorDecision projects a tool reactor outcome into a shared policy decision record (requirements 1.7, 3.3, 4.6, 9.1, 9.5). Frontend-specific tool syntax is kept out of evidence semantics: only the canonical ToolDecision enum is projected. Tool reactor runs after backend attempt is committed, so BackendAttempted is true.

func RecordStageObservation

func RecordStageObservation(obs StageMetrics, stage, outcome string, seconds float64, count, bytes int64)

RecordStageObservation records duration and optional safe counts/bytes with bounded labels. count<=0 and bytes<=0 are ignored so sinks never receive non-positive counter adds.

func RunCompactionPreserverAfterResponseRelease

func RunCompactionPreserverAfterResponseRelease(
	ctx context.Context,
	log *slog.Logger,
	obs StageMetrics,
	preservers []compaction.Preserver,
	ev lipapi.Event,
	meta compaction.PreservationMeta,
	services compaction.Services,
) error

RunCompactionPreserverAfterResponseRelease invokes the optional final release-commit callback in registration order. A deep-isolated canonical event is supplied so the notification cannot mutate the event already committed by the detector and released to the client.

func RunCompactionPreserverBeforeRequest

func RunCompactionPreserverBeforeRequest(
	ctx context.Context,
	log *slog.Logger,
	obs StageMetrics,
	preservers []compaction.Preserver,
	call *lipapi.Call,
	preview compaction.RequestPreview,
	meta compaction.PreservationMeta,
	services compaction.Services,
) error

RunCompactionPreserverBeforeRequest invokes ordered preservation callbacks before the primary request opens. Each callback gets its own bounded clone baseline. Callback errors, panics, and invalid mutations roll back only that callback and are isolated from primary traffic; later preservers continue.

func RunCompactionPreserverBeforeResponseRelease

func RunCompactionPreserverBeforeResponseRelease(
	ctx context.Context,
	log *slog.Logger,
	obs StageMetrics,
	preservers []compaction.Preserver,
	ev *lipapi.Event,
	preview compaction.ResponsePreview,
	meta compaction.PreservationMeta,
	services compaction.Services,
) error

RunCompactionPreserverBeforeResponseRelease invokes ordered final-response preservation callbacks. Each callback is transactional against the exact pre-callback event. A failed, panicking, or invalid callback is rolled back and the committed detector can therefore observe the restored final event.

func RunCompactionPreserverRequestOpenFailed

func RunCompactionPreserverRequestOpenFailed(
	ctx context.Context,
	log *slog.Logger,
	obs StageMetrics,
	preservers []compaction.Preserver,
	meta compaction.PreservationMeta,
	services compaction.Services,
) error

RunCompactionPreserverRequestOpenFailed invokes the optional failed-open lifecycle callback in registration order. Only preservers implementing the additive RequestOpenFailedPreserver interface participate; errors and panics are isolated so primary open/error authority remains in core.

func RunCompactionPreserverRequestOpened

func RunCompactionPreserverRequestOpened(
	ctx context.Context,
	log *slog.Logger,
	obs StageMetrics,
	preservers []compaction.Preserver,
	call lipapi.Call,
	events []compaction.Event,
	meta compaction.PreservationMeta,
	services compaction.Services,
) error

RunCompactionPreserverRequestOpened invokes preservation callbacks after the primary request has opened. The content arguments are deep-copied per callback, so callback-local mutation cannot alter primary traffic or another callback. Errors and panics are feature-local fail-open outcomes.

func RunFinalStreamObservationStage

func RunFinalStreamObservationStage(ctx context.Context, log *slog.Logger, obs StageMetrics, session *FinalStreamObservationSession, ev lipapi.Event, committed bool) error

func RunPreRequestStage

func RunPreRequestStage(ctx context.Context, log *slog.Logger, obs StageMetrics, handlers []prerequest.Handler, call *lipapi.Call, meta prerequest.Meta, svc prerequest.Services) (err error)

RunPreRequestStage runs admission handlers in stable order after request shaping and before route planning.

func RunRequestTransformStage

func RunRequestTransformStage(ctx context.Context, log *slog.Logger, obs StageMetrics, transforms []request.Transform, call *lipapi.Call, meta request.RequestMeta, svc request.Services) (err error)

RunRequestTransformStage runs request-wide transforms in stable order (design §5, §17). Errors from handlers with FailOpen are logged and skipped; FailClosed stops the chain. The call is re-validated after the chain completes.

func RunRouteHintStage

func RunRouteHintStage(ctx context.Context, log *slog.Logger, providers []routehint.Provider, call *lipapi.Call, meta routehint.Input) ([]string, error)

RunRouteHintStage invokes route hint providers with fail-open semantics (design §17 route_hinting).

func RunSessionClassificationStage

func RunSessionClassificationStage(ctx context.Context, log *slog.Logger, classifier sessionclassification.Classifier, in sessionclassification.Input) session.Classification

RunSessionClassificationStage evaluates the optional SDK classifier and returns a validated projection. An existing positive classification is immutable.

func RunSessionOpenStage

func RunSessionOpenStage(ctx context.Context, log *slog.Logger, obs StageMetrics, openers []session.Opener, in session.OpenInput) session.OpenResult

RunSessionOpenStage invokes session openers in registration order with fail-open semantics (design §17 session_open default).

func RunToolCatalogFilterStage

func RunToolCatalogFilterStage(ctx context.Context, log *slog.Logger, obs StageMetrics, filters []toolcatalog.Filter, call *lipapi.Call, meta toolcatalog.CatalogMeta, svc toolcatalog.Services) (err error)

RunToolCatalogFilterStage runs tool catalog filters in stable order (design §4, §17). After the chain, lipapi.ReconcileToolChoiceAfterToolListChange runs once, then the call is validated.

func RunToolPolicyStage

func RunToolPolicyStage(in ToolPolicyStageInput) (err error)

RunToolPolicyStage runs provider-neutral tool-call policies before tool reactors. in.Policies must already be in execution order (as produced by toolpolicy.MaterializeSorted or by RequestRuntimeSnapshot.ToolCallPoliciesExecution); the stage does not re-sort.

func SafeCallReasoningObserveBytes

func SafeCallReasoningObserveBytes(call *lipapi.Call) int64

SafeCallReasoningObserveBytes returns aggregate reasoning payload byte lengths for a call.

func SafeEventObserveBytes

func SafeEventObserveBytes(ev lipapi.Event) int64

SafeEventObserveBytes returns content-safe byte totals for an observed stream event.

func StageDescriptorByID

func StageDescriptorByID(id string) (feature.StageDescriptor, bool)

StageDescriptorByID returns the descriptor for a legal stage id.

func StreamFinished

func StreamFinished(events []lipapi.Event) bool

StreamFinished reports whether the canonical stream reached a terminal completion marker.

func ValidateDecisionRecord

func ValidateDecisionRecord(record policydecision.Record) error

ValidateDecisionRecord is a thin compatibility wrapper that delegates to policydecision.ValidateRecord. It is retained because existing tests and runtime integration call it directly; new callers should use the SDK validator (requirements 1.5, 3.6, 4.4, 6.6).

func ValidateStageID

func ValidateStageID(id string) bool

ValidateStageID reports whether id is a legal pipeline stage.

func WithAttemptEvidence

func WithAttemptEvidence(ctx context.Context, fn AttemptEvidenceFunc) context.Context

WithAttemptEvidence attaches fn to ctx so the runtime attempt-record boundary can emit per-attempt policy decision evidence. A nil fn means no evidence is emitted.

func WithDecisionEvidence

func WithDecisionEvidence(ctx context.Context, ev *DecisionEvidence) context.Context

WithDecisionEvidence attaches ev to ctx so stage runners can project and emit per-provider policy decision evidence. A nil ev means no evidence is emitted.

func WithRequestRuntimeSnapshot

func WithRequestRuntimeSnapshot(ctx context.Context, snap *RequestRuntimeSnapshot) context.Context

WithRequestRuntimeSnapshot attaches snap to ctx for the remainder of the request lifetime. snap must remain valid and unchanged for the lifetime of ctx (see RequestRuntimeSnapshot).

Types

type AttemptEvidenceFunc

type AttemptEvidenceFunc func(ctx context.Context, providerID string, err error)

AttemptEvidenceFunc projects an attempt-lifecycle failure into shared policy decision evidence when attached to the request context. It is invoked at the attempt-record boundary (the narrow runtime seam) for attempt failures that ProjectAttemptObservation can represent. Implementations must not change retry/failover behavior or no-output/failover invariants; evidence emission is a side effect isolated from request execution.

The func is defined in extensions (not a context seam in hooks) because the attempt lifecycle runner lives in internal/core/runtime, which already imports extensions. Carrying it on the context preserves the no-observer/ non-interference default: when no seam is attached, no evidence is emitted (requirements 7.6, 10.5).

A nil fn means no evidence is emitted.

func AttemptEvidenceFromContext

func AttemptEvidenceFromContext(ctx context.Context) AttemptEvidenceFunc

AttemptEvidenceFromContext returns the evidence func attached by WithAttemptEvidence, or nil when none is attached.

func NewAttemptEvidenceFunc

func NewAttemptEvidenceFunc(ev *DecisionEvidence) AttemptEvidenceFunc

NewAttemptEvidenceFunc returns an AttemptEvidenceFunc that projects an attempt-lifecycle failure into shared policy decision evidence and emits it through the seam's emitter (requirements 3.6, 4.5, 7.2, 7.5).

The returned func is intended to be attached to the request context via WithAttemptEvidence by the stream path so the attempt-record boundary can invoke it for attempt failures. A nil seam (or nil emitter) yields a func that emits nothing, preserving the no-observer/non-interference default (requirements 7.6, 10.5).

A successful attempt (err == nil) has no representable policy semantics and emits nothing; runtime behavior is still preserved (requirements 9.5, 10.5). The attempt stage runs after backend attempt is committed, so BackendAttempted is true on projected records.

type AttemptTransformStageResult

type AttemptTransformStageResult struct {
	Excluded   bool
	ReasonCode string
	ProviderID string
}

func RunCandidateAttemptTransformStage

func RunCandidateAttemptTransformStage(
	ctx context.Context,
	log *slog.Logger,
	obs StageMetrics,
	transforms []request.AttemptTransform,
	call *lipapi.Call,
	meta request.AttemptMeta,
	svc request.Services,
) (res AttemptTransformStageResult, err error)

type CompletionGateChainResult

type CompletionGateChainResult struct {
	Events   []lipapi.Event
	Replaced bool // true when OutcomeReplace was applied (not ignored after output commitment)
}

CompletionGateChainResult is the outcome of running completion gates over a buffered stream.

func ApplyCompletionGateChain

func ApplyCompletionGateChain(ctx context.Context, gates []completion.Gate, meta completion.Meta, original []lipapi.Event, outputCommitted bool, svc completion.Services, log *slog.Logger) (CompletionGateChainResult, error)

ApplyCompletionGateChain runs sorted gates over the buffered completion (design §6, §17). When outputCommitted is true, replacement outcomes are ignored (original buffer preserved). Handler errors honor per-gate FailureMode; the stage default is fail-open (see DefaultFailurePolicyForStage). log may be nil; when set, completion-gate panics are logged via [logFailOpenExtensionPanic] before they are returned to the runtime stream boundary (panics are not swallowed by fail-open policy).

type CompletionGatesView

type CompletionGatesView interface {
	CompletionGates() []completion.Gate
}

CompletionGatesView is the narrow extension seam for completion-gate discovery (hexagonal task 5.1). Callers that only need gates should depend on this interface rather than the full snapshot.

type DecisionContextOptions

type DecisionContextOptions struct {
	// OutputCommitted records whether client-visible output has already been committed for
	// this request (completion/stream-stage decisions). False for pre-backend stages.
	OutputCommitted bool
	// EvaluationTimeout is the configured decision-provider evaluation budget for the
	// target stage/provider. Zero means no new timeout is applied (legacy behavior).
	EvaluationTimeout time.Duration
	// EvaluationDeadline is the derived evaluation deadline (now + EvaluationTimeout). Zero
	// means no deadline is applied.
	EvaluationDeadline time.Time
}

DecisionContextOptions carries the non-view inputs needed to assemble a policydecision.Context: lifecycle state and the evaluation timeout budget for the target stage/provider (requirements 2.1, 6.3). All fields are optional; zero values preserve legacy/local-anonymous semantics.

type DecisionEvidence

type DecisionEvidence struct {
	Emitter       *EvidenceEmitter
	Views         execctx.Views
	TimeoutBudget TimeoutBudgetSource
	TimeoutGuard  *ProviderTimeoutGuard
	// OutputCommittedSource, when non-nil, returns the current client-visible
	// output-committed state for stream-stage evidence. It is consulted by
	// decisionContextFor only to escalate a caller-supplied false to true (never
	// to downgrade an explicit true), so callers that already know the
	// authoritative committed state (e.g. the completion gate) are unaffected.
	//
	// The func is invoked fresh on each evidence emission; runtime context caching
	// must not snapshot its result, so a stream whose commitment state changes
	// mid-flight is never recorded with a stale bool. nil preserves pre-backend
	// behavior (OutputCommitted stays false).
	OutputCommittedSource func() bool
}

DecisionEvidence is the request-scoped policy decision evidence seam carried through the context for stage runners (requirements 3.1-3.6, 4.1-4.4, 7.6). It binds the evidence emitter to the safe execution views and timeout budget source the runners use to build policydecision.Context per provider.

Stage runners look this up via DecisionEvidenceFromContext. When absent (or when Emitter is nil), runners emit no evidence, preserving the no-observer/non-interference default (requirements 9.1, 10.5).

Views carries the authoritative safe attribution used to build decision contexts. For pre-backend stages the runtime attaches a pre-backend views snapshot here (execctx views are not yet on the context). For stream stages the runtime may leave Views zero; the helper prefers execctx views already attached to the context when present.

func DecisionEvidenceFromContext

func DecisionEvidenceFromContext(ctx context.Context) *DecisionEvidence

DecisionEvidenceFromContext returns the evidence seam attached by WithDecisionEvidence, or nil when none is attached.

func (*DecisionEvidence) WithViews

func (ev *DecisionEvidence) WithViews(views execctx.Views) *DecisionEvidence

WithViews returns a copy of ev with Views replaced by views. The Emitter, TimeoutBudget and OutputCommittedSource are shared so callers can refresh the safe attribution snapshot between phases (e.g. submit vs post-submit pre-backend stages) without rebuilding the seam or re-resolving the emitter. Returns nil when ev is nil so callers can chain on an absent seam.

type DefaultTimeoutBudgetSource

type DefaultTimeoutBudgetSource struct{}

DefaultTimeoutBudgetSource returns a zero budget for every stage/provider, preserving legacy extension behavior (requirement 6.3, 10.5).

func (DefaultTimeoutBudgetSource) TimeoutFor

TimeoutFor returns zero for every stage/provider.

type EvidenceEmitter

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

EvidenceEmitter delivers bounded, normalized policy decision records to the configured policy observer and structured logs (requirements 7.1, 7.3, 7.4, 7.6, 7.7). Observer failures and log failures are isolated from request execution in this spec: a misbehaving observer or sink cannot change runtime outcomes.

Privileged-visibility gating: records marked EvidencePrivileged are withheld from the observer and structured logs unless DiagnosticsEnabled is true. Default (EvidenceDefault) records are always eligible for emission after normalization.

High-cardinality values such as trace and leg identifiers may be structured log attributes but must not become metric labels (this emitter does not emit metrics).

func NewEvidenceEmitter

func NewEvidenceEmitter(obs policydecision.Observer, logger *slog.Logger, diagnosticsEnabled bool) *EvidenceEmitter

NewEvidenceEmitter returns an emitter that delivers records to obs (defaulting to policydecision.NoopObserver when nil) and to logger when non-nil. diagnosticsEnabled controls whether privileged-visibility records may leave the core (requirement 7.4).

func (*EvidenceEmitter) DiagnosticsEnabled

func (e *EvidenceEmitter) DiagnosticsEnabled() bool

DiagnosticsEnabled reports whether privileged-visibility records may leave the core through this emitter.

func (*EvidenceEmitter) Emit

func (e *EvidenceEmitter) Emit(ctx context.Context, record policydecision.Record)

Emit normalizes record and delivers it to the observer and structured logs. Privileged records are withheld unless diagnostics is enabled. Illegal records (unknown stage/outcome/effect or illegal outcome/effect pair) are dropped before normalization and delivery; the drop is logged at warn level with bounded fields and never changes request execution (requirements 1.5, 6.6, 7.3, 7.6, 7.7). The drop log is a distinct sink call, not a recursive Emit, so a malformed record cannot trigger further emission. Observer and log failures are ignored so request execution is unaffected. ctx is passed to the observer for its own lifecycle; ctx is never stored.

func (*EvidenceEmitter) Observer

func (e *EvidenceEmitter) Observer() policydecision.Observer

Observer returns the bound policy observer (for composition roots that want to fan out additional observers).

type FailurePolicy

type FailurePolicy uint8

FailurePolicy describes default runner behavior when a stage handler errors (design section 17).

const (
	// FailurePolicyUnset means the stage id is not in the legal pipeline.
	FailurePolicyUnset FailurePolicy = iota
	FailurePolicyFailOpen
	FailurePolicyFailClosed
)

func DefaultFailurePolicyForStage

func DefaultFailurePolicyForStage(stage string) FailurePolicy

DefaultFailurePolicyForStage returns the documented default for the stage (design section 17).

type FinalStreamObservationSession

type FinalStreamObservationSession struct {
	Log     *slog.Logger
	Metrics StageMetrics
	// contains filtered or unexported fields
}

func (*FinalStreamObservationSession) Finish

func (s *FinalStreamObservationSession) Finish(parentCtx context.Context, outcome response.StreamOutcome)

func (*FinalStreamObservationSession) Open

type ProviderTimeoutGuard

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

ProviderTimeoutGuard bounds in-process providers that ignore context cancellation. It permits at most one bounded goroutine per stage/provider in a runtime snapshot, so an uncooperative provider cannot accumulate one leaked goroutine per request.

func NewProviderTimeoutGuard

func NewProviderTimeoutGuard() *ProviderTimeoutGuard

NewProviderTimeoutGuard returns an empty guard for one runtime snapshot.

func (*ProviderTimeoutGuard) TryEnter

func (g *ProviderTimeoutGuard) TryEnter(stage, providerID string) bool

TryEnter reserves a bounded call slot for stage/provider. A nil guard permits the call. A false result means a bounded goroutine for the same stage/provider is already running.

type RequestRuntimeSnapshot

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

RequestRuntimeSnapshot is a per-build binding of hook chains and service facades published onto each request context (design §15B, task 4.2). Many request goroutines may read the same pointer without synchronization; callers must treat it as frozen after construction: do not replace fields, mutate the embedded *hooks.Bus, or swap facade implementations. Config reload or rebinding must publish a new snapshot (new RequestRuntimeSnapshot value and new executor wiring from github.com/matdev83/go-llm-interactive-proxy/internal/infra/runtimebundle.Build).

func NewRequestRuntimeSnapshot

func NewRequestRuntimeSnapshot(bus *hooks.Bus, opts SnapshotOptions) *RequestRuntimeSnapshot

NewRequestRuntimeSnapshot captures bus and facades for the lifetime of the returned value. bus must be non-nil (or replaced with hooks.New empty bus). The same *hooks.Bus must not be mutated after this call if the snapshot is shared across concurrent requests.

func RequestRuntimeSnapshotFromContext

func RequestRuntimeSnapshotFromContext(ctx context.Context) *RequestRuntimeSnapshot

RequestRuntimeSnapshotFromContext returns the snapshot from WithRequestRuntimeSnapshot, if any.

func (*RequestRuntimeSnapshot) AttemptTransforms

func (s *RequestRuntimeSnapshot) AttemptTransforms() []request.AttemptTransform

AttemptTransforms returns a defensive copy of frozen candidate attempt transforms (may be empty).

func (*RequestRuntimeSnapshot) Aux

Aux returns the auxiliary request client for this snapshot.

func (*RequestRuntimeSnapshot) CompactionObservers

func (s *RequestRuntimeSnapshot) CompactionObservers() []compaction.Observer

CompactionObservers returns a defensive copy of the frozen compaction observer slice (may be empty). Mutating the returned slice does not affect the snapshot; the snapshot's internal backing slice must never be mutated.

func (*RequestRuntimeSnapshot) CompactionPreservers

func (s *RequestRuntimeSnapshot) CompactionPreservers() []compaction.Preserver

CompactionPreservers returns a defensive copy of the frozen content-bearing preservation callback slice. Mutating the returned slice does not affect the snapshot.

func (*RequestRuntimeSnapshot) CompletionGates

func (s *RequestRuntimeSnapshot) CompletionGates() []completion.Gate

CompletionGates returns a defensive copy of frozen completion gates (may be empty).

func (*RequestRuntimeSnapshot) Generation

func (s *RequestRuntimeSnapshot) Generation() int64

Generation is an opaque build stamp (e.g. config reload generation in a future spec).

func (*RequestRuntimeSnapshot) HookBus

func (s *RequestRuntimeSnapshot) HookBus() *hooks.Bus

HookBus returns the hook bus bound at snapshot construction (brownfield compatibility).

func (*RequestRuntimeSnapshot) LocalTurnHandlers

func (s *RequestRuntimeSnapshot) LocalTurnHandlers() []localturn.Handler

LocalTurnHandlers returns a defensive copy of frozen local-turn handlers (may be empty). Mutating the returned slice does not affect the snapshot.

func (*RequestRuntimeSnapshot) LocalTurnHandlersExecution

func (s *RequestRuntimeSnapshot) LocalTurnHandlersExecution() []localturn.Handler

LocalTurnHandlersExecution returns the frozen local-turn handler slice in execution order (sorted by Order then ID). The returned slice must not be mutated.

func (*RequestRuntimeSnapshot) PolicyObserver

func (s *RequestRuntimeSnapshot) PolicyObserver() policydecision.Observer

PolicyObserver returns the frozen policy decision observer bound at snapshot construction (requirements 7.6, 10.5). A nil snapshot returns the disabled no-op default so callers can always invoke the returned observer without nil checks. The returned observer is an interface value; it is treated as frozen for the lifetime of the snapshot.

func (*RequestRuntimeSnapshot) PreRequestHandlers

func (s *RequestRuntimeSnapshot) PreRequestHandlers() []prerequest.Handler

PreRequestHandlers returns a defensive copy of frozen pre-request admission handlers (may be empty).

func (*RequestRuntimeSnapshot) ProviderTimeoutGuard

func (s *RequestRuntimeSnapshot) ProviderTimeoutGuard() *ProviderTimeoutGuard

ProviderTimeoutGuard returns the snapshot-scoped guard used to contain uncooperative bounded providers. It is shared across requests for this immutable snapshot so one stuck stage/provider cannot accumulate one goroutine per request.

func (*RequestRuntimeSnapshot) RawCapture

RawCapture returns the privileged raw capture sink for this snapshot.

func (*RequestRuntimeSnapshot) RequestTransforms

func (s *RequestRuntimeSnapshot) RequestTransforms() []request.Transform

RequestTransforms returns a defensive copy of frozen request-wide transforms (may be empty).

func (*RequestRuntimeSnapshot) RouteHintProviders

func (s *RequestRuntimeSnapshot) RouteHintProviders() []routehint.Provider

RouteHintProviders returns a defensive copy of frozen route hint providers (may be empty).

func (*RequestRuntimeSnapshot) SecretGuardExecutionPlane

func (s *RequestRuntimeSnapshot) SecretGuardExecutionPlane() SecretGuardPlane

SecretGuardExecutionPlane returns the frozen secret-guard plane without cloning the guard slice. MatcherResolver, DecisionObserver, and policy/config fields are safe to read; Guards is the snapshot's internal backing store in MaterializeSorted order and must not be mutated.

func (*RequestRuntimeSnapshot) SecretGuardPlane

func (s *RequestRuntimeSnapshot) SecretGuardPlane() SecretGuardPlane

SecretGuardPlane returns a defensive copy of the SecretGuardPlane configuration (guards slice cloned). Prefer RequestRuntimeSnapshot.SecretGuardExecutionPlane for the runtime executor hot path.

func (*RequestRuntimeSnapshot) SecretGuards

func (s *RequestRuntimeSnapshot) SecretGuards() []secretguard.Guard

SecretGuards returns a defensive copy of the frozen secret guard slice.

func (*RequestRuntimeSnapshot) SecretGuardsExecution

func (s *RequestRuntimeSnapshot) SecretGuardsExecution() []secretguard.Guard

SecretGuardsExecution returns the frozen secret guard slice without cloning.

func (*RequestRuntimeSnapshot) SessionOpeners

func (s *RequestRuntimeSnapshot) SessionOpeners() []session.Opener

SessionOpeners returns a defensive copy of the frozen session-open stage handlers (may be empty). Mutating the returned slice does not affect the snapshot.

func (*RequestRuntimeSnapshot) State

func (s *RequestRuntimeSnapshot) State() state.Store

State returns the plugin state facade for this snapshot.

func (*RequestRuntimeSnapshot) StreamObserverFactories

func (s *RequestRuntimeSnapshot) StreamObserverFactories() []response.StreamObserverFactory

StreamObserverFactories returns a defensive copy of frozen stream observer factories (may be empty).

func (*RequestRuntimeSnapshot) TerminalDecisionProvider

func (s *RequestRuntimeSnapshot) TerminalDecisionProvider() terminaldecision.Provider

TerminalDecisionProvider returns the generation provider captured by this immutable request snapshot, or nil when no provider is active.

func (*RequestRuntimeSnapshot) TerminalDecisionProviderIdentity

func (s *RequestRuntimeSnapshot) TerminalDecisionProviderIdentity() (string, bool)

TerminalDecisionProviderIdentity returns the frozen identity of the generation provider captured by this immutable request snapshot, if present.

func (*RequestRuntimeSnapshot) TimeoutBudgetSource

func (s *RequestRuntimeSnapshot) TimeoutBudgetSource() TimeoutBudgetSource

TimeoutBudgetSource returns the frozen per-request decision-provider timeout budget source bound at snapshot construction (requirements 6.3, 10.5). A nil snapshot returns the default zero-budget source so callers can always invoke TimeoutFor without nil checks.

func (*RequestRuntimeSnapshot) ToolCallFinalizers

func (s *RequestRuntimeSnapshot) ToolCallFinalizers() []toolcall.Finalizer

func (*RequestRuntimeSnapshot) ToolCallFinalizersExecution

func (s *RequestRuntimeSnapshot) ToolCallFinalizersExecution() []toolcall.Finalizer

func (*RequestRuntimeSnapshot) ToolCallPolicies

func (s *RequestRuntimeSnapshot) ToolCallPolicies() []toolpolicy.Policy

ToolCallPolicies returns a defensive copy of frozen tool-call policies (may be empty). Mutating the returned slice does not affect the snapshot.

func (*RequestRuntimeSnapshot) ToolCallPoliciesExecution

func (s *RequestRuntimeSnapshot) ToolCallPoliciesExecution() []toolpolicy.Policy

ToolCallPoliciesExecution returns the frozen tool-call policy slice in execution order (the same ordering as toolpolicy.MaterializeSorted). The returned slice must not be mutated; it is the snapshot's internal backing store. Prefer RequestRuntimeSnapshot.ToolCallPolicies for a defensive copy; this accessor exists for the runtime executor hot path.

func (*RequestRuntimeSnapshot) ToolCatalogFilters

func (s *RequestRuntimeSnapshot) ToolCatalogFilters() []toolcatalog.Filter

ToolCatalogFilters returns a defensive copy of frozen catalog filters (may be empty).

func (*RequestRuntimeSnapshot) TrafficObserver

func (s *RequestRuntimeSnapshot) TrafficObserver() traffic.Observer

TrafficObserver returns the structured traffic observer for this snapshot.

func (*RequestRuntimeSnapshot) TrafficPortBundle

func (s *RequestRuntimeSnapshot) TrafficPortBundle() traffic.PortBundle

TrafficPortBundle returns the frozen traffic emission triple for this snapshot (hexagonal task 5.1). Prefer this method over ad-hoc field access when only traffic observation ports are needed.

func (*RequestRuntimeSnapshot) TrafficRedactors

func (s *RequestRuntimeSnapshot) TrafficRedactors() []traffic.Redactor

TrafficRedactors returns a defensive copy of frozen redactors for the traffic pipeline (may be empty).

func (*RequestRuntimeSnapshot) UsageObserver

func (s *RequestRuntimeSnapshot) UsageObserver() usage.Observer

UsageObserver returns the usage observer for this snapshot.

func (*RequestRuntimeSnapshot) Workspace

func (s *RequestRuntimeSnapshot) Workspace() workspace.Resolver

Workspace returns the workspace resolver for this snapshot.

type SecretGuardAudit

type SecretGuardAudit struct {
	Observer      secretguard.Observer
	AccessMode    string
	ConfigVersion string
	TurnID        string
	Now           func() time.Time
}

type SecretGuardBlockInfo

type SecretGuardBlockInfo struct {
	GuardID  string
	Decision secretguard.Decision
}

func RunSecretGuardStage

func RunSecretGuardStage(ctx context.Context, log *slog.Logger, obs StageMetrics, guards []secretguard.Guard, call *lipapi.Call, meta secretguard.Meta, svc secretguard.Services, audit *SecretGuardAudit, decisionMetrics SecretGuardDecisionMetrics) (block *SecretGuardBlockInfo, err error)

RunSecretGuardStage evaluates secret guards in caller-provided execution order. guards must already be sorted as produced by secretguard.MaterializeSorted or RequestRuntimeSnapshot.SecretGuardExecutionPlane.

func (*SecretGuardBlockInfo) DenialError

func (b *SecretGuardBlockInfo) DenialError() error

type SecretGuardDecisionMetrics

type SecretGuardDecisionMetrics interface {
	IncDecision(action, outcome, sourceCategory string)
	IncMatch(action, outcome, sourceCategory string)
	IncQuarantine(action, outcome, sourceCategory string)
	IncFailure(action, outcome, sourceCategory string)
	IncScanLimit(action, outcome, sourceCategory string)
}

type SecretGuardPlane

type SecretGuardPlane struct {
	Guards             []secretguard.Guard
	MatcherResolver    secretguard.MatcherResolver
	DecisionObserver   secretguard.Observer
	AuditFailurePolicy secretguard.AuditFailurePolicy
	AccessMode         string
	ConfigVersion      string
}

SecretGuardPlane is the frozen secret-guard composition bound into a request snapshot.

type SnapshotOptions

type SnapshotOptions struct {
	State            state.Store
	Aux              auxiliary.Client
	TrafficObserver  traffic.Observer
	UsageObserver    usage.Observer
	RawCapture       traffic.RawCaptureSink
	Workspace        workspace.Resolver
	SecretGuardPlane SecretGuardPlane
	// PolicyObserver receives normalized policy decision evidence. Nil defaults to a
	// disabled no-op observer so deployments without policy evidence keep current request
	// outcomes (requirements 7.6, 10.5).
	PolicyObserver policydecision.Observer
	// TimeoutBudgetSource is the frozen per-request source of decision-provider evaluation
	// budgets. Nil defaults to [DefaultTimeoutBudgetSource] (zero budget for every
	// stage/provider) so legacy extension behavior is unchanged (requirements 6.3, 10.5).
	TimeoutBudgetSource TimeoutBudgetSource
	FeaturePlanes       lipfeature.FrozenPlaneSet
	Generation          int64
}

SnapshotOptions configures optional facades; zero value uses disabled placeholders.

type StageCountByteMetrics

type StageCountByteMetrics interface {
	AddStageCount(stage, outcome string, n int64)
	ObserveStageBytes(stage, outcome string, n int64)
}

StageCountByteMetrics is an optional StageMetrics extension for content-safe counts/bytes.

type StageMetrics

type StageMetrics interface {
	ObserveStage(stage, outcome string, seconds float64)
	IncFailOpenSkip(stage string)
}

StageMetrics receives extension pipeline timing and fail-open skip counts (optional on [Executor]).

type StaticTimeoutBudgetSource

type StaticTimeoutBudgetSource struct {
	Budget time.Duration
}

StaticTimeoutBudgetSource returns a fixed budget for every stage/provider. It is intended for tests and explicit configuration where one budget applies uniformly.

func (StaticTimeoutBudgetSource) TimeoutFor

TimeoutFor returns the configured budget for every stage/provider.

type TimeoutBudgetSource

type TimeoutBudgetSource interface {
	TimeoutFor(stage, providerID string) time.Duration
}

TimeoutBudgetSource is a frozen per-request source of evaluation timeouts for decision providers (requirement 6.3). The default source returns zero for every stage/provider so legacy extension behavior remains source- and behavior-compatible unless a configured provider/stage explicitly sets a budget. Implementations must be safe for concurrent reads; composition roots must treat a source as frozen for the lifetime of a request snapshot.

type TimeoutResult

type TimeoutResult[T any] struct {
	Value          T
	Err            error
	TimedOut       bool
	ParentCanceled bool
	// ProviderStillRunning reports that the helper returned because the parent or
	// evaluation deadline fired while the provider goroutine had not completed.
	ProviderStillRunning bool
	// GuardRejected reports that a ProviderTimeoutGuard rejected the call because
	// another bounded goroutine for this stage/provider is already running.
	GuardRejected bool
}

TimeoutResult carries the outcome of a bounded decision-provider call (requirements 6.1, 6.2, 6.3, 6.4). Exactly one of Value/Err is meaningful when TimedOut and ParentCanceled are both false.

func RunDecisionProviderWithDeadline

func RunDecisionProviderWithDeadline[T any](ctx context.Context, deadline time.Time, call func(context.Context) (T, error)) TimeoutResult[T]

RunDecisionProviderWithDeadline invokes call with a child context that expires at the earlier of the parent deadline and the supplied deadline. It is the single source of truth for the bounding deadline: the same deadline value is used both to bound the provider (via context.WithDeadline) and, threaded explicitly through the bounded result by stage runners, to populate policydecision.Context.EvaluationDeadline on emitted evidence (requirement 6.3).

deadline must be non-zero; callers derive it once (e.g. now+timeout) and reuse the same time.Time for both bounding and evidence projection so the provider's observed ctx.Deadline() equals the record's EvaluationDeadline exactly.

When timeout is greater than zero, the helper runs call in a bounded goroutine against the derived child context and returns as soon as the call returns, the evaluation deadline expires, or the parent context is canceled. If the parent context is canceled or expires first, ParentCanceled is true and the original context error is returned in Err; no policy-denial evidence is implied. If the evaluation deadline expires while the parent is still active, TimedOut is true and Err is nil; the caller records a policy failure or fail-open skipped record according to the provider's configured failure behavior. Late provider results after timeout are ignored and never mutate live call or stream state.

func RunDecisionProviderWithDeadlineGuarded

func RunDecisionProviderWithDeadlineGuarded[T any](ctx context.Context, deadline time.Time, guard *ProviderTimeoutGuard, stage, providerID string, call func(context.Context) (T, error)) TimeoutResult[T]

RunDecisionProviderWithDeadlineGuarded behaves like RunDecisionProviderWithDeadline and also tracks provider goroutines that are still running after the helper returns. While a tracked goroutine remains active, the guard rejects future launches for the same stage/provider.

func RunDecisionProviderWithTimeout

func RunDecisionProviderWithTimeout[T any](ctx context.Context, timeout time.Duration, call func(context.Context) (T, error)) TimeoutResult[T]

RunDecisionProviderWithTimeout invokes call with a derived child context that expires at the earlier of the parent deadline and now+timeout (requirements 6.1, 6.2, 6.3, 6.4). The derived deadline is now+timeout.

Legacy compatibility: when timeout is zero, no child context and no goroutine are created; call runs directly against ctx and the result is returned unwrapped. This preserves existing extension behavior and avoids the per-call goroutine wrapper (design Timeout Enforcement).

Stage runners that need the same deadline value projected onto emitted evidence should derive the deadline once and call RunDecisionProviderWithDeadline instead, so the bounding deadline and the evidence EvaluationDeadline are exactly identical.

type ToolPolicyStageInput

type ToolPolicyStageInput struct {
	Ctx      context.Context
	Log      *slog.Logger
	Obs      StageMetrics
	Policies []toolpolicy.Policy // execution order; see RunToolPolicyStage
	Event    lipapi.ToolEvent
	Meta     toolpolicy.Meta
	Svc      toolpolicy.Services
}

ToolPolicyStageInput carries inputs for RunToolPolicyStage.

Jump to

Keyboard shortcuts

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