updateThread changes the thread that the repository returns. The tests
shared one thread object, so a project move in one test leaked into the
next. The fixture is now frozen and each lookup returns a copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
enqueue now decides on the system kind once. The custom-agents setting
applies to every other kind. A system message without a registered
provider now fails with "Agent not found", also when the admin turned
off custom agents.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The queue and the queue consumer each mapped a message kind to its chat
surface. chatSurfaceForKind and canContinueThreadOnSurface in
agent-thread-access now hold that mapping and the access rule of each
surface. The consumer passes the surface to its access check instead of
a boolean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
resume used the client run id when one was sent and the suspension run
id otherwise. It now always uses the run id of the open suspension.
A client run id that does not match it is refused up front.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
runTurn took seven positional arguments, and the resume path passed
`undefined` for the admission. It now takes one object. It also reads
the agent id from the thread, so it does not need the provider.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sendMessage and prepareChatMessage now share one queueInput builder.
The payload rules for options, attachments, message ids and hidden
turns are in one place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
createThread checks access on the requested working project. It now
returns an existing thread only when that thread is in the same project.
A thread in another project is not found.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every system turn, start and resume, now attaches the attachment store.
A later turn can then load the files of earlier turns.
Only a completed system turn sends `done`, like Preview. `resumeRun`
rejects when the resumed turn fails, so the route can send an error.
The background `resume` still logs the failure.
The host sets `persistence` after the provider run options, and the
provider run options type omits it.
Tests cover thread creation for a new session and the error event of a
refused system turn.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A system turn now runs under one controller. The provider gets its signal
in prepareTurn, so a user cancel reaches the provider. The abort signal in
the run options of the handle now stops the turn also after admission.
The turn runtime takes an optional chat controller for this. Other chat
surfaces keep their default controller.
isBusy and getStatus use one status rule. A queued message now blocks a
project change. Cancel checks thread ownership only, and a test covers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A module can register a code-defined instance agent through a
SystemAgentProvider. SystemAgentRegistry keeps the providers.
- SystemAgentExecutionService manages the threads and turns of a system
agent. It checks the access floor (owned thread, project:read on the
working project) and then the provider.
- The queue accepts the system kind only for a registered provider. The
consumer sends system turns to the execution service.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Add the system-chat:<userId> memory resource, the system_chat
transcript source and the system value of AgentChatSurface.
canContinueSystemThread is the access rule of the surface.
- The cross-main cancel payload carries surface. It keeps
productionN8nChat for mains on the previous version.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the SSE relay and attachment helpers from AgentChatController into
AgentChatRelayService, so another chat controller can share them. The
code does not change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Map the agents.scope column on the Agent entity. An instance agent is
code-defined and belongs to no project. AgentRepository.ensureInstanceAgent
creates its row with an insert that ignores a concurrent duplicate.
Type Agent.projectId as string | null. Project-scoped repository queries
and writes filter on scope = 'project'. The queries return ProjectAgent,
which has a non-null projectId. The project-agent APIs do not see
instance agents and report them as not found.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>