primcli 3.0.0-preview.13 → 3.0.0-preview.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { B as ensureSearchReportsReplyState, Gn as sdk_gen_exports, J as EMAIL_RULE_REQUEST_EVENT_TYPES, K as replyStateSurfaceForOperation, L as ReplyStateUnsupportedError, Qt as getOwnGrant, R as assertAwaitingFilterRows, Ut as getEmail, W as isAwaitingRejectedError, X as WEBHOOK_EVENT_TYPES, Y as SENT_EMAIL_EVENT_TYPES, _n as listEndpoints, at as assertAutomatedVerdict, ct as ensureSearchReportsAutomated, dt as isAutomatedRejectedError, it as AutomatedFilterUnsupportedError, lt as expectedAutomatedVerdicts, nn as getThread, ot as automatedRejectedError, q as EMAIL_RULE_OBJECTION_EVENT_TYPES, st as automatedSurfaceForOperation, v as parseRetryAfter, z as awaitingRejectedError } from "./repeats-Dj_o6c-X-ykfsraq6.js";
1
+ import { $t as getOwnGrant, B as awaitingRejectedError, G as isAwaitingRejectedError, J as EMAIL_RULE_OBJECTION_EVENT_TYPES, Kn as sdk_gen_exports, R as ReplyStateUnsupportedError, V as ensureSearchReportsReplyState, Wt as getEmail, X as SENT_EMAIL_EVENT_TYPES, Y as EMAIL_RULE_REQUEST_EVENT_TYPES, Z as WEBHOOK_EVENT_TYPES, at as AutomatedFilterUnsupportedError, ct as automatedSurfaceForOperation, ft as isAutomatedRejectedError, lt as ensureSearchReportsAutomated, ot as assertAutomatedVerdict, q as replyStateSurfaceForOperation, rn as getThread, st as automatedRejectedError, ut as expectedAutomatedVerdicts, vn as listEndpoints, y as parseRetryAfter, z as assertAwaitingFilterRows } from "./repeats-Dj_o6c-X-C1y88ZwJ.js";
2
2
  import { C as saveCliCredentials, F as agentProfilesDirectory, H as requireDefaultLoginProfile, N as agentProfileDirectory, R as loadConnectedAgentProfile, S as resolveCliAuth, W as PrimitiveApiClient, _ as deleteCliCredentials, b as loadCliCredentials, c as resolveConfigEnvironment, d as validateCliHeaderName, f as validateCliHeaderValue, i as loadCliConfig, j as AGENT_PROFILE_ENV, m as cliAccessTokenExpiresAt, p as acquireCliCredentialsLock, w as saveSignupCredentials, x as normalizeApiBaseUrl, y as detectPrimitiveKeyEnvMisname } from "./cli-config-Ms_0vNjq.js";
3
3
  import "./oclif/global-org-flag.js";
4
4
  import { a as thrownErrorBody } from "./json-output-ch756TXV.js";
