Skip to content

refactor(think): remove convention framework - #2063

Merged
cjol merged 1 commit into
mainfrom
investigate-2046
Aug 11, 2026
Merged

refactor(think): remove convention framework#2063
cjol merged 1 commit into
mainfrom
investigate-2046

Conversation

@cjol

@cjol cjol commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

This PR removes Think's convention-driven Vite framework and CLI while keeping @cloudflare/think as an explicit runtime for hand-written Worker entries. It also retires the coupled scaffolding and host-framework examples. Fixes #2046.

Why

  • The experimental framework generated Worker entries, virtual modules, Durable Object exports, routes, Wrangler configuration, and types from repository conventions. This created a second configuration layer over standard Workers and Agents primitives.
  • The Vite plugin and CLI shipped with an explicit warning that they could change or be removed in any release.
  • We could retain compatibility stubs or convert create-think to static templates, but that would preserve the convention layer's maintenance surface. The supported direction is explicit Worker entries, exports, bindings, migrations, and routing.
  • Create Cloudflare remains available through the separate cloudflare/agents-starter repository and does not depend on these removed surfaces.

Public API Surface

These removals are breaking:

Surface Change Replacement
@cloudflare/think/vite Removed agents/vite for decorators and agents:skills, plus @cloudflare/vite-plugin
@cloudflare/think/framework Removed Explicit Worker classes and configuration
@cloudflare/think/server-entry Removed routeAgentRequest, routeSubAgentRequest, and application-owned handlers
think binary Removed Wrangler, Vite, and explicit source/configuration files
create-think Removed from the repository Create Cloudflare or a manually configured Think Worker

The Think runtime, React integration, messengers, workflows, extensions, and tool entrypoints remain available.

Architectural Changes

Before:

agents/ conventions
        |
@cloudflare/think/vite
        |
