@nylorun/runtime 0.5.0-beta → 0.7.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 +45 -0
- package/README.md +30 -60
- 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 +67 -0
- package/dist/core/runtime.js +1126 -0
- package/dist/core/store.d.ts +17 -0
- package/dist/core/store.js +146 -0
- package/dist/index.d.ts +6 -11
- package/dist/index.js +4 -6
- package/dist/mcp/connect.d.ts +31 -0
- package/dist/mcp/connect.js +190 -0
- package/dist/mcp/pool.d.ts +45 -0
- package/dist/mcp/pool.js +143 -0
- package/dist/mcp/snapshot.d.ts +25 -0
- package/dist/mcp/snapshot.js +10 -0
- package/dist/mcp/stdio.d.ts +15 -0
- package/dist/mcp/stdio.js +80 -0
- 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/dist/vault/crypto.d.ts +12 -0
- package/dist/vault/crypto.js +80 -0
- package/dist/vault/error.d.ts +4 -0
- package/dist/vault/error.js +7 -0
- package/dist/vault/kek.d.ts +6 -0
- package/dist/vault/kek.js +26 -0
- package/dist/vault/service.d.ts +59 -0
- package/dist/vault/service.js +538 -0
- package/dist/vault/url.d.ts +3 -0
- package/dist/vault/url.js +27 -0
- package/package.json +19 -13
- 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,80 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
const ROOT_TOKEN = "${PLUGIN_ROOT}";
|
|
4
|
+
const DATA_TOKEN = "${PLUGIN_DATA}";
|
|
5
|
+
/** Single-pass replacement. Text inserted for a token is not scanned again. */
|
|
6
|
+
export function expandPluginPlaceholders(value, pluginRoot, pluginData) {
|
|
7
|
+
let out = "";
|
|
8
|
+
for (let index = 0; index < value.length;) {
|
|
9
|
+
if (value.startsWith(ROOT_TOKEN, index)) {
|
|
10
|
+
out += pluginRoot;
|
|
11
|
+
index += ROOT_TOKEN.length;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (value.startsWith(DATA_TOKEN, index)) {
|
|
15
|
+
out += pluginData;
|
|
16
|
+
index += DATA_TOKEN.length;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
out += value[index];
|
|
20
|
+
index += 1;
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
export function prepareStdioLaunch(server, options) {
|
|
25
|
+
const pluginData = options.pluginData;
|
|
26
|
+
mkdirSync(pluginData, { recursive: true });
|
|
27
|
+
const pluginRoot = options.pluginRoot;
|
|
28
|
+
if (needsPluginRoot(server) && !pluginRoot)
|
|
29
|
+
throw new Error("stdio server requires a plugin root");
|
|
30
|
+
for (const key of Object.keys(server.env ?? {})) {
|
|
31
|
+
if (key === "PLUGIN_ROOT" || key === "PLUGIN_DATA")
|
|
32
|
+
throw new Error(`stdio env must not set ${key}`);
|
|
33
|
+
}
|
|
34
|
+
const command = server.command.startsWith("./")
|
|
35
|
+
? resolve(pluginRoot, server.command)
|
|
36
|
+
: server.command;
|
|
37
|
+
const args = (server.args ?? []).map((arg) => expand(arg, pluginRoot, pluginData));
|
|
38
|
+
const cwd = server.cwd
|
|
39
|
+
? resolveContainedCwd(server.cwd, pluginRoot, pluginData)
|
|
40
|
+
: (pluginRoot ?? pluginData);
|
|
41
|
+
const env = {};
|
|
42
|
+
for (const [key, value] of Object.entries(server.env ?? {}))
|
|
43
|
+
env[key] = expand(value, pluginRoot, pluginData);
|
|
44
|
+
if (pluginRoot)
|
|
45
|
+
env.PLUGIN_ROOT = pluginRoot;
|
|
46
|
+
env.PLUGIN_DATA = pluginData;
|
|
47
|
+
return { command, args, cwd, env };
|
|
48
|
+
}
|
|
49
|
+
function needsPluginRoot(server) {
|
|
50
|
+
if (server.command.startsWith("./"))
|
|
51
|
+
return true;
|
|
52
|
+
const values = [
|
|
53
|
+
...(server.args ?? []),
|
|
54
|
+
...Object.values(server.env ?? {}),
|
|
55
|
+
...(server.cwd === undefined ? [] : [server.cwd]),
|
|
56
|
+
];
|
|
57
|
+
return values.some((value) => value.includes(ROOT_TOKEN));
|
|
58
|
+
}
|
|
59
|
+
function expand(value, pluginRoot, pluginData) {
|
|
60
|
+
if (value.includes(ROOT_TOKEN) && !pluginRoot)
|
|
61
|
+
throw new Error("stdio server requires a plugin root");
|
|
62
|
+
return expandPluginPlaceholders(value, pluginRoot ?? "", pluginData);
|
|
63
|
+
}
|
|
64
|
+
function resolveContainedCwd(cwd, pluginRoot, pluginData) {
|
|
65
|
+
const expanded = expand(cwd, pluginRoot, pluginData);
|
|
66
|
+
const base = cwdTargetsData(cwd) ? pluginData : pluginRoot;
|
|
67
|
+
if (!base)
|
|
68
|
+
throw new Error("stdio server requires a plugin root");
|
|
69
|
+
const absolute = isAbsolute(expanded) ? expanded : resolve(base, expanded);
|
|
70
|
+
if (!isInside(base, absolute))
|
|
71
|
+
throw new Error(`Working directory escapes ${base}`);
|
|
72
|
+
return absolute;
|
|
73
|
+
}
|
|
74
|
+
function cwdTargetsData(cwd) {
|
|
75
|
+
return cwd === DATA_TOKEN || cwd.startsWith(`${DATA_TOKEN}/`);
|
|
76
|
+
}
|
|
77
|
+
function isInside(root, target) {
|
|
78
|
+
const rel = relative(root, target);
|
|
79
|
+
return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
|
|
80
|
+
}
|
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
|
+
}
|
package/dist/sessions/host.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { BuiltAgent,
|
|
1
|
+
import type { BuiltAgent, JsonObject, ModelAdapter } from "@nylorun/core/define";
|
|
2
|
+
import type { ExecutionInput, ExecutionState, RunResult } from "@nylorun/harness";
|
|
2
3
|
import type { CanonicalEvent, SessionStore, StoredSession, SessionSummary } from "./store.js";
|
|
3
4
|
export interface SubmitOptions {
|
|
4
5
|
readonly runId?: string;
|
|
@@ -8,6 +9,8 @@ export interface SubmitOptions {
|
|
|
8
9
|
readonly secrets?: readonly string[];
|
|
9
10
|
readonly onEvent?: (event: CanonicalEvent) => void;
|
|
10
11
|
readonly started?: Record<string, unknown>;
|
|
12
|
+
/** Seed `ExecutionState.state` when creating a new checkpoint (openSession). */
|
|
13
|
+
readonly sessionState?: JsonObject;
|
|
11
14
|
}
|
|
12
15
|
/** The built-in single-process coordinator. Storage adapters do not implement execution. */
|
|
13
16
|
export declare class SessionHost {
|
|
@@ -21,9 +24,16 @@ export declare class SessionHost {
|
|
|
21
24
|
private key;
|
|
22
25
|
read(agentId: string, sessionId: string): Promise<StoredSession | undefined>;
|
|
23
26
|
list(agentId: string): Promise<readonly SessionSummary[]>;
|
|
27
|
+
delete(agentId: string, sessionId: string): Promise<boolean>;
|
|
24
28
|
subscribe(agentId: string, sessionId: string, listener: (event: CanonicalEvent) => void): () => void;
|
|
25
29
|
submit(agent: BuiltAgent<any, any>, sessionId: string, input: ExecutionInput, options: SubmitOptions): Promise<RunResult<any>>;
|
|
26
30
|
cancel(agent: BuiltAgent<any, any>, sessionId: string): Promise<void>;
|
|
27
31
|
interrupt(agent: BuiltAgent<any, any>, sessionId: string, input: ExecutionInput, options: SubmitOptions): Promise<RunResult<any>>;
|
|
28
32
|
close(): Promise<void>;
|
|
29
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Map DX `wait-resolve` onto harness approve/respond/settle using wait metadata.
|
|
36
|
+
* Engine `ExecutionInput` already includes `wait-resolve`; resume is wired via
|
|
37
|
+
* existing interaction/deferred kinds until full wait-id resume lands.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveEngineInput(state: ExecutionState | undefined, input: ExecutionInput): ExecutionInput;
|