@@ -780,7 +780,7 @@ const operationManifest = [
780
780
  "binaryResponse": false,
781
781
  "bodyRequired": false,
782
782
  "command": "list-activity-objects",
783
- "description": "The objects the caller created, newest first, each with where it stands now, including ones since disabled, deleted or revoked where the object keeps a row: Functions, endpoints, Function triggers, pull subscriptions, recipient routes, filters, wake schedules, repeating sends and scheduled sends. An API key, an app or an agent sees what it created itself; a webhook given to an API key lists under that key. A person (a dashboard session, or their own Primitive CLI or app login) sees what they created through any credential of theirs, each item naming it, and also their API keys, personal agents, email rules, rule requests and objections to rules over their mail (objections only on the dashboard or the Primitive CLI). Each kind is listed only if the caller could list it through that kind's own route; a kind it could not is named in withheld, with no count. Ids, names and states only: never a webhook URL or mail content. Needs activity:read_self. With relation=asked it lists instead what other members asked of the person, open or answered: the rule requests made to them, and the objections to rules they wrote, under their current membership, each naming the member who asked or objected in created_through. A key, an app or an agent has none, and gets both kinds in withheld. With relation=concerning it lists instead what other members did that concerns the person's own mail: each time a rule started covering one of their mailboxes or saved a new version while covering it, named as the rule was then, naming its author in created_through, under their current membership; only on the dashboard or the Primitive CLI. A key, an app or an agent has none, and gets email_rule_coverage in withheld. A notice never reads as ended. Notices are written by a periodic job and can lag: until the notices record a rule starting to cover you, the first page lists that rule as pending (pending_complete says whether that check finished), and as_of says about when the notices last looked. For what covers you now, read GET /email-rules/covering-me.",
783
+ "description": "The objects the caller created, newest first, each with where it stands now, including ones since disabled, deleted or revoked where the object keeps a row: Functions, endpoints, Function triggers, pull subscriptions, recipient routes, filters, wake schedules, repeating sends and scheduled sends. An API key, an app or an agent sees what it created itself; a webhook given to an API key lists under that key. A person (a dashboard session, or their own Primitive CLI or app login) sees what they created through any credential of theirs, each item naming it, and also their API keys, personal agents, email rules, rule requests and objections to rules over their mail (objections only on the dashboard or the Primitive CLI). Each kind is listed only if the caller could list it through that kind's own route; a kind it could not is named in withheld, with no count. Ids, names and states only: never a webhook URL or mail content. Needs activity:read_self. With relation=asked it lists instead what other members asked of the person, open or answered: the rule requests made to them, and the objections to rules they wrote, under their current membership, each naming the member who asked or objected in created_through. A key, an app or an agent has none, and gets both kinds in withheld. With relation=concerning it lists instead what other members did that concerns the person's own mail: each time a rule started covering one of their mailboxes, saved a new version, or changed who receives its matches while covering it, named as the rule was then, naming its author in created_through, under their current membership; only on the dashboard or the Primitive CLI. A key, an app or an agent has none, and gets email_rule_coverage in withheld. A notice never reads as ended. Notices are written by a periodic job and can lag: until the notices record a rule starting to cover you, the first page lists that rule as pending (pending_complete says whether that check finished), and as_of says about when the notices last looked. For what covers you now, read GET /email-rules/covering-me.",
784
784
  "hasJsonBody": false,
785
785
  "method": "GET",
786
786
  "operationId": "listActivityObjects",
@@ -863,7 +863,7 @@ const operationManifest = [
863
863
  },
864
864
  "state": {
865
865
  "type": "string",
866
- "description": "Where it stands now: enabled, disabled, deleted or replaced (endpoints and routes), enabled or disabled (filters, wake schedules), live, suspended or deleted (Functions), active, paused or ended (repeating sends), scheduled (scheduled sends), active, suspended, expired or revoked (API keys), pending, claimed, connected or revoked (agents), draft, observe or active (email rules), pending, accepted, declined or withdrawn (rule requests and objections), and for a coverage notice what it told: started (a rule was made or turned active over your mailboxes, or an active rule came to reach you; once per rule, not per mailbox), new_version, or baseline (it already covered you when notices began). pending: a rule started covering your mailbox and the notices have not recorded that start yet (a restart within an hour of the last start notice is recorded without a new one); its id is then the rule's, and created_at when the rule last changed."
866
+ "description": "Where it stands now: enabled, disabled, deleted or replaced (endpoints and routes), enabled or disabled (filters, wake schedules), live, suspended or deleted (Functions), active, paused or ended (repeating sends), scheduled (scheduled sends), active, suspended, expired or revoked (API keys), pending, claimed, connected or revoked (agents), draft, observe or active (email rules), pending, accepted, declined or withdrawn (rule requests and objections), and for a coverage notice what it told: started (a rule was made or turned active over your mailboxes, or an active rule came to reach you; once per rule, not per mailbox), new_version, receivers_changed (who receives an active rule's matches changed: its keys, webhooks or share), or baseline (it already covered you when notices began). pending: a rule started covering your mailbox and the notices have not recorded that start yet (a restart within an hour of the last start notice is recorded without a new one); its id is then the rule's, and created_at when the rule last changed."
867
867
  },
868
868
  "created_at": {
869
869
  "type": "string",
@@ -872,7 +872,7 @@ const operationManifest = [
872
872
  "ended_at": {
873
873
  "type": ["string", "null"],
874
874
  "format": "date-time",
875
- "description": "When it was deleted, replaced or revoked, when an API key expired, when a repeating send stopped, or when a rule request or objection was decided (accepted, declined or withdrawn); null otherwise, as on every coverage notice."
875
+ "description": "When it was deleted, replaced or revoked, when an API key expired, when a repeating send stopped, or when a rule request or objection was decided (accepted, declined or withdrawn); null otherwise, as on every coverage notice (rule_now marks one whose rule was deleted)."
876
876
  },
877
877
  "owned_now": {
878
878
  "type": "string",
@@ -902,9 +902,77 @@ const operationManifest = [
902
902
  ]
903
903
  },
904
904
  "rule_id": {
905
- "description": "For email_rule_coverage, always: the rule it is about, since names need not be unique (for pending, the same as id). GET /v1/email-rules/covering-me/{rule_id} shows the rule while it covers you, or as it last decided on your mail; otherwise it answers not_found.",
905
+ "description": "For email_rule_coverage, always: the rule it is about, since names need not be unique (for pending, the same as id). GET /v1/email-rules/covering-me/{rule_id} shows the rule while it covers you, or as it last decided on your mail; otherwise it answers not_found, with details.reason rule_deleted once a rule you were told of is deleted.",
906
906
  "type": "string",
907
907
  "format": "uuid"
908
+ },
909
+ "rule_now": {
910
+ "description": "For email_rule_coverage: deleted once the rule it is about has been deleted. Left out while it exists, or when that could not be checked.",
911
+ "type": "string",
912
+ "enum": ["deleted"]
913
+ },
914
+ "receivers_change": {
915
+ "description": "For a receivers_changed notice: who received the rule's matches before and after, in totals. They can be equal when only an owner or a webhook site changed; covering-me shows them by owner. A rotated key counts once here while the old one still works; covering-me counts both.",
916
+ "type": "object",
917
+ "properties": {
918
+ "before": {
919
+ "type": "object",
920
+ "properties": {
921
+ "keys": {
922
+ "type": "integer",
923
+ "minimum": 0
924
+ },
925
+ "reads_in_full": {
926
+ "type": "integer",
927
+ "minimum": 0,
928
+ "description": "Of the keys, how many also read your own mail beyond the rule."
929
+ },
930
+ "webhooks": {
931
+ "type": "integer",
932
+ "minimum": 0
933
+ },
934
+ "shares": {
935
+ "type": "integer",
936
+ "minimum": 0
937
+ }
938
+ },
939
+ "required": [
940
+ "keys",
941
+ "reads_in_full",
942
+ "webhooks",
943
+ "shares"
944
+ ]
945
+ },
946
+ "after": {
947
+ "type": "object",
948
+ "properties": {
949
+ "keys": {
950
+ "type": "integer",
951
+ "minimum": 0
952
+ },
953
+ "reads_in_full": {
954
+ "type": "integer",
955
+ "minimum": 0,
956
+ "description": "Of the keys, how many also read your own mail beyond the rule."
957
+ },
958
+ "webhooks": {
959
+ "type": "integer",
960
+ "minimum": 0
961
+ },
962
+ "shares": {
963
+ "type": "integer",
964
+ "minimum": 0
965
+ }
966
+ },
967
+ "required": [
968
+ "keys",
969
+ "reads_in_full",
970
+ "webhooks",
971
+ "shares"
972
+ ]
973
+ }
974
+ },
975
+ "required": ["before", "after"]
908
976
  }
909
977
  },
910
978
  "required": [
@@ -951,7 +1019,7 @@ const operationManifest = [
951
1019
  "as_of": {
952
1020
  "type": ["string", "null"],
953
1021
  "format": "date-time",
954
- "description": "With relation=concerning only: about when the notices last looked at your organization; null if never or unread. Coverage since may have no notice yet."
1022
+ "description": "With relation=concerning only: about when the notices last looked at your organization; null if never or unread. Coverage since may have no notice yet. They look only while one of its rules could cover someone, so this stops moving while none could."
955
1023
  },
956
1024
  "pending_complete": {
957
1025
  "description": "With relation=concerning, on the first page only: whether the check for rules that started covering you without their start notice (state pending, listed ahead of the notices and beyond limit) was finished. false: some may be missing. Absent where there is nothing to check.",
@@ -6390,7 +6458,10 @@ const operationManifest = [
6390
6458
  null
6391
6459
  ]
6392
6460
  },
6393
- "webhook_attempt_count": { "type": "integer" },
6461
+ "webhook_attempt_count": {
6462
+ "type": "integer",
6463
+ "description": "The email's own routed delivery only (where routing sent it); a rule's deliveries never change this, and are in `rule_deliveries` on GET /emails/{id} for the mailbox's owner. Its attempts."
6464
+ },
6394
6465
  "thread_id": {
6395
6466
  "type": ["string", "null"],
6396
6467
  "format": "uuid",
@@ -9700,6 +9771,14 @@ const operationManifest = [
9700
9771
  "additionalProperties": {}
9701
9772
  }
9702
9773
  },
9774
+ "mail_limits_available": {
9775
+ "description": "Whether a key limited to some mail (addresses, domains or email rules) would be created now. While false, creating one is refused, so a client offers neither the listed-address presets nor a listed mail reach for a custom key.",
9776
+ "type": "boolean"
9777
+ },
9778
+ "org_reach_available": {
9779
+ "description": "Whether the credential policy lets a new key reach every mailbox. While false, creating one is refused, so a client offers no preset that reaches every mailbox, and a custom key lists its addresses or domains. With mail_limits_available false as well, no key can be created and every preset is unavailable.",
9780
+ "type": "boolean"
9781
+ },
9703
9782
  "matrix": {
9704
9783
  "type": "array",
9705
9784
  "items": {
@@ -10867,7 +10946,7 @@ const operationManifest = [
10867
10946
  "binaryResponse": false,
10868
10947
  "bodyRequired": true,
10869
10948
  "command": "rotate-key",
10870
- "description": "Mint a successor with a new id and the same name, grant, expiry and conditions, and return its secret once. The old key keeps working for overlap_seconds, then stops: 24 hours when overlap_seconds is omitted, at most 7 days, never past its own expires_at, and at once with 0 (which is how to rotate a key whose secret leaked). previous_key_expires_at says when the old key stops. Revoking the successor during the overlap also revokes the old key when you may revoke that one too; the revoke's predecessors_left counts any left working. Rotating does not extend a key: the successor keeps the old expiry unless expires_at gives it a new one. That needs keys:manage (403 forbidden otherwise), is held to the credential policy (400 when past its maximum lifetime, with the latest expiry it allows now in the message), and someone limited to their own keys can only keep or bring it forward (403 grant_forbidden, details.reason own_key_tighten_only); the old key then never outlives the new one. The person rotating must cover the key's access (403 grant_forbidden, details.reason target_not_covered), and a key an owner holds is rotated by an owner only (403 forbidden, details.reason owner_key_rotation), since rotating hands over the new secret. The person's own Primitive CLI login needs no step-up (it acts as the person, held to the same rules) unless the organization's credential policy sets cli_writes_require_approval; the Primitive app's token is refused, even with one. Needs the organization's credential features to be enabled; while they are not, this answers 404 feature_disabled, with details.feature naming the feature. Contact Primitive to turn them on.\n\nPerson only: a dashboard session, the person's own Primitive CLI login with no step-up (unless the organization's credential policy sets cli_writes_require_approval), or the person's own OAuth token with a step-up token for purpose `keys.create` in the Primitive-Step-Up header. Without one any other OAuth token is answered 403 step_up_required, whose details carry the dashboard URL that issues it (or 403 grant_forbidden when the person's role, or the client, could not do it anyway). The person's role must hold keys:manage. API keys and agent connection keys are refused. An agent acting for the person is refused unless it carries a step-up token the person issued in the dashboard.",
10949
+ "description": "Mint a successor with a new id and the same name, grant, expiry and conditions, and return its secret once, with its name and grant so you can see they carried over. The old key keeps working for overlap_seconds, then stops: 24 hours when overlap_seconds is omitted, at most 7 days, never past its own expires_at, and at once with 0 (which is how to rotate a key whose secret leaked). previous_key_expires_at says when the old key stops. Revoking the successor during the overlap also revokes the old key when you may revoke that one too; the revoke's predecessors_left counts any left working. Rotating does not extend a key: the successor keeps the old expiry unless expires_at gives it a new one. That needs keys:manage (403 forbidden otherwise), is held to the credential policy (400 when past its maximum lifetime, with the latest expiry it allows now in the message), and someone limited to their own keys can only keep or bring it forward (403 grant_forbidden, details.reason own_key_tighten_only); the old key then never outlives the new one. The person rotating must cover the key's access (403 grant_forbidden, details.reason target_not_covered), and a key an owner holds is rotated by an owner only (403 forbidden, details.reason owner_key_rotation), since rotating hands over the new secret. The person's own Primitive CLI login needs no step-up (it acts as the person, held to the same rules) unless the organization's credential policy sets cli_writes_require_approval; the Primitive app's token is refused, even with one. Needs the organization's credential features to be enabled; while they are not, this answers 404 feature_disabled, with details.feature naming the feature. Contact Primitive to turn them on.\n\nPerson only: a dashboard session, the person's own Primitive CLI login with no step-up (unless the organization's credential policy sets cli_writes_require_approval), or the person's own OAuth token with a step-up token for purpose `keys.create` in the Primitive-Step-Up header. Without one any other OAuth token is answered 403 step_up_required, whose details carry the dashboard URL that issues it (or 403 grant_forbidden when the person's role, or the client, could not do it anyway). The person's role must hold keys:manage. API keys and agent connection keys are refused. An agent acting for the person is refused unless it carries a step-up token the person issued in the dashboard.",
10871
10950
  "hasJsonBody": true,
10872
10951
  "method": "POST",
10873
10952
  "operationId": "rotateKey",
@@ -10925,6 +11004,36 @@ const operationManifest = [
10925
11004
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
10926
11005
  },
10927
11006
  "previous_key_id": { "type": "string" },
11007
+ "name": {
11008
+ "description": "The successor's name: the rotated key's, which it keeps.",
11009
+ "type": ["string", "null"]
11010
+ },
11011
+ "grant": {
11012
+ "description": "The grant the successor holds, as a create answers it: the rotated key's own, carried over unchanged (rules, addresses and domains included).",
11013
+ "type": "object",
11014
+ "properties": {
11015
+ "template": { "type": ["string", "null"] },
11016
+ "credential_grant_id": { "type": ["string", "null"] },
11017
+ "permissions": {
11018
+ "type": ["array", "null"],
11019
+ "items": { "type": "string" }
11020
+ },
11021
+ "mail_scope": {},
11022
+ "resources": {}
11023
+ },
11024
+ "required": [
11025
+ "template",
11026
+ "credential_grant_id",
11027
+ "permissions",
11028
+ "mail_scope",
11029
+ "resources"
11030
+ ],
11031
+ "additionalProperties": {}
11032
+ },
11033
+ "rotated_from": {
11034
+ "description": "The rotated key: previous_key_id, under the name GET /v1/keys/{id} gives it.",
11035
+ "type": "string"
11036
+ },
10928
11037
  "previous_key_expires_at": {
10929
11038
  "type": "string",
10930
11039
  "format": "date-time",
@@ -13076,6 +13185,388 @@ const operationManifest = [
13076
13185
  "tag": "Domains",
13077
13186
  "tagCommand": "domains"
13078
13187
  },
13188
+ {
13189
+ "binaryResponse": false,
13190
+ "bodyRequired": false,
13191
+ "command": "get-email-notification-preferences",
13192
+ "description": "The notification emails the calling member gets, and which of them they may turn on or off. Each kind lists its label and description, its class, whether it needs an entitlement and whether the organization has it, the organization's mode (null for platform_required) and the modes it may choose, and whether the calling member gets it and may change that.\n\nPerson only: a dashboard session or the person's own OAuth token (the CLI or an app they connected). The person's role must hold notifications:manage. API keys, agent connection keys and agents are refused.",
13193
+ "hasJsonBody": false,
13194
+ "method": "GET",
13195
+ "operationId": "getEmailNotificationPreferences",
13196
+ "path": "/email-notification-preferences",
13197
+ "pathParams": [],
13198
+ "queryParams": [],
13199
+ "headerParams": [],
13200
+ "permissions": ["notifications:manage"],
13201
+ "access": "person",
13202
+ "requestSchema": null,
13203
+ "responseSchema": {
13204
+ "type": "object",
13205
+ "properties": {
13206
+ "entitled": { "type": "boolean" },
13207
+ "kinds": {
13208
+ "type": "array",
13209
+ "items": {
13210
+ "type": "object",
13211
+ "properties": {
13212
+ "kind": { "type": "string" },
13213
+ "label": { "type": "string" },
13214
+ "description": { "type": "string" },
13215
+ "class": {
13216
+ "type": "string",
13217
+ "enum": [
13218
+ "platform_required",
13219
+ "org_requirable",
13220
+ "optional"
13221
+ ]
13222
+ },
13223
+ "entitlement_required": { "type": "boolean" },
13224
+ "entitled": { "type": "boolean" },
13225
+ "org_mode": {
13226
+ "type": ["string", "null"],
13227
+ "enum": [
13228
+ "off",
13229
+ "optional_on",
13230
+ "optional_off",
13231
+ "required",
13232
+ null
13233
+ ]
13234
+ },
13235
+ "org_mode_is_default": { "type": "boolean" },
13236
+ "org_modes": {
13237
+ "type": "array",
13238
+ "items": {
13239
+ "type": "string",
13240
+ "enum": [
13241
+ "off",
13242
+ "optional_on",
13243
+ "optional_off",
13244
+ "required"
13245
+ ]
13246
+ }
13247
+ },
13248
+ "member_enabled": { "type": "boolean" },
13249
+ "member_can_change": { "type": "boolean" }
13250
+ },
13251
+ "required": [
13252
+ "kind",
13253
+ "label",
13254
+ "description",
13255
+ "class",
13256
+ "entitlement_required",
13257
+ "entitled",
13258
+ "org_mode",
13259
+ "org_mode_is_default",
13260
+ "org_modes",
13261
+ "member_enabled",
13262
+ "member_can_change"
13263
+ ]
13264
+ }
13265
+ }
13266
+ },
13267
+ "required": ["entitled", "kinds"]
13268
+ },
13269
+ "sdkName": "getEmailNotificationPreferences",
13270
+ "summary": "Get my email notification preferences",
13271
+ "tag": "Email notifications",
13272
+ "tagCommand": "email-notifications"
13273
+ },
13274
+ {
13275
+ "binaryResponse": false,
13276
+ "bodyRequired": false,
13277
+ "command": "get-email-notification-settings",
13278
+ "description": "Every kind of notification email Primitive sends the organization's members, and who may turn it off. Each kind lists its label and description, its class, whether it needs an entitlement and whether the organization has it, the organization's mode (null for platform_required) and the modes it may choose, and whether the calling member gets it and may change that.\n\nPerson only: a dashboard session or the person's own OAuth token (the CLI or an app they connected). API keys, agent connection keys and agents are refused.",
13279
+ "hasJsonBody": false,
13280
+ "method": "GET",
13281
+ "operationId": "getEmailNotificationSettings",
13282
+ "path": "/email-notification-settings",
13283
+ "pathParams": [],
13284
+ "queryParams": [],
13285
+ "headerParams": [],
13286
+ "permissions": ["org:read"],
13287
+ "access": "person",
13288
+ "requestSchema": null,
13289
+ "responseSchema": {
13290
+ "type": "object",
13291
+ "properties": {
13292
+ "entitled": { "type": "boolean" },
13293
+ "kinds": {
13294
+ "type": "array",
13295
+ "items": {
13296
+ "type": "object",
13297
+ "properties": {
13298
+ "kind": { "type": "string" },
13299
+ "label": { "type": "string" },
13300
+ "description": { "type": "string" },
13301
+ "class": {
13302
+ "type": "string",
13303
+ "enum": [
13304
+ "platform_required",
13305
+ "org_requirable",
13306
+ "optional"
13307
+ ]
13308
+ },
13309
+ "entitlement_required": { "type": "boolean" },
13310
+ "entitled": { "type": "boolean" },
13311
+ "org_mode": {
13312
+ "type": ["string", "null"],
13313
+ "enum": [
13314
+ "off",
13315
+ "optional_on",
13316
+ "optional_off",
13317
+ "required",
13318
+ null
13319
+ ]
13320
+ },
13321
+ "org_mode_is_default": { "type": "boolean" },
13322
+ "org_modes": {
13323
+ "type": "array",
13324
+ "items": {
13325
+ "type": "string",
13326
+ "enum": [
13327
+ "off",
13328
+ "optional_on",
13329
+ "optional_off",
13330
+ "required"
13331
+ ]
13332
+ }
13333
+ },
13334
+ "member_enabled": { "type": "boolean" },
13335
+ "member_can_change": { "type": "boolean" }
13336
+ },
13337
+ "required": [
13338
+ "kind",
13339
+ "label",
13340
+ "description",
13341
+ "class",
13342
+ "entitlement_required",
13343
+ "entitled",
13344
+ "org_mode",
13345
+ "org_mode_is_default",
13346
+ "org_modes",
13347
+ "member_enabled",
13348
+ "member_can_change"
13349
+ ]
13350
+ }
13351
+ }
13352
+ },
13353
+ "required": ["entitled", "kinds"]
13354
+ },
13355
+ "sdkName": "getEmailNotificationSettings",
13356
+ "summary": "Get the organization's email notification settings",
13357
+ "tag": "Email notifications",
13358
+ "tagCommand": "email-notifications"
13359
+ },
13360
+ {
13361
+ "binaryResponse": false,
13362
+ "bodyRequired": true,
13363
+ "command": "update-email-notification-preferences",
13364
+ "description": "Turn one or more kinds on or off for the calling member. Only a kind whose organization mode is optional_on or optional_off can be changed: a platform_required kind, one the organization requires, and one it turned off are refused with 409 (conflict, details.reason not_member_choice, details.org_mode saying which), and nothing is changed. An unknown kind is refused with 400. Answers the preferences as they now stand. Each kind lists its label and description, its class, whether it needs an entitlement and whether the organization has it, the organization's mode (null for platform_required) and the modes it may choose, and whether the calling member gets it and may change that.\n\nPerson only: a dashboard session or the person's own OAuth token (the CLI or an app they connected). The person's role must hold notifications:manage. API keys, agent connection keys and agents are refused.",
13365
+ "hasJsonBody": true,
13366
+ "method": "PUT",
13367
+ "operationId": "updateEmailNotificationPreferences",
13368
+ "path": "/email-notification-preferences",
13369
+ "pathParams": [],
13370
+ "queryParams": [],
13371
+ "headerParams": [],
13372
+ "permissions": ["notifications:manage"],
13373
+ "access": "person",
13374
+ "requestSchema": {
13375
+ "type": "object",
13376
+ "properties": { "preferences": {
13377
+ "type": "object",
13378
+ "propertyNames": {
13379
+ "type": "string",
13380
+ "pattern": "^[a-z][a-z0-9_]{0,63}$"
13381
+ },
13382
+ "additionalProperties": { "type": "boolean" }
13383
+ } },
13384
+ "required": ["preferences"],
13385
+ "additionalProperties": false
13386
+ },
13387
+ "responseSchema": {
13388
+ "type": "object",
13389
+ "properties": {
13390
+ "entitled": { "type": "boolean" },
13391
+ "kinds": {
13392
+ "type": "array",
13393
+ "items": {
13394
+ "type": "object",
13395
+ "properties": {
13396
+ "kind": { "type": "string" },
13397
+ "label": { "type": "string" },
13398
+ "description": { "type": "string" },
13399
+ "class": {
13400
+ "type": "string",
13401
+ "enum": [
13402
+ "platform_required",
13403
+ "org_requirable",
13404
+ "optional"
13405
+ ]
13406
+ },
13407
+ "entitlement_required": { "type": "boolean" },
13408
+ "entitled": { "type": "boolean" },
13409
+ "org_mode": {
13410
+ "type": ["string", "null"],
13411
+ "enum": [
13412
+ "off",
13413
+ "optional_on",
13414
+ "optional_off",
13415
+ "required",
13416
+ null
13417
+ ]
13418
+ },
13419
+ "org_mode_is_default": { "type": "boolean" },
13420
+ "org_modes": {
13421
+ "type": "array",
13422
+ "items": {
13423
+ "type": "string",
13424
+ "enum": [
13425
+ "off",
13426
+ "optional_on",
13427
+ "optional_off",
13428
+ "required"
13429
+ ]
13430
+ }
13431
+ },
13432
+ "member_enabled": { "type": "boolean" },
13433
+ "member_can_change": { "type": "boolean" }
13434
+ },
13435
+ "required": [
13436
+ "kind",
13437
+ "label",
13438
+ "description",
13439
+ "class",
13440
+ "entitlement_required",
13441
+ "entitled",
13442
+ "org_mode",
13443
+ "org_mode_is_default",
13444
+ "org_modes",
13445
+ "member_enabled",
13446
+ "member_can_change"
13447
+ ]
13448
+ }
13449
+ }
13450
+ },
13451
+ "required": ["entitled", "kinds"]
13452
+ },
13453
+ "sdkName": "updateEmailNotificationPreferences",
13454
+ "summary": "Change my email notification preferences",
13455
+ "tag": "Email notifications",
13456
+ "tagCommand": "email-notifications"
13457
+ },
13458
+ {
13459
+ "binaryResponse": false,
13460
+ "bodyRequired": true,
13461
+ "command": "update-email-notification-settings",
13462
+ "description": "Set the organization's mode for one or more kinds: off (nobody gets it), optional_on (each member gets it unless they turn it off), optional_off (only members who turn it on get it) or required (every member gets it). Kinds left out keep their mode. An unknown kind is refused with 400 (validation_error, details.reason unknown_kind). A platform_required kind is refused with 409 (conflict, details.reason platform_required), and required for an optional kind with 409 (conflict, details.reason mode_not_allowed); nothing is changed. Answers the settings as they now stand. Each kind lists its label and description, its class, whether it needs an entitlement and whether the organization has it, the organization's mode (null for platform_required) and the modes it may choose, and whether the calling member gets it and may change that.\n\nPerson only: a dashboard session, or the person's own OAuth token with a step-up token for purpose `org_settings.write` in the Primitive-Step-Up header. Without one an OAuth token is answered 403 step_up_required, whose details carry the dashboard URL that issues it (or 403 grant_forbidden when the person's role could not do it anyway). The person's role must hold notification_policy:manage. API keys and agent connection keys are refused. An agent acting for the person is refused unless it carries a step-up token the person issued in the dashboard.",
13463
+ "hasJsonBody": true,
13464
+ "method": "PUT",
13465
+ "operationId": "updateEmailNotificationSettings",
13466
+ "path": "/email-notification-settings",
13467
+ "pathParams": [],
13468
+ "queryParams": [],
13469
+ "headerParams": [{
13470
+ "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
13471
+ "enum": null,
13472
+ "name": "Primitive-Step-Up",
13473
+ "required": false,
13474
+ "type": "string"
13475
+ }],
13476
+ "permissions": ["notification_policy:manage"],
13477
+ "access": "step_up",
13478
+ "requestSchema": {
13479
+ "type": "object",
13480
+ "properties": { "modes": {
13481
+ "type": "object",
13482
+ "propertyNames": {
13483
+ "type": "string",
13484
+ "pattern": "^[a-z][a-z0-9_]{0,63}$"
13485
+ },
13486
+ "additionalProperties": {
13487
+ "type": "string",
13488
+ "enum": [
13489
+ "off",
13490
+ "optional_on",
13491
+ "optional_off",
13492
+ "required"
13493
+ ]
13494
+ }
13495
+ } },
13496
+ "required": ["modes"],
13497
+ "additionalProperties": false
13498
+ },
13499
+ "responseSchema": {
13500
+ "type": "object",
13501
+ "properties": {
13502
+ "entitled": { "type": "boolean" },
13503
+ "kinds": {
13504
+ "type": "array",
13505
+ "items": {
13506
+ "type": "object",
13507
+ "properties": {
13508
+ "kind": { "type": "string" },
13509
+ "label": { "type": "string" },
13510
+ "description": { "type": "string" },
13511
+ "class": {
13512
+ "type": "string",
13513
+ "enum": [
13514
+ "platform_required",
13515
+ "org_requirable",
13516
+ "optional"
13517
+ ]
13518
+ },
13519
+ "entitlement_required": { "type": "boolean" },
13520
+ "entitled": { "type": "boolean" },
13521
+ "org_mode": {
13522
+ "type": ["string", "null"],
13523
+ "enum": [
13524
+ "off",
13525
+ "optional_on",
13526
+ "optional_off",
13527
+ "required",
13528
+ null
13529
+ ]
13530
+ },
13531
+ "org_mode_is_default": { "type": "boolean" },
13532
+ "org_modes": {
13533
+ "type": "array",
13534
+ "items": {
13535
+ "type": "string",
13536
+ "enum": [
13537
+ "off",
13538
+ "optional_on",
13539
+ "optional_off",
13540
+ "required"
13541
+ ]
13542
+ }
13543
+ },
13544
+ "member_enabled": { "type": "boolean" },
13545
+ "member_can_change": { "type": "boolean" }
13546
+ },
13547
+ "required": [
13548
+ "kind",
13549
+ "label",
13550
+ "description",
13551
+ "class",
13552
+ "entitlement_required",
13553
+ "entitled",
13554
+ "org_mode",
13555
+ "org_mode_is_default",
13556
+ "org_modes",
13557
+ "member_enabled",
13558
+ "member_can_change"
13559
+ ]
13560
+ }
13561
+ }
13562
+ },
13563
+ "required": ["entitled", "kinds"]
13564
+ },
13565
+ "sdkName": "updateEmailNotificationSettings",
13566
+ "summary": "Change the organization's email notification settings",
13567
+ "tag": "Email notifications",
13568
+ "tagCommand": "email-notifications"
13569
+ },
13079
13570
  {
13080
13571
  "binaryResponse": false,
13081
13572
  "bodyRequired": false,
@@ -13182,10 +13673,13 @@ const operationManifest = [
13182
13673
  "description": "Whether it is to a rule you wrote, in your current membership."
13183
13674
  },
13184
13675
  "message": {
13185
- "description": "Why the objector objects; null when none was given, or once withdrawn.",
13676
+ "description": "Why they object and what they ask (to turn the rule off, narrow it, or explain who receives it); null when none was given, or once withdrawn.",
13677
+ "type": ["string", "null"]
13678
+ },
13679
+ "decline_reason": {
13680
+ "description": "The author's answer when declining, which the objector sees; null when none was given.",
13186
13681
  "type": ["string", "null"]
13187
13682
  },
13188
- "decline_reason": { "type": ["string", "null"] },
13189
13683
  "created_at": {
13190
13684
  "type": "string",
13191
13685
  "format": "date-time"
@@ -13223,7 +13717,7 @@ const operationManifest = [
13223
13717
  "binaryResponse": false,
13224
13718
  "bodyRequired": true,
13225
13719
  "command": "create-email-rule-objection",
13226
- "description": "Asks the author of a rule that covers one of your own mailboxes now to turn it off. Only a rule that covers you now: one you see only because it once decided on your mail cannot be objected to. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. You may have one pending objection per rule and 50 in all, and object to the same rule again only 10 minutes after you last did, whatever became of it. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
13720
+ "description": "Objects to a rule that covers one of your own mailboxes now: say in the message what you ask its author, to turn it off, narrow it, or explain who receives what it matches. The author answers by accepting, which turns it off, or by declining, optionally with a reason you see; the rule keeps running unless they accept. Only a rule that covers you now: one you see only because it once decided on your mail cannot be objected to. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it (optionally with a reason the objector sees), or the member who made it withdraws it, which clears its message. It never changes after that. You may have one pending objection per rule and 50 in all, and object to the same rule again only 10 minutes after you last did, whatever became of it. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
13227
13721
  "hasJsonBody": true,
13228
13722
  "method": "POST",
13229
13723
  "operationId": "createEmailRuleObjection",
@@ -13251,7 +13745,7 @@ const operationManifest = [
13251
13745
  "type": ["string", "null"],
13252
13746
  "minLength": 1,
13253
13747
  "maxLength": 500,
13254
- "description": "Why you object, up to 500 characters; null or absent for none. The rule's author and the organization's owners and admins see it while it stands; withdrawing clears it."
13748
+ "description": "Why you object and what you ask, up to 500 characters: to turn the rule off, to narrow it, or to explain who receives what it matches. Null or absent for none. The rule's author and the organization's owners and admins see it while it stands; withdrawing clears it."
13255
13749
  }
13256
13750
  },
13257
13751
  "required": ["rule_id"],
@@ -13336,10 +13830,13 @@ const operationManifest = [
13336
13830
  "description": "Whether it is to a rule you wrote, in your current membership."
13337
13831
  },
13338
13832
  "message": {
13339
- "description": "Why the objector objects; null when none was given, or once withdrawn.",
13833
+ "description": "Why they object and what they ask (to turn the rule off, narrow it, or explain who receives it); null when none was given, or once withdrawn.",
13834
+ "type": ["string", "null"]
13835
+ },
13836
+ "decline_reason": {
13837
+ "description": "The author's answer when declining, which the objector sees; null when none was given.",
13340
13838
  "type": ["string", "null"]
13341
13839
  },
13342
- "decline_reason": { "type": ["string", "null"] },
13343
13840
  "created_at": {
13344
13841
  "type": "string",
13345
13842
  "format": "date-time"
@@ -13377,7 +13874,7 @@ const operationManifest = [
13377
13874
  "binaryResponse": false,
13378
13875
  "bodyRequired": false,
13379
13876
  "command": "decline-email-rule-objection",
13380
- "description": "Declines a pending objection to a rule you wrote, optionally saying why; the rule stays as it is. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
13877
+ "description": "Declines a pending objection to a rule you wrote, optionally saying why (reason), which the member who objected sees; the rule stays as it is. When its message asks you to narrow the rule or explain who receives it, the reason is your answer; narrow it with a new version as well if you agree. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
13381
13878
  "hasJsonBody": true,
13382
13879
  "method": "POST",
13383
13880
  "operationId": "declineEmailRuleObjection",
@@ -13405,7 +13902,7 @@ const operationManifest = [
13405
13902
  "type": ["string", "null"],
13406
13903
  "minLength": 1,
13407
13904
  "maxLength": 500,
13408
- "description": "Why you are declining, up to 500 characters; null or absent for none. The member who objected sees it."
13905
+ "description": "Why you are declining, up to 500 characters; null or absent for none. The member who objected sees it: it is how to answer one that asks you to narrow the rule or explain who receives it."
13409
13906
  } },
13410
13907
  "additionalProperties": false
13411
13908
  },
@@ -13488,10 +13985,13 @@ const operationManifest = [
13488
13985
  "description": "Whether it is to a rule you wrote, in your current membership."
13489
13986
  },
13490
13987
  "message": {
13491
- "description": "Why the objector objects; null when none was given, or once withdrawn.",
13988
+ "description": "Why they object and what they ask (to turn the rule off, narrow it, or explain who receives it); null when none was given, or once withdrawn.",
13989
+ "type": ["string", "null"]
13990
+ },
13991
+ "decline_reason": {
13992
+ "description": "The author's answer when declining, which the objector sees; null when none was given.",
13492
13993
  "type": ["string", "null"]
13493
13994
  },
13494
- "decline_reason": { "type": ["string", "null"] },
13495
13995
  "created_at": {
13496
13996
  "type": "string",
13497
13997
  "format": "date-time"
@@ -13529,7 +14029,7 @@ const operationManifest = [
13529
14029
  "binaryResponse": false,
13530
14030
  "bodyRequired": false,
13531
14031
  "command": "get-email-rule-covering-me",
13532
- "description": "One rule that covers one of your own mailboxes, read-only, with its current version; or one that no longer does but decided on mail at your own mailbox (covers_now false), shown as the version that last decided on your mail, without its current name. You see its name, wording, current tree, state, kind and author, and which of your own mailboxes it reaches. Its wording and tree are shown as written, so they can name other addresses (a condition on who an email is from or to). While it covers you, you also see who receives what it matches (receivers): how many API keys, webhooks and shares, the member each belongs to (or the organization), and the sites the webhooks deliver to. You do not see which keys, webhooks or shares they are (no ids, names, prefixes, or URLs beyond the site), the other inboxes and domains its scope names, or its saved expectations. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
14032
+ "description": "One rule that covers one of your own mailboxes, read-only, with its current version; or one that no longer does but decided on mail at your own mailbox (covers_now false), shown as the version that last decided on your mail, without its current name. You see its name, wording, current tree, state, kind and author, and which of your own mailboxes it reaches. Its wording and tree are shown as written, so they can name other addresses (a condition on who an email is from or to). While it covers you, you also see who receives what it matches (receivers): how many API keys, webhooks and shares, the member each belongs to (or the organization), and the sites the webhooks deliver to. You do not see which keys, webhooks or shares they are (no ids, names, prefixes, or URLs beyond the site), the other inboxes and domains its scope names, or its saved expectations. For what happened to one email of yours, every rule's decision on it and, for another member's rule covering you, who receives what it shares: GET /v1/emails/{id}/rule-results (primitive email-rules list-email-rule-results --email-id <email-id>). Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
13533
14033
  "hasJsonBody": false,
13534
14034
  "method": "GET",
13535
14035
  "operationId": "getEmailRuleCoveringMe",
@@ -13571,6 +14071,10 @@ const operationManifest = [
13571
14071
  "enum": ["grant", "do_not_share"],
13572
14072
  "description": "grant: what it matches can be shared. do_not_share: it blocks sharing what it matches."
13573
14073
  },
14074
+ "do_not_share_note": {
14075
+ "description": "On a grant rule: that the active do-not-share rules over an email run first, keeping from it what they match and holding back what they cannot decide, which is decided again for up to a day and shared only if none of them then matches it.",
14076
+ "type": "string"
14077
+ },
13574
14078
  "author": {
13575
14079
  "type": "object",
13576
14080
  "properties": {
@@ -13674,6 +14178,11 @@ const operationManifest = [
13674
14178
  "description": "On a rule over your mail: how many of these keys also list one of your own mailboxes, or its domain, so they read your mail there beyond what this rule matches.",
13675
14179
  "type": "integer",
13676
14180
  "minimum": 0
14181
+ },
14182
+ "newest_at": {
14183
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
14184
+ "type": "string",
14185
+ "format": "date-time"
13677
14186
  }
13678
14187
  },
13679
14188
  "required": ["owner", "count"]
@@ -13729,6 +14238,11 @@ const operationManifest = [
13729
14238
  "description": "Hosts past those listed.",
13730
14239
  "type": "integer",
13731
14240
  "minimum": 0
14241
+ },
14242
+ "newest_at": {
14243
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
14244
+ "type": "string",
14245
+ "format": "date-time"
13732
14246
  }
13733
14247
  },
13734
14248
  "required": ["owner", "count"]
@@ -13766,6 +14280,11 @@ const operationManifest = [
13766
14280
  "count": {
13767
14281
  "type": "integer",
13768
14282
  "exclusiveMinimum": 0
14283
+ },
14284
+ "newest_at": {
14285
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
14286
+ "type": "string",
14287
+ "format": "date-time"
13769
14288
  }
13770
14289
  },
13771
14290
  "required": ["beneficiary", "count"]
@@ -13830,6 +14349,11 @@ const operationManifest = [
13830
14349
  "description": "On a rule over your mail: how many of these keys also list one of your own mailboxes, or its domain, so they read your mail there beyond what this rule matches.",
13831
14350
  "type": "integer",
13832
14351
  "minimum": 0
14352
+ },
14353
+ "newest_at": {
14354
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
14355
+ "type": "string",
14356
+ "format": "date-time"
13833
14357
  }
13834
14358
  },
13835
14359
  "required": ["owner", "count"]
@@ -13885,6 +14409,11 @@ const operationManifest = [
13885
14409
  "description": "Hosts past those listed.",
13886
14410
  "type": "integer",
13887
14411
  "minimum": 0
14412
+ },
14413
+ "newest_at": {
14414
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
14415
+ "type": "string",
14416
+ "format": "date-time"
13888
14417
  }
13889
14418
  },
13890
14419
  "required": ["owner", "count"]
@@ -13922,6 +14451,11 @@ const operationManifest = [
13922
14451
  "count": {
13923
14452
  "type": "integer",
13924
14453
  "exclusiveMinimum": 0
14454
+ },
14455
+ "newest_at": {
14456
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
14457
+ "type": "string",
14458
+ "format": "date-time"
13925
14459
  }
13926
14460
  },
13927
14461
  "required": ["beneficiary", "count"]
@@ -14045,7 +14579,7 @@ const operationManifest = [
14045
14579
  "binaryResponse": false,
14046
14580
  "bodyRequired": false,
14047
14581
  "command": "get-email-rule-objection",
14048
- "description": "One objection you can see. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
14582
+ "description": "One objection you can see. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it (optionally with a reason the objector sees), or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
14049
14583
  "hasJsonBody": false,
14050
14584
  "method": "GET",
14051
14585
  "operationId": "getEmailRuleObjection",
@@ -14141,10 +14675,13 @@ const operationManifest = [
14141
14675
  "description": "Whether it is to a rule you wrote, in your current membership."
14142
14676
  },
14143
14677
  "message": {
14144
- "description": "Why the objector objects; null when none was given, or once withdrawn.",
14678
+ "description": "Why they object and what they ask (to turn the rule off, narrow it, or explain who receives it); null when none was given, or once withdrawn.",
14679
+ "type": ["string", "null"]
14680
+ },
14681
+ "decline_reason": {
14682
+ "description": "The author's answer when declining, which the objector sees; null when none was given.",
14145
14683
  "type": ["string", "null"]
14146
14684
  },
14147
- "decline_reason": { "type": ["string", "null"] },
14148
14685
  "created_at": {
14149
14686
  "type": "string",
14150
14687
  "format": "date-time"
@@ -14305,7 +14842,7 @@ const operationManifest = [
14305
14842
  "binaryResponse": false,
14306
14843
  "bodyRequired": false,
14307
14844
  "command": "list-email-rule-objections",
14308
- "description": "Objections you made and objections to rules you wrote (by default), only one of the two, or every objection you can see, newest first. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it, or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
14845
+ "description": "Objections you made and objections to rules you wrote (by default), only one of the two, or every objection you can see, newest first. An objection is seen by the member who made it, the rule's author and the organization's owners and admins. It stays pending until the rule's author accepts it, turning the rule off, or declines it (optionally with a reason the objector sees), or the member who made it withdraws it, which clears its message. It never changes after that. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
14309
14846
  "hasJsonBody": false,
14310
14847
  "method": "GET",
14311
14848
  "operationId": "listEmailRuleObjections",
@@ -14440,10 +14977,13 @@ const operationManifest = [
14440
14977
  "description": "Whether it is to a rule you wrote, in your current membership."
14441
14978
  },
14442
14979
  "message": {
14443
- "description": "Why the objector objects; null when none was given, or once withdrawn.",
14980
+ "description": "Why they object and what they ask (to turn the rule off, narrow it, or explain who receives it); null when none was given, or once withdrawn.",
14981
+ "type": ["string", "null"]
14982
+ },
14983
+ "decline_reason": {
14984
+ "description": "The author's answer when declining, which the objector sees; null when none was given.",
14444
14985
  "type": ["string", "null"]
14445
14986
  },
14446
- "decline_reason": { "type": ["string", "null"] },
14447
14987
  "created_at": {
14448
14988
  "type": "string",
14449
14989
  "format": "date-time"
@@ -14602,7 +15142,7 @@ const operationManifest = [
14602
15142
  "binaryResponse": false,
14603
15143
  "bodyRequired": false,
14604
15144
  "command": "list-email-rules-covering-me",
14605
- "description": "Every email rule that covers one of your own mailboxes now, yours included, newest first, in any state. A rule covers you when the scope it runs with now reaches one of your own mailboxes (your member mailbox, or a personal agent's address): one whose author reads all your mail (author scope, for an author who reads the organization's mail or holds an access entry for your mailbox), or one that names your mailbox or its domain (selected). Another member's own_mailboxes rule never covers you. You see its name, wording, current tree, state, kind and author, and which of your own mailboxes it reaches. Its wording and tree are shown as written, so they can name other addresses (a condition on who an email is from or to). While it covers you, you also see who receives what it matches (receivers): how many API keys, webhooks and shares, the member each belongs to (or the organization), and the sites the webhooks deliver to. You do not see which keys, webhooks or shares they are (no ids, names, prefixes, or URLs beyond the site), the other inboxes and domains its scope names, or its saved expectations. A page can hold fewer rules than limit, or none, and still carry a cursor (one request reads a bounded number of rules): keep reading until meta.cursor is null. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
15145
+ "description": "Every email rule that covers one of your own mailboxes now, yours included, newest first, in any state. A rule covers you when the scope it runs with now reaches one of your own mailboxes (your member mailbox, or a personal agent's address): one whose author reads all your mail (author scope, for an author who reads the organization's mail or holds an access entry for your mailbox), or one that names your mailbox or its domain (selected). Another member's own_mailboxes rule never covers you. You see its name, wording, current tree, state, kind and author, and which of your own mailboxes it reaches. Its wording and tree are shown as written, so they can name other addresses (a condition on who an email is from or to). While it covers you, you also see who receives what it matches (receivers): how many API keys, webhooks and shares, the member each belongs to (or the organization), and the sites the webhooks deliver to. You do not see which keys, webhooks or shares they are (no ids, names, prefixes, or URLs beyond the site), the other inboxes and domains its scope names, or its saved expectations. A page can hold fewer rules than limit, or none, and still carry a cursor (one request reads a bounded number of rules): keep reading until meta.cursor is null. For what happened to one email of yours, every rule's decision on it and, for another member's rule covering you, who receives what it shares: GET /v1/emails/{id}/rule-results (primitive email-rules list-email-rule-results --email-id <email-id>). Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token). Only on your own dashboard session or the Primitive CLI.",
14606
15146
  "hasJsonBody": false,
14607
15147
  "method": "GET",
14608
15148
  "operationId": "listEmailRulesCoveringMe",
@@ -14652,6 +15192,10 @@ const operationManifest = [
14652
15192
  "enum": ["grant", "do_not_share"],
14653
15193
  "description": "grant: what it matches can be shared. do_not_share: it blocks sharing what it matches."
14654
15194
  },
15195
+ "do_not_share_note": {
15196
+ "description": "On a grant rule: that the active do-not-share rules over an email run first, keeping from it what they match and holding back what they cannot decide, which is decided again for up to a day and shared only if none of them then matches it.",
15197
+ "type": "string"
15198
+ },
14655
15199
  "author": {
14656
15200
  "type": "object",
14657
15201
  "properties": {
@@ -14751,6 +15295,11 @@ const operationManifest = [
14751
15295
  "description": "On a rule over your mail: how many of these keys also list one of your own mailboxes, or its domain, so they read your mail there beyond what this rule matches.",
14752
15296
  "type": "integer",
14753
15297
  "minimum": 0
15298
+ },
15299
+ "newest_at": {
15300
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
15301
+ "type": "string",
15302
+ "format": "date-time"
14754
15303
  }
14755
15304
  },
14756
15305
  "required": ["owner", "count"]
@@ -14806,6 +15355,11 @@ const operationManifest = [
14806
15355
  "description": "Hosts past those listed.",
14807
15356
  "type": "integer",
14808
15357
  "minimum": 0
15358
+ },
15359
+ "newest_at": {
15360
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
15361
+ "type": "string",
15362
+ "format": "date-time"
14809
15363
  }
14810
15364
  },
14811
15365
  "required": ["owner", "count"]
@@ -14843,6 +15397,11 @@ const operationManifest = [
14843
15397
  "count": {
14844
15398
  "type": "integer",
14845
15399
  "exclusiveMinimum": 0
15400
+ },
15401
+ "newest_at": {
15402
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
15403
+ "type": "string",
15404
+ "format": "date-time"
14846
15405
  }
14847
15406
  },
14848
15407
  "required": ["beneficiary", "count"]
@@ -14907,6 +15466,11 @@ const operationManifest = [
14907
15466
  "description": "On a rule over your mail: how many of these keys also list one of your own mailboxes, or its domain, so they read your mail there beyond what this rule matches.",
14908
15467
  "type": "integer",
14909
15468
  "minimum": 0
15469
+ },
15470
+ "newest_at": {
15471
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
15472
+ "type": "string",
15473
+ "format": "date-time"
14910
15474
  }
14911
15475
  },
14912
15476
  "required": ["owner", "count"]
@@ -14962,6 +15526,11 @@ const operationManifest = [
14962
15526
  "description": "Hosts past those listed.",
14963
15527
  "type": "integer",
14964
15528
  "minimum": 0
15529
+ },
15530
+ "newest_at": {
15531
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
15532
+ "type": "string",
15533
+ "format": "date-time"
14965
15534
  }
14966
15535
  },
14967
15536
  "required": ["owner", "count"]
@@ -14999,6 +15568,11 @@ const operationManifest = [
14999
15568
  "count": {
15000
15569
  "type": "integer",
15001
15570
  "exclusiveMinimum": 0
15571
+ },
15572
+ "newest_at": {
15573
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
15574
+ "type": "string",
15575
+ "format": "date-time"
15002
15576
  }
15003
15577
  },
15004
15578
  "required": ["beneficiary", "count"]
@@ -15145,10 +15719,13 @@ const operationManifest = [
15145
15719
  "description": "Whether it is to a rule you wrote, in your current membership."
15146
15720
  },
15147
15721
  "message": {
15148
- "description": "Why the objector objects; null when none was given, or once withdrawn.",
15722
+ "description": "Why they object and what they ask (to turn the rule off, narrow it, or explain who receives it); null when none was given, or once withdrawn.",
15723
+ "type": ["string", "null"]
15724
+ },
15725
+ "decline_reason": {
15726
+ "description": "The author's answer when declining, which the objector sees; null when none was given.",
15149
15727
  "type": ["string", "null"]
15150
15728
  },
15151
- "decline_reason": { "type": ["string", "null"] },
15152
15729
  "created_at": {
15153
15730
  "type": "string",
15154
15731
  "format": "date-time"
@@ -15323,6 +15900,44 @@ const operationManifest = [
15323
15900
  "required": ["checked", "matched"]
15324
15901
  }, { "type": "null" }]
15325
15902
  },
15903
+ "breadth_scope": {
15904
+ "type": "object",
15905
+ "properties": {
15906
+ "evaluation_scope": {
15907
+ "type": "string",
15908
+ "enum": [
15909
+ "author",
15910
+ "own_mailboxes",
15911
+ "selected"
15912
+ ],
15913
+ "description": "author, own_mailboxes or selected, as a rule's evaluation_scope."
15914
+ },
15915
+ "scope_selection": {
15916
+ "type": ["object", "null"],
15917
+ "properties": {
15918
+ "addresses": {
15919
+ "maxItems": 100,
15920
+ "type": "array",
15921
+ "items": {
15922
+ "type": "string",
15923
+ "maxLength": 320
15924
+ }
15925
+ },
15926
+ "domains": {
15927
+ "maxItems": 50,
15928
+ "type": "array",
15929
+ "items": {
15930
+ "type": "string",
15931
+ "maxLength": 253
15932
+ }
15933
+ }
15934
+ },
15935
+ "required": ["addresses", "domains"],
15936
+ "description": "What a selected scope names; null for any other."
15937
+ }
15938
+ },
15939
+ "required": ["evaluation_scope", "scope_selection"]
15940
+ },
15326
15941
  "kind": {
15327
15942
  "description": "Only grant.",
15328
15943
  "type": "string",
@@ -15612,7 +16227,7 @@ const operationManifest = [
15612
16227
  "type": ["boolean", "null"]
15613
16228
  },
15614
16229
  "notice": {
15615
- "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, then what accepting would share. For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
16230
+ "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, what accepting would share, then where to see what becomes of one email (rule-results). For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
15616
16231
  "type": ["string", "null"]
15617
16232
  }
15618
16233
  },
@@ -15735,7 +16350,7 @@ const operationManifest = [
15735
16350
  "minimum": 0
15736
16351
  },
15737
16352
  "objection_notice": {
15738
- "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
16353
+ "description": "With pending_objections above 0: that members objected (each message says what they ask: to turn it off, narrow it or explain it), and how to answer.",
15739
16354
  "type": "string"
15740
16355
  },
15741
16356
  "created_at": {
@@ -16005,7 +16620,7 @@ const operationManifest = [
16005
16620
  ]
16006
16621
  },
16007
16622
  "notice": {
16008
- "description": "What is now shared, in words. When you changed the rule and it can now match mail the request left out by who it was sent to (such as mail where an address is only copied), that comes first.",
16623
+ "description": "What is now shared, in words. When you changed the rule and it can now match mail the request left out by who it was sent to (such as mail where an address is only copied), that comes first; when you changed its english, tree or explanation (edited_on_accept; a new name alone is no change), that rule.current is what you agreed to and request what was asked.",
16009
16624
  "type": "string"
16010
16625
  }
16011
16626
  },
@@ -16274,6 +16889,44 @@ const operationManifest = [
16274
16889
  "required": ["checked", "matched"]
16275
16890
  }, { "type": "null" }]
16276
16891
  },
16892
+ "breadth_scope": {
16893
+ "type": "object",
16894
+ "properties": {
16895
+ "evaluation_scope": {
16896
+ "type": "string",
16897
+ "enum": [
16898
+ "author",
16899
+ "own_mailboxes",
16900
+ "selected"
16901
+ ],
16902
+ "description": "author, own_mailboxes or selected, as a rule's evaluation_scope."
16903
+ },
16904
+ "scope_selection": {
16905
+ "type": ["object", "null"],
16906
+ "properties": {
16907
+ "addresses": {
16908
+ "maxItems": 100,
16909
+ "type": "array",
16910
+ "items": {
16911
+ "type": "string",
16912
+ "maxLength": 320
16913
+ }
16914
+ },
16915
+ "domains": {
16916
+ "maxItems": 50,
16917
+ "type": "array",
16918
+ "items": {
16919
+ "type": "string",
16920
+ "maxLength": 253
16921
+ }
16922
+ }
16923
+ },
16924
+ "required": ["addresses", "domains"],
16925
+ "description": "What a selected scope names; null for any other."
16926
+ }
16927
+ },
16928
+ "required": ["evaluation_scope", "scope_selection"]
16929
+ },
16277
16930
  "sql": {
16278
16931
  "description": "Present only when include_sql was true.",
16279
16932
  "type": "object",
@@ -16627,6 +17280,44 @@ const operationManifest = [
16627
17280
  },
16628
17281
  "required": ["checked", "matched"]
16629
17282
  }, { "type": "null" }]
17283
+ },
17284
+ "breadth_scope": {
17285
+ "type": "object",
17286
+ "properties": {
17287
+ "evaluation_scope": {
17288
+ "type": "string",
17289
+ "enum": [
17290
+ "author",
17291
+ "own_mailboxes",
17292
+ "selected"
17293
+ ],
17294
+ "description": "author, own_mailboxes or selected, as a rule's evaluation_scope."
17295
+ },
17296
+ "scope_selection": {
17297
+ "type": ["object", "null"],
17298
+ "properties": {
17299
+ "addresses": {
17300
+ "maxItems": 100,
17301
+ "type": "array",
17302
+ "items": {
17303
+ "type": "string",
17304
+ "maxLength": 320
17305
+ }
17306
+ },
17307
+ "domains": {
17308
+ "maxItems": 50,
17309
+ "type": "array",
17310
+ "items": {
17311
+ "type": "string",
17312
+ "maxLength": 253
17313
+ }
17314
+ }
17315
+ },
17316
+ "required": ["addresses", "domains"],
17317
+ "description": "What a selected scope names; null for any other."
17318
+ }
17319
+ },
17320
+ "required": ["evaluation_scope", "scope_selection"]
16630
17321
  }
16631
17322
  },
16632
17323
  "required": [
@@ -16732,7 +17423,7 @@ const operationManifest = [
16732
17423
  "minimum": 0
16733
17424
  },
16734
17425
  "objection_notice": {
16735
- "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
17426
+ "description": "With pending_objections above 0: that members objected (each message says what they ask: to turn it off, narrow it or explain it), and how to answer.",
16736
17427
  "type": "string"
16737
17428
  },
16738
17429
  "created_at": {
@@ -16861,6 +17552,10 @@ const operationManifest = [
16861
17552
  ]
16862
17553
  }
16863
17554
  },
17555
+ "do_not_share_note": {
17556
+ "description": "On a grant rule's create answer: that active do-not-share rules reaching an email run first: one that matches it keeps it from being shared through this rule, and one that cannot decide it holds it back while it is decided again for up to a day; it is shared through this rule only if none of them then matches it.",
17557
+ "type": "string"
17558
+ },
16864
17559
  "expectations": {
16865
17560
  "description": "Its saved expectations, each list by email id, as stored: on every create, and on an update that set or removed them.",
16866
17561
  "type": "object",
@@ -17121,7 +17816,7 @@ const operationManifest = [
17121
17816
  "binaryResponse": false,
17122
17817
  "bodyRequired": true,
17123
17818
  "command": "create-email-rule-request",
17124
- "description": "Asks another member of the organization for a rule over their own mail, from a compile response or a tree you wrote. Name them by user id, or by their member mailbox address, or by their verified work email if you can list members (at most 30 lookups by address an hour). Nothing is evaluated or shared until they accept, and they may change the rule first. A message (up to 500 characters, fixed once sent) tells them why you ask. At most 5 pending requests to one member and 50 in all. With members:read the answer says whether they can accept it under their role (target_can_accept, target_warning); POST /v1/email-rule-requests/target-check checks first. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
17819
+ "description": "Asks another member of the organization for a rule over their own mail, from a compile response or a tree you wrote. Name them by user id, or by their member mailbox address, or by their verified work email if you can list members (at most 30 lookups by address an hour). Nothing is evaluated or shared until they accept, and they may change the rule first. A message (up to 500 characters, fixed once sent) tells them why you ask. With an Idempotency-Key, asking again for exactly what a pending request of yours to the same member asks (name, english, tree, explanation and message, naming them the same way) returns that request with existing: true (200), so a retry never asks them twice. At most 5 pending requests to one member and 50 in all. With members:read the answer says whether they can accept it under their role (target_can_accept, target_warning); POST /v1/email-rule-requests/target-check checks first. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
17125
17820
  "hasJsonBody": true,
17126
17821
  "method": "POST",
17127
17822
  "operationId": "createEmailRuleRequest",
@@ -17129,7 +17824,7 @@ const operationManifest = [
17129
17824
  "pathParams": [],
17130
17825
  "queryParams": [],
17131
17826
  "headerParams": [{
17132
- "description": "Optional, and not used to deduplicate: this operation stores no result by the key, so a retry with the same key runs the request again. Operations that need a credential refuse a key that is empty, longer than 255 characters or not printable ASCII with 400 `validation_error` and echo a valid one in the Idempotency-Key response header; operations that need none ignore it.",
17827
+ "description": "Optional. With a key, a create identical to a pending request of yours to the same member (name, english, tree, explanation and message, naming the member the same way) returns that request with existing: true (200) instead of making a second one. The key itself is not stored, so any key does this, and a create without one is never matched to an existing request. A key that is empty, longer than 255 characters or not printable ASCII is refused with 400 `validation_error`; a valid one is echoed in the Idempotency-Key response header.",
17133
17828
  "enum": null,
17134
17829
  "name": "Idempotency-Key",
17135
17830
  "required": false,
@@ -17270,6 +17965,44 @@ const operationManifest = [
17270
17965
  "required": ["checked", "matched"]
17271
17966
  }, { "type": "null" }]
17272
17967
  },
17968
+ "breadth_scope": {
17969
+ "type": "object",
17970
+ "properties": {
17971
+ "evaluation_scope": {
17972
+ "type": "string",
17973
+ "enum": [
17974
+ "author",
17975
+ "own_mailboxes",
17976
+ "selected"
17977
+ ],
17978
+ "description": "author, own_mailboxes or selected, as a rule's evaluation_scope."
17979
+ },
17980
+ "scope_selection": {
17981
+ "type": ["object", "null"],
17982
+ "properties": {
17983
+ "addresses": {
17984
+ "maxItems": 100,
17985
+ "type": "array",
17986
+ "items": {
17987
+ "type": "string",
17988
+ "maxLength": 320
17989
+ }
17990
+ },
17991
+ "domains": {
17992
+ "maxItems": 50,
17993
+ "type": "array",
17994
+ "items": {
17995
+ "type": "string",
17996
+ "maxLength": 253
17997
+ }
17998
+ }
17999
+ },
18000
+ "required": ["addresses", "domains"],
18001
+ "description": "What a selected scope names; null for any other."
18002
+ }
18003
+ },
18004
+ "required": ["evaluation_scope", "scope_selection"]
18005
+ },
17273
18006
  "kind": {
17274
18007
  "description": "Only grant.",
17275
18008
  "type": "string",
@@ -17445,7 +18178,7 @@ const operationManifest = [
17445
18178
  "type": ["boolean", "null"]
17446
18179
  },
17447
18180
  "notice": {
17448
- "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, then what accepting would share. For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
18181
+ "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, what accepting would share, then where to see what becomes of one email (rule-results). For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
17449
18182
  "type": ["string", "null"]
17450
18183
  },
17451
18184
  "target_can_accept": {
@@ -17455,6 +18188,11 @@ const operationManifest = [
17455
18188
  "target_warning": {
17456
18189
  "description": "Why the member asked cannot accept it yet, naming the permission their role lacks. The request is still sent. Null when they can, or when target_can_accept is null.",
17457
18190
  "type": ["string", "null"]
18191
+ },
18192
+ "existing": {
18193
+ "description": "Present, as true, when the create carried an Idempotency-Key and you already had a pending request to this member asking exactly this (name, english, tree, explanation and message, naming the member the same way): that request is returned (200), and no second one is made or sent.",
18194
+ "type": "boolean",
18195
+ "const": true
17458
18196
  }
17459
18197
  },
17460
18198
  "required": [
@@ -17683,7 +18421,7 @@ const operationManifest = [
17683
18421
  "type": ["boolean", "null"]
17684
18422
  },
17685
18423
  "notice": {
17686
- "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, then what accepting would share. For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
18424
+ "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, what accepting would share, then where to see what becomes of one email (rule-results). For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
17687
18425
  "type": ["string", "null"]
17688
18426
  }
17689
18427
  },
@@ -17957,7 +18695,7 @@ const operationManifest = [
17957
18695
  "format": "uuid"
17958
18696
  },
17959
18697
  "kind": {
17960
- "description": "For an unsaved tree: do_not_share tests it as a rule that blocks sharing, so an email it cannot decide counts as matched. A saved rule uses its own.",
18698
+ "description": "For an unsaved tree: do_not_share tests it as a rule that blocks sharing, so an email it cannot decide counts as matched. A saved rule or a request uses its own.",
17961
18699
  "type": "string",
17962
18700
  "enum": ["grant", "do_not_share"]
17963
18701
  },
@@ -18308,7 +19046,7 @@ const operationManifest = [
18308
19046
  "minimum": 0
18309
19047
  },
18310
19048
  "objection_notice": {
18311
- "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
19049
+ "description": "With pending_objections above 0: that members objected (each message says what they ask: to turn it off, narrow it or explain it), and how to answer.",
18312
19050
  "type": "string"
18313
19051
  },
18314
19052
  "created_at": {
@@ -18745,7 +19483,7 @@ const operationManifest = [
18745
19483
  "type": ["boolean", "null"]
18746
19484
  },
18747
19485
  "notice": {
18748
- "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, then what accepting would share. For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
19486
+ "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, what accepting would share, then where to see what becomes of one email (rule-results). For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
18749
19487
  "type": ["string", "null"]
18750
19488
  }
18751
19489
  },
@@ -19644,7 +20382,7 @@ const operationManifest = [
19644
20382
  "type": ["boolean", "null"]
19645
20383
  },
19646
20384
  "notice": {
19647
- "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, then what accepting would share. For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
20385
+ "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, what accepting would share, then where to see what becomes of one email (rule-results). For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
19648
20386
  "type": ["string", "null"]
19649
20387
  }
19650
20388
  },
@@ -19899,9 +20637,10 @@ const operationManifest = [
19899
20637
  "enum": [
19900
20638
  "draft",
19901
20639
  "created_after_email",
20640
+ "outside_scope",
19902
20641
  null
19903
20642
  ],
19904
- "description": "What is known about a rule with no result on this email. draft: the rule is a draft now. created_after_email: the rule was created after the email arrived. Null when it has a result, or when nothing is known."
20643
+ "description": "What is known about a rule with no result on this email. draft: the rule is a draft now. created_after_email: the rule was created after the email arrived. outside_scope: the mailbox this email is in is outside what its scope names (its author's own mailboxes, its selection, or all its author reads now), so it does not decide on it. Null when it has a result, or when nothing is known (it may not have decided yet)."
19905
20644
  },
19906
20645
  "author_left": {
19907
20646
  "description": "On a do-not-share rule whose author is no longer a member of the organization: it blocks nothing now, whatever it decided. Absent otherwise.",
@@ -19984,6 +20723,11 @@ const operationManifest = [
19984
20723
  "description": "On a rule over your mail: how many of these keys also list one of your own mailboxes, or its domain, so they read your mail there beyond what this rule matches.",
19985
20724
  "type": "integer",
19986
20725
  "minimum": 0
20726
+ },
20727
+ "newest_at": {
20728
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
20729
+ "type": "string",
20730
+ "format": "date-time"
19987
20731
  }
19988
20732
  },
19989
20733
  "required": ["owner", "count"]
@@ -20039,6 +20783,11 @@ const operationManifest = [
20039
20783
  "description": "Hosts past those listed.",
20040
20784
  "type": "integer",
20041
20785
  "minimum": 0
20786
+ },
20787
+ "newest_at": {
20788
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
20789
+ "type": "string",
20790
+ "format": "date-time"
20042
20791
  }
20043
20792
  },
20044
20793
  "required": ["owner", "count"]
@@ -20076,6 +20825,11 @@ const operationManifest = [
20076
20825
  "count": {
20077
20826
  "type": "integer",
20078
20827
  "exclusiveMinimum": 0
20828
+ },
20829
+ "newest_at": {
20830
+ "description": "When the newest of these was made, to the minute: a key, a webhook or the share, or the rule (the share, for what reaches it only through the share) if that was later. Not when a key was given access to this rule, or anything turned back on; a rotated key shows as newly made, and one key on two rules can show the same minute under both.",
20831
+ "type": "string",
20832
+ "format": "date-time"
20079
20833
  }
20080
20834
  },
20081
20835
  "required": ["beneficiary", "count"]
@@ -21081,7 +21835,7 @@ const operationManifest = [
21081
21835
  "minimum": 0
21082
21836
  },
21083
21837
  "objection_notice": {
21084
- "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
21838
+ "description": "With pending_objections above 0: that members objected (each message says what they ask: to turn it off, narrow it or explain it), and how to answer.",
21085
21839
  "type": "string"
21086
21840
  },
21087
21841
  "created_at": {
@@ -21324,7 +22078,7 @@ const operationManifest = [
21324
22078
  "binaryResponse": false,
21325
22079
  "bodyRequired": true,
21326
22080
  "command": "test-email-rule",
21327
- "description": "Runs a rule over received mail and reports what it would decide for each email (matched, near_miss, no_match, unclassified, skipped or not_found), with a summary of how each top-level condition and question decided. Test an unsaved tree, or a saved rule by rule_id (its current version, as its author). It reads only mail its author can read and its scope covers. A rule you see only because it covers your own mailboxes runs as you, over only those, from the dashboard or the Primitive CLI. Without email_ids it tests the newest emails, at most 200 and none older than 30 days, plus any expected ones. Unclassified emails over 2 hours old are classified first unless classify_missing is false. It records no decision and grants nothing, and stops at about 90 seconds. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22081
+ "description": "Runs a rule over received mail and reports what it would decide for each email (matched, near_miss, no_match, unclassified, skipped or not_found), with a summary of how each top-level condition and question decided. Test an unsaved tree, a saved rule by rule_id (its current version, as its author), or a pending rule request by request_id (its rule as asked, as you, over your own mailboxes). It reads only mail its author can read and its scope covers. A rule you see only because it covers your own mailboxes runs as you, over only those, from the dashboard or the Primitive CLI. Without email_ids it tests the newest emails, at most 200 and none older than 30 days, plus any expected ones. Unclassified emails over 2 hours old are classified first unless classify_missing is false. It records no decision and grants nothing, and stops at about 90 seconds. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
21328
22082
  "hasJsonBody": true,
21329
22083
  "method": "POST",
21330
22084
  "operationId": "testEmailRule",
@@ -21414,8 +22168,13 @@ const operationManifest = [
21414
22168
  "type": "string",
21415
22169
  "format": "uuid"
21416
22170
  },
22171
+ "request_id": {
22172
+ "description": "A pending rule request you can see: its rule as asked, tested as you over your own mailboxes (what accepting would share, for the member asked). Give one of tree, rule_id and request_id.",
22173
+ "type": "string",
22174
+ "format": "uuid"
22175
+ },
21417
22176
  "kind": {
21418
- "description": "For an unsaved tree: do_not_share tests it as a rule that blocks sharing, so an email it cannot decide counts as matched. A saved rule uses its own.",
22177
+ "description": "For an unsaved tree: do_not_share tests it as a rule that blocks sharing, so an email it cannot decide counts as matched. A saved rule or a request uses its own.",
21419
22178
  "type": "string",
21420
22179
  "enum": ["grant", "do_not_share"]
21421
22180
  },
@@ -21620,6 +22379,54 @@ const operationManifest = [
21620
22379
  "type": "integer",
21621
22380
  "minimum": 0,
21622
22381
  "description": "The most emails the recent window holds: 200, less one place per expected email."
22382
+ },
22383
+ "scope": {
22384
+ "type": "object",
22385
+ "properties": {
22386
+ "evaluation_scope": {
22387
+ "type": "string",
22388
+ "enum": [
22389
+ "author",
22390
+ "own_mailboxes",
22391
+ "selected"
22392
+ ],
22393
+ "description": "author, own_mailboxes or selected, as a rule's evaluation_scope."
22394
+ },
22395
+ "scope_selection": {
22396
+ "type": ["object", "null"],
22397
+ "properties": {
22398
+ "addresses": {
22399
+ "maxItems": 100,
22400
+ "type": "array",
22401
+ "items": {
22402
+ "type": "string",
22403
+ "maxLength": 320
22404
+ }
22405
+ },
22406
+ "domains": {
22407
+ "maxItems": 50,
22408
+ "type": "array",
22409
+ "items": {
22410
+ "type": "string",
22411
+ "maxLength": 253
22412
+ }
22413
+ }
22414
+ },
22415
+ "required": ["addresses", "domains"],
22416
+ "description": "What a selected scope names; null for any other."
22417
+ }
22418
+ },
22419
+ "required": ["evaluation_scope", "scope_selection"]
22420
+ },
22421
+ "oldest_at": {
22422
+ "type": ["string", "null"],
22423
+ "format": "date-time",
22424
+ "description": "When the oldest email tested arrived, expected ones included; null when none was. The window is the newest 200 the scope reaches, so a narrower scope reaches further back: compare counts only over the same scope and span."
22425
+ },
22426
+ "newest_at": {
22427
+ "type": ["string", "null"],
22428
+ "format": "date-time",
22429
+ "description": "When the newest email tested arrived; null when none was."
21623
22430
  }
21624
22431
  },
21625
22432
  "required": [
@@ -22126,7 +22933,7 @@ const operationManifest = [
22126
22933
  "binaryResponse": false,
22127
22934
  "bodyRequired": true,
22128
22935
  "command": "test-email-rule-on-sample",
22129
- "description": "Reports what a rule would decide for an email you describe (sender, recipients, subject, plain text, attachment types and file names; optionally automated, from a mailing list, and a spam score), classified and decided as received mail would be. A rule reading a spam score you gave none for decides on none (unset_columns). Nothing is stored. Test an unsaved tree or a saved rule by rule_id. The answer includes what the classifier read. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22936
+ "description": "Reports what a rule would decide for an email you describe (sender, recipients, subject, plain text, attachment types and file names; optionally automated, from a mailing list, and a spam score), classified and decided as received mail would be. A rule reading a spam score you gave none for decides on none (unset_columns). Nothing is stored. Test an unsaved tree, a saved rule by rule_id, or a pending rule request by request_id (its rule as asked, as you). The answer includes what the classifier read. Needs mail:read, the email rules feature, and a member's own full-access credential (no API key, agent key or narrowed token).",
22130
22937
  "hasJsonBody": true,
22131
22938
  "method": "POST",
22132
22939
  "operationId": "testEmailRuleOnSample",
@@ -22216,8 +23023,13 @@ const operationManifest = [
22216
23023
  "type": "string",
22217
23024
  "format": "uuid"
22218
23025
  },
23026
+ "request_id": {
23027
+ "description": "A pending rule request you can see: its rule as asked, tested as you over your own mailboxes (what accepting would share, for the member asked). Give one of tree, rule_id and request_id.",
23028
+ "type": "string",
23029
+ "format": "uuid"
23030
+ },
22219
23031
  "kind": {
22220
- "description": "For an unsaved tree: do_not_share tests it as a rule that blocks sharing, so an email it cannot decide counts as matched. A saved rule uses its own.",
23032
+ "description": "For an unsaved tree: do_not_share tests it as a rule that blocks sharing, so an email it cannot decide counts as matched. A saved rule or a request uses its own.",
22221
23033
  "type": "string",
22222
23034
  "enum": ["grant", "do_not_share"]
22223
23035
  },
@@ -22316,6 +23128,10 @@ const operationManifest = [
22316
23128
  ],
22317
23129
  "additionalProperties": false,
22318
23130
  "description": "A made-up received email. Nothing about it is stored."
23131
+ },
23132
+ "with_protection": {
23133
+ "description": "With true, a grant rule that matches is also judged against the active do-not-share rules over the first recipient's mailbox, which you must read in full and the rule must run over; see protection. Limited to 20 an hour per member.",
23134
+ "type": "boolean"
22319
23135
  }
22320
23136
  },
22321
23137
  "required": ["email"],
@@ -22472,6 +23288,59 @@ const operationManifest = [
22472
23288
  "required": ["value", "confidence"]
22473
23289
  },
22474
23290
  "description": "What the classifier read from the email, by fact: its value and confidence."
23291
+ },
23292
+ "protection": {
23293
+ "description": "With with_protection: whether a match would be shared. Nothing is stored.",
23294
+ "type": "object",
23295
+ "properties": {
23296
+ "mailbox": {
23297
+ "type": "string",
23298
+ "description": "The mailbox judged: the first recipient."
23299
+ },
23300
+ "outcome": {
23301
+ "type": "string",
23302
+ "enum": [
23303
+ "shared",
23304
+ "withheld",
23305
+ "not_matched"
23306
+ ],
23307
+ "description": "not_matched: the rule did not match, so nothing would be shared and no do-not-share rule was judged."
23308
+ },
23309
+ "withheld_by": {
23310
+ "type": "array",
23311
+ "items": {
23312
+ "type": "object",
23313
+ "properties": {
23314
+ "id": {
23315
+ "type": "string",
23316
+ "format": "uuid"
23317
+ },
23318
+ "name": { "type": "string" },
23319
+ "reason": {
23320
+ "type": "string",
23321
+ "enum": ["matched", "undecided"]
23322
+ }
23323
+ },
23324
+ "required": [
23325
+ "id",
23326
+ "name",
23327
+ "reason"
23328
+ ]
23329
+ },
23330
+ "description": "Do-not-share rules you can see that would keep it from being shared. undecided: left undecided as real mail would leave it, or not reached by this test (past 50 rules, 10 rules' questions, or the time limit); it is held back either way."
23331
+ },
23332
+ "withheld_by_hidden": {
23333
+ "type": "integer",
23334
+ "minimum": 0,
23335
+ "description": "Rules you cannot see that would."
23336
+ }
23337
+ },
23338
+ "required": [
23339
+ "mailbox",
23340
+ "outcome",
23341
+ "withheld_by",
23342
+ "withheld_by_hidden"
23343
+ ]
22475
23344
  }
22476
23345
  },
22477
23346
  "required": [
@@ -22633,6 +23502,44 @@ const operationManifest = [
22633
23502
  "required": ["checked", "matched"]
22634
23503
  }, { "type": "null" }]
22635
23504
  },
23505
+ "breadth_scope": {
23506
+ "type": "object",
23507
+ "properties": {
23508
+ "evaluation_scope": {
23509
+ "type": "string",
23510
+ "enum": [
23511
+ "author",
23512
+ "own_mailboxes",
23513
+ "selected"
23514
+ ],
23515
+ "description": "author, own_mailboxes or selected, as a rule's evaluation_scope."
23516
+ },
23517
+ "scope_selection": {
23518
+ "type": ["object", "null"],
23519
+ "properties": {
23520
+ "addresses": {
23521
+ "maxItems": 100,
23522
+ "type": "array",
23523
+ "items": {
23524
+ "type": "string",
23525
+ "maxLength": 320
23526
+ }
23527
+ },
23528
+ "domains": {
23529
+ "maxItems": 50,
23530
+ "type": "array",
23531
+ "items": {
23532
+ "type": "string",
23533
+ "maxLength": 253
23534
+ }
23535
+ }
23536
+ },
23537
+ "required": ["addresses", "domains"],
23538
+ "description": "What a selected scope names; null for any other."
23539
+ }
23540
+ },
23541
+ "required": ["evaluation_scope", "scope_selection"]
23542
+ },
22636
23543
  "kind": {
22637
23544
  "description": "Must be the rule's own (400 kind_mismatch).",
22638
23545
  "type": "string",
@@ -23048,7 +23955,7 @@ const operationManifest = [
23048
23955
  "minimum": 0
23049
23956
  },
23050
23957
  "objection_notice": {
23051
- "description": "With pending_objections above 0: that members asked you to turn it off, and how to answer.",
23958
+ "description": "With pending_objections above 0: that members objected (each message says what they ask: to turn it off, narrow it or explain it), and how to answer.",
23052
23959
  "type": "string"
23053
23960
  },
23054
23961
  "created_at": {
@@ -23177,6 +24084,10 @@ const operationManifest = [
23177
24084
  ]
23178
24085
  }
23179
24086
  },
24087
+ "do_not_share_note": {
24088
+ "description": "On a grant rule's create answer: that active do-not-share rules reaching an email run first: one that matches it keeps it from being shared through this rule, and one that cannot decide it holds it back while it is decided again for up to a day; it is shared through this rule only if none of them then matches it.",
24089
+ "type": "string"
24090
+ },
23180
24091
  "expectations": {
23181
24092
  "description": "Its saved expectations, each list by email id, as stored: on every create, and on an update that set or removed them.",
23182
24093
  "type": "object",
@@ -23622,7 +24533,7 @@ const operationManifest = [
23622
24533
  "type": ["boolean", "null"]
23623
24534
  },
23624
24535
  "notice": {
23625
- "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, then what accepting would share. For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
24536
+ "description": "For the member asked, while pending: what the rule actually does (server_explanation), Primitive's warnings, what accepting would share, then where to see what becomes of one email (rule-results). For the member who asked, once accepted with changes: that the request is not what is shared. Else null.",
23626
24537
  "type": ["string", "null"]
23627
24538
  }
23628
24539
  },
@@ -24340,16 +25251,27 @@ const operationManifest = [
24340
25251
  null
24341
25252
  ]
24342
25253
  },
24343
- "webhook_attempt_count": { "type": "integer" },
25254
+ "webhook_attempt_count": {
25255
+ "type": "integer",
25256
+ "description": "The email's own routed delivery only (where routing sent it); a rule's deliveries never change this, and are in `rule_deliveries` on GET /emails/{id} for the mailbox's owner. Its attempts."
25257
+ },
24344
25258
  "webhook_last_attempt_at": {
24345
25259
  "type": ["string", "null"],
24346
- "format": "date-time"
25260
+ "format": "date-time",
25261
+ "description": "The email's own routed delivery only (where routing sent it); a rule's deliveries never change this, and are in `rule_deliveries` on GET /emails/{id} for the mailbox's owner. Its last attempt."
25262
+ },
25263
+ "webhook_last_status_code": {
25264
+ "type": ["integer", "null"],
25265
+ "description": "The email's own routed delivery only (where routing sent it); a rule's deliveries never change this, and are in `rule_deliveries` on GET /emails/{id} for the mailbox's owner. What it last answered."
25266
+ },
25267
+ "webhook_last_error": {
25268
+ "type": ["string", "null"],
25269
+ "description": "The email's own routed delivery only (where routing sent it); a rule's deliveries never change this, and are in `rule_deliveries` on GET /emails/{id} for the mailbox's owner. Its last error."
24347
25270
  },
24348
- "webhook_last_status_code": { "type": ["integer", "null"] },
24349
- "webhook_last_error": { "type": ["string", "null"] },
24350
25271
  "webhook_fired_at": {
24351
25272
  "type": ["string", "null"],
24352
- "format": "date-time"
25273
+ "format": "date-time",
25274
+ "description": "The email's own routed delivery only (where routing sent it); a rule's deliveries never change this, and are in `rule_deliveries` on GET /emails/{id} for the mailbox's owner. When it was delivered."
24353
25275
  },
24354
25276
  "smtp_helo": { "type": ["string", "null"] },
24355
25277
  "smtp_mail_from": {
@@ -24903,6 +25825,81 @@ const operationManifest = [
24903
25825
  }
24904
25826
  }
24905
25827
  },
25828
+ "rule_deliveries": {
25829
+ "type": ["object", "null"],
25830
+ "description": "Where Email Rules delivered this email by webhook, for the owner of the mailbox it is at, on their own dashboard session or the Primitive CLI. Null for everyone else, and when it could not be read in time. A key reading it through a rule is not a delivery.",
25831
+ "required": [
25832
+ "rules",
25833
+ "hidden",
25834
+ "complete"
25835
+ ],
25836
+ "properties": {
25837
+ "rules": {
25838
+ "type": "array",
25839
+ "description": "Each rule over your mail that queued a webhook delivery of this email, newest rule first.",
25840
+ "items": {
25841
+ "type": "object",
25842
+ "required": [
25843
+ "rule_id",
25844
+ "name",
25845
+ "delivered",
25846
+ "pending",
25847
+ "failed",
25848
+ "not_sent",
25849
+ "hosts",
25850
+ "hosts_not_listed"
25851
+ ],
25852
+ "properties": {
25853
+ "rule_id": {
25854
+ "type": "string",
25855
+ "format": "uuid",
25856
+ "description": "The rule. A webhook sent it through several rules counts under one."
25857
+ },
25858
+ "name": { "type": "string" },
25859
+ "delivered": {
25860
+ "type": "integer",
25861
+ "minimum": 0,
25862
+ "description": "Webhooks it was delivered to."
25863
+ },
25864
+ "pending": {
25865
+ "type": "integer",
25866
+ "minimum": 0,
25867
+ "description": "Webhooks still owed it."
25868
+ },
25869
+ "failed": {
25870
+ "type": "integer",
25871
+ "minimum": 0,
25872
+ "description": "Webhooks whose attempts gave up."
25873
+ },
25874
+ "not_sent": {
25875
+ "type": "integer",
25876
+ "minimum": 0,
25877
+ "description": "Webhooks it was not sent to after all."
25878
+ },
25879
+ "hosts": {
25880
+ "type": "array",
25881
+ "items": { "type": "string" },
25882
+ "description": "The sites it was delivered to at the time, named as the covering view names sites; they may differ from the rule's current receivers there."
25883
+ },
25884
+ "hosts_not_listed": {
25885
+ "type": "integer",
25886
+ "minimum": 0,
25887
+ "description": "Sites past the listed ones."
25888
+ }
25889
+ }
25890
+ }
25891
+ },
25892
+ "hidden": {
25893
+ "type": "integer",
25894
+ "minimum": 0,
25895
+ "description": "Rules that queued a delivery of it that are not listed: they do not cover your mailboxes now, or are past the 50 one answer judges."
25896
+ },
25897
+ "complete": {
25898
+ "type": "boolean",
25899
+ "description": "False when a delivery may be missing: the email is older than 14 days, or it has more deliveries or rules than one answer reads."
25900
+ }
25901
+ }
25902
+ },
24906
25903
  "from_name": {
24907
25904
  "type": ["string", "null"],
24908
25905
  "description": "Display name from the `From:` header, decoded for display: RFC 2047 encoded words are decoded in their declared charset and folded lines are joined (e.g. `Dana Rúiz` for `=?UTF-8?Q?Dana_R=C3=BAiz?= <dana@example.com>`). Null when the header carries no display name. For display only: identify the sender by `from_email`, never by this name, which the sender chooses freely."
@@ -25798,7 +26795,10 @@ const operationManifest = [
25798
26795
  null
25799
26796
  ]
25800
26797
  },
25801
- "webhook_attempt_count": { "type": "integer" },
26798
+ "webhook_attempt_count": {
26799
+ "type": "integer",
26800
+ "description": "The email's own routed delivery only (where routing sent it); a rule's deliveries never change this, and are in `rule_deliveries` on GET /emails/{id} for the mailbox's owner. Its attempts."
26801
+ },
25802
26802
  "thread_id": {
25803
26803
  "type": ["string", "null"],
25804
26804
  "format": "uuid",
@@ -26533,7 +27533,10 @@ const operationManifest = [
26533
27533
  null
26534
27534
  ]
26535
27535
  },
26536
- "webhook_attempt_count": { "type": "integer" },
27536
+ "webhook_attempt_count": {
27537
+ "type": "integer",
27538
+ "description": "The email's own routed delivery only (where routing sent it); a rule's deliveries never change this, and are in `rule_deliveries` on GET /emails/{id} for the mailbox's owner. Its attempts."
27539
+ },
26537
27540
  "thread_id": {
26538
27541
  "type": ["string", "null"],
26539
27542
  "format": "uuid",
@@ -36707,7 +37710,7 @@ const operationManifest = [
36707
37710
  "pathParams": [],
36708
37711
  "queryParams": [],
36709
37712
  "headerParams": [{
36710
- "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo or semantic search setting), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
37713
+ "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
36711
37714
  "enum": null,
36712
37715
  "name": "Primitive-Step-Up",
36713
37716
  "required": false,
@@ -36853,7 +37856,7 @@ const operationManifest = [
36853
37856
  "pathParams": [],
36854
37857
  "queryParams": [],
36855
37858
  "headerParams": [{
36856
- "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo or semantic search setting), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
37859
+ "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
36857
37860
  "enum": null,
36858
37861
  "name": "Primitive-Step-Up",
36859
37862
  "required": false,
@@ -36902,7 +37905,7 @@ const operationManifest = [
36902
37905
  "pathParams": [],
36903
37906
  "queryParams": [],
36904
37907
  "headerParams": [{
36905
- "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo or semantic search setting), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
37908
+ "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
36906
37909
  "enum": null,
36907
37910
  "name": "Primitive-Step-Up",
36908
37911
  "required": false,
@@ -36962,7 +37965,7 @@ const operationManifest = [
36962
37965
  "pathParams": [],
36963
37966
  "queryParams": [],
36964
37967
  "headerParams": [{
36965
- "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo or semantic search setting), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
37968
+ "description": "A single-use step-up token of purpose `org_settings.write` (Change the organization name, logo, semantic search setting or email notification settings), valid 5 minutes. When this operation answers 403 `step_up_required`, the person confirms at the link in `details.step_up_url` and the request is retried with the token that page shows, in this header. Accepted only with the person's own OAuth token of the client it was confirmed for; refused with a token issued for MCP, with any API key or agent connection key, and with a dashboard session.",
36966
37969
  "enum": null,
36967
37970
  "name": "Primitive-Step-Up",
36968
37971
  "required": false,
@@ -46690,7 +47693,7 @@ const operationManifest = [
46690
47693
  "type": "object",
46691
47694
  "additionalProperties": false,
46692
47695
  "required": ["url"],
46693
- "description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of the message gets the same `url`, so send one message per recipient when the URL identifies who to unsubscribe. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
47696
+ "description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of a message would get the same `url`, so another recipient could unsubscribe whoever it identifies: a reply with `unsubscribe` must go to the one address it answers, and one with `reply_all` that adds a cc is refused with a `validation_error`. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
46694
47697
  "properties": {
46695
47698
  "url": {
46696
47699
  "type": "string",
@@ -47878,7 +48881,7 @@ const operationManifest = [
47878
48881
  "type": "object",
47879
48882
  "additionalProperties": false,
47880
48883
  "required": ["url"],
47881
- "description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of the message gets the same `url`, so send one message per recipient when the URL identifies who to unsubscribe. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
48884
+ "description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of a message would get the same `url`, so another recipient could unsubscribe whoever it identifies: a send with `unsubscribe` must have exactly one recipient, one entry across `to`, `cc` and `bcc` (an address given twice counts twice), and a send to more is refused with a `validation_error`. Send one message per recipient, for example with `POST /v1/send-mail/batch`. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
47882
48885
  "properties": {
47883
48886
  "url": {
47884
48887
  "type": "string",
@@ -48385,7 +49388,7 @@ const operationManifest = [
48385
49388
  "type": "object",
48386
49389
  "additionalProperties": false,
48387
49390
  "required": ["url"],
48388
- "description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of the message gets the same `url`, so send one message per recipient when the URL identifies who to unsubscribe. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
49391
+ "description": "One-click unsubscribe for the message. Primitive writes a `List-Unsubscribe` header naming `url` (and `mailto` when given) and, unless `one_click` is false, `List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058), and DKIM-signs both. A receiver that offers one-click unsubscribe sends an HTTPS POST to `url` with the form body `List-Unsubscribe=One-Click`; your endpoint records the unsubscribe. Every recipient of a message would get the same `url`, so another recipient could unsubscribe whoever it identifies: a send with `unsubscribe` must have exactly one recipient, one entry across `to`, `cc` and `bcc` (an address given twice counts twice), and a send to more is refused with a `validation_error`. Send one message per recipient, for example with `POST /v1/send-mail/batch`. The `headers` map refuses both header names, so this field is the only way to set them, and the headers it adds count toward the 25 entries `headers` may hold. A field whose rendered `List-Unsubscribe` would pass 980 characters is refused. Not accepted on a repeating send.",
48389
49392
  "properties": {
48390
49393
  "url": {
48391
49394
  "type": "string",
@@ -51932,6 +52935,48 @@ function deliveryAttemptLines(data) {
51932
52935
  }
51933
52936
  return lines.length > 0 ? [`Attempts (${lines.length}):`, ...lines] : [];
51934
52937
  }
52938
+ /**
52939
+ * What an endpoint's DELIVERING column says, from `delivery` and never from
52940
+ * `enabled`: an endpoint can be on and still be sent nothing (its owner's key
52941
+ * revoked, say). A delivery the answer gives is read for any kind. Without
52942
+ * one, a webhook (or an endpoint of no stated kind) shows `unknown`, since it
52943
+ * could not be checked just now or the server does not say, and any other
52944
+ * kind (a Function or pull endpoint, which has no delivery) shows `-`.
52945
+ */
52946
+ function deliveringCell(endpoint) {
52947
+ const delivery = endpoint.delivery;
52948
+ if (isRecord$3(delivery) && typeof delivery.delivering === "boolean") {
52949
+ if (delivery.delivering) return "yes";
52950
+ const codes = Array.isArray(delivery.reasons) ? delivery.reasons.flatMap((reason) => isRecord$3(reason) && typeof reason.code === "string" && reason.code ? [oneLine(reason.code)] : []) : [];
52951
+ return codes.length > 0 ? `no (${codes.join(", ")})` : "no";
52952
+ }
52953
+ return endpoint.kind === void 0 || endpoint.kind === "http" ? "unknown" : "-";
52954
+ }
52955
+ /**
52956
+ * A table of an endpoints list (listEndpoints): each endpoint's id, kind,
52957
+ * whether it is on (ENABLED) and whether it is being sent mail at all
52958
+ * (DELIVERING, from `delivery`), and its name. Nothing for an empty list.
52959
+ */
52960
+ function endpointDeliveryLines(data) {
52961
+ const rows = Array.isArray(data) ? data.filter(isRecord$3) : [];
52962
+ if (rows.length === 0) return [];
52963
+ const text = (value) => typeof value === "string" && value ? oneLine(value) : "";
52964
+ const table = [[
52965
+ "ID",
52966
+ "KIND",
52967
+ "ENABLED",
52968
+ "DELIVERING",
52969
+ "NAME"
52970
+ ], ...rows.map((row) => [
52971
+ text(row.id),
52972
+ text(row.kind),
52973
+ typeof row.enabled === "boolean" ? row.enabled ? "yes" : "no" : "",
52974
+ deliveringCell(row),
52975
+ text(row.name)
52976
+ ])];
52977
+ const widths = table[0].map((_, column) => Math.max(...table.map((cells) => (cells[column] ?? "").length)));
52978
+ return table.map((cells) => cells.map((cell, column) => column === cells.length - 1 ? cell : cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd());
52979
+ }
51935
52980
  const routeReport = {
51936
52981
  flags: {},
51937
52982
  report: (data) => ({ lines: routeWarningLines(data) })
@@ -51942,6 +52987,10 @@ const ANSWER_REPORT_EXTENSIONS = {
51942
52987
  getDelivery: {
51943
52988
  flags: {},
51944
52989
  report: (data) => ({ lines: deliveryAttemptLines(data) })
52990
+ },
52991
+ listEndpoints: {
52992
+ flags: {},
52993
+ report: (data) => ({ lines: endpointDeliveryLines(data) })
51945
52994
  }
51946
52995
  };
51947
52996
  // src/oclif/user-grant.ts
@@ -53064,16 +54113,44 @@ const SAMPLE_TEXT_MAX = 4e3;
53064
54113
  const SAMPLE_SUBJECT_MAX = 998;
53065
54114
  const SAMPLE_LIST_MAX = 10;
53066
54115
  /**
54116
+ * The fields of each sample read from a file that this CLI does not know
54117
+ * (`email field "x"`, `sample field "y"`). They are sent as they are: the API
54118
+ * validates samples itself, so a field it added after this CLI was built is
54119
+ * not refused here. Said once per file, for the samples actually sent
54120
+ * (unknownFieldsNote).
54121
+ */
54122
+ const UNKNOWN_FIELDS = /* @__PURE__ */ new WeakMap();
54123
+ /**
54124
+ * A field name from a file or an answer, quoted and safe to print: JSON
54125
+ * escapes control characters, and a long name is cut.
54126
+ */
54127
+ function shownKey(key) {
54128
+ return JSON.stringify(key.length > 60 ? `${key.slice(0, 57)}...` : key).replace(/[\u007f-\u009f]/g, "?");
54129
+ }
54130
+ /**
54131
+ * One line naming the fields this CLI does not know in the samples sent, with
54132
+ * the numbers of the samples in the file that carry each; null when none do.
54133
+ */
54134
+ function unknownFieldsNote(source, sent, all) {
54135
+ const carriers = /* @__PURE__ */ new Map();
54136
+ for (const sample of sent) {
54137
+ const number = all.indexOf(sample) + 1;
54138
+ for (const field of UNKNOWN_FIELDS.get(sample) ?? []) carriers.set(field, [...carriers.get(field) ?? [], number]);
54139
+ }
54140
+ if (carriers.size === 0) return null;
54141
+ return `${source} has fields this CLI does not know, which it does not refuse but leaves for the API to judge (if the API refuses one, take it out): ${[...carriers].map(([field, numbers]) => `${field} (sample${numbers.length === 1 ? "" : "s"} ${numbers.join(", ")})`).join(", ")}.`;
54142
+ }
54143
+ /**
53067
54144
  * Refuse, naming the sample and the field, a made-up email the API would
53068
- * refuse to keep: the fields it takes and their bounds. The API checks the
53069
- * rest (address forms, a NUL).
54145
+ * refuse to keep: the bounds of the fields it takes. Returns the fields this
54146
+ * CLI does not know, which are passed on. The API checks the rest (address
54147
+ * forms, a NUL).
53070
54148
  */
53071
54149
  function checkSampleEmail(email, at) {
53072
54150
  const fail = (what) => {
53073
54151
  throw usageError$2(`${at}: email ${what}.`);
53074
54152
  };
53075
- const unknown = Object.keys(email).find((key) => !SAMPLE_EMAIL_KEYS.has(key));
53076
- if (unknown !== void 0) fail(`takes no "${unknown}" (it takes ${[...SAMPLE_EMAIL_KEYS].join(", ")})`);
54153
+ const unknown = Object.keys(email).filter((key) => !SAMPLE_EMAIL_KEYS.has(key)).map((key) => `email field ${shownKey(key)}`);
53077
54154
  if (!isRecord$1(email.from) || typeof email.from.address !== "string") fail("needs from.address");
53078
54155
  const list = (key, min) => {
53079
54156
  const value = email[key];
@@ -53091,6 +54168,7 @@ function checkSampleEmail(email, at) {
53091
54168
  if (typeof email.text !== "string" || email.text.length === 0 || email.text.length > SAMPLE_TEXT_MAX) fail(`text must be 1 to ${SAMPLE_TEXT_MAX} characters`);
53092
54169
  for (const key of ["automated", "from_mailing_list"]) if (email[key] !== void 0 && typeof email[key] !== "boolean") fail(`${key} must be true or false`);
53093
54170
  if (email.spam_score !== void 0 && (typeof email.spam_score !== "number" || !Number.isFinite(email.spam_score))) fail("spam_score must be a number");
54171
+ return unknown;
53094
54172
  }
53095
54173
  /** The keys a sample may carry: a saved one's id and time are taken and ignored. */
53096
54174
  const SAMPLE_KEYS = /* @__PURE__ */ new Set([
@@ -53113,7 +54191,8 @@ function samplesFromJson(value, source) {
53113
54191
  const samples = lookAlikeSamples(data);
53114
54192
  if (samples.length === 0) throw usageError$2(`${source} is a generate-look-alikes answer with no look-alikes in it.`);
53115
54193
  samples.forEach((sample, index) => {
53116
- checkSampleEmail(sample.email, `${source}, look-alike ${index + 1}`);
54194
+ const unknown = checkSampleEmail(sample.email, `${source}, look-alike ${index + 1}`);
54195
+ if (unknown.length > 0) UNKNOWN_FIELDS.set(sample, unknown);
53117
54196
  });
53118
54197
  return samples;
53119
54198
  }
@@ -53121,22 +54200,25 @@ function samplesFromJson(value, source) {
53121
54200
  return data.map((item, index) => {
53122
54201
  const at = `${source}, sample ${index + 1}`;
53123
54202
  if (!isRecord$1(item)) throw usageError$2(`${at} is not an object.`);
53124
- const unknown = Object.keys(item).find((key) => !SAMPLE_KEYS.has(key));
53125
- if (unknown !== void 0) throw usageError$2(`${at}: "${unknown}" is not a sample field; a sample takes expected, label and email.`);
54203
+ const unknown = Object.keys(item).filter((key) => !SAMPLE_KEYS.has(key));
53126
54204
  if (item.expected !== "match" && item.expected !== "no_match") throw usageError$2(`${at}: expected must be "match" or "no_match".`);
53127
54205
  if (!isRecord$1(item.email)) throw usageError$2(`${at}: email must be an object, as test-email-rule-on-sample takes it.`);
53128
- checkSampleEmail(item.email, at);
54206
+ const unknownEmail = checkSampleEmail(item.email, at);
53129
54207
  const label = item.label;
53130
54208
  if (label !== void 0 && label !== null) {
53131
54209
  if (typeof label !== "string") throw usageError$2(`${at}: label must be a string.`);
53132
54210
  if (label.trim() === "") throw usageError$2(`${at}: label is blank; leave it out or say what the email stands for.`);
53133
54211
  if (label.trim().length > SAMPLE_LABEL_MAX) throw usageError$2(`${at}: label is longer than ${SAMPLE_LABEL_MAX} characters.`);
53134
54212
  }
53135
- return {
54213
+ const sample = {
54214
+ ...Object.fromEntries(unknown.map((key) => [key, item[key]])),
53136
54215
  expected: item.expected,
53137
54216
  ...label !== void 0 ? { label } : {},
53138
54217
  email: item.email
53139
54218
  };
54219
+ const fields = [...unknown.map((key) => `sample field ${shownKey(key)}`), ...unknownEmail];
54220
+ if (fields.length > 0) UNKNOWN_FIELDS.set(sample, fields);
54221
+ return sample;
53140
54222
  });
53141
54223
  }
53142
54224
  /**
@@ -53187,6 +54269,8 @@ function readSamplesFile(path, flag, pick) {
53187
54269
  const all = samplesFromJson(readJsonFileFlag(path, `--${flag}`), source);
53188
54270
  const samples = typeof pick === "string" ? pickSamples(all, pick, source) : all;
53189
54271
  if (samples.length > 20) throw usageError$2(`${source} holds ${samples.length} samples, and a rule or a test takes at most 20; take some with --pick.`);
54272
+ const note = unknownFieldsNote(source, samples, all);
54273
+ if (note) process.stderr.write(`${note}\n`);
53190
54274
  return samples;
53191
54275
  }
53192
54276
  /** The one of `names` given as a file path, or null; --pick without one is refused. */
@@ -54007,7 +55091,11 @@ function writtenEmailRuleReport(data, request) {
54007
55091
  if (restored) lines.push(restored);
54008
55092
  const reach = affectsLine(rule.affects);
54009
55093
  if (reach) lines.push(reach);
54010
- for (const note of [rule.share_notice, rule.notice]) {
55094
+ for (const note of [
55095
+ rule.share_notice,
55096
+ rule.notice,
55097
+ rule.do_not_share_note
55098
+ ]) {
54011
55099
  const line = noteLine(note);
54012
55100
  if (line) lines.push(line);
54013
55101
  }
@@ -54229,6 +55317,25 @@ function updateEmailRuleErrorExitCode(payload) {
54229
55317
  function undecidedSamplesMayStay(count, reason) {
54230
55318
  return count > 0 && !RETRYABLE_REASONS.has(reason) && reason !== "author_unresolvable";
54231
55319
  }
55320
+ /**
55321
+ * The stderr report of test-email-rule-on-sample with --with-protection:
55322
+ * whether a match would be shared, and the do-not-share rules that would
55323
+ * keep it from being shared. Nothing without protection in the answer.
55324
+ */
55325
+ function testSampleProtectionReport(data) {
55326
+ if (!isRecord(data) || !isRecord(data.protection)) return { lines: [] };
55327
+ const protection = data.protection;
55328
+ const mailbox = typeof protection.mailbox === "string" && protection.mailbox ? ` over ${protection.mailbox}` : "";
55329
+ if (protection.outcome === "not_matched") return { lines: [`Protection${mailbox}: the rule did not match, so nothing would be shared and no do-not-share rule was judged.`] };
55330
+ if (protection.outcome === "shared") return { lines: [`Protection${mailbox}: a match would be shared; no do-not-share rule keeps it back.`] };
55331
+ if (protection.outcome !== "withheld") return { lines: [] };
55332
+ const withheld = (Array.isArray(protection.withheld_by) ? protection.withheld_by : []).filter(isRecord);
55333
+ const named = withheld.map((rule) => `${typeof rule.name === "string" ? rule.name : "a rule"} (${rule.id}, ${rule.reason === "undecided" ? "undecided" : "matched"})`);
55334
+ const hidden = typeof protection.withheld_by_hidden === "number" && protection.withheld_by_hidden > 0 ? protection.withheld_by_hidden : 0;
55335
+ const by = [...named, ...hidden > 0 ? [plural$1(hidden, "rule you cannot see", "rules you cannot see")] : []];
55336
+ const undecided = withheld.some((rule) => rule.reason === "undecided") ? " An undecided one holds it back until it is decided." : "";
55337
+ return { lines: [`Protection${mailbox}: a match would be withheld${by.length > 0 ? ` by ${by.join(", ")}` : ""}.${undecided}`] };
55338
+ }
54232
55339
  /** Where compile counted breadth, by the evaluation_scope it was sent. */
54233
55340
  const BREADTH_SCOPE = {
54234
55341
  own_mailboxes: "in your own mailboxes",
@@ -54241,7 +55348,8 @@ function compileEmailRuleReport(data, request) {
54241
55348
  const lines = (Array.isArray(data.warnings) ? data.warnings : []).filter((warning) => typeof warning === "string" && warning.trim() !== "").map((warning) => `Warning: ${warning.trim()}`);
54242
55349
  const breadth = data.breadth;
54243
55350
  const sent = isRecord(request?.body) && typeof request.body.evaluation_scope === "string" ? request.body.evaluation_scope : void 0;
54244
- const where = sent === void 0 ? "in your own mailboxes, the default; pass --evaluation-scope to count over the scope you will save it with" : BREADTH_SCOPE[sent] ?? `in evaluation_scope ${sent}`;
55351
+ const counted = isRecord(data.breadth_scope) && typeof data.breadth_scope.evaluation_scope === "string" ? data.breadth_scope.evaluation_scope : sent;
55352
+ const where = sent === void 0 && (counted === void 0 || counted === "own_mailboxes") ? "in your own mailboxes, the default; pass --evaluation-scope to count over the scope you will save it with" : BREADTH_SCOPE[counted ?? "own_mailboxes"] ?? `in evaluation_scope ${counted}`;
54245
55353
  if (isRecord(breadth) && typeof breadth.checked === "number" && typeof breadth.matched === "number") lines.push(`Breadth: its conditions hold for ${breadth.matched} of ${plural$1(breadth.checked, "recent email", "recent emails")} checked ${where}.`);
54246
55354
  return { lines };
54247
55355
  }
@@ -54396,7 +55504,8 @@ const EMAIL_RULE_OPERATION_EXTENSIONS = {
54396
55504
  testEmailRuleOnSample: {
54397
55505
  flags: scopeSelectionFlags(),
54398
55506
  bodyFields: scopeSelectionBodyFields,
54399
- bodyProperties: ["scope_selection"]
55507
+ bodyProperties: ["scope_selection"],
55508
+ report: testSampleProtectionReport
54400
55509
  },
54401
55510
  acceptEmailRuleRequest: {
54402
55511
  flags: {
@@ -54684,7 +55793,7 @@ function unknownEventTypes(eventTypes) {
54684
55793
  function unknownEventTypesWarning(unknown) {
54685
55794
  return `Warning: ${unknown.join(", ")} ${unknown.length === 1 ? "is" : "are"} not an event type this CLI knows. Subscriptions match exact names, so a misspelled name receives nothing. Continuing; newer event types may not be known to this CLI yet.\n`;
54686
55795
  }
54687
- function isPlainObject(value) {
55796
+ function isPlainObject$1(value) {
54688
55797
  return value !== null && typeof value === "object" && !Array.isArray(value);
54689
55798
  }
54690
55799
  /**
@@ -54697,16 +55806,16 @@ async function currentEndpointRules(endpointId, listEndpoints) {
54697
55806
  if (response.error) throw new EventTypesFlagError("Could not read the endpoint's current rules to keep them. Retry, or pass the full rules with --raw-body.");
54698
55807
  const row = (response.data?.data ?? []).find((endpoint) => endpoint.id === endpointId);
54699
55808
  if (!row) throw new EventTypesFlagError(`No endpoint ${endpointId} is visible to this credential, so its rules cannot be updated.`);
54700
- return isPlainObject(row.rules) ? row.rules : {};
55809
+ return isPlainObject$1(row.rules) ? row.rules : {};
54701
55810
  }
54702
55811
  /**
54703
55812
  * Put `eventTypes` in the request body's `rules.event_types`, on top of
54704
55813
  * `baseRules` (rules the caller passed in the body win over `baseRules`).
54705
55814
  */
54706
55815
  function withEventTypes(body, eventTypes, baseRules = {}) {
54707
- if (body !== void 0 && !isPlainObject(body)) throw new EventTypesFlagError("--raw-body must be a JSON object when also passing --event-types.");
55816
+ if (body !== void 0 && !isPlainObject$1(body)) throw new EventTypesFlagError("--raw-body must be a JSON object when also passing --event-types.");
54708
55817
  const current = body ?? {};
54709
- if (current.rules !== void 0 && !isPlainObject(current.rules)) throw new EventTypesFlagError("rules in --raw-body must be a JSON object when also passing --event-types.");
55818
+ if (current.rules !== void 0 && !isPlainObject$1(current.rules)) throw new EventTypesFlagError("rules in --raw-body must be a JSON object when also passing --event-types.");
54710
55819
  const explicitRules = current.rules ?? {};
54711
55820
  return {
54712
55821
  ...current,
@@ -54719,7 +55828,115 @@ function withEventTypes(body, eventTypes, baseRules = {}) {
54719
55828
  }
54720
55829
  /** True when the caller already passed `rules` in the request body. */
54721
55830
  function bodyHasRules(body) {
54722
- return isPlainObject(body) && body.rules !== void 0;
55831
+ return isPlainObject$1(body) && body.rules !== void 0;
55832
+ }
55833
+ // src/oclif/endpoint-url-shown.ts
55834
+ /** The commands whose answers carry endpoints, by operation sdkName. */
55835
+ const ENDPOINT_URL_OPERATIONS = /* @__PURE__ */ new Set([
55836
+ "getEndpoint",
55837
+ "listEndpoints",
55838
+ "createEndpoint",
55839
+ "updateEndpoint"
55840
+ ]);
55841
+ /** The commands that send a URL, whose value is checked for the placeholder. */
55842
+ const ENDPOINT_URL_WRITE_OPERATIONS = /* @__PURE__ */ new Set(["createEndpoint", "updateEndpoint"]);
55843
+ /** What a hidden query string is replaced with. */
55844
+ const HIDDEN_QUERY = "?... (query hidden)";
55845
+ const SHOW_URL_FLAG_DESCRIPTION = "Print each webhook URL in full, its query string included. Without it the query string is shown as ?... (query hidden), with url_query_hidden: true on the endpoint, because it can carry a secret such as a deployment protection bypass token.";
55846
+ /**
55847
+ * Said when an answer had a URL shortened: on stderr without --json, and in
55848
+ * the document's summary with --json --envelope.
55849
+ */
55850
+ const HIDDEN_QUERY_NOTE = "A webhook URL's query string is hidden, as it can carry a secret. Pass --show-url to print it in full.";
55851
+ /**
55852
+ * The URL with its query string, and anything after it, replaced by
55853
+ * HIDDEN_QUERY; null when it has no query string. A `?` after a `#` is part
55854
+ * of the fragment, not a query.
55855
+ */
55856
+ function urlWithQueryHidden(url) {
55857
+ const query = url.indexOf("?");
55858
+ if (query < 0) return null;
55859
+ const fragment = url.indexOf("#");
55860
+ if (fragment >= 0 && fragment < query) return null;
55861
+ return `${url.slice(0, query)}${HIDDEN_QUERY}`;
55862
+ }
55863
+ function hideOne(endpoint) {
55864
+ if (endpoint === null || typeof endpoint !== "object" || Array.isArray(endpoint)) return {
55865
+ value: endpoint,
55866
+ hidden: false
55867
+ };
55868
+ const url = endpoint.url;
55869
+ const shown = typeof url === "string" ? urlWithQueryHidden(url) : null;
55870
+ if (shown === null) return {
55871
+ value: endpoint,
55872
+ hidden: false
55873
+ };
55874
+ return {
55875
+ value: {
55876
+ ...endpoint,
55877
+ url: shown,
55878
+ url_query_hidden: true
55879
+ },
55880
+ hidden: true
55881
+ };
55882
+ }
55883
+ /**
55884
+ * An answer's data (one endpoint, or a list of them) with each URL's query
55885
+ * string hidden, and whether any was.
55886
+ */
55887
+ function endpointsWithQueryHidden(data) {
55888
+ if (!Array.isArray(data)) {
55889
+ const one = hideOne(data);
55890
+ return {
55891
+ data: one.value,
55892
+ hidden: one.hidden
55893
+ };
55894
+ }
55895
+ let hidden = false;
55896
+ return {
55897
+ data: data.map((row) => {
55898
+ const one = hideOne(row);
55899
+ hidden ||= one.hidden;
55900
+ return one.value;
55901
+ }),
55902
+ hidden
55903
+ };
55904
+ }
55905
+ function isPlainObject(value) {
55906
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
55907
+ const prototype = Object.getPrototypeOf(value);
55908
+ return prototype === Object.prototype || prototype === null;
55909
+ }
55910
+ /** How deep errorWithQueryHidden looks into an error payload. */
55911
+ const ERROR_DEPTH = 8;
55912
+ /**
55913
+ * An error answer with the query string hidden in every `url` it names, such
55914
+ * as `details.holder.url` on a 409 for a slot another endpoint holds.
55915
+ * Anything that is not a plain object or a list is left as it is.
55916
+ */
55917
+ function errorWithQueryHidden(payload, depth = 0) {
55918
+ if (depth > ERROR_DEPTH) return payload;
55919
+ if (Array.isArray(payload)) return payload.map((entry) => errorWithQueryHidden(entry, depth + 1));
55920
+ if (!isPlainObject(payload)) return payload;
55921
+ const out = {};
55922
+ for (const [key, value] of Object.entries(payload)) out[key] = errorWithQueryHidden(value, depth + 1);
55923
+ return hideOne(out).value;
55924
+ }
55925
+ /**
55926
+ * A query string as a partial copy of HIDDEN_QUERY leaves it: a terminal's
55927
+ * word selection stops at its first space, after `?...`.
55928
+ */
55929
+ const HIDDEN_QUERY_START = "?...";
55930
+ /**
55931
+ * The refusal for a request body whose url is a URL as these commands print
55932
+ * it with the query hidden, whole or cut at its first space, so a printed URL
55933
+ * is never written back in place of the real one; null for any other body.
55934
+ */
55935
+ function hiddenQueryUrlRefusal(body) {
55936
+ if (body === null || typeof body !== "object" || Array.isArray(body)) return null;
55937
+ const url = body.url;
55938
+ if (typeof url !== "string" || !url.includes(HIDDEN_QUERY_START)) return null;
55939
+ return `The url carries ${HIDDEN_QUERY_START}, the start of ${HIDDEN_QUERY}, which is how the endpoint commands print a URL with its query string hidden, not the URL itself. Pass the full URL; \`primitive endpoints get <id> --show-url\` prints a webhook's URL in full.`;
54723
55940
  }
54724
55941
  // src/oclif/endpoints-test-redirect.ts
54725
55942
  async function detectFunctionEndpoint(endpointId, listEndpoints) {
@@ -56576,7 +57793,7 @@ function savedConnectedProfiles(configDir, exclude) {
56576
57793
  */
56577
57794
  async function otherProfiles(configDir, session, current) {
56578
57795
  if (session) try {
56579
- const { connectedProfilesForSession } = await import("./machine-session-BMr-zPLx.js").then((n) => n.s);
57796
+ const { connectedProfilesForSession } = await import("./machine-session-DyGP7wDs.js").then((n) => n.s);
56580
57797
  return {
56581
57798
  inSession: true,
56582
57799
  profiles: connectedProfilesForSession(configDir, session, current)
@@ -57438,8 +58655,13 @@ const NETWORK_ERROR_HINTS = {
57438
58655
  ENETUNREACH: "Hint: the network is unreachable. If you're behind a proxy and set HTTP(S)_PROXY, re-run with NODE_USE_ENV_PROXY=1 (Node 22+ ignores those env vars by default). `primitive doctor` reports the local environment in one shot.",
57439
58656
  ECONNREFUSED: "Hint: the server refused the connection. Check that your firewall allows egress to *.primitive.dev, that your PRIMITIVE_API_BASE_URL_* overrides (if any) point at a reachable host, and re-run with NODE_USE_ENV_PROXY=1 if you're behind a proxy. `primitive doctor` reports the local environment in one shot.",
57440
58657
  ETIMEDOUT: "Hint: the connection timed out. Check egress rules and proxy configuration; if you're behind a proxy, re-run with NODE_USE_ENV_PROXY=1 and HTTPS_PROXY set. `primitive doctor` reports the local environment in one shot.",
57441
- EAI_AGAIN: "Hint: DNS lookup failed. Check /etc/resolv.conf inside containers, and try `curl -v https://api.primitive.dev/v1/account` to confirm the host resolves. `primitive doctor` reports the local environment in one shot."
58658
+ EAI_AGAIN: "Hint: DNS lookup failed. Check /etc/resolv.conf inside containers, and try `curl -v https://api.primitive.dev/v1/account` to confirm the host resolves. `primitive doctor` reports the local environment in one shot.",
58659
+ ENOTFOUND: "Hint: the API host name did not resolve. Check PRIMITIVE_API_BASE_URL and any --api-base-url for a typo, and your DNS. `primitive doctor` reports the local environment in one shot."
57442
58660
  };
58661
+ /** The hint for a network failure's code (`cause.code` on a failed fetch), if there is one. */
58662
+ function networkErrorHint(code) {
58663
+ return Object.hasOwn(NETWORK_ERROR_HINTS, code) ? NETWORK_ERROR_HINTS[code] : void 0;
58664
+ }
57443
58665
  const RATE_LIMIT_CODES = /* @__PURE__ */ new Set(["rate_limited", "rate_limit_exceeded"]);
57444
58666
  /** When to retry a rate-limited request, from Retry-After when it was sent. */
57445
58667
  function rateLimitHint(response) {
@@ -57662,6 +58884,10 @@ function buildFlags(operation) {
57662
58884
  multiple: true
57663
58885
  });
57664
58886
  if (operation.binaryResponse) flags.output = Flags.string({ description: "Write binary response bytes to a file" });
58887
+ if (ENDPOINT_URL_OPERATIONS.has(operation.sdkName)) {
58888
+ if (flags["show-url"] !== void 0) throw new Error(`${operation.sdkName}: --show-url is both a generated flag and the CLI's own`);
58889
+ flags["show-url"] = Flags.boolean({ description: SHOW_URL_FLAG_DESCRIPTION });
58890
+ }
57665
58891
  if (ATTACHMENT_WAIT_OPERATIONS.has(operation.sdkName)) flags.wait = Flags.boolean({
57666
58892
  allowNo: true,
57667
58893
  default: true,
@@ -57757,6 +58983,9 @@ const OPERATION_HINTS = {
57757
58983
  getMemory: "Tip: prefer `primitive memories get <key> [--function <function-id>]` for the common read path.",
57758
58984
  deleteMemory: "Tip: prefer `primitive memories delete <key> [--function <function-id>]` for the common delete path.",
57759
58985
  searchMemories: "Tip: prefer `primitive memories search [prefix] [--metadata-only] [--function <function-id>]` for the common search path.",
58986
+ createEmailRuleObjection: "Tip: say in --message (up to 500 characters) what you want. It can ask for less than turning the rule off: ask the author to narrow the rule, or to explain where its matches go (its receivers). The author accepts, which turns the rule off, or declines, optionally giving a reason, which you see in `primitive email-rule-oversight get-email-rule-objection <objection-id>`. You can withdraw it with `primitive email-rule-oversight withdraw-email-rule-objection <objection-id>`. Objecting does not change the rule.",
58987
+ declineEmailRuleObjection: "Tip: the member who objected sees --reason (up to 500 characters). When the objection asked you to narrow the rule or explain a receiver, the reason can answer that: say what you changed, or what the receiver is for.",
58988
+ listEndpoints: "Tip: without --json, a table on stderr after the JSON shows each endpoint's ENABLED and DELIVERING. DELIVERING is read from delivery.delivering, whether a webhook is being sent mail at all, not from enabled: an endpoint can be enabled and still not be delivering (its owner's key revoked, say), shown as no with the reason codes in parentheses. A webhook whose delivery could not be checked shows unknown, and a Function or pull endpoint shows -.",
57760
58989
  startAgentSignup: "Tip: pass --terms-accepted. Capture the signup_token from the response and feed it to `primitive agent verify-agent-signup --signup-token <token> --verification-code <6-digit-code>` (the verify flag accepts --code as an alias). The high-level `primitive signup <email>` command walks an interactive user through both steps with friendlier prompts.",
57761
58990
  testEmailRule: "Tip: --expect-match and --expect-no-match (or --expect-file) set expect, the emails the rule must match and must not. When no expect is sent, by these flags or in the body, a saved rule's saved expectations are judged instead. Either way, the answer reports the expectations it judged in expectations: passed, counts and per-email results. (create-email-rule and update-email-rule instead judge the rule's saved expectations on the version they save, in expectations_check: passed, decided, failed and undecided.) The exit code follows that report: 1 when one failed; 1 when one is undecided and testing again will not decide it (its email cannot be found or read, cannot be classified, or cannot be tested: skipped); 3 when the only undecided ones can be decided later (not classified yet, or a question unanswered), so test again later. Exit 1 also means the request failed. With --json, error.code tells these apart: expectations_failed, expectations_unresolvable, expectations_undecided (exit 3), or the API's own code. Without --json a readable report follows the JSON on stderr: warnings, how each condition held, the expectations, and the values each email that did not match was compared on.",
57762
58991
  verifyAgentSignup: "Tip: pass --verification-code <code> (or --code; both work). The response carries OAuth tokens but not your assigned inbox domain; run `primitive domains list` (or `primitive whoami`) after success to see the managed *.primitive.email address that routes to this account."
@@ -57846,6 +59075,13 @@ function createOperationCommand(operation) {
57846
59075
  "verifyAgentSignup",
57847
59076
  "resendAgentSignupVerification"
57848
59077
  ].includes(operation.operationId)) requireDefaultLoginProfile();
59078
+ const prereadBody = ENDPOINT_URL_WRITE_OPERATIONS.has(operation.sdkName) && operation.hasJsonBody ? { value: readJsonBody(parsedFlags) } : void 0;
59079
+ if (prereadBody) {
59080
+ const urlFlag = [...bodyFieldFlagToProperty].find(([, property]) => property === "url")?.[0];
59081
+ const flagUrl = urlFlag ? parsedFlags[urlFlag] : void 0;
59082
+ const refusal = hiddenQueryUrlRefusal(typeof flagUrl === "string" ? { url: flagUrl } : prereadBody.value);
59083
+ if (refusal) throw new Errors.CLIError(refusal, { exit: 2 });
59084
+ }
57849
59085
  await runWithTiming(parsedFlags.time === true, async () => {
57850
59086
  const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
57851
59087
  apiKey: typeof parsedFlags["api-key"] === "string" ? parsedFlags["api-key"] : void 0,
@@ -57854,7 +59090,7 @@ function createOperationCommand(operation) {
57854
59090
  });
57855
59091
  let body;
57856
59092
  if (operation.hasJsonBody) {
57857
- const explicit = readJsonBody(parsedFlags);
59093
+ const explicit = prereadBody ? prereadBody.value : readJsonBody(parsedFlags);
57858
59094
  const overrides = collectBodyFieldFlags(parsedFlags, bodyFieldFlagToProperty);
57859
59095
  const extensionFlagFor = /* @__PURE__ */ new Map();
57860
59096
  for (const field of extension?.bodyFields?.(parsedFlags, explicit) ?? []) {
@@ -57874,6 +59110,10 @@ function createOperationCommand(operation) {
57874
59110
  }
57875
59111
  } else body = explicit;
57876
59112
  }
59113
+ if (ENDPOINT_URL_WRITE_OPERATIONS.has(operation.sdkName)) {
59114
+ const refusal = hiddenQueryUrlRefusal(body);
59115
+ if (refusal) throw new Errors.CLIError(refusal, { exit: 2 });
59116
+ }
57877
59117
  if (EVENT_TYPES_FLAG_OPERATIONS.has(operation.sdkName) && Array.isArray(parsedFlags["event-types"])) body = await applyEventTypesFlag({
57878
59118
  body,
57879
59119
  values: parsedFlags["event-types"],
@@ -57993,7 +59233,7 @@ function createOperationCommand(operation) {
57993
59233
  if (result.error) {
57994
59234
  const errorPayload = extractErrorPayload(result.error);
57995
59235
  const operationErrorHint = OPERATION_ERROR_HINTS[operation.sdkName]?.(extractErrorCode(errorPayload), body, errorPayload, collectValues(operation.pathParams, parsedFlags, operation.sdkName), parsedFlags);
57996
- writeErrorWithHints(errorPayload, result.response, { codeHint: !(operationErrorHint && OPERATION_ERROR_HINTS_REPLACING_CODE_HINT.has(operation.sdkName)) });
59236
+ writeErrorWithHints(ENDPOINT_URL_OPERATIONS.has(operation.sdkName) && parsedFlags["show-url"] !== true ? errorWithQueryHidden(errorPayload) : errorPayload, result.response, { codeHint: !(operationErrorHint && OPERATION_ERROR_HINTS_REPLACING_CODE_HINT.has(operation.sdkName)) });
57997
59237
  if (operationErrorHint) process.stderr.write(`${operationErrorHint}\n`);
57998
59238
  if (stepUpUrl) process.stderr.write(approveThenRerunHint(stepUpUrl));
57999
59239
  if (replyStateSurface && isAwaitingRejectedError(errorPayload)) process.stderr.write(`${awaitingRejectedError(replyStateSurface).message}\n`);
@@ -58141,12 +59381,26 @@ function createOperationCommand(operation) {
58141
59381
  }
58142
59382
  };
58143
59383
  }
58144
- let printed = jsonOutput && withEnvelope ? operationJsonDocument(envelope, summary) : operationOutputPayload(envelope, withEnvelope);
59384
+ let shownEnvelope = envelope;
59385
+ let queryHidden = false;
59386
+ if (ENDPOINT_URL_OPERATIONS.has(operation.sdkName) && parsedFlags["show-url"] !== true && envelope) {
59387
+ const shown = endpointsWithQueryHidden(envelope.data);
59388
+ if (shown.hidden) {
59389
+ shownEnvelope = {
59390
+ ...envelope,
59391
+ data: shown.data
59392
+ };
59393
+ queryHidden = true;
59394
+ if (jsonOutput && withEnvelope) summary.push(HIDDEN_QUERY_NOTE);
59395
+ }
59396
+ }
59397
+ let printed = jsonOutput && withEnvelope ? operationJsonDocument(shownEnvelope, summary) : operationOutputPayload(shownEnvelope, withEnvelope);
58145
59398
  if (jsonOutput && report?.exitCode && report.error && printed !== null && typeof printed === "object" && !Array.isArray(printed)) printed = {
58146
59399
  ...printed,
58147
59400
  error: report.error
58148
59401
  };
58149
59402
  this.log(JSON.stringify(printed, null, 2));
59403
+ if (queryHidden && !jsonOutput) process.stderr.write(`${HIDDEN_QUERY_NOTE}\n`);
58150
59404
  if (reportError !== void 0) process.stderr.write(`Warning: ${operation.sdkName} succeeded but its report could not be built (${reportError}). The JSON above is the full answer; read it rather than the exit code.\n`);
58151
59405
  if (report && !jsonOutput) for (const line of report.lines) process.stderr.write(`${line}\n`);
58152
59406
  if (reportError !== void 0 && report?.exitCode && !jsonOutput) process.stderr.write(`The answer's expectations were not all shown to pass, so the command exits ${report.exitCode}.\n`);
@@ -58180,4 +59434,4 @@ function emptyDeliveriesHint(sdkName, query) {
58180
59434
  function canonicalizeCliReferences(description) {
58181
59435
  return description.replaceAll("`primitive emails:latest`", "`primitive emails latest`").replaceAll("`primitive describe emails:get-email | jq '.responseSchema.properties'`", "`primitive describe emails:get | jq '.responseSchema.properties'`");
58182
59436
  }
58183
- export { readEmailInteraction as $, SAVED_CLI_OAUTH_SESSION_EXPIRED_MESSAGE as $t, explainPendingMailNotFound as A, cliInvocation as At, skillFirstLine as B, formatAlreadySentNotice as Bt, promptLine as C, sendOutcomeExitCode as Ct, formatBrowserLoginInstructions as D, successfulSendOutcome as Dt, canOfferCopyHotkey as E, serializeErrorPayload as Et, latestOwnOutbound as F, loadSkillLine as Ft, clearConsumedPendingMail as G, classificationFactLines as Gt, wakeRecipientField as H, emptyEmailListHint as Ht, readThreadContext as I, currentMailSessionKey as It, readPendingMail as J, noFactsMessage as Jt, clearReadPendingMail as K, classificationOf as Kt, sentInThread as L, detectAgentRuntime as Lt, describeWake as M, defaultConnectionName as Mt, displayAddress as N, generatedAgentName as Nt, listenForCopyHotkey as O, thrownErrorExitCode as Ot, formatMailWakeLine as P, identitySuggestions as Pt, interactionNextActions as Q, ConnectedAgentKeyRequiredError as Qt, serverMuted as R, detectRuntimeSession as Rt, approveThenRerunHint as S, sendIdentityFields as St, withApproval as T, sentHistoryWindowStart as Tt, wakeRelationship as U, mailReach as Ut, wakeReadCommand as V, withFlagSuggestion as Vt, claimPendingMail as W, readCallerMailFor as Wt, removePendingMail as X, ATTACHMENT_WAIT_MAX_MS as Xt, recordPendingMail as Y, ATTACHMENT_WAIT_FLAG_DESCRIPTION as Yt, interactionHumanLine as Z, withAttachmentWait as Zt, takesStepUp as _, setKnownCommands as _n, formatPriorRepliesWarning as _t, createOperationCommand as a, selectedOrgFromEnv as an, SEND_OUTCOME_HELP as at, STEP_UP_HEADER as b, priorRepliesThatWentOut as bt, formatErrorPayload as c, credentialsFromUserLogin as cn, buildFollowUpCommand as ct, isGrantRefusal as d, revokeUserLogin as dn, classifySendError as dt, createAuthenticatedCliApiClient as en, replyExpectation as et, parameterFlagName as f, roleKeyLabel as fn, deriveSendIdempotencyKey as ft, surfaceUnauthorizedHint as g, sanitizeForTerminal as gn, formatPriorRepliesCheckSkipped as gt, runWithTiming as h, routeWarningLines as hn, formatDeletedEarlierSendNotice as ht, TIME_FLAG_DESCRIPTION as i, resolveCliApiRequestConfig as in, MAX_SEND_WAIT_TIMEOUT_MS as it, bareAddress$1 as j, connectNextSteps as jt, openBrowser as k, DEFAULT_ENROLL_NAME as kt, hasControlCharacter as l, fetchCliOrgs as ln, buildThrownSendFailureEnvelope as lt, refusalDetails as m, switchCurrentOrg as mn, followUpCommandPrefix as mt, API_ERROR_CODES as n, refreshStoredCliCredentials as nn, wakeInteractionSentence as nt, extractErrorCode as o, CliOrgRefusedError as on, apiResultHttpStatus as ot, readTextFileFlag as p, roleKeyText as pn, excludeSignalReplies as pt, pendingMailPath as q, isEmailId as qt, OPERATION_PATH_FLAGS as r, refuseOrgSelectionForProfile as rn, IDEMPOTENCY_KEY_FLAG_DESCRIPTION as rt, extractErrorPayload as s, RESTRICTED_ROLE_NOTE as sn, assertValidIdempotencyKey as st, API_BASE_URL_FLAG_DESCRIPTION as t, createCliApiClient as tn, replyInteractionWarning as tt, headerFlagName as u, isBuiltInRoleKey as un, checkPriorReplies as ut, writeErrorWithHints as v, operationManifest as vn, formatSendFailureSummary as vt, terminalApprovalIo as w, sendWaitClampNotice as wt, STEP_UP_TOKEN_FLAG_DESCRIPTION as x, reportSendCommandResult as xt, BROWSER_FLAG_DESCRIPTION as y, formatSendRecordFailureSummary as yt, serverRelationship as z, runtimeSessionVariableList as zt };
59437
+ export { interactionNextActions as $, ConnectedAgentKeyRequiredError as $t, openBrowser as A, DEFAULT_ENROLL_NAME as At, serverRelationship as B, runtimeSessionVariableList as Bt, approveThenRerunHint as C, sendIdentityFields as Ct, canOfferCopyHotkey as D, serializeErrorPayload as Dt, withApproval as E, sentHistoryWindowStart as Et, formatMailWakeLine as F, identitySuggestions as Ft, claimPendingMail as G, readCallerMailFor as Gt, wakeReadCommand as H, withFlagSuggestion as Ht, latestOwnOutbound as I, loadSkillLine as It, pendingMailPath as J, isEmailId as Jt, clearConsumedPendingMail as K, classificationFactLines as Kt, readThreadContext as L, currentMailSessionKey as Lt, bareAddress$1 as M, connectNextSteps as Mt, describeWake as N, defaultConnectionName as Nt, formatBrowserLoginInstructions as O, successfulSendOutcome as Ot, displayAddress as P, generatedAgentName as Pt, interactionHumanLine as Q, withAttachmentWait as Qt, sentInThread as R, detectAgentRuntime as Rt, STEP_UP_TOKEN_FLAG_DESCRIPTION as S, reportSendCommandResult as St, terminalApprovalIo as T, sendWaitClampNotice as Tt, wakeRecipientField as U, emptyEmailListHint as Ut, skillFirstLine as V, formatAlreadySentNotice as Vt, wakeRelationship as W, mailReach as Wt, recordPendingMail as X, ATTACHMENT_WAIT_FLAG_DESCRIPTION as Xt, readPendingMail as Y, noFactsMessage as Yt, removePendingMail as Z, ATTACHMENT_WAIT_MAX_MS as Zt, surfaceUnauthorizedHint as _, sanitizeForTerminal as _n, formatPriorRepliesCheckSkipped as _t, createOperationCommand as a, resolveCliApiRequestConfig as an, MAX_SEND_WAIT_TIMEOUT_MS as at, BROWSER_FLAG_DESCRIPTION as b, formatSendRecordFailureSummary as bt, formatErrorPayload as c, RESTRICTED_ROLE_NOTE as cn, assertValidIdempotencyKey as ct, isGrantRefusal as d, isBuiltInRoleKey as dn, checkPriorReplies as dt, SAVED_CLI_OAUTH_SESSION_EXPIRED_MESSAGE as en, readEmailInteraction as et, networkErrorHint as f, revokeUserLogin as fn, classifySendError as ft, runWithTiming as g, routeWarningLines as gn, formatDeletedEarlierSendNotice as gt, refusalDetails as h, switchCurrentOrg as hn, followUpCommandPrefix as ht, TIME_FLAG_DESCRIPTION as i, refuseOrgSelectionForProfile as in, IDEMPOTENCY_KEY_FLAG_DESCRIPTION as it, explainPendingMailNotFound as j, cliInvocation as jt, listenForCopyHotkey as k, thrownErrorExitCode as kt, hasControlCharacter as l, credentialsFromUserLogin as ln, buildFollowUpCommand as lt, readTextFileFlag as m, roleKeyText as mn, excludeSignalReplies as mt, API_ERROR_CODES as n, createCliApiClient as nn, replyInteractionWarning as nt, extractErrorCode as o, selectedOrgFromEnv as on, SEND_OUTCOME_HELP as ot, parameterFlagName as p, roleKeyLabel as pn, deriveSendIdempotencyKey as pt, clearReadPendingMail as q, classificationOf as qt, OPERATION_PATH_FLAGS as r, refreshStoredCliCredentials as rn, wakeInteractionSentence as rt, extractErrorPayload as s, CliOrgRefusedError as sn, apiResultHttpStatus as st, API_BASE_URL_FLAG_DESCRIPTION as t, createAuthenticatedCliApiClient as tn, replyExpectation as tt, headerFlagName as u, fetchCliOrgs as un, buildThrownSendFailureEnvelope as ut, takesStepUp as v, setKnownCommands as vn, formatPriorRepliesWarning as vt, promptLine as w, sendOutcomeExitCode as wt, STEP_UP_HEADER as x, priorRepliesThatWentOut as xt, writeErrorWithHints as y, operationManifest as yn, formatSendFailureSummary as yt, serverMuted as z, detectRuntimeSession as zt };