virtual:think/* modules
        |
generated Worker entry, exports, routing, types, and config

After:

Think subclasses
        |
explicit src/server.ts exports and routing
        |
explicit wrangler.jsonc bindings and migrations
        |
agents/vite + @cloudflare/vite-plugin where needed

Code Changes

  • @cloudflare/think no longer builds or publishes the Vite plugin, framework helpers, server-entry helpers, CLI, or Studio bundle. Package dependencies and test wiring now cover only the retained runtime.
  • examples/assistant now uses an explicit Worker entry and routeSubAgentRequest. It exports the readable AssistantDirectory and MyAssistant runtime classes directly and declares the root binding and migration explicitly.
  • The explicit Assistant entry exports CodemodeRuntime, and its Vite configuration uses agents/vite directly for bundled Agent Skills.
  • packages/create-think, the six think-starters, and the React Router and TanStack Start framework examples are removed.
  • Current Think documentation now shows explicit Worker exports, Wrangler bindings and migrations, routing, and Agent Skills plugin registration.
  • Workspace, lockfile, changeset, package-build, test-matrix, and reproduction-skill wiring no longer reference the deleted projects.

Compatibility

  • Applications using the removed Vite plugin must replace virtual:think/entry with an explicit Worker entry and declare their Durable Object exports, bindings, migrations, and routing.
  • Persistently deployed applications must preserve their generated Durable Object class names and migration history when moving to explicit entries. TypeScript aliases alone are not enough when sub-agent registries depend on Class.name.
  • Historical create-think releases fetch templates from the deleted think-starters paths and are not retained as a compatibility path.
  • npm create cloudflare with cloudflare/agents-starter remains unaffected because that repository already uses an explicit Worker setup.

Open in Devin Review
@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 53a1e8a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@cloudflare/think Minor
@cloudflare/agent-think Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review
Comment on lines +93 to +102
// Requests without a child segment belong to the directory itself
// (including `/chat` and `/chat/mcp-callback`).
if (url.pathname.startsWith(SUB_AGENT_PREFIX)) {
const childPath = url.pathname.slice(SUB_AGENT_PREFIX.length);
return routeSubAgentRequest(request, directory, {
fromPath: `/sub/${SUB_AGENT_SEGMENT}/${childPath}`
});
}

return directory.fetch(request);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Chat links naming a non-existent sub-chat silently open the parent directory instead of returning not-found

A chat URL whose child segment does not name a real child class is handed straight to the user's directory (directory.fetch(request) at examples/assistant/src/server.ts:102) instead of being rejected, so a malformed chat link quietly opens the directory conversation rather than reporting that the chat does not exist.

Impact: Users following a bad or stale chat link get connected to their top-level directory instead of a clear "not found", which is confusing and hides typos in links.

Why the fallthrough happens: prefix match is class-specific, and the parent ignores unresolvable sub markers

SUB_AGENT_PREFIX is the literal /chat/sub/my-assistant/ (examples/assistant/src/server.ts:38-39). A request such as /chat/sub/other-thing/abc does not match that prefix, so it takes the directory.fetch(request) branch. Inside the parent, Agent.fetch parses the sub marker against ctx.exports keys (packages/agents/src/index.ts:7015-7021); other-thing resolves to no exported class, parseSubAgentPath returns null, and the request is served by the directory itself via super.fetch.

The removed implementation routed the whole /chat* space through think.router.routeSubAgent(...), which returned 404 whenever a /sub/... segment could not be resolved for the declared parent. To keep that contract, detect any /chat/sub/ path that is not the known child prefix and return 404 before falling back to the directory.

Suggested change
// Requests without a child segment belong to the directory itself
// (including `/chat` and `/chat/mcp-callback`).
if (url.pathname.startsWith(SUB_AGENT_PREFIX)) {
const childPath = url.pathname.slice(SUB_AGENT_PREFIX.length);
return routeSubAgentRequest(request, directory, {
fromPath: `/sub/${SUB_AGENT_SEGMENT}/${childPath}`
});
}
return directory.fetch(request);
// Requests without a child segment belong to the directory itself
// (including `/chat` and `/chat/mcp-callback`).
if (url.pathname.startsWith(SUB_AGENT_PREFIX)) {
const childPath = url.pathname.slice(SUB_AGENT_PREFIX.length);
return routeSubAgentRequest(request, directory, {
fromPath: `/sub/${SUB_AGENT_SEGMENT}/${childPath}`
});
}
// Any other `/chat/sub/...` shape names a child that does not
// exist — reject it rather than serving the directory.
if (url.pathname.startsWith("/chat/sub/")) {
return new Response("Not found", { status: 404 });
}
return directory.fetch(request);
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@pkg-pr-new

pkg-pr-new Bot commented Aug 6, 2026

Copy link
Copy Markdown

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@2063

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@2063

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@2063

hono-agents

npm i https://pkg.pr.new/hono-agents@2063

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@2063

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@2063

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@2063

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@2063

commit: 53a1e8a

@cjol

cjol commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Tagging @threepointone to check this is the surface you had in mind for removal

@cjol
cjol requested a review from threepointone August 6, 2026 14:53
@cjol
cjol merged commit 3de6c8e into main Aug 11, 2026
8 checks passed
@cjol
cjol deleted the investigate-2046 branch August 11, 2026 16:35
@github-actions github-actions Bot mentioned this pull request Aug 11, 2026
cjol added a commit that referenced this pull request Aug 24, 2026
Main vendored the PartyServer runtime into packages/agents/src/lifecycle
and dropped the `Server` base class (#2133). `Agent` now extends
`DurableObject` directly and owns a `Lifecycle`, which moved the whole
cold-RPC boundary this branch depends on.

Resolutions:

- `__unsafe_ensureInitialized()` keeps its cold-RPC guard but now starts
  the lifecycle instead of delegating to a `Server` supermethod.
- The wrapper exclusion set can no longer be derived from
  `Server.prototype`. The platform-owned part still comes from
  `DurableObject.prototype`; the lifecycle callbacks come from a new
  exported `LifecycleHostCallback` union checked with `satisfies`, so a
  new callback cannot silently become wrappable; the remaining runtime
  entry points and lifecycle accessors are listed explicitly.
- Sub-agent connection RPC combines this branch's initialization with
  main's `this.lifecycle.getConnection()`.
- `setName()` no longer exists, so the naming regressions now cover the
  lifecycle's read-only `__ps_name` migration and its addressing error.
- The changeset and `docs/agents/lifecycle.md` describe the lifecycle
  contract rather than PartyServer bootstrapping.

Also removes stale local build output for the examples and package that
#2063 deleted.

Validated with `pnpm run check` (114 projects) and
`nx run-many -t test` for `agents` (2605) and `@cloudflare/think` (890).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant