api

package
v0.3.0-alpha.3 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: Apache-2.0 Imports: 37 Imported by: 0

Documentation

Overview

Package api is the canonical surface of gramaton's operations. Every transport (HTTP, MCP, CLI proxy, future gRPC/websocket) consumes this package. The goal is one definition of "what fields an operation accepts" and "what it does" -- no transport-specific struct drift.

Each operation lives in its own file (api/<op>.go) and follows the same shape:

type XxxRequest struct { ... with json + jsonschema tags ... }
type XxxResponse struct { ... }
const XxxDescription = "..."
func (a *API) Xxx(ctx context.Context, req XxxRequest) (XxxResponse, *APIError)

Transports read these types and methods via hand-written binding tables in their own packages. There is no reflection or codegen.

Index

Constants

View Source
const (
	FaultPhaseChunkSave      = "chunk_save"
	FaultPhaseJobstoreUpdate = "jobstore_update"
	// FaultPhaseEdgeFixup fires inside the L6 chunked runner's
	// post-chunks edge-fixup save. Returning an error simulates the
	// fixup commit failing; the runner rolls back the in-memory edges
	// and marks Job failed/edge_fixup_failed.
	FaultPhaseEdgeFixup = "edge_fixup"
	// FaultPhasePanic is honored only by FaultInjector implementations
	// that can panic on demand. CaptureBatch's runner consults this
	// via tests via the panic-injection seam.
	FaultPhasePanic = "panic"
)

Phase names recognized by FaultInjector. Defined as constants so implementations don't drift from the call sites.

View Source
const (
	BackupStatusDescription  = "List existing backups with size and creation time."
	BackupCreateDescription  = "" /* 157-byte string literal not displayed */
	BackupRestoreDescription = "Restore the store from a backup archive. Overwrites current data -- requires force=true."
	BackupExportDescription  = "Export the store as json, csv, or markdown. Streams the export bytes to the caller."
	BackupImportDescription  = "Import records from a prior export (JSON lines)."
)
View Source
const (
	BranchListDescription     = "List all branches with their commit hashes and which one is active."
	BranchCreateDescription   = "Create a new branch at the current HEAD. Use for safe experimentation before merging into main."
	BranchCheckoutDescription = "Switch to an existing branch. The graph is loaded from the branch's commit off-lock; indexes are rebuilt under lock."
	BranchMergeDescription    = "Merge a branch into main (fast-forward). Deletes the branch ref on success."
	BranchDiscardDescription  = "Discard a branch without merging. Switches back to main if the discarded branch was active."
)
View Source
const (
	DefaultClearMode      = ClearModeResolve
	DefaultSupersession   = SupersessionCollection
	DefaultCuration       = CurationNone
	DefaultContradictions = ContradictionsOn
)

Default values used when a collection's property is absent. The read-time fallback these constants power is why we don't need a migrate-time sweep on every config change.

DefaultCuration is CurationNone. Bare-bones collections created without a template or explicit curation knob skip LLM work -- the safe default for "I want a collection, didn't say anything else." Templates that want LLM-driven enrichment (backlog, todo, reading-list, journal, references) declare curation=standard explicitly. The flip from CurationStandard followed activation of curation on collection items: default=standard was risk-free while items lacked content_full and no LLM stage actually ran; once activation made the knob load-bearing, the deliberate choice was to make LLM costs explicitly opt-in rather than rely on an inherited default.

View Source
const (
	CurationStatusDescription  = "" /* 239-byte string literal not displayed */
	CurationTriggerDescription = "Run a curation cycle now. Returns triggered=false (with the prior status) when a cycle is already in progress."
	CurationDryRunDescription  = "" /* 132-byte string literal not displayed */
	CurationBatchDescription   = "Classify every pending record in one call (LLM required). Use when piggyback curation has fallen behind."
	CurationDrainDescription   = "" /* 416-byte string literal not displayed */
)

Description constants are shared by HTTP, MCP, and CLI proxy transports so the surface text never drifts between them.

View Source
const (
	MaxKeywordLength   = 256
	MaxSourceRefLen    = 2048
	MaxContextFieldLen = 2048
	MaxEdgeTypeLen     = 256
	MaxMatchLength     = 1024
	MaxTopicLength     = 1024
	MaxFactLen         = 10000
	MaxMetaKeys        = 50
	MaxMetaKeyLen      = 64
	MaxMetaValueLen    = 1024
	MaxReembedBatch    = 500
	// MaxCollectionBatchSize caps how many items a single bulk add
	// can commit. Bigger batches would tie up the engine write lock
	// for an extended period and potentially exceed provider
	// batch-embed limits. 500 is a starting point -- raise it only
	// after profiling.
	MaxCollectionBatchSize = 500
	// MaxSyncBatchSize caps the synchronous capture_batch path. Beyond
	// this, the wall-clock and lock-hold cost outweigh the latency
	// savings vs a series of single captures; the async path (Layer 5)
	// raises the cap to MaxAsyncBatchSize.
	MaxSyncBatchSize = 500
	// MaxBatchBytes is the per-request total content-byte ceiling for
	// the capture_batch path. Defends against a 1000-item × 100MB-each
	// memory blowup; Phase 0 validation rejects oversize before any
	// allocation.
	MaxBatchBytes = 256 * 1024 * 1024
	// MaxClientRefLen caps the per-item ClientRef label.
	MaxClientRefLen = 128
	// MaxBatchEdgeMultiplier caps how many edges a batch may carry
	// relative to its item count. The cap is len(Items) *
	// MaxBatchEdgeMultiplier; rejecting a 10000-edge request against
	// a 5-item batch keeps Phase 0 from chewing up unbounded
	// validation work for an obviously-malformed request.
	MaxBatchEdgeMultiplier = 10
	// MaxAsyncBatchSize caps the async capture_batch path. The async
	// runner splits work into MaxSyncBatchSize-per-chunk pieces and
	// commits per-chunk progress, so this can be much larger than the
	// sync cap. Operator override via cfg.Jobs.MaxAsyncBatchSize.
	// MaxBatchBytes (256MB) remains the byte-level safety net.
	MaxAsyncBatchSize = 10000
	// MaxJobsListLimit caps gramaton_jobs_list pagination. Above
	// this an operator should narrow filters (status, kind, time
	// range) rather than scroll a single huge response.
	MaxJobsListLimit = 200
	// DefaultJobsListLimit is the limit applied when the request
	// omits one.
	DefaultJobsListLimit = 50
	// MaxJobsListOffset caps the pagination offset. Once a caller
	// is scrolling past 100k jobs, narrowing by status / kind / time
	// is the right tool; allowing arbitrary offset turns a single
	// request into an unbounded bbolt scan.
	MaxJobsListOffset = 100_000
	// MaxKindLen caps the Job kind filter string. The kind set is
	// closed (capture_batch and future ops); 64 is generous.
	MaxKindLen = 64
	// MaxRFC3339Len caps the wire length of an RFC3339 timestamp
	// argument. Real RFC3339 maxes around 30 characters; 64 leaves
	// headroom for nanoseconds + offset and rejects pathological
	// 10MB inputs.
	MaxRFC3339Len = 64
	// MaxResultTimeoutMS caps CaptureBatchResult's blocking timeout.
	// Holding a connection for longer than this is a footgun; the
	// caller should poll Status instead.
	MaxResultTimeoutMS = 30 * 60 * 1000 // 30 minutes
)

Per-field length + cardinality limits used by api method validation. Keep these in sync with anything the HTTP layer enforces separately (e.g. JSON body size, ingest size) -- those limits are at a coarser layer and stay with the transport.

View Source
const (
	MaxSearchTop      = 1000
	MaxMissingFields  = 50
	MaxSearchHops     = 10
	MaxExploreDepth   = 10
	MaxExploreNodes   = 10000
	MaxEdgeTypes      = 50
	MaxDuplicatePairs = 1000
	MaxLogLimit       = 500
	MaxLogTraversal   = 5000
	// MaxLogActionsFilter bounds the size of the Actions filter
	// array on gramaton_log. A caller passing thousands of Kinds
	// would otherwise inflate the in-memory set and force a scan
	// of every commit's Actions slice against all of them. 64 is
	// generous -- the Kind set today is under 20.
	MaxLogActionsFilter = 64
)

Search input cardinality limits.

View Source
const (
	MaxProjectionFields   = 64
	MaxFilterKeys         = 20
	MaxFilterValuesPerKey = 100
)

Collection item listing limits. Bounds on the new projection and filter knobs keep CollectionItems from being a DoS amplifier -- 10k field names or a filter map with 1000 keys * 1000 values each would otherwise be valid inputs.

View Source
const CaptureBatchCancelDescription = `` /* 450-byte string literal not displayed */

CaptureBatchCancelDescription is the MCP tool description for gramaton_capture_batch_cancel.

View Source
const CaptureBatchDescription = `` /* 894-byte string literal not displayed */

CaptureBatchDescription is the MCP tool description shared by every transport (MCP server registration and CLI MCP proxy).

View Source
const CaptureBatchResultDescription = `` /* 523-byte string literal not displayed */

CaptureBatchResultDescription is the MCP tool description for gramaton_capture_batch_result.

View Source
const CaptureBatchStatusDescription = `` /* 325-byte string literal not displayed */

CaptureBatchStatusDescription is the MCP tool description for gramaton_capture_batch_status.

View Source
const CaptureDescription = `` /* 947-byte string literal not displayed */

CaptureDescription is the MCP tool description shared by every transport that surfaces capture (direct MCP registration and the CLI MCP proxy). Changes here update both surfaces.

View Source
const ClassifyDescription = "Classify a pending record with metadata. Sets processing_status to processed."

ClassifyDescription is the MCP tool description for gramaton_classify.

View Source
const CollectionAddBatchDescription = "" /* 785-byte string literal not displayed */
View Source
const CollectionAddDescription = "" /* 515-byte string literal not displayed */
View Source
const CollectionCreateDescription = "" /* 255-byte string literal not displayed */
View Source
const CollectionDeleteDescription = "Retire a collection (reversible). Items and edges are preserved. Call again on a retired collection to re-activate it."
View Source
const CollectionItemsDescription = "" /* 483-byte string literal not displayed */
View Source
const CollectionListDescription = "" /* 181-byte string literal not displayed */
View Source
const CollectionMigrateDescription = "" /* 156-byte string literal not displayed */
View Source
const CollectionMoveDescription = "Move an item from one collection to another. The item's fields are validated against the target collection's schema."
View Source
const CollectionRemoveDescription = "Remove an item from a collection. The item node is preserved in the graph; only the membership edge is deleted."
View Source
const CollectionRenameDescription = "Rename a collection. Name must be unique."
View Source
const CollectionSchemaDescription = "Read a collection's schema and migration status."
View Source
const CollectionUpdateDescription = "" /* 142-byte string literal not displayed */
View Source
const DeleteRecordDescription = "Soft-delete a record. Sets processing_status=deleted and records deleted_at; the node is retained for provenance/rollback."

DeleteRecordDescription is the MCP tool description for the delete operation. Soft-delete semantics: sets processing_status = "deleted" rather than removing the node.

View Source
const DiffDescription = "" /* 171-byte string literal not displayed */

DiffDescription is shared by HTTP, MCP, and CLI proxy transports.

View Source
const DuplicatesDescription = "Find near-duplicate records by embedding similarity."

DuplicatesDescription is the MCP tool description.

View Source
const ExploreDescription = "Traverse the graph from a starting record. Returns connected nodes and edges within the given depth."

ExploreDescription is the MCP tool description for gramaton_explore.

View Source
const HistoryDescription = "View the change history for a specific record, walking commit metadata backwards from HEAD."

HistoryDescription is the MCP tool description for gramaton_history.

View Source
const InspectDescription = "" /* 551-byte string literal not displayed */

InspectDescription is the MCP tool description for gramaton_inspect.

View Source
const JobsListDescription = `` /* 444-byte string literal not displayed */

JobsListDescription is the MCP tool description for gramaton_jobs_list.

View Source
const LinkDescription = "Create a typed edge from one record to another."

LinkDescription is the MCP tool description for gramaton_link.

View Source
const LogDescription = "" /* 608-byte string literal not displayed */

LogDescription is shared by HTTP, MCP, and CLI proxy transports.

View Source
const MaxClientSessionIDLen = 256

MaxClientSessionIDLen caps how long a client_session_id can be. Keeps pathological callers from blowing up hook-state file paths.

View Source
const MaxMutationsPerCommit = 20

MaxMutationsPerCommit caps the per-commit mutation slice when IncludeRecordMutations=true. A curation batch touching 50 records would otherwise balloon a single log entry; truncation keeps response size predictable and is flagged via LogEntry.MutationsTruncated so callers know to drop to gramaton_history if they need the full set.

View Source
const MaxResetStuckIDs = 10000

MaxResetStuckIDs caps the number of explicit IDs accepted by CurationResetStuck. The operation is loopback-gated and the body size is already bounded, but an explicit cap keeps the api layer honest about its blast radius. 10000 matches MaxExploreNodes -- "max population we'll touch in one operation." Operators with more than 10k stuck records have a deeper problem the recovery verb alone can't help with.

View Source
const PendingDescription = "List records awaiting classification (processing_status=captured)."

PendingDescription is the MCP tool description for gramaton_pending.

View Source
const ReembedDescription = "" /* 133-byte string literal not displayed */

ReembedDescription is shared by HTTP, MCP, and CLI proxy.

View Source
const ResolveDescription = "" /* 439-byte string literal not displayed */

