primcli 1.27.0 → 1.30.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 CHANGED
@@ -134,6 +134,33 @@ primitive emails latest --limit 5
134
134
 
135
135
  Run `primitive --help` for the full command list. Per-command help (`primitive functions deploy --help`) carries enough detail that an agent can compose any operation without leaving the terminal.
136
136
 
137
+ ## Waiting with connected credentials
138
+
139
+ After sending, wait for the existing send's reply without sending again:
140
+
141
+ ```bash
142
+ primitive emails wait --reply-to-sent-email-id <sent-id> --from peer@example.com
143
+ ```
144
+
145
+ The CLI reads that sent record to derive your receiving address. Optional `--to`
146
+ must match it. The peer stays explicit because sent records do not expose a
147
+ complete recipient inventory. Connected waits include existing replies by
148
+ default, so fast replies are not missed; use `--since <timestamp>` to narrow the
149
+ window. They require an authenticated peer and the exact outbound parent.
150
+ Interaction attachments remain pending with inspection guidance. JSONL includes
151
+ matching email details, or use `--table` for compact output and `--number N` for
152
+ multiple replies. Timeout exits 1; repeat the same wait to recover without
153
+ resending. Connected waits share one local address event receiver and recover
154
+ through exact-parent search, without scanning inbox history. Additional content
155
+ filters on this command require organization credentials.
156
+
157
+ Connected `primitive chat <peer> <message> --from <own-address>` registers its
158
+ reply wait and authenticates the receiver before sending. It records an explicit
159
+ idempotency key before the request. If the send response is lost, repeating the
160
+ same command looks up that key without sending again. A timeout retains the exact
161
+ parent claim for resume. A plain reply is an email response, not proof that a task
162
+ is complete.
163
+
137
164
  ## Authentication
138
165
 
139
166
  Use `primitive signin` or `primitive login` for existing accounts. With no email, both use browser approval; `primitive signin browser` and `primitive login browser` are the explicit browser forms.
@@ -168,6 +195,35 @@ primitive deliveries replay --id <delivery-id>
168
195
 
169
196
  Generated API commands remain available for compatibility and full schema parity, for example `primitive emails:list-emails` and `primitive sending:reply-to-email`.
170
197
 
198
+ ## Send outcomes and exit codes
199
+
200
+ `primitive chat`, `primitive chat reply`, `primitive send` and `primitive reply`
201
+ report the same outcomes. Exit codes tell you whether a message left and whether
202
+ sending again is safe. With `--json`, stdout is an envelope for every outcome
203
+ (failures included) whose `outcome` field carries the name.
204
+
205
+ | Outcome | Exit | Meaning |
206
+ |---|---|---|
207
+ | `replied` | 0 | Chat only: the message was sent and a reply arrived. |
208
+ | `sent` | 0 | Accepted for delivery. `status: "queued"` is a success, not a pending failure. |
209
+ | `already_sent` | 0 | The server recognised an identical earlier send, or refused with HTTP 410 `sent_email_deleted` because that earlier send was deleted. Nothing new went out. Do not resend. |
210
+ | `not_sent` | 1 | The API rejected the request (HTTP 400, 401, 402, 403, 404, 413, 422 or 429), the command failed before sending, or the send record has status `agent_failed`, `gate_denied` or `canceled`. Nothing went out. |
211
+ | (usage error) | 2 | Invalid flags or arguments. Nothing went out. |
212
+ | `sent_awaiting_reply` | 3 | Chat only: the message was sent but no reply arrived before `--timeout`. Wait with the printed command; do not resend. |
213
+ | `uncertain` | 4 | Transport error, conflict, server error, or a send record with status `unknown`. The message may or may not have gone out; check `primitive sent list` before retrying. |
214
+
215
+ A chat that times out prints `Message sent (id X). No reply yet after Ns. Do NOT
216
+ resend; wait with: <command>`, and its `--json` envelope has `"reply": null`, the
217
+ `sent` record, and `follow_up_commands` that only wait on or inspect that send.
218
+
219
+ Without `--json`, `send` and `reply` keep printing the send record on stdout exactly
220
+ as before and add a one-line stderr summary such as `Reply sent (queued for
221
+ delivery, id X). Do not resend.` Before sending, `primitive reply` (and
222
+ `primitive chat --reply`) warns on stderr when the inbound email already has a reply
223
+ that went out. The warning never blocks the send; if the lookup fails, the reply is
224
+ still sent and stderr says the check was skipped. `--json` includes the replies as
225
+ `prior_replies`.
226
+
171
227
  ## Remove mailbox history
