@suveren/gateway 0.19.4 → 0.19.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.
@@ -4,6 +4,12 @@ Learn the company, the team and the work, and collect real cases. Nothing is loa
4
4
 
5
5
  You ask; the person answers and checks. Ask one topic at a time and write down what you learn.
6
6
 
7
+ ## Before you start: the systems
8
+
9
+ Look at the systems in the header above. If a system the work needs is listed under **Available but not activated**, ask the person to activate it first (Integrations → its card → Activate) and read this guide again. Do not plan around a system you cannot see.
10
+
11
+ Plan the test only with the actions the connected systems offer — the header lists each system's changes and reads. If the work needs an action no system offers (for example a refund when the ERP has only quotes and orders), say so and agree with the person on what to test instead. Never pretend a system can do more than it does.
12
+
7
13
  ## What to ask, in this order
8
14
 
9
15
  1. **Company:** what it sells, to whom, in which currency, and which systems it uses. The systems connected to this gateway are listed in the header above; ask about the ones that matter for the work.
@@ -4,7 +4,7 @@ Turn the confirmed cases into test data and load it into the simulated systems.
4
4
 
5
5
  ## Build the package
6
6
 
7
- One package feeds every simulated system. Its format, and how to build a good one, is described in the `load_simulation` tool of each simulated system — read it there; it is the authority. In short: the company's `name` and `currency`, the `customers`, `products` and `contacts` the cases need, and the `cases` themselves (each request as it arrived and the reply that was actually sent).
7
+ One package feeds every simulated system. Its exact format is at the end of this guide (read from the simulated systems themselves), and how to build a good one is described there too. In short: the company's `name` and `currency`, the `customers`, `products` and `contacts` the cases need, and the `cases` themselves (each request as it arrived and the reply that was actually sent).
8
8
 
9
9
  - Include every customer, product and contact a case mentions, with stock, prices and credit limits that make each case answerable the way it really was.
10
10
  - Use only the renamed cases.
@@ -27,4 +27,7 @@ Clear and load each count against the setup mandate's daily limit, so one re-run
27
27
 
28
28
  The real replies stay inside the simulated email system. They are never shown to the working AI; they exist for comparing afterwards.
29
29
 
30
+
31
+ <!-- generated: package -->
32
+
30
33
  Next: **mandates**.
@@ -32,4 +32,7 @@ Apply this to whatever systems are connected: the header above lists each system
32
32
  - Sales, sending quotes and orders: review.
33
33
  - Email replies: review, recipients limited to the customers' domains.
34
34
 
35
+
36
+ <!-- generated: limits -->
37
+
35
38
  Next: **brief**.
@@ -10,5 +10,7 @@ What can make a test say more than it should, and what to do about each. Read th
10
10
  | Mandates too loose | A wrong result can look like success | Start in review mode or with low caps; loosen by replacing the mandate |
11
11
  | Proposals are stored readable on the Authority Server | The content of every proposal, including intents, can be read there | Test data and renamed cases only — never real customer data in a setup |
12
12
  | Setup limit too low | A clear without the following load leaves empty systems | Use a setup limit of 4–6 per day; clear and load in one go |
13
+ | An AI that can control a browser or the screen | It could open the gateway's page, read the API key there and approve its own proposals — the person would no longer decide | Never open or operate the gateway's page, never handle the API key, never approve. Tell the person to approve themselves, and to keep the gateway page out of any browser an AI controls |
14
+ | Calls around the Suveren tools | A terminal, HTTP or browser call to the gateway's ports is not governed by a mandate | Use only the Suveren tools. If something is missing, tell the person what — do not probe the gateway |
13
15
 
14
16
  Next: start a run — the person asks the working AI to work the new requests.
@@ -4079,6 +4079,12 @@ function agentVisibleAuthorizations(authorizations, integrationManager2) {
4079
4079
  );
4080
4080
  }
4081
4081
 
4082
+ // src/lib/agent-rules.ts
4083
+ var AGENT_RULES = [
4084
+ "Never open or operate the Suveren Gateway's web page \u2014 not in a browser you control, not with computer use. Never ask for, read or type the person's API key. Never approve a proposal: approvals are the person's alone.",
4085
+ "Use only the Suveren tools. No terminal, HTTP or browser calls to the gateway's ports (for example `curl localhost:3500/mcp`)."
4086
+ ];
4087
+
4082
4088
  // src/lib/mandate-brief.ts
