@nylorun/harness 0.8.0-beta.1 → 0.11.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/README.md +5 -38
- package/dist/build/assemble.js +1 -0
- package/dist/build/bind-tool.js +4 -3
- package/dist/build/builder.js +26 -0
- package/dist/build/helpers.d.ts +2 -2
- package/dist/build/manifest.js +12 -1
- package/dist/build/schema.d.ts +11 -16
- package/dist/build/schema.js +141 -22
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +2 -0
- package/dist/model/adapters.d.ts +160 -0
- package/dist/model/adapters.js +545 -0
- package/dist/{model-normalize.d.ts → model/normalize.d.ts} +2 -2
- package/dist/{model-normalize.js → model/normalize.js} +35 -4
- package/dist/model/prepared.d.ts +16 -0
- package/dist/model/prepared.js +11 -0
- package/dist/session/event-log.d.ts +4 -3
- package/dist/session/input-queue.d.ts +7 -4
- package/dist/session/input-queue.js +12 -0
- package/dist/session/output-contract.d.ts +6 -0
- package/dist/session/output-contract.js +12 -0
- package/dist/session/scheduler.js +1 -1
- package/dist/session/seed.js +79 -5
- package/dist/session/session.d.ts +8 -5
- package/dist/session/session.js +38 -2
- package/dist/session/state.d.ts +1 -1
- package/dist/session/submission-stream.d.ts +5 -4
- package/dist/step/context-draft.js +1 -7
- package/dist/step/model-configuration.js +6 -20
- package/dist/step/project.js +25 -3
- package/dist/step/resolve.d.ts +1 -0
- package/dist/step/resolve.js +1 -0
- package/dist/step/run.d.ts +1 -0
- package/dist/step/run.js +25 -4
- package/dist/step/seal.d.ts +4 -2
- package/dist/step/seal.js +64 -13
- package/dist/step/step-context.js +1 -1
- package/dist/turn/plan-runner.js +38 -3
- package/dist/turn/runner.d.ts +6 -4
- package/dist/turn/runner.js +6 -4
- package/dist/types/manifest.d.ts +5 -4
- package/dist/types/middleware.d.ts +10 -1
- package/dist/types/model.d.ts +21 -13
- package/dist/types/session.d.ts +34 -13
- package/dist/types/shared.d.ts +16 -3
- package/dist/types/tool.d.ts +54 -17
- package/package.json +15 -12
- package/dist/utils/digest.d.ts +0 -1
- package/dist/utils/digest.js +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,72 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0-beta
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 54ab304: Support portable Runtime protocol version 2 while retaining legacy Studio
|
|
8
|
+
manifest support. Move the Studio CLI into Runtime: use `nylorun studio
|
|
9
|
+
--agent-url <url>` instead of `nylo studio`. Applications should install Runtime
|
|
10
|
+
directly and keep Studio as a development dependency.
|
|
11
|
+
|
|
12
|
+
Retain session lists and media across development reloads, and recognize agent
|
|
13
|
+
file changes on Windows. Validate noninteractive creator startup during release
|
|
14
|
+
verification with deferred provider configuration.
|
|
15
|
+
|
|
16
|
+
Include Harness in this release so the creator does not rely on an unpublished
|
|
17
|
+
compatibility pin. Ship and verify all four packages together.
|
|
18
|
+
|
|
3
19
|
All notable changes to `@nylorun/harness` are documented in this file.
|
|
4
20
|
|
|
5
21
|
The project follows [Semantic Versioning](https://semver.org/). Before 1.0, the public API is experimental: breaking changes may occur in minor releases, while patch releases are reserved for compatible fixes.
|
|
6
22
|
|
|
23
|
+
## Unreleased
|
|
24
|
+
|
|
25
|
+
## [0.10.0-beta.1] - 2026-09-03
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Portable ordered media input parts with opaque JSON references preserved through sessions,
|
|
30
|
+
transcripts, middleware, model calls, and observations.
|
|
31
|
+
- `preparedModel()` for adapters that need to materialize a provider request while exposing the
|
|
32
|
+
JSON-safe derived call through the new `model.prepared` observation.
|
|
33
|
+
- Direct `inputSchema` and optional `outputSchema` tool contracts. Harness accepts Zod v4,
|
|
34
|
+
synchronous Standard Schema values with JSON Schema conversion, and explicit validator-backed
|
|
35
|
+
`defineSchema()` contracts.
|
|
36
|
+
- Structured input and output validation diagnostics on failed tool results.
|
|
37
|
+
- Per-turn `session.input({ ... }, { outputSchema })` contracts for locally validated terminal JSON
|
|
38
|
+
results, including canonical model-call projection, JSON candidates, immutable final persistence,
|
|
39
|
+
and `output.invalid` tripwires.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Bundled provider translators map direct URL image references and fail unsupported media with the
|
|
44
|
+
stable `model.unsupported-content` error rather than dropping or stringifying it.
|
|
45
|
+
- Chat Completions and Responses translators project terminal output schemas without choosing
|
|
46
|
+
provider strictness; Anthropic Messages reports `model.unsupported-output-schema` until an
|
|
47
|
+
application supplies a custom prepared adapter.
|
|
48
|
+
- Tool definitions use `inputSchema` instead of `parameters`. Completed output is validated when an
|
|
49
|
+
`outputSchema` is supplied; the resulting JSON is the same value recorded, observed, and sent to
|
|
50
|
+
the model. Model configuration and `model.requested` observations expose optional output schemas.
|
|
51
|
+
|
|
52
|
+
### Breaking changes
|
|
53
|
+
|
|
54
|
+
- Replace every tool `parameters` field with `inputSchema`. `outputSchema` is now an optional tool
|
|
55
|
+
contract and completed tool output may be any JSON value, not only a string.
|
|
56
|
+
- Terminal results, final events, and stream projections may now be JSON values. Consumers that
|
|
57
|
+
assumed string output must render or serialize `JsonValue` safely.
|
|
58
|
+
- Custom model adapters receive the `reportPreparedCall` context method. Adapters that materialize
|
|
59
|
+
provider requests should use it (or `preparedModel()`) to emit one JSON-safe derived request.
|
|
60
|
+
|
|
61
|
+
## [0.9.0-beta.1] - 2026-09-02
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- Public provider translator helpers under `@nylorun/harness/model/adapters` for OpenAI-compatible
|
|
66
|
+
Chat Completions, OpenAI Responses, and Anthropic Messages model loops.
|
|
67
|
+
- `MiddlewareManifest` and declared middleware contributions in `AgentManifest`, including static
|
|
68
|
+
instructions, tool metadata, and model controls for host tooling such as Studio.
|
|
69
|
+
|
|
7
70
|
## [0.8.0-beta.1] - 2026-08-31
|
|
8
71
|
|
|
9
72
|
### Breaking changes
|
|
@@ -93,3 +156,4 @@ The project follows [Semantic Versioning](https://semver.org/). Before 1.0, the
|
|
|
93
156
|
[0.6.0-beta.1]: https://github.com/nylorun/harness/tree/main/harness
|
|
94
157
|
[0.7.0-beta.1]: https://github.com/nylorun/harness/tree/main/harness
|
|
95
158
|
[0.8.0-beta.1]: https://github.com/nylorun/harness/tree/main/harness
|
|
159
|
+
[0.9.0-beta.1]: https://github.com/nylorun/harness/tree/main/harness
|
package/README.md
CHANGED
|
@@ -1,43 +1,10 @@
|
|
|
1
1
|
# `@nylorun/harness`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
an agent from a model adapter and named capabilities; keep provider clients, stores, HTTP, and
|
|
5
|
-
credentials in ordinary application code.
|
|
3
|
+
Nylorun's TypeScript agent runtime.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
import { Agent, model, tool } from "@nylorun/harness";
|
|
11
|
-
import { z } from "zod";
|
|
12
|
-
|
|
13
|
-
const adapter = model(async (call) => provider.complete(call));
|
|
14
|
-
const echo = tool({
|
|
15
|
-
name: "echo",
|
|
16
|
-
description: "Echo text",
|
|
17
|
-
parameters: z.object({ text: z.string() }),
|
|
18
|
-
execute: async ({ text }) => ({ kind: "completed", output: text }),
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const agent = Agent({
|
|
22
|
-
id: "echo",
|
|
23
|
-
name: "Echo",
|
|
24
|
-
instructions: "Use echo when asked.",
|
|
25
|
-
})
|
|
26
|
-
.use({ id: "echo", tools: [echo] })
|
|
27
|
-
.with(adapter)
|
|
28
|
-
.build();
|
|
5
|
+
For installation, examples, API details, and release notes, see the
|
|
6
|
+
[Nylorun Harness repository](https://github.com/nylorun/harness).
|
|
29
7
|
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Design boundaries
|
|
34
|
-
|
|
35
|
-
- **Capabilities** declare model-visible tools, instructions, context, and candidate review through
|
|
36
|
-
`.use()`.
|
|
37
|
-
- **Services** are dependencies you construct and pass to capabilities, such as a store, MCP client,
|
|
38
|
-
or container runner.
|
|
39
|
-
- **Host code** owns transport, persistence, credentials, and background work.
|
|
40
|
-
- **Harness core** owns the loop, validation, scheduling, and recording invariants.
|
|
8
|
+
> **Experimental beta.** Install with `npm install @nylorun/harness@beta`.
|
|
41
9
|
|
|
42
|
-
|
|
43
|
-
release notes.
|
|
10
|
+
Repository development: [contributing](../CONTRIBUTING.md). Package publication: [releasing](../RELEASING.md).
|
package/dist/build/assemble.js
CHANGED
|
@@ -27,6 +27,7 @@ export function assembleAgent(middleware, invoke, identity) {
|
|
|
27
27
|
id: item.id,
|
|
28
28
|
handle: item.handle,
|
|
29
29
|
...(item.state === undefined ? {} : { state: item.state }),
|
|
30
|
+
...(item.contributions === undefined ? {} : { contributions: item.contributions }),
|
|
30
31
|
}));
|
|
31
32
|
}
|
|
32
33
|
}
|
package/dist/build/bind-tool.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { HarnessError } from "../errors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { normalizedSchemasFor } from "./schema.js";
|
|
3
3
|
/** Prepares an executable Tool and records its middleware slot provenance. */
|
|
4
4
|
export function bindTool(item, owner) {
|
|
5
5
|
if (!item.name)
|
|
6
6
|
throw new HarnessError("tool.invalid-name", "Tool name must not be empty");
|
|
7
7
|
if (typeof item.execute !== "function")
|
|
8
8
|
throw new HarnessError("tool.invalid", `Tool '${item.name}' must provide execute()`);
|
|
9
|
-
const
|
|
9
|
+
const schemas = normalizedSchemasFor(item);
|
|
10
10
|
return Object.freeze({
|
|
11
11
|
name: item.name,
|
|
12
12
|
...(item.description ? { description: item.description } : {}),
|
|
13
|
-
|
|
13
|
+
inputSchema: schemas.inputSchema,
|
|
14
|
+
...(schemas.outputSchema === undefined ? {} : { outputSchema: schemas.outputSchema }),
|
|
14
15
|
execute: item.execute.bind(item),
|
|
15
16
|
owner: Object.freeze({ ...owner }),
|
|
16
17
|
});
|
package/dist/build/builder.js
CHANGED
|
@@ -99,6 +99,7 @@ function compileDeclaration(declaration) {
|
|
|
99
99
|
const tools = copyItems(declaration.tools, declaration.id);
|
|
100
100
|
const instructions = copyItems(declaration.instructions, declaration.id);
|
|
101
101
|
const model = declaration.model;
|
|
102
|
+
const contributions = snapshotContributions(instructions?.items, tools?.items, model);
|
|
102
103
|
const handle = async (request, next) => {
|
|
103
104
|
if (tools)
|
|
104
105
|
request.configuration.tools.set(tools.slot, tools.items);
|
|
@@ -114,8 +115,33 @@ function compileDeclaration(declaration) {
|
|
|
114
115
|
...(declaration.state === undefined
|
|
115
116
|
? {}
|
|
116
117
|
: { state: declaration.state }),
|
|
118
|
+
...(contributions === undefined ? {} : { contributions }),
|
|
117
119
|
};
|
|
118
120
|
}
|
|
121
|
+
function snapshotContributions(instructions, tools, model) {
|
|
122
|
+
const snapInstructions = instructions === undefined ? undefined : Object.freeze([...instructions]);
|
|
123
|
+
const snapTools = tools === undefined
|
|
124
|
+
? undefined
|
|
125
|
+
: Object.freeze(tools.map((tool) => Object.freeze({
|
|
126
|
+
name: tool.name,
|
|
127
|
+
...(tool.description === undefined ? {} : { description: tool.description }),
|
|
128
|
+
})));
|
|
129
|
+
const snapModel = model === undefined ? undefined : snapshotModel(model);
|
|
130
|
+
if (snapInstructions === undefined && snapTools === undefined && snapModel === undefined) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return Object.freeze({
|
|
134
|
+
...(snapInstructions === undefined ? {} : { instructions: snapInstructions }),
|
|
135
|
+
...(snapTools === undefined ? {} : { tools: snapTools }),
|
|
136
|
+
...(snapModel === undefined ? {} : { model: snapModel }),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function snapshotModel(model) {
|
|
140
|
+
return Object.freeze({
|
|
141
|
+
...(model.id === undefined ? {} : { id: model.id }),
|
|
142
|
+
...(model.controls === undefined ? {} : { controls: Object.freeze({ ...model.controls }) }),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
119
145
|
function copyItems(value, defaultSlot) {
|
|
120
146
|
if (value === undefined)
|
|
121
147
|
return undefined;
|
package/dist/build/helpers.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ModelAdapter } from "../types/model.js";
|
|
2
2
|
import type { StepMiddleware } from "../types/middleware.js";
|
|
3
|
-
import type { ToolDefinition,
|
|
4
|
-
export declare const tool: <
|
|
3
|
+
import type { ToolDefinition, ToolInputSchema, ToolOutputSchema } from "../types/tool.js";
|
|
4
|
+
export declare const tool: <InputSchema extends ToolInputSchema, State = never, OutputSchema extends ToolOutputSchema | undefined = undefined>(value: ToolDefinition<InputSchema, State, OutputSchema>) => ToolDefinition<InputSchema, State, OutputSchema>;
|
|
5
5
|
export declare const model: <T extends ModelAdapter>(value: T) => T;
|
|
6
6
|
export declare const middleware: <T extends StepMiddleware>(value: T) => T;
|
package/dist/build/manifest.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { deepFreeze } from "../utils/immutable.js";
|
|
2
2
|
export function createManifest(input) {
|
|
3
|
-
const middleware = input.middleware.map((
|
|
3
|
+
const middleware = input.middleware.map((item) => projectMiddleware(item));
|
|
4
4
|
return deepFreeze({
|
|
5
5
|
id: input.id,
|
|
6
6
|
name: input.name,
|
|
7
7
|
middleware,
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
|
+
function projectMiddleware(item) {
|
|
11
|
+
const contributions = item.contributions;
|
|
12
|
+
return {
|
|
13
|
+
id: item.id,
|
|
14
|
+
...(contributions?.instructions === undefined
|
|
15
|
+
? {}
|
|
16
|
+
: { instructions: contributions.instructions }),
|
|
17
|
+
...(contributions?.tools === undefined ? {} : { tools: contributions.tools }),
|
|
18
|
+
...(contributions?.model === undefined ? {} : { model: contributions.model }),
|
|
19
|
+
};
|
|
20
|
+
}
|
package/dist/build/schema.d.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
readonly
|
|
5
|
-
readonly
|
|
6
|
-
} | {
|
|
7
|
-
readonly ok: false;
|
|
8
|
-
readonly issues: readonly string[];
|
|
9
|
-
};
|
|
10
|
-
export interface NormalizedToolSchema<T> {
|
|
11
|
-
readonly jsonSchema: import("../types/shared.js").JsonObject;
|
|
12
|
-
validate(value: unknown): SchemaValidation<T>;
|
|
1
|
+
import type { BoundToolSchema, ToolDefinition, ToolSchema, ToolSchemaSource } from "../types/tool.js";
|
|
2
|
+
export type { SchemaValidation } from "../types/tool.js";
|
|
3
|
+
interface NormalizedToolSchemas {
|
|
4
|
+
readonly inputSchema: BoundToolSchema<unknown>;
|
|
5
|
+
readonly outputSchema?: BoundToolSchema<unknown>;
|
|
13
6
|
}
|
|
7
|
+
/** Creates a portable, explicitly validated schema source from raw JSON Schema. */
|
|
8
|
+
export declare function defineSchema<T>(value: ToolSchema<T>): ToolSchema<T>;
|
|
14
9
|
/** Eagerly prepares a definition authored through Harness's tool() helper. */
|
|
15
10
|
export declare function prepareTool<T extends ToolDefinition>(definition: T): T;
|
|
16
|
-
/** Returns
|
|
17
|
-
export declare function
|
|
18
|
-
/** Converts
|
|
19
|
-
export declare function normalizeSchema<
|
|
11
|
+
/** Returns cached normalized contracts, preparing raw definitions on first bind. */
|
|
12
|
+
export declare function normalizedSchemasFor(definition: ToolDefinition): NormalizedToolSchemas;
|
|
13
|
+
/** Converts an accepted tool schema source into Harness's immutable runtime representation. */
|
|
14
|
+
export declare function normalizeSchema<T>(source: ToolSchemaSource<T>, role: "input" | "output"): BoundToolSchema<T>;
|
package/dist/build/schema.js
CHANGED
|
@@ -2,56 +2,162 @@ import { z } from "zod";
|
|
|
2
2
|
import { HarnessError } from "../errors.js";
|
|
3
3
|
import { copyJsonObject } from "../utils/immutable.js";
|
|
4
4
|
const preparedSchemas = new WeakMap();
|
|
5
|
+
/** Creates a portable, explicitly validated schema source from raw JSON Schema. */
|
|
6
|
+
export function defineSchema(value) {
|
|
7
|
+
if (!value || typeof value !== "object" || typeof value.validate !== "function")
|
|
8
|
+
throw new HarnessError("tool.invalid-schema", "Tool schema must provide validate()");
|
|
9
|
+
return Object.freeze({
|
|
10
|
+
jsonSchema: copyJsonObject(value.jsonSchema, "tool schema jsonSchema"),
|
|
11
|
+
validate: value.validate.bind(value),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
5
14
|
/** Eagerly prepares a definition authored through Harness's tool() helper. */
|
|
6
15
|
export function prepareTool(definition) {
|
|
7
|
-
|
|
16
|
+
normalizedSchemasFor(definition);
|
|
8
17
|
return definition;
|
|
9
18
|
}
|
|
10
|
-
/** Returns
|
|
11
|
-
export function
|
|
19
|
+
/** Returns cached normalized contracts, preparing raw definitions on first bind. */
|
|
20
|
+
export function normalizedSchemasFor(definition) {
|
|
12
21
|
const cached = preparedSchemas.get(definition);
|
|
13
22
|
if (cached)
|
|
14
23
|
return cached;
|
|
15
|
-
const normalized =
|
|
24
|
+
const normalized = Object.freeze({
|
|
25
|
+
inputSchema: normalizeSchema(definition.inputSchema, "input"),
|
|
26
|
+
...(definition.outputSchema === undefined
|
|
27
|
+
? {}
|
|
28
|
+
: { outputSchema: normalizeSchema(definition.outputSchema, "output") }),
|
|
29
|
+
});
|
|
16
30
|
preparedSchemas.set(definition, normalized);
|
|
17
31
|
return normalized;
|
|
18
32
|
}
|
|
19
|
-
/** Converts
|
|
20
|
-
export function normalizeSchema(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
/** Converts an accepted tool schema source into Harness's immutable runtime representation. */
|
|
34
|
+
export function normalizeSchema(source, role) {
|
|
35
|
+
const normalized = isZodSchema(source)
|
|
36
|
+
? normalizeZodSchema(source, role)
|
|
37
|
+
: isStandardSchema(source)
|
|
38
|
+
? normalizeStandardSchema(source, role)
|
|
39
|
+
: isToolSchema(source)
|
|
40
|
+
? normalizeExplicitSchema(source)
|
|
41
|
+
: undefined;
|
|
42
|
+
if (!normalized)
|
|
43
|
+
throw new HarnessError("tool.invalid-schema", "Tool schema must be a Zod schema, synchronous Standard Schema with JSON Schema conversion, or defineSchema() contract");
|
|
44
|
+
if (role === "input" && normalized.jsonSchema.type !== "object")
|
|
45
|
+
throw new HarnessError("tool.invalid-schema", "Tool inputSchema JSON Schema root type must be object");
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
function normalizeZodSchema(source, role) {
|
|
49
|
+
if (hasDeclaredAsyncWork(source))
|
|
50
|
+
throw new HarnessError("tool.invalid-schema", "Tool schema must validate synchronously");
|
|
27
51
|
let jsonSchema;
|
|
28
52
|
try {
|
|
29
|
-
jsonSchema = copyJsonObject(z.toJSONSchema(
|
|
53
|
+
jsonSchema = copyJsonObject(z.toJSONSchema(source, { target: "draft-07" }), `tool.${role}Schema.jsonSchema`);
|
|
30
54
|
}
|
|
31
55
|
catch (error) {
|
|
32
|
-
throw new HarnessError("tool.invalid-schema", `Tool
|
|
33
|
-
}
|
|
34
|
-
if (jsonSchema.type !== "object") {
|
|
35
|
-
throw new HarnessError("tool.invalid-schema", "Tool JSON Schema root type must be object");
|
|
56
|
+
throw new HarnessError("tool.invalid-schema", `Tool ${role}Schema must convert to JSON Schema: ${message(error)}`, { cause: error });
|
|
36
57
|
}
|
|
37
58
|
return Object.freeze({
|
|
38
59
|
jsonSchema,
|
|
39
60
|
validate(value) {
|
|
40
61
|
try {
|
|
41
|
-
const result =
|
|
62
|
+
const result = source.safeParse(value);
|
|
42
63
|
if (result.success)
|
|
43
64
|
return { ok: true, value: result.data };
|
|
44
65
|
return {
|
|
45
66
|
ok: false,
|
|
46
|
-
issues: result.error.issues.map((
|
|
67
|
+
issues: Object.freeze(result.error.issues.map((entry) => issue(entry.path.filter(isPathSegment), entry.code, entry.message))),
|
|
47
68
|
};
|
|
48
69
|
}
|
|
49
70
|
catch (error) {
|
|
50
|
-
return { ok: false, issues: [synchronousIssue(error)] };
|
|
71
|
+
return { ok: false, issues: [issue([], "schema_error", synchronousIssue(error))] };
|
|
51
72
|
}
|
|
52
73
|
},
|
|
53
74
|
});
|
|
54
75
|
}
|
|
76
|
+
function normalizeStandardSchema(source, role) {
|
|
77
|
+
let jsonSchema;
|
|
78
|
+
try {
|
|
79
|
+
jsonSchema = copyJsonObject(role === "input"
|
|
80
|
+
? source["~standard"].jsonSchema.input()
|
|
81
|
+
: source["~standard"].jsonSchema.output(), `tool.${role}Schema.jsonSchema`);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new HarnessError("tool.invalid-schema", `Tool ${role}Schema must convert to JSON Schema: ${message(error)}`, { cause: error });
|
|
85
|
+
}
|
|
86
|
+
return Object.freeze({
|
|
87
|
+
jsonSchema,
|
|
88
|
+
validate(value) {
|
|
89
|
+
try {
|
|
90
|
+
const result = source["~standard"].validate(value);
|
|
91
|
+
if (isPromiseLike(result))
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
issues: [issue([], "async_validation", "Schema validation must be synchronous")],
|
|
95
|
+
};
|
|
96
|
+
if (result && typeof result === "object" && "issues" in result && result.issues)
|
|
97
|
+
return { ok: false, issues: Object.freeze(result.issues.map(standardIssue)) };
|
|
98
|
+
if (result && typeof result === "object" && "value" in result)
|
|
99
|
+
return { ok: true, value: result.value };
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
issues: [issue([], "schema_error", "Standard Schema returned an invalid result")],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return { ok: false, issues: [issue([], "schema_error", synchronousIssue(error))] };
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function normalizeExplicitSchema(source) {
|
|
112
|
+
return Object.freeze({
|
|
113
|
+
jsonSchema: copyJsonObject(source.jsonSchema, "tool schema jsonSchema"),
|
|
114
|
+
validate(value) {
|
|
115
|
+
try {
|
|
116
|
+
const result = source.validate(value);
|
|
117
|
+
if (isPromiseLike(result))
|
|
118
|
+
return {
|
|
119
|
+
ok: false,
|
|
120
|
+
issues: [issue([], "async_validation", "Schema validation must be synchronous")],
|
|
121
|
+
};
|
|
122
|
+
if (!result || typeof result !== "object" || typeof result.ok !== "boolean")
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
issues: [issue([], "schema_error", "Tool schema returned an invalid result")],
|
|
126
|
+
};
|
|
127
|
+
if (result.ok)
|
|
128
|
+
return { ok: true, value: result.value };
|
|
129
|
+
return { ok: false, issues: Object.freeze(result.issues.map(normalizeIssue)) };
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
return { ok: false, issues: [issue([], "schema_error", synchronousIssue(error))] };
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function isZodSchema(value) {
|
|
138
|
+
return value instanceof z.ZodType;
|
|
139
|
+
}
|
|
140
|
+
function isStandardSchema(value) {
|
|
141
|
+
if (!value || typeof value !== "object" || !("~standard" in value))
|
|
142
|
+
return false;
|
|
143
|
+
const standard = value["~standard"];
|
|
144
|
+
if (!standard || typeof standard !== "object")
|
|
145
|
+
return false;
|
|
146
|
+
const record = standard;
|
|
147
|
+
if (typeof record.validate !== "function" ||
|
|
148
|
+
!record.jsonSchema ||
|
|
149
|
+
typeof record.jsonSchema !== "object")
|
|
150
|
+
return false;
|
|
151
|
+
const jsonSchema = record.jsonSchema;
|
|
152
|
+
return typeof jsonSchema.input === "function" && typeof jsonSchema.output === "function";
|
|
153
|
+
}
|
|
154
|
+
function isToolSchema(value) {
|
|
155
|
+
return (!!value &&
|
|
156
|
+
typeof value === "object" &&
|
|
157
|
+
"jsonSchema" in value &&
|
|
158
|
+
"validate" in value &&
|
|
159
|
+
typeof value.validate === "function");
|
|
160
|
+
}
|
|
55
161
|
/** Zod exposes declared checks in its stable v4 definition graph; reject async checks before a run. */
|
|
56
162
|
function hasDeclaredAsyncWork(schema) {
|
|
57
163
|
return visitDefinition(schema._zod?.def, new Set());
|
|
@@ -64,8 +170,6 @@ function visitDefinition(value, seen) {
|
|
|
64
170
|
if (seen.has(value))
|
|
65
171
|
return false;
|
|
66
172
|
seen.add(value);
|
|
67
|
-
// Nested Zod schemas carry their own definition graph. Inspect it directly so public helpers
|
|
68
|
-
// such as `parseAsync` do not make every otherwise-synchronous schema look asynchronous.
|
|
69
173
|
const nestedDefinition = value._zod?.def;
|
|
70
174
|
if (nestedDefinition !== undefined && nestedDefinition !== value)
|
|
71
175
|
return visitDefinition(nestedDefinition, seen);
|
|
@@ -75,6 +179,21 @@ function visitDefinition(value, seen) {
|
|
|
75
179
|
}
|
|
76
180
|
return false;
|
|
77
181
|
}
|
|
182
|
+
function standardIssue(value) {
|
|
183
|
+
return issue(value.path?.filter(isPathSegment) ?? [], value.code ?? "invalid", value.message ?? "Invalid value");
|
|
184
|
+
}
|
|
185
|
+
function normalizeIssue(value) {
|
|
186
|
+
return issue(value.path?.filter(isPathSegment) ?? [], value.code ?? "invalid", value.message ?? "Invalid value");
|
|
187
|
+
}
|
|
188
|
+
function issue(path, code, text) {
|
|
189
|
+
return Object.freeze({ path: Object.freeze([...path]), code, message: text });
|
|
190
|
+
}
|
|
191
|
+
function isPathSegment(value) {
|
|
192
|
+
return typeof value === "string" || typeof value === "number";
|
|
193
|
+
}
|
|
194
|
+
function isPromiseLike(value) {
|
|
195
|
+
return (!!value && typeof value === "object" && typeof value.then === "function");
|
|
196
|
+
}
|
|
78
197
|
function synchronousIssue(error) {
|
|
79
198
|
const reason = message(error);
|
|
80
199
|
return reason.includes("Promise during synchronous parse")
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Machine-readable error raised by Harness-owned code. */
|
|
2
|
-
export type HarnessErrorCode = "agent.build-failed" | "agent.lifecycle-sealed" | "capability.state.create-failed" | "capability.state.undeclared" | "context.invalid-item" | "context.invalid-item-type" | "context.invalid-order" | "context.invalid-reason" | "context.invalid-slot" | "interaction.invalid" | "interaction.missing-resume" | "interaction.uncorrelated-resume" | "json.invalid-data" | "json.invalid-object" | "middleware.next-after-return" | "middleware.next-called-twice" | "middleware.request-mutators-revoked" | "model.candidate-missing" | "model.invalid-candidate" | "model.invalid-directive" | "configuration.duplicate-tool-name" | "configuration.invalid" | "configuration.invalid-instructions" | "configuration.invalid-order" | "configuration.invalid-reason" | "configuration.invalid-slot" | "configuration.invalid-tools" | "configuration.model-selection-conflict" | "response.invalid-replacement" | "session.invalid-seed" | "session.record-failed" | "session.stale-result" | "tool.invalid" | "tool.invalid-arguments" | "tool.invalid-name" | "tool.invalid-schema" | "tool.invalid-tool-result";
|
|
2
|
+
export type HarnessErrorCode = "agent.build-failed" | "agent.lifecycle-sealed" | "capability.state.create-failed" | "capability.state.undeclared" | "context.invalid-item" | "context.invalid-item-type" | "context.invalid-order" | "context.invalid-reason" | "context.invalid-slot" | "interaction.invalid" | "interaction.missing-resume" | "interaction.uncorrelated-resume" | "input.invalid-content" | "output.invalid" | "output.invalid-schema" | "json.invalid-data" | "json.invalid-object" | "middleware.next-after-return" | "middleware.next-called-twice" | "middleware.request-mutators-revoked" | "model.candidate-missing" | "model.adapter-invalid-options" | "model.adapter-invalid-response" | "model.invalid-candidate" | "model.invalid-directive" | "model.unsupported-content" | "model.unsupported-output-schema" | "configuration.duplicate-tool-name" | "configuration.invalid" | "configuration.invalid-instructions" | "configuration.invalid-order" | "configuration.invalid-reason" | "configuration.invalid-slot" | "configuration.invalid-tools" | "configuration.model-selection-conflict" | "response.invalid-replacement" | "session.invalid-seed" | "session.record-failed" | "session.stale-result" | "tool.invalid" | "tool.invalid-arguments" | "tool.invalid-output" | "tool.invalid-name" | "tool.invalid-schema" | "tool.invalid-tool-result";
|
|
3
3
|
export type HarnessErrorDetails = Readonly<Record<string, string | number | boolean>>;
|
|
4
4
|
export interface HarnessErrorOptions {
|
|
5
5
|
readonly cause?: unknown;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,12 @@ export { HarnessError, isHarnessError } from "./errors.js";
|
|
|
4
4
|
export type { HarnessErrorCode, HarnessErrorDetails, HarnessErrorOptions } from "./errors.js";
|
|
5
5
|
export { BuiltAgent } from "./build/agent.js";
|
|
6
6
|
export { middleware, model, tool } from "./build/helpers.js";
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export type {
|
|
7
|
+
export { defineSchema } from "./build/schema.js";
|
|
8
|
+
export { preparedModel } from "./model/prepared.js";
|
|
9
|
+
export type { PreparedModelOptions } from "./model/prepared.js";
|
|
10
|
+
export type { AgentManifest, MiddlewareManifest } from "./types/manifest.js";
|
|
11
|
+
export type { BoundMiddleware, CapabilityDeclaration, CapabilityItems, CapabilityState, MiddlewareContributions, StepInput, StepMiddleware, StepRequest, StepResponse, } from "./types/middleware.js";
|
|
12
|
+
export type { ModelCandidate, ModelControls, ModelDirective, ModelEvidence, ModelFinishReason, ModelAdapter, ModelAdapterContext, ModelPreparedCall, ContextContributor, ContextMutationOptions, ContextSnapshot, ModelCall, ModelCallTool, ModelOutputBlock, PromptContentPart, PromptItem, ModelConfigurationContributor, ModelConfigurationInstruction, ModelConfigurationMutationOptions, ModelConfigurationSnapshot, ModelConfigurationTool, ModelRequest, ModelToolCall, ModelUsage, } from "./types/model.js";
|
|
10
13
|
export type { BuildDiagnostic, ContextItem, DeferredOutcome, JsonObject, JsonPrimitive, JsonValue, ObserveEvent, ObserveModelConfigurationSnapshot, ObserveModelRequested, ObserveSealedCall, ObserveToolSnapshot, Observer, Tripwire, } from "./types/shared.js";
|
|
11
|
-
export type { ActiveExecutionRecord, ActiveInteractionExecutionRecord, ActiveModelExecutionRecord, ActiveToolCallRecord, ActiveToolsExecutionRecord, InteractionReply, InputCompletion, InputEvent, InputHandle, InputOptions, MessageInput, Session, SessionInput, SessionIdentity, SessionEvent, SessionOptions, SessionRecord, SessionRecorder, SessionRunOptions, SessionSeed, SeededSessionOptions, SessionSnapshot, TranscriptEntry, } from "./types/session.js";
|
|
12
|
-
export type { BoundToolSchema, BoundToolDefinition, Interaction, RequiredInteraction, SealedToolCall, ToolContent, ToolDefinition, ToolExecutionContext, ToolExecutionResume,
|
|
14
|
+
export type { ActiveExecutionRecord, ActiveInteractionExecutionRecord, ActiveModelExecutionRecord, ActiveToolCallRecord, ActiveToolsExecutionRecord, InteractionReply, InputCompletion, InputEvent, InputHandle, InputOptions, OutputInputOptions, MessageInput, Session, SessionInput, SessionIdentity, SessionEvent, SessionOptions, SessionRecord, SessionRecorder, SessionRunOptions, SessionSeed, SeededSessionOptions, SessionSnapshot, TranscriptEntry, UserContentPart, } from "./types/session.js";
|
|
15
|
+
export type { BoundToolSchema, BoundToolDefinition, Interaction, RequiredInteraction, SealedToolCall, ToolContent, ToolDefinition, ToolExecutionContext, ToolExecutionResume, ToolInputSchema, ToolOutputSchema, ToolSchema, ToolSchemaSource, StandardToolSchema, StandardSchemaIssue, SchemaIssue, SchemaValidation, SchemaOutput, ToolOwner, ToolOutcome, ToolResult, ToolValidationFailureDetails, } from "./types/tool.js";
|
package/dist/index.js
CHANGED
|
@@ -2,3 +2,5 @@ export { Agent, AgentBuilder, AgentBuildError, AgentLifecycleError, BoundAgentBu
|
|
|
2
2
|
export { HarnessError, isHarnessError } from "./errors.js";
|
|
3
3
|
export { BuiltAgent } from "./build/agent.js";
|
|
4
4
|
export { middleware, model, tool } from "./build/helpers.js";
|
|
5
|
+
export { defineSchema } from "./build/schema.js";
|
|
6
|
+
export { preparedModel } from "./model/prepared.js";
|