@junejs/server 1.0.0-dev.36 → 1.0.0-dev.45
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/dist/agent-durable.d.ts +19 -2
- package/dist/agent-durable.js +118 -7
- package/dist/agent-native.d.ts +13 -2
- package/dist/agent-native.js +108 -6
- package/dist/app.d.ts +1 -0
- package/dist/app.js +10 -1
- package/dist/build.d.ts +3 -3
- package/dist/build.js +8 -2
- package/dist/{config-freeze-ZWC_Qd4h.d.ts → config-freeze-CyXvNqi2.d.ts} +1 -1
- package/dist/config-freeze.d.ts +1 -1
- package/dist/core-version.js +1 -1
- package/dist/dev.d.ts +6 -2
- package/dist/dev.js +40 -9
- package/dist/host.d.ts +1 -0
- package/dist/host.js +2 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/{manifest-DSNX4I-G.d.ts → manifest-CwTT1zVs.d.ts} +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +3 -1
- package/dist/pipeline.js +15 -6
- package/dist/route-rank.d.ts +10 -0
- package/dist/route-rank.js +38 -0
- package/dist/route-scan-BMbBzPIB.d.ts +20 -0
- package/dist/route-scan.d.ts +2 -2
- package/dist/route-scan.js +47 -2
- package/dist/router.js +58 -44
- package/dist/{worker-Dv4l6Ybs.d.ts → worker-2qbQjj2c.d.ts} +1 -0
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +52 -76
- package/package.json +3 -3
- package/src/agent-durable.ts +164 -11
- package/src/agent-native.ts +161 -7
- package/src/app.ts +22 -1
- package/src/build.ts +11 -1
- package/src/core-version.ts +1 -1
- package/src/dev.ts +41 -8
- package/src/host.ts +4 -3
- package/src/manifest.ts +2 -0
- package/src/pipeline.ts +20 -4
- package/src/route-rank.ts +60 -0
- package/src/route-scan.ts +78 -1
- package/src/router.ts +75 -58
- package/src/worker.ts +66 -82
- package/dist/route-scan-DMlwoESy.d.ts +0 -13
package/dist/agent-durable.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Channel, ChannelFactory } from "@junejs/core/agent-config";
|
|
2
|
-
import { AuthorizeAnswer, ChannelPolicy, InboundEvent, Model, Msg, ProactiveTrigger, SessionStore, Tool, TurnError, TurnEvent, TurnFailurePhase } from "@junejs/core/agent-runtime";
|
|
2
|
+
import { AuthorizeAnswer, ChannelPolicy, InboundEvent, InputAnnouncement, Model, Msg, NoteKind, ProactiveTrigger, SessionStore, Tool, TurnError, TurnEvent, TurnFailurePhase } from "@junejs/core/agent-runtime";
|
|
3
3
|
import { Connection } from "@junejs/core/connections";
|
|
4
4
|
import { Resources } from "@junejs/core/resources";
|
|
5
5
|
//#region src/agent-durable.d.ts
|
|
@@ -14,6 +14,7 @@ export interface SqlStorage {
|
|
|
14
14
|
export interface DurableStorage {
|
|
15
15
|
sql: SqlStorage;
|
|
16
16
|
transactionSync<T>(fn: () => T): T;
|
|
17
|
+
setAlarm?(scheduledTime: number): unknown;
|
|
17
18
|
}
|
|
18
19
|
export interface DurableObjectState {
|
|
19
20
|
storage: DurableStorage;
|
|
@@ -40,7 +41,7 @@ export declare class DoSessionStore implements SessionStore {
|
|
|
40
41
|
getSessionKey(): string | undefined;
|
|
41
42
|
setSessionKey(key: string): void;
|
|
42
43
|
tx<T>(fn: () => T): T;
|
|
43
|
-
reset(): number;
|
|
44
|
+
reset(inTx?: () => void): number;
|
|
44
45
|
unwrap<H = unknown>(): H;
|
|
45
46
|
}
|
|
46
47
|
export type DoAgentDef = {
|
|
@@ -61,6 +62,7 @@ export type DoAgentDef = {
|
|
|
61
62
|
step?: string;
|
|
62
63
|
}) => void | Promise<void>;
|
|
63
64
|
authorizeAnswer?: AuthorizeAnswer;
|
|
65
|
+
onInputAnnouncement?: (a: InputAnnouncement) => void | Promise<void>;
|
|
64
66
|
};
|
|
65
67
|
export declare class AgentDurableObject {
|
|
66
68
|
private session;
|
|
@@ -79,11 +81,18 @@ export declare class AgentDurableObject {
|
|
|
79
81
|
private readonly doEnv;
|
|
80
82
|
private readonly services;
|
|
81
83
|
private readonly authorizeAnswer?;
|
|
84
|
+
private readonly onInputAnnouncement?;
|
|
85
|
+
private readonly storage;
|
|
86
|
+
private announceFailures;
|
|
82
87
|
private resolveResources;
|
|
83
88
|
private resolveConnections;
|
|
84
89
|
private ready;
|
|
85
90
|
constructor(state: DurableObjectState, def: DoAgentDef);
|
|
86
91
|
private resolveSession;
|
|
92
|
+
alarm(): Promise<void>;
|
|
93
|
+
private armWatchdog;
|
|
94
|
+
private scheduleAlarm;
|
|
95
|
+
private deliverHeld;
|
|
87
96
|
turn(input: {
|
|
88
97
|
turnId?: string;
|
|
89
98
|
userText: string;
|
|
@@ -100,6 +109,14 @@ export declare class AgentDurableObject {
|
|
|
100
109
|
}): Promise<{
|
|
101
110
|
turnId: string;
|
|
102
111
|
}>;
|
|
112
|
+
note(input: {
|
|
113
|
+
by: string;
|
|
114
|
+
kind: NoteKind;
|
|
115
|
+
text: string;
|
|
116
|
+
session?: string;
|
|
117
|
+
}): Promise<{
|
|
118
|
+
noteId: string;
|
|
119
|
+
}>;
|
|
103
120
|
reset(opts?: {
|
|
104
121
|
session?: string;
|
|
105
122
|
}): Promise<{
|
package/dist/agent-durable.js
CHANGED
|
@@ -54,7 +54,7 @@ var DoSessionStore = class {
|
|
|
54
54
|
tx(fn) {
|
|
55
55
|
return this.storage.transactionSync(fn);
|
|
56
56
|
}
|
|
57
|
-
reset() {
|
|
57
|
+
reset(inTx) {
|
|
58
58
|
return this.storage.transactionSync(() => {
|
|
59
59
|
const sql = this.sql;
|
|
60
60
|
sql.exec(`CREATE TABLE IF NOT EXISTS agent_messages_archive (generation INTEGER, seq INTEGER, body TEXT)`);
|
|
@@ -67,6 +67,7 @@ var DoSessionStore = class {
|
|
|
67
67
|
sql.exec("DELETE FROM agent_steps");
|
|
68
68
|
this.setStatus("new");
|
|
69
69
|
sql.exec("INSERT INTO agent_meta (k, v) VALUES ('generation', ?) ON CONFLICT(k) DO UPDATE SET v = ?", String(generation + 1), String(generation + 1));
|
|
70
|
+
inTx?.();
|
|
70
71
|
return generation;
|
|
71
72
|
});
|
|
72
73
|
}
|
|
@@ -108,6 +109,9 @@ var AgentDurableObject = class {
|
|
|
108
109
|
doEnv;
|
|
109
110
|
services;
|
|
110
111
|
authorizeAnswer;
|
|
112
|
+
onInputAnnouncement;
|
|
113
|
+
storage;
|
|
114
|
+
announceFailures = 0;
|
|
111
115
|
resolveResources() {
|
|
112
116
|
return this.resourcesOpened ??= Promise.resolve(typeof this.resourcesInput === "function" ? this.resourcesInput(this.doEnv) : this.resourcesInput ?? {});
|
|
113
117
|
}
|
|
@@ -138,8 +142,13 @@ var AgentDurableObject = class {
|
|
|
138
142
|
this.doEnv = def.env;
|
|
139
143
|
this.services = def.services;
|
|
140
144
|
this.authorizeAnswer = def.authorizeAnswer;
|
|
145
|
+
this.onInputAnnouncement = def.onInputAnnouncement;
|
|
146
|
+
this.storage = state.storage;
|
|
141
147
|
const name = def.name ?? "agent";
|
|
142
148
|
const sink = new InProcEventSink();
|
|
149
|
+
sink.subscribe((e) => {
|
|
150
|
+
if (e.type === "turn.started") this.armWatchdog();
|
|
151
|
+
});
|
|
143
152
|
sink.subscribe((e) => {
|
|
144
153
|
if (e.type !== "turn.failed") return;
|
|
145
154
|
const defaultLog = () => console.error(`[june] agent "${name}" turn ${e.turnId} failed${e.step ? ` at ${e.step}` : ""}: ${e.error.stack ?? e.error.message}${e.error.causeChain?.length ? `\ncaused by: ${e.error.causeChain.join(" ← ")}` : ""}`);
|
|
@@ -193,8 +202,71 @@ var AgentDurableObject = class {
|
|
|
193
202
|
if (key !== void 0 && key !== "self" && stored === void 0) this.store.setSessionKey(key);
|
|
194
203
|
this.sessionKey = resolved;
|
|
195
204
|
this.session = new AgentSession(this.name, resolved, this.store, this.sink, this.model, this.tools, crossDoUnsupported, this.channelInstructions);
|
|
205
|
+
this.session.onDequeue = (heldTurnId, held) => {
|
|
206
|
+
if (held.hostContext?.deliver && held.event) this.deliverHeld(this.session, heldTurnId, held.event);
|
|
207
|
+
};
|
|
208
|
+
const announce = this.onInputAnnouncement;
|
|
209
|
+
if (announce) {
|
|
210
|
+
this.session.onAnnounce = async (a) => {
|
|
211
|
+
await ensureScope();
|
|
212
|
+
const resources = await this.ready();
|
|
213
|
+
try {
|
|
214
|
+
await runInScope({
|
|
215
|
+
resources,
|
|
216
|
+
services: this.services
|
|
217
|
+
}, () => announce(a));
|
|
218
|
+
} catch (err) {
|
|
219
|
+
this.scheduleAlarm(Math.min(5e3 * 2 ** this.announceFailures, 3e5));
|
|
220
|
+
this.announceFailures++;
|
|
221
|
+
throw err;
|
|
222
|
+
}
|
|
223
|
+
this.announceFailures = 0;
|
|
224
|
+
};
|
|
225
|
+
this.session.flushAnnouncements();
|
|
226
|
+
}
|
|
196
227
|
return this.session;
|
|
197
228
|
}
|
|
229
|
+
async alarm() {
|
|
230
|
+
if (!this.onInputAnnouncement) return;
|
|
231
|
+
await this.resolveSession().flushAnnouncements();
|
|
232
|
+
}
|
|
233
|
+
armWatchdog() {
|
|
234
|
+
if (this.onInputAnnouncement) this.scheduleAlarm(6e4);
|
|
235
|
+
}
|
|
236
|
+
scheduleAlarm(inMs) {
|
|
237
|
+
const failed = (err) => console.error(`[june] agent "${this.name}": could not schedule the announcement alarm — delivery waits for the next announcement, turn or rebuild:`, err);
|
|
238
|
+
try {
|
|
239
|
+
Promise.resolve(this.storage.setAlarm?.(Date.now() + inMs)).catch(failed);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
failed(err);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
deliverHeld(session, turnId, event) {
|
|
245
|
+
const logRenderFailure = (err) => console.error(`[june] agent "${this.name}": delivered render for held turn ${turnId} failed:`, err);
|
|
246
|
+
try {
|
|
247
|
+
const channel = this.channels.find((c) => c.name === event.source);
|
|
248
|
+
if (!channel?.deliver) {
|
|
249
|
+
logRenderFailure(/* @__PURE__ */ new Error(`no deliver()-capable channel named "${event.source}" is wired into this DO — the held turn runs, but its reply is not rendered`));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const it = observeTurnEvents(session, turnId)[Symbol.asyncIterator]();
|
|
253
|
+
const events = { [Symbol.asyncIterator]: () => it };
|
|
254
|
+
const target = {
|
|
255
|
+
channelId: event.channelId,
|
|
256
|
+
threadId: event.threadId,
|
|
257
|
+
recipientUserId: event.user?.id,
|
|
258
|
+
recipientTeamId: event.teamId
|
|
259
|
+
};
|
|
260
|
+
this.ready().then((resources) => runInScope({
|
|
261
|
+
resources,
|
|
262
|
+
services: this.services
|
|
263
|
+
}, () => channel.deliver(target, events, { session: this.sessionKey }))).catch(logRenderFailure).finally(() => {
|
|
264
|
+
it.return?.();
|
|
265
|
+
});
|
|
266
|
+
} catch (err) {
|
|
267
|
+
logRenderFailure(err);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
198
270
|
async turn(input) {
|
|
199
271
|
await ensureScope();
|
|
200
272
|
const resources = await this.ready();
|
|
@@ -213,8 +285,14 @@ var AgentDurableObject = class {
|
|
|
213
285
|
services: this.services
|
|
214
286
|
}, () => session.start(input));
|
|
215
287
|
}
|
|
288
|
+
note(input) {
|
|
289
|
+
const { session, ...note } = input;
|
|
290
|
+
return this.resolveSession(session).note(note);
|
|
291
|
+
}
|
|
216
292
|
reset(opts) {
|
|
217
|
-
|
|
293
|
+
const session = this.resolveSession(opts?.session);
|
|
294
|
+
this.armWatchdog();
|
|
295
|
+
return session.reset();
|
|
218
296
|
}
|
|
219
297
|
transcript() {
|
|
220
298
|
if (this.session) return this.session.transcript();
|
|
@@ -225,10 +303,13 @@ var AgentDurableObject = class {
|
|
|
225
303
|
const url = new URL(req.url);
|
|
226
304
|
const key = req.headers.get("x-june-session") ?? void 0;
|
|
227
305
|
if (req.method === "POST" && url.pathname.endsWith("/turn")) {
|
|
228
|
-
const { userText, turnId, event, trigger } = await req.json();
|
|
306
|
+
const { userText, turnId, event, trigger, ifSuspended } = await req.json();
|
|
229
307
|
await ensureScope();
|
|
230
308
|
const replace = url.searchParams.get("replace") === "1";
|
|
231
309
|
const wantsDeliver = url.searchParams.get("deliver") === "1";
|
|
310
|
+
const detached = url.searchParams.get("detach") === "1";
|
|
311
|
+
if (ifSuspended === "queue" && !wantsDeliver && !detached) return Response.json({ error: `ifSuspended "queue" needs deliver=1 or detach=1: a held turn runs later, when no one is streaming it` }, { status: 400 });
|
|
312
|
+
if (ifSuspended === "queue" && replace) return Response.json({ error: `ifSuspended "queue" and replace=1 cannot be combined — replacing would drop held turns that were accepted` }, { status: 400 });
|
|
232
313
|
let deliverChannel;
|
|
233
314
|
if (wantsDeliver) {
|
|
234
315
|
if (!event?.channelId) return Response.json({ error: "deliver=1 needs an inbound event (event.channelId names the reply target)" }, { status: 400 });
|
|
@@ -240,6 +321,7 @@ var AgentDurableObject = class {
|
|
|
240
321
|
let started;
|
|
241
322
|
try {
|
|
242
323
|
session = this.resolveSession(key);
|
|
324
|
+
this.armWatchdog();
|
|
243
325
|
started = runInScope({
|
|
244
326
|
resources,
|
|
245
327
|
services: this.services
|
|
@@ -248,11 +330,17 @@ var AgentDurableObject = class {
|
|
|
248
330
|
turnId,
|
|
249
331
|
event,
|
|
250
332
|
trigger,
|
|
251
|
-
replace
|
|
333
|
+
replace,
|
|
334
|
+
ifSuspended,
|
|
335
|
+
hostContext: wantsDeliver ? { deliver: true } : void 0
|
|
252
336
|
}));
|
|
253
337
|
} catch (err) {
|
|
254
338
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 409 });
|
|
255
339
|
}
|
|
340
|
+
if (started.queued) return Response.json({
|
|
341
|
+
turnId: started.turnId,
|
|
342
|
+
queued: true
|
|
343
|
+
}, { status: 202 });
|
|
256
344
|
if (wantsDeliver) {
|
|
257
345
|
const logRenderFailure = (err) => console.error(`[june] agent "${this.name}": delivered render for turn ${started.turnId} failed:`, err);
|
|
258
346
|
try {
|
|
@@ -275,7 +363,7 @@ var AgentDurableObject = class {
|
|
|
275
363
|
}
|
|
276
364
|
return Response.json({ turnId: started.turnId }, { status: 202 });
|
|
277
365
|
}
|
|
278
|
-
if (
|
|
366
|
+
if (detached) return Response.json({ turnId: started.turnId }, { status: 202 });
|
|
279
367
|
return new Response(sseTurnStream(session, started.turnId), { headers: SSE_HEADERS });
|
|
280
368
|
}
|
|
281
369
|
if (req.method === "POST" && url.pathname.endsWith("/resume")) {
|
|
@@ -310,6 +398,7 @@ var AgentDurableObject = class {
|
|
|
310
398
|
console.error(`[june] agent "${this.name}": authorizeAnswer failed for input "${inputId}" of turn ${turnId}:`, err);
|
|
311
399
|
return Response.json({ error: `authorizeAnswer failed: ${err instanceof Error ? err.message : String(err)} — the answer was NOT applied` }, { status: 500 });
|
|
312
400
|
}
|
|
401
|
+
this.armWatchdog();
|
|
313
402
|
try {
|
|
314
403
|
runInScope({
|
|
315
404
|
resources,
|
|
@@ -347,12 +436,33 @@ var AgentDurableObject = class {
|
|
|
347
436
|
} catch (err) {
|
|
348
437
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 409 });
|
|
349
438
|
}
|
|
439
|
+
this.armWatchdog();
|
|
350
440
|
try {
|
|
351
441
|
return Response.json(await session.reset());
|
|
352
442
|
} catch (err) {
|
|
353
443
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 409 });
|
|
354
444
|
}
|
|
355
445
|
}
|
|
446
|
+
if (req.method === "POST" && url.pathname.endsWith("/note")) {
|
|
447
|
+
let input;
|
|
448
|
+
try {
|
|
449
|
+
input = await req.json();
|
|
450
|
+
} catch {
|
|
451
|
+
return Response.json({ error: "note: the body must be JSON { by, kind, text }" }, { status: 400 });
|
|
452
|
+
}
|
|
453
|
+
let session;
|
|
454
|
+
try {
|
|
455
|
+
session = this.resolveSession(key);
|
|
456
|
+
} catch (err) {
|
|
457
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 409 });
|
|
458
|
+
}
|
|
459
|
+
try {
|
|
460
|
+
return Response.json(await session.note(input));
|
|
461
|
+
} catch (err) {
|
|
462
|
+
if (err instanceof TypeError) return Response.json({ error: err.message }, { status: 400 });
|
|
463
|
+
throw err;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
356
466
|
if (url.pathname.endsWith("/transcript")) {
|
|
357
467
|
if (key !== void 0) try {
|
|
358
468
|
return Response.json({ transcript: this.resolveSession(key).transcript() });
|
|
@@ -361,7 +471,7 @@ var AgentDurableObject = class {
|
|
|
361
471
|
}
|
|
362
472
|
return Response.json({ transcript: this.transcript() });
|
|
363
473
|
}
|
|
364
|
-
return new Response("agent DO — POST /turn, POST /resume, POST /reset, or GET /transcript", { status: 404 });
|
|
474
|
+
return new Response("agent DO — POST /turn, POST /resume, POST /reset, POST /note, or GET /transcript", { status: 404 });
|
|
365
475
|
}
|
|
366
476
|
};
|
|
367
477
|
const SSE_HEADERS = {
|
|
@@ -608,7 +718,8 @@ function serializeTurn(userText, o) {
|
|
|
608
718
|
userText,
|
|
609
719
|
turnId: o?.turnId,
|
|
610
720
|
event: o?.event,
|
|
611
|
-
trigger: o?.trigger
|
|
721
|
+
trigger: o?.trigger,
|
|
722
|
+
ifSuspended: o?.ifSuspended
|
|
612
723
|
};
|
|
613
724
|
try {
|
|
614
725
|
return JSON.stringify(payload);
|
package/dist/agent-native.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { SyncSqlite } from "./sqlite-driver.js";
|
|
2
2
|
import { AgentDefinition, ChannelContext } from "@junejs/core/agent-config";
|
|
3
|
-
import { AgentSession, ChannelPolicy, Model, Runtime, Tool } from "@junejs/core/agent-runtime";
|
|
3
|
+
import { AgentSession, ChannelPolicy, InputAnnouncement, Model, Runtime, Tool } from "@junejs/core/agent-runtime";
|
|
4
4
|
//#region src/agent-native.d.ts
|
|
5
5
|
export type AgentDef = {
|
|
6
6
|
model: Model;
|
|
7
7
|
tools: Tool[];
|
|
8
8
|
instructions?: string;
|
|
9
9
|
channelInstructions?: Record<string, string | ChannelPolicy>;
|
|
10
|
+
onInputAnnouncement?: (a: InputAnnouncement) => void | Promise<void>;
|
|
10
11
|
};
|
|
11
12
|
export declare function toAgentDef(agent: AgentDefinition, model: Model): AgentDef;
|
|
12
13
|
export type NativeRuntimeOptions = {
|
|
@@ -16,11 +17,15 @@ export declare class NativeRuntime implements Runtime {
|
|
|
16
17
|
private actors;
|
|
17
18
|
private readonly agents;
|
|
18
19
|
private readonly db;
|
|
20
|
+
private readonly announceRetry;
|
|
19
21
|
private readonly maxSessions;
|
|
22
|
+
private closing;
|
|
20
23
|
constructor(agents: Record<string, AgentDef>, db: SyncSqlite, opts?: NativeRuntimeOptions);
|
|
21
24
|
agentDef(name: string): AgentDef | undefined;
|
|
22
25
|
session(agent: string, id: string): AgentSession;
|
|
23
26
|
get sessionCount(): number;
|
|
27
|
+
recoverAnnouncements(): void;
|
|
28
|
+
close(): Promise<void>;
|
|
24
29
|
private evictIdle;
|
|
25
30
|
}
|
|
26
31
|
export declare function createNativeRuntime(agents: Record<string, AgentDef>, path?: string, opts?: NativeRuntimeOptions): Promise<NativeRuntime>;
|
|
@@ -28,16 +33,22 @@ export declare class MemoryRuntime implements Runtime {
|
|
|
28
33
|
private actors;
|
|
29
34
|
private stores;
|
|
30
35
|
private readonly agents;
|
|
36
|
+
private readonly announceRetry;
|
|
37
|
+
private closed;
|
|
31
38
|
constructor(agents: Record<string, AgentDef>);
|
|
32
39
|
agentDef(name: string): AgentDef | undefined;
|
|
33
40
|
session(agent: string, id: string): AgentSession;
|
|
41
|
+
close(): Promise<void>;
|
|
34
42
|
}
|
|
35
43
|
export type AgentBackend = "native" | "memory" | "durable";
|
|
44
|
+
export type InProcessRuntime = Runtime & {
|
|
45
|
+
close(): Promise<void>;
|
|
46
|
+
};
|
|
36
47
|
export declare function createAgentRuntime(agents: Record<string, AgentDef>, opts?: {
|
|
37
48
|
backend?: AgentBackend;
|
|
38
49
|
path?: string;
|
|
39
50
|
maxSessions?: number;
|
|
40
|
-
}): Promise<
|
|
51
|
+
}): Promise<InProcessRuntime>;
|
|
41
52
|
export declare function mountAgent(agent: AgentDefinition, runtime: Runtime, opts?: {
|
|
42
53
|
chatPath?: string;
|
|
43
54
|
channels?: boolean;
|
package/dist/agent-native.js
CHANGED
|
@@ -53,7 +53,7 @@ var SqliteSessionStore = class {
|
|
|
53
53
|
throw e;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
reset() {
|
|
56
|
+
reset(inTx) {
|
|
57
57
|
return this.tx(() => {
|
|
58
58
|
this.db.exec(`CREATE TABLE IF NOT EXISTS agent_messages_archive (session_id TEXT, generation INTEGER, seq INTEGER, body TEXT)`);
|
|
59
59
|
this.db.exec(`CREATE TABLE IF NOT EXISTS agent_steps_archive (session_id TEXT, generation INTEGER, id TEXT, output TEXT)`);
|
|
@@ -65,6 +65,7 @@ var SqliteSessionStore = class {
|
|
|
65
65
|
this.db.query("DELETE FROM agent_steps WHERE session_id = ?").run(this.sid);
|
|
66
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
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?.();
|
|
68
69
|
return generation;
|
|
69
70
|
});
|
|
70
71
|
}
|
|
@@ -89,19 +90,74 @@ var InProcEventSink = class {
|
|
|
89
90
|
return this.subs.size;
|
|
90
91
|
}
|
|
91
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";
|
|
92
143
|
function toAgentDef(agent, model) {
|
|
93
144
|
return {
|
|
94
145
|
model,
|
|
95
146
|
tools: agent.tools,
|
|
96
147
|
instructions: buildSystemPrompt(agent),
|
|
97
|
-
...agent.channelInstructions ? { channelInstructions: agent.channelInstructions } : {}
|
|
148
|
+
...agent.channelInstructions ? { channelInstructions: agent.channelInstructions } : {},
|
|
149
|
+
...agent.onInputAnnouncement ? { onInputAnnouncement: agent.onInputAnnouncement } : {}
|
|
98
150
|
};
|
|
99
151
|
}
|
|
100
152
|
var NativeRuntime = class {
|
|
101
153
|
actors = /* @__PURE__ */ new Map();
|
|
102
154
|
agents;
|
|
103
155
|
db;
|
|
156
|
+
announceRetry = new AnnouncementRetry((agent, id) => {
|
|
157
|
+
this.session(agent, id).flushAnnouncements();
|
|
158
|
+
});
|
|
104
159
|
maxSessions;
|
|
160
|
+
closing;
|
|
105
161
|
constructor(agents, db, opts = {}) {
|
|
106
162
|
assertCoreRuntimeVersion("NativeRuntime");
|
|
107
163
|
this.agents = agents;
|
|
@@ -117,6 +173,7 @@ var NativeRuntime = class {
|
|
|
117
173
|
session(agent, id) {
|
|
118
174
|
const key = `${agent}:${id}`;
|
|
119
175
|
const hit = this.actors.get(key);
|
|
176
|
+
if (!hit && this.closing) throw new Error(`NativeRuntime is closed: cannot open session "${key}"`);
|
|
120
177
|
if (hit) {
|
|
121
178
|
this.actors.delete(key);
|
|
122
179
|
this.actors.set(key, hit);
|
|
@@ -126,7 +183,7 @@ var NativeRuntime = class {
|
|
|
126
183
|
if (!def) throw new Error(`unknown agent: ${agent}`);
|
|
127
184
|
const model = def.instructions ? withSystem(def.model, def.instructions) : def.model;
|
|
128
185
|
const sink = new InProcEventSink();
|
|
129
|
-
const session = new AgentSession(agent, id, new SqliteSessionStore(this.db, key), sink, model, def.tools, this, def.channelInstructions);
|
|
186
|
+
const session = wireAnnouncements(new AgentSession(agent, id, new SqliteSessionStore(this.db, key), sink, model, def.tools, this, def.channelInstructions), def, this.announceRetry);
|
|
130
187
|
this.evictIdle();
|
|
131
188
|
this.actors.set(key, {
|
|
132
189
|
session,
|
|
@@ -137,6 +194,25 @@ var NativeRuntime = class {
|
|
|
137
194
|
get sessionCount() {
|
|
138
195
|
return this.actors.size;
|
|
139
196
|
}
|
|
197
|
+
recoverAnnouncements() {
|
|
198
|
+
const rows = this.db.query("SELECT output FROM agent_steps WHERE id = ?").all(ANNOUNCE_OUTBOX_STEP);
|
|
199
|
+
for (const { output } of rows) {
|
|
200
|
+
const [first] = JSON.parse(output);
|
|
201
|
+
if (first && this.agents[first.agent]?.onInputAnnouncement) this.session(first.agent, first.session);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
close() {
|
|
205
|
+
this.closing ??= (async () => {
|
|
206
|
+
this.announceRetry.stop();
|
|
207
|
+
while ([...this.actors.values()].some((a) => !a.session.idle())) await new Promise((r) => setTimeout(r, 10));
|
|
208
|
+
this.actors.clear();
|
|
209
|
+
if (ownedDbs.has(this.db)) {
|
|
210
|
+
ownedDbs.delete(this.db);
|
|
211
|
+
this.db.close();
|
|
212
|
+
}
|
|
213
|
+
})();
|
|
214
|
+
return this.closing;
|
|
215
|
+
}
|
|
140
216
|
evictIdle() {
|
|
141
217
|
if (this.actors.size < this.maxSessions) return;
|
|
142
218
|
for (const [key, a] of this.actors) {
|
|
@@ -146,7 +222,21 @@ var NativeRuntime = class {
|
|
|
146
222
|
}
|
|
147
223
|
};
|
|
148
224
|
async function createNativeRuntime(agents, path = ":memory:", opts = {}) {
|
|
149
|
-
|
|
225
|
+
const db = await openLocalSqliteSync(path);
|
|
226
|
+
ownedDbs.add(db);
|
|
227
|
+
let runtime;
|
|
228
|
+
try {
|
|
229
|
+
runtime = new NativeRuntime(agents, db, opts);
|
|
230
|
+
runtime.recoverAnnouncements();
|
|
231
|
+
return runtime;
|
|
232
|
+
} catch (err) {
|
|
233
|
+
if (runtime) await runtime.close();
|
|
234
|
+
else {
|
|
235
|
+
ownedDbs.delete(db);
|
|
236
|
+
db.close();
|
|
237
|
+
}
|
|
238
|
+
throw err;
|
|
239
|
+
}
|
|
150
240
|
}
|
|
151
241
|
var MemorySessionStore = class {
|
|
152
242
|
msgs = [];
|
|
@@ -181,7 +271,7 @@ var MemorySessionStore = class {
|
|
|
181
271
|
tx(fn) {
|
|
182
272
|
return fn();
|
|
183
273
|
}
|
|
184
|
-
reset() {
|
|
274
|
+
reset(inTx) {
|
|
185
275
|
const generation = this.generation++;
|
|
186
276
|
this.archives.push({
|
|
187
277
|
generation,
|
|
@@ -191,6 +281,7 @@ var MemorySessionStore = class {
|
|
|
191
281
|
this.msgs = [];
|
|
192
282
|
this.steps = /* @__PURE__ */ new Map();
|
|
193
283
|
this.status = "new";
|
|
284
|
+
inTx?.();
|
|
194
285
|
return generation;
|
|
195
286
|
}
|
|
196
287
|
unwrap() {}
|
|
@@ -199,6 +290,10 @@ var MemoryRuntime = class {
|
|
|
199
290
|
actors = /* @__PURE__ */ new Map();
|
|
200
291
|
stores = /* @__PURE__ */ new Map();
|
|
201
292
|
agents;
|
|
293
|
+
announceRetry = new AnnouncementRetry((agent, id) => {
|
|
294
|
+
this.session(agent, id).flushAnnouncements();
|
|
295
|
+
});
|
|
296
|
+
closed = false;
|
|
202
297
|
constructor(agents) {
|
|
203
298
|
assertCoreRuntimeVersion("MemoryRuntime");
|
|
204
299
|
this.agents = agents;
|
|
@@ -210,16 +305,23 @@ var MemoryRuntime = class {
|
|
|
210
305
|
const key = `${agent}:${id}`;
|
|
211
306
|
let a = this.actors.get(key);
|
|
212
307
|
if (!a) {
|
|
308
|
+
if (this.closed) throw new Error(`MemoryRuntime is closed: cannot open session "${key}"`);
|
|
213
309
|
const def = this.agents[agent];
|
|
214
310
|
if (!def) throw new Error(`unknown agent: ${agent}`);
|
|
215
311
|
const store = new MemorySessionStore();
|
|
216
312
|
this.stores.set(key, store);
|
|
217
313
|
const model = def.instructions ? withSystem(def.model, def.instructions) : def.model;
|
|
218
|
-
a = new AgentSession(agent, id, store, new InProcEventSink(), model, def.tools, this, def.channelInstructions);
|
|
314
|
+
a = wireAnnouncements(new AgentSession(agent, id, store, new InProcEventSink(), model, def.tools, this, def.channelInstructions), def, this.announceRetry);
|
|
219
315
|
this.actors.set(key, a);
|
|
220
316
|
}
|
|
221
317
|
return a;
|
|
222
318
|
}
|
|
319
|
+
async close() {
|
|
320
|
+
this.closed = true;
|
|
321
|
+
this.announceRetry.stop();
|
|
322
|
+
this.actors.clear();
|
|
323
|
+
this.stores.clear();
|
|
324
|
+
}
|
|
223
325
|
};
|
|
224
326
|
async function createAgentRuntime(agents, opts = {}) {
|
|
225
327
|
const backend = opts.backend ?? "native";
|
package/dist/app.d.ts
CHANGED
package/dist/app.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { discoverAgent } from "./agent-discover.js";
|
|
2
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";
|
|
@@ -115,6 +116,7 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
115
116
|
config.dataLayer?.install();
|
|
116
117
|
let pipelinePromise;
|
|
117
118
|
const getPipeline = () => pipelinePromise ??= buildPipeline();
|
|
119
|
+
let agentRuntime;
|
|
118
120
|
async function buildPipeline() {
|
|
119
121
|
const { maps, css } = await getModuleCss();
|
|
120
122
|
await registerCssModules(maps);
|
|
@@ -138,6 +140,7 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
138
140
|
const backend = agent.runtime.backend === "durable" ? "native" : agent.runtime.backend;
|
|
139
141
|
const model = anthropic({ model: def.model });
|
|
140
142
|
const rt = await createAgentRuntime({ [def.name]: toAgentDef(def, model) }, { backend });
|
|
143
|
+
agentRuntime = rt;
|
|
141
144
|
const mounted = mountAgent(def, rt, {
|
|
142
145
|
chatPath: agent.runtime.chat.path,
|
|
143
146
|
channels: agent.runtime.channels
|
|
@@ -257,6 +260,8 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
257
260
|
return toPipeline();
|
|
258
261
|
},
|
|
259
262
|
async warmup() {
|
|
263
|
+
const problems = routeProblems([await scanRoutes(appDir), hasJuneRoutes ? await scanRoutes(juneRoutesDir) : []], dirname(appDir));
|
|
264
|
+
if (problems) console.error(problems);
|
|
260
265
|
const files = await routeFiles(appDir, { pageConvention: true });
|
|
261
266
|
if (hasJuneRoutes) files.push(...await routeFiles(juneRoutesDir, { pageConvention: true }));
|
|
262
267
|
for (const file of files) await import(pathToFileURL(file).href).catch((err) => {
|
|
@@ -264,7 +269,11 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
264
269
|
});
|
|
265
270
|
},
|
|
266
271
|
routePaths,
|
|
267
|
-
earlyHints: () => config.earlyHints ?? []
|
|
272
|
+
earlyHints: () => config.earlyHints ?? [],
|
|
273
|
+
async close() {
|
|
274
|
+
await pipelinePromise?.catch(() => {});
|
|
275
|
+
await agentRuntime?.close();
|
|
276
|
+
}
|
|
268
277
|
};
|
|
269
278
|
}
|
|
270
279
|
//#endregion
|
package/dist/build.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as scanAppRoutes, o as scanRoutes, t as RouteEntry } from "./route-scan-BMbBzPIB.js";
|
|
2
2
|
import { t as generateContent } from "./content-freeze-CyiW2fRK.js";
|
|
3
|
-
import { i as workerName, n as normalizeBase, r as resolveDeployAdapter, t as freezeConfig } from "./config-freeze-
|
|
4
|
-
import { n as buildManifest } from "./manifest-
|
|
3
|
+
import { i as workerName, n as normalizeBase, r as resolveDeployAdapter, t as freezeConfig } from "./config-freeze-CyXvNqi2.js";
|
|
4
|
+
import { n as buildManifest } from "./manifest-CwTT1zVs.js";
|
|
5
5
|
//#region src/build.d.ts
|
|
6
6
|
export type BuildResult = {
|
|
7
7
|
outFile: string;
|
package/dist/build.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxTransform } from "./tsconfig-jsx.js";
|
|
2
2
|
import { generateAgentModule } from "./agent-compile.js";
|
|
3
3
|
import { findMiddlewareFile } from "./router.js";
|
|
4
|
+
import { scanAppRoutes, scanRoutes } from "./route-scan.js";
|
|
4
5
|
import { resolveBoundary } from "./segment.js";
|
|
5
6
|
import { buildModuleCss, registerCssModules, rolldownCssModulesPlugin } from "./css-modules.js";
|
|
6
7
|
import { bundleClientToFile, findClientEntry } from "./client-bundle.js";
|
|
@@ -12,7 +13,6 @@ import { loadJuneConfig } from "./config-loader.js";
|
|
|
12
13
|
import { freezeConfig, normalizeBase, resolveDeployAdapter, workerName } from "./config-freeze.js";
|
|
13
14
|
import { generateContent } from "./content-freeze.js";
|
|
14
15
|
import { createWorker } from "./worker.js";
|
|
15
|
-
import { scanAppRoutes, scanRoutes } from "./route-scan.js";
|
|
16
16
|
import { buildManifest, importLayout } from "./manifest.js";
|
|
17
17
|
import { buildRsc, findRscRoutes } from "./rsc-build.js";
|
|
18
18
|
import { existsSync, lstatSync } from "node:fs";
|
|
@@ -154,6 +154,8 @@ async function juneBuild(appRoot, options = {}) {
|
|
|
154
154
|
if (r.loading) loadings.push(` ${JSON.stringify(r.path)}: ${loadingId(r.loading)},`);
|
|
155
155
|
});
|
|
156
156
|
const resourceRoutesField = resources.length ? `\n resourceRoutes: [\n${resources.join("\n")}\n ],` : "";
|
|
157
|
+
const generatedPaths = routes.filter((r) => r.generated).map((r) => r.path);
|
|
158
|
+
const generatedRoutesField = generatedPaths.length ? `\n generatedRoutes: ${JSON.stringify(generatedPaths)},` : "";
|
|
157
159
|
const layoutBoundariesField = boundaries.length ? `\n layoutBoundaries: {\n${boundaries.join("\n")}\n },` : "";
|
|
158
160
|
const builtExtraFile = findMiddlewareFile(appDir);
|
|
159
161
|
if (builtExtraFile) imports.push(`import extra from ${JSON.stringify(importPath(genDir, builtExtraFile))};`);
|
|
@@ -208,6 +210,10 @@ export class JuneAgentDO extends DurableObject {
|
|
|
208
210
|
fetch(req: Request): Promise<Response> {
|
|
209
211
|
return this.#agent.fetch(req);
|
|
210
212
|
}
|
|
213
|
+
// Delivers input announcements left undelivered — a failed hook's retry, or the watchdog (#260).
|
|
214
|
+
alarm(): Promise<void> {
|
|
215
|
+
return this.#agent.alarm();
|
|
216
|
+
}
|
|
211
217
|
}
|
|
212
218
|
` : "";
|
|
213
219
|
const entry = `// AUTO-GENERATED by \`june build\` — do not edit. Regenerate: june build .
|
|
@@ -220,7 +226,7 @@ ${statics.join("\n")}
|
|
|
220
226
|
},
|
|
221
227
|
dynamicRoutes: [
|
|
222
228
|
${dynamics.join("\n")}
|
|
223
|
-
],${resourceRoutesField}
|
|
229
|
+
],${resourceRoutesField}${generatedRoutesField}
|
|
224
230
|
layoutChains: {
|
|
225
231
|
${chains.join("\n")}
|
|
226
232
|
},${layoutBoundariesField}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as JuneAdapter } from "./adapter-DcZ-W1-7.js";
|
|
2
|
-
import { n as WorkerManifest } from "./worker-
|
|
2
|
+
import { n as WorkerManifest } from "./worker-2qbQjj2c.js";
|
|
3
3
|
import { JuneConfig } from "@junejs/core/config";
|
|
4
4
|
import { DocumentConfig } from "@junejs/core/document";
|
|
5
5
|
//#region src/config-freeze.d.ts
|
package/dist/config-freeze.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as workerName, n as normalizeBase, r as resolveDeployAdapter, t as freezeConfig } from "./config-freeze-
|
|
1
|
+
import { i as workerName, n as normalizeBase, r as resolveDeployAdapter, t as freezeConfig } from "./config-freeze-CyXvNqi2.js";
|
|
2
2
|
export { freezeConfig, normalizeBase, resolveDeployAdapter, workerName };
|