@junejs/server 0.1.0 → 0.1.2-dev.0

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.
@@ -1,13 +1,14 @@
1
1
  import { Channel, ChannelFactory } from "@junejs/core/agent-config";
2
- import { Model, Msg, SessionStore, Tool } from "@junejs/core/agent-runtime";
2
+ import { InboundEvent, Model, Msg, SessionStore, Tool } from "@junejs/core/agent-runtime";
3
3
  import { Resources } from "@junejs/core/resources";
4
4
  //#region src/agent-durable.d.ts
5
- interface SqlStorageCursor<T = Record<string, unknown>> {
5
+ type SqlStorageValue = ArrayBuffer | string | number | null;
6
+ interface SqlStorageCursor<T extends Record<string, SqlStorageValue> = Record<string, SqlStorageValue>> {
6
7
  toArray(): T[];
7
8
  one(): T;
8
9
  }
9
10
  interface SqlStorage {
10
- exec<T = Record<string, unknown>>(query: string, ...bindings: unknown[]): SqlStorageCursor<T>;
11
+ exec<T extends Record<string, SqlStorageValue> = Record<string, SqlStorageValue>>(query: string, ...bindings: unknown[]): SqlStorageCursor<T>;
11
12
  }
12
13
  interface DurableStorage {
13
14
  sql: SqlStorage;
@@ -53,6 +54,7 @@ declare class AgentDurableObject {
53
54
  turn(input: {
54
55
  turnId?: string;
55
56
  userText: string;
57
+ event?: InboundEvent;
56
58
  }): Promise<string>;
57
59
  transcript(): import("@junejs/core/agent-runtime").Turn[];
58
60
  fetch(req: Request): Promise<Response>;
@@ -69,4 +71,4 @@ declare function durableChannelSurface(getNamespace: () => DurableObjectNamespac
69
71
  waitUntil?: (p: Promise<unknown>) => void;
70
72
  }): (req: Request) => Promise<Response | null>;
71
73
  //#endregion
72
- export { AgentDurableObject, DoAgentDef, DoSessionStore, DurableObjectNamespace, DurableObjectState, DurableObjectStub, DurableStorage, SqlStorage, SqlStorageCursor, durableAgentSurface, durableChannelSurface, durableFetch };
74
+ export { AgentDurableObject, DoAgentDef, DoSessionStore, DurableObjectNamespace, DurableObjectState, DurableObjectStub, DurableStorage, SqlStorage, SqlStorageCursor, SqlStorageValue, durableAgentSurface, durableChannelSurface, durableFetch };
@@ -85,10 +85,11 @@ var AgentDurableObject = class {
85
85
  async fetch(req) {
86
86
  const url = new URL(req.url);
87
87
  if (req.method === "POST" && url.pathname.endsWith("/turn")) {
88
- const { userText, turnId } = await req.json();
88
+ const { userText, turnId, event } = await req.json();
89
89
  return Response.json({ text: await this.turn({
90
90
  userText,
91
- turnId
91
+ turnId,
92
+ event
92
93
  }) });
93
94
  }
94
95
  if (url.pathname.endsWith("/transcript")) return Response.json({ transcript: this.transcript() });
@@ -128,14 +129,30 @@ function durableChannelSurface(getNamespace, opts) {
128
129
  if (!namespace) throw new Error("durableChannelSurface: no Durable Object namespace bound (env.AGENT)");
129
130
  const { text } = await (await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request("https://do/turn", {
130
131
  method: "POST",
131
- body: JSON.stringify({
132
- userText: message,
133
- turnId: o?.turnId
134
- })
132
+ body: serializeTurn(message, o)
135
133
  }))).json();
136
134
  return text;
137
135
  }
138
136
  });
139
137
  }
138
+ function serializeTurn(userText, o) {
139
+ const payload = {
140
+ userText,
141
+ turnId: o?.turnId,
142
+ event: o?.event
143
+ };
144
+ try {
145
+ return JSON.stringify(payload);
146
+ } catch {
147
+ const event = o?.event ? {
148
+ ...o.event,
149
+ raw: void 0
150
+ } : void 0;
151
+ return JSON.stringify({
152
+ ...payload,
153
+ event
154
+ });
155
+ }
156
+ }
140
157
  //#endregion
141
158
  export { AgentDurableObject, DoSessionStore, durableAgentSurface, durableChannelSurface, durableFetch };
@@ -154,7 +154,8 @@ function mountAgent(agent, runtime, opts = {}) {
154
154
  agent,
155
155
  run: (message, o) => runtime.session(agent.name, o?.session ?? "default").turn({
156
156
  turnId: o?.turnId,
157
- userText: message
157
+ userText: message,
158
+ event: o?.event
158
159
  })
159
160
  };
160
161
  const channels = channelFetch(agent, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junejs/server",
3
- "version": "0.1.0",
3
+ "version": "0.1.2-dev.0",
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",
@@ -125,8 +125,8 @@
125
125
  "build": "tsdown"
126
126
  },
