@nylorun/harness 0.12.0-beta → 0.15.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 (137) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/HOST_CONTRACT.md +27 -0
  3. package/README.md +86 -5
  4. package/dist/compatibility.d.ts +3 -0
  5. package/dist/compatibility.js +3 -0
  6. package/dist/definition/agent-definition.d.ts +19 -0
  7. package/dist/definition/agent-definition.js +20 -0
  8. package/dist/definition/compatibility.d.ts +11 -0
  9. package/dist/definition/compatibility.js +30 -0
  10. package/dist/definition/registry.d.ts +10 -0
  11. package/dist/definition/registry.js +49 -0
  12. package/dist/index.d.ts +4 -15
  13. package/dist/index.js +1 -6
  14. package/dist/loop/dispatch.d.ts +2 -0
  15. package/dist/loop/dispatch.js +327 -0
  16. package/dist/loop/host-suspension.d.ts +6 -0
  17. package/dist/loop/host-suspension.js +10 -0
  18. package/dist/loop/initial-state.d.ts +13 -0
  19. package/dist/loop/initial-state.js +30 -0
  20. package/dist/loop/invocation.d.ts +24 -0
  21. package/dist/loop/invocation.js +37 -0
  22. package/dist/{model → loop/model}/adapters.d.ts +4 -2
  23. package/dist/{model → loop/model}/adapters.js +3 -2
  24. package/dist/{model → loop/model}/normalize.d.ts +2 -2
  25. package/dist/{model → loop/model}/normalize.js +2 -2
  26. package/dist/{model → loop/model}/prepared.d.ts +3 -3
  27. package/dist/{step → loop/model}/project.d.ts +1 -1
  28. package/dist/{step → loop/model}/project.js +2 -10
  29. package/dist/loop/observe.d.ts +17 -0
  30. package/dist/loop/observe.js +53 -0
  31. package/dist/loop/options.d.ts +11 -0
  32. package/dist/loop/options.js +69 -0
  33. package/dist/loop/resume.d.ts +4 -0
  34. package/dist/loop/resume.js +52 -0
  35. package/dist/loop/run.d.ts +5 -0
  36. package/dist/loop/run.js +264 -0
  37. package/dist/loop/state.d.ts +8 -0
  38. package/dist/loop/state.js +218 -0
  39. package/dist/{step → loop/step}/canonicalize.d.ts +2 -2
  40. package/dist/{step → loop/step}/canonicalize.js +2 -2
  41. package/dist/{step → loop/step}/compose.d.ts +3 -2
  42. package/dist/{step → loop/step}/compose.js +8 -5
  43. package/dist/loop/step/context-draft.d.ts +8 -0
  44. package/dist/{step → loop/step}/context-draft.js +5 -22
  45. package/dist/loop/step/describe.d.ts +9 -0
  46. package/dist/loop/step/describe.js +22 -0
  47. package/dist/loop/step/dynamics.d.ts +29 -0
  48. package/dist/loop/step/dynamics.js +147 -0
  49. package/dist/{step → loop/step}/model-configuration.d.ts +4 -3
  50. package/dist/{step → loop/step}/model-configuration.js +3 -3
  51. package/dist/loop/step/resolve.d.ts +11 -0
  52. package/dist/{step → loop/step}/resolve.js +5 -3
  53. package/dist/loop/step/run.d.ts +30 -0
  54. package/dist/{step → loop/step}/run.js +104 -53
  55. package/dist/loop/step/runtime.d.ts +40 -0
  56. package/dist/{step → loop/step}/seal.d.ts +13 -8
  57. package/dist/{step → loop/step}/seal.js +8 -6
  58. package/dist/{step → loop/step}/slot-assembly.d.ts +4 -4
  59. package/dist/{step → loop/step}/slot-assembly.js +4 -4
  60. package/dist/{step → loop/step}/step-context.d.ts +11 -12
  61. package/dist/{step → loop/step}/step-context.js +5 -27
  62. package/dist/loop/tool-result.d.ts +4 -0
  63. package/dist/loop/tool-result.js +30 -0
  64. package/dist/loop/transcript.d.ts +2 -0
  65. package/dist/{session/seed.js → loop/transcript.js} +11 -41
  66. package/dist/loop/waits.d.ts +50 -0
  67. package/dist/loop/waits.js +11 -0
  68. package/dist/run/durable.d.ts +71 -0
  69. package/dist/run/durable.js +164 -0
  70. package/dist/run/index.d.ts +33 -0
  71. package/dist/run/index.js +38 -0
  72. package/dist/types/client.d.ts +88 -0
  73. package/dist/types/execution.d.ts +114 -0
  74. package/dist/utils/ids.d.ts +1 -0
  75. package/dist/utils/ids.js +11 -1
  76. package/package.json +18 -6
  77. package/dist/build/agent.d.ts +0 -17
  78. package/dist/build/agent.js +0 -32
  79. package/dist/build/assemble.d.ts +0 -7
  80. package/dist/build/assemble.js +0 -41
  81. package/dist/build/bind-tool.d.ts +0 -3
  82. package/dist/build/bind-tool.js +0 -18
  83. package/dist/build/builder.d.ts +0 -38
  84. package/dist/build/builder.js +0 -136
  85. package/dist/build/helpers.d.ts +0 -6
  86. package/dist/build/helpers.js +0 -4
  87. package/dist/build/manifest.d.ts +0 -7
  88. package/dist/build/manifest.js +0 -20
  89. package/dist/build/schema.d.ts +0 -14
  90. package/dist/build/schema.js +0 -205
  91. package/dist/errors.d.ts +0 -17
  92. package/dist/errors.js +0 -17
  93. package/dist/session/capability-state.d.ts +0 -14
  94. package/dist/session/capability-state.js +0 -67
  95. package/dist/session/event-log.d.ts +0 -12
  96. package/dist/session/event-log.js +0 -63
  97. package/dist/session/input-queue.d.ts +0 -41
  98. package/dist/session/input-queue.js +0 -94
  99. package/dist/session/output-contract.d.ts +0 -6
  100. package/dist/session/output-contract.js +0 -12
  101. package/dist/session/record.d.ts +0 -11
  102. package/dist/session/record.js +0 -26
  103. package/dist/session/scheduler.d.ts +0 -62
  104. package/dist/session/scheduler.js +0 -475
  105. package/dist/session/seed.d.ts +0 -10
  106. package/dist/session/session.d.ts +0 -17
  107. package/dist/session/session.js +0 -101
  108. package/dist/session/state.d.ts +0 -15
  109. package/dist/session/state.js +0 -48
  110. package/dist/session/submission-stream.d.ts +0 -16
  111. package/dist/session/submission-stream.js +0 -46
  112. package/dist/step/context-draft.d.ts +0 -10
  113. package/dist/step/resolve.d.ts +0 -10
  114. package/dist/step/run.d.ts +0 -33
  115. package/dist/turn/plan-runner.d.ts +0 -72
  116. package/dist/turn/plan-runner.js +0 -389
  117. package/dist/turn/runner.d.ts +0 -69
  118. package/dist/turn/runner.js +0 -159
  119. package/dist/types/manifest.d.ts +0 -19
  120. package/dist/types/middleware.d.ts +0 -95
  121. package/dist/types/model.d.ts +0 -184
  122. package/dist/types/session.d.ts +0 -230
  123. package/dist/types/session.js +0 -1
  124. package/dist/types/shared.d.ts +0 -249
  125. package/dist/types/shared.js +0 -1
  126. package/dist/types/tool.d.ts +0 -139
  127. package/dist/types/tool.js +0 -1
  128. package/dist/utils/immutable.d.ts +0 -5
  129. package/dist/utils/immutable.js +0 -68
  130. package/dist/utils/maps.d.ts +0 -1
  131. package/dist/utils/maps.js +0 -37
  132. package/dist/utils/observe.d.ts +0 -3
  133. package/dist/utils/observe.js +0 -13
  134. /package/dist/{model → loop/model}/prepared.js +0 -0
  135. /package/dist/{types/manifest.js → loop/step/runtime.js} +0 -0
  136. /package/dist/types/{middleware.js → client.js} +0 -0
  137. /package/dist/types/{model.js → execution.js} +0 -0
