primcli 1.48.0 → 1.49.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,6 +1,6 @@
1
- import { A as readThreadContext, Mt as createAuthenticatedCliApiClient, fn as getSentEmail } from "../api-command-DzQ8KxD9.js";
1
+ import { A as readThreadContext, Mt as createAuthenticatedCliApiClient, fn as getSentEmail } from "../api-command-BixHVNwQ.js";
2
2
  import { _ as acquireListenLock } from "../shared-mail-files-SPPcis9g.js";
3
- import { t as sendSignal } from "../signal-command-BbeV2Nd8.js";
3
+ import { t as sendSignal } from "../signal-command-D0Rig6eA.js";
4
4
  import { AUTO_SIGNAL_KIND_ENV, AUTO_WORKING_RENEW_MS, autoSignalsDisabled, clearWorkingSending, markWorkingSending, readAutoClaim, readWorkingLease, recordRenewer, stopWorkingLease, writeWorkingLease } from "./auto-signals.js";
5
5
  import { join } from "node:path";
6
6
  import { homedir } from "node:os";
@@ -1,5 +1,5 @@
1
1
  import { d as readMailJson, h as writeMailJson, i as mailId, l as privateMailDirectory, r as mailAddress } from "../shared-mail-files-SPPcis9g.js";
2
- import { a as scopedChatSenderTrust, n as isPlainChatReply } from "../scoped-chat-DhxtuNXs.js";
2
+ import { a as scopedChatSenderTrust, n as isPlainChatReply } from "../scoped-chat-DSLrmgWQ.js";
3
3
  import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { join } from "node:path";
@@ -1,13 +1,13 @@
1
1
  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-DGrZCUfw.js";
2
- import { $ as buildThrownSendFailureEnvelope, $n as stopRepeatFromEmail, $t as getAgentContactPolicy, A as readThreadContext, An as listWakeSchedules, At as queryUsesAutomated, B as recordPendingMail, Bn as resendAgentSignupVerification, Bt as createEndpoint, C as queryUsesAwaiting, Cn as listFunctionLogs, Ct as AutomatedFilterUnsupportedError, D as displayAddress, Dn as listSentEmails, Dt as expectedAutomatedVerdicts, E as describeWake, En as listRoutes, Et as ensureSearchReportsAutomated, F as wakeRecipientField, Ft as resolveCliApiRequestConfig, G as replyExpectation, Gn as sendEmail, H as interactionHumanLine, Hn as searchEmails, Ht as createRoute, I as wakeRelationship, In as redeemCreditCode, It as addDefaultNetworkMember, J as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, Jn as setMemory, Jt as deleteRoute, K as replyInteractionWarning, Kn as setFunctionRoute, L as clearReadPendingMail, Ln as removeDefaultNetworkMember, Lt as checkDefaultNetworkContactAdmission, M as serverMuted, Mn as pullWebhookEvent, Mt as createAuthenticatedCliApiClient, N as serverRelationship, Nt as createCliApiClient, O as formatWakeContext, On as listWakeAuthorizations, Ot as hasAutomatedVerdict, P as wakeReadCommand, Pt as refreshStoredCliCredentials, Q as buildFollowUpCommand, Qn as startCliLogin, Qt as getAccount, R as pendingMailPath, Rn as reorderRoutes, Rt as cliLogout, S as isAwaitingRejectedError, Sn as listEndpoints, St as AUTOMATED_VALUES, T as bareAddress, Tn as listRepeatingSends, Tt as automatedRejectedError, U as interactionNextActions, Un as searchMemories, Ut as createWakeAuthorization, V as removePendingMail, Vn as runWakeSchedule, Vt as createFunction, W as readEmailInteraction, Wn as semanticSearch, Wt as createWakeSchedule, X as apiResultHttpStatus, Xn as startAgentClaim, Xt as deleteWakeSchedule, Y as SEND_OUTCOME_HELP, Yn as simulateRoute, Yt as deleteWakeAuthorization, Z as assertValidIdempotencyKey, Zn as startAgentSignup, _ as awaitingRejectedError, _t as formatAlreadySentNotice, a as extractErrorCode, an as getFunction, ar as updateRoute, at as formatPriorRepliesWarning, b as formatRepliesCell, bn as listDomains, bt as AUTOMATED_FILTER_UNSUPPORTED_CODE, c as readTextFileFlag, cn as getInboxStatus, cr as verifyAgentClaim, ct as priorRepliesThatWentOut, d as writeErrorWithHints, dn as getRepeatingSend, dt as sendOutcomeExitCode, er as testFunction, et as checkPriorReplies, f as AWAITING_FLAG_DESCRIPTION, fn as getSentEmail, ft as sentHistoryWindowStart, g as assertAwaitingFilterRows, gn as listAgentNetworks, gt as currentMailSessionKey, h as ReplyStateUnsupportedError, hn as listAgentContacts, ht as thrownErrorExitCode, i as createOperationCommand, in as getEmail, ir as updateRepeatingSend, it as formatPriorRepliesCheckSkipped, j as sentInThread, jn as pollCliLogin, k as latestOwnOutbound, kn as listWakeDispatches, kt as isAutomatedRejectedError, l as runWithTiming, ln as getMemory, lr as verifyAgentSignup, lt as reportSendCommandResult, m as REPLY_STATE_UNSUPPORTED_CODE, mn as getWakeSchedule, mt as successfulSendOutcome, n as API_ERROR_CODES, nn as getCreditBalance, nr as updateDefaultNetworkMember, nt as deriveSendIdempotencyKey, o as extractErrorPayload, on as getFunctionRouting, or as updateWakeAuthorization, ot as formatSendFailureSummary, p as AWAITING_VALUES, pn as getThread, pt as serializeErrorPayload, q as wakeInteractionSentence, qn as setFunctionSecret, qt as deleteMemory, r as TIME_FLAG_DESCRIPTION, rn as getDefaultNetworkAgent, rr as updateFunction, rt as formatDeletedEarlierSendNotice, s as formatErrorPayload, sn as getFunctionTestRunTrace, sr as updateWakeSchedule, st as formatSendRecordFailureSummary, t as API_BASE_URL_FLAG_DESCRIPTION, tn as getConversation, tr as unsetFunctionRoute, tt as classifySendError, u as surfaceUnauthorizedHint, un as getOrgRoutingTopology, ut as sendIdentityFields, v as ensureSearchReportsReplyState, vn as listDefaultNetworkAgents, vt as writeIdempotentReplayBannerIfReplay, w as explainPendingMailNotFound, wn as listFunctions, wt as assertAutomatedVerdict, x as hasReplyState, xn as listEmails, xt as AUTOMATED_FLAG_DESCRIPTION, y as formatAwaitingCell, yn as listDefaultNetworkMembers, yt as withFlagSuggestion, z as readPendingMail, zn as replyToEmail, zt as completeWebhookEvent } from "../api-command-DzQ8KxD9.js";
2
+ import { $ as buildThrownSendFailureEnvelope, $n as stopRepeatFromEmail, $t as getAgentContactPolicy, A as readThreadContext, An as listWakeSchedules, At as queryUsesAutomated, B as recordPendingMail, Bn as resendAgentSignupVerification, Bt as createEndpoint, C as queryUsesAwaiting, Cn as listFunctionLogs, Ct as AutomatedFilterUnsupportedError, D as displayAddress, Dn as listSentEmails, Dt as expectedAutomatedVerdicts, E as describeWake, En as listRoutes, Et as ensureSearchReportsAutomated, F as wakeRecipientField, Ft as resolveCliApiRequestConfig, G as replyExpectation, Gn as sendEmail, H as interactionHumanLine, Hn as searchEmails, Ht as createRoute, I as wakeRelationship, In as redeemCreditCode, It as addDefaultNetworkMember, J as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, Jn as setMemory, Jt as deleteRoute, K as replyInteractionWarning, Kn as setFunctionRoute, L as clearReadPendingMail, Ln as removeDefaultNetworkMember, Lt as checkDefaultNetworkContactAdmission, M as serverMuted, Mn as pullWebhookEvent, Mt as createAuthenticatedCliApiClient, N as serverRelationship, Nt as createCliApiClient, O as formatWakeContext, On as listWakeAuthorizations, Ot as hasAutomatedVerdict, P as wakeReadCommand, Pt as refreshStoredCliCredentials, Q as buildFollowUpCommand, Qn as startCliLogin, Qt as getAccount, R as pendingMailPath, Rn as reorderRoutes, Rt as cliLogout, S as isAwaitingRejectedError, Sn as listEndpoints, St as AUTOMATED_VALUES, T as bareAddress, Tn as listRepeatingSends, Tt as automatedRejectedError, U as interactionNextActions, Un as searchMemories, Ut as createWakeAuthorization, V as removePendingMail, Vn as runWakeSchedule, Vt as createFunction, W as readEmailInteraction, Wn as semanticSearch, Wt as createWakeSchedule, X as apiResultHttpStatus, Xn as startAgentClaim, Xt as deleteWakeSchedule, Y as SEND_OUTCOME_HELP, Yn as simulateRoute, Yt as deleteWakeAuthorization, Z as assertValidIdempotencyKey, Zn as startAgentSignup, _ as awaitingRejectedError, _t as formatAlreadySentNotice, a as extractErrorCode, an as getFunction, ar as updateRoute, at as formatPriorRepliesWarning, b as formatRepliesCell, bn as listDomains, bt as AUTOMATED_FILTER_UNSUPPORTED_CODE, c as readTextFileFlag, cn as getInboxStatus, cr as verifyAgentClaim, ct as priorRepliesThatWentOut, d as writeErrorWithHints, dn as getRepeatingSend, dt as sendOutcomeExitCode, er as testFunction, et as checkPriorReplies, f as AWAITING_FLAG_DESCRIPTION, fn as getSentEmail, ft as sentHistoryWindowStart, g as assertAwaitingFilterRows, gn as listAgentNetworks, gt as currentMailSessionKey, h as ReplyStateUnsupportedError, hn as listAgentContacts, ht as thrownErrorExitCode, i as createOperationCommand, in as getEmail, ir as updateRepeatingSend, it as formatPriorRepliesCheckSkipped, j as sentInThread, jn as pollCliLogin, k as latestOwnOutbound, kn as listWakeDispatches, kt as isAutomatedRejectedError, l as runWithTiming, ln as getMemory, lr as verifyAgentSignup, lt as reportSendCommandResult, m as REPLY_STATE_UNSUPPORTED_CODE, mn as getWakeSchedule, mt as successfulSendOutcome, n as API_ERROR_CODES, nn as getCreditBalance, nr as updateDefaultNetworkMember, nt as deriveSendIdempotencyKey, o as extractErrorPayload, on as getFunctionRouting, or as updateWakeAuthorization, ot as formatSendFailureSummary, p as AWAITING_VALUES, pn as getThread, pt as serializeErrorPayload, q as wakeInteractionSentence, qn as setFunctionSecret, qt as deleteMemory, r as TIME_FLAG_DESCRIPTION, rn as getDefaultNetworkAgent, rr as updateFunction, rt as formatDeletedEarlierSendNotice, s as formatErrorPayload, sn as getFunctionTestRunTrace, sr as updateWakeSchedule, st as formatSendRecordFailureSummary, t as API_BASE_URL_FLAG_DESCRIPTION, tn as getConversation, tr as unsetFunctionRoute, tt as classifySendError, u as surfaceUnauthorizedHint, un as getOrgRoutingTopology, ut as sendIdentityFields, v as ensureSearchReportsReplyState, vn as listDefaultNetworkAgents, vt as writeIdempotentReplayBannerIfReplay, w as explainPendingMailNotFound, wn as listFunctions, wt as assertAutomatedVerdict, x as hasReplyState, xn as listEmails, xt as AUTOMATED_FLAG_DESCRIPTION, y as formatAwaitingCell, yn as listDefaultNetworkMembers, yt as withFlagSuggestion, z as readPendingMail, zn as replyToEmail, zt as completeWebhookEvent } from "../api-command-BixHVNwQ.js";
3
3
  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-SPPcis9g.js";
