prompt

package
v1.113.1 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package prompt provides prompt management for the MCP data platform. It defines the Store interface for prompt persistence and the Prompt type that represents a user-managed MCP prompt.

Index

Constants

View Source
const (
	ScopeGlobal   = "global"
	ScopePersona  = "persona"
	ScopePersonal = "personal"
)

Scope constants define prompt visibility levels.

View Source
const (
	SourceOperator = "operator"
	SourceAgent    = "agent"
	SourceSystem   = "system"
)

Source constants define prompt origins.

View Source
const (
	StatusDraft      = "draft"
	StatusApproved   = "approved"
	StatusDeprecated = "deprecated"
	StatusSuperseded = "superseded"
)

Status constants define the prompt promotion lifecycle. A prompt starts as draft, becomes approved (on admin promotion to persona/global scope), may later be deprecated, and is finally superseded by a replacement.

View Source
const (
	VersionStatusDraft      = "draft"
	VersionStatusApplied    = "applied"
	VersionStatusSuperseded = "superseded"
	VersionStatusRejected   = "rejected"
)

Version status constants. A version row is the immutable snapshot of a prompt's reviewable substance at one mutation:

  • draft: a proposed edit to an approved shared prompt, awaiting admin approval; the live prompt row continues to serve the previously applied snapshot.
  • applied: the snapshot was applied to the live row (either directly, for edits that need no review, or via ApproveVersion). The version the live row currently serves is Prompt.Version; older applied rows are history.
  • superseded: a draft that was still pending when a different draft was approved; kept for history, never applicable.
  • rejected: a draft an admin explicitly rejected.
View Source
const (
	DefaultSearchLimit = 20
)

Search result limits. DefaultSearchLimit is the top-K returned when the caller does not specify one; maxSearchLimit bounds an explicit request so a single ranked query cannot ask for an unbounded result set.

Variables

View Source
var ErrAttachmentNotFound = errors.New("attachment not found")

ErrAttachmentNotFound reports that no attachment links the prompt and resource named in the request.

View Source
var ErrAttachmentScope = errors.New("attachment scope violation")

ErrAttachmentScope marks every rejection from the attachment scope rule, so handlers can map it to a conflict carrying the author-facing message rather than to a generic failure. Every error CheckAttachScope, CheckAttachOwnership, and CheckPromotionAttachments return wraps it.

View Source
var ErrCollectionExists = errors.New("a collection with that name already exists")

ErrCollectionExists rejects a create or rename that collides with an existing collection name (names are an org-wide vocabulary, unique case-insensitively). REST handlers map it to 409.

View Source
var ErrCollectionNotFound = errors.New("collection not found")

ErrCollectionNotFound rejects an assignment to a collection that does not exist (e.g. deleted between listing and assigning). REST handlers map it to 404.

View Source
var ErrReviewRequiredMixedEdit = errors.New(
	"content changes to an approved shared prompt require review and cannot be combined with " +
		"scope, status, or other non-versioned changes; submit them as separate updates")

ErrReviewRequiredMixedEdit rejects an edit that combines a review-gated substance change (content or arguments of an approved shared prompt) with changes a draft version cannot carry (scope, personas, status, name, category, owner, enabled, promotion request). The two must be submitted separately so the deferred draft is exactly the reviewable snapshot.

View Source
var ErrVersionConflict = errors.New("prompt version conflict")

ErrVersionConflict marks a version write rejected because the prompt or version state changed underneath the caller (a draft already resolved, a prompt retired, an edit racing an approval). REST handlers map it to 409; any other store error is an internal failure.

Functions

func CheckAttachOwnership added in v1.111.0

func CheckAttachOwnership(callerSub, callerEmail, promptOwnerEmail string, res AttachmentScope) error

CheckAttachOwnership reports whether the caller may attach a user-scoped resource, which is allowed only when the resource lives in the caller's own user scope and the prompt is the caller's own personal prompt. It is separate from CheckAttachScope because it needs the caller's identity, which the scope rule alone does not.

An admin is deliberately not exempt: attaching another user's private resource would build an SOP whose materials only that user can read.

