@pithy-sh/support 0.6.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +3 -1
  2. package/dist/capability.d.ts +1 -1
  3. package/dist/capability.d.ts.map +1 -1
  4. package/dist/capability.js +5 -4
  5. package/dist/config/config.d.ts +7 -5
  6. package/dist/config/config.d.ts.map +1 -1
  7. package/dist/config/config.js +6 -5
  8. package/dist/http/handlers.d.ts +2 -2
  9. package/dist/http/handlers.d.ts.map +1 -1
  10. package/dist/http/handlers.js +3 -2
  11. package/dist/http/responses.d.ts +9 -5
  12. package/dist/http/responses.d.ts.map +1 -1
  13. package/dist/http/responses.js +10 -12
  14. package/dist/http/routes.d.ts +3 -3
  15. package/dist/http/routes.d.ts.map +1 -1
  16. package/dist/http/routes.js +4 -3
  17. package/dist/http/schemas.d.ts +11 -9
  18. package/dist/http/schemas.d.ts.map +1 -1
  19. package/dist/http/schemas.js +15 -8
  20. package/dist/http/scopes.d.ts.map +1 -1
  21. package/dist/http/scopes.js +2 -1
  22. package/dist/provision/provisionSupport.d.ts +19 -20
  23. package/dist/provision/provisionSupport.d.ts.map +1 -1
  24. package/dist/provision/provisionSupport.js +14 -17
  25. package/dist/provision/sendingCheck.d.ts +7 -0
  26. package/dist/provision/sendingCheck.d.ts.map +1 -0
  27. package/dist/provision/sendingCheck.js +63 -0
  28. package/dist/reply/fromName.d.ts +20 -0
  29. package/dist/reply/fromName.d.ts.map +1 -0
  30. package/dist/reply/fromName.js +29 -0
  31. package/dist/reply/send.d.ts +10 -1
  32. package/dist/reply/send.d.ts.map +1 -1
  33. package/dist/reply/send.js +16 -2
  34. package/dist/reply/sender.d.ts +16 -0
  35. package/dist/reply/sender.d.ts.map +1 -0
  36. package/dist/reply/sender.js +27 -0
  37. package/dist/store/threads.d.ts +1 -1
  38. package/dist/version.generated.d.ts +1 -1
  39. package/dist/version.generated.js +1 -1
  40. package/package.json +8 -8
  41. package/pithy.manifest.json +2 -2
  42. package/src/capability.ts +18 -15
  43. package/src/config/config.ts +13 -5
  44. package/src/http/handlers.ts +4 -3
  45. package/src/http/responses.ts +18 -13
  46. package/src/http/routes.ts +9 -8
  47. package/src/http/schemas.ts +23 -13
  48. package/src/http/scopes.ts +2 -1
  49. package/src/provision/provisionSupport.ts +20 -21
  50. package/src/provision/sendingCheck.ts +69 -0
  51. package/src/reply/fromName.ts +37 -0
  52. package/src/reply/send.ts +31 -3
  53. package/src/reply/sender.ts +35 -0
  54. package/src/store/threads.ts +1 -1
  55. package/src/version.generated.ts +1 -1
package/README.md CHANGED
@@ -12,7 +12,9 @@ pithy add support
12
12
 
13
13
  **A shared inbox.** Assign a conversation to somebody, filter the inbox by who has it, and see who else has a thread open before you reply: `POST /support/threads/:id/assign` behind `support:threads:assign`, `assignee=<ref>` or `assignee=none` on the listing, every handover kept in the thread's history, and one `support.thread` presence room per thread, with `viewing` and `replying`. Your own signed-in staff reach both through `support({ staff: { readPower, assignPower } })`, on unassignable roles other than the one ownership transfer confers, so only an operator can confer them. The seam is [`docs/CONTROL-PLANE.md`](../../docs/CONTROL-PLANE.md).
14
14
 
