@sellable/install 0.1.320 → 0.1.321

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.
@@ -1,169 +1,67 @@
1
1
  # Hermes Slack Profile Scoping
2
2
 
3
- Use this when creating or updating customer Hermes profiles that need Slack
4
- access.
5
-
6
- ## Default Rule
7
-
8
- For Sellable-managed customer channels, use one dedicated Slack app/token pair
9
- per active Hermes customer profile. Run native `hermes -p <profile> gateway run`
10
- for that profile only. Do not run multiple active Hermes profile gateways
11
- against the same `SLACK_APP_TOKEN`.
12
-
13
- The customer's Slack app is bound to the exact Slack channel id and the
14
- customer's profile-local Sellable MCP config. Customer profiles must not store
15
- or read Sellable Admin MCP credentials, admin Slack app tokens, or sibling
16
- profile config.
17
-
18
- The shared dispatcher remains a fallback/research path only. Use it only when
19
- Sellable Admin explicitly owns one shared listener and route registry.
20
-
21
- The 2026-07-06 Hostinger POC proved the native Hermes listener for the
22
- `sellable-admin` profile with the existing `Sellable Reply Bot` app in
23
- `#team-chat`: a human mention produced a new Hermes session and a threaded bot
24
- reply. Treat that as proof that the gateway/listener path works for one owning
25
- profile, not as approval for multiple active customer profiles sharing one app
3
+ Sellable customer profiles use one dedicated native Slack app/token pair, one
4
+ workspace-locked plain Sellable MCP, and one native Hermes s6 gateway. There is
5
+ no shared dispatcher, route registry, listener fallback, or shared customer app
26
6
  token.
27
7
 
28
- The same day, the `acme` profile reused the same redacted Slack token
29
- fingerprints for an outbound-only smoke to `#sellable-acme` via:
30
-
31
- ```bash
32
- hermes --profile acme send --to slack:C0BFERDV3N0 --subject '[Hermes acme smoke]' ...
33
- ```
34
-
35
- That succeeded and is useful as an internal smoke test. It does not change the
36
- inbound listener rule: each active customer profile needs its own native Slack
37
- app token pair unless Sellable Admin deliberately chooses the dispatcher
38
- fallback.
39
-
40
- ## Which Slack Values Are Enough
41
-
42
- For outbound sends and ordinary Slack Web API calls, the important value is the
43
- profile-local `SLACK_BOT_TOKEN`.
44
-
45
- For native Hermes live listening through Slack Socket Mode, the profile also
46
- needs `SLACK_APP_TOKEN`, an app-level `xapp-...` token with `connections:write`.
47
- `SLACK_APP_ID`, `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`,
48
- `SLACK_SIGNING_SECRET`, `SLACK_VERIFICATION_TOKEN`, `SLACK_TEAM_ID`, and
49
- `SLACK_OPERATOR_EMAIL` do not replace `SLACK_APP_TOKEN`.
50
-
51
- ## Required Slack App Shape
52
-
53
- Outbound Slack sends can work with fewer permissions. Native Hermes inbound
54
- listening needs the Slack app itself to be configured for Socket Mode and event
55
- delivery:
8
+ The production Sellable Reply Bot remains separate HTTP-interactivity product
9
+ infrastructure. Keep Socket Mode off and keep its request URL at
10
+ `https://app.sellable.dev/api/v2/slack/interactions`.
56
11
 
57
- - Enable Socket Mode.
58
- - Create an app-level token with `connections:write`.
59
- - Install the bot with `app_mentions:read`, `channels:history`,
60
- `channels:read`, `chat:write`, `groups:history`, `groups:read`,
61
- `im:history`, `im:read`, `mpim:history`, `mpim:read`, and `users:read`.
62
- - Subscribe bot events for `app_mention`, `message.channels`,
63
- `message.groups`, `message.im`, and `message.mpim`.
64
- - Reinstall the app after changing scopes or event subscriptions.
12
+ ## Public Boundary
65
13
 
66
- If the Slack app has `incoming-webhook`, Slack will ask for a webhook channel
67
- during reinstall. Pick the profile home channel. That webhook selection does
68
- not replace `SLACK_ALLOWED_CHANNELS`; keep the profile-local allowlist explicit.
69
-
70
- ## Profile Bootstrap Command
71
-
72
- Use the Sellable installer profile bootstrap:
14
+ Customer onboarding is owned by the Sellable Admin native-app skill through one
15
+ public command:
73
16
 
