primitive 1.72.2 → 1.73.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, 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-CKMKBlx7.js";
2
- import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, F as agentProfileName, G as PrimitiveApiClient, I as agentProfilesDirectory, M as AGENT_PROFILE_ENV, N as AgentConnectionSetupError, O as loadActiveChatState, P as agentProfileDirectory, T as saveSignupCredentials, U as requireDefaultLoginProfile, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_ADDRESS_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, z as loadConnectedAgentProfile } from "../cli-config--YTRdJ3t.js";
3
- import { $ as MAX_SEND_WAIT_TIMEOUT_MS, $n as listSentAttachmentArchiveMembers, $t as refreshStoredCliCredentials, A as formatMailWakeLine, An as getFunction, Ar as updateFunction, At as loadSkillLine, B as clearConsumedPendingMail, Bn as listAgentContacts, Bt as automatedRejectedError, C as hasReplyState, Cn as getAccount, Cr as startAgentClaim, Ct as thrownErrorExitCode, D as bareAddress, Dn as getCreditBalance, Dr as testFunction, E as explainPendingMailNotFound, En as getConversation, Er as stopRepeatFromEmail, Et as connectNextSteps, F as serverRelationship, Fn as getOrgRoutingTopology, Fr as verifyAgentClaim, Ft as AUTOMATED_FILTER_UNSUPPORTED_CODE, G as removePendingMail, Gn as listDomains, Gt as queryUsesAutomated, H as pendingMailPath, Ht as expectedAutomatedVerdicts, In as getRepeatingSend, Ir as verifyAgentSignup, It as AUTOMATED_FLAG_DESCRIPTION, J as readEmailInteraction, Jn as listEndpoints, Jt as withAttachmentWait, K as interactionHumanLine, Kn as listEmailAttachmentArchiveMembers, Kt as ATTACHMENT_WAIT_FLAG_DESCRIPTION, L as wakeReadCommand, Ln as getSentEmail, Lt as AUTOMATED_VALUES, M as readThreadContext, Mn as getFunctionTestRunTrace, Mr as updateRoute, Mt as formatAlreadySentNotice, N as sentInThread, Nn as getInboxStatus, Nr as updateWakeAuthorization, Nt as writeIdempotentReplayBannerIfReplay, O as describeWake, On as getDefaultNetworkAgent, Or as unsetFunctionRoute, Ot as generatedAgentName, P as serverMuted, Pn as getMemory, Pr as updateWakeSchedule, Pt as withFlagSuggestion, Q as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, Qn as listRoutes, Qt as createCliApiClient, R as wakeRecipientField, Rn as getThread, Rt as AutomatedFilterUnsupportedError, S as formatRepliesCell, Sn as downloadSentAttachmentByFilename, Sr as simulateRoute, St as successfulSendOutcome, T as queryUsesAwaiting, Tr as startCliLogin, Tt as cliInvocation, U as readPendingMail, Un as listDefaultNetworkAgents, Ut as hasAutomatedVerdict, V as clearReadPendingMail, Vn as listAgentNetworks, Vt as ensureSearchReportsAutomated, W as recordPendingMail, Wn as listDefaultNetworkMembers, Wt as isAutomatedRejectedError, X as replyInteractionWarning, Xn as listFunctions, Y as replyExpectation, Yn as listFunctionLogs, Yt as ConnectedAgentKeyRequiredError, Zn as listRepeatingSends, Zt as createAuthenticatedCliApiClient, _ as ReplyStateUnsupportedError, _n as deleteWakeSchedule, _r as semanticSearch, _t as sendIdentityFields, a as extractErrorCode, an as cliLogout, ar as pullWebhookEvent, at as checkPriorReplies, b as ensureSearchReportsReplyState, br as setFunctionSecret, bt as sentHistoryWindowStart, c as hasControlCharacter$1, cn as createFunction, ct as excludeSignalReplies, d as runWithTiming, dn as createWakeSchedule, dr as reorderRoutes, dt as formatPriorRepliesCheckSkipped, en as resolveCliApiRequestConfig, er as listSentEmails, et as SEND_OUTCOME_HELP, f as surfaceUnauthorizedHint, fr as replyToEmail, ft as formatPriorRepliesWarning, g as REPLY_STATE_UNSUPPORTED_CODE, gn as deleteWakeAuthorization, gr as searchMemories, gt as reportSendCommandResult, h as AWAITING_VALUES, hn as deleteRoute, hr as searchEmails, ht as priorRepliesThatWentOut, i as createOperationCommand, in as checkDefaultNetworkContactAdmission, ir as pollCliLogin, it as buildThrownSendFailureEnvelope, j as latestOwnOutbound, jn as getFunctionRouting, jr as updateRepeatingSend, jt as currentMailSessionKey, k as displayAddress, kn as getEmail, kr as updateDefaultNetworkMember, kt as identitySuggestions, l as headerFlagName, ln as createRoute, lr as redeemCreditCode, lt as followUpCommandPrefix, m as AWAITING_FLAG_DESCRIPTION, mn as deleteMemory, mr as runWakeSchedule, mt as formatSendRecordFailureSummary, n as API_ERROR_CODES, nn as cancelSentEmail, nr as listWakeDispatches, nt as assertValidIdempotencyKey, o as extractErrorPayload, on as completeWebhookEvent, ot as classifySendError, p as writeErrorWithHints, pr as resendAgentSignupVerification, pt as formatSendFailureSummary, q as interactionNextActions, qn as listEmails, qt as ATTACHMENT_WAIT_MAX_MS, r as TIME_FLAG_DESCRIPTION, rn as cancelSentEmailByIdempotencyKey, rr as listWakeSchedules, rt as buildFollowUpCommand, s as formatErrorPayload, sn as createEndpoint, st as deriveSendIdempotencyKey, t as API_BASE_URL_FLAG_DESCRIPTION, tn as addDefaultNetworkMember, tr as listWakeAuthorizations, tt as apiResultHttpStatus, u as readTextFileFlag, un as createWakeAuthorization, ur as removeDefaultNetworkMember, ut as formatDeletedEarlierSendNotice, v as assertAwaitingFilterRows, vn as downloadEmailAttachmentArchiveMember, vr as sendEmail, vt as sendOutcomeExitCode, w as isAwaitingRejectedError, wn as getAgentContactPolicy, wr as startAgentSignup, x as formatAwaitingCell, xn as downloadSentAttachmentArchiveMember, xr as setMemory, xt as serializeErrorPayload, y as awaitingRejectedError, yn as downloadEmailAttachmentByFilename, yr as setFunctionRoute, yt as sendWaitClampNotice, z as wakeRelationship, zn as getWakeSchedule, zt as assertAutomatedVerdict } from "../api-command-BrwPbyPP.js";
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-Byt5sPWW.js";
6
- import { $ as installClaudeWakeHook, A as evaluateContactPolicy, B as contactAddress, C as RECEIVER_MODES, Ct as withOwnerMemberAddress, D as NETWORK_ADMISSION_PENDING_RETRY_MS, E as ContactPolicyReadRetryError, F as disconnectAgent, G as connectAgent, H as runContactRequest, I as AddressNotesApiError, J as refusalAfterReplacement, K as readAgentInvitation, L as addressNoteTarget, M as AgentDisconnectError, N as claimOverRevokedSetup, O as NetworkAdmissionPendingError, P as clearRevokedAddresses, Q as releaseRegistrationsForProfile, R as runAddressNotesRequest, S as runAgentConnect, St as refreshOwnerMemberAddressPeriodically, T as verificationReplySubmitted, U as agentConnectionStatus, V as contactAgentAddress, W as agentInvitationHash, X as savedReceiver, Y as saveConnectionName, Z as MACHINE_RUNTIMES, _ as enrollmentResumes, _t as jsonIndentation, a as endSession, at as describeSessionHookItem, b as invitationProfileName, bt as writeManagedFile, c as pendingSessionDisconnects, ct as findOnPath, d as registerSession, dt as readClaudeSettings, et as recordHookMailCheck, f as retryPendingDisconnects, ft as repairClaudeHooks, g as enrollAgent, gt as inspectManagedBlock, h as staleSessionAgents, ht as MachineFileError, i as connectedProfilesForSession, it as describeSessionHookChange, j as canonicalContactSelector, k as createNotificationContactPolicy, l as psProcessInfo, lt as hasMachineSessionHooks, m as runDetached, mt as writeClaudeSettings, n as OMP_MAIN, nt as claudeConfigDir, o as headlessClaudeRun, ot as ensureHookLauncher, p as revokeStaleSessionAgents, pt as samePath, q as receiverStatusCommand, r as agentConnectionState, rt as currentCliLocation, st as ephemeralCliReason, t as HOOK_END_GRACE_MS, tt as boundSessionProfiles, u as readClaudeHookInput, ut as inspectClaudeHooks, v as connectWarnings, vt as readManagedFile, w as externalReceivingState, x as pollCheckCommand, xt as readHookLauncherWarning, y as defaultAgentProfileName, yt as upsertManagedBlock, z as ContactsApiError } from "../machine-session-Be6j29GE.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-DTFkP360.js";
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-DeEEZPbQ.js";
1
+ import { $ as AutomatedFilterUnsupportedError, $t as listDomains, A as sleep$3, An as setMemory, At as getAgentContactPolicy, B as hasReplyState, Bn as updateRepeatingSend, Bt as getMemory, C as collectNewAcceptedEmails, Ct as deleteWakeSchedule, D as filtersFromFlags, Dn as sendEmail, Dt as downloadSentAttachmentArchiveMember, E as fetchEmailSearchPage, En as semanticSearch, F as assertAwaitingFilterRows, Fn as stopRepeatFromEmail, Ft as getEmail, Gn as verifyAgentSignup, Gt as getWakeSchedule, H as queryUsesAwaiting, Hn as updateWakeAuthorization, Ht as getRepeatingSend, I as awaitingRejectedError, In as testFunction, It as getFunction, J as parseWebhookEvent, Jt as listAgentContacts, K as isEmailReceivedEvent, L as ensureSearchReportsReplyState, Ln as unsetFunctionRoute, Lt as getFunctionRouting, M as AWAITING_VALUES, Mn as startAgentClaim, Mt as getConversation, N as REPLY_STATE_UNSUPPORTED_CODE, Nn as startAgentSignup, Nt as getCreditBalance, O as quoteDslValue, On as setFunctionRoute, Ot as downloadSentAttachmentByFilename, P as ReplyStateUnsupportedError, Pn as startCliLogin, Pt as getDefaultNetworkAgent, Q as AUTOMATED_VALUES, Qt as listDefaultNetworkMembers, R as formatAwaitingCell, Rn as updateDefaultNetworkMember, Rt as getFunctionTestRunTrace, S as transientFailure, Sn as runWakeSchedule, St as deleteWakeAuthorization, T as encodeReceivedAtSearchCursor, Tn as searchMemories, Tt as downloadEmailAttachmentByFilename, Un as updateWakeSchedule, Ut as getSentEmail, V as isAwaitingRejectedError, Vn as updateRoute, Vt as getOrgRoutingTopology, Wn as verifyAgentClaim, Wt as getThread, X as AUTOMATED_FILTER_UNSUPPORTED_CODE, Y as parseFromHeader, Yt as listAgentNetworks, Z as AUTOMATED_FLAG_DESCRIPTION, Zt as listDefaultNetworkAgents, _ as describeApiFailure, _n as redeemCreditCode, _t as createWakeSchedule, a as REPEAT_IDLE_MAX_MINUTES, an as listRepeatingSends, at as hasAutomatedVerdict, b as retryTransient, bn as replyToEmail, bt as deleteMemory, cn as listSentEmails, ct as cancelSentEmail, d as parseRepeatTick, dn as listWakeSchedules, dt as cliLogout, en as listEmailAttachmentArchiveMembers, et as assertAutomatedVerdict, f as preparePresenceAliveEmail, fn as pollCliLogin, ft as completeWebhookEvent, g as WaitDeadlineReached, gt as createWakeAuthorization, ht as createRoute, i as REPEAT_EVERY_MAX_MINUTES, in as listFunctions, j as AWAITING_FLAG_DESCRIPTION, jn as simulateRoute, k as sinceFromFlags, kn as setFunctionSecret, kt as getAccount, l as parseInteractionEnvelope$1, ln as listWakeAuthorizations, lt as cancelSentEmailByIdempotencyKey, m as repeatStopCommand, mt as createFunction, nn as listEndpoints, o as REPEAT_MAX_SENDS_MAX, on as listRoutes, ot as isAutomatedRejectedError, p as prepareSignalEmail, pn as pullWebhookEvent, pt as createEndpoint, rn as listFunctionLogs, s as buildRepeatStopBody, sn as listSentAttachmentArchiveMembers, st as addDefaultNetworkMember, tn as listEmails, tt as automatedRejectedError, u as parsePresenceEnvelope, un as listWakeDispatches, ut as checkDefaultNetworkContactAdmission, vn as removeDefaultNetworkMember, w as cursorFromAcceptedRows, wn as searchEmails, wt as downloadEmailAttachmentArchiveMember, x as retryingOperation, xn as resendAgentSignupVerification, xt as deleteRoute, y as pauseForTransientFailure, yn as reorderRoutes, z as formatRepliesCell, zn as updateFunction, zt as getInboxStatus } from "../repeats-BL7ZC-_6-B_ESsWTH.js";
2
+ import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, F as agentProfileName, G as PrimitiveApiClient, I as agentProfilesDirectory, M as AGENT_PROFILE_ENV, N as AgentConnectionSetupError, O as loadActiveChatState, P as agentProfileDirectory, T as saveSignupCredentials, U as requireDefaultLoginProfile, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as AGENT_ADDRESS_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, z as loadConnectedAgentProfile } from "../cli-config-C0Y_Ppol.js";
3
+ 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-CZfm7E2k.js";
4
+ import { $ as formatDeletedEarlierSendNotice, A as clearReadPendingMail, At as createCliApiClient, C as serverRelationship, Ct as withFlagSuggestion, D as wakeRelationship, Dt as ConnectedAgentKeyRequiredError, E as wakeRecipientField, Et as withAttachmentWait, F as interactionHumanLine, G as assertValidIdempotencyKey, H as MAX_SEND_WAIT_TIMEOUT_MS, I as interactionNextActions, J as checkPriorReplies, K as buildFollowUpCommand, L as readEmailInteraction, M as readPendingMail, Mt as resolveCliApiRequestConfig, N as recordPendingMail, O as claimPendingMail, P as removePendingMail, Q as followUpCommandPrefix, R as replyExpectation, S as serverMuted, St as writeIdempotentReplayBannerIfReplay, T as wakeReadCommand, Tt as ATTACHMENT_WAIT_MAX_MS, U as SEND_OUTCOME_HELP, V as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, W as apiResultHttpStatus, X as deriveSendIdempotencyKey, Y as classifySendError, Z as excludeSignalReplies, _ as displayAddress, _t as generatedAgentName, a as extractErrorCode, at as reportSendCommandResult, b as readThreadContext, bt as currentMailSessionKey, c as hasControlCharacter$1, ct as sendWaitClampNotice, d as runWithTiming, dt as successfulSendOutcome, et as formatPriorRepliesCheckSkipped, f as surfaceUnauthorizedHint, ft as thrownErrorExitCode, g as describeWake, h as bareAddress, ht as connectNextSteps, i as createOperationCommand, it as priorRepliesThatWentOut, j as pendingMailPath, jt as refreshStoredCliCredentials, k as clearConsumedPendingMail, kt as createAuthenticatedCliApiClient, l as headerFlagName, lt as sentHistoryWindowStart, m as explainPendingMailNotFound, mt as cliInvocation, n as API_ERROR_CODES, nt as formatSendFailureSummary, o as extractErrorPayload, ot as sendIdentityFields, p as writeErrorWithHints, q as buildThrownSendFailureEnvelope, r as TIME_FLAG_DESCRIPTION, rt as formatSendRecordFailureSummary, s as formatErrorPayload, st as sendOutcomeExitCode, t as API_BASE_URL_FLAG_DESCRIPTION, tt as formatPriorRepliesWarning, u as readTextFileFlag, ut as serializeErrorPayload, v as formatMailWakeLine, vt as identitySuggestions, wt as ATTACHMENT_WAIT_FLAG_DESCRIPTION, x as sentInThread, xt as formatAlreadySentNotice, y as latestOwnOutbound, yt as loadSkillLine, z as replyInteractionWarning } from "../api-command-fKP3NYa7.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-DJAb9SGm.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-B6V5-grn.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-DAiOb622.js";
8
+ import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background-cD6n8VYO.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-Cte4mAL3.js";
10
10
  import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, readWorkingLease, restoreAutoWorking } from "./auto-signals.js";
