@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
@@ -1,9 +1,9 @@
1
1
  import { z } from "zod";
2
- import { HarnessError } from "../errors.js";
3
- import { assertJson, copyJson } from "../utils/immutable.js";
2
+ import { HarnessError } from "@nylorun/core/define";
3
+ import { assertJson, copyJson } from "@nylorun/core/define";
4
4
  import { definitionFor } from "../definition/agent-definition.js";
5
5
  import { initializeExecutionState } from "./initial-state.js";
6
- import { validateTranscript } from "../execution/transcript.js";
6
+ import { validateTranscript } from "./transcript.js";
7
7
  const id = z.string().min(1);
8
8
  const json = z.unknown().refine((value) => {
9
9
  try {
@@ -74,12 +74,21 @@ const call = z
74
74
  ])
75
75
  .optional(),
76
76
  token: json.optional(),
77
+ wait: z
78
+ .object({
79
+ kind: z.enum(["ask", "approve", "sleep", "waitFor"]),
80
+ waitId: id,
81
+ name: z.string().optional(),
82
+ })
83
+ .strict()
84
+ .optional(),
77
85
  })
78
86
  .strict();
79
87
  const schema = z
80
88
  .object({
81
89
  version: z.literal(1),
82
90
  agentId: id,
91
+ manifestHash: id.optional(),
83
92
  executionId: id,
84
93
  outputContract: z.record(z.string(), json).optional(),
85
94
  revision: z.number().int().nonnegative(),
@@ -87,6 +96,7 @@ const schema = z
87
96
  transcript: z.array(json),
88
97
  status: z.enum(["ready", "active", "completed", "paused", "cancelled", "failed"]),
89
98
  cancelledCalls: z.array(call).optional(),
99
+ state: z.record(z.string(), json).optional(),
90
100
  plan: z
91
101
  .object({
92
102
  turnId: id,
@@ -1,5 +1,5 @@
1
- import type { ModelCandidate, ModelOutputBlock, ModelToolCall } from "../../types/model.js";
2
- import type { JsonObject } from "../../types/shared.js";
1
+ import type { ModelCandidate, ModelOutputBlock, ModelToolCall } from "@nylorun/core/define";
2
+ import type { JsonObject } from "@nylorun/core/define";
3
3
  export interface CanonicalCall {
4
4
  readonly id: string;
5
5
  readonly name: string;
@@ -1,5 +1,5 @@
1
1
  import { createId } from "../../utils/ids.js";
2
- import { copyJson } from "../../utils/immutable.js";
2
+ import { copyJson } from "@nylorun/core/define";
3
3
  export function canonicalizeCalls(calls) {
4
4
  const idCounts = new Map();
5
5
  for (const call of calls) {
@@ -1,5 +1,5 @@
1
- import type { BoundMiddleware } from "../../definition/bound.js";
2
- import type { StepResponse } from "../../types/middleware.js";
1
+ import type { BoundMiddleware } from "@nylorun/core/define";
2
+ import type { StepResponse } from "@nylorun/core/define";
3
3
  import type { ObserveEmit } from "../observe.js";
4
4
  import { StepContext } from "./step-context.js";
5
5
  export declare function runMiddleware(middleware: readonly BoundMiddleware[], context: StepContext, terminal: () => Promise<StepResponse>, observe: ObserveEmit): Promise<StepResponse>;
@@ -1,4 +1,5 @@
1
- import { HarnessError, isHarnessError } from "../../errors.js";
1
+ import { HostSuspension } from "../host-suspension.js";
2
+ import { HarnessError, isHarnessError } from "@nylorun/core/define";
2
3
  import { isBrandedResponse, StepContext } from "./step-context.js";
3
4
  export async function runMiddleware(middleware, context, terminal, observe) {
4
5
  const dispatch = async (index) => {
@@ -49,6 +50,8 @@ export async function runMiddleware(middleware, context, terminal, observe) {
49
50
  handlerError ??= error;
50
51
  }
51
52
  }
53
+ if (handlerError instanceof HostSuspension)
54
+ throw handlerError;
52
55
  if (handlerError) {
53
56
  if (nextCalledTwice ||
54
57
  (isHarnessError(handlerError) && handlerError.code === "middleware.next-called-twice")) {
@@ -1,5 +1,5 @@
1
- import type { ContextMutationOptions, ContextSnapshot } from "../../types/model.js";
2
- import type { ContextItem } from "../../types/shared.js";
1
+ import type { ContextMutationOptions, ContextSnapshot } from "@nylorun/core/define";
2
+ import type { ContextItem } from "@nylorun/core/define";
3
3
  /** Per-step model-visible context supplied explicitly by middleware. */
4
4
  export declare class ContextDraft {
5
5
  #private;
@@ -1,5 +1,5 @@
1
- import { HarnessError } from "../../errors.js";
2
- import { copyJson } from "../../utils/immutable.js";
1
+ import { HarnessError } from "@nylorun/core/define";
2
+ import { copyJson } from "@nylorun/core/define";
3
3
  import { SlotDraft } from "./slot-assembly.js";
4
4
  const CONTEXT_TYPE = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
5
5
  /** Per-step model-visible context supplied explicitly by middleware. */
@@ -1,6 +1,6 @@
1
- import type { ModelConfigurationSnapshot } from "../../types/model.js";
2
- import type { ToolDescriptor } from "../../types/tool.js";
3
- import type { BoundToolDefinition } from "../../definition/bound.js";
1
+ import type { ModelConfigurationSnapshot } from "@nylorun/core/define";
2
+ import type { ToolDescriptor } from "@nylorun/core/define";
3
+ import type { BoundToolDefinition } from "@nylorun/core/define";
4
4
  /** Executable configuration stays inside the invocation. */
5
5
  export interface InternalModelConfigurationSnapshot extends Omit<ModelConfigurationSnapshot, "tools"> {
6
6
  readonly tools: readonly BoundToolDefinition[];
@@ -1,4 +1,4 @@
1
- import { copyJson } from "../../utils/immutable.js";
1
+ import { copyJson } from "@nylorun/core/define";
2
2
  export function describeTool(tool) {
3
3
  return copyJson({
4
4
  name: tool.name,
@@ -0,0 +1,29 @@
1
+ import type { AgentDefinition } from "../../definition/agent-definition.js";
2
+ import type { Decision } from "@nylorun/core/define";
3
+ import type { JsonValue } from "@nylorun/core/define";
4
+ import type { PromptItem } from "@nylorun/core/define";
5
+ import type { InputEvent } from "@nylorun/core/define";
6
+ import type { ModelConfigurationDraft } from "./model-configuration.js";
7
+ export interface DynamicsContext {
8
+ readonly agent: AgentDefinition;
9
+ readonly info?: unknown;
10
+ readonly step: number;
11
+ readonly arrivals: readonly InputEvent[];
12
+ readonly messages: readonly PromptItem[];
13
+ sessionState: Record<string, JsonValue>;
14
+ }
15
+ /** Run all beforeModelCall handlers in capability `.use()` order. */
16
+ export declare function applyBeforeModelCall(ctx: DynamicsContext, configuration: ModelConfigurationDraft): Promise<{
17
+ readonly blocked?: string;
18
+ }>;
19
+ /** Run all afterModelCall handlers; merge with strictest-wins rules. */
20
+ export declare function applyAfterModelCall(ctx: DynamicsContext, args: {
21
+ readonly text?: string;
22
+ readonly toolCalls: readonly {
23
+ readonly id: string;
24
+ readonly name: string;
25
+ readonly args: JsonValue;
26
+ }[];
27
+ }): Promise<Decision>;
28
+ export declare function mergeDecisions(decisions: readonly Decision[]): Decision;
29
+ export declare function hasDynamics(agent: AgentDefinition): boolean;
@@ -0,0 +1,149 @@
1
+ import { HarnessError } from "@nylorun/core/define";
2
+ /** Run all beforeModelCall handlers in capability `.use()` order. */
3
+ export async function applyBeforeModelCall(ctx, configuration) {
4
+ const patches = [];
5
+ for (const capability of ctx.agent.manifest.capabilities) {
6
+ const impl = ctx.agent.implementations[capability.id];
7
+ if (!impl?.beforeModelCall)
8
+ continue;
9
+ const raw = await impl.beforeModelCall({
10
+ input: projectInput(ctx.arrivals),
11
+ info: ctx.info,
12
+ state: { ...ctx.sessionState },
13
+ messages: ctx.messages,
14
+ step: ctx.step,
15
+ });
16
+ const patch = normalizeCapabilityPatch(capability.id, raw ?? {}, capability.id === "agent");
17
+ patches.push({ id: capability.id, patch, agentLevel: capability.id === "agent" });
18
+ }
19
+ if (!patches.length)
20
+ return {};
21
+ let blocked;
22
+ const capabilitySwitches = new Map();
23
+ const toolSwitches = new Map();
24
+ const extraInstructions = [];
25
+ for (const { id, patch, agentLevel } of patches) {
26
+ if (patch.block)
27
+ blocked = patch.block;
28
+ if (patch.state) {
29
+ for (const [key, value] of Object.entries(patch.state))
30
+ ctx.sessionState[key] = value;
31
+ }
32
+ if (patch.instructions)
33
+ extraInstructions.push(...patch.instructions);
34
+ if (patch.capabilities) {
35
+ for (const [name, on] of Object.entries(patch.capabilities)) {
36
+ if (!agentLevel && name !== id)
37
+ throw new HarnessError("configuration.invalid", `Capability '${id}' beforeModelCall may only toggle itself, not '${name}'`);
38
+ if (!ctx.agent.manifest.capabilities.some((item) => item.id === name))
39
+ throw new HarnessError("configuration.invalid", `beforeModelCall referenced unknown capability '${name}'`);
40
+ capabilitySwitches.set(name, on);
41
+ }
42
+ }
43
+ if (patch.tools) {
44
+ for (const [name, on] of Object.entries(patch.tools)) {
45
+ const known = ctx.agent.manifest.capabilities.some((cap) => cap.tools?.some((tool) => tool.name === name) ||
46
+ ctx.agent.implementations[cap.id]?.tools?.[name] !== undefined);
47
+ if (!known)
48
+ throw new HarnessError("configuration.invalid", `beforeModelCall referenced unknown tool '${name}'`);
49
+ if (!agentLevel) {
50
+ const declared = ctx.agent.manifest.capabilities
51
+ .find((cap) => cap.id === id)
52
+ ?.tools?.some((tool) => tool.name === name);
53
+ const owned = declared || ctx.agent.implementations[id]?.tools?.[name] !== undefined;
54
+ if (!owned)
55
+ throw new HarnessError("configuration.invalid", `Capability '${id}' beforeModelCall may only toggle its own tools`);
56
+ }
57
+ toolSwitches.set(name, on);
58
+ }
59
+ }
60
+ }
61
+ if (blocked)
62
+ return { blocked };
63
+ let order = 10_000;
64
+ for (const [capabilityId, on] of capabilitySwitches) {
65
+ if (on)
66
+ continue;
67
+ configuration.setTools("dynamics", order++, capabilityId, []);
68
+ }
69
+ for (const [toolName, on] of toolSwitches) {
70
+ if (on)
71
+ continue;
72
+ for (const capability of ctx.agent.manifest.capabilities) {
73
+ const tools = ctx.agent.implementations[capability.id]?.tools;
74
+ if (!tools?.[toolName])
75
+ continue;
76
+ const remaining = Object.values(tools).filter((tool) => tool.name !== toolName);
77
+ configuration.setTools("dynamics", order++, capability.id, remaining);
78
+ }
79
+ }
80
+ if (extraInstructions.length)
81
+ configuration.setInstructions("dynamics", order++, "dynamics", Object.freeze([...extraInstructions]));
82
+ return {};
83
+ }
84
+ /** Run all afterModelCall handlers; merge with strictest-wins rules. */
85
+ export async function applyAfterModelCall(ctx, args) {
86
+ const decisions = [];
87
+ for (const capability of ctx.agent.manifest.capabilities) {
88
+ const impl = ctx.agent.implementations[capability.id];
89
+ if (!impl?.afterModelCall)
90
+ continue;
91
+ const decision = await impl.afterModelCall(args, {
92
+ info: ctx.info,
93
+ state: { ...ctx.sessionState },
94
+ });
95
+ decisions.push(decision ?? {});
96
+ }
97
+ return mergeDecisions(decisions);
98
+ }
99
+ export function mergeDecisions(decisions) {
100
+ let block;
101
+ let retry;
102
+ const deny = new Map();
103
+ const approve = new Map();
104
+ let text;
105
+ for (const decision of decisions) {
106
+ if (decision.block)
107
+ block = decision.block;
108
+ if (decision.retry)
109
+ retry = decision.retry;
110
+ for (const item of decision.deny ?? [])
111
+ deny.set(item.id, item.reason);
112
+ for (const item of decision.approve ?? [])
113
+ approve.set(item.id, item.prompt);
114
+ if (decision.text !== undefined)
115
+ text = decision.text;
116
+ }
117
+ if (block)
118
+ return { block };
119
+ if (retry)
120
+ return { retry, ...(text === undefined ? {} : { text }) };
121
+ return {
122
+ ...(text === undefined ? {} : { text }),
123
+ ...(deny.size ? { deny: [...deny.entries()].map(([id, reason]) => ({ id, reason })) } : {}),
124
+ ...(approve.size
125
+ ? { approve: [...approve.entries()].map(([id, prompt]) => ({ id, prompt })) }
126
+ : {}),
127
+ };
128
+ }
129
+ export function hasDynamics(agent) {
130
+ return agent.manifest.capabilities.some((capability) => capability.beforeModelCall || capability.afterModelCall);
131
+ }
132
+ function normalizeCapabilityPatch(id, patch, agentLevel) {
133
+ if (patch.active === undefined)
134
+ return patch;
135
+ if (agentLevel)
136
+ throw new HarnessError("configuration.invalid", "Agent-level beforeModelCall must use capabilities{}, not active");
137
+ return {
138
+ ...patch,
139
+ capabilities: { ...(patch.capabilities ?? {}), [id]: patch.active },
140
+ };
141
+ }
142
+ function projectInput(arrivals) {
143
+ const message = arrivals.find((item) => item.kind === "user-message");
144
+ if (!message)
145
+ return undefined;
146
+ if ("text" in message && typeof message.text === "string")
147
+ return message.text;
148
+ return undefined;
149
+ }
@@ -1,6 +1,6 @@
1
1
  import type { InternalModelConfigurationSnapshot } from "./describe.js";
2
- import type { ModelConfigurationMutationOptions, ModelDirective } from "../../types/model.js";
3
- import type { ToolDefinition } from "../../types/tool.js";
2
+ import type { ModelConfigurationMutationOptions, ModelDirective } from "@nylorun/core/define";
3
+ import type { ToolDefinition } from "@nylorun/core/define";
4
4
  /** Per-step, middleware-owned model configuration draft. It never persists past the call. */
5
5
  export declare class ModelConfigurationDraft {
6
6
  #private;
@@ -1,7 +1,7 @@
1
- import { bindTool } from "../../definition/bind-tool.js";
2
- import { HarnessError, isHarnessError } from "../../errors.js";
1
+ import { bindTool } from "@nylorun/core/define";
2
+ import { HarnessError, isHarnessError } from "@nylorun/core/define";
3
3
  import { normalizeDirective, sameDirective } from "../model/normalize.js";
4
- import { copyJson } from "../../utils/immutable.js";
4
+ import { copyJson } from "@nylorun/core/define";
5
5
  import { checkedReason, slotOwner, SlotDraft } from "./slot-assembly.js";
6
6
  /** Per-step, middleware-owned model configuration draft. It never persists past the call. */
7
7
  export class ModelConfigurationDraft {
@@ -1,7 +1,7 @@
1
- import type { TurnOutputContract } from "../../definition/output-contract.js";
2
- import type { InputEvent } from "../../types/transcript.js";
3
- import type { ModelRequest } from "../../types/model.js";
4
- import type { ToolResult } from "../../types/tool.js";
1
+ import type { TurnOutputContract } from "@nylorun/core/define";
2
+ import type { InputEvent } from "@nylorun/core/define";
3
+ import type { ModelRequest } from "@nylorun/core/define";
4
+ import type { ToolResult } from "@nylorun/core/define";
5
5
  import type { StepContext } from "./step-context.js";
6
6
  export declare function resolveModelRequest(input: {
7
7
  context: StepContext;
@@ -1,8 +1,8 @@
1
1
  import type { ExecutionSnapshot, StepRuntime } from "./runtime.js";
2
- import type { TurnOutputContract } from "../../definition/output-contract.js";
3
- import type { ModelCandidate } from "../../types/model.js";
4
- import type { InputEvent } from "../../types/transcript.js";
5
- import type { ToolResult } from "../../types/tool.js";
2
+ import type { TurnOutputContract } from "@nylorun/core/define";
3
+ import type { ModelCandidate } from "@nylorun/core/define";
4
+ import type { InputEvent } from "@nylorun/core/define";
5
+ import type { ToolResult } from "@nylorun/core/define";
6
6
  import type { ObserveEmit } from "../observe.js";
7
7
  import { type SealedStepOutput } from "./seal.js";
8
8
  export interface StepRunResult {
@@ -11,6 +11,7 @@ export interface StepRunResult {
11
11
  readonly output: SealedStepOutput;
12
12
  readonly requestedModelId?: string;
13
13
  readonly modelInvocationId?: string;
14
+ readonly retry?: string;
14
15
  }
15
16
  export declare function runStep(input: {
16
17
  agent: StepRuntime;
@@ -1,6 +1,7 @@
1
+ import { HostSuspension } from "../host-suspension.js";
1
2
  import { normalizeCandidate } from "../model/normalize.js";
2
- import { HarnessError, isHarnessError } from "../../errors.js";
3
- import { copyJson } from "../../utils/immutable.js";
3
+ import { HarnessError, isHarnessError } from "@nylorun/core/define";
4
+ import { copyJson } from "@nylorun/core/define";
4
5
  import { runMiddleware } from "./compose.js";
5
6
  import { ContextDraft } from "./context-draft.js";
6
7
  import { StepContext } from "./step-context.js";
@@ -9,9 +10,11 @@ import { resolveModelRequest } from "./resolve.js";
9
10
  import { sealStep } from "./seal.js";
10
11
  import { ModelConfigurationDraft } from "./model-configuration.js";
11
12
  import { createId } from "../../utils/ids.js";
13
+ import { applyAfterModelCall, applyBeforeModelCall, hasDynamics, } from "./dynamics.js";
12
14
  export async function runStep(input) {
13
15
  let requestedModelId;
14
16
  let modelInvocationId;
17
+ let retryFeedback;
15
18
  const stepInput = Object.freeze({
16
19
  executionId: input.executionId,
17
20
  turnId: input.turnId,
@@ -38,6 +41,46 @@ export async function runStep(input) {
38
41
  input.signal.throwIfAborted();
39
42
  if (context.currentTripwire)
40
43
  return context.tripwire(context.currentTripwire);
44
+ const definition = input.agent.definition;
45
+ const dynamicsCtx = definition && hasDynamics(definition)
46
+ ? {
47
+ agent: definition,
48
+ info: input.info,
49
+ step: input.stepNumber - 1,
50
+ arrivals: input.arrivals,
51
+ messages: projectModelCall(resolveModelRequest({
52
+ context,
53
+ arrivals: input.arrivals,
54
+ toolResults: input.toolResults,
55
+ output: input.output,
56
+ })).prompt,
57
+ sessionState: input.agent.sessionState ?? {},
58
+ }
59
+ : undefined;
60
+ if (dynamicsCtx) {
61
+ try {
62
+ const before = await applyBeforeModelCall(dynamicsCtx, configuration);
63
+ if (input.agent.sessionState) {
64
+ Object.assign(input.agent.sessionState, dynamicsCtx.sessionState);
65
+ }
66
+ if (input.agent.recordAfterDynamics)
67
+ await input.agent.recordAfterDynamics();
68
+ if (before.blocked) {
69
+ return context.tripwire({
70
+ code: "dynamics.blocked",
71
+ message: before.blocked,
72
+ });
73
+ }
74
+ }
75
+ catch (error) {
76
+ if (error instanceof HostSuspension)
77
+ throw error;
78
+ return context.tripwire({
79
+ code: isHarnessError(error) ? error.code : "configuration.invalid",
80
+ message: error instanceof Error ? error.message : String(error),
81
+ });
82
+ }
83
+ }
41
84
  try {
42
85
  const selected = configuration.snapshot();
43
86
  for (const tool of selected.tools)
@@ -46,6 +89,8 @@ export async function runStep(input) {
46
89
  context.sealContext(runtimeContext.snapshot());
47
90
  }
48
91
  catch (error) {
92
+ if (error instanceof HostSuspension)
93
+ throw error;
49
94
  const message = error instanceof Error ? error.message : String(error);
50
95
  return context.tripwire({
51
96
  code: isHarnessError(error) ? error.code : "configuration.invalid",
@@ -103,9 +148,48 @@ export async function runStep(input) {
103
148
  const candidate = context.currentCandidate;
104
149
  if (!candidate)
105
150
  throw new HarnessError("model.candidate-missing", "Model candidate missing after mint");
151
+ if (dynamicsCtx) {
152
+ const text = candidate.output
153
+ ?.filter((block) => block.type === "text")
154
+ .map((block) => block.text)
155
+ .join("");
156
+ const toolCalls = (candidate.output ?? [])
157
+ .filter((block) => block.type === "tool-call")
158
+ .map((block) => {
159
+ const call = block;
160
+ return { id: call.id, name: call.name, args: call.args };
161
+ });
162
+ const decision = await applyAfterModelCall(dynamicsCtx, {
163
+ ...(text ? { text } : {}),
164
+ toolCalls,
165
+ });
166
+ if (input.agent.recordAfterDynamics)
167
+ await input.agent.recordAfterDynamics();
168
+ if (decision.block) {
169
+ return context.tripwire({ code: "dynamics.blocked", message: decision.block });
170
+ }
171
+ if (decision.retry) {
172
+ retryFeedback = decision.retry;
173
+ return context.tripwire({ code: "dynamics.retry", message: decision.retry });
174
+ }
175
+ if (decision.text !== undefined) {
176
+ minted.replace({
177
+ output: [{ type: "text", text: decision.text }],
178
+ });
179
+ }
180
+ for (const item of decision.deny ?? [])
181
+ minted.deny(item.id, item.reason);
182
+ for (const item of decision.approve ?? [])
183
+ minted.requireInteraction(item.id, {
184
+ kind: "approval",
185
+ prompt: item.prompt,
186
+ });
187
+ }
106
188
  return minted;
107
189
  }
108
190
  catch (error) {
191
+ if (error instanceof HostSuspension)
192
+ throw error;
109
193
  if (input.signal.aborted)
110
194
  throw input.signal.reason;
111
195
  return context.tripwire({
@@ -122,6 +206,7 @@ export async function runStep(input) {
122
206
  ...(requestedModelId === undefined ? {} : { requestedModelId }),
123
207
  output,
124
208
  ...(modelInvocationId ? { modelInvocationId } : {}),
209
+ ...(retryFeedback ? { retry: retryFeedback } : {}),
125
210
  });
126
211
  }
127
212
  function snapshotStepStart(input) {
@@ -1,13 +1,21 @@
1
1
  import type { ToolRegistry } from "../../definition/registry.js";
2
- import type { ModelAdapter } from "../../types/model.js";
3
- import type { InputEvent, TranscriptEntry } from "../../types/transcript.js";
4
- import type { ToolResult } from "../../types/tool.js";
5
- import type { BoundMiddleware } from "../../definition/bound.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";
6
8
  /** Compiled definition pieces plus the per-run model adapter for one step. */
7
9
  export interface StepRuntime {
8
10
  readonly middleware: readonly BoundMiddleware[];
9
11
  readonly invoke: ModelAdapter;
10
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>;
11
19
  }
12
20
  export interface StepInput {
13
21
  readonly executionId: string;
@@ -1,9 +1,9 @@
1
- import type { BoundToolDefinition } from "../../definition/bound.js";
2
- import type { ModelCandidate } from "../../types/model.js";
3
- import type { JsonValue, Tripwire } from "../../types/shared.js";
4
- import type { RequiredInteraction, 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";
5
5
  import type { StepContext } from "./step-context.js";
6
- import type { TurnOutputContract } from "../../definition/output-contract.js";
6
+ import type { TurnOutputContract } from "@nylorun/core/define";
7
7
  export interface SealedToolCall {
8
8
  readonly callId: string;
9
9
  readonly toolName: string;
@@ -1,7 +1,7 @@
1
1
  import { textFromOutput } from "../model/normalize.js";
2
2
  import { createId } from "../../utils/ids.js";
3
- import { HarnessError, isHarnessError } from "../../errors.js";
4
- import { assertJson, copyJson } from "../../utils/immutable.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,
@@ -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;
@@ -1,4 +1,4 @@
1
- import { HarnessError } from "../../errors.js";
1
+ import { HarnessError } from "@nylorun/core/define";
2
2
  /**
3
3
  * Shared per-step slot store. Declarations are discarded with the draft that owns this instance.
4
4
  * Configuration and context supply their typed values and snapshot them after this assembler has
@@ -1,11 +1,11 @@
1
- import type { BoundToolDefinition } from "../../definition/bound.js";
1
+ import type { BoundToolDefinition } from "@nylorun/core/define";
2
2
  import type { StepInput } from "./runtime.js";
3
3
  import type { InternalModelConfigurationSnapshot } from "./describe.js";
4
- import type { ContextSnapshot, ModelCandidate, ModelDirective } from "../../types/model.js";
5
- import type { StepRequest, StepResponse } from "../../types/middleware.js";
6
- import type { Tripwire } from "../../types/shared.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
7
  import type { ObserveEmit } from "../observe.js";
8
- import type { Interaction } from "../../types/tool.js";
8
+ import type { Interaction } from "@nylorun/core/define";
9
9
  import { type CanonicalCall } from "./canonicalize.js";
10
10
  import { ContextDraft } from "./context-draft.js";
11
11
  import { ModelConfigurationDraft } from "./model-configuration.js";
@@ -1,5 +1,5 @@
1
- import { HarnessError, isHarnessError } from "../../errors.js";
2
- import { freezeGraph } from "../../utils/immutable.js";
1
+ import { HarnessError, isHarnessError } from "@nylorun/core/define";
2
+ import { freezeGraph } from "@nylorun/core/define";
3
3
  import { callsFromCanonical, candidateFromCanonical, canonicalizeOutput, identityKey, } from "./canonicalize.js";
4
4
  import { normalizeCandidate } from "../model/normalize.js";
5
5
  import { ContextDraft } from "./context-draft.js";
@@ -1,4 +1,4 @@
1
- import type { BoundToolDefinition } from "../definition/bound.js";
1
+ import type { BoundToolDefinition } from "@nylorun/core/define";
2
2
  import type { SavedToolCall } from "../types/execution.js";
3
- import type { ToolOutcome, ToolResult } from "../types/tool.js";
3
+ import type { ToolOutcome, ToolResult } from "@nylorun/core/define";
4
4
  export declare function toolResult(call: SavedToolCall, definition: BoundToolDefinition, outcome: ToolOutcome): ToolResult;
@@ -1,5 +1,5 @@
1
- import { HarnessError } from "../errors.js";
2
- import { copyJson } from "../utils/immutable.js";
1
+ import { HarnessError } from "@nylorun/core/define";
2
+ import { copyJson } from "@nylorun/core/define";
3
3
  export function toolResult(call, definition, outcome) {
4
4
  const identity = { callId: call.callId, toolName: call.toolName };
5
5
  if (outcome.kind === "completed") {
@@ -1,2 +1,2 @@
1
- import type { TranscriptEntry } from "../types/transcript.js";
1
+ import type { TranscriptEntry } from "@nylorun/core/define";
2
2
  export declare function validateTranscript(value: readonly TranscriptEntry[]): readonly TranscriptEntry[];
@@ -1,6 +1,6 @@
1
- import { HarnessError } from "../errors.js";
1
+ import { HarnessError } from "@nylorun/core/define";
2
2
  import { normalizeCandidate } from "./model/normalize.js";
3
- import { assertJson, copyJson, copyJsonObject } from "../utils/immutable.js";
3
+ import { assertJson, copyJson, copyJsonObject } from "@nylorun/core/define";
4
4
  export function validateTranscript(value) {
5
5
  try {
6
6
  if (!Array.isArray(value))