@cognipeer/agent-sdk 0.8.4 → 0.8.6

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
@@ -808,6 +808,28 @@ function normalizeUsage(raw) {
808
808
  };
809
809
  return normalized;
810
810
  }
811
+ function recordUsage(state, modelName, normalized) {
812
+ const usageState = state.usage || { perRequest: [], totals: {} };
813
+ const id = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
814
+ const cachedInput = normalized.prompt_tokens_details?.cached_tokens;
815
+ usageState.perRequest.push({
816
+ id,
817
+ modelName,
818
+ usage: normalized,
819
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
820
+ turn: usageState.perRequest.length + 1,
821
+ cachedInput
822
+ });
823
+ const previous = usageState.totals[modelName] || { input: 0, output: 0, total: 0, cachedInput: 0 };
824
+ usageState.totals[modelName] = {
825
+ input: previous.input + (Number(normalized.prompt_tokens) || 0),
826
+ output: previous.output + (Number(normalized.completion_tokens) || 0),
827
+ total: previous.total + (Number(normalized.total_tokens) || 0),
828
+ cachedInput: previous.cachedInput + (Number(cachedInput) || 0)
829
+ };
830
+ state.usage = usageState;
831
+ return id;
832
+ }
811
833
  function num(...vals) {
812
834
  for (const v of vals) {
813
835
  if (v === 0) return 0;
@@ -2946,24 +2968,7 @@ function createAgentCoreNode(opts) {
2946
2968
  messageList: messagesWithResponse
2947
2969
  });
2948
2970
  if (normalized) {
2949
- const usageState = state.usage || { perRequest: [], totals: {} };
2950
- const id = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2951
- const turn = usageState.perRequest.length + 1;
2952
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2953
- const cachedInputTok = normalized.prompt_tokens_details?.cached_tokens;
2954
- usageState.perRequest.push({ id, modelName, usage: normalized, timestamp, turn, cachedInput: cachedInputTok });
2955
- const inputTok = normalized.prompt_tokens;
2956
- const outputTok = normalized.completion_tokens;
2957
- const totalTok = normalized.total_tokens;
2958
- const key = modelName;
2959
- const agg = usageState.totals[key] || { input: 0, output: 0, total: 0, cachedInput: 0 };
2960
- usageState.totals[key] = {
2961
- input: agg.input + (Number(inputTok) || 0),
2962
- output: agg.output + (Number(outputTok) || 0),
2963
- total: agg.total + (Number(totalTok) || 0),
2964
- cachedInput: agg.cachedInput + (Number(cachedInputTok) || 0)
2965
- };
2966
- state.usage = usageState;
2971
+ recordUsage(state, modelName, normalized);
2967
2972
  }
2968
2973
  return { messages: messagesWithResponse, usage: state.usage };
2969
2974
  };
@@ -5746,24 +5751,7 @@ function createAgentNode(opts) {
5746
5751
  messageList: messagesWithResponse
5747
5752
  });
5748
5753
  if (normalized) {
5749
- const usageState = state.usage || { perRequest: [], totals: {} };
5750
- const id = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
5751
- const turn = usageState.perRequest.length + 1;
5752
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
5753
- const cachedInputTok = normalized.prompt_tokens_details.cached_tokens;
5754
- usageState.perRequest.push({ id, modelName, usage: normalized, timestamp, turn, cachedInput: cachedInputTok });
5755
- const inputTok = normalized.prompt_tokens;
5756
- const outputTok = normalized.completion_tokens;
5757
- const totalTok = normalized.total_tokens;
5758
- const key = modelName;
5759
- const agg = usageState.totals[key] || { input: 0, output: 0, total: 0, cachedInput: 0 };
5760
- usageState.totals[key] = {
5761
- input: agg.input + (Number(inputTok) || 0),
5762
- output: agg.output + (Number(outputTok) || 0),
5763
- total: agg.total + (Number(totalTok) || 0),
5764
- cachedInput: agg.cachedInput + (Number(cachedInputTok) || 0)
5765
- };
5766
- state.usage = usageState;
5754
+ recordUsage(state, modelName, normalized);
5767
5755
  }
5768
5756
  return { messages: messagesWithResponse, usage: state.usage };
5769
5757
  };
@@ -6207,6 +6195,7 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
6207
6195
  outputTokensActual = normalized.completion_tokens;
6208
6196
  totalTokensActual = normalized.total_tokens;
6209
6197
  cachedInputTokens = normalized.prompt_tokens_details?.cached_tokens;
