@dench.com/cli 3.8.2-staging.391 → 3.8.3-staging.398

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.
@@ -2428,6 +2428,19 @@ const campaignSignal = z.enum([
2428
2428
  "funding",
2429
2429
  "launch",
2430
2430
  "new_leader",
2431
+ "voice_engagement",
2432
+ "company_engagement",
2433
+ "brand_engagement",
2434
+ "lookalike",
2435
+ ]);
2436
+ /** Signals that take terms: topics, roles, or LinkedIn profile/page URLs. */
2437
+ const campaignTermSignal = z.enum([
2438
+ "post_engagement",
2439
+ "keyword",
2440
+ "hiring",
2441
+ "voice_engagement",
2442
+ "company_engagement",
2443
+ "brand_engagement",
2431
2444
  ]);
2432
2445
  const campaignTargeting = z.object({
2433
2446
  titles: z.array(z.string()),
@@ -2436,12 +2449,104 @@ const campaignTargeting = z.object({
2436
2449
  industries: z.array(z.string()),
2437
2450
  excludes: z.array(z.string()),
2438
2451
  companies: z.array(z.string()).optional(),
2452
+ companyTypes: z.array(z.string()).optional(),
2439
2453
  });
2440
2454
  const campaignSignalTerms = z.object({
2441
- post_engagement: z.array(z.string()).optional(),
2455
+ post_engagement: z
2456
+ .array(z.string())
2457
+ .optional()
2458
+ .describe(
2459
+ 'Post topics, phrased as what the buyer does ("using AI for outbound"), not bare product names.',
2460
+ ),
2442
2461
  keyword: z.array(z.string()).optional(),
2443
- hiring: z.array(z.string()).optional(),
2462
+ hiring: z.array(z.string()).optional().describe("Roles being hired for."),
2463
+ voice_engagement: z
2464
+ .array(z.string())
2465
+ .optional()
2466
+ .describe("LinkedIn profile URLs of market voices whose posts to watch."),
2467
+ company_engagement: z
2468
+ .array(z.string())
2469
+ .optional()
2470
+ .describe("LinkedIn company page URLs of companies and competitors."),
2471
+ brand_engagement: z
2472
+ .array(z.string())
2473
+ .optional()
2474
+ .describe("The sender's own LinkedIn company page URL."),
2475
+ exact: z
2476
+ .array(z.enum(["post_engagement", "keyword"]))
2477
+ .optional()
2478
+ .describe(
2479
+ "Topic signals searched by their exact keywords only (no drafted phrases).",
2480
+ ),
2481
+ });
2482
+ const campaignDraftSource = z
2483
+ .enum(["signals", "lookalike", "existing", "crm", "search"])
2484
+ .describe(
2485
+ "How people come in: buying signals, people like one best lead, a pasted list or CSV, CRM records, or a people search.",
2486
+ );
2487
+ const campaignDraftScreen = z.enum([
2488
+ "source",
2489
+ "signals",
2490
+ "lookalike",
2491
+ "target",
2492
+ "messages",
2493
+ "outreach",
2494
+ "review",
2495
+ ]);
2496
+ const campaignLookalike = z.object({
2497
+ seed: z.object({
2498
+ name: z.string(),
2499
+ linkedinUrl: z.string(),
2500
+ title: z.string().optional(),
2501
+ companyName: z.string().optional(),
2502
+ companyDomain: z.string().optional(),
2503
+ avatarUrl: z.string().optional(),
2504
+ }),
2505
+ title: z.string(),
2506
+ similarRoles: z.boolean(),
2507
+ roles: z.array(z.string()),
2508
+ location: z.string().optional(),
2509
+ industry: z.string().optional(),
2510
+ relatedIndustries: z.boolean(),
2511
+ industries: z.array(z.string()),
2512
+ companySizes: z.array(z.string()),
2513
+ companies: z.array(z.object({ name: z.string(), domain: z.string() })),
2444
2514
  });
2515
+ const campaignOutreachBrief = z.object({
2516
+ goal: z.string().describe("What a reply should lead to, e.g. a demo."),
2517
+ helpWith: z.string().describe("The problem the product solves for them."),
2518
+ tone: z.enum(["conversational", "warm", "direct", "formal"]),
2519
+ channels: z.enum(["both", "email", "linkedin"]),
2520
+ });
2521
+ const campaignDraftStep = z.object({
2522
+ type: z.enum(["email", "wait", "linkedin_connection", "linkedin_message"]),
2523
+ waitHours: z.number().optional(),
2524
+ emailSubject: z.string().optional(),
2525
+ emailTextBody: z.string().optional(),
2526
+ emailReplyToPrevious: z.boolean().optional(),
2527
+ onlyIf: z.enum(["always", "connected", "not_connected"]).optional(),
2528
+ linkedinMessageBody: z.string().optional(),
2529
+ linkedinMessageMode: z
2530
+ .enum(["skip_if_not_connected", "wait_until_connected"])
2531
+ .optional(),
2532
+ connectionTimeoutHours: z.number().optional(),
2533
+ });
2534
+ const campaignDraftSending = z.object({
2535
+ emailMailboxAccountIds: z.array(z.string()),
2536
+ linkedinAccountIds: z.array(z.string()),
2537
+ timezone: z.string(),
2538
+ startHour: z.number().int().min(0).max(24),
2539
+ endHour: z.number().int().min(0).max(24),
2540
+ weekdays: z
2541
+ .array(z.number().int().min(0).max(6))
2542
+ .describe("0 is Sunday, 6 is Saturday."),
2543
+ dailySendCap: z.number().int().min(1).max(100).optional(),
2544
+ });
2545
+ const campaignApprovalMode = z
2546
+ .enum(["auto", "manual"])
2547
+ .describe(
2548
+ "auto: new people a signal finds start the sequence on their own. manual: they wait for approval.",
2549
+ );
2445
2550
  const campaignClientRequestId = z
2446
2551
  .string()
2447
2552
  .trim()
@@ -3047,18 +3152,30 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3047
3152
  status: z.enum(["active", "paused"]).optional(),
3048
3153
  cadence: z.enum(["daily", "weekly", "monthly"]).optional(),
3049
3154
  findMoreMatching: z.boolean().optional(),
3155
+ approvalMode: campaignApprovalMode.optional(),
3050
3156
  targeting: campaignTargeting.optional(),
3051
3157
  signal: z
3052
3158
  .object({ signal: campaignSignal, enabled: z.boolean() })
3053
3159
  .optional(),
3054
3160
  terms: z
3055
3161
  .object({
3056
- signal: z.enum(["post_engagement", "keyword", "hiring"]),
3162
+ signal: campaignTermSignal,
3057
3163
  values: z.array(z.string()),
3058
3164
  })
3059
3165
  .optional(),
3060
3166
  })