127
127
  "dependencies": {
128
- "@junejs/core": ">=0.1.0",
129
- "@junejs/db": ">=0.0.33",
128
+ "@junejs/core": ">=0.1.1-dev.0",
129
+ "@junejs/db": ">=0.0.34-dev.0",
130
130
  "@momiji-rs/sparkdown": "^0.0.4",
131
131
  "lightningcss": "^1.27.0",
132
132
  "oxc-parser": "^0.137.0",
@@ -22,6 +22,7 @@ import {
22
22
  type Model,
23
23
  type Msg,
24
24
  type Runtime,
25
+ type InboundEvent,
25
26
  type SessionStore,
26
27
  type Tool,
27
28
  } from "@junejs/core/agent-runtime";
@@ -37,12 +38,23 @@ import {
37
38
  import { ensureScope, runInScope } from "@junejs/db";
38
39
 
39
40
  // ── minimal structural Cloudflare surface (no @cloudflare/workers-types dep) ──
40
- export interface SqlStorageCursor<T = Record<string, unknown>> {
41
+ // SqlStorage row values are exactly what workerd's SQLite returns. Mirroring
42
+ // @cloudflare/workers-types' `T extends Record<string, SqlStorageValue>` constraint
43
+ // STRUCTURALLY (not importing it) makes `this.ctx.storage` directly assignable to
44
+ // these interfaces, so a DO shell needs no `as unknown as JuneDoState` cast. The
45
+ // constraint (not just a default) is what lets the two `exec` signatures unify:
46
+ // an unconstrained `T` promises `toArray(): T[]` for arbitrary T, which workerd's
47
+ // cursor — only ever `Record<string, SqlStorageValue>` rows — cannot satisfy.
48
+ export type SqlStorageValue = ArrayBuffer | string | number | null;
49
+ export interface SqlStorageCursor<T extends Record<string, SqlStorageValue> = Record<string, SqlStorageValue>> {
41
50
  toArray(): T[];
42
51
  one(): T;
43
52
  }
44
53
  export interface SqlStorage {
45
- exec<T = Record<string, unknown>>(query: string, ...bindings: unknown[]): SqlStorageCursor<T>;
54
+ exec<T extends Record<string, SqlStorageValue> = Record<string, SqlStorageValue>>(
55
+ query: string,
56
+ ...bindings: unknown[]
57
+ ): SqlStorageCursor<T>;
46
58
  }
47
59
  export interface DurableStorage {
48
60
  sql: SqlStorage;
@@ -179,7 +191,7 @@ export class AgentDurableObject {
179
191
  // Juno's batch-loader registry) can't leak across turns on a long-lived DO.
180
192
  // ensureScope() lazily wires node:async_hooks (workerd via nodejs_compat), as the
181
193
  // pipeline does; without it runInScope is a pass-through and ambient reads throw.
182
- async turn(input: { turnId?: string; userText: string }): Promise<string> {
194
+ async turn(input: { turnId?: string; userText: string; event?: InboundEvent }): Promise<string> {
183
195
  await ensureScope();
184
196
  return runInScope({ resources: this.resources, services: this.services }, () => this.session.turn(input));
185
197
  }
@@ -191,8 +203,8 @@ export class AgentDurableObject {
191
203
  async fetch(req: Request): Promise<Response> {
192
204
  const url = new URL(req.url);
193
205
  if (req.method === "POST" && url.pathname.endsWith("/turn")) {
194
- const { userText, turnId } = (await req.json()) as { userText: string; turnId?: string };
195
- return Response.json({ text: await this.turn({ userText, turnId }) });
206
+ const { userText, turnId, event } = (await req.json()) as { userText: string; turnId?: string; event?: InboundEvent };
207
+ return Response.json({ text: await this.turn({ userText, turnId, event }) });
196
208
  }
197
209
  if (url.pathname.endsWith("/transcript")) return Response.json({ transcript: this.transcript() });
198
210
  return new Response("agent DO — POST /turn or GET /transcript", { status: 404 });
@@ -282,7 +294,7 @@ export function durableChannelSurface(
282
294
  namespace,
283
295
  opts.agentName,
284
296
  o?.session ?? "default",
285
- new Request("https://do/turn", { method: "POST", body: JSON.stringify({ userText: message, turnId: o?.turnId }) }),
297
+ new Request("https://do/turn", { method: "POST", body: serializeTurn(message, o) }),
286
298
  );
287
299
  const { text } = (await res.json()) as { text: string };
288
300
  return text;
@@ -290,3 +302,19 @@ export function durableChannelSurface(
290
302
  };
291
303
  return channelDispatch(resolved, ctx);
292
304
  }
305
+
306
+ // Serialize a turn for the /turn RPC body. InboundEvent.raw is `unknown` — the
307
+ // untouched platform payload — so a (third-party) channel could attach something
308
+ // JSON.stringify chokes on (a circular object, a BigInt). raw isn't needed to route or
309
+ // run the turn, so on a serialization failure we drop it rather than let an
310
+ // unserializable payload take down turn forwarding entirely. (raw is optional on
311
+ // InboundEvent precisely because it may not survive this boundary.)
312
+ function serializeTurn(userText: string, o?: { turnId?: string; event?: InboundEvent }): string {
313
+ const payload = { userText, turnId: o?.turnId, event: o?.event };
314
+ try {
315
+ return JSON.stringify(payload);
316
+ } catch {
317
+ const event = o?.event ? { ...o.event, raw: undefined } : undefined;
318
+ return JSON.stringify({ ...payload, event });
319
+ }
320
+ }
@@ -207,7 +207,7 @@ export function mountAgent(
207
207
  const ctx: ChannelContext = {
208
208
  agent,
209
209
  run: (message, o) =>
210
- runtime.session(agent.name, o?.session ?? "default").turn({ turnId: o?.turnId, userText: message }),
210
+ runtime.session(agent.name, o?.session ?? "default").turn({ turnId: o?.turnId, userText: message, event: o?.event }),
211
211
  };
212
212
  const channels = channelFetch(agent, ctx);
213
213
  const surface = async (req: Request): Promise<Response | null> => {