@kirha/planner 0.1.6 → 0.1.8

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 (3) hide show
  1. package/dist/index.cjs +857 -0
  2. package/dist/index.js +122 -20372
  3. package/package.json +6 -5
package/dist/index.cjs ADDED
@@ -0,0 +1,857 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __toCommonJS = (from) => {
33
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
34
+ if (entry)
35
+ return entry;
36
+ entry = __defProp({}, "__esModule", { value: true });
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (var key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(entry, key))
40
+ __defProp(entry, key, {
41
+ get: __accessProp.bind(from, key),
42
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
43
+ });
44
+ }
45
+ __moduleCache.set(from, entry);
46
+ return entry;
47
+ };
48
+ var __moduleCache;
49
+ var __returnValue = (v) => v;
50
+ function __exportSetter(name, newValue) {
51
+ this[name] = __returnValue.bind(null, newValue);
52
+ }
53
+ var __export = (target, all) => {
54
+ for (var name in all)
55
+ __defProp(target, name, {
56
+ get: all[name],
57
+ enumerable: true,
58
+ configurable: true,
59
+ set: __exportSetter.bind(all, name)
60
+ });
61
+ };
62
+
63
+ // src/index.ts
64
+ var exports_src = {};
65
+ __export(exports_src, {
66
+ parsePlanSteps: () => parsePlanSteps,
67
+ isValidPlan: () => isValidPlan,
68
+ Planner: () => Planner,
69
+ Plan: () => Plan,
70
+ LATEST_MODEL_NAME: () => LATEST_MODEL_NAME
71
+ });
72
+ module.exports = __toCommonJS(exports_src);
73
+ var import_openai = __toESM(require("openai"));
74
+ var import_json53 = __toESM(require("json5"));
75
+
76
+ // src/parser/index.ts
77
+ var import_zod = require("zod");
78
+ var import_json5 = __toESM(require("json5"));
79
+ var import_uuid = require("uuid");
80
+
81
+ // src/utils.ts
82
+ var BRACKET_INDEX_REGEX = /\[(\d+)\]/g;
83
+ var BRACKET_DOUBLE_QUOTE_REGEX = /\["([^"]+)"\]/g;
84
+ var BRACKET_SINGLE_QUOTE_REGEX = /\['([^']+)'\]/g;
85
+ var INDEX_MARKER_PREFIX = "\x00IDX:";
86
+ function createIndexMarker(index) {
87
+ return `${INDEX_MARKER_PREFIX}${index}`;
88
+ }
89
+ function isIndexMarker(segment) {
90
+ return segment.startsWith(INDEX_MARKER_PREFIX);
91
+ }
92
+ function parseIndexMarker(segment) {
93
+ return Number(segment.slice(INDEX_MARKER_PREFIX.length));
94
+ }
95
+ function normalizeToSegments(path) {
96
+ if (!path) {
97
+ return "";
98
+ }
99
+ return path.replace(BRACKET_INDEX_REGEX, (_, index) => `.${createIndexMarker(index)}`).replace(BRACKET_DOUBLE_QUOTE_REGEX, ".$1").replace(BRACKET_SINGLE_QUOTE_REGEX, ".$1");
100
+ }
101
+ function parsePath(path) {
102
+ const normalized = normalizeToSegments(path);
103
+ if (!normalized) {
104
+ return [];
105
+ }
106
+ return normalized.split(".").filter(Boolean).map((segment) => {
107
+ if (isIndexMarker(segment)) {
108
+ return parseIndexMarker(segment);
109
+ }
110
+ return segment;
111
+ });
112
+ }
113
+ function normalizePath(path) {
114
+ return formatPath(parsePath(path));
115
+ }
116
+ function formatPath(path) {
117
+ if (path.length === 0) {
118
+ return "";
119
+ }
120
+ return path.reduce((acc, part) => {
121
+ if (typeof part === "number") {
122
+ return `${acc}[${part}]`;
123
+ }
124
+ return acc ? `${acc}.${part}` : part;
125
+ }, "");
126
+ }
127
+ function isNumericString(value) {
128
+ return /^\d+$/.test(value);
129
+ }
130
+ function getNestedValue(obj, path) {
131
+ let current = obj;
132
+ for (const part of path) {
133
+ if (current === null || current === undefined) {
134
+ return;
135
+ }
136
+ if (typeof part === "number") {
137
+ if (!Array.isArray(current)) {
138
+ return;
139
+ }
140
+ current = current[part];
141
+ continue;
142
+ }
143
+ if (Array.isArray(current) && isNumericString(part)) {
144
+ current = current[Number(part)];
145
+ continue;
146
+ }
147
+ current = current[part];
148
+ }
149
+ return current;
150
+ }
151
+ function traverseReferences(value, callbacks, currentPath = []) {
152
+ if (isDependencyReference(value)) {
153
+ callbacks.onDependency?.(value, currentPath);
154
+ return;
155
+ }
156
+ if (isStringTemplateReference(value)) {
157
+ callbacks.onTemplate?.(value, currentPath);
158
+ return;
159
+ }
160
+ if (Array.isArray(value)) {
161
+ for (const [index, item] of value.entries()) {
162
+ traverseReferences(item, callbacks, [...currentPath, index]);
163
+ }
164
+ return;
165
+ }
166
+ if (typeof value === "object" && value !== null) {
167
+ for (const [key, item] of Object.entries(value)) {
168
+ traverseReferences(item, callbacks, [...currentPath, key]);
169
+ }
170
+ }
171
+ }
172
+ function extractDependencyStepIds(args) {
173
+ const deps = [];
174
+ traverseReferences(args, {
175
+ onDependency: (ref) => deps.push(ref.$fromStep),
176
+ onTemplate: (ref) => {
177
+ for (const val of ref.$values) {
178
+ deps.push(val.$fromStep);
179
+ }
180
+ }
181
+ });
182
+ return [...new Set(deps)];
183
+ }
184
+ function isDependencyReference(value) {
185
+ return typeof value === "object" && value !== null && "$fromStep" in value && "$outputKey" in value;
186
+ }
187
+ function isStringTemplateReference(value) {
188
+ return typeof value === "object" && value !== null && "$fromTemplateString" in value && "$values" in value && Array.isArray(value.$values);
189
+ }
190
+ function isRawDependencyReference(value) {
191
+ return typeof value === "object" && value !== null && "fromStep" in value && "outputKey" in value;
192
+ }
193
+
194
+ // src/parser/template-string-parser.ts
195
+ var NUMERIC_PLACEHOLDER = /\{(\d+)(?:\.([^}]+))?\}/g;
196
+ function parseTemplateString(tpl, stepIdByIndex) {
197
+ const matches = Array.from(tpl.matchAll(NUMERIC_PLACEHOLDER));
198
+ if (matches.length === 0)
199
+ return tpl;
200
+ const values = [];
201
+ let result = "";
202
+ let lastIndex = 0;
203
+ for (const match of matches) {
204
+ const [full, idxStr, path] = match;
205
+ if (!idxStr) {
206
+ console.error("Invalid template reference: missing index", {
207
+ template: tpl,
208
+ index: idxStr
209
+ });
210
+ result += tpl.slice(lastIndex, (match.index ?? 0) + full.length);
211
+ lastIndex = (match.index ?? 0) + full.length;
212
+ continue;
213
+ }
214
+ const idx = Number.parseInt(idxStr, 10);
215
+ const stepId = stepIdByIndex.get(idx);
216
+ if (!stepId) {
217
+ console.error(`Invalid template reference: step index ${idx} not found`, {
218
+ template: tpl,
219
+ index: idx,
220
+ path
221
+ });
222
+ result += tpl.slice(lastIndex, (match.index ?? 0) + full.length);
223
+ lastIndex = (match.index ?? 0) + full.length;
224
+ continue;
225
+ }
226
+ values.push({
227
+ $fromStep: stepId,
228
+ $outputKey: path ? normalizePath(path) : ""
229
+ });
230
+ const start = match.index ?? 0;
231
+ result += `${tpl.slice(lastIndex, start)}{${values.length - 1}}`;
232
+ lastIndex = start + full.length;
233
+ }
234
+ result += tpl.slice(lastIndex);
235
+ if (values.length === 0) {
236
+ return tpl;
237
+ }
238
+ return { $fromTemplateString: result, $values: values };
239
+ }
240
+
241
+ // src/parser/index.ts
242
+ var DependencyReferenceSchema = import_zod.z.object({
243
+ fromStep: import_zod.z.number(),
244
+ outputKey: import_zod.z.string()
245
+ });
246
+ var ParamSchema = import_zod.z.lazy(() => import_zod.z.union([
247
+ import_zod.z.string(),
248
+ import_zod.z.number(),
249
+ import_zod.z.boolean(),
250
+ import_zod.z.null(),
251
+ import_zod.z.array(ParamSchema),
252
+ DependencyReferenceSchema,
253
+ import_zod.z.record(import_zod.z.string(), ParamSchema)
254
+ ]));
255
+ var ToolStepSchema = import_zod.z.object({
256
+ toolName: import_zod.z.string().optional(),
257
+ tool: import_zod.z.string().optional(),
258
+ arguments: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
259
+ args: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
260
+ params: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
261
+ parameters: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
262
+ thought: import_zod.z.string().optional()
263
+ }).transform((step) => ({
264
+ toolName: step.toolName ?? step.tool,
265
+ arguments: step.arguments ?? step.args ?? step.params ?? step.parameters ?? {},
266
+ thought: step.thought
267
+ })).refine((step) => step.toolName !== undefined, {
268
+ message: 'Step must have either "toolName" or "tool"'
269
+ });
270
+ var PlanSchema = import_zod.z.array(ToolStepSchema);
271
+ function parseModelOutput(raw) {
272
+ const thinkMatch = raw.match(/<think>([\s\S]*?)<\/think>/);
273
+ const planMatch = raw.match(/<plan>([\s\S]*?)<\/plan>/);
274
+ const think = thinkMatch?.[1] ? thinkMatch[1].trim() : undefined;
275
+ const plan = planMatch?.[1] ? parsePlanSteps(planMatch[1]) : undefined;
276
+ return { think, plan };
277
+ }
278
+ function parsePlanSteps(rawSteps) {
279
+ const parsedRawSteps = parseJsonCodeBlock(rawSteps);
280
+ const rawPlan = PlanSchema.safeParse(parsedRawSteps);
281
+ if (!rawPlan.success) {
282
+ console.error(`Invalid plan: ${rawPlan.error.message}`, {
283
+ rawPlan,
284
+ rawSteps
285
+ });
286
+ throw new Error(`invalid json parsing: ${rawPlan.error.message}`);
287
+ }
288
+ const stepIdByIndex = new Map;
289
+ const rawPlanWithIds = rawPlan.data.map((step, index) => {
290
+ const stepId = import_uuid.v4();
291
+ stepIdByIndex.set(index, stepId);
292
+ return {
293
+ stepId,
294
+ ...step
295
+ };
296
+ });
297
+ return rawPlanWithIds.map((step) => ({
298
+ stepId: step.stepId,
299
+ status: "pending" /* Pending */,
300
+ toolName: step.toolName,
301
+ arguments: transformParams(step.arguments, stepIdByIndex),
302
+ thought: step.thought
303
+ }));
304
+ }
305
+ function parseJsonCodeBlock(codeBlock) {
306
+ const trimmed = codeBlock.trim();
307
+ const starts = ["{", "["].map((c) => trimmed.indexOf(c)).filter((i) => i !== -1);
308
+ const ends = ["}", "]"].map((c) => trimmed.lastIndexOf(c)).filter((i) => i !== -1);
309
+ if (starts.length === 0 || ends.length === 0) {
310
+ throw new Error("invalid json parsing: no JSON object or array found");
311
+ }
312
+ const start = Math.min(...starts);
313
+ const end = Math.max(...ends);
314
+ if (end < start) {
315
+ throw new Error("invalid json parsing: malformed JSON boundaries");
316
+ }
317
+ const jsonSubstring = trimmed.slice(start, end + 1);
318
+ try {
319
+ return import_json5.default.parse(jsonSubstring);
320
+ } catch (error) {
321
+ if (error instanceof Error) {
322
+ console.error(`invalid json parsing: ${error.message}`, { codeBlock });
323
+ throw new Error(`invalid json parsing: ${error.message}`);
324
+ }
325
+ throw new Error("invalid json parsing");
326
+ }
327
+ }
328
+ function transformParams(params, stepIdByIndex) {
329
+ const transformedParams = {};
330
+ for (const [key, value] of Object.entries(params)) {
331
+ transformedParams[key] = transformParamsValue(value, stepIdByIndex);
332
+ }
333
+ return transformedParams;
334
+ }
335
+ function transformParamsValue(value, stepIdByIndex) {
336
+ if (Array.isArray(value)) {
337
+ return value.map((v) => transformParamsValue(v, stepIdByIndex));
338
+ }
339
+ if (typeof value === "string") {
340
+ const spec = parseTemplateString(value, stepIdByIndex);
341
+ if (typeof spec !== "string") {
342
+ return spec;
343
+ }
344
+ return spec;
345
+ }
346
+ if (isRawDependencyReference(value)) {
347
+ const ref = value;
348
+ const stepId = stepIdByIndex.get(ref.fromStep);
349
+ if (!stepId) {
350
+ console.error(`Invalid dependency reference: step ${ref.fromStep} not found`, { dependencyReference: value });
351
+ throw new Error(`Invalid dependency reference: step ${ref.fromStep} not found`);
352
+ }
353
+ return { $fromStep: stepId, $outputKey: normalizePath(ref.outputKey) };
354
+ }
355
+ if (typeof value === "object" && value !== null) {
356
+ const obj = {};
357
+ for (const [k, v] of Object.entries(value)) {
358
+ obj[k] = transformParamsValue(v, stepIdByIndex);
359
+ }
360
+ return obj;
361
+ }
362
+ return value;
363
+ }
364
+
365
+ // src/executor/index.ts
366
+ async function executePlan(steps, options) {
367
+ const toolsByName = new Map(options.tools.map((t) => [t.name, t]));
368
+ const results = [];
369
+ const outputsByStepId = new Map;
370
+ const statusByStepId = new Map(steps.map((s) => [s.stepId, "pending" /* Pending */]));
371
+ const tryExecuteStep = async (step) => {
372
+ const tool = toolsByName.get(step.toolName);
373
+ if (!tool) {
374
+ statusByStepId.set(step.stepId, "skipped" /* Skipped */);
375
+ results.push({
376
+ stepId: step.stepId,
377
+ toolName: step.toolName,
378
+ arguments: {},
379
+ output: null,
380
+ error: `Tool "${step.toolName}" not found`
381
+ });
382
+ return;
383
+ }
384
+ let resolvedArgs;
385
+ try {
386
+ resolvedArgs = resolveArguments(step.arguments, outputsByStepId);
387
+ } catch (error) {
388
+ statusByStepId.set(step.stepId, "skipped" /* Skipped */);
389
+ results.push({
390
+ stepId: step.stepId,
391
+ toolName: step.toolName,
392
+ arguments: {},
393
+ output: null,
394
+ error: `Failed to resolve arguments: ${error instanceof Error ? error.message : String(error)}`
395
+ });
396
+ return;
397
+ }
398
+ statusByStepId.set(step.stepId, "executing" /* Executing */);
399
+ try {
400
+ const output = await tool.handler(resolvedArgs);
401
+ outputsByStepId.set(step.stepId, output);
402
+ statusByStepId.set(step.stepId, "done" /* Done */);
403
+ results.push({
404
+ stepId: step.stepId,
405
+ toolName: step.toolName,
406
+ arguments: resolvedArgs,
407
+ output
408
+ });
409
+ } catch (error) {
410
+ statusByStepId.set(step.stepId, "failed" /* Failed */);
411
+ results.push({
412
+ stepId: step.stepId,
413
+ toolName: step.toolName,
414
+ arguments: resolvedArgs,
415
+ output: null,
416
+ error: error instanceof Error ? error.message : String(error)
417
+ });
418
+ }
419
+ };
420
+ const getReadySteps = () => {
421
+ return steps.filter((step) => {
422
+ if (statusByStepId.get(step.stepId) !== "pending" /* Pending */) {
423
+ return false;
424
+ }
425
+ const deps = extractDependencyStepIds(step.arguments);
426
+ return deps.every((depStepId) => statusByStepId.get(depStepId) === "done" /* Done */);
427
+ });
428
+ };
429
+ let readySteps = getReadySteps();
430
+ while (readySteps.length > 0) {
431
+ await Promise.all(readySteps.map(tryExecuteStep));
432
+ readySteps = getReadySteps();
433
+ }
434
+ for (const step of steps) {
435
+ if (statusByStepId.get(step.stepId) === "pending" /* Pending */) {
436
+ statusByStepId.set(step.stepId, "skipped" /* Skipped */);
437
+ results.push({
438
+ stepId: step.stepId,
439
+ toolName: step.toolName,
440
+ arguments: step.arguments,
441
+ output: null,
442
+ error: "Skipped: dependencies not satisfied"
443
+ });
444
+ }
445
+ }
446
+ const stepOrder = new Map(steps.map((s, i) => [s.stepId, i]));
447
+ results.sort((a, b) => (stepOrder.get(a.stepId) ?? 0) - (stepOrder.get(b.stepId) ?? 0));
448
+ return results;
449
+ }
450
+ function resolveArguments(args, outputsByStepId) {
451
+ const resolved = {};
452
+ for (const [key, value] of Object.entries(args)) {
453
+ resolved[key] = resolveValue(value, outputsByStepId);
454
+ }
455
+ return resolved;
456
+ }
457
+ function resolveValue(value, outputsByStepId) {
458
+ if (Array.isArray(value)) {
459
+ return value.map((v) => resolveValue(v, outputsByStepId));
460
+ }
461
+ if (isDependencyReference(value)) {
462
+ const stepOutput = outputsByStepId.get(value.$fromStep);
463
+ if (stepOutput === undefined) {
464
+ throw new Error(`Step ${value.$fromStep} output not found`);
465
+ }
466
+ return getNestedValue(stepOutput, parsePath(value.$outputKey));
467
+ }
468
+ if (isStringTemplateReference(value)) {
469
+ let result = value.$fromTemplateString;
470
+ for (const [i, ref] of value.$values.entries()) {
471
+ const stepOutput = outputsByStepId.get(ref.$fromStep);
472
+ const resolvedValue = getNestedValue(stepOutput, parsePath(ref.$outputKey));
473
+ const stringified = typeof resolvedValue === "object" && resolvedValue !== null ? JSON.stringify(resolvedValue) : String(resolvedValue);
474
+ result = result.replace(`{${i}}`, stringified);
475
+ }
476
+ return result;
477
+ }
478
+ if (typeof value === "object" && value !== null) {
479
+ const resolved = {};
480
+ for (const [k, v] of Object.entries(value)) {
481
+ resolved[k] = resolveValue(v, outputsByStepId);
482
+ }
483
+ return resolved;
484
+ }
485
+ return value;
486
+ }
487
+
488
+ // src/validator/index.ts
489
+ var import_json52 = __toESM(require("json5"));
490
+ var import_zod2 = require("zod");
491
+ function isValidPlan(steps, tools) {
492
+ const errors = [];
493
+ const toolsByName = new Map(tools.map((tool) => [tool.name, tool]));
494
+ const schemasByTool = new Map;
495
+ const stepsById = new Map(steps.map((step) => [step.stepId, step]));
496
+ for (const tool of tools) {
497
+ try {
498
+ schemasByTool.set(tool.name, {
499
+ input: parseSchema(tool.inputSchema),
500
+ output: parseSchema(tool.outputSchema)
501
+ });
502
+ } catch (error) {
503
+ const detail = error instanceof Error ? error.message : String(error);
504
+ errors.push({
505
+ code: "schema_parse_error",
506
+ message: `Failed to parse input/output schema for tool "${tool.name}": ${detail}`,
507
+ toolName: tool.name
508
+ });
509
+ }
510
+ }
511
+ for (const step of steps) {
512
+ const tool = toolsByName.get(step.toolName);
513
+ if (!tool) {
514
+ errors.push({
515
+ code: "tool_not_found",
516
+ message: `Tool "${step.toolName}" not found`,
517
+ stepId: step.stepId,
518
+ toolName: step.toolName
519
+ });
520
+ continue;
521
+ }
522
+ const schemas = schemasByTool.get(tool.name);
523
+ if (!schemas) {
524
+ errors.push({
525
+ code: "schema_parse_error",
526
+ message: `Schemas for tool "${tool.name}" could not be parsed`,
527
+ stepId: step.stepId,
528
+ toolName: tool.name
529
+ });
530
+ continue;
531
+ }
532
+ traverseReferences(step.arguments, {
533
+ onDependency: (ref, inputPath) => {
534
+ const formattedInputPath = formatPath(inputPath);
535
+ const expectedSchema = getSchemaAtPath(schemas.input, inputPath);
536
+ if (!expectedSchema) {
537
+ errors.push({
538
+ code: "input_key_missing",
539
+ message: `Input path "${formattedInputPath}" not found on tool "${step.toolName}"`,
540
+ stepId: step.stepId,
541
+ toolName: step.toolName,
542
+ argumentPath: formattedInputPath,
543
+ fromStepId: ref.$fromStep,
544
+ outputPath: ref.$outputKey
545
+ });
546
+ return;
547
+ }
548
+ validateOutputReference(ref, formattedInputPath, expectedSchema, step, stepsById, schemasByTool, errors);
549
+ },
550
+ onTemplate: (ref, inputPath) => {
551
+ validateStringTemplateReference(ref, inputPath, step, schemas.input, stepsById, schemasByTool, errors);
552
+ }
553
+ });
554
+ }
555
+ return { valid: errors.length === 0, errors };
556
+ }
557
+ function parseSchema(rawSchema) {
558
+ try {
559
+ return import_zod2.z.fromJSONSchema(import_json52.default.parse(rawSchema));
560
+ } catch (error) {
561
+ const message = error instanceof Error ? error.message : String(error);
562
+ throw new Error(`Invalid JSON schema: ${message}`);
563
+ }
564
+ }
565
+ function validateOutputReference(reference, inputPath, expectedSchema, step, stepsById, schemasByTool, errors) {
566
+ const baseContext = {
567
+ stepId: step.stepId,
568
+ toolName: step.toolName,
569
+ argumentPath: inputPath,
570
+ fromStepId: reference.$fromStep,
571
+ outputPath: reference.$outputKey
572
+ };
573
+ const sourceStep = stepsById.get(reference.$fromStep);
574
+ if (!sourceStep) {
575
+ errors.push({
576
+ code: "dependency_step_missing",
577
+ message: `Step "${reference.$fromStep}" not found`,
578
+ ...baseContext
579
+ });
580
+ return;
581
+ }
582
+ const sourceSchemas = schemasByTool.get(sourceStep.toolName);
583
+ if (!sourceSchemas) {
584
+ errors.push({
585
+ code: "schema_parse_error",
586
+ message: `Output schema for tool "${sourceStep.toolName}" could not be parsed`,
587
+ ...baseContext
588
+ });
589
+ return;
590
+ }
591
+ const outputSchema = getSchemaAtPath(sourceSchemas.output, parsePath(reference.$outputKey));
592
+ if (!outputSchema) {
593
+ errors.push({
594
+ code: "output_key_missing",
595
+ message: `Output key "${reference.$outputKey}" not found on tool "${sourceStep.toolName}"`,
596
+ ...baseContext
597
+ });
598
+ return;
599
+ }
600
+ if (!isSchemaCompatible(expectedSchema, outputSchema)) {
601
+ errors.push({
602
+ code: "type_mismatch",
603
+ message: `Type mismatch for "${inputPath}"`,
604
+ ...baseContext,
605
+ expectedType: describeSchemaType(expectedSchema),
606
+ actualType: describeSchemaType(outputSchema)
607
+ });
608
+ }
609
+ }
610
+ function validateStringTemplateReference(reference, inputPath, step, inputSchema, stepsById, schemasByTool, errors) {
611
+ const formattedInputPath = formatPath(inputPath);
612
+ const expectedSchema = getSchemaAtPath(inputSchema, inputPath);
613
+ if (!expectedSchema) {
614
+ errors.push({
615
+ code: "input_key_missing",
616
+ message: `Input path "${formattedInputPath}" not found on tool "${step.toolName}"`,
617
+ stepId: step.stepId,
618
+ toolName: step.toolName,
619
+ argumentPath: formattedInputPath
620
+ });
621
+ return;
622
+ }
623
+ if (!isSchemaCompatible(expectedSchema, import_zod2.z.string())) {
624
+ errors.push({
625
+ code: "type_mismatch",
626
+ message: `Type mismatch for "${formattedInputPath}"`,
627
+ stepId: step.stepId,
628
+ toolName: step.toolName,
629
+ argumentPath: formattedInputPath,
630
+ expectedType: describeSchemaType(expectedSchema),
631
+ actualType: "string"
632
+ });
633
+ }
634
+ const stringCoercible = import_zod2.z.union([
635
+ import_zod2.z.string(),
636
+ import_zod2.z.number(),
637
+ import_zod2.z.boolean(),
638
+ import_zod2.z.object({}),
639
+ import_zod2.z.array(import_zod2.z.any())
640
+ ]);
641
+ for (const ref of reference.$values) {
642
+ validateOutputReference(ref, formattedInputPath, stringCoercible, step, stepsById, schemasByTool, errors);
643
+ }
644
+ }
645
+ function getSchemaAtPath(schema, path) {
646
+ const unwrapped = unwrapSchema(schema);
647
+ if (path.length === 0) {
648
+ return unwrapped;
649
+ }
650
+ if (unwrapped instanceof import_zod2.z.ZodUnion || unwrapped instanceof import_zod2.z.ZodXor) {
651
+ const resolvedOptions = unwrapped.options.map((option) => getSchemaAtPath(option, path)).filter((resolved) => resolved !== undefined);
652
+ if (resolvedOptions.length === 0) {
653
+ return;
654
+ }
655
+ if (resolvedOptions.length === 1) {
656
+ return resolvedOptions[0];
657
+ }
658
+ return import_zod2.z.union(resolvedOptions);
659
+ }
660
+ const segment = path[0];
661
+ if (segment === undefined) {
662
+ return unwrapped;
663
+ }
664
+ const remainingPath = path.slice(1);
665
+ if (typeof segment === "number") {
666
+ if (!(unwrapped instanceof import_zod2.z.ZodArray)) {
667
+ return;
668
+ }
669
+ return getSchemaAtPath(unwrapped.element, remainingPath);
670
+ }
671
+ if (unwrapped instanceof import_zod2.z.ZodArray && isNumericString(segment)) {
672
+ return getSchemaAtPath(unwrapped.element, remainingPath);
673
+ }
674
+ if (!(unwrapped instanceof import_zod2.z.ZodObject)) {
675
+ return;
676
+ }
677
+ const childSchema = getObjectChildSchema(unwrapped, segment);
678
+ if (!childSchema) {
679
+ return;
680
+ }
681
+ return getSchemaAtPath(childSchema, remainingPath);
682
+ }
683
+ function getObjectChildSchema(schema, segment) {
684
+ const shape = schema.shape;
685
+ const direct = shape[segment];
686
+ if (direct) {
687
+ return direct;
688
+ }
689
+ const catchall = schema.def.catchall;
690
+ if (catchall && !(catchall instanceof import_zod2.z.ZodUnknown) && !(catchall instanceof import_zod2.z.ZodAny) && !(catchall instanceof import_zod2.z.ZodNever)) {
691
+ return catchall;
692
+ }
693
+ if (Object.keys(shape).length === 0) {
694
+ return import_zod2.z.any();
695
+ }
696
+ return;
697
+ }
698
+ function unwrapSchema(schema) {
699
+ if (schema instanceof import_zod2.z.ZodOptional || schema instanceof import_zod2.z.ZodDefault || schema instanceof import_zod2.z.ZodNullable) {
700
+ return unwrapSchema(schema.def.innerType);
701
+ }
702
+ return schema;
703
+ }
704
+ function isSchemaCompatible(expected, actual) {
705
+ const expectedUnwrapped = unwrapSchema(expected);
706
+ const actualUnwrapped = unwrapSchema(actual);
707
+ if (expectedUnwrapped instanceof import_zod2.z.ZodAny || actualUnwrapped instanceof import_zod2.z.ZodAny) {
708
+ return true;
709
+ }
710
+ if (expectedUnwrapped instanceof import_zod2.z.ZodUnion || expectedUnwrapped instanceof import_zod2.z.ZodXor) {
711
+ return expectedUnwrapped.options.some((option) => isSchemaCompatible(option, actualUnwrapped));
712
+ }
713
+ if (actualUnwrapped instanceof import_zod2.z.ZodUnion || actualUnwrapped instanceof import_zod2.z.ZodXor) {
714
+ return actualUnwrapped.options.some((option) => isSchemaCompatible(expectedUnwrapped, option));
715
+ }
716
+ if (expectedUnwrapped instanceof import_zod2.z.ZodArray && actualUnwrapped instanceof import_zod2.z.ZodArray) {
717
+ return isSchemaCompatible(expectedUnwrapped.element, actualUnwrapped.element);
718
+ }
719
+ if (expectedUnwrapped instanceof import_zod2.z.ZodObject && actualUnwrapped instanceof import_zod2.z.ZodObject) {
720
+ const expectedShape = expectedUnwrapped.shape;
721
+ const actualShape = actualUnwrapped.shape;
722
+ for (const [key, expectedFieldSchema] of Object.entries(expectedShape)) {
723
+ if (isOptionalField(expectedFieldSchema)) {
724
+ continue;
725
+ }
726
+ const actualFieldSchema = actualShape[key];
727
+ if (!actualFieldSchema) {
728
+ return false;
729
+ }
730
+ if (!isSchemaCompatible(expectedFieldSchema, actualFieldSchema)) {
731
+ return false;
732
+ }
733
+ }
734
+ for (const [key, actualFieldSchema] of Object.entries(actualShape)) {
735
+ const expectedFieldSchema = expectedShape[key];
736
+ if (!expectedFieldSchema) {
737
+ continue;
738
+ }
739
+ if (!isSchemaCompatible(expectedFieldSchema, actualFieldSchema)) {
740
+ return false;
741
+ }
742
+ }
743
+ return true;
744
+ }
745
+ const expectedTypes = getTypeSet(expected);
746
+ const actualTypes = getTypeSet(actual);
747
+ if (expectedTypes.has("any") || actualTypes.has("unknown")) {
748
+ return true;
749
+ }
750
+ for (const actualType of actualTypes) {
751
+ if (expectedTypes.has(actualType)) {
752
+ return true;
753
+ }
754
+ }
755
+ return false;
756
+ }
757
+ function isOptionalField(schema) {
758
+ return schema instanceof import_zod2.z.ZodOptional || schema instanceof import_zod2.z.ZodDefault;
759
+ }
760
+ var KNOWN_ZOD_TYPES = [
761
+ [import_zod2.z.ZodAny, "any"],
762
+ [import_zod2.z.ZodUnknown, "any"],
763
+ [import_zod2.z.ZodString, "string"],
764
+ [import_zod2.z.ZodNumber, "number"],
765
+ [import_zod2.z.ZodBoolean, "boolean"],
766
+ [import_zod2.z.ZodNull, "null"],
767
+ [import_zod2.z.ZodArray, "array"],
768
+ [import_zod2.z.ZodTuple, "array"],
769
+ [import_zod2.z.ZodObject, "object"],
770
+ [import_zod2.z.ZodEnum, "string"]
771
+ ];
772
+ var LITERAL_TYPE_MAP = {
773
+ string: "string",
774
+ number: "number",
775
+ boolean: "boolean"
776
+ };
777
+ function getTypeSet(schema) {
778
+ const unwrapped = unwrapSchema(schema);
779
+ for (const [ZodClass, typeName] of KNOWN_ZOD_TYPES) {
780
+ if (unwrapped instanceof ZodClass) {
781
+ return new Set([typeName]);
782
+ }
783
+ }
784
+ if (unwrapped instanceof import_zod2.z.ZodLiteral) {
785
+ const type = LITERAL_TYPE_MAP[typeof unwrapped.value];
786
+ return new Set([type ?? "unknown"]);
787
+ }
788
+ if (unwrapped instanceof import_zod2.z.ZodUnion || unwrapped instanceof import_zod2.z.ZodXor) {
789
+ const types = new Set;
790
+ for (const option of unwrapped.options) {
791
+ for (const type of getTypeSet(option)) {
792
+ types.add(type);
793
+ }
794
+ }
795
+ return types;
796
+ }
797
+ return new Set(["unknown"]);
798
+ }
799
+ function describeSchemaType(schema) {
800
+ return [...getTypeSet(schema)].sort().join(" | ");
801
+ }
802
+ // src/index.ts
803
+ var LATEST_MODEL_NAME = "kirha/planner";
804
+
805
+ class Plan {
806
+ steps;
807
+ think;
808
+ constructor(steps, think) {
809
+ this.steps = steps;
810
+ this.think = think;
811
+ }
812
+ async execute(options) {
813
+ return executePlan(this.steps, options);
814
+ }
815
+ }
816
+
817
+ class Planner {
818
+ openai;
819
+ model;
820
+ constructor(baseUrl, {
821
+ apiKey,
822
+ model = LATEST_MODEL_NAME
823
+ }) {
824
+ this.openai = new import_openai.default({
825
+ baseURL: baseUrl,
826
+ apiKey: apiKey ?? ""
827
+ });
828
+ this.model = model;
829
+ }
830
+ async generatePlan(query, options) {
831
+ const tools = import_json53.default.stringify(options.tools);
832
+ const additionalInstructions = options.instructions ? `# Instructions
833
+ ${options.instructions}
834
+ ` : "";
835
+ const systemPrompt = `${additionalInstructions}# Available tools
836
+ <tools>${tools}</tools>`;
837
+ const response = await this.openai.chat.completions.create({
838
+ model: this.model,
839
+ messages: [
840
+ { role: "system", content: systemPrompt },
841
+ { role: "user", content: query }
842
+ ],
843
+ temperature: options.temperature ?? 0.3,
844
+ max_tokens: options.maxTokens ?? 1e4
845
+ });
846
+ const choice = response.choices?.[0];
847
+ if (!choice) {
848
+ throw new Error("No response from model");
849
+ }
850
+ const rawResponse = choice.message.content;
851
+ if (!rawResponse) {
852
+ throw new Error("No plan generated");
853
+ }
854
+ const { think, plan } = parseModelOutput(rawResponse);
855
+ return plan ? new Plan(plan, think) : undefined;
856
+ }
857
+ }