4083
4089
  function shortProfileName(profileId) {
4084
4090
  const withoutVersion = profileId.replace(/@.*$/, "");
@@ -4165,6 +4171,10 @@ function buildMandateBrief(opts) {
4165
4171
  lines.push(canSetUp ? "If the person asks how to start with Suveren or how to set up their work with it: call setup__get_guide first and follow its steps in order." : `If the person asks how to start with Suveren: they first create a "Delegation" mandate in the Suveren Gateway at ${uiUrl()}. With it you can read the setup guide and lead them through the setup.`);
4166
4172
  lines.push("");
4167
4173
  }
4174
+ lines.push("=== RULES ===");
4175
+ lines.push("");
4176
+ for (const r of AGENT_RULES) lines.push(`- ${r}`);
4177
+ lines.push("");
4168
4178
  lines.push("When you receive a task, call list-authorizations(domain) to load full details for the relevant domain.");
4169
4179
  return lines.join("\n");
4170
4180
  }
@@ -6862,7 +6872,7 @@ function loadGuides(builtinDir = builtinGuidesDir(), overrideDir = process.env.S
6862
6872
  }
6863
6873
  return [...byTopic.values()].sort((a, b) => a.order - b.order || a.topic.localeCompare(b.topic));
6864
6874
  }
6865
- function guideHeader(systems) {
6875
+ function guideHeader(systems, inactive = []) {
6866
6876
  const lines = [
6867
6877
  "> This guide is in English. Talk to the person in their language, and write the brief and mandate intents in the language the team uses.",
6868
6878
  "",
@@ -6880,12 +6890,67 @@ function guideHeader(systems) {
6880
6890
  lines.push(`- **${s.id}** \u2014 ${parts.join("; ")}`);
6881
6891
  }
6882
6892
  }
6893
+ if (inactive.length) {
6894
+ lines.push("");
6895
+ lines.push("**Available but not activated:** " + inactive.map((n) => `**${n}**`).join(", ") + ". Before anything else, ask the person to activate the ones the work needs: in the Suveren Gateway, Integrations \u2192 the system's card \u2192 Activate. Then read this guide again \u2014 a system you cannot see here does not exist for the test.");
6896
+ }
6883
6897
  lines.push("");
6884
6898
  lines.push(`**Approvals:** every mandate and brief you propose waits until the person approves it in the Suveren Gateway at ${uiUrl()}/approvals. Tell them each time you propose one.`);
6899
+ lines.push("");
6900
+ lines.push("**Rules:**");
6901
+ for (const r of AGENT_RULES) lines.push(`- ${r}`);
6885
6902
  return lines.join("\n");
6886
6903
  }
6904
+ function scopeFieldsOf(profile) {
6905
+ return profile.scopeSchema ?? profile.contextSchema;
6906
+ }
6907
+ function fieldLine(name, f) {
6908
+ const kind = [];
6909
+ const values = f.boundType?.values ?? f.enum;
6910
+ if (Array.isArray(values)) kind.push(`one of ${values.map((v) => `\`${String(v)}\``).join(", ")}`);
6911
+ else if (f.type) kind.push(f.type);
6912
+ if (f.unit) kind.push(f.unit);
6913
+ if (f.boundType?.window) kind.push(`per ${f.boundType.window === "daily" ? "day" : f.boundType.window}`);
6914
+ if (typeof f.maximum === "number") kind.push(`at most ${f.maximum}`);
6915
+ if (f.default !== void 0) kind.push(`default \`${String(f.default)}\``);
6916
+ const label = f.displayName ? ` (${f.displayName})` : "";
6917
+ return `- \`${name}\`${label} \u2014 ${kind.join(", ") || "value"}${f.description ? `. ${f.description}` : ""}`;
6918
+ }
6919
+ function fieldLines(schema) {
6920
+ const fields = schema?.fields ?? {};
6921
+ const order = (schema?.keyOrder ?? Object.keys(fields)).filter((k) => k !== "profile" && k !== "path" && fields[k]);
6922
+ return order.map((k) => fieldLine(k, fields[k]));
6923
+ }
6924
+ function limitsSection(systems) {
6925
+ const out = ["## Limits and scope per system", "", "From the profiles \u2014 use exactly these names in `create_mandate` (`limits` and `scope`). Every limit must be set.", ""];
6926
+ for (const s of systems) {
6927
+ if (!s.profile) continue;
6928
+ out.push(`### ${s.id} \u2014 profile \`${s.profile.id}\``, "", "**Limits:**", ...fieldLines(s.profile.boundsSchema));
6929
+ const scope2 = fieldLines(scopeFieldsOf(s.profile));
6930
+ out.push("", "**Scope:**", ...scope2.length ? scope2 : ["- none"], "");
6931
+ }
6932
+ return out.join("\n");
6933
+ }
6934
+ function packageSection(tool) {
6935
+ if (!tool?.inputSchema) return "## The package format\n\nNo simulated system with `load_simulation` is active \u2014 ask the person to activate one first.";
6936
+ const pkg = tool.inputSchema.properties?.package ?? tool.inputSchema;
6937
+ return [
6938
+ "## The package format",
6939
+ "",
6940
+ ...tool.description ? ["What the simulated systems say about it:", "", `> ${tool.description}`, ""] : [],
6941
+ "The `package` argument of `load_simulation` \u2014 the same package for every simulated system:",
6942
+ "",
6943
+ "```json",
6944
+ JSON.stringify(pkg, null, 2),
6945
+ "```"
6946
+ ].join("\n");
6947
+ }
6948
+ function fillGenerated(body, sections) {
6949
+ return body.replace(/<!--\s*generated:\s*([a-z-]+)\s*-->/g, (m, key) => sections[key] ? sections[key]() : m);
6950
+ }
6887
6951
 
