@nylorun/core 0.0.0-bootstrap → 0.1.1-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 (70) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +192 -0
  3. package/README.md +16 -0
  4. package/dist/compatibility.d.ts +3 -0
  5. package/dist/compatibility.js +3 -0
  6. package/dist/contracts.d.ts +246 -0
  7. package/dist/contracts.js +204 -0
  8. package/dist/define.d.ts +31 -0
  9. package/dist/define.js +15 -0
  10. package/dist/definition/assemble.d.ts +26 -0
  11. package/dist/definition/assemble.js +50 -0
  12. package/dist/definition/bind-agent.d.ts +8 -0
  13. package/dist/definition/bind-agent.js +61 -0
  14. package/dist/definition/bind-tool.d.ts +4 -0
  15. package/dist/definition/bind-tool.js +25 -0
  16. package/dist/definition/binding.d.ts +15 -0
  17. package/dist/definition/binding.js +9 -0
  18. package/dist/definition/bound.d.ts +20 -0
  19. package/dist/definition/bound.js +1 -0
  20. package/dist/definition/builder.d.ts +63 -0
  21. package/dist/definition/builder.js +215 -0
  22. package/dist/definition/declaration.d.ts +10 -0
  23. package/dist/definition/declaration.js +70 -0
  24. package/dist/definition/from.d.ts +6 -0
  25. package/dist/definition/from.js +135 -0
  26. package/dist/definition/helpers.d.ts +21 -0
  27. package/dist/definition/helpers.js +29 -0
  28. package/dist/definition/implementations.d.ts +16 -0
  29. package/dist/definition/implementations.js +3 -0
  30. package/dist/definition/manifest.d.ts +9 -0
  31. package/dist/definition/manifest.js +57 -0
  32. package/dist/definition/output-contract.d.ts +6 -0
  33. package/dist/definition/output-contract.js +12 -0
  34. package/dist/definition/schema-json.d.ts +3 -0
  35. package/dist/definition/schema-json.js +22 -0
  36. package/dist/definition/schema.d.ts +16 -0
  37. package/dist/definition/schema.js +281 -0
  38. package/dist/definition/tool-error.d.ts +9 -0
  39. package/dist/definition/tool-error.js +20 -0
  40. package/dist/errors.d.ts +17 -0
  41. package/dist/errors.js +22 -0
  42. package/dist/index.d.ts +10 -0
  43. package/dist/index.js +1 -0
  44. package/dist/types/agent.d.ts +12 -0
  45. package/dist/types/agent.js +1 -0
  46. package/dist/types/dynamics.d.ts +53 -0
  47. package/dist/types/dynamics.js +1 -0
  48. package/dist/types/manifest.d.ts +34 -0
  49. package/dist/types/manifest.js +1 -0
  50. package/dist/types/middleware.d.ts +74 -0
  51. package/dist/types/middleware.js +1 -0
  52. package/dist/types/model.d.ts +184 -0
  53. package/dist/types/model.js +1 -0
  54. package/dist/types/observe.d.ts +180 -0
  55. package/dist/types/observe.js +1 -0
  56. package/dist/types/shared.d.ts +25 -0
  57. package/dist/types/shared.js +1 -0
  58. package/dist/types/tool.d.ts +187 -0
  59. package/dist/types/tool.js +1 -0
  60. package/dist/types/transcript.d.ts +65 -0
  61. package/dist/types/transcript.js +1 -0
  62. package/dist/utils/canonical.d.ts +1 -0
  63. package/dist/utils/canonical.js +10 -0
  64. package/dist/utils/hash.d.ts +3 -0
  65. package/dist/utils/hash.js +7 -0
  66. package/dist/utils/immutable.d.ts +6 -0
  67. package/dist/utils/immutable.js +76 -0
  68. package/package.json +57 -7
  69. package/index.js +0 -1
  70. package/publish.out +0 -0
