@cognipeer/agent-sdk 0.7.1 → 0.7.2

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/README.md CHANGED
@@ -36,6 +36,7 @@ Highlights:
36
36
  - **Planning mode** – adaptive system prompt + TODO tool supports full plan writes and version-safe partial updates.
37
37
  - **Unified reasoning surface** – one `reasoning` config controls provider-native reasoning and post-tool plain-text reflections.
38
38
  - **Structured output** – provide a Zod schema and the agent injects a finalize tool to capture JSON deterministically.
39
+ - **Skills / progressive disclosure** – expose cheap capability headers first, then bind skill tools on demand with `open_skill` and `bind_skill_tools`.
39
40
  - **Multi-agent and handoffs** – wrap agents as tools or transfer control mid-run with `asTool` / `asHandoff`.
40
41
  - **Human-in-the-loop** – `needsApproval` tools pause for review; `humanInTheLoop.askUser` enables a built-in `ask_user_question` tool that pauses with a structured multi-choice prompt and resumes from the user's answer.
41
42
  - **Usage + events** – normalize provider usage, surface `tool_call`, `tool_approval`, `user_question`, `plan`, `summarization`, `reflection`, `metadata`, and `handoff` events.
@@ -165,7 +166,33 @@ Tool-response retention is lazy and summarizer-driven:
165
166
  - `criticalTools` are never reduced. The built-in list covers `response`, `manage_todo_list`, and `get_tool_response`.
166
167
  - `maxToolResponseChars` / `maxToolResponseTokens` only drive an eager hard-cap truncation when a single tool output is big enough to blow up the very next model call. The truncated head always points at `get_tool_response` for recovery.
167
168
 
168
- The smart wrapper now supports runtime presets (`fast`, `balanced`, `deep`, `research`), custom profiles layered on top of a base preset, structured summarization, hybrid context compaction, configurable tool-response retention, in-memory fact storage, delegation limits, and an eval harness via `runSmartAgentEvalHarness(...)`.
169
+ The smart wrapper now supports runtime presets (`fast`, `balanced`, `deep`, `research`), custom profiles layered on top of a base preset, structured summarization, hybrid context compaction, skill-based progressive disclosure, configurable tool-response retention, in-memory fact storage, delegation limits, and an eval harness via `runSmartAgentEvalHarness(...)`.
170
+
171
+ For large or optional tool catalogs, pass `skills` instead of binding every tool directly:
172
+
173
+ ```ts
174
+ import { createSmartAgent, type Skill } from "@cognipeer/agent-sdk";
175
+
176
+ const pdfSkill: Skill = {
177
+ key: "builtin:pdf",
178
+ title: "PDF",
179
+ header: "read and summarize PDF files when the user references a PDF",
180
+ prompt: "Use pdf_read before pdf_summarize and preserve file ids in the answer.",
181
+ listToolIndex: () => [
182
+ { name: "pdf_read", description: "Extract PDF text by file id" },
183
+ { name: "pdf_summarize", description: "Summarize extracted PDF text" },
184
+ ],
185
+ bindTools: (names) => bindPdfTools(names),
186
+ };
187
+
188
+ const agent = createSmartAgent({
189
+ model,
190
+ skills: [pdfSkill],
191
+ skillPolicy: { maxOpenSkills: 2, maxBoundToolsPerSkill: 8, maxBoundToolsTotal: 24 },
192
+ });
193
+ ```
194
+
195
+ The model sees the skill header first. It calls `open_skill` to load the skill prompt and either bind a small skill immediately or get a tool index for `bind_skill_tools`.
169
196
 
170
197
  You can also define a custom profile by extending a built-in preset and overriding only the knobs you need:
171
198
 
package/dist/index.cjs CHANGED
@@ -3036,6 +3036,17 @@ function createToolsNode(initialTools, opts) {
3036
3036
  }
3037
3037
  const durationMs = Date.now() - start;
3038
3038
  const executionId = nanoid.nanoid();
