@rubytech/create-maxy-code 0.1.244 → 0.1.247

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 (71) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-06-04-public-agent-knowledge-delivery.md +230 -0
  3. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  4. package/payload/platform/plugins/admin/PLUGIN.md +4 -1
  5. package/payload/platform/plugins/admin/hooks/__tests__/pin-identity-gate.test.sh +96 -0
  6. package/payload/platform/plugins/admin/hooks/pin-identity-gate.sh +136 -0
  7. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts +2 -0
  8. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts.map +1 -0
  9. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js +34 -0
  10. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js.map +1 -0
  11. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.d.ts +2 -0
  12. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.d.ts.map +1 -0
  13. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.js +50 -0
  14. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.js.map +1 -0
  15. package/payload/platform/plugins/admin/mcp/dist/index.js +9 -1
  16. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  17. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts +6 -0
  18. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts.map +1 -0
  19. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js +32 -0
  20. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js.map +1 -0
  21. package/payload/platform/plugins/admin/skills/investigate/SKILL.md +34 -17
  22. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +16 -16
  23. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +37 -56
  24. package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +5 -0
  25. package/payload/platform/plugins/admin/skills/unzip-attachment/SKILL.md +5 -0
  26. package/payload/platform/plugins/admin/skills/update-knowledge/SKILL.md +5 -0
  27. package/payload/platform/plugins/cloudflare/.claude-plugin/plugin.json +1 -1
  28. package/payload/platform/plugins/cloudflare/PLUGIN.md +10 -7
  29. package/payload/platform/plugins/cloudflare/references/api.md +166 -0
  30. package/payload/platform/plugins/cloudflare/references/d1-data-capture.md +157 -0
  31. package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +6 -6
  32. package/payload/platform/plugins/cloudflare/references/hosting-sites.md +66 -0
  33. package/payload/platform/plugins/cloudflare/references/manual-setup.md +5 -3
  34. package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +72 -0
  35. package/payload/platform/plugins/docs/references/admin-identity-gate.md +81 -0
  36. package/payload/platform/plugins/docs/references/cloudflare.md +4 -4
  37. package/payload/platform/plugins/docs/references/deployment.md +1 -1
  38. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +1 -0
  39. package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +5 -0
  40. package/payload/platform/plugins/workflows/skills/workflow-manager/SKILL.md +5 -0
  41. package/payload/platform/scripts/check-skill-frontmatter.mjs +139 -0
  42. package/payload/platform/scripts/setup-account.sh +21 -8
  43. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts +39 -0
  44. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts.map +1 -0
  45. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js +133 -0
  46. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js.map +1 -0
  47. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  48. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  49. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  50. package/payload/platform/services/claude-session-manager/dist/index.js +17 -0
  51. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  52. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts +12 -0
  53. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts.map +1 -1
  54. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js +1 -1
  55. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js.map +1 -1
  56. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +1 -1
  57. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  58. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +31 -5
  59. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  60. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +2 -1
  61. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  62. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +39 -6
  63. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  64. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
  65. package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -2
  66. package/payload/server/{chunk-SRO5RFMV.js → chunk-JMEX5NRX.js} +1 -3
  67. package/payload/server/maxy-edge.js +1 -1
  68. package/payload/server/server.js +261 -153
  69. package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +0 -55
  70. package/payload/platform/templates/agents/public/SOUL.md +0 -19
  71. package/payload/platform/templates/agents/public/config.json +0 -8
