@junejs/server 1.0.0-dev.13 → 1.0.0-dev.17

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.
@@ -52,6 +52,22 @@ var DoSessionStore = class {
52
52
  tx(fn) {
53
53
  return this.storage.transactionSync(fn);
54
54
  }
55
+ reset() {
56
+ return this.storage.transactionSync(() => {
57
+ const sql = this.sql;
58
+ sql.exec(`CREATE TABLE IF NOT EXISTS agent_messages_archive (generation INTEGER, seq INTEGER, body TEXT)`);
59
+ sql.exec(`CREATE TABLE IF NOT EXISTS agent_steps_archive (generation INTEGER, id TEXT, output TEXT)`);
60
+ const rows = this.sql.exec("SELECT v FROM agent_meta WHERE k = 'generation'").toArray();
61
+ const generation = rows.length ? Number(rows[0].v) : 0;
62
+ sql.exec("INSERT INTO agent_messages_archive (generation, seq, body) SELECT ?, seq, body FROM agent_messages", generation);
63
+ sql.exec("DELETE FROM agent_messages");
64
+ sql.exec("INSERT INTO agent_steps_archive (generation, id, output) SELECT ?, id, output FROM agent_steps", generation);
65
+ sql.exec("DELETE FROM agent_steps");
66
+ this.setStatus("new");
67
+ sql.exec("INSERT INTO agent_meta (k, v) VALUES ('generation', ?) ON CONFLICT(k) DO UPDATE SET v = ?", String(generation + 1), String(generation + 1));
68
+ return generation;
69
+ });
70
+ }
55
71
  unwrap() {
56
72
  return this.sql;
57
73
  }
@@ -83,13 +99,19 @@ var AgentDurableObject = class {
83
99
  tools;
84
100
  channels;
85
101
  channelInstructions;
86
- resources;
102
+ resourcesInput;
103
+ resourcesOpened;
104
+ doEnv;
87
105
  services;
106
+ resolveResources() {
107
+ return this.resourcesOpened ??= Promise.resolve(typeof this.resourcesInput === "function" ? this.resourcesInput(this.doEnv) : this.resourcesInput ?? {});
108
+ }
88
109
  constructor(state, def) {
89
110
  assertCoreRuntimeVersion(`AgentDurableObject(${def.name ?? "agent"})`);
90
111
  const store = new DoSessionStore(state.storage);
91
112
  const model = def.instructions ? withSystem(def.model, def.instructions) : def.model;
92
- this.resources = def.resources ?? {};
113
+ this.resourcesInput = def.resources;
114
+ this.doEnv = def.env;
93
115
  this.services = def.services;
94
116
  const name = def.name ?? "agent";
95
117
  const sink = new InProcEventSink();
@@ -146,20 +168,25 @@ var AgentDurableObject = class {
146
168
  }
147
169
  async turn(input) {
148
170
  await ensureScope();
171
+ const resources = await this.resolveResources();
149
172
  const session = this.resolveSession(input.session);
150
173
  return runInScope({
151
- resources: this.resources,
174
+ resources,
152
175
  services: this.services
153
176
  }, () => session.turn(input));
154
177
  }
155
178
  async start(input) {
156
179
  await ensureScope();
180
+ const resources = await this.resolveResources();
157
181
  const session = this.resolveSession(input.session);
158
182
  return runInScope({
159
- resources: this.resources,
183
+ resources,
160
184
  services: this.services
161
185
  }, () => session.start(input));
162
186
  }
187
+ reset(opts) {
188
+ return this.resolveSession(opts?.session).reset();
189
+ }
163
190
  transcript() {
164
191
  if (this.session) return this.session.transcript();
165
192
  if (this.store.getSessionKey() !== void 0) return this.resolveSession().transcript();
@@ -171,6 +198,7 @@ var AgentDurableObject = class {
171
198
  if (req.method === "POST" && url.pathname.endsWith("/turn")) {
172
199
  const { userText, turnId, event, trigger } = await req.json();
173
200
  await ensureScope();
201
+ const replace = url.searchParams.get("replace") === "1";
174
202
  const wantsDeliver = url.searchParams.get("deliver") === "1";
175
203
  let deliverChannel;
176
204
  if (wantsDeliver) {
@@ -178,18 +206,20 @@ var AgentDurableObject = class {
178
206
  deliverChannel = this.channels.find((c) => c.name === event.source);
179
207
  if (!deliverChannel?.deliver) return Response.json({ error: `agent "${this.name}": no deliver()-capable channel named "${event.source}" is wired into this DO (DoAgentDef.channels) — the turn was NOT started` }, { status: 501 });
180
208
  }
209
+ const resources = await this.resolveResources();
181
210
  let session;
182
211
  let started;
183
212
  try {
184
213
  session = this.resolveSession(key);
185
214
  started = runInScope({
186
- resources: this.resources,
215
+ resources,
187
216
  services: this.services
188
217
  }, () => session.start({
189
218
  userText,
190
219
  turnId,
191
220
  event,
192
- trigger
221
+ trigger,
222
+ replace
193
223
  }));
194
224
  } catch (err) {
195
225
  return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 409 });
@@ -206,7 +236,7 @@ var AgentDurableObject = class {
206
236
  recipientTeamId: event.teamId
207
237
  };
208
238
  Promise.resolve().then(() => runInScope({
209
- resources: this.resources,
239
+ resources,
210
240
  services: this.services
211
241
  }, () => deliverChannel.deliver(target, events, { session: this.sessionKey }))).catch(logRenderFailure).finally(() => {
212
242
  it.return?.();
@@ -229,11 +259,12 @@ var AgentDurableObject = class {
229
259
  resumeChannel = this.channels.find((c) => c.name === source);
230
260
  if (!resumeChannel?.deliverResume) return Response.json({ error: `agent "${this.name}": no deliverResume()-capable channel named "${source}" is wired into this DO (DoAgentDef.channels) — the answer was NOT applied` }, { status: 501 });
231
261
  }
262
+ const resources = await this.resolveResources();
232
263
  let session;
233
264
  try {
234
265
  session = this.resolveSession(key);
235
266
  runInScope({
236
- resources: this.resources,
267
+ resources,
237
268
  services: this.services
238
269
  }, () => session.resume(turnId, inputId, input, { by }));
239
270
  } catch (err) {
@@ -246,7 +277,7 @@ var AgentDurableObject = class {
246
277
  const it = observeTurnEvents(session, turnId)[Symbol.asyncIterator]();
247
278
  const events = { [Symbol.asyncIterator]: () => it };
248
279
  Promise.resolve().then(() => runInScope({
249
- resources: this.resources,
280
+ resources,
250
281
  services: this.services
251
282
  }, () => resumeChannel.deliverResume(target, events, { session: this.sessionKey }))).catch(logRenderFailure).finally(() => {
252
283
  it.return?.();
@@ -258,6 +289,19 @@ var AgentDurableObject = class {
258
289
  }
259
290
  return new Response(sseTurnStream(session, turnId), { headers: SSE_HEADERS });
260
291
  }
292
+ if (req.method === "POST" && url.pathname.endsWith("/reset")) {
293
+ let session;
294
+ try {
295
+ session = this.resolveSession(key);
296
+ } catch (err) {
297
+ return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 409 });
298
+ }
299
+ try {
300
+ return Response.json(await session.reset());
301
+ } catch (err) {
302
+ return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 409 });
303
+ }
304
+ }
261
305
  if (url.pathname.endsWith("/transcript")) {
262
306
  if (key !== void 0) try {
263
307
  return Response.json({ transcript: this.resolveSession(key).transcript() });
@@ -266,7 +310,7 @@ var AgentDurableObject = class {
266
310
  }
267
311
  return Response.json({ transcript: this.transcript() });
268
312
  }
269
- return new Response("agent DO — POST /turn, POST /resume, or GET /transcript", { status: 404 });
313
+ return new Response("agent DO — POST /turn, POST /resume, POST /reset, or GET /transcript", { status: 404 });
270
314
  }
271
315
  };
272
316
  const SSE_HEADERS = {
@@ -292,7 +336,7 @@ function sseTurnStream(session, turnId) {
292
336
  }, 2e4);
293
337
  unsub = session.observe((e) => {
294
338
  controller.enqueue(enc.encode(`data: ${JSON.stringify(e)}\n\n`));
295
- if (e.type === "turn.completed" || e.type === "turn.failed" || e.type === "input.requested") {
339
+ if (e.type === "turn.completed" || e.type === "turn.failed" || e.type === "turn.cancelled" || e.type === "input.requested") {
296
340
  stop();
297
341
  controller.close();
298
342
  }
@@ -309,7 +353,7 @@ function observeTurnEvents(session, turnId) {
309
353
  let notify;
310
354
  const unsub = session.observe((e) => {
311
355
  queue.push(e);
312
- if (e.type === "turn.completed" || e.type === "turn.failed" || e.type === "input.requested") terminal = true;
356
+ if (e.type === "turn.completed" || e.type === "turn.failed" || e.type === "turn.cancelled" || e.type === "input.requested") terminal = true;
313
357
  notify?.();
314
358
  }, { turnId });
315
359
  return { [Symbol.asyncIterator]() {
@@ -366,6 +410,7 @@ async function sseTurnFinalText(res) {
366
410
  const e = JSON.parse(line.slice(5).trim());
367
411
  if (e.type === "turn.completed") return e.text;
368
412
  if (e.type === "turn.failed") throw new Error(e.error.message, { cause: e.error });
413
+ if (e.type === "turn.cancelled") throw new Error(`turn ${e.turnId} was cancelled (${e.reason})`);
369
414
  }
370
415
  if (done) break;
371
416
  }
@@ -451,7 +496,7 @@ function durableChannelSurface(getNamespace, opts) {
451
496
  run: async (message, o) => {
452
497
  const namespace = getNamespace();
453
498
  if (!namespace) throw new Error("durableChannelSurface: no Durable Object namespace bound (env.AGENT)");
454
- return sseTurnFinalText(await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request("https://do/turn", {
499
+ return sseTurnFinalText(await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request(`https://do/turn${o?.replace ? "?replace=1" : ""}`, {
455
500
  method: "POST",
456
501
  body: serializeTurn(message, o)
457
502
  })));
@@ -459,7 +504,7 @@ function durableChannelSurface(getNamespace, opts) {
459
504
  runDetached: async (message, o) => {
460
505
  const namespace = getNamespace();
461
506
  if (!namespace) throw new Error("durableChannelSurface: no Durable Object namespace bound (env.AGENT)");
462
- const res = await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request("https://do/turn?detach=1", {
507
+ const res = await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request(`https://do/turn?detach=1${o?.replace ? "&replace=1" : ""}`, {
463
508
  method: "POST",
464
509
  body: serializeTurn(message, o)
465
510
  }));
@@ -472,7 +517,7 @@ function durableChannelSurface(getNamespace, opts) {
472
517
  runDelivered: async (message, o) => {
473
518
  const namespace = getNamespace();
474
519
  if (!namespace) throw new Error("durableChannelSurface: no Durable Object namespace bound (env.AGENT)");
475
- const res = await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request("https://do/turn?deliver=1", {
520
+ const res = await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request(`https://do/turn?deliver=1${o?.replace ? "&replace=1" : ""}`, {
476
521
  method: "POST",
477
522
  body: serializeTurn(message, o)
478
523
  }));
@@ -486,7 +531,7 @@ function durableChannelSurface(getNamespace, opts) {
486
531
  runStream: async function* (message, o) {
487
532
  const namespace = getNamespace();
488
533
  if (!namespace) throw new Error("durableChannelSurface: no Durable Object namespace bound (env.AGENT)");
489
- yield* sseTurnEvents(await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request("https://do/turn", {
534
+ yield* sseTurnEvents(await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request(`https://do/turn${o?.replace ? "?replace=1" : ""}`, {
490
535
  method: "POST",
491
536
  body: serializeTurn(message, o)
492
537
  })));
@@ -512,6 +557,16 @@ function durableChannelSurface(getNamespace, opts) {
512
557
  throw new Error(`resumeDelivered: resume was not accepted (status ${res.status}): ${detail}`);
513
558
  }
514
559
  return await res.json();
560
+ },
561
+ resetSession: async (o) => {
562
+ const namespace = getNamespace();
563
+ if (!namespace) throw new Error("durableChannelSurface: no Durable Object namespace bound (env.AGENT)");
564
+ const res = await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request("https://do/reset", { method: "POST" }));
565
+ if (!res.ok) {
566
+ const detail = (await res.text()).slice(0, 200);
567
+ throw new Error(`resetSession: reset was not accepted (status ${res.status}): ${detail}`);
568
+ }
569
+ return await res.json();
515
570
  }
516
571
  });
517
572
  }
@@ -51,6 +51,21 @@ var SqliteSessionStore = class {
51
51
  throw e;
52
52
  }
53
53
  }
54
+ reset() {
55
+ return this.tx(() => {
56
+ this.db.exec(`CREATE TABLE IF NOT EXISTS agent_messages_archive (session_id TEXT, generation INTEGER, seq INTEGER, body TEXT)`);
57
+ this.db.exec(`CREATE TABLE IF NOT EXISTS agent_steps_archive (session_id TEXT, generation INTEGER, id TEXT, output TEXT)`);
58
+ this.db.exec(`CREATE TABLE IF NOT EXISTS agent_session_generations (session_id TEXT PRIMARY KEY, generation INTEGER)`);
59
+ const generation = this.db.query("SELECT generation FROM agent_session_generations WHERE session_id = ?").get(this.sid)?.generation ?? 0;
60
+ this.db.query("INSERT INTO agent_messages_archive (session_id, generation, seq, body) SELECT session_id, ?, seq, body FROM agent_messages WHERE session_id = ?").run(generation, this.sid);
61
+ this.db.query("DELETE FROM agent_messages WHERE session_id = ?").run(this.sid);
62
+ this.db.query("INSERT INTO agent_steps_archive (session_id, generation, id, output) SELECT session_id, ?, id, output FROM agent_steps WHERE session_id = ?").run(generation, this.sid);
63
+ this.db.query("DELETE FROM agent_steps WHERE session_id = ?").run(this.sid);
64
+ this.db.query("INSERT INTO agent_sessions (session_id, status) VALUES (?, 'new') ON CONFLICT(session_id) DO UPDATE SET status = 'new'").run(this.sid);
65
+ this.db.query("INSERT INTO agent_session_generations (session_id, generation) VALUES (?, ?) ON CONFLICT(session_id) DO UPDATE SET generation = ?").run(this.sid, generation + 1, generation + 1);
66
+ return generation;
67
+ });
68
+ }
54
69
  unwrap() {
55
70
  return this.db;
56
71
  }
@@ -99,6 +114,8 @@ var MemorySessionStore = class {
99
114
  msgs = [];
100
115
  steps = /* @__PURE__ */ new Map();
101
116
  status = "new";
117
+ generation = 0;
118
+ archives = [];
102
119
  appendMessage(m) {
103
120
  this.msgs.push(m);
104
121
  }
@@ -126,6 +143,18 @@ var MemorySessionStore = class {
126
143
  tx(fn) {
127
144
  return fn();
128
145
  }
146
+ reset() {
147
+ const generation = this.generation++;
148
+ this.archives.push({
149
+ generation,
150
+ msgs: this.msgs,
151
+ steps: this.steps
152
+ });
153
+ this.msgs = [];
154
+ this.steps = /* @__PURE__ */ new Map();
155
+ this.status = "new";
156
+ return generation;
157
+ }
129
158
  unwrap() {}
130
159
  };
131
160
  var MemoryRuntime = class {
@@ -166,14 +195,17 @@ function mountAgent(agent, runtime, opts = {}) {
166
195
  turnId: o?.turnId,
167
196
  userText: message,
168
197
  event: o?.event,
169
- trigger: o?.trigger
198
+ trigger: o?.trigger,
199
+ replace: o?.replace
170
200
  }),
171
201
  runDetached: async (message, o) => runtime.session(agent.name, o?.session ?? "default").start({
172
202
  turnId: o?.turnId,
173
203
  userText: message,
174
204
  event: o?.event,
175
- trigger: o?.trigger
176
- })
205
+ trigger: o?.trigger,
206
+ replace: o?.replace
207
+ }),
208
+ resetSession: (o) => runtime.session(agent.name, o?.session ?? "default").reset()
177
209
  };
178
210
  const channels = channelFetch(agent, ctx);
179
211
  const surface = async (req) => {
package/dist/build.js CHANGED
@@ -1,8 +1,9 @@
1
+ import { jsxTransform } from "./tsconfig-jsx.js";
1
2
  import { deno, staticSite, vercel, workers } from "./adapter.js";
3
+ import { generateAgentModule } from "./agent-compile.js";
2
4
  import { findMiddlewareFile } from "./router.js";
3
5
  import { resolveBoundary } from "./segment.js";
4
6
  import { buildModuleCss, registerCssModules, rolldownCssModulesPlugin } from "./css-modules.js";
5
- import { jsxTransform } from "./tsconfig-jsx.js";
6
7
  import { CLIENT_SCRIPT_URL, bundleClientToFile, findClientEntry } from "./client-bundle.js";
7
8
  import { generateIslandRegistry } from "./island-registry.js";
8
9
  import { STYLES_URL, cssTargets, findGlobalCss, globalCssUsesTailwind, minifyCss, processCss } from "./css.js";
@@ -262,6 +263,12 @@ async function buildManifest(appRoot) {
262
263
  const mod = await import(pathToFileURL(extraFile).href);
263
264
  if (typeof mod.default === "function") extra = mod.default;
264
265
  }
266
+ let agentName;
267
+ const agentDir = join(appDir, frozen.agent.runtime.dir);
268
+ if (frozen.agent.runtime.enabled && existsSync(agentDir)) {
269
+ const cfgFile = join(agentDir, "agent.ts");
270
+ agentName = (existsSync(cfgFile) ? (await import(pathToFileURL(cfgFile).href)).default : void 0)?.name ?? basename(agentDir);
271
+ }
265
272
  return {
266
273
  routes,
267
274
  dynamicRoutes,
@@ -271,6 +278,7 @@ async function buildManifest(appRoot) {
271
278
  loadings,
272
279
  document: frozen.document,
273
280
  agent: frozen.agent,
281
+ agentName,
274
282
  i18n: frozen.i18n,
275
283
  earlyHints: frozen.earlyHints,
276
284
  extra
@@ -317,6 +325,24 @@ async function juneBuild(appRoot, options = {}) {
317
325
  } : void 0 },
318
326
  config: fullConfig
319
327
  });
328
+ const agentDir = join(appDir, frozen.agent.runtime.dir);
329
+ let agentModule = null;
330
+ if (frozen.agent.runtime.enabled) {
331
+ const generated = generateAgentModule(agentDir);
332
+ if (generated && adapter.capabilities.durableObjects) agentModule = generated;
333
+ else if (generated) console.warn(`[june build] app/${frozen.agent.runtime.dir}/ found, but the ${adapter.name}() target has no Durable Objects — the durable agent is not mounted.`);
334
+ }
335
+ if (agentModule) {
336
+ const sdkResolvable = (fromDir) => {
337
+ for (let d = resolve(fromDir);;) {
338
+ if (existsSync(join(d, "node_modules", "@anthropic-ai", "sdk", "package.json"))) return true;
339
+ const parent = dirname(d);
340
+ if (parent === d) return false;
341
+ d = parent;
342
+ }
343
+ };
344
+ if (!sdkResolvable(appRoot)) throw new Error(`app/${frozen.agent.runtime.dir}/ mounts a durable agent whose model is Claude — add the SDK to the app so it bundles for workerd: bun add @anthropic-ai/sdk`);
345
+ }
320
346
  const cssOut = await processCss(appDir, { minify: true });
321
347
  let cssAsset = null;
322
348
  if (cssOut !== null) {
@@ -407,7 +433,8 @@ async function juneBuild(appRoot, options = {}) {
407
433
  imports.push(`import { turso } from "@junejs/server/db";`);
408
434
  imports.push(`import { memoizeResources } from "@junejs/server/resources";`);
409
435
  } else if (hasResources) imports.push(`import { bindWorkerResources } from "@junejs/server/resources";`);
410
- const resourcesField = tursoDb ? `\n resources: memoizeResources({ db: turso() }),` : hasResources ? `\n resources: bindWorkerResources(${JSON.stringify(resourceFlags)}),` : "";
436
+ const resourcesExpr = tursoDb ? `memoizeResources({ db: turso() })` : hasResources ? `bindWorkerResources(${JSON.stringify(resourceFlags)})` : null;
437
+ const resourcesField = resourcesExpr ? `\n resources: ${resourcesExpr},` : "";
411
438
  const dataLayerModule = fullConfig.dataLayer?.module;
412
439
  if (dataLayerModule) imports.push(`import { installDataLayer } from ${JSON.stringify(dataLayerModule)};`);
413
440
  const dataLayerBoot = dataLayerModule ? "\ninstallDataLayer();\n" : "";
@@ -417,6 +444,36 @@ async function juneBuild(appRoot, options = {}) {
417
444
  imports.push(`import { services as __appServices } from ${JSON.stringify(spec)};`);
418
445
  }
419
446
  const servicesField = servicesModule ? `\n services: __appServices,` : "";
447
+ if (agentModule) {
448
+ imports.push(`import __agentModule from ${JSON.stringify(importPath(genDir, agentModule.file))};`);
449
+ imports.push(`import { assembleDurable } from "@junejs/core/agent-config";`);
450
+ imports.push(`import { anthropic } from "@junejs/core/agent-models";`);
451
+ imports.push(`import { AgentDurableObject } from "@junejs/server/agent-durable";`);
452
+ imports.push(`import { DurableObject } from "cloudflare:workers";`);
453
+ imports.push(`import Anthropic from "@anthropic-ai/sdk";`);
454
+ }
455
+ const agentNameField = agentModule ? `\n agentName: __agentModule.config.name,` : "";
456
+ const doClass = agentModule ? `
457
+ // The durable agent: one Durable Object per session, the compiled agent/
458
+ // directory as its definition — adapted tools (+ read_skill when skills exist),
459
+ // the assembled system prompt, channel factories resolved with the DO's own
460
+ // env, and the app's declared resources opened from THIS isolate's env (lazily,
461
+ // at the first turn — the same ambient db a tool sees in native dev). Model:
462
+ // Anthropic over a statically bundled client (workerd cannot resolve a bare
463
+ // import at runtime), keyed by the ANTHROPIC_API_KEY secret — a missing secret
464
+ // surfaces as the SDK's own clear construction error on the first agent request.
465
+ const __agentDef = assembleDurable(__agentModule);
466
+ export class JuneAgentDO extends DurableObject {
467
+ #agent = new AgentDurableObject(this.ctx, {
468
+ ...__agentDef,
469
+ model: anthropic({ model: __agentModule.config.model, client: new Anthropic({ apiKey: (this.env as { ANTHROPIC_API_KEY?: string }).ANTHROPIC_API_KEY }) }),
470
+ env: this.env,${resourcesExpr ? `\n resources: ${resourcesExpr},` : ""}${servicesModule ? "\n services: __appServices(this.env)," : ""}
471
+ });
472
+ fetch(req: Request): Promise<Response> {
473
+ return this.#agent.fetch(req);
474
+ }
475
+ }
476
+ ` : "";
420
477
  const entry = `// AUTO-GENERATED by \`june build\` — do not edit. Regenerate: june build .
421
478
  ${adapterEntry.imports.join("\n")}
422
479
  ${imports.join("\n")}
@@ -435,12 +492,12 @@ ${chains.join("\n")}
435
492
  ${loadings.join("\n")}
436
493
  },
437
494
  document: ${JSON.stringify(frozen.document, null, 2).replace(/\n/g, "\n ")},
438
- agent: ${JSON.stringify(frozen.agent)},${frozen.i18n ? `\n i18n: ${JSON.stringify(frozen.i18n)},` : ""}
495
+ agent: ${JSON.stringify(frozen.agent)},${agentNameField}${frozen.i18n ? `\n i18n: ${JSON.stringify(frozen.i18n)},` : ""}
439
496
  earlyHints: ${JSON.stringify(frozen.earlyHints)},${builtExtraFile ? "\n extra," : ""}${resourcesField}${servicesField}
440
497
  });
441
498
 
442
499
  ${adapterEntry.wrap("pipeline")}
443
- `;
500
+ ${doClass}`;
444
501
  const entryFile = join(genDir, "worker-entry.tsx");
445
502
  await writeFile(entryFile, entry);
446
503
  const { rolldown } = await import("rolldown");
@@ -455,6 +512,7 @@ ${adapterEntry.wrap("pipeline")}
455
512
  plugins: [rolldownCssModulesPlugin(cssModuleMaps)],
456
513
  external: (id) => {
457
514
  if (isBunSpecifier(id)) return true;
515
+ if (id.startsWith("cloudflare:")) return true;
458
516
  if (/\.(wasm|ttf|otf|woff2?|png|jpe?g|avif|webp)$/.test(id)) return true;
459
517
  return [...adapter.buildExternal ?? [], ...options.external ?? frozen.buildExternal].some((e) => id === e || id.startsWith(`${e}/`));
460
518
  },
@@ -561,10 +619,16 @@ ${adapterEntry.wrap("pipeline")}
561
619
  }
562
620
  const pkgPath = join(appRoot, "package.json");
563
621
  const defaultName = workerName((existsSync(pkgPath) ? JSON.parse(await Bun.file(pkgPath).text()).name : void 0) ?? basename(appRoot));
564
- const plan = resourcesCfg?.db && resourcesCfg.db.kind !== "turso" ? { db: {
565
- binding: "DB",
566
- databaseName: `${defaultName}-db`
567
- } } : {};
622
+ const plan = {
623
+ ...resourcesCfg?.db && resourcesCfg.db.kind !== "turso" ? { db: {
624
+ binding: "DB",
625
+ databaseName: `${defaultName}-db`
626
+ } } : {},
627
+ ...agentModule ? { agent: {
628
+ binding: "AGENT",
629
+ className: "JuneAgentDO"
630
+ } } : {}
631
+ };
568
632
  await adapter.emit({
569
633
  appRoot,
570
634
  outDir,
@@ -576,6 +640,7 @@ ${adapterEntry.wrap("pipeline")}
576
640
  publicFiles
577
641
  });
578
642
  if (plan.db) console.log(` ↳ d1 binding "${plan.db.binding}" emitted — set database_id in wrangler.jsonc (wrangler d1 create ${plan.db.databaseName})`);
643
+ if (plan.agent) console.log(` ↳ durable agent mounted (${plan.agent.className} → env.${plan.agent.binding}; chat at ${frozen.agent.runtime.chat.path})`);
579
644
  return {
580
645
  outFile,
581
646
  routes: routes.filter((r) => !r.dynamic).map((r) => r.path),
@@ -1,5 +1,5 @@
1
- import { rolldownCssModulesPlugin } from "./css-modules.js";
2
1
  import { jsxTransform } from "./tsconfig-jsx.js";
2
+ import { rolldownCssModulesPlugin } from "./css-modules.js";
3
3
  import { existsSync } from "node:fs";
4
4
  import { mkdir, writeFile } from "node:fs/promises";
5
5
  import { dirname, join } from "node:path";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { AdapterCapabilities, AdapterEmitContext, AdapterEntry, JuneAdapter, ResourcePlan, deno, staticSite, vercel, workers } from "./adapter.js";
2
+ import { AGENT_MODULE_FILE, AgentDirScan, emitAgentModule, findAgentDir, generateAgentModule, scanAgentDir } from "./agent-compile.js";
2
3
  import { CreateAppOptions, JuneApp, createApp } from "./app.js";
3
4
  import { R2Bucket, localBlob, r2 } from "./blob.js";
4
5
  import { a as Pipeline, d as createPipeline, n as LayoutComponent, o as PipelineConfig, s as Resolved, u as RouteResolver } from "./pipeline-cCR6MGNK.js";
@@ -24,4 +25,4 @@ import { Negotiated, negotiate } from "./negotiate.js";
24
25
  import { blob, currentServices, db, kv } from "@junejs/db";
25
26
  import { ServicesConfig, defineServices } from "@junejs/core/config";
26
27
  import { I18nConfig, LOCALE_COOKIE, LocaleConfig, LocaleMatch, dirForLang, localeDir, localeHref, matchPinnedLocale, negotiateLocale, parseAcceptLanguage, resolveRequestLocale } from "@junejs/core/i18n";
27
- export { type AdapterCapabilities, type AdapterEmitContext, type AdapterEntry, type BuildResult, type ContentEntry, type CreateAppOptions, type D1Database, type D1Exec, type DeployResult, type DevServer, type DevServerOptions, type I18nConfig, type JuneAdapter, type JuneApp, type JuneDb, type JuneHost, LOCALE_COOKIE, type LayoutComponent, type LocaleConfig, type LocaleMatch, type MatchOptions, type MigrateResult, type Migration, type Negotiated, type Pipeline, type PipelineConfig, type R2Bucket, type Resolved, type ResourceFlags, type ResourcePlan, type RouteMatch, type RouteResolver, type RouteTreeMatch, type RunResult, type SegmentMatch, type ServeHandle, type ServicesConfig, type SpawnedModule, type WorkerEnv, type WorkerManifest, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, currentServices, d1, db, defineServices, deno, dirForLang, entry, freezeConfig, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
28
+ export { AGENT_MODULE_FILE, type AdapterCapabilities, type AdapterEmitContext, type AdapterEntry, type AgentDirScan, type BuildResult, type ContentEntry, type CreateAppOptions, type D1Database, type D1Exec, type DeployResult, type DevServer, type DevServerOptions, type I18nConfig, type JuneAdapter, type JuneApp, type JuneDb, type JuneHost, LOCALE_COOKIE, type LayoutComponent, type LocaleConfig, type LocaleMatch, type MatchOptions, type MigrateResult, type Migration, type Negotiated, type Pipeline, type PipelineConfig, type R2Bucket, type Resolved, type ResourceFlags, type ResourcePlan, type RouteMatch, type RouteResolver, type RouteTreeMatch, type RunResult, type SegmentMatch, type ServeHandle, type ServicesConfig, type SpawnedModule, type WorkerEnv, type WorkerManifest, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, currentServices, d1, db, defineServices, deno, dirForLang, emitAgentModule, entry, findAgentDir, freezeConfig, generateAgentModule, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanAgentDir, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { deno, staticSite, vercel, workers } from "./adapter.js";
2
+ import { AGENT_MODULE_FILE, emitAgentModule, findAgentDir, generateAgentModule, scanAgentDir } from "./agent-compile.js";
2
3
  import { listRoutes, matchRoute, matchRouteTree, resolveNotFound } from "./router.js";
3
4
  import { negotiate } from "./negotiate.js";
4
5
  import { createPipeline } from "./pipeline.js";
@@ -24,4 +25,4 @@ import { memoryKv, redisKv } from "./kv.js";
24
25
  import { blob, currentServices, db, kv } from "@junejs/db";
25
26
  import { defineServices } from "@junejs/core/config";
26
27
  import { LOCALE_COOKIE, dirForLang, localeDir, localeHref, matchPinnedLocale, negotiateLocale, parseAcceptLanguage, resolveRequestLocale } from "@junejs/core/i18n";
27
- export { LOCALE_COOKIE, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, currentServices, d1, db, defineServices, deno, dirForLang, entry, freezeConfig, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
28
+ export { AGENT_MODULE_FILE, LOCALE_COOKIE, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, currentServices, d1, db, defineServices, deno, dirForLang, emitAgentModule, entry, findAgentDir, freezeConfig, generateAgentModule, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanAgentDir, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
@@ -1,4 +1,5 @@
1
1
  //#region src/tsconfig-jsx.d.ts
2
+ declare const stripJsonc: (s: string) => string;
2
3
  /** Read the app's tsconfig.json and return compilerOptions.jsxImportSource.
3
4
  * Follows one level of `extends` so apps that inherit from a base tsconfig
4
5
  * (e.g. "@kurajs/docs/tsconfig.kura.json") are handled correctly.
@@ -12,4 +13,4 @@ declare function jsxTransform(appRoot: string): Promise<{
12
13
  importSource?: string;
13
14
  }>;
14
15
  //#endregion
15
- export { appJsxImportSource, jsxTransform };
16
+ export { appJsxImportSource, jsxTransform, stripJsonc };
@@ -32,4 +32,4 @@ async function jsxTransform(appRoot) {
32
32
  };
33
33
  }
34
34
  //#endregion
35
- export { appJsxImportSource, jsxTransform };
35
+ export { appJsxImportSource, jsxTransform, stripJsonc };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junejs/server",
3
- "version": "1.0.0-dev.13",
3
+ "version": "1.0.0-dev.17",
4
4
  "description": "June host adapters + dev server. Host-coupled; depends on the pure @junejs/core contract layer.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -36,6 +36,12 @@
36
36
  "types": "./dist/agent-discover.d.ts",
37
37
  "default": "./dist/agent-discover.js"
38
38
  },
39
+ "./agent-compile": {
40
+ "source": "./src/agent-compile.ts",
41
+ "bun": "./src/agent-compile.ts",
42
+ "types": "./dist/agent-compile.d.ts",
43
+ "default": "./dist/agent-compile.js"
44
+ },
39
45
  "./db": {
40
46
  "source": "./src/db.ts",
41
47
  "bun": "./src/db.ts",
@@ -132,13 +138,14 @@
132
138
  "rolldown": "^1.1.0"
133
139
  },
134
140
  "peerDependencies": {
135
- "@junejs/core": ">=0.2.0-dev.30",
141
+ "@junejs/core": ">=0.2.0-dev.33",
136
142
  "react": "^19.2.0",
137
143
  "react-dom": "^19.2.0",
138
144
  "react-server-dom-webpack": "19.2.7",
139
145
  "pg": "^8.0.0",
140
146
  "mysql2": "^3.0.0",
141
- "@libsql/client": "^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0"
147
+ "@libsql/client": "^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0",
148
+ "@anthropic-ai/sdk": ">=0.60.0"
142
149
  },
143
150
  "peerDependenciesMeta": {
144
151
  "react-server-dom-webpack": {
@@ -152,6 +159,9 @@
152
159
  },
153
160
  "@libsql/client": {
154
161
  "optional": true
162
+ },
163
+ "@anthropic-ai/sdk": {
164
+ "optional": true
155
165
  }
156
166
  },
157
167
  "repository": {