@junejs/server 0.1.2-dev.9 → 0.1.2
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 +3 -39
- package/dist/agent-durable.js +26 -297
- package/dist/agent-native.d.ts +0 -2
- package/dist/agent-native.js +10 -28
- package/dist/worker.js +1 -1
- package/package.json +3 -3
- package/src/agent-durable.ts +25 -438
- package/src/agent-native.ts +13 -27
- package/src/worker.ts +5 -2
- package/dist/core-version.d.ts +0 -5
- package/dist/core-version.js +0 -12
- package/src/core-version.ts +0 -34
package/src/agent-native.ts
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
import {
|
|
11
11
|
AgentSession,
|
|
12
12
|
withSystem,
|
|
13
|
-
type
|
|
14
|
-
type TurnEvent,
|
|
13
|
+
type Broadcaster,
|
|
15
14
|
type Model,
|
|
16
15
|
type Msg,
|
|
17
16
|
type Runtime,
|
|
@@ -20,7 +19,6 @@ import {
|
|
|
20
19
|
} from "@junejs/core/agent-runtime";
|
|
21
20
|
import { channelFetch, type AgentDefinition, type ChannelContext } from "@junejs/core/agent-config";
|
|
22
21
|
import { openLocalSqliteSync, type SyncSqlite } from "./sqlite-driver";
|
|
23
|
-
import { assertCoreRuntimeVersion } from "./core-version";
|
|
24
22
|
|
|
25
23
|
function initSchema(db: SyncSqlite) {
|
|
26
24
|
db.exec(`CREATE TABLE IF NOT EXISTS agent_sessions (session_id TEXT PRIMARY KEY, status TEXT)`);
|
|
@@ -47,8 +45,8 @@ class SqliteSessionStore implements SessionStore {
|
|
|
47
45
|
return (this.db.query("SELECT body FROM agent_messages WHERE session_id = ? ORDER BY seq").all(this.sid) as { body: string }[])
|
|
48
46
|
.map((r) => JSON.parse(r.body));
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
return this.messages().some((m) =>
|
|
48
|
+
hasUserTurn(turnId: string): boolean {
|
|
49
|
+
return this.messages().some((m) => m.role === "user" && m.turnId === turnId);
|
|
52
50
|
}
|
|
53
51
|
getStep(id: string): unknown | undefined {
|
|
54
52
|
const r = this.db.query("SELECT output FROM agent_steps WHERE session_id = ? AND id = ?").get(this.sid, id) as { output: string } | undefined;
|
|
@@ -57,9 +55,6 @@ class SqliteSessionStore implements SessionStore {
|
|
|
57
55
|
putStep(id: string, output: unknown) {
|
|
58
56
|
this.db.query("INSERT INTO agent_steps (session_id, id, output) VALUES (?, ?, ?)").run(this.sid, id, JSON.stringify(output));
|
|
59
57
|
}
|
|
60
|
-
delStep(id: string) {
|
|
61
|
-
this.db.query("DELETE FROM agent_steps WHERE session_id = ? AND id = ?").run(this.sid, id);
|
|
62
|
-
}
|
|
63
58
|
getStatus(): string {
|
|
64
59
|
return (this.db.query("SELECT status FROM agent_sessions WHERE session_id = ?").get(this.sid) as { status: string } | undefined)?.status ?? "new";
|
|
65
60
|
}
|
|
@@ -82,15 +77,15 @@ class SqliteSessionStore implements SessionStore {
|
|
|
82
77
|
unwrap<H = unknown>(): H { return this.db as unknown as H; }
|
|
83
78
|
}
|
|
84
79
|
|
|
85
|
-
class
|
|
86
|
-
private subs = new Set<(
|
|
87
|
-
|
|
88
|
-
subscribe(cb: (
|
|
80
|
+
class InProcBroadcaster implements Broadcaster {
|
|
81
|
+
private subs = new Set<(t: string) => void>();
|
|
82
|
+
publish(turnId: string) { this.subs.forEach((cb) => { try { cb(turnId); } catch { /* a bad subscriber must not break publish */ } }); }
|
|
83
|
+
subscribe(cb: (t: string) => void): () => void { this.subs.add(cb); return () => this.subs.delete(cb); }
|
|
89
84
|
}
|
|
90
85
|
|
|
91
86
|
// `instructions` (the agent's system prompt) is injected into the model per turn
|
|
92
87
|
// by the runtime (withSystem) — single-sourced on the def, not baked into `model`.
|
|
93
|
-
export type AgentDef = { model: Model; tools: Tool[]; instructions?: string
|
|
88
|
+
export type AgentDef = { model: Model; tools: Tool[]; instructions?: string };
|
|
94
89
|
|
|
95
90
|
// The native Runtime: a registry of agent definitions over one SQLite handle,
|
|
96
91
|
// handing out (and memoizing) an AgentSession actor per (agent, id).
|
|
@@ -100,7 +95,6 @@ export class NativeRuntime implements Runtime {
|
|
|
100
95
|
private readonly db: SyncSqlite;
|
|
101
96
|
|
|
102
97
|
constructor(agents: Record<string, AgentDef>, db: SyncSqlite) {
|
|
103
|
-
assertCoreRuntimeVersion("NativeRuntime"); // #94: fail power-on, not mid-turn
|
|
104
98
|
this.agents = agents;
|
|
105
99
|
this.db = db;
|
|
106
100
|
initSchema(db);
|
|
@@ -113,7 +107,7 @@ export class NativeRuntime implements Runtime {
|
|
|
113
107
|
const def = this.agents[agent];
|
|
114
108
|
if (!def) throw new Error(`unknown agent: ${agent}`);
|
|
115
109
|
const model = def.instructions ? withSystem(def.model, def.instructions) : def.model;
|
|
116
|
-
a = new AgentSession(agent, id, new SqliteSessionStore(this.db, key), new
|
|
110
|
+
a = new AgentSession(agent, id, new SqliteSessionStore(this.db, key), new InProcBroadcaster(), model, def.tools, this);
|
|
117
111
|
this.actors.set(key, a);
|
|
118
112
|
}
|
|
119
113
|
return a;
|
|
@@ -141,10 +135,9 @@ class MemorySessionStore implements SessionStore {
|
|
|
141
135
|
private status = "new";
|
|
142
136
|
appendMessage(m: Msg) { this.msgs.push(m); }
|
|
143
137
|
messages(): Msg[] { return this.msgs.slice(); }
|
|
144
|
-
|
|
138
|
+
hasUserTurn(turnId: string): boolean { return this.msgs.some((m) => m.role === "user" && m.turnId === turnId); }
|
|
145
139
|
getStep(id: string): unknown | undefined { return this.steps.has(id) ? this.steps.get(id) : undefined; }
|
|
146
140
|
putStep(id: string, output: unknown) { this.steps.set(id, output); }
|
|
147
|
-
delStep(id: string) { this.steps.delete(id); }
|
|
148
141
|
getStatus(): string { return this.status; }
|
|
149
142
|
setStatus(s: string) { this.status = s; }
|
|
150
143
|
tx<T>(fn: () => T): T { return fn(); } // no rollback: an in-memory store is not a durability tier
|
|
@@ -156,7 +149,6 @@ export class MemoryRuntime implements Runtime {
|
|
|
156
149
|
private stores = new Map<string, MemorySessionStore>();
|
|
157
150
|
private readonly agents: Record<string, AgentDef>;
|
|
158
151
|
constructor(agents: Record<string, AgentDef>) {
|
|
159
|
-
assertCoreRuntimeVersion("MemoryRuntime"); // #94: fail power-on, not mid-turn
|
|
160
152
|
this.agents = agents;
|
|
161
153
|
}
|
|
162
154
|
session(agent: string, id: string): AgentSession {
|
|
@@ -167,7 +159,7 @@ export class MemoryRuntime implements Runtime {
|
|
|
167
159
|
if (!def) throw new Error(`unknown agent: ${agent}`);
|
|
168
160
|
const store = new MemorySessionStore();
|
|
169
161
|
this.stores.set(key, store);
|
|
170
|
-
a = new AgentSession(agent, id, store, new
|
|
162
|
+
a = new AgentSession(agent, id, store, new InProcBroadcaster(), def.model, def.tools, this);
|
|
171
163
|
this.actors.set(key, a);
|
|
172
164
|
}
|
|
173
165
|
return a;
|
|
@@ -203,7 +195,7 @@ export async function createAgentRuntime(
|
|
|
203
195
|
export function mountAgent(
|
|
204
196
|
agent: AgentDefinition,
|
|
205
197
|
runtime: Runtime,
|
|
206
|
-
opts: { chatPath?: string; channels?: boolean
|
|
198
|
+
opts: { chatPath?: string; channels?: boolean } = {},
|
|
207
199
|
): {
|
|
208
200
|
surface: (req: Request) => Promise<Response | null>;
|
|
209
201
|
fetch: (req: Request) => Promise<Response | null>;
|
|
@@ -214,14 +206,8 @@ export function mountAgent(
|
|
|
214
206
|
const channelsOn = opts.channels ?? true;
|
|
215
207
|
const ctx: ChannelContext = {
|
|
216
208
|
agent,
|
|
217
|
-
services: opts.services, // same DI bag reachable from channel hooks (parity with durableChannelSurface)
|
|
218
209
|
run: (message, o) =>
|
|
219
|
-
runtime.session(agent.name, o?.session ?? "default").turn({ turnId: o?.turnId, userText: message
|
|
220
|
-
// FIRE-AND-FORGET (#77): start() without awaiting the result. Native has no waitUntil
|
|
221
|
-
// ceiling (Node keeps floating promises alive), but the seam is the same so a channel
|
|
222
|
-
// written against ctx.runDetached behaves identically on both targets.
|
|
223
|
-
runDetached: async (message, o) =>
|
|
224
|
-
runtime.session(agent.name, o?.session ?? "default").start({ turnId: o?.turnId, userText: message, event: o?.event, trigger: o?.trigger }),
|
|
210
|
+
runtime.session(agent.name, o?.session ?? "default").turn({ turnId: o?.turnId, userText: message }),
|
|
225
211
|
};
|
|
226
212
|
const channels = channelFetch(agent, ctx);
|
|
227
213
|
const surface = async (req: Request): Promise<Response | null> => {
|
package/src/worker.ts
CHANGED
|
@@ -274,8 +274,11 @@ export function withAssets(
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
// 2. Static assets (prerendered HTML/.md/.json, /client.js, hashed CSS)
|
|
277
|
-
// served direct.
|
|
278
|
-
|
|
277
|
+
// served direct — for reads only. Assets never answer a write, and on
|
|
278
|
+
// workerd handing the incoming request to ASSETS.fetch() consumes its
|
|
279
|
+
// body: a POST that 404s here would reach the pipeline with a used stream
|
|
280
|
+
// (POST /mcp → -32700 Parse error, an action's input gone).
|
|
281
|
+
if (assets && (request.method === "GET" || request.method === "HEAD")) {
|
|
279
282
|
const a = await assets.fetch(request);
|
|
280
283
|
if (a.status !== 404) {
|
|
281
284
|
const ct = a.headers.get("content-type") ?? "";
|
package/dist/core-version.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
//#region src/core-version.d.ts
|
|
2
|
-
declare function assertCoreRuntimeVersion(surface: string): void;
|
|
3
|
-
declare function checkCoreRuntimeVersion(surface: string, actual: number | undefined, expected?: number): void;
|
|
4
|
-
//#endregion
|
|
5
|
-
export { assertCoreRuntimeVersion, checkCoreRuntimeVersion };
|
package/dist/core-version.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import * as coreRuntime from "@junejs/core/agent-runtime";
|
|
2
|
-
//#region src/core-version.ts
|
|
3
|
-
const EXPECTED_CORE_RUNTIME_API = 1;
|
|
4
|
-
function assertCoreRuntimeVersion(surface) {
|
|
5
|
-
checkCoreRuntimeVersion(surface, coreRuntime.RUNTIME_API_VERSION);
|
|
6
|
-
}
|
|
7
|
-
function checkCoreRuntimeVersion(surface, actual, expected = EXPECTED_CORE_RUNTIME_API) {
|
|
8
|
-
if (actual === expected) return;
|
|
9
|
-
throw new Error(`${surface}: @junejs/server is built against @junejs/core runtime API v${expected}, but the @junejs/core it resolved provides ${actual === void 0 ? "no RUNTIME_API_VERSION (a pre-0.1.1-dev.16 core)" : `v${actual}`} — two copies of @junejs/core are likely installed (one nested under @junejs/server). Align the app's @junejs/core with @junejs/server's peer range, then reinstall/dedupe so one copy remains.`);
|
|
10
|
-
}
|
|
11
|
-
//#endregion
|
|
12
|
-
export { assertCoreRuntimeVersion, checkCoreRuntimeVersion };
|
package/src/core-version.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// core-version.ts — the #94 belt-and-braces: fail at POWER-ON when the @junejs/core
|
|
2
|
-
// copy this server resolved is not the one it was built for.
|
|
3
|
-
//
|
|
4
|
-
// The primary fix is @junejs/core being a peerDependency (the app's single resolution
|
|
5
|
-
// always wins), but a nested second copy can still appear (npm legacy peer handling,
|
|
6
|
-
// a stale lockfile, a transitive pin). When it does, the failure without this check
|
|
7
|
-
// is far from the cause and mid-turn ("sink.emit is not a function", first seen in
|
|
8
|
-
// crisp-agent production); with it, construction throws with both versions named.
|
|
9
|
-
//
|
|
10
|
-
// Namespace import + optional read on purpose: a PRE-#94 core has no
|
|
11
|
-
// RUNTIME_API_VERSION export, and a named import of a missing export would die at
|
|
12
|
-
// module link time with an unhelpful SyntaxError — exactly the confusion this
|
|
13
|
-
// module exists to remove.
|
|
14
|
-
import * as coreRuntime from "@junejs/core/agent-runtime";
|
|
15
|
-
|
|
16
|
-
// The contract number this server tree is built against. Bump in lockstep with
|
|
17
|
-
// core's RUNTIME_API_VERSION whenever the server↔core runtime contract changes.
|
|
18
|
-
const EXPECTED_CORE_RUNTIME_API = 1;
|
|
19
|
-
|
|
20
|
-
export function assertCoreRuntimeVersion(surface: string): void {
|
|
21
|
-
checkCoreRuntimeVersion(surface, (coreRuntime as { RUNTIME_API_VERSION?: number }).RUNTIME_API_VERSION);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// The pure check, split out so the mismatch paths are unit-testable (the ambient
|
|
25
|
-
// read above can only ever see the workspace's own — matching — core).
|
|
26
|
-
export function checkCoreRuntimeVersion(surface: string, actual: number | undefined, expected: number = EXPECTED_CORE_RUNTIME_API): void {
|
|
27
|
-
if (actual === expected) return;
|
|
28
|
-
throw new Error(
|
|
29
|
-
`${surface}: @junejs/server is built against @junejs/core runtime API v${expected}, ` +
|
|
30
|
-
`but the @junejs/core it resolved provides ${actual === undefined ? "no RUNTIME_API_VERSION (a pre-0.1.1-dev.16 core)" : `v${actual}`} — ` +
|
|
31
|
-
`two copies of @junejs/core are likely installed (one nested under @junejs/server). ` +
|
|
32
|
-
`Align the app's @junejs/core with @junejs/server's peer range, then reinstall/dedupe so one copy remains.`,
|
|
33
|
-
);
|
|
34
|
-
}
|