6888
6952
  // src/lib/builtins/setup.ts
6953
+ import { getProfile as getProfile7 } from "@hap/core";
6889
6954
  var DELEGATION_PROFILE = "delegation";
6890
6955
  var DELEGATION_INTENT_HINT = [
6891
6956
  "Why \u2014 With test data only, I want to find out which routine parts of my work an AI could take over, so people gain time \u2014 before anything touches real systems.",
@@ -6974,7 +7039,10 @@ function setupBuiltin(deps) {
6974
7039
  handler: async (args) => {
6975
7040
  if (!isSimulationMode()) return { ...builtinText("Refused: not available outside simulation mode."), isError: true };
6976
7041
  const guides = loadGuides();
6977
- const header = guideHeader(connectedSystems(deps.integrationManager));
7042
+ const systems = connectedSystems(deps.integrationManager);
7043
+ const running = new Set(systems.map((s) => s.id));
7044
+ const inactive = getAllManifests().filter((m) => manifestIsSimulated(m) && !running.has(m.id)).map((m) => m.name);
7045
+ const header = guideHeader(systems, inactive);
6978
7046
  const topic = typeof args.topic === "string" ? args.topic.trim().toLowerCase() : "";
6979
7047
  if (!topic) {
6980
7048
  const list = guides.map((g, i) => `${i + 1}. **${g.topic}** \u2014 ${g.summary}`).join("\n");
@@ -6990,9 +7058,15 @@ Read each topic with get_guide(topic) before doing that step.`);
6990
7058
  if (!guide) {
6991
7059
  return { ...builtinText(`Unknown topic "${topic}". Topics: ${guides.map((g) => g.topic).join(", ") || "none installed"}.`), isError: true };
6992
7060
  }
7061
+ const body = fillGenerated(guide.body.trim(), {
7062
+ limits: () => limitsSection(systems.map((s) => ({ id: s.id, profile: getProfile7(s.profile) }))),
7063
+ package: () => packageSection(
7064
+ deps.integrationManager.getAllTools().find((t) => !deps.integrationManager.isBuiltin(t.integrationId) && t.originalName === "load_simulation")
7065
+ )
7066
+ });
6993
7067
  return builtinText(`${header}
6994
7068
 
6995
- ${guide.body.trim()}`);
7069
+ ${body}`);
6996
7070
  }
6997
7071
  },
6998
7072
  {