primcli 3.0.0-preview.6 → 3.0.0-preview.7

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,6 +1,7 @@
1
- import { B as ensureSearchReportsReplyState, Ht as getEmail, J as SENT_EMAIL_EVENT_TYPES, K as replyStateSurfaceForOperation, L as ReplyStateUnsupportedError, R as assertAwaitingFilterRows, W as isAwaitingRejectedError, Wn as sdk_gen_exports, Y as WEBHOOK_EVENT_TYPES, Zt as getOwnGrant, at as automatedRejectedError, ct as expectedAutomatedVerdicts, gn as listEndpoints, it as assertAutomatedVerdict, ot as automatedSurfaceForOperation, q as EMAIL_RULE_REQUEST_EVENT_TYPES, rt as AutomatedFilterUnsupportedError, st as ensureSearchReportsAutomated, tn as getThread, ut as isAutomatedRejectedError, v as parseRetryAfter, z as awaitingRejectedError } from "./repeats-Dj_o6c-X-CP0h4alF.js";
1
+ import { B as ensureSearchReportsReplyState, Ht as getEmail, J as SENT_EMAIL_EVENT_TYPES, K as replyStateSurfaceForOperation, L as ReplyStateUnsupportedError, R as assertAwaitingFilterRows, W as isAwaitingRejectedError, Wn as sdk_gen_exports, Y as WEBHOOK_EVENT_TYPES, Zt as getOwnGrant, at as automatedRejectedError, ct as expectedAutomatedVerdicts, gn as listEndpoints, it as assertAutomatedVerdict, ot as automatedSurfaceForOperation, q as EMAIL_RULE_REQUEST_EVENT_TYPES, rt as AutomatedFilterUnsupportedError, st as ensureSearchReportsAutomated, tn as getThread, ut as isAutomatedRejectedError, v as parseRetryAfter, z as awaitingRejectedError } from "./repeats-Dj_o6c-X-C6tOKAqk.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
+ import { a as thrownErrorBody } from "./json-output-ch756TXV.js";
4
5
  import { d as readMailJson, h as writeMailJson, m as withMailLock, p as syncMailDirectory, u as privateMailPermissions } from "./shared-mail-files-BAQSGxxz.js";
5
6
  import { Args, Command, Errors, Flags } from "@oclif/core";
6
7
  import { existsSync, lstatSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
@@ -13014,6 +13015,25 @@ const operationManifest = [
13014
13015
  "type": "string",
13015
13016
  "minLength": 1,
13016
13017
  "maxLength": 64
13018
+ },
13019
+ "breadth": {
13020
+ "description": "Ignored (the compile answer's breadth).",
13021
+ "anyOf": [{
13022
+ "type": "object",
13023
+ "properties": {
13024
+ "checked": {
13025
+ "type": "integer",
13026
+ "minimum": 0,
13027
+ "description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in your own mailboxes (for a grant rule, those you may share), the ones you can still read."
13028
+ },
13029
+ "matched": {
13030
+ "type": "integer",
13031
+ "minimum": 0,
13032
+ "description": "Of those, how many they hold for (for a do-not-share rule, one they cannot decide counts)."
13033
+ }
13034
+ },
13035
+ "required": ["checked", "matched"]
13036
+ }, { "type": "null" }]
13017
13037
  }
13018
13038
  },
13019
13039
  "additionalProperties": false
@@ -13244,10 +13264,37 @@ const operationManifest = [
13244
13264
  "type": ["integer", "null"],
13245
13265
  "exclusiveMinimum": 0
13246
13266
  },
13267
+ "revision": {
13268
+ "description": "Moves on by one with every update. Send it as if_revision.",
13269
+ "type": "integer",
13270
+ "exclusiveMinimum": 0
13271
+ },
13247
13272
  "author_user_id": {
13248
13273
  "type": "string",
13249
13274
  "format": "uuid"
13250
13275
  },
