@nylorun/harness 0.13.0-beta → 0.16.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 (123) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/HOST_CONTRACT.md +27 -0
  3. package/README.md +62 -100
  4. package/dist/compatibility.d.ts +3 -0
  5. package/dist/compatibility.js +3 -0
  6. package/dist/definition/agent-definition.d.ts +11 -3
  7. package/dist/definition/agent-definition.js +18 -10
  8. package/dist/definition/compatibility.d.ts +11 -0
  9. package/dist/definition/compatibility.js +30 -0
  10. package/dist/definition/registry.d.ts +2 -2
  11. package/dist/definition/registry.js +8 -7
  12. package/dist/index.d.ts +4 -18
  13. package/dist/index.js +1 -6
  14. package/dist/loop/dispatch.js +327 -0
  15. package/dist/loop/host-suspension.d.ts +6 -0
  16. package/dist/loop/host-suspension.js +10 -0
  17. package/dist/loop/initial-state.d.ts +13 -0
  18. package/dist/loop/initial-state.js +30 -0
  19. package/dist/{execution → loop}/invocation.d.ts +5 -2
  20. package/dist/{execution → loop}/invocation.js +8 -3
  21. package/dist/{execution → loop}/model/adapters.d.ts +4 -2
  22. package/dist/{execution → loop}/model/adapters.js +3 -2
  23. package/dist/{execution → loop}/model/normalize.d.ts +2 -2
  24. package/dist/{execution → loop}/model/normalize.js +2 -2
  25. package/dist/{execution → loop}/model/prepared.d.ts +2 -2
  26. package/dist/{execution → loop}/model/project.d.ts +1 -1
  27. package/dist/{execution → loop}/model/project.js +1 -1
  28. package/dist/{execution → loop}/observe.d.ts +2 -2
  29. package/dist/{execution → loop}/observe.js +1 -1
  30. package/dist/{execution → loop}/options.d.ts +2 -1
  31. package/dist/{execution → loop}/options.js +28 -8
  32. package/dist/{execution → loop}/resume.d.ts +1 -1
  33. package/dist/{execution → loop}/resume.js +2 -2
  34. package/dist/{execution → loop}/run.d.ts +1 -1
  35. package/dist/{execution → loop}/run.js +9 -1
  36. package/dist/{execution → loop}/state.d.ts +2 -2
  37. package/dist/{execution → loop}/state.js +13 -3
  38. package/dist/{execution → loop}/step/canonicalize.d.ts +2 -2
  39. package/dist/{execution → loop}/step/canonicalize.js +1 -1
  40. package/dist/{execution → loop}/step/compose.d.ts +2 -2
  41. package/dist/{execution → loop}/step/compose.js +4 -1
  42. package/dist/{execution → loop}/step/context-draft.d.ts +2 -2
  43. package/dist/{execution → loop}/step/context-draft.js +2 -2
  44. package/dist/{execution → loop}/step/describe.d.ts +3 -3
  45. package/dist/{execution → loop}/step/describe.js +1 -1
  46. package/dist/loop/step/dynamics.d.ts +29 -0
  47. package/dist/loop/step/dynamics.js +149 -0
  48. package/dist/{execution → loop}/step/model-configuration.d.ts +2 -2
  49. package/dist/{execution → loop}/step/model-configuration.js +3 -3
  50. package/dist/{execution → loop}/step/resolve.d.ts +4 -4
  51. package/dist/{execution → loop}/step/run.d.ts +5 -4
  52. package/dist/{execution → loop}/step/run.js +87 -2
  53. package/dist/{execution → loop}/step/runtime.d.ts +12 -4
  54. package/dist/{execution → loop}/step/seal.d.ts +5 -5
  55. package/dist/{execution → loop}/step/seal.js +2 -2
  56. package/dist/{execution → loop}/step/slot-assembly.d.ts +1 -1
  57. package/dist/{execution → loop}/step/slot-assembly.js +1 -1
  58. package/dist/{execution → loop}/step/step-context.d.ts +5 -5
  59. package/dist/{execution → loop}/step/step-context.js +2 -2
  60. package/dist/{execution → loop}/tool-result.d.ts +2 -2
  61. package/dist/{execution → loop}/tool-result.js +2 -2
  62. package/dist/{execution → loop}/transcript.d.ts +1 -1
  63. package/dist/{execution → loop}/transcript.js +2 -2
  64. package/dist/loop/waits.d.ts +50 -0
  65. package/dist/loop/waits.js +11 -0
  66. package/dist/run/durable.d.ts +80 -0
  67. package/dist/run/durable.js +176 -0
  68. package/dist/run/index.d.ts +33 -0
  69. package/dist/run/index.js +38 -0
  70. package/dist/types/client.d.ts +88 -0
  71. package/dist/types/execution.d.ts +22 -5
  72. package/dist/utils/ids.d.ts +1 -0
  73. package/dist/utils/ids.js +11 -1
  74. package/package.json +21 -9
  75. package/dist/definition/assemble.d.ts +0 -22
  76. package/dist/definition/assemble.js +0 -43
  77. package/dist/definition/bind-agent.d.ts +0 -9
  78. package/dist/definition/bind-agent.js +0 -21
  79. package/dist/definition/bind-tool.d.ts +0 -4
  80. package/dist/definition/bind-tool.js +0 -19
  81. package/dist/definition/bound.d.ts +0 -16
  82. package/dist/definition/builder.d.ts +0 -30
  83. package/dist/definition/builder.js +0 -94
  84. package/dist/definition/declaration.d.ts +0 -3
  85. package/dist/definition/declaration.js +0 -53
  86. package/dist/definition/helpers.d.ts +0 -6
  87. package/dist/definition/helpers.js +0 -4
  88. package/dist/definition/manifest.d.ts +0 -9
  89. package/dist/definition/manifest.js +0 -45
  90. package/dist/definition/output-contract.d.ts +0 -6
  91. package/dist/definition/output-contract.js +0 -12
  92. package/dist/definition/schema.d.ts +0 -14
  93. package/dist/definition/schema.js +0 -205
  94. package/dist/errors.d.ts +0 -17
  95. package/dist/errors.js +0 -17
  96. package/dist/execution/dispatch.js +0 -164
  97. package/dist/execution/initial-state.d.ts +0 -3
  98. package/dist/execution/initial-state.js +0 -14
  99. package/dist/types/agent.d.ts +0 -9
  100. package/dist/types/agent.js +0 -1
  101. package/dist/types/manifest.d.ts +0 -22
  102. package/dist/types/manifest.js +0 -1
  103. package/dist/types/middleware.d.ts +0 -66
  104. package/dist/types/middleware.js +0 -1
  105. package/dist/types/model.d.ts +0 -184
  106. package/dist/types/model.js +0 -1
  107. package/dist/types/observe.d.ts +0 -165
  108. package/dist/types/observe.js +0 -1
  109. package/dist/types/shared.d.ts +0 -25
  110. package/dist/types/shared.js +0 -1
  111. package/dist/types/tool.d.ts +0 -138
  112. package/dist/types/tool.js +0 -1
  113. package/dist/types/transcript.d.ts +0 -65
  114. package/dist/types/transcript.js +0 -1
  115. package/dist/utils/canonical.d.ts +0 -1
  116. package/dist/utils/canonical.js +0 -10
  117. package/dist/utils/immutable.d.ts +0 -6
  118. package/dist/utils/immutable.js +0 -76
  119. /package/dist/{execution → loop}/dispatch.d.ts +0 -0
  120. /package/dist/{execution → loop}/model/prepared.js +0 -0
  121. /package/dist/{execution → loop}/step/resolve.js +0 -0
  122. /package/dist/{execution → loop}/step/runtime.js +0 -0
  123. /package/dist/{definition/bound.js → types/client.js} +0 -0
