Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .agents/skills/mcp-tools/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,20 @@ When adding or changing a tool:
AI Search resources in this repo use a common shape:

```text
docs in R2 -> Cloudflare AI Search vector index -> shared MCP runtime -> source-specific MCP tools
Items API uploads -> Cloudflare built-in storage and vector index -> shared MCP runtime -> source-specific MCP tools
```

For a new AI Search-backed document source:

1. Create or adopt the R2 content bucket and AI Search namespaces in `createAgentResources` in `apps/api/infra/resources.ts`.
2. Bind `AI_SEARCH`, `WORKFLOW_AI_SEARCH`, and `AI_SEARCH_CONTENT_BUCKET` in `apps/api/infra/index.ts`.
1. Create or adopt AI Search namespaces in `createAgentResources` in `apps/api/infra/resources.ts`. New instances use built-in storage through the namespace binding.
2. Bind `AI_SEARCH` and `WORKFLOW_AI_SEARCH` in `apps/api/infra/index.ts`. Retain `AI_SEARCH_CONTENT_BUCKET` only for explicit legacy-content transition and saved workflow compatibility; it is not a source for new instances.
3. Add public source metadata in `packages/shared/src/session-tools.ts`.
4. Persist runtime source records through `AiSearchRegistryStore` in `packages/api/src/server/background/db/ai-search.ts`, keeping `maxResults` explicit. Tool names come from `getAiSearchMcpToolNames`.
5. Keep MCP tool registration in `packages/api/src/server/mcp/ai-search-server.ts`. Dispatch into the shared runtime in `packages/api/src/server/mcp/ai-search-runtime.ts`.
6. Run the MCP integration tests and update any session UI source picker affected by the new source.
6. Upload and delete documents through the namespace Items API. Workflows use the `ai-search-upload` storage adapter, with administrator authorization for global sources.
7. Run the MCP integration tests and update any session UI source picker affected by the new source. Verify indexing and retrieval against an isolated real namespace.

Keep legacy R2/website source records readable and exportable, with an explicit migration state. Do not rewrite their storage type or delete original content automatically. Follow `docs/ai-search.md` for the transition.