172
228
 
173
229
  `primitive sent delete --id <sent-email-id>` removes sender history and owned
@@ -206,6 +262,24 @@ primitive memories get state --function <function-id>
206
262
  Values must be valid JSON. Strings must be quoted as JSON strings, so use
207
263
  `'"hello"'`, not `hello`.
208
264
 
265
+ ## Credits
266
+
267
+ Redeem a credit code for your organization and check the credit balance.
268
+
269
+ ```bash
270
+ primitive credits redeem LAUNCH50
271
+ primitive credits balance
272
+ primitive credits balance --json
273
+ ```
274
+
275
+ `credits redeem` prints the credit added and its expiry. On a refusal it prints
276
+ the server's message (for example an invalid or already redeemed code) and
277
+ exits non-zero. Redeeming needs an organization owner or admin; with an API key,
278
+ the key's creator must currently be an owner or admin. Each run sends a new
279
+ Idempotency-Key; pass `--idempotency-key <key>` to retry the same redemption
280
+ safely. On any failure the command prints the key it used to stderr, so you can
281
+ run the same command again with `--idempotency-key <key>`.
282
+
209
283
  ## Recipient routing
210
284
 
211
285
  Bind a recipient address to a destination so inbound mail resolves to a single
@@ -317,3 +391,91 @@ timeout; Ctrl-C exits 130. Bare `primitive listen` prints one raw JSON event per
317
391
  line. Use `--transport poll` explicitly for HTTP polling. Accept or enqueue each
318
392
  event within 30 seconds. Closing preserves pending work, and retries can deliver
319
393
  an event more than once.