3061
3167
  .strict(),
3168
+ "campaigns.sources.run": z.object({ sequenceId: z.string() }).strict(),
3169
+ "campaigns.sources.overview": z.object({ sequenceId: z.string() }).strict(),
3170
+ "campaigns.sources.pending": z.object({ sequenceId: z.string() }).strict(),
3171
+ "campaigns.sources.review": z
3172
+ .object({
3173
+ sequenceId: z.string(),
3174
+ enrollmentIds: z.array(z.string()).min(1),
3175
+ decision: z.enum(["approve", "reject"]),
3176
+ })
3177
+ .strict(),
3178
+ "campaigns.queue": z.object({ sequenceId: z.string() }).strict(),
3062
3179
  "campaigns.sources.preview": z
3063
3180
  .object({
3064
3181
  sequenceId: z.string(),
@@ -3073,6 +3190,18 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3073
3190
  targeting: campaignTargeting,
3074
3191
  signals: z.array(campaignSignal),
3075
3192
  signalTerms: campaignSignalTerms.optional(),
3193
+ findMoreMatching: z
3194
+ .boolean()
3195
+ .optional()
3196
+ .describe("Also add people who match the audience without a signal."),
3197
+ name: z.string().max(120).optional(),
3198
+ source: campaignDraftSource.optional(),
3199
+ screen: campaignDraftScreen.optional(),
3200
+ lookalike: campaignLookalike
3201
+ .optional()
3202
+ .describe(
3203
+ 'For source lookalike: the edited result of builder lookalike, with signals ["lookalike"].',
3204
+ ),
3076
3205
  })