74
17
  ```bash
75
- sellable hermes profile bootstrap \
76
- --profile acme \
77
- --profiles-root /srv/hermes/profiles \
78
- --workspace-id ws_acme \
79
- --workspace-name Acme \
80
- --token-file /run/secrets/sellable-acme-token \
81
- --slack-bot-token "$SLACK_BOT_TOKEN" \
82
- --slack-app-token "$SLACK_APP_TOKEN" \
83
- --slack-home-channel C0ACME12345 \
84
- --slack-home-channel-name sellable-acme \
85
- --slack-allowed-users U0OPERATOR1 \
86
- --slack-require-mention true \
87
- --json
18
+ sellable-admin-install hermes customer bootstrap --mode <mode> [approved inputs]
88
19
  ```
89
20
 
90
- If `--slack-home-channel` is supplied and `--slack-allowed-channels` is omitted,
91
- the installer writes `SLACK_ALLOWED_CHANNELS=<home-channel-id>` automatically.
92
- That makes the profile fail closed to the customer channel by default.
21
+ The operator uses read-only `plan`, then approved `apply`, and later
22
+ `resume|status|verify|rebuild|rollback` with the persisted `runId`. Do not ask
23
+ the operator to coordinate the public Sellable installer, Slack API/CLI,
24
+ cloud-browser, Gmail, profile, or gateway internals separately.
93
25
 
94
- Use `--slack-allowed-channels C...,G...` only when a profile is intentionally
95
- allowed to listen in more than one channel. Pass Slack IDs, not `#channel-name`
96
- strings or wildcards.
26
+ The approval packet binds exact team/workspace/channel/profile/user identity,
27
+ customer Sellable token-file ref, `gpt-5.6-sol`, redacted evidence root, and the
28
+ exact tested Admin install/MCP and Sellable install/MCP candidates plus all four
29
+ SHA-256 values.
97
30
 
98
- ## Resulting Profile Env
31
+ ## Public Sellable Installer Role
99
32
 
100
- For the dedicated customer Slack app model, the profile-local `.env` should
101
- contain only scoped Slack keys for that profile:
102
-
103
- ```env
104
- SLACK_BOT_TOKEN=...
105
- SLACK_APP_TOKEN=...
106
- SLACK_HOME_CHANNEL=C0ACME12345
107
- SLACK_HOME_CHANNEL_NAME=sellable-acme
108
- SLACK_ALLOWED_CHANNELS=C0ACME12345
109
- SLACK_ALLOWED_USERS=U0OPERATOR1
110
- SLACK_REQUIRE_MENTION=true
111
- ```
112
-
113
- Keep the file mode at `0600`. Do not copy raw tokens into planning docs, chat,
114
- or committed artifacts. Record token fingerprints only.
115
-
116
- The profile's Hermes MCP env must also include the profile-local Sellable
117
- config, customer workspace lock, and fail-closed guard:
33
+ The Admin factory calls the pinned `@sellable/install` candidate internally and
34
+ passes the pinned `@sellable/mcp` candidate through `--mcp-package`. The public
35
+ installer writes profile-local Sellable config and Hermes MCP env with:
118
36
 
119
37
  ```yaml
120
- SELLABLE_CONFIG_PATH: /srv/hermes/profiles/acme/sellable/config.json
121
- SELLABLE_CONFIGS_DIR: /srv/hermes/profiles/acme/sellable/configs
122
- SELLABLE_LOCK_WORKSPACE_ID: ws_acme
38
+ SELLABLE_CONFIG_PATH: /opt/data/profiles/<customerSlug>/sellable/config.json
39
+ SELLABLE_CONFIGS_DIR: /opt/data/profiles/<customerSlug>/sellable/configs
40
+ SELLABLE_LOCK_WORKSPACE_ID: <approvedWorkspaceId>
123
41
  SELLABLE_REQUIRE_WORKSPACE_LOCK: "1"
124
42
  ```
125
43
 
126
- `SELLABLE_REQUIRE_WORKSPACE_LOCK=1` means an unbound customer profile fails
127
- closed before making Sellable API calls instead of falling back to shared or
128
- admin config state. Only the `sellable-admin` profile should provision,
129
- inspect, or repair other profiles.
130
-
131
- For the shared dispatcher model, do not copy the shared `SLACK_BOT_TOKEN` or
132
- `SLACK_APP_TOKEN` into customer profile `.env` files. The dispatcher owns those
133
- tokens and route metadata decides the customer profile.
44
+ It does not create Slack apps, handle Slack tokens, install Admin MCP, or choose
45
+ the profile/workspace/channel from prompt text.
134
46
 
