primcli 1.63.0 → 1.65.0

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 { a as REPEAT_IDLE_MAX_MINUTES, d as parseRepeatTick, f as preparePresenceAliveEmail, g as isEmailReceivedEvent, i as REPEAT_EVERY_MAX_MINUTES, l as parseInteractionEnvelope$1, m as repeatStopCommand, o as REPEAT_MAX_SENDS_MAX, p as prepareSignalEmail, s as buildRepeatStopBody, u as parsePresenceEnvelope, v as parseWebhookEvent, y as parseFromHeader } from "../repeats-x4QM2yoG-DTpqXyRo.js";
1
+ import { a as REPEAT_IDLE_MAX_MINUTES, b as parseWebhookEvent, d as parseRepeatTick, f as preparePresenceAliveEmail, i as REPEAT_EVERY_MAX_MINUTES, l as parseInteractionEnvelope$1, m as repeatStopCommand, o as REPEAT_MAX_SENDS_MAX, p as prepareSignalEmail, s as buildRepeatStopBody, u as parsePresenceEnvelope, v as isEmailReceivedEvent, x as parseFromHeader } from "../repeats-BL7ZC-_6-Dzangtkf.js";
2
2
  import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, F as agentProfilesDirectory, H as requireDefaultLoginProfile, M as AgentConnectionSetupError, N as agentProfileDirectory, O as loadActiveChatState, P as agentProfileName, R as loadConnectedAgentProfile, T as saveSignupCredentials, W as PrimitiveApiClient, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_PROFILE_ENV, k as loadChatConversationByLocalId, 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 saveCliCredentials, x as loadCliCredentials, y as detectPrimitiveKeyEnvMisname } from "../cli-config-DMszFbGn.js";
3
- import { $ as apiResultHttpStatus, $n as replyToEmail, $t as completeWebhookEvent, A as readThreadContext, An as listAgentNetworks, At as formatAlreadySentNotice, B as pendingMailPath, Bn as listRoutes, Bt as expectedAutomatedVerdicts, C as queryUsesAwaiting, Cn as getMemory, Cr as verifyAgentSignup, Ct as cliInvocation, D as displayAddress, Dn as getThread, Dt as identitySuggestions, E as describeWake, En as getSentEmail, Et as generatedAgentName, F as wakeReadCommand, Fn as listEmails, Ft as AUTOMATED_VALUES, G as interactionNextActions, Gn as pollCliLogin, H as recordPendingMail, Hn as listWakeAuthorizations, Ht as isAutomatedRejectedError, I as wakeRecipientField, In as listEndpoints, It as AutomatedFilterUnsupportedError, J as replyInteractionWarning, Jt as refreshStoredCliCredentials, K as readEmailInteraction, Kn as pullWebhookEvent, Kt as createAuthenticatedCliApiClient, L as wakeRelationship, Ln as listFunctionLogs, Lt as assertAutomatedVerdict, M as serverMuted, Mn as listDefaultNetworkAgents, Mt as withFlagSuggestion, N as serverRelationship, Nn as listDefaultNetworkMembers, Nt as AUTOMATED_FILTER_UNSUPPORTED_CODE, O as formatMailWakeLine, On as getWakeSchedule, Ot as loadSkillLine, Pn as listDomains, Pt as AUTOMATED_FLAG_DESCRIPTION, Q as SEND_OUTCOME_HELP, Qn as reorderRoutes, Qt as cliLogout, R as clearConsumedPendingMail, Rn as listFunctions, Rt as automatedRejectedError, S as isAwaitingRejectedError, Sn as getInboxStatus, Sr as verifyAgentClaim, T as bareAddress, Tn as getRepeatingSend, U as removePendingMail, Un as listWakeDispatches, Ut as queryUsesAutomated, V as readPendingMail, Vn as listSentEmails, Vt as hasAutomatedVerdict, W as interactionHumanLine, Wn as listWakeSchedules, Wt as ConnectedAgentKeyRequiredError, X as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, Xn as redeemCreditCode, Xt as addDefaultNetworkMember, Yt as resolveCliApiRequestConfig, Z as MAX_SEND_WAIT_TIMEOUT_MS, Zn as removeDefaultNetworkMember, Zt as checkDefaultNetworkContactAdmission, _ as awaitingRejectedError, _n as getDefaultNetworkAgent, _r as updateFunction, _t as sendWaitClampNotice, a as extractErrorCode, ar as sendEmail, at as deriveSendIdempotencyKey, b as formatRepliesCell, bn as getFunctionRouting, br as updateWakeAuthorization, bt as successfulSendOutcome, c as readTextFileFlag, cn as deleteRoute, cr as setMemory, ct as formatDeletedEarlierSendNotice, d as writeErrorWithHints, dr as startAgentSignup, dt as formatSendFailureSummary, en as createEndpoint, er as resendAgentSignupVerification, et as assertValidIdempotencyKey, f as AWAITING_FLAG_DESCRIPTION, fn as getAccount, fr as startCliLogin, ft as formatSendRecordFailureSummary, g as assertAwaitingFilterRows, gn as getCreditBalance, gr as updateDefaultNetworkMember, gt as sendOutcomeExitCode, h as ReplyStateUnsupportedError, hn as getConversation, hr as unsetFunctionRoute, ht as sendIdentityFields, i as createOperationCommand, in as createWakeSchedule, ir as semanticSearch, it as classifySendError, j as sentInThread, jt as writeIdempotentReplayBannerIfReplay, k as latestOwnOutbound, kn as listAgentContacts, kt as currentMailSessionKey, l as runWithTiming, ln as deleteWakeAuthorization, lr as simulateRoute, lt as formatPriorRepliesCheckSkipped, m as REPLY_STATE_UNSUPPORTED_CODE, mr as testFunction, mt as reportSendCommandResult, n as API_ERROR_CODES, nn as createRoute, nr as searchEmails, nt as buildThrownSendFailureEnvelope, o as extractErrorPayload, or as setFunctionRoute, ot as excludeSignalReplies, p as AWAITING_VALUES, pn as getAgentContactPolicy, pr as stopRepeatFromEmail, pt as priorRepliesThatWentOut, q as replyExpectation, qt as createCliApiClient, r as TIME_FLAG_DESCRIPTION, rn as createWakeAuthorization, rr as searchMemories, rt as checkPriorReplies, s as formatErrorPayload, sn as deleteMemory, sr as setFunctionSecret, st as followUpCommandPrefix, t as API_BASE_URL_FLAG_DESCRIPTION, tn as createFunction, tr as runWakeSchedule, tt as buildFollowUpCommand, u as surfaceUnauthorizedHint, un as deleteWakeSchedule, ur as startAgentClaim, ut as formatPriorRepliesWarning, v as ensureSearchReportsReplyState, vn as getEmail, vr as updateRepeatingSend, vt as sentHistoryWindowStart, w as explainPendingMailNotFound, wn as getOrgRoutingTopology, wt as connectNextSteps, x as hasReplyState, xn as getFunctionTestRunTrace, xr as updateWakeSchedule, xt as thrownErrorExitCode, y as formatAwaitingCell, yn as getFunction, yr as updateRoute, yt as serializeErrorPayload, z as clearReadPendingMail, zn as listRepeatingSends, zt as ensureSearchReportsAutomated } from "../api-command-CDjvGDQS.js";
3
+ import { $ as apiResultHttpStatus, $n as replyToEmail, $t as completeWebhookEvent, A as readThreadContext, An as listAgentNetworks, At as formatAlreadySentNotice, B as pendingMailPath, Bn as listRoutes, Bt as expectedAutomatedVerdicts, C as queryUsesAwaiting, Cn as getMemory, Cr as verifyAgentSignup, Ct as cliInvocation, D as displayAddress, Dn as getThread, Dt as identitySuggestions, E as describeWake, En as getSentEmail, Et as generatedAgentName, F as wakeReadCommand, Fn as listEmails, Ft as AUTOMATED_VALUES, G as interactionNextActions, Gn as pollCliLogin, H as recordPendingMail, Hn as listWakeAuthorizations, Ht as isAutomatedRejectedError, I as wakeRecipientField, In as listEndpoints, It as AutomatedFilterUnsupportedError, J as replyInteractionWarning, Jt as refreshStoredCliCredentials, K as readEmailInteraction, Kn as pullWebhookEvent, Kt as createAuthenticatedCliApiClient, L as wakeRelationship, Ln as listFunctionLogs, Lt as assertAutomatedVerdict, M as serverMuted, Mn as listDefaultNetworkAgents, Mt as withFlagSuggestion, N as serverRelationship, Nn as listDefaultNetworkMembers, Nt as AUTOMATED_FILTER_UNSUPPORTED_CODE, O as formatMailWakeLine, On as getWakeSchedule, Ot as loadSkillLine, Pn as listDomains, Pt as AUTOMATED_FLAG_DESCRIPTION, Q as SEND_OUTCOME_HELP, Qn as reorderRoutes, Qt as cliLogout, R as clearConsumedPendingMail, Rn as listFunctions, Rt as automatedRejectedError, S as isAwaitingRejectedError, Sn as getInboxStatus, Sr as verifyAgentClaim, T as bareAddress, Tn as getRepeatingSend, U as removePendingMail, Un as listWakeDispatches, Ut as queryUsesAutomated, V as readPendingMail, Vn as listSentEmails, Vt as hasAutomatedVerdict, W as interactionHumanLine, Wn as listWakeSchedules, Wt as ConnectedAgentKeyRequiredError, X as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, Xn as redeemCreditCode, Xt as addDefaultNetworkMember, Yt as resolveCliApiRequestConfig, Z as MAX_SEND_WAIT_TIMEOUT_MS, Zn as removeDefaultNetworkMember, Zt as checkDefaultNetworkContactAdmission, _ as awaitingRejectedError, _n as getDefaultNetworkAgent, _r as updateFunction, _t as sendWaitClampNotice, a as extractErrorCode, ar as sendEmail, at as deriveSendIdempotencyKey, b as formatRepliesCell, bn as getFunctionRouting, br as updateWakeAuthorization, bt as successfulSendOutcome, c as readTextFileFlag, cn as deleteRoute, cr as setMemory, ct as formatDeletedEarlierSendNotice, d as writeErrorWithHints, dr as startAgentSignup, dt as formatSendFailureSummary, en as createEndpoint, er as resendAgentSignupVerification, et as assertValidIdempotencyKey, f as AWAITING_FLAG_DESCRIPTION, fn as getAccount, fr as startCliLogin, ft as formatSendRecordFailureSummary, g as assertAwaitingFilterRows, gn as getCreditBalance, gr as updateDefaultNetworkMember, gt as sendOutcomeExitCode, h as ReplyStateUnsupportedError, hn as getConversation, hr as unsetFunctionRoute, ht as sendIdentityFields, i as createOperationCommand, in as createWakeSchedule, ir as semanticSearch, it as classifySendError, j as sentInThread, jt as writeIdempotentReplayBannerIfReplay, k as latestOwnOutbound, kn as listAgentContacts, kt as currentMailSessionKey, l as runWithTiming, ln as deleteWakeAuthorization, lr as simulateRoute, lt as formatPriorRepliesCheckSkipped, m as REPLY_STATE_UNSUPPORTED_CODE, mr as testFunction, mt as reportSendCommandResult, n as API_ERROR_CODES, nn as createRoute, nr as searchEmails, nt as buildThrownSendFailureEnvelope, o as extractErrorPayload, or as setFunctionRoute, ot as excludeSignalReplies, p as AWAITING_VALUES, pn as getAgentContactPolicy, pr as stopRepeatFromEmail, pt as priorRepliesThatWentOut, q as replyExpectation, qt as createCliApiClient, r as TIME_FLAG_DESCRIPTION, rn as createWakeAuthorization, rr as searchMemories, rt as checkPriorReplies, s as formatErrorPayload, sn as deleteMemory, sr as setFunctionSecret, st as followUpCommandPrefix, t as API_BASE_URL_FLAG_DESCRIPTION, tn as createFunction, tr as runWakeSchedule, tt as buildFollowUpCommand, u as surfaceUnauthorizedHint, un as deleteWakeSchedule, ur as startAgentClaim, ut as formatPriorRepliesWarning, v as ensureSearchReportsReplyState, vn as getEmail, vr as updateRepeatingSend, vt as sentHistoryWindowStart, w as explainPendingMailNotFound, wn as getOrgRoutingTopology, wt as connectNextSteps, x as hasReplyState, xn as getFunctionTestRunTrace, xr as updateWakeSchedule, xt as thrownErrorExitCode, y as formatAwaitingCell, yn as getFunction, yr as updateRoute, yt as serializeErrorPayload, z as clearReadPendingMail, zn as listRepeatingSends, zt as ensureSearchReportsAutomated } from "../api-command-0u5Qd_a-.js";
4
4
  import { S as resolveListenSubscription, _ 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, x as normalizeListenOrigin, y as listenIdentity } from "../shared-mail-files-MwlVE90c.js";
