History

misc stuff

Rhys Sullivan committed 8 months ago

Commit f045f3c1 parent 20f99de16 files · +744 −1741Browse files

Showing the changes to packages/codemode-core/src (4 of 16 files). Show all

Unified · Split

4 files
  1. packages/codemode-core/src/discovery.ts +205 −33
  2. packages/codemode-core/src/index.test.ts +78 −0
  3. packages/codemode-core/src/system-tools.ts +143 −167
  4. packages/codemode-core/src/types.ts +40 −0
packages/codemode-core/src/discovery.ts+205−33
@@ -6,10 +6,14 @@ import type { · 5 unmodified lines
66 DescribePrimitive,
77 DiscoverPrimitive,
88 DiscoveryPrimitives,
9+ SearchHit,
910 ToolCatalog,
11+ ToolCatalogEntry,
1012 ToolDescriptor,
1113 ToolMap,
14+ ToolNamespace,
1215 ToolPath,
16+ ToolSchemaBundle,
1317 } from "./types";
1418
1519 const tokenize = (value: string): string[] =>
@@ -57,18 +61,117 @@ const namespaceFromPath = (path: string): string => { · 41 unmodified lines
5761 return second ? `${first}.${second}` : first;
5862 };
5963
64+const namespaceFromCatalogEntry = (entry: ToolCatalogEntry): string =>
65+ entry.namespace ?? namespaceFromPath(entry.descriptor.path);
66+
67+const searchableTextForEntry = (entry: ToolCatalogEntry): string =>
68+ entry.searchText?.trim().toLowerCase() || searchableTextForTool(entry.descriptor);
69+
70+const scoreCatalogEntry = (
71+ queryTokens: readonly string[],
72+ entry: ToolCatalogEntry,
73+): number => {
74+ if (entry.score) {
75+ return entry.score(queryTokens);
76+ }
77+
78+ const haystack = searchableTextForEntry(entry);
79+ return queryTokens.reduce(
80+ (total, token) => total + (haystack.includes(token) ? 1 : 0),
81+ 0,
82+ );
83+};
84+
85+const mergeToolNamespaces = (
86+ groups: ReadonlyArray<readonly ToolNamespace[]>,
87+): ToolNamespace[] => {
88+ const merged = new Map<string, ToolNamespace>();
89+
90+ for (const group of groups) {
91+ for (const namespace of group) {
92+ const existing = merged.get(namespace.namespace);
93+ merged.set(namespace.namespace, {
94+ namespace: namespace.namespace,
95+ displayName: existing?.displayName ?? namespace.displayName,
96+ ...(existing?.toolCount !== undefined || namespace.toolCount !== undefined
97+ ? {
98+ toolCount:
99+ (existing?.toolCount ?? 0) + (namespace.toolCount ?? 0),
100+ }
101+ : {}),
102+ });
103+ }
104+ }
105+
106+ return [...merged.values()].sort((left, right) =>
107+ left.namespace.localeCompare(right.namespace),
108+ );
109+};
110+
111+const dedupeToolDescriptors = (
112+ groups: ReadonlyArray<readonly ToolDescriptor[]>,
113+): ToolDescriptor[] => {
114+ const merged = new Map<string, ToolDescriptor>();
115+
116+ for (const group of groups) {
117+ for (const descriptor of group) {
118+ if (!merged.has(descriptor.path)) {
119+ merged.set(descriptor.path, descriptor);
120+ }
121+ }
122+ }
123+
124+ return [...merged.values()].sort((left, right) =>
125+ left.path.localeCompare(right.path),
126+ );
127+};
128+
129+const dedupeSearchHits = (
130+ groups: ReadonlyArray<readonly SearchHit[]>,
131+): SearchHit[] => {
132+ const merged = new Map<string, SearchHit>();
133+
134+ for (const group of groups) {
135+ for (const hit of group) {
136+ if (!merged.has(hit.path)) {
137+ merged.set(hit.path, hit);
138+ }
139+ }
140+ }
141+
142+ return [...merged.values()].sort(
143+ (left, right) => right.score - left.score || left.path.localeCompare(right.path),
144+ );
145+};
146+
60147 export function createToolCatalogFromTools(input: {
61148 tools: ToolMap;
62149 defaultNamespace?: string;
63150 }): ToolCatalog {
64− const descriptors = toolDescriptorsFromTools({
65− tools: input.tools,
151+ return createToolCatalogFromEntries({
152+ entries: toolDescriptorsFromTools({
153+ tools: input.tools,
154+ }).map((descriptor) => ({
155+ descriptor,
156+ ...(input.defaultNamespace !== undefined
157+ ? { namespace: input.defaultNamespace }
158+ : {}),
159+ })),
66160 });
67− const byPath = new Map(descriptors.map((descriptor) => [descriptor.path as string, descriptor]));
161+}
162+
163+export function createToolCatalogFromEntries(input: {
164+ entries: ReadonlyArray<ToolCatalogEntry>;
165+ getSchemaBundle?: (input: { id: string }) => Effect.Effect<ToolSchemaBundle | null, unknown>;
166+}): ToolCatalog {
167+ const entries = [...input.entries];
168+ const byPath = new Map(
169+ entries.map((entry) => [entry.descriptor.path as string, entry]),
170+ );
68171 const namespaceCounts = new Map<string, number>();
69172
70− for (const descriptor of descriptors) {
71− const namespace = input.defaultNamespace ?? namespaceFromPath(descriptor.path);
173+ for (const entry of entries) {
174+ const namespace = namespaceFromCatalogEntry(entry);
72175 namespaceCounts.set(namespace, (namespaceCounts.get(namespace) ?? 0) + 1);
73176 }
74177
@@ -81,23 +184,22 @@ export function createToolCatalogFromTools(input: { · 6 unmodified lines
81184 ),
82185 listTools: ({ namespace, query, limit, includeSchemas = false }) =>
83186 Effect.succeed(
84− descriptors
85− .filter((descriptor) => {
86− const descriptorNamespace = input.defaultNamespace ?? namespaceFromPath(descriptor.path);
87− return !namespace || descriptorNamespace === namespace;
88− })
89− .filter((descriptor) => {
187+ entries
188+ .filter((entry) =>
189+ !namespace || namespaceFromCatalogEntry(entry) === namespace,
190+ )
191+ .filter((entry) => {
90192 if (!query) {
91193 return true;
92194 }
93195
94− const haystack = searchableTextForTool(descriptor);
196+ const haystack = searchableTextForEntry(entry);
95197 return tokenize(query).every((token) => haystack.includes(token));
96198 })
97199 .slice(0, limit)
98− .map((descriptor) =>
200+ .map((entry) =>
99201 projectDescriptor({
100− descriptor,
202+ descriptor: entry.descriptor,
101203 includeSchemas,
102204 })
103205 ),
@@ -106,33 +208,27 @@ export function createToolCatalogFromTools(input: { · 2 unmodified lines
106208 Effect.succeed(
107209 byPath.get(path)
108210 ? projectDescriptor({
109− descriptor: byPath.get(path)!,
211+ descriptor: byPath.get(path)!.descriptor,
110212 includeSchemas,
111213 })
112214 : null,
113215 ),
114− getSchemaBundle: () => Effect.succeed(null),
216+ getSchemaBundle: ({ id }) =>
217+ input.getSchemaBundle
218+ ? input.getSchemaBundle({ id })
219+ : Effect.succeed(null),
115220 searchTools: ({ query, namespace, limit }) => {
116221 const queryTokens = tokenize(query);
117222
118223 return Effect.succeed(
119− descriptors
120− .filter((descriptor) => {
121− const descriptorNamespace = input.defaultNamespace ?? namespaceFromPath(descriptor.path);
122− return !namespace || descriptorNamespace === namespace;
123− })
124− .map((descriptor) => {
125− const haystack = searchableTextForTool(descriptor);
126− const score = queryTokens.reduce(
127− (total, token) => total + (haystack.includes(token) ? 1 : 0),
128− 0,
129− );
130−
131− return {
132− path: descriptor.path,
133− score,
134− };
135− })
224+ entries
225+ .filter((entry) =>
226+ !namespace || namespaceFromCatalogEntry(entry) === namespace,
227+ )
228+ .map((entry) => ({
229+ path: entry.descriptor.path,
230+ score: scoreCatalogEntry(queryTokens, entry),
231+ }))
136232 .filter((hit) => hit.score > 0)
137233 .sort((left, right) => right.score - left.score)
138234 .slice(0, limit),
@@ -141,6 +237,82 @@ export function createToolCatalogFromTools(input: { · 2 unmodified lines
141237 } satisfies ToolCatalog;
142238 }
143239
240+export function mergeToolCatalogs(input: {
241+ catalogs: ReadonlyArray<ToolCatalog>;
242+}): ToolCatalog {
243+ const catalogs = [...input.catalogs];
244+
245+ return {
246+ listNamespaces: ({ limit }) =>
247+ Effect.gen(function* () {
248+ const groups = yield* Effect.forEach(
249+ catalogs,
250+ (catalog) => catalog.listNamespaces({ limit: Math.max(limit, limit * catalogs.length) }),
251+ { concurrency: "unbounded" },
252+ );
253+
254+ return mergeToolNamespaces(groups).slice(0, limit);
255+ }),
256+
257+ listTools: ({ namespace, query, limit, includeSchemas = false }) =>
258+ Effect.gen(function* () {
259+ const groups = yield* Effect.forEach(
260+ catalogs,
261+ (catalog) =>
262+ catalog.listTools({
263+ ...(namespace !== undefined ? { namespace } : {}),
264+ ...(query !== undefined ? { query } : {}),
265+ limit: Math.max(limit, limit * catalogs.length),
266+ includeSchemas,
267+ }),
268+ { concurrency: "unbounded" },
269+ );
270+
271+ return dedupeToolDescriptors(groups).slice(0, limit);
272+ }),
273+
274+ getToolByPath: ({ path, includeSchemas }) =>
275+ Effect.gen(function* () {
276+ for (const catalog of catalogs) {
277+ const descriptor = yield* catalog.getToolByPath({ path, includeSchemas });
278+ if (descriptor) {
279+ return descriptor;
280+ }
281+ }
282+
283+ return null;
284+ }),
285+
286+ getSchemaBundle: ({ id }) =>
287+ Effect.gen(function* () {
288+ for (const catalog of catalogs) {
289+ const bundle = yield* catalog.getSchemaBundle({ id });
290+ if (bundle) {
291+ return bundle;
292+ }
293+ }
294+
295+ return null;
296+ }),
297+
298+ searchTools: ({ query, namespace, limit }) =>
299+ Effect.gen(function* () {
300+ const groups = yield* Effect.forEach(
301+ catalogs,
302+ (catalog) =>
303+ catalog.searchTools({
304+ query,
305+ ...(namespace !== undefined ? { namespace } : {}),
306+ limit: Math.max(limit, limit * catalogs.length),
307+ }),
308+ { concurrency: "unbounded" },
309+ );
310+
311+ return dedupeSearchHits(groups).slice(0, limit);
312+ }),
313+ } satisfies ToolCatalog;
314+}
315+
144316 export function createStaticDiscoveryFromTools(input: {
145317 tools: ToolMap;
146318 sourceKey?: string;
packages/codemode-core/src/index.test.ts+78−0
@@ -9,6 +9,7 @@ import * as TestClock from "effect/TestClock"; · 8 unmodified lines
99 import {
1010 allowAllToolInteractions,
1111 createToolCatalogDiscovery,
12+ createToolCatalogFromEntries,
1213 createToolCatalogFromTools,
1314 createStaticDiscoveryFromTools,
1415 createSystemToolMap,
@@ -355,6 +356,83 @@ describe("codemode-core", () => { · 340 unmodified lines
355356 }),
356357 );
357358
359+ it.effect("builds a tool catalog from descriptor entries", () =>
360+ Effect.gen(function* () {
361+ const catalog = createToolCatalogFromEntries({
362+ entries: [
363+ {
364+ namespace: "github.repos",
365+ searchText: "github repository details get repo",
366+ descriptor: {
367+ path: asToolPath("github.repos.getRepo"),
368+ sourceKey: "source.github",
369+ description: "Get repository details",
370+ inputType: "{ owner: string; repo: string }",
371+ outputType: "Repository",
372+ inputSchemaJson: '{"type":"object"}',
373+ },
374+ },
375+ {
376+ namespace: "github.issues",
377+ searchText: "github issues list repository issues",
378+ score: (queryTokens) =>
379+ queryTokens.includes("issues") && queryTokens.includes("repository")
380+ ? 50
381+ : 0,
382+ descriptor: {
383+ path: asToolPath("github.issues.list"),
384+ sourceKey: "source.github",
385+ description: "List repository issues",
386+ inputType: "{ owner: string; repo: string }",
387+ outputType: "Issue[]",
388+ },
389+ },
390+ ],
391+ getSchemaBundle: ({ id }) =>
392+ Effect.succeed(
393+ id === "bundle_1"
394+ ? {
395+ id,
396+ kind: "json_schema_ref_map",
397+ hash: "hash_1",
398+ refsJson: "{}",
399+ }
400+ : null,
401+ ),
402+ });
403+
404+ const namespaces = yield* catalog.listNamespaces({ limit: 10 });
405+ expect(namespaces).toEqual([
406+ { namespace: "github.repos", toolCount: 1 },
407+ { namespace: "github.issues", toolCount: 1 },
408+ ]);
409+
410+ const listed = yield* catalog.listTools({
411+ namespace: "github.repos",
412+ query: "repository details",
413+ limit: 10,
414+ includeSchemas: false,
415+ });
416+ expect(listed[0]?.path).toBe("github.repos.getRepo");
417+ expect(listed[0]?.inputSchemaJson).toBeUndefined();
418+
419+ const discovered = yield* catalog.searchTools({
420+ query: "repository issues",
421+ limit: 10,
422+ });
423+ expect(discovered[0]?.path).toBe("github.issues.list");
424+
425+ const described = yield* catalog.getToolByPath({
426+ path: asToolPath("github.repos.getRepo"),
427+ includeSchemas: true,
428+ });
429+ expect(described?.inputSchemaJson).toContain('"type"');
430+
431+ const bundle = yield* catalog.getSchemaBundle({ id: "bundle_1" });
432+ expect(bundle?.id).toBe("bundle_1");
433+ }),
434+ );
435+
358436 it.effect("system tools can be composed as normal tools", () =>
359437 Effect.gen(function* () {
360438 const catalog = createToolCatalogFromTools({
packages/codemode-core/src/system-tools.ts+143−167
@@ -3,11 +3,13 @@ import * as Schema from "effect/Schema"; · 2 unmodified lines
33
44 import { createDiscoveryPrimitivesFromToolCatalog } from "./discovery";
55 import { toTool } from "./tool-map";
6−import type {
7− DiscoveryPrimitives,
8− ToolCatalog,
9− ToolMap,
10− ToolPath,
6+import {
7+ ToolDescriptorSchema,
8+ ToolNamespaceSchema,
9+ ToolSchemaBundleSchema,
10+ type ToolCatalog,
11+ type ToolMap,
12+ type ToolPath,
1113 } from "./types";
1214
1315 const asToolPath = (value: string): ToolPath => value as ToolPath;
@@ -20,13 +22,7 @@ const catalogNamespacesInputSchema = Schema.standardSchemaV1( · 6 unmodified lines
2022
2123 const catalogNamespacesOutputSchema = Schema.standardSchemaV1(
2224 Schema.Struct({
23− namespaces: Schema.Array(
24− Schema.Struct({
25− namespace: Schema.String,
26− displayName: Schema.optional(Schema.String),
27− toolCount: Schema.optional(Schema.Number),
28− }),
29− ),
25+ namespaces: Schema.Array(ToolNamespaceSchema),
3026 }),
3127 );
3228
@@ -41,23 +37,7 @@ const catalogToolsInputSchema = Schema.standardSchemaV1( · 8 unmodified lines
4137
4238 const catalogToolsOutputSchema = Schema.standardSchemaV1(
4339 Schema.Struct({
44− results: Schema.Array(
45− Schema.Struct({
46− path: Schema.String,
47− sourceKey: Schema.String,
48− description: Schema.optional(Schema.String),
49− interaction: Schema.optional(Schema.String),
50− inputType: Schema.optional(Schema.String),
51− outputType: Schema.optional(Schema.String),
52− inputSchemaJson: Schema.optional(Schema.String),
53− outputSchemaJson: Schema.optional(Schema.String),
54− schemaBundleId: Schema.optional(Schema.String),
55− exampleInputJson: Schema.optional(Schema.String),
56− exampleOutputJson: Schema.optional(Schema.String),
57− providerKind: Schema.optional(Schema.String),
58− providerDataJson: Schema.optional(Schema.String),
59− }),
60− ),
40+ results: Schema.Array(ToolDescriptorSchema),
6141 }),
6242 );
6343
@@ -69,7 +49,7 @@ const describeToolInputSchema = Schema.standardSchemaV1( · 5 unmodified lines
6949 );
7050
7151 const describeToolOutputSchema = Schema.standardSchemaV1(
72− Schema.NullOr(Schema.Unknown),
52+ Schema.NullOr(ToolDescriptorSchema),
7353 );
7454
7555 const describeSchemaBundleInputSchema = Schema.standardSchemaV1(
@@ -79,14 +59,7 @@ const describeSchemaBundleInputSchema = Schema.standardSchemaV1( · 3 unmodified lines
7959 );
8060
8161 const describeSchemaBundleOutputSchema = Schema.standardSchemaV1(
82− Schema.NullOr(
83− Schema.Struct({
84− id: Schema.String,
85− kind: Schema.String,
86− hash: Schema.String,
87− refsJson: Schema.String,
88− }),
89− ),
62+ Schema.NullOr(ToolSchemaBundleSchema),
9063 );
9164
9265 const discoverInputSchema = Schema.standardSchemaV1(
@@ -97,17 +70,12 @@ const discoverInputSchema = Schema.standardSchemaV1( · 4 unmodified lines
9770 }),
9871 );
9972
100−const discoverResultItemSchema = Schema.Struct({
101− path: Schema.String,
102− score: Schema.Number,
103− description: Schema.optional(Schema.String),
104− interaction: Schema.optional(Schema.String),
105− inputType: Schema.optional(Schema.String),
106− outputType: Schema.optional(Schema.String),
107− inputSchemaJson: Schema.optional(Schema.String),
108− outputSchemaJson: Schema.optional(Schema.String),
109− schemaBundleId: Schema.optional(Schema.String),
110−});
73+const discoverResultItemSchema = Schema.extend(
74+ ToolDescriptorSchema,
75+ Schema.Struct({
76+ score: Schema.Number,
77+ }),
78+);
11179
11280 const discoverOutputSchema = Schema.standardSchemaV1(
11381 Schema.Struct({
@@ -118,7 +86,8 @@ const discoverOutputSchema = Schema.standardSchemaV1( · 4 unmodified lines
11886 );
11987
12088 export type CreateSystemToolMapInput = {
121− catalog: ToolCatalog;
89+ catalog?: ToolCatalog;
90+ getCatalog?: () => ToolCatalog;
12291 sourceKey?: string;
12392 };
12493
@@ -126,130 +95,137 @@ export const createSystemToolMap = ( · 1 unmodified line
12695 input: CreateSystemToolMapInput,
12796 ): ToolMap => {
12897 const sourceKey = input.sourceKey ?? "system";
129− const primitives: DiscoveryPrimitives = createDiscoveryPrimitivesFromToolCatalog({
130− catalog: input.catalog,
131− });
98+ const getCatalog = (): ToolCatalog => {
99+ if (input.catalog) {
100+ return input.catalog;
101+ }
102+ if (input.getCatalog) {
103+ return input.getCatalog();
104+ }
105+
106+ throw new Error("createSystemToolMap requires a catalog or getCatalog");
107+ };
108+ const getPrimitives = () =>
109+ createDiscoveryPrimitivesFromToolCatalog({
110+ catalog: getCatalog(),
111+ }) as Required<ReturnType<typeof createDiscoveryPrimitivesFromToolCatalog>>;
132112
133113 const tools: ToolMap = {};
134114
135− if (primitives.catalog) {
136− tools["catalog.namespaces"] = toTool({
137− tool: {
138− description: "List available namespaces with display names and tool counts",
139− inputSchema: catalogNamespacesInputSchema,
140− outputSchema: catalogNamespacesOutputSchema,
141− execute: ({ limit }: { limit?: number }) =>
142− Effect.runPromise(
143− primitives.catalog!.namespaces({
144− ...(limit !== undefined ? { limit } : {}),
145− }),
146− ),
147− },
148− metadata: {
149− sourceKey,
150− interaction: "auto",
151− },
152− });
115+ tools["catalog.namespaces"] = toTool({
116+ tool: {
117+ description: "List available namespaces with display names and tool counts",
118+ inputSchema: catalogNamespacesInputSchema,
119+ outputSchema: catalogNamespacesOutputSchema,
120+ execute: ({ limit }: { limit?: number }) =>
121+ Effect.runPromise(
122+ getPrimitives().catalog.namespaces({
123+ ...(limit !== undefined ? { limit } : {}),
124+ }),
125+ ),
126+ },
127+ metadata: {
128+ sourceKey,
129+ interaction: "auto",
130+ },
131+ });
153132
154− tools["catalog.tools"] = toTool({
155− tool: {
156− description: "List tools with optional namespace and query filters",
157− inputSchema: catalogToolsInputSchema,
158− outputSchema: catalogToolsOutputSchema,
159− execute: (
160− input: {
161− namespace?: string;
162− query?: string;
163− limit?: number;
164− includeSchemas?: boolean;
165− },
166− ) =>
167− Effect.runPromise(
168− primitives.catalog!.tools({
169− ...(input.namespace !== undefined ? { namespace: input.namespace } : {}),
170− ...(input.query !== undefined ? { query: input.query } : {}),
171− ...(input.limit !== undefined ? { limit: input.limit } : {}),
172− ...(input.includeSchemas !== undefined
173− ? { includeSchemas: input.includeSchemas }
174− : {}),
175− }),
176− ),
177− },
178− metadata: {
179− sourceKey,
180− interaction: "auto",
181− },
182− });
183− }
133+ tools["catalog.tools"] = toTool({
134+ tool: {
135+ description: "List tools with optional namespace and query filters",
136+ inputSchema: catalogToolsInputSchema,
137+ outputSchema: catalogToolsOutputSchema,
138+ execute: (
139+ toolInput: {
140+ namespace?: string;
141+ query?: string;
142+ limit?: number;
143+ includeSchemas?: boolean;
144+ },
145+ ) =>
146+ Effect.runPromise(
147+ getPrimitives().catalog.tools({
148+ ...(toolInput.namespace !== undefined
149+ ? { namespace: toolInput.namespace }
150+ : {}),
151+ ...(toolInput.query !== undefined ? { query: toolInput.query } : {}),
152+ ...(toolInput.limit !== undefined ? { limit: toolInput.limit } : {}),
153+ ...(toolInput.includeSchemas !== undefined
154+ ? { includeSchemas: toolInput.includeSchemas }
155+ : {}),
156+ }),
157+ ),
158+ },
159+ metadata: {
160+ sourceKey,
161+ interaction: "auto",
162+ },
163+ });
184164
185− if (primitives.describe) {
186− tools["describe.tool"] = toTool({
187− tool: {
188− description: "Get metadata and optional schemas for a tool path",
189− inputSchema: describeToolInputSchema,
190− outputSchema: describeToolOutputSchema,
191− execute: ({ path, includeSchemas }: { path: string; includeSchemas?: boolean }) =>
192− Effect.runPromise(
193− primitives.describe!.tool({
194− path: asToolPath(path),
195− ...(includeSchemas !== undefined ? { includeSchemas } : {}),
196− }),
197− ),
198− },
199− metadata: {
200− sourceKey,
201− interaction: "auto",
202− },
203− });
165+ tools["describe.tool"] = toTool({
166+ tool: {
167+ description: "Get metadata and optional schemas for a tool path",
168+ inputSchema: describeToolInputSchema,
169+ outputSchema: describeToolOutputSchema,
170+ execute: ({ path, includeSchemas }: { path: string; includeSchemas?: boolean }) =>
171+ Effect.runPromise(
172+ getPrimitives().describe.tool({
173+ path: asToolPath(path),
174+ ...(includeSchemas !== undefined ? { includeSchemas } : {}),
175+ }),
176+ ),
177+ },
178+ metadata: {
179+ sourceKey,
180+ interaction: "auto",
181+ },
182+ });
204183
205− tools["describe.schemaBundle"] = toTool({
206− tool: {
207− description: "Get a shared schema bundle by id",
208− inputSchema: describeSchemaBundleInputSchema,
209− outputSchema: describeSchemaBundleOutputSchema,
210− execute: ({ id }: { id: string }) =>
211− Effect.runPromise(
212− primitives.describe!.schemaBundle({
213− id,
214− }),
215− ),
216− },
217− metadata: {
218− sourceKey,
219− interaction: "auto",
220− },
221− });
222− }
184+ tools["describe.schemaBundle"] = toTool({
185+ tool: {
186+ description: "Get a shared schema bundle by id",
187+ inputSchema: describeSchemaBundleInputSchema,
188+ outputSchema: describeSchemaBundleOutputSchema,
189+ execute: ({ id }: { id: string }) =>
190+ Effect.runPromise(
191+ getPrimitives().describe.schemaBundle({
192+ id,
193+ }),
194+ ),
195+ },
196+ metadata: {
197+ sourceKey,
198+ interaction: "auto",
199+ },
200+ });
223201
224− if (primitives.discover) {
225− tools.discover = toTool({
226− tool: {
227− description: "Search tools by intent and return ranked matches",
228− inputSchema: discoverInputSchema,
229− outputSchema: discoverOutputSchema,
230− execute: (
231− input: {
232− query: string;
233− limit?: number;
234− includeSchemas?: boolean;
235− },
236− ) =>
237− Effect.runPromise(
238− primitives.discover!({
239− query: input.query,
240− ...(input.limit !== undefined ? { limit: input.limit } : {}),
241− ...(input.includeSchemas !== undefined
242− ? { includeSchemas: input.includeSchemas }
243− : {}),
244− }),
245− ),
246− },
247− metadata: {
248− sourceKey,
249− interaction: "auto",
250− },
251− });
252− }
202+ tools.discover = toTool({
203+ tool: {
204+ description: "Search tools by intent and return ranked matches",
205+ inputSchema: discoverInputSchema,
206+ outputSchema: discoverOutputSchema,
207+ execute: (
208+ toolInput: {
209+ query: string;
210+ limit?: number;
211+ includeSchemas?: boolean;
212+ },
213+ ) =>
214+ Effect.runPromise(
215+ getPrimitives().discover({
216+ query: toolInput.query,
217+ ...(toolInput.limit !== undefined ? { limit: toolInput.limit } : {}),
218+ ...(toolInput.includeSchemas !== undefined
219+ ? { includeSchemas: toolInput.includeSchemas }
220+ : {}),
221+ }),
222+ ),
223+ },
224+ metadata: {
225+ sourceKey,
226+ interaction: "auto",
227+ },
228+ });
253229
254230 return tools;
255231 };
packages/codemode-core/src/types.ts+40−0
@@ -1,5 +1,6 @@
11 import type { StandardSchemaV1 } from "@standard-schema/spec";
22 import type * as Effect from "effect/Effect";
3+import * as Schema from "effect/Schema";
34
45 export type ToolPath = string & { readonly __toolPath: unique symbol };
56
@@ -142,6 +143,25 @@ export type ToolDescriptor = { · 136 unmodified lines
142143 providerDataJson?: string;
143144 };
144145
146+export const ToolDescriptorSchema = Schema.Struct({
147+ path: Schema.String,
148+ sourceKey: Schema.String,
149+ description: Schema.optional(Schema.String),
150+ interaction: Schema.optional(
151+ Schema.Union(Schema.Literal("auto"), Schema.Literal("required")),
152+ ),
153+ elicitation: Schema.optional(Schema.Unknown),
154+ inputType: Schema.optional(Schema.String),
155+ outputType: Schema.optional(Schema.String),
156+ inputSchemaJson: Schema.optional(Schema.String),
157+ outputSchemaJson: Schema.optional(Schema.String),
158+ schemaBundleId: Schema.optional(Schema.String),
159+ exampleInputJson: Schema.optional(Schema.String),
160+ exampleOutputJson: Schema.optional(Schema.String),
161+ providerKind: Schema.optional(Schema.String),
162+ providerDataJson: Schema.optional(Schema.String),
163+});
164+
145165 export type ToolSchemaBundle = {
146166 id: string;
147167 kind: string;
@@ -149,17 +169,37 @@ export type ToolSchemaBundle = { · 1 unmodified line
149169 refsJson: string;
150170 };
151171
172+export const ToolSchemaBundleSchema = Schema.Struct({
173+ id: Schema.String,
174+ kind: Schema.String,
175+ hash: Schema.String,
176+ refsJson: Schema.String,
177+});
178+
152179 export type ToolNamespace = {
153180 namespace: string;
154181 displayName?: string;
155182 toolCount?: number;
156183 };
157184
185+export const ToolNamespaceSchema = Schema.Struct({
186+ namespace: Schema.String,
187+ displayName: Schema.optional(Schema.String),
188+ toolCount: Schema.optional(Schema.Number),
189+});
190+
158191 export type SearchHit = {
159192 path: ToolPath;
160193 score: number;
161194 };
162195
196+export type ToolCatalogEntry = {
197+ descriptor: ToolDescriptor;
198+ namespace?: string;
199+ searchText?: string;
200+ score?: (queryTokens: readonly string[]) => number;
201+};
202+
163203 export interface ToolCatalog {
164204 listNamespaces(input: {
165205 limit: number;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t