@f5-sales-demo/xcsh 22.4.11 → 22.5.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "22.4.11",
4
+ "version": "22.5.0",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -67,13 +67,13 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@agentclientprotocol/sdk": "1.4.0",
70
- "@f5-sales-demo/pi-agent-core": "22.4.11",
71
- "@f5-sales-demo/pi-ai": "22.4.11",
72
- "@f5-sales-demo/pi-natives": "22.4.11",
73
- "@f5-sales-demo/pi-resource-management": "22.4.11",
74
- "@f5-sales-demo/pi-tui": "22.4.11",
75
- "@f5-sales-demo/pi-utils": "22.4.11",
76
- "@f5-sales-demo/xcsh-stats": "22.4.11",
70
+ "@f5-sales-demo/pi-agent-core": "22.5.0",
71
+ "@f5-sales-demo/pi-ai": "22.5.0",
72
+ "@f5-sales-demo/pi-natives": "22.5.0",
73
+ "@f5-sales-demo/pi-resource-management": "22.5.0",
74
+ "@f5-sales-demo/pi-tui": "22.5.0",
75
+ "@f5-sales-demo/pi-utils": "22.5.0",
76
+ "@f5-sales-demo/xcsh-stats": "22.5.0",
77
77
  "@mozilla/readability": "^0.6",
78
78
  "@resvg/resvg-wasm": "2.6.2",
79
79
  "@sinclair/typebox": "0.34.52",
@@ -20,7 +20,7 @@ import { hardenAgentConfigFileSync, writeAgentConfigFileSync } from "./agent-con
20
20
  import { DEFAULT_MODEL_ROLE } from "./settings-schema";
21
21
 
22
22
  /** Current config schema version. Bump when the generated format changes. */
23
- export const CURRENT_CONFIG_VERSION = 11;
23
+ export const CURRENT_CONFIG_VERSION = 12;
24
24
  const LITELLM_CONFIG_DIR_MODE = 0o700;
25
25
  const LITELLM_MODELS_FILE_MODE = 0o600;
26
26
  const GENERATED_LITELLM_MARKER = "# Auto-generated by xcsh for LiteLLM proxy";
