@mcoda/mswarm 0.1.107 → 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/README.md +17 -0
- package/dist/codali-executor.d.ts.map +1 -1
- package/dist/codali-executor.js +17 -3
- package/dist/codali-executor.js.map +1 -1
- package/dist/runtime.d.ts +16 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +292 -36
- package/dist/runtime.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +4 -0
- package/dist/server.js.map +1 -1
- package/dist/vendor/codali/agents/AgentInventory.d.ts +56 -0
- package/dist/vendor/codali/agents/AgentInventory.d.ts.map +1 -0
- package/dist/vendor/codali/agents/AgentInventory.js +131 -0
- package/dist/vendor/codali/agents/RoleResolution.d.ts +49 -0
- package/dist/vendor/codali/agents/RoleResolution.d.ts.map +1 -0
- package/dist/vendor/codali/agents/RoleResolution.js +166 -0
- package/dist/vendor/codali/api/ChatCompletionsAdapter.d.ts +76 -0
- package/dist/vendor/codali/api/ChatCompletionsAdapter.d.ts.map +1 -0
- package/dist/vendor/codali/api/ChatCompletionsAdapter.js +54 -0
- package/dist/vendor/codali/api/CodaliApi.d.ts +78 -0
- package/dist/vendor/codali/api/CodaliApi.d.ts.map +1 -0
- package/dist/vendor/codali/api/CodaliApi.js +273 -0
- package/dist/vendor/codali/api/CodaliServer.d.ts +42 -0
- package/dist/vendor/codali/api/CodaliServer.d.ts.map +1 -0
- package/dist/vendor/codali/api/CodaliServer.js +118 -0
- package/dist/vendor/codali/cli/AskCommand.d.ts +66 -0
- package/dist/vendor/codali/cli/AskCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/AskCommand.js +536 -0
- package/dist/vendor/codali/cli/AuthCommand.d.ts +19 -0
- package/dist/vendor/codali/cli/AuthCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/AuthCommand.js +161 -0
- package/dist/vendor/codali/cli/ServeCommand.d.ts +5 -0
- package/dist/vendor/codali/cli/ServeCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/ServeCommand.js +87 -0
- package/dist/vendor/codali/cli/ToolsCommand.d.ts +22 -0
- package/dist/vendor/codali/cli/ToolsCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/ToolsCommand.js +208 -0
- package/dist/vendor/codali/cli.d.ts.map +1 -1
- package/dist/vendor/codali/cli.js +29 -1
- package/dist/vendor/codali/connectors/http/HttpToolDefinition.d.ts +74 -0
- package/dist/vendor/codali/connectors/http/HttpToolDefinition.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/http/HttpToolDefinition.js +190 -0
- package/dist/vendor/codali/connectors/http/HttpToolSource.d.ts +18 -0
- package/dist/vendor/codali/connectors/http/HttpToolSource.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/http/HttpToolSource.js +29 -0
- package/dist/vendor/codali/connectors/mcp/McpClient.d.ts +132 -0
- package/dist/vendor/codali/connectors/mcp/McpClient.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/mcp/McpClient.js +215 -0
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.d.ts +75 -0
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.js +180 -0
- package/dist/vendor/codali/connectors/mcp/McpToolAdapter.d.ts +48 -0
- package/dist/vendor/codali/connectors/mcp/McpToolAdapter.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/mcp/McpToolAdapter.js +101 -0
- package/dist/vendor/codali/connectors/mcp/McpToolSource.d.ts +27 -0
- package/dist/vendor/codali/connectors/mcp/McpToolSource.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/mcp/McpToolSource.js +33 -0
- package/dist/vendor/codali/connectors/media/ImageGenerationTool.d.ts +43 -0
- package/dist/vendor/codali/connectors/media/ImageGenerationTool.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/media/ImageGenerationTool.js +122 -0
- package/dist/vendor/codali/connectors/oauth/DeviceCodeAuth.d.ts +74 -0
- package/dist/vendor/codali/connectors/oauth/DeviceCodeAuth.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/oauth/DeviceCodeAuth.js +153 -0
- package/dist/vendor/codali/docdex/DocdexClient.d.ts +2 -0
- package/dist/vendor/codali/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/vendor/codali/docdex/DocdexClient.js +4 -0
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts +51 -3
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/CodaliGateway.js +226 -2
- package/dist/vendor/codali/gateway/CodaliGatewayTypes.d.ts +34 -0
- package/dist/vendor/codali/gateway/CodaliGatewayTypes.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/EvidenceNormalizer.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/EvidenceNormalizer.js +90 -1
- package/dist/vendor/codali/gateway/Finalizer.d.ts +50 -0
- package/dist/vendor/codali/gateway/Finalizer.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/Finalizer.js +79 -0
- package/dist/vendor/codali/gateway/GatewayPlanner.d.ts +13 -0
- package/dist/vendor/codali/gateway/GatewayPlanner.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/GatewayPlanner.js +81 -16
- package/dist/vendor/codali/gateway/GatewayStateMachine.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/GatewayStateMachine.js +6 -0
- package/dist/vendor/codali/gateway/GatewayTracer.d.ts +93 -0
- package/dist/vendor/codali/gateway/GatewayTracer.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/GatewayTracer.js +214 -0
- package/dist/vendor/codali/gateway/LocalGatewayProvider.d.ts +46 -0
- package/dist/vendor/codali/gateway/LocalGatewayProvider.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/LocalGatewayProvider.js +161 -0
- package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.d.ts +79 -0
- package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.js +354 -0
- package/dist/vendor/codali/gateway/RelevanceJudge.d.ts +14 -0
- package/dist/vendor/codali/gateway/RelevanceJudge.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/RelevanceJudge.js +87 -0
- package/dist/vendor/codali/gateway/ResponseSchema.d.ts +32 -0
- package/dist/vendor/codali/gateway/ResponseSchema.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/ResponseSchema.js +120 -0
- package/dist/vendor/codali/gateway/TemporalContext.d.ts +36 -0
- package/dist/vendor/codali/gateway/TemporalContext.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/TemporalContext.js +115 -0
- package/dist/vendor/codali/gateway/ToolCapabilityCompiler.d.ts +5 -0
- package/dist/vendor/codali/gateway/ToolCapabilityCompiler.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/ToolCapabilityCompiler.js +27 -2
- package/dist/vendor/codali/gateway/ToolDescriptorSource.d.ts +6 -0
- package/dist/vendor/codali/gateway/ToolDescriptorSource.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/ToolDescriptorSource.js +30 -0
- package/dist/vendor/codali/gateway/ToolExposure.d.ts +36 -0
- package/dist/vendor/codali/gateway/ToolExposure.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/ToolExposure.js +154 -0
- package/dist/vendor/codali/index.d.ts +57 -0
- package/dist/vendor/codali/index.d.ts.map +1 -1
- package/dist/vendor/codali/index.js +45 -0
- package/dist/vendor/codali/providers/ClaudeCliProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/ClaudeCliProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/ClaudeCliProvider.js +2 -0
- package/dist/vendor/codali/providers/CodexCliProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/CodexCliProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/CodexCliProvider.js +2 -0
- package/dist/vendor/codali/providers/MswarmWorkerProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/MswarmWorkerProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/MswarmWorkerProvider.js +2 -0
- package/dist/vendor/codali/providers/OllamaRemoteProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/OllamaRemoteProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/OllamaRemoteProvider.js +2 -0
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts +2 -0
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.js +2 -0
- package/dist/vendor/codali/providers/ProviderTypes.d.ts +13 -0
- package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/vendor/codali/providers/ProviderTypes.js +1 -1
- package/dist/vendor/codali/runcontext/CredentialFile.d.ts +12 -0
- package/dist/vendor/codali/runcontext/CredentialFile.d.ts.map +1 -0
- package/dist/vendor/codali/runcontext/CredentialFile.js +81 -0
- package/dist/vendor/codali/runcontext/RunContextResolver.d.ts +126 -0
- package/dist/vendor/codali/runcontext/RunContextResolver.d.ts.map +1 -0
- package/dist/vendor/codali/runcontext/RunContextResolver.js +389 -0
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.d.ts +1 -1
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -1
- package/dist/vendor/codali/storage/CodaliDatasetPrivacyEngine.js +12 -0
- package/dist/vendor/codali/tools/ToolRegistry.d.ts +14 -1
- package/dist/vendor/codali/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/vendor/codali/tools/ToolRegistry.js +38 -1
- package/dist/vendor/codali/tools/ToolTypes.d.ts +35 -0
- package/dist/vendor/codali/tools/ToolTypes.d.ts.map +1 -1
- package/dist/vendor/codali/tools/ToolTypes.js +21 -0
- package/dist/vendor/codali/tools/TruncateResult.d.ts +29 -0
- package/dist/vendor/codali/tools/TruncateResult.d.ts.map +1 -0
- package/dist/vendor/codali/tools/TruncateResult.js +91 -0
- package/dist/vendor/codali/tools/diff/DiffTool.d.ts.map +1 -1
- package/dist/vendor/codali/tools/diff/DiffTool.js +2 -0
- package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts +21 -1
- package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/vendor/codali/tools/docdex/DocdexTools.js +108 -16
- package/dist/vendor/codali/tools/filesystem/FileTools.d.ts.map +1 -1
- package/dist/vendor/codali/tools/filesystem/FileTools.js +8 -0
- package/dist/vendor/codali/tools/search/SearchTool.d.ts.map +1 -1
- package/dist/vendor/codali/tools/search/SearchTool.js +3 -1
- package/dist/vendor/codali/tools/shell/ShellTool.d.ts.map +1 -1
- package/dist/vendor/codali/tools/shell/ShellTool.js +2 -0
- package/package.json +3 -3
- package/scripts/stable-diffusion-cpp-auth-guard.py +475 -0
- package/scripts/stable-diffusion-cpp-openai-server.py +44 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { resolveCodaliGatewayAgentTiers, } from "../gateway/AgentTierResolver.js";
|
|
2
|
+
/** Internal roles each configurable role stands in for. */
|
|
3
|
+
const ROLE_EXPANSION = {
|
|
4
|
+
// Thinking stages: JSON in, JSON out, no tool calls. A small fast model is
|
|
5
|
+
// usually enough, and these run on every question.
|
|
6
|
+
orchestrator: [
|
|
7
|
+
"router",
|
|
8
|
+
"classifier",
|
|
9
|
+
"planner",
|
|
10
|
+
"query_expander",
|
|
11
|
+
"context_refiner",
|
|
12
|
+
"verifier",
|
|
13
|
+
"repair",
|
|
14
|
+
],
|
|
15
|
+
// Tool-calling stages. Accuracy here decides whether a run gathers anything:
|
|
16
|
+
// a weak model invents dates and empty ids. Split out so a bigger model can
|
|
17
|
+
// be put exactly here without slowing down planning.
|
|
18
|
+
worker: ["rag_worker", "tool_worker", "extractor"],
|
|
19
|
+
synthesizer: ["final_synthesizer"],
|
|
20
|
+
media: ["image_worker"],
|
|
21
|
+
};
|
|
22
|
+
/** Phase 1 resolves only these; media arrives with image generation. */
|
|
23
|
+
export const PHASE_ONE_ROLES = [
|
|
24
|
+
"orchestrator",
|
|
25
|
+
"worker",
|
|
26
|
+
"synthesizer",
|
|
27
|
+
];
|
|
28
|
+
/** Internal roles whose policies already demand tool support, best first. */
|
|
29
|
+
const TOOL_CAPABLE_PREFERENCE = ["tool_worker", "rag_worker"];
|
|
30
|
+
/**
|
|
31
|
+
* Adapters driven through a text CLI. They advertise `supportsTools: true`
|
|
32
|
+
* because the CLI itself has tools, but Codali's provider for them cannot
|
|
33
|
+
* surface a structured tool call — so choosing one as the tool worker yields a
|
|
34
|
+
* correct plan and zero tool calls.
|
|
35
|
+
*/
|
|
36
|
+
const NON_TOOL_CALLING_ADAPTERS = new Set(["claude-cli", "codex-cli", "gemini-cli"]);
|
|
37
|
+
const canEmitToolCalls = (assignment) => assignment?.candidate.supportsTools === true &&
|
|
38
|
+
!NON_TOOL_CALLING_ADAPTERS.has((assignment.candidate.adapter ?? "").toLowerCase());
|
|
39
|
+
const findToolCapableAssignment = (resolution, preferred) => {
|
|
40
|
+
if (canEmitToolCalls(preferred))
|
|
41
|
+
return preferred;
|
|
42
|
+
for (const role of TOOL_CAPABLE_PREFERENCE) {
|
|
43
|
+
const assignment = resolution.assignments[role];
|
|
44
|
+
if (canEmitToolCalls(assignment))
|
|
45
|
+
return assignment;
|
|
46
|
+
}
|
|
47
|
+
return Object.values(resolution.assignments).find((assignment) => canEmitToolCalls(assignment));
|
|
48
|
+
};
|
|
49
|
+
const findBySlug = (inventory, slug) => inventory.find((agent) => agent.slug === slug || agent.id === slug);
|
|
50
|
+
/**
|
|
51
|
+
* Builds the tier policy the resolver consumes. An explicitly bound slug is
|
|
52
|
+
* honoured for every internal role the configurable role stands in for, so
|
|
53
|
+
* binding `orchestrator` really does move planning, verification and repair
|
|
54
|
+
* onto that agent.
|
|
55
|
+
*/
|
|
56
|
+
const buildAgentPolicy = (bindings, inventory, warnings) => {
|
|
57
|
+
if (!bindings)
|
|
58
|
+
return undefined;
|
|
59
|
+
const roles = {};
|
|
60
|
+
let hasBinding = false;
|
|
61
|
+
for (const [role, slug] of Object.entries(bindings)) {
|
|
62
|
+
if (!slug)
|
|
63
|
+
continue;
|
|
64
|
+
const agent = findBySlug(inventory, slug);
|
|
65
|
+
if (!agent) {
|
|
66
|
+
warnings.push(`agent_binding_not_found:${role}:${slug}`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
hasBinding = true;
|
|
70
|
+
// Use the bound agent's *own* tier for the role. Imposing the role's
|
|
71
|
+
// nominal tier here silently discards the operator's choice: binding
|
|
72
|
+
// `orchestrator` to a large agent left it failing a "medium" requirement
|
|
73
|
+
// and falling back to whatever the resolver preferred, with the config
|
|
74
|
+
// appearing to have no effect.
|
|
75
|
+
const boundTier = (typeof agent.tier === "string" ? agent.tier : undefined) ??
|
|
76
|
+
(role === "synthesizer" ? "large" : role === "media" ? "image" : "medium");
|
|
77
|
+
for (const internalRole of ROLE_EXPANSION[role] ?? []) {
|
|
78
|
+
roles[internalRole] = {
|
|
79
|
+
tier: boundTier,
|
|
80
|
+
preferredRunnerKinds: undefined,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return hasBinding ? { resolver: "mcoda_inventory", roles } : undefined;
|
|
85
|
+
};
|
|
86
|
+
export const resolveConfigurableRoles = (input) => {
|
|
87
|
+
const warnings = [];
|
|
88
|
+
const roles = input.roles ?? PHASE_ONE_ROLES;
|
|
89
|
+
const internalRoles = roles.flatMap((role) => ROLE_EXPANSION[role] ?? []);
|
|
90
|
+
const agentPolicy = buildAgentPolicy(input.bindings, input.inventory, warnings);
|
|
91
|
+
const resolution = resolveCodaliGatewayAgentTiers({
|
|
92
|
+
inventory: input.inventory,
|
|
93
|
+
agentPolicy,
|
|
94
|
+
roles: internalRoles,
|
|
95
|
+
allowImageWorker: input.allowImageWorker ?? roles.includes("media"),
|
|
96
|
+
});
|
|
97
|
+
const assignments = {};
|
|
98
|
+
const bindingsOut = {};
|
|
99
|
+
for (const role of roles) {
|
|
100
|
+
// An explicit binding is the operator's decision and overrides tier
|
|
101
|
+
// matching outright. Routing it through the tier policy silently discarded
|
|
102
|
+
// the choice: mcoda's inventory carries no `tier` field — the resolver
|
|
103
|
+
// infers one — so a bound large agent failed a nominal "medium"
|
|
104
|
+
// requirement and the config appeared to do nothing.
|
|
105
|
+
const boundSlug = input.bindings?.[role];
|
|
106
|
+
if (boundSlug) {
|
|
107
|
+
const candidate = resolution.candidates.find((entry) => entry.slug === boundSlug || entry.id === boundSlug);
|
|
108
|
+
if (candidate) {
|
|
109
|
+
const existing = Object.values(resolution.assignments).find((assignment) => assignment.candidate.slug === candidate.slug);
|
|
110
|
+
assignments[role] = existing ?? {
|
|
111
|
+
role,
|
|
112
|
+
policy: { tier: candidate.tier },
|
|
113
|
+
candidate,
|
|
114
|
+
agent: {
|
|
115
|
+
slug: candidate.slug,
|
|
116
|
+
adapter: candidate.adapter ?? "",
|
|
117
|
+
model: candidate.model ?? "",
|
|
118
|
+
supportsTools: candidate.supportsTools,
|
|
119
|
+
contextWindow: candidate.contextWindow,
|
|
120
|
+
maxOutputTokens: candidate.maxOutputTokens,
|
|
121
|
+
},
|
|
122
|
+
score: 0,
|
|
123
|
+
reasons: ["explicit_binding"],
|
|
124
|
+
};
|
|
125
|
+
bindingsOut[role] = candidate.slug;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
warnings.push(`agent_binding_unavailable:${role}:${boundSlug}`);
|
|
129
|
+
}
|
|
130
|
+
// Otherwise the role is served by the first of its internal roles that
|
|
131
|
+
// resolved. They share a tier, so any is a valid stand-in.
|
|
132
|
+
for (const internalRole of ROLE_EXPANSION[role] ?? []) {
|
|
133
|
+
const assignment = resolution.assignments[internalRole];
|
|
134
|
+
if (assignment) {
|
|
135
|
+
assignments[role] = assignment;
|
|
136
|
+
bindingsOut[role] = assignment.candidate.slug;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (!assignments[role]) {
|
|
141
|
+
warnings.push(`agent_role_unresolved:${role}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// An internal role that failed to resolve is only worth reporting when the
|
|
145
|
+
// configurable role standing in for it also failed. Otherwise every run on a
|
|
146
|
+
// sparse inventory emits a wall of errors for roles that fell back correctly
|
|
147
|
+
// and never affected the answer.
|
|
148
|
+
const servedInternalRoles = new Set(roles
|
|
149
|
+
.filter((role) => assignments[role])
|
|
150
|
+
.flatMap((role) => ROLE_EXPANSION[role] ?? []));
|
|
151
|
+
for (const error of resolution.errors) {
|
|
152
|
+
if (error.role && servedInternalRoles.has(error.role))
|
|
153
|
+
continue;
|
|
154
|
+
warnings.push(`agent_resolution_error:${error.code}${error.role ? `:${error.role}` : ""}`);
|
|
155
|
+
}
|
|
156
|
+
const toolCapable = findToolCapableAssignment(resolution,
|
|
157
|
+
// An explicitly bound worker wins; otherwise fall back to the orchestrator.
|
|
158
|
+
assignments.worker ?? assignments.orchestrator);
|
|
159
|
+
if (!toolCapable) {
|
|
160
|
+
warnings.push("no_tool_capable_agent");
|
|
161
|
+
}
|
|
162
|
+
else if (toolCapable !== assignments.orchestrator) {
|
|
163
|
+
warnings.push(`tool_worker_substituted:${toolCapable.candidate.slug}`);
|
|
164
|
+
}
|
|
165
|
+
return { resolution, assignments, bindings: bindingsOut, toolCapable, warnings };
|
|
166
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { runCodali, type CodaliMessage, type CodaliRequest, type CodaliResult } from "./CodaliApi.js";
|
|
2
|
+
import type { RunContext } from "../runcontext/RunContextResolver.js";
|
|
3
|
+
/**
|
|
4
|
+
* OpenAI-compatible chat-completions adapter.
|
|
5
|
+
*
|
|
6
|
+
* okacam AI chat already speaks this shape. Rather than changing AI chat to
|
|
7
|
+
* learn Codali's contract, Codali speaks AI chat's — the product points its
|
|
8
|
+
* existing client at a different base URL and gets orchestration, tools, and
|
|
9
|
+
* citations without a rewrite.
|
|
10
|
+
*
|
|
11
|
+
* The adapter is deliberately thin. It maps message shapes and nothing else;
|
|
12
|
+
* all routing, tool selection and budget enforcement stay in the one code path
|
|
13
|
+
* `runCodali` provides, so the adapter cannot drift from the CLI's behaviour.
|
|
14
|
+
*/
|
|
15
|
+
export interface ChatCompletionMessage {
|
|
16
|
+
role: string;
|
|
17
|
+
content: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface ChatCompletionRequest {
|
|
20
|
+
model?: string;
|
|
21
|
+
messages: ChatCompletionMessage[];
|
|
22
|
+
stream?: boolean;
|
|
23
|
+
response_format?: {
|
|
24
|
+
type?: string;
|
|
25
|
+
json_schema?: {
|
|
26
|
+
schema?: Record<string, unknown>;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
/** Codali-specific extras, ignored by a plain OpenAI client. */
|
|
30
|
+
codali?: {
|
|
31
|
+
runContext?: RunContext;
|
|
32
|
+
workspaceRoot?: string;
|
|
33
|
+
mode?: CodaliRequest["mode"];
|
|
34
|
+
media?: CodaliRequest["media"];
|
|
35
|
+
budgets?: CodaliRequest["budgets"];
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export interface ChatCompletionResponse {
|
|
39
|
+
id: string;
|
|
40
|
+
object: "chat.completion";
|
|
41
|
+
created: number;
|
|
42
|
+
model: string;
|
|
43
|
+
choices: Array<{
|
|
44
|
+
index: number;
|
|
45
|
+
message: {
|
|
46
|
+
role: "assistant";
|
|
47
|
+
content: string;
|
|
48
|
+
};
|
|
49
|
+
finish_reason: "stop" | "length" | "content_filter";
|
|
50
|
+
}>;
|
|
51
|
+
usage?: {
|
|
52
|
+
prompt_tokens: number;
|
|
53
|
+
completion_tokens: number;
|
|
54
|
+
total_tokens: number;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Codali's provenance, carried alongside the OpenAI shape. A plain client
|
|
58
|
+
* ignores this; a product that wants citations reads it. Without it the
|
|
59
|
+
* adapter would throw away the very thing that makes an orchestrated answer
|
|
60
|
+
* trustworthy.
|
|
61
|
+
*/
|
|
62
|
+
codali: {
|
|
63
|
+
status: CodaliResult["status"];
|
|
64
|
+
sources: CodaliResult["sources"];
|
|
65
|
+
artifacts: CodaliResult["artifacts"];
|
|
66
|
+
warnings: string[];
|
|
67
|
+
traceId: string;
|
|
68
|
+
output?: unknown;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export declare const toCodaliMessages: (messages: readonly ChatCompletionMessage[]) => CodaliMessage[];
|
|
72
|
+
export interface ChatAdapterDependencies {
|
|
73
|
+
run?: typeof runCodali;
|
|
74
|
+
}
|
|
75
|
+
export declare const handleChatCompletion: (request: ChatCompletionRequest, deps?: ChatAdapterDependencies) => Promise<ChatCompletionResponse>;
|
|
76
|
+
//# sourceMappingURL=ChatCompletionsAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatCompletionsAdapter.d.ts","sourceRoot":"","sources":["../../src/api/ChatCompletionsAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACtG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAEtE;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,qBAAqB,EAAE,CAAC;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAA;KAAE,CAAC;IACxF,gEAAgE;IAChE,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,UAAU,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAC7B,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAC/B,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;KACpC,CAAC;CACH;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE;YAAE,IAAI,EAAE,WAAW,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAChD,aAAa,EAAE,MAAM,GAAG,QAAQ,GAAG,gBAAgB,CAAC;KACrD,CAAC,CAAC;IACH,KAAK,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACnF;;;;;OAKG;IACH,MAAM,EAAE;QACN,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC/B,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;QACjC,SAAS,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;QACrC,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;CACH;AAID,eAAO,MAAM,gBAAgB,GAC3B,UAAU,SAAS,qBAAqB,EAAE,KACzC,aAAa,EAMT,CAAC;AAWR,MAAM,WAAW,uBAAuB;IACtC,GAAG,CAAC,EAAE,OAAO,SAAS,CAAC;CACxB;AAED,eAAO,MAAM,oBAAoB,GAC/B,SAAS,qBAAqB,EAC9B,OAAM,uBAA4B,KACjC,OAAO,CAAC,sBAAsB,CAsChC,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { runCodali } from "./CodaliApi.js";
|
|
2
|
+
const CHAT_ROLES = new Set(["system", "user", "assistant"]);
|
|
3
|
+
export const toCodaliMessages = (messages) => messages
|
|
4
|
+
.filter((message) => CHAT_ROLES.has(message.role) && typeof message.content === "string")
|
|
5
|
+
.map((message) => ({
|
|
6
|
+
role: message.role,
|
|
7
|
+
content: message.content,
|
|
8
|
+
}));
|
|
9
|
+
const readResponseSchema = (request) => {
|
|
10
|
+
const format = request.response_format;
|
|
11
|
+
if (!format)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (format.type === "json_schema")
|
|
14
|
+
return format.json_schema?.schema;
|
|
15
|
+
return undefined;
|
|
16
|
+
};
|
|
17
|
+
export const handleChatCompletion = async (request, deps = {}) => {
|
|
18
|
+
const run = deps.run ?? runCodali;
|
|
19
|
+
const result = await run({
|
|
20
|
+
messages: toCodaliMessages(request.messages),
|
|
21
|
+
runContext: request.codali?.runContext,
|
|
22
|
+
workspaceRoot: request.codali?.workspaceRoot,
|
|
23
|
+
mode: request.codali?.mode,
|
|
24
|
+
media: request.codali?.media,
|
|
25
|
+
budgets: request.codali?.budgets,
|
|
26
|
+
responseSchema: readResponseSchema(request),
|
|
27
|
+
responseMode: request.response_format?.type === "json_object" ? "json" : "text",
|
|
28
|
+
product: { name: "chat-completions" },
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
id: `chatcmpl-${result.traceId}`,
|
|
32
|
+
object: "chat.completion",
|
|
33
|
+
created: Math.floor(Date.now() / 1000),
|
|
34
|
+
model: request.model ?? "codali",
|
|
35
|
+
choices: [
|
|
36
|
+
{
|
|
37
|
+
index: 0,
|
|
38
|
+
message: { role: "assistant", content: result.answer },
|
|
39
|
+
// A partial or clarification-seeking answer is not a normal stop. A
|
|
40
|
+
// client that only inspects finish_reason must not mistake it for a
|
|
41
|
+
// complete response.
|
|
42
|
+
finish_reason: result.status === "succeeded" ? "stop" : "length",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
codali: {
|
|
46
|
+
status: result.status,
|
|
47
|
+
sources: result.sources,
|
|
48
|
+
artifacts: result.artifacts,
|
|
49
|
+
warnings: result.warnings,
|
|
50
|
+
traceId: result.traceId,
|
|
51
|
+
output: result.output,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { attachMcpTools } from "../connectors/mcp/McpToolSource.js";
|
|
2
|
+
import { type ImageGenerationConfig } from "../connectors/media/ImageGenerationTool.js";
|
|
3
|
+
import { createProviderForAssignment } from "../gateway/LocalGatewayProvider.js";
|
|
4
|
+
import { getAgentInventory } from "../agents/AgentInventory.js";
|
|
5
|
+
import { ToolRegistry } from "../tools/ToolRegistry.js";
|
|
6
|
+
import type { RunContext } from "../runcontext/RunContextResolver.js";
|
|
7
|
+
import type { CodaliArtifactRef, CodaliGatewayMode, CodaliGatewaySource, CodaliGatewayStatus } from "../gateway/CodaliGatewayTypes.js";
|
|
8
|
+
/**
|
|
9
|
+
* The canonical Codali API.
|
|
10
|
+
*
|
|
11
|
+
* This is the contract products integrate against — okacam AI chat, line items,
|
|
12
|
+
* badges, employee daily-log review. One entry point, one result shape, the
|
|
13
|
+
* same behaviour the CLI gets, so a product never has to reimplement routing,
|
|
14
|
+
* tool selection, evidence handling, or budget enforcement.
|
|
15
|
+
*
|
|
16
|
+
* Everything a run is allowed to touch arrives on the request as `runContext`.
|
|
17
|
+
* Codali does not call back into the host to fetch tenant configuration: the
|
|
18
|
+
* authenticated host already holds it, and a callback would make every request
|
|
19
|
+
* a circular distributed dependency.
|
|
20
|
+
*/
|
|
21
|
+
export interface CodaliMessage {
|
|
22
|
+
role: "system" | "user" | "assistant";
|
|
23
|
+
content: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CodaliRequest {
|
|
26
|
+
messages: CodaliMessage[];
|
|
27
|
+
/** Tenant scope, tools, credentials, agent bindings, limits. */
|
|
28
|
+
runContext?: RunContext;
|
|
29
|
+
/** JSON Schema the answer must satisfy. Enforced, with one repair attempt. */
|
|
30
|
+
responseSchema?: Record<string, unknown>;
|
|
31
|
+
responseMode?: "text" | "json" | "artifact";
|
|
32
|
+
mode?: CodaliGatewayMode;
|
|
33
|
+
/** Workspace the run resolves paths against. Defaults to `process.cwd()`. */
|
|
34
|
+
workspaceRoot?: string;
|
|
35
|
+
requestId?: string;
|
|
36
|
+
product?: {
|
|
37
|
+
name?: string;
|
|
38
|
+
version?: string;
|
|
39
|
+
surface?: string;
|
|
40
|
+
};
|
|
41
|
+
/** Image generation config; absent means the media tool is not offered. */
|
|
42
|
+
media?: ImageGenerationConfig;
|
|
43
|
+
budgets?: {
|
|
44
|
+
maxRounds?: number;
|
|
45
|
+
maxToolCalls?: number;
|
|
46
|
+
maxModelCalls?: number;
|
|
47
|
+
deadlineMs?: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface CodaliSourceRef extends CodaliGatewaySource {
|
|
51
|
+
}
|
|
52
|
+
export interface CodaliResult {
|
|
53
|
+
status: CodaliGatewayStatus;
|
|
54
|
+
/** Text answer, or the clarifying question when status is needs_clarification. */
|
|
55
|
+
answer: string;
|
|
56
|
+
/** Schema-conformant structured output when a responseSchema was supplied. */
|
|
57
|
+
output: unknown;
|
|
58
|
+
sources: CodaliSourceRef[];
|
|
59
|
+
artifacts: CodaliArtifactRef[];
|
|
60
|
+
warnings: string[];
|
|
61
|
+
traceId: string;
|
|
62
|
+
}
|
|
63
|
+
/** Flattens a message list into the single query the gateway plans against. */
|
|
64
|
+
export declare const messagesToQuery: (messages: readonly CodaliMessage[]) => string;
|
|
65
|
+
export interface CodaliRunDependencies {
|
|
66
|
+
loadInventory?: typeof getAgentInventory;
|
|
67
|
+
resolveRunContext?: (workspaceRoot: string) => Promise<RunContext>;
|
|
68
|
+
buildRegistry?: (workspaceRoot: string, context: RunContext) => ToolRegistry;
|
|
69
|
+
createProvider?: typeof createProviderForAssignment;
|
|
70
|
+
attachMcp?: typeof attachMcpTools;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Runs one Codali request end to end.
|
|
74
|
+
*
|
|
75
|
+
* The same code path the CLI uses, so a product and the terminal cannot drift.
|
|
76
|
+
*/
|
|
77
|
+
export declare const runCodali: (request: CodaliRequest, deps?: CodaliRunDependencies) => Promise<CodaliResult>;
|
|
78
|
+
//# sourceMappingURL=CodaliApi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodaliApi.d.ts","sourceRoot":"","sources":["../../src/api/CodaliApi.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,4CAA4C,CAAC;AAIpD,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAGjF,OAAO,EAAE,iBAAiB,EAAyB,MAAM,6BAA6B,CAAC;AAGvF,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAEjB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAE1C;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,gEAAgE;IAChE,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;IAC5C,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,2EAA2E;IAC3E,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAC9B,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,eAAgB,SAAQ,mBAAmB;CAAG;AAE/D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAsBD,+EAA+E;AAC/E,eAAO,MAAM,eAAe,GAAI,UAAU,SAAS,aAAa,EAAE,KAAG,MAWpE,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACzC,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,YAAY,CAAC;IAC7E,cAAc,CAAC,EAAE,OAAO,2BAA2B,CAAC;IACpD,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,GACpB,SAAS,aAAa,EACtB,OAAM,qBAA0B,KAC/B,OAAO,CAAC,YAAY,CAkOtB,CAAC"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { attachHttpTools } from "../connectors/http/HttpToolSource.js";
|
|
3
|
+
import { attachMcpTools } from "../connectors/mcp/McpToolSource.js";
|
|
4
|
+
import { createImageGenerationTool, } from "../connectors/media/ImageGenerationTool.js";
|
|
5
|
+
import { DocdexClient } from "../docdex/DocdexClient.js";
|
|
6
|
+
import { createCodaliGateway } from "../gateway/CodaliGateway.js";
|
|
7
|
+
import { createLocalGatewayTaskRunner } from "../gateway/LocalGatewayTaskRunner.js";
|
|
8
|
+
import { createProviderForAssignment } from "../gateway/LocalGatewayProvider.js";
|
|
9
|
+
import { gatewayToolDescriptorsFromRegistry } from "../gateway/ToolDescriptorSource.js";
|
|
10
|
+
import { GatewayTracer } from "../gateway/GatewayTracer.js";
|
|
11
|
+
import { getAgentInventory, filterLocallyDrivable } from "../agents/AgentInventory.js";
|
|
12
|
+
import { resolveConfigurableRoles } from "../agents/RoleResolution.js";
|
|
13
|
+
import { providerSupportsToolCalls } from "../providers/ProviderTypes.js";
|
|
14
|
+
import { ToolRegistry } from "../tools/ToolRegistry.js";
|
|
15
|
+
import { createDocdexTools } from "../tools/docdex/DocdexTools.js";
|
|
16
|
+
import { LocalConfigRunContextResolver } from "../runcontext/RunContextResolver.js";
|
|
17
|
+
const DEFAULTS = {
|
|
18
|
+
maxRounds: 3,
|
|
19
|
+
maxToolCalls: 20,
|
|
20
|
+
maxModelCalls: 12,
|
|
21
|
+
deadlineMs: 600000,
|
|
22
|
+
maxParallelWorkers: 3,
|
|
23
|
+
};
|
|
24
|
+
const DEFAULT_DOCDEX_TOOLS = [
|
|
25
|
+
"docdex_search",
|
|
26
|
+
"docdex_open",
|
|
27
|
+
"docdex_symbols",
|
|
28
|
+
"docdex_tree",
|
|
29
|
+
"docdex_files",
|
|
30
|
+
"docdex_impact_graph",
|
|
31
|
+
"docdex_batch_search",
|
|
32
|
+
"docdex_web_research",
|
|
33
|
+
"docdex_stats",
|
|
34
|
+
];
|
|
35
|
+
/** Flattens a message list into the single query the gateway plans against. */
|
|
36
|
+
export const messagesToQuery = (messages) => {
|
|
37
|
+
const lastUser = [...messages].reverse().find((message) => message.role === "user");
|
|
38
|
+
if (!lastUser)
|
|
39
|
+
return "";
|
|
40
|
+
const priorContext = messages
|
|
41
|
+
.slice(0, messages.indexOf(lastUser))
|
|
42
|
+
.filter((message) => message.role !== "system")
|
|
43
|
+
.slice(-4)
|
|
44
|
+
.map((message) => `${message.role}: ${message.content}`);
|
|
45
|
+
return priorContext.length > 0
|
|
46
|
+
? `${priorContext.join("\n")}\n\nCurrent question: ${lastUser.content}`
|
|
47
|
+
: lastUser.content;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Runs one Codali request end to end.
|
|
51
|
+
*
|
|
52
|
+
* The same code path the CLI uses, so a product and the terminal cannot drift.
|
|
53
|
+
*/
|
|
54
|
+
export const runCodali = async (request, deps = {}) => {
|
|
55
|
+
const workspaceRoot = request.workspaceRoot ?? process.cwd();
|
|
56
|
+
const query = messagesToQuery(request.messages);
|
|
57
|
+
const tracer = new GatewayTracer(query);
|
|
58
|
+
if (!query.trim()) {
|
|
59
|
+
return {
|
|
60
|
+
status: "failed",
|
|
61
|
+
answer: "The request contained no user message.",
|
|
62
|
+
output: undefined,
|
|
63
|
+
sources: [],
|
|
64
|
+
artifacts: [],
|
|
65
|
+
warnings: ["empty_request"],
|
|
66
|
+
traceId: request.requestId ?? randomUUID(),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Host-supplied context wins; otherwise fall back to local trusted config.
|
|
70
|
+
const context = request.runContext ??
|
|
71
|
+
(deps.resolveRunContext
|
|
72
|
+
? await deps.resolveRunContext(workspaceRoot)
|
|
73
|
+
: await new LocalConfigRunContextResolver().resolve({ workspaceRoot }));
|
|
74
|
+
for (const warning of context.warnings ?? [])
|
|
75
|
+
tracer.addWarning(warning);
|
|
76
|
+
const inventory = await (deps.loadInventory ?? getAgentInventory)();
|
|
77
|
+
const drivable = filterLocallyDrivable(inventory.agents);
|
|
78
|
+
const roles = resolveConfigurableRoles({
|
|
79
|
+
inventory: drivable,
|
|
80
|
+
bindings: context.agentRoles,
|
|
81
|
+
roles: request.media ? ["orchestrator", "synthesizer", "media"] : undefined,
|
|
82
|
+
});
|
|
83
|
+
for (const warning of roles.warnings)
|
|
84
|
+
tracer.addWarning(warning);
|
|
85
|
+
const orchestrator = roles.assignments.orchestrator;
|
|
86
|
+
const synthesizer = roles.assignments.synthesizer;
|
|
87
|
+
if (!orchestrator && !synthesizer) {
|
|
88
|
+
return {
|
|
89
|
+
status: "failed",
|
|
90
|
+
answer: "No usable model agent could be resolved for this run.",
|
|
91
|
+
output: undefined,
|
|
92
|
+
sources: [],
|
|
93
|
+
artifacts: [],
|
|
94
|
+
warnings: [...tracer.snapshot().warnings, "no_agents_resolved"],
|
|
95
|
+
traceId: request.requestId ?? randomUUID(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const makeProvider = deps.createProvider ?? createProviderForAssignment;
|
|
99
|
+
const primary = makeProvider((synthesizer ?? orchestrator));
|
|
100
|
+
const orchestratorProvider = orchestrator ? makeProvider(orchestrator) : undefined;
|
|
101
|
+
const workerCandidates = [roles.toolCapable, orchestrator, synthesizer].filter(Boolean);
|
|
102
|
+
let worker = primary;
|
|
103
|
+
for (const candidate of workerCandidates) {
|
|
104
|
+
const provider = makeProvider(candidate);
|
|
105
|
+
if (providerSupportsToolCalls(provider)) {
|
|
106
|
+
worker = provider;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const toolsExecutable = providerSupportsToolCalls(worker);
|
|
111
|
+
// ---- Tools ------------------------------------------------------------
|
|
112
|
+
const registry = deps.buildRegistry?.(workspaceRoot, context) ?? buildDefaultRegistry(workspaceRoot, context);
|
|
113
|
+
const mcp = await (deps.attachMcp ?? attachMcpTools)({ context, toolRegistry: registry });
|
|
114
|
+
for (const warning of mcp.warnings)
|
|
115
|
+
tracer.addWarning(warning);
|
|
116
|
+
const http = attachHttpTools({ context, toolRegistry: registry });
|
|
117
|
+
for (const warning of http.warnings)
|
|
118
|
+
tracer.addWarning(warning);
|
|
119
|
+
const artifacts = [];
|
|
120
|
+
const runId = request.requestId ?? `codali-${randomUUID()}`;
|
|
121
|
+
if (request.media) {
|
|
122
|
+
registry.register(createImageGenerationTool({
|
|
123
|
+
config: request.media,
|
|
124
|
+
workspaceRoot,
|
|
125
|
+
runId,
|
|
126
|
+
onArtifact: (artifact) => artifacts.push(artifact),
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
const registered = registry.list().map((tool) => tool.name);
|
|
130
|
+
const readOnlyMcpTools = registry
|
|
131
|
+
.catalog(mcp.registered)
|
|
132
|
+
.filter((descriptor) => descriptor.readOnly)
|
|
133
|
+
.map((descriptor) => descriptor.name);
|
|
134
|
+
const defaultAllowed = [
|
|
135
|
+
...DEFAULT_DOCDEX_TOOLS,
|
|
136
|
+
...readOnlyMcpTools,
|
|
137
|
+
...http.registered,
|
|
138
|
+
...(request.media ? ["media_generate_image"] : []),
|
|
139
|
+
];
|
|
140
|
+
const allowedTools = toolsExecutable
|
|
141
|
+
? (context.allowedTools ?? defaultAllowed).filter((tool) => registered.includes(tool))
|
|
142
|
+
: [];
|
|
143
|
+
// Every allowed tool must be declared to the capability compiler, which
|
|
144
|
+
// otherwise discards it as `not_declared` — including docdex tools outside
|
|
145
|
+
// its hardcoded backing-tool set, such as `docdex_web_research`. That
|
|
146
|
+
// omission kept the "web" capability off the classifier's list entirely, so
|
|
147
|
+
// no question could ever reach the public web.
|
|
148
|
+
const connectorTools = allowedTools;
|
|
149
|
+
// ---- Budgets ----------------------------------------------------------
|
|
150
|
+
const min = (a, b) => (b === undefined ? a : Math.min(a, b));
|
|
151
|
+
const maxRounds = min(request.budgets?.maxRounds ?? DEFAULTS.maxRounds, context.limits?.maxRounds);
|
|
152
|
+
const maxToolCalls = min(request.budgets?.maxToolCalls ?? DEFAULTS.maxToolCalls, context.limits?.maxToolCalls);
|
|
153
|
+
const deadlineMs = min(request.budgets?.deadlineMs ?? DEFAULTS.deadlineMs, context.limits?.deadlineMs);
|
|
154
|
+
const gatewayRequest = {
|
|
155
|
+
id: runId,
|
|
156
|
+
query,
|
|
157
|
+
mode: request.mode ?? "balanced",
|
|
158
|
+
product: request.product ?? { name: "codali-api" },
|
|
159
|
+
tenant: context.tenant,
|
|
160
|
+
tools: connectorTools.length > 0
|
|
161
|
+
? { actualTools: connectorTools, readOnlyTools: connectorTools }
|
|
162
|
+
: undefined,
|
|
163
|
+
docdex: {
|
|
164
|
+
enabled: true,
|
|
165
|
+
baseUrl: context.docdex?.baseUrl,
|
|
166
|
+
repoRoot: context.repo?.root ?? workspaceRoot,
|
|
167
|
+
repoId: context.docdex?.repoId,
|
|
168
|
+
},
|
|
169
|
+
policy: {
|
|
170
|
+
allowedTools,
|
|
171
|
+
deniedTools: context.deniedTools,
|
|
172
|
+
maxIterations: maxRounds,
|
|
173
|
+
maxRuntimeMs: deadlineMs,
|
|
174
|
+
maxToolCalls,
|
|
175
|
+
maxModelCalls: request.budgets?.maxModelCalls ?? DEFAULTS.maxModelCalls,
|
|
176
|
+
maxEvidenceItems: 40,
|
|
177
|
+
maxImageArtifacts: request.media ? 4 : 0,
|
|
178
|
+
maxContextPackTokens: 16000,
|
|
179
|
+
allowWrites: false,
|
|
180
|
+
allowShell: false,
|
|
181
|
+
allowDestructiveOperations: false,
|
|
182
|
+
allowOutsideWorkspace: false,
|
|
183
|
+
requireFinalLargeModel: false,
|
|
184
|
+
allowDegradedFinalAnswer: true,
|
|
185
|
+
allowImageWorker: Boolean(request.media),
|
|
186
|
+
},
|
|
187
|
+
response: {
|
|
188
|
+
format: request.responseSchema ? "json" : (request.responseMode === "json" ? "json" : "text"),
|
|
189
|
+
schema: request.responseSchema,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
const gateway = createCodaliGateway({
|
|
193
|
+
provider: primary,
|
|
194
|
+
// Classify and plan on the orchestrator model; only the final answer pays
|
|
195
|
+
// for the large one.
|
|
196
|
+
plannerProvider: orchestratorProvider ?? primary,
|
|
197
|
+
// So the trace names the model that actually ran.
|
|
198
|
+
finalAgent: synthesizer ?? orchestrator,
|
|
199
|
+
agentInventory: drivable,
|
|
200
|
+
toolDescriptors: gatewayToolDescriptorsFromRegistry(registry, allowedTools),
|
|
201
|
+
taskRunner: createLocalGatewayTaskRunner({
|
|
202
|
+
provider: worker,
|
|
203
|
+
registry,
|
|
204
|
+
toolContext: {
|
|
205
|
+
workspaceRoot,
|
|
206
|
+
allowShell: false,
|
|
207
|
+
allowDestructiveOperations: false,
|
|
208
|
+
allowOutsideWorkspace: false,
|
|
209
|
+
},
|
|
210
|
+
onEvent: (event) => {
|
|
211
|
+
if (event.type === "tool_call") {
|
|
212
|
+
tracer.recordToolCall({ taskId: event.taskId, tool: event.tool, args: event.args });
|
|
213
|
+
}
|
|
214
|
+
else if (event.type === "tool_result") {
|
|
215
|
+
tracer.recordToolResult({
|
|
216
|
+
taskId: event.taskId,
|
|
217
|
+
tool: event.tool,
|
|
218
|
+
ok: event.ok,
|
|
219
|
+
latencyMs: event.latencyMs,
|
|
220
|
+
errorCode: event.errorCode,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
}),
|
|
225
|
+
workerOptions: {
|
|
226
|
+
maxRuntimeMs: deadlineMs,
|
|
227
|
+
maxToolCalls,
|
|
228
|
+
perTaskTimeoutMs: Math.max(60000, Math.floor(deadlineMs / 2)),
|
|
229
|
+
maxParallelWorkers: DEFAULTS.maxParallelWorkers,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
try {
|
|
233
|
+
const result = await gateway.run(gatewayRequest);
|
|
234
|
+
return {
|
|
235
|
+
status: result.status,
|
|
236
|
+
answer: result.answer,
|
|
237
|
+
output: result.output ?? result.answer,
|
|
238
|
+
sources: result.sources,
|
|
239
|
+
// Artifacts collected by the media tool are authoritative; the gateway's
|
|
240
|
+
// own list covers anything a worker persisted directly.
|
|
241
|
+
artifacts: artifacts.length > 0 ? artifacts : result.artifacts,
|
|
242
|
+
warnings: [...tracer.snapshot().warnings, ...result.warnings],
|
|
243
|
+
traceId: result.runId,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
return {
|
|
248
|
+
status: "failed",
|
|
249
|
+
answer: `Codali run failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
250
|
+
output: undefined,
|
|
251
|
+
sources: [],
|
|
252
|
+
artifacts,
|
|
253
|
+
warnings: tracer.snapshot().warnings,
|
|
254
|
+
traceId: runId,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
await mcp.registry?.close();
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
const buildDefaultRegistry = (workspaceRoot, context) => {
|
|
262
|
+
const registry = new ToolRegistry();
|
|
263
|
+
const client = new DocdexClient({
|
|
264
|
+
baseUrl: context.docdex?.baseUrl ?? "http://127.0.0.1:28491",
|
|
265
|
+
repoRoot: context.repo?.root ?? workspaceRoot,
|
|
266
|
+
repoId: context.docdex?.repoId,
|
|
267
|
+
apiKey: context.docdex?.apiKey,
|
|
268
|
+
allowedOperations: context.docdex?.allowedOperations,
|
|
269
|
+
});
|
|
270
|
+
for (const tool of createDocdexTools(client))
|
|
271
|
+
registry.register(tool);
|
|
272
|
+
return registry;
|
|
273
|
+
};
|