5
- import { _ as detectAgentRuntime, a as NotificationRetryError, b as installedVersion, c as readConversationStatusContent, d as defaultSessionSocket, f as NativeSessionDisconnectedError, g as connectSkillTarget, h as NotificationOutcomeUnknownError, i as notificationReceiptPage, l as SESSION_UUID$1, m as NativeSessionNotLoadedError, n as notificationSenders, o as isRoutineNotificationContent, p as NativeSessionError, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, v as installConnectSkill, x as readBundledConnectSkill, y as installedConnectSkillFile } from "../notify-session-7JzG7GNX.js";
6
- import { $ as describeSessionHookItem, A as evaluateContactPolicy, B as agentInvitationHash, C as RECEIVER_MODES, D as NETWORK_ADMISSION_PENDING_RETRY_MS, E as ContactPolicyReadRetryError, F as ContactsApiError, G as saveConnectionName, H as readAgentInvitation, I as contactAddress, J as recordHookMailCheck, K as savedReceiver, L as contactAgentAddress, M as AddressNotesApiError, N as addressNoteTarget, O as NetworkAdmissionPendingError, P as runAddressNotesRequest, Q as describeSessionHookChange, R as runContactRequest, S as runAgentConnect, T as verificationReplySubmitted, U as receiverStatusCommand, V as connectAgent, W as refusalAfterReplacement, X as claudeConfigDir, Y as boundSessionProfiles, Z as currentCliLocation, _ as disconnectAgent, _t as withOwnerMemberAddress, a as endSession, at as readClaudeSettings, b as invitationProfileName, c as pendingSessionDisconnects, ct as writeClaudeSettings, d as registerSession, dt as jsonIndentation, et as ensureHookLauncher, f as retryPendingDisconnects, ft as readManagedFile, g as AgentDisconnectError, gt as refreshOwnerMemberAddressPeriodically, h as enrollmentResumes, ht as readHookLauncherWarning, i as connectedProfilesForSession, it as inspectClaudeHooks, j as canonicalContactSelector, k as createNotificationContactPolicy, l as psProcessInfo, lt as MachineFileError, m as enrollAgent, mt as writeManagedFile, n as OMP_MAIN, nt as findOnPath, o as headlessClaudeRun, ot as repairClaudeHooks, p as runDetached, pt as upsertManagedBlock, q as installClaudeWakeHook, r as agentConnectionState, rt as hasMachineSessionHooks, st as samePath, t as MACHINE_RUNTIMES, tt as ephemeralCliReason, u as readClaudeHookInput, ut as inspectManagedBlock, v as connectWarnings, w as externalReceivingState, x as pollCheckCommand, y as defaultAgentProfileName, z as agentConnectionStatus } from "../machine-session-NjXIXTRX.js";
7
- import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply, o as presenceDisposition, r as isScopedChatReply, s as readPresenceProjection, t as isConnectedChatCredential } from "../scoped-chat-BvkUTggT.js";
5
+ import { _ as detectAgentRuntime, a as NotificationRetryError, b as installedVersion, c as readConversationStatusContent, d as defaultSessionSocket, f as NativeSessionDisconnectedError, g as connectSkillTarget, h as NotificationOutcomeUnknownError, i as notificationReceiptPage, l as SESSION_UUID$1, m as NativeSessionNotLoadedError, n as notificationSenders, o as isRoutineNotificationContent, p as NativeSessionError, r as openSessionNotifications, s as notificationPartReader, t as notificationScope, v as installConnectSkill, x as readBundledConnectSkill, y as installedConnectSkillFile } from "../notify-session-BaS84f3y.js";
6
+ import { $ as boundSessionProfiles, A as AgentDisconnectError, B as runContactRequest, C as verificationReplySubmitted, D as createNotificationContactPolicy, E as NetworkAdmissionPendingError, F as addressNoteTarget, G as receiverStatusCommand, H as agentInvitationHash, I as runAddressNotesRequest, J as savedReceiver, K as refusalAfterReplacement, L as ContactsApiError, M as clearRevokedAddresses, N as disconnectAgent, O as evaluateContactPolicy, P as AddressNotesApiError, Q as recordHookMailCheck, R as contactAddress, S as externalReceivingState, T as NETWORK_ADMISSION_PENDING_RETRY_MS, U as connectAgent, V as agentConnectionStatus, W as readAgentInvitation, X as releaseRegistrationsForProfile, Y as MACHINE_RUNTIMES, Z as installClaudeWakeHook, _ as defaultAgentProfileName, _t as upsertManagedBlock, a as endSession, at as ephemeralCliReason, b as runAgentConnect, bt as refreshOwnerMemberAddressPeriodically, c as pendingSessionDisconnects, ct as inspectClaudeHooks, d as registerSession, dt as samePath, et as claudeConfigDir, f as retryPendingDisconnects, ft as writeClaudeSettings, g as connectWarnings, gt as readManagedFile, h as enrollmentResumes, ht as jsonIndentation, i as connectedProfilesForSession, it as ensureHookLauncher, j as claimOverRevokedSetup, k as canonicalContactSelector, l as psProcessInfo, lt as readClaudeSettings, m as enrollAgent, mt as inspectManagedBlock, n as OMP_MAIN, nt as describeSessionHookChange, o as headlessClaudeRun, ot as findOnPath, p as runDetached, pt as MachineFileError, q as saveConnectionName, r as agentConnectionState, rt as describeSessionHookItem, st as hasMachineSessionHooks, t as HOOK_END_GRACE_MS, tt as currentCliLocation, u as readClaudeHookInput, ut as repairClaudeHooks, v as invitationProfileName, vt as writeManagedFile, w as ContactPolicyReadRetryError, x as RECEIVER_MODES, xt as withOwnerMemberAddress, y as pollCheckCommand, yt as readHookLauncherWarning, z as contactAgentAddress } from "../machine-session-Bmv7ZOQh.js";
7
+ import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply, o as presenceDisposition, r as isScopedChatReply, s as readPresenceProjection, t as isConnectedChatCredential } from "../scoped-chat-BW041zEY.js";
8
8
  import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background--0peztKE.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-50rw5Sh-.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-CwIFkT9e.js";
10
10
  import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, readWorkingLease, restoreAutoWorking } from "./auto-signals.js";
11
11
  import { t as guardJsonOutputCommands } from "../json-output-CEskSQJi.js";
12
12
  import { Args, Command, Errors, Flags, ux } from "@oclif/core";
@@ -1651,7 +1651,8 @@ const openapiDocument = {
1651
1651
  },