@@ -0,0 +1,135 @@
1
+ import { HarnessError } from "../errors.js";
2
+ import { assembleAgent } from "./assemble.js";
3
+ import { schemaFromJSON } from "./schema-json.js";
4
+ import { deepFreeze } from "../utils/immutable.js";
5
+ /** Rebuild an agent from manifest JSON + in-process implementations (T28). */
6
+ export function agentFrom(json, implementations) {
7
+ const manifest = normalizeManifest(json);
8
+ const entries = [];
9
+ const dynamics = new Map();
10
+ for (const capability of manifest.capabilities) {
11
+ const impl = implementations[capability.id] ?? {};
12
+ const tools = resolveTools(capability, impl.tools);
13
+ if (capability.beforeModelCall && !impl.beforeModelCall)
14
+ throw new HarnessError("agent.build-failed", `Missing beforeModelCall implementation for capability '${capability.id}'`);
15
+ if (capability.afterModelCall && !impl.afterModelCall)
16
+ throw new HarnessError("agent.build-failed", `Missing afterModelCall implementation for capability '${capability.id}'`);
17
+ const handle = impl.middleware ??
18
+ (async (request, next) => {
19
+ if (tools.length)
20
+ request.configuration.tools.set(capability.id, tools.map((tool) => implementations[capability.id]?.tools?.[tool.name] ?? tool));
21
+ if (capability.instructions)
22
+ request.configuration.instructions.set(capability.id, capability.instructions);
23
+ return next();
24
+ });
25
+ entries.push(Object.freeze({
26
+ id: capability.id,
27
+ handle,
28
+ hasMiddleware: impl.middleware !== undefined || capability.hasMiddleware,
29
+ tools: capability.tools !== undefined
30
+ ? tools.map((tool) => {
31
+ const live = impl.tools?.[tool.name];
32
+ if (!live)
33
+ throw new HarnessError("agent.build-failed", `Missing tool implementation '${tool.name}' for capability '${capability.id}'`);
34
+ return live;
35
+ })
36
+ : undefined,
37
+ contributions: Object.freeze({
38
+ ...(capability.instructions
39
+ ? { instructions: capability.instructions }
40
+ : {}),
41
+ ...(capability.tools
42
+ ? {
43
+ tools: capability.tools.map((tool) => Object.freeze({
44
+ name: tool.name,
45
+ ...(tool.description === undefined
46
+ ? {}
47
+ : { description: tool.description }),
48
+ })),
49
+ }
50
+ : {}),
51
+ }),
52
+ ...(capability.beforeModelCall ? { beforeModelCall: true } : {}),
53
+ ...(capability.afterModelCall ? { afterModelCall: true } : {}),
54
+ }));
55
+ dynamics.set(capability.id, {
56
+ ...(impl.beforeModelCall
57
+ ? { beforeModelCall: impl.beforeModelCall }
58
+ : {}),
59
+ ...(impl.afterModelCall
60
+ ? { afterModelCall: impl.afterModelCall }
61
+ : {}),
62
+ ...(impl.middleware ? { middleware: impl.middleware } : {}),
63
+ });
64
+ }
65
+ const outputSchema = manifest.outputSchema
66
+ ? schemaFromJSON(manifest.outputSchema)
67
+ : undefined;
68
+ const result = assembleAgent(entries, { id: manifest.id, name: manifest.name, outputSchema }, dynamics);
69
+ if (!result.ok)
70
+ throw new HarnessError("agent.build-failed", result.diagnostics.map((item) => item.message).join("; "));
71
+ return result.agent;
72
+ }
73
+ function resolveTools(capability, tools) {
74
+ if (!capability.tools?.length)
75
+ return [];
76
+ return capability.tools.map((tool) => {
77
+ const live = tools?.[tool.name];
78
+ if (!live)
79
+ throw new HarnessError("agent.build-failed", `Missing tool implementation '${tool.name}' for capability '${capability.id}'`);
80
+ return live;
81
+ });
82
+ }
83
+ function normalizeManifest(json) {
84
+ if (!json || typeof json !== "object" || Array.isArray(json))
85
+ throw new HarnessError("agent.build-failed", "Agent.from requires a manifest object");
86
+ const value = json;
87
+ if (typeof value.id !== "string" || !value.id)
88
+ throw new HarnessError("agent.build-failed", "Manifest id must be a non-empty string");
89
+ if (typeof value.name !== "string" || !value.name)
90
+ throw new HarnessError("agent.build-failed", "Manifest name must be a non-empty string");
91
+ if (!Array.isArray(value.capabilities))
92
+ throw new HarnessError("agent.build-failed", "Manifest capabilities must be an array");
93
+ const schemaVersion = value.schemaVersion === undefined ? 2 : value.schemaVersion;
94
+ if (schemaVersion !== 2)
95
+ throw new HarnessError("agent.build-failed", `Unsupported manifest schemaVersion ${schemaVersion}`);
96
+ // Reject top-level model (Runtime-owned).
97
+ if ("model" in value && value.model !== undefined)
98
+ throw new HarnessError("agent.build-failed", "Manifest must not include top-level model; Runtime owns model resolution");
99
+ return deepFreeze({
100
+ schemaVersion: 2,
101
+ id: value.id,
102
+ name: value.name,
103
+ ...(value.outputSchema && typeof value.outputSchema === "object"
104
+ ? { outputSchema: value.outputSchema }
105
+ : {}),
106
+ capabilities: value.capabilities.map(normalizeCapability),
107
+ });
108
+ }
109
+ function normalizeCapability(capability) {
110
+ return Object.freeze({
111
+ id: capability.id,
112
+ kind: capability.kind ?? "capability",
113
+ hasMiddleware: capability.hasMiddleware ?? false,
114
+ ...(capability.instructions
115
+ ? { instructions: Object.freeze([...capability.instructions]) }
116
+ : {}),
117
+ ...(capability.tools
118
+ ? { tools: Object.freeze(capability.tools.map(normalizeTool)) }
119
+ : {}),
120
+ ...(capability.beforeModelCall ? { beforeModelCall: true } : {}),
121
+ ...(capability.afterModelCall ? { afterModelCall: true } : {}),
122
+ });
123
+ }
124
+ function normalizeTool(tool) {
125
+ return Object.freeze({
126
+ name: tool.name,
127
+ ...(tool.description === undefined
128
+ ? {}
129
+ : { description: tool.description }),
130
+ inputSchema: tool.inputSchema,
131
+ ...(tool.outputSchema === undefined
132
+ ? {}
133
+ : { outputSchema: tool.outputSchema }),
134
+ });
135
+ }
@@ -0,0 +1,21 @@
1
+ import type { ModelAdapter } from "../types/model.js";
2
+ import type { StepMiddleware } from "../types/middleware.js";
3
+ import type { ToolDefinition, ToolInputSchema, ToolOutputSchema, ToolSchemaSource } from "../types/tool.js";
4
+ export declare const tool: <InputSchema extends ToolInputSchema, Info = unknown, OutputSchema extends ToolOutputSchema | undefined = undefined>(value: ToolDefinition<InputSchema, Info, OutputSchema>) => ToolDefinition<InputSchema, Info, OutputSchema>;
5
+ /** @deprecated Prefer Runtime model resolution. Capability-disguised `.use({ model })` is not the taught path. */
6
+ export declare const model: <T extends ModelAdapter>(value: T) => T;
7
+ /** @deprecated Prefer `beforeModelCall` / `afterModelCall`. Kept through 1.0. */
8
+ export declare const middleware: <T extends StepMiddleware>(value: T) => T;
9
+ /** Compose a reusable capability bundle (tools, instructions, optional dynamics). */
10
+ export declare function capability<Info = unknown>(declaration: {
11
+ readonly id: string;
12
+ readonly tools?: readonly ToolDefinition<any, Info, any>[];
13
+ readonly instructions?: string | readonly string[];
14
+ readonly beforeModelCall?: import("../types/dynamics.js").BeforeModelCallFn<Info>;
15
+ readonly afterModelCall?: import("../types/dynamics.js").AfterModelCallFn<Info>;
16
+ /** @deprecated Prefer beforeModelCall / afterModelCall. */
17
+ readonly middleware?: StepMiddleware<Info>;
18
+ /** @deprecated Model resolution is Runtime-owned; not projected into the manifest. */
19
+ readonly model?: import("../types/model.js").ModelDirective;
20
+ }): import("../types/middleware.js").CapabilityDeclaration<Info>;
21
+ export type { ToolSchemaSource };
@@ -0,0 +1,29 @@
1
+ import { prepareTool } from "./schema.js";
2
+ export const tool = (value) => prepareTool(value);
3
+ /** @deprecated Prefer Runtime model resolution. Capability-disguised `.use({ model })` is not the taught path. */
4
+ export const model = (value) => value;
5
+ /** @deprecated Prefer `beforeModelCall` / `afterModelCall`. Kept through 1.0. */
6
+ export const middleware = (value) => value;
7
+ /** Compose a reusable capability bundle (tools, instructions, optional dynamics). */
8
+ export function capability(declaration) {
9
+ const instructions = declaration.instructions === undefined
10
+ ? undefined
11
+ : typeof declaration.instructions === "string"
12
+ ? [declaration.instructions]
13
+ : declaration.instructions;
14
+ return {
15
+ id: declaration.id,
16
+ ...(declaration.tools === undefined ? {} : { tools: declaration.tools }),
17
+ ...(instructions === undefined ? {} : { instructions }),
18
+ ...(declaration.beforeModelCall === undefined
19
+ ? {}
20
+ : { beforeModelCall: declaration.beforeModelCall }),
21
+ ...(declaration.afterModelCall === undefined
22
+ ? {}
23
+ : { afterModelCall: declaration.afterModelCall }),
24
+ ...(declaration.middleware === undefined
25
+ ? {}
26
+ : { middleware: declaration.middleware }),
27
+ ...(declaration.model === undefined ? {} : { model: declaration.model }),
28
+ };
29
+ }
@@ -0,0 +1,16 @@
1
+ import type { AfterModelCallFn, BeforeModelCallFn } from "../types/dynamics.js";
2
+ import type { ToolDefinition } from "../types/tool.js";
3
+ import type { StepMiddleware } from "../types/middleware.js";
4
+ /** In-process implementations table keyed by capability id (never serialized). */
5
+ export interface Implementations<Info = unknown> {
6
+ readonly [capabilityId: string]: {
7
+ readonly tools?: {
8
+ readonly [toolName: string]: ToolDefinition<any, Info, any>;
9
+ };
10
+ readonly beforeModelCall?: BeforeModelCallFn<Info>;
11
+ readonly afterModelCall?: AfterModelCallFn<Info>;
12
+ /** @deprecated Local engine only through 1.0. */
13
+ readonly middleware?: StepMiddleware<Info>;
14
+ };
15
+ }
16
+ export declare function emptyImplementations(): Implementations;
@@ -0,0 +1,3 @@
1
+ export function emptyImplementations() {
2
+ return Object.freeze({});
3
+ }
@@ -0,0 +1,9 @@
1
+ import type { BoundMiddleware } from "./bound.js";
2
+ import type { AgentManifest } from "../types/manifest.js";
3
+ import type { ToolSchemaSource } from "../types/tool.js";
4
+ export declare function createManifest(input: {
5
+ id: string;
6
+ name: string;
7
+ outputSchema?: ToolSchemaSource;
8
+ middleware: readonly BoundMiddleware[];
9
+ }): AgentManifest;
@@ -0,0 +1,57 @@
1
+ import { bindOutputContract } from "./output-contract.js";
2
+ import { normalizedSchemasFor } from "./schema.js";
3
+ import { deepFreeze } from "../utils/immutable.js";
4
+ export function createManifest(input) {
5
+ const capabilities = input.middleware.map((item) => projectCapability(item));
6
+ return deepFreeze({
7
+ schemaVersion: 2,
8
+ id: input.id,
9
+ name: input.name,
10
+ ...(input.outputSchema === undefined
11
+ ? {}
12
+ : {
13
+ outputSchema: bindOutputContract(input.outputSchema).schema
14
+ .jsonSchema,
15
+ }),
16
+ capabilities,
17
+ });
18
+ }
19
+ function projectCapability(item) {
20
+ const contributions = item.contributions;
21
+ return {
22
+ id: item.id,
23
+ kind: capabilityKind(item),
24
+ hasMiddleware: item.hasMiddleware,
25
+ ...(contributions?.instructions === undefined
26
+ ? {}
27
+ : { instructions: contributions.instructions }),
28
+ ...(item.tools === undefined
29
+ ? {}
30
+ : { tools: item.tools.map((tool) => projectTool(tool)) }),
31
+ ...(item.beforeModelCall ? { beforeModelCall: true } : {}),
32
+ ...(item.afterModelCall ? { afterModelCall: true } : {}),
33
+ // Deliberately omit capability.model from new manifests (Runtime-owned).
34
+ };
35
+ }
36
+ function capabilityKind(item) {
37
+ if (item.id === "agent")
38
+ return "agent";
39
+ if (item.contributions !== undefined ||
40
+ item.beforeModelCall ||
41
+ item.afterModelCall)
42
+ return "capability";
43
+ return "middleware";
44
+ }
45
+ function projectTool(tool) {
46
+ const schemas = normalizedSchemasFor(tool);
47
+ return {
48
+ name: tool.name,
49
+ ...(tool.description === undefined
50
+ ? {}
51
+ : { description: tool.description }),
52
+ inputSchema: schemas.inputSchema.jsonSchema,
53
+ ...(schemas.outputSchema === undefined
54
+ ? {}
55
+ : { outputSchema: schemas.outputSchema.jsonSchema }),
56
+ };
57
+ }
@@ -0,0 +1,6 @@
1
+ import type { ToolSchema, ToolSchemaSource } from "../types/tool.js";
2
+ /** Runtime-only validator paired with the portable JSON Schema projected to models. */
3
+ export interface TurnOutputContract {
4
+ readonly schema: ToolSchema<unknown>;
5
+ }
6
+ export declare function bindOutputContract(source: ToolSchemaSource): TurnOutputContract;
@@ -0,0 +1,12 @@
1
+ import { HarnessError, isHarnessError } from "../errors.js";
2
+ import { normalizeSchema } from "./schema.js";
3
+ export function bindOutputContract(source) {
4
+ try {
5
+ return Object.freeze({ schema: normalizeSchema(source, "output") });
6
+ }
7
+ catch (cause) {
8
+ if (isHarnessError(cause))
9
+ throw new HarnessError("output.invalid-schema", cause.message, { cause });
10
+ throw new HarnessError("output.invalid-schema", String(cause), { cause });
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ import type { JsonObject } from "../types/shared.js";
2
+ /** Unsupported JSON Schema constructs fail closed during definition loading. */
3
+ export declare function schemaFromJSON(jsonSchema: JsonObject): import("../define.js").ToolSchema<unknown>;
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ import { defineSchema } from "./schema.js";
3
+ /** Unsupported JSON Schema constructs fail closed during definition loading. */
4
+ export function schemaFromJSON(jsonSchema) {
5
+ const schema = z.fromJSONSchema(jsonSchema);
6
+ return defineSchema({
7
+ jsonSchema,
8
+ validate(value) {
9
+ const result = schema.safeParse(value);
10
+ return result.success
11
+ ? { ok: true, value: result.data }
12
+ : {
13
+ ok: false,
14
+ issues: result.error.issues.map((issue) => ({
15
+ path: issue.path.filter((v) => typeof v === "string" || typeof v === "number"),
16
+ code: issue.code,
17
+ message: issue.message,
18
+ })),
19
+ };
20
+ },
21
+ });
22
+ }
@@ -0,0 +1,16 @@
1
+ import type { ToolDefinition, ToolSchema, ToolSchemaSource } from "../types/tool.js";
2
+ export type { SchemaValidation } from "../types/tool.js";
3
+ interface NormalizedToolSchemas {
4
+ readonly inputSchema: ToolSchema<unknown>;
5
+ readonly outputSchema?: ToolSchema<unknown>;
6
+ }
7
+ /** Creates a portable, explicitly validated schema source from raw JSON Schema. */
8
+ export declare function defineSchema<T>(value: ToolSchema<T>): ToolSchema<T>;
9
+ /** Eagerly prepares a definition authored through Harness's tool() helper. */
10
+ export declare function prepareTool<T extends ToolDefinition>(definition: T): T;
11
+ /** Resolves `input`/`output`/`run` aliases onto the legacy field names. */
12
+ export declare function normalizeToolDefinition(definition: ToolDefinition): ToolDefinition;
13
+ /** Returns cached normalized contracts, preparing raw definitions on first bind. */
14
+ export declare function normalizedSchemasFor(definition: ToolDefinition): NormalizedToolSchemas;
15
+ /** Converts an accepted tool schema source into Harness's immutable runtime representation. */
16
+ export declare function normalizeSchema<T>(source: ToolSchemaSource<T>, role: "input" | "output"): ToolSchema<T>;
@@ -0,0 +1,281 @@
1
+ import { z } from "zod";
2
+ import { HarnessError } from "../errors.js";
3
+ import { copyJsonObject } from "../utils/immutable.js";
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 ||
8
+ typeof value !== "object" ||
9
+ typeof value.validate !== "function")
10
+ throw new HarnessError("tool.invalid-schema", "Tool schema must provide validate()");
11
+ return Object.freeze({
12
+ jsonSchema: copyJsonObject(value.jsonSchema, "tool schema jsonSchema"),
13
+ validate: value.validate.bind(value),
14
+ });
15
+ }
16
+ /** Eagerly prepares a definition authored through Harness's tool() helper. */
17
+ export function prepareTool(definition) {
18
+ const normalized = normalizeToolDefinition(definition);
19
+ normalizedSchemasFor(normalized);
20
+ return normalized;
21
+ }
22
+ /** Resolves `input`/`output`/`run` aliases onto the legacy field names. */
23
+ export function normalizeToolDefinition(definition) {
24
+ const alreadyCanonical = definition.inputSchema !== undefined &&
25
+ typeof definition.execute === "function" &&
26
+ definition.input === undefined &&
27
+ definition.output === undefined &&
28
+ definition.run === undefined;
29
+ if (alreadyCanonical)
30
+ return definition;
31
+ const inputSchema = definition.inputSchema ?? definition.input;
32
+ const outputSchema = definition.outputSchema ?? definition.output;
33
+ const execute = definition.execute ?? definition.run;
34
+ if (!inputSchema)
35
+ throw new HarnessError("tool.invalid-schema", `Tool '${definition.name}' must provide input or inputSchema`);
36
+ if (typeof execute !== "function")
37
+ throw new HarnessError("tool.invalid", `Tool '${definition.name}' must provide run() or execute()`);
38
+ return {
39
+ name: definition.name,
40
+ ...(definition.description === undefined
41
+ ? {}
42
+ : { description: definition.description }),
43
+ inputSchema,
44
+ ...(outputSchema === undefined ? {} : { outputSchema }),
45
+ execute,
46
+ ...(definition.approval === undefined
47
+ ? {}
48
+ : { approval: definition.approval }),
49
+ ...(definition.effects === undefined
50
+ ? {}
51
+ : { effects: definition.effects }),
52
+ };
53
+ }
54
+ /** Returns cached normalized contracts, preparing raw definitions on first bind. */
55
+ export function normalizedSchemasFor(definition) {
56
+ const cached = preparedSchemas.get(definition);
57
+ if (cached)
58
+ return cached;
59
+ const prepared = normalizeToolDefinition(definition);
60
+ const cachedPrepared = prepared !== definition ? preparedSchemas.get(prepared) : undefined;
61
+ if (cachedPrepared) {
62
+ preparedSchemas.set(definition, cachedPrepared);
63
+ return cachedPrepared;
64
+ }
65
+ if (!prepared.inputSchema)
66
+ throw new HarnessError("tool.invalid-schema", `Tool '${prepared.name}' is missing inputSchema`);
67
+ const normalized = Object.freeze({
68
+ inputSchema: normalizeSchema(prepared.inputSchema, "input"),
69
+ ...(prepared.outputSchema === undefined
70
+ ? {}
71
+ : { outputSchema: normalizeSchema(prepared.outputSchema, "output") }),
72
+ });
73
+ preparedSchemas.set(prepared, normalized);
74
+ if (definition !== prepared)
75
+ preparedSchemas.set(definition, normalized);
76
+ return normalized;
77
+ }
78
+ /** Converts an accepted tool schema source into Harness's immutable runtime representation. */
79
+ export function normalizeSchema(source, role) {
80
+ const normalized = isZodSchema(source)
81
+ ? normalizeZodSchema(source, role)
82
+ : isStandardSchema(source)
83
+ ? normalizeStandardSchema(source, role)
84
+ : isToolSchema(source)
85
+ ? normalizeExplicitSchema(source)
86
+ : undefined;
87
+ if (!normalized)
88
+ throw new HarnessError("tool.invalid-schema", "Tool schema must be a Zod schema, synchronous Standard Schema with JSON Schema conversion, or defineSchema() contract");
89
+ if (role === "input" && normalized.jsonSchema.type !== "object")
90
+ throw new HarnessError("tool.invalid-schema", "Tool inputSchema JSON Schema root type must be object");
91
+ return normalized;
92
+ }
93
+ function normalizeZodSchema(source, role) {
94
+ if (hasDeclaredAsyncWork(source))
95
+ throw new HarnessError("tool.invalid-schema", "Tool schema must validate synchronously");
96
+ let jsonSchema;
97
+ try {
98
+ jsonSchema = copyJsonObject(z.toJSONSchema(source, { target: "draft-07" }), `tool.${role}Schema.jsonSchema`);
99
+ }
100
+ catch (error) {
101
+ throw new HarnessError("tool.invalid-schema", `Tool ${role}Schema must convert to JSON Schema: ${message(error)}`, { cause: error });
102
+ }
103
+ return Object.freeze({
104
+ jsonSchema,
105
+ validate(value) {
106
+ try {
107
+ const result = source.safeParse(value);
108
+ if (result.success)
109
+ return { ok: true, value: result.data };
110
+ return {
111
+ ok: false,
112
+ issues: Object.freeze(result.error.issues.map((entry) => issue(entry.path.filter(isPathSegment), entry.code, entry.message))),
113
+ };
114
+ }
115
+ catch (error) {
116
+ return {
117
+ ok: false,
118
+ issues: [issue([], "schema_error", synchronousIssue(error))],
119
+ };
120
+ }
121
+ },
122
+ });
123
+ }
124
+ function normalizeStandardSchema(source, role) {
125
+ let jsonSchema;
126
+ try {
127
+ jsonSchema = copyJsonObject(role === "input"
128
+ ? source["~standard"].jsonSchema.input()
129
+ : source["~standard"].jsonSchema.output(), `tool.${role}Schema.jsonSchema`);
130
+ }
131
+ catch (error) {
132
+ throw new HarnessError("tool.invalid-schema", `Tool ${role}Schema must convert to JSON Schema: ${message(error)}`, { cause: error });
133
+ }
134
+ return Object.freeze({
135
+ jsonSchema,
136
+ validate(value) {
137
+ try {
138
+ const result = source["~standard"].validate(value);
139
+ if (isPromiseLike(result))
140
+ return {
141
+ ok: false,
142
+ issues: [
143
+ issue([], "async_validation", "Schema validation must be synchronous"),
144
+ ],
145
+ };
146
+ if (result &&
147
+ typeof result === "object" &&
148
+ "issues" in result &&
149
+ result.issues)
150
+ return {
151
+ ok: false,
152
+ issues: Object.freeze(result.issues.map(standardIssue)),
153
+ };
154
+ if (result && typeof result === "object" && "value" in result)
155
+ return { ok: true, value: result.value };
156
+ return {
157
+ ok: false,
158
+ issues: [
159
+ issue([], "schema_error", "Standard Schema returned an invalid result"),
160
+ ],
161
+ };
162
+ }
163
+ catch (error) {
164
+ return {
165
+ ok: false,
166
+ issues: [issue([], "schema_error", synchronousIssue(error))],
167
+ };
168
+ }
169
+ },
170
+ });
171
+ }
172
+ function normalizeExplicitSchema(source) {
173
+ return Object.freeze({
174
+ jsonSchema: copyJsonObject(source.jsonSchema, "tool schema jsonSchema"),
175
+ validate(value) {
176
+ try {
177
+ const result = source.validate(value);
178
+ if (isPromiseLike(result))
179
+ return {
180
+ ok: false,
181
+ issues: [
182
+ issue([], "async_validation", "Schema validation must be synchronous"),
183
+ ],
184
+ };
185
+ if (!result ||
186
+ typeof result !== "object" ||
187
+ typeof result.ok !== "boolean")
188
+ return {
189
+ ok: false,
190
+ issues: [
191
+ issue([], "schema_error", "Tool schema returned an invalid result"),
192
+ ],
193
+ };
194
+ if (result.ok)
195
+ return { ok: true, value: result.value };
196
+ return {
197
+ ok: false,
198
+ issues: Object.freeze(result.issues.map(normalizeIssue)),
199
+ };
200
+ }
201
+ catch (error) {
202
+ return {
203
+ ok: false,
204
+ issues: [issue([], "schema_error", synchronousIssue(error))],
205
+ };
206
+ }
207
+ },
208
+ });
209
+ }
210
+ function isZodSchema(value) {
211
+ return value instanceof z.ZodType;
212
+ }
213
+ function isStandardSchema(value) {
214
+ if (!value || typeof value !== "object" || !("~standard" in value))
215
+ return false;
216
+ const standard = value["~standard"];
217
+ if (!standard || typeof standard !== "object")
218
+ return false;
219
+ const record = standard;
220
+ if (typeof record.validate !== "function" ||
221
+ !record.jsonSchema ||
222
+ typeof record.jsonSchema !== "object")
223
+ return false;
224
+ const jsonSchema = record.jsonSchema;
225
+ return (typeof jsonSchema.input === "function" &&
226
+ typeof jsonSchema.output === "function");
227
+ }
228
+ function isToolSchema(value) {
229
+ return (!!value &&
230
+ typeof value === "object" &&
231
+ "jsonSchema" in value &&
232
+ "validate" in value &&
233
+ typeof value.validate === "function");
234
+ }
235
+ /** Zod exposes declared checks in its stable v4 definition graph; reject async checks before a run. */
236
+ function hasDeclaredAsyncWork(schema) {
237
+ return visitDefinition(schema._zod?.def, new Set());
238
+ }
239
+ function visitDefinition(value, seen) {
240
+ if (typeof value === "function")
241
+ return value.constructor.name === "AsyncFunction";
242
+ if (!value || typeof value !== "object")
243
+ return false;
244
+ if (seen.has(value))
245
+ return false;
246
+ seen.add(value);
247
+ const nestedDefinition = value._zod?.def;
248
+ if (nestedDefinition !== undefined && nestedDefinition !== value)
249
+ return visitDefinition(nestedDefinition, seen);
250
+ for (const item of Object.values(value)) {
251
+ if (visitDefinition(item, seen))
252
+ return true;
253
+ }
254
+ return false;
255
+ }
256
+ function standardIssue(value) {
257
+ return issue(value.path?.filter(isPathSegment) ?? [], value.code ?? "invalid", value.message ?? "Invalid value");
258
+ }
259
+ function normalizeIssue(value) {
260
+ return issue(value.path?.filter(isPathSegment) ?? [], value.code ?? "invalid", value.message ?? "Invalid value");
261
+ }
262
+ function issue(path, code, text) {
263
+ return Object.freeze({ path: Object.freeze([...path]), code, message: text });
264
+ }
265
+ function isPathSegment(value) {
266
+ return typeof value === "string" || typeof value === "number";
267
+ }
268
+ function isPromiseLike(value) {
269
+ return (!!value &&
270
+ typeof value === "object" &&
271
+ typeof value.then === "function");
272
+ }
273
+ function synchronousIssue(error) {
274
+ const reason = message(error);
275
+ return reason.includes("Promise during synchronous parse")
276
+ ? "Schema validation must be synchronous"
277
+ : reason;
278
+ }
279
+ function message(error) {
280
+ return error instanceof Error ? error.message : String(error);
281
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Thrown from a tool `run` / `execute` to produce a failed tool result.
3
+ * Prefer this over returning `{ kind: "failed" }` in new code.
4
+ */
5
+ export declare class ToolError extends Error {
6
+ readonly code: string;
7
+ constructor(code: string, message: string);
8
+ }
9
+ export declare function isToolError(error: unknown): error is ToolError;
@@ -0,0 +1,20 @@
1
+ const errorBrand = Symbol.for("@nylorun/core/ToolError");
2
+ /**
3
+ * Thrown from a tool `run` / `execute` to produce a failed tool result.
4
+ * Prefer this over returning `{ kind: "failed" }` in new code.
5
+ */
6
+ export class ToolError extends Error {
7
+ code;
8
+ constructor(code, message) {
9
+ super(message);
10
+ this.name = "ToolError";
11
+ Object.defineProperty(this, errorBrand, { value: true });
12
+ this.code = code;
13
+ }
14
+ }
15
+ export function isToolError(error) {
16
+ return (error instanceof ToolError ||
17
+ (typeof error === "object" &&
18
+ error !== null &&
19
+ error[errorBrand] === true));
20
+ }