3077
3206
  .strict(),
3078
3207
  "campaigns.builder.get": z.object({ draftId: z.string() }).strict(),
@@ -3082,6 +3211,59 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3082
3211
  draftId: z.string(),
3083
3212
  name: z.string().max(120).optional(),
3084
3213
  timezone: z.string().optional(),
3214
+ paused: z
3215
+ .boolean()
3216
+ .optional()
3217
+ .describe("Save paused: signals wait to run until launch."),
3218
+ })
3219
+ .strict(),
3220
+ "campaigns.builder.drafts": noInput,
3221
+ "campaigns.builder.resume": z.object({ draftId: z.string() }).strict(),
3222
+ "campaigns.builder.discard": z.object({ draftId: z.string() }).strict(),
3223
+ "campaigns.builder.outreach": z
3224
+ .object({
3225
+ draftId: z.string(),
3226
+ outreachMode: z
3227
+ .enum(["ai", "manual"])
3228
+ .optional()
3229
+ .describe(
3230
+ "ai: generate the sequence from the brief. manual: write steps.",
3231
+ ),
3232
+ brief: campaignOutreachBrief.optional(),
3233
+ steps: z.array(campaignDraftStep).optional(),
3234
+ sending: campaignDraftSending.optional(),
3235
+ approvalMode: campaignApprovalMode.optional(),
3236
+ })
3237
+ .strict(),
3238
+ "campaigns.builder.sequence": z
3239
+ .object({ draftId: z.string(), brief: campaignOutreachBrief })
3240
+ .strict(),
3241
+ "campaigns.builder.lookalike": z
3242
+ .object({
3243
+ linkedinUrl: z
3244
+ .string()
3245
+ .max(300)
3246
+ .optional()
3247
+ .describe("The best lead's LinkedIn profile URL."),
3248
+ suggest: z
3249
+ .object({ title: z.string(), industry: z.string().optional() })
3250
+ .optional()
3251
+ .describe("Suggest similar roles and related industries again."),
3252
+ rescope: z
3253
+ .object({
3254
+ seed: campaignLookalike.shape.seed,
3255
+ company: z.string(),
3256
+ title: z.string().optional(),
3257
+ location: z.string().optional(),
3258
+ })
3259
+ .optional()
3260
+ .describe("Re-scope the lookalike to a different company."),
3261
+ })
3262
+ .strict(),
3263
+ "campaigns.builder.voices": z
3264
+ .object({
3265
+ topics: z.array(z.string()).optional(),
3266
+ market: z.string().max(300).optional(),
3085
3267
  })
3086
3268
  .strict(),
3087
3269
  "campaigns.builder.opener": z
@@ -3090,9 +3272,6 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3090
3272
  "campaigns.builder.autofill": z
3091
3273
  .object({ description: z.string().min(3).max(300) })
3092
3274
  .strict(),
3093
- "campaigns.builder.terms": z
3094
- .object({ targeting: campaignTargeting })
3095
- .strict(),
3096
3275
  "campaigns.builder.estimate": z
3097
3276
  .object({
3098
3277
  kind: z.enum(["people", "week"]),
@@ -5091,6 +5270,11 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5091
5270
  "campaigns.sources.signals": z.record(z.string(), z.unknown()).nullable(),
5092
5271
  "campaigns.sources.update": z.null(),
5093
5272
  "campaigns.sources.preview": z.object({ ok: z.boolean() }).passthrough(),
5273
+ "campaigns.sources.run": z.null(),
5274
+ "campaigns.sources.overview": z.record(z.string(), z.unknown()),
5275
+ "campaigns.sources.pending": z.array(z.record(z.string(), z.unknown())),
5276
+ "campaigns.sources.review": z.record(z.string(), z.unknown()),
5277
+ "campaigns.queue": z.record(z.string(), z.unknown()).nullable(),
5094
5278
  "campaigns.builder.profile": z
5095
5279
  .object({ hasProfile: z.boolean(), targeting: campaignTargeting })
5096
5280
  .passthrough(),
@@ -5112,12 +5296,24 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5112
5296
  enrolled: z.number(),
5113
5297
  }),
5114
5298
  "campaigns.builder.opener": z.null(),
