primcli 3.0.0-preview.11 → 3.0.0-preview.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,12 @@
1
- import { $ as readEmailInteraction, A as explainPendingMailNotFound, At as cliInvocation, Bt as formatAlreadySentNotice, C as promptLine$2, Ct as sendOutcomeExitCode, D as formatBrowserLoginInstructions, Dt as successfulSendOutcome, E as canOfferCopyHotkey, Et as serializeErrorPayload, F as latestOwnOutbound, Ft as loadSkillLine, G as clearConsumedPendingMail, Gt as classificationFactLines, H as wakeRecipientField, Ht as emptyEmailListHint, I as readThreadContext, It as currentMailSessionKey, J as readPendingMail, Jt as noFactsMessage, K as clearReadPendingMail, Kt as classificationOf, L as sentInThread, Lt as detectAgentRuntime, M as describeWake, N as displayAddress, Nt as generatedAgentName, O as listenForCopyHotkey, Ot as thrownErrorExitCode, P as formatMailWakeLine, Pt as identitySuggestions, Q as interactionNextActions, Qt as ConnectedAgentKeyRequiredError, R as serverMuted, Rt as detectRuntimeSession, S as approveThenRerunHint, St as sendIdentityFields, T as withApproval, Tt as sentHistoryWindowStart, U as wakeRelationship, Ut as mailReach, V as wakeReadCommand, Vt as withFlagSuggestion, W as claimPendingMail, Wt as readCallerMailFor, X as removePendingMail, Xt as ATTACHMENT_WAIT_MAX_MS, Y as recordPendingMail, Yt as ATTACHMENT_WAIT_FLAG_DESCRIPTION, Z as interactionHumanLine, Zt as withAttachmentWait, _ as takesStepUp, _n as setKnownCommands, _t as formatPriorRepliesWarning, a as createOperationCommand, an as selectedOrgFromEnv, at as SEND_OUTCOME_HELP, b as STEP_UP_HEADER$1, bt as priorRepliesThatWentOut, c as formatErrorPayload, cn as credentialsFromUserLogin, ct as buildFollowUpCommand, d as isGrantRefusal, dn as revokeUserLogin, dt as classifySendError, en as createAuthenticatedCliApiClient, et as replyExpectation, f as parameterFlagName, fn as roleKeyLabel, ft as deriveSendIdempotencyKey, g as surfaceUnauthorizedHint, gn as sanitizeForTerminal, gt as formatPriorRepliesCheckSkipped, h as runWithTiming, hn as routeWarningLines, ht as formatDeletedEarlierSendNotice, i as TIME_FLAG_DESCRIPTION, in as resolveCliApiRequestConfig, it as MAX_SEND_WAIT_TIMEOUT_MS, j as bareAddress, jt as connectNextSteps, k as openBrowser, l as hasControlCharacter$1, ln as fetchCliOrgs, lt as buildThrownSendFailureEnvelope, m as refusalDetails, mn as switchCurrentOrg, mt as followUpCommandPrefix, n as API_ERROR_CODES, nn as refreshStoredCliCredentials, o as extractErrorCode, on as CliOrgRefusedError, ot as apiResultHttpStatus, p as readTextFileFlag, pn as roleKeyText, pt as excludeSignalReplies, q as pendingMailPath, qt as isEmailId, r as OPERATION_PATH_FLAGS, rn as refuseOrgSelectionForProfile, rt as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, s as extractErrorPayload, sn as RESTRICTED_ROLE_NOTE, st as assertValidIdempotencyKey, t as API_BASE_URL_FLAG_DESCRIPTION, tn as createCliApiClient, tt as replyInteractionWarning, u as headerFlagName, un as isBuiltInRoleKey, ut as checkPriorReplies, v as writeErrorWithHints, vn as operationManifest, vt as formatSendFailureSummary, w as terminalApprovalIo, wt as sendWaitClampNotice, x as STEP_UP_TOKEN_FLAG_DESCRIPTION, xt as reportSendCommandResult, y as BROWSER_FLAG_DESCRIPTION, yt as formatSendRecordFailureSummary, z as serverRelationship, zt as runtimeSessionVariableList } from "../api-command-CkMEFiBL.js";
2
- import { $ as parseWebhookEvent, $n as simulateRoute, $t as getRepeatingSend, A as lateMailReread, An as listWakeDispatches, At as deleteWakeSchedule, B as ensureSearchReportsReplyState, Bn as replyToEmail, Bt as getConversation, C as waitOutRetryAfter, Cn as listOrganizationPeople, Ct as createWakeAuthorization, D as encodeReceivedAtSearchCursor, Dn as listSentAttachmentArchiveMembers, Dt as deleteMemory, E as cursorFromAcceptedRows, En as listRoutes, F as AWAITING_VALUES, Ft as downloadSentAttachmentByFilename, G as queryUsesAwaiting, Gt as getFunctionRouting, H as formatRepliesCell, Hn as revokeCredential, Ht as getDefaultNetworkAgent, I as REPLY_STATE_UNSUPPORTED_CODE, It as getAccessMode, Jn as semanticSearch, Jt as getKey, Kn as searchEmails, Kt as getFunctionTestRunTrace, L as ReplyStateUnsupportedError, Ln as redeemCreditCode, Lt as getAccount, M as sinceFromFlags, Mn as pollCliLogin, Mt as downloadEmailAttachmentByFilename, N as sleep$3, Nn as pullWebhookEvent, O as fetchEmailSearchPage, On as listSentEmails, Ot as deleteRoute, P as AWAITING_FLAG_DESCRIPTION, Pt as downloadSentAttachmentArchiveMember, Qn as setMemory, Qt as getOwnGrant, R as assertAwaitingFilterRows, Rn as removeDefaultNetworkMember, Rt as getAgentContactPolicy, S as transientFailure, Sn as listMailboxAccess, St as createRoute, T as collectNewAcceptedEmails, Tn as listRoles, U as hasReplyState, Un as rotateKey, Ut as getEmail, V as formatAwaitingCell, Vn as resendAgentSignupVerification, Vt as getCreditBalance, W as isAwaitingRejectedError, Wn as runWakeSchedule, Wt as getFunction, Xn as setFunctionRoute, Xt as getOrgRoutingTopology, Yn as sendEmail, Yt as getMemory, Z as isEmailReceivedEvent, Zn as setFunctionSecret, Zt as getOutboundStatus, _ as describeApiFailure, _n as listEndpoints, _t as cliLogout, a as REPEAT_IDLE_MAX_MINUTES, an as leaveOrganization, ar as unsetFunctionRoute, at as assertAutomatedVerdict, b as retryTransient, bn as listGroups, bt as createFunction, cn as listAgentContacts, cr as updateKey, d as parseRepeatTick, dn as listDefaultNetworkAgents, dr as updateWakeAuthorization, dt as isAutomatedRejectedError, en as getSendFrom, er as startAgentClaim, et as parseFromHeader, f as preparePresenceAliveEmail, fn as listDefaultNetworkMembers, fr as updateWakeSchedule, ft as addDefaultNetworkMember, g as WaitDeadlineReached, gn as listEmails, gt as checkSendability, hn as listEmailRuleResults, hr as whoami, ht as checkDefaultNetworkContactAdmission, i as REPEAT_EVERY_MAX_MINUTES, ir as testFunction, it as AutomatedFilterUnsupportedError, j as quoteDslValue, jn as listWakeSchedules, jt as downloadEmailAttachmentArchiveMember, k as filtersFromFlags, kn as listWakeAuthorizations, kt as deleteWakeAuthorization, l as parseInteractionEnvelope, ln as listAgentNetworks, lr as updateRepeatingSend, m as repeatStopCommand, mn as listEmailAttachmentArchiveMembers, mr as verifyAgentSignup, mt as cancelSentEmailByIdempotencyKey, nn as getThread, nr as startCliLogin, nt as AUTOMATED_FLAG_DESCRIPTION, o as REPEAT_MAX_SENDS_MAX, on as listActivityObjects, or as updateDefaultNetworkMember, ot as automatedRejectedError, p as prepareSignalEmail, pn as listDomains, pr as verifyAgentClaim, pt as cancelSentEmail, qn as searchMemories, qt as getInboxStatus, rn as getWakeSchedule, rr as stopRepeatFromEmail, rt as AUTOMATED_VALUES, s as buildRepeatStopBody, sr as updateFunction, tn as getSentEmail, tr as startAgentSignup, tt as AUTOMATED_FILTER_UNSUPPORTED_CODE, u as parsePresenceEnvelope, ur as updateRoute, ut as hasAutomatedVerdict, vn as listFunctionLogs, vt as completeWebhookEvent, w as SINCE_VALUE_HELP, wn as listRepeatingSends, wt as createWakeSchedule, x as retryingOperation, xn as listKeys, xt as createKey, y as pauseForTransientFailure, yn as listFunctions, yt as createEndpoint, z as awaitingRejectedError, zn as reorderRoutes } from "../repeats-Dj_o6c-X-ChTwFlyB.js";
3
- import { S as readBundledConnectSkill, _ as NotificationOutcomeUnknownError, a as NotificationRetryError, b as installedConnectSkillFile, c as readConversationStatusContent, d as SESSION_UUID$1, g as NativeSessionNotLoadedError, h as NativeSessionError, i as notificationReceiptPage, l as createListenHandler, m as NativeSessionDisconnectedError, n as notificationSenders, o as isRoutineNotificationContent, p as defaultSessionSocket, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, u as readableDelivery, v as connectSkillTarget, x as installedVersion, y as installConnectSkill } from "../notify-session-DcFHxUL9.js";
1
+ import { $ as readEmailInteraction, A as explainPendingMailNotFound, At as cliInvocation, Bt as formatAlreadySentNotice, C as promptLine$2, Ct as sendOutcomeExitCode, D as formatBrowserLoginInstructions, Dt as successfulSendOutcome, E as canOfferCopyHotkey, Et as serializeErrorPayload, F as latestOwnOutbound, Ft as loadSkillLine, G as clearConsumedPendingMail, Gt as classificationFactLines, H as wakeRecipientField, Ht as emptyEmailListHint, I as readThreadContext, It as currentMailSessionKey, J as readPendingMail, Jt as noFactsMessage, K as clearReadPendingMail, Kt as classificationOf, L as sentInThread, Lt as detectAgentRuntime, M as describeWake, N as displayAddress, Nt as generatedAgentName, O as listenForCopyHotkey, Ot as thrownErrorExitCode, P as formatMailWakeLine, Pt as identitySuggestions, Q as interactionNextActions, Qt as ConnectedAgentKeyRequiredError, R as serverMuted, Rt as detectRuntimeSession, S as approveThenRerunHint, St as sendIdentityFields, T as withApproval, Tt as sentHistoryWindowStart, U as wakeRelationship, Ut as mailReach, V as wakeReadCommand, Vt as withFlagSuggestion, W as claimPendingMail, Wt as readCallerMailFor, X as removePendingMail, Xt as ATTACHMENT_WAIT_MAX_MS, Y as recordPendingMail, Yt as ATTACHMENT_WAIT_FLAG_DESCRIPTION, Z as interactionHumanLine, Zt as withAttachmentWait, _ as takesStepUp, _n as setKnownCommands, _t as formatPriorRepliesWarning, a as createOperationCommand, an as selectedOrgFromEnv, at as SEND_OUTCOME_HELP, b as STEP_UP_HEADER$1, bt as priorRepliesThatWentOut, c as formatErrorPayload, cn as credentialsFromUserLogin, ct as buildFollowUpCommand, d as isGrantRefusal, dn as revokeUserLogin, dt as classifySendError, en as createAuthenticatedCliApiClient, et as replyExpectation, f as parameterFlagName, fn as roleKeyLabel, ft as deriveSendIdempotencyKey, g as surfaceUnauthorizedHint, gn as sanitizeForTerminal, gt as formatPriorRepliesCheckSkipped, h as runWithTiming, hn as routeWarningLines, ht as formatDeletedEarlierSendNotice, i as TIME_FLAG_DESCRIPTION, in as resolveCliApiRequestConfig, it as MAX_SEND_WAIT_TIMEOUT_MS, j as bareAddress, jt as connectNextSteps, k as openBrowser, l as hasControlCharacter$1, ln as fetchCliOrgs, lt as buildThrownSendFailureEnvelope, m as refusalDetails, mn as switchCurrentOrg, mt as followUpCommandPrefix, n as API_ERROR_CODES, nn as refreshStoredCliCredentials, o as extractErrorCode, on as CliOrgRefusedError, ot as apiResultHttpStatus, p as readTextFileFlag, pn as roleKeyText, pt as excludeSignalReplies, q as pendingMailPath, qt as isEmailId, r as OPERATION_PATH_FLAGS, rn as refuseOrgSelectionForProfile, rt as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, s as extractErrorPayload, sn as RESTRICTED_ROLE_NOTE, st as assertValidIdempotencyKey, t as API_BASE_URL_FLAG_DESCRIPTION, tn as createCliApiClient, tt as replyInteractionWarning, u as headerFlagName, un as isBuiltInRoleKey, ut as checkPriorReplies, v as writeErrorWithHints, vn as operationManifest, vt as formatSendFailureSummary, w as terminalApprovalIo, wt as sendWaitClampNotice, x as STEP_UP_TOKEN_FLAG_DESCRIPTION, xt as reportSendCommandResult, y as BROWSER_FLAG_DESCRIPTION, yt as formatSendRecordFailureSummary, z as serverRelationship, zt as runtimeSessionVariableList } from "../api-command-D5Tj9C-K.js";
2
+ import { $ as parseWebhookEvent, $n as simulateRoute, $t as getRepeatingSend, A as lateMailReread, An as listWakeDispatches, At as deleteWakeSchedule, B as ensureSearchReportsReplyState, Bn as replyToEmail, Bt as getConversation, C as waitOutRetryAfter, Cn as listOrganizationPeople, Ct as createWakeAuthorization, D as encodeReceivedAtSearchCursor, Dn as listSentAttachmentArchiveMembers, Dt as deleteMemory, E as cursorFromAcceptedRows, En as listRoutes, F as AWAITING_VALUES, Ft as downloadSentAttachmentByFilename, G as queryUsesAwaiting, Gt as getFunctionRouting, H as formatRepliesCell, Hn as revokeCredential, Ht as getDefaultNetworkAgent, I as REPLY_STATE_UNSUPPORTED_CODE, It as getAccessMode, Jn as semanticSearch, Jt as getKey, Kn as searchEmails, Kt as getFunctionTestRunTrace, L as ReplyStateUnsupportedError, Ln as redeemCreditCode, Lt as getAccount, M as sinceFromFlags, Mn as pollCliLogin, Mt as downloadEmailAttachmentByFilename, N as sleep$3, Nn as pullWebhookEvent, O as fetchEmailSearchPage, On as listSentEmails, Ot as deleteRoute, P as AWAITING_FLAG_DESCRIPTION, Pt as downloadSentAttachmentArchiveMember, Qn as setMemory, Qt as getOwnGrant, R as assertAwaitingFilterRows, Rn as removeDefaultNetworkMember, Rt as getAgentContactPolicy, S as transientFailure, Sn as listMailboxAccess, St as createRoute, T as collectNewAcceptedEmails, Tn as listRoles, U as hasReplyState, Un as rotateKey, Ut as getEmail, V as formatAwaitingCell, Vn as resendAgentSignupVerification, Vt as getCreditBalance, W as isAwaitingRejectedError, Wn as runWakeSchedule, Wt as getFunction, Xn as setFunctionRoute, Xt as getOrgRoutingTopology, Yn as sendEmail, Yt as getMemory, Z as isEmailReceivedEvent, Zn as setFunctionSecret, Zt as getOutboundStatus, _ as describeApiFailure, _n as listEndpoints, _t as cliLogout, a as REPEAT_IDLE_MAX_MINUTES, an as leaveOrganization, ar as unsetFunctionRoute, at as assertAutomatedVerdict, b as retryTransient, bn as listGroups, bt as createFunction, cn as listAgentContacts, cr as updateKey, d as parseRepeatTick, dn as listDefaultNetworkAgents, dr as updateWakeAuthorization, dt as isAutomatedRejectedError, en as getSendFrom, er as startAgentClaim, et as parseFromHeader, f as preparePresenceAliveEmail, fn as listDefaultNetworkMembers, fr as updateWakeSchedule, ft as addDefaultNetworkMember, g as WaitDeadlineReached, gn as listEmails, gt as checkSendability, hn as listEmailRuleResults, hr as whoami, ht as checkDefaultNetworkContactAdmission, i as REPEAT_EVERY_MAX_MINUTES, ir as testFunction, it as AutomatedFilterUnsupportedError, j as quoteDslValue, jn as listWakeSchedules, jt as downloadEmailAttachmentArchiveMember, k as filtersFromFlags, kn as listWakeAuthorizations, kt as deleteWakeAuthorization, l as parseInteractionEnvelope, ln as listAgentNetworks, lr as updateRepeatingSend, m as repeatStopCommand, mn as listEmailAttachmentArchiveMembers, mr as verifyAgentSignup, mt as cancelSentEmailByIdempotencyKey, nn as getThread, nr as startCliLogin, nt as AUTOMATED_FLAG_DESCRIPTION, o as REPEAT_MAX_SENDS_MAX, on as listActivityObjects, or as updateDefaultNetworkMember, ot as automatedRejectedError, p as prepareSignalEmail, pn as listDomains, pr as verifyAgentClaim, pt as cancelSentEmail, qn as searchMemories, qt as getInboxStatus, rn as getWakeSchedule, rr as stopRepeatFromEmail, rt as AUTOMATED_VALUES, s as buildRepeatStopBody, sr as updateFunction, tn as getSentEmail, tr as startAgentSignup, tt as AUTOMATED_FILTER_UNSUPPORTED_CODE, u as parsePresenceEnvelope, ur as updateRoute, ut as hasAutomatedVerdict, vn as listFunctionLogs, vt as completeWebhookEvent, w as SINCE_VALUE_HELP, wn as listRepeatingSends, wt as createWakeSchedule, x as retryingOperation, xn as listKeys, xt as createKey, y as pauseForTransientFailure, yn as listFunctions, yt as createEndpoint, z as awaitingRejectedError, zn as reorderRoutes } from "../repeats-Dj_o6c-X-ykfsraq6.js";
3
+ import { S as readBundledConnectSkill, _ as NotificationOutcomeUnknownError, a as NotificationRetryError, b as installedConnectSkillFile, c as readConversationStatusContent, d as SESSION_UUID$1, g as NativeSessionNotLoadedError, h as NativeSessionError, i as notificationReceiptPage, l as createListenHandler, m as NativeSessionDisconnectedError, n as notificationSenders, o as isRoutineNotificationContent, p as defaultSessionSocket, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, u as readableDelivery, v as connectSkillTarget, x as installedVersion, y as installConnectSkill } from "../notify-session-cIUz_kxR.js";
4
4
  import { A as AGENT_ADDRESS_ENV, C as saveCliCredentials, D as loadActiveChatState, E as deleteChatState, F as agentProfilesDirectory, H as requireDefaultLoginProfile, M as AgentConnectionSetupError, N as agentProfileDirectory, O as loadChatConversationByLocalId, P as agentProfileName, R as loadConnectedAgentProfile, S as resolveCliAuth, T as chatStatePath, W as PrimitiveApiClient, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as loadCliCredentials, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_PROFILE_ENV, k as saveActiveChatState, l as saveCliConfig, m as cliAccessTokenExpiresAt, n as deleteCliConfig, o as redactCliEnvironment, p as acquireCliCredentialsLock, r as emptyCliConfig, s as removeCliEnvironment, u as upsertCliEnvironment, v as deleteCliCredentialsLock, w as saveSignupCredentials, y as detectPrimitiveKeyEnvMisname } from "../cli-config-Ms_0vNjq.js";
5
5
  import { C as resolveListenSubscription, S as normalizeListenOrigin, _ as acquireListenLock, a as mailLockBusy, c as mailTime, d as readMailJson, f as removeMailFile, g as ListenStateError, h as writeMailJson, i as mailId, l as privateMailDirectory, m as withMailLock, o as mailObject, u as privateMailPermissions, y as listenIdentity } from "../shared-mail-files-BAQSGxxz.js";
6
- import { $ as saveConnectionName, A as ContactPolicyReadRetryError, B as AddressNotesApiError, C as RECEIVER_MODES, Ct as upsertManagedBlock, D as registrationSummary, Dt as refreshOwnerMemberAddressPeriodically, E as failedSessionRegistration, Et as addSessionReceiver, F as canonicalContactSelector, G as contactAgentAddress, H as runAddressNotesRequest, I as AgentDisconnectError, J as agentInvitationHash, K as runContactRequest, L as claimOverRevokedSetup, M as NetworkAdmissionPendingError, N as createNotificationContactPolicy, O as registrationSummaryLine, Ot as withOwnerMemberAddress, P as evaluateContactPolicy, Q as refusalAfterReplacement, R as clearRevokedAddresses, S as runAgentConnect, St as readManagedFile, T as verificationReplySubmitted, Tt as readHookLauncherWarning, U as ContactsApiError, V as addressNoteTarget, W as contactAddress, X as readAgentInvitation, Y as connectAgent, Z as receiverStatusCommand, _ as enrollmentResumes, _t as samePath, a as endSession, at as boundSessionProfiles, b as invitationProfileName, bt as inspectManagedBlock, c as pendingSessionDisconnects, ct as describeSessionHookChange, d as registerSession, dt as ephemeralCliReason, et as savedReceiver, f as retryPendingDisconnects, ft as findOnPath, g as enrollAgent, gt as repairClaudeHooks, h as staleSessionAgents, ht as readClaudeSettings, i as connectedProfilesForSession, it as recordHookMailCheck, j as NETWORK_ADMISSION_PENDING_RETRY_MS, k as waitForRegistrations, l as psProcessInfo, lt as describeSessionHookItem, m as runDetached, mt as inspectClaudeHooks, n as OMP_MAIN, nt as releaseRegistrationsForProfile, o as headlessClaudeRun, ot as claudeConfigDir, p as revokeStaleSessionAgents, pt as hasMachineSessionHooks, q as agentConnectionStatus, r as agentConnectionState, rt as installClaudeWakeHook, st as currentCliLocation, t as HOOK_END_GRACE_MS, tt as MACHINE_RUNTIMES, u as readClaudeHookInput, ut as ensureHookLauncher, v as connectWarnings, vt as writeClaudeSettings, w as externalReceivingState, wt as writeManagedFile, x as pollCheckCommand, xt as jsonIndentation, y as defaultAgentProfileName, yt as MachineFileError, z as disconnectAgent } from "../machine-session-D39TUEGF.js";
7
- import { a as readBeforeDeadline, c as readPresenceProjection, i as readApiBeforeDeadline, n as isPlainChatReply, o as scopedChatSenderTrust, r as isScopedChatReply, s as presenceDisposition, t as isConnectedChatCredential } from "../scoped-chat-DVhJu-DF.js";
6
+ import { $ as saveConnectionName, A as ContactPolicyReadRetryError, B as AddressNotesApiError, C as RECEIVER_MODES, Ct as upsertManagedBlock, D as registrationSummary, Dt as refreshOwnerMemberAddressPeriodically, E as failedSessionRegistration, Et as addSessionReceiver, F as canonicalContactSelector, G as contactAgentAddress, H as runAddressNotesRequest, I as AgentDisconnectError, J as agentInvitationHash, K as runContactRequest, L as claimOverRevokedSetup, M as NetworkAdmissionPendingError, N as createNotificationContactPolicy, O as registrationSummaryLine, Ot as withOwnerMemberAddress, P as evaluateContactPolicy, Q as refusalAfterReplacement, R as clearRevokedAddresses, S as runAgentConnect, St as readManagedFile, T as verificationReplySubmitted, Tt as readHookLauncherWarning, U as ContactsApiError, V as addressNoteTarget, W as contactAddress, X as readAgentInvitation, Y as connectAgent, Z as receiverStatusCommand, _ as enrollmentResumes, _t as samePath, a as endSession, at as boundSessionProfiles, b as invitationProfileName, bt as inspectManagedBlock, c as pendingSessionDisconnects, ct as describeSessionHookChange, d as registerSession, dt as ephemeralCliReason, et as savedReceiver, f as retryPendingDisconnects, ft as findOnPath, g as enrollAgent, gt as repairClaudeHooks, h as staleSessionAgents, ht as readClaudeSettings, i as connectedProfilesForSession, it as recordHookMailCheck, j as NETWORK_ADMISSION_PENDING_RETRY_MS, k as waitForRegistrations, l as psProcessInfo, lt as describeSessionHookItem, m as runDetached, mt as inspectClaudeHooks, n as OMP_MAIN, nt as releaseRegistrationsForProfile, o as headlessClaudeRun, ot as claudeConfigDir, p as revokeStaleSessionAgents, pt as hasMachineSessionHooks, q as agentConnectionStatus, r as agentConnectionState, rt as installClaudeWakeHook, st as currentCliLocation, t as HOOK_END_GRACE_MS, tt as MACHINE_RUNTIMES, u as readClaudeHookInput, ut as ensureHookLauncher, v as connectWarnings, vt as writeClaudeSettings, w as externalReceivingState, wt as writeManagedFile, x as pollCheckCommand, xt as jsonIndentation, y as defaultAgentProfileName, yt as MachineFileError, z as disconnectAgent } from "../machine-session-BMr-zPLx.js";
7
+ import { a as readBeforeDeadline, c as readPresenceProjection, i as readApiBeforeDeadline, n as isPlainChatReply, o as scopedChatSenderTrust, r as isScopedChatReply, s as presenceDisposition, t as isConnectedChatCredential } from "../scoped-chat-DVSsLSRw.js";
8
8
  import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, readWorkingLease, restoreAutoWorking } from "./auto-signals.js";