13276
+ "author": {
13277
+ "type": "object",
13278
+ "properties": {
13279
+ "user_id": {
13280
+ "type": "string",
13281
+ "format": "uuid"
13282
+ },
13283
+ "name": {
13284
+ "description": "Their name; null unless you can list members or this is you, and when they have none set.",
13285
+ "type": ["string", "null"]
13286
+ },
13287
+ "email": {
13288
+ "description": "Sign-in email; null unless you can list member emails or this is you.",
13289
+ "type": ["string", "null"]
13290
+ }
13291
+ },
13292
+ "required": [
13293
+ "user_id",
13294
+ "name",
13295
+ "email"
13296
+ ]
13297
+ },
13251
13298
  "is_author": {
13252
13299
  "type": "boolean",
13253
13300
  "description": "Whether you are its author, in your current membership. Only its author edits it; others who see it may only disable or delete it (while shared, only if they manage members)."
@@ -13260,91 +13307,94 @@ const operationManifest = [
13260
13307
  "type": "string",
13261
13308
  "format": "date-time"
13262
13309
  },
13263
- "current": { "anyOf": [{
13264
- "type": "object",
13265
- "properties": {
13266
- "version": {
13267
- "type": "integer",
13268
- "exclusiveMinimum": 0
13269
- },
13270
- "english": { "type": "string" },
13271
- "tree": {
13272
- "type": "object",
13273
- "properties": {
13274
- "version": {
13275
- "type": "number",
13276
- "const": 1
13277
- },
13278
- "vocabulary": {
13279
- "type": "string",
13280
- "description": "Vocabulary version, such as v1."
13281
- },
13282
- "when": {
13283
- "type": "object",
13284
- "propertyNames": { "type": "string" },
13285
- "additionalProperties": {},
13286
- "description": "A condition {column, op, value?, min_confidence?} (see GET /email-rules/vocabulary) or {all: [...]}, {any: [...]}, {not: ...}."
13287
- },
13288
- "questions": {
13289
- "type": "array",
13290
- "items": { "type": "string" },
13291
- "description": "Yes/no questions asked once the conditions match; all must be yes."
13292
- }
13310
+ "current": {
13311
+ "description": "Its current version: its text (english, tree, explanation, assumptions, warnings) and who compiled it. current_version is its number. GET /email-rules lists rules without it.",
13312
+ "anyOf": [{
13313
+ "type": "object",
13314
+ "properties": {
13315
+ "version": {
13316
+ "type": "integer",
13317
+ "exclusiveMinimum": 0
13293
13318
  },
13294
- "required": [
13295
- "version",
13296
- "vocabulary",
13297
- "when",
13298
- "questions"
13299
- ]
13300
- },
13301
- "explanation": { "type": "string" },
13302
- "assumptions": {
13303
- "type": "array",
13304
- "items": { "type": "string" }
13305
- },
13306
- "warnings": {
13307
- "type": "array",
13308
- "items": { "type": "string" }
13309
- },
13310
- "compiled_by": {
13311
- "type": "string",
13312
- "description": "Who produced the tree, as stated when saved (not verified), such as rule-compiler-v1 or member."
13313
- },
13314
- "vocabulary": { "type": "string" },
13315
- "created_at": {
13316
- "type": "string",
13317
- "format": "date-time"
13318
- },
13319
- "sql": {
13320
- "description": "Present only when include_sql was true.",
13321
- "type": "object",
13322
- "properties": {
13323
- "expression": {
13324
- "type": "string",
13325
- "description": "A parameterized boolean expression over one row per email."
13319
+ "english": { "type": "string" },
13320
+ "tree": {
13321
+ "type": "object",
13322
+ "properties": {
13323
+ "version": {
13324
+ "type": "number",
13325
+ "const": 1
13326
+ },
13327
+ "vocabulary": {
13328
+ "type": "string",
13329
+ "description": "Vocabulary version, such as v1."
13330
+ },
13331
+ "when": {
13332
+ "type": "object",
13333
+ "propertyNames": { "type": "string" },
13334
+ "additionalProperties": {},
13335
+ "description": "A condition {column, op, value?, min_confidence?} (see GET /email-rules/vocabulary) or {all: [...]}, {any: [...]}, {not: ...}."
13336
+ },
13337
+ "questions": {
13338
+ "type": "array",
13339
+ "items": { "type": "string" },
13340
+ "description": "Yes/no questions asked once the conditions match; all must be yes."
13341
+ }
13326
13342
  },
13327
- "params": {
13328
- "type": "array",
13329
- "items": {},
13330
- "description": "The values bound to $1, $2 and so on."
13331
- }
13343
+ "required": [
13344
+ "version",
13345
+ "vocabulary",
13346
+ "when",
13347
+ "questions"
13348
+ ]
13332
13349
  },
13333
- "required": ["expression", "params"]
13334
- }
13335
- },
13336
- "required": [
13337
- "version",
13338
- "english",
13339
- "tree",
13340
- "explanation",
13341
- "assumptions",
13342
- "warnings",
13343
- "compiled_by",
13344
- "vocabulary",
13345
- "created_at"
13346
- ]
13347
- }, { "type": "null" }] }
13350
+ "explanation": { "type": "string" },
13351
+ "assumptions": {
13352
+ "type": "array",
13353
+ "items": { "type": "string" }
13354
+ },
13355
+ "warnings": {
13356
+ "type": "array",
13357
+ "items": { "type": "string" }
13358
+ },
13359
+ "compiled_by": {
13360
+ "type": "string",
13361
+ "description": "Who produced the tree, as stated when saved (not verified), such as rule-compiler-v1 or member."
13362
+ },
13363
+ "vocabulary": { "type": "string" },
13364
+ "created_at": {
13365
+ "type": "string",
13366
+ "format": "date-time"
13367
+ },
13368
+ "sql": {
13369
+ "description": "Present only when include_sql was true.",
13370
+ "type": "object",
13371
+ "properties": {
13372
+ "expression": {
13373
+ "type": "string",
13374
+ "description": "A parameterized boolean expression over one row per email."
13375
+ },
13376
+ "params": {
13377
+ "type": "array",
13378
+ "items": {},
13379
+ "description": "The values bound to $1, $2 and so on."
13380
+ }
13381
+ },
13382
+ "required": ["expression", "params"]
13383
+ }
13384
+ },
13385
+ "required": [
13386
+ "version",
13387
+ "english",
13388
+ "tree",
13389
+ "explanation",
13390
+ "assumptions",
13391
+ "warnings",
13392
+ "compiled_by",
13393
+ "vocabulary",
13394
+ "created_at"
13395
+ ]
13396
+ }, { "type": "null" }]
13397
+ }
13348
13398
  },
13349
13399
  "required": [
13350
13400
  "id",
@@ -13613,7 +13663,7 @@ const operationManifest = [
13613
13663
  "binaryResponse": false,
13614
13664
  "bodyRequired": true,
13615
13665
  "command": "compile-email-rule",
13616
- "description": "Turns a plain-English description of which received emails to match into a rule tree over the published column contract, with a plain-English explanation of what it matches, the assumptions made, and warnings for anything the columns cannot express. Where the columns fall short the rule may carry up to three yes/no questions, asked about an email only after its conditions match. Nothing is saved. A rule with no questions has its conditions counted, read only, over the caller's recent classified mail (what a test of it covers by default), so a warning can say when it matches nearly all of it. The SQL the rule renders to is returned only when include_sql is true. kind do_not_share compiles a do-not-share rule for recall: conditions only as narrow as the description says, the judgement of what it describes put into questions, and with nothing a column decides for certain, conditions that hold for every email. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
13666
+ "description": "Turns a plain-English description of which received emails to match into a rule tree over the published column contract, with a plain-English explanation of what it matches, the assumptions made, and warnings for anything the columns cannot express. Where the columns fall short the rule may carry up to three yes/no questions, asked about an email only after its conditions match. Nothing is saved. A rule with no questions has its conditions counted, read only, over the caller's recent classified mail (what a test of it covers by default): breadth is that count, and a warning says when it matches nearly all of it. The SQL the rule renders to is returned only when include_sql is true. kind do_not_share compiles a do-not-share rule for recall: conditions only as narrow as the description says, the judgement of what it describes put into questions, and with nothing a column decides for certain, conditions that hold for every email. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
13617
13667
  "hasJsonBody": true,
13618
13668
  "method": "POST",
13619
13669
  "operationId": "compileEmailRule",
@@ -13686,7 +13736,7 @@ const operationManifest = [
13686
13736
  },
13687
13737
  "explanation": {
13688
13738
  "type": "string",
13689
- "description": "What the rule matches, in plain English."
13739
+ "description": "What the rule matches, in plain English: only which emails, not what it grants or shares, which follows from its state, its scope and the keys, webhooks and people it reaches. The compiler leaves out a sentence that plainly says what the rule hands over."
13690
13740
  },
13691
13741
  "assumptions": {
13692
13742
  "type": "array",
@@ -13702,6 +13752,25 @@ const operationManifest = [
13702
13752
  "type": "string",
13703
13753
  "description": "An opaque name for the version of the rule compiler that produced the rule, such as rule-compiler-v1. Send it back as compiled_by when you save the rule."
13704
13754
  },
13755
+ "breadth": {
13756
+ "description": "How many of your recent emails its conditions hold for, counted over your own mailboxes whatever scope you save it with, warning or not. Null when not counted in full: a rule with questions, headers-only access, no test of it possible as you, or a count that failed or ran out of time. Writes ignore it, so a compile answer (without include_sql) can be sent back unchanged.",
13757
+ "anyOf": [{
13758
+ "type": "object",
13759
+ "properties": {
13760
+ "checked": {
13761
+ "type": "integer",
13762
+ "minimum": 0,
13763
+ "description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in your own mailboxes (for a grant rule, those you may share), the ones you can still read."
13764
+ },
13765
+ "matched": {
13766
+ "type": "integer",
13767
+ "minimum": 0,
13768
+ "description": "Of those, how many they hold for (for a do-not-share rule, one they cannot decide counts)."
13769
+ }
13770
+ },
13771
+ "required": ["checked", "matched"]
13772
+ }, { "type": "null" }]
13773
+ },
13705
13774
  "sql": {
13706
13775
  "description": "Present only when include_sql was true.",
13707
13776
  "type": "object",
@@ -13897,6 +13966,25 @@ const operationManifest = [
13897
13966
  "type": "string",
13898
13967
  "minLength": 1,
13899
13968
  "maxLength": 64
13969
+ },
13970
+ "breadth": {
13971
+ "description": "Ignored (the compile answer's breadth).",
13972
+ "anyOf": [{
13973
+ "type": "object",
13974
+ "properties": {
13975
+ "checked": {
13976
+ "type": "integer",
13977
+ "minimum": 0,
13978
+ "description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in your own mailboxes (for a grant rule, those you may share), the ones you can still read."
13979
+ },
13980
+ "matched": {
13981
+ "type": "integer",
13982
+ "minimum": 0,
13983
+ "description": "Of those, how many they hold for (for a do-not-share rule, one they cannot decide counts)."
13984
+ }
13985
+ },
13986
+ "required": ["checked", "matched"]
13987
+ }, { "type": "null" }]
13900
13988
  }
13901
13989
  },
13902
13990
  "required": [
@@ -13957,10 +14045,37 @@ const operationManifest = [
13957
14045
  "type": ["integer", "null"],
13958
14046
  "exclusiveMinimum": 0
13959
14047
  },
14048
+ "revision": {
14049
+ "description": "Moves on by one with every update. Send it as if_revision.",
14050
+ "type": "integer",
14051
+ "exclusiveMinimum": 0
14052
+ },
13960
14053
  "author_user_id": {
13961
14054
  "type": "string",
13962
14055
  "format": "uuid"
13963
14056
  },
14057
+ "author": {
14058
+ "type": "object",
14059
+ "properties": {
14060
+ "user_id": {
14061
+ "type": "string",
14062
+ "format": "uuid"
14063
+ },
14064
+ "name": {
14065
+ "description": "Their name; null unless you can list members or this is you, and when they have none set.",
14066
+ "type": ["string", "null"]
14067
+ },
14068
+ "email": {
14069
+ "description": "Sign-in email; null unless you can list member emails or this is you.",
14070
+ "type": ["string", "null"]
14071
+ }
14072
+ },
14073
+ "required": [
14074
+ "user_id",
14075
+ "name",
14076
+ "email"
14077
+ ]
14078
+ },
13964
14079
  "is_author": {
13965
14080
  "type": "boolean",
13966
14081
  "description": "Whether you are its author, in your current membership. Only its author edits it; others who see it may only disable or delete it (while shared, only if they manage members)."
@@ -13973,91 +14088,94 @@ const operationManifest = [
13973
14088
  "type": "string",
13974
14089
  "format": "date-time"
13975
14090
  },
13976
- "current": { "anyOf": [{
13977
- "type": "object",
13978
- "properties": {
13979
- "version": {
13980
- "type": "integer",
13981
- "exclusiveMinimum": 0
13982
- },
13983
- "english": { "type": "string" },
13984
- "tree": {
13985
- "type": "object",
13986
- "properties": {
13987
- "version": {
13988
- "type": "number",
13989
- "const": 1
13990
- },
13991
- "vocabulary": {
13992
- "type": "string",
13993
- "description": "Vocabulary version, such as v1."
13994
- },
13995
- "when": {
13996
- "type": "object",
13997
- "propertyNames": { "type": "string" },
13998
- "additionalProperties": {},
13999
- "description": "A condition {column, op, value?, min_confidence?} (see GET /email-rules/vocabulary) or {all: [...]}, {any: [...]}, {not: ...}."
14000
- },
14001
- "questions": {
14002
- "type": "array",
14003
- "items": { "type": "string" },
14004
- "description": "Yes/no questions asked once the conditions match; all must be yes."
14005
- }
14091
+ "current": {
14092
+ "description": "Its current version: its text (english, tree, explanation, assumptions, warnings) and who compiled it. current_version is its number. GET /email-rules lists rules without it.",
14093
+ "anyOf": [{
14094
+ "type": "object",
14095
+ "properties": {
14096
+ "version": {
14097
+ "type": "integer",
14098
+ "exclusiveMinimum": 0
14006
14099
  },
14007
- "required": [
14008
- "version",
14009
- "vocabulary",
14010
- "when",
14011
- "questions"
14012
- ]
14013
- },
14014
- "explanation": { "type": "string" },
14015
- "assumptions": {
14016
- "type": "array",
14017
- "items": { "type": "string" }
14018
- },
14019
- "warnings": {
14020
- "type": "array",
14021
- "items": { "type": "string" }
14022
- },
14023
- "compiled_by": {
14024
- "type": "string",
14025
- "description": "Who produced the tree, as stated when saved (not verified), such as rule-compiler-v1 or member."
14026
- },
14027
- "vocabulary": { "type": "string" },
14028
- "created_at": {
14029
- "type": "string",
14030
- "format": "date-time"
14031
- },
14032
- "sql": {
14033
- "description": "Present only when include_sql was true.",
14034
- "type": "object",
14035
- "properties": {
14036
- "expression": {
14037
- "type": "string",
14038
- "description": "A parameterized boolean expression over one row per email."
14100
+ "english": { "type": "string" },
14101
+ "tree": {
14102
+ "type": "object",
14103
+ "properties": {
14104
+ "version": {
14105
+ "type": "number",
14106
+ "const": 1
14107
+ },
14108
+ "vocabulary": {
14109
+ "type": "string",
14110
+ "description": "Vocabulary version, such as v1."
14111
+ },
14112
+ "when": {
14113
+ "type": "object",
14114
+ "propertyNames": { "type": "string" },
14115
+ "additionalProperties": {},
14116
+ "description": "A condition {column, op, value?, min_confidence?} (see GET /email-rules/vocabulary) or {all: [...]}, {any: [...]}, {not: ...}."
14117
+ },
14118
+ "questions": {
14119
+ "type": "array",
14120
+ "items": { "type": "string" },
14121
+ "description": "Yes/no questions asked once the conditions match; all must be yes."
14122
+ }
14039
14123
  },
14040
- "params": {
14041
- "type": "array",
14042
- "items": {},
14043
- "description": "The values bound to $1, $2 and so on."
14044
- }
14124
+ "required": [
14125
+ "version",
14126
+ "vocabulary",
14127
+ "when",
14128
+ "questions"
14129
+ ]
14045
14130
  },
14046
- "required": ["expression", "params"]
14047
- }
14048
- },
14049
- "required": [
14050
- "version",
14051
- "english",
14052
- "tree",
14053
- "explanation",
14054
- "assumptions",
14055
- "warnings",
14056
- "compiled_by",
14057
- "vocabulary",
14058
- "created_at"
14059
- ]
14060
- }, { "type": "null" }] },
14131
+ "explanation": { "type": "string" },
14132
+ "assumptions": {
14133
+ "type": "array",
14134
+ "items": { "type": "string" }
14135
+ },
14136
+ "warnings": {
14137
+ "type": "array",
14138
+ "items": { "type": "string" }
14139
+ },
14140
+ "compiled_by": {
14141
+ "type": "string",
14142
+ "description": "Who produced the tree, as stated when saved (not verified), such as rule-compiler-v1 or member."
14143
+ },
14144
+ "vocabulary": { "type": "string" },
14145
+ "created_at": {
14146
+ "type": "string",
14147
+ "format": "date-time"
14148
+ },
14149
+ "sql": {
14150
+ "description": "Present only when include_sql was true.",
14151
+ "type": "object",
14152
+ "properties": {
14153
+ "expression": {
14154
+ "type": "string",
14155
+ "description": "A parameterized boolean expression over one row per email."
14156
+ },
14157
+ "params": {
14158
+ "type": "array",
14159
+ "items": {},
14160
+ "description": "The values bound to $1, $2 and so on."
14161
+ }
14162
+ },
14163
+ "required": ["expression", "params"]
14164
+ }
14165
+ },
14166
+ "required": [
14167
+ "version",
14168
+ "english",
14169
+ "tree",
14170
+ "explanation",
14171
+ "assumptions",
14172
+ "warnings",
14173
+ "compiled_by",
14174
+ "vocabulary",
14175
+ "created_at"
14176
+ ]
14177
+ }, { "type": "null" }]
14178
+ },
14061
14179
  "scope_warnings": {
14062
14180
  "description": "Names in scope_selection that match nothing this organization receives mail at (a typo, or an inbox not yet in use). Present only when there are some.",
14063
14181
  "type": "array",
@@ -14089,7 +14207,7 @@ const operationManifest = [
14089
14207
  }
14090
14208
  },
14091
14209
  "expectations_check": {
14092
- "description": "After a write that saved a version or changed its expectations, if it has some: them, judged on that version from stored classifications without asking its questions. Undecided: one that needs answers not yet kept for that version, is unclassified, skipped or not reached, or that its author does not read. Absent when it could not be judged (the rule changed again, its author cannot be resolved, your access shows headers only, it could not finish in time, or the check failed). Test by rule_id for the rest.",
14210
+ "description": "After a write that saved a version or changed its expectations, if it has some: them, judged on that version from stored classifications without asking its questions. Undecided: one that needs answers not yet kept for that version, is unclassified, skipped or not reached, or that its author does not read. Absent when it could not be judged (the rule changed again, its author cannot be resolved, your access shows headers only, it could not finish in time, or the check failed). Test by rule_id for the rest. With if_expectations_pass, the judgment made before the save, which passed.",
14093
14211
  "type": "object",
14094
14212
  "properties": {
14095
14213
  "version": {
@@ -14117,6 +14235,10 @@ const operationManifest = [
14117
14235
  "type": "string",
14118
14236
  "format": "uuid"
14119
14237
  }
14238
+ },
14239
+ "note": {
14240
+ "description": "On an update that saved a version that did not pass: that it was saved anyway, is current, and whether it is live.",
14241
+ "type": "string"
14120
14242
  }
14121
14243
  },
14122
14244
  "required": [
@@ -14125,6 +14247,41 @@ const operationManifest = [
14125
14247
  "failed",
14126
14248
  "undecided"
14127
14249
  ]
14250
+ },
14251
+ "restored_from": {
14252
+ "description": "When this write saved an earlier version again (restore_version): which, and that the scope is not part of a version.",
14253
+ "type": "object",
14254
+ "properties": {
14255
+ "version": {
14256
+ "type": "integer",
14257
+ "exclusiveMinimum": 0
14258
+ },
14259
+ "note": { "type": "string" }
14260
+ },
14261
+ "required": ["version", "note"]
14262
+ },
14263
+ "affects": {
14264
+ "description": "After an update that saved a version: API keys listing the rule, the webhooks they own, and shares this write ended. The rule keeps its state. Absent if not read.",
14265
+ "type": "object",
14266
+ "properties": {
14267
+ "keys": {
14268
+ "type": "integer",
14269
+ "minimum": 0
14270
+ },
14271
+ "endpoints": {
14272
+ "type": "integer",
14273
+ "minimum": 0
14274
+ },
14275
+ "shares_ended": {
14276
+ "type": "integer",
14277
+ "minimum": 0
14278
+ }
14279
+ },
14280
+ "required": [
14281
+ "keys",
14282
+ "endpoints",
14283
+ "shares_ended"
14284
+ ]
14128
14285
  }
14129
14286
  },
14130
14287
  "required": [
@@ -14258,6 +14415,25 @@ const operationManifest = [
14258
14415
  "type": "string",
14259
14416
  "minLength": 1,
14260
14417
  "maxLength": 64
14418
+ },
14419
+ "breadth": {
14420
+ "description": "Ignored (the compile answer's breadth).",
14421
+ "anyOf": [{
14422
+ "type": "object",
14423
+ "properties": {
14424
+ "checked": {
14425
+ "type": "integer",
14426
+ "minimum": 0,
14427
+ "description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in your own mailboxes (for a grant rule, those you may share), the ones you can still read."
14428
+ },
14429
+ "matched": {
14430
+ "type": "integer",
14431
+ "minimum": 0,
14432
+ "description": "Of those, how many they hold for (for a do-not-share rule, one they cannot decide counts)."
14433
+ }
14434
+ },
14435
+ "required": ["checked", "matched"]
14436
+ }, { "type": "null" }]
14261
14437
  }
14262
14438
  },
14263
14439
  "required": [
@@ -14796,7 +14972,7 @@ const operationManifest = [
14796
14972
  "binaryResponse": false,
14797
14973
  "bodyRequired": false,
14798
14974
  "command": "get-email-rule",
14799
- "description": "A rule, its scope (evaluation_scope, and scope_selection for a selected rule), its current version and its saved expectations. What the scope comes to for its author now is GET /email-rules/{id}/scope. The SQL the version renders to is included only when include_sql is true. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
14975
+ "description": "A rule, its scope (evaluation_scope, and scope_selection for a selected rule), its current version and its saved expectations. The current version's text (english, tree, explanation, assumptions, warnings) is under current, and current_version is its number. What the scope comes to for its author now is GET /email-rules/{id}/scope. The SQL the version renders to is included only when include_sql is true. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
14800
14976
  "hasJsonBody": false,
14801
14977
  "method": "GET",
14802
14978
  "operationId": "getEmailRule",
@@ -14870,10 +15046,37 @@ const operationManifest = [
14870
15046
  "type": ["integer", "null"],
14871
15047
  "exclusiveMinimum": 0
14872
15048
  },
15049
+ "revision": {
15050
+ "description": "Moves on by one with every update. Send it as if_revision.",
15051
+ "type": "integer",
15052
+ "exclusiveMinimum": 0
15053
+ },
14873
15054
  "author_user_id": {
14874
15055
  "type": "string",
14875
15056
  "format": "uuid"
14876
15057
  },
15058
+ "author": {
15059
+ "type": "object",
15060
+ "properties": {
15061
+ "user_id": {
15062
+ "type": "string",
15063
+ "format": "uuid"
15064
+ },
15065
+ "name": {
15066
+ "description": "Their name; null unless you can list members or this is you, and when they have none set.",
15067
+ "type": ["string", "null"]
15068
+ },
15069
+ "email": {
15070
+ "description": "Sign-in email; null unless you can list member emails or this is you.",
15071
+ "type": ["string", "null"]
15072
+ }
15073
+ },
15074
+ "required": [
15075
+ "user_id",
15076
+ "name",
15077
+ "email"
15078
+ ]
15079
+ },
14877
15080
  "is_author": {
14878
15081
  "type": "boolean",
14879
15082
  "description": "Whether you are its author, in your current membership. Only its author edits it; others who see it may only disable or delete it (while shared, only if they manage members)."
@@ -14886,91 +15089,94 @@ const operationManifest = [
14886
15089
  "type": "string",
14887
15090
  "format": "date-time"
14888
15091
  },
14889
- "current": { "anyOf": [{
14890
- "type": "object",
14891
- "properties": {
14892
- "version": {
14893
- "type": "integer",
14894
- "exclusiveMinimum": 0
14895
- },
14896
- "english": { "type": "string" },
14897
- "tree": {
14898
- "type": "object",
14899
- "properties": {
14900
- "version": {
14901
- "type": "number",
14902
- "const": 1
14903
- },
14904
- "vocabulary": {
14905
- "type": "string",
14906
- "description": "Vocabulary version, such as v1."
14907
- },
14908
- "when": {
14909
- "type": "object",
14910
- "propertyNames": { "type": "string" },
14911
- "additionalProperties": {},
14912
- "description": "A condition {column, op, value?, min_confidence?} (see GET /email-rules/vocabulary) or {all: [...]}, {any: [...]}, {not: ...}."
14913
- },
14914
- "questions": {
14915
- "type": "array",
14916
- "items": { "type": "string" },
14917
- "description": "Yes/no questions asked once the conditions match; all must be yes."
14918
- }
15092
+ "current": {
15093
+ "description": "Its current version: its text (english, tree, explanation, assumptions, warnings) and who compiled it. current_version is its number. GET /email-rules lists rules without it.",
15094
+ "anyOf": [{
15095
+ "type": "object",
15096
+ "properties": {
15097
+ "version": {
15098
+ "type": "integer",
15099
+ "exclusiveMinimum": 0
14919
15100
  },
14920
- "required": [
14921
- "version",
14922
- "vocabulary",
14923
- "when",
14924
- "questions"
14925
- ]
14926
- },
14927
- "explanation": { "type": "string" },
14928
- "assumptions": {
14929
- "type": "array",
14930
- "items": { "type": "string" }
14931
- },
14932
- "warnings": {
14933
- "type": "array",
14934
- "items": { "type": "string" }
14935
- },
14936
- "compiled_by": {
14937
- "type": "string",
14938
- "description": "Who produced the tree, as stated when saved (not verified), such as rule-compiler-v1 or member."
14939
- },
14940
- "vocabulary": { "type": "string" },
14941
- "created_at": {
14942
- "type": "string",
14943
- "format": "date-time"
14944
- },
14945
- "sql": {
14946
- "description": "Present only when include_sql was true.",
14947
- "type": "object",
14948
- "properties": {
14949
- "expression": {
14950
- "type": "string",
14951
- "description": "A parameterized boolean expression over one row per email."
15101
+ "english": { "type": "string" },
15102
+ "tree": {
15103
+ "type": "object",
15104
+ "properties": {
15105
+ "version": {
15106
+ "type": "number",
15107
+ "const": 1
15108
+ },
15109
+ "vocabulary": {
15110
+ "type": "string",
15111
+ "description": "Vocabulary version, such as v1."
15112
+ },
15113
+ "when": {
15114
+ "type": "object",
15115
+ "propertyNames": { "type": "string" },
15116
+ "additionalProperties": {},
15117
+ "description": "A condition {column, op, value?, min_confidence?} (see GET /email-rules/vocabulary) or {all: [...]}, {any: [...]}, {not: ...}."
15118
+ },
15119
+ "questions": {
15120
+ "type": "array",
15121
+ "items": { "type": "string" },
15122
+ "description": "Yes/no questions asked once the conditions match; all must be yes."
15123
+ }
14952
15124
  },
14953
- "params": {
14954
- "type": "array",
14955
- "items": {},
14956
- "description": "The values bound to $1, $2 and so on."
14957
- }
15125
+ "required": [
15126
+ "version",
15127
+ "vocabulary",
15128
+ "when",
15129
+ "questions"
15130
+ ]
14958
15131
  },
14959
- "required": ["expression", "params"]
14960
- }
14961
- },
14962
- "required": [
14963
- "version",
14964
- "english",
14965
- "tree",
14966
- "explanation",
14967
- "assumptions",
14968
- "warnings",
14969
- "compiled_by",
14970
- "vocabulary",
14971
- "created_at"
14972
- ]
14973
- }, { "type": "null" }] },
15132
+ "explanation": { "type": "string" },
15133
+ "assumptions": {
15134
+ "type": "array",
15135
+ "items": { "type": "string" }
15136
+ },
15137
+ "warnings": {
15138
+ "type": "array",
15139
+ "items": { "type": "string" }
15140
+ },
15141
+ "compiled_by": {
15142
+ "type": "string",
15143
+ "description": "Who produced the tree, as stated when saved (not verified), such as rule-compiler-v1 or member."
15144
+ },
15145
+ "vocabulary": { "type": "string" },
15146
+ "created_at": {
15147
+ "type": "string",
15148
+ "format": "date-time"
15149
+ },
15150
+ "sql": {
15151
+ "description": "Present only when include_sql was true.",
15152
+ "type": "object",
15153
+ "properties": {
15154
+ "expression": {
15155
+ "type": "string",
15156
+ "description": "A parameterized boolean expression over one row per email."
15157
+ },
15158
+ "params": {
15159
+ "type": "array",
15160
+ "items": {},
15161
+ "description": "The values bound to $1, $2 and so on."
15162
+ }
15163
+ },
15164
+ "required": ["expression", "params"]
15165
+ }
15166
+ },
15167
+ "required": [
15168
+ "version",
15169
+ "english",
15170
+ "tree",
15171
+ "explanation",
15172
+ "assumptions",
15173
+ "warnings",
15174
+ "compiled_by",
15175
+ "vocabulary",
15176
+ "created_at"
15177
+ ]
15178
+ }, { "type": "null" }]
15179
+ },
14974
15180
  "expectations": {
14975
15181
  "description": "Its saved expectations, each list by email id.",
14976
15182
  "type": "object",
@@ -17144,7 +17350,7 @@ const operationManifest = [
17144
17350
  "binaryResponse": false,
17145
17351
  "bodyRequired": false,
17146
17352
  "command": "list-email-rules",
17147
- "description": "The rules the caller can see, newest first: their own, or every member's rules for a caller who reads the whole organization's mail. Each carries its scope (evaluation_scope, and scope_selection for a selected rule). With matched_email_id, only the rules that matched that email. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
17353
+ "description": "The rules the caller can see, newest first: their own, or every member's rules for a caller who reads the whole organization's mail. Each carries its scope (evaluation_scope, and scope_selection for a selected rule) and current_version, but not the version's text: read that under current from GET /email-rules/{id}. With matched_email_id, only the rules that matched that email. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
17148
17354
  "hasJsonBody": false,
17149
17355
  "method": "GET",
17150
17356
  "operationId": "listEmailRules",
@@ -17233,10 +17439,37 @@ const operationManifest = [
17233
17439
  "type": ["integer", "null"],
17234
17440
  "exclusiveMinimum": 0
17235
17441
  },
17442
+ "revision": {
17443
+ "description": "Moves on by one with every update. Send it as if_revision.",
17444
+ "type": "integer",
17445
+ "exclusiveMinimum": 0
17446
+ },
17236
17447
  "author_user_id": {
17237
17448
  "type": "string",
17238
17449
  "format": "uuid"
17239
17450
  },
17451
+ "author": {
17452
+ "type": "object",
17453
+ "properties": {
17454
+ "user_id": {
17455
+ "type": "string",
17456
+ "format": "uuid"
17457
+ },
17458
+ "name": {
17459
+ "description": "Their name; null unless you can list members or this is you, and when they have none set.",
17460
+ "type": ["string", "null"]
17461
+ },
17462
+ "email": {
17463
+ "description": "Sign-in email; null unless you can list member emails or this is you.",
17464
+ "type": ["string", "null"]
17465
+ }
17466
+ },
17467
+ "required": [
17468
+ "user_id",
17469
+ "name",
17470
+ "email"
17471
+ ]
17472
+ },
17240
17473
  "is_author": {
17241
17474
  "type": "boolean",
17242
17475
  "description": "Whether you are its author, in your current membership. Only its author edits it; others who see it may only disable or delete it (while shared, only if they manage members)."
@@ -18437,7 +18670,7 @@ const operationManifest = [
18437
18670
  "binaryResponse": false,
18438
18671
  "bodyRequired": true,
18439
18672
  "command": "update-email-rule",
18440
- "description": "Renames the rule, saves a new version (english and tree together, with an explanation or one the server writes; earlier versions stay), changes its state (draft is off; observe; active), whose mail it runs over (evaluation_scope, with scope_selection when selected) or its saved expectations (each saved version and new set is judged in expectations_check). Anyone but its author who sees it may only disable it (state draft), and while it is shared only if they manage members from the dashboard. Without evaluation_scope the scope stays; a scope change saves a new version with the same conditions, and what was granted stays. A shared rule keeps its scope; a kind never changes. Send if_version, the current_version you read, so a write never replaces a version saved since. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
18673
+ "description": "Renames the rule, saves a new version (english and tree together, with an explanation or one the server writes; earlier versions stay) or an earlier one again (restore_version), changes its state (draft is off; observe; active), whose mail it runs over (evaluation_scope, with scope_selection when selected) or its saved expectations (each saved version and new set is judged in expectations_check). Anyone but its author who sees it may only disable it (state draft), and while it is shared only if they manage members from the dashboard. Without evaluation_scope the scope stays; a scope change saves a new version with the same conditions, and what was granted stays. A shared rule keeps its scope; a kind never changes. A saved version takes effect at once (an active rule stays active); affects counts what it reaches. Send if_version, the current_version you read, so a write never replaces a version saved since, or if_revision to refuse it after any update. Without if_expectations_pass a version is saved even when its expectations_check fails. Needs mail:read, a member's own credential with their full access (not an API key, agent key or narrowed token), and the email rules feature.",
18441
18674
  "hasJsonBody": true,
18442
18675
  "method": "PATCH",
18443
18676
  "operationId": "updateEmailRule",
@@ -18539,6 +18772,25 @@ const operationManifest = [
18539
18772
  "minLength": 1,
18540
18773
  "maxLength": 64
18541
18774
  },
18775
+ "breadth": {
18776
+ "description": "Ignored (the compile answer's breadth).",
18777
+ "anyOf": [{
18778
+ "type": "object",
18779
+ "properties": {
18780
+ "checked": {
18781
+ "type": "integer",
18782
+ "minimum": 0,
18783
+ "description": "Classified emails the conditions were decided for: of the newest 200 from the last 30 days in your own mailboxes (for a grant rule, those you may share), the ones you can still read."
18784
+ },
18785
+ "matched": {
18786
+ "type": "integer",
18787
+ "minimum": 0,
18788
+ "description": "Of those, how many they hold for (for a do-not-share rule, one they cannot decide counts)."
18789
+ }
18790
+ },
18791
+ "required": ["checked", "matched"]
18792
+ }, { "type": "null" }]
18793
+ },
18542
18794
  "evaluation_scope": {
18543
18795
  "type": "string",
18544
18796
  "enum": [
@@ -18576,7 +18828,7 @@ const operationManifest = [
18576
18828
  "description": "For a selected rule: inboxes, and domains (each address at them the author reads); each one you can read."
18577
18829
  },
18578
18830
  "if_version": {
18579
- "description": "The current_version you read. If the rule's now differs, nothing changes: 409 email_rule_conflict, details.reason version_moved and details.current_version. Send it with a new version so a concurrent edit is never replaced unseen. It guards versions only: a change to expectations alone saves none.",
18831
+ "description": "The current_version you read: if it has moved, nothing is saved (409 email_rule_conflict, version_moved). It guards versions only; if_revision guards every update.",
18580
18832
  "type": "integer",
18581
18833
  "minimum": 1
18582
18834
  },
@@ -18602,6 +18854,22 @@ const operationManifest = [
18602
18854
  },
18603
18855
  "additionalProperties": false,
18604
18856
  "description": "Replaces its saved expectations (author only); null removes them."
18857
+ },
18858
+ "if_revision": {
18859
+ "description": "The revision you read: if the rule was updated since, nothing is saved (409 email_rule_conflict, revision_moved).",
18860
+ "type": "integer",
18861
+ "minimum": 1,
18862
+ "maximum": 2147483647
18863
+ },
18864
+ "if_expectations_pass": {
18865
+ "description": "true: judge the rule as this write leaves it (new tree, restored version, new scope, or the version it activates) against the expectations it leaves, as POST /email-rules/test would, before saving; it can take as long as a test. Failed: 409 expectations_failed; undecided: 409 expectations_undecided (both with details.expectations_check); none to judge: 400 no_expectations. Refused writes save nothing; a pass saves unless the rule moved meanwhile (409 email_rule_conflict). Only writes that save a version or activate the rule are judged.",
18866
+ "type": "boolean"
18867
+ },
18868
+ "restore_version": {
18869
+ "description": "Save version n again as the next version (author only): its english, tree, explanation, assumptions, warnings and compiled_by. A version carries no scope: the rule keeps its own unless evaluation_scope is sent. 404 version_not_found; 400 already_current or version_not_renderable. Not with a version's own fields.",
18870
+ "type": "integer",
18871
+ "minimum": 1,
18872
+ "maximum": 2147483647
18605
18873
  }
18606
18874
  },
18607
18875
  "additionalProperties": false
@@ -18657,10 +18925,37 @@ const operationManifest = [
18657
18925
  "type": ["integer", "null"],
18658
18926
  "exclusiveMinimum": 0
18659
18927
  },
18928
+ "revision": {
18929
+ "description": "Moves on by one with every update. Send it as if_revision.",
18930
+ "type": "integer",
18931
+ "exclusiveMinimum": 0
18932
+ },
18660
18933
  "author_user_id": {
18661
18934
  "type": "string",
18662
18935
  "format": "uuid"
18663
18936
  },
18937
+ "author": {
18938
+ "type": "object",
18939
+ "properties": {
18940
+ "user_id": {
18941
+ "type": "string",
18942
+ "format": "uuid"
18943
+ },
18944
+ "name": {
18945
+ "description": "Their name; null unless you can list members or this is you, and when they have none set.",
18946
+ "type": ["string", "null"]
18947
+ },
18948
+ "email": {
18949
+ "description": "Sign-in email; null unless you can list member emails or this is you.",
18950
+ "type": ["string", "null"]
18951
+ }
18952
+ },
18953
+ "required": [
18954
+ "user_id",
18955
+ "name",
18956
+ "email"
18957
+ ]
18958
+ },
18664
18959
  "is_author": {
18665
18960
  "type": "boolean",
18666
18961
  "description": "Whether you are its author, in your current membership. Only its author edits it; others who see it may only disable or delete it (while shared, only if they manage members)."
@@ -18673,91 +18968,94 @@ const operationManifest = [
18673
18968
  "type": "string",
18674
18969
  "format": "date-time"
18675
18970
  },
18676
- "current": { "anyOf": [{
18677
- "type": "object",
18678
- "properties": {
18679
- "version": {
18680
- "type": "integer",
18681
- "exclusiveMinimum": 0
18682
- },
18683
- "english": { "type": "string" },
18684
- "tree": {
18685
- "type": "object",
18686
- "properties": {
18687
- "version": {
18688
- "type": "number",
18689
- "const": 1
18690
- },
18691
- "vocabulary": {
18692
- "type": "string",
18693
- "description": "Vocabulary version, such as v1."
18694
- },
18695
- "when": {
18696
- "type": "object",
18697
- "propertyNames": { "type": "string" },
18698
- "additionalProperties": {},
18699
- "description": "A condition {column, op, value?, min_confidence?} (see GET /email-rules/vocabulary) or {all: [...]}, {any: [...]}, {not: ...}."
18700
- },
18701
- "questions": {
18702
- "type": "array",
18703
- "items": { "type": "string" },
18704
- "description": "Yes/no questions asked once the conditions match; all must be yes."
18705
- }
18971
+ "current": {
18972
+ "description": "Its current version: its text (english, tree, explanation, assumptions, warnings) and who compiled it. current_version is its number. GET /email-rules lists rules without it.",
18973
+ "anyOf": [{
18974
+ "type": "object",
18975
+ "properties": {
18976
+ "version": {
18977
+ "type": "integer",
18978
+ "exclusiveMinimum": 0
18706
18979
  },
18707
- "required": [
18708
- "version",
18709
- "vocabulary",
18710
- "when",
18711
- "questions"
18712
- ]
18713
- },
18714
- "explanation": { "type": "string" },
18715
- "assumptions": {
18716
- "type": "array",
18717
- "items": { "type": "string" }
18718
- },
18719
- "warnings": {
18720
- "type": "array",
18721
- "items": { "type": "string" }
18722
- },
18723
- "compiled_by": {
18724
- "type": "string",
18725
- "description": "Who produced the tree, as stated when saved (not verified), such as rule-compiler-v1 or member."
18726
- },
18727
- "vocabulary": { "type": "string" },
18728
- "created_at": {
18729
- "type": "string",
18730
- "format": "date-time"
18731
- },
18732
- "sql": {
18733
- "description": "Present only when include_sql was true.",
18734
- "type": "object",
18735
- "properties": {
18736
- "expression": {
18737
- "type": "string",
18738
- "description": "A parameterized boolean expression over one row per email."
18980
+ "english": { "type": "string" },
18981
+ "tree": {
18982
+ "type": "object",
18983
+ "properties": {
18984
+ "version": {
18985
+ "type": "number",
18986
+ "const": 1
18987
+ },
18988
+ "vocabulary": {
18989
+ "type": "string",
18990
+ "description": "Vocabulary version, such as v1."
18991
+ },
18992
+ "when": {
18993
+ "type": "object",
18994
+ "propertyNames": { "type": "string" },
18995
+ "additionalProperties": {},
18996
+ "description": "A condition {column, op, value?, min_confidence?} (see GET /email-rules/vocabulary) or {all: [...]}, {any: [...]}, {not: ...}."
18997
+ },
18998
+ "questions": {
18999
+ "type": "array",
19000
+ "items": { "type": "string" },
19001
+ "description": "Yes/no questions asked once the conditions match; all must be yes."
19002
+ }
18739
19003
  },
18740
- "params": {
18741
- "type": "array",
18742
- "items": {},
18743
- "description": "The values bound to $1, $2 and so on."
18744
- }
19004
+ "required": [
19005
+ "version",
19006
+ "vocabulary",
19007
+ "when",
19008
+ "questions"
19009
+ ]
18745
19010
  },
18746
- "required": ["expression", "params"]
18747
- }
18748
- },
18749
- "required": [
18750
- "version",
18751
- "english",
18752
- "tree",
18753
- "explanation",
18754
- "assumptions",
18755
- "warnings",
18756
- "compiled_by",
18757
- "vocabulary",
18758
- "created_at"
18759
- ]
18760
- }, { "type": "null" }] },
19011
+ "explanation": { "type": "string" },
19012
+ "assumptions": {
19013
+ "type": "array",
19014
+ "items": { "type": "string" }
19015
+ },
19016
+ "warnings": {
19017
+ "type": "array",
19018
+ "items": { "type": "string" }
19019
+ },
19020
+ "compiled_by": {
19021
+ "type": "string",
19022
+ "description": "Who produced the tree, as stated when saved (not verified), such as rule-compiler-v1 or member."
19023
+ },
19024
+ "vocabulary": { "type": "string" },
19025
+ "created_at": {
19026
+ "type": "string",
19027
+ "format": "date-time"
19028
+ },
19029
+ "sql": {
19030
+ "description": "Present only when include_sql was true.",
19031
+ "type": "object",
19032
+ "properties": {
19033
+ "expression": {
19034
+ "type": "string",
19035
+ "description": "A parameterized boolean expression over one row per email."
19036
+ },
19037
+ "params": {
19038
+ "type": "array",
19039
+ "items": {},
19040
+ "description": "The values bound to $1, $2 and so on."
19041
+ }
19042
+ },
19043
+ "required": ["expression", "params"]
19044
+ }
19045
+ },
19046
+ "required": [
19047
+ "version",
19048
+ "english",
19049
+ "tree",
19050
+ "explanation",
19051
+ "assumptions",
19052
+ "warnings",
19053
+ "compiled_by",
19054
+ "vocabulary",
19055
+ "created_at"
19056
+ ]
19057
+ }, { "type": "null" }]
19058
+ },
18761
19059
  "scope_warnings": {
18762
19060
  "description": "Names in scope_selection that match nothing this organization receives mail at (a typo, or an inbox not yet in use). Present only when there are some.",
18763
19061
  "type": "array",
@@ -18789,7 +19087,7 @@ const operationManifest = [
18789
19087
  }
18790
19088
  },
18791
19089
  "expectations_check": {
18792
- "description": "After a write that saved a version or changed its expectations, if it has some: them, judged on that version from stored classifications without asking its questions. Undecided: one that needs answers not yet kept for that version, is unclassified, skipped or not reached, or that its author does not read. Absent when it could not be judged (the rule changed again, its author cannot be resolved, your access shows headers only, it could not finish in time, or the check failed). Test by rule_id for the rest.",
19090
+ "description": "After a write that saved a version or changed its expectations, if it has some: them, judged on that version from stored classifications without asking its questions. Undecided: one that needs answers not yet kept for that version, is unclassified, skipped or not reached, or that its author does not read. Absent when it could not be judged (the rule changed again, its author cannot be resolved, your access shows headers only, it could not finish in time, or the check failed). Test by rule_id for the rest. With if_expectations_pass, the judgment made before the save, which passed.",
18793
19091
  "type": "object",
18794
19092
  "properties": {
18795
19093
  "version": {
@@ -18817,6 +19115,10 @@ const operationManifest = [
18817
19115
  "type": "string",
18818
19116
  "format": "uuid"
18819
19117
  }
19118
+ },
19119
+ "note": {
19120
+ "description": "On an update that saved a version that did not pass: that it was saved anyway, is current, and whether it is live.",
19121
+ "type": "string"
18820
19122
  }
18821
19123
  },
18822
19124
  "required": [
@@ -18825,6 +19127,41 @@ const operationManifest = [
18825
19127
  "failed",
18826
19128
  "undecided"
18827
19129
  ]
19130
+ },
19131
+ "restored_from": {
19132
+ "description": "When this write saved an earlier version again (restore_version): which, and that the scope is not part of a version.",
19133
+ "type": "object",
19134
+ "properties": {
19135
+ "version": {
19136
+ "type": "integer",
19137
+ "exclusiveMinimum": 0
19138
+ },
19139
+ "note": { "type": "string" }
19140
+ },
19141
+ "required": ["version", "note"]
19142
+ },
19143
+ "affects": {
19144
+ "description": "After an update that saved a version: API keys listing the rule, the webhooks they own, and shares this write ended. The rule keeps its state. Absent if not read.",
19145
+ "type": "object",
19146
+ "properties": {
19147
+ "keys": {
19148
+ "type": "integer",
19149
+ "minimum": 0
19150
+ },
19151
+ "endpoints": {
19152
+ "type": "integer",
19153
+ "minimum": 0
19154
+ },
19155
+ "shares_ended": {
19156
+ "type": "integer",
19157
+ "minimum": 0
19158
+ }
19159
+ },
19160
+ "required": [
19161
+ "keys",
19162
+ "endpoints",
19163
+ "shares_ended"
19164
+ ]
18828
19165
  }
18829
19166
  },
18830
19167
  "required": [
@@ -46372,11 +46709,11 @@ const operationManifest = [
46372
46709
  "description": "Whether this is the latest attempt of its delivery: the one recorded last, where a record of no attempt (a rule skip or a held send, attempt_count 0) is latest only when no attempt was made. A retry is recorded as a new attempt with its own id and attempt_count (not always higher than the one before), so an earlier attempt never changes: follow the latest one, or read attempts on any of them."
46373
46710
  },
46374
46711
  "retry_scheduled": {
46375
- "description": "Whether an automatic retry will try this delivery again. A retry is a delivery pass for the email: it sends again to the endpoint the email's routing resolves then (its recorded route, or the current endpoint for its domain), unless the email already reached it, deciding it afresh (its rules, its owner's access, whether it is still on), so true promises a pass, not a delivery to this endpoint. Null on every attempt of a delivery retried or settled on a schedule of its own, which this does not report: a held one (its release settles it, including the attempts it makes itself), a pull attempt, a rule-granted one, and a sent email's or other event's delivery. On the latest attempt that failed, is pending or was skipped in a way still owed, of an email that was not rejected and still has its content, to an endpoint that is still on, takes the event, has an owner the pass would deliver to (a revoked or suspended key, or an owner that may not receive the message, reads false) and has not received the email by another delivery: null while a delivery pass for the email is running (whether another follows is decided when it ends), true when the email has another automatic pass to come (a scheduled retry, the reclaim of a pass that stopped, or the recovery of a first pass that did not finish), and false otherwise. False on every other attempt, including every attempt a later one followed: no automatic retry of the email's delivery will try it again, which does not mean nothing else ever will.",
46712
+ "description": "Whether an automatic retry will try this delivery again. A retry is a delivery pass for the email: it sends again to the endpoint the email's routing resolves then (its recorded route, or the current endpoint for its domain), unless the email already reached it, deciding it afresh (its rules, its owner's access, whether it is still on), so true promises a pass, not a delivery to this endpoint. A rule-granted delivery is retried on a schedule of its own, to the same endpoint, deciding it afresh: on its latest attempt true while another attempt is to come (the delivery's next_attempt_at says when); null while an attempt is under way (read it again shortly), while it waits on an owner who cannot receive mail or on a held endpoint (checked again hourly, and sent once that clears), when it is past its 7-day limit, or when that could not be read just now; and false otherwise, including once the endpoint is off or already has the email. Null on every attempt of a delivery retried or settled on a schedule of its own that this does not report: a held one (its release settles it, including the attempts it makes itself), a pull attempt, and a sent email's or other event's delivery. On the latest attempt that failed, is pending or was skipped in a way still owed, of an email that was not rejected and still has its content, to an endpoint that is still on, takes the event, has an owner the pass would deliver to (a revoked or suspended key, or an owner that may not receive the message, reads false) and has not received the email by another delivery: null while a delivery pass for the email is running (whether another follows is decided when it ends), true when the email has another automatic pass to come (a scheduled retry, the reclaim of a pass that stopped, or the recovery of a first pass that did not finish), and false otherwise. False on every other attempt, including every attempt a later one followed: no automatic retry of the email's delivery will try it again, which does not mean nothing else ever will.",
46376
46713
  "type": ["boolean", "null"]
46377
46714
  },
46378
46715
  "next_attempt_at": {
46379
- "description": "When the next automatic retry of this delivery is due. Not reported: always null. Whether one is coming is retry_scheduled.",
46716
+ "description": "When the next automatic attempt of a rule-granted delivery is due: set on its latest attempt while retry_scheduled is true. The attempt runs at the first retry pass at or after this time (one runs every few minutes), so a time already past means it is due now. Null otherwise, and on every delivery that is not rule-granted, whose retries are passes for the email with no time reported (whether one is coming is retry_scheduled).",
46380
46717
  "type": ["string", "null"]
46381
46718
  },
46382
46719
  "attempts": {
@@ -46406,7 +46743,7 @@ const operationManifest = [
46406
46743
  "type": ["string", "null"]
46407
46744
  },
46408
46745
  "retry_scheduled": {
46409
- "description": "Whether an automatic retry will try this delivery again. A retry is a delivery pass for the email: it sends again to the endpoint the email's routing resolves then (its recorded route, or the current endpoint for its domain), unless the email already reached it, deciding it afresh (its rules, its owner's access, whether it is still on), so true promises a pass, not a delivery to this endpoint. Null on every attempt of a delivery retried or settled on a schedule of its own, which this does not report: a held one (its release settles it, including the attempts it makes itself), a pull attempt, a rule-granted one, and a sent email's or other event's delivery. On the latest attempt that failed, is pending or was skipped in a way still owed, of an email that was not rejected and still has its content, to an endpoint that is still on, takes the event, has an owner the pass would deliver to (a revoked or suspended key, or an owner that may not receive the message, reads false) and has not received the email by another delivery: null while a delivery pass for the email is running (whether another follows is decided when it ends), true when the email has another automatic pass to come (a scheduled retry, the reclaim of a pass that stopped, or the recovery of a first pass that did not finish), and false otherwise. False on every other attempt, including every attempt a later one followed: no automatic retry of the email's delivery will try it again, which does not mean nothing else ever will.",
46746
+ "description": "Whether an automatic retry will try this delivery again. A retry is a delivery pass for the email: it sends again to the endpoint the email's routing resolves then (its recorded route, or the current endpoint for its domain), unless the email already reached it, deciding it afresh (its rules, its owner's access, whether it is still on), so true promises a pass, not a delivery to this endpoint. A rule-granted delivery is retried on a schedule of its own, to the same endpoint, deciding it afresh: on its latest attempt true while another attempt is to come (the delivery's next_attempt_at says when); null while an attempt is under way (read it again shortly), while it waits on an owner who cannot receive mail or on a held endpoint (checked again hourly, and sent once that clears), when it is past its 7-day limit, or when that could not be read just now; and false otherwise, including once the endpoint is off or already has the email. Null on every attempt of a delivery retried or settled on a schedule of its own that this does not report: a held one (its release settles it, including the attempts it makes itself), a pull attempt, and a sent email's or other event's delivery. On the latest attempt that failed, is pending or was skipped in a way still owed, of an email that was not rejected and still has its content, to an endpoint that is still on, takes the event, has an owner the pass would deliver to (a revoked or suspended key, or an owner that may not receive the message, reads false) and has not received the email by another delivery: null while a delivery pass for the email is running (whether another follows is decided when it ends), true when the email has another automatic pass to come (a scheduled retry, the reclaim of a pass that stopped, or the recovery of a first pass that did not finish), and false otherwise. False on every other attempt, including every attempt a later one followed: no automatic retry of the email's delivery will try it again, which does not mean nothing else ever will.",
46410
46747
  "type": ["boolean", "null"]
46411
46748
  }
46412
46749
  },
@@ -46620,12 +46957,12 @@ const operationManifest = [
46620
46957
  },
46621
46958
  "retry_scheduled": {
46622
46959
  "type": ["boolean", "null"],
46623
- "description": "Whether an automatic retry will try this delivery again. A retry is a delivery pass for the email: it sends again to the endpoint the email's routing resolves then (its recorded route, or the current endpoint for its domain), unless the email already reached it, deciding it afresh (its rules, its owner's access, whether it is still on), so true promises a pass, not a delivery to this endpoint. Null on every attempt of a delivery retried or settled on a schedule of its own, which this does not report: a held one (its release settles it, including the attempts it makes itself), a pull attempt, a rule-granted one, and a sent email's or other event's delivery. On the latest attempt that failed, is pending or was skipped in a way still owed, of an email that was not rejected and still has its content, to an endpoint that is still on, takes the event, has an owner the pass would deliver to (a revoked or suspended key, or an owner that may not receive the message, reads false) and has not received the email by another delivery: null while a delivery pass for the email is running (whether another follows is decided when it ends), true when the email has another automatic pass to come (a scheduled retry, the reclaim of a pass that stopped, or the recovery of a first pass that did not finish), and false otherwise. False on every other attempt, including every attempt a later one followed: no automatic retry of the email's delivery will try it again, which does not mean nothing else ever will."
46960
+ "description": "Whether an automatic retry will try this delivery again. A retry is a delivery pass for the email: it sends again to the endpoint the email's routing resolves then (its recorded route, or the current endpoint for its domain), unless the email already reached it, deciding it afresh (its rules, its owner's access, whether it is still on), so true promises a pass, not a delivery to this endpoint. A rule-granted delivery is retried on a schedule of its own, to the same endpoint, deciding it afresh: on its latest attempt true while another attempt is to come (the delivery's next_attempt_at says when); null while an attempt is under way (read it again shortly), while it waits on an owner who cannot receive mail or on a held endpoint (checked again hourly, and sent once that clears), when it is past its 7-day limit, or when that could not be read just now; and false otherwise, including once the endpoint is off or already has the email. Null on every attempt of a delivery retried or settled on a schedule of its own that this does not report: a held one (its release settles it, including the attempts it makes itself), a pull attempt, and a sent email's or other event's delivery. On the latest attempt that failed, is pending or was skipped in a way still owed, of an email that was not rejected and still has its content, to an endpoint that is still on, takes the event, has an owner the pass would deliver to (a revoked or suspended key, or an owner that may not receive the message, reads false) and has not received the email by another delivery: null while a delivery pass for the email is running (whether another follows is decided when it ends), true when the email has another automatic pass to come (a scheduled retry, the reclaim of a pass that stopped, or the recovery of a first pass that did not finish), and false otherwise. False on every other attempt, including every attempt a later one followed: no automatic retry of the email's delivery will try it again, which does not mean nothing else ever will."
46624
46961
  },
46625
46962
  "next_attempt_at": {
46626
46963
  "type": ["string", "null"],
46627
46964
  "format": "date-time",
46628
- "description": "When the next automatic retry of this delivery is due. Not reported: always null. Whether one is coming is retry_scheduled."
46965
+ "description": "When the next automatic attempt of a rule-granted delivery is due: set on its latest attempt while retry_scheduled is true. The attempt runs at the first retry pass at or after this time (one runs every few minutes), so a time already past means it is due now. Null otherwise, and on every delivery that is not rule-granted, whose retries are passes for the email with no time reported (whether one is coming is retry_scheduled)."
46629
46966
  }
46630
46967
  },
46631
46968
  "required": [
@@ -48218,7 +48555,8 @@ function expectationsCheckLine(check, ruleId) {
48218
48555
  else if (undecided.length > 0) parts.push("none failed");
48219
48556
  if (undecided.length > 0) parts.push(`${undecided.length} undecided (${undecided.join(", ")})`);
48220
48557
  const rest = undecided.length > 0 ? ` To decide the rest, run \`primitive email-rules test-email-rule --rule-id ${ruleId}\`.` : "";
48221
- return `Expectations on ${on}: ${parts.length > 0 ? parts.join(", ") : "not passed"}.${rest}`;
48558
+ const note = typeof check.note === "string" && check.note.trim() ? ` ${check.note.trim()}` : "";
48559
+ return `Expectations on ${on}: ${parts.length > 0 ? parts.join(", ") : "not passed"}.${rest}${note}`;
48222
48560
  }
48223
48561
  /** The flag that makes a rule write's exit code follow its expectations_check. */
48224
48562
  const FAIL_ON_EXPECTATIONS_FLAG = "fail-on-expectations";
@@ -48230,7 +48568,7 @@ const FAIL_ON_EXPECTATIONS_FLAG = "fail-on-expectations";
48230
48568
  * `passed` as false or as null when nothing failed and something is
48231
48569
  * undecided; any other check that did not pass exits 1.
48232
48570
  */
48233
- function writtenExpectationsOutcome(check, sentExpectations, ruleId) {
48571
+ function writtenExpectationsOutcome(check, sentExpectations, ruleId, saved = "") {
48234
48572
  const test = `\`primitive email-rules test-email-rule --rule-id ${ruleId}\``;
48235
48573
  if (!isRecord(check)) {
48236
48574
  if (!sentExpectations) return {};
@@ -48238,7 +48576,7 @@ function writtenExpectationsOutcome(check, sentExpectations, ruleId) {
48238
48576
  exitCode: 3,
48239
48577
  error: {
48240
48578
  code: "expectations_unchecked",
48241
- message: `The expectations this write sent were not checked on it. Test them with ${test}.`
48579
+ message: `${saved}The expectations this write sent were not checked on it. Test them with ${test}.`
48242
48580
  }
48243
48581
  };
48244
48582
  }
@@ -48248,25 +48586,61 @@ function writtenExpectationsOutcome(check, sentExpectations, ruleId) {
48248
48586
  exitCode: 1,
48249
48587
  error: {
48250
48588
  code: "expectations_failed",
48251
- message: `${plural$1(failed.length, "expectation", "expectations")} failed on this write: ${failed.join(", ")}.`
48589
+ message: `${saved}${plural$1(failed.length, "expectation", "expectations")} failed on this write: ${failed.join(", ")}.`
48252
48590
  }
48253
48591
  };
48254
48592
  if (undecided.length > 0) return {
48255
48593
  exitCode: 3,
48256
48594
  error: {
48257
48595
  code: "expectations_undecided",
48258
- message: `${plural$1(undecided.length, "expectation is", "expectations are")} undecided on this write, which judges them without classifying mail or asking the rule's questions: ${undecided.join(", ")}. Test them with ${test}.`
48596
+ message: `${saved}${plural$1(undecided.length, "expectation is", "expectations are")} undecided on this write, which judges them without classifying mail or asking the rule's questions: ${undecided.join(", ")}. Test them with ${test}.`
48259
48597
  }
48260
48598
  };
48261
48599
  if (check.passed !== true) return {
48262
48600
  exitCode: 1,
48263
48601
  error: {
48264
48602
  code: "expectations_failed",
48265
- message: "The write's expectations check did not pass."
48603
+ message: `${saved}The write's expectations check did not pass.`
48266
48604
  }
48267
48605
  };
48268
48606
  return {};
48269
48607
  }
48608
+ /**
48609
+ * update-email-rule's post-save outcome says the write was saved: the server
48610
+ * judges a write before saving only when it saves a version or turns the
48611
+ * rule active, and refuses with these same codes, saving nothing.
48612
+ */
48613
+ const SAVED_UNJUDGED = "The write was saved: the server judges a write before saving only when it saves a version or turns the rule active, so not this one. ";
48614
+ /** What a restore_version write saved, as its answer says. */
48615
+ function restoredLine(restored, currentVersion) {
48616
+ if (!isRecord(restored) || typeof restored.version !== "number") return void 0;
48617
+ const note = typeof restored.note === "string" && restored.note.trim() ? ` ${restored.note.trim()}` : "";
48618
+ const as = typeof currentVersion === "number" ? `as version ${currentVersion}, now current` : "as the current version";
48619
+ return `Saved version ${restored.version} again ${as}.${note}`;
48620
+ }
48621
+ /**
48622
+ * Who an update that saved a version reaches, as its answer counts them: the
48623
+ * keys listing the rule, the webhooks those keys own, and the shares the
48624
+ * write ended.
48625
+ */
48626
+ function affectsLine(affects) {
48627
+ if (!isRecord(affects)) return void 0;
48628
+ const count = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
48629
+ const keys = count(affects.keys);
48630
+ const endpoints = count(affects.endpoints);
48631
+ const shares = count(affects.shares_ended);
48632
+ if (keys === void 0 || endpoints === void 0 || shares === void 0) return void 0;
48633
+ return `This version reaches ${plural$1(keys, "API key", "API keys")} listing the rule and ${plural$1(endpoints, "webhook", "webhooks")} those keys own; it ended ${plural$1(shares, "share", "shares")}. The rule keeps its state.`;
48634
+ }
48635
+ /** The rule's author, by name and email where the answer gives them. */
48636
+ function authorLine(author) {
48637
+ if (!isRecord(author) || typeof author.user_id !== "string") return void 0;
48638
+ const name = typeof author.name === "string" && author.name.trim() ? author.name.trim() : null;
48639
+ const email = typeof author.email === "string" && author.email.trim() ? author.email.trim() : null;
48640
+ if (name && email) return `Author: ${name} <${email}>`;
48641
+ if (name || email) return `Author: ${name ?? email}`;
48642
+ return `Author: user ${author.user_id} (name and email not shown to you)`;
48643
+ }
48270
48644
  /** The stderr report of a rule written by create-email-rule or update-email-rule. */
48271
48645
  function writtenEmailRuleReport(data, request) {
48272
48646
  if (!isRecord(data)) return { lines: [] };
@@ -48274,6 +48648,10 @@ function writtenEmailRuleReport(data, request) {
48274
48648
  const ruleId = typeof rule.id === "string" ? rule.id : "<rule-id>";
48275
48649
  const lines = scopeWarningLines(rule.scope_warnings);
48276
48650
  if (rule.kind === "do_not_share") lines.unshift(`This rule blocks sharing (kind do_not_share): while it is active, no rule shares an email it matches.${typeof rule.state === "string" && rule.state !== "active" ? " It blocks nothing until it is active." : ""}`);
48651
+ const restored = restoredLine(rule.restored_from, rule.current_version);
48652
+ if (restored) lines.push(restored);
48653
+ const reach = affectsLine(rule.affects);
48654
+ if (reach) lines.push(reach);
48277
48655
  const sentExpectations = isRecord(request.body) && isRecord(request.body.expectations);
48278
48656
  const checked = expectationsCheckLine(rule.expectations_check, ruleId);
48279
48657
  if (checked) lines.push(checked);
@@ -48281,9 +48659,13 @@ function writtenEmailRuleReport(data, request) {
48281
48659
  if (request.flags?.["fail-on-expectations"] !== true) return { lines };
48282
48660
  return {
48283
48661
  lines,
48284
- ...writtenExpectationsOutcome(rule.expectations_check, sentExpectations, ruleId)
48662
+ ...writtenExpectationsOutcome(rule.expectations_check, sentExpectations, ruleId, isUpdate(request) ? SAVED_UNJUDGED : "")
48285
48663
  };
48286
48664
  }
48665
+ /** Whether a rule write is an update: it names the rule in its path. */
48666
+ function isUpdate(request) {
48667
+ return typeof request.path?.id === "string";
48668
+ }
48287
48669
  /**
48288
48670
  * A rule write's exit code when its report cannot be built, under
48289
48671
  * --fail-on-expectations: 1 for a check that is there and did not pass, and 3
@@ -48350,9 +48732,93 @@ function versionMovedHint(payload, ruleId) {
48350
48732
  const read = `Read it again with \`primitive email-rules get-email-rule ${id}\` and reapply your change to what it shows`;
48351
48733
  return current === void 0 ? `Hint: the rule's version is not the if_version you sent, so nothing was saved, and the answer did not give its current version. ${read}.` : `Hint: the rule's current version is ${current}, not the if_version you sent, so nothing was saved. ${read}, then retry with --if-version ${current}.`;
48352
48734
  }
48353
- /** A write's --fail-on-expectations, the same on create and update. */
48735
+ /** The read-again advice every conflict ends with. */
48736
+ function readAgain(id) {
48737
+ return `Read it again with \`primitive email-rules get-email-rule ${id}\` and send the write again`;
48738
+ }
48739
+ /**
48740
+ * The hint for update-email-rule's refusals: an email_rule_conflict by its
48741
+ * details.reason (version_moved, revision_moved, expectations_moved,
48742
+ * rule_changed), and the expectations guard's expectations_failed,
48743
+ * expectations_undecided and no_expectations. Nothing was saved in any of
48744
+ * them.
48745
+ */
48746
+ function updateEmailRuleErrorHint(code, payload, ruleId, body) {
48747
+ const id = typeof ruleId === "string" && ruleId ? ruleId : "<rule-id>";
48748
+ const details = errorDetails(payload);
48749
+ const guarded = isRecord(body) && body.if_expectations_pass === true;
48750
+ if (code === "email_rule_conflict") switch (details.reason) {
48751
+ case "version_moved": return versionMovedHint(payload, ruleId);
48752
+ case "revision_moved": {
48753
+ const current = typeof details.current_revision === "number" ? details.current_revision : void 0;
48754
+ return current === void 0 ? `Hint: the rule was updated since the if_revision you sent, so nothing was saved. ${readAgain(id)} with --if-revision set to its revision.` : `Hint: the rule is now at revision ${current}, not the if_revision you sent, so nothing was saved. ${readAgain(id)}, reapplying your change to what it shows, with --if-revision ${current}.`;
48755
+ }
48756
+ case "expectations_moved": return `Hint: the rule's saved expectations changed while this write was checked against them, so nothing was saved. ${readAgain(id)}.`;
48757
+ case "rule_changed": return guarded ? `Hint: the rule changed after this write was judged against its expectations and before it was saved, so saving it would have gone unchecked; nothing was saved. ${readAgain(id)}.` : `Hint: the rule changed while this write was being saved, so nothing was saved. ${readAgain(id)}.`;
48758
+ default: return;
48759
+ }
48760
+ if (code === "expectations_failed" || code === "expectations_undecided") {
48761
+ const check = isRecord(details.expectations_check) ? details.expectations_check : {};
48762
+ const failed = Array.isArray(check.failed) ? check.failed : [];
48763
+ const undecided = Array.isArray(check.undecided) ? check.undecided : [];
48764
+ const parts = [...failed.length > 0 ? [`failed: ${failed.join(", ")}`] : [], ...undecided.length > 0 ? [`undecided: ${undecided.join(", ")}`] : []];
48765
+ return `Hint: the rule, as this write would leave it, was judged against its expectations before saving (if_expectations_pass), and nothing was saved.${parts.length > 0 ? ` Expectations ${parts.join("; ")}.` : ""}${code === "expectations_failed" ? "" : ` ${UNDECIDED_NEXT_STEP[String(details.reason)] ?? UNDECIDED_NEXT_STEP.none}`} The command exits ${updateEmailRuleErrorExitCode(payload) ?? 1}.`;
48766
+ }
48767
+ if (code === "no_expectations") return "Hint: nothing was saved. Give the rule expectations with --expectations-file (in this write or before it), or send the write without if_expectations_pass (--fail-on-expectations or --if-expectations-pass).";
48768
+ }
48769
+ /** What to do about an undecided judgment, by details.reason ("none": no reason given). */
48770
+ const UNDECIDED_NEXT_STEP = {
48771
+ none: "Some could not be decided yet (mail not classified yet, or a question not answered yet); send the write again later.",
48772
+ deadline: "The judgment ran out of time; send the write again.",
48773
+ error: "The judgment could not finish; send the write again.",
48774
+ client_gone: "The request ended before the write was saved; send it again.",
48775
+ author_unresolvable: "The rule cannot run as its author over the mail it would cover (details.author_reason), so sending the write again will not decide them."
48776
+ };
48777
+ /**
48778
+ * update-email-rule's exit code for the expectations guard's refusals: 1 when
48779
+ * one failed, or when they cannot be decided by trying again (the rule cannot
48780
+ * run as its author); 3 when trying again later can decide them.
48781
+ */
48782
+ function updateEmailRuleErrorExitCode(payload) {
48783
+ const code = isRecord(payload) ? payload.code : void 0;
48784
+ if (code === "expectations_failed") return 1;
48785
+ if (code !== "expectations_undecided") return void 0;
48786
+ return errorDetails(payload).reason === "author_unresolvable" ? 1 : 3;
48787
+ }
48788
+ /** The stderr report of compile-email-rule: how many recent emails it holds for. */
48789
+ function compileEmailRuleReport(data) {
48790
+ if (!isRecord(data)) return { lines: [] };
48791
+ const breadth = data.breadth;
48792
+ if (!isRecord(breadth) || typeof breadth.checked !== "number" || typeof breadth.matched !== "number") return { lines: [] };
48793
+ return { lines: [`Breadth: its conditions hold for ${breadth.matched} of ${plural$1(breadth.checked, "recent email", "recent emails")} checked in your own mailboxes, whatever scope you save it with.`] };
48794
+ }
48795
+ /** The stderr report of get-email-rule: who wrote it, and its revision. */
48796
+ function getEmailRuleReport(data) {
48797
+ if (!isRecord(data)) return { lines: [] };
48798
+ const lines = [];
48799
+ const author = authorLine(data.author);
48800
+ if (author) lines.push(author);
48801
+ if (typeof data.revision === "number") lines.push(`Revision ${data.revision}: pass --if-revision ${data.revision} to update-email-rule so a change made since is never replaced unseen.`);
48802
+ return { lines };
48803
+ }
48804
+ /** create-email-rule's --fail-on-expectations: the exit code follows the check made after saving. */
48354
48805
  const FAIL_ON_EXPECTATIONS_HELP = "Exit 1 when an expectation in the answer's expectations_check failed, and 3 when none failed but one is undecided or the expectations this write sent were not checked (test the rule to decide). Without it the command exits 0 whatever the check says.";
48806
+ /** update-email-rule's --fail-on-expectations: the server refuses the write. */
48807
+ const UPDATE_FAIL_ON_EXPECTATIONS_HELP = "Refuse the write unless the rule, as this write would leave it, passes its expectations. It sends if_expectations_pass (over any in --body-file or --raw-body), so the server judges a new version, a restored one, a new scope or turning the rule active before saving, and saves nothing when one failed (exit 1), one is undecided (exit 3, or 1 when the rule cannot run as its author) or the rule has none (exit 1). It can take as long as a test of the rule; the command waits for it. Judging needs a credential that reads the mail's content. A write the server does not judge (one changing only expectations, say) is saved, and the exit code then follows the answer's expectations_check, as on create. With --json, a refused write's document holds the API's error alone; a saved write's holds the rule.";
48355
48808
  const EXPECTATION_FLAG_NOTE = "Replaces any expect in --body-file or --raw-body. The answer reports them in expectations (passed, counts, results) and the exit code follows it; see --help-full.";
48809
+ /**
48810
+ * update-email-rule's --fail-on-expectations sends if_expectations_pass, over
48811
+ * any in the body. Asking for it while turning it off is refused.
48812
+ */
48813
+ function expectationsGuardBodyFields(flags) {
48814
+ if (flags["fail-on-expectations"] !== true) return [];
48815
+ if (flags["if-expectations-pass"] === false) throw usageError("--fail-on-expectations asks the server to judge the write before saving it; it cannot be combined with --no-if-expectations-pass.");
48816
+ return [{
48817
+ flag: `--${FAIL_ON_EXPECTATIONS_FLAG}`,
48818
+ property: "if_expectations_pass",
48819
+ value: true
48820
+ }];
48821
+ }
48356
48822
  const EMAIL_RULE_OPERATION_EXTENSIONS = {
48357
48823
  testEmailRule: {
48358
48824
  flags: {
@@ -48392,13 +48858,22 @@ const EMAIL_RULE_OPERATION_EXTENSIONS = {
48392
48858
  updateEmailRule: {
48393
48859
  flags: {
48394
48860
  "expectations-file": Flags.string({ description: "A JSON file holding the emails the rule must match and must not, such as {\"match\": [\"<email-id>\"], \"no_match\": [\"<email-id>\"]}, replacing its saved expectations; a file holding null removes them. Sets expectations, over any in --body-file or --raw-body; the answer judges them in expectations_check (passed, decided, failed, undecided); the report test-email-rule answers with is named expectations instead." }),
48395
- [FAIL_ON_EXPECTATIONS_FLAG]: Flags.boolean({ description: FAIL_ON_EXPECTATIONS_HELP })
48861
+ [FAIL_ON_EXPECTATIONS_FLAG]: Flags.boolean({ description: UPDATE_FAIL_ON_EXPECTATIONS_HELP })
48396
48862
  },
48397
- bodyFields: expectationsBodyFields(true),
48863
+ bodyFields: (flags) => [...expectationsBodyFields(true)(flags), ...expectationsGuardBodyFields(flags)],
48398
48864
  bodyProperties: ["expectations"],
48865
+ errorExitCode: updateEmailRuleErrorExitCode,
48399
48866
  fallbackExitCode: writtenFallbackExitCode,
48400
48867
  report: writtenEmailRuleReport
48401
48868
  },
48869
+ compileEmailRule: {
48870
+ flags: {},
48871
+ report: compileEmailRuleReport
48872
+ },
48873
+ getEmailRule: {
48874
+ flags: {},
48875
+ report: getEmailRuleReport
48876
+ },
48402
48877
  getEmailRuleScope: {
48403
48878
  flags: {},
48404
48879
  report: emailRuleScopeReport
@@ -49625,7 +50100,6 @@ function serializeErrorPayload(payload) {
49625
50100
  * outcome is unknown.
49626
50101
  */
49627
50102
  function buildThrownSendFailureEnvelope(params) {
49628
- const message = params.error instanceof Error ? params.error.message : String(params.error);
49629
50103
  const outcome = params.requestStarted ? "uncertain" : "not_sent";
49630
50104
  return {
49631
50105
  outcome,
@@ -49637,7 +50111,7 @@ function buildThrownSendFailureEnvelope(params) {
49637
50111
  }),
49638
50112
  sent: null,
49639
50113
  http_status: null,
49640
- error: { message },
50114
+ error: thrownErrorBody(params.error),
49641
50115
  follow_up_commands: params.requestStarted && params.attemptStartedAtIso ? buildSendCommandFollowUps({
49642
50116
  attemptStartedAtIso: params.attemptStartedAtIso,
49643
50117
  idempotencyKey: params.idempotencyKey,
@@ -50530,7 +51004,7 @@ function savedConnectedProfiles(configDir, exclude) {
50530
51004
  */
50531
51005
  async function otherProfiles(configDir, session, current) {
50532
51006
  if (session) try {
50533
- const { connectedProfilesForSession } = await import("./machine-session-3H-rNH8v.js").then((n) => n.s);
51007
+ const { connectedProfilesForSession } = await import("./machine-session-YQWxPePp.js").then((n) => n.s);
50534
51008
  return {
50535
51009
  inSession: true,
50536
51010
  profiles: connectedProfilesForSession(configDir, session, current)
@@ -51637,7 +52111,7 @@ const OPERATION_ERROR_HINTS = {
51637
52111
  inviteOrganizationMember: (code, body, payload) => inviteRefusalHint(code, body, payload),
51638
52112
  createEndpoint: (code, body, payload) => createEndpointRuleRequestHint(code, body, payload),
51639
52113
  updateEndpoint: (code, body, payload) => updateEndpointRuleRequestHint(code, body, payload),
51640
- updateEmailRule: (code, _body, payload, path) => code === "email_rule_conflict" ? versionMovedHint(payload, path?.id) : void 0,
52114
+ updateEmailRule: (code, body, payload, path) => updateEmailRuleErrorHint(code, payload, path?.id, body),
51641
52115
  verifyDomain: (code, _body, payload) => code === "dns_verification_failed" ? domainVerificationFailedHint(payload) : void 0,
51642
52116
  addDomain: (code, body) => {
51643
52117
  if (code === "mail_relay_unavailable") return MAIL_RELAY_UNAVAILABLE_MESSAGE;
@@ -51867,7 +52341,7 @@ function createOperationCommand(operation) {
51867
52341
  }
51868
52342
  });
51869
52343
  if (operation.sdkName === "getEmail" && extractErrorCode(errorPayload) === "not_found" && typeof parsedFlags.id === "string") await explainPendingMailNotFound(this.config.configDir, parsedFlags.id);
51870
- process.exitCode = 1;
52344
+ process.exitCode = extension?.errorExitCode?.(errorPayload) || 1;
51871
52345
  return;
51872
52346
  }
51873
52347
  if (operation.binaryResponse) {
@@ -52005,4 +52479,4 @@ const EMPTY_RESULT_HINTS = {
52005
52479
  function canonicalizeCliReferences(description) {
52006
52480
  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'`");
52007
52481
  }
52008
- export { replyExpectation as $, createCliApiClient as $t, bareAddress$1 as A, connectNextSteps as At, wakeReadCommand as B, withFlagSuggestion as Bt, terminalApprovalIo as C, sendWaitClampNotice as Ct, listenForCopyHotkey as D, thrownErrorExitCode as Dt, formatBrowserLoginInstructions as E, successfulSendOutcome as Et, readThreadContext as F, currentMailSessionKey as Ft, clearReadPendingMail as G, isEmailId as Gt, wakeRelationship as H, readCallerMailFor as Ht, sentInThread as I, detectAgentRuntime as It, recordPendingMail as J, ATTACHMENT_WAIT_MAX_MS as Jt, pendingMailPath as K, noFactsMessage as Kt, serverMuted as L, detectRuntimeSession as Lt, displayAddress as M, generatedAgentName as Mt, formatMailWakeLine as N, identitySuggestions as Nt, openBrowser as O, DEFAULT_ENROLL_NAME as Ot, latestOwnOutbound as P, loadSkillLine as Pt, readEmailInteraction as Q, createAuthenticatedCliApiClient as Qt, serverRelationship as R, runtimeSessionVariableList as Rt, promptLine as S, sendOutcomeExitCode as St, canOfferCopyHotkey as T, serializeErrorPayload as Tt, claimPendingMail as U, classificationFactLines as Ut, wakeRecipientField as V, emptyEmailListHint as Vt, clearConsumedPendingMail as W, classificationOf as Wt, interactionHumanLine as X, ConnectedAgentKeyRequiredError as Xt, removePendingMail as Y, withAttachmentWait as Yt, interactionNextActions as Z, SAVED_CLI_OAUTH_SESSION_EXPIRED_MESSAGE as Zt, writeErrorWithHints as _, formatSendFailureSummary as _t, createOperationCommand as a, RESTRICTED_ROLE_NOTE as an, apiResultHttpStatus as at, STEP_UP_TOKEN_FLAG_DESCRIPTION as b, reportSendCommandResult as bt, formatErrorPayload as c, isBuiltInRoleKey as cn, buildThrownSendFailureEnvelope as ct, isGrantRefusal as d, roleKeyText as dn, deriveSendIdempotencyKey as dt, refreshStoredCliCredentials as en, replyInteractionWarning as et, parameterFlagName as f, switchCurrentOrg as fn, excludeSignalReplies as ft, takesStepUp as g, operationManifest as gn, formatPriorRepliesWarning as gt, surfaceUnauthorizedHint as h, setKnownCommands as hn, formatPriorRepliesCheckSkipped as ht, TIME_FLAG_DESCRIPTION as i, CliOrgRefusedError as in, SEND_OUTCOME_HELP as it, describeWake as j, defaultConnectionName as jt, explainPendingMailNotFound as k, cliInvocation as kt, hasControlCharacter as l, revokeUserLogin as ln, checkPriorReplies as lt, runWithTiming as m, sanitizeForTerminal as mn, formatDeletedEarlierSendNotice as mt, API_ERROR_CODES as n, resolveCliApiRequestConfig as nn, IDEMPOTENCY_KEY_FLAG_DESCRIPTION as nt, extractErrorCode as o, credentialsFromUserLogin as on, assertValidIdempotencyKey as ot, readTextFileFlag as p, routeWarningLines as pn, followUpCommandPrefix as pt, readPendingMail as q, ATTACHMENT_WAIT_FLAG_DESCRIPTION as qt, OPERATION_PATH_FLAGS as r, selectedOrgFromEnv as rn, MAX_SEND_WAIT_TIMEOUT_MS as rt, extractErrorPayload as s, fetchCliOrgs as sn, buildFollowUpCommand as st, API_BASE_URL_FLAG_DESCRIPTION as t, refuseOrgSelectionForProfile as tn, wakeInteractionSentence as tt, headerFlagName as u, roleKeyLabel as un, classifySendError as ut, BROWSER_FLAG_DESCRIPTION as v, formatSendRecordFailureSummary as vt, withApproval as w, sentHistoryWindowStart as wt, approveThenRerunHint as x, sendIdentityFields as xt, STEP_UP_HEADER as y, priorRepliesThatWentOut as yt, skillFirstLine as z, formatAlreadySentNotice as zt };
52482
+ 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 };