5299
+ "campaigns.builder.drafts": z.array(z.record(z.string(), z.unknown())),
5300
+ "campaigns.builder.resume": z
5301
+ .object({
5302
+ clientRequestId: z.string(),
5303
+ status: z.string(),
5304
+ targeting: campaignTargeting,
5305
+ signals: z.array(campaignSignal),
5306
+ })
5307
+ .passthrough()
5308
+ .nullable(),
5309
+ "campaigns.builder.discard": z.null(),
5310
+ "campaigns.builder.outreach": z.null(),
5311
+ "campaigns.builder.sequence": z.record(z.string(), z.unknown()),
5312
+ "campaigns.builder.lookalike": z.record(z.string(), z.unknown()),
5313
+ "campaigns.builder.voices": z.record(z.string(), z.unknown()),
5115
5314
  "campaigns.builder.autofill": z
5116
5315
  .object({ targeting: campaignTargeting })
5117
5316
  .passthrough(),
5118
- "campaigns.builder.terms": z
5119
- .object({ signalTerms: campaignSignalTerms })
5120
- .passthrough(),
5121
5317
  "campaigns.builder.estimate": z
5122
5318
  .object({ count: z.number().nullable() })
5123
5319
  .passthrough(),
@@ -104,14 +104,25 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
104
104
  {"id":"campaigns.sources.signals","group":"campaigns","summary":"Read signal choices, suggested terms and current source configuration.","cli":"dench campaign sources signals","method":"GET","path":"/campaigns/{sequenceId}/sources/signals","permission":"campaigns:read","pathParams":["sequenceId"]},
105
105
  {"id":"campaigns.sources.update","group":"campaigns","summary":"Update source cadence, pause/resume, targeting, signals or terms. Active sources may perform billable future work.","cli":"dench campaign sources set","method":"PATCH","path":"/campaigns/{sequenceId}/sources","permission":"campaigns:write","pathParams":["sequenceId"]},
106
106
  {"id":"campaigns.sources.preview","group":"campaigns","summary":"Preview a signal source using its current audience. Provider credits may apply; does not enroll people.","cli":"dench campaign sources preview","method":"POST","path":"/campaigns/{sequenceId}/sources/preview","permission":"campaigns:write","pathParams":["sequenceId"]},
107
+ {"id":"campaigns.sources.run","group":"campaigns","summary":"Run a live campaign's signals now instead of waiting for the schedule (Run now). Provider credits apply; limited to 6 runs an hour.","cli":"dench campaign sources run","method":"POST","path":"/campaigns/{sequenceId}/sources/run","permission":"campaigns:write","pathParams":["sequenceId"]},
108
+ {"id":"campaigns.sources.overview","group":"campaigns","summary":"Read the Sources tab: each signal's status, last and next run, people found and anything needing attention.","cli":"dench campaign sources overview","method":"GET","path":"/campaigns/{sequenceId}/sources/overview","permission":"campaigns:read","pathParams":["sequenceId"]},
109
+ {"id":"campaigns.sources.pending","group":"campaigns","summary":"List people a signal found who are waiting for approval (manual approval mode).","cli":"dench campaign sources pending","method":"GET","path":"/campaigns/{sequenceId}/sources/pending","permission":"campaigns:read","pathParams":["sequenceId"]},
110
+ {"id":"campaigns.sources.review","group":"campaigns","summary":"Approve people waiting for review (they start the sequence) or reject them.","cli":"dench campaign sources approve|reject","method":"POST","path":"/campaigns/{sequenceId}/sources/review","permission":"campaigns:write","pathParams":["sequenceId"]},
111
+ {"id":"campaigns.queue","group":"campaigns","summary":"Today's queue: who the campaign plans to contact today, warmest first, within the senders' limits.","cli":"dench campaign queue","method":"GET","path":"/campaigns/{sequenceId}/queue","permission":"campaigns:read","pathParams":["sequenceId"]},
107
112
  {"id":"campaigns.builder.profile","group":"campaigns","summary":"Read the company profile and suggested campaign audience.","cli":"dench campaign builder profile","method":"GET","path":"/campaigns/builder/profile","permission":"campaigns:read","pathParams":[]},
