flavor-code 1.3.9 → 1.3.11

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.
@@ -16,7 +16,7 @@ import {
16
16
  redactErrorText,
17
17
  transcriptReducer,
18
18
  withTimeout
19
- } from "./chunk-M3U2JICC.js";
19
+ } from "./chunk-IGFN2DM5.js";
20
20
  import "./chunk-IG4Z23CL.js";
21
21
  import {
22
22
  Box_default,
@@ -1257,7 +1257,7 @@ function slashCandidatePresentation(selected) {
1257
1257
  matchStyle: { color: "ansi:cyan", bold: true }
1258
1258
  };
1259
1259
  }
1260
- function buildSlashCandidates(commands, plugins, skills) {
1260
+ function buildSlashCandidates(commands, plugins, skills, tools = []) {
1261
1261
  const candidates = /* @__PURE__ */ new Map();
1262
1262
  for (const command of commands) {
1263
1263
  candidates.set(command.name, { name: command.name, kind: "command", description: command.description });
@@ -1267,6 +1267,11 @@ function buildSlashCandidates(commands, plugins, skills) {
1267
1267
  candidates.set(plugin.name, plugin.description === void 0 ? { name: plugin.name, kind: "plugin" } : { name: plugin.name, kind: "plugin", description: plugin.description });
1268
1268
  }
1269
1269
  }
1270
+ for (const tool of tools) {
1271
+ if (!candidates.has(tool.name)) {
1272
+ candidates.set(tool.name, tool.description === void 0 ? { name: tool.name, kind: "tool" } : { name: tool.name, kind: "tool", description: tool.description });
1273
+ }
1274
+ }
1270
1275
  for (const skill of skills) {
1271
1276
  if (!candidates.has(skill.name)) {
1272
1277
  candidates.set(skill.name, {
@@ -1850,7 +1855,8 @@ function appRuntimeOptions(props, output, onApprovalChange) {
1850
1855
  ...props.palAlias === void 0 ? {} : { alias: props.palAlias }
1851
1856
  },
1852
1857
  output,
1853
- onApprovalChange
1858
+ onApprovalChange,
1859
+ onToolsChange: onApprovalChange
1854
1860
  };
1855
1861
  }
1856
1862
  function App({ workspace, home, resumeSession, instanceId, palAlias }) {
@@ -1954,13 +1960,23 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1954
1960
  }
1955
1961
  dispatch({ type: "restore", state: created.restoredTranscript });
1956
1962
  runtimeRef.current = created;
1957
- setSlashCandidates(buildSlashCandidates(BUILTIN_SLASH_CANDIDATES, created.services.pluginCommands(), []));
1963
+ setSlashCandidates(buildSlashCandidates(
1964
+ BUILTIN_SLASH_CANDIDATES,
1965
+ created.services.pluginCommands(),
1966
+ [],
1967
+ created.services.managedToolCommands()
1968
+ ));
1958
1969
  setRuntime(created);
1959
1970
  await created.session.start();
1960
1971
  try {
1961
1972
  const skills = await created.services.skills();
1962
1973
  if (!disposed) {
1963
- setSlashCandidates(buildSlashCandidates(BUILTIN_SLASH_CANDIDATES, created.services.pluginCommands(), skills));
1974
+ setSlashCandidates(buildSlashCandidates(
1975
+ BUILTIN_SLASH_CANDIDATES,
1976
+ created.services.pluginCommands(),
1977
+ skills,
1978
+ created.services.managedToolCommands()
1979
+ ));
1964
1980
  }
1965
1981
  } catch {
1966
1982
  }
@@ -1975,6 +1991,28 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1975
1991
  `));
1976
1992
  };
1977
1993
  }, [workspace, home, resumeSession, instanceId, palAlias]);
1994
+ useEffect3(() => {
1995
+ if (runtime === void 0) return;
1996
+ let disposed = false;
1997
+ void runtime.services.skills().then((skills) => {
1998
+ if (!disposed) setSlashCandidates(buildSlashCandidates(
1999
+ BUILTIN_SLASH_CANDIDATES,
2000
+ runtime.services.pluginCommands(),
2001
+ skills,
2002
+ runtime.services.managedToolCommands()
2003
+ ));
2004
+ }).catch(() => {
2005
+ if (!disposed) setSlashCandidates(buildSlashCandidates(
2006
+ BUILTIN_SLASH_CANDIDATES,
2007
+ runtime.services.pluginCommands(),
2008
+ [],
2009
+ runtime.services.managedToolCommands()
2010
+ ));
2011
+ });
2012
+ return () => {
2013
+ disposed = true;
2014
+ };
2015
+ }, [runtime, revision]);
1978
2016
  useEffect3(() => installSigintHandler(process, interrupt), [interrupt]);
1979
2017
  useEffect3(() => {
1980
2018
  if (runtime?.services.ideContext === void 0) {
@@ -2347,7 +2385,6 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2347
2385
  setDismissedMentionInput(void 0);
2348
2386
  }
2349
2387
  });
2350
- void revision;
2351
2388
  if (runtime === void 0) return /* @__PURE__ */ jsx5(
2352
2389
  StartingLayout,
2353
2390
  {
@@ -56,6 +56,8 @@ function normalizeProviderError(error) {
56
56
  code = "context_overflow";
57
57
  } else if (status === 404 || /model.*not.?found|not.?found.*model/.test(searchable)) {
58
58
  code = "model_not_found";
59
+ } else if (status !== void 0 && status >= 500 && status <= 599) {
60
+ code = "network";
59
61
  } else if (status === void 0 && /econn|enet|socket|network|fetch failed|connection|timeout/.test(searchable)) {
60
62
  code = "network";
61
63
  }
@@ -1981,10 +1983,17 @@ var FILE_TYPE_EXTENSIONS = {
1981
1983
  python: [".py", ".pyi"],
1982
1984
  typescript: [".ts", ".tsx", ".mts", ".cts"]
1983
1985
  };
1986
+ var BooleanInput = z.preprocess((value) => {
1987
+ if (typeof value !== "string") return value;
1988
+ const normalized = value.trim().toLowerCase();
1989
+ if (normalized === "true") return true;
1990
+ if (normalized === "false") return false;
1991
+ return value;
1992
+ }, z.boolean());
1984
1993
  var GlobInput = z.object({
1985
1994
  pattern: z.string().min(1),
1986
1995
  path: z.string().min(1).describe('Workspace-relative search directory. Use JSON null, not the string "null", for the workspace root.').nullable().optional(),
1987
- includeIgnored: z.boolean().describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
1996
+ includeIgnored: BooleanInput.describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
1988
1997
  limit: z.coerce.number().int().positive().max(1e5).nullable().optional()
1989
1998
  });
1990
1999
  var GrepInput = z.object({
@@ -1992,7 +2001,7 @@ var GrepInput = z.object({
1992
2001
  path: z.string().min(1).describe('Workspace-relative search path. Use JSON null, not the string "null", for the workspace root.').nullable().optional(),
1993
2002
  glob: z.string().min(1).nullable().optional(),
1994
2003
  fileType: z.enum(FILE_TYPES).nullable().optional(),
1995
- includeIgnored: z.boolean().describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
2004
+ includeIgnored: BooleanInput.describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
1996
2005
  context: z.coerce.number().int().nonnegative().max(100).nullable().optional(),
1997
2006
  limit: z.coerce.number().int().positive().max(1e5).nullable().optional()
1998
2007
  });
@@ -2987,7 +2996,8 @@ var MVP_COMMANDS = [
2987
2996
  "fork",
2988
2997
  "pals",
2989
2998
  "chat",
2990
- "co-work"
2999
+ "co-work",
3000
+ "tool"
2991
3001
  ];
2992
3002
  var COMMAND_DESCRIPTIONS = {
2993
3003
  model: "Switch the active model",
@@ -3025,16 +3035,24 @@ var COMMAND_DESCRIPTIONS = {
3025
3035
  fork: "Continue context from a prior session node",
3026
3036
  pals: "List, rename, or inspect active Flavor pals",
3027
3037
  chat: "Send a task to another Flavor pal",
3028
- "co-work": "Coordinate a shared goal with another Flavor pal"
3038
+ "co-work": "Coordinate a shared goal with another Flavor pal",
3039
+ tool: "Run a registered managed tool with a JSON object"
3029
3040
  };
3030
- function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
3041
+ function parseSlashCommand(input, dynamicCommands = [], skillCommands = [], managedTools = []) {
3031
3042
  const trimmed = input.trim();
3032
3043
  if (!trimmed.startsWith("/")) return null;
3033
- const [rawName = "", ...args] = trimmed.slice(1).split(/\s+/);
3044
+ const match = /^(\S+)(?:\s+([\s\S]*))?$/u.exec(trimmed.slice(1));
3045
+ const rawName = match?.[1] ?? "";
3046
+ const rawArguments = match?.[2] ?? "";
3047
+ const args = rawArguments.length === 0 ? [] : rawArguments.split(/\s+/u);
3034
3048
  const name = rawName.toLowerCase();
3035
3049
  if (!MVP_COMMANDS.includes(name) && dynamicCommands.includes(name)) {
3036
3050
  return { name: "plugin", command: name, args };
3037
3051
  }
3052
+ if (!MVP_COMMANDS.includes(name)) {
3053
+ const managedTool = managedTools.find((tool) => tool.toLowerCase() === name);
3054
+ if (managedTool !== void 0) return { name: "managed-tool", tool: managedTool, input: rawArguments };
3055
+ }
3038
3056
  if (!MVP_COMMANDS.includes(name) && skillCommands.includes(name)) {
3039
3057
  return { name: "skill", skill: name, prompt: args.join(" ") };
3040
3058
  }
@@ -3056,6 +3074,15 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
3056
3074
  }
3057
3075
  return { name, mode: normalized };
3058
3076
  }
3077
+ if (name === "tool") {
3078
+ const [requested] = args;
3079
+ const managedTool = requested === void 0 ? void 0 : managedTools.find((candidate) => candidate.toLowerCase() === requested.toLowerCase());
3080
+ if (managedTool === void 0) {
3081
+ return { name: "invalid", command: name, message: "Use /tool <registered-tool> [JSON object]." };
3082
+ }
3083
+ const inputStart = rawArguments.search(/\s/u);
3084
+ return { name: "managed-tool", tool: managedTool, input: inputStart < 0 ? "" : rawArguments.slice(inputStart).trimStart() };
3085
+ }
3059
3086
  if (name === "audit") {
3060
3087
  const toolFilter = args.length > 0 ? args.join(" ") : void 0;
3061
3088
  return { name, toolFilter };
@@ -3283,6 +3310,7 @@ var HELP = [
3283
3310
  "/review [focus] review uncommitted changes before committing",
3284
3311
  "/evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>> self-improvement loop",
3285
3312
  "/mcp [status|tools|reconnect|enable|disable] manage MCP servers",
3313
+ "/tool <registered-tool> [JSON object] run a registered tool (or use /<tool-name>)",
3286
3314
  "/ide show VS Code connection and cursor/selection",
3287
3315
  "/pals [--verbose|rename <alias>|info <alias-or-uuid>]",
3288
3316
  "/chat <alias-or-uuid> <goal> send a task that starts on the receiving pal",
@@ -3554,7 +3582,12 @@ var FlavorSession = class {
3554
3582
  } catch {
3555
3583
  }
3556
3584
  }
3557
- const command = parseSlashCommand(prompt, this.#services.pluginCommands().map(({ name }) => name), skillNames);
3585
+ const command = parseSlashCommand(
3586
+ prompt,
3587
+ this.#services.pluginCommands().map(({ name }) => name),
3588
+ skillNames,
3589
+ this.#services.managedToolCommands().map(({ name }) => name)
3590
+ );
3558
3591
  if (command !== null) await this.#dispatch(command, controller.signal);
3559
3592
  else for await (const event of this.#services.run(prompt, controller.signal, {
3560
3593
  getSteeringMessages: () => this.#drainMessages("steer", true).map((item) => item.text),
@@ -3656,6 +3689,8 @@ var FlavorSession = class {
3656
3689
  this.#notice(`Main permissions set to ${command.mode}. Child approvals use bubble mode unless plan mode is active.`);
3657
3690
  } else if (command.name === "plugin") {
3658
3691
  this.#notice(format(await this.#services.runPluginCommand(command.command, command.args, signal)));
3692
+ } else if (command.name === "managed-tool") {
3693
+ this.#notice(format(await this.#services.runManagedTool(command.tool, command.input, signal)));
3659
3694
  } else if (command.name === "skill") {
3660
3695
  for await (const event of this.#services.runSkill(command.skill, command.prompt, signal)) this.#services.output(event);
3661
3696
  } else if (command.name === "loop") {
@@ -6527,9 +6562,10 @@ function withStructuredOutput(options) {
6527
6562
  throw new Error("Structured output retry delays must be non-negative integers");
6528
6563
  }
6529
6564
  }
6530
- const tool = modelToolFromZod(options.name, options.description, options.schema);
6565
+ const generatedTool = options.modelInputSchema === void 0 ? modelToolFromZod(options.name, options.description, options.schema) : { name: options.name, description: options.description, inputSchema: options.modelInputSchema };
6566
+ const tool = options.modelStrict === void 0 ? generatedTool : { ...generatedTool, strict: options.modelStrict };
6531
6567
  const maxAttempts = retry.maxRetries + 1;
6532
- const jsonSchema = tool.inputSchema;
6568
+ const validationSchema = jsonSchemaFromZod(options.schema);
6533
6569
  const model = {
6534
6570
  async *stream(request) {
6535
6571
  let candidate;
@@ -6602,7 +6638,7 @@ function withStructuredOutput(options) {
6602
6638
  }
6603
6639
  }
6604
6640
  if (candidateCount === 1 && candidate !== null && typeof candidate === "object") {
6605
- candidate = coerceByJsonSchema(candidate, jsonSchema);
6641
+ candidate = coerceByJsonSchema(candidate, validationSchema);
6606
6642
  }
6607
6643
  if (candidateCount !== 1) {
6608
6644
  attemptError = {
@@ -6674,7 +6710,13 @@ function withStructuredOutput(options) {
6674
6710
  return model;
6675
6711
  }
6676
6712
  function strictJsonSchema(schema) {
6677
- return ensureStrictSchema(z7.toJSONSchema(schema));
6713
+ return strictJsonSchemaObject(jsonSchemaFromZod(schema));
6714
+ }
6715
+ function jsonSchemaFromZod(schema) {
6716
+ return z7.toJSONSchema(schema);
6717
+ }
6718
+ function strictJsonSchemaObject(schema) {
6719
+ return ensureStrictSchema(schema);
6678
6720
  }
6679
6721
  function extractJsonObject(text) {
6680
6722
  const trimmed = text.trim();
@@ -6722,6 +6764,7 @@ function coerceByJsonSchema(value, schema) {
6722
6764
  const node = schema;
6723
6765
  const branches = [
6724
6766
  ...Array.isArray(node.anyOf) ? node.anyOf : [],
6767
+ ...Array.isArray(node.oneOf) ? node.oneOf : [],
6725
6768
  ...Array.isArray(node.allOf) ? node.allOf : []
6726
6769
  ].filter((branch) => branch !== null && typeof branch === "object");
6727
6770
  const types = [];
@@ -6741,40 +6784,108 @@ function coerceByJsonSchema(value, schema) {
6741
6784
  if (lowered === "true") return true;
6742
6785
  if (lowered === "false") return false;
6743
6786
  }
6787
+ if (types.includes("object") && !types.includes("string") && typeof value === "string") {
6788
+ const trimmed = value.trim();
6789
+ if (trimmed.startsWith("{")) {
6790
+ try {
6791
+ const parsed = JSON.parse(trimmed);
6792
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
6793
+ return coerceByJsonSchema(parsed, node);
6794
+ }
6795
+ } catch {
6796
+ }
6797
+ }
6798
+ }
6799
+ if (types.includes("array") && !Array.isArray(value) && value !== null && value !== void 0 && !types.some((type) => matchesJsonSchemaType(value, type))) {
6800
+ const arraySchema = [node, ...branches].find((candidate) => {
6801
+ const type = candidate.type;
6802
+ return type === "array" || Array.isArray(type) && type.includes("array");
6803
+ });
6804
+ if (typeof value === "string" && value.trim().startsWith("[")) {
6805
+ try {
6806
+ const parsed = JSON.parse(value.trim());
6807
+ if (Array.isArray(parsed)) return coerceByJsonSchema(parsed, arraySchema ?? node);
6808
+ } catch {
6809
+ }
6810
+ }
6811
+ return [arraySchema?.items === void 0 ? value : coerceByJsonSchema(value, arraySchema.items)];
6812
+ }
6744
6813
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
6745
- const properties = node.properties ?? branches.find((branch) => branch.properties)?.properties;
6814
+ const objectSchema = [node, ...branches].find((candidate) => candidate.type === "object" || candidate.properties !== void 0);
6815
+ const properties = objectSchema?.properties;
6746
6816
  if (properties !== void 0) {
6747
6817
  const output = {};
6818
+ const required2 = new Set(Array.isArray(objectSchema?.required) ? objectSchema.required : []);
6748
6819
  for (const [key, child] of Object.entries(value)) {
6749
- output[key] = properties[key] !== void 0 ? coerceByJsonSchema(child, properties[key]) : child;
6820
+ const childSchema = properties[key];
6821
+ if (child === null && childSchema !== void 0 && !required2.has(key) && !jsonSchemaAllowsNull(childSchema)) {
6822
+ continue;
6823
+ }
6824
+ output[key] = childSchema !== void 0 ? coerceByJsonSchema(child, childSchema) : child;
6750
6825
  }
6751
6826
  return output;
6752
6827
  }
6753
6828
  }
6754
- if (types.includes("array") && Array.isArray(value) && node.items !== void 0) {
6755
- return value.map((item) => coerceByJsonSchema(item, node.items));
6829
+ if (types.includes("array") && Array.isArray(value)) {
6830
+ const arraySchema = [node, ...branches].find((candidate) => {
6831
+ const type = candidate.type;
6832
+ return type === "array" || Array.isArray(type) && type.includes("array");
6833
+ });
6834
+ if (arraySchema?.items !== void 0) {
6835
+ return value.map((item) => coerceByJsonSchema(item, arraySchema.items));
6836
+ }
6756
6837
  }
6757
6838
  return value;
6758
6839
  }
6840
+ function matchesJsonSchemaType(value, type) {
6841
+ if (type === "null") return value === null;
6842
+ if (type === "array") return Array.isArray(value);
6843
+ if (type === "object") return value !== null && typeof value === "object" && !Array.isArray(value);
6844
+ if (type === "integer") return typeof value === "number" && Number.isInteger(value);
6845
+ if (type === "number") return typeof value === "number";
6846
+ return typeof value === type;
6847
+ }
6848
+ function jsonSchemaAllowsNull(schema) {
6849
+ if (schema === null || typeof schema !== "object") return false;
6850
+ const node = schema;
6851
+ if (node.type === "null" || Array.isArray(node.type) && node.type.includes("null")) return true;
6852
+ return [node.anyOf, node.oneOf].some((branches) => Array.isArray(branches) && branches.some((branch) => jsonSchemaAllowsNull(branch)));
6853
+ }
6759
6854
  function modelToolFromZod(name, description, schema) {
6760
6855
  return { name, description, inputSchema: strictJsonSchema(schema) };
6761
6856
  }
6762
6857
  function ensureStrictSchema(schema) {
6858
+ const output = { ...schema };
6859
+ for (const keyword of ["anyOf", "oneOf", "allOf", "prefixItems"]) {
6860
+ const branches = schema[keyword];
6861
+ if (Array.isArray(branches)) {
6862
+ output[keyword] = branches.map((branch) => typeof branch === "object" && branch !== null ? ensureStrictSchema(branch) : branch);
6863
+ }
6864
+ }
6865
+ if (typeof schema.items === "object" && schema.items !== null) {
6866
+ output.items = ensureStrictSchema(schema.items);
6867
+ }
6868
+ for (const keyword of ["$defs", "definitions"]) {
6869
+ const definitions = schema[keyword];
6870
+ if (typeof definitions === "object" && definitions !== null && !Array.isArray(definitions)) {
6871
+ output[keyword] = Object.fromEntries(Object.entries(definitions).map(([key, definition]) => [
6872
+ key,
6873
+ typeof definition === "object" && definition !== null ? ensureStrictSchema(definition) : definition
6874
+ ]));
6875
+ }
6876
+ }
6763
6877
  if (schema.type !== "object" || typeof schema.properties !== "object" || schema.properties === null) {
6764
- return schema;
6878
+ return output;
6765
6879
  }
6766
- const required2 = Array.isArray(schema.required) ? [...schema.required] : [];
6767
- const requiredSet = new Set(required2);
6880
+ const requiredSet = new Set(Array.isArray(schema.required) ? schema.required : []);
6768
6881
  const properties = {};
6769
6882
  for (const [key, value] of Object.entries(schema.properties)) {
6770
6883
  const child = typeof value === "object" && value !== null ? ensureStrictSchema(value) : value;
6771
- if (requiredSet.has(key)) properties[key] = child;
6772
- else {
6773
- properties[key] = { anyOf: [child, { type: "null" }] };
6774
- requiredSet.add(key);
6775
- }
6884
+ if (requiredSet.has(key) || jsonSchemaAllowsNull(child)) properties[key] = child;
6885
+ else properties[key] = { anyOf: [child, { type: "null" }] };
6886
+ requiredSet.add(key);
6776
6887
  }
6777
- return { ...schema, additionalProperties: false, required: [...requiredSet], properties };
6888
+ return { ...output, additionalProperties: false, required: [...requiredSet], properties };
6778
6889
  }
6779
6890
  function repairMessages(messages, tool, original, candidate, error) {
6780
6891
  const details = [
@@ -7144,19 +7255,11 @@ var AgentLoop = class {
7144
7255
  });
7145
7256
  continue;
7146
7257
  }
7147
- const validation = collected.kind === "valid" ? this.#options.runtime.validate(collected) : { ok: false, error: collected.error };
7258
+ const validation = collected.kind === "valid" ? this.#options.runtime.normalize(collected) : { ok: false, error: collected.error };
7148
7259
  if (validation.ok) {
7149
7260
  toolCalls.push({ id: collected.id, name: collected.name, input: validation.input });
7150
7261
  continue;
7151
7262
  }
7152
- if (collected.kind === "valid") {
7153
- const coerced = coerceByJsonSchema(collected.input, strictJsonSchema(definition.inputSchema));
7154
- const revalidation = this.#options.runtime.validate({ ...collected, input: coerced });
7155
- if (revalidation.ok) {
7156
- toolCalls.push({ id: collected.id, name: collected.name, input: revalidation.input });
7157
- continue;
7158
- }
7159
- }
7160
7263
  const repairModelId = this.#options.fallbackModelId ?? this.#options.modelId;
7161
7264
  const invalidOutput = collected.kind === "invalid" ? collected.rawInput : serializeToolInput(collected.input);
7162
7265
  const structured = withStructuredOutput({
@@ -7165,6 +7268,8 @@ var AgentLoop = class {
7165
7268
  name: definition.name,
7166
7269
  description: definition.description,
7167
7270
  schema: definition.inputSchema,
7271
+ ...definition.modelInputSchema === void 0 ? {} : { modelInputSchema: definition.modelInputSchema },
7272
+ ...definition.modelStrict === void 0 ? {} : { modelStrict: definition.modelStrict },
7168
7273
  beforeAttempt: async ({ attempt: repairAttempt, maxAttempts: repairMaxAttempts, messageCount }) => {
7169
7274
  const before = await this.#options.hooks.emit({
7170
7275
  version: 1,
@@ -7645,12 +7750,24 @@ var ToolRuntime = class {
7645
7750
  throw error;
7646
7751
  }
7647
7752
  }
7753
+ /**
7754
+ * Validates a call and applies the same conservative provider-argument
7755
+ * normalization used by the agent loop before falling back to repair.
7756
+ */
7757
+ normalize(call) {
7758
+ const validation = this.validate(call);
7759
+ if (validation.ok || validation.error.code === "unknown_tool") return validation;
7760
+ const tool = this.#tools.get(call.name);
7761
+ if (tool === void 0) return validation;
7762
+ const input = coerceByJsonSchema(call.input, jsonSchemaFromZod(tool.inputSchema));
7763
+ return this.validate({ ...call, input });
7764
+ }
7648
7765
  async #execute(call, context) {
7649
7766
  const tool = this.#tools.get(call.name);
7650
7767
  if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
7651
7768
  const signal = context.signal ?? new AbortController().signal;
7652
7769
  const toolCallId = call.id ?? randomUUID7();
7653
- const validation = this.validate(call);
7770
+ const validation = this.normalize(call);
7654
7771
  if (!validation.ok) {
7655
7772
  return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
7656
7773
  }
@@ -9379,6 +9496,7 @@ var PluginHost = class {
9379
9496
  description: contribution.description,
9380
9497
  inputSchema: z13.unknown(),
9381
9498
  ...contribution.modelInputSchema === void 0 ? {} : { modelInputSchema: contribution.modelInputSchema },
9499
+ ...contribution.modelInputSchema === void 0 ? {} : { modelStrict: false },
9382
9500
  ...contribution.readOnly === void 0 ? {} : { readOnly: contribution.readOnly },
9383
9501
  paths: () => [],
9384
9502
  execute: (input, _signal, toolContext) => session.invoke("tool", contribution.name, [
@@ -17786,7 +17904,7 @@ function compileDefinition(record, scope2, workspace) {
17786
17904
  name: record.name,
17787
17905
  description: record.description,
17788
17906
  inputSchema,
17789
- modelInputSchema: record.inputSchema,
17907
+ modelInputSchema: strictJsonSchemaObject(record.inputSchema),
17790
17908
  modelStrict: true,
17791
17909
  ...record.agents === void 0 ? {} : { agents: record.agents },
17792
17910
  paths: () => [],
@@ -20850,6 +20968,7 @@ async function createProductionRuntime(options) {
20850
20968
  managedTools.push(tool);
20851
20969
  }
20852
20970
  if (harnessCreated) harness.replaceMainTools(tools);
20971
+ options.onToolsChange?.();
20853
20972
  };
20854
20973
  tools.push(...createManagedToolManagementTools({
20855
20974
  store: managedToolStore,
@@ -22179,6 +22298,25 @@ ${renderMemoryDocument(entries)}`;
22179
22298
  signal.throwIfAborted();
22180
22299
  return awaitWithSignal(Promise.resolve(handler(args, { workspace, signal })), signal);
22181
22300
  },
22301
+ managedToolCommands: () => managedTools.map(({ name, description }) => ({ name, description })).sort((left, right) => left.name.localeCompare(right.name)),
22302
+ runManagedTool: async (name, rawInput, signal) => {
22303
+ const tool = managedTools.find((candidate) => sameToolName(candidate.name, name));
22304
+ if (tool === void 0) throw new Error(`Registered tool "${name}" is no longer available.`);
22305
+ signal.throwIfAborted();
22306
+ const input = normalizeToolCallInput(rawInput);
22307
+ const call = { name: tool.name, input };
22308
+ const validation = harness.main.runtime.normalize(call);
22309
+ if (!validation.ok) throw new Error(`Invalid input for registered tool "${tool.name}": ${validation.error.message}`);
22310
+ harness.main.runtime.beginTurn();
22311
+ const result = await harness.main.runtime.execute(
22312
+ { name: tool.name, input: validation.input },
22313
+ { agent: "main", ownerId: "main", signal }
22314
+ );
22315
+ if (!result.ok) {
22316
+ throw new Error(`Registered tool "${tool.name}" failed (${result.error?.code ?? "unknown"}): ${result.error?.message ?? "Unknown error"}`);
22317
+ }
22318
+ return result.output !== void 0 ? result.output : result.content;
22319
+ },
22182
22320
  output: emitOutput,
22183
22321
  questions,
22184
22322
  async login() {
@@ -22573,7 +22711,7 @@ ${expandSkillArguments(await skills.loadBody(skill), prompt)}`);
22573
22711
  ...additionalContext === void 0 ? {} : { additionalContext },
22574
22712
  ...getSteeringMessages === void 0 ? {} : { getSteeringMessages }
22575
22713
  })) {
22576
- if (event.type === "error" && /adapter|provider|api.?key|model/i.test(event.error.message)) {
22714
+ if (event.type === "error" && event.error.code !== "network" && event.error.code !== "rate_limit" && /adapter|provider|api.?key|model/i.test(event.error.message)) {
22577
22715
  yield { ...event, error: {
22578
22716
  ...event.error,
22579
22717
  message: `${event.error.message}. Configure providers and agents in .flavor/flavor.json or set OPENAI_API_KEY/ANTHROPIC_API_KEY.`
@@ -4,7 +4,7 @@ import {
4
4
  message,
5
5
  redactErrorText,
6
6
  redactSecrets
7
- } from "./chunk-M3U2JICC.js";
7
+ } from "./chunk-IGFN2DM5.js";
8
8
 
9
9
  // src/rpc/server.ts
10
10
  import { createInterface } from "readline";
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module"; const require = createRequire(impor
3
3
  import {
4
4
  FlavorRpcServer,
5
5
  TraceRecorder
6
- } from "./chunk-D4334NAE.js";
6
+ } from "./chunk-JZ5FVBET.js";
7
7
  import {
8
8
  staticTaskLines
9
9
  } from "./chunk-KKIBYDJI.js";
@@ -23,7 +23,7 @@ import {
23
23
  packageVersion,
24
24
  redactErrorText,
25
25
  runPalBroker
26
- } from "./chunk-M3U2JICC.js";
26
+ } from "./chunk-IGFN2DM5.js";
27
27
  import {
28
28
  McpServerConfigSchema,
29
29
  McpServerNameSchema,
@@ -877,7 +877,7 @@ async function runInteractiveCli(props) {
877
877
  const [{ render, AlternateScreen }, { createElement }, { App }] = await Promise.all([
878
878
  import("./claude-ink-ORUA7GWG.js"),
879
879
  import("react"),
880
- import("./app-XAV5LRM7.js")
880
+ import("./app-L52CTVNR.js")
881
881
  ]);
882
882
  const instance = await render(createElement(
883
883
  AlternateScreen,