@junejs/server 0.1.2 → 0.2.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.
Files changed (130) hide show
  1. package/assets/icon-latin.LICENSE.txt +92 -0
  2. package/assets/icon-latin.ttf +0 -0
  3. package/dist/adapter-DcZ-W1-7.d.ts +62 -0
  4. package/dist/adapter.d.ts +2 -56
  5. package/dist/adapter.js +62 -5
  6. package/dist/agent-compile.d.ts +38 -0
  7. package/dist/agent-compile.js +145 -0
  8. package/dist/agent-discover.d.ts +4 -4
  9. package/dist/agent-discover.js +30 -44
  10. package/dist/agent-durable.d.ts +94 -19
  11. package/dist/agent-durable.js +647 -32
  12. package/dist/agent-native.d.ts +44 -13
  13. package/dist/agent-native.js +264 -27
  14. package/dist/app.d.ts +5 -5
  15. package/dist/app.js +42 -10
  16. package/dist/blob.d.ts +4 -5
  17. package/dist/build.d.ts +8 -29
  18. package/dist/build.js +138 -289
  19. package/dist/client-bundle.d.ts +8 -9
  20. package/dist/client-bundle.js +1 -1
  21. package/dist/config-freeze-CyXvNqi2.d.ts +17 -0
  22. package/dist/config-freeze.d.ts +2 -0
  23. package/dist/config-freeze.js +54 -0
  24. package/dist/config-loader.d.ts +3 -4
  25. package/dist/connection-auth.d.ts +38 -0
  26. package/dist/connection-auth.js +31 -0
  27. package/dist/content-freeze-CyiW2fRK.d.ts +4 -0
  28. package/dist/content-freeze.d.ts +2 -0
  29. package/dist/content-freeze.js +83 -0
  30. package/dist/content.d.ts +22 -9
  31. package/dist/content.js +43 -3
  32. package/dist/core-version.d.ts +4 -0
  33. package/dist/core-version.js +12 -0
  34. package/dist/css-modules-ambient.d.ts +2 -1
  35. package/dist/css-modules.d.ts +10 -11
  36. package/dist/css.d.ts +9 -10
  37. package/dist/d1-migrate.d.ts +6 -7
  38. package/dist/d1-migrate.js +3 -2
  39. package/dist/db.d.ts +2 -2
  40. package/dist/deploy.d.ts +3 -4
  41. package/dist/deploy.js +3 -2
  42. package/dist/dev-reload.d.ts +3 -4
  43. package/dist/dev.d.ts +8 -5
  44. package/dist/dev.js +40 -9
  45. package/dist/favicon.d.ts +37 -0
  46. package/dist/favicon.js +248 -0
  47. package/dist/host.d.ts +6 -5
  48. package/dist/host.js +2 -1
  49. package/dist/icon-letter.d.ts +3 -0
  50. package/dist/icon-letter.js +8 -0
  51. package/dist/index.d.ts +11 -5
  52. package/dist/index.js +9 -3
  53. package/dist/instrumentation.d.ts +2 -3
  54. package/dist/instrumentation.js +2 -1
  55. package/dist/island-registry.d.ts +6 -7
  56. package/dist/isolate-local.d.ts +5 -0
  57. package/dist/isolate-local.js +12 -0
  58. package/dist/kv.d.ts +3 -4
  59. package/dist/llms-links.d.ts +9 -0
  60. package/dist/llms-links.js +65 -0
  61. package/dist/manifest-CwTT1zVs.d.ts +11 -0
  62. package/dist/manifest.d.ts +2 -0
  63. package/dist/manifest.js +113 -0
  64. package/dist/negotiate.d.ts +4 -3
  65. package/dist/negotiate.js +24 -14
  66. package/dist/{pipeline-CCp9idQ4.d.ts → pipeline-BHUjBO0G.d.ts} +13 -1
  67. package/dist/pipeline.d.ts +2 -2
  68. package/dist/pipeline.js +286 -35
  69. package/dist/resources.d.ts +5 -6
  70. package/dist/route-rank.d.ts +10 -0
  71. package/dist/route-rank.js +38 -0
  72. package/dist/route-scan-BMbBzPIB.d.ts +20 -0
  73. package/dist/route-scan.d.ts +2 -0
  74. package/dist/route-scan.js +100 -0
  75. package/dist/router.d.ts +12 -13
  76. package/dist/router.js +58 -44
  77. package/dist/rsc-build.d.ts +6 -7
  78. package/dist/rsc-bundle.d.ts +6 -7
  79. package/dist/rsc-manifest.d.ts +5 -6
  80. package/dist/rsc-runtime/flight-render-routes.d.ts +3 -4
  81. package/dist/rsc-runtime/flight-render.d.ts +2 -3
  82. package/dist/rsc-runtime/flight-to-html.d.ts +2 -3
  83. package/dist/rsc-runtime/flight-to-html.js +3 -2
  84. package/dist/rsc-runtime/rsc-worker.js +11 -8
  85. package/dist/rsc-runtime/rsdw-shims.d.ts +2 -1
  86. package/dist/segment.d.ts +4 -5
  87. package/dist/sqlite-driver.d.ts +11 -11
  88. package/dist/sqlite-driver.js +17 -4
  89. package/dist/static-files.d.ts +4 -5
  90. package/dist/tsconfig-jsx.d.ts +4 -4
  91. package/dist/tsconfig-jsx.js +1 -1
  92. package/dist/turn-events.d.ts +4 -0
  93. package/dist/turn-events.js +46 -0
  94. package/dist/web-manifest.d.ts +5 -0
  95. package/dist/web-manifest.js +42 -0
  96. package/dist/worker-2qbQjj2c.d.ts +64 -0
  97. package/dist/worker.d.ts +2 -56
  98. package/dist/worker.js +121 -103
  99. package/package.json +22 -9
  100. package/src/adapter.ts +84 -3
  101. package/src/agent-compile.ts +271 -0
  102. package/src/agent-discover.ts +77 -63
  103. package/src/agent-durable.ts +998 -30
  104. package/src/agent-native.ts +370 -35
  105. package/src/app.ts +75 -6
  106. package/src/build.ts +237 -444
  107. package/src/config-freeze.ts +109 -0
  108. package/src/connection-auth.ts +101 -0
  109. package/src/content-freeze.ts +151 -0
  110. package/src/content.ts +55 -4
  111. package/src/core-version.ts +34 -0
  112. package/src/dev.ts +41 -8
  113. package/src/favicon.ts +325 -0
  114. package/src/host.ts +4 -3
  115. package/src/icon-letter.ts +14 -0
  116. package/src/index.ts +22 -0
  117. package/src/isolate-local.ts +33 -0
  118. package/src/llms-links.ts +96 -0
  119. package/src/manifest.ts +130 -0
  120. package/src/negotiate.ts +39 -15
  121. package/src/pipeline.ts +494 -44
  122. package/src/route-rank.ts +60 -0
  123. package/src/route-scan.ts +161 -0
  124. package/src/router.ts +75 -58
  125. package/src/rsc-runtime/rsc-worker.tsx +5 -4
  126. package/src/sqlite-driver.ts +36 -6
  127. package/src/tsconfig-jsx.ts +2 -2
  128. package/src/turn-events.ts +43 -0
  129. package/src/web-manifest.ts +38 -0
  130. package/src/worker.ts +197 -117
