primcli 1.38.1 → 1.40.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 +19 -0
- package/dist/{auto-signals-UPcJmV3j.js → auto-signals-C8nMME8J.js} +1 -1
- package/dist/{connect-skill-HJNQC3A9.js → connect-skill-DjKt8NC8.js} +2 -2
- package/dist/oclif/auto-signal-worker.js +3 -3
- package/dist/oclif/auto-signals.js +1 -1
- package/dist/oclif/index.js +267 -67
- package/dist/oclif/receiver-heal.js +1 -1
- package/dist/{repeats-CQmyvUGQ-CMnOmWgL.js → repeats-CoK0tpCK-Bx7nmyRi.js} +2 -2
- package/dist/{sdk.gen-CugwPoCf.js → sdk.gen-CgKZwHs3.js} +1 -1
- package/dist/{signal-command-CKaEqXro.js → signal-command-C8Ph0ylh.js} +4 -4
- package/dist/skills/primitive-connect/SKILL.md +54 -7
- package/dist/skills/primitive-connect/scripts/check-cli-capabilities.mjs +8 -3
- package/dist/skills/primitive-connect.json +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -862,6 +862,25 @@ choices when those options are omitted, and refuses only an option that
|
|
|
862
862
|
conflicts with them, naming it. Select the saved profile for later commands with the result's
|
|
863
863
|
`selectProfile`.
|
|
864
864
|
|
|
865
|
+
A runtime with no local session ID or hooks, such as a cloud-hosted session
|
|
866
|
+
whose commands run in a separate sandbox, connects with the same command and no
|
|
867
|
+
`--session` (an empty `--session` is treated the same way). It claims and
|
|
868
|
+
verifies the invitation normally and selects `--receiver poll`: nothing is
|
|
869
|
+
installed, the profile defaults to `connection-<invitation hash prefix>`, and
|
|
870
|
+
the result's `receiving.checkCommand` checks for new mail. The agent runs it at
|
|
871
|
+
the start of each turn and after it sends:
|
|
872
|
+
|
|
873
|
+
```sh
|
|
874
|
+
npx -y primitive@latest agent connect --name Research --info "Reviews pull requests" --json < private-invitation.txt
|
|
875
|
+
PRIMITIVE_AGENT_PROFILE=connection-0123456789ab primitive agent check-mail --json
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
`agent check-mail` prints the email IDs, senders and threads that arrived since
|
|
879
|
+
the profile's previous check (never subjects or bodies), leaving out fyi
|
|
880
|
+
acknowledgements, muted threads, and the setup and presence mail from the
|
|
881
|
+
connection's control address. Its position advances only after the result is
|
|
882
|
+
printed, so an interrupted check repeats mail rather than losing it.
|
|
883
|
+
|
|
865
884
|
Verification submission and delivery are separate from receiver health. A queued
|
|
866
885
|
verification reply is accepted for delivery. Do not claim again or resend because
|
|
867
886
|
setup was interrupted. The CLI preserves its private recovery state.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as isTrustedSender, c as classifySignalContent } from "./repeats-
|
|
1
|
+
import { _ as isTrustedSender, c as classifySignalContent } from "./repeats-CoK0tpCK-Bx7nmyRi.js";
|
|
2
2
|
import { d as readMailJson, h as writeMailJson, i as mailId, l as privateMailDirectory, r as mailAddress } from "./shared-mail-files-CZqI1kj6.js";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as isTrustedSender, c as classifySignalContent, g as isEmailReceivedEvent, v as parseWebhookEvent } from "./repeats-
|
|
2
|
-
import { g as downloadEmailAttachmentPart } from "./sdk.gen-
|
|
1
|
+
import { _ as isTrustedSender, c as classifySignalContent, g as isEmailReceivedEvent, v as parseWebhookEvent } from "./repeats-CoK0tpCK-Bx7nmyRi.js";
|
|
2
|
+
import { g as downloadEmailAttachmentPart } from "./sdk.gen-CgKZwHs3.js";
|
|
3
3
|
import { _ as acquireListenLock, g as ListenStateError, y as listenIdentity } from "./shared-mail-files-CZqI1kj6.js";
|
|
4
4
|
import { closeSync, copyFileSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, opendirSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { createHash, randomUUID, scryptSync } from "node:crypto";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { j as getSentEmail } from "../sdk.gen-
|
|
1
|
+
import { j as getSentEmail } from "../sdk.gen-CgKZwHs3.js";
|
|
2
2
|
import { _ as acquireListenLock } from "../shared-mail-files-CZqI1kj6.js";
|
|
3
|
-
import { Mt as createAuthenticatedCliApiClient, t as sendSignal, z as readThreadContext } from "../signal-command-
|
|
4
|
-
import { A as stopWorkingLease, b as readWorkingLease, d as autoSignalsDisabled, j as writeWorkingLease, p as clearWorkingSending, r as AUTO_SIGNAL_KIND_ENV, s as AUTO_WORKING_RENEW_MS, v as markWorkingSending, x as recordRenewer, y as readAutoClaim } from "../auto-signals-
|
|
3
|
+
import { Mt as createAuthenticatedCliApiClient, t as sendSignal, z as readThreadContext } from "../signal-command-C8Ph0ylh.js";
|
|
4
|
+
import { A as stopWorkingLease, b as readWorkingLease, d as autoSignalsDisabled, j as writeWorkingLease, p as clearWorkingSending, r as AUTO_SIGNAL_KIND_ENV, s as AUTO_WORKING_RENEW_MS, v as markWorkingSending, x as recordRenewer, y as readAutoClaim } from "../auto-signals-C8nMME8J.js";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
//#region src/oclif/auto-signal-worker.ts
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as stopWorkingLease, C as releaseStopHold, D as resumeAutoWorking, E as restoreWorkingLease, O as spawnAutoSignalWorker, S as recordSentSignal, T as restoreAutoWorking, _ as isSentSignal, a as AUTO_WORKING_CAP_MS, b as readWorkingLease, c as activeWorkingLeases, d as autoSignalsDisabled, f as claimAutoRead, g as haltAutoWorking, h as dispatchAutoWorking, i as AUTO_SIGNAL_WORKER_ENV, j as writeWorkingLease, k as startWorkingLease, l as autoSignalEligible, m as dispatchAutoRead, n as AUTO_SIGNAL_EMAIL_ENV, o as AUTO_WORKING_EXPIRES_SECONDS, p as clearWorkingSending, r as AUTO_SIGNAL_KIND_ENV, s as AUTO_WORKING_RENEW_MS, t as AUTO_SIGNALS_OPT_OUT_ENV, u as autoSignalEntrypoint, v as markWorkingSending, w as renewerAlive, x as recordRenewer, y as readAutoClaim } from "../auto-signals-
|
|
1
|
+
import { A as stopWorkingLease, C as releaseStopHold, D as resumeAutoWorking, E as restoreWorkingLease, O as spawnAutoSignalWorker, S as recordSentSignal, T as restoreAutoWorking, _ as isSentSignal, a as AUTO_WORKING_CAP_MS, b as readWorkingLease, c as activeWorkingLeases, d as autoSignalsDisabled, f as claimAutoRead, g as haltAutoWorking, h as dispatchAutoWorking, i as AUTO_SIGNAL_WORKER_ENV, j as writeWorkingLease, k as startWorkingLease, l as autoSignalEligible, m as dispatchAutoRead, n as AUTO_SIGNAL_EMAIL_ENV, o as AUTO_WORKING_EXPIRES_SECONDS, p as clearWorkingSending, r as AUTO_SIGNAL_KIND_ENV, s as AUTO_WORKING_RENEW_MS, t as AUTO_SIGNALS_OPT_OUT_ENV, u as autoSignalEntrypoint, v as markWorkingSending, w as renewerAlive, x as recordRenewer, y as readAutoClaim } from "../auto-signals-C8nMME8J.js";
|
|
2
2
|
export { AUTO_SIGNALS_OPT_OUT_ENV, AUTO_SIGNAL_EMAIL_ENV, AUTO_SIGNAL_KIND_ENV, AUTO_SIGNAL_WORKER_ENV, AUTO_WORKING_CAP_MS, AUTO_WORKING_EXPIRES_SECONDS, AUTO_WORKING_RENEW_MS, activeWorkingLeases, autoSignalEligible, autoSignalEntrypoint, autoSignalsDisabled, claimAutoRead, clearWorkingSending, dispatchAutoRead, dispatchAutoWorking, haltAutoWorking, isSentSignal, markWorkingSending, readAutoClaim, readWorkingLease, recordRenewer, recordSentSignal, releaseStopHold, renewerAlive, restoreAutoWorking, restoreWorkingLease, resumeAutoWorking, spawnAutoSignalWorker, startWorkingLease, stopWorkingLease, writeWorkingLease };
|
package/dist/oclif/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { a as REPEAT_IDLE_MAX_MINUTES, d as parseRepeatTick, f as preparePresenceAliveEmail, g as isEmailReceivedEvent, i as REPEAT_EVERY_MAX_MINUTES, l as parseInteractionEnvelope$1, m as repeatStopCommand, o as REPEAT_MAX_SENDS_MAX, p as prepareSignalEmail, s as buildRepeatStopBody, u as parsePresenceEnvelope, v as parseWebhookEvent, y as parseFromHeader } from "../repeats-
|
|
1
|
+
import { a as REPEAT_IDLE_MAX_MINUTES, d as parseRepeatTick, f as preparePresenceAliveEmail, g as isEmailReceivedEvent, i as REPEAT_EVERY_MAX_MINUTES, l as parseInteractionEnvelope$1, m as repeatStopCommand, o as REPEAT_MAX_SENDS_MAX, p as prepareSignalEmail, s as buildRepeatStopBody, u as parsePresenceEnvelope, v as parseWebhookEvent, y as parseFromHeader } from "../repeats-CoK0tpCK-Bx7nmyRi.js";
|
|
2
2
|
import { a as agentProfilesDirectory, c as loadConnectedAgentProfile, d as requireDefaultLoginProfile, f as saveConnectedAgentProfile, i as agentProfileName, l as parseConnectedAgentProfile, n as AgentConnectionSetupError, o as connectedAgentIdentity, r as agentProfileDirectory, s as connectedApiBaseUrl, t as AGENT_PROFILE_ENV, u as parseOwnerMemberAddress } from "../connected-agent-profile-8tpOMdq3.js";
|
|
3
|
-
import { $ as putAgentContactPolicy, A as getRepeatingSend, B as listEmails, C as getEmail, Ct as updateFunction, D as getInboxStatus, Dt as updateWakeSchedule, E as getFunctionTestRunTrace, Et as updateWakeAuthorization, F as listAgentNetworks, G as listRoutes, H as listFunctionLogs, I as listContacts, J as listWakeDispatches, K as listSentEmails, L as listDefaultNetworkAgents, M as getThread, N as getWakeSchedule, O as getMemory, Ot as verifyAgentClaim, P as listAgentContacts, Q as putAgentContact, R as listDefaultNetworkMembers, S as getDefaultNetworkAgent, St as updateDefaultNetworkMember, T as getFunctionRouting, Tt as updateRoute, U as listFunctions, V as listEndpoints, W as listRepeatingSends, X as pollCliLogin, Y as listWakeSchedules, Z as pullWebhookEvent, _ as getAccount, _t as startAgentSignup, a as createEndpoint, at as resendAgentSignupVerification, b as getConversation, bt as testFunction, c as createWakeAuthorization, ct as searchEmails, d as deleteContact, dt as sendEmail, et as putContact, f as deleteMemory, ft as setFunctionRoute, gt as startAgentClaim, h as deleteWakeSchedule, ht as simulateRoute, i as completeWebhookEvent, it as replyToEmail, j as getSentEmail, k as getOrgRoutingTopology, kt as verifyAgentSignup, l as createWakeSchedule, lt as searchMemories, m as deleteWakeAuthorization, mt as setMemory, n as checkDefaultNetworkContactAdmission, nt as removeDefaultNetworkMember, o as createFunction, ot as runWakeSchedule, p as deleteRoute, pt as setFunctionSecret, q as listWakeAuthorizations, r as cliLogout, rt as reorderRoutes, s as createRoute, t as addDefaultNetworkMember, tt as redeemCreditCode, u as deleteAgentContact, ut as semanticSearch, v as getAgentContactPolicy, vt as startCliLogin, w as getFunction, wt as updateRepeatingSend, x as getCreditBalance, xt as unsetFunctionRoute, y as getContact, yt as stopRepeatFromEmail, z as listDomains } from "../sdk.gen-
|
|
3
|
+
import { $ as putAgentContactPolicy, A as getRepeatingSend, B as listEmails, C as getEmail, Ct as updateFunction, D as getInboxStatus, Dt as updateWakeSchedule, E as getFunctionTestRunTrace, Et as updateWakeAuthorization, F as listAgentNetworks, G as listRoutes, H as listFunctionLogs, I as listContacts, J as listWakeDispatches, K as listSentEmails, L as listDefaultNetworkAgents, M as getThread, N as getWakeSchedule, O as getMemory, Ot as verifyAgentClaim, P as listAgentContacts, Q as putAgentContact, R as listDefaultNetworkMembers, S as getDefaultNetworkAgent, St as updateDefaultNetworkMember, T as getFunctionRouting, Tt as updateRoute, U as listFunctions, V as listEndpoints, W as listRepeatingSends, X as pollCliLogin, Y as listWakeSchedules, Z as pullWebhookEvent, _ as getAccount, _t as startAgentSignup, a as createEndpoint, at as resendAgentSignupVerification, b as getConversation, bt as testFunction, c as createWakeAuthorization, ct as searchEmails, d as deleteContact, dt as sendEmail, et as putContact, f as deleteMemory, ft as setFunctionRoute, gt as startAgentClaim, h as deleteWakeSchedule, ht as simulateRoute, i as completeWebhookEvent, it as replyToEmail, j as getSentEmail, k as getOrgRoutingTopology, kt as verifyAgentSignup, l as createWakeSchedule, lt as searchMemories, m as deleteWakeAuthorization, mt as setMemory, n as checkDefaultNetworkContactAdmission, nt as removeDefaultNetworkMember, o as createFunction, ot as runWakeSchedule, p as deleteRoute, pt as setFunctionSecret, q as listWakeAuthorizations, r as cliLogout, rt as reorderRoutes, s as createRoute, t as addDefaultNetworkMember, tt as redeemCreditCode, u as deleteAgentContact, ut as semanticSearch, v as getAgentContactPolicy, vt as startCliLogin, w as getFunction, wt as updateRepeatingSend, x as getCreditBalance, xt as unsetFunctionRoute, y as getContact, yt as stopRepeatFromEmail, z as listDomains } from "../sdk.gen-CgKZwHs3.js";
|
|
4
4
|
import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, O as loadActiveChatState, T as saveSignupCredentials, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as PrimitiveApiClient, 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-UOlrfcPz.js";
|
|
5
5
|
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, p as syncMailDirectory, r as mailAddress, s as mailString, u as privateMailPermissions, x as normalizeListenOrigin, y as listenIdentity } from "../shared-mail-files-CZqI1kj6.js";
|
|
6
|
-
import { $ as extractErrorCode, A as reserveSharedMailSubscription, At as queryUsesAutomated, B as sentInThread, C as thrownErrorExitCode, Ct as AutomatedFilterUnsupportedError, D as ensureSharedMailSubscription, Dt as expectedAutomatedVerdicts, E as sharedMailScope, Et as ensureSearchReportsAutomated, F as describeWake, Ft as resolveCliApiRequestConfig, G as EventConnection, H as serverRelationship, I as displayAddress, J as pushFile, K as EventReceiverError, L as formatWakeContext, M as readConversationFollow, Mt as createAuthenticatedCliApiClient, N as otherParticipants, Nt as createCliApiClient, O as createSharedMailWaiter, Ot as hasAutomatedVerdict, P as bareAddress, Pt as refreshStoredCliCredentials, Q as createOperationCommand, R as latestOwnOutbound, S as successfulSendOutcome, St as AUTOMATED_VALUES, T as openSharedMailReceiver, Tt as automatedRejectedError, U as wakeRelationship, V as serverMuted, W as readSharedMailOwner, X as API_ERROR_CODES, Y as API_BASE_URL_FLAG_DESCRIPTION, Z as TIME_FLAG_DESCRIPTION, _ as reportSendCommandResult, _t as queryUsesAwaiting, a as assertValidIdempotencyKey, at as writeErrorWithHints, b as sentHistoryWindowStart, bt as AUTOMATED_FILTER_UNSUPPORTED_CODE, c as checkPriorReplies, ct as REPLY_STATE_UNSUPPORTED_CODE, d as formatDeletedEarlierSendNotice, dt as awaitingRejectedError, et as extractErrorPayload, f as formatPriorRepliesCheckSkipped, ft as ensureSearchReportsReplyState, g as priorRepliesThatWentOut, gt as isAwaitingRejectedError, h as formatSendRecordFailureSummary, ht as hasReplyState, i as apiResultHttpStatus, it as surfaceUnauthorizedHint, j as followEmailConversation, k as openSharedMailStore, kt as isAutomatedRejectedError, l as classifySendError, lt as ReplyStateUnsupportedError, m as formatSendFailureSummary, mt as formatRepliesCell, n as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, nt as readTextFileFlag, o as buildFollowUpCommand, ot as AWAITING_FLAG_DESCRIPTION, p as formatPriorRepliesWarning, pt as formatAwaitingCell, q as pullFile, r as SEND_OUTCOME_HELP, rt as runWithTiming, s as buildThrownSendFailureEnvelope, st as AWAITING_VALUES, t as sendSignal, tt as formatErrorPayload, u as deriveSendIdempotencyKey, ut as assertAwaitingFilterRows, v as sendIdentityFields, vt as formatAlreadySentNotice, w as reconcileChatSend, wt as assertAutomatedVerdict, x as serializeErrorPayload, xt as AUTOMATED_FLAG_DESCRIPTION, y as sendOutcomeExitCode, yt as writeIdempotentReplayBannerIfReplay, z as readThreadContext } from "../signal-command-
|
|
7
|
-
import { _ as NativeSessionDisconnectedError, a as readBundledConnectSkill, b as NotificationOutcomeUnknownError, c as openSessionNotifications, d as isRoutineNotificationContent, f as notificationPartReader, g as defaultSessionSocket, h as connectNativeSession, i as installedVersion, l as notificationReceiptPage, m as SESSION_UUID$1, n as detectAgentRuntime, 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-
|
|
6
|
+
import { $ as extractErrorCode, A as reserveSharedMailSubscription, At as queryUsesAutomated, B as sentInThread, C as thrownErrorExitCode, Ct as AutomatedFilterUnsupportedError, D as ensureSharedMailSubscription, Dt as expectedAutomatedVerdicts, E as sharedMailScope, Et as ensureSearchReportsAutomated, F as describeWake, Ft as resolveCliApiRequestConfig, G as EventConnection, H as serverRelationship, I as displayAddress, J as pushFile, K as EventReceiverError, L as formatWakeContext, M as readConversationFollow, Mt as createAuthenticatedCliApiClient, N as otherParticipants, Nt as createCliApiClient, O as createSharedMailWaiter, Ot as hasAutomatedVerdict, P as bareAddress, Pt as refreshStoredCliCredentials, Q as createOperationCommand, R as latestOwnOutbound, S as successfulSendOutcome, St as AUTOMATED_VALUES, T as openSharedMailReceiver, Tt as automatedRejectedError, U as wakeRelationship, V as serverMuted, W as readSharedMailOwner, X as API_ERROR_CODES, Y as API_BASE_URL_FLAG_DESCRIPTION, Z as TIME_FLAG_DESCRIPTION, _ as reportSendCommandResult, _t as queryUsesAwaiting, a as assertValidIdempotencyKey, at as writeErrorWithHints, b as sentHistoryWindowStart, bt as AUTOMATED_FILTER_UNSUPPORTED_CODE, c as checkPriorReplies, ct as REPLY_STATE_UNSUPPORTED_CODE, d as formatDeletedEarlierSendNotice, dt as awaitingRejectedError, et as extractErrorPayload, f as formatPriorRepliesCheckSkipped, ft as ensureSearchReportsReplyState, g as priorRepliesThatWentOut, gt as isAwaitingRejectedError, h as formatSendRecordFailureSummary, ht as hasReplyState, i as apiResultHttpStatus, it as surfaceUnauthorizedHint, j as followEmailConversation, k as openSharedMailStore, kt as isAutomatedRejectedError, l as classifySendError, lt as ReplyStateUnsupportedError, m as formatSendFailureSummary, mt as formatRepliesCell, n as IDEMPOTENCY_KEY_FLAG_DESCRIPTION, nt as readTextFileFlag, o as buildFollowUpCommand, ot as AWAITING_FLAG_DESCRIPTION, p as formatPriorRepliesWarning, pt as formatAwaitingCell, q as pullFile, r as SEND_OUTCOME_HELP, rt as runWithTiming, s as buildThrownSendFailureEnvelope, st as AWAITING_VALUES, t as sendSignal, tt as formatErrorPayload, u as deriveSendIdempotencyKey, ut as assertAwaitingFilterRows, v as sendIdentityFields, vt as formatAlreadySentNotice, w as reconcileChatSend, wt as assertAutomatedVerdict, x as serializeErrorPayload, xt as AUTOMATED_FLAG_DESCRIPTION, y as sendOutcomeExitCode, yt as writeIdempotentReplayBannerIfReplay, z as readThreadContext } from "../signal-command-C8Ph0ylh.js";
|
|
7
|
+
import { _ as NativeSessionDisconnectedError, a as readBundledConnectSkill, b as NotificationOutcomeUnknownError, c as openSessionNotifications, d as isRoutineNotificationContent, f as notificationPartReader, g as defaultSessionSocket, h as connectNativeSession, i as installedVersion, l as notificationReceiptPage, m as SESSION_UUID$1, n as detectAgentRuntime, 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-DjKt8NC8.js";
|
|
8
8
|
import { c as startBackgroundListen, i as backgroundListenToken, l as stopBackgroundListen, o as runBackgroundListen, r as backgroundListenStatus, u as verifyBackgroundListenTarget } from "../listen-background-Bsofqq_6.js";
|
|
9
|
-
import { F as readBeforeDeadline, I as scopedChatSenderTrust, L as presenceDisposition, M as isConnectedChatCredential, N as isPlainChatReply, P as isScopedChatReply, R as readPresenceProjection, T as restoreAutoWorking, g as haltAutoWorking, h as dispatchAutoWorking, l as autoSignalEligible, m as dispatchAutoRead } from "../auto-signals-
|
|
9
|
+
import { F as readBeforeDeadline, I as scopedChatSenderTrust, L as presenceDisposition, M as isConnectedChatCredential, N as isPlainChatReply, P as isScopedChatReply, R as readPresenceProjection, T as restoreAutoWorking, g as haltAutoWorking, h as dispatchAutoWorking, l as autoSignalEligible, m as dispatchAutoRead } from "../auto-signals-C8nMME8J.js";
|
|
10
10
|
import { t as guardJsonOutputCommands } from "../json-output-BVRgl6jf.js";
|
|
11
11
|
import { Args, Command, Errors, Flags, ux } from "@oclif/core";
|
|
12
12
|
import { accessSync, chmodSync, closeSync, constants, copyFileSync, cpSync, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
@@ -13274,7 +13274,7 @@ const openapiDocument = {
|
|
|
13274
13274
|
"properties": {
|
|
13275
13275
|
"every_minutes": {
|
|
13276
13276
|
"type": "integer",
|
|
13277
|
-
"minimum":
|
|
13277
|
+
"minimum": 1,
|
|
13278
13278
|
"maximum": 10080
|
|
13279
13279
|
},
|
|
13280
13280
|
"only_if_recipient_idle_minutes": {
|
|
@@ -13412,7 +13412,7 @@ const openapiDocument = {
|
|
|
13412
13412
|
},
|
|
13413
13413
|
"every_minutes": {
|
|
13414
13414
|
"type": "integer",
|
|
13415
|
-
"minimum":
|
|
13415
|
+
"minimum": 1,
|
|
13416
13416
|
"maximum": 10080
|
|
13417
13417
|
},
|
|
13418
13418
|
"only_if_recipient_idle_minutes": {
|
|
@@ -29828,7 +29828,7 @@ const operationManifest = [
|
|
|
29828
29828
|
},
|
|
29829
29829
|
"every_minutes": {
|
|
29830
29830
|
"type": "integer",
|
|
29831
|
-
"minimum":
|
|
29831
|
+
"minimum": 1,
|
|
29832
29832
|
"maximum": 10080
|
|
29833
29833
|
},
|
|
29834
29834
|
"only_if_recipient_idle_minutes": {
|
|
@@ -32386,7 +32386,7 @@ const operationManifest = [
|
|
|
32386
32386
|
"properties": {
|
|
32387
32387
|
"every_minutes": {
|
|
32388
32388
|
"type": "integer",
|
|
32389
|
-
"minimum":
|
|
32389
|
+
"minimum": 1,
|
|
32390
32390
|
"maximum": 10080
|
|
32391
32391
|
},
|
|
32392
32392
|
"only_if_recipient_idle_minutes": {
|
|
@@ -32959,7 +32959,7 @@ const operationManifest = [
|
|
|
32959
32959
|
"properties": {
|
|
32960
32960
|
"every_minutes": {
|
|
32961
32961
|
"type": "integer",
|
|
32962
|
-
"minimum":
|
|
32962
|
+
"minimum": 1,
|
|
32963
32963
|
"maximum": 10080
|
|
32964
32964
|
},
|
|
32965
32965
|
"only_if_recipient_idle_minutes": {
|
|
@@ -34983,6 +34983,139 @@ function isMemoryJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
|
34983
34983
|
}
|
|
34984
34984
|
}
|
|
34985
34985
|
//#endregion
|
|
34986
|
+
//#region src/oclif/agent-mail-check.ts
|
|
34987
|
+
/**
|
|
34988
|
+
* One check for mail that arrived since this profile's previous check. It is
|
|
34989
|
+
* the receiver for runtimes that nothing can wake (poll receiving): the agent
|
|
34990
|
+
* runs it at the start of each turn and after it sends.
|
|
34991
|
+
*
|
|
34992
|
+
* It tails `GET /emails?since=<cursor>`, which returns mail newer than the
|
|
34993
|
+
* cursor oldest first, without mail marked fyi (acknowledgements) or in
|
|
34994
|
+
* threads this address muted. The first check starts before the address's
|
|
34995
|
+
* first email. The cursor is saved privately per profile, only after the
|
|
34996
|
+
* result was handed to `emit`, so a failed or interrupted check repeats mail
|
|
34997
|
+
* rather than losing it. Reads also show this address's receiver as live.
|
|
34998
|
+
*/
|
|
34999
|
+
const CURSOR_FILE = "mail-check.json";
|
|
35000
|
+
var MailCheckApiError = class extends Error {
|
|
35001
|
+
constructor(payload) {
|
|
35002
|
+
super("The mail check request failed.");
|
|
35003
|
+
this.payload = payload;
|
|
35004
|
+
}
|
|
35005
|
+
};
|
|
35006
|
+
function readCursor(path) {
|
|
35007
|
+
try {
|
|
35008
|
+
const saved = readMailJson(path);
|
|
35009
|
+
if (saved && typeof saved === "object" && !Array.isArray(saved) && saved.version === 1) {
|
|
35010
|
+
const cursor = saved.cursor;
|
|
35011
|
+
if (typeof cursor === "string" && cursor.length > 0) return cursor;
|
|
35012
|
+
}
|
|
35013
|
+
} catch {}
|
|
35014
|
+
return null;
|
|
35015
|
+
}
|
|
35016
|
+
/** Setup challenges and presence probes come from the control address. */
|
|
35017
|
+
function isControlMail(email, identity) {
|
|
35018
|
+
return email.presence_control !== void 0 || email.sender.trim().toLowerCase() === identity.ownerAddress.trim().toLowerCase();
|
|
35019
|
+
}
|
|
35020
|
+
async function checkAgentMail(options) {
|
|
35021
|
+
const { identity } = options;
|
|
35022
|
+
const directory = agentProfileDirectory(options.configDir, identity.profileName);
|
|
35023
|
+
privateMailDirectory(directory, true);
|
|
35024
|
+
const release = acquireListenLock(directory, "mail-check");
|
|
35025
|
+
try {
|
|
35026
|
+
const path = join(directory, CURSOR_FILE);
|
|
35027
|
+
const saved = readCursor(path);
|
|
35028
|
+
let cursor = saved;
|
|
35029
|
+
const emails = [];
|
|
35030
|
+
let controlSkipped = 0;
|
|
35031
|
+
let more = false;
|
|
35032
|
+
const maxPages = options.maxPages ?? 5;
|
|
35033
|
+
for (let page = 0; page < maxPages; page++) {
|
|
35034
|
+
const query = {
|
|
35035
|
+
since: cursor ?? "start",
|
|
35036
|
+
limit: 100,
|
|
35037
|
+
exclude_fyi: "true",
|
|
35038
|
+
exclude_muted: "true"
|
|
35039
|
+
};
|
|
35040
|
+
const result = await listEmails({
|
|
35041
|
+
client: options.client.client,
|
|
35042
|
+
query,
|
|
35043
|
+
responseStyle: "fields"
|
|
35044
|
+
});
|
|
35045
|
+
if (result.error || !result.data) throw new MailCheckApiError(result.error);
|
|
35046
|
+
const rows = result.data.data ?? [];
|
|
35047
|
+
for (const row of rows) if (isControlMail(row, identity)) controlSkipped++;
|
|
35048
|
+
else emails.push({
|
|
35049
|
+
id: row.id,
|
|
35050
|
+
received_at: row.received_at,
|
|
35051
|
+
sender: row.sender,
|
|
35052
|
+
thread_id: row.thread_id ?? null
|
|
35053
|
+
});
|
|
35054
|
+
const next = result.data.meta?.cursor ?? null;
|
|
35055
|
+
if (rows.length === 0 || !next) break;
|
|
35056
|
+
cursor = next;
|
|
35057
|
+
more = page === maxPages - 1;
|
|
35058
|
+
}
|
|
35059
|
+
const output = {
|
|
35060
|
+
outcome: emails.length > 0 ? "mail" : "empty",
|
|
35061
|
+
emails,
|
|
35062
|
+
more,
|
|
35063
|
+
control_skipped: controlSkipped,
|
|
35064
|
+
read_command: `PRIMITIVE_AGENT_PROFILE=${identity.profileName} ${options.invocation ?? "primitive"} emails get --id <id> --brief`
|
|
35065
|
+
};
|
|
35066
|
+
options.emit(output);
|
|
35067
|
+
if (cursor && cursor !== saved) writeMailJson(path, {
|
|
35068
|
+
version: 1,
|
|
35069
|
+
cursor
|
|
35070
|
+
});
|
|
35071
|
+
return output;
|
|
35072
|
+
} finally {
|
|
35073
|
+
release();
|
|
35074
|
+
}
|
|
35075
|
+
}
|
|
35076
|
+
//#endregion
|
|
35077
|
+
//#region src/oclif/commands/agent-check-mail.ts
|
|
35078
|
+
/** npx runs the CLI from its cache; print follow-up commands the same way. */
|
|
35079
|
+
function invocation$2(entry) {
|
|
35080
|
+
return entry && /[\\/]_npx[\\/]/.test(entry) ? "npx -y primitive@latest" : "primitive";
|
|
35081
|
+
}
|
|
35082
|
+
var AgentCheckMailCommand = class AgentCheckMailCommand extends Command {
|
|
35083
|
+
static summary = "Check a connected agent's mail since its last check";
|
|
35084
|
+
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 \`primitive emails get --id <id> --brief\`.
|
|
35085
|
+
|
|
35086
|
+
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.
|
|
35087
|
+
|
|
35088
|
+
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; never subject or body), \`more\`, \`control_skipped\` and \`read_command\`.`;
|
|
35089
|
+
static examples = [`${AGENT_PROFILE_ENV}=connection-0123456789ab <%= config.bin %> agent check-mail --json`];
|
|
35090
|
+
static flags = { json: Flags.boolean({ description: "Print JSON (already the default)" }) };
|
|
35091
|
+
async run() {
|
|
35092
|
+
await this.parse(AgentCheckMailCommand);
|
|
35093
|
+
if (!process.env["PRIMITIVE_AGENT_PROFILE"]?.trim()) this.error(`Select a connected agent profile with ${AGENT_PROFILE_ENV}=<profile>. The profile is printed by agent connect.`, { exit: 1 });
|
|
35094
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({ configDir: this.config.configDir });
|
|
35095
|
+
if (!auth.connectedAgent) this.error(`Select a connected agent profile with ${AGENT_PROFILE_ENV}=<profile>.`, { exit: 1 });
|
|
35096
|
+
try {
|
|
35097
|
+
await checkAgentMail({
|
|
35098
|
+
configDir: this.config.configDir,
|
|
35099
|
+
identity: auth.connectedAgent,
|
|
35100
|
+
client: apiClient,
|
|
35101
|
+
invocation: invocation$2(process.argv[1]),
|
|
35102
|
+
emit: (result) => this.log(JSON.stringify(result))
|
|
35103
|
+
});
|
|
35104
|
+
} catch (error) {
|
|
35105
|
+
if (!(error instanceof MailCheckApiError)) throw error;
|
|
35106
|
+
const payload = extractErrorPayload(error.payload);
|
|
35107
|
+
writeErrorWithHints(payload);
|
|
35108
|
+
surfaceUnauthorizedHint({
|
|
35109
|
+
auth,
|
|
35110
|
+
baseUrlOverridden,
|
|
35111
|
+
configDir: this.config.configDir,
|
|
35112
|
+
payload
|
|
35113
|
+
});
|
|
35114
|
+
process.exitCode = 1;
|
|
35115
|
+
}
|
|
35116
|
+
}
|
|
35117
|
+
};
|
|
35118
|
+
//#endregion
|
|
34986
35119
|
//#region src/oclif/claude-wake-install.ts
|
|
34987
35120
|
const HOOK_MARKER = "primitive-agent-wake-v1";
|
|
34988
35121
|
const WAKE_EVENTS = ["Stop", "SessionStart"];
|
|
@@ -35242,8 +35375,15 @@ function agentConnectionStatus(configDir, profileName) {
|
|
|
35242
35375
|
const setup = profile ? readMailJson(join(agentProfileDirectory(configDir, profileName), "setup.json")) : null;
|
|
35243
35376
|
const saved = setup && typeof setup === "object" && !Array.isArray(setup) ? setup : null;
|
|
35244
35377
|
const session = saved && typeof saved.session === "string" && SESSION_UUID$1.test(saved.session) ? saved.session : null;
|
|
35245
|
-
const mode = saved
|
|
35246
|
-
const receiving = profile &&
|
|
35378
|
+
const mode = saved?.receiverMode === "external" || saved?.receiverMode === "poll" ? saved.receiverMode : "native";
|
|
35379
|
+
const receiving = profile && saved && mode === "poll" ? {
|
|
35380
|
+
mode,
|
|
35381
|
+
sessionId: session,
|
|
35382
|
+
state: "poll",
|
|
35383
|
+
reason: "agent_checks_mail",
|
|
35384
|
+
lastSuccessfulMailCheckAt: null,
|
|
35385
|
+
liveness: "unknown"
|
|
35386
|
+
} : profile && session && mode === "native" ? (() => {
|
|
35247
35387
|
const scope = notificationScope(profile.api_base_url, profile.api_key);
|
|
35248
35388
|
const listener = backgroundListenStatus({
|
|
35249
35389
|
configDir,
|
|
@@ -36225,6 +36365,11 @@ function createNotificationContactPolicy(options) {
|
|
|
36225
36365
|
const SUBJECT = "Connect your agent to Primitive";
|
|
36226
36366
|
const MARKER = /\bprimitive-connection:([a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}):([1-9]\d*)\b/g;
|
|
36227
36367
|
const fail = (message) => new AgentConnectionSetupError(message);
|
|
36368
|
+
const RECEIVER_MODES = [
|
|
36369
|
+
"native",
|
|
36370
|
+
"external",
|
|
36371
|
+
"poll"
|
|
36372
|
+
];
|
|
36228
36373
|
const RECEIPT_STATUSES = new Set([
|
|
36229
36374
|
"queued",
|
|
36230
36375
|
"submitted_to_agent",
|
|
@@ -36333,7 +36478,7 @@ function parseState(value) {
|
|
|
36333
36478
|
"receipt"
|
|
36334
36479
|
];
|
|
36335
36480
|
const row = mailObject(value, value && typeof value === "object" && Object.hasOwn(value, "receiverMode") ? [...keys, "receiverMode"] : keys);
|
|
36336
|
-
if (row.version !== 1 || row.receiverMode !== void 0 && !
|
|
36481
|
+
if (row.version !== 1 || row.receiverMode !== void 0 && !RECEIVER_MODES.includes(row.receiverMode) || typeof row.contactRequests !== "boolean" || ![
|
|
36337
36482
|
"waiting",
|
|
36338
36483
|
"sending",
|
|
36339
36484
|
"sent"
|
|
@@ -36348,7 +36493,7 @@ function parseState(value) {
|
|
|
36348
36493
|
if (!/^[a-f0-9]{64}$/.test(invitationHash)) throw fail("Saved setup identity is invalid.");
|
|
36349
36494
|
const state = {
|
|
36350
36495
|
version: 1,
|
|
36351
|
-
session: mailId(row.session),
|
|
36496
|
+
session: row.session === null && row.receiverMode === "poll" ? null : mailId(row.session),
|
|
36352
36497
|
receiverMode: row.receiverMode ?? "native",
|
|
36353
36498
|
invitationHash,
|
|
36354
36499
|
since: mailTime(row.since),
|
|
@@ -36569,7 +36714,7 @@ function defaults$1() {
|
|
|
36569
36714
|
*/
|
|
36570
36715
|
function refuseSetupConflicts(state, params) {
|
|
36571
36716
|
const conflicts = [
|
|
36572
|
-
...state.session !== params.session ? [`--session (this profile is bound to session ${state.session})`] : [],
|
|
36717
|
+
...(params.session === void 0 ? state.receiverMode !== "poll" : state.session !== params.session) ? [state.session ? `--session (this profile is bound to session ${state.session})` : "--session (this profile's setup binds no session)"] : [],
|
|
36573
36718
|
...params.receiverMode !== void 0 && state.receiverMode !== params.receiverMode ? [`--receiver ${params.receiverMode} (saved: ${state.receiverMode})`] : [],
|
|
36574
36719
|
...params.contactRequests !== void 0 && state.contactRequests !== params.contactRequests ? [state.contactRequests ? "contact requests off (saved setup has --contact-requests)" : "--contact-requests (saved setup did not enable it)"] : []
|
|
36575
36720
|
];
|
|
@@ -36578,7 +36723,7 @@ function refuseSetupConflicts(state, params) {
|
|
|
36578
36723
|
/** One resumable operation owns setup plumbing; it never stores or replays the invitation. */
|
|
36579
36724
|
async function setupAgent(params) {
|
|
36580
36725
|
const profileName = agentProfileName(params.profileName);
|
|
36581
|
-
if (!SESSION_UUID$1.test(params.session)) throw fail("--session requires the exact loaded session UUID.");
|
|
36726
|
+
if (params.session !== void 0 && !SESSION_UUID$1.test(params.session)) throw fail("--session requires the exact loaded session UUID.");
|
|
36582
36727
|
if (params.resume ? params.invitation !== void 0 : params.invitation === void 0) throw fail("Use an invitation on stdin for setup, or --resume without an invitation.");
|
|
36583
36728
|
const timeoutMs = params.timeoutMs ?? 3e4;
|
|
36584
36729
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > 12e4) throw fail("Setup timeout must be between zero and two minutes.");
|
|
@@ -36595,7 +36740,9 @@ async function setupAgent(params) {
|
|
|
36595
36740
|
saved = value === null ? null : parseState(value);
|
|
36596
36741
|
} catch {}
|
|
36597
36742
|
if (saved) refuseSetupConflicts(saved, params);
|
|
36598
|
-
|
|
36743
|
+
const receiverMode = params.receiverMode ?? (params.resume ? saved?.receiverMode : void 0);
|
|
36744
|
+
if (params.session === void 0 && (receiverMode ?? "native") !== "poll") throw fail("--session requires the exact loaded session UUID. Without one, use --receiver poll. No invitation was claimed.");
|
|
36745
|
+
if ((receiverMode ?? "native") === "native" && params.session) try {
|
|
36599
36746
|
await dependencies.preflight(params.session);
|
|
36600
36747
|
} catch {
|
|
36601
36748
|
throw fail("This exact session is not available for native receiving. No invitation was claimed. Open a supported session and retry setup.");
|
|
@@ -36616,7 +36763,7 @@ async function setupAgent(params) {
|
|
|
36616
36763
|
if (!state) {
|
|
36617
36764
|
state = {
|
|
36618
36765
|
version: 1,
|
|
36619
|
-
session: params.session,
|
|
36766
|
+
session: params.session ?? null,
|
|
36620
36767
|
receiverMode: params.receiverMode ?? "native",
|
|
36621
36768
|
invitationHash: hash,
|
|
36622
36769
|
since: (/* @__PURE__ */ new Date(dependencies.now() - 15 * 6e4)).toISOString(),
|
|
@@ -36666,7 +36813,7 @@ async function setupAgent(params) {
|
|
|
36666
36813
|
].includes(state.receipt?.status ?? "") ? "reply_failed" : ["unknown", "wait_timeout"].includes(state.receipt?.status ?? "") ? "delivery_unknown" : "reply_submitted" : state?.phase === "sending" ? "send_unknown" : "challenge_pending";
|
|
36667
36814
|
const result = (receiving, ownerNotifications) => ({
|
|
36668
36815
|
identity: context.identity,
|
|
36669
|
-
sessionId: params.session,
|
|
36816
|
+
sessionId: params.session ?? state?.session ?? null,
|
|
36670
36817
|
verification: {
|
|
36671
36818
|
state: confirmed && submittedState() === "reply_submitted" ? "verified" : submittedState(),
|
|
36672
36819
|
...confirmed ? { verifiedAt: confirmed.verifiedAt } : {},
|
|
@@ -36677,7 +36824,7 @@ async function setupAgent(params) {
|
|
|
36677
36824
|
},
|
|
36678
36825
|
receiving: { state: receiving },
|
|
36679
36826
|
...ownerNotifications ? { ownerNotifications } : {},
|
|
36680
|
-
resumeCommand: `primitive agent connect --profile ${profileName} --session ${
|
|
36827
|
+
resumeCommand: `primitive agent connect --profile ${profileName}${state?.session ? ` --session ${state.session}` : ""}${state?.receiverMode === "external" || state?.receiverMode === "poll" ? ` --receiver ${state.receiverMode}` : ""} --resume${state?.contactRequests ? " --contact-requests" : ""} --json`,
|
|
36681
36828
|
guidance: confirmed ? "Primitive verified this connection. Receiving health is reported separately. Keep this profile for all mail commands." : submittedState() === "reply_submitted" ? "The verification reply was submitted and Primitive has not confirmed it yet. Verification usually completes within seconds; check it later with GET /agent-connections/me. Receiving health is reported separately. Keep this profile for all mail commands." : "Reply submission is not proof of delivery or app verification. Receiving health is reported separately. Keep this profile for all mail commands."
|
|
36682
36829
|
});
|
|
36683
36830
|
const key = `connection-verification-${state.invitationHash}`;
|
|
@@ -36711,14 +36858,14 @@ async function setupAgent(params) {
|
|
|
36711
36858
|
}
|
|
36712
36859
|
const ownerNotifications = await dependencies.enableOwner(context);
|
|
36713
36860
|
let healthy = false;
|
|
36714
|
-
if (state.receiverMode
|
|
36715
|
-
healthy = await dependencies.startListener(profileName,
|
|
36861
|
+
if ((state.receiverMode ?? "native") === "native" && state.session) try {
|
|
36862
|
+
healthy = await dependencies.startListener(profileName, state.session, state.contactRequests, params.configDir);
|
|
36716
36863
|
} catch {}
|
|
36717
36864
|
if (verificationTimeoutMs > 0 && submittedState() === "reply_submitted") {
|
|
36718
36865
|
const check = await awaitVerification(context, dependencies, verificationTimeoutMs);
|
|
36719
36866
|
if (check.state === "verified") confirmed = { verifiedAt: check.verifiedAt };
|
|
36720
36867
|
}
|
|
36721
|
-
return result(state.receiverMode === "external" ? "external_setup_required" : healthy ? "healthy" : "not_ready", ownerNotifications);
|
|
36868
|
+
return result(state.receiverMode === "external" ? "external_setup_required" : state.receiverMode === "poll" ? "poll" : healthy ? "healthy" : "not_ready", ownerNotifications);
|
|
36722
36869
|
} finally {
|
|
36723
36870
|
release();
|
|
36724
36871
|
}
|
|
@@ -36814,7 +36961,8 @@ const AGENT_CONNECT_CAPABILITIES = [
|
|
|
36814
36961
|
"claude_external_hooks",
|
|
36815
36962
|
"bundled_skill_install",
|
|
36816
36963
|
"agent_info_seed",
|
|
36817
|
-
"resume"
|
|
36964
|
+
"resume",
|
|
36965
|
+
"poll_receiver"
|
|
36818
36966
|
];
|
|
36819
36967
|
const AGENT_INFO_NOTE = "AGENT_INFO";
|
|
36820
36968
|
const MAX_NAME_LENGTH = 80;
|
|
@@ -36837,7 +36985,7 @@ function savedSetup(configDir, profileName) {
|
|
|
36837
36985
|
if (!saved || typeof saved !== "object" || Array.isArray(saved)) return null;
|
|
36838
36986
|
const row = saved;
|
|
36839
36987
|
return {
|
|
36840
|
-
receiverMode: row.receiverMode === "external"
|
|
36988
|
+
receiverMode: row.receiverMode === "external" || row.receiverMode === "poll" ? row.receiverMode : "native",
|
|
36841
36989
|
contactRequests: row.contactRequests === true
|
|
36842
36990
|
};
|
|
36843
36991
|
} catch {
|
|
@@ -36861,6 +37009,20 @@ function clearPendingAgentInfo(path) {
|
|
|
36861
37009
|
function defaultAgentProfileName(session) {
|
|
36862
37010
|
return `session-${session.toLowerCase()}`;
|
|
36863
37011
|
}
|
|
37012
|
+
/**
|
|
37013
|
+
* The profile for a setup that binds no session. It is derived from the
|
|
37014
|
+
* invitation, so running the same invitation again finds the same profile and
|
|
37015
|
+
* its saved claim instead of claiming twice. A one-way hash prefix reveals
|
|
37016
|
+
* nothing usable about the invitation.
|
|
37017
|
+
*/
|
|
37018
|
+
function invitationProfileName(invitation) {
|
|
37019
|
+
const parsed = parseAgentInvitation(invitation);
|
|
37020
|
+
return `connection-${createHash("sha256").update(parsed.apiBaseUrl).update("\0").update(parsed.token).digest("hex").slice(0, 12)}`;
|
|
37021
|
+
}
|
|
37022
|
+
/** The command that checks a poll-receiving profile for new mail. */
|
|
37023
|
+
function pollCheckCommand(invocation, profileName) {
|
|
37024
|
+
return `PRIMITIVE_AGENT_PROFILE=${profileName} ${invocation} agent check-mail --json`;
|
|
37025
|
+
}
|
|
36864
37026
|
/** A short private role note; never a transcript or secret. */
|
|
36865
37027
|
function agentInfoValue(name, info) {
|
|
36866
37028
|
const clean = (value, max, label) => {
|
|
@@ -36948,11 +37110,15 @@ async function runAgentConnect(options) {
|
|
|
36948
37110
|
...defaults(options),
|
|
36949
37111
|
...Object.fromEntries(Object.entries(options.dependencies ?? {}).filter(([, value]) => value !== void 0))
|
|
36950
37112
|
};
|
|
36951
|
-
|
|
36952
|
-
|
|
36953
|
-
|
|
36954
|
-
|
|
36955
|
-
|
|
37113
|
+
const session = options.session?.trim() ? options.session : void 0;
|
|
37114
|
+
if (session !== void 0 && !SESSION_UUID$1.test(session)) throw new AgentConnectionSetupError("--session requires the exact loaded session UUID.");
|
|
37115
|
+
if (session === void 0 && options.receiver && options.receiver !== "poll") throw new AgentConnectionSetupError(`--receiver ${options.receiver} requires the exact loaded session UUID. Without one, use --receiver poll. No invitation was claimed.`);
|
|
37116
|
+
if (session === void 0 && options.resume && !options.profileName) throw new AgentConnectionSetupError("Pass --profile to resume a setup that binds no session.");
|
|
37117
|
+
const runtime = session ? detectAgentRuntime(session, env) : null;
|
|
37118
|
+
const invitation = options.resume || session || options.profileName ? void 0 : await options.readInvitation();
|
|
37119
|
+
const profileName = agentProfileName(options.profileName ?? (session ? defaultAgentProfileName(session) : invitationProfileName(invitation ?? "")));
|
|
37120
|
+
const receiver = options.receiver ?? (options.resume ? savedSetup(options.configDir, profileName)?.receiverMode ?? null : null) ?? (!session ? "poll" : runtime === "claude" ? "external" : "native");
|
|
37121
|
+
if (receiver === "external" && env.CLAUDE_CODE_SESSION_ID?.trim().toLowerCase() !== session?.toLowerCase()) throw new AgentConnectionSetupError("External receiving requires this exact Claude session ID. No invitation was claimed.");
|
|
36956
37122
|
const agentInfo = agentInfoValue(options.name, options.info);
|
|
36957
37123
|
const skipped = [];
|
|
36958
37124
|
let skill;
|
|
@@ -36989,11 +37155,11 @@ async function runAgentConnect(options) {
|
|
|
36989
37155
|
const result = await dependencies.setupAgent({
|
|
36990
37156
|
configDir: options.configDir,
|
|
36991
37157
|
profileName,
|
|
36992
|
-
session:
|
|
37158
|
+
...session ? { session } : {},
|
|
36993
37159
|
receiverMode: receiver,
|
|
36994
37160
|
resume: options.resume,
|
|
36995
37161
|
contactRequests: options.contactRequests,
|
|
36996
|
-
...options.resume ? {} : { invitation: await options.readInvitation() }
|
|
37162
|
+
...options.resume ? {} : { invitation: invitation ?? await options.readInvitation() }
|
|
36997
37163
|
});
|
|
36998
37164
|
const verified = verificationReplySubmitted(result.verification.state);
|
|
36999
37165
|
const ownerMemberAddress = await dependencies.refreshOwnerMemberAddress(result.identity.profileName);
|
|
@@ -37007,7 +37173,7 @@ async function runAgentConnect(options) {
|
|
|
37007
37173
|
configDir: options.configDir,
|
|
37008
37174
|
profileName: result.identity.profileName,
|
|
37009
37175
|
agentAddress: result.identity.agentAddress,
|
|
37010
|
-
sessionId:
|
|
37176
|
+
sessionId: session ?? "",
|
|
37011
37177
|
env
|
|
37012
37178
|
});
|
|
37013
37179
|
else skipped.push({
|
|
@@ -37053,22 +37219,27 @@ async function runAgentConnect(options) {
|
|
|
37053
37219
|
reason: "already_present"
|
|
37054
37220
|
});
|
|
37055
37221
|
}
|
|
37222
|
+
const invocation = options.invocation ?? "primitive";
|
|
37056
37223
|
const receiving = receiver === "external" ? {
|
|
37057
37224
|
...result.receiving,
|
|
37058
37225
|
mode: "external",
|
|
37059
37226
|
hook: externalHook
|
|
37227
|
+
} : receiver === "poll" ? {
|
|
37228
|
+
...result.receiving,
|
|
37229
|
+
mode: "poll",
|
|
37230
|
+
checkCommand: pollCheckCommand(invocation, result.identity.profileName)
|
|
37060
37231
|
} : {
|
|
37061
37232
|
...result.receiving,
|
|
37062
37233
|
mode: "native",
|
|
37063
37234
|
mailCheck: mailCheck?.state ?? "not_started",
|
|
37064
37235
|
lastSuccessfulMailCheckAt: mailCheck?.lastSuccessfulMailCheckAt ?? null
|
|
37065
37236
|
};
|
|
37066
|
-
const receiverReady = receiver === "external" ? externalHook === "installed_unverified" : result.receiving.state === "healthy";
|
|
37237
|
+
const receiverReady = receiver === "external" ? externalHook === "installed_unverified" : receiver === "poll" || result.receiving.state === "healthy";
|
|
37067
37238
|
const resumeCommand = [
|
|
37068
|
-
|
|
37239
|
+
invocation,
|
|
37069
37240
|
"agent connect",
|
|
37070
37241
|
`--profile ${result.identity.profileName}`,
|
|
37071
|
-
`--session ${
|
|
37242
|
+
...session ? [`--session ${session}`] : [],
|
|
37072
37243
|
`--receiver ${receiver}`,
|
|
37073
37244
|
"--resume",
|
|
37074
37245
|
...options.contactRequests ?? savedSetup(options.configDir, profileName)?.contactRequests ? ["--contact-requests"] : [],
|
|
@@ -37083,7 +37254,7 @@ async function runAgentConnect(options) {
|
|
|
37083
37254
|
ownerAddress: result.identity.ownerAddress,
|
|
37084
37255
|
ownerMemberAddress,
|
|
37085
37256
|
profile: result.identity.profileName,
|
|
37086
|
-
sessionId:
|
|
37257
|
+
sessionId: session ?? null,
|
|
37087
37258
|
runtime,
|
|
37088
37259
|
cli: {
|
|
37089
37260
|
version: options.cliVersion,
|
|
@@ -37105,15 +37276,16 @@ async function runAgentConnect(options) {
|
|
|
37105
37276
|
//#endregion
|
|
37106
37277
|
//#region src/oclif/commands/agent-connect.ts
|
|
37107
37278
|
/** npx runs the CLI from its cache; resume through npx too so the command works without a global install. */
|
|
37108
|
-
function invocation(entry) {
|
|
37279
|
+
function invocation$1(entry) {
|
|
37109
37280
|
return entry && /[\\/]_npx[\\/]/.test(entry) ? "npx -y primitive@latest" : "primitive";
|
|
37110
37281
|
}
|
|
37111
37282
|
var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
37112
|
-
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. 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
|
|
37283
|
+
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. Select the saved identity with PRIMITIVE_AGENT_PROFILE. Use --profile with --status --json to inspect saved identity and local receiver health offline.";
|
|
37113
37284
|
static summary = "Connect and verify an agent address";
|
|
37114
37285
|
static examples = [
|
|
37115
37286
|
"<%= config.bin %> agent connect --session \"$CODEX_THREAD_ID\" --name Research --info \"Reviews pull requests\" --json < private-invitation.txt",
|
|
37116
37287
|
"<%= config.bin %> agent connect --session \"$CLAUDE_CODE_SESSION_ID\" --contact-requests --json < private-invitation.txt",
|
|
37288
|
+
"<%= config.bin %> agent connect --receiver poll --name Research --json < private-invitation.txt",
|
|
37117
37289
|
"<%= config.bin %> agent connect --profile work --session 11111111-1111-4111-8111-111111111111 --contact-requests --json < private-invitation.txt",
|
|
37118
37290
|
"<%= config.bin %> agent connect --profile work --session 11111111-1111-4111-8111-111111111111 --contact-requests --resume --json",
|
|
37119
37291
|
"<%= config.bin %> agent connect --profile work --session 11111111-1111-4111-8111-111111111111 --receiver external --contact-requests --json < private-invitation.txt",
|
|
@@ -37122,33 +37294,31 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
37122
37294
|
"PRIMITIVE_AGENT_PROFILE=work <%= config.bin %> whoami --json"
|
|
37123
37295
|
];
|
|
37124
37296
|
static flags = {
|
|
37125
|
-
profile: Flags.string({ description: "Local agent profile name; defaults to session-<session> with --session. Existing OAuth login is preserved" }),
|
|
37297
|
+
profile: Flags.string({ description: "Local agent profile name; defaults to session-<session> with --session, or connection-<invitation hash prefix> for poll receiving. Existing OAuth login is preserved" }),
|
|
37126
37298
|
session: Flags.string({
|
|
37127
|
-
description: "Exact current session UUID; verify email and configure receiving after claiming",
|
|
37299
|
+
description: "Exact current session UUID; verify email and configure receiving after claiming. Omit it (or pass it empty) when the runtime exposes none, which selects poll receiving",
|
|
37128
37300
|
exclusive: ["status"]
|
|
37129
37301
|
}),
|
|
37130
37302
|
receiver: Flags.string({
|
|
37131
|
-
description: "Native starts a supported session receiver; external installs the exact Claude session's fail-open Stop hook and resume SessionStart hook
|
|
37132
|
-
options: [
|
|
37133
|
-
|
|
37303
|
+
description: "Native starts a supported session receiver; external installs the exact Claude session's fail-open Stop hook and resume SessionStart hook; poll installs nothing and the agent checks for mail itself with `agent check-mail`. Defaults to external in Claude Code, native elsewhere, and poll without a session",
|
|
37304
|
+
options: [...RECEIVER_MODES],
|
|
37305
|
+
exclusive: ["status"]
|
|
37134
37306
|
}),
|
|
37135
37307
|
resume: Flags.boolean({
|
|
37136
37308
|
description: "Resume this session's saved setup without reading stdin, claiming again, or resending verification",
|
|
37137
|
-
dependsOn: ["session"],
|
|
37138
37309
|
exclusive: ["status"]
|
|
37139
37310
|
}),
|
|
37140
37311
|
"contact-requests": Flags.boolean({
|
|
37141
37312
|
description: "Receive owner-enabled first contact requests in this session; grants no task authority",
|
|
37142
|
-
dependsOn: ["session"],
|
|
37143
37313
|
exclusive: ["status"]
|
|
37144
37314
|
}),
|
|
37145
37315
|
name: Flags.string({
|
|
37146
37316
|
description: "Short agent name for a private AGENT_INFO note, written only when the note is absent",
|
|
37147
|
-
|
|
37317
|
+
exclusive: ["status"]
|
|
37148
37318
|
}),
|
|
37149
37319
|
info: Flags.string({
|
|
37150
37320
|
description: "Short role and capability description for a private AGENT_INFO note, written only when the note is absent",
|
|
37151
|
-
|
|
37321
|
+
exclusive: ["status"]
|
|
37152
37322
|
}),
|
|
37153
37323
|
skill: Flags.boolean({
|
|
37154
37324
|
description: "Install or refresh the primitive-connect skill bundled with this CLI for the detected runtime",
|
|
@@ -37157,13 +37327,14 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
37157
37327
|
}),
|
|
37158
37328
|
project: Flags.boolean({
|
|
37159
37329
|
description: "Install the skill into this project (.claude/skills or .agents/skills) instead of the user skills folder",
|
|
37160
|
-
|
|
37330
|
+
exclusive: ["status"]
|
|
37161
37331
|
}),
|
|
37162
37332
|
status: Flags.boolean({ description: "Read saved identity and local receiver health offline without reading stdin" }),
|
|
37163
37333
|
json: Flags.boolean({ description: "Print setup status and identity metadata, never the credential" })
|
|
37164
37334
|
};
|
|
37165
37335
|
async run() {
|
|
37166
37336
|
const { flags } = await this.parse(AgentConnectCommand);
|
|
37337
|
+
let pollProfile;
|
|
37167
37338
|
try {
|
|
37168
37339
|
if (flags.status) {
|
|
37169
37340
|
if (!flags.profile) throw new AgentConnectionSetupError("Pass --profile with --status.");
|
|
@@ -37177,15 +37348,24 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
37177
37348
|
}
|
|
37178
37349
|
return;
|
|
37179
37350
|
}
|
|
37180
|
-
|
|
37351
|
+
const session = flags.session?.trim() || void 0;
|
|
37352
|
+
if (!(flags.session === void 0 && flags.profile !== void 0 && flags.receiver === void 0 && !flags.resume && !flags["contact-requests"] && flags.name === void 0 && flags.info === void 0)) {
|
|
37353
|
+
if (!session && (flags.receiver === "native" || flags.receiver === "external")) throw new AgentConnectionSetupError(`--receiver ${flags.receiver} requires the exact loaded session UUID. Without one, use --receiver poll. No invitation was claimed.`);
|
|
37354
|
+
let readInvitation = () => readAgentInvitation(process.stdin, process.stdin.isTTY);
|
|
37355
|
+
if (!session && !flags.resume) if (flags.profile) pollProfile = flags.profile;
|
|
37356
|
+
else {
|
|
37357
|
+
const text = await readInvitation();
|
|
37358
|
+
pollProfile = invitationProfileName(text);
|
|
37359
|
+
readInvitation = async () => text;
|
|
37360
|
+
}
|
|
37181
37361
|
const output = await runAgentConnect({
|
|
37182
37362
|
configDir: this.config.configDir,
|
|
37183
37363
|
packageRoot: this.config.root,
|
|
37184
37364
|
cliVersion: this.config.version,
|
|
37185
37365
|
cliPath: process.argv[1] ?? "",
|
|
37186
|
-
invocation: invocation(process.argv[1]),
|
|
37187
|
-
session
|
|
37188
|
-
profileName: flags.profile,
|
|
37366
|
+
invocation: invocation$1(process.argv[1]),
|
|
37367
|
+
session,
|
|
37368
|
+
profileName: flags.profile ?? pollProfile,
|
|
37189
37369
|
receiver: flags.receiver,
|
|
37190
37370
|
resume: flags.resume,
|
|
37191
37371
|
contactRequests: flags["contact-requests"],
|
|
@@ -37193,21 +37373,23 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
37193
37373
|
info: flags.info,
|
|
37194
37374
|
skill: flags.skill,
|
|
37195
37375
|
project: flags.project,
|
|
37196
|
-
readInvitation
|
|
37376
|
+
readInvitation
|
|
37197
37377
|
});
|
|
37198
37378
|
const external = output.receiving.mode === "external";
|
|
37379
|
+
const poll = output.receiving.mode === "poll";
|
|
37199
37380
|
if (flags.json) this.log(JSON.stringify(output));
|
|
37200
37381
|
else {
|
|
37201
37382
|
this.log(`Agent ${output.address}: verification ${output.verification.state}; receiving ${output.receiving.state}; skill ${output.skill.state}.`);
|
|
37202
37383
|
this.log(`Select it with ${output.selectProfile}.`);
|
|
37203
37384
|
if (output.ownerNotifications === "silenced") this.log("Owner notifications remain silenced by the existing preference or policy.");
|
|
37204
37385
|
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.");
|
|
37386
|
+
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}`);
|
|
37205
37387
|
if (output.status !== "connected") this.log(`Resume: ${output.resumeCommand}`);
|
|
37206
37388
|
}
|
|
37207
|
-
if (external && output.externalHook !== "installed_unverified" || !external && output.receiving.state !== "healthy" || !verificationReplySubmitted(output.verification.state)) process.exitCode = 2;
|
|
37389
|
+
if (external && output.externalHook !== "installed_unverified" || !external && !poll && output.receiving.state !== "healthy" || !verificationReplySubmitted(output.verification.state)) process.exitCode = 2;
|
|
37208
37390
|
return;
|
|
37209
37391
|
}
|
|
37210
|
-
if (!flags.profile) throw new AgentConnectionSetupError("Pass --
|
|
37392
|
+
if (!flags.profile) throw new AgentConnectionSetupError("Pass --profile for claim-only.");
|
|
37211
37393
|
const claimed = await connectAgent({
|
|
37212
37394
|
configDir: this.config.configDir,
|
|
37213
37395
|
profileName: flags.profile,
|
|
@@ -37224,7 +37406,7 @@ var AgentConnectCommand = class AgentConnectCommand extends Command {
|
|
|
37224
37406
|
this.log(result.ownerReportGuidance);
|
|
37225
37407
|
}
|
|
37226
37408
|
} catch (error) {
|
|
37227
|
-
throw new Errors.CLIError(error instanceof AgentConnectionSetupError ? error.message : flags.session ? `Setup paused. Preserve the private profile and run ${invocation(process.argv[1])} agent connect --profile ${flags.profile ?? defaultAgentProfileName(flags.session)} --session ${flags.session}${flags.receiver ? ` --receiver ${flags.receiver}` : ""} --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 });
|
|
37409
|
+
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 });
|
|
37228
37410
|
}
|
|
37229
37411
|
}
|
|
37230
37412
|
};
|
|
@@ -37413,6 +37595,7 @@ function boundSession(directory, profile) {
|
|
|
37413
37595
|
if (raw === null) return null;
|
|
37414
37596
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new AgentDisconnectError("Saved session setup is invalid. The connection was not changed.");
|
|
37415
37597
|
const row = raw;
|
|
37598
|
+
if (row.receiverMode === "poll" && row.session === null && row.invitationHash === profile.invitation_hash) return null;
|
|
37416
37599
|
if (typeof row.session !== "string" || !SESSION_UUID$1.test(row.session) || row.invitationHash !== profile.invitation_hash) throw new AgentDisconnectError("Saved session binding does not match this profile. The connection was not changed.");
|
|
37417
37600
|
return row.session.toLowerCase();
|
|
37418
37601
|
}
|
|
@@ -37580,7 +37763,7 @@ function savedEnrollment(value) {
|
|
|
37580
37763
|
"ownerAddress"
|
|
37581
37764
|
];
|
|
37582
37765
|
if (row?.version === 2) keys.push("createRequestId", "continueAttempted");
|
|
37583
|
-
if (!row || Object.keys(row).length !== keys.length || keys.some((key) => !Object.hasOwn(row, key)) || row.version !== 1 && row.version !== 2 || row.version === 2 && (typeof row.createRequestId !== "string" || !SESSION_UUID$1.test(row.createRequestId) || typeof row.continueAttempted !== "boolean") || typeof row.session !== "string" || !SESSION_UUID$1.test(row.session) || row.profile !== `session-${row.session}` || typeof row.name !== "string" || !validName(row.name) || !(typeof row.address === "string" && validAddress(row.address) || row.version === 2 && row.address === null && row.phase === "create_attempted") || typeof row.orgId !== "string" || !row.orgId || typeof row.grantId !== "string" || !row.grantId || typeof row.apiBaseUrl !== "string" || !
|
|
37766
|
+
if (!row || Object.keys(row).length !== keys.length || keys.some((key) => !Object.hasOwn(row, key)) || row.version !== 1 && row.version !== 2 || row.version === 2 && (typeof row.createRequestId !== "string" || !SESSION_UUID$1.test(row.createRequestId) || typeof row.continueAttempted !== "boolean") || typeof row.session !== "string" || !SESSION_UUID$1.test(row.session) || row.profile !== `session-${row.session}` || typeof row.name !== "string" || !validName(row.name) || !(typeof row.address === "string" && validAddress(row.address) || row.version === 2 && row.address === null && row.phase === "create_attempted") || typeof row.orgId !== "string" || !row.orgId || typeof row.grantId !== "string" || !row.grantId || typeof row.apiBaseUrl !== "string" || !RECEIVER_MODES.includes(row.receiverMode) || typeof row.contactRequests !== "boolean" || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !["create_attempted", "setup_attempted"].includes(String(row.phase)) || row.phase === "create_attempted" && (row.invitationHash !== null || row.ownerAddress !== null) || row.phase === "setup_attempted" && (typeof row.address !== "string" || typeof row.invitationHash !== "string" || !/^[a-f0-9]{64}$/.test(row.invitationHash) || typeof row.ownerAddress !== "string" || !row.ownerAddress.includes("@"))) throw refuse("Saved enrollment state is invalid. Preserve its private files.");
|
|
37584
37767
|
connectedApiBaseUrl(row.apiBaseUrl);
|
|
37585
37768
|
if (row.ownerAddress !== null) try {
|
|
37586
37769
|
if (mailAddress(row.ownerAddress) !== row.ownerAddress) throw new Error();
|
|
@@ -37606,6 +37789,7 @@ function resolvedEnrollment(state) {
|
|
|
37606
37789
|
};
|
|
37607
37790
|
}
|
|
37608
37791
|
async function preflight(session, receiver, env) {
|
|
37792
|
+
if (receiver === "poll") return;
|
|
37609
37793
|
if (receiver === "external") {
|
|
37610
37794
|
if (env.CLAUDE_CODE_SESSION_ID !== session) throw refuse("External receiving requires this exact Claude session ID. No address was created.");
|
|
37611
37795
|
return;
|
|
@@ -38036,6 +38220,10 @@ async function enrollAgent(params) {
|
|
|
38036
38220
|
}
|
|
38037
38221
|
//#endregion
|
|
38038
38222
|
//#region src/oclif/commands/agent-enroll.ts
|
|
38223
|
+
/** npx runs the CLI from its cache; print follow-up commands the same way. */
|
|
38224
|
+
function invocation(entry) {
|
|
38225
|
+
return entry && /[\\/]_npx[\\/]/.test(entry) ? "npx -y primitive@latest" : "primitive";
|
|
38226
|
+
}
|
|
38039
38227
|
var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
38040
38228
|
static summary = "Give this coding session an address in your organization";
|
|
38041
38229
|
static description = "On this trusted machine, use the saved member OAuth login to create one address for the exact loaded session, claim its one-use invitation privately, answer the email challenge, and poll the owner's connection list for confirmed pairing. Native receiving starts when supported. With --receiver external in the exact Claude session, install a fail-open Stop hook and resume SessionStart hook in that runtime's settings after the verification reply; idle wake remains unverified until tested with real mail. With --contact-requests, the login conditionally enables first-contact intake for this exact address after verification, preserving existing policy rules. An explicit disable or uncertain policy update is reported separately without losing pairing or receiver setup. The server allocates a readable address. An uncertain creation resumes the same saved request; recovered results do not include another invitation. Use --continue-setup once to explicitly continue a recovered pending connection. An uncertain continuation or claim is held for owner recovery, never repeated automatically. Requires a verified Primitive-managed domain. This command does not accept API keys, a connection profile, or an invitation argument.";
|
|
@@ -38048,8 +38236,8 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
38048
38236
|
name: Flags.string({ description: "Short agent display name; defaults to Coding agent" }),
|
|
38049
38237
|
"continue-setup": Flags.boolean({ description: "Explicitly issue one invitation only if the recovered agent is still pending; never reconnect a claimed agent" }),
|
|
38050
38238
|
receiver: Flags.string({
|
|
38051
|
-
options: [
|
|
38052
|
-
description: "Native session receiver,
|
|
38239
|
+
options: [...RECEIVER_MODES],
|
|
38240
|
+
description: "Native session receiver, external runtime event hook, or poll: nothing is installed and the agent checks for mail itself with `agent check-mail`"
|
|
38053
38241
|
}),
|
|
38054
38242
|
"contact-requests": Flags.boolean({ description: "Enable this agent's first-contact policy with the saved member login, then receive relevant requests" }),
|
|
38055
38243
|
json: Flags.boolean({ description: "Print status without credentials or invitation" })
|
|
@@ -38072,7 +38260,15 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
38072
38260
|
agentAddress: result.identity.agentAddress,
|
|
38073
38261
|
sessionId: flags.session
|
|
38074
38262
|
}) : null;
|
|
38075
|
-
const output = {
|
|
38263
|
+
const output = result.receiving.state === "poll" ? {
|
|
38264
|
+
...result,
|
|
38265
|
+
receiving: {
|
|
38266
|
+
...result.receiving,
|
|
38267
|
+
mode: "poll",
|
|
38268
|
+
checkCommand: pollCheckCommand(invocation(process.argv[1]), result.identity.profileName)
|
|
38269
|
+
},
|
|
38270
|
+
externalHook
|
|
38271
|
+
} : {
|
|
38076
38272
|
...result,
|
|
38077
38273
|
externalHook
|
|
38078
38274
|
};
|
|
@@ -38086,9 +38282,10 @@ var AgentEnrollCommand = class AgentEnrollCommand extends Command {
|
|
|
38086
38282
|
if (externalHook === "installed_unverified") this.log("External receive hook installed for this runtime. Idle wake still needs a live mail check.");
|
|
38087
38283
|
else if (externalHook === "unavailable") this.log("Could not install the external receive hook. Pairing is confirmed, but later mail will not wake this session automatically.");
|
|
38088
38284
|
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.");
|
|
38285
|
+
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}`);
|
|
38089
38286
|
else if (result.receiving.state !== "healthy") this.log("Pairing is confirmed; receiving needs separate setup or recovery.");
|
|
38090
38287
|
}
|
|
38091
|
-
if (externalHook === "unavailable" || flags["contact-requests"] && result.contactRequestPolicy !== "enabled" || result.connection.status !== "connected" || flags.receiver !== "external" && result.receiving.state !== "healthy") process.exitCode = 2;
|
|
38288
|
+
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;
|
|
38092
38289
|
} catch (error) {
|
|
38093
38290
|
throw new Errors.CLIError(error instanceof AgentConnectionSetupError ? error.message : "Enrollment paused. Preserve this session's private profile and inspect the pending agent in the app before retrying. No second address was requested automatically.", { exit: 1 });
|
|
38094
38291
|
}
|
|
@@ -40386,10 +40583,10 @@ function contactRequestSessionKey(context, env = process.env) {
|
|
|
40386
40583
|
}
|
|
40387
40584
|
if (typeof raw !== "object" || Array.isArray(raw)) throw sessionRequired();
|
|
40388
40585
|
const setup = raw;
|
|
40389
|
-
const session = mailId(setup.session);
|
|
40390
40586
|
const receiverMode = setup.receiverMode ?? "native";
|
|
40587
|
+
const poll = receiverMode === "poll" && setup.session === null;
|
|
40391
40588
|
const receipt = setup.receipt;
|
|
40392
|
-
if (setup.version !== 1 || setup.invitationHash !== profile.invitation_hash || setup.phase !== "sent" || !["native", "external"].includes(String(receiverMode)) || !receipt || typeof receipt !== "object" || Array.isArray(receipt) || ![
|
|
40589
|
+
if (setup.version !== 1 || setup.invitationHash !== profile.invitation_hash || setup.phase !== "sent" || !(poll || ["native", "external"].includes(String(receiverMode))) || !receipt || typeof receipt !== "object" || Array.isArray(receipt) || ![
|
|
40393
40590
|
"queued",
|
|
40394
40591
|
"submitted_to_agent",
|
|
40395
40592
|
"delivered",
|
|
@@ -40397,6 +40594,8 @@ function contactRequestSessionKey(context, env = process.env) {
|
|
|
40397
40594
|
"scheduled"
|
|
40398
40595
|
].includes(String(receipt.status))) throw sessionRequired();
|
|
40399
40596
|
mailId(receipt.id);
|
|
40597
|
+
if (poll) return null;
|
|
40598
|
+
const session = mailId(setup.session);
|
|
40400
40599
|
const key = `${receiverMode === "external" ? "claude" : "codex"}:${session}`;
|
|
40401
40600
|
if (hasRuntime && runtime !== key || !hasRuntime && receiverMode !== "external") throw sessionRequired();
|
|
40402
40601
|
return key;
|
|
@@ -45882,7 +46081,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
45882
46081
|
url: "https://primitive.dev"
|
|
45883
46082
|
};
|
|
45884
46083
|
const SDK_VERSION_RANGE = "^1.34.0";
|
|
45885
|
-
const CLI_VERSION_RANGE = "^1.
|
|
46084
|
+
const CLI_VERSION_RANGE = "^1.40.0";
|
|
45886
46085
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
45887
46086
|
function renderHandler() {
|
|
45888
46087
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -56744,8 +56943,8 @@ var PaymentsRegisterPayoutAddressCommand = class PaymentsRegisterPayoutAddressCo
|
|
|
56744
56943
|
function repeatFlags(exclusive, requirement) {
|
|
56745
56944
|
return {
|
|
56746
56945
|
"repeat-every": Flags.integer({
|
|
56747
|
-
description: `Send this message now and then again every N minutes (
|
|
56748
|
-
min:
|
|
56946
|
+
description: `Send this message now and then again every N minutes (1 to ${REPEAT_EVERY_MAX_MINUTES}) in the same thread. ${requirement} Manage it later with \`primitive repeats\`.`,
|
|
56947
|
+
min: 1,
|
|
56749
56948
|
max: REPEAT_EVERY_MAX_MINUTES,
|
|
56750
56949
|
exclusive
|
|
56751
56950
|
}),
|
|
@@ -60422,6 +60621,7 @@ const COMMANDS = {
|
|
|
60422
60621
|
completion: CompletionCommand,
|
|
60423
60622
|
"agent:upgrade": AgentUpgradeCommand,
|
|
60424
60623
|
"agent:connect": AgentConnectCommand,
|
|
60624
|
+
"agent:check-mail": AgentCheckMailCommand,
|
|
60425
60625
|
"agent:disconnect": AgentDisconnectCommand,
|
|
60426
60626
|
"agent:enroll": AgentEnrollCommand,
|
|
60427
60627
|
"agent:session-register": AgentSessionRegisterCommand,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as loadConnectedAgentProfile, i as agentProfileName, r as agentProfileDirectory, t as AGENT_PROFILE_ENV } from "../connected-agent-profile-8tpOMdq3.js";
|
|
2
2
|
import { d as readMailJson } from "../shared-mail-files-CZqI1kj6.js";
|
|
3
|
-
import { m as SESSION_UUID, n as detectAgentRuntime, o as notificationScope } from "../connect-skill-
|
|
3
|
+
import { m as SESSION_UUID, n as detectAgentRuntime, o as notificationScope } from "../connect-skill-DjKt8NC8.js";
|
|
4
4
|
import { a as claimBackgroundListenRestart, n as backgroundListenRestartable, r as backgroundListenStatus } from "../listen-background-Bsofqq_6.js";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
@@ -9676,7 +9676,7 @@ function normalizeOptions(options) {
|
|
|
9676
9676
|
};
|
|
9677
9677
|
}
|
|
9678
9678
|
//#endregion
|
|
9679
|
-
//#region ../sdk-node/dist/repeats-
|
|
9679
|
+
//#region ../sdk-node/dist/repeats-CoK0tpCK.js
|
|
9680
9680
|
const UUID$3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9681
9681
|
const ATOM = "[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+";
|
|
9682
9682
|
const MAILBOX$1 = new RegExp(`^${ATOM}(?:\\.${ATOM})*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$`);
|
|
@@ -10378,7 +10378,7 @@ function readRepeatTick(envelope) {
|
|
|
10378
10378
|
status: "invalid",
|
|
10379
10379
|
reason: "invalid_payload"
|
|
10380
10380
|
};
|
|
10381
|
-
if (!p || typeof p.repeat_id !== "string" || !UUID.test(p.repeat_id) || !integerIn(p.sequence, 1, Number.MAX_SAFE_INTEGER) || !integerIn(p.every_minutes,
|
|
10381
|
+
if (!p || typeof p.repeat_id !== "string" || !UUID.test(p.repeat_id) || !integerIn(p.sequence, 1, Number.MAX_SAFE_INTEGER) || !integerIn(p.every_minutes, 1, 10080) || !(p.only_if_recipient_idle_minutes === null || integerIn(p.only_if_recipient_idle_minutes, 1, 10080)) || typeof p.stoppable_by_recipient !== "boolean") return invalid;
|
|
10382
10382
|
return {
|
|
10383
10383
|
status: "valid",
|
|
10384
10384
|
tick: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as __exportAll } from "./repeats-
|
|
1
|
+
import { b as __exportAll } from "./repeats-CoK0tpCK-Bx7nmyRi.js";
|
|
2
2
|
import { m as createConfig, p as createClient } from "./connected-agent-profile-8tpOMdq3.js";
|
|
3
3
|
//#region ../packages/api-core/src/api/client.gen.ts
|
|
4
4
|
const client = createClient(createConfig({ baseUrl: "https://api.primitive.dev/v1" }));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { h as sendPreparedSignal, p as prepareSignalEmail } from "./repeats-
|
|
1
|
+
import { h as sendPreparedSignal, p as prepareSignalEmail } from "./repeats-CoK0tpCK-Bx7nmyRi.js";
|
|
2
2
|
import { d as requireDefaultLoginProfile } from "./connected-agent-profile-8tpOMdq3.js";
|
|
3
|
-
import { C as getEmail, K as listSentEmails, V as listEndpoints, a as createEndpoint, ct as searchEmails, dt as sendEmail, st as sdk_gen_exports } from "./sdk.gen-
|
|
3
|
+
import { C as getEmail, K as listSentEmails, V as listEndpoints, a as createEndpoint, ct as searchEmails, dt as sendEmail, st as sdk_gen_exports } from "./sdk.gen-CgKZwHs3.js";
|
|
4
4
|
import { C as resolveCliAuth, S as normalizeApiBaseUrl, T as saveSignupCredentials, _ as deleteCliCredentials, c as resolveConfigEnvironment, d as validateCliHeaderName, f as validateCliHeaderValue, i as loadCliConfig, j as PrimitiveApiClient, m as cliAccessTokenExpiresAt, p as acquireCliCredentialsLock, w as saveCliCredentials, x as loadCliCredentials, y as detectPrimitiveKeyEnvMisname } from "./cli-config-UOlrfcPz.js";
|
|
5
5
|
import { _ as acquireListenLock, a as mailLockBusy, 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-CZqI1kj6.js";
|
|
6
|
-
import { F as readBeforeDeadline, I as scopedChatSenderTrust, N as isPlainChatReply, S as recordSentSignal } from "./auto-signals-
|
|
6
|
+
import { F as readBeforeDeadline, I as scopedChatSenderTrust, N as isPlainChatReply, S as recordSentSignal } from "./auto-signals-C8nMME8J.js";
|
|
7
7
|
import { Command, Errors, Flags } from "@oclif/core";
|
|
8
8
|
import { opendirSync, readFileSync, watch, writeFileSync } from "node:fs";
|
|
9
9
|
import { createHash, randomUUID, scryptSync } from "node:crypto";
|
|
@@ -1542,7 +1542,7 @@ async function pullFile(root, outPath, opts) {
|
|
|
1542
1542
|
return manifest;
|
|
1543
1543
|
}
|
|
1544
1544
|
//#endregion
|
|
1545
|
-
//#region ../sdk-node/dist/api-
|
|
1545
|
+
//#region ../sdk-node/dist/api-CV59-Ci2.js
|
|
1546
1546
|
const jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) };
|
|
1547
1547
|
function createSseClient({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) {
|
|
1548
1548
|
let lastEventId;
|
|
@@ -14,9 +14,9 @@ authorizes this connection, private credential storage, the verification reply
|
|
|
14
14
|
and continued receiving; do not ask for that approval again.
|
|
15
15
|
|
|
16
16
|
The command validates the invitation's API origin, claims it once, answers the
|
|
17
|
-
verification email, starts receiving for this exact session
|
|
18
|
-
|
|
19
|
-
result. Give it the owner's setup URL on stdin through a quoted heredoc, never
|
|
17
|
+
verification email, starts receiving for this exact session (or sets up
|
|
18
|
+
checking for mail when nothing can wake this session), installs or refreshes
|
|
19
|
+
this skill for your runtime from the CLI package, and prints one JSON result. Give it the owner's setup URL on stdin through a quoted heredoc, never
|
|
20
20
|
as a command argument, file in a shared directory, log entry or note. Run it in
|
|
21
21
|
your own non-interactive tool shell, which keeps no command history; never
|
|
22
22
|
paste it into an interactive terminal.
|
|
@@ -37,6 +37,24 @@ npx -y primitive@latest agent connect --session "${CODEX_THREAD_ID:-$CODEX_SESSI
|
|
|
37
37
|
INVITATION
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
Without a session to bind, omit `--session`:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npx -y primitive@latest agent connect --json <<'INVITATION'
|
|
44
|
+
<the owner's setup URL>
|
|
45
|
+
INVITATION
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Use this form, without asking the owner, when this session cannot be woken from
|
|
49
|
+
the machine that runs your commands: your runtime's session variable above is
|
|
50
|
+
empty or unset, or the conversation runs somewhere other than that machine (a
|
|
51
|
+
cloud-hosted session whose tools run in a separate sandbox), so a hook or
|
|
52
|
+
listener installed there could never reach you. An instruction that passes an
|
|
53
|
+
empty session variable is treated the same way. The invitation is claimed and
|
|
54
|
+
verified as usual; the CLI installs nothing and you check for new mail
|
|
55
|
+
yourself. That is a complete connection, not a fallback that needs the owner's
|
|
56
|
+
choice. Never guess, borrow or invent a session ID to get automatic wake.
|
|
57
|
+
|
|
40
58
|
Add `--contact-requests` only when the owner asked this address to receive
|
|
41
59
|
first-contact requests from unknown senders. Add `--name "<short name>"` and
|
|
42
60
|
`--info "<role and useful capabilities>"` to seed this address's private
|
|
@@ -52,7 +70,10 @@ Read the one JSON result:
|
|
|
52
70
|
`ownerAddress` is a control address for setup and presence only.
|
|
53
71
|
For Claude Code, `externalHook: "installed_unverified"` means the wake hooks
|
|
54
72
|
are installed but idle wake stays unverified until a real mail event reaches
|
|
55
|
-
this session; say so rather than promising later delivery. For
|
|
73
|
+
this session; say so rather than promising later delivery. For
|
|
74
|
+
`receiving.mode: "poll"`, add one line such as "Incoming mail is checked at
|
|
75
|
+
the start of each turn and after I send; nothing wakes this session in
|
|
76
|
+
between." and follow [Checking for mail](#checking-for-mail). For a native
|
|
56
77
|
receiver, `receiving.mailCheck: "confirmed"` means the listener completed a
|
|
57
78
|
mail check after setup. Prefix later `primitive` commands with
|
|
58
79
|
`selectProfile` (`PRIMITIVE_AGENT_PROFILE=<profile>`).
|
|
@@ -253,7 +274,8 @@ command-help tour, or test conversations. If setup paused, run its
|
|
|
253
274
|
A globally installed `primitive` may be older than the one command expects.
|
|
254
275
|
Before using one for enrollment or claim-only, run the read-only preflight with
|
|
255
276
|
the directory containing this `SKILL.md` as the working directory. Use
|
|
256
|
-
`external` for Claude Code
|
|
277
|
+
`external` for Claude Code, `native` for a supported native runtime, or `poll`
|
|
278
|
+
for a session without a local session ID or hooks:
|
|
257
279
|
|
|
258
280
|
```sh
|
|
259
281
|
primitive --version
|
|
@@ -539,7 +561,8 @@ the eventual ordinary reply. For a near-term answer required in this turn, use
|
|
|
539
561
|
`primitive chat` without `--async` and evaluate the exact reply. If asynchronous
|
|
540
562
|
receiving is unavailable, send once with `primitive send`, retain its sent ID,
|
|
541
563
|
and use an exact-parent reply wait when needed; report that later session
|
|
542
|
-
delivery is unavailable.
|
|
564
|
+
delivery is unavailable. With poll receiving, the reply appears in a later
|
|
565
|
+
mail check; keep the sent ID with the task to match it. Never resend merely because a wait timed out.
|
|
543
566
|
Before relying on a peer to pick up asynchronous work, check its receiver state
|
|
544
567
|
as described under [Receiving presence](#receiving-presence); delivery to its
|
|
545
568
|
inbox does not mean any session will read it.
|
|
@@ -581,6 +604,22 @@ a reply arrived or repeating delivery status leaves the delegated question
|
|
|
581
604
|
unanswered. Keep simultaneous conversations separate; the reply grants no new
|
|
582
605
|
access to private history and does not require a reply to an ACK.
|
|
583
606
|
|
|
607
|
+
### Checking for mail
|
|
608
|
+
|
|
609
|
+
With `receiving.mode: "poll"`, nothing delivers mail to this session. Run the
|
|
610
|
+
result's `receiving.checkCommand` (it is `PRIMITIVE_AGENT_PROFILE=<profile>
|
|
611
|
+
primitive agent check-mail --json`) at the start of every turn and again after
|
|
612
|
+
you send or reply. It prints the IDs, senders and threads of mail that arrived
|
|
613
|
+
since the previous check, never subjects or bodies, and leaves out
|
|
614
|
+
acknowledgements, muted threads and setup mail. Read each listed email with
|
|
615
|
+
`primitive emails get --id <id> --brief` under the selected profile, then
|
|
616
|
+
handle it as described above. When `more` is true, check again after handling
|
|
617
|
+
them. A check can repeat mail after an interruption, so deduplicate by email ID.
|
|
618
|
+
Over HTTP, the inbox tail with `wait=0` is the same check. Do not hold a turn
|
|
619
|
+
open with sleep loops to imitate a wake, and do not ask the owner to switch
|
|
620
|
+
runtimes; mail sent while the session is idle waits for the next turn and is
|
|
621
|
+
not lost. Peers see this address as `live` only while it keeps checking.
|
|
622
|
+
|
|
584
623
|
After setup, report pairing and the current receiving evidence, then end the
|
|
585
624
|
setup turn and return control to the owner. The supported receiver owns later
|
|
586
625
|
mail arrival. Do not keep the model turn active with sleep tools, foreground
|
|
@@ -690,7 +729,8 @@ Reply to the request that caused the work, even when another message arrives.
|
|
|
690
729
|
One agent entry in the app may contain several independent conversations. Recover
|
|
691
730
|
context through explicit reply ancestry and saved task context, not the latest
|
|
692
731
|
message from that person. Missing history is a limitation to explain, not proof
|
|
693
|
-
that the conversation is new. Start a fresh thread for an unrelated topic
|
|
732
|
+
that the conversation is new. Start a fresh thread for an unrelated new topic;
|
|
733
|
+
status updates still go to the home thread described below.
|
|
694
734
|
|
|
695
735
|
An owner may request an email update from the coding chat, outside the email
|
|
696
736
|
thread. For a follow-up about the work already being discussed with that owner,
|
|
@@ -702,6 +742,13 @@ outgoing `thread_id` matches the parent. Do not start a fresh send (`POST
|
|
|
702
742
|
or report progress on that work. If no relevant thread is known, start one to the owner's personal address (`owner_member_address`) and say that it is new;
|
|
703
743
|
do not attach the update to an unrelated email just because it is recent.
|
|
704
744
|
|
|
745
|
+
Keep one home thread with the owner for status updates, plans and decision
|
|
746
|
+
requests: the thread the owner set up for contact, or else your first
|
|
747
|
+
conversation with them. When the owner asks something in a topic-specific
|
|
748
|
+
thread, answer that question there, but post later status and unrelated updates
|
|
749
|
+
back in the home thread. Never start a new thread for an update when an
|
|
750
|
+
appropriate thread already exists.
|
|
751
|
+
|
|
705
752
|
For an authorized ordinary request, honor the requested response format. A request
|
|
706
753
|
to reply with one word should receive that word. Keep setup diagnostics and
|
|
707
754
|
requests for additional tests out of the ordinary answer unless the owner asked
|
|
@@ -4,8 +4,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
4
4
|
const receiver = process.argv.length === 4 && process.argv[2] === '--receiver'
|
|
5
5
|
? process.argv[3]
|
|
6
6
|
: null;
|
|
7
|
-
if (receiver !== 'native' && receiver !== 'external') {
|
|
8
|
-
process.stderr.write('Usage: node scripts/check-cli-capabilities.mjs --receiver native|external\n');
|
|
7
|
+
if (receiver !== 'native' && receiver !== 'external' && receiver !== 'poll') {
|
|
8
|
+
process.stderr.write('Usage: node scripts/check-cli-capabilities.mjs --receiver native|external|poll\n');
|
|
9
9
|
process.exitCode = 2;
|
|
10
10
|
} else {
|
|
11
11
|
const checks = [
|
|
@@ -14,7 +14,12 @@ if (receiver !== 'native' && receiver !== 'external') {
|
|
|
14
14
|
{ args: ['listen', '--help'], label: 'listen', patterns: [/external mail events at tool-output authority/i, /never synthetic user messages/i] },
|
|
15
15
|
{ args: ['network', 'peers', '--help'], label: 'network peers', patterns: [/discover listed peers/i, /--owner\b/] },
|
|
16
16
|
];
|
|
17
|
-
if (receiver === '
|
|
17
|
+
if (receiver === 'poll') {
|
|
18
|
+
// Nothing is installed and no listener runs: the agent checks for mail.
|
|
19
|
+
checks[0].patterns.push(/poll installs nothing/i);
|
|
20
|
+
checks[1].patterns.push(/\bpoll\b/);
|
|
21
|
+
checks[2] = { args: ['agent', 'check-mail', '--help'], label: 'agent check-mail', patterns: [/since its previous check/i] };
|
|
22
|
+
} else if (receiver === 'external') {
|
|
18
23
|
checks[0].patterns.push(/installs the exact Claude session's fail-open Stop hook/i, /resume SessionStart hook/i);
|
|
19
24
|
checks[1].patterns.push(/exact Claude session, install a fail-open Stop hook/i, /resume SessionStart hook/i);
|
|
20
25
|
checks[2].patterns.push(/--wake\b/, /--hook-session\b/);
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primitive-connect",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "a48b2ea39305f258",
|
|
4
4
|
"source": {
|
|
5
5
|
"repository": "https://github.com/primitivedotdev/skills",
|
|
6
6
|
"path": "skills/primitive-connect",
|
|
7
|
-
"commit": "
|
|
7
|
+
"commit": "47e1cb5b6ecef4294c4f4067962c7e6041a625d0"
|
|
8
8
|
},
|
|
9
9
|
"files": {
|
|
10
|
-
"SKILL.md": "
|
|
10
|
+
"SKILL.md": "6631dab8209a823b3790df20856146676deb505a2f7a52de87e953fd75388694",
|
|
11
11
|
"package-lock.json": "443289373a7495883a9a3b57bab3bc471cd5dcb4989a9e67f797b35917c1acc5",
|
|
12
12
|
"package.json": "8cc45b2c28ba415960c9b85f6c1449f15f9c10bac0b96922e7907b40d50e3dbf",
|
|
13
13
|
"references/communication.md": "c931aada9553a8592088c28de5b272d5e0a15234aa9c9318ea7d1d5f69b39a79",
|
|
14
14
|
"references/contact-requests.md": "7923bc855a56f11b088c73c73b7d9c5b04fef3f45eb758f28040d44e46a0d9c6",
|
|
15
15
|
"references/native-session.md": "d6d529d87edc68a450be4c8b162488efa666c59fc23c35576fe6887235ff3d33",
|
|
16
16
|
"references/private-api-fallback.md": "63b5d8c8ab765561fc6031298462469a8f56fd348ac14c5003e214f780e9329b",
|
|
17
|
-
"scripts/check-cli-capabilities.mjs": "
|
|
17
|
+
"scripts/check-cli-capabilities.mjs": "9625ddaba1989a5f52a5077fdd0513620ab81be640bd83dd678708eefe019222",
|
|
18
18
|
"scripts/connection.mjs": "2c96eda1e9a277a241555f98dd858523b8f77a9ddd82d52a6ac34e844206c3d6",
|
|
19
19
|
"scripts/mail.mjs": "bfdfb60a739f1537d0e676dc5ff44fd5a65dba4303e3781dce8754c8d671cf51"
|
|
20
20
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primcli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.40.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,
|