ResolveDescription is the MCP tool description for gramaton_resolve.

View Source
const SearchDescription = "" /* 1487-byte string literal not displayed */

SearchDescription is the MCP tool description for gramaton_search. Leads with triggers (not mechanics) to prompt agents to call it BEFORE producing project-state content, not after. The retrieval failure mode that motivated this framing was agents writing architecture answers from general knowledge without first checking for project-specific prior thinking in the store.

View Source
const SessionCommitDescription = `` /* 246-byte string literal not displayed */

SessionCommitDescription is the MCP tool description for gramaton_session_commit.

View Source
const SessionGetDescription = `Get the current session state including all topics and segments. Use to review what has been captured so far.`

SessionGetDescription is the MCP tool description for gramaton_session_get.

View Source
const SessionPrepareDescription = `` /* 626-byte string literal not displayed */

SessionPrepareDescription is the MCP tool description for gramaton_session_prepare. Leads with "eagerly throughout" language to counter the prior-version regression where agents self-triggered far less often than the old gramaton_observe tool did.

View Source
const SessionStartDescription = `` /* 186-byte string literal not displayed */

SessionStartDescription is the MCP tool description for gramaton_session_start.

View Source
const StatsDescription = "" /* 340-byte string literal not displayed */

StatsDescription is the MCP tool description for gramaton_stats.

View Source
const StatusDescription = "Get store health: node/edge counts, embedding status."

StatusDescription is the MCP tool description for gramaton_status.

View Source
const UnlinkDescription = "Delete an edge by its edge_id."

UnlinkDescription is the MCP tool description for gramaton_unlink.

View Source
const UpdateDescription = "Update metadata on a Memory record. For collection item fields, use gramaton_collection_update instead."

UpdateDescription is the MCP tool description for gramaton_update.

Variables

View Source
var (
	ValidTemporalities = map[string]bool{
		"immutable": true, "durable": true, "temporal": true, "ephemeral": true,
	}
	ValidKnowledgeTypes = map[string]bool{
		"episodic": true, "semantic": true, "procedural": true,
		"conceptual": true, "reference": true, "collection": true,
	}
	ValidEpistemicStatuses = map[string]bool{
		"well_established": true, "probable": true, "speculative": true,
		"contested": true, "refuted": true,
	}
	ValidResolutions = map[string]bool{
		"completed": true, "superseded": true, "abandoned": true, "obsolete": true,
	}
)

Enum sets. Exposed so transport-level validators (e.g. MCP schema hints) can reference the same source of truth.

Functions

func ListTemplates

func ListTemplates() []string

ListTemplates returns the names of all registered templates sorted alphabetically. Useful for a future "gramaton template list" CLI or the collection-creation wizard.

func MaxKeywords

func MaxKeywords() int

MaxKeywords returns the configured cap on keyword list length. Falls back to 100 when unset.

func MaxSummaryShort

func MaxSummaryShort() int

MaxSummaryShort returns the configured cap on summary_short length in characters. Falls back to 1000 when unset.

func SetLimits

func SetLimits(lim config.LimitsConfig)

SetLimits installs the configured limits. Zero values are preserved verbatim (no silent fallback) so config omissions surface clearly.

func WithTenant

func WithTenant(ctx context.Context, tenant string) context.Context

WithTenant returns a derived context carrying the supplied tenant identifier. Empty string is treated identically to "no tenant" (single-tenant deployment); pass it explicitly only when you have a meaningful identity.

The api/ layer reads this back via tenantFromContext when creating or filtering jobs. Until real caller identity wires in (HTTP middleware on a remote-callable mode, MCP session token, etc.), every request runs with tenant="" and the persisted Job records reflect that. The data layer is ready for the multi-tenant switch-over without a migration.

IMPORTANT — trust boundary: when remote-callable mode wires this in, the tenant value MUST come from a verified identity (signed JWT, OIDC subject claim, HTTP middleware that authenticates a session before stamping ctx). It must NEVER be read from a caller-controlled HTTP header or JSON field — that would let any caller list any tenant's jobs by spoofing the value. tenantContextKey is unexported precisely to keep external packages from constructing it; only this package's setter is the official path.

Types

type API

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

API is the single operational surface consumed by all transports. Construct one per server process; pass it to each transport's binding registrar.

func New

func New(deps Dependencies) *API

New constructs an API. The returned pointer is safe for concurrent use -- methods acquire engine locks as needed.

func (*API) BackupCreate

func (a *API) BackupCreate(ctx context.Context) (BackupCreateResponse, *APIError)

BackupCreate snapshots HEAD/refs/FORMAT under RLock, releases, and then runs the compression pass off-lock. Concurrent writes can land during the compression; they are not included in the archive (which is what "snapshot at time T" means).

func (*API) BackupExport

func (a *API) BackupExport(ctx context.Context, req ExportRequest, w io.Writer) (string, *APIError)

BackupExport streams matching records in the requested format.

Three-phase pattern (lock-discipline preserving):

  • Phase 1 (RLock): apply filters + collect candidate IDs. Filter args (Text, Match, Filter, etc.) reuse the search machinery; an empty filter set means "every record."
  • Phase 2 (no lock): the response writer.
  • Phase 3 (per-record RLock + write): backup.StreamRecords reacquires the lock briefly per record, fetches it, releases, then writes to w. Concurrent writers aren't blocked across the entire export.

Returns the content-type for the format on success. The export is exhaustive over the matched set -- no candidate_cap, no pagination -- since the loopback gate already restricts callers.

func (*API) BackupImport

func (a *API) BackupImport(ctx context.Context, req ImportRequest) (ImportResponse, *APIError)

BackupImport takes a batch of ExportRecord values and imports them. Current behavior matches pre-migration -- backup.ImportJSON handles its own concurrency, and we rebuild indexes after.

func (*API) BackupRestore

func (a *API) BackupRestore(ctx context.Context, req RestoreRequest) (RestoreResponse, *APIError)

BackupRestore overwrites the current data directory with the archive's contents. Necessarily holds the engine write lock for the duration of extraction + index rebuild; this is a rare destructive operation and splitting it further would add complexity without real value.

Path confinement: req.Path must live under the configured backup directory. Without this gate any caller could restore from an arbitrary tarball on the host filesystem -- a malicious archive could exploit tar-extraction edge cases or simply replace the live store with attacker-controlled state.

func (*API) BackupStatus

func (a *API) BackupStatus(ctx context.Context) (BackupStatusResponse, *APIError)

BackupStatus lists archives in the backup directory. No engine lock needed -- the backup directory is independent of the live store.

func (*API) BranchCheckout

func (a *API) BranchCheckout(ctx context.Context, name string) (BranchCheckoutResponse, *APIError)

BranchCheckout loads the branch's committed graph state. Three-phase lock discipline:

  1. RLock: read the target ref hash, release.
  2. No lock: parse the committed graph into a fresh *graph.Graph that SHARES the engine's BboltEdgeStore -- otherwise edges added on the new branch silently bypass bbolt persistence.
  3. Lock: write HEAD + active branch FIRST (so a partial failure leaves no in-memory/on-disk divergence), then SwapGraph, then rebuild indexes.

func (*API) BranchCreate

func (a *API) BranchCreate(ctx context.Context, req BranchCreateRequest) (BranchCreateResponse, *APIError)

BranchCreate writes a new ref at the current HEAD. Short lock hold -- one ReadRef + one WriteRef -- so no off-lock split.

func (*API) BranchDiscard

func (a *API) BranchDiscard(ctx context.Context, name string) (BranchDiscardResponse, *APIError)

BranchDiscard deletes a branch ref without merging. If the discarded branch is the active one, switches HEAD + active back to main BEFORE deleting -- otherwise we'd leave HEAD pointing at a missing ref. Short lock hold -- only on-disk writes.

func (*API) BranchList

func (a *API) BranchList(ctx context.Context) (BranchListResponse, *APIError)

BranchList returns every ref plus the active branch.

func (*API) BranchMerge

func (a *API) BranchMerge(ctx context.Context, name string) (BranchMergeResponse, *APIError)

BranchMerge fast-forwards main to absorb the named branch, then deletes the branch ref. Same off-lock parse pattern as checkout; the new graph shares the engine's BboltEdgeStore so post-merge edge writes persist.

Note: engine.Save() writes HEAD as part of its commit path, so merge's "swap then save" sequence ends with HEAD pointing at the new merge commit. main ref is updated after Save -- if that ref write fails the new commit exists in the object store and HEAD points at it (recoverable: re-running merge will pick up where we left off, since the graph is still in main's state).

func (*API) Capture

func (a *API) Capture(ctx context.Context, req CaptureRequest) (CaptureResponse, *APIError)

Capture creates a new knowledge record. Pre-embeds content_short outside the engine write lock, then holds the lock for the minimum time needed to insert the node, attach the embedding, check dedup, and save. Auto-supersession: if the captured record is a near-duplicate (cosine >= dedup.threshold) of an existing record, the older one is marked historical and a "supersedes" edge links the new record to it. Returns ErrConflict only when dedup.action = "reject" AND a duplicate is found.

func (*API) CaptureBatch

func (a *API) CaptureBatch(ctx context.Context, req CaptureBatchRequest) (CaptureBatchResponse, *APIError)

CaptureBatch dispatches to either the synchronous core or the async runner based on req.Wait. Both paths share envelope validation, ClientToken idempotency, and Job.Create up front; the per-item commit work runs inline (sync) or in a goroutine (async).

L5 single-chunk runner: the entire batch commits in one Save call in the runner goroutine (same shape as sync, just off the request path). L6 introduces multi-chunk + cross-chunk edge fixup.

func (*API) CaptureBatchCancel

CaptureBatchCancel flips the Job's status to cancelled and signals the runner's context (which the runner observes between chunks and during embed). One retry on transient JobStore.Update failure to tolerate brief bbolt contention.

func (*API) CaptureBatchResult

func (a *API) CaptureBatchResult(ctx context.Context, req CaptureBatchResultRequest) (CaptureBatchResponse, *APIError)

CaptureBatchResult blocks (with poll backoff) until the Job reaches a terminal state or the timeout elapses. On timeout returns the current Job snapshot and a "timeout" APIError so the caller knows the wait didn't complete.

The timeout is bounded by MaxResultTimeoutMS even when the caller passes a larger value. Holding a connection for longer is a footgun; the caller should poll Status instead. Per-tenant Job access is enforced inside the poll loop.

func (*API) CaptureBatchStatus

CaptureBatchStatus reads the current Job state. Read-only; never touches the engine write lock. Cross-tenant access surfaces ErrNotFound rather than ErrForbidden so existence isn't leaked.

func (*API) Classify

func (a *API) Classify(ctx context.Context, req ClassifyRequest) (ClassifyResponse, *APIError)

Classify moves a record out of the "captured" state by setting its classification fields and flipping processing_status to "processed". Unlike Update, Classify is idempotent -- calling it twice with the same args is safe.

func (*API) CollectionAdd

func (a *API) CollectionAdd(ctx context.Context, collectionID string, req CollectionAddRequest) (CollectionAddResponse, *APIError)

func (*API) CollectionAddBatch

func (a *API) CollectionAddBatch(ctx context.Context, collectionID string, req CollectionAddBatchRequest) (CollectionAddBatchResponse, *APIError)

CollectionAddBatch adds many items to a collection in one call. The implementation runs in two phases:

Phase 1 (off-lock): schema-validate every item and batch-embed
the concatenated text per item in a single provider call. Per-
item validation failures are recorded without engaging the
engine. An embed-call failure is tolerated -- items that would
have been embedded fall back to embed-less add, preserving the
rest of the batch.

Phase 2 (write lock): load the collection + schema, run the
dedup pass (existing members via CollCache plus intra-batch
titles) and commit all passing items inside a single
BatchIndexWrites transaction. One Save at the end.

Best-effort semantics: per-item validation and dedup failures are reported in Failed; items that pass pre-checks commit atomically. A Save-phase failure aborts the whole batch with a top-level APIError; it does not produce partial per-item results.

func (*API) CollectionCreate

func (*API) CollectionDelete

func (a *API) CollectionDelete(ctx context.Context, collectionID string) (CollectionDeleteResponse, *APIError)

func (*API) CollectionItems

func (a *API) CollectionItems(ctx context.Context, collectionID string, req CollectionItemsRequest) (CollectionItemsResponse, *APIError)

CollectionItems is deliberately unpaginated -- exhaustive retrieval is the contract that distinguishes Collections from Memory. If a collection grows large enough to need pagination, it's a signal to split it. Filter narrows the result by exact schema-field match (preserving the exhaustive contract). Fields projects the per-item `fields` sub-map down to a allowlist -- both are there so agents can audit large collections without dragging the full-fidelity `details` payload.

When req.AsOf is set, CollectionItems switches to point-in-time mode: the response reflects the commit at-or-before AsOf (D7 CommitAt), and each member is read at its per-commit state. The response carries `as_of` + `semantics: "point_in_time"` so agents don't have to guess.

func (*API) CollectionList

func (a *API) CollectionList(ctx context.Context, req CollectionListRequest) (CollectionListResponse, *APIError)

func (*API) CollectionMigrate

func (a *API) CollectionMigrate(ctx context.Context, collectionID string, req CollectionMigrateRequest) (CollectionMigrateResponse, *APIError)

func (*API) CollectionMove

func (a *API) CollectionMove(ctx context.Context, collectionID, itemID string, req CollectionMoveRequest) (CollectionMoveResponse, *APIError)

func (*API) CollectionRemove

