@frankieai/core 0.2.3 → 0.2.4

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.d.ts CHANGED
@@ -179,10 +179,25 @@ declare const toolPolicySchema: z.ZodObject<{
179
179
  deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
180
180
  denyByDefault: z.ZodDefault<z.ZodBoolean>;
181
181
  }, z.core.$strip>;
182
+ /**
183
+ * A starter card shown on the empty chat screen: a title and short description
184
+ * the reader sees, and the message that is sent as if they typed it when they
185
+ * click the card. Configured per workspace in the dashboard's Appearance tab.
186
+ */
187
+ declare const starterPromptSchema: z.ZodObject<{
188
+ title: z.ZodString;
189
+ description: z.ZodOptional<z.ZodString>;
190
+ message: z.ZodString;
191
+ }, z.core.$strip>;
182
192
  declare const agentConfigSchema: z.ZodObject<{
183
193
  systemPrompt: z.ZodDefault<z.ZodString>;
184
194
  greeting: z.ZodDefault<z.ZodString>;
185
195
  suggestedPrompts: z.ZodDefault<z.ZodArray<z.ZodString>>;
196
+ starterPrompts: z.ZodDefault<z.ZodArray<z.ZodObject<{
197
+ title: z.ZodString;
198
+ description: z.ZodOptional<z.ZodString>;
199
+ message: z.ZodString;
200
+ }, z.core.$strip>>>;
186
201
  maxTurns: z.ZodDefault<z.ZodNumber>;
187
202
  maxBudgetUsd: z.ZodOptional<z.ZodNumber>;
188
203
  tools: z.ZodPrefault<z.ZodObject<{
@@ -267,6 +282,11 @@ declare const frankieConfigSchema: z.ZodObject<{
267
282
  systemPrompt: z.ZodDefault<z.ZodString>;
268
283
  greeting: z.ZodDefault<z.ZodString>;
269
284
  suggestedPrompts: z.ZodDefault<z.ZodArray<z.ZodString>>;
285
+ starterPrompts: z.ZodDefault<z.ZodArray<z.ZodObject<{
286
+ title: z.ZodString;
287
+ description: z.ZodOptional<z.ZodString>;
288
+ message: z.ZodString;
289
+ }, z.core.$strip>>>;
270
290
  maxTurns: z.ZodDefault<z.ZodNumber>;
271
291
  maxBudgetUsd: z.ZodOptional<z.ZodNumber>;
272
292
  tools: z.ZodPrefault<z.ZodObject<{
@@ -589,6 +609,12 @@ interface CreateSessionResponse {
589
609
  anonymous: boolean;
590
610
  };
591
611
  }
612
+ /** A starter card on the empty chat screen: what the reader sees, and the message clicking it sends. */
613
+ interface StarterPrompt {
614
+ title: string;
615
+ description?: string;
616
+ message: string;
617
+ }
592
618
  /** The subset of tenant config the widget is allowed to see. */
593
619
  interface PublicWidgetConfig {
594
620
  tenantId: string;
@@ -600,6 +626,8 @@ interface PublicWidgetConfig {
600
626
  };
601
627
  greeting: string;
602
628
  suggestedPrompts: string[];
629
+ /** Explicit starter cards (title + description + message). Take precedence over `suggestedPrompts` on the empty screen. Optional so older servers stay compatible. */
630
+ starterPrompts?: StarterPrompt[];
603
631
  theme: WidgetTheme;
604
632
  /** Whether the widget may open a session without a host-signed user token. */
605
633
  allowAnonymous: boolean;
@@ -724,6 +752,19 @@ declare function toolDescriptions(config: FrankieConfig, toolIds: string[]): Arr
724
752
  id: string;
725
753
  description: string;
726
754
  }>;
755
+ /**
756
+ * Starter suggestion prompts derived from the workspace's own published,
757
+ * runnable skills, so an empty chat offers what this assistant can actually do
758
+ * instead of a blank screen. Each entry is a clean action title ("Generate a
759
+ * lesson"), optionally with a short subtitle after " — " when the skill's goal
760
+ * adds real information. The chat empty state shows the title as the card title
761
+ * and the subtitle beneath it, and sends only the title. Auto-generated skill
762
+ * names (e.g. "product · generate lesson") and their boilerplate CRUD goals are
763
+ * cleaned up so the cards read like real suggestions, not internal ids. Used
764
+ * only when the workspace hasn't authored its own `suggestedPrompts`; deduped
765
+ * and capped to the config limit (6).
766
+ */
767
+ declare function suggestedPromptsFromSkills(config: FrankieConfig): string[];
727
768
  /**
728
769
  * Deterministic starter for the manual editor — not a claim that anything
729
770
  * was executed, and not model-generated. Follows the same structure asked of
@@ -766,4 +807,4 @@ declare function compareApiDocuments(before: ApiDocument, after: ApiDocument): {
766
807
  unchangedIds: string[];
767
808
  };
768
809
 
769
- export { type AgentConfig, type ApiCapability, type ApiDocument, type ApiSourceConfig, type ApiToolInventory, type AuthConfig, type AuthStrategy, type BrandingConfig, type ChatAttachment, type ChatRequestBody, type CreateSessionBody, type CreateSessionResponse, FIREWORKS_MODELS, type FeaturesConfig, type FrankieConfig, type FrankieConfigInput, type FrankieErrorCode, type FrankieServerEvent, type HostToolRequest, type HostToolResult, type InstallPlan, type InstallProject, type LimitsConfig, type LoadedSkill, type McpServerConfig, type ModelChoice, type ModelConfig, type PublicFeatures, type PublicWidgetConfig, type StopReason, type ThinkingLevel, type ToolDecision, type ToolPolicy, type ToolResultBody, type UsageSummary, type WidgetTheme, type WidgetThemeConfig, type WorkspaceSkill, activeSkills, agentConfigSchema, apiCapabilitySchema, apiMethods, apiOperations, apiSourceSchema, approvedSkillTools, assertNoCommittedFrankieKeys, authConfigSchema, brandingConfigSchema, canonicalJson, compareApiDocuments, defineFrankieConfig, describeTool, draftSkill, evaluateToolPolicy, featuresConfigSchema, frankieConfigSchema, limitsConfigSchema, matchPath, mcpServerSchema, modelChoiceSchema, modelConfigSchema, operationId, parseFrankieConfig, planHostedInstall, sdkUpdateWorkflow, skillSchema, thinkingLevelSchema, toolDescriptions, toolPolicySchema, validateApiDocument, widgetThemeSchema };
810
+ export { type AgentConfig, type ApiCapability, type ApiDocument, type ApiSourceConfig, type ApiToolInventory, type AuthConfig, type AuthStrategy, type BrandingConfig, type ChatAttachment, type ChatRequestBody, type CreateSessionBody, type CreateSessionResponse, FIREWORKS_MODELS, type FeaturesConfig, type FrankieConfig, type FrankieConfigInput, type FrankieErrorCode, type FrankieServerEvent, type HostToolRequest, type HostToolResult, type InstallPlan, type InstallProject, type LimitsConfig, type LoadedSkill, type McpServerConfig, type ModelChoice, type ModelConfig, type PublicFeatures, type PublicWidgetConfig, type StarterPrompt, type StopReason, type ThinkingLevel, type ToolDecision, type ToolPolicy, type ToolResultBody, type UsageSummary, type WidgetTheme, type WidgetThemeConfig, type WorkspaceSkill, activeSkills, agentConfigSchema, apiCapabilitySchema, apiMethods, apiOperations, apiSourceSchema, approvedSkillTools, assertNoCommittedFrankieKeys, authConfigSchema, brandingConfigSchema, canonicalJson, compareApiDocuments, defineFrankieConfig, describeTool, draftSkill, evaluateToolPolicy, featuresConfigSchema, frankieConfigSchema, limitsConfigSchema, matchPath, mcpServerSchema, modelChoiceSchema, modelConfigSchema, operationId, parseFrankieConfig, planHostedInstall, sdkUpdateWorkflow, skillSchema, starterPromptSchema, suggestedPromptsFromSkills, thinkingLevelSchema, toolDescriptions, toolPolicySchema, validateApiDocument, widgetThemeSchema };
package/dist/index.js CHANGED
@@ -82,6 +82,36 @@ function toolDescriptions(config, toolIds) {
82
82
  }
83
83
  return toolIds.map((id) => ({ id, description: found.get(id) ?? id }));
84
84
  }
85
+ function suggestedPromptsFromSkills(config) {
86
+ const seen = /* @__PURE__ */ new Set();
87
+ const prompts = [];
88
+ for (const skill of activeSkills(config)) {
89
+ const title = cleanSkillName(skill.name);
90
+ if (!title) continue;
91
+ const key = title.toLowerCase();
92
+ if (seen.has(key)) continue;
93
+ seen.add(key);
94
+ const detail = summarizeGoal(skill.goal, title);
95
+ prompts.push(detail ? `${title} \u2014 ${detail}` : title);
96
+ if (prompts.length >= 6) break;
97
+ }
98
+ return prompts;
99
+ }
100
+ function cleanSkillName(name) {
101
+ const trimmed = name.trim();
102
+ const withoutSource = trimmed.replace(/^[^·/:]{1,40}?\s*[·/:]\s*/, "");
103
+ const cleaned = (withoutSource || trimmed).replace(/[·/_]+/g, " ").replace(/\s+/g, " ").trim();
104
+ return cleaned ? cleaned.charAt(0).toUpperCase() + cleaned.slice(1) : "";
105
+ }
106
+ function summarizeGoal(goal, title) {
107
+ let cleaned = goal.trim().replace(/^use this(?: skill)?(?: when (?:the user (?:asks?(?: you)?(?: to| for| about)?|wants?(?: to)?|needs?(?: to)?)|you (?:need|want) to)| to)\s+/i, "").replace(/\s+/g, " ").replace(/[.\s]+$/, "");
108
+ const restatement = cleaned.match(/^.{1,40}?\s+[—–-]\s+(.+)$/);
109
+ if (restatement) cleaned = restatement[1];
110
+ if (/^(listing|creating|reading|updating|managing)\b/i.test(cleaned) || /^(create|read|update|delete|list|manage),/i.test(cleaned)) return "";
111
+ if (!cleaned || cleaned.toLowerCase() === title.toLowerCase()) return "";
112
+ const capped = cleaned.length > 80 ? cleaned.slice(0, 80).replace(/\s+\S*$/, "") + "\u2026" : cleaned;
113
+ return capped.charAt(0).toUpperCase() + capped.slice(1);
114
+ }
85
115
  function draftSkill(goal, tools) {
86
116
  return [
87
117
  `Use this when: ${goal}`,
@@ -334,12 +364,23 @@ var toolPolicySchema = z2.object({
334
364
  */
335
365
  denyByDefault: z2.boolean().default(true)
336
366
  });
367
+ var starterPromptSchema = z2.object({
368
+ title: z2.string().trim().min(1).max(80),
369
+ description: z2.string().trim().max(160).optional(),
370
+ message: z2.string().trim().min(1).max(2e3)
371
+ });
337
372
  var agentConfigSchema = z2.object({
338
373
  /** Prepended to the runtime's own operating instructions. */
339
374
  systemPrompt: z2.string().default(""),
340
375
  /** First message the widget shows before the user types anything. */
341
376
  greeting: z2.string().default("Hi! I can walk you through getting set up. What are you trying to do?"),
342
377
  suggestedPrompts: z2.array(z2.string()).max(6).default([]),
378
+ /**
379
+ * Explicit starter cards for the empty chat screen (title + description +
380
+ * the message clicking sends). When set, these take precedence over
381
+ * `suggestedPrompts` and over any skill-derived defaults.
382
+ */
383
+ starterPrompts: z2.array(starterPromptSchema).max(6).default([]),
343
384
  maxTurns: z2.number().int().positive().max(50).default(12),
344
385
  /** Hard stop on estimated spend per session. */
345
386
  maxBudgetUsd: z2.number().positive().optional(),
@@ -490,6 +531,17 @@ async function getUserToken() {
490
531
  throw new Error("The chat sign-in endpoint returned " + response.status + ".");
491
532
  }
492
533
  `;
534
+ const hostApiHeaders = auth?.client && !workspace.allowAnonymous ? `
535
+ headers: async () => {
536
+ const session = (await ${auth.client.name}?.auth.getSession())?.data.session;
537
+ return session?.access_token ? { Authorization: \`Bearer \${session.access_token}\` } : {};
538
+ },` : "";
539
+ const hostApi = `const hostApi = {
540
+ baseUrl: typeof window !== "undefined" ? window.location.origin : "",
541
+ credentials: "${auth?.client || workspace.allowAnonymous ? "same-origin" : "include"}" as const,${hostApiHeaders}
542
+ };
543
+ `;
544
+ const apiProp = " api={hostApi}";
493
545
  let priorChat;
494
546
  try {
495
547
  const previous = JSON.parse(files[root + "frankie.install.json"] ?? "{}");
@@ -502,17 +554,17 @@ async function getUserToken() {
502
554
  if (chatPath !== "/chat") notes.push("Existing /chat preserved. Frankie chat is available at /frankie/chat.");
503
555
  const mount = `"use client";
504
556
  import { usePathname } from "next/navigation";
505
- ${shared}${clientAuthImport}${getToken}
557
+ ${shared}${clientAuthImport}${getToken}${hostApi}
506
558
  export function FrankieWidgetMount() {
507
559
  const pathname = usePathname();
508
560
  const key = process.env.NEXT_PUBLIC_FRANKIE_KEY;
509
561
  if (!key || pathname === ${JSON.stringify(chatPath)}) return null;
510
- return <FrankieProvider publishableKey={key}${authProp}><FrankieWidget /></FrankieProvider>;
562
+ return <FrankieProvider publishableKey={key}${authProp}${apiProp}><FrankieWidget /></FrankieProvider>;
511
563
  }
512
564
  export function WorkspaceChatPage() {
513
565
  const key = process.env.NEXT_PUBLIC_FRANKIE_KEY;
514
566
  if (!key) return <p>Chat is not configured. Set NEXT_PUBLIC_FRANKIE_KEY in your deployment.</p>;
515
- return <FrankieChatPage publishableKey={key}${authProp} />;
567
+ return <FrankieChatPage publishableKey={key}${authProp}${apiProp} />;
516
568
  }
517
569
  `;
518
570
  const moduleName = existingMount ? "FrankieSetupChat" : "FrankieWidgetMount";
@@ -623,7 +675,7 @@ export async function GET() {
623
675
  if (!operationCount) notes.push("No supported API routes detected. The OpenAPI draft has no operations; add the real schema before connecting tools.");
624
676
  if (withheld) notes.push(`Withheld ${withheld} newly discovered write operation(s) with no detected authorization guard (no Express/Fastify middleware argument, Next.js middleware.ts matcher covering the route, or in-handler auth check). Add them to the spec yourself once they're protected.`);
625
677
  notes.push("Generated OpenAPI includes read and write routes. Review schemas before approving tools; new connections still default to GET-only access.");
626
- notes.push("Verify the public spec URL is reachable without dashboard authentication before connecting it.");
678
+ notes.push("No public spec endpoint is required: Frankie connects this committed spec on install and reads later changes from your repository through the GitHub connection.");
627
679
  notes.push("The token endpoint issues a signed user JWT, but your product API must validate it and enforce user/resource permissions before exposing tools.");
628
680
  const envPath = root + ".env.example";
629
681
  const env = (files[envPath] ?? "").replace(/^NEXT_PUBLIC_FRANKIE_KEY=.*\n?/gm, "").replace(/^FRANKIE_JWT_SECRET=.*\n?/gm, "").trimEnd();
@@ -634,9 +686,9 @@ Workspace: ${workspace.name}
634
686
 
635
687
  1. Copy NEXT_PUBLIC_FRANKIE_KEY from Frankie \u2192 Workspace \u2192 Developer details into the frontend deployment environment. Frankie deliberately leaves the value blank in this PR.
636
688
  ${workspace.allowAnonymous ? "" : "2. Add FRANKIE_JWT_SECRET from this workspace to the server hosting /api/frankie/token. Never commit the value.\n"}3. Run your package manager and commit the updated lockfile (for pnpm: pnpm install --lockfile-only). The remote GitHub installer cannot safely synthesize a lockfile.
637
- 4. Confirm https://YOUR_APP/frankie.openapi.json is public. If your Next.js middleware protects all paths, explicitly bypass /frankie.openapi.json before connecting it.
689
+ 4. Nothing to expose: this PR commits your spec to the repository, Frankie connects it for you on install, and later spec changes are read from your repository through the GitHub connection \u2014 no public spec endpoint or middleware bypass is required.
638
690
  5. Open ${chatPath}. It is created inside the detected authenticated dashboard group when one exists; add its link to the host sidebar during review.
639
- 6. Connect https://YOUR_APP/frankie.openapi.json in Frankie \u2192 Tools & MCP. The document includes read and write routes; approve tools and choose access limits separately.
691
+ 6. In Frankie \u2192 Tools & MCP, approve the operations you want and choose access limits (the spec is already connected from this PR; it includes read and write routes). After merging later spec changes, use GitHub integration \u2192 Refresh Frankie from merged spec.
640
692
  7. Forwarding sends the Frankie-signed JWT. Your API must validate it and enforce user/resource permissions. It is not automatically interchangeable with a Firebase or OAuth access token.
641
693
  8. Add NEXT_PUBLIC_FRANKIE_KEY to every frontend deployment environment (Preview and Production). ${workspace.allowAnonymous ? "" : "Add FRANKIE_JWT_SECRET only to the server environment running /api/frankie/token; never expose it to the browser.\n"}9. Review conversations in Workspace \u2192 Chat sessions.
642
694
 
@@ -1026,6 +1078,8 @@ export {
1026
1078
  planHostedInstall,
1027
1079
  sdkUpdateWorkflow,
1028
1080
  skillSchema,
1081
+ starterPromptSchema,
1082
+ suggestedPromptsFromSkills,
1029
1083
  thinkingLevelSchema,
1030
1084
  toolDescriptions,
1031
1085
  toolPolicySchema,