@nylorun/runtime 0.5.0-beta → 0.6.0-beta
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/CHANGELOG.md +30 -0
- package/README.md +29 -61
- package/dist/config.d.ts +1 -1
- package/dist/configuration.d.ts +3 -0
- package/dist/configuration.js +3 -0
- package/dist/contracts.d.ts +4 -2
- package/dist/core/main.js +40 -0
- package/dist/core/provider.d.ts +12 -0
- package/dist/core/provider.js +60 -0
- package/dist/core/runtime.d.ts +50 -0
- package/dist/core/runtime.js +877 -0
- package/dist/core/store.d.ts +16 -0
- package/dist/core/store.js +101 -0
- package/dist/index.d.ts +6 -11
- package/dist/index.js +4 -6
- package/dist/model/defaults.d.ts +1 -1
- package/dist/model/http-model.d.ts +1 -1
- package/dist/node/local-sessions.js +17 -0
- package/dist/server/host.d.ts +42 -4
- package/dist/server/host.js +69 -3
- package/dist/session/api.d.ts +10 -0
- package/dist/session/api.js +15 -0
- package/dist/session/default.d.ts +5 -0
- package/dist/session/default.js +30 -0
- package/dist/session/handle.d.ts +27 -0
- package/dist/session/handle.js +199 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/sessions/host.d.ts +11 -1
- package/dist/sessions/host.js +75 -7
- package/dist/sessions/store.d.ts +1 -0
- package/dist/sessions/store.js +3 -0
- package/package.json +16 -11
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -100
- package/dist/dev-entry.js +0 -2
- package/dist/dev.d.ts +0 -2
- package/dist/dev.js +0 -126
- package/dist/environment.d.ts +0 -2
- package/dist/environment.js +0 -64
- package/dist/launcher.d.ts +0 -1
- package/dist/launcher.js +0 -33
- package/dist/model/configure.d.ts +0 -12
- package/dist/model/configure.js +0 -155
- /package/dist/{dev-entry.d.ts → core/main.d.ts} +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import type { LiveEvent } from "@nylorun/core/contracts";
|
|
3
|
+
export declare function canonical(value: unknown): string;
|
|
4
|
+
export declare class Store {
|
|
5
|
+
readonly db: DatabaseSync;
|
|
6
|
+
constructor(path: string);
|
|
7
|
+
tx<T>(fn: () => T): T;
|
|
8
|
+
get<T = any>(table: string, id: string): T | undefined;
|
|
9
|
+
all<T = any>(table: string): T[];
|
|
10
|
+
put(table: string, id: string, body: unknown): void;
|
|
11
|
+
event(sessionId: string, turnId: string | null, type: string, payload: unknown): LiveEvent;
|
|
12
|
+
history(sessionId: string, cursor?: string): {
|
|
13
|
+
items: LiveEvent[];
|
|
14
|
+
cursor: string | null;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
export function canonical(value) {
|
|
4
|
+
if (Array.isArray(value))
|
|
5
|
+
return "[" + value.map(canonical).join(",") + "]";
|
|
6
|
+
if (value && typeof value === "object")
|
|
7
|
+
return ("{" +
|
|
8
|
+
Object.keys(value)
|
|
9
|
+
.sort()
|
|
10
|
+
.filter((k) => value[k] !== undefined)
|
|
11
|
+
.map((k) => JSON.stringify(k) + ":" + canonical(value[k]))
|
|
12
|
+
.join(",") +
|
|
13
|
+
"}");
|
|
14
|
+
return JSON.stringify(value) ?? "null";
|
|
15
|
+
}
|
|
16
|
+
export class Store {
|
|
17
|
+
db;
|
|
18
|
+
constructor(path) {
|
|
19
|
+
this.db = new DatabaseSync(path);
|
|
20
|
+
this.db
|
|
21
|
+
.exec(`PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; PRAGMA busy_timeout=5000;
|
|
22
|
+
CREATE TABLE IF NOT EXISTS definitions(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
23
|
+
CREATE TABLE IF NOT EXISTS sessions(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
24
|
+
CREATE TABLE IF NOT EXISTS commands(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
25
|
+
CREATE TABLE IF NOT EXISTS checkpoints(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
26
|
+
CREATE TABLE IF NOT EXISTS effects(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
27
|
+
CREATE TABLE IF NOT EXISTS actions(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
28
|
+
CREATE TABLE IF NOT EXISTS events(sequence INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, body TEXT NOT NULL);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS events_session ON events(session_id, sequence);`);
|
|
30
|
+
}
|
|
31
|
+
tx(fn) {
|
|
32
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
33
|
+
try {
|
|
34
|
+
const result = fn();
|
|
35
|
+
this.db.exec("COMMIT");
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
this.db.exec("ROLLBACK");
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
get(table, id) {
|
|
44
|
+
const row = this.db.prepare(`SELECT body FROM ${table} WHERE id=?`).get(id);
|
|
45
|
+
return row ? JSON.parse(String(row.body)) : undefined;
|
|
46
|
+
}
|
|
47
|
+
all(table) {
|
|
48
|
+
return this.db
|
|
49
|
+
.prepare(`SELECT body FROM ${table}`)
|
|
50
|
+
.all()
|
|
51
|
+
.map((r) => JSON.parse(String(r.body)));
|
|
52
|
+
}
|
|
53
|
+
put(table, id, body) {
|
|
54
|
+
this.db
|
|
55
|
+
.prepare(`INSERT INTO ${table}(id,body) VALUES(?,?) ON CONFLICT(id) DO UPDATE SET body=excluded.body`)
|
|
56
|
+
.run(id, JSON.stringify(body));
|
|
57
|
+
}
|
|
58
|
+
event(sessionId, turnId, type, payload) {
|
|
59
|
+
const base = {
|
|
60
|
+
eventId: randomUUID(),
|
|
61
|
+
sessionId,
|
|
62
|
+
turnId,
|
|
63
|
+
createdAt: new Date().toISOString(),
|
|
64
|
+
type,
|
|
65
|
+
payload,
|
|
66
|
+
};
|
|
67
|
+
const result = this.db
|
|
68
|
+
.prepare("INSERT INTO events(session_id,body) VALUES(?,?)")
|
|
69
|
+
.run(sessionId, "{}");
|
|
70
|
+
const event = {
|
|
71
|
+
...base,
|
|
72
|
+
cursor: Buffer.from(`${sessionId}:${result.lastInsertRowid}`).toString("base64url"),
|
|
73
|
+
};
|
|
74
|
+
this.db
|
|
75
|
+
.prepare("UPDATE events SET body=? WHERE sequence=?")
|
|
76
|
+
.run(JSON.stringify(event), result.lastInsertRowid);
|
|
77
|
+
return event;
|
|
78
|
+
}
|
|
79
|
+
history(sessionId, cursor) {
|
|
80
|
+
let sequence = 0;
|
|
81
|
+
if (cursor) {
|
|
82
|
+
const decoded = Buffer.from(cursor, "base64url").toString();
|
|
83
|
+
const prefix = `${sessionId}:`;
|
|
84
|
+
if (!decoded.startsWith(prefix) ||
|
|
85
|
+
!/^\d+$/.test(decoded.slice(prefix.length)))
|
|
86
|
+
throw new Error("Invalid cursor");
|
|
87
|
+
sequence = Number(decoded.slice(prefix.length));
|
|
88
|
+
}
|
|
89
|
+
const items = this.db
|
|
90
|
+
.prepare("SELECT body FROM events WHERE session_id=? AND sequence>? ORDER BY sequence")
|
|
91
|
+
.all(sessionId, sequence)
|
|
92
|
+
.map((r) => JSON.parse(String(r.body)));
|
|
93
|
+
const last = this.db
|
|
94
|
+
.prepare("SELECT body FROM events WHERE session_id=? ORDER BY sequence DESC LIMIT 1")
|
|
95
|
+
.get(sessionId);
|
|
96
|
+
return {
|
|
97
|
+
items,
|
|
98
|
+
cursor: last ? JSON.parse(String(last.body)).cursor : null,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
export type
|
|
2
|
-
export type
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export type {
|
|
7
|
-
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
|
|
8
|
-
export type { RuntimeMedia, MediaAsset, MediaReference } from "./media.js";
|
|
9
|
-
export { httpModel, type HttpModelOptions, type ModelEnvironment, } from "./model/http-model.js";
|
|
10
|
-
export { agUiEvents } from "./server/ag-ui.js";
|
|
11
|
-
export type { ModelFactoryOptions, ModelPreview } from "./model/defaults.js";
|
|
1
|
+
export { CoreRuntime, createRuntime, startRuntime, type RuntimeOptions } from "./core/runtime.js";
|
|
2
|
+
export { scriptedModel, gatewayModel, type ModelProvider } from "./core/provider.js";
|
|
3
|
+
export { httpModel, type HttpModelOptions, type ModelEnvironment } from "./model/http-model.js";
|
|
4
|
+
export type { RuntimeAgent } from "./contracts.js";
|
|
5
|
+
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes } from "./media.js";
|
|
6
|
+
export type { RuntimeModelAdapter } from "./contracts.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes
|
|
5
|
-
export { httpModel, } from "./model/http-model.js";
|
|
6
|
-
export { agUiEvents } from "./server/ag-ui.js";
|
|
1
|
+
export { CoreRuntime, createRuntime, startRuntime } from "./core/runtime.js";
|
|
2
|
+
export { scriptedModel, gatewayModel } from "./core/provider.js";
|
|
3
|
+
export { httpModel } from "./model/http-model.js";
|
|
4
|
+
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes } from "./media.js";
|
package/dist/model/defaults.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelAdapter } from "@nylorun/
|
|
1
|
+
import type { ModelAdapter } from "@nylorun/core/define";
|
|
2
2
|
import type { ModelFactoryOptions } from "./defaults.js";
|
|
3
3
|
export type ModelEnvironment = Readonly<Record<string, string | undefined>>;
|
|
4
4
|
export interface HttpModelOptions extends ModelFactoryOptions {
|
|
@@ -134,6 +134,23 @@ export function localSessions(options) {
|
|
|
134
134
|
.flatMap((session) => (session ? [sessionSummary(session)] : []))
|
|
135
135
|
.sort((a, b) => b.startedAt - a.startedAt);
|
|
136
136
|
},
|
|
137
|
+
async delete(agentId, sessionId) {
|
|
138
|
+
const operation = operations.then(async () => {
|
|
139
|
+
await check();
|
|
140
|
+
const target = pathFor(agentId, sessionId);
|
|
141
|
+
try {
|
|
142
|
+
await rm(target);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (isMissing(error))
|
|
147
|
+
return false;
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
operations = operation.then(() => { }, () => { });
|
|
152
|
+
return operation;
|
|
153
|
+
},
|
|
137
154
|
async close() {
|
|
138
155
|
await operations;
|
|
139
156
|
if (closed)
|
package/dist/server/host.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Hono, type Context } from "hono";
|
|
2
2
|
import type { JsonValue, RuntimeAgent } from "../contracts.js";
|
|
3
|
+
import type { BuiltAgent, ModelAdapter } from "@nylorun/core/define";
|
|
3
4
|
import { SessionHost } from "../sessions/host.js";
|
|
4
5
|
import type { RuntimeConfig } from "../config.js";
|
|
5
6
|
import type { ModelEnvironment } from "../model/http-model.js";
|
|
7
|
+
import { type OpenSessionOptions, type SessionHandle } from "../session/handle.js";
|
|
6
8
|
export type RuntimeActor = Readonly<{
|
|
7
9
|
id: string;
|
|
8
10
|
context?: Record<string, JsonValue>;
|
|
@@ -14,19 +16,55 @@ export type AgentRouterOptions = Readonly<{
|
|
|
14
16
|
getEnvironment?: (context: Context) => ModelEnvironment | Promise<ModelEnvironment>;
|
|
15
17
|
getRequestMetadata?: (context: Context) => Record<string, JsonValue> | undefined | Promise<Record<string, JsonValue> | undefined>;
|
|
16
18
|
}>;
|
|
17
|
-
|
|
19
|
+
/** 1.0 compatibility: requires an explicit Runtime and returns a Hono app. */
|
|
20
|
+
export type ServeAgentsCompatOptions = AgentRouterOptions & {
|
|
18
21
|
readonly agents: readonly RuntimeAgent[];
|
|
19
22
|
readonly runtime: Runtime;
|
|
20
23
|
};
|
|
24
|
+
/** DX v5.6 local: optional Runtime; returns `{ fetch }`. */
|
|
25
|
+
export type ServeAgentsFetchOptions = AgentRouterOptions & {
|
|
26
|
+
readonly agents?: readonly RuntimeAgent[];
|
|
27
|
+
readonly runtime?: Runtime;
|
|
28
|
+
/** Alias for `getInfo` — keep the name `info` (never `user`). */
|
|
29
|
+
readonly info?: AgentRouterOptions["getInfo"];
|
|
30
|
+
readonly on?: {
|
|
31
|
+
readonly session?: {
|
|
32
|
+
readonly created?: (session: {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly agentId: string;
|
|
35
|
+
}) => void;
|
|
36
|
+
readonly idle?: (session: {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly agentId: string;
|
|
39
|
+
}) => void;
|
|
40
|
+
readonly failed?: (session: {
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly agentId: string;
|
|
43
|
+
}) => void;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
export type ServeAgentsOptions = ServeAgentsCompatOptions;
|
|
48
|
+
export type ServeAgentsFetch = {
|
|
49
|
+
readonly fetch: (request: Request) => Response | Promise<Response>;
|
|
50
|
+
};
|
|
21
51
|
declare const kServe: unique symbol;
|
|
22
52
|
export declare class Runtime {
|
|
23
|
-
|
|
53
|
+
readonly config: RuntimeConfig;
|
|
24
54
|
readonly host: SessionHost;
|
|
25
55
|
private served;
|
|
26
56
|
private closing?;
|
|
27
57
|
constructor(config?: RuntimeConfig);
|
|
28
58
|
close: () => Promise<void>;
|
|
29
|
-
|
|
59
|
+
resolveModel(onPreview?: (preview: import("../model/defaults.js").ModelPreview) => void): ModelAdapter;
|
|
60
|
+
openSession(agent: BuiltAgent<any, any>, options?: OpenSessionOptions): SessionHandle;
|
|
61
|
+
listSessions(agentId: string): Promise<readonly import("../sessions/store.js").SessionSummary[]>;
|
|
62
|
+
getSession(agentId: string, sessionId: string): Promise<import("../sessions/store.js").StoredSession | undefined>;
|
|
63
|
+
deleteSession(agentId: string, sessionId: string): Promise<boolean>;
|
|
64
|
+
[kServe](options: Omit<ServeAgentsCompatOptions, "runtime"> & {
|
|
65
|
+
readonly agents: readonly RuntimeAgent[];
|
|
66
|
+
}): Hono<any>;
|
|
30
67
|
}
|
|
31
|
-
export declare function serveAgents(options:
|
|
68
|
+
export declare function serveAgents(options: ServeAgentsCompatOptions): Hono<any>;
|
|
69
|
+
export declare function serveAgents(options: ServeAgentsFetchOptions): ServeAgentsFetch;
|
|
32
70
|
export {};
|
package/dist/server/host.js
CHANGED
|
@@ -8,6 +8,8 @@ import { memorySessions } from "../sessions/store.js";
|
|
|
8
8
|
import { SessionHost } from "../sessions/host.js";
|
|
9
9
|
import { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, } from "../media.js";
|
|
10
10
|
import { defaultModel, processEnvironment, registerRuntimeLifecycle, } from "../model/defaults.js";
|
|
11
|
+
import { createSessionHandle, } from "../session/handle.js";
|
|
12
|
+
import { installDefaultRuntimeFactory } from "../session/default.js";
|
|
11
13
|
const randomUUID = () => crypto.randomUUID();
|
|
12
14
|
const kServe = Symbol("serve");
|
|
13
15
|
export class Runtime {
|
|
@@ -21,6 +23,26 @@ export class Runtime {
|
|
|
21
23
|
registerRuntimeLifecycle(this.close);
|
|
22
24
|
}
|
|
23
25
|
close = () => (this.closing ??= this.host.close());
|
|
26
|
+
resolveModel(onPreview) {
|
|
27
|
+
return (this.config.onModelCall ??
|
|
28
|
+
(this.config.createModel ?? defaultModel)({
|
|
29
|
+
environment: this.config.environment ?? processEnvironment(),
|
|
30
|
+
onPreview,
|
|
31
|
+
media: this.config.media,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
openSession(agent, options) {
|
|
35
|
+
return createSessionHandle(this, agent, options);
|
|
36
|
+
}
|
|
37
|
+
async listSessions(agentId) {
|
|
38
|
+
return this.host.list(agentId);
|
|
39
|
+
}
|
|
40
|
+
async getSession(agentId, sessionId) {
|
|
41
|
+
return this.host.read(agentId, sessionId);
|
|
42
|
+
}
|
|
43
|
+
async deleteSession(agentId, sessionId) {
|
|
44
|
+
return this.host.delete(agentId, sessionId);
|
|
45
|
+
}
|
|
24
46
|
[kServe](options) {
|
|
25
47
|
if (this.served)
|
|
26
48
|
throw new Error("A Runtime may only be served once.");
|
|
@@ -141,8 +163,20 @@ export class Runtime {
|
|
|
141
163
|
id: document.id,
|
|
142
164
|
state: document.status,
|
|
143
165
|
pending_interaction: document.state?.plan?.calls.find((call) => call.status === "interaction")?.interaction,
|
|
166
|
+
pending_waits: document.state?.plan?.calls
|
|
167
|
+
.filter((call) => (call.status === "interaction" || call.status === "deferred") &&
|
|
168
|
+
call.wait)
|
|
169
|
+
.map((call) => call.wait),
|
|
144
170
|
});
|
|
145
171
|
});
|
|
172
|
+
app.delete("/:agentId/v1/sessions/:session", async (context) => {
|
|
173
|
+
const agent = agentFor(context);
|
|
174
|
+
const sessionId = context.req.param("session");
|
|
175
|
+
const deleted = await this.host.delete(agent.id, sessionId);
|
|
176
|
+
if (!deleted)
|
|
177
|
+
return context.json({ error: "unknown session" }, 404);
|
|
178
|
+
return context.json({ session_id: sessionId, deleted: true });
|
|
179
|
+
});
|
|
146
180
|
app.post("/:agentId/v1/sessions/:session", async (context) => {
|
|
147
181
|
const agent = agentFor(context), sessionId = context.req.param("session");
|
|
148
182
|
const payload = await context.req.json();
|
|
@@ -161,6 +195,20 @@ export class Runtime {
|
|
|
161
195
|
}
|
|
162
196
|
if (payload.settlement)
|
|
163
197
|
input = { kind: "settle", ...payload.settlement };
|
|
198
|
+
else if (payload.action === "wait-resolve" ||
|
|
199
|
+
payload.waitResolve ||
|
|
200
|
+
(interaction?.kind === "wait-resolve" &&
|
|
201
|
+
typeof interaction.waitId === "string")) {
|
|
202
|
+
const wait = payload.waitResolve ??
|
|
203
|
+
(interaction?.kind === "wait-resolve" ? interaction : payload);
|
|
204
|
+
if (typeof wait.waitId !== "string")
|
|
205
|
+
return context.json({ error: "wait-resolve requires waitId" }, 400);
|
|
206
|
+
input = {
|
|
207
|
+
kind: "wait-resolve",
|
|
208
|
+
waitId: wait.waitId,
|
|
209
|
+
...("value" in wait ? { value: wait.value } : {}),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
164
212
|
else if (interaction?.kind === "approval" &&
|
|
165
213
|
typeof interaction.id === "string" &&
|
|
166
214
|
typeof interaction.approved === "boolean")
|
|
@@ -178,7 +226,9 @@ export class Runtime {
|
|
|
178
226
|
value: interaction.value,
|
|
179
227
|
};
|
|
180
228
|
else
|
|
181
|
-
return context.json({
|
|
229
|
+
return context.json({
|
|
230
|
+
error: "expected a correlated interaction, settlement, or wait-resolve",
|
|
231
|
+
}, 400);
|
|
182
232
|
const document = await this.host.read(agent.id, sessionId);
|
|
183
233
|
if (!document || document.status !== "waiting")
|
|
184
234
|
return context.json({ error: "interaction is no longer pending" }, 409);
|
|
@@ -280,9 +330,25 @@ export class Runtime {
|
|
|
280
330
|
}
|
|
281
331
|
}
|
|
282
332
|
export function serveAgents(options) {
|
|
283
|
-
|
|
284
|
-
|
|
333
|
+
if ("runtime" in options && options.runtime) {
|
|
334
|
+
const { runtime, ...rest } = options;
|
|
335
|
+
return runtime[kServe]({
|
|
336
|
+
...rest,
|
|
337
|
+
agents: options.agents ?? [],
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const runtime = new Runtime({});
|
|
341
|
+
const getInfo = options.getInfo ?? ("info" in options ? options.info : undefined);
|
|
342
|
+
const app = runtime[kServe]({
|
|
343
|
+
...options,
|
|
344
|
+
agents: options.agents ?? [],
|
|
345
|
+
...(getInfo ? { getInfo } : {}),
|
|
346
|
+
});
|
|
347
|
+
return {
|
|
348
|
+
fetch: (request) => app.fetch(request),
|
|
349
|
+
};
|
|
285
350
|
}
|
|
351
|
+
installDefaultRuntimeFactory(() => new Runtime({}));
|
|
286
352
|
/** Workers-style provider bindings on context.env; ignore Hono Node stream slots. */
|
|
287
353
|
function bindingsEnvironment(context) {
|
|
288
354
|
const env = context.env;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BuiltAgent } from "@nylorun/core/define";
|
|
2
|
+
import { setDefaultRuntime } from "./default.js";
|
|
3
|
+
import type { OpenSessionOptions, SessionHandle } from "./handle.js";
|
|
4
|
+
export { setDefaultRuntime };
|
|
5
|
+
export type { OpenSessionOptions, SessionHandle };
|
|
6
|
+
/** Open-or-create a local session with an agent (bound to the default Runtime). */
|
|
7
|
+
export declare function openSession(agent: BuiltAgent<any, any>, options?: OpenSessionOptions): SessionHandle;
|
|
8
|
+
export declare function listSessions(agentId: string): Promise<readonly import("../sessions/store.js").SessionSummary[]>;
|
|
9
|
+
export declare function getSession(agentId: string, sessionId: string): Promise<import("../sessions/store.js").StoredSession | undefined>;
|
|
10
|
+
export declare function deleteSession(agentId: string, sessionId: string): Promise<boolean>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getDefaultRuntime, setDefaultRuntime, } from "./default.js";
|
|
2
|
+
export { setDefaultRuntime };
|
|
3
|
+
/** Open-or-create a local session with an agent (bound to the default Runtime). */
|
|
4
|
+
export function openSession(agent, options) {
|
|
5
|
+
return getDefaultRuntime().openSession(agent, options);
|
|
6
|
+
}
|
|
7
|
+
export async function listSessions(agentId) {
|
|
8
|
+
return getDefaultRuntime().listSessions(agentId);
|
|
9
|
+
}
|
|
10
|
+
export async function getSession(agentId, sessionId) {
|
|
11
|
+
return getDefaultRuntime().getSession(agentId, sessionId);
|
|
12
|
+
}
|
|
13
|
+
export async function deleteSession(agentId, sessionId) {
|
|
14
|
+
return getDefaultRuntime().deleteSession(agentId, sessionId);
|
|
15
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Runtime } from "../server/host.js";
|
|
2
|
+
/** Wired from `server/host.ts` after `Runtime` is defined (avoids init cycles). */
|
|
3
|
+
export declare function installDefaultRuntimeFactory(factory: () => Runtime): void;
|
|
4
|
+
export declare function setDefaultRuntime(runtime: Runtime | undefined): void;
|
|
5
|
+
export declare function getDefaultRuntime(): Runtime;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
let defaultRuntime;
|
|
2
|
+
let bannerShown = false;
|
|
3
|
+
let createRuntime;
|
|
4
|
+
/** Wired from `server/host.ts` after `Runtime` is defined (avoids init cycles). */
|
|
5
|
+
export function installDefaultRuntimeFactory(factory) {
|
|
6
|
+
createRuntime = factory;
|
|
7
|
+
}
|
|
8
|
+
export function setDefaultRuntime(runtime) {
|
|
9
|
+
defaultRuntime = runtime;
|
|
10
|
+
}
|
|
11
|
+
export function getDefaultRuntime() {
|
|
12
|
+
if (defaultRuntime)
|
|
13
|
+
return defaultRuntime;
|
|
14
|
+
if (!createRuntime)
|
|
15
|
+
throw new Error("Default Runtime factory is not installed");
|
|
16
|
+
defaultRuntime = createRuntime();
|
|
17
|
+
maybeBanner();
|
|
18
|
+
return defaultRuntime;
|
|
19
|
+
}
|
|
20
|
+
function maybeBanner() {
|
|
21
|
+
if (bannerShown || typeof process === "undefined")
|
|
22
|
+
return;
|
|
23
|
+
if (process.env.NYLORUN_QUIET === "1")
|
|
24
|
+
return;
|
|
25
|
+
bannerShown = true;
|
|
26
|
+
const model = process.env.MODEL_PROVIDER && process.env.MODEL
|
|
27
|
+
? `${process.env.MODEL_PROVIDER}/${process.env.MODEL}`
|
|
28
|
+
: "unset";
|
|
29
|
+
console.info(`nylorun ▸ local runtime · loop runs in this process · sessions in memory · model ${model} (your key)`);
|
|
30
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BuiltAgent, JsonObject, JsonValue, ModelAdapter } from "@nylorun/core/define";
|
|
2
|
+
import type { Session } from "@nylorun/harness";
|
|
3
|
+
import type { RuntimeConfig } from "../config.js";
|
|
4
|
+
import type { SessionHost } from "../sessions/host.js";
|
|
5
|
+
export type OpenSessionOptions = {
|
|
6
|
+
readonly id?: string;
|
|
7
|
+
/** Trusted host bag — keep the name `info` (never `user`). */
|
|
8
|
+
readonly info?: unknown;
|
|
9
|
+
/** Initial durable session memory (`ExecutionState.state`). */
|
|
10
|
+
readonly state?: JsonObject;
|
|
11
|
+
};
|
|
12
|
+
export type SessionHandle<Output = unknown> = Session<{
|
|
13
|
+
readonly id: string;
|
|
14
|
+
}, Output> & {
|
|
15
|
+
readonly agentId: string;
|
|
16
|
+
};
|
|
17
|
+
export type SessionRuntime = {
|
|
18
|
+
readonly host: SessionHost;
|
|
19
|
+
readonly config: RuntimeConfig;
|
|
20
|
+
resolveModel(): ModelAdapter;
|
|
21
|
+
};
|
|
22
|
+
export declare function createSessionHandle(runtime: SessionRuntime, agent: BuiltAgent<any, any>, options?: OpenSessionOptions): SessionHandle;
|
|
23
|
+
/** Resolve a wait by harness waitId (H6). */
|
|
24
|
+
export declare function resolveWait(runtime: SessionRuntime, agent: BuiltAgent<any, any>, sessionId: string, waitId: string, value?: JsonValue, options?: {
|
|
25
|
+
readonly info?: unknown;
|
|
26
|
+
readonly onModelCall?: ModelAdapter;
|
|
27
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
export function createSessionHandle(runtime, agent, options = {}) {
|
|
2
|
+
const id = options.id ?? crypto.randomUUID();
|
|
3
|
+
let seedState = options.state;
|
|
4
|
+
const info = options.info;
|
|
5
|
+
const submitOptions = () => {
|
|
6
|
+
const sessionState = seedState;
|
|
7
|
+
seedState = undefined;
|
|
8
|
+
return {
|
|
9
|
+
info: info === undefined
|
|
10
|
+
? { sessionId: id }
|
|
11
|
+
: { ...info, sessionId: id },
|
|
12
|
+
onModelCall: runtime.resolveModel(),
|
|
13
|
+
...(sessionState ? { sessionState } : {}),
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
const handle = {
|
|
17
|
+
id,
|
|
18
|
+
agentId: agent.id,
|
|
19
|
+
async input(message) {
|
|
20
|
+
try {
|
|
21
|
+
const result = await runtime.host.submit(agent, id, message, submitOptions());
|
|
22
|
+
const cursor = String((await runtime.host.read(agent.id, id))?.events.at(-1)?.seq ?? 0);
|
|
23
|
+
return {
|
|
24
|
+
status: "accepted",
|
|
25
|
+
turnId: result.state.plan?.turnId ?? result.state.executionId,
|
|
26
|
+
cursor,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
status: "rejected",
|
|
32
|
+
error: {
|
|
33
|
+
code: error && typeof error === "object" && "code" in error
|
|
34
|
+
? String(error.code)
|
|
35
|
+
: "runtime.input-rejected",
|
|
36
|
+
message: error instanceof Error ? error.message : String(error),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
async *stream(opts) {
|
|
42
|
+
let after = opts?.after ? Number(opts.after) : 0;
|
|
43
|
+
if (!Number.isFinite(after) || after < 0)
|
|
44
|
+
after = 0;
|
|
45
|
+
const queue = [];
|
|
46
|
+
let notify;
|
|
47
|
+
const wake = () => {
|
|
48
|
+
notify?.();
|
|
49
|
+
notify = undefined;
|
|
50
|
+
};
|
|
51
|
+
const unsubscribe = runtime.host.subscribe(agent.id, id, (event) => {
|
|
52
|
+
queue.push(event);
|
|
53
|
+
wake();
|
|
54
|
+
});
|
|
55
|
+
try {
|
|
56
|
+
const existing = await runtime.host.read(agent.id, id);
|
|
57
|
+
for (const event of existing?.events ?? [])
|
|
58
|
+
if (event.seq > after)
|
|
59
|
+
queue.push(event);
|
|
60
|
+
for (;;) {
|
|
61
|
+
while (queue.length) {
|
|
62
|
+
const event = queue.shift();
|
|
63
|
+
if (event.seq <= after)
|
|
64
|
+
continue;
|
|
65
|
+
after = event.seq;
|
|
66
|
+
const mapped = mapEvent(event, id);
|
|
67
|
+
if (mapped)
|
|
68
|
+
yield mapped;
|
|
69
|
+
}
|
|
70
|
+
await new Promise((resolve) => {
|
|
71
|
+
notify = resolve;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
unsubscribe();
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
async history() {
|
|
80
|
+
const document = await runtime.host.read(agent.id, id);
|
|
81
|
+
return (document?.events ?? [])
|
|
82
|
+
.filter((event) => ["final", "interaction.required", "error", "cancelled"].includes(event.type))
|
|
83
|
+
.map((event) => event.payload);
|
|
84
|
+
},
|
|
85
|
+
async approve(interactionId, approved) {
|
|
86
|
+
await runtime.host.submit(agent, id, { kind: "approve", interactionId, approved }, submitOptions());
|
|
87
|
+
},
|
|
88
|
+
async respond(interactionId, value) {
|
|
89
|
+
await runtime.host.submit(agent, id, { kind: "respond", interactionId, value }, submitOptions());
|
|
90
|
+
},
|
|
91
|
+
async cancel() {
|
|
92
|
+
await runtime.host.cancel(agent, id);
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
return handle;
|
|
96
|
+
}
|
|
97
|
+
/** Resolve a wait by harness waitId (H6). */
|
|
98
|
+
export async function resolveWait(runtime, agent, sessionId, waitId, value, options) {
|
|
99
|
+
const input = {
|
|
100
|
+
kind: "wait-resolve",
|
|
101
|
+
waitId,
|
|
102
|
+
...(value === undefined ? {} : { value }),
|
|
103
|
+
};
|
|
104
|
+
await runtime.host.submit(agent, sessionId, input, {
|
|
105
|
+
info: options?.info,
|
|
106
|
+
onModelCall: options?.onModelCall ?? runtime.resolveModel(),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function mapEvent(event, sessionId) {
|
|
110
|
+
const turnId = typeof event.payload.turnId === "string"
|
|
111
|
+
? event.payload.turnId
|
|
112
|
+
: typeof event.payload.executionId === "string"
|
|
113
|
+
? event.payload.executionId
|
|
114
|
+
: sessionId;
|
|
115
|
+
const cursor = String(event.seq);
|
|
116
|
+
const at = event.ts;
|
|
117
|
+
switch (event.type) {
|
|
118
|
+
case "session.run.started":
|
|
119
|
+
return { type: "turn.started", cursor, at, sessionId, turnId };
|
|
120
|
+
case "tool.started":
|
|
121
|
+
return {
|
|
122
|
+
type: "tool.started",
|
|
123
|
+
tool: String(event.payload.toolName ?? "tool"),
|
|
124
|
+
sessionId,
|
|
125
|
+
turnId,
|
|
126
|
+
};
|
|
127
|
+
case "tool.progress":
|
|
128
|
+
return {
|
|
129
|
+
type: "tool.progress",
|
|
130
|
+
tool: String(event.payload.toolName ?? "tool"),
|
|
131
|
+
sessionId,
|
|
132
|
+
turnId,
|
|
133
|
+
};
|
|
134
|
+
case "tool.completed":
|
|
135
|
+
return {
|
|
136
|
+
type: event.payload.outcome === "failed" ? "tool.failed" : "tool.completed",
|
|
137
|
+
tool: String(event.payload.toolName ?? "tool"),
|
|
138
|
+
sessionId,
|
|
139
|
+
turnId,
|
|
140
|
+
};
|
|
141
|
+
case "interaction.required": {
|
|
142
|
+
const interaction = event.payload.interaction;
|
|
143
|
+
return {
|
|
144
|
+
type: "interaction.required",
|
|
145
|
+
id: String(interaction?.id ?? ""),
|
|
146
|
+
prompt: String(interaction?.prompt ?? ""),
|
|
147
|
+
...(interaction?.metadata ? { options: interaction.metadata } : {}),
|
|
148
|
+
sessionId,
|
|
149
|
+
turnId,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
case "final":
|
|
153
|
+
return {
|
|
154
|
+
type: "turn.settled",
|
|
155
|
+
result: {
|
|
156
|
+
status: "completed",
|
|
157
|
+
output: event.payload.output,
|
|
158
|
+
},
|
|
159
|
+
sessionId,
|
|
160
|
+
turnId,
|
|
161
|
+
};
|
|
162
|
+
case "error":
|
|
163
|
+
return {
|
|
164
|
+
type: "turn.settled",
|
|
165
|
+
result: {
|
|
166
|
+
status: "failed",
|
|
167
|
+
error: {
|
|
168
|
+
code: String(event.payload.code ?? "execution.failed"),
|
|
169
|
+
message: String(event.payload.message ?? "failed"),
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
sessionId,
|
|
173
|
+
turnId,
|
|
174
|
+
};
|
|
175
|
+
case "cancelled":
|
|
176
|
+
return {
|
|
177
|
+
type: "turn.settled",
|
|
178
|
+
result: { status: "cancelled" },
|
|
179
|
+
sessionId,
|
|
180
|
+
turnId,
|
|
181
|
+
};
|
|
182
|
+
default:
|
|
183
|
+
if (event.type === "model.requested" || event.type === "model.completed")
|
|
184
|
+
return {
|
|
185
|
+
type: "status",
|
|
186
|
+
status: "thinking",
|
|
187
|
+
sessionId,
|
|
188
|
+
turnId,
|
|
189
|
+
};
|
|
190
|
+
if (event.type.startsWith("tool."))
|
|
191
|
+
return {
|
|
192
|
+
type: "status",
|
|
193
|
+
status: "calling_tool",
|
|
194
|
+
sessionId,
|
|
195
|
+
turnId,
|
|
196
|
+
};
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
}
|