Follow-up to #2054: once an in-flight send is released by switching conversations, the composer now waits until the session store has attached the opened conversation before it can send or stop, so a quick click or Enter can no longer post the new draft into the previous conversation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Switching conversations in the workbench while a session creation, message, upload or stop was still in flight let it finish into the conversation now shown (navigating back, refilling another draft, attaching the upload there, or leaving STOP stuck). The composer now fences in-flight work by the conversation the pane was navigated to, generalising the knowledge-base hand-over guard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build the material-library route schema once in a template PGlite database and load each test's isolated database from its dump, instead of booting a new PGlite and running the full schema bootstrap per test. File time in CI 64.6 s -> 32.8 s, slowest test 5.1 s (timeout) -> 1.25 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On pull requests and merge-queue entries, build the main image only when Dockerfile, .dockerignore, package manifests, lockfile, pnpm workspace config, .npmrc, packages/**, scripts/**, next.config.*, tsconfig*.json or ci.yml change; app-only changes are already compiled by the E2E build. Pushes to main keep building unless only docs changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split the serial check job into Lint & Typecheck, four root unit-test shards, package tests and storage tests, with a final job that keeps the required check name "Lint, Typecheck & Unit Tests" and fails unless every split job succeeded. A shared setup-workspace action installs with --ignore-scripts and restores the @openmaic package dist from a content-keyed cache; the lint job still runs the real postinstall to keep the tracked-file check honest. The required check drops from ~20 min to ~5 min.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each vitest worker now gets its own database for the PostgreSQL contract suite, because advisory locks are database-wide and files sharing one database blocked each other's schema bootstrap and owner claims. Takeover tests retry the claim while an aborted worker's in-flight step still holds the row; the extraction budget test waits for the budget to elapse instead of a fixed delay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The E2E job restored .next/cache keyed only on the lockfile and next.config.ts, so builds reused a cache saved before newer CSS landed and the new mobile specs failed; the 80-test reporter cadence made it look like a hang until the job timeout. Drop the cache restore; in CI report every test, set an explicit 30 s per-test timeout and a 9-minute global timeout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the merge_group trigger so the required check reports for merge queue entries, and read merge_group.base_sha where the workflow used the pull request base (package version bump check, main-image change detection). Contract test updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adapt the existing homepage for phones without duplicating its business logic.
- Shared adaptive layer: media-query subscriptions, a server UA hint for the first paint, VisualViewport/safe-area helpers, and a phone page-scroll boundary. The workspace 1024px pane rule is unchanged.
- One homepage with phone containers: compact composer, course-options dialog with a dedicated Course materials view, and a searchable library drawer with touch-accessible course actions; desktop keeps the inline library and attachment popover.
- Attachment capability cleanup is page-owned, so switching the document parser removes unsupported files (with a notice) regardless of which panel is open.
- Recorder lifetime: the stop control stays visible while recording/processing, and unmount stops tracks, recognition, timers and pending transcription.
- 16px inputs, no horizontal overflow, 12 locales updated, plus unit and Playwright coverage.
Follow-ups: homepage landscape layout (with classroom playback adaptation); Safari + Sogou blank area above the keyboard.
Run the App-domain PostgreSQL contracts step with 30 s test and hook timeouts instead of the 5 s default; schema setup and race tests against the shared database service intermittently exceeded it on busy runners. The workflow contract test now skips only --testTimeout/--hookTimeout when checking that the step lists every tracked *.pg.test.ts, so any other option still fails it.
A failed /preview returned its error message to the caller but logged nothing about why. Every 5xx preview_request line now carries a fixed errorCode (deadline_exceeded, resource_mode_unsupported, chromium_unconfigured, browser_launch_failed, browser_crashed, browser_timeout, scene_not_found, empty_image or internal), derived from the error's name and known message prefixes; the message itself is never logged. 5xx preview lines are written at ERROR to stderr. HTTP responses are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add AGENTS.md with the rules the automated Codex review follows (severity, re-review scope, focus areas) and a Review Threads section in CONTRIBUTING.md: answer every thread before resolving it, handle outdated threads, keep a changing PR in draft, and a failed or stuck Codex review does not block merge.
The materials block in the agent system prompt is now static guidance that does not list the attached set, so attaching a file, fetching a page or finishing an extraction no longer changes the prompt head and invalidates the provider prefix cache. list_materials, follow-up attachment notes and fetch_url results still tell the model what is attached; a new session's opening attachments now reach the first run through the same attachment note. Closes#1858.
Node ran as PID 1 and never reaped orphaned Chromium processes, so a long-running preview container filled its pid limit with zombies and every browser launch then failed (code 133). tini (pinned 0.19.0-1+b3 from the same Debian snapshot) is now the entrypoint; it forwards signals and reaps orphans.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Make HTML <audio>/<video> in the main interactive frame and lecture narration mutually exclusive, in the classroom and the standalone HTML player: frame media starting pauses narration, and resuming narration with Play sends a fire-and-forget pause to the current frame. Media ending does not auto-resume narration. WebAudio is not intercepted and no acknowledgement is awaited.
Make knowledge-base failure guidance actionable, localize material business errors in the run's interface language, stop conversation-only sources from pointing at a nonexistent material location, and keep ground-drop metadata available at drag activation so a single-move release unfiles the item. Follow-up to #1892.
Limit concurrent TTS requests per connection (managed id, or provider + base URL + key hash; configurable, default 1), carry Retry-After cooldowns across narration requests without applying them to quota refusals, and report classified failure codes in generate_tts / generate_actions results. Bumps @openmaic/generation to 0.3.27.
Uncaught action execution rejections stranded normal playback and whiteboard reconstruction. Log and skip failed stored actions at both await boundaries while retaining generation checks, so later actions and completion continue without restarting stale playback.
Closes#2013
Export renderScenePrompt('slide-content', { imageElementEnabled, generatedImageEnabled, generatedVideoEnabled }) returning the exact system prompt the slide generator sends; generateSlideContent now builds its prompt through the same helper so they cannot drift. @openmaic/generation 0.3.26.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slide formula with an upright symbol (\Delta, \Omega, \mathrm{...}, a literal Δ) made the exported deck unreadable: mathml2omml writes a <w:rPr> into styled runs and the slide part never declares the w: prefix. postProcessOmml now removes those elements and gives styled runs the same Cambria Math <a:rPr> as other runs, after their <m:rPr>.
Fixes#1986
New users no longer get Chinese speech recognition and an English browser voice regardless of their language. An unset ASR language uses auto-detection when the provider supports it and otherwise resolves from the interface locale; browser recognition always receives an explicit language. Chinese text keeps the interface's zh-* region for preview, classroom narration and discussion speech. Saved ASR language choices are preserved.
Fixes#1548
Thumbnail markers no longer flash a missing-narration warning on first mount or after a scene reorder while the legacy local-audio lookup is pending. The issue collector keeps its evidence contract; only the thumbnail filters local-audio-unchecked issues. A failed lookup resolves as no cached audio so warnings are not hidden indefinitely.
Related to #1900
Native wb_draw_shape now takes an SVG path with a viewBox, plus rotation, transparent fill and optional stroke, with strict geometry validation. Removes the rectangle/circle/triangle presets and the shape field from the Native tool; stored shapes and Legacy preset playback stay compatible. Prompt guidance states coordinate, scaling, rotation and arc principles with one-line standard parts. Adds a whiteboard-shape evaluation harness.
Closes#1853
When an assigned image's real aspect ratio differed from the model's image box by more than 10%, reconciliation kept the width and recomputed the height, so a box flatter than its image grew downward over the elements below it. Fit the image like object-fit: contain instead: the largest box with the image's ratio inside the model's box, centered there. @openmaic/generation 0.3.25.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- validation skips elements and canvas fields the patch leaves unchanged; new or changed ones are validated per declared type with precise property pointers; line elements accept rotate
- read_stage text view returns field-level writable pointers
- str_replace: missing-field and missing-anchor errors list real fields / closest excerpt; path-less str_replace resolves to a unique text-view field
- arrays and ids: append at index == length or -, clearer out-of-bounds errors, server-owned element/action ids preserved, duplicate action ids rejected
- structural leniency: /content/elements alias, unambiguous op inference, missing action ids assigned, add_element renderer defaults
- speech audio is kept only when the batch-start action with the same id had identical text
- malformed tool-call JSON is never stored or replayed; the parse diagnosis is returned to the model and the call is blocked before execution
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MinerU's content_list describes every cropped image referenced by img_path, not only items of type "image": tables (caption in table_caption) and display equations are cropped into the images dict as well. Read page_idx and bbox for table and equation items too, and use the table caption as the description; equations keep no description.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- system prompt: describe results for learning-content creators without internal identifiers, claim only what tool results show, carry requests through, keep the final report brief; scope "answer without a tool" to pure questions
- pro-editing: drop the contradictory hard-call-cap claim; finish large requests instead of handing work back, with a non-converging page noted in the final report
- stage-design: skip plan confirmation when the user already gave a full structure or asked to proceed; check narration audio via generate_tts counts and read each page's actions subtree once for blank or off-topic narration; brief closing report
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- generate_scene: describe brief (teaching intent of one page) and materialFacts (content written onto the page)
- narration results report the voice actually used and whether existing audio was kept; set_roster notes that existing audio keeps its voice until generate_tts runs with force
- render_scene_preview accepts a 1-based page order or /scenes/<selector>, lists pages on a miss, and always renders at device scale 1
- use_material_media explains when an ast_ id is already a usable src
- import_pptx: clearer error for legacy .ppt files
- workbench tool cards show page labels instead of raw JSON paths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test-only. The heartbeat-loss scenario failed intermittently on PostgreSQL (expected 2 transactions to be 0) because of three races in the test:
- The stale run read the shared test clock, so after `expire` its heartbeat could renew the lease before the takeover claimed; the run now keeps the time it claimed at.
- The claim skips a row a heartbeat has locked (FOR UPDATE SKIP LOCKED); the takeover is retried, bounded, and asserted.
- A fixed 100 ms sleep assumed a post-takeover heartbeat had returned; the scenario now waits for one that reports the loss.
On failure the scenario releases the gated run before rethrowing, so its heartbeat timer does not outlive teardown.
Version 1.3.0-rc.1, the first release candidate for 1.3.0 (knowledge base). CHANGELOG section with breaking changes, release conditions and known issues; README News entries.
UI-only polish of the knowledge base page; no data flow, API, persistence or i18n changes.
- The top-level drop ground folds away at rest and opens only while a file in a folder is dragged, so the list card sizes to its rows.
- Usage becomes one panel with the knowledge base allowance and the separately counted file storage side by side, each with a progress bar.
- File rows get type-aware tinted icons, status chips, taller rows with dividers and a hover tint; folders use the accent folder icon and open folders show their files on a sunken ground.
- The empty state is centred with an upload action.
DeepSeek's fetch endpoint sends response headers only after it has fetched the whole page, so the fixed 10s header timeout aborted slow pages that the provider still completed and billed. Use the request deadline (bodyTimeoutMs, default 30s) for the header wait, as the Firecrawl provider already does.
Fixes#1984
Whiteboard text and code elements can now be selected and copied: only their wrappers get select-text, copied code keeps indentation without line numbers, and the rest of the canvas stays non-selectable. Full-canvas motion wrappers are pointer-events none with only the element subtree receiving events, so earlier elements stay hit-testable, and dragging from shapes or empty canvas still pans. Selection is scoped to the whiteboard via a selectable prop, so slide playback is unchanged. Part of #1945 (step 1).
Key the cached legacy-audio evidence in useSceneIssues on the inputs observeLegacySpeechAudio reads (stage, scene id, order, and each speech action's id, text, audioId and audioInvalidated) instead of the scene object, so unrelated immutable edits no longer flash a local-audio-unchecked marker on the thumbnail. Nothing is persisted; real issues still surface. Also aligns the zh-TW sceneHasErrors label.
Adds `integration/workbench-home` to the CI `push` and `pull_request` branch lists, following the existing integration branches, so the branch cut from `main` and its sub-PRs run the main CI. Tracked in #1957.
Interactive scenes load KaTeX, Three.js (with addons), Chart.js and the Tailwind v4 browser runtime from the deployment (/vendor/interactive, configurable via the build-time INTERACTIVE_ASSET_BASE_URL) instead of public CDNs. KaTeX is injected only into pages with detectable math, synchronously at the start of head (same-origin and immutable-cached), with the #1966 fallback when the asset fails; reprocessing refreshes platform-injected KaTeX tags against the current asset base. A parser-based mapper rewrites compatible public CDN URLs for these libraries to the hosted copies, drops integrity, leaves unknown URLs untouched and is idempotent; stored scenes are reprocessed on open and before export, and exports fetch hosted assets directly. Legacy Tailwind v3 Play CDN is not mapped. @openmaic/generation 0.3.24. Closes#1971.
* fix(renderer): fit short Cartesian charts (#1968)
ECharts default grid margins consume 145px and collapse short charts. Use relative margins with label containment and a scrollable legend in the app and SDK, leave room above line labels, and preserve explicit imported plot rectangles. Register ECharts 6 containment compatibility and cover real SVG layout, text bounds, and resize behavior.
* fix(renderer): keep compact chart endpoint labels visible
* fix(deps): sync renderer ECharts peer constraint in lockfile
* fix(renderer): size chart text and legend margins in pixels
Percentage text margins over-reserved space on normal-size charts: an 800x450 two-series line plot shrank from the 305px main baseline to 214px. Use 22px label headroom and a 26px legend row, and verify normal-size plot height and resizing in both builders.
Give the prior-run and park-attempt PostgreSQL contract suites separate schemas and drop leftover schemas before setup, so parallel suites no longer truncate each other's tables and deadlock. Test-only change. Closes#1954.
TTS provider presets in YAML can now declare a provider-local voice list and options.defaultVoice. Config validation rejects voices on non-TTS presets and defaultVoice values outside the configured list; cloned voices are preserved. Documented in all doc locales.
Formulas deferred by the rendering budget stay hidden until they are typeset, so raw LaTeX no longer flashes on dense slides; sources that cannot be typeset are still shown as text. Budgets and scheduling are unchanged. Bumps @openmaic/renderer to 0.1.17.
Related to #1869
Co-authored-by: YizukiAme <YizukiAme@users.noreply.github.com>
Adds the owner-scoped material library ("Knowledge base" in the UI) from the integration branch, as tracked in #1736 (RFC: Discussion #1716).
- Library page from the left rail: one folder-and-file list with search, automatic parsing after upload, Parse again, in-place folder creation and renaming, drag-to-move, source and folder deletion with confirmation, usage and separately counted file storage.
- Composer `@` and toolbar picker over the whole library; failed materials cannot be attached from any entry point.
- Agent tools to list, organize and parse materials, with one-line summaries in tool rows; deletion stays page-only.
- Originals and extraction outputs live in the asset pool under generic `asset_root_refs`; extraction is reused across conversations.
- Every successful upload, from classic and Pro and including existing undeleted uploads, stays in the library until explicitly deleted (#1898). The 24-hour sweep only reclaims unfinished upload reservations.
- `@openmaic/storage` 0.38.0.
Release conditions (breaking): stop all old instances before this version accepts writes, do not roll back below this release, and enable `MATERIALS_POOL_BACKFILL=1` only after every old instance has stopped. See CHANGELOG for schema additions and `ASSET_COLLECTION_GRACE_MS`.
Inject the platform's KaTeX at the start of <head>, ahead of page scripts, guarded by a data-openmaic-katex marker instead of the word "katex", with no-op stand-ins when the CDN is unreachable, so pages that ship their own KaTeX no longer crash with a ReferenceError and init()-filled labels typeset on first paint. Adds an interactive-math prompt snippet and bumps @openmaic/generation to 0.3.23.
Fixes#1538
Add @openmaic/geometry, a browser-independent validator for SVG path syntax, finite view boxes and transforms and bounded resources, with structured errors and shared shape-to-text recovery. Generation, rendering, both editors and PPTX export use it: invalid shapes with a label render as text, unlabelled ones are omitted, stored records are not rewritten, and recovered shapes cannot be resized. Imported PPTX labels keep their own insets. Bumps generation 0.3.22, renderer 0.1.16, editor 0.0.12 and geometry 0.1.0.
Closes#1851
Co-authored-by: cosarah <cosarah@users.noreply.github.com>
Share a per-frame markup allowance (256k characters) across live prose roots, let thumbnails use only what the foreground leaves and finish in idle time, reuse deferred typesetting results, and keep snapshot rendering synchronous. Matrix-heavy slides no longer block the main thread in one long task; ordinary and dense slides still typeset before paint. Bumps @openmaic/renderer to 0.1.15.
Fixes#1869
Co-authored-by: YizukiAme <YizukiAme@users.noreply.github.com>
Phase 1 of Course issues (#1900): a pure collectCourseIssues in @openmaic/dsl 0.12.0 derives issues from the current course document (speech without audio, generated-media placeholders, dangling cues, action-content lint) with stable ids and structured evidence, and the editor thumbnails show warning markers that clear when the issue is fixed. Narration issues are skipped when the workspace cannot generate audio.
Related to #1900
Co-authored-by: Oliveryfaso <Oliveryfaso@users.noreply.github.com>
Pass catalogue voice gender to the agent-profile prompt, ask the model to keep voice, name and persona gender consistent, persist a language-independent voiceDesign.gender, and make the fallback voice pick prefer the agent's gender. Catalogues without male/female data keep the previous behavior.
Closes#1084
Co-authored-by: Frank-zhu2000 <Frank-zhu2000@users.noreply.github.com>