@geminixiang/mikan 1.0.0-beta.91 → 1.0.0-beta.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/harness/tools/codemode.d.ts.map +1 -1
- package/dist/harness/tools/codemode.js +11 -4
- package/dist/harness/tools/codemode.js.map +1 -1
- package/dist/harness/tools/tool-search.d.ts.map +1 -1
- package/dist/harness/tools/tool-search.js +76 -19
- package/dist/harness/tools/tool-search.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,12 @@ any release.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.0.0-beta.92]
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Codemode guides the model to discover every tool a task needs in one script and to parse MCP results and emit only the fields the answer needs, because everything a script emits is sent again on every later model call. `searchTools()` and `tool_search` rank with BM25 over names, descriptions, argument schemas, and the MCP server name, splitting camelCase like Pi, so a query such as `OpenConnector` finds `mcp__open-connector__*` tools; the default limit is 8.
|
|
17
|
+
|
|
12
18
|
## [1.0.0-beta.91]
|
|
13
19
|
|
|
14
20
|
### Changed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codemode.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/codemode.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAmB,MAAM,aAAa,CAAC;AAS1F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAIlE;AAqBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,CAK3E;AAeD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"codemode.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/codemode.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAmB,MAAM,aAAa,CAAC;AAS1F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAIlE;AAqBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,CAK3E;AAeD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,CA0KjF"}
|
|
@@ -137,13 +137,20 @@ Global output helpers:
|
|
|
137
137
|
Global discovery helpers (async functions, not methods on tools):
|
|
138
138
|
${renderDeclarations({ globals })}
|
|
139
139
|
|
|
140
|
-
Tools declared to you are also callable from scripts; each one's description says what its call resolves to. Other tools, such as MCP tools, are not listed: find them with searchTools(), inspect them with describeTool() or describeNamespace(), or filter ALL_TOOLS, then call tools[name](args)
|
|
140
|
+
Tools declared to you are also callable from scripts; each one's description says what its call resolves to. Other tools, such as MCP tools, are not listed: find them with searchTools(), inspect them with describeTool() or describeNamespace(), or filter ALL_TOOLS, then call tools[name](args).
|
|
141
|
+
Discover in one script: inspect every namespace and tool the task may need together, then call them in the next script using the declared parameter and return types. Do not guess argument names. Discovery does not load schemas into the model's direct tool set.
|
|
141
142
|
\`\`\`js
|
|
142
143
|
const matches = await searchTools(query, { namespace });
|
|
143
|
-
text(await
|
|
144
|
+
text((await describeNamespace(namespace))?.instructions);
|
|
145
|
+
for (const match of matches.slice(0, 3)) text(await describeTool(match.name));
|
|
144
146
|
\`\`\`
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
MCP tools resolve to their complete CallToolResult { content, structuredContent?, isError? }, never truncated: text results are in content[i].text, usually JSON to parse, and an MCP error resolves with isError: true instead of rejecting. Never emit a whole result: everything you emit stays in the conversation and is sent again on every later model call. Parse it and emit only the fields the answer needs:
|
|
148
|
+
\`\`\`js
|
|
149
|
+
const result = await tools[name](args);
|
|
150
|
+
if (result.isError) return result.content[0]?.text;
|
|
151
|
+
const data = JSON.parse(result.content[0].text);
|
|
152
|
+
return data.items.map(({ id, title }) => ({ id, title }));
|
|
153
|
+
\`\`\``,
|
|
147
154
|
parameters: Type.Object({ label: LABEL_PARAMETER, code: Type.String() }),
|
|
148
155
|
execute: async (params, api, context) => {
|
|
149
156
|
const id = api.callId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codemode.js","sourceRoot":"","sources":["../../../src/harness/tools/codemode.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n CodemodeSandbox,\n mcpStructuredContentSchema,\n parseCodemodeSource,\n renderDeclarations,\n renderToolOutputType,\n renderToolSample,\n toCodemodeIdentifier,\n type CodemodeTool,\n type CodemodeJsonSchema,\n} from \"@earendil-works/pi-codemode\";\nimport { validateToolArguments, type JsonObject } from \"@earendil-works/pi-ai\";\nimport { withAbortSignal } from \"@earendil-works/chord/context\";\nimport type { ToolExecutionApi } from \"@earendil-works/pi-durable\";\nimport { Type } from \"typebox\";\nimport type { CodemodeToolOptions, MikanHarnessTool, MikanToolResult } from \"../types.js\";\nimport { LABEL_PARAMETER } from \"./host-fn-tool.js\";\nimport { tagHarnessTool } from \"./pi-tools.js\";\nimport { START_TASK_TOOL } from \"./task.js\";\nimport { mcpNamespaceKey, searchTools, TOOL_SEARCH_TOOL } from \"./tool-search.js\";\nimport { isRecord } from \"../../unknown-values.js\";\n\nconst TEXT_OUTPUT_SCHEMA: CodemodeJsonSchema = { type: \"string\" };\n\nexport function isCodemodeCallable(tool: MikanHarnessTool): boolean {\n return (\n tool.name !== \"codemode\" && tool.name !== START_TASK_TOOL && tool.name !== TOOL_SEARCH_TOOL\n );\n}\n\nfunction outputSchemaOf(tool: MikanHarnessTool): CodemodeJsonSchema {\n return (tool.outputSchema as CodemodeJsonSchema | undefined) ?? TEXT_OUTPUT_SCHEMA;\n}\n\nfunction describeOutput(schema: CodemodeJsonSchema): string {\n const type = renderToolOutputType(schema);\n if (type === \"string\") return \"a string\";\n const properties =\n isRecord(schema) && isRecord(schema.properties) ? schema.properties : undefined;\n if (properties && mcpStructuredContentSchema(schema) === undefined) {\n const required = new Set(\n isRecord(schema) && Array.isArray(schema.required) ? schema.required : [],\n );\n const fields = Object.keys(properties).map((name) => (required.has(name) ? name : `${name}?`));\n return `\\`{ ${fields.join(\", \")} }\\``;\n }\n return `\\`${type.replace(/\\s+/g, \" \")}\\``;\n}\n\nexport function withScriptCallNote(tool: MikanHarnessTool): MikanHarnessTool {\n return tagHarnessTool({\n ...tool,\n description: `${tool.description.trim()}\\n\\nCodemode: \\`tools.${toCodemodeIdentifier(tool.name)}(args)\\` resolves to ${describeOutput(outputSchemaOf(tool))}.`,\n });\n}\n\nfunction scriptValue(tool: MikanHarnessTool, result: MikanToolResult, output: string): unknown {\n if (tool.outputSchema && result.structuredContent !== undefined) return result.structuredContent;\n const content = result.content ?? [{ type: \"text\" as const, text: output }];\n const text = content\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\\n\");\n if (result.isError) throw new Error(text || `Tool \"${tool.name}\" failed`);\n if (result.structuredContent !== undefined) return result.structuredContent;\n if (content.some((part) => part.type === \"image\")) return { content };\n return text;\n}\n\nexport function createCodemodeTool(options: CodemodeToolOptions): MikanHarnessTool {\n const callable = options.tools.filter(isCodemodeCallable);\n const declarations = callable.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters as CodemodeJsonSchema,\n outputSchema: outputSchemaOf(tool),\n execute: () => undefined,\n }));\n const namespaces = (options.servers ?? []).flatMap((server) => {\n const key = mcpNamespaceKey(server.name);\n const tools = callable\n .filter((tool) => tool.namespace !== undefined && mcpNamespaceKey(tool.namespace) === key)\n .map((tool) => tool.name);\n if (tools.length === 0) return [];\n return [\n {\n key,\n value: {\n name: `mcp__${server.name}`,\n description: server.description,\n instructions: server.instructions,\n tools,\n },\n },\n ];\n });\n const samples = new Map(declarations.map((tool) => [tool.name, renderToolSample(tool)]));\n const globals: CodemodeTool[] = [\n {\n name: \"searchTools\",\n spread: true,\n description: \"Search authorized tools without changing model declarations.\",\n signature:\n \"(query: string, options?: { limit?: number; namespace?: string }): Promise<Array<{ name: string; description: string }>>\",\n execute: (args) => {\n if (!Array.isArray(args) || typeof args[0] !== \"string\")\n throw new Error(\"searchTools requires a string query\");\n const settings: unknown = args[1];\n if (settings !== undefined && !isRecord(settings))\n throw new Error(\"searchTools options must be an object\");\n const limit: unknown = settings?.limit;\n const namespace: unknown = settings?.namespace;\n if (limit !== undefined && typeof limit !== \"number\")\n throw new Error(\"limit must be a number\");\n if (namespace !== undefined && typeof namespace !== \"string\")\n throw new Error(\"namespace must be a string\");\n return searchTools({ tools: callable, query: args[0], limit, namespace }).map((tool) => ({\n name: tool.name,\n description: samples.get(tool.name) ?? tool.description,\n }));\n },\n },\n {\n name: \"describeTool\",\n description: \"Return the full declaration of an authorized tool, or undefined.\",\n signature: \"(name: string): Promise<string | undefined>\",\n execute: (name) => (typeof name === \"string\" ? samples.get(name) : undefined),\n },\n {\n name: \"describeNamespace\",\n description:\n \"Return an MCP server's description, instructions, and tool names, or undefined. Accepts `mcp__<server>` or `<server>`.\",\n signature:\n \"(name: string): Promise<{ name: string; description?: string; instructions?: string; tools: string[] } | undefined>\",\n execute: (name) => {\n if (typeof name !== \"string\") return undefined;\n const key = mcpNamespaceKey(name);\n return namespaces.find((namespace) => namespace.key === key)?.value;\n },\n },\n ];\n return tagHarnessTool({\n name: \"codemode\",\n description: `Run JavaScript code to orchestrate/compose authorized tool calls.\n- Evaluates raw JavaScript (not Markdown code fences) in a fresh QuickJS sandbox as an async function body: top-level await and return work.\n- Nested tools are on the global tools object: await tools.<name>(args). Each takes an object and resolves to text or a structured value; failed calls reject with an Error.\n- Batch independent calls with await Promise.allSettled([...]) and filter results before emitting them. Only script output and its return value reach the model.\n- No Node, filesystem, network, process, modules or timers except through authorized tools. Unawaited calls are cancelled when the script finishes. Tool side effects are real and are not undone if the script fails.\n- start_task, tool_search and recursive codemode are not callable from scripts.\n\nGlobal output helpers:\n- text(value) and console.* append text; image(dataUrlOrImageContent) forwards an image; exit() ends the script successfully.\n- return value emits the final value. store/load values last only for this script, not across calls.\n- ALL_TOOLS lists the authorized nested tools as { name, description } entries.\n\nGlobal discovery helpers (async functions, not methods on tools):\n${renderDeclarations({ globals })}\n\nTools declared to you are also callable from scripts; each one's description says what its call resolves to. Other tools, such as MCP tools, are not listed: find them with searchTools(), inspect them with describeTool() or describeNamespace(), or filter ALL_TOOLS, then call tools[name](args):\n\\`\\`\\`js\nconst matches = await searchTools(query, { namespace });\ntext(await describeTool(matches[0].name));\n\\`\\`\\`\nIf declarations are not known yet, emit the discovered samples first, then write a later script using their parameter and return types. Do not guess argument names. Discovery does not load schemas into the model's direct tool set.\nMCP tools resolve to their complete CallToolResult { content, structuredContent?, isError? }, never truncated: text results are in content[i].text (often JSON to parse), and an MCP error resolves with isError: true instead of rejecting. Filter it in the script and emit only what is needed.`,\n parameters: Type.Object({ label: LABEL_PARAMETER, code: Type.String() }),\n execute: async (params, api, context) => {\n const id = api.callId;\n const { code } = validateToolArguments(\n { name: \"codemode\", description: \"\", parameters: Type.Object({ code: Type.String() }) },\n { type: \"toolCall\", id, name: \"codemode\", arguments: params as JsonObject },\n );\n const source = parseCodemodeSource(code);\n const pending = new Set<Promise<unknown>>();\n const tools: CodemodeTool[] = callable.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters as CodemodeJsonSchema,\n execute: (args, { signal }) => {\n const call = (async () => {\n signal.throwIfAborted();\n const validated: Record<string, unknown> = validateToolArguments(tool, {\n type: \"toolCall\",\n id,\n name: tool.name,\n arguments: args as JsonObject,\n });\n const nested = nestedApi(api, `${id}:${randomUUID()}`);\n const result = await options.executeNested(tool, [\n validated,\n nested.api,\n withAbortSignal(signal, context),\n ]);\n return scriptValue(tool, result, nested.output());\n })();\n pending.add(call);\n void call.then(\n () => pending.delete(call),\n () => pending.delete(call),\n );\n return call;\n },\n }));\n const sandbox = new CodemodeSandbox({\n tools,\n globals,\n timeoutMs: source.options.timeoutMs ?? 60_000,\n memoryLimitBytes: 64 * 1024 * 1024,\n });\n try {\n const result = await sandbox.execute(source.code, { signal: context.abortSignal });\n const output = [...result.output];\n if (result.ok && result.value !== undefined)\n output.push({ type: \"text\", text: JSON.stringify(result.value) });\n if (!result.ok) output.push({ type: \"text\", text: result.error.message });\n let remaining = Math.min(source.options.maxOutputTokens ?? 10_000, 10_000) * 4;\n const content = output.map((part) => {\n if (part.type !== \"text\") return part;\n const text =\n part.text.length > remaining\n ? `${part.text.slice(0, remaining)}\\n[Codemode output truncated; emit less data.]`\n : part.text;\n remaining = Math.max(0, remaining - part.text.length);\n return { type: \"text\" as const, text };\n });\n return { content, details: undefined, isError: !result.ok };\n } finally {\n await sandbox.close();\n await Promise.allSettled(pending);\n }\n },\n });\n}\n\nfunction nestedApi(\n parent: ToolExecutionApi,\n callId: string,\n): { api: ToolExecutionApi; output: () => string } {\n const chunks: string[] = [];\n const decoder = new TextDecoder();\n return {\n api: {\n ...parent,\n callId,\n output: (chunk) => {\n chunks.push(typeof chunk === \"string\" ? chunk : decoder.decode(chunk));\n },\n diagnostic: (diagnostic) => {\n chunks.push(`\\n[${diagnostic.severity}] ${diagnostic.message}`);\n },\n details: async () => undefined,\n },\n output: () => chunks.join(\"\"),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"codemode.js","sourceRoot":"","sources":["../../../src/harness/tools/codemode.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n CodemodeSandbox,\n mcpStructuredContentSchema,\n parseCodemodeSource,\n renderDeclarations,\n renderToolOutputType,\n renderToolSample,\n toCodemodeIdentifier,\n type CodemodeTool,\n type CodemodeJsonSchema,\n} from \"@earendil-works/pi-codemode\";\nimport { validateToolArguments, type JsonObject } from \"@earendil-works/pi-ai\";\nimport { withAbortSignal } from \"@earendil-works/chord/context\";\nimport type { ToolExecutionApi } from \"@earendil-works/pi-durable\";\nimport { Type } from \"typebox\";\nimport type { CodemodeToolOptions, MikanHarnessTool, MikanToolResult } from \"../types.js\";\nimport { LABEL_PARAMETER } from \"./host-fn-tool.js\";\nimport { tagHarnessTool } from \"./pi-tools.js\";\nimport { START_TASK_TOOL } from \"./task.js\";\nimport { mcpNamespaceKey, searchTools, TOOL_SEARCH_TOOL } from \"./tool-search.js\";\nimport { isRecord } from \"../../unknown-values.js\";\n\nconst TEXT_OUTPUT_SCHEMA: CodemodeJsonSchema = { type: \"string\" };\n\nexport function isCodemodeCallable(tool: MikanHarnessTool): boolean {\n return (\n tool.name !== \"codemode\" && tool.name !== START_TASK_TOOL && tool.name !== TOOL_SEARCH_TOOL\n );\n}\n\nfunction outputSchemaOf(tool: MikanHarnessTool): CodemodeJsonSchema {\n return (tool.outputSchema as CodemodeJsonSchema | undefined) ?? TEXT_OUTPUT_SCHEMA;\n}\n\nfunction describeOutput(schema: CodemodeJsonSchema): string {\n const type = renderToolOutputType(schema);\n if (type === \"string\") return \"a string\";\n const properties =\n isRecord(schema) && isRecord(schema.properties) ? schema.properties : undefined;\n if (properties && mcpStructuredContentSchema(schema) === undefined) {\n const required = new Set(\n isRecord(schema) && Array.isArray(schema.required) ? schema.required : [],\n );\n const fields = Object.keys(properties).map((name) => (required.has(name) ? name : `${name}?`));\n return `\\`{ ${fields.join(\", \")} }\\``;\n }\n return `\\`${type.replace(/\\s+/g, \" \")}\\``;\n}\n\nexport function withScriptCallNote(tool: MikanHarnessTool): MikanHarnessTool {\n return tagHarnessTool({\n ...tool,\n description: `${tool.description.trim()}\\n\\nCodemode: \\`tools.${toCodemodeIdentifier(tool.name)}(args)\\` resolves to ${describeOutput(outputSchemaOf(tool))}.`,\n });\n}\n\nfunction scriptValue(tool: MikanHarnessTool, result: MikanToolResult, output: string): unknown {\n if (tool.outputSchema && result.structuredContent !== undefined) return result.structuredContent;\n const content = result.content ?? [{ type: \"text\" as const, text: output }];\n const text = content\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\\n\");\n if (result.isError) throw new Error(text || `Tool \"${tool.name}\" failed`);\n if (result.structuredContent !== undefined) return result.structuredContent;\n if (content.some((part) => part.type === \"image\")) return { content };\n return text;\n}\n\nexport function createCodemodeTool(options: CodemodeToolOptions): MikanHarnessTool {\n const callable = options.tools.filter(isCodemodeCallable);\n const declarations = callable.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters as CodemodeJsonSchema,\n outputSchema: outputSchemaOf(tool),\n execute: () => undefined,\n }));\n const namespaces = (options.servers ?? []).flatMap((server) => {\n const key = mcpNamespaceKey(server.name);\n const tools = callable\n .filter((tool) => tool.namespace !== undefined && mcpNamespaceKey(tool.namespace) === key)\n .map((tool) => tool.name);\n if (tools.length === 0) return [];\n return [\n {\n key,\n value: {\n name: `mcp__${server.name}`,\n description: server.description,\n instructions: server.instructions,\n tools,\n },\n },\n ];\n });\n const samples = new Map(declarations.map((tool) => [tool.name, renderToolSample(tool)]));\n const globals: CodemodeTool[] = [\n {\n name: \"searchTools\",\n spread: true,\n description: \"Search authorized tools without changing model declarations.\",\n signature:\n \"(query: string, options?: { limit?: number; namespace?: string }): Promise<Array<{ name: string; description: string }>>\",\n execute: (args) => {\n if (!Array.isArray(args) || typeof args[0] !== \"string\")\n throw new Error(\"searchTools requires a string query\");\n const settings: unknown = args[1];\n if (settings !== undefined && !isRecord(settings))\n throw new Error(\"searchTools options must be an object\");\n const limit: unknown = settings?.limit;\n const namespace: unknown = settings?.namespace;\n if (limit !== undefined && typeof limit !== \"number\")\n throw new Error(\"limit must be a number\");\n if (namespace !== undefined && typeof namespace !== \"string\")\n throw new Error(\"namespace must be a string\");\n return searchTools({ tools: callable, query: args[0], limit, namespace }).map((tool) => ({\n name: tool.name,\n description: samples.get(tool.name) ?? tool.description,\n }));\n },\n },\n {\n name: \"describeTool\",\n description: \"Return the full declaration of an authorized tool, or undefined.\",\n signature: \"(name: string): Promise<string | undefined>\",\n execute: (name) => (typeof name === \"string\" ? samples.get(name) : undefined),\n },\n {\n name: \"describeNamespace\",\n description:\n \"Return an MCP server's description, instructions, and tool names, or undefined. Accepts `mcp__<server>` or `<server>`.\",\n signature:\n \"(name: string): Promise<{ name: string; description?: string; instructions?: string; tools: string[] } | undefined>\",\n execute: (name) => {\n if (typeof name !== \"string\") return undefined;\n const key = mcpNamespaceKey(name);\n return namespaces.find((namespace) => namespace.key === key)?.value;\n },\n },\n ];\n return tagHarnessTool({\n name: \"codemode\",\n description: `Run JavaScript code to orchestrate/compose authorized tool calls.\n- Evaluates raw JavaScript (not Markdown code fences) in a fresh QuickJS sandbox as an async function body: top-level await and return work.\n- Nested tools are on the global tools object: await tools.<name>(args). Each takes an object and resolves to text or a structured value; failed calls reject with an Error.\n- Batch independent calls with await Promise.allSettled([...]) and filter results before emitting them. Only script output and its return value reach the model.\n- No Node, filesystem, network, process, modules or timers except through authorized tools. Unawaited calls are cancelled when the script finishes. Tool side effects are real and are not undone if the script fails.\n- start_task, tool_search and recursive codemode are not callable from scripts.\n\nGlobal output helpers:\n- text(value) and console.* append text; image(dataUrlOrImageContent) forwards an image; exit() ends the script successfully.\n- return value emits the final value. store/load values last only for this script, not across calls.\n- ALL_TOOLS lists the authorized nested tools as { name, description } entries.\n\nGlobal discovery helpers (async functions, not methods on tools):\n${renderDeclarations({ globals })}\n\nTools declared to you are also callable from scripts; each one's description says what its call resolves to. Other tools, such as MCP tools, are not listed: find them with searchTools(), inspect them with describeTool() or describeNamespace(), or filter ALL_TOOLS, then call tools[name](args).\nDiscover in one script: inspect every namespace and tool the task may need together, then call them in the next script using the declared parameter and return types. Do not guess argument names. Discovery does not load schemas into the model's direct tool set.\n\\`\\`\\`js\nconst matches = await searchTools(query, { namespace });\ntext((await describeNamespace(namespace))?.instructions);\nfor (const match of matches.slice(0, 3)) text(await describeTool(match.name));\n\\`\\`\\`\nMCP tools resolve to their complete CallToolResult { content, structuredContent?, isError? }, never truncated: text results are in content[i].text, usually JSON to parse, and an MCP error resolves with isError: true instead of rejecting. Never emit a whole result: everything you emit stays in the conversation and is sent again on every later model call. Parse it and emit only the fields the answer needs:\n\\`\\`\\`js\nconst result = await tools[name](args);\nif (result.isError) return result.content[0]?.text;\nconst data = JSON.parse(result.content[0].text);\nreturn data.items.map(({ id, title }) => ({ id, title }));\n\\`\\`\\``,\n parameters: Type.Object({ label: LABEL_PARAMETER, code: Type.String() }),\n execute: async (params, api, context) => {\n const id = api.callId;\n const { code } = validateToolArguments(\n { name: \"codemode\", description: \"\", parameters: Type.Object({ code: Type.String() }) },\n { type: \"toolCall\", id, name: \"codemode\", arguments: params as JsonObject },\n );\n const source = parseCodemodeSource(code);\n const pending = new Set<Promise<unknown>>();\n const tools: CodemodeTool[] = callable.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters as CodemodeJsonSchema,\n execute: (args, { signal }) => {\n const call = (async () => {\n signal.throwIfAborted();\n const validated: Record<string, unknown> = validateToolArguments(tool, {\n type: \"toolCall\",\n id,\n name: tool.name,\n arguments: args as JsonObject,\n });\n const nested = nestedApi(api, `${id}:${randomUUID()}`);\n const result = await options.executeNested(tool, [\n validated,\n nested.api,\n withAbortSignal(signal, context),\n ]);\n return scriptValue(tool, result, nested.output());\n })();\n pending.add(call);\n void call.then(\n () => pending.delete(call),\n () => pending.delete(call),\n );\n return call;\n },\n }));\n const sandbox = new CodemodeSandbox({\n tools,\n globals,\n timeoutMs: source.options.timeoutMs ?? 60_000,\n memoryLimitBytes: 64 * 1024 * 1024,\n });\n try {\n const result = await sandbox.execute(source.code, { signal: context.abortSignal });\n const output = [...result.output];\n if (result.ok && result.value !== undefined)\n output.push({ type: \"text\", text: JSON.stringify(result.value) });\n if (!result.ok) output.push({ type: \"text\", text: result.error.message });\n let remaining = Math.min(source.options.maxOutputTokens ?? 10_000, 10_000) * 4;\n const content = output.map((part) => {\n if (part.type !== \"text\") return part;\n const text =\n part.text.length > remaining\n ? `${part.text.slice(0, remaining)}\\n[Codemode output truncated; emit less data.]`\n : part.text;\n remaining = Math.max(0, remaining - part.text.length);\n return { type: \"text\" as const, text };\n });\n return { content, details: undefined, isError: !result.ok };\n } finally {\n await sandbox.close();\n await Promise.allSettled(pending);\n }\n },\n });\n}\n\nfunction nestedApi(\n parent: ToolExecutionApi,\n callId: string,\n): { api: ToolExecutionApi; output: () => string } {\n const chunks: string[] = [];\n const decoder = new TextDecoder();\n return {\n api: {\n ...parent,\n callId,\n output: (chunk) => {\n chunks.push(typeof chunk === \"string\" ? chunk : decoder.decode(chunk));\n },\n diagnostic: (diagnostic) => {\n chunks.push(`\\n[${diagnostic.severity}] ${diagnostic.message}`);\n },\n details: async () => undefined,\n },\n output: () => chunks.join(\"\"),\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-search.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/tool-search.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-search.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/tool-search.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAK9F,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAqB9C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AA6CD,wBAAgB,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,EAAE,CA4C1E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,gBAAgB,CAyCrF"}
|
|
@@ -2,7 +2,9 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { validateToolArguments } from "@earendil-works/pi-ai";
|
|
3
3
|
import { LABEL_PARAMETER } from "./host-fn-tool.js";
|
|
4
4
|
import { tagHarnessTool } from "./pi-tools.js";
|
|
5
|
+
import { isRecord } from "../../unknown-values.js";
|
|
5
6
|
export const TOOL_SEARCH_TOOL = "tool_search";
|
|
7
|
+
const DEFAULT_SEARCH_LIMIT = 8;
|
|
6
8
|
const searchSchema = Type.Object({
|
|
7
9
|
label: LABEL_PARAMETER,
|
|
8
10
|
query: Type.String({
|
|
@@ -13,39 +15,94 @@ const searchSchema = Type.Object({
|
|
|
13
15
|
minimum: 1,
|
|
14
16
|
maximum: 20,
|
|
15
17
|
multipleOf: 1,
|
|
16
|
-
description:
|
|
18
|
+
description: `Maximum number of matches; default ${DEFAULT_SEARCH_LIMIT}.`,
|
|
17
19
|
})),
|
|
18
20
|
namespace: Type.Optional(Type.String({ description: "Restrict matches to an MCP server name." })),
|
|
19
21
|
});
|
|
20
22
|
export function mcpNamespaceKey(name) {
|
|
21
23
|
return name.replace(/^mcp__/, "").replace(/-/g, "_");
|
|
22
24
|
}
|
|
25
|
+
const STOP_WORDS = new Set("a an and are as at be by for from in is it of on or that the this to with".split(" "));
|
|
26
|
+
function stem(term) {
|
|
27
|
+
if (term.length > 4 && term.endsWith("ies"))
|
|
28
|
+
return `${term.slice(0, -3)}y`;
|
|
29
|
+
if (term.length > 4 && /(ches|shes|sses|xes|zes)$/.test(term))
|
|
30
|
+
return term.slice(0, -2);
|
|
31
|
+
if (term.length > 3 && term.endsWith("s") && !term.endsWith("ss"))
|
|
32
|
+
return term.slice(0, -1);
|
|
33
|
+
return term;
|
|
34
|
+
}
|
|
35
|
+
function tokenize(text) {
|
|
36
|
+
return text
|
|
37
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
38
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
|
|
39
|
+
.toLowerCase()
|
|
40
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
41
|
+
.filter((term) => term.length > 0 && !STOP_WORDS.has(term))
|
|
42
|
+
.map(stem);
|
|
43
|
+
}
|
|
44
|
+
function schemaText(schema, parts) {
|
|
45
|
+
if (!isRecord(schema))
|
|
46
|
+
return;
|
|
47
|
+
if (typeof schema.description === "string")
|
|
48
|
+
parts.push(schema.description);
|
|
49
|
+
if (isRecord(schema.properties)) {
|
|
50
|
+
for (const [name, property] of Object.entries(schema.properties)) {
|
|
51
|
+
parts.push(name);
|
|
52
|
+
schemaText(property, parts);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
schemaText(schema.items, parts);
|
|
56
|
+
}
|
|
57
|
+
function searchDocument(tool) {
|
|
58
|
+
const parts = [tool.name, tool.name.replaceAll("_", " "), tool.description];
|
|
59
|
+
schemaText(tool.parameters, parts);
|
|
60
|
+
if (tool.namespace)
|
|
61
|
+
parts.push(tool.namespace);
|
|
62
|
+
return tokenize(parts.join(" "));
|
|
63
|
+
}
|
|
64
|
+
const BM25_K1 = 1.2;
|
|
65
|
+
const BM25_B = 0.75;
|
|
23
66
|
export function searchTools(options) {
|
|
24
|
-
const query = options.query.trim()
|
|
67
|
+
const query = options.query.trim();
|
|
25
68
|
if (!query)
|
|
26
69
|
throw new Error("query must not be empty");
|
|
27
|
-
const limit = options.limit ??
|
|
70
|
+
const limit = options.limit ?? DEFAULT_SEARCH_LIMIT;
|
|
28
71
|
if (!Number.isInteger(limit) || limit < 1 || limit > 20)
|
|
29
72
|
throw new Error("limit must be an integer from 1 to 20");
|
|
30
|
-
const
|
|
31
|
-
return options.tools
|
|
32
|
-
.filter((tool) => options.namespace === undefined ||
|
|
73
|
+
const tools = options.tools.filter((tool) => options.namespace === undefined ||
|
|
33
74
|
(tool.namespace !== undefined &&
|
|
34
|
-
mcpNamespaceKey(tool.namespace) === mcpNamespaceKey(options.namespace)))
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
(
|
|
43
|
-
return
|
|
75
|
+
mcpNamespaceKey(tool.namespace) === mcpNamespaceKey(options.namespace)));
|
|
76
|
+
const exact = tools.find((tool) => tool.name === query);
|
|
77
|
+
const terms = [...new Set(tokenize(query))];
|
|
78
|
+
if (terms.length === 0 || tools.length === 0)
|
|
79
|
+
return exact ? [exact] : [];
|
|
80
|
+
const counts = tools.map((tool) => {
|
|
81
|
+
const termCounts = new Map();
|
|
82
|
+
for (const term of searchDocument(tool))
|
|
83
|
+
termCounts.set(term, (termCounts.get(term) ?? 0) + 1);
|
|
84
|
+
return termCounts;
|
|
85
|
+
});
|
|
86
|
+
const lengths = counts.map((termCounts) => [...termCounts.values()].reduce((sum, count) => sum + count, 0));
|
|
87
|
+
const averageLength = lengths.reduce((sum, length) => sum + length, 0) / tools.length || 1;
|
|
88
|
+
const idf = new Map(terms.map((term) => {
|
|
89
|
+
const frequency = counts.filter((termCounts) => termCounts.has(term)).length;
|
|
90
|
+
return [term, Math.log(1 + (tools.length - frequency + 0.5) / (frequency + 0.5))];
|
|
91
|
+
}));
|
|
92
|
+
const ranked = tools
|
|
93
|
+
.map((tool, index) => {
|
|
94
|
+
const norm = BM25_K1 * (1 - BM25_B + (BM25_B * lengths[index]) / averageLength);
|
|
95
|
+
const score = terms.reduce((sum, term) => {
|
|
96
|
+
const count = counts[index].get(term);
|
|
97
|
+
return count
|
|
98
|
+
? sum + (idf.get(term) ?? 0) * ((count * (BM25_K1 + 1)) / (count + norm))
|
|
99
|
+
: sum;
|
|
100
|
+
}, 0);
|
|
101
|
+
return { tool, score: tool === exact ? Number.POSITIVE_INFINITY : score };
|
|
44
102
|
})
|
|
45
103
|
.filter((match) => match.score > 0)
|
|
46
|
-
.toSorted((a, b) => b.score - a.score)
|
|
47
|
-
|
|
48
|
-
.map(({ tool }) => tool);
|
|
104
|
+
.toSorted((a, b) => b.score - a.score);
|
|
105
|
+
return ranked.slice(0, limit).map(({ tool }) => tool);
|
|
49
106
|
}
|
|
50
107
|
export function createToolSearchTool(options) {
|
|
51
108
|
const namespaces = [...new Set(options.tools.map((tool) => tool.namespace).filter(Boolean))];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-search.js","sourceRoot":"","sources":["../../../src/harness/tools/tool-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { Type, type Static } from \"typebox\";\nimport { validateToolArguments, type JsonObject } from \"@earendil-works/pi-ai\";\nimport type { MikanHarnessTool, ToolSearchOptions, ToolSearchToolOptions } from \"../types.js\";\nimport { LABEL_PARAMETER } from \"./host-fn-tool.js\";\nimport { tagHarnessTool } from \"./pi-tools.js\";\n\nexport const TOOL_SEARCH_TOOL = \"tool_search\";\n\nconst searchSchema = Type.Object({\n label: LABEL_PARAMETER,\n query: Type.String({\n minLength: 1,\n description:\n \"Keywords describing the tool or task. Searches tool names, descriptions and argument schemas, not web content.\",\n }),\n limit: Type.Optional(\n Type.Number({\n minimum: 1,\n maximum: 20,\n multipleOf: 1,\n description: \"Maximum number of matches; default 5.\",\n }),\n ),\n namespace: Type.Optional(Type.String({ description: \"Restrict matches to an MCP server name.\" })),\n});\n\nexport function mcpNamespaceKey(name: string): string {\n return name.replace(/^mcp__/, \"\").replace(/-/g, \"_\");\n}\n\nexport function searchTools(options: ToolSearchOptions): MikanHarnessTool[] {\n const query = options.query.trim().toLowerCase();\n if (!query) throw new Error(\"query must not be empty\");\n const limit = options.limit ?? 5;\n if (!Number.isInteger(limit) || limit < 1 || limit > 20)\n throw new Error(\"limit must be an integer from 1 to 20\");\n const terms = [...new Set(query.match(/[\\p{L}\\p{N}]+/gu) ?? [])];\n return options.tools\n .filter(\n (tool) =>\n options.namespace === undefined ||\n (tool.namespace !== undefined &&\n mcpNamespaceKey(tool.namespace) === mcpNamespaceKey(options.namespace)),\n )\n .map((tool) => {\n const name = tool.name.toLowerCase();\n const description = tool.description.toLowerCase();\n const schema = JSON.stringify(tool.parameters).toLowerCase();\n const score = terms.reduce(\n (sum, term) =>\n sum +\n (name.includes(term) ? 3 : 0) +\n (description.includes(term) ? 1 : 0) +\n (schema.includes(term) ? 0.25 : 0),\n 0,\n );\n return { tool, score: score + (name === query ? 100 : 0) };\n })\n .filter((match) => match.score > 0)\n .toSorted((a, b) => b.score - a.score)\n .slice(0, limit)\n .map(({ tool }) => tool);\n}\n\nexport function createToolSearchTool(options: ToolSearchToolOptions): MikanHarnessTool {\n const namespaces = [...new Set(options.tools.map((tool) => tool.namespace).filter(Boolean))];\n return tagHarnessTool({\n name: TOOL_SEARCH_TOOL,\n description: `Search authorized MCP tools that are not declared yet, then load matching schemas for the next model call. Use descriptive keywords or an exact tool name. Already loaded tools stay available. This searches tool metadata, not web pages or repository content. Available MCP namespaces: ${namespaces.join(\", \") || \"none\"}.`,\n parameters: searchSchema,\n execute: async (params, api, context) => {\n const id = api.callId;\n context.abortSignal?.throwIfAborted();\n const args: Static<typeof searchSchema> = validateToolArguments(\n { name: TOOL_SEARCH_TOOL, description: \"\", parameters: searchSchema },\n {\n type: \"toolCall\",\n id,\n name: TOOL_SEARCH_TOOL,\n arguments: params as JsonObject,\n },\n );\n const matches = searchTools({\n tools: options.tools.filter((tool) => !options.loaded.has(tool.name)),\n query: args.query,\n limit: args.limit,\n namespace: args.namespace,\n });\n const loaded = matches.map((tool) => tool.name);\n if (loaded.length > 0) await options.load(loaded, context);\n return {\n content: [\n {\n type: \"text\",\n text:\n loaded.length === 0\n ? \"No matching unloaded tools found. Already loaded tools remain available.\"\n : `Loaded ${loaded.length} tools for the next model call:\\n${matches.map((tool) => `- ${tool.name}: ${tool.description.split(/\\r?\\n/)[0]?.slice(0, 300)}`).join(\"\\n\")}`,\n },\n ],\n details: { loaded },\n control: loaded.length > 0 ? { addTools: loaded } : undefined,\n };\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool-search.js","sourceRoot":"","sources":["../../../src/harness/tools/tool-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { Type, type Static } from \"typebox\";\nimport { validateToolArguments, type JsonObject } from \"@earendil-works/pi-ai\";\nimport type { MikanHarnessTool, ToolSearchOptions, ToolSearchToolOptions } from \"../types.js\";\nimport { LABEL_PARAMETER } from \"./host-fn-tool.js\";\nimport { tagHarnessTool } from \"./pi-tools.js\";\nimport { isRecord } from \"../../unknown-values.js\";\n\nexport const TOOL_SEARCH_TOOL = \"tool_search\";\nconst DEFAULT_SEARCH_LIMIT = 8;\n\nconst searchSchema = Type.Object({\n label: LABEL_PARAMETER,\n query: Type.String({\n minLength: 1,\n description:\n \"Keywords describing the tool or task. Searches tool names, descriptions and argument schemas, not web content.\",\n }),\n limit: Type.Optional(\n Type.Number({\n minimum: 1,\n maximum: 20,\n multipleOf: 1,\n description: `Maximum number of matches; default ${DEFAULT_SEARCH_LIMIT}.`,\n }),\n ),\n namespace: Type.Optional(Type.String({ description: \"Restrict matches to an MCP server name.\" })),\n});\n\nexport function mcpNamespaceKey(name: string): string {\n return name.replace(/^mcp__/, \"\").replace(/-/g, \"_\");\n}\n\nconst STOP_WORDS: ReadonlySet<string> = new Set(\n \"a an and are as at be by for from in is it of on or that the this to with\".split(\" \"),\n);\n\nfunction stem(term: string): string {\n if (term.length > 4 && term.endsWith(\"ies\")) return `${term.slice(0, -3)}y`;\n if (term.length > 4 && /(ches|shes|sses|xes|zes)$/.test(term)) return term.slice(0, -2);\n if (term.length > 3 && term.endsWith(\"s\") && !term.endsWith(\"ss\")) return term.slice(0, -1);\n return term;\n}\n\nfunction tokenize(text: string): string[] {\n return text\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1 $2\")\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter((term) => term.length > 0 && !STOP_WORDS.has(term))\n .map(stem);\n}\n\nfunction schemaText(schema: unknown, parts: string[]): void {\n if (!isRecord(schema)) return;\n if (typeof schema.description === \"string\") parts.push(schema.description);\n if (isRecord(schema.properties)) {\n for (const [name, property] of Object.entries(schema.properties)) {\n parts.push(name);\n schemaText(property, parts);\n }\n }\n schemaText(schema.items, parts);\n}\n\nfunction searchDocument(tool: MikanHarnessTool): string[] {\n const parts = [tool.name, tool.name.replaceAll(\"_\", \" \"), tool.description];\n schemaText(tool.parameters, parts);\n if (tool.namespace) parts.push(tool.namespace);\n return tokenize(parts.join(\" \"));\n}\n\nconst BM25_K1 = 1.2;\nconst BM25_B = 0.75;\n\nexport function searchTools(options: ToolSearchOptions): MikanHarnessTool[] {\n const query = options.query.trim();\n if (!query) throw new Error(\"query must not be empty\");\n const limit = options.limit ?? DEFAULT_SEARCH_LIMIT;\n if (!Number.isInteger(limit) || limit < 1 || limit > 20)\n throw new Error(\"limit must be an integer from 1 to 20\");\n const tools = options.tools.filter(\n (tool) =>\n options.namespace === undefined ||\n (tool.namespace !== undefined &&\n mcpNamespaceKey(tool.namespace) === mcpNamespaceKey(options.namespace)),\n );\n const exact = tools.find((tool) => tool.name === query);\n const terms = [...new Set(tokenize(query))];\n if (terms.length === 0 || tools.length === 0) return exact ? [exact] : [];\n const counts = tools.map((tool) => {\n const termCounts = new Map<string, number>();\n for (const term of searchDocument(tool)) termCounts.set(term, (termCounts.get(term) ?? 0) + 1);\n return termCounts;\n });\n const lengths = counts.map((termCounts) =>\n [...termCounts.values()].reduce((sum, count) => sum + count, 0),\n );\n const averageLength = lengths.reduce((sum, length) => sum + length, 0) / tools.length || 1;\n const idf = new Map(\n terms.map((term) => {\n const frequency = counts.filter((termCounts) => termCounts.has(term)).length;\n return [term, Math.log(1 + (tools.length - frequency + 0.5) / (frequency + 0.5))] as const;\n }),\n );\n const ranked = tools\n .map((tool, index) => {\n const norm = BM25_K1 * (1 - BM25_B + (BM25_B * lengths[index]!) / averageLength);\n const score = terms.reduce((sum, term) => {\n const count = counts[index]!.get(term);\n return count\n ? sum + (idf.get(term) ?? 0) * ((count * (BM25_K1 + 1)) / (count + norm))\n : sum;\n }, 0);\n return { tool, score: tool === exact ? Number.POSITIVE_INFINITY : score };\n })\n .filter((match) => match.score > 0)\n .toSorted((a, b) => b.score - a.score);\n return ranked.slice(0, limit).map(({ tool }) => tool);\n}\n\nexport function createToolSearchTool(options: ToolSearchToolOptions): MikanHarnessTool {\n const namespaces = [...new Set(options.tools.map((tool) => tool.namespace).filter(Boolean))];\n return tagHarnessTool({\n name: TOOL_SEARCH_TOOL,\n description: `Search authorized MCP tools that are not declared yet, then load matching schemas for the next model call. Use descriptive keywords or an exact tool name. Already loaded tools stay available. This searches tool metadata, not web pages or repository content. Available MCP namespaces: ${namespaces.join(\", \") || \"none\"}.`,\n parameters: searchSchema,\n execute: async (params, api, context) => {\n const id = api.callId;\n context.abortSignal?.throwIfAborted();\n const args: Static<typeof searchSchema> = validateToolArguments(\n { name: TOOL_SEARCH_TOOL, description: \"\", parameters: searchSchema },\n {\n type: \"toolCall\",\n id,\n name: TOOL_SEARCH_TOOL,\n arguments: params as JsonObject,\n },\n );\n const matches = searchTools({\n tools: options.tools.filter((tool) => !options.loaded.has(tool.name)),\n query: args.query,\n limit: args.limit,\n namespace: args.namespace,\n });\n const loaded = matches.map((tool) => tool.name);\n if (loaded.length > 0) await options.load(loaded, context);\n return {\n content: [\n {\n type: \"text\",\n text:\n loaded.length === 0\n ? \"No matching unloaded tools found. Already loaded tools remain available.\"\n : `Loaded ${loaded.length} tools for the next model call:\\n${matches.map((tool) => `- ${tool.name}: ${tool.description.split(/\\r?\\n/)[0]?.slice(0, 300)}`).join(\"\\n\")}`,\n },\n ],\n details: { loaded },\n control: loaded.length > 0 ? { addTools: loaded } : undefined,\n };\n },\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geminixiang/mikan",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.92",
|
|
4
4
|
"description": "Self-hosted AI coding agent for organizations: every Slack channel and DM gets its own sandboxed workspace, credentials, and memory.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|