Repository: vscode
Issue: Tools API & prompt-tsx: `tokenOptions` and `PromptSizing` not really paired
Labels: api,panel-chat

Here's the typing for `prompt-tsx`'s `PromptSizing`:
```ts
/**
 * The sizing hint for the prompt element. Prompt elements should take this into account when rendering.
 */
export interface PromptSizing {
    /**
     * The computed token allocation for this prompt element to adhere to when rendering,
     * if it specified {@link BasePromptElementProps.flexBasis}.
     */
    readonly tokenBudget: number;
    /**
     * Metadata about the endpoint being used.
     */
    readonly endpoint: IChatEndpointInfo;
    /**
     * Counts the number of tokens the text consumes.
     */
    countTokens(text: string, token?: CancellationToken): Promise<number> | number;
}
```

And here's `LanguageModelToolInvocationOptions`'s `tokenOptions` property:
```ts
/**
 * Options to hint at how many tokens the tool should return in its response.
 */
tokenOptions?: {
	/**
	 * If known, the maximum number of tokens the tool should emit in its result.
	 */
	tokenBudget: number;

	/**
	 * Count the number of tokens in a message using the model specific tokenizer-logic.
	 * @param text A string.
	 * @param token Optional cancellation token.  See {@link CancellationTokenSource} for how to create one.
	 * @returns A thenable that resolves to the number of tokens.
	 */
	countTokens(text: string, token?: CancellationToken): Thenable<number>;
};
```

You can see in my `ToolCall`, that I want to really just pass `sizing` into `invokeTool` as the `tokenOptions`:
```ts
export class ToolCall extends PromptElement<ToolCallProps, void> {
    async render(state: void, sizing: PromptSizing) {
        const contentType = agentSupportedContentTypes.find(type => this.props.tool.supportedContentTypes.includes(type));
        if (!contentType) {
            console.error(l10n.t('Tool does not support any of the agent\'s content types: {0}', this.props.tool.name));
            return <ToolMessage toolCallId={this.props.toolCall.toolCallId}>Tool unsupported</ToolMessage>;
        }

        const tokenOptions: LanguageModelToolInvocationOptions<unknown>['tokenOptions'] = {
            tokenBudget: sizing.tokenBudget,
            countTokens: async (content: string) => sizing.countTokens(content),
        };

        const result = await lm.invokeTool(
            this.props.toolCall.name,
            {
                parameters: this.props.toolCall.parameters,
                requestedContentTypes: [contentType],
                toolInvocationToken: this.props.toolInvocationToken,
                tokenOptions
            },
            dummyCancellationToken
        );
        return <>
            <ToolMessage toolCallId={this.props.toolCall.toolCallId}>
                {contentType === 'text/plain' ?
                    result[contentType] :
                    <elementJSON data={result[contentType]}></elementJSON>}
            </ToolMessage>
        </>;
    }
}
```
But I can't because:
```
Type 'PromptSizing' is not assignable to type '{ tokenBudget: number; countTokens(text: string, token?: CancellationToken | undefined): Thenable<number>; }'.
  The types returned by 'countTokens(...)' are incompatible between these types.
    Type 'number | Promise<number>' is not assignable to type 'Thenable<number>'.
      Type 'number' is not assignable to type 'Thenable<number>'.ts(2322)
```
They're just slightly different.

Can these be aligned so that I don't have to write as much boilerplate?