@rubytech/taskmaster 1.14.2 → 1.16.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.
Files changed (50) hide show
  1. package/dist/agents/apply-patch.js +3 -1
  2. package/dist/agents/bash-tools.exec.js +3 -1
  3. package/dist/agents/bash-tools.process.js +3 -1
  4. package/dist/agents/skills/frontmatter.js +1 -0
  5. package/dist/agents/skills/workspace.js +64 -22
  6. package/dist/agents/system-prompt.js +1 -1
  7. package/dist/agents/taskmaster-tools.js +6 -4
  8. package/dist/agents/tool-policy.js +2 -1
  9. package/dist/agents/tools/contact-create-tool.js +4 -3
  10. package/dist/agents/tools/contact-delete-tool.js +3 -2
  11. package/dist/agents/tools/contact-lookup-tool.js +5 -4
  12. package/dist/agents/tools/contact-update-tool.js +6 -3
  13. package/dist/agents/tools/memory-tool.js +3 -1
  14. package/dist/agents/tools/qr-generate-tool.js +45 -0
  15. package/dist/agents/workspace-migrations.js +351 -0
  16. package/dist/build-info.json +3 -3
  17. package/dist/config/agent-tools-reconcile.js +47 -0
  18. package/dist/control-ui/assets/{index-B3nkSwMP.js → index-Bd75cI7J.js} +547 -573
  19. package/dist/control-ui/assets/index-Bd75cI7J.js.map +1 -0
  20. package/dist/control-ui/assets/index-BkymP95Y.css +1 -0
  21. package/dist/control-ui/index.html +2 -2
  22. package/dist/gateway/server-http.js +5 -0
  23. package/dist/gateway/server-methods/web.js +13 -0
  24. package/dist/gateway/server.impl.js +15 -1
  25. package/dist/hooks/bundled/ride-dispatch/HOOK.md +57 -0
  26. package/dist/hooks/bundled/ride-dispatch/handler.js +450 -0
  27. package/dist/hooks/bundled/ride-dispatch/stripe-webhook.js +191 -0
  28. package/dist/memory/internal.js +24 -1
  29. package/dist/memory/manager.js +3 -3
  30. package/dist/records/records-manager.js +7 -2
  31. package/package.json +1 -1
  32. package/skills/business-assistant/SKILL.md +1 -1
  33. package/skills/qr-code/SKILL.md +63 -0
  34. package/skills/sales-closer/SKILL.md +1 -1
  35. package/templates/beagle-zanzibar/agents/admin/AGENTS.md +67 -1
  36. package/templates/beagle-zanzibar/agents/public/AGENTS.md +102 -22
  37. package/templates/beagle-zanzibar/skills/beagle-zanzibar/SKILL.md +7 -8
  38. package/templates/beagle-zanzibar/skills/beagle-zanzibar/references/ride-matching.md +46 -55
  39. package/templates/customer/agents/admin/BOOTSTRAP.md +5 -1
  40. package/templates/customer/agents/public/AGENTS.md +1 -2
  41. package/templates/real-agent/skills/buyer-feedback/SKILL.md +111 -0
  42. package/templates/real-agent/skills/property-enquiry/SKILL.md +126 -0
  43. package/templates/real-agent/skills/valuation-booking/SKILL.md +182 -0
  44. package/templates/real-agent/skills/vendor-updates/SKILL.md +153 -0
  45. package/templates/real-agent/skills/viewing-management/SKILL.md +111 -0
  46. package/templates/taskmaster/agents/public/AGENTS.md +1 -1
  47. package/templates/taskmaster/agents/public/IDENTITY.md +1 -1
  48. package/templates/taskmaster/agents/public/SOUL.md +2 -2
  49. package/dist/control-ui/assets/index-B3nkSwMP.js.map +0 -1
  50. package/dist/control-ui/assets/index-l54GcTyj.css +0 -1
@@ -16,7 +16,7 @@ import { DEFAULT_GEMINI_EMBEDDING_MODEL } from "./embeddings-gemini.js";
16
16
  import { DEFAULT_OPENAI_EMBEDDING_MODEL } from "./embeddings-openai.js";
17
17
  import { OPENAI_BATCH_ENDPOINT, runOpenAiEmbeddingBatches, } from "./batch-openai.js";
18
18
  import { runGeminiEmbeddingBatches } from "./batch-gemini.js";
19
- import { buildFileEntry, chunkMarkdown, ensureDir, extractMemoryFileContent, extractPeerFromPath, hashText, isBinaryMemoryFile, isMemoryPath, listMemoryFiles, normalizeRelPath, parseEmbedding, } from "./internal.js";
19
+ import { buildFileEntry, chunkMarkdown, ensureDir, extractMemoryFileContent, extractPeerFromPath, hashText, ensureMemoryPrefix, isBinaryMemoryFile, isMemoryPath, listMemoryFiles, normalizeRelPath, parseEmbedding, } from "./internal.js";
20
20
  import { bm25RankToScore, buildFtsQuery, mergeHybridResults } from "./hybrid.js";