func (a *API) CollectionRemove(ctx context.Context, collectionID, itemID string) (CollectionRemoveResponse, *APIError)

func (*API) CollectionRename

func (a *API) CollectionRename(ctx context.Context, collectionID string, req CollectionRenameRequest) (CollectionRenameResponse, *APIError)

func (*API) CollectionSchemaRead

func (a *API) CollectionSchemaRead(ctx context.Context, collectionID string) (CollectionSchemaReadResponse, *APIError)

func (*API) CollectionSchemaUpdate

func (a *API) CollectionSchemaUpdate(ctx context.Context, collectionID string, req CollectionSchemaUpdateRequest) (CollectionSchemaUpdateResponse, *APIError)

func (*API) CollectionUpdate

func (a *API) CollectionUpdate(ctx context.Context, collectionID, itemID string, req CollectionUpdateRequest) (CollectionUpdateResponse, *APIError)

func (*API) ConfigDir

func (a *API) ConfigDir() string

ConfigDir returns the configuration directory path (where hook-state files and similar artifacts live).

func (*API) CurationBatch

func (a *API) CurationBatch(ctx context.Context) (CurationBatchResponse, *APIError)

CurationBatch classifies every pending record in one pass. Requires an LLM provider on the engine. Long-running; the caller should expect this to block for the duration.

func (*API) CurationDrainContradictions

func (a *API) CurationDrainContradictions(ctx context.Context) (CurationDrainResponse, *APIError)

CurationDrainContradictions artificially marks every in-window contradiction-candidate pair as "no_contradiction" without calling the LLM. The operator is saying "I don't want to pay for the autonomous pass to organically drain this pool; I accept that real contradictions in the drained set won't be flagged." Edges carry an artificial: true property so future re-check logic can distinguish them from LLM-verified marks.

func (*API) CurationDryRun

func (a *API) CurationDryRun(ctx context.Context) (CurationDryRunResponse, *APIError)

CurationDryRun runs the autonomous curation pipeline without applying changes. The LLM is still called so callers see what would have happened. Deterministic curation runs normally.

func (*API) CurationListStuck

func (a *API) CurationListStuck(ctx context.Context) (CurationListStuckResponse, *APIError)

CurationListStuck walks the graph and returns every record with a stuck task status. Read-only; safe to call frequently.

func (*API) CurationResetStuck

CurationResetStuck flips stuck records back to their pre-failure status and clears their per-task attempts counter + last-error property. The next curation cycle will retry them. Operator-driven recovery; use the matching CLI verb gramaton curation stuck-records-reset which adds a count + LLM-cost-warning + Y/N confirmation around this call.

When req.IDs is empty: reset every stuck record across all tasks. When req.IDs is non-empty: reset only the listed records; non-stuck IDs in the list are silently ignored (no error -- caller may pass a list from a prior CurationListStuck snapshot whose state has since changed).

func (*API) CurationStatus

func (a *API) CurationStatus(ctx context.Context) (CurationStatusResponse, *APIError)

CurationStatus returns the runner's status and current manifest. Cheap; safe to call frequently. Returns ErrUnavailable if the runner is not configured.

func (*API) CurationTrigger

func (a *API) CurationTrigger(ctx context.Context) (CurationTriggerResponse, *APIError)

CurationTrigger asks the runner to start a cycle. Returns triggered=false (not an error) when a cycle is already in flight.

func (*API) DeleteRecord

func (a *API) DeleteRecord(ctx context.Context, req DeleteRecordRequest) (DeleteRecordResponse, *APIError)

DeleteRecord sets processing_status to "deleted" and timestamps the delete. Caller can still inspect the record; search deprioritizes deleted records by default.

func (*API) Diff

func (a *API) Diff(ctx context.Context, req DiffRequest) (DiffResponse, *APIError)

Diff computes added/modified/removed records between two commits identified by since and until. When since is empty, diffs against the chain root (full history). When until is empty, diffs up to HEAD. When since is provided but no commit before it exists (newer store), returns empty buckets rather than an error; same for until set to a date before the earliest indexed commit.

func (*API) Duplicates

func (a *API) Duplicates(ctx context.Context, req DuplicatesRequest) (DuplicatesResponse, *APIError)

Duplicates runs FindDuplicates with the given threshold/cap.

func (*API) Engine

func (a *API) Engine() *core.Engine

Engine returns the underlying engine. Exposed for transport-layer wiring that needs direct access (e.g. MCP request bindings that want to acquire engine locks around registered tool metadata). Prefer calling API methods over reaching through this accessor.

func (*API) Explore

func (a *API) Explore(ctx context.Context, req ExploreRequest) (ExploreResponse, *APIError)

Explore runs a bounded BFS from NodeID. MaxNodes caps the returned subgraph; the response reports Truncated=true when the cap fires so callers know the result is incomplete.

func (*API) History

func (a *API) History(ctx context.Context, req HistoryRequest) (HistoryResponse, *APIError)

History walks the commit chain back from HEAD looking for commits where the record's serialized property hash changed. Without Since/Until, traversal is capped at MaxLogTraversal to bound the read-lock hold. With Since/Until, the walk narrows via the D7 timestamp index: start from CommitAt(Until) (default HEAD), stop when a commit's timestamp falls before Since. Date-bounded walks bypass the traversal cap -- the range itself bounds the work.

RC-4: a record that's deleted and later recreated should surface the recreation as a first-appearance, not a spurious modification against the pre-deletion hash. Reset prevHash on found=false so the not-found gap breaks the comparison chain cleanly.

func (*API) Inspect

func (a *API) Inspect(ctx context.Context, req InspectRequest) (InspectResponse, *APIError)

Inspect returns a record with its properties, metadata summary, and related edges. Records access and spreads activation (D14). When IncludeContent is false, content_full is omitted from the properties map. Lazily loads the node from storage if not cached.

func (*API) JobsList

func (a *API) JobsList(ctx context.Context, req JobsListRequest) (JobsListResponse, *APIError)

JobsList enumerates JobStore entries with the supplied filter, scoped to the caller's tenant. Cross-tenant rows never leak: the JobStore filter pins TenantID to the value derived from context.

func (a *API) Link(ctx context.Context, req LinkRequest) (LinkResponse, *APIError)

Link adds an edge between two records. Returns ErrNotFound if either endpoint doesn't exist; ErrInvalid for bad weight or overlong type.

func (*API) Log

func (a *API) Log(ctx context.Context, req LogRequest) (LogResponse, *APIError)

Log walks the commit chain back from HEAD under a read lock. By default, capped at MaxLogLimit entries and never traverses past the chain root. When Since/Until are set, the walk is narrowed via the D7 timestamp index: start from CommitAt(Until) (default HEAD), stop when a commit's timestamp falls before Since. Date-bounded walks bypass MaxLogTraversal because the range itself bounds the work.

Phase 8 adds three filters + an enrichment flag: Actions filters by CommitAction.Kind (D3), ExcludeCuration filters by Message prefix so it works against pre-D3 commits, and IncludeRecordMutations adds per-record mutation summaries inline.

func (*API) Logger

func (a *API) Logger() *slog.Logger

Logger returns the API's logger. Transports should use this for anything emitted under the api component namespace.

func (*API) Pending

func (a *API) Pending(ctx context.Context, req PendingRequest) (PendingResponse, *APIError)

Pending returns records that have been captured but not yet classified. Ordering is index-walk order (not time-based) so agents that classify-and-move cover the set over multiple calls.

func (*API) Reembed

func (a *API) Reembed(ctx context.Context, req ReembedRequest) (ReembedResponse, *APIError)

Reembed regenerates embeddings for records whose stored embedding_model differs from the current embedder, or whose content_short embedding is missing. Three-phase to avoid holding the engine lock during embedding I/O:

  1. RLock: identify candidates and gather embedding source texts.
  2. (no lock): run the embedder; on context-length errors, retry each text individually with halving truncation.
  3. Lock: apply vectors and persist.

func (*API) Resolve

func (a *API) Resolve(ctx context.Context, req ResolveRequest) (ResolveResponse, *APIError)

Resolve marks a record and auto-sets valid_until so search deprioritizes the record going forward. Repeated calls with different resolutions overwrite; resolution_note is set only when provided.

When the record is a collection item, also flips the collection's `status` enum field (when present) to a closed-equivalent value. See inferClosedStatus for the heuristic.

func (*API) Runner

func (a *API) Runner() *curation.Runner

Runner returns the curation runner. Exposed for transports that need to trigger or inspect curation state directly.

func (*API) Search

func (a *API) Search(ctx context.Context, req SearchRequest) (SearchResponse, *APIError)

Search executes a hybrid BM25 + vector query. Pre-embeds query text outside the read lock, runs the search under RLock, then records access under Lock so retrieval metadata is kept fresh. Retrieved IDs are tracked so the observe pipeline can skip re-extracting content we just surfaced to the agent.

Pagination: a fresh call materializes up to cfg.Search.Pagination. CandidateCap ranked candidates into a snapshot keyed by a ULID query_id, slices the first PageSize for the response, and emits a page table covering the snapshot. A subsequent call with the same Cursor (from any PageRef.Cursor or NextCursor) slices the same snapshot at the encoded boundaries; record content is fetched fresh per page so modifications surface immediately while the match set stays stable for the snapshot's TTL.

func (*API) SessionArchive

func (a *API) SessionArchive(ctx context.Context, sessionID string, sourcePath string) (map[string]any, *APIError)

SessionArchive compresses a conversation transcript and stores it as a gzip file referenced from the Session node. The archive is NOT indexed or searchable -- it's a break-glass backup of the raw conversation.

func (*API) SessionCommit

func (a *API) SessionCommit(ctx context.Context, sessionID string, segments []CommitSegment) (SessionCommitResponse, *APIError)

SessionCommit appends extracted segments to the session. Validates that prepare was called first. Creates new topics as needed. Phase 2: stores in Session only (no Memory records, no embedding).

func (*API) SessionGet

func (a *API) SessionGet(ctx context.Context, sessionID string) (map[string]any, *APIError)

SessionGet returns the full state of a Session.

func (*API) SessionPrepare

func (a *API) SessionPrepare(ctx context.Context, sessionID string) (map[string]any, *APIError)

SessionPrepare returns extraction instructions and current session state. Sets an in-memory prepared flag so commit can validate the two-phase flow.

func (*API) SessionStart

func (a *API) SessionStart(ctx context.Context, clientSessionID string, source string) (map[string]any, *APIError)

SessionStart creates a new Session, chains to previous sessions on resume, or returns the current session for idempotent agent calls.

source="startup": fresh session, no chaining source="resume": new session chained to latest with same client_session_id source="" (agent call): return existing if found, create fresh otherwise

func (*API) SetBackupSnapshotHook

func (a *API) SetBackupSnapshotHook(ch chan struct{})

SetBackupSnapshotHook installs a channel that BackupCreate closes after phase-1 snapshot returns. Tests use this to race a concurrent capture in between snapshot and compression with no timing assumptions. Pass nil to clear.

func (*API) SetChunkSizeForTests

func (a *API) SetChunkSizeForTests(size int)

SetChunkSizeForTests overrides the chunked async runner's chunk size so tests can exercise multi-chunk behavior without seeding 1000+ items. Pass 0 to clear (production behavior: MaxSyncBatchSize). Production must never set this; in-package tests only.

func (*API) SetFaultInjector

func (a *API) SetFaultInjector(fi FaultInjector)

SetFaultInjector installs the FaultInjector consulted at named phases of long-running operations. Pass nil to clear. Production must never set this; in-package tests only.

func (*API) SetRunner

func (a *API) SetRunner(r *curation.Runner)

SetRunner installs the curation runner after construction. The runner is created later in the server lifecycle (after New returns) so it can't be passed via Dependencies at construction time; this setter bridges that gap. Safe to call once before any session or curation operations are invoked.

func (*API) ShutdownAsync

func (a *API) ShutdownAsync(ctx context.Context) error

ShutdownAsync prevents new async runners from spawning, cancels every in-flight runner's context, and waits for all of them to exit (or until ctx.Done()). Call this before closing the engine so runners don't touch a closed bbolt handle.

func (*API) StartPreparedSweeper

func (a *API) StartPreparedSweeper()

StartPreparedSweeper launches the background sweeper goroutine and stores its cancel function for shutdown. Idempotent -- if a sweeper is already running, return without starting a second one. A server with multiple entry points (Serve, StartHTTP) could otherwise leak goroutines with every additional start.

func (*API) Stats

func (a *API) Stats(ctx context.Context) (StatsResponse, *APIError)

Stats iterates the graph under a read lock and counts records by key metadata fields. Excludes chunk nodes and deleted records.

func (*API) Status

func (a *API) Status(ctx context.Context, _ StatusRequest) (StatusResponse, *APIError)

Status returns liveness-adjacent counters. Light enough to call every few seconds from a dashboard.

func (*API) StopPreparedSweeper

func (a *API) StopPreparedSweeper()

StopPreparedSweeper cancels the sweeper goroutine started by StartPreparedSweeper. Safe to call even if the sweeper never started (no-op).

func (a *API) Unlink(ctx context.Context, req UnlinkRequest) (UnlinkResponse, *APIError)

Unlink removes an edge. Returns ErrNotFound if the edge doesn't exist.

func (*API) Update

func (a *API) Update(ctx context.Context, req UpdateRequest) (UpdateResponse, *APIError)

