@sellable/install 0.1.787 → 0.1.790
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/container/Dockerfile +7 -7
- package/container/README.md +1 -1
- package/lib/sellable-agent/default-profile-bundles/shared/manifest.fragment.json +5 -5
- package/lib/sellable-agent/default-profile-bundles/shared/skills/sellable/sellable-campaign-daily-results/SKILL.md +78 -40
- package/lib/sellable-agent/default-profile-bundles/shared/skills/sellable/sellable-campaign-daily-review/SKILL.md +83 -17
- package/lib/sellable-agent/default-profile-bundles/shared/skills/sellable/sellable-campaign-weekly-review/SKILL.md +69 -48
- package/lib/sellable-agent/default-profile-bundles/shared/skills/sellable/sellable-create-campaign/SKILL.md +2 -0
- package/lib/sellable-agent/default-profile-bundles/shared/skills/sellable/sellable-refill-sends/SKILL.md +21 -0
- package/lib/sellable-agent/external-runtime-builder.mjs +1 -1
- package/lib/sellable-agent/fly-admin-image/Dockerfile +1 -1
- package/lib/sellable-agent/fly-customer-image/Dockerfile +4 -4
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +28 -2
- package/lib/sellable-agent/fly-customer-image/hermes-runs-client.mjs +75 -1
- package/lib/sellable-agent/hermes-memory-snapshot.mjs +9 -7
- package/lib/sellable-agent/host-bootstrap.mjs +2 -2
- package/lib/sellable-agent/host-worker.mjs +2 -2
- package/lib/sellable-agent/profile-materializer.mjs +2 -2
- package/lib/sellable-agent/provisioning-adapter.mjs +2 -2
- package/lib/sellable-agent/public-skills.json +9 -9
- package/package.json +1 -1
- package/skill-templates/campaign-daily-results.md +78 -40
- package/skill-templates/campaign-daily-review.md +83 -17
- package/skill-templates/campaign-weekly-review.md +69 -48
- package/skill-templates/create-ab-test.md +21 -0
- package/skill-templates/create-campaign.md +2 -0
- package/skill-templates/create-evergreen-campaigns.md +21 -0
- package/skill-templates/find-leads.md +21 -0
- package/skill-templates/first-campaign-onboarding.md +21 -0
- package/skill-templates/refill-sends.md +21 -0
- package/skill-templates/refresh-sender-engagement.md +21 -0
|
@@ -200,6 +200,39 @@ function canonicalHistory(value) {
|
|
|
200
200
|
return result;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
// The models a caller may name for one endpoint run. The server checks the
|
|
204
|
+
// same list before it hands the run over; the runtime checks it again here so
|
|
205
|
+
// a run can never reach Hermes with a model that was not reviewed.
|
|
206
|
+
export const ENDPOINT_MODEL_SELECTION_ALLOWLIST = Object.freeze({
|
|
207
|
+
openrouter: Object.freeze([
|
|
208
|
+
"anthropic/claude-opus-5.5",
|
|
209
|
+
"anthropic/claude-sonnet-5.5",
|
|
210
|
+
]),
|
|
211
|
+
});
|
|
212
|
+
const ENDPOINT_MODEL_REASONING_EFFORTS = Object.freeze([
|
|
213
|
+
"low",
|
|
214
|
+
"medium",
|
|
215
|
+
"high",
|
|
216
|
+
"xhigh",
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
export function validateEndpointModelSelection(value) {
|
|
220
|
+
if (
|
|
221
|
+
!exactObject(value) ||
|
|
222
|
+
Object.keys(value).sort().join(",") !== "model,provider,reasoningEffort" ||
|
|
223
|
+
!Object.hasOwn(ENDPOINT_MODEL_SELECTION_ALLOWLIST, value.provider) ||
|
|
224
|
+
!ENDPOINT_MODEL_SELECTION_ALLOWLIST[value.provider].includes(value.model) ||
|
|
225
|
+
!ENDPOINT_MODEL_REASONING_EFFORTS.includes(value.reasoningEffort)
|
|
226
|
+
) {
|
|
227
|
+
throw new Error("hermes_runs_model_selection_rejected");
|
|
228
|
+
}
|
|
229
|
+
return Object.freeze({
|
|
230
|
+
provider: value.provider,
|
|
231
|
+
model: value.model,
|
|
232
|
+
reasoningEffort: value.reasoningEffort,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
203
236
|
export function validateOpenerOutputContract(value) {
|
|
204
237
|
const schema = value?.schema;
|
|
205
238
|
if (
|
|
@@ -232,7 +265,7 @@ function compileCreateBody(input, openerStage = false) {
|
|
|
232
265
|
"input",
|
|
233
266
|
"instructions",
|
|
234
267
|
"correlation",
|
|
235
|
-
...(openerStage ? ["outputContract"] : []),
|
|
268
|
+
...(openerStage ? ["outputContract"] : ["modelSelection"]),
|
|
236
269
|
];
|
|
237
270
|
if (Object.keys(input).some((key) => !allowed.includes(key))) {
|
|
238
271
|
throw new Error("hermes_runs_submit_rejected");
|
|
@@ -254,11 +287,24 @@ function compileCreateBody(input, openerStage = false) {
|
|
|
254
287
|
if (openerStage && history.length !== 0) {
|
|
255
288
|
throw new Error("hermes_opener_stage_history_rejected");
|
|
256
289
|
}
|
|
290
|
+
const modelSelection =
|
|
291
|
+
input.modelSelection === undefined
|
|
292
|
+
? null
|
|
293
|
+
: validateEndpointModelSelection(input.modelSelection);
|
|
257
294
|
return Object.freeze({
|
|
258
295
|
input: compiledInput,
|
|
259
296
|
instructions,
|
|
260
297
|
conversation_history: history,
|
|
261
298
|
session_id: `${openerStage ? "sellable_opener_stage" : "sellable_endpoint"}_${sha256(correlation).slice(0, 32)}`,
|
|
299
|
+
// Hermes 0.21.5 honours a per-run provider and model on /v1/runs and
|
|
300
|
+
// resolves the provider's credential from the machine's own pool.
|
|
301
|
+
...(modelSelection === null
|
|
302
|
+
? {}
|
|
303
|
+
: {
|
|
304
|
+
provider: modelSelection.provider,
|
|
305
|
+
model: modelSelection.model,
|
|
306
|
+
model_options: { reasoning_effort: modelSelection.reasoningEffort },
|
|
307
|
+
}),
|
|
262
308
|
...(input.outputContract === undefined
|
|
263
309
|
? {}
|
|
264
310
|
: {
|
|
@@ -287,6 +333,30 @@ async function readBounded(response, maximum = MAX_RESPONSE_BYTES) {
|
|
|
287
333
|
}
|
|
288
334
|
}
|
|
289
335
|
|
|
336
|
+
const SERVED_RUNTIME_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
337
|
+
|
|
338
|
+
// The provider and model that actually served a run (a fallback can differ
|
|
339
|
+
// from the request) and its token counts, so cost and model are auditable.
|
|
340
|
+
function servedRuntime(value) {
|
|
341
|
+
return exactObject(value) &&
|
|
342
|
+
SERVED_RUNTIME_ID.test(value.provider ?? "") &&
|
|
343
|
+
SERVED_RUNTIME_ID.test(value.model ?? "")
|
|
344
|
+
? Object.freeze({ provider: value.provider, model: value.model })
|
|
345
|
+
: null;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function runUsage(value) {
|
|
349
|
+
if (!exactObject(value)) return null;
|
|
350
|
+
const count = (key) =>
|
|
351
|
+
Number.isSafeInteger(value[key]) && value[key] >= 0 ? value[key] : null;
|
|
352
|
+
return Object.freeze({
|
|
353
|
+
inputTokens: count("input_tokens"),
|
|
354
|
+
outputTokens: count("output_tokens"),
|
|
355
|
+
cacheReadTokens: count("cache_read_tokens"),
|
|
356
|
+
cacheWriteTokens: count("cache_write_tokens"),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
290
360
|
function ensureRunId(value) {
|
|
291
361
|
if (!RUN_ID.test(value ?? "")) throw new Error("hermes_runs_run_id_rejected");
|
|
292
362
|
return value;
|
|
@@ -340,6 +410,8 @@ function nativeStatus(value, expectedRunId) {
|
|
|
340
410
|
classification: "COMPLETED",
|
|
341
411
|
output,
|
|
342
412
|
outputDigest: sha256(stableJson(output)),
|
|
413
|
+
servedRuntime: servedRuntime(value.runtime),
|
|
414
|
+
usage: runUsage(value.usage),
|
|
343
415
|
});
|
|
344
416
|
}
|
|
345
417
|
if (status === "failed") {
|
|
@@ -439,6 +511,8 @@ export function sanitizeHermesRunEvent(value, options = {}) {
|
|
|
439
511
|
output: value.output,
|
|
440
512
|
outputDigest: sha256(stableJson(value.output)),
|
|
441
513
|
usageDigest: sha256(stableJson(value.usage)),
|
|
514
|
+
servedRuntime: servedRuntime(value.runtime),
|
|
515
|
+
usage: runUsage(value.usage),
|
|
442
516
|
});
|
|
443
517
|
}
|
|
444
518
|
if (value.event === "run.failed") {
|
|
@@ -79,15 +79,17 @@ export const HERMES_FIRST_CAMPAIGN_ONBOARDING_SKILL =
|
|
|
79
79
|
const HERMES_FIRST_CAMPAIGN_ONBOARDING_SKILL_GROUP = "sellable-defaults";
|
|
80
80
|
export const HERMES_CAMPAIGN_DAILY_REVIEW_SKILL =
|
|
81
81
|
"sellable-campaign-daily-review";
|
|
82
|
-
// Each job
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
82
|
+
// Each job ticks every hour of every day so that a customer in any time zone is
|
|
83
|
+
// reached. The product's due check decides: it wakes the agent only on the
|
|
84
|
+
// customer's local working days (weekly: Mondays), from the job's report time
|
|
85
|
+
// (review 08:00, results and weekly 09:00) for four hours, and never for a
|
|
86
|
+
// finished period, so the later ticks in that window are the retries. Every
|
|
87
|
+
// other tick is a cheap "not due" answer and the agent stays asleep.
|
|
86
88
|
export const HERMES_CUSTOMER_PROGRAM_CRONS = Object.freeze([
|
|
87
89
|
Object.freeze({
|
|
88
90
|
jobKey: "campaign_daily_review",
|
|
89
91
|
name: "sellable-default-campaign-daily-review",
|
|
90
|
-
schedule: "0
|
|
92
|
+
schedule: "0 * * * *",
|
|
91
93
|
script: "customer-program-due-campaign-daily-review.py",
|
|
92
94
|
skills: Object.freeze([
|
|
93
95
|
HERMES_CAMPAIGN_DAILY_REVIEW_SKILL,
|
|
@@ -106,7 +108,7 @@ export const HERMES_CUSTOMER_PROGRAM_CRONS = Object.freeze([
|
|
|
106
108
|
Object.freeze({
|
|
107
109
|
jobKey: "campaign_daily_results",
|
|
108
110
|
name: "sellable-default-campaign-daily-results",
|
|
109
|
-
schedule: "0
|
|
111
|
+
schedule: "0 * * * *",
|
|
110
112
|
script: "customer-program-due-campaign-daily-results.py",
|
|
111
113
|
skills: Object.freeze(["sellable-campaign-daily-results"]),
|
|
112
114
|
prompt: Object.freeze([
|
|
@@ -119,7 +121,7 @@ export const HERMES_CUSTOMER_PROGRAM_CRONS = Object.freeze([
|
|
|
119
121
|
Object.freeze({
|
|
120
122
|
jobKey: "campaign_weekly_review",
|
|
121
123
|
name: "sellable-default-campaign-weekly-review",
|
|
122
|
-
schedule: "0
|
|
124
|
+
schedule: "0 * * * *",
|
|
123
125
|
script: "customer-program-due-campaign-weekly-review.py",
|
|
124
126
|
skills: Object.freeze([
|
|
125
127
|
"sellable-campaign-weekly-review",
|
|
@@ -38,8 +38,8 @@ import { installAgentHostServices } from "./service-installer.mjs";
|
|
|
38
38
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
39
39
|
const VERSION = "sellable-agent-host-bootstrap/v1";
|
|
40
40
|
const RECEIPT_VERSION = "sellable-agent-host-registration/v1";
|
|
41
|
-
const INSTALLER_PACKAGE = "@sellable/install@0.1.
|
|
42
|
-
const MCP_PACKAGE = "@sellable/mcp@0.1.
|
|
41
|
+
const INSTALLER_PACKAGE = "@sellable/install@0.1.790";
|
|
42
|
+
const MCP_PACKAGE = "@sellable/mcp@0.1.955";
|
|
43
43
|
const HERMES_VERSION = "0.18.0";
|
|
44
44
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
45
45
|
const SHA256_16 = /^[a-f0-9]{16}$/;
|
|
@@ -1546,8 +1546,8 @@ export function compileClaimToProfileDesired(activeClaim, config) {
|
|
|
1546
1546
|
]) ||
|
|
1547
1547
|
pinned.hermesCli !== "hermes" ||
|
|
1548
1548
|
pinned.hermesVersion !== "0.18.0" ||
|
|
1549
|
-
pinned.installerPackage !== "@sellable/install@0.1.
|
|
1550
|
-
pinned.mcpPackage !== "@sellable/mcp@0.1.
|
|
1549
|
+
pinned.installerPackage !== "@sellable/install@0.1.790" ||
|
|
1550
|
+
pinned.mcpPackage !== "@sellable/mcp@0.1.955" ||
|
|
1551
1551
|
!Array.isArray(policy.toolInclude) ||
|
|
1552
1552
|
policy.toolInclude.length === 0 ||
|
|
1553
1553
|
!Number.isSafeInteger(slack.generation) ||
|
|
@@ -312,8 +312,8 @@ function validateDesired(desired) {
|
|
|
312
312
|
desired.serviceCredentialGeneration < 1 ||
|
|
313
313
|
desired.hermesCli !== "hermes" ||
|
|
314
314
|
desired.hermesVersion !== "0.18.0" ||
|
|
315
|
-
desired.installerPackage !== "@sellable/install@0.1.
|
|
316
|
-
desired.mcpPackage !== "@sellable/mcp@0.1.
|
|
315
|
+
desired.installerPackage !== "@sellable/install@0.1.790" ||
|
|
316
|
+
desired.mcpPackage !== "@sellable/mcp@0.1.955" ||
|
|
317
317
|
!Array.isArray(desired.toolInclude) ||
|
|
318
318
|
desired.toolInclude.length === 0 ||
|
|
319
319
|
desired.toolInclude.some((tool) => !/^[a-z][a-z0-9_]{0,127}$/.test(tool)) ||
|
|
@@ -25,8 +25,8 @@ import { fileURLToPath } from "node:url";
|
|
|
25
25
|
import { deriveContainedProfileId } from "./profile-materializer.mjs";
|
|
26
26
|
|
|
27
27
|
export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
|
|
28
|
-
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.
|
|
29
|
-
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.
|
|
28
|
+
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.790";
|
|
29
|
+
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.955";
|
|
30
30
|
const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice(
|
|
31
31
|
PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1
|
|
32
32
|
);
|