4
- import { $ as describeSessionHookItem, A as NetworkAdmissionPendingError, B as runContactRequest, C as pollCheckCommand, D as verificationReplySubmitted, E as RECEIVER_MODES, F as addressNoteTarget, G as receiverStatusCommand, H as agentInvitationHash, I as runAddressNotesRequest, J as recordHookMailCheck, K as savedReceiver, L as ContactsApiError, M as evaluateContactPolicy, N as canonicalContactSelector, O as ContactPolicyReadRetryError, P as AddressNotesApiError, Q as describeSessionHookChange, R as contactAddress, S as invitationProfileName, T as withOwnerMemberAddress, U as connectAgent, V as agentConnectionStatus, W as readAgentInvitation, X as claudeConfigDir, Y as boundSessionProfiles, Z as currentCliLocation, _ as disconnectAgent, a as endSession, at as samePath, b as identitySuggestions, c as pendingSessionDisconnects, ct as inspectManagedBlock, d as registerSession, dt as upsertManagedBlock, et as ephemeralCliReason, f as retryPendingDisconnects, ft as writeManagedFile, g as AgentDisconnectError, h as enrollmentResumes, i as connectedProfilesForSession, it as repairClaudeHooks, j as createNotificationContactPolicy, k as NETWORK_ADMISSION_PENDING_RETRY_MS, l as psProcessInfo, lt as jsonIndentation, m as enrollAgent, n as OMP_MAIN, nt as inspectClaudeHooks, o as headlessClaudeRun, ot as writeClaudeSettings, p as runDetached, q as installClaudeWakeHook, r as agentConnectionState, rt as readClaudeSettings, st as MachineFileError, t as MACHINE_RUNTIMES, tt as findOnPath, u as readClaudeHookInput, ut as readManagedFile, v as cliInvocation, w as runAgentConnect, x as defaultAgentProfileName, y as generatedAgentName, z as contactAgentAddress } from "../machine-session-BaJ1Xmdm.js";
5
- import { _ as NativeSessionDisconnectedError, a as readBundledConnectSkill, b as NotificationOutcomeUnknownError, c as openSessionNotifications, d as isRoutineNotificationContent, f as notificationPartReader, g as defaultSessionSocket, i as installedVersion, l as notificationReceiptPage, m as SESSION_UUID$1, o as notificationScope, p as readConversationStatusContent, r as installConnectSkill, s as notificationSenders, t as connectSkillTarget, u as NotificationRetryError, v as NativeSessionError, y as NativeSessionNotLoadedError } from "../connect-skill-DHPc9dVN.js";
6
- import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background-CyLc8VBn.js";
7
- 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-D2xtLium-DdiJECLI.js";
8
- import { a as ensureSharedMailSubscription, c as reserveSharedMailSubscription, d as otherParticipants, f as EventConnection, h as pushFile, i as sharedMailScope, l as followEmailConversation, m as pullFile, n as reconcileChatSend, o as createSharedMailWaiter, p as EventReceiverError, r as openSharedMailReceiver, s as openSharedMailStore, t as sendSignal, u as readConversationFollow } from "../signal-command-BbeV2Nd8.js";
9
- 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-DhxtuNXs.js";
10
- import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, restoreAutoWorking } from "./auto-signals.js";
4
+ import { $ as currentCliLocation, A as NETWORK_ADMISSION_PENDING_RETRY_MS, B as contactAgentAddress, C as invitationProfileName, D as externalReceivingState, E as RECEIVER_MODES, F as AddressNotesApiError, G as readAgentInvitation, H as agentConnectionStatus, I as addressNoteTarget, J as savedReceiver, K as receiverStatusCommand, L as runAddressNotesRequest, M as createNotificationContactPolicy, N as evaluateContactPolicy, O as verificationReplySubmitted, P as canonicalContactSelector, Q as claudeConfigDir, R as ContactsApiError, S as defaultAgentProfileName, T as runAgentConnect, U as agentInvitationHash, V as runContactRequest, W as connectAgent, X as recordHookMailCheck, Y as installClaudeWakeHook, Z as boundSessionProfiles, _ as disconnectAgent, _t as readHookLauncherWarning, a as endSession, at as hasMachineSessionHooks, b as identitySuggestions, c as pendingSessionDisconnects, ct as repairClaudeHooks, d as registerSession, dt as MachineFileError, et as describeSessionHookChange, f as retryPendingDisconnects, ft as inspectManagedBlock, g as AgentDisconnectError, gt as writeManagedFile, h as enrollmentResumes, ht as upsertManagedBlock, i as connectedProfilesForSession, it as findOnPath, j as NetworkAdmissionPendingError, k as ContactPolicyReadRetryError, l as psProcessInfo, lt as samePath, m as enrollAgent, mt as readManagedFile, n as OMP_MAIN, nt as ensureHookLauncher, o as headlessClaudeRun, ot as inspectClaudeHooks, p as runDetached, pt as jsonIndentation, q as refusalAfterReplacement, r as agentConnectionState, rt as ephemeralCliReason, st as readClaudeSettings, t as MACHINE_RUNTIMES, tt as describeSessionHookItem, u as readClaudeHookInput, ut as writeClaudeSettings, v as cliInvocation, vt as refreshOwnerMemberAddressPeriodically, w as pollCheckCommand, x as connectWarnings, y as generatedAgentName, yt as withOwnerMemberAddress, z as contactAddress } from "../machine-session-BEkDi4_N.js";
5
+ 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-DSLrmgWQ.js";
6
+ import { _ as NativeSessionDisconnectedError, a as readBundledConnectSkill, b as NotificationOutcomeUnknownError, c as openSessionNotifications, d as isRoutineNotificationContent, f as notificationPartReader, g as defaultSessionSocket, i as installedVersion, l as notificationReceiptPage, m as SESSION_UUID$1, o as notificationScope, p as readConversationStatusContent, r as installConnectSkill, s as notificationSenders, t as connectSkillTarget, u as NotificationRetryError, v as NativeSessionError, y as NativeSessionNotLoadedError } from "../connect-skill-DBkhFAkO.js";
7
+ import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background-CMfxkeVP.js";
8
+ 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-D2xtLium-B8Wq41vG.js";
9
+ import { a as ensureSharedMailSubscription, c as reserveSharedMailSubscription, d as otherParticipants, f as EventConnection, h as pushFile, i as sharedMailScope, l as followEmailConversation, m as pullFile, n as reconcileChatSend, o as createSharedMailWaiter, p as EventReceiverError, r as openSharedMailReceiver, s as openSharedMailStore, t as sendSignal, u as readConversationFollow } from "../signal-command-D0Rig6eA.js";
10
+ import { autoSignalEligible, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, restoreAutoWorking } from "./auto-signals.js";
11
11
  import { t as guardJsonOutputCommands } from "../json-output-Cf-GFeff.js";