Each AI Search source should expose two MCP tools:

Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/verify-solzero/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ nub exec e2e list --changed-since origin/master
nub run test:e2e -- --changed-since origin/master
```

The environment loader reads the selected private file, then `config/.env` and `config/.dev.vars`; existing environment values win. Supply `CLOUDFLARE_ACCOUNT_ID` and `CF_AI_GATEWAY_E2E_TOKEN`. The launcher boots local `alchemy dev` and does not read `CLOUDFLARE_API_TOKEN`. Never print credential values. The launcher creates its owned run’s `.dev.vars` with mode 0600 and generates the isolated administrator password in an ignored file. Use the suite's registered secret handles for browser credential input.
The environment loader reads the selected private file, then `config/.env` and `config/.dev.vars`; existing environment values win. Supply `CLOUDFLARE_ACCOUNT_ID` and `CF_AI_GATEWAY_E2E_TOKEN`. The ordinary core launcher boots local `alchemy dev` and does not read `CLOUDFLARE_API_TOKEN`. The explicit isolated AI Search launcher documented in `docs/e2e.md` supplies that credential only when its profile enables native remote AI Search bindings. Never print credential values. The launcher creates its owned run’s `.dev.vars` with mode 0600 and generates the isolated administrator password in an ignored file. Use the suite's registered secret handles for browser credential input.

`nub run test` runs core flows, including Isolate, OpenCode, and Codex prompts against the mock `AiGateway` layer, which echoes the user message. `nub run test:e2e:all` adds the deferred external integrations and Claude Code; `nub run test:e2e:external` selects only those deferred cases. Focus one area with `nub run test:e2e -- tests/<file>.e2e.ts` after reading its feature file under `features/`. Read External fixture preflight in `docs/e2e.md` before deferred suites. Missing integration credentials or linked identities are blockers, and collection alone does not establish coverage.

Expand All @@ -47,6 +47,6 @@ Cloud-agent proof follows [Cloud-agent PR artifacts](../../../AGENTS.md#cloud-ag

Use only the isolated e2e profile and disposable fixtures. Do not run `infra:deploy:*` or `db:copy-d1-to-planetscale` during verification. Preserve pinned Alchemy, Effect, Wrangler, and provider versions unless the requested fix requires a change. The e2e launcher owns process cleanup; preserve unrelated development stacks. Delete temporary integration fixtures and revoke minted tokens in `finally` blocks.

When e2e stops the app target, the target stops `alchemy dev`, removes the run's labelled Docker containers and images, and removes `.e2e/runs/<UUID>/`. It never calls the Cloudflare API. Preserve the replay cache, the shared `default` gateway, and unrelated resources. Runs are local only, so there is no recovery command; after a SIGKILL, stop leftover processes by hand. See `docs/e2e.md` for remote preview limitations.
When e2e stops the app target, the target stops `alchemy dev`, removes the run's labelled Docker containers and images, and removes `.e2e/runs/<UUID>/`. The ordinary core launcher never calls the Cloudflare API. The separate AI Search launcher creates unique owned namespaces, deletes their instances and namespaces, verifies absence, and records ownership receipts. Preserve the replay cache, the shared `default` gateway, and unrelated resources. Runs are local only, so there is no recovery command; after a SIGKILL, stop leftover processes by hand. See `docs/e2e.md` for remote preview limitations.

The previous `control-solzero` Chrome driver and its recipes were removed. Keep new regression cases in the shared e2e suite rather than rebuilding that driver.
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ Keep these product facts honest:
The user deferred live positive results for these fixtures. Authored cases and preflight
remain available; do not claim green external coverage without a real passing run.
Broader linking/clone/webhook/event paths remain unverified in `docs/e2e.md`.

Built-in AI Search creation, Items upload/index/search/delete, workflow ingestion, and Admin UI
checks live in `tests/ai-search.external.e2e.ts`. Use the isolated launcher in `docs/e2e.md`;
it creates a fresh namespace pair and verifies cleanup. No seeded standing source is reused.
An API proof does not establish the separate browser authoring flow.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
"schemaVersion": "trace-1",
"createdAt": "2026-11-09T16:29:47.957Z",
"payload": {
"actions": [
{
"name": "tap",
"summary": "tap button \"New Source\"",
"target": {
"role": "button",
"name": "New Source"
}
}
],
"executor": {
"name": "decision",
"version": "2"
},
"recordedFor": {
"testId": "tests/ai-search.external.e2e.ts::administrators%20create%20built-in%20sources%20and%20open%20their%20documents",
"targetId": "local-cloudflare",
"instructionDigest": "a09dac185a810615ce3ee09265cc9c5ec6490c161918d7e86dbb71c0863cadc2",
"paramsDigest": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
"callIndex": 0,
"agent": "default"
},
"recordedBy": {
"provider": "anthropic.messages",
"model": "anthropic/claude-haiku-5.5"
},
"summary": "The screen shows the step is done (p=0.968, confidence 0.905).",
"startPath": "/admin/ai-search",
"endPath": "/admin/ai-search",
"endAnchors": [
{
"role": "heading",
"name": "Create Source"
},
{
"text": "Configure the Cloudflare AI Search instance exposed to agent sessions."
},
{
"role": "button",
"name": "Close AI Search source dialog"
},
{
"text": "ID"
},
{
"text": "Used as the source ID, Cloudflare AI Search instance ID, and basis for MCP tool names."
},
{
"role": "textbox",
"name": "ID",
"placeholder": "product-docs"
},
{
"text": "Label"
},
{
"text": "Human-readable name shown to admins and used in agent-facing tool descriptions."
}
],
"goneAnchors": [
{
"role": "link",
"name": "Fork Fixture"
},
{
"role": "link",
"name": "Agents"
},
{
"role": "button",
"name": "Expand agents menu"
},
{
"role": "link",
"name": "Workflows"
},
{
"role": "link",
"name": "Bots"
},
{
"text": "Mini Apps (coming soon)"
},
{
"text": "Admin"
},
{
"role": "link",
"name": "AI Search"
}
],
"endWaitMs": 11299,
"keyedBy": {
"cacheSchema": "trace-1",
"policyVersion": "conservative/6",
"project": "356bb4dad9f823ea5064055664450c1dbb1e4d9cbbfb201ca71c7993a2c18dcd",
"platform": "web",
"engineName": "web",
"engineSpiVersion": 1,
"appIdentity": "e18a7246aa64bc6cabcfc1fff593fa36af4d2bccf2dc172e6d2c8d9ad9cb96af",
"agentContextDigest": "44e79ebe1b494e7f7d7911f15d6f0a211137848d9b4d3549ff598d1cc728f3e8"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"schemaVersion": "trace-1",
"createdAt": "2026-11-09T16:29:47.958Z",
"payload": {
"actions": [
{
"name": "tap",
"summary": "tap button \"Documents\" in \"{{param:/label}}\"",
"target": {
"role": "button",
"name": "Documents",
"within": "{{param:/label}}"
}
}
],
"executor": {
"name": "decision",
"version": "2"
},
"recordedFor": {
"testId": "tests/ai-search.external.e2e.ts::administrators%20create%20built-in%20sources%20and%20open%20their%20documents",
"targetId": "local-cloudflare",
"instructionDigest": "416778188b6ffd4dbdb3a735e3dd1f0e3077defec59f5954becffa183587b79e",
"paramsDigest": "44fcbcfb85a7779b6de0f181cfc52ea37d67de119015c2eaf599e1f9843e0d7f",
"callIndex": 0,
"agent": "default"
},
"recordedBy": {
"provider": "anthropic.messages",
"model": "anthropic/claude-haiku-5.5"
},
"summary": "The screen shows the step is done (p=0.924, confidence 0.787).",
"startPath": "/admin/ai-search",
"endPath": "/admin/ai-search",
"endAnchors": [
{
"text": "Close documents"
},
{
"text": "Uploads are indexed asynchronously. Refresh to check processing status. A matching filename replaces that document. PDF and text files support up to 10 MiB; other formats support up to 4 MiB."
},
{
"role": "button",
"name": "Upload document"
},
{
"text": "Refresh documents"
},
{
"text": "Previous documents"
},
{
"text": "Page 1"
},
{
"text": "Next documents"
},
{
"role": "button",
"name": "Close documents"
}
],
"endWaitMs": 11434,
"keyedBy": {
"cacheSchema": "trace-1",
"policyVersion": "conservative/6",
"project": "356bb4dad9f823ea5064055664450c1dbb1e4d9cbbfb201ca71c7993a2c18dcd",
"platform": "web",
"engineName": "web",
"engineSpiVersion": 1,
"appIdentity": "e18a7246aa64bc6cabcfc1fff593fa36af4d2bccf2dc172e6d2c8d9ad9cb96af",
"agentContextDigest": "44e79ebe1b494e7f7d7911f15d6f0a211137848d9b4d3549ff598d1cc728f3e8"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"schemaVersion": "trace-1",
"createdAt": "2026-11-09T16:29:47.958Z",
"payload": {
"actions": [
{
"name": "tap",
"summary": "tap button \"Create Source\"",
"target": {
"role": "button",
"name": "Create Source"
}
}
],
"executor": {
"name": "decision",
"version": "2"
},
"recordedFor": {
"testId": "tests/ai-search.external.e2e.ts::administrators%20create%20built-in%20sources%20and%20open%20their%20documents",
"targetId": "local-cloudflare",
"instructionDigest": "4cd8aaec5b19f7210f68f2226e1c58bb99802b65701db0dbed3b7b08bdfed84a",
"paramsDigest": "44fcbcfb85a7779b6de0f181cfc52ea37d67de119015c2eaf599e1f9843e0d7f",
"callIndex": 0,
"agent": "default"
},
"recordedBy": {
"provider": "anthropic.messages",
"model": "anthropic/claude-haiku-5.5"
},
"summary": "The screen shows the step is done (p=0.658, confidence 0.273).",
"startPath": "/admin/ai-search",
"endPath": "/admin/ai-search",
"endAnchors": [
{
"role": "status",
"name": "Loading"
},
{
"role": "button",
"name": "Loading Create Source"
}
],
"goneAnchors": [
{
"role": "button",
"name": "Create Source"
}
],
"endWaitMs": 11113,
"keyedBy": {
"cacheSchema": "trace-1",
"policyVersion": "conservative/6",
"project": "356bb4dad9f823ea5064055664450c1dbb1e4d9cbbfb201ca71c7993a2c18dcd",
"platform": "web",
"engineName": "web",
"engineSpiVersion": 1,
"appIdentity": "e18a7246aa64bc6cabcfc1fff593fa36af4d2bccf2dc172e6d2c8d9ad9cb96af",
"agentContextDigest": "44e79ebe1b494e7f7d7911f15d6f0a211137848d9b4d3549ff598d1cc728f3e8"
}
}
}
2 changes: 0 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ jobs:
GITHUB_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
GITHUB_APP_WEBHOOK_SECRET: ${{ secrets.GH_APP_WEBHOOK_SECRET }}
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
CF_AI_SEARCH_SERVICE_TOKEN_ID: ${{ secrets.CF_AI_SEARCH_SERVICE_TOKEN_ID }}
run: |
nub run config:write-stage-vars -- ${{ inputs.environment }} config/.${{ inputs.environment }}.vars

Expand All @@ -91,5 +90,4 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
PLANETSCALE_SERVICE_TOKEN_ID: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_ID }}
PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
CF_AI_SEARCH_SERVICE_TOKEN_ID: ${{ secrets.CF_AI_SEARCH_SERVICE_TOKEN_ID }}
WRANGLER_SEND_METRICS: "false"
4 changes: 0 additions & 4 deletions .github/workflows/pr-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ jobs:
GITHUB_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
GITHUB_APP_WEBHOOK_SECRET: ${{ secrets.GH_APP_WEBHOOK_SECRET }}
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
CF_AI_SEARCH_SERVICE_TOKEN_ID: ${{ secrets.CF_AI_SEARCH_SERVICE_TOKEN_ID }}
run: |
nub run config:write-stage-vars -- pre config/.pre.vars

Expand All @@ -165,7 +164,6 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
PLANETSCALE_SERVICE_TOKEN_ID: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_ID }}
PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
CF_AI_SEARCH_SERVICE_TOKEN_ID: ${{ secrets.CF_AI_SEARCH_SERVICE_TOKEN_ID }}
WRANGLER_SEND_METRICS: "false"
PREVIEW_STAGE: ${{ steps.deploy-lifetime.outputs.stage }}

Expand Down Expand Up @@ -264,7 +262,6 @@ jobs:
GITHUB_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
GITHUB_APP_WEBHOOK_SECRET: ${{ secrets.GH_APP_WEBHOOK_SECRET }}
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
CF_AI_SEARCH_SERVICE_TOKEN_ID: ${{ secrets.CF_AI_SEARCH_SERVICE_TOKEN_ID }}
run: |
nub run config:write-stage-vars -- pre config/.pre.vars

Expand All @@ -281,7 +278,6 @@ jobs:
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CF_AI_SEARCH_SERVICE_TOKEN_ID: ${{ secrets.CF_AI_SEARCH_SERVICE_TOKEN_ID }}
WRANGLER_SEND_METRICS: "false"
PREVIEW_STAGE: ${{ steps.cleanup-lifetime.outputs.stage }}

Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ jobs:
GITHUB_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
GITHUB_APP_WEBHOOK_SECRET: ${{ secrets.GH_APP_WEBHOOK_SECRET }}
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
CF_AI_SEARCH_SERVICE_TOKEN_ID: ${{ secrets.CF_AI_SEARCH_SERVICE_TOKEN_ID }}
run: |
nub run config:write-stage-vars -- pre config/.pre.vars

Expand All @@ -122,6 +121,5 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
PLANETSCALE_SERVICE_TOKEN_ID: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_ID }}
PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
CF_AI_SEARCH_SERVICE_TOKEN_ID: ${{ secrets.CF_AI_SEARCH_SERVICE_TOKEN_ID }}
WRANGLER_SEND_METRICS: "false"
ALCHEMY_STAGE: pre
16 changes: 16 additions & 0 deletions .tegami/2026-11-09-ai-search-built-in-storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
packages:
"release:solzero": major
---

## Upload documents directly to AI Search

AI Search sources now use built-in storage. Administrators can upload and delete documents and
inspect indexing status without an external-storage service token. Workflows can ingest text and
JSON with the new Upload to AI Search node.

R2 and website source creation is removed. Existing source records and original content remain
intact and are marked as requiring migration; these sources are unavailable for retrieval until
operators copy their originals into new built-in sources and update their selections. Follow
`docs/ai-search.md` before cutover. Deployment does not copy or delete legacy content. Workflow
artifact and skill storage remain unchanged.
Loading
Loading