@sellable/install 0.1.317 → 0.1.318

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.
@@ -26,8 +26,8 @@
26
26
  "ownership": "message strategy, proof inventory, token rules, skeptical-prospect review, and selected winner only",
27
27
  "codex": {
28
28
  "description": "Message Drafting worker for campaign-backed template proposals after confirm_lead_list imports a non-empty bounded review batch.",
29
- "model": "gpt-5.5",
30
- "modelReasoningEffort": "xhigh",
29
+ "model": "gpt-5.6-sol",
30
+ "modelReasoningEffort": "high",
31
31
  "sandboxMode": "read-only",
32
32
  "nicknameCandidates": [
33
33
  "Message Drafting",
@@ -1660,7 +1660,7 @@ Treat host capabilities as concrete functions, not prose conventions:
1660
1660
  import and before dispatching Message Drafting only.
1661
1661
  - \`launch_message_drafting\`: Claude Code uses \`Task\` with \`subagent_type\`
1662
1662
  \`post-find-leads-message-scout\` when listed; Codex uses the returned
1663
- compatibility agent or a generic \`gpt-5.5\` / \`xhigh\` Message Drafting agent.
1663
+ compatibility agent or a generic \`gpt-5.6-sol\` / \`high\` Message Drafting agent.
1664
1664
 
1665
1665
  If a required interactive question function or MCP loader is missing, stop and
1666
1666
  explain the Sellable install/reload problem. Source and filter work use
@@ -3057,8 +3057,8 @@ function generateCodexAgentToml(agent) {
3057
3057
  const codex = agent.codex;
3058
3058
  return `name = ${quoteToml(agent.name)}
3059
3059
  description = ${quoteToml(codex.description)}
3060
- model = ${quoteToml(codex.model || "gpt-5.5")}
3061
- model_reasoning_effort = ${quoteToml(codex.modelReasoningEffort || "xhigh")}
3060
+ model = ${quoteToml(codex.model || "gpt-5.6-sol")}
3061
+ model_reasoning_effort = ${quoteToml(codex.modelReasoningEffort || "high")}
3062
3062
  sandbox_mode = ${quoteToml(codex.sandboxMode || "read-only")}
3063
3063
  nickname_candidates = ${tomlArray(codex.nicknameCandidates || [agent.displayName])}
3064
3064
  developer_instructions = ${tomlMultilineString(agent.prompt)}
@@ -45,6 +45,14 @@ export const REQUIRED_SELLABLE_MCP_TOOLS = [
45
45
 
46
46
  export const FORBIDDEN_SELLABLE_MCP_TOOLS = ["refill_campaign_sends"];
47
47
 
48
+ const CONNECTION_ONLY_EVERGREEN_PROMPT_CHUNK_LIMIT = 48_000;
49
+ const CONNECTION_ONLY_EVERGREEN_MAX_PROMPT_CHUNKS = 8;
50
+ const CONNECTION_ONLY_EVERGREEN_PROOF_NEEDLES = {
51
+ campaignSequenceOptions: 'campaignSequenceOptions:{ mode:"connection_only" }',
52
+ templateRef: 'templateRef:"connection_only"',
53
+ sendInviteReceipt: 'sequenceReceipt.actionTypes:["send_invite"]',
54
+ };
55
+
48
56
  export const CREATE_CAMPAIGN_SMOKE_CALLS = [
49
57
  {
50
58
  name: "get_auth_status",
@@ -84,6 +92,14 @@ export const CREATE_CAMPAIGN_SMOKE_CALLS = [
84
92
  limit: 1200,
85
93
  },
86
94
  },
95
+ {
96
+ name: "get_subskill_prompt",
97
+ arguments: {
98
+ subskillName: "create-evergreen-campaigns",
99
+ offset: 0,
100
+ limit: CONNECTION_ONLY_EVERGREEN_PROMPT_CHUNK_LIMIT,
101
+ },
102
+ },
87
103
  {
88
104
  name: "refill_sends",
89
105
  arguments: {
@@ -187,6 +203,43 @@ function parseJsonText(text) {
187
203
  }
188
204
  }
189
205
 
206
+ function emptyConnectionOnlyEvergreenProofs() {
207
+ return Object.fromEntries(
208
+ Object.keys(CONNECTION_ONLY_EVERGREEN_PROOF_NEEDLES).map((key) => [
209
+ key,
210
+ false,
211
+ ])
212
+ );
213
+ }
214
+
215
+ function getConnectionOnlyEvergreenProofs(prompt) {
216
+ return Object.fromEntries(
217
+ Object.entries(CONNECTION_ONLY_EVERGREEN_PROOF_NEEDLES).map(
218
+ ([key, needle]) => [key, prompt.includes(needle)]
219
+ )
220
+ );
221
+ }
222
+
223
+ function mergeConnectionOnlyEvergreenProofs(target, source) {
224
+ if (!source) return target;
225
+ for (const key of Object.keys(CONNECTION_ONLY_EVERGREEN_PROOF_NEEDLES)) {
226
+ target[key] = target[key] === true || source[key] === true;
227
+ }
228
+ return target;
229
+ }
230
+
231
+ function summarizeConnectionOnlyEvergreenProofs(proofs) {
232
+ const merged = { ...emptyConnectionOnlyEvergreenProofs(), ...(proofs || {}) };
233
+ const missing = Object.keys(CONNECTION_ONLY_EVERGREEN_PROOF_NEEDLES).filter(
234
+ (key) => merged[key] !== true
235
+ );
236
+ return {
237
+ ok: missing.length === 0,
238
+ proofs: merged,
239
+ missing,
240
+ };
241
+ }
242
+
190
243
  function summarizeToolResult(name, result) {
191
244
  const text = redact(textFromToolResult(result));
192
245
  const parsed = parseJsonText(text);
@@ -230,10 +283,26 @@ function summarizeToolResult(name, result) {
230
283
  }
231
284
 
232
285
  if (name === "get_subskill_prompt" && parsed) {
286
+ const prompt = typeof parsed.prompt === "string" ? parsed.prompt : "";
287
+ const connectionOnlyEvergreenProofs =
288
+ parsed.name === "create-evergreen-campaigns"
289
+ ? getConnectionOnlyEvergreenProofs(prompt)
290
+ : null;
233
291
  return {
234
292
  ...summary,
235
293
  subskillName: parsed.name || null,
236
- promptChars: typeof parsed.prompt === "string" ? parsed.prompt.length : 0,
294
+ promptChars: prompt.length,
295
+ promptLength:
296
+ typeof parsed.promptLength === "number" ? parsed.promptLength : null,
297
+ offset: typeof parsed.offset === "number" ? parsed.offset : null,
298
+ limit: typeof parsed.limit === "number" ? parsed.limit : null,
299
+ connectionOnlyEvergreenProofs,
300
+ connectionOnlyEvergreenGuidance:
301
+ parsed.name === "create-evergreen-campaigns"
302
+ ? summarizeConnectionOnlyEvergreenProofs(
303
+ connectionOnlyEvergreenProofs
304
+ ).ok
305
+ : null,
237
306
  hasMore: parsed.hasMore === true,
238
307
  nextOffset:
239
308
  typeof parsed.nextOffset === "number" ? parsed.nextOffset : null,
@@ -267,6 +336,7 @@ async function verifyCreateCampaignSmoke({
267
336
  .map((call) => call.name)
268
337
  .filter((name) => !available.has(name));
269
338
  const calls = [];
339
+ const connectionOnlyEvergreenProofs = emptyConnectionOnlyEvergreenProofs();
270
340
 
271
341
  if (missingTools.length > 0) {
272
342
  return {
@@ -280,67 +350,195 @@ async function verifyCreateCampaignSmoke({
280
350
  }
281
351
 
282
352
  for (const call of smokeCalls) {
283
- const callStartedAt = new Date().toISOString();
284
- try {
285
- const result = await client.callTool(
286
- { name: call.name, arguments: call.arguments },
287
- undefined,
288
- {
289
- timeout: timeoutMs,
290
- maxTotalTimeout: timeoutMs,
353
+ let nextCall = call;
354
+ let evergreenPromptChunks = 0;
355
+ const evergreenPromptOffsets = new Set();
356
+
357
+ while (nextCall) {
358
+ const callStartedAt = new Date().toISOString();
359
+ try {
360
+ const result = await client.callTool(
361
+ { name: nextCall.name, arguments: nextCall.arguments },
362
+ undefined,
363
+ {
364
+ timeout: timeoutMs,
365
+ maxTotalTimeout: timeoutMs,
366
+ }
367
+ );
368
+ const summary = summarizeToolResult(nextCall.name, result);
369
+ calls.push({
370
+ name: nextCall.name,
371
+ arguments: nextCall.arguments,
372
+ ok: summary.isError !== true,
373
+ summary,
374
+ startedAt: callStartedAt,
375
+ completedAt: new Date().toISOString(),
376
+ });
377
+ if (summary.isError === true) {
378
+ return {
379
+ ok: false,
380
+ missingTools: [],
381
+ calls,
382
+ error: `${nextCall.name} returned an MCP tool error`,
383
+ startedAt,
384
+ completedAt: new Date().toISOString(),
385
+ };
291
386
  }
292
- );
293
- const summary = summarizeToolResult(call.name, result);
294
- calls.push({
295
- name: call.name,
296
- arguments: call.arguments,
297
- ok: summary.isError !== true,
298
- summary,
299
- startedAt: callStartedAt,
300
- completedAt: new Date().toISOString(),
301
- });
302
- if (summary.isError === true) {
387
+
388
+ const isConnectionOnlyEvergreenPrompt =
389
+ nextCall.name === "get_subskill_prompt" &&
390
+ nextCall.arguments?.subskillName === "create-evergreen-campaigns";
391
+ if (!isConnectionOnlyEvergreenPrompt) {
392
+ break;
393
+ }
394
+
395
+ evergreenPromptChunks += 1;
396
+ evergreenPromptOffsets.add(nextCall.arguments?.offset ?? 0);
397
+ mergeConnectionOnlyEvergreenProofs(
398
+ connectionOnlyEvergreenProofs,
399
+ summary.connectionOnlyEvergreenProofs
400
+ );
401
+
402
+ if (summary.hasMore !== true) {
403
+ break;
404
+ }
405
+ if (typeof summary.nextOffset !== "number") {
406
+ return {
407
+ ok: false,
408
+ missingTools: [],
409
+ calls,
410
+ connectionOnlyEvergreenGuidance:
411
+ summarizeConnectionOnlyEvergreenProofs(
412
+ connectionOnlyEvergreenProofs
413
+ ),
414
+ error:
415
+ "create-evergreen-campaigns prompt chunk is missing nextOffset",
416
+ startedAt,
417
+ completedAt: new Date().toISOString(),
418
+ };
419
+ }
420
+ if (evergreenPromptOffsets.has(summary.nextOffset)) {
421
+ return {
422
+ ok: false,
423
+ missingTools: [],
424
+ calls,
425
+ connectionOnlyEvergreenGuidance:
426
+ summarizeConnectionOnlyEvergreenProofs(
427
+ connectionOnlyEvergreenProofs
428
+ ),
429
+ error:
430
+ "create-evergreen-campaigns prompt chunk repeated nextOffset",
431
+ startedAt,
432
+ completedAt: new Date().toISOString(),
433
+ };
434
+ }
435
+ if (
436
+ evergreenPromptChunks >= CONNECTION_ONLY_EVERGREEN_MAX_PROMPT_CHUNKS
437
+ ) {
438
+ return {
439
+ ok: false,
440
+ missingTools: [],
441
+ calls,
442
+ connectionOnlyEvergreenGuidance:
443
+ summarizeConnectionOnlyEvergreenProofs(
444
+ connectionOnlyEvergreenProofs
445
+ ),
446
+ error:
447
+ "create-evergreen-campaigns prompt exceeded smoke chunk limit",
448
+ startedAt,
449
+ completedAt: new Date().toISOString(),
450
+ };
451
+ }
452
+
453
+ nextCall = {
454
+ ...nextCall,
455
+ arguments: {
456
+ ...nextCall.arguments,
457
+ offset: summary.nextOffset,
458
+ limit:
459
+ nextCall.arguments?.limit ||
460
+ CONNECTION_ONLY_EVERGREEN_PROMPT_CHUNK_LIMIT,
461
+ },
462
+ };
463
+ } catch (err) {
464
+ const error = err instanceof Error ? err.message : String(err);
465
+ calls.push({
466
+ name: nextCall.name,
467
+ arguments: nextCall.arguments,
468
+ ok: false,
469
+ summary: null,
470
+ error: redact(error),
471
+ startedAt: callStartedAt,
472
+ completedAt: new Date().toISOString(),
473
+ });
303
474
  return {
304
475
  ok: false,
305
476
  missingTools: [],
306
477
  calls,
307
- error: `${call.name} returned an MCP tool error`,
478
+ error: redact(error),
308
479
  startedAt,
309
480
  completedAt: new Date().toISOString(),
310
481
  };
311
482
  }
312
- } catch (err) {
313
- const error = err instanceof Error ? err.message : String(err);
314
- calls.push({
315
- name: call.name,
316
- arguments: call.arguments,
317
- ok: false,
318
- summary: null,
319
- error: redact(error),
320
- startedAt: callStartedAt,
321
- completedAt: new Date().toISOString(),
322
- });
323
- return {
324
- ok: false,
325
- missingTools: [],
326
- calls,
327
- error: redact(error),
328
- startedAt,
329
- completedAt: new Date().toISOString(),
330
- };
331
483
  }
332
484
  }
333
485
 
486
+ const connectionOnlyEvergreenGuidance =
487
+ summarizeConnectionOnlyEvergreenProofs(connectionOnlyEvergreenProofs);
488
+ if (connectionOnlyEvergreenGuidance.ok !== true) {
489
+ return {
490
+ ok: false,
491
+ missingTools: [],
492
+ calls,
493
+ connectionOnlyEvergreenGuidance,
494
+ error: `create-evergreen-campaigns prompt is missing connection-only guidance: ${connectionOnlyEvergreenGuidance.missing.join(", ")}`,
495
+ startedAt,
496
+ completedAt: new Date().toISOString(),
497
+ };
498
+ }
499
+
334
500
  return {
335
501
  ok: true,
336
502
  missingTools: [],
337
503
  calls,
504
+ connectionOnlyEvergreenGuidance,
338
505
  error: null,
339
506
  startedAt,
340
507
  completedAt: new Date().toISOString(),
341
508
  };
342
509
  }
343
510
 
511
+ function verifyConnectionOnlyEvergreenContract(tools) {
512
+ const setupTool = (tools || []).find(
513
+ (tool) => tool?.name === "setup_evergreen_campaigns"
514
+ );
515
+ const schema = setupTool?.inputSchema || {};
516
+ const properties = schema.properties || {};
517
+ const sequenceOptions = properties.campaignSequenceOptions || {};
518
+ const mode = sequenceOptions.properties?.mode || {};
519
+ const issues = [];
520
+
521
+ if (!properties.workspaceId) {
522
+ issues.push("setup_evergreen_campaigns.workspaceId");
523
+ }
524
+ if (!properties.campaignSequenceOptions) {
525
+ issues.push("setup_evergreen_campaigns.campaignSequenceOptions");
526
+ }
527
+ if (!Array.isArray(mode.enum) || !mode.enum.includes("connection_only")) {
528
+ issues.push("setup_evergreen_campaigns.campaignSequenceOptions.mode");
529
+ }
530
+ if (sequenceOptions.additionalProperties !== false) {
531
+ issues.push(
532
+ "setup_evergreen_campaigns.campaignSequenceOptions.additionalProperties"
533
+ );
534
+ }
535
+
536
+ return {
537
+ ok: issues.length === 0,
538
+ issues,
539
+ };
540
+ }
541
+
344
542
  function summarizeAttempt(result, attempt) {
345
543
  return {
346
544
  attempt,
@@ -348,6 +546,12 @@ function summarizeAttempt(result, attempt) {
348
546
  availableToolCount: result.availableTools?.length || 0,
349
547
  missingToolCount: result.missingTools?.length || 0,
350
548
  forbiddenToolCount: result.forbiddenTools?.length || 0,
549
+ connectionOnlyEvergreenContract: result.connectionOnlyEvergreenContract
550
+ ? {
551
+ ok: result.connectionOnlyEvergreenContract.ok === true,
552
+ issues: result.connectionOnlyEvergreenContract.issues || [],
553
+ }
554
+ : null,
351
555
  error: result.error || null,
352
556
  createCampaignSmoke: result.createCampaignSmoke
353
557
  ? {
@@ -419,6 +623,7 @@ async function verifySellableMcpRuntimeOnce({
419
623
  let missingTools = [...requiredTools];
420
624
  let forbiddenTools = [];
421
625
  let createCampaignSmoke = null;
626
+ let connectionOnlyEvergreenContract = { ok: false, issues: [] };
422
627
  let error = null;
423
628
 
424
629
  try {
@@ -432,6 +637,8 @@ async function verifySellableMcpRuntimeOnce({
432
637
  forbiddenTools = availableTools.filter((tool) =>
433
638
  FORBIDDEN_SELLABLE_MCP_TOOLS.includes(tool)
434
639
  );
640
+ connectionOnlyEvergreenContract =
641
+ verifyConnectionOnlyEvergreenContract(toolList.tools);
435
642
  if (forbiddenTools.length > 0) {
436
643
  createCampaignSmoke = {
437
644
  ok: false,
@@ -475,6 +682,7 @@ async function verifySellableMcpRuntimeOnce({
475
682
  !error &&
476
683
  missingTools.length === 0 &&
477
684
  forbiddenTools.length === 0 &&
685
+ connectionOnlyEvergreenContract.ok === true &&
478
686
  createCampaignSmoke?.ok === true,
479
687
  skipped: false,
480
688
  command,
@@ -483,6 +691,7 @@ async function verifySellableMcpRuntimeOnce({
483
691
  availableTools,
484
692
  missingTools,
485
693
  forbiddenTools,
694
+ connectionOnlyEvergreenContract,
486
695
  createCampaignSmoke,
487
696
  stderrTail: stderrLines,
488
697
  error: error ? redact(error) : null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.317",
3
+ "version": "0.1.318",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {
@@ -721,7 +721,7 @@ Treat host capabilities as concrete functions, not prose conventions:
721
721
  import and before dispatching Message Drafting only.
722
722
  - `launch_message_drafting`: Claude Code uses `Task` with `subagent_type`
723
723
  `post-find-leads-message-scout` when listed; Codex uses the returned
724
- compatibility agent or a generic `gpt-5.5` / `xhigh` Message Drafting agent.
724
+ compatibility agent or a generic `gpt-5.6-sol` / `high` Message Drafting agent.
725
725
 
726
726
  If a required interactive question function or MCP loader is missing, stop and
727
727
  explain the Sellable install/reload problem. Source work uses product-native MCP
@@ -1082,8 +1082,8 @@ updates.
1082
1082
  In Codex, the filter-choice answer is the campaign-scoped go-ahead to use
1083
1083
  this single Message Drafting background agent in step-wise and YOLO modes.
1084
1084
  Do not ask a separate question to start it. If the named custom agent is not
1085
- available, spawn a generic background agent with `model: "gpt-5.5"` and
1086
- `reasoning_effort: "xhigh"` using the same lean campaign/table basis. If no
1085
+ available, spawn a generic background agent with `model: "gpt-5.6-sol"` and
1086
+ `reasoning_effort: "high"` using the same lean campaign/table basis. If no
1087
1087
  background-agent tool is callable, start the same full message branch inline
1088
1088
  before filter drafting or skip-filter message review and record it as
1089
1089
  `statusSource: "parent-thread-fallback"`.
@@ -134,6 +134,19 @@ run without user input, or similar, run in **automation mode**:
134
134
  exactly one quality-valid route-proof row when needed, and attach the
135
135
  recommended sequence, but it still must not launch campaigns, schedule
136
136
  sends, send messages, or spend paid InMail.
137
+ - **Connection-only evergreen completion is optional and non-default.** Use it
138
+ only when the prompt explicitly says connection-only, invite-only,
139
+ connection request only, no DMs, or no follow-ups. Pass the typed option
140
+ `campaignSequenceOptions:{ mode:"connection_only" }` plus an explicit
141
+ `workspaceId` to `setup_evergreen_campaigns`. Omit
142
+ `campaignSequenceOptions` for the standard tier-recommended Premium,
143
+ Sales Nav, Recruiter, and paid InMail behavior.
144
+ Connection-only completion still creates/reuses the same evergreen lanes,
145
+ source rows, and paused/unlaunched review state, but it attaches only the
146
+ canonical invite template and persists `currentStep:"send"` as the paused
147
+ review step. It does not require Message Drafting, generated messages,
148
+ route-proof approval, first DM, InMail, View Profile fallback, follow-up
149
+ branch, launch, scheduling, or sends.
137
150
 
138
151
  If the invoking prompt explicitly asks for interactive message polish or sample
139
152
  proof, run in **interactive polish mode** and use the confirmation/sample steps
@@ -424,7 +437,7 @@ fallback only when app thread tools are unavailable; the receipt must include
424
437
  `fallbackReason`, or backend verify rejects it.
425
438
  When visible Codex app thread tools are unavailable but local Codex CLI is
426
439
  available, the accepted durable streaming-worker command shape is:
427
- `codex -a never -s danger-full-access -c model_reasoning_effort=xhigh exec --skip-git-repo-check -m <worker-model> -C <repo> -o <worker-final-file> -`.
440
+ `codex -a never -s danger-full-access -c model_reasoning_effort=high exec --skip-git-repo-check -m <worker-model> -C <repo> -o <worker-final-file> -`.
428
441
  The approval, sandbox, and reasoning-effort config flags must come before the
429
442
  `exec` subcommand for Codex CLI builds that expose `-a`/`-s`/`-c` only at top
430
443
  level. `--skip-git-repo-check` belongs after `exec` because current customer and VPS Codex CLI builds expose it
@@ -434,26 +447,26 @@ forms fail on current customer CLI installs. Pipe the lane packet prompt on
434
447
  stdin, require the worker to write `workerDispatch.receiptArtifactHint`, and
435
448
  pass exactly one lane packet per worker.
436
449
  When launching durable Codex CLI workers from an automation parent, pass an
437
- explicit supported worker model and explicit `xhigh` reasoning effort instead
450
+ explicit supported worker model and explicit `high` reasoning effort instead
438
451
  of relying on the Codex CLI defaults. Use the parent runtime model when known,
439
452
  for example:
440
- `codex -a never -s danger-full-access -c model_reasoning_effort=xhigh exec --skip-git-repo-check -m <worker-model> -C <repo> -o <worker-final-file> -`.
453
+ `codex -a never -s danger-full-access -c model_reasoning_effort=high exec --skip-git-repo-check -m <worker-model> -C <repo> -o <worker-final-file> -`.
441
454
  In Codex CLI, the parent runtime model is visible in the run header as
442
455
  `model: <model-name>`. Copy that exact model string into child worker launches
443
- first. If the parent header says `model: gpt-5.5`, launch workers with
444
- `-m gpt-5.5` plus `-c model_reasoning_effort=xhigh`; do not invent or probe
456
+ first. If the parent header says `model: gpt-5.6-sol`, launch workers with
457
+ `-m gpt-5.6-sol` plus `-c model_reasoning_effort=high`; do not invent or probe
445
458
  nearby aliases such as `gpt-5.3-codex`, `gpt-5.2`, `gpt-5-codex`,
446
459
  `codex-latest`, or `codex-mini-latest` before trying the exact parent model. A
447
- one-line probe must include the same `-c model_reasoning_effort=xhigh` override
460
+ one-line probe must include the same `-c model_reasoning_effort=high` override
448
461
  and count as supported only when it exits 0 and returns the requested output; a
449
462
  session header followed by a `not supported` error is rejected, not accepted.
450
463
  Do not rely on the Codex CLI default model or default reasoning effort; some
451
- customer and VPS installs default to unavailable model aliases or to `high`
452
- reasoning. `-m gpt-5.5` alone is not enough. If a child worker reports GPT 5.5
453
- with `high` reasoning, treat that as a launcher bug, relaunch with the explicit
454
- `xhigh` config before mutation, and do not ask the user to continue through the
464
+ customer and VPS installs default to unavailable model aliases or to reasoning
465
+ below `high`. `-m gpt-5.6-sol` alone is not enough. If a child worker reports GPT 5.6-Sol
466
+ with reasoning below `high`, treat that as a launcher bug, relaunch with the explicit
467
+ `high` config before mutation, and do not ask the user to continue through the
455
468
  model-quality warning. If the parent cannot identify a supported worker model
456
- and launch it with `xhigh` reasoning, stop with
469
+ and launch it with `high` reasoning, stop with
457
470
  `blocked: worker_model_unavailable` before mutation.
458
471
  When wrapping multiple local Codex CLI workers in a shell launcher, run the
459
472
  wrapper with `/bin/bash -lc` or another explicitly chosen portable shell. Do not
@@ -466,7 +479,7 @@ Do not embed `<<'WORKER_PROMPT'` heredocs inside a single quoted or double
466
479
  quoted `/bin/bash -lc '...'` command string; nested quoting is brittle and can
467
480
  truncate the first worker before mutation. For multi-worker launchers, write one
468
481
  plain prompt file per lane under the current run directory, then start each
469
- worker with `codex -a never -s danger-full-access -c model_reasoning_effort=xhigh exec --skip-git-repo-check -m "$WORKER_MODEL" -C "$REPO" -o
482
+ worker with `codex -a never -s danger-full-access -c model_reasoning_effort=high exec --skip-git-repo-check -m "$WORKER_MODEL" -C "$REPO" -o
470
483
  "$worker_final_file" - < "$worker_prompt_file"`. Keep launcher shell variables
471
484
  double-quoted and keep the prompt heredoc only in a standalone script/prompt-file
472
485
  write step, not inside an already quoted shell argument. If the first launcher
@@ -572,7 +585,7 @@ launchers, write the same prompt body to `<worker-prompt-file>` and launch the
572
585
  worker with stdin redirected from that file:
573
586
 
574
587
  ```
575
- codex -a never -s danger-full-access -c model_reasoning_effort=xhigh exec --skip-git-repo-check -m <worker-model> -C <repo> -o <worker-final-file> - <<'WORKER_PROMPT'
588
+ codex -a never -s danger-full-access -c model_reasoning_effort=high exec --skip-git-repo-check -m <worker-model> -C <repo> -o <worker-final-file> - <<'WORKER_PROMPT'
576
589
  Use $sellable:create-campaign as the governing campaign workflow for this one lane worker.
577
590
  Use the evergreen plan/packet below only for lane scope, source metadata,
578
591
  postconditions, side-effect caps, and durable receipt proof.
@@ -619,10 +632,10 @@ Do not launch, start, schedule, send, or use paid InMail.
619
632
  For filter proof, durable receipt status must be `filterDecisionReceipt.status:"applied"` only; never `completed`, `confirmed`, `done`, or aliases.
620
633
  For generated messages, `update_cell` is allowed only for the semantic Approved checkbox. Never use `update_cell` for generated message text/body/sample copy. Bad copy requires `revise_message_template_and_rerun` or brief/template revision plus Generate Message rerun. Any generated-message cell override is `blocked: generated_message_cell_override`.
621
634
  If `bootstrap_create_campaign.modelQuality.status === "warn"` because the child
622
- worker reports GPT 5.5 with `high` reasoning, that is a parent launcher
635
+ worker reports GPT 5.6-Sol with reasoning below `high`, that is a parent launcher
623
636
  configuration bug, not an operator approval path inside the worker. Stop before
624
637
  mutation, tell the parent to relaunch this lane with
625
- `-c model_reasoning_effort=xhigh`, and do not mark the worker goal complete.
638
+ `-c model_reasoning_effort=high`, and do not mark the worker goal complete.
626
639
  Complete only this lane. Do not end with narration only. Before your final
627
640
  response, run a local file-existence and JSON self-check for
628
641
  <receiptArtifactPath>. If the lane succeeded, write the canonical success
@@ -643,7 +656,7 @@ receipt, or an accepted Post Engagers no-source blocked/no-op receipt.
643
656
  WORKER_PROMPT
644
657
 
645
658
  # Multi-worker launcher equivalent:
646
- codex -a never -s danger-full-access -c model_reasoning_effort=xhigh exec --skip-git-repo-check -m <worker-model> -C <repo> -o <worker-final-file> - < <worker-prompt-file>
659
+ codex -a never -s danger-full-access -c model_reasoning_effort=high exec --skip-git-repo-check -m <worker-model> -C <repo> -o <worker-final-file> - < <worker-prompt-file>
647
660
  ```
648
661
 
649
662
  If any placeholder cannot be filled from the current plan, matching
@@ -720,6 +733,9 @@ not a parent-thread summary. The receipt must include:
720
733
  creation, preserve that original packet only in a separate `parentLanePacket`
721
734
  object; do not leave `setupPlanCall.campaignId:null` or
722
735
  `setupPlanCall.tableId:null`.
736
+ If the parent plan used connection-only, the receipt must also include
737
+ `setupPlanCall.campaignSequenceOptions:{ "mode":"connection_only" }` or
738
+ `setupPlanCall.sequencePolicy.mode:"connection_only"`.
723
739
  Short form: For create lanes, `setupPlanCall.campaignId` and `setupPlanCall.tableId` must be the actual created campaign/table ids.
724
740
  Short form: Do not leave `setupPlanCall.campaignId:null` or `setupPlanCall.tableId:null`.
725
741
  - `createCampaignWorkflowReceipt`: proof the worker loaded the actual installed
@@ -810,22 +826,40 @@ not a parent-thread summary. The receipt must include:
810
826
  them into canonical `promptLoadedToHasMoreFalse`, `requiredAssetsLoaded`,
811
827
  `validationLoaded`, `reviewBatchRowHash`, and
812
828
  `messageDraftRecommendation`.
829
+ Connection-only lanes are the exception: do not include
830
+ `messageDraftingReceipt` or run generated-message prep unless the operator
831
+ separately asked for message copy. The parent verifier expects no message
832
+ proof for connection-only lanes.
813
833
  - `reviewBatchReceipt`: review-batch row ids/hash, generated row count,
814
834
  quality-valid route-proof row id when approved, and proof that no broad
815
835
  approve-all occurred.
836
+ Connection-only lanes are the exception: do not approve a route-proof row and
837
+ do not include a required review-batch receipt.
816
838
  - `sequenceReceipt`: exact current workflowTableId, recommended non-paid
817
839
  sequence attach/precheck result, and readback showing `hasSequence:true` when
818
840
  completion is claimed. If the tool output uses
819
841
  `attachRecommendedSequenceResult.actionTypes` or
820
842
  `nonPaidRecommendedSequence`, copy them to canonical `actionTypes` and
821
843
  `nonPaid`.
844
+ For connection-only lanes, do not call `attach_recommended_sequence`. Attach
845
+ with `attach_sequence({ tableId, templateRef:"connection_only", currentStep:"send" })`, then
846
+ include exact current-template proof:
847
+ `sequenceReceipt.hasSequence:true`,
848
+ `sequenceReceipt.actionTypes:["send_invite"]`, and
849
+ `sequenceReceipt.nonPaid:true`. Receipts that only say
850
+ `tool:"attach_recommended_sequence"` or include `send_dm`,
851
+ `send_inmail_open`, `send_inmail_closed`, or `view_profile` are failing
852
+ connection-only receipts.
822
853
  - final paused-send proof: if the current campaign table is `DRAFT` after the
823
854
  sequence is attached, call the product `pause_campaign({ campaignId })`
824
855
  endpoint/tool to put the unlaunched campaign into `PAUSED` review state, then
825
856
  reread the campaign/table. Do not raw-write `campaignStatus`, do not start or
826
857
  launch, and do not schedule/send. Completion requires reread proof of
827
- `currentStep:"send"` and `campaignStatus:"PAUSED"`. If the raw output nests
828
- this in `finalCampaignRead` or `finalTableRead`, copy it to canonical
858
+ `currentStep:"send"` and `campaignStatus:"PAUSED"` for standard and
859
+ connection-only customer-visible lanes. `send` is the product's paused
860
+ review step; do not use unsupported aliases such as `review` as final proof.
861
+ If the raw output nests this in `finalCampaignRead` or `finalTableRead`, copy
862
+ it to canonical
829
863
  `finalPausedSendProof.currentStep` and
830
864
  `finalPausedSendProof.campaignStatus`.
831
865
  - `verifyCall`: the exact `setup_evergreen_campaigns({ mode:"verify",
@@ -1088,9 +1122,14 @@ plain row generation. The lane worker must inline the same
1088
1122
  approvedGeneratedMessageCount exactly 1. Do not report completion with 2+
1089
1123
  approved rows.
1090
1124
  Approval shorthand: Do not report completion with 2+ approved rows.
1091
- 8. Continue to `attach_recommended_sequence({ campaignId, currentStep:"send" })`
1092
- and, if the campaign is still `DRAFT`, `pause_campaign({ campaignId })`.
1093
- Reread the campaign/table before claiming completion.
1125
+ 8. Standard mode: continue to
1126
+ `attach_recommended_sequence({ campaignId, currentStep:"send" })` and, if
1127
+ the campaign is still `DRAFT`, `pause_campaign({ campaignId })`. Reread the
1128
+ campaign/table before claiming completion.
1129
+ Connection-only mode: skip Message Drafting and route-proof approval, call
1130
+ `attach_sequence({ tableId, templateRef:"connection_only", currentStep:"send" })`, prove
1131
+ `sequenceReceipt.actionTypes:["send_invite"]`, and pause/reread without
1132
+ launching or sending.
1094
1133
 
1095
1134
  That packaged worker path must return `messageDraftingReceipt.statusSource:
1096
1135
  "packaged-generate-messages-worker"`. It is accepted only when the receipt
@@ -1515,11 +1554,14 @@ Message, and verify current-revision sample messages before final completion.
1515
1554
  - DM lanes: add a `Delivery format:` line — either `multiline (each paragraph sends as its own DM message)` or `single message`. When multiline, the template's blank-line paragraphs ARE the message boundaries — write each one as a standalone typed message.
1516
1555
  - **InMail lanes can never be multiline**: an InMail is one message and the recipient must reply before anything else can be sent. InMail-bound templates must read as one cohesive message — declare `Delivery format: single message (InMail — no follow-up until reply)` and never structure the copy to depend on multi-message pacing.
1517
1556
 
1518
- - The sequence is auto-selected by sender tier; do not hand-author sequence
1557
+ - In standard mode, the sequence is auto-selected by sender tier; do not hand-author sequence
1519
1558
  templates here. Sales Nav/Recruiter senders may receive the unified Sales
1520
1559
  Nav cascade through `attach_recommended_sequence`; attaching it does not
1521
1560
  spend paid InMail credits by itself. Do not substitute the manual Paid
1522
1561
  InMail Campaign template.
1562
+ For explicit connection-only evergreen lanes only, use the backend-owned
1563
+ `attach_sequence({ tableId, templateRef:"connection_only", currentStep:"send" })` path and do
1564
+ not call `attach_recommended_sequence`.
1523
1565
  3. **Customer-Visible Completion Contract**: a named evergreen lane that appears
1524
1566
  as a campaign card or campaign-backed table is not done when the shell exists.
1525
1567
  It is done only when the customer can open the campaign and land on final
@@ -1543,7 +1585,7 @@ Message, and verify current-revision sample messages before final completion.
1543
1585
  evergreen setup. Do not leave customer-visible campaigns at
1544
1586
  `filter-choice`, `filter-rules`, or `apply-icp-rubric` and report success.
1545
1587
  Do not proceed to Message Drafting until saved filters are applied.
1546
- - Message Drafting has run from the current campaign/table basis, using the
1588
+ - In standard mode, Message Drafting has run from the current campaign/table basis, using the
1547
1589
  create-campaign message prompt/assets and validation gate. Updating
1548
1590
  `currentStep:"messages"` is not proof. Parent-thread handwritten copy is
1549
1591
  not a substitute. The proof receipt must show
@@ -1584,6 +1626,9 @@ Message, and verify current-revision sample messages before final completion.
1584
1626
  Fallback samples must still reject source/conversation hedges such as
1585
1627
  `"hope this is relevant"`, `"might be interested"`, or `"saw you in a few
1586
1628
  conversations"`.
1629
+ For explicit connection-only lanes, this whole Message Drafting and sample
1630
+ proof block is not required and must not be faked with parent-written
1631
+ messages.
1587
1632
  - The first review batch exists and at least 3 review rows have generated
1588
1633
  messages from the approved brief. If fewer than 3 usable rows exist, report
1589
1634
  the actual count and why.
@@ -1596,7 +1641,7 @@ Message, and verify current-revision sample messages before final completion.
1596
1641
  completion, approve exactly one quality-valid generated row. If one or more
1597
1642
  rows are already approved, do not add more approvals during evergreen
1598
1643
  completion. Never broad approve all rows.
1599
- - The recommended tier-aware sequence is attached to the current campaign
1644
+ - In standard mode, the recommended tier-aware sequence is attached to the current campaign
1600
1645
  table, and the watched campaign is on Send. Use
1601
1646
  `attach_recommended_sequence({ campaignId, currentStep:"send" })` when a
1602
1647
  safe attach is needed. After `confirm_lead_list` or any source-list copy,
@@ -1611,6 +1656,10 @@ Message, and verify current-revision sample messages before final completion.
1611
1656
  manual Paid InMail Campaign, and never attach/replace sequence outside the
1612
1657
  current table unless the current lane packet explicitly allowed sequence
1613
1658
  repair.
1659
+ For explicit connection-only lanes, the current campaign table must have
1660
+ exactly the canonical invite-only sequence:
1661
+ `sequenceReceipt.actionTypes:["send_invite"]`. Any DM, InMail, or View
1662
+ Profile action means the lane is not complete.
1614
1663
  - If the current campaign table is still `DRAFT` after sequence/readiness
1615
1664
  proof, call `pause_campaign({ campaignId })` and reread. `pause_campaign`
1616
1665
  is the product-native review-state transition; it is not a launch and does
@@ -62,6 +62,11 @@ for lease expiry; never guess a fence.
62
62
  The default `intent:"auto"` inspects `managed_waterfall`,
63
63
  `dashboard_evergreen`, and `active_campaign` lane sources. Report the lane
64
64
  source and lane chain as proof for every selected sender.
65
+ Connection-only evergreen lanes are invite-only refill targets. When the packet
66
+ or campaign proof shows exactly `["send_invite"]`, report and execute only
67
+ `selectedLane:"send_invite"` work; do not infer DM, InMail, Sales Nav cascade,
68
+ paid-credit refresh, message generation, follow-up, sequence mutation,
69
+ launch/start beyond packet authority, scheduler writes, or direct sends.
65
70
 
66
71
  If membership blocks a workspace read, report the structured
67
72
  `workspace_access` blocker instead of retrying auth or switching workspaces.
@@ -196,6 +196,11 @@ rather than asking which campaign class to fill. If a stale target plan selects
196
196
  planner before mutation.
197
197
  Short form: trust the target plan's inferred lane when it is a connection invite,
198
198
  paid-InMail refill lane, or unified Sales Nav cascade.
199
+ Connection-only evergreen campaigns with exact `["send_invite"]` sequence proof
200
+ are plain invite capacity: treat them as `selectedLane:"send_invite"` only. Do
201
+ not infer DM, InMail, Sales Nav cascade, paid-credit refresh, message
202
+ generation, follow-up, sequence mutation, launch/start, scheduling override, or
203
+ direct-send work from a connection-only lane.
199
204
 
200
205
  Structured planner packet:
201
206