11
11
  import { n as jsonOutputRequested, r as runStreamingJson, t as guardJsonOutputCommands } from "../json-output-Ce10AsSb.js";
12
12
  import { Args, Command, Errors, Flags, ux } from "@oclif/core";
@@ -40006,7 +40006,7 @@ const RETRYABLE_STATUS = /* @__PURE__ */ new Set([
40006
40006
  503,
40007
40007
  504
40008
40008
  ]);
40009
- function sleep$3(ms) {
40009
+ function sleep$2(ms) {
40010
40010
  return new Promise((resolve) => setTimeout(resolve, ms));
40011
40011
  }
40012
40012
  /**
@@ -40028,7 +40028,7 @@ async function retryingFetch(url, init, label, maxRetries = DEFAULT_MAX_RETRIES)
40028
40028
  throw new Error(`${label}: network error after ${attempt} retries: ${networkError?.message ?? networkError}`);
40029
40029
  }
40030
40030
  if (res) await res.text().catch(() => {});
40031
- await sleep$3(Math.min(1e3 * 2 ** attempt, 15e3) + Math.floor(Math.random() * 250));
40031
+ await sleep$2(Math.min(1e3 * 2 ** attempt, 15e3) + Math.floor(Math.random() * 250));
40032
40032
  }
40033
40033
  }
40034
40034
  async function initiate(opts, manifest) {
@@ -40371,7 +40371,7 @@ function withClearedNote(message, cleared) {
40371
40371
  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.`;
40372
40372
  }
40373
40373
  var AgentConnectCommand = class AgentConnectCommand extends Command {
40374
- 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, writes a private AGENT_RESUME note holding the command that resumes the bound session ("claude --resume <session>" or "codex resume <session>") once verified (or deletes an existing one when no resumable session is bound), 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.`;
40374
+ 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, writes a private AGENT_RESUME note holding the command that resumes the bound session ("claude --resume <session>" or "codex resume <session>") once verified (or deletes an existing one when no resumable session is bound), 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. A profile whose earlier setup or enrollment never saved a credential is not an address: a pasted invitation is claimed into it (its unfinished state is moved aside), and the result's replacedUnfinished says whether that finished the same pending address. 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, and reports the session's last registration failure as registration when one was recorded. 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.`;
40375
40375
  static summary = "Connect and verify an agent address";
40376
40376
  static examples = [
40377
40377
  "<%= config.bin %> agent connect --session \"$CODEX_THREAD_ID\" --name Research --info \"Reviews pull requests\" --json < private-invitation.txt",
@@ -40444,12 +40444,14 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
40444
40444
  });
40445
40445
  if (picked.profile === null && picked.session === null) throw new AgentConnectionSetupError(`Pass --profile <name>, --session <id> or set ${AGENT_PROFILE_ENV}.`);
40446
40446
  if (picked.profile === null) {
40447
- const detail = statusProfileMissingDetail(picked.bound);
40447
+ const registration = picked.bound.length === 0 && picked.session ? registrationSummary(this.config.configDir, defaultAgentProfileName(picked.session)) : null;
40448
+ const detail = registration ? `${statusProfileMissingDetail(picked.bound)} ${registrationSummaryLine(registration)}` : statusProfileMissingDetail(picked.bound);
40448
40449
  this.log(flags.json ? JSON.stringify({
40449
40450
  status: "not_configured",
40450
40451
  profileName: null,
40451
40452
  session: picked.session,
40452
40453
  ...picked.bound.length > 1 ? { profiles: picked.bound } : {},
40454
+ ...registration ? { registration } : {},
40453
40455
  detail
40454
40456
  }) : detail);
40455
40457
  process.exitCode = 1;
@@ -40457,20 +40459,25 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
40457
40459
  }
40458
40460
  const profileName = picked.profile;
40459
40461
  const result = agentConnectionStatus(this.config.configDir, profileName);
40462
+ const registration = registrationSummary(this.config.configDir, profileName);
40460
40463
  if (result.status !== "configured") {
40461
- const detail = `Agent profile ${profileName} is not configured. Run \`primitive agent connect\` with the owner's invitation to connect it.`;
40464
+ const detail = registration ? `Agent profile ${profileName} is not configured. ${registrationSummaryLine(registration)}` : `Agent profile ${profileName} is not configured. Run \`primitive agent connect\` with the owner's invitation to connect it.`;
40462
40465
  this.log(flags.json ? JSON.stringify({
40463
40466
  ...result,
40467
+ ...registration ? { registration } : {},
40464
40468
  detail
40465
40469
  }) : detail);
40466
40470
  process.exitCode = 1;
40467
40471
  return;
40468
40472
  }
40469
- if (flags.json) this.log(JSON.stringify(result));
40473
+ if (flags.json) this.log(JSON.stringify(registration?.lastFailure ? {
40474
+ ...result,
40475
+ registration
40476
+ } : result));
40470
40477
  else {
40471
40478
  const receiving = result.receiving;
40472
40479
  const why = [receiving.reason, receiving.failureCode !== receiving.reason ? receiving.failureCode : null].filter(Boolean);
40473
- this.log(`Agent profile ${profileName} is configured for ${result.identity.agentAddress}. Local receiving: ${receiving.state}${why.length ? ` (${why.join(", ")})` : ""}.${receiving.detail ? ` ${receiving.detail}` : ""}${receiving.lastFiredAt ? ` Receive hook last ran at ${receiving.lastFiredAt}.` : ""}${receiving.lastSuccessfulMailCheckAt ? ` Last completed mail check at ${receiving.lastSuccessfulMailCheckAt}.` : ""}`);
40480
+ this.log(`Agent profile ${profileName} is configured for ${result.identity.agentAddress}. Local receiving: ${receiving.state}${why.length ? ` (${why.join(", ")})` : ""}.${receiving.detail ? ` ${receiving.detail}` : ""}${receiving.lastFiredAt ? ` Receive hook last ran at ${receiving.lastFiredAt}.` : ""}${receiving.lastSuccessfulMailCheckAt ? ` Last completed mail check at ${receiving.lastSuccessfulMailCheckAt}.` : ""}${registration?.lastFailure ? ` ${registrationSummaryLine(registration)}` : ""}`);
40474
40481
  }
40475
40482
  return;
40476
40483
  }
@@ -40855,7 +40862,7 @@ function invocation(entry) {
40855
40862
  }
40856
40863
  var AgentEnrollCommand = class AgentEnrollCommand extends Command {
40857
40864
  static summary = "Give this coding session an address in your organization";
40858
- static description = `On this trusted machine, use the saved member OAuth login to create one address for the exact loaded session, claim its one-use invitation privately, answer the email challenge, and poll the owner's connection list for confirmed pairing. Native receiving starts when supported. With --receiver external in the exact Claude session, install a fail-open Stop hook and resume SessionStart hook in that runtime's settings after the verification reply; idle wake remains unverified until tested with real mail. With --contact-requests, the login conditionally enables first-contact intake for this exact address after verification, preserving existing policy rules. An explicit disable or uncertain policy update is reported separately without losing pairing or receiver setup. The server allocates a readable address. An uncertain creation resumes the same saved request; recovered results do not include another invitation. Use --continue-setup once to explicitly continue a recovered pending connection. An uncertain continuation or claim is held for owner recovery, never repeated automatically. Requires a verified Primitive-managed domain. This command does not accept API keys, a connection profile, or an invitation argument. One address per session: if this session already has another connected Primitive address, nothing is created and the command exits 3 with status already_connected; 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.`;
40865
+ static description = `On this trusted machine, use the saved member OAuth login to create one address for the exact loaded session, claim its one-use invitation privately, answer the email challenge, and poll the owner's connection list for confirmed pairing. Native receiving starts when supported. With --receiver external in the exact Claude session, install a fail-open Stop hook and resume SessionStart hook in that runtime's settings after the verification reply; idle wake remains unverified until tested with real mail. With --contact-requests, the login conditionally enables first-contact intake for this exact address after verification, preserving existing policy rules. An explicit disable or uncertain policy update is reported separately without losing pairing or receiver setup. The server allocates a readable address. An uncertain creation resumes the same saved request; recovered results do not include another invitation. Use --continue-setup once to explicitly continue a recovered pending connection. An uncertain continuation is held for owner recovery, never repeated automatically. When a claim left no saved credential (it was never sent, was refused, or its answer was lost), a rerun signed in as the agent's owner in the same organization asks Primitive for one fresh invitation for the same address and claims it; without that login the refusal names the step that finishes it from the app. Each attempt is recorded, without secrets, in the profile's registration log, which agent connect --status reports. Requires a verified Primitive-managed domain. This command does not accept API keys, a connection profile, or an invitation argument. One address per session: if this session already has another connected Primitive address, nothing is created and the command exits 3 with status already_connected; 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.`;
40859
40866
  static examples = ["<%= config.bin %> agent enroll --session 11111111-1111-4111-8111-111111111111 --name Research --contact-requests --json", "<%= config.bin %> agent enroll --session 11111111-1111-4111-8111-111111111111 --receiver external --name Research --json"];
40860
40867
  static flags = {
40861
40868
  session: Flags.string({
@@ -41002,7 +41009,7 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
41002
41009
  }
41003
41010
  if (externalHook === "unavailable" || flags["contact-requests"] && result.contactRequestPolicy !== "enabled" || result.connection.status !== "connected" || flags.receiver !== "external" && flags.receiver !== "poll" && result.receiving.state !== "healthy") process.exitCode = 2;
41004
41011
  } catch (error) {
41005
- throw new Errors.CLIError(error instanceof AgentConnectionSetupError ? error.message : "Enrollment paused. Preserve this session's private profile and inspect the pending agent in the app before retrying. No second address was requested automatically.", { exit: 1 });
41012
+ throw new Errors.CLIError(error instanceof AgentConnectionSetupError ? error.message : "Enrollment stopped on an unexpected error. Rerun the same command to resume this session's saved enrollment; it never requests a second address.", { exit: 1 });
41006
41013
  }
41007
41014
  }
41008
41015
  };
@@ -42601,7 +42608,7 @@ function searchFailure(response) {
42601
42608
  }
42602
42609
  /** Inspect an exact email ID obtained from a targeted query or event journal. */
42603
42610
  async function inspectTargetedReply(params) {
42604
- const result = await readBeforeDeadline(params.deadline, (signal) => getEmail({
42611
+ const result = await readApiBeforeDeadline(params.deadline, (signal) => getEmail({
42605
42612
  client: params.apiClient.client,
42606
42613
  path: { id: params.id },
42607
42614
  signal,
@@ -42609,7 +42616,8 @@ async function inspectTargetedReply(params) {
42609
42616
  }));
42610
42617
  if (result === null) return null;
42611
42618
  const email = result.data?.data;
42612
- if (result.error || !email || email.id !== params.id) throw invalid$2(`Could not inspect reply candidate ${params.id}.`);
42619
+ if (result.error) throw invalid$2(`Could not inspect reply candidate ${params.id}: ${describeApiFailure(result)}.`);
42620
+ if (!email || email.id !== params.id) throw invalid$2(`Could not inspect reply candidate ${params.id}.`);
42613
42621
  const presence = presenceDisposition(email);
42614
42622
  if (presence !== "ordinary") return {
42615
42623
  kind: presence === "pending" ? "pending" : "unrelated",
@@ -42685,7 +42693,7 @@ async function inspectTargetedReply(params) {
42685
42693
  /** One targeted recovery page. The event owner decides when recovery is needed. */
42686
42694
  async function readTargetedReplyPage(params) {
42687
42695
  if (!params.sentId || !params.from || !params.recipient) throw invalid$2("Targeted reply recovery requires the exact sent email, receiving address, and peer.");
42688
- const result = await readBeforeDeadline(params.deadline, (signal) => searchEmails({
42696
+ const result = await readApiBeforeDeadline(params.deadline, (signal) => searchEmails({
42689
42697
  client: params.apiClient.client,
42690
42698
  signal,
42691
42699
  query: {
@@ -42715,6 +42723,11 @@ async function readTargetedReplyPage(params) {
42715
42723
  }
42716
42724
  //#endregion
42717
42725
  //#region src/oclif/connected-reply-wait.ts
42726
+ /**
42727
+ * Slack for clock differences between this machine, which records when a
42728
+ * wait was created, and the server, which records when mail was received.
42729
+ */
42730
+ const REPLY_CLOCK_SKEW_MS = 6e5;
42718
42731
  function recoveryEventId(id) {
42719
42732
  const h = createHash("sha256").update(`reply-recovery:${id}`).digest("hex");
42720
42733
  return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
@@ -42802,6 +42815,20 @@ async function openConnectedReplyWait(options) {
42802
42815
  let closed = false;
42803
42816
  let closing;
42804
42817
  const timedOut = () => options.deadline != null && Date.now() >= options.deadline;
42818
+ let earliestReply;
42819
+ /**
42820
+ * The earliest receipt time a reply to this wait's send can have: the wait
42821
+ * is registered before the send, so mail received well before it is
42822
+ * retained history, not a reply.
42823
+ */
42824
+ async function replyFloor() {
42825
+ if (earliestReply !== void 0) return earliestReply;
42826
+ const intent = await store.readWait(requestId);
42827
+ const created = Date.parse(intent?.createdAt ?? "");
42828
+ const earlier = [options.sentAt, options.since].map((value) => Date.parse(value ?? "")).filter(Number.isFinite);
42829
+ earliestReply = Number.isFinite(created) ? Math.min(created, ...earlier) - REPLY_CLOCK_SKEW_MS : Number.NEGATIVE_INFINITY;
42830
+ return earliestReply;
42831
+ }
42805
42832
  async function release() {
42806
42833
  if (released) return;
42807
42834
  await (await openSharedMailStore({
@@ -42892,6 +42919,7 @@ async function openConnectedReplyWait(options) {
42892
42919
  requestId = bound.requestId;
42893
42920
  sentId = bound.sentEmailId ?? void 0;
42894
42921
  recoveryNeeded = true;
42922
+ earliestReply = void 0;
42895
42923
  },
42896
42924
  uncertain: () => store.markWaitUncertain(requestId),
42897
42925
  async cancelBeforeSend() {
@@ -42981,7 +43009,9 @@ async function openConnectedReplyWait(options) {
42981
43009
  limit: options.pageSize,
42982
43010
  cursor: localCursor
42983
43011
  });
43012
+ const floor = await replyFloor();
42984
43013
  for (const row of local.emails) {
43014
+ if (!row.details && Date.parse(row.receivedAt) < floor) continue;
42985
43015
  if (row.details && row.details.authorization !== "pending" && (row.details.peer !== options.recipient || row.details.replyToSentEmailId !== sentId)) continue;
42986
43016
  const reply = await inspect(row.emailId);
42987
43017
  if (reply) return reply;
@@ -43658,132 +43688,6 @@ function warnIfSharedProfile(params) {
43658
43688
  const warning = sharedProfileWarning(params);
43659
43689
  if (warning) process.stderr.write(`Warning: ${warning}\n`);
43660
43690
  }
43661
- //#endregion
43662
- //#region src/oclif/commands/emails-poll.ts
43663
- function quoteDslValue(value) {
43664
- if (/^[^\s"]+$/.test(value)) return value;
43665
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
43666
- }
43667
- function combineQ(q, domain) {
43668
- const parts = [q?.trim(), domain ? `domain:${quoteDslValue(domain.trim())}` : void 0].filter((part) => Boolean(part));
43669
- return parts.length > 0 ? parts.join(" ") : void 0;
43670
- }
43671
- function normalizeIsoDate(value, label) {
43672
- const parsed = new Date(value);
43673
- if (Number.isNaN(parsed.getTime())) throw new Error(`${label} must be a valid date or ISO-8601 timestamp.`);
43674
- return parsed.toISOString();
43675
- }
43676
- function filtersFromFlags(flags) {
43677
- if (flags.automated !== void 0 && !AUTOMATED_VALUES.includes(flags.automated)) throw new Error("--automated must be true or false.");
43678
- return {
43679
- automated: flags.automated === void 0 ? void 0 : flags.automated === "true",
43680
- awaiting: flags.awaiting,
43681
- body: flags.body,
43682
- domain: flags.domain,
43683
- domainId: flags["domain-id"],
43684
- from: flags.from,
43685
- hasAttachment: flags["has-attachment"],
43686
- q: flags.q,
43687
- replyToSentEmailId: flags["reply-to-sent-email-id"],
43688
- spamScoreGte: flags["spam-score-gte"],
43689
- spamScoreLt: flags["spam-score-lt"],
43690
- subject: flags.subject,
43691
- to: flags.to
43692
- };
43693
- }
43694
- function sinceFromFlags(flags) {
43695
- if (flags.since) return normalizeIsoDate(flags.since, "--since");
43696
- return flags["include-existing"] ? void 0 : (/* @__PURE__ */ new Date()).toISOString();
43697
- }
43698
- function buildEmailSearchQuery(params) {
43699
- const query = {
43700
- include_facets: "false",
43701
- limit: params.pageSize,
43702
- snippet: "false",
43703
- sort: "received_at_asc"
43704
- };
43705
- const q = combineQ(params.filters.q, params.filters.domain);
43706
- if (q) query.q = q;
43707
- if (params.filters.awaiting) query.awaiting = params.filters.awaiting;
43708
- if (params.filters.automated !== void 0) query.automated = params.filters.automated ? "true" : "false";
43709
- if (params.filters.body) query.body = params.filters.body;
43710
- if (params.filters.domainId) query.domain_id = params.filters.domainId;
43711
- if (params.filters.from) query.from = params.filters.from;
43712
- if (params.filters.hasAttachment !== void 0) query.has_attachment = params.filters.hasAttachment ? "true" : "false";
43713
- if (params.filters.spamScoreGte !== void 0) query.spam_score_gte = params.filters.spamScoreGte;
43714
- if (params.filters.spamScoreLt !== void 0) query.spam_score_lt = params.filters.spamScoreLt;
43715
- if (params.filters.replyToSentEmailId) query.reply_to_sent_email_id = params.filters.replyToSentEmailId;
43716
- if (params.filters.subject) query.subject = params.filters.subject;
43717
- if (params.filters.to) query.to = params.filters.to;
43718
- if (params.since) query.date_from = params.since;
43719
- if (params.cursor) query.cursor = params.cursor;
43720
- return query;
43721
- }
43722
- function encodeReceivedAtSearchCursor(email) {
43723
- const raw = `r|${new Date(email.received_at).toISOString()}|${email.id}`;
43724
- return Buffer.from(raw, "utf8").toString("base64url");
43725
- }
43726
- function cursorFromRows(rows) {
43727
- const last = rows.at(-1);
43728
- return last ? encodeReceivedAtSearchCursor(last) : null;
43729
- }
43730
- function cursorFromAcceptedRows(rows) {
43731
- for (let i = rows.length - 1; i >= 0; i--) {
43732
- const row = rows[i];
43733
- if (row.status === "accepted" || row.status === "completed") return encodeReceivedAtSearchCursor(row);
43734
- }
43735
- return null;
43736
- }
43737
- function collectNewAcceptedEmails(rows, seenIds) {
43738
- const fresh = [];
43739
- for (const row of rows) {
43740
- if (row.status !== "accepted" && row.status !== "completed") continue;
43741
- if (seenIds.has(row.id)) continue;
43742
- seenIds.add(row.id);
43743
- fresh.push(row);
43744
- }
43745
- return fresh;
43746
- }
43747
- async function fetchEmailSearchPage(params) {
43748
- const result = await searchEmails({
43749
- client: params.apiClient.client,
43750
- query: buildEmailSearchQuery({
43751
- cursor: params.cursor,
43752
- filters: params.filters,
43753
- pageSize: params.pageSize,
43754
- since: params.since
43755
- }),
43756
- responseStyle: "fields"
43757
- });
43758
- const wantsReplyState = Boolean(params.filters.awaiting) || queryUsesAwaiting(params.filters.q);
43759
- const wantsAutomated = params.filters.automated !== void 0 || queryUsesAutomated(params.filters.q);
43760
- if (result.error) {
43761
- if (wantsReplyState && isAwaitingRejectedError(result.error)) throw awaitingRejectedError("GET /emails/search");
43762
- if (wantsAutomated && isAutomatedRejectedError(result.error)) throw automatedRejectedError("GET /emails/search");
43763
- return {
43764
- ok: false,
43765
- error: result.error
43766
- };
43767
- }
43768
- const envelope = result.data;
43769
- const rows = envelope?.data ?? [];
43770
- if (wantsReplyState) {
43771
- assertAwaitingFilterRows(rows, "GET /emails/search");
43772
- if (rows.length === 0) await ensureSearchReportsReplyState(params.apiClient);
43773
- }
43774
- if (wantsAutomated) {
43775
- assertAutomatedVerdict(rows, "GET /emails/search", expectedAutomatedVerdicts(params.filters.automated, params.filters.q));
43776
- if (rows.length === 0) await ensureSearchReportsAutomated(params.apiClient);
43777
- }
43778
- return {
43779
- ok: true,
43780
- cursor: envelope?.meta.cursor ?? cursorFromRows(rows),
43781
- rows
43782
- };
43783
- }
43784
- function sleep$2(ms) {
43785
- return new Promise((resolve) => setTimeout(resolve, ms));
43786
- }
43787
43691
  function cliError$6(message) {
43788
43692
  return new Errors.CLIError(message, { exit: 1 });
43789
43693
  }
@@ -45399,10 +45303,23 @@ async function waitForReply(params) {
45399
45303
  deadline: totalDeadline
45400
45304
  });
45401
45305
  let strictFilterUnsupported = false;
45306
+ let notBefore = 0;
45307
+ const holdRetryAfter = (failure) => {
45308
+ if (failure.retryAfterMs !== void 0) notBefore = Math.max(notBefore, Date.now() + failure.retryAfterMs);
45309
+ };
45402
45310
  for (const phase of phases) {
45403
45311
  if (phase.label === "strict" && strictFilterUnsupported) continue;
45312
+ if (notBefore > Date.now()) {
45313
+ const remaining = totalDeadline === null ? Number.POSITIVE_INFINITY : totalDeadline - Date.now();
45314
+ if (remaining <= notBefore - Date.now()) {
45315
+ await sleep$3(Math.max(0, remaining));
45316
+ return null;
45317
+ }
45318
+ await sleep$3(notBefore - Date.now());
45319
+ }
45404
45320
  const seenIds = /* @__PURE__ */ new Set();
45405
45321
  let cursor = null;
45322
+ let failures = 0;
45406
45323
  while (true) {
45407
45324
  if (phase.deadline !== null && Date.now() >= phase.deadline) break;
45408
45325
  const page = await fetchEmailSearchPage({
@@ -45413,14 +45330,25 @@ async function waitForReply(params) {
45413
45330
  since: params.sentAtIso
45414
45331
  });
45415
45332
  if (!page.ok) {
45333
+ const failure = transientFailure(page);
45334
+ if (failure) {
45335
+ if (!await pauseForTransientFailure(failure, failures, {
45336
+ deadline: phase.deadline,
45337
+ notice,
45338
+ onTransientFailure: holdRetryAfter
45339
+ })) break;
45340
+ failures += 1;
45341
+ continue;
45342
+ }
45416
45343
  const payload = extractErrorPayload(page.error);
45417
- writeErrorWithHints(payload);
45344
+ writeErrorWithHints(payload, page.response);
45418
45345
  surfaceUnauthorizedHint({
45419
45346
  ...params.authFailureContext,
45420
45347
  payload
45421
45348
  });
45422
45349
  throw new Errors.CLIError("Failed to poll for reply.", { exit: 1 });
45423
45350
  }
45351
+ failures = 0;
45424
45352
  let lastAccepted;
45425
45353
  for (let i = page.rows.length - 1; i >= 0; i--) {
45426
45354
  const row = page.rows[i];
@@ -45432,14 +45360,19 @@ async function waitForReply(params) {
45432
45360
  if (lastAccepted) cursor = encodeReceivedAtSearchCursor(lastAccepted);
45433
45361
  const matches = collectNewAcceptedEmails(page.rows, seenIds);
45434
45362
  for (const match of matches) {
45435
- const full = await getEmail({
45363
+ const full = await retryTransient(() => getEmail({
45436
45364
  client: params.apiClient.client,
45437
45365
  path: { id: match.id },
45438
45366
  responseStyle: "fields"
45367
+ }), {
45368
+ deadline: phase.deadline,
45369
+ notice,
45370
+ onTransientFailure: holdRetryAfter
45439
45371
  });
45372
+ if (full === null) break;
45440
45373
  if (full.error) {
45441
45374
  const payload = extractErrorPayload(full.error);
45442
- writeErrorWithHints(payload);
45375
+ writeErrorWithHints(payload, full.response);
45443
45376
  surfaceUnauthorizedHint({
45444
45377
  ...params.authFailureContext,
45445
45378
  payload
@@ -45463,7 +45396,7 @@ async function waitForReply(params) {
45463
45396
  if (lastAccepted !== void 0) continue;
45464
45397
  if (phase.deadline !== null && Date.now() >= phase.deadline) break;
45465
45398
  if (totalDeadline !== null && Date.now() >= totalDeadline) return null;
45466
- await sleep$2(params.interval * 1e3);
45399
+ await sleep$3(params.interval * 1e3);
45467
45400
  }
45468
45401
  }
45469
45402
  return null;
@@ -47622,7 +47555,7 @@ async function resolveScopedEmailWait(params) {
47622
47555
  const requestedTo = to === void 0 ? void 0 : exactAddress(to, "--to");
47623
47556
  if (Object.values(unsupported).some((value) => value !== void 0 && value !== false)) throw invalid$1("Connected email waits support exact --from, --to, and --reply-to-sent-email-id matching only; search, body, subject, domain, attachment, and spam filters are unavailable.");
47624
47557
  const sentId = replyToSentEmailId.toLowerCase();
47625
- const result = await readBeforeDeadline(params.deadline, (signal) => getSentEmail({
47558
+ const result = await readApiBeforeDeadline(params.deadline, (signal) => getSentEmail({
47626
47559
  signal,
47627
47560
  client: params.apiClient.client,
47628
47561
  path: { id: sentId },
@@ -47630,7 +47563,8 @@ async function resolveScopedEmailWait(params) {
47630
47563
  }));
47631
47564
  if (result === null) return null;
47632
47565
  const sent = result.data?.data;
47633
- if (result.error || !sent || sent.id !== sentId) throw invalid$1("Could not read that sent email with the connected credential. Inspect the existing send before retrying; do not send it again.");
47566
+ if (result.error) throw invalid$1(`Could not read that sent email with the connected credential: ${describeApiFailure(result)}. Inspect the existing send before retrying; do not send it again.`);
47567
+ if (!sent || sent.id !== sentId) throw invalid$1("Could not read that sent email with the connected credential. Inspect the existing send before retrying; do not send it again.");
47634
47568
  const ownAddress = exactAddress(sent.from_address, "The sent email's from_address");
47635
47569
  if (sent.from_header != null && sent.from_header !== "") {
47636
47570
  const header = typeof sent.from_header === "string" ? parseFromHeader(sent.from_header) : null;
@@ -47640,7 +47574,8 @@ async function resolveScopedEmailWait(params) {
47640
47574
  return {
47641
47575
  from: ownAddress,
47642
47576
  recipient,
47643
- sentId
47577
+ sentId,
47578
+ ...typeof sent.created_at === "string" ? { sentAt: sent.created_at } : {}
47644
47579
  };
47645
47580
  }
47646
47581
  //#endregion
@@ -47665,7 +47600,10 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
47665
47600
  Connected waits share an address event subscription and recover replies with
47666
47601
  exact-parent search. They never scan inbox history. Additional content
47667
47602
  filters. The command never sends mail. On timeout it exits 1; run the same
47668
- wait again to recover a reply to the existing send without resending it.`;
47603
+ wait again to recover a reply to the existing send without resending it.
47604
+ A rate limit (HTTP 429, honouring Retry-After), a 502, 503 or 504, or a
47605
+ network error during the wait is retried with backoff until --timeout,
47606
+ with one note on stderr per retry.`;
47669
47607
  static summary = "Wait for matching inbound emails";
47670
47608
  static examples = [
47671
47609
  "<%= config.bin %> emails wait --reply-to-sent-email-id <sent-id> --from peer@example.com",
@@ -47776,6 +47714,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
47776
47714
  const idWidth = pickIdWidth(Boolean(process.stdout.isTTY));
47777
47715
  const seenIds = /* @__PURE__ */ new Set();
47778
47716
  let cursor = null;
47717
+ let failures = 0;
47779
47718
  let matched = 0;
47780
47719
  let headerPrinted = false;
47781
47720
  const printEmail = (email) => {
@@ -47834,8 +47773,14 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
47834
47773
  throw error;
47835
47774
  }
47836
47775
  if (!page.ok) {
47776
+ const failure = transientFailure(page);
47777
+ if (failure) {
47778
+ if (!await pauseForTransientFailure(failure, failures, { deadline })) break;
47779
+ failures += 1;
47780
+ continue;
47781
+ }
47837
47782
  const payload = extractErrorPayload(page.error);
47838
- writeErrorWithHints(payload);
47783
+ writeErrorWithHints(payload, page.response);
47839
47784
  surfaceUnauthorizedHint({
47840
47785
  auth,
47841
47786
  baseUrlOverridden,
@@ -47845,6 +47790,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
47845
47790
  process.exitCode = 1;
47846
47791
  return;
47847
47792
  }
47793
+ failures = 0;
47848
47794
  const nextCursor = cursorFromAcceptedRows(page.rows);
47849
47795
  const cursorAdvanced = Boolean(nextCursor && nextCursor !== cursor);
47850
47796
  if (nextCursor) cursor = nextCursor;
@@ -47854,7 +47800,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
47854
47800
  }
47855
47801
  if (cursorAdvanced) continue;
47856
47802
  if (deadline !== null && Date.now() >= deadline) break;
47857
- await sleep$2(flags.interval * 1e3);
47803
+ await sleep$3(flags.interval * 1e3);
47858
47804
  }
47859
47805
  const message = `Timed out waiting for ${flags.number} matching email${flags.number === 1 ? "" : "s"}; received ${matched}.`;
47860
47806
  this.timedOut = {
@@ -47948,6 +47894,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
47948
47894
  const idWidth = pickIdWidth(Boolean(process.stdout.isTTY));
47949
47895
  const seenIds = /* @__PURE__ */ new Set();
47950
47896
  let cursor = null;
47897
+ let failures = 0;
47951
47898
  let printed = 0;
47952
47899
  let headerPrinted = false;
47953
47900
  while (deadline === null || Date.now() < deadline) {
@@ -47965,8 +47912,14 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
47965
47912
  throw error;
47966
47913
  }
47967
47914
  if (!page.ok) {
47915
+ const failure = transientFailure(page);
47916
+ if (failure) {
47917
+ if (!await pauseForTransientFailure(failure, failures, { deadline })) break;
47918
+ failures += 1;
47919
+ continue;
47920
+ }
47968
47921
  const payload = extractErrorPayload(page.error);
47969
- writeErrorWithHints(payload);
47922
+ writeErrorWithHints(payload, page.response);
47970
47923
  surfaceUnauthorizedHint({
47971
47924
  auth,
47972
47925
  baseUrlOverridden,
@@ -47976,6 +47929,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
47976
47929
  process.exitCode = 1;
47977
47930
  return;
47978
47931
  }
47932
+ failures = 0;
47979
47933
  const nextCursor = cursorFromAcceptedRows(page.rows);
47980
47934
  const cursorAdvanced = Boolean(nextCursor && nextCursor !== cursor);
47981
47935
  if (nextCursor) cursor = nextCursor;
@@ -47993,7 +47947,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
47993
47947
  }
47994
47948
  if (cursorAdvanced) continue;
47995
47949
  if (deadline !== null && Date.now() >= deadline) break;
47996
- await sleep$2(flags.interval * 1e3);
47950
+ await sleep$3(flags.interval * 1e3);
47997
47951
  }
47998
47952
  }
47999
47953
  };
@@ -49155,7 +49109,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
49155
49109
  url: "https://primitive.dev"
49156
49110
  };
49157
49111
  const SDK_VERSION_RANGE = "^2.0.0";
49158
- const CLI_VERSION_RANGE = "^1.72.2";
49112
+ const CLI_VERSION_RANGE = "^1.73.0";
49159
49113
  const ESBUILD_VERSION_RANGE = "^0.27.0";
49160
49114
  function renderHandler() {
49161
49115
  return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
@@ -50020,7 +49974,7 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
50020
49974
  return;
50021
49975
  }
50022
49976
  completedInitialFollowPoll = true;
50023
- await sleep$2(flags["poll-interval"] * 1e3);
49977
+ await sleep$3(flags["poll-interval"] * 1e3);
50024
49978
  }
50025
49979
  });
50026
49980
  }
@@ -50931,7 +50885,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
50931
50885
  trace = fetched;
50932
50886
  break;
50933
50887
  }
50934
- await sleep$2(pollIntervalMs);
50888
+ await sleep$3(pollIntervalMs);
50935
50889
  }
50936
50890
  if (!trace) {
50937
50891
  const finalTrace = ((await getFunctionTestRunTrace({
@@ -51096,6 +51050,32 @@ const DEFAULT_API = {
51096
51050
  getEmail,
51097
51051
  listEmails
51098
51052
  };
51053
+ /**
51054
+ * The API for a wait: a rate limit, gateway failure or network error backs
51055
+ * off and reads again until the deadline (null: no deadline), then throws
51056
+ * WaitDeadlineReached. These are all reads, so a retry repeats nothing.
51057
+ */
51058
+ function waitingInboxApi(deadline, api = DEFAULT_API) {
51059
+ const options = { deadline };
51060
+ return {
51061
+ ...api,
51062
+ getConversation: retryingOperation(api.getConversation, options),
51063
+ getEmail: retryingOperation(api.getEmail, options),
51064
+ listEmails: retryingOperation(api.listEmails, options, (input) => {
51065
+ const query = input.query;
51066
+ if (deadline === null || query?.wait === void 0) return input;
51067
+ const remainingMs = deadline - Date.now();
51068
+ if (remainingMs <= 0) return null;
51069
+ return {
51070
+ ...input,
51071
+ query: {
51072
+ ...query,
51073
+ wait: Math.min(query.wait, Math.floor(remainingMs / 1e3))
51074
+ }
51075
+ };
51076
+ })
51077
+ };
51078
+ }
51099
51079
  async function listPage(api, apiClient, query) {
51100
51080
  let result = await api.listEmails({
51101
51081
  client: apiClient.client,
@@ -51415,7 +51395,7 @@ var InboxNextCommand = class InboxNextCommand extends Command {
51415
51395
 
51416
51396
  NOT A WORK QUEUE. Nothing is claimed or locked. Two agents running \`inbox next\` on the same inbox get the same email until one of them replies, and both may answer it. Run one agent per inbox, or coordinate outside Primitive.
51417
51397
 
51418
- --wait blocks until something awaits you. It takes the inbox's newest position before checking, then long-polls from that position, re-checking reply state whenever mail arrives and at least every 30 seconds, so nothing that arrives between the check and the wait is missed.
51398
+ --wait blocks until something awaits you. It takes the inbox's newest position before checking, then long-polls from that position, re-checking reply state whenever mail arrives and at least every 30 seconds, so nothing that arrives between the check and the wait is missed. A rate limit (HTTP 429, honouring Retry-After), a 502, 503 or 504, or a network error during the wait is retried with backoff until --timeout, with one note on stderr per retry; if it lasts until then, the wait exits 5 as if nothing arrived.
51419
51399
 
51420
51400
  --json prints one stable envelope (version 1): \`outcome\` ("email" | "empty" | "error"), \`email\` (id, thread_id, message_id, received_at, from, from_email, to, subject, awaiting, reply_count, last_replied_at, body_text, from_known_address, auth { spf, dmarc }), \`automated\` ({ automated, reasons[], automation_headers_known }, the server's verdict), \`conversation\` (thread_id, subject, message_count, truncated, messages[] with role user|assistant), \`reply_command\`, \`interaction\` ({ hint, kind, fyi, category, plain_reply_completes, no_reply_needed }, decided by the server from a DKIM-authenticated interaction part; null when not reported), \`next_actions\` (the commands that answer the email, best first: each has kind, command, argv, description, placeholders, requires_message; empty when no reply is needed or this CLI cannot answer the interaction), \`automated_awaiting\` ({ total, capped }: automated mail also awaiting a reply, on the empty outcome; null otherwise), and \`error\` ({ code, message }) on failure.
51421
51401
 
@@ -51480,12 +51460,17 @@ var InboxNextCommand = class InboxNextCommand extends Command {
51480
51460
  process.exitCode = INBOX_NEXT_EXIT_CODES.error;
51481
51461
  };
51482
51462
  const deadline = flags.wait && flags.timeout > 0 ? Date.now() + flags.timeout * 1e3 : null;
51463
+ const api = flags.wait ? waitingInboxApi(deadline) : void 0;
51483
51464
  try {
51484
- let since = flags.wait ? await baselineCursor({ apiClient }) : null;
51465
+ let since = flags.wait ? await baselineCursor({
51466
+ apiClient,
51467
+ api
51468
+ }) : null;
51485
51469
  let announcedWait = false;
51486
51470
  for (;;) {
51487
51471
  const result = await findNextAwaiting({
51488
51472
  apiClient,
51473
+ api,
51489
51474
  includeAutomated: flags["include-automated"]
51490
51475
  });
51491
51476
  if (result.outcome === "email" || !flags.wait || since === null) {
@@ -51509,12 +51494,20 @@ var InboxNextCommand = class InboxNextCommand extends Command {
51509
51494
  const pollStartedAt = Date.now();
51510
51495
  since = await waitForActivity({
51511
51496
  apiClient,
51497
+ api,
51512
51498
  since,
51513
51499
  seconds: Math.floor(remainingMs / 1e3)
51514
51500
  });
51515
51501
  if (since === before && Date.now() - pollStartedAt < 1e3) await sleep$1(1e3);
51516
51502
  }
51517
51503
  } catch (error) {
51504
+ if (error instanceof WaitDeadlineReached) {
51505
+ await this.printResult({
51506
+ outcome: "empty",
51507
+ automated_awaiting: null
51508
+ }, bin, flags, apiClient);
51509
+ return;
51510
+ }
51518
51511
  if (error instanceof ReplyStateUnsupportedError || error instanceof AutomatedFilterUnsupportedError) {
51519
51512
  fail(error.code, error.message);
51520
51513
  return;
@@ -53241,7 +53234,9 @@ async function runStandaloneListen(options) {
53241
53234
  const random = options.random ?? Math.random;
53242
53235
  const sleep = options.sleep ?? listenSleep;
53243
53236
  const stderr = options.stderr ?? process.stderr;
53244
- const signal = options.signal;
53237
+ const hard = options.signal;
53238
+ const receiving = options.stopReceiving ? AbortSignal.any([hard, options.stopReceiving]) : hard;
53239
+ let signal = receiving;
53245
53240
  const mode = options.mode ?? "stdout";
53246
53241
  const events = options.events;
53247
53242
  if (options.number !== void 0 && (!Number.isSafeInteger(options.number) || options.number < 1)) throw new ListenError("--number must be a positive integer.");
@@ -53398,6 +53393,7 @@ async function runStandaloneListen(options) {
53398
53393
  continue;
53399
53394
  }
53400
53395
  signal.throwIfAborted();
53396
+ signal = hard;
53401
53397
  let handled;
53402
53398
  try {
53403
53399
  handled = await options.handler(delivery, signal);
@@ -53439,6 +53435,7 @@ async function runStandaloneListen(options) {
53439
53435
  if (!(error instanceof RequestFailure) || error.status !== 409 || error.code !== "stale_delivery") throw error;
53440
53436
  stderr.write("Delivery ownership expired before confirmation; it may be delivered again.\n");
53441
53437
  }
53438
+ signal = receiving;
53442
53439
  }
53443
53440
  } catch (error) {
53444
53441
  if (!signal.aborted) throw error;
@@ -53793,14 +53790,16 @@ async function createWakeMail(options) {
53793
53790
  kind: "mail",
53794
53791
  context
53795
53792
  })) return outcome(false);
53796
- if (admission.kind === "request") pendingRequest = {
53797
- sender,
53798
- emailId: detail.id,
53799
- eventId,
53800
- decidedSenders: [...policy.members()],
53801
- context
53802
- };
53803
- else {
53793
+ if (admission.kind === "request") {
53794
+ if (pendingRequest) completePending();
53795
+ pendingRequest = {
53796
+ sender,
53797
+ emailId: detail.id,
53798
+ eventId,
53799
+ decidedSenders: [...policy.members()],
53800
+ context
53801
+ };
53802
+ } else {
53804
53803
  const wake = {
53805
53804
  wakeId: detail.id,
53806
53805
  senderRelation: admission.senderRelation,
@@ -53855,6 +53854,11 @@ const RESUME_LOCK_RETRY_MS = 4e3;
53855
53854
  * newer mail) waits for that newer mail before waking for the held one.
53856
53855
  */
53857
53856
  const HELD_WAKE_SETTLE_MS = 5e3;
53857
+ /**
53858
+ * After a wake, how long the listener keeps taking mail that is already
53859
+ * queued, so mail that arrived together is announced together.
53860
+ */
53861
+ const WAKE_DRAIN_MS = 1500;
53858
53862
  const MAIL_CHECK_RECORD_MS = 5e3;
53859
53863
  const RESUME_LOCK_RETRY_STEP_MS = 250;
53860
53864
  function isListenLockContention(error) {
@@ -54245,9 +54249,12 @@ var ListenCommand = class ListenCommand extends Command {
54245
54249
  });
54246
54250
  let timedOut = false;
54247
54251
  let lastMailCheckRecord = Number.NEGATIVE_INFINITY;
54252
+ const receiveWindow = new AbortController();
54253
+ const sharedNotify = Boolean(target && flags["notify-session"] && requestConfig);
54248
54254
  const timer = flags.timeout === void 0 ? void 0 : setTimeout(() => {
54249
54255
  timedOut = true;
54250
- controller.abort();
54256
+ receiveWindow.abort();
54257
+ if (sharedNotify) controller.abort();
54251
54258
  }, flags.timeout * 1e3);
54252
54259
  const cancel = () => controller.abort();
54253
54260
  process.on("SIGINT", cancel);
@@ -54291,6 +54298,7 @@ var ListenCommand = class ListenCommand extends Command {
54291
54298
  socketPath
54292
54299
  } : void 0,
54293
54300
  handler: wake?.handler ?? handler,
54301
+ stopReceiving: receiveWindow.signal,
54294
54302
  stderr: flags.wake ? new Writable({ write(_chunk, _encoding, done) {
54295
54303
  done();
54296
54304
  } }) : void 0,
@@ -54318,7 +54326,7 @@ var ListenCommand = class ListenCommand extends Command {
54318
54326
  for (;;) try {
54319
54327
  await runListen(settle ? {
54320
54328
  ...options,
54321
- signal: AbortSignal.any([controller.signal, settle])
54329
+ stopReceiving: AbortSignal.any([receiveWindow.signal, settle])
54322
54330
  } : options);
54323
54331
  break;
54324
54332
  } catch (error) {
@@ -54330,7 +54338,15 @@ var ListenCommand = class ListenCommand extends Command {
54330
54338
  }
54331
54339
  wake?.completed();
54332
54340
  if (settle?.aborted && !controller.signal.aborted) wake?.wakeHeld?.();
54333
- } while (wake && !wake.wakeId() && !wake.status() && !controller.signal.aborted);
54341
+ } while (wake && !wake.wakeId() && !wake.status() && !controller.signal.aborted && !receiveWindow.signal.aborted);
54342
+ if (wake && hookSessionId && (wake.wakeId() || wake.status()) && !controller.signal.aborted) try {
54343
+ await runListen({
54344
+ ...options,
54345
+ number: void 0,
54346
+ stopReceiving: AbortSignal.any([receiveWindow.signal, AbortSignal.timeout(WAKE_DRAIN_MS)])
54347
+ });
54348
+ wake.completed();
54349
+ } catch {}
54334
54350
  } else {
54335
54351
  const notify = options.notifySession;
54336
54352
  let expectedCwd;
@@ -54410,7 +54426,7 @@ var ListenCommand = class ListenCommand extends Command {
54410
54426
  configDir: this.config.configDir,
54411
54427
  ...auto
54412
54428
  });
54413
- } else if (controller.signal.aborted) process.exitCode = timedOut && flags.wake ? 0 : timedOut ? 2 : 130;
54429
+ } else if (controller.signal.aborted || timedOut) process.exitCode = timedOut && flags.wake ? 0 : timedOut ? 2 : 130;
54414
54430
  }
54415
54431
  };
54416
54432
  //#endregion
@@ -54672,7 +54688,7 @@ var ListenInitCommand = class ListenInitCommand extends Command {
54672
54688
  const UUID$3 = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
54673
54689
  var ListenPendingCommand = class ListenPendingCommand extends Command {
54674
54690
  static summary = "List mail a session's wake listener accepted but not yet read";
54675
- static description = `Prints the durable pending notices for one Claude Code session of the selected connected profile. A notice is written before its wake event is acknowledged and is removed when the session reads that exact email with \`primitive emails get --id <id>\`. Notices carry only server-derived metadata (sender, thread, whether this profile sent in the thread, newer-mail count), never subject or body. Status notices (kind status) name a peer signal on a message this session sent and have nothing to read; clear them with --clear after delivering them. Unread mail notices are never dropped: at 50 notices the list reports full and newer mail stays queued, unacknowledged, until some are read. Reading never takes the writer lock, so it works while a listener holds it.`;
54691
+ static description = `Prints the durable pending notices for one Claude Code session of the selected connected profile. A notice is written before its wake event is acknowledged and is removed when the session reads that exact email with \`primitive emails get --id <id>\`. Notices carry only server-derived metadata (sender, thread, whether this profile sent in the thread, newer-mail count), never subject or body. Status notices (kind status) name a peer signal on a message this session sent and have nothing to read; clear them with --clear after delivering them. --claim marks exact ids as announced to the session and prints the ids this call claimed as \`claimed\`; receive hooks use it so each email is announced at most once. Past 50 mail notices the oldest announced one gives way; mail the session has not been shown is never dropped, so when all 50 are unannounced the list reports full and newer mail stays queued, unacknowledged. Reading never takes the writer lock, so it works while a listener holds it.`;
54676
54692
  static examples = ["PRIMITIVE_AGENT_PROFILE=work <%= config.bin %> listen pending --session <claude-session-id>", "PRIMITIVE_AGENT_PROFILE=work <%= config.bin %> listen pending --session <claude-session-id> --clear <email-id>"];
54677
54693
  static flags = {
54678
54694
  session: Flags.string({ description: "Claude Code session ID (defaults to CLAUDE_CODE_SESSION_ID in a Claude session)" }),
@@ -54680,6 +54696,11 @@ var ListenPendingCommand = class ListenPendingCommand extends Command {
54680
54696
  multiple: true,
54681
54697
  description: "Remove these exact email IDs after delivering them (repeatable)"
54682
54698
  }),
54699
+ claim: Flags.string({
54700
+ multiple: true,
54701
+ exclusive: ["clear"],
54702
+ description: "Mark these exact email IDs as announced to the session and report which ones this call claimed (repeatable)"
54703
+ }),
54683
54704
  json: Flags.boolean({ description: "Print JSON (already the default)" })
54684
54705
  };
54685
54706
  async run() {
@@ -54692,6 +54713,9 @@ var ListenPendingCommand = class ListenPendingCommand extends Command {
54692
54713
  if (!session || !UUID$3.test(session)) this.error("Pass --session with the Claude Code session UUID.");
54693
54714
  const clear = flags.clear ?? [];
54694
54715
  if (clear.some((id) => !UUID$3.test(id))) this.error("--clear takes exact email UUIDs.");
54716
+ const claim = flags.claim ?? [];
54717
+ if (claim.some((id) => !UUID$3.test(id))) this.error("--claim takes exact email UUIDs.");
54718
+ const claimed = claim.length ? await claimPendingMail(this.config.configDir, profileName, session, claim) : void 0;
54695
54719
  const notices = clear.length ? await removePendingMail(this.config.configDir, profileName, session, clear) : readPendingMail(this.config.configDir, profileName, session);
54696
54720
  this.log(JSON.stringify({
54697
54721
  session_id: session.toLowerCase(),
@@ -54700,6 +54724,7 @@ var ListenPendingCommand = class ListenPendingCommand extends Command {
54700
54724
  read_command: wakeReadCommand("<id>", profileName),
54701
54725
  path: pendingMailPath(this.config.configDir, profileName, session),
54702
54726
  full: notices.length >= 50,
54727
+ ...claimed ? { claimed } : {},
54703
54728
  notices
54704
54729
  }, null, 2));
54705
54730
  }
@@ -54852,6 +54877,8 @@ function listenForCopyHotkey(params) {
54852
54877
  //#endregion
54853
54878
  //#region src/oclif/commands/login.ts
54854
54879
  const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
54880
+ /** How long a login waits for an in-flight agent registration on this machine. */
54881
+ const REGISTRATION_WAIT_MS = 15e3;
54855
54882
  function cliError$3(message) {
54856
54883
  return new Errors.CLIError(message, { exit: 1 });
54857
54884
  }
@@ -54987,6 +55014,7 @@ var LoginCommand$1 = class extends Command {
54987
55014
  await this.runBrowserLogin(flags, this.retryCommand());
54988
55015
  }
54989
55016
  async runBrowserLogin(flags, retryCommand = this.retryCommand()) {
55017
+ await waitForRegistrations(this.config.configDir, REGISTRATION_WAIT_MS, { onWait: () => process.stderr.write("Waiting a few seconds for this machine's agent registration to finish before changing the login.\n") });
54990
55018
  let releaseCredentialsLock;
54991
55019
  try {
54992
55020
  releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
@@ -56274,7 +56302,7 @@ const TITLES = {
56274
56302
  "claude.settings_valid": "Claude Code settings file",
56275
56303
  "claude.hook.session_start": "Claude SessionStart hook",
56276
56304
  "claude.hook.session_end": "Claude SessionEnd hook",
56277
- "claude.hook.stop": "Claude per-session receive hooks",
56305
+ "claude.hook.stop": "Claude receive hooks",
56278
56306
  "claude.instructions": "Claude Code instructions",
56279
56307
  "codex.hook.session_start": "Codex SessionStart hook",
56280
56308
  "codex.instructions": "Codex instructions",
@@ -56710,7 +56738,11 @@ async function runMachineDoctor(options) {
56710
56738
  throw new MachineFileError(`The hook launcher ${cli.launcher?.path ?? ""} could not be written.`);
56711
56739
  }
56712
56740
  if (!repaired.changed.size) return id === "claude.hook.stop";
56713
- writeClaudeSettings({
56741
+ for (const item of repaired.receivers) addSessionReceiver(item.configDir, item.session, {
56742
+ profile: item.profile,
56743
+ address: item.address
56744
+ });
56745
+ if (JSON.stringify(repaired.settings) !== JSON.stringify(settingsRead.settings)) writeClaudeSettings({
56714
56746
  claudeDir: paths.claudeDir,
56715
56747
  read: settingsRead.read,
56716
56748
  settings: repaired.settings,
@@ -59592,7 +59624,7 @@ async function pollForSettlementInteraction(params) {
59592
59624
  }
59593
59625
  if (receipt) return receipt;
59594
59626
  if (deadline !== null && Date.now() >= deadline) break;
59595
- await sleep$2(params.intervalSeconds * 1e3);
59627
+ await sleep$3(params.intervalSeconds * 1e3);
59596
59628
  }
59597
59629
  return null;
59598
59630
  }
@@ -63269,8 +63301,8 @@ function setupChallengeIds(configDir, agent) {
63269
63301
  const id = savedSetupChallengeId(agentProfileDirectory(configDir, agent.profileName));
63270
63302
  return id ? [id] : [];
63271
63303
  }
63272
- async function listRows(apiClient, query) {
63273
- const result = await listEmails({
63304
+ async function listRows(api, apiClient, query) {
63305
+ const result = await api.listEmails({
63274
63306
  client: apiClient.client,
63275
63307
  query,
63276
63308
  responseStyle: "fields"
@@ -63282,8 +63314,8 @@ async function listRows(apiClient, query) {
63282
63314
  cursor: envelope?.meta?.cursor ?? null
63283
63315
  };
63284
63316
  }
63285
- async function readDetail(apiClient, id) {
63286
- const result = await getEmail({
63317
+ async function readDetail(api, apiClient, id) {
63318
+ const result = await api.getEmail({
63287
63319
  client: apiClient.client,
63288
63320
  path: { id },
63289
63321
  responseStyle: "fields"
@@ -63296,13 +63328,13 @@ async function readDetail(apiClient, id) {
63296
63328
  const detail = result.data?.data;
63297
63329
  return detail !== null && typeof detail === "object" ? detail : null;
63298
63330
  }
63299
- async function firstMatch(apiClient, rows, filters, seen) {
63331
+ async function firstMatch(api, apiClient, rows, filters, seen) {
63300
63332
  for (const row of rows) {
63301
63333
  const id = str(row.id);
63302
63334
  if (!id || seen.has(id)) continue;
63303
63335
  seen.add(id);
63304
63336
  if (!rowMayMatch(row, filters)) continue;
63305
- const detail = await readDetail(apiClient, id);
63337
+ const detail = await readDetail(api, apiClient, id);
63306
63338
  if (detail && detailMatches(detail, filters)) return detail;
63307
63339
  }
63308
63340
  return null;
@@ -63317,32 +63349,49 @@ function arrivedSince(row, threshold) {
63317
63349
  * the deadline.
63318
63350
  */
63319
63351
  async function waitForEmail(params) {
63352
+ const { deadline } = params;
63353
+ const api = waitingInboxApi(deadline, {
63354
+ getConversation,
63355
+ getEmail,
63356
+ listEmails
63357
+ });
63358
+ try {
63359
+ return await tailForEmail(api, params);
63360
+ } catch (error) {
63361
+ if (error instanceof WaitDeadlineReached) return null;
63362
+ throw error;
63363
+ }
63364
+ }
63365
+ async function tailForEmail(api, params) {
63320
63366
  const { apiClient, filters, deadline } = params;
63321
63367
  const seen = new Set(params.skipIds ?? []);
63322
- let since = await baselineCursor({ apiClient });
63323
- const recent = await listRows(apiClient, { limit: EXISTING_PAGE_SIZE });
63324
- const early = await firstMatch(apiClient, recent.rows.filter((row) => arrivedSince(row, params.startedAt)).reverse(), filters, seen);
63368
+ let since = await baselineCursor({
63369
+ apiClient,
63370
+ api
63371
+ });
63372
+ const recent = await listRows(api, apiClient, { limit: EXISTING_PAGE_SIZE });
63373
+ const early = await firstMatch(api, apiClient, recent.rows.filter((row) => arrivedSince(row, params.startedAt)).reverse(), filters, seen);
63325
63374
  if (early) return early;
63326
63375
  if (params.includeExisting) {
63327
- const found = await firstMatch(apiClient, recent.rows, filters, seen);
63376
+ const found = await firstMatch(api, apiClient, recent.rows, filters, seen);
63328
63377
  if (found) return found;
63329
63378
  }
63330
63379
  for (;;) {
63331
63380
  const remainingMs = deadline === null ? MAX_LONG_POLL_SECONDS * 1e3 : deadline - Date.now();
63332
63381
  if (remainingMs <= 0) return null;
63333
63382
  const startedAt = Date.now();
63334
- const page = await listRows(apiClient, {
63383
+ const page = await listRows(api, apiClient, {
63335
63384
  limit: TAIL_PAGE_SIZE,
63336
63385
  since,
63337
63386
  wait: Math.max(0, Math.min(MAX_LONG_POLL_SECONDS, Math.floor(remainingMs / 1e3)))
63338
63387
  });
63339
- const found = await firstMatch(apiClient, page.rows, filters, seen);
63388
+ const found = await firstMatch(api, apiClient, page.rows, filters, seen);
63340
63389
  if (found) return found;
63341
63390
  if (page.rows.length > 0 && page.cursor) {
63342
63391
  since = page.cursor;
63343
63392
  continue;
63344
63393
  }
63345
- if (Date.now() - startedAt < 1e3) await sleep$2(Math.max(0, Math.min(1e3, remainingMs)));
63394
+ if (Date.now() - startedAt < 1e3) await sleep$3(Math.max(0, Math.min(1e3, remainingMs)));
63346
63395
  }
63347
63396
  }
63348
63397
  function describeFilters(filters) {
@@ -63364,6 +63413,8 @@ Only mail that arrives after the command starts matches, unless --include-existi
63364
63413
 
63365
63414
  --code prints only the verification or one-time code, with spaces or dashes between its groups removed. --link prints only the most likely verification, sign-in, confirm, magic or reset link, with an obvious click-tracking wrapper removed. Without either, the email is printed: sender, subject, time, its ID and the start of its text, ready for \`primitive reply --id <id>\`. --json prints one object with id, thread_id, from, to, subject, received_at, text and, when asked, code and link (null when none was found).
63366
63415
 
63416
+ A rate limit (HTTP 429, honouring Retry-After), a 502, 503 or 504, or a network error during the wait is retried with backoff until --timeout, with one note on stderr per retry; if it lasts until then, the wait times out (exit 5).
63417
+
63367
63418
  Exit codes:
63368
63419
  - 0: a matching email arrived and was printed.
63369
63420
  - 1: error (API failure or authentication).
@@ -64117,10 +64168,10 @@ var WakeSchedulesUpdateCommand = class WakeSchedulesUpdateCommand extends Comman
64117
64168
  };
64118
64169
  //#endregion
64119
64170
  //#region src/oclif/commands/whoami.ts
64120
- function formatWhoamiSummary(account, managedInboxDomain) {
64171
+ function formatWhoamiSummary(account, managedInboxDomain, orgName = null) {
64121
64172
  const lines = [
64122
64173
  `Authenticated as ${account.email}`,
64123
- `Account id: ${account.id}`,
64174
+ orgName ? `Organization: ${orgName} (${account.id})` : `Organization id: ${account.id}`,
64124
64175
  `Plan: ${account.plan}`
64125
64176
  ];
64126
64177
  if (managedInboxDomain) lines.push(`Managed inbox: any-local-part@${managedInboxDomain}`);
@@ -64209,14 +64260,25 @@ var WhoamiCommand = class WhoamiCommand extends Command {
64209
64260
  });
64210
64261
  if (!domainsResult.error) managedInboxDomain = (domainsResult.data?.data ?? []).find((row) => row.verified && row.managed_zone !== null)?.domain ?? null;
64211
64262
  } catch {}
64263
+ const orgName = auth.credentials?.org_id === account.id ? auth.credentials.org_name ?? null : null;
64264
+ const session = guardedSession(void 0, process.env);
64265
+ let registration = null;
64266
+ try {
64267
+ registration = session ? failedSessionRegistration(this.config.configDir, session) : null;
64268
+ } catch {
64269
+ registration = null;
64270
+ }
64212
64271
  if (flags.json) {
64213
64272
  this.log(JSON.stringify({
64214
64273
  ...account,
64215
- managed_inbox_domain: managedInboxDomain
64274
+ managed_inbox_domain: managedInboxDomain,
64275
+ org_name: orgName,
64276
+ ...registration ? { session_registration: registration } : {}
64216
64277
  }, null, 2));
64217
64278
  return;
64218
64279
  }
64219
- this.log(formatWhoamiSummary(account, managedInboxDomain));
64280
+ this.log(formatWhoamiSummary(account, managedInboxDomain, orgName));
64281
+ if (registration) this.log(registrationSummaryLine(registration));
64220
64282
  });
64221
64283
  }
64222
64284
  };