@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.
- package/HERMES-SLACK-PROFILE-SCOPING.md +44 -146
- package/README.md +20 -103
- package/bin/sellable-install.mjs +17 -23
- package/package.json +1 -1
- package/skill-templates/refill-sends.md +7 -42
|
@@ -1,169 +1,67 @@
|
|
|
1
1
|
# Hermes Slack Profile Scoping
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
|
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
|
-
|
|
91
|
-
the
|
|
92
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
##
|
|
31
|
+
## Public Sellable Installer Role
|
|
99
32
|
|
|
100
|
-
|
|
101
|
-
|
|
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: /
|
|
121
|
-
SELLABLE_CONFIGS_DIR: /
|
|
122
|
-
SELLABLE_LOCK_WORKSPACE_ID:
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
49
|
+
Customer profiles are blank-created. They contain:
|
|
155
50
|
|
|
156
|
-
- profile-local
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
- `
|
|
160
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
|
86
|
+
## Hermes Customer Profiles
|
|
87
87
|
|
|
88
|
-
|
|
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
|
-
|
|
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
|
|
99
|
-
`
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
100
|
+
The internal profile stage writes profile-local Sellable paths and the exact
|
|
101
|
+
workspace boundary:
|
|
104
102
|
|
|
105
|
-
```
|
|
106
|
-
sellable
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
126
|
-
`
|
|
127
|
-
|
|
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
|
|
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/`
|
package/bin/sellable-install.mjs
CHANGED
|
@@ -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
|
|
217
|
-
|
|
218
|
-
sellable hermes
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
@@ -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[],
|
|
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,
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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,
|