Update sets metadata fields on an existing record. Empty / nil fields leave the existing property unchanged. Setting valid_until to "clear" removes the valid_until + resolution + resolved_at triple (undoes supersession or resolution). Returns ErrInvalid for any unknown enum value or out-of-range numeric. Refuses to update Session segments (append-only per D19).

func (*API) UsageTracker

func (a *API) UsageTracker() *llm.UsageTracker

UsageTracker returns the LLM usage tracker. Exposed for transports that need to surface cost information outside of a specific operation call (e.g. /v1/llm/stats).

type APIError

type APIError struct {
	Code       string // stable, machine-readable (e.g. "not_found", "input_error")
	Message    string // human-readable; safe to show end-users
	HTTPStatus int    // maps directly to HTTP response status
	Retryable  bool   // true when the caller can retry without changing the request
	Cause      error  // optional underlying error for errors.Is/As chaining (never serialized)
}

APIError is the canonical error type returned by every api method. Preserving the structured fields (vs. plain error wrapping) lets transports format the same error consistently:

  • HTTP writes Code/Message/Retryable into the error envelope at HTTPStatus
  • MCP emits the message with a structured error return
  • CLI surfaces Code/Message

Methods return (Response, *APIError); nil means success. Do not return a non-nil APIError alongside a partial Response -- callers should assume a non-nil APIError means the operation did not commit.

func ErrConflict

func ErrConflict(msg string) *APIError

ErrConflict signals a precondition that prevents the write (e.g. duplicate detected, record in wrong state). 409. Not retryable without a behavior change from the caller.

func ErrForbidden

func ErrForbidden(msg string) *APIError

ErrForbidden signals an operation not permitted for this caller/surface (e.g. loopback-only endpoint called from a non-loopback origin). 403.

func ErrInternal

func ErrInternal(msg string) *APIError

ErrInternal signals an unexpected failure that the caller cannot fix. 500, retryable (the transient cases are the common ones).

func ErrInvalid

func ErrInvalid(msg string) *APIError

ErrInvalid signals a field has the wrong shape/value. 400, retryable once the caller fixes the value.

func ErrMissing

func ErrMissing(msg string) *APIError

ErrMissing signals a required field is empty. 400, retryable once the caller includes the field.

func ErrNotFound

func ErrNotFound(msg string) *APIError

ErrNotFound signals the target resource does not exist. 404. Not retryable -- the caller should not keep asking.

func ErrPrepareRequired

func ErrPrepareRequired(msg string) *APIError

ErrPrepareRequired signals that gramaton_session_commit was called without a prior gramaton_session_prepare. 409 because the server is in a wrong-state rather than the request being malformed; retryable after the caller prepares.

func ErrUnavailable

func ErrUnavailable(msg string) *APIError

ErrUnavailable signals that an operation cannot proceed because a required dependency is not configured (e.g. curation runner is off, LLM provider is missing, no embedder). 503 because the server is healthy but the feature is not. Not retryable: the caller cannot fix this without an operator changing config.

func (*APIError) Error

func (e *APIError) Error() string

Error makes APIError satisfy the error interface for callers that want to treat it as one. Transports typically do not use this; they read the fields directly.

func (*APIError) Unwrap

func (e *APIError) Unwrap() error

Unwrap lets callers use errors.Is / errors.As to reach the underlying cause (e.g. graph.ErrNotFound) without string-matching on Code. Only meaningful when the constructor populates Cause -- most internal callsites don't, and transports never serialize it.

type BackupCreateResponse

type BackupCreateResponse struct {
	Path       string   `json:"path"`
	SizeBytes  int64    `json:"size_bytes"`
	DeletedOld []string `json:"deleted_old,omitempty"`
}

type BackupInfo

type BackupInfo struct {
	Path      string `json:"path"`
	SizeBytes int64  `json:"size_bytes"`
	Created   string `json:"created"`
}

BackupInfo describes one archive found in the backup directory.

type BackupStatusResponse

type BackupStatusResponse struct {
	BackupDir string       `json:"backup_dir"`
	Backups   []BackupInfo `json:"backups"`
	Count     int          `json:"count"`
}

type BatchAddFailure

type BatchAddFailure struct {
	Index     int    `json:"index"`
	ClientRef string `json:"client_ref,omitempty"`
	Code      string `json:"code"`
	Message   string `json:"message"`
}

BatchAddFailure is one entry in CollectionAddBatchResponse.Failed. Code matches the APIError codes ("input_error", "duplicate", etc.).

type BatchAddSuccess

type BatchAddSuccess struct {
	Index        int    `json:"index"`
	ClientRef    string `json:"client_ref,omitempty"`
	ID           string `json:"id"`
	Deduplicated bool   `json:"deduplicated,omitempty"`
}

BatchAddSuccess is one entry in CollectionAddBatchResponse.Added. Deduplicated=true means the item's title already existed on a curation=none collection and ID points to the pre-existing item; the batch did not create a new node for this entry. This mirrors single-add's idempotent-return shape so callers don't need to branch on batch vs. single for the same collection profile.

type BatchItemFailure

type BatchItemFailure struct {
	Index     int    `json:"index"`
	ClientRef string `json:"client_ref,omitempty"`
	Code      string `json:"code"`
	Message   string `json:"message"`
}

BatchItemFailure describes one item that did NOT commit. Index maps back to the original Items slice; ClientRef is echoed when the caller supplied one. Code is a stable machine-readable token; Message is human-readable. Distinct from api.ItemFailure (which uses ItemID for pre-existing record references in SessionCommit/CollectionMigrate).

type BranchCheckoutResponse

type BranchCheckoutResponse struct {
	Name       string `json:"name"`
	Commit     string `json:"commit"`
	CheckedOut bool   `json:"checked_out"`
}

type BranchCreateRequest

type BranchCreateRequest struct {
	Name string `json:"name" jsonschema:"branch name (see ValidBranchName)"`
}

type BranchCreateResponse

type BranchCreateResponse struct {
	Name    string `json:"name"`
	Commit  string `json:"commit"`
	Created bool   `json:"created"`
}

type BranchDiscardResponse

type BranchDiscardResponse struct {
	Discarded string `json:"discarded"`
}

type BranchEntry

type BranchEntry struct {
	Name   string `json:"name"`
	Commit string `json:"commit"`
	Active bool   `json:"active,omitempty"`
}

BranchEntry is one branch in a list response. Hash is truncated for display consistency with gramaton_log.

type BranchListResponse

type BranchListResponse struct {
	Branches []BranchEntry `json:"branches"`
	Current  string        `json:"current"`
}

type BranchMergeResponse

type BranchMergeResponse struct {
	Merged    string `json:"merged"`
	NewCommit string `json:"new_commit"`
}

type CaptureBatchAdded

type CaptureBatchAdded struct {
	ID         string             `json:"id"`
	ClientRef  string             `json:"client_ref,omitempty"`
	Warnings   []string           `json:"warnings,omitempty"`
	Superseded []SupersededRecord `json:"superseded,omitempty"`
}

CaptureBatchAdded describes one record that the batch successfully committed. ClientRef is echoed back when the request supplied one. Warnings collect non-fatal events (embed fallback, internal supersession, etc.) per item.

type CaptureBatchCancelRequest

type CaptureBatchCancelRequest struct {
	JobID string `json:"job_id" jsonschema:"the job_id returned by gramaton_capture_batch"`
}

CaptureBatchCancelRequest selects a job by ID.

type CaptureBatchCancelResponse

type CaptureBatchCancelResponse struct {
	JobID     string `json:"job_id"`
	Status    string `json:"status"`
	Cancelled bool   `json:"cancelled"`
}

CaptureBatchCancelResponse echoes the post-cancel terminal state. If the job was already terminal (completed/failed/cancelled), the call is a no-op and Status reflects the prior terminal value; Cancelled is true only when this call moved the state.

type CaptureBatchItem

type CaptureBatchItem struct {
	CaptureRequest
	ClientRef string `` /* 203-byte string literal not displayed */
}

CaptureBatchItem is one record in a CaptureBatchRequest. Embeds the existing single-capture shape to keep field semantics identical and adds a per-item ClientRef the caller can use to wire intra-batch edges (Layer 4).

type CaptureBatchRequest

type CaptureBatchRequest struct {
	Items            []CaptureBatchItem `` /* 135-byte string literal not displayed */
	Edges            []EdgeSpec         `` /* 196-byte string literal not displayed */
	Wait             *bool              `` /* 181-byte string literal not displayed */
	ClientToken      string             `` /* 164-byte string literal not displayed */
	SkipSupersession bool               `` /* 141-byte string literal not displayed */
}

CaptureBatchRequest captures up to MaxSyncBatchSize records in a single call. Items follow the same shape as CaptureRequest with an optional ClientRef for in-batch identity.

Layer 3 honors only the synchronous path; passing Wait=false returns ErrInvalid until Layer 5 wires the async runner. ClientToken + canonicalized RequestHash provide cross-call idempotency.

type CaptureBatchResponse

type CaptureBatchResponse struct {
	JobID       string              `json:"job_id"`
	Status      string              `json:"status"`
	Added       []CaptureBatchAdded `json:"added,omitempty"`
	Failed      []BatchItemFailure  `json:"failed,omitempty"`
	Edges       []EdgeAdded         `json:"edges,omitempty"`
	EdgesFailed []EdgeFailure       `json:"edges_failed,omitempty"`
	Stats       CaptureBatchStats   `json:"stats"`
	Warnings    []string            `json:"warnings,omitempty"`
}

CaptureBatchResponse is the canonical output of CaptureBatch. Sync mode populates Added/Failed/Stats inline; async mode (Layer 5) fills JobID + Status only and the caller polls.

type CaptureBatchResultRequest

type CaptureBatchResultRequest struct {
	JobID     string `json:"job_id" jsonschema:"the job_id returned by gramaton_capture_batch"`
	TimeoutMS int    `` /* 151-byte string literal not displayed */
}

CaptureBatchResultRequest selects a job by ID with an optional timeout. TimeoutMS=0 falls back to cfg.Jobs.ResultDefaultTimeout (30 minutes) so a caller doesn't accidentally hang forever on a stuck job.

type CaptureBatchStats

type CaptureBatchStats struct {
	TotalItems      int `json:"total_items"`
	AddedCount      int `json:"added_count"`
	FailedCount     int `json:"failed_count"`
	SupersededCount int `json:"superseded_count"`
	EdgesAdded      int `json:"edges_added,omitempty"`
	EdgesFailed     int `json:"edges_failed,omitempty"`
}

CaptureBatchStats summarizes the outcome counts. Always populated (zero values when nothing happened).

type CaptureBatchStatusRequest

type CaptureBatchStatusRequest struct {
	JobID string `json:"job_id" jsonschema:"the job_id returned by gramaton_capture_batch"`
}

CaptureBatchStatusRequest selects a job by ID for a status read.

type CaptureBatchStatusResponse

type CaptureBatchStatusResponse struct {
	JobID          string             `json:"job_id"`
	Status         string             `json:"status"`
	Kind           string             `json:"kind"`
	TotalItems     int                `json:"total_items"`
	ProcessedCount int                `json:"processed_count"`
	FailureReason  string             `json:"failure_reason,omitempty"`
	Errors         []BatchItemFailure `json:"errors,omitempty"`
	ClientRefToID  map[string]string  `json:"client_ref_to_id,omitempty"`
	ClientToken    string             `json:"client_token,omitempty"`
}

CaptureBatchStatusResponse is the live snapshot of a Job. Errors[] surfaces per-item failures collected so far so a caller can correct inputs and retry without waiting for the run to finish. ClientRefToID maps caller-supplied refs to assigned ULIDs; with failed runs it lets a caller resume edge creation via gramaton_link.

type CaptureRequest

type CaptureRequest struct {
	Content                string         `json:"content" jsonschema:"the knowledge to store (required)"`
	Temporality            string         `json:"temporality,omitempty" jsonschema:"immutable|durable|temporal|ephemeral"`
	Confidence             *float64       `json:"confidence,omitempty" jsonschema:"number between 0.0 and 1.0"`
	KnowledgeType          string         `json:"knowledge_type,omitempty" jsonschema:"episodic|semantic|procedural|conceptual|reference"`
	EpistemicStatus        string         `json:"epistemic_status,omitempty" jsonschema:"well_established|probable|speculative|contested|refuted"`
	Importance             *float64       `json:"importance,omitempty" jsonschema:"number between 0.0 and 1.0"`
	Keywords               []string       `json:"keywords,omitempty" jsonschema:"array of keyword strings for search"`
	SummaryShort           string         `json:"summary_short,omitempty" jsonschema:"~750 chars (semantic anchor for embedding)"`
	SourceRef              string         `json:"source_ref,omitempty" jsonschema:"source URL or path"`
	SourceCredibility      *float64       `json:"source_credibility,omitempty" jsonschema:"number between 0.0 and 1.0"`
	TestimonyHops          *int64         `` /* 152-byte string literal not displayed */
	ContextAbout           string         `json:"context_about,omitempty" jsonschema:"topic/domain"`
	ContextWho             string         `json:"context_who,omitempty" jsonschema:"entities involved"`
	ContextPrompted        string         `json:"context_prompted,omitempty" jsonschema:"what prompted this capture"`
	ContextFindable        string         `json:"context_findable_by,omitempty" jsonschema:"future retrieval terms"`
	ContextRelated         string         `json:"context_related,omitempty" jsonschema:"related concepts or records"`
	ContextSourceType      string         `` /* 142-byte string literal not displayed */
	ContextTimeSensitivity string         `` /* 133-byte string literal not displayed */
	ContextReliability     string         `json:"context_reliability,omitempty" jsonschema:"reliability signals (e.g. peer-reviewed, unverified, first-hand experience)"`
	ContextCaptureReason   string         `` /* 134-byte string literal not displayed */
	ValidFrom              string         `json:"valid_from,omitempty" jsonschema:"RFC3339; optional lower lifecycle bound"`
	ValidUntil             string         `json:"valid_until,omitempty" jsonschema:"RFC3339; optional expiration"`
	AssertedAsOf           string         `` /* 135-byte string literal not displayed */
	Meta                   map[string]any `` /* 183-byte string literal not displayed */
}