@@ -0,0 +1,166 @@
1
+ # Cloudflare API — master token, minted narrow tokens, endpoint map
2
+
3
+ The Cloudflare REST API is a permitted surface on this install. It is how the agent does DNS edits, Pages deploys, D1 queries, Access policies, and apex-CNAME creation without driving the dashboard. This reference is the library: where the canonical docs live, how auth works (one operator-provisioned master token; the agent mints short-lived narrow tokens from it), where the master is stored, and the curated endpoints worth knowing.
4
+
5
+ Canonical reference (always the source of truth for request shapes, never mirror it wholesale here): **https://developers.cloudflare.com/api/**
6
+
7
+ ---
8
+
9
+ ## Auth model — one master, many minted narrow tokens
10
+
11
+ There are two distinct tokens in play. Keep them separate.
12
+
13
+ - **Master token** — fully-scoped, long-lived, provisioned **once** by the operator in the dashboard (see § Provisioning the master token, below). Broad enough to manage Pages, D1, DNS, **and** create API tokens. The agent reads it only to mint narrow tokens. It is never passed to `wrangler`, never put on a command line that gets echoed, never printed.
14
+ - **Minted narrow token** — scoped to exactly one operation class (a single-zone DNS edit, a one-project Pages deploy, a D1 query), short-lived, created by the agent via the API call below. Exported into the environment for the one `wrangler` / API call, then discarded. Never written to disk, never committed, never echoed into chat.
15
+
16
+ Why mint instead of hand-rolling: the operator provisions the master once; every per-operation token is minted by the agent on demand, so the operator never hand-crafts scoped tokens and no broad token is ever exported to a tool that could log it.
17
+
18
+ ---
19
+
20
+ ## Master token storage — account-scoped secrets file
21
+
22
+ Store the master at an account-scoped secrets file, **not** in any deployable project tree:
23
+
24
+ ```
25
+ ~/<brand>-code/data/accounts/<accountId>/secrets/cloudflare.env # mode 600, umask 077
26
+ ```
27
+
28
+ (worked example: `~/realagent-code/data/accounts/<accountId>/secrets/cloudflare.env`)
29
+
30
+ It holds exactly two values:
31
+
32
+ ```
33
+ CLOUDFLARE_API_TOKEN=<master>
34
+ CLOUDFLARE_ACCOUNT_ID=<accountId>
35
+ ```
36
+
37
+ Rationale (binding):
38
+
39
+ 1. **Account-isolated** — under `data/accounts/<accountId>/`, consistent with brand isolation; one account's master never sits where another account's flow can read it.
40
+ 2. **Outside every deployable/git project tree** — so the god-token can never be committed or shipped in a Pages upload. This is the failure mode a project-root `.env` invites: a `.env` next to a site's source gets swept into the `wrangler pages deploy` upload or a `git add .`.
41
+ 3. **Sourced on demand for the master only** — `set -a; . <file>; set +a` to load `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` into the environment for the mint call; per-operation narrow tokens go into the ephemeral environment of their single command, never into this file.
42
+
43
+ Create it once (mode 600, parent dirs 700):
44
+
45
+ ```bash
46
+ ACCOUNT_ID="<accountId>"
47
+ SECRETS_DIR="${HOME}/<brand>-code/data/accounts/${ACCOUNT_ID}/secrets"
48
+ ( umask 077; mkdir -p "${SECRETS_DIR}" )
49
+ # The operator pastes the master token; the agent never echoes it back.
50
+ ( umask 077; printf 'CLOUDFLARE_API_TOKEN=%s\nCLOUDFLARE_ACCOUNT_ID=%s\n' "<master>" "${ACCOUNT_ID}" > "${SECRETS_DIR}/cloudflare.env" )
51
+ chmod 600 "${SECRETS_DIR}/cloudflare.env"
52
+ ```
53
+
54
+ Load the master for a mint call:
55
+
56
+ ```bash
57
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Provisioning the master token (ADVANCED — operator-guided, never automated)
63
+
64
+ This is the one manual, operator-driven step. The agent relays the click-path; it does **not** browser-automate it (no Playwright, no Chrome DevTools, no consent-page driver).
65
+
66
+ 1. Open **https://dash.cloudflare.com/profile/api-tokens** in your browser.
67
+ 2. Click **Create Token** → **Create Custom Token** → **Get started**.
68
+ 3. Name it (e.g. `<brand> master`). Under **Permissions**, add these rows (Account-level unless noted):
69
+ - **Account · Cloudflare Pages · Edit**
70
+ - **Account · D1 · Edit**
71
+ - **Account · API Tokens · Write** ← this is what lets the agent mint narrow tokens
72
+ - **Zone · DNS · Edit** (Zone Resources → the zones you route)
73
+ - **Account · Cloudflare Tunnel · Edit** (only if you want the API to manage tunnels alongside `cloudflared`)
74
+ 4. **Account Resources** → Include → your account. **Zone Resources** → Include → the zones in scope.
75
+ 5. Click **Continue to summary**, then **Create Token**.
76
+ 6. Copy the token **once** — Cloudflare shows it a single time. Paste it into the secrets file (above). The agent stores it without echoing it back.
77
+
78
+ Verify the master works (token value never printed — only the verification result):
79
+
80
+ ```bash
81
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
82
+ curl -sS -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
83
+ "https://api.cloudflare.com/client/v4/user/tokens/verify" | jq '.result.status'
84
+ # expect: "active"
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Minting a narrow token
90
+
91
+ The agent mints a per-operation token from the master via `POST /accounts/{account_id}/tokens`. Scope it to the single operation, give it a short TTL, export it for the one call, discard it.
92
+
93
+ ```bash
94
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a # loads the master + account id
95
+
96
+ # Example: a one-project Pages-deploy token. Adjust the policy for the operation.
97
+ MINTED=$(curl -sS -X POST \
98
+ -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
99
+ -H "Content-Type: application/json" \
100
+ "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/tokens" \
101
+ --data @- <<'JSON' | jq -r '.result.value'
102
+ {
103
+ "name": "ephemeral-pages-deploy",
104
+ "policies": [{
105
+ "effect": "allow",
106
+ "resources": { "com.cloudflare.api.account.<accountId>": "*" },
107
+ "permission_groups": [{ "id": "<pages-edit-permission-group-id>", "name": "Pages Write" }]
108
+ }],
109
+ "expires_on": "<iso8601-a-few-minutes-out>"
110
+ }
111
+ JSON
112
+ )
113
+
114
+ # Use it for exactly one command, in this command's environment only:
115
+ CLOUDFLARE_API_TOKEN="${MINTED}" wrangler pages deploy ./dist --project-name <project> --branch=main
116
+
117
+ unset MINTED # discard; never written to disk, never echoed
118
+ ```
119
+
120
+ Look up `<pages-edit-permission-group-id>` (and the ids for DNS Edit, D1 Edit, etc.) once via `GET /accounts/{account_id}/tokens/permission_groups`; they are stable per account.
121
+
122
+ **Redaction is binding.** Every example above pipes the token into a variable or an `Authorization` header and never to stdout. The agent surfaces the operation as verb + target — "minting a Pages-deploy token", "creating CNAME `chat.example.com`" — never the secret. A failure surfaces the API error body with any `Authorization` value masked.
123
+
124
+ ---
125
+
126
+ ## Curated endpoint map
127
+
128
+ Request/response shapes live at the canonical docs URL; this is the index of what to reach for. `${ACC}` = `CLOUDFLARE_ACCOUNT_ID`, `${ZONE}` = the zone id, `${TOKEN}` = a **minted narrow** token.
129
+
130
+ ### Token create / verify
131
+ - `GET /user/tokens/verify` — confirm a token is active.
132
+ - `POST /accounts/${ACC}/tokens` — mint a narrow token (see above).
133
+ - `GET /accounts/${ACC}/tokens/permission_groups` — resolve permission-group ids for scoping.
134
+
135
+ ### DNS records (zone-scoped; needs **Zone · DNS · Edit**)
136
+ - `GET /zones/${ZONE}/dns_records` — list / find a record id.
137
+ - `POST /zones/${ZONE}/dns_records` — create. For an **apex CNAME**, set `"type":"CNAME"`, `"name":"@"` (or the bare zone name), `"content":"<UUID>.cfargotunnel.com"`, `"proxied":true` — Cloudflare flattens it automatically. This is the API equivalent of the dashboard apex edit in `dashboard-guide.md`.
138
+ - `PUT/PATCH /zones/${ZONE}/dns_records/{id}` — update. `DELETE …/{id}` — remove a stray record.
139
+
140
+ ```bash
141
+ curl -sS -X POST -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
142
+ "https://api.cloudflare.com/client/v4/zones/${ZONE}/dns_records" \
143
+ --data '{"type":"CNAME","name":"@","content":"<UUID>.cfargotunnel.com","proxied":true}' \
144
+ | jq '{ok:.success, name:.result.name}'
145
+ ```
146
+
147
+ ### Zones
148
+ - `GET /zones?account.id=${ACC}` — list zones on the account; find a zone id by name (`?name=example.com`).
149
+
150
+ ### Tunnels (Account · Cloudflare Tunnel)
151
+ - `GET /accounts/${ACC}/cfd_tunnel` — list tunnels.
152
+ - `POST /accounts/${ACC}/cfd_tunnel` — create. `DELETE …/{tunnel_id}` — delete.
153
+ - The OAuth/`cloudflared` path in `manual-setup.md` remains the primary tunnel flow; use the API only when an end-to-end programmatic run is wanted.
154
+
155
+ ### Access (Zero Trust) applications + policies
156
+ - `POST /accounts/${ACC}/access/apps` and `POST /accounts/${ACC}/access/apps/{app_id}/policies` — author an Access application + policy programmatically. The dashboard click-path in `dashboard-guide.md` § "Author an Access policy" is the alternative for operators who prefer to click.
157
+
158
+ ---
159
+
160
+ ## When to use which path
161
+
162
+ - **Tunnel create/route/run** → `cloudflared` (OAuth cert), per `manual-setup.md`. The API can manage tunnels too, but the cert path is the established one.
163
+ - **Apex CNAME** → API (above) or dashboard (`dashboard-guide.md`). Either flattens correctly.
164
+ - **Pages deploy** → `wrangler` with a minted Pages-Edit token, per `hosting-sites.md`.
165
+ - **D1 read/write** → `wrangler d1 execute --remote` with a minted D1-Edit token, per `d1-data-capture.md`.
166
+ - **Inspecting account state** (which zones, which tunnels) → API `GET` with a read-scoped minted token, or the dashboard.
@@ -0,0 +1,157 @@
1
+ # Capturing form submissions into Cloudflare D1
2
+
3
+ This is the established pattern for turning a static site's contact / waitlist form into durable, queryable leads: the form POSTs to a **Pages Function**, the Function inserts a row into a **Cloudflare D1** database, and the agent reads and sweeps new rows with `wrangler d1 execute --remote`. It is live on `realagent.pages.dev` today — the worked example below is that deployment, generalized with placeholders.
4
+
5
+ Pair this with `hosting-sites.md` (how the site itself gets deployed) and `api.md` (the master token + mint-narrow discipline that authenticates every `wrangler` call).
6
+
7
+ ---
8
+
9
+ ## The single most common breakage — token scope
10
+
11
+ A Pages-only token **cannot touch D1**. The deploy succeeds, the form renders, and every submission silently 500s at the D1 insert. The token used for D1-backed Pages work must carry **both**:
12
+
13
+ - **Account · Cloudflare Pages · Edit**
14
+ - **Account · D1 · Edit**
15
+
16
+ Mint a narrow token that includes both permission groups (see `api.md` § Minting a narrow token) before any `wrangler d1` or D1-backed deploy command. This was observed live in the session that built `realagent.pages.dev`; it is the first thing to check when captures stop arriving.
17
+
18
+ ---
19
+
20
+ ## 0. Mint the Pages-+-D1 token
21
+
22
+ Every command below uses `${MINTED_PAGES_D1}` — a narrow token scoped to **both** Pages Edit and D1 Edit, minted from the master per `api.md` § Minting a narrow token. Mint it once at the start of the session and export it; it is never written to disk or echoed:
23
+
24
+ ```bash
25
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a # loads master + account id
26
+ MINTED_PAGES_D1=$(curl -sS -X POST \
27
+ -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
28
+ -H "Content-Type: application/json" \
29
+ "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/tokens" \
30
+ --data @- <<'JSON' | jq -r '.result.value'
31
+ {
32
+ "name": "ephemeral-pages-d1",
33
+ "policies": [{
34
+ "effect": "allow",
35
+ "resources": { "com.cloudflare.api.account.<accountId>": "*" },
36
+ "permission_groups": [
37
+ { "id": "<pages-edit-permission-group-id>", "name": "Pages Write" },
38
+ { "id": "<d1-edit-permission-group-id>", "name": "D1 Write" }
39
+ ]
40
+ }],
41
+ "expires_on": "<iso8601-a-few-minutes-out>"
42
+ }
43
+ JSON
44
+ )
45
+ ```
46
+
47
+ Resolve the two permission-group ids once via `GET /accounts/{account_id}/tokens/permission_groups` (see `api.md`). When the work is done, `unset MINTED_PAGES_D1`.
48
+
49
+ ---
50
+
51
+ ## 1. Create the database
52
+
53
+ ```bash
54
+ CLOUDFLARE_API_TOKEN="${MINTED_PAGES_D1}" wrangler d1 create <db-name>
55
+ # worked example: wrangler d1 create realagent-leads
56
+ ```
57
+
58
+ `wrangler` prints the database `name` and `database_id`. Copy the `database_id` into `wrangler.toml` (next step). The token value is never printed — only the database identifiers.
59
+
60
+ ## 2. Bind the database in `wrangler.toml`
61
+
62
+ The binding name (`DB` below) is the variable the Pages Function reads off `env`. Keep `wrangler.toml` in the site's project root — it carries no secret, only the database id.
63
+
64
+ ```toml
65
+ name = "<project>" # worked example: realagent
66
+ pages_build_output_dir = "dist" # or the framework's output dir
67
+
68
+ [[d1_databases]]
69
+ binding = "DB"
70
+ database_name = "<db-name>" # realagent-leads
71
+ database_id = "<database_id>" # from step 1
72
+ ```
73
+
74
+ ## 3. Create the table
75
+
76
+ Apply the schema to the remote database (`--remote` targets the live D1, not a local replica):
77
+
78
+ ```bash
79
+ CLOUDFLARE_API_TOKEN="${MINTED_PAGES_D1}" wrangler d1 execute <db-name> --remote --command \
80
+ "CREATE TABLE IF NOT EXISTS leads (
81
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
82
+ name TEXT,
83
+ email TEXT NOT NULL,
84
+ message TEXT,
85
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
86
+ swept INTEGER NOT NULL DEFAULT 0
87
+ );"
88
+ ```
89
+
90
+ The `swept` column is the read-cursor: a row is `swept = 0` until the agent has ingested it, then flipped to `1`. This is what makes "show me new leads" a deterministic query rather than a guess.
91
+
92
+ ## 4. The Pages Function — `functions/api/contact.ts`
93
+
94
+ A file at `functions/api/contact.ts` is served at `POST /api/contact` automatically (Pages Functions route by file path). It reads the `DB` binding and inserts a row. No secret lives in this file — the D1 binding is injected by the platform at runtime.
95
+
96
+ ```ts
97
+ interface Env { DB: D1Database }
98
+
99
+ export const onRequestPost: PagesFunction<Env> = async ({ request, env }) => {
100
+ const form = await request.formData();
101
+ const email = String(form.get("email") ?? "").trim();
102
+ if (!email) return new Response("email required", { status: 400 });
103
+
104
+ await env.DB
105
+ .prepare("INSERT INTO leads (name, email, message) VALUES (?, ?, ?)")
106
+ .bind(String(form.get("name") ?? ""), email, String(form.get("message") ?? ""))
107
+ .run();
108
+
109
+ return new Response(null, { status: 303, headers: { Location: "/thanks" } });
110
+ };
111
+ ```
112
+
113
+ The site's form posts to it:
114
+
115
+ ```html
116
+ <form method="POST" action="/api/contact">
117
+ <input name="email" type="email" required>
118
+ <input name="name">
119
+ <textarea name="message"></textarea>
120
+ <button>Join the waitlist</button>
121
+ </form>
122
+ ```
123
+
124
+ ## 5. Deploy
125
+
126
+ Deploy via `hosting-sites.md`. The binding in `wrangler.toml` is what wires the deployed Function to D1; the deploy token must carry both Pages Edit and D1 Edit (above).
127
+
128
+ ## 6. Read and sweep new submissions
129
+
130
+ Read everything not yet ingested:
131
+
132
+ ```bash
133
+ CLOUDFLARE_API_TOKEN="${MINTED_PAGES_D1}" wrangler d1 execute <db-name> --remote --json --command \
134
+ "SELECT id, name, email, message, created_at FROM leads WHERE swept = 0 ORDER BY id;"
135
+ ```
136
+
137
+ After ingesting those rows (e.g. writing them into the graph), mark them swept so the next read returns only newer ones:
138
+
139
+ ```bash
140
+ CLOUDFLARE_API_TOKEN="${MINTED_PAGES_D1}" wrangler d1 execute <db-name> --remote --command \
141
+ "UPDATE leads SET swept = 1 WHERE swept = 0;"
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Outcome contract
147
+
148
+ D1 capture is done when a **test POST to the live form** appears in the unswept set:
149
+
150
+ ```bash
151
+ curl -sS -X POST -d "email=test@example.com&name=verify" "https://<project>.pages.dev/api/contact" -i | head -1
152
+ # then:
153
+ CLOUDFLARE_API_TOKEN="${MINTED_PAGES_D1}" wrangler d1 execute <db-name> --remote --command \
154
+ "SELECT email FROM leads WHERE swept = 0;"
155
+ ```
156
+
157
+ The test email appearing in that `SELECT` is the proof. A 200/303 on the POST alone is not — the row in D1 is the contract. Surface the operation as verb + target ("inserting a test lead", "reading unswept rows from `realagent-leads`"); the token value never appears.
@@ -105,12 +105,12 @@ Use this when you need to audit which hostnames are pointing at which `<UUID>.cf
105
105
 