135
- ## Shared Dispatcher Ownership
136
-
137
- The shared Slack dispatcher and its route registry are fallback Sellable Admin
138
- surfaces. This customer-facing installer must not create shared route bundles or
139
- write shared listener tokens. Sellable Admin owns Slack app creation and profile
140
- provisioning, then calls this installer for profile-local Sellable runtime
141
- bootstrap inside the already-provisioned Hermes profile.
142
-
143
- ## Verification
144
-
145
- Run the installer and dispatcher UAT before publishing installer changes:
146
-
147
- ```bash
148
- npm run test:unit -- tests/install-package/agent-preferences.test.ts
149
- scripts/run-phase92-hermes-profile-bootstrap-uat.sh --mode local-temp
150
- scripts/run-phase92-hermes-profile-bootstrap-uat.sh --mode linux-shaped
151
- scripts/run-phase031-hermes-slack-dispatcher-uat.sh --mode local-fixture
152
- ```
47
+ ## Customer Isolation
153
48
 
154
- Expected proof:
49
+ Customer profiles are blank-created. They contain:
155
50
 
156
- - profile-local Sellable config paths remain authoritative
157
- - generated profile `.env` has `SLACK_HOME_CHANNEL`
158
- - generated profile `.env` has `SLACK_ALLOWED_CHANNELS`
159
- - `acme` allowlist points at the `sellable-acme` fixture channel id
160
- - redaction scan passes
51
+ - dedicated profile-local Slack runtime secrets in mode-`0600` storage
52
+ - exact channel and allowed-human ID allowlists
53
+ - customer-safe manifest with no slash/App Home/assistant/DM/MPIM surfaces
54
+ - the current `openai-codex` `gpt-5.6-sol` model object
55
+ - Hermes root OAuth fallback from `/opt/data/auth.json`
56
+ - pinned plain Sellable MCP locked to one approved workspace
57
+ - one profile-specific native s6 gateway
161
58
 
162
- ## Current Phase 03 Status
59
+ They never contain customer-local `auth.json`, provider pools, OpenRouter/raw
60
+ OpenAI keys, Sellable Admin MCP/admin skills, browser/Gmail/provisioning
61
+ credentials, TOTP material, or sibling data.
163
62
 
164
- Phase 03 proved the profile-scoped installer path, outbound Hermes send path,
165
- and live Slack listener for the `sellable-admin` profile. Phase 03.1 now targets
166
- dedicated native Slack apps per customer profile, profile-local Sellable MCP
167
- locks, and fail-closed customer profile behavior so managed customer channels
168
- can be onboarded through the VPS without local-only browser state, manual SSH
169
- edits, or untracked processes.
63
+ Direct MCP proof before gateway start must show one intended active/visible
64
+ workspace, `siblingInventoryFetched=false`, and no Admin MCP. Slack acceptance
65
+ then requires real API/CLI, secret-file, s6 process, same-thread reply,
66
+ negative-isolation, restart, rollback, rebuild, and redaction receipts tied to
67
+ the immutable package hashes.
package/README.md CHANGED
@@ -83,120 +83,37 @@ Auth is stored once at:
83
83
  ~/.sellable/config.json
84
84
  ```
85
85
 
86
- ## Hermes Profile-Scoped Installs
86
+ ## Hermes Customer Profiles
87
87
 
88
- For a single local Hermes profile, point Sellable auth and memory at the profile
89
- instead of the global `~/.sellable/config.json`:
88
+ Customer onboarding is owned by the Sellable Admin native customer bootstrap:
90
89
 
91
90
  ```bash
