@mcoda/codali 0.1.108 → 0.1.109
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/dist/agents/AgentInventory.d.ts +56 -0
- package/dist/agents/AgentInventory.d.ts.map +1 -0
- package/dist/agents/AgentInventory.js +131 -0
- package/dist/agents/RoleResolution.d.ts +49 -0
- package/dist/agents/RoleResolution.d.ts.map +1 -0
- package/dist/agents/RoleResolution.js +166 -0
- package/dist/api/ChatCompletionsAdapter.d.ts +76 -0
- package/dist/api/ChatCompletionsAdapter.d.ts.map +1 -0
- package/dist/api/ChatCompletionsAdapter.js +54 -0
- package/dist/api/CodaliApi.d.ts +78 -0
- package/dist/api/CodaliApi.d.ts.map +1 -0
- package/dist/api/CodaliApi.js +273 -0
- package/dist/api/CodaliServer.d.ts +42 -0
- package/dist/api/CodaliServer.d.ts.map +1 -0
- package/dist/api/CodaliServer.js +118 -0
- package/dist/cli/AskCommand.d.ts +66 -0
- package/dist/cli/AskCommand.d.ts.map +1 -0
- package/dist/cli/AskCommand.js +536 -0
- package/dist/cli/AuthCommand.d.ts +19 -0
- package/dist/cli/AuthCommand.d.ts.map +1 -0
- package/dist/cli/AuthCommand.js +161 -0
- package/dist/cli/ServeCommand.d.ts +5 -0
- package/dist/cli/ServeCommand.d.ts.map +1 -0
- package/dist/cli/ServeCommand.js +87 -0
- package/dist/cli/ToolsCommand.d.ts +22 -0
- package/dist/cli/ToolsCommand.d.ts.map +1 -0
- package/dist/cli/ToolsCommand.js +208 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -1
- package/dist/connectors/http/HttpToolDefinition.d.ts +74 -0
- package/dist/connectors/http/HttpToolDefinition.d.ts.map +1 -0
- package/dist/connectors/http/HttpToolDefinition.js +190 -0
- package/dist/connectors/http/HttpToolSource.d.ts +18 -0
- package/dist/connectors/http/HttpToolSource.d.ts.map +1 -0
- package/dist/connectors/http/HttpToolSource.js +29 -0
- package/dist/connectors/mcp/McpClient.d.ts +132 -0
- package/dist/connectors/mcp/McpClient.d.ts.map +1 -0
- package/dist/connectors/mcp/McpClient.js +215 -0
- package/dist/connectors/mcp/McpServerRegistry.d.ts +75 -0
- package/dist/connectors/mcp/McpServerRegistry.d.ts.map +1 -0
- package/dist/connectors/mcp/McpServerRegistry.js +180 -0
- package/dist/connectors/mcp/McpToolAdapter.d.ts +48 -0
- package/dist/connectors/mcp/McpToolAdapter.d.ts.map +1 -0
- package/dist/connectors/mcp/McpToolAdapter.js +101 -0
- package/dist/connectors/mcp/McpToolSource.d.ts +27 -0
- package/dist/connectors/mcp/McpToolSource.d.ts.map +1 -0
- package/dist/connectors/mcp/McpToolSource.js +33 -0
- package/dist/connectors/media/ImageGenerationTool.d.ts +43 -0
- package/dist/connectors/media/ImageGenerationTool.d.ts.map +1 -0
- package/dist/connectors/media/ImageGenerationTool.js +122 -0
- package/dist/connectors/oauth/DeviceCodeAuth.d.ts +74 -0
- package/dist/connectors/oauth/DeviceCodeAuth.d.ts.map +1 -0
- package/dist/connectors/oauth/DeviceCodeAuth.js +153 -0
- package/dist/docdex/DocdexClient.d.ts +2 -0
- package/dist/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/docdex/DocdexClient.js +4 -0
- package/dist/gateway/CodaliGateway.d.ts +51 -3
- package/dist/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/gateway/CodaliGateway.js +226 -2
- package/dist/gateway/CodaliGatewayTypes.d.ts +34 -0
- package/dist/gateway/CodaliGatewayTypes.d.ts.map +1 -1
- package/dist/gateway/EvidenceNormalizer.d.ts.map +1 -1
- package/dist/gateway/EvidenceNormalizer.js +90 -1
- package/dist/gateway/Finalizer.d.ts +50 -0
- package/dist/gateway/Finalizer.d.ts.map +1 -0
- package/dist/gateway/Finalizer.js +79 -0
- package/dist/gateway/GatewayPlanner.d.ts +13 -0
- package/dist/gateway/GatewayPlanner.d.ts.map +1 -1
- package/dist/gateway/GatewayPlanner.js +81 -16
- package/dist/gateway/GatewayStateMachine.d.ts.map +1 -1
- package/dist/gateway/GatewayStateMachine.js +6 -0
- package/dist/gateway/GatewayTracer.d.ts +93 -0
- package/dist/gateway/GatewayTracer.d.ts.map +1 -0
- package/dist/gateway/GatewayTracer.js +214 -0
- package/dist/gateway/LocalGatewayProvider.d.ts +46 -0
- package/dist/gateway/LocalGatewayProvider.d.ts.map +1 -0
- package/dist/gateway/LocalGatewayProvider.js +161 -0
- package/dist/gateway/LocalGatewayTaskRunner.d.ts +79 -0
- package/dist/gateway/LocalGatewayTaskRunner.d.ts.map +1 -0
- package/dist/gateway/LocalGatewayTaskRunner.js +354 -0
- package/dist/gateway/RelevanceJudge.d.ts +14 -0
- package/dist/gateway/RelevanceJudge.d.ts.map +1 -0
- package/dist/gateway/RelevanceJudge.js +87 -0
- package/dist/gateway/ResponseSchema.d.ts +32 -0
- package/dist/gateway/ResponseSchema.d.ts.map +1 -0
- package/dist/gateway/ResponseSchema.js +120 -0
- package/dist/gateway/TemporalContext.d.ts +36 -0
- package/dist/gateway/TemporalContext.d.ts.map +1 -0
- package/dist/gateway/TemporalContext.js +115 -0
- package/dist/gateway/ToolCapabilityCompiler.d.ts +5 -0
- package/dist/gateway/ToolCapabilityCompiler.d.ts.map +1 -1
- package/dist/gateway/ToolCapabilityCompiler.js +27 -2
- package/dist/gateway/ToolDescriptorSource.d.ts +6 -0
- package/dist/gateway/ToolDescriptorSource.d.ts.map +1 -0
- package/dist/gateway/ToolDescriptorSource.js +30 -0
- package/dist/gateway/ToolExposure.d.ts +36 -0
- package/dist/gateway/ToolExposure.d.ts.map +1 -0
- package/dist/gateway/ToolExposure.js +154 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -0
- package/dist/providers/ClaudeCliProvider.d.ts +2 -0
- package/dist/providers/ClaudeCliProvider.d.ts.map +1 -1
- package/dist/providers/ClaudeCliProvider.js +2 -0
- package/dist/providers/CodexCliProvider.d.ts +2 -0
- package/dist/providers/CodexCliProvider.d.ts.map +1 -1
- package/dist/providers/CodexCliProvider.js +2 -0
- package/dist/providers/MswarmWorkerProvider.d.ts +2 -0
- package/dist/providers/MswarmWorkerProvider.d.ts.map +1 -1
- package/dist/providers/MswarmWorkerProvider.js +2 -0
- package/dist/providers/OllamaRemoteProvider.d.ts +2 -0
- package/dist/providers/OllamaRemoteProvider.d.ts.map +1 -1
- package/dist/providers/OllamaRemoteProvider.js +2 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts +2 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/providers/OpenAiCompatibleProvider.js +2 -0
- package/dist/providers/ProviderTypes.d.ts +13 -0
- package/dist/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/providers/ProviderTypes.js +1 -1
- package/dist/runcontext/CredentialFile.d.ts +12 -0
- package/dist/runcontext/CredentialFile.d.ts.map +1 -0
- package/dist/runcontext/CredentialFile.js +81 -0
- package/dist/runcontext/RunContextResolver.d.ts +126 -0
- package/dist/runcontext/RunContextResolver.d.ts.map +1 -0
- package/dist/runcontext/RunContextResolver.js +389 -0
- package/dist/storage/CodaliDatasetPrivacyEngine.d.ts +1 -1
- package/dist/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -1
- package/dist/storage/CodaliDatasetPrivacyEngine.js +12 -0
- package/dist/tools/ToolRegistry.d.ts +14 -1
- package/dist/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/tools/ToolRegistry.js +38 -1
- package/dist/tools/ToolTypes.d.ts +35 -0
- package/dist/tools/ToolTypes.d.ts.map +1 -1
- package/dist/tools/ToolTypes.js +21 -0
- package/dist/tools/TruncateResult.d.ts +29 -0
- package/dist/tools/TruncateResult.d.ts.map +1 -0
- package/dist/tools/TruncateResult.js +91 -0
- package/dist/tools/diff/DiffTool.d.ts.map +1 -1
- package/dist/tools/diff/DiffTool.js +2 -0
- package/dist/tools/docdex/DocdexTools.d.ts +21 -1
- package/dist/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/tools/docdex/DocdexTools.js +108 -16
- package/dist/tools/filesystem/FileTools.d.ts.map +1 -1
- package/dist/tools/filesystem/FileTools.js +8 -0
- package/dist/tools/search/SearchTool.d.ts.map +1 -1
- package/dist/tools/search/SearchTool.js +3 -1
- package/dist/tools/shell/ShellTool.d.ts.map +1 -1
- package/dist/tools/shell/ShellTool.js +2 -0
- package/package.json +4 -3
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decides whether local search results actually answer the question.
|
|
3
|
+
*
|
|
4
|
+
* The alternative approaches are both worse:
|
|
5
|
+
*
|
|
6
|
+
* - **Score thresholds** do not discriminate. Measured against this repository,
|
|
7
|
+
* "GDP of France 2025" scores 29.07 while "gateway planner class" scores
|
|
8
|
+
* 14.35 — the irrelevant query scores higher, because a test fixture contains
|
|
9
|
+
* the phrase. Any threshold would be arbitrary and frequently wrong.
|
|
10
|
+
* - **Keyword rules** ("if the query mentions GDP or news, go to the web") are
|
|
11
|
+
* precisely the hardcoding this project rules out: they work for the examples
|
|
12
|
+
* someone thought of and fail on the next question.
|
|
13
|
+
*
|
|
14
|
+
* A model reading the titles is generic and costs one short call. It is asked
|
|
15
|
+
* for a single token so a small local model can answer it quickly.
|
|
16
|
+
*/
|
|
17
|
+
const MAX_TITLES = 8;
|
|
18
|
+
const MAX_TITLE_CHARS = 90;
|
|
19
|
+
const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
20
|
+
/**
|
|
21
|
+
* Reduces a result payload to a short list of titles. The judge needs to know
|
|
22
|
+
* *what* was found, not the contents — sending snippets would cost more than
|
|
23
|
+
* the web call being avoided.
|
|
24
|
+
*/
|
|
25
|
+
export const summarizeResultTitles = (results) => {
|
|
26
|
+
if (!isRecord(results))
|
|
27
|
+
return [];
|
|
28
|
+
const hits = ["hits", "results", "matches"]
|
|
29
|
+
.map((key) => results[key])
|
|
30
|
+
.find((value) => Array.isArray(value) && value.length > 0);
|
|
31
|
+
if (!hits)
|
|
32
|
+
return [];
|
|
33
|
+
const titles = [];
|
|
34
|
+
for (const hit of hits.slice(0, MAX_TITLES)) {
|
|
35
|
+
if (!isRecord(hit))
|
|
36
|
+
continue;
|
|
37
|
+
const title = (typeof hit.rel_path === "string" && hit.rel_path) ||
|
|
38
|
+
(typeof hit.relPath === "string" && hit.relPath) ||
|
|
39
|
+
(typeof hit.path === "string" && hit.path) ||
|
|
40
|
+
(typeof hit.title === "string" && hit.title) ||
|
|
41
|
+
(typeof hit.url === "string" && hit.url) ||
|
|
42
|
+
(typeof hit.doc_id === "string" && hit.doc_id) ||
|
|
43
|
+
"";
|
|
44
|
+
if (title)
|
|
45
|
+
titles.push(String(title).slice(0, MAX_TITLE_CHARS));
|
|
46
|
+
}
|
|
47
|
+
return titles;
|
|
48
|
+
};
|
|
49
|
+
export const createRelevanceJudge = (options) => async ({ query, results }) => {
|
|
50
|
+
const titles = summarizeResultTitles(results);
|
|
51
|
+
// Nothing to judge; the caller's presence check already decided.
|
|
52
|
+
if (titles.length === 0)
|
|
53
|
+
return true;
|
|
54
|
+
const response = await options.provider.generate({
|
|
55
|
+
messages: [
|
|
56
|
+
{
|
|
57
|
+
role: "system",
|
|
58
|
+
content: [
|
|
59
|
+
"You decide whether local search results are relevant to a question.",
|
|
60
|
+
"You are shown only the titles or file paths that were found.",
|
|
61
|
+
"Answer with exactly one word: YES if these look like they could answer the question, NO if they are about something else.",
|
|
62
|
+
"A repository file that merely happens to mention a word from the question is NO.",
|
|
63
|
+
].join("\n"),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
role: "user",
|
|
67
|
+
content: [
|
|
68
|
+
`Question: ${query}`,
|
|
69
|
+
"",
|
|
70
|
+
"Results found locally:",
|
|
71
|
+
...titles.map((title) => `- ${title}`),
|
|
72
|
+
"",
|
|
73
|
+
"Could these answer the question? Reply YES or NO.",
|
|
74
|
+
].join("\n"),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
toolChoice: "none",
|
|
78
|
+
maxTokens: options.maxTokens ?? 5,
|
|
79
|
+
temperature: 0,
|
|
80
|
+
});
|
|
81
|
+
const verdict = response.message.content.trim().toUpperCase();
|
|
82
|
+
// Default to keeping the local result when the answer is unreadable: a
|
|
83
|
+
// needless web call is a worse failure than a slightly weak local hit.
|
|
84
|
+
if (verdict.startsWith("NO"))
|
|
85
|
+
return false;
|
|
86
|
+
return true;
|
|
87
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enforcement for `CodaliGatewayRequest.response.schema`.
|
|
3
|
+
*
|
|
4
|
+
* The field has been part of the request contract for a while but was never
|
|
5
|
+
* validated — only `response.format === "json"` was read, so a caller could ask
|
|
6
|
+
* for a schema and receive anything at all. That pushes parsing and correction
|
|
7
|
+
* logic into every consuming product, which is precisely the duplication Codali
|
|
8
|
+
* exists to remove.
|
|
9
|
+
*
|
|
10
|
+
* Validation reuses the same structural rules as tool-argument validation so a
|
|
11
|
+
* schema behaves identically whether it describes a tool input or a response.
|
|
12
|
+
*/
|
|
13
|
+
export interface ResponseSchemaViolation {
|
|
14
|
+
path: string;
|
|
15
|
+
message: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ResponseSchemaValidation {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
value?: unknown;
|
|
20
|
+
violations: ResponseSchemaViolation[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Extracts a JSON value from a model response. Models routinely wrap JSON in
|
|
24
|
+
* prose or a fenced block even when told not to, and rejecting those outright
|
|
25
|
+
* would burn a repair attempt on a formatting quirk rather than a real
|
|
26
|
+
* structural failure.
|
|
27
|
+
*/
|
|
28
|
+
export declare const extractJsonPayload: (content: string) => unknown;
|
|
29
|
+
export declare const validateResponseAgainstSchema: (content: string, schema: Record<string, unknown> | undefined) => ResponseSchemaValidation;
|
|
30
|
+
/** Human-readable violation list, used to prompt one repair attempt. */
|
|
31
|
+
export declare const describeViolations: (violations: ResponseSchemaViolation[]) => string;
|
|
32
|
+
//# sourceMappingURL=ResponseSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResponseSchema.d.ts","sourceRoot":"","sources":["../../src/gateway/ResponseSchema.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,uBAAuB,EAAE,CAAC;CACvC;AAwED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,OAuBpD,CAAC;AAUF,eAAO,MAAM,6BAA6B,GACxC,SAAS,MAAM,EACf,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KAC1C,wBAmBF,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,kBAAkB,GAAI,YAAY,uBAAuB,EAAE,KAAG,MACY,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2
|
+
const typeOf = (value) => {
|
|
3
|
+
if (value === null)
|
|
4
|
+
return "null";
|
|
5
|
+
if (Array.isArray(value))
|
|
6
|
+
return "array";
|
|
7
|
+
if (typeof value === "number")
|
|
8
|
+
return Number.isInteger(value) ? "integer" : "number";
|
|
9
|
+
return typeof value;
|
|
10
|
+
};
|
|
11
|
+
const typeMatches = (value, expected) => {
|
|
12
|
+
const actual = typeOf(value);
|
|
13
|
+
if (expected === actual)
|
|
14
|
+
return true;
|
|
15
|
+
// An integer satisfies "number"; a whole float satisfies "integer".
|
|
16
|
+
if (expected === "number" && actual === "integer")
|
|
17
|
+
return true;
|
|
18
|
+
if (expected === "integer" && actual === "number")
|
|
19
|
+
return Number.isInteger(value);
|
|
20
|
+
return false;
|
|
21
|
+
};
|
|
22
|
+
const validateAgainst = (value, schema, path, violations) => {
|
|
23
|
+
const expectedTypes = schema.type
|
|
24
|
+
? (Array.isArray(schema.type) ? schema.type : [schema.type])
|
|
25
|
+
: undefined;
|
|
26
|
+
if (expectedTypes && !expectedTypes.some((type) => typeMatches(value, type))) {
|
|
27
|
+
violations.push({
|
|
28
|
+
path,
|
|
29
|
+
message: `expected ${expectedTypes.join(" or ")}, received ${typeOf(value)}`,
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (schema.enum && !schema.enum.some((entry) => entry === value)) {
|
|
34
|
+
violations.push({
|
|
35
|
+
path,
|
|
36
|
+
message: `value is not one of ${JSON.stringify(schema.enum)}`,
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(value) && schema.items) {
|
|
41
|
+
value.forEach((entry, index) => {
|
|
42
|
+
validateAgainst(entry, schema.items, `${path}[${index}]`, violations);
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (isRecord(value)) {
|
|
47
|
+
for (const key of schema.required ?? []) {
|
|
48
|
+
if (!(key in value)) {
|
|
49
|
+
violations.push({ path: `${path}.${key}`, message: "required property is missing" });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const properties = schema.properties ?? {};
|
|
53
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
54
|
+
const child = properties[key];
|
|
55
|
+
if (child) {
|
|
56
|
+
validateAgainst(entry, child, `${path}.${key}`, violations);
|
|
57
|
+
}
|
|
58
|
+
else if (schema.additionalProperties === false) {
|
|
59
|
+
violations.push({ path: `${path}.${key}`, message: "unexpected property" });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Extracts a JSON value from a model response. Models routinely wrap JSON in
|
|
66
|
+
* prose or a fenced block even when told not to, and rejecting those outright
|
|
67
|
+
* would burn a repair attempt on a formatting quirk rather than a real
|
|
68
|
+
* structural failure.
|
|
69
|
+
*/
|
|
70
|
+
export const extractJsonPayload = (content) => {
|
|
71
|
+
const trimmed = content.trim();
|
|
72
|
+
if (!trimmed)
|
|
73
|
+
return undefined;
|
|
74
|
+
const direct = tryParse(trimmed);
|
|
75
|
+
if (direct !== undefined)
|
|
76
|
+
return direct;
|
|
77
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
78
|
+
if (fenced?.[1]) {
|
|
79
|
+
const parsed = tryParse(fenced[1].trim());
|
|
80
|
+
if (parsed !== undefined)
|
|
81
|
+
return parsed;
|
|
82
|
+
}
|
|
83
|
+
const firstBrace = trimmed.search(/[[{]/);
|
|
84
|
+
if (firstBrace >= 0) {
|
|
85
|
+
const lastBrace = Math.max(trimmed.lastIndexOf("}"), trimmed.lastIndexOf("]"));
|
|
86
|
+
if (lastBrace > firstBrace) {
|
|
87
|
+
const parsed = tryParse(trimmed.slice(firstBrace, lastBrace + 1));
|
|
88
|
+
if (parsed !== undefined)
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
};
|
|
94
|
+
const tryParse = (value) => {
|
|
95
|
+
try {
|
|
96
|
+
return JSON.parse(value);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
export const validateResponseAgainstSchema = (content, schema) => {
|
|
103
|
+
if (!schema) {
|
|
104
|
+
return { ok: true, value: content, violations: [] };
|
|
105
|
+
}
|
|
106
|
+
const payload = extractJsonPayload(content);
|
|
107
|
+
if (payload === undefined) {
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
violations: [{ path: "$", message: "response was not parseable as JSON" }],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const violations = [];
|
|
114
|
+
validateAgainst(payload, schema, "$", violations);
|
|
115
|
+
return violations.length === 0
|
|
116
|
+
? { ok: true, value: payload, violations: [] }
|
|
117
|
+
: { ok: false, value: payload, violations };
|
|
118
|
+
};
|
|
119
|
+
/** Human-readable violation list, used to prompt one repair attempt. */
|
|
120
|
+
export const describeViolations = (violations) => violations.map((violation) => `- ${violation.path}: ${violation.message}`).join("\n");
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves relative time expressions to absolute ranges at plan time.
|
|
3
|
+
*
|
|
4
|
+
* "How is Bekir's performance going for the last two weeks?" is only answerable
|
|
5
|
+
* if every worker agrees on what "the last two weeks" means. Left to the model,
|
|
6
|
+
* each task invents its own window, connector queries disagree, and the run is
|
|
7
|
+
* unreproducible — re-running tomorrow silently asks a different question.
|
|
8
|
+
*
|
|
9
|
+
* So the range is computed once, in code, stamped into the trace, and handed to
|
|
10
|
+
* the planner as a fact. Resolution is deliberately conservative: an expression
|
|
11
|
+
* that cannot be resolved unambiguously is left alone rather than guessed at.
|
|
12
|
+
*/
|
|
13
|
+
export interface AbsoluteRange {
|
|
14
|
+
/** Inclusive ISO-8601 start instant. */
|
|
15
|
+
since: string;
|
|
16
|
+
/** Exclusive ISO-8601 end instant. */
|
|
17
|
+
until: string;
|
|
18
|
+
/** The phrase this range was derived from. */
|
|
19
|
+
phrase: string;
|
|
20
|
+
days: number;
|
|
21
|
+
}
|
|
22
|
+
export interface TemporalContext {
|
|
23
|
+
/** The instant the run treats as "now". */
|
|
24
|
+
now: string;
|
|
25
|
+
range?: AbsoluteRange;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extracts an absolute range from a query. Returns undefined when the query
|
|
29
|
+
* contains no relative time expression, or one we cannot resolve confidently —
|
|
30
|
+
* a wrong window is worse than no window, because it looks authoritative.
|
|
31
|
+
*/
|
|
32
|
+
export declare const resolveRelativeRange: (query: string, now?: Date) => AbsoluteRange | undefined;
|
|
33
|
+
export declare const buildTemporalContext: (query: string, now?: Date) => TemporalContext;
|
|
34
|
+
/** The lines handed to the planner so every task shares one window. */
|
|
35
|
+
export declare const renderTemporalContext: (context: TemporalContext) => string[];
|
|
36
|
+
//# sourceMappingURL=TemporalContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TemporalContext.d.ts","sourceRoot":"","sources":["../../src/gateway/TemporalContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AA2DD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,EACb,MAAK,IAAiB,KACrB,aAAa,GAAG,SA6BlB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,EACb,MAAK,IAAiB,KACrB,eAGD,CAAC;AAEH,uEAAuE;AACvE,eAAO,MAAM,qBAAqB,GAAI,SAAS,eAAe,KAAG,MAAM,EAiBtE,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves relative time expressions to absolute ranges at plan time.
|
|
3
|
+
*
|
|
4
|
+
* "How is Bekir's performance going for the last two weeks?" is only answerable
|
|
5
|
+
* if every worker agrees on what "the last two weeks" means. Left to the model,
|
|
6
|
+
* each task invents its own window, connector queries disagree, and the run is
|
|
7
|
+
* unreproducible — re-running tomorrow silently asks a different question.
|
|
8
|
+
*
|
|
9
|
+
* So the range is computed once, in code, stamped into the trace, and handed to
|
|
10
|
+
* the planner as a fact. Resolution is deliberately conservative: an expression
|
|
11
|
+
* that cannot be resolved unambiguously is left alone rather than guessed at.
|
|
12
|
+
*/
|
|
13
|
+
const DAY_MS = 86400000;
|
|
14
|
+
const WORD_NUMBERS = {
|
|
15
|
+
one: 1,
|
|
16
|
+
two: 2,
|
|
17
|
+
three: 3,
|
|
18
|
+
four: 4,
|
|
19
|
+
five: 5,
|
|
20
|
+
six: 6,
|
|
21
|
+
seven: 7,
|
|
22
|
+
eight: 8,
|
|
23
|
+
nine: 9,
|
|
24
|
+
ten: 10,
|
|
25
|
+
a: 1,
|
|
26
|
+
an: 1,
|
|
27
|
+
};
|
|
28
|
+
const UNIT_DAYS = {
|
|
29
|
+
day: 1,
|
|
30
|
+
days: 1,
|
|
31
|
+
week: 7,
|
|
32
|
+
weeks: 7,
|
|
33
|
+
month: 30,
|
|
34
|
+
months: 30,
|
|
35
|
+
quarter: 91,
|
|
36
|
+
quarters: 91,
|
|
37
|
+
year: 365,
|
|
38
|
+
years: 365,
|
|
39
|
+
};
|
|
40
|
+
/** Fixed phrases with no quantity, e.g. "yesterday". */
|
|
41
|
+
const FIXED_PHRASES = [
|
|
42
|
+
{ pattern: /\byesterday\b/i, days: 1 },
|
|
43
|
+
{ pattern: /\btoday\b/i, days: 1 },
|
|
44
|
+
{ pattern: /\bthis week\b/i, days: 7 },
|
|
45
|
+
{ pattern: /\blast week\b/i, days: 7 },
|
|
46
|
+
{ pattern: /\bthis month\b/i, days: 30 },
|
|
47
|
+
{ pattern: /\blast month\b/i, days: 30 },
|
|
48
|
+
{ pattern: /\bthis quarter\b/i, days: 91 },
|
|
49
|
+
{ pattern: /\blast quarter\b/i, days: 91 },
|
|
50
|
+
{ pattern: /\bthis year\b/i, days: 365 },
|
|
51
|
+
{ pattern: /\blast year\b/i, days: 365 },
|
|
52
|
+
{ pattern: /\brecently\b/i, days: 14 },
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Matches "last two weeks", "past 30 days", "previous 3 months".
|
|
56
|
+
* The quantity may be digits or a small word number.
|
|
57
|
+
*/
|
|
58
|
+
const QUANTIFIED = /\b(?:last|past|previous|recent)\s+(\d{1,3}|[a-z]+)\s+(day|days|week|weeks|month|months|quarter|quarters|year|years)\b/i;
|
|
59
|
+
const parseQuantity = (raw) => {
|
|
60
|
+
const numeric = Number.parseInt(raw, 10);
|
|
61
|
+
if (Number.isFinite(numeric) && numeric > 0 && numeric <= 999)
|
|
62
|
+
return numeric;
|
|
63
|
+
return WORD_NUMBERS[raw.toLowerCase()];
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Extracts an absolute range from a query. Returns undefined when the query
|
|
67
|
+
* contains no relative time expression, or one we cannot resolve confidently —
|
|
68
|
+
* a wrong window is worse than no window, because it looks authoritative.
|
|
69
|
+
*/
|
|
70
|
+
export const resolveRelativeRange = (query, now = new Date()) => {
|
|
71
|
+
const quantified = query.match(QUANTIFIED);
|
|
72
|
+
if (quantified) {
|
|
73
|
+
const quantity = parseQuantity(quantified[1] ?? "");
|
|
74
|
+
const unitDays = UNIT_DAYS[(quantified[2] ?? "").toLowerCase()];
|
|
75
|
+
if (quantity && unitDays) {
|
|
76
|
+
const days = quantity * unitDays;
|
|
77
|
+
return {
|
|
78
|
+
since: new Date(now.getTime() - days * DAY_MS).toISOString(),
|
|
79
|
+
until: now.toISOString(),
|
|
80
|
+
phrase: quantified[0],
|
|
81
|
+
days,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const fixed of FIXED_PHRASES) {
|
|
86
|
+
const match = query.match(fixed.pattern);
|
|
87
|
+
if (match) {
|
|
88
|
+
return {
|
|
89
|
+
since: new Date(now.getTime() - fixed.days * DAY_MS).toISOString(),
|
|
90
|
+
until: now.toISOString(),
|
|
91
|
+
phrase: match[0],
|
|
92
|
+
days: fixed.days,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return undefined;
|
|
97
|
+
};
|
|
98
|
+
export const buildTemporalContext = (query, now = new Date()) => ({
|
|
99
|
+
now: now.toISOString(),
|
|
100
|
+
range: resolveRelativeRange(query, now),
|
|
101
|
+
});
|
|
102
|
+
/** The lines handed to the planner so every task shares one window. */
|
|
103
|
+
export const renderTemporalContext = (context) => {
|
|
104
|
+
const lines = [`Current time: ${context.now}`];
|
|
105
|
+
if (context.range) {
|
|
106
|
+
lines.push(`Resolved time range for "${context.range.phrase}": ${context.range.since} to ${context.range.until} (${context.range.days} days).`, "Use these absolute timestamps in every tool call. Do not compute your own dates.");
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Stating the current time invites a model to use it as a range start,
|
|
110
|
+
// which asks for everything since this instant and returns nothing.
|
|
111
|
+
// Observed: `list_commits since=<now>`, a valid call with an empty answer.
|
|
112
|
+
lines.push("The question named no time period. Do not use the current time as a start or end date — that would match nothing.", "Omit date filters entirely and let the tool return its most recent results.");
|
|
113
|
+
}
|
|
114
|
+
return lines;
|
|
115
|
+
};
|
|
@@ -26,6 +26,11 @@ export interface CodaliGatewayCompiledToolCapability {
|
|
|
26
26
|
}
|
|
27
27
|
export interface ToolCapabilityCompilerInput {
|
|
28
28
|
policy: CodaliGatewayPolicy;
|
|
29
|
+
/**
|
|
30
|
+
* Tools whose read-only status has been declared by policy rather than
|
|
31
|
+
* inferred from their name. See {@link classifyCapabilityRisk}.
|
|
32
|
+
*/
|
|
33
|
+
declaredReadOnlyTools?: string[];
|
|
29
34
|
docdex?: CodaliRuntimeDocdexInput;
|
|
30
35
|
tools?: CodaliRuntimeToolManifest;
|
|
31
36
|
requiredDocdexOperations?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolCapabilityCompiler.d.ts","sourceRoot":"","sources":["../../src/gateway/ToolCapabilityCompiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAE5B,mCAAmC,EACnC,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EACV,mBAAmB,EACnB,6BAA6B,EAC9B,MAAM,yBAAyB,CAAC;AAEjC,MAAM,MAAM,+BAA+B,GACvC,QAAQ,GACR,SAAS,GACT,cAAc,GACd,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,iCAAiC,GACzC,SAAS,GACT,QAAQ,GACR,SAAS,CAAC;AAEd,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,mCAAmC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,+BAA+B,CAAC;IACtC,MAAM,EAAE,iCAAiC,CAAC;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,6BAA6B,CAAC;IAC5C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,KAAK,CAAC,EAAE,yBAAyB,CAAC;IAClC,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;IACpC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,yBAAyB;IACxC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,4BAA4B,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,mCAAmC,CAAC;IACrD,YAAY,EAAE,mCAAmC,EAAE,CAAC;IACpD,YAAY,EAAE,wBAAwB,EAAE,CAAC;IACzC,QAAQ,EAAE,0BAA0B,EAAE,CAAC;IACvC,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACrC,wBAAwB,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,eAAO,MAAM,sCAAsC,iHAOzC,CAAC;AAEX,eAAO,MAAM,qCAAqC,mMAgBxC,CAAC;
|
|
1
|
+
{"version":3,"file":"ToolCapabilityCompiler.d.ts","sourceRoot":"","sources":["../../src/gateway/ToolCapabilityCompiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAE5B,mCAAmC,EACnC,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EACV,mBAAmB,EACnB,6BAA6B,EAC9B,MAAM,yBAAyB,CAAC;AAEjC,MAAM,MAAM,+BAA+B,GACvC,QAAQ,GACR,SAAS,GACT,cAAc,GACd,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,iCAAiC,GACzC,SAAS,GACT,QAAQ,GACR,SAAS,CAAC;AAEd,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,mCAAmC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,+BAA+B,CAAC;IACtC,MAAM,EAAE,iCAAiC,CAAC;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,6BAA6B,CAAC;IAC5C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,KAAK,CAAC,EAAE,yBAAyB,CAAC;IAClC,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;IACpC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,yBAAyB;IACxC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,4BAA4B,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,mCAAmC,CAAC;IACrD,YAAY,EAAE,mCAAmC,EAAE,CAAC;IACpD,YAAY,EAAE,wBAAwB,EAAE,CAAC;IACzC,QAAQ,EAAE,0BAA0B,EAAE,CAAC;IACvC,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACrC,wBAAwB,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,eAAO,MAAM,sCAAsC,iHAOzC,CAAC;AAEX,eAAO,MAAM,qCAAqC,mMAgBxC,CAAC;AAwWX,eAAO,MAAM,uBAAuB,GAClC,OAAO,2BAA2B,KACjC,yBAgSF,CAAC"}
|
|
@@ -91,10 +91,28 @@ const hasRiskToken = (tool, tokens) => {
|
|
|
91
91
|
return toolNameTokens(tool).some((token) => tokens.has(token)) ||
|
|
92
92
|
[...tokens].some((token) => normalized === token || normalized.endsWith(`_${token}`));
|
|
93
93
|
};
|
|
94
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Classifies a tool's risk.
|
|
96
|
+
*
|
|
97
|
+
* The name heuristic exists because, before connectors, a tool's name was the
|
|
98
|
+
* only signal available. It cannot distinguish "get a commit" from "commit
|
|
99
|
+
* something": `get_commit` ends with `_commit` and was therefore classified as
|
|
100
|
+
* a write, which blocked an ordinary read.
|
|
101
|
+
*
|
|
102
|
+
* A tool whose read-only status has been *declared* — an operator marking an
|
|
103
|
+
* MCP server read-only, or an HTTP tool restricted to GET/HEAD — carries better
|
|
104
|
+
* information than its name, so the declaration wins.
|
|
105
|
+
*
|
|
106
|
+
* The destructive check is not overridable. A declaration that `delete_file` is
|
|
107
|
+
* read-only is a configuration mistake, and the floor should hold against it.
|
|
108
|
+
*/
|
|
109
|
+
const classifyCapabilityRisk = (name, readOnly, declaredReadOnly = false) => {
|
|
95
110
|
if (hasRiskToken(name, DESTRUCTIVE_TOOL_TOKENS)) {
|
|
96
111
|
return "destructive_blocked";
|
|
97
112
|
}
|
|
113
|
+
if (declaredReadOnly && readOnly) {
|
|
114
|
+
return "read_only";
|
|
115
|
+
}
|
|
98
116
|
if (!readOnly || hasRiskToken(name, WRITE_TOOL_TOKENS)) {
|
|
99
117
|
return "write_with_approval";
|
|
100
118
|
}
|
|
@@ -268,8 +286,15 @@ export const compileToolCapabilities = (input) => {
|
|
|
268
286
|
const contracts = normalizeAppToolContracts(input.policy.appToolContracts, warnings);
|
|
269
287
|
const allowedBackingTools = new Set(unique(input.allowedBackingTools ?? CODALI_GATEWAY_READ_ONLY_BACKING_TOOLS));
|
|
270
288
|
const requiredDocdexOperations = new Set(unique(input.requiredDocdexOperations ?? []));
|
|
289
|
+
const declaredReadOnly = new Set(unique([
|
|
290
|
+
...(input.declaredReadOnlyTools ?? []),
|
|
291
|
+
...readManifestTools(input.tools ?? input.docdex?.toolManifest, [
|
|
292
|
+
"readOnlyTools",
|
|
293
|
+
"read_only_tools",
|
|
294
|
+
]),
|
|
295
|
+
]));
|
|
271
296
|
const addCapability = (name, kind, status, readOnly, backingTools, reasons) => {
|
|
272
|
-
const riskCategory = classifyCapabilityRisk(name, readOnly);
|
|
297
|
+
const riskCategory = classifyCapabilityRisk(name, readOnly, declaredReadOnly.has(name));
|
|
273
298
|
capabilities.push({
|
|
274
299
|
name,
|
|
275
300
|
kind,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ToolRegistry } from "../tools/ToolRegistry.js";
|
|
2
|
+
import type { ToolDefinition } from "../tools/ToolTypes.js";
|
|
3
|
+
import type { CodaliGatewayPlannerToolDescriptor } from "./GatewayPlanner.js";
|
|
4
|
+
export declare const gatewayToolDescriptorsFromDefinitions: (tools: readonly ToolDefinition[]) => Record<string, CodaliGatewayPlannerToolDescriptor>;
|
|
5
|
+
export declare const gatewayToolDescriptorsFromRegistry: (registry: ToolRegistry, names?: readonly string[]) => Record<string, CodaliGatewayPlannerToolDescriptor>;
|
|
6
|
+
//# sourceMappingURL=ToolDescriptorSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolDescriptorSource.d.ts","sourceRoot":"","sources":["../../src/gateway/ToolDescriptorSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,uBAAuB,CAAC;AAE5E,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,qBAAqB,CAAC;AAqB9E,eAAO,MAAM,qCAAqC,GAChD,OAAO,SAAS,cAAc,EAAE,KAC/B,MAAM,CAAC,MAAM,EAAE,kCAAkC,CAMnD,CAAC;AAEF,eAAO,MAAM,kCAAkC,GAC7C,UAAU,YAAY,EACtB,QAAQ,SAAS,MAAM,EAAE,KACxB,MAAM,CAAC,MAAM,EAAE,kCAAkC,CAMnD,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { toolDescriptorFor } from "../tools/ToolRegistry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Bridges the executor's tool registry to the planner's model-facing view.
|
|
4
|
+
*
|
|
5
|
+
* This exists so there is exactly one place where a tool's schema is turned
|
|
6
|
+
* into something a model reads. If the planner and the executor ever disagree
|
|
7
|
+
* about a tool's arguments, it is because something bypassed this function.
|
|
8
|
+
*/
|
|
9
|
+
const toPlannerDescriptor = (descriptor) => ({
|
|
10
|
+
name: descriptor.name,
|
|
11
|
+
description: descriptor.description,
|
|
12
|
+
inputSchema: descriptor.inputSchema,
|
|
13
|
+
outputSchema: descriptor.outputSchema,
|
|
14
|
+
capability: descriptor.capability,
|
|
15
|
+
readOnly: descriptor.readOnly,
|
|
16
|
+
});
|
|
17
|
+
export const gatewayToolDescriptorsFromDefinitions = (tools) => {
|
|
18
|
+
const descriptors = {};
|
|
19
|
+
for (const tool of tools) {
|
|
20
|
+
descriptors[tool.name] = toPlannerDescriptor(toolDescriptorFor(tool));
|
|
21
|
+
}
|
|
22
|
+
return descriptors;
|
|
23
|
+
};
|
|
24
|
+
export const gatewayToolDescriptorsFromRegistry = (registry, names) => {
|
|
25
|
+
const descriptors = {};
|
|
26
|
+
for (const descriptor of registry.catalog(names)) {
|
|
27
|
+
descriptors[descriptor.name] = toPlannerDescriptor(descriptor);
|
|
28
|
+
}
|
|
29
|
+
return descriptors;
|
|
30
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CodaliGatewayPlannerToolDescriptor } from "./GatewayPlanner.js";
|
|
2
|
+
/** Cap on tools expanded in one planner prompt, after capability narrowing. */
|
|
3
|
+
export declare const MAX_EXPANDED_TOOLS = 40;
|
|
4
|
+
export interface ToolExposureDescriptor extends CodaliGatewayPlannerToolDescriptor {
|
|
5
|
+
capability?: string;
|
|
6
|
+
readOnly?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export type ToolExposureDescriptors = Record<string, string | ToolExposureDescriptor>;
|
|
9
|
+
interface NormalizedDescriptor {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
capability: string;
|
|
13
|
+
readOnly: boolean;
|
|
14
|
+
inputSchema?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export declare const normalizeToolExposure: (toolNames: readonly string[], descriptors: ToolExposureDescriptors | undefined) => NormalizedDescriptor[];
|
|
17
|
+
export declare const groupByCapability: (tools: NormalizedDescriptor[]) => Map<string, NormalizedDescriptor[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Stage 1 rendering: one line per capability, no schemas. This is what the
|
|
20
|
+
* classifier sees, and it stays a fixed small size no matter how many tools a
|
|
21
|
+
* tenant has connected.
|
|
22
|
+
*/
|
|
23
|
+
export declare const renderCapabilityLines: (toolNames: readonly string[], descriptors: ToolExposureDescriptors | undefined) => string;
|
|
24
|
+
export interface RenderToolLinesResult {
|
|
25
|
+
text: string;
|
|
26
|
+
expandedTools: string[];
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Stage 2 rendering: full descriptions and argument shapes, narrowed to the
|
|
31
|
+
* capabilities the classifier selected. Passing no selection expands
|
|
32
|
+
* everything, which is the correct behaviour for small local tool sets.
|
|
33
|
+
*/
|
|
34
|
+
export declare const renderToolLines: (toolNames: readonly string[], descriptors: ToolExposureDescriptors | undefined, selectedCapabilities?: readonly string[]) => RenderToolLinesResult;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=ToolExposure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolExposure.d.ts","sourceRoot":"","sources":["../../src/gateway/ToolExposure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,qBAAqB,CAAC;AAuB9E,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,MAAM,WAAW,sBAAuB,SAAQ,kCAAkC;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAC1C,MAAM,EACN,MAAM,GAAG,sBAAsB,CAChC,CAAC;AAEF,UAAU,oBAAoB;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAwDD,eAAO,MAAM,qBAAqB,GAChC,WAAW,SAAS,MAAM,EAAE,EAC5B,aAAa,uBAAuB,GAAG,SAAS,KAC/C,oBAAoB,EACwC,CAAC;AAEhE,eAAO,MAAM,iBAAiB,GAC5B,OAAO,oBAAoB,EAAE,KAC5B,GAAG,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAQpC,CAAC;AAeF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,WAAW,SAAS,MAAM,EAAE,EAC5B,aAAa,uBAAuB,GAAG,SAAS,KAC/C,MAUF,CAAC;AAqBF,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC1B,WAAW,SAAS,MAAM,EAAE,EAC5B,aAAa,uBAAuB,GAAG,SAAS,EAChD,uBAAuB,SAAS,MAAM,EAAE,KACvC,qBA+BF,CAAC"}
|