/// import { BuiltInLLMMessage, Cell, Default, handler, NAME, navigateTo, OpaqueRef, pattern, wish, } from "commontools"; import Chat from "../chatbot.tsx"; import Note from "../notes/note.tsx"; // Simple random ID generator (crypto.randomUUID not available in pattern env) const generateId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`; import { addListItem, calculator, ListItem, readListItems, readWebpage, searchWeb, } from "../system/common-tools.tsx"; import { type MentionableCharm } from "../system/backlinks-index.tsx"; type ChatbotNoteInput = { title?: Cell>; messages?: Cell, []>>; }; type ChatbotNoteResult = { messages: Default, []>; chat: any; list: Default; // Optional: expose sub-charms as mentionable targets mentionable?: MentionableCharm[]; }; const newNote = handler< { /** The text content of the note */ title: string; content?: string; /** A cell to store the result message indicating success or error */ result: Cell; }, { allCharms: Cell; index: any } >( (args, _) => { try { const n = Note({ title: args.title, content: args.content ?? "", noteId: generateId(), }); args.result.set( `Created note ${args.title}`, ); // TODO(bf): we have to navigate here until DX1 lands // then we go back to pushing to allCharms return navigateTo(n); } catch (error) { args.result.set(`Error: ${(error as any)?.message || ""}`); } }, ); const listMentionable = handler< { /** A cell to store the result text */ result: Cell; }, { mentionable: MentionableCharm[] } >( (args, state) => { try { const namesList = state.mentionable.map((charm) => charm[NAME]); args.result.set(JSON.stringify(namesList)); } catch (error) { args.result.set(`Error: ${(error as any)?.message || ""}`); } }, ); const readContentByIndex = handler< { /** A cell to store the result text */ index: number; result: Cell; }, { allNotes: Note[] } >( (args, state) => { try { args.result.set( state.allNotes[args.index]?.content || "No content found", ); } catch (error) { args.result.set(`Error: ${(error as any)?.message || ""}`); } }, ); type Note = MentionableCharm & { content: string }; const editContentByIndex = handler< { /** The index of the note to edit */ index: number; /** The new text content of the note */ body: string; /** A cell to store the result message indicating success or error */ result: Cell; }, { allNotes: Cell } >( (args, state) => { try { const charms = state.allNotes.get(); if (args.index < 0 || args.index >= charms.length) { args.result.set(`Error: Invalid index ${args.index}`); return; } state.allNotes.key(args.index).key("content").set(args.body); args.result.set(`Updated note at index ${args.index}!`); } catch (error) { args.result.set(`Error: ${(error as any)?.message || ""}`); } }, ); const navigateToNote = handler< { /** The index of the note to navigate to */ index: number; /** A cell to store the result message indicating success or error */ result: Cell; }, { allCharms: Cell } >( (args, state) => { try { const charms = state.allCharms.get(); if (args.index < 0 || args.index >= charms.length) { args.result.set(`Error: Invalid index ${args.index}`); return; } const targetCharm = charms[args.index]; args.result.set(`Navigating to note: ${targetCharm[NAME]}`); return navigateTo(state.allCharms.key(args.index)); } catch (error) { args.result.set(`Error: ${(error as any)?.message || ""}`); } }, ); type BacklinksIndex = { mentionable: MentionableCharm[]; }; export default pattern( ({ title, messages }) => { const allCharms = wish>("#allCharms"); const index = wish>( "#default/backlinksIndex", ); const mentionable = wish>( "#mentionable", ); const list = Cell.of([]); const tools = { searchWeb: { pattern: searchWeb, }, readWebpage: { pattern: readWebpage, }, calculator: { pattern: calculator, }, addListItem: { handler: addListItem({ list }), }, readListItems: { handler: readListItems({ list }), }, listMentionable: { description: "List all mentionable items titles (read the body with readNoteByIndex).", handler: listMentionable({ mentionable }), }, readContentByIndex: { description: "Read the content of a mentionable by its index in the listMentionable() list (if possible)", handler: readContentByIndex({ allNotes: mentionable as unknown as OpaqueRef, }), }, editContentByIndex: { description: "Edit the content of a mentionable by its index in the listMentionable() list (if possible)", handler: editContentByIndex({ allNotes: mentionable as unknown as OpaqueRef, }), }, navigateToNote: { description: "Navigate to a mentionable by its index in the listMentionable() list.", handler: navigateToNote({ allCharms: allCharms as unknown as OpaqueRef, }), }, newNote: { description: "Create a new note instance", handler: newNote({ allCharms: allCharms as unknown as OpaqueRef, index: index as unknown as OpaqueRef, }), }, }; const chat = Chat({ messages, tools }); return { [NAME]: title, chat, messages, list, // Expose sub-charms as mentionable targets mentionable: [chat as unknown as MentionableCharm], }; }, );