@dench.com/cli 2.7.5 → 2.7.7-staging.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -198,7 +198,7 @@ For staging:
198
198
 
199
199
  ```bash
200
200
  dench signin --staging
201
- dench signin --host https://workspace-staging.dench.com
201
+ dench signin --host https://staging.dench.com
202
202
  ```
203
203
 
204
204
  ## Sandbox / API key auth (no `dench signin` required)
package/crm.ts CHANGED
@@ -687,6 +687,21 @@ const api = {
687
687
  reposition: makeFunctionReference<"mutation">(
688
688
  "functions/workspaceNav:repositionMyNavItem",
689
689
  ),
690
+ createGroup: makeFunctionReference<"mutation">(
691
+ "functions/workspaceNav:createNavGroup",
692
+ ),
693
+ renameGroup: makeFunctionReference<"mutation">(
694
+ "functions/workspaceNav:renameNavGroup",
695
+ ),
696
+ deleteGroup: makeFunctionReference<"mutation">(
697
+ "functions/workspaceNav:deleteNavGroup",
698
+ ),
699
+ moveItemToGroup: makeFunctionReference<"mutation">(
700
+ "functions/workspaceNav:moveNavItemToGroup",
701
+ ),
702
+ setRootPin: makeFunctionReference<"mutation">(
703
+ "functions/workspaceNav:setNavRootPin",
704
+ ),
690
705
  },
691
706
  fields: {
692
707
  list: makeFunctionReference<"query">("functions/crm/fields:list"),
@@ -959,13 +974,15 @@ async function runCrmMembersCommand(ctx: CrmCliContext): Promise<void> {
959
974
  }
960
975
 
961
976
  /**
962
- * `dench crm sidebar …` — the workspace app rail.
977
+ * `dench crm sidebar …` — the workspace app rail's Library.
963
978
  *
964
- * Row VISIBILITY is team-shared and lives on the CRM records themselves
965
- * (`crm objects archive`, `crm views pin/archive`). Row ORDER is a
966
- * per-member preference, which is what `move` writes. `list` shows both
967
- * halves: the visible rows with their 1-based positions, plus what's
968
- * hidden and why.
979
+ * The whole layout is WORKSPACE-SHARED: one tree of rows and named
980
+ * groups for every member. Permissions split by operation `move` (and
981
+ * `crm objects archive` / `crm views archive`) are ADMIN-ONLY because
982
+ * they change what everyone sees; `group …` operations and view pinning
983
+ * are open to any member. `list` shows the visible rows in tree order
984
+ * (rows inside a group carry `groupId`/`groupName`), the groups, what's
985
+ * hidden and why, and whether YOU are an admin (`viewerIsAdmin`).
969
986
  */
970
987
  async function runSidebarCommand(ctx: CrmCliContext): Promise<void> {
971
988
  const verb = ctx.args.shift() ?? "list";
@@ -997,6 +1014,85 @@ async function runSidebarCommand(ctx: CrmCliContext): Promise<void> {
997
1014
  );
998
1015
  return;
999
1016
  }
1017
+ // Pin/unpin a Library row (or a whole group) on the Sidebar's home
1018
+ // level shortcut strip. Member-level, workspace-shared — like view
1019
+ // pinning.
1020
+ case "pin":
1021
+ case "unpin": {
1022
+ const navId = shift(ctx.args, "nav id");
1023
+ assertNoUnknownFlags(ctx.args, `crm sidebar ${verb}`);
1024
+ out(
1025
+ ctx,
1026
+ await callMutation(ctx, api.sidebar.setRootPin, {
1027
+ navId,
1028
+ pinned: verb === "pin",
1029
+ }),
1030
+ );
1031
+ return;
1032
+ }
1033
+ case "group": {
1034
+ const groupVerb = ctx.args.shift();
1035
+ switch (groupVerb) {
1036
+ case "create": {
1037
+ const name = shift(ctx.args, "group name");
1038
+ assertNoUnknownFlags(ctx.args, "crm sidebar group create");
1039
+ out(ctx, await callMutation(ctx, api.sidebar.createGroup, { name }));
1040
+ return;
1041
+ }
1042
+ case "rename": {
1043
+ const groupId = shift(ctx.args, "group id");
1044
+ const name = shift(ctx.args, "new name");
1045
+ assertNoUnknownFlags(ctx.args, "crm sidebar group rename");
1046
+ out(
1047
+ ctx,
1048
+ await callMutation(ctx, api.sidebar.renameGroup, {
1049
+ groupId,
1050
+ name,
1051
+ }),
1052
+ );
1053
+ return;
1054
+ }
1055
+ case "delete": {
1056
+ const groupId = shift(ctx.args, "group id");
1057
+ assertNoUnknownFlags(ctx.args, "crm sidebar group delete");
1058
+ out(
1059
+ ctx,
1060
+ await callMutation(ctx, api.sidebar.deleteGroup, { groupId }),
1061
+ );
1062
+ return;
1063
+ }
1064
+ case "add": {
1065
+ const navId = shift(ctx.args, "nav id");
1066
+ const groupId = shift(ctx.args, "group id");
1067
+ assertNoUnknownFlags(ctx.args, "crm sidebar group add");
1068
+ out(
1069
+ ctx,
1070
+ await callMutation(ctx, api.sidebar.moveItemToGroup, {
1071
+ navId,
1072
+ groupId,
1073
+ }),
1074
+ );
1075
+ return;
1076
+ }
1077
+ case "remove": {
1078
+ const navId = shift(ctx.args, "nav id");
1079
+ assertNoUnknownFlags(ctx.args, "crm sidebar group remove");
1080
+ out(
1081
+ ctx,
1082
+ await callMutation(ctx, api.sidebar.moveItemToGroup, {
1083
+ navId,
1084
+ groupId: null,
1085
+ }),
1086
+ );
1087
+ return;
1088
+ }
1089
+ default:
1090
+ throw new CrmCliError(
1091
+ `Unknown crm sidebar group verb: ${groupVerb ?? "(none)"}. ` +
1092
+ "Use create|rename|delete|add|remove.",
1093
+ );
1094
+ }
1095
+ }
1000
1096
  default:
1001
1097
  throw new CrmCliError(`Unknown crm sidebar verb: ${verb}`);
1002
1098
  }