92
- profile="$HOME/.hermes/profiles/acme"
93
- sellable --host hermes \
94
- --sellable-config-path "$profile/sellable/config.json" \
95
- --sellable-configs-dir "$profile/sellable/configs"
91
+ sellable-admin-install hermes customer bootstrap --mode <mode> [approved inputs]
96
92
  ```
97
93
 
98
- That writes `SELLABLE_CONFIG_PATH` and `SELLABLE_CONFIGS_DIR` into
99
- `mcp_servers.sellable.env`, so first-run login, `get_auth_status`, workspace
100
- switches, and Sellable memory all use the same profile-local files. Hosted MCP
101
- mode does not receive local filesystem paths.
94
+ That command is the sole operator-facing interface. It calls the exact pinned
95
+ `@sellable/install` tarball internally and passes the exact pinned
96
+ `@sellable/mcp` tarball through `--mcp-package`. Do not ask an operator to run
97
+ the public installer, supply Slack tokens in argv, edit profile files, or manage
98
+ the gateway as separate onboarding steps.
102
99
 
103
- For zero-shot local or Hostinger/Linux VPS bootstrap, use the profile command:
100
+ The internal profile stage writes profile-local Sellable paths and the exact
101
+ workspace boundary:
104
102
 
105
- ```bash
106
- sellable hermes profile bootstrap \
107
- --profile acme \
108
- --profiles-root /srv/hermes/profiles \
109
- --workspace-id ws_acme \
110
- --workspace-name Acme \
111
- --token-file /run/secrets/sellable-acme-token \
112
- --json
113
- ```
114
-
115
- It creates:
116
-
117
- ```text
118
- /srv/hermes/profiles/acme/config.yaml
119
- /srv/hermes/profiles/acme/sellable/config.json
120
- /srv/hermes/profiles/acme/sellable/configs/
121
- /srv/hermes/profiles/acme/skills/sellable/
122
- /srv/hermes/profiles/acme/.env # only when Slack token inputs are supplied
103
+ ```yaml
104
+ SELLABLE_CONFIG_PATH: /opt/data/profiles/<customerSlug>/sellable/config.json
105
+ SELLABLE_CONFIGS_DIR: /opt/data/profiles/<customerSlug>/sellable/configs
106
+ SELLABLE_LOCK_WORKSPACE_ID: <approvedWorkspaceId>
107
+ SELLABLE_REQUIRE_WORKSPACE_LOCK: "1"
123
108
  ```
124
109
 
125
- Bootstrap also writes `SELLABLE_LOCK_WORKSPACE_ID=<workspace-id>` and
126
- `SELLABLE_REQUIRE_WORKSPACE_LOCK=1` into the profile's Hermes MCP env.
127
- Customer profiles are therefore locked to their own Sellable workspace:
128
- Sellable MCP requests are forced to that workspace, workspace switching/creation
129
- is blocked, workspace listings are filtered, and a missing lock fails closed
130
- instead of falling back to shared/admin config. Use the `sellable-admin` profile
131
- for provisioning or cross-profile inspection.
110
+ The locked MCP requests only the approved workspace, blocks workspace switching
111
+ and creation, and reports `siblingInventoryFetched=false`. The customer profile
112
+ must contain no Admin MCP, admin token, customer-local OAuth, or sibling config.
132
113
 
133
114
  See [`HERMES-SLACK-PROFILE-SCOPING.md`](./HERMES-SLACK-PROFILE-SCOPING.md) for