15
- **Search, by words and by meaning.** `GET /support/threads/search` takes `q`, a `mode` (`keyword`, `semantic` or `hybrid`), a `limit` (20, at most 50) and the inbox list's filters, and returns ranked threads, each named by its best-matching message with that message's raw `bm25` and similarity. Keyword is FTS5 with `search.fts` on and the `LIKE` scan otherwise; hybrid fuses it with semantic search by rank, so a paraphrase is found and an order number still comes first. The inbox list no longer takes `q`. **Semantic search costs a second inference per inbound message on your own Workers AI binding**, on top of classification — roughly double the per-message AI cost — so `search: { semantic: true }` is off until you turn it on. `pithy support provision` creates the Vectorize index and backfills it once, and `pithy vector reprocess --index support-messages` re-embeds after a model change, with no `vector()` composed. Vectors carry message ids only. See [`docs/commands/support.md`](../../docs/commands/support.md#search).
15
+ **Search, by words and by meaning.** `POST /support/threads/search` takes, in its body, `q`, a `mode` (`keyword`, `semantic` or `hybrid`), a `limit` (20, at most 50) and the inbox list's filters, and returns ranked threads, each named by its best-matching message with that message's raw `bm25` and similarity. Keyword is FTS5 with `search.fts` on and the `LIKE` scan otherwise; hybrid fuses it with semantic search by rank, so a paraphrase is found and an order number still comes first. The inbox list no longer takes `q`. **Semantic search costs a second inference per inbound message on your own Workers AI binding**, on top of classification — roughly double the per-message AI cost — so `search: { semantic: true }` is off until you turn it on. `pithy support provision` creates the Vectorize index and backfills it once, and `pithy vector reprocess --index support-messages` re-embeds after a model change, with no `vector()` composed. Vectors carry message ids only. See [`docs/commands/support.md`](../../docs/commands/support.md#search).
16
+
17
+ **Replies come from the inbox.** A mailed reply is sent from the address the customer wrote to, under the name of whoever answered: `"Jim Scott" <support@help.example.com>`, with that inbox as the `Reply-To`. `reply.fromName` is the display name, with `{agent}` replaced by the agent's name (default `{agent}`); with no agent name, the email capability's `fromName` is used. A fixed `reply.fromName` with no `{agent}` is used for every reply. Support declares the addresses it sends as, `inboundAddresses` and `reply.replyToAddress`, through `email().enqueueAs` (`@pithy-sh/email` ^0.9.0; an older one is refused at compose). A thread whose inbox is no longer claimed is answered from `reply.replyToAddress`, or refused when none is set. A domain Email Service cannot send from fails the job with the domain named, and nothing is sent from another address in its place. `pithy doctor` names each sending domain, inbound or reply-to, that cannot send.
16
18
 
17
19
  _Everything else is on the site. `pithy.sh/docs` is canonical — new prose goes there, not here._
18
20
 
@@ -46,7 +46,7 @@ export interface SupportWiring {
46
46
  enqueueEmail: EmailCapability["enqueue"] | undefined;
47
47
  /**
48
48
  * The optional peers the sender link reads through — `auth()`'s and `payments()`'s surfaces, set by
49
- * `compose` when each is composed. Found, never imported (#645): see `link/sender.ts`.
49
+ * `compose` when each is composed. Found, never imported: see `link/sender.ts`.
50
50
  */
51
51
  peers: SenderPeers;
52
52
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,0CAA0C,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAItE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAGxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAO9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAIjD,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAW7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG;IAChD,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,8BAA8B;IAC9B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD;;;OAGG;IACH,KAAK,EAAE,WAAW,CAAC;IACnB;;;;;;;;OAQG;IACH,IAAI,EAAE,SAAS,CAAC;CACjB;AA4ED;;;;;GAKG;AACH,MAAM,WAAW,iBACf,SAAQ,UAAU,CAAC,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,OAAO,gBAAgB,CAAC;IAC3F,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,wGAAwG;IACxG,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,iBAAiB,CAuIvE;AAED,2GAA2G;AAC3G,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,IAAI,iBAAiB,CAE3F"}
1
+ {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,0CAA0C,CAAC;AAC7F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAGxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAO9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAMjD,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAW7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG;IAChD,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,8BAA8B;IAC9B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,EAAE,eAAe,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD;;;OAGG;IACH,KAAK,EAAE,WAAW,CAAC;IACnB;;;;;;;;OAQG;IACH,IAAI,EAAE,SAAS,CAAC;CACjB;AA4ED;;;;;GAKG;AACH,MAAM,WAAW,iBACf,SAAQ,UAAU,CAAC,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,OAAO,gBAAgB,CAAC;IAC3F,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,wGAAwG;IACxG,UAAU,EAAE,iBAAiB,CAAC;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,iBAAiB,CAyIvE;AAED,2GAA2G;AAC3G,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,IAAI,iBAAiB,CAE3F"}
@@ -15,13 +15,14 @@ import { createSupportEmailHandler } from "./inbound/handler.js";
15
15
  import { senderPeers } from "./link/peers.js";
16
16
  import { SUPPORT_MIGRATIONS } from "./migrations/set.js";
17
17
  import { supportPresence } from "./presence/room.js";
18
+ import { supportSendingSettings } from "./provision/sendingCheck.js";
18
19
  import { supportUserPurge } from "./purge/contribution.js";
20
+ import { replySender } from "./reply/sender.js";
19
21
  import { supportExampleSeed } from "./seeds/example.js";
20
22
  import { PACKAGE_NAME, PACKAGE_VERSION } from "./version.generated.js";
21
23
  import { noopEmit } from "@pithy-sh/core/src/audit/recorder";
22
24
  import { defineCapability } from "@pithy-sh/core/src/capability/capability";
23
25
  import { workflowBindings } from "@pithy-sh/core/src/workflow/bindings";
24
- import { isEmailCapability } from "@pithy-sh/email/src/capability";
25
26
  //#region src/capability.ts
26
27
  /**
27
28
  * Sort order of the support migrations within the app database, relative to other capabilities.
@@ -164,7 +165,8 @@ function support(options = {}) {
164
165
  inapplicableSecrets: inapplicableAttachmentSecrets(resolved),
165
166
  workflows: supportWorkflows,
166
167
  requiredBindings,
167
- ciPermissions: ["email:routing"],
168
+ inboundRouting: { addresses: resolved.inboundAddresses },
169
+ settings: supportSendingSettings(resolved),
168
170
  databases: { app: {
169
171
  binding: "DB",
170
172
  tables: supportTables,
@@ -182,8 +184,7 @@ function support(options = {}) {
182
184
  ...resolved.staff ? { powers: [resolved.staff.readPower, resolved.staff.assignPower] } : {},
183
185
  compose: ({ capabilities }) => {
184
186
  assertStaffComposition(resolved.staff, capabilities);
185
- const email = capabilities.find(isEmailCapability);
186
- wiring.enqueueEmail = email?.enqueue;
187
+ wiring.enqueueEmail = replySender(capabilities, resolved);
187
188
  wiring.peers = senderPeers(capabilities, resolved);
188
189
  },
189
190
  client: () => clientProjection(resolved, mountPath),
@@ -128,6 +128,7 @@ export type SupportSubmissionConfig = z.output<typeof SupportSubmissionConfig>;
128
128
  export declare const SupportReplyConfig: z.ZodObject<{
129
129
  enabled: z.ZodDefault<z.ZodBoolean>;
130
130
  replyToAddress: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string | undefined, string | undefined>>;
131
+ fromName: z.ZodDefault<z.ZodString>;
131
132
  deliverInApp: z.ZodDefault<z.ZodBoolean>;
132
133
  snippets: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
133
134
  label: z.ZodString;
@@ -179,7 +180,7 @@ export type SupportSearchConfig = z.output<typeof SupportSearchConfig>;
179
180
  export declare function supportSemantic(search: SupportSearchConfig): SupportSemanticConfig | undefined;
180
181
  /** The full support configuration. */
181
182
  /**
182
- * **Who of the app's own signed-in users works the inbox (#816).** Off until named.
183
+ * **Who of the app's own signed-in users works the inbox.** Off until named.
183
184
  *
184
185
  * The inbox holds every customer's correspondence, so a signed-in user reaches it only through a power the
185
186
  * adopter names in their own role catalog, held in the organization the request acts for — the same seam
@@ -238,6 +239,7 @@ export declare const SupportConfig: z.ZodObject<{
238
239
  reply: z.ZodPrefault<z.ZodObject<{
239
240
  enabled: z.ZodDefault<z.ZodBoolean>;
240
241
  replyToAddress: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string | undefined, string | undefined>>;
242
+ fromName: z.ZodDefault<z.ZodString>;
241
243
  deliverInApp: z.ZodDefault<z.ZodBoolean>;
242
244
  snippets: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
243
245
  label: z.ZodString;
@@ -269,10 +271,10 @@ export type SupportConfigInput = z.input<typeof SupportConfig>;
269
271
  * All three default `true`, so the ordinary project is unaffected by this predicate existing.
270
272
  *
271
273
  * Two callers, and they must agree: `capability.ts` declares the binding only when this is true, and
272
- * `pithy support provision` creates the bucket only when this is true. They disagreed before #440 —
273
- * the provisioner asked about `enabled` and `retainRaw` and never about submissions, so a project that
274
- * wanted uploads but no mail attachments got a binding pointing at a bucket nothing had created, and
275
- * every submitted file was dropped with a warning. One predicate is what makes that unrepresentable.
274
+ * `pithy support provision` creates the bucket only when this is true. Were they to ask different
275
+ * questions, a project that wanted uploads but no mail attachments would get a binding pointing at a
276
+ * bucket nothing had created, and every submitted file would be dropped with a warning. One predicate is
277
+ * what makes that unrepresentable.
276
278
  */
277
279
  export declare function supportNeedsBucket(config: SupportConfig): boolean;
278
280
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AAEvE,0DAA0D;AAC1D,eAAO,MAAM,eAAe;;;;;iBA+BkF,CAAC;AAC/G,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE/D,+DAA+D;AAC/D,eAAO,MAAM,wBAAwB;;;;;iBA6BkF,CAAC;AACxH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEjF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;;;iBA8C8D,CAAC;AAC9F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;GAeG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,eAAO,MAAM,kCAAkC;;;;;iBAkC5C,CAAC;AACJ,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAErG;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,2FAA2F;AAC3F,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;iBAwCjC,CAAC;AACJ,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE/E,uCAAuC;AACvC,eAAO,MAAM,kBAAkB;;;;;;;;;iBAmC5B,CAAC;AACJ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,oBAAoB,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,oGAAoG;AACpG,eAAO,MAAM,qBAAqB;;;iBAmB2B,CAAC;AAC9D,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE3E;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;;;iBAiB7B,CAAC;AACJ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,qBAAqB,GAAG,SAAS,CAI9F;AAED,sCAAsC;AACtC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB;;;iBAiB5B,CAAC;AACJ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCvB,CAAC;AACJ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAC3D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAEjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AAEvE,0DAA0D;AAC1D,eAAO,MAAM,eAAe;;;;;iBA+BkF,CAAC;AAC/G,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE/D,+DAA+D;AAC/D,eAAO,MAAM,wBAAwB;;;;;iBA6BkF,CAAC;AACxH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEjF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;;;iBA8C8D,CAAC;AAC9F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;GAeG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,eAAO,MAAM,kCAAkC;;;;;iBAkC5C,CAAC;AACJ,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAErG;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,2FAA2F;AAC3F,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;iBAwCjC,CAAC;AACJ,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE/E,uCAAuC;AACvC,eAAO,MAAM,kBAAkB;;;;;;;;;;iBA2C5B,CAAC;AACJ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,oBAAoB,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,oGAAoG;AACpG,eAAO,MAAM,qBAAqB;;;iBAmB2B,CAAC;AAC9D,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE3E;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;;;iBAiB7B,CAAC;AACJ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,qBAAqB,GAAG,SAAS,CAI9F;AAED,sCAAsC;AACtC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB;;;iBAiB5B,CAAC;AACJ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAErE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCvB,CAAC;AACJ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAC3D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAEjE"}
@@ -128,6 +128,7 @@ const SupportSubmissionConfig = z.object({
128
128
  const SupportReplyConfig = z.object({
129
129
  enabled: z.boolean().default(true).describe("Allow replies from the control-plane route. Off makes the inbox read-only, which is a reasonable setting for a project that answers support from its own mail client."),
130
130
  replyToAddress: z.string().optional().transform((address) => address === void 0 ? void 0 : parseAddress(address) ?? normalizeAddress(address)).describe("The `Reply-To` a reply carries — the address the customer's answer comes back to, which must be one of `inboundAddresses` or the conversation ends there. Defaults to the inbox address the thread arrived on, which is almost always what you want."),
131
+ fromName: z.string().min(1).max(120).default("{agent}").describe("The display name a mailed reply is sent under, with `{agent}` replaced by the answering agent's name: `{agent} at Acme` sends `\"Jim Scott at Acme\" <support@help.example.com>`. The address is the inbox the thread arrived at, or `reply.replyToAddress` for a thread whose inbox is not in `inboundAddresses` or that arrived at none. With no agent name the email capability's `fromName` is used instead; a name with no `{agent}`, such as `Acme Support`, is used for every reply."),
131
132
  deliverInApp: z.boolean().default(false).describe("Answer an `app` thread by storing the reply for the submitter to read in the app, instead of mailing it. **A choice, not a fallback.** Turning on Email Routing takes over the zone's MX, so a project already running mail on that domain cannot receive support replies without consequences everywhere else on it — and for a submitter who is a signed-in user sitting on the screen they wrote from, the answer is better placed there anyway. In-app delivery also happens automatically when there is no address to reply from and no email capability to send with, whatever this says; the setting is what makes it reachable for a project whose mail works fine. It never applies to an `email` thread: a mail thread's sender has no read-back, so a stored answer there is one nobody would ever see."),
132
133
  snippets: z.record(z.string(), SupportReplySnippet).default({}).describe("Your own canned replies, merged over the ones Pithy ships and winning on a key collision. These are *starting points a human picks and edits* in the dashboard, not automatic replies — nothing here is ever sent without somebody pressing send. Author them with `defineSupportReplies` so a malformed one fails where it is written.")
133
134
  }).describe("Reply settings — whether replies are allowed, where the answer goes, and what to offer as a starting point.");
@@ -176,7 +177,7 @@ function supportSemantic(search) {
176
177
  }
177
178
  /** The full support configuration. */
178
179
  /**
179
- * **Who of the app's own signed-in users works the inbox (#816).** Off until named.
180
+ * **Who of the app's own signed-in users works the inbox.** Off until named.
180
181
  *
181
182
  * The inbox holds every customer's correspondence, so a signed-in user reaches it only through a power the
182
183
  * adopter names in their own role catalog, held in the organization the request acts for — the same seam
@@ -217,10 +218,10 @@ const SupportConfig = z.object({
217
218
  * All three default `true`, so the ordinary project is unaffected by this predicate existing.
218
219
  *
219
220
  * Two callers, and they must agree: `capability.ts` declares the binding only when this is true, and
220
- * `pithy support provision` creates the bucket only when this is true. They disagreed before #440 —
221
- * the provisioner asked about `enabled` and `retainRaw` and never about submissions, so a project that
222
- * wanted uploads but no mail attachments got a binding pointing at a bucket nothing had created, and
223
- * every submitted file was dropped with a warning. One predicate is what makes that unrepresentable.
221
+ * `pithy support provision` creates the bucket only when this is true. Were they to ask different
222
+ * questions, a project that wanted uploads but no mail attachments would get a binding pointing at a
223
+ * bucket nothing had created, and every submitted file would be dropped with a warning. One predicate is
224
+ * what makes that unrepresentable.
224
225
  */
225
226
  function supportNeedsBucket(config) {
226
227
  return config.attachments.enabled || config.attachments.retainRaw || config.submission.attachments.enabled;
@@ -15,7 +15,7 @@ import { type SenderPeers } from "../link/sender";
15
15
  import { sendReply } from "../reply/send";
16
16
  import type { SupportReplySnippets } from "../reply/snippets";
17
17
  import type { SupportAssignResponse, SupportFlagsResponse, SupportMyThreadResponse, SupportMyThreadsResponse, SupportReclassifiedResponse, SupportReplySentResponse, SupportReplyView, SupportSearchResponse, SupportSubmissionResponse, SupportThreadResponse, SupportThreadsResponse, SupportThreadView } from "./responses";
18
- import type { ArchiveThreadInput, AssignThreadInput, FlagsInput, ListThreadsQuery, MyThreadsQuery, RepliesQuery, ReplyInput, SearchThreadsQuery, SubmitFeedbackInput } from "./schemas";
18
+ import type { ArchiveThreadInput, AssignThreadInput, FlagsInput, ListThreadsQuery, MyThreadsQuery, RepliesQuery, ReplyInput, SearchThreadsBody, SubmitFeedbackInput } from "./schemas";
19
19
  /**
20
20
  * The support handlers — every one of them behind the `control-plane` gate, and every one of them
21
21
  * taking already-validated values.
@@ -71,7 +71,7 @@ export declare function listInbox(deps: HandlerDeps, query: ListThreadsQuery, vi
71
71
  * Search the inbox: keyword and semantic, ranked as one. The semantic seams are whatever this Worker
72
72
  * was given — absent bindings skip that backend and say so, rather than failing the search.
73
73
  */
74
- export declare function searchInbox(deps: HandlerDeps, query: SearchThreadsQuery, viewer: string): Promise<SupportSearchResponse>;
74
+ export declare function searchInbox(deps: HandlerDeps, query: SearchThreadsBody, viewer: string): Promise<SupportSearchResponse>;
75
75
  /** Read one conversation in full. */
76
76
  export declare function readConversation(deps: HandlerDeps, threadId: string): Promise<SupportThreadResponse>;
77
77
  /** Mark a conversation done, or reopen it. */
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/http/handlers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/http/handlers.ts"],"names":[],"mappings":"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"}
@@ -55,7 +55,7 @@ async function searchInbox(deps, query, viewer) {
55
55
  q: query.q,
56
56
  ...query.mode !== void 0 ? { mode: query.mode } : {},
57
57
  ...query.limit !== void 0 ? { limit: query.limit } : {},
58
- archived: query.archived === "true",
58
+ archived: query.archived === true,
59
59
  category: query.category,
60
60
  declaredCategory: query.declaredCategory,
61
61
  priority: query.priority,
@@ -91,7 +91,8 @@ async function presentAttachments(deps, attachments) {
91
91
  size: attachment.size,
92
92
  sha256: attachment.sha256,
93
93
  inline: attachment.inline,
94
- url
94
+ url,
95
+ messageId: attachment.messageId
95
96
  };
96
97
  }));
97
98
  }
@@ -138,6 +138,7 @@ export declare const SupportAttachmentView: z.ZodObject<{
138
138
  sha256: z.ZodString;
139
139
  inline: z.ZodBoolean;
140
140
  url: z.ZodNullable<z.ZodString>;
141
+ messageId: z.ZodOptional<z.ZodString>;
141
142
  }, z.core.$strip>;
142
143
  export type SupportAttachmentView = z.output<typeof SupportAttachmentView>;
143
144
  /** One of the sender's purchases, flattened to what a support console renders. */
@@ -326,7 +327,7 @@ export declare const SupportSearchResultView: z.ZodObject<{
326
327
  similarity: z.ZodOptional<z.ZodNumber>;
327
328
  }, z.core.$strip>;
328
329
  export type SupportSearchResultView = z.output<typeof SupportSearchResultView>;
329
- /** `GET {base}/threads/search`. */
330
+ /** `POST {base}/threads/search`. */
330
331
  export declare const SupportSearchResponse: z.ZodObject<{
331
332
  mode: z.ZodEnum<{
332
333
  hybrid: "hybrid";
@@ -503,6 +504,7 @@ export declare const SupportThreadResponse: z.ZodObject<{
503
504
  sha256: z.ZodString;
504
505
  inline: z.ZodBoolean;
505
506
  url: z.ZodNullable<z.ZodString>;
507
+ messageId: z.ZodOptional<z.ZodString>;
506
508
  }, z.core.$strip>>;
507
509
  sender: z.ZodObject<{
508
510
  authenticated: z.ZodBoolean;
@@ -700,9 +702,8 @@ export type SupportFlagsResponse = z.output<typeof SupportFlagsResponse>;
700
702
  * Below is the same projection for the other consumer: a management client reading a Worker **it does
701
703
  * not control**. That client is crossing a trust boundary — a fork, a bug, a half-finished deploy, or a
702
704
  * hostile Worker each put a member here that no enum of ours declares — and under the strict schema one
703
- * such token costs it the entire response. It cost `pithy-sh/dashboard#15` exactly that: one
704
- * unrecognized `channel` and the support pane rendered zero of twenty-five conversations. The client
705
- * then had to widen our shape locally, which is the hand-held mirror `#113` exists to forbid.
705
+ * such token costs it the entire response: one unrecognized `channel` and a support pane renders none of
706
+ * its conversations, and the client is left widening our shape locally, the hand-held mirror the kit forbids.
706
707
  *
707
708
  * So the tolerance is published rather than reimplemented, by the one pattern
708
709
  * `@pithy-sh/core/src/projection/asRead` states for the whole kit: same objects, every enum read as a
@@ -843,7 +844,7 @@ export declare const SupportThreadsResponseAsRead: z.ZodObject<{
843
844
  nextCursor: z.ZodNullable<z.ZodString>;
844
845
  }, z.core.$strip>;
845
846
  export type SupportThreadsResponseAsRead = z.output<typeof SupportThreadsResponseAsRead>;
846
- /** `GET {base}/threads/search`, as a client that does not control the Worker must read it. */
847
+ /** `POST {base}/threads/search`, as a client that does not control the Worker must read it. */
847
848
  export declare const SupportSearchResponseAsRead: z.ZodObject<{
848
849
  mode: z.ZodString;
849
850
  backends: z.ZodObject<{
@@ -953,6 +954,7 @@ export declare const SupportThreadResponseAsRead: z.ZodObject<{
953
954
  sha256: z.ZodString;
954
955
  inline: z.ZodBoolean;
955
956
  url: z.ZodNullable<z.ZodString>;
957
+ messageId: z.ZodOptional<z.ZodString>;
956
958
  }, z.core.$strip>>;
957
959
  sender: z.ZodObject<{
958
960
  authenticated: z.ZodBoolean;
@@ -1111,6 +1113,7 @@ export declare const SupportMyMessageView: z.ZodObject<{
1111
1113
  sha256: z.ZodString;
1112
1114
  inline: z.ZodBoolean;
1113
1115
  url: z.ZodNullable<z.ZodString>;
1116
+ messageId: z.ZodOptional<z.ZodString>;
1114
1117
  }, z.core.$strip>>;
1115
1118
  sentAt: z.ZodISODateTime;
1116
1119
  }, z.core.$strip>;
@@ -1168,6 +1171,7 @@ export declare const SupportMyThreadResponse: z.ZodObject<{
1168
1171
  sha256: z.ZodString;
1169
1172
  inline: z.ZodBoolean;
1170
1173
  url: z.ZodNullable<z.ZodString>;
1174
+ messageId: z.ZodOptional<z.ZodString>;
1171
1175
  }, z.core.$strip>>;
1172
1176
  sentAt: z.ZodISODateTime;
1173
1177
  }, z.core.$strip>>;
@@ -1 +1 @@
1
- {"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/http/responses.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/http/responses.ts"],"names":[],"mappings":"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"}
@@ -27,16 +27,14 @@ import { asRead } from "@pithy-sh/core/src/projection/asRead";
27
27
  *
28
28
  * **A browser imports this file, so it reaches no module that needs the Workers runtime.** That is not
29
29
  * a preference about bundle size; it is what "a client validates what comes back" means once the client
30
- * is a Vite program with the DOM lib and no Workers types. This file imported `SUPPORT_BILLING_SCOPE`
31
- * from `link/sender.ts` for one release and brought D1, Kysely and two capabilities' table maps with it,
32
- * which took `pithy-sh/dashboard`'s client build down on an error in `@pithy-sh/auth` (#419). The rule is
33
- * held by `tooling/browser-scopes`, which compiles this module with `types: []` and asks the compiler
30
+ * is a Vite program with the DOM lib and no Workers types: an import of `link/sender.ts` would bring D1,
31
+ * Kysely and two capabilities' table maps into that build. The rule is held by `tooling/browser-scopes`, which compiles this module with `types: []` and asks the compiler
34
32
  * which files that program included.
35
33
  *
36
34
  * **A field added here later is `.optional()`, not merely `.nullable()`.** This module is read across a
37
35
  * version boundary — a management client validates a response with this schema against a customer's
38
36
  * Worker at whatever kit version it is on — so an additive required key fails `safeParse` for everyone
39
- * below that release and takes the whole pane with it (#450). Absent then means *this Worker cannot
37
+ * below that release and takes the whole pane with it. Absent then means *this Worker cannot
40
38
  * say*, which is a different fact from `null`.
41
39
  */
42
40
  /** Where a page resumes, or the end of the list. */
@@ -104,7 +102,8 @@ const SupportAttachmentView = z.object({
104
102
  size: z.number().int().describe("The size in bytes."),
105
103
  sha256: z.string().describe("The content digest, so a client can tell two identical attachments apart from one."),
106
104
  inline: z.boolean().describe("Whether it was referenced inline in the HTML body rather than attached."),
107
- url: z.string().nullable().describe("A signed URL valid for a few minutes, or null when no credentials were available to sign one. The `storageKey` is never here: it is server-derived precisely so a client cannot name an object or guess the one beside it.")
105
+ url: z.string().nullable().describe("A signed URL valid for a few minutes, or null when no credentials were available to sign one. The `storageKey` is never here: it is server-derived precisely so a client cannot name an object or guess the one beside it."),
106
+ messageId: z.string().optional().describe("The id of the message it arrived on, so a client can show each file under the message that carried it. Absent from a Worker that predates it.")
108
107
  }).describe("One attachment as a client receives it — metadata and a signed URL, never the storage key.");
109
108
  /** One of the sender's purchases, flattened to what a support console renders. */
110
109
  const SenderPurchaseView = z.object({
@@ -185,7 +184,7 @@ const SupportSearchResultView = z.object({
185
184
  bm25: z.number().optional().describe("That message's FTS5 `bm25`, raw: lower is better, and comparable only within one search. Absent when FTS5 did not run or did not match it — including on the `LIKE` scan, which has no score."),
186
185
  similarity: z.number().optional().describe("That message's similarity to the query in the semantic index, raw: higher is closer. Absent when semantic search did not run or did not match it. Never normalized against `bm25` — the two are reported as they are.")
187
186
  }).describe("One thread a search found, named by its best-matching message, with that message's raw scores.");
188
- /** `GET {base}/threads/search`. */
187
+ /** `POST {base}/threads/search`. */
189
188
  const SupportSearchResponse = z.object({
190
189
  mode: z.enum([
191
190
  "keyword",
@@ -218,7 +217,7 @@ const SupportAssignmentView = z.object({
218
217
  const SupportThreadResponse = z.object({
219
218
  thread: SupportThreadView.describe("The conversation."),
220
219
  messages: z.array(SupportMessageView).describe("Its messages, oldest first — a conversation reads downward."),
221
- attachments: z.array(SupportAttachmentView).describe("Its attachments, across every message."),
220
+ attachments: z.array(SupportAttachmentView).describe("Its attachments, across every message, each naming the message it arrived on in `messageId`."),
222
221
  sender: SenderContextView.describe("What the app already knows about the sender."),
223
222
  replies: z.array(SupportReplyView).describe("The canned replies, ordered for this thread's category."),
224
223
  assignments: z.array(SupportAssignmentView).optional().describe("Every change of the assignee, oldest first. Empty when nobody was ever assigned; absent from a Worker that predates assignment.")
@@ -277,9 +276,8 @@ const SupportFlagsResponse = z.object({ ok: z.literal(true).describe("Always tru
277
276
  * Below is the same projection for the other consumer: a management client reading a Worker **it does
278
277
  * not control**. That client is crossing a trust boundary — a fork, a bug, a half-finished deploy, or a
279
278
  * hostile Worker each put a member here that no enum of ours declares — and under the strict schema one
280
- * such token costs it the entire response. It cost `pithy-sh/dashboard#15` exactly that: one
281
- * unrecognized `channel` and the support pane rendered zero of twenty-five conversations. The client
282
- * then had to widen our shape locally, which is the hand-held mirror `#113` exists to forbid.
279
+ * such token costs it the entire response: one unrecognized `channel` and a support pane renders none of
280
+ * its conversations, and the client is left widening our shape locally, the hand-held mirror the kit forbids.
283
281
  *
284
282
  * So the tolerance is published rather than reimplemented, by the one pattern
285
283
  * `@pithy-sh/core/src/projection/asRead` states for the whole kit: same objects, every enum read as a
@@ -303,7 +301,7 @@ const SupportListedThreadViewAsRead = asRead(SupportListedThreadView);
303
301
  const SupportMessageViewAsRead = asRead(SupportMessageView);
304
302
  /** `GET {base}/threads`, as a client that does not control the Worker must read it. */
305
303
  const SupportThreadsResponseAsRead = asRead(SupportThreadsResponse);
306
- /** `GET {base}/threads/search`, as a client that does not control the Worker must read it. */
304
+ /** `POST {base}/threads/search`, as a client that does not control the Worker must read it. */
307
305
  const SupportSearchResponseAsRead = asRead(SupportSearchResponse);
308
306
  /** `GET {base}/threads/:id`, as a client that does not control the Worker must read it. */
309
307
  const SupportThreadResponseAsRead = asRead(SupportThreadResponse);
@@ -8,7 +8,7 @@ import { type HandlerDeps } from "./handlers";
8
8
  * The support routes, their verification strategies, and what each accepts:
9
9
  *
10
10
  * GET /support/threads → the inbox (control-plane: support:threads:read) query: ListThreadsQuery
11
- * GET /support/threads/search → search (control-plane: support:threads:read) query: SearchThreadsQuery
11
+ * POST /support/threads/search → search (control-plane: support:threads:read) json: SearchThreadsBody
12
12
  * GET /support/threads/:id → one thread (control-plane: support:threads:read) param: ThreadIdParam
13
13
  * POST /support/threads/:id/archive → done/reopen (control-plane: support:threads:archive) param + json
14
14
  * POST /support/threads/:id/assign → who has it (control-plane: support:threads:assign) param + json
@@ -58,8 +58,8 @@ import { type HandlerDeps } from "./handlers";
58
58
  * person who most needs to reach support is often the one whose access is broken, and a role the kit
59
59
  * invented would make one adopter's account model a condition on everybody's ability to report a bug.
60
60
  *
61
- * But an adopter whose *own* model makes some submissions act-on-behalf-of — `pithy-sh/dashboard#10`'s
62
- * discount application is made for an organization, and a member may not make one — needs somewhere to
61
+ * But an adopter whose *own* model makes some submissions act-on-behalf-of — a discount application made
62
+ * for an organization, which a member may not make — needs somewhere to
63
63
  * put that check, and it must not be the client. **The seam for it already exists, in the composition
64
64
  * contract rather than in this capability's config**, and it is documented here because it was not
65
65
  * discoverable rather than because it was missing:
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/http/routes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAM7E,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAGL,KAAK,WAAW,EAWjB,MAAM,YAAY,CAAC;AAoCpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkGG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qDAAqD;IACrD,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAChE;;;OAGG;IACH,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAuCD,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,CAqNtG"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/http/routes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAM7E,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAGL,KAAK,WAAW,EAWjB,MAAM,YAAY,CAAC;AAoCpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkGG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qDAAqD;IACrD,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAChE;;;OAGG;IACH,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAuCD,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,CAsNtG"}
@@ -3,7 +3,7 @@
3
3
  import { requireAuth, requireStaff } from "./guards.js";
4
4
  import { archiveConversation, assignConversation, listInbox, listMyThreads, listReplies, readConversation, readMyThread, reclassifyConversation, replyToConversation, searchInbox, submitFeedbackRequest, updateFlags } from "./handlers.js";
5
5
  import { SupportArchiveResponse, SupportAssignResponse, SupportFlagsResponse, SupportMyThreadResponse, SupportMyThreadsResponse, SupportReclassifiedResponse, SupportRepliesResponse, SupportReplySentResponse, SupportSearchResponse, SupportSubmissionResponse, SupportThreadResponse, SupportThreadsResponse } from "./responses.js";
6
- import { ArchiveThreadInput, AssignThreadInput, FlagsInput, ListThreadsQuery, MyThreadsQuery, RepliesQuery, ReplyInput, SearchThreadsQuery, SubmitFeedbackInput, ThreadIdParam } from "./schemas.js";
6
+ import { ArchiveThreadInput, AssignThreadInput, FlagsInput, ListThreadsQuery, MyThreadsQuery, RepliesQuery, ReplyInput, SearchThreadsBody, SubmitFeedbackInput, ThreadIdParam } from "./schemas.js";
7
7
  import { SUPPORT_THREADS_ARCHIVE_SCOPE, SUPPORT_THREADS_ASSIGN_SCOPE, SUPPORT_THREADS_FLAG_SCOPE, SUPPORT_THREADS_READ_SCOPE, SUPPORT_THREADS_RECLASSIFY_SCOPE, SUPPORT_THREADS_REPLY_SCOPE } from "./scopes.js";
8
8
  import { InternalError } from "@pithy-sh/core/src/error/pithyError";
9
9
  import { requireControlPlane } from "@pithy-sh/core/src/controlPlane/http/guard";
@@ -54,12 +54,13 @@ function registerSupportRoutes(options) {
54
54
  scopes: [SUPPORT_THREADS_READ_SCOPE],
55
55
  responses: { 200: SupportThreadsResponse }
56
56
  }), requireControlPlane(SUPPORT_THREADS_READ_SCOPE), validate("query", ListThreadsQuery), async (c) => c.json(await listInbox(await resolve(c), c.req.valid("query"), viewer(c))));
57
- app.get(`${base}/threads/search`, describeRoute({
57
+ app.post(`${base}/threads/search`, describeRoute({
58
58
  summary: "Search the inbox, by words and by meaning, ranked as one.",
59
+ description: "The words travel in the body, because they are often a customer's address or order number and a query string is written to every request log between the client and the Worker. Read-only.",
59
60
  verification: "control-plane",
60
61
  scopes: [SUPPORT_THREADS_READ_SCOPE],
61
62
  responses: { 200: SupportSearchResponse }
62
- }), requireControlPlane(SUPPORT_THREADS_READ_SCOPE), validate("query", SearchThreadsQuery), async (c) => c.json(await searchInbox(await resolve(c), c.req.valid("query"), viewer(c))));
63
+ }), requireControlPlane(SUPPORT_THREADS_READ_SCOPE), validate("json", SearchThreadsBody), async (c) => c.json(await searchInbox(await resolve(c), c.req.valid("json"), viewer(c))));
63
64
  app.get(`${base}/replies`, describeRoute({
64
65
  summary: "List the canned replies.",
65
66
  verification: "control-plane",
@@ -21,7 +21,7 @@ export type ThreadIdParam = z.infer<typeof ThreadIdParam>;
21
21
  /**
22
22
  * The inbox listing query.
23
23
  *
24
- * **Strict, and there is no `q`.** Listing lists; `GET {basePath}/threads/search` searches. An unknown
24
+ * **Strict, and there is no `q`.** Listing lists; `POST {basePath}/threads/search` searches. An unknown
25
25
  * key is refused as `validation/invalid_input` rather than ignored, so a client sending `q` is told so
26
26
  * instead of getting the unfiltered inbox back as though it were the answer.
27
27
  */
@@ -60,12 +60,13 @@ export declare const SupportSearchMode: z.ZodEnum<{
60
60
  semantic: "semantic";
61
61
  }>;
62
62
  export type SupportSearchMode = z.infer<typeof SupportSearchMode>;
63
- /** The search query. */
64
- export declare const SearchThreadsQuery: z.ZodObject<{
65
- archived: z.ZodOptional<z.ZodEnum<{
66
- false: "false";
67
- true: "true";
68
- }>>;
63
+ /**
64
+ * The body of `POST {basePath}/threads/search`: the words, the mode, how many, and the inbox listing's filters.
65
+ * A body, not a query string, because the words are often a customer's address or order number and a URL is
66
+ * written to every request log between the client and the Worker. Strict, so a misspelled filter is refused
67
+ * rather than silently widening the search.
68
+ */
69
+ export declare const SearchThreadsBody: z.ZodObject<{
69
70
  category: z.ZodOptional<z.ZodString>;
70
71
  declaredCategory: z.ZodOptional<z.ZodString>;
71
72
  priority: z.ZodOptional<z.ZodEnum<{
@@ -91,9 +92,10 @@ export declare const SearchThreadsQuery: z.ZodObject<{
91
92
  keyword: "keyword";
92
93
  semantic: "semantic";
93
94
  }>>;
94
- limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
95
+ limit: z.ZodOptional<z.ZodNumber>;
96
+ archived: z.ZodOptional<z.ZodBoolean>;
95
97
  }, z.core.$strict>;
96
- export type SearchThreadsQuery = z.infer<typeof SearchThreadsQuery>;
98
+ export type SearchThreadsBody = z.infer<typeof SearchThreadsBody>;
97
99
  /** The archive/unarchive body. */
98
100
  export declare const ArchiveThreadInput: z.ZodObject<{
99
101
  archived: z.ZodBoolean;
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/http/schemas.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB;;;;;;;;;;;GAWG;AAEH,+BAA+B;AAC/B,eAAO,MAAM,aAAa;;iBAOsC,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAyC1D;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiBmE,CAAC;AACjG,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,yBAAyB;AACzB,eAAO,MAAM,iBAAiB;;;;EAI3B,CAAC;AACJ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,wBAAwB;AACxB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAuBqF,CAAC;AACrH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,kCAAkC;AAClC,eAAO,MAAM,kBAAkB;;iBAQuB,CAAC;AACvD,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,mCAAmC;AACnC,eAAO,MAAM,aAAa;;;iBAOoE,CAAC;AAC/F,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,yDAAyD;AACzD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,iGAAiG;AACjG,eAAO,MAAM,iBAAiB;;;;;;iBAeiC,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,sBAAsB;AACtB,eAAO,MAAM,UAAU;;;iBAgB0B,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,iCAAiC;AACjC,eAAO,MAAM,UAAU;;;iBAWmC,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB;;;;iBA4B8D,CAAC;AACpG,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;iBAiCqF,CAAC;AACtH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,uCAAuC;AACvC,eAAO,MAAM,cAAc;;;iBAiBxB,CAAC;AACJ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,sCAAsC;AACtC,eAAO,MAAM,YAAY;;iBAM4B,CAAC;AACtD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/http/schemas.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB;;;;;;;;;;;GAWG;AAEH,+BAA+B;AAC/B,eAAO,MAAM,aAAa;;iBAOsC,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAyC1D;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiBmE,CAAC;AACjG,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,yBAAyB;AACzB,eAAO,MAAM,iBAAiB;;;;EAI3B,CAAC;AACJ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAwBsF,CAAC;AACrH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,kCAAkC;AAClC,eAAO,MAAM,kBAAkB;;iBAQuB,CAAC;AACvD,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,mCAAmC;AACnC,eAAO,MAAM,aAAa;;;iBAOoE,CAAC;AAC/F,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,yDAAyD;AACzD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,iGAAiG;AACjG,eAAO,MAAM,iBAAiB;;;;;;iBAeiC,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,sBAAsB;AACtB,eAAO,MAAM,UAAU;;;iBAmB0B,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,iCAAiC;AACjC,eAAO,MAAM,UAAU;;;iBAWmC,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB;;;;iBA4B8D,CAAC;AACpG,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;iBAiCqF,CAAC;AACtH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,uCAAuC;AACvC,eAAO,MAAM,cAAc;;;iBAiBxB,CAAC;AACJ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,sCAAsC;AACtC,eAAO,MAAM,YAAY;;iBAM4B,CAAC;AACtD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
@@ -6,6 +6,7 @@ import { UNASSIGNED } from "../data/assignment.js";
6
6
  import { SupportSubmissionContext } from "../data/message.js";
7
7
  import "../store/paging.js";
8
8
  import { z } from "zod";
9
+ import { freeText } from "@pithy-sh/core/src/http/freeText";
9
10
  import { PresenceParticipantName, PresenceParticipantRef } from "@pithy-sh/core/src/presence/frames";
10
11
  //#region src/http/schemas.ts
11
12
  /** An assignee reference, as the listing filters on it and an assignment names it. */
@@ -40,7 +41,7 @@ const threadFilterFields = {
40
41
  /**
41
42
  * The inbox listing query.
42
43
  *
43
- * **Strict, and there is no `q`.** Listing lists; `GET {basePath}/threads/search` searches. An unknown
44
+ * **Strict, and there is no `q`.** Listing lists; `POST {basePath}/threads/search` searches. An unknown
44
45
  * key is refused as `validation/invalid_input` rather than ignored, so a client sending `q` is told so
45
46
  * instead of getting the unfiltered inbox back as though it were the answer.
46
47
  */
@@ -55,12 +56,18 @@ const SupportSearchMode = z.enum([
55
56
  "semantic",
56
57
  "hybrid"
57
58
  ]).describe("`keyword` finds the exact words — FTS5 when `search.fts` is on, the `LIKE` scan otherwise. `semantic` finds the meaning, through the message index. `hybrid` runs both and ranks them as one.");
58
- /** The search query. */
59
- const SearchThreadsQuery = z.object({
60
- q: z.string().trim().min(1).max(200).describe("What to search for, in the customer's words or yours. An order number, an error, or a description."),
59
+ /**
60
+ * The body of `POST {basePath}/threads/search`: the words, the mode, how many, and the inbox listing's filters.
61
+ * A body, not a query string, because the words are often a customer's address or order number and a URL is
62
+ * written to every request log between the client and the Worker. Strict, so a misspelled filter is refused
63
+ * rather than silently widening the search.
64
+ */
65
+ const SearchThreadsBody = z.object({
66
+ ...threadFilterFields,
67
+ q: freeText(z.string().trim().min(1).max(200)).describe("What to search for, in the customer's words or yours. An order number, an error, or a description."),
61
68
  mode: SupportSearchMode.optional().describe("Which backends to run. Absent runs every one that is on, as `hybrid`. `semantic` or `hybrid` while `search.semantic` is off is refused, naming the flag."),
62
- limit: z.coerce.number().int().min(1).max(50).optional().describe(`How many threads to return, best first. Default 20, at most 50. No cursor: a ranked list has a top, not pages.`),
63
- ...threadFilterFields
69
+ limit: z.number().int().min(1).max(50).optional().describe(`How many threads to return, best first. Default 20, at most 50. No cursor: a ranked list has a top, not pages.`),
70
+ archived: z.boolean().optional().describe("`true` for the done pile, `false` or absent for the open inbox — which is the default on purpose.")
64
71
  }).strict().describe("A search over the inbox: the words, the mode, how many, and the same filters the inbox listing takes.");
65
72
  /** The archive/unarchive body. */
66
73
  const ArchiveThreadInput = z.object({ archived: z.boolean().describe("`true` marks the conversation done; `false` reopens it. Explicit rather than a toggle, so a retried request lands on the state the caller meant rather than flipping it back.") }).describe("Mark a conversation done, or reopen it.");
@@ -79,7 +86,7 @@ const AssignThreadInput = z.object({
79
86
  /** The reply body. */
80
87
  const ReplyInput = z.object({
81
88
  body: z.string().min(1).max(5e4).describe("The reply text, as a human wrote and edited it. Rendered HTML-escaped into the adopter's email shell — it is prose, never markup."),
82
- agentName: z.string().min(1).max(80).optional().describe("Who is answering, signed at the bottom. Omitted rather than guessed.")
89
+ agentName: z.string().min(1).max(80).regex(/^[^@<>"]*$/, "A name may not contain @, <, > or a double quote.").optional().describe("Who is answering, signed at the bottom and written into the reply's From line. Omitted rather than guessed. May not contain `@`, `<`, `>` or `\"`, so the display name cannot pass for an address.")
83
90
  }).describe("An answer to send to the customer.");
84
91
  /** The per-viewer flags body. */
85
92
  const FlagsInput = z.object({
@@ -127,4 +134,4 @@ const MyThreadsQuery = z.object({
127
134
  /** The canned-reply catalog query. */
128
135
  const RepliesQuery = z.object({ category: CategoryFilter.optional().describe("Order the catalog for this category — its snippets first, then the general-purpose ones. Ordering, never filtering: a misclassified thread must not hide the snippet its operator actually needs.") }).describe("How to order the canned reply catalog.");
129
136
  //#endregion
130
- export { ArchiveThreadInput, AssignThreadInput, AssigneeInput, FlagsInput, ListThreadsQuery, MAX_ASSIGNMENT_NOTE_CHARS, MyThreadsQuery, RepliesQuery, ReplyInput, SearchThreadsQuery, SubmitFeedbackInput, SubmittedAttachmentInput, SupportSearchMode, ThreadIdParam };
137
+ export { ArchiveThreadInput, AssignThreadInput, AssigneeInput, FlagsInput, ListThreadsQuery, MAX_ASSIGNMENT_NOTE_CHARS, MyThreadsQuery, RepliesQuery, ReplyInput, SearchThreadsBody, SubmitFeedbackInput, SubmittedAttachmentInput, SupportSearchMode, ThreadIdParam };
@@ -1 +1 @@
1
- {"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/http/scopes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sDAAsD,CAAC;AAClG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,iGAAiG;AACjG,eAAO,MAAM,0BAA0B,EAAE,iBAA0C,CAAC;AAEpF,oFAAoF;AACpF,eAAO,MAAM,6BAA6B,EAAE,iBAA6C,CAAC;AAE1F;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,iBAA2C,CAAC;AAEtF,8EAA8E;AAC9E,eAAO,MAAM,gCAAgC,EAAE,iBAAgD,CAAC;AAEhG,wGAAwG;AACxG,eAAO,MAAM,0BAA0B,EAAE,iBAA0C,CAAC;AAEpF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,iBAA4C,CAAC;AAExF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS,iBAAiB,EAOpE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,EAAE,CA2E5E"}
1
+ {"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/http/scopes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sDAAsD,CAAC;AAClG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,iGAAiG;AACjG,eAAO,MAAM,0BAA0B,EAAE,iBAA0C,CAAC;AAEpF,oFAAoF;AACpF,eAAO,MAAM,6BAA6B,EAAE,iBAA6C,CAAC;AAE1F;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,iBAA2C,CAAC;AAEtF,8EAA8E;AAC9E,eAAO,MAAM,gCAAgC,EAAE,iBAAgD,CAAC;AAEhG,wGAAwG;AACxG,eAAO,MAAM,0BAA0B,EAAE,iBAA0C,CAAC;AAEpF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,iBAA4C,CAAC;AAExF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS,iBAAiB,EAOpE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,EAAE,CA4E5E"}
@@ -76,11 +76,12 @@ function supportAdminRoutes(basePath) {
76
76
  summary: "List the inbox, newest first, filtered by category, priority, assignee, and archived."
77
77
  },
78
78
  {
79
- method: "GET",
79
+ method: "POST",
80
80
  operation: "threads.search",
81
81
  path: `${basePath}/threads/search`,
82
82
  scope: SUPPORT_THREADS_READ_SCOPE,
83
83
  kind: "reads",
84
+ readInBody: true,
84
85
  summary: "Search the inbox by words and by meaning, ranked as one, with the same filters as the list."
85
86
  },
86
87
  {