A user-scoped resource identifies its owner by subject or by email, because an admin can scope a resource to a user by email address before that user has ever signed in (see resource.VisibleScopes). Both forms are accepted here for the same reason they are accepted there: rejecting the email form would block an author from attaching a resource they demonstrably can read.

func CheckAttachScope added in v1.111.0

func CheckAttachScope(promptScope string, promptPersonas []string, res AttachmentScope) error

CheckAttachScope reports whether a resource may be attached to a prompt of the given scope, and returns a caller-facing error naming the resource when it may not.

The rule is that an attachment must be at least as widely visible as the prompt that carries it. A prompt is served to everyone its scope reaches, so a narrower attachment would produce a shared SOP whose materials are missing for most of its audience: the failure would surface at serve time, to a reader who cannot fix it, rather than at authoring time to the one person who can.

resource global    -> any prompt
resource persona P -> personal prompts, and persona prompts scoped to
                      exactly P (a prompt serving several personas would
                      reach readers outside P)
resource user U     -> personal prompts only, and only the attaching
                      author's own (enforced by the caller, which knows the
                      subject; see CheckAttachOwnership)

func CheckPromotionAttachments added in v1.111.0

func CheckPromotionAttachments(targetScope string, targetPersonas []string, attached []AttachmentScope) error

CheckPromotionAttachments reports whether every attachment on a prompt would still satisfy the scope rule once the prompt moves to the target scope. It is the gate on the #1009 promotion flow: promoting a personal prompt that carries a private template must fail at request time, naming the offending resource, rather than succeed into a shared scope whose readers get nothing.

func IndexText added in v1.81.0

func IndexText(p *Prompt) string

IndexText composes the text a prompt is embedded and lexically indexed on for semantic discovery: its title (display name, falling back to the name), description, body, and tags. The indexjobs prompts consumer and the request-path search MUST agree on this composition so a stored embedding lives in the same space as the query; it is defined once here for both. Empty fields are skipped so a sparse prompt does not pad the text with blank lines.

func RequiresReview added in v1.108.0

func RequiresReview(before, after *Prompt) bool

RequiresReview reports whether an edit must go through admin review before being served: a change to the prompt's substance (content or arguments) when the pre-edit prompt is an approved shared (global or persona) prompt. Personal prompts version silently (the owner is the only consumer), a prompt that was never approved has no approved snapshot to protect, and system rows are read-only config mirrors that are never versioned or reviewed (their content is owned by server configuration and re-ingested at startup).

func SnapshotChanged added in v1.108.0

func SnapshotChanged(before, after *Prompt) bool

SnapshotChanged reports whether any versioned snapshot field (content, display name, description, arguments, tags) differs between the two states.

func ValidateCollectionDescription added in v1.109.0

func ValidateCollectionDescription(desc string) error

ValidateCollectionDescription bounds a collection description.

func ValidateCollectionName added in v1.109.0

func ValidateCollectionName(name string) error

ValidateCollectionName checks that a collection name is present and bounded. Collection names are free-text display names (unlike prompt names, which are invocation identifiers), so only presence and length are enforced.

func ValidateName

func ValidateName(name string) error

ValidateName checks that a prompt name is well-formed.

func ValidateScope

func ValidateScope(scope string) error

ValidateScope checks that a scope value is allowed for database-stored prompts.

func ValidateStatus added in v1.81.0

func ValidateStatus(status string) error

ValidateStatus checks that a status value is recognized.

func ValidateStatusTransition added in v1.81.0

func ValidateStatusTransition(from, to string) error

ValidateStatusTransition checks whether a status transition is allowed.

func ValidateTags added in v1.81.0

func ValidateTags(tags []string) error

ValidateTags checks that a prompt's tag list is within bounds.

Types

type Argument

type Argument struct {
	Name        string `json:"name" example:"date"`
	Description string `json:"description" example:"The date to analyze (YYYY-MM-DD)"`
	Required    bool   `json:"required" example:"true"`
}

Argument describes a prompt argument.

type Attachment added in v1.111.0