134
- the customer Slack isolation contract and Socket Mode token requirements.
135
-
136
- If no token is supplied, bootstrap still creates a pending
137
- `sellable/config.json` so the profile path is concrete from the first MCP
138
- launch. Finish auth with:
139
-
140
- ```bash
141
- sellable auth set <token> --workspace-id <workspace_id> \
142
- --sellable-config-path /srv/hermes/profiles/acme/sellable/config.json
143
- ```
144
-
145
- Slack Socket Mode tokens can be written profile-locally:
146
-
147
- ```bash
148
- sellable hermes profile bootstrap \
149
- --profile acme \
150
- --profiles-root /srv/hermes/profiles \
151
- --workspace-id ws_acme \
152
- --slack-bot-token "$SLACK_BOT_TOKEN" \
153
- --slack-app-token "$SLACK_APP_TOKEN"
154
- ```
155
-
156
- The Slack app must also be configured for inbound Socket Mode delivery. Outbound
157
- Slack Web API sends can work before this is complete, but Hermes will not hear
158
- mentions until the app has Socket Mode enabled, an app-level token with
159
- `connections:write`, bot scopes/events for `app_mention` plus channel/group/IM
160
- message events, and a fresh workspace reinstall after scope or event changes.
161
-
162
- Scope the profile to its customer channel at install time:
163
-
164
- ```bash
165
- sellable hermes profile bootstrap \
166
- --profile acme \
167
- --profiles-root /srv/hermes/profiles \
168
- --workspace-id ws_acme \
169
- --slack-bot-token "$SLACK_BOT_TOKEN" \
170
- --slack-app-token "$SLACK_APP_TOKEN" \
171
- --slack-home-channel C0ACME12345 \
172
- --slack-home-channel-name sellable-acme \
173
- --slack-allowed-users U0OPERATOR1 \
174
- --slack-require-mention true
175
- ```
176
-
177
- When `--slack-home-channel` is supplied without
178
- `--slack-allowed-channels`, bootstrap writes
179
- `SLACK_ALLOWED_CHANNELS=<home-channel-id>` too. That fail-closes a customer
180
- profile to the intended channel by default. Use
181
- `--slack-allowed-channels C...,G...` only when a profile is deliberately allowed
182
- to listen in multiple channels. Pass channel IDs, not `#channel-name`, and use
183
- separate Slack bot/app tokens per customer profile when you need hard isolation
184
- between concurrently listening Hermes profiles.
185
-
186
- Customer profiles should use customer-scoped Sellable credentials. A shared
187
- Sellable admin token can work for internal admin automation, but it is weaker
188
- isolation and should not be the default for customer profiles. After changing
189
- Hermes profile config, restart the gateway/session or run `/reload-mcp` and
190
- `/reload-skills`.
191
-
192
- Validate the active profile with the same path inputs:
193
-
194
- ```bash
195
- SELLABLE_CONFIG_PATH=/srv/hermes/profiles/acme/sellable/config.json \
196
- SELLABLE_CONFIGS_DIR=/srv/hermes/profiles/acme/sellable/configs \
197
- sellable --verify-only --host hermes --json \
198
- --artifact /srv/hermes/profiles/acme/sellable/verify-runtime.json
199
- ```
115
+ the dedicated native Slack app, root OAuth fallback, immutable package, s6
116
+ gateway, rebuild, and live UAT contract.
200
117
 
201
118
  Claude Code, Codex, and Hermes are configured to launch the same packaged MCP server. The
202
119
  installer also writes Sellable agent definitions from the packaged `agents/`
@@ -213,26 +213,19 @@ Auth:
213
213
  also pass --sellable-config-path or set SELLABLE_CONFIG_PATH so auth and MCP
214
214
  runtime use the same profile-local config file.
215
215
 
