@jun133/kitty 0.0.13 → 0.0.15

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.js CHANGED
@@ -40,7 +40,7 @@ var init_package = __esm({
40
40
  "package.json"() {
41
41
  package_default = {
42
42
  name: "@jun133/kitty",
43
- version: "0.0.13",
43
+ version: "0.0.15",
44
44
  description: "Agent",
45
45
  license: "MIT",
46
46
  keywords: [
@@ -120,7 +120,7 @@ function getErrorMessage(error) {
120
120
  return "Environment error: network connection failed; the current provider/base URL is unreachable. Check network, proxy settings, or `KITTY_BASE_URL`.";
121
121
  }
122
122
  if (status === 404 || lower.includes("returned 404")) {
123
- return "User-fixable error: provider returned 404. Check whether `KITTY_BASE_URL` is the correct OpenAI-compatible API base URL.";
123
+ return "User-fixable error: provider endpoint returned 404. Check `KITTY_PROVIDER`, `KITTY_MODEL`, and `KITTY_BASE_URL` as one provider profile; the selected provider may use Responses instead of Chat Completions.";
124
124
  }
125
125
  if (typeof status === "number" && status >= 500) {
126
126
  return `Provider error: service returned ${status}. Retry later or confirm the provider service is healthy.`;
@@ -1917,14 +1917,21 @@ async function launchCommand(command, cwd, timeoutMs, abortSignal) {
1917
1917
  function encodePowerShellCommand(command) {
1918
1918
  const wrapped = [
1919
1919
  "$ProgressPreference = 'SilentlyContinue'",
1920
+ "$ErrorActionPreference = 'Stop'",
1920
1921
  "[Console]::InputEncoding = [System.Text.Encoding]::UTF8",
1921
1922
  "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8",
1922
1923
  "$OutputEncoding = [System.Text.Encoding]::UTF8",
1923
1924
  "try { chcp 65001 > $null } catch { }",
1925
+ "$code = 0",
1926
+ "try {",
1924
1927
  `& { ${command} }`,
1925
1928
  "$code = if ($null -ne $LASTEXITCODE) { [int]$LASTEXITCODE } elseif ($?) { 0 } else { 1 }",
1929
+ "} catch {",
1930
+ "[Console]::Error.WriteLine($_.Exception.Message)",
1931
+ "$code = 1",
1932
+ "}",
1926
1933
  "exit $code"
1927
- ].join("; ");
1934
+ ].join("\n");
1928
1935
  return Buffer.from(wrapped, "utf16le").toString("base64");
1929
1936
  }
1930
1937
  function buildCommandEnvironment() {
@@ -1943,6 +1950,26 @@ var init_launch = __esm({
1943
1950
  }
1944
1951
  });
1945
1952
 
1953
+ // src/utils/commandRunner/output.ts
1954
+ function normalizeCommandOutput(output) {
1955
+ if (!output.includes("#< CLIXML")) {
1956
+ return output;
1957
+ }
1958
+ const errors = [...output.matchAll(/<S\s+S="Error">([\s\S]*?)<\/S>/g)].map((match) => decodePowerShellText(match[1] ?? "")).map((line) => line.trimEnd()).filter(Boolean);
1959
+ if (errors.length === 0) {
1960
+ return output;
1961
+ }
1962
+ return errors.join("\n").replace(/\n{3,}/g, "\n\n").trim();
1963
+ }
1964
+ function decodePowerShellText(value) {
1965
+ return value.replace(/_x000D__x000A_/g, "\n").replace(/_x000D_/g, "\r").replace(/_x000A_/g, "\n").replace(/_x0009_/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
1966
+ }
1967
+ var init_output = __esm({
1968
+ "src/utils/commandRunner/output.ts"() {
1969
+ "use strict";
1970
+ }
1971
+ });
1972
+
1946
1973
  // src/extensions/tools/background/tools/backgroundRun.ts
1947
1974
  function createBackgroundOutputTracker(onOutput) {
1948
1975
  let buffer = "";
@@ -1978,6 +2005,7 @@ var init_backgroundRun = __esm({
1978
2005
  "src/extensions/tools/background/tools/backgroundRun.ts"() {
1979
2006
  "use strict";
1980
2007
  init_launch();
2008
+ init_output();
1981
2009
  init_fs();
1982
2010
  init_shared2();
1983
2011
  init_background();
@@ -2016,9 +2044,10 @@ var init_backgroundRun = __esm({
2016
2044
  registerBackgroundProcess(job.id, subprocess);
2017
2045
  store.markRunning(job.id, { pid: subprocess.pid ?? 0 });
2018
2046
  const outputTracker = createBackgroundOutputTracker((output) => {
2047
+ const normalizedOutput = normalizeCommandOutput(output);
2019
2048
  store.updateRunningOutput(job.id, {
2020
- output,
2021
- summary: summarizeBackgroundOutput(output),
2049
+ output: normalizedOutput,
2050
+ summary: summarizeBackgroundOutput(normalizedOutput),
2022
2051
  lastOutputAt: (/* @__PURE__ */ new Date()).toISOString()
2023
2052
  });
2024
2053
  });
@@ -2028,7 +2057,7 @@ var init_backgroundRun = __esm({
2028
2057
  void subprocess.then(async (result) => {
2029
2058
  outputTracker.flush();
2030
2059
  const running2 = store.load(job.id);
2031
- const resultOutput = typeof result.all === "string" ? result.all : "";
2060
+ const resultOutput = normalizeCommandOutput(typeof result.all === "string" ? result.all : "");
2032
2061
  const output = resultOutput || running2?.output || "";
2033
2062
  store.close(job.id, {
2034
2063
  status: result.exitCode === 0 ? "completed" : "failed",
@@ -2040,7 +2069,7 @@ var init_backgroundRun = __esm({
2040
2069
  }, async (error) => {
2041
2070
  outputTracker.flush();
2042
2071
  const running2 = store.load(job.id);
2043
- const errorOutput = typeof error.all === "string" ? error.all : "";
2072
+ const errorOutput = normalizeCommandOutput(typeof error.all === "string" ? error.all : "");
2044
2073
  const output = errorOutput || running2?.output || String(error.message);
2045
2074
  store.close(job.id, {
2046
2075
  status: "failed",
@@ -3878,439 +3907,17 @@ var init_outputCapture = __esm({
3878
3907
  }
3879
3908
  });
3880
3909
 
3881
- // src/utils/commandRunner/platformArgs.ts
3882
- function splitByAndAnd(command) {
3883
- const segments = [];
3884
- let current = "";
3885
- let inSingle = false;
3886
- let inDouble = false;
3887
- for (let index = 0; index < command.length; index += 1) {
3888
- const char = command.charAt(index);
3889
- if (char === "'" && !inDouble) {
3890
- inSingle = !inSingle;
3891
- current += char;
3892
- continue;
3893
- }
3894
- if (char === '"' && !inSingle) {
3895
- inDouble = !inDouble;
3896
- current += char;
3897
- continue;
3898
- }
3899
- if (!inSingle && !inDouble && char === "&" && command.charAt(index + 1) === "&") {
3900
- if (current.trim()) {
3901
- segments.push(current.trim());
3902
- }
3903
- current = "";
3904
- index += 1;
3905
- continue;
3906
- }
3907
- current += char;
3908
- }
3909
- if (current.trim()) {
3910
- segments.push(current.trim());
3911
- }
3912
- return segments.length > 0 ? segments : [command];
3913
- }
3914
- function joinWithAndSemantics(segments) {
3915
- if (segments.length <= 1) {
3916
- return segments[0] ?? "";
3917
- }
3918
- let script = segments[0] ?? "";
3919
- for (let index = 1; index < segments.length; index += 1) {
3920
- const segment = segments[index];
3921
- script += `; if ($?) { ${segment} }`;
3922
- }
3923
- return script;
3924
- }
3925
- function splitArgs(command) {
3926
- const args = [];
3927
- let current = "";
3928
- let inSingle = false;
3929
- let inDouble = false;
3930
- for (let index = 0; index < command.length; index += 1) {
3931
- const char = command.charAt(index);
3932
- if (char === "'" && !inDouble) {
3933
- inSingle = !inSingle;
3934
- continue;
3935
- }
3936
- if (char === '"' && !inSingle) {
3937
- inDouble = !inDouble;
3938
- continue;
3939
- }
3940
- if (!inSingle && !inDouble && /\s/.test(char)) {
3941
- if (current) {
3942
- args.push(current);
3943
- current = "";
3944
- }
3945
- continue;
3946
- }
3947
- current += char;
3948
- }
3949
- if (current) {
3950
- args.push(current);
3951
- }
3952
- return args;
3953
- }
3954
- function expandPaths(paths) {
3955
- return paths.flatMap((targetPath) => expandBraces(targetPath));
3956
- }
3957
- function normalizeWindowsPath(value) {
3958
- if (value.includes("://")) {
3959
- return value;
3960
- }
3961
- return value.replace(/\//g, "\\");
3962
- }
3963
- function quotePowerShell(value) {
3964
- const escaped = value.replace(/'/g, "''");
3965
- return `'${escaped}'`;
3966
- }
3967
- function expandBraces(input) {
3968
- const start = findBraceStart(input);
3969
- if (start === -1) {
3970
- return [input];
3971
- }
3972
- const end = findMatchingBrace(input, start);
3973
- if (end === -1) {
3974
- return [input];
3975
- }
3976
- const prefix = input.slice(0, start);
3977
- const suffix = input.slice(end + 1);
3978
- const body = input.slice(start + 1, end);
3979
- const parts = splitBraceParts(body);
3980
- const expandedSuffix = expandBraces(suffix);
3981
- const results = [];
3982
- for (const part of parts) {
3983
- for (const expandedPart of expandBraces(part)) {
3984
- for (const tail of expandedSuffix) {
3985
- results.push(`${prefix}${expandedPart}${tail}`);
3986
- }
3987
- }
3988
- }
3989
- return results;
3990
- }
3991
- function findBraceStart(input) {
3992
- let inSingle = false;
3993
- let inDouble = false;
3994
- for (let index = 0; index < input.length; index += 1) {
3995
- const char = input.charAt(index);
3996
- if (char === "'" && !inDouble) {
3997
- inSingle = !inSingle;
3998
- continue;
3999
- }
4000
- if (char === '"' && !inSingle) {
4001
- inDouble = !inDouble;
4002
- continue;
4003
- }
4004
- if (!inSingle && !inDouble && char === "{") {
4005
- return index;
4006
- }
4007
- }
4008
- return -1;
4009
- }
4010
- function findMatchingBrace(input, start) {
4011
- let depth = 0;
4012
- let inSingle = false;
4013
- let inDouble = false;
4014
- for (let index = start; index < input.length; index += 1) {
4015
- const char = input.charAt(index);
4016
- if (char === "'" && !inDouble) {
4017
- inSingle = !inSingle;
4018
- continue;
4019
- }
4020
- if (char === '"' && !inSingle) {
4021
- inDouble = !inDouble;
4022
- continue;
4023
- }
4024
- if (inSingle || inDouble) {
4025
- continue;
4026
- }
4027
- if (char === "{") {
4028
- depth += 1;
4029
- } else if (char === "}") {
4030
- depth -= 1;
4031
- if (depth === 0) {
4032
- return index;
4033
- }
4034
- }
4035
- }
4036
- return -1;
4037
- }
4038
- function splitBraceParts(input) {
4039
- const parts = [];
4040
- let current = "";
4041
- let depth = 0;
4042
- for (let index = 0; index < input.length; index += 1) {
4043
- const char = input.charAt(index);
4044
- if (char === "{") {
4045
- depth += 1;
4046
- current += char;
4047
- continue;
4048
- }
4049
- if (char === "}") {
4050
- depth -= 1;
4051
- current += char;
4052
- continue;
4053
- }
4054
- if (char === "," && depth === 0) {
4055
- parts.push(current);
4056
- current = "";
4057
- continue;
4058
- }
4059
- current += char;
4060
- }
4061
- if (current) {
4062
- parts.push(current);
4063
- }
4064
- return parts.length > 0 ? parts : [input];
4065
- }
4066
- var init_platformArgs = __esm({
4067
- "src/utils/commandRunner/platformArgs.ts"() {
4068
- "use strict";
4069
- }
4070
- });
4071
-
4072
- // src/utils/commandRunner/platformTransforms.ts
4073
- function startsWithExplicitShell(command) {
4074
- return /^\s*(cmd(?:\.exe)?\s+\/c|powershell(?:\.exe)?\b|pwsh\b|bash\b)/i.test(command);
4075
- }
4076
- function normalizeWindowsSegment(segment) {
4077
- const trimmed = segment.trim();
4078
- if (!trimmed) {
4079
- return segment;
4080
- }
4081
- const lowered = trimmed.toLowerCase();
4082
- if (lowered.startsWith("get-childitem") || lowered.startsWith("new-item")) {
4083
- return segment;
4084
- }
4085
- if (lowered.startsWith("ls")) {
4086
- return normalizeLsSegment(trimmed);
4087
- }
4088
- if (lowered.startsWith("mkdir") || lowered.startsWith("md ")) {
4089
- return normalizeMkdirSegment(trimmed);
4090
- }
4091
- if (lowered.startsWith("rm ")) {
4092
- return normalizeRemoveSegment(trimmed);
4093
- }
4094
- if (lowered.startsWith("cp ")) {
4095
- return normalizeCopySegment(trimmed);
4096
- }
4097
- if (lowered.startsWith("mv ")) {
4098
- return normalizeMoveSegment(trimmed);
4099
- }
4100
- if (lowered.startsWith("touch ")) {
4101
- return normalizeTouchSegment(trimmed);
4102
- }
4103
- if (lowered.startsWith("cat ")) {
4104
- return normalizeCatSegment(trimmed);
4105
- }
4106
- return segment;
4107
- }
4108
- function normalizeLsSegment(segment) {
4109
- const args = splitArgs(segment).slice(1);
4110
- const flags = args.filter((arg) => arg.startsWith("-"));
4111
- const paths = args.filter((arg) => !arg.startsWith("-"));
4112
- const force = flags.some((flag) => flag.includes("a"));
4113
- const targetPath = paths[0];
4114
- let command = "Get-ChildItem";
4115
- if (force) {
4116
- command += " -Force";
4117
- }
4118
- if (targetPath) {
4119
- command += ` -LiteralPath ${quotePowerShell(normalizeWindowsPath(targetPath))}`;
4120
- }
4121
- return command;
4122
- }
4123
- function normalizeMkdirSegment(segment) {
4124
- const args = splitArgs(segment);
4125
- if (args.length <= 1) {
4126
- return segment;
4127
- }
4128
- const rest = args.slice(1);
4129
- let hasParents = false;
4130
- const paths = rest.filter((arg) => {
4131
- const lowered = arg.toLowerCase();
4132
- if (lowered === "-p" || lowered === "--parents") {
4133
- hasParents = true;
4134
- return false;
4135
- }
4136
- return true;
4137
- });
4138
- const needsNormalization = hasParents || paths.some((targetPath) => targetPath.includes("{"));
4139
- if (!needsNormalization) {
4140
- return segment;
4141
- }
4142
- const expanded = expandPaths(paths);
4143
- if (expanded.length === 0) {
4144
- return segment;
4145
- }
4146
- const normalizedPaths = expanded.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath)));
4147
- return `New-Item -ItemType Directory -Force -Path ${normalizedPaths.join(", ")}`;
4148
- }
4149
- function normalizeRemoveSegment(segment) {
4150
- const args = splitArgs(segment);
4151
- if (args.length <= 1) {
4152
- return segment;
4153
- }
4154
- const flags = args.slice(1).filter((arg) => arg.startsWith("-"));
4155
- const paths = args.slice(1).filter((arg) => !arg.startsWith("-"));
4156
- if (paths.length === 0) {
4157
- return segment;
4158
- }
4159
- const recurse = flags.some((flag) => /r/i.test(flag));
4160
- const force = flags.some((flag) => /f/i.test(flag));
4161
- let command = "Remove-Item";
4162
- if (recurse) {
4163
- command += " -Recurse";
4164
- }
4165
- if (force) {
4166
- command += " -Force";
4167
- }
4168
- command += ` -LiteralPath ${paths.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath))).join(", ")}`;
4169
- return command;
4170
- }
4171
- function normalizeCopySegment(segment) {
4172
- const args = splitArgs(segment);
4173
- if (args.length < 3) {
4174
- return segment;
4175
- }
4176
- const flags = args.slice(1).filter((arg) => arg.startsWith("-"));
4177
- const paths = args.slice(1).filter((arg) => !arg.startsWith("-"));
4178
- if (paths.length < 2) {
4179
- return segment;
4180
- }
4181
- const recurse = flags.some((flag) => /r/i.test(flag));
4182
- const force = flags.some((flag) => /f/i.test(flag));
4183
- const destination = paths[paths.length - 1];
4184
- if (!destination) {
4185
- return segment;
4186
- }
4187
- const sources = paths.slice(0, -1);
4188
- let command = "Copy-Item";
4189
- if (recurse) {
4190
- command += " -Recurse";
4191
- }
4192
- if (force) {
4193
- command += " -Force";
4194
- }
4195
- command += ` -Path ${sources.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath))).join(", ")}`;
4196
- command += ` -Destination ${quotePowerShell(normalizeWindowsPath(destination))}`;
4197
- return command;
4198
- }
4199
- function normalizeMoveSegment(segment) {
4200
- const args = splitArgs(segment);
4201
- if (args.length < 3) {
4202
- return segment;
4203
- }
4204
- const flags = args.slice(1).filter((arg) => arg.startsWith("-"));
4205
- const paths = args.slice(1).filter((arg) => !arg.startsWith("-"));
4206
- if (paths.length < 2) {
4207
- return segment;
4208
- }
4209
- const force = flags.some((flag) => /f/i.test(flag));
4210
- const destination = paths[paths.length - 1];
4211
- if (!destination) {
4212
- return segment;
4213
- }
4214
- const sources = paths.slice(0, -1);
4215
- let command = "Move-Item";
4216
- if (force) {
4217
- command += " -Force";
4218
- }
4219
- command += ` -Path ${sources.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath))).join(", ")}`;
4220
- command += ` -Destination ${quotePowerShell(normalizeWindowsPath(destination))}`;
4221
- return command;
4222
- }
4223
- function normalizeTouchSegment(segment) {
4224
- const args = splitArgs(segment).slice(1);
4225
- if (args.length === 0) {
4226
- return segment;
4227
- }
4228
- const expanded = expandPaths(args);
4229
- if (expanded.length === 0) {
4230
- return segment;
4231
- }
4232
- const paths = expanded.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath)));
4233
- return `New-Item -ItemType File -Force -Path ${paths.join(", ")}`;
4234
- }
4235
- function normalizeCatSegment(segment) {
4236
- const args = splitArgs(segment).slice(1);
4237
- if (args.length === 0) {
4238
- return segment;
4239
- }
4240
- const targetPath = args[0];
4241
- if (!targetPath) {
4242
- return segment;
4243
- }
4244
- return `Get-Content -LiteralPath ${quotePowerShell(normalizeWindowsPath(targetPath))}`;
4245
- }
4246
- var init_platformTransforms = __esm({
4247
- "src/utils/commandRunner/platformTransforms.ts"() {
4248
- "use strict";
4249
- init_platformArgs();
4250
- }
4251
- });
4252
-
4253
- // src/utils/commandRunner/platform.ts
4254
- function normalizeCommandForPlatform(command) {
4255
- if (process.platform !== "win32") {
4256
- return command;
4257
- }
4258
- const trimmed = command.trim();
4259
- if (!trimmed) {
4260
- return command;
4261
- }
4262
- const normalized = normalizeWindowsCommand(trimmed);
4263
- return normalizeNpmCommandNames(normalized);
4264
- }
4265
- function normalizeWindowsCommand(command) {
4266
- if (startsWithExplicitShell(command)) {
4267
- return command;
4268
- }
4269
- const segments = splitByAndAnd(command);
4270
- const normalizedSegments = segments.map((segment) => normalizeWindowsSegment(segment));
4271
- return joinWithAndSemantics(normalizedSegments);
4272
- }
4273
- function normalizeNpmCommandNames(command) {
4274
- const commandNames = {
4275
- npm: "npm.cmd",
4276
- npx: "npx.cmd",
4277
- pnpm: "pnpm.cmd",
4278
- yarn: "yarn.cmd"
4279
- };
4280
- const pattern = /(^|[;&|]|\&\&)\s*(npm|npx|pnpm|yarn)(?=\s|$)/gi;
4281
- return command.replace(pattern, (match, prefix, tool) => {
4282
- const replacement = commandNames[String(tool).toLowerCase()];
4283
- if (!replacement) {
4284
- return match;
4285
- }
4286
- if (!prefix) {
4287
- return replacement;
4288
- }
4289
- return `${prefix} ${replacement}`;
4290
- });
4291
- }
4292
- var init_platform = __esm({
4293
- "src/utils/commandRunner/platform.ts"() {
4294
- "use strict";
4295
- init_platformArgs();
4296
- init_platformTransforms();
4297
- }
4298
- });
4299
-
4300
3910
  // src/utils/commandRunner/run.ts
4301
3911
  async function runCommandWithPolicy(options) {
4302
- const normalizedCommand = normalizeCommandForPlatform(options.command);
4303
- return runCommandOnce({
4304
- ...options,
4305
- command: normalizedCommand
4306
- });
3912
+ return runCommandOnce(options);
4307
3913
  }
4308
3914
  async function runCommandOnce(options) {
4309
3915
  const start = Date.now();
4310
3916
  let stalled = false;
4311
3917
  let stallTimer = null;
4312
3918
  let forceKillTimer = null;
4313
- const { subprocess } = await launchCommand(options.command, options.cwd, options.timeoutMs, options.abortSignal);
3919
+ const launched = await launchCommand(options.command, options.cwd, options.timeoutMs, options.abortSignal);
3920
+ const { subprocess } = launched;
4314
3921
  const outputCapture = await createBashOutputCapture(options.outputCapture ?? {});
4315
3922
  const clearTimers = () => {
4316
3923
  if (stallTimer) {
@@ -4363,9 +3970,11 @@ async function runCommandOnce(options) {
4363
3970
  const result = await subprocess;
4364
3971
  clearTimers();
4365
3972
  const shellOutput = await outputCapture.finalize();
3973
+ const output = normalizeCommandOutput(shellOutput.outputPreview);
4366
3974
  return {
3975
+ command: options.command,
4367
3976
  exitCode: typeof result.exitCode === "number" ? result.exitCode : null,
4368
- output: shellOutput.outputPreview,
3977
+ output,
4369
3978
  outputPath: shellOutput.outputPath,
4370
3979
  truncated: shellOutput.truncated,
4371
3980
  outputChars: shellOutput.outputChars,
@@ -4380,9 +3989,12 @@ async function runCommandOnce(options) {
4380
3989
  const timedOut = isTimedOutError(error);
4381
3990
  clearTimers();
4382
3991
  const shellOutput = await outputCapture.finalize();
3992
+ const fallbackOutput = shellOutput.outputChars > 0 ? shellOutput.outputPreview : readProcessOutput(error);
3993
+ const output = normalizeCommandOutput(fallbackOutput);
4383
3994
  return {
3995
+ command: options.command,
4384
3996
  exitCode: readExitCode(error),
4385
- output: shellOutput.outputChars > 0 ? shellOutput.outputPreview : readProcessOutput(error),
3997
+ output,
4386
3998
  outputPath: shellOutput.outputPath,
4387
3999
  truncated: shellOutput.truncated,
4388
4000
  outputChars: shellOutput.outputChars,
@@ -4423,7 +4035,7 @@ var init_run = __esm({
4423
4035
  init_abort();
4424
4036
  init_outputCapture();
4425
4037
  init_launch();
4426
- init_platform();
4038
+ init_output();
4427
4039
  STALL_KILL_TIMEOUT_MS = 5e3;
4428
4040
  }
4429
4041
  });
@@ -6493,81 +6105,325 @@ var init_config = __esm({
6493
6105
  }
6494
6106
  });
6495
6107
 
6496
- // src/provider/capabilities.ts
6497
- function resolveProviderCapabilities(input) {
6498
- const provider = normalizeProviderName(input.provider);
6499
- const model = normalizeModelName(input.model);
6500
- if (provider === "deepseek" || model.startsWith("deepseek-")) {
6501
- return {
6502
- provider: "deepseek",
6503
- model,
6504
- wireApi: "chat.completions",
6505
- supportsReasoningContent: true,
6506
- defaultReasoningEnabled: true,
6507
- defaultReasoningEffort: "high",
6508
- requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
6509
- doctorProbeTimeoutMs: DEFAULT_DOCTOR_PROBE_TIMEOUT_MS
6510
- };
6108
+ // src/provider/catalog.ts
6109
+ function listModelInfos() {
6110
+ return [...MODEL_CATALOG];
6111
+ }
6112
+ function findProviderInfo(providerId) {
6113
+ return PROVIDER_CATALOG.find((provider) => provider.id === normalizeProviderId(providerId));
6114
+ }
6115
+ function findModelInfo(providerId, modelId) {
6116
+ const normalizedProvider = normalizeProviderId(providerId);
6117
+ const normalizedModel = normalizeModelId(modelId);
6118
+ const known = MODEL_CATALOG.find((model) => model.providerId === normalizedProvider && model.id === normalizedModel);
6119
+ if (known) {
6120
+ return known;
6511
6121
  }
6512
- if (provider === "openai" || model === "gpt-5.4") {
6513
- return {
6514
- provider: "openai",
6515
- model,
6516
- wireApi: "responses",
6517
- supportsReasoningContent: false,
6518
- defaultReasoningEnabled: true,
6519
- defaultReasoningEffort: "xhigh",
6520
- requestTimeoutMs: RELAY_REQUEST_TIMEOUT_MS,
6521
- doctorProbeTimeoutMs: RELAY_DOCTOR_PROBE_TIMEOUT_MS
6522
- };
6122
+ if (normalizedProvider === "openai-compatible") {
6123
+ return createOpenAiCompatibleModelInfo(normalizedModel);
6124
+ }
6125
+ return void 0;
6126
+ }
6127
+ function resolveModelProfile(input) {
6128
+ const configuredProvider = normalizeProviderId(input.provider);
6129
+ const configuredModel = normalizeModelId(input.model);
6130
+ const provider = findProviderInfo(configuredProvider);
6131
+ if (!provider) {
6132
+ throw new Error(`Unknown provider: ${configuredProvider}. Check KITTY_PROVIDER.`);
6133
+ }
6134
+ const model = findModelInfo(configuredProvider, configuredModel);
6135
+ if (!model) {
6136
+ throw new Error(`Unknown model for provider ${configuredProvider}: ${configuredModel}. Check KITTY_MODEL.`);
6523
6137
  }
6524
6138
  return {
6525
6139
  provider,
6526
6140
  model,
6527
- wireApi: "chat.completions",
6528
- supportsReasoningContent: false,
6529
- defaultReasoningEnabled: false,
6530
- requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
6531
- doctorProbeTimeoutMs: DEFAULT_DOCTOR_PROBE_TIMEOUT_MS
6141
+ configuredProvider,
6142
+ configuredModel
6532
6143
  };
6533
6144
  }
6534
- function normalizeProviderName(value) {
6145
+ function normalizeProviderId(value) {
6535
6146
  const normalized = String(value ?? "").trim().toLowerCase();
6536
- return normalized || DEFAULT_PROVIDER;
6147
+ return normalized || "openai-compatible";
6537
6148
  }
6538
- function normalizeModelName(value) {
6149
+ function normalizeModelId(value) {
6539
6150
  return String(value ?? "").trim();
6540
6151
  }
6541
- var DEFAULT_PROVIDER, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_DOCTOR_PROBE_TIMEOUT_MS, RELAY_REQUEST_TIMEOUT_MS, RELAY_DOCTOR_PROBE_TIMEOUT_MS;
6542
- var init_capabilities = __esm({
6543
- "src/provider/capabilities.ts"() {
6152
+ function createOpenAiCompatibleModelInfo(modelId) {
6153
+ return {
6154
+ id: modelId,
6155
+ providerId: "openai-compatible",
6156
+ label: modelId,
6157
+ wireApi: "chat.completions",
6158
+ capabilities: {
6159
+ tools: true,
6160
+ reasoning: false,
6161
+ reasoningContentReplay: "never",
6162
+ streaming: true,
6163
+ usage: true,
6164
+ cache: "none"
6165
+ },
6166
+ request: {
6167
+ thinkingDefault: "disabled",
6168
+ maxOutputTokensParam: "max_tokens"
6169
+ },
6170
+ limit: {
6171
+ context: 128e3,
6172
+ output: 8e3
6173
+ }
6174
+ };
6175
+ }
6176
+ var DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_DOCTOR_PROBE_TIMEOUT_MS, RELAY_REQUEST_TIMEOUT_MS, RELAY_DOCTOR_PROBE_TIMEOUT_MS, PROVIDER_CATALOG, DEEPSEEK_MODEL_BASE, GPT_RESPONSES_MODEL_BASE, MODEL_CATALOG;
6177
+ var init_catalog = __esm({
6178
+ "src/provider/catalog.ts"() {
6544
6179
  "use strict";
6545
- DEFAULT_PROVIDER = "openai-compatible";
6546
6180
  DEFAULT_REQUEST_TIMEOUT_MS = 10 * 60 * 1e3;
6547
6181
  DEFAULT_DOCTOR_PROBE_TIMEOUT_MS = 1e4;
6548
6182
  RELAY_REQUEST_TIMEOUT_MS = 15 * 60 * 1e3;
6549
6183
  RELAY_DOCTOR_PROBE_TIMEOUT_MS = 45e3;
6184
+ PROVIDER_CATALOG = [
6185
+ {
6186
+ id: "deepseek",
6187
+ label: "DeepSeek official",
6188
+ apiKind: "deepseek-openai-compatible",
6189
+ transport: "standard",
6190
+ defaultBaseUrl: "https://api.deepseek.com",
6191
+ requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
6192
+ doctorProbeTimeoutMs: DEFAULT_DOCTOR_PROBE_TIMEOUT_MS
6193
+ },
6194
+ {
6195
+ id: "yls",
6196
+ label: "YLS Codex",
6197
+ apiKind: "openai-sdk",
6198
+ transport: "relay",
6199
+ defaultBaseUrl: "https://code.ylsagi.com/codex",
6200
+ requestTimeoutMs: RELAY_REQUEST_TIMEOUT_MS,
6201
+ doctorProbeTimeoutMs: RELAY_DOCTOR_PROBE_TIMEOUT_MS
6202
+ },
6203
+ {
6204
+ id: "ttapi",
6205
+ label: "TTAPI",
6206
+ apiKind: "openai-sdk",
6207
+ transport: "relay",
6208
+ defaultBaseUrl: "https://w.ciykj.cn",
6209
+ requestTimeoutMs: RELAY_REQUEST_TIMEOUT_MS,
6210
+ doctorProbeTimeoutMs: RELAY_DOCTOR_PROBE_TIMEOUT_MS
6211
+ },
6212
+ {
6213
+ id: "openai",
6214
+ label: "OpenAI official",
6215
+ apiKind: "openai-sdk",
6216
+ transport: "standard",
6217
+ defaultBaseUrl: "https://api.openai.com/v1",
6218
+ requestTimeoutMs: RELAY_REQUEST_TIMEOUT_MS,
6219
+ doctorProbeTimeoutMs: RELAY_DOCTOR_PROBE_TIMEOUT_MS
6220
+ },
6221
+ {
6222
+ id: "openai-compatible",
6223
+ label: "OpenAI-compatible",
6224
+ apiKind: "openai-compatible",
6225
+ transport: "standard",
6226
+ defaultBaseUrl: "",
6227
+ requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
6228
+ doctorProbeTimeoutMs: DEFAULT_DOCTOR_PROBE_TIMEOUT_MS
6229
+ }
6230
+ ];
6231
+ DEEPSEEK_MODEL_BASE = {
6232
+ wireApi: "chat.completions",
6233
+ capabilities: {
6234
+ tools: true,
6235
+ reasoning: true,
6236
+ reasoningContentReplay: "tool-call-required",
6237
+ streaming: true,
6238
+ usage: true,
6239
+ cache: "provider-automatic"
6240
+ },
6241
+ request: {
6242
+ thinkingDefault: "enabled",
6243
+ reasoningEffortDefault: "max",
6244
+ maxOutputTokensParam: "max_tokens"
6245
+ },
6246
+ limit: {
6247
+ context: 128e3,
6248
+ output: 8e3
6249
+ }
6250
+ };
6251
+ GPT_RESPONSES_MODEL_BASE = {
6252
+ wireApi: "responses",
6253
+ capabilities: {
6254
+ tools: true,
6255
+ reasoning: true,
6256
+ reasoningContentReplay: "never",
6257
+ streaming: true,
6258
+ usage: true,
6259
+ cache: "prompt-cache-key"
6260
+ },
6261
+ request: {
6262
+ thinkingDefault: "enabled",
6263
+ reasoningEffortDefault: "high",
6264
+ maxOutputTokensParam: "max_output_tokens"
6265
+ },
6266
+ limit: {
6267
+ context: 4e5,
6268
+ output: 128e3
6269
+ }
6270
+ };
6271
+ MODEL_CATALOG = [
6272
+ {
6273
+ id: "deepseek-v4-flash",
6274
+ providerId: "deepseek",
6275
+ label: "DeepSeek V4 Flash",
6276
+ ...DEEPSEEK_MODEL_BASE
6277
+ },
6278
+ {
6279
+ id: "deepseek-v4-pro",
6280
+ providerId: "deepseek",
6281
+ label: "DeepSeek V4 Pro",
6282
+ ...DEEPSEEK_MODEL_BASE
6283
+ },
6284
+ {
6285
+ id: "gpt-5.5",
6286
+ providerId: "yls",
6287
+ label: "GPT-5.5 via YLS",
6288
+ ...GPT_RESPONSES_MODEL_BASE
6289
+ },
6290
+ {
6291
+ id: "gpt-5.4",
6292
+ providerId: "yls",
6293
+ label: "GPT-5.4 via YLS",
6294
+ ...GPT_RESPONSES_MODEL_BASE,
6295
+ request: {
6296
+ ...GPT_RESPONSES_MODEL_BASE.request,
6297
+ reasoningEffortDefault: "xhigh"
6298
+ }
6299
+ },
6300
+ {
6301
+ id: "gpt-5.4",
6302
+ providerId: "ttapi",
6303
+ label: "GPT-5.4 via TTAPI",
6304
+ ...GPT_RESPONSES_MODEL_BASE,
6305
+ request: {
6306
+ ...GPT_RESPONSES_MODEL_BASE.request,
6307
+ thinkingDefault: "disabled",
6308
+ reasoningEffortDefault: "xhigh"
6309
+ }
6310
+ },
6311
+ {
6312
+ id: "gpt-5.5",
6313
+ providerId: "openai",
6314
+ label: "GPT-5.5",
6315
+ ...GPT_RESPONSES_MODEL_BASE
6316
+ },
6317
+ {
6318
+ id: "gpt-5.4",
6319
+ providerId: "openai",
6320
+ label: "GPT-5.4",
6321
+ ...GPT_RESPONSES_MODEL_BASE,
6322
+ request: {
6323
+ ...GPT_RESPONSES_MODEL_BASE.request,
6324
+ reasoningEffortDefault: "xhigh"
6325
+ }
6326
+ }
6327
+ ];
6328
+ }
6329
+ });
6330
+
6331
+ // src/provider/transport.ts
6332
+ function resolveProviderProbeKind(profile) {
6333
+ if (profile.provider.transport === "relay") {
6334
+ return profile.model.wireApi;
6335
+ }
6336
+ return "models";
6337
+ }
6338
+ function buildProviderProbeRequest(input) {
6339
+ return {
6340
+ endpoint: buildProviderProbeEndpoint(input.baseUrl, input.probe),
6341
+ method: input.probe === "models" ? "GET" : "POST",
6342
+ headers: {
6343
+ Authorization: `Bearer ${input.apiKey}`,
6344
+ ...input.probe === "models" ? {} : { "Content-Type": "application/json" }
6345
+ },
6346
+ body: buildProviderProbeBody(input.probe, input.model)
6347
+ };
6348
+ }
6349
+ function buildProviderProbeEndpoint(baseUrl, probe) {
6350
+ if (probe === "responses") {
6351
+ return buildEndpoint(baseUrl, "responses");
6352
+ }
6353
+ if (probe === "chat.completions") {
6354
+ return buildEndpoint(baseUrl, "chat/completions");
6355
+ }
6356
+ return buildEndpoint(baseUrl, "models");
6357
+ }
6358
+ function buildProviderProbeBody(probe, model) {
6359
+ if (probe === "models") {
6360
+ return void 0;
6361
+ }
6362
+ if (probe === "responses") {
6363
+ return JSON.stringify({
6364
+ model,
6365
+ input: [
6366
+ {
6367
+ role: "user",
6368
+ content: "Return ok."
6369
+ }
6370
+ ],
6371
+ max_output_tokens: 8
6372
+ });
6373
+ }
6374
+ return JSON.stringify({
6375
+ model,
6376
+ messages: [
6377
+ {
6378
+ role: "user",
6379
+ content: "Return ok."
6380
+ }
6381
+ ],
6382
+ max_tokens: 8,
6383
+ stream: false
6384
+ });
6385
+ }
6386
+ function buildEndpoint(baseUrl, path59) {
6387
+ try {
6388
+ return new URL(path59, ensureTrailingSlash(baseUrl)).toString();
6389
+ } catch {
6390
+ throw new Error(
6391
+ `User-fixable error: \`KITTY_BASE_URL\` is not a valid URL: ${baseUrl}. Fix it and rerun \`kitty doctor\`.`
6392
+ );
6393
+ }
6394
+ }
6395
+ function ensureTrailingSlash(baseUrl) {
6396
+ return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
6397
+ }
6398
+ var init_transport = __esm({
6399
+ "src/provider/transport.ts"() {
6400
+ "use strict";
6550
6401
  }
6551
6402
  });
6552
6403
 
6553
6404
  // src/provider/connection.ts
6554
6405
  async function probeProviderConnection(input) {
6555
- const capabilities = resolveProviderCapabilities({
6406
+ const profile = resolveModelProfile({
6556
6407
  provider: input.provider,
6557
6408
  model: input.model
6558
6409
  });
6410
+ const probe = resolveProviderProbeKind(profile);
6559
6411
  const fetchImpl = input.fetchImpl ?? fetch;
6560
- const probeTimeoutMs = capabilities.doctorProbeTimeoutMs;
6412
+ const probeTimeoutMs = profile.provider.doctorProbeTimeoutMs;
6561
6413
  let lastFailure;
6562
6414
  for (const candidateBaseUrl of buildProviderBaseUrlCandidates(input.baseUrl)) {
6563
- const endpoint = buildModelsEndpoint(candidateBaseUrl);
6415
+ const request = buildProviderProbeRequest({
6416
+ baseUrl: candidateBaseUrl,
6417
+ apiKey: input.apiKey,
6418
+ model: profile.model.id,
6419
+ probe
6420
+ });
6564
6421
  let response;
6565
6422
  try {
6566
- response = await fetchImpl(endpoint, {
6567
- method: "GET",
6568
- headers: {
6569
- Authorization: `Bearer ${input.apiKey}`
6570
- },
6423
+ response = await fetchImpl(request.endpoint, {
6424
+ method: request.method,
6425
+ headers: request.headers,
6426
+ body: request.body,
6571
6427
  signal: AbortSignal.timeout(probeTimeoutMs)
6572
6428
  });
6573
6429
  } catch (error) {
@@ -6581,7 +6437,7 @@ async function probeProviderConnection(input) {
6581
6437
  if (response.status === 404) {
6582
6438
  lastFailure = {
6583
6439
  kind: "user",
6584
- message: `User-fixable error: ${endpoint} returned 404. Check whether \`KITTY_BASE_URL\` is the correct OpenAI-compatible API base URL.`,
6440
+ message: `User-fixable error: provider endpoint returned 404 at ${request.endpoint}. Check \`KITTY_PROVIDER\`, \`KITTY_MODEL\`, and \`KITTY_BASE_URL\` together; this provider uses ${profile.model.wireApi}.`,
6585
6441
  probeTimeoutMs
6586
6442
  };
6587
6443
  continue;
@@ -6607,10 +6463,27 @@ async function probeProviderConnection(input) {
6607
6463
  probeTimeoutMs
6608
6464
  };
6609
6465
  }
6466
+ if (probe === "responses") {
6467
+ return {
6468
+ kind: "ok",
6469
+ probe: "responses",
6470
+ resolvedBaseUrl: candidateBaseUrl,
6471
+ probeTimeoutMs
6472
+ };
6473
+ }
6474
+ if (probe === "chat.completions") {
6475
+ return {
6476
+ kind: "ok",
6477
+ probe: "chat.completions",
6478
+ resolvedBaseUrl: candidateBaseUrl,
6479
+ probeTimeoutMs
6480
+ };
6481
+ }
6610
6482
  const payload = await response.json().catch(() => null);
6611
6483
  const models = Array.isArray(payload?.data) ? payload.data.length : 0;
6612
6484
  return {
6613
6485
  kind: "ok",
6486
+ probe: "models",
6614
6487
  models,
6615
6488
  resolvedBaseUrl: candidateBaseUrl,
6616
6489
  probeTimeoutMs
@@ -6631,23 +6504,14 @@ function buildProviderBaseUrlCandidates(baseUrl) {
6631
6504
  try {
6632
6505
  const parsed = new URL(normalized);
6633
6506
  if (parsed.pathname === "" || parsed.pathname === "/") {
6634
- candidates.push(trimTrailingSlash(new URL("v1", ensureTrailingSlash(parsed.toString())).toString()));
6507
+ candidates.push(trimTrailingSlash(new URL("v1", ensureTrailingSlash2(parsed.toString())).toString()));
6635
6508
  }
6636
6509
  } catch {
6637
6510
  return candidates;
6638
6511
  }
6639
6512
  return [...new Set(candidates)];
6640
6513
  }
6641
- function buildModelsEndpoint(baseUrl) {
6642
- try {
6643
- return new URL("models", ensureTrailingSlash(baseUrl)).toString();
6644
- } catch {
6645
- throw new Error(
6646
- `User-fixable error: \`KITTY_BASE_URL\` is not a valid URL: ${baseUrl}. Fix it and rerun \`kitty doctor\`.`
6647
- );
6648
- }
6649
- }
6650
- function ensureTrailingSlash(baseUrl) {
6514
+ function ensureTrailingSlash2(baseUrl) {
6651
6515
  return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
6652
6516
  }
6653
6517
  function trimTrailingSlash(baseUrl) {
@@ -6671,7 +6535,8 @@ function buildNetworkErrorMessage(baseUrl, error) {
6671
6535
  var init_connection = __esm({
6672
6536
  "src/provider/connection.ts"() {
6673
6537
  "use strict";
6674
- init_capabilities();
6538
+ init_catalog();
6539
+ init_transport();
6675
6540
  }
6676
6541
  });
6677
6542
 
@@ -6684,18 +6549,27 @@ var init_providerPresets = __esm({
6684
6549
  "src/config/providerPresets.ts"() {
6685
6550
  "use strict";
6686
6551
  PROVIDER_PRESETS = [
6552
+ {
6553
+ label: "YLS Codex + GPT-5.5",
6554
+ provider: "yls",
6555
+ baseUrl: "https://code.ylsagi.com/codex",
6556
+ model: "gpt-5.5",
6557
+ thinking: "enabled",
6558
+ reasoningEffort: "high",
6559
+ activeByDefault: false
6560
+ },
6687
6561
  {
6688
6562
  label: "YLS Codex + GPT-5.4",
6689
- provider: "openai",
6563
+ provider: "yls",
6690
6564
  baseUrl: "https://code.ylsagi.com/codex",
6691
6565
  model: "gpt-5.4",
6692
- thinking: "disabled",
6566
+ thinking: "enabled",
6693
6567
  reasoningEffort: "xhigh",
6694
6568
  activeByDefault: false
6695
6569
  },
6696
6570
  {
6697
6571
  label: "TTAPI + GPT-5.4",
6698
- provider: "openai",
6572
+ provider: "ttapi",
6699
6573
  baseUrl: "https://w.ciykj.cn",
6700
6574
  model: "gpt-5.4",
6701
6575
  thinking: "disabled",
@@ -6739,7 +6613,8 @@ async function inspectConfigPreflight(rootDir) {
6739
6613
  const model = parsedEnv[KITTY_ENV.model] ?? "";
6740
6614
  const baseUrl = parsedEnv[KITTY_ENV.baseUrl] ?? "";
6741
6615
  const providerPreset = readProviderPresetLabel({ provider, model, baseUrl });
6742
- const ready = files.every((file) => file.exists) && missingKeys.length === 0;
6616
+ const catalog = readCatalogProfile({ provider, model });
6617
+ const ready = files.every((file) => file.exists) && missingKeys.length === 0 && !catalog.error;
6743
6618
  return {
6744
6619
  rootDir: normalizedRoot,
6745
6620
  kittyDir,
@@ -6748,6 +6623,10 @@ async function inspectConfigPreflight(rootDir) {
6748
6623
  activeKeys,
6749
6624
  missingKeys,
6750
6625
  providerPreset,
6626
+ providerProfile: catalog.providerProfile,
6627
+ modelProfile: catalog.modelProfile,
6628
+ wireApi: catalog.wireApi,
6629
+ catalogError: catalog.error,
6751
6630
  provider,
6752
6631
  model,
6753
6632
  baseUrl,
@@ -6773,6 +6652,10 @@ function formatConfigPreflightReport(report) {
6773
6652
  `model: ${report.env.model || "(missing)"}`,
6774
6653
  `baseUrl: ${report.env.baseUrl || "(missing)"}`,
6775
6654
  `provider preset: ${formatProviderPresetFact(report)}`,
6655
+ `provider profile: ${report.env.providerProfile ?? "(unresolved)"}`,
6656
+ `model profile: ${report.env.modelProfile ?? "(unresolved)"}`,
6657
+ `wire API: ${report.env.wireApi ?? "(unresolved)"}`,
6658
+ report.env.catalogError ? `catalog: ${report.env.catalogError}` : "catalog: ok",
6776
6659
  `api key: ${report.env.apiKeyPresent ? "present" : "missing"}`,
6777
6660
  `preflight: ${report.ready ? "ready" : "not_ready"}`,
6778
6661
  "next:",
@@ -6821,6 +6704,23 @@ function readProviderPresetLabel(input) {
6821
6704
  (preset) => preset.provider === input.provider && preset.model === input.model && preset.baseUrl === input.baseUrl
6822
6705
  )?.label;
6823
6706
  }
6707
+ function readCatalogProfile(input) {
6708
+ if (!input.provider || !input.model) {
6709
+ return {};
6710
+ }
6711
+ try {
6712
+ const profile = resolveModelProfile(input);
6713
+ return {
6714
+ providerProfile: profile.provider.label,
6715
+ modelProfile: profile.model.label,
6716
+ wireApi: profile.model.wireApi
6717
+ };
6718
+ } catch (error) {
6719
+ return {
6720
+ error: error instanceof Error ? error.message : String(error)
6721
+ };
6722
+ }
6723
+ }
6824
6724
  function formatProviderPresetFact(report) {
6825
6725
  if (report.env.providerPreset) {
6826
6726
  return report.env.providerPreset;
@@ -6847,6 +6747,7 @@ var init_preflight = __esm({
6847
6747
  import_dotenv2 = __toESM(require("dotenv"));
6848
6748
  init_envKeys();
6849
6749
  init_providerPresets();
6750
+ init_catalog();
6850
6751
  init_statePaths();
6851
6752
  }
6852
6753
  });
@@ -6901,6 +6802,16 @@ function registerDoctorCommand(program, options) {
6901
6802
  ui.info(`provider: ${runtime.config.provider}`);
6902
6803
  ui.info(`model: ${runtime.config.model}`);
6903
6804
  ui.info(`baseUrl: ${runtime.config.baseUrl}`);
6805
+ const profile = resolveModelProfile({
6806
+ provider: runtime.config.provider,
6807
+ model: runtime.config.model
6808
+ });
6809
+ ui.info(`provider profile: ${profile.provider.label}`);
6810
+ ui.info(`model profile: ${profile.model.label}`);
6811
+ ui.info(`wire API: ${profile.model.wireApi}`);
6812
+ ui.info(`reasoning replay: ${profile.model.capabilities.reasoningContentReplay}`);
6813
+ ui.info(`context limit: ${profile.model.limit.context}`);
6814
+ ui.info(`output limit: ${profile.model.limit.output}`);
6904
6815
  if (!runtime.config.apiKey.trim()) {
6905
6816
  throw new Error(
6906
6817
  "User-fixable error: API key not found. Set `KITTY_API_KEY` in the current project `.kitty/.env`, then rerun `kitty doctor`."
@@ -6914,7 +6825,7 @@ function registerDoctorCommand(program, options) {
6914
6825
  apiKey: runtime.config.apiKey
6915
6826
  });
6916
6827
  if (diagnosis.kind === "ok") {
6917
- ui.success(`Provider reachable. models=${diagnosis.models}`);
6828
+ ui.success(formatProviderProbeSuccess(diagnosis));
6918
6829
  if (diagnosis.resolvedBaseUrl !== runtime.config.baseUrl) {
6919
6830
  ui.info(`resolvedBaseUrl: ${diagnosis.resolvedBaseUrl}`);
6920
6831
  }
@@ -6927,12 +6838,19 @@ function registerDoctorCommand(program, options) {
6927
6838
  throw new Error(diagnosis.message);
6928
6839
  });
6929
6840
  }
6841
+ function formatProviderProbeSuccess(diagnosis) {
6842
+ if (diagnosis.probe === "responses") {
6843
+ return "Provider reachable. responses probe ok";
6844
+ }
6845
+ return `Provider reachable. models=${diagnosis.models ?? 0}`;
6846
+ }
6930
6847
  var import_node_path23;
6931
6848
  var init_doctor = __esm({
6932
6849
  "src/cli/commands/doctor.ts"() {
6933
6850
  "use strict";
6934
6851
  import_node_path23 = __toESM(require("path"));
6935
6852
  init_connection();
6853
+ init_catalog();
6936
6854
  init_preflight();
6937
6855
  init_console();
6938
6856
  init_stdio();
@@ -9300,7 +9218,7 @@ function isPathIgnored(targetPath, rules, isDirectory3 = false) {
9300
9218
  if (!relativePath) {
9301
9219
  continue;
9302
9220
  }
9303
- const candidate = isDirectory3 ? ensureTrailingSlash2(relativePath) : relativePath;
9221
+ const candidate = isDirectory3 ? ensureTrailingSlash3(relativePath) : relativePath;
9304
9222
  if (!rule.matcher.test(candidate)) {
9305
9223
  continue;
9306
9224
  }
@@ -9400,7 +9318,7 @@ function toRelativePosix(baseDir, targetPath) {
9400
9318
  }
9401
9319
  return relativePath.replace(/\\/g, "/");
9402
9320
  }
9403
- function ensureTrailingSlash2(value) {
9321
+ function ensureTrailingSlash3(value) {
9404
9322
  return value.endsWith("/") ? value : `${value}/`;
9405
9323
  }
9406
9324
  function normalizeFastGlobIgnorePattern(rule) {
@@ -10549,17 +10467,17 @@ __export(cachePolicy_exports, {
10549
10467
  resolveProviderCachePolicy: () => resolveProviderCachePolicy
10550
10468
  });
10551
10469
  function resolveProviderCachePolicy(input) {
10552
- const capabilities = resolveProviderCapabilities(input);
10553
- if (capabilities.provider === "openai") {
10470
+ const profile = resolveModelProfile(input);
10471
+ if (profile.model.capabilities.cache === "prompt-cache-key") {
10554
10472
  return {
10555
10473
  provider: "openai",
10556
10474
  automaticPrefixCache: true,
10557
10475
  promptCacheKey: buildPromptCacheKey(input)
10558
10476
  };
10559
10477
  }
10560
- if (capabilities.provider === "deepseek") {
10478
+ if (profile.model.capabilities.cache === "provider-automatic") {
10561
10479
  return {
10562
- provider: "deepseek",
10480
+ provider: profile.provider.id === "deepseek" ? "deepseek" : "generic",
10563
10481
  automaticPrefixCache: true
10564
10482
  };
10565
10483
  }
@@ -10586,852 +10504,33 @@ function stableHash(value) {
10586
10504
  var init_cachePolicy = __esm({
10587
10505
  "src/provider/cachePolicy.ts"() {
10588
10506
  "use strict";
10589
- init_capabilities();
10590
- }
10591
- });
10592
-
10593
- // src/provider/apiRetry.ts
10594
- async function withApiRetries(operation, abortSignal) {
10595
- let lastError;
10596
- for (let attempt = 1; attempt <= API_MAX_RETRIES; attempt += 1) {
10597
- try {
10598
- return await operation();
10599
- } catch (error) {
10600
- if (isAbortError(error)) {
10601
- throw error;
10602
- }
10603
- lastError = error;
10604
- if (!isRetryableApiError(error) || attempt === API_MAX_RETRIES) {
10605
- break;
10606
- }
10607
- await sleepWithSignal(API_RETRY_BASE_DELAY_MS * attempt, abortSignal);
10608
- }
10609
- }
10610
- throw lastError;
10611
- }
10612
- function isRetryableApiError(error) {
10613
- const status = error.status;
10614
- if (typeof status === "number") {
10615
- return status === 408 || status === 409 || status === 429 || status >= 500;
10616
- }
10617
- const message = String(error.message ?? error).toLowerCase();
10618
- return message.includes("timeout") || message.includes("network") || message.includes("connection error") || message.includes("connection reset") || message.includes("econnreset") || message.includes("econnrefused") || message.includes("connect timeout") || message.includes("temporarily") || message.includes("rate limit") || message.includes("overloaded");
10619
- }
10620
- var API_MAX_RETRIES, API_RETRY_BASE_DELAY_MS;
10621
- var init_apiRetry = __esm({
10622
- "src/provider/apiRetry.ts"() {
10623
- "use strict";
10624
- init_abort();
10625
- API_MAX_RETRIES = 3;
10626
- API_RETRY_BASE_DELAY_MS = 1200;
10627
- }
10628
- });
10629
-
10630
- // src/provider/chatRequestBody.ts
10631
- function buildProviderRequestBody(input) {
10632
- const capabilities = resolveProviderCapabilities(input);
10633
- const thinking = capabilities.provider === "deepseek" ? resolveDeepSeekThinking(input.messages, input.thinking ?? "enabled") : input.thinking;
10634
- const body = {
10635
- model: input.model,
10636
- messages: toChatCompletionMessages(input.messages),
10637
- tools: input.tools,
10638
- stream: input.stream
10639
- };
10640
- if (capabilities.provider !== "deepseek" && input.tools?.length) {
10641
- body.tool_choice = "auto";
10642
- }
10643
- if (input.stream) {
10644
- body.stream_options = {
10645
- include_usage: true
10646
- };
10647
- }
10648
- const cachePolicy = resolveProviderCachePolicy(input);
10649
- if (cachePolicy.promptCacheKey) {
10650
- body.prompt_cache_key = cachePolicy.promptCacheKey;
10651
- }
10652
- if (typeof input.maxOutputTokens === "number" && Number.isFinite(input.maxOutputTokens)) {
10653
- body.max_tokens = Math.max(1, Math.trunc(input.maxOutputTokens));
10654
- }
10655
- if (capabilities.provider === "deepseek") {
10656
- body.thinking = { type: thinking };
10657
- if (thinking === "enabled") {
10658
- body.reasoning_effort = normalizeDeepSeekReasoningEffort(input.reasoningEffort ?? capabilities.defaultReasoningEffort);
10659
- }
10660
- } else if (input.forceReasoning || capabilities.defaultReasoningEnabled) {
10661
- body.thinking = { type: "enabled" };
10662
- }
10663
- return body;
10664
- }
10665
- function resolveDeepSeekThinking(messages, requested) {
10666
- if (requested === "disabled") {
10667
- return "disabled";
10668
- }
10669
- return hasUnreplayableAssistantReasoning(messages) ? "disabled" : "enabled";
10670
- }
10671
- function hasUnreplayableAssistantReasoning(messages) {
10672
- return messages.some(
10673
- (message) => message.role === "assistant" && Array.isArray(message.toolCalls) && message.toolCalls.length > 0 && message.reasoningContent === void 0
10674
- );
10675
- }
10676
- function normalizeDeepSeekReasoningEffort(effort) {
10677
- if (effort === void 0 || effort === "minimal" || effort === "low" || effort === "medium" || effort === "high") {
10678
- return "high";
10679
- }
10680
- if (effort === "xhigh" || effort === "max") {
10681
- return "max";
10682
- }
10683
- return "high";
10684
- }
10685
- var init_chatRequestBody = __esm({
10686
- "src/provider/chatRequestBody.ts"() {
10687
- "use strict";
10688
- init_cachePolicy();
10689
- init_capabilities();
10690
- init_chatCompletionsAdapter();
10691
- }
10692
- });
10693
-
10694
- // src/provider/chatCompletionsAdapter.ts
10695
- function abortStream(stream) {
10696
- try {
10697
- stream?.controller?.abort();
10698
- } catch {
10699
- }
10700
- }
10701
- function toChatCompletionMessages(messages) {
10702
- return messages.map((message) => {
10703
- if (message.role === "tool") {
10704
- return {
10705
- role: "tool",
10706
- content: message.content ?? "",
10707
- tool_call_id: message.toolCallId ?? ""
10708
- };
10709
- }
10710
- if (message.role === "assistant" && message.toolCalls?.length) {
10711
- const assistantMessage = {
10712
- role: "assistant",
10713
- content: message.content ?? "",
10714
- tool_calls: message.toolCalls
10715
- };
10716
- if (message.reasoningContent !== void 0) {
10717
- assistantMessage.reasoning_content = message.reasoningContent;
10718
- }
10719
- return assistantMessage;
10720
- }
10721
- const baseMessage = {
10722
- role: message.role,
10723
- content: message.content ?? "",
10724
- name: message.name
10725
- };
10726
- if (message.role === "assistant" && message.reasoningContent !== void 0) {
10727
- baseMessage.reasoning_content = message.reasoningContent;
10728
- }
10729
- return baseMessage;
10730
- });
10731
- }
10732
- var chatCompletionsAdapter;
10733
- var init_chatCompletionsAdapter = __esm({
10734
- "src/provider/chatCompletionsAdapter.ts"() {
10735
- "use strict";
10736
- init_messages();
10737
- init_chatRequestBody();
10738
- init_usageNormalizer();
10739
- init_abort();
10740
- chatCompletionsAdapter = {
10741
- wireApi: "chat.completions",
10742
- async fetchStreaming(client, request) {
10743
- const startedAt = Date.now();
10744
- let usage;
10745
- throwIfAborted(request.abortSignal, "Streaming request aborted");
10746
- try {
10747
- const stream = await client.chat.completions.create(
10748
- {
10749
- ...buildProviderRequestBody({
10750
- provider: request.provider,
10751
- model: request.model,
10752
- messages: request.messages,
10753
- tools: request.tools,
10754
- stream: true,
10755
- forceReasoning: request.forceReasoning,
10756
- thinking: request.thinking,
10757
- reasoningEffort: request.reasoningEffort,
10758
- maxOutputTokens: request.maxOutputTokens,
10759
- sessionId: request.sessionId,
10760
- projectRoot: request.projectRoot
10761
- }),
10762
- signal: request.abortSignal
10763
- }
10764
- );
10765
- if (request.abortSignal?.aborted) {
10766
- abortStream(stream);
10767
- throw createAbortError("Streaming aborted");
10768
- }
10769
- let content = "";
10770
- let reasoningContent = "";
10771
- const toolCallParts = /* @__PURE__ */ new Map();
10772
- for await (const chunk of stream) {
10773
- if (request.abortSignal?.aborted) {
10774
- abortStream(stream);
10775
- throw createAbortError("Streaming aborted");
10776
- }
10777
- usage = normalizeProviderUsage(chunk.usage) ?? usage;
10778
- const delta = chunk.choices?.[0]?.delta;
10779
- if (!delta) {
10780
- continue;
10781
- }
10782
- if (typeof delta.content === "string" && delta.content.length > 0) {
10783
- content += delta.content;
10784
- request.callbacks?.onAssistantDelta?.(delta.content);
10785
- }
10786
- if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
10787
- reasoningContent += delta.reasoning_content;
10788
- request.callbacks?.onReasoningDelta?.(delta.reasoning_content);
10789
- }
10790
- if (Array.isArray(delta.tool_calls)) {
10791
- for (const toolCall of delta.tool_calls) {
10792
- const index = typeof toolCall.index === "number" ? toolCall.index : 0;
10793
- const existing = toolCallParts.get(index) ?? {
10794
- id: toolCall.id ?? `tool-${index}`,
10795
- name: "",
10796
- arguments: ""
10797
- };
10798
- if (toolCall.id) {
10799
- existing.id = toolCall.id;
10800
- }
10801
- if (toolCall.function?.name) {
10802
- existing.name += toolCall.function.name;
10803
- }
10804
- if (toolCall.function?.arguments) {
10805
- existing.arguments += toolCall.function.arguments;
10806
- }
10807
- toolCallParts.set(index, existing);
10808
- }
10809
- }
10810
- }
10811
- return {
10812
- content: content.length > 0 ? content : null,
10813
- reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
10814
- streamedAssistantContent: content.length > 0,
10815
- streamedReasoningContent: reasoningContent.length > 0,
10816
- toolCalls: [...toolCallParts.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
10817
- id: toolCall.id,
10818
- type: "function",
10819
- function: {
10820
- name: toolCall.name,
10821
- arguments: toolCall.arguments
10822
- }
10823
- }))
10824
- };
10825
- } finally {
10826
- request.onRequestMetric?.({
10827
- durationMs: Date.now() - startedAt,
10828
- usage
10829
- });
10830
- }
10831
- },
10832
- async fetchNonStreaming(client, request) {
10833
- const startedAt = Date.now();
10834
- let usage;
10835
- throwIfAborted(request.abortSignal, "Request aborted");
10836
- try {
10837
- const completion = await client.chat.completions.create(
10838
- {
10839
- ...buildProviderRequestBody({
10840
- provider: request.provider,
10841
- model: request.model,
10842
- messages: request.messages,
10843
- tools: request.tools,
10844
- stream: false,
10845
- forceReasoning: request.forceReasoning,
10846
- thinking: request.thinking,
10847
- reasoningEffort: request.reasoningEffort,
10848
- maxOutputTokens: request.maxOutputTokens,
10849
- sessionId: request.sessionId,
10850
- projectRoot: request.projectRoot
10851
- }),
10852
- signal: request.abortSignal
10853
- }
10854
- );
10855
- usage = normalizeProviderUsage(completion.usage);
10856
- const message = completion.choices[0]?.message;
10857
- if (!message) {
10858
- throw new Error("API returned no message.");
10859
- }
10860
- return {
10861
- content: typeof message.content === "string" ? message.content : collapseContentParts(message.content),
10862
- reasoningContent: readReasoningContent(message),
10863
- streamedAssistantContent: false,
10864
- streamedReasoningContent: false,
10865
- toolCalls: (message.tool_calls ?? []).filter((call) => call.type === "function").map((call) => ({
10866
- id: call.id,
10867
- type: "function",
10868
- function: {
10869
- name: call.function.name,
10870
- arguments: call.function.arguments
10871
- }
10872
- }))
10873
- };
10874
- } finally {
10875
- request.onRequestMetric?.({
10876
- durationMs: Date.now() - startedAt,
10877
- usage
10878
- });
10879
- }
10880
- }
10881
- };
10882
- }
10883
- });
10884
-
10885
- // src/provider/responsesAdapter.ts
10886
- function buildResponsesRequestBody(request) {
10887
- const capabilities = resolveProviderCapabilities({
10888
- provider: request.provider,
10889
- model: request.model
10890
- });
10891
- const body = {
10892
- model: request.model,
10893
- input: toResponsesInput(request.messages),
10894
- tools: request.tools?.map((tool) => ({
10895
- type: "function",
10896
- name: tool.function.name,
10897
- description: tool.function.description,
10898
- parameters: tool.function.parameters ?? null,
10899
- strict: false
10900
- })),
10901
- tool_choice: request.tools?.length ? "auto" : void 0
10902
- };
10903
- if (typeof request.maxOutputTokens === "number" && Number.isFinite(request.maxOutputTokens)) {
10904
- body.max_output_tokens = Math.max(1, Math.trunc(request.maxOutputTokens));
10905
- }
10906
- const cachePolicy = resolveProviderCachePolicy({
10907
- provider: request.provider,
10908
- model: request.model,
10909
- sessionId: request.sessionId,
10910
- projectRoot: request.projectRoot
10911
- });
10912
- if (cachePolicy.promptCacheKey) {
10913
- body.prompt_cache_key = cachePolicy.promptCacheKey;
10914
- }
10915
- const reasoningEffort = normalizeResponsesReasoningEffort(
10916
- request.reasoningEffort ?? capabilities.defaultReasoningEffort
10917
- );
10918
- if (request.forceReasoning || capabilities.defaultReasoningEnabled || reasoningEffort) {
10919
- body.reasoning = {
10920
- effort: reasoningEffort ?? "high",
10921
- summary: "detailed"
10922
- };
10923
- }
10924
- return body;
10925
- }
10926
- function normalizeResponsesReasoningEffort(effort) {
10927
- return effort === "max" ? void 0 : effort;
10928
- }
10929
- function toResponsesInput(messages) {
10930
- const items = [];
10931
- for (const message of messages) {
10932
- if (message.role === "tool") {
10933
- items.push({
10934
- type: "function_call_output",
10935
- call_id: message.toolCallId ?? "",
10936
- output: message.content ?? ""
10937
- });
10938
- continue;
10939
- }
10940
- if (message.role === "assistant" && message.toolCalls?.length) {
10941
- if (typeof message.content === "string" && message.content.trim().length > 0) {
10942
- items.push({
10943
- type: "message",
10944
- role: "assistant",
10945
- content: message.content
10946
- });
10947
- }
10948
- for (const toolCall of message.toolCalls) {
10949
- items.push({
10950
- type: "function_call",
10951
- call_id: toolCall.id,
10952
- name: toolCall.function.name,
10953
- arguments: toolCall.function.arguments
10954
- });
10955
- }
10956
- continue;
10957
- }
10958
- items.push({
10959
- type: "message",
10960
- role: message.role,
10961
- content: message.content ?? ""
10962
- });
10963
- }
10964
- return items;
10965
- }
10966
- function normalizeOutputText(response) {
10967
- const outputText = response.output_text;
10968
- if (typeof outputText === "string" && outputText.trim().length > 0) {
10969
- return outputText;
10970
- }
10971
- const output = response.output;
10972
- if (!Array.isArray(output)) {
10973
- return null;
10974
- }
10975
- const fragments = output.flatMap((item) => {
10976
- if (!item || typeof item !== "object" || item.type !== "message") {
10977
- return [];
10978
- }
10979
- const content = item.content;
10980
- if (!Array.isArray(content)) {
10981
- return [];
10982
- }
10983
- return content.flatMap((part) => {
10984
- if (!part || typeof part !== "object" || part.type !== "output_text") {
10985
- return [];
10986
- }
10987
- return typeof part.text === "string" ? [part.text] : [];
10988
- });
10989
- });
10990
- return fragments.length > 0 ? fragments.join("") : null;
10991
- }
10992
- function readResponseToolCalls(response) {
10993
- const output = response.output;
10994
- if (!Array.isArray(output)) {
10995
- return [];
10996
- }
10997
- return output.filter((item) => Boolean(item) && typeof item === "object" && item.type === "function_call").map((item) => ({
10998
- id: item.call_id ?? item.id ?? crypto.randomUUID(),
10999
- type: "function",
11000
- function: {
11001
- name: item.name ?? "",
11002
- arguments: item.arguments ?? ""
11003
- }
11004
- }));
11005
- }
11006
- function readResponseReasoning(response) {
11007
- const output = response.output;
11008
- if (!Array.isArray(output)) {
11009
- return void 0;
11010
- }
11011
- const fragments = output.flatMap((item) => {
11012
- if (!item || typeof item !== "object" || item.type !== "reasoning") {
11013
- return [];
11014
- }
11015
- const reasoningItem = item;
11016
- const summary = Array.isArray(reasoningItem.summary) ? reasoningItem.summary.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
11017
- const content = Array.isArray(reasoningItem.content) ? reasoningItem.content.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
11018
- return [...content, ...summary];
11019
- });
11020
- return fragments.length > 0 ? fragments.join("") : void 0;
11021
- }
11022
- function abortStream2(stream) {
11023
- try {
11024
- stream?.controller?.abort();
11025
- } catch {
11026
- }
11027
- }
11028
- var responsesAdapter;
11029
- var init_responsesAdapter = __esm({
11030
- "src/provider/responsesAdapter.ts"() {
11031
- "use strict";
11032
- init_capabilities();
11033
- init_cachePolicy();
11034
- init_usageNormalizer();
11035
- init_abort();
11036
- responsesAdapter = {
11037
- wireApi: "responses",
11038
- async fetchStreaming(client, request) {
11039
- const startedAt = Date.now();
11040
- let usage;
11041
- throwIfAborted(request.abortSignal, "Streaming request aborted");
11042
- try {
11043
- const stream = await client.responses.create(
11044
- {
11045
- ...buildResponsesRequestBody(request),
11046
- stream: true
11047
- },
11048
- {
11049
- signal: request.abortSignal
11050
- }
11051
- );
11052
- if (request.abortSignal?.aborted) {
11053
- abortStream2(stream);
11054
- throw createAbortError("Streaming aborted");
11055
- }
11056
- let content = "";
11057
- let reasoningContent = "";
11058
- const toolCalls = /* @__PURE__ */ new Map();
11059
- for await (const event of stream) {
11060
- if (request.abortSignal?.aborted) {
11061
- abortStream2(stream);
11062
- throw createAbortError("Streaming aborted");
11063
- }
11064
- usage = normalizeProviderUsage(event.response?.usage) ?? usage;
11065
- if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
11066
- content += event.delta;
11067
- request.callbacks?.onAssistantDelta?.(event.delta);
11068
- continue;
11069
- }
11070
- if ((event.type === "response.reasoning_text.delta" || event.type === "response.reasoning_summary_text.delta") && typeof event.delta === "string") {
11071
- reasoningContent += event.delta;
11072
- request.callbacks?.onReasoningDelta?.(event.delta);
11073
- continue;
11074
- }
11075
- if (event.type === "response.function_call_arguments.delta" && typeof event.delta === "string") {
11076
- const index = typeof event.output_index === "number" ? event.output_index : 0;
11077
- const existing = toolCalls.get(index) ?? {
11078
- id: event.item_id ?? `tool-${index}`,
11079
- name: "",
11080
- arguments: ""
11081
- };
11082
- existing.arguments += event.delta;
11083
- toolCalls.set(index, existing);
11084
- continue;
11085
- }
11086
- if (event.type === "response.function_call_arguments.done") {
11087
- const index = typeof event.output_index === "number" ? event.output_index : 0;
11088
- const existing = toolCalls.get(index) ?? {
11089
- id: event.item_id ?? `tool-${index}`,
11090
- name: "",
11091
- arguments: ""
11092
- };
11093
- if (typeof event.name === "string") {
11094
- existing.name = event.name;
11095
- }
11096
- if (typeof event.arguments === "string" && event.arguments.length > 0) {
11097
- existing.arguments = event.arguments;
11098
- }
11099
- toolCalls.set(index, existing);
11100
- continue;
11101
- }
11102
- if (event.type === "response.output_item.done" && event.item?.type === "function_call") {
11103
- const index = typeof event.output_index === "number" ? event.output_index : 0;
11104
- toolCalls.set(index, {
11105
- id: event.item.call_id ?? event.item.id ?? `tool-${index}`,
11106
- name: event.item.name ?? "",
11107
- arguments: event.item.arguments ?? ""
11108
- });
11109
- }
11110
- }
11111
- return {
11112
- content: content.length > 0 ? content : null,
11113
- reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
11114
- streamedAssistantContent: content.length > 0,
11115
- streamedReasoningContent: reasoningContent.length > 0,
11116
- toolCalls: [...toolCalls.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
11117
- id: toolCall.id,
11118
- type: "function",
11119
- function: {
11120
- name: toolCall.name,
11121
- arguments: toolCall.arguments
11122
- }
11123
- }))
11124
- };
11125
- } finally {
11126
- request.onRequestMetric?.({
11127
- durationMs: Date.now() - startedAt,
11128
- usage
11129
- });
11130
- }
11131
- },
11132
- async fetchNonStreaming(client, request) {
11133
- const startedAt = Date.now();
11134
- let usage;
11135
- throwIfAborted(request.abortSignal, "Request aborted");
11136
- try {
11137
- const response = await client.responses.create(
11138
- {
11139
- ...buildResponsesRequestBody(request),
11140
- stream: false
11141
- },
11142
- {
11143
- signal: request.abortSignal
11144
- }
11145
- );
11146
- usage = normalizeProviderUsage(response.usage);
11147
- return {
11148
- content: normalizeOutputText(response),
11149
- reasoningContent: readResponseReasoning(response),
11150
- streamedAssistantContent: false,
11151
- streamedReasoningContent: false,
11152
- toolCalls: readResponseToolCalls(response)
11153
- };
11154
- } finally {
11155
- request.onRequestMetric?.({
11156
- durationMs: Date.now() - startedAt,
11157
- usage
11158
- });
11159
- }
11160
- }
11161
- };
10507
+ init_catalog();
11162
10508
  }
11163
10509
  });
11164
10510
 
11165
- // src/provider/client.ts
11166
- function createProviderClientPool(config) {
11167
- const capabilities = resolveProviderCapabilities({
11168
- provider: config.provider,
11169
- model: config.model
11170
- });
11171
- const baseUrls = buildProviderBaseUrlCandidates(config.baseUrl);
11172
- const clients = /* @__PURE__ */ new Map();
11173
- let preferredBaseUrl;
10511
+ // src/provider/capabilities.ts
10512
+ function resolveProviderCapabilities(input) {
10513
+ const profile = resolveModelProfile(input);
11174
10514
  return {
11175
- candidates() {
11176
- const ordered = preferredBaseUrl ? [preferredBaseUrl, ...baseUrls.filter((baseUrl) => baseUrl !== preferredBaseUrl)] : baseUrls;
11177
- return ordered.map((baseUrl) => ({
11178
- baseUrl,
11179
- client: getOrCreateClient(baseUrl)
11180
- }));
11181
- },
11182
- markHealthy(baseUrl) {
11183
- preferredBaseUrl = baseUrl;
11184
- }
11185
- };
11186
- function getOrCreateClient(baseUrl) {
11187
- const existing = clients.get(baseUrl);
11188
- if (existing) {
11189
- return existing;
11190
- }
11191
- const client = new import_openai.default({
11192
- apiKey: config.apiKey,
11193
- baseURL: baseUrl,
11194
- timeout: capabilities.requestTimeoutMs,
11195
- maxRetries: 0
11196
- });
11197
- clients.set(baseUrl, client);
11198
- return client;
11199
- }
11200
- }
11201
- function isProviderClientPool(value) {
11202
- return Boolean(
11203
- value && typeof value === "object" && typeof value.candidates === "function" && typeof value.markHealthy === "function"
11204
- );
11205
- }
11206
- var import_openai;
11207
- var init_client = __esm({
11208
- "src/provider/client.ts"() {
11209
- "use strict";
11210
- import_openai = __toESM(require("openai"));
11211
- init_capabilities();
11212
- init_connection();
11213
- }
11214
- });
11215
-
11216
- // src/provider/request.ts
11217
- async function fetchAssistantResponse(client, messages, request, tools, callbacks, abortSignal, onRequestMetric, observability) {
11218
- const capabilities = resolveProviderCapabilities(request);
11219
- const adapter = selectProviderWireAdapter(capabilities.wireApi);
11220
- return tryFetch(
11221
- adapter,
11222
- client,
11223
- messages,
11224
- request,
11225
- tools,
11226
- callbacks,
11227
- false,
11228
- abortSignal,
11229
- onRequestMetric,
11230
- observability
11231
- );
11232
- }
11233
- async function tryFetch(adapter, client, messages, request, tools, callbacks, forceReasoning, abortSignal, onRequestMetric, observability) {
11234
- const startedAt = Date.now();
11235
- let latestMetric;
11236
- let resolvedBaseUrl;
11237
- const forwardMetric = (metric) => {
11238
- latestMetric = metric;
11239
- onRequestMetric?.(metric);
10515
+ provider: profile.provider.id,
10516
+ model: profile.model.id,
10517
+ wireApi: profile.model.wireApi,
10518
+ supportsReasoningContent: profile.model.capabilities.reasoningContentReplay !== "never",
10519
+ defaultReasoningEnabled: profile.model.capabilities.reasoning,
10520
+ defaultReasoningEffort: profile.model.request.reasoningEffortDefault,
10521
+ requestTimeoutMs: profile.provider.requestTimeoutMs,
10522
+ doctorProbeTimeoutMs: profile.provider.doctorProbeTimeoutMs
11240
10523
  };
11241
- if (observability) {
11242
- await recordObservabilityEvent(observability.rootDir, {
11243
- event: "model.request",
11244
- status: "started",
11245
- sessionId: observability.sessionId,
11246
- identityKind: observability.identityKind,
11247
- identityName: observability.identityName,
11248
- model: request.model,
11249
- details: {
11250
- provider: request.provider,
11251
- configuredModel: observability.configuredModel,
11252
- requestModel: request.model,
11253
- wireApi: adapter.wireApi,
11254
- baseUrl: resolvedBaseUrl
11255
- }
11256
- });
11257
- }
11258
- try {
11259
- const response = await withApiRetries(
11260
- () => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
11261
- resolvedBaseUrl = baseUrl;
11262
- return adapter.fetchStreaming(providerClient, {
11263
- provider: request.provider,
11264
- model: request.model,
11265
- messages,
11266
- tools,
11267
- callbacks,
11268
- forceReasoning,
11269
- thinking: request.thinking,
11270
- reasoningEffort: request.reasoningEffort,
11271
- maxOutputTokens: request.maxOutputTokens,
11272
- sessionId: request.sessionId,
11273
- projectRoot: request.projectRoot,
11274
- abortSignal,
11275
- onRequestMetric: forwardMetric
11276
- });
11277
- }),
11278
- abortSignal
11279
- );
11280
- if (observability) {
11281
- await recordObservabilityEvent(observability.rootDir, {
11282
- event: "model.request",
11283
- status: "completed",
11284
- sessionId: observability.sessionId,
11285
- identityKind: observability.identityKind,
11286
- identityName: observability.identityName,
11287
- model: request.model,
11288
- durationMs: Date.now() - startedAt,
11289
- details: {
11290
- provider: request.provider,
11291
- configuredModel: observability.configuredModel,
11292
- requestModel: request.model,
11293
- wireApi: adapter.wireApi,
11294
- baseUrl: resolvedBaseUrl,
11295
- usage: latestMetric?.usage,
11296
- usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
11297
- }
11298
- });
11299
- }
11300
- return response;
11301
- } catch (error) {
11302
- if (isAbortError(error)) {
11303
- throw error;
11304
- }
11305
- try {
11306
- const response = await withApiRetries(
11307
- () => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
11308
- resolvedBaseUrl = baseUrl;
11309
- return adapter.fetchNonStreaming(providerClient, {
11310
- provider: request.provider,
11311
- model: request.model,
11312
- messages,
11313
- tools,
11314
- callbacks,
11315
- forceReasoning,
11316
- thinking: request.thinking,
11317
- reasoningEffort: request.reasoningEffort,
11318
- maxOutputTokens: request.maxOutputTokens,
11319
- sessionId: request.sessionId,
11320
- projectRoot: request.projectRoot,
11321
- abortSignal,
11322
- onRequestMetric: forwardMetric
11323
- });
11324
- }),
11325
- abortSignal
11326
- );
11327
- if (observability) {
11328
- await recordObservabilityEvent(observability.rootDir, {
11329
- event: "model.request",
11330
- status: "completed",
11331
- sessionId: observability.sessionId,
11332
- identityKind: observability.identityKind,
11333
- identityName: observability.identityName,
11334
- model: request.model,
11335
- durationMs: Date.now() - startedAt,
11336
- details: {
11337
- provider: request.provider,
11338
- configuredModel: observability.configuredModel,
11339
- requestModel: request.model,
11340
- wireApi: adapter.wireApi,
11341
- baseUrl: resolvedBaseUrl,
11342
- usage: latestMetric?.usage,
11343
- usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
11344
- }
11345
- });
11346
- }
11347
- return response;
11348
- } catch (fallbackError) {
11349
- if (!isAbortError(fallbackError) && observability) {
11350
- await recordObservabilityEvent(observability.rootDir, {
11351
- event: "model.request",
11352
- status: "failed",
11353
- sessionId: observability.sessionId,
11354
- identityKind: observability.identityKind,
11355
- identityName: observability.identityName,
11356
- model: request.model,
11357
- durationMs: Date.now() - startedAt,
11358
- error: fallbackError,
11359
- details: {
11360
- provider: request.provider,
11361
- configuredModel: observability.configuredModel,
11362
- requestModel: request.model,
11363
- wireApi: adapter.wireApi,
11364
- baseUrl: resolvedBaseUrl,
11365
- usage: latestMetric?.usage,
11366
- usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
11367
- }
11368
- });
11369
- }
11370
- throw fallbackError;
11371
- }
11372
- }
11373
- }
11374
- function selectProviderWireAdapter(wireApi) {
11375
- if (wireApi === "responses") {
11376
- return responsesAdapter;
11377
- }
11378
- return chatCompletionsAdapter;
11379
- }
11380
- async function invokeWithProviderClients(client, operation) {
11381
- if (!isProviderClientPool(client)) {
11382
- return operation(client, void 0);
11383
- }
11384
- let lastError;
11385
- const candidates = client.candidates();
11386
- for (let index = 0; index < candidates.length; index += 1) {
11387
- const candidate = candidates[index];
11388
- try {
11389
- const result = await operation(candidate.client, candidate.baseUrl);
11390
- client.markHealthy(candidate.baseUrl);
11391
- return result;
11392
- } catch (error) {
11393
- lastError = error;
11394
- if (isAbortError(error)) {
11395
- throw error;
11396
- }
11397
- const hasMoreCandidates = index < candidates.length - 1;
11398
- if (!hasMoreCandidates || !canRetryWithAlternateBaseUrl(error)) {
11399
- throw error;
11400
- }
11401
- }
11402
- }
11403
- throw lastError;
11404
10524
  }
11405
- function canRetryWithAlternateBaseUrl(error) {
11406
- const status = error.status;
11407
- const message = String(error.message ?? error).toLowerCase();
11408
- return status === 404 || status === 405 || message.includes("404") || message.includes("not found");
11409
- }
11410
- var init_request = __esm({
11411
- "src/provider/request.ts"() {
11412
- "use strict";
11413
- init_apiRetry();
11414
- init_usageNormalizer();
11415
- init_abort();
11416
- init_writer();
11417
- init_capabilities();
11418
- init_chatCompletionsAdapter();
11419
- init_responsesAdapter();
11420
- init_client();
11421
- }
11422
- });
11423
-
11424
- // src/provider/index.ts
11425
- var init_provider = __esm({
11426
- "src/provider/index.ts"() {
10525
+ var init_capabilities = __esm({
10526
+ "src/provider/capabilities.ts"() {
11427
10527
  "use strict";
11428
- init_capabilities();
11429
- init_request();
10528
+ init_catalog();
11430
10529
  }
11431
10530
  });
11432
10531
 
11433
10532
  // src/session/messages.ts
11434
- function buildChatMessages(systemPrompt, messages, contextWindowMessages, model) {
10533
+ function buildChatMessages(systemPrompt, messages, contextWindowMessages, model, provider) {
11435
10534
  const recentMessages = messages.slice(-contextWindowMessages);
11436
10535
  return [
11437
10536
  {
@@ -11440,7 +10539,8 @@ function buildChatMessages(systemPrompt, messages, contextWindowMessages, model)
11440
10539
  },
11441
10540
  ...recentMessages.map(
11442
10541
  (message, index) => toChatMessage(message, {
11443
- includeReasoning: shouldIncludeStoredAssistantReasoning(recentMessages, index, model)
10542
+ includeReasoning: shouldIncludeStoredAssistantReasoning(recentMessages, index, model, provider),
10543
+ provider
11444
10544
  })
11445
10545
  )
11446
10546
  ];
@@ -11525,19 +10625,23 @@ function expandStartToToolBoundary(messages, startIndex) {
11525
10625
  }
11526
10626
  return index;
11527
10627
  }
11528
- function modelUsesReasoningContent(model) {
11529
- return resolveProviderCapabilities({ model }).supportsReasoningContent;
10628
+ function modelUsesReasoningContent(model, provider) {
10629
+ if (provider) {
10630
+ return resolveProviderCapabilities({ provider, model }).supportsReasoningContent;
10631
+ }
10632
+ return listModelInfos().some((item) => item.id === model && item.capabilities.reasoningContentReplay !== "never");
11530
10633
  }
11531
10634
  function isAssistantMessageInLatestTurn(messages, index) {
11532
10635
  return messages[index]?.role === "assistant" && index > findLatestUserIndex(messages);
11533
10636
  }
11534
- function shouldIncludeStoredAssistantReasoning(messages, index, model) {
11535
- return modelUsesReasoningContent(model) && messages[index]?.role === "assistant";
10637
+ function shouldIncludeStoredAssistantReasoning(messages, index, model, provider) {
10638
+ return resolveProviderCapabilities({ provider, model }).supportsReasoningContent && messages[index]?.role === "assistant";
11536
10639
  }
11537
10640
  var init_messages = __esm({
11538
10641
  "src/session/messages.ts"() {
11539
10642
  "use strict";
11540
- init_provider();
10643
+ init_catalog();
10644
+ init_capabilities();
11541
10645
  }
11542
10646
  });
11543
10647
 
@@ -11689,7 +10793,8 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
11689
10793
  const safeMaxChars = Math.max(8e3, config.maxContextChars);
11690
10794
  const conversation = buildVisibleConversationWindow(messages);
11691
10795
  const conversationMessages = conversation.messages;
11692
- const fullMessages = composeChatMessages(systemPrompt, conversationMessages, config.model);
10796
+ const provider = config.provider ?? "openai-compatible";
10797
+ const fullMessages = composeChatMessages(systemPrompt, conversationMessages, config.model, provider);
11693
10798
  const initialEstimatedChars = estimateChatMessagesChars(fullMessages);
11694
10799
  const initialPromptMetrics = measureSystemPrompt(systemPrompt);
11695
10800
  const initialSources = buildBudgetSources(systemPrompt, conversationMessages);
@@ -11718,7 +10823,7 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
11718
10823
  const summary = compressedFrameHead.length > 0 ? summarizeConversation(compressedFrameHead, config.contextSummaryChars) : void 0;
11719
10824
  const summaryPrompt = appendSummary(systemPrompt, summary);
11720
10825
  let workingTail = compactTailMessages(tailMessages, "normal");
11721
- let requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model);
10826
+ let requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model, provider);
11722
10827
  let estimatedChars = estimateChatMessagesChars(requestMessages);
11723
10828
  let promptMetrics = measureSystemPrompt(summaryPrompt);
11724
10829
  let cacheLayout = buildCacheLayoutReport(summaryPrompt, workingTail);
@@ -11743,7 +10848,7 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
11743
10848
  };
11744
10849
  }
11745
10850
  workingTail = compactTailMessages(tailMessages, "aggressive");
11746
- requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model);
10851
+ requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model, provider);
11747
10852
  estimatedChars = estimateChatMessagesChars(requestMessages);
11748
10853
  promptMetrics = measureSystemPrompt(summaryPrompt);
11749
10854
  cacheLayout = buildCacheLayoutReport(summaryPrompt, workingTail);
@@ -11779,7 +10884,8 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
11779
10884
  const hardMessages = composeChatMessages(
11780
10885
  hardPrompt,
11781
10886
  compactedHardTail,
11782
- config.model
10887
+ config.model,
10888
+ provider
11783
10889
  );
11784
10890
  const hardEstimatedChars = estimateChatMessagesChars(hardMessages);
11785
10891
  const hardCacheLayout = buildCacheLayoutReport(hardPrompt, compactedHardTail);
@@ -11814,7 +10920,7 @@ function sliceTailMessages(messages, tailCount) {
11814
10920
  const safeStartIndex = expandStartToToolBoundary(messages, startIndex);
11815
10921
  return messages.slice(safeStartIndex);
11816
10922
  }
11817
- function composeChatMessages(systemPrompt, messages, model) {
10923
+ function composeChatMessages(systemPrompt, messages, model, provider) {
11818
10924
  return [
11819
10925
  {
11820
10926
  role: "system",
@@ -11826,7 +10932,7 @@ function composeChatMessages(systemPrompt, messages, model) {
11826
10932
  name: message.name,
11827
10933
  toolCallId: message.tool_call_id,
11828
10934
  toolCalls: message.tool_calls,
11829
- reasoningContent: shouldIncludeStoredAssistantReasoning(messages, index, model) ? message.reasoningContent : void 0
10935
+ reasoningContent: shouldIncludeStoredAssistantReasoning(messages, index, model, provider) ? message.reasoningContent : void 0
11830
10936
  }))
11831
10937
  ];
11832
10938
  }
@@ -12864,6 +11970,853 @@ var init_finalize = __esm({
12864
11970
  }
12865
11971
  });
12866
11972
 
11973
+ // src/provider/apiRetry.ts
11974
+ async function withApiRetries(operation, abortSignal) {
11975
+ let lastError;
11976
+ for (let attempt = 1; attempt <= API_MAX_RETRIES; attempt += 1) {
11977
+ try {
11978
+ return await operation();
11979
+ } catch (error) {
11980
+ if (isAbortError(error)) {
11981
+ throw error;
11982
+ }
11983
+ lastError = error;
11984
+ if (!isRetryableApiError(error) || attempt === API_MAX_RETRIES) {
11985
+ break;
11986
+ }
11987
+ await sleepWithSignal(API_RETRY_BASE_DELAY_MS * attempt, abortSignal);
11988
+ }
11989
+ }
11990
+ throw lastError;
11991
+ }
11992
+ function isRetryableApiError(error) {
11993
+ const status = error.status;
11994
+ if (typeof status === "number") {
11995
+ return status === 408 || status === 409 || status === 429 || status >= 500;
11996
+ }
11997
+ const message = String(error.message ?? error).toLowerCase();
11998
+ return message.includes("timeout") || message.includes("network") || message.includes("connection error") || message.includes("connection reset") || message.includes("econnreset") || message.includes("econnrefused") || message.includes("connect timeout") || message.includes("temporarily") || message.includes("rate limit") || message.includes("overloaded");
11999
+ }
12000
+ var API_MAX_RETRIES, API_RETRY_BASE_DELAY_MS;
12001
+ var init_apiRetry = __esm({
12002
+ "src/provider/apiRetry.ts"() {
12003
+ "use strict";
12004
+ init_abort();
12005
+ API_MAX_RETRIES = 3;
12006
+ API_RETRY_BASE_DELAY_MS = 1200;
12007
+ }
12008
+ });
12009
+
12010
+ // src/provider/chatRequestBody.ts
12011
+ function buildProviderRequestBody(input) {
12012
+ const capabilities = resolveProviderCapabilities(input);
12013
+ const thinking = capabilities.provider === "deepseek" ? resolveDeepSeekThinking(input.messages, input.thinking ?? "enabled") : input.thinking;
12014
+ const body = {
12015
+ model: input.model,
12016
+ messages: toChatCompletionMessages(input.messages),
12017
+ tools: input.tools,
12018
+ stream: input.stream
12019
+ };
12020
+ if (capabilities.provider !== "deepseek" && input.tools?.length) {
12021
+ body.tool_choice = "auto";
12022
+ }
12023
+ if (input.stream) {
12024
+ body.stream_options = {
12025
+ include_usage: true
12026
+ };
12027
+ }
12028
+ const cachePolicy = resolveProviderCachePolicy(input);
12029
+ if (cachePolicy.promptCacheKey) {
12030
+ body.prompt_cache_key = cachePolicy.promptCacheKey;
12031
+ }
12032
+ if (typeof input.maxOutputTokens === "number" && Number.isFinite(input.maxOutputTokens)) {
12033
+ body.max_tokens = Math.max(1, Math.trunc(input.maxOutputTokens));
12034
+ }
12035
+ if (capabilities.provider === "deepseek") {
12036
+ body.thinking = { type: thinking };
12037
+ if (thinking === "enabled") {
12038
+ body.reasoning_effort = normalizeDeepSeekReasoningEffort(input.reasoningEffort ?? capabilities.defaultReasoningEffort);
12039
+ }
12040
+ } else if (input.forceReasoning || capabilities.defaultReasoningEnabled) {
12041
+ body.thinking = { type: "enabled" };
12042
+ }
12043
+ return body;
12044
+ }
12045
+ function resolveDeepSeekThinking(messages, requested) {
12046
+ if (requested === "disabled") {
12047
+ return "disabled";
12048
+ }
12049
+ if (hasUnreplayableAssistantReasoning(messages)) {
12050
+ throw new Error("DeepSeek thinking tool-call replay requires stored reasoning_content. Start a new turn or disable KITTY_THINKING.");
12051
+ }
12052
+ return "enabled";
12053
+ }
12054
+ function hasUnreplayableAssistantReasoning(messages) {
12055
+ return messages.some(
12056
+ (message) => message.role === "assistant" && Array.isArray(message.toolCalls) && message.toolCalls.length > 0 && message.reasoningContent === void 0
12057
+ );
12058
+ }
12059
+ function normalizeDeepSeekReasoningEffort(effort) {
12060
+ if (effort === void 0 || effort === "minimal" || effort === "low" || effort === "medium" || effort === "high") {
12061
+ return "high";
12062
+ }
12063
+ if (effort === "xhigh" || effort === "max") {
12064
+ return "max";
12065
+ }
12066
+ return "high";
12067
+ }
12068
+ var init_chatRequestBody = __esm({
12069
+ "src/provider/chatRequestBody.ts"() {
12070
+ "use strict";
12071
+ init_cachePolicy();
12072
+ init_capabilities();
12073
+ init_chatCompletionsAdapter();
12074
+ }
12075
+ });
12076
+
12077
+ // src/provider/chatCompletionsAdapter.ts
12078
+ function abortStream(stream) {
12079
+ try {
12080
+ stream?.controller?.abort();
12081
+ } catch {
12082
+ }
12083
+ }
12084
+ function toChatCompletionMessages(messages) {
12085
+ return messages.map((message) => {
12086
+ if (message.role === "tool") {
12087
+ return {
12088
+ role: "tool",
12089
+ content: message.content ?? "",
12090
+ tool_call_id: message.toolCallId ?? ""
12091
+ };
12092
+ }
12093
+ if (message.role === "assistant" && message.toolCalls?.length) {
12094
+ const assistantMessage = {
12095
+ role: "assistant",
12096
+ content: message.content ?? "",
12097
+ tool_calls: message.toolCalls
12098
+ };
12099
+ if (message.reasoningContent !== void 0) {
12100
+ assistantMessage.reasoning_content = message.reasoningContent;
12101
+ }
12102
+ return assistantMessage;
12103
+ }
12104
+ const baseMessage = {
12105
+ role: message.role,
12106
+ content: message.content ?? "",
12107
+ name: message.name
12108
+ };
12109
+ if (message.role === "assistant" && message.reasoningContent !== void 0) {
12110
+ baseMessage.reasoning_content = message.reasoningContent;
12111
+ }
12112
+ return baseMessage;
12113
+ });
12114
+ }
12115
+ var chatCompletionsAdapter;
12116
+ var init_chatCompletionsAdapter = __esm({
12117
+ "src/provider/chatCompletionsAdapter.ts"() {
12118
+ "use strict";
12119
+ init_messages();
12120
+ init_chatRequestBody();
12121
+ init_usageNormalizer();
12122
+ init_abort();
12123
+ chatCompletionsAdapter = {
12124
+ wireApi: "chat.completions",
12125
+ async fetchStreaming(client, request) {
12126
+ const startedAt = Date.now();
12127
+ let usage;
12128
+ throwIfAborted(request.abortSignal, "Streaming request aborted");
12129
+ try {
12130
+ const stream = await client.chat.completions.create(
12131
+ {
12132
+ ...buildProviderRequestBody({
12133
+ provider: request.provider,
12134
+ model: request.model,
12135
+ messages: request.messages,
12136
+ tools: request.tools,
12137
+ stream: true,
12138
+ forceReasoning: request.forceReasoning,
12139
+ thinking: request.thinking,
12140
+ reasoningEffort: request.reasoningEffort,
12141
+ maxOutputTokens: request.maxOutputTokens,
12142
+ sessionId: request.sessionId,
12143
+ projectRoot: request.projectRoot
12144
+ }),
12145
+ signal: request.abortSignal
12146
+ }
12147
+ );
12148
+ if (request.abortSignal?.aborted) {
12149
+ abortStream(stream);
12150
+ throw createAbortError("Streaming aborted");
12151
+ }
12152
+ let content = "";
12153
+ let reasoningContent = "";
12154
+ const toolCallParts = /* @__PURE__ */ new Map();
12155
+ for await (const chunk of stream) {
12156
+ if (request.abortSignal?.aborted) {
12157
+ abortStream(stream);
12158
+ throw createAbortError("Streaming aborted");
12159
+ }
12160
+ usage = normalizeProviderUsage(chunk.usage) ?? usage;
12161
+ const delta = chunk.choices?.[0]?.delta;
12162
+ if (!delta) {
12163
+ continue;
12164
+ }
12165
+ if (typeof delta.content === "string" && delta.content.length > 0) {
12166
+ content += delta.content;
12167
+ request.callbacks?.onAssistantDelta?.(delta.content);
12168
+ }
12169
+ if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
12170
+ reasoningContent += delta.reasoning_content;
12171
+ request.callbacks?.onReasoningDelta?.(delta.reasoning_content);
12172
+ }
12173
+ if (Array.isArray(delta.tool_calls)) {
12174
+ for (const toolCall of delta.tool_calls) {
12175
+ const index = typeof toolCall.index === "number" ? toolCall.index : 0;
12176
+ const existing = toolCallParts.get(index) ?? {
12177
+ id: toolCall.id ?? `tool-${index}`,
12178
+ name: "",
12179
+ arguments: ""
12180
+ };
12181
+ if (toolCall.id) {
12182
+ existing.id = toolCall.id;
12183
+ }
12184
+ if (toolCall.function?.name) {
12185
+ existing.name += toolCall.function.name;
12186
+ }
12187
+ if (toolCall.function?.arguments) {
12188
+ existing.arguments += toolCall.function.arguments;
12189
+ }
12190
+ toolCallParts.set(index, existing);
12191
+ }
12192
+ }
12193
+ }
12194
+ return {
12195
+ content: content.length > 0 ? content : null,
12196
+ reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
12197
+ streamedAssistantContent: content.length > 0,
12198
+ streamedReasoningContent: reasoningContent.length > 0,
12199
+ toolCalls: [...toolCallParts.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
12200
+ id: toolCall.id,
12201
+ type: "function",
12202
+ function: {
12203
+ name: toolCall.name,
12204
+ arguments: toolCall.arguments
12205
+ }
12206
+ }))
12207
+ };
12208
+ } finally {
12209
+ request.onRequestMetric?.({
12210
+ durationMs: Date.now() - startedAt,
12211
+ usage
12212
+ });
12213
+ }
12214
+ },
12215
+ async fetchNonStreaming(client, request) {
12216
+ const startedAt = Date.now();
12217
+ let usage;
12218
+ throwIfAborted(request.abortSignal, "Request aborted");
12219
+ try {
12220
+ const completion = await client.chat.completions.create(
12221
+ {
12222
+ ...buildProviderRequestBody({
12223
+ provider: request.provider,
12224
+ model: request.model,
12225
+ messages: request.messages,
12226
+ tools: request.tools,
12227
+ stream: false,
12228
+ forceReasoning: request.forceReasoning,
12229
+ thinking: request.thinking,
12230
+ reasoningEffort: request.reasoningEffort,
12231
+ maxOutputTokens: request.maxOutputTokens,
12232
+ sessionId: request.sessionId,
12233
+ projectRoot: request.projectRoot
12234
+ }),
12235
+ signal: request.abortSignal
12236
+ }
12237
+ );
12238
+ usage = normalizeProviderUsage(completion.usage);
12239
+ const message = completion.choices[0]?.message;
12240
+ if (!message) {
12241
+ throw new Error("API returned no message.");
12242
+ }
12243
+ return {
12244
+ content: typeof message.content === "string" ? message.content : collapseContentParts(message.content),
12245
+ reasoningContent: readReasoningContent(message),
12246
+ streamedAssistantContent: false,
12247
+ streamedReasoningContent: false,
12248
+ toolCalls: (message.tool_calls ?? []).filter((call) => call.type === "function").map((call) => ({
12249
+ id: call.id,
12250
+ type: "function",
12251
+ function: {
12252
+ name: call.function.name,
12253
+ arguments: call.function.arguments
12254
+ }
12255
+ }))
12256
+ };
12257
+ } finally {
12258
+ request.onRequestMetric?.({
12259
+ durationMs: Date.now() - startedAt,
12260
+ usage
12261
+ });
12262
+ }
12263
+ }
12264
+ };
12265
+ }
12266
+ });
12267
+
12268
+ // src/provider/responsesAdapter.ts
12269
+ function buildResponsesRequestBody(request) {
12270
+ const capabilities = resolveProviderCapabilities({
12271
+ provider: request.provider,
12272
+ model: request.model
12273
+ });
12274
+ const body = {
12275
+ model: request.model,
12276
+ input: toResponsesInput(request.messages),
12277
+ tools: request.tools?.map((tool) => ({
12278
+ type: "function",
12279
+ name: tool.function.name,
12280
+ description: tool.function.description,
12281
+ parameters: tool.function.parameters ?? null,
12282
+ strict: false
12283
+ })),
12284
+ tool_choice: request.tools?.length ? "auto" : void 0
12285
+ };
12286
+ if (typeof request.maxOutputTokens === "number" && Number.isFinite(request.maxOutputTokens)) {
12287
+ body.max_output_tokens = Math.max(1, Math.trunc(request.maxOutputTokens));
12288
+ }
12289
+ const cachePolicy = resolveProviderCachePolicy({
12290
+ provider: request.provider,
12291
+ model: request.model,
12292
+ sessionId: request.sessionId,
12293
+ projectRoot: request.projectRoot
12294
+ });
12295
+ if (cachePolicy.promptCacheKey) {
12296
+ body.prompt_cache_key = cachePolicy.promptCacheKey;
12297
+ }
12298
+ const reasoningEffort = request.thinking === "disabled" ? void 0 : normalizeResponsesReasoningEffort(
12299
+ request.reasoningEffort ?? capabilities.defaultReasoningEffort
12300
+ );
12301
+ if (request.thinking !== "disabled" && (request.forceReasoning || capabilities.defaultReasoningEnabled || request.thinking === "enabled" || reasoningEffort)) {
12302
+ body.reasoning = {
12303
+ effort: reasoningEffort ?? "high",
12304
+ summary: "detailed"
12305
+ };
12306
+ }
12307
+ return body;
12308
+ }
12309
+ function normalizeResponsesReasoningEffort(effort) {
12310
+ if (effort === "xhigh") {
12311
+ return "xhigh";
12312
+ }
12313
+ return effort === "max" ? void 0 : effort;
12314
+ }
12315
+ function toResponsesInput(messages) {
12316
+ const items = [];
12317
+ for (const message of messages) {
12318
+ if (message.role === "tool") {
12319
+ items.push({
12320
+ type: "function_call_output",
12321
+ call_id: message.toolCallId ?? "",
12322
+ output: message.content ?? ""
12323
+ });
12324
+ continue;
12325
+ }
12326
+ if (message.role === "assistant" && message.toolCalls?.length) {
12327
+ if (typeof message.content === "string" && message.content.trim().length > 0) {
12328
+ items.push({
12329
+ type: "message",
12330
+ role: "assistant",
12331
+ content: message.content
12332
+ });
12333
+ }
12334
+ for (const toolCall of message.toolCalls) {
12335
+ items.push({
12336
+ type: "function_call",
12337
+ call_id: toolCall.id,
12338
+ name: toolCall.function.name,
12339
+ arguments: toolCall.function.arguments
12340
+ });
12341
+ }
12342
+ continue;
12343
+ }
12344
+ items.push({
12345
+ type: "message",
12346
+ role: message.role,
12347
+ content: message.content ?? ""
12348
+ });
12349
+ }
12350
+ return items;
12351
+ }
12352
+ function normalizeOutputText(response) {
12353
+ const outputText = response.output_text;
12354
+ if (typeof outputText === "string" && outputText.trim().length > 0) {
12355
+ return outputText;
12356
+ }
12357
+ const output = response.output;
12358
+ if (!Array.isArray(output)) {
12359
+ return null;
12360
+ }
12361
+ const fragments = output.flatMap((item) => {
12362
+ if (!item || typeof item !== "object" || item.type !== "message") {
12363
+ return [];
12364
+ }
12365
+ const content = item.content;
12366
+ if (!Array.isArray(content)) {
12367
+ return [];
12368
+ }
12369
+ return content.flatMap((part) => {
12370
+ if (!part || typeof part !== "object" || part.type !== "output_text") {
12371
+ return [];
12372
+ }
12373
+ return typeof part.text === "string" ? [part.text] : [];
12374
+ });
12375
+ });
12376
+ return fragments.length > 0 ? fragments.join("") : null;
12377
+ }
12378
+ function readResponseToolCalls(response) {
12379
+ const output = response.output;
12380
+ if (!Array.isArray(output)) {
12381
+ return [];
12382
+ }
12383
+ return output.filter((item) => Boolean(item) && typeof item === "object" && item.type === "function_call").map((item) => ({
12384
+ id: item.call_id ?? item.id ?? crypto.randomUUID(),
12385
+ type: "function",
12386
+ function: {
12387
+ name: item.name ?? "",
12388
+ arguments: item.arguments ?? ""
12389
+ }
12390
+ }));
12391
+ }
12392
+ function readResponseReasoning(response) {
12393
+ const output = response.output;
12394
+ if (!Array.isArray(output)) {
12395
+ return void 0;
12396
+ }
12397
+ const fragments = output.flatMap((item) => {
12398
+ if (!item || typeof item !== "object" || item.type !== "reasoning") {
12399
+ return [];
12400
+ }
12401
+ const reasoningItem = item;
12402
+ const summary = Array.isArray(reasoningItem.summary) ? reasoningItem.summary.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
12403
+ const content = Array.isArray(reasoningItem.content) ? reasoningItem.content.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
12404
+ return [...content, ...summary];
12405
+ });
12406
+ return fragments.length > 0 ? fragments.join("") : void 0;
12407
+ }
12408
+ function abortStream2(stream) {
12409
+ try {
12410
+ stream?.controller?.abort();
12411
+ } catch {
12412
+ }
12413
+ }
12414
+ var responsesAdapter;
12415
+ var init_responsesAdapter = __esm({
12416
+ "src/provider/responsesAdapter.ts"() {
12417
+ "use strict";
12418
+ init_capabilities();
12419
+ init_cachePolicy();
12420
+ init_usageNormalizer();
12421
+ init_abort();
12422
+ responsesAdapter = {
12423
+ wireApi: "responses",
12424
+ async fetchStreaming(client, request) {
12425
+ const startedAt = Date.now();
12426
+ let usage;
12427
+ throwIfAborted(request.abortSignal, "Streaming request aborted");
12428
+ try {
12429
+ const stream = await client.responses.create(
12430
+ {
12431
+ ...buildResponsesRequestBody(request),
12432
+ stream: true
12433
+ },
12434
+ {
12435
+ signal: request.abortSignal
12436
+ }
12437
+ );
12438
+ if (request.abortSignal?.aborted) {
12439
+ abortStream2(stream);
12440
+ throw createAbortError("Streaming aborted");
12441
+ }
12442
+ let content = "";
12443
+ let reasoningContent = "";
12444
+ const toolCalls = /* @__PURE__ */ new Map();
12445
+ for await (const event of stream) {
12446
+ if (request.abortSignal?.aborted) {
12447
+ abortStream2(stream);
12448
+ throw createAbortError("Streaming aborted");
12449
+ }
12450
+ usage = normalizeProviderUsage(event.response?.usage) ?? usage;
12451
+ if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
12452
+ content += event.delta;
12453
+ request.callbacks?.onAssistantDelta?.(event.delta);
12454
+ continue;
12455
+ }
12456
+ if ((event.type === "response.reasoning_text.delta" || event.type === "response.reasoning_summary_text.delta") && typeof event.delta === "string") {
12457
+ reasoningContent += event.delta;
12458
+ request.callbacks?.onReasoningDelta?.(event.delta);
12459
+ continue;
12460
+ }
12461
+ if (event.type === "response.function_call_arguments.delta" && typeof event.delta === "string") {
12462
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
12463
+ const existing = toolCalls.get(index) ?? {
12464
+ id: event.item_id ?? `tool-${index}`,
12465
+ name: "",
12466
+ arguments: ""
12467
+ };
12468
+ existing.arguments += event.delta;
12469
+ toolCalls.set(index, existing);
12470
+ continue;
12471
+ }
12472
+ if (event.type === "response.function_call_arguments.done") {
12473
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
12474
+ const existing = toolCalls.get(index) ?? {
12475
+ id: event.item_id ?? `tool-${index}`,
12476
+ name: "",
12477
+ arguments: ""
12478
+ };
12479
+ if (typeof event.name === "string") {
12480
+ existing.name = event.name;
12481
+ }
12482
+ if (typeof event.arguments === "string" && event.arguments.length > 0) {
12483
+ existing.arguments = event.arguments;
12484
+ }
12485
+ toolCalls.set(index, existing);
12486
+ continue;
12487
+ }
12488
+ if (event.type === "response.output_item.done" && event.item?.type === "function_call") {
12489
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
12490
+ toolCalls.set(index, {
12491
+ id: event.item.call_id ?? event.item.id ?? `tool-${index}`,
12492
+ name: event.item.name ?? "",
12493
+ arguments: event.item.arguments ?? ""
12494
+ });
12495
+ }
12496
+ }
12497
+ return {
12498
+ content: content.length > 0 ? content : null,
12499
+ reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
12500
+ streamedAssistantContent: content.length > 0,
12501
+ streamedReasoningContent: reasoningContent.length > 0,
12502
+ toolCalls: [...toolCalls.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
12503
+ id: toolCall.id,
12504
+ type: "function",
12505
+ function: {
12506
+ name: toolCall.name,
12507
+ arguments: toolCall.arguments
12508
+ }
12509
+ }))
12510
+ };
12511
+ } finally {
12512
+ request.onRequestMetric?.({
12513
+ durationMs: Date.now() - startedAt,
12514
+ usage
12515
+ });
12516
+ }
12517
+ },
12518
+ async fetchNonStreaming(client, request) {
12519
+ const startedAt = Date.now();
12520
+ let usage;
12521
+ throwIfAborted(request.abortSignal, "Request aborted");
12522
+ try {
12523
+ const response = await client.responses.create(
12524
+ {
12525
+ ...buildResponsesRequestBody(request),
12526
+ stream: false
12527
+ },
12528
+ {
12529
+ signal: request.abortSignal
12530
+ }
12531
+ );
12532
+ usage = normalizeProviderUsage(response.usage);
12533
+ return {
12534
+ content: normalizeOutputText(response),
12535
+ reasoningContent: readResponseReasoning(response),
12536
+ streamedAssistantContent: false,
12537
+ streamedReasoningContent: false,
12538
+ toolCalls: readResponseToolCalls(response)
12539
+ };
12540
+ } finally {
12541
+ request.onRequestMetric?.({
12542
+ durationMs: Date.now() - startedAt,
12543
+ usage
12544
+ });
12545
+ }
12546
+ }
12547
+ };
12548
+ }
12549
+ });
12550
+
12551
+ // src/provider/client.ts
12552
+ function createProviderClientPool(config) {
12553
+ const capabilities = resolveProviderCapabilities({
12554
+ provider: config.provider,
12555
+ model: config.model
12556
+ });
12557
+ const baseUrls = buildProviderBaseUrlCandidates(config.baseUrl);
12558
+ const clients = /* @__PURE__ */ new Map();
12559
+ let preferredBaseUrl;
12560
+ return {
12561
+ candidates() {
12562
+ const ordered = preferredBaseUrl ? [preferredBaseUrl, ...baseUrls.filter((baseUrl) => baseUrl !== preferredBaseUrl)] : baseUrls;
12563
+ return ordered.map((baseUrl) => ({
12564
+ baseUrl,
12565
+ client: getOrCreateClient(baseUrl)
12566
+ }));
12567
+ },
12568
+ markHealthy(baseUrl) {
12569
+ preferredBaseUrl = baseUrl;
12570
+ }
12571
+ };
12572
+ function getOrCreateClient(baseUrl) {
12573
+ const existing = clients.get(baseUrl);
12574
+ if (existing) {
12575
+ return existing;
12576
+ }
12577
+ const client = new import_openai.default({
12578
+ apiKey: config.apiKey,
12579
+ baseURL: baseUrl,
12580
+ timeout: capabilities.requestTimeoutMs,
12581
+ maxRetries: 0
12582
+ });
12583
+ clients.set(baseUrl, client);
12584
+ return client;
12585
+ }
12586
+ }
12587
+ function isProviderClientPool(value) {
12588
+ return Boolean(
12589
+ value && typeof value === "object" && typeof value.candidates === "function" && typeof value.markHealthy === "function"
12590
+ );
12591
+ }
12592
+ var import_openai;
12593
+ var init_client = __esm({
12594
+ "src/provider/client.ts"() {
12595
+ "use strict";
12596
+ import_openai = __toESM(require("openai"));
12597
+ init_capabilities();
12598
+ init_connection();
12599
+ }
12600
+ });
12601
+
12602
+ // src/provider/request.ts
12603
+ async function fetchAssistantResponse(client, messages, request, tools, callbacks, abortSignal, onRequestMetric, observability) {
12604
+ const capabilities = resolveProviderCapabilities(request);
12605
+ const adapter = selectProviderWireAdapter(capabilities.wireApi);
12606
+ return tryFetch(
12607
+ adapter,
12608
+ client,
12609
+ messages,
12610
+ request,
12611
+ tools,
12612
+ callbacks,
12613
+ false,
12614
+ abortSignal,
12615
+ onRequestMetric,
12616
+ observability
12617
+ );
12618
+ }
12619
+ async function tryFetch(adapter, client, messages, request, tools, callbacks, forceReasoning, abortSignal, onRequestMetric, observability) {
12620
+ const startedAt = Date.now();
12621
+ let latestMetric;
12622
+ let resolvedBaseUrl;
12623
+ const forwardMetric = (metric) => {
12624
+ latestMetric = metric;
12625
+ onRequestMetric?.(metric);
12626
+ };
12627
+ if (observability) {
12628
+ await recordObservabilityEvent(observability.rootDir, {
12629
+ event: "model.request",
12630
+ status: "started",
12631
+ sessionId: observability.sessionId,
12632
+ identityKind: observability.identityKind,
12633
+ identityName: observability.identityName,
12634
+ model: request.model,
12635
+ details: {
12636
+ provider: request.provider,
12637
+ configuredModel: observability.configuredModel,
12638
+ requestModel: request.model,
12639
+ wireApi: adapter.wireApi,
12640
+ baseUrl: resolvedBaseUrl
12641
+ }
12642
+ });
12643
+ }
12644
+ try {
12645
+ const response = await withApiRetries(
12646
+ () => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
12647
+ resolvedBaseUrl = baseUrl;
12648
+ return adapter.fetchStreaming(providerClient, {
12649
+ provider: request.provider,
12650
+ model: request.model,
12651
+ messages,
12652
+ tools,
12653
+ callbacks,
12654
+ forceReasoning,
12655
+ thinking: request.thinking,
12656
+ reasoningEffort: request.reasoningEffort,
12657
+ maxOutputTokens: request.maxOutputTokens,
12658
+ sessionId: request.sessionId,
12659
+ projectRoot: request.projectRoot,
12660
+ abortSignal,
12661
+ onRequestMetric: forwardMetric
12662
+ });
12663
+ }),
12664
+ abortSignal
12665
+ );
12666
+ if (observability) {
12667
+ await recordObservabilityEvent(observability.rootDir, {
12668
+ event: "model.request",
12669
+ status: "completed",
12670
+ sessionId: observability.sessionId,
12671
+ identityKind: observability.identityKind,
12672
+ identityName: observability.identityName,
12673
+ model: request.model,
12674
+ durationMs: Date.now() - startedAt,
12675
+ details: {
12676
+ provider: request.provider,
12677
+ configuredModel: observability.configuredModel,
12678
+ requestModel: request.model,
12679
+ wireApi: adapter.wireApi,
12680
+ baseUrl: resolvedBaseUrl,
12681
+ usage: latestMetric?.usage,
12682
+ usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
12683
+ }
12684
+ });
12685
+ }
12686
+ return response;
12687
+ } catch (error) {
12688
+ if (isAbortError(error)) {
12689
+ throw error;
12690
+ }
12691
+ try {
12692
+ const response = await withApiRetries(
12693
+ () => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
12694
+ resolvedBaseUrl = baseUrl;
12695
+ return adapter.fetchNonStreaming(providerClient, {
12696
+ provider: request.provider,
12697
+ model: request.model,
12698
+ messages,
12699
+ tools,
12700
+ callbacks,
12701
+ forceReasoning,
12702
+ thinking: request.thinking,
12703
+ reasoningEffort: request.reasoningEffort,
12704
+ maxOutputTokens: request.maxOutputTokens,
12705
+ sessionId: request.sessionId,
12706
+ projectRoot: request.projectRoot,
12707
+ abortSignal,
12708
+ onRequestMetric: forwardMetric
12709
+ });
12710
+ }),
12711
+ abortSignal
12712
+ );
12713
+ if (observability) {
12714
+ await recordObservabilityEvent(observability.rootDir, {
12715
+ event: "model.request",
12716
+ status: "completed",
12717
+ sessionId: observability.sessionId,
12718
+ identityKind: observability.identityKind,
12719
+ identityName: observability.identityName,
12720
+ model: request.model,
12721
+ durationMs: Date.now() - startedAt,
12722
+ details: {
12723
+ provider: request.provider,
12724
+ configuredModel: observability.configuredModel,
12725
+ requestModel: request.model,
12726
+ wireApi: adapter.wireApi,
12727
+ baseUrl: resolvedBaseUrl,
12728
+ usage: latestMetric?.usage,
12729
+ usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
12730
+ }
12731
+ });
12732
+ }
12733
+ return response;
12734
+ } catch (fallbackError) {
12735
+ if (!isAbortError(fallbackError) && observability) {
12736
+ await recordObservabilityEvent(observability.rootDir, {
12737
+ event: "model.request",
12738
+ status: "failed",
12739
+ sessionId: observability.sessionId,
12740
+ identityKind: observability.identityKind,
12741
+ identityName: observability.identityName,
12742
+ model: request.model,
12743
+ durationMs: Date.now() - startedAt,
12744
+ error: fallbackError,
12745
+ details: {
12746
+ provider: request.provider,
12747
+ configuredModel: observability.configuredModel,
12748
+ requestModel: request.model,
12749
+ wireApi: adapter.wireApi,
12750
+ baseUrl: resolvedBaseUrl,
12751
+ usage: latestMetric?.usage,
12752
+ usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
12753
+ }
12754
+ });
12755
+ }
12756
+ throw fallbackError;
12757
+ }
12758
+ }
12759
+ }
12760
+ function selectProviderWireAdapter(wireApi) {
12761
+ if (wireApi === "responses") {
12762
+ return responsesAdapter;
12763
+ }
12764
+ return chatCompletionsAdapter;
12765
+ }
12766
+ async function invokeWithProviderClients(client, operation) {
12767
+ if (!isProviderClientPool(client)) {
12768
+ return operation(client, void 0);
12769
+ }
12770
+ let lastError;
12771
+ const candidates = client.candidates();
12772
+ for (let index = 0; index < candidates.length; index += 1) {
12773
+ const candidate = candidates[index];
12774
+ try {
12775
+ const result = await operation(candidate.client, candidate.baseUrl);
12776
+ client.markHealthy(candidate.baseUrl);
12777
+ return result;
12778
+ } catch (error) {
12779
+ lastError = error;
12780
+ if (isAbortError(error)) {
12781
+ throw error;
12782
+ }
12783
+ const hasMoreCandidates = index < candidates.length - 1;
12784
+ if (!hasMoreCandidates || !canRetryWithAlternateBaseUrl(error)) {
12785
+ throw error;
12786
+ }
12787
+ }
12788
+ }
12789
+ throw lastError;
12790
+ }
12791
+ function canRetryWithAlternateBaseUrl(error) {
12792
+ const status = error.status;
12793
+ const message = String(error.message ?? error).toLowerCase();
12794
+ return status === 404 || status === 405 || message.includes("404") || message.includes("not found");
12795
+ }
12796
+ var init_request = __esm({
12797
+ "src/provider/request.ts"() {
12798
+ "use strict";
12799
+ init_apiRetry();
12800
+ init_usageNormalizer();
12801
+ init_abort();
12802
+ init_writer();
12803
+ init_capabilities();
12804
+ init_chatCompletionsAdapter();
12805
+ init_responsesAdapter();
12806
+ init_client();
12807
+ }
12808
+ });
12809
+
12810
+ // src/provider/index.ts
12811
+ var init_provider = __esm({
12812
+ "src/provider/index.ts"() {
12813
+ "use strict";
12814
+ init_capabilities();
12815
+ init_catalog();
12816
+ init_request();
12817
+ }
12818
+ });
12819
+
12867
12820
  // src/provider/retryPolicy.ts
12868
12821
  function isRecoverableTurnError(error) {
12869
12822
  if (isRetryableApiError(error)) {
@@ -13167,6 +13120,7 @@ async function updateSessionMemoryAfterTurn(input) {
13167
13120
  identityName: input.identity.name,
13168
13121
  model: input.requestModel
13169
13122
  });
13123
+ input.options.callbacks?.onStatus?.("\u603B\u7ED3\u4E2D");
13170
13124
  try {
13171
13125
  const memoryResponse = input.options.fetchSessionMemoryResponse ? await input.options.fetchSessionMemoryResponse(modelRequest) : await fetchAssistantResponse(
13172
13126
  input.client,
@@ -13217,6 +13171,8 @@ async function updateSessionMemoryAfterTurn(input) {
13217
13171
  error
13218
13172
  });
13219
13173
  return input.session;
13174
+ } finally {
13175
+ input.options.callbacks?.onStatus?.("");
13220
13176
  }
13221
13177
  }
13222
13178
  async function updateSessionTitleAfterTurn(input) {
@@ -13264,6 +13220,7 @@ async function updateSessionTitleAfterTurn(input) {
13264
13220
  identityName: input.identity.name,
13265
13221
  model: input.requestModel
13266
13222
  });
13223
+ input.options.callbacks?.onStatus?.("\u6807\u9898\u751F\u6210\u4E2D");
13267
13224
  try {
13268
13225
  const titleResponse = input.options.fetchSessionTitleResponse ? await input.options.fetchSessionTitleResponse(modelRequest) : await fetchAssistantResponse(
13269
13226
  input.client,
@@ -13315,6 +13272,8 @@ async function updateSessionTitleAfterTurn(input) {
13315
13272
  error
13316
13273
  });
13317
13274
  return input.session;
13275
+ } finally {
13276
+ input.options.callbacks?.onStatus?.("");
13318
13277
  }
13319
13278
  }
13320
13279
  var init_lifecycle = __esm({
@@ -14033,7 +13992,7 @@ var init_bash = __esm({
14033
13992
  const status = result.aborted ? "aborted" : result.stalled ? "stalled" : result.timedOut ? "timed_out" : result.exitCode === 0 ? "completed" : "failed";
14034
13993
  const outputGovernance = governToolOutput({
14035
13994
  toolName: "bash",
14036
- command,
13995
+ command: result.command,
14037
13996
  status,
14038
13997
  exitCode: result.exitCode,
14039
13998
  durationMs: result.durationMs,
@@ -14061,7 +14020,7 @@ var init_bash = __esm({
14061
14020
  return okResult(
14062
14021
  JSON.stringify(
14063
14022
  {
14064
- command,
14023
+ command: result.command,
14065
14024
  cwd: resolvedCwd,
14066
14025
  exitCode: result.exitCode,
14067
14026
  status,
@@ -226409,6 +226368,63 @@ var init_toolBatch = __esm({
226409
226368
  }
226410
226369
  });
226411
226370
 
226371
+ // src/session/events.ts
226372
+ var events_exports = {};
226373
+ __export(events_exports, {
226374
+ SessionEventStore: () => SessionEventStore
226375
+ });
226376
+ function createEventId() {
226377
+ return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${Math.random().toString(16).slice(2, 10)}`;
226378
+ }
226379
+ function sanitizeSessionId2(sessionId) {
226380
+ return sessionId.replace(/[^a-zA-Z0-9_.-]/g, "_");
226381
+ }
226382
+ var import_promises30, import_node_path42, SessionEventStore;
226383
+ var init_events = __esm({
226384
+ "src/session/events.ts"() {
226385
+ "use strict";
226386
+ import_promises30 = __toESM(require("fs/promises"));
226387
+ import_node_path42 = __toESM(require("path"));
226388
+ SessionEventStore = class {
226389
+ constructor(eventsDir) {
226390
+ this.eventsDir = eventsDir;
226391
+ }
226392
+ async append(event) {
226393
+ const record = {
226394
+ id: createEventId(),
226395
+ createdAt: event.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
226396
+ type: event.type,
226397
+ sessionId: event.sessionId,
226398
+ cwd: event.cwd,
226399
+ host: event.host,
226400
+ message: event.message,
226401
+ details: event.details
226402
+ };
226403
+ await import_promises30.default.mkdir(this.eventsDir, { recursive: true });
226404
+ await import_promises30.default.appendFile(this.getSessionEventPath(event.sessionId), `${JSON.stringify(record)}
226405
+ `, "utf8");
226406
+ return record;
226407
+ }
226408
+ async list(sessionId, limit = 100) {
226409
+ const filePath = this.getSessionEventPath(sessionId);
226410
+ let raw = "";
226411
+ try {
226412
+ raw = await import_promises30.default.readFile(filePath, "utf8");
226413
+ } catch (error) {
226414
+ if (error.code === "ENOENT") {
226415
+ return [];
226416
+ }
226417
+ throw error;
226418
+ }
226419
+ return raw.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line)).slice(-limit);
226420
+ }
226421
+ getSessionEventPath(sessionId) {
226422
+ return import_node_path42.default.join(this.eventsDir, `${sanitizeSessionId2(sessionId)}.jsonl`);
226423
+ }
226424
+ };
226425
+ }
226426
+ });
226427
+
226412
226428
  // src/agent/turn/toolFailure.ts
226413
226429
  function readToolFailureError(output) {
226414
226430
  try {
@@ -226449,9 +226465,16 @@ async function processToolCallBatch(input) {
226449
226465
  const batchToolMessages = [];
226450
226466
  const batchModelOutputs = [];
226451
226467
  const batchChangedPaths = /* @__PURE__ */ new Set();
226468
+ const sessionEvents = new SessionEventStore(options.config.paths.eventsDir);
226452
226469
  const leadWaitExecutionsBefore = identity.kind === "lead" ? listLeadWaitExecutions(projectContext.stateRootDir) : [];
226453
226470
  for (const toolCall of response.toolCalls) {
226454
226471
  throwIfAborted(options.abortSignal, "Turn aborted by user.");
226472
+ await sessionEvents.append({
226473
+ type: "tool.started",
226474
+ sessionId: session.id,
226475
+ cwd: options.cwd,
226476
+ details: buildToolStartedEventDetails(toolCall, identity)
226477
+ });
226455
226478
  options.callbacks?.onToolCall?.(toolCall.function.name, toolCall.function.arguments);
226456
226479
  await recordObservabilityEvent(projectContext.stateRootDir, {
226457
226480
  event: "tool.execution",
@@ -226485,6 +226508,7 @@ async function processToolCallBatch(input) {
226485
226508
  } else if (metadata?.sessionDiff) {
226486
226509
  session = await options.sessionStore.save(noteSessionDiff(session, metadata.sessionDiff));
226487
226510
  }
226511
+ const failureError = result.ok ? void 0 : readToolFailureError(result.output);
226488
226512
  await recordObservabilityEvent(projectContext.stateRootDir, {
226489
226513
  event: "tool.execution",
226490
226514
  status: result.ok ? "completed" : "failed",
@@ -226493,11 +226517,23 @@ async function processToolCallBatch(input) {
226493
226517
  identityName: identity.name,
226494
226518
  toolName: toolCall.function.name,
226495
226519
  durationMs,
226496
- error: result.ok ? void 0 : readToolFailureError(result.output),
226520
+ error: failureError,
226497
226521
  details: {
226498
226522
  changedPathCount: metadata?.changedPaths?.length ?? 0
226499
226523
  }
226500
226524
  });
226525
+ await sessionEvents.append({
226526
+ type: result.ok ? "tool.completed" : "tool.failed",
226527
+ sessionId: session.id,
226528
+ cwd: options.cwd,
226529
+ details: buildToolFinishedEventDetails({
226530
+ toolCall,
226531
+ identity,
226532
+ durationMs,
226533
+ changedPathCount: metadata?.changedPaths?.length ?? 0,
226534
+ error: failureError ? formatToolFailureError(failureError) : void 0
226535
+ })
226536
+ });
226501
226537
  if (metadata?.outputGovernance) {
226502
226538
  await recordObservabilityEvent(projectContext.stateRootDir, {
226503
226539
  event: "tool.output",
@@ -226591,6 +226627,33 @@ async function processToolCallBatch(input) {
226591
226627
  }
226592
226628
  };
226593
226629
  }
226630
+ function buildToolStartedEventDetails(toolCall, identity) {
226631
+ return {
226632
+ toolName: toolCall.function.name,
226633
+ toolCallId: toolCall.id,
226634
+ identityKind: identity.kind,
226635
+ identityName: identity.name,
226636
+ argumentsPreview: previewToolArguments(toolCall.function.arguments)
226637
+ };
226638
+ }
226639
+ function buildToolFinishedEventDetails(input) {
226640
+ return {
226641
+ toolName: input.toolCall.function.name,
226642
+ toolCallId: input.toolCall.id,
226643
+ identityKind: input.identity.kind,
226644
+ identityName: input.identity.name,
226645
+ durationMs: input.durationMs,
226646
+ changedPathCount: input.changedPathCount,
226647
+ error: input.error
226648
+ };
226649
+ }
226650
+ function previewToolArguments(rawArgs) {
226651
+ const normalized = rawArgs.replace(/\s+/g, " ").trim();
226652
+ return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
226653
+ }
226654
+ function formatToolFailureError(error) {
226655
+ return error.code ? `${error.code}: ${error.message}` : error.message;
226656
+ }
226594
226657
  var init_toolBatchLifecycle = __esm({
226595
226658
  "src/agent/turn/toolBatchLifecycle.ts"() {
226596
226659
  "use strict";
@@ -226603,6 +226666,7 @@ var init_toolBatchLifecycle = __esm({
226603
226666
  init_persistence();
226604
226667
  init_toolBatch();
226605
226668
  init_writer();
226669
+ init_events();
226606
226670
  init_abort();
226607
226671
  init_toolFailure();
226608
226672
  }
@@ -226718,13 +226782,13 @@ function createChangeId() {
226718
226782
  const random = import_node_crypto4.default.randomUUID().slice(0, 8);
226719
226783
  return `${date}-${random}`;
226720
226784
  }
226721
- var import_node_crypto4, import_promises30, import_node_path42, ChangeStore;
226785
+ var import_node_crypto4, import_promises31, import_node_path43, ChangeStore;
226722
226786
  var init_store5 = __esm({
226723
226787
  "src/agent/changes/store.ts"() {
226724
226788
  "use strict";
226725
226789
  import_node_crypto4 = __toESM(require("crypto"));
226726
- import_promises30 = __toESM(require("fs/promises"));
226727
- import_node_path42 = __toESM(require("path"));
226790
+ import_promises31 = __toESM(require("fs/promises"));
226791
+ import_node_path43 = __toESM(require("path"));
226728
226792
  ChangeStore = class {
226729
226793
  constructor(changesDir) {
226730
226794
  this.changesDir = changesDir;
@@ -226732,8 +226796,8 @@ var init_store5 = __esm({
226732
226796
  async record(input) {
226733
226797
  const id = createChangeId();
226734
226798
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
226735
- const blobDir = import_node_path42.default.join(this.changesDir, id);
226736
- await import_promises30.default.mkdir(blobDir, { recursive: true });
226799
+ const blobDir = import_node_path43.default.join(this.changesDir, id);
226800
+ await import_promises31.default.mkdir(blobDir, { recursive: true });
226737
226801
  const operations = await Promise.all(
226738
226802
  input.operations.map(async (operation, index) => {
226739
226803
  const beforeSnapshotPath = await this.writeSnapshot(
@@ -226773,21 +226837,21 @@ var init_store5 = __esm({
226773
226837
  preview: input.preview,
226774
226838
  operations
226775
226839
  };
226776
- await import_promises30.default.mkdir(this.changesDir, { recursive: true });
226777
- await import_promises30.default.writeFile(this.getMetadataPath(id), `${JSON.stringify(record, null, 2)}
226840
+ await import_promises31.default.mkdir(this.changesDir, { recursive: true });
226841
+ await import_promises31.default.writeFile(this.getMetadataPath(id), `${JSON.stringify(record, null, 2)}
226778
226842
  `, "utf8");
226779
226843
  return record;
226780
226844
  }
226781
226845
  async list(limit = 20) {
226782
- await import_promises30.default.mkdir(this.changesDir, { recursive: true });
226783
- const entries = await import_promises30.default.readdir(this.changesDir, { withFileTypes: true });
226846
+ await import_promises31.default.mkdir(this.changesDir, { recursive: true });
226847
+ const entries = await import_promises31.default.readdir(this.changesDir, { withFileTypes: true });
226784
226848
  const changes = await Promise.all(
226785
- entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => this.load(import_node_path42.default.basename(entry.name, ".json")))
226849
+ entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => this.load(import_node_path43.default.basename(entry.name, ".json")))
226786
226850
  );
226787
226851
  return changes.sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, limit);
226788
226852
  }
226789
226853
  async load(id) {
226790
- const raw = await import_promises30.default.readFile(this.getMetadataPath(id), "utf8");
226854
+ const raw = await import_promises31.default.readFile(this.getMetadataPath(id), "utf8");
226791
226855
  return JSON.parse(raw);
226792
226856
  }
226793
226857
  async loadLatestUndoable() {
@@ -226811,17 +226875,17 @@ var init_store5 = __esm({
226811
226875
  restoredPaths.push(operation.path);
226812
226876
  if (operation.beforeSnapshotPath) {
226813
226877
  const buffer = await this.readSnapshot(operation.beforeSnapshotPath);
226814
- await import_promises30.default.mkdir(import_node_path42.default.dirname(operation.path), { recursive: true });
226815
- await import_promises30.default.writeFile(operation.path, buffer);
226878
+ await import_promises31.default.mkdir(import_node_path43.default.dirname(operation.path), { recursive: true });
226879
+ await import_promises31.default.writeFile(operation.path, buffer);
226816
226880
  continue;
226817
226881
  }
226818
- await import_promises30.default.rm(operation.path, { force: true });
226882
+ await import_promises31.default.rm(operation.path, { force: true });
226819
226883
  }
226820
226884
  const updated = {
226821
226885
  ...record,
226822
226886
  undoneAt: (/* @__PURE__ */ new Date()).toISOString()
226823
226887
  };
226824
- await import_promises30.default.writeFile(this.getMetadataPath(updated.id), `${JSON.stringify(updated, null, 2)}
226888
+ await import_promises31.default.writeFile(this.getMetadataPath(updated.id), `${JSON.stringify(updated, null, 2)}
226825
226889
  `, "utf8");
226826
226890
  return {
226827
226891
  record: updated,
@@ -226829,19 +226893,19 @@ var init_store5 = __esm({
226829
226893
  };
226830
226894
  }
226831
226895
  getMetadataPath(id) {
226832
- return import_node_path42.default.join(this.changesDir, `${id}.json`);
226896
+ return import_node_path43.default.join(this.changesDir, `${id}.json`);
226833
226897
  }
226834
226898
  async writeSnapshot(blobDir, label, buffer) {
226835
226899
  if (!buffer) {
226836
226900
  return void 0;
226837
226901
  }
226838
226902
  const fileName = `${label}.bin`;
226839
- const absolutePath = import_node_path42.default.join(blobDir, fileName);
226840
- await import_promises30.default.writeFile(absolutePath, buffer);
226841
- return import_node_path42.default.relative(this.changesDir, absolutePath);
226903
+ const absolutePath = import_node_path43.default.join(blobDir, fileName);
226904
+ await import_promises31.default.writeFile(absolutePath, buffer);
226905
+ return import_node_path43.default.relative(this.changesDir, absolutePath);
226842
226906
  }
226843
226907
  async readSnapshot(relativePath) {
226844
- return import_promises30.default.readFile(import_node_path42.default.join(this.changesDir, relativePath));
226908
+ return import_promises31.default.readFile(import_node_path43.default.join(this.changesDir, relativePath));
226845
226909
  }
226846
226910
  };
226847
226911
  }
@@ -227233,14 +227297,14 @@ async function recordHostTurnFinished(rootDir, input) {
227233
227297
  });
227234
227298
  }
227235
227299
  function resolveHostStateRoot(stateDir, fallbackCwd) {
227236
- const kittyDir = import_node_path43.default.dirname(stateDir);
227237
- return import_node_path43.default.basename(kittyDir).toLowerCase() === PROJECT_STATE_DIR_NAME ? import_node_path43.default.dirname(kittyDir) : fallbackCwd;
227300
+ const kittyDir = import_node_path44.default.dirname(stateDir);
227301
+ return import_node_path44.default.basename(kittyDir).toLowerCase() === PROJECT_STATE_DIR_NAME ? import_node_path44.default.dirname(kittyDir) : fallbackCwd;
227238
227302
  }
227239
- var import_node_path43, QueuedHostMessageRecorder;
227303
+ var import_node_path44, QueuedHostMessageRecorder;
227240
227304
  var init_hostEvents = __esm({
227241
227305
  "src/observability/hostEvents.ts"() {
227242
227306
  "use strict";
227243
- import_node_path43 = __toESM(require("path"));
227307
+ import_node_path44 = __toESM(require("path"));
227244
227308
  init_writer();
227245
227309
  init_statePaths();
227246
227310
  QueuedHostMessageRecorder = class {
@@ -227270,63 +227334,6 @@ var init_hostEvents = __esm({
227270
227334
  }
227271
227335
  });
227272
227336
 
227273
- // src/session/events.ts
227274
- var events_exports = {};
227275
- __export(events_exports, {
227276
- SessionEventStore: () => SessionEventStore
227277
- });
227278
- function createEventId() {
227279
- return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${Math.random().toString(16).slice(2, 10)}`;
227280
- }
227281
- function sanitizeSessionId2(sessionId) {
227282
- return sessionId.replace(/[^a-zA-Z0-9_.-]/g, "_");
227283
- }
227284
- var import_promises31, import_node_path44, SessionEventStore;
227285
- var init_events = __esm({
227286
- "src/session/events.ts"() {
227287
- "use strict";
227288
- import_promises31 = __toESM(require("fs/promises"));
227289
- import_node_path44 = __toESM(require("path"));
227290
- SessionEventStore = class {
227291
- constructor(eventsDir) {
227292
- this.eventsDir = eventsDir;
227293
- }
227294
- async append(event) {
227295
- const record = {
227296
- id: createEventId(),
227297
- createdAt: event.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
227298
- type: event.type,
227299
- sessionId: event.sessionId,
227300
- cwd: event.cwd,
227301
- host: event.host,
227302
- message: event.message,
227303
- details: event.details
227304
- };
227305
- await import_promises31.default.mkdir(this.eventsDir, { recursive: true });
227306
- await import_promises31.default.appendFile(this.getSessionEventPath(event.sessionId), `${JSON.stringify(record)}
227307
- `, "utf8");
227308
- return record;
227309
- }
227310
- async list(sessionId, limit = 100) {
227311
- const filePath = this.getSessionEventPath(sessionId);
227312
- let raw = "";
227313
- try {
227314
- raw = await import_promises31.default.readFile(filePath, "utf8");
227315
- } catch (error) {
227316
- if (error.code === "ENOENT") {
227317
- return [];
227318
- }
227319
- throw error;
227320
- }
227321
- return raw.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line)).slice(-limit);
227322
- }
227323
- getSessionEventPath(sessionId) {
227324
- return import_node_path44.default.join(this.eventsDir, `${sanitizeSessionId2(sessionId)}.jsonl`);
227325
- }
227326
- };
227327
- }
227328
- });
227329
-
227330
227337
  // src/host/toolRegistry.ts
227331
227338
  async function createHostToolRegistry(config, options = {}) {
227332
227339
  const extraTools = options.extraTools ?? [];
@@ -231040,7 +231047,7 @@ function createCliOutputPort() {
231040
231047
  }
231041
231048
  };
231042
231049
  }
231043
- var init_output = __esm({
231050
+ var init_output2 = __esm({
231044
231051
  "src/shell/cli/output.ts"() {
231045
231052
  "use strict";
231046
231053
  init_console();
@@ -231387,7 +231394,7 @@ function createCliInteractionShell() {
231387
231394
  var init_shell = __esm({
231388
231395
  "src/shell/cli/shell.ts"() {
231389
231396
  "use strict";
231390
- init_output();
231397
+ init_output2();
231391
231398
  init_readlineInput();
231392
231399
  init_turnDisplay();
231393
231400
  }
@@ -231641,6 +231648,9 @@ function formatSessionEventsForCli(result) {
231641
231648
  return result.events.map(formatSessionEventForCli).join("\n");
231642
231649
  }
231643
231650
  function formatSessionEventForCli(event) {
231651
+ if (isToolEvent(event)) {
231652
+ return formatToolEventForCli(event);
231653
+ }
231644
231654
  const parts = [
231645
231655
  event.createdAt,
231646
231656
  event.type,
@@ -231650,10 +231660,39 @@ function formatSessionEventForCli(event) {
231650
231660
  ];
231651
231661
  return parts.filter(Boolean).join(" ");
231652
231662
  }
231663
+ function formatToolEventForCli(event) {
231664
+ const details = event.details ?? {};
231665
+ const toolName = readString7(details.toolName);
231666
+ const toolCallId = readString7(details.toolCallId);
231667
+ const durationMs = readNumber3(details.durationMs);
231668
+ const changedPathCount = readNumber3(details.changedPathCount);
231669
+ const error = readString7(details.error);
231670
+ const parts = [
231671
+ event.createdAt,
231672
+ event.type,
231673
+ toolName ? `tool=${toolName}` : void 0,
231674
+ toolCallId ? `call=${toolCallId}` : void 0,
231675
+ durationMs === void 0 ? void 0 : `duration=${durationMs}ms`,
231676
+ changedPathCount === void 0 ? void 0 : `changed=${changedPathCount}`,
231677
+ error ? `error=${formatInline(error)}` : void 0,
231678
+ event.host ? `host=${event.host}` : void 0,
231679
+ event.message ? `message=${formatInline(event.message)}` : void 0
231680
+ ];
231681
+ return parts.filter(Boolean).join(" ");
231682
+ }
231683
+ function isToolEvent(event) {
231684
+ return event.type === "tool.started" || event.type === "tool.completed" || event.type === "tool.failed";
231685
+ }
231653
231686
  function formatInline(value) {
231654
231687
  const normalized = value.replace(/\s+/g, " ").trim();
231655
231688
  return normalized.length > 120 ? `${normalized.slice(0, 117)}...` : normalized;
231656
231689
  }
231690
+ function readString7(value) {
231691
+ return typeof value === "string" && value.length > 0 ? value : void 0;
231692
+ }
231693
+ function readNumber3(value) {
231694
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
231695
+ }
231657
231696
  var init_events3 = __esm({
231658
231697
  "src/cli/commands/events.ts"() {
231659
231698
  "use strict";