@getjack/jack 0.1.35 → 0.1.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getjack/jack",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "description": "Ship before you forget why you started. The vibecoder's deployment CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -65,6 +65,7 @@ export default async function update(): Promise<void> {
65
65
  const installedApps = await installMcpConfigsToAllApps();
66
66
  if (installedApps.length > 0) {
67
67
  info(`MCP config updated for: ${installedApps.join(", ")}`);
68
+ info("Restart your coding agent session to use the new version");
68
69
  }
69
70
  } catch {
70
71
  // Non-critical - don't fail update if MCP repair fails
package/src/index.ts CHANGED
@@ -58,6 +58,9 @@ const cli = meow(
58
58
 
59
59
  Run 'jack <command> --help' for command-specific options.
60
60
 
61
+ Full docs: https://docs.getjack.org
62
+ AI context: https://docs.getjack.org/llms.txt
63
+
61
64
  Examples
62
65
  $ jack init Set up once
63
66
  $ jack new my-app Create and deploy
@@ -249,6 +252,11 @@ if (!skipVersionCheck) {
249
252
  updateCheckPromise = checkForUpdate().catch(() => null);
250
253
  }
251
254
 
255
+ // Handle --help for subcommands (meow only auto-handles --help when no positional args)
256
+ if (command && (cli.flags as Record<string, unknown>).help) {
257
+ cli.showHelp(0);
258
+ }
259
+
252
260
  try {
253
261
  switch (command) {
254
262
  case "init": {
@@ -187,6 +187,50 @@ export async function checkSlugAvailability(slug: string): Promise<SlugAvailabil
187
187
  return response.json() as Promise<SlugAvailabilityResponse>;
188
188
  }
189
189
 
190
+ // ============================================================================
191
+ // Project Overview (unified endpoint)
192
+ // ============================================================================
193
+
194
+ export interface ProjectOverview {
195
+ project: {
196
+ id: string;
197
+ name: string;
198
+ slug: string;
199
+ status: string;
200
+ url: string;
201
+ tags: string[];
202
+ created_at: string;
203
+ updated_at: string;
204
+ };
205
+ resources: ProjectResource[];
206
+ latest_deployment: DeploymentInfo | null;
207
+ crons: CronScheduleInfo[];
208
+ }
209
+
210
+ /**
211
+ * Fetch a unified project overview: project info, resources, latest deployment, and crons.
212
+ * Single API call replaces 3-5 separate calls.
213
+ */
214
+ export async function fetchProjectOverview(projectId: string): Promise<ProjectOverview> {
215
+ const { authFetch } = await import("./auth/index.ts");
216
+
217
+ const response = await authFetch(
218
+ `${getControlApiUrl()}/v1/projects/${encodeURIComponent(projectId)}/overview`,
219
+ );
220
+
221
+ if (!response.ok) {
222
+ if (response.status === 404) {
223
+ throw new Error("Project not found");
224
+ }
225
+ const err = (await response.json().catch(() => ({ message: "Unknown error" }))) as {
226
+ message?: string;
227
+ };
228
+ throw new Error(err.message || `Failed to fetch project overview: ${response.status}`);
229
+ }
230
+
231
+ return response.json() as Promise<ProjectOverview>;
232
+ }
233
+
190
234
  export interface DatabaseInfoResponse {
191
235
  name: string;
192
236
  id: string;
@@ -2013,30 +2013,8 @@ export async function getProjectStatus(
2013
2013
  workerUrl = await buildManagedUrl(projectName, link.owner_username, resolvedPath);
2014
2014
  }
2015
2015
 
2016
- // Get database name on-demand
2016
+ // Get database name and deployment data
2017
2017
  let dbName: string | null = null;
2018
- if (link?.deploy_mode === "managed") {
2019
- // For managed projects, fetch from control plane
2020
- try {
2021
- const { fetchProjectResources } = await import("./control-plane.ts");
2022
- const resources = await fetchProjectResources(link.project_id);
2023
- const d1 = resources.find((r) => r.resource_type === "d1");
2024
- dbName = d1?.resource_name || null;
2025
- } catch {
2026
- // Ignore errors, dbName stays null
2027
- }
2028
- } else if (localExists) {
2029
- // For BYO, parse from wrangler config
2030
- try {
2031
- const { parseWranglerResources } = await import("./resources.ts");
2032
- const resources = await parseWranglerResources(resolvedPath);
2033
- dbName = resources.d1?.name || null;
2034
- } catch {
2035
- // Ignore errors, dbName stays null
2036
- }
2037
- }
2038
-
2039
- // Fetch real deployment data for managed projects
2040
2018
  let lastDeployAt: string | null = null;
2041
2019
  let deployCount = 0;
2042
2020
  let lastDeployStatus: string | null = null;
@@ -2044,11 +2022,14 @@ export async function getProjectStatus(
2044
2022
  let lastDeployMessage: string | null = null;
2045
2023
 
2046
2024
  if (link?.deploy_mode === "managed") {
2025
+ // Single overview call replaces fetchProjectResources + fetchDeployments
2047
2026
  try {
2048
- const { fetchDeployments } = await import("./control-plane.ts");
2049
- const result = await fetchDeployments(link.project_id);
2050
- deployCount = result.total;
2051
- const latest = result.deployments[0];
2027
+ const { fetchProjectOverview } = await import("./control-plane.ts");
2028
+ const overview = await fetchProjectOverview(link.project_id);
2029
+ const d1 = overview.resources.find((r) => r.resource_type === "d1");
2030
+ dbName = d1?.resource_name || null;
2031
+
2032
+ const latest = overview.latest_deployment;
2052
2033
  if (latest) {
2053
2034
  lastDeployAt = latest.created_at;
2054
2035
  lastDeployStatus = latest.status;
@@ -2056,7 +2037,16 @@ export async function getProjectStatus(
2056
2037
  lastDeployMessage = latest.message;
2057
2038
  }
2058
2039
  } catch {
2059
- // Silent fail — deploy tracking is supplementary
2040
+ // Silent fail — supplementary data
2041
+ }
2042
+ } else if (localExists) {
2043
+ // For BYO, parse from wrangler config
2044
+ try {
2045
+ const { parseWranglerResources } = await import("./resources.ts");
2046
+ const resources = await parseWranglerResources(resolvedPath);
2047
+ dbName = resources.d1?.name || null;
2048
+ } catch {
2049
+ // Ignore errors, dbName stays null
2060
2050
  }
2061
2051
  }
2062
2052
 
@@ -11,7 +11,9 @@ import { existsSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
  import {
13
13
  type CronScheduleInfo,
14
+ type ProjectOverview,
14
15
  executeManagedSql,
16
+ fetchProjectOverview,
15
17
  fetchProjectResources,
16
18
  listCronSchedules as listCronSchedulesApi,
17
19
  } from "../control-plane.ts";
@@ -99,6 +101,90 @@ export async function getProjectEnvironment(projectDir: string): Promise<Project
99
101
  const link = await readProjectLink(projectDir);
100
102
  const deployMode: DeployMode = link?.deploy_mode ?? "byo";
101
103
 
104
+ // Managed mode: use single overview call instead of 3-5 separate calls
105
+ if (deployMode === "managed" && link?.project_id) {
106
+ return getManagedProjectEnvironment(projectDir, link.project_id);
107
+ }
108
+
109
+ // BYO mode: original multi-call path
110
+ return getByoProjectEnvironment(projectDir, deployMode, link?.project_id);
111
+ }
112
+
113
+ /**
114
+ * Managed mode: single overview call + local enrichment.
115
+ */
116
+ async function getManagedProjectEnvironment(
117
+ projectDir: string,
118
+ projectId: string,
119
+ ): Promise<ProjectEnvironment> {
120
+ // 1. Single API call for project + resources + deployment + crons
121
+ const overview = await fetchProjectOverview(projectId);
122
+
123
+ // 2. Convert resources to bindings
124
+ const rawResources = overview.resources as ControlPlaneResource[];
125
+ const resolved = convertControlPlaneResources(rawResources);
126
+ const bindings = buildBindingsFromResolved(resolved);
127
+
128
+ // Also parse wrangler.jsonc for vectorize and durable_objects
129
+ await enrichFromWranglerConfig(projectDir, bindings);
130
+
131
+ // 3. Get variables from wrangler.jsonc
132
+ const wranglerResources = await parseWranglerResourcesSafe(projectDir);
133
+ const variables = wranglerResources?.vars ?? {};
134
+
135
+ // 4. Get secrets names
136
+ const secretsSet = await getSecretsNames(projectDir);
137
+
138
+ // 5. Get database schema (if D1 exists)
139
+ let database: DatabaseSchema | null = null;
140
+ if (bindings.d1) {
141
+ database = await getDatabaseSchema(projectDir, "managed", projectId, bindings.d1.database_name);
142
+ }
143
+
144
+ // 6. Map crons from overview
145
+ const crons: CronEntry[] = overview.crons.map((s) => ({
146
+ expression: s.expression,
147
+ enabled: s.enabled,
148
+ last_run_status: s.last_run_status,
149
+ }));
150
+
151
+ // 7. Detect issues
152
+ const issues = detectIssues(bindings, rawResources, wranglerResources, projectDir);
153
+
154
+ // 8. Map deployment info
155
+ const dep = overview.latest_deployment;
156
+
157
+ return {
158
+ project: {
159
+ name: overview.project.name,
160
+ url: overview.project.url,
161
+ deploy_mode: "managed",
162
+ last_deploy: dep
163
+ ? {
164
+ at: dep.created_at,
165
+ status: dep.status,
166
+ message: dep.message,
167
+ source: dep.source,
168
+ }
169
+ : null,
170
+ },
171
+ bindings,
172
+ secrets_set: secretsSet,
173
+ variables,
174
+ database,
175
+ crons,
176
+ issues,
177
+ };
178
+ }
179
+
180
+ /**
181
+ * BYO mode: original multi-call path.
182
+ */
183
+ async function getByoProjectEnvironment(
184
+ projectDir: string,
185
+ deployMode: DeployMode,
186
+ projectId?: string,
187
+ ): Promise<ProjectEnvironment> {
102
188
  // 1. Get project status (name, URL, deploy info)
103
189
  const status = await getProjectStatus(undefined, projectDir);
104
190
  if (!status) {
@@ -109,7 +195,7 @@ export async function getProjectEnvironment(projectDir: string): Promise<Project
109
195
  const { bindings, rawResources, wranglerResources } = await getBindings(
110
196
  projectDir,
111
197
  deployMode,
112
- link?.project_id,
198
+ projectId,
113
199
  );
114
200
 
115
201
  // 3. Get variables from wrangler.jsonc
@@ -124,18 +210,12 @@ export async function getProjectEnvironment(projectDir: string): Promise<Project
124
210
  database = await getDatabaseSchema(
125
211
  projectDir,
126
212
  deployMode,
127
- link?.project_id,
213
+ projectId,
128
214
  bindings.d1.database_name,
129
215
  );
130
216
  }
131
217
 
132
- // 6. Get crons (managed only)
133
- let crons: CronEntry[] = [];
134
- if (deployMode === "managed" && link?.project_id) {
135
- crons = await getCrons(link.project_id);
136
- }
137
-
138
- // 7. Detect issues
218
+ // 6. Detect issues
139
219
  const issues = detectIssues(bindings, rawResources, wranglerResources, projectDir);
140
220
 
141
221
  return {
@@ -157,11 +237,53 @@ export async function getProjectEnvironment(projectDir: string): Promise<Project
157
237
  secrets_set: secretsSet,
158
238
  variables,
159
239
  database,
160
- crons,
240
+ crons: [], // BYO doesn't have managed crons
161
241
  issues,
162
242
  };
163
243
  }
164
244
 
245
+ /**
246
+ * Build EnvironmentBindings from resolved resources.
247
+ */
248
+ function buildBindingsFromResolved(resolved: ResolvedResources): EnvironmentBindings {
249
+ const result: EnvironmentBindings = {};
250
+ if (resolved.d1) {
251
+ result.d1 = {
252
+ binding: resolved.d1.binding,
253
+ database_name: resolved.d1.name,
254
+ database_id: resolved.d1.id,
255
+ };
256
+ }
257
+ if (resolved.r2?.length) {
258
+ result.r2 = resolved.r2.map((r) => ({
259
+ binding: r.binding,
260
+ bucket_name: r.name,
261
+ }));
262
+ }
263
+ if (resolved.kv?.length) {
264
+ result.kv = resolved.kv.map((k) => ({
265
+ binding: k.binding,
266
+ namespace_id: k.id,
267
+ name: k.name,
268
+ }));
269
+ }
270
+ if (resolved.ai) {
271
+ result.ai = { binding: resolved.ai.binding };
272
+ }
273
+ return result;
274
+ }
275
+
276
+ /**
277
+ * Safely parse wrangler resources, returning undefined on failure.
278
+ */
279
+ async function parseWranglerResourcesSafe(projectDir: string): Promise<ResolvedResources | undefined> {
280
+ try {
281
+ return await parseWranglerResources(projectDir);
282
+ } catch {
283
+ return undefined;
284
+ }
285
+ }
286
+
165
287
  // ============================================================================
166
288
  // Internal Helpers
167
289
  // ============================================================================
@@ -332,7 +332,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
332
332
  {
333
333
  name: "create_project",
334
334
  description:
335
- "Create a new project from a template. Automatically installs dependencies, deploys, and registers the project. Also supports forking: pass a 'username/slug' template to fork a published project, or a project slug to fork your own.",
335
+ "Create a new project from a template. Returns targetDir you MUST use that path as your working directory for all subsequent file edits and tool calls (e.g. deploy_project project_path). Projects are created in ~/.jack/projects/<name>. Also supports forking: pass 'username/slug' to fork a published project.",
336
336
  inputSchema: {
337
337
  type: "object",
338
338
  properties: {
@@ -939,7 +939,14 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
939
939
  content: [
940
940
  {
941
941
  type: "text",
942
- text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
942
+ text: JSON.stringify(
943
+ formatSuccessResponse(result, startTime, [
944
+ `IMPORTANT: Your working directory for this project is ${result.targetDir} — use this path for all file edits and as project_path in subsequent tool calls (deploy_project, execute_sql, etc).`,
945
+ `The project has AGENTS.md and CLAUDE.md with full context. Read ${result.targetDir}/AGENTS.md first.`,
946
+ ]),
947
+ null,
948
+ 2,
949
+ ),
943
950
  },
944
951
  ],
945
952
  };
@@ -6,9 +6,9 @@
6
6
  "name": "jack-template",
7
7
  "dependencies": {
8
8
  "@ai-sdk/react": "^3.0.88",
9
- "@cloudflare/ai-chat": "^0.0.8",
9
+ "@cloudflare/ai-chat": "^0.1.0",
10
10
  "@tailwindcss/typography": "^0.5.19",
11
- "agents": "^0.4",
11
+ "agents": "^0.5",
12
12
  "ai": "^6",
13
13
  "class-variance-authority": "^0.7.1",
14
14
  "clsx": "^2.1.1",
@@ -117,27 +117,25 @@
117
117
 
118
118
  "@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
119
119
 
120
- "@cloudflare/ai-chat": ["@cloudflare/ai-chat@0.0.8", "", { "peerDependencies": { "agents": "^0.4.1", "ai": "^6.0.0", "react": "^19.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-KzHcirpAJWMNveo86qvA5ZFAEMgpUOb9/VM0sm3lowu2APvcLhCPMZQDlykicnqV2Gf0uFLBDgoslQbjtPHVxg=="],
121
-
122
- "@cloudflare/codemode": ["@cloudflare/codemode@0.0.6", "", { "dependencies": { "zod-to-ts": "^2.0.0" }, "peerDependencies": { "agents": "^0.3.7", "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-P8ba7fgyeOOEODgU+lyUj82P89VfslKExsdF6nPGRebIbgiCbbpoLHBmBtdRRsIasVHUhceSazJxIS6dg70yRA=="],
120
+ "@cloudflare/ai-chat": ["@cloudflare/ai-chat@0.1.1", "", { "peerDependencies": { "@ai-sdk/react": "^3.0.0", "agents": "^0.5.0", "ai": "^6.0.0", "react": "^19.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-FOjvObW0S3oKQBuoMvHlwyOPSek8i4HKn07HHZGu/Noz5gPElbpiGZyCYmSPZAiOzMFgNLuJso5lbrHBjB9QdA=="],
123
121
 
124
122
  "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="],
125
123
 
126
- "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.12.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20260115.0" }, "optionalPeers": ["workerd"] }, "sha512-tP/Wi+40aBJovonSNJSsS7aFJY0xjuckKplmzDs2Xat06BJ68B6iG7YDUWXJL8gNn0gqW7YC5WhlYhO3QbugQA=="],
124
+ "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.13.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20260213.0" }, "optionalPeers": ["workerd"] }, "sha512-bT2rnecesLjDBHgouMEPW9EQ7iLE8OG58srMuCEpAGp75xabi6j124SdS8XZ+dzB3sYBW4iQvVeCTCbAnMMVtA=="],
127
125
 
128
- "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.25.0", "", { "dependencies": { "@cloudflare/unenv-preset": "2.12.1", "miniflare": "4.20260212.0", "unenv": "2.0.0-rc.24", "wrangler": "4.65.0", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-IZV17IekBxc7dcu8TZw5I8DTQ+F6CT8f/rUwLYrQZdsPnfaqbPoc8t9/2V9ci+XNfgGRBNJxd/4YGuyJg+h1pQ=="],
126
+ "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.25.1", "", { "dependencies": { "@cloudflare/unenv-preset": "2.13.0", "miniflare": "4.20260217.0", "unenv": "2.0.0-rc.24", "wrangler": "4.66.0", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-0i6L33AboDiNI/QYYwcfWeXHonQozTEPIqzFmvV8zqzgfjOijGMg08YQMxuJlkaVu6oPG/6umrSVawbDGY++vw=="],
129
127
 
130
- "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260212.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-kLxuYutk88Wlo7edp8mlkN68TgZZ9237SUnuX9kNaD5jcOdblUqiBctMRZeRcPsuoX/3g2t0vS4ga02NBEVRNg=="],
128
+ "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260217.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-t1KRT0j4gwLntixMoNujv/UaS89Q7+MPRhkklaSup5tNhl3zBZOIlasBUSir69eXetqLZu8sypx3i7zE395XXA=="],
131
129
 
132
- "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260212.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fqoqQWMA1D0ZzDOD8sp0allREM2M8GHdpxMXQ8EdZpZ70z5bJbJ9Vr4qe35++FNIZJspsDHfTw3Xm/M4ELm/dQ=="],
130
+ "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260217.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9pEZ15BmELt0Opy79LTxUvbo55QAI4GnsnsvmgBxaQlc4P0dC8iycBGxbOpegkXnRx/LFj51l2zunfTo0EdATg=="],
133
131
 
134
- "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260212.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bCSQoZzDzV5MSh4ueWo1DgmOn4Hf3QBu4Yo3eQFXA2llYFIu/sZgRtkEehw1X2/SY5Sn6O0EMCqxJYRf82Wdeg=="],
132
+ "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260217.0", "", { "os": "linux", "cpu": "x64" }, "sha512-IrZfxQ4b/4/RDQCJsyoxKrCR+cEqKl81yZOirMOKoRrDOmTjn4evYXaHoLBh2PjUKY1Imly7ZiC6G1p0xNIOwg=="],
135
133
 
136
- "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260212.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-GPvp1iiKQodtbUDi6OmR5I0vD75lawB54tdYGtmypuHC7ZOI2WhBmhb3wCxgnQNOG1z7mhCQrzRCoqrKwYbVWQ=="],
134
+ "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260217.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGU1wq69ym4sFBVWhQeddZrRrG0hJM/SlZ5DwVDga/zBJ3WXxcDsFAgg1dToDfildTde5ySXN7jAasSmWko9rg=="],
137
135
 
138
- "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260212.0", "", { "os": "win32", "cpu": "x64" }, "sha512-wHRI218Xn4ndgWJCUHH4Zx0YlU5q/o6OmcxXkcw95tJOsQn4lDrhppioPh4eScxJZALf2X+ODeZcyQTCq5exGw=="],
136
+ "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260217.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4T65u1321z1Zet9n7liQsSW7g3EXM5SWIT7kJ/uqkEtkPnIzZBIowMQgkvL5W9SpGZks9t3mTQj7hiUia8Gq9Q=="],
139
137
 
140
- "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260214.0", "", {}, "sha512-qb8rgbAdJR4BAPXolXhFL/wuGtecHLh1veOyZ1mK6QqWuCdI3vK1biKC0i3lzmzdLR/DZvsN3mNtpUE8zpWGEg=="],
138
+ "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260217.0", "", {}, "sha512-R5s8h/zj91g6HSB/qndpXGS5Xc8t8Ik3BwY6qwe7XXV6r3Gey1gdthFSK4A2IrPQEmTsc7wEXbs9KpBLNttlqg=="],
141
139
 
142
140
  "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
143
141
 
@@ -573,7 +571,7 @@
573
571
 
574
572
  "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
575
573
 
576
- "agents": ["agents@0.4.1", "", { "dependencies": { "@cfworker/json-schema": "^4.1.1", "@modelcontextprotocol/sdk": "1.26.0", "cron-schedule": "^6.0.0", "escape-html": "^1.0.3", "json-schema": "^0.4.0", "json-schema-to-typescript": "^15.0.4", "mimetext": "^3.0.28", "nanoid": "^5.1.6", "partyserver": "^0.1.4", "partysocket": "1.1.13", "yargs": "^18.0.0" }, "peerDependencies": { "@ai-sdk/openai": "^3.0.0", "@ai-sdk/react": "^3.0.0", "@cloudflare/ai-chat": "^0.0.8", "@cloudflare/codemode": "^0.0.7", "@x402/core": "^2.0.0", "@x402/evm": "^2.0.0", "ai": "^6.0.0", "react": "^19.0.0", "viem": ">=2.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["@ai-sdk/openai", "@ai-sdk/react", "@x402/core", "@x402/evm", "viem"], "bin": { "agents": "dist/cli/index.js" } }, "sha512-1ARZ5AXpPEn1+Wv/Dt++nH3iLStDxlci7Bm47sfE6qLlJpY9oG5nzyH2TRKwFFt9bMAgYz177i6p2NiEd/coVQ=="],
574
+ "agents": ["agents@0.5.0", "", { "dependencies": { "@cfworker/json-schema": "^4.1.1", "@modelcontextprotocol/sdk": "1.26.0", "cron-schedule": "^6.0.0", "escape-html": "^1.0.3", "json-schema": "^0.4.0", "json-schema-to-typescript": "^15.0.4", "mimetext": "^3.0.28", "nanoid": "^5.1.6", "partyserver": "^0.1.5", "partysocket": "1.1.13", "yargs": "^18.0.0" }, "peerDependencies": { "@ai-sdk/openai": "^3.0.0", "@ai-sdk/react": "^3.0.0", "@cloudflare/ai-chat": "^0.1.0", "@cloudflare/codemode": "^0.0.8", "@x402/core": "^2.0.0", "@x402/evm": "^2.0.0", "ai": "^6.0.0", "react": "^19.0.0", "viem": ">=2.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["@ai-sdk/openai", "@ai-sdk/react", "@cloudflare/ai-chat", "@cloudflare/codemode", "@x402/core", "@x402/evm", "viem"], "bin": { "agents": "dist/cli/index.js" } }, "sha512-iECyHMROnfESwO6SO44MQ3z/BP6e6MGZgBb980Zg1ls2WdBmxUTXHeaW4qUESWUhwj5LmtYCmuewfYCgIPKzZQ=="],
577
575
 
578
576
  "ai": ["ai@6.0.86", "", { "dependencies": { "@ai-sdk/gateway": "3.0.46", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.15", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-U2W2LBCHA/pr0Ui7vmmsjBiLEzBbZF3yVHNy7Rbzn7IX+SvoQPFM5rN74hhfVzZoE8zBuGD4nLLk+j0elGacvQ=="],
579
577
 
@@ -1079,9 +1077,9 @@
1079
1077
 
1080
1078
  "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
1081
1079
 
1082
- "miniflare": ["miniflare@4.20260212.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.18.2", "workerd": "1.20260212.0", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-Lgxq83EuR2q/0/DAVOSGXhXS1V7GDB04HVggoPsenQng8sqEDR3hO4FigIw5ZI2Sv2X7kIc30NCzGHJlCFIYWg=="],
1080
+ "miniflare": ["miniflare@4.20260217.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.18.2", "workerd": "1.20260217.0", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-t2v02Vi9SUiiXoHoxLvsntli7N35e/35PuRAYEqHWtHOdDX3bqQ73dBQ0tI12/8ThCb2by2tVs7qOvgwn6xSBQ=="],
1083
1081
 
1084
- "minimatch": ["minimatch@10.2.0", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w=="],
1082
+ "minimatch": ["minimatch@10.2.1", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A=="],
1085
1083
 
1086
1084
  "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
1087
1085
 
@@ -1381,11 +1379,11 @@
1381
1379
 
1382
1380
  "which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
1383
1381
 
1384
- "workerd": ["workerd@1.20260212.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260212.0", "@cloudflare/workerd-darwin-arm64": "1.20260212.0", "@cloudflare/workerd-linux-64": "1.20260212.0", "@cloudflare/workerd-linux-arm64": "1.20260212.0", "@cloudflare/workerd-windows-64": "1.20260212.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-4B9BoZUzKSRv3pVZGEPh7OX+Q817hpUqAUtz5O0TxJVqo4OsYJAUA/sY177Q5ha/twjT9KaJt2DtQzE+oyCOzw=="],
1382
+ "workerd": ["workerd@1.20260217.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260217.0", "@cloudflare/workerd-darwin-arm64": "1.20260217.0", "@cloudflare/workerd-linux-64": "1.20260217.0", "@cloudflare/workerd-linux-arm64": "1.20260217.0", "@cloudflare/workerd-windows-64": "1.20260217.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-6jVisS6wB6KbF+F9DVoDUy9p7MON8qZCFSaL8OcDUioMwknsUPFojUISu3/c30ZOZ24D4h7oqaahFc5C6huilw=="],
1385
1383
 
1386
1384
  "workers-ai-provider": ["workers-ai-provider@3.1.1", "", { "peerDependencies": { "@ai-sdk/provider": "^3.0.0", "ai": "^6.0.0" } }, "sha512-07Bnjp2390oAyfLASmWaujqW7KOt+vt0wdR7DAsLjsp3NzSKhvRAvicmHDkgdYR87R6NKjsEBWp+fpP87php4w=="],
1387
1385
 
1388
- "wrangler": ["wrangler@4.65.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.12.1", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260212.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260212.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260212.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-R+n3o3tlGzLK9I4fGocPReOuvcnjhtOL2aCVKkHMeuEwt9pPbOO4FxJtx/ec5cIUG/otRyJnfQGCAr9DplBVng=="],
1386
+ "wrangler": ["wrangler@4.66.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.13.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260217.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260217.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260217.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-b9RVIdKai0BXDuYg0iN0zwVnVbULkvdKGP7Bf1uFY2GhJ/nzDGqgwQbCwgDIOhmaBC8ynhk/p22M2jc8tJy+dQ=="],
1389
1387
 
1390
1388
  "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
1391
1389
 
@@ -1415,8 +1413,6 @@
1415
1413
 
1416
1414
  "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
1417
1415
 
1418
- "zod-to-ts": ["zod-to-ts@2.0.0", "", { "peerDependencies": { "typescript": "^5.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-aHsUgIl+CQutKAxtRNeZslLCLXoeuSq+j5HU7q3kvi/c2KIAo6q4YjT7/lwFfACxLB923ELHYMkHmlxiqFy4lw=="],
1419
-
1420
1416
  "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
1421
1417
 
1422
1418
  "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
@@ -8,9 +8,9 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@ai-sdk/react": "^3.0.88",
11
- "@cloudflare/ai-chat": "^0.0.8",
11
+ "@cloudflare/ai-chat": "^0.1.0",
12
12
  "@tailwindcss/typography": "^0.5.19",
13
- "agents": "^0.4",
13
+ "agents": "^0.5",
14
14
  "ai": "^6",
15
15
  "class-variance-authority": "^0.7.1",
16
16
  "clsx": "^2.1.1",
@@ -29,6 +29,8 @@ function getAIProvider(env: Env) {
29
29
  }
30
30
 
31
31
  export class Chat extends AIChatAgent<Env> {
32
+ maxPersistedMessages = 500;
33
+
32
34
  async onChatMessage(onFinish: StreamTextOnFinishCallback<ToolSet>) {
33
35
  const provider = getAIProvider(this.env);
34
36