* feat(app-plugin-agents): answer with an online agent when a runner agent cannot run
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* fix(ci): align chat fixtures and allow cold route imports
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* fix(app-plugin-agents): require an available model for online fallback
* fix(agents): align fallback copy and consolidate model regressions
* fix(agents): use claim eligibility throughout chat fallback
* fix(agents): suppress repeated online fallback failure notices
---------
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* fix(app-plugin-agents): merge work per actor and hand variables only to trusted runners
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* fix(app-plugin-agents): deliver run variables after a final trust check, and share claim eligibility
* fix(app-plugin-agents): isolate variable access failures and restore undelivered claims
* fix(app-plugin-agents): renumber the secrets-refused migration after the runner tool slots one
* fix(app-plugin-agents): send variables with every run unless marked for team runners only
* fix(app-plugin-agents): keep new wait and variable metadata optional
* fix(app-plugin-agents): send wait reasons as codes with params and word them on the client
* test(agents): describe claim regressions by behavior and use neutral examples
* fix(agents): enforce team-only variables for jobs
* fix(agents): expose job secret refusal to custom sources
---------
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* feat(ui-library): add the approval-ui block
Presentational components for an approval process: progress, timeline,
route preview, parallel branches, receipts and an action bar with forms.
They take data already shaped and translated for display and depend on
no plugin. Includes a preview demo, its registry example and tests.
* docs(app-skills): list the approval-ui block in the UI Library catalog
* feat(agents): limit runs per coding tool on a runner
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* feat(agent-runner): limit runs per coding tool with --slots
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* feat(agents): edit and show limits per coding tool on the runtimes page
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* chore: format and add the changeset for limits per coding tool
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* fix(agents): make the fields for limits per coding tool optional in shared types
* fix(agents): align queue component with per-tool wait reason
* feat(agents): one tools table per runtime, saved with its settings
---------
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* feat(app-plugin-agents): send custom request headers and a per-conversation session id to model services
A model service can carry request headers of its own (a value may be secret: sealed like the API key and never answered) and name a session header, such as OpenCode Go's x-opencode-session, under which every model call of a conversation sends one stable id. Every request to a provider names the plugin first in its user agent.
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* fix(app-plugin-agents): send OpenCode's session id without a service setting
A model service no longer names a session header. A call to an OpenCode base URL (Zen or Go) carries x-opencode-session by itself, with one session id per conversation and a new one for each call outside any; the id hashes the conversation's subject and thread. The service form drops the session-header field.
* refactor(app-plugin-agents): keep only the OpenCode session header
Plain services no longer send request headers of their own: the headers and headersEncrypted columns, the service API's headers field, the form's header table and the sealing they needed are removed. A call to an OpenCode base URL (Zen or Go) carries x-opencode-session automatically, with one session id per conversation and a new one for each call outside any, and every request names the plugin first in its user agent.
---------
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
Add record lifecycle definitions, transactional transitions, durable effects, recovery, jobs integration, and React bindings. Include ticket, expense, and office flow examples in the Examples application, with concurrency and database compatibility fixes.
Group generated plugin tests by source directory, provide shared Node test configuration, and document the layout. Include TSX tests in Node projects with a regression test for test discovery.
The daily trend chart fixed its Y axis at 56px, which fits token counts but clips cost ticks such as US$20.00 at the chart's left edge. Let recharts measure the formatted labels instead.
Co-authored-by: Project lead <agent+studio-project-lead@studio.noreply>
* fix(projects): keep an invitation's row menu open when the list renders again
The columns depended on the whole useMutation result, a new object each render, so every cell remounted.
* test: open popups the way a user does
A Base UI trigger is wired to its popup only once React runs the passive effects of the render that mounted it, so a bare fireEvent.click right after the trigger appears is dropped under load. Open popups with userEvent, fix the timing races this exposed, and cancel pending replies when the UI Library chat demo resets.
* chore: add a changeset for the new test dependency
* Add the agents, projects, knowledge and releases plugins
Four plugins for agent-run applications, the UI Library's agents and projects items, and the generated UI Library catalog of the application Skill with its check.
* Wait on the sent attachment's link as it renders last in the chat test
* Keep the demo chat's reply timers off window, so they outlive a test's environment quietly
* Update the plugins, templates and UI Library for the new runtime
Device authorization, scoped API keys, service accounts, user invitations and preferences, secrets-backed credentials, the inbox and the cn package across the existing plugins, examples and the three templates, with the UI Library's auth, inbox, authorization and component items.
* Declare the UI Library site's favicon
* Use the NocoBase favicon on the UI Library site
* Fix generated-app shell tests and the Hub's user deletion test
Inline the authentication client in the templates' Vitest config so its guards share the test's react-router, and update the Hub test to the users plugin's new copy and spinner label.
Encrypted secrets, a command manifest derived from the API document, environment-variable manifests, sample data and on-demand Apps in the runtime, plus the new agent protocol, application CLI client, agent runner and Docker App Host packages.
* fix(app-plugin-ai-employee): translate the nocobase-ai Registry item in the plugin namespace
The Registry item translated through its own locale files and a useAITranslate()
hook that picked a language by prefix and fell back to the English default, so a
key it never defined showed English in every language without anything noticing.
It now calls useTranslation('@nocobase/app-plugin-ai-employee') like the file
plugin's Registry item and the plugin's own pages, and its copy moves into the
plugin's client/locales, which the registered plugin loads.
Four keys the item used without defining them get English and Chinese text, and
a test fails when a key the Registry passes to t() is missing from either locale
file or a file translates outside the plugin namespace.
* feat(examples): add an AI employee tasks example and a global AI employee entry
@nocobase/app-plugin-ai-employee-example registers the AI employee iris and the
read-only tool example-ticket-history through AIResourceRegistrar, declares the
/ai-employee-example route with a fallback page, and publishes a tasks-page
Registry item that replaces it: support tickets whose AIEmployeeShortcut offers
an auto-sent analysis and a reply left in the composer, with the selected ticket
as work context, and a button that starts a queue triage through the global chat
controller.
The examples template installs the nocobase-ai item, wraps its signed-in layout
in a global AI employee entry (a floating trigger that opens the shared
conversation as a side panel and expands into a dialog), registers the example
plugin, installs its tasks page and links it from the homepage.
* test(templates): let the Examples layout carry its AI employee entry
The framework alignment check compares the Examples layout with Default byte for
byte, and the global AI employee entry wraps the signed-in shell. Like the
notification button in the header, it is an Examples-owned demonstration: the
check now removes exactly that wrapper, its import and its marker comment,
re-indents what it wraps, and still requires the shell itself to match Default.
* feat(app-plugin-ai-employee): release unused conversation checkpoints on a schedule
A recurring job on the application's jobs service releases the agent
checkpoints of conversations nobody has used for a week, every day at
03:00 UTC by default. Releasing a conversation sets its thread to 0 and
deletes the checkpoints of every thread it has had, in one transaction per
batch, only while the conversation is still unused. A conversation whose
latest message asks for a tool call is never released.
The next run on a released conversation replays its latest stored messages
onto a fresh thread. This applies only to conversations kept in the
plugin's own tables, never to a resume or a tool decision, and never to a
thread-0 conversation that still has a checkpoint there or has no messages,
so existing runs behave as before.
The schedule is configured under ai.checkpointCleanup and validated by
config check.
* feat(app-plugin-ai-employee): default a conversation's thread to 1
A conversation starts on thread 1 when AIConversationsManager creates it,
but the aiConversations.thread column defaulted to 0, so a row inserted
without a thread looked like a conversation whose checkpoints the cleanup
had released. A new migration sets the default to 1.
Thread 0 now always means a released conversation, so the agent decides
whether to replay stored messages from the thread number alone, without
reading the checkpoint store.
* fix(app-plugin-ai-employee): read each cleanup batch's latest messages in two queries
The checkpoint cleaner looked up the latest message of every candidate
conversation with its own query. It now finds each conversation's latest
message id with one groupBy over the batch and reads those messages with one
more query.
The aggregate goes through the Repository rather than the query builder,
because it reads a bigint result back exactly on every dialect, and a
message id is a snowflake beyond the integers a JavaScript number holds.
* refactor(templates): move Playwright tests to tests/playwright/ and drop the local AI e2e test
Keep every test under tests/: Playwright's testDir is tests/playwright/,
Vitest excludes that directory, and tsconfig.node.json typechecks it. The
directory ships with the template like the rest of tests/. The optional
local AI server test is removed; test:e2e still passes with no tests.
The application Skills describe the new location, and the upgrade Skill
says how an existing application moves its e2e/ files.
* refactor(app-template-hub): exclude tests/playwright/ from Vitest like the other templates
The template alignment test requires the three templates to share one
vitest.config.ts. Hub has no Playwright, so the exclusion changes nothing
there today, and a Hub application that adds Playwright already has it.