@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.
Files changed (63) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +30 -60
  3. package/dist/config.d.ts +1 -1
  4. package/dist/configuration.d.ts +3 -0
  5. package/dist/configuration.js +3 -0
  6. package/dist/contracts.d.ts +4 -2
  7. package/dist/core/main.js +40 -0
  8. package/dist/core/provider.d.ts +12 -0
  9. package/dist/core/provider.js +60 -0
  10. package/dist/core/runtime.d.ts +67 -0
  11. package/dist/core/runtime.js +1126 -0
  12. package/dist/core/store.d.ts +17 -0
  13. package/dist/core/store.js +146 -0
  14. package/dist/index.d.ts +6 -11
  15. package/dist/index.js +4 -6
  16. package/dist/mcp/connect.d.ts +31 -0
  17. package/dist/mcp/connect.js +190 -0
  18. package/dist/mcp/pool.d.ts +45 -0
  19. package/dist/mcp/pool.js +143 -0
  20. package/dist/mcp/snapshot.d.ts +25 -0
  21. package/dist/mcp/snapshot.js +10 -0
  22. package/dist/mcp/stdio.d.ts +15 -0
  23. package/dist/mcp/stdio.js +80 -0
  24. package/dist/model/defaults.d.ts +1 -1
  25. package/dist/model/http-model.d.ts +1 -1
  26. package/dist/node/local-sessions.js +17 -0
  27. package/dist/server/host.d.ts +42 -4
  28. package/dist/server/host.js +69 -3
  29. package/dist/session/api.d.ts +10 -0
  30. package/dist/session/api.js +15 -0
  31. package/dist/session/default.d.ts +5 -0
  32. package/dist/session/default.js +30 -0
  33. package/dist/session/handle.d.ts +27 -0
  34. package/dist/session/handle.js +199 -0
  35. package/dist/session/index.d.ts +2 -0
  36. package/dist/session/index.js +2 -0
  37. package/dist/sessions/host.d.ts +11 -1
  38. package/dist/sessions/host.js +75 -7
  39. package/dist/sessions/store.d.ts +1 -0
  40. package/dist/sessions/store.js +3 -0
  41. package/dist/vault/crypto.d.ts +12 -0
  42. package/dist/vault/crypto.js +80 -0
  43. package/dist/vault/error.d.ts +4 -0
  44. package/dist/vault/error.js +7 -0
  45. package/dist/vault/kek.d.ts +6 -0
  46. package/dist/vault/kek.js +26 -0
  47. package/dist/vault/service.d.ts +59 -0
  48. package/dist/vault/service.js +538 -0
  49. package/dist/vault/url.d.ts +3 -0
  50. package/dist/vault/url.js +27 -0
  51. package/package.json +19 -13
  52. package/dist/cli.d.ts +0 -2
  53. package/dist/cli.js +0 -100
  54. package/dist/dev-entry.js +0 -2
  55. package/dist/dev.d.ts +0 -2
  56. package/dist/dev.js +0 -126
  57. package/dist/environment.d.ts +0 -2
  58. package/dist/environment.js +0 -64
  59. package/dist/launcher.d.ts +0 -1
  60. package/dist/launcher.js +0 -33
  61. package/dist/model/configure.d.ts +0 -12
  62. package/dist/model/configure.js +0 -155
  63. /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
+ }
@@ -1,4 +1,4 @@
1
- import type { ModelAdapter } from "@nylorun/harness";
1
+ import type { ModelAdapter } from "@nylorun/core/define";
2
2
  import { type ModelEnvironment } from "./http-model.js";
3
3
  export interface ModelPreview {
4
4
  readonly invocationId: string;
@@ -1,4 +1,4 @@
1
- import type { ModelAdapter } from "@nylorun/harness";
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)
@@ -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
- export type ServeAgentsOptions = AgentRouterOptions & {
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
- private readonly config;
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
- [kServe](options: Omit<ServeAgentsOptions, "runtime">): Hono<any>;
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: ServeAgentsOptions): Hono<any>;
68
+ export declare function serveAgents(options: ServeAgentsCompatOptions): Hono<any>;
69
+ export declare function serveAgents(options: ServeAgentsFetchOptions): ServeAgentsFetch;
32
70
  export {};
@@ -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({ error: "expected a correlated interaction or settlement" }, 400);
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
- const { runtime, ...rest } = options;
284
- return runtime[kServe](rest);
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
+ }
@@ -0,0 +1,2 @@
1
+ export { createSessionHandle, resolveWait, type OpenSessionOptions, type SessionHandle, type SessionRuntime, } from "./handle.js";
2
+ export { getDefaultRuntime, setDefaultRuntime, installDefaultRuntimeFactory, } from "./default.js";
@@ -0,0 +1,2 @@
1
+ export { createSessionHandle, resolveWait, } from "./handle.js";
2
+ export { getDefaultRuntime, setDefaultRuntime, installDefaultRuntimeFactory, } from "./default.js";
@@ -1,4 +1,5 @@
1
- import type { BuiltAgent, ExecutionInput, ModelAdapter, RunResult } from "@nylorun/harness";
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;