106
106
  ## Author an Access policy for SSH or SMB
107
107
 
108
- When the agent adds an SSH or SMB ingress hostname (per `references/manual-setup.md`), it surfaces an
109
- `ACTION REQUIRED` block naming this click-path. The agent does NOT
110
- create the Access policy Cloudflare API/SDK is banned
111
- (`feedback_cf_api_total_eradication`) and `cloudflared` CLI has no
112
- Access-application create subcommand so the operator must author it
113
- in the dashboard before off-LAN clients can reach the Pi.
108
+ When the agent adds an SSH or SMB ingress hostname (per `references/manual-setup.md`),
109
+ the Access policy must exist before off-LAN clients can reach the Pi. There are two
110
+ paths: the agent can author it via the Cloudflare API with a minted Access-scoped
111
+ token (see `references/api.md` § Access), or the operator can author it by hand in the
112
+ dashboard with the click-path below. `cloudflared` CLI has no Access-application create
113
+ subcommand, so the dashboard path is the manual alternative to the API.
114
114
 
115
115
  1. Click **Zero Trust** in the sidebar.
116
116
  2. Click **Access** → **Applications**.
@@ -0,0 +1,66 @@
1
+ # Deploying a site to Cloudflare Pages
2
+
3
+ How to put a static or Next.js site on Cloudflare Pages via `wrangler`, the way `realagent.pages.dev` is deployed. This covers the build → deploy → verify loop and the Next.js specifics. Auth is a minted narrow token per `api.md`; if the site also captures form data, read `d1-data-capture.md` for the dual Pages-Edit **and** D1-Edit scope.
4
+
5
+ This is distinct from `references/serving-published-sites.md`, which serves a platform-published site at a custom domain through the brand's **cloudflared tunnel**. This reference is about **Cloudflare Pages** — Cloudflare hosts the build, not the install device.
6
+
7
+ ---
8
+
9
+ ## When to use Pages vs the tunnel
10
+
11
+ - **Cloudflare Pages** (this reference) — a standalone marketing/landing/app site that Cloudflare builds and serves on its edge, optionally with Pages Functions + D1. Use when the site is its own project and you want Cloudflare to host it.
12
+ - **The brand tunnel** (`manual-setup.md` + `serving-published-sites.md`) — expose something running on the install device at a custom domain. Use when the content is served by the platform itself.
13
+
14
+ ---
15
+
16
+ ## 1. Build the site
17
+
18
+ Produce the static output the framework emits.
19
+
20
+ - **Static / Vite / Astro:** `npm run build` → output in `dist/` (or the framework default).
21
+ - **Next.js:** Pages needs a static or edge-compatible build. For a fully static site use `output: "export"` in `next.config.js` and `npm run build` → output in `out/`. For a Next.js app with server routes, deploy through the Pages adapter (`@cloudflare/next-on-pages`): `npx @cloudflare/next-on-pages` → output in `.vercel/output/static`.
22
+
23
+ ## 2. Configure `wrangler.toml`
24
+
25
+ ```toml
26
+ name = "<project>" # the Pages project name, e.g. realagent
27
+ pages_build_output_dir = "<output-dir>" # dist | out | .vercel/output/static
28
+ compatibility_date = "2024-01-01"
29
+ compatibility_flags = ["nodejs_compat"] # required for next-on-pages and most Functions
30
+ ```
31
+
32
+ If the site captures form data, add the `[[d1_databases]]` binding from `d1-data-capture.md` here.
33
+
34
+ ## 3. Mint a deploy token
35
+
36
+ A Pages deploy needs **Account · Cloudflare Pages · Edit**. If the site has Pages Functions hitting D1, the token also needs **Account · D1 · Edit** (the single most common breakage — see `d1-data-capture.md`). Mint it per `api.md` § Minting a narrow token:
37
+
38
+ ```bash
39
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
40
+ # MINTED = a narrow token scoped to Pages Edit (+ D1 Edit if the site uses D1)
41
+ ```
42
+
43
+ ## 4. Deploy
44
+
45
+ ```bash
46
+ CLOUDFLARE_API_TOKEN="${MINTED}" wrangler pages deploy <output-dir> \
47
+ --project-name <project> --branch=main
48
+ ```
49
+
50
+ `--branch=main` deploys to the production alias (`https://<project>.pages.dev`); any other branch name produces a preview URL instead. The first deploy of a new project name creates the project. The token value is never printed — `wrangler` prints the deployment URL, which is what gets surfaced.
51
+
52
+ ## 5. Custom domain (optional)
53
+
54
+ Attach a custom domain to the Pages project in the dashboard (**Workers & Pages → your project → Custom domains → Set up a domain**), or via the API. Cloudflare provisions the certificate and the CNAME automatically when the zone is on the same account.
55
+
56
+ ---
57
+
58
+ ## Outcome contract
59
+
60
+ Hosting is done when the deployed URL serves the **new** build:
61
+
62
+ ```bash
63
+ curl -s "https://<project>.pages.dev/" | head
64
+ ```
65
+
66
+ The response showing the new markup is the proof. A successful `wrangler` exit alone is not the contract — the live URL returning the new content is. Surface the operation as verb + target ("deploying Pages project `realagent`"); the token value never appears in chat or stdout.
@@ -293,9 +293,11 @@ does this on every Pi). The script skips the SMB ingress with
293
293
  `[tunnel-install] smb-ingress-skipped reason=samba-not-provisioned`