6198
+ recordUsage(state, getModelName(model) || "unknown_model", normalized);
6210
6199
  }
6211
6200
  } catch (err) {
6212
6201
  durationMs = Date.now() - startTime;
@@ -6922,6 +6911,42 @@ async function resolveAvailableSkills(skills, opts = {}) {
6922
6911
  }
6923
6912
  return out;
6924
6913
  }
6914
+ var WORD_SPLIT = /[^\p{L}\p{N}]+/u;
6915
+ var MIN_TERM_LENGTH = 3;
6916
+ var FIELD_WEIGHTS = { key: 4, title: 3, header: 2 };
6917
+ var wordsOf = (text) => text.toLowerCase().split(WORD_SPLIT).filter((word) => word.length >= 2);
6918
+ 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);
6919
+ function searchSkills(skills, query, limit = 10) {
6920
+ const cap = Math.max(1, limit);
6921
+ const normalized = (query || "").trim().toLowerCase();
6922
+ const terms = [
6923
+ ...new Set(normalized.split(WORD_SPLIT).filter((term) => term.length >= MIN_TERM_LENGTH))
6924
+ ];
6925
+ if (terms.length === 0) return skills.slice(0, cap);
6926
+ const scored = skills.map((skill, index) => {
6927
+ const fields = {
6928
+ key: wordsOf(skill.key),
6929
+ title: wordsOf(skill.title || ""),
6930
+ header: wordsOf(skill.header || "")
6931
+ };
6932
+ const exact = skill.key.toLowerCase() === normalized || (skill.title || "").toLowerCase() === normalized;
6933
+ let score = exact ? 100 : 0;
6934
+ let coverage = 0;
6935
+ for (const term of terms) {
6936
+ let hit = false;
6937
+ for (const [field, weight] of Object.entries(FIELD_WEIGHTS)) {
6938
+ if (fields[field].some((word) => termHitsWord(term, word))) {
6939
+ score += weight;
6940
+ hit = true;
6941
+ }
6942
+ }
6943
+ if (hit) coverage += 1;
6944
+ }
6945
+ return { skill, score, coverage, index };
6946
+ }).filter((entry) => entry.score > 0);
6947
+ scored.sort((a, b) => b.coverage - a.coverage || b.score - a.score || a.index - b.index);
6948
+ return scored.slice(0, cap).map((entry) => entry.skill);
6949
+ }
6925
6950
  function buildSkillHeaderBlock(skills) {
6926
6951
  if (skills.length === 0) return "";
6927
6952
  const lines = skills.map((skill) => `- ${skill.key}: ${skill.header}`);
@@ -6945,24 +6970,65 @@ function dedupeToolsByName(tools) {
6945
6970
 
6946
6971
  // src/smart/skills/skillTools.ts
6947
6972
  var MAX_RETURNED_INDEX = 40;
6948
- 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.";
6973
+ var DEFAULT_SEARCH_LIMIT = 10;
6974
+ var MAX_SEARCH_LIMIT = 25;
6975
+ var isSearchDisclosure = (policy) => policy.disclosure === "search";
6976
+ var keySource = (policy) => isSearchDisclosure(policy) ? "search_skills" : "<available_skills>";
6977
+ 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.`;
6949
6978
  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.";
6979
+ 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.';
6950
6980
  function findSkill(deps, key) {
6951
6981
  return deps.skills.find((skill) => skill.key === key);
6952
6982
  }
6983
+ function createSearchSkillsTool(deps) {
6984
+ const tool = createTool({
6985
+ name: "search_skills",
6986
+ description: SEARCH_SKILLS_DESCRIPTION,
6987
+ schema: zod.z.object({
6988
+ query: zod.z.string().optional().describe("What you are trying to do, in a few words. Omit to list available skills."),
6989
+ limit: zod.z.number().int().positive().optional().describe(`Max results to return (default ${DEFAULT_SEARCH_LIMIT}).`)
6990
+ }).strict(),
6991
+ func: async ({ query, limit }) => {
6992
+ const { skills, policy, registryRef: ref } = tool._deps;
6993
+ const available = await resolveAvailableSkills(skills, { modelTier: policy.modelTier });
6994
+ if (available.length === 0) {
6995
+ return { skills: [], total: 0, hint: "No skills are available in this workspace." };
6996
+ }
6997
+ const capped = Math.min(limit || DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT);
6998
+ const matches = searchSkills(available, query, capped);
6999
+ const missed = matches.length === 0;
7000
+ const shown = missed ? available.slice(0, capped) : matches;
7001
+ return {
7002
+ skills: shown.map((skill) => ({
7003
+ skillKey: skill.key,
7004
+ title: skill.title,
7005
+ header: skill.header,
7006
+ ...ref.openedSkillKeys.includes(skill.key) ? { alreadyOpen: true } : {}
7007
+ })),
7008
+ total: available.length,
7009
+ 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."
7010
+ };
7011
+ }
7012
+ });
7013
+ tool._deps = deps;
7014
+ tool.__source = "smart-skills";
7015
+ return tool;
7016
+ }
6953
7017
  function createOpenSkillTool(deps) {
6954
7018
  const tool = createTool({
6955
7019
  name: "open_skill",
6956
- description: OPEN_SKILL_DESCRIPTION,
7020
+ description: openSkillDescription(deps.policy),
6957
7021
  schema: zod.z.object({
6958
- skillKey: zod.z.string().min(1).describe("A key from <available_skills>."),
7022
+ skillKey: zod.z.string().min(1).describe(`A key from ${keySource(deps.policy)}.`),
6959
7023
  query: zod.z.string().optional().describe("What you need from the skill \u2014 used to rank a large skill's tools.")
6960
7024
  }).strict(),
6961
7025
  func: async ({ skillKey, query }) => {
6962
7026
  const { registryRef: ref, policy } = tool._deps;
6963
7027
  const skill = findSkill(tool._deps, skillKey);
6964
7028
  if (!skill) {
6965
- return { error: `Unknown skill: ${skillKey}. Choose a key listed in <available_skills>.` };
7029
+ return {
7030
+ error: `Unknown skill: ${skillKey}. ${isSearchDisclosure(policy) ? "Call search_skills to get a valid key." : "Choose a key listed in <available_skills>."}`
7031
+ };
6966
7032
  }
6967
7033
  if (skill.isAvailable && !await skill.isAvailable()) {
6968
7034
  return { error: `Skill ${skillKey} is not available in this workspace.` };
@@ -7051,7 +7117,9 @@ function createBindSkillToolsTool(deps) {
7051
7117
  return tool;
7052
7118
  }
7053
7119
  function createSkillTools(deps) {
7054
- return [createOpenSkillTool(deps), createBindSkillToolsTool(deps)];
7120
+ const tools = [createOpenSkillTool(deps), createBindSkillToolsTool(deps)];
7121
+ if (isSearchDisclosure(deps.policy)) tools.unshift(createSearchSkillsTool(deps));
7122
+ return tools;
7055
7123
  }
7056
7124
 
7057
7125
  // src/smart/subagents/types.ts
@@ -7731,7 +7799,8 @@ function createSmartAgent(opts) {
7731
7799
  if (changed) toolSet.skillRegistryRef.__onToolsChanged?.();
7732
7800
  }
7733
7801
  const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
7734
- const availableSkills = skillsEnabled ? await resolveAvailableSkills(skills, { modelTier: skillPolicy.modelTier }) : [];
7802
+ const catalogDisclosure = skillPolicy.disclosure !== "search";
7803
+ const availableSkills = skillsEnabled && catalogDisclosure ? await resolveAvailableSkills(skills, { modelTier: skillPolicy.modelTier }) : [];
7735
7804
  const skillBlock = availableSkills.length > 0 ? buildSkillHeaderBlock(availableSkills) : void 0;
7736
7805
  let subagentBlock;
7737
7806
  if (subagentsEnabled) {
@@ -11008,6 +11077,7 @@ exports.createParallelTool = createParallelTool;
11008
11077
  exports.createProvider = createProvider;
11009
11078
  exports.createRegexGuardrail = createRegexGuardrail;
11010
11079
  exports.createResolverNode = createResolverNode;
11080
+ exports.createSearchSkillsTool = createSearchSkillsTool;
11011
11081
  exports.createSkillRegistryRef = createSkillRegistryRef;
11012
11082
  exports.createSkillTools = createSkillTools;
11013
11083
  exports.createSmartAgent = createSmartAgent;
@@ -11059,6 +11129,7 @@ exports.resolveUserQuestionState = resolveUserQuestionState;
11059
11129
  exports.restoreSnapshot = restoreSnapshot;
11060
11130
  exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
11061
11131
  exports.scoreItemsByRelevance = scoreItemsByRelevance;
11132
+ exports.searchSkills = searchSkills;
11062
11133
  exports.seedChildMessages = seedChildMessages;
11063
11134
  exports.selectTopIndicesInOrder = selectTopIndicesInOrder;
11064
11135
  exports.setTokenCounter = setTokenCounter;