9
- import { a as ensureSharedMailSubscription, c as isConnectedAgentKey, d as reserveSharedMailSubscription, f as followEmailConversation, g as EventReceiverError, h as EventConnection, i as sharedMailScope, l as createSharedMailWaiter, m as otherParticipants, n as reconcileChatSend, p as readConversationFollow, r as openSharedMailReceiver, s as AGENT_CONNECTION_REQUIRED_MESSAGE, t as sendSignal, u as openSharedMailStore } from "../signal-command-CGgHQ_po.js";
9
+ import { a as ensureSharedMailSubscription, c as isConnectedAgentKey, d as reserveSharedMailSubscription, f as followEmailConversation, g as EventReceiverError, h as EventConnection, i as sharedMailScope, l as createSharedMailWaiter, m as otherParticipants, n as reconcileChatSend, p as readConversationFollow, r as openSharedMailReceiver, s as AGENT_CONNECTION_REQUIRED_MESSAGE, t as sendSignal, u as openSharedMailStore } from "../signal-command-gpH2AhIi.js";
10
10
  import { a as thrownErrorBody, i as runStreamingJson, n as guardJsonOutputCommands, r as jsonOutputRequested, t as JSON_OUTPUT_FLAG } from "../json-output-ch756TXV.js";
11
11
  import { a as backgroundListenStatus, c as runBackgroundListen, d as stopBackgroundListen, f as verifyBackgroundListenTarget, o as backgroundListenToken, r as backgroundListenConfiguration, u as startBackgroundListen } from "../listen-background-DyBYca28.js";
12
12
  import { ORG_SELECTION_ENV } from "./global-org-flag.js";
@@ -2502,7 +2502,12 @@ const openapiDocument = {
2502
2502
  "name": "outbound_only",
2503
2503
  "in": "query",
2504
2504
  "schema": { "type": "boolean" },
2505
- "description": "When true, include only outbound DNS records. Verified domains default to outbound-only; pending claims default to all required records."
2505
+ "description": "When true, include only outbound DNS records. Cannot be combined with inbound_only=true. By default, include all records."
2506
+ }, {
2507
+ "name": "inbound_only",
2508
+ "in": "query",
2509
+ "schema": { "type": "boolean" },
2510
+ "description": "When true, include only inbound MX and ownership TXT records. Cannot be combined with outbound_only=true."
2506
2511
  }],
2507
2512
  "responses": {
2508
2513
  "200": {
@@ -4782,7 +4787,7 @@ const openapiDocument = {
4782
4787
  "description": "Existing named pull destination resumed",
4783
4788
  "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
4784
4789
  "type": "object",
4785
- "properties": { "data": { "$ref": "#/components/schemas/Endpoint" } }
4790
+ "properties": { "data": { "$ref": "#/components/schemas/CreatedEndpoint" } }
4786
4791
  }] } } },
4787
4792
  "headers": {
4788
4793
  "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
@@ -4795,7 +4800,7 @@ const openapiDocument = {
4795
4800
  "description": "Endpoint created (or reactivated)",
4796
4801
  "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
4797
4802
  "type": "object",
4798
- "properties": { "data": { "$ref": "#/components/schemas/Endpoint" } }
4803
+ "properties": { "data": { "$ref": "#/components/schemas/CreatedEndpoint" } }
4799
4804
  }] } } }
4800
4805
  },
