@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.
- package/LICENSE +661 -0
- package/README.md +30 -0
- package/agent.d.ts +33 -0
- package/agent.d.ts.map +1 -0
- package/agent.js +53 -0
- package/agent.js.map +1 -0
- package/analysis.d.ts +72 -0
- package/analysis.d.ts.map +1 -0
- package/analysis.js +114 -0
- package/analysis.js.map +1 -0
- package/builder.d.ts +7 -0
- package/builder.d.ts.map +1 -0
- package/builder.js +28 -0
- package/builder.js.map +1 -0
- package/client.d.ts +91 -0
- package/client.d.ts.map +1 -0
- package/client.js +314 -0
- package/client.js.map +1 -0
- package/context.d.ts +18 -0
- package/context.d.ts.map +1 -0
- package/context.js +84 -0
- package/context.js.map +1 -0
- package/digest.d.ts +13 -0
- package/digest.d.ts.map +1 -0
- package/digest.js +23 -0
- package/digest.js.map +1 -0
- package/doc-chat.d.ts +36 -0
- package/doc-chat.d.ts.map +1 -0
- package/doc-chat.js +106 -0
- package/doc-chat.js.map +1 -0
- package/extract.d.ts +10 -0
- package/extract.d.ts.map +1 -0
- package/extract.js +31 -0
- package/extract.js.map +1 -0
- package/index.d.ts +14 -0
- package/index.d.ts.map +1 -0
- package/index.js +14 -0
- package/index.js.map +1 -0
- package/models.d.ts +11 -0
- package/models.d.ts.map +1 -0
- package/models.js +104 -0
- package/models.js.map +1 -0
- package/package.json +77 -0
- package/prompts.d.ts +11 -0
- package/prompts.d.ts.map +1 -0
- package/prompts.js +22 -0
- package/prompts.js.map +1 -0
- package/react.d.ts +47 -0
- package/react.d.ts.map +1 -0
- package/react.js +137 -0
- package/react.js.map +1 -0
- package/vision.d.ts +66 -0
- package/vision.d.ts.map +1 -0
- package/vision.js +137 -0
- package/vision.js.map +1 -0
- package/writing.d.ts +15 -0
- package/writing.d.ts.map +1 -0
- package/writing.js +46 -0
- package/writing.js.map +1 -0
package/doc-chat.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Document AI agent — a doc-aware assistant embedded in a host editor
|
|
2
|
+
// that can answer questions, WRITE a whole document, and make surgical
|
|
3
|
+
// exact-match edits (find→replace, Claude-Code-style) through tools the host
|
|
4
|
+
// executes against the canonical document. One call runs a full tool loop and
|
|
5
|
+
// returns the final assistant text plus what changed.
|
|
6
|
+
import { generateText, stepCountIs, tool } from 'ai';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { AIDisabledError, getModel } from './client.js';
|
|
9
|
+
const DOC_AGENT_SYSTEM = `You are an expert documentation assistant embedded inside a document editor. You help write and edit controlled documents — policies, procedures, manuals, forms, specifications, reports, and training material.
|
|
10
|
+
|
|
11
|
+
You can EDIT THE OPEN DOCUMENT DIRECTLY with your tools:
|
|
12
|
+
- read_document — returns the document's current plain text. Read before editing when you are not certain of the exact current wording.
|
|
13
|
+
- edit_document — precise find→replace edits. Each "find" must be an EXACT substring of the current document text (copy it verbatim from read_document output, including punctuation and spacing) and should be long enough to be unique. Keep each "find" within a single paragraph. Every occurrence is replaced; the result reports the occurrence count per edit — 0 means your "find" did not match and you must re-read and retry with corrected text.
|
|
14
|
+
- write_document — replaces the ENTIRE document. Use for drafting a document from scratch or a full rewrite. Provide clean semantic HTML using only: <h1> <h2> <h3> <p> <ul> <ol> <li> <strong> <em> <blockquote> <table> <thead> <tbody> <tr> <th> <td>. No markdown, no code fences.
|
|
15
|
+
|
|
16
|
+
Working style:
|
|
17
|
+
- When asked to draft/write a document: call write_document with the complete content, then reply with a one-sentence confirmation of what you created.
|
|
18
|
+
- When asked to change/fix/update specific content: use edit_document with tight, exact find strings. On a 0-count result, re-read the document and retry once with corrected text.
|
|
19
|
+
- When asked a question or for review/feedback: just answer in plain prose — do not modify the document.
|
|
20
|
+
- Write in clear, professional, context-appropriate language. Never fabricate regulatory citations, standard numbers, dates, or figures; use [PLACEHOLDER] where a specific must be supplied (named individuals or role-holders, addresses, effective/revision dates, specific figures).
|
|
21
|
+
- Your final reply is a short plain-prose summary or answer — never dump document content into the reply when a tool already applied it.`;
|
|
22
|
+
/**
|
|
23
|
+
* Run one agent turn (multi-step tool loop) for the document assistant.
|
|
24
|
+
* Throws AIDisabledError when the tenant has no model configured.
|
|
25
|
+
*/
|
|
26
|
+
export async function runDocAgent(config, args) {
|
|
27
|
+
const model = getModel(config, 'smart');
|
|
28
|
+
if (!model)
|
|
29
|
+
throw new AIDisabledError();
|
|
30
|
+
// Org identity rides on the config (see getTenantAiConfig).
|
|
31
|
+
const orgName = config?.org?.name?.trim();
|
|
32
|
+
const orgContext = orgName
|
|
33
|
+
? `\n\nThis document is authored for the organization "${orgName}". Use this exact name wherever the document refers to the organization (e.g. "${orgName} is committed to…") — do NOT use a placeholder for the company name.`
|
|
34
|
+
: '';
|
|
35
|
+
const docContext = args.docText?.trim()
|
|
36
|
+
? `\n\nThe document currently contains:\n"""\n${args.docText.slice(0, 16_000)}\n"""`
|
|
37
|
+
: '\n\nThe document is currently empty.';
|
|
38
|
+
const actions = [];
|
|
39
|
+
let docChanged = false;
|
|
40
|
+
const tools = {
|
|
41
|
+
read_document: tool({
|
|
42
|
+
description: "Read the document's current plain text.",
|
|
43
|
+
inputSchema: z.object({}),
|
|
44
|
+
execute: async () => {
|
|
45
|
+
actions.push('Read the document');
|
|
46
|
+
const text = await args.tools.readDocument();
|
|
47
|
+
return text ? text.slice(0, 24_000) : '(the document is empty)';
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
edit_document: tool({
|
|
51
|
+
description: 'Apply precise find→replace edits to the document. Each find must be an exact, unique substring of the current text.',
|
|
52
|
+
inputSchema: z.object({
|
|
53
|
+
edits: z
|
|
54
|
+
.array(z.object({
|
|
55
|
+
find: z.string().min(1).max(4000),
|
|
56
|
+
replace: z.string().max(8000),
|
|
57
|
+
}))
|
|
58
|
+
.min(1)
|
|
59
|
+
.max(20),
|
|
60
|
+
}),
|
|
61
|
+
execute: async ({ edits }) => {
|
|
62
|
+
const results = await args.tools.editDocument(edits);
|
|
63
|
+
const changes = results.reduce((n, r) => n + r.count, 0);
|
|
64
|
+
if (changes > 0) {
|
|
65
|
+
docChanged = true;
|
|
66
|
+
actions.push(`Edited the document (${changes} change${changes === 1 ? '' : 's'})`);
|
|
67
|
+
}
|
|
68
|
+
const misses = results.filter((r) => r.count === 0);
|
|
69
|
+
if (misses.length > 0)
|
|
70
|
+
actions.push(`${misses.length} edit(s) did not match`);
|
|
71
|
+
return results
|
|
72
|
+
.map((r) => r.count > 0
|
|
73
|
+
? `Replaced ${r.count} occurrence(s) of: ${r.find.slice(0, 80)}`
|
|
74
|
+
: `NOT FOUND (0 occurrences — re-read the document and retry with exact text): ${r.find.slice(0, 120)}`)
|
|
75
|
+
.join('\n');
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
write_document: tool({
|
|
79
|
+
description: 'Replace the entire document with new HTML content (drafting from scratch or a full rewrite).',
|
|
80
|
+
inputSchema: z.object({ html: z.string().min(20).max(400_000) }),
|
|
81
|
+
execute: async ({ html }) => {
|
|
82
|
+
await args.tools.writeDocument(html);
|
|
83
|
+
docChanged = true;
|
|
84
|
+
actions.push('Wrote the document');
|
|
85
|
+
return 'The document now contains the provided content.';
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
};
|
|
89
|
+
const messages = args.messages
|
|
90
|
+
.slice(-20)
|
|
91
|
+
.map((m) => ({ role: m.role, content: m.content }));
|
|
92
|
+
const result = await generateText({
|
|
93
|
+
model,
|
|
94
|
+
system: DOC_AGENT_SYSTEM + orgContext + docContext,
|
|
95
|
+
messages,
|
|
96
|
+
tools,
|
|
97
|
+
stopWhen: stepCountIs(args.maxSteps ?? 8),
|
|
98
|
+
temperature: 0.4,
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
text: result.text.trim() || (docChanged ? 'Done.' : ''),
|
|
102
|
+
actions,
|
|
103
|
+
docChanged,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=doc-chat.js.map
|
package/doc-chat.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-chat.js","sourceRoot":"","sources":["../src/doc-chat.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uEAAuE;AACvE,6EAA6E;AAC7E,8EAA8E;AAC9E,sDAAsD;AAEtD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,IAAI,EAAqB,MAAM,IAAI,CAAA;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAiB,MAAM,UAAU,CAAA;AAsBnE,MAAM,gBAAgB,GAAG;;;;;;;;;;;;yIAYgH,CAAA;AAEzI;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAmC,EACnC,IAKC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,eAAe,EAAE,CAAA;IAEvC,4DAA4D;IAC5D,MAAM,OAAO,GAAG,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IACzC,MAAM,UAAU,GAAG,OAAO;QACxB,CAAC,CAAC,uDAAuD,OAAO,kFAAkF,OAAO,sEAAsE;QAC/N,CAAC,CAAC,EAAE,CAAA;IACN,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE;QACrC,CAAC,CAAC,8CAA8C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO;QACpF,CAAC,CAAC,sCAAsC,CAAA;IAE1C,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,UAAU,GAAG,KAAK,CAAA;IAEtB,MAAM,KAAK,GAAG;QACZ,aAAa,EAAE,IAAI,CAAC;YAClB,WAAW,EAAE,yCAAyC;YACtD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;gBACjC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAA;gBAC5C,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAA;YACjE,CAAC;SACF,CAAC;QACF,aAAa,EAAE,IAAI,CAAC;YAClB,WAAW,EACT,qHAAqH;YACvH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;gBACpB,KAAK,EAAE,CAAC;qBACL,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;oBACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;oBACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;iBAC9B,CAAC,CACH;qBACA,GAAG,CAAC,CAAC,CAAC;qBACN,GAAG,CAAC,EAAE,CAAC;aACX,CAAC;YACF,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;gBACpD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;gBACxD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAChB,UAAU,GAAG,IAAI,CAAA;oBACjB,OAAO,CAAC,IAAI,CAAC,wBAAwB,OAAO,UAAU,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAA;gBACpF,CAAC;gBACD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAA;gBACnD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,wBAAwB,CAAC,CAAA;gBAC7E,OAAO,OAAO;qBACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACT,CAAC,CAAC,KAAK,GAAG,CAAC;oBACT,CAAC,CAAC,YAAY,CAAC,CAAC,KAAK,sBAAsB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;oBAChE,CAAC,CAAC,+EAA+E,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC1G;qBACA,IAAI,CAAC,IAAI,CAAC,CAAA;YACf,CAAC;SACF,CAAC;QACF,cAAc,EAAE,IAAI,CAAC;YACnB,WAAW,EACT,8FAA8F;YAChG,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAChE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;gBAC1B,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;gBACpC,UAAU,GAAG,IAAI,CAAA;gBACjB,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAA;gBAClC,OAAO,iDAAiD,CAAA;YAC1D,CAAC;SACF,CAAC;KACH,CAAA;IAED,MAAM,QAAQ,GAAmB,IAAI,CAAC,QAAQ;SAC3C,KAAK,CAAC,CAAC,EAAE,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAErD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,KAAK;QACL,MAAM,EAAE,gBAAgB,GAAG,UAAU,GAAG,UAAU;QAClD,QAAQ;QACR,KAAK;QACL,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACzC,WAAW,EAAE,GAAG;KACjB,CAAC,CAAA;IAEF,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,OAAO;QACP,UAAU;KACX,CAAA;AACH,CAAC","sourcesContent":["// Document AI agent — a doc-aware assistant embedded in a host editor\n// that can answer questions, WRITE a whole document, and make surgical\n// exact-match edits (find→replace, Claude-Code-style) through tools the host\n// executes against the canonical document. One call runs a full tool loop and\n// returns the final assistant text plus what changed.\n\nimport { generateText, stepCountIs, tool, type ModelMessage } from 'ai'\nimport { z } from 'zod'\nimport { AIDisabledError, getModel, type AiConfig } from './client'\n\nexport type DocChatMessage = { role: 'user' | 'assistant'; content: string }\n\nexport type DocAgentToolImpl = {\n /** Fresh plain text of the current document. */\n readDocument: () => Promise<string>\n /** Exact-match text edits; returns per-edit occurrence counts. */\n editDocument: (\n edits: { find: string; replace: string }[],\n ) => Promise<{ find: string; count: number }[]>\n /** Replace the entire document with new HTML content. */\n writeDocument: (html: string) => Promise<void>\n}\n\nexport type DocAgentResult = {\n text: string\n /** Human-readable notes about what the agent did (shown in the panel). */\n actions: string[]\n docChanged: boolean\n}\n\nconst DOC_AGENT_SYSTEM = `You are an expert documentation assistant embedded inside a document editor. You help write and edit controlled documents — policies, procedures, manuals, forms, specifications, reports, and training material.\n\nYou can EDIT THE OPEN DOCUMENT DIRECTLY with your tools:\n- read_document — returns the document's current plain text. Read before editing when you are not certain of the exact current wording.\n- edit_document — precise find→replace edits. Each \"find\" must be an EXACT substring of the current document text (copy it verbatim from read_document output, including punctuation and spacing) and should be long enough to be unique. Keep each \"find\" within a single paragraph. Every occurrence is replaced; the result reports the occurrence count per edit — 0 means your \"find\" did not match and you must re-read and retry with corrected text.\n- write_document — replaces the ENTIRE document. Use for drafting a document from scratch or a full rewrite. Provide clean semantic HTML using only: <h1> <h2> <h3> <p> <ul> <ol> <li> <strong> <em> <blockquote> <table> <thead> <tbody> <tr> <th> <td>. No markdown, no code fences.\n\nWorking style:\n- When asked to draft/write a document: call write_document with the complete content, then reply with a one-sentence confirmation of what you created.\n- When asked to change/fix/update specific content: use edit_document with tight, exact find strings. On a 0-count result, re-read the document and retry once with corrected text.\n- When asked a question or for review/feedback: just answer in plain prose — do not modify the document.\n- Write in clear, professional, context-appropriate language. Never fabricate regulatory citations, standard numbers, dates, or figures; use [PLACEHOLDER] where a specific must be supplied (named individuals or role-holders, addresses, effective/revision dates, specific figures).\n- Your final reply is a short plain-prose summary or answer — never dump document content into the reply when a tool already applied it.`\n\n/**\n * Run one agent turn (multi-step tool loop) for the document assistant.\n * Throws AIDisabledError when the tenant has no model configured.\n */\nexport async function runDocAgent(\n config: AiConfig | null | undefined,\n args: {\n messages: DocChatMessage[]\n docText?: string\n tools: DocAgentToolImpl\n maxSteps?: number\n },\n): Promise<DocAgentResult> {\n const model = getModel(config, 'smart')\n if (!model) throw new AIDisabledError()\n\n // Org identity rides on the config (see getTenantAiConfig).\n const orgName = config?.org?.name?.trim()\n const orgContext = orgName\n ? `\\n\\nThis document is authored for the organization \"${orgName}\". Use this exact name wherever the document refers to the organization (e.g. \"${orgName} is committed to…\") — do NOT use a placeholder for the company name.`\n : ''\n const docContext = args.docText?.trim()\n ? `\\n\\nThe document currently contains:\\n\"\"\"\\n${args.docText.slice(0, 16_000)}\\n\"\"\"`\n : '\\n\\nThe document is currently empty.'\n\n const actions: string[] = []\n let docChanged = false\n\n const tools = {\n read_document: tool({\n description: \"Read the document's current plain text.\",\n inputSchema: z.object({}),\n execute: async () => {\n actions.push('Read the document')\n const text = await args.tools.readDocument()\n return text ? text.slice(0, 24_000) : '(the document is empty)'\n },\n }),\n edit_document: tool({\n description:\n 'Apply precise find→replace edits to the document. Each find must be an exact, unique substring of the current text.',\n inputSchema: z.object({\n edits: z\n .array(\n z.object({\n find: z.string().min(1).max(4000),\n replace: z.string().max(8000),\n }),\n )\n .min(1)\n .max(20),\n }),\n execute: async ({ edits }) => {\n const results = await args.tools.editDocument(edits)\n const changes = results.reduce((n, r) => n + r.count, 0)\n if (changes > 0) {\n docChanged = true\n actions.push(`Edited the document (${changes} change${changes === 1 ? '' : 's'})`)\n }\n const misses = results.filter((r) => r.count === 0)\n if (misses.length > 0) actions.push(`${misses.length} edit(s) did not match`)\n return results\n .map((r) =>\n r.count > 0\n ? `Replaced ${r.count} occurrence(s) of: ${r.find.slice(0, 80)}`\n : `NOT FOUND (0 occurrences — re-read the document and retry with exact text): ${r.find.slice(0, 120)}`,\n )\n .join('\\n')\n },\n }),\n write_document: tool({\n description:\n 'Replace the entire document with new HTML content (drafting from scratch or a full rewrite).',\n inputSchema: z.object({ html: z.string().min(20).max(400_000) }),\n execute: async ({ html }) => {\n await args.tools.writeDocument(html)\n docChanged = true\n actions.push('Wrote the document')\n return 'The document now contains the provided content.'\n },\n }),\n }\n\n const messages: ModelMessage[] = args.messages\n .slice(-20)\n .map((m) => ({ role: m.role, content: m.content }))\n\n const result = await generateText({\n model,\n system: DOC_AGENT_SYSTEM + orgContext + docContext,\n messages,\n tools,\n stopWhen: stepCountIs(args.maxSteps ?? 8),\n temperature: 0.4,\n })\n\n return {\n text: result.text.trim() || (docChanged ? 'Done.' : ''),\n actions,\n docChanged,\n }\n}\n"]}
|
package/extract.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type AiConfig } from './client.js';
|
|
3
|
+
export declare const entryMetaSchema: z.ZodObject<{
|
|
4
|
+
summary: z.ZodString;
|
|
5
|
+
tags: z.ZodArray<z.ZodString>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
export type EntryMeta = z.infer<typeof entryMetaSchema>;
|
|
8
|
+
/** Extract summary + tags from entry plaintext. Null when AI is unconfigured or text is too short. */
|
|
9
|
+
export declare function extractEntryMeta(config: AiConfig | null | undefined, bodyText: string): Promise<EntryMeta | null>;
|
|
10
|
+
//# sourceMappingURL=extract.d.ts.map
|
package/extract.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGlD,eAAO,MAAM,eAAe;;;iBAO1B,CAAA;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAEvD,sGAAsG;AACtG,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAiB3B"}
|
package/extract.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Structured metadata extraction from an entry's text: a bounded summary and
|
|
2
|
+
// normalized topic tags suitable for application-owned indexing.
|
|
3
|
+
import { generateObject } from 'ai';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { getModel } from './client.js';
|
|
6
|
+
import { ENTRY_WRITING_SYSTEM } from './prompts.js';
|
|
7
|
+
export const entryMetaSchema = z.object({
|
|
8
|
+
summary: z.string().describe('One short paragraph, plain language, recapping the entry'),
|
|
9
|
+
tags: z
|
|
10
|
+
.array(z.string())
|
|
11
|
+
.describe('Between 3 and 8 short lowercase topic tags grounded in the supplied text. No punctuation.'),
|
|
12
|
+
});
|
|
13
|
+
/** Extract summary + tags from entry plaintext. Null when AI is unconfigured or text is too short. */
|
|
14
|
+
export async function extractEntryMeta(config, bodyText) {
|
|
15
|
+
const model = getModel(config, 'fast');
|
|
16
|
+
if (!model)
|
|
17
|
+
return null;
|
|
18
|
+
const text = bodyText.trim();
|
|
19
|
+
if (text.length < 12)
|
|
20
|
+
return null;
|
|
21
|
+
const { object } = await generateObject({
|
|
22
|
+
model,
|
|
23
|
+
schema: entryMetaSchema,
|
|
24
|
+
system: ENTRY_WRITING_SYSTEM,
|
|
25
|
+
prompt: `Read this activity entry and extract a one-paragraph summary and topic tags. Use only information present in the text.\n\n---\n${text}`,
|
|
26
|
+
temperature: 0.2,
|
|
27
|
+
});
|
|
28
|
+
const tags = Array.from(new Set(object.tags.map((t) => t.trim().toLowerCase()).filter(Boolean))).slice(0, 8);
|
|
29
|
+
return { ...object, tags };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=extract.js.map
|
package/extract.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,iEAAiE;AAEjE,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;AAEhD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0DAA0D,CAAC;IACxF,IAAI,EAAE,CAAC;SACJ,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,CACP,2FAA2F,CAC5F;CACJ,CAAC,CAAA;AAIF,sGAAsG;AACtG,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAmC,EACnC,QAAgB;IAEhB,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;IAC5B,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAA;IAEjC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC;QACtC,KAAK;QACL,MAAM,EAAE,eAAe;QACvB,MAAM,EAAE,oBAAoB;QAC5B,MAAM,EAAE,kIAAkI,IAAI,EAAE;QAChJ,WAAW,EAAE,GAAG;KACjB,CAAC,CAAA;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CACrB,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CACxE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACb,OAAO,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,CAAA;AAC5B,CAAC","sourcesContent":["// Structured metadata extraction from an entry's text: a bounded summary and\n// normalized topic tags suitable for application-owned indexing.\n\nimport { generateObject } from 'ai'\nimport { z } from 'zod'\nimport { getModel, type AiConfig } from './client'\nimport { ENTRY_WRITING_SYSTEM } from './prompts'\n\nexport const entryMetaSchema = z.object({\n summary: z.string().describe('One short paragraph, plain language, recapping the entry'),\n tags: z\n .array(z.string())\n .describe(\n 'Between 3 and 8 short lowercase topic tags grounded in the supplied text. No punctuation.',\n ),\n})\n\nexport type EntryMeta = z.infer<typeof entryMetaSchema>\n\n/** Extract summary + tags from entry plaintext. Null when AI is unconfigured or text is too short. */\nexport async function extractEntryMeta(\n config: AiConfig | null | undefined,\n bodyText: string,\n): Promise<EntryMeta | null> {\n const model = getModel(config, 'fast')\n if (!model) return null\n const text = bodyText.trim()\n if (text.length < 12) return null\n\n const { object } = await generateObject({\n model,\n schema: entryMetaSchema,\n system: ENTRY_WRITING_SYSTEM,\n prompt: `Read this activity entry and extract a one-paragraph summary and topic tags. Use only information present in the text.\\n\\n---\\n${text}`,\n temperature: 0.2,\n })\n const tags = Array.from(\n new Set(object.tags.map((t) => t.trim().toLowerCase()).filter(Boolean)),\n ).slice(0, 8)\n return { ...object, tags }\n}\n"]}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { AgentDisabledError, DEFAULT_AGENT_MAX_STEPS, normalizeAgentMaxSteps, runAgentTurn, type AgentModelTier, type AgentTurnResult, type RunAgentTurnArgs, } from './agent.js';
|
|
2
|
+
export { tool, type LanguageModel, type ModelMessage, type ToolSet, type UIMessage } from 'ai';
|
|
3
|
+
export * from './client.js';
|
|
4
|
+
export * from './models.js';
|
|
5
|
+
export * from './prompts.js';
|
|
6
|
+
export * from './writing.js';
|
|
7
|
+
export * from './doc-chat.js';
|
|
8
|
+
export * from './extract.js';
|
|
9
|
+
export * from './vision.js';
|
|
10
|
+
export * from './digest.js';
|
|
11
|
+
export * from './analysis.js';
|
|
12
|
+
export * from './builder.js';
|
|
13
|
+
export * from './context.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,EACZ,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,IAAI,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,IAAI,CAAA;AAC9F,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA"}
|
package/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { AgentDisabledError, DEFAULT_AGENT_MAX_STEPS, normalizeAgentMaxSteps, runAgentTurn, } from './agent.js';
|
|
2
|
+
export { tool } from 'ai';
|
|
3
|
+
export * from './client.js';
|
|
4
|
+
export * from './models.js';
|
|
5
|
+
export * from './prompts.js';
|
|
6
|
+
export * from './writing.js';
|
|
7
|
+
export * from './doc-chat.js';
|
|
8
|
+
export * from './extract.js';
|
|
9
|
+
export * from './vision.js';
|
|
10
|
+
export * from './digest.js';
|
|
11
|
+
export * from './analysis.js';
|
|
12
|
+
export * from './builder.js';
|
|
13
|
+
export * from './context.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,GAIb,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,IAAI,EAAuE,MAAM,IAAI,CAAA;AAC9F,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA","sourcesContent":["export {\n AgentDisabledError,\n DEFAULT_AGENT_MAX_STEPS,\n normalizeAgentMaxSteps,\n runAgentTurn,\n type AgentModelTier,\n type AgentTurnResult,\n type RunAgentTurnArgs,\n} from './agent'\nexport { tool, type LanguageModel, type ModelMessage, type ToolSet, type UIMessage } from 'ai'\nexport * from './client'\nexport * from './models'\nexport * from './prompts'\nexport * from './writing'\nexport * from './doc-chat'\nexport * from './extract'\nexport * from './vision'\nexport * from './digest'\nexport * from './analysis'\nexport * from './builder'\nexport * from './context'\n"]}
|
package/models.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type AiConfig } from './client.js';
|
|
2
|
+
export type ModelListItem = {
|
|
3
|
+
id: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Fetch the available model ids for a provider config. Throws on HTTP / auth
|
|
8
|
+
* errors so the caller can surface the provider's message.
|
|
9
|
+
*/
|
|
10
|
+
export declare function listModels(config: AiConfig): Promise<ModelListItem[]>;
|
|
11
|
+
//# sourceMappingURL=models.d.ts.map
|
package/models.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmC,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGzE,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAoD1D;;;GAGG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA4D3E"}
|
package/models.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Model discovery — queries each provider's "list models" endpoint so the
|
|
2
|
+
// settings UI can offer dynamic dropdowns instead of free-text model ids.
|
|
3
|
+
// Runs server-side only (the API key never leaves the server).
|
|
4
|
+
import { providerSpec, validateAiBaseUrl } from './client.js';
|
|
5
|
+
import { secureFetch } from '@braedonsaunders/appkit-sync/egress';
|
|
6
|
+
function trimSlash(u) {
|
|
7
|
+
let end = u.length;
|
|
8
|
+
while (end > 0 && u.charCodeAt(end - 1) === 47)
|
|
9
|
+
end -= 1;
|
|
10
|
+
return u.slice(0, end);
|
|
11
|
+
}
|
|
12
|
+
function dedupeSort(items) {
|
|
13
|
+
const map = new Map();
|
|
14
|
+
for (const it of items) {
|
|
15
|
+
if (it.id && !map.has(it.id))
|
|
16
|
+
map.set(it.id, it);
|
|
17
|
+
}
|
|
18
|
+
return [...map.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
19
|
+
}
|
|
20
|
+
async function fetchJson(url, headers) {
|
|
21
|
+
// Custom OpenAI-compatible endpoints are tenant-configured. Use the shared
|
|
22
|
+
// HTTPS/public-DNS transport so redirects, DNS rebinding, private networks,
|
|
23
|
+
// response size, and timeouts are enforced at the socket boundary.
|
|
24
|
+
const res = await secureFetch(url, {
|
|
25
|
+
headers,
|
|
26
|
+
timeoutMs: 15_000,
|
|
27
|
+
maxResponseBytes: 2 * 1024 * 1024,
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
const body = await res.text().catch(() => '');
|
|
31
|
+
throw new Error(`${res.status} ${res.statusText}${body ? ` — ${body.slice(0, 160)}` : ''}`);
|
|
32
|
+
}
|
|
33
|
+
return res.json();
|
|
34
|
+
}
|
|
35
|
+
function asArray(value) {
|
|
36
|
+
return Array.isArray(value) ? value : [];
|
|
37
|
+
}
|
|
38
|
+
function str(value) {
|
|
39
|
+
return typeof value === 'string' ? value : value == null ? '' : String(value);
|
|
40
|
+
}
|
|
41
|
+
/** OpenAI returns every model (embeddings, tts, image…) — keep chat-capable text ones. */
|
|
42
|
+
function isOpenAiChatModel(id) {
|
|
43
|
+
if (/embedding|whisper|tts|audio|dall-e|image|moderation|realtime|transcribe|search|similarity|edit|babbage|davinci/i.test(id)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return /^(gpt-|o1|o3|o4|chatgpt)/i.test(id);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fetch the available model ids for a provider config. Throws on HTTP / auth
|
|
50
|
+
* errors so the caller can surface the provider's message.
|
|
51
|
+
*/
|
|
52
|
+
export async function listModels(config) {
|
|
53
|
+
const spec = providerSpec(config.provider);
|
|
54
|
+
const key = config.apiKey;
|
|
55
|
+
switch (spec.kind) {
|
|
56
|
+
case 'anthropic': {
|
|
57
|
+
const json = await fetchJson('https://api.anthropic.com/v1/models?limit=1000', {
|
|
58
|
+
'x-api-key': key,
|
|
59
|
+
'anthropic-version': '2023-06-01',
|
|
60
|
+
});
|
|
61
|
+
const data = asArray(json?.data);
|
|
62
|
+
return dedupeSort(data.map((m) => ({
|
|
63
|
+
id: str(m.id),
|
|
64
|
+
label: m.display_name ? str(m.display_name) : undefined,
|
|
65
|
+
})));
|
|
66
|
+
}
|
|
67
|
+
case 'openai': {
|
|
68
|
+
const json = await fetchJson('https://api.openai.com/v1/models', {
|
|
69
|
+
Authorization: `Bearer ${key}`,
|
|
70
|
+
});
|
|
71
|
+
const data = asArray(json?.data);
|
|
72
|
+
return dedupeSort(data.map((m) => ({ id: str(m.id) })).filter((m) => isOpenAiChatModel(m.id)));
|
|
73
|
+
}
|
|
74
|
+
case 'google': {
|
|
75
|
+
const json = await fetchJson(`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(key)}&pageSize=1000`, {});
|
|
76
|
+
const models = asArray(json?.models);
|
|
77
|
+
return dedupeSort(models
|
|
78
|
+
.filter((m) => {
|
|
79
|
+
const methods = m.supportedGenerationMethods;
|
|
80
|
+
return Array.isArray(methods) && methods.includes('generateContent');
|
|
81
|
+
})
|
|
82
|
+
.map((m) => ({
|
|
83
|
+
id: str(m.name).replace(/^models\//, ''),
|
|
84
|
+
label: m.displayName ? str(m.displayName) : undefined,
|
|
85
|
+
})));
|
|
86
|
+
}
|
|
87
|
+
case 'openai-compatible': {
|
|
88
|
+
const baseURL = (await validateAiBaseUrl(config.provider, config.baseUrl)) || spec.baseUrl;
|
|
89
|
+
if (!baseURL)
|
|
90
|
+
throw new Error('A base URL is required to list models.');
|
|
91
|
+
const json = await fetchJson(`${trimSlash(baseURL)}/models`, {
|
|
92
|
+
Authorization: `Bearer ${key}`,
|
|
93
|
+
});
|
|
94
|
+
const root = json;
|
|
95
|
+
const data = asArray(root?.data).length ? asArray(root?.data) : asArray(root?.models);
|
|
96
|
+
return dedupeSort(data.map((m) => {
|
|
97
|
+
const id = str(m.id || m.name);
|
|
98
|
+
const name = str(m.name);
|
|
99
|
+
return { id, label: name && name !== id ? name : undefined };
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=models.js.map
|
package/models.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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","sourcesContent":["// Model discovery — queries each provider's \"list models\" endpoint so the\n// settings UI can offer dynamic dropdowns instead of free-text model ids.\n// Runs server-side only (the API key never leaves the server).\n\nimport { providerSpec, validateAiBaseUrl, type AiConfig } from './client'\nimport { secureFetch } from '@braedonsaunders/appkit-sync/egress'\n\nexport type ModelListItem = { id: string; label?: string }\n\nfunction trimSlash(u: string): string {\n let end = u.length\n while (end > 0 && u.charCodeAt(end - 1) === 47) end -= 1\n return u.slice(0, end)\n}\n\nfunction dedupeSort(items: ModelListItem[]): ModelListItem[] {\n const map = new Map<string, ModelListItem>()\n for (const it of items) {\n if (it.id && !map.has(it.id)) map.set(it.id, it)\n }\n return [...map.values()].sort((a, b) => a.id.localeCompare(b.id))\n}\n\nasync function fetchJson(url: string, headers: Record<string, string>): Promise<unknown> {\n // Custom OpenAI-compatible endpoints are tenant-configured. Use the shared\n // HTTPS/public-DNS transport so redirects, DNS rebinding, private networks,\n // response size, and timeouts are enforced at the socket boundary.\n const res = await secureFetch(url, {\n headers,\n timeoutMs: 15_000,\n maxResponseBytes: 2 * 1024 * 1024,\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new Error(`${res.status} ${res.statusText}${body ? ` — ${body.slice(0, 160)}` : ''}`)\n }\n return res.json()\n}\n\nfunction asArray(value: unknown): Record<string, unknown>[] {\n return Array.isArray(value) ? (value as Record<string, unknown>[]) : []\n}\n\nfunction str(value: unknown): string {\n return typeof value === 'string' ? value : value == null ? '' : String(value)\n}\n\n/** OpenAI returns every model (embeddings, tts, image…) — keep chat-capable text ones. */\nfunction isOpenAiChatModel(id: string): boolean {\n if (\n /embedding|whisper|tts|audio|dall-e|image|moderation|realtime|transcribe|search|similarity|edit|babbage|davinci/i.test(\n id,\n )\n ) {\n return false\n }\n return /^(gpt-|o1|o3|o4|chatgpt)/i.test(id)\n}\n\n/**\n * Fetch the available model ids for a provider config. Throws on HTTP / auth\n * errors so the caller can surface the provider's message.\n */\nexport async function listModels(config: AiConfig): Promise<ModelListItem[]> {\n const spec = providerSpec(config.provider)\n const key = config.apiKey\n\n switch (spec.kind) {\n case 'anthropic': {\n const json = await fetchJson('https://api.anthropic.com/v1/models?limit=1000', {\n 'x-api-key': key,\n 'anthropic-version': '2023-06-01',\n })\n const data = asArray((json as Record<string, unknown>)?.data)\n return dedupeSort(\n data.map((m) => ({\n id: str(m.id),\n label: m.display_name ? str(m.display_name) : undefined,\n })),\n )\n }\n case 'openai': {\n const json = await fetchJson('https://api.openai.com/v1/models', {\n Authorization: `Bearer ${key}`,\n })\n const data = asArray((json as Record<string, unknown>)?.data)\n return dedupeSort(data.map((m) => ({ id: str(m.id) })).filter((m) => isOpenAiChatModel(m.id)))\n }\n case 'google': {\n const json = await fetchJson(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(key)}&pageSize=1000`,\n {},\n )\n const models = asArray((json as Record<string, unknown>)?.models)\n return dedupeSort(\n models\n .filter((m) => {\n const methods = m.supportedGenerationMethods\n return Array.isArray(methods) && methods.includes('generateContent')\n })\n .map((m) => ({\n id: str(m.name).replace(/^models\\//, ''),\n label: m.displayName ? str(m.displayName) : undefined,\n })),\n )\n }\n case 'openai-compatible': {\n const baseURL = (await validateAiBaseUrl(config.provider, config.baseUrl)) || spec.baseUrl\n if (!baseURL) throw new Error('A base URL is required to list models.')\n const json = await fetchJson(`${trimSlash(baseURL)}/models`, {\n Authorization: `Bearer ${key}`,\n })\n const root = json as Record<string, unknown>\n const data = asArray(root?.data).length ? asArray(root?.data) : asArray(root?.models)\n return dedupeSort(\n data.map((m) => {\n const id = str(m.id || m.name)\n const name = str(m.name)\n return { id, label: name && name !== id ? name : undefined }\n }),\n )\n }\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@braedonsaunders/appkit-ai",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "App-agnostic bounded agent loop and streaming assistant interface, with provider policy supplied by the host app.",
|
|
5
|
+
"license": "AGPL-3.0-or-later",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./index.d.ts",
|
|
10
|
+
"import": "./index.js",
|
|
11
|
+
"default": "./index.js"
|
|
12
|
+
},
|
|
13
|
+
"./react": {
|
|
14
|
+
"types": "./react.d.ts",
|
|
15
|
+
"import": "./react.js",
|
|
16
|
+
"default": "./react.js"
|
|
17
|
+
},
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"main": "./index.js",
|
|
21
|
+
"types": "./index.d.ts",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@ai-sdk/anthropic": "^3.0.96",
|
|
24
|
+
"@ai-sdk/google": "^3.0.91",
|
|
25
|
+
"@ai-sdk/openai": "^3.0.84",
|
|
26
|
+
"@ai-sdk/openai-compatible": "^2.0.59",
|
|
27
|
+
"@braedonsaunders/appkit-sync": "^1.1.0",
|
|
28
|
+
"ai": "^6.0.224",
|
|
29
|
+
"zod": "^4.4.3"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@braedonsaunders/appkit-ui": "^0.2.0",
|
|
33
|
+
"lucide-react": "^1.24.0",
|
|
34
|
+
"react": "^19.0.0",
|
|
35
|
+
"react-dom": "^19.0.0",
|
|
36
|
+
"react-markdown": "^10.1.0",
|
|
37
|
+
"remark-gfm": "^4.0.1"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"@braedonsaunders/appkit-ui": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"lucide-react": {
|
|
44
|
+
"optional": true
|
|
45
|
+
},
|
|
46
|
+
"react": {
|
|
47
|
+
"optional": true
|
|
48
|
+
},
|
|
49
|
+
"react-dom": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"react-markdown": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"remark-gfm": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"author": "Braedon Saunders",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "git+https://github.com/braedonsaunders/appkit.git",
|
|
63
|
+
"directory": "packages/ai"
|
|
64
|
+
},
|
|
65
|
+
"homepage": "https://github.com/braedonsaunders/appkit/tree/main/packages/ai#readme",
|
|
66
|
+
"bugs": {
|
|
67
|
+
"url": "https://github.com/braedonsaunders/appkit/issues"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=22"
|
|
71
|
+
},
|
|
72
|
+
"keywords": [
|
|
73
|
+
"appkit",
|
|
74
|
+
"application-framework",
|
|
75
|
+
"typescript"
|
|
76
|
+
]
|
|
77
|
+
}
|
package/prompts.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const ENTRY_WRITING_SYSTEM = "You are an expert writing assistant embedded in a business application. You help people turn notes and activity records into clear, accurate professional writing.\n\nVoice and rules:\n- Plain, clear, professional language. Preserve the source's point of view and level of formality. Be concrete and specific.\n- NEVER invent facts: no names, dates, measurements, locations, or events that are not present or clearly implied in the source text.\n- Preserve domain terminology exactly unless the user asks for a rewrite of that terminology.\n- Do not add headings, preambles, or commentary unless explicitly asked. Return only the requested text.";
|
|
2
|
+
/**
|
|
3
|
+
* A system-prompt fragment that grounds generated content in the real
|
|
4
|
+
* organization name (from `AiConfig.org`), so the model uses it instead of a
|
|
5
|
+
* placeholder. Returns '' when no org name is available, so callers can append
|
|
6
|
+
* unconditionally. Reserve [PLACEHOLDER] for genuinely unknown specifics.
|
|
7
|
+
*/
|
|
8
|
+
export declare function orgContextLine(org?: {
|
|
9
|
+
name?: string | null;
|
|
10
|
+
} | null): string;
|
|
11
|
+
//# sourceMappingURL=prompts.d.ts.map
|
package/prompts.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,oBAAoB,yoBAMwE,CAAA;AAEzG;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,GAAG,MAAM,CAK5E"}
|
package/prompts.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Shared system prompts. Kept in one place so factuality and editing rules are
|
|
2
|
+
// consistent across writing assist and structured extraction.
|
|
3
|
+
export const ENTRY_WRITING_SYSTEM = `You are an expert writing assistant embedded in a business application. You help people turn notes and activity records into clear, accurate professional writing.
|
|
4
|
+
|
|
5
|
+
Voice and rules:
|
|
6
|
+
- Plain, clear, professional language. Preserve the source's point of view and level of formality. Be concrete and specific.
|
|
7
|
+
- NEVER invent facts: no names, dates, measurements, locations, or events that are not present or clearly implied in the source text.
|
|
8
|
+
- Preserve domain terminology exactly unless the user asks for a rewrite of that terminology.
|
|
9
|
+
- Do not add headings, preambles, or commentary unless explicitly asked. Return only the requested text.`;
|
|
10
|
+
/**
|
|
11
|
+
* A system-prompt fragment that grounds generated content in the real
|
|
12
|
+
* organization name (from `AiConfig.org`), so the model uses it instead of a
|
|
13
|
+
* placeholder. Returns '' when no org name is available, so callers can append
|
|
14
|
+
* unconditionally. Reserve [PLACEHOLDER] for genuinely unknown specifics.
|
|
15
|
+
*/
|
|
16
|
+
export function orgContextLine(org) {
|
|
17
|
+
const name = org?.name?.trim();
|
|
18
|
+
return name
|
|
19
|
+
? `\n\nThis is for the organization "${name}". Where content names or refers to the organization, use "${name}" — do not use a placeholder for the organization's own name.`
|
|
20
|
+
: '';
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=prompts.js.map
|
package/prompts.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8DAA8D;AAE9D,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;yGAMqE,CAAA;AAEzG;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,GAAqC;IAClE,MAAM,IAAI,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAC9B,OAAO,IAAI;QACT,CAAC,CAAC,qCAAqC,IAAI,8DAA8D,IAAI,+DAA+D;QAC5K,CAAC,CAAC,EAAE,CAAA;AACR,CAAC","sourcesContent":["// Shared system prompts. Kept in one place so factuality and editing rules are\n// consistent across writing assist and structured extraction.\n\nexport const ENTRY_WRITING_SYSTEM = `You are an expert writing assistant embedded in a business application. You help people turn notes and activity records into clear, accurate professional writing.\n\nVoice and rules:\n- Plain, clear, professional language. Preserve the source's point of view and level of formality. Be concrete and specific.\n- NEVER invent facts: no names, dates, measurements, locations, or events that are not present or clearly implied in the source text.\n- Preserve domain terminology exactly unless the user asks for a rewrite of that terminology.\n- Do not add headings, preambles, or commentary unless explicitly asked. Return only the requested text.`\n\n/**\n * A system-prompt fragment that grounds generated content in the real\n * organization name (from `AiConfig.org`), so the model uses it instead of a\n * placeholder. Returns '' when no org name is available, so callers can append\n * unconditionally. Reserve [PLACEHOLDER] for genuinely unknown specifics.\n */\nexport function orgContextLine(org?: { name?: string | null } | null): string {\n const name = org?.name?.trim()\n return name\n ? `\\n\\nThis is for the organization \"${name}\". Where content names or refers to the organization, use \"${name}\" — do not use a placeholder for the organization's own name.`\n : ''\n}\n"]}
|
package/react.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type AgentMessage = {
|
|
3
|
+
id: string;
|
|
4
|
+
role: 'user' | 'assistant' | 'system';
|
|
5
|
+
parts: unknown[];
|
|
6
|
+
};
|
|
7
|
+
export type AgentPanelLabels = {
|
|
8
|
+
title: string;
|
|
9
|
+
welcomeTitle: string;
|
|
10
|
+
welcomeDescription: string;
|
|
11
|
+
disabledTitle: string;
|
|
12
|
+
disabledDescription: string;
|
|
13
|
+
placeholder: string;
|
|
14
|
+
send: string;
|
|
15
|
+
stop: string;
|
|
16
|
+
failed: string;
|
|
17
|
+
input: string;
|
|
18
|
+
result: string;
|
|
19
|
+
};
|
|
20
|
+
export type AgentPanelProps = {
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
initialMessages?: AgentMessage[];
|
|
23
|
+
suggestions?: string[];
|
|
24
|
+
labels?: Partial<AgentPanelLabels>;
|
|
25
|
+
send?: (prompt: string, signal: AbortSignal) => Promise<Response>;
|
|
26
|
+
maxPromptCharacters?: number;
|
|
27
|
+
toolLabels?: Record<string, string>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The streaming thread/composer extracted from the sibling assistant. The app
|
|
31
|
+
* owns persistence and the HTTP transport; appkit owns UI-message decoding,
|
|
32
|
+
* cancellation, ordered part rendering, and tool cards.
|
|
33
|
+
*/
|
|
34
|
+
export declare function AgentPanel({ enabled, initialMessages, suggestions, labels: labelOverrides, send, maxPromptCharacters, toolLabels, }: AgentPanelProps): React.JSX.Element;
|
|
35
|
+
export declare function ChatMarkdown({ children }: {
|
|
36
|
+
children: string;
|
|
37
|
+
}): React.JSX.Element;
|
|
38
|
+
export declare function AgentToolCard({ name, label, state, input, output, inputLabel, resultLabel }: {
|
|
39
|
+
name: string;
|
|
40
|
+
label?: string;
|
|
41
|
+
state: string;
|
|
42
|
+
input?: unknown;
|
|
43
|
+
output?: unknown;
|
|
44
|
+
inputLabel?: string;
|
|
45
|
+
resultLabel?: string;
|
|
46
|
+
}): React.JSX.Element;
|
|
47
|
+
//# sourceMappingURL=react.d.ts.map
|
package/react.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqB9B,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;IACrC,KAAK,EAAE,OAAO,EAAE,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAgBD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,CAAC,EAAE,YAAY,EAAE,CAAA;IAChC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAClC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IACjE,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC,CAAA;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,eAAoB,EACpB,WAAgB,EAChB,MAAM,EAAE,cAAc,EACtB,IAAI,EACJ,mBAA4B,EAC5B,UAAU,GACX,EAAE,eAAe,qBA0DjB;AA+BD,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,qBAE9D;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAoB,EAAE,WAAsB,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,qBAK/O"}
|