1652
1652
  "responses": {
1653
1653
  "200": {
1654
- "description": "Outbound relay result",
1654
+ "description": "Outbound relay result. When the request was answered with an\nexisting send instead of making a new one, the response carries\n`Idempotent-Replayed: true` and `data.idempotency`, and nothing\nwas sent for this request.\n",
1655
+ "headers": { "Idempotent-Replayed": { "$ref": "#/components/headers/IdempotentReplayed" } },
1655
1656
  "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
1656
1657
  "type": "object",
1657
1658
  "properties": { "data": { "$ref": "#/components/schemas/SendMailResult" } }
@@ -2409,7 +2410,7 @@ const openapiDocument = {
2409
2410
  "/webhooks/deliveries": { "get": {
2410
2411
  "operationId": "listDeliveries",
2411
2412
  "summary": "List webhook deliveries",
2412
- "description": "Returns a paginated list of webhook delivery attempts. Each delivery\nincludes a nested `email` object with sender, recipient, and subject.\n",
2413
+ "description": "Returns a paginated list of webhook delivery attempts, newest first.\nA delivery of an inbound email includes a nested `email` object with\nsender, recipient, and subject. A delivery of an opt-in\n`sent_email.*` event has `email_id` and `email` set to null and\n`sent_email_id` set to the send it is about. Filter by `event_type`\nand `sent_email_id` to find the deliveries of one send.\n",
2413
2414
  "tags": ["Webhook Deliveries"],
2414
2415
  "parameters": [
2415
2416
  { "$ref": "#/components/parameters/Cursor" },
@@ -2423,6 +2424,25 @@ const openapiDocument = {
2423
2424
  },
2424
2425
  "description": "Filter by email ID"
2425
2426
  },
2427
+ {
2428
+ "name": "event_type",
2429
+ "in": "query",
2430
+ "schema": {
2431
+ "type": "string",
2432
+ "minLength": 1,
2433
+ "maxLength": 200
2434
+ },
2435
+ "description": "Only deliveries of this event type, for example `sent_email.failed`. Deliveries recorded before event types existed have none and never match."
2436
+ },
2437
+ {
2438
+ "name": "sent_email_id",
2439
+ "in": "query",
2440
+ "schema": {
2441
+ "type": "string",
2442
+ "format": "uuid"
2443
+ },
2444
+ "description": "Only deliveries of `sent_email.*` events about this sent email."
2445
+ },
2426
2446
  {
2427
2447
  "name": "status",
2428
2448
  "in": "query",
@@ -2432,7 +2452,8 @@ const openapiDocument = {
2432
2452
  "pending",
2433
2453
  "delivered",
2434
2454
  "header_confirmed",
2435
- "failed"
2455
+ "failed",
2456
+ "skipped_by_rules"
2436
2457
  ]
2437
2458
  },
2438
2459
  "description": "Filter by delivery status"
@@ -2478,14 +2499,14 @@ const openapiDocument = {
2478
2499
  "required": true,
2479
2500
  "schema": {
2480
2501
  "type": "string",
2481
- "pattern": "^\\d+$"
2502
+ "pattern": "^(\\d+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"
2482
2503
  },
2483
- "description": "Delivery ID (numeric)"
2504
+ "description": "Delivery ID, as `id` in the deliveries list: a numeric string or a UUID."
2484
2505
  }],
2485
2506
  "post": {
2486
2507
  "operationId": "replayDelivery",
2487
2508
  "summary": "Replay a webhook delivery",
2488
- "description": "Re-sends the stored webhook payload from a previous delivery attempt.\nIf the original endpoint is still active, it is targeted. If the\noriginal endpoint was deleted, the oldest active endpoint is used.\nDeactivated endpoints cannot be replayed to. Rate limited per-org,\nsharing an org-wide budget with email replays.\n",
2509
+ "description": "Re-sends a previous delivery to its original endpoint. If that\nendpoint was deleted or deactivated, the replay is rejected.\nSupports inbound email deliveries and `sent_email.*` deliveries.\nAn inbound email delivery is replayed with its original stored\npayload and event id. A `sent_email.*` delivery is replayed with the\nsame event id but a payload rebuilt from the send as it is now; it\nis rejected if the send was deleted or the endpoint is no longer an\nactive http endpoint. Rate limited per-org, sharing an org-wide\nbudget with email replays.\n",
2489
2510
  "tags": ["Webhook Deliveries"],
2490
2511
  "responses": {
2491
2512
  "200": {
@@ -2555,7 +2576,8 @@ const openapiDocument = {
2555
2576
  },
2556
2577
  "responses": {
2557
2578
  "200": {
2558
- "description": "Outbound relay result",
2579
+ "description": "Outbound relay result. When the request was answered with an\nexisting send instead of making a new one, the response carries\n`Idempotent-Replayed: true` and `data.idempotency`, and nothing\nwas sent for this request.\n",
2580
+ "headers": { "Idempotent-Replayed": { "$ref": "#/components/headers/IdempotentReplayed" } },
2559
2581
  "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
2560
2582
  "type": "object",
2561
2583
  "properties": { "data": { "$ref": "#/components/schemas/SendMailResult" } }
@@ -8275,6 +8297,20 @@ const openapiDocument = {
8275
8297
  }
8276
8298
  }
8277
8299
  },
8300
+ "headers": {
8301
+ "IdempotentReplayed": {
8302
+ "description": "Set to `true` when the request was answered with an existing send\ninstead of making a new one. Absent on a request that sent. On a\nbatch it means at least one message replayed; see\n`Idempotent-Replayed-Count`.\n",
8303
+ "schema": { "type": "string" },
8304
+ "example": "true"
8305
+ },
8306
+ "IdempotentReplayedCount": {
8307
+ "description": "Batch sends only: how many messages were answered with an existing\nsend instead of being sent. Absent when every message sent.\n",
8308
+ "schema": {
8309
+ "type": "integer",
8310
+ "minimum": 1
8311
+ }
8312
+ }
8313
+ },
8278
8314
  "responses": {
8279
8315
  "AttachmentPart": {
8280
8316
  "description": "Original attachment bytes; never a JSON envelope or a base64 string",
@@ -12806,6 +12842,11 @@ const openapiDocument = {
12806
12842
  "type": "string",
12807
12843
  "description": "Bare email address parsed from `to_header`."
12808
12844
  },
12845
+ "recipient_count": {
12846
+ "type": ["integer", "null"],
12847
+ "minimum": 0,
12848
+ "description": "Every recipient of the send, To, Cc and Bcc together, each address counted once: the size of `to_addresses`, `cc` and `bcc` combined without duplicates. On a mail relay send `bcc` lists every hidden recipient. Null when the request uses an address-bound agent connection key whose address is not the sender, because the count would reveal Bcc recipients. The `sent_email` object of the `sent_email.*` webhook events carries the same field."
12849
+ },
12809
12850
  "subject": { "type": "string" },
12810
12851
  "body_size_bytes": {
12811
12852
  "type": "integer",
@@ -13724,6 +13765,46 @@ const openapiDocument = {
13724
13765
  "reply_sent_email_id"
13725
13766
  ]
13726
13767
  },
13768
+ "SendMailIdempotencyReplay": {
13769
+ "type": "object",
13770
+ "description": "Present only when the request was answered with an existing send\ninstead of making a new one (`idempotent_replay: true`). Nothing\nwas sent for this request. Says how the idempotency key was\nderived and which send the request collapsed onto. On the replay\nof a stored failure the same object is under `error.details`.\n",
13771
+ "properties": {
13772
+ "replayed": {
13773
+ "type": "boolean",
13774
+ "description": "Always `true`. The object is absent on a request that made a send of its own."
13775
+ },
13776
+ "key_source": {
13777
+ "type": "string",
13778
+ "enum": [
13779
+ "explicit",
13780
+ "auto_content",
13781
+ "function_trigger"
13782
+ ],
13783
+ "description": "`explicit`: the request carried an `Idempotency-Key` header.\n`auto_content`: no header was sent, so the key was derived\nfrom the canonical request content (recipients included) and\na fixed 5-minute window; an identical send inside the same\nwindow was not sent again. `function_trigger`: no header on a\nsend made by a Function, so the key was derived from the\ncontent, the Function and the inbound email (or event) that\ninvoked it.\n"
13784
+ },
13785
+ "original_sent_email_id": {
13786
+ "type": "string",
13787
+ "format": "uuid",
13788
+ "description": "The send this request collapsed onto. Same value as `id`."
13789
+ },
13790
+ "original_created_at": {
13791
+ "type": ["string", "null"],
13792
+ "format": "date-time",
13793
+ "description": "When that send was created."
13794
+ },
13795
+ "window_seconds": {
13796
+ "type": ["integer", "null"],
13797
+ "description": "Length of the content window (300) when an `auto_content`\nkey matched on content. Null for `explicit` and\n`function_trigger` keys, which have no window, and for a\nkeyless reply matched because its parent already has a reply\n(`dedup_reason: parent_already_replied`).\n"
13798
+ }
13799
+ },
13800
+ "required": [
13801
+ "replayed",
13802
+ "key_source",
13803
+ "original_sent_email_id",
13804
+ "original_created_at",
13805
+ "window_seconds"
13806
+ ]
13807
+ },
13727
13808
  "SendMailResult": {
13728
13809
  "type": "object",
13729
13810
  "properties": {
@@ -13780,6 +13861,11 @@ const openapiDocument = {
13780
13861
  "type": "boolean",
13781
13862
  "description": "True when the response replays a previously-recorded send\nkeyed by `client_idempotency_key` (same key, same canonical\npayload). False on a fresh send and on gate-denied\nresponses. Lets callers branch on cache state without\ndiffing fields.\n"
13782
13863
  },
13864
+ "dedup_reason": {
13865
+ "type": ["string", "null"],
13866
+ "description": "Why the response was a replay; null or absent on a fresh\nsend. `content_hash_match`: the idempotency key matched an\nearlier send. `parent_already_replied`: a reply sent without\nan `Idempotency-Key` header was matched to the reply its\nparent email already has. Pass an `Idempotency-Key` to send\nanother reply to the same email.\n"
13867
+ },
13868
+ "idempotency": { "$ref": "#/components/schemas/SendMailIdempotencyReplay" },
13783
13869
  "scheduled_at": {
13784
13870
  "type": "string",
13785
13871
  "format": "date-time",
@@ -14337,7 +14423,8 @@ const openapiDocument = {
14337
14423
  "items": {
14338
14424
  "type": "string",
14339
14425
  "minLength": 1
14340
- }
14426
+ },
14427
+ "description": "Event types this endpoint subscribes to, matched by exact string. Omitted means every event type except the opt-in `sent_email.*` events (`sent_email.accepted`, `sent_email.delivered`, `sent_email.failed`, `sent_email.completed`), which an endpoint receives only when it lists them, and only for sends made after it first listed one. An endpoint that lists `sent_email.*` events and also handles inbound mail must keep its inbound event types (such as `email.received`) in the list. An empty array is rejected; omit the field instead."
14341
14428
  } }
14342
14429
  },
14343
14430
  "created_at": {
@@ -14460,7 +14547,8 @@ const openapiDocument = {
14460
14547
  "items": {
14461
14548
  "type": "string",
14462
14549
  "minLength": 1
14463
- }
14550
+ },
14551
+ "description": "Event types this endpoint subscribes to, matched by exact string. Omitted means every event type except the opt-in `sent_email.*` events (`sent_email.accepted`, `sent_email.delivered`, `sent_email.failed`, `sent_email.completed`), which an endpoint receives only when it lists them, and only for sends made after it first listed one. An endpoint that lists `sent_email.*` events and also handles inbound mail must keep its inbound event types (such as `email.received`) in the list. An empty array is rejected; omit the field instead."
14464
14552
  } }
14465
14553
  },
14466
14554
  "is_route_target": {
@@ -14498,7 +14586,8 @@ const openapiDocument = {
14498
14586
  "items": {
14499
14587
  "type": "string",
14500
14588
  "minLength": 1
14501
- }
14589
+ },
14590
+ "description": "Event types this endpoint subscribes to, matched by exact string. Omitted means every event type except the opt-in `sent_email.*` events (`sent_email.accepted`, `sent_email.delivered`, `sent_email.failed`, `sent_email.completed`), which an endpoint receives only when it lists them, and only for sends made after it first listed one. An endpoint that lists `sent_email.*` events and also handles inbound mail must keep its inbound event types (such as `email.received`) in the list. An empty array is rejected; omit the field instead."
14502
14591
  } }
14503
14592
  }
14504
14593
  },
@@ -15182,11 +15271,21 @@ const openapiDocument = {
15182
15271
  "properties": {
15183
15272
  "id": {
15184
15273
  "type": "string",
15185
- "description": "Delivery ID (numeric string)"
15274
+ "description": "Delivery ID: a numeric string or a UUID. Pass it to the replay operation as is."
15186
15275
  },
15187
15276
  "email_id": {
15188
- "type": "string",
15189
- "format": "uuid"
15277
+ "type": ["string", "null"],
15278
+ "format": "uuid",
15279
+ "description": "The inbound email this delivery is about. Null for deliveries that are not about a received email, such as `sent_email.*` events."
15280
+ },
15281
+ "event_type": {
15282
+ "type": ["string", "null"],
15283
+ "description": "The delivered event type, for example `email.received` or `sent_email.delivered`. Null for deliveries recorded before event types existed."
15284
+ },
15285
+ "sent_email_id": {
15286
+ "type": ["string", "null"],
15287
+ "format": "uuid",
15288
+ "description": "The sent email a `sent_email.*` delivery is about. Null for every other delivery."
15190
15289
  },
15191
15290
  "org_id": {
15192
15291
  "type": "string",
@@ -15196,19 +15295,27 @@ const openapiDocument = {
15196
15295
  "type": "string",
15197
15296
  "format": "uuid"
15198
15297
  },
15199
- "endpoint_url": { "type": "string" },
15298
+ "endpoint_url": {
15299
+ "type": ["string", "null"],
15300
+ "description": "The endpoint's URL. For a function-backed endpoint, an opaque `function://<id>` identifier rather than a callable URL."
15301
+ },
15200
15302
  "status": {
15201
15303
  "type": "string",
15202
15304
  "enum": [
15203
15305
  "pending",
15204
15306
  "delivered",
15205
15307
  "header_confirmed",
15206
- "failed"
15308
+ "failed",
15309
+ "skipped_by_rules"
15207
15310
  ]
15208
15311
  },
15209
15312
  "attempt_count": { "type": "integer" },
15210
15313
  "duration_ms": { "type": ["integer", "null"] },
15211
15314
  "last_error": { "type": ["string", "null"] },
15315
+ "last_error_code": {
15316
+ "type": ["string", "null"],
15317
+ "description": "A stable code for the last failure, for example `http_500`."
15318
+ },
15212
15319
  "created_at": {
15213
15320
  "type": "string",
15214
15321
  "format": "date-time"
@@ -15221,10 +15328,11 @@ const openapiDocument = {
15221
15328
  "type": ["object", "null"],
15222
15329
  "properties": {
15223
15330
  "sender": { "type": "string" },
15224
- "recipient": { "type": "string" },
15331
+ "recipient": { "type": ["string", "null"] },
15225
15332
  "subject": { "type": ["string", "null"] }
15226
15333
  },
15227
- "required": ["sender", "recipient"]
15334
+ "required": ["sender", "recipient"],
15335
+ "description": "Null for deliveries that are not about a received email."
15228
15336
  }
15229
15337
  },
15230
15338
  "required": [
@@ -24736,7 +24844,8 @@ const operationManifest = [
24736
24844
  "items": {
24737
24845
  "type": "string",
24738
24846
  "minLength": 1
24739
- }
24847
+ },
24848
+ "description": "Event types this endpoint subscribes to, matched by exact string. Omitted means every event type except the opt-in `sent_email.*` events (`sent_email.accepted`, `sent_email.delivered`, `sent_email.failed`, `sent_email.completed`), which an endpoint receives only when it lists them, and only for sends made after it first listed one. An endpoint that lists `sent_email.*` events and also handles inbound mail must keep its inbound event types (such as `email.received`) in the list. An empty array is rejected; omit the field instead."
24740
24849
  } }
24741
24850
  },
24742
24851
  "is_route_target": {
@@ -24808,7 +24917,8 @@ const operationManifest = [
24808
24917
  "items": {
24809
24918
  "type": "string",
24810
24919
  "minLength": 1
24811
- }
24920
+ },
24921
+ "description": "Event types this endpoint subscribes to, matched by exact string. Omitted means every event type except the opt-in `sent_email.*` events (`sent_email.accepted`, `sent_email.delivered`, `sent_email.failed`, `sent_email.completed`), which an endpoint receives only when it lists them, and only for sends made after it first listed one. An endpoint that lists `sent_email.*` events and also handles inbound mail must keep its inbound event types (such as `email.received`) in the list. An empty array is rejected; omit the field instead."
24812
24922
  } }
24813
24923
  },
24814
24924
  "created_at": {
@@ -24988,7 +25098,8 @@ const operationManifest = [
24988
25098
  "items": {
24989
25099
  "type": "string",
24990
25100
  "minLength": 1
24991
- }
25101
+ },
25102
+ "description": "Event types this endpoint subscribes to, matched by exact string. Omitted means every event type except the opt-in `sent_email.*` events (`sent_email.accepted`, `sent_email.delivered`, `sent_email.failed`, `sent_email.completed`), which an endpoint receives only when it lists them, and only for sends made after it first listed one. An endpoint that lists `sent_email.*` events and also handles inbound mail must keep its inbound event types (such as `email.received`) in the list. An empty array is rejected; omit the field instead."
24992
25103
  } }
24993
25104
  },
24994
25105
  "created_at": {
@@ -25394,7 +25505,8 @@ const operationManifest = [
25394
25505
  "items": {
25395
25506
  "type": "string",
25396
25507
  "minLength": 1
25397
- }
25508
+ },
25509
+ "description": "Event types this endpoint subscribes to, matched by exact string. Omitted means every event type except the opt-in `sent_email.*` events (`sent_email.accepted`, `sent_email.delivered`, `sent_email.failed`, `sent_email.completed`), which an endpoint receives only when it lists them, and only for sends made after it first listed one. An endpoint that lists `sent_email.*` events and also handles inbound mail must keep its inbound event types (such as `email.received`) in the list. An empty array is rejected; omit the field instead."
25398
25510
  } }
