@mcoda/codali 0.1.110 → 0.1.112
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/cli/AskCommand.d.ts.map +1 -1
- package/dist/cli/AskCommand.js +20 -1
- package/dist/docdex/DocdexClient.d.ts +6 -0
- package/dist/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/docdex/DocdexClient.js +2 -0
- package/dist/gateway/CodaliGateway.d.ts +2 -1
- package/dist/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/gateway/CodaliGateway.js +104 -37
- package/dist/gateway/CodaliGatewayTypes.d.ts +10 -0
- package/dist/gateway/CodaliGatewayTypes.d.ts.map +1 -1
- package/dist/gateway/EvidenceNormalizer.d.ts.map +1 -1
- package/dist/gateway/EvidenceNormalizer.js +3 -0
- package/dist/gateway/GatewayPlanner.d.ts.map +1 -1
- package/dist/gateway/GatewayPlanner.js +37 -1
- package/dist/gateway/GroundingMode.d.ts +35 -0
- package/dist/gateway/GroundingMode.d.ts.map +1 -0
- package/dist/gateway/GroundingMode.js +69 -0
- package/dist/gateway/LocalGatewayProvider.d.ts +16 -0
- package/dist/gateway/LocalGatewayProvider.d.ts.map +1 -1
- package/dist/gateway/LocalGatewayProvider.js +24 -1
- package/dist/gateway/LocalGatewayTaskRunner.d.ts.map +1 -1
- package/dist/gateway/LocalGatewayTaskRunner.js +10 -1
- package/dist/gateway/RelevanceJudge.d.ts.map +1 -1
- package/dist/gateway/RelevanceJudge.js +25 -8
- package/dist/providers/OpenAiCompatibleProvider.d.ts +1 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/providers/OpenAiCompatibleProvider.js +55 -3
- package/dist/providers/ProviderTypes.d.ts +2 -0
- package/dist/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/tools/ToolRegistry.d.ts +5 -1
- package/dist/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/tools/ToolRegistry.js +79 -2
- package/dist/tools/docdex/DocdexTools.d.ts +13 -0
- package/dist/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/tools/docdex/DocdexTools.js +167 -12
- package/package.json +3 -3
|
@@ -32,6 +32,22 @@ export declare const CODALI_MSWARM_PRIORITY = -10;
|
|
|
32
32
|
export declare const providerNameForAdapter: (adapter: string | undefined) => string;
|
|
33
33
|
export declare const createProviderForName: (name: string, config: ProviderConfig) => Provider;
|
|
34
34
|
export declare const resolveMswarmApiKey: () => string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* The key for a self-hosted agent that is not behind the mswarm relay.
|
|
37
|
+
*
|
|
38
|
+
* mcoda stores agent secrets encrypted (`agent_auth.encrypted_secret`) and
|
|
39
|
+
* `agent list --json` reports only whether one is configured, so an agent
|
|
40
|
+
* registered with `authMode: bearer` and a direct base URL arrives here with no
|
|
41
|
+
* key at all. The provider then threw before making any request, which surfaced
|
|
42
|
+
* as a worker failing in 0ms on every call while the run still reported
|
|
43
|
+
* success — the finalizer answered without evidence rather than the run
|
|
44
|
+
* stopping on a configuration error.
|
|
45
|
+
*
|
|
46
|
+
* Codali therefore takes the key from the operator's own environment, matching
|
|
47
|
+
* how the mswarm key is read from local trusted config. The per-agent form wins
|
|
48
|
+
* so several self-hosted models can be used at once.
|
|
49
|
+
*/
|
|
50
|
+
export declare const localAgentApiKeyEnvVar: (slug: string) => string;
|
|
35
51
|
/**
|
|
36
52
|
* Creates a provider for an assigned agent. The raw inventory entry carries the
|
|
37
53
|
* transport details (base URL, runner kind, auth mode) that the tier resolver
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalGatewayProvider.d.ts","sourceRoot":"","sources":["../../src/gateway/LocalGatewayProvider.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAE3E;;;;;;GAMG;AAEH,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA0CD;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAwB1C;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,GAAG,SAAS,KAAG,MAapE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EACZ,QAAQ,cAAc,KACrB,QAWF,CAAC;AAYF,eAAO,MAAM,mBAAmB,QAAO,MAAM,GAAG,SA4B/C,CAAC;
|
|
1
|
+
{"version":3,"file":"LocalGatewayProvider.d.ts","sourceRoot":"","sources":["../../src/gateway/LocalGatewayProvider.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAE3E;;;;;;GAMG;AAEH,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA0CD;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAwB1C;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,GAAG,SAAS,KAAG,MAapE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EACZ,QAAQ,cAAc,KACrB,QAWF,CAAC;AAYF,eAAO,MAAM,mBAAmB,QAAO,MAAM,GAAG,SA4B/C,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,GAAI,MAAM,MAAM,KAAG,MACoB,CAAC;AAU3E;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GACtC,YAAY,4BAA4B,EACxC,UAAS,oBAAyB,KACjC,QAqCF,CAAC;AAEF;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,YAAW,QAAQ;IAIhD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAL9B,QAAQ,CAAC,IAAI,wBAAwB;gBAGlB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EACnC,QAAQ,EAAE,QAAQ,EAClB,WAAW,GAAE,MAAM,MAAM,GAAG,SAA2B;IAG1E,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ;IAK7C,QAAQ,CACZ,OAAO,EAAE,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAC3C,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;CAGpC"}
|
|
@@ -135,6 +135,26 @@ export const resolveMswarmApiKey = () => {
|
|
|
135
135
|
cachedMswarmKey = null;
|
|
136
136
|
return undefined;
|
|
137
137
|
};
|
|
138
|
+
/**
|
|
139
|
+
* The key for a self-hosted agent that is not behind the mswarm relay.
|
|
140
|
+
*
|
|
141
|
+
* mcoda stores agent secrets encrypted (`agent_auth.encrypted_secret`) and
|
|
142
|
+
* `agent list --json` reports only whether one is configured, so an agent
|
|
143
|
+
* registered with `authMode: bearer` and a direct base URL arrives here with no
|
|
144
|
+
* key at all. The provider then threw before making any request, which surfaced
|
|
145
|
+
* as a worker failing in 0ms on every call while the run still reported
|
|
146
|
+
* success — the finalizer answered without evidence rather than the run
|
|
147
|
+
* stopping on a configuration error.
|
|
148
|
+
*
|
|
149
|
+
* Codali therefore takes the key from the operator's own environment, matching
|
|
150
|
+
* how the mswarm key is read from local trusted config. The per-agent form wins
|
|
151
|
+
* so several self-hosted models can be used at once.
|
|
152
|
+
*/
|
|
153
|
+
export const localAgentApiKeyEnvVar = (slug) => `CODALI_AGENT_API_KEY_${slug.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`;
|
|
154
|
+
const resolveLocalAgentApiKey = (slug) => {
|
|
155
|
+
const perAgent = slug ? process.env[localAgentApiKeyEnvVar(slug)]?.trim() : undefined;
|
|
156
|
+
return perAgent || process.env.CODALI_API_KEY?.trim() || undefined;
|
|
157
|
+
};
|
|
138
158
|
const isMswarmEndpoint = (baseUrl) => Boolean(baseUrl && /(^|\/\/)([^/]*\.)?mswarm\.org/i.test(baseUrl));
|
|
139
159
|
/**
|
|
140
160
|
* Creates a provider for an assigned agent. The raw inventory entry carries the
|
|
@@ -153,9 +173,12 @@ export const createProviderForAssignment = (assignment, options = {}) => {
|
|
|
153
173
|
const apiKey = options.apiKey ??
|
|
154
174
|
readString(nested, ["apiKey", "api_key"]) ??
|
|
155
175
|
readString(raw, ["apiKey", "api_key"]) ??
|
|
156
|
-
(isMswarmEndpoint(baseUrl)
|
|
176
|
+
(isMswarmEndpoint(baseUrl)
|
|
177
|
+
? resolveMswarmApiKey()
|
|
178
|
+
: resolveLocalAgentApiKey(assignment.candidate.slug));
|
|
157
179
|
const config = {
|
|
158
180
|
model: assignment.candidate.model ?? readString(raw, ["defaultModel", "model"]) ?? "",
|
|
181
|
+
agentSlug: assignment.candidate.slug,
|
|
159
182
|
baseUrl,
|
|
160
183
|
apiKey,
|
|
161
184
|
timeoutMs: options.timeoutMs ?? LOCAL_MODEL_TIMEOUT_MS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalGatewayTaskRunner.d.ts","sourceRoot":"","sources":["../../src/gateway/LocalGatewayTaskRunner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAGT,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,KAAK,EAEV,+BAA+B,EAC/B,gCAAgC,EAChC,6BAA6B,EAE9B,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAe1C,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,IAAI,CAAC;CACxD;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAClE;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"LocalGatewayTaskRunner.d.ts","sourceRoot":"","sources":["../../src/gateway/LocalGatewayTaskRunner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAGT,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,KAAK,EAEV,+BAA+B,EAC/B,gCAAgC,EAChC,6BAA6B,EAE9B,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAe1C,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,IAAI,CAAC;CACxD;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAClE;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAsEN,qBAAa,sBAAuB,YAAW,6BAA6B;IAC9D,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,6BAA6B;IAE7D,GAAG,CACP,KAAK,EAAE,+BAA+B,GACrC,OAAO,CAAC,gCAAgC,CAAC;CAqT7C;AAED,eAAO,MAAM,4BAA4B,GACvC,SAAS,6BAA6B,KACrC,sBAA6D,CAAC;AAEjE,eAAO,MAAM,aAAa,UApYD,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAoY7B,CAAC"}
|
|
@@ -70,7 +70,16 @@ const buildTaskMessages = (input, hasTools) => {
|
|
|
70
70
|
"You are a Codali gateway worker.",
|
|
71
71
|
"Gather evidence for one bounded sub-task. Do not answer the user's overall question.",
|
|
72
72
|
hasTools
|
|
73
|
-
?
|
|
73
|
+
? [
|
|
74
|
+
"Call the tools you need in a single batch. You get exactly one opportunity to call tools, so request everything you need at once.",
|
|
75
|
+
// Told only that it had one chance, the worker reliably issued a
|
|
76
|
+
// single search and stopped. A search ranks whole files; it does not
|
|
77
|
+
// return the value of a constant or the contents of a directory, so
|
|
78
|
+
// questions of that shape came back unanswered with the budget for
|
|
79
|
+
// seven more calls unspent.
|
|
80
|
+
"One search is rarely enough. In the same batch, add the tools that cover what a search cannot: listing a directory's files, reading a named file, or looking up a symbol's definition.",
|
|
81
|
+
"Prefer several complementary calls over one broad one — unused calls cost nothing, a missing one costs the answer.",
|
|
82
|
+
].join("\n")
|
|
74
83
|
: "No tools are available. Answer from the task description alone or state what is missing.",
|
|
75
84
|
"Report only what the tool results actually support. Never invent file paths, identifiers, figures, or quotations.",
|
|
76
85
|
"If the results are insufficient, say so plainly instead of guessing; another round may be scheduled.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RelevanceJudge.d.ts","sourceRoot":"","sources":["../../src/gateway/RelevanceJudge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAyBrE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,SAAS,OAAO,KAAG,MAAM,EAqB9D,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"RelevanceJudge.d.ts","sourceRoot":"","sources":["../../src/gateway/RelevanceJudge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAyBrE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,SAAS,OAAO,KAAG,MAAM,EAqB9D,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoCD;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,GACpC,OAAO,MAAM,EACb,QAAQ,SAAS,MAAM,EAAE,KACxB,OAWF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,SAAS,qBAAqB,KAC7B,cAyCF,CAAC"}
|
|
@@ -58,6 +58,25 @@ const STOPWORDS = new Set([
|
|
|
58
58
|
"class", "file", "code", "show", "list", "find", "tell", "give", "about",
|
|
59
59
|
"purpose", "exists", "existence", "summary", "summarize", "explain",
|
|
60
60
|
]);
|
|
61
|
+
/**
|
|
62
|
+
* Splits text into comparable words, breaking CamelCase as well as punctuation.
|
|
63
|
+
*
|
|
64
|
+
* A symbol name is one word to a human and several to a path. Asked "which file
|
|
65
|
+
* defines CodaliGatewayPlannerError", the question yields the single term
|
|
66
|
+
* `codaligatewayplannererror`, while `gateway/GatewayPlanner.ts` flattens to
|
|
67
|
+
* `gateway gatewayplanner ts` — no overlap, so the correct file was discarded.
|
|
68
|
+
* Splitting both sides on case boundaries puts `gateway` and `planner` on each
|
|
69
|
+
* side, and the match succeeds.
|
|
70
|
+
*/
|
|
71
|
+
const wordsIn = (value) => value
|
|
72
|
+
// Insert a break between a lowercase/digit and a following uppercase, so
|
|
73
|
+
// `GatewayPlanner` becomes `Gateway Planner` before the lowercase pass.
|
|
74
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
75
|
+
// And between an acronym and a following word: `HTTPClient` -> `HTTP Client`.
|
|
76
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
|
|
77
|
+
.toLowerCase()
|
|
78
|
+
.split(/[^a-z0-9]+/)
|
|
79
|
+
.filter(Boolean);
|
|
61
80
|
/**
|
|
62
81
|
* Whether any result title shares a distinctive word with the question.
|
|
63
82
|
*
|
|
@@ -69,16 +88,14 @@ const STOPWORDS = new Set([
|
|
|
69
88
|
* results — which is where it earns its keep.
|
|
70
89
|
*/
|
|
71
90
|
export const titlesShareTermsWithQuery = (query, titles) => {
|
|
72
|
-
const terms = query
|
|
73
|
-
.toLowerCase()
|
|
74
|
-
.split(/[^a-z0-9]+/)
|
|
75
|
-
.filter((word) => word.length > 3 && !STOPWORDS.has(word));
|
|
91
|
+
const terms = wordsIn(query).filter((word) => word.length > 3 && !STOPWORDS.has(word));
|
|
76
92
|
if (terms.length === 0)
|
|
77
93
|
return false;
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
// Whole words only. Splitting both sides the same way already lets a symbol
|
|
95
|
+
// in the question meet the same symbol in a path, so a substring pass would
|
|
96
|
+
// add nothing but false matches ("string" hitting `StringUtils.ts`).
|
|
97
|
+
const haystack = ` ${wordsIn(titles.join(" ")).join(" ")} `;
|
|
98
|
+
return terms.some((term) => haystack.includes(` ${term} `));
|
|
82
99
|
};
|
|
83
100
|
export const createRelevanceJudge = (options) => async ({ query, results }) => {
|
|
84
101
|
const titles = summarizeResultTitles(results);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Provider, ProviderConfig, ProviderRequest, ProviderResponse } from "./ProviderTypes.js";
|
|
2
|
+
export declare const fetchWithTransientRetry: (url: string, init: RequestInit, controller?: AbortController) => Promise<Response>;
|
|
2
3
|
export declare class OpenAiCompatibleProvider implements Provider {
|
|
3
4
|
private config;
|
|
4
5
|
/** OpenAI-style function calling is supported and parsed from `tool_calls`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenAiCompatibleProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OpenAiCompatibleProvider.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EAEd,eAAe,EACf,gBAAgB,EAGjB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"OpenAiCompatibleProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OpenAiCompatibleProvider.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EAEd,eAAe,EACf,gBAAgB,EAGjB,MAAM,oBAAoB,CAAC;AAgJ5B,eAAO,MAAM,uBAAuB,GAClC,KAAK,MAAM,EACX,MAAM,WAAW,EACjB,aAAa,eAAe,KAC3B,OAAO,CAAC,QAAQ,CAclB,CAAC;AAEF,qBAAa,wBAAyB,YAAW,QAAQ;IAS3C,OAAO,CAAC,MAAM;IAR1B,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,QAAQ;IAElC,IAAI,SAAuB;IAC3B,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,sBAAsB,CAAoC;gBAE9C,MAAM,EAAE,cAAc;IAWpC,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0MnE,OAAO,CAAC,WAAW;CA0BpB"}
|
|
@@ -80,6 +80,49 @@ const buildPromptOnlyFormatInstruction = (format) => {
|
|
|
80
80
|
}
|
|
81
81
|
return undefined;
|
|
82
82
|
};
|
|
83
|
+
/**
|
|
84
|
+
* A single dropped connection must not fail a whole run.
|
|
85
|
+
*
|
|
86
|
+
* Self-hosted llama.cpp behind a relay closes sockets under load, surfacing as
|
|
87
|
+
* a bare `TypeError: fetch failed` with no status. Measured over fifty live
|
|
88
|
+
* questions this took out four runs outright — the caller saw an empty result,
|
|
89
|
+
* not a slow one. Retried, each of them succeeded.
|
|
90
|
+
*
|
|
91
|
+
* Deliberately narrow: only connection-level errors are retried. An HTTP error
|
|
92
|
+
* status is the server's considered answer and repeating it just wastes a
|
|
93
|
+
* minute, and an abort is our own timeout firing, where retrying would double
|
|
94
|
+
* the wait the caller already declined.
|
|
95
|
+
*/
|
|
96
|
+
const TRANSIENT_RETRIES = 2;
|
|
97
|
+
const TRANSIENT_RETRY_DELAY_MS = 750;
|
|
98
|
+
const isTransientNetworkError = (error) => {
|
|
99
|
+
if (error instanceof Error && error.name === "AbortError")
|
|
100
|
+
return false;
|
|
101
|
+
const cause = error?.cause;
|
|
102
|
+
const code = cause?.code ?? error?.code;
|
|
103
|
+
if (typeof code === "string") {
|
|
104
|
+
return ["ECONNRESET", "ECONNREFUSED", "EPIPE", "ETIMEDOUT", "UND_ERR_SOCKET"].includes(code);
|
|
105
|
+
}
|
|
106
|
+
return error instanceof TypeError && /fetch failed|network|socket/i.test(error.message);
|
|
107
|
+
};
|
|
108
|
+
export const fetchWithTransientRetry = async (url, init, controller) => {
|
|
109
|
+
let lastError;
|
|
110
|
+
for (let attempt = 0; attempt <= TRANSIENT_RETRIES; attempt += 1) {
|
|
111
|
+
try {
|
|
112
|
+
return await fetch(url, init);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
lastError = error;
|
|
116
|
+
// Our own timeout already fired; there is no time left to spend.
|
|
117
|
+
if (controller?.signal.aborted || !isTransientNetworkError(error))
|
|
118
|
+
throw error;
|
|
119
|
+
if (attempt === TRANSIENT_RETRIES)
|
|
120
|
+
break;
|
|
121
|
+
await new Promise((resolve) => setTimeout(resolve, TRANSIENT_RETRY_DELAY_MS * (attempt + 1)));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
throw lastError;
|
|
125
|
+
};
|
|
83
126
|
export class OpenAiCompatibleProvider {
|
|
84
127
|
constructor(config) {
|
|
85
128
|
this.config = config;
|
|
@@ -156,12 +199,12 @@ export class OpenAiCompatibleProvider {
|
|
|
156
199
|
const timeoutMs = this.config.timeoutMs ?? 60000;
|
|
157
200
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
158
201
|
try {
|
|
159
|
-
const response = await
|
|
202
|
+
const response = await fetchWithTransientRetry(url, {
|
|
160
203
|
method: "POST",
|
|
161
204
|
headers,
|
|
162
205
|
body: JSON.stringify(body),
|
|
163
206
|
signal: controller.signal,
|
|
164
|
-
});
|
|
207
|
+
}, controller);
|
|
165
208
|
if (!response.ok) {
|
|
166
209
|
const errorBody = await response.text();
|
|
167
210
|
throw new Error(`OpenAI-compatible error ${response.status}: ${errorBody}`);
|
|
@@ -284,7 +327,16 @@ export class OpenAiCompatibleProvider {
|
|
|
284
327
|
};
|
|
285
328
|
}
|
|
286
329
|
if (!this.config.apiKey) {
|
|
287
|
-
|
|
330
|
+
// Name the agent and the variable that fixes it. The old message named a
|
|
331
|
+
// single global key, which is wrong once more than one self-hosted model
|
|
332
|
+
// is registered, and gave no clue which agent had failed.
|
|
333
|
+
const slug = this.config.agentSlug;
|
|
334
|
+
const perAgent = slug
|
|
335
|
+
? ` Set ${`CODALI_AGENT_API_KEY_${slug.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`}, or`
|
|
336
|
+
: "";
|
|
337
|
+
throw new Error(`AUTH_REQUIRED: no API key for ${slug ?? "this OpenAI-compatible agent"} ` +
|
|
338
|
+
`(auth mode "bearer", base URL ${this.config.baseUrl ?? "unset"}).` +
|
|
339
|
+
`${perAgent} set CODALI_API_KEY, or register the agent with auth mode "none".`);
|
|
288
340
|
}
|
|
289
341
|
return { mode: "bearer", authorization: `Bearer ${this.config.apiKey}` };
|
|
290
342
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderTypes.d.ts","sourceRoot":"","sources":["../../src/providers/ProviderTypes.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProviderTypes.d.ts","sourceRoot":"","sources":["../../src/providers/ProviderTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { type ToolCapability, type ToolContext, type ToolDefinition, type ToolDescriptor, type ToolExecutionResult } from "./ToolTypes.js";
|
|
1
|
+
import { type ToolCapability, type ToolContext, type ToolDefinition, type ToolDescriptor, type ToolExecutionResult, type ToolSchemaDefinition } from "./ToolTypes.js";
|
|
2
2
|
/**
|
|
3
3
|
* Projects a registered tool into its model-facing descriptor. Kept as a free
|
|
4
4
|
* function so connector adapters can reuse the same defaulting rules.
|
|
5
5
|
*/
|
|
6
6
|
export declare const toolDescriptorFor: (tool: ToolDefinition) => ToolDescriptor;
|
|
7
|
+
export declare const sanitizeToolArguments: (value: unknown, schema: ToolSchemaDefinition | undefined) => {
|
|
8
|
+
value: unknown;
|
|
9
|
+
dropped: string[];
|
|
10
|
+
};
|
|
7
11
|
export declare class ToolRegistry {
|
|
8
12
|
private tools;
|
|
9
13
|
register(tool: ToolDefinition): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolRegistry.d.ts","sourceRoot":"","sources":["../../src/tools/ToolRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,EAGnB,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"ToolRegistry.d.ts","sourceRoot":"","sources":["../../src/tools/ToolRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,EAGnB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EAE1B,MAAM,gBAAgB,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,cAAc,KAAG,cAOvD,CAAC;AA4HH,eAAO,MAAM,qBAAqB,GAChC,OAAO,OAAO,EACd,QAAQ,oBAAoB,GAAG,SAAS,KACvC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAwBrC,CAAC;AAyLF,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAqC;IAElD,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAOpC,IAAI,IAAI,cAAc,EAAE;IAIxB,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC;IAQ/E;;;;OAIG;IACH,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,EAAE;IAOpD,2EAA2E;IAC3E,YAAY,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;IAaxE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAwC/F"}
|
|
@@ -79,6 +79,78 @@ const validateSchema = (schema, path = "$") => {
|
|
|
79
79
|
}
|
|
80
80
|
return undefined;
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Repairs arguments that are nearly right before validating them.
|
|
84
|
+
*
|
|
85
|
+
* A worker gets one batch of tool calls, so a rejected call is not retried — it
|
|
86
|
+
* takes the whole sub-task down. Measured against the live suite, small models
|
|
87
|
+
* lost calls to two trivial mistakes: naming a field `depth` when the schema
|
|
88
|
+
* says `maxDepth`, and sending `"2"` where a number was wanted. Both were
|
|
89
|
+
* reported as `tool_invalid_args` and the question came back unanswered.
|
|
90
|
+
*
|
|
91
|
+
* Two repairs, both safe:
|
|
92
|
+
*
|
|
93
|
+
* - **Unknown properties are dropped.** The handler would ignore them anyway;
|
|
94
|
+
* failing instead only converts a usable call into no call at all. The tool
|
|
95
|
+
* still runs with its documented defaults, which beats returning nothing.
|
|
96
|
+
* - **Scalar strings are coerced** to the declared type when they parse
|
|
97
|
+
* exactly. `"2"` becomes `2`; `"two"` stays a string and is still rejected.
|
|
98
|
+
*
|
|
99
|
+
* Nothing here weakens validation: a missing required argument, or a value that
|
|
100
|
+
* cannot be coerced, fails exactly as before.
|
|
101
|
+
*/
|
|
102
|
+
const coerceScalar = (value, schema) => {
|
|
103
|
+
if (typeof value !== "string")
|
|
104
|
+
return value;
|
|
105
|
+
const types = readSchemaTypes(schema);
|
|
106
|
+
if (!types || types.some((type) => type === "string"))
|
|
107
|
+
return value;
|
|
108
|
+
const trimmed = value.trim();
|
|
109
|
+
if (types.includes("number") || types.includes("integer")) {
|
|
110
|
+
const parsed = Number(trimmed);
|
|
111
|
+
if (trimmed !== "" && Number.isFinite(parsed)) {
|
|
112
|
+
if (types.includes("integer") && !Number.isInteger(parsed))
|
|
113
|
+
return value;
|
|
114
|
+
return parsed;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (types.includes("boolean")) {
|
|
118
|
+
if (trimmed.toLowerCase() === "true")
|
|
119
|
+
return true;
|
|
120
|
+
if (trimmed.toLowerCase() === "false")
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
};
|
|
125
|
+
export const sanitizeToolArguments = (value, schema) => {
|
|
126
|
+
const dropped = [];
|
|
127
|
+
const walk = (input, node, path) => {
|
|
128
|
+
if (!node)
|
|
129
|
+
return input;
|
|
130
|
+
if (Array.isArray(input)) {
|
|
131
|
+
return node.items ? input.map((entry, index) => walk(entry, node.items, `${path}[${index}]`)) : input;
|
|
132
|
+
}
|
|
133
|
+
if (!isObject(input))
|
|
134
|
+
return coerceScalar(input, node);
|
|
135
|
+
const properties = node.properties;
|
|
136
|
+
if (!properties)
|
|
137
|
+
return input;
|
|
138
|
+
const output = {};
|
|
139
|
+
for (const [key, entry] of Object.entries(input)) {
|
|
140
|
+
const propertySchema = properties[key];
|
|
141
|
+
if (!propertySchema) {
|
|
142
|
+
if (node.additionalProperties === true)
|
|
143
|
+
output[key] = entry;
|
|
144
|
+
else
|
|
145
|
+
dropped.push(path ? `${path}.${key}` : key);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
output[key] = walk(entry, propertySchema, path ? `${path}.${key}` : key);
|
|
149
|
+
}
|
|
150
|
+
return output;
|
|
151
|
+
};
|
|
152
|
+
return { value: walk(value, schema, ""), dropped };
|
|
153
|
+
};
|
|
82
154
|
const runtimeTypeOf = (value) => {
|
|
83
155
|
if (value === null)
|
|
84
156
|
return "null";
|
|
@@ -315,12 +387,17 @@ export class ToolRegistry {
|
|
|
315
387
|
if (schemaError) {
|
|
316
388
|
return { ok: false, output: "", error: schemaError };
|
|
317
389
|
}
|
|
318
|
-
const
|
|
390
|
+
const sanitized = tool.inputSchema
|
|
391
|
+
? sanitizeToolArguments(args, tool.inputSchema)
|
|
392
|
+
: { value: args, dropped: [] };
|
|
393
|
+
const validationError = tool.inputSchema
|
|
394
|
+
? validateValue(sanitized.value, tool.inputSchema)
|
|
395
|
+
: undefined;
|
|
319
396
|
if (validationError) {
|
|
320
397
|
return { ok: false, output: "", error: validationError };
|
|
321
398
|
}
|
|
322
399
|
try {
|
|
323
|
-
const result = await tool.handler(
|
|
400
|
+
const result = await tool.handler(sanitized.value, context);
|
|
324
401
|
return { ok: true, output: result.output, data: result.data };
|
|
325
402
|
}
|
|
326
403
|
catch (error) {
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { ToolDefinition } from "../ToolTypes.js";
|
|
2
2
|
import type { DocdexClient } from "../../docdex/DocdexClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* Rewrites a path into the repo-relative form docdex accepts.
|
|
5
|
+
*
|
|
6
|
+
* Docdex answers `invalid path` for a leading slash, a `./` prefix, or an
|
|
7
|
+
* absolute path, and a model asked to list a repository reaches for exactly
|
|
8
|
+
* those. That took out every "which files are in X" question: the tool was
|
|
9
|
+
* chosen correctly and then rejected on a formatting detail. Normalising here
|
|
10
|
+
* is the adapter's job — the caller meant the same directory either way.
|
|
11
|
+
*
|
|
12
|
+
* Returns undefined for "the whole repository", which is what docdex wants when
|
|
13
|
+
* no path is given.
|
|
14
|
+
*/
|
|
15
|
+
export declare const toRepoRelativePath: (value: string | undefined, workspaceRoot?: string) => string | undefined;
|
|
3
16
|
/**
|
|
4
17
|
* Judges whether search results actually answer the question.
|
|
5
18
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocdexTools.d.ts","sourceRoot":"","sources":["../../../src/tools/docdex/DocdexTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"DocdexTools.d.ts","sourceRoot":"","sources":["../../../src/tools/docdex/DocdexTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAuFjE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,GAAG,SAAS,EACzB,gBAAgB,MAAM,KACrB,MAAM,GAAG,SAWX,CAAC;AAoHF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEvB,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,YAAY,EACpB,UAAS,iBAAsB,KAC9B,cAAc,EAAgE,CAAC"}
|