primcli 1.49.0 → 1.51.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,9 +1,9 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-CNZpIYwj.js";
2
2
  import { B as parseOwnerMemberAddress, F as agentProfilesDirectory, I as connectedAgentIdentity, L as connectedApiBaseUrl, M as AgentConnectionSetupError, N as agentProfileDirectory, P as agentProfileName, R as loadConnectedAgentProfile, U as saveConnectedAgentProfile, V as profileAlreadyConnectedMessage, W as PrimitiveApiClient, j as AGENT_PROFILE_ENV, x as loadCliCredentials, z as parseConnectedAgentProfile } from "./cli-config-DGrZCUfw.js";
3
- import { $t as getAgentContactPolicy, Dn as listSentEmails, Fn as putContact, Gn as sendEmail, Gt as deleteAgentContact, Hn as searchEmails, Kt as deleteContact, Nn as putAgentContact, Pn as putAgentContactPolicy, Pt as refreshStoredCliCredentials, _n as listContacts, bn as listDomains, en as getContact, hn as listAgentContacts, in as getEmail } from "./api-command-BixHVNwQ.js";
3
+ import { $n as searchEmails, En as listAgentContacts, Gn as putAgentContactPolicy, Gt as refreshStoredCliCredentials, Kn as putContact, On as listContacts, Rn as listSentEmails, V as defaultConnectionName, Wn as putAgentContact, dn as getContact, hn as getEmail, jn as listDomains, nn as deleteAgentContact, nr as sendEmail, rn as deleteContact, un as getAgentContactPolicy } from "./api-command-Bd21hvqY.js";
4
4
  import { _ as acquireListenLock, c as mailTime, d as readMailJson, f as removeMailFile, g as ListenStateError, h as writeMailJson, i as mailId, l as privateMailDirectory, o as mailObject, r as mailAddress, s as mailString } from "./shared-mail-files-SPPcis9g.js";
5
- import { a as scopedChatSenderTrust } from "./scoped-chat-DSLrmgWQ.js";
6
- import { _ as NativeSessionDisconnectedError, a as readBundledConnectSkill, g as defaultSessionSocket, h as connectNativeSession, m as SESSION_UUID, n as detectAgentRuntime, o as notificationScope, r as installConnectSkill, u as NotificationRetryError } from "./connect-skill-DBkhFAkO.js";
5
+ import { _ as detectAgentRuntime, a as NotificationRetryError, d as defaultSessionSocket, f as NativeSessionDisconnectedError, l as SESSION_UUID, t as notificationScope, u as connectNativeSession, v as installConnectSkill, x as readBundledConnectSkill } from "./notify-session-ejjYXO3O.js";
6
+ import { a as scopedChatSenderTrust } from "./scoped-chat-BJVYBRim.js";
7
7
  import { l as stopBackgroundListen, r as backgroundListenStatus } from "./listen-background-CMfxkeVP.js";
8
8
  import { t as readSharedMailOwner } from "./shared-mail-watch-CWhpfJ0N.js";
