@braedonsaunders/appkit-ai 1.0.1

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.
Files changed (59) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +30 -0
  3. package/agent.d.ts +33 -0
  4. package/agent.d.ts.map +1 -0
  5. package/agent.js +53 -0
  6. package/agent.js.map +1 -0
  7. package/analysis.d.ts +72 -0
  8. package/analysis.d.ts.map +1 -0
  9. package/analysis.js +114 -0
  10. package/analysis.js.map +1 -0
  11. package/builder.d.ts +7 -0
  12. package/builder.d.ts.map +1 -0
  13. package/builder.js +28 -0
  14. package/builder.js.map +1 -0
  15. package/client.d.ts +91 -0
  16. package/client.d.ts.map +1 -0
  17. package/client.js +314 -0
  18. package/client.js.map +1 -0
  19. package/context.d.ts +18 -0
  20. package/context.d.ts.map +1 -0
  21. package/context.js +84 -0
  22. package/context.js.map +1 -0
  23. package/digest.d.ts +13 -0
  24. package/digest.d.ts.map +1 -0
  25. package/digest.js +23 -0
  26. package/digest.js.map +1 -0
  27. package/doc-chat.d.ts +36 -0
  28. package/doc-chat.d.ts.map +1 -0
  29. package/doc-chat.js +106 -0
  30. package/doc-chat.js.map +1 -0
  31. package/extract.d.ts +10 -0
  32. package/extract.d.ts.map +1 -0
  33. package/extract.js +31 -0
  34. package/extract.js.map +1 -0
  35. package/index.d.ts +14 -0
  36. package/index.d.ts.map +1 -0
  37. package/index.js +14 -0
  38. package/index.js.map +1 -0
  39. package/models.d.ts +11 -0
  40. package/models.d.ts.map +1 -0
  41. package/models.js +104 -0
  42. package/models.js.map +1 -0
  43. package/package.json +77 -0
  44. package/prompts.d.ts +11 -0
  45. package/prompts.d.ts.map +1 -0
  46. package/prompts.js +22 -0
  47. package/prompts.js.map +1 -0
  48. package/react.d.ts +47 -0
  49. package/react.d.ts.map +1 -0
  50. package/react.js +137 -0
  51. package/react.js.map +1 -0
  52. package/vision.d.ts +66 -0
  53. package/vision.d.ts.map +1 -0
  54. package/vision.js +137 -0
  55. package/vision.js.map +1 -0
  56. package/writing.d.ts +15 -0
  57. package/writing.d.ts.map +1 -0
  58. package/writing.js +46 -0
  59. package/writing.js.map +1 -0
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @braedonsaunders/appkit-ai
2
+
3
+ The shared multi-step agent layer for bounded, tenant-aware assistant surfaces.
4
+
5
+ The server entry point accepts an AI SDK `LanguageModel`, system prompt, prior
6
+ model messages, and a tenant-bound `ToolSet`. It deliberately imports no app
7
+ schema, tenant package, provider credential store, or domain prompt. A consuming
8
+ app resolves those at the request boundary and exposes only tools whose execute
9
+ functions already enforce its `RequestContext` and RBAC policy.
10
+
11
+ `@braedonsaunders/appkit-ai/react` supplies the streaming assistant thread, UI-message decoder,
12
+ markdown renderer, generic tool-use cards, abort control, composer, welcome and
13
+ disabled states. Conversation persistence and the HTTP transport stay app-owned.
14
+
15
+ ```ts
16
+ import { runAgentTurn } from '@braedonsaunders/appkit-ai'
17
+
18
+ return runAgentTurn({
19
+ model,
20
+ system,
21
+ messages,
22
+ tools: tenantBoundTools,
23
+ onComplete: persistAssistantTurn,
24
+ })
25
+ ```
26
+
27
+ For visual tool loops, call `pruneVisualToolContext` from the AI SDK
28
+ `prepareStep` hook. It removes historical and exact-duplicate tool-result
29
+ images while leaving the newest distinct frames, tool structure, text, and
30
+ user-supplied images intact. Persist the complete screenshot record separately.
package/agent.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { type LanguageModel, type ModelMessage, type ToolSet, type UIMessage } from 'ai';
2
+ export type AgentModelTier = 'fast' | 'smart';
3
+ export declare class AgentDisabledError extends Error {
4
+ constructor(message?: string);
5
+ }
6
+ export type AgentTurnResult = {
7
+ parts: UIMessage['parts'];
8
+ aborted: boolean;
9
+ finishReason: string;
10
+ usage: {
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ };
14
+ };
15
+ export type RunAgentTurnArgs = {
16
+ model: LanguageModel | null | undefined;
17
+ messages: ModelMessage[];
18
+ system: string;
19
+ tools: ToolSet;
20
+ maxSteps?: number;
21
+ temperature?: number;
22
+ abortSignal?: AbortSignal;
23
+ onComplete?: (result: AgentTurnResult) => void | Promise<void>;
24
+ errorMessage?: string;
25
+ };
26
+ export declare const DEFAULT_AGENT_MAX_STEPS = 12;
27
+ export declare function normalizeAgentMaxSteps(value?: number): number;
28
+ /**
29
+ * One tenant-agnostic, multi-step tool-using turn. The consuming app resolves
30
+ * the model and closes every tool over its own RequestContext/RBAC boundary.
31
+ */
32
+ export declare function runAgentTurn(args: RunAgentTurnArgs): Response;
33
+ //# sourceMappingURL=agent.d.ts.map
package/agent.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,OAAO,EACZ,KAAK,SAAS,EACf,MAAM,IAAI,CAAA;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,CAAA;AAE7C,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,SAA+C;CAInE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;CACrD,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,CAAA;IACvC,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAEzC,wBAAgB,sBAAsB,CAAC,KAAK,SAA0B,GAAG,MAAM,CAG9E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,QAAQ,CAgC7D"}
package/agent.js ADDED
@@ -0,0 +1,53 @@
1
+ import { stepCountIs, streamText, } from 'ai';
2
+ export class AgentDisabledError extends Error {
3
+ constructor(message = 'No AI model is available for this request.') {
4
+ super(message);
5
+ this.name = 'AgentDisabledError';
6
+ }
7
+ }
8
+ export const DEFAULT_AGENT_MAX_STEPS = 12;
9
+ export function normalizeAgentMaxSteps(value = DEFAULT_AGENT_MAX_STEPS) {
10
+ if (!Number.isFinite(value))
11
+ return DEFAULT_AGENT_MAX_STEPS;
12
+ return Math.max(2, Math.min(32, Math.trunc(value)));
13
+ }
14
+ /**
15
+ * One tenant-agnostic, multi-step tool-using turn. The consuming app resolves
16
+ * the model and closes every tool over its own RequestContext/RBAC boundary.
17
+ */
18
+ export function runAgentTurn(args) {
19
+ if (!args.model)
20
+ throw new AgentDisabledError();
21
+ const result = streamText({
22
+ model: args.model,
23
+ system: args.system,
24
+ messages: args.messages,
25
+ tools: args.tools,
26
+ stopWhen: stepCountIs(normalizeAgentMaxSteps(args.maxSteps)),
27
+ temperature: args.temperature ?? 0.3,
28
+ abortSignal: args.abortSignal,
29
+ });
30
+ return result.toUIMessageStreamResponse({
31
+ sendReasoning: false,
32
+ onError: () => args.errorMessage ?? 'The assistant hit an error completing that step. Please try again.',
33
+ onFinish: async ({ responseMessage, isAborted, finishReason }) => {
34
+ if (!args.onComplete)
35
+ return;
36
+ let usage = { inputTokens: 0, outputTokens: 0 };
37
+ try {
38
+ const total = await result.totalUsage;
39
+ usage = { inputTokens: total.inputTokens ?? 0, outputTokens: total.outputTokens ?? 0 };
40
+ }
41
+ catch {
42
+ // Provider usage metadata is optional; completion content still lands.
43
+ }
44
+ await args.onComplete({
45
+ parts: responseMessage.parts,
46
+ aborted: isAborted,
47
+ finishReason: finishReason ?? (isAborted ? 'abort' : 'stop'),
48
+ usage,
49
+ });
50
+ },
51
+ });
52
+ }
53
+ //# sourceMappingURL=agent.js.map
package/agent.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,UAAU,GAKX,MAAM,IAAI,CAAA;AAIX,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAO,GAAG,4CAA4C;QAChE,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAA;IAClC,CAAC;CACF;AAqBD,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAA;AAEzC,MAAM,UAAU,sBAAsB,CAAC,KAAK,GAAG,uBAAuB;IACpE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,uBAAuB,CAAA;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAsB;IACjD,IAAI,CAAC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,kBAAkB,EAAE,CAAA;IAC/C,MAAM,MAAM,GAAG,UAAU,CAAC;QACxB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,WAAW,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5D,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,GAAG;QACpC,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC,CAAA;IAEF,OAAO,MAAM,CAAC,yBAAyB,CAAC;QACtC,aAAa,EAAE,KAAK;QACpB,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,IAAI,oEAAoE;QACxG,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,EAAE;YAC/D,IAAI,CAAC,IAAI,CAAC,UAAU;gBAAE,OAAM;YAC5B,IAAI,KAAK,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAA;YAC/C,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,UAAU,CAAA;gBACrC,KAAK,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,CAAC,EAAE,CAAA;YACxF,CAAC;YAAC,MAAM,CAAC;gBACP,uEAAuE;YACzE,CAAC;YACD,MAAM,IAAI,CAAC,UAAU,CAAC;gBACpB,KAAK,EAAE,eAAe,CAAC,KAAK;gBAC5B,OAAO,EAAE,SAAS;gBAClB,YAAY,EAAE,YAAY,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;gBAC5D,KAAK;aACN,CAAC,CAAA;QACJ,CAAC;KACF,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import {\n stepCountIs,\n streamText,\n type LanguageModel,\n type ModelMessage,\n type ToolSet,\n type UIMessage,\n} from 'ai'\n\nexport type AgentModelTier = 'fast' | 'smart'\n\nexport class AgentDisabledError extends Error {\n constructor(message = 'No AI model is available for this request.') {\n super(message)\n this.name = 'AgentDisabledError'\n }\n}\n\nexport type AgentTurnResult = {\n parts: UIMessage['parts']\n aborted: boolean\n finishReason: string\n usage: { inputTokens: number; outputTokens: number }\n}\n\nexport type RunAgentTurnArgs = {\n model: LanguageModel | null | undefined\n messages: ModelMessage[]\n system: string\n tools: ToolSet\n maxSteps?: number\n temperature?: number\n abortSignal?: AbortSignal\n onComplete?: (result: AgentTurnResult) => void | Promise<void>\n errorMessage?: string\n}\n\nexport const DEFAULT_AGENT_MAX_STEPS = 12\n\nexport function normalizeAgentMaxSteps(value = DEFAULT_AGENT_MAX_STEPS): number {\n if (!Number.isFinite(value)) return DEFAULT_AGENT_MAX_STEPS\n return Math.max(2, Math.min(32, Math.trunc(value)))\n}\n\n/**\n * One tenant-agnostic, multi-step tool-using turn. The consuming app resolves\n * the model and closes every tool over its own RequestContext/RBAC boundary.\n */\nexport function runAgentTurn(args: RunAgentTurnArgs): Response {\n if (!args.model) throw new AgentDisabledError()\n const result = streamText({\n model: args.model,\n system: args.system,\n messages: args.messages,\n tools: args.tools,\n stopWhen: stepCountIs(normalizeAgentMaxSteps(args.maxSteps)),\n temperature: args.temperature ?? 0.3,\n abortSignal: args.abortSignal,\n })\n\n return result.toUIMessageStreamResponse({\n sendReasoning: false,\n onError: () => args.errorMessage ?? 'The assistant hit an error completing that step. Please try again.',\n onFinish: async ({ responseMessage, isAborted, finishReason }) => {\n if (!args.onComplete) return\n let usage = { inputTokens: 0, outputTokens: 0 }\n try {\n const total = await result.totalUsage\n usage = { inputTokens: total.inputTokens ?? 0, outputTokens: total.outputTokens ?? 0 }\n } catch {\n // Provider usage metadata is optional; completion content still lands.\n }\n await args.onComplete({\n parts: responseMessage.parts,\n aborted: isAborted,\n finishReason: finishReason ?? (isAborted ? 'abort' : 'stop'),\n usage,\n })\n },\n })\n}\n"]}
package/analysis.d.ts ADDED
@@ -0,0 +1,72 @@
1
+ import { z } from 'zod';
2
+ import { type AiConfig } from './client.js';
3
+ import type { DigestEntry } from './digest.js';
4
+ export declare const activityAnalysisSchema: z.ZodObject<{
5
+ summary: z.ZodString;
6
+ sentiment: z.ZodObject<{
7
+ label: z.ZodEnum<{
8
+ positive: "positive";
9
+ steady: "steady";
10
+ mixed: "mixed";
11
+ concerned: "concerned";
12
+ negative: "negative";
13
+ }>;
14
+ score: z.ZodNumber;
15
+ rationale: z.ZodString;
16
+ }, z.core.$strip>;
17
+ themes: z.ZodArray<z.ZodObject<{
18
+ label: z.ZodString;
19
+ count: z.ZodNumber;
20
+ }, z.core.$strip>>;
21
+ issues: z.ZodArray<z.ZodObject<{
22
+ title: z.ZodString;
23
+ severity: z.ZodEnum<{
24
+ low: "low";
25
+ medium: "medium";
26
+ high: "high";
27
+ }>;
28
+ detail: z.ZodString;
29
+ location: z.ZodNullable<z.ZodString>;
30
+ }, z.core.$strip>>;
31
+ actions: z.ZodArray<z.ZodObject<{
32
+ action: z.ZodString;
33
+ owner: z.ZodString;
34
+ priority: z.ZodEnum<{
35
+ low: "low";
36
+ medium: "medium";
37
+ high: "high";
38
+ }>;
39
+ rationale: z.ZodString;
40
+ }, z.core.$strip>>;
41
+ }, z.core.$strip>;
42
+ export type ActivityAnalysis = z.infer<typeof activityAnalysisSchema>;
43
+ export declare const datasetAnalysisSchema: z.ZodObject<{
44
+ summary: z.ZodString;
45
+ points: z.ZodArray<z.ZodObject<{
46
+ title: z.ZodString;
47
+ detail: z.ZodString;
48
+ tone: z.ZodEnum<{
49
+ positive: "positive";
50
+ negative: "negative";
51
+ neutral: "neutral";
52
+ watch: "watch";
53
+ }>;
54
+ }, z.core.$strip>>;
55
+ }, z.core.$strip>;
56
+ export type DatasetAnalysis = z.infer<typeof datasetAnalysisSchema>;
57
+ /** Analyse an arbitrary tabular query result under a user-supplied instruction.
58
+ * Null when AI is unconfigured or there are no rows. */
59
+ export declare function analyseDataset(config: AiConfig | null | undefined, args: {
60
+ instruction: string;
61
+ columns: {
62
+ key: string;
63
+ label: string;
64
+ }[];
65
+ rows: Record<string, unknown>[];
66
+ }): Promise<DatasetAnalysis | null>;
67
+ /** Analyse a batch of activity entries. Null when AI is unconfigured or empty. */
68
+ export declare function analyseActivityEntries(config: AiConfig | null | undefined, args: {
69
+ scope?: string;
70
+ entries: DigestEntry[];
71
+ }): Promise<ActivityAnalysis | null>;
72
+ //# sourceMappingURL=analysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCjC,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAIrE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;iBAchC,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AAQnE;yDACyD;AACzD,wBAAsB,cAAc,CAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EACnC,IAAI,EAAE;IACJ,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACzC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAChC,GACA,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAoBjC;AAED,kFAAkF;AAClF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EACnC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,GAC/C,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA4BlC"}
package/analysis.js ADDED
@@ -0,0 +1,114 @@
1
+ // Bulk analysis of activity entries: overall sentiment, recurring issues, and
2
+ // recommended actions routed to an owner. This is the structured counterpart
3
+ // to the short prose `generateDigest`.
4
+ import { generateObject } from 'ai';
5
+ import { z } from 'zod';
6
+ import { getModel } from './client.js';
7
+ import { ENTRY_WRITING_SYSTEM } from './prompts.js';
8
+ export const activityAnalysisSchema = z.object({
9
+ summary: z.string().describe('2–3 sentence plain-language recap for an operational manager.'),
10
+ sentiment: z
11
+ .object({
12
+ label: z
13
+ .enum(['positive', 'steady', 'mixed', 'concerned', 'negative'])
14
+ .describe('Overall tone across the entries.'),
15
+ score: z.number().min(-1).max(1).describe('-1 very negative … 0 neutral … 1 very positive.'),
16
+ rationale: z.string().describe('One sentence on what drove the sentiment.'),
17
+ })
18
+ .describe('Overall sentiment of the journals in this period.'),
19
+ themes: z
20
+ .array(z.object({ label: z.string(), count: z.number().int().min(1) }))
21
+ .max(8)
22
+ .describe('Recurring topics / themes, most frequent first.'),
23
+ issues: z
24
+ .array(z.object({
25
+ title: z.string().describe('Short title for the surfaced issue or risk.'),
26
+ severity: z.enum(['low', 'medium', 'high']),
27
+ detail: z.string().describe('1–2 sentences of context, grounded in the entries.'),
28
+ location: z.string().nullable().describe('Location if the issue is location-specific, else null.'),
29
+ }))
30
+ .max(8)
31
+ .describe('Problems, risks or recurring concerns surfaced by the journals.'),
32
+ actions: z
33
+ .array(z.object({
34
+ action: z.string().describe('A concrete recommended corrective action.'),
35
+ owner: z
36
+ .string()
37
+ .describe('Who should own it — a named person or role drawn from the entries, so it reaches the right people.'),
38
+ priority: z.enum(['low', 'medium', 'high']),
39
+ rationale: z.string().describe('Why this action, tied to a surfaced issue.'),
40
+ }))
41
+ .max(6)
42
+ .describe('Recommended corrective actions, each routed to the most appropriate owner.'),
43
+ });
44
+ // --- Generic dataset analysis (Insights AI cards) ---------------------------
45
+ export const datasetAnalysisSchema = z.object({
46
+ summary: z.string().describe('2–3 sentence plain-language takeaway, grounded in the data.'),
47
+ points: z
48
+ .array(z.object({
49
+ title: z.string().describe('Short headline for the finding.'),
50
+ detail: z.string().describe('1–2 sentences of supporting detail, grounded in the data.'),
51
+ tone: z
52
+ .enum(['positive', 'neutral', 'watch', 'negative'])
53
+ .describe('Sentiment / urgency of this finding.'),
54
+ }))
55
+ .max(8)
56
+ .describe('Key findings / insights, most important first. Returning fewer is fine.'),
57
+ });
58
+ function cell(v) {
59
+ if (v === null || typeof v === 'undefined')
60
+ return '';
61
+ if (typeof v === 'object')
62
+ return JSON.stringify(v);
63
+ return String(v).replace(/\n/g, ' ').slice(0, 120);
64
+ }
65
+ /** Analyse an arbitrary tabular query result under a user-supplied instruction.
66
+ * Null when AI is unconfigured or there are no rows. */
67
+ export async function analyseDataset(config, args) {
68
+ const model = getModel(config, 'smart');
69
+ if (!model || args.rows.length === 0 || args.columns.length === 0)
70
+ return null;
71
+ const cols = args.columns;
72
+ const header = cols.map((c) => c.label).join(' | ');
73
+ const sep = cols.map(() => '---').join(' | ');
74
+ const body = args.rows
75
+ .slice(0, 500)
76
+ .map((r) => cols.map((c) => cell(r[c.key])).join(' | '))
77
+ .join('\n');
78
+ const { object } = await generateObject({
79
+ model,
80
+ schema: datasetAnalysisSchema,
81
+ system: 'You are a meticulous data analyst. Analyse the dataset and follow the user instruction. Ground every statement STRICTLY in the data provided — never invent numbers, names or events. If the data is thin, say so and return fewer findings.',
82
+ prompt: `Instruction: ${args.instruction}\n\nDataset (${args.rows.length} rows shown${args.rows.length > 500 ? ', truncated to 500' : ''}):\n${header}\n${sep}\n${body}`,
83
+ temperature: 0.2,
84
+ });
85
+ return object;
86
+ }
87
+ /** Analyse a batch of activity entries. Null when AI is unconfigured or empty. */
88
+ export async function analyseActivityEntries(config, args) {
89
+ const model = getModel(config, 'smart');
90
+ if (!model || args.entries.length === 0)
91
+ return null;
92
+ const scope = args.scope ?? 'recent';
93
+ const corpus = args.entries
94
+ .slice(0, 200)
95
+ .map((e) => `- [${e.date}${e.location ? ` · ${e.location}` : ''}${e.author ? ` · ${e.author}` : ''}] ${e.text}`)
96
+ .join('\n');
97
+ const { object } = await generateObject({
98
+ model,
99
+ schema: activityAnalysisSchema,
100
+ system: ENTRY_WRITING_SYSTEM,
101
+ prompt: `You are analysing ${args.entries.length} activity entries from the ${scope} period. Produce a structured analysis for an operational manager:
102
+ - the overall tone and what drives it,
103
+ - the recurring themes,
104
+ - the concrete issues, risks or concerns the entries surface,
105
+ - and recommended corrective actions, each assigned to the most appropriate owner (name the specific person or role from the entries wherever possible) so the recommendation reaches the right people.
106
+ Ground every claim in the entries — do not invent incidents. If little is surfaced, return fewer items rather than padding.
107
+
108
+ ---
109
+ ${corpus}`,
110
+ temperature: 0.3,
111
+ });
112
+ return object;
113
+ }
114
+ //# sourceMappingURL=analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.js","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,uCAAuC;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAiB,MAAM,UAAU,CAAA;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAGhD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+DAA+D,CAAC;IAC7F,SAAS,EAAE,CAAC;SACT,MAAM,CAAC;QACN,KAAK,EAAE,CAAC;aACL,IAAI,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;aAC9D,QAAQ,CAAC,kCAAkC,CAAC;QAC/C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,iDAAiD,CAAC;QAC5F,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;KAC5E,CAAC;SACD,QAAQ,CAAC,mDAAmD,CAAC;IAChE,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACtE,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,iDAAiD,CAAC;IAC9D,MAAM,EAAE,CAAC;SACN,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;QACzE,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC3C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oDAAoD,CAAC;QACjF,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;KACnG,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,iEAAiE,CAAC;IAC9E,OAAO,EAAE,CAAC;SACP,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;QACxE,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,CACP,oGAAoG,CACrG;QACH,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC3C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;KAC7E,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,4EAA4E,CAAC;CAC1F,CAAC,CAAA;AAIF,+EAA+E;AAE/E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6DAA6D,CAAC;IAC3F,MAAM,EAAE,CAAC;SACN,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;QAC7D,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2DAA2D,CAAC;QACxF,IAAI,EAAE,CAAC;aACJ,IAAI,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;aAClD,QAAQ,CAAC,sCAAsC,CAAC;KACpD,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,yEAAyE,CAAC;CACvF,CAAC,CAAA;AAIF,SAAS,IAAI,CAAC,CAAU;IACtB,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,WAAW;QAAE,OAAO,EAAE,CAAA;IACrD,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;IACnD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AACpD,CAAC;AAED;yDACyD;AACzD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAmC,EACnC,IAIC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAC9E,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAA;IACzB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACnD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI;SACnB,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;SACb,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SACvD,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC;QACtC,KAAK;QACL,MAAM,EAAE,qBAAqB;QAC7B,MAAM,EACJ,8OAA8O;QAChP,MAAM,EAAE,gBAAgB,IAAI,CAAC,WAAW,gBAAgB,IAAI,CAAC,IAAI,CAAC,MAAM,cAAc,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,OAAO,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE;QACxK,WAAW,EAAE,GAAG;KACjB,CAAC,CAAA;IACF,OAAO,MAAM,CAAA;AACf,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAmC,EACnC,IAAgD;IAEhD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAA;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO;SACxB,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;SACb,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CACtG;SACA,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC;QACtC,KAAK;QACL,MAAM,EAAE,sBAAsB;QAC9B,MAAM,EAAE,oBAAoB;QAC5B,MAAM,EAAE,qBAAqB,IAAI,CAAC,OAAO,CAAC,MAAM,8BAA8B,KAAK;;;;;;;;EAQrF,MAAM,EAAE;QACN,WAAW,EAAE,GAAG;KACjB,CAAC,CAAA;IACF,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["// Bulk analysis of activity entries: overall sentiment, recurring issues, and\n// recommended actions routed to an owner. This is the structured counterpart\n// to the short prose `generateDigest`.\n\nimport { generateObject } from 'ai'\nimport { z } from 'zod'\nimport { getModel, type AiConfig } from './client'\nimport { ENTRY_WRITING_SYSTEM } from './prompts'\nimport type { DigestEntry } from './digest'\n\nexport const activityAnalysisSchema = z.object({\n summary: z.string().describe('2–3 sentence plain-language recap for an operational manager.'),\n sentiment: z\n .object({\n label: z\n .enum(['positive', 'steady', 'mixed', 'concerned', 'negative'])\n .describe('Overall tone across the entries.'),\n score: z.number().min(-1).max(1).describe('-1 very negative … 0 neutral … 1 very positive.'),\n rationale: z.string().describe('One sentence on what drove the sentiment.'),\n })\n .describe('Overall sentiment of the journals in this period.'),\n themes: z\n .array(z.object({ label: z.string(), count: z.number().int().min(1) }))\n .max(8)\n .describe('Recurring topics / themes, most frequent first.'),\n issues: z\n .array(\n z.object({\n title: z.string().describe('Short title for the surfaced issue or risk.'),\n severity: z.enum(['low', 'medium', 'high']),\n detail: z.string().describe('1–2 sentences of context, grounded in the entries.'),\n location: z.string().nullable().describe('Location if the issue is location-specific, else null.'),\n }),\n )\n .max(8)\n .describe('Problems, risks or recurring concerns surfaced by the journals.'),\n actions: z\n .array(\n z.object({\n action: z.string().describe('A concrete recommended corrective action.'),\n owner: z\n .string()\n .describe(\n 'Who should own it — a named person or role drawn from the entries, so it reaches the right people.',\n ),\n priority: z.enum(['low', 'medium', 'high']),\n rationale: z.string().describe('Why this action, tied to a surfaced issue.'),\n }),\n )\n .max(6)\n .describe('Recommended corrective actions, each routed to the most appropriate owner.'),\n})\n\nexport type ActivityAnalysis = z.infer<typeof activityAnalysisSchema>\n\n// --- Generic dataset analysis (Insights AI cards) ---------------------------\n\nexport const datasetAnalysisSchema = z.object({\n summary: z.string().describe('2–3 sentence plain-language takeaway, grounded in the data.'),\n points: z\n .array(\n z.object({\n title: z.string().describe('Short headline for the finding.'),\n detail: z.string().describe('1–2 sentences of supporting detail, grounded in the data.'),\n tone: z\n .enum(['positive', 'neutral', 'watch', 'negative'])\n .describe('Sentiment / urgency of this finding.'),\n }),\n )\n .max(8)\n .describe('Key findings / insights, most important first. Returning fewer is fine.'),\n})\n\nexport type DatasetAnalysis = z.infer<typeof datasetAnalysisSchema>\n\nfunction cell(v: unknown): string {\n if (v === null || typeof v === 'undefined') return ''\n if (typeof v === 'object') return JSON.stringify(v)\n return String(v).replace(/\\n/g, ' ').slice(0, 120)\n}\n\n/** Analyse an arbitrary tabular query result under a user-supplied instruction.\n * Null when AI is unconfigured or there are no rows. */\nexport async function analyseDataset(\n config: AiConfig | null | undefined,\n args: {\n instruction: string\n columns: { key: string; label: string }[]\n rows: Record<string, unknown>[]\n },\n): Promise<DatasetAnalysis | null> {\n const model = getModel(config, 'smart')\n if (!model || args.rows.length === 0 || args.columns.length === 0) return null\n const cols = args.columns\n const header = cols.map((c) => c.label).join(' | ')\n const sep = cols.map(() => '---').join(' | ')\n const body = args.rows\n .slice(0, 500)\n .map((r) => cols.map((c) => cell(r[c.key])).join(' | '))\n .join('\\n')\n\n const { object } = await generateObject({\n model,\n schema: datasetAnalysisSchema,\n system:\n 'You are a meticulous data analyst. Analyse the dataset and follow the user instruction. Ground every statement STRICTLY in the data provided — never invent numbers, names or events. If the data is thin, say so and return fewer findings.',\n prompt: `Instruction: ${args.instruction}\\n\\nDataset (${args.rows.length} rows shown${args.rows.length > 500 ? ', truncated to 500' : ''}):\\n${header}\\n${sep}\\n${body}`,\n temperature: 0.2,\n })\n return object\n}\n\n/** Analyse a batch of activity entries. Null when AI is unconfigured or empty. */\nexport async function analyseActivityEntries(\n config: AiConfig | null | undefined,\n args: { scope?: string; entries: DigestEntry[] },\n): Promise<ActivityAnalysis | null> {\n const model = getModel(config, 'smart')\n if (!model || args.entries.length === 0) return null\n const scope = args.scope ?? 'recent'\n const corpus = args.entries\n .slice(0, 200)\n .map(\n (e) =>\n `- [${e.date}${e.location ? ` · ${e.location}` : ''}${e.author ? ` · ${e.author}` : ''}] ${e.text}`,\n )\n .join('\\n')\n\n const { object } = await generateObject({\n model,\n schema: activityAnalysisSchema,\n system: ENTRY_WRITING_SYSTEM,\n prompt: `You are analysing ${args.entries.length} activity entries from the ${scope} period. Produce a structured analysis for an operational manager:\n- the overall tone and what drives it,\n- the recurring themes,\n- the concrete issues, risks or concerns the entries surface,\n- and recommended corrective actions, each assigned to the most appropriate owner (name the specific person or role from the entries wherever possible) so the recommendation reaches the right people.\nGround every claim in the entries — do not invent incidents. If little is surfaced, return fewer items rather than padding.\n\n---\n${corpus}`,\n temperature: 0.3,\n })\n return object\n}\n"]}
package/builder.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { type AiConfig, type ModelTier } from './client.js';
2
+ export declare function runBuilderPrompt(config: AiConfig | null | undefined, args: {
3
+ system: string;
4
+ prompt: string;
5
+ tier?: ModelTier;
6
+ }): Promise<string | null>;
7
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AASA,OAAO,EAAY,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAA;AAGlE,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EACnC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,GACzD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB"}
package/builder.js ADDED
@@ -0,0 +1,28 @@
1
+ // AI builder generation — turns a natural-language prompt into structured JSON
2
+ // (a form/App schema, or an automation/Flow graph) for the visual Builder.
3
+ //
4
+ // Generic on purpose: this package stays decoupled from @braedonsaunders/appkit-forms-core.
5
+ // The caller supplies the `system` spec (schema shape + field catalogue + an
6
+ // example) and validates the returned text against the forms-core zod schemas,
7
+ // retrying with the validation error on failure.
8
+ import { generateText } from 'ai';
9
+ import { getModel } from './client.js';
10
+ import { orgContextLine } from './prompts.js';
11
+ export async function runBuilderPrompt(config, args) {
12
+ const model = getModel(config, args.tier ?? 'smart');
13
+ if (!model)
14
+ return null;
15
+ try {
16
+ const { text } = await generateText({
17
+ model,
18
+ system: args.system + orgContextLine(config?.org),
19
+ prompt: args.prompt,
20
+ });
21
+ return text;
22
+ }
23
+ catch (e) {
24
+ console.warn('[ai/builder] generation failed:', e instanceof Error ? e.message : e);
25
+ return null;
26
+ }
27
+ }
28
+ //# sourceMappingURL=builder.js.map
package/builder.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,2EAA2E;AAC3E,EAAE;AACF,4FAA4F;AAC5F,6EAA6E;AAC7E,+EAA+E;AAC/E,iDAAiD;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,QAAQ,EAAiC,MAAM,UAAU,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAE1C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAmC,EACnC,IAA0D;IAE1D,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,CAAA;IACpD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,YAAY,CAAC;YAClC,KAAK;YACL,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC;YACjD,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,iCAAiC,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACnF,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC","sourcesContent":["// AI builder generation — turns a natural-language prompt into structured JSON\n// (a form/App schema, or an automation/Flow graph) for the visual Builder.\n//\n// Generic on purpose: this package stays decoupled from @braedonsaunders/appkit-forms-core.\n// The caller supplies the `system` spec (schema shape + field catalogue + an\n// example) and validates the returned text against the forms-core zod schemas,\n// retrying with the validation error on failure.\n\nimport { generateText } from 'ai'\nimport { getModel, type AiConfig, type ModelTier } from './client'\nimport { orgContextLine } from './prompts'\n\nexport async function runBuilderPrompt(\n config: AiConfig | null | undefined,\n args: { system: string; prompt: string; tier?: ModelTier },\n): Promise<string | null> {\n const model = getModel(config, args.tier ?? 'smart')\n if (!model) return null\n try {\n const { text } = await generateText({\n model,\n system: args.system + orgContextLine(config?.org),\n prompt: args.prompt,\n })\n return text\n } catch (e) {\n console.warn('[ai/builder] generation failed:', e instanceof Error ? e.message : e)\n return null\n }\n}\n"]}
package/client.d.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { type LanguageModel } from 'ai';
2
+ export type AiProvider = 'anthropic' | 'openai' | 'google' | 'openrouter' | 'groq' | 'xai' | 'deepseek' | 'mistral' | 'custom';
3
+ export type ModelTier = 'fast' | 'smart';
4
+ /**
5
+ * Platform-wide AI policy governing per-tenant overrides (mirrors the email/SMS
6
+ * policy modes). 'disabled' is a global kill switch; 'global_only' forces the
7
+ * platform provider for every tenant; 'tenant_optional' lets each tenant use its
8
+ * own provider and falls back to the platform default.
9
+ */
10
+ export type AiPolicyMode = 'tenant_optional' | 'global_only' | 'disabled';
11
+ export type AiConfig = {
12
+ provider: AiProvider;
13
+ apiKey: string;
14
+ modelFast?: string | null;
15
+ modelSmart?: string | null;
16
+ /**
17
+ * Endpoint for OpenAI-compatible providers. Required for `custom`; for the
18
+ * named compatible providers it is an optional override of the built-in URL.
19
+ */
20
+ baseUrl?: string | null;
21
+ /**
22
+ * Organization (tenant) identity for prompt grounding, so generated content
23
+ * uses the real org name instead of a placeholder. Populated by
24
+ * `getTenantAiConfig`; content-generation paths inject it into the system
25
+ * prompt, analysis/vision paths ignore it.
26
+ */
27
+ org?: {
28
+ name: string;
29
+ } | null;
30
+ };
31
+ type ProviderKind = 'anthropic' | 'openai' | 'google' | 'openai-compatible';
32
+ export type ProviderSpec = {
33
+ value: AiProvider;
34
+ label: string;
35
+ kind: ProviderKind;
36
+ /** Built-in endpoint for a named OpenAI-compatible provider (null otherwise). */
37
+ baseUrl: string | null;
38
+ /** True when the tenant MUST supply their own base URL (i.e. `custom`). */
39
+ requiresBaseUrl: boolean;
40
+ /** Default fast/smart model ids (placeholders + fallbacks). Empty for `custom`. */
41
+ fast: string;
42
+ smart: string;
43
+ /** Placeholder shown in the API-key field. */
44
+ keyHint: string;
45
+ /** Optional note about the model-id format for this provider. */
46
+ modelHint?: string;
47
+ /**
48
+ * True when this provider's API accepts IMAGE content inside a tool result
49
+ * (Anthropic does; OpenAI's and Google's function/tool results are text/JSON
50
+ * only). Gates vision tools that return rendered page images to the model —
51
+ * see `providerSupportsImageToolResults`.
52
+ */
53
+ visionToolResults: boolean;
54
+ };
55
+ /**
56
+ * Provider catalogue — single source of truth for the settings UI, the model
57
+ * factory and config validation. Add a provider here and it lights up everywhere.
58
+ */
59
+ export declare const AI_PROVIDER_SPECS: ProviderSpec[];
60
+ export declare function isAiProvider(value: unknown): value is AiProvider;
61
+ export declare function providerSpec(provider: AiProvider): ProviderSpec;
62
+ /**
63
+ * Validate and canonicalize a persisted AI endpoint override. Runtime requests
64
+ * repeat the public-DNS check immediately before opening each socket.
65
+ */
66
+ export declare function validateAiBaseUrl(provider: AiProvider, rawBaseUrl: string | null | undefined): Promise<string | null>;
67
+ /** Socket-pinned transport for tenant-configurable OpenAI-compatible endpoints. */
68
+ export declare const secureAiFetch: typeof globalThis.fetch;
69
+ export declare function defaultModel(provider: AiProvider, tier: ModelTier): string;
70
+ /**
71
+ * Whether the configured provider accepts IMAGE content in a tool result — the
72
+ * capability that lets a tool hand rendered PDF pages back to the model for
73
+ * vision reading. Currently Anthropic only; other providers' tool/function
74
+ * results are text/JSON only, so exposing such a tool to them would break the
75
+ * agent turn. Used to gate the assistant's `view_document_pages` tool.
76
+ */
77
+ export declare function providerSupportsImageToolResults(config: AiConfig | null | undefined): boolean;
78
+ export declare function isAiConfigured(config: AiConfig | null | undefined): config is AiConfig;
79
+ /** Resolve a language model from a tenant's config, or null when not configured. */
80
+ export declare function getModel(config: AiConfig | null | undefined, tier?: ModelTier): LanguageModel | null;
81
+ export declare class AIDisabledError extends Error {
82
+ readonly name = "AIDisabledError";
83
+ constructor();
84
+ }
85
+ /** Live test of a config — sends a tiny prompt and reports success/failure. */
86
+ export declare function pingModel(config: AiConfig | null | undefined): Promise<{
87
+ ok: boolean;
88
+ message: string;
89
+ }>;
90
+ export {};
91
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,IAAI,CAAA;AAmBrD,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,YAAY,GACZ,MAAM,GACN,KAAK,GACL,UAAU,GACV,SAAS,GACT,QAAQ,CAAA;AAEZ,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;AAExC;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG,aAAa,GAAG,UAAU,CAAA;AAEzE,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,UAAU,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB;;;;;OAKG;IACH,GAAG,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC9B,CAAA;AAID,KAAK,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,mBAAmB,CAAA;AAE3E,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,UAAU,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,YAAY,CAAA;IAClB,iFAAiF;IACjF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,2EAA2E;IAC3E,eAAe,EAAE,OAAO,CAAA;IACxB,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAA;IACf,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,iBAAiB,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,YAAY,EAyG3C,CAAA;AAOD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,UAAU,GAAG,YAAY,CAE/D;AAQD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,UAAU,EACpB,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACpC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4BxB;AAqCD,mFAAmF;AACnF,eAAO,MAAM,aAAa,EAAE,OAAO,UAAU,CAAC,KAiB7C,CAAA;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,CAG1E;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAG7F;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,IAAI,QAAQ,CAMtF;AAED,oFAAoF;AACpF,wBAAgB,QAAQ,CACtB,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EACnC,IAAI,GAAE,SAAkB,GACvB,aAAa,GAAG,IAAI,CA0BtB;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAkB,IAAI,qBAAoB;;CAI3C;AAED,+EAA+E;AAC/E,wBAAsB,SAAS,CAC7B,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,GAClC,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAc3C"}