@stll/folio-agents 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/parse.js ADDED
@@ -0,0 +1,157 @@
1
+ //#region src/parse.ts
2
+ const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3
+ const isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
4
+ /**
5
+ * Hard caps on `suggest_changes` / `add_comment` / `reply_comment` input
6
+ * size. Without these, a single tool call could ask the bridge to apply an
7
+ * unbounded number of operations, or push an arbitrarily large string into
8
+ * the tracked-changes engine, in one shot. `execute.ts` reuses
9
+ * {@link MAX_OPERATION_TEXT_LENGTH} for `reply_comment`'s text cap too, so
10
+ * the limit stays a single number shared across every text-bearing tool.
11
+ */
12
+ const MAX_OPERATIONS_PER_CALL = 50;
13
+ const MAX_OPERATION_TEXT_LENGTH = 1e5;
14
+ /** Plain-language error for a string argument over {@link MAX_OPERATION_TEXT_LENGTH}. */
15
+ const explainTextTooLong = (label, length) => `${label} is ${length.toLocaleString()} characters, over the ${MAX_OPERATION_TEXT_LENGTH.toLocaleString()}-character limit; shorten it or split it into multiple operations.`;
16
+ /**
17
+ * Validate `add_comment`'s raw tool-call arguments and build the
18
+ * `commentOnBlock` {@link FolioAIEditOperation} it applies. Pure: does not
19
+ * touch a bridge or document.
20
+ */
21
+ const parseAddCommentInput = (args) => {
22
+ if (!isPlainObject(args)) return {
23
+ ok: false,
24
+ error: "add_comment expects an object with `blockId` and `text` strings."
25
+ };
26
+ const blockId = args["blockId"];
27
+ const quote = args["quote"];
28
+ const text = args["text"];
29
+ if (!isNonEmptyString(blockId)) return {
30
+ ok: false,
31
+ error: "add_comment requires a non-empty string `blockId`."
32
+ };
33
+ if (!isNonEmptyString(text)) return {
34
+ ok: false,
35
+ error: "add_comment requires a non-empty string `text`."
36
+ };
37
+ if (text.length > 1e5) return {
38
+ ok: false,
39
+ error: explainTextTooLong("add_comment's `text`", text.length)
40
+ };
41
+ if (quote !== void 0 && typeof quote !== "string") return {
42
+ ok: false,
43
+ error: "add_comment's `quote` must be a string when provided."
44
+ };
45
+ if (typeof quote === "string" && quote.length > 1e5) return {
46
+ ok: false,
47
+ error: explainTextTooLong("add_comment's `quote`", quote.length)
48
+ };
49
+ return {
50
+ ok: true,
51
+ operation: {
52
+ id: "comment-1",
53
+ type: "commentOnBlock",
54
+ blockId,
55
+ comment: { text },
56
+ ...quote !== void 0 ? { quote } : {}
57
+ }
58
+ };
59
+ };
60
+ const OPERATION_TYPES = "replaceInBlock, insertAfterBlock, insertBeforeBlock, replaceBlock, deleteBlock";
61
+ const isOperationType = (value) => value === "replaceInBlock" || value === "insertAfterBlock" || value === "insertBeforeBlock" || value === "replaceBlock" || value === "deleteBlock";
62
+ /** Validate + map one `suggest_changes` operation, or return a plain-language error string. */
63
+ const buildSuggestedOperation = (raw, index) => {
64
+ if (!isPlainObject(raw)) return `operations[${index}] must be an object.`;
65
+ const type = raw["type"];
66
+ const blockId = raw["blockId"];
67
+ const id = raw["id"];
68
+ const comment = raw["comment"];
69
+ if (!isOperationType(type)) return `operations[${index}].type must be one of ${OPERATION_TYPES}.`;
70
+ if (!isNonEmptyString(blockId)) return `operations[${index}].blockId must be a non-empty string.`;
71
+ if (id !== void 0 && !isNonEmptyString(id)) return `operations[${index}].id must be a non-empty string when provided.`;
72
+ if (comment !== void 0 && typeof comment !== "string") return `operations[${index}].comment must be a string when provided.`;
73
+ if (typeof comment === "string" && comment.length > 1e5) return explainTextTooLong(`operations[${index}].comment`, comment.length);
74
+ const opId = isNonEmptyString(id) ? id : `op-${index + 1}`;
75
+ const commentField = typeof comment === "string" ? { comment: { text: comment } } : {};
76
+ if (type === "replaceInBlock") {
77
+ const find = raw["find"];
78
+ const replace = raw["replace"];
79
+ if (!isNonEmptyString(find)) return `operations[${index}] (replaceInBlock) requires a non-empty string \`find\`.`;
80
+ if (find.length > 1e5) return explainTextTooLong(`operations[${index}] (replaceInBlock) \`find\``, find.length);
81
+ if (typeof replace !== "string") return `operations[${index}] (replaceInBlock) requires a string \`replace\`.`;
82
+ if (replace.length > 1e5) return explainTextTooLong(`operations[${index}] (replaceInBlock) \`replace\``, replace.length);
83
+ return {
84
+ id: opId,
85
+ type,
86
+ blockId,
87
+ find,
88
+ replace,
89
+ ...commentField
90
+ };
91
+ }
92
+ if (type === "insertAfterBlock" || type === "insertBeforeBlock") {
93
+ const text = raw["text"];
94
+ if (!isNonEmptyString(text)) return `operations[${index}] (${type}) requires a non-empty string \`text\`.`;
95
+ if (text.length > 1e5) return explainTextTooLong(`operations[${index}] (${type}) \`text\``, text.length);
96
+ return {
97
+ id: opId,
98
+ type,
99
+ blockId,
100
+ text,
101
+ ...commentField
102
+ };
103
+ }
104
+ if (type === "replaceBlock") {
105
+ const text = raw["text"];
106
+ if (!isNonEmptyString(text)) return "operations[" + index + "] (replaceBlock) requires a non-empty string `text`.";
107
+ if (text.length > 1e5) return explainTextTooLong(`operations[${index}] (replaceBlock) \`text\``, text.length);
108
+ return {
109
+ id: opId,
110
+ type,
111
+ blockId,
112
+ text,
113
+ ...commentField
114
+ };
115
+ }
116
+ return {
117
+ id: opId,
118
+ type,
119
+ blockId,
120
+ ...commentField
121
+ };
122
+ };
123
+ /**
124
+ * Validate `suggest_changes`' raw tool-call arguments and build the
125
+ * {@link FolioAIEditOperation}s it applies. Pure: does not touch a bridge or
126
+ * document.
127
+ */
128
+ const parseSuggestChangesInput = (args) => {
129
+ if (!isPlainObject(args) || !Array.isArray(args["operations"])) return {
130
+ ok: false,
131
+ error: "suggest_changes requires an `operations` array."
132
+ };
133
+ const rawOperations = args["operations"];
134
+ if (rawOperations.length === 0) return {
135
+ ok: false,
136
+ error: "suggest_changes' `operations` array must not be empty."
137
+ };
138
+ if (rawOperations.length > 50) return {
139
+ ok: false,
140
+ error: `suggest_changes' \`operations\` array has ${rawOperations.length.toLocaleString()} entries, over the 50-operation limit; batch it across multiple suggest_changes calls.`
141
+ };
142
+ const operations = [];
143
+ for (const [index, raw] of rawOperations.entries()) {
144
+ const built = buildSuggestedOperation(raw, index);
145
+ if (typeof built === "string") return {
146
+ ok: false,
147
+ error: built
148
+ };
149
+ operations.push(built);
150
+ }
151
+ return {
152
+ ok: true,
153
+ operations
154
+ };
155
+ };
156
+ //#endregion
157
+ export { MAX_OPERATIONS_PER_CALL, MAX_OPERATION_TEXT_LENGTH, explainTextTooLong, parseAddCommentInput, parseSuggestChangesInput };
@@ -0,0 +1,24 @@
1
+ import { FolioAgentToolDefinition } from "./types.js";
2
+
3
+ //#region src/providers.d.ts
4
+ /** Anthropic Messages API tool-definition shape (`input_schema`, snake_case). */
5
+ type AnthropicToolDefinition = {
6
+ name: string;
7
+ description: string;
8
+ input_schema: Record<string, unknown>;
9
+ };
10
+ /** Map folio's provider-neutral tool definitions onto Anthropic's tool shape. */
11
+ declare const toAnthropicTools: (definitions: FolioAgentToolDefinition[]) => AnthropicToolDefinition[];
12
+ /** OpenAI Chat Completions / Responses API function-tool shape. */
13
+ type OpenAIToolDefinition = {
14
+ type: "function";
15
+ function: {
16
+ name: string;
17
+ description: string;
18
+ parameters: Record<string, unknown>;
19
+ };
20
+ };
21
+ /** Map folio's provider-neutral tool definitions onto OpenAI's function-tool shape. */
22
+ declare const toOpenAITools: (definitions: FolioAgentToolDefinition[]) => OpenAIToolDefinition[];
23
+ //#endregion
24
+ export { AnthropicToolDefinition, OpenAIToolDefinition, toAnthropicTools, toOpenAITools };
@@ -0,0 +1,18 @@
1
+ //#region src/providers.ts
2
+ /** Map folio's provider-neutral tool definitions onto Anthropic's tool shape. */
3
+ const toAnthropicTools = (definitions) => definitions.map((definition) => ({
4
+ name: definition.name,
5
+ description: definition.description,
6
+ input_schema: definition.inputSchema
7
+ }));
8
+ /** Map folio's provider-neutral tool definitions onto OpenAI's function-tool shape. */
9
+ const toOpenAITools = (definitions) => definitions.map((definition) => ({
10
+ type: "function",
11
+ function: {
12
+ name: definition.name,
13
+ description: definition.description,
14
+ parameters: definition.inputSchema
15
+ }
16
+ }));
17
+ //#endregion
18
+ export { toAnthropicTools, toOpenAITools };
@@ -0,0 +1,16 @@
1
+ import { FolioAgentToolDefinition } from "./types.js";
2
+
3
+ //#region src/tools.d.ts
4
+ /**
5
+ * The tools this package exposes, described for an LLM. Every tool that reads
6
+ * or mutates the document expects `blockId` values that came from
7
+ * `read_document` or `find_text` in THIS conversation — block ids are not
8
+ * guessable and change whenever the document's structure changes. Every
9
+ * mutation (`add_comment`, `suggest_changes`) becomes a tracked change or
10
+ * comment pending human review; nothing is silently finalized.
11
+ */
12
+ declare const FOLIO_AGENT_TOOLS: FolioAgentToolDefinition[];
13
+ /** The tool definitions this package exposes. Same array as {@link FOLIO_AGENT_TOOLS}, as a function for symmetry with the other providers. */
14
+ declare const getFolioToolDefinitions: () => FolioAgentToolDefinition[];
15
+ //#endregion
16
+ export { FOLIO_AGENT_TOOLS, getFolioToolDefinitions };
package/dist/tools.js ADDED
@@ -0,0 +1,224 @@
1
+ import { FOLIO_AGENT_TOOL_NAMES } from "./types.js";
2
+ /**
3
+ * The tools this package exposes, described for an LLM. Every tool that reads
4
+ * or mutates the document expects `blockId` values that came from
5
+ * `read_document` or `find_text` in THIS conversation — block ids are not
6
+ * guessable and change whenever the document's structure changes. Every
7
+ * mutation (`add_comment`, `suggest_changes`) becomes a tracked change or
8
+ * comment pending human review; nothing is silently finalized.
9
+ */
10
+ const FOLIO_AGENT_TOOLS = [
11
+ {
12
+ name: FOLIO_AGENT_TOOL_NAMES.readDocument,
13
+ description: "Read the full document body as a list of blocks (paragraphs, headings, list items). Call this first, or whenever you need fresh block ids after a mutation — block ids from a stale read may no longer resolve.",
14
+ inputSchema: {
15
+ type: "object",
16
+ properties: {},
17
+ required: [],
18
+ additionalProperties: false
19
+ }
20
+ },
21
+ {
22
+ name: FOLIO_AGENT_TOOL_NAMES.findText,
23
+ description: "Search the document body for a text string and return `{ matches, truncated, totalMatches }`, where each match has its block id, which occurrence within that block it is, and surrounding context. `matches` is capped at 200 entries; `truncated` is true and `totalMatches` reports the real count when there were more — narrow the query instead of assuming you saw every hit. Use this to locate the blockId for a known phrase instead of reading the whole document.",
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ query: {
28
+ type: "string",
29
+ description: "Non-empty text to search for, up to 1,000 characters."
30
+ },
31
+ matchCase: {
32
+ type: "boolean",
33
+ description: "Case-sensitive match. Defaults to false (case-insensitive)."
34
+ }
35
+ },
36
+ required: ["query"],
37
+ additionalProperties: false
38
+ }
39
+ },
40
+ {
41
+ name: FOLIO_AGENT_TOOL_NAMES.readComments,
42
+ description: "Read comment threads in the document, each with its author, text, resolved status, anchored block, and replies. Filter to unresolved (\"open\") comments to see what still needs attention.",
43
+ inputSchema: {
44
+ type: "object",
45
+ properties: { filter: {
46
+ type: "string",
47
+ enum: [
48
+ "all",
49
+ "open",
50
+ "resolved"
51
+ ],
52
+ description: "Which comments to return. Defaults to \"all\"."
53
+ } },
54
+ required: [],
55
+ additionalProperties: false
56
+ }
57
+ },
58
+ {
59
+ name: FOLIO_AGENT_TOOL_NAMES.readChanges,
60
+ description: "Read pending tracked changes (insertions and deletions) awaiting human review. Use this to see the effect of edits already suggested via `suggest_changes` before proposing more.",
61
+ inputSchema: {
62
+ type: "object",
63
+ properties: {},
64
+ required: [],
65
+ additionalProperties: false
66
+ }
67
+ },
68
+ {
69
+ name: FOLIO_AGENT_TOOL_NAMES.addComment,
70
+ description: "Attach a comment to a block, optionally quoting the specific text it is about. The comment is added immediately (comments are not tracked changes) but the underlying text is left untouched — use this for notes/questions, and `suggest_changes` for edits. `text` and `quote` are each capped at 100,000 characters.",
71
+ inputSchema: {
72
+ type: "object",
73
+ properties: {
74
+ blockId: {
75
+ type: "string",
76
+ description: "The block to comment on, from `read_document` or `find_text`."
77
+ },
78
+ quote: {
79
+ type: "string",
80
+ description: "Optional exact text within the block this comment is about, up to 100,000 characters."
81
+ },
82
+ text: {
83
+ type: "string",
84
+ description: "The comment body, up to 100,000 characters."
85
+ }
86
+ },
87
+ required: ["blockId", "text"],
88
+ additionalProperties: false
89
+ }
90
+ },
91
+ {
92
+ name: FOLIO_AGENT_TOOL_NAMES.suggestChanges,
93
+ description: "Propose one or more edits as tracked changes for a human to accept or reject — nothing is applied directly to the visible text. Each operation needs a blockId from `read_document` or `find_text`; if the document changed since that read, re-read it and retry with fresh ids (a skip reason will say so). At most 50 operations per call — batch larger edits across multiple calls. Each `find` / `replace` / `text` / `comment` string is capped at 100,000 characters.",
94
+ inputSchema: {
95
+ type: "object",
96
+ properties: { operations: {
97
+ type: "array",
98
+ description: "The edits to propose, applied in order. At most 50 per call.",
99
+ items: {
100
+ type: "object",
101
+ properties: {
102
+ id: {
103
+ type: "string",
104
+ description: "Optional caller-supplied operation id, echoed back in `applied`/`skipped`. Auto-generated (op-1, op-2, …) when omitted."
105
+ },
106
+ type: {
107
+ type: "string",
108
+ enum: [
109
+ "replaceInBlock",
110
+ "insertAfterBlock",
111
+ "insertBeforeBlock",
112
+ "replaceBlock",
113
+ "deleteBlock"
114
+ ],
115
+ description: "The kind of edit to make."
116
+ },
117
+ blockId: {
118
+ type: "string",
119
+ description: "The block to edit, from `read_document` or `find_text`."
120
+ },
121
+ find: {
122
+ type: "string",
123
+ description: "Required for `replaceInBlock`: the exact text to find within the block, up to 100,000 characters."
124
+ },
125
+ replace: {
126
+ type: "string",
127
+ description: "Required for `replaceInBlock`: the text to replace `find` with, up to 100,000 characters."
128
+ },
129
+ text: {
130
+ type: "string",
131
+ description: "Required for `insertAfterBlock` / `insertBeforeBlock` / `replaceBlock`: the text to insert or replace the block with, up to 100,000 characters."
132
+ },
133
+ comment: {
134
+ type: "string",
135
+ description: "Optional comment explaining this edit, attached to the affected text, up to 100,000 characters."
136
+ }
137
+ },
138
+ required: ["type", "blockId"],
139
+ additionalProperties: false
140
+ }
141
+ } },
142
+ required: ["operations"],
143
+ additionalProperties: false
144
+ }
145
+ },
146
+ {
147
+ name: FOLIO_AGENT_TOOL_NAMES.replyComment,
148
+ description: "Reply to an existing comment thread, referenced by the id from `read_comments`. `text` is capped at 100,000 characters.",
149
+ inputSchema: {
150
+ type: "object",
151
+ properties: {
152
+ commentId: {
153
+ type: "string",
154
+ description: "The comment id from `read_comments`."
155
+ },
156
+ text: {
157
+ type: "string",
158
+ description: "The reply body, up to 100,000 characters."
159
+ }
160
+ },
161
+ required: ["commentId", "text"],
162
+ additionalProperties: false
163
+ }
164
+ },
165
+ {
166
+ name: FOLIO_AGENT_TOOL_NAMES.resolveComment,
167
+ description: "Mark a comment thread resolved, or pass `reopen: true` to reopen a previously resolved one.",
168
+ inputSchema: {
169
+ type: "object",
170
+ properties: {
171
+ commentId: {
172
+ type: "string",
173
+ description: "The comment id from `read_comments`."
174
+ },
175
+ reopen: {
176
+ type: "boolean",
177
+ description: "Reopen an already-resolved thread instead of resolving it."
178
+ }
179
+ },
180
+ required: ["commentId"],
181
+ additionalProperties: false
182
+ }
183
+ },
184
+ {
185
+ name: FOLIO_AGENT_TOOL_NAMES.readPage,
186
+ description: "Read the plain text of one page (1-based) as currently paginated in the live editor. Only available when the document is open in a live, paginated editor surface — not on a headless document.",
187
+ inputSchema: {
188
+ type: "object",
189
+ properties: { page: {
190
+ type: "number",
191
+ description: "1-based page number."
192
+ } },
193
+ required: ["page"],
194
+ additionalProperties: false
195
+ }
196
+ },
197
+ {
198
+ name: FOLIO_AGENT_TOOL_NAMES.readSelection,
199
+ description: "Read the user's current text selection in the live editor, as plain text. Only available on a live editor surface with an active selection.",
200
+ inputSchema: {
201
+ type: "object",
202
+ properties: {},
203
+ required: [],
204
+ additionalProperties: false
205
+ }
206
+ },
207
+ {
208
+ name: FOLIO_AGENT_TOOL_NAMES.scrollToBlock,
209
+ description: "Scroll the live editor to the given block and select it, so the user can see what you are discussing. Only available on a live editor surface.",
210
+ inputSchema: {
211
+ type: "object",
212
+ properties: { blockId: {
213
+ type: "string",
214
+ description: "The block to scroll to, from `read_document` or `find_text`."
215
+ } },
216
+ required: ["blockId"],
217
+ additionalProperties: false
218
+ }
219
+ }
220
+ ];
221
+ /** The tool definitions this package exposes. Same array as {@link FOLIO_AGENT_TOOLS}, as a function for symmetry with the other providers. */
222
+ const getFolioToolDefinitions = () => FOLIO_AGENT_TOOLS;
223
+ //#endregion
224
+ export { FOLIO_AGENT_TOOLS, getFolioToolDefinitions };
@@ -0,0 +1,115 @@
1
+ //#region src/types.d.ts
2
+ /**
3
+ * Provider-neutral tool-layer types. `FolioAgentToolDefinition` describes a
4
+ * tool the same way every model provider ends up wanting it described (name +
5
+ * prose description + a JSON-Schema-shaped input schema); `providers.ts` maps
6
+ * that single shape onto each SDK's own tool-definition envelope.
7
+ */
8
+ /** Every tool name this package exposes, as a stable string union (no enums). */
9
+ declare const FOLIO_AGENT_TOOL_NAMES: {
10
+ readonly readDocument: "read_document";
11
+ readonly findText: "find_text";
12
+ readonly readComments: "read_comments";
13
+ readonly readChanges: "read_changes";
14
+ readonly addComment: "add_comment";
15
+ readonly suggestChanges: "suggest_changes";
16
+ readonly replyComment: "reply_comment";
17
+ readonly resolveComment: "resolve_comment";
18
+ readonly readPage: "read_page";
19
+ readonly readSelection: "read_selection";
20
+ readonly scrollToBlock: "scroll_to_block";
21
+ };
22
+ type FolioAgentToolName = (typeof FOLIO_AGENT_TOOL_NAMES)[keyof typeof FOLIO_AGENT_TOOL_NAMES];
23
+ /**
24
+ * A provider-neutral tool definition: name, an LLM-facing description (when to
25
+ * call it, what it returns), and a JSON-Schema (draft-07-ish) object schema for
26
+ * its arguments. `providers.ts` maps a list of these onto Anthropic's or
27
+ * OpenAI's tool-definition shape.
28
+ */
29
+ type FolioAgentToolDefinition = {
30
+ name: FolioAgentToolName;
31
+ description: string;
32
+ inputSchema: Record<string, unknown>;
33
+ };
34
+ /**
35
+ * Result of executing one tool call. `ok: false` covers every EXPECTED failure
36
+ * (bad arguments, an unknown tool, a capability the bridge does not support, a
37
+ * domain-level skip) — never throw for these, since the whole point is to feed
38
+ * the reason back to the model so it can retry or adjust. `executeFolioToolCall`
39
+ * is the only boundary layer allowed to catch an unexpected throw and fold it
40
+ * into this shape too.
41
+ */
42
+ type FolioToolCallResult = {
43
+ ok: true;
44
+ result: unknown;
45
+ } | {
46
+ ok: false;
47
+ error: string;
48
+ };
49
+ /** One document block as exposed to a model: id, kind, and its plain text. */
50
+ type FolioAgentBlock = {
51
+ blockId: string;
52
+ kind: string;
53
+ text: string;
54
+ };
55
+ /** One `find_text` match: which block, which occurrence within it, and a text window around the match. */
56
+ type FolioAgentTextMatch = {
57
+ blockId: string; /** 0-based index of this occurrence within its block (multiple matches in one block increment this). */
58
+ occurrenceInBlock: number; /** The match plus ~40 characters of surrounding context on each side. */
59
+ context: string;
60
+ };
61
+ /**
62
+ * Result of {@link FOLIO_AGENT_TOOL_NAMES.findText}. `matches` is capped at a
63
+ * fixed limit; when the query has more hits than that, `truncated` is `true`
64
+ * and `totalMatches` reports the real count so the model knows to narrow the
65
+ * query instead of assuming it saw everything.
66
+ */
67
+ type FolioAgentFindTextResult = {
68
+ matches: FolioAgentTextMatch[];
69
+ truncated: boolean;
70
+ totalMatches: number;
71
+ };
72
+ /** One reply within a comment thread. */
73
+ type FolioAgentCommentReply = {
74
+ id: string;
75
+ author: string;
76
+ text: string;
77
+ };
78
+ /**
79
+ * A comment thread, shaped to match what {@link FolioDocxReviewer.getComments}
80
+ * (from `@stll/folio-core/server`) returns, minus fields a tool-calling model
81
+ * has no use for (raw dates). `resolved` mirrors the underlying model's
82
+ * `done` flag; `quote` mirrors `anchoredText` (the text the comment is
83
+ * attached to, empty when unavailable).
84
+ */
85
+ type FolioAgentComment = {
86
+ id: string;
87
+ author: string;
88
+ text: string;
89
+ resolved: boolean;
90
+ blockId: string | null;
91
+ quote: string;
92
+ replies: FolioAgentCommentReply[];
93
+ };
94
+ /** A pending tracked change (insertion or deletion), shaped to match {@link FolioDocxReviewer.getChanges}. */
95
+ type FolioAgentChange = {
96
+ id: string;
97
+ type: "insertion" | "deletion";
98
+ author: string;
99
+ text: string;
100
+ blockId: string | null;
101
+ };
102
+ /** Filter for {@link FOLIO_AGENT_TOOL_NAMES.readComments}. */
103
+ type FolioAgentCommentFilter = "all" | "open" | "resolved";
104
+ /** Result of {@link FOLIO_AGENT_TOOL_NAMES.addComment} / `suggest_changes`. */
105
+ type FolioAgentApplyOperationsSummary = {
106
+ applied: {
107
+ id: string;
108
+ }[];
109
+ skipped: {
110
+ id: string;
111
+ reason: string;
112
+ }[];
113
+ };
114
+ //#endregion
115
+ export { FOLIO_AGENT_TOOL_NAMES, FolioAgentApplyOperationsSummary, FolioAgentBlock, FolioAgentChange, FolioAgentComment, FolioAgentCommentFilter, FolioAgentCommentReply, FolioAgentFindTextResult, FolioAgentTextMatch, FolioAgentToolDefinition, FolioAgentToolName, FolioToolCallResult };
package/dist/types.js ADDED
@@ -0,0 +1,23 @@
1
+ //#region src/types.ts
2
+ /**
3
+ * Provider-neutral tool-layer types. `FolioAgentToolDefinition` describes a
4
+ * tool the same way every model provider ends up wanting it described (name +
5
+ * prose description + a JSON-Schema-shaped input schema); `providers.ts` maps
6
+ * that single shape onto each SDK's own tool-definition envelope.
7
+ */
8
+ /** Every tool name this package exposes, as a stable string union (no enums). */
9
+ const FOLIO_AGENT_TOOL_NAMES = {
10
+ readDocument: "read_document",
11
+ findText: "find_text",
12
+ readComments: "read_comments",
13
+ readChanges: "read_changes",
14
+ addComment: "add_comment",
15
+ suggestChanges: "suggest_changes",
16
+ replyComment: "reply_comment",
17
+ resolveComment: "resolve_comment",
18
+ readPage: "read_page",
19
+ readSelection: "read_selection",
20
+ scrollToBlock: "scroll_to_block"
21
+ };
22
+ //#endregion
23
+ export { FOLIO_AGENT_TOOL_NAMES };
package/package.json CHANGED
@@ -1 +1,61 @@
1
- {"name":"@stll/folio-agents","version":"0.0.1","description":"Placeholder. Real releases publish from stella/folio CI via OIDC trusted publishing.","license":"Apache-2.0"}
1
+ {
2
+ "name": "@stll/folio-agents",
3
+ "version": "0.1.0",
4
+ "description": "Framework-neutral LLM tool layer over folio's ai-edits engine: function-calling tools so an agent can read and mutate .docx documents through @stll/folio-core.",
5
+ "keywords": [
6
+ "agent",
7
+ "ai",
8
+ "docx",
9
+ "function-calling",
10
+ "llm",
11
+ "ooxml",
12
+ "tanstack-intent",
13
+ "tools",
14
+ "word"
15
+ ],
16
+ "homepage": "https://github.com/stella/folio",
17
+ "bugs": {
18
+ "url": "https://github.com/stella/folio/issues"
19
+ },
20
+ "license": "Apache-2.0",
21
+ "author": "STLL",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/stella/folio.git",
25
+ "directory": "packages/agents"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "skills",
30
+ "README.md",
31
+ "LICENSE",
32
+ "NOTICE.md"
33
+ ],
34
+ "type": "module",
35
+ "sideEffects": false,
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js"
40
+ },
41
+ "./*": {
42
+ "types": "./dist/*.d.ts",
43
+ "import": "./dist/*.js"
44
+ }
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "scripts": {
50
+ "build": "rm -rf dist && tsdown",
51
+ "prepack": "bun run build",
52
+ "pack:dry-run": "bun pm pack --dry-run",
53
+ "typecheck": "tsgo --noEmit -p tsconfig.build.json",
54
+ "test": "bun test src"
55
+ },
56
+ "dependencies": {
57
+ "@stll/folio-core": "^0.2.0"
58
+ },
59
+ "main": "./dist/index.js",
60
+ "types": "./dist/index.d.ts"
61
+ }