@sellable/install 0.1.327 → 0.1.329
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 +169 -0
- package/README.md +41 -5
- package/agents/registry.json +2 -2
- package/bin/sellable-install.mjs +385 -112
- package/lib/codex-plugin-selection.mjs +442 -0
- package/lib/runtime-verify.mjs +2 -31
- package/package.json +3 -2
- package/skill-templates/create-campaign.md +3 -3
- package/skill-templates/create-evergreen-campaigns.md +16 -16
- package/skill-templates/refill-sends-v2.md +6 -6
- package/skill-templates/refill-sends.md +91 -353
- package/skill-templates/find-leads.md +0 -57
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Hermes Slack Profile Scoping
|
|
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
|
|
26
|
+
token.
|
|
27
|
+
|
|
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:
|
|
56
|
+
|
|
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.
|
|
65
|
+
|
|
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:
|
|
73
|
+
|
|
74
|
+
```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
|
|
88
|
+
```
|
|
89
|
+
|
|
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.
|
|
93
|
+
|
|
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.
|
|
97
|
+
|
|
98
|
+
## Resulting Profile Env
|
|
99
|
+
|
|
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:
|
|
118
|
+
|
|
119
|
+
```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
|
|
123
|
+
SELLABLE_REQUIRE_WORKSPACE_LOCK: "1"
|
|
124
|
+
```
|
|
125
|
+
|
|
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.
|
|
134
|
+
|
|
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
|
+
```
|
|
153
|
+
|
|
154
|
+
Expected proof:
|
|
155
|
+
|
|
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
|
|
161
|
+
|
|
162
|
+
## Current Phase 03 Status
|
|
163
|
+
|
|
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.
|
package/README.md
CHANGED
|
@@ -122,6 +122,17 @@ It creates:
|
|
|
122
122
|
/srv/hermes/profiles/acme/.env # only when Slack token inputs are supplied
|
|
123
123
|
```
|
|
124
124
|
|
|
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.
|
|
132
|
+
|
|
133
|
+
See [`HERMES-SLACK-PROFILE-SCOPING.md`](./HERMES-SLACK-PROFILE-SCOPING.md) for
|
|
134
|
+
the customer Slack isolation contract and Socket Mode token requirements.
|
|
135
|
+
|
|
125
136
|
If no token is supplied, bootstrap still creates a pending
|
|
126
137
|
`sellable/config.json` so the profile path is concrete from the first MCP
|
|
127
138
|
launch. Finish auth with:
|
|
@@ -142,6 +153,36 @@ sellable hermes profile bootstrap \
|
|
|
142
153
|
--slack-app-token "$SLACK_APP_TOKEN"
|
|
143
154
|
```
|
|
144
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
|
+
|
|
145
186
|
Customer profiles should use customer-scoped Sellable credentials. A shared
|
|
146
187
|
Sellable admin token can work for internal admin automation, but it is weaker
|
|
147
188
|
isolation and should not be the default for customer profiles. After changing
|
|
@@ -171,7 +212,6 @@ source-scout agents.
|
|
|
171
212
|
Use the same public entrypoints across supported hosts:
|
|
172
213
|
|
|
173
214
|
- Claude Code: `/sellable:create-campaign`
|
|
174
|
-
- Claude Code: `/sellable:find-leads`
|
|
175
215
|
- Claude Code: `/sellable:create-ab-test`
|
|
176
216
|
- Claude Code: `/sellable:create-evergreen-campaigns`
|
|
177
217
|
- Claude Code: `/sellable:foundation`
|
|
@@ -180,7 +220,6 @@ Use the same public entrypoints across supported hosts:
|
|
|
180
220
|
- Claude Code: `/sellable:refresh-sender-engagement`
|
|
181
221
|
- Claude Code: `/sellable:refill-sends`
|
|
182
222
|
- Codex: `$sellable:create-campaign`
|
|
183
|
-
- Codex: `$sellable:find-leads`
|
|
184
223
|
- Codex: `$sellable:create-ab-test`
|
|
185
224
|
- Codex: `$sellable:create-evergreen-campaigns`
|
|
186
225
|
- Codex: `$sellable:foundation`
|
|
@@ -189,7 +228,6 @@ Use the same public entrypoints across supported hosts:
|
|
|
189
228
|
- Codex: `$sellable:refresh-sender-engagement`
|
|
190
229
|
- Codex: `$sellable:refill-sends`
|
|
191
230
|
- Hermes: `/sellable-create-campaign`
|
|
192
|
-
- Hermes: `/sellable-find-leads`
|
|
193
231
|
- Hermes: `/sellable-create-ab-test`
|
|
194
232
|
- Hermes: `/sellable-create-evergreen-campaigns`
|
|
195
233
|
- Hermes: `/sellable-foundation`
|
|
@@ -199,7 +237,6 @@ Use the same public entrypoints across supported hosts:
|
|
|
199
237
|
- Hermes: `/sellable-refill-sends`
|
|
200
238
|
- Codex Desktop plugin: `sellable@sellable`
|
|
201
239
|
- Codex visible skill: `Sellable Create Campaign`
|
|
202
|
-
- Codex visible skill: `Sellable Find Leads`
|
|
203
240
|
- Codex visible skill: `Sellable Create A/B Test`
|
|
204
241
|
- Codex visible skill: `Sellable Create Evergreen Campaigns`
|
|
205
242
|
- Codex visible skill: `Sellable Foundation`
|
|
@@ -208,7 +245,6 @@ Use the same public entrypoints across supported hosts:
|
|
|
208
245
|
- Codex visible skill: `Sellable Refresh Sender Engagement`
|
|
209
246
|
- Codex visible skill: `Sellable Refill Sends`
|
|
210
247
|
- Internal MCP workflow prompt: `create-campaign-v2`
|
|
211
|
-
- Internal MCP workflow prompt: `find-leads-v2`
|
|
212
248
|
- Internal MCP workflow prompt: `refill-sends-workflow`
|
|
213
249
|
- Internal/backward-compatible memory prompt: `interview`
|
|
214
250
|
|
package/agents/registry.json
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"ownership": "message strategy, proof inventory, token rules, skeptical-prospect review, and selected winner only",
|
|
27
27
|
"codex": {
|
|
28
28
|
"description": "Message Drafting worker for campaign-backed template proposals after confirm_lead_list imports a non-empty bounded review batch.",
|
|
29
|
-
"model": "gpt-5.
|
|
30
|
-
"modelReasoningEffort": "
|
|
29
|
+
"model": "gpt-5.6-sol",
|
|
30
|
+
"modelReasoningEffort": "high",
|
|
31
31
|
"sandboxMode": "read-only",
|
|
32
32
|
"nicknameCandidates": [
|
|
33
33
|
"Message Drafting",
|