flavor-code 1.3.10 → 1.3.12

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.
@@ -209,6 +209,13 @@ var ProviderConfigSchema = z2.object({
209
209
  cheapModel: z2.string().min(1).optional(),
210
210
  models: z2.array(z2.string().min(1)).max(100).optional(),
211
211
  maxOutputTokens: z2.number().int().positive().optional(),
212
+ // Extended-thinking budget in output tokens for the Anthropic protocol.
213
+ // Defaults to DEFAULT_THINKING_BUDGET; set to 0 to disable the request
214
+ // parameter. Provider thinking deltas are forwarded whenever they arrive.
215
+ thinkingBudget: z2.number().int().min(0).optional(),
216
+ // Reasoning effort requested from the OpenAI Responses protocol. Omitted by
217
+ // default so strict compatible gateways never see an unknown parameter.
218
+ thinkingEffort: z2.enum(["low", "medium", "high"]).optional(),
212
219
  // Send the Claude Code client fingerprint (User-Agent/x-app) with Anthropic requests,
213
220
  // for gateways that restrict the Anthropic protocol to Claude clients.
214
221
  claudeClient: z2.boolean().optional(),
@@ -5874,7 +5881,12 @@ function providerValidMessages(input) {
5874
5881
  const calls = original.toolCalls.filter((call) => call.id && call.name && availableResults.has(call.id));
5875
5882
  calls.forEach((call) => announced.add(call.id));
5876
5883
  if (!original.content && calls.length === 0) continue;
5877
- output.push({ role: "assistant", content: original.content, ...calls.length === 0 ? {} : { toolCalls: calls.map((call) => ({ ...call })) } });
5884
+ output.push({
5885
+ role: "assistant",
5886
+ content: original.content,
5887
+ ...calls.length === 0 ? {} : { toolCalls: calls.map((call) => ({ ...call })) },
5888
+ ...original.thinkingBlocks === void 0 ? {} : { thinkingBlocks: original.thinkingBlocks.map((block) => ({ ...block })) }
5889
+ });
5878
5890
  continue;
5879
5891
  }
5880
5892
  if (original.role === "tool") {
@@ -6095,9 +6107,10 @@ function withStructuredOutput(options) {
6095
6107
  throw new Error("Structured output retry delays must be non-negative integers");
6096
6108
  }
6097
6109
  }
6098
- const tool = modelToolFromZod(options.name, options.description, options.schema);
6110
+ const generatedTool = options.modelInputSchema === void 0 ? modelToolFromZod(options.name, options.description, options.schema) : { name: options.name, description: options.description, inputSchema: options.modelInputSchema };
6111
+ const tool = options.modelStrict === void 0 ? generatedTool : { ...generatedTool, strict: options.modelStrict };
6099
6112
  const maxAttempts = retry.maxRetries + 1;
6100
- const jsonSchema = tool.inputSchema;
6113
+ const validationSchema = jsonSchemaFromZod(options.schema);
6101
6114
  const model = {
6102
6115
  async *stream(request) {
6103
6116
  let candidate;
@@ -6170,7 +6183,7 @@ function withStructuredOutput(options) {
6170
6183
  }
6171
6184
  }
6172
6185
  if (candidateCount === 1 && candidate !== null && typeof candidate === "object") {
6173
- candidate = coerceByJsonSchema(candidate, jsonSchema);
6186
+ candidate = coerceByJsonSchema(candidate, validationSchema);
6174
6187
  }
6175
6188
  if (candidateCount !== 1) {
6176
6189
  attemptError = {
@@ -6242,7 +6255,13 @@ function withStructuredOutput(options) {
6242
6255
  return model;
6243
6256
  }
6244
6257
  function strictJsonSchema(schema) {
6245
- return ensureStrictSchema(z12.toJSONSchema(schema));
6258
+ return strictJsonSchemaObject(jsonSchemaFromZod(schema));
6259
+ }
6260
+ function jsonSchemaFromZod(schema) {
6261
+ return z12.toJSONSchema(schema);
6262
+ }
6263
+ function strictJsonSchemaObject(schema) {
6264
+ return ensureStrictSchema(schema);
6246
6265
  }
6247
6266
  function extractJsonObject(text) {
6248
6267
  const trimmed = text.trim();
@@ -6290,6 +6309,7 @@ function coerceByJsonSchema(value, schema) {
6290
6309
  const node = schema;
6291
6310
  const branches = [
6292
6311
  ...Array.isArray(node.anyOf) ? node.anyOf : [],
6312
+ ...Array.isArray(node.oneOf) ? node.oneOf : [],
6293
6313
  ...Array.isArray(node.allOf) ? node.allOf : []
6294
6314
  ].filter((branch) => branch !== null && typeof branch === "object");
6295
6315
  const types = [];
@@ -6309,40 +6329,108 @@ function coerceByJsonSchema(value, schema) {
6309
6329
  if (lowered === "true") return true;
6310
6330
  if (lowered === "false") return false;
6311
6331
  }
6332
+ if (types.includes("object") && !types.includes("string") && typeof value === "string") {
6333
+ const trimmed = value.trim();
6334
+ if (trimmed.startsWith("{")) {
6335
+ try {
6336
+ const parsed = JSON.parse(trimmed);
6337
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
6338
+ return coerceByJsonSchema(parsed, node);
6339
+ }
6340
+ } catch {
6341
+ }
6342
+ }
6343
+ }
6344
+ if (types.includes("array") && !Array.isArray(value) && value !== null && value !== void 0 && !types.some((type) => matchesJsonSchemaType(value, type))) {
6345
+ const arraySchema = [node, ...branches].find((candidate) => {
6346
+ const type = candidate.type;
6347
+ return type === "array" || Array.isArray(type) && type.includes("array");
6348
+ });
6349
+ if (typeof value === "string" && value.trim().startsWith("[")) {
6350
+ try {
6351
+ const parsed = JSON.parse(value.trim());
6352
+ if (Array.isArray(parsed)) return coerceByJsonSchema(parsed, arraySchema ?? node);
6353
+ } catch {
6354
+ }
6355
+ }
6356
+ return [arraySchema?.items === void 0 ? value : coerceByJsonSchema(value, arraySchema.items)];
6357
+ }
6312
6358
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
6313
- const properties = node.properties ?? branches.find((branch) => branch.properties)?.properties;
6359
+ const objectSchema = [node, ...branches].find((candidate) => candidate.type === "object" || candidate.properties !== void 0);
6360
+ const properties = objectSchema?.properties;
6314
6361
  if (properties !== void 0) {
6315
6362
  const output = {};
6363
+ const required2 = new Set(Array.isArray(objectSchema?.required) ? objectSchema.required : []);
6316
6364
  for (const [key, child] of Object.entries(value)) {
6317
- output[key] = properties[key] !== void 0 ? coerceByJsonSchema(child, properties[key]) : child;
6365
+ const childSchema = properties[key];
6366
+ if (child === null && childSchema !== void 0 && !required2.has(key) && !jsonSchemaAllowsNull(childSchema)) {
6367
+ continue;
6368
+ }
6369
+ output[key] = childSchema !== void 0 ? coerceByJsonSchema(child, childSchema) : child;
6318
6370
  }
6319
6371
  return output;
6320
6372
  }
6321
6373
  }
6322
- if (types.includes("array") && Array.isArray(value) && node.items !== void 0) {
6323
- return value.map((item) => coerceByJsonSchema(item, node.items));
6374
+ if (types.includes("array") && Array.isArray(value)) {
6375
+ const arraySchema = [node, ...branches].find((candidate) => {
6376
+ const type = candidate.type;
6377
+ return type === "array" || Array.isArray(type) && type.includes("array");
6378
+ });
6379
+ if (arraySchema?.items !== void 0) {
6380
+ return value.map((item) => coerceByJsonSchema(item, arraySchema.items));
6381
+ }
6324
6382
  }
6325
6383
  return value;
6326
6384
  }
6385
+ function matchesJsonSchemaType(value, type) {
6386
+ if (type === "null") return value === null;
6387
+ if (type === "array") return Array.isArray(value);
6388
+ if (type === "object") return value !== null && typeof value === "object" && !Array.isArray(value);
6389
+ if (type === "integer") return typeof value === "number" && Number.isInteger(value);
6390
+ if (type === "number") return typeof value === "number";
6391
+ return typeof value === type;
6392
+ }
6393
+ function jsonSchemaAllowsNull(schema) {
6394
+ if (schema === null || typeof schema !== "object") return false;
6395
+ const node = schema;
6396
+ if (node.type === "null" || Array.isArray(node.type) && node.type.includes("null")) return true;
6397
+ return [node.anyOf, node.oneOf].some((branches) => Array.isArray(branches) && branches.some((branch) => jsonSchemaAllowsNull(branch)));
6398
+ }
6327
6399
  function modelToolFromZod(name, description, schema) {
6328
6400
  return { name, description, inputSchema: strictJsonSchema(schema) };
6329
6401
  }
6330
6402
  function ensureStrictSchema(schema) {
6403
+ const output = { ...schema };
6404
+ for (const keyword of ["anyOf", "oneOf", "allOf", "prefixItems"]) {
6405
+ const branches = schema[keyword];
6406
+ if (Array.isArray(branches)) {
6407
+ output[keyword] = branches.map((branch) => typeof branch === "object" && branch !== null ? ensureStrictSchema(branch) : branch);
6408
+ }
6409
+ }
6410
+ if (typeof schema.items === "object" && schema.items !== null) {
6411
+ output.items = ensureStrictSchema(schema.items);
6412
+ }
6413
+ for (const keyword of ["$defs", "definitions"]) {
6414
+ const definitions = schema[keyword];
6415
+ if (typeof definitions === "object" && definitions !== null && !Array.isArray(definitions)) {
6416
+ output[keyword] = Object.fromEntries(Object.entries(definitions).map(([key, definition]) => [
6417
+ key,
6418
+ typeof definition === "object" && definition !== null ? ensureStrictSchema(definition) : definition
6419
+ ]));
6420
+ }
6421
+ }
6331
6422
  if (schema.type !== "object" || typeof schema.properties !== "object" || schema.properties === null) {
6332
- return schema;
6423
+ return output;
6333
6424
  }
6334
- const required2 = Array.isArray(schema.required) ? [...schema.required] : [];
6335
- const requiredSet = new Set(required2);
6425
+ const requiredSet = new Set(Array.isArray(schema.required) ? schema.required : []);
6336
6426
  const properties = {};
6337
6427
  for (const [key, value] of Object.entries(schema.properties)) {
6338
6428
  const child = typeof value === "object" && value !== null ? ensureStrictSchema(value) : value;
6339
- if (requiredSet.has(key)) properties[key] = child;
6340
- else {
6341
- properties[key] = { anyOf: [child, { type: "null" }] };
6342
- requiredSet.add(key);
6343
- }
6429
+ if (requiredSet.has(key) || jsonSchemaAllowsNull(child)) properties[key] = child;
6430
+ else properties[key] = { anyOf: [child, { type: "null" }] };
6431
+ requiredSet.add(key);
6344
6432
  }
6345
- return { ...schema, additionalProperties: false, required: [...requiredSet], properties };
6433
+ return { ...output, additionalProperties: false, required: [...requiredSet], properties };
6346
6434
  }
6347
6435
  function repairMessages(messages, tool, original, candidate, error) {
6348
6436
  const details = [
@@ -6521,6 +6609,7 @@ var AgentLoop = class {
6521
6609
  }
6522
6610
  let assistantText = "";
6523
6611
  const collectedToolCalls = [];
6612
+ const collectedThinking = [];
6524
6613
  let completed = false;
6525
6614
  let reactiveRetried = false;
6526
6615
  let attempt = 1;
@@ -6558,6 +6647,7 @@ var AgentLoop = class {
6558
6647
  }
6559
6648
  assistantText = "";
6560
6649
  collectedToolCalls.length = 0;
6650
+ collectedThinking.length = 0;
6561
6651
  completed = false;
6562
6652
  let terminalError;
6563
6653
  let providerError = false;
@@ -6578,6 +6668,10 @@ var AgentLoop = class {
6578
6668
  assistantText += event.text;
6579
6669
  accumulatedText += event.text;
6580
6670
  yield event;
6671
+ } else if (event.type === "thinking") {
6672
+ yield event;
6673
+ } else if (event.type === "thinking-block") {
6674
+ collectedThinking.push({ text: event.text, ...event.signature === void 0 ? {} : { signature: event.signature } });
6581
6675
  } else if (event.type === "tool-call") {
6582
6676
  collectedToolCalls.push({ kind: "valid", id: event.id, name: event.name, input: event.input });
6583
6677
  } else if (event.type === "invalid-tool-call") {
@@ -6712,19 +6806,11 @@ var AgentLoop = class {
6712
6806
  });
6713
6807
  continue;
6714
6808
  }
6715
- const validation = collected.kind === "valid" ? this.#options.runtime.validate(collected) : { ok: false, error: collected.error };
6809
+ const validation = collected.kind === "valid" ? this.#options.runtime.normalize(collected) : { ok: false, error: collected.error };
6716
6810
  if (validation.ok) {
6717
6811
  toolCalls.push({ id: collected.id, name: collected.name, input: validation.input });
6718
6812
  continue;
6719
6813
  }
6720
- if (collected.kind === "valid") {
6721
- const coerced = coerceByJsonSchema(collected.input, strictJsonSchema(definition.inputSchema));
6722
- const revalidation = this.#options.runtime.validate({ ...collected, input: coerced });
6723
- if (revalidation.ok) {
6724
- toolCalls.push({ id: collected.id, name: collected.name, input: revalidation.input });
6725
- continue;
6726
- }
6727
- }
6728
6814
  const repairModelId = this.#options.fallbackModelId ?? this.#options.modelId;
6729
6815
  const invalidOutput = collected.kind === "invalid" ? collected.rawInput : serializeToolInput(collected.input);
6730
6816
  const structured = withStructuredOutput({
@@ -6733,6 +6819,8 @@ var AgentLoop = class {
6733
6819
  name: definition.name,
6734
6820
  description: definition.description,
6735
6821
  schema: definition.inputSchema,
6822
+ ...definition.modelInputSchema === void 0 ? {} : { modelInputSchema: definition.modelInputSchema },
6823
+ ...definition.modelStrict === void 0 ? {} : { modelStrict: definition.modelStrict },
6736
6824
  beforeAttempt: async ({ attempt: repairAttempt, maxAttempts: repairMaxAttempts, messageCount }) => {
6737
6825
  const before = await this.#options.hooks.emit({
6738
6826
  version: 1,
@@ -6931,7 +7019,8 @@ var AgentLoop = class {
6931
7019
  const assistantMessage = {
6932
7020
  role: "assistant",
6933
7021
  content: assistantText,
6934
- toolCalls: toolCalls.map(({ id, name, input }) => ({ id, name, input }))
7022
+ toolCalls: toolCalls.map(({ id, name, input }) => ({ id, name, input })),
7023
+ ...collectedThinking.length === 0 ? {} : { thinkingBlocks: collectedThinking.map((block) => ({ ...block })) }
6935
7024
  };
6936
7025
  this.#options.context.appendMany([assistantMessage, ...stagedMessages]);
6937
7026
  for (const { call, result } of stagedResults) {
@@ -7658,12 +7747,24 @@ var ToolRuntime = class {
7658
7747
  throw error;
7659
7748
  }
7660
7749
  }
7750
+ /**
7751
+ * Validates a call and applies the same conservative provider-argument
7752
+ * normalization used by the agent loop before falling back to repair.
7753
+ */
7754
+ normalize(call) {
7755
+ const validation = this.validate(call);
7756
+ if (validation.ok || validation.error.code === "unknown_tool") return validation;
7757
+ const tool = this.#tools.get(call.name);
7758
+ if (tool === void 0) return validation;
7759
+ const input = coerceByJsonSchema(call.input, jsonSchemaFromZod(tool.inputSchema));
7760
+ return this.validate({ ...call, input });
7761
+ }
7661
7762
  async #execute(call, context) {
7662
7763
  const tool = this.#tools.get(call.name);
7663
7764
  if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
7664
7765
  const signal = context.signal ?? new AbortController().signal;
7665
7766
  const toolCallId = call.id ?? randomUUID7();
7666
- const validation = this.validate(call);
7767
+ const validation = this.normalize(call);
7667
7768
  if (!validation.ok) {
7668
7769
  return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
7669
7770
  }
@@ -9414,6 +9515,7 @@ var PluginHost = class {
9414
9515
  description: contribution.description,
9415
9516
  inputSchema: z18.unknown(),
9416
9517
  ...contribution.modelInputSchema === void 0 ? {} : { modelInputSchema: contribution.modelInputSchema },
9518
+ ...contribution.modelInputSchema === void 0 ? {} : { modelStrict: false },
9417
9519
  ...contribution.readOnly === void 0 ? {} : { readOnly: contribution.readOnly },
9418
9520
  paths: () => [],
9419
9521
  execute: (input, _signal, toolContext) => session.invoke("tool", contribution.name, [
@@ -13204,6 +13306,7 @@ var CLAUDE_CLIENT_HEADERS = {
13204
13306
  "x-app": "cli"
13205
13307
  };
13206
13308
  var DEFAULT_MAX_OUTPUT_TOKENS = 32768;
13309
+ var DEFAULT_THINKING_BUDGET = 8192;
13207
13310
  var MAX_CACHE_MARKERS = 4;
13208
13311
  function collectCacheMarkers(system, messages) {
13209
13312
  const markers = [];
@@ -13262,9 +13365,26 @@ function formatCacheUsage(model, snapshot4, shape) {
13262
13365
  ...shape === void 0 ? {} : { requestMessages: shape.messages, requestMarkers: shape.markers }
13263
13366
  });
13264
13367
  }
13368
+ function stripThinkingBlocks(messages) {
13369
+ for (const message2 of messages) {
13370
+ if (message2.role !== "assistant" || !Array.isArray(message2.content)) continue;
13371
+ const blocks = message2.content;
13372
+ if (!blocks.some((block) => block?.type === "thinking")) continue;
13373
+ message2.content = blocks.filter((block) => block?.type !== "thinking");
13374
+ }
13375
+ }
13376
+ function isThinkingParamRejected(error) {
13377
+ const status = error?.status;
13378
+ if (status !== void 0 && status !== 400) return false;
13379
+ const message2 = error instanceof Error ? error.message : String(error ?? "");
13380
+ return /\bthinking\b/iu.test(message2) && /(budget_tokens|expected|not supported|unsupported|unknown|invalid|extra inputs|unrecognized)/iu.test(message2);
13381
+ }
13265
13382
  var AnthropicModelAdapter = class {
13266
13383
  client;
13267
13384
  maxOutputTokens;
13385
+ thinkingBudget;
13386
+ /** Set once an endpoint rejects the `thinking` parameter; later requests stop sending it. */
13387
+ #thinkingRejected = false;
13268
13388
  debugUsage;
13269
13389
  constructor(options) {
13270
13390
  const maxOutputTokens = options.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
@@ -13272,6 +13392,7 @@ var AnthropicModelAdapter = class {
13272
13392
  throw new Error("maxOutputTokens must be a positive integer");
13273
13393
  }
13274
13394
  this.maxOutputTokens = maxOutputTokens;
13395
+ this.thinkingBudget = options.thinkingBudget ?? DEFAULT_THINKING_BUDGET;
13275
13396
  this.debugUsage = options.debugUsage ?? isEnvTruthy(process.env.FLAVOR_DEBUG_USAGE);
13276
13397
  this.client = options.client ?? new Anthropic({
13277
13398
  ...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
@@ -13279,6 +13400,16 @@ var AnthropicModelAdapter = class {
13279
13400
  ...options.headers === void 0 ? {} : { defaultHeaders: options.headers }
13280
13401
  });
13281
13402
  }
13403
+ /**
13404
+ * Extended-thinking request parameter when enabled, otherwise undefined.
13405
+ * Anthropic requires the budget to fit inside max_tokens with a 1024 floor.
13406
+ */
13407
+ #thinkingParam() {
13408
+ if (this.#thinkingRejected || this.thinkingBudget <= 0) return void 0;
13409
+ const budget = Math.min(this.thinkingBudget, this.maxOutputTokens - 1);
13410
+ if (budget < 1024) return void 0;
13411
+ return { type: "enabled", budget_tokens: budget };
13412
+ }
13282
13413
  #logUsage(request, snapshot4, shape) {
13283
13414
  const line = formatCacheUsage(request.model, snapshot4, shape);
13284
13415
  if (this.debugUsage) {
@@ -13334,7 +13465,13 @@ var AnthropicModelAdapter = class {
13334
13465
  }
13335
13466
  messages.push({ role: "user", content: results });
13336
13467
  } else if (message2.role === "assistant" && message2.toolCalls?.length) {
13468
+ const echoThinking = this.#thinkingRejected ? [] : (message2.thinkingBlocks ?? []).filter((block) => block.signature !== void 0);
13337
13469
  const content = [
13470
+ ...echoThinking.map((block) => ({
13471
+ type: "thinking",
13472
+ thinking: block.text,
13473
+ signature: block.signature
13474
+ })),
13338
13475
  ...modelContentText(message2.content) ? [{ type: "text", text: modelContentText(message2.content) }] : [],
13339
13476
  ...message2.toolCalls.map((call) => ({
13340
13477
  type: "tool_use",
@@ -13396,18 +13533,56 @@ var AnthropicModelAdapter = class {
13396
13533
  stream: true,
13397
13534
  messages,
13398
13535
  ...system ? { system } : {},
13536
+ ...(() => {
13537
+ const thinking = this.#thinkingParam();
13538
+ return thinking === void 0 ? {} : { thinking };
13539
+ })(),
13399
13540
  tools: [...request.tools].sort((a, b) => a.name.localeCompare(b.name)).map((tool) => ({
13400
13541
  name: tool.name,
13401
13542
  description: tool.description,
13402
13543
  input_schema: { ...tool.inputSchema, type: "object" }
13403
13544
  }))
13404
13545
  };
13405
- const stream = await this.client.messages.create(body, { signal: request.signal });
13546
+ let stream;
13547
+ try {
13548
+ stream = await this.client.messages.create(body, { signal: request.signal });
13549
+ } catch (error) {
13550
+ if (this.thinkingBudget > 0 && !this.#thinkingRejected && isThinkingParamRejected(error)) {
13551
+ this.#thinkingRejected = true;
13552
+ delete body.thinking;
13553
+ stripThinkingBlocks(body.messages);
13554
+ stream = await this.client.messages.create(body, { signal: request.signal });
13555
+ } else {
13556
+ throw error;
13557
+ }
13558
+ }
13559
+ const thinkingBuffers = /* @__PURE__ */ new Map();
13406
13560
  for await (const event of stream) {
13407
13561
  if (event.type === "message_start") {
13408
13562
  hasUsage = event.message?.usage !== void 0;
13409
13563
  inputTokens = updateInputUsage(inputUsage, event.message?.usage);
13410
13564
  outputTokens = event.message?.usage?.output_tokens ?? outputTokens;
13565
+ } else if (event.type === "content_block_start" && event.content_block?.type === "thinking" && event.index !== void 0) {
13566
+ thinkingBuffers.set(event.index, { text: "" });
13567
+ } else if (event.type === "content_block_delta" && event.delta?.type === "thinking_delta") {
13568
+ if (event.index !== void 0 && event.delta.thinking) {
13569
+ const pending2 = thinkingBuffers.get(event.index);
13570
+ if (pending2) pending2.text += event.delta.thinking;
13571
+ }
13572
+ if (event.delta.thinking) yield { type: "thinking", text: event.delta.thinking };
13573
+ } else if (event.type === "content_block_delta" && event.delta?.type === "signature_delta" && event.index !== void 0) {
13574
+ const pending2 = thinkingBuffers.get(event.index);
13575
+ if (pending2) pending2.signature = (pending2.signature ?? "") + (event.delta.signature ?? "");
13576
+ } else if (event.type === "content_block_stop" && event.index !== void 0 && thinkingBuffers.has(event.index)) {
13577
+ const pending2 = thinkingBuffers.get(event.index);
13578
+ if (pending2.text.length > 0 || pending2.signature !== void 0) {
13579
+ yield {
13580
+ type: "thinking-block",
13581
+ text: pending2.text,
13582
+ ...pending2.signature === void 0 ? {} : { signature: pending2.signature }
13583
+ };
13584
+ }
13585
+ thinkingBuffers.delete(event.index);
13411
13586
  } else if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && event.index !== void 0 && event.content_block.id && event.content_block.name) {
13412
13587
  pendingTools.set(event.index, {
13413
13588
  id: event.content_block.id,
@@ -13610,12 +13785,14 @@ function formatOpenAIUsage(model, breakdown) {
13610
13785
  var OpenAIModelAdapter = class {
13611
13786
  client;
13612
13787
  debugUsage;
13788
+ thinkingEffort;
13613
13789
  constructor(options) {
13614
13790
  this.client = options.client ?? new OpenAI({
13615
13791
  ...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
13616
13792
  ...options.baseURL === void 0 ? {} : { baseURL: options.baseURL }
13617
13793
  });
13618
13794
  this.debugUsage = options.debugUsage ?? isEnvTruthy(process.env.FLAVOR_DEBUG_USAGE);
13795
+ this.thinkingEffort = options.thinkingEffort;
13619
13796
  }
13620
13797
  #logUsage(model, breakdown) {
13621
13798
  if (breakdown === void 0) return;
@@ -13637,6 +13814,7 @@ var OpenAIModelAdapter = class {
13637
13814
  const body = {
13638
13815
  model: request.model,
13639
13816
  input: (await Promise.all(request.messages.map(toInput))).flat(),
13817
+ ...this.thinkingEffort === void 0 ? {} : { reasoning: { effort: this.thinkingEffort } },
13640
13818
  tools: [...request.tools].sort((a, b) => a.name.localeCompare(b.name)).map((tool) => ({
13641
13819
  type: "function",
13642
13820
  name: tool.name,
@@ -13669,6 +13847,8 @@ var OpenAIModelAdapter = class {
13669
13847
  }
13670
13848
  } else if (event.type === "response.output_text.delta" && event.delta) {
13671
13849
  yield { type: "text", text: event.delta };
13850
+ } else if ((event.type === "response.reasoning_summary_text.delta" || event.type === "response.reasoning_text.delta") && event.delta) {
13851
+ yield { type: "thinking", text: event.delta };
13672
13852
  } else if (event.type === "response.function_call_arguments.done" && event.output_index !== void 0 && event.name) {
13673
13853
  const callId = callIds.get(event.output_index);
13674
13854
  if (callId && !emittedCalls.has(event.output_index)) {
@@ -15218,6 +15398,13 @@ import { basename as basename5, dirname as dirname15, join as join23, relative a
15218
15398
  import { randomUUID as randomUUID13 } from "crypto";
15219
15399
  import { z as z27 } from "zod";
15220
15400
 
15401
+ // src/ui/thinking-line.ts
15402
+ var THINKING_MAX_STORED_CHARS = 4e3;
15403
+ function appendThinkingText(previous, delta) {
15404
+ const next = (previous ?? "") + delta;
15405
+ return next.length > THINKING_MAX_STORED_CHARS ? next.slice(-THINKING_MAX_STORED_CHARS) : next;
15406
+ }
15407
+
15221
15408
  // src/ui/transcript.ts
15222
15409
  function createTranscriptState() {
15223
15410
  return { completed: [], nextId: 1 };
@@ -15314,6 +15501,9 @@ function transcriptReducer(state, action) {
15314
15501
  if (event.type === "text") {
15315
15502
  return { ...state, active: addText(withoutModelActivity(state.active), event.text) };
15316
15503
  }
15504
+ if (event.type === "thinking") {
15505
+ return { ...state, active: attachThinking(state.active, event.text) };
15506
+ }
15317
15507
  if (event.type === "tool-start") return upsertStatus({ ...state, active: withoutModelActivity(state.active) }, {
15318
15508
  kind: "status",
15319
15509
  id: `tool:${event.id}`,
@@ -15614,6 +15804,18 @@ function finishActive(state) {
15614
15804
  ...state.taskSnapshot === void 0 ? {} : { taskSnapshot: state.taskSnapshot }
15615
15805
  };
15616
15806
  }
15807
+ function attachThinking(turn, text) {
15808
+ const blocks = [...turn.blocks];
15809
+ const index = blocks.findIndex((block2) => block2.kind === "status" && block2.activity === "model" && block2.state === "running");
15810
+ if (index < 0) return turn;
15811
+ const block = blocks[index];
15812
+ if (block === void 0 || block.kind !== "status") return turn;
15813
+ blocks[index] = {
15814
+ ...block,
15815
+ thinkingText: appendThinkingText(block.thinkingText, text)
15816
+ };
15817
+ return { ...turn, blocks };
15818
+ }
15617
15819
  function withoutModelActivity(turn, id) {
15618
15820
  const blocks = turn.blocks.filter((block) => block.kind !== "status" || block.activity !== "model" || id !== void 0 && block.id !== id);
15619
15821
  if (blocks.length === turn.blocks.length) return turn;
@@ -19923,7 +20125,7 @@ function compileDefinition(record3, scope2, workspace) {
19923
20125
  name: record3.name,
19924
20126
  description: record3.description,
19925
20127
  inputSchema,
19926
- modelInputSchema: record3.inputSchema,
20128
+ modelInputSchema: strictJsonSchemaObject(record3.inputSchema),
19927
20129
  modelStrict: true,
19928
20130
  ...record3.agents === void 0 ? {} : { agents: record3.agents },
19929
20131
  paths: () => [],
@@ -20413,16 +20615,24 @@ var MVP_COMMANDS = [
20413
20615
  "fork",
20414
20616
  "pals",
20415
20617
  "chat",
20416
- "co-work"
20618
+ "co-work",
20619
+ "tool"
20417
20620
  ];
20418
- function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
20621
+ function parseSlashCommand(input, dynamicCommands = [], skillCommands = [], managedTools = []) {
20419
20622
  const trimmed = input.trim();
20420
20623
  if (!trimmed.startsWith("/")) return null;
20421
- const [rawName = "", ...args] = trimmed.slice(1).split(/\s+/);
20624
+ const match = /^(\S+)(?:\s+([\s\S]*))?$/u.exec(trimmed.slice(1));
20625
+ const rawName = match?.[1] ?? "";
20626
+ const rawArguments = match?.[2] ?? "";
20627
+ const args = rawArguments.length === 0 ? [] : rawArguments.split(/\s+/u);
20422
20628
  const name = rawName.toLowerCase();
20423
20629
  if (!MVP_COMMANDS.includes(name) && dynamicCommands.includes(name)) {
20424
20630
  return { name: "plugin", command: name, args };
20425
20631
  }
20632
+ if (!MVP_COMMANDS.includes(name)) {
20633
+ const managedTool = managedTools.find((tool) => tool.toLowerCase() === name);
20634
+ if (managedTool !== void 0) return { name: "managed-tool", tool: managedTool, input: rawArguments };
20635
+ }
20426
20636
  if (!MVP_COMMANDS.includes(name) && skillCommands.includes(name)) {
20427
20637
  return { name: "skill", skill: name, prompt: args.join(" ") };
20428
20638
  }
@@ -20444,6 +20654,15 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
20444
20654
  }
20445
20655
  return { name, mode: normalized };
20446
20656
  }
20657
+ if (name === "tool") {
20658
+ const [requested] = args;
20659
+ const managedTool = requested === void 0 ? void 0 : managedTools.find((candidate) => candidate.toLowerCase() === requested.toLowerCase());
20660
+ if (managedTool === void 0) {
20661
+ return { name: "invalid", command: name, message: "Use /tool <registered-tool> [JSON object]." };
20662
+ }
20663
+ const inputStart = rawArguments.search(/\s/u);
20664
+ return { name: "managed-tool", tool: managedTool, input: inputStart < 0 ? "" : rawArguments.slice(inputStart).trimStart() };
20665
+ }
20447
20666
  if (name === "audit") {
20448
20667
  const toolFilter = args.length > 0 ? args.join(" ") : void 0;
20449
20668
  return { name, toolFilter };
@@ -20671,6 +20890,7 @@ var HELP = [
20671
20890
  "/review [focus] review uncommitted changes before committing",
20672
20891
  "/evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>> self-improvement loop",
20673
20892
  "/mcp [status|tools|reconnect|enable|disable] manage MCP servers",
20893
+ "/tool <registered-tool> [JSON object] run a registered tool (or use /<tool-name>)",
20674
20894
  "/ide show VS Code connection and cursor/selection",
20675
20895
  "/pals [--verbose|rename <alias>|info <alias-or-uuid>]",
20676
20896
  "/chat <alias-or-uuid> <goal> send a task that starts on the receiving pal",
@@ -20942,7 +21162,12 @@ var FlavorSession = class {
20942
21162
  } catch {
20943
21163
  }
20944
21164
  }
20945
- const command = parseSlashCommand(prompt, this.#services.pluginCommands().map(({ name }) => name), skillNames);
21165
+ const command = parseSlashCommand(
21166
+ prompt,
21167
+ this.#services.pluginCommands().map(({ name }) => name),
21168
+ skillNames,
21169
+ this.#services.managedToolCommands().map(({ name }) => name)
21170
+ );
20946
21171
  if (command !== null) await this.#dispatch(command, controller2.signal);
20947
21172
  else for await (const event of this.#services.run(prompt, controller2.signal, {
20948
21173
  getSteeringMessages: () => this.#drainMessages("steer", true).map((item) => item.text),
@@ -21044,6 +21269,8 @@ var FlavorSession = class {
21044
21269
  this.#notice(`Main permissions set to ${command.mode}. Child approvals use bubble mode unless plan mode is active.`);
21045
21270
  } else if (command.name === "plugin") {
21046
21271
  this.#notice(format(await this.#services.runPluginCommand(command.command, command.args, signal)));
21272
+ } else if (command.name === "managed-tool") {
21273
+ this.#notice(format(await this.#services.runManagedTool(command.tool, command.input, signal)));
21047
21274
  } else if (command.name === "skill") {
21048
21275
  for await (const event of this.#services.runSkill(command.skill, command.prompt, signal)) this.#services.output(event);
21049
21276
  } else if (command.name === "loop") {
@@ -24644,6 +24871,7 @@ async function createProductionRuntime(options) {
24644
24871
  managedTools.push(tool);
24645
24872
  }
24646
24873
  if (harnessCreated) harness.replaceMainTools(tools);
24874
+ options.onToolsChange?.();
24647
24875
  };
24648
24876
  tools.push(...createManagedToolManagementTools({
24649
24877
  store: managedToolStore,
@@ -25973,6 +26201,25 @@ ${renderMemoryDocument(entries)}`;
25973
26201
  signal.throwIfAborted();
25974
26202
  return awaitWithSignal3(Promise.resolve(handler(args, { workspace, signal })), signal);
25975
26203
  },
26204
+ managedToolCommands: () => managedTools.map(({ name, description }) => ({ name, description })).sort((left, right) => left.name.localeCompare(right.name)),
26205
+ runManagedTool: async (name, rawInput, signal) => {
26206
+ const tool = managedTools.find((candidate) => sameToolName(candidate.name, name));
26207
+ if (tool === void 0) throw new Error(`Registered tool "${name}" is no longer available.`);
26208
+ signal.throwIfAborted();
26209
+ const input = normalizeToolCallInput(rawInput);
26210
+ const call = { name: tool.name, input };
26211
+ const validation = harness.main.runtime.normalize(call);
26212
+ if (!validation.ok) throw new Error(`Invalid input for registered tool "${tool.name}": ${validation.error.message}`);
26213
+ harness.main.runtime.beginTurn();
26214
+ const result = await harness.main.runtime.execute(
26215
+ { name: tool.name, input: validation.input },
26216
+ { agent: "main", ownerId: "main", signal }
26217
+ );
26218
+ if (!result.ok) {
26219
+ throw new Error(`Registered tool "${tool.name}" failed (${result.error?.code ?? "unknown"}): ${result.error?.message ?? "Unknown error"}`);
26220
+ }
26221
+ return result.output !== void 0 ? result.output : result.content;
26222
+ },
25976
26223
  output: emitOutput,
25977
26224
  questions,
25978
26225
  async login() {
@@ -26023,8 +26270,12 @@ ${renderMemoryDocument(entries)}`;
26023
26270
  };
26024
26271
  registry.register(next.providerId, next.apiType === "anthropic" ? new AnthropicModelAdapter({
26025
26272
  ...adapterOptions,
26273
+ ...providerConfig?.thinkingBudget === void 0 ? {} : { thinkingBudget: providerConfig.thinkingBudget },
26026
26274
  ...providerConfig?.claudeClient === true ? { headers: CLAUDE_CLIENT_HEADERS } : {}
26027
- }) : new OpenAIModelAdapter(adapterOptions));
26275
+ }) : new OpenAIModelAdapter({
26276
+ ...adapterOptions,
26277
+ ...providerConfig?.thinkingEffort === void 0 ? {} : { thinkingEffort: providerConfig.thinkingEffort }
26278
+ }));
26028
26279
  effectiveLlm = next;
26029
26280
  mainModel = `${next.providerId}:${next.defaultModel}`;
26030
26281
  childModel = `${next.providerId}:${next.cheapModel}`;
@@ -26610,8 +26861,12 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
26610
26861
  };
26611
26862
  const adapter = apiProtocol === "anthropic" ? new AnthropicModelAdapter({
26612
26863
  ...adapterOptions,
26864
+ ...runtimeProvider.thinkingBudget === void 0 ? {} : { thinkingBudget: runtimeProvider.thinkingBudget },
26613
26865
  ...runtimeProvider.claudeClient === true ? { headers: CLAUDE_CLIENT_HEADERS } : {}
26614
- }) : new OpenAIModelAdapter(adapterOptions);
26866
+ }) : new OpenAIModelAdapter({
26867
+ ...adapterOptions,
26868
+ ...runtimeProvider.thinkingEffort === void 0 ? {} : { thinkingEffort: runtimeProvider.thinkingEffort }
26869
+ });
26615
26870
  const cacheProfile = resolveCacheProfile({ apiType: apiProtocol, baseURL: runtimeProvider.baseURL });
26616
26871
  if (apiProtocol === "openai" && isDashScopeBaseURL(runtimeProvider.baseURL)) {
26617
26872
  diagnostics.push(
@@ -29286,7 +29541,8 @@ var DesktopRuntimeController = class {
29286
29541
  ...runtime.approvals.pending === void 0 ? {} : { approval: runtime.approvals.pending },
29287
29542
  ...runtime.services.questions.pending === void 0 ? {} : { questions: runtime.services.questions.pending },
29288
29543
  ...runtime.memoryReviews.pending.length === 0 ? {} : { memoryReviews: runtime.memoryReviews.pending },
29289
- ...(runtime.memoryReviews.autoDismissSeconds ?? 0) > 0 ? { memoryAutoDismissSeconds: runtime.memoryReviews.autoDismissSeconds } : {}
29544
+ ...(runtime.memoryReviews.autoDismissSeconds ?? 0) > 0 ? { memoryAutoDismissSeconds: runtime.memoryReviews.autoDismissSeconds } : {},
29545
+ ...(runtime.services.managedToolCommands?.().length ?? 0) === 0 ? {} : { managedTools: runtime.services.managedToolCommands() }
29290
29546
  },
29291
29547
  diagnostics: runtime?.diagnostics ?? [],
29292
29548
  models: this.#models,
@@ -29340,6 +29596,9 @@ var DesktopRuntimeController = class {
29340
29596
  },
29341
29597
  onApprovalChange: () => {
29342
29598
  if (this.#runtime !== void 0) this.#publishSnapshot();
29599
+ },
29600
+ onToolsChange: () => {
29601
+ if (this.#runtime !== void 0) this.#publishSnapshot();
29343
29602
  }
29344
29603
  });
29345
29604
  outputSessionId = runtime.sessionId;