type Attachment struct {
	PromptID   string `json:"prompt_id" example:"prompt_a1b2c3d4"`
	ResourceID string `json:"resource_id" example:"res_01HK7R9F"`

	// Position orders attachments within a prompt, starting at 0. The order is
	// authored, not incidental: an SOP that says "fill the template, then check
	// it against the rubric" needs the template first.
	Position int `json:"position" example:"0"`

	// AttachedBy is the email of the author who created the link, kept for the
	// same reason approval stamps are kept: an operator reading a shared SOP
	// needs to know who put the material there.
	AttachedBy string `json:"attached_by,omitempty" example:"analyst@example.com"`
}

Attachment links a managed resource to a prompt as reference material the prompt's procedure depends on: the template it fills, the checklist it follows, the brand header it embeds, the sample payload it matches (#1013).

The link is stored by resource id, not by a copy of the resource, so editing the uploaded file updates every prompt that attaches it. The row deliberately carries no foreign key to the resource table: deleting a resource must leave the attachment row behind so the prompt still serves and both the served result and the portal can flag the link as broken.

type AttachmentProvider added in v1.111.0

type AttachmentProvider interface {
	// Attachments returns the underlying attachment capability, or nil when the
	// backing store does not support attachments.
	Attachments() AttachmentStore
}

AttachmentProvider exposes the attachment capability through store decorators that would otherwise hide it from a type assertion, mirroring CollectionProvider. The promptlayer notifying wrapper implements it by delegating to the wrapped store; the composition root resolves the capability with AsAttachmentStore.

type AttachmentScope added in v1.111.0

type AttachmentScope struct {
	// ResourceID identifies the resource in error messages.
	ResourceID string
	// DisplayName names the resource in error messages, so an author who is
	// blocked learns which attachment is at fault without a second lookup.
	DisplayName string
	// Scope is the resource's visibility: "global", "persona", or "user".
	Scope string
	// ScopeID is the persona name for a persona-scoped resource, the owning
	// user's subject for a user-scoped one, and empty for a global one.
	ScopeID string
}

AttachmentScope is the subset of a resource's identity the attach-time scope rule needs. It exists so the rule can live in pkg/prompt without importing pkg/resource: the caller reads the resource and passes its scope through.

type AttachmentStore added in v1.111.0

type AttachmentStore interface {
	// Attach links a resource to a prompt at the end of the prompt's ordered
	// attachment list. Attaching an already-attached resource is a no-op that
	// preserves the existing position, so a double-click never reorders.
	Attach(ctx context.Context, a Attachment) error

	// Detach removes one link. Returns ErrAttachmentNotFound when no such link
	// exists. Remaining attachments keep their relative order.
	Detach(ctx context.Context, promptID, resourceID string) error

	// ListByPrompt returns one prompt's attachments in authored order.
	ListByPrompt(ctx context.Context, promptID string) ([]Attachment, error)

	// ListByResource returns the ids of prompts that attach a resource, so the
	// resource detail view can answer "what depends on this file?" before an
	// operator deletes it.
	ListByResource(ctx context.Context, resourceID string) ([]string, error)

	// Reorder rewrites one prompt's attachment order to exactly the given
	// resource ids. Ids not currently attached are rejected; omitting a
	// currently attached id detaches it.
	Reorder(ctx context.Context, promptID string, resourceIDs []string) error
}

AttachmentStore persists the prompt-to-resource links.

func AsAttachmentStore added in v1.111.0

func AsAttachmentStore(store Store) AttachmentStore

AsAttachmentStore resolves the attachment capability from a prompt store, looking through any decorator that implements AttachmentProvider. Returns nil when the store has no attachment support (a file-only or in-memory store).

type Collection added in v1.109.0

type Collection struct {
	ID          string    `json:"id" example:"col_a1b2c3d4"`
	Name        string    `json:"name" example:"Sales Reporting"`
	Description string    `json:"description" example:"Weekly and daily sales SOPs"`
	CreatedBy   string    `json:"created_by" example:"jane@example.com"`
	PromptCount int       `json:"prompt_count" example:"7"`
	CreatedAt   time.Time `json:"created_at" example:"2026-07-01T14:30:00Z"`
	UpdatedAt   time.Time `json:"updated_at" example:"2026-07-01T14:30:00Z"`
}