9
9
  import { accessSync, chmodSync, closeSync, constants, copyFileSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
@@ -1636,6 +1636,7 @@ const MAX_INVITATION_BYTES = 4096;
1636
1636
  const MAX_RESPONSE_BYTES = 32768;
1637
1637
  const UNCERTAIN_CLAIM = "Setup did not complete safely. The invitation may have been consumed. Request a fresh invitation from the owner; do not retry this invitation.";
1638
1638
  const INVITATION_UNAVAILABLE = "This invitation was already used, has expired, or was revoked, so it cannot connect this agent. Ask the owner to copy a fresh setup instruction from the app. Nothing was changed on this machine.";
1639
+ const INVITATION_ATTEMPTED_HERE = "An earlier setup on this machine already submitted this invitation and did not finish, so it was not submitted again; it may already be used. Ask the owner to copy a fresh setup instruction from the app. Nothing was changed on this machine.";
1639
1640
  const INVITATION_INVALID = "Primitive rejected this invitation as invalid. Ask the owner to copy a fresh setup instruction from the app. Nothing was changed on this machine.";
1640
1641
  /**
1641
1642
  * The server answered the claim with a definite refusal, so nothing was
@@ -1683,12 +1684,70 @@ async function definiteClaimRefusal(response) {
1683
1684
  }
1684
1685
  return null;
1685
1686
  }
1687
+ /** The message for an invitation another profile on this machine already claimed. */
1688
+ function invitationUsedHereMessage(address) {
1689
+ return `This invitation was already used on this machine${address ? ` to connect ${address}` : ""} under another profile, so it cannot connect this agent. Ask the owner to copy a fresh setup instruction from the app. Nothing was changed on this machine.`;
1690
+ }
1691
+ /**
1692
+ * A local record says this invitation was submitted before. It is never
1693
+ * submitted again: the claim is one-use, and a second submission could not
1694
+ * recover a credential lost with an earlier response. The refusal names the
1695
+ * address when the earlier attempt finished, and is a definite local refusal
1696
+ * either way, so a caller may discard whatever it created for this run.
1697
+ */
1698
+ function earlierAttemptRefusal(configDir, journal, invitationHash) {
1699
+ const record = journal;
1700
+ let address = null;
1701
+ if (record?.status === "claimed" && typeof record.agent_address === "string") address = record.agent_address;
1702
+ else if (typeof record?.profile_name === "string") try {
1703
+ const earlier = loadConnectedAgentProfile(configDir, record.profile_name);
1704
+ if (earlier?.invitation_hash === invitationHash) address = earlier.agent_address;
1705
+ } catch {}
1706
+ return address !== null || record?.status === "claimed" ? new AgentInvitationRejectedError(invitationUsedHereMessage(address), "invitation_used_here") : new AgentInvitationRejectedError(INVITATION_ATTEMPTED_HERE, "invitation_attempted_here");
1707
+ }
1686
1708
  /** Removes a directory only when it is empty. Best effort. */
1687
1709
  function removeEmptyDirectory(path) {
1688
1710
  try {
1689
1711
  rmdirSync(path);
1690
1712
  } catch {}
1691
1713
  }
1714
+ const CONNECTION_NAME_FILE = "connection-name.json";
1715
+ /**
1716
+ * The display name this profile's claim reported, saved beside the profile
1717
+ * so a resumed setup can report it too. Undefined when none was saved.
1718
+ */
1719
+ function savedConnectionName(configDir, profileName) {
1720
+ try {
1721
+ const saved = readMailJson(join(agentProfileDirectory(configDir, profileName), CONNECTION_NAME_FILE));
1722
+ return saved?.version === 1 ? claimedConnectionName({ data: { connection: { name: saved.name } } }) : void 0;
1723
+ } catch {
1724
+ return;
1725
+ }
1726
+ }
1727
+ /**
1728
+ * Saves the connection's current display name beside its profile: from the
1729
+ * claim, and again after `agent rename`. Best effort: without it a resume
1730
+ * omits the name, as for a claim made by an older version.
1731
+ */
1732
+ function saveConnectionName(configDir, profileName, name) {
1733
+ try {
1734
+ writeMailJson(join(agentProfileDirectory(configDir, profileName), CONNECTION_NAME_FILE), {
1735
+ version: 1,
1736
+ name
1737
+ });
1738
+ } catch {}
1739
+ }
1740
+ /** The display name a claim response reports, or undefined when unusable. */
1741
+ function claimedConnectionName(value) {
1742
+ const name = value?.data?.connection?.name;
1743
+ if (typeof name !== "string") return void 0;
1744
+ const trimmed = name.trim();
1745
+ const printable = Array.from(trimmed).every((character) => {
1746
+ const code = character.charCodeAt(0);
1747
+ return code >= 32 && !(code >= 127 && code <= 159) && code !== 8232 && code !== 8233;
1748
+ });
1749
+ return trimmed.length > 0 && trimmed.length <= 80 && printable ? trimmed : void 0;
1750
+ }
1692
1751
  /** The receiver a saved profile's setup record names. Offline. */
1693
1752
  function savedReceiver(configDir, profileName) {
1694
1753
  try {
@@ -1903,12 +1962,18 @@ async function connectAgent(params) {
1903
1962
  identity: connectedAgentIdentity(profileName, existing)
1904
1963
  };
1905
1964
  }
1965
+ const journal = join(directory, "claims", `${invitationHash}.json`);
1966
+ privateMailDirectory(join(directory, "claims"), true);
1967
+ let earlier;
1968
+ try {
1969
+ earlier = readMailJson(journal);
1970
+ } catch {
1971
+ throw new AgentConnectionSetupError(UNCERTAIN_CLAIM);
1972
+ }
1973
+ if (earlier !== null) throw earlierAttemptRefusal(params.configDir, earlier, invitationHash);
1906
1974
  privateMailDirectory(join(directory, "profiles"), true);
1907
1975
  const profileDirectory = agentProfileDirectory(params.configDir, profileName);
1908
1976
  privateMailDirectory(profileDirectory, true);
1909
- const journal = join(directory, "claims", `${invitationHash}.json`);
1910
- privateMailDirectory(join(directory, "claims"), true);
1911
- if (readMailJson(journal) !== null) throw new AgentConnectionSetupError(UNCERTAIN_CLAIM);
1912
1977
  writeMailJson(journal, {
1913
1978
  version: 1,
1914
1979
  profile_name: profileName,
@@ -1940,11 +2005,23 @@ async function connectAgent(params) {
1940
2005
  removeEmptyDirectory(profileDirectory);
1941
2006
  throw refusal;
1942
2007
  }
1943
- const profile = profileFromClaim(await readClaimResponse(response), invitation, invitationHash, params.now ?? Date.now);
2008
+ const body = await readClaimResponse(response);
2009
+ const profile = profileFromClaim(body, invitation, invitationHash, params.now ?? Date.now);
2010
+ const name = claimedConnectionName(body);
1944
2011
  saveConnectedAgentProfile(params.configDir, profileName, profile);
2012
+ try {
2013
+ writeMailJson(journal, {
2014
+ version: 1,
2015
+ profile_name: profileName,
2016
+ status: "claimed",
2017
+ agent_address: profile.agent_address
2018
+ });
2019
+ } catch {}
2020
+ if (name !== void 0) saveConnectionName(params.configDir, profileName, name);
1945
2021
  return {
1946
2022
  status: "claimed",
1947
- identity: connectedAgentIdentity(profileName, profile)
2023
+ identity: connectedAgentIdentity(profileName, profile),
2024
+ ...name === void 0 ? {} : { name }
1948
2025
  };
1949
2026
  } catch (error) {
1950
2027
  if (error instanceof AgentInvitationRejectedError) throw error;
@@ -3140,6 +3217,7 @@ async function setupAgent(params) {
3140
3217
  privateMailDirectory(directory, true);
3141
3218
  const release = acquireListenLock(directory, "setup");
3142
3219
  let discardStub = false;
3220
+ let claimedName;
3143
3221
  try {
3144
3222
  const path = join(directory, "setup.json");
3145
3223
  const value = readMailJson(path);
@@ -3167,13 +3245,13 @@ async function setupAgent(params) {
3167
3245
  writeMailJson(path, state);
3168
3246
  }
3169
3247
  try {
3170
- await connectAgent({
3248
+ claimedName = (await connectAgent({
3171
3249
  configDir: params.configDir,
3172
3250
  profileName,
3173
3251
  invitation: params.invitation ?? "",
3174
3252
  fetch: params.fetch,
3175
3253
  presence: true
3176
- });
3254
+ })).name;
3177
3255
  } catch (error) {
3178
3256
  if (error instanceof AgentInvitationRejectedError && created) {
3179
3257
  discardStub = true;
@@ -3215,8 +3293,10 @@ async function setupAgent(params) {
3215
3293
  "bounced",
3216
3294
  "canceled"
3217
3295
  ].includes(state.receipt?.status ?? "") ? "reply_failed" : ["unknown", "wait_timeout"].includes(state.receipt?.status ?? "") ? "delivery_unknown" : "reply_submitted" : state?.phase === "sending" ? "send_unknown" : "challenge_pending";
3296
+ claimedName ??= savedConnectionName(params.configDir, profileName);
3218
3297
  const result = (receiving, ownerNotifications) => ({
3219
3298
  identity: context.identity,
3299
+ ...claimedName === void 0 ? {} : { connectionName: claimedName },
3220
3300
  sessionId: params.session ?? state?.session ?? null,
3221
3301
  verification: {
3222
3302
  state: confirmed && submittedState() === "reply_submitted" ? "verified" : submittedState(),
@@ -3675,6 +3755,10 @@ async function runAgentConnect(options) {
3675
3755
  ownerAddress: result.identity.ownerAddress,
3676
3756
  ownerMemberAddress,
3677
3757
  profile: result.identity.profileName,
3758
+ ...result.connectionName === void 0 ? {} : {
3759
+ name: result.connectionName,
3760
+ nameIsDefault: defaultConnectionName(result.connectionName, result.identity.agentAddress)
3761
+ },
3678
3762
  sessionId: session ?? null,
3679
3763
  runtime,
3680
3764
  cli: {
@@ -3696,45 +3780,6 @@ async function runAgentConnect(options) {
3696
3780
  };
3697
3781
  }
3698
3782
  //#endregion
3699
- //#region src/oclif/agent-identity-suggestions.ts
3700
- /** The connection name enrollment uses when none is given. */
3701
- const DEFAULT_ENROLL_NAME = "Coding agent";
3702
- /** npx runs the CLI from its cache; print follow-up commands the same way. */
3703
- function cliInvocation(entry) {
3704
- return entry && /[\\/]_npx[\\/]/.test(entry) ? "npx -y primitive@latest" : "primitive";
3705
- }
3706
- /**
3707
- * Whether an enrollment's connection name is the fallback it chose by itself.
3708
- * Session registration's `<runtime>-<repository>` names are reported by
3709
- * registration, which knows the exact name it generated.
3710
- */
3711
- function generatedAgentName(name) {
3712
- return name === DEFAULT_ENROLL_NAME;
3713
- }
3714
- /**
3715
- * Additive result fields for a connect, enroll or session registration.
3716
- * `nameIsDefault` appears only when it is known whether the connection name
3717
- * is one this CLI generated. The runtime note is suggested once, after a
3718
- * connection this run completed.
3719
- */
3720
- function identitySuggestions(params) {
3721
- const prefix = `PRIMITIVE_AGENT_PROFILE=${params.profile} ${params.invocation}`;
3722
- const suggestions = [];
3723
- const nameIsDefault = params.nameIsDefault;
3724
- if (nameIsDefault) suggestions.push({
3725
- kind: "rename",
3726
- command: `${prefix} agent rename "<new name>"`
3727
- });
3728
- if (params.connectedNow) suggestions.push({
3729
- kind: "runtime_note",
3730
- command: `${prefix} agent runtime set`
3731
- });
3732
- return nameIsDefault === void 0 ? { suggestions } : {
3733
- nameIsDefault,
3734
- suggestions
3735
- };
3736
- }
3737
- //#endregion
3738
3783
  //#region src/oclif/agent-disconnect.ts
3739
3784
  var AgentDisconnectError = class extends Error {};
3740
3785
  function credentialDigest(profile) {
@@ -5211,4 +5256,4 @@ async function runDetached(params) {
5211
5256
  }
5212
5257
  }
5213
5258
  //#endregion
5214
- export { currentCliLocation as $, NETWORK_ADMISSION_PENDING_RETRY_MS as A, contactAgentAddress as B, invitationProfileName as C, externalReceivingState as D, RECEIVER_MODES as E, AddressNotesApiError as F, readAgentInvitation as G, agentConnectionStatus as H, addressNoteTarget as I, savedReceiver as J, receiverStatusCommand as K, runAddressNotesRequest as L, createNotificationContactPolicy as M, evaluateContactPolicy as N, verificationReplySubmitted as O, canonicalContactSelector as P, claudeConfigDir as Q, ContactsApiError as R, defaultAgentProfileName as S, runAgentConnect as T, agentInvitationHash as U, runContactRequest as V, connectAgent as W, recordHookMailCheck as X, installClaudeWakeHook as Y, boundSessionProfiles as Z, disconnectAgent as _, readHookLauncherWarning as _t, endSession as a, hasMachineSessionHooks as at, identitySuggestions as b, pendingSessionDisconnects as c, repairClaudeHooks as ct, registerSession as d, MachineFileError as dt, describeSessionHookChange as et, retryPendingDisconnects as f, inspectManagedBlock as ft, AgentDisconnectError as g, writeManagedFile as gt, enrollmentResumes as h, upsertManagedBlock as ht, connectedProfilesForSession as i, findOnPath as it, NetworkAdmissionPendingError as j, ContactPolicyReadRetryError as k, psProcessInfo as l, samePath as lt, enrollAgent as m, readManagedFile as mt, OMP_MAIN as n, ensureHookLauncher as nt, headlessClaudeRun as o, inspectClaudeHooks as ot, runDetached as p, jsonIndentation as pt, refusalAfterReplacement as q, agentConnectionState as r, ephemeralCliReason as rt, machine_session_exports as s, readClaudeSettings as st, MACHINE_RUNTIMES as t, describeSessionHookItem as tt, readClaudeHookInput as u, writeClaudeSettings as ut, cliInvocation as v, refreshOwnerMemberAddressPeriodically as vt, pollCheckCommand as w, connectWarnings as x, generatedAgentName as y, withOwnerMemberAddress as yt, contactAddress as z };
5259
+ export { describeSessionHookItem as $, evaluateContactPolicy as A, agentInvitationHash as B, RECEIVER_MODES as C, NETWORK_ADMISSION_PENDING_RETRY_MS as D, ContactPolicyReadRetryError as E, ContactsApiError as F, saveConnectionName as G, readAgentInvitation as H, contactAddress as I, recordHookMailCheck as J, savedReceiver as K, contactAgentAddress as L, AddressNotesApiError as M, addressNoteTarget as N, NetworkAdmissionPendingError as O, runAddressNotesRequest as P, describeSessionHookChange as Q, runContactRequest as R, runAgentConnect as S, verificationReplySubmitted as T, receiverStatusCommand as U, connectAgent as V, refusalAfterReplacement as W, claudeConfigDir as X, boundSessionProfiles as Y, currentCliLocation as Z, disconnectAgent as _, withOwnerMemberAddress as _t, endSession as a, readClaudeSettings as at, invitationProfileName as b, pendingSessionDisconnects as c, writeClaudeSettings as ct, registerSession as d, jsonIndentation as dt, ensureHookLauncher as et, retryPendingDisconnects as f, readManagedFile as ft, AgentDisconnectError as g, refreshOwnerMemberAddressPeriodically as gt, enrollmentResumes as h, readHookLauncherWarning as ht, connectedProfilesForSession as i, inspectClaudeHooks as it, canonicalContactSelector as j, createNotificationContactPolicy as k, psProcessInfo as l, MachineFileError as lt, enrollAgent as m, writeManagedFile as mt, OMP_MAIN as n, findOnPath as nt, headlessClaudeRun as o, repairClaudeHooks as ot, runDetached as p, upsertManagedBlock as pt, installClaudeWakeHook as q, agentConnectionState as r, hasMachineSessionHooks as rt, machine_session_exports as s, samePath as st, MACHINE_RUNTIMES as t, ephemeralCliReason as tt, readClaudeHookInput as u, inspectManagedBlock as ut, connectWarnings as v, externalReceivingState as w, pollCheckCommand as x, defaultAgentProfileName as y, agentConnectionStatus as z };