@suveren/gateway 0.24.0 → 0.25.0

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.
@@ -3858,6 +3858,11 @@ function zeroCappedBound(profileId, bounds, actionType) {
3858
3858
  }
3859
3859
  return null;
3860
3860
  }
3861
+ function toolAlwaysListed(tool, isBuiltin) {
3862
+ if (tool.internal || !tool.gating?.profile) return false;
3863
+ if (tool.gating.hideUnlessAuthorized || tool.gating.category === "disabled") return false;
3864
+ return !isBuiltin(tool.integrationId);
3865
+ }
3861
3866
  function toolIsAuthorizedForDisplay(tool, matchingAuths) {
3862
3867
  if (!tool.gating?.hideUnlessAuthorized) return true;
3863
3868
  if (matchingAuths.length === 0) return false;
@@ -4033,7 +4038,7 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
4033
4038
  return {
4034
4039
  content: [{
4035
4040
  type: "text",
4036
- text: refusals.length > 0 ? `Refused: ${reasons.charAt(0).toUpperCase() + reasons.slice(1)} No ticket was requested.` : `No active authorization matching profile "${profile}". A decision owner must grant authority via the Authority UI before this tool can be used.`
4041
+ text: refusals.length > 0 ? `Refused: ${reasons.charAt(0).toUpperCase() + reasons.slice(1)} No ticket was requested.` : noMandateRefusal(tool.namespacedName, profile)
4037
4042
  }],
4038
4043
  isError: true
4039
4044
  };
@@ -4645,20 +4650,21 @@ Proposal ID: ${proposal.id}. Use check-pending-commitments to track status.`
4645
4650
  }
4646
4651
  });
4647
4652
  }
4648
- function buildProxiedToolDescription(tool, state2) {
4653
+ function noMandateRefusal(toolName, profile) {
4654
+ return `Refused: no mandate covers ${toolName} (${shortProfile(profile)}). Nothing was done. Ask the person to grant a ${shortProfile(profile)} mandate in the gateway, then try again. No ticket was requested.`;
4655
+ }
4656
+ function shortProfile(profileId) {
4657
+ const parts = profileId.replace(/@.*$/, "").split("/");
4658
+ return parts[parts.length - 1];
4659
+ }
4660
+ function buildProxiedToolDescription(tool) {
4649
4661
  if (!tool.gating || !tool.gating.profile) {
4650
4662
  return `[Suveren: no gating config] ${tool.description}`;
4651
4663
  }
4652
4664
  const profile = tool.gating.profile;
4653
- const auths = state2.getEnrichedAuthorizations();
4654
- const hasAuth = auths.some(
4655
- (a) => a.complete && profileMatches(a.profileId, profile)
4656
- );
4657
- if (!hasAuth) {
4658
- return `[Suveren: ${profile} \u2014 no active authorization] ${tool.description}`;
4659
- }
4665
+ const condition = `works only under a ${shortProfile(profile)} mandate, check list-authorizations`;
4660
4666
  if (tool.gating.category === "read") {
4661
- return `[Suveren: ${profile} \u2014 read] ${tool.description}`;
4667
+ return `[Suveren: ${profile} \u2014 read; ${condition}] ${tool.description}`;
4662
4668
  }
4663
4669
  const parts = [];
4664
4670
  if (tool.gating.staticExecution?.action_type) {
@@ -4671,7 +4677,7 @@ function buildProxiedToolDescription(tool, state2) {
4671
4677
  parts.push(`${mappedFields.join(", ")} checked`);
4672
4678
  }
4673
4679
  const tag = parts.length > 0 ? parts.join(", ") : "gated";
4674
- return `[Suveren: ${profile} \u2014 ${tag}] ${tool.description}`;
4680
+ return `[Suveren: ${profile} \u2014 ${tag}; ${condition}] ${tool.description}`;
4675
4681
  }
4676
4682
 
4677
4683
  // src/lib/agent-visibility.ts
@@ -4727,7 +4733,8 @@ function buildMandateBrief(opts) {
4727
4733
  "You are connected to Suveren \u2014 the gateway that gates every privileged tool call you make.",
4728
4734
  "Suveren implements the bounded-authority model from the open Human Agency Protocol (HAP).",
4729
4735
  "You have bounded authorities granted by human decision owners.",
4730
- "You MUST stay within these bounds \u2014 the Gatekeeper will reject actions that exceed them."
4736
+ "You MUST stay within these bounds \u2014 the Gatekeeper will reject actions that exceed them.",
4737
+ "Tools are listed even where no mandate covers them yet. If a call is refused because no mandate covers it, tell the person and ask them to grant one \u2014 do not retry it or work around it with another tool."
4731
4738
  ];
4732
4739
  if (opts.asVersionRefusal) {
4733
4740
  lines.push("");
@@ -4823,7 +4830,7 @@ function buildCapabilityMap(profileId, integrationManager2) {
4823
4830
  const readOnly = [];
4824
4831
  const undescribed = [];
4825
4832
  for (const tool of allTools) {
4826
- if (!tool.gating || !tool.gating.profile) {
4833
+ if (tool.internal || !tool.gating || !tool.gating.profile) {
4827
4834
  continue;
4828
4835
  }
4829
4836
  if (!profileMatches(tool.gating.profile, shortName2) && tool.gating.profile !== profileId) {
@@ -4864,6 +4871,20 @@ function buildCapabilityMap(profileId, integrationManager2) {
4864
4871
  }
4865
4872
  return lines.join("\n");
4866
4873
  }
4874
+ function listedWithoutMandate(authorizations, integrationManager2) {
4875
+ if (!integrationManager2) return [];
4876
+ const isBuiltin = (id) => integrationManager2.isBuiltin?.(id) ?? false;
4877
+ const byProfile = /* @__PURE__ */ new Map();
4878
+ for (const tool of integrationManager2.getAllTools()) {
4879
+ if (!toolAlwaysListed(tool, isBuiltin)) continue;
4880
+ const profile = tool.gating.profile;
4881
+ if (authorizations.some((a) => a.complete && profileMatches(a.profileId, profile))) continue;
4882
+ const short = shortProfileName2(profile);
4883
+ byProfile.set(short, [...byProfile.get(short) ?? [], tool.namespacedName]);
4884
+ }
4885
+ return [...byProfile.entries()].map(([profile, tools]) => ` ${profile}: ${tools.join(", ")}`);
4886
+ }
4887
+ var LISTED_WITHOUT_MANDATE_HEADING = "Listed but not usable yet \u2014 no mandate covers these; every call is refused until the person grants one:";
4867
4888
  function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
4868
4889
  return async (args) => {
4869
4890
  if (!state2.spClient.isUnlocked()) {
@@ -4872,13 +4893,11 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
4872
4893
  const authorizations = agentVisibleAuthorizations(state2.getEnrichedAuthorizations(), integrationManager2);
4873
4894
  const now = Math.floor(Date.now() / 1e3);
4874
4895
  const domain = args?.domain;
4896
+ const notYet = listedWithoutMandate(authorizations, integrationManager2);
4875
4897
  if (authorizations.length === 0) {
4876
- return {
4877
- content: [{
4878
- type: "text",
4879
- text: "No active authorizations. A decision owner must grant authority via the Authority UI."
4880
- }]
4881
- };
4898
+ const text = ["No active authorizations \u2014 no tool is usable yet. Ask the person to grant a mandate in the gateway."];
4899
+ if (notYet.length > 0) text.push("", LISTED_WITHOUT_MANDATE_HEADING, ...notYet);
4900
+ return { content: [{ type: "text", text: text.join("\n") }] };
4882
4901
  }
4883
4902
  if (domain) {
4884
4903
  const matching = authorizations.filter((auth) => {
@@ -4999,7 +5018,7 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
4999
5018
  output.push("");
5000
5019
  }
5001
5020
  if (active.length > 0) {
5002
- output.push("Active authorizations:");
5021
+ output.push("Active authorizations (usable now):");
5003
5022
  output.push(...active);
5004
5023
  }
5005
5024
  if (pending.length > 0) {
@@ -5007,6 +5026,11 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
5007
5026
  output.push("Pending (missing owners):");
5008
5027
  output.push(...pending);
5009
5028
  }
5029
+ if (notYet.length > 0) {
5030
+ if (output.length > 0) output.push("");
5031
+ output.push(LISTED_WITHOUT_MANDATE_HEADING);
5032
+ output.push(...notYet);
5033
+ }
5010
5034
  return {
5011
5035
  content: [{
5012
5036
  type: "text",
@@ -5623,7 +5647,7 @@ function createMcpServer(state2, integrationManager2) {
5623
5647
  }
5624
5648
  const handler = createGatedToolHandler(tool, integrationManager2, state2);
5625
5649
  const zodShape = jsonSchemaToZodShape(tool.inputSchema);
5626
- const description = buildProxiedToolDescription(tool, state2);
5650
+ const description = buildProxiedToolDescription(tool);
5627
5651
  try {
5628
5652
  const registered = server.registerTool(
5629
5653
  tool.namespacedName,
@@ -5641,10 +5665,11 @@ function createMcpServer(state2, integrationManager2) {
5641
5665
  }
5642
5666
  function refreshTools() {
5643
5667
  const auths = state2.getEnrichedAuthorizations();
5668
+ const isBuiltin = (id) => integrationManager2?.isBuiltin?.(id) ?? false;
5644
5669
  for (const [, { tool, registered }] of proxiedTools) {
5645
- const description = buildProxiedToolDescription(tool, state2);
5646
- registered.update({ description });
5647
- if (tool.gating?.profile) {
5670
+ if (toolAlwaysListed(tool, isBuiltin)) {
5671
+ registered.enable();
5672
+ } else if (tool.gating?.profile) {
5648
5673
  const matchingAuths = auths.filter(
5649
5674
  (a) => a.complete && profileMatches(a.profileId, tool.gating.profile)
5650
5675
  );