108
113
  {"id":"campaigns.builder.save","group":"campaigns","summary":"Save a signal campaign's audience, signals and terms idempotently. Does not start a build.","cli":"dench campaign builder save","method":"POST","path":"/campaigns/builder/drafts","permission":"campaigns:write","pathParams":[]},
109
114
  {"id":"campaigns.builder.get","group":"campaigns","summary":"Read signal build progress, preview people, messages and errors.","cli":"dench campaign builder get","method":"GET","path":"/campaigns/builder/drafts/{draftId}","permission":"campaigns:read","pathParams":["draftId"]},
110
115
  {"id":"campaigns.builder.build","group":"campaigns","summary":"Start the same billable signal/audience/message build used by the UI; poll the draft for progress.","cli":"dench campaign builder build","method":"POST","path":"/campaigns/builder/drafts/{draftId}/build","permission":"campaigns:write","pathParams":["draftId"]},
111
116
  {"id":"campaigns.builder.create","group":"campaigns","summary":"Create a campaign from a reviewed signal draft. Does not launch the campaign.","cli":"dench campaign builder create","method":"POST","path":"/campaigns/builder/drafts/{draftId}/create","permission":"campaigns:write","pathParams":["draftId"]},
112
117
  {"id":"campaigns.builder.opener","group":"campaigns","summary":"Edit or reset one preview person's opening line before creating the campaign.","cli":"dench campaign builder opener","method":"PUT","path":"/campaigns/builder/drafts/{draftId}/people/{leadId}/opener","permission":"campaigns:write","pathParams":["draftId","leadId"]},
118
+ {"id":"campaigns.builder.drafts","group":"campaigns","summary":"List saved, unfinished campaign drafts that can be resumed.","cli":"dench campaign builder drafts","method":"GET","path":"/campaigns/builder/drafts","permission":"campaigns:read","pathParams":[]},
119
+ {"id":"campaigns.builder.resume","group":"campaigns","summary":"Read a saved draft's setup (source, name, targeting, signals, terms, lookalike) to resume it.","cli":"dench campaign builder resume","method":"GET","path":"/campaigns/builder/drafts/{draftId}/setup","permission":"campaigns:read","pathParams":["draftId"]},
120
+ {"id":"campaigns.builder.discard","group":"campaigns","summary":"Discard a saved draft that has not become a campaign.","cli":"dench campaign builder discard","method":"DELETE","path":"/campaigns/builder/drafts/{draftId}","permission":"campaigns:write","pathParams":["draftId"]},
121
+ {"id":"campaigns.builder.outreach","group":"campaigns","summary":"Save a draft's Outreach and Review choices: AI or manual steps, the brief, the steps, who sends and when, and auto or manual approval.","cli":"dench campaign builder outreach","method":"PATCH","path":"/campaigns/builder/drafts/{draftId}/outreach","permission":"campaigns:write","pathParams":["draftId"]},
122
+ {"id":"campaigns.builder.sequence","group":"campaigns","summary":"Generate the draft's sequence from a brief (goal, problem, tone, channels), as Generate sequence does. AI credits apply.","cli":"dench campaign builder sequence","method":"POST","path":"/campaigns/builder/drafts/{draftId}/sequence","permission":"campaigns:write","pathParams":["draftId"]},
123
+ {"id":"campaigns.builder.lookalike","group":"campaigns","summary":"Set up a lookalike from the best lead's LinkedIn URL (role, similar roles, place, industry, similar companies), suggest roles again, or re-scope to a company. Nothing is saved. Provider credits apply.","cli":"dench campaign builder lookalike","method":"POST","path":"/campaigns/builder/lookalike","permission":"campaigns:write","pathParams":[]},
124
+ {"id":"campaigns.builder.voices","group":"campaigns","summary":"Suggest market voices (LinkedIn experts) for the campaign's topics, to watch with voice_engagement. AI credits apply.","cli":"dench campaign builder voices","method":"POST","path":"/campaigns/builder/voices","permission":"campaigns:write","pathParams":[]},
113
125
  {"id":"campaigns.builder.autofill","group":"campaigns","summary":"Turn an audience description into targeting filters. AI credits apply.","cli":"dench campaign builder autofill","method":"POST","path":"/campaigns/builder/autofill","permission":"campaigns:write","pathParams":[]},
