@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.
@@ -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-Beuhzjy_.js"></script>
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 mode = job.isolation?.postToMainMode ?? "summary";
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, driver rating, vehicle type, estimated journey time\n` +
261
- ` Do NOT reveal driver name, phone, or plate — those are gated by payment\n`);
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, note it and compile offers when ready.\n` +
431
- `If the driver is declining, update their status and active negotiation index.\n`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.17.0",
3
+ "version": "1.17.5",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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. Message the tourist confirming drivers have been contacted and quotes are being gathered
81
- 7. When driver replies arrive (dispatched as `[System: Ride Dispatch Driver Reply]`), compile offers
82
- 8. Message the tourist with up to 3 competing offers fare, rating, vehicle type, journey time. Do NOT reveal driver name, phone, or plate (gated by payment)
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 with: driver name, phone, vehicle details, plate, and pickup PIN
101
- 4. Message the driver with: passenger name, pickup time/location, fare, and QR code URL
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
- Process the driver's message in the context of the ongoing negotiation. If it's a fare quote, note it. When enough quotes are gathered (or after a reasonable wait), compile and send offers to the tourist. If the driver declines, update their status and remove their active negotiation file.
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: +XXXXXXXXXXX
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 (injected by the operations agent), 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.
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: +XXXXXXXXXXX
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 send it directly to the tourist. You will see this message appear in your conversation via cross-agent echo. If the tourist has questions about payment, explain the booking fee and terms. Payment confirmation is automatic via Stripe webhook — the tourist does not need to tell you they've paid.
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 (the operations agent will inject driver details and pickup PIN into the conversation), acknowledge the details to the tourist. 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."
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