3039
+ if (output && typeof output === "object" && Array.isArray(output.__runtimeToolsDelta)) {
3040
+ const delta = output.__runtimeToolsDelta;
3041
+ delete output.__runtimeToolsDelta;
3042
+ const currentRuntime = state.agent || runtime;
3043
+ const currentTools = currentRuntime?.tools ?? [];
3044
+ const present = new Set(currentTools.map((tool2) => tool2?.name));
3045
+ const additions = delta.filter((tool2) => tool2 && tool2.name && !present.has(tool2.name));
3046
+ if (additions.length > 0 && currentRuntime) {
3047
+ state.agent = { ...currentRuntime, tools: [...currentTools, ...additions] };
3048
+ }
3049
+ }
3039
3050
  if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
3040
3051
  toolHistory.push({ executionId, toolName, args, output: "handoff:ok", rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "handoff" });
3041
3052
  push({ role: "tool", content: "ok", tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
@@ -6193,6 +6204,190 @@ async function writeSummaryFactsToMemory(store, config, summary, sourceTurn) {
6193
6204
  return store.get(config.memory.scope, { limit: config.context.lastTurnsToKeep + summary.stable_facts.length });
6194
6205
  }
6195
6206
 
6207
+ // src/smart/skills/types.ts
6208
+ var DEFAULT_SKILL_POLICY = {
6209
+ maxOpenSkills: 4,
6210
+ maxBoundToolsPerSkill: 12,
6211
+ maxBoundToolsTotal: 40
6212
+ };
6213
+ var SMALL_TIER_SKILL_POLICY = {
6214
+ maxOpenSkills: 1,
6215
+ maxBoundToolsPerSkill: 6,
6216
+ maxBoundToolsTotal: 18,
6217
+ modelTier: "small"
6218
+ };
6219
+ function createSkillRegistryRef() {
6220
+ return { openedSkillKeys: [], boundSkillTools: [] };
6221
+ }
6222
+
6223
+ // src/smart/skills/registry.ts
6224
+ function composeToolSets(input) {
6225
+ const without = [...input.base, ...input.boundSkillTools];
6226
+ const withRec = input.recoveryTool ? [...without, input.recoveryTool] : [...without];
6227
+ return { toolsWithoutRecovery: without, toolsWithRecovery: withRec };
6228
+ }
6229
+ function appendBoundTools(ref, newTools, policy) {
6230
+ const present = new Set(ref.boundSkillTools.map((tool) => tool.name));
6231
+ const added = [];
6232
+ for (const tool of newTools) {
6233
+ if (ref.boundSkillTools.length >= policy.maxBoundToolsTotal) break;
6234
+ if (present.has(tool.name)) continue;
6235
+ present.add(tool.name);
6236
+ ref.boundSkillTools.push(tool);
6237
+ added.push(tool);
6238
+ }
6239
+ return added;
6240
+ }
6241
+ function canOpenSkill(ref, policy) {
6242
+ if (ref.openedSkillKeys.length >= policy.maxOpenSkills) {
6243
+ return { ok: false, reason: `max ${policy.maxOpenSkills} skill(s) already open` };
6244
+ }
6245
+ if (ref.boundSkillTools.length >= policy.maxBoundToolsTotal) {
6246
+ return { ok: false, reason: "skill tool budget exhausted" };
6247
+ }
6248
+ return { ok: true };
6249
+ }
6250
+ async function resolveAvailableSkills(skills, opts = {}) {
6251
+ const out = [];
6252
+ for (const skill of skills) {
6253
+ if (skill.minModelTier === "large" && opts.modelTier === "small") continue;
6254
+ const available = skill.isAvailable ? await skill.isAvailable() : true;
6255
+ if (available) out.push(skill);
6256
+ }
6257
+ return out;
6258
+ }
6259
+ function buildSkillHeaderBlock(skills) {
6260
+ if (skills.length === 0) return "";
6261
+ const lines = skills.map((skill) => `- ${skill.key}: ${skill.header}`);
6262
+ return [
6263
+ "<available_skills>",
6264
+ "Open a skill with open_skill(skillKey) to load its tools on demand. Only open what the task needs; close nothing \u2014 opened skills stay available for the rest of the run.",
6265
+ ...lines,
6266
+ "</available_skills>"
6267
+ ].join("\n");
6268
+ }
6269
+ function dedupeToolsByName(tools) {
6270
+ const seen = /* @__PURE__ */ new Set();
6271
+ const out = [];
6272
+ for (const tool of tools) {
6273
+ if (seen.has(tool.name)) continue;
6274
+ seen.add(tool.name);
6275
+ out.push(tool);
6276
+ }
6277
+ return out;
6278
+ }
6279
+
6280
+ // src/smart/skills/skillTools.ts
6281
+ var MAX_RETURNED_INDEX = 40;
6282
+ 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.";
6283
+ 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.";
6284
+ function findSkill(deps, key) {
6285
+ return deps.skills.find((skill) => skill.key === key);
6286
+ }
6287
+ function createOpenSkillTool(deps) {
6288
+ const tool = createTool({
6289
+ name: "open_skill",
6290
+ description: OPEN_SKILL_DESCRIPTION,
6291
+ schema: zod.z.object({
6292
+ skillKey: zod.z.string().min(1).describe("A key from <available_skills>."),
6293
+ query: zod.z.string().optional().describe("What you need from the skill \u2014 used to rank a large skill's tools.")
6294
+ }).strict(),
6295
+ func: async ({ skillKey, query }) => {
6296
+ const { registryRef: ref, policy } = tool._deps;
6297
+ const skill = findSkill(tool._deps, skillKey);
6298
+ if (!skill) {
6299
+ return { error: `Unknown skill: ${skillKey}. Choose a key listed in <available_skills>.` };
6300
+ }
6301
+ if (skill.isAvailable && !await skill.isAvailable()) {
6302
+ return { error: `Skill ${skillKey} is not available in this workspace.` };
6303
+ }
6304
+ const alreadyOpen = ref.openedSkillKeys.includes(skillKey);
6305
+ if (!alreadyOpen) {
6306
+ const guard = canOpenSkill(ref, policy);
6307
+ if (!guard.ok) {
6308
+ return {
6309
+ error: `Cannot open ${skillKey}: ${guard.reason}. Work with the skills already open.`
6310
+ };
6311
+ }
6312
+ ref.openedSkillKeys.push(skillKey);
6313
+ }
6314
+ const prompt = typeof skill.prompt === "function" ? await skill.prompt() : skill.prompt;
6315
+ const index = await skill.listToolIndex();
6316
+ if (index.length <= policy.maxBoundToolsPerSkill) {
6317
+ const bound = await skill.bindTools();
6318
+ const added = appendBoundTools(ref, bound, policy);
6319
+ if (added.length > 0) ref.__onToolsChanged?.();
6320
+ return {
6321
+ skillKey,
6322
+ prompt,
6323
+ needsSandbox: Boolean(skill.needsSandbox),
6324
+ boundTools: added.map((t) => t.name),
6325
+ // Inject the bound tools into the live runtime for this loop.
6326
+ __runtimeToolsDelta: added
6327
+ };
6328
+ }
6329
+ let defaultBound = [];
6330
+ let defaultAdded = [];
6331
+ if ((!query || query.trim().length < 3) && skill.defaultBindNames?.length) {
6332
+ const bound = await skill.bindTools(skill.defaultBindNames.slice(0, policy.maxBoundToolsPerSkill));
6333
+ defaultAdded = appendBoundTools(ref, bound, policy);
6334
+ if (defaultAdded.length > 0) ref.__onToolsChanged?.();
6335
+ defaultBound = defaultAdded.map((t) => t.name);
6336
+ }
6337
+ const headers = query && query.trim().length >= 3 && skill.rankToolHeaders ? await skill.rankToolHeaders(query, index) : index;
6338
+ return {
6339
+ skillKey,
6340
+ prompt,
6341
+ needsSandbox: Boolean(skill.needsSandbox),
6342
+ boundTools: defaultBound,
6343
+ toolIndex: headers.slice(0, MAX_RETURNED_INDEX).map((h) => ({ name: h.name, description: h.description })),
6344
+ hint: "Call bind_skill_tools(skillKey, toolNames) with only the tools you need.",
6345
+ ...defaultAdded.length > 0 ? { __runtimeToolsDelta: defaultAdded } : {}
6346
+ };
6347
+ }
6348
+ });
6349
+ tool._deps = deps;
6350
+ tool.__source = "smart-skills";
6351
+ return tool;
6352
+ }
6353
+ function createBindSkillToolsTool(deps) {
6354
+ const tool = createTool({
6355
+ name: "bind_skill_tools",
6356
+ description: BIND_SKILL_TOOLS_DESCRIPTION,
6357
+ schema: zod.z.object({
6358
+ skillKey: zod.z.string().min(1),
6359
+ toolNames: zod.z.array(zod.z.string().min(1)).min(1).describe("Tool names from the skill's index.")
6360
+ }).strict(),
6361
+ func: async ({ skillKey, toolNames }) => {
6362
+ const { registryRef: ref, policy } = tool._deps;
6363
+ const skill = findSkill(tool._deps, skillKey);
6364
+ if (!skill) {
6365
+ return { error: `Unknown skill: ${skillKey}.` };
6366
+ }
6367
+ if (!ref.openedSkillKeys.includes(skillKey)) {
6368
+ return { error: `Call open_skill("${skillKey}") before binding its tools.` };
6369
+ }
6370
+ const capped = toolNames.slice(0, policy.maxBoundToolsPerSkill);
6371
+ const bound = await skill.bindTools(capped);
6372
+ const added = appendBoundTools(ref, bound, policy);
6373
+ if (added.length > 0) ref.__onToolsChanged?.();
6374
+ return {
6375
+ skillKey,
6376
+ boundTools: added.map((t) => t.name),
6377
+ requested: capped.length,
6378
+ truncated: toolNames.length > capped.length,
6379
+ ...added.length > 0 ? { __runtimeToolsDelta: added } : {}
6380
+ };
6381
+ }
6382
+ });
6383
+ tool._deps = deps;
6384
+ tool.__source = "smart-skills";
6385
+ return tool;
6386
+ }
6387
+ function createSkillTools(deps) {
6388
+ return [createOpenSkillTool(deps), createBindSkillToolsTool(deps)];
6389
+ }
6390
+
6196
6391
  // src/smart/index.ts
6197
6392
  function createSmartAgent(opts) {
6198
6393
  const resolved = normalizeSmartAgentOptions(opts);
@@ -6227,6 +6422,9 @@ function createSmartAgent(opts) {
6227
6422
  if (askUserConfig) {
6228
6423
  factoryToolsWithoutRecovery.push(createAskUserQuestionTool(factoryStateRef, askUserConfig));
6229
6424
  }
6425
+ const skills = opts.skills ?? [];
6426
+ const skillPolicy = opts.skillPolicy ?? DEFAULT_SKILL_POLICY;
6427
+ const skillsEnabled = skills.length > 0;
6230
6428
  const soManagerFactory = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
6231
6429
  const modelCapabilities = opts.outputSchema ? getModelCapabilities(opts.model) : void 0;
6232
6430
  const responseFinalizeTool = soManagerFactory && modelCapabilities?.structuredOutput !== "native" ? soManagerFactory.getResponseTool() : void 0;
@@ -6234,12 +6432,31 @@ function createSmartAgent(opts) {
6234
6432
  const ref = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
6235
6433
  const ctxTools = createContextTools(ref, { planningEnabled, includeGetToolResponse: false });
6236
6434
  const recoveryTool = createGetToolResponseTool(ref);
6237
- const base2 = [...userTools, ...ctxTools];
6238
- if (responseFinalizeTool) base2.push(responseFinalizeTool);
6239
- if (askUserConfig) base2.push(createAskUserQuestionTool(ref, askUserConfig));
6240
- const without = base2;
6241
- const withRec = [...without, recoveryTool];
6242
- return { stateRef: ref, toolsWithoutRecovery: without, toolsWithRecovery: withRec };
6435
+ const coreBase = [...userTools, ...ctxTools];
6436
+ if (responseFinalizeTool) coreBase.push(responseFinalizeTool);
6437
+ if (askUserConfig) coreBase.push(createAskUserQuestionTool(ref, askUserConfig));
6438
+ if (!skillsEnabled) {
6439
+ const without = coreBase;
6440
+ const withRec = [...without, recoveryTool];
6441
+ return { stateRef: ref, toolsWithoutRecovery: without, toolsWithRecovery: withRec };
6442
+ }
6443
+ const toolSet = { stateRef: ref, toolsWithoutRecovery: [], toolsWithRecovery: [] };
6444
+ const skillRegistryRef = createSkillRegistryRef();
6445
+ const skillMetaTools = createSkillTools({ registryRef: skillRegistryRef, skills, policy: skillPolicy });
6446
+ const baseWithMeta = [...coreBase, ...skillMetaTools];
6447
+ const rebuild = () => {
6448
+ const composed = composeToolSets({
6449
+ base: baseWithMeta,
6450
+ boundSkillTools: skillRegistryRef.boundSkillTools,
6451
+ recoveryTool
6452
+ });
6453
+ toolSet.toolsWithoutRecovery = composed.toolsWithoutRecovery;
6454
+ toolSet.toolsWithRecovery = composed.toolsWithRecovery;
6455
+ };
6456
+ skillRegistryRef.__onToolsChanged = rebuild;
6457
+ toolSet.skillRegistryRef = skillRegistryRef;
6458
+ rebuild();
6459
+ return toolSet;
6243
6460
  };
6244
6461
  const base = createAgent({ ...runtimeOpts, tools: factoryToolsWithoutRecovery });
6245
6462
  base.__runtime.runtimeProfile = resolved.runtimeProfile;
@@ -6290,9 +6507,9 @@ function createSmartAgent(opts) {
6290
6507
  planVersion: ref.planVersion || state.planVersion || 1
6291
6508
  };
6292
6509
  }
6293
- function systemMessage2() {
6510
+ function systemMessage2(extraBlock) {
6294
6511
  const sys = buildSystemPrompt(
6295
- [opts.systemPrompt, runtimeHint, structuredOutputHint].filter(Boolean).join("\n"),
6512
+ [opts.systemPrompt, runtimeHint, structuredOutputHint, extraBlock].filter(Boolean).join("\n"),
6296
6513
  planningEnabled,
6297
6514
  opts.name || "Agent",
6298
6515
  opts.todoListPrompt
@@ -6342,8 +6559,10 @@ function createSmartAgent(opts) {
6342
6559
  stateRef.planVersion = input.planVersion || input.plan?.version || 0;
6343
6560
  stateRef.adherenceScore = input.plan?.adherenceScore || 0;
6344
6561
  const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
6562
+ const availableSkills = skillsEnabled ? await resolveAvailableSkills(skills, { modelTier: skillPolicy.modelTier }) : [];
6563
+ const skillBlock = availableSkills.length > 0 ? buildSkillHeaderBlock(availableSkills) : void 0;
6345
6564
  const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
6346
- const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
6565
+ const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(skillBlock), ...input.messages || []];
6347
6566
  let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages, toolSet);
6348
6567
  let lastResult = null;
6349
6568
  let rawMessages = [...seedMessages];
@@ -9129,25 +9348,32 @@ exports.AzureProvider = AzureProvider;
9129
9348
  exports.BaseProvider = BaseProvider;
9130
9349
  exports.BedrockProvider = BedrockProvider;
9131
9350
  exports.DEFAULT_PROFILE_CONFIGS = DEFAULT_PROFILE_CONFIGS;
9351
+ exports.DEFAULT_SKILL_POLICY = DEFAULT_SKILL_POLICY;
9132
9352
  exports.GuardrailPhase = GuardrailPhase;
9133
9353
  exports.InMemoryMemoryStore = InMemoryMemoryStore;
9134
9354
  exports.NativeJsonSchemaStrategy = NativeJsonSchemaStrategy;
9135
9355
  exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
9136
9356
  exports.OpenAIProvider = OpenAIProvider;
9137
9357
  exports.ProviderError = ProviderError;
9358
+ exports.SMALL_TIER_SKILL_POLICY = SMALL_TIER_SKILL_POLICY;
9138
9359
  exports.StructuredOutputManager = StructuredOutputManager;
9139
9360
  exports.ToolBasedStrategy = ToolBasedStrategy;
9140
9361
  exports.VertexProvider = VertexProvider;
9141
9362
  exports.agentVerdictRule = agentVerdictRule;
9363
+ exports.appendBoundTools = appendBoundTools;
9364
+ exports.buildSkillHeaderBlock = buildSkillHeaderBlock;
9142
9365
  exports.buildSystemPrompt = buildSystemPrompt;
9366
+ exports.canOpenSkill = canOpenSkill;
9143
9367
  exports.captureSnapshot = captureSnapshot;
9144
9368
  exports.codePresenceRule = codePresenceRule;
9145
9369
  exports.cognipeerSink = cognipeerSink;
9370
+ exports.composeToolSets = composeToolSets;
9146
9371
  exports.countApproxTokens = countApproxTokens;
9147
9372
  exports.countMessagesTokens = countMessagesTokens;
9148
9373
  exports.createAgent = createAgent;
9149
9374
  exports.createAgentNode = createAgentNode;
9150
9375
  exports.createAskUserQuestionTool = createAskUserQuestionTool;
9376
+ exports.createBindSkillToolsTool = createBindSkillToolsTool;
9151
9377
  exports.createCodeGuardrail = createCodeGuardrail;
9152
9378
  exports.createContextSummarizeNode = createContextSummarizeNode;
9153
9379
  exports.createContextTools = createContextTools;
@@ -9155,15 +9381,19 @@ exports.createGetToolResponseTool = createGetToolResponseTool;
9155
9381
  exports.createGuardrail = createGuardrail;
9156
9382
  exports.createJsonGuardrail = createJsonGuardrail;
9157
9383
  exports.createManageTodoTool = createManageTodoTool;
9384
+ exports.createOpenSkillTool = createOpenSkillTool;
9158
9385
  exports.createProvider = createProvider;
9159
9386
  exports.createRegexGuardrail = createRegexGuardrail;
9160
9387
  exports.createResolverNode = createResolverNode;
9388
+ exports.createSkillRegistryRef = createSkillRegistryRef;
9389
+ exports.createSkillTools = createSkillTools;
9161
9390
  exports.createSmartAgent = createSmartAgent;
9162
9391
  exports.createTool = createTool;
9163
9392
  exports.createToolLimitFinalizeNode = createToolLimitFinalizeNode;
9164
9393
  exports.createToolsNode = createToolsNode;
9165
9394
  exports.customCallbackRule = customCallbackRule;
9166
9395
  exports.customSink = customSink;
9396
+ exports.dedupeToolsByName = dedupeToolsByName;
9167
9397
  exports.defaultTokenCounter = defaultTokenCounter;
9168
9398
  exports.emptyUsage = emptyUsage;
9169
9399
  exports.evaluateGuardrails = evaluateGuardrails;
@@ -9186,6 +9416,7 @@ exports.normalizeSmartAgentOptions = normalizeSmartAgentOptions;
9186
9416
  exports.otlpSink = otlpSink;
9187
9417
  exports.readMemoryFacts = readMemoryFacts;
9188
9418
  exports.regexRule = regexRule;
9419
+ exports.resolveAvailableSkills = resolveAvailableSkills;
9189
9420
  exports.resolveMemoryStore = resolveMemoryStore;
9190
9421
  exports.resolveReasoning = resolveReasoning;
9191
9422
  exports.resolveStrategy = resolveStrategy;