25399
25511
  }
25400
25512
  },
@@ -25458,7 +25570,8 @@ const operationManifest = [
25458
25570
  "items": {
25459
25571
  "type": "string",
25460
25572
  "minLength": 1
25461
- }
25573
+ },
25574
+ "description": "Event types this endpoint subscribes to, matched by exact string. Omitted means every event type except the opt-in `sent_email.*` events (`sent_email.accepted`, `sent_email.delivered`, `sent_email.failed`, `sent_email.completed`), which an endpoint receives only when it lists them, and only for sends made after it first listed one. An endpoint that lists `sent_email.*` events and also handles inbound mail must keep its inbound event types (such as `email.received`) in the list. An empty array is rejected; omit the field instead."
25462
25575
  } }
25463
25576
  },
25464
25577
  "created_at": {
@@ -31368,6 +31481,11 @@ const operationManifest = [
31368
31481
  "type": "string",
31369
31482
  "description": "Bare email address parsed from `to_header`."
31370
31483
  },
31484
+ "recipient_count": {
31485
+ "type": ["integer", "null"],
31486
+ "minimum": 0,
31487
+ "description": "Every recipient of the send, To, Cc and Bcc together, each address counted once: the size of `to_addresses`, `cc` and `bcc` combined without duplicates. On a mail relay send `bcc` lists every hidden recipient. Null when the request uses an address-bound agent connection key whose address is not the sender, because the count would reveal Bcc recipients. The `sent_email` object of the `sent_email.*` webhook events carries the same field."
31488
+ },
31371
31489
  "subject": { "type": "string" },
31372
31490
  "body_size_bytes": {
31373
31491
  "type": "integer",
@@ -32103,6 +32221,11 @@ const operationManifest = [
32103
32221
  "type": "string",
32104
32222
  "description": "Bare email address parsed from `to_header`."
32105
32223
  },
32224
+ "recipient_count": {
32225
+ "type": ["integer", "null"],
32226
+ "minimum": 0,
32227
+ "description": "Every recipient of the send, To, Cc and Bcc together, each address counted once: the size of `to_addresses`, `cc` and `bcc` combined without duplicates. On a mail relay send `bcc` lists every hidden recipient. Null when the request uses an address-bound agent connection key whose address is not the sender, because the count would reveal Bcc recipients. The `sent_email` object of the `sent_email.*` webhook events carries the same field."
32228
+ },
32106
32229
  "subject": { "type": "string" },
32107
32230
  "body_size_bytes": {
32108
32231
  "type": "integer",
@@ -32603,6 +32726,11 @@ const operationManifest = [
32603
32726
  "type": "string",
32604
32727
  "description": "Bare email address parsed from `to_header`."
32605
32728
  },
32729
+ "recipient_count": {
32730
+ "type": ["integer", "null"],
32731
+ "minimum": 0,
32732
+ "description": "Every recipient of the send, To, Cc and Bcc together, each address counted once: the size of `to_addresses`, `cc` and `bcc` combined without duplicates. On a mail relay send `bcc` lists every hidden recipient. Null when the request uses an address-bound agent connection key whose address is not the sender, because the count would reveal Bcc recipients. The `sent_email` object of the `sent_email.*` webhook events carries the same field."
32733
+ },
32606
32734
  "subject": { "type": "string" },
32607
32735
  "body_size_bytes": {
32608
32736
  "type": "integer",
@@ -33007,6 +33135,50 @@ const operationManifest = [
33007
33135
  "type": "boolean",
33008
33136
  "description": "True when the response replays a previously-recorded send\nkeyed by `client_idempotency_key` (same key, same canonical\npayload). False on a fresh send and on gate-denied\nresponses. Lets callers branch on cache state without\ndiffing fields.\n"
33009
33137
  },
33138
+ "dedup_reason": {
33139
+ "type": ["string", "null"],
33140
+ "description": "Why the response was a replay; null or absent on a fresh\nsend. `content_hash_match`: the idempotency key matched an\nearlier send. `parent_already_replied`: a reply sent without\nan `Idempotency-Key` header was matched to the reply its\nparent email already has. Pass an `Idempotency-Key` to send\nanother reply to the same email.\n"
33141
+ },
33142
+ "idempotency": {
33143
+ "type": "object",
33144
+ "description": "Present only when the request was answered with an existing send\ninstead of making a new one (`idempotent_replay: true`). Nothing\nwas sent for this request. Says how the idempotency key was\nderived and which send the request collapsed onto. On the replay\nof a stored failure the same object is under `error.details`.\n",
33145
+ "properties": {
33146
+ "replayed": {
33147
+ "type": "boolean",
33148
+ "description": "Always `true`. The object is absent on a request that made a send of its own."
33149
+ },
33150
+ "key_source": {
33151
+ "type": "string",
33152
+ "enum": [
33153
+ "explicit",
33154
+ "auto_content",
33155
+ "function_trigger"
33156
+ ],
33157
+ "description": "`explicit`: the request carried an `Idempotency-Key` header.\n`auto_content`: no header was sent, so the key was derived\nfrom the canonical request content (recipients included) and\na fixed 5-minute window; an identical send inside the same\nwindow was not sent again. `function_trigger`: no header on a\nsend made by a Function, so the key was derived from the\ncontent, the Function and the inbound email (or event) that\ninvoked it.\n"
33158
+ },
33159
+ "original_sent_email_id": {
33160
+ "type": "string",
33161
+ "format": "uuid",
33162
+ "description": "The send this request collapsed onto. Same value as `id`."
33163
+ },
33164
+ "original_created_at": {
33165
+ "type": ["string", "null"],
33166
+ "format": "date-time",
33167
+ "description": "When that send was created."
33168
+ },
33169
+ "window_seconds": {
33170
+ "type": ["integer", "null"],
33171
+ "description": "Length of the content window (300) when an `auto_content`\nkey matched on content. Null for `explicit` and\n`function_trigger` keys, which have no window, and for a\nkeyless reply matched because its parent already has a reply\n(`dedup_reason: parent_already_replied`).\n"
33172
+ }
33173
+ },
33174
+ "required": [
33175
+ "replayed",
33176
+ "key_source",
33177
+ "original_sent_email_id",
33178
+ "original_created_at",
33179
+ "window_seconds"
33180
+ ]
33181
+ },
33010
33182
  "scheduled_at": {
33011
33183
  "type": "string",
33012
33184
  "format": "date-time",
@@ -33122,6 +33294,11 @@ const operationManifest = [
33122
33294
  "type": "string",
33123
33295
  "description": "Bare email address parsed from `to_header`."
33124
33296
  },
33297
+ "recipient_count": {
33298
+ "type": ["integer", "null"],
33299
+ "minimum": 0,
33300
+ "description": "Every recipient of the send, To, Cc and Bcc together, each address counted once: the size of `to_addresses`, `cc` and `bcc` combined without duplicates. On a mail relay send `bcc` lists every hidden recipient. Null when the request uses an address-bound agent connection key whose address is not the sender, because the count would reveal Bcc recipients. The `sent_email` object of the `sent_email.*` webhook events carries the same field."
33301
+ },
33125
33302
  "subject": { "type": "string" },
33126
33303
  "body_size_bytes": {
33127
33304
  "type": "integer",
@@ -33775,6 +33952,50 @@ const operationManifest = [
33775
33952
  "type": "boolean",
33776
33953
  "description": "True when the response replays a previously-recorded send\nkeyed by `client_idempotency_key` (same key, same canonical\npayload). False on a fresh send and on gate-denied\nresponses. Lets callers branch on cache state without\ndiffing fields.\n"
33777
33954
  },
33955
+ "dedup_reason": {
33956
+ "type": ["string", "null"],
33957
+ "description": "Why the response was a replay; null or absent on a fresh\nsend. `content_hash_match`: the idempotency key matched an\nearlier send. `parent_already_replied`: a reply sent without\nan `Idempotency-Key` header was matched to the reply its\nparent email already has. Pass an `Idempotency-Key` to send\nanother reply to the same email.\n"
33958
+ },
33959
+ "idempotency": {
33960
+ "type": "object",
33961
+ "description": "Present only when the request was answered with an existing send\ninstead of making a new one (`idempotent_replay: true`). Nothing\nwas sent for this request. Says how the idempotency key was\nderived and which send the request collapsed onto. On the replay\nof a stored failure the same object is under `error.details`.\n",
33962
+ "properties": {
33963
+ "replayed": {
33964
+ "type": "boolean",
33965
+ "description": "Always `true`. The object is absent on a request that made a send of its own."
33966
+ },
33967
+ "key_source": {
33968
+ "type": "string",
33969
+ "enum": [
33970
+ "explicit",
33971
+ "auto_content",
33972
+ "function_trigger"
33973
+ ],
33974
+ "description": "`explicit`: the request carried an `Idempotency-Key` header.\n`auto_content`: no header was sent, so the key was derived\nfrom the canonical request content (recipients included) and\na fixed 5-minute window; an identical send inside the same\nwindow was not sent again. `function_trigger`: no header on a\nsend made by a Function, so the key was derived from the\ncontent, the Function and the inbound email (or event) that\ninvoked it.\n"
33975
+ },
33976
+ "original_sent_email_id": {
33977
+ "type": "string",
33978
+ "format": "uuid",
33979
+ "description": "The send this request collapsed onto. Same value as `id`."
33980
+ },
33981
+ "original_created_at": {
33982
+ "type": ["string", "null"],
33983
+ "format": "date-time",
33984
+ "description": "When that send was created."
33985
+ },
33986
+ "window_seconds": {
33987
+ "type": ["integer", "null"],
33988
+ "description": "Length of the content window (300) when an `auto_content`\nkey matched on content. Null for `explicit` and\n`function_trigger` keys, which have no window, and for a\nkeyless reply matched because its parent already has a reply\n(`dedup_reason: parent_already_replied`).\n"
33989
+ }
33990
+ },
33991
+ "required": [
33992
+ "replayed",
33993
+ "key_source",
33994
+ "original_sent_email_id",
33995
+ "original_created_at",
33996
+ "window_seconds"
33997
+ ]
33998
+ },
33778
33999
  "scheduled_at": {
33779
34000
  "type": "string",
33780
34001
  "format": "date-time",
@@ -35340,7 +35561,7 @@ const operationManifest = [
35340
35561
  "binaryResponse": false,
35341
35562
  "bodyRequired": false,
35342
35563
  "command": "list-deliveries",
35343
- "description": "Returns a paginated list of webhook delivery attempts. Each delivery\nincludes a nested `email` object with sender, recipient, and subject.\n",
35564
+ "description": "Returns a paginated list of webhook delivery attempts, newest first.\nA delivery of an inbound email includes a nested `email` object with\nsender, recipient, and subject. A delivery of an opt-in\n`sent_email.*` event has `email_id` and `email` set to null and\n`sent_email_id` set to the send it is about. Filter by `event_type`\nand `sent_email_id` to find the deliveries of one send.\n",
35344
35565
  "hasJsonBody": false,
35345
35566
  "method": "GET",
35346
35567
  "operationId": "listDeliveries",
@@ -35371,13 +35592,28 @@ const operationManifest = [
35371
35592
  "required": false,
35372
35593
  "type": "string"
35373
35594
  },
35595
+ {
35596
+ "description": "Only deliveries of this event type, for example `sent_email.failed`. Deliveries recorded before event types existed have none and never match.",
35597
+ "enum": null,
35598
+ "name": "event_type",
35599
+ "required": false,
35600
+ "type": "string"
35601
+ },
35602
+ {
35603
+ "description": "Only deliveries of `sent_email.*` events about this sent email.",
35604
+ "enum": null,
35605
+ "name": "sent_email_id",
35606
+ "required": false,
35607
+ "type": "string"
35608
+ },
35374
35609
  {
35375
35610
  "description": "Filter by delivery status",
35376
35611
  "enum": [
35377
35612
  "pending",
35378
35613
  "delivered",
35379
35614
  "header_confirmed",
35380
- "failed"
35615
+ "failed",
35616
+ "skipped_by_rules"
35381
35617
  ],
35382
35618
  "name": "status",
35383
35619
  "required": false,
@@ -35406,11 +35642,21 @@ const operationManifest = [
35406
35642
  "properties": {
35407
35643
  "id": {
35408
35644
  "type": "string",
35409
- "description": "Delivery ID (numeric string)"
35645
+ "description": "Delivery ID: a numeric string or a UUID. Pass it to the replay operation as is."
35410
35646
  },
35411
35647
  "email_id": {
35412
- "type": "string",
35413
- "format": "uuid"
35648
+ "type": ["string", "null"],
35649
+ "format": "uuid",
35650
+ "description": "The inbound email this delivery is about. Null for deliveries that are not about a received email, such as `sent_email.*` events."
35651
+ },
35652
+ "event_type": {
35653
+ "type": ["string", "null"],
35654
+ "description": "The delivered event type, for example `email.received` or `sent_email.delivered`. Null for deliveries recorded before event types existed."
35655
+ },
35656
+ "sent_email_id": {
35657
+ "type": ["string", "null"],
35658
+ "format": "uuid",
35659
+ "description": "The sent email a `sent_email.*` delivery is about. Null for every other delivery."
35414
35660
  },
35415
35661
  "org_id": {
35416
35662
  "type": "string",
@@ -35420,19 +35666,27 @@ const operationManifest = [
35420
35666
  "type": "string",
35421
35667
  "format": "uuid"
35422
35668
  },
35423
- "endpoint_url": { "type": "string" },
35669
+ "endpoint_url": {
35670
+ "type": ["string", "null"],
35671
+ "description": "The endpoint's URL. For a function-backed endpoint, an opaque `function://<id>` identifier rather than a callable URL."
35672
+ },
35424
35673
  "status": {
35425
35674
  "type": "string",
35426
35675
  "enum": [
35427
35676
  "pending",
35428
35677
  "delivered",
35429
35678
  "header_confirmed",
35430
- "failed"
35679
+ "failed",
35680
+ "skipped_by_rules"
35431
35681
  ]
35432
35682
  },
35433
35683
  "attempt_count": { "type": "integer" },
35434
35684
  "duration_ms": { "type": ["integer", "null"] },
35435
35685
  "last_error": { "type": ["string", "null"] },
35686
+ "last_error_code": {
35687
+ "type": ["string", "null"],
35688
+ "description": "A stable code for the last failure, for example `http_500`."
35689
+ },
35436
35690
  "created_at": {
35437
35691
  "type": "string",
35438
35692
  "format": "date-time"
@@ -35445,10 +35699,11 @@ const operationManifest = [
35445
35699
  "type": ["object", "null"],
35446
35700
  "properties": {
35447
35701
  "sender": { "type": "string" },
35448
- "recipient": { "type": "string" },
35702
+ "recipient": { "type": ["string", "null"] },
35449
35703
  "subject": { "type": ["string", "null"] }
35450
35704
  },
35451
- "required": ["sender", "recipient"]
35705
+ "required": ["sender", "recipient"],
35706
+ "description": "Null for deliveries that are not about a received email."
35452
35707
  }
35453
35708
  },
35454
35709
  "required": [
@@ -35473,13 +35728,13 @@ const operationManifest = [
35473
35728
  "binaryResponse": false,
35474
35729
  "bodyRequired": false,
35475
35730
  "command": "replay-delivery",
35476
- "description": "Re-sends the stored webhook payload from a previous delivery attempt.\nIf the original endpoint is still active, it is targeted. If the\noriginal endpoint was deleted, the oldest active endpoint is used.\nDeactivated endpoints cannot be replayed to. Rate limited per-org,\nsharing an org-wide budget with email replays.\n",
35731
+ "description": "Re-sends a previous delivery to its original endpoint. If that\nendpoint was deleted or deactivated, the replay is rejected.\nSupports inbound email deliveries and `sent_email.*` deliveries.\nAn inbound email delivery is replayed with its original stored\npayload and event id. A `sent_email.*` delivery is replayed with the\nsame event id but a payload rebuilt from the send as it is now; it\nis rejected if the send was deleted or the endpoint is no longer an\nactive http endpoint. Rate limited per-org, sharing an org-wide\nbudget with email replays.\n",
35477
35732
  "hasJsonBody": false,
35478
35733
  "method": "POST",
35479
35734
  "operationId": "replayDelivery",
35480
35735
  "path": "/webhooks/deliveries/{id}/replay",
35481
35736
  "pathParams": [{
35482
- "description": "Delivery ID (numeric)",
35737
+ "description": "Delivery ID, as `id` in the deliveries list: a numeric string or a UUID.",
35483
35738
  "enum": null,
35484
35739
  "name": "id",
35485
35740
  "required": true,
@@ -36137,6 +36392,11 @@ async function replaceSessionAddresses(params) {
36137
36392
  const directory = agentProfileDirectory(params.configDir, profile);
36138
36393
  archive(join(directory, "setup.json"), stamp);
36139
36394
  archive(join(directory, "enrollment", "state.json"), stamp);
36395
+ await releaseRegistrationsForProfile({
36396
+ configDir: params.configDir,
36397
+ profileName: profile,
36398
+ now: params.now
36399
+ });
36140
36400
  }
36141
36401
  } catch (error) {
36142
36402
  const done = replaced.length ? ` Already disconnected: ${replaced.map((r) => r.address).join(", ")}.` : "";
@@ -36188,8 +36448,16 @@ function statusProfileMissingDetail(bound) {
36188
36448
  function invocation$1(entry) {
36189
36449
  return entry && /[\\/]_npx[\\/]/.test(entry) ? "npx -y primitive@latest" : "primitive";
36190
36450
  }
36451
+ /**
36452
+ * Names the addresses this run cleared as already revoked, so a later
36453
+ * refusal or failure that says nothing changed is not taken literally.
36454
+ */
36455
+ function withClearedNote(message, cleared) {
36456
+ if (!cleared.length) return message;
36457
+ return `${message} Before that, this run cleared ${cleared.map((row) => `${row.address} (profile ${row.profile})`).join(", ")} on this machine because Primitive confirmed ${cleared.length === 1 ? "its credential was" : "their credentials were"} already revoked; ${cleared.length === 1 ? "it stays" : "they stay"} disconnected.`;
36458
+ }
36191
36459
  var AgentConnectCommand = class AgentConnectCommand extends Command {
36192
- static description = `Connect this coding session to Primitive in one call. With --session, the command checks this CLI's capabilities, installs or refreshes the matching primitive-connect skill for the detected runtime (Claude Code or Codex) from files bundled in this CLI, claims the owner's private setup invitation from piped stdin, answers one authenticated setup challenge, enables owner notifications, starts receiving, seeds a private AGENT_INFO note from --name and --info when absent, and prints one JSON result. The receiver defaults to external hooks in Claude Code and the native background listener elsewhere. When the native default cannot work because this machine has no local session socket (for example Codex older than 0.158.0), the command connects with poll receiving instead and reports receiving.fallbackReason and receiving.fallbackDetail. Without a session (no --session, or an empty one) the receiver is poll: for runtimes with no local session ID or hooks, such as a cloud-hosted conversation whose commands run in a separate sandbox, the invitation is claimed and verified the same way, nothing is installed, and the result's receiving.checkCommand is the command the agent runs to check for new mail at the start of each turn and after it sends. Native receiving starts or reuses a supervised background listener and waits briefly for its first successful mail check; external receiving installs the exact Claude session's fail-open Stop hook and resume SessionStart hook after the verification reply; a real idle mail event must still verify wake. The profile defaults to session-<session>, or to connection-<invitation hash prefix> without a session. --resume continues saved progress without reading stdin or replaying a claim or uncertain verification send. After the reply is submitted it waits up to a minute for the server to confirm the connection and reports verification.state verified, or reply_submitted while confirmation is still pending. Verification is separate from receiver health. Both official production (https://api.primitive.dev/v1) and staging (https://api.primitive-staging-1.com/v1) invitations pin their API origin. Never pass an invitation as a command argument. One address per session: if this session already has a connected Primitive address, including one saved in the profile being connected, the command claims nothing, leaves stdin unread, and exits 3 with status already_connected. Its detail names how to continue that profile's own connection when that is the only one: --resume for a setup with saved progress, or --keep-existing to refresh a claim-only profile from the same invitation. Otherwise ask the user whether to keep the existing address or disconnect it first, then rerun with --keep-existing or --replace-existing. Never choose for the user. Select the saved identity with PRIMITIVE_AGENT_PROFILE. Use --status --json with --profile, with PRIMITIVE_AGENT_PROFILE set, or with --session (the address connected for that session) to inspect saved identity and local receiver health offline; it exits 1 when nothing is configured. A status check before connecting is optional: connect itself claims nothing and exits 3 when the session already has an address. A completed connection's result carries nextSteps, in order: report to the owner in two short sentences, make one offer to rename a generated name and record where the agent runs, and load the primitive-connect skill before handling mail. When the claim reported one, the result also carries the connection's name and nameIsDefault.`;
36460
+ static description = `Connect this coding session to Primitive in one call. With --session, the command checks this CLI's capabilities, installs or refreshes the matching primitive-connect skill for the detected runtime (Claude Code or Codex) from files bundled in this CLI, claims the owner's private setup invitation from piped stdin, answers one authenticated setup challenge, enables owner notifications, starts receiving, seeds a private AGENT_INFO note from --name and --info when absent, and prints one JSON result. The receiver defaults to external hooks in Claude Code and the native background listener elsewhere. When the native default cannot work because this machine has no local session socket (for example Codex older than 0.158.0), the command connects with poll receiving instead and reports receiving.fallbackReason and receiving.fallbackDetail. Without a session (no --session, or an empty one) the receiver is poll: for runtimes with no local session ID or hooks, such as a cloud-hosted conversation whose commands run in a separate sandbox, the invitation is claimed and verified the same way, nothing is installed, and the result's receiving.checkCommand is the command the agent runs to check for new mail at the start of each turn and after it sends. Native receiving starts or reuses a supervised background listener and waits briefly for its first successful mail check; external receiving installs the exact Claude session's fail-open Stop hook and resume SessionStart hook after the verification reply; a real idle mail event must still verify wake. The profile defaults to session-<session>, or to connection-<invitation hash prefix> without a session. --resume continues saved progress without reading stdin or replaying a claim or uncertain verification send. After the reply is submitted it waits up to a minute for the server to confirm the connection and reports verification.state verified, or reply_submitted while confirmation is still pending. Verification is separate from receiver health. Both official production (https://api.primitive.dev/v1) and staging (https://api.primitive-staging-1.com/v1) invitations pin their API origin. Never pass an invitation as a command argument. One address per session: an address whose credential Primitive confirms was already revoked (for example, revoked in the app before the owner reconnected it) no longer counts; it is disconnected locally, reported in clearedRevoked, and its saved setup is moved aside so the new invitation can be claimed into the same profile. Otherwise, if this session already has a connected Primitive address, including one saved in the profile being connected, the command claims nothing, leaves stdin unread, and exits 3 with status already_connected. Its detail names how to continue that profile's own connection when that is the only one: --resume for a setup with saved progress, or --keep-existing to refresh a claim-only profile from the same invitation. Otherwise ask the user whether to keep the existing address or disconnect it first, then rerun with --keep-existing or --replace-existing. Never choose for the user. Select the saved identity with PRIMITIVE_AGENT_PROFILE. Use --status --json with --profile, with PRIMITIVE_AGENT_PROFILE set, or with --session (the address connected for that session) to inspect saved identity and local receiver health offline; it exits 1 when nothing is configured. A status check before connecting is optional: connect itself claims nothing and exits 3 when the session already has an address. A completed connection's result carries nextSteps, in order: report to the owner in two short sentences, make one offer to rename a generated name and record where the agent runs, and load the primitive-connect skill before handling mail. When the claim reported one, the result also carries the connection's name and nameIsDefault.`;
36193
36461
  static summary = "Connect and verify an agent address";
36194
36462
  static examples = [
36195
36463
  "<%= config.bin %> agent connect --session \"$CODEX_THREAD_ID\" --name Research --info \"Reviews pull requests\" --json < private-invitation.txt",
@@ -36250,6 +36518,7 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
36250
36518
  const { flags } = await this.parse(AgentConnectCommand);
36251
36519
  let pollProfile;
36252
36520
  let replaced = [];
36521
+ let cleared = [];
36253
36522
  try {
36254
36523
  if (flags.status) {
36255
36524
  const statusSession = flags.session?.trim();
@@ -36321,10 +36590,25 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
36321
36590
  address: check.target.address
36322
36591
  }];
36323
36592
  if (bound.length > 0 && !flags["replace-existing"]) {
36324
- const continuation = check.target && targetContinuation(this.config.configDir, check.target.profile, claimOnly);
36593
+ const result = await clearRevokedAddresses({
36594
+ configDir: this.config.configDir,
36595
+ rows: bound,
36596
+ disconnect: disconnectAgent
36597
+ });
36598
+ bound = result.remaining;
36599
+ cleared = result.cleared;
36600
+ if (!flags.json) for (const row of cleared) this.log(`Cleared ${row.address} (profile ${row.profile}): Primitive confirmed its credential was already revoked.`);
36601
+ }
36602
+ if (bound.length > 0 && !flags["replace-existing"]) {
36603
+ const continuation = check.target && bound.some((row) => row.profile === check.target?.profile) && targetContinuation(this.config.configDir, check.target.profile, claimOnly);
36325
36604
  const refusal = alreadyConnected(guardSession, bound, "connect", continuation || void 0);
36326
- if (flags.json) this.log(JSON.stringify(refusal));
36327
- else this.log(refusal.detail);
36605
+ const detail = withClearedNote(refusal.detail, cleared);
36606
+ if (flags.json) this.log(JSON.stringify(cleared.length ? {
36607
+ ...refusal,
36608
+ detail,
36609
+ clearedRevoked: cleared
36610
+ } : refusal));
36611
+ else this.log(detail);
36328
36612
  process.exitCode = 3;
36329
36613
  return;
36330
36614
  }
@@ -36386,10 +36670,11 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
36386
36670
  guidance: `${output.guidance} Next: ${nextSteps.join(" ")}`
36387
36671
  } : {}
36388
36672
  };
36389
- if (flags.json) this.log(JSON.stringify(replaced.length ? {
36673
+ if (flags.json) this.log(JSON.stringify({
36390
36674
  ...suggested,
36391
- replacedExisting: replaced
36392
- } : suggested));
36675
+ ...replaced.length ? { replacedExisting: replaced } : {},
36676
+ ...cleared.length ? { clearedRevoked: cleared } : {}
36677
+ }));
36393
36678
  else {
36394
36679
  this.log(`Agent ${output.address}: verification ${output.verification.state}; receiving ${output.receiving.state}; skill ${output.skill.state}.`);
36395
36680
  this.log(`Select it with ${output.selectProfile}.`);
@@ -36411,25 +36696,29 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
36411
36696
  }
36412
36697
  if (!flags.profile) throw new AgentConnectionSetupError("Pass --profile for claim-only.");
36413
36698
  const invitation = await readInvitationOnce();
36414
- agentInvitationHash(invitation);
36699
+ const invitationHash = agentInvitationHash(invitation);
36415
36700
  await replaceExisting?.();
36416
- const claimed = await connectAgent({
36701
+ const profileName = flags.profile;
36702
+ const claimed = await claimOverRevokedSetup({
36417
36703
  configDir: this.config.configDir,
36418
- profileName: flags.profile,
36419
- invitation
36420
- });
36704
+ profileName,
36705
+ invitationHash
36706
+ }, (claimLockHeld) => connectAgent({
36707
+ configDir: this.config.configDir,
36708
+ profileName,
36709
+ invitation,
36710
+ claimLockHeld
36711
+ }));
36421
36712
  const result = await withOwnerMemberAddress(claimed, {
36422
36713
  configDir: this.config.configDir,
36423
36714
  onlyIfUnknown: claimed.status === "claimed"
36424
36715
  });
36425
36716
  const warnings = connectWarnings(this.config.configDir, result.identity.profileName);
36426
- if (flags.json) this.log(JSON.stringify(replaced.length ? {
36717
+ if (flags.json) this.log(JSON.stringify({
36427
36718
  ...result,
36428
36719
  warnings,
36429
- replacedExisting: replaced
36430
- } : {
36431
- ...result,
36432
- warnings
36720
+ ...replaced.length ? { replacedExisting: replaced } : {},
36721
+ ...cleared.length ? { clearedRevoked: cleared } : {}
36433
36722
  }));
36434
36723
  else {
36435
36724
  this.log(result.status === "claimed" ? "Agent profile saved. Email readiness is not yet verified." : "Agent profile already configured; no claim was sent.");
@@ -36439,7 +36728,7 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
36439
36728
  }
36440
36729
  } catch (caught) {
36441
36730
  const error = refusalAfterReplacement(caught, replaced);
36442
- throw new Errors.CLIError(error instanceof AgentConnectionSetupError ? error.message : flags.session?.trim() ? `Setup paused. Preserve the private profile and run ${invocation$1(process.argv[1])} agent connect --profile ${flags.profile ?? defaultAgentProfileName(flags.session.trim())} --session ${flags.session.trim()}${flags.receiver ? ` --receiver ${flags.receiver}` : ""} --resume --json. Do not manually resend verification or reclaim the invitation.` : pollProfile ? `Setup paused. Preserve the private profile and run ${invocation$1(process.argv[1])} agent connect --profile ${pollProfile} --receiver poll --resume --json. Do not manually resend verification or reclaim the invitation.` : "Agent setup did not complete. Preserve the private profile and request a fresh owner invitation if a claim may have been submitted.", { exit: 1 });
36731
+ throw new Errors.CLIError(withClearedNote(error instanceof AgentConnectionSetupError ? error.message : flags.session?.trim() ? `Setup paused. Preserve the private profile and run ${invocation$1(process.argv[1])} agent connect --profile ${flags.profile ?? defaultAgentProfileName(flags.session.trim())} --session ${flags.session.trim()}${flags.receiver ? ` --receiver ${flags.receiver}` : ""} --resume --json. Do not manually resend verification or reclaim the invitation.` : pollProfile ? `Setup paused. Preserve the private profile and run ${invocation$1(process.argv[1])} agent connect --profile ${pollProfile} --receiver poll --resume --json. Do not manually resend verification or reclaim the invitation.` : "Agent setup did not complete. Preserve the private profile and request a fresh owner invitation if a claim may have been submitted.", cleared), { exit: 1 });
36443
36732
  }
36444
36733
  }
36445
36734
  };
@@ -36620,7 +36909,7 @@ var AgentContactsUpdateCommand = class AgentContactsUpdateCommand extends Comman
36620
36909
  //#endregion
36621
36910
  //#region src/oclif/commands/agent-disconnect.ts
36622
36911
  var AgentDisconnectCommand = class AgentDisconnectCommand extends Command {
36623
- static description = "Disconnect exactly one saved connected-agent profile. Stop its tracked bound receiver, revoke only that address's bound credential at its pinned Primitive origin, then remove only the local credential after confirmed revocation. Mail, notes, setup evidence and notification receipts remain. This does not permanently remove the connection record; an owner or admin can do that separately.";
36912
+ static description = "Disconnect exactly one saved connected-agent profile. Stop its tracked bound receiver, revoke only that address's bound credential at its pinned Primitive origin, then remove only the local credential after confirmed revocation. When Primitive refuses the credential as unauthorized because it was already revoked (in the app or from another machine), the same credential is checked once more and, only if that confirms it no longer authenticates, the disconnect completes and reports revocation already_revoked; a server error or network failure is never treated as confirmation. Mail, notes, setup evidence and notification receipts remain. This does not permanently remove the connection record; an owner or admin can do that separately.";
36624
36913
  static summary = "Disconnect one connected agent";
36625
36914
  static examples = ["<%= config.bin %> agent disconnect --profile work", "<%= config.bin %> agent disconnect --profile work --json"];
36626
36915
  static flags = {
@@ -37291,7 +37580,7 @@ var AgentRuntimeGetCommand = class AgentRuntimeGetCommand extends Command {
37291
37580
  //#region src/oclif/commands/agent-session-end.ts
37292
37581
  var AgentSessionEndCommand = class AgentSessionEndCommand extends Command {
37293
37582
  static summary = "Disconnect the agent a session registered when it ends";
37294
- static description = "Disconnects the agent that `primitive agent session-register` created for this session, and removes its exact-session receive hooks. Agents connected any other way are left connected. Never blocks or fails a session: every outcome exits 0 with a status. --hook reads Claude's SessionEnd hook input from stdin and disconnects in the background.";
37583
+ static description = "Disconnects the agent that `primitive agent session-register` created for this session, and removes its exact-session receive hooks. Agents connected any other way are left connected, including an agent later connected into the same profile with `primitive agent connect`. Never blocks or fails a session: every outcome exits 0 with a status. --hook reads Claude's SessionEnd hook input from stdin and disconnects in the background after a grace period: Claude reports the same end for a restart, a switch to another session and a real exit, so a start of the same session within the grace period cancels the end and keeps its agent.";
37295
37584
  static examples = ["<%= config.bin %> agent session-end --runtime claude --json", "<%= config.bin %> agent session-end --runtime codex --session 11111111-1111-4111-8111-111111111111"];
37296
37585
  static flags = {
37297
37586
  runtime: Flags.string({
@@ -37300,6 +37589,12 @@ var AgentSessionEndCommand = class AgentSessionEndCommand extends Command {
37300
37589
  description: "The coding agent runtime this session runs in"
37301
37590
  }),
37302
37591
  session: Flags.string({ description: "Exact session UUID; defaults to the runtime's own session ID" }),
37592
+ grace: Flags.integer({
37593
+ min: 0,
37594
+ max: 3600,
37595
+ description: "Seconds to wait before disconnecting; a start of the same session meanwhile cancels the end"
37596
+ }),
37597
+ reason: Flags.string({ description: "The runtime's reason for the end, recorded with it" }),
37303
37598
  hook: Flags.boolean({ description: "Read Claude hook JSON from stdin and disconnect in the background" }),
37304
37599
  json: Flags.boolean({ description: "Print the result as JSON" }),
37305
37600
  quiet: Flags.boolean({ description: "Print nothing" })
@@ -37320,6 +37615,9 @@ var AgentSessionEndCommand = class AgentSessionEndCommand extends Command {
37320
37615
  runtime,
37321
37616
  "--session",
37322
37617
  input.sessionId,
37618
+ "--grace",
37619
+ String(HOOK_END_GRACE_MS / 1e3),
37620
+ ...input.reason ? ["--reason", input.reason] : [],
37323
37621
  "--quiet"
37324
37622
  ],
37325
37623
  env: process.env,
@@ -37330,7 +37628,9 @@ var AgentSessionEndCommand = class AgentSessionEndCommand extends Command {
37330
37628
  const result = await endSession({
37331
37629
  configDir: this.config.configDir,
37332
37630
  runtime,
37333
- session: flags.session
37631
+ session: flags.session,
37632
+ graceMs: (flags.grace ?? 0) * 1e3,
37633
+ reason: flags.reason ?? null
37334
37634
  });
37335
37635
  const { status, ...rest } = result;
37336
37636
  if (flags.json) this.log(JSON.stringify({
@@ -39490,6 +39790,10 @@ function chatFailureText(message) {
39490
39790
  function chatHeading(text) {
39491
39791
  return chatColor("bold", text);
39492
39792
  }
39793
+ const CHAT_REPLAY_NOTICE = {
39794
+ keyOrigin: "cli_derived",
39795
+ advise: false
39796
+ };
39493
39797
  function chatNoticeText(message) {
39494
39798
  return chatColor("yellow", message);
39495
39799
  }
@@ -39789,7 +40093,7 @@ function chatNoun(context) {
39789
40093
  return context.parentReply === void 0 ? "Message" : "Reply";
39790
40094
  }
39791
40095
  function formatChatRepliedMessage(context, outcome) {
39792
- if (outcome === "already_sent") return `${formatAlreadySentNotice(context.sent)} Its existing reply from ${context.reply.from_email} is shown.`;
40096
+ if (outcome === "already_sent") return `${formatAlreadySentNotice(context.sent, CHAT_REPLAY_NOTICE)} Its existing reply from ${context.reply.from_email} is shown.`;
39793
40097
  return `${chatNoun(context)} sent (id ${context.sent.id}) and a reply arrived from ${context.reply.from_email}.`;
39794
40098
  }
39795
40099
  function buildChatJsonEnvelope(context, outcome = "replied") {
@@ -39853,7 +40157,7 @@ function formatChatAwaitingReplyMessage(context, options) {
39853
40157
  const [next] = buildChatExistingReplyCommands(context, options.existingReplyId);
39854
40158
  const status = options.waitError === void 0 ? "No reply to it yet." : `Loading its reply failed: ${options.waitError}.`;
39855
40159
  const action = next.kind === "inspect_reply" ? "read the reply with" : next.kind === "inspect_inbox" ? "inspect the inbox with" : "wait with";
39856
- return `${formatAlreadySentNotice(context.sent)} ${status} Do NOT resend; ${action}: ${next.command}`;
40160
+ return `${formatAlreadySentNotice(context.sent, CHAT_REPLAY_NOTICE)} ${status} Do NOT resend; ${action}: ${next.command}`;
39857
40161
  }
39858
40162
  const [wait] = buildChatRecoveryCommands(context);
39859
40163
  const status = options.waitError === void 0 ? `No reply yet after ${context.timeoutSeconds}s.` : `Waiting for the reply failed: ${options.waitError}`;
@@ -40656,7 +40960,7 @@ var ChatCommand = class ChatCommand extends Command {
40656
40960
  await connectedWait?.bind(sent.id);
40657
40961
  if (flags.async) {
40658
40962
  const outcome = sent.idempotent_replay ? "already_sent" : "sent";
40659
- const message = sent.idempotent_replay ? `${formatAlreadySentNotice(sent)} This conversation remains bound to this session; inspect it if the answer may have arrived already.` : `${chatNoun(baseContext)} sent (id ${sent.id}). This conversation is bound to this session for status and later replies. Keep its configured receiver active.`;
40963
+ const message = sent.idempotent_replay ? `${formatAlreadySentNotice(sent, CHAT_REPLAY_NOTICE)} This conversation remains bound to this session; inspect it if the answer may have arrived already.` : `${chatNoun(baseContext)} sent (id ${sent.id}). This conversation is bound to this session for status and later replies. Keep its configured receiver active.`;
40660
40964
  const result = {
40661
40965
  outcome,
40662
40966
  exit_code: sendOutcomeExitCode(outcome),
@@ -40855,7 +41159,7 @@ var ChatCommand = class ChatCommand extends Command {
40855
41159
  }
40856
41160
  reportReplied(context, outcome) {
40857
41161
  this.chatProgress.outcomeReported = true;
40858
- if (outcome === "already_sent") process.stderr.write(`${chatNoticeText(formatAlreadySentNotice(context.sent))}\n`);
41162
+ if (outcome === "already_sent") process.stderr.write(`${chatNoticeText(formatAlreadySentNotice(context.sent, CHAT_REPLAY_NOTICE))}\n`);
40859
41163
  if (context.json) this.log(JSON.stringify(buildChatJsonEnvelope(context, outcome), null, 2));
40860
41164
  else this.log(formatChatResponse(context));
40861
41165
  }
@@ -44604,7 +44908,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
44604
44908
  url: "https://primitive.dev"
44605
44909
  };
44606
44910
  const SDK_VERSION_RANGE = "^2.0.0";
44607
- const CLI_VERSION_RANGE = "^1.63.0";
44911
+ const CLI_VERSION_RANGE = "^1.65.0";
44608
44912
  const ESBUILD_VERSION_RANGE = "^0.27.0";
44609
44913
  function renderHandler() {
44610
44914
  return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
@@ -54990,9 +55294,12 @@ var PaymentsPayEmailCommand = class PaymentsPayEmailCommand extends Command {
54990
55294
  }
54991
55295
  const sent = result.data?.data ?? null;
54992
55296
  const replayed = sent?.idempotent_replay === true;
54993
- if (replayed) writeIdempotentReplayBannerIfReplay(sent, { write: (chunk) => {
54994
- process.stderr.write(chunk);
54995
- } });
55297
+ if (replayed) writeIdempotentReplayBannerIfReplay(sent, {
55298
+ advise: false,
55299
+ write: (chunk) => {
55300
+ process.stderr.write(chunk);
55301
+ }
55302
+ });
54996
55303
  let settlement = null;
54997
55304
  if (flags["wait-settle"] && !replayed && payeeTo) {
54998
55305
  process.stderr.write("Payment sent. Waiting for the x402 settlement interaction email (settlement is async)...\n");
@@ -55867,6 +56174,7 @@ var ReplyCommand = class ReplyCommand extends Command {
55867
56174
  const outcome = reportSendCommandResult({
55868
56175
  attemptStartedAtIso,
55869
56176
  idempotencyKey,
56177
+ idempotencyKeyOrigin: flags["idempotency-key"] === void 0 ? "cli_derived" : "caller",
55870
56178
  extraEnvelopeFields: {
55871
56179
  ...priorRepliesEnvelopeFields(priorRepliesCheck),
55872
56180
  ...replyTargetEnvelopeFields(flags, this.replyTarget),
@@ -56993,6 +57301,7 @@ var SendCommand = class SendCommand extends Command {
56993
57301
  attemptStartedAtIso,
56994
57302
  ...this.waitNotice ? { extraEnvelopeFields: { wait_notice: this.waitNotice } } : {},
56995
57303
  idempotencyKey,
57304
+ idempotencyKeyOrigin: flags["idempotency-key"] === void 0 ? "cli_derived" : "caller",
56996
57305
  json: flags.json,
56997
57306
  log: (line) => this.log(line),
56998
57307
  noun: "Message",