@stll/folio-agents
Version:
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.
209 lines • 8.32 kB
TypeScript
import { FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FolioAITextRangeHandle, FolioDocumentOperationIssue, FolioDocumentOperationReceipt, FolioDocumentOutlineEntry, FolioDocumentSectionHandle, FolioDocumentStoryHandle, FolioReviewChange } from "@stll/folio-core/server";
//#region src/types.d.ts
/**
* Provider-neutral tool-layer types. `FolioAgentToolDefinition` describes a
* tool the same way every model provider ends up wanting it described (name +
* prose description + a JSON-Schema-shaped input schema); `providers.ts` maps
* that single shape onto each SDK's own tool-definition envelope.
*/
declare const folioAgentToolDefinitionBrand: unique symbol;
type FolioAgentJsonObjectSchema = {
readonly type: "object";
readonly properties: Record<string, unknown>;
readonly required: readonly string[];
readonly additionalProperties: false;
} & Record<string, unknown>;
/** Every tool name this package exposes, as a stable string union (no enums). */
declare const FOLIO_AGENT_TOOL_NAMES: {
readonly readDocument: "read_document";
readonly getDocumentOutline: "get_document_outline";
readonly readSection: "read_section";
readonly listStories: "list_stories";
readonly readStory: "read_story";
readonly findText: "find_text";
readonly readComments: "read_comments";
readonly readChanges: "read_changes";
readonly addComment: "add_comment";
readonly suggestChanges: "suggest_changes";
readonly replyComment: "reply_comment";
readonly resolveComment: "resolve_comment";
readonly readPage: "read_page";
readonly readSelection: "read_selection";
readonly scrollToBlock: "scroll_to_block";
readonly showInDocument: "show_in_document";
};
type FolioAgentToolName = (typeof FOLIO_AGENT_TOOL_NAMES)[keyof typeof FOLIO_AGENT_TOOL_NAMES];
/**
* A provider-neutral tool definition: name, an LLM-facing description (when to
* call it, what it returns), and a JSON-Schema (draft-07-ish) object schema for
* its arguments. `providers.ts` maps a list of these onto Anthropic's or
* OpenAI's tool-definition shape.
*/
type FolioAgentToolDefinition = {
name: FolioAgentToolName;
description: string;
inputSchema: Record<string, unknown>;
};
type FolioAgentTypedToolDefinition<TName extends FolioAgentToolName = FolioAgentToolName, TInput = unknown, TOutput = unknown, TSchema extends FolioAgentJsonObjectSchema = FolioAgentJsonObjectSchema> = FolioAgentToolDefinition & {
readonly name: TName;
readonly inputSchema: TSchema;
readonly [folioAgentToolDefinitionBrand]?: {
readonly input: TInput;
readonly output: TOutput;
};
};
/**
* Result of executing one tool call. `ok: false` covers every EXPECTED failure
* (bad arguments, an unknown tool, a capability the bridge does not support, a
* domain-level skip) — never throw for these, since the whole point is to feed
* the reason back to the model so it can retry or adjust. `executeFolioToolCall`
* is the only boundary layer allowed to catch an unexpected throw and fold it
* into this shape too.
*/
type FolioToolCallResult<TResult = unknown> = {
ok: true;
result: TResult;
} | {
ok: false;
error: string;
};
/** One document block as exposed to a model: id, kind, and its plain text. */
type FolioAgentBlock = {
blockId: string;
kind: string;
text: string;
/**
* Normalized-text hash of this block at read time. Echo it back as
* `precondition.blockTextHash` on a `suggest_changes` / `add_comment`
* operation targeting this block so the apply call is guarded against
* edits made to the document between this read and that apply — without
* it, only same-call staleness within one `suggest_changes` batch is
* caught.
*/
blockTextHash: string;
};
/** One main-story `find_text` match. Existing consumers can keep using its block and range directly. */
type FolioAgentTextMatch = {
/** Present on results from the current executor; optional for source compatibility with older hosts. */
type?: "main";
story?: {
type: "main";
};
blockId: string;
/** Normalized-text hash of the whole containing block; see {@link FolioAgentBlock.blockTextHash}. */
blockTextHash: string;
/** Stable handle that can be passed directly to `show_in_document` or a range operation. */
range: FolioAITextRangeHandle;
/** 0-based index of this occurrence within its block. */
occurrenceInBlock: number;
/** Real rendered page when a live paginated surface supplies it. */
page?: number;
context: string;
};
type FolioAgentStoryTextMatch = {
type: "story";
story: Exclude<FolioDocumentStoryHandle, {
type: "main";
}>;
startOffset: number;
endOffset: number;
/** 0-based index of this occurrence within the story. */
occurrenceInStory: number;
context: string;
};
type FolioAgentOutlineEntry = FolioDocumentOutlineEntry & {
page?: number;
};
type FolioAgentDocumentOutline = {
sections: FolioAgentOutlineEntry[];
totalSections: number;
truncated: boolean;
};
type FolioAgentSectionRead = {
handle: FolioDocumentSectionHandle;
heading: FolioDocumentOutlineEntry;
blocks: FolioAgentBlock[];
totalBlocks: number;
truncated: boolean;
nextAfterBlockId?: string;
};
/**
* Result of {@link FOLIO_AGENT_TOOL_NAMES.findText}. `matches` is capped at a
* fixed limit; when the query has more hits than that, `truncated` is `true`
* and `totalMatches` reports the real count so the model knows to narrow the
* query instead of assuming it saw everything.
*/
type FolioAgentFindTextResult = {
matches: FolioAgentTextMatch[];
truncated: boolean;
totalMatches: number;
};
type FolioAgentStoryFindTextResult = {
matches: FolioAgentStoryTextMatch[];
truncated: boolean;
totalMatches: number;
};
type FolioAgentScopedFindTextResult = FolioAgentFindTextResult | FolioAgentStoryFindTextResult;
/** One reply within a comment thread. */
type FolioAgentCommentReply = {
id: string;
author: string;
text: string;
};
/**
* A comment thread, shaped to match what {@link FolioDocxReviewer.getComments}
* (from `@stll/folio-core/server`) returns, minus fields a tool-calling model
* has no use for (raw dates). `resolved` mirrors the underlying model's
* `done` flag; `quote` mirrors `anchoredText` (the text the comment is
* attached to, empty when unavailable).
*/
type FolioAgentComment = {
id: string;
author: string;
text: string;
resolved: boolean;
blockId: string | null;
quote: string;
replies: FolioAgentCommentReply[];
};
/** A pending tracked change, shaped to match {@link FolioDocxReviewer.getChanges}. */
type FolioAgentChange = {
id: string;
type: FolioReviewChange["type"];
author: string;
text: string;
blockId: string | null;
};
/** Filter for {@link FOLIO_AGENT_TOOL_NAMES.readComments}. */
type FolioAgentCommentFilter = "all" | "open" | "resolved";
/**
* One lenient-decoding step the parser took on a tool call's arguments
* (a `kind` key read as `type`, an operation supplied as a JSON string, a
* stray property dropped). Reported back to the model so sloppy calls
* still succeed without the normalisation going unnoticed.
*/
type FolioAgentInputNormalization = {
/** Where in the arguments, e.g. `operations[2].kind`. */
path: string;
message: string;
};
/** Result of {@link FOLIO_AGENT_TOOL_NAMES.addComment} / `suggest_changes`. */
type FolioAgentApplyOperationsSummary = {
version: typeof FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION;
applied: {
id: string;
}[];
/** Operations a host review-queue bridge accepted for later human review instead of applying. */
queued: {
id: string;
}[];
skipped: {
id: string;
reason: string;
}[];
issues: FolioDocumentOperationIssue[];
receipts: FolioDocumentOperationReceipt[];
normalizations: FolioAgentInputNormalization[];
};
//#endregion
export { FOLIO_AGENT_TOOL_NAMES, FolioAgentApplyOperationsSummary, FolioAgentBlock, FolioAgentChange, FolioAgentComment, FolioAgentCommentFilter, FolioAgentCommentReply, FolioAgentDocumentOutline, FolioAgentFindTextResult, FolioAgentInputNormalization, FolioAgentJsonObjectSchema, FolioAgentOutlineEntry, FolioAgentScopedFindTextResult, FolioAgentSectionRead, FolioAgentStoryFindTextResult, FolioAgentStoryTextMatch, FolioAgentTextMatch, FolioAgentToolDefinition, FolioAgentToolName, FolioAgentTypedToolDefinition, FolioToolCallResult };