@frankieai/core 0.1.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.
- package/dist/index.cjs +271 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +377 -0
- package/dist/index.d.ts +377 -0
- package/dist/index.js +230 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
agentConfigSchema: () => agentConfigSchema,
|
|
24
|
+
apiSourceSchema: () => apiSourceSchema,
|
|
25
|
+
authConfigSchema: () => authConfigSchema,
|
|
26
|
+
brandingConfigSchema: () => brandingConfigSchema,
|
|
27
|
+
defineFrankieConfig: () => defineFrankieConfig,
|
|
28
|
+
describeTool: () => describeTool,
|
|
29
|
+
evaluateToolPolicy: () => evaluateToolPolicy,
|
|
30
|
+
frankieConfigSchema: () => frankieConfigSchema,
|
|
31
|
+
limitsConfigSchema: () => limitsConfigSchema,
|
|
32
|
+
mcpServerSchema: () => mcpServerSchema,
|
|
33
|
+
modelConfigSchema: () => modelConfigSchema,
|
|
34
|
+
parseFrankieConfig: () => parseFrankieConfig,
|
|
35
|
+
thinkingLevelSchema: () => thinkingLevelSchema,
|
|
36
|
+
toolPolicySchema: () => toolPolicySchema,
|
|
37
|
+
widgetThemeSchema: () => widgetThemeSchema
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/config.ts
|
|
42
|
+
var import_zod = require("zod");
|
|
43
|
+
var widgetThemeSchema = import_zod.z.object({
|
|
44
|
+
primary: import_zod.z.string().optional(),
|
|
45
|
+
primaryForeground: import_zod.z.string().optional(),
|
|
46
|
+
background: import_zod.z.string().optional(),
|
|
47
|
+
foreground: import_zod.z.string().optional(),
|
|
48
|
+
muted: import_zod.z.string().optional(),
|
|
49
|
+
border: import_zod.z.string().optional(),
|
|
50
|
+
radius: import_zod.z.string().optional(),
|
|
51
|
+
fontFamily: import_zod.z.string().optional(),
|
|
52
|
+
mode: import_zod.z.enum(["light", "dark", "system"]).default("system")
|
|
53
|
+
}).prefault({});
|
|
54
|
+
var thinkingLevelSchema = import_zod.z.enum(["minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
55
|
+
var modelConfigSchema = import_zod.z.object({
|
|
56
|
+
/**
|
|
57
|
+
* Any provider the model layer knows about. `fireworks` is the default;
|
|
58
|
+
* `anthropic`, `openai`, `google`, `groq`, `together`, `openrouter` and
|
|
59
|
+
* ~30 others resolve too, which is what makes the model a config choice.
|
|
60
|
+
*/
|
|
61
|
+
provider: import_zod.z.string().min(1).default("fireworks"),
|
|
62
|
+
/**
|
|
63
|
+
* Model id within that provider. The default is Kimi K2.7 on Fireworks,
|
|
64
|
+
* which serves a native Anthropic-Messages endpoint and a 262K window.
|
|
65
|
+
*/
|
|
66
|
+
model: import_zod.z.string().default("accounts/fireworks/models/kimi-k2p7-code"),
|
|
67
|
+
/** Env var holding the provider key. Never put the key in config itself. */
|
|
68
|
+
apiKeyEnv: import_zod.z.string().default("FIREWORKS_API_KEY"),
|
|
69
|
+
/** Override the provider's base URL (self-hosted gateways, tests). */
|
|
70
|
+
baseUrl: import_zod.z.string().url().optional(),
|
|
71
|
+
maxTokens: import_zod.z.number().int().positive().max(2e5).default(8192),
|
|
72
|
+
temperature: import_zod.z.number().min(0).max(2).default(0.6),
|
|
73
|
+
/**
|
|
74
|
+
* Reasoning depth. Lowest by default: an onboarding widget is latency-
|
|
75
|
+
* sensitive and its questions rarely need deliberation.
|
|
76
|
+
*/
|
|
77
|
+
thinkingLevel: thinkingLevelSchema.default("minimal"),
|
|
78
|
+
/** Forward the model's reasoning stream to the client as thinking deltas. */
|
|
79
|
+
streamReasoning: import_zod.z.boolean().default(false)
|
|
80
|
+
});
|
|
81
|
+
var mcpServerSchema = import_zod.z.object({
|
|
82
|
+
name: import_zod.z.string().min(1).regex(/^[a-z0-9][a-z0-9_-]*$/i, "MCP server names must be alphanumeric with - or _"),
|
|
83
|
+
/** `http` covers streamable HTTP; `sse` is the legacy transport. */
|
|
84
|
+
transport: import_zod.z.enum(["http", "sse"]).default("http"),
|
|
85
|
+
url: import_zod.z.string().url(),
|
|
86
|
+
/** Static headers, e.g. a service token for the company's own MCP server. */
|
|
87
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).default({}),
|
|
88
|
+
/**
|
|
89
|
+
* When set, the verified end-user JWT is forwarded to the MCP server in this
|
|
90
|
+
* header so the company's tools can act as that user. Use with care: only
|
|
91
|
+
* enable it for MCP servers you control.
|
|
92
|
+
*/
|
|
93
|
+
forwardUserTokenAs: import_zod.z.string().optional(),
|
|
94
|
+
/** Extra claims from the user token to forward as `x-frankie-<claim>`. */
|
|
95
|
+
forwardUserClaims: import_zod.z.array(import_zod.z.string()).default([])
|
|
96
|
+
});
|
|
97
|
+
var apiSourceSchema = import_zod.z.object({
|
|
98
|
+
name: import_zod.z.string().min(1).regex(/^[a-z0-9][a-z0-9_-]*$/i, "API source names must be alphanumeric with - or _"),
|
|
99
|
+
/** URL or local path to an OpenAPI 3.x document (JSON or YAML). */
|
|
100
|
+
spec: import_zod.z.string().min(1),
|
|
101
|
+
/** Overrides the spec's own `servers[0].url`. Required if the spec has none. */
|
|
102
|
+
baseUrl: import_zod.z.string().url().optional(),
|
|
103
|
+
/**
|
|
104
|
+
* Operation ids the agent may call. Empty means "every operation that passes
|
|
105
|
+
* the other filters" — fine for a small internal API, risky for a large one.
|
|
106
|
+
*/
|
|
107
|
+
operations: import_zod.z.array(import_zod.z.string()).default([]),
|
|
108
|
+
/**
|
|
109
|
+
* Path globs to include, e.g. `/projects/**`. Empty means all paths.
|
|
110
|
+
*/
|
|
111
|
+
includePaths: import_zod.z.array(import_zod.z.string()).default([]),
|
|
112
|
+
/**
|
|
113
|
+
* HTTP methods the agent may use. Read-only by default: an onboarding agent
|
|
114
|
+
* wired to a whole API should not be able to DELETE on its first day. Widen
|
|
115
|
+
* deliberately, per API.
|
|
116
|
+
*/
|
|
117
|
+
methods: import_zod.z.array(import_zod.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"])).default(["GET"]),
|
|
118
|
+
/** Static headers, e.g. a service token. */
|
|
119
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).default({}),
|
|
120
|
+
/**
|
|
121
|
+
* When set, the verified end-user JWT is forwarded in this header, so your
|
|
122
|
+
* API authorises the call exactly as it would any other request from them.
|
|
123
|
+
*/
|
|
124
|
+
forwardUserTokenAs: import_zod.z.string().optional(),
|
|
125
|
+
/** Extra claims from the user token to forward as `x-frankie-<claim>`. */
|
|
126
|
+
forwardUserClaims: import_zod.z.array(import_zod.z.string()).default([]),
|
|
127
|
+
/** Per-call timeout. */
|
|
128
|
+
timeoutMs: import_zod.z.number().int().positive().default(3e4),
|
|
129
|
+
/** Response bodies longer than this are truncated before the model sees them. */
|
|
130
|
+
maxResponseChars: import_zod.z.number().int().positive().default(8e3)
|
|
131
|
+
});
|
|
132
|
+
var toolPolicySchema = import_zod.z.object({
|
|
133
|
+
/**
|
|
134
|
+
* Tool names the agent may call without asking. MCP tools are named
|
|
135
|
+
* `mcp__<server>__<tool>` and generated API tools `api__<name>__<operation>`;
|
|
136
|
+
* naming the prefix alone (`mcp__acme`, `api__acme`) allows all of them.
|
|
137
|
+
*/
|
|
138
|
+
allow: import_zod.z.array(import_zod.z.string()).default([]),
|
|
139
|
+
/** Explicit denials; evaluated before `allow`. */
|
|
140
|
+
deny: import_zod.z.array(import_zod.z.string()).default([]),
|
|
141
|
+
/**
|
|
142
|
+
* Deny anything not in `allow`. On by default — an onboarding agent living
|
|
143
|
+
* on a public marketing page should not have an open tool surface.
|
|
144
|
+
*/
|
|
145
|
+
denyByDefault: import_zod.z.boolean().default(true)
|
|
146
|
+
});
|
|
147
|
+
var agentConfigSchema = import_zod.z.object({
|
|
148
|
+
/** Prepended to the runtime's own operating instructions. */
|
|
149
|
+
systemPrompt: import_zod.z.string().default(""),
|
|
150
|
+
/** First message the widget shows before the user types anything. */
|
|
151
|
+
greeting: import_zod.z.string().default("Hi! I can walk you through getting set up. What are you trying to do?"),
|
|
152
|
+
suggestedPrompts: import_zod.z.array(import_zod.z.string()).max(6).default([]),
|
|
153
|
+
maxTurns: import_zod.z.number().int().positive().max(50).default(12),
|
|
154
|
+
/** Hard stop on estimated spend per session. */
|
|
155
|
+
maxBudgetUsd: import_zod.z.number().positive().optional(),
|
|
156
|
+
tools: toolPolicySchema.prefault({}),
|
|
157
|
+
/** Show tool activity in the transcript. */
|
|
158
|
+
showToolActivity: import_zod.z.boolean().default(true)
|
|
159
|
+
});
|
|
160
|
+
var authConfigSchema = import_zod.z.object({
|
|
161
|
+
/**
|
|
162
|
+
* Safe to ship in client bundles. Identifies the tenant; it authorises
|
|
163
|
+
* nothing on its own — the origin allowlist and the user token do that.
|
|
164
|
+
*/
|
|
165
|
+
publishableKey: import_zod.z.string().min(8),
|
|
166
|
+
/** Exact origins allowed to open sessions, e.g. `https://app.acme.com`. */
|
|
167
|
+
allowedOrigins: import_zod.z.array(import_zod.z.string()).default([]),
|
|
168
|
+
/** Let visitors chat without a host-signed token (marketing pages). */
|
|
169
|
+
allowAnonymous: import_zod.z.boolean().default(false),
|
|
170
|
+
/** Env var holding the HS256 secret the host signs user tokens with. */
|
|
171
|
+
jwtSecretEnv: import_zod.z.string().default("FRANKIE_JWT_SECRET"),
|
|
172
|
+
/** Verify against a JWKS endpoint instead of a shared secret (RS/ES256). */
|
|
173
|
+
jwksUrl: import_zod.z.string().url().optional(),
|
|
174
|
+
jwtIssuer: import_zod.z.string().optional(),
|
|
175
|
+
jwtAudience: import_zod.z.string().optional(),
|
|
176
|
+
/** How long an issued session token is good for. */
|
|
177
|
+
sessionTtlSeconds: import_zod.z.number().int().positive().default(60 * 60),
|
|
178
|
+
/** Env var holding the secret used to sign Frankie's own session tokens. */
|
|
179
|
+
sessionSecretEnv: import_zod.z.string().default("FRANKIE_SESSION_SECRET")
|
|
180
|
+
});
|
|
181
|
+
var limitsConfigSchema = import_zod.z.object({
|
|
182
|
+
/** Messages per session per window. */
|
|
183
|
+
messagesPerWindow: import_zod.z.number().int().positive().default(30),
|
|
184
|
+
windowSeconds: import_zod.z.number().int().positive().default(15 * 60),
|
|
185
|
+
/** Sessions a single IP may open per window. */
|
|
186
|
+
sessionsPerIpPerWindow: import_zod.z.number().int().positive().default(20),
|
|
187
|
+
/** Longest a single turn may run before it is aborted. */
|
|
188
|
+
turnTimeoutMs: import_zod.z.number().int().positive().default(12e4)
|
|
189
|
+
});
|
|
190
|
+
var brandingConfigSchema = import_zod.z.object({
|
|
191
|
+
name: import_zod.z.string().default("Assistant"),
|
|
192
|
+
tagline: import_zod.z.string().optional(),
|
|
193
|
+
avatarUrl: import_zod.z.string().url().optional(),
|
|
194
|
+
launcherLabel: import_zod.z.string().default("Need a hand?")
|
|
195
|
+
});
|
|
196
|
+
var frankieConfigSchema = import_zod.z.object({
|
|
197
|
+
tenantId: import_zod.z.string().min(1),
|
|
198
|
+
branding: brandingConfigSchema.prefault({}),
|
|
199
|
+
theme: widgetThemeSchema,
|
|
200
|
+
model: modelConfigSchema.prefault({}),
|
|
201
|
+
agent: agentConfigSchema.prefault({}),
|
|
202
|
+
auth: authConfigSchema,
|
|
203
|
+
mcpServers: import_zod.z.array(mcpServerSchema).default([]),
|
|
204
|
+
/** APIs turned into tools from their OpenAPI document. */
|
|
205
|
+
apis: import_zod.z.array(apiSourceSchema).default([]),
|
|
206
|
+
limits: limitsConfigSchema.prefault({})
|
|
207
|
+
});
|
|
208
|
+
function parseFrankieConfig(input) {
|
|
209
|
+
return frankieConfigSchema.parse(input);
|
|
210
|
+
}
|
|
211
|
+
function defineFrankieConfig(config) {
|
|
212
|
+
return config;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/tools.ts
|
|
216
|
+
function evaluateToolPolicy(toolName, policy) {
|
|
217
|
+
if (matchesAny(toolName, policy.deny)) {
|
|
218
|
+
return { allowed: false, reason: `"${toolName}" is on the deny list for this agent.` };
|
|
219
|
+
}
|
|
220
|
+
if (matchesAny(toolName, policy.allow)) {
|
|
221
|
+
return { allowed: true, reason: "allowed by policy" };
|
|
222
|
+
}
|
|
223
|
+
if (policy.denyByDefault) {
|
|
224
|
+
return { allowed: false, reason: `"${toolName}" is not in this agent's allow list.` };
|
|
225
|
+
}
|
|
226
|
+
return { allowed: true, reason: "no policy match; default allow" };
|
|
227
|
+
}
|
|
228
|
+
function matchesAny(toolName, patterns) {
|
|
229
|
+
return patterns.some((pattern) => matches(toolName, pattern));
|
|
230
|
+
}
|
|
231
|
+
function matches(toolName, pattern) {
|
|
232
|
+
if (pattern === "*") return true;
|
|
233
|
+
if (pattern.endsWith("*")) return toolName.startsWith(pattern.slice(0, -1));
|
|
234
|
+
if (toolName === pattern) return true;
|
|
235
|
+
return toolName.startsWith(`${pattern}__`);
|
|
236
|
+
}
|
|
237
|
+
function describeTool(toolName, input) {
|
|
238
|
+
const mcp = /^mcp__([^_]+(?:_[^_]+)*)__(.+)$/.exec(toolName);
|
|
239
|
+
if (mcp) {
|
|
240
|
+
const [, server, tool] = mcp;
|
|
241
|
+
return `${humanise(tool ?? toolName)} \xB7 ${server}`;
|
|
242
|
+
}
|
|
243
|
+
const inputRecord = typeof input === "object" && input !== null ? input : {};
|
|
244
|
+
const hint = inputRecord["path"] ?? inputRecord["url"] ?? inputRecord["query"] ?? inputRecord["command"];
|
|
245
|
+
return typeof hint === "string" ? `${humanise(toolName)}: ${truncate(hint, 60)}` : humanise(toolName);
|
|
246
|
+
}
|
|
247
|
+
function humanise(name) {
|
|
248
|
+
return name.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
|
|
249
|
+
}
|
|
250
|
+
function truncate(value, max) {
|
|
251
|
+
return value.length <= max ? value : `${value.slice(0, max - 1)}\u2026`;
|
|
252
|
+
}
|
|
253
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
254
|
+
0 && (module.exports = {
|
|
255
|
+
agentConfigSchema,
|
|
256
|
+
apiSourceSchema,
|
|
257
|
+
authConfigSchema,
|
|
258
|
+
brandingConfigSchema,
|
|
259
|
+
defineFrankieConfig,
|
|
260
|
+
describeTool,
|
|
261
|
+
evaluateToolPolicy,
|
|
262
|
+
frankieConfigSchema,
|
|
263
|
+
limitsConfigSchema,
|
|
264
|
+
mcpServerSchema,
|
|
265
|
+
modelConfigSchema,
|
|
266
|
+
parseFrankieConfig,
|
|
267
|
+
thinkingLevelSchema,
|
|
268
|
+
toolPolicySchema,
|
|
269
|
+
widgetThemeSchema
|
|
270
|
+
});
|
|
271
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/tools.ts"],"sourcesContent":["export * from \"./config.js\";\nexport * from \"./protocol.js\";\nexport * from \"./tools.js\";\n","import { z } from \"zod\";\n\n/**\n * Tenant configuration. One config == one embedded agent. Everything a\n * customer needs to change to make Frankie theirs lives here, so the code\n * itself stays generic.\n */\n\nexport const widgetThemeSchema = z\n .object({\n primary: z.string().optional(),\n primaryForeground: z.string().optional(),\n background: z.string().optional(),\n foreground: z.string().optional(),\n muted: z.string().optional(),\n border: z.string().optional(),\n radius: z.string().optional(),\n fontFamily: z.string().optional(),\n mode: z.enum([\"light\", \"dark\", \"system\"]).default(\"system\"),\n })\n .prefault({});\n\nexport const thinkingLevelSchema = z.enum([\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\n\nexport const modelConfigSchema = z.object({\n /**\n * Any provider the model layer knows about. `fireworks` is the default;\n * `anthropic`, `openai`, `google`, `groq`, `together`, `openrouter` and\n * ~30 others resolve too, which is what makes the model a config choice.\n */\n provider: z.string().min(1).default(\"fireworks\"),\n /**\n * Model id within that provider. The default is Kimi K2.7 on Fireworks,\n * which serves a native Anthropic-Messages endpoint and a 262K window.\n */\n model: z.string().default(\"accounts/fireworks/models/kimi-k2p7-code\"),\n /** Env var holding the provider key. Never put the key in config itself. */\n apiKeyEnv: z.string().default(\"FIREWORKS_API_KEY\"),\n /** Override the provider's base URL (self-hosted gateways, tests). */\n baseUrl: z.string().url().optional(),\n maxTokens: z.number().int().positive().max(200_000).default(8192),\n temperature: z.number().min(0).max(2).default(0.6),\n /**\n * Reasoning depth. Lowest by default: an onboarding widget is latency-\n * sensitive and its questions rarely need deliberation.\n */\n thinkingLevel: thinkingLevelSchema.default(\"minimal\"),\n /** Forward the model's reasoning stream to the client as thinking deltas. */\n streamReasoning: z.boolean().default(false),\n});\n\nexport const mcpServerSchema = z.object({\n name: z\n .string()\n .min(1)\n .regex(/^[a-z0-9][a-z0-9_-]*$/i, \"MCP server names must be alphanumeric with - or _\"),\n /** `http` covers streamable HTTP; `sse` is the legacy transport. */\n transport: z.enum([\"http\", \"sse\"]).default(\"http\"),\n url: z.string().url(),\n /** Static headers, e.g. a service token for the company's own MCP server. */\n headers: z.record(z.string(), z.string()).default({}),\n /**\n * When set, the verified end-user JWT is forwarded to the MCP server in this\n * header so the company's tools can act as that user. Use with care: only\n * enable it for MCP servers you control.\n */\n forwardUserTokenAs: z.string().optional(),\n /** Extra claims from the user token to forward as `x-frankie-<claim>`. */\n forwardUserClaims: z.array(z.string()).default([]),\n});\n\n/**\n * An HTTP API described by an OpenAPI document, turned into tools directly.\n *\n * This is the \"no MCP server to write\" path: point Frankie at a spec, name the\n * operations the agent may use, and it builds the tools at startup.\n */\nexport const apiSourceSchema = z.object({\n name: z\n .string()\n .min(1)\n .regex(/^[a-z0-9][a-z0-9_-]*$/i, \"API source names must be alphanumeric with - or _\"),\n /** URL or local path to an OpenAPI 3.x document (JSON or YAML). */\n spec: z.string().min(1),\n /** Overrides the spec's own `servers[0].url`. Required if the spec has none. */\n baseUrl: z.string().url().optional(),\n /**\n * Operation ids the agent may call. Empty means \"every operation that passes\n * the other filters\" — fine for a small internal API, risky for a large one.\n */\n operations: z.array(z.string()).default([]),\n /**\n * Path globs to include, e.g. `/projects/**`. Empty means all paths.\n */\n includePaths: z.array(z.string()).default([]),\n /**\n * HTTP methods the agent may use. Read-only by default: an onboarding agent\n * wired to a whole API should not be able to DELETE on its first day. Widen\n * deliberately, per API.\n */\n methods: z.array(z.enum([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"])).default([\"GET\"]),\n /** Static headers, e.g. a service token. */\n headers: z.record(z.string(), z.string()).default({}),\n /**\n * When set, the verified end-user JWT is forwarded in this header, so your\n * API authorises the call exactly as it would any other request from them.\n */\n forwardUserTokenAs: z.string().optional(),\n /** Extra claims from the user token to forward as `x-frankie-<claim>`. */\n forwardUserClaims: z.array(z.string()).default([]),\n /** Per-call timeout. */\n timeoutMs: z.number().int().positive().default(30_000),\n /** Response bodies longer than this are truncated before the model sees them. */\n maxResponseChars: z.number().int().positive().default(8_000),\n});\n\nexport const toolPolicySchema = z.object({\n /**\n * Tool names the agent may call without asking. MCP tools are named\n * `mcp__<server>__<tool>` and generated API tools `api__<name>__<operation>`;\n * naming the prefix alone (`mcp__acme`, `api__acme`) allows all of them.\n */\n allow: z.array(z.string()).default([]),\n /** Explicit denials; evaluated before `allow`. */\n deny: z.array(z.string()).default([]),\n /**\n * Deny anything not in `allow`. On by default — an onboarding agent living\n * on a public marketing page should not have an open tool surface.\n */\n denyByDefault: z.boolean().default(true),\n});\n\nexport const agentConfigSchema = z.object({\n /** Prepended to the runtime's own operating instructions. */\n systemPrompt: z.string().default(\"\"),\n /** First message the widget shows before the user types anything. */\n greeting: z.string().default(\"Hi! I can walk you through getting set up. What are you trying to do?\"),\n suggestedPrompts: z.array(z.string()).max(6).default([]),\n maxTurns: z.number().int().positive().max(50).default(12),\n /** Hard stop on estimated spend per session. */\n maxBudgetUsd: z.number().positive().optional(),\n tools: toolPolicySchema.prefault({}),\n /** Show tool activity in the transcript. */\n showToolActivity: z.boolean().default(true),\n});\n\nexport const authConfigSchema = z.object({\n /**\n * Safe to ship in client bundles. Identifies the tenant; it authorises\n * nothing on its own — the origin allowlist and the user token do that.\n */\n publishableKey: z.string().min(8),\n /** Exact origins allowed to open sessions, e.g. `https://app.acme.com`. */\n allowedOrigins: z.array(z.string()).default([]),\n /** Let visitors chat without a host-signed token (marketing pages). */\n allowAnonymous: z.boolean().default(false),\n /** Env var holding the HS256 secret the host signs user tokens with. */\n jwtSecretEnv: z.string().default(\"FRANKIE_JWT_SECRET\"),\n /** Verify against a JWKS endpoint instead of a shared secret (RS/ES256). */\n jwksUrl: z.string().url().optional(),\n jwtIssuer: z.string().optional(),\n jwtAudience: z.string().optional(),\n /** How long an issued session token is good for. */\n sessionTtlSeconds: z.number().int().positive().default(60 * 60),\n /** Env var holding the secret used to sign Frankie's own session tokens. */\n sessionSecretEnv: z.string().default(\"FRANKIE_SESSION_SECRET\"),\n});\n\nexport const limitsConfigSchema = z.object({\n /** Messages per session per window. */\n messagesPerWindow: z.number().int().positive().default(30),\n windowSeconds: z.number().int().positive().default(15 * 60),\n /** Sessions a single IP may open per window. */\n sessionsPerIpPerWindow: z.number().int().positive().default(20),\n /** Longest a single turn may run before it is aborted. */\n turnTimeoutMs: z.number().int().positive().default(120_000),\n});\n\nexport const brandingConfigSchema = z.object({\n name: z.string().default(\"Assistant\"),\n tagline: z.string().optional(),\n avatarUrl: z.string().url().optional(),\n launcherLabel: z.string().default(\"Need a hand?\"),\n});\n\nexport const frankieConfigSchema = z.object({\n tenantId: z.string().min(1),\n branding: brandingConfigSchema.prefault({}),\n theme: widgetThemeSchema,\n model: modelConfigSchema.prefault({}),\n agent: agentConfigSchema.prefault({}),\n auth: authConfigSchema,\n mcpServers: z.array(mcpServerSchema).default([]),\n /** APIs turned into tools from their OpenAPI document. */\n apis: z.array(apiSourceSchema).default([]),\n limits: limitsConfigSchema.prefault({}),\n});\n\nexport type ThinkingLevel = z.infer<typeof thinkingLevelSchema>;\nexport type WidgetThemeConfig = z.infer<typeof widgetThemeSchema>;\nexport type ModelConfig = z.infer<typeof modelConfigSchema>;\nexport type McpServerConfig = z.infer<typeof mcpServerSchema>;\nexport type ApiSourceConfig = z.infer<typeof apiSourceSchema>;\nexport type ToolPolicy = z.infer<typeof toolPolicySchema>;\nexport type AgentConfig = z.infer<typeof agentConfigSchema>;\nexport type AuthConfig = z.infer<typeof authConfigSchema>;\nexport type LimitsConfig = z.infer<typeof limitsConfigSchema>;\nexport type BrandingConfig = z.infer<typeof brandingConfigSchema>;\nexport type FrankieConfig = z.infer<typeof frankieConfigSchema>;\nexport type FrankieConfigInput = z.input<typeof frankieConfigSchema>;\n\nexport function parseFrankieConfig(input: unknown): FrankieConfig {\n return frankieConfigSchema.parse(input);\n}\n\n/** Helper for `frankie.config.ts` files — gives editors full completion. */\nexport function defineFrankieConfig(config: FrankieConfigInput): FrankieConfigInput {\n return config;\n}\n","import type { ToolPolicy } from \"./config.js\";\n\nexport interface ToolDecision {\n allowed: boolean;\n reason: string;\n}\n\n/**\n * Decide whether the agent may run a tool.\n *\n * Patterns are matched exactly, or as a prefix when they end in `*`. MCP tools\n * arrive as `mcp__<server>__<tool>`, so listing `mcp__acme` (no trailing star)\n * also allows every tool on the `acme` server.\n */\nexport function evaluateToolPolicy(toolName: string, policy: ToolPolicy): ToolDecision {\n if (matchesAny(toolName, policy.deny)) {\n return { allowed: false, reason: `\"${toolName}\" is on the deny list for this agent.` };\n }\n if (matchesAny(toolName, policy.allow)) {\n return { allowed: true, reason: \"allowed by policy\" };\n }\n if (policy.denyByDefault) {\n return { allowed: false, reason: `\"${toolName}\" is not in this agent's allow list.` };\n }\n return { allowed: true, reason: \"no policy match; default allow\" };\n}\n\nfunction matchesAny(toolName: string, patterns: readonly string[]): boolean {\n return patterns.some((pattern) => matches(toolName, pattern));\n}\n\nfunction matches(toolName: string, pattern: string): boolean {\n if (pattern === \"*\") return true;\n if (pattern.endsWith(\"*\")) return toolName.startsWith(pattern.slice(0, -1));\n if (toolName === pattern) return true;\n // `mcp__acme` covers `mcp__acme__create_workspace`.\n return toolName.startsWith(`${pattern}__`);\n}\n\n/** A short human label for a tool call, for the \"Frankie is …\" line in the UI. */\nexport function describeTool(toolName: string, input: unknown): string {\n const mcp = /^mcp__([^_]+(?:_[^_]+)*)__(.+)$/.exec(toolName);\n if (mcp) {\n const [, server, tool] = mcp;\n return `${humanise(tool ?? toolName)} · ${server}`;\n }\n const inputRecord = typeof input === \"object\" && input !== null ? (input as Record<string, unknown>) : {};\n const hint = inputRecord[\"path\"] ?? inputRecord[\"url\"] ?? inputRecord[\"query\"] ?? inputRecord[\"command\"];\n return typeof hint === \"string\" ? `${humanise(toolName)}: ${truncate(hint, 60)}` : humanise(toolName);\n}\n\nfunction humanise(name: string): string {\n return name\n .replace(/[_-]+/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .replace(/\\b\\w/g, (c) => c.toUpperCase())\n .trim();\n}\n\nfunction truncate(value: string, max: number): string {\n return value.length <= max ? value : `${value.slice(0, max - 1)}…`;\n}\n"],"mappings":"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","names":[]}
|