@@ -0,0 +1,50 @@
1
+ import type { JsonObject, JsonValue } from "@nylorun/core/define";
2
+ /** Internal signal thrown by durable wait helpers; caught by the tool dispatcher. */
3
+ export declare class WaitSignal {
4
+ readonly outcome: {
5
+ readonly kind: "interaction-required";
6
+ readonly interaction: {
7
+ readonly kind: "approval" | "response";
8
+ readonly prompt: string;
9
+ readonly metadata?: JsonObject;
10
+ };
11
+ readonly token?: JsonValue;
12
+ readonly wait: {
13
+ readonly kind: "ask" | "approve" | "sleep" | "waitFor";
14
+ readonly waitId: string;
15
+ readonly name?: string;
16
+ };
17
+ } | {
18
+ readonly kind: "deferred";
19
+ readonly token?: JsonValue;
20
+ readonly wait: {
21
+ readonly kind: "ask" | "approve" | "sleep" | "waitFor";
22
+ readonly waitId: string;
23
+ readonly name?: string;
24
+ };
25
+ };
26
+ readonly name = "WaitSignal";
27
+ constructor(outcome: {
28
+ readonly kind: "interaction-required";
29
+ readonly interaction: {
30
+ readonly kind: "approval" | "response";
31
+ readonly prompt: string;
32
+ readonly metadata?: JsonObject;
33
+ };
34
+ readonly token?: JsonValue;
35
+ readonly wait: {
36
+ readonly kind: "ask" | "approve" | "sleep" | "waitFor";
37
+ readonly waitId: string;
38
+ readonly name?: string;
39
+ };
40
+ } | {
41
+ readonly kind: "deferred";
42
+ readonly token?: JsonValue;
43
+ readonly wait: {
44
+ readonly kind: "ask" | "approve" | "sleep" | "waitFor";
45
+ readonly waitId: string;
46
+ readonly name?: string;
47
+ };
48
+ });
49
+ }
50
+ export declare function isWaitSignal(value: unknown): value is WaitSignal;
@@ -0,0 +1,11 @@
1
+ /** Internal signal thrown by durable wait helpers; caught by the tool dispatcher. */
2
+ export class WaitSignal {
3
+ outcome;
4
+ name = "WaitSignal";
5
+ constructor(outcome) {
6
+ this.outcome = outcome;
7
+ }
8
+ }
9
+ export function isWaitSignal(value) {
10
+ return value instanceof WaitSignal;
11
+ }
@@ -0,0 +1,80 @@
1
+ import type { AgentManifest } from "@nylorun/core/define";
2
+ import type { ExecutionInput, ExecutionState, RunResult } from "../types/execution.js";
3
+ import type { JsonObject } from "@nylorun/core/define";
4
+ import type { ActionOutcome } from "@nylorun/core/contracts";
5
+ import { ENGINE_VERSION } from "../compatibility.js";
6
+ /** Private persistence contract for hosts; this is not a session wire type. */
7
+ export interface DurableCheckpoint {
8
+ readonly version: 1;
9
+ readonly engineVersion: typeof ENGINE_VERSION;
10
+ readonly manifestHash: string;
11
+ readonly sessionId: string;
12
+ readonly turnId: string;
13
+ /** Increment for each human/event continuation, not for an action result. */
14
+ readonly segment: number;
15
+ readonly input: ExecutionInput;
16
+ readonly state?: ExecutionState;
17
+ readonly info?: JsonObject;
18
+ }
19
+ export interface HostEffect {
20
+ readonly effectId: string;
21
+ readonly sessionId: string;
22
+ readonly turnId: string;
23
+ readonly agentId: string;
24
+ readonly manifestHash: string;
25
+ readonly kind: "model" | "tool" | "beforeModelCall" | "afterModelCall";
26
+ readonly capabilityId?: string;
27
+ readonly toolName?: string;
28
+ readonly input: unknown;
29
+ readonly context: Record<string, unknown>;
30
+ }
31
+ export type EffectResolution = {
32
+ readonly status: "completed";
33
+ readonly outcome: ActionOutcome;
34
+ } | {
35
+ readonly status: "pending" | "uncertain";
36
+ };
37
+ export interface DurableHost {
38
+ /** Atomically load-or-create by effectId; validate identical request on replay.
39
+ * Return recorded outcomes. Record intent BEFORE invoking a provider or exposing
40
+ * customer work. Uncertain effects must never be automatically executed again.
41
+ */
42
+ resolveEffect(effect: HostEffect): Promise<EffectResolution>;
43
+ }
44
+ export type DurableResult = {
45
+ readonly status: "waiting" | "uncertain";
46
+ readonly checkpoint: DurableCheckpoint;
47
+ readonly effectIds: readonly string[];
48
+ } | {
49
+ readonly status: "completed" | "paused" | "cancelled" | "failed";
50
+ readonly checkpoint: DurableCheckpoint;
51
+ readonly result: RunResult<unknown>;
52
+ };
53
+ /** Discovered tool advertised for this execution. It is not part of the hashed manifest. */
54
+ export interface DurableSessionTool {
55
+ readonly capabilityId: string;
56
+ readonly name: string;
57
+ readonly description?: string;
58
+ readonly inputSchema: JsonObject;
59
+ readonly outputSchema?: JsonObject;
60
+ }
61
+ export declare function createDurableCheckpoint(input: {
62
+ manifest: AgentManifest;
63
+ sessionId: string;
64
+ turnId: string;
65
+ input: ExecutionInput;
66
+ state?: ExecutionState;
67
+ info?: JsonObject;
68
+ segment?: number;
69
+ }): DurableCheckpoint;
70
+ /** Deterministic reconstruction uses individually journaled effects, never a replayable whole loop.
71
+ * A suspended checkpoint is the immutable segment start plus the host's durable effect journal.
72
+ * Returning waiting releases all execution stacks; no process-local continuation survives.
73
+ */
74
+ export declare function runDurable(options: {
75
+ manifest: AgentManifest;
76
+ checkpoint: DurableCheckpoint;
77
+ host: DurableHost;
78
+ signal?: AbortSignal;
79
+ sessionTools?: readonly DurableSessionTool[];
80
+ }): Promise<DurableResult>;
@@ -0,0 +1,176 @@
1
+ import { HarnessError } from "@nylorun/core/define";
2
+ import { AgentManifestSchema } from "@nylorun/core/contracts";
3
+ import { agentFrom } from "@nylorun/core/define";
4
+ import { definitionFor } from "../definition/agent-definition.js";
5
+ import { schemaFromJSON } from "@nylorun/core/define";
6
+ import { hashManifest } from "@nylorun/core/define";
7
+ import { withDeterministicIds } from "../utils/ids.js";
8
+ import { execute } from "../loop/run.js";
9
+ import { initializeExecutionState } from "../loop/initial-state.js";
10
+ import { HostSuspension } from "../loop/host-suspension.js";
11
+ import { CHECKPOINT_VERSION, ENGINE_VERSION } from "../compatibility.js";
12
+ export function createDurableCheckpoint(input) {
13
+ AgentManifestSchema.parse(input.manifest);
14
+ return {
15
+ version: CHECKPOINT_VERSION,
16
+ engineVersion: ENGINE_VERSION,
17
+ manifestHash: hashManifest(input.manifest),
18
+ sessionId: input.sessionId,
19
+ turnId: input.turnId,
20
+ segment: input.segment ?? 0,
21
+ input: input.input,
22
+ ...(input.state ? { state: input.state } : {}),
23
+ ...(input.info ? { info: input.info } : {}),
24
+ };
25
+ }
26
+ /** Deterministic reconstruction uses individually journaled effects, never a replayable whole loop.
27
+ * A suspended checkpoint is the immutable segment start plus the host's durable effect journal.
28
+ * Returning waiting releases all execution stacks; no process-local continuation survives.
29
+ */
30
+ export async function runDurable(options) {
31
+ const { manifest, checkpoint, host } = options;
32
+ AgentManifestSchema.parse(manifest);
33
+ if (checkpoint.version !== CHECKPOINT_VERSION ||
34
+ checkpoint.engineVersion !== ENGINE_VERSION ||
35
+ checkpoint.manifestHash !== hashManifest(manifest))
36
+ throw new HarnessError("execution.incompatible", "Incompatible durable checkpoint");
37
+ if (checkpoint.state &&
38
+ (checkpoint.state.manifestHash !== checkpoint.manifestHash ||
39
+ checkpoint.state.agentId !== manifest.id ||
40
+ checkpoint.state.executionId !== checkpoint.sessionId))
41
+ throw new HarnessError("execution.incompatible", "Checkpoint definition/session mismatch");
42
+ let serial = 0;
43
+ const pending = new Map();
44
+ const inFlight = new Set();
45
+ const effect = async (kind, input, context, capabilityId, toolName, identity) => {
46
+ const effectId = `${checkpoint.turnId}:${checkpoint.segment}:${kind}:${identity ?? ++serial}`;
47
+ const request = JSON.parse(JSON.stringify({
48
+ effectId,
49
+ sessionId: checkpoint.sessionId,
50
+ turnId: checkpoint.turnId,
51
+ agentId: manifest.id,
52
+ manifestHash: checkpoint.manifestHash,
53
+ kind,
54
+ input,
55
+ context,
56
+ ...(capabilityId ? { capabilityId } : {}),
57
+ ...(toolName ? { toolName } : {}),
58
+ }));
59
+ const operation = host.resolveEffect(request);
60
+ inFlight.add(operation);
61
+ let result;
62
+ try {
63
+ result = await operation;
64
+ }
65
+ finally {
66
+ inFlight.delete(operation);
67
+ }
68
+ if (result.status !== "completed") {
69
+ pending.set(effectId, result.status);
70
+ throw new HostSuspension(effectId, result.status);
71
+ }
72
+ return result.outcome;
73
+ };
74
+ let patchTail = Promise.resolve();
75
+ const hostedTool = (capabilityId, tool) => ({
76
+ name: tool.name,
77
+ ...(tool.description === undefined ? {} : { description: tool.description }),
78
+ inputSchema: schemaFromJSON(tool.inputSchema),
79
+ ...(tool.outputSchema ? { outputSchema: schemaFromJSON(tool.outputSchema) } : {}),
80
+ async execute(args, ctx) {
81
+ const previous = patchTail;
82
+ let release;
83
+ patchTail = new Promise((resolve) => {
84
+ release = resolve;
85
+ });
86
+ try {
87
+ const outcome = await effect("tool", args, {
88
+ executionId: ctx.executionId,
89
+ turnId: ctx.turnId,
90
+ stepId: ctx.stepId,
91
+ callId: ctx.callId,
92
+ invocationId: ctx.invocationId,
93
+ idempotencyKey: ctx.idempotencyKey,
94
+ info: ctx.info,
95
+ state: ctx.state.entries(),
96
+ resume: ctx.resume,
97
+ }, capabilityId, tool.name, ctx.invocationId);
98
+ // Apply concurrently resolved patches in manifest call order, independent of host I/O timing.
99
+ await previous;
100
+ for (const [key, value] of Object.entries(outcome.statePatch ?? {}))
101
+ ctx.state.set(key, value);
102
+ const value = outcome.value;
103
+ return value?.kind === "interaction-required"
104
+ ? {
105
+ ...value,
106
+ interaction: {
107
+ ...value.interaction,
108
+ id: `${checkpoint.turnId}:${checkpoint.segment}:interaction:${ctx.invocationId}`,
109
+ },
110
+ }
111
+ : value;
112
+ }
113
+ finally {
114
+ await previous;
115
+ release();
116
+ }
117
+ },
118
+ });
119
+ const implementations = {};
120
+ const sessionTools = options.sessionTools ?? [];
121
+ for (const capability of manifest.capabilities) {
122
+ const tools = {};
123
+ for (const tool of capability.tools ?? [])
124
+ tools[tool.name] = hostedTool(capability.id, tool);
125
+ for (const tool of sessionTools)
126
+ if (tool.capabilityId === capability.id)
127
+ tools[tool.name] = hostedTool(capability.id, tool);
128
+ implementations[capability.id] = {
129
+ tools,
130
+ ...(capability.beforeModelCall
131
+ ? {
132
+ beforeModelCall: async (args) => (await effect("beforeModelCall", args, { info: checkpoint.info }, capability.id))
133
+ .value,
134
+ }
135
+ : {}),
136
+ ...(capability.afterModelCall
137
+ ? {
138
+ afterModelCall: async (args, ctx) => (await effect("afterModelCall", args, ctx, capability.id)).value,
139
+ }
140
+ : {}),
141
+ };
142
+ }
143
+ const definition = definitionFor(agentFrom(manifest, implementations, sessionTools.length === 0
144
+ ? undefined
145
+ : {
146
+ sessionTools: sessionTools.map((tool) => ({
147
+ capabilityId: tool.capabilityId,
148
+ name: tool.name,
149
+ })),
150
+ }));
151
+ // Manifest reconstruction must retain definition identity, including schemas and instructions.
152
+ if (definition.hash !== checkpoint.manifestHash)
153
+ throw new HarnessError("execution.incompatible", "Reconstructed definition hash mismatch");
154
+ try {
155
+ const result = await withDeterministicIds(`${checkpoint.turnId}_${checkpoint.segment}`, () => execute(definition, {
156
+ state: checkpoint.state ??
157
+ initializeExecutionState(definition, { executionId: checkpoint.sessionId }),
158
+ input: checkpoint.input,
159
+ info: checkpoint.info,
160
+ signal: options.signal,
161
+ onModelCall: async (call, ctx) => (await effect("model", call, { request: ctx.request, invocationId: ctx.invocationId }, undefined, undefined, ctx.invocationId)).value,
162
+ }));
163
+ return { status: result.status, checkpoint: { ...checkpoint, state: result.state }, result };
164
+ }
165
+ catch (error) {
166
+ // Parallel tool dispatch can suspend more than one action. Drain persistence before returning.
167
+ await Promise.allSettled([...inFlight]);
168
+ if (!(error instanceof HostSuspension))
169
+ throw error;
170
+ return {
171
+ status: [...pending.values()].includes("uncertain") ? "uncertain" : "waiting",
172
+ checkpoint,
173
+ effectIds: [...pending.keys()],
174
+ };
175
+ }
176
+ }
@@ -0,0 +1,33 @@
1
+ import { checkCompatibility } from "../definition/compatibility.js";
2
+ import type { Implementations } from "@nylorun/core/define";
3
+ import type { AgentBinding, BuiltAgent } from "@nylorun/core/define";
4
+ import type { AgentManifest } from "@nylorun/core/define";
5
+ import type { ExecutionState, RunOptions, RunResult } from "../types/execution.js";
6
+ import { execute } from "../loop/run.js";
7
+ import { createExecutionState, validateExecutionState } from "../loop/state.js";
8
+ export interface RunBinding<Info = unknown> {
9
+ readonly manifest: AgentManifest;
10
+ readonly implementations: Implementations<Info>;
11
+ /** Optional authoring binding preserves live schemas and executable snapshots. */
12
+ readonly authoring?: AgentBinding;
13
+ }
14
+ export interface BoundRunOptions<Info = unknown> extends RunOptions<Info> {
15
+ readonly binding: RunBinding<Info>;
16
+ }
17
+ /**
18
+ * Run the model/tool loop from a definition binding + optional checkpoint.
19
+ * This is the public execution surface Runtime (and tests) should call.
20
+ */
21
+ export declare function run<Info = unknown, Output = string>(options: BoundRunOptions<Info>): Promise<RunResult<Output>>;
22
+ /** Create a fresh checkpoint bound to a manifest hash. */
23
+ export declare function createRunState(binding: RunBinding, options?: {
24
+ readonly executionId?: string;
25
+ readonly state?: import("@nylorun/core/define").JsonObject;
26
+ }): ExecutionState;
27
+ /** Build a run binding from an explicitly authored agent. */
28
+ export declare function bindingFromAgent(agent: BuiltAgent): RunBinding;
29
+ export { checkCompatibility, createExecutionState, validateExecutionState, execute };
30
+ export type { Implementations, ExecutionState, RunResult, RunOptions };
31
+ export { createDurableCheckpoint, runDurable } from "./durable.js";
32
+ export type { DurableCheckpoint, DurableSessionTool, HostEffect, EffectResolution, DurableHost, DurableResult, } from "./durable.js";
33
+ export type { ModelAdapter, ModelCandidate, ModelCall, ModelRequest } from "@nylorun/core/define";
@@ -0,0 +1,38 @@
1
+ import { definitionFor, definitionFromBinding } from "../definition/agent-definition.js";
2
+ import { checkCompatibility } from "../definition/compatibility.js";
3
+ import { bindingFromAgent as authoringBindingFromAgent, agentFrom } from "@nylorun/core/define";
4
+ import { execute } from "../loop/run.js";
5
+ import { createExecutionState, validateExecutionState } from "../loop/state.js";
6
+ import { initializeExecutionState } from "../loop/initial-state.js";
7
+ /**
8
+ * Run the model/tool loop from a definition binding + optional checkpoint.
9
+ * This is the public execution surface Runtime (and tests) should call.
10
+ */
11
+ export async function run(options) {
12
+ const definition = resolveDefinition(options.binding);
13
+ if (options.state?.manifestHash) {
14
+ const compatibility = checkCompatibility(definition.manifest, options.state);
15
+ if (!compatibility.ok)
16
+ throw compatibility.error;
17
+ }
18
+ const { binding: _, ...runOptions } = options;
19
+ return execute(definition, runOptions);
20
+ }
21
+ /** Create a fresh checkpoint bound to a manifest hash. */
22
+ export function createRunState(binding, options) {
23
+ const definition = resolveDefinition(binding);
24
+ return initializeExecutionState(definition, options);
25
+ }
26
+ function resolveDefinition(binding) {
27
+ if (binding.authoring)
28
+ return definitionFromBinding(binding.authoring);
29
+ const agent = agentFrom(binding.manifest, binding.implementations);
30
+ return definitionFor(agent);
31
+ }
32
+ /** Build a run binding from an explicitly authored agent. */
33
+ export function bindingFromAgent(agent) {
34
+ const authoring = authoringBindingFromAgent(agent);
35
+ return { manifest: authoring.manifest, implementations: authoring.implementations, authoring };
36
+ }
37
+ export { checkCompatibility, createExecutionState, validateExecutionState, execute };
38
+ export { createDurableCheckpoint, runDurable } from "./durable.js";
@@ -0,0 +1,88 @@
1
+ import type { JsonObject, JsonValue } from "@nylorun/core/define";
2
+ import type { MessageInput } from "@nylorun/core/define";
3
+ import type { SavedToolCall } from "./execution.js";
4
+ /**
5
+ * Definition-parameterized client types (H11). Types only — harness does not implement Session.
6
+ * Runtime imports these for `openSession` / stream typing later.
7
+ */
8
+ export interface InputAccepted {
9
+ readonly status: "accepted" | "rejected";
10
+ readonly error?: {
11
+ readonly code: string;
12
+ readonly message: string;
13
+ };
14
+ readonly turnId?: string;
15
+ readonly cursor?: string;
16
+ readonly steered?: boolean;
17
+ }
18
+ export type Result<Output = unknown> = {
19
+ readonly status: "completed";
20
+ readonly output: Output;
21
+ } | {
22
+ readonly status: "paused";
23
+ readonly pending: readonly SavedToolCall[];
24
+ } | {
25
+ readonly status: "failed";
26
+ readonly error: {
27
+ readonly code: string;
28
+ readonly message: string;
29
+ };
30
+ } | {
31
+ readonly status: "cancelled";
32
+ };
33
+ export interface Turn<Output = unknown> {
34
+ readonly id: string;
35
+ readonly sessionId: string;
36
+ readonly status: "running" | "settled";
37
+ readonly result?: Result<Output>;
38
+ }
39
+ export type Event<Output = unknown> = {
40
+ readonly type: "turn.started";
41
+ readonly cursor: string;
42
+ readonly at: string;
43
+ readonly sessionId: string;
44
+ readonly turnId: string;
45
+ } | {
46
+ readonly type: "status";
47
+ readonly status: "thinking" | "calling_tool" | "waiting" | "requires_action";
48
+ readonly sessionId: string;
49
+ readonly turnId: string;
50
+ } | {
51
+ readonly type: "text.delta";
52
+ readonly text: string;
53
+ readonly sessionId: string;
54
+ readonly turnId: string;
55
+ } | {
56
+ readonly type: "tool.started" | "tool.progress" | "tool.completed" | "tool.failed";
57
+ readonly tool: string;
58
+ readonly sessionId: string;
59
+ readonly turnId: string;
60
+ } | {
61
+ readonly type: "interaction.required";
62
+ readonly id: string;
63
+ readonly prompt: string;
64
+ readonly options?: JsonObject;
65
+ readonly sessionId: string;
66
+ readonly turnId: string;
67
+ } | {
68
+ readonly type: "turn.settled";
69
+ readonly result: Result<Output>;
70
+ readonly sessionId: string;
71
+ readonly turnId: string;
72
+ };
73
+ /** Structural agent identity used to parameterize Session/Event types. */
74
+ export interface AgentIdentity {
75
+ readonly id: string;
76
+ }
77
+ export interface Session<A extends AgentIdentity = AgentIdentity, Output = unknown> {
78
+ readonly id: string;
79
+ readonly agentId: A["id"];
80
+ input(message: MessageInput | string): Promise<InputAccepted>;
81
+ stream(opts?: {
82
+ readonly after?: string;
83
+ }): AsyncIterable<Event<Output>>;
84
+ history(): Promise<readonly JsonValue[]>;
85
+ approve(id: string, approved: boolean): Promise<void>;
86
+ respond(id: string, value: JsonValue): Promise<void>;
87
+ cancel(): Promise<void>;
88
+ }
@@ -1,8 +1,8 @@
1
- import type { ModelAdapter } from "./model.js";
2
- import type { InputEvent, MessageInput, TranscriptEntry } from "./transcript.js";
3
- import type { ObserveEvent } from "./observe.js";
4
- import type { JsonObject, JsonValue, Tripwire } from "./shared.js";
5
- import type { RequiredInteraction, ToolExecutionResume, ToolOutcome, ToolResult } from "./tool.js";
1
+ import type { ModelAdapter } from "@nylorun/core/define";
2
+ import type { InputEvent, MessageInput, TranscriptEntry } from "@nylorun/core/define";
3
+ import type { ObserveEvent } from "@nylorun/core/define";
4
+ import type { JsonObject, JsonValue, Tripwire } from "@nylorun/core/define";
5
+ import type { RequiredInteraction, ToolExecutionResume, ToolOutcome, ToolResult } from "@nylorun/core/define";
6
6
  export type ExecutionInput = MessageInput | InputEvent | {
7
7
  readonly kind: "continue";
8
8
  } | {
@@ -11,6 +11,10 @@ export type ExecutionInput = MessageInput | InputEvent | {
11
11
  readonly outcome: Extract<ToolOutcome, {
12
12
  kind: "completed" | "failed" | "denied";
13
13
  }>;
14
+ } | {
15
+ readonly kind: "wait-resolve";
16
+ readonly waitId: string;
17
+ readonly value?: JsonValue;
14
18
  };
15
19
  /** Portable reference to deployed code. Applications must preserve this value intact. */
16
20
  export interface ToolReference {
@@ -36,6 +40,12 @@ export interface SavedToolCall {
36
40
  readonly interactionPhase?: "before" | "execute";
37
41
  readonly resume?: ToolExecutionResume;
38
42
  readonly token?: JsonValue;
43
+ /** Optional wait metadata for H6 durable waits. */
44
+ readonly wait?: {
45
+ readonly kind: "ask" | "approve" | "sleep" | "waitFor";
46
+ readonly waitId: string;
47
+ readonly name?: string;
48
+ };
39
49
  }
40
50
  export interface ExecutionPlan {
41
51
  readonly turnId: string;
@@ -52,6 +62,8 @@ export interface ExecutionPlan {
52
62
  export interface ExecutionState {
53
63
  readonly version: 1;
54
64
  readonly agentId: string;
65
+ /** Definition binding — required for resume after Phase 2. Optional on legacy states. */
66
+ readonly manifestHash?: string;
55
67
  readonly executionId: string;
56
68
  readonly outputContract?: JsonObject;
57
69
  readonly revision: number;
@@ -61,6 +73,11 @@ export interface ExecutionState {
61
73
  readonly plan?: ExecutionPlan;
62
74
  /** Abandoned actions retained for reconciliation, never automatically dispatched. */
63
75
  readonly cancelledCalls?: readonly SavedToolCall[];
76
+ /**
77
+ * Durable session memory (tools write via `ctx.state`; beforeModelCall reads).
78
+ * Runtime persists this with the checkpoint.
79
+ */
80
+ readonly state?: JsonObject;
64
81
  }
65
82
  export type ExecutionEvent = (ObserveEvent | {
66
83
  readonly type: "execution.completed" | "execution.paused" | "execution.cancelled" | "execution.failed" | "observation.failed";
@@ -1 +1,2 @@
1
+ export declare function withDeterministicIds<T>(seed: string, fn: () => T): T;
1
2
  export declare function createId(prefix: string): string;
package/dist/utils/ids.js CHANGED
@@ -1,3 +1,13 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const scope = new AsyncLocalStorage();
3
+ export function withDeterministicIds(seed, fn) {
4
+ return scope.run({ seed, counts: new Map() }, fn);
5
+ }
1
6
  export function createId(prefix) {
2
- return `${prefix}_${globalThis.crypto.randomUUID()}`;
7
+ const value = scope.getStore();
8
+ if (!value)
9
+ return `${prefix}_${globalThis.crypto.randomUUID()}`;
10
+ const count = (value.counts.get(prefix) ?? 0) + 1;
11
+ value.counts.set(prefix, count);
12
+ return `${prefix}_${value.seed}_${count}`;
3
13
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nylorun/harness",
3
- "version": "0.13.0-beta",
4
- "description": "Nylorun's TypeScript agent runtime. See github.com/nylorun/harness.",
3
+ "version": "0.16.0-beta",
4
+ "description": "Nylorun's TypeScript agent execution engine. See github.com/nylorun/harness.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -28,16 +28,25 @@
28
28
  "types": "./dist/index.d.ts",
29
29
  "import": "./dist/index.js"
30
30
  },
31
+ "./run": {
32
+ "types": "./dist/run/index.d.ts",
33
+ "import": "./dist/run/index.js"
34
+ },
31
35
  "./model/adapters": {
32
- "types": "./dist/execution/model/adapters.d.ts",
33
- "import": "./dist/execution/model/adapters.js"
36
+ "types": "./dist/loop/model/adapters.d.ts",
37
+ "import": "./dist/loop/model/adapters.js"
38
+ },
39
+ "./compatibility": {
40
+ "types": "./dist/compatibility.d.ts",
41
+ "import": "./dist/compatibility.js"
34
42
  }
35
43
  },
36
44
  "files": [
37
45
  "dist",
38
46
  "README.md",
39
47
  "CHANGELOG.md",
40
- "LICENSE"
48
+ "LICENSE",
49
+ "HOST_CONTRACT.md"
41
50
  ],
42
51
  "sideEffects": false,
43
52
  "publishConfig": {
@@ -61,12 +70,15 @@
61
70
  "devDependencies": {
62
71
  "@types/node": "^26.5.0",
63
72
  "@typescript/native": "npm:typescript@^7.0.2",
64
- "prettier": "^3.9.6",
73
+ "prettier": "^3.9.8",
65
74
  "typescript": "npm:@typescript/typescript6@^6.0.2",
66
- "vitest": "^5.0.0",
67
- "zod": "^4.1.12"
75
+ "vitest": "^5.0.1",
76
+ "zod": "^4.6.5"
77
+ },
78
+ "dependencies": {
79
+ "@nylorun/core": "0.2.0-beta"
68
80
  },
69
81
  "peerDependencies": {
70
- "zod": "^4.1.12"
82
+ "zod": "^4.6.5"
71
83
  }
72
84
  }
@@ -1,22 +0,0 @@
1
- import type { BoundMiddleware } from "./bound.js";
2
- import type { BuiltAgent } from "../types/agent.js";
3
- import type { AgentManifest } from "../types/manifest.js";
4
- import type { BuildDiagnostic } from "../types/shared.js";
5
- import type { RunOptions, RunResult } from "../types/execution.js";
6
- import type { ToolSchemaSource } from "../types/tool.js";
7
- import type { AgentDefinition } from "./agent-definition.js";
8
- type BuildResult<Agent> = {
9
- readonly ok: true;
10
- readonly agent: Agent;
11
- readonly manifest: AgentManifest;
12
- } | {
13
- readonly ok: false;
14
- readonly diagnostics: readonly BuildDiagnostic[];
15
- };
16
- type AgentRun = (definition: AgentDefinition, options: RunOptions<any>) => Promise<RunResult<any>>;
17
- export declare function assembleAgent(middleware: readonly BoundMiddleware[], identity: Readonly<{
18
- id: string;
19
- name: string;
20
- outputSchema?: ToolSchemaSource;
21
- }>, run: AgentRun): BuildResult<BuiltAgent>;
22
- export {};