216
- Hermes profile bootstrap:
217
- sellable hermes profile bootstrap --profile acme --profiles-root /srv/hermes/profiles --workspace-id ws_acme --token-file /run/secrets/sellable-token --json
218
- sellable hermes profile bootstrap --profile-root "$HOME/.hermes/profiles/acme" --workspace-id ws_acme --json
219
-
220
- Bootstrap writes <profileRoot>/config.yaml, <profileRoot>/sellable/config.json,
221
- <profileRoot>/sellable/configs, Sellable Hermes skills, and optional
222
- <profileRoot>/.env Slack token and channel-scope keys. For customer profiles,
223
- pass --slack-home-channel; bootstrap defaults SLACK_ALLOWED_CHANNELS to that
224
- exact channel id unless --slack-allowed-channels is supplied. Use
225
- customer-scoped Sellable credentials and customer-scoped Slack credentials for
226
- customer profiles; shared admin tokens are weaker isolation and should remain
227
- internal-only. Bootstrap writes SELLABLE_REQUIRE_WORKSPACE_LOCK=1 whenever a
228
- workspace id is supplied, so a customer profile fails closed if the workspace
229
- lock is later removed or not yet provisioned.
230
-
231
- Hermes shared Slack dispatcher:
232
- Shared listener setup and route-registry validation are internal Sellable
233
- Admin responsibilities. This customer-facing installer only bootstraps a
234
- profile-local Sellable runtime after Sellable Admin provisions the profile
235
- and dispatcher route.
216
+ Hermes customer profile stage:
217
+ Managed customer onboarding uses only:
218
+ sellable-admin-install hermes customer bootstrap --mode <mode> [approved inputs]
219
+
220
+ The Admin state machine invokes this package's profile bootstrap internally
221
+ with exact token-file, workspace-lock, profile-root, and --mcp-package inputs.
222
+ Operators must not use the low-level profile command to coordinate Slack,
223
+ profile, or gateway onboarding manually. Customer profiles use root Codex
224
+ OAuth fallback and contain no local auth.json, provider pool, or Admin MCP.
225
+
226
+ Hermes customer Slack runtime:
227
+ Each customer profile uses its own dedicated native Slack app and gateway.
228
+ Sellable Admin owns the single resumable customer bootstrap interface.
236
229
  `;
237
230
  }
238
231
 
@@ -2099,9 +2092,9 @@ Desktop, then start a new thread.
2099
2092
 
2100
2093
  1. Call \`mcp__sellable__get_auth_status({})\`.
2101
2094
  2. Resolve the target workspace id from the user request, automation config, or install-time workspace mapping. Pass \`workspaceId\` on every scheduled or \`--yolo\` refill tool call. Missing \`workspaceId\` in scheduled or \`--yolo\` mode is a blocker; return \`WORKSPACE_REQUIRED\` instead of running against shared config state. Do not solve automation workspace uncertainty by changing the shared active workspace.
2102
- 3. Normalize invocation arguments with \`mcp__sellable__refill_sends({ yolo, executionMode, requireWorkspace, workspaceId, senders, senderIds, senderNames, actionTypes, targetDate, untilDate })\` when the host exposes typed MCP tools. Type shape: \`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" })\`.
2095
+ 3. Normalize invocation arguments with \`mcp__sellable__refill_sends({ yolo, executionMode, requireWorkspace, workspaceId, senders, senderIds, senderNames, targetDate, untilDate })\` when the host exposes typed MCP tools. Type shape: \`refill_sends({ yolo?: boolean, executionMode?: "manual" | "scheduled" | "yolo", requireWorkspace?: boolean, workspaceId?: string, senders?: string[], senderIds?: string[], senderNames?: string[], horizonSendDays?: number, untilDate?: "YYYY-MM-DD", targetDate?: "YYYY-MM-DD" })\`.
2103
2096
  4. Clover scheduled automation example: \`mcp__sellable__refill_sends({ yolo:false, executionMode:"scheduled", requireWorkspace:true, workspaceId:"cmlq1v8ms0000jx04ang4hi7e" })\`, then \`mcp__sellable__get_refill_target_plan({ intent:"plain", approvalMode:"mark_ready", workspaceId:"cmlq1v8ms0000jx04ang4hi7e" })\`.
2104
- 5. Call \`mcp__sellable__get_refill_target_plan({ workspaceId, senders, senderIds, senderNames, actionTypes, targetDate, untilDate })\` before any mutation. Render \`target.eligibleSenderLedger\`, \`target.senderRefillPlans[]\`, and \`target.globalActionQueue\` as the structured packet. Preserve the labels \`Need to prepare\`, \`Goal\`, \`Already sent\`, \`Scheduled\`, \`Ready and waiting to be scheduled\`, and \`Still need\`. In \`--yolo\`, \`mcp__sellable__refill_sends\` maintains a run-local \`refreshedPaidInmailSenderIds\` set, refreshes stale/missing paid InMail facts for each selected paid-InMail sender at most once, reruns \`get_refill_target_plan\`, and returns \`autoPaidInmailRefresh\` plus the post-refresh \`targetPlan\` before choosing the next prep/source-copy/bounded-approval/read-only wait or exact-date sweep action. Refill ladder: approve generated rows only through the explicit bounded approval gate, process existing same-campaign unenriched/unprepared rows before source work, then same-source copy, then provider-aligned source-more. New source/provider switches change reply-rate baseline and are manual alternates, not \`--yolo\` side effects. Do not present paid-credit refresh as the next operator action after \`refill_sends\` returns that post-refresh plan. For exact-date proof, \`mcp__sellable__get_scheduler_fill_capacity\` is read-only and tells how many cells the scheduler will try to place for a sender/action/date. When the planner returns a request-scoped exact-date scheduler sweep, execute \`mcp__sellable__run_scheduler_sweep({ workspaceId, action:"run", targetDate, requestKey, targetShapeRevision })\` once, capture its receipt, and fully reread before another action. It may schedule eligible workspace cells through existing gates, but it never sends directly or bypasses limits. If the returned target is already complete, stop with the no-op receipt. Only non-exact or no-sweep waits use the read-only scheduler wait/re-read loop until projected coverage fills, a concrete non-scheduler blocker appears, or Christian explicitly stops/statuses the run.
2097
+ 5. Call \`mcp__sellable__get_refill_target_plan({ workspaceId, senders, senderIds, senderNames, targetDate, untilDate })\` before any mutation. Render \`target.eligibleSenderLedger\`, \`target.senderRefillPlans[]\`, and \`target.globalActionQueue\` as the structured packet. Preserve the labels \`Need to prepare\`, \`Goal\`, \`Already sent\`, \`Scheduled\`, \`Ready and waiting to be scheduled\`, and \`Still need\`. In \`--yolo\`, \`mcp__sellable__refill_sends\` maintains a run-local \`refreshedPaidInmailSenderIds\` set, refreshes stale/missing paid InMail facts for each selected paid-InMail sender at most once, reruns \`get_refill_target_plan\`, and returns \`autoPaidInmailRefresh\` plus the post-refresh \`targetPlan\` before choosing the next prep/source-copy/bounded-approval/read-only wait action. Refill ladder: approve generated rows only through the explicit bounded approval gate, process existing same-campaign unenriched/unprepared rows before source work, then same-source copy, then provider-aligned source-more. New source/provider switches change reply-rate baseline and are manual alternates, not \`--yolo\` side effects. Do not present paid-credit refresh as the next operator action after \`refill_sends\` returns that post-refresh plan. For exact-date proof, \`mcp__sellable__get_scheduler_fill_capacity\` is read-only and tells how many cells the scheduler will try to place for a sender/action/date. When ready rows cover the target but scheduled rows do not, \`mcp__sellable__run_scheduler_sweep({ workspaceId, action:"run" })\` may request immediate placement within existing gates and returns a receipt, but it never sends or bypasses limits. If the returned target is already complete, stop with the no-op receipt. If ready rows cover the target but scheduled rows do not, keep the run open in the read-only scheduler wait/re-read loop until projected coverage fills, a concrete non-scheduler blocker appears, or Christian explicitly stops/statuses the run.
2105
2098
  6. Load the canonical prompt and deterministic flow asset via
2106
2099
  \`mcp__sellable__get_subskill_prompt({ subskillName: "refill-sends" })\`,
2107
2100
  then load \`mcp__sellable__get_subskill_prompt({ subskillName: "refill-sends-workflow" })\`,
@@ -2112,7 +2105,8 @@ Desktop, then start a new thread.
2112
2105
  facts are present, refresh exact selected sender facts once, rerun
2113
2106
  \`get_refill_target_plan\`, and only then enter \`wait_for_scheduler\` if
2114
2107
  freshness is clean.
2115
- 7. Follow the canonical prompt exactly. Do not directly send, bypass scheduler gates, or raw-write scheduler fields. Only the exact rendered paused-campaign start and request-scoped scheduler sweep may use their existing product-gated paths; do not launch, archive, or delete unrelated work.
2108
+ 7. Follow the canonical prompt exactly. Do not launch, start, archive, delete,
2109
+ or write scheduler rows as part of refill sends.
2116
2110
 
2117
2111
  ## MCP Prompt Fallback
2118
2112
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.320",
3
+ "version": "0.1.321",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {
@@ -83,8 +83,6 @@ Accepted invocation flags in the same user request:
83
83
  - `senderIds: <id>, <id>` or `senderNames: <name>, <name>`: explicit selector
84
84
  alternatives when the host preserves natural-language arguments better than
85
85
  shell-style flags.
86
- - `actionTypes: send_invite, send_inmail_closed`: optional lane selector. Omit
87
- `actionTypes` to preserve the target planner's lane inference.
88
86
  - `untilDate: YYYY-MM-DD`: explicit date selector alternative when the host
89
87
  preserves natural-language arguments better than shell-style flags.
90
88
  - `targetDate: YYYY-MM-DD`: exact-date selector alternative when the host
@@ -93,7 +91,7 @@ Accepted invocation flags in the same user request:
93
91
  When the host can call typed MCP tools, start with:
94
92
 
95
93
  ```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" })
