export interface ArtifactEdge { from: string; relation: string; to: string; } export interface Artifact { id: string; kind: string; title: string; status: string; subtype: string; body: string; labels: string[]; extra: Record; created_at: string; updated_at: string; /** Short, globally-unique, human/agent-typeable name -- see domain/artifact-alias.ts. Always present once created; the id remains the true backend identity. */ alias: string; edges?: ArtifactEdge[]; } /** * A list operation's default projection: everything needed to identify, browse, and pick * one artifact out of many, without its body/extra -- the same fields show()'s own full * Artifact carries minus the two that make listing dozens of rows as expensive as showing * each one individually (a Playbook's full runbook body, a Rule's condition/action text). */ export interface ArtifactSummary { id: string; kind: string; title: string; status: string; subtype: string; labels: string[]; created_at: string; updated_at: string; alias: string; } export function summarizeArtifact(artifact: Artifact): ArtifactSummary { const { id, kind, title, status, subtype, labels, created_at, updated_at, alias } = artifact; return { id, kind, title, status, subtype, labels, created_at, updated_at, alias }; } export interface CreateArtifactInput { kind?: string; title?: string; status?: string; body?: string; labels?: string[]; extra?: Record; id?: string; subtype?: string; templateId?: string; /** Overrides the title-derived auto-generated alias -- must be unique and match isValidAlias's format, or creation throws a real conflict/validation error. */ alias?: string; } export interface UpdateArtifactInput { title?: string; body?: string; labels?: string[]; alias?: string; } export interface ArtifactQueryCursor { createdAt: string; id: string; } export interface ArtifactQuery { kind?: string; status?: string; statuses?: string[]; subtype?: string; excludeSubtype?: string; text?: string; labels?: string[]; extraEquals?: Record; limit?: number; /** Stable inventory ordering, independent of content updates. Defaults to updated_at descending. */ order?: "updated_desc" | "created_desc"; /** Exclusive keyset cursor for created_desc ordering. */ after?: ArtifactQueryCursor; /** Trashed artifacts (see artifact-trash.ts) are excluded from every query by default; set true to include them, e.g. for a trash-listing view. */ includeTrashed?: boolean; /** * Restrict to exactly these ids, still subject to every other filter (kind, trash exclusion, * etc.) -- for a caller that already has a bounded candidate id set (e.g. Tasks.list's * project/graph scope) and needs query()'s own trash-exclusion without a full-kind scan. * Empty array is a real "match nothing", not "unset". */ ids?: string[]; } export interface ArtifactGraphOptions { tree?: boolean; depth?: number; maxNodes?: number; } export interface ArtifactLink { from: string; relation: string; to: string; } export interface RelationshipQuery { kind?: string; artifactIds?: string[]; limit?: number; } /** * A label of the form "source:" marks an artifact as ingested/projected from an * external, non-Papyrus system (e.g. web-spider's own "source:web-spider" convention on the * Docs it creates) -- content Papyrus does not own and cannot safely rewrite without silently * diverging from the true source. Editing one directly would look like a correction but really * just be a local fork nobody re-syncs. */ export const EXTERNAL_SOURCE_LABEL_PREFIX = "source:"; /** The external system name from a "source:" label, or undefined if this artifact has no such label (i.e. it's Papyrus-native content). */ export function externalSourceOf(artifact: Pick): string | undefined { const label = artifact.labels.find((entry) => entry.startsWith(EXTERNAL_SOURCE_LABEL_PREFIX)); return label === undefined ? undefined : label.slice(EXTERNAL_SOURCE_LABEL_PREFIX.length) || undefined; } /** Throws if the artifact is a read-only external projection; a caller must never silently rewrite content it doesn't own the source of. */ export function requireLocallyOwnedContent(artifact: Artifact): Artifact { const system = externalSourceOf(artifact); if (system !== undefined) { throw new Error( `"${artifact.title}" is a read-only projection from ${system}; edit it there, or capture a correction as a new linked Doc, until a write-back capability is integrated`, ); } return artifact; }