@sellable/install 0.1.312 → 0.1.314

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.
@@ -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",
@@ -89,7 +97,7 @@ export const CREATE_CAMPAIGN_SMOKE_CALLS = [
89
97
  arguments: {
90
98
  subskillName: "create-evergreen-campaigns",
91
99
  offset: 0,
92
- limit: 4000,
100
+ limit: CONNECTION_ONLY_EVERGREEN_PROMPT_CHUNK_LIMIT,
93
101
  },
94
102
  },
95
103
  {
@@ -195,6 +203,43 @@ function parseJsonText(text) {
195
203
  }
196
204
  }
197
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
+
198
243
  function summarizeToolResult(name, result) {
199
244
  const text = redact(textFromToolResult(result));
200
245
  const parsed = parseJsonText(text);
@@ -239,15 +284,24 @@ function summarizeToolResult(name, result) {
239
284
 
240
285
  if (name === "get_subskill_prompt" && parsed) {
241
286
  const prompt = typeof parsed.prompt === "string" ? parsed.prompt : "";
287
+ const connectionOnlyEvergreenProofs =
288
+ parsed.name === "create-evergreen-campaigns"
289
+ ? getConnectionOnlyEvergreenProofs(prompt)
290
+ : null;
242
291
  return {
243
292
  ...summary,
244
293
  subskillName: parsed.name || null,
245
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,
246
300
  connectionOnlyEvergreenGuidance:
247
301
  parsed.name === "create-evergreen-campaigns"
248
- ? prompt.includes('campaignSequenceOptions:{ mode:"connection_only" }') &&
249
- prompt.includes('templateRef:"connection_only"') &&
250
- prompt.includes('sequenceReceipt.actionTypes:["send_invite"]')
302
+ ? summarizeConnectionOnlyEvergreenProofs(
303
+ connectionOnlyEvergreenProofs
304
+ ).ok
251
305
  : null,
252
306
  hasMore: parsed.hasMore === true,
253
307
  nextOffset:
@@ -282,6 +336,7 @@ async function verifyCreateCampaignSmoke({
282
336
  .map((call) => call.name)
283
337
  .filter((name) => !available.has(name));
284
338
  const calls = [];
339
+ const connectionOnlyEvergreenProofs = emptyConnectionOnlyEvergreenProofs();
285
340
 
286
341
  if (missingTools.length > 0) {
287
342
  return {
@@ -295,76 +350,158 @@ async function verifyCreateCampaignSmoke({
295
350
  }
296
351
 
297
352
  for (const call of smokeCalls) {
298
- const callStartedAt = new Date().toISOString();
299
- try {
300
- const result = await client.callTool(
301
- { name: call.name, arguments: call.arguments },
302
- undefined,
303
- {
304
- timeout: timeoutMs,
305
- 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
+ };
306
386
  }
307
- );
308
- const summary = summarizeToolResult(call.name, result);
309
- calls.push({
310
- name: call.name,
311
- arguments: call.arguments,
312
- ok: summary.isError !== true,
313
- summary,
314
- startedAt: callStartedAt,
315
- completedAt: new Date().toISOString(),
316
- });
317
- if (summary.isError === true) {
318
- return {
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,
319
468
  ok: false,
320
- missingTools: [],
321
- calls,
322
- error: `${call.name} returned an MCP tool error`,
323
- startedAt,
469
+ summary: null,
470
+ error: redact(error),
471
+ startedAt: callStartedAt,
324
472
  completedAt: new Date().toISOString(),
325
- };
326
- }
327
- if (
328
- call.name === "get_subskill_prompt" &&
329
- call.arguments?.subskillName === "create-evergreen-campaigns" &&
330
- summary.connectionOnlyEvergreenGuidance !== true
331
- ) {
473
+ });
332
474
  return {
333
475
  ok: false,
334
476
  missingTools: [],
335
477
  calls,
336
- error:
337
- "create-evergreen-campaigns prompt is missing connection-only guidance",
478
+ error: redact(error),
338
479
  startedAt,
339
480
  completedAt: new Date().toISOString(),
340
481
  };
341
482
  }
342
- } catch (err) {
343
- const error = err instanceof Error ? err.message : String(err);
344
- calls.push({
345
- name: call.name,
346
- arguments: call.arguments,
347
- ok: false,
348
- summary: null,
349
- error: redact(error),
350
- startedAt: callStartedAt,
351
- completedAt: new Date().toISOString(),
352
- });
353
- return {
354
- ok: false,
355
- missingTools: [],
356
- calls,
357
- error: redact(error),
358
- startedAt,
359
- completedAt: new Date().toISOString(),
360
- };
361
483
  }
362
484
  }
363
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
+
364
500
  return {
365
501
  ok: true,
366
502
  missingTools: [],
367
503
  calls,
504
+ connectionOnlyEvergreenGuidance,
368
505
  error: null,
369
506
  startedAt,
370
507
  completedAt: new Date().toISOString(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.312",
3
+ "version": "0.1.314",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {
@@ -854,11 +854,11 @@ not a parent-thread summary. The receipt must include:
854
854
  endpoint/tool to put the unlaunched campaign into `PAUSED` review state, then
855
855
  reread the campaign/table. Do not raw-write `campaignStatus`, do not start or
856
856
  launch, and do not schedule/send. Completion requires reread proof of
857
- `currentStep:"send"` and `campaignStatus:"PAUSED"` for standard
858
- customer-visible lanes. Connection-only lanes may finish at a paused
859
- unlaunched review step such as `currentStep:"review"` as long as the exact
860
- invite-only sequence proof and source-row proof pass. If the raw output nests
861
- this in `finalCampaignRead` or `finalTableRead`, copy it to canonical
857
+ `currentStep:"send"` and `campaignStatus:"PAUSED"` for standard and
858
+ connection-only customer-visible lanes. `send` is the product's paused
859
+ review step; do not use unsupported aliases such as `review` as final proof.
860
+ If the raw output nests this in `finalCampaignRead` or `finalTableRead`, copy
861
+ it to canonical
862
862
  `finalPausedSendProof.currentStep` and
863
863
  `finalPausedSendProof.campaignStatus`.
864
864
  - `verifyCall`: the exact `setup_evergreen_campaigns({ mode:"verify",