4801
4806
  "400": {
@@ -21769,7 +21774,7 @@ const openapiDocument = {
21769
21774
  "/activity/objects": { "get": {
21770
21775
  "operationId": "listActivityObjects",
21771
21776
  "summary": "List what you created, or what you were asked",
21772
- "description": "The objects the caller created, newest first, each with where it stands now, including ones since disabled, deleted or revoked where the object keeps a row: Functions, endpoints, Function triggers, pull subscriptions, recipient routes, filters, wake schedules, repeating sends and scheduled sends. An API key, an app or an agent sees what it created itself; a webhook given to an API key lists under that key. A person (a dashboard session, or their own Primitive CLI or app login) sees what they created through any credential of theirs, each item naming it, and also their API keys, personal agents, email rules, rule requests and objections to rules over their mail (objections only on the dashboard or the Primitive CLI). Each kind is listed only if the caller could list it through that kind's own route; a kind it could not is named in withheld, with no count. Ids, names and states only: never a webhook URL or mail content. Needs activity:read_self. With relation=asked it lists instead what other members asked of the person, open or answered: the rule requests made to them, and the objections to rules they wrote, under their current membership, each naming the member who asked or objected in created_through. A key, an app or an agent has none, and gets both kinds in withheld. With relation=concerning it lists instead what other members did that concerns the person's own mail: each time a rule started covering one of their mailboxes or saved a new version while covering it, named as the rule was then, naming its author in created_through, under their current membership; only on the dashboard or the Primitive CLI. A key, an app or an agent has none, and gets email_rule_coverage in withheld. A notice never reads as ended.",
21777
+ "description": "The objects the caller created, newest first, each with where it stands now, including ones since disabled, deleted or revoked where the object keeps a row: Functions, endpoints, Function triggers, pull subscriptions, recipient routes, filters, wake schedules, repeating sends and scheduled sends. An API key, an app or an agent sees what it created itself; a webhook given to an API key lists under that key. A person (a dashboard session, or their own Primitive CLI or app login) sees what they created through any credential of theirs, each item naming it, and also their API keys, personal agents, email rules, rule requests and objections to rules over their mail (objections only on the dashboard or the Primitive CLI). Each kind is listed only if the caller could list it through that kind's own route; a kind it could not is named in withheld, with no count. Ids, names and states only: never a webhook URL or mail content. Needs activity:read_self. With relation=asked it lists instead what other members asked of the person, open or answered: the rule requests made to them, and the objections to rules they wrote, under their current membership, each naming the member who asked or objected in created_through. A key, an app or an agent has none, and gets both kinds in withheld. With relation=concerning it lists instead what other members did that concerns the person's own mail: each time a rule started covering one of their mailboxes or saved a new version while covering it, named as the rule was then, naming its author in created_through, under their current membership; only on the dashboard or the Primitive CLI. A key, an app or an agent has none, and gets email_rule_coverage in withheld. A notice never reads as ended. Notices are written by a periodic job and can lag: until the notices record a rule starting to cover you, the first page lists that rule as pending (pending_complete says whether that check finished), and as_of says about when the notices last looked. For what covers you now, read GET /email-rules/covering-me.",
21773
21778
  "tags": ["Activity"],
21774
21779
  "security": [{ "BearerAuth": [] }],
21775
21780
  "parameters": [
@@ -21780,7 +21785,7 @@ const openapiDocument = {
21780
21785
  "description": "created (the default), asked or concerning.",
21781
21786
  "schema": {
21782
21787
  "default": "created",
21783
- "description": "created (the default): what you created. asked: what other members asked of you, open or answered: rule requests made to you, and objections to rules you wrote; kind may then name only email_rule_request and email_rule_objection. concerning: what other members did that concerns your own mail: a rule that started covering one of your mailboxes, or saved a new version while covering it (email_rule_coverage, the only kind it takes). asked and concerning are for a person's own view only.",
21788
+ "description": "created (the default): what you created. asked: what other members asked of you, open or answered: rule requests made to you, and objections to rules you wrote; kind may then name only email_rule_request and email_rule_objection. concerning: what other members did that concerns your own mail: an active rule that started covering one of your mailboxes, or saved a new version while covering it (email_rule_coverage, the only kind it takes). A rule in draft or observe is not told of; GET /v1/email-rules/covering-me lists it. asked and concerning are for a person's own view only.",
21784
21789
  "type": "string",
21785
21790
  "enum": [
21786
21791
  "created",
@@ -21819,7 +21824,7 @@ const openapiDocument = {
21819
21824
  "name": "limit",
21820
21825
  "in": "query",
21821
21826
  "required": false,
21822
- "description": "Objects per page, from 1 to 200; defaults to 50.",
21827
+ "description": "Objects per page, from 1 to 200; defaults to 50. With relation=concerning the first page may also hold pending items beyond it.",
21823
21828
  "schema": {
21824
21829
  "default": 50,
21825
21830
  "type": "integer",
@@ -21927,7 +21932,7 @@ const openapiDocument = {
21927
21932
  "/email-rules/vocabulary": { "get": {
21928
21933
  "operationId": "getEmailRuleVocabulary",
21929
21934
  "summary": "Read the column catalog",
21930
- "description": "What a rule tree you write yourself can say: each column of the current vocabulary with its type, source, meaning, operators and the value each takes, options, and whether it takes min_confidence; and the tree's limits, its stored size included. Built from the checks a saved or tested tree must pass. A tree is {version: tree_version, vocabulary: version, when, questions}. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
21935
+ "description": "What a rule tree you write yourself can say: each column of the current vocabulary with its type, source, meaning, operators and the value each takes, options, and whether it takes min_confidence; and the tree's limits, its stored size included. Built from the checks a saved or tested tree must pass. A tree is {version: tree_version, vocabulary: version, when, questions}. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
21931
21936
  "tags": ["Email Rules"],
21932
21937
  "security": [{ "BearerAuth": [] }],
21933
21938
  "responses": {
@@ -21965,7 +21970,7 @@ const openapiDocument = {
21965
21970
  }
21966
21971
  },
21967
21972
  "403": {
21968
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
21973
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
21969
21974
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
21970
21975
  },
21971
21976
  "429": {
@@ -21997,7 +22002,7 @@ const openapiDocument = {
21997
22002
  "/email-rules/compile": { "post": {
21998
22003
  "operationId": "compileEmailRule",
21999
22004
  "summary": "Compile a rule description",
22000
- "description": "Turns a plain-English description of which received emails to match into a rule tree over the column contract, with an explanation of what it matches, the assumptions made, and warnings for what the columns cannot express. Where the columns fall short it adds up to five yes/no questions, asked only after the conditions match; they read the subject, the new text and attachment file names, never attachment contents. Nothing is saved. For a rule with no questions, breadth counts, read only, how much of your recent classified mail its conditions match. include_sql returns the SQL it renders to. kind do_not_share compiles for recall. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22005
+ "description": "Turns a plain-English description of which received emails to match into a rule tree over the column contract, with an explanation of what it matches, the assumptions made, and warnings for what the columns cannot express. Where the columns fall short it adds up to five yes/no questions, asked only after the conditions match; they read the subject, the new text and attachment file names, never attachment contents. Nothing is saved. For a rule with no questions, breadth counts, read only, how much of the recent classified mail in evaluation_scope (by default your own mailboxes) its conditions match; for a grant rule with questions, conditions_breadth counts its conditions alone. include_sql returns the SQL it renders to. kind do_not_share compiles for recall. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22001
22006
  "tags": ["Email Rules"],
22002
22007
  "security": [{ "BearerAuth": [] }],
22003
22008
  "requestBody": {
@@ -22052,7 +22057,11 @@ const openapiDocument = {
22052
22057
  }
22053
22058
  },
22054
22059
  "403": {
22055
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
22060
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read.",
22061
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22062
+ },
22063
+ "409": {
22064
+ "description": "email_rule_author_unresolvable: with evaluation_scope selected, you cannot act as a rule's author right now (details.reason left, revoked, suspended, membership_ended or no_mail_read).",
22056
22065
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22057
22066
  },
22058
22067
  "422": {
@@ -22103,7 +22112,7 @@ const openapiDocument = {
22103
22112
  "/email-rules/test": { "post": {
22104
22113
  "operationId": "testEmailRule",
22105
22114
  "summary": "Test a rule against received mail",
22106
- "description": "Runs a rule over received mail and reports what it would decide for each email (matched, near_miss, no_match, unclassified, skipped or not_found), with a summary of how each top-level condition and question decided. Test an unsaved tree, or a saved rule by rule_id (its current version, as its author). It reads only mail its author can read and its scope covers. Without email_ids it tests the newest emails, at most 200 and none older than 30 days, plus any expected ones. Unclassified emails over 2 hours old are classified first unless classify_missing is false. It records no decision and grants nothing, and stops at about 90 seconds. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22115
+ "description": "Runs a rule over received mail and reports what it would decide for each email (matched, near_miss, no_match, unclassified, skipped or not_found), with a summary of how each top-level condition and question decided. Test an unsaved tree, or a saved rule by rule_id (its current version, as its author). It reads only mail its author can read and its scope covers. A rule you see only because it covers your own mailboxes runs as you, over only those, from the dashboard or the Primitive CLI. Without email_ids it tests the newest emails, at most 200 and none older than 30 days, plus any expected ones. Unclassified emails over 2 hours old are classified first unless classify_missing is false. It records no decision and grants nothing, and stops at about 90 seconds. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22107
22116
  "tags": ["Email Rules"],
22108
22117
  "security": [{ "BearerAuth": [] }],
22109
22118
  "requestBody": {
@@ -22149,15 +22158,15 @@ const openapiDocument = {
22149
22158
  }
22150
22159
  },
22151
22160
  "403": {
22152
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and a rule decides on what emails say. email_rule_scope_not_readable: scope_selection names something you cannot read.",
22161
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and a rule decides on what emails say. email_rule_scope_not_readable: scope_selection names something you cannot read. forbidden with details.reason covering_view_only: you read this rule only in the covering view, since it decided on your mail before and covers none of your mailboxes now (details.covering_view).",
22153
22162
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22154
22163
  },
22155
22164
  "404": {
22156
- "description": "not_found: rule_id names no rule you can see (details.reason rule_not_found).",
22165
+ "description": "not_found: rule_id names no rule you can see or that covers your mailboxes (details.reason rule_not_found).",
22157
22166
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22158
22167
  },
22159
22168
  "409": {
22160
- "description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree, the caller). details.reason is left, revoked, suspended, membership_ended, no_mail_read, no_own_mailbox, no_shareable_mailbox or no_selected_mailbox.",
22169
+ "description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree or a rule covering you, the caller). details.reason is left, revoked, suspended, membership_ended, no_mail_read, no_own_mailbox, no_shareable_mailbox or no_selected_mailbox.",
22161
22170
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22162
22171
  },
22163
22172
  "429": {
@@ -22200,7 +22209,7 @@ const openapiDocument = {
22200
22209
  "/email-rules/look-alikes": { "post": {
22201
22210
  "operationId": "generateLookAlikes",
22202
22211
  "summary": "Write look-alike emails a rule should or should not match",
22203
- "description": "Writes made-up emails that come close to what a grant rule describes, and reports what the rule decides for each, as test-sample would. Most should not match (expected no_match): business near misses from its senders and topic (kind other: an all-clear, a reply with nothing new, a related document, another subject) and private mail (a personal medical bill, pay for named people, a personal purchase, personal banking or tax). By default a third, rounded down, are near cases it should match (expected match, kind other). failed means the rule decided one against expected. Nothing is stored and no mail is read. Keep them, with their expected, with add_expectation_samples, or for a request with the accept's expectation_samples. Limited to 20 an hour per member. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22212
+ "description": "Writes made-up emails that come close to what a grant rule describes, and reports what the rule decides for each, as test-sample would. Most should not match (expected no_match): business near misses from its senders and topic (kind other: an all-clear, a reply with nothing new, a related document, another subject) and private mail (a personal medical bill, pay for named people, a personal purchase, personal banking or tax). By default a third, rounded down, are near cases it should match (expected match, kind other). failed means the rule decided one against expected. Nothing is stored and no mail is read. Keep them, with their expected, with add_expectation_samples, or for a request with the accept's expectation_samples. Limited to 20 an hour per member. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22204
22213
  "tags": ["Email Rules"],
22205
22214
  "security": [{ "BearerAuth": [] }],
22206
22215
  "requestBody": {
@@ -22246,15 +22255,15 @@ const openapiDocument = {
22246
22255
  }
22247
22256
  },
22248
22257
  "403": {
22249
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. email_rule_scope_not_readable: scope_selection names something you cannot read.",
22258
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read. forbidden with details.reason covering_view_only: you read this rule only in the covering view, since it decided on your mail before and covers none of your mailboxes now (details.covering_view).",
22250
22259
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22251
22260
  },
22252
22261
  "404": {
22253
- "description": "not_found: rule_id names no rule you can see (details.reason rule_not_found), or request_id no rule request you can see (request_not_found).",
22262
+ "description": "not_found: rule_id names no rule you can see or that covers your mailboxes (details.reason rule_not_found), or request_id no rule request you can see (request_not_found).",
22254
22263
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22255
22264
  },
22256
22265
  "409": {
22257
- "description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree or a request, you). email_rule_request_not_pending: request_id names a request already decided.",
22266
+ "description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree, a request or a rule covering you, you). email_rule_request_not_pending: request_id names a request already decided.",
22258
22267
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22259
22268
  },
22260
22269
  "422": {
@@ -22305,7 +22314,7 @@ const openapiDocument = {
22305
22314
  "/email-rules/test-sample": { "post": {
22306
22315
  "operationId": "testEmailRuleOnSample",
22307
22316
  "summary": "Test a rule against a made-up email",
22308
- "description": "Reports what a rule would decide for an email you describe (sender, recipients, subject, plain text, attachment types; optionally automated, from a mailing list, and a spam score), classified and decided as received mail would be. A rule reading a spam score you gave none for decides on none (unset_columns). Nothing is stored. Test an unsaved tree or a saved rule by rule_id. The answer includes what the classifier read. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22317
+ "description": "Reports what a rule would decide for an email you describe (sender, recipients, subject, plain text, attachment types and file names; optionally automated, from a mailing list, and a spam score), classified and decided as received mail would be. A rule reading a spam score you gave none for decides on none (unset_columns). Nothing is stored. Test an unsaved tree or a saved rule by rule_id. The answer includes what the classifier read. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22309
22318
  "tags": ["Email Rules"],
22310
22319
  "security": [{ "BearerAuth": [] }],
22311
22320
  "requestBody": {
@@ -22351,15 +22360,15 @@ const openapiDocument = {
22351
22360
  }
22352
22361
  },
22353
22362
  "403": {
22354
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. email_rule_scope_not_readable: scope_selection names something you cannot read.",
22363
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read. forbidden with details.reason covering_view_only: you read this rule only in the covering view, since it decided on your mail before and covers none of your mailboxes now (details.covering_view).",
22355
22364
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22356
22365
  },
22357
22366
  "404": {
22358
- "description": "not_found: rule_id names no rule you can see (details.reason rule_not_found).",
22367
+ "description": "not_found: rule_id names no rule you can see or that covers your mailboxes (details.reason rule_not_found).",
22359
22368
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22360
22369
  },
22361
22370
  "409": {
22362
- "description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree, you), so it would decide nothing for real. details.reason is left, revoked, suspended, membership_ended, no_mail_read, no_own_mailbox, no_shareable_mailbox or no_selected_mailbox.",
22371
+ "description": "email_rule_author_unresolvable: the rule cannot run as its author (for an unsaved tree or a rule covering you, you), so it would decide nothing for real. details.reason is left, revoked, suspended, membership_ended, no_mail_read, no_own_mailbox, no_shareable_mailbox or no_selected_mailbox.",
22363
22372
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22364
22373
  },
22365
22374
  "429": {
@@ -22403,7 +22412,7 @@ const openapiDocument = {
22403
22412
  "post": {
22404
22413
  "operationId": "createEmailRule",
22405
22414
  "summary": "Create a rule",
22406
- "description": "Saves a rule as version 1 from a compile response or a tree you wrote, checked against the column contract. It starts in observe (recorded, nothing granted or blocked) unless state is draft, or active for a do_not_share rule. You are its author, and it never reaches mail you cannot read. evaluation_scope is own_mailboxes unless you name author or selected (scope_selection names only what you can read). expectations are judged in expectations_check. kind do_not_share blocks sharing what it matches; a key cannot list it (details.reason rule_blocks_sharing). Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22415
+ "description": "Saves a rule as version 1 from a compile response or a tree you wrote, checked against the column contract. It starts in observe (recorded, nothing granted or blocked) unless state is draft, or active for a do_not_share rule. You are its author, and it never reaches mail you cannot read. evaluation_scope is own_mailboxes unless you name author or selected (scope_selection names only what you can read). expectations are judged in expectations_check. kind do_not_share blocks sharing what it matches; a key cannot list it (details.reason rule_blocks_sharing). Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22407
22416
  "tags": ["Email Rules"],
22408
22417
  "security": [{ "BearerAuth": [] }],
22409
22418
  "requestBody": {
@@ -22452,7 +22461,7 @@ const openapiDocument = {
22452
22461
  }
22453
22462
  },
22454
22463
  "403": {
22455
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. email_rule_scope_not_readable: scope_selection names something you cannot read (details.addresses, details.domains).",
22464
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_scope_not_readable: scope_selection names something you cannot read (details.addresses, details.domains).",
22456
22465
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22457
22466
  },
22458
22467
  "409": {
@@ -22499,7 +22508,7 @@ const openapiDocument = {
22499
22508
  "get": {
22500
22509
  "operationId": "listEmailRules",
22501
22510
  "summary": "List rules",
22502
- "description": "The rules the caller can see, newest first: their own, or every member's rules for a caller who reads the whole organization's mail. Each carries its scope (evaluation_scope, and scope_selection for a selected rule) and current_version, but not the version's text: read that under current from GET /email-rules/{id}. With matched_email_id, only the rules that matched that email. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22511
+ "description": "The rules the caller can see, newest first: their own, or every member's rules for a caller who reads the whole organization's mail. Each carries its scope (evaluation_scope, and scope_selection for a selected rule) and current_version, but not the version's text: read that under current from GET /email-rules/{id}. With matched_email_id, only the rules that matched that email. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22503
22512
  "tags": ["Email Rules"],
22504
22513
  "security": [{ "BearerAuth": [] }],
22505
22514
  "parameters": [
@@ -22588,7 +22597,7 @@ const openapiDocument = {
22588
22597
  }
22589
22598
  },
22590
22599
  "403": {
22591
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. grant_forbidden with details.reason mail_depth: matched_email_id with access to mail that shows headers only, since which rules matched an email is read from what it says.",
22600
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.reason mail_depth: matched_email_id with access to mail that shows headers only, since which rules matched an email is read from what it says.",
22592
22601
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22593
22602
  },
22594
22603
  "429": {
@@ -22622,7 +22631,7 @@ const openapiDocument = {
22622
22631
  "get": {
22623
22632
  "operationId": "getEmailRule",
22624
22633
  "summary": "Read a rule",
22625
- "description": "A rule, its scope (evaluation_scope, and scope_selection for a selected rule), its current version and its saved expectations. The current version's text (english, tree, explanation, assumptions, warnings) is under current, and current_version is its number. What the scope comes to for its author now is GET /email-rules/{id}/scope. The SQL the version renders to is included only when include_sql is true. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22634
+ "description": "A rule, its scope (evaluation_scope, and scope_selection for a selected rule), its current version and its saved expectations. The current version's text (english, tree, explanation, assumptions, warnings) is under current, and current_version is its number. What the scope comes to for its author now is GET /email-rules/{id}/scope. The SQL the version renders to is included only when include_sql is true. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22626
22635
  "tags": ["Email Rules"],
22627
22636
  "security": [{ "BearerAuth": [] }],
22628
22637
  "parameters": [{
@@ -22692,7 +22701,7 @@ const openapiDocument = {
22692
22701
  }
22693
22702
  },
22694
22703
  "403": {
22695
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. forbidden with details.reason covering_view_only (your dashboard session or the Primitive CLI only): you may read this rule only because it covers your mailbox; details.covering_view is where (getEmailRuleCoveringMe).",
22704
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. forbidden with details.reason covering_view_only (your dashboard session or the Primitive CLI only): you may read this rule only because it covers your mailbox or decided on your mail; details.covering_view is where (getEmailRuleCoveringMe).",
22696
22705
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22697
22706
  },
22698
22707
  "404": {
@@ -22737,7 +22746,7 @@ const openapiDocument = {
22737
22746
  "patch": {
22738
22747
  "operationId": "updateEmailRule",
22739
22748
  "summary": "Change a rule",
22740
- "description": "Renames the rule, saves a new version (english and tree together; earlier versions stay) or restores one (restore_version), changes its state (draft is off; observe; active), its scope (evaluation_scope, with scope_selection when selected) or its saved expectations (judged in expectations_check). Anyone but its author who sees it may only set state draft, and while it is shared only if they manage members from the dashboard. A scope change saves a new version; a shared rule keeps its scope, and a kind never changes. A saved version takes effect at once. if_version refuses a write once a version was saved since you read it; if_revision, once anything changed. Without if_expectations_pass a version is saved even when its expectations fail. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22749
+ "description": "Renames the rule, saves a new version (english and tree together; earlier versions stay) or restores one (restore_version), changes its state (draft is off; observe; active), its scope (evaluation_scope, with scope_selection when selected) or its saved expectations (judged in expectations_check). Anyone but its author who sees it may only set state draft, and while it is shared only if they manage members from the dashboard. A scope change saves a new version; a shared rule keeps its scope, and a kind never changes. A saved version takes effect at once. if_version refuses a write once a version was saved since you read it; if_revision, once anything changed. Without if_expectations_pass a version is saved even when its expectations fail, except on an active rule keys or webhooks read: that write is checked first unless if_expectations_pass is false. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22741
22750
  "tags": ["Email Rules"],
22742
22751
  "security": [{ "BearerAuth": [] }],
22743
22752
  "parameters": [{
@@ -22770,7 +22779,7 @@ const openapiDocument = {
22770
22779
  }
22771
22780
  },
22772
22781
  "400": {
22773
- "description": "validation_error: invalid body or tree (details.errors), too large (too_large), a version that no longer renders (version_not_renderable), restore_version names the current version (already_current), or over 20 made-up emails (too_many_expectation_samples). no_expectations: if_expectations_pass with no expectations to judge; nothing was saved.",
22782
+ "description": "validation_error: invalid body or tree (details.errors), too large (too_large), a version that no longer renders (version_not_renderable), restore_version names the current version (already_current), over 20 made-up emails (too_many_expectation_samples), or a kind other than the rule's own (kind_mismatch). no_expectations: if_expectations_pass with no expectations to judge, or a write that removes them from an active rule keys or webhooks read; nothing was saved.",
22774
22783
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22775
22784
  },
22776
22785
  "401": {
@@ -22793,7 +22802,7 @@ const openapiDocument = {
22793
22802
  }
22794
22803
  },
22795
22804
  "403": {
22796
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. email_rule_author_only: only the rule's author may make this change. forbidden (rule_shared): the rule is shared, so only its author or a member manager can turn it off. email_rule_scope_not_readable: scope_selection names something you cannot read. grant_forbidden (mail_depth): if_expectations_pass needs content access to mail when it judges emails by id.",
22805
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_author_only: only the rule's author may make this change. forbidden (rule_shared): the rule is shared, so only its author or a member manager can turn it off. email_rule_scope_not_readable: scope_selection names something you cannot read. grant_forbidden (mail_depth): judging a write (if_expectations_pass, or by default) needs content access to mail when it judges emails by id.",
22797
22806
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22798
22807
  },
22799
22808
  "404": {
@@ -22801,7 +22810,7 @@ const openapiDocument = {
22801
22810
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22802
22811
  },
22803
22812
  "409": {
22804
- "description": "email_rule_conflict: the rule changed (details.reason version_moved with current_version, revision_moved with current_revision, expectations_moved or rule_changed); read it again and retry. email_rule_scope_locked: the rule is shared. email_rule_author_unresolvable: the rule could not run as you over the chosen scope. expectations_failed, expectations_unreadable (details.unreadable) or expectations_undecided, with if_expectations_pass: nothing was saved; details carries current_version, state and expectations_check.",
22813
+ "description": "email_rule_conflict: the rule changed (details.reason version_moved with current_version, revision_moved with current_revision, expectations_moved, rule_changed or readers_changed); read it again and retry. email_rule_scope_locked: the rule is shared. email_rule_author_unresolvable: the rule could not run as you over the chosen scope. expectations_failed, expectations_unreadable (details.unreadable) or expectations_undecided, with if_expectations_pass or by default on an active rule keys or webhooks read (details.read_by): nothing was saved; details carries current_version, state and expectations_check.",
22805
22814
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22806
22815
  },
22807
22816
  "429": {
@@ -22826,6 +22835,10 @@ const openapiDocument = {
22826
22835
  }
22827
22836
  }
22828
22837
  } }
22838
+ },
22839
+ "503": {
22840
+ "description": "service_unavailable (rule_keys_unread or rule_readers_unread): the keys or readers of an active rule could not be read in time; nothing was saved.",
22841
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22829
22842
  }
22830
22843
  },
22831
22844
  "x-primitive-permission": ["mail:read"]
@@ -22833,7 +22846,7 @@ const openapiDocument = {
22833
22846
  "delete": {
22834
22847
  "operationId": "deleteEmailRule",
22835
22848
  "summary": "Delete a rule",
22836
- "description": "Deletes a rule with its versions, its decisions and the access it granted. A share of it ends (end_reason rule_deleted), and the member who shared can still list what it handed over. The answer lists the shares it ended (ended_shares) and the unrevoked keys that still list it, which now read nothing through it (keys, other_keys; left out when they could not be read). The author or a reader of all the organization's mail may delete it; while it is shared, only the author or someone who manages members from the dashboard. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22849
+ "description": "Deletes a rule with its versions, its decisions and the access it granted. A share of it ends (end_reason rule_deleted), and the member who shared can still list what it handed over. The answer lists the shares it ended (ended_shares), says whom with (share_notice), and lists the unrevoked keys that still list it, which now read nothing through it (keys, other_keys; left out when they could not be read). The author or a reader of all the organization's mail may delete it; while it is shared, only the author or someone who manages members from the dashboard. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22837
22850
  "tags": ["Email Rules"],
22838
22851
  "security": [{ "BearerAuth": [] }],
22839
22852
  "parameters": [{
@@ -22881,7 +22894,7 @@ const openapiDocument = {
22881
22894
  }
22882
22895
  },
22883
22896
  "403": {
22884
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. forbidden with details.reason rule_shared: the rule is shared with another member.",
22897
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. forbidden with details.reason rule_shared: the rule is shared with another member.",
22885
22898
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
22886
22899
  },
22887
22900
  "404": {
@@ -22927,7 +22940,7 @@ const openapiDocument = {
22927
22940
  "/email-rules/{id}/decisions": { "get": {
22928
22941
  "operationId": "listEmailRuleDecisions",
22929
22942
  "summary": "List a rule's decisions",
22930
- "description": "What the rule decided for each email it was evaluated against, newest first: whether it matched, whether it nearly did, how each part decided and the answers to its questions, with which email it was (from_address, subject, received_at). Only decisions on emails the caller can read are listed. With matched, only matches (true) or only the rest (false). Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
22943
+ "description": "What the rule decided for each email it was evaluated against, newest first: whether it matched, whether it nearly did, how each part decided and the answers to its questions, with which email it was (from_address, subject, received_at). Only decisions on emails the caller can read are listed. With matched, only matches (true) or only the rest (false). Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22931
22944
  "tags": ["Email Rules"],
22932
22945
  "security": [{ "BearerAuth": [] }],
22933
22946
  "parameters": [
@@ -23026,7 +23039,7 @@ const openapiDocument = {
23026
23039
  }
23027
23040
  },
23028
23041
  "403": {
23029
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and what a rule decided is read from what emails say.",
23042
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and what a rule decided is read from what emails say. forbidden with details.reason covering_view_only (your dashboard session or the Primitive CLI only): you may read this rule only because it covers your mailbox or decided on your mail; details.covering_view is where (listEmailRuleDecisionsOnMyMail).",
23030
23043
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23031
23044
  },
23032
23045
  "404": {
@@ -23071,7 +23084,7 @@ const openapiDocument = {
23071
23084
  "/emails/{id}/rule-results": { "get": {
23072
23085
  "operationId": "listEmailRuleResults",
23073
23086
  "summary": "List every rule's result on an email",
23074
- "description": "What each rule you can see decided about this email (matched, not matched, or not run), in the words of the deciding version. A do_not_share rule that matched it blocks sharing while active; earlier shares stay. Only a caller who can read the email gets an answer. classification is every fact the classifier read, as rule conditions read it. At most 50 rules are listed, results first; not_listed counts the rest. Another member's rule that decided on your own mail is listed read_only (dashboard and CLI only), with who receives what it shares (receivers) while it covers you; hidden counts deciding rules you cannot see. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23087
+ "description": "What each rule you can see decided about this email (matched, not matched, or not run), in the words of the deciding version. A do_not_share rule that matched it blocks sharing while active; earlier shares stay. Only a caller who can read the email gets an answer. classification is every fact the classifier read, as rule conditions read it. At most 50 rules are listed, results first; not_listed counts the rest. Another member's rule that decided on your own mail is listed read_only (dashboard and CLI only), with who receives what it shares (receivers) while it covers you; hidden counts deciding rules you cannot see. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23075
23088
  "tags": ["Email Rules"],
23076
23089
  "security": [{ "BearerAuth": [] }],
23077
23090
  "parameters": [{
@@ -23119,7 +23132,7 @@ const openapiDocument = {
23119
23132
  }
23120
23133
  },
23121
23134
  "403": {
23122
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and what a rule decided is read from what the email says.",
23135
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and what a rule decided is read from what the email says.",
23123
23136
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23124
23137
  },
23125
23138
  "404": {
@@ -23155,7 +23168,7 @@ const openapiDocument = {
23155
23168
  "/email-rules/{id}/versions": { "get": {
23156
23169
  "operationId": "listEmailRuleVersions",
23157
23170
  "summary": "Read versions of a rule",
23158
- "description": "A rule's versions as they were written: what each one's conditions and questions were, so a decision can be read against the version that made it. Without versions, every version newest first, a page at a time (meta.cursor). With versions, only those named, oldest first; numbers that are not versions of the rule are left out. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23171
+ "description": "A rule's versions as they were written: what each one's conditions and questions were, so a decision can be read against the version that made it. Without versions, every version newest first, a page at a time (meta.cursor). With versions, only those named, oldest first; numbers that are not versions of the rule are left out. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23159
23172
  "tags": ["Email Rules"],
23160
23173
  "security": [{ "BearerAuth": [] }],
23161
23174
  "parameters": [
@@ -23196,7 +23209,7 @@ const openapiDocument = {
23196
23209
  "required": false,
23197
23210
  "description": "Read only these version numbers, comma-separated, at most 100. Not with limit or cursor.",
23198
23211
  "schema": {
23199
- "description": "Read only these version numbers, comma-separated. Without it, every version, newest first, a page at a time.",
23212
+ "description": "Read only these version numbers, comma-separated. Without it, the versions listed, newest first, a page at a time.",
23200
23213
  "type": "string",
23201
23214
  "pattern": "^[1-9]\\d{0,8}(,[1-9]\\d{0,8}){0,99}$"
23202
23215
  }
@@ -23256,7 +23269,7 @@ const openapiDocument = {
23256
23269
  }
23257
23270
  },
23258
23271
  "403": {
23259
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
23272
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. forbidden with details.reason covering_view_only (your dashboard session or the Primitive CLI only): you may read this rule only because it covers your mailbox or decided on your mail; details.covering_view is where (listEmailRuleVersionsCoveringMe while it covers you).",
23260
23273
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23261
23274
  },
23262
23275
  "404": {
@@ -23301,7 +23314,7 @@ const openapiDocument = {
23301
23314
  "/email-rules/{id}/scope": { "get": {
23302
23315
  "operationId": "getEmailRuleScope",
23303
23316
  "summary": "Read what a rule covers",
23304
- "description": "Whose mail the rule runs over, and what that comes to now: a rule never reaches further than what its author can read when it runs, so what a selected rule names and its author can no longer read stops matching. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23317
+ "description": "Whose mail the rule runs over, and what that comes to now: a rule never reaches further than what its author can read when it runs, so what a selected rule names and its author can no longer read stops matching. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23305
23318
  "tags": ["Email Rules"],
23306
23319
  "security": [{ "BearerAuth": [] }],
23307
23320
  "parameters": [{
@@ -23349,7 +23362,7 @@ const openapiDocument = {
23349
23362
  }
23350
23363
  },
23351
23364
  "403": {
23352
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
23365
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
23353
23366
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23354
23367
  },
23355
23368
  "404": {
@@ -23385,7 +23398,7 @@ const openapiDocument = {
23385
23398
  "/email-rule-scope-options": { "get": {
23386
23399
  "operationId": "listEmailRuleScopeOptions",
23387
23400
  "summary": "List what a rule can cover",
23388
- "description": "How much the caller can read, and the inboxes and domains the caller can read, which is what they may name when a rule runs over selected inboxes and domains. For a caller who reads the whole organization, addresses lists the inboxes known as someone's (member addresses, members' verified work emails at the organization's domains, agents' addresses and inboxes with shared access), not every address that receives mail: may_name_unlisted says when an address that is not listed, such as an inbox nobody has claimed, may be named too. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23401
+ "description": "How much the caller can read, and the inboxes and domains the caller can read, which is what they may name when a rule runs over selected inboxes and domains. For a caller who reads the whole organization, addresses lists the inboxes known as someone's (member addresses, members' verified work emails at the organization's domains, agents' addresses and inboxes with shared access), not every address that receives mail: may_name_unlisted says when an address that is not listed, such as an inbox nobody has claimed, may be named too. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23389
23402
  "tags": ["Email Rules"],
23390
23403
  "security": [{ "BearerAuth": [] }],
23391
23404
  "parameters": [{
@@ -23447,7 +23460,7 @@ const openapiDocument = {
23447
23460
  }
23448
23461
  },
23449
23462
  "403": {
23450
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
23463
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
23451
23464
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23452
23465
  },
23453
23466
  "409": {
@@ -23484,7 +23497,7 @@ const openapiDocument = {
23484
23497
  "post": {
23485
23498
  "operationId": "createEmailRuleRequest",
23486
23499
  "summary": "Ask a member for a rule",
23487
- "description": "Asks another member of the organization for a rule over their own mail, from a compile response or a tree you wrote. Name them by user id, or by their member mailbox address, or by their verified work email if you can list members (at most 30 lookups by address an hour). Nothing is evaluated or shared until they accept, and they may change the rule first. A message (up to 500 characters, fixed once sent) tells them why you ask. At most 5 pending requests to one member and 50 in all. With members:read the answer says whether they can accept it under their role (target_can_accept, target_warning); POST /v1/email-rule-requests/target-check checks first. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23500
+ "description": "Asks another member of the organization for a rule over their own mail, from a compile response or a tree you wrote. Name them by user id, or by their member mailbox address, or by their verified work email if you can list members (at most 30 lookups by address an hour). Nothing is evaluated or shared until they accept, and they may change the rule first. A message (up to 500 characters, fixed once sent) tells them why you ask. At most 5 pending requests to one member and 50 in all. With members:read the answer says whether they can accept it under their role (target_can_accept, target_warning); POST /v1/email-rule-requests/target-check checks first. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23488
23501
  "tags": ["Email Rules"],
23489
23502
  "security": [{ "BearerAuth": [] }],
23490
23503
  "requestBody": {
@@ -23524,7 +23537,7 @@ const openapiDocument = {
23524
23537
  }
23525
23538
  },
23526
23539
  "403": {
23527
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
23540
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
23528
23541
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23529
23542
  },
23530
23543
  "404": {
@@ -23579,7 +23592,7 @@ const openapiDocument = {
23579
23592
  "get": {
23580
23593
  "operationId": "listEmailRuleRequests",
23581
23594
  "summary": "List rule requests",
23582
- "description": "Rule requests, newest first: those made to you (box incoming, the default), those you made (outgoing), or every one you can see (all). Pending ones by default; status decided lists accepted, declined and withdrawn ones, and any lists both. A request is visible to the member who made it, the member it was made to, and the organization's owners and admins. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23595
+ "description": "Rule requests, newest first: those made to you (box incoming, the default), those you made (outgoing), or every one you can see (all). Pending ones by default; status decided lists accepted, declined and withdrawn ones, and any lists both. A request is visible to the member who made it, the member it was made to, and the organization's owners and admins. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23583
23596
  "tags": ["Email Rules"],
23584
23597
  "security": [{ "BearerAuth": [] }],
23585
23598
  "parameters": [
@@ -23688,7 +23701,7 @@ const openapiDocument = {
23688
23701
  }
23689
23702
  },
23690
23703
  "403": {
23691
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
23704
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
23692
23705
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23693
23706
  },
23694
23707
  "429": {
@@ -23721,7 +23734,7 @@ const openapiDocument = {
23721
23734
  "/email-rule-requests/target-check": { "post": {
23722
23735
  "operationId": "checkEmailRuleRequestTarget",
23723
23736
  "summary": "Check whether a member can accept a rule request",
23724
- "description": "Says, before you ask, whether a member could accept a rule request now under their role, and if not, which permission their role lacks. Nothing is sent and nothing is recorded. Needs members:read, as listing members and their roles does. At most 60 checks an hour. Name them by user id, or by their member mailbox address or verified work email (counted with asking: at most 30 lookups by address an hour). Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23737
+ "description": "Says, before you ask, whether a member could accept a rule request now under their role, and if not, which permission their role lacks. Nothing is sent and nothing is recorded. Needs members:read, as listing members and their roles does. At most 60 checks an hour. Name them by user id, or by their member mailbox address or verified work email (counted with asking: at most 30 lookups by address an hour). Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23725
23738
  "tags": ["Email Rules"],
23726
23739
  "security": [{ "BearerAuth": [] }],
23727
23740
  "requestBody": {
@@ -23767,7 +23780,7 @@ const openapiDocument = {
23767
23780
  }
23768
23781
  },
23769
23782
  "403": {
23770
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. grant_forbidden with details.permission members:read: you cannot list members and their roles.",
23783
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. grant_forbidden with details.permission members:read: you cannot list members and their roles.",
23771
23784
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23772
23785
  },
23773
23786
  "404": {
@@ -23819,7 +23832,7 @@ const openapiDocument = {
23819
23832
  "get": {
23820
23833
  "operationId": "getEmailRuleRequest",
23821
23834
  "summary": "Read a rule request",
23822
- "description": "A rule request: the rule as proposed, who made it and to whom, and whether each of them is still a member. A request is visible to the member who made it, the member it was made to, and the organization's owners and admins. If you are the member asked, you can try the rule as proposed before answering: pass the request's tree to POST /v1/email-rules/test to test it on mail you have received. To try it on an email that has not arrived, pass the request's tree to POST /v1/email-rules/test-sample with one you make up, or have look-alikes written (POST /v1/email-rules/look-alikes with request_id); neither stores anything. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23835
+ "description": "A rule request: the rule as proposed, who made it and to whom, and whether each of them is still a member. A request is visible to the member who made it, the member it was made to, and the organization's owners and admins. If you are the member asked, you can try the rule as proposed before answering: pass the request's tree to POST /v1/email-rules/test to test it on mail you have received. To try it on an email that has not arrived, pass the request's tree to POST /v1/email-rules/test-sample with one you make up, or have look-alikes written (POST /v1/email-rules/look-alikes with request_id); neither stores anything. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23823
23836
  "tags": ["Email Rules"],
23824
23837
  "security": [{ "BearerAuth": [] }],
23825
23838
  "parameters": [{
@@ -23867,7 +23880,7 @@ const openapiDocument = {
23867
23880
  }
23868
23881
  },
23869
23882
  "403": {
23870
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
23883
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
23871
23884
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23872
23885
  },
23873
23886
  "404": {
@@ -23903,7 +23916,7 @@ const openapiDocument = {
23903
23916
  "delete": {
23904
23917
  "operationId": "deleteEmailRuleRequest",
23905
23918
  "summary": "Delete a withdrawn rule request",
23906
- "description": "Deletes a request you withdrew, for everyone who could see it. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
23919
+ "description": "Deletes a request you withdrew, for everyone who could see it. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23907
23920
  "tags": ["Email Rules"],
23908
23921
  "security": [{ "BearerAuth": [] }],
23909
23922
  "parameters": [{
@@ -23951,7 +23964,7 @@ const openapiDocument = {
23951
23964
  }
23952
23965
  },
23953
23966
  "403": {
23954
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. email_rule_request_proposer_only: only the member who made the request may delete it.",
23967
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_request_proposer_only: only the member who made the request may delete it.",
23955
23968
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
23956
23969
  },
23957
23970
  "404": {
@@ -23992,7 +24005,7 @@ const openapiDocument = {
23992
24005
  "/email-rule-requests/{id}/decline": { "post": {
23993
24006
  "operationId": "declineEmailRuleRequest",
23994
24007
  "summary": "Decline a rule request",
23995
- "description": "Declines a pending request made to you. Nothing is written but the decision and, if you give one, its reason (reason, up to 500 characters), which the member who asked sees with the request. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
24008
+ "description": "Declines a pending request made to you. Nothing is written but the decision and, if you give one, its reason (reason, up to 500 characters), which the member who asked sees with the request. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
23996
24009
  "tags": ["Email Rules"],
23997
24010
  "security": [{ "BearerAuth": [] }],
23998
24011
  "parameters": [{
@@ -24058,7 +24071,7 @@ const openapiDocument = {
24058
24071
  }
24059
24072
  },
24060
24073
  "403": {
24061
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. email_rule_request_target_only: only the member the request was made to may decline it.",
24074
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_request_target_only: only the member the request was made to may decline it.",
24062
24075
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
24063
24076
  },
24064
24077
  "404": {
@@ -24098,7 +24111,7 @@ const openapiDocument = {
24098
24111
  "/email-rule-requests/{id}/withdraw": { "post": {
24099
24112
  "operationId": "withdrawEmailRuleRequest",
24100
24113
  "summary": "Withdraw a rule request",
24101
- "description": "Withdraws a pending request you made. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
24114
+ "description": "Withdraws a pending request you made. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
24102
24115
  "tags": ["Email Rules"],
24103
24116
  "security": [{ "BearerAuth": [] }],
24104
24117
  "parameters": [{
@@ -24156,7 +24169,7 @@ const openapiDocument = {
24156
24169
  }
24157
24170
  },
24158
24171
  "403": {
24159
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. email_rule_request_proposer_only: only the member who made the request may withdraw it.",
24172
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_request_proposer_only: only the member who made the request may withdraw it.",
24160
24173
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
24161
24174
  },
24162
24175
  "404": {
@@ -24196,7 +24209,7 @@ const openapiDocument = {
24196
24209
  "/email-rule-requests/{id}/accept": { "post": {
24197
24210
  "operationId": "acceptEmailRuleRequest",
24198
24211
  "summary": "Accept a rule request",
24199
- "description": "Writes the rule as yours, over your own mailboxes only, turns it on, and shares what it matches from now on with the member who asked: each email in full with its attachments, but never raw. It does not go back through mail already received. Send no body to accept it as asked, or change it first with english and tree together (and an explanation, or the server writes one). Because this gives your mail to someone else, it is limited to the dashboard and the Primitive CLI: other OAuth apps and agent tokens are refused. Before accepting, test it on your mail: pass the request's tree to POST /v1/email-rules/test. To try it on an email that has not arrived, pass the request's tree to POST /v1/email-rules/test-sample with one you make up, or have look-alikes written (POST /v1/email-rules/look-alikes with request_id); neither stores anything. expectation_samples are kept on the rule it writes. Accepting also needs mail:share_own. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
24212
+ "description": "Writes the rule as yours, over your own mailboxes only, turns it on, and shares what it matches from now on with the member who asked: each email in full with its attachments, but never raw. It does not go back through mail already received. Send no body to accept it as asked, or change it first with english and tree together (and an explanation, or the server writes one). Because this gives your mail to someone else, it is limited to the dashboard and the Primitive CLI: other OAuth apps and agent tokens are refused. Before accepting, test it on your mail: pass the request's tree to POST /v1/email-rules/test. To try it on an email that has not arrived, pass the request's tree to POST /v1/email-rules/test-sample with one you make up, or have look-alikes written (POST /v1/email-rules/look-alikes with request_id); neither stores anything. expectation_samples are kept on the rule it writes. Accepting also needs mail:share_own. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
24200
24213
  "tags": ["Email Rules"],
24201
24214
  "security": [{ "BearerAuth": [] }],
24202
24215
  "parameters": [{
@@ -24256,7 +24269,7 @@ const openapiDocument = {
24256
24269
  }
24257
24270
  },
24258
24271
  "403": {
24259
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead. email_rule_request_target_only: only the member the request was made to may accept it. forbidden with details.reason first_party_required: accepted from another OAuth app or with an agent token. grant_forbidden with details.permission mail:share_own: the caller's role does not let them share their own mail through email rules.",
24272
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden. email_rule_request_target_only: only the member the request was made to may accept it. forbidden with details.reason first_party_required: accepted from another OAuth app or with an agent token. grant_forbidden with details.permission mail:share_own: the caller's role does not let them share their own mail through email rules.",
24260
24273
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
24261
24274
  },
24262
24275
  "404": {
@@ -24300,7 +24313,7 @@ const openapiDocument = {
24300
24313
  "/email-rule-shares": { "get": {
24301
24314
  "operationId": "listEmailRuleShares",
24302
24315
  "summary": "List rule shares",
24303
- "description": "Lists rules shared with you, rules you share, or every share you can see. A share is visible to its two people and the organization's owners and admins. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
24316
+ "description": "Lists rules shared with you, rules you share, or every share you can see. A share is visible to its two people and the organization's owners and admins. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
24304
24317
  "tags": ["Email Rules"],
24305
24318
  "security": [{ "BearerAuth": [] }],
24306
24319
  "parameters": [
@@ -24401,7 +24414,7 @@ const openapiDocument = {
24401
24414
  }
24402
24415
  },
24403
24416
  "403": {
24404
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
24417
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
24405
24418
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
24406
24419
  },
24407
24420
  "429": {
@@ -24550,7 +24563,7 @@ const openapiDocument = {
24550
24563
  "get": {
24551
24564
  "operationId": "getEmailRuleShare",
24552
24565
  "summary": "Get a rule share",
24553
- "description": "One share. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
24566
+ "description": "One share. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
24554
24567
  "tags": ["Email Rules"],
24555
24568
  "security": [{ "BearerAuth": [] }],
24556
24569
  "parameters": [{
@@ -24598,7 +24611,7 @@ const openapiDocument = {
24598
24611
  }
24599
24612
  },
24600
24613
  "403": {
24601
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead.",
24614
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden.",
24602
24615
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
24603
24616
  },
24604
24617
  "404": {
@@ -24845,7 +24858,7 @@ const openapiDocument = {
24845
24858
  "/email-rules/covering-me": { "get": {
24846
24859
  "operationId": "listEmailRulesCoveringMe",
24847
24860
  "summary": "List the rules over your mail",
24848
- "description": "Every email rule that covers one of your own mailboxes now, yours included, newest first, in any state. A rule covers you when the scope it runs with now reaches one of your own mailboxes (your member mailbox, or a personal agent's address): one whose author reads all your mail (author scope, for an author who reads the organization's mail or holds an access entry for your mailbox), or one that names your mailbox or its domain (selected). Another member's own_mailboxes rule never covers you. You see its name, wording, current tree, state, kind and author, and which of your own mailboxes it reaches. Its wording and tree are shown as written, so they can name other addresses (a condition on who an email is from or to). While it covers you, you also see who receives what it matches (receivers): how many API keys, webhooks and shares, and the member each belongs to, or the organization. You do not see which keys, webhooks or shares they are (no ids, names, prefixes or URLs), the other inboxes and domains its scope names, or its saved expectations. A page can hold fewer rules than limit, or none, and still carry a cursor (one request reads a bounded number of rules): keep reading until meta.cursor is null. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
24861
+ "description": "Every email rule that covers one of your own mailboxes now, yours included, newest first, in any state. A rule covers you when the scope it runs with now reaches one of your own mailboxes (your member mailbox, or a personal agent's address): one whose author reads all your mail (author scope, for an author who reads the organization's mail or holds an access entry for your mailbox), or one that names your mailbox or its domain (selected). Another member's own_mailboxes rule never covers you. You see its name, wording, current tree, state, kind and author, and which of your own mailboxes it reaches. Its wording and tree are shown as written, so they can name other addresses (a condition on who an email is from or to). While it covers you, you also see who receives what it matches (receivers): how many API keys, webhooks and shares, the member each belongs to (or the organization), and the sites the webhooks deliver to. You do not see which keys, webhooks or shares they are (no ids, names, prefixes, or URLs beyond the site), the other inboxes and domains its scope names, or its saved expectations. A page can hold fewer rules than limit, or none, and still carry a cursor (one request reads a bounded number of rules): keep reading until meta.cursor is null. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
24849
24862
  "tags": ["Email Rule Oversight"],
24850
24863
  "security": [{ "BearerAuth": [] }],
24851
24864
  "parameters": [{
@@ -24911,7 +24924,7 @@ const openapiDocument = {
24911
24924
  }
24912
24925
  },
24913
24926
  "403": {
24914
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
24927
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
24915
24928
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
24916
24929
  },
24917
24930
  "429": {
@@ -24943,7 +24956,7 @@ const openapiDocument = {
24943
24956
  "/email-rules/covering-me/{id}": { "get": {
24944
24957
  "operationId": "getEmailRuleCoveringMe",
24945
24958
  "summary": "Get a rule over your mail",
24946
- "description": "One rule that covers one of your own mailboxes, read-only, with its current version; or one that no longer does but decided on mail at your own mailbox (covers_now false), shown as the version that last decided on your mail, without its current name. You see its name, wording, current tree, state, kind and author, and which of your own mailboxes it reaches. Its wording and tree are shown as written, so they can name other addresses (a condition on who an email is from or to). While it covers you, you also see who receives what it matches (receivers): how many API keys, webhooks and shares, and the member each belongs to, or the organization. You do not see which keys, webhooks or shares they are (no ids, names, prefixes or URLs), the other inboxes and domains its scope names, or its saved expectations. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
24959
+ "description": "One rule that covers one of your own mailboxes, read-only, with its current version; or one that no longer does but decided on mail at your own mailbox (covers_now false), shown as the version that last decided on your mail, without its current name. You see its name, wording, current tree, state, kind and author, and which of your own mailboxes it reaches. Its wording and tree are shown as written, so they can name other addresses (a condition on who an email is from or to). While it covers you, you also see who receives what it matches (receivers): how many API keys, webhooks and shares, the member each belongs to (or the organization), and the sites the webhooks deliver to. You do not see which keys, webhooks or shares they are (no ids, names, prefixes, or URLs beyond the site), the other inboxes and domains its scope names, or its saved expectations. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
24947
24960
  "tags": ["Email Rule Oversight"],
24948
24961
  "security": [{ "BearerAuth": [] }],
24949
24962
  "parameters": [{
@@ -24991,7 +25004,7 @@ const openapiDocument = {
24991
25004
  }
24992
25005
  },
24993
25006
  "403": {
24994
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
25007
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
24995
25008
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
24996
25009
  },
24997
25010
  "404": {
@@ -25027,7 +25040,7 @@ const openapiDocument = {
25027
25040
  "/email-rules/covering-me/{id}/decisions": { "get": {
25028
25041
  "operationId": "listEmailRuleDecisionsOnMyMail",
25029
25042
  "summary": "List what a rule decided about your mail",
25030
- "description": "What a rule that covers your mailboxes, or decided on your mail, decided about mail at your own mailboxes, newest first: never about anyone else's mail. Each decision is in the words of the version that made it, which can be earlier than the current one: the questions it asked about that email (question_answers) and the columns its conditions read (parts). Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
25043
+ "description": "What a rule that covers your mailboxes, or decided on your mail, decided about mail at your own mailboxes, newest first: never about anyone else's mail. Each decision is in the words of the version that made it, which can be earlier than the current one: the questions it asked about that email (question_answers) and the columns its conditions read (parts). Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
25031
25044
  "tags": ["Email Rule Oversight"],
25032
25045
  "security": [{ "BearerAuth": [] }],
25033
25046
  "parameters": [
@@ -25117,7 +25130,135 @@ const openapiDocument = {
25117
25130
  }
25118
25131
  },
25119
25132
  "403": {
25120
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and what a rule decided is read from what emails say.",
25133
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. grant_forbidden with details.reason mail_depth: your access to mail shows headers only, and what a rule decided is read from what emails say.",
25134
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25135
+ },
25136
+ "404": {
25137
+ "description": "not_found: no rule with this id covers your mailboxes or decided on your mail.",
25138
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25139
+ },
25140
+ "429": {
25141
+ "description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
25142
+ "headers": {
25143
+ "Retry-After": {
25144
+ "schema": { "type": "integer" },
25145
+ "description": "Seconds to wait before retrying"
25146
+ },
25147
+ "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
25148
+ "ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
25149
+ "ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
25150
+ "ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
25151
+ },
25152
+ "content": { "application/json": {
25153
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
25154
+ "example": {
25155
+ "success": false,
25156
+ "error": {
25157
+ "code": "rate_limit_exceeded",
25158
+ "message": "Rate limit exceeded"
25159
+ }
25160
+ }
25161
+ } }
25162
+ }
25163
+ },
25164
+ "x-primitive-permission": ["mail:read"]
25165
+ } },
25166
+ "/email-rules/covering-me/{id}/versions": { "get": {
25167
+ "operationId": "listEmailRuleVersionsCoveringMe",
25168
+ "summary": "Read versions of a rule over your mail",
25169
+ "description": "The versions of a rule that covers one of your own mailboxes now that applied to your mail: its current version and each earlier one that decided on mail at your own mailboxes, so a decision can be read in the words of the version that made it. Each has what the covering view shows of a version: its wording, tree, explanation and assumptions. A version that did neither is not listed. Without versions, newest first, a page at a time (meta.cursor); with versions, only those named that are listed, oldest first. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
25170
+ "tags": ["Email Rule Oversight"],
25171
+ "security": [{ "BearerAuth": [] }],
25172
+ "parameters": [
25173
+ {
25174
+ "name": "id",
25175
+ "in": "path",
25176
+ "required": true,
25177
+ "description": "The rule id.",
25178
+ "schema": {
25179
+ "type": "string",
25180
+ "format": "uuid"
25181
+ }
25182
+ },
25183
+ {
25184
+ "name": "limit",
25185
+ "in": "query",
25186
+ "required": false,
25187
+ "description": "Versions per page when versions is not given: 1 to 100, default 50.",
25188
+ "schema": {
25189
+ "type": "integer",
25190
+ "minimum": 1,
25191
+ "maximum": 100
25192
+ }
25193
+ },
25194
+ {
25195
+ "name": "cursor",
25196
+ "in": "query",
25197
+ "required": false,
25198
+ "description": "meta.cursor from the preceding page. Omit for the first page. Not with versions.",
25199
+ "schema": {
25200
+ "type": "string",
25201
+ "maxLength": 200
25202
+ }
25203
+ },
25204
+ {
25205
+ "name": "versions",
25206
+ "in": "query",
25207
+ "required": false,
25208
+ "description": "Read only these version numbers, comma-separated, at most 100. Not with limit or cursor.",
25209
+ "schema": {
25210
+ "description": "Read only these version numbers, comma-separated. Without it, the versions listed, newest first, a page at a time.",
25211
+ "type": "string",
25212
+ "pattern": "^[1-9]\\d{0,8}(,[1-9]\\d{0,8}){0,99}$"
25213
+ }
25214
+ }
25215
+ ],
25216
+ "responses": {
25217
+ "200": {
25218
+ "description": "Read versions of a rule over your mail",
25219
+ "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
25220
+ "type": "object",
25221
+ "required": ["data"],
25222
+ "properties": {
25223
+ "data": { "$ref": "#/components/schemas/ListEmailRuleVersionsCoveringMeResult" },
25224
+ "meta": {
25225
+ "$ref": "#/components/schemas/CursorPageMeta",
25226
+ "description": "Absent when versions is given."
25227
+ }
25228
+ }
25229
+ }] } } },
25230
+ "headers": {
25231
+ "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
25232
+ "ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
25233
+ "ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
25234
+ "ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
25235
+ }
25236
+ },
25237
+ "400": {
25238
+ "description": "validation_error: a cursor not returned in meta.cursor, or versions with limit or cursor.",
25239
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25240
+ },
25241
+ "401": {
25242
+ "description": "Invalid or missing API key",
25243
+ "content": { "application/json": {
25244
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
25245
+ "example": {
25246
+ "success": false,
25247
+ "error": {
25248
+ "code": "unauthorized",
25249
+ "message": "Invalid or missing API key"
25250
+ }
25251
+ }
25252
+ } },
25253
+ "headers": {
25254
+ "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
25255
+ "ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
25256
+ "ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
25257
+ "ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
25258
+ }
25259
+ },
25260
+ "403": {
25261
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. forbidden with details.reason no_longer_covers: it decided on your mail before but covers none of your mailboxes now; details.covering_view (getEmailRuleCoveringMe) shows the version that last decided on your mail.",
25121
25262
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25122
25263
  },
25123
25264
  "404": {
@@ -25154,7 +25295,7 @@ const openapiDocument = {
25154
25295
  "post": {
25155
25296
  "operationId": "createEmailRuleObjection",
25156
25297
  "summary": "Object to a rule over your mail",
25157
- "description": "Asks the author of a rule that covers one of your own mailboxes now to turn it off. Only a rule that covers you now: one you see only because it once decided on your mail cannot be objected to. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. You may have one pending objection per rule and 50 in all, and object to the same rule again only 10 minutes after you last did, whatever became of it. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
25298
+ "description": "Asks the author of a rule that covers one of your own mailboxes now to turn it off. Only a rule that covers you now: one you see only because it once decided on your mail cannot be objected to. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. You may have one pending objection per rule and 50 in all, and object to the same rule again only 10 minutes after you last did, whatever became of it. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
25158
25299
  "tags": ["Email Rule Oversight"],
25159
25300
  "security": [{ "BearerAuth": [] }],
25160
25301
  "requestBody": {
@@ -25200,7 +25341,7 @@ const openapiDocument = {
25200
25341
  }
25201
25342
  },
25202
25343
  "403": {
25203
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
25344
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
25204
25345
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25205
25346
  },
25206
25347
  "404": {
@@ -25255,7 +25396,7 @@ const openapiDocument = {
25255
25396
  "get": {
25256
25397
  "operationId": "listEmailRuleObjections",
25257
25398
  "summary": "List objections",
25258
- "description": "Objections you made, objections to rules you wrote, or every objection you can see, newest first. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
25399
+ "description": "Objections you made and objections to rules you wrote (by default), only one of the two, or every objection you can see, newest first. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
25259
25400
  "tags": ["Email Rule Oversight"],
25260
25401
  "security": [{ "BearerAuth": [] }],
25261
25402
  "parameters": [
@@ -25263,12 +25404,13 @@ const openapiDocument = {
25263
25404
  "name": "box",
25264
25405
  "in": "query",
25265
25406
  "required": false,
25266
- "description": "outgoing (the default): objections you made. incoming: objections to rules you wrote. all: every objection you can see.",
25407
+ "description": "mine (the default): objections you made and objections to rules you wrote, each saying which (is_objector, is_author). outgoing: only those you made. incoming: only those to rules you wrote. all: every objection you can see.",
25267
25408
  "schema": {
25268
- "default": "outgoing",
25269
- "description": "outgoing: objections you made; incoming: objections to rules you wrote; all: every objection you can see (owners and admins see the organization's).",
25409
+ "default": "mine",
25410
+ "description": "mine: objections you made and objections to rules you wrote (is_objector or is_author says which); outgoing: only those you made; incoming: only those to rules you wrote; each in your current membership. all: every objection you can see (owners and admins see the organization's).",
25270
25411
  "type": "string",
25271
25412
  "enum": [
25413
+ "mine",
25272
25414
  "outgoing",
25273
25415
  "incoming",
25274
25416
  "all"
@@ -25355,7 +25497,7 @@ const openapiDocument = {
25355
25497
  }
25356
25498
  },
25357
25499
  "403": {
25358
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
25500
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
25359
25501
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25360
25502
  },
25361
25503
  "429": {
@@ -25388,7 +25530,7 @@ const openapiDocument = {
25388
25530
  "/email-rule-objections/{id}": { "get": {
25389
25531
  "operationId": "getEmailRuleObjection",
25390
25532
  "summary": "Get an objection",
25391
- "description": "One objection you can see. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
25533
+ "description": "One objection you can see. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
25392
25534
  "tags": ["Email Rule Oversight"],
25393
25535
  "security": [{ "BearerAuth": [] }],
25394
25536
  "parameters": [{
@@ -25436,7 +25578,7 @@ const openapiDocument = {
25436
25578
  }
25437
25579
  },
25438
25580
  "403": {
25439
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
25581
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI.",
25440
25582
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25441
25583
  },
25442
25584
  "404": {
@@ -25472,7 +25614,7 @@ const openapiDocument = {
25472
25614
  "/email-rule-objections/{id}/withdraw": { "post": {
25473
25615
  "operationId": "withdrawEmailRuleObjection",
25474
25616
  "summary": "Withdraw an objection",
25475
- "description": "Withdraws a pending objection you made, and clears its message. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
25617
+ "description": "Withdraws a pending objection you made, and clears its message. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
25476
25618
  "tags": ["Email Rule Oversight"],
25477
25619
  "security": [{ "BearerAuth": [] }],
25478
25620
  "parameters": [{
@@ -25530,7 +25672,7 @@ const openapiDocument = {
25530
25672
  }
25531
25673
  },
25532
25674
  "403": {
25533
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. forbidden with details.reason objector_only: only the member who made it can withdraw it.",
25675
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. forbidden with details.reason objector_only: only the member who made it can withdraw it.",
25534
25676
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25535
25677
  },
25536
25678
  "404": {
@@ -25570,7 +25712,7 @@ const openapiDocument = {
25570
25712
  "/email-rule-objections/{id}/decline": { "post": {
25571
25713
  "operationId": "declineEmailRuleObjection",
25572
25714
  "summary": "Decline an objection to your rule",
25573
- "description": "Declines a pending objection to a rule you wrote, optionally saying why; the rule stays as it is. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
25715
+ "description": "Declines a pending objection to a rule you wrote, optionally saying why; the rule stays as it is. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
25574
25716
  "tags": ["Email Rule Oversight"],
25575
25717
  "security": [{ "BearerAuth": [] }],
25576
25718
  "parameters": [{
@@ -25645,7 +25787,7 @@ const openapiDocument = {
25645
25787
  }
25646
25788
  },
25647
25789
  "403": {
25648
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. forbidden with details.reason author_only: only the rule's author can decline it.",
25790
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. forbidden with details.reason author_only: only the rule's author can decline it.",
25649
25791
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25650
25792
  },
25651
25793
  "404": {
@@ -25685,7 +25827,7 @@ const openapiDocument = {
25685
25827
  "/email-rule-objections/{id}/accept": { "post": {
25686
25828
  "operationId": "acceptEmailRuleObjection",
25687
25829
  "summary": "Accept an objection to your rule",
25688
- "description": "Accepts a pending objection to a rule you wrote: turns the rule off (state draft), as updating it would, and records the objection accepted, together. You can turn the rule on again later. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature. Only on your own dashboard session or the Primitive CLI.",
25830
+ "description": "Accepts a pending objection to a rule you wrote: turns the rule off (state draft), as updating it would, and records the objection accepted, together. You can turn the rule on again later. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
25689
25831
  "tags": ["Email Rule Oversight"],
25690
25832
  "security": [{ "BearerAuth": [] }],
25691
25833
  "parameters": [{
@@ -25743,7 +25885,7 @@ const openapiDocument = {
25743
25885
  }
25744
25886
  },
25745
25887
  "403": {
25746
- "description": "feature_not_enabled: the email rules feature is not enabled for this organization yet. Permission refusals answer forbidden or grant_forbidden instead; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. forbidden with details.reason author_only: only the rule's author can accept it.",
25888
+ "description": "feature_not_enabled: this organization lacks the email rules feature; permission refusals answer forbidden or grant_forbidden; details.reason first_party_required means this credential is not your dashboard session or the Primitive CLI. forbidden with details.reason author_only: only the rule's author can accept it.",
25747
25889
  "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
25748
25890
  },
25749
25891
  "404": {
@@ -26102,6 +26244,218 @@ const openapiDocument = {
26102
26244
  }
26103
26245
  }]
26104
26246
  } },
26247
+ "/mail-imports/google/messages": { "post": {
26248
+ "operationId": "importGoogleMailMessage",
26249
+ "summary": "Import one historical Google Workspace message",
26250
+ "description": "Requires the organization email_import entitlement, granted by staff. Only the configured current owner, using a full personal session or verified first-party CLI, may import into the configured primary mailbox. Primitive verifies the transient provider token and fetches RAW content itself. No raw uploads or client provenance are accepted. Sent, draft, spam and trash messages are skipped. Historical mail appears in normal history without live delivery effects. An indeterminate response requires retrying the same provider ID. User provider credentials are never stored or queued.",
26251
+ "tags": ["Emails"],
26252
+ "security": [{ "BearerAuth": [] }],
26253
+ "requestBody": {
26254
+ "required": true,
26255
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportGoogleMailMessageRequest" } } }
26256
+ },
26257
+ "responses": {
26258
+ "200": {
26259
+ "description": "Import one historical Google Workspace message",
26260
+ "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
26261
+ "type": "object",
26262
+ "required": ["data"],
26263
+ "properties": { "data": { "$ref": "#/components/schemas/ImportGoogleMailMessageResult" } }
26264
+ }] } } },
26265
+ "headers": {
26266
+ "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
26267
+ "ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
26268
+ "ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
26269
+ "ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
26270
+ }
26271
+ },
26272
+ "400": {
26273
+ "description": "Invalid request parameters",
26274
+ "content": { "application/json": {
26275
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
26276
+ "example": {
26277
+ "success": false,
26278
+ "error": {
26279
+ "code": "validation_error",
26280
+ "message": "Invalid domain format"
26281
+ }
26282
+ }
26283
+ } }
26284
+ },
26285
+ "401": {
26286
+ "description": "mail_import_provider_auth: reauthorize the provider.",
26287
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
26288
+ },
26289
+ "403": {
26290
+ "description": "feature_disabled: the organization lacks email_import; or mail_import_forbidden: identity or current access differs.",
26291
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
26292
+ },
26293
+ "409": {
26294
+ "description": "mail_import_cap_exceeded or mail_import_storage_limit: the fixed pilot budget is exhausted.",
26295
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
26296
+ },
26297
+ "422": {
26298
+ "description": "Mail import input, content, storage or publication fence could not be accepted.",
26299
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
26300
+ },
26301
+ "429": {
26302
+ "description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
26303
+ "headers": {
26304
+ "Retry-After": {
26305
+ "schema": { "type": "integer" },
26306
+ "description": "Seconds to wait before retrying"
26307
+ },
26308
+ "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
26309
+ "ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
26310
+ "ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
26311
+ "ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
26312
+ },
26313
+ "content": { "application/json": {
26314
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
26315
+ "example": {
26316
+ "success": false,
26317
+ "error": {
26318
+ "code": "rate_limit_exceeded",
26319
+ "message": "Rate limit exceeded"
26320
+ }
26321
+ }
26322
+ } }
26323
+ },
26324
+ "502": {
26325
+ "description": "Provider acquisition failed or exceeded its deadline.",
26326
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
26327
+ },
26328
+ "503": {
26329
+ "description": "mail_import_disabled or mail_import_unavailable; retry the same ID after resolving the cause.",
26330
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
26331
+ }
26332
+ },
26333
+ "parameters": [{
26334
+ "in": "header",
26335
+ "name": "X-Primitive-Google-Access-Token",
26336
+ "required": true,
26337
+ "description": "Transient provider user access token. Send only in this redacted header, never in a URL or JSON body. Uses the dedicated configured desktop client and readonly/identity scopes.",
26338
+ "schema": {
26339
+ "type": "string",
26340
+ "minLength": 1,
26341
+ "maxLength": 8192,
26342
+ "writeOnly": true
26343
+ }
26344
+ }, {
26345
+ "name": "Idempotency-Key",
26346
+ "in": "header",
26347
+ "required": false,
26348
+ "description": "Optional, and not used to deduplicate: this operation stores no result by the key, so a retry with the same key runs the request again. Operations that need a credential refuse a key that is empty, longer than 255 characters or not printable ASCII with 400 `validation_error` and echo a valid one in the Idempotency-Key response header; operations that need none ignore it.",
26349
+ "schema": {
26350
+ "type": "string",
26351
+ "minLength": 1,
26352
+ "maxLength": 255
26353
+ }
26354
+ }],
26355
+ "x-primitive-permission": ["mail:read"]
26356
+ } },
26357
+ "/mail-imports/google/reconcile": { "post": {
26358
+ "operationId": "reconcileGoogleMailImport",
26359
+ "summary": "Reconcile scoped historical import attempts",
26360
+ "description": "The same configured owner may recover expired attempts after the email_import entitlement is revoked. Recovery does not require that entitlement. Selects only server-owned manifests for the configured mailbox/account/policy. No provider token, object keys, prefixes or email IDs are accepted. Unsettled uploads remain unresolved and charged; this operation does not promise automatic cleanup after a process dies.",
26361
+ "tags": ["Emails"],
26362
+ "security": [{ "BearerAuth": [] }],
26363
+ "requestBody": {
26364
+ "required": true,
26365
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReconcileGoogleMailImportRequest" } } }
26366
+ },
26367
+ "responses": {
26368
+ "200": {
26369
+ "description": "Reconcile scoped historical import attempts",
26370
+ "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
26371
+ "type": "object",
26372
+ "required": ["data"],
26373
+ "properties": { "data": { "$ref": "#/components/schemas/ReconcileGoogleMailImportResult" } }
26374
+ }] } } },
26375
+ "headers": {
26376
+ "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit" },
26377
+ "ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining" },
26378
+ "ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
26379
+ "ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy" }
26380
+ }
26381
+ },
26382
+ "400": {
26383
+ "description": "Invalid request parameters",
26384
+ "content": { "application/json": {
26385
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
26386
+ "example": {
26387
+ "success": false,
26388
+ "error": {
26389
+ "code": "validation_error",
26390
+ "message": "Invalid domain format"
26391
+ }
26392
+ }
26393
+ } }
26394
+ },
26395
+ "401": {
26396
+ "description": "Invalid or missing API key",
26397
+ "content": { "application/json": {
26398
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
26399
+ "example": {
26400
+ "success": false,
26401
+ "error": {
26402
+ "code": "unauthorized",
26403
+ "message": "Invalid or missing API key"
26404
+ }
26405
+ }
26406
+ } },
26407
+ "headers": {
26408
+ "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-unauthenticated" },
26409
+ "ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-unauthenticated" },
26410
+ "ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset-unauthenticated" },
26411
+ "ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-unauthenticated" }
26412
+ }
26413
+ },
26414
+ "403": {
26415
+ "description": "mail_import_forbidden: current owner access is required.",
26416
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
26417
+ },
26418
+ "429": {
26419
+ "description": "Rate limit exceeded. The `ratelimit-*` headers describe the limiter that rejected this request, with its own limit and window, not the API budget that successful responses report. A limiter that reports only a wait sends `Retry-After` alone.",
26420
+ "headers": {
26421
+ "Retry-After": {
26422
+ "schema": { "type": "integer" },
26423
+ "description": "Seconds to wait before retrying"
26424
+ },
26425
+ "ratelimit-limit": { "$ref": "#/components/headers/ratelimit-limit-429" },
26426
+ "ratelimit-remaining": { "$ref": "#/components/headers/ratelimit-remaining-429" },
26427
+ "ratelimit-reset": { "$ref": "#/components/headers/ratelimit-reset" },
26428
+ "ratelimit-policy": { "$ref": "#/components/headers/ratelimit-policy-429" }
26429
+ },
26430
+ "content": { "application/json": {
26431
+ "schema": { "$ref": "#/components/schemas/ErrorResponse" },
26432
+ "example": {
26433
+ "success": false,
26434
+ "error": {
26435
+ "code": "rate_limit_exceeded",
26436
+ "message": "Rate limit exceeded"
26437
+ }
26438
+ }
26439
+ } }
26440
+ },
26441
+ "503": {
26442
+ "description": "mail_import_disabled: no valid policy is configured; or mail_import_unavailable.",
26443
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
26444
+ }
26445
+ },
26446
+ "x-primitive-permission": ["mail:read"],
26447
+ "parameters": [{
26448
+ "name": "Idempotency-Key",
26449
+ "in": "header",
26450
+ "required": false,
26451
+ "description": "Optional, and not used to deduplicate: this operation stores no result by the key, so a retry with the same key runs the request again. Operations that need a credential refuse a key that is empty, longer than 255 characters or not printable ASCII with 400 `validation_error` and echo a valid one in the Idempotency-Key response header; operations that need none ignore it.",
26452
+ "schema": {
26453
+ "type": "string",
26454
+ "minLength": 1,
26455
+ "maxLength": 255
26456
+ }
26457
+ }]
26458
+ } },
26105
26459
  "/emails/{id}/signal": { "post": {
26106
26460
  "operationId": "signalEmail",
26107
26461
  "summary": "Send a read, working or typing signal about an email",
@@ -34312,7 +34666,7 @@ const openapiDocument = {
34312
34666
  "summary": "List API keys",
34313
34667
  "tags": ["Credentials"],
34314
34668
  "security": [{ "BearerAuth": [] }],
34315
- "description": "Key metadata, never a secret: name, prefix, grant, creator, last use, expiry, suspension and whether each key works now (status). Revoked keys are left out unless include_revoked is true, which lists them too with status revoked, revoked_at and revoked_by, under the same access rules as keys that work. A grant's mail_scope.depth is raw only when the key holds mail.raw:read and its scope lists an address or a domain, otherwise content. A rule the key lists that reads nothing any more (a deleted rule, or a share that ended) is listed under grant.mail_scope.ended_rules, each with its id and reason (rule_deleted or share_ended), and not under rules. The person needs access to the whole organization's mail. Needs the organization's credential features to be enabled; while they are not, this answers 404 feature_disabled, with details.feature naming the feature. Contact Primitive to turn them on.\n\nPerson only: a dashboard session or the person's own Primitive CLI token; no other OAuth app. The person's role must hold keys:read. API keys, agent connection keys and agents are refused.",
34669
+ "description": "Key metadata, never a secret: name, prefix, grant, creator, last use, expiry, suspension and whether each key works now (status). Revoked keys are left out unless include_revoked is true, which lists them too with status revoked, revoked_at and revoked_by, under the same access rules as keys that work. A grant's mail_scope.depth is raw only when the key holds mail.raw:read and its scope lists an address or a domain, otherwise content. A rule the key lists that reads nothing any more (a deleted rule, or a share that ended) is listed under grant.mail_scope.ended_rules, each with its id and reason (rule_deleted or share_ended), and not under rules. grant.mail_scope.rule_versions gives each rule it lists directly its current_version, state and version_saved_at. The person needs access to the whole organization's mail. Needs the organization's credential features to be enabled; while they are not, this answers 404 feature_disabled, with details.feature naming the feature. Contact Primitive to turn them on.\n\nPerson only: a dashboard session or the person's own Primitive CLI token; no other OAuth app. The person's role must hold keys:read. API keys, agent connection keys and agents are refused.",
34316
34670
  "x-primitive-person-only": { "access": "session_or_cli" },
34317
34671
  "parameters": [{
34318
34672
  "name": "cursor",
@@ -34763,7 +35117,7 @@ const openapiDocument = {
34763
35117
  "summary": "Get an API key",
34764
35118
  "tags": ["Credentials"],
34765
35119
  "security": [{ "BearerAuth": [] }],
34766
- "description": "One key's metadata, never its secret, with whether it works now (status). A revoked key whose record is kept is answered too, with status revoked, revoked_at and revoked_by, under the same access rules as a key that works: one the caller may not read is 404, revoked or not. Its grant's mail_scope.depth is raw only when the key holds mail.raw:read and its scope lists an address or a domain, otherwise content. A rule the key lists that reads nothing any more (a deleted rule, or a share that ended) is listed under grant.mail_scope.ended_rules, each with its id and reason (rule_deleted or share_ended), and not under rules. The person needs access to the whole organization's mail. Needs the organization's credential features to be enabled; while they are not, this answers 404 feature_disabled, with details.feature naming the feature. Contact Primitive to turn them on.\n\nPerson only: a dashboard session or the person's own Primitive CLI token; no other OAuth app. The person's role must hold keys:read. API keys, agent connection keys and agents are refused.",
35120
+ "description": "One key's metadata, never its secret, with whether it works now (status). A revoked key whose record is kept is answered too, with status revoked, revoked_at and revoked_by, under the same access rules as a key that works: one the caller may not read is 404, revoked or not. Its grant's mail_scope.depth is raw only when the key holds mail.raw:read and its scope lists an address or a domain, otherwise content. A rule the key lists that reads nothing any more (a deleted rule, or a share that ended) is listed under grant.mail_scope.ended_rules, each with its id and reason (rule_deleted or share_ended), and not under rules. grant.mail_scope.rule_versions gives each rule it lists directly its current_version, state and version_saved_at. The person needs access to the whole organization's mail. Needs the organization's credential features to be enabled; while they are not, this answers 404 feature_disabled, with details.feature naming the feature. Contact Primitive to turn them on.\n\nPerson only: a dashboard session or the person's own Primitive CLI token; no other OAuth app. The person's role must hold keys:read. API keys, agent connection keys and agents are refused.",
34767
35121
  "x-primitive-person-only": { "access": "session_or_cli" },
34768
35122
  "parameters": [{
34769
35123
  "name": "id",
@@ -37500,7 +37854,7 @@ const openapiDocument = {
37500
37854
  "get": {
37501
37855
  "operationId": "getRelayAutoJoin",
37502
37856
  "summary": "Get the auto-join setting",
37503
- "description": "Whether someone who proves an address on the organization's mail relay domain with Google Workspace joins automatically, and with what access. `option` is null when auto-join is off and an invitation is required; `options` lists the choices. Answers 404 `feature_disabled` unless the organization has mail relay.",
37857
+ "description": "Whether someone who proves an address on the organization's mail relay domain with Google Workspace joins automatically. Auto-joined people have no mail access until an owner or admin assigns a role. `option` is null when auto-join is off and an invitation is required; legacy mail-grant settings read as `no_access`. `options` lists the choices. Answers 404 `feature_disabled` unless the organization has mail relay.",
37504
37858
  "tags": ["Roles and members"],
37505
37859
  "security": [{ "BearerAuth": [] }],
37506
37860
  "responses": {
@@ -37587,7 +37941,7 @@ const openapiDocument = {
37587
37941
  "put": {
37588
37942
  "operationId": "setRelayAutoJoin",
37589
37943
  "summary": "Change the auto-join setting",
37590
- "description": "`option` is a choice's id from the GET, or null to require an invitation (turning it off needs only members:manage). Choosing an option answers 404 `feature_disabled` unless the organization has mail relay, 400 `validation_error` for an id that is not one of the choices, 403 `grant_forbidden` unless the caller is an owner or admin who can assign roles and send invitations and covers the role the option gives, 409 `role_key_taken` when a custom role already holds that role's key, and 409 `role_assignment_unavailable` when the organization cannot be given that role yet. Person only: a dashboard session, or the person's own OAuth or CLI token with a session step-up of purpose `relay_auto_join.write` (the person confirms at the step_up_url a 403 step_up_required answer carries, which names this client, then send the token in the Primitive-Step-Up header; the token works only with the client it was confirmed for). Needs members:manage, and roles:assign to turn it on. API keys are refused.",
37944
+ "description": "`option` is a choice's id from the GET, or null to require an invitation (turning it off needs only members:manage). Auto-joined people have no mail access until an owner or admin assigns a role; legacy mail-grant choices cannot be selected. Choosing an option answers 404 `feature_disabled` unless the organization has mail relay, 400 `validation_error` for an id outside the current catalog, 403 `grant_forbidden` without owner or admin assignment authority, and 409 `role_assignment_unavailable` when the restricted role cannot be assigned yet. Person only: a dashboard session, or the person's own OAuth or CLI token with a session step-up of purpose `relay_auto_join.write` (the person confirms at the step_up_url a 403 step_up_required answer carries, which names this client, then send the token in the Primitive-Step-Up header; the token works only with the client it was confirmed for). Needs members:manage, and roles:assign to turn it on. API keys are refused.",
37591
37945
  "tags": ["Roles and members"],
37592
37946
  "security": [{ "BearerAuth": [] }],
37593
37947
  "requestBody": {
@@ -40313,6 +40667,7 @@ const openapiDocument = {
40313
40667
  "build_failed",
40314
40668
  "builder_unavailable",
40315
40669
  "builtin_role",
40670
+ "chunk_size_mismatch",
40316
40671
  "chunk_too_large",
40317
40672
  "chunks_missing",
40318
40673
  "claim_failed",
@@ -40397,6 +40752,7 @@ const openapiDocument = {
40397
40752
  "invalid_code",
40398
40753
  "invalid_cursor",
40399
40754
  "invalid_email",
40755
+ "invalid_manifest",
40400
40756
  "invalid_payload_reference",
40401
40757
  "invalid_request",
40402
40758
  "invalid_signature",
@@ -40419,6 +40775,22 @@ const openapiDocument = {
40419
40775
  "latest_message_mismatch",
40420
40776
  "latest_message_thread_unresolved",
40421
40777
  "load_test_recipient_not_allowed",
40778
+ "mail_import_cap_exceeded",
40779
+ "mail_import_content_mismatch",
40780
+ "mail_import_deadline",
40781
+ "mail_import_disabled",
40782
+ "mail_import_fenced",
40783
+ "mail_import_forbidden",
40784
+ "mail_import_invalid_input",
40785
+ "mail_import_invalid_provider_response",
40786
+ "mail_import_out_of_window",
40787
+ "mail_import_policy_mismatch",
40788
+ "mail_import_provider_auth",
40789
+ "mail_import_provider_unavailable",
40790
+ "mail_import_storage_failed",
40791
+ "mail_import_storage_limit",
40792
+ "mail_import_too_large",
40793
+ "mail_import_unavailable",
40422
40794
  "mail_relay_unavailable",
40423
40795
  "mail_routing_conflict",
40424
40796
  "mail_topic_edit_conflict",
@@ -42607,6 +42979,35 @@ const openapiDocument = {
42607
42979
  "format": "uuid",
42608
42980
  "description": "With `group=message`: the send of this message, when you can read it (under a connected agent key, only its own sends)."
42609
42981
  },
42982
+ "imported": {
42983
+ "type": ["object", "null"],
42984
+ "description": "Historical acquisition; never sender or SMTP delivery proof. Null for live mail. Excluded from live tails and deltas.",
42985
+ "required": [
42986
+ "from",
42987
+ "method",
42988
+ "verification",
42989
+ "at"
42990
+ ],
42991
+ "properties": {
42992
+ "from": {
42993
+ "type": "string",
42994
+ "enum": ["google_workspace"]
42995
+ },
42996
+ "method": {
42997
+ "type": "string",
42998
+ "enum": ["primitive_direct"]
42999
+ },
43000
+ "verification": {
43001
+ "type": "string",
43002
+ "enum": ["provider_fetched"]
43003
+ },
43004
+ "at": {
43005
+ "type": "string",
43006
+ "format": "date-time",
43007
+ "description": "When Primitive acquired this historical message."
43008
+ }
43009
+ }
43010
+ },
42610
43011
  "snippet": {
42611
43012
  "type": ["string", "null"],
42612
43013
  "maxLength": 200,
@@ -42629,6 +43030,7 @@ const openapiDocument = {
42629
43030
  "automated_reasons",
42630
43031
  "from_verified",
42631
43032
  "from_header_email",
43033
+ "imported",
42632
43034
  "snippet"
42633
43035
  ]
42634
43036
  },
@@ -43252,6 +43654,40 @@ const openapiDocument = {
43252
43654
  }
43253
43655
  }
43254
43656
  },
43657
+ "attachments": {
43658
+ "type": "array",
43659
+ "items": { "$ref": "#/components/schemas/EmailAttachment" },
43660
+ "description": "Attachment metadata (file name, type, size), the same items in the same order as `parsed.attachments`. Kept for a credential that reads headers only, where `parsed` is null. Empty when the email has none, its content is withheld (read `status` and `rejection_reason`), or it is not parsed yet or its parse failed (read `parse_status`). No bytes; download a part with `/emails/{id}/attachments/{part_index}`."
43661
+ },
43662
+ "imported": {
43663
+ "type": ["object", "null"],
43664
+ "description": "Historical acquisition; never sender or SMTP delivery proof. Null for live mail. Excluded from live tails and deltas.",
43665
+ "required": [
43666
+ "from",
43667
+ "method",
43668
+ "verification",
43669
+ "at"
43670
+ ],
43671
+ "properties": {
43672
+ "from": {
43673
+ "type": "string",
43674
+ "enum": ["google_workspace"]
43675
+ },
43676
+ "method": {
43677
+ "type": "string",
43678
+ "enum": ["primitive_direct"]
43679
+ },
43680
+ "verification": {
43681
+ "type": "string",
43682
+ "enum": ["provider_fetched"]
43683
+ },
43684
+ "at": {
43685
+ "type": "string",
43686
+ "format": "date-time",
43687
+ "description": "When Primitive acquired this historical message."
43688
+ }
43689
+ }
43690
+ },
43255
43691
  "snippet": {
43256
43692
  "type": ["string", "null"],
43257
43693
  "maxLength": 200,
@@ -43281,6 +43717,8 @@ const openapiDocument = {
43281
43717
  "from_verified",
43282
43718
  "from_header_email",
43283
43719
  "from_name",
43720
+ "attachments",
43721
+ "imported",
43284
43722
  "snippet"
43285
43723
  ]
43286
43724
  },
@@ -43802,6 +44240,35 @@ const openapiDocument = {
43802
44240
  "items": { "$ref": "#/components/schemas/EmailAttachment" },
43803
44241
  "description": "Attachment metadata for this message, each item as its own detail read reports it: `parsed.attachments` on `/emails/{id}` for inbound, `attachments` on `/sent-emails/{id}` for outbound, in the same order. Empty when there are none or an inbound message has no parsed content. No bytes or download URLs; download a part with `/emails/{id}/attachments/{part_index}` or `/sent-emails/{id}/attachments/{part_index}`."
43804
44242
  },
44243
+ "imported": {
44244
+ "type": ["object", "null"],
44245
+ "description": "Historical acquisition; never sender or SMTP delivery proof. Null for live mail. Excluded from live tails and deltas.",
44246
+ "required": [
44247
+ "from",
44248
+ "method",
44249
+ "verification",
44250
+ "at"
44251
+ ],
44252
+ "properties": {
44253
+ "from": {
44254
+ "type": "string",
44255
+ "enum": ["google_workspace"]
44256
+ },
44257
+ "method": {
44258
+ "type": "string",
44259
+ "enum": ["primitive_direct"]
44260
+ },
44261
+ "verification": {
44262
+ "type": "string",
44263
+ "enum": ["provider_fetched"]
44264
+ },
44265
+ "at": {
44266
+ "type": "string",
44267
+ "format": "date-time",
44268
+ "description": "When Primitive acquired this historical message."
44269
+ }
44270
+ }
44271
+ },
43805
44272
  "copies": {
43806
44273
  "type": "array",
43807
44274
  "items": {
@@ -44072,6 +44539,33 @@ const openapiDocument = {
44072
44539
  }
44073
44540
  }
44074
44541
  },
44542
+ "unsubscribe": {
44543
+ "type": "object",
44544
+ "additionalProperties": false,
44545
+ "required": ["url"],
44546
+ "description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of the message gets the same `url`, so send one message per recipient when the URL identifies who to unsubscribe. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
44547
+ "properties": {
44548
+ "url": {
44549
+ "type": "string",
44550
+ "minLength": 1,
44551
+ "maxLength": 700,
44552
+ "pattern": "^[Hh][Tt][Tt][Pp][Ss]://(?:[A-Za-z0-9\\-._~:/?#\\[\\]@!$&'()*+;=]|%[0-9A-Fa-f]{2})+$",
44553
+ "description": "Absolute https URL that unsubscribes the recipient, at most 700 characters of URL characters only: printable ASCII with no spaces, double quotes, angle brackets or commas (percent-encode a comma as %2C), `%` only as a percent-encoded byte, `[` and `]` only around an IPv6 address host, a port from 1 to 65535, no `=?` (which mail readers can decode as an encoded word), and no user name or password. The host must be public: not a single-label name, `localhost` or a name under `.localhost`, `.internal`, `.local`, `.home.arpa` or `.localdomain`, or a loopback, link-local, private, shared, reserved, multicast or unspecified IP address, including an IPv6 address that embeds one. The scheme is written in lower case."
44554
+ },
44555
+ "one_click": {
44556
+ "type": "boolean",
44557
+ "default": true,
44558
+ "description": "Whether to also write `List-Unsubscribe-Post: List-Unsubscribe=One-Click`, which tells the receiver it may unsubscribe with a single POST to `url`. Set false when `url` needs the recipient to open a page and confirm."
44559
+ },
44560
+ "mailto": {
44561
+ "type": "string",
44562
+ "minLength": 3,
44563
+ "maxLength": 254,
44564
+ "pattern": "^[A-Za-z0-9!$'*+=_~-]+(?:\\.[A-Za-z0-9!$'*+=_~-]+)*@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$",
44565
+ "description": "An address to also offer as a `mailto:` unsubscribe, written after `url`. The address alone, without the `mailto:` prefix: at most 64 letters, digits and `.!$'*+=_~-` before the `@`, and a dotted domain after it. An `=` is written as `%3D` in the header."
44566
+ }
44567
+ }
44568
+ },
44075
44569
  "headers": {
44076
44570
  "type": "object",
44077
44571
  "maxProperties": 25,
@@ -45117,6 +45611,33 @@ const openapiDocument = {
45117
45611
  "type": "boolean",
45118
45612
  "description": "Send an informational message the receiver need not answer or wake for. It is sent as an `ack` signal (status `received`) about the message in `in_reply_to`, whose note is `body_text` (at most 2000 characters, which may be omitted). Receivers see it as `fyi: true`. Plain text only: it cannot be combined with `body_html`, `attachments`, `payload_attachments` or `scheduled_at`, and on `/send-mail` it requires `in_reply_to`."
45119
45613
  },
45614
+ "unsubscribe": {
45615
+ "type": "object",
45616
+ "additionalProperties": false,
45617
+ "required": ["url"],
45618
+ "description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of the message gets the same `url`, so send one message per recipient when the URL identifies who to unsubscribe. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
45619
+ "properties": {
45620
+ "url": {
45621
+ "type": "string",
45622
+ "minLength": 1,
45623
+ "maxLength": 700,
45624
+ "pattern": "^[Hh][Tt][Tt][Pp][Ss]://(?:[A-Za-z0-9\\-._~:/?#\\[\\]@!$&'()*+;=]|%[0-9A-Fa-f]{2})+$",
45625
+ "description": "Absolute https URL that unsubscribes the recipient, at most 700 characters of URL characters only: printable ASCII with no spaces, double quotes, angle brackets or commas (percent-encode a comma as %2C), `%` only as a percent-encoded byte, `[` and `]` only around an IPv6 address host, a port from 1 to 65535, no `=?` (which mail readers can decode as an encoded word), and no user name or password. The host must be public: not a single-label name, `localhost` or a name under `.localhost`, `.internal`, `.local`, `.home.arpa` or `.localdomain`, or a loopback, link-local, private, shared, reserved, multicast or unspecified IP address, including an IPv6 address that embeds one. The scheme is written in lower case."
45626
+ },
45627
+ "one_click": {
45628
+ "type": "boolean",
45629
+ "default": true,
45630
+ "description": "Whether to also write `List-Unsubscribe-Post: List-Unsubscribe=One-Click`, which tells the receiver it may unsubscribe with a single POST to `url`. Set false when `url` needs the recipient to open a page and confirm."
45631
+ },
45632
+ "mailto": {
45633
+ "type": "string",
45634
+ "minLength": 3,
45635
+ "maxLength": 254,
45636
+ "pattern": "^[A-Za-z0-9!$'*+=_~-]+(?:\\.[A-Za-z0-9!$'*+=_~-]+)*@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$",
45637
+ "description": "An address to also offer as a `mailto:` unsubscribe, written after `url`. The address alone, without the `mailto:` prefix: at most 64 letters, digits and `.!$'*+=_~-` before the `@`, and a dotted domain after it. An `=` is written as `%3D` in the header."
45638
+ }
45639
+ }
45640
+ },
45120
45641
  "headers": {
45121
45642
  "type": "object",
45122
45643
  "maxProperties": 25,
@@ -50236,7 +50757,7 @@ const openapiDocument = {
50236
50757
  },
50237
50758
  "state": {
50238
50759
  "type": "string",
50239
- "description": "Where it stands now: enabled, disabled, deleted or replaced (endpoints and routes), enabled or disabled (filters, wake schedules), live, suspended or deleted (Functions), active, paused or ended (repeating sends), scheduled (scheduled sends), active, suspended, expired or revoked (API keys), pending, claimed, connected or revoked (agents), draft, observe or active (email rules), pending, accepted, declined or withdrawn (rule requests and objections), and for a coverage notice what it told: started (a rule began covering your mailbox), new_version, or baseline (it already covered you when notices began)."
50760
+ "description": "Where it stands now: enabled, disabled, deleted or replaced (endpoints and routes), enabled or disabled (filters, wake schedules), live, suspended or deleted (Functions), active, paused or ended (repeating sends), scheduled (scheduled sends), active, suspended, expired or revoked (API keys), pending, claimed, connected or revoked (agents), draft, observe or active (email rules), pending, accepted, declined or withdrawn (rule requests and objections), and for a coverage notice what it told: started (a rule was made or turned active over your mailboxes, or an active rule came to reach you; once per rule, not per mailbox), new_version, or baseline (it already covered you when notices began). pending: a rule started covering your mailbox and the notices have not recorded that start yet (a restart within an hour of the last start notice is recorded without a new one); its id is then the rule's, and created_at when the rule last changed."
50240
50761
  },
50241
50762
  "created_at": {
50242
50763
  "type": "string",
@@ -50245,7 +50766,7 @@ const openapiDocument = {
50245
50766
  "ended_at": {
50246
50767
  "type": ["string", "null"],
50247
50768
  "format": "date-time",
50248
- "description": "When it was deleted, replaced or revoked, when an API key expired, when a repeating send stopped, or when a rule request or objection was decided (accepted, declined or withdrawn); null otherwise."
50769
+ "description": "When it was deleted, replaced or revoked, when an API key expired, when a repeating send stopped, or when a rule request or objection was decided (accepted, declined or withdrawn); null otherwise, as on every coverage notice."
50249
50770
  },
50250
50771
  "owned_now": {
50251
50772
  "type": "string",
@@ -50273,6 +50794,11 @@ const openapiDocument = {
50273
50794
  "id",
50274
50795
  "name"
50275
50796
  ]
50797
+ },
50798
+ "rule_id": {
50799
+ "description": "For email_rule_coverage, always: the rule it is about, since names need not be unique (for pending, the same as id). GET /v1/email-rules/covering-me/{rule_id} shows the rule while it covers you, or as it last decided on your mail; otherwise it answers not_found.",
50800
+ "type": "string",
50801
+ "format": "uuid"
50276
50802
  }
50277
50803
  },
50278
50804
  "required": [
@@ -50315,6 +50841,15 @@ const openapiDocument = {
50315
50841
  "next_cursor": {
50316
50842
  "description": "Pass as cursor to read the next page; null on the last page. A page can hold fewer items than limit, or none, while next_cursor is set.",
50317
50843
  "type": ["string", "null"]
50844
+ },
50845
+ "as_of": {
50846
+ "type": ["string", "null"],
50847
+ "format": "date-time",
50848
+ "description": "With relation=concerning only: about when the notices last looked at your organization; null if never or unread. Coverage since may have no notice yet."
50849
+ },
50850
+ "pending_complete": {
50851
+ "description": "With relation=concerning, on the first page only: whether the check for rules that started covering you without their start notice (state pending, listed ahead of the notices and beyond limit) was finished. false: some may be missing. Absent where there is nothing to check.",
50852
+ "type": "boolean"
50318
50853
  }
50319
50854
  },
50320
50855
  "required": [
@@ -50514,7 +51049,7 @@ const openapiDocument = {
50514
51049
  "checked": {
50515
51050
  "type": "integer",
50516
51051
  "minimum": 0,
50517
- "description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in your own mailboxes (for a grant rule, those you may share), the ones you can still read."
51052
+ "description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in the scope counted (evaluation_scope as sent; by default your own mailboxes, for a grant rule those you may share), the ones you can still read."
50518
51053
  },
50519
51054
  "matched": {
50520
51055
  "type": "integer",
@@ -50524,6 +51059,33 @@ const openapiDocument = {
50524
51059
  },
50525
51060
  "required": ["checked", "matched"]
50526
51061
  },
51062
+ "EmailRuleScopeSelection": {
51063
+ "type": "object",
51064
+ "properties": {
51065
+ "addresses": {
51066
+ "maxItems": 100,
51067
+ "type": "array",
51068
+ "items": {
51069
+ "type": "string",
51070
+ "minLength": 3,
51071
+ "maxLength": 320,
51072
+ "pattern": "^[^\\s@]+@[^\\s@]+$"
51073
+ }
51074
+ },
51075
+ "domains": {
51076
+ "maxItems": 50,
51077
+ "type": "array",
51078
+ "items": {
51079
+ "type": "string",
51080
+ "minLength": 1,
51081
+ "maxLength": 253,
51082
+ "pattern": "^[^\\s@]+\\.[^\\s@]+$"
51083
+ }
51084
+ }
51085
+ },
51086
+ "additionalProperties": false,
51087
+ "description": "For a selected rule: inboxes, and domains (each address at them the author reads); each one you can read."
51088
+ },
50527
51089
  "CompileEmailRuleRequest": {
50528
51090
  "type": "object",
50529
51091
  "properties": {
@@ -50541,7 +51103,17 @@ const openapiDocument = {
50541
51103
  "include_sql": {
50542
51104
  "description": "Also return the SQL the rule renders to. Off by default.",
50543
51105
  "type": "boolean"
50544
- }
51106
+ },
51107
+ "evaluation_scope": {
51108
+ "description": "The scope breadth is counted over, as a test of the tree runs it: send the one you will save the rule with. Nothing is saved. own_mailboxes (the default): mail to your own addresses and agents (for a rule that shares, those mail:share_own lets you share). author: all you can read, inboxes added later included. selected: what scope_selection names.",
51109
+ "type": "string",
51110
+ "enum": [
51111
+ "author",
51112
+ "own_mailboxes",
51113
+ "selected"
51114
+ ]
51115
+ },
51116
+ "scope_selection": { "$ref": "#/components/schemas/EmailRuleScopeSelection" }
50545
51117
  },
50546
51118
  "required": ["english"],
50547
51119
  "additionalProperties": false
@@ -50562,14 +51134,23 @@ const openapiDocument = {
50562
51134
  "warnings": {
50563
51135
  "type": "array",
50564
51136
  "items": { "type": "string" },
50565
- "description": "Parts of the description the column contract cannot express, and how the rule handles them. It also has notes on reading automated, from_mailing_list or sender_relationship, a sender address the description does not give, and a grant worded as a prohibition. When the rule has no questions and its conditions hold for all of the caller's recent classified mail (what a test of it covers by default), or for at least 90% of at least 20 emails, the first warning says so. That count is not made for a caller whose access shows headers only, or who could not run a test of the rule, and may also be skipped when the compile ran long or the count fails."
51137
+ "description": "Parts of the description the column contract cannot express, how the rule handles them, and the compiler's own notes on the rule (the Email Rules docs list them). When the rule has no questions and its conditions hold for nearly all of your recent mail (see breadth), or it is a grant rule whose conditions alone hold for over 20% of at least 20 emails (see conditions_breadth), the first warning says so."
50566
51138
  },
50567
51139
  "compiled_by": {
50568
51140
  "type": "string",
50569
51141
  "description": "An opaque name for the version of the rule compiler that produced the rule, such as rule-compiler-v1. Send it back as compiled_by when you save the rule."
50570
51142
  },
51143
+ "kind": {
51144
+ "description": "The kind it was compiled for. Send it with the tree to a test.",
51145
+ "type": "string",
51146
+ "enum": ["grant", "do_not_share"]
51147
+ },
50571
51148
  "breadth": {
50572
- "description": "How many of your recent emails its conditions hold for, counted over your own mailboxes whatever scope you save it with, warning or not. Null when not counted in full: a rule with questions, headers-only access, no test of it possible as you, or a count that failed or ran out of time. Writes ignore it, so a compile answer (without include_sql) can be sent back unchanged.",
51149
+ "description": "How many recent emails its conditions hold for, warning or not, counted over evaluation_scope as sent (by default your own mailboxes, whatever scope you save it with). Null when not counted in full: a rule with questions, headers-only access, no test of it possible as you, a count that failed or ran out of time, or, with no scope sent, a rule that holds only for mail to To or Cc addresses outside your own mailboxes (a warning says so). Writes ignore it, so a compile answer (without include_sql) can be sent back unchanged.",
51150
+ "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
51151
+ },
51152
+ "conditions_breadth": {
51153
+ "description": "For a grant rule with questions, breadth counted on its conditions alone: the emails its questions are asked about. Null for any other rule, or when not counted in full (as breadth).",
50573
51154
  "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
50574
51155
  },
50575
51156
  "sql": {
@@ -50597,6 +51178,34 @@ const openapiDocument = {
50597
51178
  "compiled_by"
50598
51179
  ]
50599
51180
  },
51181
+ "EmailRuleTested": {
51182
+ "type": "object",
51183
+ "properties": {
51184
+ "id": {
51185
+ "type": ["string", "null"],
51186
+ "format": "uuid"
51187
+ },
51188
+ "version": {
51189
+ "type": ["integer", "null"],
51190
+ "exclusiveMinimum": 0
51191
+ },
51192
+ "vocabulary": { "type": "string" },
51193
+ "kind": {
51194
+ "description": "The kind it was tested as.",
51195
+ "type": "string",
51196
+ "enum": ["grant", "do_not_share"]
51197
+ },
51198
+ "note": {
51199
+ "description": "Present when a tree was sent without kind, so it was tested as grant.",
51200
+ "type": "string"
51201
+ }
51202
+ },
51203
+ "required": [
51204
+ "id",
51205
+ "version",
51206
+ "vocabulary"
51207
+ ]
51208
+ },
50600
51209
  "EmailRuleScopeWarning": {
50601
51210
  "type": "object",
50602
51211
  "properties": {
@@ -50714,33 +51323,6 @@ const openapiDocument = {
50714
51323
  ],
50715
51324
  "additionalProperties": false
50716
51325
  },
50717
- "EmailRuleScopeSelection": {
50718
- "type": "object",
50719
- "properties": {
50720
- "addresses": {
50721
- "maxItems": 100,
50722
- "type": "array",
50723
- "items": {
50724
- "type": "string",
50725
- "minLength": 3,
50726
- "maxLength": 320,
50727
- "pattern": "^[^\\s@]+@[^\\s@]+$"
50728
- }
50729
- },
50730
- "domains": {
50731
- "maxItems": 50,
50732
- "type": "array",
50733
- "items": {
50734
- "type": "string",
50735
- "minLength": 1,
50736
- "maxLength": 253,
50737
- "pattern": "^[^\\s@]+\\.[^\\s@]+$"
50738
- }
50739
- }
50740
- },
50741
- "additionalProperties": false,
50742
- "description": "For a selected rule: inboxes, and domains (each address at them the author reads); each one you can read."
50743
- },
50744
51326
  "EmailRuleExpectationSampleInput": {
50745
51327
  "type": "object",
50746
51328
  "properties": {
@@ -50827,6 +51409,16 @@ const openapiDocument = {
50827
51409
  "pattern": "^[^\\u0000]*$"
50828
51410
  }
50829
51411
  },
51412
+ "attachment_names": {
51413
+ "maxItems": 10,
51414
+ "type": "array",
51415
+ "items": {
51416
+ "type": "string",
51417
+ "minLength": 1,
51418
+ "maxLength": 255,
51419
+ "pattern": "^[^\\u0000-\\u001f\\u007f]*$"
51420
+ }
51421
+ },
50830
51422
  "automated": { "type": "boolean" },
50831
51423
  "from_mailing_list": { "type": "boolean" },
50832
51424
  "spam_score": {
@@ -50860,7 +51452,7 @@ const openapiDocument = {
50860
51452
  },
50861
51453
  "scope_selection": { "$ref": "#/components/schemas/EmailRuleScopeSelection" },
50862
51454
  "rule_id": {
50863
- "description": "A saved rule you can see. Its current version is tested, with its author's access.",
51455
+ "description": "A saved rule you can see. Its current version is tested, with its author's access; one you see only because it covers your mailboxes, with yours, over only those (dashboard or Primitive CLI only).",
50864
51456
  "type": "string",
50865
51457
  "format": "uuid"
50866
51458
  },
@@ -50917,25 +51509,7 @@ const openapiDocument = {
50917
51509
  "TestEmailRuleResult": {
50918
51510
  "type": "object",
50919
51511
  "properties": {
50920
- "rule": {
50921
- "type": "object",
50922
- "properties": {
50923
- "id": {
50924
- "type": ["string", "null"],
50925
- "format": "uuid"
50926
- },
50927
- "version": {
50928
- "type": ["integer", "null"],
50929
- "exclusiveMinimum": 0
50930
- },
50931
- "vocabulary": { "type": "string" }
50932
- },
50933
- "required": [
50934
- "id",
50935
- "version",
50936
- "vocabulary"
50937
- ]
50938
- },
51512
+ "rule": { "$ref": "#/components/schemas/EmailRuleTested" },
50939
51513
  "window": {
50940
51514
  "type": "object",
50941
51515
  "properties": {
@@ -51183,6 +51757,11 @@ const openapiDocument = {
51183
51757
  "minimum": 0,
51184
51758
  "description": "Emails decided on their conditions: matched, near_miss and no_match, less any missing a fact the rule reads."
51185
51759
  },
51760
+ "conditions_held": {
51761
+ "description": "Of decided, how many its conditions alone held for. Compile's breadth and conditions_breadth count this over recent mail in the scope sent to it, by default your own; here it is counted over the emails this test decided.",
51762
+ "type": "integer",
51763
+ "minimum": 0
51764
+ },
51186
51765
  "conditions": {
51187
51766
  "type": "array",
51188
51767
  "items": {
@@ -51250,7 +51829,7 @@ const openapiDocument = {
51250
51829
  "warnings": {
51251
51830
  "type": "array",
51252
51831
  "items": { "type": "string" },
51253
- "description": "What to look at first, such as what stopped every email when nothing matched."
51832
+ "description": "What to look at first, such as what stopped every email when nothing matched. On a test of the recent window, a grant rule whose conditions held for more than 20% of at least 20 emails checked (at least 90% if it has no questions), or for every one of fewer, gets a warning saying so, before those about what it decided."
51254
51833
  }
51255
51834
  },
51256
51835
  "required": [
@@ -51406,6 +51985,10 @@ const openapiDocument = {
51406
51985
  "type": "array",
51407
51986
  "items": { "type": "string" }
51408
51987
  },
51988
+ "attachment_names": {
51989
+ "type": "array",
51990
+ "items": { "type": "string" }
51991
+ },
51409
51992
  "automated": { "type": "boolean" },
51410
51993
  "from_mailing_list": { "type": "boolean" }
51411
51994
  },
@@ -51416,6 +51999,7 @@ const openapiDocument = {
51416
51999
  "subject",
51417
52000
  "text",
51418
52001
  "attachment_types",
52002
+ "attachment_names",
51419
52003
  "automated",
51420
52004
  "from_mailing_list"
51421
52005
  ],
@@ -51491,7 +52075,7 @@ const openapiDocument = {
51491
52075
  },
51492
52076
  "scope_selection": { "$ref": "#/components/schemas/EmailRuleScopeSelection" },
51493
52077
  "rule_id": {
51494
- "description": "A saved rule you can see: its current version and words.",
52078
+ "description": "A saved rule you can see, or one that covers your mailboxes (as you, sent to one of them; dashboard or Primitive CLI only): its current version and words.",
51495
52079
  "type": "string",
51496
52080
  "format": "uuid"
51497
52081
  },
@@ -51540,25 +52124,7 @@ const openapiDocument = {
51540
52124
  "GenerateLookAlikesResult": {
51541
52125
  "type": "object",
51542
52126
  "properties": {
51543
- "rule": {
51544
- "type": "object",
51545
- "properties": {
51546
- "id": {
51547
- "type": ["string", "null"],
51548
- "format": "uuid"
51549
- },
51550
- "version": {
51551
- "type": ["integer", "null"],
51552
- "exclusiveMinimum": 0
51553
- },
51554
- "vocabulary": { "type": "string" }
51555
- },
51556
- "required": [
51557
- "id",
51558
- "version",
51559
- "vocabulary"
51560
- ]
51561
- },
52127
+ "rule": { "$ref": "#/components/schemas/EmailRuleTested" },
51562
52128
  "look_alikes": {
51563
52129
  "type": "array",
51564
52130
  "items": { "$ref": "#/components/schemas/EmailRuleLookAlike" }
@@ -51621,7 +52187,7 @@ const openapiDocument = {
51621
52187
  },
51622
52188
  "scope_selection": { "$ref": "#/components/schemas/EmailRuleScopeSelection" },
51623
52189
  "rule_id": {
51624
- "description": "A saved rule you can see. Its current version is tested.",
52190
+ "description": "A saved rule you can see, or one that covers your mailboxes (as you; dashboard or Primitive CLI only). Its current version is tested.",
51625
52191
  "type": "string",
51626
52192
  "format": "uuid"
51627
52193
  },
@@ -51692,6 +52258,17 @@ const openapiDocument = {
51692
52258
  "maxLength": 255
51693
52259
  }
51694
52260
  },
52261
+ "attachment_names": {
52262
+ "description": "File names of those attachments, which the classifier and the questions read. With attachment_types, the same number, paired in order.",
52263
+ "maxItems": 20,
52264
+ "type": "array",
52265
+ "items": {
52266
+ "type": "string",
52267
+ "minLength": 1,
52268
+ "maxLength": 255,
52269
+ "pattern": "^[^\\u0000-\\u001f\\u007f]*$"
52270
+ }
52271
+ },
51695
52272
  "automated": {
51696
52273
  "description": "Whether the email was sent automatically, as its headers would show. Defaults to false.",
51697
52274
  "type": "boolean"
@@ -51722,25 +52299,7 @@ const openapiDocument = {
51722
52299
  "TestEmailRuleOnSampleResult": {
51723
52300
  "type": "object",
51724
52301
  "properties": {
51725
- "rule": {
51726
- "type": "object",
51727
- "properties": {
51728
- "id": {
51729
- "type": ["string", "null"],
51730
- "format": "uuid"
51731
- },
51732
- "version": {
51733
- "type": ["integer", "null"],
51734
- "exclusiveMinimum": 0
51735
- },
51736
- "vocabulary": { "type": "string" }
51737
- },
51738
- "required": [
51739
- "id",
51740
- "version",
51741
- "vocabulary"
51742
- ]
51743
- },
52302
+ "rule": { "$ref": "#/components/schemas/EmailRuleTested" },
51744
52303
  "received_at": {
51745
52304
  "type": "string",
51746
52305
  "format": "date-time",
@@ -51932,6 +52491,15 @@ const openapiDocument = {
51932
52491
  "type": "boolean",
51933
52492
  "description": "Whether you are its author, in your current membership. Only its author edits it."
51934
52493
  },
52494
+ "pending_objections": {
52495
+ "description": "To its author, on the dashboard or the Primitive CLI: objections to it waiting for their answer (GET /email-rule-objections). A write's answer leaves it out when it could not be read in time: absent is not 0.",
52496
+ "type": "integer",
52497
+ "minimum": 0
52498
+ },
52499
+ "objection_notice": {
52500
+ "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
52501
+ "type": "string"
52502
+ },
51935
52503
  "created_at": {
51936
52504
  "type": "string",
51937
52505
  "format": "date-time"
@@ -51949,6 +52517,27 @@ const openapiDocument = {
51949
52517
  "type": "array",
51950
52518
  "items": { "$ref": "#/components/schemas/EmailRuleScopeWarning" }
51951
52519
  },
52520
+ "expectations": {
52521
+ "description": "Its saved expectations, each list by email id, as stored: on every create, and on an update that set or removed them.",
52522
+ "type": "object",
52523
+ "properties": {
52524
+ "match": {
52525
+ "type": "array",
52526
+ "items": {
52527
+ "type": "string",
52528
+ "format": "uuid"
52529
+ }
52530
+ },
52531
+ "no_match": {
52532
+ "type": "array",
52533
+ "items": {
52534
+ "type": "string",
52535
+ "format": "uuid"
52536
+ }
52537
+ }
52538
+ },
52539
+ "required": ["match", "no_match"]
52540
+ },
51952
52541
  "expectation_samples": {
51953
52542
  "description": "Its saved made-up emails, when this write set or added some.",
51954
52543
  "type": "array",
@@ -52046,6 +52635,14 @@ const openapiDocument = {
52046
52635
  "endpoints",
52047
52636
  "shares_ended"
52048
52637
  ]
52638
+ },
52639
+ "notice": {
52640
+ "description": "After a write that saved a version of an active rule keys or webhooks read without checking it first: what it changes, and how to have the next one checked.",
52641
+ "type": "string"
52642
+ },
52643
+ "share_notice": {
52644
+ "description": "After a write that ended the rule's share: that it did, given even when affects is not, with whom it was with and the version they accepted when those can be read.",
52645
+ "type": "string"
52049
52646
  }
52050
52647
  },
52051
52648
  "required": [
@@ -52268,7 +52865,7 @@ const openapiDocument = {
52268
52865
  }
52269
52866
  },
52270
52867
  "warnings": {
52271
- "description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test (on a rule request, every compiler note on the rule).",
52868
+ "description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test, or on a grant rule that tests only who an email was sent to (on a rule request, every compiler note on the rule).",
52272
52869
  "maxItems": 10,
52273
52870
  "type": "array",
52274
52871
  "items": {
@@ -52286,6 +52883,10 @@ const openapiDocument = {
52286
52883
  "breadth": {
52287
52884
  "description": "Ignored (the compile answer's breadth).",
52288
52885
  "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
52886
+ },
52887
+ "conditions_breadth": {
52888
+ "description": "Ignored.",
52889
+ "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
52289
52890
  }
52290
52891
  },
52291
52892
  "required": [
@@ -52355,6 +52956,15 @@ const openapiDocument = {
52355
52956
  "type": "boolean",
52356
52957
  "description": "Whether you are its author, in your current membership. Only its author edits it."
52357
52958
  },
52959
+ "pending_objections": {
52960
+ "description": "To its author, on the dashboard or the Primitive CLI: objections to it waiting for their answer (GET /email-rule-objections). A write's answer leaves it out when it could not be read in time: absent is not 0.",
52961
+ "type": "integer",
52962
+ "minimum": 0
52963
+ },
52964
+ "objection_notice": {
52965
+ "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
52966
+ "type": "string"
52967
+ },
52358
52968
  "created_at": {
52359
52969
  "type": "string",
52360
52970
  "format": "date-time"
@@ -52453,6 +53063,15 @@ const openapiDocument = {
52453
53063
  "type": "boolean",
52454
53064
  "description": "Whether you are its author, in your current membership. Only its author edits it."
52455
53065
  },
53066
+ "pending_objections": {
53067
+ "description": "To its author, on the dashboard or the Primitive CLI: objections to it waiting for their answer (GET /email-rule-objections). A write's answer leaves it out when it could not be read in time: absent is not 0.",
53068
+ "type": "integer",
53069
+ "minimum": 0
53070
+ },
53071
+ "objection_notice": {
53072
+ "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
53073
+ "type": "string"
53074
+ },
52456
53075
  "created_at": {
52457
53076
  "type": "string",
52458
53077
  "format": "date-time"
@@ -52550,7 +53169,7 @@ const openapiDocument = {
52550
53169
  }
52551
53170
  },
52552
53171
  "warnings": {
52553
- "description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test (on a rule request, every compiler note on the rule).",
53172
+ "description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test, or on a grant rule that tests only who an email was sent to (on a rule request, every compiler note on the rule).",
52554
53173
  "maxItems": 10,
52555
53174
  "type": "array",
52556
53175
  "items": {
@@ -52569,6 +53188,15 @@ const openapiDocument = {
52569
53188
  "description": "Ignored (the compile answer's breadth).",
52570
53189
  "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
52571
53190
  },
53191
+ "conditions_breadth": {
53192
+ "description": "Ignored.",
53193
+ "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
53194
+ },
53195
+ "kind": {
53196
+ "description": "Must be the rule's own (400 kind_mismatch).",
53197
+ "type": "string",
53198
+ "enum": ["grant", "do_not_share"]
53199
+ },
52572
53200
  "evaluation_scope": {
52573
53201
  "type": "string",
52574
53202
  "enum": [
@@ -52627,7 +53255,7 @@ const openapiDocument = {
52627
53255
  "maximum": 2147483647
52628
53256
  },
52629
53257
  "if_expectations_pass": {
52630
- "description": "true: judge the rule as this write leaves it (new tree, restored version, new scope, or the version it activates) against the expectations it leaves, as POST /email-rules/test would, before saving; it can take as long as a test. Failed: 409 expectations_failed; on emails the rule cannot read: 409 expectations_unreadable; undecided: 409 expectations_undecided (each with details.expectations_check); none to judge: 400 no_expectations. Refused writes save nothing; a pass saves unless the rule moved meanwhile (409 email_rule_conflict). Only writes that save a version or activate the rule are judged.",
53258
+ "description": "true: judge the rule as this write leaves it (new tree, restored version, new scope, or the version it activates) against the expectations it leaves, as POST /email-rules/test would, before saving; it can take as long as a test. Failed: 409 expectations_failed; on emails the rule cannot read: 409 expectations_unreadable; undecided: 409 expectations_undecided (each with details.expectations_check); none to judge: 400 no_expectations. Refused writes save nothing; a pass saves unless the rule moved meanwhile (409 email_rule_conflict). Only writes that save a version or activate the rule are judged. Omitted: judged as with true when the write saves a version or changes the scope of an active rule that API keys or webhooks read and that has expectations. false: never judged first.",
52631
53259
  "type": "boolean"
52632
53260
  },
52633
53261
  "restore_version": {
@@ -52675,6 +53303,10 @@ const openapiDocument = {
52675
53303
  "other_keys": {
52676
53304
  "description": "How many more keys, not revoked, list the rule that you may not read. They are not named.",
52677
53305
  "type": "integer"
53306
+ },
53307
+ "share_notice": {
53308
+ "description": "When the delete ended a share: that it did, with whom it was with and the version they accepted when those can be read.",
53309
+ "type": "string"
52678
53310
  }
52679
53311
  },
52680
53312
  "required": ["deleted"]
@@ -52849,11 +53481,16 @@ const openapiDocument = {
52849
53481
  "count": {
52850
53482
  "type": "integer",
52851
53483
  "exclusiveMinimum": 0
53484
+ },
53485
+ "reads_in_full": {
53486
+ "description": "On a rule over your mail: how many of these keys also list one of your own mailboxes, or its domain, so they read your mail there beyond what this rule matches.",
53487
+ "type": "integer",
53488
+ "minimum": 0
52852
53489
  }
52853
53490
  },
52854
53491
  "required": ["owner", "count"]
52855
53492
  },
52856
- "description": "API keys that work now (not revoked, expired or suspended) whose access lists the rule, or its live share: they read what it matches. Owner: the member who holds each, or the organization."
53493
+ "description": "API keys that work now (not revoked, expired or suspended) and hold mail:read, whose access lists the rule or its live share. Owner: who holds each, or the organization."
52857
53494
  },
52858
53495
  "webhooks": {
52859
53496
  "type": "array",
@@ -52894,11 +53531,21 @@ const openapiDocument = {
52894
53531
  "count": {
52895
53532
  "type": "integer",
52896
53533
  "exclusiveMinimum": 0
53534
+ },
53535
+ "hosts": {
53536
+ "description": "Distinct sites they deliver to (the domain a host is registered under, an app's own name on a known app platform, or a function URL service in its region), sorted, at most 10; \"unknown host\" (last) for any that cannot be named.",
53537
+ "type": "array",
53538
+ "items": { "type": "string" }
53539
+ },
53540
+ "hosts_not_listed": {
53541
+ "description": "Hosts past those listed.",
53542
+ "type": "integer",
53543
+ "minimum": 0
52897
53544
  }
52898
53545
  },
52899
53546
  "required": ["owner", "count"]
52900
53547
  },
52901
- "description": "Webhooks that are sent what it matches: those whose key, as delivery acts as it, lists the rule or its live share (owner: the member who holds that key, or the organization), and the webhooks of the member the rule is shared with (owner: that member)."
53548
+ "description": "Webhooks taking received mail, bounces or reports that are sent what it matches: those of the key delivery acts as, when it lists the rule or its live share (owner: its holder, or the organization), and those of the member it is shared with (owner: that member)."
52902
53549
  },
52903
53550
  "shares": {
52904
53551
  "type": "array",
@@ -52935,7 +53582,7 @@ const openapiDocument = {
52935
53582
  },
52936
53583
  "required": ["beneficiary", "count"]
52937
53584
  },
52938
- "description": "The rule's live share, if it has one (a rule is shared at most once): the member it gives what it matches to."
53585
+ "description": "The rule's live share (at most one), by its member, if they can read it and it is among their newest 20 shares."
52939
53586
  }
52940
53587
  },
52941
53588
  "required": [
@@ -52943,7 +53590,7 @@ const openapiDocument = {
52943
53590
  "webhooks",
52944
53591
  "shares"
52945
53592
  ],
52946
- "description": "Who receives what the rule shares now, by person and count; empty unless it is an active grant rule."
53593
+ "description": "Who receives what the rule shares, by person and count. As receivers, empty unless it is an active grant rule."
52947
53594
  },
52948
53595
  "ListEmailRuleResultsResult": {
52949
53596
  "type": "object",
@@ -53405,7 +54052,7 @@ const openapiDocument = {
53405
54052
  }
53406
54053
  },
53407
54054
  "warnings": {
53408
- "description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test (on a rule request, every compiler note on the rule).",
54055
+ "description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test, or on a grant rule that tests only who an email was sent to (on a rule request, every compiler note on the rule).",
53409
54056
  "maxItems": 10,
53410
54057
  "type": "array",
53411
54058
  "items": {
@@ -53423,6 +54070,15 @@ const openapiDocument = {
53423
54070
  "breadth": {
53424
54071
  "description": "Ignored (the compile answer's breadth).",
53425
54072
  "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
54073
+ },
54074
+ "conditions_breadth": {
54075
+ "description": "Ignored.",
54076
+ "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
54077
+ },
54078
+ "kind": {
54079
+ "description": "Only grant.",
54080
+ "type": "string",
54081
+ "const": "grant"
53426
54082
  }
53427
54083
  },
53428
54084
  "required": [
@@ -53798,6 +54454,15 @@ const openapiDocument = {
53798
54454
  "type": "boolean",
53799
54455
  "description": "Whether you are its author, in your current membership. Only its author edits it."
53800
54456
  },
54457
+ "pending_objections": {
54458
+ "description": "To its author, on the dashboard or the Primitive CLI: objections to it waiting for their answer (GET /email-rule-objections). A write's answer leaves it out when it could not be read in time: absent is not 0.",
54459
+ "type": "integer",
54460
+ "minimum": 0
54461
+ },
54462
+ "objection_notice": {
54463
+ "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
54464
+ "type": "string"
54465
+ },
53801
54466
  "created_at": {
53802
54467
  "type": "string",
53803
54468
  "format": "date-time"
@@ -54018,7 +54683,7 @@ const openapiDocument = {
54018
54683
  }
54019
54684
  },
54020
54685
  "warnings": {
54021
- "description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test (on a rule request, every compiler note on the rule).",
54686
+ "description": "The compile response's warnings. Stored as sent, then any server note they lack on an address or domain the description names that the rule does not test, or on a grant rule that tests only who an email was sent to (on a rule request, every compiler note on the rule).",
54022
54687
  "maxItems": 10,
54023
54688
  "type": "array",
54024
54689
  "items": {
@@ -54037,6 +54702,15 @@ const openapiDocument = {
54037
54702
  "description": "Ignored (the compile answer's breadth).",
54038
54703
  "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
54039
54704
  },
54705
+ "conditions_breadth": {
54706
+ "description": "Ignored.",
54707
+ "anyOf": [{ "$ref": "#/components/schemas/EmailRuleBreadth" }, { "type": "null" }]
54708
+ },
54709
+ "kind": {
54710
+ "description": "Only grant.",
54711
+ "type": "string",
54712
+ "const": "grant"
54713
+ },
54040
54714
  "expectation_samples": {
54041
54715
  "description": "Made-up emails the rule must match or not, such as look-alikes it must never match, kept on the rule this accept writes.",
54042
54716
  "minItems": 1,
@@ -54054,7 +54728,7 @@ const openapiDocument = {
54054
54728
  "rule": { "$ref": "#/components/schemas/EmailRuleWithVersion" },
54055
54729
  "share": { "$ref": "#/components/schemas/EmailRuleShare" },
54056
54730
  "notice": {
54057
- "description": "What is now shared, in words.",
54731
+ "description": "What is now shared, in words. When you changed the rule and it can now match mail the request left out by who it was sent to (such as mail where an address is only copied), that comes first.",
54058
54732
  "type": "string"
54059
54733
  }
54060
54734
  },
@@ -54078,6 +54752,34 @@ const openapiDocument = {
54078
54752
  "items": {
54079
54753
  "type": "object",
54080
54754
  "properties": {
54755
+ "imported": {
54756
+ "type": ["object", "null"],
54757
+ "properties": {
54758
+ "from": {
54759
+ "type": "string",
54760
+ "const": "google_workspace"
54761
+ },
54762
+ "method": {
54763
+ "type": "string",
54764
+ "const": "primitive_direct"
54765
+ },
54766
+ "verification": {
54767
+ "type": "string",
54768
+ "const": "provider_fetched"
54769
+ },
54770
+ "at": {
54771
+ "type": "string",
54772
+ "format": "date-time"
54773
+ }
54774
+ },
54775
+ "required": [
54776
+ "from",
54777
+ "method",
54778
+ "verification",
54779
+ "at"
54780
+ ],
54781
+ "default": null
54782
+ },
54081
54783
  "delivery_report": {
54082
54784
  "type": ["object", "null"],
54083
54785
  "properties": {
@@ -54315,6 +55017,7 @@ const openapiDocument = {
54315
55017
  }
54316
55018
  },
54317
55019
  "required": [
55020
+ "imported",
54318
55021
  "id",
54319
55022
  "message_id",
54320
55023
  "domain_id",
@@ -54390,7 +55093,11 @@ const openapiDocument = {
54390
55093
  "exclusiveMinimum": 0
54391
55094
  },
54392
55095
  "receivers": {
54393
- "description": "Null when it could not be read in time; try again.",
55096
+ "description": "Null when it could not be read in time, or for a rule shown in draft or observe that turned active or was deleted after the page was read; try again.",
55097
+ "anyOf": [{ "$ref": "#/components/schemas/EmailRuleReceivers" }, { "type": "null" }]
55098
+ },
55099
+ "receivers_once_active": {
55100
+ "description": "For a grant rule in draft or observe: who would receive what it matches once it is turned active. None of them receives anything yet. Null for any other rule, and as receivers is null.",
54394
55101
  "anyOf": [{ "$ref": "#/components/schemas/EmailRuleReceivers" }, { "type": "null" }]
54395
55102
  },
54396
55103
  "created_at": {
@@ -54408,6 +55115,7 @@ const openapiDocument = {
54408
55115
  "covers",
54409
55116
  "current_version",
54410
55117
  "receivers",
55118
+ "receivers_once_active",
54411
55119
  "created_at"
54412
55120
  ]
54413
55121
  },
@@ -54478,7 +55186,11 @@ const openapiDocument = {
54478
55186
  "description": "Null when covers_now is false."
54479
55187
  },
54480
55188
  "receivers": {
54481
- "description": "Null when covers_now is false (who receives it now is not about your mail), or when it could not be read in time.",
55189
+ "description": "Null when covers_now is false (who receives it now is not about your mail), when it could not be read in time, or for a rule shown in draft or observe that turned active or was deleted after it was read; try again.",
55190
+ "anyOf": [{ "$ref": "#/components/schemas/EmailRuleReceivers" }, { "type": "null" }]
55191
+ },
55192
+ "receivers_once_active": {
55193
+ "description": "As on the list; also null when covers_now is false.",
54482
55194
  "anyOf": [{ "$ref": "#/components/schemas/EmailRuleReceivers" }, { "type": "null" }]
54483
55195
  },
54484
55196
  "created_at": {
@@ -54508,6 +55220,7 @@ const openapiDocument = {
54508
55220
  "covers",
54509
55221
  "current_version",
54510
55222
  "receivers",
55223
+ "receivers_once_active",
54511
55224
  "created_at",
54512
55225
  "covers_now",
54513
55226
  "current"
@@ -54625,6 +55338,10 @@ const openapiDocument = {
54625
55338
  "type": "array",
54626
55339
  "items": { "$ref": "#/components/schemas/EmailRuleDecisionOnMyMail" }
54627
55340
  },
55341
+ "ListEmailRuleVersionsCoveringMeResult": {
55342
+ "type": "array",
55343
+ "items": { "$ref": "#/components/schemas/EmailRuleCoveringMeVersion" }
55344
+ },
54628
55345
  "EmailRuleObjection": {
54629
55346
  "type": "object",
54630
55347
  "properties": {
@@ -54665,8 +55382,14 @@ const openapiDocument = {
54665
55382
  "description": "Who wrote the rule.",
54666
55383
  "$ref": "#/components/schemas/EmailRuleObjectionPerson"
54667
55384
  },
54668
- "is_objector": { "type": "boolean" },
54669
- "is_author": { "type": "boolean" },
55385
+ "is_objector": {
55386
+ "type": "boolean",
55387
+ "description": "Whether you made it, in your current membership."
55388
+ },
55389
+ "is_author": {
55390
+ "type": "boolean",
55391
+ "description": "Whether it is to a rule you wrote, in your current membership."
55392
+ },
54670
55393
  "message": {
54671
55394
  "description": "Why the objector objects; null when none was given, or once withdrawn.",
54672
55395
  "type": ["string", "null"]
@@ -54884,6 +55607,82 @@ const openapiDocument = {
54884
55607
  "outbound_ready"
54885
55608
  ]
54886
55609
  },
55610
+ "ImportGoogleMailMessageRequest": {
55611
+ "type": "object",
55612
+ "properties": { "message_id": {
55613
+ "type": "string",
55614
+ "pattern": "^[A-Za-z0-9_-]{1,128}$"
55615
+ } },
55616
+ "required": ["message_id"],
55617
+ "additionalProperties": false
55618
+ },
55619
+ "ImportGoogleMailMessageResult": { "anyOf": [
55620
+ {
55621
+ "type": "object",
55622
+ "properties": {
55623
+ "status": {
55624
+ "type": "string",
55625
+ "enum": ["imported", "already_present"]
55626
+ },
55627
+ "email_id": {
55628
+ "type": "string",
55629
+ "format": "uuid",
55630
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
55631
+ }
55632
+ },
55633
+ "required": ["status", "email_id"]
55634
+ },
55635
+ {
55636
+ "type": "object",
55637
+ "properties": { "status": {
55638
+ "type": "string",
55639
+ "enum": [
55640
+ "suppressed",
55641
+ "in_progress",
55642
+ "indeterminate"
55643
+ ]
55644
+ } },
55645
+ "required": ["status"]
55646
+ },
55647
+ {
55648
+ "type": "object",
55649
+ "properties": {
55650
+ "status": {
55651
+ "type": "string",
55652
+ "const": "skipped"
55653
+ },
55654
+ "reason": {
55655
+ "type": "string",
55656
+ "enum": [
55657
+ "excluded_label",
55658
+ "out_of_window",
55659
+ "too_large",
55660
+ "provider_missing"
55661
+ ]
55662
+ }
55663
+ },
55664
+ "required": ["status", "reason"]
55665
+ }
55666
+ ] },
55667
+ "ReconcileGoogleMailImportRequest": {
55668
+ "type": "object",
55669
+ "properties": {},
55670
+ "additionalProperties": false
55671
+ },
55672
+ "ReconcileGoogleMailImportResult": {
55673
+ "type": "object",
55674
+ "properties": {
55675
+ "cleaned": {
55676
+ "type": "integer",
55677
+ "minimum": 0
55678
+ },
55679
+ "unresolved": {
55680
+ "type": "integer",
55681
+ "minimum": 0
55682
+ }
55683
+ },
55684
+ "required": ["cleaned", "unresolved"]
55685
+ },
54887
55686
  "ThreadMute": {
54888
55687
  "type": "object",
54889
55688
  "required": [
@@ -55078,6 +55877,7 @@ const openapiDocument = {
55078
55877
  "type": "object",
55079
55878
  "description": "What an inbound email says, as plain text, with the fields that identify it.",
55080
55879
  "required": [
55880
+ "imported",
55081
55881
  "id",
55082
55882
  "thread_id",
55083
55883
  "received_at",
@@ -55098,6 +55898,35 @@ const openapiDocument = {
55098
55898
  "attachments"
55099
55899
  ],
55100
55900
  "properties": {
55901
+ "imported": {
55902
+ "type": ["object", "null"],
55903
+ "description": "Historical acquisition; never sender or SMTP delivery proof. Null for live mail. Excluded from live tails and deltas.",
55904
+ "required": [
55905
+ "from",
55906
+ "method",
55907
+ "verification",
55908
+ "at"
55909
+ ],
55910
+ "properties": {
55911
+ "from": {
55912
+ "type": "string",
55913
+ "enum": ["google_workspace"]
55914
+ },
55915
+ "method": {
55916
+ "type": "string",
55917
+ "enum": ["primitive_direct"]
55918
+ },
55919
+ "verification": {
55920
+ "type": "string",
55921
+ "enum": ["provider_fetched"]
55922
+ },
55923
+ "at": {
55924
+ "type": "string",
55925
+ "format": "date-time",
55926
+ "description": "When Primitive acquired this historical message."
55927
+ }
55928
+ }
55929
+ },
55101
55930
  "id": {
55102
55931
  "type": "string",
55103
55932
  "format": "uuid",
@@ -58590,6 +59419,24 @@ const openapiDocument = {
58590
59419
  "agent_address",
58591
59420
  "person"
58592
59421
  ]
59422
+ },
59423
+ "address_warnings": {
59424
+ "type": "array",
59425
+ "items": {
59426
+ "type": "object",
59427
+ "properties": {
59428
+ "address": { "type": "string" },
59429
+ "reason": { "type": "string" },
59430
+ "description": { "type": "string" }
59431
+ },
59432
+ "required": [
59433
+ "address",
59434
+ "reason",
59435
+ "description"
59436
+ ],
59437
+ "additionalProperties": {}
59438
+ },
59439
+ "description": "Notes on the key's grant, worked out from it on every read: whole_mailbox, an address or domain listed beside email rules, which the key reads in full; no_send_address, with address empty, a key that can send but lists only email rules. The notes that need the organization's mailboxes and domains (member_address, unowned_domain) are given only when the grant is written. Only rules not deleted and shares not ended count. Empty, or absent, for a key on a template, and empty for a revoked key."
58593
59440
  }
58594
59441
  },
58595
59442
  "required": [
@@ -59442,6 +60289,27 @@ const openapiDocument = {
59442
60289
  "additionalProperties": {}
59443
60290
  }
59444
60291
  },
60292
+ "rules_added": {
60293
+ "description": "Each rule (or share) the edit put on a key that reads mail, with granted_emails, how many emails it has already shared, which the key can now read while it is active: counted up to 10,000, with more: true past that; null when not counted.",
60294
+ "type": "array",
60295
+ "items": {
60296
+ "type": "object",
60297
+ "properties": {
60298
+ "id": { "type": "string" },
60299
+ "granted_emails": {
60300
+ "type": ["integer", "null"],
60301
+ "minimum": 0,
60302
+ "maximum": 9007199254740991
60303
+ },
60304
+ "more": {
60305
+ "type": "boolean",
60306
+ "const": true
60307
+ }
60308
+ },
60309
+ "required": ["id", "granted_emails"],
60310
+ "additionalProperties": {}
60311
+ }
60312
+ },
59445
60313
  "grant_unguarded": {
59446
60314
  "description": "Present, as true, when the grant was changed without expected_grant_ref, so the change was not checked against changes made since the key was read. Send grant.ref from GET /v1/keys/{id} as expected_grant_ref to have such a change refused instead (409 conflict, details.reason grant_changed).",
59447
60315
  "type": "boolean",
@@ -59877,6 +60745,24 @@ const openapiDocument = {
59877
60745
  "rotated_to": {
59878
60746
  "description": "The id of the newest key rotated from this key that the caller may read; null until it is rotated, or when the caller may read no key rotated from it. A revoked one is still named: GET /v1/keys/{id} answers it.",
59879
60747
  "type": ["string", "null"]
60748
+ },
60749
+ "address_warnings": {
60750
+ "type": "array",
60751
+ "items": {
60752
+ "type": "object",
60753
+ "properties": {
60754
+ "address": { "type": "string" },
60755
+ "reason": { "type": "string" },
60756
+ "description": { "type": "string" }
60757
+ },
60758
+ "required": [
60759
+ "address",
60760
+ "reason",
60761
+ "description"
60762
+ ],
60763
+ "additionalProperties": {}
60764
+ },
60765
+ "description": "Notes on the key's grant, worked out from it on every read: whole_mailbox, an address or domain listed beside email rules, which the key reads in full; no_send_address, with address empty, a key that can send but lists only email rules. The notes that need the organization's mailboxes and domains (member_address, unowned_domain) are given only when the grant is written. Only rules not deleted and shares not ended count. Empty, or absent, for a key on a template, and empty for a revoked key."
59880
60766
  }
59881
60767
  },
59882
60768
  "required": [
@@ -59976,6 +60862,24 @@ const openapiDocument = {
59976
60862
  "rotated_to": {
59977
60863
  "description": "The id of the newest key rotated from this key that the caller may read; null until it is rotated, or when the caller may read no key rotated from it. A revoked one is still named: GET /v1/keys/{id} answers it.",
59978
60864
  "type": ["string", "null"]
60865
+ },
60866
+ "address_warnings": {
60867
+ "type": "array",
60868
+ "items": {
60869
+ "type": "object",
60870
+ "properties": {
60871
+ "address": { "type": "string" },
60872
+ "reason": { "type": "string" },
60873
+ "description": { "type": "string" }
60874
+ },
60875
+ "required": [
60876
+ "address",
60877
+ "reason",
60878
+ "description"
60879
+ ],
60880
+ "additionalProperties": {}
60881
+ },
60882
+ "description": "Notes on the key's grant, worked out from it on every read: whole_mailbox, an address or domain listed beside email rules, which the key reads in full; no_send_address, with address empty, a key that can send but lists only email rules. The notes that need the organization's mailboxes and domains (member_address, unowned_domain) are given only when the grant is written. Only rules not deleted and shares not ended count. Empty, or absent, for a key on a template, and empty for a revoked key."
59979
60883
  }
59980
60884
  },
59981
60885
  "required": [
@@ -62746,6 +63650,29 @@ const openapiDocument = {
62746
63650
  ],
62747
63651
  "description": "A default wake schedule the install creates for the function: the command it sends, a 5-field cron expression and timezone, and the scheduler sender localpart on the installed address's domain. The owner can change or delete it after install."
62748
63652
  },
63653
+ "EndpointCreateWarning": {
63654
+ "type": "object",
63655
+ "properties": {
63656
+ "code": { "type": "string" },
63657
+ "message": { "type": "string" },
63658
+ "endpoint_id": {
63659
+ "description": "The other webhook a same_path_as_another_endpoint warning names.",
63660
+ "type": "string",
63661
+ "format": "uuid",
63662
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
63663
+ }
63664
+ },
63665
+ "required": ["code", "message"],
63666
+ "additionalProperties": {}
63667
+ },
63668
+ "CreatedEndpoint": { "allOf": [{ "$ref": "#/components/schemas/Endpoint" }, {
63669
+ "type": "object",
63670
+ "properties": { "warnings": {
63671
+ "description": "For a webhook (kind http). same_path_as_another_endpoint: another webhook you can see, on and not deleted, sends to the same scheme, host and path with any query string, so each email both receive is delivered twice; endpoint_id names it (at most 5, newest first). The URL is never echoed. Empty when none; absent when it could not be checked (a caller who may not read endpoints, or a failed read). Other codes may be added.",
63672
+ "type": "array",
63673
+ "items": { "$ref": "#/components/schemas/EndpointCreateWarning" }
63674
+ } }
63675
+ }] },
62749
63676
  "EndpointUpdateWarning": {
62750
63677
  "type": "object",
62751
63678
  "properties": {
@@ -63148,8 +64075,11 @@ const KEY_WRITE_OPERATION_IDS = /* @__PURE__ */ new Set([
63148
64075
  "credentials:update-key",
63149
64076
  "credentials:revoke-credential"
63150
64077
  ]);
63151
- /** The operations the CLI generates commands for: the manifest without the key writes. */
63152
- const cliOperationManifest = operationManifest.filter((operation) => !KEY_WRITE_OPERATION_IDS.has(`${operation.tagCommand}:${operation.command}`));
64078
+ const CREDENTIAL_HEADER_OPERATION_IDS = /* @__PURE__ */ new Set(["emails:import-google-mail-message"]);
64079
+ /** Every operation id the CLI generates no command for. */
64080
+ const CLI_OMITTED_OPERATION_IDS = /* @__PURE__ */ new Set([...KEY_WRITE_OPERATION_IDS, ...CREDENTIAL_HEADER_OPERATION_IDS]);
64081
+ /** The operations the CLI generates commands for: the manifest without CLI_OMITTED_OPERATION_IDS. */
64082
+ const cliOperationManifest = operationManifest.filter((operation) => !CLI_OMITTED_OPERATION_IDS.has(`${operation.tagCommand}:${operation.command}`));
63153
64083
  // src/oclif/commands/activity.ts
63154
64084
  const API_KEY_FLAG$3 = Flags.string({
63155
64085
  description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
@@ -63183,6 +64113,7 @@ async function call$1(request) {
63183
64113
  };
63184
64114
  }
63185
64115
  }
64116
+ /** A time as "YYYY-MM-DD HH:MM" in UTC; anything that is not a time, as given. */
63186
64117
  function minute(value) {
63187
64118
  if (!value) return "";
63188
64119
  const date = new Date(value);
@@ -63244,31 +64175,46 @@ function formatAskedRows(items, complete = false) {
63244
64175
  const line = (cells) => cells.map((cell, i) => i === cells.length - 1 ? cell : cell.padEnd(widths[i] ?? 0)).join(" ");
63245
64176
  return [line(header), ...rows.map(line)].join("\n");
63246
64177
  }
64178
+ /** Where to see what covers you now, whatever the notices have caught up with. */
64179
+ const COVERING_NOW = `\`primitive email-rule-oversight list-email-rules-covering-me\` shows what covers you now.`;
63247
64180
  /**
63248
64181
  * One line per coverage notice: what it told (started, new_version, or
63249
64182
  * baseline for a rule that already covered you when notices began), when, the
63250
- * rule's name then, and its author. No id: a notice's id is not a rule's, and
63251
- * no command takes it. `complete` says the listing is the whole of it (from
63252
- * the first page to the last, nothing withheld), the only time an empty one
63253
- * means no notice has been written for you.
64183
+ * rule's name then, and its author; and, first, each rule that covers you now
64184
+ * whose notice is not written yet (pending, dated when the rule last
64185
+ * changed), then the rule's id (rule_id; a pending row's own id is the
64186
+ * rule's), never the notice's own id, which no command takes.
64187
+ * `complete` says the listing is the whole of it (from the first page to the
64188
+ * last, nothing withheld). Even then an empty one is no promise that nothing
64189
+ * covers you: notices can lag, so it
64190
+ * says how far they go (`asOf`, when the server says) and where to look now.
63254
64191
  */
63255
- function formatConcerningRows(items, complete = false) {
63256
- if (items.length === 0) return complete ? "No other member's rule has covered your mail since these notices began." : "Nothing to show.";
64192
+ function formatConcerningRows(items, complete = false, asOf, pendingComplete) {
64193
+ const unfinished = pendingComplete === false ? "The check for rules that cover you with no notice written yet did not finish, so some may be missing: run this again in a moment." : null;
64194
+ if (items.length === 0) return complete ? `${asOf ? `Nothing recorded for you as of ${minute(asOf)} UTC.` : "Nothing recorded for you yet."} Notices can lag, so a rule that starts covering your mail may not appear here yet. ${COVERING_NOW}` : unfinished ? `${unfinished} ${COVERING_NOW}` : "Nothing to show.";
63257
64195
  const rows = items.map((item) => [
63258
64196
  item.state,
63259
64197
  minute(item.created_at),
63260
64198
  item.name ?? "(unnamed)",
63261
- item.created_through.name ?? item.created_through.id ?? ""
64199
+ item.created_through.name ?? item.created_through.id ?? "",
64200
+ item.rule_id ?? (item.state === "pending" ? item.id : "")
63262
64201
  ]);
63263
64202
  const header = [
63264
64203
  "NOTICE",
63265
64204
  "WHEN (UTC)",
63266
64205
  "RULE",
63267
- "AUTHOR"
64206
+ "AUTHOR",
64207
+ "RULE ID"
63268
64208
  ];
63269
64209
  const widths = header.map((h, i) => Math.max(h.length, ...rows.map((row) => (row[i] ?? "").length)));
63270
- const line = (cells) => cells.map((cell, i) => i === cells.length - 1 ? cell : cell.padEnd(widths[i] ?? 0)).join(" ");
63271
- return [line(header), ...rows.map(line)].join("\n");
64210
+ const line = (cells) => cells.map((cell, i) => i === cells.length - 1 ? cell : cell.padEnd(widths[i] ?? 0)).join(" ").trimEnd();
64211
+ const pending = items.some((item) => item.state === "pending") ? ["", `pending: the rule covers you now and its notice is not written yet. ${COVERING_NOW}`] : [];
64212
+ return [
64213
+ line(header),
64214
+ ...rows.map(line),
64215
+ ...pending,
64216
+ ...unfinished ? ["", pending.length > 0 ? unfinished : `${unfinished} ${COVERING_NOW}`] : []
64217
+ ].join("\n");
63272
64218
  }
63273
64219
  /** What was left out, and why: no counts, only the kinds and what would show them. */
63274
64220
  function formatWithheld(withheld) {
@@ -63300,6 +64246,8 @@ async function runActivityList(command, flags, relation) {
63300
64246
  const items = [];
63301
64247
  let covers = "";
63302
64248
  let withheld = [];
64249
+ let asOf;
64250
+ let pendingComplete;
63303
64251
  let cursor = flags.cursor;
63304
64252
  for (let page = 0; page < (flags.all ? 50 : 1); page += 1) {
63305
64253
  const result = await call$1(listActivityObjects({
@@ -63326,6 +64274,11 @@ async function runActivityList(command, flags, relation) {
63326
64274
  }
63327
64275
  covers = result.data.covers;
63328
64276
  withheld = result.data.withheld;
64277
+ if (page === 0) {
64278
+ const first = result.data;
64279
+ asOf = first.as_of;
64280
+ pendingComplete = first.pending_complete;
64281
+ }
63329
64282
  items.push(...result.data.items);
63330
64283
  cursor = result.data.next_cursor ?? void 0;
63331
64284
  if (!cursor) break;
@@ -63335,12 +64288,14 @@ async function runActivityList(command, flags, relation) {
63335
64288
  covers,
63336
64289
  items,
63337
64290
  withheld,
63338
- next_cursor: cursor ?? null
64291
+ next_cursor: cursor ?? null,
64292
+ ...asOf !== void 0 ? { as_of: asOf } : {},
64293
+ ...pendingComplete !== void 0 ? { pending_complete: pendingComplete } : {}
63339
64294
  }, null, 2));
63340
64295
  return;
63341
64296
  }
63342
- const complete = flags.state === "all" && !flags.cursor && !cursor && withheld.length === 0;
63343
- command.log(relation === "asked" ? formatAskedRows(items, complete) : relation === "concerning" ? formatConcerningRows(items, complete) : formatActivityRows(covers, items));
64297
+ const complete = flags.state === "all" && !flags.cursor && !cursor && withheld.length === 0 && pendingComplete !== false;
64298
+ command.log(relation === "asked" ? formatAskedRows(items, complete) : relation === "concerning" ? formatConcerningRows(items, complete, asOf, pendingComplete) : formatActivityRows(covers, items));
63344
64299
  const notShown = formatWithheld(withheld);
63345
64300
  if (notShown) command.log(`\n${notShown}`);
63346
64301
  if (cursor) command.log(`\nMore: ${resumeCommand(command.config.bin, flags, cursor, relation)}`);
@@ -63423,9 +64378,11 @@ var ActivityAskedCommand = class ActivityAskedCommand extends Command {
63423
64378
  }
63424
64379
  };
63425
64380
  var ActivityConcerningCommand = class ActivityConcerningCommand extends Command {
63426
- static description = `List what other members did that concerns your own mail, newest first: each time another member's rule started covering one of your mailboxes (started), or saved a new version while covering it (new_version), named as the rule was then, with its author. A rule that already covered you when these notices began is listed once as baseline.
64381
+ static description = `List what other members did that concerns your own mail, newest first: each time another member's rule started covering your mailboxes, that is, it was made or turned active over them or an active rule came to reach you, once per rule, not per mailbox (started), or saved a new version while covering it (new_version), named as the rule was then, with its author. A rule that already covered you when these notices began is listed once as baseline. A rule in draft or observe grants and blocks nothing and gets no notice while it stays there, though \`primitive email-rule-oversight list-email-rules-covering-me\` lists it; turning it active is what is listed, and a rule that covers you again within an hour of its last started or baseline notice gets no new one, though a version it saved meanwhile is listed as new_version. A rule that stops covering you, a rename, and changes to its keys, webhooks or share are not listed.
63427
64382
 
63428
- Signed in with \`primitive login\`, you see the notices about your own mail under your current membership. A key, an app or an agent has none. The rules that cover you now, with their ids, are listed by \`primitive email-rule-oversight list-email-rules-covering-me\`; ask a rule's author to turn it off with \`primitive email-rule-oversight create-email-rule-objection --rule-id <rule-id>\`. With --json each item's id is the notice's, not the rule's.`;
64383
+ Notices can lag. Until a start is written, a rule that now covers you is listed first as pending, dated when the rule last changed, and with --json its id is the rule's. When the check for such rules did not finish, the listing says so and --json carries pending_complete: false; run it again in a moment.
64384
+
64385
+ Signed in with \`primitive login\`, you see the notices about your own mail under your current membership. A key, an app or an agent has none. The rules that cover you now, with their ids, are listed by \`primitive email-rule-oversight list-email-rules-covering-me\`; ask a rule's author to turn it off with \`primitive email-rule-oversight create-email-rule-objection --rule-id <rule-id>\`. The table's RULE ID, and rule_id with --json, is the rule's id, which \`primitive email-rule-oversight get-email-rule-covering-me <rule-id>\` and create-email-rule-objection --rule-id take; with --json each notice's own id is the notice's, not the rule's.`;
63429
64386
  static summary = "List rules that started covering your mail or changed";
63430
64387
  static examples = ["<%= config.bin %> activity concerning", "<%= config.bin %> activity concerning --all --json"];
63431
64388
  static flags = {
@@ -67804,11 +68761,12 @@ async function sendFromAnswer(apiClient, authFailureContext, purpose) {
67804
68761
  const missing = await missingSendPermission(apiClient);
67805
68762
  if (missing) throw new MissingSendPermissionError(missing);
67806
68763
  }
67807
- const parts = [typeof data.reason === "string" && data.reason.trim() ? data.reason.trim().replace(/\.?$/, ".") : "This credential has no single address to send from, so --from has no default."];
68764
+ const nothingListed = addresses.length === 0 && domains.length === 0;
68765
+ const parts = [typeof data.reason === "string" && data.reason.trim() ? data.reason.trim().replace(/\.?$/, ".") : nothingListed ? "This credential lists no address or domain it can send from, so it cannot send." : "This credential has no single address to send from, so --from has no default."];
67808
68766
  if (addresses.length > 0) parts.push(`Addresses you may send from: ${addresses.join(", ")}.`);
67809
68767
  if (domains.length > 0) parts.push(`${addresses.length > 0 ? "Also any" : "Any"} address at: ${domains.join(", ")}.`);
67810
68768
  parts.push(await claimHint(apiClient));
67811
- parts.push("Pass the address to use with --from <address>.");
68769
+ if (!nothingListed) parts.push("Pass the address to use with --from <address>.");
67812
68770
  throw new Errors.CLIError(parts.filter((part) => part).join(" "), { exit: 1 });
67813
68771
  }
67814
68772
  /** The address of resolveDefaultFrom. */
@@ -70493,6 +71451,130 @@ async function checkDomains(client) {
70493
71451
  };
70494
71452
  }
70495
71453
  }
71454
+ const ASKED_PAGE = 50;
71455
+ const ASKED_KINDS = ["email_rule_request", "email_rule_objection"];
71456
+ /**
71457
+ * One page of the caller's activity list; "refused" when the credential may
71458
+ * not list activity at all (401 or 403), null when it could not be read.
71459
+ */
71460
+ async function activityPage(client, query) {
71461
+ try {
71462
+ const result = await listActivityObjects({
71463
+ client: client.client,
71464
+ query,
71465
+ responseStyle: "fields"
71466
+ });
71467
+ const status = result.response?.status;
71468
+ if (status === 401 || status === 403) return "refused";
71469
+ if (result.error) return null;
71470
+ return result.data?.data ?? null;
71471
+ } catch {
71472
+ return null;
71473
+ }
71474
+ }
71475
+ /**
71476
+ * Up to ASKED_PAGES pages of a list, stopping early once `enough` says the
71477
+ * rest cannot matter: the first page (which alone carries what a list says
71478
+ * beyond its items), every item read, and whether a page was left unread.
71479
+ * "refused" or null as the first page is; a later page that fails to read is
71480
+ * a page left unread.
71481
+ */
71482
+ async function activityPages(client, query, enough = () => false) {
71483
+ const first = await activityPage(client, query);
71484
+ if (first === "refused" || first === null) return first;
71485
+ const items = [...first.items];
71486
+ let page = first;
71487
+ for (let read = 1;; read += 1) {
71488
+ const cursor = page.next_cursor;
71489
+ if (cursor == null || enough(page)) return {
71490
+ first,
71491
+ items,
71492
+ more: false
71493
+ };
71494
+ if (read >= 5) return {
71495
+ first,
71496
+ items,
71497
+ more: true
71498
+ };
71499
+ const next = await activityPage(client, {
71500
+ ...query,
71501
+ cursor
71502
+ });
71503
+ if (next === "refused" || next === null) return {
71504
+ first,
71505
+ items,
71506
+ more: true
71507
+ };
71508
+ items.push(...next.items);
71509
+ page = next;
71510
+ }
71511
+ }
71512
+ const plural$1 = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
71513
+ /**
71514
+ * What is waiting on a person, read from the activity lists (notification
71515
+ * emails can be off, so this is where they are told): rule requests made to
71516
+ * them and objections to rules they wrote that are still open, rules that
71517
+ * cover their mail now with no notice written yet, and coverage notices from
71518
+ * the last RECENT_COVERAGE_DAYS days. Null for a credential that is asked
71519
+ * nothing: one the list refuses, one whose list is not a person's own, or one
71520
+ * from which every asked kind is withheld. Whatever it could not see (a kind
71521
+ * withheld from it, a page it did not read, a pending check the API did not
71522
+ * finish) is said, so the row is never OK for it, and counts read past are
71523
+ * lower bounds. WARN when anything is waiting or unchecked; never a FAIL.
71524
+ */
71525
+ async function checkAskedOfYou(client, now = Date.now()) {
71526
+ const asked = await activityPages(client, {
71527
+ relation: "asked",
71528
+ state: "live",
71529
+ limit: ASKED_PAGE
71530
+ });
71531
+ if (asked === "refused") return null;
71532
+ if (!asked) return {
71533
+ status: "warn",
71534
+ message: "could not check what is waiting for you",
71535
+ hint: "Run `primitive activity asked --state live` and `primitive activity concerning`."
71536
+ };
71537
+ const hidden = new Set(asked.first.withheld.map((entry) => entry.kind));
71538
+ if (asked.first.covers !== "you" || ASKED_KINDS.every((kind) => hidden.has(kind))) return null;
71539
+ const since = now - 6048e5;
71540
+ const read = await activityPages(client, {
71541
+ relation: "concerning",
71542
+ state: "all",
71543
+ limit: ASKED_PAGE
71544
+ }, (page) => {
71545
+ const last = page.items.at(-1);
71546
+ return last !== void 0 && Date.parse(last.created_at) < since;
71547
+ });
71548
+ const concerning = read === "refused" ? null : read;
71549
+ const coverageHidden = concerning?.first.withheld.some((entry) => entry.kind === "email_rule_coverage") ?? false;
71550
+ const coverageUnfinished = concerning !== null && (concerning.more || concerning.first.pending_complete === false);
71551
+ const requests = asked.items.filter((item) => item.kind === "email_rule_request").length;
71552
+ const objections = asked.items.filter((item) => item.kind === "email_rule_objection").length;
71553
+ const waiting = asked.more ? [`at least ${requests + objections} rule requests and objections`] : [...requests > 0 ? [plural$1(requests, "rule request")] : [], ...objections > 0 ? [plural$1(objections, "objection")] : []];
71554
+ const items = concerning?.items ?? [];
71555
+ const pending = items.filter((item) => item.state === "pending").length;
71556
+ const notices = items.filter((item) => (item.state === "started" || item.state === "new_version") && Date.parse(item.created_at) >= since).length;
71557
+ const parts = [
71558
+ ...waiting.length > 0 ? [`${waiting.join(" and ")} waiting for you`] : [],
71559
+ ...ASKED_KINDS.filter((kind) => hidden.has(kind)).map((kind) => `${kind === "email_rule_request" ? "rule requests" : "objections"} not shown to this credential`),
71560
+ ...pending > 0 ? [`${concerning?.first.pending_complete === false ? "at least " : ""}${plural$1(pending, "rule")} ${pending === 1 ? "covers" : "cover"} your mail with no notice written yet`] : [],
71561
+ ...notices > 0 ? [`${concerning?.more ? "at least " : ""}${plural$1(notices, "coverage notice")} in the last 7 days`] : [],
71562
+ ...coverageHidden ? ["rules over your mail not shown to this credential"] : !concerning ? ["could not check the rules over your mail"] : coverageUnfinished ? ["the check of the rules over your mail did not finish"] : []
71563
+ ];
71564
+ if (parts.length === 0) {
71565
+ const asOf = concerning?.first.as_of;
71566
+ return {
71567
+ status: "ok",
71568
+ message: `nothing waiting for you; ${asOf ? `coverage notices as of ${minute(asOf)} UTC` : "coverage notices can lag"}`
71569
+ };
71570
+ }
71571
+ const commands = [...waiting.length > 0 ? ["`primitive activity asked --state live`"] : [], ...pending > 0 || notices > 0 || !concerning || coverageUnfinished ? ["`primitive activity concerning`"] : []];
71572
+ return {
71573
+ status: "warn",
71574
+ message: parts.join("; "),
71575
+ ...commands.length > 0 ? { hint: `Run ${commands.join(" and ")}.` } : {}
71576
+ };
71577
+ }
70496
71578
  /**
70497
71579
  * A background start that gave up, such as a receiver replacing its lost
70498
71580
  * supervisor, has no terminal to report to. Its saved reason is read
@@ -70521,7 +71603,7 @@ function checkReceiverStart(configDir, profileName) {
70521
71603
  }
70522
71604
  }
70523
71605
  var DoctorCommand = class DoctorCommand extends Command {
70524
- static description = `Run a one-shot environment health check: Node version, proxy env, CLI auth resolution, /account reachability, and verified-domain status. With PRIMITIVE_AGENT_PROFILE selected, reports saved identity offline and points to whoami and exact listener status instead of account-only checks. Prints actionable hints for each warning or failure.`;
71606
+ static description = `Run a one-shot environment health check: Node version, proxy env, CLI auth resolution, /account reachability, verified-domain status, and, signed in as yourself, the rule requests and objections waiting for you and the rules that recently started covering your mail. With PRIMITIVE_AGENT_PROFILE selected, reports saved identity offline and points to whoami and exact listener status instead of account-only checks. Prints actionable hints for each warning or failure.`;
70525
71607
  static summary = "Check the local environment and live API for common problems";
70526
71608
  static examples = ["<%= config.bin %> doctor", "<%= config.bin %> doctor --api-key prim_..."];
70527
71609
  static flags = {
@@ -70587,6 +71669,11 @@ var DoctorCommand = class DoctorCommand extends Command {
70587
71669
  label: "Domains",
70588
71670
  outcome: domainsOutcome
70589
71671
  });
71672
+ const asked = await checkAskedOfYou(apiClient);
71673
+ if (asked) rows.push({
71674
+ label: "Asked of you",
71675
+ outcome: asked
71676
+ });
70590
71677
  }
70591
71678
  }
70592
71679
  }
@@ -70609,9 +71696,10 @@ var DoctorCommand = class DoctorCommand extends Command {
70609
71696
  }
70610
71697
  };
70611
71698
  // src/oclif/commands/domains-zone-file.ts
70612
- function zoneFileUrl(baseUrl, domainId, outboundOnly) {
71699
+ function zoneFileUrl(baseUrl, domainId, outboundOnly, inboundOnly = false) {
70613
71700
  const url = new URL(`${baseUrl.replace(/\/$/, "")}/domains/${encodeURIComponent(domainId)}/zone-file`);
70614
71701
  if (outboundOnly) url.searchParams.set("outbound_only", "true");
71702
+ if (inboundOnly) url.searchParams.set("inbound_only", "true");
70615
71703
  return url.toString();
70616
71704
  }
70617
71705
  function contentDispositionFilename(value) {
@@ -70651,7 +71739,8 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
70651
71739
  "<%= config.bin %> domains zone-file --id <domain-id>",
70652
71740
  "<%= config.bin %> domains zone-file --id <domain-id> --output example.com.zone",
70653
71741
  "<%= config.bin %> domains zone-file --domain example.com --output example.com.zone",
70654
- "<%= config.bin %> domains zone-file --id <domain-id> --outbound-only"
71742
+ "<%= config.bin %> domains zone-file --id <domain-id> --outbound-only",
71743
+ "<%= config.bin %> domains zone-file --id <domain-id> --inbound-only"
70655
71744
  ];
70656
71745
  static flags = {
70657
71746
  "api-key": Flags.string({
@@ -70669,7 +71758,14 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
70669
71758
  char: "o",
70670
71759
  description: "Write the zone file to this path instead of stdout. Defaults to stdout."
70671
71760
  }),
70672
- "outbound-only": Flags.boolean({ description: "Include only outbound DNS records. Verified domains default to outbound-only at the API; this flag is explicit for agents and scripts." }),
71761
+ "outbound-only": Flags.boolean({
71762
+ description: "Include only the outbound DNS records. Without it or --inbound-only, the file has every record. Not with --inbound-only.",
71763
+ exclusive: ["inbound-only"]
71764
+ }),
71765
+ "inbound-only": Flags.boolean({
71766
+ description: "Include only the inbound records: MX and the ownership TXT record. Not with --outbound-only.",
71767
+ exclusive: ["outbound-only"]
71768
+ }),
70673
71769
  time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
70674
71770
  };
70675
71771
  async run() {
@@ -70706,7 +71802,7 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
70706
71802
  if (!domainId) throw new Errors.CLIError("Could not resolve a domain id.", { exit: 1 });
70707
71803
  let response;
70708
71804
  try {
70709
- response = await fetch(zoneFileUrl(requestConfig.resolvedApiBaseUrl, domainId, flags["outbound-only"]), { headers: {
71805
+ response = await fetch(zoneFileUrl(requestConfig.resolvedApiBaseUrl, domainId, flags["outbound-only"], flags["inbound-only"]), { headers: {
70710
71806
  ...requestConfig.headers ?? {},
70711
71807
  ...auth.apiKey ? { authorization: `Bearer ${auth.apiKey}` } : {}
70712
71808
  } });
@@ -73607,8 +74703,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
73607
74703
  name: "Primitive Team",
73608
74704
  url: "https://primitive.dev"
73609
74705
  };
73610
- const SDK_VERSION_RANGE = "^3.0.0-preview.11";
73611
- const CLI_VERSION_RANGE = "^3.0.0-preview.11";
74706
+ const SDK_VERSION_RANGE = "^3.0.0-preview.13";
74707
+ const CLI_VERSION_RANGE = "^3.0.0-preview.13";
73612
74708
  const ESBUILD_VERSION_RANGE = "^0.27.0";
73613
74709
  function renderHandler() {
73614
74710
  return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
@@ -76983,12 +78079,27 @@ function describePermissions(grant) {
76983
78079
  const permissions = grant.permissions ?? [];
76984
78080
  return permissions.length > 0 ? permissions.join(", ") : "none";
76985
78081
  }
78082
+ /**
78083
+ * The notes the server gives on a key's grant on every read (address_warnings),
78084
+ * such as an address it reads in full beside its email rules. Read loosely:
78085
+ * an older server sends none.
78086
+ */
78087
+ function keyNotes(key) {
78088
+ const notes = key.address_warnings;
78089
+ if (!Array.isArray(notes)) return [];
78090
+ return notes.flatMap((note) => {
78091
+ const description = note?.description;
78092
+ return typeof description === "string" ? [description] : [];
78093
+ });
78094
+ }
76986
78095
  function formatKeyRows(rows, options = {}) {
76987
78096
  if (rows.length === 0) return options.includeRevoked ? "No API keys, revoked or not." : "No API keys. Revoked keys are left out; pass --include-revoked to list them too.";
76988
78097
  return rows.map((row) => {
76989
78098
  const suspended = row.suspended && row.status !== "revoked" ? " [suspended]" : "";
76990
78099
  const revoked = row.status === "revoked" ? ` [revoked${row.revoked_at ? ` ${row.revoked_at}` : ""}]` : "";
76991
- return `${row.id} ${row.key_prefix} ${row.name ?? "(unnamed)"} ${describePermissions(row.grant)} mail: ${describeMailScope(row.grant)} expires ${row.expires_at ?? "never"} last used ${row.last_used_at ?? "never"}${suspended}${revoked}`;
78100
+ const count = keyNotes(row).length;
78101
+ const notes = count > 0 ? ` [${count} ${count === 1 ? "note" : "notes"}: primitive keys get ${row.id}]` : "";
78102
+ return `${row.id} ${row.key_prefix} ${row.name ?? "(unnamed)"} ${describePermissions(row.grant)} mail: ${describeMailScope(row.grant)} expires ${row.expires_at ?? "never"} last used ${row.last_used_at ?? "never"}${suspended}${revoked}${notes}`;
76992
78103
  }).join("\n");
76993
78104
  }
76994
78105
  function formatKeyDetail(key) {
@@ -77013,6 +78124,11 @@ function formatKeyDetail(key) {
77013
78124
  lines.push("Permissions:");
77014
78125
  for (const permission of key.grant.permissions ?? []) lines.push(` ${permission}`);
77015
78126
  }
78127
+ const notes = keyNotes(key);
78128
+ if (notes.length > 0) {
78129
+ lines.push("Notes:");
78130
+ for (const note of notes) lines.push(` ${note.replace(/\r?\n/g, "\n ")}`);
78131
+ }
77016
78132
  return lines.join("\n");
77017
78133
  }
77018
78134
  const EVERY_MAILBOX_PRESETS = /* @__PURE__ */ new Set([
@@ -77515,7 +78631,7 @@ var KeysUpdateCommand = class KeysUpdateCommand extends Command {
77515
78631
 
77516
78632
  The change runs directly; if your organization requires approval, the command prints the approval link and takes --step-up-token, as for a new key. A key never holds more than you do.
77517
78633
 
77518
- With --json it prints the key as it is now, in the shape keys get prints, with changed, warnings and address_warnings alongside, notice when its permissions or mail changed, rules_removed when a change took rules off, and rules_not_on_key for rules asked to be taken off that it did not have. A permission or rule change that leaves the grant as it is, with no --name, --expires-at or --no-expiry, sends nothing and prints the key with changed: []. If the key cannot be read back after the change, key_error says why and only the change's fields are printed.`;
78634
+ With --json it prints the key as it is now, in the shape keys get prints, with changed, warnings and address_warnings alongside, notice when its permissions or mail changed, rules_added when a change put rules on it (each with granted_emails, how many emails the rule has already shared), rules_removed when a change took rules off, and rules_not_on_key for rules asked to be taken off that it did not have. A permission or rule change that leaves the grant as it is, with no --name, --expires-at or --no-expiry, sends nothing and prints the key with changed: []. If the key cannot be read back after the change, key_error says why and only the change's fields are printed.`;
77519
78635
  static summary = "Change an API key";
77520
78636
  static examples = [
77521
78637
  "<%= config.bin %> keys update 0f0e0d0c-0b0a-4908-8706-050403020100 --add mail:send,mail:reply",
@@ -77648,10 +78764,12 @@ var KeysUpdateCommand = class KeysUpdateCommand extends Command {
77648
78764
  client: auth.apiClient.client,
77649
78765
  path: { id: args.id }
77650
78766
  })) : null;
78767
+ const standingNotes = after?.ok ? after.data.address_warnings : void 0;
77651
78768
  if (flags.json) {
77652
78769
  this.log(JSON.stringify({
77653
78770
  ...after?.ok ? after.data : {},
77654
78771
  ...result.data,
78772
+ ...!grantChanged && Array.isArray(standingNotes) ? { address_warnings: standingNotes } : {},
77655
78773
  ...grantChanged ? { notice: HELD_REQUEST_NOTICE } : {},
77656
78774
  ...grantChanged && rulesRemoved.length > 0 ? { rules_removed: rulesRemoved } : {},
77657
78775
  ...rulesNotOnKey.length > 0 ? { rules_not_on_key: rulesNotOnKey } : {},
@@ -87540,6 +88658,26 @@ var ThreadsMutedCommand = class ThreadsMutedCommand extends Command {
87540
88658
  this.log(JSON.stringify(entries, null, 2));
87541
88659
  }
87542
88660
  };
88661
+ // src/oclif/commands/version.ts
88662
+ /** The facts `--version` prints, as fields. `node` is Node's version without its leading v. */
88663
+ function versionInfo(config) {
88664
+ return {
88665
+ version: config.version,
88666
+ platform: config.platform,
88667
+ arch: config.arch,
88668
+ node: process.versions.node
88669
+ };
88670
+ }
88671
+ var VersionCommand = class VersionCommand extends Command {
88672
+ static description = "Print the CLI's version, with the platform, architecture and Node version it runs on: the same line `primitive --version` prints. With --json, print them as fields. Needs no login and does not call the API.";
88673
+ static summary = "Print the CLI version";
88674
+ static examples = ["<%= config.bin %> version", "<%= config.bin %> version --json"];
88675
+ static flags = { json: JSON_OUTPUT_FLAG };
88676
+ async run() {
88677
+ const { flags } = await this.parse(VersionCommand);
88678
+ this.log(flags.json ? JSON.stringify(versionInfo(this.config), null, 2) : this.config.userAgent);
88679
+ }
88680
+ };
87543
88681
  // src/oclif/verification-extract.ts
87544
88682
  const CODE_KEYWORD = /\b(?:verification|verify|verifying|one[- ]?time|otp|passcode|pass code|security code|login code|log[- ]?in code|sign[- ]?in code|access code|auth(?:entication)? code|confirmation code|confirm|code|pin|2fa|two[- ]factor|launch code)\b/i;
87545
88683
  const NOT_A_CODE = /\b(?:order|invoice|receipt|total|subtotal|amount|price|paid|charged|phone|tel|call|fax|mobile|ref|reference|ticket|case|account|acct|customer|member|zip|postal|suite|ste|po box|tracking|transaction|txn|item|sku|qty|quantity|street|st|ave|avenue|blvd|road|rd|floor)\b|[#$€£¥]/i;
@@ -89248,6 +90386,7 @@ const COMMANDS = {
89248
90386
  signal: SignalCommand,
89249
90387
  doctor: DoctorCommand,
89250
90388
  billing: BillingCommand,
90389
+ version: VersionCommand,
89251
90390
  "machine:doctor": MachineDoctorCommand,
89252
90391
  "emails:latest": EmailsLatestCommand,
89253
90392
  "emails:attachment": EmailsAttachmentCommand,