@rubytech/taskmaster 1.17.0 → 1.17.5
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/dist/agents/workspace-migrations.js +61 -0
- package/dist/build-info.json +3 -3
- package/dist/config/agent-tools-reconcile.js +92 -0
- package/dist/control-ui/assets/{index-Beuhzjy_.js → index-koe4eKhk.js} +4 -4
- package/dist/control-ui/assets/index-koe4eKhk.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/cron/service/timer.js +5 -1
- package/dist/gateway/server-methods/apikeys.js +2 -0
- package/dist/gateway/server.impl.js +30 -1
- package/dist/hooks/bundled/ride-dispatch/handler.js +23 -9
- package/package.json +1 -1
- package/skills/stripe/SKILL.md +29 -0
- package/skills/stripe/references/setup-guide.md +156 -0
- package/templates/beagle-zanzibar/agents/admin/AGENTS.md +16 -8
- package/templates/beagle-zanzibar/agents/public/AGENTS.md +10 -5
- package/dist/control-ui/assets/index-Beuhzjy_.js.map +0 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Taskmaster Control</title>
|
|
7
7
|
<meta name="color-scheme" content="dark light" />
|
|
8
8
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
9
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-koe4eKhk.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-XqRo9tNW.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
@@ -103,7 +103,11 @@ export async function executeJob(state, job, nowMs, opts) {
|
|
|
103
103
|
}
|
|
104
104
|
if (job.sessionTarget === "isolated") {
|
|
105
105
|
const prefix = job.isolation?.postToMainPrefix?.trim() || "Cron";
|
|
106
|
-
const
|
|
106
|
+
const configuredMode = job.isolation?.postToMainMode ?? "summary";
|
|
107
|
+
// When delivery was skipped (best-effort, no external channel configured),
|
|
108
|
+
// promote to "full" so the admin sees the actual report in their main chat
|
|
109
|
+
// instead of a delivery-error summary.
|
|
110
|
+
const mode = status === "skipped" && outputText ? "full" : configuredMode;
|
|
107
111
|
let body = (summary ?? err ?? status).trim();
|
|
108
112
|
if (mode === "full") {
|
|
109
113
|
// Prefer full agent output if available; fall back to summary.
|
|
@@ -27,6 +27,8 @@ const PROVIDER_CATALOG = [
|
|
|
27
27
|
{ id: "brave", name: "Brave", category: "Web Search" },
|
|
28
28
|
{ id: "elevenlabs", name: "ElevenLabs", category: "Voice" },
|
|
29
29
|
{ id: "brevo", name: "Brevo", category: "Email" },
|
|
30
|
+
{ id: "stripe", name: "Stripe", category: "Payments" },
|
|
31
|
+
{ id: "stripe_webhook_secret", name: "Stripe Webhook Secret", category: "Payments" },
|
|
30
32
|
];
|
|
31
33
|
const VALID_PROVIDER_IDS = new Set(PROVIDER_CATALOG.map((p) => p.id));
|
|
32
34
|
export const apikeysHandlers = {
|
|
@@ -10,7 +10,7 @@ import { CONFIG_PATH_TASKMASTER, isNixMode, loadConfig, migrateLegacyConfig, rea
|
|
|
10
10
|
import { VERSION } from "../version.js";
|
|
11
11
|
import { isDiagnosticsEnabled } from "../infra/diagnostic-events.js";
|
|
12
12
|
import { logAcceptedEnvOption } from "../infra/env.js";
|
|
13
|
-
import { reconcileAgentContactTools, reconcileBeaglePublicTools, reconcileQrGenerateTool, reconcileStaleToolEntries, } from "../config/agent-tools-reconcile.js";
|
|
13
|
+
import { reconcileAgentContactTools, reconcileBeaglePublicTools, reconcileControlPanelTools, reconcileQrGenerateTool, reconcileSkillReadTool, reconcileStaleToolEntries, } from "../config/agent-tools-reconcile.js";
|
|
14
14
|
import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
|
|
15
15
|
import { clearAgentRunContext, onAgentEvent } from "../infra/agent-events.js";
|
|
16
16
|
import { onHeartbeatEvent } from "../infra/heartbeat-events.js";
|
|
@@ -198,6 +198,35 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
198
198
|
log.warn(`gateway: failed to persist qr_generate tool reconciliation: ${String(err)}`);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
// Upgrade admin agents from individual control-panel tools to group:control-panel.
|
|
202
|
+
// Agents set up before the group existed miss tools added to it later (e.g. logs_read).
|
|
203
|
+
const cpReconcile = reconcileControlPanelTools({ config: configSnapshot.config });
|
|
204
|
+
if (cpReconcile.changes.length > 0) {
|
|
205
|
+
try {
|
|
206
|
+
await writeConfigFile(cpReconcile.config);
|
|
207
|
+
configSnapshot = await readConfigFileSnapshot();
|
|
208
|
+
log.info(`gateway: reconciled control-panel tools:\n${cpReconcile.changes
|
|
209
|
+
.map((entry) => `- ${entry}`)
|
|
210
|
+
.join("\n")}`);
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
log.warn(`gateway: failed to persist control-panel tools reconciliation: ${String(err)}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Add skill_read to admin agents that predate its addition to ACCOUNT_ADMIN_TOOLS.
|
|
217
|
+
const skillReadReconcile = reconcileSkillReadTool({ config: configSnapshot.config });
|
|
218
|
+
if (skillReadReconcile.changes.length > 0) {
|
|
219
|
+
try {
|
|
220
|
+
await writeConfigFile(skillReadReconcile.config);
|
|
221
|
+
configSnapshot = await readConfigFileSnapshot();
|
|
222
|
+
log.info(`gateway: reconciled skill_read tool:\n${skillReadReconcile.changes
|
|
223
|
+
.map((entry) => `- ${entry}`)
|
|
224
|
+
.join("\n")}`);
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
log.warn(`gateway: failed to persist skill_read tool reconciliation: ${String(err)}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
201
230
|
// Stamp config with running version on startup so upgrades keep the stamp current.
|
|
202
231
|
const storedVersion = configSnapshot.config.meta?.lastTouchedVersion;
|
|
203
232
|
if (configSnapshot.exists && storedVersion !== VERSION) {
|
|
@@ -253,12 +253,15 @@ function buildTripRequestInstruction(fields, resolvedAccountId) {
|
|
|
253
253
|
`4. For each selected driver, update their status to awaiting_response via memory_write\n` +
|
|
254
254
|
`5. Write shared/active-negotiations/{driver-phone}.md with job_id: ${jobId} for each driver\n` +
|
|
255
255
|
`6. Message each driver in Swahili with the route details, pickup time, passengers, and job ID [${jobId}]\n` +
|
|
256
|
-
` Use the message tool with accountId: "${accountId}"\n` +
|
|
256
|
+
` Use the message tool with channel: "whatsapp", accountId: "${accountId}"\n` +
|
|
257
257
|
`7. Message the tourist at ${touristPhone} confirming you've contacted drivers and are waiting for quotes\n` +
|
|
258
|
-
` Use the message tool with accountId: "${accountId}"\n` +
|
|
258
|
+
` Use the message tool with channel: "whatsapp", accountId: "${accountId}"\n` +
|
|
259
259
|
`8. When drivers reply with quotes (dispatched to this session), compile the offers\n` +
|
|
260
|
-
`9. Message the tourist with up to 3 competing offers: fare,
|
|
261
|
-
`
|
|
260
|
+
`9. Message the tourist at ${touristPhone} with up to 3 competing offers: fare, vehicle type, driver rating, estimated journey time\n` +
|
|
261
|
+
` Use the message tool with channel: "whatsapp", accountId: "${accountId}"\n` +
|
|
262
|
+
` Cross-agent echo will relay the message to the tourist's active session automatically\n` +
|
|
263
|
+
` Do NOT reveal driver name, phone, or plate — those are gated by payment\n` +
|
|
264
|
+
` NOTE: Do NOT write a dispatch file for the offers — message the tourist directly\n`);
|
|
262
265
|
}
|
|
263
266
|
function buildBookingConfirmInstruction(fields, resolvedAccountId) {
|
|
264
267
|
const jobId = fields.job_id ?? "UNKNOWN";
|
|
@@ -278,7 +281,7 @@ function buildBookingConfirmInstruction(fields, resolvedAccountId) {
|
|
|
278
281
|
`1. Load the stripe skill and generate a Checkout Session for the booking fee\n` +
|
|
279
282
|
` Set metadata: booking_id="${jobId}", tourist_phone="${touristPhone}"\n` +
|
|
280
283
|
`2. Message the tourist at ${touristPhone} with the payment link and booking terms\n` +
|
|
281
|
-
` Use the message tool with accountId: "${accountId}"\n` +
|
|
284
|
+
` Use the message tool with channel: "whatsapp", accountId: "${accountId}"\n` +
|
|
282
285
|
`3. Record the booking details in shared/bookings/${jobId}.md via memory_write\n` +
|
|
283
286
|
`4. Clear the active negotiation files for drivers NOT selected (delete their shared/active-negotiations/{phone}.md)\n`);
|
|
284
287
|
}
|
|
@@ -293,9 +296,9 @@ function buildPaymentConfirmedInstruction(params) {
|
|
|
293
296
|
`1. Read the booking record at shared/bookings/${bookingId}.md for driver details\n` +
|
|
294
297
|
`2. Generate the pickup PIN and QR code (see references/pin-qr.md)\n` +
|
|
295
298
|
`3. Message the tourist at ${touristPhone} with: driver name, phone, vehicle details, plate, and pickup PIN\n` +
|
|
296
|
-
` Use the message tool with accountId: "${accountId}"\n` +
|
|
299
|
+
` Use the message tool with channel: "whatsapp", accountId: "${accountId}"\n` +
|
|
297
300
|
`4. Message the driver with: passenger name, pickup time/location, fare, and QR code URL\n` +
|
|
298
|
-
` Use the message tool with accountId: "${accountId}"\n` +
|
|
301
|
+
` Use the message tool with channel: "whatsapp", accountId: "${accountId}"\n` +
|
|
299
302
|
`5. Update the booking record status to "confirmed"\n` +
|
|
300
303
|
`6. Clear the active negotiation file for the driver (shared/active-negotiations/{phone}.md)\n`);
|
|
301
304
|
}
|
|
@@ -337,6 +340,10 @@ async function handleMemoryAdd(event) {
|
|
|
337
340
|
return;
|
|
338
341
|
}
|
|
339
342
|
const fields = parseDispatchFile(content);
|
|
343
|
+
// Normalize: public agents may write tourist_id instead of tourist_phone
|
|
344
|
+
if (!fields.tourist_phone && fields.tourist_id) {
|
|
345
|
+
fields.tourist_phone = fields.tourist_id;
|
|
346
|
+
}
|
|
340
347
|
// Load config (memory:add events don't carry cfg)
|
|
341
348
|
let cfg;
|
|
342
349
|
try {
|
|
@@ -427,8 +434,15 @@ async function handleDriverReply(event) {
|
|
|
427
434
|
`Driver phone: ${senderPhone}\n` +
|
|
428
435
|
`Message: ${text}\n\n` +
|
|
429
436
|
`Process this reply in the context of the ongoing negotiation for ${jobId}.\n` +
|
|
430
|
-
`If this is a fare quote
|
|
431
|
-
`
|
|
437
|
+
`If this is a fare quote:\n` +
|
|
438
|
+
` - Record the quote in the driver's memory profile\n` +
|
|
439
|
+
` - When all expected quotes are in (or after a reasonable wait), compile the offers\n` +
|
|
440
|
+
` - Message the tourist directly using the message tool with the compiled offers\n` +
|
|
441
|
+
` The tourist phone is in the earlier trip-request message in this session\n` +
|
|
442
|
+
` Use the message tool with channel: "whatsapp", accountId: "${accountId}"\n` +
|
|
443
|
+
` Do NOT write a dispatch file — use the message tool to send the offers directly\n` +
|
|
444
|
+
` Cross-agent echo will relay it to the tourist's active session automatically\n` +
|
|
445
|
+
`If the driver is declining, update their status in memory and delete their shared/active-negotiations/{phone-digits}.md file.\n`;
|
|
432
446
|
console.log(`[ride-dispatch] Driver reply from ${senderPhone} for ${jobId}, dispatching to admin agent "${adminAgentId}"`);
|
|
433
447
|
// Fire and forget — suppress is already set, caller will skip processForRoute
|
|
434
448
|
dispatchToAdmin({
|
package/package.json
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stripe
|
|
3
|
+
description: Guide users through getting Stripe API credentials — the secret key for payment links and the webhook signing secret for automatic post-payment workflows.
|
|
4
|
+
metadata: {"taskmaster":{"emoji":"💳"}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Stripe Setup
|
|
8
|
+
|
|
9
|
+
Walks users through creating a Stripe account (or logging in to an existing one), obtaining their secret API key, setting up a webhook endpoint, and saving both credentials. One guided flow covers both keys — the secret key enables payment link creation; the webhook signing secret enables automatic post-payment workflow dispatch without any manual user step.
|
|
10
|
+
|
|
11
|
+
## When to activate
|
|
12
|
+
|
|
13
|
+
- User asks about Stripe setup or connecting Stripe to Taskmaster
|
|
14
|
+
- Control panel shows Stripe or Stripe Webhook Secret as unconfigured and payment features are in use
|
|
15
|
+
- BOOTSTRAP detects missing Stripe key when payment link generation is needed
|
|
16
|
+
- User asks why payments aren't working or why post-payment automation isn't firing
|
|
17
|
+
|
|
18
|
+
## What it unlocks
|
|
19
|
+
|
|
20
|
+
- **Payment link generation** — create Stripe Checkout Sessions for collecting booking fees or other payments
|
|
21
|
+
- **Automatic post-payment dispatch** — Taskmaster receives `checkout.session.completed` webhook events and triggers follow-up workflows without requiring the customer to manually confirm payment
|
|
22
|
+
|
|
23
|
+
## References
|
|
24
|
+
|
|
25
|
+
| Task | When to use | Reference |
|
|
26
|
+
|------|-------------|-----------|
|
|
27
|
+
| Guided setup | User wants help getting both credentials | `references/setup-guide.md` |
|
|
28
|
+
|
|
29
|
+
Load the reference and follow its instructions.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Stripe — Guided Setup
|
|
2
|
+
|
|
3
|
+
Walk the user through getting both Stripe credentials: the secret API key (used to create payment links) and the webhook signing secret (used to verify incoming payment confirmation events). Guide with clear step-by-step instructions.
|
|
4
|
+
|
|
5
|
+
**Important:** Stripe's dashboard is dense and aimed at developers. The user only needs two things: their secret key and a webhook endpoint. Guide them directly to both — do not let them get lost in the product catalogue, reports, or analytics.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- User has a Stripe account, or is ready to create one
|
|
12
|
+
- Taskmaster's public URL — the user needs to know their hostname to construct the webhook endpoint URL (e.g. `https://taskmaster-19000.local:19000` or their public domain)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Step 1: Explain
|
|
17
|
+
|
|
18
|
+
Tell the user what you are setting up and why both keys are needed:
|
|
19
|
+
|
|
20
|
+
> "To take payments, Taskmaster needs two things from your Stripe account:
|
|
21
|
+
>
|
|
22
|
+
> 1. **Secret key** — lets Taskmaster create payment links on your behalf
|
|
23
|
+
> 2. **Webhook signing secret** — tells Taskmaster when a customer has actually paid, so it can automatically trigger follow-up actions (like releasing booking details) without waiting for the customer to say anything
|
|
24
|
+
>
|
|
25
|
+
> This takes about 5 minutes. I'll walk you through both."
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Step 2: Log in to Stripe
|
|
30
|
+
|
|
31
|
+
> "Go to **dashboard.stripe.com** and sign in. If you don't have an account yet, click **Start now** and create one — it's free to set up.
|
|
32
|
+
>
|
|
33
|
+
> Let me know when you're in the dashboard."
|
|
34
|
+
|
|
35
|
+
Wait for the user to confirm.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Step 3: Get the secret key
|
|
40
|
+
|
|
41
|
+
**Navigation: Dashboard → Developers → API keys**
|
|
42
|
+
|
|
43
|
+
> "In the left sidebar, click **Developers** (near the bottom), then click **API keys**.
|
|
44
|
+
>
|
|
45
|
+
> You'll see two keys:
|
|
46
|
+
> - **Publishable key** — starts with `pk_` — we don't need this one
|
|
47
|
+
> - **Secret key** — starts with `sk_` — this is the one we need
|
|
48
|
+
>
|
|
49
|
+
> Click **Reveal live key** (or **Reveal test key** if you're still testing). Copy the full key — it starts with `sk_live_` or `sk_test_` and is quite long. Send it to me."
|
|
50
|
+
|
|
51
|
+
Wait for the user to send the key.
|
|
52
|
+
|
|
53
|
+
Verify format (`sk_test_` or `sk_live_` prefix, 30+ characters). If it looks wrong:
|
|
54
|
+
|
|
55
|
+
> "That doesn't look quite right — the secret key should start with `sk_test_` (for test mode) or `sk_live_` (for production) and be at least 30 characters long. Can you check you've copied the **Secret key**, not the Publishable key?"
|
|
56
|
+
|
|
57
|
+
Once you have a valid key, save it:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
api_keys({ action: "set", provider: "stripe", apiKey: "<the key>" })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Confirm to the user:
|
|
64
|
+
|
|
65
|
+
> "Secret key saved. Now let's set up the webhook so Taskmaster gets notified automatically when a payment goes through."
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Step 4: Create the webhook endpoint
|
|
70
|
+
|
|
71
|
+
**Navigation: Dashboard → Developers → Webhooks → Add endpoint**
|
|
72
|
+
|
|
73
|
+
> "Still in the Developers section, click **Webhooks** in the left sidebar.
|
|
74
|
+
>
|
|
75
|
+
> Click **Add endpoint** (top-right of the page).
|
|
76
|
+
>
|
|
77
|
+
> In the **Endpoint URL** field, enter:
|
|
78
|
+
> `https://<your-taskmaster-host>/webhook/stripe`
|
|
79
|
+
>
|
|
80
|
+
> Replace `<your-taskmaster-host>` with your Taskmaster address — for example `taskmaster-19000.local:19000` if you're on your local network, or your public domain if Taskmaster is accessible from the internet.
|
|
81
|
+
>
|
|
82
|
+
> Under **Select events**, click **+ Select events**, search for `checkout.session.completed`, tick it, and click **Add events**.
|
|
83
|
+
>
|
|
84
|
+
> Then click **Add endpoint** to save.
|
|
85
|
+
>
|
|
86
|
+
> Let me know when you've done that."
|
|
87
|
+
|
|
88
|
+
If the user is unsure of their Taskmaster address:
|
|
89
|
+
|
|
90
|
+
> "Your Taskmaster address is the URL you use to access the control panel — just the host and port, without any path. For example, if your control panel is at `http://taskmaster-19000.local:19000`, your webhook URL would be `https://taskmaster-19000.local:19000/webhook/stripe`."
|
|
91
|
+
|
|
92
|
+
Wait for the user to confirm the endpoint is created.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Step 5: Get the webhook signing secret
|
|
97
|
+
|
|
98
|
+
> "After creating the endpoint, Stripe shows a **Signing secret** — it starts with `whsec_`. Click **Reveal** to show it, then copy it and send it to me.
|
|
99
|
+
>
|
|
100
|
+
> If you've already navigated away, click on the endpoint you just created in the Webhooks list — the signing secret is shown on that endpoint's detail page."
|
|
101
|
+
|
|
102
|
+
Wait for the user to send the signing secret.
|
|
103
|
+
|
|
104
|
+
Verify format (`whsec_` prefix). If it looks wrong:
|
|
105
|
+
|
|
106
|
+
> "The webhook signing secret should start with `whsec_` — that's different from the API key. Click on your webhook endpoint in the Webhooks list and look for the **Signing secret** section."
|
|
107
|
+
|
|
108
|
+
Once you have a valid signing secret, save it:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
api_keys({ action: "set", provider: "stripe_webhook_secret", apiKey: "<the whsec key>" })
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Step 6: Confirm
|
|
117
|
+
|
|
118
|
+
> "Both Stripe credentials are saved:
|
|
119
|
+
>
|
|
120
|
+
> - **Secret key** — payment links can now be created
|
|
121
|
+
> - **Webhook signing secret** — Taskmaster will receive and verify payment confirmations automatically
|
|
122
|
+
>
|
|
123
|
+
> You're all set. As soon as a customer completes a payment, Taskmaster will be notified and can trigger follow-up actions immediately — no manual step needed from the customer."
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## If the user already has a Stripe account
|
|
128
|
+
|
|
129
|
+
Skip Step 2 (they're already logged in). Proceed from Step 3.
|
|
130
|
+
|
|
131
|
+
If they already have a secret key saved somewhere, skip to Step 4.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Test mode vs live mode
|
|
136
|
+
|
|
137
|
+
| | Test mode | Live mode |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| Key prefix | `sk_test_` | `sk_live_` |
|
|
140
|
+
| Webhooks | Use test webhook endpoints | Use live webhook endpoints |
|
|
141
|
+
| Payments | No real money moves | Real payments |
|
|
142
|
+
|
|
143
|
+
Start with test mode (`sk_test_`) if the user is still building or testing their flow. Switch to live keys when ready for real payments. Both key types are stored the same way — just replace the value in the API Keys panel.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Troubleshooting
|
|
148
|
+
|
|
149
|
+
| Problem | Solution |
|
|
150
|
+
|---------|----------|
|
|
151
|
+
| Can't find Developers menu | It's in the left sidebar, near the bottom. If you're on the Stripe home page, you may need to scroll down or look for a collapsed sidebar — click the hamburger or expand icon. |
|
|
152
|
+
| Secret key is hidden / "Restricted key" shown | You may be looking at a restricted key. Click **API keys** in the Developers section — the secret key is under **Standard keys**. |
|
|
153
|
+
| Can't find signing secret after creating endpoint | Click on the endpoint row in the Webhooks list. The signing secret is on the endpoint's detail page under **Signing secret**. |
|
|
154
|
+
| Webhook URL not accepted (must be HTTPS) | Stripe requires HTTPS for webhook endpoints. If Taskmaster is only on HTTP locally, use a tunnel tool like ngrok during development, or configure a TLS certificate on your Taskmaster host. |
|
|
155
|
+
| Payments confirmed in Stripe but Taskmaster isn't notified | Check the webhook endpoint's delivery log in Stripe (Developers → Webhooks → click endpoint → Recent deliveries). If deliveries are failing, the URL may be unreachable from the internet. |
|
|
156
|
+
| "I already had a webhook set up" | You can use the existing endpoint if it already points to `/webhook/stripe` on your host. Just copy the signing secret from its detail page. |
|
|
@@ -77,9 +77,12 @@ When you receive `[System: Ride Dispatch — Trip Request]`:
|
|
|
77
77
|
- Update their status to `awaiting_response` via `memory_write`
|
|
78
78
|
- Write `shared/active-negotiations/{driver-phone-digits}.md` with `job_id`, `driver_name`, and `contacted_at`
|
|
79
79
|
5. Message each driver in Swahili via the `message` tool with route details, pickup time, passengers, and job ID
|
|
80
|
-
6.
|
|
81
|
-
7.
|
|
82
|
-
|
|
80
|
+
6. When driver replies arrive (dispatched as `[System: Ride Dispatch — Driver Reply]`), compile offers
|
|
81
|
+
7. Message the tourist at `tourist_phone` using the `message` tool with `accountId` from this dispatch
|
|
82
|
+
- Include up to 3 offers: fare, vehicle type, driver rating, estimated journey time
|
|
83
|
+
- Do NOT reveal driver name, phone, or plate — those are gated by payment
|
|
84
|
+
- Cross-agent echo will relay the WhatsApp message to the tourist's active session automatically
|
|
85
|
+
- Do NOT write a dispatch file for the offers — message directly
|
|
83
86
|
|
|
84
87
|
### Booking Confirmation
|
|
85
88
|
|
|
@@ -87,18 +90,21 @@ When you receive `[System: Ride Dispatch — Booking Confirmation]`:
|
|
|
87
90
|
|
|
88
91
|
1. Load the `stripe` skill and generate a Checkout Session for the booking fee
|
|
89
92
|
- Set metadata: `booking_id`, `tourist_phone`, `account_id` (for webhook routing)
|
|
90
|
-
2. Message the tourist with the payment link and booking terms
|
|
93
|
+
2. Message the tourist at `tourist_phone` using the `message` tool with the payment link and booking terms
|
|
94
|
+
- Cross-agent echo relays the message to the tourist's active session automatically
|
|
91
95
|
3. Record booking details in `shared/bookings/{job-id}.md` via `memory_write`
|
|
96
|
+
- Include `tourist_phone` for post-payment messaging
|
|
92
97
|
4. Clear `shared/active-negotiations/{phone}.md` for drivers NOT selected
|
|
93
98
|
|
|
94
99
|
### Payment Confirmed
|
|
95
100
|
|
|
96
101
|
When you receive `[System: Ride Dispatch — Payment Confirmed]`:
|
|
97
102
|
|
|
98
|
-
1. Read the booking record at `shared/bookings/{job-id}.md` for driver details
|
|
103
|
+
1. Read the booking record at `shared/bookings/{job-id}.md` for driver details and `tourist_session_key`
|
|
99
104
|
2. Generate the pickup PIN and QR code (see `references/pin-qr.md`)
|
|
100
|
-
3. Message the tourist
|
|
101
|
-
|
|
105
|
+
3. Message the tourist at `tourist_phone` (from the booking record) using the `message` tool with driver details and pickup PIN
|
|
106
|
+
- Cross-agent echo relays to the tourist's active session automatically
|
|
107
|
+
4. Message the driver with passenger name, pickup time/location, fare, and QR code URL
|
|
102
108
|
5. Update the booking record status to `confirmed`
|
|
103
109
|
6. Clear the active negotiation file for the confirmed driver
|
|
104
110
|
|
|
@@ -106,7 +112,9 @@ When you receive `[System: Ride Dispatch — Payment Confirmed]`:
|
|
|
106
112
|
|
|
107
113
|
When you receive `[System: Ride Dispatch — Driver Reply]`:
|
|
108
114
|
|
|
109
|
-
|
|
115
|
+
1. If the driver is quoting a fare: record it in their memory profile (`memory_write` on `drivers/{name}.md`)
|
|
116
|
+
2. When all expected quotes are received (or after a reasonable wait): compile the offers and message the tourist directly using the `message` tool at the `tourist_phone` from the trip-request earlier in this session — do NOT write a dispatch file
|
|
117
|
+
3. If the driver is declining: update their status in memory to `idle` and delete their `shared/active-negotiations/{phone-digits}.md` file
|
|
110
118
|
|
|
111
119
|
### Active Negotiation Index
|
|
112
120
|
|
|
@@ -23,6 +23,7 @@ Before responding:
|
|
|
23
23
|
- Proactively look up contact details for people mentioned in system messages
|
|
24
24
|
|
|
25
25
|
If a user asks for information about another person, politely decline — it would violate strict security protocols.
|
|
26
|
+
**Declined conversations stay declined.** When you've correctly identified a message as misdirected or outside your scope and responded accordingly, that conversation is concluded. If the same sender follows up with requests of any kind, decline and end the conversation. Performing services for someone you've already turned away contradicts your own assessment and opens the service to general-purpose misuse.
|
|
26
27
|
|
|
27
28
|
---
|
|
28
29
|
|
|
@@ -54,6 +55,10 @@ The knowledge base is the single source of truth. If it doesn't cover what's bei
|
|
|
54
55
|
|
|
55
56
|
This is a **prescribed workflow**. When a tourist requests a ride, execute every step in order. Do not stop, skip, or defer any step. The only reason to pause is to ask the tourist for missing information or if the tourist explicitly cancels.
|
|
56
57
|
|
|
58
|
+
### Step 0 — Confirm identification
|
|
59
|
+
|
|
60
|
+
All users are verified before they reach you — WhatsApp users by their phone number, webchat users by OTP verification. The tourist's phone number is their session identifier. Use it as `tourist_phone` in dispatch files.
|
|
61
|
+
|
|
57
62
|
### Step 1 — Capture the request
|
|
58
63
|
|
|
59
64
|
Gather: pickup location, destination, date/time, number of passengers, luggage, special requests. If the tourist gave everything in one message, proceed immediately. If anything is missing, ask — then resume from Step 2 when they reply.
|
|
@@ -74,7 +79,7 @@ Write a dispatch file via `memory_write` to `shared/dispatch/{job-id}-trip-reque
|
|
|
74
79
|
# Dispatch: Trip Request
|
|
75
80
|
job_id: BGL-XXXX
|
|
76
81
|
phase: trip-request
|
|
77
|
-
tourist_phone:
|
|
82
|
+
tourist_phone: [the tourist's phone number — from WhatsApp session or OTP-verified phone]
|
|
78
83
|
tourist_name: [name if given]
|
|
79
84
|
pickup: [pickup location]
|
|
80
85
|
destination: [destination]
|
|
@@ -93,7 +98,7 @@ After writing the dispatch file, tell the tourist: "I'm reaching out to our driv
|
|
|
93
98
|
|
|
94
99
|
### Step 6 — Present offers
|
|
95
100
|
|
|
96
|
-
When driver offers appear in your conversation
|
|
101
|
+
When driver offers appear in your conversation as a `[System: Ride Dispatch — Driver Offers]` message, present up to 3 competing offers to the tourist: fare, driver rating, vehicle type, estimated journey time. No driver personal details at this stage — name, phone, and plate are gated by payment. See `references/ride-matching.md` for formatting.
|
|
97
102
|
|
|
98
103
|
### Step 7 — Confirm booking
|
|
99
104
|
|
|
@@ -103,7 +108,7 @@ When the tourist chooses an offer, confirm the details and write a booking confi
|
|
|
103
108
|
# Dispatch: Booking Confirmation
|
|
104
109
|
job_id: BGL-XXXX
|
|
105
110
|
phase: booking-confirm
|
|
106
|
-
tourist_phone:
|
|
111
|
+
tourist_phone: [same phone used in trip-request]
|
|
107
112
|
tourist_name: [name]
|
|
108
113
|
driver_name: [selected driver name from offer]
|
|
109
114
|
driver_phone: [selected driver phone from offer]
|
|
@@ -113,11 +118,11 @@ account_id: [your account ID]
|
|
|
113
118
|
|
|
114
119
|
### Step 8 — Payment
|
|
115
120
|
|
|
116
|
-
The operations agent will generate a Stripe payment link and
|
|
121
|
+
The operations agent will generate a Stripe payment link and relay it to your conversation as a `[System: Ride Dispatch — Payment Link]` message. Present the payment link and terms to the tourist. If the tourist has questions about payment, explain the booking fee. Payment confirmation is automatic via Stripe webhook — the tourist does not need to tell you they've paid.
|
|
117
122
|
|
|
118
123
|
### Step 9 — Post-payment
|
|
119
124
|
|
|
120
|
-
Once payment is confirmed
|
|
125
|
+
Once payment is confirmed, the operations agent will relay driver details and the pickup PIN to your conversation as a `[System: Ride Dispatch — Booking Complete]` message. Present the driver details to the tourist and explain the PIN verification process: "Your driver has a QR code. You have the PIN. Scan the QR or ask the driver to quote your PIN — works without internet."
|
|
121
126
|
|
|
122
127
|
### Step 10 — Record and follow up
|
|
123
128
|
|