CaptureRequest is the canonical input to the capture operation. Every transport (HTTP, MCP, CLI proxy) uses this struct directly -- there is no per-transport copy to drift from.

json tags are the HTTP wire format. jsonschema tags surface as MCP tool descriptions (the MCP SDK reads them via reflection when the struct is passed as a tool args type).

type CaptureResponse

type CaptureResponse struct {
	ID         string             `json:"id"`
	Warnings   []string           `json:"warnings,omitempty"`
	Superseded []SupersededRecord `json:"superseded,omitempty"`
}

CaptureResponse is the canonical output of the capture operation. Omitted fields use json:",omitempty" so the wire format stays tight on the happy path (no warnings, no supersession).

type ClassifyRequest

type ClassifyRequest struct {
	ID              string   `json:"-" jsonschema:"-"`
	Temporality     string   `json:"temporality,omitempty" jsonschema:"immutable|durable|temporal|ephemeral"`
	Confidence      *float64 `json:"confidence,omitempty" jsonschema:"number between 0.0 and 1.0"`
	KnowledgeType   string   `json:"knowledge_type,omitempty" jsonschema:"episodic|semantic|procedural|conceptual|reference"`
	EpistemicStatus string   `json:"epistemic_status,omitempty" jsonschema:"well_established|probable|speculative|contested|refuted"`
	Importance      *float64 `json:"importance,omitempty" jsonschema:"number between 0.0 and 1.0"`
	Keywords        []string `json:"keywords,omitempty" jsonschema:"array of keyword strings"`
	SummaryShort    string   `json:"summary_short,omitempty" jsonschema:"~750 chars (semantic anchor for embedding)"`
}

ClassifyRequest carries the metadata fields to apply when promoting a pending record to processed. ID is transport-set.

type ClassifyResponse

type ClassifyResponse struct {
	ID      string `json:"id"`
	Updated bool   `json:"updated"`
}

ClassifyResponse confirms the record was promoted.

type ClearMode

type ClearMode string

ClearMode controls what happens when a collection is cleared (e.g. via a future `gramaton collection clear` or the resolve path on bulk operations).

resolve (default) -- item records get resolution="completed"
                     and valid_until set to the clear time.
                     Historical purchase queries keep working.
unlink            -- remove the member_of edge at HEAD; the
                     item record persists and can be re-linked.
const (
	ClearModeResolve ClearMode = "resolve"
	ClearModeUnlink  ClearMode = "unlink"
)

func CollectionClearMode

func CollectionClearMode(n *graph.Node) ClearMode

CollectionClearMode returns the collection's clear_mode config, falling back to DefaultClearMode when the property is absent or holds an empty string.

type CollectionAddBatchRequest

type CollectionAddBatchRequest struct {
	Items []CollectionAddItem `json:"items" jsonschema:"array of items to add (max 500)"`
}

type CollectionAddBatchResponse

type CollectionAddBatchResponse struct {
	CollectionID string            `json:"collection_id"`
	Added        []BatchAddSuccess `json:"added"`
	Failed       []BatchAddFailure `json:"failed"`
}

type CollectionAddItem

type CollectionAddItem struct {
	Fields    map[string]any `json:"fields" jsonschema:"item fields (must match collection schema if defined)"`
	ClientRef string         `json:"client_ref,omitempty" jsonschema:"optional caller handle echoed in the per-item result"`
}

CollectionAddItem is one item inside a CollectionAddBatchRequest. The optional ClientRef is echoed back in the per-item result so callers can correlate outcomes with their own records without relying on positional order.

type CollectionAddRequest

type CollectionAddRequest struct {
	Fields map[string]any `json:"fields"`
}

type CollectionAddResponse

type CollectionAddResponse struct {
	ID           string `json:"id"`
	CollectionID string `json:"collection_id"`
	Deduplicated bool   `json:"deduplicated,omitempty"`
}

CollectionAddResponse covers both the normal-add and the minimal-curation idempotent-dedup branches. Deduplicated is true when the item's title already existed on a curation=minimal collection; the ID points at the pre-existing item (no new node created).

type CollectionCreateRequest

type CollectionCreateRequest struct {
	Name        string            `json:"name"`
	Description string            `json:"description,omitempty"`
	Schema      *CollectionSchema `json:"schema,omitempty"`

	// Behaviour knobs. All optional -- absent means "use the default",
	// via the read-time fallback in collection_config.go. Passing an
	// explicit value stores it on the collection node so future reads
	// surface it without the fallback (useful for visibility).
	ClearMode      string `json:"clear_mode,omitempty"`
	Supersession   string `json:"supersession,omitempty"`
	Curation       string `json:"curation,omitempty"`
	Contradictions string `json:"contradictions,omitempty"`

	// Template names a pre-built Collection shape. Valid names are
	// exposed via api.ListTemplates; ships with backlog / todo /
	// reading-list / shopping-list / packing-list / journal /
	// references. When set, the template's schema + behaviour fields
	// populate any caller fields that are left empty. Caller-provided
	// fields always win (shallow merge).
	Template string `json:"template,omitempty"`
}

type CollectionCreateResponse

type CollectionCreateResponse struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

CollectionCreateResponse: {id, name}.

type CollectionDeleteResponse

type CollectionDeleteResponse struct {
	ID             string `json:"id"`
	Retired        bool   `json:"retired,omitempty"`
	Unretired      bool   `json:"unretired,omitempty"`
	ItemsPreserved int    `json:"items_preserved,omitempty"`
}

CollectionDeleteResponse covers both the retire and unretire branches. Exactly one of Retired / Unretired is true. ItemsPreserved is set only on the retire branch (count of items left intact under the retired collection).

type CollectionItem

type CollectionItem struct {
	ID             string         `json:"id"`
	CreatedAt      string         `json:"created_at,omitempty"`
	Fields         map[string]any `json:"fields,omitempty"`
	NeedsMigration []string       `json:"needs_migration,omitempty"`
}

CollectionItem is one row in CollectionItemsResponse.Items. Fields is dynamic (collection-schema-driven, optionally projected); typing it as map[string]any preserves wire shape and avoids per-schema codegen.

type CollectionItemsRequest

type CollectionItemsRequest struct {
	Sort           string `json:"sort,omitempty"`
	Order          string `json:"order,omitempty"`
	IncludeRetired bool   `json:"include_retired,omitempty"`

	// Fields is a allowlist of schema field names to include in each
	// item's `fields` sub-map. Empty/nil means return every field
	// (today's behavior). `id`, `created_at`, and `needs_migration`
	// are always included at the top level regardless of this list.
	Fields []string `json:"fields,omitempty"`

	// Filter is a schema-field -> expected-value(s) map that keeps an
	// item only when every listed field matches. Values may be a
	// single string for exact match, or a []string / []any of strings
	// for "any-of" match (OR within a field, AND across fields).
	// Unknown field names match nothing (empty result). Filtering
	// preserves the exhaustive-retrieval contract because matches are
	// exact, not ranked.
	Filter map[string]any `json:"filter,omitempty"`

	// Match is a literal substring search across the item's string
	// fields (case-insensitive). Use to narrow within an exhaustive
	// collection list (e.g. "all open backlog items mentioning
	// 'auth'" via filter={"status":"open"} + match="auth"). Distinct
	// from Filter which is exact-match: Match scans every field.*
	// string property for the substring. Mirrors SearchRequest.Match
	// semantics but scoped to a single collection.
	Match string `json:"match,omitempty"`

	// AsOf, when set, returns point-in-time membership: the members
	// that had `member_of` edges to the collection at the commit
	// immediately before AsOf, with each member's state at that
	// commit. Response carries `as_of` + `semantics: "point_in_time"`
	// so agents don't have to guess. Accepts YYYY-MM-DD or RFC3339.
	// Future dates are rejected. The filter/sort/projection knobs
	// still apply, but migration accounting is skipped (historical
	// snapshots are read-only).
	AsOf string `json:"as_of,omitempty"`
}

type CollectionItemsResponse

type CollectionItemsResponse struct {
	CollectionID string                    `json:"collection_id"`
	Items        []CollectionItem          `json:"items"`
	Count        int                       `json:"count"`
	Migration    *CollectionMigrationState `json:"migration,omitempty"`
	AsOf         string                    `json:"as_of,omitempty"`
	Semantics    string                    `json:"semantics,omitempty"`
}

CollectionItemsResponse covers both the live (HEAD) read and the point-in-time (as_of) branch. AsOf + Semantics are emitted only on the point-in-time branch.

type CollectionListEntry

type CollectionListEntry struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	HasSchema   bool   `json:"has_schema,omitempty"`
	ItemCount   int    `json:"item_count"`
	Retired     bool   `json:"retired,omitempty"`
	CreatedAt   string `json:"created_at,omitempty"`
}

CollectionListEntry is one row in CollectionListResponse.Collections. Description / HasSchema / Retired / CreatedAt are optional and are emitted only when present on the underlying node.

type CollectionListRequest

type CollectionListRequest struct {
	Limit  int
	Offset int
}

type CollectionListResponse

type CollectionListResponse struct {
	Collections []CollectionListEntry `json:"collections"`
	Showing     int                   `json:"showing"`
	Total       int                   `json:"total"`
	HasMore     bool                  `json:"has_more,omitempty"`
	NextOffset  int                   `json:"next_offset,omitempty"`
}

CollectionListResponse pagination matches the original map shape: HasMore + NextOffset are emitted only when a next page exists.

type CollectionMigrateRequest

type CollectionMigrateRequest struct {
	Field string `json:"field"`
	Value any    `json:"value"`
}

type CollectionMigrateResponse

type CollectionMigrateResponse struct {
	Migrated          int           `json:"migrated"`
	Field             string        `json:"field"`
	MigrationComplete bool          `json:"migration_complete"`
	Failed            []ItemFailure `json:"failed,omitempty"`
}

CollectionMigrateResponse: {migrated, field, migration_complete, failed?}.

Failed is the per-item failure list (P3-B). Today the migration loop has no per-item failure path -- the only loop branch is "node gone between collect and apply" which silently `continue`s -- so Failed is omitempty and currently nil-emitted. The slot is reserved for future partial-success work where caller-recoverable failures (validation drift, contended writes) are recorded alongside the items that did migrate.

type CollectionMigrationState

type CollectionMigrationState struct {
	Fields    []string `json:"fields"`
	Total     int64    `json:"total"`
	Done      int      `json:"done,omitempty"`
	Remaining int      `json:"remaining,omitempty"`
	Message   string   `json:"message,omitempty"`
}

CollectionMigrationState describes an in-progress schema migration. Returned inside CollectionItemsResponse, CollectionSchemaReadResponse, and CollectionSchemaUpdateResponse. Done/Remaining are only populated by CollectionItems (computed over the live member set); Message is only set by CollectionSchemaUpdate.

type CollectionMoveRequest

type CollectionMoveRequest struct {
	TargetCollectionID string `json:"target_collection_id"`
}

type CollectionMoveResponse

type CollectionMoveResponse struct {
	Moved            bool   `json:"moved"`
	ItemID           string `json:"item_id"`
	FromCollectionID string `json:"from_collection_id"`
	ToCollectionID   string `json:"to_collection_id"`
}

CollectionMoveResponse: {moved, item_id, from/to_collection_id}.

type CollectionRemoveResponse

type CollectionRemoveResponse struct {
	Removed      bool   `json:"removed"`
	ItemID       string `json:"item_id"`
	CollectionID string `json:"collection_id"`
}

CollectionRemoveResponse: {removed, item_id, collection_id}.

type CollectionRenameRequest

type CollectionRenameRequest struct {
	Name string `json:"name"`
}

type CollectionRenameResponse

type CollectionRenameResponse struct {
	Renamed bool   `json:"renamed"`
	ID      string `json:"id"`
	Name    string `json:"name"`
}

CollectionRenameResponse: {renamed, id, name}.

type CollectionSchema

type CollectionSchema struct {
	Fields        []SchemaField `json:"fields"`
	ContentFields []string      `json:"content_fields,omitempty" yaml:"content_fields,omitempty"`
}

CollectionSchema defines what fields items in a collection must/may have.

ContentFields is an ordered list of field names whose values constitute the canonical text representation of an item for LLM-stage curation (classify, summarize, contradictions, concept synthesis) and for embedding. Each name must reference a declared field of type=string. When unset, items in this collection use a schemaless wide concatenation of every field.* string property (acceptable for curation=none collections; rejected at create time for curation=standard collections lacking a template).

type CollectionSchemaReadResponse

type CollectionSchemaReadResponse struct {
	CollectionID string                    `json:"collection_id"`
	Schema       *CollectionSchema         `json:"schema,omitempty"`
	Migration    *CollectionMigrationState `json:"migration,omitempty"`
}

CollectionSchemaReadResponse: {collection_id, schema?, migration?}.

type CollectionSchemaUpdateRequest

type CollectionSchemaUpdateRequest struct {
	Schema CollectionSchema `json:"schema"`
}