114
- {"id":"campaigns.builder.terms","group":"campaigns","summary":"Suggest signal terms for the selected audience. AI credits apply.","cli":"dench campaign builder terms","method":"POST","path":"/campaigns/builder/terms","permission":"campaigns:write","pathParams":[]},
115
126
  {"id":"campaigns.builder.estimate","group":"campaigns","summary":"Estimate audience or weekly signal volume using the UI's cached estimator. Provider credits may apply.","cli":"dench campaign builder estimate","method":"POST","path":"/campaigns/builder/estimate","permission":"campaigns:write","pathParams":[]},
116
127
  {"id":"commands.list","group":"meta","summary":"List all CLI/API operations and parity classifications.","cli":"dench help","method":"GET","path":"/commands","permission":null,"pathParams":[]},
117
128
  {"id":"auth.signin.create","group":"auth","summary":"Create an agent sign-in request.","cli":"dench signin","method":"POST","path":"/auth/signin-requests","permission":"auth:write","pathParams":[]},
@@ -607,7 +618,17 @@ export const APP_SDK_METHODS: Readonly<Record<string, AppSdkMethodInfo>> = {
607
618
  "campaigns.createFromDraft": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.create","doc":"Create a campaign from a reviewed signal draft, without launching."},
608
619
  "campaigns.setDraftOpener": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.opener","doc":"Edit a preview person's opening line."},
609
620
  "campaigns.autofill": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.autofill","doc":"Translate a description into targeting. AI credits apply."},
610
- "campaigns.suggestTerms": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.terms","doc":"Suggest signal terms. AI credits apply."},
621
+ "campaigns.savedDrafts": {"kind":"op","permission":"campaigns:read","op":"campaigns.builder.drafts","doc":"List saved, unfinished campaign drafts."},
622
+ "campaigns.resumeDraft": {"kind":"op","permission":"campaigns:read","op":"campaigns.builder.resume","doc":"Read a saved draft's setup to resume it."},
623
+ "campaigns.discardDraft": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.discard","doc":"Discard a saved draft."},
624
+ "campaigns.saveOutreach": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.outreach","doc":"Save a draft's steps, brief, senders, schedule and approval mode."},
625
+ "campaigns.generateSequence": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.sequence","doc":"Generate a draft's sequence from a brief. AI credits apply."},
626
+ "campaigns.lookalike": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.lookalike","doc":"Set up a lookalike from a best lead's LinkedIn URL. Provider credits apply."},
627
+ "campaigns.suggestVoices": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.voices","doc":"Suggest LinkedIn experts to watch for the topics. AI credits apply."},
628
+ "campaigns.runSources": {"kind":"op","permission":"campaigns:write","op":"campaigns.sources.run","doc":"Run a live campaign's signals now. Provider credits apply."},
629
+ "campaigns.pendingReview": {"kind":"op","permission":"campaigns:read","op":"campaigns.sources.pending","doc":"People a signal found who wait for approval."},
630
+ "campaigns.reviewPeople": {"kind":"op","permission":"campaigns:write","op":"campaigns.sources.review","doc":"Approve or reject people waiting for review."},
631
+ "campaigns.todayQueue": {"kind":"op","permission":"campaigns:read","op":"campaigns.queue","doc":"Who the campaign plans to contact today."},
611
632
  "campaigns.estimate": {"kind":"op","permission":"campaigns:write","op":"campaigns.builder.estimate","doc":"Estimate audience or signal volume."},
612
633
  "campaigns.mailboxes": {"kind":"op","permission":"campaigns:read","op":"campaigns.accounts.email","doc":"Mailboxes campaigns can send from, with warm-up and daily cap."},
613
634
  "campaigns.linkedinAccounts": {"kind":"op","permission":"campaigns:read","op":"campaigns.accounts.linkedin","doc":"LinkedIn accounts campaigns can send from, with capacity."},
@@ -725,11 +725,71 @@ export const APP_SDK_SURFACE: AppSdkSurface = {
725
725
  args: `opts: ${A("campaigns.builder.autofill")}`,
726
726
  returns: R("campaigns.builder.autofill"),
727
727
  },
728
- suggestTerms: {
729
- op: "campaigns.builder.terms",
730
- doc: "Suggest signal terms. AI credits apply.",
731
- args: `opts: ${A("campaigns.builder.terms")}`,
732
- returns: R("campaigns.builder.terms"),
728
+ savedDrafts: {
729
+ op: "campaigns.builder.drafts",
730
+ doc: "List saved, unfinished campaign drafts.",
731
+ args: "",
732
+ returns: R("campaigns.builder.drafts"),
733
+ },
734
+ resumeDraft: {
735
+ op: "campaigns.builder.resume",
736
+ doc: "Read a saved draft's setup to resume it.",
737
+ args: "draftId: string",
738
+ returns: R("campaigns.builder.resume"),
739
+ },
740
+ discardDraft: {
741
+ op: "campaigns.builder.discard",
742
+ doc: "Discard a saved draft.",
743
+ args: "draftId: string",
744
+ returns: R("campaigns.builder.discard"),
745
+ },
746
+ saveOutreach: {
747
+ op: "campaigns.builder.outreach",
748
+ doc: "Save a draft's steps, brief, senders, schedule and approval mode.",
749
+ args: `opts: ${A("campaigns.builder.outreach")}`,
750
+ returns: R("campaigns.builder.outreach"),
751
+ },
752
+ generateSequence: {
753
+ op: "campaigns.builder.sequence",
754
+ doc: "Generate a draft's sequence from a brief. AI credits apply.",
755
+ args: `opts: ${A("campaigns.builder.sequence")}`,
756
+ returns: R("campaigns.builder.sequence"),
757
+ },
758
+ lookalike: {
759
+ op: "campaigns.builder.lookalike",
760
+ doc: "Set up a lookalike from a best lead's LinkedIn URL. Provider credits apply.",
761
+ args: `opts: ${A("campaigns.builder.lookalike")}`,
762
+ returns: R("campaigns.builder.lookalike"),
763
+ },
764
+ suggestVoices: {
765
+ op: "campaigns.builder.voices",
766
+ doc: "Suggest LinkedIn experts to watch for the topics. AI credits apply.",
767
+ args: `opts: ${A("campaigns.builder.voices")}`,
768
+ returns: R("campaigns.builder.voices"),
769
+ },
770
+ runSources: {
771
+ op: "campaigns.sources.run",
772
+ doc: "Run a live campaign's signals now. Provider credits apply.",
773
+ args: "sequenceId: string",
774
+ returns: R("campaigns.sources.run"),
775
+ },
776
+ pendingReview: {
777
+ op: "campaigns.sources.pending",
778
+ doc: "People a signal found who wait for approval.",
779
+ args: "sequenceId: string",
780
+ returns: R("campaigns.sources.pending"),
781
+ },
782
+ reviewPeople: {
783
+ op: "campaigns.sources.review",
784
+ doc: "Approve or reject people waiting for review.",
785
+ args: `opts: ${A("campaigns.sources.review")}`,
786
+ returns: R("campaigns.sources.review"),
787
+ },
788
+ todayQueue: {
789
+ op: "campaigns.queue",
790
+ doc: "Who the campaign plans to contact today.",
791
+ args: "sequenceId: string",
792
+ returns: R("campaigns.queue"),
733
793
  },
734
794
  estimate: {
735
795
  op: "campaigns.builder.estimate",
@@ -1501,6 +1501,12 @@ export async function runCampaignAgentCommand(
1501
1501
  searchSummary(page),
1502
1502
  );
1503
1503
  }
1504
+ if (command === "queue") {
1505
+ const reference = shift(ctx.args, "campaign id or name");
1506
+ finishArgs(ctx.args, "queue");
1507
+ const sequenceId = (await ctx.resolveCampaign(reference))._id;
1508
+ return print(ctx, await invoke(ctx, "campaigns.queue", { sequenceId }));
1509
+ }
1504
1510
  const verb = shift(ctx.args, `${command} subcommand`);
1505
1511
  const input = await objectInput(ctx.args);
1506
1512
  if (command === "sources") {
@@ -1509,12 +1515,26 @@ export async function runCampaignAgentCommand(
1509
1515
  signals: "signals",
1510
1516
  set: "update",
1511
1517
  preview: "preview",
1518
+ run: "run",
1519
+ overview: "overview",
1520
+ pending: "pending",
1521
+ approve: "review",
1522
+ reject: "review",
1512
1523
  };
1513
1524
  const operation = operations[verb];
1514
1525
  if (!operation)
1515
- throw new Error("Use campaign sources get|signals|set|preview");
1526
+ throw new Error(
1527
+ "Use campaign sources get|signals|overview|set|preview|run|pending|approve|reject",
1528
+ );
1516
1529
  const signal = value(ctx.args, "--signal");
1517
1530
  if (signal) input.signal = signal;
1531
+ const approval = value(ctx.args, "--approval");
1532
+ if (approval) input.approvalMode = approval;
1533
+ if (operation === "review") {
1534
+ input.decision = verb;
1535
+ const people = value(ctx.args, "--people");
1536
+ if (people) input.enrollmentIds = splitListValue(people);
1537
+ }
1518
1538
  const reference = shift(ctx.args, "campaign id or name");
1519
1539
  finishArgs(ctx.args, "sources");
1520
1540
  validate(`campaigns.sources.${operation}`, {
@@ -1538,12 +1558,18 @@ export async function runCampaignAgentCommand(
1538
1558
  "create",
1539
1559
  "opener",
1540
1560
  "autofill",
1541
- "terms",
1542
1561
  "estimate",
1562
+ "drafts",
1563
+ "resume",
1564
+ "discard",
1565
+ "outreach",
1566
+ "sequence",
1567
+ "lookalike",
1568
+ "voices",
1543
1569
  ]);
1544
1570
  if (!verbs.has(verb))
1545
1571
  throw new Error(
1546
- "Use campaign builder profile|save|get|build|create|opener|autofill|terms|estimate",
1572
+ "Use campaign builder profile|autofill|estimate|lookalike|voices|save|get|build|opener|outreach|sequence|create|drafts|resume|discard",
1547
1573
  );
1548
1574
  const name = value(ctx.args, "--name");
1549
1575
  const timezone = value(ctx.args, "--timezone");
@@ -1553,7 +1579,46 @@ export async function runCampaignAgentCommand(
1553
1579
  if (timezone) input.timezone = timezone;
1554
1580
  if (description) input.description = description;
1555
1581
  if (text !== undefined) input.opener = text;
1556
- if (["get", "build", "create", "opener"].includes(verb) && !input.draftId)
1582
+ if (verb === "create" && hasFlag(ctx.args, "--paused")) input.paused = true;
1583
+ if (verb === "lookalike") {
1584
+ const url = value(ctx.args, "--linkedin-url");
1585
+ if (url) input.linkedinUrl = url;
1586
+ }
1587
+ if (verb === "voices") {
1588
+ const topics = value(ctx.args, "--topics");
1589
+ const market = value(ctx.args, "--market");
1590
+ if (topics) input.topics = splitListValue(topics);
1591
+ if (market) input.market = market;
1592
+ }
1593
+ if (verb === "outreach") {
1594
+ const approval = value(ctx.args, "--approval");
1595
+ const mode = value(ctx.args, "--mode");
1596
+ if (approval) input.approvalMode = approval;
1597
+ if (mode) input.outreachMode = mode;
1598
+ }
1599
+ if (verb === "sequence" && !input.brief) {
1600
+ const brief = {
1601
+ goal: value(ctx.args, "--goal"),
1602
+ helpWith: value(ctx.args, "--help-with"),
1603
+ tone: value(ctx.args, "--tone") ?? "conversational",
1604
+ channels: value(ctx.args, "--channels") ?? "both",
1605
+ };
1606
+ if (brief.goal || brief.helpWith)
1607
+ input.brief = { ...brief, helpWith: brief.helpWith ?? "" };
1608
+ }
1609
+ if (
1610
+ [
1611
+ "get",
1612
+ "build",
1613
+ "create",
1614
+ "opener",
1615
+ "resume",
1616
+ "discard",
1617
+ "outreach",
1618
+ "sequence",
1619
+ ].includes(verb) &&
1620
+ !input.draftId
1621
+ )
1557
1622
  input.draftId = shift(ctx.args, "draft id");
1558
1623
  if (verb === "opener" && !input.leadId)
1559
1624
  input.leadId = shift(ctx.args, "draft lead id");