@topy-ai/maggie 0.7.52 → 0.7.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-zh-TW.md +28 -4
- package/README.md +93 -60
- package/bin/maggie.js +67 -12
- package/bundled-contracts/maggie-assistant-tools/booking-receptionist-example-v1.json +161 -0
- package/bundled-contracts/maggie-assistant-tools/tool-registry-v1.schema.json +268 -20
- package/bundled-contracts/maggie-deployment/bridge-host-example-v1.json +49 -7
- package/bundled-contracts/maggiedash/booking-email-templates-v1.json +181 -13
- package/bundled-contracts/maggiedash/booking-rules-v1.json +1699 -0
- package/bundled-contracts/maggiedash/stripe-booking-capabilities-v1.json +398 -39
- package/bundled-references/maggiedash-booking/PRD.md +22 -11
- package/bundled-references/maggiedash-booking/QA-TEST-PLAN.md +1 -1
- package/bundled-references/maggiedash-booking/RULES.md +167 -0
- package/bundled-references/maggiedash-booking/RUNTIME-OPERATIONS.md +2 -2
- package/bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md +42 -4
- package/bundled-references/provider-adapter-contract.md +16 -3
- package/bundled-skills/catalog.json +1 -1
- package/bundled-skills/maggie-assistant-tools/SKILL.md +54 -3
- package/bundled-skills/maggie-assistant-tools/references/approvals-and-audit.md +27 -0
- package/bundled-skills/maggie-assistant-tools/references/booking-receptionist.md +45 -0
- package/bundled-skills/maggie-assistant-tools/references/bridge-host.md +54 -0
- package/bundled-skills/maggie-auth-reference/SKILL.md +18 -0
- package/bundled-skills/maggie-blog/SKILL.md +8 -0
- package/bundled-skills/maggie-booking/SKILL.md +66 -14
- package/bundled-skills/maggie-content-localization/SKILL.md +26 -0
- package/bundled-skills/maggie-dash/SKILL.md +28 -2
- package/bundled-skills/maggie-deployment/SKILL.md +48 -0
- package/bundled-skills/maggie-deployment/references/cloudflare.md +19 -0
- package/bundled-skills/maggie-deployment/references/vps.md +92 -0
- package/bundled-skills/maggie-design/SKILL.md +98 -3
- package/bundled-skills/maggie-design/references/app-ui-patterns.md +79 -0
- package/bundled-skills/maggie-feedback/SKILL.md +27 -2
- package/bundled-skills/maggie-marketplace/SKILL.md +12 -0
- package/bundled-skills/maggie-marketplace/references/directory-search.md +51 -0
- package/bundled-skills/maggie-marketplace/references/directory.mjs +112 -0
- package/bundled-skills/maggie-mobile-app/SKILL.md +65 -6
- package/bundled-skills/maggie-mobile-app/references/payments-and-push.md +78 -0
- package/bundled-skills/maggie-qa-workflow/SKILL.md +60 -0
- package/bundled-skills/maggie-seo-geo/SKILL.md +33 -0
- package/bundled-skills/maggie-service-booking/SKILL.md +28 -3
- package/bundled-skills/maggie-social-share/SKILL.md +56 -4
- package/bundled-skills/maggie-social-share/references/queue-and-connections.md +28 -0
- package/bundled-tools/clis/maggie_assistant_tools.py +84 -3
- package/bundled-tools/clis/maggie_booking.py +24 -12
- package/bundled-tools/clis/maggie_booking_connect_qa.py +136 -0
- package/bundled-tools/clis/maggie_booking_rules.py +476 -0
- package/bundled-tools/clis/maggie_bridge_host.py +77 -1
- package/bundled-tools/clis/maggie_dash.py +173 -3
- package/bundled-tools/clis/maggie_deployment_ops.py +524 -0
- package/bundled-tools/clis/maggie_design.py +15 -2
- package/bundled-tools/clis/maggie_design_bulk.py +290 -0
- package/bundled-tools/clis/maggie_design_lint.py +205 -3
- package/bundled-tools/clis/maggie_feedback.py +138 -28
- package/bundled-tools/clis/maggie_live_urls.py +138 -0
- package/bundled-tools/clis/maggie_localization.py +3 -0
- package/bundled-tools/clis/maggie_localization_keys.py +151 -0
- package/bundled-tools/clis/maggie_media_generate.py +174 -0
- package/bundled-tools/clis/maggie_mobile.py +219 -4
- package/bundled-tools/clis/maggie_product_release.py +49 -1
- package/bundled-tools/clis/maggie_qa_app_preflight.py +122 -4
- package/bundled-tools/clis/maggie_qa_fit.py +217 -0
- package/bundled-tools/clis/maggie_qa_workflow.py +5 -0
- package/bundled-tools/clis/maggie_schema_complexity.py +84 -0
- package/bundled-tools/clis/maggie_seo_routes.py +154 -0
- package/bundled-tools/clis/maggie_service_migration.py +272 -0
- package/bundled-tools/clis/maggie_social_queue.py +2 -0
- package/bundled-tools/clis/maggie_social_share.py +174 -7
- package/bundled-tools/integrations/maggie-api-pull.md +22 -0
- package/bundled-tools/runtime/assistant_approval.py +98 -0
- package/package.json +1 -1
- package/references/provider-adapter-contract.md +16 -3
- package/bundled-contracts/maggiedash/execution-board.json +0 -5463
- package/bundled-references/maggiedash-booking/CURRENT-STATE.md +0 -210
- package/bundled-references/maggiedash-booking/EXECUTION-BOARD.json +0 -5463
- package/bundled-references/maggiedash-booking/ORAWELLNESS-INTEGRATION-AUDIT.md +0 -270
- package/bundled-references/maggiedash-booking/PROGRESS.md +0 -279
- package/bundled-references/maggiedash-booking/README.md +0 -288
- package/bundled-references/maggiedash-booking/SKILLS-AND-CLI.md +0 -551
- package/bundled-references/maggiedash-booking/TASK-RUNBOOK.md +0 -107
- package/bundled-references/maggiedash-booking/TASKS.md +0 -178
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Booking rules and shared test vectors
|
|
2
|
+
|
|
3
|
+
Every host built on the MaggieDash booking model implements these rules the
|
|
4
|
+
same way. Each rule is a pure function with test vectors in
|
|
5
|
+
[`contracts/maggiedash/booking-rules-v1.json`](../../bundled-contracts/maggiedash/booking-rules-v1.json)
|
|
6
|
+
and a reference implementation in `tools/clis/maggie_booking_rules.py`.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
maggie booking rules-vectors --list # rules, why they exist, vector counts
|
|
10
|
+
maggie booking rules-vectors # reference implementation (must pass)
|
|
11
|
+
maggie booking rules-vectors --host-command "node scripts/booking-rules.mjs"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`--host-command` runs the host's own code: the command reads one line
|
|
15
|
+
`{"rule": "<id>", "input": {...}}` from stdin and prints the result JSON. Only
|
|
16
|
+
the fields a vector lists are compared, so hosts may return more. Add the
|
|
17
|
+
command to CI so a port of the engine cannot drift.
|
|
18
|
+
|
|
19
|
+
Conventions: money is integer minor units; instants are UTC ISO-8601; local
|
|
20
|
+
wall times are interpreted in the venue's IANA time zone.
|
|
21
|
+
|
|
22
|
+
## Lifecycle
|
|
23
|
+
|
|
24
|
+
**`manage_action`** — what a client's manage link may do. A client cancels
|
|
25
|
+
or reschedules only a `confirmed` booking that has not started; never one
|
|
26
|
+
on hold (`held`, `pending_payment`), arrived or in service, or closed
|
|
27
|
+
(`completed`, `cancelled`, `no_show`, `expired`). A window policy stops
|
|
28
|
+
online changes inside the window ("contact the business"). The business may
|
|
29
|
+
cancel anything not closed but never reschedules a booking in service. Check
|
|
30
|
+
the booking's **own** start time, not only the new slot's.
|
|
31
|
+
|
|
32
|
+
**`late_payment`** — Checkout completed after the hold expired. If the time
|
|
33
|
+
is still free, reinstate the booking as confirmed; otherwise refund the full
|
|
34
|
+
amount on the connected account and tell the client the time was taken. A
|
|
35
|
+
repeated event is a no-op. Never just record the payment: a paid client
|
|
36
|
+
without a booking is the worst outcome. Hosts also need a job that expires
|
|
37
|
+
`pending_payment` bookings whose Checkout session expired, so the slot is
|
|
38
|
+
released.
|
|
39
|
+
|
|
40
|
+
## Time and money
|
|
41
|
+
|
|
42
|
+
**`schedule_to_utc`** — every schedule input (time off, closures, shifts,
|
|
43
|
+
opening hours) is a local wall time in the **venue's** zone; the browser's
|
|
44
|
+
zone never matters. Send the local value and the location id to the server,
|
|
45
|
+
or convert with the venue zone in the client. A time skipped by the spring
|
|
46
|
+
clock change is rejected; a repeated autumn time uses the first occurrence
|
|
47
|
+
and is flagged. Staff without a location use their default location's zone.
|
|
48
|
+
|
|
49
|
+
**`money_minor`** — node-postgres returns `bigint` as a string. Parse money
|
|
50
|
+
columns to integers in the repository (or set `pg.types.setTypeParser(20,
|
|
51
|
+
…)` when every value fits a safe integer) before any arithmetic or JSON for
|
|
52
|
+
the booking sheet; reject decimals and floats.
|
|
53
|
+
|
|
54
|
+
**`sale_total`** — till maths: item prices × quantity, plus add-ons **only
|
|
55
|
+
when not already included in the booking item price** (a booking item that
|
|
56
|
+
already contains its add-ons must not be charged twice), minus discounts,
|
|
57
|
+
plus a service charge on the discounted amount (rounded half up). Vouchers
|
|
58
|
+
and packages are payments against the total, not discounts. Tips are added
|
|
59
|
+
after the service charge, never commissioned, and split between team members
|
|
60
|
+
by share, handing out remainder pennies one at a time.
|
|
61
|
+
|
|
62
|
+
The checkout around it: receipts for every sale, invoices and quotes with a
|
|
63
|
+
number sequence per business, pay-by-QR (a Checkout link on the client's
|
|
64
|
+
phone for the sale), and Stripe Terminal **server-driven** readers on the
|
|
65
|
+
business's connected account (register by code per branch location; record
|
|
66
|
+
the payment when the PaymentIntent succeeds; test with the simulated reader
|
|
67
|
+
`simulated-wpe`).
|
|
68
|
+
|
|
69
|
+
**`payment_policy_setup`** — deposit or full prepayment needs a connected
|
|
70
|
+
account that can take charges. Setup refuses the policy until then (and
|
|
71
|
+
`maggie booking release-gate` should flag a live service whose policy needs
|
|
72
|
+
payments while they are off), instead of refusing clients at the last step.
|
|
73
|
+
Do not create a `pending_payment` booking before the payment capability is
|
|
74
|
+
confirmed.
|
|
75
|
+
|
|
76
|
+
## Notifications and calendar
|
|
77
|
+
|
|
78
|
+
**`reminders_due`** — reminder offsets are per business (default 24 h and
|
|
79
|
+
2 h). Each stage is sent once per booking (store a sent marker per booking
|
|
80
|
+
and stage; dedupe key `<booking>:reminder:<stage>`). A booking made inside a
|
|
81
|
+
stage's window never gets that stage; a stage the worker missed is skipped,
|
|
82
|
+
not sent late.
|
|
83
|
+
|
|
84
|
+
**`notification_plan`** — cancellation and reschedule templates name who
|
|
85
|
+
acted (`by_client`, `by_business`, `by_system`). The business is told when a
|
|
86
|
+
client or the system cancels or reschedules; the client is always told.
|
|
87
|
+
|
|
88
|
+
**`ics_text`** — escape `\`, `;`, `,` and newlines in TEXT values (RFC 5545)
|
|
89
|
+
and fold lines at 75 octets. Attach the `.ics` to the confirmation e-mail
|
|
90
|
+
(`text/calendar; method=PUBLISH`) rather than linking to it. Per-team-member
|
|
91
|
+
calendar feeds use a long random token per person (revocable, rotated on
|
|
92
|
+
role change) and show only that person's bookings, without client contact
|
|
93
|
+
details.
|
|
94
|
+
|
|
95
|
+
## Availability
|
|
96
|
+
|
|
97
|
+
**`plan_slots`** (reference planner) and **`rota_week`**, **`price_for`**:
|
|
98
|
+
|
|
99
|
+
- opening hours may have several ranges per day (lunch breaks);
|
|
100
|
+
- staff may follow an N-week rota anchored to a Monday;
|
|
101
|
+
- a staff member may have their own duration for a treatment;
|
|
102
|
+
- "anyone available" picks the free person with the fewest bookings that
|
|
103
|
+
day, then sort order;
|
|
104
|
+
- the service time must sit inside opening hours and staff hours and outside
|
|
105
|
+
closures; buffers only keep other bookings away (as MaggieDash `planSlots`);
|
|
106
|
+
- an optional minimum gap rejects starts that would leave a gap shorter than
|
|
107
|
+
it next to another booking;
|
|
108
|
+
- group bookings of 2-6 need that many free people at the same start;
|
|
109
|
+
- slots are on the local grid and stay correct on clock-change days;
|
|
110
|
+
- peak/off-peak price rules adjust the base price by weekday and local time.
|
|
111
|
+
|
|
112
|
+
Multiple locations: run the planner per branch with that branch's hours,
|
|
113
|
+
rooms and staff; a resource (room, bed) is free only when no overlapping
|
|
114
|
+
allocation holds it.
|
|
115
|
+
|
|
116
|
+
## Team
|
|
117
|
+
|
|
118
|
+
**`permission_check`** — roles `owner`, `manager`, `reception`, `therapist`
|
|
119
|
+
carry named permissions; the owner may grant or revoke them per role, except
|
|
120
|
+
`team.manage` and `billing.manage`, which stay owner-only. "Own bookings
|
|
121
|
+
only" follows from the absence of `bookings.view_all`, never from a role
|
|
122
|
+
name. A **PIN switch** on a shared device can only lower the role
|
|
123
|
+
(owner → therapist), and the effective role applies everywhere: API checks,
|
|
124
|
+
pages, menus and actions such as clock-in — compute it once per request from
|
|
125
|
+
the session and the switched profile, and render navigation from the same
|
|
126
|
+
permission set.
|
|
127
|
+
|
|
128
|
+
**`commission`** and **`hourly_pay`** — commission per person and kind
|
|
129
|
+
(service, product) on net sales after discounts, never on tips or service
|
|
130
|
+
charge; hourly pay from clock-in/out minus unpaid breaks, per minute. A pay
|
|
131
|
+
run combines commission, hourly pay and tips per person for a period and
|
|
132
|
+
exports CSV for accounting tools (columns: period, staff name, staff
|
|
133
|
+
reference, hours, hourly pay, commission, tips, total, currency — map them to
|
|
134
|
+
Xero/QuickBooks payroll or journal imports).
|
|
135
|
+
|
|
136
|
+
## Clients
|
|
137
|
+
|
|
138
|
+
**`online_booking_gate`** — client safety before online booking: a blocked
|
|
139
|
+
client sees a neutral "please contact us" (never "you are blocked"); a
|
|
140
|
+
treatment with a patch-test category needs a recorded patch test that is not
|
|
141
|
+
older than its validity (for example 180 days) and at least 48 hours before
|
|
142
|
+
the appointment. Health questions and the business's own health items are
|
|
143
|
+
special-category data: store answers per client with consent and a version
|
|
144
|
+
of the form, show them only to staff with `clients.health`, and keep them out
|
|
145
|
+
of exports, analytics and AI prompts unless the client consented.
|
|
146
|
+
|
|
147
|
+
**`merge_clients`** — merging duplicates adds both point balances and
|
|
148
|
+
lifetime points, recomputes the tier, and keeps every history entry and
|
|
149
|
+
membership. **`membership_credits`** — memberships bill monthly on the
|
|
150
|
+
business's connected account; a paused month neither bills nor adds
|
|
151
|
+
credits.
|
|
152
|
+
|
|
153
|
+
**`link_booking_to_account`** — signed-in client accounts sit beside the
|
|
154
|
+
token links: a guest booking joins an account only when the account's e-mail
|
|
155
|
+
is verified and equal (case and spaces ignored). The account lists bookings,
|
|
156
|
+
packages, memberships and saved cards (cards stay on the business's
|
|
157
|
+
connected account; show brand, last four and expiry only). The manage-token
|
|
158
|
+
link keeps working for guests.
|
|
159
|
+
|
|
160
|
+
## Release smoke business
|
|
161
|
+
|
|
162
|
+
Keep one **hidden production smoke business** per platform so release smoke
|
|
163
|
+
tests can book, reschedule and cancel without touching real businesses:
|
|
164
|
+
excluded from search, sitemaps and directories (`noindex`), pay-at-venue or a
|
|
165
|
+
test-mode connected account only, its own staff and services, and bookings
|
|
166
|
+
cleaned up by the smoke run. `maggie booking release-gate` evidence should
|
|
167
|
+
include one full booking round trip on it after every production deploy.
|
|
@@ -67,7 +67,7 @@ loaded, the structured error output retains that prior timestamp for stale
|
|
|
67
67
|
health evaluation. An external scheduler monitor is still required to detect
|
|
68
68
|
a worker that never starts, because no in-process health report can be emitted
|
|
69
69
|
while the process is down.
|
|
70
|
-
The additive status table is now present in
|
|
70
|
+
The additive status table is now present in the reference host's staging; the six-job preview
|
|
71
71
|
there passed with `write=false`, zero alerts, and no provider dispatch. The
|
|
72
72
|
preview intentionally left `last_success_at` empty. Production still needs the
|
|
73
73
|
schema migration before the updated worker can run.
|
|
@@ -104,7 +104,7 @@ claim or delivery result. `--write` performs database writes and invokes the
|
|
|
104
104
|
host provider for each claimed notification. Reconcile staging outbox/provider
|
|
105
105
|
activity before any write-mode run.
|
|
106
106
|
|
|
107
|
-
The generic Astro PostgreSQL worker and
|
|
107
|
+
The generic Astro PostgreSQL worker and the reference host's worker include the
|
|
108
108
|
schema-versioned `health` summary in successful cycle output and caught
|
|
109
109
|
schema/lock/runtime failures. It carries stable alert codes, severity, a
|
|
110
110
|
correlation ID, and counts only; the worker does not send an alert itself.
|
|
@@ -44,11 +44,11 @@ and the [API reference](https://docs.stripe.com/api).
|
|
|
44
44
|
| Events/webhooks | Core | Idempotent payment state projection | Verify raw body before parsing |
|
|
45
45
|
| Coupons + Promotion Codes | Optional | Campaign and referral discounts | Stripe applies the discount; local rules own eligibility |
|
|
46
46
|
| Customer Balance Transactions | Optional | Stripe invoice credits | Not a gift-card ledger by itself |
|
|
47
|
-
| Billing Subscriptions + Invoices |
|
|
47
|
+
| Billing Subscriptions + Invoices | Optional | Memberships billed monthly | On a platform, create them on the business's connected account; pause/resume per [RULES.md](./RULES.md) |
|
|
48
48
|
| Customer Portal | Future | Customer-managed subscriptions and billing details | Use short-lived portal sessions |
|
|
49
49
|
| Stripe Tax | Optional | VAT/tax calculation and transaction records | Confirm jurisdiction, evidence, and price ownership |
|
|
50
|
-
| Terminal |
|
|
51
|
-
| Connect |
|
|
50
|
+
| Terminal | Optional | In-person payments at the till | Server-driven readers per branch location; simulated reader in test mode |
|
|
51
|
+
| Connect | Platform | Many businesses on one booking platform | Direct charges on each business's connected account; see "Platform hosts" below. Not needed for one business or a multi-location tenant |
|
|
52
52
|
|
|
53
53
|
The implementation maps Stripe's provider states into the existing normalized
|
|
54
54
|
states (`requires_action`, `pending`, `failed`, `succeeded`, `cancelled`, and
|
|
@@ -105,6 +105,44 @@ accounting, CRM, support, analytics, marketing, and workflow automation. It is
|
|
|
105
105
|
an integration surface, not a reason to add third-party credentials to the
|
|
106
106
|
MaggieDash browser bundle.
|
|
107
107
|
|
|
108
|
+
## Platform hosts (Stripe Connect)
|
|
109
|
+
|
|
110
|
+
A single business, or one business with several locations, uses its own
|
|
111
|
+
Stripe account and needs no Connect. A **platform** that hosts many
|
|
112
|
+
independent businesses (a booking marketplace, a salon software provider)
|
|
113
|
+
takes each business's payments on that business's own account:
|
|
114
|
+
|
|
115
|
+
- **Accounts**: one connected account per business, created and onboarded
|
|
116
|
+
with Accounts v2 (`/v2/core/accounts`, account links). Store the account
|
|
117
|
+
id on the business workspace; never let a client choose it.
|
|
118
|
+
- **Direct charges**: every Checkout Session, PaymentIntent, SetupIntent,
|
|
119
|
+
refund, subscription and Terminal call runs **on the connected account**
|
|
120
|
+
(`Stripe-Account` header / `stripeAccount` option). The business is the
|
|
121
|
+
merchant of record; its customers, saved cards and disputes live there.
|
|
122
|
+
- **Fees**: an application fee is optional and a product decision; "no
|
|
123
|
+
platform fee" is a valid configuration — leave `application_fee_amount`
|
|
124
|
+
unset rather than zero.
|
|
125
|
+
- **Events**: Connect events arrive with `event.account`. Use a Connect
|
|
126
|
+
(thin-event) destination or a Connect webhook endpoint with its own signing
|
|
127
|
+
secret. For every payment event check the **amount, currency and account**
|
|
128
|
+
against the booking before changing state; an event for another account is
|
|
129
|
+
ignored and logged.
|
|
130
|
+
- **Saved cards and off-session charges** (deposits, no-show fees) use
|
|
131
|
+
SetupIntents on the connected account with the client's consent; a card
|
|
132
|
+
saved on one business is never visible to another.
|
|
133
|
+
- **Memberships**: subscriptions on the connected account; pausing uses the
|
|
134
|
+
subscription's pause collection, and credits follow the
|
|
135
|
+
`membership_credits` rule in [RULES.md](./RULES.md).
|
|
136
|
+
- **Payouts and disputes**: read them per account (`GET /v1/payouts`,
|
|
137
|
+
`/v1/disputes` with the account header) for the business's dashboard;
|
|
138
|
+
answering disputes stays in the Stripe Dashboard or an embedded component.
|
|
139
|
+
- **Setup gate**: a business may choose deposit or full prepayment only once
|
|
140
|
+
its account can take charges (`payment_policy_setup` rule); the setup UI
|
|
141
|
+
blocks it and the release gate flags it.
|
|
142
|
+
- **QA**: `maggie booking connect-qa --detect` finds a test-mode connected
|
|
143
|
+
account and prints the `stripe listen --forward-connect-to …` command and
|
|
144
|
+
the payment cases to run.
|
|
145
|
+
|
|
108
146
|
## Live setup runbook (read-only verification)
|
|
109
147
|
|
|
110
148
|
Switching a host to live mode is the step most likely to create real objects
|
|
@@ -154,7 +192,7 @@ Any host-side live readiness check must likewise issue `GET` requests only.
|
|
|
154
192
|
4. Coupons/Promotion Codes for campaigns; add local referral attribution.
|
|
155
193
|
5. Stripe Tax if the host has the required tax configuration.
|
|
156
194
|
6. Billing/Portal for memberships; Terminal for in-person operations; Connect
|
|
157
|
-
|
|
195
|
+
for a platform that hosts many businesses (see "Platform hosts").
|
|
158
196
|
7. Gift-card partner or local ledger after accounting and redemption rules are
|
|
159
197
|
approved. The local-ledger option is fixed by the
|
|
160
198
|
[voucher lifecycle contract](./VOUCHERS.md).
|
|
@@ -173,9 +173,22 @@ rules from a redacted JSON manifest; see the Social Share skill for shapes.
|
|
|
173
173
|
|
|
174
174
|
## Structured model output
|
|
175
175
|
|
|
176
|
-
Keep structured-output schemas small.
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
Keep structured-output schemas small. The limit is **schema complexity**, not
|
|
177
|
+
vocabulary size: roughly enum values × repeated array items × nesting.
|
|
178
|
+
Gemini rejected a JSON schema whose enum had 125 values with
|
|
179
|
+
`400 INVALID_ARGUMENT`, and the caller silently fell back for every request.
|
|
180
|
+
It also rejected a plan schema with a 5-6 value `template_key` enum on each
|
|
181
|
+
of up to 20 array items (`maxItems: 20`) — immediately and with no detail —
|
|
182
|
+
while 4 values or `maxItems: 10` passed. So:
|
|
183
|
+
|
|
184
|
+
- never put an `enum` inside repeated array items; describe the allowed
|
|
185
|
+
values in the prompt or the field `description` and validate server-side;
|
|
186
|
+
- keep `maxItems` small on arrays of objects, and always set it;
|
|
187
|
+
- run `maggie tool maggie_social_share.py schema-check schema.json`
|
|
188
|
+
(`tools/clis/maggie_schema_complexity.py`) before a release; it fails on
|
|
189
|
+
the patterns above and reports an estimated complexity score.
|
|
190
|
+
|
|
191
|
+
For a large vocabulary (styles, categories, catalogue IDs):
|
|
179
192
|
|
|
180
193
|
1. ask for a free string (optionally with a short list of examples in the
|
|
181
194
|
prompt) instead of a large `enum`;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
6
6
|
"name": "maggie-assistant-tools",
|
|
7
|
-
"description": "Build an AI assistant's tools once and expose them safely to an in-app agent, an MCP server and ChatGPT/Claude apps. Use for tool registries, risk levels and gates, approval cards with signed one-time tokens, audit logs, MCP annotations, user-facing capability pages, publishing product CLI/MCP packages and app reviews, and multi-tenant assistant bridge hosts."
|
|
7
|
+
"description": "Build an AI assistant's tools once and expose them safely to an in-app agent, an MCP server and ChatGPT/Claude apps. Use for tool registries, risk levels and gates, result cards, approval cards with signed one-time tokens and standing workflow approvals, audit logs, MCP annotations, public discovery MCP and audience binding, visitor-facing receptionists, user-facing capability pages, publishing product CLI/MCP packages and app reviews, and multi-tenant assistant bridge hosts."
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"name": "maggie-auth-reference",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: maggie-assistant-tools
|
|
3
|
-
description: Build an AI assistant's tools once and expose them safely to an in-app agent, an MCP server and ChatGPT/Claude apps. Use for tool registries, risk levels and gates, approval cards with signed one-time tokens, audit logs, MCP annotations, user-facing capability pages, publishing product CLI/MCP packages and app reviews, and multi-tenant assistant bridge hosts.
|
|
3
|
+
description: Build an AI assistant's tools once and expose them safely to an in-app agent, an MCP server and ChatGPT/Claude apps. Use for tool registries, risk levels and gates, result cards, approval cards with signed one-time tokens and standing workflow approvals, audit logs, MCP annotations, public discovery MCP and audience binding, visitor-facing receptionists, user-facing capability pages, publishing product CLI/MCP packages and app reviews, and multi-tenant assistant bridge hosts.
|
|
4
4
|
metadata:
|
|
5
5
|
version: 1.0.0
|
|
6
6
|
---
|
|
@@ -31,7 +31,9 @@ all read that one definition. Per tool:
|
|
|
31
31
|
| `risk` | `read`, `write-reversible`, `write-spends` (credits, quota, AI generation), `write-public` (public or hard to undo), `never` (the assistant must not do it) — `R0`…`W4` are accepted aliases |
|
|
32
32
|
| `gates` | declared preconditions, e.g. `verified-site`, `credits`, `ai-generation` |
|
|
33
33
|
| `scope` | `account` or `workspace`; account writes are audited at account scope |
|
|
34
|
-
| `channels` | `in-app`, `mcp`, `chatgpt`, `claude`, `cli` |
|
|
34
|
+
| `channels` | `in-app`, `mcp`, `chatgpt`, `claude`, `cli`, `web-widget` (visitors) |
|
|
35
|
+
| `card` | optional result card: `place`, `slot-picker`, `booking`, `voucher`, `change-preview`, `numbers`, `draft`, `list` |
|
|
36
|
+
| `mcpServers` | which declared MCP servers expose the tool (public discovery vs signed-in) |
|
|
35
37
|
|
|
36
38
|
Write user copy by hand. Rewriting model descriptions with regexes ("the
|
|
37
39
|
user" → "you", dropping `(from connected_apps_list)`) needs overrides for a
|
|
@@ -55,6 +57,50 @@ tools are not served on MCP; list them on the capability page instead.
|
|
|
55
57
|
you approve" and "publishes or is hard to undo", plus a "can't do" section
|
|
56
58
|
from `cantDo` entries and `never` tools, using only user copy.
|
|
57
59
|
|
|
60
|
+
## Result cards and conversations
|
|
61
|
+
|
|
62
|
+
A tool may declare a `card`: the app and ChatGPT/Claude widgets render the
|
|
63
|
+
result's structured content as that card (a place, a slot picker that opens
|
|
64
|
+
booking, a change preview before approval, a set of numbers, a draft), and
|
|
65
|
+
every other surface shows the text result, which the tool must still return.
|
|
66
|
+
A `change-preview` card belongs to a write: it is what the approval card
|
|
67
|
+
shows. Keep one card component per type and reuse it across tools.
|
|
68
|
+
|
|
69
|
+
Declare where conversations live with `threads`. Server-stored threads let a
|
|
70
|
+
person continue on another device and let support see a conversation **with
|
|
71
|
+
consent**; they need `retentionDays`, `supportAccess` (`none` or
|
|
72
|
+
`with-consent`) and `deleteOnAccountDeletion: true`. Keep approval tokens out
|
|
73
|
+
of the stored thread text (they live with the model turn, see below).
|
|
74
|
+
|
|
75
|
+
## MCP servers: public discovery and signed-in
|
|
76
|
+
|
|
77
|
+
Declare servers in `mcpServers`:
|
|
78
|
+
|
|
79
|
+
- **Public discovery** (`auth: "none"`) lets AI assistants and AI search
|
|
80
|
+
answer from published data: search, details, an availability summary and a
|
|
81
|
+
booking link. Read tools only, never account-scoped, and a `rateLimit`
|
|
82
|
+
per IP (per minute and/or per day). Serve only what the public web pages
|
|
83
|
+
already show; no customer data.
|
|
84
|
+
- **Signed-in** (`auth: "oauth"`) binds every token to the server: `resource`
|
|
85
|
+
is the canonical https URL, clients send it as the RFC 8707 resource
|
|
86
|
+
indicator, and the server rejects tokens whose audience is another
|
|
87
|
+
resource. Binding is on by default; turning it off needs an
|
|
88
|
+
`audienceException` with the reason. Publish protected-resource metadata
|
|
89
|
+
(RFC 9728) and check it with `maggie product mcp-check`.
|
|
90
|
+
|
|
91
|
+
## A visitor-facing receptionist
|
|
92
|
+
|
|
93
|
+
A chat on a business's public page answers strangers, so no approval card is
|
|
94
|
+
possible. Tools on the `web-widget` channel must be read-only and
|
|
95
|
+
workspace-scoped, and the registry needs a `visitorPolicy`: off until the
|
|
96
|
+
business turns it on, daily caps per visitor and per business, every answer
|
|
97
|
+
logged for the business, answers from the business's own facts only, and
|
|
98
|
+
never card details. See the [booking receptionist
|
|
99
|
+
example](../../bundled-contracts/maggie-assistant-tools/booking-receptionist-example-v1.json)
|
|
100
|
+
and [receptionist notes](references/booking-receptionist.md): it finds real
|
|
101
|
+
free times and opens the normal booking sheet; it never books or takes a
|
|
102
|
+
card.
|
|
103
|
+
|
|
58
104
|
## Approvals and audit
|
|
59
105
|
|
|
60
106
|
Read [approvals and audit](references/approvals-and-audit.md) before
|
|
@@ -62,6 +108,11 @@ implementing writes. In short: a write runs only with a signed, short-lived,
|
|
|
62
108
|
single-use approval bound to the exact calls, caller and scope; the token is
|
|
63
109
|
stored with the model turn on the server so a client answering only yes/no
|
|
64
110
|
works; every write produces one audit entry with a readable summary.
|
|
111
|
+
Scheduled and event-driven workflows use a **standing approval**: a signed
|
|
112
|
+
grant for one workflow, its tools and per-tool limits (per day, per run, fixed
|
|
113
|
+
arguments, maximum amount) that expires within 90 days and can be revoked;
|
|
114
|
+
calls outside the limits fall back to a normal approval card, and each run is
|
|
115
|
+
audited with channel `workflow` and the grant id.
|
|
65
116
|
|
|
66
117
|
```bash
|
|
67
118
|
maggie assistant approval-selftest
|
|
@@ -87,7 +138,7 @@ See [bridge host](references/bridge-host.md) and
|
|
|
87
138
|
## Hard boundaries
|
|
88
139
|
|
|
89
140
|
- Never let the model see or choose an approval token; never accept a write
|
|
90
|
-
without one.
|
|
141
|
+
without one (or a standing approval whose limits cover the call).
|
|
91
142
|
- Never expose `never` tools or billing changes on MCP or third-party apps.
|
|
92
143
|
- Never publish a package or submit an app for review without an explicit
|
|
93
144
|
yes; these commands only check.
|
|
@@ -52,3 +52,30 @@ One entry per write, written by the server after the call:
|
|
|
52
52
|
`maggie assistant audit-check` replays a recorded session
|
|
53
53
|
(`{calls: [{tool, workspace, actionId, ok}], audit: [...]}`) against the
|
|
54
54
|
registry and fails on missing, duplicate, unreadable or mis-scoped entries.
|
|
55
|
+
|
|
56
|
+
## Standing approvals for workflows
|
|
57
|
+
|
|
58
|
+
Approvals are single-use per turn, which makes automations ("when someone
|
|
59
|
+
cancels, offer the slot to the waitlist", "ask for a review the day after a
|
|
60
|
+
visit") ask every time. A standing approval is a signed grant the owner gives
|
|
61
|
+
once for one workflow:
|
|
62
|
+
|
|
63
|
+
- **Bound to** the owner, the workspace, the workflow id and a list of tool
|
|
64
|
+
grants. Each grant has `maxPerDay` (required), optional `maxPerRun`,
|
|
65
|
+
`fixedArgs` (for example the e-mail template) and `maxAmountMinor` for
|
|
66
|
+
money.
|
|
67
|
+
- **Expires** within 90 days; the owner renews it from the workflow screen.
|
|
68
|
+
Revocation is a server-side list checked on every run.
|
|
69
|
+
- **Every run** is checked against the grant and the day's usage on the
|
|
70
|
+
server, gets an idempotency key from the grant id and the call, and is
|
|
71
|
+
audited with channel `workflow` and `standingApprovalId`.
|
|
72
|
+
- **Outside the limits** (another tool, a different template, the daily cap
|
|
73
|
+
reached, an amount above the maximum, expired or revoked) the call is not
|
|
74
|
+
run; the workflow queues a normal approval card for the owner instead.
|
|
75
|
+
- Never grant `never` tools; prefer narrow grants (one template, a small
|
|
76
|
+
daily cap) over broad ones.
|
|
77
|
+
|
|
78
|
+
The reference implementation is `issue_standing` / `check_standing` /
|
|
79
|
+
`StandingStore` in `tools/runtime/assistant_approval.py`;
|
|
80
|
+
`maggie assistant approval-selftest` exercises it, and `audit-check` rejects
|
|
81
|
+
workflow audit entries without the grant id.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# AI receptionist for a booking page
|
|
2
|
+
|
|
3
|
+
A receptionist answers visitors on one business's public page: "Do you do
|
|
4
|
+
lash lifts?", "How much is a 60-minute massage?", "Anything on Saturday
|
|
5
|
+
morning?". It is a reader and a guide, never an agent that books.
|
|
6
|
+
|
|
7
|
+
## What it may do
|
|
8
|
+
|
|
9
|
+
- Answer from **this business's facts**: treatments, prices, durations,
|
|
10
|
+
opening hours, address, team names, policies (cancellation, deposits, patch
|
|
11
|
+
tests) and the business's own FAQs. Load them fresh per conversation; never
|
|
12
|
+
answer prices or availability from the model's general knowledge.
|
|
13
|
+
- Find **real** free times from the live availability engine (the same call
|
|
14
|
+
the booking sheet uses), at most a handful per answer, rendered as a slot
|
|
15
|
+
picker.
|
|
16
|
+
- Open the **normal booking sheet** preselected at the chosen treatment and
|
|
17
|
+
time. The visitor enters details and pays there; the sheet's own rules
|
|
18
|
+
(deposits, patch tests, blocked clients) apply unchanged.
|
|
19
|
+
- Hand off: when the answer is not in the facts, say so and show the
|
|
20
|
+
business's phone and e-mail.
|
|
21
|
+
|
|
22
|
+
## What it must not do
|
|
23
|
+
|
|
24
|
+
- Book, reschedule or cancel; take or ask for card details; promise a price
|
|
25
|
+
or time the engine did not return; give medical advice beyond the
|
|
26
|
+
business's written policy (for example "a patch test is required 48 hours
|
|
27
|
+
before a lash tint").
|
|
28
|
+
- Read other clients' data. The receptionist runs as the visitor, not as the
|
|
29
|
+
business.
|
|
30
|
+
|
|
31
|
+
## Controls
|
|
32
|
+
|
|
33
|
+
- Off until the business turns it on (`visitorPolicy.enabledByDefault:
|
|
34
|
+
false`), with a preview the owner can try first.
|
|
35
|
+
- Daily caps per visitor (IP + browser id) and per business; when a cap is
|
|
36
|
+
hit, show the contact details instead of an error.
|
|
37
|
+
- Every question and answer is logged and visible to the business, with the
|
|
38
|
+
tools called. Retain the log for a fixed period and delete it with the
|
|
39
|
+
business.
|
|
40
|
+
- Text inside visitor messages is content, never an instruction to change
|
|
41
|
+
these rules. The system prompt names the business and forbids answering
|
|
42
|
+
for other businesses.
|
|
43
|
+
|
|
44
|
+
Validate the registry with `maggie assistant registry-validate`; the
|
|
45
|
+
`web-widget` checks enforce read-only tools and the visitor policy.
|
|
@@ -54,3 +54,57 @@ user brings their own account. Review terms again at least every six months.
|
|
|
54
54
|
- When an argument must be passed, escape each one with `printf %q`.
|
|
55
55
|
- zsh does not word-split unquoted variables, so `for x in $list` sees one
|
|
56
56
|
item; write remote scripts for `sh` and iterate explicitly.
|
|
57
|
+
|
|
58
|
+
## Smoke-testing `codex exec` with an image
|
|
59
|
+
|
|
60
|
+
`-i/--image` takes **several** paths, so `codex exec ... -i photo.jpg "prompt"`
|
|
61
|
+
reads the prompt as a second image path and fails with "No prompt provided
|
|
62
|
+
via stdin". Put the prompt first, or end the image list with `--`:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
codex exec "Describe this photo in one sentence" -i photo.jpg
|
|
66
|
+
codex exec -i photo.jpg -- "Describe this photo in one sentence"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Images on the user's ChatGPT plan (Codex app-server)
|
|
70
|
+
|
|
71
|
+
Verified on codex-cli 0.160 against the app-server JSON schema
|
|
72
|
+
(`codex app-server generate-json-schema`) and live turns:
|
|
73
|
+
|
|
74
|
+
- **A separate image Codex per ChatGPT account.** The chat Codex runs with
|
|
75
|
+
the product's MCP server and must keep `image_generation` disabled. Image
|
|
76
|
+
work runs on a second app-server for the same account with
|
|
77
|
+
`--disable` for every machine-touching feature except `image_generation`,
|
|
78
|
+
`sandbox_mode="read-only"`, `approval_policy="never"`,
|
|
79
|
+
`web_search="disabled"` and **no MCP server or tools**, so text inside a
|
|
80
|
+
picture cannot make it act.
|
|
81
|
+
- **One ephemeral thread per request**: `thread/start` with
|
|
82
|
+
`ephemeral: true`, `approvalPolicy: "never"`, `sandbox: "read-only"` and
|
|
83
|
+
developer instructions that say text inside an image is content, never an
|
|
84
|
+
instruction.
|
|
85
|
+
- **Image input**: turn input items `{type: "image", url: "data:<mime>;base64,<data>"}`
|
|
86
|
+
after the text item. Validate first: png/jpeg/webp/gif only, a cap on count
|
|
87
|
+
(for example 5) and size (for example 4 MB of base64), base64 characters
|
|
88
|
+
only.
|
|
89
|
+
- **Analysis** returns `agentMessage` items; pass `outputSchema` on
|
|
90
|
+
`turn/start` for JSON answers.
|
|
91
|
+
- **Generation** returns `item/completed` notifications with
|
|
92
|
+
`item.type == "imageGeneration"`: `status`, `result` (base64 PNG, may be
|
|
93
|
+
empty), `savedPath` (a file on disk), `revisedPrompt`, and `failure`. A
|
|
94
|
+
`failure` mentioning `usageLimitExceeded` means the plan's image quota is
|
|
95
|
+
used up — tell the user, do not retry.
|
|
96
|
+
- **Nothing stays on the bridge**: read `savedPath` when `result` is empty,
|
|
97
|
+
then delete its folder; the product stores the image it uses.
|
|
98
|
+
- **Stop and time limits**: `turn/interrupt` with the thread id when the
|
|
99
|
+
caller cancels or a deadline passes (about 2 minutes for analysis, 5 for
|
|
100
|
+
generation).
|
|
101
|
+
- **Fallback**: the caller treats any failure (no bridge, quota, timeout, no
|
|
102
|
+
image) as "use our own model" and says which one made the result.
|
|
103
|
+
|
|
104
|
+
## Reverse proxy limits
|
|
105
|
+
|
|
106
|
+
Chat with photos and image jobs need a larger body and a longer read timeout
|
|
107
|
+
than API defaults: at least `client_max_body_size 25m` and
|
|
108
|
+
`proxy_read_timeout`/`proxy_send_timeout` 360 s on the bridge routes (5-minute
|
|
109
|
+
generations plus margin). `maggie deployment bridge-check` compares the
|
|
110
|
+
observed nginx values with the plan's `proxy` block.
|
|
@@ -39,3 +39,21 @@ table, run that mirror after the primary provider session succeeds. A missing
|
|
|
39
39
|
optional table or temporary mirror failure must be a safe, redacted warning and
|
|
40
40
|
must not reject a valid primary session. Make the mirror blocking only when the
|
|
41
41
|
host has explicitly documented it as part of the authentication contract.
|
|
42
|
+
|
|
43
|
+
## External identity providers: Firebase session cookies
|
|
44
|
+
|
|
45
|
+
When the host uses Firebase Authentication with server session cookies,
|
|
46
|
+
`verifySessionCookie(cookie, true)` (`checkRevoked`) compares the cookie's
|
|
47
|
+
`auth_time` with the user's `tokensValidAfterTime`, both in whole seconds. A
|
|
48
|
+
user created (or whose tokens were revoked) in the same second as the cookie
|
|
49
|
+
was minted is treated as revoked: `auth/session-cookie-revoked`, intermittently.
|
|
50
|
+
|
|
51
|
+
- QA fixtures that create a user and immediately sign in must wait about
|
|
52
|
+
1.5 s after `createUser` (or after `revokeRefreshTokens`) before minting the
|
|
53
|
+
ID token and session cookie.
|
|
54
|
+
- Production sign-up flows hit the same race when they create the account and
|
|
55
|
+
the session in one request; mint the session from the client's fresh ID
|
|
56
|
+
token after creation, and on `auth/session-cookie-revoked` right after
|
|
57
|
+
sign-up retry once after a short delay instead of signing the person out.
|
|
58
|
+
- Keep `checkRevoked: true` on sensitive routes; do not turn it off to hide
|
|
59
|
+
the race.
|
|
@@ -56,6 +56,14 @@ Search/sort views are not indexable; drafts never appear in public routes, RSS,
|
|
|
56
56
|
or sitemap output. Provider keys remain server-side. Public publication and
|
|
57
57
|
migrations always require explicit confirmation.
|
|
58
58
|
|
|
59
|
+
API Pull deliveries need their live URL recorded so social posts and internal
|
|
60
|
+
links point at the page that exists now: the site reports it on publish
|
|
61
|
+
(`maggie blog live-urls record --content-id ID --url URL`), and
|
|
62
|
+
`maggie blog live-urls match --sitemap URL --write` fills the gaps
|
|
63
|
+
additively — it never retires an earlier match, and the original page wins
|
|
64
|
+
over translated copies that share the slug. See
|
|
65
|
+
[where a delivered post went live](../../bundled-tools/integrations/maggie-api-pull.md#where-a-delivered-post-went-live).
|
|
66
|
+
|
|
59
67
|
Automatic publication is an opt-in host policy, not the default. Keep human
|
|
60
68
|
review enabled unless the owner explicitly chooses otherwise. If a host has
|
|
61
69
|
that approval, validate its evidence before its scheduler calls the host's
|