@sellable/install 0.1.363 → 0.1.365-phase111.20260720162325
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/bin/sellable-install.mjs +2 -7
- package/container/Dockerfile +8 -12
- package/container/README.md +2 -2
- package/container/entrypoint.sh +0 -4
- package/lib/sellable-agent/external-runtime-builder.mjs +1 -1
- 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 +5 -5
- package/lib/sellable-agent/service-installer.mjs +1 -1
- package/package.json +1 -1
- package/skill-templates/refill-sends-evergreen.md +5 -0
- package/skill-templates/refill-sends-v2.md +36 -1
- package/skill-templates/refill-sends.md +49 -1
package/bin/sellable-install.mjs
CHANGED
|
@@ -3712,8 +3712,7 @@ function upsertCodexMcpServerConfig(content, opts) {
|
|
|
3712
3712
|
content,
|
|
3713
3713
|
"mcp_servers.sellable",
|
|
3714
3714
|
`[mcp_servers.sellable]
|
|
3715
|
-
url = ${quoteToml(withHostedWatchModeDriver(opts.hostedUrl, "codex"))}
|
|
3716
|
-
default_tools_approval_mode = "approve"`
|
|
3715
|
+
url = ${quoteToml(withHostedWatchModeDriver(opts.hostedUrl, "codex"))}`
|
|
3717
3716
|
);
|
|
3718
3717
|
content = removeTomlSection(content, "mcp_servers.sellable.env");
|
|
3719
3718
|
return content;
|
|
@@ -3725,8 +3724,7 @@ default_tools_approval_mode = "approve"`
|
|
|
3725
3724
|
"mcp_servers.sellable",
|
|
3726
3725
|
`[mcp_servers.sellable]
|
|
3727
3726
|
command = ${quoteToml(command)}
|
|
3728
|
-
args = ${tomlArray(args)}
|
|
3729
|
-
default_tools_approval_mode = "approve"`
|
|
3727
|
+
args = ${tomlArray(args)}`
|
|
3730
3728
|
);
|
|
3731
3729
|
content = upsertTomlTable(
|
|
3732
3730
|
content,
|
|
@@ -3740,9 +3738,6 @@ ${tomlEnvTable("codex", opts)}`
|
|
|
3740
3738
|
function codexMcpServerMatches(content, opts) {
|
|
3741
3739
|
const server = readTomlTable(content, "mcp_servers.sellable");
|
|
3742
3740
|
if (!server) return false;
|
|
3743
|
-
if (!server.includes('default_tools_approval_mode = "approve"')) {
|
|
3744
|
-
return false;
|
|
3745
|
-
}
|
|
3746
3741
|
if (opts.server === "hosted") {
|
|
3747
3742
|
return (
|
|
3748
3743
|
server.includes(
|
package/container/Dockerfile
CHANGED
|
@@ -3,13 +3,13 @@ FROM ${HERMES_IMAGE}
|
|
|
3
3
|
|
|
4
4
|
USER root
|
|
5
5
|
|
|
6
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
7
|
-
ARG MCP_PACKAGE=@sellable/mcp@0.1.
|
|
6
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.365-phase111.20260720162325
|
|
7
|
+
ARG MCP_PACKAGE=@sellable/mcp@0.1.626-phase111.20260720143315
|
|
8
8
|
ARG INSTALLER_INTEGRITY
|
|
9
|
-
ARG MCP_INTEGRITY=sha512-
|
|
9
|
+
ARG MCP_INTEGRITY=sha512-pP5LMKh67n9YUXJV8WPSY++130BTXSmnYvHvtXam502ihtWM+ziUGfWqsydFB1nSSTb9GBbFNJQrfEeh+ANyKA==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.
|
|
12
|
-
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.
|
|
11
|
+
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.365-phase111.20260720162325" \
|
|
12
|
+
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.626-phase111.20260720143315" \
|
|
13
13
|
&& test -n "${INSTALLER_INTEGRITY}" \
|
|
14
14
|
&& test "$(npm view "${INSTALLER_PACKAGE}" dist.integrity)" = "${INSTALLER_INTEGRITY}" \
|
|
15
15
|
&& test "$(npm view "${MCP_PACKAGE}" dist.integrity)" = "${MCP_INTEGRITY}" \
|
|
@@ -23,17 +23,13 @@ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.363" \
|
|
|
23
23
|
&& mkdir -p /opt/sellable-agent/install-root /opt/sellable-agent/mcp-root \
|
|
24
24
|
&& npm install --prefix /opt/sellable-agent/install-root --omit=dev --ignore-scripts --no-audit --no-fund "${INSTALLER_PACKAGE}" \
|
|
25
25
|
&& npm install --prefix /opt/sellable-agent/mcp-root --omit=dev --ignore-scripts --no-audit --no-fund "${MCP_PACKAGE}" \
|
|
26
|
-
&& node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.
|
|
26
|
+
&& node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.365-phase111.20260720162325' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.626-phase111.20260720143315') throw new Error('package identity rejected');" \
|
|
27
27
|
&& node --input-type=module -e "import { buildExternalRuntimeClosure } from '/opt/sellable-agent/install-root/node_modules/@sellable/install/lib/sellable-agent/external-runtime-builder.mjs'; const built = buildExternalRuntimeClosure({ mcpPackageRoot: '/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp', mcpNodeModulesRoot: '/opt/sellable-agent/mcp-root/node_modules', hermesSourceRoot: '/opt/hermes', ownerUid: 0, ownerGid: 0 }); if (!built.closureDigest) throw new Error('runtime closure rejected');" \
|
|
28
|
-
&& node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.
|
|
28
|
+
&& node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.365-phase111.20260720162325', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.626-phase111.20260720143315', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
|
|
29
29
|
&& npm cache clean --force
|
|
30
30
|
|
|
31
31
|
COPY entrypoint.sh /usr/local/bin/sellable-agent-container-entrypoint
|
|
32
|
-
RUN
|
|
33
|
-
&& test ! -e /usr/bin/node \
|
|
34
|
-
&& ln -s /usr/local/bin/node /usr/bin/node \
|
|
35
|
-
&& test "$(readlink -f /usr/bin/node)" = "/usr/local/bin/node" \
|
|
36
|
-
&& chmod 0555 /usr/local/bin/sellable-agent-container-entrypoint
|
|
32
|
+
RUN chmod 0555 /usr/local/bin/sellable-agent-container-entrypoint
|
|
37
33
|
|
|
38
34
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
39
35
|
CMD /command/s6-svstat /etc/services.d/sellable-agent-host-worker | grep -q '^up' || exit 1
|
package/container/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
This image is a dedicated Agent worker/runtime boundary beside an existing Hermes dashboard. It never replaces or reconfigures the dashboard container.
|
|
4
4
|
|
|
5
|
-
Build from this directory after `@sellable/install@0.1.
|
|
5
|
+
Build from this directory after `@sellable/install@0.1.365-phase111.20260720162325` is published:
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
docker build --platform linux/amd64 \
|
|
9
9
|
--build-arg INSTALLER_INTEGRITY='sha512-…' \
|
|
10
|
-
--tag sellable-agent-host:0.1.
|
|
10
|
+
--tag sellable-agent-host:0.1.365-phase111.20260720162325 .
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Create a private `bootstrap/` directory owned by root with mode `0700`. It must contain `bootstrap.json`, `provider-auth`, and `slack-app`, each owned by root with mode `0600`. `bootstrap.json` contains only non-secret identities, hashes, package integrities, and the control-plane URL. The other two files contain the provider JSON and global Slack app token whose hashes are pinned by `bootstrap.json`.
|
package/container/entrypoint.sh
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
set -eu
|
|
3
3
|
|
|
4
|
-
PATH="/command:${PATH}"
|
|
5
|
-
export PATH
|
|
6
|
-
|
|
7
4
|
BOOTSTRAP_CONFIG=/var/lib/sellable-agent-bootstrap/bootstrap.json
|
|
8
5
|
BOOTSTRAP_BIN=/opt/sellable-agent/install-root/node_modules/.bin/sellable-agent-host-bootstrap
|
|
9
6
|
|
|
10
7
|
test "$(id -u)" = "0"
|
|
11
8
|
test "$(id -g)" = "0"
|
|
12
|
-
install -d -m 0755 -o root -g root /run/s6/container_environment
|
|
13
9
|
test -x "${BOOTSTRAP_BIN}"
|
|
14
10
|
test -f "${BOOTSTRAP_CONFIG}"
|
|
15
11
|
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
|
|
29
29
|
const MANIFEST_VERSION = "sellable-agent-external-runtime-closure/v1";
|
|
30
30
|
const MCP_PACKAGE = "@sellable/mcp";
|
|
31
|
-
const MCP_VERSION = "0.1.
|
|
31
|
+
const MCP_VERSION = "0.1.626-phase111.20260720143315";
|
|
32
32
|
const SAFE_COMPONENT = /^[A-Za-z0-9@._+-]+$/;
|
|
33
33
|
const HERMES_EXCLUDES = new Set([".git", ".playwright", "node_modules"]);
|
|
34
34
|
|
|
@@ -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.365-phase111.20260720162325";
|
|
42
|
+
const MCP_PACKAGE = "@sellable/mcp@0.1.626-phase111.20260720143315";
|
|
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}$/;
|
|
@@ -957,8 +957,8 @@ export function compileClaimToProfileDesired(activeClaim, config) {
|
|
|
957
957
|
]) ||
|
|
958
958
|
!exactObject(runtimeService, ["principalId", "policyHash", "credentialGeneration", "credentialFingerprint"]) ||
|
|
959
959
|
pinned.hermesCli !== "hermes" || pinned.hermesVersion !== "0.18.0" ||
|
|
960
|
-
pinned.installerPackage !== "@sellable/install@0.1.
|
|
961
|
-
pinned.mcpPackage !== "@sellable/mcp@0.1.
|
|
960
|
+
pinned.installerPackage !== "@sellable/install@0.1.365-phase111.20260720162325" ||
|
|
961
|
+
pinned.mcpPackage !== "@sellable/mcp@0.1.626-phase111.20260720143315" ||
|
|
962
962
|
!Array.isArray(policy.toolInclude) || policy.toolInclude.length === 0 ||
|
|
963
963
|
!Number.isSafeInteger(slack.generation) || slack.generation < 1 ||
|
|
964
964
|
!/^A[A-Z0-9]{8,20}$/.test(slack.appId ?? "") ||
|
|
@@ -131,8 +131,8 @@ function validateDesired(desired) {
|
|
|
131
131
|
!Number.isSafeInteger(desired.slackGeneration) || desired.slackGeneration < 1 ||
|
|
132
132
|
!Number.isSafeInteger(desired.serviceCredentialGeneration) || desired.serviceCredentialGeneration < 1 ||
|
|
133
133
|
desired.hermesCli !== "hermes" || desired.hermesVersion !== "0.18.0" ||
|
|
134
|
-
desired.installerPackage !== "@sellable/install@0.1.
|
|
135
|
-
desired.mcpPackage !== "@sellable/mcp@0.1.
|
|
134
|
+
desired.installerPackage !== "@sellable/install@0.1.365-phase111.20260720162325" ||
|
|
135
|
+
desired.mcpPackage !== "@sellable/mcp@0.1.626-phase111.20260720143315" ||
|
|
136
136
|
!Array.isArray(desired.toolInclude) || desired.toolInclude.length === 0 ||
|
|
137
137
|
desired.toolInclude.some((tool) => !/^[a-z][a-z0-9_]{0,127}$/.test(tool)) ||
|
|
138
138
|
new Set(desired.toolInclude).size !== desired.toolInclude.length ||
|
|
@@ -19,8 +19,8 @@ import { fileURLToPath } from "node:url";
|
|
|
19
19
|
import { deriveContainedProfileId } from "./profile-materializer.mjs";
|
|
20
20
|
|
|
21
21
|
export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
|
|
22
|
-
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.
|
|
23
|
-
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.
|
|
22
|
+
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.365-phase111.20260720162325";
|
|
23
|
+
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.626-phase111.20260720143315";
|
|
24
24
|
|
|
25
25
|
export function deriveAgentProfileId(workspaceId, agentId, hostId) {
|
|
26
26
|
return deriveContainedProfileId(workspaceId, agentId, hostId);
|
|
@@ -576,7 +576,7 @@ function inspectProvisionedProfile({
|
|
|
576
576
|
!matchesReadback(manifest, request) ||
|
|
577
577
|
manifest.serviceCredentialReference !== serviceCredentialReference ||
|
|
578
578
|
manifest.installerPackage !== PINNED_INSTALL_PACKAGE ||
|
|
579
|
-
manifest.installerVersion !== "0.1.
|
|
579
|
+
manifest.installerVersion !== "0.1.365-phase111.20260720162325" ||
|
|
580
580
|
manifest.mcpPackage !== PINNED_MCP_PACKAGE ||
|
|
581
581
|
manifest.credentialKeyVersion !== credentialKeyVersion ||
|
|
582
582
|
!/^[a-f0-9]{16}$/.test(manifest.credentialFingerprint) ||
|
|
@@ -781,7 +781,7 @@ function successReceipt({
|
|
|
781
781
|
subject: `agent-profile:${observed.profileId}`,
|
|
782
782
|
cli: {
|
|
783
783
|
installPackage: PINNED_INSTALL_PACKAGE,
|
|
784
|
-
installVersion: "0.1.
|
|
784
|
+
installVersion: "0.1.365-phase111.20260720162325",
|
|
785
785
|
mcpPackage: PINNED_MCP_PACKAGE,
|
|
786
786
|
command: "hermes profile bootstrap",
|
|
787
787
|
},
|
|
@@ -821,7 +821,7 @@ function failure(code, stages, extra = {}, request = null) {
|
|
|
821
821
|
subject: safeIdentity ? `agent-profile:${safeIdentity}` : "agent-profile:unbound",
|
|
822
822
|
cli: {
|
|
823
823
|
installPackage: PINNED_INSTALL_PACKAGE,
|
|
824
|
-
installVersion: "0.1.
|
|
824
|
+
installVersion: "0.1.365-phase111.20260720162325",
|
|
825
825
|
mcpPackage: PINNED_MCP_PACKAGE,
|
|
826
826
|
command: "hermes profile bootstrap",
|
|
827
827
|
},
|
|
@@ -305,7 +305,7 @@ function configurationFiles(input, checked) {
|
|
|
305
305
|
].join("\n");
|
|
306
306
|
return [
|
|
307
307
|
["/etc/sellable-agent/egress-proxy.json", `${JSON.stringify(checked.proxyConfig, null, 2)}\n`, 0o444],
|
|
308
|
-
["/etc/sellable-agent/worker.json", `${JSON.stringify(input.workerConfig, null, 2)}\n`,
|
|
308
|
+
["/etc/sellable-agent/worker.json", `${JSON.stringify(input.workerConfig, null, 2)}\n`, 0o600],
|
|
309
309
|
["/etc/sellable-agent/runtime-helper.json", `${JSON.stringify(input.helperConfig, null, 2)}\n`, 0o600],
|
|
310
310
|
["/etc/sudoers.d/sellable-agent-runtime-helper", sudoers, 0o440],
|
|
311
311
|
].map(([logicalPath, bytes, mode]) => ({ logicalPath, bytes, mode }));
|
package/package.json
CHANGED
|
@@ -17,3 +17,8 @@ Use `refill-sends-v2` instead:
|
|
|
17
17
|
```text
|
|
18
18
|
get_subskill_prompt({ subskillName: "refill-sends-v2" })
|
|
19
19
|
```
|
|
20
|
+
|
|
21
|
+
The alias must not strip exact `targetConfig`, server `runHandle`,
|
|
22
|
+
`reportingContext`, canonical `refill_reporting.v2`, or an explicit
|
|
23
|
+
`messageTemplateRevision`. It never infers revision authority from
|
|
24
|
+
`forceRerun`.
|
|
@@ -52,9 +52,35 @@ refill_sends_v2({ workspaceId, dryRun:true, intent:"auto", senderIds? })
|
|
|
52
52
|
To resume an in-progress run, pass the handle back exactly:
|
|
53
53
|
|
|
54
54
|
```text
|
|
55
|
-
refill_sends_v2({ workspaceId, runId,
|
|
55
|
+
refill_sends_v2({ workspaceId, runHandle:{runId,fence}, targetConfig, reportingContext, messageTemplateRevision? })
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
`targetConfig` and the server-issued `runHandle` are separate authorities. Keep
|
|
59
|
+
both byte-for-byte through every continuation and takeover; never fold the
|
|
60
|
+
rotating fence into target configuration.
|
|
61
|
+
|
|
62
|
+
An approved copy change is reachable only through an explicit
|
|
63
|
+
`messageTemplateRevision` object with `version:1`, `source:"user_approved"`,
|
|
64
|
+
the approved `templateMarkdown`, `messageTemplateRevision` identity,
|
|
65
|
+
`priorTemplateAuthorityDigest`, `nextTemplateAuthorityDigest`, 1–500 exact
|
|
66
|
+
`cohortRowIds`, and `requestId`, `effectId`, and `revisionOperationId`. Pass it
|
|
67
|
+
with the exact `targetConfig` and `reportingContext`; the command obtains or
|
|
68
|
+
resumes the server run handle and lets the planner name
|
|
69
|
+
`revise_message_template_and_rerun`. Missing or partial input means no revision.
|
|
70
|
+
`forceRerun:true` on a queue operation never authorizes or implies a template
|
|
71
|
+
change.
|
|
72
|
+
|
|
73
|
+
After a committed and dispatched revision receipt, continue only with the
|
|
74
|
+
planner-provided revision-derived `approve_messages` action. It must preserve
|
|
75
|
+
the exact revision cohort as `rowSelector:{type:"rowIds",rowIds:[...]}` plus
|
|
76
|
+
`approvalMode:"approve"`, the compiled readiness identity, and the canonical
|
|
77
|
+
`reportingContext`, even when the ordinary sender lane is ineligible. Do not
|
|
78
|
+
replace it with `queue_campaign_cells` on Approved cells; that path cannot mint
|
|
79
|
+
the authoritative approval/lane-scope receipt. Require `laneScope` in the
|
|
80
|
+
bounded preparation receipt before the lane is considered approved. A
|
|
81
|
+
disconnected or zero-capacity sender still blocks scheduling and does not
|
|
82
|
+
authorize reconnecting, campaign start, or direct send.
|
|
83
|
+
|
|
58
84
|
If a stale handle loses the lease, the tool reports the holder status and the
|
|
59
85
|
approximately 10 minute lockout window. Reinvoke with the current handle or wait
|
|
60
86
|
for lease expiry; never guess a fence.
|
|
@@ -85,6 +111,10 @@ The run may execute only packet-named bounded work:
|
|
|
85
111
|
when the campaign is in the packet's pinned lane chain;
|
|
86
112
|
- refresh paid InMail credit facts during bootstrap and once per scheduler-wait
|
|
87
113
|
entry when the packet requires it.
|
|
114
|
+
- apply `revise_message_template_and_rerun` only when the packet contains the
|
|
115
|
+
complete explicit user-approved revision envelope and exact fenced target.
|
|
116
|
+
- after that revision commits, apply only its exact-row, readiness-bound
|
|
117
|
+
`approve_messages` continuation and require the returned lane-scope receipt.
|
|
88
118
|
|
|
89
119
|
The loop records planned -> did -> outcome before and after every foreign
|
|
90
120
|
mutation. It refuses stale packets by fingerprint and validates packet
|
|
@@ -168,5 +198,10 @@ reason or resume handle, lane source, lane chain, chosen label with token
|
|
|
168
198
|
secondary, plan revision, journal path, blocked continuation packets, and
|
|
169
199
|
firstFailing checklist when present.
|
|
170
200
|
|
|
201
|
+
Preserve `refill_reporting.v2` exactly in progress, continuation, replay, and
|
|
202
|
+
terminal results. Do not recompute its semantic counts or drop live-preparation
|
|
203
|
+
epoch, snapshot sequence, reset reason, watermark, blockers, compatibility, or
|
|
204
|
+
terminal fields. Never surface raw prospect or message-copy fields.
|
|
205
|
+
|
|
171
206
|
Real-run journals live under `~/.sellable/refill/runs` by default and append an
|
|
172
207
|
index line. Dry runs include the dry marker and do not create run records.
|
|
@@ -93,7 +93,7 @@ Accepted invocation flags in the same user request:
|
|
|
93
93
|
When the host can call typed MCP tools, start with:
|
|
94
94
|
|
|
95
95
|
```text
|
|
96
|
-
refill_sends({ yolo?: boolean, executionMode?: "manual" | "scheduled" | "yolo", requireWorkspace?: boolean, workspaceId?: string, senders?: string[], senderIds?: string[], senderNames?: string[], actionTypes?: ("send_invite" | "send_inmail_closed")[], horizonSendDays?: number, untilDate?: "YYYY-MM-DD", targetDate?: "YYYY-MM-DD",
|
|
96
|
+
refill_sends({ yolo?: boolean, executionMode?: "manual" | "scheduled" | "yolo", requireWorkspace?: boolean, workspaceId?: string, senders?: string[], senderIds?: string[], senderNames?: string[], actionTypes?: ("send_invite" | "send_inmail_closed")[], horizonSendDays?: number, untilDate?: "YYYY-MM-DD", targetDate?: "YYYY-MM-DD", runHandle?: RefillRunHandleV1, targetConfig?: RefillTargetConfigV1, reportingContext?: RefillReportingContextV2, messageTemplateRevision?: MessageTemplateRevisionV1 })
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
That command helper normalizes arguments and returns the execution contract. In
|
|
@@ -104,6 +104,49 @@ host must immediately call `refill_sends` again with the original full scope
|
|
|
104
104
|
plus the exact returned `runId` and `fence`. Do this automatically inside the
|
|
105
105
|
same user command until terminal projected coverage or a concrete blocker; do
|
|
106
106
|
not ask the user to type `continue` and do not start a second run.
|
|
107
|
+
|
|
108
|
+
`targetConfig` and the server-issued `runHandle` are separate values and must
|
|
109
|
+
survive each continuation unchanged except for a server-issued takeover fence.
|
|
110
|
+
Only a literal `messageTemplateRevision` with `source:"user_approved"`, the
|
|
111
|
+
approved `templateMarkdown`, prior/new authority digests, 1–500 exact cohort
|
|
112
|
+
row IDs, and request/effect/revision-operation IDs can authorize
|
|
113
|
+
`revise_message_template_and_rerun`. No envelope means no revision. A
|
|
114
|
+
`forceRerun:true` queue call cannot infer or authorize template mutation.
|
|
115
|
+
When the user supplied a complete revision except for the current prior digest,
|
|
116
|
+
acquire the exact run first and reread with its unchanged `targetConfig` and
|
|
117
|
+
`runHandle`. Use only `reportingContext.source.templateAuthorityDigest` from
|
|
118
|
+
that fenced reread as `priorTemplateAuthorityDigest`, then immediately continue
|
|
119
|
+
the same run with the complete literal revision—even when the ordinary refill
|
|
120
|
+
queue is empty or the sender is otherwise ineligible. An empty ordinary queue
|
|
121
|
+
must not suppress an explicit revision. If the fenced reread returns no digest,
|
|
122
|
+
stop with `template_authority_missing` or the returned
|
|
123
|
+
`templateAuthorityBlocker`; never guess the digest.
|
|
124
|
+
After the revision receipt is committed and dispatched, the next mutation must
|
|
125
|
+
be the planner-provided revision-derived `approve_messages` action. Preserve
|
|
126
|
+
its exact `rowSelector:{type:"rowIds",rowIds:[...]}` cohort,
|
|
127
|
+
`approvalMode:"approve"`, compiled readiness authority, and
|
|
128
|
+
`reportingContext`, even if the ordinary sender lane is ineligible. Never use
|
|
129
|
+
`queue_campaign_cells` to flip Approved cells: that path cannot produce the
|
|
130
|
+
authoritative approval/lane-scope receipt. Require the bounded preparation
|
|
131
|
+
receipt's `laneScope` before considering the selected first-touch lane
|
|
132
|
+
approved. Zero sender capacity remains a scheduler blocker and never authorizes
|
|
133
|
+
reconnection, campaign start, or direct send.
|
|
134
|
+
|
|
135
|
+
Before that approval mutation, perform an exact authoritative copy audit with
|
|
136
|
+
`select_campaign_cells({ columnRole:"generateMessage",
|
|
137
|
+
rowSelector:{type:"rowIds",rowIds:[...]},
|
|
138
|
+
includeGeneratedMessageReview:true })` over the complete revision cohort in
|
|
139
|
+
batches of at most 20. Require every
|
|
140
|
+
`authoritativeGeneratedMessageReview.complete:true`, every row status to be
|
|
141
|
+
completed-like, the exact user-approved rendered subject/body, the expected
|
|
142
|
+
`templateAuthorityDigest`, and `matchesCurrentTemplateAuthority:true`. This
|
|
143
|
+
projection is the only MCP copy-review source of truth. Never use formula
|
|
144
|
+
Message/Subject columns, campaign message previews, table previews, or CSV
|
|
145
|
+
exports to validate revised Generate Message results. If any row is missing,
|
|
146
|
+
non-current, or text/digest mismatched, stop before approval and report
|
|
147
|
+
`regenerated_copy_integrity_failed`; never regenerate again implicitly or
|
|
148
|
+
approve the cohort on counts/revision hashes alone.
|
|
149
|
+
|
|
107
150
|
Safe primitives are paid-credit refresh,
|
|
108
151
|
existing-row message preparation, generated-message approval, receipt-proven
|
|
109
152
|
same-source row copy, one bounded Signal Discovery search derived only from
|
|
@@ -567,6 +610,11 @@ scheduler wait loop. Do not finish the refill goal, mark it complete, or mark it
|
|
|
567
610
|
blocked only because it is still `awaiting_scheduler_after_ready_buffer`; keep
|
|
568
611
|
waiting unless Christian stops the run or the host cannot continue.
|
|
569
612
|
|
|
613
|
+
Carry the canonical `refill_reporting.v2` DTO through progress, continuation,
|
|
614
|
+
replay, and terminal reporting without recomputing counts or losing live epoch,
|
|
615
|
+
snapshot sequence, reset reason, watermark, blockers, compatibility, or
|
|
616
|
+
terminal outcome. Redact raw copy and prospect fields.
|
|
617
|
+
|
|
570
618
|
Future scheduled coverage and already sent actions are distinct; only
|
|
571
619
|
scheduler-owned future scheduled actions count as scheduled coverage. For
|
|
572
620
|
multiple target dates, finish D1 execution and the full D1 reread before
|