@@ -1071,9 +1167,10 @@ async function runObjectsCommand(ctx: CrmCliContext): Promise<void> {
1071
1167
  out(ctx, await callMutation(ctx, api.objects.remove, { name }));
1072
1168
  return;
1073
1169
  }
1074
- // Archive is the sidebar's "remove from my rail", NOT a delete: the
1170
+ // Archive is the sidebar's "remove from the rail", NOT a delete: the
1075
1171
  // table, its rows and every link to it keep working, the row just
1076
- // moves into the rail's Archived flyout. Team-shared.
1172
+ // moves into the rail's Archived flyout. Team-shared, and ADMIN-ONLY
1173
+ // (the server rejects non-admin members).
1077
1174
  case "archive":
1078
1175
  case "unarchive": {
1079
1176
  const name = shift(ctx.args, "object name");
@@ -2785,7 +2882,8 @@ type CrmFieldForEnrichment = FieldCandidate & {
2785
2882
  category: EnrichmentCategory;
2786
2883
  key: string;
2787
2884
  apolloPath: string;
2788
- inputFieldName: string;
2885
+ inputFieldName?: string;
2886
+ strategy?: Record<string, unknown>;
2789
2887
  };
2790
2888
  };
2791
2889
 
@@ -2794,6 +2892,8 @@ type CrmEntryForEnrichment = {
2794
2892
  fields: Record<string, unknown>;
2795
2893
  };
2796
2894
 
