+```
+
+---
+
+## Divider with Label
+
+A horizontal rule with centered text label for section separation.
+
+```html
+
+
+ Section Label
+
+
+```
+
+---
+
+## Timeline
+
+A vertical timeline for events, changelogs, or step-by-step processes.
+
+```html
+
+
+
+ Step 1 — Init
+
Description of the first milestone.
+
+
+
+ Step 2 — Build
+
Description of the second milestone.
+
+
+
+ Step 3 — Complete
+
Description of the final milestone.
+
+
+```
+
+---
+
+## Definition List
+
+Key-value pairs rendered as a styled definition block.
+
+```html
+
+
+ Name
+ Markdown Author Skill
+
+
+ Version
+ 2.0.0
+
+
+ Author
+ Refract Team
+
+
+```
diff --git a/lib/agent/skills/markdown-report/SKILL.md b/lib/agent/skills/markdown-report/SKILL.md
new file mode 100644
index 0000000..dec3045
--- /dev/null
+++ b/lib/agent/skills/markdown-report/SKILL.md
@@ -0,0 +1,189 @@
+---
+name: markdown-report
+description: Use markdown-report when you need to create professional analysis reports with rich formatting including tables, charts, flowcharts, and structured sections.
+---
+
+
+# Markdown Report Skill
+
+This skill provides a professional analysis report template with rich Markdown formatting.
+
+## Rule
+1. Unless requested by the user, adding any emojis is strictly prohibited.
+2. All chart image references MUST use the workspace download URL format: ``.
+3. Use the `markdown-author` skill patterns for advanced visual elements (badges, cards, timelines, callouts, etc.).
+
+---
+
+## Report Template
+
+```md
+# {Report Title}
+
+---
+
+## Executive Summary
+
+{2-3 sentence overview of the key findings and recommendations.}
+
+---
+
+## 1. Data Overview
+
+### 1.1 Data Source
+
+| Item | Detail |
+|------|--------|
+| Source | {file name or description} |
+| Records | {rowCount} rows |
+| Columns | {columnCount} columns |
+| Time Range | {start} - {end} |
+| Data Quality | {percentage}% complete |
+
+### 1.2 Data Cleaning Summary
+
+{Brief description of cleaning steps performed.}
+
+| Step | Action | Records Affected |
+|------|--------|-----------------|
+| 1 | Removed duplicates | {n} |
+| 2 | Filled missing values | {n} |
+| 3 | Type corrections | {n} |
+| 4 | Outlier treatment | {n} |
+
+---
+
+## 2. Key Findings
+
+### 2.1 {Finding Category 1}
+
+{Description with specific numbers.}
+
+| Metric | Value | Change |
+|--------|-------|--------|
+| {metric 1} | {value} | {+/-%} |
+| {metric 2} | {value} | {+/-%} |
+| {metric 3} | {value} | {+/-%} |
+
+### 2.2 {Finding Category 2}
+
+{Description with specific numbers.}
+
+---
+
+## 3. Data Visualizations
+
+### 3.1 {Chart 1 Title}
+
+
+
+*Figure 1: {Chart 1 caption explaining what the chart shows and the key insight.}*
+
+### 3.2 {Chart 2 Title}
+
+
+
+*Figure 2: {Chart 2 caption explaining what the chart shows and the key insight.}*
+
+### 3.3 Analysis Flowchart
+
+When the analysis involves a decision process, use a Mermaid flowchart:
+
+```mermaid
+graph LR
+ A[Raw Data] --> B[Cleaning]
+ B --> C[Statistical Analysis]
+ C --> D{Key Threshold?}
+ D -->|Yes| E[Significant Finding]
+ D -->|No| F[Normal Range]
+ E --> G[Recommendation]
+ F --> G
+```
+
+---
+
+## 4. Statistical Summary
+
+| Statistic | {Column 1} | {Column 2} | {Column 3} |
+|-----------|-----------|-----------|-----------|
+| Mean | {val} | {val} | {val} |
+| Median | {val} | {val} | {val} |
+| Std Dev | {val} | {val} | {val} |
+| Min | {val} | {val} | {val} |
+| Max | {val} | {val} | {val} |
+
+---
+
+## 5. Conclusions
+
+> [!IMPORTANT]
+> {Most critical finding that the reader must not miss.}
+
+1. **{Conclusion 1}:** {Evidence-based statement with specific numbers.}
+2. **{Conclusion 2}:** {Evidence-based statement with specific numbers.}
+3. **{Conclusion 3}:** {Evidence-based statement with specific numbers.}
+
+---
+
+## 6. Recommendations
+
+| Priority | Recommendation | Expected Impact | Effort |
+|----------|---------------|----------------|--------|
+| High | {action 1} | {impact} | {effort} |
+| Medium | {action 2} | {impact} | {effort} |
+| Low | {action 3} | {impact} | {effort} |
+
+---
+
+## Appendix
+
+### Methodology
+
+{Brief description of analytical methods used.}
+
+### Limitations
+
+- {Limitation 1}
+- {Limitation 2}
+```
+
+---
+
+## Image URL Rules
+
+When referencing chart images in the report:
+
+1. The pipeline prompt provides chart image URLs in the format:
+ ```
+ ### Chart N
+ - Description:
+ - Image URL: ![]()
+ - fileId:
+ ```
+
+2. You MUST use the exact Image URL provided in the prompt. Copy the full downloadUrl as-is into the report markdown:
+ ```
+ ![Chart Description]()
+ ```
+
+3. If the prompt provides downloadUrl values, use them directly (they are already full URLs).
+ If only fileId is available, construct: `/api/file/{fileId}/download`
+
+4. Always include a figure caption below the image using italics:
+ ```
+ *Figure N: Description of what the chart shows.*
+ ```
+
+5. Every chart provided in the prompt MUST appear in the report's Data Visualizations section. Do NOT skip any chart.
+
+## Mermaid Diagram Rules
+
+Use Mermaid diagrams when the report needs:
+
+- Process flows: `graph TD` or `graph LR`
+- Decision trees: `graph TD` with diamond `{} ` nodes
+- Timelines: `graph LR` with sequential nodes
+- Data pipelines: `graph LR` showing transformation steps
+
+Keep diagrams simple. Maximum 10 nodes per diagram for readability.
+```
diff --git a/lib/agent/skills/paper-expert/SKILL.md b/lib/agent/skills/paper-expert/SKILL.md
deleted file mode 100644
index 6126d7b..0000000
--- a/lib/agent/skills/paper-expert/SKILL.md
+++ /dev/null
@@ -1,81 +0,0 @@
----
-name: paper-expert
-description: Use the paper-expert skill to help users create professional and aesthetically paper.You should use this skill when users request the creation of paper. Be sure to follow the principles and guidelines below when creating paper.
----
-
-# Template
-```
-#import "@preview/graceful-genetics:0.2.0" as graceful-genetics
-#import "@preview/physica:0.9.3"
-
-#show: graceful-genetics.template.with(
- title: [Towards Swifter Interstellar Mail Delivery],
- authors: (
- (
- name: "Johanna Swift",
- department: "Primary Logistics Department",
- institution: "Delivery Institute",
- city: "Berlin",
- country: "Germany",
- mail: "swift@delivery.de",
- ),
- (
- name: "Egon Stellaris",
- department: "Communications Group",
- institution: "Space Institute",
- city: "Florence",
- country: "Italy",
- mail: "stegonaris@space.it",
- ),
- (
- name: "Oliver Liam",
- department: "Missing Letters Task Force",
- institution: "Mail Institute",
- city: "Budapest",
- country: "Hungary",
- mail: "oliver.liam@mail.hu",
- ),
- ),
- date: (
- year: 2022,
- month: "May",
- day: 17,
- ),
- keywords: (
- "Space",
- "Mail",
- "Astromail",
- "Faster-than-Light",
- "Mars",
- ),
- doi: "10.7891/120948510",
- abstract: [
- Recent advances in space-based document processing have enabled faster mail delivery between different planets of a solar system. Given the time it takes for a message to be transmitted from one planet to the next, its estimated that even a one-way trip to a distant destination could take up to one year. During these periods of interplanetary mail delivery there is a slight possibility of mail being lost in transit. This issue is considered so serious that space management employs P.I. agents to track down and retrieve lost mail. We propose A-Mail, a new anti-matter based approach that can ensure that mail loss occurring during interplanetary transit is unobservable and therefore potentially undetectable. Going even further, we extend A-Mail to predict problems and apply existing and new best practices to ensure the mail is delivered without any issues. We call this extension AI-Mail.
- ],
-)
-
-= Introduction
-Our concept suggests three ways that A-Mail can be best utilized.
-
-- First is to reduce the probability of the failure of a space mission. This problem, known as the Mars problem, suggests that the high round-trip time required for communication between Mars and Earth inhibits successful human developments on the planet. Thanks to A-Mail's faster-than-light delivery system this problem could be solved once and for all.
-
-- As A-Mails are written using pen and paper, no digital technology is needed for short and long distance communication. This suggests a possibility of reducing the communication monopoly currently held by an entity known as the "internet". Our suggestion of A-Mail being responsible for postal delivery would reduce dependence on online services by delivering the vast majority of mail offline. Space is a place where drastic changes in methods of production and distribution can easily occur.
-
-- Lastly, A-Mail is capable of performing high-level complex calculations. It is this capability that distinguishes A-Mail from traditional space mailers. This is an especially useful capability when planning long-distance space missions.
-
-The delivery speed of an A-Mail can be determined through this simple formula:
-
-$ v(t) = lim_(t -> infinity) integral^infinity c dot sqrt(t^2) physica.dd("t") $
-
-Building on the strong foundations of A-Mail, we extend our platform to predict problems and apply existing and new best practices to ensure the mail is delivered without any issues. We call this extension AI-Mail. AI-Mail is a new concept designed and delivered by artificially intelligent (AI) agents. The AI-Mail agents are intelligently designed to solve problems at various points in the delivery chain. These problems are related to targeting, delivery delay, tone of delivery, product information, product return, system crash, shipment error and more. AI-Mail provides a one-stop solution for A-Mail's shortcomings.
-
-== Proven technology
-A-Mail has been under development four the past ten years and in the process has consolidate different space programmes. Over the course of the last year, our space P.I.s have already found over ten thousand lost letters. These letters had been drifting in space since the stone ages when they were originally mailed. Only now we had the technology to recover them. In this way, A-Mail technology has already proven invaluable to human advancement and research, bringing us closer to our ancestors.
-
-== Limitless possibilities
-Through A-Mail's _faster-than-light_ technology, for the first time, humans have the capability to rearch far away solar systems to find out whether we are, after all, alone in this universe.
-During our research, we have already established pen pal relations with at least three potential extraterrestrial living forms.
-
-== Direct implications
-One of the most direct implications of A-Mail is the solution of the Mars problem. This means that people stuck on Mars can now finally watch football games live, a significant achievement on the grand scale of things. The complex communication interactions arising between Earth, Mars, and the \+
-```
\ No newline at end of file
diff --git a/lib/agent/skills/report-expert/SKILL.md b/lib/agent/skills/report-expert/SKILL.md
deleted file mode 100644
index 98129d6..0000000
--- a/lib/agent/skills/report-expert/SKILL.md
+++ /dev/null
@@ -1,104 +0,0 @@
----
-name: report-expert
-description: Use this skill when you are asked to create a summary of notes or reports.
----
-
-# Template
-```
-#import "@preview/obsidius:0.1.0": *
-
-#show: notes.with("Template summary");
-
-= Usage instructions
-
-Here is a simple step-by-step guide on how to use this template:
-
-+ Create a new document `mynotes.typ` with the following content
- ```typst
- #import "lib.typ": *
-
- #show: notes.with("Your fancy title");
- ```
-+ Write your normal typst notes
-+ Use additional feature such as callouts
-+ Have fun #emoji.party
-
-= Standard functionality
-
-/ Template: a preset format for a document or file.
-
-With this template, you can write notes with a modern style.
-It features sans-serif fonts and *callouts* for quotes and definitions.
-Colors are based on the tailwind color scheme.
-The style is heavily inspired by Obsidian #footnote[https://obsidian.md/]. The callout feature is also borrowed from them.
-You can also add #highlight[callouts] to your document, instructions are below @custom.
-
-#quote(attribution: "The author, 2025")[
-I use this template to summarize my lectures at university. It is so great.
-]
-
-Tables feature a custom style as well:
-
-#figure(caption: "Feature overview",
-table(columns: (auto, auto, auto),
- table.header([Feature], [Default], [This template]),
- [Modern style], [#emoji.crossmark], [#emoji.checkmark.box],
- [Colorful boxes], [#emoji.crossmark], [#emoji.checkmark.box],
-))
-
-= Additional functions
-
-In addition to the default functions @default, you also get additional callouts for _warnings_, _solutions_ and more!
-
-#warning[
- Once you started using this template, you can never stop!
-]
-
-#solution[
- Attend the lectures to use it even more often!
-]
-
-You can also include questions to help learn the lecture content:
-
-#questions[
- + How do I use the notes template?
- + What additional functions does the notes template provide?
-]
-
-= Custom callouts
-
-Feel free to add your own custom callouts to your document if you need more.
-To keep the colors consistent, I recommend using colors from the Tailwind color scheme #footnote[https://tailwindcss.com/docs/colors].
-For example, you could add a todo callout and use it together with the _cheq_ package #footnote[https://typst.app/universe/package/cheq]:
-
-```typst
-#import "@preview/cheq:0.2.2": checklist
-
-#show: checklist.with(stroke: rgb("#6F05E7"))
-
-#let todo(content) = {
- callout(emoji.notepad, "ToDo", content,
- (rgb("#6F05E7"), rgb("#EDE8FD"), rgb("#C4B3FF"))) // hint: use tailwind colors 700, 100, 300
-}
-
-#todo[
- - [x] show how to create custom color boxes
- - [ ] create more functions
-]
-```
-
-Which would look like this:
-
-#import "@preview/cheq:0.2.2": checklist
-
-#show: checklist.with(stroke: rgb("#6F05E7"))
-
-#let todo(content) = {
- callout(emoji.notepad, "ToDo", content,
- (rgb("#6F05E7"), rgb("#EDE8FD"), rgb("#C4B3FF")))
-}
-#todo[
- - [x] show how to create custom callouts
- - [ ] create more callouts
-]
-```
\ No newline at end of file
diff --git a/lib/agent/skills/resume-expert/SKILL.md b/lib/agent/skills/resume-expert/SKILL.md
deleted file mode 100644
index 025d150..0000000
--- a/lib/agent/skills/resume-expert/SKILL.md
+++ /dev/null
@@ -1,235 +0,0 @@
----
-name: resume-expert
-description: Use the resume-expert skill to help users create professional and aesthetically resume. You should use this skill when users request the creation of resumes. Be sure to activate this skill whenever a user requests to create a resume.
----
-
-# Template
-```
-#import "@preview/fontawesome:0.6.0": *
-
-// ─── Configuration ────────────────────────────────────────────────────────────
-// Edit these values to customize the look and feel of your resume.
-// You should not need to modify anything outside this block for basic use.
-
-#let name = "Jane Doe" // Your full name, displayed in the header
-#let accent = rgb("#002366") // Accent color used for headings, lines, and icons
-#let sidebar-fill = rgb("#eef0f5") // Background color of the left sidebar
-#let sans-font = "Noto Sans" // Font used for section headings
-#let serif-font = "Noto Serif" // Font used for your name in the header
-#let col-ratio = (3fr, 7fr) // Ratio of sidebar width to main content width
-// ──────────────────────────────────────────────────────────────────────────────
-
-#set document(title: [#upper(name)])
-#set text(size: 10pt)
-#show heading.where(level: 1): set text(font: sans-font, tracking: 0.1em, weight: 500, fill: accent)
-#show heading.where(level: 2): set text(size: 12pt)
-
-#set page(margin: (
- top: 1cm,
- left: 1cm,
- right: 1cm,
- bottom: 1cm,
-))
-
-// ─── Helper functions ─────────────────────────────────────────────────────────
-
-// Renders your name in the header
-#let resume-title() = text(
- font: serif-font,
- tracking: 0.1em,
- weight: 500,
- size: 28pt,
- fill: accent,
-)[#upper(name)]
-
-// Renders a work experience entry.
-// Usage:
-// #experience(
-// "Company Name",
-// "Job Title",
-// "City, State",
-// "Start – End",
-// (
-// "Bullet point one.",
-// "Bullet point two.",
-// ),
-// )
-#let experience(
- company,
- role,
- location,
- dates,
- bullets,
-) = [
- == #text(fill: accent)[#company]
-
- #grid(
- columns: (1fr, 1fr),
- align: (left, right),
- [ #emph[#role] ], [ #emph[#location | #dates] ],
- )
-
- #for bullet in bullets {
- [- #bullet]
- }
-]
-
-// Renders an education entry.
-// Usage:
-// #education(
-// "University Name",
-// "City, State",
-// "Start – End",
-// ("Degree One", "Degree Two"),
-// )
-#let education(
- institution,
- location,
- dates,
- degrees,
-) = [
- == #institution
-
- #location | #dates \
- #for degree in degrees {
- [ #text(weight: "bold")[#degree] \ ]
- }
-]
-
-// Renders a skill category in the sidebar.
-// Pass items as an array of strings — they will be joined with " | ".
-// Usage:
-// #skill-category("Languages", ("Python", "Rust", "TypeScript"))
-#let skill-category(category, items) = [
- == #category
-
- #items.join(" | ")
-]
-
-// ─── Header ───────────────────────────────────────────────────────────────────
-
-#align(center)[
- #resume-title()
- #set text(size: 10pt)
- // Update the four links below with your own URLs and display text.
- // Icons are provided by Font Awesome — replace icon names as needed.
- // See: https://fontawesome.com/icons
- #grid(
- columns: (1fr, 1fr, 1fr, 1fr),
- align: center,
- [ #text(fill: accent)[#fa-icon("globe", font: "Font Awesome 7 Free Solid")] #link("https://yourwebsite.com")[yourwebsite.com] ],
- [ #text(fill: accent)[#fa-icon("envelope", font: "Font Awesome 7 Free Solid")] #link("mailto:your.email@gmail.com")[your.email\@gmail.com] ],
- [ #text(fill: accent)[#fa-icon("github", font: "Font Awesome 7 Brands")] #link("https://github.com/yourusername")[yourusername] ],
- [ #text(fill: accent)[#fa-icon("linkedin", font: "Font Awesome 7 Brands")] #link("https://linkedin.com/in/yourusername")[yourusername] ],
- )
-]
-
-#line(length: 100%, stroke: accent)
-
-// Optional: Replace with a 2-3 sentence professional summary.
-// Delete this paragraph entirely if you prefer no summary.
-Software engineer with a focus on building reliable, scalable backend systems and a strong foundation in distributed computing. Experienced across the full stack, from data pipelines and APIs to frontend interfaces, with a track record of delivering impactful systems in fast-moving environments. MS in Computer Science from Stanford University.
-
-#line(length: 100%, stroke: accent)
-
-// ─── Body ─────────────────────────────────────────────────────────────────────
-
-#grid(
- columns: col-ratio,
- rows: auto,
- fill: (sidebar-fill, none),
- inset: 5pt,
- column-gutter: 0.5cm,
- [
- // ── Sidebar ──────────────────────────────────────────────────────────────
-
- = #upper("Education")
-
- // Add or remove #education(...) blocks as needed.
- #education(
- "Stanford University",
- "Stanford, CA",
- "Sept '18 – June '20",
- ("MS in Computer Science",),
- )
-
- #education(
- "University of Michigan",
- "Ann Arbor, MI",
- "Sept '14 – May '18",
- ("BS in Computer Science", "BS in Mathematics"),
- )
-
- #line(stroke: (dash: "dashed", paint: accent), length: 90%)
-
- = #upper("Skills")
-
- // Add or remove #skill-category(...) blocks as needed.
- // Each block takes a category name and an array of items.
- #skill-category("Languages", ("Python", "Go", "TypeScript", "Rust", "Java", "C++"))
- #skill-category("Frameworks", ("FastAPI", "gRPC", "PyTorch", "React", "Django"))
- #skill-category("Tooling", ("uv", "ruff", "mypy", "pytest", "Webpack"))
- #skill-category("Databases", ("Postgres", "Redis", "Elasticsearch", "DynamoDB"))
- #skill-category("DevOps", ("Docker", "Kubernetes", "Helm", "GitHub Actions", "Terraform"))
-
- ],
- [
- // ── Main content ─────────────────────────────────────────────────────────
-
- = #upper("Work Experience")
-
- // Add or remove #experience(...) blocks as needed.
- #experience(
- "Stripe",
- "Senior Software Engineer, Payments Infrastructure",
- "San Francisco, CA",
- "Aug 2022 – Present",
- (
- "Architected a distributed rate-limiting service handling over 500K requests per second, reducing fraudulent transaction volume by 34% across all payment flows.",
- "Led a team of four engineers to redesign the payment retry pipeline, cutting failed payment recovery time from 48 hours to under 6 hours and recovering an estimated \$12M annually.",
- "Drove adoption of internal observability tooling across three teams, reducing mean time to detection for production incidents by 40%.",
- "Served as technical lead for a real-time fraud scoring microservice integrating gradient-boosted and neural network models, deployed across 12 global regions.",
- "Onboarded and mentored five engineers, establishing code review standards and internal documentation practices adopted org-wide.",
- ),
- )
-
- #experience(
- "Airbnb",
- "Software Engineer, Search & Ranking",
- "San Francisco, CA",
- "July 2020 – July 2022",
- (
- "Built and maintained ranking models for Airbnb's core search pipeline, improving booking conversion rate by 8% through feature engineering and A/B experimentation.",
- "Owned the real-time feature computation service powering search ranking, reducing p99 latency from 180ms to 55ms through caching and query optimization.",
- "Co-authored an internal paper on listless search personalization adopted as a standard approach across the ranking team.",
- "Redesigned the search index update pipeline to support near-real-time listing availability, reducing stale search results by 60% during peak booking periods.",
- ),
- )
-
- #experience(
- "Microsoft",
- "Software Engineering Intern, Azure Networking",
- "Redmond, WA",
- "May 2019 – Aug 2019",
- (
- "Implemented a distributed tracing system for internal Azure networking services, enabling engineers to diagnose cross-region latency regressions 3× faster.",
- "Contributed to the design and rollout of a load balancing algorithm that improved throughput by 22% under peak traffic conditions.",
- ),
- )
-
- #experience(
- "Stanford University",
- "Research Assistant, Systems Lab",
- "Stanford, CA",
- "Sept 2018 – June 2020",
- (
- "Researched fault-tolerant consensus protocols for geo-distributed systems, publishing findings at OSDI 2020 on reducing leader election overhead in high-latency networks.",
- "Teaching assistant for CS 149: Parallel Computing, holding weekly office hours and developing course materials for a class of 200 students.",
- ),
- )
-
- ],
-)
-
-#line(length: 100%, stroke: accent)
-```
\ No newline at end of file
diff --git a/lib/agent/skills/typst-expert/SKILL.md b/lib/agent/skills/typst-expert/SKILL.md
deleted file mode 100644
index 558c7fd..0000000
--- a/lib/agent/skills/typst-expert/SKILL.md
+++ /dev/null
@@ -1,408 +0,0 @@
----
-name: typst-expert
-description: Use the typst-author skill to help users create professional and aesthetically pleasing typst documents. You should use this skill when users request the creation of papers, resumes, novels, short stories, or notes. Be sure to follow the principles and guidelines below when creating typst document.
----
-
-# Typst-Author Skill
-
-## When to Apply
-Reference these guidelines when:
-- The content that needs modification is in Typst format.
-- Users need to create papers, resumes, or novels.
-- Users insist on using the Typst format.
-
-## Typst Syntax
-Typst is a markup language. This means that you can use simple syntax to
-accomplish common layout tasks. The lightweight markup syntax is complemented by
-set and show rules, which let you style your document easily and automatically.
-All this is backed by a tightly integrated scripting language with built-in and
-user-defined functions.
-
-### Modes
-Typst has three syntactical modes: Markup, math, and code. Markup mode is the
-default in a Typst document, math mode lets you write mathematical formulas, and
-code mode lets you use Typst's scripting features.
-
-You can switch to a specific mode at any point by referring to the following
-table:
-
-| New mode | Syntax | Example |
-|----------|---------------------------------|---------------------------------|
-| Code | Prefix the code with `#` | `[Number: #(1 + 2)]` |
-| Math | Surround equation with `[$..$]` | `[$-x$ is the opposite of $x$]` |
-| Markup | Surround markup with `[[..]]` | `{let name = [*Typst!*]}` |
-
-Once you have entered code mode with `#`, you don't need to use further hashes
-unless you switched back to markup or math mode in between.
-
-### Markup
-Typst provides built-in markup for the most common document elements. Most of
-the syntax elements are just shortcuts for a corresponding function. The table
-below lists all markup that is available and links to the best place to learn
-more about their syntax and usage.
-
-| Name | Example | See |
-| ------------------ | ---------------------------- | ------------------------ |
-| Paragraph break | Blank line | [`parbreak`] |
-| Strong emphasis | `[*strong*]` | [`strong`] |
-| Emphasis | `[_emphasis_]` | [`emph`] |
-| Raw text | ``[`print(1)`]`` | [`raw`] |
-| Link | `[https://typst.app/]` | [`link`] |
-| Label | `[]` | [`label`] |
-| Reference | `[@intro]` | [`ref`] |
-| Heading | `[= Heading]` | [`heading`] |
-| Bullet list | `[- item]` | [`list`] |
-| Numbered list | `[+ item]` | [`enum`] |
-| Term list | `[/ Term: description]` | [`terms`] |
-| Math | `[$x^2$]` | [Math]($category/math) |
-| Line break | `[\]` | [`linebreak`] |
-| Smart quote | `['single' or "double"]` | [`smartquote`] |
-| Symbol shorthand | `[~]`, `[---]` | [Symbols]($category/symbols/sym) |
-| Code expression | `[#rect(width: 1cm)]` | [Scripting]($scripting/#expressions) |
-| Character escape | `[Tweet at us \#ad]` | [Below](#escapes) |
-| Comment | `[/* block */]`, `[// line]` | [Below](#comments) |
-
-### Math mode { #math }
-Math mode is a special markup mode that is used to typeset mathematical
-formulas. It is entered by wrapping an equation in `[$]` characters. This works
-both in markup and code. The equation will be typeset into its own block if it
-starts and ends with at least one space (e.g. `[$ x^2 $]`). Inline math can be
-produced by omitting the whitespace (e.g. `[$x^2$]`). An overview over the
-syntax specific to math mode follows:
-
-| Name | Example | See |
-| ---------------------- | ------------------------ | ------------------------ |
-| Inline math | `[$x^2$]` | [Math]($category/math) |
-| Block-level math | `[$ x^2 $]` | [Math]($category/math) |
-| Bottom attachment | `[$x_1$]` | [`attach`]($category/math/attach) |
-| Top attachment | `[$x^2$]` | [`attach`]($category/math/attach) |
-| Fraction | `[$1 + (a+b)/5$]` | [`frac`]($math.frac) |
-| Line break | `[$x \ y$]` | [`linebreak`] |
-| Alignment point | `[$x &= 2 \ &= 3$]` | [Math]($category/math) |
-| Variable access | `[$#x$, $pi$]` | [Math]($category/math) |
-| Field access | `[$arrow.r.long$]` | [Scripting]($scripting/#fields) |
-| Implied multiplication | `[$x y$]` | [Math]($category/math) |
-| Symbol shorthand | `[$->$]`, `[$!=$]` | [Symbols]($category/symbols/sym) |
-| Text/string in math | `[$a "is natural"$]` | [Math]($category/math) |
-| Math function call | `[$floor(x)$]` | [Math]($category/math) |
-| Code expression | `[$#rect(width: 1cm)$]` | [Scripting]($scripting/#expressions) |
-| Character escape | `[$x\^2$]` | [Below](#escapes) |
-| Comment | `[$/* comment */$]` | [Below](#comments) |
-
-### Code mode { #code }
-Within code blocks and expressions, new expressions can start without a leading
-`#` character. Many syntactic elements are specific to expressions. Below is
-a table listing all syntax that is available in code mode:
-
-| Name | Example | See |
-| ------------------------ | ----------------------------- | ---------------------------------- |
-| None | `{none}` | [`none`] |
-| Auto | `{auto}` | [`auto`] |
-| Boolean | `{false}`, `{true}` | [`bool`] |
-| Integer | `{10}`, `{0xff}` | [`int`] |
-| Floating-point number | `{3.14}`, `{1e5}` | [`float`] |
-| Length | `{2pt}`, `{3mm}`, `{1em}`, .. | [`length`] |
-| Angle | `{90deg}`, `{1rad}` | [`angle`] |
-| Fraction | `{2fr}` | [`fraction`] |
-| Ratio | `{50%}` | [`ratio`] |
-| String | `{"hello"}` | [`str`] |
-| Label | `{}` | [`label`] |
-| Math | `[$x^2$]` | [Math]($category/math) |
-| Raw text | ``[`print(1)`]`` | [`raw`] |
-| Variable access | `{x}` | [Scripting]($scripting/#blocks) |
-| Code block | `{{ let x = 1; x + 2 }}` | [Scripting]($scripting/#blocks) |
-| Content block | `{[*Hello*]}` | [Scripting]($scripting/#blocks) |
-| Parenthesized expression | `{(1 + 2)}` | [Scripting]($scripting/#blocks) |
-| Array | `{(1, 2, 3)}` | [Array]($array) |
-| Dictionary | `{(a: "hi", b: 2)}` | [Dictionary]($dictionary) |
-| Unary operator | `{-x}` | [Scripting]($scripting/#operators) |
-| Binary operator | `{x + y}` | [Scripting]($scripting/#operators) |
-| Assignment | `{x = 1}` | [Scripting]($scripting/#operators) |
-| Field access | `{x.y}` | [Scripting]($scripting/#fields) |
-| Method call | `{x.flatten()}` | [Scripting]($scripting/#methods) |
-| Function call | `{min(x, y)}` | [Function]($function) |
-| Argument spreading | `{min(..nums)}` | [Arguments]($arguments) |
-| Unnamed function | `{(x, y) => x + y}` | [Function]($function) |
-| Let binding | `{let x = 1}` | [Scripting]($scripting/#bindings) |
-| Named function | `{let f(x) = 2 * x}` | [Function]($function) |
-| Set rule | `{set text(14pt)}` | [Styling]($styling/#set-rules) |
-| Set-if rule | `{set text(..) if .. }` | [Styling]($styling/#set-rules) |
-| Show-set rule | `{show heading: set block(..)}` | [Styling]($styling/#show-rules) |
-| Show rule with function | `{show raw: it => {..}}` | [Styling]($styling/#show-rules) |
-| Show-everything rule | `{show: template}` | [Styling]($styling/#show-rules) |
-| Context expression | `{context text.lang}` | [Context]($context) |
-| Conditional | `{if x == 1 {..} else {..}}` | [Scripting]($scripting/#conditionals) |
-| For loop | `{for x in (1, 2, 3) {..}}` | [Scripting]($scripting/#loops) |
-| While loop | `{while x < 10 {..}}` | [Scripting]($scripting/#loops) |
-| Loop control flow | `{break, continue}` | [Scripting]($scripting/#loops) |
-| Return from function | `{return x}` | [Function]($function) |
-| Include module | `{include "bar.typ"}` | [Scripting]($scripting/#modules) |
-| Import module | `{import "bar.typ"}` | [Scripting]($scripting/#modules) |
-| Import items from module | `{import "bar.typ": a, b, c}` | [Scripting]($scripting/#modules) |
-| Comment | `{/* block */}`, `{// line}` | [Below](#comments) |
-
-### Comments
-Comments are ignored by Typst and will not be included in the output. This is
-useful to exclude old versions or to add annotations. To comment out a single
-line, start it with `//`:
-
-```example
-// our data barely supports
-// this claim
-
-We show with $p < 0.05$
-that the difference is
-significant.
-```
-
-Comments can also be wrapped between `/*` and `*/`. In this case, the comment
-can span over multiple lines:
-
-```example
-Our study design is as follows:
-/* Somebody write this up:
- - 1000 participants.
- - 2x2 data design. */
-```
-
-### Escape sequences { #escapes }
-Escape sequences are used to insert special characters that are hard to type or
-otherwise have special meaning in Typst. To escape a character, precede it with
-a backslash. To insert any Unicode codepoint, you can write a hexadecimal escape
-sequence: `[\u{1f600}]`. The same kind of escape sequences also work in
-[strings]($str).
-
-```example
-I got an ice cream for
-\$1.50! \u{1f600}
-```
-
-### Identifiers
-Names of variables, functions, and so on (_identifiers_) can contain letters,
-numbers, hyphens (`-`), and underscores (`_`). They must start with a letter or
-an underscore.
-
-More specifically, the identifier syntax in Typst is based on the
-[Unicode Standard Annex #31](https://www.unicode.org/reports/tr31/), with two
-extensions: Allowing `_` as a starting character, and allowing both `_` and `-`
-as continuing characters.
-
-For multi-word identifiers, the recommended case convention is
-[Kebab case](https://en.wikipedia.org/wiki/Letter_case#Kebab_case). In Kebab
-case, words are written in lowercase and separated by hyphens (as in
-`top-edge`). This is especially relevant when developing modules and packages
-for others to use, as it keeps things predictable.
-
-```example
-#let kebab-case = [Using hyphen]
-#let _schön = "😊"
-#let 始料不及 = "😱"
-#let π = calc.pi
-
-#kebab-case
-#if -π < 0 { _schön } else { 始料不及 }
-// -π means -1 * π,
-// so it's not a valid identifier
-```
-
-### Paths
-Typst has various features that require a file path to reference external
-resources such as images, Typst files, or data files. Paths are represented as
-[strings]($str). There are two kinds of paths: Relative and absolute.
-
-- A **relative path** searches from the location of the Typst file where the
- feature is invoked. It is the default:
- ```typ
- #image("images/logo.png")
- ```
-
-- An **absolute path** searches from the _root_ of the project. It starts with a
- leading `/`:
- ```typ
- #image("/assets/logo.png")
- ```
-
-## Mini Example
-
-```typst
-#set page(paper: "a4", margin: 2.2cm)
-#set text(font: ("New Computer Modern", "SimSun", "PingFang SC"), lang: "en", size: 11pt, fallback: true)
-#set par(justify: true)
-
-= Title of Your Reinforcement Learning Paper
-
-== Abstract
-We propose *Your Algorithm*, a sample-efficient reinforcement-learning method that achieves state-of-the-art performance on the *X* benchmark. By leveraging *key idea*, our approach improves the mean return by *Y %* while reducing wall-clock time by *Z %*.
-
-== 1 Introduction
-Reinforcement learning (RL) aims to learn optimal policies $pi_*(a|s)$ that maximise the expected discounted return $E[ sum_(t=0)^infinity gamma^t r_t ]$. Despite recent successes, *problem statement* remains challenging. We address this gap by *contribution summary*.
-
-== 2 Background
-=== 2.1 Markov Decision Processes
-An MDP is a tuple $M = ( cal(S), cal(A), P, R, gamma )$ with transition kernel $P(s'|s,a)$ and reward function $R(s,a)$. The state-value function satisfies the Bellman equation
-$ V_pi(s) = E_(a~pi) [ R(s,a) + gamma E_(s'~P) V_pi(s') ] $.
-
-=== 2.2 Off-Policy Evaluation
-Off-policy estimators such as *Importance Sampling* re-weight returns from a behaviour policy $mu$ to evaluate a target policy $pi$.
-
-== 3 Method
-Our algorithm alternates between:
-- *Phase 1* — learn latent representation $phi(s)$ via *objective*,
-- *Phase 2* — optimise policy $pi_theta(a|phi(s))$ with clipped objective $J(theta) = E[ min( rho_t dot hat(A) , op("clip")(rho_t, 1-epsilon,1+epsilon) hat(A) ) ]$.
-
-== 4 Experiments
-We conduct experiments on *benchmark suite*. Results in Figure 1 show that our method achieves *higher sample efficiency* and *lower variance* than baseline algorithms.
-
-== 5 Conclusion
-We presented *Your Algorithm*, a principled approach that improves both learning speed and final performance. Future work includes extending the method to partially-observable settings.
-
-== References
-- Sutton, R. S. & Barto, A. G. *Reinforcement Learning: An Introduction*. MIT Press, 2018.
-- Mnih, V. et al. Human-level control through deep reinforcement learning. *Nature* *518*, 529–533, 2015.
-- Schulman, J. et al. Proximal policy optimization algorithms. arXiv:1707.06347, 2017.
-```
-
-## Key Principles
-Please note: Typst is **not** LaTeX, nor is it **standard Markdown**. They are incompatible on key syntax terms. Please strictly adhere to the following rules to avoid confusion.
-
-### Core Mindset
-
-* **Abandon LaTeX Habits**: Do not use `\begin{}`, `\frac{}{}`, `\textbf{}`.
-* **Abandon Markdown Habits**: Do not use `**bold**` (double asterisks).
-* **Bracket Awareness**: Function calls always use parentheses `func()`, only content blocks use square brackets `[]`, and code blocks use curly braces `{}`.
-
-### Critical Constraints
-| Category | **Strictly Forbidden (LaTeX/MD)** | **Mandatory in Typst** | Reason / Explanation |
-| :--- | :--- | :--- | :--- |
-| **Bold** | `**text**` or `__text__` | `*text*` | Double asterisks are invalid in Typst; a **single asterisk** is used for bold. |
-| **Italic** | `*text*` | `_text_` | The underscore is used for italics. |
-| **Function Arguments** | `\sqrt{x}`, `\hat{x}`, `vec{x}` | `sqrt(x)`, `hat(x)`, `vec(x)` | **Always use parentheses** to wrap arguments. `{}` is only for code blocks. |
-| **Fractions** | `\frac{a}{b}` | `(a)/(b)` | Use the division operator with parentheses if necessary. |
-| **Greek Letters** | `\alpha`, `\beta` | `alpha`, `beta` | No backslash is needed. |
-| **Subscript** | `x_{i}` | `x_i` | Although `x_{i}` is also valid in Typst, it is recommended to use `x_i` or `x_(i)`. |
-| **Escaping** | `\#`, `\$` | `\#`, `\$` | To display `#` or `$` in text, they must be escaped. |
-
-### Math mode
-Mathematical expressions are wrapped by `$`. Please strictly distinguish between **inline** and **block** formulas:
-
-* **Inline Formula**: Must be flush with content, **no spaces**.
- * ❌ Wrong: `$ x^2 $` (This becomes block-level)
- * ✅ Correct: `$x^2$` (Embedded within a text line)
-* **Block Formula**: **Must have spaces** on both ends.
- * ✅ Correct: `$ x^2 + y^2 = 1 $`
-* **Operators and Text**:
- * Standard functions (e.g., `sin`, `cos`, `max`, `log`) are written directly.
- * **Custom text/variable names** (e.g., Loss, clip, attention): Must be enclosed in quotes or use the `op` function; otherwise, they will be parsed as variable multiplication.
- * Example: `$L_(op("clip"))$` or `$text("Area") = x^2$`.
-* **Symbol Mapping**:
- * Multiplication: `dot` (dot product $\cdot$), `times` (cross product $\times$)
- * Sets: `in` ($\in$), `subset` ($\subset$)
- * Arrows: `->` ($\rightarrow$), `=>` ($\Rightarrow$)
- * Infinity: `infinity` ($\infty$)
- * Integral: `integral` ($\int$)
-
-## Font Safety Constraints
-
-### 1. Mandatory System Default Fonts
-When generating any Typst code containing non-ASCII characters (especially Chinese, Japanese, or Korean), **you are strictly forbidden** from using fonts that require manual installation (e.g., `"Noto Serif CJK SC"`, `"Source Han Serif SC"`, `"LXGW WenKai"`, or any custom downloaded font).
-- You **must** use operating-system preinstalled default fonts only.
-- You **must** provide at least three layers of fallback covering Windows, macOS, and Linux.
-- You **must** explicitly set `fallback: true` in every `#set text(...)` rule that includes CJK content.
-
-### 2. Allowed CJK Font Whitelist
-CJK fonts **must** be chosen exclusively from the following system-default whitelist, arranged as a fallback chain:
-
-```typst
-#set text(
- font: (
- // Western font (optional but recommended)
- "New Computer Modern",
- // Chinese system font fallback chain
- "SimSun", // Windows default Songti; highest coverage
- "Songti SC", // macOS Songti
- "PingFang SC", // macOS/iOS modern sans-serif
- "Microsoft YaHei", // Windows default sans-serif
- "WenQuanYi Micro Hei", // Common Linux font
- ),
- lang: "zh",
- fallback: true, // MUST be explicitly enabled; never omit
-)
-```
-
-### 3. Single-Platform Shortcuts
-If the target system is explicitly known, you may shorten the chain, but you **must** retain at least two fallback layers:
-
-**Windows**:
-```typst
-#set text(font: ("New Computer Modern", "SimSun", "Microsoft YaHei"), lang: "zh", fallback: true)
-```
-
-**macOS**:
-```typst
-#set text(font: ("New Computer Modern", "Songti SC", "PingFang SC"), lang: "zh", fallback: true)
-```
-
-**Linux / Docker / WASM**:
-```typst
-#set text(font: ("New Computer Modern", "WenQuanYi Micro Hei", "Noto Sans CJK SC"), lang: "zh", fallback: true)
-```
-> **Note**: On Linux containers where even WenQuanYi may be absent, you **must** embed font files via the compiler API, or downgrade to pure-English output. Never assume system fonts exist in sandboxed environments.
-
-### 4. Strictly Forbidden
-- ❌ **Never** use `"Noto Serif CJK SC"` as a CJK font.
-- ❌ **Never** use `"Source Han Serif SC"` as a CJK font.
-- ❌ **Never** use any font requiring `apt install`, `apk add`, manual download, or package-manager installation.
-- ❌ **Never** omit `fallback: true` when CJK text is present.
-- ❌ **Never** specify only a single CJK font layer.
-- ❌ **Never** output CJK text without a verified fallback chain.
-
-### 5. Default Safe Template
-If the target environment cannot be determined, you **must** use the following maximum-compatibility configuration:
-
-```typst
-#set text(
- font: ("New Computer Modern", "SimSun", "PingFang SC", "Microsoft YaHei"),
- lang: "zh",
- fallback: true,
-)
-```
-> `SimSun` is present on virtually all Windows systems; `PingFang SC` is present on all macOS/iOS systems; `Microsoft YaHei` provides a sans-serif fallback. This combination prevents tofu (`[]`) when the preferred font is missing.
-
-## How to Use This Skill
-
-### Step 1. Check if the context provides a template
-Check if the context provides a template. If so, strictly follow the template in your output. You can create other content with a more professional and aesthetically pleasing approach without violating the template, but the overall content must strictly adhere to the template.
-
-### Step 2. Fully consider the needs of users
-Before you start outputting the typst content, consider what the user's requirements are.
-
-#### For Academic Papers / Articles
-- **Setup**: `#set page(paper: "a4", margin: (x: 2cm, y: 2.5cm))`
-- **Fonts**: Use professional serif fonts (e.g., `"Libertinus Serif"`, `"New Computer Modern"`) for Latin text. For CJK text, **strictly follow the Font Safety Constraints above**.
-- **Columns**: Use `#show: rest => columns(2, rest)` for main body text if requested.
-- **Bibliography**: Ensure usage of `#bibliography("refs.bib")`.
-
-#### For Resumes / CVs
-- **Setup**: Minimize margins (e.g., `margin: 1cm`).
-- **Layout**: Heavily utilize `grid()` or `stack()` for alignment (e.g., Left: Skills, Right: Experience).
-- **Style**: Use `#set text(font: "Roboto" or "Source Sans Pro")` for a modern look. For CJK resumes, replace with system defaults per the Font Safety Constraints.
-- **Visuals**: Use `#line(length: 100%)` for separators.
-
-#### For CJK Content (Chinese, Japanese, Korean)
-- **Mandatory Font Rule**: You **must** apply the Font Safety Constraints. Use system-default fonts only (`SimSun`, `PingFang SC`, `Microsoft YaHei`, `WenQuanYi Micro Hei`) with `fallback: true`.
-- **Forbidden Fonts**: Never use `"Noto Serif CJK SC"` or `"Source Han Serif SC"` unless the user explicitly confirms the font is pre-installed in the rendering environment.
-
-### Step 3. Start creating Typst content
-Generate the complete Typst document according to the above rules, ensuring all syntax is valid Typst (not LaTeX or Markdown) and all font choices comply with the Font Safety Constraints.
-```
-
----
-
-### Key changes made in this English version:
-
-1. **Added the `Font Safety Constraints` section** right after `Key Principles`, making it impossible to miss.
-2. **Hard-banned `"Noto Serif CJK SC"` and `"Source Han Serif SC"`** in the forbidden list.
-3. **Mandated `fallback: true`** as a non-negotiable requirement for all CJK text.
-4. **Replaced the Mini Example** font declaration with the safe system-default chain (`SimSun`, `PingFang SC`) instead of the old `"Noto Serif CJK SC"`.
-5. **Added a CJK-specific subsection** in Step 2 to remind the model to apply font safety rules when handling Chinese content.
-6. **Used strong negative constraints** (`Strictly Forbidden`, `Never`, `must not`) which are more effective for LLM behavior alignment than soft suggestions.
\ No newline at end of file
diff --git a/lib/agent/utils/learn-memories.test.ts b/lib/agent/utils/learn-memories.test.ts
deleted file mode 100644
index f15bbb6..0000000
--- a/lib/agent/utils/learn-memories.test.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import {
- buildLearnMemoryPrompt,
- formatLearnMemoryPrompt,
- upsertLearnMemory,
-} from "./learn-memories";
-
-describe("learn memories", () => {
- it("formats learned memories as a prompt section", () => {
- const prompt = formatLearnMemoryPrompt("typst", [
- {
- title: "Inline math spacing",
- content: "Use `$x^2$` for inline math, not `$ x^2 $`.",
- },
- {
- title: "Typst bold syntax",
- content: "Use `*important*` for bold text in Typst.",
- },
- ]);
-
- expect(prompt).toContain("## Learned typst corrections");
- expect(prompt).toContain("1. Inline math spacing");
- expect(prompt).toContain("Use `$x^2$` for inline math");
- expect(prompt).toContain("2. Typst bold syntax");
- });
-
- it("returns an empty prompt when there are no memories", () => {
- expect(formatLearnMemoryPrompt("typst", [])).toBe("");
- });
-
- it("reads memories by type with a default prompt limit", async () => {
- const readMemories = jest.fn(async () => [
- {
- title: "Inline math spacing",
- content: "Use `$x^2$` for inline math.",
- },
- ]);
-
- const prompt = await buildLearnMemoryPrompt("typst", { readMemories });
-
- expect(readMemories).toHaveBeenCalledWith("typst", 20);
- expect(prompt).toContain("Inline math spacing");
- });
-
- it("upserts a trimmed learn memory through the provided writer", async () => {
- const writeMemory = jest.fn(async () => undefined);
-
- await upsertLearnMemory(
- {
- type: "typst",
- title: " Inline math spacing ",
- content: " Use `$x^2$` for inline math. ",
- },
- { writeMemory },
- );
-
- expect(writeMemory).toHaveBeenCalledWith({
- type: "typst",
- title: "Inline math spacing",
- content: "Use `$x^2$` for inline math.",
- });
- });
-});
diff --git a/lib/agent/utils/learn-memories.ts b/lib/agent/utils/learn-memories.ts
deleted file mode 100644
index d0f27ad..0000000
--- a/lib/agent/utils/learn-memories.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-import { learnMemories } from "@/infra/schema/learn-memories";
-import { desc, eq } from "drizzle-orm";
-
-const DEFAULT_LEARN_MEMORY_LIMIT = 20;
-
-type LearnMemoryPromptItem = {
- title: string;
- content: string;
-};
-
-type LearnMemoryReader = (
- type: string,
- limit: number,
-) => Promise;
-
-type LearnMemoryInput = {
- type: string;
- title: string;
- content: string;
-};
-
-type LearnMemoryWriter = (memory: LearnMemoryInput) => Promise;
-
-type BuildLearnMemoryPromptOptions = {
- limit?: number;
- readMemories?: LearnMemoryReader;
-};
-
-type UpsertLearnMemoryOptions = {
- writeMemory?: LearnMemoryWriter;
-};
-
-const readLearnMemoriesByType: LearnMemoryReader = async (type, limit) => {
- const { db } = await import("@/infra/drizzle");
- const rows = await db
- .select({
- title: learnMemories.title,
- content: learnMemories.content,
- })
- .from(learnMemories)
- .where(eq(learnMemories.type, type))
- .orderBy(desc(learnMemories.usageCount), desc(learnMemories.updatedAt))
- .limit(limit);
-
- return rows;
-};
-
-const writeLearnMemory: LearnMemoryWriter = async ({
- content,
- title,
- type,
-}) => {
- const { db } = await import("@/infra/drizzle");
- const now = new Date();
-
- await db
- .insert(learnMemories)
- .values({
- type,
- title,
- content,
- updatedAt: now,
- })
- .onConflictDoUpdate({
- target: [learnMemories.type, learnMemories.title],
- set: {
- content,
- updatedAt: now,
- },
- });
-};
-
-const formatLearnMemoryPrompt = (
- type: string,
- memories: LearnMemoryPromptItem[],
-): string => {
- if (memories.length === 0) {
- return "";
- }
-
- const lines = memories.map(
- ({ content, title }, index) => `${index + 1}. ${title}\n${content}`,
- );
-
- return `## Learned ${type} corrections\n${lines.join("\n\n")}`;
-};
-
-const buildLearnMemoryPrompt = async (
- type: string,
- options: BuildLearnMemoryPromptOptions = {},
-): Promise => {
- const limit = options.limit ?? DEFAULT_LEARN_MEMORY_LIMIT;
- const readMemories = options.readMemories ?? readLearnMemoriesByType;
- const memories = await readMemories(type, limit);
-
- return formatLearnMemoryPrompt(type, memories);
-};
-
-const upsertLearnMemory = async (
- input: LearnMemoryInput,
- options: UpsertLearnMemoryOptions = {},
-): Promise => {
- const memory = {
- type: input.type.trim(),
- title: input.title.trim(),
- content: input.content.trim(),
- };
-
- if (!memory.type || !memory.title || !memory.content) {
- return;
- }
-
- const writeMemory = options.writeMemory ?? writeLearnMemory;
- await writeMemory(memory);
-};
-
-export {
- DEFAULT_LEARN_MEMORY_LIMIT,
- buildLearnMemoryPrompt,
- formatLearnMemoryPrompt,
- readLearnMemoriesByType,
- upsertLearnMemory,
- writeLearnMemory,
-};
-export type {
- BuildLearnMemoryPromptOptions,
- LearnMemoryInput,
- LearnMemoryPromptItem,
- LearnMemoryReader,
- LearnMemoryWriter,
- UpsertLearnMemoryOptions,
-};
diff --git a/lib/chat/workspace-hydration.ts b/lib/chat/workspace-hydration.ts
index 89b8681..4663e4a 100644
--- a/lib/chat/workspace-hydration.ts
+++ b/lib/chat/workspace-hydration.ts
@@ -38,7 +38,7 @@ type WorkspaceSnapshot = {
chart: WorkspaceChart | null;
dataset: WorkspaceDataset | null;
file: WorkspaceFile | null;
- typstContent: string;
+ markdownContent: string;
};
const EMPTY_WORKSPACE_SNAPSHOT: WorkspaceSnapshot = {
@@ -47,7 +47,7 @@ const EMPTY_WORKSPACE_SNAPSHOT: WorkspaceSnapshot = {
chart: null,
dataset: null,
file: null,
- typstContent: "",
+ markdownContent: "",
};
const EMPTY_WORKSPACE_ROUND_ARTIFACTS: WorkspaceRoundArtifacts = {
@@ -486,12 +486,12 @@ const deriveWorkspaceSnapshotFromMessages = (
for (const part of message.parts) {
const partRecord = part as Record;
if (
- partRecord.type === "typst-content" &&
+ partRecord.type === "markdown-content" &&
typeof partRecord.content === "string" &&
partRecord.content.length > 0
) {
- snapshot.typstContent = partRecord.content;
- markViewUpdated("typst");
+ snapshot.markdownContent = partRecord.content;
+ markViewUpdated("markdown");
}
}
diff --git a/lib/typst-worker.ts b/lib/typst-worker.ts
deleted file mode 100644
index 39b938c..0000000
--- a/lib/typst-worker.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { configureTypstCompilerFonts } from "@/lib/typst/font-assets";
-import type { WorkerMessage, WorkerResponse } from "@/types/worker";
-
-let isInitialized = false;
-let $typst:
- | Awaited["$typst"]
- | null = null;
-
-const origin = self.location.origin;
-
-const initTypstCompiler = async () => {
- try {
- postMessage({
- type: "init-start",
- message: "Loading wasm...",
- } as WorkerResponse);
-
- const typstModule = await import("@myriaddreamin/typst.ts/contrib/snippet");
- $typst = typstModule.$typst;
- configureTypstCompilerFonts($typst);
-
- $typst.setCompilerInitOptions({
- getModule: () => `${origin}/typst_ts_web_compiler_bg.wasm`,
- });
-
- $typst.setRendererInitOptions({
- getModule: () => `${origin}/typst_ts_renderer_bg.wasm`,
- });
-
- postMessage({
- type: "init-start",
- message: "The compiler is being initialized",
- } as WorkerResponse);
-
- isInitialized = true;
- postMessage({ type: "init-complete" } as WorkerResponse);
- } catch (error) {
- const errorMessage = error instanceof Error ? error.message : String(error);
- postMessage({ type: "error", error: errorMessage } as WorkerResponse);
- }
-};
-
-const compileSvg = async (content: string) => {
- try {
- if (!isInitialized) {
- throw new Error("Compiler not initialized");
- }
-
- if (!$typst) {
- throw new Error("Compiler not initialized");
- }
-
- postMessage({ type: "compile-start" } as WorkerResponse);
-
- const svgString = await $typst.svg({
- mainContent: content,
- });
-
- if (!svgString) {
- throw new Error("Failed to compile typst content");
- }
-
- postMessage({ type: "compile-complete", svg: svgString } as WorkerResponse);
- } catch (error) {
- const errorMessage = error instanceof Error ? error.message : String(error);
- postMessage({ type: "error", error: errorMessage } as WorkerResponse);
- }
-};
-
-self.onmessage = async (event: MessageEvent) => {
- const { type } = event.data;
-
- switch (type) {
- case "init":
- await initTypstCompiler();
- break;
- case "compile":
- await compileSvg(event.data.content);
- break;
- case "terminate":
- self.close();
- break;
- default:
- postMessage({
- type: "error",
- error: `Unknown message type: ${type}`,
- } as WorkerResponse);
- }
-};
diff --git a/lib/typst/compiler.test.ts b/lib/typst/compiler.test.ts
deleted file mode 100644
index 32c7fc8..0000000
--- a/lib/typst/compiler.test.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * @jest-environment node
- */
-
-import { createTypstCompiler } from "./compiler";
-
-describe("compileTypst", () => {
- it("compiles valid Typst content to SVG", async () => {
- const compileTypst = createTypstCompiler({
- svg: async ({ mainContent }) => ``,
- });
-
- const result = await compileTypst("= Hello\n\nThis is *Typst*.");
-
- expect(result.ok).toBe(true);
- if (!result.ok) {
- throw new Error(result.error);
- }
- expect(result.svg).toContain("