frontpage-host 0.3.14 → 0.3.16

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/AGENTS.md CHANGED
@@ -264,7 +264,7 @@ Manual group selection uses `groups.people` (20 per page, phone-only contacts in
264
264
 
265
265
  ## AI Support Agent
266
266
 
267
- Scale and internal sites, after owner activation. No pilot allowlist. Use `frontpage customer-inbox get|put|post RESOURCE`, stable write keys and current versions. Full guide: [CUSTOMER-INBOX.md](./CUSTOMER-INBOX.md). `export CONVERSATION_ID --out FILE` writes authorized paginated history and file metadata; `download FILE_ID --out FILE` downloads through a file grant. Outputs never overwrite existing files. `frontpage usage` reports shared site credits, mail and storage. The owner toolbar uses `manage_customer_inbox` for all site-wide Inbox settings, unlocked knowledge, files, customer conversations, private guidance, replies, takeover/return and recovery. The support agent has a separate server-bound customer scope and requires verification for People data. Both agents cannot edit or unlock locked knowledge; human UI/CLI can unlock it. Never lend a website agent a human CLI key.
267
+ Scale and internal sites, after owner activation. No pilot allowlist. Use `frontpage customer-inbox get|put|post RESOURCE`, stable write keys and current versions for settings, sources and conversations. Knowledge saves use last-save-wins; read the latest note before editing. Knowledge history supports limit/cursor pagination and full revision reads by version, within the plan Rewind allowance plus the latest revision. Archive notes through POST knowledge/ID/archive with {} to stop future use while preserving all records; do not archive without the owner asking. Full guide: [CUSTOMER-INBOX.md](./CUSTOMER-INBOX.md). `export CONVERSATION_ID --out FILE` writes authorized paginated history and file metadata; `download FILE_ID --out FILE` downloads through a file grant. Outputs never overwrite existing files. `frontpage usage` reports shared site credits, mail and storage. The owner toolbar uses `manage_customer_inbox` for all site-wide Inbox settings, unlocked knowledge, files, customer conversations, private guidance, replies, takeover/return and recovery. The support agent has a separate server-bound customer scope and requires verification for People data. Both agents cannot edit, archive or unlock locked knowledge; human UI/CLI can unlock it. Never lend a website agent a human CLI key.
268
268
 
269
269
 
270
270
  Payments, invoices and recurring billing: see packaged [PAYMENTS.md](./PAYMENTS.md), `frontpage help payments`, and https://frontpage.host/developers/payments. Version 0.2.0 removes writable currency, Store checkout mode and Store-owned money commands; Store stays cart-only. Preview/review before sends; keep stable operation keys on retries. Autopay requires the customer’s own verified authorization.
@@ -282,6 +282,8 @@ AI Support Agent team guidance: administrators may set `guidanceEmail` through t
282
282
 
283
283
  ## Emails workspace workflows
284
284
 
285
+ Campaign timing uses the existing `mail save --file` and `mail campaign preview|send|get` commands. Read [MAIL.md, Campaign scheduling](./MAIL.md#campaign-scheduling) before using it. Save document.scheduledAt as a future ISO timestamp with an explicit timezone, null for Send now, or omit to retain it. Saving never confirms sending; overdue drafts remain drafts. Read settings for the business timezone. Review the post-save content timestamp, exact audience, eligible count and saved schedule before campaign send with a stable requestKey. Confirmed groups/filters resolve once, and normal delivery restrictions remain. The owner agent uses save_mail_editor then manage_people area:campaign; MCP uses save_mail_editor and campaign.preview/send/get. Direct saved-email sends cannot bypass a dated campaign. Reuse exact requests after uncertain responses.
286
+
285
287
  See [MAIL.md](./MAIL.md) for the complete save contract, reusable people picker directory, saved campaign recipients, Off-series enrollment and resume timing, all sender settings/null resets, suppression rules, Sent queries and details, and retry-safe examples. `frontpage mail save --file save.json` saves template and pending series together; it never sends. New series default On; existing series retain their saved state. Save email steps before enrolling anyone. Check the returned series status before attaching it to a rule; an Off series holds enrollment until turned On. `frontpage mail people` reads the same site-scoped directory as the workspace. Use `frontpage help mail --json` for current commands and flags. No new CLI binary, API namespace or migration command is required. Hosted guides and article help must be updated through the Frontpage CLI.
286
288
 
287
289
  Rule placement: read a rule revision, then use `people workspace rules.place` with `id`, `owner` and `expectedRevision`. This only organizes the rule; it does not change its trigger or execution. New UI rules/attachments save paused. Generic attachment calls still need explicit `enabled:false` for paused creation. Saved optional conditions and exact duration minutes remain supported through CLI and agent calls.
package/CUSTOMER-INBOX.md CHANGED
@@ -50,9 +50,11 @@ Aliases use 3–48 ASCII letters, numbers and single hyphens. The service normal
50
50
  | --- | --- | --- |
51
51
  | `knowledge` | GET | Recent notes; optional `?q=search` |
52
52
  | `knowledge/<id>` | GET | One full note |
53
- | `knowledge/<id>/history` | GET | Immutable revisions for that note |
54
- | `knowledge` | PUT | `{"title":"Evening visits","body":"Ask the owner before promising an evening visit.","expectedVersion":0,"reason":"Owner policy"}` |
55
- | `knowledge/<id>` | PUT | Current title/body, `expectedVersion`, `reason`, optional `locked` or `restoreVersion` |
53
+ | `knowledge/<id>/history?limit=20` | GET | Revision summaries, `nextCursor`, and the plan history allowance; follow `&cursor=...` |
54
+ | `knowledge/<id>/history?version=3` | GET | One full revision within the plan allowance, or the latest version |
55
+ | `knowledge/<id>/archive` | POST | `{}`; keeps content and history, removes the note from future use |
56
+ | `knowledge` | PUT | `{"title":"Evening visits","body":"Ask the owner before promising an evening visit.","reason":"Owner policy"}` |
57
+ | `knowledge/<id>` | PUT | Current title/body, `reason`, optional `locked` or `restoreVersion`; legacy `expectedVersion` is accepted but does not reject a save |
56
58
  | `sources` | GET | Approved source records; `?overview=true` also includes the latest learning job status |
57
59
  | `sources` | PUT | `{"url":"https://example.com/help","enabled":true,"expectedVersion":0}`; fetches public text |
58
60
 
@@ -73,7 +75,19 @@ The response data is the same source with `enabled:false` and version `3`; its s
73
75
 
74
76
  Supply `body` on a source write to save an owner-approved excerpt instead of fetching. Published sources refresh when the agent reads a source older than 24 hours. The fetcher reuses Core's DNS-pinned public HTTP infrastructure, rejects private addresses, checks redirects and bounds content. Sources over 16000 text characters require an owner-approved excerpt. No scripts run. A source version/digest fences knowledge updates against a changed source.
75
77
 
76
- Knowledge search uses bounded literal keywords from natural-language queries and ranks notes by matched terms. Read the full matching note before updating it; update its current ID/version instead of creating a conflicting duplicate.
78
+ Knowledge search uses bounded literal keywords from natural-language queries and ranks notes by matched terms. Read the full matching note before updating it; read its latest content and update that same ID instead of creating a duplicate. Knowledge uses last-save-wins: two saves can both succeed; the last completed save becomes current, and each saved change stays in history. This does not change version checks for settings, approved sources, or conversations.
79
+
80
+ History uses the same central Rewind allowance as template history. Scale includes 90 days; internal sites have uncapped history. The latest revision stays available regardless of age. Older snapshots remain stored, but history reads and restores only allow revisions within the allowance or the latest revision. A paginated response includes `revisions` (version, actor, createdAt), `currentVersion`, `nextCursor`, `windowDays`, `unlimited`, `canUpgrade`, and `moreBeyondWindow`. A `version` request returns `{revision}` with full saved content. Older callers that omit pagination keep their full-revision response shape, bounded to 100 entries and the same allowance.
81
+
82
+ ```bash
83
+ frontpage customer-inbox get 'knowledge/NOTE_UUID/history?limit=20' --site my-site --json
84
+ frontpage customer-inbox get 'knowledge/NOTE_UUID/history?limit=20&cursor=21' --site my-site --json
85
+ frontpage customer-inbox get 'knowledge/NOTE_UUID/history?version=3' --site my-site --json
86
+ # archive.json contains {}. Use the same file and key when retrying.
87
+ frontpage customer-inbox post knowledge/NOTE_UUID/archive --file archive.json --request-key archive_note_001 --site my-site --json
88
+ ```
89
+
90
+ Archiving retains the note and every revision, hides it from normal lists/searches and future agent reads, and prevents later saves from reactivating it. There is no archived-notes screen or permanent-delete action. Runs already in progress keep what they loaded. Archiving does not remove matching information from approved webpages or replies already sent.
77
91
 
78
92
  Lock a knowledge note with `locked:true`. Neither the support agent nor the owner toolbar agent can change locked notes or locks. Human UI/CLI actions can unlock them. Restore an older note with `restoreVersion`; the service creates a new revision and preserves the old history. The agent can change only internal knowledge, using verified owner guidance or a current approved source. It cannot edit public pages or articles.
79
93
 
@@ -372,7 +386,7 @@ The support agent has its own system prompt plus the versioned private business
372
386
  | --- | --- | --- |
373
387
  | `knowledge_read` | `query`, `id` or `sourceId` | Search internal notes or read complete approved evidence. |
374
388
  | `history_read` | `before`, optional `query` | Retrieve up to ten older eligible messages in the assigned conversation under the `knowledge_read` grant; bounded to 6000 text characters. |
375
- | `knowledge_update` | Optional `id`; `title`, `body`, `expectedVersion`, `reason`, `evidenceKind`, `evidenceId`; source `evidenceVersion` | Available only in a restricted learning job. Update unlocked business facts from that job's assigned owner guidance or approved source revision. One-off guidance stays in the conversation. No public editing. |
389
+ | `knowledge_update` | Optional `id` and legacy `expectedVersion`; `title`, `body`, `reason`, `evidenceKind`, `evidenceId`; source `evidenceVersion` | Available only in a restricted learning job. Update unlocked business facts from that job's assigned owner guidance or approved source revision. One-off guidance stays in the conversation. No public editing. |
376
390
  | `customer_read` | `sections` (`profile`, `notes`, `orders`, `activity`), optional `before` | Read only the request-bound verified customer. Notes page 25 at a time; orders page 20. A cursor must belong to that same customer and site. Order ownership ambiguity for an app identity returns `account_mapping_required`, with no orders. |
377
391
  | `verify_customer` | None | Request proof at the server-bound mailbox and pause. Resume unanswered work after confirmation and People projection. No recipient selector. |
378
392
  | `customer_update` | `name`/`phone`, `evidenceMessageId` | Enrich from an explicit customer statement; cannot change identity or permission. |
package/MAIL.md CHANGED
@@ -100,6 +100,89 @@ Include `audience` with a one_off document in the same `mail save` request. Supp
100
100
 
101
101
  For an audience-only change, the existing `mail campaign audience --id DOCUMENT_ID --file audience.json` remains supported. Its JSON is `{"audience":{"personIds":["PERSON_ID"]}}`, or `{"audience":null}` to clear. Saved selection is intent; final send review resolves groups and filters again, checks permission and delivery restrictions, and freezes the actual recipients and content. Started campaigns cannot be edited or sent twice with a new key. Read `PEOPLE.md` for preview/send review and stable send keys.
102
102
 
103
+ ## Campaign scheduling
104
+
105
+ The workspace, CLI, owner agent and MCP use the same saved campaign and confirmation service. Send now is the default. Saving a date never confirms sending: a draft stays a draft even after that date passes. Only an explicit campaign confirmation makes it eligible for the worker. The worker selects confirmed (`pending`) campaigns whose scheduled time has arrived, or whose scheduled time is null. Its existing minute pass and delivery checks can delay sending; the selected time is the earliest send time, not a delivery guarantee.
106
+
107
+ | Action | CLI | Owner agent | MCP |
108
+ | --- | --- | --- | --- |
109
+ | Read content | `mail email get ID --json` | `get_mail_email` | `frontpage_mail_read`, operation `get_mail_email` |
110
+ | Read timezone | `mail settings get --json` | `set_mail_settings({})` (read only) | `frontpage_mail_read`, operation `get_mail_settings` |
111
+ | Save draft, audience and timing | `mail save --file draft.json` | `save_mail_editor` | `frontpage_mail_write`, operation `save_mail_editor` |
112
+ | Review audience | `mail campaign preview --file audience.json` | `manage_people`, area `campaign`, operation `preview` | `frontpage_mail_read`, operation `campaign.preview` |
113
+ | Confirm sending or scheduling | `mail campaign send --id ID --file review.json --request-key KEY` | `manage_people`, area `campaign`, operation `send` | `frontpage_mail_write`, operation `campaign.send` |
114
+ | Read saved audience, timing and status | `mail campaign get --id ID --json` | `manage_people`, area `campaign`, operation `get` | `frontpage_mail_read`, operation `campaign.get` |
115
+
116
+ Use `--site YOUR_SITE --json` with each CLI command. Agent calls bind the current site; never supply a siteId. MCP calls take `slug`, `operation` and `payload`, with the same fields inside payload. Existing role, plan, consent and delivery checks apply.
117
+
118
+ ### Save a schedule without sending
119
+
120
+ Read the current campaign and `mail settings get` first. Convert the owner's chosen local date and time using the saved IANA timezone. `document.scheduledAt` accepts a future ISO timestamp with `Z` or an explicit offset. Do not guess an offset or silently shift a time that does not exist during daylight-saving changes. Ask for clarification if a repeated local time is ambiguous.
121
+
122
+ - A timestamp selects Schedule for later.
123
+ - `null` selects Send now and clears the saved date. It still does not send.
124
+ - Omitting the field preserves existing timing.
125
+ - Templates and series do not accept this field.
126
+
127
+ Example `draft.json` for an existing campaign; replace IDs, timestamps, content and audience with the current reviewed values. Omit id and expectedUpdatedAt when creating a new campaign. Choose a future date for the actual request.
128
+
129
+ ```json
130
+ {
131
+ "requestKey": "UNIQUE_SAVE_KEY",
132
+ "document": {
133
+ "id": "CAMPAIGN_DOCUMENT_ID",
134
+ "expectedUpdatedAt": "EXACT_CURRENT_DOCUMENT_TIMESTAMP",
135
+ "kind": "one_off",
136
+ "name": "November update",
137
+ "subject": "Your November update",
138
+ "preheader": "News from our team.",
139
+ "blocks": [{"type":"paragraph","text":"Here is our latest news."}],
140
+ "scheduledAt": "2030-11-15T09:00:00-06:00"
141
+ },
142
+ "audience": {"personIds":["PERSON_ID"]}
143
+ }
144
+ ```
145
+
146
+ ```sh
147
+ frontpage mail save --file draft.json --site YOUR_SITE --json
148
+ frontpage mail email get CAMPAIGN_DOCUMENT_ID --site YOUR_SITE --json
149
+ frontpage mail campaign get --id CAMPAIGN_DOCUMENT_ID --site YOUR_SITE --json
150
+ frontpage mail campaign preview --file audience.json --site YOUR_SITE --json
151
+ ```
152
+
153
+ `audience.json` contains `{"audience":{"personIds":["PERSON_ID"]}}`. Supported choices also include `groupIds`, `stageIds`, `filters`, and `addresses`. The UI's Choose people option uses personIds; List individual email addresses uses addresses. Read `campaign get` to reuse a saved selection. When changing audience sources, send only the new source rather than merging the old selection. Use `audience:null` in `mail save` to clear it. Clearing or replacing an audience does not clear the saved schedule. Explicit addresses do not create People records or grant email permission.
154
+
155
+ ### Review and confirm
156
+
157
+ Show the owner the content, eligible recipient count, and send date/time in the business timezone before confirmation. Fetch current content and campaign state after saving, and preview the exact audience. The confirmation file `review.json` carries those reviewed values:
158
+
159
+ ```json
160
+ {
161
+ "review": {
162
+ "audience": {"personIds":["PERSON_ID"]},
163
+ "eligible": 1,
164
+ "documentUpdatedAt": "EXACT_POST_SAVE_DOCUMENT_TIMESTAMP",
165
+ "scheduledAt": "2030-11-15T15:00:00.000Z"
166
+ }
167
+ }
168
+ ```
169
+
170
+ ```sh
171
+ # This is the confirmation: it authorizes sending when due.
172
+ frontpage mail campaign send --id CAMPAIGN_DOCUMENT_ID --file review.json --request-key UNIQUE_SEND_KEY --site YOUR_SITE --json
173
+ frontpage mail campaign get --id CAMPAIGN_DOCUMENT_ID --site YOUR_SITE --json
174
+ ```
175
+
176
+ Copy the canonical UTC scheduledAt from the saved response into review, or null for Send now. Use the exact post-save document timestamp and preview's eligible count; never use a timestamp from before an audience save. The service rejects a changed reviewed audience, count, content timestamp or scheduled time. A past date needs a new future date or Send now before confirmation. Existing older callers without review fields remain supported; agents should always supply the complete review.
177
+
178
+ Confirmation freezes the eligible recipients and content. Groups and filters resolve at confirmation, not at the future send time. Permission, opt-out and delivery checks still apply before sending. A confirmed future campaign has stored status `pending` and scheduledAt; the UI labels that combination Scheduled. Drafts remain `draft` regardless of date. Queued, sent and delivered are different states; read campaign counts and the send ledger. Confirmed campaigns cannot be edited through this flow; cancellation and rescheduling are not added here.
179
+
180
+ Keep the same requestKey and exact payload after an uncertain save or confirmation. Read back campaign state and retry the original request; never create a new send key to recover an uncertain result. A changed request needs a fresh review. The older `mail send email` / `send_mail_email` path sends immediately and cannot confirm a scheduled campaign; it returns a conflict for dated campaigns. Use campaign send instead. Test sends and dry runs do not confirm or start the campaign; existing test restrictions still apply.
181
+
182
+ ### Email capability release check
183
+
184
+ For each changed Email capability, check its shared service, UI, public API, CLI help and examples, owner-agent schema/instructions, and MCP operation/docs. Add focused contract checks and test the real adapters. Run `node scripts/people/sync-agent-docs.mjs` after editing the packaged manuals and `node scripts/people/sync-agent-docs.mjs --check` before release. Publish hosted guides through the Frontpage CLI only, then verify the public page. Do not maintain a separate scheduling implementation or a second internal manual.
185
+
103
186
  ## People directory and enrollment
104
187
 
105
188
  ```bash
package/PEOPLE.md CHANGED
@@ -577,6 +577,10 @@ frontpage mail campaign get --id DRAFT_ID --site YOUR_SITE --json
577
577
 
578
578
  Preview reports matched, eligible, no-email, suppressed, and permission-missing counts and sends nothing. Sending freezes content and eligible recipients, deduplicates email addresses, and uses the existing paced email queue. Reuse the send request key after a lost response. Check `campaign get` for queued, sent, pending, failed, and skipped work; queued does not mean delivered.
579
579
 
580
+ Campaigns can also schedule a future send through `mail save` with `document.scheduledAt`. A date on a draft never sends it. After reviewing content, audience and time, `mail campaign send` confirms the saved schedule; the existing worker waits until due. Send now uses scheduledAt:null. `campaign get` returns stored status and scheduledAt; pending plus a future time displays Scheduled in the UI. Groups and filters resolve when confirmed. Read [MAIL.md, Campaign scheduling](./MAIL.md#campaign-scheduling) for complete save, review, retry, agent and MCP examples. Direct `mail send email` is for immediate unscheduled sends and cannot bypass a saved schedule.
581
+
582
+ For the owner agent, call `set_mail_settings({})` to read the business timezone, then `save_mail_editor` with a one_off document and future ISO scheduledAt including Z or an explicit offset. Omission preserves timing; null clears it. Read `get_mail_email` after saving and `manage_people` with area:campaign, operation:get for saved timing/audience, then operation:preview for the exact audience and eligible count. Show the owner the content, recipients and local send time. On explicit confirmation, call `manage_people` with area:campaign, operation:send, payload:{documentId,requestKey,review:{audience,eligible,documentUpdatedAt,scheduledAt}}. Use the post-save document timestamp, preview count and canonical saved UTC time. These values must still match; a changed review needs a fresh read and approval. Keep the original requestKey and payload after an uncertain result and read back state before retrying. Never use direct send_mail_email to bypass timing. MCP uses `frontpage_mail_write` operation:save_mail_editor and campaign.send, with campaign.get/preview through `frontpage_mail_read`. A confirmed campaign cannot be edited or rescheduled in this flow.
583
+
580
584
  ## CLI, API, and agent reference
581
585
 
582
586
  The editor, public API, CLI, and site agent call the same application service. Every operation below is available without opening the UI.
package/README.md CHANGED
@@ -113,7 +113,7 @@ An explicit stock target already equal to the current count is a no-op. Repeatin
113
113
 
114
114
  ### AI Support Agent
115
115
 
116
- Included on Scale and internal sites after owner activation. Use `frontpage customer-inbox` for settings, knowledge and customer conversations. The command and API paths keep their existing names. See [AI Support Agent](./CUSTOMER-INBOX.md) for permissions, private guidance, takeover, verification and shared usage.
116
+ Included on Scale and internal sites after owner activation. Use `frontpage customer-inbox` for settings, knowledge and customer conversations. Knowledge saves use last-save-wins. Read paginated revision history within the plan Rewind allowance, or archive a note to stop future use without deleting its records. The command and API paths keep their existing names. See [AI Support Agent](./CUSTOMER-INBOX.md) for permissions, private guidance, takeover, verification and shared usage.
117
117
 
118
118
  ### Website models and AI credits
119
119
 
@@ -5,7 +5,7 @@ import { takeFlag, assertNoUnknownFlags } from "../argv.js";
5
5
  import { getApiKey } from "../auth/keychain.js";
6
6
  import { apiBase, v1Get, httpErrorFromV1, CliHttpError, EXIT_AUTH, EXIT_USAGE, EXIT_OK, exitCodeForError, } from "../http/client.js";
7
7
  import { emitOk, emitErr, isJson } from "../runtime.js";
8
- const RESOURCE = /^(?:status|activation|address|widget|website|settings|knowledge(?:\/[0-9a-f-]+(?:\/history)?)?|sources(?:\/[0-9a-f-]+)?|blocks(?:\/[0-9a-f-]+\/unblock)?|jobs(?:\/[0-9a-f-]+(?:\/resume)?)?|files(?:\/[0-9a-f-]+)?|receipts(?:\/[0-9a-f-]+)?|conversations(?:\/[0-9a-f-]+(?:\/(?:messages|routing|guidance|reply|control|state|revoke-sessions|verification))?)?)(?:\?[^#]*)?$/;
8
+ const RESOURCE = /^(?:status|activation|address|widget|website|settings|knowledge(?:\/[0-9a-f-]+(?:\/(?:history|archive))?)?|sources(?:\/[0-9a-f-]+)?|blocks(?:\/[0-9a-f-]+\/unblock)?|jobs(?:\/[0-9a-f-]+(?:\/resume)?)?|files(?:\/[0-9a-f-]+)?|receipts(?:\/[0-9a-f-]+)?|conversations(?:\/[0-9a-f-]+(?:\/(?:messages|routing|guidance|reply|control|state|revoke-sessions|verification))?)?)(?:\?[^#]*)?$/;
9
9
  export function inboxCommand(args) {
10
10
  const work = [...args], verb = work.shift(), resource = work.shift(), file = takeFlag(work, "--file"), key = takeFlag(work, "--request-key"), out = takeFlag(work, "--out");
11
11
  assertNoUnknownFlags(work, "customer-inbox");
package/dist/registry.js CHANGED
@@ -428,12 +428,16 @@ export const COMMANDS = [
428
428
  "frontpage customer-inbox put settings --file ./inbox-settings.json --request-key settings_revision_1",
429
429
  "frontpage customer-inbox get conversations",
430
430
  "frontpage customer-inbox get knowledge",
431
+ "frontpage customer-inbox get 'knowledge/NOTE_UUID/history?limit=20'",
432
+ "frontpage customer-inbox post knowledge/NOTE_UUID/archive --file archive.json --request-key archive_note_001",
431
433
  ],
432
434
  flags: [
433
435
  "Available on Scale and internal sites after owner activation; no pilot allowlist",
434
436
  "Writes require a stable --request-key and a JSON file; reuse both on retries",
435
437
  "Human CLI and owner-agent tools manage authorized conversations; customer support tools stay bound to the verified customer",
436
438
  "Luna and 10× AI billing are fixed",
439
+ "Knowledge uses last-save-wins; history follows plan Rewind limits and always includes the latest revision",
440
+ "Archive knowledge with {} to stop future use while keeping the note and history",
437
441
  "Full request examples: CUSTOMER-INBOX.md",
438
442
  ],
439
443
  seeAlso: ["people", "usage"],
@@ -587,6 +591,8 @@ export const COMMANDS = [
587
591
  "frontpage mail series steps set <id> --file steps.json",
588
592
  "frontpage mail template history <id> --limit 20 --json",
589
593
  "frontpage mail template history-get <id> --history-id <saved-copy-id> --json",
594
+ "frontpage mail save --file scheduled-draft.json --site YOUR_SITE --json",
595
+ "frontpage mail campaign send --id DRAFT_ID --file review.json --request-key SEND_KEY --site YOUR_SITE --json",
590
596
  "frontpage mail suppress add --email bounce@example.com",
591
597
  ],
592
598
  flags: [
@@ -596,6 +602,7 @@ export const COMMANDS = [
596
602
  "sends list --q --status --type campaign|series|confirmation|template|test|inline --page 1 --limit 20.",
597
603
  "Series status active means On; paused/draft mean Off. Enrollment while Off waits at step 1, without sending. Existing people resume their next unsent step.",
598
604
  "campaign preview|audience|send|get. --id DRAFT_ID, --file audience.json ({audience:{groupIds|stageIds|personIds|filters}}). send requires --request-key; reuse it after a network error. Templates/series are for automation; campaigns have separate drafts. See PEOPLE.md.",
605
+ "Schedule campaigns through save document.scheduledAt (future ISO time with timezone); null means Send now, omission preserves timing. A dated draft never sends until campaign send confirms it. Supply review:{audience,eligible,documentUpdatedAt,scheduledAt} from fresh saved reads/preview. campaign get returns status and scheduledAt; pending plus a future date means Scheduled. Direct send email cannot bypass saved timing. See MAIL.md, Campaign scheduling.",
599
606
  "template history <id> pages saved copies with --limit (20 by default, max 100) and --cursor. history-get <id> --history-id reads full content. Both reads follow the plan Rewind window, plus Latest at any age; older copies stay stored. Lists report windowDays, unlimited, canUpgrade and moreBeyondWindow. Use template set with the stable template ID to save edited content as latest; past copies stay unchanged. Read template get before editing. History begins with content retained when history shipped, not lost earlier edits.",
600
607
  "email list is unsent drafts only. Sent one-offs are mail sends list",
601
608
  "send --file is an inline one-off (no saved document)",
package/llms.txt CHANGED
@@ -61,3 +61,5 @@ Complete Store edits: use existing items update --file with changed fields and v
61
61
  Template history (frontpage-host 0.3.11+): read `mail template get ID` before editing. `mail template history ID --limit 20 --cursor CURSOR` pages saved copies; `mail template history-get ID --history-id COPY_ID` fetches full content. Edit any copy, then `mail template set ID --file content.json` with the stable template ID and content fields only to make a new latest copy. Last template save wins; past copies remain. Campaign and series conflict checks remain. Retry uncertain saves through `mail save` with the same requestKey and exact payload. History begins with content retained at launch; earlier overwritten edits are unavailable. History reads use the plan’s Rewind window and always include Latest, even when older. Older copies stay stored; lists and direct reads apply the same limit. Window metadata explains the limit and whether older copies exist. See MAIL.md.
62
62
 
63
63
  Video uploads: the current CLI prepares a browser-compatible MP4 after uploading a local video, including prompt --attach. Use the returned sitePath and actual filename. prepared:false plus warning means the original was retained and playback compatibility is not assured. media list prefers an exact completed prepared copy. Existing video URLs are unchanged. See AGENTS.md for the upload and fallback contract.
64
+
65
+ AI Support Agent knowledge: read the latest note, then save the same ID. Last save wins and prior saved changes remain in history. Knowledge history supports limit/cursor and version reads within the plan Rewind allowance plus the latest version. Archive with customer-inbox post knowledge/ID/archive and {} to stop future use without deleting records. See CUSTOMER-INBOX.md.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontpage-host",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
4
4
  "description": "Frontpage CLI — manage your site from the terminal",
5
5
  "type": "module",
6
6
  "bin": {