type CollectionSchemaUpdateResponse

type CollectionSchemaUpdateResponse struct {
	Updated      bool                      `json:"updated"`
	CollectionID string                    `json:"collection_id"`
	Migration    *CollectionMigrationState `json:"migration,omitempty"`
}

CollectionSchemaUpdateResponse: {updated, collection_id, migration?}.

type CollectionUpdateRequest

type CollectionUpdateRequest struct {
	Fields map[string]any `json:"fields"`
}

type CollectionUpdateResponse

type CollectionUpdateResponse struct {
	Updated bool   `json:"updated"`
	ItemID  string `json:"item_id"`
}

CollectionUpdateResponse: {updated, item_id}.

type CommitSegment

type CommitSegment struct {
	Content         string   `json:"content"`
	TopicName       string   `json:"topic"`
	Temporality     string   `json:"temporality,omitempty"`
	Confidence      *float64 `json:"confidence,omitempty"`
	KnowledgeType   string   `json:"knowledge_type,omitempty"`
	EpistemicStatus string   `json:"epistemic_status,omitempty"`
	Keywords        []string `json:"keywords,omitempty"`
	SummaryShort    string   `json:"summary_short,omitempty"`
	PromoteToMemory *bool    `json:"promote_to_memory,omitempty"`
}

CommitSegment is a single segment submitted via session_commit.

PromoteToMemory implements the two-tier extraction model: when true (or unset), the segment becomes both a Session segment (BM25-indexed) and a Memory record (vector + BM25 indexed, full epistemic metadata, auto-supersession). When false, only the Session segment is created -- BM25-searchable but not vector-embedded, no Memory record, no extracted_as edge. Use false for exploration, dead ends, open questions, and other "valuable context" content that shouldn't pollute the Memory store's vector space.

type ConfidenceDist

type ConfidenceDist struct {
	High     int `json:"high"`     // >= 0.9
	Medium   int `json:"medium"`   // 0.7-0.9
	Moderate int `json:"moderate"` // 0.4-0.7
	Low      int `json:"low"`      // < 0.4
	Unset    int `json:"unset"`
}

ConfidenceDist groups records into confidence bands for summary.

type Contradictions

type Contradictions string

Contradictions controls whether the system generates contradicts edges between records in this collection and other records. The stage is LLM-driven, so off saves cost on collections where pairwise contradiction detection is meaningless (journal entries, bookmarks, recipe collections).

on (default) -- contradictions stage runs against records here.
off          -- contradictions stage skipped.
const (
	ContradictionsOn  Contradictions = "on"
	ContradictionsOff Contradictions = "off"
)

func CollectionContradictions

func CollectionContradictions(n *graph.Node) Contradictions

CollectionContradictions returns the collection's contradictions config, falling back to DefaultContradictions when absent.

type Curation

type Curation string

Curation controls per-collection LLM-analysis intensity. Each pipeline stage that consults this knob (classify, summarize, observation_extract, concept synthesis) runs only when the effective value resolves to standard.

standard (default) -- LLM analysis runs.
none               -- no LLM analysis runs. Records still get
                      embedded for vector search; supersession
                      and contradictions are governed by their
                      own knobs.

Legacy values "minimal" and "full" are normalized on read -- minimal -> none, full -> standard -- so existing collections keep working without a migration sweep. Writes reject the legacy values.

const (
	CurationStandard Curation = "standard"
	CurationNone     Curation = "none"
)

func CollectionCuration

func CollectionCuration(n *graph.Node) Curation

CollectionCuration returns the collection's curation profile, falling back to DefaultCuration when absent. Legacy values from the pre-redesign 4-level enum are normalized on read so existing stores keep working without a migration sweep.

type CurationBatchResponse

type CurationBatchResponse struct {
	Result *curation.BatchResult `json:"result"`
}

CurationBatchResponse wraps the BatchResult from the curation package so transport callers see the same shape regardless of surface.

type CurationDrainResponse

type CurationDrainResponse struct {
	Result *curation.DrainResult `json:"result"`
}

CurationDrainResponse reports the outcome of an artificial drain of the contradiction-detection candidate pool. See CurationDrainContradictions for the safety tradeoffs.

type CurationDryRunResponse

type CurationDryRunResponse struct {
	DryRun         bool                     `json:"dry_run"`
	PlannedChanges []curation.PlannedChange `json:"planned_changes"`
	Classified     int                      `json:"classified"`
	Summaries      int                      `json:"summaries"`
	LLMCalls       int                      `json:"llm_calls"`
	Errors         int                      `json:"errors"`
	Status         curation.EnhancedStatus  `json:"status"`
}

CurationDryRunResponse mirrors the trigger response but reports what the autonomous pipeline *would* have changed instead of applying the changes. Deterministic curation still ran (it is always safe).

type CurationListStuckResponse

type CurationListStuckResponse struct {
	Records []StuckRecord  `json:"records"`
	Counts  map[string]int `json:"counts"`
}

CurationListStuckResponse is the read-only stuck-record inventory. Records is the per-record breakdown; Counts is a per-task summary for callers that only need totals.

type CurationResetStuckRequest

type CurationResetStuckRequest struct {
	IDs []string `json:"ids,omitempty" jsonschema:"specific record IDs to reset; empty resets all stuck records"`
}

CurationResetStuckRequest selects which stuck records to un-stick. IDs empty = reset every stuck record. IDs non-empty = reset only those records (and only the ones in that set that are actually stuck; non-stuck IDs in the list are silently ignored).

type CurationResetStuckResponse

type CurationResetStuckResponse struct {
	Reset  int            `json:"reset"`
	Counts map[string]int `json:"counts"`
}

CurationResetStuckResponse reports how many records were reset and the per-task breakdown. Reset is the total across all tasks.

type CurationStatusResponse

type CurationStatusResponse struct {
	Status   curation.EnhancedStatus `json:"status"`
	Manifest *curation.StoreManifest `json:"manifest,omitempty"`
}

CurationStatusResponse reports the runner's current state plus the most-recent store manifest. Both fields may be nil when the runner has not yet completed its first cycle.

type CurationTriggerResponse

type CurationTriggerResponse struct {
	Triggered bool                    `json:"triggered"`
	Message   string                  `json:"message,omitempty"`
	Status    curation.EnhancedStatus `json:"status"`
}

CurationTriggerResponse reports whether a manual trigger was accepted (false when a cycle was already in progress) and the runner's status after the call.

type DeleteRecordRequest

type DeleteRecordRequest struct {
	ID     string `json:"-" jsonschema:"-"`
	Reason string `json:"reason,omitempty" jsonschema:"why the record is being deleted (stored on the record)"`
}

DeleteRecordRequest soft-deletes a record with an optional reason.

type DeleteRecordResponse

type DeleteRecordResponse struct {
	ID      string `json:"id"`
	Deleted bool   `json:"deleted"`
}

DeleteRecordResponse confirms the soft-delete.

type Dependencies

type Dependencies struct {
	Engine       *core.Engine
	Runner       *curation.Runner
	UsageTracker *llm.UsageTracker
	Log          *slog.Logger
	ConfigDir    string
	StoreName    string
}

Dependencies holds the collaborators an API needs at construction. Keeping this explicit (rather than letting methods reach into a god-object) makes it obvious what an operation depends on and gives tests a clean surface to stub.

type DiffEntry

type DiffEntry struct {
	ID           string `json:"id"`
	SummaryShort string `json:"summary_short,omitempty"`
}

DiffEntry is one record in the diff. SummaryShort is omitted when the record no longer exists (Removed entries) or has no short.

type DiffRequest

type DiffRequest struct {
	Since string `json:"since,omitempty" jsonschema:"show changes after date (YYYY-MM-DD or RFC3339); empty means against chain root"`
	Until string `json:"until,omitempty" jsonschema:"show changes up to date (YYYY-MM-DD or RFC3339); empty means up to HEAD"`
	Topic string `json:"topic,omitempty" jsonschema:"filter by topic substring (matches content_keywords + content_short, case-insensitive)"`
	Limit int    `json:"limit,omitempty" jsonschema:"max changes to return (default 50, max 1000)"`
}

DiffRequest carries the diff window + optional topic filter. Since is empty -> diff against the chain root (full history). Until is empty -> diff up to HEAD. Both accept YYYY-MM-DD or RFC3339.

type DiffResponse

type DiffResponse struct {
	Added     []DiffEntry `json:"added"`
	Modified  []DiffEntry `json:"modified"`
	Removed   []DiffEntry `json:"removed"`
	Truncated bool        `json:"truncated,omitempty"`
	Limit     int         `json:"limit,omitempty"`
}

DiffResponse partitions the changed nodes into added/modified/ removed buckets. Modified means the node ID appeared in both the added and removed sets at the prolly-tree level (content hash changed).

type DuplicatesRequest

type DuplicatesRequest struct {
	Threshold float64 `json:"threshold,omitempty" jsonschema:"minimum cosine similarity (default 0.92, must be > 0 and <= 1)"`
	MaxPairs  int     `json:"max_pairs,omitempty" jsonschema:"maximum pair count to return (default 50, max 1000)"`
}

DuplicatesRequest carries the similarity threshold + pair cap.

type DuplicatesResponse

type DuplicatesResponse struct {
	Pairs     []search.DuplicatePair `json:"pairs"`
	Count     int                    `json:"count"`
	Truncated bool                   `json:"truncated,omitempty"`
}

DuplicatesResponse lists duplicate pairs.

type EdgeAdded

type EdgeAdded struct {
	Index    int     `json:"index"`
	EdgeID   string  `json:"edge_id"`
	SourceID string  `json:"source_id"`
	TargetID string  `json:"target_id"`
	Type     string  `json:"type"`
	Weight   float64 `json:"weight"`
}

EdgeAdded describes one edge that was successfully created. Index maps back to the original Edges slice so callers can correlate.

type EdgeFailure

type EdgeFailure struct {
	Index   int    `json:"index"`
	Code    string `json:"code"`
	Message string `json:"message"`
}

EdgeFailure describes one edge that did NOT commit. Code is one of:

  • source_item_failed / target_item_failed: the referenced ClientRef points at an item whose Phase 0 validation failed
  • source_id_not_found / target_id_not_found: the referenced ID (or ClientRef) doesn't resolve to any record
  • self_loop: source and target resolve to the same node
  • duplicate_edge: same (source, target, type) tuple appears earlier in the batch
  • invalid_type / invalid_weight: per-edge shape rejected
  • missing_endpoint: neither id nor client_ref supplied for an endpoint, or both supplied for the same endpoint

type EdgeSpec

type EdgeSpec struct {
	SourceID        string   `json:"source_id,omitempty" jsonschema:"existing record id OR id assigned to a successful batch item"`
	SourceClientRef string   `json:"source_client_ref,omitempty" jsonschema:"client_ref of a batch item (mutually exclusive with source_id)"`
	TargetID        string   `json:"target_id,omitempty" jsonschema:"existing record id OR id assigned to a successful batch item"`
	TargetClientRef string   `json:"target_client_ref,omitempty" jsonschema:"client_ref of a batch item (mutually exclusive with target_id)"`
	Type            string   `json:"type" jsonschema:"edge type (e.g. related_to, supports, contradicts)"`
	Weight          *float64 `json:"weight,omitempty" jsonschema:"0.0-1.0; default 0.5"`
}

EdgeSpec describes a single edge to create alongside the batch's items. Exactly one of (SourceID, SourceClientRef) must be set per endpoint; same for target. ID resolves to an existing record OR a successful item from this batch. ClientRef resolves only to a successful item in this batch.

type ExploreRequest

type ExploreRequest struct {
	NodeID    string   `json:"node_id" jsonschema:"starting record ID"`
	Depth     int      `json:"depth,omitempty" jsonschema:"max traversal depth (default 2, max 10)"`
	EdgeTypes []string `json:"edge_types,omitempty" jsonschema:"restrict to these edge types (empty = all)"`
	MinWeight float64  `json:"min_weight,omitempty" jsonschema:"drop edges below this weight (default 0.0)"`
	MaxNodes  int      `json:"max_nodes,omitempty" jsonschema:"cap result node count (default 100, max 10000)"`
}

ExploreRequest traverses the graph from a starting node.

type ExploreResponse

type ExploreResponse struct {
	Nodes     []graph.SubgraphNode `json:"nodes"`
	Edges     []graph.SubgraphEdge `json:"edges"`
	Truncated bool                 `json:"truncated,omitempty"`
	MaxNodes  int                  `json:"max_nodes,omitempty"`
}

ExploreResponse carries the traversal result subgraph.

type ExportRequest

type ExportRequest struct {
	Format string `` /* 150-byte string literal not displayed */

	// Filter fields. Additive; absent fields mean "no filter on
	// this dimension."
	Text             string            `json:"text,omitempty" jsonschema:"vector-similarity query; ranks results by relevance (otherwise sorted by created_at desc)"`
	Match            string            `json:"match,omitempty" jsonschema:"literal substring match across content fields (case-insensitive)"`
	Store            string            `json:"store,omitempty" jsonschema:"memory|sessions|all (default: all)"`
	Keywords         []string          `json:"keywords,omitempty" jsonschema:"keywords that must all be present"`
	Temporality      string            `json:"temporality,omitempty"`
	KnowledgeType    string            `json:"knowledge_type,omitempty"`
	EpistemicStatus  string            `json:"epistemic_status,omitempty"`
	Resolution       string            `json:"resolution,omitempty"`
	ProcessingStatus string            `json:"processing_status,omitempty"`
	Since            string            `json:"since,omitempty" jsonschema:"YYYY-MM-DD or RFC3339 lower bound on created_at"`
	Meta             map[string]string `json:"meta,omitempty"`
}