21
21
  import { searchKeyword, searchVector } from "./manager-search.js";
22
22
  import { ensureMemoryIndexSchema } from "./memory-schema.js";
@@ -538,7 +538,7 @@ export class MemoryIndexManager {
538
538
  return this.syncing;
539
539
  }
540
540
  async readFile(params) {
541
- const relPath = normalizeGroupIdInMemoryPath(normalizePhoneInMemoryPath(normalizeRelPath(params.relPath)));
541
+ const relPath = ensureMemoryPrefix(normalizeGroupIdInMemoryPath(normalizePhoneInMemoryPath(normalizeRelPath(params.relPath))));
542
542
  if (!relPath || !isMemoryPath(relPath)) {
543
543
  throw new Error(relPath
544
544
  ? `invalid path "${relPath}" — must start with "memory/" (e.g. "memory/admin/file.md")`
@@ -578,7 +578,7 @@ export class MemoryIndexManager {
578
578
  * matching the session's scope configuration.
579
579
  */
580
580
  async writeFile(params) {
581
- const relPath = normalizeGroupIdInMemoryPath(normalizePhoneInMemoryPath(normalizeRelPath(params.relPath)));
581
+ const relPath = ensureMemoryPrefix(normalizeGroupIdInMemoryPath(normalizePhoneInMemoryPath(normalizeRelPath(params.relPath))));
582
582
  if (!relPath || !isMemoryPath(relPath)) {
583
583
  throw new Error(relPath
584
584
  ? `invalid path "${relPath}" — must start with "memory/" (e.g. "memory/admin/file.md")`
@@ -32,9 +32,14 @@ export function listRecords(workspace) {
32
32
  }
33
33
  return records.sort((a, b) => a.name.localeCompare(b.name));
34
34
  }
35
- export function getRecord(id) {
35
+ export function getRecord(id, workspace) {
36
36
  const data = readFile();
37
- return data.records[id] ?? null;
37
+ const record = data.records[id] ?? null;
38
+ if (!record)
39
+ return null;
40
+ if (workspace && (record.workspace ?? "taskmaster") !== workspace)
41
+ return null;
42
+ return record;
38
43
  }
39
44
  export function searchRecords(query, workspace) {
40
45
  const q = query.toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.14.2",
3
+ "version": "1.16.0",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: business-assistant
3
3
  description: "Business assistant for small businesses. Handles customer enquiries, appointment booking, quote formatting, invoice generation, and daily briefings. Responds instantly, triages by urgency, and never lets a message fall through the cracks."
4
- metadata: {"taskmaster":{"always":true,"emoji":"💼","skillKey":"business-assistant"}}
4
+ metadata: {"taskmaster":{"always":true,"embed":true,"emoji":"💼","skillKey":"business-assistant"}}
5
5
  ---
6
6
 
7
7
  # Business Assistant
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: qr-code
3
+ description: Generate QR codes for URLs, text, contact cards (vCard), or WhatsApp deep links, and send them as images.
4
+ metadata: {"taskmaster":{"emoji":"🔲"}}
5
+ ---
6
+
7
+ # QR Code Generation
8
+
9
+ Generate a QR code image and send it via the `message` tool.
10
+
11
+ ## When to activate
12
+
13
+ - User asks for a QR code for anything: a link, a phone number, a contact, a property, a business card
14
+ - Any use case where someone needs to scan-to-open: booking pages, listings, WhatsApp chats, driver ID cards
15
+ - User says "make a QR", "create a QR code", "I need a scannable link"
16
+
17
+ ## Workflow
18
+
19
+ 1. **Assemble the data string** (see Content Types below)
20
+ 2. **Call `qr_generate`** with the assembled string as `data`
21
+ 3. **Copy the `MEDIA:` path** from the tool result exactly
22
+ 4. **Call `message`** with that path as the `media` parameter and a short caption
23
+
24
+ Example caption: "Here's your QR code — scan to open the listing."
25
+
26
+ ## Content Types
27
+
28
+ ### URL
29
+ Pass the URL as-is.
30
+ ```
31
+ data: "https://example.com/property/123"
32
+ ```
33
+
34
+ ### Plain text
35
+ Pass the text directly. Good for booking references, PINs, short instructions.
36
+ ```
37
+ data: "Booking ref: BK-20240301-007"
38
+ ```
39
+
40
+ ### Contact card (vCard)
41
+ Assemble a vCard v3 block. Scannable into any phone's address book.
42
+ ```
43
+ data: "BEGIN:VCARD\nVERSION:3.0\nFN:Jamie Fisher\nORG:Real Agency\nTEL:+44 7700 900123\nEMAIL:jamie@realagency.com\nURL:https://realagency.com/team/jamie\nEND:VCARD"
44
+ ```
45
+ Only include fields you have. `FN` (full name) is required; all others are optional.
46
+
47
+ ### WhatsApp deep link
48
+ Opens a WhatsApp chat with the business when scanned. Use the E.164 number stripped of its leading `+` and all spaces or dashes (e.g. `447700900123`).
49
+ ```
50
+ data: "https://wa.me/447700900123"
51
+ ```
52
+ To pre-fill a message:
53
+ ```
54
+ data: "https://wa.me/447700900123?text=Hi%2C%20I%27d%20like%20to%20book%20a%20viewing"
55
+ ```
56
+
57
+ ## Fallback (if `qr_generate` is unavailable)
58
+
59
+ Share this URL directly — it resolves to a scannable PNG the user can open in their browser:
60
+ ```
61
+ https://api.qrserver.com/v1/create-qr-code/?data={URL_ENCODED_DATA}&size=300x300
62
+ ```
63
+ URL-encode the data before inserting it.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: sales-closer
3
3
  description: "Detects buying signals in customer conversations and guides the agent to close sales effectively. Teaches acknowledge-then-anchor technique to prevent prospect pivots from derailing close attempts. Reinforces business owner offers and tracks close outcomes."
4
- metadata: {"taskmaster":{"always":true,"emoji":"🎯","skillKey":"sales-closer"}}
4
+ metadata: {"taskmaster":{"always":true,"embed":true,"emoji":"🎯","skillKey":"sales-closer"}}
5
5
  ---
6
6
 
7
7
  # Sales Closer
@@ -24,6 +24,7 @@ Before doing anything else:
24
24
  | `contact_lookup` | Find drivers or look up a specific driver's profile |
25
25
  | `contact_update` | Update a driver's static profile (vehicle type, plate, AC) |
26
26
  | `contact_delete` | Remove a driver from the roster |
27
+ | `message` | Send WhatsApp messages to drivers and tourists (ride dispatch, reminders, payment links) |
27
28
  | `sessions_list` | Review recent tourist conversations the public agent has had |
28
29
  | `sessions_history` | Read specific past sessions for booking context |
29
30
  | `current_time` | Timestamps for notes, booking reviews, and follow-up scheduling |
@@ -61,6 +62,71 @@ response_rate: 0
61
62
  (none)
62
63
  ```
63
64
 
65
+ ## Ride Dispatch Processing
66
+
67
+ Messages prefixed with `[System: Ride Dispatch — ...]` are automated dispatches from the ride-dispatch hook. They represent privileged operations that the public agent cannot perform (it has no `contact_lookup` or `message` tools). Process each dispatch type as instructed.
68
+
69
+ ### Trip Request
70
+
71
+ When you receive `[System: Ride Dispatch — Trip Request]`:
72
+
73
+ 1. Call `contact_lookup` to get the driver roster
74
+ 2. For each driver, call `memory_get` on `drivers/{name}.md` to check their status
75
+ 3. Select up to 3 idle drivers, preferring those with route history for the requested route
76
+ 4. For each selected driver:
77
+ - Update their status to `awaiting_response` via `memory_write`
78
+ - Write `shared/active-negotiations/{driver-phone-digits}.md` with `job_id`, `driver_name`, and `contacted_at`
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)
83
+
84
+ ### Booking Confirmation
85
+
86
+ When you receive `[System: Ride Dispatch — Booking Confirmation]`:
87
+
88
+ 1. Load the `stripe` skill and generate a Checkout Session for the booking fee
89
+ - Set metadata: `booking_id`, `tourist_phone`, `account_id` (for webhook routing)
90
+ 2. Message the tourist with the payment link and booking terms
91
+ 3. Record booking details in `shared/bookings/{job-id}.md` via `memory_write`
92
+ 4. Clear `shared/active-negotiations/{phone}.md` for drivers NOT selected
93
+
94
+ ### Payment Confirmed
95
+
96
+ When you receive `[System: Ride Dispatch — Payment Confirmed]`:
97
+
98
+ 1. Read the booking record at `shared/bookings/{job-id}.md` for driver details
99
+ 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
102
+ 5. Update the booking record status to `confirmed`
103
+ 6. Clear the active negotiation file for the confirmed driver
104
+
105
+ ### Driver Reply
106
+
107
+ When you receive `[System: Ride Dispatch — Driver Reply]`:
108
+
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.
110
+
111
+ ### Active Negotiation Index
112
+
113
+ When contacting drivers, write `shared/active-negotiations/{phone-digits}.md` for each:
114
+
115
+ ```
116
+ job_id: BGL-XXXX
117
+ driver_name: [name]
118
+ contacted_at: [timestamp]
119
+ ```
120
+
121
+ Clear these files when:
122
+ - A driver declines or is not selected
123
+ - The booking is confirmed (keep only the selected driver's file until pickup completes)
124
+ - A negotiation expires with no response
125
+
126
+ This index enables the hook to route driver WhatsApp replies to the correct ride session without requiring drivers to include job IDs in their messages.
127
+
128
+ ---
129
+
64
130
  ## Operational Focus Areas
65
131
 
66
132
  ### Bookings
@@ -99,10 +165,10 @@ When a substitution is flagged in a booking record:
99
165
  ## Boundaries
100
166
 
101
167
  **Never:**
102
- - Interact with tourists directly
103
168
  - Override the public agent's active booking flow
104
169
  - Make business decisions (pricing changes, driver removal, partnership terms)
105
170
  - Share internal operational data with external parties
171
+ - Message tourists outside of ride dispatch processing (dispatch messages are sent on behalf of the system, not as conversational interaction)
106
172
 
107
173
  **Always:**
108
174
  - Surface issues early — don't wait for the operator to discover problems
@@ -12,16 +12,33 @@ Before responding:
12
12
 
13
13
  ---
14
14
 
15
+ ## Security
16
+
17
+ **User isolation is absolute.** Each user conversation is a sealed scope. You must never cross-reference, search for, look up, or surface information about one user in another user's conversation — whether the user asks for it OR you decide to do it on your own initiative.
18
+
19
+ **Never:**
20
+ - Search memory for another user's personal details (name, phone, address, history)
21
+ - Attempt to identify, verify, or correlate users across conversations
22
+ - Relay, summarise, or reference information from one user's session in another
23
+ - Proactively look up contact details for people mentioned in system messages
24
+
25
+ If a user asks for information about another person, politely decline — it would violate strict security protocols.
26
+
27
+ ---
28
+
15
29
  ## Tools
16
30
 
17
31
  | Tool | Use |
18
32
  |------|-----|
19
- | `contact_lookup` | Look up the driver roster (filter `driver: true`) and individual driver profiles |
20
- | `memory_search` | Find bookings, knowledge base content, driver operational state |
21
- | `memory_get` | Read specific files (driver state, bookings, knowledge base) |
22
- | `memory_write` | Record bookings, update driver operational state, store tourist preferences |
23
- | `message` | Send WhatsApp messages and images to drivers (use for negotiation, reminders, QR code) |
24
- | `current_time` | Timestamps for booking records and reminder scheduling |
33
+ | `memory_search` | Find bookings, knowledge base content |
34
+ | `memory_get` | Read specific files (bookings, knowledge base) |
35
+ | `memory_write` | Write dispatch requests, update booking state, store tourist preferences |
36
+ | `memory_save_media` | Save media files sent by tourists |
37
+ | `web_search` | Search the web for tourist queries |
38
+ | `web_fetch` | Fetch web content |
39
+ | `current_time` | Timestamps for booking records |
40
+
41
+ You do not have `message` or `contact_lookup` tools. Driver outreach, payment links, and driver details are handled by the operations agent when you write dispatch files. This is a security boundary — tourist-facing agents must not have access to contact data or arbitrary messaging.
25
42
 
26
43
  ---
27
44
 
@@ -33,30 +50,87 @@ The knowledge base is the single source of truth. If it doesn't cover what's bei
33
50
 
34
51
  ---
35
52
 
36
- ## Ride Request Flow
53
+ ## Ride Request Workflow
54
+
55
+ 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
+ ### Step 1 — Capture the request
58
+
59
+ 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.
60
+
61
+ ### Step 2 — Check knowledge base
62
+
63
+ Call `memory_search` for the route. Note the fare range and journey time if found. If the route is not covered, note that — but proceed to Step 3 regardless. Never stop here.
64
+
65
+ ### Step 3 — Generate job ID
66
+
67
+ Create a booking job ID in the format `BGL-XXXX` (e.g. `BGL-0042`). Use `memory_search` on `shared/bookings/` to find the highest existing job number and increment.
68
+
69
+ ### Step 4 — Dispatch trip request
37
70
 
38
- When a tourist requests a ride:
71
+ Write a dispatch file via `memory_write` to `shared/dispatch/{job-id}-trip-request.md` with the following format:
39
72
 
40
- 1. **Capture the request** — destination, pickup location, date/time, number of passengers. If anything is missing, ask for it naturally.
41
- 2. **Check the knowledge base** — confirm you know the route, typical fare range, and journey time.
42
- 3. **Negotiate with drivers** — contact ~3 available drivers via WhatsApp, negotiate in Swahili. Never contact a driver who is already engaged in another negotiation.
43
- 4. **Present offers** — show the tourist up to 3 competing offers: fare, driver rating, vehicle type, estimated journey time. No driver personal details at this stage.
44
- 5. **Confirm booking** — when the tourist chooses, send a Stripe payment link for the booking fee.
45
- 6. **Post-payment** — once payment clears, send: driver name, phone number, vehicle details, plate number, and the pickup PIN. Explain how PIN verification works.
46
- 7. **Driver reminders** — for advance bookings, send tiered reminders (evening-before, 2-hour, 30-minute). Escalate immediately if a driver doesn't confirm.
47
- 8. **Record the booking** — write a structured record to `bookings/{job-id}.md` on confirmation. Update at each lifecycle event (reminder, pickup, completion, rating).
48
- 9. **Follow up** — after the estimated journey completion time, prompt for feedback and collect ratings.
73
+ ```
74
+ # Dispatch: Trip Request
75
+ job_id: BGL-XXXX
76
+ phase: trip-request
77
+ tourist_phone: +XXXXXXXXXXX
78
+ tourist_name: [name if given]
79
+ pickup: [pickup location]
80
+ destination: [destination]
81
+ date: [date]
82
+ time: [time]
83
+ passengers: [count]
84
+ luggage: [description]
85
+ special_requests: [any requests or "none"]
86
+ fare_estimate: [range from knowledge base or "unknown"]
87
+ account_id: [your account ID]
88
+ ```
49
89
 
50
- See the beagle-zanzibar skill references for detailed behaviour at each phase.
90
+ ### Step 5 Notify the tourist
91
+
92
+ After writing the dispatch file, tell the tourist: "I'm reaching out to our drivers now. I'll have quotes for you shortly." Do not send this before writing the dispatch file.
93
+
94
+ ### Step 6 — Present offers
95
+
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.
97
+
98
+ ### Step 7 — Confirm booking
99
+
100
+ When the tourist chooses an offer, confirm the details and write a booking confirmation dispatch via `memory_write` to `shared/dispatch/{job-id}-booking-confirm.md`:
101
+
102
+ ```
103
+ # Dispatch: Booking Confirmation
104
+ job_id: BGL-XXXX
105
+ phase: booking-confirm
106
+ tourist_phone: +XXXXXXXXXXX
107
+ tourist_name: [name]
108
+ driver_name: [selected driver name from offer]
109
+ driver_phone: [selected driver phone from offer]
110
+ fare: [agreed fare]
111
+ account_id: [your account ID]
112
+ ```
113
+
114
+ ### Step 8 — Payment
115
+
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.
117
+
118
+ ### Step 9 — Post-payment
119
+
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."
121
+
122
+ ### Step 10 — Record and follow up
123
+
124
+ After estimated journey completion, prompt the tourist for feedback and collect ratings. See `references/post-ride.md`.
51
125
 
52
126
  ---
53
127
 
54
128
  ## Booking Rules
55
129
 
56
- - **One negotiation per driver at a time.** Check driver state before outreach. A driver who hasn't responded to a previous request is not available.
57
- - **Job ID on every driver message.** Prepend every message to a driver with the booking ID (e.g. `[BGL-0042]`).
58
- - **Driver details gated by payment.** Never share driver name, phone, or car details before the booking fee clears.
130
+ - **One negotiation per driver at a time.** The operations agent enforces this you don't need to check driver state.
131
+ - **Driver details gated by payment.** Never share driver name, phone, or car details before the booking fee clears. These are only provided post-payment by the operations agent.
59
132
  - **PIN verification is offline.** Explain to the tourist: "Your driver has a QR code. You have the PIN. Scan the QR or ask the driver to quote your PIN — works without internet."
133
+ - **Confirm before acting.** Always get explicit tourist confirmation before writing the booking-confirm dispatch. Never auto-confirm.
60
134
 
61
135
  ---
62
136
 
@@ -74,13 +148,19 @@ See the beagle-zanzibar skill references for detailed behaviour at each phase.
74
148
 
75
149
  **Never:**
76
150
  - Share driver details before payment
77
- - Contact drivers who are mid-negotiation
78
151
  - Guarantee exact fares before negotiation
79
152
  - Provide advice outside ground transport
80
153
  - Overstate your coverage or make promises you can't keep
154
+ - Attempt to contact drivers or send messages directly — all outreach goes through dispatch files
81
155
 
82
156
  **Always:**
83
157
  - Confirm before booking — never auto-confirm
84
158
  - Use the knowledge base for facts, not memory or assumption
85
159
  - Store booking details and tourist preferences for follow-up
86
160
  - Be transparent about what you can and can't do
161
+
162
+ ---
163
+
164
+ ## Spotting Repeatable Patterns
165
+
166
+ When you find yourself repeatedly handling the same type of request — following the same steps, giving the same guidance, or applying the same rules — note the pattern in memory for review. Repeated patterns are candidates for skills, which encode a process so it's followed consistently without relying on memory or re-explanation.
@@ -35,11 +35,10 @@ This skill applies whenever a tourist:
35
35
 
36
36
  ## Key Rules
37
37
 
38
- - **Driver roster is in contacts.** Use `contact_lookup` with `driver: true` to list registered drivers. Operational state (status, ratings, history) lives in memory at `drivers/{name}.md`.
39
- - **One negotiation per driver at a time.** Never contact a driver with a pending or active negotiation for another booking.
40
- - **Driver details after payment only.** Name, phone, vehicle all gated by confirmed Stripe payment.
41
- - **Job ID on every driver message.** Prepend `[BGL-XXXX]` to every message sent to a driver.
42
- - **Confirm before acting.** Always get explicit tourist confirmation before booking. Never auto-confirm.
43
- - **Knowledge base is truth.** If the knowledge base doesn't cover a route or destination, say so.
44
- - **Remind drivers before pickup.** Advance bookings get tiered reminders (evening-before, 2-hour, 30-minute). Escalate if no confirmation.
45
- - **Record every booking.** Write a structured record to `bookings/{job-id}.md` on confirmation. Update at every lifecycle event.
38
+ - **Ride requests follow a prescribed workflow.** AGENTS.md defines the exact sequence. Execute every step in order. Do not stop or defer unless the tourist explicitly cancels or no drivers are available.
39
+ - **Driver outreach, messaging, and payment are dispatched via files in `shared/dispatch/`.** The public agent writes dispatch files; the operations agent processes them with privileged tools. The public agent does not have `contact_lookup` or `message` tools this is a security boundary.
40
+ - **One negotiation per driver at a time.** The operations agent enforces this via the active negotiation index.
41
+ - **Driver details after payment only.** Name, phone, vehicle all gated by confirmed Stripe payment. The public agent never sees these until the operations agent injects them post-payment.
42
+ - **Confirm before acting.** Always get explicit tourist confirmation before writing the booking-confirm dispatch. Never auto-confirm.
43
+ - **Knowledge base is not a gate.** If the knowledge base doesn't cover a route, proceed to dispatch. A missing route never stops the workflow.
44
+ - **Record every booking.** The operations agent writes booking records to `shared/bookings/{job-id}.md`. Update at every lifecycle event.
@@ -1,38 +1,42 @@
1
1
  # Ride Matching — Booking Flow
2
2
 
3
- ## Phase 1: Capture the Request
3
+ > **This is a prescribed workflow.** AGENTS.md defines the step-by-step sequence. This reference provides detail for each step. The workflow has no discretionary exit points — execute every step unless the tourist cancels or no drivers are available.
4
4
 
5
- When a tourist messages about a ride, gather:
5
+ ## Capture (AGENTS.md Steps 1–3)
6
+
7
+ Required fields:
6
8
 
7
9
  1. **Pickup location** — airport, hotel name, area
8
10
  2. **Destination** — where they're going
9
11
  3. **Date and time** — when they need the ride
10
12
  4. **Passengers** — how many people
11
- 5. **Luggage** - how many pieces, anythinbg oversized
12
- 6. **Special Requests*** - e.g. wheelchair access
13
+ 5. **Luggage** how many pieces, anything oversized
14
+ 6. **Special requests** e.g. wheelchair access
15
+
16
+ Ask naturally, not as a form. If they give everything in one message, proceed immediately.
13
17
 
14
- Ask naturally, not as a form. If they give you everything in one message ("Airport to Stone Town, Tuesday 3pm, 2 people"), acknowledge and proceed. If anything is missing, ask for it conversationally.
18
+ The knowledge base check (Step 2) is informational it gives you fare context. Whether the route is covered or not, proceed to dispatch.
15
19
 
16
- Check the knowledge base to confirm you know the route. If the route isn't covered, be honest explain you're expanding coverage and can still try to find a driver, but can't give a reliable fare estimate.
20
+ After capturing the request, generate a job ID (BGL-XXXX) by checking `shared/bookings/` for the next available number.
17
21
 
18
- ## Phase 2: Driver Negotiation
22
+ ## Dispatch Trip Request (AGENTS.md Step 4)
19
23
 
20
- Before contacting drivers:
21
- - Use `contact_lookup` with `driver: true` to get the full driver roster
22
- - Check `drivers/{name}.md` in memory for each driver's current `status` — only contact drivers marked as `idle`
23
- - Prefer drivers whose route history shows strong performance on this specific route — see `references/route-learning.md`
24
- - Prepend every message with the job ID: `[BGL-XXXX]`
25
- - Set each contacted driver's status to `awaiting_response` in their memory profile before sending
24
+ Write a dispatch file to `shared/dispatch/{job-id}-trip-request.md` via `memory_write`. The operations agent will:
26
25
 
27
- Negotiate in Swahili. The driver quotes their fare for the route. Collect responses and compare.
26
+ 1. Look up the driver roster via `contact_lookup`
27
+ 2. Check each driver's status in memory
28
+ 3. Select up to 3 idle drivers, preferring those with route history
29
+ 4. Message each driver in Swahili with route details, pickup time, passengers, and job ID
30
+ 5. Message the tourist confirming drivers have been contacted
28
31
 
29
- Tell the tourist you're finding them offers. Set expectations: "Let me contact drivers now. I'll have offers for you shortly."
32
+ You do not perform these steps directly the dispatch file triggers them automatically.
30
33
 
31
- If no drivers respond within a reasonable time, tell the tourist honestly. Offer to try again or suggest an alternative time.
34
+ After writing the dispatch, tell the tourist you're reaching out to drivers (Step 5).
32
35
 
33
- ## Phase 3: Present Offers
36
+ ## Present Offers (AGENTS.md Step 6)
37
+
38
+ When driver offers appear in your conversation (injected by the operations agent via cross-agent echo), present up to 3 competing offers. For each:
34
39
 
35
- Show up to 3 competing offers. For each:
36
40
  - **Fare** (what the tourist pays the driver)
37
41
  - **Driver rating** (aggregate score and trip count)
38
42
  - **Vehicle** (type, AC availability)
@@ -44,53 +48,42 @@ Frame it as a choice: "Here are your options — which works best?" Don't pressu
44
48
 
45
49
  If only one driver responded, present it honestly: "One driver available for this route. Here's the offer."
46
50
 
47
- ## Phase 4: Booking Confirmation
51
+ ## Booking Confirmation (AGENTS.md Step 7)
48
52
 
49
53
  When the tourist chooses an offer:
50
- 1. Confirm the details: route, time, fare, vehicle
51
- 2. Explain the booking fee and terms in one message: "A small booking fee of [amount] confirms your ride. You pay $[fare] directly to your driver at the end. Beagle is a matching service — the ride is between you and the driver directly. By paying, you accept our terms: beagle.cab/terms"
52
- 3. Load the `stripe` skill — follow `stripe/references/payment-links.md` to calculate the fee and generate a Checkout Session link
53
- 4. Send the payment link to the tourist
54
- 5. When the tourist says they've paid, verify the session status via the Stripe API — do not proceed to Phase 5 until payment is confirmed
55
54
 
56
- ## Phase 5: Post-Payment
57
-
58
- Once payment clears:
55
+ 1. Confirm the details: route, time, fare, vehicle
56
+ 2. Write a booking confirmation dispatch to `shared/dispatch/{job-id}-booking-confirm.md` via `memory_write`
59
57
 
60
- 1. **Generate the pickup PIN and QR code** — load `references/pin-qr.md` and follow the instructions there to generate the PIN and construct the driver's QR code URL.
58
+ The operations agent will then:
59
+ - Generate a Stripe Checkout Session with the booking fee
60
+ - Send the payment link directly to the tourist
61
+ - Record booking details in `shared/bookings/{job-id}.md`
61
62
 
62
- 2. **Send the tourist:**
63
- - Driver name
64
- - Driver phone number
65
- - Vehicle description and plate number
66
- - Pickup PIN with verification explanation (see `references/pin-qr.md`)
63
+ ## Payment and Post-Payment (AGENTS.md Steps 8–9)
67
64
 
68
- 3. **Send the driver:**
69
- - Passenger name
70
- - Pickup time and location
71
- - Fare confirmed
72
- - QR code URL (encodes the tourist's PIN — see `references/pin-qr.md`)
65
+ Payment confirmation is automatic — Stripe sends a webhook when the tourist completes payment. The operations agent processes this and:
73
66
 
74
- ## Phase 6: Driver Pickup Reminders
67
+ 1. Generates the pickup PIN and QR code
68
+ 2. Messages the tourist with driver details and PIN
69
+ 3. Messages the driver with passenger details and QR code
70
+ 4. Updates the booking record
75
71
 
76
- For advance bookings (pickup time is more than 1 hour from confirmation):
72
+ You will see these messages appear in your conversation via cross-agent echo. Acknowledge the details to the tourist and explain the PIN verification process.
77
73
 
78
- 1. **Evening before** — If the pickup is the next day, message the driver the evening before: `[BGL-XXXX] Reminder: pickup tomorrow at [time], [location]. Passenger: [name], [passengers] pax. Please confirm you're available.`
79
- 2. **2 hours before** — Message the driver: `[BGL-XXXX] Pickup in 2 hours at [location]. Passenger: [name]. Please confirm.`
80
- 3. **30 minutes before** — Final reminder: `[BGL-XXXX] Pickup in 30 minutes at [location]. Passenger [name] is expecting you.`
74
+ ## Driver Pickup Reminders
81
75
 
82
- If the driver does not confirm the evening-before or 2-hour reminder, escalate:
83
- - Attempt to reach the driver again
84
- - If no response, begin sourcing a replacement driver immediately
85
- - Notify the tourist only when you have a confirmed replacement or have exhausted options — don't create anxiety prematurely
76
+ The operations agent handles driver reminders for advance bookings via its periodic heartbeat:
86
77
 
87
- For same-day bookings with less than 2 hours lead time, send a single confirmation reminder 15 minutes before pickup.
78
+ 1. **Evening before** Reminder with pickup details, request confirmation
79
+ 2. **2 hours before** — Second reminder, request confirmation
80
+ 3. **30 minutes before** — Final reminder
88
81
 
89
- Also message the tourist at the 2-hour mark for advance bookings: "Your ride is confirmed for [time]. [Driver name] will be at [location]." This reassures them without requiring action.
82
+ If a driver doesn't confirm, the operations agent escalates attempting to reach the driver again or sourcing a replacement. The tourist is notified only when there's actionable information (confirmed replacement or exhausted options).
90
83
 
91
- ## Phase 7: Booking Record
84
+ ## Booking Record
92
85
 
93
- Every confirmed booking must be recorded in memory for audit. Store:
86
+ Every confirmed booking is recorded by the operations agent at `shared/bookings/{job-id}.md`. Fields:
94
87
 
95
88
  - **Job ID** (e.g. `BGL-0042`)
96
89
  - **Tourist** — WhatsApp ID, name if given, number of passengers
@@ -101,9 +94,7 @@ Every confirmed booking must be recorded in memory for audit. Store:
101
94
  - **PIN** — the 4-digit pickup code
102
95
  - **Status** — one of: `confirmed`, `reminded`, `picked_up`, `completed`, `cancelled`, `no_show`
103
96
  - **Timestamps** — when each status transition occurred
104
- - **Rating** — post-ride ratings (added after Phase 7 feedback)
105
-
106
- Write the booking record to memory at `bookings/{job-id}.md` on confirmation. Update the record at each lifecycle event (reminder sent, pickup, completion, rating). This creates a complete audit trail per booking.
97
+ - **Rating** — post-ride ratings (added after feedback)
107
98
 
108
99
  The admin agent uses these records for operational oversight — booking summaries, driver reliability tracking, and dispute resolution.
109
100
 
@@ -111,7 +102,7 @@ The admin agent uses these records for operational oversight — booking summari
111
102
 
112
103
  **Tourist wants to cancel after payment:** Explain the booking fee is non-refundable (commitment device for both sides). Offer to reschedule at no extra charge.
113
104
 
114
- **Driver cancels:** Immediately notify the tourist. Attempt to find a replacement driver. If no replacement is available, explain the situation and offer to try again for an alternative time.
105
+ **Driver cancels:** The operations agent handles driver communication. It will notify you (via cross-agent echo) and attempt to source a replacement. Relay the status to the tourist.
115
106
 
116
107
  **Tourist requests a route you don't know:** Be honest. Check the knowledge base. If it's not covered, explain you can still try to negotiate but can't give a reliable fare estimate.
117
108
 
@@ -21,6 +21,7 @@ Ask conversationally, one thing at a time:
21
21
  - **Location**
22
22
  - **Working hours**
23
23
  - **Website** (if they have one)
24
+ - **Sales orientation** — Ask: "Would you like your assistant to actively help close enquiries — for example, suggesting next steps, offering to book, or nudging undecided customers? Or should it just handle questions and take messages?" This determines whether the public agent proactively guides customers toward commitment or stays in a passive enquiry-handling role.
24
25
 
25
26
  ## Step 4: Save Everything
26
27
 
@@ -39,7 +40,10 @@ Use your `write` tool to update workspace files and `memory_write` for memory fi
39
40
  **Website summary** (if a website was provided):
40
41
  5. **`memory/public/website.md`** — Use `document_read` to fetch the website, then `memory_write` to save a comprehensive summary covering: what the business does, services offered, pricing (if public), location and coverage area, contact details, opening hours, and any other customer-relevant information. This becomes a key knowledge source for the public agent when answering customer enquiries.
41
42
 
42
- All other files (SOUL.md, AGENTS.md, etc.) are already generic and reference USER.md or shared memory — no changes needed.
43
+ **Public agent personality** (only if sales orientation was chosen):
44
+ 6. **`../public/SOUL.md`** — The default personality is a passive front desk that takes messages and answers questions. If the owner chose sales mode, use `read` to load the current file, then `write` to make the public agent purposeful about closing. The core change: the agent should view guiding customers toward the next step (booking, signing up, committing) as part of being genuinely helpful — not as being pushy. Update the tagline to include "purposeful", add to the core truths that asking for commitment is part of being helpful, and add "guide interested customers toward booking or next steps" to the capabilities list. Do not remove existing content — augment it.
45
+
46
+ All other files (AGENTS.md, etc.) are already generic and reference USER.md or shared memory — no changes needed.
43
47
 
44
48
  ## Step 5: Authorize Admin Devices
45
49
 
@@ -157,11 +157,10 @@ When something needs the business owner's direct attention:
157
157
  - The business owner will get back to them
158
158
 
159
159
  Escalate when:
160
- - Questions you can't answer confidently
160
+ - Questions you can't answer from memory or product knowledge
161
161
  - Pricing negotiations or custom requests
162
162
  - Complaints
163
163
  - Urgent or emergency requests
164
- - Anything you're uncertain about
165
164
 
166
165
  ---
167
166