Adds the `activity` tool. `list` reads further back than the window the agent
is handed, or filtered to one kind of event or one resource; `expand` opens a
single entry and returns everything else the log knows about the same
resource. That is the read the deferred `(resourceType, resourceId, id)` index
now exists for, added in its own migration rather than up front.
Expanding never fetches the live resource. `workflows`, `executions` and
`credentials` already do that, with their own permission checks — so an
expansion names the call to make instead, and the entry carries the id to make
it with. A missing id comes back as `notFound` rather than an error: entries
are pruned, so a stale id the agent read from an earlier block is an ordinary
outcome, not a failure.
Closes a scoping hole this feature opened. The window read had no project
filter, so a conversation not bound to a project would have been shown
activity from every project on the instance, including ones the user cannot
open. Reads are now bounded by the conversation's project, or by the user's
accessible projects when there is none, and an entry with no project is
excluded outright — there is no way to prove it is in scope. An expand outside
that scope is indistinguishable from a pruned id.
Keeping deletions visible under that rule needed the writer to change too. A
`workflow-deleted` event fires after the sharing rows that would name its
project are gone, so the project is recalled from the log's own earlier
entries for the same resource; credential updates and deletes do the same.
Without it the entries most worth surfacing would have been silently dropped
by the new filter.
The tool is always loaded when the log is enabled, and absent entirely when it
is not. The activity block names it, so deferring it would price every expand
at search_tools plus load_tool.
Adds an append-only `activity_event` table fed from the event bus, so the
instance agent can be handed what the user has been doing instead of spending
a turn discovering it. Entries are pointers — category, verb, and the ids
needed to fetch the full record — not copies of the resource.
Two decisions worth flagging:
`resourceId` carries no foreign key, unlike every other reference in the
schema. An entry has to outlive what it describes; a workflow deletion is the
entry most worth keeping, and a cascade would erase it exactly when it became
interesting. `workflow_review_activity.workflowId` made and undid this same
mistake. Neither vocabulary column gets a CHECK, for the reason that table
dropped its own: a feed covers more kinds with every feature, and each
widening is another SQLite table recreation.
The relay registers in `BaseCommand` rather than the instance-ai module.
`workflow-post-execute` is emitted wherever the execution ran, which in
scaling mode is the worker, and that module loads on main only — registering
there would silently lose every execution entry under scaling.
Off by default behind `N8N_INSTANCE_AI_ACTIVITY_LOG_ENABLED`; the flag is
read once at init, so a disabled instance carries no listeners. Pruning runs
on the leader main only, by age with an entry-count backstop.
Postgres schema docs are not regenerated — that leg needs Docker, which is
unavailable here. The sqlite docs are current.