Collection is a named group organizing the prompt library by team, domain, or workflow (#1010). Collections are visible to every portal user; a prompt belongs to at most one collection.

type CollectionProvider added in v1.109.0

type CollectionProvider interface {
	// Collections returns the underlying collection capability, or nil when
	// the backing store does not support collections.
	Collections() CollectionStore
}

CollectionProvider exposes the collection capability through store decorators that would otherwise hide it from a type assertion. The promptlayer notifying wrapper implements it by delegating to the wrapped store; the composition root resolves the capability with AsCollectionStore.

type CollectionStore added in v1.109.0

type CollectionStore interface {
	// CreateCollection persists a new collection, generating its ID. Returns
	// ErrCollectionExists on a name collision.
	CreateCollection(ctx context.Context, c *Collection) error

	// GetCollection retrieves a collection by ID. Returns nil, nil if not found.
	GetCollection(ctx context.Context, id string) (*Collection, error)

	// ListCollections returns every collection with its prompt count, ordered
	// by name.
	ListCollections(ctx context.Context) ([]Collection, error)

	// UpdateCollection renames or re-describes a collection. Returns
	// ErrCollectionExists on a name collision.
	UpdateCollection(ctx context.Context, id, name, description string) error

	// DeleteCollection removes a collection, releasing its prompts to the
	// default (uncollected) group.
	DeleteCollection(ctx context.Context, id string) error

	// SetPromptCollection assigns a prompt to a collection, or clears the
	// assignment when collectionID is empty. It touches only the assignment:
	// no version snapshot is produced and the review gate is not involved
	// (placement is not reviewable substance).
	SetPromptCollection(ctx context.Context, promptID, collectionID string) error
}

CollectionStore is the optional collection capability of a prompt store. The PostgreSQL store implements it; deployments without a database have no collections and the REST routes are not mounted.

func AsCollectionStore added in v1.109.0

func AsCollectionStore(store Store) CollectionStore

AsCollectionStore resolves the collection capability from a prompt store, looking through any decorator that implements CollectionProvider. Returns nil when the store has no collection support.

type EditOutcome added in v1.108.0

type EditOutcome struct {
	// Applied is true when the live row was updated (the edit is being served).
	Applied bool `json:"applied"`
	// PendingVersion is the draft version number when the edit was deferred
	// for review; zero when Applied.
	PendingVersion int `json:"pending_version,omitempty"`
}

EditOutcome reports how ApplyEdit landed an edit: applied to the live prompt row, or deferred as a pending draft version awaiting admin approval.

func ApplyEdit added in v1.108.0

func ApplyEdit(ctx context.Context, store Store, before, after *Prompt, author string) (EditOutcome, error)

ApplyEdit lands a prompt edit through the one shared review gate used by every mutation surface (manage_prompt tool, admin REST, portal REST). before must be the persisted pre-edit state and after the fully mutated copy; author is the actor recorded on any version produced.

A review-gated edit (RequiresReview) becomes a pending draft version and leaves the live row untouched, so other users keep being served the approved snapshot until an admin approves the draft. Every other edit is applied via UpdateWithVersion, which snapshots a new applied version when a versioned field changed. The versioning capability is asserted from the store itself; a store without it (no-DB deployments, plain test stores) degrades to a plain unversioned update.

type ListFilter

type ListFilter struct {
	Scope           string   // "global", "persona", "personal", or "" for all
	Personas        []string // filter by persona membership (OR match)
	OwnerEmail      string   // filter by owner
	Enabled         *bool    // filter by enabled state
	Search          string   // free-text search on name, display_name, description
	ReviewRequested *bool    // filter by pending promotion request (admin queue)
	Source          string   // include only this origin (operator, agent, system); "" for all
	ExcludeSource   string   // exclude this origin (e.g. "system" to hide ingested static prompts)
}

ListFilter controls which prompts are returned by List.

type Prompt

type Prompt struct {
	ID          string     `json:"id" example:"prompt_a1b2c3d4"`
	Name        string     `json:"name" example:"daily-sales-report"`
	DisplayName string     `json:"display_name" example:"Daily Sales Report"`
	Description string     `json:"description" example:"Generate a daily sales summary by region"`
	Content     string     `json:"content" example:"Analyze sales data for {date} grouped by region."`
	Arguments   []Argument `json:"arguments"`
	Category    string     `json:"category" example:"analysis"`
	Scope       string     `json:"scope" example:"persona"`
	Personas    []string   `json:"personas" example:"analyst,data-engineer"`
	OwnerEmail  string     `json:"owner_email" example:"admin@example.com"`
	Source      string     `json:"source" example:"database"`
	Enabled     bool       `json:"enabled" example:"true"`
	Tags        []string   `json:"tags" example:"sales,reporting"`

	// Promotion lifecycle.
	Status       string     `json:"status" example:"approved"`
	ApprovedBy   string     `json:"approved_by,omitempty" example:"admin@example.com"`
	ApprovedAt   *time.Time `json:"approved_at,omitempty"`
	DeprecatedAt *time.Time `json:"deprecated_at,omitempty"`
	SupersededBy string     `json:"superseded_by,omitempty" example:"daily-sales-report-v2"`

	// CollectionID places the prompt in at most one collection (#1010); empty
	// means uncollected. Placement is organizational metadata, not reviewable
	// substance: it is never versioned and changing it never triggers review.
	CollectionID string `json:"collection_id,omitempty" example:"col_a1b2c3d4"`

	// Version is the number of the snapshot the live row currently serves
	// (see VersionStore). Pending draft versions above this number exist in
	// the version history but are not served until approved.
	Version int `json:"version" example:"4"`

	// Serve-derived usage (see UsageReader). Populated only on read surfaces
	// wired to a usage reader (manage_prompt get, the prompt usage REST
	// endpoints); omitted elsewhere. Zero/absent means never served within the
	// audit retention window.
	RunCount  int64      `json:"run_count,omitempty" example:"37"`
	LastRunAt *time.Time `json:"last_run_at,omitempty"`

	// Promotion request: an owner asks to move a personal prompt into a shared
	// scope. ReviewRequested marks the prompt as pending in the admin queue;
	// RequestedScope/RequestedPersonas record the target. Cleared on approve or
	// reject (see ApplyPromotionRequest / ApprovePromotion).
	ReviewRequested   bool     `json:"review_requested" example:"false"`
	RequestedScope    string   `json:"requested_scope,omitempty" example:"persona"`
	RequestedPersonas []string `json:"requested_personas,omitempty" example:"analyst"`

	CreatedAt time.Time `json:"created_at" example:"2026-01-15T14:30:00Z"`
	UpdatedAt time.Time `json:"updated_at" example:"2026-01-15T14:30:00Z"`
}

Prompt represents a user-managed MCP prompt.

func (*Prompt) ApplyPromotionRequest added in v1.81.0

func (p *Prompt) ApplyPromotionRequest(requestedScope string, requestedPersonas []string) error

ApplyPromotionRequest records an owner's request to promote a personal prompt into a shared scope. The prompt must be personal; the target must be persona (with at least one persona) or global. It only sets the request signal; the scope does not change until an admin approves (see ApprovePromotion).

func (*Prompt) ApplyStatusTransition added in v1.81.0

func (p *Prompt) ApplyStatusTransition(newStatus, supersededBy, actorEmail string, isAdmin bool, now time.Time) error

ApplyStatusTransition validates and applies a status change to the prompt, stamping the lifecycle metadata. A no-op when newStatus is empty or unchanged. Approval (-> approved) requires isAdmin; supersededBy is recorded when moving to superseded. now is passed in for testability. Returns an error on an invalid or unauthorized transition. Shared by the manage_prompt tool and the admin API so both enforce the lifecycle identically.

func (*Prompt) ApprovePromotion added in v1.81.0

func (p *Prompt) ApprovePromotion(actorEmail string, now time.Time) error

ApprovePromotion applies a pending promotion request: it moves the prompt to the requested scope/personas, marks it approved (stamping the admin), and clears the request signal. Returns an error if there is no pending request or the prompt is no longer personal (its scope changed out from under the request). The caller is responsible for checking the target shared name is free.

func (*Prompt) RejectPromotion added in v1.81.0

func (p *Prompt) RejectPromotion()

RejectPromotion clears a pending promotion request, leaving the prompt personal and otherwise unchanged.

type ScoredPrompt added in v1.81.0

type ScoredPrompt struct {
	Prompt Prompt  `json:"prompt"`
	Score  float64 `json:"score"`
}

ScoredPrompt pairs a prompt with its relevance score in [0,1].

type SearchQuery added in v1.81.0

type SearchQuery struct {
	Embedding  []float32 // query vector; nil selects lexical-only ranking
	QueryText  string    // raw query text for the lexical arm
	OwnerEmail string    // caller identity, for personal-scope visibility
	Persona    string    // caller persona, for persona-scope visibility
	IsAdmin    bool      // admin callers rank across all approved prompts
	Scope      string    // optional explicit scope filter ("" = all visible)
	Limit      int       // max results; see EffectiveLimit
}

SearchQuery describes a relevance ranking request over the prompt library. Visibility is applied before ranking (you cannot rank a prompt you cannot read): a non-admin caller sees global prompts, persona prompts matching Persona, and their own personal prompts; an admin sees every approved prompt. Only approved, enabled prompts are ever ranked. A nil Embedding selects lexical-only ranking (the graceful-degradation path when no embedding provider is configured); a non-nil Embedding selects hybrid ranking.

func (SearchQuery) EffectiveLimit added in v1.81.0

func (q SearchQuery) EffectiveLimit() int

EffectiveLimit clamps the requested limit into [1, maxSearchLimit], defaulting an unset or out-of-range value to DefaultSearchLimit.

type Searcher added in v1.81.0

type Searcher interface {
	Search(ctx context.Context, q SearchQuery) ([]ScoredPrompt, error)
}

Searcher ranks approved prompts by relevance to a query within the caller's visibility. It is a capability separate from Store: only a backing store that can rank (the PostgreSQL store with pgvector) implements it, so the feature degrades to absent rather than forcing every Store implementation to carry a ranking query.

type Store

type Store interface {
	// Create persists a new prompt.
	Create(ctx context.Context, p *Prompt) error

	// Get retrieves a non-personal (global or persona) prompt by name, which is
	// globally unique. Returns nil, nil if not found. Personal prompts are
	// per-owner and must be fetched with GetPersonal.
	Get(ctx context.Context, name string) (*Prompt, error)

	// GetPersonal retrieves a personal prompt by its owner and name. Personal
	// names are unique only within an owner, so the owner is required to
	// disambiguate. Returns nil, nil if not found.
	GetPersonal(ctx context.Context, ownerEmail, name string) (*Prompt, error)

	// ListPersonalByName retrieves every personal prompt with the given name,
	// across all owners. Personal names are unique only within an owner, so this
	// may return more than one row; callers with platform-wide visibility (an
	// admin resolving a prompt they did not author) use it to address a personal
	// prompt by name and to disambiguate by owner. Returns an empty slice if none
	// match.
	ListPersonalByName(ctx context.Context, name string) ([]Prompt, error)

	// GetByID retrieves a prompt by ID. Returns nil, nil if not found.
	GetByID(ctx context.Context, id string) (*Prompt, error)

	// Update modifies an existing prompt.
	Update(ctx context.Context, p *Prompt) error

	// Delete removes a prompt by name.
	Delete(ctx context.Context, name string) error

	// DeleteByID removes a prompt by ID.
	DeleteByID(ctx context.Context, id string) error

	// List returns prompts matching the filter.
	List(ctx context.Context, filter ListFilter) ([]Prompt, error)

	// Count returns the number of prompts matching the filter.
	Count(ctx context.Context, filter ListFilter) (int, error)
}

Store defines the interface for prompt persistence.

type Usage added in v1.108.0

type Usage struct {
	RunCount  int64      `json:"run_count" example:"37"`
	LastRunAt *time.Time `json:"last_run_at,omitempty"`
}

Usage is the audit-derived usage rollup for one prompt: how many times it was served (prompts/get or manage_prompt use) within the audit retention window, and when it was last served. A prompt that was never served has no entry in a usage map.

type UsageReader added in v1.108.0

type UsageReader interface {
	PromptUsage(ctx context.Context, promptIDs []string) (map[string]Usage, error)
}

UsageReader reports serve-derived usage for a set of prompt IDs. The audit store implements it by aggregating prompt_serve audit events; prompts absent from the returned map have never been served (within retention).

type Version added in v1.108.0

type Version struct {
	ID          string     `json:"id" example:"ver_a1b2c3d4"`
	PromptID    string     `json:"prompt_id" example:"prompt_a1b2c3d4"`
	Version     int        `json:"version" example:"4"`
	DisplayName string     `json:"display_name" example:"Daily Sales Report"`
	Description string     `json:"description" example:"Generate a daily sales summary by region"`
	Content     string     `json:"content" example:"Analyze sales data for {date} grouped by region."`
	Arguments   []Argument `json:"arguments"`
	Tags        []string   `json:"tags" example:"sales,reporting"`
	Author      string     `json:"author" example:"jane@example.com"`
	Status      string     `json:"status" example:"applied"`
	ApprovedBy  string     `json:"approved_by,omitempty" example:"admin@example.com"`
	ApprovedAt  *time.Time `json:"approved_at,omitempty"`
	CreatedAt   time.Time  `json:"created_at" example:"2026-06-12T14:30:00Z"`
}

Version is one immutable snapshot of a prompt's versioned fields (content, display name, description, arguments, tags), with the author who produced it and the approval stamp bound to this specific version. Approval stamps on a version never change once set: approving v5 does not alter the recorded approval of v4.

type VersionStore added in v1.108.0

type VersionStore interface {
	// UpdateWithVersion persists p like Store.Update and, when any versioned
	// snapshot field (content, display name, description, arguments, tags)
	// differs from the stored row, records a new applied version authored by
	// author and advances p.Version to it.
	UpdateWithVersion(ctx context.Context, p *Prompt, author string) error

	// CreateDraftVersion snapshots proposed's versioned fields as a new draft
	// version of the prompt without touching the live row, returning the new
	// version number. The live row continues to be served unchanged until the
	// draft is approved.
	CreateDraftVersion(ctx context.Context, promptID string, proposed *Prompt, author string) (int, error)

	// ListVersions returns every version of the prompt, newest first.
	ListVersions(ctx context.Context, promptID string) ([]Version, error)

	// GetVersion returns one version with its full content, or nil, nil when
	// the prompt has no such version.
	GetVersion(ctx context.Context, promptID string, version int) (*Version, error)

	// ApproveVersion applies draft version's snapshot to the live prompt row,
	// marks the prompt approved with approver's stamp, binds the same stamp to
	// the version row (status applied), and supersedes any other pending
	// drafts. Returns the updated prompt.
	ApproveVersion(ctx context.Context, promptID string, version int, approver string) (*Prompt, error)

	// RejectVersion marks a pending draft version rejected, leaving the live
	// prompt row untouched.
	RejectVersion(ctx context.Context, promptID string, version int) error
}

VersionStore is the optional versioning capability of a prompt store. The PostgreSQL store implements it; a store without it (some tests) degrades to unversioned updates via ApplyEdit's fallback. All write methods are transactional with the prompts row they touch.

Directories

Path Synopsis
Package attachhttp exposes prompt resource attachments (#1013) over REST for both operator surfaces: the portal (a prompt author manages the materials on their own prompts) and the admin API (any prompt).
Package attachhttp exposes prompt resource attachments (#1013) over REST for both operator surfaces: the portal (a prompt author manages the materials on their own prompts) and the admin API (any prompt).
Package attachserve resolves a prompt's attached resources (#1013) into the form each serving surface needs: MCP content items appended to a prompts/get result, and a JSON summary carried by manage_prompt use.
Package attachserve resolves a prompt's attached resources (#1013) into the form each serving surface needs: MCP content items appended to a prompts/get result, and a JSON summary carried by manage_prompt use.
Package postgres provides PostgreSQL storage for prompts.
Package postgres provides PostgreSQL storage for prompts.
Package promptindex is the prompt-library consumer of the shared indexjobs framework (#557, epic #525 phase 4).
Package promptindex is the prompt-library consumer of the shared indexjobs framework (#557, epic #525 phase 4).
Package versionhttp exposes prompt version history, draft review actions, audit-derived usage stats (#1009), and prompt collection management (#1010) over REST.
Package versionhttp exposes prompt version history, draft review actions, audit-derived usage stats (#1009), and prompt collection management (#1010) over REST.

Jump to

Keyboard shortcuts

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