@cognipeer/agent-sdk 0.8.3 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4573,6 +4573,7 @@ function resolveUserQuestionState(state, resolution) {
4573
4573
  ctx
4574
4574
  };
4575
4575
  }
4576
+ var ASK_USER_TOOL_NAME = "ask_user_question";
4576
4577
  var DEFAULT_DESCRIPTION_WITH_FREETEXT = "Pause the agent and surface a structured prompt to the human user when you genuinely cannot proceed without their input. Group up to 4 closely related questions into a single call. Prefer concrete options the user can pick over open-ended free-text \u2014 list `options` whenever you can anticipate the choices. The user may type a custom answer for any question. Do not use this tool for things you can deduce from the conversation, the system prompt, or another tool. After the user answers, their selections come back as the tool result.";
4577
4578
  var DEFAULT_DESCRIPTION_NO_FREETEXT = "Pause the agent and surface a structured multi-choice prompt to the human user when you genuinely cannot proceed without their input. Group up to 4 closely related questions into a single call. Every question MUST include at least two `options` \u2014 free-text answers are disabled for this agent. Do not use this tool for things you can deduce from the conversation, the system prompt, or another tool. After the user picks, their selections come back as the tool result.";
4578
4579
  function buildOptionSchema() {
@@ -4608,7 +4609,7 @@ function createAskUserQuestionTool(stateRef, config) {
4608
4609
  const description = config.promptOverride ?? (allowFreeText ? DEFAULT_DESCRIPTION_WITH_FREETEXT : DEFAULT_DESCRIPTION_NO_FREETEXT);
4609
4610
  const questionSchema = buildQuestionSchema(allowFreeText);
4610
4611
  const askUser = createTool({
4611
- name: "ask_user_question",
4612
+ name: ASK_USER_TOOL_NAME,
4612
4613
  description,
4613
4614
  schema: zod.z.object({
4614
4615
  questions: zod.z.array(questionSchema).min(1).max(4).describe("1-4 related questions to ask in a single pause.")
@@ -4624,7 +4625,7 @@ function createAskUserQuestionTool(stateRef, config) {
4624
4625
  const entry = {
4625
4626
  id: nanoid.nanoid(),
4626
4627
  toolCallId,
4627
- toolName: "ask_user_question",
4628
+ toolName: ASK_USER_TOOL_NAME,
4628
4629
  questions,
4629
4630
  status: "pending",
4630
4631
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -4980,14 +4981,15 @@ function createAgent(opts) {
4980
4981
  })();
4981
4982
  const askUserOnQuestion = typeof opts.humanInTheLoop?.askUser === "object" ? opts.humanInTheLoop?.askUser?.onQuestion : void 0;
4982
4983
  const askUserStateRef = askUserConfig ? { pendingUserQuestions: void 0, ctx: void 0, __onEvent: void 0, __currentToolCallId: void 0 } : void 0;
4983
- if (askUserConfig && askUserStateRef) {
4984
+ const hasTool = (name) => toolsBase.some((tool) => tool?.name === name);
4985
+ if (askUserConfig && askUserStateRef && !hasTool(ASK_USER_TOOL_NAME)) {
4984
4986
  toolsBase.push(createAskUserQuestionTool(askUserStateRef, askUserConfig));
4985
4987
  }
4986
4988
  const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
4987
4989
  if (soManager) {
4988
4990
  const modelCapabilities = getModelCapabilities(opts.model);
4989
4991
  const responseTool = soManager.getResponseTool();
4990
- if (responseTool && modelCapabilities.structuredOutput !== "native") {
4992
+ if (responseTool && modelCapabilities.structuredOutput !== "native" && !hasTool(responseTool.name)) {
4991
4993
  toolsBase.push(responseTool);
4992
4994
  }
4993
4995
  }
@@ -6920,6 +6922,42 @@ async function resolveAvailableSkills(skills, opts = {}) {
6920
6922
  }
6921
6923
  return out;
6922
6924
  }
6925
+ var WORD_SPLIT = /[^\p{L}\p{N}]+/u;
6926
+ var MIN_TERM_LENGTH = 3;
6927
+ var FIELD_WEIGHTS = { key: 4, title: 3, header: 2 };
6928
+ var wordsOf = (text) => text.toLowerCase().split(WORD_SPLIT).filter((word) => word.length >= 2);
6929
+ var termHitsWord = (term, word) => word === term || word.startsWith(term) || word.length >= 3 && term.startsWith(word) || term.length >= 5 && word.length >= 5 && word.slice(0, 4) === term.slice(0, 4);
6930
+ function searchSkills(skills, query, limit = 10) {
6931
+ const cap = Math.max(1, limit);
6932
+ const normalized = (query || "").trim().toLowerCase();
6933
+ const terms = [
6934
+ ...new Set(normalized.split(WORD_SPLIT).filter((term) => term.length >= MIN_TERM_LENGTH))
6935
+ ];
6936
+ if (terms.length === 0) return skills.slice(0, cap);
6937
+ const scored = skills.map((skill, index) => {
6938
+ const fields = {
6939
+ key: wordsOf(skill.key),
6940
+ title: wordsOf(skill.title || ""),
6941
+ header: wordsOf(skill.header || "")
6942
+ };
6943
+ const exact = skill.key.toLowerCase() === normalized || (skill.title || "").toLowerCase() === normalized;
6944
+ let score = exact ? 100 : 0;
6945
+ let coverage = 0;
6946
+ for (const term of terms) {
6947
+ let hit = false;
6948
+ for (const [field, weight] of Object.entries(FIELD_WEIGHTS)) {
6949
+ if (fields[field].some((word) => termHitsWord(term, word))) {
6950
+ score += weight;
6951
+ hit = true;
6952
+ }
6953
+ }
6954
+ if (hit) coverage += 1;
6955
+ }
6956
+ return { skill, score, coverage, index };
6957
+ }).filter((entry) => entry.score > 0);
6958
+ scored.sort((a, b) => b.coverage - a.coverage || b.score - a.score || a.index - b.index);
6959
+ return scored.slice(0, cap).map((entry) => entry.skill);
6960
+ }
6923
6961
  function buildSkillHeaderBlock(skills) {
6924
6962
  if (skills.length === 0) return "";
6925
6963
  const lines = skills.map((skill) => `- ${skill.key}: ${skill.header}`);
@@ -6943,24 +6981,65 @@ function dedupeToolsByName(tools) {
6943
6981
 
6944
6982
  // src/smart/skills/skillTools.ts
6945
6983
  var MAX_RETURNED_INDEX = 40;
6946
- var OPEN_SKILL_DESCRIPTION = "Load a capability on demand. Pass a skillKey from <available_skills>. A small skill binds all its tools immediately; a large one returns a tool index \u2014 then call bind_skill_tools with only the tools you need. Open only what the task needs.";
6984
+ var DEFAULT_SEARCH_LIMIT = 10;
6985
+ var MAX_SEARCH_LIMIT = 25;
6986
+ var isSearchDisclosure = (policy) => policy.disclosure === "search";
6987
+ var keySource = (policy) => isSearchDisclosure(policy) ? "search_skills" : "<available_skills>";
6988
+ var openSkillDescription = (policy) => `Load a capability on demand. Pass a skillKey from ${keySource(policy)}. A small skill binds all its tools immediately; a large one returns a tool index \u2014 then call bind_skill_tools with only the tools you need. Open only what the task needs.`;
6947
6989
  var BIND_SKILL_TOOLS_DESCRIPTION = "After open_skill on a large skill, bind a chosen subset of its tools by name. Pick the few tools the task actually needs.";
6990
+ var SEARCH_SKILLS_DESCRIPTION = 'Find a reusable playbook (a "skill") for the task at hand. Skills are proven, pre-written procedures for specific kinds of work \u2014 each with its own tools, which are NOT in your tool list until you open the skill. Call this early whenever a request looks like an established procedure, or when you seem to be missing a capability you would expect to have: describe the task in `query`. Returns matching skill keys; pass one to open_skill to load its instructions and tools. An empty query lists what exists.';
6948
6991
  function findSkill(deps, key) {
6949
6992
  return deps.skills.find((skill) => skill.key === key);
6950
6993
  }
6994
+ function createSearchSkillsTool(deps) {
6995
+ const tool = createTool({
6996
+ name: "search_skills",
6997
+ description: SEARCH_SKILLS_DESCRIPTION,
6998
+ schema: zod.z.object({
6999
+ query: zod.z.string().optional().describe("What you are trying to do, in a few words. Omit to list available skills."),
7000
+ limit: zod.z.number().int().positive().optional().describe(`Max results to return (default ${DEFAULT_SEARCH_LIMIT}).`)
7001
+ }).strict(),
7002
+ func: async ({ query, limit }) => {
7003
+ const { skills, policy, registryRef: ref } = tool._deps;
7004
+ const available = await resolveAvailableSkills(skills, { modelTier: policy.modelTier });
7005
+ if (available.length === 0) {
7006
+ return { skills: [], total: 0, hint: "No skills are available in this workspace." };
7007
+ }
7008
+ const capped = Math.min(limit || DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT);
7009
+ const matches = searchSkills(available, query, capped);
7010
+ const missed = matches.length === 0;
7011
+ const shown = missed ? available.slice(0, capped) : matches;
7012
+ return {
7013
+ skills: shown.map((skill) => ({
7014
+ skillKey: skill.key,
7015
+ title: skill.title,
7016
+ header: skill.header,
7017
+ ...ref.openedSkillKeys.includes(skill.key) ? { alreadyOpen: true } : {}
7018
+ })),
7019
+ total: available.length,
7020
+ hint: missed ? `Nothing matched "${query}" literally \u2014 listing ${shown.length} of ${available.length} instead. Judge them by their headers; call open_skill(skillKey) if one fits, otherwise carry on without a skill.` : "Call open_skill(skillKey) to load the one that fits."
7021
+ };
7022
+ }
7023
+ });
7024
+ tool._deps = deps;
7025
+ tool.__source = "smart-skills";
7026
+ return tool;
7027
+ }
6951
7028
  function createOpenSkillTool(deps) {
6952
7029
  const tool = createTool({
6953
7030
  name: "open_skill",
6954
- description: OPEN_SKILL_DESCRIPTION,
7031
+ description: openSkillDescription(deps.policy),
6955
7032
  schema: zod.z.object({
6956
- skillKey: zod.z.string().min(1).describe("A key from <available_skills>."),
7033
+ skillKey: zod.z.string().min(1).describe(`A key from ${keySource(deps.policy)}.`),
6957
7034
  query: zod.z.string().optional().describe("What you need from the skill \u2014 used to rank a large skill's tools.")
6958
7035
  }).strict(),
6959
7036
  func: async ({ skillKey, query }) => {
6960
7037
  const { registryRef: ref, policy } = tool._deps;
6961
7038
  const skill = findSkill(tool._deps, skillKey);
6962
7039
  if (!skill) {
6963
- return { error: `Unknown skill: ${skillKey}. Choose a key listed in <available_skills>.` };
7040
+ return {
7041
+ error: `Unknown skill: ${skillKey}. ${isSearchDisclosure(policy) ? "Call search_skills to get a valid key." : "Choose a key listed in <available_skills>."}`
7042
+ };
6964
7043
  }
6965
7044
  if (skill.isAvailable && !await skill.isAvailable()) {
6966
7045
  return { error: `Skill ${skillKey} is not available in this workspace.` };
@@ -7049,7 +7128,9 @@ function createBindSkillToolsTool(deps) {
7049
7128
  return tool;
7050
7129
  }
7051
7130
  function createSkillTools(deps) {
7052
- return [createOpenSkillTool(deps), createBindSkillToolsTool(deps)];
7131
+ const tools = [createOpenSkillTool(deps), createBindSkillToolsTool(deps)];
7132
+ if (isSearchDisclosure(deps.policy)) tools.unshift(createSearchSkillsTool(deps));
7133
+ return tools;
7053
7134
  }
7054
7135
 
7055
7136
  // src/smart/subagents/types.ts
@@ -7729,7 +7810,8 @@ function createSmartAgent(opts) {
7729
7810
  if (changed) toolSet.skillRegistryRef.__onToolsChanged?.();
7730
7811
  }
7731
7812
  const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
7732
- const availableSkills = skillsEnabled ? await resolveAvailableSkills(skills, { modelTier: skillPolicy.modelTier }) : [];
7813
+ const catalogDisclosure = skillPolicy.disclosure !== "search";
7814
+ const availableSkills = skillsEnabled && catalogDisclosure ? await resolveAvailableSkills(skills, { modelTier: skillPolicy.modelTier }) : [];
7733
7815
  const skillBlock = availableSkills.length > 0 ? buildSkillHeaderBlock(availableSkills) : void 0;
7734
7816
  let subagentBlock;
7735
7817
  if (subagentsEnabled) {
@@ -10947,6 +11029,7 @@ function createProvider(config) {
10947
11029
  }
10948
11030
  }
10949
11031
 
11032
+ exports.ASK_USER_TOOL_NAME = ASK_USER_TOOL_NAME;
10950
11033
  exports.AnthropicProvider = AnthropicProvider;
10951
11034
  exports.AzureProvider = AzureProvider;
10952
11035
  exports.BaseProvider = BaseProvider;
@@ -11005,6 +11088,7 @@ exports.createParallelTool = createParallelTool;
11005
11088
  exports.createProvider = createProvider;
11006
11089
  exports.createRegexGuardrail = createRegexGuardrail;
11007
11090
  exports.createResolverNode = createResolverNode;
11091
+ exports.createSearchSkillsTool = createSearchSkillsTool;
11008
11092
  exports.createSkillRegistryRef = createSkillRegistryRef;
11009
11093
  exports.createSkillTools = createSkillTools;
11010
11094
  exports.createSmartAgent = createSmartAgent;
@@ -11056,6 +11140,7 @@ exports.resolveUserQuestionState = resolveUserQuestionState;
11056
11140
  exports.restoreSnapshot = restoreSnapshot;
11057
11141
  exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
11058
11142
  exports.scoreItemsByRelevance = scoreItemsByRelevance;
11143
+ exports.searchSkills = searchSkills;
11059
11144
  exports.seedChildMessages = seedChildMessages;
11060
11145
  exports.selectTopIndicesInOrder = selectTopIndicesInOrder;
11061
11146
  exports.setTokenCounter = setTokenCounter;