94
+ refill_sends({ yolo?: boolean, executionMode?: "manual" | "scheduled" | "yolo", requireWorkspace?: boolean, workspaceId?: string, senders?: string[], senderIds?: string[], senderNames?: string[], horizonSendDays?: number, untilDate?: "YYYY-MM-DD", targetDate?: "YYYY-MM-DD" })
97
95
  ```
98
96
 
99
97
  That command helper normalizes arguments and returns the execution contract. In
@@ -101,22 +99,14 @@ non-yolo mode it does not mutate. In `--yolo`, it may execute exactly one safe
101
99
  bounded primitive from the fresh `target.globalActionQueue[0]`, then reread and
102
100
  return the new target plan; currently safe primitives are paid-credit refresh,
103
101
  existing-row message preparation, generated-message approval, receipt-proven
104
- same-source row copy, a request-scoped exact-date product scheduler sweep, and
105
- read-only wait rereads. Same-source copy/source
102
+ same-source row copy, and read-only wait rereads. Same-source copy/source
106
103
  fallback is safe only after receipt-proven exhaustion:
107
104
  `hasMoreFrontierRows:false`, zero `approvalCandidates`, no `stuckActiveCells`,
108
- and no non-terminal `approvedNotDispatched` work. It does not run unbounded
109
- approval, lower paid-InMail thresholds, switch source families, create
110
- campaigns, launch, or send directly; it never raw-writes scheduler fields.
111
- Continue with the workflow below for route selection, state rereads, approval
112
- gating, source import, preparation, and bounded approval.
113
-
114
- The approval scope is the exact `workspaceId`, `targetDate`, sender ids, action
115
- types, campaign/table/source ids, `targetShapeRevision`, and `actionKey` in the
116
- rendered packet. `stateRevision` and sent/scheduled/ready counts are mutable
117
- progress, not approval-scope drift. Execute only
118
- `target.globalActionQueue[0]`, then perform a full authoritative reread before
119
- deciding any next action.
105
+ and no non-terminal `approvedNotDispatched` work. It does not run unbounded approval, lower
106
+ paid-InMail thresholds, switch source families, create campaigns, launch, send,
107
+ or write scheduler rows. Continue with the workflow below for route selection,
108
+ state rereads, approval gating, source import, preparation, and bounded
109
+ approval.
120
110
 
121
111
  ## Workspace Contract
122
112
 
@@ -286,27 +276,10 @@ need raw proof, call the read-only `get_scheduler_fill_capacity` query for the
286
276
  same sender/action/date; it tells the MCP how many cells the product scheduler
287
277
  will try to place and does not import, approve, schedule, refresh credits, or
288
278
  mutate.
289
- Treat `rollingWeeklyInvite.capacityFreedDuringWindow:true` as proof that
290
- rolling-weekly capacity frees later in the send window and remains schedulable;
291
- use its timing fields instead of treating the window-start gate as a permanent
292
- full-day blocker.
293
279
  When the refill loop has ready rows and needs scheduler pickup now, use
294
280
  `run_scheduler_sweep` with the same explicit `workspaceId`; it can place cells
295
281
  within existing scheduler gates and returns the receipt, but it never sends or
296
282
  bypasses limits.
297
- A scheduler sweep is a visible workspace-wide scheduling side effect. The
298
- request packet limits the expected target changes to its exact date, senders,
299
- action types, campaigns, tables, and stable sources, but the product scheduler
300
- may also place unrelated eligible workspace work; disclose that possibility in
301
- the approval packet and receipt. For an exact-date sweep action, execute it
302
- once, then reread the full target plan before taking or presenting another
303
- action.
304
- The exact-date `run_scheduler_sweep` may schedule eligible workspace cells
305
- through existing product gates; it never sends directly or raw-writes scheduler
306
- fields. Only non-exact or no-sweep scheduler waits are read-only.
307
- On resume, reconcile the current target plan and matching sweep status/receipt
308
- before retrying. A matching same-key terminal receipt replays; an
309
- `uncertain_outcome` stops for reconciliation and must not schedule again.
310
283
  Scheduler-run receipt interpretation: `cellsConsidered is allocation-attempt
311
284
  count`, not total ready supply, while `readyCellsFound` is ready inventory found
312
285
  before prefilters. Inspect `campaignScopeSummary` before assuming the selected
@@ -419,14 +392,6 @@ scheduler wait loop. Do not finish the refill goal, mark it complete, or mark it
419
392
  blocked only because it is still `awaiting_scheduler_after_ready_buffer`; keep
420
393
  waiting unless Christian stops the run or the host cannot continue.
421
394
 
422
- Future scheduled coverage and already sent actions are distinct; only
423
- scheduler-owned future scheduled actions count as scheduled coverage. For
424
- multiple target dates, finish D1 execution and the full D1 reread before
425
- planning D2. Completion proof is product-native Sellable MCP evidence only:
426
- target plan, campaign refill state, scheduler capacity, sweep/status, and
427
- bounded receipts. Never use individual cell ids, Prisma, SQL, direct database
428
- access, or production-environment scripts as completion proof.
429
-
430
395
  In `--yolo`, the default fill target is the scheduler-forward 48-hour window
431
396
  unless `--target-date`/`targetDate`, `--until`/`untilDate`, or an explicit
432
397
  compatibility `horizonSendDays` is provided. If a target date is provided,