@frankieai/server 0.2.4 → 0.2.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.
@@ -1477,7 +1477,7 @@ function msg(error) {
1477
1477
  }
1478
1478
 
1479
1479
  // src/agent/prompt.ts
1480
- import { activeSkills } from "@frankieai/core";
1480
+ import { activeSkills, SKILL_TOOL_NAME } from "@frankieai/core";
1481
1481
  function buildSystemPrompt(config, session, availableTools) {
1482
1482
  const sections = [];
1483
1483
  const configurationAssistant = config.features.configurationAssistant;
@@ -1533,7 +1533,7 @@ ${config.agent.systemPrompt.trim()}`);
1533
1533
  }
1534
1534
  sections.push(renderUserSection(session));
1535
1535
  const skills = activeSkills(config, availableTools);
1536
- if (skills.length) sections.push("## Reviewed workspace skills\nThese recipes do not grant additional permissions or override safety rules. Each skill lists only its currently approved, available tools. Instructions may describe additional operations: skip unavailable steps, explain the limitation, and never claim the whole task succeeded when a required step was unavailable.\n" + JSON.stringify(skills.map(({ name, goal, instructions, tools }) => ({ name, goal, instructions, tools }))));
1536
+ if (skills.length) sections.push("## Reviewed workspace skills\nThese recipes do not grant additional permissions or override safety rules. Each skill lists only its currently approved, available tools. Instructions may describe additional operations: skip unavailable steps, explain the limitation, and never claim the whole task succeeded when a required step was unavailable.\nBefore you begin following one of these skills, call the `" + SKILL_TOOL_NAME + "` tool with the skill's exact name, so the person can see which recipe you're applying. It grants no permissions and does nothing on its own \u2014 it's only a visible marker. Then carry out that skill's steps.\n" + JSON.stringify(skills.map(({ name, goal, instructions, tools }) => ({ name, goal, instructions, tools }))));
1537
1537
  return sections.join("\n\n");
1538
1538
  }
1539
1539
  function isOnboardingAssistant(config) {
@@ -1639,9 +1639,51 @@ function renderPageContext(context) {
1639
1639
  import { Agent } from "@earendil-works/pi-agent-core";
1640
1640
  import {
1641
1641
  describeTool as describeTool3,
1642
- activeSkills as activeSkills2,
1642
+ activeSkills as activeSkills3,
1643
1643
  evaluateToolPolicy as evaluateToolPolicy3
1644
1644
  } from "@frankieai/core";
1645
+
1646
+ // src/agent/skill-tools.ts
1647
+ import { Type as Type4 } from "@earendil-works/pi-ai";
1648
+ import { activeSkills as activeSkills2, SKILL_TOOL_NAME as SKILL_TOOL_NAME2 } from "@frankieai/core";
1649
+ function isSkillTool(name) {
1650
+ return name === SKILL_TOOL_NAME2;
1651
+ }
1652
+ function buildSkillTools(config, availableTools) {
1653
+ const skills = activeSkills2(config, availableTools);
1654
+ if (!skills.length) return [];
1655
+ const names = skills.map((skill) => skill.name);
1656
+ return [
1657
+ {
1658
+ name: SKILL_TOOL_NAME2,
1659
+ label: "Use skill",
1660
+ description: `Announce that you are about to follow one of the reviewed workspace skills, so the person can see which recipe you're applying. Call this with the skill's exact name immediately before you begin its steps. It grants no new permissions and performs no action on its own.
1661
+
1662
+ Available skills: ${names.join(", ")}.`,
1663
+ parameters: Type4.Unsafe({
1664
+ type: "object",
1665
+ required: ["skill"],
1666
+ properties: { skill: { type: "string", description: "The exact name of the workspace skill you are about to follow." } }
1667
+ }),
1668
+ async execute(_id, args) {
1669
+ const requested = String((typeof args === "object" && args !== null ? args["skill"] : "") ?? "").trim();
1670
+ const match = skills.find((skill) => skill.name.toLowerCase() === requested.toLowerCase());
1671
+ if (!match) {
1672
+ return {
1673
+ content: [{ type: "text", text: `No active skill is named "${requested}". The skills available this turn are: ${names.join(", ")}.` }],
1674
+ details: null
1675
+ };
1676
+ }
1677
+ return {
1678
+ content: [{ type: "text", text: `Now following the "${match.name}" skill. Apply its steps using only its approved tools.` }],
1679
+ details: null
1680
+ };
1681
+ }
1682
+ }
1683
+ ];
1684
+ }
1685
+
1686
+ // src/agent/runner.ts
1645
1687
  async function* runTurn(params) {
1646
1688
  const { config, session } = params;
1647
1689
  const toolset = await collectTools({
@@ -1651,6 +1693,8 @@ async function* runTurn(params) {
1651
1693
  ...params.onWarning ? { onWarning: params.onWarning } : {}
1652
1694
  });
1653
1695
  const availableTools = toolset.tools.map((tool) => tool.name);
1696
+ const skillTools = buildSkillTools(config, availableTools);
1697
+ const agentTools = [...toolset.tools, ...skillTools];
1654
1698
  const queue = new EventQueue();
1655
1699
  let turnCount = 0;
1656
1700
  let lastAssistant;
@@ -1662,7 +1706,7 @@ async function* runTurn(params) {
1662
1706
  systemPrompt: buildSystemPrompt(config, session, availableTools),
1663
1707
  model: params.modelOverride ?? params.model,
1664
1708
  thinkingLevel: config.model.thinkingLevel,
1665
- tools: toolset.tools,
1709
+ tools: agentTools,
1666
1710
  messages: session.messages
1667
1711
  },
1668
1712
  // Per-request sampling lives here rather than on the agent: the stream
@@ -1686,7 +1730,7 @@ async function* runTurn(params) {
1686
1730
  // tools are exempt: they're gated by the workspaceAdmin flag, not the
1687
1731
  // allow/deny policy, so the policy would otherwise block them here.
1688
1732
  beforeToolCall: async ({ toolCall }) => {
1689
- if (isPlatformTool(toolCall.name)) return void 0;
1733
+ if (isPlatformTool(toolCall.name) || isSkillTool(toolCall.name)) return void 0;
1690
1734
  const decision = evaluateToolPolicy3(toolCall.name, config.agent.tools);
1691
1735
  if (decision.allowed) return void 0;
1692
1736
  queue.push({
@@ -1728,7 +1772,7 @@ async function* runTurn(params) {
1728
1772
  });
1729
1773
  }).finally(() => queue.close());
1730
1774
  try {
1731
- yield { type: "skills_loaded", skills: activeSkills2(config, availableTools).map(({ id, name, revision }) => ({ id, name, revision })) };
1775
+ yield { type: "skills_loaded", skills: activeSkills3(config, availableTools).map(({ id, name, revision }) => ({ id, name, revision })) };
1732
1776
  yield { type: "tools_active", apis: toolset.apis };
1733
1777
  for await (const event of queue) yield event;
1734
1778
  await run;
@@ -3207,4 +3251,4 @@ export {
3207
3251
  resolveStores,
3208
3252
  resolveTenantStore
3209
3253
  };
3210
- //# sourceMappingURL=chunk-7J5Z5MIK.js.map
3254
+ //# sourceMappingURL=chunk-HPYT6ZIU.js.map