294
294
  rather than failing.
295
295
 
296
- **Access policy is dashboard-only.** Cloudflare API/SDK is forbidden;
297
- `cloudflared` CLI has no Access-application create subcommand. After
298
- the ingress is in place, author the policy in the dashboard:
296
+ **Access policy API or dashboard.** `cloudflared` CLI has no
297
+ Access-application create subcommand, so author the policy either via
298
+ the Cloudflare API with a minted Access-scoped token (see
299
+ `references/api.md` § Access) or by hand in the dashboard. After the
300
+ ingress is in place, the dashboard path is:
299
301
 
300
302
  ```
301
303
  Cloudflare → Zero Trust → Access → Applications → Add → Self-hosted
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: cloudflare
3
+ description: Cloudflare operations for the install — tunnel setup/diagnosis/reset, DNS edits, Pages hosting, and D1 data capture. The agent drives `cloudflared` and `wrangler` directly via Bash and reads the matching reference before each operation class; success is always a live behavioural signal (external HTTP 200, a deployed URL, a D1 round-trip), never an exit code.
4
+ ---
5
+
6
+ # Cloudflare operations
7
+
8
+ This is the entry point for every Cloudflare task on the install. Pick the operation class, read the matching reference, run the command via Bash, relay the literal output, and prove the outcome with a live signal. There are no Cloudflare MCP tools; the plugin registers none. The agent never browser-automates the dashboard — the operator clicks where a click is genuinely required.
9
+
10
+ | You want to… | Read | Outcome contract |
11
+ |---|---|---|
12
+ | Stand up / diagnose / reset a tunnel so a hostname is reachable | `references/manual-setup.md` | external `curl -I https://<hostname>` → `200` |
13
+ | Switch accounts, edit an apex CNAME by hand, or other dashboard-only clicks | `references/dashboard-guide.md` | the click-path completed in the operator's browser |
14
+ | Tear down corrupt tunnel state and start clean | `references/reset-guide.md` | a fresh `manual-setup.md` run reaches `200` |
15
+ | Call the Cloudflare API (DNS, zones, tunnels, Access, token mint) | `references/api.md` | the API call returns success and the change is observable |
16
+ | Deploy a static / Next.js site to Cloudflare Pages | `references/hosting-sites.md` | the deployed URL serves the new build |
17
+ | Capture form/waitlist submissions into a database | `references/d1-data-capture.md` | a test POST appears in `SELECT … WHERE swept = 0` |
18
+
19
+ ## Two auth paths, by operation class
20
+
21
+ - **Tunnel auth is OAuth.** `cloudflared tunnel login` writes `cert.pem`; the operator clicks the linkified OAuth URL in their own browser. This is the only path for tunnel create/route/run. See `references/manual-setup.md`.
22
+ - **API auth is the master token.** The operator provisions one fully-scoped master token once (an **advanced**, operator-guided dashboard step — the agent does not automate it) and stores it at the account-scoped secrets file. The agent reads the master only to **mint a narrowly-scoped, short-lived token** for each operation — a single-zone DNS edit, a one-project Pages deploy, a D1 query — and exports that ephemeral token for the one call. See `references/api.md` for the storage convention, the mint flow, and the binding redaction discipline.
23
+
24
+ Neither the master token nor any minted token is ever written into a project tree, committed, or echoed into chat.
25
+
26
+ ## Outcome contracts — what "done" means
27
+
28
+ Every Cloudflare operation proves itself with a live behavioural signal surfaced verbatim in chat:
29
+
30
+ - **Tunnel:** `curl -I https://<hostname>` from outside the local network returns `HTTP/2 200` (or `HTTP/1.1 200 OK`). No state file, no `result=ok` line, no "service is active" claim substitutes. If the curl returns anything else, diagnose with `cloudflared tunnel info <tunnelId>` and `systemctl --user status ${BRAND}-cloudflared.service` and recover per `references/manual-setup.md`.
31
+ - **Hosting:** the deployed Pages URL returns the new build (`curl -s https://<project>.pages.dev/ | head` shows the new markup).
32
+ - **D1 capture:** a test POST to the live form appears in `wrangler d1 execute <db> --remote --command "SELECT … WHERE swept = 0"`.
33
+
34
+ ## Inputs (tunnel)
35
+
36
+ Four inputs come from the operator in chat: the admin FQDN, optional public FQDN, optional apex FQDN, and the admin password. `BRAND` is derived from disk — never hardcoded — with `BRAND=$(jq -r .hostname ~/.<configDir>/brand.json)`. The same install can host different brands on different ports; `brand.json.hostname` is the only authoritative source.
37
+
38
+ ## Execution
39
+
40
+ The agent reads the relevant reference and executes its steps with Bash. Every `cloudflared` / `wrangler` / `curl` invocation's stdout and stderr are relayed into chat verbatim — minus any secret, which is always redacted. The OAuth URL printed by `cloudflared tunnel login` is linkified by the native PTY; the operator clicks it in their own browser. The agent does not spawn a browser, automate the Cloudflare consent page, or drive the dashboard via Playwright or Chrome DevTools.
41
+
42
+ Tunnel mutations the agent performs from `references/manual-setup.md` (each one only after reading the relevant step):
43
+
44
+ - `cloudflared tunnel login` — emits the OAuth URL; cert lands at `~/.cloudflared/cert.pem` after the operator authorises.
45
+ - `cloudflared tunnel --origincert <cert> create <name>` — produces `<tunnelId>.json` credentials.
46
+ - Write `~/.${BRAND}/cloudflared/config.yml` (ingress block per the runbook).
47
+ - `cloudflared tunnel --origincert <cert> route dns <tunnelId> <hostname>` — one call per non-apex hostname.
48
+ - For non-admin hostnames not starting with `public.`, append to `~/.${BRAND}/alias-domains.json` so `isPublicHost()` treats them as public.
49
+ - Install + start the `${BRAND}-cloudflared.service` user unit (`systemctl --user daemon-reload && systemctl --user enable --now ${BRAND}-cloudflared.service`).
50
+
51
+ After the service is up, the agent runs `curl -I https://<admin-hostname>` and pastes the response. The setup-done claim only fires when a `200` line appears in that response.
52
+
53
+ ## Apex hostnames
54
+
55
+ The `cloudflared` CLI cannot route apex records (e.g. `maxy.chat`) — standard DNS forbids a CNAME at the zone apex. There are now two ways to create the apex record: the operator edits the apex CNAME in the dashboard (`references/dashboard-guide.md` § "Edit an apex CNAME"), or the agent creates it via the Cloudflare API with a minted DNS-scoped token (`references/api.md` § DNS records). Either reaches the same flattened-CNAME result; pick the dashboard path when the operator prefers to click, the API path when an end-to-end agent run is wanted.
56
+
57
+ ## Reset
58
+
59
+ When local Cloudflare state is corrupt or the operator wants to start from a known-good cert, follow `references/reset-guide.md`. The agent issues the relevant `cloudflared tunnel delete` and `rm -rf ~/.${BRAND}/cloudflared/` commands in Bash, then re-runs the setup steps above.
60
+
61
+ ## Tool discipline — binding
62
+
63
+ When the operator's request touches Cloudflare, the agent's permitted actions are:
64
+
65
+ - Invoke `cloudflared` via Bash, following `references/manual-setup.md`.
66
+ - Invoke `wrangler` and the Cloudflare API via Bash, following `references/api.md`, `references/hosting-sites.md`, and `references/d1-data-capture.md`, using a freshly-minted narrow token per operation.
67
+ - Write `config.yml` and `alias-domains.json` per the runbook.
68
+ - Install and start the brand's cloudflared user service.
69
+ - Quote `references/manual-setup.md`, `references/reset-guide.md`, or `references/dashboard-guide.md` verbatim when a dashboard click-path is the right tool.
70
+ - Verify reachability via `curl -I https://<hostname>` and surface the response.
71
+
72
+ The agent does not drive the dashboard via Playwright or Chrome DevTools, and does not browser-automate master-token creation. It does not synthesise `cloudflared` flag combinations from web search or training — every command comes from the runbook. It never writes a token to disk in a project tree, commits one, or prints one into chat. When a step fails, the agent reports the exact output, names the recovery step from `references/reset-guide.md`, and stops. Improvisation — "let me try a different flag" or "let me drive the dashboard myself" — is the behaviour this rule exists to prevent.
@@ -0,0 +1,81 @@
1
+ # Native-CC admin PIN identity gate
2
+
3
+ Every native-CC admin session — the operator chatting through claude.ai/code (web,
4
+ desktop, mobile) over the per-account `claude rc` daemon — must bind a
5
+ PIN-authenticated `userId` before any tool runs. This closes the gap where the
6
+ daemon stamped one boot-time `USER_ID` (from `users.json`) onto every session,
7
+ which is correct for at most one human on a multi-admin install. (Task 619.)
8
+
9
+ The legacy cookie/PIN browser login (`POST /api/admin/session`) is a separate
10
+ surface documented in [`admin-session.md`](admin-session.md); this gate covers the
11
+ native path, which that login never touched.
12
+
13
+ ## How it works
14
+
15
+ Three pieces, plus a standing audit. No cross-process `session_id` plumbing: the
16
+ hook keys state by the `session_id` it reads from stdin; validation and profile
17
+ resolution live behind one loopback endpoint.
18
+
19
+ 1. **Shared validator** — `ui/app/lib/admin-identity/pin-validator.ts`. The single
20
+ PIN primitive (`validatePin` → `match` / `miss` / `unavailable` / `corrupt`),
21
+ shared by the legacy browser login and the gate so the two cannot drift.
22
+
23
+ 2. **Loopback validate endpoint** — `POST /api/admin/identity/validate`
24
+ (`ui/server/routes/admin/identity.ts`). Loopback-only (rejects any request
25
+ carrying `x-forwarded-for`, which only edge-proxied browser requests have).
26
+ Body `{ pin, sessionId? }`. On a match it resolves the operator's `userName`
27
+ and `aboutOwner` via the same helpers the browser path uses
28
+ (`resolveUserIdentity`, `resolveOwnerProfileBlock`) — `loadUserProfile` is never
29
+ duplicated. On a miss with a `sessionId` it fires a best-effort manager
30
+ `POST /<sessionId>/stop` (only the UI process reliably holds the manager port).
31
+
32
+ 3. **PreToolUse gate hook** — `plugins/admin/hooks/pin-identity-gate.sh`, wired in
33
+ `setup-account.sh` as the first universal PreToolUse entry. Runs only for native
34
+ operator sessions (`MAXY_SESSION_ROLE=admin` and empty `MAXY_SPECIALIST` — the
35
+ latter excludes pty-spawned specialist/recorder sessions). Per tool call: if an
36
+ auth-state file exists for the `session_id`, allow; if the tool is
37
+ `admin-identity-authenticate`, post the PIN to the validate endpoint and on a
38
+ match write the state file and allow (on a miss/unavailable, deny + terminate);
39
+ any other tool while unauthenticated is denied with an instruction to
40
+ authenticate first. Enforcement is in the hook, not the agent's choice — a model
41
+ that skips the prompt reaches no other tool.
42
+
43
+ The agent-callable surface is the eager-loaded `admin-identity-authenticate` MCP
44
+ tool (admin plugin); the gate validates and binds before allowing it, and the tool
45
+ returns the authenticated operator's profile to the agent.
46
+
47
+ Auth state: `<accountDir>/state/admin-identity/<session_id>.json` (`{userId, ts}`).
48
+ One file per authenticated conversation; bound for the session lifetime, not
49
+ re-prompted per message. Task-tool specialist subagents share the operator's
50
+ `session_id`, so they pass once the operator is authenticated.
51
+
52
+ ## Cases
53
+
54
+ - **Zero admins / absent / empty `users.json`** → `pin-unavailable`, the gate
55
+ refuses; never a bound empty `userId`. A real install always has `users.json`
56
+ (the installer/seed writes it), so this is a misconfiguration guard.
57
+ - **One admin** → still prompts; the single matching PIN binds that `userId`.
58
+ - **Many admins** → each PIN binds its own `userId`. The account is fixed (the
59
+ install's own scope); there is no multi-account picker on this path.
60
+ - **Mismatch** → the session is terminated on the first miss (no retry budget).
61
+
62
+ ## Observability
63
+
64
+ One structured `[admin-identity]` line per step, correlated by `sessionId`:
65
+
66
+ ```
67
+ op=loopback-spawn-received → op=pin-prompted → (op=pin-bound source=pin |
68
+ op=pin-miss → op=session-terminated) ; the MCP tool emits op=about-owner-loaded.
69
+ ```
70
+
71
+ **Standing audit** (`services/claude-session-manager/src/admin-identity-audit.ts`,
72
+ every 60s) reconciles, independently of the hook, live native-operator sessions
73
+ (watcher rows with `role === null` — daemon-spawned, no sidecar) that reached real
74
+ tool use against the auth-state files. Any such session with no state file emits
75
+ `[admin-identity] op=audit-orphan sessionId=…
76
+ reason=loopback-session-without-authenticated-userId`. This is the no-event
77
+ detector: a silent gate bypass leaves no per-session log line, but the audit still
78
+ surfaces it.
79
+
80
+ Diagnostic path: `journalctl --user -u <brand>-admin | grep -E '\[admin-identity\]'`,
81
+ then filter by `sessionId` for one session's full lifeline.
@@ -1,6 +1,6 @@
1
1
  # Cloudflare Tunnel — the dashboard is the source of truth
2
2
 
3
- Each installation has its own Cloudflare account. Sign-in is OAuth: the agent invokes `cloudflared tunnel login` via Bash; the Cloudflare Authorize URL streams into the admin chat PTY and the native terminal renders it as a clickable link. Click it, authorise in your own browser, and `cloudflared` writes `cert.pem` to the brand's config directory. The agent never reads or mutates Cloudflare account state directly whatever you see in your logged-in dashboard is the single source of truth. When something needs doing on the account side (adding a domain, deleting a stray entry, switching accounts), the agent relays the click-paths; you run them in your browser.
3
+ Each installation has its own Cloudflare account. The **tunnel** sign-in is OAuth: the agent invokes `cloudflared tunnel login` via Bash; the Cloudflare Authorize URL streams into the admin chat PTY and the native terminal renders it as a clickable link. Click it, authorise in your own browser, and `cloudflared` writes `cert.pem` to the brand's config directory. For **everything else** (DNS, Pages, D1, Access) the agent uses the Cloudflare API, authenticated by a short-lived narrow token it mints from a master token you provision once in the dashboard (an advanced step the agent never automates). Some account-side jobs — adding a domain, switching accounts are still easiest in your browser, and the agent relays those click-paths; the rest it can do directly via the API.
4
4
 
5
5
  ## Identity model
6
6
 
@@ -8,10 +8,10 @@ Each installation has its own Cloudflare account. Sign-in is OAuth: the agent in
8
8
  |------|--------|
9
9
  | **Product identity** (Maxy vs Real Agent) | `brand.json` (`productName`, `configDir`) — known at install. |
10
10
  | **Cloudflare account identity** | `cert.pem` from OAuth. One account per brand per device. |
11
- | **Domain scope** (which zones the operator can route) | Live Cloudflare dashboard — the operator picks the zone in the dashboard during OAuth or names it in chat. The agent does not enumerate zones programmatically. |
11
+ | **Domain scope** (which zones the operator can route) | The operator picks the zone in the dashboard during OAuth or names it in chat; the agent can also enumerate zones via the API with a minted read-scoped token. |
12
12
  | **Local tunnel state** | `~/{configDir}/cloudflared/` — `cert.pem`, `<UUID>.json`, `config.yml`, `alias-domains.json`. |
13
13
 
14
- There is no token-based auth for the operator-owned path (Mode A). To switch Cloudflare accounts, the agent runs the reset flow from `plugins/cloudflare/references/reset-guide.md` (deletes the cert and every tunnel on the current account), then the manual-setup flow again — `cloudflared tunnel login` picks a fresh account when you sign in.
14
+ Tunnel auth on the operator-owned path (Mode A) is the OAuth cert (`cert.pem`); API operations use a narrow token the agent mints from your master token. To switch Cloudflare accounts, the agent runs the reset flow from `plugins/cloudflare/references/reset-guide.md` (deletes the cert and every tunnel on the current account), then the manual-setup flow again — `cloudflared tunnel login` picks a fresh account when you sign in.
15
15
 
16
16
  ## Setup flow
17
17
 
@@ -97,6 +97,6 @@ The most common cause is wrong nameservers on the domain. The domain must use Cl
97
97
 
98
98
  **Does:** invokes `cloudflared` directly via Bash, following `plugins/cloudflare/references/manual-setup.md` step by step; quotes click-paths from the reference files verbatim; verifies external reachability with `curl -I` and surfaces the response.
99
99
 
100
- **Does not:** drive the Cloudflare dashboard via Playwright, synthesise alternative `cloudflared` flag sequences not in the runbook, call any Cloudflare API or SDK, write or edit `cert.pem` / `config.yml` directly outside the runbook's instructions.
100
+ **Does not:** drive the Cloudflare dashboard via Playwright, browser-automate master-token creation, synthesise alternative `cloudflared` flag sequences not in the runbook, write or echo any API token, write or edit `cert.pem` / `config.yml` directly outside the runbook's instructions.
101
101
 
102
102
  When a command fails, the agent reports the failure and cites the relevant recovery step. It does not improvise.
@@ -70,7 +70,7 @@ Grep for both in `~/.<brand>/logs/install-*.log`. Absence after a clean install
70
70
 
71
71
  The first user-domain write the agent attempts (e.g. recording who the operator is) hits the graph-write gate's `Write blocked (no-admin-user)` or `Write blocked (no-local-business)` error. The agent then asks the persona question, persists the answer through the `business-profile` skill or `profile-update.personFields`, and proceeds. The error itself is the signal — grep `Write blocked` in `~/.<brand>/logs/server.log` to confirm.
72
72
 
73
- Cloudflare, WhatsApp, Telegram, and any other dormant capability surfaces on owner request via the `<dormant-plugins>` sentinel the manager injects per-spawn. Execution is the existing plugin skill (`cloudflare:setup-tunnel`, etc.) — no banner, no per-step flag.
73
+ Cloudflare, WhatsApp, Telegram, and any other dormant capability surfaces on owner request via the `<dormant-plugins>` sentinel the manager injects per-spawn. Execution is the existing plugin skill (`cloudflare:cloudflare`, etc.) — no banner, no per-step flag.
74
74
 
75
75
  ### Per-spawn system-prompt sentinels
76
76
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: conversation-archive-enrich
3
+ description: "Walk an existing ConversationArchive's chunks and derive high-confidence claims and insights into the graph, one operator-gated proposal per row. Use when asked to enrich, derive insights from, or mine an existing conversation archive."
3
4
  ---
4
5
 
5
6
  # Conversation Archive — chunk-anchored insight derivation