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.
- package/README.md +20 -3
- package/bin/claude-pending-mail.mjs +95 -1
- package/bin/claude-wake.mjs +3 -1
- package/dist/{api-command-BixHVNwQ.js → api-command-Bd21hvqY.js} +195 -6
- package/dist/{machine-session-BEkDi4_N.js → machine-session-DqxgyupM.js} +95 -50
- package/dist/{connect-skill-DBkhFAkO.js → notify-session-ejjYXO3O.js} +283 -242
- package/dist/oclif/auto-signal-worker.js +2 -2
- package/dist/oclif/auto-signals.js +1 -1
- package/dist/oclif/index.js +217 -32
- package/dist/oclif/receiver-heal.js +1 -1
- package/dist/{signal-command-D0Rig6eA.js → signal-command-DaQuMT8l.js} +2 -2
- package/dist/skills/primitive-connect/SKILL.md +3 -1
- package/dist/skills/primitive-connect/references/communication.md +7 -0
- package/dist/skills/primitive-connect.json +4 -4
- package/package.json +1 -1
- /package/dist/{scoped-chat-DSLrmgWQ.js → scoped-chat-BJVYBRim.js} +0 -0
package/dist/oclif/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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
|
|
2
|
+
import { $ as readEmailInteraction, $n as searchEmails, $t as createRoute, A as readThreadContext, An as listDefaultNetworkMembers, At as withFlagSuggestion, B as connectNextSteps, Bn as listWakeDispatches, Bt as isAutomatedRejectedError, C as queryUsesAwaiting, Cn as getSentEmail, Ct as sentHistoryWindowStart, D as displayAddress, Dn as listAgentNetworks, Dt as currentMailSessionKey, E as describeWake, En as listAgentContacts, Et as thrownErrorExitCode, F as wakeReadCommand, Fn as listFunctions, Ft as assertAutomatedVerdict, G as clearConsumedPendingMail, Gt as refreshStoredCliCredentials, H as generatedAgentName, Hn as pollCliLogin, I as wakeRecipientField, In as listRepeatingSends, It as automatedRejectedError, J as readPendingMail, Jn as removeDefaultNetworkMember, Jt as checkDefaultNetworkContactAdmission, K as clearReadPendingMail, Kt as resolveCliApiRequestConfig, L as wakeRelationship, Ln as listRoutes, Lt as ensureSearchReportsAutomated, M as serverMuted, Mn as listEmails, Mt as AUTOMATED_FLAG_DESCRIPTION, N as serverRelationship, Nn as listEndpoints, Nt as AUTOMATED_VALUES, O as formatMailWakeLine, Ot as formatAlreadySentNotice, Pn as listFunctionLogs, Pt as AutomatedFilterUnsupportedError, Q as interactionNextActions, Qn as runWakeSchedule, Qt as createFunction, Rn as listSentEmails, Rt as expectedAutomatedVerdicts, S as isAwaitingRejectedError, Sn as getRepeatingSend, St as sendWaitClampNotice, T as bareAddress, Tn as getWakeSchedule, Tt as successfulSendOutcome, U as identitySuggestions, Un as pullWebhookEvent, Ut as createAuthenticatedCliApiClient, Vn as listWakeSchedules, Vt as queryUsesAutomated, W as loadSkillLine, Wt as createCliApiClient, X as removePendingMail, Xn as replyToEmail, Xt as completeWebhookEvent, Y as recordPendingMail, Yn as reorderRoutes, Yt as cliLogout, Z as interactionHumanLine, Zn as resendAgentSignupVerification, Zt as createEndpoint, _ as awaitingRejectedError, _n as getFunctionRouting, _r as updateWakeAuthorization, _t as formatSendRecordFailureSummary, a as extractErrorCode, an as deleteRoute, ar as setMemory, at as SEND_OUTCOME_HELP, b as formatRepliesCell, bn as getMemory, br as verifyAgentSignup, bt as sendIdentityFields, c as readTextFileFlag, cr as startAgentSignup, ct as buildFollowUpCommand, d as writeErrorWithHints, dr as testFunction, dt as classifySendError, en as createWakeAuthorization, er as searchMemories, et as replyExpectation, f as AWAITING_FLAG_DESCRIPTION, fn as getConversation, fr as unsetFunctionRoute, ft as deriveSendIdempotencyKey, g as assertAwaitingFilterRows, gn as getFunction, gr as updateRoute, gt as formatSendFailureSummary, h as ReplyStateUnsupportedError, hn as getEmail, hr as updateRepeatingSend, ht as formatPriorRepliesWarning, i as createOperationCommand, in as deleteMemory, ir as setFunctionSecret, it as MAX_SEND_WAIT_TIMEOUT_MS, j as sentInThread, jn as listDomains, jt as AUTOMATED_FILTER_UNSUPPORTED_CODE, k as latestOwnOutbound, kn as listDefaultNetworkAgents, kt as writeIdempotentReplayBannerIfReplay, l as runWithTiming, ln as getAccount, lr as startCliLogin, lt as buildThrownSendFailureEnvelope, m as REPLY_STATE_UNSUPPORTED_CODE, mn as getDefaultNetworkAgent, mr as updateFunction, mt as formatPriorRepliesCheckSkipped, n as API_ERROR_CODES, nr as sendEmail, o as extractErrorPayload, on as deleteWakeAuthorization, or as simulateRoute, ot as apiResultHttpStatus, p as AWAITING_VALUES, pn as getCreditBalance, pr as updateDefaultNetworkMember, pt as formatDeletedEarlierSendNotice, q as pendingMailPath, qn as redeemCreditCode, qt as addDefaultNetworkMember, r as TIME_FLAG_DESCRIPTION, rr as setFunctionRoute, rt as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, s as formatErrorPayload, sn as deleteWakeSchedule, sr as startAgentClaim, st as assertValidIdempotencyKey, t as API_BASE_URL_FLAG_DESCRIPTION, tn as createWakeSchedule, tr as semanticSearch, tt as replyInteractionWarning, u as surfaceUnauthorizedHint, un as getAgentContactPolicy, ur as stopRepeatFromEmail, ut as checkPriorReplies, v as ensureSearchReportsReplyState, vn as getFunctionTestRunTrace, vr as updateWakeSchedule, vt as priorRepliesThatWentOut, w as explainPendingMailNotFound, wn as getThread, wt as serializeErrorPayload, x as hasReplyState, xn as getOrgRoutingTopology, xt as sendOutcomeExitCode, y as formatAwaitingCell, yn as getInboxStatus, yr as verifyAgentClaim, yt as reportSendCommandResult, z as cliInvocation, zn as listWakeAuthorizations, zt as hasAutomatedVerdict } from "../api-command-Bd21hvqY.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 {
|
|
5
|
-
import { a as
|
|
6
|
-
import {
|
|
4
|
+
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-ejjYXO3O.js";
|
|
5
|
+
import { $ as describeSessionHookItem, A as evaluateContactPolicy, B as agentInvitationHash, C as RECEIVER_MODES, D as NETWORK_ADMISSION_PENDING_RETRY_MS, E as ContactPolicyReadRetryError, F as ContactsApiError, G as saveConnectionName, H as readAgentInvitation, I as contactAddress, J as recordHookMailCheck, K as savedReceiver, L as contactAgentAddress, M as AddressNotesApiError, N as addressNoteTarget, O as NetworkAdmissionPendingError, P as runAddressNotesRequest, Q as describeSessionHookChange, R as runContactRequest, S as runAgentConnect, T as verificationReplySubmitted, U as receiverStatusCommand, V as connectAgent, W as refusalAfterReplacement, X as claudeConfigDir, Y as boundSessionProfiles, Z as currentCliLocation, _ as disconnectAgent, _t as withOwnerMemberAddress, a as endSession, at as readClaudeSettings, b as invitationProfileName, c as pendingSessionDisconnects, ct as writeClaudeSettings, d as registerSession, dt as jsonIndentation, et as ensureHookLauncher, f as retryPendingDisconnects, ft as readManagedFile, g as AgentDisconnectError, gt as refreshOwnerMemberAddressPeriodically, h as enrollmentResumes, ht as readHookLauncherWarning, i as connectedProfilesForSession, it as inspectClaudeHooks, j as canonicalContactSelector, k as createNotificationContactPolicy, l as psProcessInfo, lt as MachineFileError, m as enrollAgent, mt as writeManagedFile, n as OMP_MAIN, nt as findOnPath, o as headlessClaudeRun, ot as repairClaudeHooks, p as runDetached, pt as upsertManagedBlock, q as installClaudeWakeHook, r as agentConnectionState, rt as hasMachineSessionHooks, st as samePath, t as MACHINE_RUNTIMES, tt as ephemeralCliReason, u as readClaudeHookInput, ut as inspectManagedBlock, v as connectWarnings, w as externalReceivingState, x as pollCheckCommand, y as defaultAgentProfileName, z as agentConnectionStatus } from "../machine-session-DqxgyupM.js";
|
|
6
|
+
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-BJVYBRim.js";
|
|
7
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
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-
|
|
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-DaQuMT8l.js";
|
|
10
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";
|
|
@@ -12255,6 +12255,14 @@ const openapiDocument = {
|
|
|
12255
12255
|
"user_id",
|
|
12256
12256
|
"name"
|
|
12257
12257
|
]
|
|
12258
|
+
},
|
|
12259
|
+
"fyi": {
|
|
12260
|
+
"type": "boolean",
|
|
12261
|
+
"description": "Outbound messages only. True when the send was an informational signal (a read, working or typing signal, or an acknowledgement) and nothing else, so it is not a reply that takes part in the conversation. Absent on inbound messages. Older servers omit it.\n"
|
|
12262
|
+
},
|
|
12263
|
+
"interaction_hint": {
|
|
12264
|
+
"type": "string",
|
|
12265
|
+
"description": "Outbound messages only. How to place this send in a timeline: `status` (a pure status signal), `card` or `none`. Not declared as a closed enum so that a value added later does not fail decoding; treat an unknown value as `none`. Absent on inbound messages. Older servers omit it.\n"
|
|
12258
12266
|
}
|
|
12259
12267
|
},
|
|
12260
12268
|
"required": ["direction", "id"]
|
|
@@ -12360,6 +12368,14 @@ const openapiDocument = {
|
|
|
12360
12368
|
"user_id",
|
|
12361
12369
|
"name"
|
|
12362
12370
|
]
|
|
12371
|
+
},
|
|
12372
|
+
"fyi": {
|
|
12373
|
+
"type": "boolean",
|
|
12374
|
+
"description": "Outbound messages only. True when the send was an informational signal (a read, working or typing signal, or an acknowledgement) and nothing else, so it is not a reply that takes part in the conversation. Absent on inbound messages. Older servers omit it.\n"
|
|
12375
|
+
},
|
|
12376
|
+
"interaction_hint": {
|
|
12377
|
+
"type": "string",
|
|
12378
|
+
"description": "Outbound messages only. How to place this send in a timeline: `status` (a pure status signal), `card` or `none`. Not declared as a closed enum so that a value added later does not fail decoding; treat an unknown value as `none`. Absent on inbound messages. Older servers omit it.\n"
|
|
12363
12379
|
}
|
|
12364
12380
|
},
|
|
12365
12381
|
"required": [
|
|
@@ -22848,6 +22864,14 @@ const operationManifest = [
|
|
|
22848
22864
|
"user_id",
|
|
22849
22865
|
"name"
|
|
22850
22866
|
]
|
|
22867
|
+
},
|
|
22868
|
+
"fyi": {
|
|
22869
|
+
"type": "boolean",
|
|
22870
|
+
"description": "Outbound messages only. True when the send was an informational signal (a read, working or typing signal, or an acknowledgement) and nothing else, so it is not a reply that takes part in the conversation. Absent on inbound messages. Older servers omit it.\n"
|
|
22871
|
+
},
|
|
22872
|
+
"interaction_hint": {
|
|
22873
|
+
"type": "string",
|
|
22874
|
+
"description": "Outbound messages only. How to place this send in a timeline: `status` (a pure status signal), `card` or `none`. Not declared as a closed enum so that a value added later does not fail decoding; treat an unknown value as `none`. Absent on inbound messages. Older servers omit it.\n"
|
|
22851
22875
|
}
|
|
22852
22876
|
},
|
|
22853
22877
|
"required": [
|
|
@@ -34266,6 +34290,14 @@ const operationManifest = [
|
|
|
34266
34290
|
"user_id",
|
|
34267
34291
|
"name"
|
|
34268
34292
|
]
|
|
34293
|
+
},
|
|
34294
|
+
"fyi": {
|
|
34295
|
+
"type": "boolean",
|
|
34296
|
+
"description": "Outbound messages only. True when the send was an informational signal (a read, working or typing signal, or an acknowledgement) and nothing else, so it is not a reply that takes part in the conversation. Absent on inbound messages. Older servers omit it.\n"
|
|
34297
|
+
},
|
|
34298
|
+
"interaction_hint": {
|
|
34299
|
+
"type": "string",
|
|
34300
|
+
"description": "Outbound messages only. How to place this send in a timeline: `status` (a pure status signal), `card` or `none`. Not declared as a closed enum so that a value added later does not fail decoding; treat an unknown value as `none`. Absent on inbound messages. Older servers omit it.\n"
|
|
34269
34301
|
}
|
|
34270
34302
|
},
|
|
34271
34303
|
"required": ["direction", "id"]
|
|
@@ -35321,6 +35353,7 @@ async function checkAgentMail(options) {
|
|
|
35321
35353
|
more = page === maxPages - 1;
|
|
35322
35354
|
}
|
|
35323
35355
|
const output = {
|
|
35356
|
+
...emails.length > 0 ? { guidance: loadSkillLine(installedConnectSkillFile({ runtime: mailCheckRuntime() })) } : {},
|
|
35324
35357
|
outcome: emails.length > 0 ? "mail" : "empty",
|
|
35325
35358
|
emails,
|
|
35326
35359
|
more,
|
|
@@ -35340,6 +35373,11 @@ async function checkAgentMail(options) {
|
|
|
35340
35373
|
release();
|
|
35341
35374
|
}
|
|
35342
35375
|
}
|
|
35376
|
+
/** The runtime this mail check runs in, from the runtime's own session variable. */
|
|
35377
|
+
function mailCheckRuntime() {
|
|
35378
|
+
const key = currentMailSessionKey();
|
|
35379
|
+
return key?.startsWith("claude:") ? "claude" : key?.startsWith("codex:") ? "codex" : null;
|
|
35380
|
+
}
|
|
35343
35381
|
//#endregion
|
|
35344
35382
|
//#region src/oclif/commands/agent-check-mail.ts
|
|
35345
35383
|
/** npx runs the CLI from its cache; print follow-up commands the same way. */
|
|
@@ -35352,7 +35390,7 @@ var AgentCheckMailCommand = class AgentCheckMailCommand extends Command {
|
|
|
35352
35390
|
|
|
35353
35391
|
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.
|
|
35354
35392
|
|
|
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>).`;
|
|
35393
|
+
Requires a connected agent profile selected with ${AGENT_PROFILE_ENV}. Output is one JSON document: \`guidance\` (present only with mail: one line to load the primitive-connect skill before handling it), \`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>).`;
|
|
35356
35394
|
static examples = [`${AGENT_PROFILE_ENV}=connection-0123456789ab <%= config.bin %> agent check-mail --json`];
|
|
35357
35395
|
static flags = { json: Flags.boolean({ description: "Print JSON (already the default)" }) };
|
|
35358
35396
|
async run() {
|
|
@@ -35511,7 +35549,7 @@ function invocation$1(entry) {
|
|
|
35511
35549
|
return entry && /[\\/]_npx[\\/]/.test(entry) ? "npx -y primitive@latest" : "primitive";
|
|
35512
35550
|
}
|
|
35513
35551
|
var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
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.`;
|
|
35552
|
+
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. 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.`;
|
|
35515
35553
|
static summary = "Connect and verify an agent address";
|
|
35516
35554
|
static examples = [
|
|
35517
35555
|
"<%= config.bin %> agent connect --session \"$CODEX_THREAD_ID\" --name Research --info \"Reviews pull requests\" --json < private-invitation.txt",
|
|
@@ -35666,13 +35704,29 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
35666
35704
|
});
|
|
35667
35705
|
const external = output.receiving.mode === "external";
|
|
35668
35706
|
const poll = output.receiving.mode === "poll";
|
|
35707
|
+
const identity = identitySuggestions({
|
|
35708
|
+
invocation: invocation$1(process.argv[1]),
|
|
35709
|
+
profile: output.profile,
|
|
35710
|
+
..."nameIsDefault" in output ? { nameIsDefault: output.nameIsDefault } : {},
|
|
35711
|
+
connectedNow: output.status === "connected"
|
|
35712
|
+
});
|
|
35713
|
+
const nextSteps = output.status === "connected" ? connectNextSteps({
|
|
35714
|
+
address: output.address,
|
|
35715
|
+
receiving: output.receiving,
|
|
35716
|
+
..."name" in output ? { name: output.name } : {},
|
|
35717
|
+
suggestions: identity.suggestions,
|
|
35718
|
+
skillFile: installedConnectSkillFile({
|
|
35719
|
+
runtime: output.skill.runtime,
|
|
35720
|
+
installedPath: output.skill.path
|
|
35721
|
+
})
|
|
35722
|
+
}) : [];
|
|
35669
35723
|
const suggested = {
|
|
35670
35724
|
...output,
|
|
35671
|
-
...
|
|
35672
|
-
|
|
35673
|
-
|
|
35674
|
-
|
|
35675
|
-
}
|
|
35725
|
+
...identity,
|
|
35726
|
+
...nextSteps.length ? {
|
|
35727
|
+
nextSteps,
|
|
35728
|
+
guidance: `${output.guidance} Next: ${nextSteps.join(" ")}`
|
|
35729
|
+
} : {}
|
|
35676
35730
|
};
|
|
35677
35731
|
if (flags.json) this.log(JSON.stringify(replaced.length ? {
|
|
35678
35732
|
...suggested,
|
|
@@ -35687,6 +35741,12 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
35687
35741
|
if (poll && "fallbackDetail" in output.receiving) this.log(String(output.receiving.fallbackDetail));
|
|
35688
35742
|
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}`);
|
|
35689
35743
|
if (output.status !== "connected") this.log(`Resume: ${output.resumeCommand}`);
|
|
35744
|
+
if (nextSteps.length) {
|
|
35745
|
+
this.log("Next steps:");
|
|
35746
|
+
nextSteps.forEach((step, index) => {
|
|
35747
|
+
this.log(`${index + 1}. ${step}`);
|
|
35748
|
+
});
|
|
35749
|
+
}
|
|
35690
35750
|
}
|
|
35691
35751
|
if (external && output.externalHook !== "installed_unverified" || !external && !poll && output.receiving.state !== "healthy" || !verificationReplySubmitted(output.verification.state)) process.exitCode = 2;
|
|
35692
35752
|
return;
|
|
@@ -36035,14 +36095,29 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
36035
36095
|
nameIsDefault: generatedAgentName(result.name),
|
|
36036
36096
|
connectedNow: result.connection.status === "connected"
|
|
36037
36097
|
});
|
|
36038
|
-
const
|
|
36098
|
+
const nextSteps = result.connection.status === "connected" ? connectNextSteps({
|
|
36099
|
+
address: result.identity.agentAddress,
|
|
36100
|
+
receiving: {
|
|
36101
|
+
state: output.receiving.state,
|
|
36102
|
+
mode: "mode" in output.receiving ? String(output.receiving.mode) : flags.receiver === "external" ? "external" : "native"
|
|
36103
|
+
},
|
|
36104
|
+
name: result.name,
|
|
36105
|
+
suggestions: identity.suggestions,
|
|
36106
|
+
skillFile: installedConnectSkillFile({ runtime: detectAgentRuntime(flags.session) })
|
|
36107
|
+
}) : [];
|
|
36108
|
+
const guided = nextSteps.length ? {
|
|
36039
36109
|
...output,
|
|
36040
36110
|
...identity,
|
|
36041
|
-
|
|
36111
|
+
nextSteps,
|
|
36112
|
+
guidance: `${output.guidance} Next: ${nextSteps.join(" ")}`
|
|
36042
36113
|
} : {
|
|
36043
36114
|
...output,
|
|
36044
36115
|
...identity
|
|
36045
36116
|
};
|
|
36117
|
+
const printed = replaced.length ? {
|
|
36118
|
+
...guided,
|
|
36119
|
+
replacedExisting: replaced
|
|
36120
|
+
} : guided;
|
|
36046
36121
|
if (flags.json) this.log(JSON.stringify(printed));
|
|
36047
36122
|
else {
|
|
36048
36123
|
this.log(`Agent ${result.identity.agentAddress}: pairing ${result.connection.status}; verification ${result.verification.state}; receiving ${output.receiving.state}.`);
|
|
@@ -36056,6 +36131,12 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
36056
36131
|
if (result.connection.status !== "connected") this.log("Pairing is not yet confirmed. Rerun this command with the same options to resume the saved session; do not create another address.");
|
|
36057
36132
|
else if ("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}`);
|
|
36058
36133
|
else if (result.receiving.state !== "healthy") this.log("Pairing is confirmed; receiving needs separate setup or recovery.");
|
|
36134
|
+
if (nextSteps.length) {
|
|
36135
|
+
this.log("Next steps:");
|
|
36136
|
+
nextSteps.forEach((step, index) => {
|
|
36137
|
+
this.log(`${index + 1}. ${step}`);
|
|
36138
|
+
});
|
|
36139
|
+
}
|
|
36059
36140
|
}
|
|
36060
36141
|
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;
|
|
36061
36142
|
} catch (error) {
|
|
@@ -36336,6 +36417,8 @@ var AgentRenameCommand = class AgentRenameCommand extends Command {
|
|
|
36336
36417
|
address,
|
|
36337
36418
|
name
|
|
36338
36419
|
});
|
|
36420
|
+
const own = auth.connectedAgent;
|
|
36421
|
+
if (own && own.agentAddress.toLowerCase() === connection.address.toLowerCase()) saveConnectionName(this.config.configDir, own.profileName, connection.name);
|
|
36339
36422
|
if (flags.json) this.log(JSON.stringify({ connection }, null, 2));
|
|
36340
36423
|
else this.log(`Renamed ${connection.address} to "${connection.name}". The address is unchanged.`);
|
|
36341
36424
|
} catch (error) {
|
|
@@ -37415,6 +37498,25 @@ function parseReceipt(raw) {
|
|
|
37415
37498
|
if (!value || value.version !== 1 || typeof value.request_hash !== "string" || typeof value.sent_at !== "string" || !Number.isFinite(Date.parse(value.sent_at)) || typeof value.completed !== "boolean" || value.reply !== void 0 && (!value.reply || typeof value.reply !== "object" || typeof value.reply.emailId !== "string" || !value.reply.emailId || typeof value.reply.requestId !== "string" || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value.reply.requestId)) || value.idempotency_key !== void 0 && (typeof value.idempotency_key !== "string" || !/^[!-~]{1,255}$/.test(value.idempotency_key)) || value.send_attempted !== void 0 && typeof value.send_attempted !== "boolean" || value.sent !== null && (!value.sent || typeof value.sent.id !== "string" || !value.sent.id || typeof value.sent.status !== "string")) throw new Error("Invalid chat receipt");
|
|
37416
37499
|
return value;
|
|
37417
37500
|
}
|
|
37501
|
+
/**
|
|
37502
|
+
* True when a receipt exists for `scope` and is not completed. An unreadable
|
|
37503
|
+
* receipt counts as unfinished: beginChatReceipt then reports it instead of
|
|
37504
|
+
* a retry quietly starting a second send beside it.
|
|
37505
|
+
*/
|
|
37506
|
+
function hasUnfinishedChatReceipt(configDir, scope) {
|
|
37507
|
+
const path = join(configDir, "chat-receipts", `${chatRequestHash(scope)}.json`);
|
|
37508
|
+
let raw;
|
|
37509
|
+
try {
|
|
37510
|
+
raw = readFileSync(path, "utf8");
|
|
37511
|
+
} catch (error) {
|
|
37512
|
+
return error.code !== "ENOENT";
|
|
37513
|
+
}
|
|
37514
|
+
try {
|
|
37515
|
+
return !parseReceipt(raw).completed;
|
|
37516
|
+
} catch {
|
|
37517
|
+
return true;
|
|
37518
|
+
}
|
|
37519
|
+
}
|
|
37418
37520
|
/** Caller holds the request/parent lock until completion. Unknown POSTs never retry. */
|
|
37419
37521
|
function beginChatReceipt(configDir, scope, requestHash, options = {}) {
|
|
37420
37522
|
const directory = join(configDir, "chat-receipts");
|
|
@@ -37874,6 +37976,9 @@ async function openConnectedReplyWait(options) {
|
|
|
37874
37976
|
async observed(emailId) {
|
|
37875
37977
|
if (options.contactRequest && await store.observeNotifiedContactReply(emailId, requestId, waiter.token)) return;
|
|
37876
37978
|
await store.markWaitObserved(emailId, requestId);
|
|
37979
|
+
try {
|
|
37980
|
+
await clearConsumedPendingMail(options.configDir, options.sessionKey, emailId);
|
|
37981
|
+
} catch {}
|
|
37877
37982
|
},
|
|
37878
37983
|
finish: () => store.finishWait(requestId),
|
|
37879
37984
|
close() {
|
|
@@ -38484,6 +38589,62 @@ function deriveSubject(body) {
|
|
|
38484
38589
|
}
|
|
38485
38590
|
return "Message";
|
|
38486
38591
|
}
|
|
38592
|
+
const CHAT_SUBJECT_MAX_LENGTH = 60;
|
|
38593
|
+
const TITLES$1 = new Set([
|
|
38594
|
+
"dr",
|
|
38595
|
+
"mr",
|
|
38596
|
+
"mrs",
|
|
38597
|
+
"ms",
|
|
38598
|
+
"prof",
|
|
38599
|
+
"sr",
|
|
38600
|
+
"jr",
|
|
38601
|
+
"st",
|
|
38602
|
+
"vs",
|
|
38603
|
+
"e.g",
|
|
38604
|
+
"i.e",
|
|
38605
|
+
"approx",
|
|
38606
|
+
"fig"
|
|
38607
|
+
]);
|
|
38608
|
+
const ABBREVIATIONS = new Set([
|
|
38609
|
+
"etc",
|
|
38610
|
+
"inc",
|
|
38611
|
+
"ltd",
|
|
38612
|
+
"co",
|
|
38613
|
+
"no"
|
|
38614
|
+
]);
|
|
38615
|
+
/**
|
|
38616
|
+
* The line up to its first sentence end: `.`, `?` or `!` followed by
|
|
38617
|
+
* whitespace or the line end. A period after a title-like abbreviation or
|
|
38618
|
+
* a single letter (an initial) does not end the sentence, nor does one
|
|
38619
|
+
* after another abbreviation when the text continues in lowercase or a
|
|
38620
|
+
* digit.
|
|
38621
|
+
*/
|
|
38622
|
+
function firstSentence(line) {
|
|
38623
|
+
const end = /[.?!](?=\s|$)/g;
|
|
38624
|
+
for (let match = end.exec(line); match; match = end.exec(line)) {
|
|
38625
|
+
if (match[0] === ".") {
|
|
38626
|
+
const bare = (/(\S+)$/.exec(line.slice(0, match.index))?.[1] ?? "").replace(/^[("'[]+/, "").toLowerCase();
|
|
38627
|
+
if (TITLES$1.has(bare) || /^[a-z]$/.test(bare)) continue;
|
|
38628
|
+
const next = /^\s+(\S)/.exec(line.slice(match.index + 1))?.[1] ?? "";
|
|
38629
|
+
if (ABBREVIATIONS.has(bare) && /[a-z0-9]/.test(next)) continue;
|
|
38630
|
+
}
|
|
38631
|
+
return line.slice(0, match.index + 1);
|
|
38632
|
+
}
|
|
38633
|
+
return line;
|
|
38634
|
+
}
|
|
38635
|
+
/**
|
|
38636
|
+
* A short subject for a chat message, whose body is usually one long line
|
|
38637
|
+
* or a short paragraph: the first sentence of the first non-empty line,
|
|
38638
|
+
* cut at a word boundary to at most CHAT_SUBJECT_MAX_LENGTH characters.
|
|
38639
|
+
* The body is sent unchanged; replies thread on headers, not the subject.
|
|
38640
|
+
*/
|
|
38641
|
+
function deriveChatSubject(body) {
|
|
38642
|
+
const sentence = firstSentence(deriveSubject(body));
|
|
38643
|
+
if (sentence.length <= CHAT_SUBJECT_MAX_LENGTH) return sentence;
|
|
38644
|
+
const room = sentence.slice(0, CHAT_SUBJECT_MAX_LENGTH - 3);
|
|
38645
|
+
const space = room.lastIndexOf(" ");
|
|
38646
|
+
return `${(space >= CHAT_SUBJECT_MAX_LENGTH / 2 ? room.slice(0, space) : room).replace(/[\s,;:.!?-]+$/, "")}...`;
|
|
38647
|
+
}
|
|
38487
38648
|
function isVerifiedDomain(domain) {
|
|
38488
38649
|
return domain.is_active === true;
|
|
38489
38650
|
}
|
|
@@ -39594,18 +39755,23 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
39594
39755
|
subject = derivedReplySubject(replyContext.parentReply);
|
|
39595
39756
|
} else {
|
|
39596
39757
|
from = selectedFrom ?? await pickDefaultFromAddress(apiClient, authFailureContext);
|
|
39597
|
-
subject = flags.subject ??
|
|
39758
|
+
subject = flags.subject ?? deriveChatSubject(message);
|
|
39598
39759
|
}
|
|
39599
|
-
const
|
|
39760
|
+
const hashFor = (value) => chatRequestHash({
|
|
39600
39761
|
api: auth.apiBaseUrl,
|
|
39601
39762
|
account: auth.credentials?.org_id ?? chatCredentialIdentity(auth.apiKey, auth.apiBaseUrl),
|
|
39602
39763
|
from,
|
|
39603
39764
|
recipient: args.recipient,
|
|
39604
|
-
subject,
|
|
39765
|
+
subject: value,
|
|
39605
39766
|
message,
|
|
39606
39767
|
parent: parentReply?.id ?? flags["in-reply-to"],
|
|
39607
39768
|
attachments
|
|
39608
39769
|
});
|
|
39770
|
+
if (!parentReply && flags.subject === void 0) {
|
|
39771
|
+
const previous = deriveSubject(message);
|
|
39772
|
+
if (previous !== subject && hasUnfinishedChatReceipt(this.config.configDir, hashFor(previous))) subject = previous;
|
|
39773
|
+
}
|
|
39774
|
+
const requestHash = hashFor(subject);
|
|
39609
39775
|
const scope = parentReply ? chatRequestHash({
|
|
39610
39776
|
api: auth.apiBaseUrl,
|
|
39611
39777
|
from,
|
|
@@ -43421,7 +43587,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
43421
43587
|
url: "https://primitive.dev"
|
|
43422
43588
|
};
|
|
43423
43589
|
const SDK_VERSION_RANGE = "^1.35.0";
|
|
43424
|
-
const CLI_VERSION_RANGE = "^1.
|
|
43590
|
+
const CLI_VERSION_RANGE = "^1.51.0";
|
|
43425
43591
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
43426
43592
|
function renderHandler() {
|
|
43427
43593
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -47750,7 +47916,9 @@ async function createWakeMail(options) {
|
|
|
47750
47916
|
thread_id: notice.context.threadId,
|
|
47751
47917
|
in_thread: notice.context.inThread,
|
|
47752
47918
|
newer: notice.context.newer ?? null,
|
|
47753
|
-
...notice.context.interaction ? { interaction: notice.context.interaction } : {}
|
|
47919
|
+
...notice.context.interaction ? { interaction: notice.context.interaction } : {},
|
|
47920
|
+
relationship: notice.context.relationship,
|
|
47921
|
+
attachments: notice.context.attachments
|
|
47754
47922
|
} : {
|
|
47755
47923
|
kind: "status",
|
|
47756
47924
|
email_id: detail.id,
|
|
@@ -47858,10 +48026,10 @@ async function createWakeMail(options) {
|
|
|
47858
48026
|
}
|
|
47859
48027
|
return outcome(true);
|
|
47860
48028
|
}
|
|
47861
|
-
if (
|
|
48029
|
+
if (requested && detail.reply_to_sent_email_id) {
|
|
47862
48030
|
if (["bound", "completed"].includes(requested.status) && requested.sentEmailId === detail.reply_to_sent_email_id && requested.peer === sender && (requested.sessionKey === null || requested.sessionKey === options.sessionKey) && Date.parse(detail.received_at) >= Date.parse(requested.createdAt)) {
|
|
47863
48031
|
if (requested.contactRequest ? isContactAcceptance(await readContactInteraction(detail, readPart, signal, Date.parse(detail.received_at)), requested.contactRequest) : isPlainChatReply(detail)) {
|
|
47864
|
-
admission = await policy.admitResponse(sender, detail.received_at, signal, detail.id);
|
|
48032
|
+
if (!admission || admission.kind === "request") admission = await policy.admitResponse(sender, detail.received_at, signal, detail.id);
|
|
47865
48033
|
if (admission) {
|
|
47866
48034
|
const disposition = await store.wakeDisposition(detail.id, detail.reply_to_sent_email_id);
|
|
47867
48035
|
if (disposition === "observed") return outcome(true);
|
|
@@ -48495,12 +48663,14 @@ var ListenCommand = class ListenCommand extends Command {
|
|
|
48495
48663
|
process.stderr.write(`Primitive status arrived: ${status.emailId}${recipient} ${status.kind} ${status.peer} ${status.sentEmailId}. This is activity on an exact conversation this session started, not a new task.\n`);
|
|
48496
48664
|
process.exitCode = 2;
|
|
48497
48665
|
} else if (wake?.wakeId()) {
|
|
48498
|
-
|
|
48499
|
-
|
|
48500
|
-
|
|
48501
|
-
|
|
48502
|
-
|
|
48503
|
-
|
|
48666
|
+
process.stderr.write(formatMailWakeLine({
|
|
48667
|
+
emailId: wake.wakeId() ?? "",
|
|
48668
|
+
recipient,
|
|
48669
|
+
relation: wake.senderRelation?.(),
|
|
48670
|
+
context: wake.context?.(),
|
|
48671
|
+
profileName: hookProfileName,
|
|
48672
|
+
skillFile: installedConnectSkillFile({ runtime: "claude" })
|
|
48673
|
+
}));
|
|
48504
48674
|
process.exitCode = 2;
|
|
48505
48675
|
const auto = wake.autoSignal?.();
|
|
48506
48676
|
if (auto) dispatchAutoRead({
|
|
@@ -54580,7 +54750,8 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
54580
54750
|
} else emailId = flags.id;
|
|
54581
54751
|
const priorRepliesCheck = await checkPriorReplies({
|
|
54582
54752
|
client: apiClient.client,
|
|
54583
|
-
emailId
|
|
54753
|
+
emailId,
|
|
54754
|
+
isLocalSignal: (sentId) => isSentSignal(this.config.configDir, sentId)
|
|
54584
54755
|
});
|
|
54585
54756
|
this.priorRepliesCheck = priorRepliesCheck;
|
|
54586
54757
|
const priorRepliesMessage = priorRepliesCheck.status === "checked" ? formatPriorRepliesWarning(priorRepliesCheck.prior) : formatPriorRepliesCheckSkipped(emailId, priorRepliesCheck.reason);
|
|
@@ -54700,7 +54871,10 @@ function priorRepliesEnvelopeFields(check) {
|
|
|
54700
54871
|
};
|
|
54701
54872
|
return check.status === "checked" ? {
|
|
54702
54873
|
prior_replies: check.prior,
|
|
54703
|
-
prior_replies_check: {
|
|
54874
|
+
prior_replies_check: {
|
|
54875
|
+
status: "checked",
|
|
54876
|
+
signals_excluded: check.signals ?? 0
|
|
54877
|
+
}
|
|
54704
54878
|
} : {
|
|
54705
54879
|
prior_replies: null,
|
|
54706
54880
|
prior_replies_check: {
|
|
@@ -55612,7 +55786,10 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55612
55786
|
]
|
|
55613
55787
|
}),
|
|
55614
55788
|
wait: Flags.boolean({ description: "Block until the receiving MTA returns an outcome. Without --wait, the call returns once Primitive has accepted the message for delivery." }),
|
|
55615
|
-
"wait-timeout-ms": Flags.integer({
|
|
55789
|
+
"wait-timeout-ms": Flags.integer({
|
|
55790
|
+
description: `Maximum time to wait for the delivery outcome when --wait is set. Defaults to 30000ms; the server waits at most ${MAX_SEND_WAIT_TIMEOUT_MS}ms, so a longer value is shortened to that with a notice. This waits for delivery, not for a reply: to wait for the recipient's answer, run \`primitive emails wait --reply-to-sent-email-id <sent id> --from <recipient> --timeout <seconds>\` after sending.`,
|
|
55791
|
+
min: 1
|
|
55792
|
+
}),
|
|
55616
55793
|
"idempotency-key": Flags.string({ description: IDEMPOTENCY_KEY_FLAG_DESCRIPTION }),
|
|
55617
55794
|
json: Flags.boolean({ description: "Emit one outcome envelope { outcome, exit_code, outcome_message, sent_email_id, idempotency_key, sent, http_status, error, follow_up_commands } on stdout for every outcome, including failures, with nothing on stderr. Without --json, stdout is the send record as before." }),
|
|
55618
55795
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION }),
|
|
@@ -55626,6 +55803,8 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55626
55803
|
attemptStartedAtIso = null;
|
|
55627
55804
|
idempotencyKey = null;
|
|
55628
55805
|
sendRequestStarted = false;
|
|
55806
|
+
/** Set when --wait-timeout-ms was shortened to the server's limit. */
|
|
55807
|
+
waitNotice = null;
|
|
55629
55808
|
async run() {
|
|
55630
55809
|
const { flags } = await this.parse(SendCommand);
|
|
55631
55810
|
try {
|
|
@@ -55636,7 +55815,8 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55636
55815
|
error,
|
|
55637
55816
|
idempotencyKey: this.idempotencyKey,
|
|
55638
55817
|
noun: "Message",
|
|
55639
|
-
requestStarted: this.sendRequestStarted
|
|
55818
|
+
requestStarted: this.sendRequestStarted,
|
|
55819
|
+
...this.waitNotice ? { extraEnvelopeFields: { wait_notice: this.waitNotice } } : {}
|
|
55640
55820
|
}), null, 2));
|
|
55641
55821
|
if (this.sendRequestStarted) {
|
|
55642
55822
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -55691,10 +55871,14 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55691
55871
|
...flags.bcc !== void 0 ? { bcc: flags.bcc } : {},
|
|
55692
55872
|
subject
|
|
55693
55873
|
};
|
|
55874
|
+
const requestedWait = flags["wait-timeout-ms"];
|
|
55875
|
+
const waitTimeoutMs = requestedWait === void 0 ? void 0 : Math.min(requestedWait, MAX_SEND_WAIT_TIMEOUT_MS);
|
|
55876
|
+
if (flags.wait && requestedWait !== void 0 && requestedWait > 3e4) this.waitNotice = sendWaitClampNotice(requestedWait);
|
|
55877
|
+
if (this.waitNotice && !flags.json) process.stderr.write(`${this.waitNotice}\n`);
|
|
55694
55878
|
const threading = {
|
|
55695
55879
|
...flags["in-reply-to"] !== void 0 ? { in_reply_to: flags["in-reply-to"] } : {},
|
|
55696
55880
|
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
55697
|
-
...
|
|
55881
|
+
...waitTimeoutMs !== void 0 ? { wait_timeout_ms: waitTimeoutMs } : {},
|
|
55698
55882
|
...repeat !== void 0 ? { repeat } : {}
|
|
55699
55883
|
};
|
|
55700
55884
|
const idempotencyKey = this.idempotencyKey ?? deriveSendIdempotencyKey("send", flags.fyi ? {
|
|
@@ -55741,6 +55925,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
55741
55925
|
});
|
|
55742
55926
|
const outcome = reportSendCommandResult({
|
|
55743
55927
|
attemptStartedAtIso,
|
|
55928
|
+
...this.waitNotice ? { extraEnvelopeFields: { wait_notice: this.waitNotice } } : {},
|
|
55744
55929
|
idempotencyKey,
|
|
55745
55930
|
json: flags.json,
|
|
55746
55931
|
log: (line) => this.log(line),
|
|
@@ -1,6 +1,6 @@
|
|
|
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 {
|
|
3
|
+
import { _ as detectAgentRuntime, l as SESSION_UUID, t as notificationScope } from "../notify-session-ejjYXO3O.js";
|
|
4
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";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Rn as listSentEmails, T as bareAddress, Tt as successfulSendOutcome, Zt as createEndpoint, dt as classifySendError, hn as getEmail, nr as sendEmail, o as extractErrorPayload } from "./api-command-Bd21hvqY.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 { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply } from "./scoped-chat-
|
|
3
|
+
import { a as scopedChatSenderTrust, i as readBeforeDeadline, n as isPlainChatReply } from "./scoped-chat-BJVYBRim.js";
|
|
4
4
|
import { h as sendPreparedSignal, p as prepareSignalEmail } from "./repeats-D2xtLium-B8Wq41vG.js";
|
|
5
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";
|
|
@@ -639,7 +639,9 @@ The sender sees your latest Read, ACK or Working as `peer_signal_on_my_last` on
|
|
|
639
639
|
its own message. A signal is never completion. Published SDK
|
|
640
640
|
interaction helpers remain available when an existing adapter owns signaling.
|
|
641
641
|
[Communication helpers](references/communication.md) describes their durable
|
|
642
|
-
outbox contract
|
|
642
|
+
outbox contract; those helpers are only for agents that claimed over HTTP with
|
|
643
|
+
the helper's own state. A CLI-connected agent uses `primitive reply`, `send`,
|
|
644
|
+
`chat` and `signal`. Do not read or copy a CLI credential into another helper.
|
|
643
645
|
|
|
644
646
|
Detailed validation is optional unless the owner requested it or a failure needs
|
|
645
647
|
diagnosis. When testing, distinguish an ordinary exchange, active-session delivery,
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Threaded conversations and activity
|
|
2
2
|
|
|
3
|
+
**Connected with `primitive agent connect` or `agent enroll`?** Use the CLI:
|
|
4
|
+
`primitive reply`, `primitive send`, `primitive chat` and `primitive signal`
|
|
5
|
+
with your profile, as the mail loop in `SKILL.md` shows. The `scripts/mail.mjs`
|
|
6
|
+
helpers below read only the helper's own connection state (from
|
|
7
|
+
`scripts/connection.mjs`, used by the HTTP claim path); a CLI-connected agent has
|
|
8
|
+
none, and they fail with "No saved connection".
|
|
9
|
+
|
|
3
10
|
The stable contract is email. Use the runtime's existing receiver, credential
|
|
4
11
|
store, authenticated-sender policy, job queue, and durable outbox. The examples
|
|
5
12
|
below are optional conveniences, not a required agent framework or hosting model.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primitive-connect",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "df942e0eaa3cee52",
|
|
4
4
|
"source": {
|
|
5
5
|
"repository": "https://github.com/primitivedotdev/skills",
|
|
6
6
|
"path": "skills/primitive-connect",
|
|
7
|
-
"commit": "
|
|
7
|
+
"commit": "b3c0ba77e2788174c84c5ed8543407d133d74188"
|
|
8
8
|
},
|
|
9
9
|
"files": {
|
|
10
|
-
"SKILL.md": "
|
|
10
|
+
"SKILL.md": "ccfd07ff6f6e019c4f7fcf72b50d0e20f12ce5147b991e00df8d4ecceaf89ca6",
|
|
11
11
|
"package-lock.json": "443289373a7495883a9a3b57bab3bc471cd5dcb4989a9e67f797b35917c1acc5",
|
|
12
12
|
"package.json": "8cc45b2c28ba415960c9b85f6c1449f15f9c10bac0b96922e7907b40d50e3dbf",
|
|
13
|
-
"references/communication.md": "
|
|
13
|
+
"references/communication.md": "4a604cb2db35fc00b31e59661474c310d0d610e37f4a86329fffb45d62a8bf57",
|
|
14
14
|
"references/contact-requests.md": "7923bc855a56f11b088c73c73b7d9c5b04fef3f45eb758f28040d44e46a0d9c6",
|
|
15
15
|
"references/manual-setup.md": "5be9348251604dce1d371abfdd3332d2b56f88783461299b80868a9b716a073b",
|
|
16
16
|
"references/native-session.md": "da1bb8c3d79392a46c4d3469b250c8d041150cabcc2ae6182cdcbf577151a2b6",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primcli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.51.0",
|
|
4
4
|
"description": "Official Primitive CLI: deploy Primitive Functions, send and inspect mail, manage endpoints, all from the terminal. Wraps the @primitivedotdev/sdk runtime client with one-shot commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
File without changes
|