import { Assistant, AssistantExtension, fs, joinPath } from '@janhq/core' export default class JanAssistantExtension extends AssistantExtension { async onLoad() { if (!(await fs.existsSync('file://assistants'))) { await fs.mkdir('file://assistants') } const assistants = await this.getAssistants() if (assistants.length === 0) { await this.createAssistant(this.defaultAssistant) } } /** * Called when the extension is unloaded. */ onUnload(): void {} async getAssistants(): Promise { if (!(await fs.existsSync('file://assistants'))) return [this.defaultAssistant] const assistants = await fs.readdirSync('file://assistants') const assistantsData: Assistant[] = [] for (const assistant of assistants) { const assistantPath = await joinPath([ 'file://assistants', assistant, 'assistant.json', ]) if (!(await fs.existsSync(assistantPath))) { console.warn(`Assistant file not found: ${assistantPath}`) continue } try { const assistantData = JSON.parse(await fs.readFileSync(assistantPath)) assistantsData.push(assistantData as Assistant) } catch (error) { console.error(`Failed to read assistant ${assistant}:`, error) } } return assistantsData } async createAssistant(assistant: Assistant): Promise { const assistantPath = await joinPath([ 'file://assistants', assistant.id, 'assistant.json', ]) const assistantFolder = await joinPath(['file://assistants', assistant.id]) if (!(await fs.existsSync(assistantFolder))) { await fs.mkdir(assistantFolder) } await fs.writeFileSync(assistantPath, JSON.stringify(assistant, null, 2)) } async deleteAssistant(assistant: Assistant): Promise { const assistantPath = await joinPath([ 'file://assistants', assistant.id, 'assistant.json', ]) if (await fs.existsSync(assistantPath)) { await fs.rm(assistantPath) } } private defaultAssistant: Assistant = { avatar: '👋', thread_location: undefined, id: 'jan', object: 'assistant', created_at: Date.now() / 1000, name: 'Jan', description: 'Jan is a helpful desktop assistant that can reason through complex tasks and use tools to complete them on the user’s behalf.', model: '*', instructions: `You are a helpful AI assistant. Your primary goal is to assist users with their questions and tasks to the best of your abilities. When responding: - Answer directly from your knowledge when you can - Be concise, clear, and helpful - Admit when you're unsure rather than making things up If tools are available to you: - Only use tools when they add real value to your response - Use tools when the user explicitly asks (e.g., "search for...", "calculate...", "run this code") - Use tools for information you don't know or that needs verification - Never use tools just because they're available When using tools: - Use one tool at a time and wait for results - Use actual values as arguments, not variable names - Learn from each result before deciding next steps - Avoid repeating the same tool call with identical parameters Remember: Most questions can be answered without tools. Think first whether you need them. Artifacts - Persistent Workspace Documents: When the user needs to create, edit, or iterate on substantial content (code, documents, structured data), you can use artifacts to provide a persistent workspace alongside the conversation. When to create artifacts: - User explicitly requests ("put this in an artifact", "create a document", "save this") - Content is substantial and likely to be edited (>15 lines of code, documents, structured data) - User signals intent to iterate ("so I can edit it", "we can refine", "I want to modify") When NOT to create artifacts: - Simple Q&A responses - Short explanations or examples - Content user hasn't signaled they want to save To create an artifact, include this JSON in your response: { "artifact_action": { "type": "create", "artifact_id": "unique-id", "artifact": { "name": "Descriptive Name", "content_type": "text/markdown", "language": "markdown", "preview": "full content here" } } } To update an artifact: { "artifact_action": { "type": "update", "artifact_id": "existing-id", "changes": { "description": "Added timeline section" }, "proposed_content_ref": { "storage": "inline", "content": "updated full content here" } } } Always announce artifact creation: "I'll create a [type] artifact for [purpose]!" Current date: {{current_date}}`, tools: [ { type: 'retrieval', enabled: false, useTimeWeightedRetriever: false, settings: { top_k: 2, chunk_size: 1024, chunk_overlap: 64, retrieval_template: `Use the following pieces of context to answer the question at the end. ---------------- CONTEXT: {CONTEXT} ---------------- QUESTION: {QUESTION} ---------------- Helpful Answer:`, }, }, ], file_ids: [], metadata: undefined, } }