@@ -0,0 +1,40 @@
1
+ import type { ToolRegistry } from "../../definition/registry.js";
2
+ import type { AgentDefinition } from "../../definition/agent-definition.js";
3
+ import type { ModelAdapter } from "@nylorun/core/define";
4
+ import type { InputEvent, TranscriptEntry } from "@nylorun/core/define";
5
+ import type { ToolResult } from "@nylorun/core/define";
6
+ import type { BoundMiddleware } from "@nylorun/core/define";
7
+ import type { JsonValue } from "@nylorun/core/define";
8
+ /** Compiled definition pieces plus the per-run model adapter for one step. */
9
+ export interface StepRuntime {
10
+ readonly middleware: readonly BoundMiddleware[];
11
+ readonly invoke: ModelAdapter;
12
+ readonly registry: ToolRegistry;
13
+ /** Full definition for before/after dynamics (optional for middleware-only agents). */
14
+ readonly definition?: AgentDefinition;
15
+ /** Mutable session memory bag shared with tools. */
16
+ sessionState?: Record<string, JsonValue>;
17
+ /** Called after Patch/Decision applied so the host can record. */
18
+ recordAfterDynamics?: () => Promise<void>;
19
+ }
20
+ export interface StepInput {
21
+ readonly executionId: string;
22
+ readonly turnId: string;
23
+ readonly stepId: string;
24
+ /** One-based position of this turn within the execution. */
25
+ readonly turnNumber: number;
26
+ /** One-based position of this Model Step within the Turn. */
27
+ readonly stepNumber: number;
28
+ readonly info?: unknown;
29
+ readonly arrivals: readonly InputEvent[];
30
+ readonly toolResults: readonly ToolResult[];
31
+ readonly transcript: readonly TranscriptEntry[];
32
+ }
33
+ /** Internal view consumed by a single model step. */
34
+ export interface ExecutionSnapshot {
35
+ readonly id: string;
36
+ readonly status: "running";
37
+ readonly turnCount: number;
38
+ readonly revision: number;
39
+ readonly transcript: readonly TranscriptEntry[];
40
+ }
@@ -1,9 +1,17 @@
1
- import type { ModelCandidate } from "../types/model.js";
2
- import type { Tripwire } from "../types/shared.js";
3
- import type { BoundToolDefinition, RequiredInteraction, SealedToolCall, ToolOwner, ToolResult } from "../types/tool.js";
1
+ import type { BoundToolDefinition } from "@nylorun/core/define";
2
+ import type { ModelCandidate } from "@nylorun/core/define";
3
+ import type { JsonValue, Tripwire } from "@nylorun/core/define";
4
+ import type { RequiredInteraction, ToolOwner, ToolResult } from "@nylorun/core/define";
4
5
  import type { StepContext } from "./step-context.js";