@@ -119,6 +119,7 @@ export function generateModelsYml(baseUrl: string, options?: GenerateModelsYmlOp
119
119
  " modelAllowlist:",
120
120
  " - gpt-6-luna",
121
121
  " - gpt-5.6-terra",
122
+ " - gpt-6.1-sol",
122
123
  " - gpt-6-sol",
123
124
  " - gpt-6-astra",
124
125
  " - gpt-5.6-luna",
@@ -133,6 +134,10 @@ export function generateModelsYml(baseUrl: string, options?: GenerateModelsYmlOp
133
134
  " name: GPT-5.6 Terra",
134
135
  " api: openai-responses",
135
136
  ` baseUrl: "${responsesBaseUrl}"`,
137
+ " - id: gpt-6.1-sol",
138
+ " name: GPT-6.1 Sol",
139
+ " api: openai-responses",
140
+ ` baseUrl: "${responsesBaseUrl}"`,
136
141
  " - id: gpt-6-sol",
137
142
  " name: GPT-6 Sol",
138
143
  " api: openai-responses",
@@ -198,6 +203,25 @@ export function generateModelsYml(baseUrl: string, options?: GenerateModelsYmlOp
198
203
  " maxTokens: 128000",
199
204
  " compat:",
200
205
  " supportsTemperature: false",
206
+ " gpt-6.1-sol:",
207
+ " reasoning: true",
208
+ " input:",
209
+ " - text",
210
+ " - image",
211
+ " cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }",
212
+ " thinking:",
213
+ " mode: effort",
214
+ " defaultLevel: medium",
215
+ " supportedLevels:",
216
+ " - { effort: low, description: Light reasoning }",
217
+ " - { effort: medium, description: Balanced reasoning }",
218
+ " - { effort: high, description: Deep reasoning }",
219
+ " - { effort: xhigh, description: Very deep reasoning }",
220
+ " - { effort: max, description: Maximum reasoning }",
221
+ " contextWindow: 922000",
222
+ " maxTokens: 128000",
223
+ " compat:",
224
+ " supportsTemperature: false",
201
225
  " gpt-6-sol:",
202
226
  " reasoning: true",
203
227
  " input:",
@@ -12,6 +12,7 @@ function compareVersions(left: string, right: string): number {
12
12
 
13
13
  /** Present the exact model-interaction contract when each provider advertises the model. */
14
14
  const CODEX_INTERACTION_MODEL_IDS = new Set([
15
+ "gpt-6.1-sol",
15
16
  "gpt-6-astra",
16
17
  "gpt-6-sol",
17
18
  "gpt-6-luna",
@@ -1,12 +1,15 @@
1
1
  import * as path from "node:path";
2
2
  import {
3
+ type AnyModel,
3
4
  type Api,
4
5
  type AssistantMessageEventStream,
5
6
  applyCodexInteractionMetadata,
6
7
  applyGeneratedModelPolicies,
8
+ type ClassifierModel,
7
9
  type CodexContextTier,
8
10
  type Context,
9
11
  createModelManager,
12
+ createOperationCatalog,
10
13
  DEFAULT_LOCAL_TOKEN,
11
14
  enrichModelThinking,
12
15
  getBundledModels,
@@ -14,6 +17,7 @@ import {
14
17
  googleAntigravityModelManagerOptions,
15
18
  googleGeminiCliModelManagerOptions,
16
19
  googleVertexModelManagerOptions,
20
+ type ImageModel,
17
21
  type Model,
18
22
  type ModelManagerOptions,
19
23
  type ModelRefreshStrategy,
@@ -31,7 +35,7 @@ import {
31
35
  unregisterCustomApis,
32
36
  unregisterOAuthProviders,
33
37
  } from "@f5-sales-demo/pi-ai";
34
- import { $env, $envExact, isRecord, logger } from "@f5-sales-demo/pi-utils";
38
+ import { $env, $envExact, getModelDbPath, isRecord, logger } from "@f5-sales-demo/pi-utils";
35
39
  import { type Static, Type } from "@sinclair/typebox";
36
40
  import { type ConfigError, ConfigFile } from "../config";
37
41
  import { hasLiteLLMEnv, probeAndUpgradeLiteLLMConfig, startupHealthCheck } from "../config/auto-config";
@@ -147,6 +151,8 @@ const ReasoningEffortMapSchema = Type.Object({
147
151
  medium: Type.Optional(Type.String()),
148
152
  high: Type.Optional(Type.String()),
149
153
  xhigh: Type.Optional(Type.String()),
154
+ max: Type.Optional(Type.String()),
155
+ ultra: Type.Optional(Type.String()),
150
156
  });
151
157
 
152
158
  const OpenAICompatSchema = Type.Object({
@@ -157,6 +163,7 @@ const OpenAICompatSchema = Type.Object({
157
163
  reasoningEffortMap: Type.Optional(ReasoningEffortMapSchema),
158
164
  maxTokensField: Type.Optional(Type.Union([Type.Literal("max_completion_tokens"), Type.Literal("max_tokens")])),
159
165
  supportsUsageInStreaming: Type.Optional(Type.Boolean()),
166
+ supportsFinishReason: Type.Optional(Type.Boolean()),
160
167
  requiresToolResultName: Type.Optional(Type.Boolean()),
161
168
  requiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),
162
169
  requiresThinkingAsText: Type.Optional(Type.Boolean()),
@@ -167,12 +174,77 @@ const OpenAICompatSchema = Type.Object({
167
174
  Type.Literal("zai"),
168
175
  Type.Literal("qwen"),
169
176
  Type.Literal("qwen-chat-template"),
177
+ Type.Literal("ant-ling"),
178
+ Type.Literal("baseten"),
179
+ Type.Literal("deepseek"),
180
+ Type.Literal("together"),
181
+ Type.Literal("chat-template"),
182
+ Type.Literal("string-thinking"),
170
183
  ]),
171
184
  ),
172
185
  openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
186
+ chatTemplateKwargs: Type.Optional(
187
+ Type.Record(
188
+ Type.String(),
189
+ Type.Union([
190
+ Type.String(),
191
+ Type.Number(),
192
+ Type.Boolean(),
193
+ Type.Null(),
194
+ Type.Object({
195
+ $var: Type.Union([
196
+ Type.Literal("thinking.enabled"),
197
+ Type.Literal("thinking.effort"),
198
+ Type.Literal("thinking.budget"),
199
+ ]),
200
+ omitWhenOff: Type.Optional(Type.Boolean()),
201
+ }),
202
+ ]),
203
+ ),
204
+ ),
205
+ chatTemplateArgs: Type.Optional(
206
+ Type.Record(
207
+ Type.String(),
208
+ Type.Union([
209
+ Type.String(),
210
+ Type.Number(),
211
+ Type.Boolean(),
212
+ Type.Null(),
213
+ Type.Object({
214
+ $var: Type.Union([
215
+ Type.Literal("thinking.enabled"),
216
+ Type.Literal("thinking.effort"),
217
+ Type.Literal("thinking.budget"),
218
+ ]),
219
+ omitWhenOff: Type.Optional(Type.Boolean()),
220
+ }),
221
+ ]),
222
+ ),
223
+ ),
224
+ thinkingTokenBudgetField: Type.Optional(
225
+ Type.Union([
226
+ Type.Literal("thinking_token_budget"),
227
+ Type.Literal("thinking_budget"),
228
+ Type.Literal("thinking_budget_tokens"),
229
+ ]),
230
+ ),
231
+ supportsThinkingTokenBudget: Type.Optional(Type.Boolean()),
232
+ sendSessionAffinityHeaders: Type.Optional(Type.Boolean()),
233
+ allowEmptySignature: Type.Optional(Type.Boolean()),
234
+ supportsCacheControlOnTools: Type.Optional(Type.Boolean()),
235
+ forceAdaptiveThinking: Type.Optional(Type.Boolean()),
173
236
  vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),
174
237
  extraBody: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
175
238
  supportsStrictMode: Type.Optional(Type.Boolean()),
239
+ supportsMidConvoSystemMessages: Type.Optional(Type.Boolean()),
240
+ supportsMidConvoToolChanges: Type.Optional(Type.Boolean()),
241
+ supportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),
242
+ supportsAdditionalTools: Type.Optional(Type.Boolean()),
243
+ supportsToolSearch: Type.Optional(Type.Boolean()),
244
+ supportsExplicitPromptCacheMode: Type.Optional(Type.Boolean()),
245
+ supportsCachedReasoningUpdates: Type.Optional(Type.Boolean()),
246
+ supportsWebSocketSteering: Type.Optional(Type.Boolean()),
247
+ supportsMaxOutputTokens: Type.Optional(Type.Boolean()),
176
248
  toolStrictMode: Type.Optional(Type.Union([Type.Literal("all_strict"), Type.Literal("none")])),
177
249
  });
178
250
 
@@ -184,6 +256,7 @@ const EffortSchema = Type.Union([
184
256
  Type.Literal("high"),
185
257
  Type.Literal("xhigh"),
186
258
  Type.Literal("max"),
259
+ Type.Literal("ultra"),
187
260
  ]);
188
261
 
189
262
  const ThinkingControlModeSchema = Type.Union([
@@ -213,6 +286,8 @@ const ModelDefinitionSchema = Type.Object({
213
286
  name: Type.Optional(Type.String({ minLength: 1 })),
214
287
  api: Type.Optional(
215
288
  Type.Union([
289
+ Type.Literal("pi-messages"),
290
+ Type.Literal("mistral-conversations"),
216
291
  Type.Literal("openai-completions"),
217
292
  Type.Literal("openai-responses"),
218
293
  Type.Literal("openai-codex-responses"),
@@ -232,10 +307,22 @@ const ModelDefinitionSchema = Type.Object({
232
307
  output: Type.Number(),
233
308
  cacheRead: Type.Number(),
234
309
  cacheWrite: Type.Number(),
310
+ tiers: Type.Optional(
311
+ Type.Array(
312
+ Type.Object({
313
+ inputTokensAbove: Type.Number(),
314
+ input: Type.Number(),
315
+ output: Type.Number(),
316
+ cacheRead: Type.Number(),
317
+ cacheWrite: Type.Number(),
318
+ }),
319
+ ),
320
+ ),
235
321
  }),
236
322
  ),
237
323
  premiumMultiplier: Type.Optional(Type.Number()),
238
324
  contextWindow: Type.Optional(Type.Number()),
325
+ maxInputTokens: Type.Optional(Type.Number()),
239
326
  maxTokens: Type.Optional(Type.Number()),
240
327
  headers: Type.Optional(Type.Record(Type.String(), Type.String())),
241
328
  compat: Type.Optional(OpenAICompatSchema),
@@ -254,10 +341,22 @@ const ModelOverrideSchema = Type.Object({
254
341
  output: Type.Optional(Type.Number()),
255
342
  cacheRead: Type.Optional(Type.Number()),
256
343
  cacheWrite: Type.Optional(Type.Number()),
344
+ tiers: Type.Optional(
345
+ Type.Array(
346
+ Type.Object({
347
+ inputTokensAbove: Type.Number(),
348
+ input: Type.Number(),
349
+ output: Type.Number(),
350
+ cacheRead: Type.Number(),
351
+ cacheWrite: Type.Number(),
352
+ }),
353
+ ),
354
+ ),
257
355
  }),
258
356
  ),
259
357
  premiumMultiplier: Type.Optional(Type.Number()),
260
358
  contextWindow: Type.Optional(Type.Number()),
359
+ maxInputTokens: Type.Optional(Type.Number()),
261
360
  maxTokens: Type.Optional(Type.Number()),
262
361
  headers: Type.Optional(Type.Record(Type.String(), Type.String())),
263
362
  compat: Type.Optional(OpenAICompatSchema),
@@ -329,6 +428,7 @@ interface ProviderValidationModel {
329
428
  id: string;
330
429
  api?: Api;
331
430
  contextWindow?: number;
431
+ maxInputTokens?: number;
332
432
  maxTokens?: number;
333
433
  }
334
434
 
@@ -543,10 +643,12 @@ type OllamaDiscoveredModelMetadata = {
543
643
  reasoning: boolean;
544
644
  input: ("text" | "image")[];
545
645
  contextWindow?: number;
646
+ maxInputTokens?: number;
546
647
  };
547
648
 
548
649
  type LlamaCppDiscoveredServerMetadata = {
549
650
  contextWindow?: number;
651
+ maxInputTokens?: number;
550
652
  input?: ("text" | "image")[];
551
653
  };
552
654
 
@@ -699,6 +801,7 @@ function applyModelOverride(model: Model<Api>, override: ModelOverride): Model<A
699
801
  if (override.thinking !== undefined) result.thinking = override.thinking as ThinkingConfig;
700
802
  if (override.input !== undefined) result.input = override.input as ("text" | "image")[];
701
803
  if (override.contextWindow !== undefined) result.contextWindow = override.contextWindow;
804
+ if (override.maxInputTokens !== undefined) result.maxInputTokens = override.maxInputTokens;
702
805
  if (override.maxTokens !== undefined) result.maxTokens = override.maxTokens;
703
806
  if (override.contextPromotionTarget !== undefined) result.contextPromotionTarget = override.contextPromotionTarget;
704
807
  if (override.premiumMultiplier !== undefined) result.premiumMultiplier = override.premiumMultiplier;
@@ -708,6 +811,7 @@ function applyModelOverride(model: Model<Api>, override: ModelOverride): Model<A
708
811
  output: override.cost.output ?? model.cost.output,
709
812
  cacheRead: override.cost.cacheRead ?? model.cost.cacheRead,
710
813
  cacheWrite: override.cost.cacheWrite ?? model.cost.cacheWrite,
814
+ tiers: override.cost.tiers ?? model.cost.tiers,
711
815
  };
712
816
  }
713
817
  if (override.headers) {
@@ -725,8 +829,9 @@ interface CustomModelDefinitionLike {
725
829
  reasoning?: boolean;
726
830
  thinking?: ThinkingConfig;
727
831
  input?: ("text" | "image")[];
728
- cost?: { input: number; output: number; cacheRead: number; cacheWrite: number };
832
+ cost?: import("@f5-sales-demo/pi-ai").ModelCost;
729
833
  contextWindow?: number;
834
+ maxInputTokens?: number;
730
835
  maxTokens?: number;
731
836
  headers?: Record<string, string>;
732
837
  compat?: Model<Api>["compat"];
@@ -747,8 +852,9 @@ type CustomModelOverlay = {
747
852
  reasoning?: boolean;
748
853
  thinking?: ThinkingConfig;
749
854
  input?: ("text" | "image")[];
750
- cost?: { input: number; output: number; cacheRead: number; cacheWrite: number };
855
+ cost?: import("@f5-sales-demo/pi-ai").ModelCost;
751
856
  contextWindow?: number;
857
+ maxInputTokens?: number;
752
858
  maxTokens?: number;
753
859
  headers?: Record<string, string>;
754
860
  compat?: Model<Api>["compat"];
@@ -795,6 +901,7 @@ function buildCustomModelOverlay(
795
901
  input: modelDef.input as ("text" | "image")[] | undefined,
796
902
  cost: modelDef.cost,
797
903
  contextWindow: modelDef.contextWindow,
904
+ maxInputTokens: modelDef.maxInputTokens,
798
905
  maxTokens: modelDef.maxTokens,
799
906
  headers: mergeCustomModelHeaders(providerHeaders, modelDef.headers, authHeader, providerApiKey),
800
907
  compat: mergeCompat(providerCompat, modelDef.compat),
@@ -825,6 +932,7 @@ function finalizeCustomModel(model: CustomModelOverlay, options: CustomModelBuil
825
932
  thinking: resolvedModel.thinking,
826
933
  input: input as ("text" | "image")[],
827
934
  cost,
935
+ maxInputTokens: resolvedModel.maxInputTokens,
828
936
  contextWindow: resolvedModel.contextWindow ?? (options.useDefaults ? 128000 : undefined),
829
937
  maxTokens: resolvedModel.maxTokens ?? (options.useDefaults ? 16384 : undefined),
830
938
  headers: resolvedModel.headers,
@@ -854,6 +962,7 @@ function getDisabledProviderIdsFromSettings(): Set<string> {
854
962
  * Model registry - loads and manages models, resolves API keys via AuthStorage.
855
963
  */
856
964
  export class ModelRegistry {
965
+ #operationCatalog: ReturnType<typeof createOperationCatalog>;
857
966
  #models: Model<Api>[] = [];
858
967
  #canonicalIndex: CanonicalModelIndex = { records: [], byId: new Map(), bySelector: new Map() };
859
968
  #customProviderApiKeys: Map<string, string> = new Map();
@@ -907,6 +1016,9 @@ export class ModelRegistry {
907
1016
  ) {
908
1017
  this.#modelsConfigFile = ModelsConfigFile.relocate(modelsPath);
909
1018
  this.#cacheDbPath = modelsPath ? path.join(path.dirname(modelsPath), "models.db") : undefined;
1019
+ this.#operationCatalog = createOperationCatalog({
1020
+ cacheDbPath: this.#cacheDbPath ?? getModelDbPath(),
1021
+ });
910
1022
  // Set up fallback resolver for custom provider API keys
911
1023
  this.authStorage.setFallbackResolver(provider => {
912
1024
  const keyConfig = this.#customProviderApiKeys.get(provider);
@@ -2066,6 +2178,7 @@ export class ModelRegistry {
2066
2178
  const items: Array<{
2067
2179
  id: string;
2068
2180
  contextWindow?: number;
2181
+ maxInputTokens?: number;
2069
2182
  context_window?: unknown;
2070
2183
  max_input_tokens?: unknown;
2071
2184
  max_output_tokens?: unknown;
@@ -2233,6 +2346,42 @@ export class ModelRegistry {
2233
2346
  return this.#models;
2234
2347
  }
2235
2348
 
2349
+ getImageModels(provider: string): ImageModel[] {
2350
+ return this.#operationCatalog
2351
+ .getImageModels(provider)
2352
+ .map(model => ({ ...model, baseUrl: this.#providerOverrides.get(provider)?.baseUrl ?? model.baseUrl }));
2353
+ }
2354
+
2355
+ getClassifierModels(provider: string): ClassifierModel[] {
2356
+ return this.#operationCatalog
2357
+ .getClassifierModels(provider)
2358
+ .map(model => ({ ...model, baseUrl: this.#providerOverrides.get(provider)?.baseUrl ?? model.baseUrl }));
2359
+ }
2360
+
2361
+ getAnyModels(provider: string): AnyModel[] {
2362
+ return [
2363
+ ...this.#models.filter(model => model.provider === provider),
2364
+ ...this.getImageModels(provider),
2365
+ ...this.getClassifierModels(provider),
2366
+ ];
2367
+ }
2368
+
2369
+ async refreshOperationModels(provider: string, signal?: AbortSignal): Promise<void> {
2370
+ const apiKey = await this.getApiKeyForProvider(provider);
2371
+ await this.#operationCatalog.refresh(provider, {
2372
+ apiKey,
2373
+ signal,
2374
+ baseUrl: this.getProviderBaseUrl(provider),
2375
+ ...(provider === "llama.cpp"
2376
+ ? {
2377
+ modelIds: this.getAll()
2378
+ .filter(model => model.provider === provider)
2379
+ .map(model => model.id),
2380
+ }
2381
+ : {}),
2382
+ });
2383
+ }
2384
+
2236
2385
  #isModelAvailable(model: Model<Api>): boolean {
2237
2386
  const disabledProviders = getDisabledProviderIdsFromSettings();
2238
2387
  return (
@@ -2711,7 +2860,7 @@ export interface ProviderConfigInput {
2711
2860
  reasoning: boolean;
2712
2861
  thinking?: ThinkingConfig;
2713
2862
  input: ("text" | "image")[];
2714
- cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
2863
+ cost: import("@f5-sales-demo/pi-ai").ModelCost;
2715
2864
  contextWindow: number;
2716
2865
  maxTokens: number;
2717
2866
  headers?: Record<string, string>;
@@ -161,22 +161,21 @@ const DEFAULT_CYCLE_ORDER: string[] = ["smol", "default", "slow"];
161
161
  /**
162
162
  * Binary-baked default model role. Ships in the binary so a fresh install needs
163
163
  * NO `~/.xcsh/agent/config.yml` — `/login` only supplies the (PII) proxy URL + key.
164
- * GPT-5.6 Terra Medium is the current general-purpose default through the OpenAI
165
- * Chat Completions route on LiteLLM. Keep the effort explicit in the role so returning
164
+ * GPT-6.1 Sol Medium is the default through the OpenAI Responses route on LiteLLM. Keep the effort explicit in the role so returning
166
165
  * from a lower-effort role cannot inherit that role's effort.
167
166
  */
168
- export const DEFAULT_MODEL_ROLE = "litellm/gpt-5.6-terra:medium";
167
+ export const DEFAULT_MODEL_ROLE = "litellm/gpt-6.1-sol:medium";
169
168
  /** Fast role for lightweight work (commit messages, titles, memory summaries). */
170
169
  const SMOL_MODEL_ROLE = "litellm/gpt-6-luna:low";
171
170
  /**
172
171
  * Baked role map for the current internal LiteLLM catalog. Sol handles slow and
173
- * planning work, while Luna and Terra cover lightweight and default work.
172
+ * planning and default work, while Luna covers lightweight work.
174
173
  */
175
174
  export const DEFAULT_MODEL_ROLES: Record<string, string> = {
176
175
  default: DEFAULT_MODEL_ROLE,
177
176
  smol: SMOL_MODEL_ROLE,
178
- slow: "litellm/gpt-6-sol:high",
179
- plan: "litellm/gpt-6-sol:high",
177
+ slow: "litellm/gpt-6.1-sol:high",
178
+ plan: "litellm/gpt-6.1-sol:high",
180
179
  };
181
180
  const EMPTY_MODEL_TAGS_RECORD: ModelTagsSettings = {};
182
181
  export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
@@ -184,6 +184,9 @@ export interface CustomTool<TParams extends TSchema = TSchema, TDetails = any> {
184
184
  hidden?: boolean;
185
185
  /** If true, tool may stage deferred changes that require explicit resolve/discard. */
186
186
  deferrable?: boolean;
187
+ /** Opt-in execution through the host's background job lifecycle. */
188
+ async?: boolean;
189
+ constrainedSampling?: import("@f5-sales-demo/pi-ai").Tool["constrainedSampling"];
187
190
  /**
188
191
  * Execute the tool.
189
192
  * @param toolCallId - Unique ID for this tool call
@@ -313,6 +313,8 @@ export interface ToolSessionEvent {
313
313
  * Tool definition for registerTool().
314
314
  */
315
315
  export interface ToolDefinition<TParams extends TSchema = TSchema, TDetails = unknown> {
316
+ async?: boolean;
317
+ constrainedSampling?: import("@f5-sales-demo/pi-ai").Tool["constrainedSampling"];
316
318
  /** Tool name (used in LLM tool calls) */
317
319
  name: string;
318
320
  /** Human-readable label for UI */
@@ -1323,7 +1325,7 @@ export interface ProviderModelConfig {
1323
1325
  /** Supported input types. */
1324
1326
  input: ("text" | "image")[];
1325
1327
  /** Cost per million tokens. */
1326
- cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
1328
+ cost: import("@f5-sales-demo/pi-ai").ModelCost;
1327
1329
  /** Premium Copilot requests charged per user-initiated request. */
1328
1330
  premiumMultiplier?: number;
1329
1331
  /** Maximum context window size in tokens. */