@bike4mind/cli 0.2.2 → 0.2.3-feat-questmaster-gpt5-function-calling.17056

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.
File without changes
@@ -7,7 +7,7 @@ import {
7
7
  getSettingsMap,
8
8
  getSettingsValue,
9
9
  secureParameters
10
- } from "./chunk-NZADYDFG.js";
10
+ } from "./chunk-BSDDDQF4.js";
11
11
  import {
12
12
  KnowledgeType,
13
13
  SupportedFabFileMimeTypes
File without changes
File without changes
File without changes
@@ -4112,6 +4112,12 @@ var OpenAIBackend = class {
4112
4112
  }
4113
4113
  if (options.tools?.length) {
4114
4114
  parameters.tools = this.formatTools(options.tools);
4115
+ if (options.tool_choice) {
4116
+ parameters.tool_choice = options.tool_choice;
4117
+ }
4118
+ if (options.parallel_tool_calls !== void 0) {
4119
+ parameters.parallel_tool_calls = options.parallel_tool_calls;
4120
+ }
4115
4121
  }
4116
4122
  const response = await this._api.chat.completions.create(parameters, {
4117
4123
  signal: options.abortSignal
@@ -7978,6 +7984,165 @@ function extractQuestMasterData(reply, options = {}) {
7978
7984
  return questMasterData;
7979
7985
  }
7980
7986
 
7987
+ // ../../b4m-core/packages/utils/dist/src/questMasterToolSchema.js
7988
+ var GPT5_MODELS_WITH_TOOL_SUPPORT = [
7989
+ ChatModels.GPT5,
7990
+ ChatModels.GPT5_MINI,
7991
+ ChatModels.GPT5_NANO,
7992
+ ChatModels.GPT5_1,
7993
+ ChatModels.GPT5_2
7994
+ ];
7995
+ function isGPT5ModelWithToolSupport(model) {
7996
+ return GPT5_MODELS_WITH_TOOL_SUPPORT.includes(model);
7997
+ }
7998
+ var createQuestPlanToolSchema = {
7999
+ name: "create_quest_plan",
8000
+ description: `Break down a user request into a structured quest plan with actionable tasks.
8001
+
8002
+ GUIDELINES:
8003
+ - Analyze the user's request and create 3-7 main quests
8004
+ - Each quest must have 2-5 specific, actionable subquests
8005
+ - Quest titles should be clear and action-oriented (e.g., "Set up database schema" not "Step 1")
8006
+ - Provide detailed descriptions for each quest
8007
+ - Assess complexity accurately: Easy (< 1 hour), Medium (1-4 hours), Hard (> 4 hours)
8008
+ - Generate 2-5 relevant tags categorizing the work (e.g., "web-development", "database", "api")
8009
+
8010
+ ALWAYS call this function to respond. Do NOT respond with plain text.`,
8011
+ parameters: {
8012
+ type: "object",
8013
+ properties: {
8014
+ goal: {
8015
+ type: "string",
8016
+ description: "The overall objective the user wants to achieve"
8017
+ },
8018
+ tags: {
8019
+ type: "array",
8020
+ items: { type: "string" },
8021
+ description: 'Categories for this quest (e.g., "web-development", "api", "database")'
8022
+ },
8023
+ quests: {
8024
+ type: "array",
8025
+ description: "The main quests (3-7) that break down the goal",
8026
+ items: {
8027
+ type: "object",
8028
+ properties: {
8029
+ id: {
8030
+ type: "string",
8031
+ description: 'Unique identifier (e.g., "quest-1", "setup-database")'
8032
+ },
8033
+ title: {
8034
+ type: "string",
8035
+ description: 'Clear, action-oriented title (e.g., "Configure authentication middleware")'
8036
+ },
8037
+ description: {
8038
+ type: "string",
8039
+ description: "Detailed explanation of what needs to be done and why"
8040
+ },
8041
+ complexity: {
8042
+ type: "string",
8043
+ enum: ["Easy", "Medium", "Hard"],
8044
+ description: "Difficulty assessment based on effort required"
8045
+ },
8046
+ subQuests: {
8047
+ type: "array",
8048
+ description: "Concrete subtasks (2-5) within this quest",
8049
+ items: {
8050
+ type: "object",
8051
+ properties: {
8052
+ id: {
8053
+ type: "string",
8054
+ description: "Unique identifier for the subquest"
8055
+ },
8056
+ title: {
8057
+ type: "string",
8058
+ description: "Specific, actionable subtask title"
8059
+ }
8060
+ },
8061
+ required: ["id", "title"]
8062
+ }
8063
+ }
8064
+ },
8065
+ required: ["id", "title", "description", "complexity", "subQuests"]
8066
+ }
8067
+ }
8068
+ },
8069
+ required: ["goal", "quests"]
8070
+ }
8071
+ };
8072
+ function parseQuestPlanFunctionCall(args) {
8073
+ if (!args || typeof args !== "string" || args.trim() === "") {
8074
+ throw new Error("Function call arguments are empty or invalid");
8075
+ }
8076
+ let parsed;
8077
+ try {
8078
+ parsed = JSON.parse(args);
8079
+ } catch (e) {
8080
+ throw new Error(`Failed to parse function call arguments as JSON: ${e}`);
8081
+ }
8082
+ if (!parsed || typeof parsed !== "object") {
8083
+ throw new Error("Function call arguments must be an object");
8084
+ }
8085
+ const result = parsed;
8086
+ if (!result.goal || typeof result.goal !== "string" || result.goal.trim() === "") {
8087
+ throw new Error("Function call missing required field: goal (must be non-empty string)");
8088
+ }
8089
+ if (!Array.isArray(result.quests)) {
8090
+ throw new Error("Function call missing required field: quests (must be an array)");
8091
+ }
8092
+ if (result.quests.length === 0) {
8093
+ throw new Error("Function call returned empty quests array");
8094
+ }
8095
+ for (let i = 0; i < result.quests.length; i++) {
8096
+ const quest = result.quests[i];
8097
+ if (!quest || typeof quest !== "object") {
8098
+ throw new Error(`Quest ${i} is not a valid object`);
8099
+ }
8100
+ const questObj = quest;
8101
+ if (!questObj.id || typeof questObj.id !== "string" || questObj.id.trim() === "") {
8102
+ throw new Error(`Quest ${i} missing required field: id (must be non-empty string)`);
8103
+ }
8104
+ if (!questObj.title || typeof questObj.title !== "string" || questObj.title.trim() === "") {
8105
+ throw new Error(`Quest ${i} missing required field: title (must be non-empty string)`);
8106
+ }
8107
+ if (!questObj.description || typeof questObj.description !== "string" || questObj.description.trim() === "") {
8108
+ throw new Error(`Quest ${i} missing required field: description (must be non-empty string)`);
8109
+ }
8110
+ if (!questObj.complexity || !["Easy", "Medium", "Hard"].includes(questObj.complexity)) {
8111
+ throw new Error(`Quest ${i} has invalid complexity: ${questObj.complexity} (must be Easy, Medium, or Hard)`);
8112
+ }
8113
+ if (!Array.isArray(questObj.subQuests)) {
8114
+ throw new Error(`Quest "${questObj.id}" missing required field: subQuests (must be an array)`);
8115
+ }
8116
+ if (questObj.subQuests.length === 0) {
8117
+ throw new Error(`Quest "${questObj.id}" has no subQuests (minimum 2 required)`);
8118
+ }
8119
+ for (let j = 0; j < questObj.subQuests.length; j++) {
8120
+ const subQuest = questObj.subQuests[j];
8121
+ if (!subQuest || typeof subQuest !== "object") {
8122
+ throw new Error(`Quest "${questObj.id}" subQuest ${j} is not a valid object`);
8123
+ }
8124
+ const subQuestObj = subQuest;
8125
+ if (!subQuestObj.id || typeof subQuestObj.id !== "string" || subQuestObj.id.trim() === "") {
8126
+ throw new Error(`Quest "${questObj.id}" subQuest ${j} missing required field: id (must be non-empty string)`);
8127
+ }
8128
+ if (!subQuestObj.title || typeof subQuestObj.title !== "string" || subQuestObj.title.trim() === "") {
8129
+ throw new Error(`Quest "${questObj.id}" subQuest ${j} missing required field: title (must be non-empty string)`);
8130
+ }
8131
+ }
8132
+ }
8133
+ if (result.tags !== void 0) {
8134
+ if (!Array.isArray(result.tags)) {
8135
+ throw new Error("Function call field tags must be an array if provided");
8136
+ }
8137
+ for (let i = 0; i < result.tags.length; i++) {
8138
+ if (typeof result.tags[i] !== "string") {
8139
+ throw new Error(`Function call field tags[${i}] must be a string`);
8140
+ }
8141
+ }
8142
+ }
8143
+ return result;
8144
+ }
8145
+
7981
8146
  // ../../b4m-core/packages/utils/dist/src/questMaster.js
7982
8147
  var sanitizeId = (id) => {
7983
8148
  return id.replace(/\s+/g, "-").replace(/[^a-zA-Z0-9_.-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
@@ -8008,10 +8173,9 @@ var QuestMaster = class {
8008
8173
  }
8009
8174
  return "Easy";
8010
8175
  }
8011
- async processQuestPlan(questPlanText) {
8176
+ async processQuestPlan(input) {
8012
8177
  try {
8013
8178
  await this.onStatusUpdate(this.quest, "Processing quest plan...");
8014
- this.logger.log("Processing quest plan:", questPlanText);
8015
8179
  const defaultResponse = {
8016
8180
  type: "narrative",
8017
8181
  meta: {
@@ -8028,135 +8192,161 @@ var QuestMaster = class {
8028
8192
  }
8029
8193
  ]
8030
8194
  };
8031
- if (!questPlanText || typeof questPlanText !== "string") {
8032
- this.logger.warn("Invalid questPlanText received:", questPlanText);
8033
- if (this.quest) {
8034
- this.quest.questMasterReply = JSON.stringify(defaultResponse);
8035
- await this.db.quests.update({
8036
- id: this.quest.id,
8037
- questMasterReply: this.quest.questMasterReply,
8038
- reply: null,
8039
- replies: [],
8040
- status: "done",
8041
- type: "message"
8042
- });
8043
- }
8044
- return;
8045
- }
8046
8195
  if (!this.quest) {
8047
8196
  throw new Error("No quest context available");
8048
8197
  }
8049
- const extracted = extractQuestMasterData(questPlanText, { logger: this.logger });
8050
- const metaStartTag = "<!--QuestMasterMeta";
8051
- const metaEndTag = "-->";
8052
- const metaStartIndex = questPlanText.indexOf(metaStartTag);
8053
- let goal = "Quest Plan";
8054
- let tags = [];
8055
8198
  let questResponse;
8056
- if (metaStartIndex !== -1) {
8057
- const metaEndIndex = questPlanText.indexOf(metaEndTag, metaStartIndex);
8058
- if (metaEndIndex !== -1) {
8059
- const metaContent = questPlanText.slice(metaStartIndex + metaStartTag.length, metaEndIndex).trim();
8060
- try {
8061
- const jsonStartIndex = metaContent.indexOf("{");
8062
- if (jsonStartIndex !== -1) {
8063
- const metaData = JSON.parse(metaContent.slice(jsonStartIndex));
8064
- goal = metaData.goal || goal;
8065
- tags = Array.isArray(metaData.tags) ? metaData.tags : [];
8066
- }
8067
- } catch (err) {
8068
- this.logger.error("Error parsing meta data:", err);
8069
- }
8070
- }
8071
- }
8072
- if (extracted.length > 0) {
8073
- this.logger.log("Successfully extracted QuestMaster data:", extracted);
8074
- await this.onStatusUpdate(this.quest, "Extracted QuestMaster data");
8075
- const validQuests = extracted.filter((quest) => quest !== null && typeof quest === "object" && quest.id && quest.title && quest.title !== "Step 1" && // Explicitly filter out generic Step 1
8076
- quest.description && quest.description !== "No description available");
8077
- if (validQuests.length === 0) {
8078
- throw new Error("No valid quests found after filtering");
8079
- }
8199
+ if (input !== null && typeof input === "object") {
8200
+ this.logger.log("Processing quest plan from function calling result");
8080
8201
  questResponse = {
8081
8202
  type: "quest_plan",
8082
8203
  meta: {
8083
- goal,
8084
- totalSteps: validQuests.length,
8085
- tags
8204
+ goal: input.goal,
8205
+ totalSteps: input.quests.length,
8206
+ tags: input.tags
8086
8207
  },
8087
- quests: validQuests.map((quest) => ({
8208
+ quests: input.quests.map((quest) => ({
8088
8209
  id: quest.id,
8089
- // No fallback - we filtered for valid IDs
8090
8210
  title: quest.title,
8091
- // No fallback - we filtered for valid titles
8092
8211
  description: quest.description,
8093
- // No fallback - we filtered for valid descriptions
8094
- complexity: quest.complexity || "Medium",
8212
+ complexity: quest.complexity,
8095
8213
  status: "not_started",
8096
- subQuests: Array.isArray(quest.subQuests) ? quest.subQuests.filter((sub) => sub && sub.id && sub.title && !sub.title.startsWith("Subtask")).map((sub) => ({
8214
+ subQuests: quest.subQuests.map((sub) => ({
8097
8215
  id: sub.id,
8098
8216
  title: sub.title,
8099
8217
  status: "not_started"
8100
- })) : []
8218
+ }))
8101
8219
  }))
8102
8220
  };
8103
- if (questResponse.quests.some((quest) => quest.subQuests.length === 0)) {
8104
- throw new Error("Some quests have no valid subquests after filtering");
8105
- }
8106
8221
  } else {
8107
- try {
8108
- const questPlan = JSON.parse(questPlanText);
8109
- if (questPlan && questPlan.questChain && Array.isArray(questPlan.questChain)) {
8110
- const validQuestChain = questPlan.questChain.filter((item) => item && item.quest && item.quest !== "Step 1" && item.details && item.details !== "No description available");
8111
- if (validQuestChain.length === 0) {
8112
- throw new Error("No valid quests found in quest chain");
8222
+ const questPlanText = input;
8223
+ this.logger.log("Processing quest plan:", questPlanText);
8224
+ if (!questPlanText || typeof questPlanText !== "string") {
8225
+ this.logger.warn("Invalid questPlanText received:", questPlanText);
8226
+ if (this.quest) {
8227
+ this.quest.questMasterReply = JSON.stringify(defaultResponse);
8228
+ await this.db.quests.update({
8229
+ id: this.quest.id,
8230
+ questMasterReply: this.quest.questMasterReply,
8231
+ reply: null,
8232
+ replies: [],
8233
+ status: "done",
8234
+ type: "message"
8235
+ });
8236
+ }
8237
+ return;
8238
+ }
8239
+ const extracted = extractQuestMasterData(questPlanText, { logger: this.logger });
8240
+ const metaStartTag = "<!--QuestMasterMeta";
8241
+ const metaEndTag = "-->";
8242
+ const metaStartIndex = questPlanText.indexOf(metaStartTag);
8243
+ let goal = "Quest Plan";
8244
+ let tags = [];
8245
+ if (metaStartIndex !== -1) {
8246
+ const metaEndIndex = questPlanText.indexOf(metaEndTag, metaStartIndex);
8247
+ if (metaEndIndex !== -1) {
8248
+ const metaContent = questPlanText.slice(metaStartIndex + metaStartTag.length, metaEndIndex).trim();
8249
+ try {
8250
+ const jsonStartIndex = metaContent.indexOf("{");
8251
+ if (jsonStartIndex !== -1) {
8252
+ const metaData = JSON.parse(metaContent.slice(jsonStartIndex));
8253
+ goal = metaData.goal || goal;
8254
+ tags = Array.isArray(metaData.tags) ? metaData.tags : [];
8255
+ }
8256
+ } catch (err) {
8257
+ this.logger.error("Error parsing meta data:", err);
8113
8258
  }
8114
- questResponse = {
8115
- type: "quest_plan",
8116
- meta: {
8117
- goal: questPlan.abstractObjective || "Process Request",
8118
- totalSteps: validQuestChain.length
8119
- },
8120
- quests: validQuestChain.map((item, index) => ({
8121
- id: `quest-${index + 1}`,
8122
- title: item.quest,
8123
- description: item.details,
8124
- complexity: this.determineComplexity(item.details),
8125
- status: "Not Started",
8126
- subQuests: []
8127
- // This will trigger a retry since we require subquests
8128
- }))
8129
- };
8130
- } else {
8131
- if (!questPlanText.trim() || questPlanText.trim() === "Step 1") {
8132
- throw new Error("Empty or invalid response received");
8259
+ }
8260
+ }
8261
+ if (extracted.length > 0) {
8262
+ this.logger.log("Successfully extracted QuestMaster data:", extracted);
8263
+ await this.onStatusUpdate(this.quest, "Extracted QuestMaster data");
8264
+ const validQuests = extracted.filter((quest) => quest !== null && typeof quest === "object" && quest.id && quest.title && quest.title !== "Step 1" && // Explicitly filter out generic Step 1
8265
+ quest.description && quest.description !== "No description available");
8266
+ if (validQuests.length === 0) {
8267
+ throw new Error("No valid quests found after filtering");
8268
+ }
8269
+ questResponse = {
8270
+ type: "quest_plan",
8271
+ meta: {
8272
+ goal,
8273
+ totalSteps: validQuests.length,
8274
+ tags
8275
+ },
8276
+ quests: validQuests.map((quest) => ({
8277
+ id: quest.id,
8278
+ // No fallback - we filtered for valid IDs
8279
+ title: quest.title,
8280
+ // No fallback - we filtered for valid titles
8281
+ description: quest.description,
8282
+ // No fallback - we filtered for valid descriptions
8283
+ complexity: quest.complexity || "Medium",
8284
+ status: "not_started",
8285
+ subQuests: Array.isArray(quest.subQuests) ? quest.subQuests.filter((sub) => sub && sub.id && sub.title && !sub.title.startsWith("Subtask")).map((sub) => ({
8286
+ id: sub.id,
8287
+ title: sub.title,
8288
+ status: "not_started"
8289
+ })) : []
8290
+ }))
8291
+ };
8292
+ if (questResponse.quests.some((quest) => quest.subQuests.length === 0)) {
8293
+ throw new Error("Some quests have no valid subquests after filtering");
8294
+ }
8295
+ } else {
8296
+ try {
8297
+ const questPlan = JSON.parse(questPlanText);
8298
+ if (questPlan && questPlan.questChain && Array.isArray(questPlan.questChain)) {
8299
+ const validQuestChain = questPlan.questChain.filter((item) => item && item.quest && item.quest !== "Step 1" && item.details && item.details !== "No description available");
8300
+ if (validQuestChain.length === 0) {
8301
+ throw new Error("No valid quests found in quest chain");
8302
+ }
8303
+ questResponse = {
8304
+ type: "quest_plan",
8305
+ meta: {
8306
+ goal: questPlan.abstractObjective || "Process Request",
8307
+ totalSteps: validQuestChain.length
8308
+ },
8309
+ quests: validQuestChain.map((item, index) => ({
8310
+ id: `quest-${index + 1}`,
8311
+ title: item.quest,
8312
+ description: item.details,
8313
+ complexity: this.determineComplexity(item.details),
8314
+ status: "not_started",
8315
+ subQuests: []
8316
+ // This will trigger a retry since we require subquests
8317
+ }))
8318
+ };
8319
+ } else {
8320
+ if (!questPlanText.trim() || questPlanText.trim() === "Step 1") {
8321
+ throw new Error("Empty or invalid response received");
8322
+ }
8323
+ questResponse = {
8324
+ type: "narrative",
8325
+ meta: {
8326
+ goal: "Direct Response",
8327
+ totalSteps: 1
8328
+ },
8329
+ quests: [
8330
+ {
8331
+ id: "narrative-1",
8332
+ title: "Process Response",
8333
+ description: questPlanText.trim(),
8334
+ complexity: "Medium",
8335
+ subQuests: [
8336
+ {
8337
+ id: "narrative-1-1",
8338
+ title: "Review Response",
8339
+ status: "not_started"
8340
+ }
8341
+ ]
8342
+ }
8343
+ ]
8344
+ };
8133
8345
  }
8134
- questResponse = {
8135
- type: "narrative",
8136
- meta: {
8137
- goal: "Direct Response",
8138
- totalSteps: 1
8139
- },
8140
- quests: [
8141
- {
8142
- id: "narrative-1",
8143
- title: "Process Response",
8144
- description: questPlanText.trim(),
8145
- complexity: "Medium",
8146
- subQuests: [
8147
- {
8148
- id: "narrative-1-1",
8149
- title: "Review Response",
8150
- status: "not_started"
8151
- }
8152
- ]
8153
- }
8154
- ]
8155
- };
8346
+ } catch (parseError) {
8347
+ this.logger.error("Error parsing quest plan JSON:", parseError);
8348
+ questResponse = defaultResponse;
8156
8349
  }
8157
- } catch (parseError) {
8158
- this.logger.error("Error parsing quest plan JSON:", parseError);
8159
- questResponse = defaultResponse;
8160
8350
  }
8161
8351
  }
8162
8352
  if (!this.validateQuestResponse(questResponse)) {
@@ -8356,9 +8546,106 @@ var QuestMaster = class {
8356
8546
  }
8357
8547
  -->`;
8358
8548
  }
8549
+ /**
8550
+ * Create a quest plan using GPT-5's function calling feature.
8551
+ * This method is used when the model is a GPT-5 variant that supports tools
8552
+ * but doesn't reliably follow HTML comment formatting.
8553
+ *
8554
+ * @param model - The GPT-5 model to use
8555
+ * @param prompt - The user's request to break down into quests
8556
+ * @returns The parsed quest plan result
8557
+ */
8558
+ async createQuestPlanWithFunctionCalling(model, prompt, retryCount = 0) {
8559
+ const MAX_RETRIES = 2;
8560
+ const messages = [
8561
+ {
8562
+ role: "system",
8563
+ content: `You are the QuestMaster, an AI agent that breaks down complex tasks into manageable quests.
8564
+
8565
+ When given a user request, you MUST call the create_quest_plan function to structure your response.
8566
+ Do NOT respond with plain text - ALWAYS use the function call.
8567
+
8568
+ Guidelines:
8569
+ - Break down complex tasks into 3-7 main quests
8570
+ - Each quest needs 2-5 specific, actionable subquests
8571
+ - Make titles clear and action-oriented (e.g., "Set up database schema" not "Step 1")
8572
+ - Provide detailed descriptions explaining what needs to be done
8573
+ - Assess complexity accurately: Easy (< 1 hour), Medium (1-4 hours), Hard (> 4 hours)
8574
+ - Generate 2-5 relevant tags categorizing the work`
8575
+ },
8576
+ { role: "user", content: prompt }
8577
+ ];
8578
+ const toolDef = {
8579
+ toolFn: async (params) => JSON.stringify(params),
8580
+ // Not executed, just captured
8581
+ toolSchema: createQuestPlanToolSchema
8582
+ };
8583
+ const options = {
8584
+ temperature: retryCount > 0 ? 0.5 : 0.7,
8585
+ // Lower temperature for structured JSON outputs; even lower on retries
8586
+ n: 1,
8587
+ stream: false,
8588
+ tools: [toolDef],
8589
+ executeTools: false,
8590
+ // Don't execute, just capture the function call
8591
+ tool_choice: { type: "function", function: { name: "create_quest_plan" } },
8592
+ // Force function call
8593
+ parallel_tool_calls: false
8594
+ // Required for structured outputs
8595
+ };
8596
+ let functionResult = null;
8597
+ let lastError = null;
8598
+ try {
8599
+ await this.llm.complete(model, messages, options, async (text, completionInfo) => {
8600
+ if (completionInfo?.toolsUsed?.length) {
8601
+ const toolCall = completionInfo.toolsUsed[0];
8602
+ if (toolCall.name === "create_quest_plan" && toolCall.arguments) {
8603
+ this.logger.log("GPT-5 function call received, parsing arguments...");
8604
+ functionResult = parseQuestPlanFunctionCall(toolCall.arguments);
8605
+ }
8606
+ }
8607
+ });
8608
+ } catch (error) {
8609
+ lastError = error instanceof Error ? error : new Error(String(error));
8610
+ this.logger.error("Error in GPT-5 function calling:", lastError);
8611
+ }
8612
+ if (!functionResult) {
8613
+ if (retryCount < MAX_RETRIES) {
8614
+ this.logger.warn(`GPT-5 did not return function call, retrying (attempt ${retryCount + 1}/${MAX_RETRIES})...`);
8615
+ return this.createQuestPlanWithFunctionCalling(model, prompt, retryCount + 1);
8616
+ }
8617
+ throw lastError || new Error("GPT-5 did not call create_quest_plan function after retries");
8618
+ }
8619
+ const result = functionResult;
8620
+ const questsWithoutSubquests = result.quests.filter((q) => !q.subQuests || q.subQuests.length === 0);
8621
+ if (questsWithoutSubquests.length > 0) {
8622
+ if (retryCount < MAX_RETRIES) {
8623
+ this.logger.warn(`Some quests missing subquests: ${questsWithoutSubquests.map((q) => q.id).join(", ")}, retrying...`);
8624
+ return this.createQuestPlanWithFunctionCalling(model, prompt, retryCount + 1);
8625
+ } else {
8626
+ this.logger.warn(`Returning quest plan with ${questsWithoutSubquests.length} quests missing subquests after max retries (${MAX_RETRIES}): ${questsWithoutSubquests.map((q) => q.id).join(", ")}`);
8627
+ }
8628
+ }
8629
+ this.logger.log("Successfully received GPT-5 function call result:", {
8630
+ goal: result.goal,
8631
+ questCount: result.quests.length,
8632
+ tags: result.tags
8633
+ });
8634
+ return result;
8635
+ }
8359
8636
  async createQuestPlan(model, prompt) {
8360
8637
  try {
8361
8638
  await this.onStatusUpdate(this.quest, "Creating quest plan...");
8639
+ if (isGPT5ModelWithToolSupport(model)) {
8640
+ this.logger.log(`Using function calling approach for GPT-5 model: ${model}`);
8641
+ try {
8642
+ const result = await this.createQuestPlanWithFunctionCalling(model, prompt);
8643
+ await this.processQuestPlan(result);
8644
+ return;
8645
+ } catch (functionCallError) {
8646
+ this.logger.error("Function calling failed, falling back to HTML approach:", functionCallError);
8647
+ }
8648
+ }
8362
8649
  const messages = [
8363
8650
  {
8364
8651
  role: "system",
@@ -10452,6 +10739,9 @@ export {
10452
10739
  sendToConnection,
10453
10740
  ClientMessageSender,
10454
10741
  OpenaiModerationsService,
10742
+ isGPT5ModelWithToolSupport,
10743
+ createQuestPlanToolSchema,
10744
+ parseQuestPlanFunctionCall,
10455
10745
  QuestMaster,
10456
10746
  OpenAIImageService,
10457
10747
  TestImageService,
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  BadRequestError,
4
4
  secureParameters
5
- } from "./chunk-NZADYDFG.js";
5
+ } from "./chunk-BSDDDQF4.js";
6
6
  import {
7
7
  GenericCreditDeductTransaction,
8
8
  ImageEditUsageTransaction,
File without changes
@@ -6,7 +6,7 @@ import {
6
6
  getSettingsByNames,
7
7
  obfuscateApiKey,
8
8
  secureParameters
9
- } from "./chunk-NZADYDFG.js";
9
+ } from "./chunk-BSDDDQF4.js";
10
10
  import {
11
11
  ApiKeyType,
12
12
  MementoTier,
File without changes
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  createFabFile,
4
4
  createFabFileSchema
5
- } from "./chunk-5N4PG5EH.js";
6
- import "./chunk-NZADYDFG.js";
5
+ } from "./chunk-55HUCQWS.js";
6
+ import "./chunk-BSDDDQF4.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
8
  import "./chunk-WR6YZRQV.js";
9
9
  import "./chunk-PDX44BCA.js";
File without changes
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-GZTILMGE.js";
8
- import "./chunk-Y4HMTEDH.js";
9
- import "./chunk-5N4PG5EH.js";
7
+ } from "./chunk-QGLZY6IF.js";
8
+ import "./chunk-CS24JD7W.js";
9
+ import "./chunk-55HUCQWS.js";
10
10
  import {
11
11
  BFLImageService,
12
12
  BaseStorage,
@@ -15,7 +15,7 @@ import {
15
15
  OpenAIBackend,
16
16
  OpenAIImageService,
17
17
  XAIImageService
18
- } from "./chunk-NZADYDFG.js";
18
+ } from "./chunk-BSDDDQF4.js";
19
19
  import {
20
20
  Logger
21
21
  } from "./chunk-AMDXHL6S.js";
@@ -9178,7 +9178,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
9178
9178
  // package.json
9179
9179
  var package_default = {
9180
9180
  name: "@bike4mind/cli",
9181
- version: "0.2.2",
9181
+ version: "0.2.3-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
9182
9182
  type: "module",
9183
9183
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
9184
9184
  license: "UNLICENSED",
@@ -9279,11 +9279,11 @@ var package_default = {
9279
9279
  zustand: "^4.5.4"
9280
9280
  },
9281
9281
  devDependencies: {
9282
- "@bike4mind/agents": "workspace:*",
9283
- "@bike4mind/common": "workspace:*",
9284
- "@bike4mind/mcp": "workspace:*",
9285
- "@bike4mind/services": "workspace:*",
9286
- "@bike4mind/utils": "workspace:*",
9282
+ "@bike4mind/agents": "0.1.0",
9283
+ "@bike4mind/common": "2.37.1-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
9284
+ "@bike4mind/mcp": "1.19.5-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
9285
+ "@bike4mind/services": "2.31.1-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
9286
+ "@bike4mind/utils": "2.0.4-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
9287
9287
  "@types/diff": "^5.0.9",
9288
9288
  "@types/jsonwebtoken": "^9.0.4",
9289
9289
  "@types/node": "^22.9.0",
@@ -9294,7 +9294,8 @@ var package_default = {
9294
9294
  tsx: "^4.21.0",
9295
9295
  typescript: "^5.9.3",
9296
9296
  vitest: "^3.2.4"
9297
- }
9297
+ },
9298
+ gitHead: "aaf443baeab1b87a7675e1577cc6535b2042bcdc"
9298
9299
  };
9299
9300
 
9300
9301
  // src/config/constants.ts
File without changes
File without changes
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  findMostSimilarMemento,
4
4
  getRelevantMementos
5
- } from "./chunk-GZTILMGE.js";
6
- import "./chunk-NZADYDFG.js";
5
+ } from "./chunk-QGLZY6IF.js";
6
+ import "./chunk-BSDDDQF4.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
8
  import "./chunk-WR6YZRQV.js";
9
9
  import "./chunk-PDX44BCA.js";
File without changes
File without changes
@@ -65,6 +65,7 @@ import {
65
65
  cleanMermaidSyntax,
66
66
  computeCosineSimilarity,
67
67
  convertCodeBlocksToArtifacts,
68
+ createQuestPlanToolSchema,
68
69
  createTokenizer,
69
70
  decodeBase64DataUrl,
70
71
  detectURLs,
@@ -97,6 +98,7 @@ import {
97
98
  includeImagePromptSystemMessage,
98
99
  initializeConfig,
99
100
  invalidateSettingsCache,
101
+ isGPT5ModelWithToolSupport,
100
102
  isMermaidSyntax,
101
103
  isModelAccessible,
102
104
  isPlainText,
@@ -105,6 +107,7 @@ import {
105
107
  isZodError,
106
108
  obfuscateApiKey,
107
109
  parseArtifacts,
110
+ parseQuestPlanFunctionCall,
108
111
  processFabFilesServer,
109
112
  processUrlsFromPrompt,
110
113
  secureParameters,
@@ -117,7 +120,7 @@ import {
117
120
  validateMermaidSyntax,
118
121
  warmUpSettingsCache,
119
122
  withRetry
120
- } from "./chunk-NZADYDFG.js";
123
+ } from "./chunk-BSDDDQF4.js";
121
124
  import {
122
125
  Logger,
123
126
  NotificationDeduplicator,
@@ -196,6 +199,7 @@ export {
196
199
  cleanMermaidSyntax,
197
200
  computeCosineSimilarity,
198
201
  convertCodeBlocksToArtifacts,
202
+ createQuestPlanToolSchema,
199
203
  createTokenizer,
200
204
  decodeBase64DataUrl,
201
205
  detectURLs,
@@ -228,6 +232,7 @@ export {
228
232
  includeImagePromptSystemMessage,
229
233
  initializeConfig,
230
234
  invalidateSettingsCache,
235
+ isGPT5ModelWithToolSupport,
231
236
  isMermaidSyntax,
232
237
  isModelAccessible,
233
238
  isPlainText,
@@ -238,6 +243,7 @@ export {
238
243
  notifyEventLogsToSlack,
239
244
  obfuscateApiKey,
240
245
  parseArtifacts,
246
+ parseQuestPlanFunctionCall,
241
247
  postLowCreditsNotificationToSlack,
242
248
  postMessageToSlack,
243
249
  processFabFilesServer,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  SubtractCreditsSchema,
4
4
  subtractCredits
5
- } from "./chunk-Y4HMTEDH.js";
6
- import "./chunk-NZADYDFG.js";
5
+ } from "./chunk-CS24JD7W.js";
6
+ import "./chunk-BSDDDQF4.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
8
  import "./chunk-WR6YZRQV.js";
9
9
  import "./chunk-PDX44BCA.js";
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.2.2",
3
+ "version": "0.2.3-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -30,6 +30,15 @@
30
30
  "dist",
31
31
  "bin"
32
32
  ],
33
+ "scripts": {
34
+ "dev": "tsx src/index.tsx",
35
+ "build": "tsup",
36
+ "typecheck": "tsc --noEmit",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "start": "node dist/index.js",
40
+ "prepublishOnly": "pnpm build"
41
+ },
33
42
  "dependencies": {
34
43
  "@anthropic-ai/sdk": "^0.22.0",
35
44
  "@aws-sdk/client-apigatewaymanagementapi": "3.654.0",
@@ -92,6 +101,11 @@
92
101
  "zustand": "^4.5.4"
93
102
  },
94
103
  "devDependencies": {
104
+ "@bike4mind/agents": "0.1.0",
105
+ "@bike4mind/common": "2.37.1-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
106
+ "@bike4mind/mcp": "1.19.5-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
107
+ "@bike4mind/services": "2.31.1-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
108
+ "@bike4mind/utils": "2.0.4-feat-questmaster-gpt5-function-calling.17056+aaf443bae",
95
109
  "@types/diff": "^5.0.9",
96
110
  "@types/jsonwebtoken": "^9.0.4",
97
111
  "@types/node": "^22.9.0",
@@ -101,19 +115,7 @@
101
115
  "tsup": "^8.5.1",
102
116
  "tsx": "^4.21.0",
103
117
  "typescript": "^5.9.3",
104
- "vitest": "^3.2.4",
105
- "@bike4mind/common": "2.37.0",
106
- "@bike4mind/agents": "0.1.0",
107
- "@bike4mind/mcp": "1.19.4",
108
- "@bike4mind/services": "2.31.0",
109
- "@bike4mind/utils": "2.0.3"
118
+ "vitest": "^3.2.4"
110
119
  },
111
- "scripts": {
112
- "dev": "tsx src/index.tsx",
113
- "build": "tsup",
114
- "typecheck": "tsc --noEmit",
115
- "test": "vitest run",
116
- "test:watch": "vitest",
117
- "start": "node dist/index.js"
118
- }
119
- }
120
+ "gitHead": "aaf443baeab1b87a7675e1577cc6535b2042bcdc"
121
+ }