export type FileNodeType = "file" | "dir"; /** * JWT claims expected by the Relayfile Go API when your token provider mints * bearer tokens for SDK requests. * * Example minting payload: * `const claims: RelayFileJwtClaims = { workspace_id: "ws_123", agent_name: "review-bot", aud: ["relayfile"] };` */ export interface RelayFileJwtClaims { /** Workspace id the token is scoped to, for example `ws_123`. */ workspace_id: string; /** Stable agent name presented to the Relayfile server, for example `review-bot`. */ agent_name: string; /** Audience must contain `relayfile`. */ aud: "relayfile" | string[]; exp?: number; iat?: number; nbf?: number; iss?: string; sub?: string; [claim: string]: unknown; } export interface TreeEntry { path: string; type: FileNodeType; revision: string; provider?: string; providerObjectId?: string; size?: number; updatedAt?: string; propertyCount?: number; relationCount?: number; permissionCount?: number; commentCount?: number; } export interface TreeResponse { path: string; entries: TreeEntry[]; nextCursor: string | null; } export interface FileSemantics { properties?: Record; relations?: string[]; permissions?: string[]; comments?: string[]; } /** * Stable identity for an idempotent write, used by the server to coalesce * duplicate deliveries (webhook retries, cross-product fan-in). Two writes * with equal `(kind, key)` in the same workspace within the dedup window * resolve to the same op. Callers typically derive `key` from the upstream * event id, e.g. `github:push:`. */ export interface ContentIdentity { kind: string; key: string; ttlSeconds?: number; } export interface RelayFileReadCacheOptions { /** Cache TTL in ms. Default: 5000. */ ttlMs?: number; /** Max cached entries before LRU eviction. Default: 500. */ maxEntries?: number; } export interface FileReadResponse { path: string; revision: string; contentType: string; content: string; encoding?: "utf-8" | "base64"; provider?: string; providerObjectId?: string; lastEditedAt?: string; semantics?: FileSemantics; } export interface FileWriteRequest { contentType?: string; content: string; encoding?: "utf-8" | "base64"; semantics?: FileSemantics; contentIdentity?: ContentIdentity; } export interface BulkWriteFile { path: string; contentType?: string; content: string; encoding?: "utf-8" | "base64"; contentIdentity?: ContentIdentity; } export interface BulkWriteInput { workspaceId: string; files: BulkWriteFile[]; forkId?: string; correlationId?: string; signal?: AbortSignal; } export interface BulkWriteResponse { written: number; errorCount: number; errors: Array<{ path: string; code: string; message: string; }>; results?: Array<{ path: string; revision: string; contentType?: string; opId?: string; contentIdentity?: ContentIdentity; writeback?: { provider?: string; state?: string; }; }>; correlationId: string; } export interface FileQueryItem { path: string; revision: string; contentType: string; provider?: string; providerObjectId?: string; lastEditedAt?: string; size: number; properties?: Record; relations?: string[]; permissions?: string[]; comments?: string[]; } export interface FileQueryResponse { items: FileQueryItem[]; nextCursor: string | null; } export type WritebackState = "pending" | "succeeded" | "failed" | "dead_lettered"; export type WritebackListState = WritebackState | "dead"; export interface WriteQueuedResponse { opId: string; status: "queued" | "pending"; targetRevision: string; writeback?: { provider?: string; state?: WritebackState; }; } export type FilesystemEventType = "file.created" | "file.updated" | "file.deleted" | "dir.created" | "dir.deleted" | "sync.error" | "sync.ignored" | "sync.suppressed" | "sync.stale" | "writeback.failed" | "writeback.succeeded"; export type EventOrigin = "provider_sync" | "agent_write" | "system"; export interface FilesystemEvent { eventId: string; type: FilesystemEventType; path: string; revision: string; contentHash?: string; origin?: EventOrigin; provider?: string; correlationId?: string; timestamp: string; } export interface ChangeEventResource { path: string; kind: string; id: string; provider: string; } export interface ChangeEventActor { id: string; displayName?: string; } export interface ChangeEventSummary { title?: string; status?: string; priority?: string; labels?: string[]; actor?: ChangeEventActor; fieldsChanged?: string[]; /** Optional compact tag list. Producers must cap this at 8 entries. */ tags?: string[]; } /** * Canonical lightweight event summary shape exported for proactive-runtime * adapters. `buildSummary(payload)` should return this exact structure. */ export type EventSummary = ChangeEventSummary; export type ExpansionLevel = "summary" | "full" | "diff" | "thread"; export interface SummaryExpansion { level: "summary"; path: string; summary: ChangeEventSummary; } export interface FullExpansion { level: "full"; path: string; data: TData; } export interface DiffExpansion { level: "diff"; path: string; diff: Record; } export interface ThreadExpansion { level: "thread"; path: string; thread: Record; } export type Expansion = L extends "summary" ? SummaryExpansion : L extends "full" ? FullExpansion : L extends "diff" ? DiffExpansion : ThreadExpansion; /** * Proactive runtime relayfile notification envelope. * * This differs from the lower-level FilesystemEvent feed above. The proactive * runtime consumes a small, stable notification that points at the canonical * payload in VFS instead of inlining provider payloads directly. * * M1 exposes the type so downstream packages can compile against the final * shape before M2 wires live delivery. */ export interface ChangeEvent { id: string; workspace: string; agentId?: string; type: "relayfile.changed"; occurredAt: string; resource: ChangeEventResource; summary: ChangeEventSummary; expand(level?: L): Promise>; digest?: string; } export interface DigestWindow { /** ISO 8601, inclusive. */ readonly from: string; /** ISO 8601, exclusive. */ readonly to: string; } export interface DigestBullet { /** One-line past-tense description, e.g. "AGE-16 moved to in-review". */ readonly text: string; /** Mount-relative canonical path the bullet points at. */ readonly canonicalPath: string; } export interface DigestSection { readonly provider: string; readonly bullets: readonly DigestBullet[]; } export interface DigestContext { readonly provider: string; readonly window: DigestWindow; changeEvents(filter?: { providers?: string[]; paths?: string[]; }): Promise; } export type DigestHandler = (ctx: DigestContext) => Promise; export interface WritebackSchemaRef { readonly provider: string; readonly resource: string; /** Mount-relative path to the served `.schema.json` virtual file. */ readonly path: string; } export interface LayoutManifestAlias { /** e.g. "by-title", "by-id", "by-edited". */ readonly segment: string; readonly description?: string; } export interface LayoutManifestResource { readonly name: string; readonly canonicalFilename: string; readonly writebackActions?: readonly string[]; readonly writebackSchemas?: readonly WritebackSchemaRef[]; readonly aliases?: readonly LayoutManifestAlias[]; } export interface LayoutManifest { readonly provider: string; readonly materialization: "eager" | "lazy"; readonly resources: readonly LayoutManifestResource[]; } export interface SubscribeOptions { coalesce?: "none" | "fire-once"; coalesceMs?: number; pathScope?: string[]; from?: "now" | "legacy"; cursor?: string; aclToken?: string; drainMs?: number; } export interface Subscription { unsubscribe(): Promise; } export type ReplayOptions = { replayOnStart?: "none"; } | { replayOnStart: `since:${string}`; } | { replayOnStart: `last:${number}`; }; export type ChangeStreamConnectionOptions = ReplayOptions & { workspaceId: string; aclToken?: string; from?: "now" | "legacy"; cursor?: string; }; export interface ChangeStreamConnection extends Subscription { readonly ready: Promise; } export interface ResourceAtEventResult { path: string; data: unknown; digest: string; } export interface ChangeLogQueryResult { events: ChangeEvent[]; } export interface EventFeedResponse { events: FilesystemEvent[]; nextCursor: string | null; } export type ExportFormat = "tar" | "json" | "patch"; export interface ExportOptions { workspaceId: string; format?: ExportFormat; correlationId?: string; signal?: AbortSignal; } export interface ExportJsonResponse { files: FileReadResponse[]; } export type OperationStatus = "pending" | "running" | "succeeded" | "failed" | "dead_lettered" | "canceled"; export type WritebackActionType = "file_upsert" | "file_delete"; export interface OperationStatusResponse { opId: string; path?: string; revision?: string; action?: WritebackActionType; provider?: string; status: OperationStatus; attemptCount: number; nextAttemptAt?: string | null; lastError?: string | null; providerResult?: Record; correlationId?: string; createdAt?: string; updatedAt?: string; completedAt?: string | null; } export interface GetOperationsOptions { status?: OperationStatus; action?: WritebackActionType; provider?: string; cursor?: string; limit?: number; correlationId?: string; signal?: AbortSignal; } export interface OperationFeedResponse { items: OperationStatusResponse[]; nextCursor: string | null; } export interface SyncRefreshRequest { provider: string; reason?: string; } export type SyncProviderStatusState = "connected" | "oauth_connected" | "sync_queued" | "syncing" | "ready" | "healthy" | "lagging" | "error" | "paused"; export interface SyncProviderStatus { provider: string; status: SyncProviderStatusState; ready?: boolean; cursor?: string | null; watermarkTs?: string | null; lagSeconds?: number; lastError?: string | null; failureCodes?: Record; deadLetteredEnvelopes?: number; deadLetteredOps?: number; webhookHealthy?: boolean; } export interface SyncStatusResponse { workspaceId: string; providers: SyncProviderStatus[]; } export interface QueuedResponse { status: "queued"; id: string; correlationId?: string; } export interface AckResponse { status: "acknowledged"; id: string; correlationId?: string; } export interface ErrorResponse { code: string; message: string; correlationId: string; details?: Record; } export interface ConflictErrorResponse extends ErrorResponse { expectedRevision: string; currentRevision: string; currentContentPreview?: string; } export interface BackendStatusResponse { backendProfile: string; stateBackend: string; envelopeQueue: string; envelopeQueueDepth: number; envelopeQueueCapacity: number; writebackQueue: string; writebackQueueDepth: number; writebackQueueCapacity: number; } export type AdminIngressAlertType = "dead_letters" | "pending_backlog" | "drop_rate" | "stale_events"; export type AdminIngressAlertSeverity = "warning" | "critical"; export type AdminIngressAlertProfile = "strict" | "balanced" | "relaxed"; export type AdminIngressEffectiveAlertProfile = AdminIngressAlertProfile | "custom"; export type AdminSyncAlertType = "status_error" | "lag_seconds" | "dead_lettered_envelopes" | "dead_lettered_ops"; export type AdminSyncAlertSeverity = "warning" | "critical"; export interface AdminIngressAlert { workspaceId: string; type: AdminIngressAlertType; severity: AdminIngressAlertSeverity; value: number; threshold: number; message: string; } export interface AdminIngressAlertThresholds { pending: number; deadLetter: number; stale: number; dropRate: number; } export interface AdminIngressAlertTotals { total: number; critical: number; warning: number; byType: Record; } export interface AdminSyncAlert { workspaceId: string; provider: string; type: AdminSyncAlertType; severity: AdminSyncAlertSeverity; value: number; threshold: number; message: string; } export interface AdminSyncAlertThresholds { statusError: number; lagSeconds: number; deadLetteredEnvelopes: number; deadLetteredOps: number; } export interface AdminSyncAlertTotals { total: number; critical: number; warning: number; byType: Record; } export interface AdminIngressStatusResponse { generatedAt: string; alertProfile: AdminIngressAlertProfile; effectiveAlertProfile: AdminIngressEffectiveAlertProfile; workspaceCount: number; returnedWorkspaceCount: number; workspaceIds: string[]; nextCursor: string | null; pendingTotal: number; deadLetterTotal: number; acceptedTotal: number; droppedTotal: number; dedupedTotal: number; coalescedTotal: number; suppressedTotal: number; staleTotal: number; thresholds: AdminIngressAlertThresholds; alertTotals: AdminIngressAlertTotals; alertsTruncated: boolean; alerts: AdminIngressAlert[]; workspaces: Record; } export interface AdminSyncStatusResponse { generatedAt: string; workspaceCount: number; returnedWorkspaceCount: number; workspaceIds: string[]; nextCursor: string | null; providerStatusCount: number; healthyCount: number; laggingCount: number; errorCount: number; pausedCount: number; deadLetteredEnvelopesTotal: number; deadLetteredOpsTotal: number; thresholds: AdminSyncAlertThresholds; alertTotals: AdminSyncAlertTotals; alertsTruncated: boolean; alerts: AdminSyncAlert[]; failureCodes: Record; workspaces: Record; } export interface ListTreeOptions { path?: string; depth?: number; cursor?: string; forkId?: string; correlationId?: string; signal?: AbortSignal; } export interface QueryFilesOptions { path?: string; provider?: string; relation?: string; permission?: string; comment?: string; properties?: Record; cursor?: string; limit?: number; forkId?: string; correlationId?: string; signal?: AbortSignal; } export interface ReadFileInput { workspaceId: string; path: string; forkId?: string; correlationId?: string; signal?: AbortSignal; } export interface GetEventsOptions { provider?: string; cursor?: string; limit?: number; correlationId?: string; signal?: AbortSignal; } export interface GetSyncStatusOptions { provider?: string; correlationId?: string; signal?: AbortSignal; } export interface WaitForDataOptions extends GetSyncStatusOptions { pollIntervalMs?: number; timeoutMs?: number; onPoll?: (elapsedMs: number, status?: SyncProviderStatus) => void; } export interface GetSyncIngressStatusOptions { provider?: string; correlationId?: string; signal?: AbortSignal; } export interface GetAdminIngressStatusOptions { workspaceId?: string; provider?: string; alertProfile?: AdminIngressAlertProfile; pendingThreshold?: number; deadLetterThreshold?: number; staleThreshold?: number; dropRateThreshold?: number; nonZeroOnly?: boolean; maxAlerts?: number; cursor?: string; limit?: number; includeWorkspaces?: boolean; includeAlerts?: boolean; correlationId?: string; signal?: AbortSignal; } export interface GetAdminSyncStatusOptions { workspaceId?: string; provider?: string; nonZeroOnly?: boolean; cursor?: string; limit?: number; includeWorkspaces?: boolean; statusErrorThreshold?: number; lagSecondsThreshold?: number; deadLetteredEnvelopesThreshold?: number; deadLetteredOpsThreshold?: number; maxAlerts?: number; includeAlerts?: boolean; correlationId?: string; signal?: AbortSignal; } export interface SyncIngressStatusResponse { workspaceId: string; queueDepth: number; queueCapacity: number; queueUtilization: number; pendingTotal: number; oldestPendingAgeSeconds: number; deadLetterTotal: number; deadLetterByProvider: Record; acceptedTotal: number; droppedTotal: number; dedupedTotal: number; coalescedTotal: number; dedupeRate: number; coalesceRate: number; suppressedTotal: number; staleTotal: number; ingressByProvider: Record; } export interface GetSyncDeadLettersOptions { provider?: string; cursor?: string; limit?: number; correlationId?: string; signal?: AbortSignal; } export interface DeadLetterItem { envelopeId: string; workspaceId: string; provider: string; deliveryId: string; correlationId?: string; failedAt: string; attemptCount: number; lastError: string; } export interface DeadLetterFeedResponse { items: DeadLetterItem[]; nextCursor: string | null; } export interface WebhookSubscriptionHealth { lastDeliveryAt: string | null; lastSuccessAt: string | null; lastError: string | null; consecutiveFailures: number; } export interface WebhookSubscription { id: string; url: string; pathGlobs: string[]; createdAt: string; updatedAt: string; health: WebhookSubscriptionHealth; } export interface RegisterWebhookInput { workspaceId: string; url: string; pathGlobs: string[]; /** * Optional HMAC secret. If omitted, relayfile generates a secret and returns * it once from the registration response. */ secret?: string; correlationId?: string; signal?: AbortSignal; } export interface RegisterWebhookResponse { subscriptionId: string; secret?: string; } export interface ListWebhooksOptions { correlationId?: string; signal?: AbortSignal; } export interface DeleteWebhookOptions { correlationId?: string; signal?: AbortSignal; } export interface GetWebhookDeadLettersOptions { cursor?: string; limit?: number; correlationId?: string; signal?: AbortSignal; } export interface WebhookDeliveryDeadLetterItem { deliveryId: string; workspaceId: string; subscriptionId: string; eventId: string; url: string; failedAt: string; attemptCount: number; lastError: string; replayCount: number; status: "dead_lettered" | "queued" | "delivered"; } export interface WebhookDeliveryDeadLetterFeedResponse { items: WebhookDeliveryDeadLetterItem[]; nextCursor: string | null; } export interface WriteFileInput { workspaceId: string; path: string; baseRevision: string; content: string; contentType?: string; encoding?: "utf-8" | "base64"; semantics?: FileSemantics; forkId?: string; contentIdentity?: ContentIdentity; correlationId?: string; signal?: AbortSignal; } export interface DeleteFileInput { workspaceId: string; path: string; baseRevision: string; forkId?: string; correlationId?: string; signal?: AbortSignal; } export interface CreateForkInput { workspaceId: string; proposalId: string; ttlSeconds?: number; correlationId?: string; signal?: AbortSignal; } export interface DiscardForkInput { workspaceId: string; forkId: string; correlationId?: string; signal?: AbortSignal; } export interface CommitForkInput { workspaceId: string; forkId: string; correlationId?: string; signal?: AbortSignal; } export interface CommitForkResponse { revision: string; writtenCount: number; deletedCount: number; } export interface IngestWebhookInput { workspaceId: string; provider: string; event_type: string; path: string; data?: Record; delivery_id?: string; timestamp?: string; headers?: Record; correlationId?: string; signal?: AbortSignal; } export type WritebackDeadLetterErrorCode = "schema_violation" | "provider_4xx" | "provider_5xx_exhausted" | "timeout"; export interface WritebackDeadLetterError { code: WritebackDeadLetterErrorCode; message: string; providerStatus?: number; providerResponse?: Record; attempts: number; firstAttemptAt: string; lastAttemptAt: string; opId: string; } export interface WritebackItem { id: string; workspaceId: string; path: string; revision: string; correlationId: string; state?: WritebackListState; provider?: string; action?: WritebackActionType; ts?: string; code?: WritebackDeadLetterErrorCode | string; message?: string; providerStatus?: number; providerResponse?: Record; attempts?: number; firstAttemptAt?: string; enqueuedAt?: string; lastAttemptAt?: string; error?: WritebackDeadLetterError; } export interface WritebackItemDetail extends WritebackItem { error?: WritebackDeadLetterError; } export interface AckWritebackInput { workspaceId: string; itemId: string; success: boolean; error?: string; /** * Provider-assigned id of the created/updated object (e.g. the Slack * message ts). When present on a successful ack, the service reconciles * the agent-authored draft file per the draftFile() rename contract * (issue #242): the draft is renamed to the canonical id, or removed when * the canonical record already materialized. The mutation is * classification-exempt — it can never enqueue a new writeback. */ externalId?: string; /** * Optional canonical projection path for the draft rename. Must stay under * the same provider root as the draft; otherwise the service falls back to * the externalId-derived name next to the draft. */ canonicalPath?: string; /** * Optional fields the provider echoed back about the written record (e.g. a * Slack message `ts` and `channel`). They are surfaced verbatim on the * operation's providerResult (recoverable via getOp), letting an agent reply * in a Slack thread using the returned ts as thread_ts. The reserved key * `providerRevision` is server-owned and cannot be overridden via this map. */ providerResult?: Record; correlationId?: string; signal?: AbortSignal; } /** Disposition of the agent-authored draft file after a successful ack. */ export interface AckWritebackDraftDisposition { action: "renamed" | "removed" | "none"; from?: string; to?: string; } export interface AckWritebackResponse { status: "acknowledged"; id: string; correlationId?: string; success: boolean; /** Present only when the ack was successful and carried an externalId. */ draft?: AckWritebackDraftDisposition; } export interface SweepWritebackDraftsInput { workspaceId: string; /** Restrict the sweep to a subtree. */ pathPrefix?: string; /** Basename globs for hand-named drafts, e.g. "wb-*.json". */ patterns?: string[]; /** Execute removals; when false the sweep is a dry run. */ apply?: boolean; correlationId?: string; signal?: AbortSignal; } export interface SweepWritebackDraftsResponse { dryRun: boolean; scanned: number; removed: Array<{ path: string; reason: "space-uuid-draft" | "pattern"; }>; skipped: Array<{ path: string; reason: "pending-writeback" | "provider-linked"; }>; }