12
12
  import { Args, Command, Errors, Flags, ux } from "@oclif/core";
13
13
  import { chmodSync, closeSync, constants, cpSync, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
@@ -35252,9 +35252,25 @@ function readCursor(path) {
35252
35252
  } catch {}
35253
35253
  return null;
35254
35254
  }
35255
- /** Setup challenges and presence probes come from the control address. */
35256
- function isControlMail(email, identity) {
35257
- return email.presence_control !== void 0 || email.sender.trim().toLowerCase() === identity.ownerAddress.trim().toLowerCase();
35255
+ /**
35256
+ * Mail the CLI handles itself: a presence probe whose authenticated
35257
+ * projection marks it as control mail (verified, or pending while its proof
35258
+ * settles; a rejected one is ordinary mail), and the exact setup challenge
35259
+ * this profile's setup recorded. Nothing is judged by sender or subject: the
35260
+ * owner can write from the control address, and that email is listed like
35261
+ * any other.
35262
+ */
35263
+ function isControlMail(email, setupChallengeId) {
35264
+ return presenceDisposition(email) !== "ordinary" || setupChallengeId !== null && email.id === setupChallengeId;
35265
+ }
35266
+ /** The setup challenge's email ID saved by this profile's setup, if any. */
35267
+ function savedSetupChallengeId(directory) {
35268
+ try {
35269
+ const id = readMailJson(join(directory, "setup.json"))?.challenge?.id;
35270
+ return typeof id === "string" && id.length > 0 ? id : null;
35271
+ } catch {
35272
+ return null;
35273
+ }
35258
35274
  }
35259
35275
  async function checkAgentMail(options) {
35260
35276
  const { identity } = options;
@@ -35263,9 +35279,14 @@ async function checkAgentMail(options) {
35263
35279
  const release = acquireListenLock(directory, "mail-check");
35264
35280
  const readCommand = (id) => `PRIMITIVE_AGENT_PROFILE=${identity.profileName} ${options.invocation ?? "primitive"} emails get --id ${id} --brief`;
35265
35281
  const to = identity.agentAddress.toLowerCase();
35282
+ const ownerMember = (options.ownerMemberAddress ?? (() => refreshOwnerMemberAddressPeriodically({
35283
+ configDir: options.configDir,
35284
+ profileName: identity.profileName
35285
+ })))().catch(() => identity.ownerMemberAddress ?? null);
35266
35286
  try {
35267
35287
  const path = join(directory, CURSOR_FILE);
35268
35288
  const saved = readCursor(path);
35289
+ const setupChallenge = savedSetupChallengeId(directory);
35269
35290
  let cursor = saved;
35270
35291
  const emails = [];
35271
35292
  let controlSkipped = 0;
@@ -35285,7 +35306,7 @@ async function checkAgentMail(options) {
35285
35306
  });
35286
35307
  if (result.error || !result.data) throw new MailCheckApiError(result.error);
35287
35308
  const rows = result.data.data ?? [];
35288
- for (const row of rows) if (isControlMail(row, identity)) controlSkipped++;
35309
+ for (const row of rows) if (isControlMail(row, setupChallenge)) controlSkipped++;
35289
35310
  else emails.push({
35290
35311
  id: row.id,
35291
35312
  received_at: row.received_at,
@@ -35304,6 +35325,7 @@ async function checkAgentMail(options) {
35304
35325
  emails,
35305
35326
  more,
35306
35327
  control_skipped: controlSkipped,
35328
+ owner_member_address: await ownerMember,
35307
35329
  profile: identity.profileName,
35308
35330
  to,
35309
35331
  read_command: readCommand("<id>")
@@ -35328,9 +35350,9 @@ var AgentCheckMailCommand = class AgentCheckMailCommand extends Command {
35328
35350
  static summary = "Check a connected agent's mail since its last check";
35329
35351
  static description = `Print the mail that reached the selected connected agent profile since its previous check, oldest first, once, without waiting. This is how an agent connected with poll receiving (no local session ID or hooks, so nothing can wake it) receives mail: run it at the start of each turn and after sending, then read each email with its \`read_command\`, which selects this profile: an email is readable only under the profile that received it.
35330
35352
 
35331
- Acknowledgements marked fyi and mail in muted threads are left out. Setup and presence mail from the connection's control address is handled by the CLI and only counted. The position is saved privately with the profile and moves forward only after mail was read, so mail is reported again rather than lost when a check fails or is interrupted; deduplicate by email ID. The first check starts before the address's first email. \`more: true\` means more new mail remains: handle these and check again.
35353
+ Acknowledgements marked fyi and mail in muted threads are left out. Presence probes and this profile's setup challenge are handled by the CLI and only counted in \`control_skipped\`; any other email from the connection's control address, such as one the owner wrote from it, is listed like any other email. The position is saved privately with the profile and moves forward only after mail was read, so mail is reported again rather than lost when a check fails or is interrupted; deduplicate by email ID. The first check starts before the address's first email. \`more: true\` means more new mail remains: handle these and check again.
35332
35354
 
35333
- Requires a connected agent profile selected with ${AGENT_PROFILE_ENV}. Output is one JSON document: \`outcome\` ("mail" | "empty"), \`emails\` (id, received_at, sender, thread_id, to, and the read_command that reads that email under this profile; never subject or body), \`more\`, \`control_skipped\`, \`profile\`, \`to\` (the address this profile receives for) and \`read_command\` (the template with <id>).`;
35355
+ Requires a connected agent profile selected with ${AGENT_PROFILE_ENV}. Output is one JSON document: \`outcome\` ("mail" | "empty"), \`emails\` (id, received_at, sender, thread_id, to, and the read_command that reads that email under this profile; never subject or body), \`more\`, \`control_skipped\`, \`owner_member_address\` (the owner's personal address, where reports and questions go, or null when none is known; refreshed from the server every few minutes), \`profile\`, \`to\` (the address this profile receives for) and \`read_command\` (the template with <id>).`;
35334
35356
  static examples = [`${AGENT_PROFILE_ENV}=connection-0123456789ab <%= config.bin %> agent check-mail --json`];
35335
35357
  static flags = { json: Flags.boolean({ description: "Print JSON (already the default)" }) };
35336
35358
  async run() {
@@ -35489,7 +35511,7 @@ function invocation$1(entry) {
35489
35511
  return entry && /[\\/]_npx[\\/]/.test(entry) ? "npx -y primitive@latest" : "primitive";
35490
35512
  }
35491
35513
  var AgentConnectCommand = class AgentConnectCommand extends Command {
35492
- 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. 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, or with PRIMITIVE_AGENT_PROFILE set, to inspect saved identity and local receiver health offline; it exits 1 when the profile is not configured.`;
35514
+ 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, or with PRIMITIVE_AGENT_PROFILE set, to inspect saved identity and local receiver health offline; it exits 1 when the profile is not configured.`;
35493
35515
  static summary = "Connect and verify an agent address";
35494
35516
  static examples = [
35495
35517
  "<%= config.bin %> agent connect --session \"$CODEX_THREAD_ID\" --name Research --info \"Reviews pull requests\" --json < private-invitation.txt",
@@ -35552,6 +35574,7 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
35552
35574
  async run() {
35553
35575
  const { flags } = await this.parse(AgentConnectCommand);
35554
35576
  let pollProfile;
35577
+ let replaced = [];
35555
35578
  try {
35556
35579
  if (flags.status) {
35557
35580
  const profileName = flags.profile ?? (process.env["PRIMITIVE_AGENT_PROFILE"]?.trim() || void 0);
@@ -35583,7 +35606,6 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
35583
35606
  invitationText ??= await readAgentInvitation(process.stdin, process.stdin.isTTY);
35584
35607
  return invitationText;
35585
35608
  };
35586
- let replaced = [];
35587
35609
  let replaceExisting;
35588
35610
  const guardSession = flags.resume ? null : flags.session !== void 0 ? session?.toLowerCase() ?? null : guardedSession(void 0);
35589
35611
  if (guardSession && !flags["keep-existing"]) {
@@ -35659,8 +35681,10 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
35659
35681
  else {
35660
35682
  this.log(`Agent ${output.address}: verification ${output.verification.state}; receiving ${output.receiving.state}; skill ${output.skill.state}.`);
35661
35683
  this.log(`Select it with ${output.selectProfile}.`);
35684
+ for (const warning of output.warnings) this.log(warning.message);
35662
35685
  if (output.ownerNotifications === "silenced") this.log("Owner notifications remain silenced by the existing preference or policy.");
35663
35686
  if (external && verificationReplySubmitted(output.verification.state)) this.log(output.externalHook === "installed_unverified" ? "External receive hook installed. Idle wake still needs a live mail check." : "External receive hook is unavailable. Pairing may complete, but this session will not wake automatically.");
35687
+ if (poll && "fallbackDetail" in output.receiving) this.log(String(output.receiving.fallbackDetail));
35664
35688
  if (poll && "checkCommand" in output.receiving) this.log(`Nothing wakes this session for new mail. Check it at the start of each turn and after sending with: ${output.receiving.checkCommand}`);
35665
35689
  if (output.status !== "connected") this.log(`Resume: ${output.resumeCommand}`);
35666
35690
  }
@@ -35680,16 +35704,23 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
35680
35704
  configDir: this.config.configDir,
35681
35705
  onlyIfUnknown: claimed.status === "claimed"
35682
35706
  });
35707
+ const warnings = connectWarnings(this.config.configDir, result.identity.profileName);
35683
35708
  if (flags.json) this.log(JSON.stringify(replaced.length ? {
35684
35709
  ...result,
35710
+ warnings,
35685
35711
  replacedExisting: replaced
35686
- } : result));
35712
+ } : {
35713
+ ...result,
35714
+ warnings
35715
+ }));
35687
35716
  else {
35688
35717
  this.log(result.status === "claimed" ? "Agent profile saved. Email readiness is not yet verified." : "Agent profile already configured; no claim was sent.");
35689
35718
  this.log(`Select it with PRIMITIVE_AGENT_PROFILE=${result.identity.profileName}.`);
35690
35719
  this.log(result.ownerReportGuidance);
35720
+ for (const warning of warnings) this.log(warning.message);
35691
35721
  }
35692
- } catch (error) {
35722
+ } catch (caught) {
35723
+ const error = refusalAfterReplacement(caught, replaced);
35693
35724
  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 });
35694
35725
  }
35695
35726
  }
@@ -35975,6 +36006,7 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
35975
36006
  agentAddress: result.identity.agentAddress,
35976
36007
  sessionId: flags.session
35977
36008
  }) : null;
36009
+ const warnings = connectWarnings(this.config.configDir, result.identity.profileName);
35978
36010
  const output = result.receiving.state === "poll" ? {
35979
36011
  ...result,
35980
36012
  receiving: {
@@ -35982,10 +36014,20 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
35982
36014
  mode: "poll",
35983
36015
  checkCommand: pollCheckCommand(invocation(process.argv[1]), result.identity.profileName)
35984
36016
  },
35985
- externalHook
36017
+ externalHook,
36018
+ warnings
36019
+ } : flags.receiver === "external" && result.connection.status !== "owner_inactive" ? {
36020
+ ...result,
36021
+ receiving: {
36022
+ ...result.receiving,
36023
+ state: externalReceivingState(externalHook)
36024
+ },
36025
+ externalHook,
36026
+ warnings
35986
36027
  } : {
35987
36028
  ...result,
35988
- externalHook
36029
+ externalHook,
36030
+ warnings
35989
36031
  };
35990
36032
  const identity = identitySuggestions({
35991
36033
  invocation: invocation(process.argv[1]),
@@ -36003,8 +36045,9 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
36003
36045
  };
36004
36046
  if (flags.json) this.log(JSON.stringify(printed));
36005
36047
  else {
36006
- this.log(`Agent ${result.identity.agentAddress}: pairing ${result.connection.status}; verification ${result.verification.state}; receiving ${result.receiving.state}.`);
36048
+ this.log(`Agent ${result.identity.agentAddress}: pairing ${result.connection.status}; verification ${result.verification.state}; receiving ${output.receiving.state}.`);
36007
36049
  this.log(`Select it with PRIMITIVE_AGENT_PROFILE=${result.identity.profileName}.`);
36050
+ for (const warning of warnings) this.log(warning.message);
36008
36051
  if (flags["contact-requests"]) this.log(`Contact requests: ${result.contactRequestPolicy}.`);
36009
36052
  if (result.contactRequestPolicy === "owner_disabled") this.log("The owner disabled contact requests for this address. Review the policy in the app if you want them enabled.");
36010
36053
  if (result.contactRequestPolicy === "unavailable") this.log("Contact-request policy could not be confirmed. Receiving can still be configured; rerun this exact enrollment after policy access is restored.");
@@ -36629,6 +36672,66 @@ var AgentSessionRegisterCommand = class AgentSessionRegisterCommand extends Comm
36629
36672
  }
36630
36673
  };
36631
36674
  //#endregion
36675
+ //#region src/oclif/commands/agent-status.ts
36676
+ /**
36677
+ * Picks the profile `agent status` reads: an explicit --profile, then
36678
+ * PRIMITIVE_AGENT_PROFILE, then the one address connected for this runtime
36679
+ * session. Null when none applies or the session has several addresses.
36680
+ */
36681
+ function statusProfile(params) {
36682
+ const env = params.env ?? process.env;
36683
+ const chosen = params.profile ?? (env["PRIMITIVE_AGENT_PROFILE"]?.trim() || void 0);
36684
+ if (chosen) return { profile: chosen };
36685
+ const session = guardedSession(void 0, env);
36686
+ if (!session) return {
36687
+ profile: null,
36688
+ bound: []
36689
+ };
36690
+ const { others } = inspectSessionAddresses({
36691
+ configDir: params.configDir,
36692
+ session
36693
+ });
36694
+ const only = others.length === 1 ? others[0] : void 0;
36695
+ return only ? { profile: only.profile } : {
36696
+ profile: null,
36697
+ bound: others.map((row) => row.profile)
36698
+ };
36699
+ }
36700
+ /** A hidden alias of `agent connect --status`, for the command agents guess. */
36701
+ var AgentStatusCommand = class AgentStatusCommand extends Command {
36702
+ static hidden = true;
36703
+ static summary = "Show this agent's saved connection (agent connect --status)";
36704
+ static description = `Same as \`agent connect --status\`: read the saved identity and local receiver health offline. The profile is --profile, else ${AGENT_PROFILE_ENV}, else the one address connected for this runtime session.`;
36705
+ static flags = {
36706
+ profile: Flags.string({ description: "Local agent profile name" }),
36707
+ json: Flags.boolean({ description: "Print the status as JSON" })
36708
+ };
36709
+ async run() {
36710
+ const { flags } = await this.parse(AgentStatusCommand);
36711
+ const picked = statusProfile({
36712
+ configDir: this.config.configDir,
36713
+ profile: flags.profile
36714
+ });
36715
+ if (picked.profile === null) {
36716
+ const detail = picked.bound.length > 1 ? `This session has several connected profiles (${picked.bound.join(", ")}). Pass --profile <name>.` : `No Primitive address is connected for this session. Pass --profile <name> or set ${AGENT_PROFILE_ENV} to inspect a saved profile.`;
36717
+ this.log(flags.json ? JSON.stringify({
36718
+ status: "not_configured",
36719
+ profileName: null,
36720
+ ...picked.bound.length > 1 ? { profiles: picked.bound } : {},
36721
+ detail
36722
+ }) : detail);
36723
+ process.exitCode = 1;
36724
+ return;
36725
+ }
36726
+ await AgentConnectCommand.run([
36727
+ "--status",
36728
+ "--profile",
36729
+ picked.profile,
36730
+ ...flags.json ? ["--json"] : []
36731
+ ], this.config);
36732
+ }
36733
+ };
36734
+ //#endregion
36632
36735
  //#region src/oclif/commands/agent-upgrade.ts
36633
36736
  /**
36634
36737
  * Interactive upgrade of an emailless agent account to a full developer
@@ -41505,7 +41608,7 @@ async function buildEmailBrief(input) {
41505
41608
  connected_agent_verified: detail.sender_connected_agent_verified === true
41506
41609
  },
41507
41610
  thread_id: threadId,
41508
- in_thread: sentInThread(thread, self) ?? null,
41611
+ in_thread: sentInThread(thread, self, input.isOwnSignal) ?? null,
41509
41612
  ...alsoAddressed(otherParticipants(detail, self)),
41510
41613
  attachments: briefAttachments(detail, { hideInteractionPart: interaction?.hint === "card" || interaction?.hint === "status" }),
41511
41614
  newer: thread?.newerInboundCount === void 0 ? null : {
@@ -41614,6 +41717,7 @@ async function runBrief(command, flags) {
41614
41717
  client: apiClient.client,
41615
41718
  detail,
41616
41719
  connected,
41720
+ isOwnSignal: (sentId) => isSentSignal(command.config.configDir, sentId),
41617
41721
  signal: AbortSignal.timeout(3e4)
41618
41722
  });
41619
41723
  command.log(flags.json ? JSON.stringify(brief, null, 2) : renderEmailBrief(brief));
@@ -43317,7 +43421,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
43317
43421
  url: "https://primitive.dev"
43318
43422
  };
43319
43423
  const SDK_VERSION_RANGE = "^1.35.0";
43320
- const CLI_VERSION_RANGE = "^1.48.0";
43424
+ const CLI_VERSION_RANGE = "^1.49.0";
43321
43425
  const ESBUILD_VERSION_RANGE = "^0.27.0";
43322
43426
  function renderHandler() {
43323
43427
  return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
@@ -46848,6 +46952,7 @@ async function runSharedNotificationListen(options) {
46848
46952
  contact: !authorization?.contactRequest
46849
46953
  }),
46850
46954
  localInThread: false,
46955
+ isOwnSignal: (sentId) => isSentSignal(options.configDir, sentId),
46851
46956
  signal: nextSignal
46852
46957
  });
46853
46958
  } catch {
@@ -47563,6 +47668,10 @@ async function createWakeMail(options) {
47563
47668
  });
47564
47669
  const identity = auth.connectedAgent;
47565
47670
  const recipient = identity?.agentAddress;
47671
+ if (identity) refreshOwnerMemberAddressPeriodically({
47672
+ configDir: options.configDir,
47673
+ profileName: identity.profileName
47674
+ });
47566
47675
  const policy = recipient ? apiContactPolicy(apiClient.client, recipient, options.contactRequests) : void 0;
47567
47676
  const notices = identity && options.contactRequests ? openContactRequestNotices(options.configDir, identity) : void 0;
47568
47677
  const activatedAt = notices ? Date.parse(notices.activate()) : 0;
@@ -47799,6 +47908,7 @@ async function createWakeMail(options) {
47799
47908
  contact: admission.kind === "allowed"
47800
47909
  }),
47801
47910
  localInThread: Boolean(requested || followed),
47911
+ isOwnSignal: (sentId) => isSentSignal(options.configDir, sentId),
47802
47912
  signal
47803
47913
  });
47804
47914
  if (!await recordNotice(detail, {
@@ -49848,7 +49958,7 @@ function shellQuote(value) {
49848
49958
  }
49849
49959
  const MANAGED_SUFFIX = /\bagent session-register --runtime codex --hook\s*$/;
49850
49960
  function codexHookCommand(cli) {
49851
- return `${shellQuote(cli.node)} ${shellQuote(cli.entry)} agent session-register --runtime codex --hook`;
49961
+ return `${shellQuote(cli.runtimeNode)} ${shellQuote(cli.entry)} agent session-register --runtime codex --hook`;
49852
49962
  }
49853
49963
  /** A SessionStart hook this CLI installed, from any CLI path or version. */
49854
49964
  function isManaged(hook) {
@@ -50074,6 +50184,10 @@ function repairCodexHook(settings, cli) {
50074
50184
  changed
50075
50185
  };
50076
50186
  }
50187
+ /** Whether Codex hooks.json carries the SessionStart hook, from any CLI path. */
50188
+ function hasCodexSessionHook(settings) {
50189
+ return managedPositions(settings).length > 0;
50190
+ }
50077
50191
  function writeCodexHooks(params) {
50078
50192
  const original = params.read.state === "present" ? params.read.text : null;
50079
50193
  const indent = original === null ? 2 : jsonIndentation(original);
@@ -50312,6 +50426,29 @@ async function ownerConnectionStatuses(credentials, fetchImpl) {
50312
50426
  }
50313
50427
  return partial();
50314
50428
  }
50429
+ /** Whether any saved agent profile still holds a credential. */
50430
+ function connectedProfileSaved(configDir) {
50431
+ let names;
50432
+ try {
50433
+ names = readdirSync(join(agentProfilesDirectory(configDir), "profiles"));
50434
+ } catch {
50435
+ return false;
50436
+ }
50437
+ return names.some((name) => {
50438
+ try {
50439
+ agentProfileName(name);
50440
+ return loadConnectedAgentProfile(configDir, name) !== null;
50441
+ } catch {
50442
+ return false;
50443
+ }
50444
+ });
50445
+ }
50446
+ /**
50447
+ * Why a machine-wide check does not apply: agents on this machine were
50448
+ * connected one session at a time with `agent connect`, and no member login
50449
+ * (which machine-wide session registration needs) is saved.
50450
+ */
50451
+ const PER_CONNECTION_SKIP = "Not set up: agents on this machine were connected one session at a time with `primitive agent connect`, which does not need it. To register every session on this machine automatically, sign in with `primitive login`, then run `primitive machine doctor --fix`.";
50315
50452
  function check(id, status, detail, extra = {}) {
50316
50453
  return {
50317
50454
  id,
@@ -50336,9 +50473,21 @@ async function runMachineDoctor(options) {
50336
50473
  const selected = (runtime) => !options.runtimes || options.runtimes.has(runtime);
50337
50474
  const present = Object.fromEntries(MACHINE_RUNTIMES.map((runtime) => [runtime, runtimePresent(runtime, paths, env)]));
50338
50475
  const skipRuntime = (runtime) => !selected(runtime) ? "Not selected with --runtime." : !present[runtime] ? `${runtime === "claude" ? "Claude Code" : runtime === "codex" ? "Codex" : "omp"} was not found on this machine.` : null;
50339
- const cli = currentCliLocation(options.cliEntry, env, options.execPath);
50476
+ const cli = currentCliLocation(options.cliEntry, env, options.execPath, {
50477
+ configDir: options.configDir,
50478
+ platform: options.platform
50479
+ });
50340
50480
  const ephemeral = cli ? ephemeralCliReason(cli.entry) : null;
50341
- const hookBlocked = !cli ? "This CLI's hook scripts were not found next to it." : ephemeral ? `This CLI runs from ${ephemeral}, which can be deleted; install it with \`npm install -g primitive\` before repairing hooks.` : null;
50481
+ const ephemeralBlocked = ephemeral ? `This CLI runs from ${ephemeral}, which can be deleted; install it with \`npm install -g primitive\` before repairing hooks.` : null;
50482
+ const hookBlocked = !cli ? "This CLI's hook scripts were not found next to it." : cli.launcher ? null : ephemeralBlocked;
50483
+ const codexBlocked = !cli ? "This CLI's hook scripts were not found next to it." : ephemeralBlocked;
50484
+ const perConnection = !(() => {
50485
+ try {
50486
+ return loadCliCredentials(options.configDir) !== null;
50487
+ } catch {
50488
+ return true;
50489
+ }
50490
+ })() && connectedProfileSaved(options.configDir);
50342
50491
  const skillEnv = {
50343
50492
  ...env,
50344
50493
  CLAUDE_CONFIG_DIR: paths.claudeDir,
@@ -50365,6 +50514,7 @@ async function runMachineDoctor(options) {
50365
50514
  path: target,
50366
50515
  action: "edit_file"
50367
50516
  });
50517
+ if (perConnection && (read.state === "absent" || inspectManagedBlock(read.text, body).state === "missing")) return check(id, "skip", PER_CONNECTION_SKIP, { path: target });
50368
50518
  if (read.state === "absent") return check(id, "fail", `${target} does not exist yet.`, {
50369
50519
  path: target,
50370
50520
  fixable: true
@@ -50409,6 +50559,7 @@ async function runMachineDoctor(options) {
50409
50559
  } catch {
50410
50560
  info = null;
50411
50561
  }
50562
+ if (!info && perConnection) return check(id, "skip", PER_CONNECTION_SKIP, { path: target });
50412
50563
  if (!info) return check(id, "fail", `Not installed at ${target}.`, {
50413
50564
  path: target,
50414
50565
  fixable: true
@@ -50467,12 +50618,20 @@ async function runMachineDoctor(options) {
50467
50618
  },
50468
50619
  restoreProfiles: options.profiles
50469
50620
  };
50621
+ const launcherMissing = () => !!cli?.launcher && settingsRead.ok && !existsSync(cli.launcher.path) && JSON.stringify(settingsRead.settings).includes(JSON.stringify(cli.launcher.path));
50470
50622
  const hookCheck = (id) => ({
50471
50623
  inspect: () => {
50472
50624
  const skip = skipRuntime("claude");
50473
50625
  if (skip) return check(id, "skip", skip);
50474
50626
  if (!settingsRead.ok) return check(id, "fail", "Claude settings.json is invalid; repair it first.", { path: join(paths.claudeDir, "settings.json") });
50475
- const finding = inspectClaudeHooks(settingsRead.settings, hookContext)[id];
50627
+ if (perConnection && id !== "claude.hook.stop" && !hasMachineSessionHooks(settingsRead.settings)) return check(id, "skip", PER_CONNECTION_SKIP, { path: join(paths.claudeDir, "settings.json") });
50628
+ const inspected = inspectClaudeHooks(settingsRead.settings, hookContext)[id];
50629
+ const finding = inspected.status === "fail" && launcherMissing() ? {
50630
+ ...inspected,
50631
+ detail: `${inspected.detail} The hook launcher ${cli?.launcher?.path} is missing; a repair writes it again.`
50632
+ } : inspected;
50633
+ const launcherWarning = id === "claude.hook.stop" && cli?.launcher ? readHookLauncherWarning(options.configDir) : null;
50634
+ if (launcherWarning && finding.status === "ok") return check(id, "warn", `${finding.detail} The last hook run reported: ${launcherWarning}`, { path: join(paths.claudeDir, "settings.json") });
50476
50635
  return check(id, finding.status, finding.detail, {
50477
50636
  fixable: finding.fixable,
50478
50637
  path: join(paths.claudeDir, "settings.json"),
@@ -50487,9 +50646,17 @@ async function runMachineDoctor(options) {
50487
50646
  repair: () => {
50488
50647
  if (!settingsRead.ok) return false;
50489
50648
  const findings = inspectClaudeHooks(settingsRead.settings, hookContext);
50490
- const ids = HOOK_CHECK_IDS.filter((hook) => (hook === id || !options.only || options.only.has(hook)) && findings[hook].status === "fail" && findings[hook].fixable);
50649
+ const machineHooksSkipped = perConnection && !hasMachineSessionHooks(settingsRead.settings);
50650
+ const ids = HOOK_CHECK_IDS.filter((hook) => (hook === "claude.hook.stop" || !machineHooksSkipped) && (hook === id || !options.only || options.only.has(hook)) && findings[hook].status === "fail" && findings[hook].fixable);
50491
50651
  const repaired = repairClaudeHooks(settingsRead.settings, hookContext, new Set(ids), { profiles: options.profiles });
50492
- if (!repaired.changed.size) return false;
50652
+ const restoreLauncher = launcherMissing();
50653
+ if (!repaired.changed.size && !restoreLauncher) return false;
50654
+ if (cli && hookBlocked === null) try {
50655
+ ensureHookLauncher(cli);
50656
+ } catch {
50657
+ throw new MachineFileError(`The hook launcher ${cli.launcher?.path ?? ""} could not be written.`);
50658
+ }
50659
+ if (!repaired.changed.size) return id === "claude.hook.stop";
50493
50660
  writeClaudeSettings({
50494
50661
  claudeDir: paths.claudeDir,
50495
50662
  read: settingsRead.read,
@@ -50512,17 +50679,21 @@ async function runMachineDoctor(options) {
50512
50679
  path: read.path,
50513
50680
  action: "edit_file"
50514
50681
  });
50682
+ if (perConnection && !hasCodexSessionHook(read.settings)) return check(id, "skip", PER_CONNECTION_SKIP, { path: read.path });
50515
50683
  const configToml = readManagedFile(join(paths.codexHome, "config.toml"));
50516
- const finding = inspectCodexHook(read, hookContext, configToml.state === "present" ? configToml.text : null);
50684
+ const finding = inspectCodexHook(read, {
50685
+ cli,
50686
+ blocked: codexBlocked
50687
+ }, configToml.state === "present" ? configToml.text : null);
50517
50688
  return check(id, finding.status, finding.detail, {
50518
50689
  fixable: finding.fixable,
50519
50690
  path: read.path,
50520
- ...finding.status === "fail" && !finding.fixable && hookBlocked ? { action: "install_cli" } : {}
50691
+ ...finding.status === "fail" && !finding.fixable && codexBlocked ? { action: "install_cli" } : {}
50521
50692
  });
50522
50693
  },
50523
50694
  repair: () => {
50524
50695
  const read = readCodexHooks(paths.codexHome);
50525
- if (!read.ok || !cli || hookBlocked) return false;
50696
+ if (!read.ok || !cli || codexBlocked) return false;
50526
50697
  const repaired = repairCodexHook(read.settings, cli);
50527
50698
  if (!repaired.changed) return false;
50528
50699
  writeCodexHooks({
@@ -50635,10 +50806,18 @@ async function runMachineDoctor(options) {
50635
50806
  } },
50636
50807
  "cli.path_stable": { inspect: () => {
50637
50808
  if (!cli) return check("cli.path_stable", "fail", "The running CLI's files could not be located.", { action: "install_cli" });
50638
- if (ephemeral) return check("cli.path_stable", "fail", `This CLI runs from ${ephemeral}; hooks pointing at it would break. Install it globally with \`npm install -g primitive\`.`, {
50809
+ if (ephemeral && !cli.launcher) return check("cli.path_stable", "fail", `This CLI runs from ${ephemeral}; hooks pointing at it would break. Install it globally with \`npm install -g primitive\`.`, {
50639
50810
  action: "install_cli",
50640
50811
  path: cli.entry
50641
50812
  });
50813
+ if (ephemeral) {
50814
+ const launcherNote = `This CLI runs from ${ephemeral}. Claude Code hooks run through ${cli.launcher?.path}, which uses the \`primitive\` on PATH or fetches this version again with npx if the cache is removed.`;
50815
+ const codexRead = readCodexHooks(paths.codexHome);
50816
+ return selected("codex") && present.codex && !(perConnection && codexRead.ok && !hasCodexSessionHook(codexRead.settings)) ? check("cli.path_stable", "warn", `${launcherNote} The Codex SessionStart hook names the CLI directly; install it globally with \`npm install -g primitive\` so that hook keeps working.`, {
50817
+ action: "install_cli",
50818
+ path: cli.entry
50819
+ }) : check("cli.path_stable", "ok", launcherNote, { path: cli.entry });
50820
+ }
50642
50821
  const onPath = findOnPath("primitive", env);
50643
50822
  if (onPath && !samePath(onPath, cli.entry)) return check("cli.path_stable", "warn", `\`primitive\` on PATH is ${onPath}, but this run is ${cli.entry}; hooks follow the copy that runs the doctor.`, { path: cli.entry });
50644
50823
  return check("cli.path_stable", "ok", `Hooks use ${cli.node} and ${cli.entry}.`, { path: cli.entry });
@@ -50650,7 +50829,7 @@ async function runMachineDoctor(options) {
50650
50829
  } catch {
50651
50830
  return check("auth.member_login", "fail", "The saved login file is unreadable. Sign in again.", { action: "login" });
50652
50831
  }
50653
- if (!saved) return check("auth.member_login", "fail", "No saved member login on this machine.", { action: "login" });
50832
+ if (!saved) return perConnection ? check("auth.member_login", "skip", "No member login is saved. Agents on this machine were connected with `primitive agent connect` and use their own saved credentials; sign in with `primitive login` only to register every session on this machine automatically.") : check("auth.member_login", "fail", "No saved member login on this machine.", { action: "login" });
50654
50833
  let credentials;
50655
50834
  try {
50656
50835
  credentials = await refreshStoredCliCredentials({
@@ -50804,7 +50983,7 @@ function renderCheck(check) {
50804
50983
  }
50805
50984
  var MachineDoctorCommand = class MachineDoctorCommand extends Command {
50806
50985
  static summary = "Check, and optionally repair, this machine's Primitive setup for coding agents";
50807
- static description = "Checks everything that lets Claude Code, Codex and omp sessions on this machine register with Primitive and answer mail: the CLI install and version, the saved member login, Claude settings and Primitive's SessionStart, SessionEnd and per-session receive hooks, the managed instruction block in each runtime's global instructions file, the bundled primitive-connect skill, and saved agent profiles disconnected in Primitive. Each check reports ok, warn, fail or skip, whether it can be repaired automatically, and an action (login, install_cli, update_cli, edit_file) when only a person can fix it. --fix repairs what it safely can: it only adds, rewrites or removes Primitive-owned hooks, blocks and skill copies, backs up any existing file beside it as <file>.primitive-bak-<timestamp> before changing it, writes atomically, and changes nothing when nothing drifted, so it is safe to run on a timer. Malformed or hand-edited files it cannot parse are reported and left unchanged. Exits 0 when every check is ok or skipped, 1 otherwise.";
50986
+ static description = "Checks everything that lets Claude Code, Codex and omp sessions on this machine register with Primitive and answer mail: the CLI install and version, the saved member login, Claude settings and Primitive's SessionStart, SessionEnd and per-session receive hooks, the managed instruction block in each runtime's global instructions file, the bundled primitive-connect skill, and saved agent profiles disconnected in Primitive. Each check reports ok, warn, fail or skip, whether it can be repaired automatically, and an action (login, install_cli, update_cli, edit_file) when only a person can fix it. --fix repairs what it safely can: it only adds, rewrites or removes Primitive-owned hooks, blocks and skill copies, backs up any existing file beside it as <file>.primitive-bak-<timestamp> before changing it, writes atomically, and changes nothing when nothing drifted, so it is safe to run on a timer. Malformed or hand-edited files it cannot parse are reported and left unchanged. On a machine whose agents were all connected with `agent connect` and that has no saved member login, machine-wide setup it does not use (the member login, SessionStart and SessionEnd hooks, instruction blocks, skills) is reported as skip, and --fix does not add it. Exits 0 when every check is ok or skipped, 1 otherwise.";
50808
50987
  static examples = [
50809
50988
  "<%= config.bin %> machine doctor",
50810
50989
  "<%= config.bin %> machine doctor --json",
@@ -57434,6 +57613,7 @@ const COMMANDS = {
57434
57613
  completion: CompletionCommand,
57435
57614
  "agent:upgrade": AgentUpgradeCommand,
57436
57615
  "agent:connect": AgentConnectCommand,
57616
+ "agent:status": AgentStatusCommand,
57437
57617
  "agent:check-mail": AgentCheckMailCommand,
57438
57618
  "agent:disconnect": AgentDisconnectCommand,
57439
57619
  "agent:enroll": AgentEnrollCommand,
@@ -1,2 +1,2 @@
1
- import { s as runBackgroundListenSupervisor } from "../listen-background-CyLc8VBn.js";
1
+ import { s as runBackgroundListenSupervisor } from "../listen-background-CMfxkeVP.js";
2
2
  export { runBackgroundListenSupervisor };
@@ -1,7 +1,7 @@
1
1
  import { N as agentProfileDirectory, P as agentProfileName, R as loadConnectedAgentProfile, j as AGENT_PROFILE_ENV } from "../cli-config-DGrZCUfw.js";
2
2
  import { d as readMailJson } from "../shared-mail-files-SPPcis9g.js";
3
- import { m as SESSION_UUID, n as detectAgentRuntime, o as notificationScope } from "../connect-skill-DHPc9dVN.js";
4
- import { a as claimBackgroundListenRestart, n as backgroundListenRestartable, r as backgroundListenStatus } from "../listen-background-CyLc8VBn.js";
3
+ import { m as SESSION_UUID, n as detectAgentRuntime, o as notificationScope } from "../connect-skill-DBkhFAkO.js";
4
+ import { a as claimBackgroundListenRestart, n as backgroundListenRestartable, r as backgroundListenStatus } from "../listen-background-CMfxkeVP.js";
5
5
  import { join } from "node:path";
6
6
  import { spawn } from "node:child_process";
7
7
  import { homedir } from "node:os";
@@ -1,4 +1,4 @@
1
- import { _ as isTrustedSender, c as classifySignalContent } from "./repeats-D2xtLium-DdiJECLI.js";
1
+ import { _ as isTrustedSender, c as classifySignalContent } from "./repeats-D2xtLium-B8Wq41vG.js";
2
2
  //#region src/oclif/presence-provenance.ts
3
3
  function readPresenceProjection(detail) {
4
4
  if (!detail || typeof detail !== "object") return null;
@@ -1,8 +1,8 @@
1
- import { Bt as createEndpoint, Dn as listSentEmails, Gn as sendEmail, T as bareAddress, in as getEmail, mt as successfulSendOutcome, o as extractErrorPayload, tt as classifySendError } from "./api-command-DzQ8KxD9.js";
1
+ import { Bt as createEndpoint, Dn as listSentEmails, Gn as sendEmail, T as bareAddress, in as getEmail, mt as successfulSendOutcome, o as extractErrorPayload, tt as classifySendError } from "./api-command-BixHVNwQ.js";
2
2
  import { _ as acquireListenLock, b as listenProcessIdentity, c as mailTime, d as readMailJson, f as removeMailFile, h as writeMailJson, i as mailId, l as privateMailDirectory, m as withMailLock, n as invalidSharedMail, o as mailObject, p as syncMailDirectory, r as mailAddress, s as mailString, t as SharedMailStateError, v as compareListenProcessIdentity, y as listenIdentity } from "./shared-mail-files-SPPcis9g.js";
3
- import { h as sendPreparedSignal, p as prepareSignalEmail } from "./repeats-D2xtLium-DdiJECLI.js";
4
- import { n as tryOwnSharedMail, r as waitForSharedMailChange, t as readSharedMailOwner } from "./shared-mail-watch-BUfUaq6j.js";
5
- import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply } from "./scoped-chat-DhxtuNXs.js";
3
+ import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply } from "./scoped-chat-DSLrmgWQ.js";
4
+ import { h as sendPreparedSignal, p as prepareSignalEmail } from "./repeats-D2xtLium-B8Wq41vG.js";
5
+ import { n as tryOwnSharedMail, r as waitForSharedMailChange, t as readSharedMailOwner } from "./shared-mail-watch-CWhpfJ0N.js";
6
6
  import { recordSentSignal } from "./oclif/auto-signals.js";
7
7
  import { Errors } from "@oclif/core";
8
8
  import { opendirSync } from "node:fs";