2895
+ type CliEnrichmentProvider = "fullenrich" | "aviato";
2896
+
2797
2897
  type EnrichmentTarget = {
2798
2898
  category: EnrichmentCategory;
2799
2899
  column: EnrichmentColumnDef;
@@ -2805,7 +2905,7 @@ type CellEnrichmentArgs = {
2805
2905
  objectName: string;
2806
2906
  entryId: string;
2807
2907
  fieldName: string;
2808
- provider?: string;
2908
+ provider?: CliEnrichmentProvider;
2809
2909
  category?: EnrichmentCategory;
2810
2910
  inputFieldName?: string;
2811
2911
  apolloPath?: string;
@@ -2825,6 +2925,16 @@ function parseCategoryFlag(
2825
2925
  throw new CrmCliError("--category must be people or company");
2826
2926
  }
2827
2927
 
2928
+ function parseProviderFlag(
2929
+ value: string | undefined,
2930
+ ): CliEnrichmentProvider | undefined {
2931
+ if (value === undefined) return undefined;
2932
+ if (value === "fullenrich" || value === "aviato") return value;
2933
+ throw new CrmCliError(
2934
+ `--provider must be fullenrich or aviato; received "${value}"`,
2935
+ );
2936
+ }
2937
+
2828
2938
  function parsePositiveInt(
2829
2939
  flag: string,
2830
2940
  value: string | undefined,
@@ -2877,8 +2987,16 @@ function readFieldEnrichment(
2877
2987
  typeof candidate.inputFieldName === "string"
2878
2988
  ? candidate.inputFieldName
2879
2989
  : undefined;
2880
- if (!category || !key || !apolloPath || !inputFieldName) return undefined;
2881
- return { category, key, apolloPath, inputFieldName };
2990
+ const strategy =
2991
+ candidate.strategy &&
2992
+ typeof candidate.strategy === "object" &&
2993
+ !Array.isArray(candidate.strategy)
2994
+ ? (candidate.strategy as Record<string, unknown>)
2995
+ : undefined;
2996
+ if (!category || !key || !apolloPath || (!inputFieldName && !strategy)) {
2997
+ return undefined;
2998
+ }
2999
+ return { category, key, apolloPath, inputFieldName, strategy };
2882
3000
  }
2883
3001
 
2884
3002
  function asEntry(value: unknown): CrmEntryForEnrichment | null {
@@ -3003,7 +3121,7 @@ async function callEnrichmentGatewayForEntry(args: {
3003
3121
  target: EnrichmentTarget;
3004
3122
  entry: CrmEntryForEnrichment;
3005
3123
  /** Explicit provider override. Defaults: people=fullenrich, company=aviato. */
3006
- provider?: string;
3124
+ provider?: CliEnrichmentProvider;
3007
3125
  gateway?: Pick<EnrichmentGatewayOptions, "apiKey" | "baseUrl">;
3008
3126
  }): Promise<Record<string, unknown>> {
3009
3127
  const { entry, target } = args;
@@ -3340,7 +3458,7 @@ async function runPeopleCommand(ctx: CrmCliContext): Promise<void> {
3340
3458
  }
3341
3459
  case "enrich": {
3342
3460
  const entryId = shift(ctx.args, "person entry id");
3343
- const provider = getFlag(ctx.args, "--provider");
3461
+ const provider = parseProviderFlag(getFlag(ctx.args, "--provider"));
3344
3462
  const fieldName = getFlag(ctx.args, "--field") ?? "LinkedIn URL";
3345
3463
  const inputFieldName = getFlag(ctx.args, "--input-field");
3346
3464
  const overwrite = hasFlag(ctx.args, "--overwrite");
@@ -3405,7 +3523,7 @@ async function runCompaniesCommand(ctx: CrmCliContext): Promise<void> {
3405
3523
  }
3406
3524
  case "enrich": {
3407
3525
  const entryId = shift(ctx.args, "company entry id");
3408
- const provider = getFlag(ctx.args, "--provider");
3526
+ const provider = parseProviderFlag(getFlag(ctx.args, "--provider"));
3409
3527
  const fieldName = getFlag(ctx.args, "--field") ?? "Company Name";
3410
3528
  const inputFieldName = getFlag(ctx.args, "--input-field") ?? "Domain";
3411
3529
  const overwrite = hasFlag(ctx.args, "--overwrite");
@@ -3438,7 +3556,7 @@ async function runEnrichCommand(ctx: CrmCliContext): Promise<void> {
3438
3556
  const objectName = shift(ctx.args, "object name");
3439
3557
  const entryId = shift(ctx.args, "entry id");
3440
3558
  const fieldName = shift(ctx.args, "field name");
3441
- const provider = getFlag(ctx.args, "--provider");
3559
+ const provider = parseProviderFlag(getFlag(ctx.args, "--provider"));
3442
3560
  const inputFieldName = getFlag(ctx.args, "--input-field");
3443
3561
  const category = parseCategoryFlag(getFlag(ctx.args, "--category"));
3444
3562
  const apolloPath = getFlag(ctx.args, "--apollo-path");
@@ -3463,7 +3581,7 @@ async function runEnrichCommand(ctx: CrmCliContext): Promise<void> {
3463
3581
  const objectName = shift(ctx.args, "object name");
3464
3582
  const fieldName = getFlag(ctx.args, "--field");
3465
3583
  const missingOnly = !hasFlag(ctx.args, "--all");
3466
- const provider = getFlag(ctx.args, "--provider");
3584
+ const provider = parseProviderFlag(getFlag(ctx.args, "--provider"));
3467
3585
  const inputFieldName = getFlag(ctx.args, "--input-field");
3468
3586
  const category = parseCategoryFlag(getFlag(ctx.args, "--category"));
3469
3587
  const apolloPath = getFlag(ctx.args, "--apollo-path");
@@ -3550,7 +3668,7 @@ Objects (CRM tables):
3550
3668
  dench crm objects archive <name> | unarchive <name>
3551
3669
  Hides/restores the object's workspace SIDEBAR row. Not a delete —
3552
3670
  the table, its rows and every link keep working; the row just moves
3553
- into the rail's Archived flyout. Team-shared.
3671
+ into the rail's Archived flyout. Team-shared and ADMIN-ONLY.
3554
3672
 
3555
3673
  Views (saved CRM subsets):
3556
3674
  dench crm views list <object> [--json]
@@ -3575,17 +3693,37 @@ Views (saved CRM subsets):
3575
3693
  clears any archive stamp.
3576
3694
  dench crm views archive <object> <view-name> | unarchive <object> <view-name>
3577
3695
  Moves the view's sidebar row into the Archived flyout. Unarchiving
3578
- re-asserts the pin so the row comes back.
3696
+ re-asserts the pin so the row comes back. ADMIN-ONLY: archiving
3697
+ hides the row for the whole workspace.
3579
3698
 
3580
- Sidebar (the workspace app rail):
3699
+ Sidebar (the workspace app rail's Library — layout is WORKSPACE-SHARED):
3581
3700
  dench crm sidebar list [--json]
3582
- Visible rows in order with 1-based positions, plus what's hidden
3583
- from the rail and why (archived vs. an unpinned view).
3701
+ Visible rows in tree order (rows inside a group carry groupId/
3702
+ groupName), the groups, what's hidden and why (archived vs. an
3703
+ unpinned view), and whether you are an admin (viewerIsAdmin).
3584
3704
  dench crm sidebar move <nav-id> <top|bottom|N>
3585
- Reposition one row. Nav ids come from 'sidebar list' e.g.
3586
- 'crm-object:task', 'crm-view:task:Due%20today', 'crm-people',
3587
- 'automation'. Row ORDER is per-member; row visibility (above) is
3588
- team-shared.
3705
+ Reposition one row (or a whole group by its group id) among the
3706
+ TOP-LEVEL slots — a group counts as one slot, and a row inside a
3707
+ group is pulled out to the top level. Nav ids come from 'sidebar
3708
+ list' — e.g. 'crm-object:task', 'crm-view:task:Due%20today',
3709
+ 'crm-people', 'group:abc123'. Automations and Outreach are fixed
3710
+ home-level tabs, not Library rows. ADMIN-ONLY: the order is
3711
+ shared by every member.
3712
+ dench crm sidebar group create <name>
3713
+ New named group (e.g. "Hiring") at the bottom of the Library.
3714
+ Open to any member; prints the group id.
3715
+ dench crm sidebar group rename <group-id> <new-name>
3716
+ dench crm sidebar group delete <group-id>
3717
+ Dissolves the group; its rows return to the top level in its slot.
3718
+ dench crm sidebar group add <nav-id> <group-id>
3719
+ File a row into a group (appended at its end).
3720
+ dench crm sidebar group remove <nav-id>
3721
+ Move a row out of its group, back to the top level.
3722
+ dench crm sidebar pin <nav-id> | unpin <nav-id>
3723
+ Pin/unpin a Library row — or a whole group (group id) — on the
3724
+ Sidebar's home level, under Chats/Inbox/Meetings/Automations/
3725
+ Outreach/Library.
3726
+ A pin is a shared shortcut, not a move; open to any member.
3589
3727
 
3590
3728
  Fields (columns):
3591
3729
  dench crm fields list <object> [--json]
package/dench.ts CHANGED
@@ -538,9 +538,8 @@ Monetize the AI "thinking…" line (supported hooks only — no file patching):
538
538
  dench ads uninstall [--target claude|shell|agentmd|all]
539
539
  Aliases: dench ad, dench adline. Help: dench ads help
540
540
 
541
- Managed email campaigns (Dench Emailing Service):
542
- dench email identity verify --from founder@example.com [--json]
543
- dench email template create --name "Intro" --subject "Hi {{firstName}}" --html-file body.html [--json]
541
+ Managed email campaigns:
542
+ dench email campaign create --name "Q2 outreach" --mailbox you@gmail.com --template <id> [--json]
544
543
  dench email campaign create --name "Q2 outreach" --identity <id> --template <id> [--json]
545
544
  dench email campaign recipients add --campaign <id> --file recipients.jsonl [--json]
546
545
  dench email campaign submit --campaign <id> [--json]
package/email.ts CHANGED
@@ -118,6 +118,48 @@ function requiredFlag(args: string[], name: string): string {
118
118
  return value;
119
119
  }
120
120
 
121
+ async function resolveMailboxAccountIds(
122
+ ctx: EmailCliContext,
123
+ mailbox: string,
124
+ ): Promise<string[]> {
125
+ const payload = await callQuery(
126
+ ctx,
127
+ "functions/emailMailboxAccounts:listAccounts",
128
+ {},
129
+ );
130
+ const accounts = Array.isArray(payload) ? (payload as JsonRecord[]) : [];
131
+ const needle = mailbox.trim().toLowerCase();
132
+ const match = accounts.find((row) => {
133
+ const id = typeof row._id === "string" ? row._id : "";
134
+ const email = typeof row.email === "string" ? row.email.toLowerCase() : "";
135
+ return id === mailbox.trim() || email === needle;
136
+ });
137
+ if (!match || typeof match._id !== "string") {
138
+ throw new EmailCliError(
139
+ `Connected inbox not found: ${mailbox}. Connect Gmail or Outlook in Outreach → Accounts.`,
140
+ );
141
+ }
142
+ return [match._id];
143
+ }
144
+
145
+ async function campaignSenderArgs(ctx: EmailCliContext): Promise<{
146
+ identityId?: string;
147
+ mailboxAccountIds?: string[];
148
+ }> {
149
+ const identity = getFlag(ctx.args, "--identity")?.trim();
150
+ const mailbox = getFlag(ctx.args, "--mailbox")?.trim();
151
+ if (identity && mailbox) {
152
+ throw new EmailCliError("Pass --mailbox or --identity, not both.");
153
+ }
154
+ if (mailbox) {
155
+ return { mailboxAccountIds: await resolveMailboxAccountIds(ctx, mailbox) };
156
+ }
157
+ if (identity) return { identityId: identity };
158
+ throw new EmailCliError(
159
+ "Pass --mailbox <email> (connected inbox) or --identity <sender>.",
160
+ );
161
+ }
162
+
121
163
  function optionalJsonFlag(args: string[], name: string): unknown | undefined {
122
164
  const raw = getFlag(args, name);
123
165
  return raw === undefined ? undefined : parseJson(raw);
@@ -185,7 +227,7 @@ function toRecipients(rows: JsonRecord[]) {
185
227
  }
186
228
 
187
229
  export function emailHelp() {
188
- console.log(`dench email — Dench Emailing Service campaign commands
230
+ console.log(`dench email — outbound email from a connected inbox (or Dench Emailing Service)
189
231
 
190
232
  Usage:
191
233
  dench email identity verify --from founder@example.com [--json]
@@ -204,7 +246,7 @@ Usage:
204
246
  dench email message list [--limit 25] [--json]
205
247
  dench email template create --name "Intro" --subject "Hi {{firstName}}" --html-file body.html [--text-file body.txt] [--json]
206
248
  dench email template preview --template <id> --data '{"firstName":"Ada"}' [--json]
207
- dench email campaign create --name "Q2 outreach" --identity <identityId|fromEmail> --template <id> [--json]
249
+ dench email campaign create --name "Q2 outreach" (--mailbox you@gmail.com | --identity <identityId|fromEmail>) --template <id> [--json]
208
250
  dench email campaign list [--status sending] [--limit 25] [--json]
209
251
  dench email campaign recipients add --campaign <id> --file recipients.jsonl [--json]
210
252
  dench email campaign recipients list --campaign <id> [--status queued] [--limit 50] [--json]
@@ -215,7 +257,7 @@ Usage:
215
257
  dench email campaign pause|resume|cancel --campaign <id> [--json]
216
258
 
217
259
  Sequences (automatic multi-step follow-ups):
218
- dench email sequence create --name "New-customer follow-up" --identity <identityId|fromEmail> [--stop-on-click] [--no-stop-on-reply] [--json]
260
+ dench email sequence create --name "New-customer follow-up" (--mailbox you@gmail.com | --identity <identityId|fromEmail>) [--stop-on-click] [--no-stop-on-reply] [--json]
219
261
  dench email sequence list [--all] [--json]
220
262
  dench email sequence status --sequence <id> [--json]
221
263
  dench email sequence steps add --sequence <id> --subject "Hi {{firstName}}" --html-file step1.html [--delay-days 0] [--delay-hours 0] [--json]
@@ -495,7 +537,7 @@ async function runIdentity(ctx: EmailCliContext) {
495
537
  const identities = await listIdentitiesQuery(ctx, domain);
496
538
  const text =
497
539
  identities.length === 0
498
- ? "No sending identities yet. Run `dench email identity verify --from you@example.com`."
540
+ ? "No sending identities. Connect Gmail or Outlook in Outreach → Accounts."
499
541
  : identities
500
542
  .map(
501
543
  (identity) =>
@@ -716,12 +758,13 @@ async function runTemplate(ctx: EmailCliContext) {
716
758
  async function runCampaign(ctx: EmailCliContext) {
717
759
  const sub = ctx.args.shift();
718
760
  if (sub === "create") {
761
+ const sender = await campaignSenderArgs(ctx);
719
762
  const payload = await callMutation(
720
763
  ctx,
721
764
  "functions/emailCampaigns:createCampaign",
722
765
  {
723
766
  name: requiredFlag(ctx.args, "--name"),
724
- identityId: requiredFlag(ctx.args, "--identity"),
767
+ ...sender,
725
768
  templateId: requiredFlag(ctx.args, "--template"),
726
769
  scheduledAt: getFlag(ctx.args, "--scheduled-at")
727
770
  ? Date.parse(requiredFlag(ctx.args, "--scheduled-at"))
@@ -867,12 +910,13 @@ async function runSequence(ctx: EmailCliContext) {
867
910
  const sub = ctx.args.shift();
868
911
  if (sub === "create") {
869
912
  const stopOnReply = !hasFlag(ctx.args, "--no-stop-on-reply");
913
+ const sender = await campaignSenderArgs(ctx);
870
914
  const payload = await callMutation(
871
915
  ctx,
872
916
  "functions/emailSequences:createSequence",
873
917
  {
874
918
  name: requiredFlag(ctx.args, "--name"),
875
- identityId: requiredFlag(ctx.args, "--identity"),
919
+ ...sender,
876
920
  replyToMode: getFlag(ctx.args, "--reply-to-mode"),
877
921
  stopOnReply,
878
922
  stopOnClick: getFlag(ctx.args, "--stop-on-click") !== undefined,
package/host.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PRODUCTION_HOST = "https://dench.com";
2
- export const STAGING_HOST = "https://workspace-staging.dench.com";
2
+ export const STAGING_HOST = "https://staging.dench.com";
3
3
  export const LOCAL_HOST = "http://localhost:3000";
4
4
  export const DEFAULT_HOST = PRODUCTION_HOST;
5
5
 
@@ -791,6 +791,12 @@ const exaResultItem = z
791
791
  image: z.string().optional(),
792
792
  favicon: z.string().optional(),
793
793
  id: z.string().optional(),
794
+ entities: z
795
+ .array(z.record(z.string(), z.unknown()))
796
+ .optional()
797
+ .describe(
798
+ "Structured entity metadata returned by category=people or category=company.",
799
+ ),
794
800
  })
795
801
  .passthrough()
796
802
  .describe("An Exa search/contents result (provider passthrough).");
@@ -1058,11 +1064,26 @@ const exaSearch = z.object({
1058
1064
  "financial report",
1059
1065
  "people",
1060
1066
  ])
1061
- .optional(),
1062
- includeDomains: z.array(z.string()).optional(),
1063
- excludeDomains: z.array(z.string()).optional(),
1064
- startPublishedDate: z.string().optional(),
1065
- endPublishedDate: z.string().optional(),
1067
+ .optional()
1068
+ .describe(
1069
+ "Entity categories return structured entities. Domain filters are unsupported for people; published-date filters are unsupported for people and company.",
1070
+ ),
1071
+ includeDomains: z
1072
+ .array(z.string())
1073
+ .optional()
1074
+ .describe("Unsupported for category=people."),
1075
+ excludeDomains: z
1076
+ .array(z.string())
1077
+ .optional()
1078
+ .describe("Unsupported for category=people."),
1079
+ startPublishedDate: z
1080
+ .string()
1081
+ .optional()
1082
+ .describe("Unsupported for category=people and category=company."),
1083
+ endPublishedDate: z
1084
+ .string()
1085
+ .optional()
1086
+ .describe("Unsupported for category=people and category=company."),
1066
1087
  includeText: z.array(z.string()).optional(),
1067
1088
  excludeText: z.array(z.string()).optional(),
1068
1089
  userLocation: z.string().optional(),
@@ -1384,6 +1405,19 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1384
1405
  to: z.string().describe("New object name."),
1385
1406
  }),
1386
1407
  "crm.objects.delete": z.object({ name: z.string() }),
1408
+ "crm.objects.archive": z.object({ name: z.string() }),
1409
+ "crm.objects.unarchive": z.object({ name: z.string() }),
1410
+
1411
+ // The workspace app rail
1412
+ "crm.sidebar.list": z.object({}),
1413
+ "crm.sidebar.move": z.object({
1414
+ navId: z
1415
+ .string()
1416
+ .describe("Nav id from `sidebar list`, e.g. crm-object:task."),
1417
+ position: z
1418
+ .union([z.literal("top"), z.literal("bottom"), z.number().int().min(1)])
1419
+ .describe("Where to land among the visible rows; N is 1-based."),
1420
+ }),
1387
1421
 
1388
1422
  // CRM saved views
1389
1423
  "crm.views.list": z.object({ objectName: z.string() }),
@@ -1424,6 +1458,14 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1424
1458
  objectName: z.string(),
1425
1459
  viewName: z.string(),
1426
1460
  }),
1461
+ "crm.views.archive": z.object({
1462
+ objectName: z.string(),
1463
+ viewName: z.string(),
1464
+ }),
1465
+ "crm.views.unarchive": z.object({
1466
+ objectName: z.string(),
1467
+ viewName: z.string(),
1468
+ }),
1427
1469
 
1428
1470
  // CRM fields
1429
1471
  "crm.fields.list": z.object({ objectName: z.string() }),
@@ -1991,6 +2033,12 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1991
2033
  "tool.search": composioToolSearch,
1992
2034
  "tool.run": composioToolRun,
1993
2035
  "tool.disconnect": z.object({ connectionId: z.string() }),
2036
+ "tool.label.set": z.object({
2037
+ connectionId: z.string(),
2038
+ displayName: z
2039
+ .string()
2040
+ .describe("Human name for the account. Empty clears it."),
2041
+ }),
1994
2042
  };
1995
2043
 
1996
2044
  // ---------------------------------------------------------------------------
@@ -2177,6 +2225,22 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
2177
2225
  "crm.objects.update": okResponse,
2178
2226
  "crm.objects.rename": okResponse,
2179
2227
  "crm.objects.delete": okResponse,
2228
+ "crm.objects.archive": okResponse,
2229
+ "crm.objects.unarchive": okResponse,
2230
+
2231
+ // The workspace app rail
2232
+ "crm.sidebar.list": z.object({
2233
+ visible: z.array(z.record(z.string(), z.unknown())),
2234
+ hidden: z.array(z.record(z.string(), z.unknown())),
2235
+ }),
2236
+ "crm.sidebar.move": z.object({
2237
+ ok: z.boolean(),
2238
+ navId: z.string(),
2239
+ /** 1-based slot the row landed in, or null when the move was refused. */
2240
+ position: z.number().nullable(),
2241
+ visibleOrder: z.array(z.string()).optional(),
2242
+ error: z.string().optional(),
2243
+ }),
2180
2244
 
2181
2245
  // CRM saved views
2182
2246
  "crm.views.list": z.object({
@@ -2205,6 +2269,8 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
2205
2269
  viewName: z.string(),
2206
2270
  removed: z.boolean(),
2207
2271
  }),
2272
+ "crm.views.archive": okResponse,
2273
+ "crm.views.unarchive": okResponse,
2208
2274
 
2209
2275
  // CRM fields
2210
2276
  "crm.fields.list": z.array(crmFieldDoc),
@@ -2676,6 +2742,15 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
2676
2742
  })
2677
2743
  .passthrough()
2678
2744
  .describe("Composio delete response (provider passthrough)."),
2745
+ "tool.labels.list": z.object({
2746
+ labels: z.array(
2747
+ z.object({ composioConnectionId: z.string(), displayName: z.string() }),
2748
+ ),
2749
+ }),
2750
+ "tool.label.set": z.object({
2751
+ ok: z.boolean(),
2752
+ displayName: z.string().nullable(),
2753
+ }),
2679
2754
 
2680
2755
  // Agent config
2681
2756
  "agentConfig.identity.show": agentConfigPayload,
@@ -2714,6 +2789,27 @@ export type ConvexExtras = {
2714
2789
  export const convexExtras: Record<string, ConvexExtras> = {
2715
2790
  "cron.enable": { fixedArgs: { enabled: true } },
2716
2791
  "cron.disable": { fixedArgs: { enabled: false } },
2792
+ // Archive/unarchive are one mutation behind two paths, so the flag comes
2793
+ // from the route rather than the body — a caller cannot un-archive by
2794
+ // POSTing `{ archived: false }` to `/archive`.
2795
+ "crm.objects.archive": { fixedArgs: { archived: true } },
2796
+ "crm.objects.unarchive": { fixedArgs: { archived: false } },
2797
+ // Same mutation for a saved view, but the path calls the object
2798
+ // `objectName` while `setArchived` calls it `name`.
2799
+ "crm.views.archive": {
2800
+ transformArgs: (input) => ({
2801
+ name: input.objectName,
2802
+ viewName: input.viewName,
2803
+ }),
2804
+ fixedArgs: { archived: true },
2805
+ },
2806
+ "crm.views.unarchive": {
2807
+ transformArgs: (input) => ({
2808
+ name: input.objectName,
2809
+ viewName: input.viewName,
2810
+ }),
2811
+ fixedArgs: { archived: false },
2812
+ },
2717
2813
  // PATCH /crm/objects/{objectName}/views/{viewName} — the path's
2718
2814
  // viewName is the upsert key: fold it into `view.name` and force
2719
2815
  // replaceExisting so the mutation overwrites instead of erroring on