394
+
395
+ ### Native session email notifications
396
+
397
+ With a connected-agent credential already configured, notify one exact loaded
398
+ Codex session of authenticated mail from explicitly approved senders:
399
+
400
+ ```bash
401
+ primitive listen --notify-session <session-uuid> --sender person@example.com
402
+ primitive listen --notify-session <session-uuid> --sender first@example.com,second@example.com
403
+ primitive listen --status --notify-session <session-uuid>
404
+ primitive listen --status --notify-session <session-uuid> --limit 100 --cursor <nextCursor>
405
+ ```
406
+
407
+ This first notification path uses the native local-session Unix socket in
408
+ Codex 0.156.1. Live runtime behavior was verified on macOS; Linux uses the same
409
+ Unix transport but has not been verified against a live runtime. The session must already be open in a terminal with
410
+ native daemon support enabled. If its socket is unavailable, this is an
411
+ unsupported runtime setup: the CLI reports the gap and exits before creating a
412
+ subscription. It does not launch a daemon, start/resume a conversation, install a
413
+ connector, or change model, approval, or sandbox settings. Windows and other
414
+ harnesses are not supported by this path. `CODEX_HOME` selects the native runtime
415
+ home when it differs from `~/.codex`.
416
+
417
+ Notification mode registers an `email.received`-only subscription and refuses
418
+ mixed existing filters before leasing events. Native notification mode requires
419
+ WebSocket and the shared saved subscription; `--transport poll` and a custom
420
+ `--subscription` are rejected. Generic stdout, exec, and forwarding listeners
421
+ retain their separate transport and subscription options. The `local-mail-*`
422
+ subscription namespace is reserved for shared receiving. An unexpected non-email
423
+ event remains uncompleted. In notification mode, `--once` means one candidate
424
+ processed during this invocation, including a policy or routine-status skip.
425
+ Waiter-owned replies and existing accepted notification receipts do not count.
426
+ It does not promise one session notification.
427
+
428
+ The foreground listener receives only the connected credential's assigned
429
+ address. `--sender` requires exact addresses with authenticated From-domain
430
+ evidence. Domain authentication does not independently prove a person's identity.
431
+ Notifications contain email/event IDs, the approved sender, and an inspection
432
+ command. Email bodies, subjects, attachments, and terminal transcripts are not
433
+ injected into the session. Verified routine ack/read/working/typing interactions
434
+ are suppressed; mixed content and unsupported protocols remain external mail
435
+ notifications. Inbound IDs are saved before the server delivery is acknowledged.
436
+ When parsing or authentication is pending, the listener retries those exact IDs
437
+ locally using current email details; an ingress acknowledgement does not mean
438
+ a native notification was accepted.
439
+
440
+ Local private receipts are scoped to the API environment, connected credential,
441
+ and exact session. Accepted means queued, not read or answered. A lost queue
442
+ response or interrupted submission is held as unknown across restarts because
443
+ the runtime does not deduplicate client message IDs. Inspect these receipts with
444
+ `--status`; it does not connect to a runtime or receive mail. Status returns up
445
+ to 100 receipts by default (maximum `--limit 1000`) and a `nextCursor` for the
446
+ next page. Individual private receipt files and an event index preserve evidence
447
+ without a lifetime aggregate-size cap; interrupted index writes recover locally
448
+ before dispatch. Unknown outcomes require inspection of that exact session before a manual resend. Do not delete
449
+ receipt state to force a retry. Definite failures before dispatch can be retried
450
+ by restarting the listener.
451
+
452
+ The CLI verifies the private socket and loaded session before each dispatch.
453
+ The native queue API cannot atomically fence a terminal closing between that
454
+ check and acceptance, so a concurrent close can leave input queued for that
455
+ same session. The CLI never retargets a different session.
456
+
457
+ Connected chat, exact-parent `emails wait`, and native notification listeners
458
+ share one receiver for the same local installation, API environment, and connected
459
+ credential. Expected replies stay with their wait; other approved mail can notify
460
+ the selected session. Another foreground participant can resume receiving when
461
+ the owner exits. Generic stdout, exec, forwarding, and `emails watch` remain
462
+ separate consumers.
463
+
464
+ Automatic runtime configuration, contact-policy synchronization, and notification
465
+ history backfill are not provided. Reply waits use targeted recovery for their
466
+ exact sent parent. Existing server queue retention and delivery-gap reporting
467
+ still apply; keep a foreground listener running for ongoing notifications.
468
+
469
+ ### Connected-agent listeners
470
+
471
+ Use the agent's existing connected-address credential with the same listener API.
472
+ The server automatically restricts its private subscription to inbound email for
473
+ that address. No owner credential, recipient filter, or relay is required. Names
474
+ are isolated per credential. Revoking or replacing the credential removes its
475
+ subscriptions and queued deliveries; reconnect with the new credential to start
476
+ receiving new events.
477
+
478
+ Address-scoped events contain parsed message content in `email.parsed`.
479
+ `email.content.raw` and `email.content.download` are null. Signed download links,
480
+ account routing metadata, and other SMTP envelope recipients are not exposed.
481
+ Attachments can be fetched through the authenticated email attachment API.
@@ -863,7 +863,7 @@ function credentialsPath(configDir) {
863
863
  function credentialsLockPath(configDir) {
864
864
  return join(configDir, CREDENTIALS_LOCK_DIR);
865
865
  }
866
- function normalize(url, fallback) {
866
+ function normalize$1(url, fallback) {
867
867
  const trimmed = url?.trim();
868
868
  if (!trimmed) return fallback;
869
869
  return trimmed.replace(/\/+$/, "");
@@ -889,7 +889,7 @@ function canonicalizeKnownApiBaseUrl(url) {
889
889
  return url;
890
890
  }
891
891
  function normalizeApiBaseUrl(url) {
892
- return canonicalizeKnownApiBaseUrl(normalize(url, DEFAULT_API_BASE_URL));
892
+ return canonicalizeKnownApiBaseUrl(normalize$1(url, DEFAULT_API_BASE_URL));
893
893
  }
894
894
  function cliAccessTokenExpiresAt(expiresInSeconds, now = Date.now) {
895
895
  return new Date(now() + expiresInSeconds * 1e3).toISOString();