@@ -1,40 +1,71 @@
1
1
  import { SyncSqlite } from "./sqlite-driver.js";
2
2
  import { AgentDefinition, ChannelContext } from "@junejs/core/agent-config";
3
- import { AgentSession, Model, Runtime, Tool } from "@junejs/core/agent-runtime";
3
+ import { AgentSession, ChannelPolicy, InputAnnouncement, Model, Runtime, Tool } from "@junejs/core/agent-runtime";
4
+ import { FileStore } from "@junejs/core/agent-files";
4
5
  //#region src/agent-native.d.ts
5
- type AgentDef = {
6
+ export type AgentDef = {
6
7
  model: Model;
7
8
  tools: Tool[];
8
9
  instructions?: string;
10
+ channelInstructions?: Record<string, string | ChannelPolicy>;
11
+ onInputAnnouncement?: (a: InputAnnouncement) => void | Promise<void>;
9
12
  };
10
- declare class NativeRuntime implements Runtime {
13
+ export declare function toAgentDef(agent: AgentDefinition, model: Model): AgentDef;
14
+ export type NativeRuntimeOptions = {
15
+ maxSessions?: number;
16
+ files?: FileStore;
17
+ };
18
+ export declare class NativeRuntime implements Runtime {
11
19
  private actors;
12
20
  private readonly agents;
13
21
  private readonly db;
14
- constructor(agents: Record<string, AgentDef>, db: SyncSqlite);
22
+ private readonly announceRetry;
23
+ private readonly maxSessions;
24
+ private closing;
25
+ readonly files: FileStore | undefined;
26
+ constructor(agents: Record<string, AgentDef>, db: SyncSqlite, opts?: NativeRuntimeOptions);
27
+ agentDef(name: string): AgentDef | undefined;
15
28
  session(agent: string, id: string): AgentSession;
29
+ get sessionCount(): number;
30
+ recoverAnnouncements(): void;
31
+ close(): Promise<void>;
32
+ private evictIdle;
16
33
  }
17
- declare function createNativeRuntime(agents: Record<string, AgentDef>, path?: string): Promise<NativeRuntime>;
18
- declare class MemoryRuntime implements Runtime {
34
+ export declare function createNativeRuntime(agents: Record<string, AgentDef>, path?: string, opts?: NativeRuntimeOptions): Promise<NativeRuntime>;
35
+ export declare class MemoryRuntime implements Runtime {
19
36
  private actors;
20
37
  private stores;
21
38
  private readonly agents;
22
- constructor(agents: Record<string, AgentDef>);
39
+ private readonly announceRetry;
40
+ private closed;
41
+ readonly files: FileStore | undefined;
42
+ constructor(agents: Record<string, AgentDef>, opts?: {
43
+ files?: FileStore;
44
+ });
45
+ agentDef(name: string): AgentDef | undefined;
23
46
  session(agent: string, id: string): AgentSession;
47
+ close(): Promise<void>;
24
48
  }
25
- type AgentBackend = "native" | "memory" | "durable";
26
- declare function createAgentRuntime(agents: Record<string, AgentDef>, opts?: {
49
+ export type AgentBackend = "native" | "memory" | "durable";
50
+ export type InProcessRuntime = Runtime & {
51
+ close(): Promise<void>;
52
+ readonly files?: FileStore;
53
+ };
54
+ export declare function createAgentRuntime(agents: Record<string, AgentDef>, opts?: {
27
55
  backend?: AgentBackend;
28
56
  path?: string;
29
- }): Promise<Runtime>;
30
- declare function mountAgent(agent: AgentDefinition, runtime: Runtime, opts?: {
57
+ maxSessions?: number;
58
+ files?: FileStore;
59
+ }): Promise<InProcessRuntime>;
60
+ export declare function mountAgent(agent: AgentDefinition, runtime: Runtime, opts?: {
31
61
  chatPath?: string;
32
62
  channels?: boolean;
63
+ services?: unknown;
64
+ files?: FileStore;
33
65
  }): {
34
66
  surface: (req: Request) => Promise<Response | null>;
35
67
  fetch: (req: Request) => Promise<Response | null>;
36
68
  startAll: () => Promise<void>;
37
69
  ctx: ChannelContext;
38
70
  };
39
- //#endregion
40
- export { AgentBackend, AgentDef, MemoryRuntime, NativeRuntime, createAgentRuntime, createNativeRuntime, mountAgent };
71
+ //#endregion
@@ -1,10 +1,13 @@
1
+ import { observeTurnEvents } from "./turn-events.js";
2
+ import { assertCoreRuntimeVersion } from "./core-version.js";
1
3
  import { openLocalSqliteSync } from "./sqlite-driver.js";
2
- import { channelFetch } from "@junejs/core/agent-config";
3
- import { AgentSession, withSystem } from "@junejs/core/agent-runtime";
4
+ import { buildSystemPrompt, channelFetch } from "@junejs/core/agent-config";
5
+ import { AgentSession, grantAnswer, withSystem } from "@junejs/core/agent-runtime";
4
6
  //#region src/agent-native.ts
5
7
  function initSchema(db) {
6
8
  db.exec(`CREATE TABLE IF NOT EXISTS agent_sessions (session_id TEXT PRIMARY KEY, status TEXT)`);
7
9
  db.exec(`CREATE TABLE IF NOT EXISTS agent_messages (seq INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, body TEXT)`);
10
+ db.exec(`CREATE INDEX IF NOT EXISTS agent_messages_session ON agent_messages (session_id, seq)`);
8
11
  db.exec(`CREATE TABLE IF NOT EXISTS agent_steps (session_id TEXT, id TEXT, output TEXT, PRIMARY KEY (session_id, id))`);
9
12
  }
10
13
  var SqliteSessionStore = class {
@@ -20,8 +23,8 @@ var SqliteSessionStore = class {
20
23
  messages() {
21
24
  return this.db.query("SELECT body FROM agent_messages WHERE session_id = ? ORDER BY seq").all(this.sid).map((r) => JSON.parse(r.body));
22
25
  }
23
- hasUserTurn(turnId) {
24
- return this.messages().some((m) => m.role === "user" && m.turnId === turnId);
26
+ hasOpeningMessage(turnId) {
27
+ return this.db.query("SELECT 1 FROM agent_messages WHERE session_id = ? AND json_extract(body, '$.turnId') = ? AND json_extract(body, '$.role') IN ('user', 'trigger') LIMIT 1").get(this.sid, turnId) != null;
25
28
  }
26
29
  getStep(id) {
27
30
  const r = this.db.query("SELECT output FROM agent_steps WHERE session_id = ? AND id = ?").get(this.sid, id);
@@ -30,6 +33,9 @@ var SqliteSessionStore = class {
30
33
  putStep(id, output) {
31
34
  this.db.query("INSERT INTO agent_steps (session_id, id, output) VALUES (?, ?, ?)").run(this.sid, id, JSON.stringify(output));
32
35
  }
36
+ delStep(id) {
37
+ this.db.query("DELETE FROM agent_steps WHERE session_id = ? AND id = ?").run(this.sid, id);
38
+ }
33
39
  getStatus() {
34
40
  return this.db.query("SELECT status FROM agent_sessions WHERE session_id = ?").get(this.sid)?.status ?? "new";
35
41
  }
@@ -47,16 +53,32 @@ var SqliteSessionStore = class {
47
53
  throw e;
48
54
  }
49
55
  }
56
+ reset(inTx) {
57
+ return this.tx(() => {
58
+ this.db.exec(`CREATE TABLE IF NOT EXISTS agent_messages_archive (session_id TEXT, generation INTEGER, seq INTEGER, body TEXT)`);
59
+ this.db.exec(`CREATE TABLE IF NOT EXISTS agent_steps_archive (session_id TEXT, generation INTEGER, id TEXT, output TEXT)`);
60
+ this.db.exec(`CREATE TABLE IF NOT EXISTS agent_session_generations (session_id TEXT PRIMARY KEY, generation INTEGER)`);
61
+ const generation = this.db.query("SELECT generation FROM agent_session_generations WHERE session_id = ?").get(this.sid)?.generation ?? 0;
62
+ 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);
63
+ this.db.query("DELETE FROM agent_messages WHERE session_id = ?").run(this.sid);
64
+ 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);
65
+ this.db.query("DELETE FROM agent_steps WHERE session_id = ?").run(this.sid);
66
+ this.db.query("INSERT INTO agent_sessions (session_id, status) VALUES (?, 'new') ON CONFLICT(session_id) DO UPDATE SET status = 'new'").run(this.sid);
67
+ 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);
68
+ inTx?.();
69
+ return generation;
70
+ });
71
+ }
50
72
  unwrap() {
51
73
  return this.db;
52
74
  }
53
75
  };
54
- var InProcBroadcaster = class {
76
+ var InProcEventSink = class {
55
77
  subs = /* @__PURE__ */ new Set();
56
- publish(turnId) {
78
+ emit(e) {
57
79
  this.subs.forEach((cb) => {
58
80
  try {
59
- cb(turnId);
81
+ cb(e);
60
82
  } catch {}
61
83
  });
62
84
  }
@@ -64,44 +86,176 @@ var InProcBroadcaster = class {
64
86
  this.subs.add(cb);
65
87
  return () => this.subs.delete(cb);
66
88
  }
89
+ get size() {
90
+ return this.subs.size;
91
+ }
67
92
  };
93
+ var AnnouncementRetry = class {
94
+ flush;
95
+ state = /* @__PURE__ */ new Map();
96
+ stopped = false;
97
+ constructor(flush) {
98
+ this.flush = flush;
99
+ }
100
+ delivered(key) {
101
+ const s = this.state.get(key);
102
+ if (s?.timer) clearTimeout(s.timer);
103
+ this.state.delete(key);
104
+ }
105
+ failed(key, agent, id) {
106
+ if (this.stopped) return;
107
+ const s = this.state.get(key) ?? { failures: 0 };
108
+ this.state.set(key, s);
109
+ if (s.timer) return;
110
+ const delay = Math.min(5e3 * 2 ** s.failures, 3e5);
111
+ s.failures++;
112
+ s.timer = setTimeout(() => {
113
+ s.timer = void 0;
114
+ this.flush(agent, id);
115
+ }, delay);
116
+ s.timer.unref?.();
117
+ }
118
+ stop() {
119
+ this.stopped = true;
120
+ for (const s of this.state.values()) if (s.timer) clearTimeout(s.timer);
121
+ this.state.clear();
122
+ }
123
+ };
124
+ const ownedDbs = /* @__PURE__ */ new WeakSet();
125
+ function wireAnnouncements(session, def, retry) {
126
+ const hook = def.onInputAnnouncement;
127
+ if (hook) {
128
+ const key = `${session.agent}:${session.id}`;
129
+ session.onAnnounce = async (a) => {
130
+ try {
131
+ await hook(a);
132
+ } catch (err) {
133
+ retry.failed(key, session.agent, session.id);
134
+ throw err;
135
+ }
136
+ retry.delivered(key);
137
+ };
138
+ session.flushAnnouncements();
139
+ }
140
+ return session;
141
+ }
142
+ const ANNOUNCE_OUTBOX_STEP = "announce-outbox";
143
+ function toAgentDef(agent, model) {
144
+ return {
145
+ model,
146
+ tools: agent.tools,
147
+ instructions: buildSystemPrompt(agent),
148
+ ...agent.channelInstructions ? { channelInstructions: agent.channelInstructions } : {},
149
+ ...agent.onInputAnnouncement ? { onInputAnnouncement: agent.onInputAnnouncement } : {}
150
+ };
151
+ }
68
152
  var NativeRuntime = class {
69
153
  actors = /* @__PURE__ */ new Map();
70
154
  agents;
71
155
  db;
72
- constructor(agents, db) {
156
+ announceRetry = new AnnouncementRetry((agent, id) => {
157
+ this.session(agent, id).flushAnnouncements();
158
+ });
159
+ maxSessions;
160
+ closing;
161
+ files;
162
+ constructor(agents, db, opts = {}) {
163
+ assertCoreRuntimeVersion("NativeRuntime");
73
164
  this.agents = agents;
74
165
  this.db = db;
166
+ this.files = opts.files;
167
+ const max = opts.maxSessions ?? 1e3;
168
+ if (!(Number.isInteger(max) && max >= 1) && max !== Infinity) throw new RangeError(`NativeRuntime: maxSessions must be an integer >= 1 (or Infinity for no cap), got ${max}`);
169
+ this.maxSessions = max;
75
170
  initSchema(db);
76
171
  }
172
+ agentDef(name) {
173
+ return this.agents[name];
174
+ }
77
175
  session(agent, id) {
78
176
  const key = `${agent}:${id}`;
79
- let a = this.actors.get(key);
80
- if (!a) {
81
- const def = this.agents[agent];
82
- if (!def) throw new Error(`unknown agent: ${agent}`);
83
- const model = def.instructions ? withSystem(def.model, def.instructions) : def.model;
84
- a = new AgentSession(agent, id, new SqliteSessionStore(this.db, key), new InProcBroadcaster(), model, def.tools, this);
85
- this.actors.set(key, a);
177
+ const hit = this.actors.get(key);
178
+ if (!hit && this.closing) throw new Error(`NativeRuntime is closed: cannot open session "${key}"`);
179
+ if (hit) {
180
+ this.actors.delete(key);
181
+ this.actors.set(key, hit);
182
+ return hit.session;
183
+ }
184
+ const def = this.agents[agent];
185
+ if (!def) throw new Error(`unknown agent: ${agent}`);
186
+ const model = def.instructions ? withSystem(def.model, def.instructions) : def.model;
187
+ const sink = new InProcEventSink();
188
+ const built = new AgentSession(agent, id, new SqliteSessionStore(this.db, key), sink, model, def.tools, this, def.channelInstructions);
189
+ built.files = this.files;
190
+ const session = wireAnnouncements(built, def, this.announceRetry);
191
+ this.evictIdle();
192
+ this.actors.set(key, {
193
+ session,
194
+ sink
195
+ });
196
+ return session;
197
+ }
198
+ get sessionCount() {
199
+ return this.actors.size;
200
+ }
201
+ recoverAnnouncements() {
202
+ const rows = this.db.query("SELECT output FROM agent_steps WHERE id = ?").all(ANNOUNCE_OUTBOX_STEP);
203
+ for (const { output } of rows) {
204
+ const [first] = JSON.parse(output);
205
+ if (first && this.agents[first.agent]?.onInputAnnouncement) this.session(first.agent, first.session);
206
+ }
207
+ }
208
+ close() {
209
+ this.closing ??= (async () => {
210
+ this.announceRetry.stop();
211
+ while ([...this.actors.values()].some((a) => !a.session.idle())) await new Promise((r) => setTimeout(r, 10));
212
+ this.actors.clear();
213
+ if (ownedDbs.has(this.db)) {
214
+ ownedDbs.delete(this.db);
215
+ this.db.close();
216
+ }
217
+ })();
218
+ return this.closing;
219
+ }
220
+ evictIdle() {
221
+ if (this.actors.size < this.maxSessions) return;
222
+ for (const [key, a] of this.actors) {
223
+ if (this.actors.size < this.maxSessions) break;
224
+ if (a.session.idle() && a.sink.size === 0) this.actors.delete(key);
86
225
  }
87
- return a;
88
226
  }
89
227
  };
90
- async function createNativeRuntime(agents, path = ":memory:") {
91
- return new NativeRuntime(agents, await openLocalSqliteSync(path));
228
+ async function createNativeRuntime(agents, path = ":memory:", opts = {}) {
229
+ const db = await openLocalSqliteSync(path);
230
+ ownedDbs.add(db);
231
+ let runtime;
232
+ try {
233
+ runtime = new NativeRuntime(agents, db, opts);
234
+ runtime.recoverAnnouncements();
235
+ return runtime;
236
+ } catch (err) {
237
+ if (runtime) await runtime.close();
238
+ else {
239
+ ownedDbs.delete(db);
240
+ db.close();
241
+ }
242
+ throw err;
243
+ }
92
244
  }
93
245
  var MemorySessionStore = class {
94
246
  msgs = [];
95
247
  steps = /* @__PURE__ */ new Map();
96
248
  status = "new";
249
+ generation = 0;
250
+ archives = [];
97
251
  appendMessage(m) {
98
252
  this.msgs.push(m);
99
253
  }
100
254
  messages() {
101
255
  return this.msgs.slice();
102
256
  }
103
- hasUserTurn(turnId) {
104
- return this.msgs.some((m) => m.role === "user" && m.turnId === turnId);
257
+ hasOpeningMessage(turnId) {
258
+ return this.msgs.some((m) => (m.role === "user" || m.role === "trigger") && m.turnId === turnId);
105
259
  }
106
260
  getStep(id) {
107
261
  return this.steps.has(id) ? this.steps.get(id) : void 0;
@@ -109,6 +263,9 @@ var MemorySessionStore = class {
109
263
  putStep(id, output) {
110
264
  this.steps.set(id, output);
111
265
  }
266
+ delStep(id) {
267
+ this.steps.delete(id);
268
+ }
112
269
  getStatus() {
113
270
  return this.status;
114
271
  }
@@ -118,44 +275,124 @@ var MemorySessionStore = class {
118
275
  tx(fn) {
119
276
  return fn();
120
277
  }
278
+ reset(inTx) {
279
+ const generation = this.generation++;
280
+ this.archives.push({
281
+ generation,
282
+ msgs: this.msgs,
283
+ steps: this.steps
284
+ });
285
+ this.msgs = [];
286
+ this.steps = /* @__PURE__ */ new Map();
287
+ this.status = "new";
288
+ inTx?.();
289
+ return generation;
290
+ }
121
291
  unwrap() {}
122
292
  };
123
293
  var MemoryRuntime = class {
124
294
  actors = /* @__PURE__ */ new Map();
125
295
  stores = /* @__PURE__ */ new Map();
126
296
  agents;
127
- constructor(agents) {
297
+ announceRetry = new AnnouncementRetry((agent, id) => {
298
+ this.session(agent, id).flushAnnouncements();
299
+ });
300
+ closed = false;
301
+ files;
302
+ constructor(agents, opts = {}) {
303
+ assertCoreRuntimeVersion("MemoryRuntime");
128
304
  this.agents = agents;
305
+ this.files = opts.files;
306
+ }
307
+ agentDef(name) {
308
+ return this.agents[name];
129
309
  }
130
310
  session(agent, id) {
131
311
  const key = `${agent}:${id}`;
132
312
  let a = this.actors.get(key);
133
313
  if (!a) {
314
+ if (this.closed) throw new Error(`MemoryRuntime is closed: cannot open session "${key}"`);
134
315
  const def = this.agents[agent];
135
316
  if (!def) throw new Error(`unknown agent: ${agent}`);
136
317
  const store = new MemorySessionStore();
137
318
  this.stores.set(key, store);
138
- a = new AgentSession(agent, id, store, new InProcBroadcaster(), def.model, def.tools, this);
319
+ const model = def.instructions ? withSystem(def.model, def.instructions) : def.model;
320
+ const built = new AgentSession(agent, id, store, new InProcEventSink(), model, def.tools, this, def.channelInstructions);
321
+ built.files = this.files;
322
+ a = wireAnnouncements(built, def, this.announceRetry);
139
323
  this.actors.set(key, a);
140
324
  }
141
325
  return a;
142
326
  }
327
+ async close() {
328
+ this.closed = true;
329
+ this.announceRetry.stop();
330
+ this.actors.clear();
331
+ this.stores.clear();
332
+ }
143
333
  };
144
334
  async function createAgentRuntime(agents, opts = {}) {
145
335
  const backend = opts.backend ?? "native";
146
- if (backend === "memory") return new MemoryRuntime(agents);
147
- if (backend === "native") return createNativeRuntime(agents, opts.path);
336
+ if (backend === "memory") return new MemoryRuntime(agents, { files: opts.files });
337
+ if (backend === "native") return createNativeRuntime(agents, opts.path, {
338
+ maxSessions: opts.maxSessions,
339
+ files: opts.files
340
+ });
148
341
  throw new Error("backend 'durable' is the Cloudflare Durable Object target — construct AgentDurableObject in your worker, not via createAgentRuntime");
149
342
  }
343
+ function warnOnDrift(agent, runtime) {
344
+ const def = runtime.agentDef?.(agent.name);
345
+ if (!def) return;
346
+ const names = (tools) => tools.map((t) => t.spec.name).sort();
347
+ const want = names(agent.tools), have = names(def.tools);
348
+ if (want.join("\0") !== have.join("\0")) {
349
+ const missing = want.filter((n) => !have.includes(n)), extra = have.filter((n) => !want.includes(n));
350
+ console.warn(`[june] mountAgent("${agent.name}"): the runtime's tools differ from the agent definition's` + (missing.length ? ` — missing from the runtime: ${missing.join(", ")}` : "") + (extra.length ? ` — only in the runtime: ${extra.join(", ")}` : "") + `. Build the runtime entry with toAgentDef(agent, model) so both come from one definition.`);
351
+ }
352
+ }
150
353
  function mountAgent(agent, runtime, opts = {}) {
151
354
  const chatPath = opts.chatPath ?? "/message";
152
355
  const channelsOn = opts.channels ?? true;
356
+ warnOnDrift(agent, runtime);
153
357
  const ctx = {
154
358
  agent,
359
+ services: opts.services,
360
+ files: opts.files ?? runtime.files,
155
361
  run: (message, o) => runtime.session(agent.name, o?.session ?? "default").turn({
156
362
  turnId: o?.turnId,
157
- userText: message
158
- })
363
+ userText: message,
364
+ event: o?.event,
365
+ trigger: o?.trigger,
366
+ replace: o?.replace
367
+ }),
368
+ runDetached: async (message, o) => runtime.session(agent.name, o?.session ?? "default").start({
369
+ turnId: o?.turnId,
370
+ userText: message,
371
+ event: o?.event,
372
+ trigger: o?.trigger,
373
+ replace: o?.replace
374
+ }),
375
+ runStream: async function* (message, o) {
376
+ const session = runtime.session(agent.name, o?.session ?? "default");
377
+ const { turnId } = session.start({
378
+ turnId: o?.turnId,
379
+ userText: message,
380
+ event: o?.event,
381
+ trigger: o?.trigger,
382
+ replace: o?.replace
383
+ });
384
+ yield* observeTurnEvents(session, turnId);
385
+ },
386
+ resumeStream: async function* (o) {
387
+ const session = runtime.session(agent.name, o.session ?? "default");
388
+ const granted = await grantAnswer(session, o, agent.authorizeAnswer);
389
+ const { turnId } = session.resume(o.turnId, o.inputId, o.input, {
390
+ by: o.by,
391
+ granted
392
+ });
393
+ yield* observeTurnEvents(session, turnId);
394
+ },
395
+ resetSession: (o) => runtime.session(agent.name, o?.session ?? "default").reset()
159
396
  };
160
397
  const channels = channelFetch(agent, ctx);
161
398
  const surface = async (req) => {
@@ -176,4 +413,4 @@ function mountAgent(agent, runtime, opts = {}) {
176
413
  };
177
414
  }
178
415
  //#endregion
179
- export { MemoryRuntime, NativeRuntime, createAgentRuntime, createNativeRuntime, mountAgent };
416
+ export { MemoryRuntime, NativeRuntime, createAgentRuntime, createNativeRuntime, mountAgent, toAgentDef };
package/dist/app.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import { JuneConfig } from "@junejs/core/config";
2
2
  //#region src/app.d.ts
3
- type CreateAppOptions = {
3
+ export type CreateAppOptions = {
4
4
  appDir: string;
5
5
  config?: JuneConfig;
6
6
  };
7
- type JuneApp = {
7
+ export type JuneApp = {
8
8
  fetch(request: Request): Promise<Response>;
9
9
  warmup(): Promise<void>;
10
10
  routePaths(): Promise<string[]>;
11
11
  earlyHints(): string[];
12
+ close(): Promise<void>;
12
13
  };
13
- declare function createApp({ appDir: appDirInput, config }: CreateAppOptions): JuneApp;
14
- //#endregion
15
- export { CreateAppOptions, JuneApp, createApp };
14
+ export declare function createApp({ appDir: appDirInput, config }: CreateAppOptions): JuneApp;
15
+ //#endregion
package/dist/app.js CHANGED
@@ -1,21 +1,24 @@
1
1
  import { discoverAgent } from "./agent-discover.js";
2
- import { createAgentRuntime, mountAgent } from "./agent-native.js";
2
+ import { createAgentRuntime, mountAgent, toAgentDef } from "./agent-native.js";
3
3
  import { findMiddlewareFile, isResourceFile, listRoutes, matchRouteTree, resolveNotFound, routeFiles } from "./router.js";
4
+ import { routeProblems, scanRoutes } from "./route-scan.js";
4
5
  import { createPipeline } from "./pipeline.js";
5
6
  import { resolveBoundary } from "./segment.js";
6
7
  import { memoizeResources } from "./resources.js";
8
+ import { localBlob } from "./blob.js";
7
9
  import { MODULE_STYLES_URL, buildModuleCss, registerCssModules } from "./css-modules.js";
8
10
  import { CLIENT_SCRIPT_URL, bundleClientSplit, findClientEntry } from "./client-bundle.js";
9
11
  import { generateIslandRegistry } from "./island-registry.js";
10
12
  import { STYLES_URL, findGlobalCss, globalCssUsesTailwind, processCssCached } from "./css.js";
11
13
  import { contentTypeFor, safeRelativePath } from "./static-files.js";
14
+ import { GENERATED_ICONS, googleFontFetcher, publicFileCheck, renderIcons, resolveIcons } from "./favicon.js";
12
15
  import { existsSync, lstatSync, realpathSync } from "node:fs";
13
16
  import { readFile } from "node:fs/promises";
14
17
  import { dirname, join, resolve } from "node:path";
15
18
  import { pathToFileURL } from "node:url";
16
- import { buildSystemPrompt } from "@junejs/core/agent-config";
19
+ import { blobFileStore, memoryFileStore } from "@junejs/core/agent-files";
17
20
  import { routeFromModule } from "@junejs/core/route";
18
- import { resolveAgent, resolveClientRouter, resolveSpeculationRules } from "@junejs/core/config";
21
+ import { deployOrigin, resolveAgent, resolveClientRouter, resolveSpeculationRules } from "@junejs/core/config";
19
22
  import { runWithTrace } from "@junejs/core/instrumentation";
20
23
  import { anthropic } from "@junejs/core/agent-models";
21
24
  //#region src/app.ts
@@ -69,19 +72,31 @@ function createApp({ appDir: appDirInput, config = {} }) {
69
72
  try {
70
73
  if (lstatSync(publicDir).isDirectory()) publicRoot = realpathSync(publicDir);
71
74
  } catch {}
75
+ const iconPlan = resolveIcons(config.site ?? {}, publicFileCheck(publicDir));
76
+ let iconsPromise;
77
+ const getIcons = () => iconsPromise ??= renderIcons({
78
+ site: config.site ?? {},
79
+ fetchFont: googleFontFetcher(join(dirname(appDir), "node_modules", ".cache", "june", "fonts")),
80
+ warn: (msg) => console.warn(msg)
81
+ }).catch((err) => {
82
+ iconsPromise = void 0;
83
+ throw err;
84
+ });
72
85
  const agent = resolveAgent(config.agent);
73
86
  const speculation = config.speculation;
74
87
  const clientEntry = findClientEntry(appDir) ?? findClientEntry(join(dirname(appDir), ".june", "routes"));
75
88
  const cssEntry = findGlobalCss(appDir);
76
89
  const docConfig = {
77
90
  site: config.site ?? {},
91
+ deployOrigin: deployOrigin(config),
78
92
  speculationRules: resolveSpeculationRules(speculation ?? void 0),
79
93
  speculationDelivery: speculation ? speculation.delivery ?? "inline" : "inline",
80
94
  viewTransitions: config.viewTransitions ?? true,
81
95
  cssReset: config.cssReset ?? !globalCssUsesTailwind(appDir),
82
96
  clientRouter: resolveClientRouter(config.clientRouter),
83
97
  clientScript: clientEntry ? CLIENT_SCRIPT_URL : null,
84
- styles: cssEntry ? STYLES_URL : null
98
+ styles: cssEntry ? STYLES_URL : null,
99
+ icons: iconPlan.icons
85
100
  };
86
101
  let moduleCssPromise;
87
102
  const getModuleCss = () => moduleCssPromise ??= buildModuleCss(appDir, dirname(appDir));
@@ -103,6 +118,7 @@ function createApp({ appDir: appDirInput, config = {} }) {
103
118
  config.dataLayer?.install();
104
119
  let pipelinePromise;
105
120
  const getPipeline = () => pipelinePromise ??= buildPipeline();
121
+ let agentRuntime;
106
122
  async function buildPipeline() {
107
123
  const { maps, css } = await getModuleCss();
108
124
  await registerCssModules(maps);
@@ -125,11 +141,14 @@ function createApp({ appDir: appDirInput, config = {} }) {
125
141
  const def = await discoverAgent(agentDir);
126
142
  const backend = agent.runtime.backend === "durable" ? "native" : agent.runtime.backend;
127
143
  const model = anthropic({ model: def.model });
128
- const mounted = mountAgent(def, await createAgentRuntime({ [def.name]: {
129
- model,
130
- tools: def.tools,
131
- instructions: buildSystemPrompt(def)
132
- } }, { backend }), {
144
+ let blobOpened;
145
+ const files = backend === "memory" ? memoryFileStore() : blobFileStore(() => blobOpened ??= resources().then(async (r) => r.blob ?? await localBlob().open()));
146
+ const rt = await createAgentRuntime({ [def.name]: toAgentDef(def, model) }, {
147
+ backend,
148
+ files
149
+ });
150
+ agentRuntime = rt;
151
+ const mounted = mountAgent(def, rt, {
133
152
  chatPath: agent.runtime.chat.path,
134
153
  channels: agent.runtime.channels
135
154
  });
@@ -238,9 +257,18 @@ function createApp({ appDir: appDirInput, config = {} }) {
238
257
  }
239
258
  }
240
259
  }
260
+ if (iconPlan.generate && (request.method === "GET" || request.method === "HEAD")) {
261
+ const name = new URL(request.url).pathname.slice(1);
262
+ if (GENERATED_ICONS.includes(name)) return getIcons().then((icons) => new Response(request.method === "HEAD" ? null : icons[name], { headers: {
263
+ "content-type": contentTypeFor(name),
264
+ "cache-control": "public, max-age=0, must-revalidate"
265
+ } }), () => toPipeline());
266
+ }
241
267
  return toPipeline();
242
268
  },
243
269
  async warmup() {
270
+ const problems = routeProblems([await scanRoutes(appDir), hasJuneRoutes ? await scanRoutes(juneRoutesDir) : []], dirname(appDir));
271
+ if (problems) console.error(problems);
244
272
  const files = await routeFiles(appDir, { pageConvention: true });
245
273
  if (hasJuneRoutes) files.push(...await routeFiles(juneRoutesDir, { pageConvention: true }));
246
274
  for (const file of files) await import(pathToFileURL(file).href).catch((err) => {
@@ -248,7 +276,11 @@ function createApp({ appDir: appDirInput, config = {} }) {
248
276
  });
249
277
  },
250
278
  routePaths,
251
- earlyHints: () => config.earlyHints ?? []
279
+ earlyHints: () => config.earlyHints ?? [],
280
+ async close() {
281
+ await pipelinePromise?.catch(() => {});
282
+ await agentRuntime?.close();
283
+ }
252
284
  };
253
285
  }
254
286
  //#endregion
package/dist/blob.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { BlobFactory } from "@junejs/core/resources";
2
2
  //#region src/blob.d.ts
3
- declare function localBlob(opts?: {
3
+ export declare function localBlob(opts?: {
4
4
  dir?: string;
5
5
  }): BlobFactory;
6
6
  interface R2ObjectBody {
7
7
  arrayBuffer(): Promise<ArrayBuffer>;
8
8
  }
9
- interface R2Bucket {
9
+ export interface R2Bucket {
10
10
  get(key: string): Promise<R2ObjectBody | null>;
11
11
  put(key: string, data: ArrayBuffer | Uint8Array | string): Promise<unknown>;
12
12
  delete(key: string): Promise<void>;
@@ -18,6 +18,5 @@ interface R2Bucket {
18
18
  }[];
19
19
  }>;
20
20
  }
21
- declare function r2(bucket: R2Bucket): BlobFactory;
22
- //#endregion
23
- export { R2Bucket, localBlob, r2 };
21
+ export declare function r2(bucket: R2Bucket): BlobFactory;
22
+ //#endregion