ExportRequest controls a streaming record export. Filter fields mirror SearchRequest's most useful subset; when none are set the request behaves like a full-store dump (legacy compatibility). When any filter is set, only matching records are exported.

The export is exhaustive over the matched set -- there is no candidate_cap or pagination on this path. The CLI is the "give me everything" escape valve from gramaton_search's MCP-side pagination.

type FaultInjector

type FaultInjector interface {
	Inject(phase string) error
}

FaultInjector is the test-only fault-injection seam. Each long-running operation calls Inject at named phases; a non-nil returned error short-circuits the operation along its error path. The interface is exported so external test packages can provide implementations, but the SetFaultInjector setter is intended for in-package tests only.

type HistoryChange

type HistoryChange struct {
	Commit    string `json:"commit"`
	Timestamp string `json:"timestamp"`
	Action    string `json:"action"`
}

HistoryChange represents a single change event for a record.

type HistoryRequest

type HistoryRequest struct {
	ID    string `json:"-" jsonschema:"-"`
	Limit int    `json:"limit,omitempty" jsonschema:"max entries (default 20, max 500)"`
	Since string `json:"since,omitempty" jsonschema:"only include changes on or after this date (YYYY-MM-DD or RFC3339)"`
	Until string `json:"until,omitempty" jsonschema:"only include changes up to this date (YYYY-MM-DD or RFC3339); empty means up to HEAD"`
}

HistoryRequest returns the per-record change history. ID is transport-set from the URL path. Limit defaults to 20 and is clamped to MaxLogLimit. Since and Until narrow the walk to a date range and bypass MaxLogTraversal when set (the timestamp index gives us O(log N) entry points).

type HistoryResponse

type HistoryResponse struct {
	ID      string          `json:"id"`
	Changes []HistoryChange `json:"changes"`
}

HistoryResponse lists the changes in reverse-chronological order (most recent first).

type ImportRequest

type ImportRequest struct {
	Records []backup.ExportRecord `json:"records"`
}

type ImportResponse

type ImportResponse struct {
	Imported int `json:"imported"`
	Skipped  int `json:"skipped"`
	Errors   int `json:"errors"`
}

type InspectRequest

type InspectRequest struct {
	ID             string `json:"id" jsonschema:"record ID to inspect"`
	IncludeContent *bool  `json:"include_content,omitempty" jsonschema:"include content_full in response (default true)"`
}

InspectRequest identifies the record to inspect and whether to include the full content in the response.

type InspectResponse

type InspectResponse struct {
	ID              string         `json:"id"`
	Properties      map[string]any `json:"properties"`
	MetadataSummary string         `json:"metadata_summary"`
	Related         []RelatedEdge  `json:"related"`

	// EffectiveCuration is the resolved per-record curation behaviour
	// computed from the node's member_of edges. Tells callers exactly
	// what curation work will run on this record (curation, supersession,
	// contradictions). Absent on structural/container nodes (collections,
	// sessions, topics) and concept-synthesis nodes -- those are not
	// records that flow through curation.
	EffectiveCuration *curation.EffectiveConfig `json:"effective_curation,omitempty"`
}

InspectResponse is the full view of a record.

type ItemFailure

type ItemFailure struct {
	Index   int    `json:"index"`
	ItemID  string `json:"item_id,omitempty"`
	Code    string `json:"code"`
	Message string `json:"message"`
}

ItemFailure is the per-item failure shape used by SessionCommit and CollectionMigrate (and structurally compatible with BatchAddFailure in the AddBatch path). Index correlates to the caller's input position; ItemID correlates to a pre-existing record the operation iterated over.

type JobSummary

type JobSummary struct {
	ID             string    `json:"id"`
	Kind           string    `json:"kind"`
	Status         string    `json:"status"`
	CreatedAt      time.Time `json:"created_at"`
	StartedAt      time.Time `json:"started_at,omitempty"`
	CompletedAt    time.Time `json:"completed_at,omitempty"`
	TotalItems     int       `json:"total_items"`
	ProcessedCount int       `json:"processed_count"`
	FailureReason  string    `json:"failure_reason,omitempty"`
}

JobSummary mirrors jobs.JobSummary at the api/ layer so callers don't have to import the internal jobs package types.

ClientToken is intentionally NOT included here. Listing other tenants' tokens (in the multi-tenant future) would let one caller guess another's idempotency window. A caller that wants to look up their own jobs by token uses CaptureBatchStatus on a known JobID, or filters JobsList by their own ClientToken via the request.

type JobsListRequest

type JobsListRequest struct {
	Status      string `json:"status,omitempty" jsonschema:"pending|running|completed|failed|cancelled (single status; omit for all)"`
	Kind        string `json:"kind,omitempty" jsonschema:"e.g. capture_batch (max 64 chars; omit for all kinds)"`
	ClientToken string `json:"client_token,omitempty" jsonschema:"exact-match UUID; scoped to the caller's tenant"`
	Since       string `json:"since,omitempty" jsonschema:"RFC3339 inclusive lower-bound on created_at; max 64 chars"`
	Until       string `json:"until,omitempty" jsonschema:"RFC3339 inclusive upper-bound on created_at; max 64 chars"`
	Limit       int    `json:"limit,omitempty" jsonschema:"page size (1..200, default 50)"`
	Offset      int    `json:"offset,omitempty" jsonschema:"pagination offset (0..100000)"`
}

