@floomhq/signaldash 0.37.0 → 0.39.0
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/README.md +21 -1
- package/bin/sd.mjs +85 -0
- package/package.json +2 -2
- package/skills/content/SKILL.md +58 -0
- package/skills/signaldash/SKILL.md +16 -2
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ setup installs both from the same pinned npm package
|
|
|
102
102
|
the human chose to execute:
|
|
103
103
|
|
|
104
104
|
```bash
|
|
105
|
-
npx -y @floomhq/signaldash@0.
|
|
105
|
+
npx -y @floomhq/signaldash@0.39.0 <invite-code>
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
Run that command in a terminal, not in an agent chat. Do not ask an agent to
|
|
@@ -132,6 +132,7 @@ SignalDash exposes:
|
|
|
132
132
|
- `li_list_chats`
|
|
133
133
|
- `li_read_messages(chat_id)`
|
|
134
134
|
- `li_mark_read(chat_id)`
|
|
135
|
+
- `li_start_chat(member_ids, text, confirm?, approval_hash?, dry_run?)`
|
|
135
136
|
- `li_send_message(chat_id?, text?, expected_watermark?, mark_read?, secretary_receipt_id?)`
|
|
136
137
|
- `li_send_invitation(provider_id, note?, confirm?)`
|
|
137
138
|
- `li_invitations_received(limit?, cursor?)`
|
|
@@ -163,6 +164,7 @@ SignalDash exposes:
|
|
|
163
164
|
- `wa_mark_read(chat_id)`
|
|
164
165
|
- `wa_get_attachment(chat_id, message_id, attachment_id)`
|
|
165
166
|
- `wa_transcribe_voice(chat_id, message_id, attachment_id)`
|
|
167
|
+
- `wa_start_chat(member_ids, text, confirm?, approval_hash?, dry_run?)`
|
|
166
168
|
- `wa_send_message(chat_id, text?, attachments?, expected_watermark?, mark_read?)`
|
|
167
169
|
- `wa_delete_message(chat_id, message_id)`
|
|
168
170
|
- `wa_delete_messages(messages)`
|
|
@@ -187,6 +189,24 @@ SignalDash exposes:
|
|
|
187
189
|
Every operation runs through the hosted SignalDash backend. Agents never
|
|
188
190
|
receive the Unipile access key.
|
|
189
191
|
|
|
192
|
+
`li_start_chat` and `wa_start_chat` are separate tools because LinkedIn and
|
|
193
|
+
WhatsApp expose different stable member-ID formats and spend different budget
|
|
194
|
+
lanes. Both use the same safety contract. A first call previews 1 to 10 exact
|
|
195
|
+
member IDs and the exact first message. A confirmed call requires the
|
|
196
|
+
payload-bound approval hash, resolves every member live, rejects protected or
|
|
197
|
+
suppressed contacts, and checks attendee-scoped provider evidence for an exact
|
|
198
|
+
existing member set. An existing match is returned without creating a chat or
|
|
199
|
+
sending the text. A new chat costs one action, independent of member count,
|
|
200
|
+
because the provider performs one irreversible conversation-and-message write.
|
|
201
|
+
Success requires readback of exactly one matching chat and exactly one own first
|
|
202
|
+
message. `dry_run:true` repeats the live preflight and stops before the provider
|
|
203
|
+
write, without consuming approval or budget. Database capability flags
|
|
204
|
+
`linkedin_chat_start` and `whatsapp_chat_start` disable the paths immediately.
|
|
205
|
+
When WhatsApp resolves an `@lid` alias to a canonical member ID, the first
|
|
206
|
+
preview returns `member_alias_requires_exact_id` and `resolved_member_id`.
|
|
207
|
+
Preview again with that exact ID before approval. SignalDash never substitutes
|
|
208
|
+
an unapproved member identity inside a confirmed write.
|
|
209
|
+
|
|
190
210
|
The inspiration library also extracts LinkedIn post references already stored
|
|
191
211
|
inside conversation `signals`. This path scans SQLite only and makes zero
|
|
192
212
|
LinkedIn or Unipile calls. Feed URLs and post slugs become canonical activity
|
package/bin/sd.mjs
CHANGED
|
@@ -351,6 +351,56 @@ const TOOLS = [
|
|
|
351
351
|
additionalProperties: false,
|
|
352
352
|
},
|
|
353
353
|
},
|
|
354
|
+
{
|
|
355
|
+
name: "sd_content_pipeline_set", path: "/sd/content/set",
|
|
356
|
+
description: "Enable or disable the database-gated content pipeline. Enabling captures only new free text from the verified Secretary group. It never publishes or schedules a post.",
|
|
357
|
+
inputSchema: { type:"object",properties:{enabled:{type:"boolean"},confirm:{type:"boolean",const:true}},required:["enabled","confirm"],additionalProperties:false },
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: "sd_content_pipeline_list", path: "/sd/content/list",
|
|
361
|
+
description: "Read the durable idea to measured state machine with zero provider calls.",
|
|
362
|
+
inputSchema: { type:"object",properties:{state:{type:"string",enum:["all","idee","gepaart","entwurf","freigegeben","geplant","veroeffentlicht","gemessen"]},limit:{type:"integer",minimum:1,maximum:500}},additionalProperties:false },
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: "sd_content_pipeline_pair", path: "/sd/content/pair",
|
|
366
|
+
description: "Pair one Federico raw content input with one exact unused library post. Refuses arcs without stored source text, author, reactions, and comments.",
|
|
367
|
+
inputSchema: { type:"object",properties:{id:{type:"string"},arc_inspiration_id:{type:"string"},confirm:{type:"boolean",const:true}},required:["id","arc_inspiration_id","confirm"],additionalProperties:false },
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: "sd_content_pipeline_draft", path: "/sd/content/draft",
|
|
371
|
+
description: "Store one model-authored human-review draft with exactly one tested lever. Runs the executable fact gate and refuses silent weak-category drafts and unsupported agent numbers.",
|
|
372
|
+
inputSchema: { type:"object",properties:{id:{type:"string"},category:{type:"string",enum:["ereignis_mit_einsatz","innenschau"]},weak_category_decision:{type:"string",enum:["ereignis_vorangestellt","bewusst_reichweitenschwach"]},category_evidence:{type:"string",minLength:1,maxLength:500,description:"Required with ereignis_vorangestellt and must exactly start the draft."},tested_lever:{type:"string",minLength:1,maxLength:120},draft_text:{type:"string",minLength:1,maxLength:65536},numeric_claims:{type:"array",maxItems:100,items:{type:"object",properties:{value:{type:"string"},origin:{type:"string",enum:["federico_raw","agent"]},source:{type:"string"},confirmed_at:{type:"string"}},required:["value","origin"],additionalProperties:false}}},required:["id","category","tested_lever","draft_text"],additionalProperties:false },
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: "sd_content_pipeline_preview", path: "/sd/content/preview",
|
|
376
|
+
description: "Re-run the fact gate and render the exact reference metrics, Federico input, and finished draft as a PNG payload for the existing guarded WhatsApp attachment tool.",
|
|
377
|
+
inputSchema: { type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:false },
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: "sd_content_pipeline_decide", path: "/sd/content/decide",
|
|
381
|
+
description: "Record Federico's exact yes, no, or correction response. Yes advances to approved but never schedules or publishes.",
|
|
382
|
+
inputSchema: { type:"object",properties:{id:{type:"string"},decision:{type:"string",enum:["yes","no","correction"]},correction:{type:"string"},decision_message_id:{type:"string",description:"Exact WhatsApp reply message id, when the decision came from the Secretary group."},confirm:{type:"boolean",const:true}},required:["id","decision","confirm"],additionalProperties:false },
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: "sd_content_pipeline_bind_schedule", path: "/sd/content/bind_schedule",
|
|
386
|
+
description: "Bind an already human-approved SignalDash scheduled post after a fresh complete SignalDash plus Buffer calendar read, fact gate, same-day, three-per-week, and active-post breathing checks.",
|
|
387
|
+
inputSchema: { type:"object",properties:{id:{type:"string"},scheduled_post_id:{type:"string"},scheduled_at:{type:"string",format:"date-time"},confirm:{type:"boolean",const:true}},required:["id","scheduled_post_id","scheduled_at","confirm"],additionalProperties:false },
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: "sd_content_pipeline_published", path: "/sd/content/published",
|
|
391
|
+
description: "Bind one planned item to exact provider publication evidence. This records evidence and publishes nothing.",
|
|
392
|
+
inputSchema: { type:"object",properties:{id:{type:"string"},post_urn:{type:"string",pattern:"^urn:li:(activity|ugcPost|share):[0-9]+$"},published_at:{type:"string",format:"date-time"},confirm:{type:"boolean",const:true}},required:["id","post_urn","published_at","confirm"],additionalProperties:false },
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: "sd_content_pipeline_measure", path: "/sd/content/measure",
|
|
396
|
+
description: "Store one due 24-hour or 72-hour provider measurement against Federico's own category baseline and emit a lever learning report at each five-post boundary.",
|
|
397
|
+
inputSchema: { type:"object",properties:{id:{type:"string"},window_hours:{type:"integer",enum:[24,72]},reactions:{type:"integer",minimum:0},comments:{type:"integer",minimum:0},impressions:{type:"integer",minimum:0},provider_evidence:{type:"object",properties:{source:{type:"string",const:"li_my_posts"},post_urn:{type:"string",pattern:"^urn:li:(activity|ugcPost|share):[0-9]+$"},captured_at:{type:"string",format:"date-time"},reactions:{type:"integer",minimum:0},comments:{type:"integer",minimum:0},impressions:{type:"integer",minimum:0}},required:["source","post_urn","captured_at","reactions","comments"],additionalProperties:true}},required:["id","window_hours","reactions","comments","provider_evidence"],additionalProperties:false },
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: "sd_content_board", path: "/sd/content/board",
|
|
401
|
+
description: "Generate the static board HTML from the same pipeline and stored shared-calendar rows. Makes zero provider calls.",
|
|
402
|
+
inputSchema: { type:"object",properties:{},additionalProperties:false },
|
|
403
|
+
},
|
|
354
404
|
{
|
|
355
405
|
name: "sd_inspiration_list",
|
|
356
406
|
path: "/sd/inspiration/list",
|
|
@@ -491,6 +541,23 @@ const TOOLS = [
|
|
|
491
541
|
additionalProperties: false,
|
|
492
542
|
},
|
|
493
543
|
},
|
|
544
|
+
{
|
|
545
|
+
name: "li_start_chat",
|
|
546
|
+
path: "/li/start_chat",
|
|
547
|
+
description: "Preview or start one LinkedIn direct or group chat with 1 to 10 exact provider member IDs and one exact first message. First call without confirm. After human approval of the complete member set and text, repeat the exact payload with confirm:true and approval_hash. dry_run:true runs the full live preflight twice and stops immediately before the provider write. One created chat spends one thread-message and aggregate action regardless of member count. Existing exact member sets are returned without creating or sending anything.",
|
|
548
|
+
inputSchema: {
|
|
549
|
+
type: "object",
|
|
550
|
+
properties: {
|
|
551
|
+
member_ids: { type: "array", minItems: 1, maxItems: 10, uniqueItems: true, items: { type: "string", minLength: 3, maxLength: 500, pattern: "^(?:ACo|ACw|AE)[A-Za-z0-9_-]+$" } },
|
|
552
|
+
text: { type: "string", minLength: 1, maxLength: 5000 },
|
|
553
|
+
confirm: { type: "boolean", default: false },
|
|
554
|
+
approval_hash: { type: "string", minLength: 64, maxLength: 64, pattern: "^[0-9a-f]{64}$" },
|
|
555
|
+
dry_run: { type: "boolean", const: true },
|
|
556
|
+
},
|
|
557
|
+
required: ["member_ids", "text"],
|
|
558
|
+
additionalProperties: false,
|
|
559
|
+
},
|
|
560
|
+
},
|
|
494
561
|
{
|
|
495
562
|
name: "li_send_message",
|
|
496
563
|
ch: "li",
|
|
@@ -1205,6 +1272,23 @@ const TOOLS = [
|
|
|
1205
1272
|
additionalProperties: false,
|
|
1206
1273
|
},
|
|
1207
1274
|
},
|
|
1275
|
+
{
|
|
1276
|
+
name: "wa_start_chat",
|
|
1277
|
+
path: "/wa/start_chat",
|
|
1278
|
+
description: "Preview or start one WhatsApp direct or group chat with 1 to 10 exact @s.whatsapp.net or @lid member IDs and one exact first message. First call without confirm. If the provider maps an @lid alias to a canonical ID, repeat the preview with the returned resolved_member_id so the human approves the exact provider-write identity. After human approval of the complete member set and text, repeat the exact payload with confirm:true and approval_hash. dry_run:true runs the full live preflight twice and stops immediately before the provider write. One created chat spends one WhatsApp/email send action regardless of member count. Existing exact member sets are returned without creating or sending anything.",
|
|
1279
|
+
inputSchema: {
|
|
1280
|
+
type: "object",
|
|
1281
|
+
properties: {
|
|
1282
|
+
member_ids: { type: "array", minItems: 1, maxItems: 10, uniqueItems: true, items: { type: "string", minLength: 5, maxLength: 500, pattern: "^[A-Za-z0-9._-]+@(?:s\\.whatsapp\\.net|lid)$" } },
|
|
1283
|
+
text: { type: "string", minLength: 1, maxLength: 5000 },
|
|
1284
|
+
confirm: { type: "boolean", default: false },
|
|
1285
|
+
approval_hash: { type: "string", minLength: 64, maxLength: 64, pattern: "^[0-9a-f]{64}$" },
|
|
1286
|
+
dry_run: { type: "boolean", const: true },
|
|
1287
|
+
},
|
|
1288
|
+
required: ["member_ids", "text"],
|
|
1289
|
+
additionalProperties: false,
|
|
1290
|
+
},
|
|
1291
|
+
},
|
|
1208
1292
|
{
|
|
1209
1293
|
name: "wa_send_message",
|
|
1210
1294
|
ch: "wa",
|
|
@@ -1482,6 +1566,7 @@ const TOOLS = [
|
|
|
1482
1566
|
text: { type: "string", minLength: 1, maxLength: 3000 },
|
|
1483
1567
|
publish: { type: "boolean" },
|
|
1484
1568
|
scheduled_at: { type: "string", format: "date-time" },
|
|
1569
|
+
content_pipeline_id: { type: "string", minLength: 1, maxLength: 200 },
|
|
1485
1570
|
first_comment: { type: "string", minLength: 1, maxLength: 1250 },
|
|
1486
1571
|
mentions: {
|
|
1487
1572
|
type: "array", maxItems: 20,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floomhq/signaldash",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "Secure LinkedIn, WhatsApp, and email access for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"test": "node --test",
|
|
21
|
-
"check": "node --check bin/sd.mjs && node --check server/server.cjs && node --check server/buffer-calendar.cjs && node --check server/composio-email.cjs && node --check server/attachments.cjs && node --check server/write-control.cjs && node --check server/secretary-store.cjs && node --check server/secretary-collector.cjs && node --check server/secretary-latest.cjs && node --check server/secretary-approvals.cjs && node --check server/secretary-push.cjs && node --check server/secretary-rules.cjs && node --check server/secretary-shadow-gate.cjs && node --check server/secretary-vault-importer.cjs && node --check server/secretary-inspiration.cjs && node --check server/linkedin-archive-importer.cjs && node --check server/voice-profile.cjs && node --check server/campaign-store.cjs && node --check server/campaign-runner.cjs && node --check server/withdrawal-store.cjs && node --check server/post-scheduler.cjs && node --check server/message-scheduler.cjs && node --check scripts/import-linkedin-archive.mjs && node --check scripts/import-secretary-vault-snapshot.mjs && node --check scripts/rebuild-secretary-actors.mjs && node --check scripts/extract-conversation-inspiration.mjs && node --check bin/signaldash.js && node --check lib/cli.js && node --check lib/config-file.js && node --check lib/mcp.js && node --check lib/rate-guard.js && node --check lib/secrets.js && node --check lib/unipile.js"
|
|
21
|
+
"check": "node --check bin/sd.mjs && node --check server/server.cjs && node --check server/content-pipeline.cjs && node --check server/buffer-calendar.cjs && node --check server/composio-email.cjs && node --check server/attachments.cjs && node --check server/write-control.cjs && node --check server/secretary-store.cjs && node --check server/secretary-collector.cjs && node --check server/secretary-latest.cjs && node --check server/secretary-approvals.cjs && node --check server/secretary-push.cjs && node --check server/secretary-rules.cjs && node --check server/secretary-shadow-gate.cjs && node --check server/secretary-vault-importer.cjs && node --check server/secretary-inspiration.cjs && node --check server/linkedin-archive-importer.cjs && node --check server/voice-profile.cjs && node --check server/campaign-store.cjs && node --check server/campaign-runner.cjs && node --check server/withdrawal-store.cjs && node --check server/post-scheduler.cjs && node --check server/message-scheduler.cjs && node --check scripts/import-linkedin-archive.mjs && node --check scripts/import-secretary-vault-snapshot.mjs && node --check scripts/rebuild-secretary-actors.mjs && node --check scripts/extract-conversation-inspiration.mjs && node --check scripts/render-content-board.mjs && node --check bin/signaldash.js && node --check lib/cli.js && node --check lib/config-file.js && node --check lib/mcp.js && node --check lib/rate-guard.js && node --check lib/secrets.js && node --check lib/unipile.js"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
24
24
|
"mcp",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: content
|
|
3
|
+
description: Turn Federico's raw content ideas into measured LinkedIn experiments through the SignalDash content pipeline. Load before writing any post for Federico.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Federico content pipeline
|
|
7
|
+
|
|
8
|
+
Use SignalDash states in order: `idee`, `gepaart`, `entwurf`, `freigegeben`,
|
|
9
|
+
`geplant`, `veroeffentlicht`, `gemessen`. Never skip a state. Never publish or
|
|
10
|
+
schedule without Federico approving the exact final text and time.
|
|
11
|
+
|
|
12
|
+
## Rules that decide the draft
|
|
13
|
+
|
|
14
|
+
- Category beats form. Lead with an event carrying real stakes. Introspection
|
|
15
|
+
either gets an event first or is labelled `bewusst_reichweitenschwach`.
|
|
16
|
+
- Word count is noise. Federico's top and bottom groups both average 10.2
|
|
17
|
+
words. Do not optimize length as a reach lever.
|
|
18
|
+
- Every post has exactly two inputs: Federico's own provable material and one
|
|
19
|
+
foreign library post with stored author, exact text, reactions, and comments.
|
|
20
|
+
An unproved arc is refused.
|
|
21
|
+
- Change exactly one tested lever per post. Store it with the draft.
|
|
22
|
+
- Measure at 24 and 72 hours against Federico's category baseline, never a
|
|
23
|
+
foreign benchmark: events with stakes 139 to 435 reactions, introspection 7
|
|
24
|
+
to 39.
|
|
25
|
+
- Treat question versus opinion endings and reply timing as hypotheses, not
|
|
26
|
+
rules. A test changes one of them while the rest stays stable.
|
|
27
|
+
|
|
28
|
+
## Operating flow
|
|
29
|
+
|
|
30
|
+
1. Read `sd_content_pipeline_list` and `sd_inspiration_list`.
|
|
31
|
+
2. Pair the idea with one evidenced unused arc using
|
|
32
|
+
`sd_content_pipeline_pair`.
|
|
33
|
+
3. Draft in Federico's raw voice. Call `sd_content_pipeline_draft` with one
|
|
34
|
+
category and one lever. For `innenschau`, pass the exact opening event as
|
|
35
|
+
`category_evidence` or mark it `bewusst_reichweitenschwach`. SignalDash
|
|
36
|
+
verifies the opening and renders either choice. The server then runs
|
|
37
|
+
`/root/secretary-build/fact_gate.py`. A refusal stops the flow.
|
|
38
|
+
4. Call `sd_content_pipeline_preview`. Send its exact PNG to the returned
|
|
39
|
+
Secretary `chat_id` through the existing WhatsApp read and attachment path.
|
|
40
|
+
Do not send a link or file reference. Federico replies yes, no, or with one
|
|
41
|
+
correction.
|
|
42
|
+
5. Record the response with `sd_content_pipeline_decide`, including the exact
|
|
43
|
+
WhatsApp `decision_message_id`. A correction returns the item to `gepaart`,
|
|
44
|
+
stays visible on the item, and requires a new fact-gated draft and image.
|
|
45
|
+
6. Before planning, call `li_scheduled_posts` and inspect SignalDash, Buffer,
|
|
46
|
+
and native LinkedIn completeness. Create the exact approved schedule only
|
|
47
|
+
through `li_draft_post` with the exact `content_pipeline_id`. SignalDash
|
|
48
|
+
binds it in the same request only after the preflight passes. Same-day
|
|
49
|
+
conflicts, three posts in one week, and a post above baseline inside its
|
|
50
|
+
two-day breathing window refuse before a schedule row exists.
|
|
51
|
+
7. Record exact publication evidence, then read the live own post at 24 and 72
|
|
52
|
+
hours and pass its reactions, comments, impressions, and provider evidence
|
|
53
|
+
to `sd_content_pipeline_measure`. The evidence must name `li_my_posts`, the
|
|
54
|
+
exact post URN and capture time, and repeat the exact submitted metrics.
|
|
55
|
+
|
|
56
|
+
Federico-origin numbers that the fact gate does not know need one simple
|
|
57
|
+
confirmation and a dated source row in `/root/secretary-build/FAKTEN.md`.
|
|
58
|
+
Agent-added numbers without a source are removed, never rationalized.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: signaldash
|
|
3
|
-
description: Operate the user's SignalDash connection to LinkedIn, WhatsApp, and email. Use this skill whenever the user says or implies "check my LinkedIn", "who messaged me", "triage my inbox", "read the thread with X", "reply to X", "send this message", "send a connection request", "create an invitation batch", "run a campaign", "connect and then message them", "message them when they accept", "check invitations", "accept this invitation", "auto-accept my invitations", "check auto-accept status", "withdraw this invitation", "search my LinkedIn connections", "find people in my network", "find people beyond my network", "suppress this contact", "check contact state", "check my action budget", "check WhatsApp", "check my email", "draft a LinkedIn post", "who engaged with my post", "who liked or commented", or "export my LinkedIn connections". Covers durable installation, invite-based setup, local-first connection search, capped paid discovery, immutable human-approved invitation batches, the human-approved campaign loop that messages a person only after a proven acceptance and stops on any reply, opt-in rate-capped invitation auto-accept, exact contact state and suppression, authoritative LinkedIn action-budget status, safe reading, drafting, approved sends, exact invitation actions, post engagement analysis, and paced connection export.
|
|
3
|
+
description: Operate the user's SignalDash connection to LinkedIn, WhatsApp, and email. Use this skill whenever the user says or implies "check my LinkedIn", "who messaged me", "triage my inbox", "read the thread with X", "reply to X", "send this message", "start a chat", "create a group chat", "introduce these people", "send a connection request", "create an invitation batch", "run a campaign", "connect and then message them", "message them when they accept", "check invitations", "accept this invitation", "auto-accept my invitations", "check auto-accept status", "withdraw this invitation", "search my LinkedIn connections", "find people in my network", "find people beyond my network", "suppress this contact", "check contact state", "check my action budget", "check WhatsApp", "check my email", "draft a LinkedIn post", "who engaged with my post", "who liked or commented", or "export my LinkedIn connections". Covers durable installation, invite-based setup, exact human-approved direct and group chat creation, local-first connection search, capped paid discovery, immutable human-approved invitation batches, the human-approved campaign loop that messages a person only after a proven acceptance and stops on any reply, opt-in rate-capped invitation auto-accept, exact contact state and suppression, authoritative LinkedIn action-budget status, safe reading, drafting, approved sends, exact invitation actions, post engagement analysis, and paced connection export.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Operate SignalDash
|
|
@@ -370,11 +370,22 @@ Use the exact tool names and argument keys below. Limits are optional.
|
|
|
370
370
|
| `sd_inspiration_import` | `source` exact LinkedIn post URL or URN; optional source author, Federico note, purpose, content kind, credit fields, and format description | Migration/manual capture through Unipile's documented post retrieval endpoint. The primary capture path remains pasting a URL into the verified Secretary WhatsApp group. |
|
|
371
371
|
| `sd_inspiration_channel_configure` | exact `chat_id`, `provider_id`, `chat_name`, and `confirm:true` | Re-prove the connected WhatsApp account and exact group in the live chat list, then enable automatic URL capture and same-group acknowledgements. Never substitute a self-chat. |
|
|
372
372
|
| `sd_inspiration_channel_set` | `capture_enabled` and `confirm:true` | Disable or re-enable capture and acknowledgements through the database kill switch without a deploy. |
|
|
373
|
+
| `sd_content_pipeline_set` | `enabled` and `confirm:true` | Enable capture from this instant or disable the complete content pipeline through its database flag. It never publishes. |
|
|
374
|
+
| `sd_content_pipeline_list` | optional `state` and `limit` | Read every durable stage from idea through measured with zero provider calls. |
|
|
375
|
+
| `sd_content_pipeline_pair` | `id`, exact `arc_inspiration_id`, `confirm:true` | Bind Federico raw material to one complete unused foreign post carrying real author, text, reaction, and comment evidence. |
|
|
376
|
+
| `sd_content_pipeline_draft` | `id`, category, one `tested_lever`, exact `draft_text`; weak-category decision, exact opening `category_evidence`, and numeric claims when applicable | Verify an asserted event is the actual draft opening, then run the executable fact and authorship gates before a draft is stored. |
|
|
377
|
+
| `sd_content_pipeline_preview` | `id` | Re-run the fact gate and render the exact arc metrics, content input, and draft as a PNG for the guarded WhatsApp attachment path. |
|
|
378
|
+
| `sd_content_pipeline_decide` | `id`, `decision`, optional correction, exact WhatsApp `decision_message_id`, `confirm:true` | Record Federico's yes, no, or correction. A correction returns to paired state for a new fact-gated image. Yes approves but does not schedule or publish. |
|
|
379
|
+
| `sd_content_pipeline_bind_schedule` | `id`, `scheduled_post_id`, `scheduled_at`, `confirm:true` | Bind an existing approved SignalDash schedule after fresh SignalDash plus Buffer calendar, frequency, same-day, fact, and breathing gates. |
|
|
380
|
+
| `sd_content_pipeline_published` | `id`, exact post URN and publish time, `confirm:true` | Record publication evidence without publishing anything. |
|
|
381
|
+
| `sd_content_pipeline_measure` | `id`, 24 or 72 hours, live metrics, provider evidence | Compare the live result with Federico's category baseline and emit lever learning every five posts. |
|
|
382
|
+
| `sd_content_board` | no arguments | Render board HTML from the same pipeline and stored shared-calendar rows with zero provider calls. |
|
|
373
383
|
| `sd_secretary_approve` | `disposition_id` required; `payload_hash` required exact 64-character hash from latest; `confirm:true` required | Create one single-use, 15-minute receipt for the exact current stored draft. It derives the sender and executable payload from storage. |
|
|
374
384
|
| `sd_secretary_reject` | `disposition_id` required; `confirm:true` required | Reject one exact current stored draft. It sends nothing. |
|
|
375
385
|
| `li_list_chats` | `limit` integer 1-100, default 20; `cursor` optional; `search` optional string, max 200 characters; `max_scan` optional integer 1-500; `unread` optional boolean | Find recent LinkedIn chats, unread counts, and exact `chat_id` values. `unread:true` is forwarded to the provider as `unread=true`; SignalDash does not fetch a full chat page and filter it locally. Search accepts a unique stored display name, exact `public_id`, member id, or ordinary chat field. A stored name/public id is resolved to its verified member id before matching, so a provider row with `name:null` remains findable. Without an explicit `max_scan`, a zero-match search extends from 200 up to a hard 500-chat/five-page bound; explicit bounds remain exact. Read `scanned_chats`, `pages_fetched`, `scan_limit`, and `exhaustive` before concluding absence. The response also reports any resolved public/member ids. Any other key, including `text` and `member_id`, is refused with `unsupported_parameter` rather than accepted and ignored. |
|
|
376
386
|
| `li_read_messages` | `chat_id` required; `limit` 1-100, default 30 | Read one resolved LinkedIn conversation before summarizing, drafting, or sending. The response includes `current_watermark`, the resolved `chat`, and `sender_name` on each message where the exact sender identity can be resolved. |
|
|
377
387
|
| `li_mark_read` | `chat_id` required | Explicitly clear one LinkedIn chat's unread state. Reading never clears unread automatically. |
|
|
388
|
+
| `li_start_chat` | `member_ids` required array of 1-10 exact `ACo`, `ACw`, or `AE` provider member IDs; `text` required exact first message; `confirm`, `approval_hash`, and `dry_run:true` apply only after preview | Start one direct or group chat. Preview first, show the complete member-ID set and exact text, then repeat that exact payload with the returned approval hash only after explicit approval. SignalDash resolves every member live, blocks protected or suppressed contacts, returns an existing exact member-set chat without sending, and charges one thread-message plus aggregate action for a new chat regardless of member count. `dry_run:true` proves the full path up to the provider write without consuming approval or budget. |
|
|
378
389
|
| `li_send_message` | Manual path: `chat_id` and `text` required; `expected_watermark` optional exact 64-character watermark; `mark_read` optional boolean. Secretary path: `secretary_receipt_id` alone. | Send one approved LinkedIn reply after an immediate read of that exact chat. A Secretary receipt derives the exact stored chat and text and refuses every caller-supplied override. |
|
|
379
390
|
| `li_send_invitation` | `provider_id` required; `note` optional, max 300 exact characters; `confirm` optional, default false | First preview one exact target and note. The server verifies relationship, both invitation directions, and absence of an existing one-to-one chat. After exact approval, repeat the identical call with `confirm:true`; jitter completes before the final preflight and action reservation. |
|
|
380
391
|
| `li_invitations_received` | `limit` integer 1-100, default 50; `cursor` optional | List one bounded page of received invitations. This read authorizes only the exact returned invitation IDs for a later accept. |
|
|
@@ -406,6 +417,7 @@ Use the exact tool names and argument keys below. Limits are optional.
|
|
|
406
417
|
| `wa_mark_read` | `chat_id` required | Explicitly clear one WhatsApp chat's unread state. Reading never clears unread automatically. |
|
|
407
418
|
| `wa_get_attachment` | `chat_id`, `message_id`, `attachment_id` all required, max 500 characters each | Download one attachment of one message in a chat this account owns. Read the chat first: the exact `message_id` and `attachment_id` come from `wa_read_messages`. Returns the stored path on the SignalDash host, mimetype, byte size and sha256. |
|
|
408
419
|
| `wa_transcribe_voice` | `chat_id`, `message_id`, `attachment_id` all required, max 500 characters each; `backend` optional, exactly `gemini` or `whisper` | Turn one WhatsApp voice note into text through SignalDash instead of fetching provider bytes yourself. Always read the returned `backend`: `gemini` is the accurate default, `whisper-small` is the weak local fallback and mangles German with English terms mixed in, and a fallback also carries `fallback_reason`. Non-audio attachments are refused with `415 not_audio`; an unknown backend with `400 unknown_backend`; a transcription that exceeds its time limit returns `504 transcription_timeout` with the stored audio path. |
|
|
420
|
+
| `wa_start_chat` | `member_ids` required array of 1-10 exact `@s.whatsapp.net` or `@lid` provider member IDs; `text` required exact first message; `confirm`, `approval_hash`, and `dry_run:true` apply only after preview | Start one direct or group chat under the same exact approval, live member resolution, protected-contact, duplicate-set, and readback contract as LinkedIn. If an `@lid` resolves to another canonical ID, repeat the preview with the returned `resolved_member_id`; SignalDash never substitutes an unapproved identity. A new chat spends one WhatsApp/email send action regardless of member count. An existing exact member-set chat is returned without sending. |
|
|
409
421
|
| `wa_send_message` | `chat_id` required; `text` optional only when a file is attached, max 5000 characters; `attachments` optional array of up to 4 exact `{filename, content_type, content_base64}` files, at most 16 MiB per file and 16 MiB per message, types `image/png`, `image/jpeg`, `image/webp`, `image/gif`, `application/pdf`, `text/csv`, `text/plain`, `application/json`, `application/zip`, xlsx; `expected_watermark` optional exact 64-character watermark; `mark_read` optional boolean, default false | Send one approved reply, one approved file, or both, in an existing WhatsApp conversation after an immediate re-read. Own outbound additions do not invalidate the read; inbound additions or mutations return `new_messages`, `changed_kind`, and `current_watermark`. Pass `expected_watermark` to bind the send to the exact reviewed state and `mark_read:true` only when the approved workflow also calls for clearing unread after the confirmed send. A file spends the same daily send budget and is recorded the same way as a text message; there is no separate attachment budget. A call carrying neither text nor an attachment is refused with `400 text_or_attachment_required`. Attachments are checked before anything is reserved, so a refusal costs no send: `400 unsupported_attachment_type`, `400 attachment_too_large`, `400 attachments_too_large`, `400 too_many_attachments`, `400 malformed_attachment_base64`, `400 invalid_attachment_filename`, and `413 request_too_large` when the whole body is too big to read. Nothing is ever truncated or dropped silently. The same caption with the same file is refused as `409 duplicate_send`; the same caption with a different file is a different message and goes through. If a send times out or the provider never confirms it, the message may still have been delivered: an identical retry is refused with `409 send_outcome_unknown`. Read the chat AGAIN, and ONLY if the message is genuinely absent, resend the identical payload with `confirm_resend:true`. The re-read is enforced, not advisory: a `confirm_resend` whose most recent read of that chat predates the failed attempt is refused with `428 reread_after_failed_send_required`, because a read taken before the attempt cannot show whether the message arrived. A re-read failure remains `502 thread_preflight_unavailable`, and a legacy proof with no watermark remains `428 read_before_send_required`; neither costs send budget. LinkedIn messages carry text only. |
|
|
410
422
|
| `wa_delete_message` | `chat_id`, `message_id` both required, max 500 characters each | Retract one message THIS account sent, in a chat this account owns. The exact `message_id` comes from `wa_read_messages`. Irreversible and never retried: someone else's message is refused with `403 message_not_own`, a message outside this chat with `403 message_forbidden`, and a delete already recorded for this exact chat and message with `409 duplicate_delete`. Deletes spend their own daily budget, so `429 rate_limit_exceeded` here never means you are out of sends. WhatsApp applies its own time and role limits to deleting for everyone and can answer successfully without removing anything, so read the chat again to confirm the message is gone. |
|
|
411
423
|
| `wa_delete_messages` | `messages` required array of 1-200 exact `{chat_id, message_id}` objects | Retract several messages this account sent. Same ownership, budget and audit path as `wa_delete_message`, executed strictly one at a time with a pause between them, never in parallel. Always read the per-entry `ok`, `code` and `error`: a partial result is normal. Entries the batch never reached before its time limit come back with `skipped:true` and `code:batch_deadline`, and were not attempted; resend exactly those to resume. |
|
|
@@ -419,7 +431,7 @@ Use the exact tool names and argument keys below. Limits are optional.
|
|
|
419
431
|
| `li_like_comment` | `post_id`, `parent_comment_id`, and `comment_id` required; `expected_watermark` optional exact 64-character watermark | Like once one exact inbound comment on this sender's own post after `li_post_comments`. SignalDash re-proves post ownership, the unchanged comment, its author and parent, no own like, sender generation, budget, and provider health immediately before writing. A 2xx is not success until bounded readback finds exactly one own like. |
|
|
420
432
|
| `li_delete_message` | `chat_id`, `message_id`, and `confirm:true` required | Remediate one exact own LinkedIn message within 60 minutes of sending. SignalDash proves account, exact chat, own authorship, timestamp eligibility, a separate remediation budget, and post-delete absence. This cannot undo delivery, reading, or notifications and never relaxes a send gate. |
|
|
421
433
|
| `li_delete_comment` | `post_id`, `comment_id`, and `confirm:true` required | Currently unavailable: the fixture-tested Unipile v2 wrapper is held at database capability state `untested` until live compatibility is proved against Federico's own removable comment. When enabled it proves own comment identity and readback. Deletion is remediation, not rollback. |
|
|
422
|
-
| `li_draft_post` | `text` required, max 3000; `publish` optional; `scheduled_at` optional offset-qualified ISO date-time; `mentions` optional array of up to 20 exact `{name,profile_id}` objects; `attachments` optional array of up to 4 exact `{filename,content_type,content_base64}` images; `first_comment` optional, max 1250 | Create a server-confirmed draft, publish now, or persist an exact future LinkedIn post and its approved first comment.
|
|
434
|
+
| `li_draft_post` | `text` required, max 3000; `publish` optional; `scheduled_at` optional offset-qualified ISO date-time; `content_pipeline_id` required for scheduling while that user's pipeline is enabled; `mentions` optional array of up to 20 exact `{name,profile_id}` objects; `attachments` optional array of up to 4 exact `{filename,content_type,content_base64}` images; `first_comment` optional, max 1250 | Create a server-confirmed draft, publish now, or persist an exact future LinkedIn post and its approved first comment. An enabled pipeline runs its fact, exact-text, shared-calendar, frequency, and breathing preflight before the schedule row is created. |
|
|
423
435
|
| `li_set_scheduled_post_first_comment` | `id` required UUID; `first_comment` required, max 1250; `confirm:true` required | Attach one exact approved first comment to a scheduled post. SignalDash publishes it through the same connected account after the post and never republishes the post if the comment fails. |
|
|
424
436
|
| `li_scheduled_posts` | no arguments | Read one shared content-calendar view across SignalDash and the configured Buffer LinkedIn channel. Inspect `completeness` and every `sources.*.state` before treating absence as an empty calendar. Native LinkedIn scheduled posts and drafts are invisible because Unipile has no documented read route for them; SignalDash does not use raw Voyager routes or linkedin.com browser access. An empty `items` array proves only that the visible sources returned no entries. |
|
|
425
437
|
| `li_cancel_scheduled_post` | `id` required UUID; `confirm:true` required | Cancel one exact post while it is still scheduled. It cannot recall an executing or published post. |
|
|
@@ -436,6 +448,7 @@ sd_secretary_push_set({"enabled":false,"confirm":true})
|
|
|
436
448
|
sd_inspiration_list({})
|
|
437
449
|
li_list_chats({"limit":20})
|
|
438
450
|
li_read_messages({"chat_id":"chat_li_7f3a","limit":20})
|
|
451
|
+
li_start_chat({"member_ids":["ACoAAExactMemberOne","ACoAAExactMemberTwo"],"text":"Amina, meet Jo. I think you two have a lot to compare."})
|
|
439
452
|
li_send_message({"chat_id":"chat_li_7f3a","text":"Yes. I’ll send it this afternoon."})
|
|
440
453
|
li_send_invitation({"provider_id":"ACoAAExactMember","note":"Hi Amina, I enjoyed your post on agent safety."})
|
|
441
454
|
li_send_invitation({"provider_id":"ACoAAExactMember","note":"Hi Amina, I enjoyed your post on agent safety.","confirm":true})
|
|
@@ -467,6 +480,7 @@ wa_list_chats({"limit":20})
|
|
|
467
480
|
wa_read_messages({"chat_id":"chat_wa_91b2","limit":20})
|
|
468
481
|
wa_get_attachment({"chat_id":"chat_wa_91b2","message_id":"msg_wa_5c71","attachment_id":"att_wa_0a33"})
|
|
469
482
|
wa_transcribe_voice({"chat_id":"chat_wa_91b2","message_id":"msg_wa_5c71","attachment_id":"att_wa_0a33"})
|
|
483
|
+
wa_start_chat({"member_ids":["4915111111111@s.whatsapp.net","202700000000000@lid"],"text":"Amina, meet Jo. I think you two have a lot to compare."})
|
|
470
484
|
wa_send_message({"chat_id":"chat_wa_91b2","text":"16:30 works. See you then."})
|
|
471
485
|
wa_send_message({"chat_id":"chat_wa_91b2","text":"Q3 numbers attached.","attachments":[{"filename":"q3-arr.csv","content_type":"text/csv","content_base64":"<base64>"}]})
|
|
472
486
|
wa_delete_message({"chat_id":"chat_wa_91b2","message_id":"msg_wa_5c71"})
|