5
- import type { TurnOutputContract } from "../session/output-contract.js";
6
+ import type { TurnOutputContract } from "@nylorun/core/define";
7
+ export interface SealedToolCall {
8
+ readonly callId: string;
9
+ readonly toolName: string;
10
+ readonly args: JsonValue;
11
+ }
6
12
  export interface ExecutablePlanEntry {
13
+ readonly definition: BoundToolDefinition;
14
+ readonly rawArgs: JsonValue;
7
15
  readonly call: SealedToolCall;
8
16
  readonly invocationId: string;
9
17
  readonly owner: ToolOwner;
@@ -25,10 +33,7 @@ export type SealedStepOutput = {
25
33
  readonly tripwire: Tripwire;
26
34
  } | {
27
35
  readonly kind: "final";
28
- readonly output: import("../types/shared.js").JsonValue;
29
- } | {
30
- readonly kind: "deferred-model";
31
- readonly active: import("../types/session.js").ActiveModelExecutionRecord;
36
+ readonly output: JsonValue;
32
37
  } | {
33
38
  readonly kind: "tools";
34
39
  readonly plan: InternalToolPlan;
@@ -1,7 +1,7 @@
1
1
  import { textFromOutput } from "../model/normalize.js";
2
- import { createId } from "../utils/ids.js";
3
- import { HarnessError, isHarnessError } from "../errors.js";
4
- import { assertJson, copyJson } from "../utils/immutable.js";
2
+ import { createId } from "../../utils/ids.js";
3
+ import { HarnessError, isHarnessError } from "@nylorun/core/define";
4
+ import { assertJson, copyJson } from "@nylorun/core/define";
5
5
  const failed = (callId, toolName, code, message, details) => Object.freeze({
6
6
  kind: "failed",
7
7
  callId,
@@ -14,8 +14,6 @@ const failed = (callId, toolName, code, message, details) => Object.freeze({
14
14
  export function sealStep(context, outputContract) {
15
15
  if (context.currentTripwire)
16
16
  return Object.freeze({ kind: "tripwire", tripwire: context.currentTripwire });
17
- if (context.currentModelDeferred)
18
- return Object.freeze({ kind: "deferred-model", active: context.currentModelDeferred });
19
17
  const calls = context.canonicalCalls();
20
18
  if (!calls.length) {
21
19
  if (outputContract)
@@ -45,7 +43,9 @@ function sealStructuredOutput(context, contract) {
45
43
  return invalidOutput("Structured terminal output must contain exactly one JSON block and no text blocks");
46
44
  const validation = contract.schema.validate(json[0].value);
47
45
  if (!validation.ok)
48
- return invalidOutput(`Structured terminal output failed validation: ${validation.issues.map(renderIssue).join("; ")}`);
46
+ return invalidOutput(`Structured terminal output failed validation: ${validation.issues
47
+ .map(renderIssue)
48
+ .join("; ")}`);
49
49
  try {
50
50
  assertJson(validation.value, "structured terminal output");
51
51
  return Object.freeze({ kind: "final", output: copyJson(validation.value) });
@@ -113,6 +113,8 @@ function sealCall(context, catalog, candidate) {
113
113
  order,
114
114
  canonical,
115
115
  executable: Object.freeze({
116
+ definition: tool,
117
+ rawArgs: copyJson(candidate.args),
116
118
  call: Object.freeze({
117
119
  callId: candidate.id,
118
120
  toolName: candidate.name,
@@ -1,4 +1,4 @@
1
- import { type HarnessErrorCode } from "../errors.js";
1
+ import { type HarnessErrorCode } from "@nylorun/core/define";
2
2
  export interface SlotOwner {
3
3
  readonly middlewareId: string;
4
4
  readonly middlewareOrder: number;
@@ -11,9 +11,9 @@ export interface SlotDeclaration<Value> {
11
11
  readonly reason?: string;
12
12
  }
13
13
  /**
14
- * Shared per-step slot store. It has no session lifetime: declarations are discarded with the
15
- * draft that owns this instance. Configuration and context supply their typed values and snapshot
16
- * them after this assembler has applied ownership, replacement, and canonical ordering.
14
+ * Shared per-step slot store. Declarations are discarded with the draft that owns this instance.
15
+ * Configuration and context supply their typed values and snapshot them after this assembler has
16
+ * applied ownership, replacement, and canonical ordering.
17
17
  */
18
18
  export declare class SlotDraft<Value> {
19
19
  #private;
@@ -1,8 +1,8 @@
1
- import { HarnessError } from "../errors.js";
1
+ import { HarnessError } from "@nylorun/core/define";
2
2
  /**
3
- * Shared per-step slot store. It has no session lifetime: declarations are discarded with the
4
- * draft that owns this instance. Configuration and context supply their typed values and snapshot
5
- * them after this assembler has applied ownership, replacement, and canonical ordering.
3
+ * Shared per-step slot store. Declarations are discarded with the draft that owns this instance.
4
+ * Configuration and context supply their typed values and snapshot them after this assembler has
5
+ * applied ownership, replacement, and canonical ordering.
6
6
  */
7
7
  export class SlotDraft {
8
8
  #slots = new Map();
@@ -1,35 +1,34 @@
1
- import type { ContextSnapshot, ModelCandidate, ModelDirective, ModelConfigurationSnapshot } from "../types/model.js";
2
- import type { StepInput, StepRequest, StepResponse } from "../types/middleware.js";
3
- import type { Tripwire } from "../types/shared.js";
4
- import type { ActiveModelExecutionRecord } from "../types/session.js";
5
- import type { ObserveEmit } from "../utils/observe.js";
6
- import type { BoundToolDefinition, Interaction } from "../types/tool.js";
1
+ import type { BoundToolDefinition } from "@nylorun/core/define";
2
+ import type { StepInput } from "./runtime.js";
3
+ import type { InternalModelConfigurationSnapshot } from "./describe.js";
4
+ import type { ContextSnapshot, ModelCandidate, ModelDirective } from "@nylorun/core/define";
5
+ import type { StepRequest, StepResponse } from "@nylorun/core/define";
6
+ import type { Tripwire } from "@nylorun/core/define";
7
+ import type { ObserveEmit } from "../observe.js";
8
+ import type { Interaction } from "@nylorun/core/define";
7
9
  import { type CanonicalCall } from "./canonicalize.js";
8
10
  import { ContextDraft } from "./context-draft.js";
9
11
  import { ModelConfigurationDraft } from "./model-configuration.js";
10
- import type { CapabilityStateRegistry } from "../session/capability-state.js";
11
12
  export declare function isBrandedResponse(value: unknown): value is StepResponse;
12
13
  /** Mutable Step state. Middleware receives leased request views and one branded response. */
13
14
  export declare class StepContext {
14
15
  #private;
15
16
  readonly input: Readonly<StepInput>;
16
- constructor(input: Readonly<StepInput>, observe: ObserveEmit, configuration: ModelConfigurationDraft, context: ContextDraft, states?: CapabilityStateRegistry);
17
+ constructor(input: Readonly<StepInput>, observe: ObserveEmit, configuration: ModelConfigurationDraft, context: ContextDraft);
17
18
  get currentTripwire(): Tripwire | undefined;
18
19
  get currentCandidate(): Readonly<ModelCandidate> | undefined;
19
- get currentModelDeferred(): ActiveModelExecutionRecord | undefined;
20
20
  get selectedDirective(): ModelDirective | undefined;
21
21
  get instructions(): readonly string[];
22
22
  contextSnapshot(): ContextSnapshot;
23
23
  get offeredTools(): readonly BoundToolDefinition[];
24
24
  get catalogByName(): ReadonlyMap<string, BoundToolDefinition>;
25
- configurationSnapshot(): ModelConfigurationSnapshot;
26
- sealConfiguration(snapshot: ModelConfigurationSnapshot): void;
25
+ configurationSnapshot(): InternalModelConfigurationSnapshot;
26
+ sealConfiguration(snapshot: InternalModelConfigurationSnapshot): void;
27
27
  sealContext(snapshot: ContextSnapshot): void;
28
28
  denialFor(callId: string): string | undefined;
29
29
  interactionFor(callId: string): Interaction | undefined;
30
30
  canonicalCalls(): readonly CanonicalCall[];
31
31
  mintFromModel(candidate: Readonly<ModelCandidate>): StepResponse;
32
- deferModel(active: ActiveModelExecutionRecord): StepResponse;
33
32
  tripwire(error: Tripwire): StepResponse;
34
33
  seal(): void;
35
34
  requestFacade(middlewareId: string, middlewareOrder: number): {
@@ -1,4 +1,5 @@
1
- import { HarnessError, isHarnessError } from "../errors.js";
1
+ import { HarnessError, isHarnessError } from "@nylorun/core/define";
2
+ import { freezeGraph } from "@nylorun/core/define";
2
3
  import { callsFromCanonical, candidateFromCanonical, canonicalizeOutput, identityKey, } from "./canonicalize.js";
3
4
  import { normalizeCandidate } from "../model/normalize.js";
4
5
  import { ContextDraft } from "./context-draft.js";
@@ -13,24 +14,21 @@ export class StepContext {
13
14
  #observe;
14
15
  #context;
15
16
  #configuration;
16
- #states;
17
17
  #denials = new Map();
18
18
  #interactions = new Map();
19
19
  #identities = new Map();
20
20
  #canonical = Object.freeze([]);
21
21
  #candidate;
22
22
  #tripwire;
23
- #modelDeferred;
24
23
  #response;
25
24
  #sealed = false;
26
25
  #configurationSnapshot;
27
26
  #contextSnapshot;
28
- constructor(input, observe, configuration, context, states) {
27
+ constructor(input, observe, configuration, context) {
29
28
  this.input = input;
30
29
  this.#observe = observe;
31
30
  this.#configuration = configuration;
32
31
  this.#context = context;
33
- this.#states = states;
34
32
  }
35
33
  get currentTripwire() {
36
34
  return this.#tripwire;
@@ -38,9 +36,6 @@ export class StepContext {
38
36
  get currentCandidate() {
39
37
  return this.#candidate;
40
38
  }
41
- get currentModelDeferred() {
42
- return this.#modelDeferred;
43
- }
44
39
  get selectedDirective() {
45
40
  return this.configurationSnapshot().model;
46
41
  }
@@ -83,10 +78,6 @@ export class StepContext {
83
78
  this.#identities.set(call.id, identityKey(call.name, call.args));
84
79
  return this.#ensureResponse();
85
80
  }
86
- deferModel(active) {
87
- this.#modelDeferred = active;
88
- return this.#ensureResponse();
89
- }
90
81
  tripwire(error) {
91
82
  this.#tripwire ??= Object.freeze({ ...error });
92
83
  return this.#ensureResponse();
@@ -138,10 +129,10 @@ export class StepContext {
138
129
  }
139
130
  });
140
131
  const request = {
141
- sessionId: this.input.sessionId,
132
+ executionId: this.input.executionId,
142
133
  turnId: this.input.turnId,
143
134
  stepId: this.input.stepId,
144
- session: this.input.session,
135
+ info: this.input.info,
145
136
  turnNumber: this.input.turnNumber,
146
137
  stepNumber: this.input.stepNumber,
147
138
  arrivals: this.input.arrivals,
@@ -171,11 +162,6 @@ export class StepContext {
171
162
  return minted;
172
163
  },
173
164
  };
174
- if (this.#states?.has(middlewareId))
175
- Object.defineProperty(request, "state", {
176
- enumerable: true,
177
- get: () => this.#states.get(middlewareId),
178
- });
179
165
  const value = Object.freeze(request);
180
166
  return Object.freeze({
181
167
  value,
@@ -253,11 +239,3 @@ function pickSidecar(key, replacement, normalized, current) {
253
239
  const value = source?.[key];
254
240
  return value === undefined ? {} : { [key]: value };
255
241
  }
256
- function freezeGraph(value, seen = new WeakSet()) {
257
- if (!value || typeof value !== "object" || seen.has(value))
258
- return value;
259
- seen.add(value);
260
- for (const item of Object.values(value))
261
- freezeGraph(item, seen);
262
- return Object.freeze(value);
263
- }
@@ -0,0 +1,4 @@
1
+ import type { BoundToolDefinition } from "@nylorun/core/define";
2
+ import type { SavedToolCall } from "../types/execution.js";
3
+ import type { ToolOutcome, ToolResult } from "@nylorun/core/define";
4
+ export declare function toolResult(call: SavedToolCall, definition: BoundToolDefinition, outcome: ToolOutcome): ToolResult;
@@ -0,0 +1,30 @@
1
+ import { HarnessError } from "@nylorun/core/define";
2
+ import { copyJson } from "@nylorun/core/define";
3
+ export function toolResult(call, definition, outcome) {
4
+ const identity = { callId: call.callId, toolName: call.toolName };
5
+ if (outcome.kind === "completed") {
6
+ const checked = definition.outputSchema?.validate(outcome.output);
7
+ if (checked && !checked.ok)
8
+ return {
9
+ ...identity,
10
+ kind: "failed",
11
+ code: "tool.invalid-output",
12
+ message: checked.issues.map((issue) => issue.message).join("; "),
13
+ details: { phase: "output", issues: checked.issues },
14
+ };
15
+ return copyJson({
16
+ ...identity,
17
+ kind: "completed",
18
+ output: checked?.ok ? checked.value : outcome.output,
19
+ });
20
+ }
21
+ if (outcome.kind === "failed" &&
22
+ typeof outcome.code === "string" &&
23
+ typeof outcome.message === "string")
24
+ return copyJson({ ...identity, kind: "failed", code: outcome.code, message: outcome.message });
25
+ if (outcome.kind === "denied" && typeof outcome.reason !== "string")
26
+ throw new HarnessError("tool.invalid-tool-result", "Tool denial reason must be a string");
27
+ if (outcome.kind === "denied" && typeof outcome.reason === "string")
28
+ return copyJson({ ...identity, kind: "denied", reason: outcome.reason });
29
+ throw new HarnessError("tool.invalid-tool-result", "Expected a completed, failed, or denied tool result");
30
+ }
@@ -0,0 +1,2 @@
1
+ import type { TranscriptEntry } from "@nylorun/core/define";
2
+ export declare function validateTranscript(value: readonly TranscriptEntry[]): readonly TranscriptEntry[];
@@ -1,39 +1,20 @@
1
- import { HarnessError } from "../errors.js";
2
- import { normalizeCandidate } from "../model/normalize.js";
3
- import { assertJson, copyJson, copyJsonObject } from "../utils/immutable.js";
4
- export function normalizeSessionSeed(seed) {
1
+ import { HarnessError } from "@nylorun/core/define";
2
+ import { normalizeCandidate } from "./model/normalize.js";
3
+ import { assertJson, copyJson, copyJsonObject } from "@nylorun/core/define";
4
+ export function validateTranscript(value) {
5
5
  try {
6
- return normalizeSeed(seed);
6
+ if (!Array.isArray(value))
7
+ fail("Transcript must be an array");
8
+ return Object.freeze(value.map((entry, index) => entryAt(entry, index)));
7
9
  }
8
10
  catch (cause) {
9
- if (cause instanceof HarnessError && cause.code === "session.invalid-seed")
11
+ if (cause instanceof HarnessError && cause.code === "execution.invalid-state")
10
12
  throw cause;
11
- throw new HarnessError("session.invalid-seed", "Session seed contains invalid typed JSON", {
13
+ throw new HarnessError("execution.invalid-state", "Transcript contains invalid typed JSON", {
12
14
  cause,
13
15
  });
14
16
  }
15
17
  }
16
- function normalizeSeed(seed) {
17
- if (!seed || typeof seed !== "object")
18
- fail("Session seed must be an object");
19
- if (!Array.isArray(seed.transcript))
20
- fail("Session seed transcript must be an array");
21
- optionalString(seed.id, "Session seed id");
22
- optionalString(seed.userId, "Session seed userId");
23
- const turnCount = count(seed.turnCount, "turnCount");
24
- const revision = count(seed.revision, "revision");
25
- const transcript = Object.freeze(seed.transcript.map((entry, index) => entryAt(entry, index)));
26
- return Object.freeze({
27
- ...(seed.id === undefined ? {} : { id: seed.id }),
28
- ...(seed.userId === undefined ? {} : { userId: seed.userId }),
29
- ...(seed.context === undefined
30
- ? {}
31
- : { context: copyJsonObject(seed.context, "session seed context") }),
32
- turnCount,
33
- revision,
34
- transcript,
35
- });
36
- }
37
18
  function entryAt(value, index) {
38
19
  if (!value || typeof value !== "object")
39
20
  fail(`Transcript entry ${index} must be an object`);
@@ -61,7 +42,7 @@ function entryAt(value, index) {
61
42
  });
62
43
  }
63
44
  catch (cause) {
64
- throw new HarnessError("session.invalid-seed", `Invalid candidate at transcript ${index}`, {
45
+ throw new HarnessError("execution.invalid-state", `Invalid candidate at transcript ${index}`, {
65
46
  cause,
66
47
  });
67
48
  }
@@ -273,21 +254,10 @@ function exactKeys(value, allowed, label) {
273
254
  if (!allowed.includes(key))
274
255
  fail(`${label} has unknown field '${key}'`);
275
256
  }
276
- function count(value, label) {
277
- if (value === undefined)
278
- return 0;
279
- if (!Number.isSafeInteger(value) || value < 0)
280
- fail(`Session seed ${label} must be a non-negative safe integer`);
281
- return value;
282
- }
283
- function optionalString(value, label) {
284
- if (value !== undefined)
285
- requiredString(value, label);
286
- }
287
257
  function requiredString(value, label) {
288
258
  if (typeof value !== "string" || value.length === 0)
289
259
  fail(`${label} must be a non-empty string`);
290
260
  }
291
261
  function fail(message) {
292
- throw new HarnessError("session.invalid-seed", message);
262
+ throw new HarnessError("execution.invalid-state", message);
293
263
  }
@@ -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,71 @@
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
+ export declare function createDurableCheckpoint(input: {
54
+ manifest: AgentManifest;
55
+ sessionId: string;
56
+ turnId: string;
57
+ input: ExecutionInput;
58
+ state?: ExecutionState;
59
+ info?: JsonObject;
60
+ segment?: number;
61
+ }): DurableCheckpoint;
62
+ /** Deterministic reconstruction uses individually journaled effects, never a replayable whole loop.
63
+ * A suspended checkpoint is the immutable segment start plus the host's durable effect journal.
64
+ * Returning waiting releases all execution stacks; no process-local continuation survives.
65
+ */
66
+ export declare function runDurable(options: {
67
+ manifest: AgentManifest;
68
+ checkpoint: DurableCheckpoint;
69
+ host: DurableHost;
70
+ signal?: AbortSignal;
71
+ }): Promise<DurableResult>;