JobsListRequest filters and paginates the JobStore. Empty fields are unconstrained except TenantID, which is always read from context and never accepted from the wire (a tenant cannot list another tenant's jobs by passing their id).

type JobsListResponse

type JobsListResponse struct {
	Jobs   []JobSummary `json:"jobs"`
	Total  int          `json:"total"`
	Limit  int          `json:"limit"`
	Offset int          `json:"offset"`
}

JobsListResponse is the lightweight summary projection. Heavy per-item fields (Result, ClientRefToID) are intentionally omitted here; callers that need them follow up with gramaton_capture_batch_status.

type LinkRequest

type LinkRequest struct {
	SourceID   string   `json:"-" jsonschema:"-"`
	TargetID   string   `json:"target_id" jsonschema:"destination record ID"`
	EdgeType   string   `json:"edge_type" jsonschema:"relationship name (e.g. related_to, supports, contradicts)"`
	EdgeWeight *float64 `json:"edge_weight,omitempty" jsonschema:"0.0-1.0, default 0.5"`
}

LinkRequest creates an edge from the source record to a target. SourceID is transport-set from the URL path. EdgeWeight is optional (default 0.5) and must be in [0.0, 1.0].

type LinkResponse

type LinkResponse struct {
	ID      string `json:"id"`
	EdgeID  string `json:"edge_id"`
	Updated bool   `json:"updated"`
}

LinkResponse returns the source record's ID + the newly created edge ID.

type LogEntry

type LogEntry struct {
	Hash               string            `json:"hash"`
	Timestamp          string            `json:"timestamp"`
	Action             string            `json:"action"`
	Mutations          []MutationSummary `json:"mutations,omitempty"`
	MutationsTruncated bool              `json:"mutations_truncated,omitempty"`
}

LogEntry is one commit in the chain. Hash is truncated to 12 characters to keep CLI/MCP output readable; full hashes can be fetched via History when needed. Mutations is populated when IncludeRecordMutations=true; MutationsTruncated flags when the per-commit cap was hit.

type LogRequest

type LogRequest struct {
	Limit                  int      `json:"limit,omitempty" jsonschema:"max entries (default 20, max 500)"`
	Since                  string   `json:"since,omitempty" jsonschema:"only include commits on or after this date (YYYY-MM-DD or RFC3339)"`
	Until                  string   `json:"until,omitempty" jsonschema:"only include commits up to this date (YYYY-MM-DD or RFC3339); empty means up to HEAD"`
	Actions                []string `` /* 185-byte string literal not displayed */
	ExcludeCuration        bool     `` /* 197-byte string literal not displayed */
	IncludeRecordMutations bool     `` /* 261-byte string literal not displayed */
}

LogRequest carries the commit-walk knobs. Since and Until narrow the walk to a date range (both optional, accept YYYY-MM-DD or RFC3339); when set they bypass MaxLogTraversal because the D7 timestamp index gives range scans directly. Actions filter by structured CommitAction.Kind (Phase 3 D3); ExcludeCuration filters via Message-prefix matching so it works against pre-D3 commits. IncludeRecordMutations enriches each returned commit with per- record mutation summaries, saving a follow-up gramaton_inspect round-trip for the common "what records changed yesterday" flow.

type LogResponse

type LogResponse struct {
	Commits []LogEntry `json:"commits"`
}

LogResponse holds commit entries newest-first.

type MutationSummary

type MutationSummary struct {
	RecordID     string `json:"record_id,omitempty"`
	Kind         string `json:"kind"`
	Field        string `json:"field,omitempty"`
	Title        string `json:"title,omitempty"`
	SummaryShort string `json:"summary_short,omitempty"`
}

MutationSummary is the enriched per-record description included in LogEntry when IncludeRecordMutations=true. Title and SummaryShort are pulled from the current HEAD record (may be absent if the record was deleted since); both are omitempty so JSON stays lean.

type PageRef

type PageRef struct {
	Range  string `json:"range"`
	Cursor string `json:"cursor"`
}

PageRef points at one page's worth of results within a search snapshot. The Range is a human-readable 1-indexed slice description; the Cursor is the opaque token to pass on a subsequent call to retrieve that page.

type PendingRecord

type PendingRecord struct {
	ID           string `json:"id"`
	SummaryShort string `json:"summary_short,omitempty"`
	CreatedAt    string `json:"created_at,omitempty"`
}

PendingRecord is a lightweight row in a pending listing.

type PendingRequest

type PendingRequest struct {
	Limit int `json:"limit,omitempty" jsonschema:"max records to return (default 50, max 500)"`
}

PendingRequest limits how many pending records to return.

type PendingResponse

type PendingResponse struct {
	Records   []PendingRecord `json:"records"`
	Total     int             `json:"total"`
	Truncated bool            `json:"truncated,omitempty"`
}

PendingResponse reports the pending records and whether the result was truncated.

type ReembedRequest

type ReembedRequest struct {
	Batch int `json:"batch,omitempty" jsonschema:"max records to process (default 50, max 500)"`
}

ReembedRequest bounds how many records the cycle processes per call. Reembed is idempotent and pageable: callers can drive the store to completion by looping until reembedded+errors == 0.

type ReembedResponse

type ReembedResponse struct {
	Reembedded int      `json:"reembedded"`
	Skipped    int      `json:"skipped"`
	Errors     int      `json:"errors"`
	ErrorIDs   []string `json:"error_ids,omitempty"`
}

ReembedResponse summarises one batch. ErrorIDs is omitted when no records failed.

type RelatedEdge

type RelatedEdge struct {
	ID           string  `json:"id"` // the other node's ID
	EdgeType     string  `json:"edge_type"`
	EdgeID       string  `json:"edge_id"`
	EdgeWeight   float64 `json:"edge_weight"`
	Direction    string  `json:"direction"` // "outbound" or "inbound"
	SummaryShort string  `json:"summary_short,omitempty"`
}

RelatedEdge describes an edge connected to an inspected record.

type ResolveRequest

type ResolveRequest struct {
	ID                        string `json:"-" jsonschema:"-"`
	Resolution                string `json:"resolution" jsonschema:"completed|superseded|abandoned|obsolete"`
	ResolutionNote            string `json:"resolution_note,omitempty" jsonschema:"free-form note about why/how"`
	AutoCloseCollectionStatus *bool  `` /* 160-byte string literal not displayed */
}

ResolveRequest marks a record as resolved (completed, superseded, abandoned, or obsolete) with an optional note.

AutoCloseCollectionStatus defaults to true when nil. Set to false when the caller wants to expire the record at the Memory layer but keep the collection item visible in the open view (rare; useful when manually staging a multi-step close).

type ResolveResponse

type ResolveResponse struct {
	ID                string            `json:"id"`
	Resolved          bool              `json:"resolved"`
	AutoClosedStatus  map[string]string `json:"auto_closed_status,omitempty"`
	CollectionWarning string            `json:"collection_warning,omitempty"`
}

ResolveResponse confirms the resolution. AutoClosedStatus reports any collection items whose schema-declared `status` enum field was also flipped to a closed-equivalent value (mapped from the resolution verb). CollectionWarning fires when the record is in a collection where the auto-close heuristic could not find a matching status field or value -- the caller should fall back to gramaton_collection_update for those.

type RestoreRequest

type RestoreRequest struct {
	Path  string `json:"path" jsonschema:"absolute path to .tar.gz archive"`
	Force bool   `json:"force" jsonschema:"must be true -- restore overwrites the current store"`
}

type RestoreResponse

type RestoreResponse struct {
	Restored bool   `json:"restored"`
	Path     string `json:"path"`
}

type RetrievalTracker

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

RetrievalTracker records which node IDs were served to agents via search/inspect/explore. The observe pipeline's feedback-loop detection reads this to avoid re-extracting knowledge that was just retrieved. Moved from server.Server because the track-serving path is now owned by api methods (formerly serviceSearch/serviceInspect/ serviceExplore lived on *Server and called s.retrieval).

func NewRetrievalTracker

func NewRetrievalTracker() *RetrievalTracker

NewRetrievalTracker returns a tracker with the historical defaults (4h max age, 500 max size). Caller can adjust via SetBounds.

func (*RetrievalTracker) Len

func (rt *RetrievalTracker) Len() int

Len returns the current tracked entry count (after pruning expired entries).

func (*RetrievalTracker) RetrievedIDs

func (rt *RetrievalTracker) RetrievedIDs() []string

RetrievedIDs returns all currently tracked, not-yet-expired IDs.

func (*RetrievalTracker) Track

func (rt *RetrievalTracker) Track(ids ...string)

Track records that the given IDs were served to an agent.

type SchemaField

type SchemaField struct {
	Name     string          `json:"name"`
	Type     SchemaFieldType `json:"type"`
	Required bool            `json:"required"`
	Values   []string        `json:"values,omitempty"` // enum and enum[] only
}

SchemaField defines a single field in a collection schema.

type SchemaFieldType

type SchemaFieldType string

SchemaFieldType identifies the type of a collection schema field.

const (
	FieldTypeString  SchemaFieldType = "string"
	FieldTypeNumber  SchemaFieldType = "number"
	FieldTypeBoolean SchemaFieldType = "boolean"
	FieldTypeDate    SchemaFieldType = "date"
	FieldTypeEnum    SchemaFieldType = "enum"
	FieldTypeEnumSet SchemaFieldType = "enum[]"
)

type SearchRequest

type SearchRequest struct {
	Text               string            `json:"text,omitempty" jsonschema:"search query text (optional -- omit for filter-only queries)"`
	Top                int               `json:"top,omitempty" jsonschema:"integer, number of results (default 10)"`
	Temporality        string            `` /* 130-byte string literal not displayed */
	KnowledgeType      string            `` /* 129-byte string literal not displayed */
	EpistemicStatus    string            `` /* 137-byte string literal not displayed */
	Resolution         string            `` /* 132-byte string literal not displayed */
	ProcessingStatus   string            `` /* 213-byte string literal not displayed */
	ConfidenceMin      *float64          `json:"confidence_min,omitempty" jsonschema:"0.0-1.0"`
	ConfidenceMax      *float64          `json:"confidence_max,omitempty" jsonschema:"0.0-1.0"`
	ImportanceMin      *float64          `json:"importance_min,omitempty" jsonschema:"0.0-1.0"`
	ImportanceMax      *float64          `json:"importance_max,omitempty" jsonschema:"0.0-1.0"`
	IncludeHistorical  bool              `json:"include_historical,omitempty" jsonschema:"include records past valid_until"`
	IncludeConcepts    bool              `` /* 366-byte string literal not displayed */
	Since              string            `json:"since,omitempty" jsonschema:"filter: created after date (YYYY-MM-DD or RFC3339)"`
	Missing            []string          `json:"missing,omitempty" jsonschema:"array of field names that must be unset (e.g. [temporality, confidence])"`
	Keywords           []string          `json:"keywords,omitempty" jsonschema:"array of keywords that must all be present on the record (exact match)"`
	AccessCountMin     *int64            `json:"access_count_min,omitempty" jsonschema:"integer, minimum access count"`
	AccessCountMax     *int64            `json:"access_count_max,omitempty" jsonschema:"integer, maximum access count"`
	LastAccessedAfter  string            `json:"last_accessed_after,omitempty" jsonschema:"filter: accessed after date (YYYY-MM-DD or RFC3339)"`
	LastAccessedBefore string            `json:"last_accessed_before,omitempty" jsonschema:"filter: accessed before date (YYYY-MM-DD or RFC3339)"`
	ValidAfter         string            `json:"valid_after,omitempty" jsonschema:"filter: valid_from after date"`
	ValidBefore        string            `json:"valid_before,omitempty" jsonschema:"filter: valid_from before date"`
	ExpiresAfter       string            `json:"expires_after,omitempty" jsonschema:"filter: valid_until after date (find records expiring after X)"`
	ExpiresBefore      string            `json:"expires_before,omitempty" jsonschema:"filter: valid_until before date (find records expiring before X)"`
	Match              string            `` /* 141-byte string literal not displayed */
	SimilarTo          string            `json:"similar_to,omitempty" jsonschema:"record ID -- find records similar to this one using its stored embedding"`
	NearNode           string            `json:"near_node,omitempty" jsonschema:"record ID -- restrict results to records within MaxHops of this node in the graph"`
	MaxHops            int               `json:"max_hops,omitempty" jsonschema:"integer, max BFS hops from NearNode (default unbounded when NearNode set)"`
	MinEdges           *int              `json:"min_edges,omitempty" jsonschema:"integer, minimum total edge count (orphan detection: max_edges=0)"`
	MaxEdges           *int              `json:"max_edges,omitempty" jsonschema:"integer, maximum total edge count"`
	Random             bool              `json:"random,omitempty" jsonschema:"return random results (ignores sort/score). Useful for serendipitous discovery or review"`
	Sort               string            `` /* 192-byte string literal not displayed */
	Order              string            `json:"order,omitempty" jsonschema:"asc or desc (default: desc)"`
	Meta               map[string]string `json:"meta,omitempty" jsonschema:"filter by structured metadata (e.g. {assignee: Sarah Chen, status: in_progress})"`
	Store              string            `json:"store,omitempty" jsonschema:"filter by store: memory|sessions|all (default: all)"`

	// Pagination fields. Cursor takes precedence: when set, all
	// other filter args are ignored (the cursor encodes the slice
	// against a previously-materialized snapshot) and the response
	// carries `ignored_params` listing what was dropped. PageSize
	// applies to both fresh searches and cursor calls.
	Cursor   string `` /* 228-byte string literal not displayed */
	PageSize int    `json:"page_size,omitempty" jsonschema:"results per page (default from server config; capped at server-side max)."`
}

SearchRequest is the canonical search input. Most fields are filters evaluated inside search.Execute; see package search for semantics.

type SearchResponse

type SearchResponse struct {
	Results     []search.Result     `json:"results"`
	Facets      search.Facets       `json:"facets,omitempty"`
	Suggestions *search.Suggestions `json:"suggestions,omitempty"`
	// Warnings surfaces non-fatal degradations -- e.g. the query
	// embedder failed so the request fell back to BM25-only, or the
	// underlying match set exceeded the snapshot's candidate cap.
	// Callers can decide whether to surface them to the user. Empty
	// on the happy path.
	Warnings []string `json:"warnings,omitempty"`

	// Pagination fields. Always populated for fresh searches;
	// populated for cursor calls based on the looked-up snapshot.
	Page       int       `json:"page,omitempty"`
	PageSize   int       `json:"page_size,omitempty"`
	Total      int       `json:"total,omitempty"`
	NextCursor string    `json:"next_cursor,omitempty"`
	QueryID    string    `json:"query_id,omitempty"`
	Pages      []PageRef `json:"pages,omitempty"`

	// IgnoredParams names request fields that were dropped because
	// a Cursor was provided. Empty on fresh searches; non-empty
	// only on cursor calls where the request also carried filter
	// args.
	IgnoredParams []string `json:"ignored_params,omitempty"`
}

SearchResponse carries ranked results plus aggregates (facets, refinement suggestions) plus pagination scaffolding.

type SessionCommitResponse

type SessionCommitResponse struct {
	SessionID            string           `json:"session_id"`
	SegmentsAdded        int              `json:"segments_added"`
	SessionOnlySegments  int              `json:"session_only_segments"`
	TopicsCreated        int              `json:"topics_created"`
	MemoryRecordsCreated int              `json:"memory_records_created"`
	EdgesCreated         int              `json:"edges_created"`
	Superseded           []map[string]any `json:"superseded,omitempty"`
	Failed               []ItemFailure    `json:"failed,omitempty"`
}

SessionCommitResponse pins SessionCommit's previous map[string]any shape. The legacy map emits session_only_segments unconditionally, so it has no omitempty here. Superseded is omitempty (emitted only when at least one record got auto-superseded by a commit segment). Failed is the per-segment failure list reserved for future partial-success work (see CollectionMigrateResponse.Failed for the same disclaimer).

type StatsResponse

type StatsResponse struct {
	TotalRecords    int            `json:"total_records"`
	Temporality     map[string]int `json:"temporality"`
	KnowledgeType   map[string]int `json:"knowledge_type"`
	EpistemicStatus map[string]int `json:"epistemic_status"`
	Confidence      ConfidenceDist `json:"confidence"`
}

StatsResponse summarises the knowledge store's composition.

Counts include ALL non-deleted, non-structural-child nodes: user records AND derived concept nodes. The manifest produced by curation (`records_by_type` etc.) deliberately excludes concept nodes since concepts are derived from clusters rather than captured by the user; both counts are correct under their own semantics.

type StatusRequest

type StatusRequest struct{}

StatusRequest has no inputs.

type StatusResponse

type StatusResponse struct {
	Nodes     int  `json:"nodes"`
	Edges     int  `json:"edges"`
	Embedding bool `json:"embedding"`
}

StatusResponse summarises server health at a glance.

type StuckRecord

type StuckRecord struct {
	ID    string `json:"id"`
	Task  string `json:"task"`
	Error string `json:"error,omitempty"`
}

StuckRecord identifies a single record-task pair flagged as stuck. A record can in principle appear under multiple tasks if it has multiple per-task status fields stuck simultaneously, though in practice each task targets a different node shape.

type SupersededRecord

type SupersededRecord struct {
	ID         string  `json:"id"`
	Summary    string  `json:"summary,omitempty"`
	Similarity float64 `json:"similarity"`
	EdgeID     string  `json:"edge_id"`
}

SupersededRecord describes a record that Capture automatically marked as historical because the new record was near-duplicate.

type Supersession

type Supersession string

Supersession controls the candidate scope for auto-supersession on short-content records.

collection (default) -- only records sharing this collection
                        participate in same-collection supersession.
                        Cross-collection collisions ("eggs" in
                        Grocery vs "eggs" in Recipes) don't fire.
store                -- legacy behaviour: any record in the store
                        is a candidate. Default for memory orphans.
none                 -- collection's items never participate in
                        auto-supersession.
const (
	SupersessionCollection Supersession = "collection"
	SupersessionStore      Supersession = "store"
	SupersessionNone       Supersession = "none"
)

func CollectionSupersession

func CollectionSupersession(n *graph.Node) Supersession

CollectionSupersession returns the collection's supersession config, falling back to DefaultSupersession when absent.

type Template

type Template struct {
	Name           string            `yaml:"name"`
	Description    string            `yaml:"description,omitempty"`
	Schema         *CollectionSchema `yaml:"schema,omitempty"`
	ClearMode      string            `yaml:"clear_mode,omitempty"`
	Supersession   string            `yaml:"supersession,omitempty"`
	Curation       string            `yaml:"curation,omitempty"`
	Contradictions string            `yaml:"contradictions,omitempty"`
}

Template is a reusable collection shape -- schema + behaviour knobs -- that CollectionCreate can instantiate by name. Templates ship embedded in the binary so fresh stores have them available without extra provisioning, and so callers can name them without worrying about filesystem paths.

Template fields mirror the fields on CollectionCreateRequest so the merge at CollectionCreate time is a plain "non-empty wins" shallow merge -- whatever the caller passes explicitly overrides the template's default.

func LookupTemplate

func LookupTemplate(name string) (*Template, bool)

LookupTemplate returns a named template from the embedded registry. The second return is false when no template matches. Lazy-initialises the registry on first call; subsequent calls are a bare map lookup.

type UnlinkRequest

type UnlinkRequest struct {
	EdgeID string `json:"-" jsonschema:"-"`
}

UnlinkRequest identifies an edge to remove by its edge ID.

type UnlinkResponse

type UnlinkResponse struct {
	EdgeID  string `json:"edge_id"`
	Deleted bool   `json:"deleted"`
}

UnlinkResponse confirms deletion.

type UpdateRequest

type UpdateRequest struct {
	ID              string         `json:"-" jsonschema:"-"` // transport-set
	Confidence      *float64       `json:"confidence,omitempty" jsonschema:"0.0-1.0"`
	Temporality     string         `json:"temporality,omitempty" jsonschema:"immutable|durable|temporal|ephemeral"`
	KnowledgeType   string         `json:"knowledge_type,omitempty" jsonschema:"episodic|semantic|procedural|conceptual|reference"`
	EpistemicStatus string         `json:"epistemic_status,omitempty" jsonschema:"well_established|probable|speculative|contested|refuted"`
	Importance      *float64       `json:"importance,omitempty" jsonschema:"0.0-1.0"`
	Keywords        []string       `json:"keywords,omitempty" jsonschema:"array of keyword strings"`
	SummaryShort    string         `json:"summary_short,omitempty" jsonschema:"~750 chars (semantic anchor for embedding)"`
	ValidUntil      string         `` /* 135-byte string literal not displayed */
	AssertedAsOf    string         `json:"asserted_as_of,omitempty" jsonschema:"when the source made this claim (YYYY-MM-DD or RFC3339)"`
	Meta            map[string]any `json:"meta,omitempty" jsonschema:"structured metadata (e.g. {assignee: Sarah, status: done})"`
}

UpdateRequest is the input to the update operation. ID is set by the transport from the URL path / tool args and is not part of the HTTP request body.

type UpdateResponse

type UpdateResponse struct {
	ID                string `json:"id"`
	Updated           bool   `json:"updated"`
	CollectionWarning string `json:"collection_warning,omitempty"`
}

UpdateResponse carries the id that was updated, whether any field actually changed, and an optional warning when updating a record that's a collection member.

Jump to

Keyboard shortcuts

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