@botbotgo/agent-harness 0.0.172 → 0.0.173

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.
@@ -84,6 +84,7 @@ export type ParsedToolObject = {
84
84
  subprocess?: boolean;
85
85
  inputSchemaRef?: string;
86
86
  hasModuleSchema?: boolean;
87
+ modelSchema?: Record<string, unknown>;
87
88
  embeddingModelRef?: string;
88
89
  backendOperation?: string;
89
90
  mcpRef?: string;
@@ -134,6 +135,7 @@ export type CompiledTool = {
134
135
  subprocess?: boolean;
135
136
  inputSchemaRef?: string;
136
137
  hasModuleSchema?: boolean;
138
+ modelSchema?: Record<string, unknown>;
137
139
  embeddingModelRef?: string;
138
140
  backendOperation?: string;
139
141
  mcpRef?: string;
@@ -125,6 +125,7 @@ registerToolKind({
125
125
  subprocess: tool.subprocess,
126
126
  inputSchemaRef: tool.inputSchemaRef,
127
127
  hasModuleSchema: tool.hasModuleSchema,
128
+ modelSchema: tool.modelSchema,
128
129
  embeddingModelRef: tool.embeddingModelRef,
129
130
  bundleRefs: [],
130
131
  hitl: tool.hitl
@@ -160,6 +161,7 @@ registerToolKind({
160
161
  subprocess: tool.subprocess,
161
162
  inputSchemaRef: tool.inputSchemaRef,
162
163
  hasModuleSchema: tool.hasModuleSchema,
164
+ modelSchema: tool.modelSchema,
163
165
  embeddingModelRef: tool.embeddingModelRef,
164
166
  backendOperation: tool.backendOperation,
165
167
  bundleRefs: [],
@@ -196,6 +198,7 @@ registerToolKind({
196
198
  subprocess: tool.subprocess,
197
199
  inputSchemaRef: tool.inputSchemaRef,
198
200
  hasModuleSchema: tool.hasModuleSchema,
201
+ modelSchema: tool.modelSchema,
199
202
  embeddingModelRef: tool.embeddingModelRef,
200
203
  mcpRef: tool.mcpRef,
201
204
  bundleRefs: [],
@@ -238,6 +241,7 @@ registerToolKind({
238
241
  subprocess: tool.subprocess,
239
242
  inputSchemaRef: tool.inputSchemaRef,
240
243
  hasModuleSchema: tool.hasModuleSchema,
244
+ modelSchema: tool.modelSchema,
241
245
  embeddingModelRef: tool.embeddingModelRef,
242
246
  bundleRefs: [],
243
247
  hitl: tool.hitl
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.171";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.172";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.171";
1
+ export const AGENT_HARNESS_VERSION = "0.0.172";
@@ -423,7 +423,14 @@ async function loadFunctionToolModule(tool) {
423
423
  const imported = await import(pathToFileURL(isolatedSourcePath).href);
424
424
  const implementationName = tool.implementationName ?? tool.id;
425
425
  const loaded = loadToolModuleDefinition(imported, implementationName);
426
- return { invoke: loaded.invoke, schema: loaded.schema, description: loaded.description, isolatedSourcePath, implementationName };
426
+ return {
427
+ invoke: loaded.invoke,
428
+ schema: loaded.schema,
429
+ modelSchema: loaded.modelSchema,
430
+ description: loaded.description,
431
+ isolatedSourcePath,
432
+ implementationName,
433
+ };
427
434
  })();
428
435
  functionToolModuleCache.set(cacheKey, loading);
429
436
  return loading;
@@ -441,6 +448,8 @@ function createFunctionToolResolver(workspace) {
441
448
  {
442
449
  name: tool.name,
443
450
  description: tool.description,
451
+ schema: tool.modelSchema,
452
+ modelSchema: tool.modelSchema,
444
453
  async invoke(input) {
445
454
  const loaded = await loadFunctionToolModule(tool);
446
455
  const parsedInput = loaded.schema.parse(input ?? {});
@@ -5,6 +5,7 @@ export type ResolvedToolLike = {
5
5
  schema?: {
6
6
  parse?: (input: unknown) => unknown;
7
7
  };
8
+ modelSchema?: Record<string, unknown>;
8
9
  invoke?: (input: unknown, config?: Record<string, unknown>) => Promise<unknown> | unknown;
9
10
  call?: (input: unknown, config?: Record<string, unknown>) => Promise<unknown> | unknown;
10
11
  func?: (input: unknown, config?: Record<string, unknown>) => Promise<unknown> | unknown;
@@ -17,4 +18,9 @@ export declare function normalizeResolvedToolSchema(resolvedTool: ResolvedToolLi
17
18
  } | z.ZodObject<{
18
19
  [x: string]: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
19
20
  }, z.core.$strip> | z.ZodObject<{}, z.core.$loose> | undefined;
21
+ export declare function normalizeModelFacingToolSchema(resolvedTool: ResolvedToolLike): Record<string, unknown> | {
22
+ parse?: (input: unknown) => unknown;
23
+ } | z.ZodObject<{
24
+ [x: string]: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
25
+ }, z.core.$strip> | z.ZodObject<{}, z.core.$loose> | undefined;
20
26
  export declare function asStructuredExecutableTool(resolvedTool: unknown, modelFacingName: string, description: string): unknown;
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { tool as createLangChainTool } from "@langchain/core/tools";
3
+ import { toJsonSchema } from "@langchain/core/utils/json_schema";
3
4
  function isZodSchemaLike(value) {
4
5
  return typeof value === "object" && value !== null && typeof value.parse === "function";
5
6
  }
@@ -10,6 +11,105 @@ function isZodRawShape(value) {
10
11
  const entries = Object.values(value);
11
12
  return entries.length > 0 && entries.every((entry) => isZodSchemaLike(entry));
12
13
  }
14
+ function isJsonSchemaObject(value) {
15
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16
+ }
17
+ function getZodLikeTypeName(value) {
18
+ if (typeof value !== "object" || value === null) {
19
+ return undefined;
20
+ }
21
+ const typed = value;
22
+ if (typeof typed._zod?.def?.type === "string") {
23
+ return typed._zod.def.type;
24
+ }
25
+ if (typeof typed._def?.typeName === "string") {
26
+ return typed._def.typeName.replace(/^Zod/, "").toLowerCase();
27
+ }
28
+ return undefined;
29
+ }
30
+ function getZodLikeInnerSchema(value) {
31
+ if (typeof value !== "object" || value === null) {
32
+ return undefined;
33
+ }
34
+ const typed = value;
35
+ return typed._zod?.def?.innerType ?? typed._zod?.def?.element ?? typed._zod?.def?.valueType ?? typed._def?.innerType ?? typed._def?.type ?? typed._def?.valueType;
36
+ }
37
+ function getZodLikeObjectShape(value) {
38
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
39
+ return undefined;
40
+ }
41
+ const typed = value;
42
+ const directShape = typeof typed.shape === "function"
43
+ ? typed.shape()
44
+ : typeof typed._def?.shape === "function"
45
+ ? typed._def.shape()
46
+ : typed.shape;
47
+ if (typeof directShape === "object" && directShape !== null && !Array.isArray(directShape)) {
48
+ return directShape;
49
+ }
50
+ const v4Shape = typeof typed._zod?.def?.shape === "function" ? typed._zod.def.shape() : typed._zod?.def?.shape;
51
+ if (typeof v4Shape === "object" && v4Shape !== null && !Array.isArray(v4Shape)) {
52
+ return v4Shape;
53
+ }
54
+ return undefined;
55
+ }
56
+ function isZodLikeOptional(value) {
57
+ const typeName = getZodLikeTypeName(value);
58
+ if (!typeName) {
59
+ return false;
60
+ }
61
+ return typeName === "optional" || typeName === "default" || typeName === "catch";
62
+ }
63
+ function buildBestEffortJsonSchema(value) {
64
+ const shape = isZodRawShape(value) ? value : getZodLikeObjectShape(value);
65
+ if (shape) {
66
+ const properties = Object.fromEntries(Object.entries(shape).map(([key, entry]) => [key, buildBestEffortJsonSchema(getZodLikeInnerSchema(entry) ?? entry) ?? {}]));
67
+ const required = Object.entries(shape)
68
+ .filter(([, entry]) => !isZodLikeOptional(entry))
69
+ .map(([key]) => key);
70
+ return {
71
+ type: "object",
72
+ properties,
73
+ ...(required.length > 0 ? { required } : {}),
74
+ };
75
+ }
76
+ const typeName = getZodLikeTypeName(value);
77
+ if (!typeName) {
78
+ return undefined;
79
+ }
80
+ if (typeName === "string") {
81
+ return { type: "string" };
82
+ }
83
+ if (typeName === "number") {
84
+ return { type: "number" };
85
+ }
86
+ if (typeName === "int" || typeName === "integer") {
87
+ return { type: "integer" };
88
+ }
89
+ if (typeName === "boolean") {
90
+ return { type: "boolean" };
91
+ }
92
+ if (typeName === "array") {
93
+ return {
94
+ type: "array",
95
+ items: buildBestEffortJsonSchema(getZodLikeInnerSchema(value)) ?? {},
96
+ };
97
+ }
98
+ if (typeName === "record") {
99
+ return {
100
+ type: "object",
101
+ additionalProperties: buildBestEffortJsonSchema(getZodLikeInnerSchema(value)) ?? {},
102
+ };
103
+ }
104
+ return {};
105
+ }
106
+ function jsonSchemaPreservesShape(schema, expectedKeys) {
107
+ if (expectedKeys.length === 0 || !isJsonSchemaObject(schema)) {
108
+ return true;
109
+ }
110
+ const properties = isJsonSchemaObject(schema.properties) ? Object.keys(schema.properties) : [];
111
+ return expectedKeys.every((key) => properties.includes(key));
112
+ }
13
113
  export function asRecord(value) {
14
114
  return typeof value === "object" && value !== null && !Array.isArray(value)
15
115
  ? { ...value }
@@ -59,6 +159,30 @@ export function normalizeResolvedToolSchema(resolvedTool) {
59
159
  }
60
160
  return z.object({}).passthrough();
61
161
  }
162
+ export function normalizeModelFacingToolSchema(resolvedTool) {
163
+ if (isJsonSchemaObject(resolvedTool.modelSchema)) {
164
+ return resolvedTool.modelSchema;
165
+ }
166
+ const normalizedSchema = normalizeResolvedToolSchema(resolvedTool);
167
+ const normalizedJsonSchema = isJsonSchemaObject(normalizedSchema) ? normalizedSchema : undefined;
168
+ if (normalizedJsonSchema && (normalizedJsonSchema.type === "object" || typeof normalizedJsonSchema.properties === "object")) {
169
+ return normalizedJsonSchema;
170
+ }
171
+ const shape = isZodRawShape(resolvedTool.schema) ? resolvedTool.schema : getZodLikeObjectShape(normalizedSchema);
172
+ const shapeKeys = shape ? Object.keys(shape) : [];
173
+ if (shapeKeys.length === 0) {
174
+ return normalizedSchema;
175
+ }
176
+ try {
177
+ if (jsonSchemaPreservesShape(toJsonSchema(normalizedSchema), shapeKeys)) {
178
+ return normalizedSchema;
179
+ }
180
+ }
181
+ catch {
182
+ // Fall through to the best-effort JSON schema below.
183
+ }
184
+ return buildBestEffortJsonSchema(resolvedTool.schema ?? normalizedSchema) ?? normalizedSchema;
185
+ }
62
186
  export function asStructuredExecutableTool(resolvedTool, modelFacingName, description) {
63
187
  if (!hasCallableToolHandler(resolvedTool)) {
64
188
  return resolvedTool;
@@ -70,6 +194,6 @@ export function asStructuredExecutableTool(resolvedTool, modelFacingName, descri
70
194
  return createLangChainTool(async (input, config) => handler(input, config), {
71
195
  name: modelFacingName,
72
196
  description,
73
- schema: normalizeResolvedToolSchema(resolvedTool),
197
+ schema: normalizeModelFacingToolSchema(resolvedTool),
74
198
  });
75
199
  }
@@ -38,13 +38,17 @@ function mapSingleFieldScalarArg(args, expectedKey, rawArgsInput) {
38
38
  }
39
39
  export function normalizeToolArgsForSchema(args, schema, rawArgsInput) {
40
40
  const schemaDef = isObject(schema) ? schema._def : undefined;
41
- const shape = schemaDef
41
+ const zodShape = schemaDef
42
42
  ? isRecord(schemaDef.shape)
43
43
  ? schemaDef.shape
44
44
  : typeof schemaDef.shape === "function"
45
45
  ? schemaDef.shape()
46
46
  : undefined
47
47
  : undefined;
48
+ const jsonShape = isObject(schema) && isRecord(schema.properties)
49
+ ? (schema.properties ?? undefined)
50
+ : undefined;
51
+ const shape = zodShape && isRecord(zodShape) ? zodShape : jsonShape;
48
52
  if (!shape || !isRecord(shape)) {
49
53
  return args;
50
54
  }
@@ -4,6 +4,7 @@ export type LoadedToolModule = {
4
4
  implementationName: string;
5
5
  invoke: (input: unknown, context?: Record<string, unknown>) => Promise<unknown> | unknown;
6
6
  schema: ReturnType<typeof normalizeToolSchema>;
7
+ modelSchema?: Record<string, unknown>;
7
8
  hasModuleSchema: boolean;
8
9
  description: string;
9
10
  retryable?: boolean;
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { readSkillMetadata } from "./runtime/support/skill-metadata.js";
3
- import { TOOL_DEFINITION_MARKER, normalizeToolSchema } from "./tools.js";
3
+ import { TOOL_DEFINITION_MARKER, buildToolJsonSchema, normalizeToolSchema } from "./tools.js";
4
4
  const TOOL_MODULE_EXTENSIONS = new Set([".mjs", ".js", ".cjs"]);
5
5
  function isToolDefinitionObject(value) {
6
6
  return typeof value === "object" && value !== null && value[TOOL_DEFINITION_MARKER] === true;
@@ -20,6 +20,7 @@ function loadToolObjectDefinition(imported, exportName) {
20
20
  implementationName: definition.name?.trim() || exportName,
21
21
  invoke: definition.invoke,
22
22
  schema: normalizeToolSchema(definition.schema),
23
+ modelSchema: buildToolJsonSchema(definition.schema),
23
24
  hasModuleSchema: true,
24
25
  description: definition.description.trim(),
25
26
  retryable: definition.retryable === true,
package/dist/tools.d.ts CHANGED
@@ -18,6 +18,7 @@ export type ToolDefinitionObject = {
18
18
  invoke: (input: unknown, context?: Record<string, unknown>) => Promise<unknown> | unknown;
19
19
  [TOOL_DEFINITION_MARKER]: true;
20
20
  };
21
+ export declare function buildToolJsonSchema(schema?: SchemaInput): Record<string, unknown> | undefined;
21
22
  export declare function normalizeToolSchema(schema: SchemaInput): ZodTypeAny & {
22
23
  shape?: ZodRawShape;
23
24
  };
package/dist/tools.js CHANGED
@@ -3,6 +3,112 @@ export const TOOL_DEFINITION_MARKER = "__agent_harness_tool_definition__";
3
3
  function isZodSchema(value) {
4
4
  return typeof value === "object" && value !== null && typeof value.parse === "function";
5
5
  }
6
+ function getZodLikeTypeName(value) {
7
+ if (typeof value !== "object" || value === null) {
8
+ return undefined;
9
+ }
10
+ const typed = value;
11
+ if (typeof typed._zod?.def?.type === "string") {
12
+ return typed._zod.def.type;
13
+ }
14
+ if (typeof typed._def?.typeName === "string") {
15
+ return typed._def.typeName.replace(/^Zod/, "").toLowerCase();
16
+ }
17
+ return undefined;
18
+ }
19
+ function getZodLikeObjectShape(value) {
20
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
21
+ return undefined;
22
+ }
23
+ const typed = value;
24
+ const directShape = typeof typed.shape === "function"
25
+ ? typed.shape()
26
+ : typeof typed._def?.shape === "function"
27
+ ? typed._def.shape()
28
+ : typed.shape;
29
+ if (typeof directShape === "object" && directShape !== null && !Array.isArray(directShape)) {
30
+ return directShape;
31
+ }
32
+ const zod4Shape = typeof typed._zod?.def?.shape === "function" ? typed._zod.def.shape() : typed._zod?.def?.shape;
33
+ if (typeof zod4Shape === "object" && zod4Shape !== null && !Array.isArray(zod4Shape)) {
34
+ return zod4Shape;
35
+ }
36
+ return undefined;
37
+ }
38
+ function getZodLikeInnerSchema(value) {
39
+ if (typeof value !== "object" || value === null) {
40
+ return undefined;
41
+ }
42
+ const typed = value;
43
+ return typed._zod?.def?.innerType ?? typed._zod?.def?.element ?? typed._zod?.def?.valueType ?? typed._def?.innerType ?? typed._def?.type ?? typed._def?.valueType;
44
+ }
45
+ function buildJsonSchemaFromZodLike(value) {
46
+ const shape = getZodLikeObjectShape(value);
47
+ if (shape) {
48
+ const properties = Object.fromEntries(Object.entries(shape).map(([key, entry]) => [key, buildJsonSchemaFromZodLike(getZodLikeInnerSchema(entry) ?? entry) ?? {}]));
49
+ const required = Object.entries(shape)
50
+ .filter(([, entry]) => {
51
+ const typeName = getZodLikeTypeName(entry);
52
+ return typeName !== "optional" && typeName !== "default" && typeName !== "catch";
53
+ })
54
+ .map(([key]) => key);
55
+ return {
56
+ type: "object",
57
+ properties,
58
+ ...(required.length > 0 ? { required } : {}),
59
+ };
60
+ }
61
+ const typeName = getZodLikeTypeName(value);
62
+ switch (typeName) {
63
+ case "string":
64
+ return { type: "string" };
65
+ case "number":
66
+ return { type: "number" };
67
+ case "int":
68
+ case "integer":
69
+ return { type: "integer" };
70
+ case "boolean":
71
+ return { type: "boolean" };
72
+ case "unknown":
73
+ case "any":
74
+ return {};
75
+ case "array":
76
+ return {
77
+ type: "array",
78
+ items: buildJsonSchemaFromZodLike(getZodLikeInnerSchema(value)) ?? {},
79
+ };
80
+ case "record":
81
+ return {
82
+ type: "object",
83
+ additionalProperties: buildJsonSchemaFromZodLike(getZodLikeInnerSchema(value)) ?? {},
84
+ };
85
+ case "optional":
86
+ case "nullable":
87
+ case "default":
88
+ case "catch":
89
+ return buildJsonSchemaFromZodLike(getZodLikeInnerSchema(value)) ?? {};
90
+ default:
91
+ return undefined;
92
+ }
93
+ }
94
+ export function buildToolJsonSchema(schema) {
95
+ if (!schema) {
96
+ return undefined;
97
+ }
98
+ if (isZodSchema(schema)) {
99
+ return buildJsonSchemaFromZodLike(schema);
100
+ }
101
+ return {
102
+ type: "object",
103
+ properties: Object.fromEntries(Object.entries(schema).map(([key, value]) => [key, buildJsonSchemaFromZodLike(value) ?? {}])),
104
+ required: Object.entries(schema)
105
+ .filter(([, value]) => {
106
+ const typeName = getZodLikeTypeName(value);
107
+ return typeName !== "optional" && typeName !== "default" && typeName !== "catch";
108
+ })
109
+ .map(([key]) => key),
110
+ };
111
+ }
6
112
  export function normalizeToolSchema(schema) {
7
113
  if (isZodSchema(schema)) {
8
114
  return schema;
@@ -765,6 +765,7 @@ export async function readToolModuleItems(root) {
765
765
  description: definition.description,
766
766
  implementationName: definition.implementationName,
767
767
  hasModuleSchema: definition.hasModuleSchema,
768
+ ...(definition.modelSchema ? { modelSchema: definition.modelSchema } : {}),
768
769
  ...(definition.retryable !== undefined ? { retryable: definition.retryable } : {}),
769
770
  ...(definition.memory ? { config: { memory: definition.memory } } : {}),
770
771
  },
@@ -270,6 +270,7 @@ export function parseToolObject(object) {
270
270
  subprocess: value.subprocess === true,
271
271
  inputSchemaRef: typeof asObject(value.inputSchema)?.ref === "string" ? String(asObject(value.inputSchema)?.ref) : undefined,
272
272
  hasModuleSchema: value.hasModuleSchema === true,
273
+ modelSchema: asObject(value.modelSchema),
273
274
  embeddingModelRef: typeof value.embeddingModelRef === "string"
274
275
  ? value.embeddingModelRef
275
276
  : typeof value.embeddingModel === "string"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.172",
3
+ "version": "0.0.173",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",