feat(core): Support multi-main deployments for Instance AI (no-changelog) (#33296)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaakko Husso
2026-07-06 10:25:44 +03:00
committed by GitHub
parent 0647cba555
commit 96ba6a82f9
29 changed files with 1745 additions and 648 deletions
@@ -25,6 +25,8 @@ export type PubSubEventName =
| 'restart-event-bus'
| 'relay-execution-lifecycle-event'
| 'relay-chat-stream-event'
| 'relay-instance-ai-event'
| 'relay-instance-ai-task-control'
| 'relay-chat-human-message'
| 'relay-chat-message-edit'
| 'reload-sso-provisioning-configuration'
@@ -580,6 +580,34 @@ describe('RunStateRegistry', () => {
expect(registry.hasSuspendedRun('thread-1')).toBe(true);
expect(registry.getSuspendedRun('thread-1')).toBe(suspendedState);
});
it('rehydrates the message-group indexes when they start empty (restart-resumed orphan)', () => {
// Simulate a restart: no prior startRun, so threadMessageGroupId /
// runIdsByMessageGroup are empty when the orphan is re-suspended.
const suspendedState = createSuspendedRunState({
threadId: 'thread-1',
runId: 'run_orphan',
messageGroupId: 'mg_orphan',
});
registry.suspendRun('thread-1', suspendedState);
expect(registry.getMessageGroupId('thread-1')).toBe('mg_orphan');
expect(registry.getRunIdsForMessageGroup('mg_orphan')).toEqual(['run_orphan']);
});
it('does not duplicate the runId in the group when suspending a run started normally', () => {
const started = registry.startRun({ threadId: 'thread-1', user: { id: 'u1', name: 'A' } });
const suspendedState = createSuspendedRunState({
threadId: 'thread-1',
runId: started.runId,
messageGroupId: started.messageGroupId,
});
registry.suspendRun('thread-1', suspendedState);
expect(registry.getRunIdsForMessageGroup(started.messageGroupId!)).toEqual([started.runId]);
});
});
describe('activateSuspendedRun', () => {
@@ -625,6 +653,24 @@ describe('RunStateRegistry', () => {
expect(result).toBeUndefined();
});
it('rehydrates the message-group indexes on activation when they start empty', () => {
// Restart-resume path: orphan re-suspended then reactivated on a main
// that never ran startRun for this thread.
const suspendedState = createSuspendedRunState({
threadId: 'thread-1',
runId: 'run_orphan',
messageGroupId: 'mg_orphan',
});
registry.suspendRun('thread-1', suspendedState);
// Clear the group indexes to isolate activation's own rehydration.
registry.deleteMessageGroup('mg_orphan');
registry.activateSuspendedRun('thread-1');
expect(registry.getMessageGroupId('thread-1')).toBe('mg_orphan');
expect(registry.getRunIdsForMessageGroup('mg_orphan')).toEqual(['run_orphan']);
});
});
describe('cancelSuspendedRun', () => {
@@ -156,16 +156,29 @@ export class RunStateRegistry<TUser = unknown> {
}
}
this.threadMessageGroupId.set(options.threadId, messageGroupId);
if (!this.runIdsByMessageGroup.has(messageGroupId)) {
this.runIdsByMessageGroup.set(messageGroupId, []);
}
const groupRunIds = this.runIdsByMessageGroup.get(messageGroupId);
if (groupRunIds) groupRunIds.push(runId);
this.indexRunInGroup(options.threadId, messageGroupId, runId);
return { runId, threadId: options.threadId, abortController, messageGroupId };
}
/**
* Seed the message-group indexes for a run: map the thread to its current
* group and record the run under that group. Idempotent.
*
* Called on `startRun` and re-applied on `suspendRun`/`activateSuspendedRun`
* so a run resumed after a restart (where these maps start empty) repopulates
* the group association the SSE bootstrap relies on.
*/
private indexRunInGroup(threadId: string, messageGroupId: string, runId: string): void {
this.threadMessageGroupId.set(threadId, messageGroupId);
let groupRunIds = this.runIdsByMessageGroup.get(messageGroupId);
if (!groupRunIds) {
groupRunIds = [];
this.runIdsByMessageGroup.set(messageGroupId, groupRunIds);
}
if (!groupRunIds.includes(runId)) groupRunIds.push(runId);
}
getThreadStatus(
threadId: string,
backgroundTasks: BackgroundTaskStatusSnapshot[],
@@ -273,6 +286,12 @@ export class RunStateRegistry<TUser = unknown> {
state.startedAt = state.startedAt ?? activeRun?.startedAt ?? state.createdAt;
state.lastActivityAt = state.lastActivityAt ?? state.createdAt;
this.suspendedRuns.set(threadId, state);
// Re-seed group indexes: on a restart-resumed orphan these maps start
// empty, so without this the SSE bootstrap loses the group association.
if (state.messageGroupId) {
this.indexRunInGroup(threadId, state.messageGroupId, state.runId);
}
}
findSuspendedByRequestId(requestId: string): SuspendedRunState<TUser> | undefined {
@@ -306,6 +325,11 @@ export class RunStateRegistry<TUser = unknown> {
startedAt: suspended.startedAt ?? suspended.createdAt,
lastActivityAt: now,
});
// Re-seed group indexes for the reactivated run (empty after a restart).
if (suspended.messageGroupId) {
this.indexRunInGroup(threadId, suspended.messageGroupId, suspended.runId);
}
return suspended;
}
@@ -363,6 +363,109 @@ describe('InstanceAiController', () => {
expect(runSyncFrame).toContain('"planItems"');
});
it('should replay events that arrive while bootstrap snapshot fetches are in flight', async () => {
memoryService.checkThreadOwnership.mockResolvedValue('owned');
instanceAiService.getThreadStatus.mockReturnValue({
hasActiveRun: true,
isSuspended: false,
backgroundTasks: [],
} as never);
instanceAiService.getMessageGroupId.mockReturnValue('mg-1');
instanceAiService.getRunIdsForMessageGroup.mockReturnValue(['run-1']);
eventBus.getEventsForRuns.mockReturnValue([]);
eventBus.getEventsAfter.mockReturnValue([]);
let subscribeHandler: ((stored: { id: number; event: unknown }) => void) | undefined;
eventBus.subscribe.mockImplementation((_threadId, handler) => {
subscribeHandler = handler as typeof subscribeHandler;
return vi.fn();
});
// While the persisted snapshot is being fetched, a relayed event arrives:
// the early subscription keeps it flowing into the store, and the replay
// after the await must pick it up exactly once.
const midAwaitEvent = {
id: 7,
event: { type: 'run-finish', runId: 'run-1', agentId: 'a1', payload: {} },
};
memoryService.getLatestRunSnapshot.mockImplementation(async () => {
subscribeHandler!(midAwaitEvent);
eventBus.getEventsAfter.mockReturnValue([midAwaitEvent] as never);
return undefined;
});
const sseRes = mock<Response & { flush?: () => void }>({
setHeader: vi.fn(),
flushHeaders: vi.fn(),
write: vi.fn(),
end: vi.fn(),
flush: vi.fn(),
});
const sseReq = mock<AuthenticatedRequest>({
user: { id: USER_ID },
headers: {},
once: vi.fn(),
});
await controller.events(sseReq, sseRes, THREAD_ID, { lastEventId: undefined } as never);
// Subscription must be registered before the async bootstrap starts, so
// sibling mains keep relaying events for this thread during the awaits.
expect(eventBus.subscribe.mock.invocationCallOrder[0]).toBeLessThan(
memoryService.getLatestRunSnapshot.mock.invocationCallOrder[0],
);
const eventFrames = (sseRes.write as Mock).mock.calls
.map(([frame]) => String(frame))
.filter((frame) => frame.includes('run-finish'));
expect(eventFrames).toEqual([`id: 7\ndata: ${JSON.stringify(midAwaitEvent.event)}\n\n`]);
});
it('should clean up the subscription when the client disconnects during bootstrap', async () => {
memoryService.checkThreadOwnership.mockResolvedValue('owned');
instanceAiService.getThreadStatus.mockReturnValue({
hasActiveRun: true,
isSuspended: false,
backgroundTasks: [],
} as never);
instanceAiService.getMessageGroupId.mockReturnValue('mg-1');
instanceAiService.getRunIdsForMessageGroup.mockReturnValue(['run-1']);
eventBus.getEventsForRuns.mockReturnValue([]);
eventBus.getEventsAfter.mockReturnValue([
{ id: 1, event: { type: 'text-delta', runId: 'run-1', agentId: 'a1', payload: {} } },
] as never);
const unsubscribe = vi.fn();
eventBus.subscribe.mockReturnValue(unsubscribe);
let closeHandler: (() => void) | undefined;
const sseReq = mock<AuthenticatedRequest>({
user: { id: USER_ID },
headers: {},
once: vi.fn((event: string, handler: () => void) => {
if (event === 'close') closeHandler = handler;
}) as never,
});
const sseRes = mock<Response & { flush?: () => void }>({
setHeader: vi.fn(),
flushHeaders: vi.fn(),
write: vi.fn(),
end: vi.fn(),
flush: vi.fn(),
});
// The client disconnects while the persisted snapshot is being fetched.
memoryService.getLatestRunSnapshot.mockImplementation(async () => {
closeHandler!();
return undefined;
});
await controller.events(sseReq, sseRes, THREAD_ID, { lastEventId: undefined } as never);
expect(unsubscribe).toHaveBeenCalledTimes(1);
expect(sseRes.write).not.toHaveBeenCalled();
});
it('should close SSE stream when thread ownership changes after pre-creation subscribe', async () => {
// Simulate: thread does not exist at connect time
memoryService.checkThreadOwnership.mockResolvedValueOnce('not_found');
@@ -468,7 +571,7 @@ describe('InstanceAiController', () => {
const result = await controller.cancel(req, res, THREAD_ID);
expect(result).toEqual({ ok: true });
expect(instanceAiService.cancelRun).toHaveBeenCalledWith(THREAD_ID);
expect(instanceAiService.routeCancelRun).toHaveBeenCalledWith(THREAD_ID);
});
it('should throw ForbiddenError for other user thread', async () => {
@@ -698,7 +801,7 @@ describe('InstanceAiController', () => {
const result = await controller.cancelTask(req, res, THREAD_ID, 'task-1');
expect(result).toEqual({ ok: true });
expect(instanceAiService.cancelBackgroundTask).toHaveBeenCalledWith(THREAD_ID, 'task-1');
expect(instanceAiService.routeCancelBackgroundTask).toHaveBeenCalledWith(THREAD_ID, 'task-1');
});
});
@@ -714,7 +817,7 @@ describe('InstanceAiController', () => {
const result = await controller.correctTask(req, res, THREAD_ID, 'task-1', payload);
expect(result).toEqual({ ok: true });
expect(instanceAiService.sendCorrectionToTask).toHaveBeenCalledWith(
expect(instanceAiService.routeCorrectionToTask).toHaveBeenCalledWith(
THREAD_ID,
'task-1',
'fix this',
@@ -969,7 +1072,7 @@ describe('InstanceAiController', () => {
const result = await controller.deleteThread(req, res, THREAD_ID);
expect(result).toEqual({ ok: true });
expect(instanceAiService.clearThreadState).toHaveBeenCalledWith(THREAD_ID);
expect(instanceAiService.routeClearThreadState).toHaveBeenCalledWith(THREAD_ID);
expect(memoryService.deleteThread).toHaveBeenCalledWith(THREAD_ID);
});
@@ -3191,3 +3191,190 @@ describe('InstanceAiService — deterministic workflow setup follow-up', () => {
).toBeUndefined();
});
});
type TaskControlInternals = {
instanceSettings: { isMultiMain: boolean };
publisher: { publishCommand: Mock };
backgroundTasks: { getTaskSnapshots: Mock };
logger: { error: Mock };
sendCorrectionToTask: Mock;
cancelBackgroundTask: Mock;
cancelRun: Mock;
clearThreadState: Mock;
routeCorrectionToTask: InstanceAiService['routeCorrectionToTask'];
routeCancelBackgroundTask: InstanceAiService['routeCancelBackgroundTask'];
routeCancelRun: InstanceAiService['routeCancelRun'];
routeClearThreadState: InstanceAiService['routeClearThreadState'];
handleRelayTaskControl: InstanceAiService['handleRelayTaskControl'];
};
function buildTaskControlService(isMultiMain: boolean): TaskControlInternals {
const service = Object.create(InstanceAiService.prototype) as unknown as TaskControlInternals;
service.instanceSettings = { isMultiMain };
service.publisher = { publishCommand: vi.fn().mockResolvedValue(undefined) };
service.backgroundTasks = { getTaskSnapshots: vi.fn(() => []) };
service.logger = { error: vi.fn() };
service.sendCorrectionToTask = vi.fn(() => 'queued');
service.cancelBackgroundTask = vi.fn();
service.cancelRun = vi.fn();
service.clearThreadState = vi.fn(async () => {});
return service;
}
describe('InstanceAiService — cross-main task-control routing', () => {
describe('routeCorrectionToTask', () => {
it('broadcasts when the task is not local and multi-main', async () => {
const service = buildTaskControlService(true);
service.sendCorrectionToTask.mockReturnValue('task-not-found');
await service.routeCorrectionToTask('thread-a', 'task-1', 'try again');
expect(service.sendCorrectionToTask).toHaveBeenCalledWith('thread-a', 'task-1', 'try again');
expect(service.publisher.publishCommand).toHaveBeenCalledWith({
command: 'relay-instance-ai-task-control',
payload: {
threadId: 'thread-a',
taskId: 'task-1',
action: 'correct',
correction: 'try again',
},
});
});
it('does not broadcast when the correction was applied locally', async () => {
const service = buildTaskControlService(true);
service.sendCorrectionToTask.mockReturnValue('queued');
await service.routeCorrectionToTask('thread-a', 'task-1', 'try again');
expect(service.publisher.publishCommand).not.toHaveBeenCalled();
});
it('does not broadcast in single-main even on a local miss', async () => {
const service = buildTaskControlService(false);
service.sendCorrectionToTask.mockReturnValue('task-not-found');
await service.routeCorrectionToTask('thread-a', 'task-1', 'try again');
expect(service.publisher.publishCommand).not.toHaveBeenCalled();
});
});
describe('routeCancelBackgroundTask', () => {
it('broadcasts when the task is not local and multi-main', async () => {
const service = buildTaskControlService(true);
service.backgroundTasks.getTaskSnapshots.mockReturnValue([]);
await service.routeCancelBackgroundTask('thread-a', 'task-1');
expect(service.cancelBackgroundTask).toHaveBeenCalledWith('thread-a', 'task-1');
expect(service.publisher.publishCommand).toHaveBeenCalledWith({
command: 'relay-instance-ai-task-control',
payload: { threadId: 'thread-a', taskId: 'task-1', action: 'cancel-task' },
});
});
it('does not broadcast when the task is local', async () => {
const service = buildTaskControlService(true);
service.backgroundTasks.getTaskSnapshots.mockReturnValue([{ taskId: 'task-1' }]);
await service.routeCancelBackgroundTask('thread-a', 'task-1');
expect(service.cancelBackgroundTask).toHaveBeenCalledWith('thread-a', 'task-1');
expect(service.publisher.publishCommand).not.toHaveBeenCalled();
});
});
describe('routeCancelRun / routeClearThreadState', () => {
it('routeCancelRun cancels locally and always fans out in multi-main', async () => {
const service = buildTaskControlService(true);
await service.routeCancelRun('thread-a');
expect(service.cancelRun).toHaveBeenCalledWith('thread-a');
expect(service.publisher.publishCommand).toHaveBeenCalledWith({
command: 'relay-instance-ai-task-control',
payload: { threadId: 'thread-a', action: 'cancel-thread' },
});
});
it('routeCancelRun does not fan out in single-main', async () => {
const service = buildTaskControlService(false);
await service.routeCancelRun('thread-a');
expect(service.cancelRun).toHaveBeenCalledWith('thread-a');
expect(service.publisher.publishCommand).not.toHaveBeenCalled();
});
it('routeClearThreadState clears locally and fans out in multi-main', async () => {
const service = buildTaskControlService(true);
await service.routeClearThreadState('thread-a');
expect(service.clearThreadState).toHaveBeenCalledWith('thread-a');
expect(service.publisher.publishCommand).toHaveBeenCalledWith({
command: 'relay-instance-ai-task-control',
payload: { threadId: 'thread-a', action: 'clear-thread' },
});
});
});
describe('handleRelayTaskControl', () => {
it('applies a relayed correction via the local method and never re-broadcasts', async () => {
const service = buildTaskControlService(true);
await service.handleRelayTaskControl({
threadId: 'thread-a',
taskId: 'task-1',
action: 'correct',
correction: 'fix it',
});
expect(service.sendCorrectionToTask).toHaveBeenCalledWith('thread-a', 'task-1', 'fix it');
expect(service.publisher.publishCommand).not.toHaveBeenCalled();
});
it('ignores a correction relay missing its correction text', async () => {
const service = buildTaskControlService(true);
await service.handleRelayTaskControl({
threadId: 'thread-a',
taskId: 'task-1',
action: 'correct',
});
expect(service.sendCorrectionToTask).not.toHaveBeenCalled();
});
it('routes cancel-task / cancel-thread / clear-thread to the local methods', async () => {
const service = buildTaskControlService(true);
await service.handleRelayTaskControl({
threadId: 'thread-a',
taskId: 'task-1',
action: 'cancel-task',
});
await service.handleRelayTaskControl({ threadId: 'thread-a', action: 'cancel-thread' });
await service.handleRelayTaskControl({ threadId: 'thread-a', action: 'clear-thread' });
expect(service.cancelBackgroundTask).toHaveBeenCalledWith('thread-a', 'task-1');
expect(service.cancelRun).toHaveBeenCalledWith('thread-a');
expect(service.clearThreadState).toHaveBeenCalledWith('thread-a');
expect(service.publisher.publishCommand).not.toHaveBeenCalled();
});
it('swallows and logs errors from a local action (no unhandled rejection on the sibling main)', async () => {
const service = buildTaskControlService(true);
service.clearThreadState.mockRejectedValue(new Error('db exploded'));
await expect(
service.handleRelayTaskControl({ threadId: 'thread-a', action: 'clear-thread' }),
).resolves.toBeUndefined();
expect(service.logger.error).toHaveBeenCalledWith(
'Failed to apply relayed Instance AI task-control',
expect.objectContaining({ threadId: 'thread-a', action: 'clear-thread' }),
);
});
});
});
@@ -1,4 +1,9 @@
import type { Logger } from '@n8n/backend-common';
import type { InstanceAiEvent } from '@n8n/api-types';
import { mock } from 'vitest-mock-extended';
import type { InstanceSettings } from 'n8n-core';
import type { Publisher } from '@/scaling/pubsub/publisher.service';
import { InProcessEventBus } from '../in-process-event-bus';
@@ -13,9 +18,20 @@ function makeEvent(type: string, runId: string): InstanceAiEvent {
describe('InProcessEventBus', () => {
let bus: InProcessEventBus;
let publisher: ReturnType<typeof mock<Publisher>>;
let instanceSettings: { isMultiMain: boolean };
function buildBus() {
const logger = mock<Logger>();
logger.scoped.mockReturnValue(logger);
publisher = mock<Publisher>();
publisher.publishCommand.mockResolvedValue(undefined);
return new InProcessEventBus(logger, instanceSettings as InstanceSettings, publisher);
}
beforeEach(() => {
bus = new InProcessEventBus();
instanceSettings = { isMultiMain: false };
bus = buildBus();
});
afterEach(() => {
@@ -191,4 +207,92 @@ describe('InProcessEventBus', () => {
expect(received).toHaveLength(1);
});
});
describe('cross-main relay', () => {
it('does not relay when single-main', () => {
bus.publish('thread-1', makeEvent('a', 'run_1'));
expect(publisher.publishCommand).not.toHaveBeenCalled();
});
it('relays each event via pubsub when multi-main', () => {
instanceSettings.isMultiMain = true;
bus = buildBus();
const event = makeEvent('a', 'run_1');
bus.publish('thread-1', event);
expect(publisher.publishCommand).toHaveBeenCalledWith({
command: 'relay-instance-ai-event',
payload: { threadId: 'thread-1', event },
});
});
it('still delivers locally even when relaying', () => {
instanceSettings.isMultiMain = true;
bus = buildBus();
const received: number[] = [];
bus.subscribe('thread-1', (e) => received.push(e.id));
bus.publish('thread-1', makeEvent('a', 'run_1'));
expect(received).toEqual([1]);
expect(publisher.publishCommand).toHaveBeenCalledTimes(1);
});
it('skips relay for oversized events but still delivers locally', () => {
instanceSettings.isMultiMain = true;
bus = buildBus();
const received: number[] = [];
bus.subscribe('thread-1', (e) => received.push(e.id));
const huge = makeEvent('a', 'run_1');
(huge.payload as { text: string }).text = 'x'.repeat(6 * 1024 * 1024);
bus.publish('thread-1', huge);
// Relay skipped (would bloat pubsub), but the local SSE client still got it
// synchronously via the emit (even though the 2 MB store cap then evicts it).
expect(publisher.publishCommand).not.toHaveBeenCalled();
expect(received).toEqual([1]);
});
it('publishLocalOnly never relays', () => {
instanceSettings.isMultiMain = true;
bus = buildBus();
bus.publishLocalOnly('thread-1', makeEvent('a', 'run_1'));
expect(publisher.publishCommand).not.toHaveBeenCalled();
expect(bus.getEventsAfter('thread-1', 0)).toHaveLength(1);
});
});
describe('handleRelayInstanceAiEvent', () => {
it('re-emits a relayed event when this main holds an SSE subscriber', () => {
const received: number[] = [];
bus.subscribe('thread-1', (e) => received.push(e.id));
bus.handleRelayInstanceAiEvent({ threadId: 'thread-1', event: makeEvent('a', 'run_1') });
expect(received).toEqual([1]);
// Re-emit must not re-relay (loop guard): publishLocalOnly path.
expect(publisher.publishCommand).not.toHaveBeenCalled();
});
it('ignores a relayed event when this main has no subscriber for the thread', () => {
bus.handleRelayInstanceAiEvent({ threadId: 'thread-1', event: makeEvent('a', 'run_1') });
// Nothing stored, since the thread has no local consumer here.
expect(bus.getEventsAfter('thread-1', 0)).toHaveLength(0);
});
});
describe('hasSubscribers', () => {
it('reflects active subscriptions', () => {
expect(bus.hasSubscribers('thread-1')).toBe(false);
const unsubscribe = bus.subscribe('thread-1', () => {});
expect(bus.hasSubscribers('thread-1')).toBe(true);
unsubscribe();
expect(bus.hasSubscribers('thread-1')).toBe(false);
});
});
});
@@ -1,7 +1,13 @@
import { Logger } from '@n8n/backend-common';
import type { InstanceAiEvent } from '@n8n/api-types';
import { OnPubSubEvent } from '@n8n/decorators';
import { Service } from '@n8n/di';
import type { InstanceAiEventBus, StoredEvent } from '@n8n/instance-ai';
import { EventEmitter } from 'node:events';
import { InstanceSettings } from 'n8n-core';
import { MAX_PUBSUB_PAYLOAD_BYTES } from '@/scaling/constants';
import { Publisher } from '@/scaling/pubsub/publisher.service';
const MAX_EVENTS_PER_THREAD = 500;
const MAX_BYTES_PER_THREAD = 2 * 1024 * 1024; // 2 MB
@@ -18,21 +24,47 @@ export class InProcessEventBus implements InstanceAiEventBus {
/** Monotonic counter per thread — never resets even after eviction. */
private readonly nextId = new Map<string, number>();
constructor() {
constructor(
private readonly logger: Logger,
private readonly instanceSettings: InstanceSettings,
private readonly publisher: Publisher,
) {
this.logger = this.logger.scoped('instance-ai');
// Avoid warnings when many SSE clients connect (each adds a listener per thread)
this.emitter.setMaxListeners(0);
}
/**
* Publish an event for a thread: store it, deliver it to local SSE
* subscribers, and — in multi-main — relay it to sibling mains so the main
* holding the client's SSE connection (which may not be this one) delivers it.
*/
publish(threadId: string, event: InstanceAiEvent): void {
// Serialize once: reused for the store's size accounting and the relay guard.
const sizeBytes = Buffer.byteLength(JSON.stringify(event), 'utf8');
this.storeAndEmit(threadId, event, sizeBytes);
this.relayToSiblings(threadId, event, sizeBytes);
}
/**
* Store + deliver locally WITHOUT relaying. Used by the pubsub handler when a
* relayed event arrives from another main — re-relaying would loop. The local
* `nextId` stamps the SSE id, so this main is the id authority for the
* connection it serves.
*/
publishLocalOnly(threadId: string, event: InstanceAiEvent): void {
this.storeAndEmit(threadId, event, Buffer.byteLength(JSON.stringify(event), 'utf8'));
}
private storeAndEmit(threadId: string, event: InstanceAiEvent, eventSizeBytes: number): void {
const events = this.getOrCreateStore(threadId);
const id = (this.nextId.get(threadId) ?? 0) + 1;
this.nextId.set(threadId, id);
const stored: StoredEvent = { id, event };
const eventSize = JSON.stringify(event).length;
events.push(stored);
this.sizeBytes.set(threadId, (this.sizeBytes.get(threadId) ?? 0) + eventSize);
this.sizeBytes.set(threadId, (this.sizeBytes.get(threadId) ?? 0) + eventSizeBytes);
// Evict oldest events if count or size exceeds caps
this.evictIfNeeded(threadId, events);
@@ -40,11 +72,49 @@ export class InProcessEventBus implements InstanceAiEventBus {
this.emitter.emit(threadId, stored);
}
private relayToSiblings(threadId: string, event: InstanceAiEvent, sizeBytes: number): void {
if (!this.instanceSettings.isMultiMain) return;
if (sizeBytes > MAX_PUBSUB_PAYLOAD_BYTES) {
this.logger.warn(
`Skipping cross-main relay of "${event.type}" event (${sizeBytes} bytes exceeds ${MAX_PUBSUB_PAYLOAD_BYTES})`,
{ threadId, runId: event.runId },
);
return;
}
void this.publisher
.publishCommand({ command: 'relay-instance-ai-event', payload: { threadId, event } })
.catch((error: unknown) =>
this.logger.error('Failed to relay Instance AI event to sibling mains', {
threadId,
error,
}),
);
}
/** A relayed event from another main: re-emit to this main's SSE clients only
* if it actually holds a subscription for the thread (avoids every main
* buffering every thread). */
@OnPubSubEvent('relay-instance-ai-event', { instanceType: 'main' })
handleRelayInstanceAiEvent({
threadId,
event,
}: { threadId: string; event: InstanceAiEvent }): void {
if (!this.hasSubscribers(threadId)) return;
this.publishLocalOnly(threadId, event);
}
subscribe(threadId: string, handler: (storedEvent: StoredEvent) => void): () => void {
this.emitter.on(threadId, handler);
return () => this.emitter.off(threadId, handler);
}
/** Whether this main currently holds an SSE subscription for the thread. */
hasSubscribers(threadId: string): boolean {
return this.emitter.listenerCount(threadId) > 0;
}
getEventsAfter(threadId: string, afterId: number): StoredEvent[] {
const events = this.store.get(threadId);
if (!events) return [];
@@ -90,7 +160,7 @@ export class InProcessEventBus implements InstanceAiEventBus {
while (events.length > MAX_EVENTS_PER_THREAD || totalSize > MAX_BYTES_PER_THREAD) {
const evicted = events.shift();
if (!evicted) break;
totalSize -= JSON.stringify(evicted.event).length;
totalSize -= Buffer.byteLength(JSON.stringify(evicted.event), 'utf8');
}
this.sizeBytes.set(threadId, Math.max(0, totalSize));
@@ -34,7 +34,7 @@ import {
Body,
Query,
} from '@n8n/decorators';
import type { StoredEvent } from '@n8n/instance-ai';
import type { AgentTreeSnapshot, StoredEvent } from '@n8n/instance-ai';
import { buildAgentTreeFromEvents } from '@n8n/instance-ai';
import { UnsupportedAttachmentError, validateAttachmentMimeTypes } from '@n8n/instance-ai/parsers';
import type { NextFunction, Request, Response } from 'express';
@@ -213,12 +213,6 @@ export class InstanceAiController {
if (ownership === 'other_user') {
throw new ForbiddenError('Not authorized for this thread');
}
if (ownership === 'owned') {
await this.instanceAiService.replayUndeliveredTerminalOutcomes(threadId, {
delivery: 'event',
});
}
// When the thread didn't exist at connect time, another user could create
// and own it before events start flowing. We re-check once on the first
// event and close the stream if ownership changed. Events are buffered
@@ -228,7 +222,74 @@ export class InstanceAiController {
const pendingEvents: StoredEvent[] = [];
const userId = req.user.id;
// 1. Set SSE headers.
// 1. Subscribe to live events before the async bootstrap below.
// hasSubscribers() must be true across the awaits that follow: in
// multi-main, sibling mains drop relayed events for threads without a
// local subscriber, so a relayed event arriving during an await would
// otherwise be lost for good. Events emitted while bootstrapping are
// NOT delivered here — they land in the event store and the replay in
// step 6 picks them up, avoiding duplicates.
let bootstrapping = true;
const deliver = (stored: StoredEvent) => {
if (ownershipVerified) {
this.writeSseEvent(res, stored);
return;
}
// When the thread was not_found at connect time, re-validate ownership
// on the first event. Buffer all events until the check resolves to
// avoid leaking data during the async gap.
pendingEvents.push(stored);
if (ownershipCheckInFlight) return;
ownershipCheckInFlight = true;
void this.memoryService
.checkThreadOwnership(userId, threadId)
.then((currentOwnership) => {
if (currentOwnership === 'other_user') {
res.end();
return;
}
ownershipVerified = true;
for (const buffered of pendingEvents) {
this.writeSseEvent(res, buffered);
}
pendingEvents.length = 0;
})
.catch(() => {
pendingEvents.length = 0;
res.end();
});
};
const unsubscribe = this.eventBus.subscribe(threadId, (stored) => {
if (bootstrapping) return;
deliver(stored);
});
// Cleanup is registered before the async bootstrap so a client disconnect
// (or an error response) during the awaits below doesn't leak the
// subscription.
let closed = false;
let keepAlive: NodeJS.Timeout | undefined = undefined;
const cleanup = () => {
closed = true;
unsubscribe();
if (keepAlive !== undefined) clearInterval(keepAlive);
};
req.once('close', cleanup);
res.once('finish', cleanup);
// 2. Re-publish any terminal outcomes that never reached the client.
if (ownership === 'owned') {
await this.instanceAiService.replayUndeliveredTerminalOutcomes(threadId, {
delivery: 'event',
});
}
// 3. Set SSE headers.
// Disable response compression — SSE streams small chunks where compression
// overhead exceeds the benefit, and each Brotli compressor retains ~8.6 MB
// of native memory for the lifetime of the connection.
@@ -239,7 +300,7 @@ export class InstanceAiController {
res.setHeader('X-Accel-Buffering', 'no');
res.flushHeaders();
// 2. Determine replay cursor
// 4. Determine replay cursor
// Last-Event-ID header (browser auto-reconnect) takes precedence over query param.
// Both are validated as non-negative integers; invalid values fall back to 0.
const headerValue = req.headers['last-event-id'];
@@ -247,20 +308,9 @@ export class InstanceAiController {
const cursor =
Number.isFinite(parsedHeader) && parsedHeader >= 0 ? parsedHeader : (query.lastEventId ?? 0);
// 3. Replay missed events then subscribe in the same tick.
// Since InProcessEventBus is synchronous and single-threaded (Node.js
// event loop), there is no window for missed events between replay and
// subscribe when done in the same synchronous block.
const missed = this.eventBus.getEventsAfter(threadId, cursor);
for (const stored of missed) {
this.writeSseEvent(res, stored);
}
// 3b. Bootstrap sync: emit one run-sync control frame per live message group.
// Multiple groups can be active simultaneously when a background task
// from an older turn outlives its original turn. Each frame uses named
// SSE event type (event: run-sync) with NO id: field so the browser's
// lastEventId is unaffected and replay cursor stays consistent.
// 5. Collect live message groups and fetch their persisted snapshots.
// Multiple groups can be active simultaneously when a background task
// from an older turn outlives its original turn.
const threadStatus = this.instanceAiService.getThreadStatus(threadId);
// Collect all distinct message groups that have live activity.
@@ -291,16 +341,41 @@ export class InstanceAiController {
}
}
const persistedSnapshots = new Map<string, AgentTreeSnapshot | undefined>();
for (const [groupId, group] of liveGroups) {
persistedSnapshots.set(
groupId,
await this.memoryService.getLatestRunSnapshot(threadId, {
messageGroupId: groupId,
// Use the group's own latest runId — NOT the thread-global
// activeRunId, which belongs to the current orchestrator turn and
// would be wrong for background groups from older turns.
runId: group.runIds.at(-1),
}),
);
}
// The client may have disconnected during the awaits above.
if (closed) return;
// 6. Replay missed events, emit run-sync frames, and flip to live delivery
// in one synchronous block. The event bus store and emitter are
// synchronous, so no event can slip between the replay and the live
// handler taking over. Events that arrived during the awaits above are
// already in the store (the early subscription in step 1 keeps relayed
// events flowing in multi-main) and are included in the replay here.
const missed = this.eventBus.getEventsAfter(threadId, cursor);
for (const stored of missed) {
deliver(stored);
}
// 6b. Bootstrap sync: emit one run-sync control frame per live message
// group. Each frame uses a named SSE event type (event: run-sync) with
// NO id: field so the browser's lastEventId is unaffected and the
// replay cursor stays consistent.
for (const [groupId, group] of liveGroups) {
const runEvents = this.eventBus.getEventsForRuns(threadId, group.runIds);
// Use the group's own latest runId — NOT the thread-global activeRunId,
// which belongs to the current orchestrator turn and would be wrong for
// background groups from older turns.
const groupRunId = group.runIds.at(-1);
const persistedSnapshot = await this.memoryService.getLatestRunSnapshot(threadId, {
messageGroupId: groupId,
runId: groupRunId,
});
const persistedSnapshot = persistedSnapshots.get(groupId);
if (runEvents.length === 0 && !persistedSnapshot) continue;
const eventTree = buildAgentTreeFromEvents(runEvents);
@@ -310,7 +385,7 @@ export class InstanceAiController {
);
res.write(
`event: run-sync\ndata: ${JSON.stringify({
runId: groupRunId,
runId: group.runIds.at(-1),
messageGroupId: groupId,
runIds: group.runIds,
agentTree,
@@ -321,53 +396,13 @@ export class InstanceAiController {
}
if (liveGroups.size > 0) res.flush?.();
// 4. Subscribe to live events
// When the thread was not_found at connect time, re-validate ownership on
// the first event. Buffer all events until the check resolves to avoid
// leaking data during the async gap.
const unsubscribe = this.eventBus.subscribe(threadId, (stored) => {
if (ownershipVerified) {
this.writeSseEvent(res, stored);
return;
}
bootstrapping = false;
pendingEvents.push(stored);
if (ownershipCheckInFlight) return;
ownershipCheckInFlight = true;
void this.memoryService
.checkThreadOwnership(userId, threadId)
.then((currentOwnership) => {
if (currentOwnership === 'other_user') {
res.end();
return;
}
ownershipVerified = true;
for (const buffered of pendingEvents) {
this.writeSseEvent(res, buffered);
}
pendingEvents.length = 0;
})
.catch(() => {
pendingEvents.length = 0;
res.end();
});
});
// 5. Keep-alive
const keepAlive = setInterval(() => {
// 7. Keep-alive
keepAlive = setInterval(() => {
res.write(': ping\n\n');
res.flush?.();
}, KEEP_ALIVE_INTERVAL_MS);
// 6. Cleanup on disconnect
const cleanup = () => {
unsubscribe();
clearInterval(keepAlive);
};
req.once('close', cleanup);
res.once('finish', cleanup);
}
@Post('/confirm/:requestId')
@@ -398,7 +433,7 @@ export class InstanceAiController {
async cancel(req: AuthenticatedRequest, _res: Response, @Param('threadId') threadId: string) {
this.requireInstanceAiEnabled();
await this.assertThreadAccess(req.user.id, threadId);
this.instanceAiService.cancelRun(threadId);
await this.instanceAiService.routeCancelRun(threadId);
return { ok: true };
}
@@ -432,7 +467,7 @@ export class InstanceAiController {
) {
this.requireInstanceAiEnabled();
await this.assertThreadAccess(req.user.id, threadId);
this.instanceAiService.cancelBackgroundTask(threadId, taskId);
await this.instanceAiService.routeCancelBackgroundTask(threadId, taskId);
return { ok: true };
}
@@ -447,7 +482,7 @@ export class InstanceAiController {
) {
this.requireInstanceAiEnabled();
await this.assertThreadAccess(req.user.id, threadId);
this.instanceAiService.sendCorrectionToTask(threadId, taskId, payload.message);
await this.instanceAiService.routeCorrectionToTask(threadId, taskId, payload.message);
return { ok: true };
}
@@ -586,7 +621,7 @@ export class InstanceAiController {
) {
this.requireInstanceAiEnabled();
await this.assertThreadAccess(req.user.id, threadId);
await this.instanceAiService.clearThreadState(threadId);
await this.instanceAiService.routeClearThreadState(threadId);
await this.memoryService.deleteThread(threadId);
return { ok: true };
}
@@ -16,7 +16,7 @@ import { Logger } from '@n8n/backend-common';
import { SsrfProtectionService } from '@n8n/backend-network';
import { GlobalConfig, SsrfProtectionConfig, type InstanceAiConfig } from '@n8n/config';
import { UserRepository, type User } from '@n8n/db';
import { OnLeaderStepdown, OnLeaderTakeover } from '@n8n/decorators';
import { OnLeaderStepdown, OnLeaderTakeover, OnPubSubEvent } from '@n8n/decorators';
import { Service } from '@n8n/di';
import {
MAX_STEPS,
@@ -100,6 +100,8 @@ import { nanoid } from 'nanoid';
import { N8N_VERSION, WORKFLOW_SDK_VERSION } from '@/constants';
import { EventService } from '@/events/event.service';
import { SourceControlPreferencesService } from '@/modules/source-control.ee/source-control-preferences.service.ee';
import type { PubSubCommandMap } from '@/scaling/pubsub/pubsub.event-map';
import { Publisher } from '@/scaling/pubsub/publisher.service';
import { AiService } from '@/services/ai.service';
import { ProxyTokenManager } from '@/services/proxy-token-manager';
import { UrlService } from '@/services/url.service';
@@ -534,6 +536,7 @@ export class InstanceAiService {
runProbe: InstanceAiRunProbe,
private readonly modelService: InstanceAiModelService,
private readonly creditService: InstanceAiCreditService,
private readonly publisher: Publisher,
private readonly instanceAiErrorReporter: InstanceAiErrorReporterService,
) {
this.logger = logger.scoped('instance-ai');
@@ -1054,6 +1057,108 @@ export class InstanceAiService {
}
}
// ── Cross-main task-control routing ───────────────────────────────────────
// User actions (correct/cancel/clear) can land on a different main than the
// one running the task/run. Each `route*` method applies the action locally
// and, when the target isn't local (or is thread-wide), broadcasts so the
// owning main applies it. Broadcast + local-gate — no shared ownership store.
// `applyTaskControlLocally` is the single action → local-method mapping,
// shared by the route entry points and the `@OnPubSubEvent` relay handler
// (which never re-broadcasts, so there's no loop). These wrappers are the
// controller entry points; internal callers keep using the local methods
// directly so they don't trigger cross-main broadcasts.
private broadcastTaskControl(payload: PubSubCommandMap['relay-instance-ai-task-control']): void {
if (!this.instanceSettings.isMultiMain) return;
void this.publisher
.publishCommand({ command: 'relay-instance-ai-task-control', payload })
.catch((error: unknown) =>
this.logger.error('Failed to relay Instance AI task-control to sibling mains', {
threadId: payload.threadId,
action: payload.action,
error,
}),
);
}
/** Apply a task-control action to this main's local slice of the thread.
* Returns whether the action's target was found locally: task-scoped
* actions report a local hit to gate re-broadcast, thread-wide actions
* always report a miss so they fan out to every main. */
private async applyTaskControlLocally({
threadId,
taskId,
action,
correction,
}: PubSubCommandMap['relay-instance-ai-task-control']): Promise<boolean> {
switch (action) {
case 'correct':
// A relay without its correction text is malformed: nothing to apply.
if (!taskId || correction === undefined) return true;
return this.sendCorrectionToTask(threadId, taskId, correction) !== 'task-not-found';
case 'cancel-task': {
if (!taskId) return true;
const isLocal = this.backgroundTasks
.getTaskSnapshots(threadId)
.some((task) => task.taskId === taskId);
this.cancelBackgroundTask(threadId, taskId);
return isLocal;
}
case 'cancel-thread':
// A thread's run + tasks can be spread across mains, so always fan out.
this.cancelRun(threadId);
return false;
case 'clear-thread':
await this.clearThreadState(threadId);
return false;
}
}
private async routeTaskControl(
payload: PubSubCommandMap['relay-instance-ai-task-control'],
): Promise<void> {
const foundLocally = await this.applyTaskControlLocally(payload);
if (!foundLocally) this.broadcastTaskControl(payload);
}
async routeCorrectionToTask(threadId: string, taskId: string, correction: string): Promise<void> {
await this.routeTaskControl({ threadId, taskId, action: 'correct', correction });
}
async routeCancelBackgroundTask(threadId: string, taskId: string): Promise<void> {
await this.routeTaskControl({ threadId, taskId, action: 'cancel-task' });
}
async routeCancelRun(threadId: string): Promise<void> {
await this.routeTaskControl({ threadId, action: 'cancel-thread' });
}
async routeClearThreadState(threadId: string): Promise<void> {
await this.routeTaskControl({ threadId, action: 'clear-thread' });
}
/** Apply a task-control action relayed from another main to this main's local
* slice of the thread. Never re-broadcasts. Not self-sent, so this never
* fires on the originating main. */
@OnPubSubEvent('relay-instance-ai-task-control', { instanceType: 'main' })
async handleRelayTaskControl(
payload: PubSubCommandMap['relay-instance-ai-task-control'],
): Promise<void> {
// Guard the whole handler: it runs as a fire-and-forget pubsub listener, so
// a throw (e.g. from the async clearThreadState) would surface as an
// unhandled rejection on this sibling main instead of being contained.
try {
await this.applyTaskControlLocally(payload);
} catch (error) {
this.logger.error('Failed to apply relayed Instance AI task-control', {
threadId: payload.threadId,
taskId: payload.taskId,
action: payload.action,
error,
});
}
}
/** Cancel all background tasks across all threads. Test-only. */
cancelAllBackgroundTasks(): number {
const cancelled = this.backgroundTasks.cancelAll();
+3 -8
View File
@@ -14,6 +14,7 @@ import { Server as WSServer } from 'ws';
import { AuthService } from '@/auth/auth.service';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { MAX_PUBSUB_PAYLOAD_BYTES } from '@/scaling/constants';
import { Publisher } from '@/scaling/pubsub/publisher.service';
import { validateOriginHeaders } from './origin-validator';
@@ -33,12 +34,6 @@ type PushEvents = {
message: OnPushMessage;
};
/**
* Max allowed size of a push message in bytes. Events going through the pubsub
* channel are trimmed if exceeding this size.
*/
const MAX_PAYLOAD_SIZE_BYTES = 5 * 1024 * 1024; // 5 MiB
/**
* Push service for uni- or bi-directional communication with frontend clients.
* Uses either server-sent events (SSE, unidirectional from backend --> frontend)
@@ -245,10 +240,10 @@ export class Push extends TypedEmitter<PushEvents> {
if (type === 'nodeExecuteAfterData') {
const eventSizeBytes = new TextEncoder().encode(JSON.stringify(pushMsg.data)).length;
if (eventSizeBytes > MAX_PAYLOAD_SIZE_BYTES) {
if (eventSizeBytes > MAX_PUBSUB_PAYLOAD_BYTES) {
const toMb = (bytes: number) => (bytes / (1024 * 1024)).toFixed(0);
const eventMb = toMb(eventSizeBytes);
const maxMb = toMb(MAX_PAYLOAD_SIZE_BYTES);
const maxMb = toMb(MAX_PUBSUB_PAYLOAD_BYTES);
this.logger.warn(
`Size of "${type}" (${eventMb} MB) exceeds max size ${maxMb} MB. Skipping...`,
+8
View File
@@ -13,6 +13,12 @@ export const WORKER_RESPONSE_PUBSUB_CHANNEL = 'n8n.worker-response';
/** Pubsub channel for MCP relay messages between main instances in multi-main queue mode. */
export const MCP_RELAY_PUBSUB_CHANNEL = 'n8n.mcp-relay';
/**
* Max allowed size in bytes of a message relayed over the pubsub channel. Events
* exceeding this are skipped (or trimmed) rather than bloating the channel.
*/
export const MAX_PUBSUB_PAYLOAD_BYTES = 5 * 1024 * 1024; // 5 MiB
/**
* Commands that should be sent to the sender as well, e.g. during workflow activation and
* deactivation in multi-main setup. */
@@ -32,6 +38,8 @@ export const IMMEDIATE_COMMANDS = new Set<PubSub.Command['command']>([
'remove-triggers-and-pollers',
'relay-execution-lifecycle-event',
'relay-chat-stream-event',
'relay-instance-ai-event',
'relay-instance-ai-task-control',
'agent-chat-subscription-changed',
'cancel-test-run',
'stop-execution',
@@ -1,6 +1,7 @@
import type {
AgentIntegrationConfig,
ChatHubMessageStatus,
InstanceAiEvent,
PushMessage,
WorkerStatus,
} from '@n8n/api-types';
@@ -150,6 +151,32 @@ export type PubSubCommandMap = {
};
};
/**
* Relay an Instance AI stream event to sibling mains.
*
* The agent runs on whichever main received `POST /chat/:threadId` and emits
* events into that main's in-process bus, but the client's `GET /events/:threadId`
* SSE connection may be held by a different main. The producing main relays each
* event; the main holding the SSE subscription re-emits it locally to its client.
*/
'relay-instance-ai-event': {
threadId: string;
event: InstanceAiEvent;
};
/**
* Relay an Instance AI task-control action (correction / cancel / clear) to
* sibling mains. The action may target a background task or run held
* in another main's in-memory state. Broadcast + local-gate: every main applies
* it only to its own local slice of the thread.
*/
'relay-instance-ai-task-control': {
threadId: string;
taskId?: string;
action: 'correct' | 'cancel-task' | 'cancel-thread' | 'clear-thread';
correction?: string;
};
/**
* Relay human message events between main instances.
* Used for cross-client synchronization when a user sends a message
@@ -59,6 +59,8 @@ export namespace PubSub {
export type WorkflowPublishWakeUp = ToCommand<'workflow-publish-wake-up'>;
export type RelayExecutionLifecycleEvent = ToCommand<'relay-execution-lifecycle-event'>;
export type RelayChatStreamEvent = ToCommand<'relay-chat-stream-event'>;
export type RelayInstanceAiEvent = ToCommand<'relay-instance-ai-event'>;
export type RelayInstanceAiTaskControl = ToCommand<'relay-instance-ai-task-control'>;
export type RelayChatHumanMessage = ToCommand<'relay-chat-human-message'>;
export type RelayChatMessageEdit = ToCommand<'relay-chat-message-edit'>;
export type ClearTestWebhooks = ToCommand<'clear-test-webhooks'>;
@@ -95,6 +97,8 @@ export namespace PubSub {
| Commands.WorkflowPublishWakeUp
| Commands.RelayExecutionLifecycleEvent
| Commands.RelayChatStreamEvent
| Commands.RelayInstanceAiEvent
| Commands.RelayInstanceAiTaskControl
| Commands.RelayChatHumanMessage
| Commands.RelayChatMessageEdit
| Commands.ClearTestWebhooks
@@ -0,0 +1,108 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*Reply with exactly the single word: pong\\. Do not use any tools\\.\\\\n\\\\n<current-date-time>\\\\n## Current Date and Time\\\\n\\\\nThe user'[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-257333ecdc34ed04df1bec188d0604b9-0ad4bf2621dc6d14-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"set-cookie": [
"_cfuvid=SFDWYwrKj7nS9SomTXtVjfUIBmcxsbqDuIO8P1pANtY-1782824956.0979054-1.0.1.1-nMZLiQRgm5ffii4i6ydwZPBEqcFn199Kx54m109XSeI; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com"
],
"request-id": [
"req_011CcZVqoMyTi1CJuAYzdLTp"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-06-30T13:09:16Z"
],
"anthropic-ratelimit-tokens-remaining": [
"17989000"
],
"anthropic-ratelimit-tokens-limit": [
"18000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-06-30T13:09:16Z"
],
"anthropic-ratelimit-requests-remaining": [
"19999"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-06-30T13:09:16Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"3000000"
],
"anthropic-ratelimit-output-tokens-limit": [
"3000000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-06-30T13:09:16Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"14989000"
],
"anthropic-ratelimit-input-tokens-limit": [
"15000000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Tue, 30 Jun 2026 13:09:21 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a13d67879a1132d9-HEL"
]
},
"cookies": {
"_cfuvid": "SFDWYwrKj7nS9SomTXtVjfUIBmcxsbqDuIO8P1pANtY-1782824956.0979054-1.0.1.1-nMZLiQRgm5ffii4i6ydwZPBEqcFn199Kx54m109XSeI"
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01BV1ez6mLA33xhnyRdY24zJ\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":3,\"cache_creation_input_tokens\":17993,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":17993,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"p\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ong\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":3,\"cache_creation_input_tokens\":17993,\"cache_read_input_tokens\":0,\"output_tokens\":5,\"output_tokens_details\":{\"thinking_tokens\":0}} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxQlYxZXo2bUxBMzN4aG55UmRZMjR6SiIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjMsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6MTc5OTMsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjowLCJjYWNoZV9jcmVhdGlvbiI6eyJlcGhlbWVyYWxfNW1faW5wdXRfdG9rZW5zIjoxNzk5MywiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjEsInNlcnZpY2VfdGllciI6InN0YW5kYXJkIiwiaW5mZXJlbmNlX2dlbyI6Imdsb2JhbCJ9fSAgICAgICAgICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfc3RhcnQKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdGFydCIsImluZGV4IjowLCJjb250ZW50X2Jsb2NrIjp7InR5cGUiOiJ0ZXh0IiwidGV4dCI6IiJ9ICAgIH0KCmV2ZW50OiBwaW5nCmRhdGE6IHsidHlwZSI6ICJwaW5nIn0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MCwiZGVsdGEiOnsidHlwZSI6InRleHRfZGVsdGEiLCJ0ZXh0IjoicCJ9ICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJ0ZXh0X2RlbHRhIiwidGV4dCI6Im9uZyJ9IH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0b3AKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdG9wIiwiaW5kZXgiOjAgICAgIH0KCmV2ZW50OiBtZXNzYWdlX2RlbHRhCmRhdGE6IHsidHlwZSI6Im1lc3NhZ2VfZGVsdGEiLCJkZWx0YSI6eyJzdG9wX3JlYXNvbiI6ImVuZF90dXJuIiwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsfSwidXNhZ2UiOnsiaW5wdXRfdG9rZW5zIjozLCJjYWNoZV9jcmVhdGlvbl9pbnB1dF90b2tlbnMiOjE3OTkzLCJjYWNoZV9yZWFkX2lucHV0X3Rva2VucyI6MCwib3V0cHV0X3Rva2VucyI6NSwib3V0cHV0X3Rva2Vuc19kZXRhaWxzIjp7InRoaW5raW5nX3Rva2VucyI6MH19ICAgICAgICB9CgpldmVudDogbWVzc2FnZV9zdG9wCmRhdGE6IHsidHlwZSI6Im1lc3NhZ2Vfc3RvcCIgICAgIH0KCg==",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0000-1782824962620-unknown-host-POST-_v1_messages-dcc60945.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -1,108 +0,0 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*Build a workflow named \\\\\"INS-164 mocked credential guard\\\\\" with a Manual Trigger connected to a Slack node that posts a me[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-096c2c1d2adf050db9c94a20a4f5ea37-cd3d7191e72d2884-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"set-cookie": [
"_cfuvid=VB_1tZGEQc003LPoTH4I4a9av.pb.j0Dm_K3jCF8_6Q-1781088435.499604-1.0.1.1-EBbUWJcv6pIpx9.oZyT9lc1PWEokxYgebRIERf2LBCg; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com"
],
"request-id": [
"req_011CbuSqPo8u678GaYz8npbW"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-06-10T10:47:15Z"
],
"anthropic-ratelimit-tokens-remaining": [
"26973000"
],
"anthropic-ratelimit-tokens-limit": [
"27000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-06-10T10:47:15Z"
],
"anthropic-ratelimit-requests-remaining": [
"19998"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-06-10T10:47:15Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"4500000"
],
"anthropic-ratelimit-output-tokens-limit": [
"4500000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-06-10T10:47:15Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"22473000"
],
"anthropic-ratelimit-input-tokens-limit": [
"22500000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Wed, 10 Jun 2026 10:47:16 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a097cc01dec8b236-HEL"
]
},
"cookies": {
"_cfuvid": "VB_1tZGEQc003LPoTH4I4a9av.pb.j0Dm_K3jCF8_6Q-1781088435.499604-1.0.1.1-EBbUWJcv6pIpx9.oZyT9lc1PWEokxYgebRIERf2LBCg"
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01QDLxqEM9e6QAx8A76gahtp\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":3,\"cache_creation_input_tokens\":15336,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":15336,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Loading\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" the workflow-builder skill before building.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01H8kceqRSsGHmLWsgBvuG4k\",\"name\":\"load_skill\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"name\\\": \\\"workflow-builder\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":3,\"cache_creation_input_tokens\":15336,\"cache_read_input_tokens\":0,\"output_tokens\":65} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxUURMeHFFTTllNlFBeDhBNzZnYWh0cCIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjMsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6MTUzMzYsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjowLCJjYWNoZV9jcmVhdGlvbiI6eyJlcGhlbWVyYWxfNW1faW5wdXRfdG9rZW5zIjoxNTMzNiwiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjEsInNlcnZpY2VfdGllciI6InN0YW5kYXJkIiwiaW5mZXJlbmNlX2dlbyI6Imdsb2JhbCJ9fX0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0YXJ0CmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RhcnQiLCJpbmRleCI6MCwiY29udGVudF9ibG9jayI6eyJ0eXBlIjoidGV4dCIsInRleHQiOiIifSAgICAgICAgICAgfQoKZXZlbnQ6IHBpbmcKZGF0YTogeyJ0eXBlIjogInBpbmcifQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoidGV4dF9kZWx0YSIsInRleHQiOiJMb2FkaW5nIn0gfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoidGV4dF9kZWx0YSIsInRleHQiOiIgdGhlIHdvcmtmbG93LWJ1aWxkZXIgc2tpbGwgYmVmb3JlIGJ1aWxkaW5nLiJ9ICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19zdG9wCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RvcCIsImluZGV4IjowICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0YXJ0CmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RhcnQiLCJpbmRleCI6MSwiY29udGVudF9ibG9jayI6eyJ0eXBlIjoidG9vbF91c2UiLCJpZCI6InRvb2x1XzAxSDhrY2VxUlNzR0htTFdzZ0J2dUc0ayIsIm5hbWUiOiJsb2FkX3NraWxsIiwiaW5wdXQiOnt9LCJjYWxsZXIiOnsidHlwZSI6ImRpcmVjdCJ9fSAgICAgICAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MSwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiIifSB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjEsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoie1wibmFtZVwiOiBcIndvcmtmbG93LWJ1aWxkZXIifSAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MSwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJcIn0ifSAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0b3AKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdG9wIiwiaW5kZXgiOjEgICAgICAgICAgfQoKZXZlbnQ6IG1lc3NhZ2VfZGVsdGEKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9kZWx0YSIsImRlbHRhIjp7InN0b3BfcmVhc29uIjoidG9vbF91c2UiLCJzdG9wX3NlcXVlbmNlIjpudWxsLCJzdG9wX2RldGFpbHMiOm51bGx9LCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjMsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6MTUzMzYsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjowLCJvdXRwdXRfdG9rZW5zIjo2NX0gICAgICAgICAgfQoKZXZlbnQ6IG1lc3NhZ2Vfc3RvcApkYXRhOiB7InR5cGUiOiJtZXNzYWdlX3N0b3AiICAgICAgICAgICAgICB9Cgo=",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0000-1781088457406-unknown-host-POST-_v1_messages-a942b0c9.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -0,0 +1,102 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*Build a workflow named \\\\\"INS-164 mocked credential guard\\\\\" with a Manual Trigger connected to a Slack node that posts a me[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-dd9f24ce6d3493e1a8ad23d7d8701464-e43ece6a6cbee859-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"request-id": [
"req_011CcfQCZed4mQmgbcXtDFr8"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-07-03T15:58:51Z"
],
"anthropic-ratelimit-tokens-remaining": [
"17989000"
],
"anthropic-ratelimit-tokens-limit": [
"18000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-07-03T15:58:51Z"
],
"anthropic-ratelimit-requests-remaining": [
"19999"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-07-03T15:58:51Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"3000000"
],
"anthropic-ratelimit-output-tokens-limit": [
"3000000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-07-03T15:58:51Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"14989000"
],
"anthropic-ratelimit-input-tokens-limit": [
"15000000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Fri, 03 Jul 2026 15:58:52 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a157180f6b9bf468-HEL"
]
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01U73eqSHxFBEZ8DRTkwRq3M\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":3,\"cache_creation_input_tokens\":18174,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":18174,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":7,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"thinking\",\"thinking\":\"\",\"signature\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"The user wants me to build a workflow named \\\"INS-164 mocked credential guard\\\" with a Manual Trigger connected to a Slack node that posts a message using a mocked slackApi credential placeholder. Let me load\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" the workflow-builder skill first.\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"signature_delta\",\"signature\":\"ErkDCmUIDxgCKkCAbaDQERxAdJR7Mv8Zk0L2gBIMqyDk5Xutac9+WomEVOGq3XucU7aBmBLwe2rk7QHGPmry9uJ+HO188mnmbbX+MhFjbGF1ZGUtc29ubmV0LTQtNjgAQgh0aGlua2luZxIMPcsHSWii08djOnBvGgwgJY3kUjOepZFnqrciMG+FK6UpWSyekQmIRG9g7cyr7Ir9pnQyz7uxQMsd5KTTwbPLlVgAWfkgzCwCV6cV1SqBAlBzDkpsQnWzj+S61GLyN7UEqoas6HJwnVydwa65o4pKkkxCXU8XQXDfK6/5LfZ5rjl4DZdacS8CfMhvotCx2inyanHHk/pzKR8yFuTKKjH5LZQ0faK4959/u7tG3yyEvd4kjayOXMMbePlE5OR1wUrze5iyJ8vnRoy5Of7rNdWRcPROzOsyetdW1cSHquSCQg8ZE7hVjuFNnW6tnJTJaJKDtpnIh0WZLYw7OhxnqCFx1XN4SlHTTImi3iiU9x8QBGL5xAvfsbAbJ0jADJbTGVYr/VxSDdUzpb2ezfhvZL3AhLnaLA8BO7PhgF4a4e/EortEVPZ5KwrtPyfwQUkWaLPOGAE=\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"Loading the workflow-builder skill before writing any code.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":2,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_015MSAudnAxno2DVs6BjXKp3\",\"name\":\"load_skill\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"name\\\": \\\"workflow-builder\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":2}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":3,\"cache_creation_input_tokens\":18174,\"cache_read_input_tokens\":0,\"output_tokens\":137,\"output_tokens_details\":{\"thinking_tokens\":67}} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxVTczZXFTSHhGQkVaOERSVGt3UnEzTSIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjMsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6MTgxNzQsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjowLCJjYWNoZV9jcmVhdGlvbiI6eyJlcGhlbWVyYWxfNW1faW5wdXRfdG9rZW5zIjoxODE3NCwiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjcsInNlcnZpY2VfdGllciI6InN0YW5kYXJkIiwiaW5mZXJlbmNlX2dlbyI6Imdsb2JhbCJ9fSAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19zdGFydApkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX3N0YXJ0IiwiaW5kZXgiOjAsImNvbnRlbnRfYmxvY2siOnsidHlwZSI6InRoaW5raW5nIiwidGhpbmtpbmciOiIiLCJzaWduYXR1cmUiOiIifSAgICAgICAgICAgICAgfQoKZXZlbnQ6IHBpbmcKZGF0YTogeyJ0eXBlIjogInBpbmcifQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoidGhpbmtpbmdfZGVsdGEiLCJ0aGlua2luZyI6IlRoZSB1c2VyIHdhbnRzIG1lIHRvIGJ1aWxkIGEgd29ya2Zsb3cgbmFtZWQgXCJJTlMtMTY0IG1vY2tlZCBjcmVkZW50aWFsIGd1YXJkXCIgd2l0aCBhIE1hbnVhbCBUcmlnZ2VyIGNvbm5lY3RlZCB0byBhIFNsYWNrIG5vZGUgdGhhdCBwb3N0cyBhIG1lc3NhZ2UgdXNpbmcgYSBtb2NrZWQgc2xhY2tBcGkgY3JlZGVudGlhbCBwbGFjZWhvbGRlci4gTGV0IG1lIGxvYWQifSB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJ0aGlua2luZ19kZWx0YSIsInRoaW5raW5nIjoiIHRoZSB3b3JrZmxvdy1idWlsZGVyIHNraWxsIGZpcnN0LiJ9ICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoic2lnbmF0dXJlX2RlbHRhIiwic2lnbmF0dXJlIjoiRXJrRENtVUlEeGdDS2tDQWJhRFFFUnhBZEpSN012OFprMEwyZ0JJTXF5RGs1WHV0YWM5K1dvbUVWT0dxM1h1Y1U3YUJtQkx3ZTJyazdRSEdQbXJ5OXVKK0hPMTg4bW5tYmJYK01oRmpiR0YxWkdVdGMyOXVibVYwTFRRdE5qZ0FRZ2gwYUdsdWEybHVaeElNUGNzSFNXaWkwOGRqT25Cdkdnd2dKWTNrVWpPZXBaRm5xcmNpTUcrRks2VXBXU3lla1FtSVJHOWc3Y3lyN0lyOXBuUXl6N3V4UU1zZDVLVFR3YlBMbFZnQVdma2d6Q3dDVjZjVjFTcUJBbEJ6RGtwc1FuV3pqK1M2MUdMeU43VUVxb2FzNkhKd25WeWR3YTY1bzRwS2treENYVThYUVhEZks2LzVMZlo1cmpsNERaZGFjUzhDZk1odm90Q3gyaW55YW5ISGsvcHpLUjh5RnVUS0tqSDVMWlEwZmFLNDk1OS91N3RHM3l5RXZkNGtqYXlPWE1NYmVQbEU1T1Ixd1VyemU1aXlKOHZuUm95NU9mN3JOZFdSY1BST3pPc3lldGRXMWNTSHF1U0NRZzhaRTdoVmp1Rk5uVzZ0bkpUSmFKS0R0cG5JaDBXWkxZdzdPaHhucUNGeDFYTjRTbEhUVEltaTNpaVU5eDhRQkdMNXhBdmZzYkFiSjBqQURKYlRHVllyL1Z4U0RkVXpwYjJlemZodlpMM0FoTG5hTEE4Qk83UGhnRjRhNGUvRW9ydEVWUFo1S3dydFB5ZndRVWtXYUxQT0dBRT0ifSAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19zdG9wCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RvcCIsImluZGV4IjowICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfc3RhcnQKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdGFydCIsImluZGV4IjoxLCJjb250ZW50X2Jsb2NrIjp7InR5cGUiOiJ0ZXh0IiwidGV4dCI6IiJ9ICAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MSwiZGVsdGEiOnsidHlwZSI6InRleHRfZGVsdGEiLCJ0ZXh0IjoiTG9hZGluZyB0aGUgd29ya2Zsb3ctYnVpbGRlciBza2lsbCBiZWZvcmUgd3JpdGluZyBhbnkgY29kZS4ifSAgICAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0b3AKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdG9wIiwiaW5kZXgiOjEgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19zdGFydApkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX3N0YXJ0IiwiaW5kZXgiOjIsImNvbnRlbnRfYmxvY2siOnsidHlwZSI6InRvb2xfdXNlIiwiaWQiOiJ0b29sdV8wMTVNU0F1ZG5BeG5vMkRWczZCalhLcDMiLCJuYW1lIjoibG9hZF9za2lsbCIsImlucHV0Ijp7fSwiY2FsbGVyIjp7InR5cGUiOiJkaXJlY3QifX0gICAgICAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjIsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoiIn0gICAgICAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjIsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoie1wibmFtZVwiOiBcIndvcmtmbG93LWJ1aWxkZXIifSAgICAgICAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MiwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJcIn0ifSAgICAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0b3AKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdG9wIiwiaW5kZXgiOjJ9CgpldmVudDogbWVzc2FnZV9kZWx0YQpkYXRhOiB7InR5cGUiOiJtZXNzYWdlX2RlbHRhIiwiZGVsdGEiOnsic3RvcF9yZWFzb24iOiJ0b29sX3VzZSIsInN0b3Bfc2VxdWVuY2UiOm51bGwsInN0b3BfZGV0YWlscyI6bnVsbH0sInVzYWdlIjp7ImlucHV0X3Rva2VucyI6MywiY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zIjoxODE3NCwiY2FjaGVfcmVhZF9pbnB1dF90b2tlbnMiOjAsIm91dHB1dF90b2tlbnMiOjEzNywib3V0cHV0X3Rva2Vuc19kZXRhaWxzIjp7InRoaW5raW5nX3Rva2VucyI6Njd9fSAgICAgICAgICAgICAgfQoKZXZlbnQ6IG1lc3NhZ2Vfc3RvcApkYXRhOiB7InR5cGUiOiJtZXNzYWdlX3N0b3AiICAgICAgICAgIH0KCg==",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0000-1783094387250-unknown-host-POST-_v1_messages-a942b0c9.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -1,108 +0,0 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*\"role\"\\s*:\\s*\"user\"[\\s\\S]{0,1500}\"type\"\\s*:\\s*\"tool_result\"[\\s\\S]{0,2000}\\\\\"success\\\\\":true[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-f82ed43335128ea318b236481fdf63c0-d7b9cef1f79e85e2-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"set-cookie": [
"_cfuvid=bD1JaEtFDBU30TuRQWLnhLWS42SfHn2_F2gIR7Y_c_4-1781088437.9595-1.0.1.1-ydwko9qf2D91tl.MRFl5naSCwiKUphpJuhlPX2ia9VM; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com"
],
"request-id": [
"req_011CbuSqaNdYw3TbqCy92oVf"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-06-10T10:47:18Z"
],
"anthropic-ratelimit-tokens-remaining": [
"26973000"
],
"anthropic-ratelimit-tokens-limit": [
"27000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-06-10T10:47:18Z"
],
"anthropic-ratelimit-requests-remaining": [
"19998"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-06-10T10:47:18Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"4500000"
],
"anthropic-ratelimit-output-tokens-limit": [
"4500000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-06-10T10:47:18Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"22473000"
],
"anthropic-ratelimit-input-tokens-limit": [
"22500000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Wed, 10 Jun 2026 10:47:20 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a097cc113efcbc86-HEL"
]
},
"cookies": {
"_cfuvid": "bD1JaEtFDBU30TuRQWLnhLWS42SfHn2_F2gIR7Y_c_4-1781088437.9595-1.0.1.1-ydwko9qf2D91tl.MRFl5naSCwiKUphpJuhlPX2ia9VM"
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01AgrALjqXcLvuxfe9FJ4Kmx\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":15474,\"cache_read_input_tokens\":15336,\"cache_creation\":{\"ephemeral_5m_input_tokens\":15474,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":47,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01QpcFwdyXg3zcJ9R39RKDdf\",\"name\":\"nodes\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"action\\\": \\\"search\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\", \\\"query\\\": \\\"Slack\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\", \\\"limit\\\": 5\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":15474,\"cache_read_input_tokens\":15336,\"output_tokens\":86} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxQWdyQUxqcVhjTHZ1eGZlOUZKNEtteCIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjEsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6MTU0NzQsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjoxNTMzNiwiY2FjaGVfY3JlYXRpb24iOnsiZXBoZW1lcmFsXzVtX2lucHV0X3Rva2VucyI6MTU0NzQsImVwaGVtZXJhbF8xaF9pbnB1dF90b2tlbnMiOjB9LCJvdXRwdXRfdG9rZW5zIjo0Nywic2VydmljZV90aWVyIjoic3RhbmRhcmQiLCJpbmZlcmVuY2VfZ2VvIjoiZ2xvYmFsIn19ICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfc3RhcnQKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdGFydCIsImluZGV4IjowLCJjb250ZW50X2Jsb2NrIjp7InR5cGUiOiJ0b29sX3VzZSIsImlkIjoidG9vbHVfMDFRcGNGd2R5WGczemNKOVIzOVJLRGRmIiwibmFtZSI6Im5vZGVzIiwiaW5wdXQiOnt9LCJjYWxsZXIiOnsidHlwZSI6ImRpcmVjdCJ9fSB9CgpldmVudDogcGluZwpkYXRhOiB7InR5cGUiOiAicGluZyJ9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoiIn0gICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoie1wiYWN0aW9uXCI6IFwic2VhcmNoIn0gICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6IlwiLCBcInF1ZXJ5XCI6IFwiU2xhY2sifSAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MCwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJcIiwgXCJsaW1pdFwiOiA1In0gICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6In0ifSAgICAgICAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0b3AKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdG9wIiwiaW5kZXgiOjAgICAgIH0KCmV2ZW50OiBtZXNzYWdlX2RlbHRhCmRhdGE6IHsidHlwZSI6Im1lc3NhZ2VfZGVsdGEiLCJkZWx0YSI6eyJzdG9wX3JlYXNvbiI6InRvb2xfdXNlIiwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsfSwidXNhZ2UiOnsiaW5wdXRfdG9rZW5zIjoxLCJjYWNoZV9jcmVhdGlvbl9pbnB1dF90b2tlbnMiOjE1NDc0LCJjYWNoZV9yZWFkX2lucHV0X3Rva2VucyI6MTUzMzYsIm91dHB1dF90b2tlbnMiOjg2fSAgICAgIH0KCmV2ZW50OiBtZXNzYWdlX3N0b3AKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdG9wIiAgICB9Cgo=",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0001-1781088457408-unknown-host-POST-_v1_messages-77bad225.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -0,0 +1,102 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*\"role\"\\s*:\\s*\"user\"[\\s\\S]{0,15000}\"type\"\\s*:\\s*\"tool_result\"[\\s\\S]{0,100000}\\\\\"success\\\\\"\\s*:\\s*true[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-2bc191e87143dee1ca785d6a3dfdad94-9578e76a3aed2054-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"request-id": [
"req_011CcfQCq55B7WXQ572CF3pE"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-07-03T15:58:54Z"
],
"anthropic-ratelimit-tokens-remaining": [
"17989000"
],
"anthropic-ratelimit-tokens-limit": [
"18000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-07-03T15:58:54Z"
],
"anthropic-ratelimit-requests-remaining": [
"19999"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-07-03T15:58:54Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"3000000"
],
"anthropic-ratelimit-output-tokens-limit": [
"3000000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-07-03T15:58:54Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"14989000"
],
"anthropic-ratelimit-input-tokens-limit": [
"15000000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Fri, 03 Jul 2026 15:58:56 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a1571825ee1d1579-HEL"
]
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01N7pz1beAbRrMwJLLxheM3K\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":19755,\"cache_read_input_tokens\":18174,\"cache_creation\":{\"ephemeral_5m_input_tokens\":19755,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Now\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" I'll look up the Slack node type definition to\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" get the exact credential type and parameter shape before writing the source\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" file.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01DSwWbqBwKbk4hxbrtadHBG\",\"name\":\"nodes\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"action\\\": \\\"search\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\", \\\"query\\\": \\\"Slack\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\", \\\"limit\\\": 5\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":19755,\"cache_read_input_tokens\":18174,\"output_tokens\":113,\"output_tokens_details\":{\"thinking_tokens\":0}} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxTjdwejFiZUFiUnJNd0pMTHhoZU0zSyIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjEsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6MTk3NTUsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjoxODE3NCwiY2FjaGVfY3JlYXRpb24iOnsiZXBoZW1lcmFsXzVtX2lucHV0X3Rva2VucyI6MTk3NTUsImVwaGVtZXJhbF8xaF9pbnB1dF90b2tlbnMiOjB9LCJvdXRwdXRfdG9rZW5zIjoxLCJzZXJ2aWNlX3RpZXIiOiJzdGFuZGFyZCIsImluZmVyZW5jZV9nZW8iOiJnbG9iYWwifX0gICAgICAgICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfc3RhcnQKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdGFydCIsImluZGV4IjowLCJjb250ZW50X2Jsb2NrIjp7InR5cGUiOiJ0ZXh0IiwidGV4dCI6IiJ9ICAgICAgICAgICAgfQoKZXZlbnQ6IHBpbmcKZGF0YTogeyJ0eXBlIjogInBpbmcifQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoidGV4dF9kZWx0YSIsInRleHQiOiJOb3cifSAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MCwiZGVsdGEiOnsidHlwZSI6InRleHRfZGVsdGEiLCJ0ZXh0IjoiIEknbGwgbG9vayB1cCB0aGUgU2xhY2sgbm9kZSB0eXBlIGRlZmluaXRpb24gdG8ifSB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJ0ZXh0X2RlbHRhIiwidGV4dCI6IiBnZXQgdGhlIGV4YWN0IGNyZWRlbnRpYWwgdHlwZSBhbmQgcGFyYW1ldGVyIHNoYXBlIGJlZm9yZSB3cml0aW5nIHRoZSBzb3VyY2UifX0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MCwiZGVsdGEiOnsidHlwZSI6InRleHRfZGVsdGEiLCJ0ZXh0IjoiIGZpbGUuIn0gICAgICAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0b3AKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdG9wIiwiaW5kZXgiOjAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0YXJ0CmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RhcnQiLCJpbmRleCI6MSwiY29udGVudF9ibG9jayI6eyJ0eXBlIjoidG9vbF91c2UiLCJpZCI6InRvb2x1XzAxRFN3V2JxQndLYms0aHhicnRhZEhCRyIsIm5hbWUiOiJub2RlcyIsImlucHV0Ijp7fSwiY2FsbGVyIjp7InR5cGUiOiJkaXJlY3QifX0gICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjEsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoiIn0gICAgICAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjEsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoie1wiYWN0aW9uXCI6IFwic2VhcmNoIn0gICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MSwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJcIiwgXCJxdWVyeVwiOiBcIlNsYWNrIn0gICAgICAgICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MSwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJcIiwgXCJsaW1pdFwiOiA1In0gICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjoxLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6In0ifSAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19zdG9wCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RvcCIsImluZGV4IjoxICAgICAgICAgICAgICB9CgpldmVudDogbWVzc2FnZV9kZWx0YQpkYXRhOiB7InR5cGUiOiJtZXNzYWdlX2RlbHRhIiwiZGVsdGEiOnsic3RvcF9yZWFzb24iOiJ0b29sX3VzZSIsInN0b3Bfc2VxdWVuY2UiOm51bGwsInN0b3BfZGV0YWlscyI6bnVsbH0sInVzYWdlIjp7ImlucHV0X3Rva2VucyI6MSwiY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zIjoxOTc1NSwiY2FjaGVfcmVhZF9pbnB1dF90b2tlbnMiOjE4MTc0LCJvdXRwdXRfdG9rZW5zIjoxMTMsIm91dHB1dF90b2tlbnNfZGV0YWlscyI6eyJ0aGlua2luZ190b2tlbnMiOjB9fSAgICAgICAgICAgICAgIH0KCmV2ZW50OiBtZXNzYWdlX3N0b3AKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdG9wIiAgICAgICAgICAgfQoK",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0001-1783094387252-unknown-host-POST-_v1_messages-f808330f.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -1,108 +0,0 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*\"role\"\\s*:\\s*\"user\"[\\s\\S]{0,1500}\"type\"\\s*:\\s*\"tool_result\"[\\s\\S]{0,2000}\\{\\\\\"results\\\\\":\\[\\{\\\\\"name\\\\\":\\\\\"n8n-nodes-base\\.slack\\\\\",\\\\\"displayName\\\\\":\\\\\"Slack\\\\\",\\\\\"description\\\\\":\\\\\"Consume Slack API\\\\\",\\\\\"version\\\\\":2\\.5,\\\\\"inputs[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-cbbd6bc2127819eef3255234d1cf3eeb-de3c3226af054035-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"set-cookie": [
"_cfuvid=bHGRW2nTkJiSJh98mLt8z.qj6FEPnfy_3h7qwEEQYOk-1781088441.8658457-1.0.1.1-9tYojpjjr1QvFWicZJWS2C7oH3dSs5R8HcZUphUxOfo; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com"
],
"request-id": [
"req_011CbuSqs2jeDyAPpFfuxPSy"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-06-10T10:47:22Z"
],
"anthropic-ratelimit-tokens-remaining": [
"26973000"
],
"anthropic-ratelimit-tokens-limit": [
"27000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-06-10T10:47:22Z"
],
"anthropic-ratelimit-requests-remaining": [
"19998"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-06-10T10:47:22Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"4500000"
],
"anthropic-ratelimit-output-tokens-limit": [
"4500000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-06-10T10:47:22Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"22473000"
],
"anthropic-ratelimit-input-tokens-limit": [
"22500000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Wed, 10 Jun 2026 10:47:24 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a097cc29aa519a02-HEL"
]
},
"cookies": {
"_cfuvid": "bHGRW2nTkJiSJh98mLt8z.qj6FEPnfy_3h7qwEEQYOk-1781088441.8658457-1.0.1.1-9tYojpjjr1QvFWicZJWS2C7oH3dSs5R8HcZUphUxOfo"
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_014fsT6GbuM5Wp39XArhTB7f\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":788,\"cache_read_input_tokens\":30810,\"cache_creation\":{\"ephemeral_5m_input_tokens\":788,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":48,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01NQ5A7N59J6JMMizNEPB4DX\",\"name\":\"nodes\",\"input\":{},\"caller\":{\"type\":\"direct\"}}}\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"action\\\": \\\"type-definition\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\", \\\"nodeTypes\\\": [{\\\"nodeType\\\": \\\"n8n-nodes-base.slack\\\", \\\"resource\\\": \\\"message\\\", \\\"operation\\\": \\\"post\\\"}]\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":788,\"cache_read_input_tokens\":30810,\"output_tokens\":99} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxNGZzVDZHYnVNNVdwMzlYQXJoVEI3ZiIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjEsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6Nzg4LCJjYWNoZV9yZWFkX2lucHV0X3Rva2VucyI6MzA4MTAsImNhY2hlX2NyZWF0aW9uIjp7ImVwaGVtZXJhbF81bV9pbnB1dF90b2tlbnMiOjc4OCwiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjQ4LCJzZXJ2aWNlX3RpZXIiOiJzdGFuZGFyZCIsImluZmVyZW5jZV9nZW8iOiJnbG9iYWwifX0gICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19zdGFydApkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX3N0YXJ0IiwiaW5kZXgiOjAsImNvbnRlbnRfYmxvY2siOnsidHlwZSI6InRvb2xfdXNlIiwiaWQiOiJ0b29sdV8wMU5RNUE3TjU5SjZKTU1pek5FUEI0RFgiLCJuYW1lIjoibm9kZXMiLCJpbnB1dCI6e30sImNhbGxlciI6eyJ0eXBlIjoiZGlyZWN0In19fQoKZXZlbnQ6IHBpbmcKZGF0YTogeyJ0eXBlIjogInBpbmcifQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6IiJ9ICAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoie1wiYWN0aW9uXCI6IFwidHlwZS1kZWZpbml0aW9uIn0gICAgICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6IlwiLCBcIm5vZGVUeXBlc1wiOiBbe1wibm9kZVR5cGVcIjogXCJuOG4tbm9kZXMtYmFzZS5zbGFja1wiLCBcInJlc291cmNlXCI6IFwibWVzc2FnZVwiLCBcIm9wZXJhdGlvblwiOiBcInBvc3RcIn1dIn0gIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MCwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJ9In0gICAgICAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19zdG9wCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RvcCIsImluZGV4IjowICAgICB9CgpldmVudDogbWVzc2FnZV9kZWx0YQpkYXRhOiB7InR5cGUiOiJtZXNzYWdlX2RlbHRhIiwiZGVsdGEiOnsic3RvcF9yZWFzb24iOiJ0b29sX3VzZSIsInN0b3Bfc2VxdWVuY2UiOm51bGwsInN0b3BfZGV0YWlscyI6bnVsbH0sInVzYWdlIjp7ImlucHV0X3Rva2VucyI6MSwiY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zIjo3ODgsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjozMDgxMCwib3V0cHV0X3Rva2VucyI6OTl9IH0KCmV2ZW50OiBtZXNzYWdlX3N0b3AKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdG9wIiAgIH0KCg==",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0002-1781088457409-unknown-host-POST-_v1_messages-ad09d9a0.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -0,0 +1,102 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*\"role\"\\s*:\\s*\"user\"[\\s\\S]{0,15000}\"type\"\\s*:\\s*\"tool_result\"[\\s\\S]{0,100000}\\{\\\\\"results\\\\\":\\[\\{\\\\\"name\\\\\":\\\\\"n8n-nodes-base\\.slack\\\\\",\\\\\"displayName\\\\\":\\\\\"Slack\\\\\",\\\\\"description\\\\\":\\\\\"Consume Slack API\\\\\",\\\\\"version\\\\\":2\\.5,\\\\\"inputs[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-ad7c731cec5cdc95b19735d4e5c39e3a-dbc0d65479ebb259-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"request-id": [
"req_011CcfQDBE2Evd9VEgVEsgWZ"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-07-03T15:58:59Z"
],
"anthropic-ratelimit-tokens-remaining": [
"17989000"
],
"anthropic-ratelimit-tokens-limit": [
"18000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-07-03T15:58:59Z"
],
"anthropic-ratelimit-requests-remaining": [
"19999"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-07-03T15:58:59Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"3000000"
],
"anthropic-ratelimit-output-tokens-limit": [
"3000000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-07-03T15:58:59Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"14989000"
],
"anthropic-ratelimit-input-tokens-limit": [
"15000000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Fri, 03 Jul 2026 15:59:00 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a157184368c95288-HEL"
]
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01VDdWSRoSbASWv9MDyupeCM\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":851,\"cache_read_input_tokens\":37929,\"cache_creation\":{\"ephemeral_5m_input_tokens\":851,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":47,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01Jb8VQ7LBfhwyqVk2mXfsHW\",\"name\":\"nodes\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"action\\\": \\\"type-definition\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\", \\\"nodeTypes\\\": [{\\\"nodeType\\\": \\\"n8n-nodes-base.slack\\\", \\\"resource\\\": \\\"message\\\", \\\"operation\\\": \\\"post\\\"}\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"]\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"}\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":851,\"cache_read_input_tokens\":37929,\"output_tokens\":99,\"output_tokens_details\":{\"thinking_tokens\":0}} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxVkRkV1NSb1NiQVNXdjlNRHl1cGVDTSIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjEsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6ODUxLCJjYWNoZV9yZWFkX2lucHV0X3Rva2VucyI6Mzc5MjksImNhY2hlX2NyZWF0aW9uIjp7ImVwaGVtZXJhbF81bV9pbnB1dF90b2tlbnMiOjg1MSwiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjQ3LCJzZXJ2aWNlX3RpZXIiOiJzdGFuZGFyZCIsImluZmVyZW5jZV9nZW8iOiJnbG9iYWwifX0gIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0YXJ0CmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RhcnQiLCJpbmRleCI6MCwiY29udGVudF9ibG9jayI6eyJ0eXBlIjoidG9vbF91c2UiLCJpZCI6InRvb2x1XzAxSmI4VlE3TEJmaHd5cVZrMm1YZnNIVyIsIm5hbWUiOiJub2RlcyIsImlucHV0Ijp7fSwiY2FsbGVyIjp7InR5cGUiOiJkaXJlY3QifX0gfQoKZXZlbnQ6IHBpbmcKZGF0YTogeyJ0eXBlIjogInBpbmcifQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6IiJ9ICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6IntcImFjdGlvblwiOiBcInR5cGUtZGVmaW5pdGlvbiJ9IH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MCwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJcIiwgXCJub2RlVHlwZXNcIjogW3tcIm5vZGVUeXBlXCI6IFwibjhuLW5vZGVzLWJhc2Uuc2xhY2tcIiwgXCJyZXNvdXJjZVwiOiBcIm1lc3NhZ2VcIiwgXCJvcGVyYXRpb25cIjogXCJwb3N0XCJ9In0gICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6Il0ifSAgICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6In0ifX0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0b3AKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdG9wIiwiaW5kZXgiOjB9CgpldmVudDogbWVzc2FnZV9kZWx0YQpkYXRhOiB7InR5cGUiOiJtZXNzYWdlX2RlbHRhIiwiZGVsdGEiOnsic3RvcF9yZWFzb24iOiJ0b29sX3VzZSIsInN0b3Bfc2VxdWVuY2UiOm51bGwsInN0b3BfZGV0YWlscyI6bnVsbH0sInVzYWdlIjp7ImlucHV0X3Rva2VucyI6MSwiY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zIjo4NTEsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjozNzkyOSwib3V0cHV0X3Rva2VucyI6OTksIm91dHB1dF90b2tlbnNfZGV0YWlscyI6eyJ0aGlua2luZ190b2tlbnMiOjB9fSAgICAgICAgIH0KCmV2ZW50OiBtZXNzYWdlX3N0b3AKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdG9wIiAgICAgICAgICAgICAgfQoK",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0002-1783094387254-unknown-host-POST-_v1_messages-c5c7530c.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -1,108 +0,0 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*\"role\"\\s*:\\s*\"user\"[\\s\\S]{0,1500}\"type\"\\s*:\\s*\"tool_result\"[\\s\\S]{0,2000}\\\\\"success\\\\\":true[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-09ee4379a1d165c5afced684e0aa41a5-52ab64298eb30feb-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"set-cookie": [
"_cfuvid=Tx2Dpu4Ih47Wl2zgPKXuE6NCJc3t.JONqQRnncu8XPk-1781088453.0067868-1.0.1.1-CUiyMIX9sqelVrmhsf_r_i4kU1OMZRpaTni.rfhEn1s; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com"
],
"request-id": [
"req_011CbuSrghQuDQvrRki9TSbm"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-06-10T10:47:33Z"
],
"anthropic-ratelimit-tokens-remaining": [
"26972000"
],
"anthropic-ratelimit-tokens-limit": [
"27000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-06-10T10:47:33Z"
],
"anthropic-ratelimit-requests-remaining": [
"19998"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-06-10T10:47:33Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"4500000"
],
"anthropic-ratelimit-output-tokens-limit": [
"4500000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-06-10T10:47:33Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"22472000"
],
"anthropic-ratelimit-input-tokens-limit": [
"22500000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Wed, 10 Jun 2026 10:47:34 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a097cc6f4faf0560-HEL"
]
},
"cookies": {
"_cfuvid": "Tx2Dpu4Ih47Wl2zgPKXuE6NCJc3t.JONqQRnncu8XPk-1781088453.0067868-1.0.1.1-CUiyMIX9sqelVrmhsf_r_i4kU1OMZRpaTni.rfhEn1s"
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_012ju9RaTdQiLkLWqsEyyGXP\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":676,\"cache_read_input_tokens\":34323,\"cache_creation\":{\"ephemeral_5m_input_tokens\":676,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" build reports `needs_setup` due to unresolved placeholders and a m\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ocked `slackApi` credential. Opening the setup card now.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01PhpA22r9iSt1gt9sMgAhDX\",\"name\":\"workflows\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"action\\\": \\\"setup\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\", \\\"workflowId\\\": \\\"oZlS9NokcpVHqKR\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"W\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"}\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":676,\"cache_read_input_tokens\":34323,\"output_tokens\":117} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxMmp1OVJhVGRRaUxrTFdxc0V5eUdYUCIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjEsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6Njc2LCJjYWNoZV9yZWFkX2lucHV0X3Rva2VucyI6MzQzMjMsImNhY2hlX2NyZWF0aW9uIjp7ImVwaGVtZXJhbF81bV9pbnB1dF90b2tlbnMiOjY3NiwiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjEsInNlcnZpY2VfdGllciI6InN0YW5kYXJkIiwiaW5mZXJlbmNlX2dlbyI6Imdsb2JhbCJ9fSAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19zdGFydApkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX3N0YXJ0IiwiaW5kZXgiOjAsImNvbnRlbnRfYmxvY2siOnsidHlwZSI6InRleHQiLCJ0ZXh0IjoiIn0gICAgICAgICAgICB9CgpldmVudDogcGluZwpkYXRhOiB7InR5cGUiOiAicGluZyJ9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJ0ZXh0X2RlbHRhIiwidGV4dCI6IlRoZSJ9ICAgICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoidGV4dF9kZWx0YSIsInRleHQiOiIgYnVpbGQgcmVwb3J0cyBgbmVlZHNfc2V0dXBgIGR1ZSB0byB1bnJlc29sdmVkIHBsYWNlaG9sZGVycyBhbmQgYSBtIn0gICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJ0ZXh0X2RlbHRhIiwidGV4dCI6Im9ja2VkIGBzbGFja0FwaWAgY3JlZGVudGlhbC4gT3BlbmluZyB0aGUgc2V0dXAgY2FyZCBub3cuIn0gICB9CgpldmVudDogY29udGVudF9ibG9ja19zdG9wCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RvcCIsImluZGV4IjowICAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0YXJ0CmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RhcnQiLCJpbmRleCI6MSwiY29udGVudF9ibG9jayI6eyJ0eXBlIjoidG9vbF91c2UiLCJpZCI6InRvb2x1XzAxUGhwQTIycjlpU3QxZ3Q5c01nQWhEWCIsIm5hbWUiOiJ3b3JrZmxvd3MiLCJpbnB1dCI6e30sImNhbGxlciI6eyJ0eXBlIjoiZGlyZWN0In19IH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MSwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiIifSAgICAgICAgICAgICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjoxLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6IntcImFjdGlvblwiOiBcInNldHVwIn0gICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjoxLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6IlwiLCBcIndvcmtmbG93SWRcIjogXCJvWmxTOU5va2NwVkhxS1IifSAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MSwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJXIn0gICAgICAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjEsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoiXCJ9In19CgpldmVudDogY29udGVudF9ibG9ja19zdG9wCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RvcCIsImluZGV4IjoxfQoKZXZlbnQ6IG1lc3NhZ2VfZGVsdGEKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9kZWx0YSIsImRlbHRhIjp7InN0b3BfcmVhc29uIjoidG9vbF91c2UiLCJzdG9wX3NlcXVlbmNlIjpudWxsLCJzdG9wX2RldGFpbHMiOm51bGx9LCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjEsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6Njc2LCJjYWNoZV9yZWFkX2lucHV0X3Rva2VucyI6MzQzMjMsIm91dHB1dF90b2tlbnMiOjExN30gICAgICAgfQoKZXZlbnQ6IG1lc3NhZ2Vfc3RvcApkYXRhOiB7InR5cGUiOiJtZXNzYWdlX3N0b3AiICAgICAgICAgICAgfQoK",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0004-1781088457412-unknown-host-POST-_v1_messages-77bad225.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -0,0 +1,102 @@
{
"httpRequest": {
"method": "POST",
"path": "/v1/messages",
"body": {
"type": "REGEX",
"regex": "[\\s\\S]*You are the n8n Instance Agent — an AI assistant embedded in an n8n instance\\. Yo[\\s\\S]*\"role\"\\s*:\\s*\"user\"[\\s\\S]{0,15000}\"type\"\\s*:\\s*\"tool_result\"[\\s\\S]{0,100000}\\\\\"success\\\\\"\\s*:\\s*true[\\s\\S]*"
}
},
"httpResponse": {
"statusCode": 200,
"reasonPhrase": "OK",
"headers": {
"vary": [
"Accept-Encoding"
],
"traceresponse": [
"00-a6bf722a2818c7538673c9e1fa0df0f2-a9f6a6efb6aeb043-01"
],
"strict-transport-security": [
"max-age=31536000; includeSubDomains; preload"
],
"request-id": [
"req_011CcfQFtv5uyC6MypPC4GQh"
],
"cf-cache-status": [
"DYNAMIC"
],
"anthropic-ratelimit-tokens-reset": [
"2026-07-03T15:59:36Z"
],
"anthropic-ratelimit-tokens-remaining": [
"17989000"
],
"anthropic-ratelimit-tokens-limit": [
"18000000"
],
"anthropic-ratelimit-requests-reset": [
"2026-07-03T15:59:36Z"
],
"anthropic-ratelimit-requests-remaining": [
"19999"
],
"anthropic-ratelimit-requests-limit": [
"20000"
],
"anthropic-ratelimit-output-tokens-reset": [
"2026-07-03T15:59:36Z"
],
"anthropic-ratelimit-output-tokens-remaining": [
"3000000"
],
"anthropic-ratelimit-output-tokens-limit": [
"3000000"
],
"anthropic-ratelimit-input-tokens-reset": [
"2026-07-03T15:59:36Z"
],
"anthropic-ratelimit-input-tokens-remaining": [
"14989000"
],
"anthropic-ratelimit-input-tokens-limit": [
"15000000"
],
"X-Robots-Tag": [
"none"
],
"Server": [
"cloudflare"
],
"Date": [
"Fri, 03 Jul 2026 15:59:38 GMT"
],
"Content-Type": [
"text/event-stream; charset=utf-8"
],
"Content-Security-Policy": [
"default-src 'none'; frame-ancestors 'none'"
],
"Cache-Control": [
"no-cache"
],
"CF-RAY": [
"a1571929da2b3b91-HEL"
]
},
"body": {
"type": "STRING",
"string": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_018UZac1pHT6eGDs68q2f6oU\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":478,\"cache_read_input_tokens\":41501,\"cache_creation\":{\"ephemeral_5m_input_tokens\":478,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":43,\"service_tier\":\"standard\",\"inference_geo\":\"global\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01TTvoYhLq6YYTAhaxyN2hW5\",\"name\":\"build-workflow\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"filePath\\\": \\\"src/workflows/ins-164-mocked-credential\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"-guard.workflow.ts\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\", \\\"name\\\": \\\"INS-164 mocked credential guard\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null},\"usage\":{\"input_tokens\":1,\"cache_creation_input_tokens\":478,\"cache_read_input_tokens\":41501,\"output_tokens\":96,\"output_tokens_details\":{\"thinking_tokens\":0}} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n",
"rawBytes": "ZXZlbnQ6IG1lc3NhZ2Vfc3RhcnQKZGF0YTogeyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtc29ubmV0LTQtNiIsImlkIjoibXNnXzAxOFVaYWMxcEhUNmVHRHM2OHEyZjZvVSIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsLCJ1c2FnZSI6eyJpbnB1dF90b2tlbnMiOjEsImNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VucyI6NDc4LCJjYWNoZV9yZWFkX2lucHV0X3Rva2VucyI6NDE1MDEsImNhY2hlX2NyZWF0aW9uIjp7ImVwaGVtZXJhbF81bV9pbnB1dF90b2tlbnMiOjQ3OCwiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjQzLCJzZXJ2aWNlX3RpZXIiOiJzdGFuZGFyZCIsImluZmVyZW5jZV9nZW8iOiJnbG9iYWwifX0gICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0YXJ0CmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfc3RhcnQiLCJpbmRleCI6MCwiY29udGVudF9ibG9jayI6eyJ0eXBlIjoidG9vbF91c2UiLCJpZCI6InRvb2x1XzAxVFR2b1loTHE2WVlUQWhheHlOMmhXNSIsIm5hbWUiOiJidWlsZC13b3JrZmxvdyIsImlucHV0Ijp7fSwiY2FsbGVyIjp7InR5cGUiOiJkaXJlY3QifX0gIH0KCmV2ZW50OiBwaW5nCmRhdGE6IHsidHlwZSI6ICJwaW5nIn0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MCwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiIifSAgICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX2RlbHRhCmRhdGE6IHsidHlwZSI6ImNvbnRlbnRfYmxvY2tfZGVsdGEiLCJpbmRleCI6MCwiZGVsdGEiOnsidHlwZSI6ImlucHV0X2pzb25fZGVsdGEiLCJwYXJ0aWFsX2pzb24iOiJ7XCJmaWxlUGF0aFwiOiBcInNyYy93b3JrZmxvd3MvaW5zLTE2NC1tb2NrZWQtY3JlZGVudGlhbCJ9ICAgICAgICAgICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoiLWd1YXJkLndvcmtmbG93LnRzIn0gICAgfQoKZXZlbnQ6IGNvbnRlbnRfYmxvY2tfZGVsdGEKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19kZWx0YSIsImluZGV4IjowLCJkZWx0YSI6eyJ0eXBlIjoiaW5wdXRfanNvbl9kZWx0YSIsInBhcnRpYWxfanNvbiI6IlwiLCBcIm5hbWVcIjogXCJJTlMtMTY0IG1vY2tlZCBjcmVkZW50aWFsIGd1YXJkIn0gICB9CgpldmVudDogY29udGVudF9ibG9ja19kZWx0YQpkYXRhOiB7InR5cGUiOiJjb250ZW50X2Jsb2NrX2RlbHRhIiwiaW5kZXgiOjAsImRlbHRhIjp7InR5cGUiOiJpbnB1dF9qc29uX2RlbHRhIiwicGFydGlhbF9qc29uIjoiXCJ9In0gICAgIH0KCmV2ZW50OiBjb250ZW50X2Jsb2NrX3N0b3AKZGF0YTogeyJ0eXBlIjoiY29udGVudF9ibG9ja19zdG9wIiwiaW5kZXgiOjAgICAgICAgICAgICAgIH0KCmV2ZW50OiBtZXNzYWdlX2RlbHRhCmRhdGE6IHsidHlwZSI6Im1lc3NhZ2VfZGVsdGEiLCJkZWx0YSI6eyJzdG9wX3JlYXNvbiI6InRvb2xfdXNlIiwic3RvcF9zZXF1ZW5jZSI6bnVsbCwic3RvcF9kZXRhaWxzIjpudWxsfSwidXNhZ2UiOnsiaW5wdXRfdG9rZW5zIjoxLCJjYWNoZV9jcmVhdGlvbl9pbnB1dF90b2tlbnMiOjQ3OCwiY2FjaGVfcmVhZF9pbnB1dF90b2tlbnMiOjQxNTAxLCJvdXRwdXRfdG9rZW5zIjo5Niwib3V0cHV0X3Rva2Vuc19kZXRhaWxzIjp7InRoaW5raW5nX3Rva2VucyI6MH19ICAgICAgICAgfQoKZXZlbnQ6IG1lc3NhZ2Vfc3RvcApkYXRhOiB7InR5cGUiOiJtZXNzYWdlX3N0b3AiICAgICAgICAgICB9Cgo=",
"contentType": "text/event-stream; charset=utf-8"
}
},
"id": "0004-1783094387259-unknown-host-POST-_v1_messages-f808330f.json",
"priority": 0,
"timeToLive": {
"unlimited": true
},
"times": {
"unlimited": true
}
}
@@ -342,6 +342,24 @@ export class ApiHelpers {
return body.data.thread;
}
/** Start an Instance AI chat run on a thread; returns the started `runId`. */
async startInstanceAiChat(
threadId: string,
message: string,
timeZone = 'UTC',
): Promise<{ runId: string }> {
const response = await this.request.post(`/rest/instance-ai/chat/${threadId}`, {
data: { message, timeZone },
});
if (!response.ok()) {
throw new TestError(
`POST /rest/instance-ai/chat/${threadId} failed (${response.status()}): ${await response.text()}`,
);
}
const body = (await response.json()) as { data: { runId: string } };
return body.data;
}
async renameInstanceAiThread(threadId: string, title: string): Promise<InstanceAiThreadInfo> {
const response = await this.request.patch(`/rest/instance-ai/threads/${threadId}`, {
data: { title },
@@ -631,6 +631,22 @@ export const instanceAiTestConfig = {
},
} as const;
/**
* Variant of `instanceAiTestConfig` for the cross-main smoke test. Unlike the
* single-main pin above, it deliberately does NOT set `mains`/`workers`, so it
* inherits the running project's topology: the `multi-main` project supplies
* 2 mains + a worker, while every other project supplies 1 main (the spec then
* skips via `mainUrls.length < 2`). It only exercises a conversational turn, so
* it never touches the unsupported agent-triggered worker-offload execution path.
*/
export const instanceAiMultiMainConfig = {
timezoneId: instanceAiTestConfig.timezoneId,
capability: {
services: instanceAiTestConfig.capability.services,
env: instanceAiTestConfig.capability.env,
},
} as const;
export const test = base.extend<InstanceAiFixtures>({
anthropicApiKey: async ({}, use) => {
await use(ANTHROPIC_API_KEY);
@@ -0,0 +1,173 @@
import http from 'node:http';
import https from 'node:https';
import { test, expect, instanceAiMultiMainConfig } from './fixtures';
import { N8N_AUTH_COOKIE } from '../../../config/constants';
// Inherits the project topology: 2 mains + worker on the `multi-main` project,
// 1 main elsewhere (the test then skips). Conversational-only — never hits the
// unsupported agent-triggered worker-offload execution path.
test.use(instanceAiMultiMainConfig);
interface SseEvent {
id?: number;
name?: string;
data: { type?: string; runId?: string; payload?: Record<string, unknown> };
}
/**
* Read an Instance AI SSE stream directly from a specific main (bypassing the
* load balancer), collecting frames until `until` matches or the timeout fires.
* Uses raw `http` like the MCP cross-main tests, since Playwright's request
* context buffers the whole response and never returns for a long-lived stream.
*/
async function collectSseUntil(opts: {
baseUrl: string;
threadId: string;
cookieHeader: string;
until: (event: SseEvent) => boolean;
timeoutMs: number;
/**
* Called once the response headers arrive. At that point the server has
* already registered the SSE subscription: the `/events` handler subscribes
* to the event bus before it flushes headers, so receiving headers is a
* race-free "subscribed" signal — no fixed delay needed.
*/
onSubscribed?: () => void;
}): Promise<SseEvent[]> {
const { baseUrl, threadId, cookieHeader, until, timeoutMs, onSubscribed } = opts;
const url = new URL(`${baseUrl}/rest/instance-ai/events/${threadId}`);
const transport = url.protocol === 'https:' ? https : http;
const events: SseEvent[] = [];
return await new Promise<SseEvent[]>((resolve, reject) => {
const req = transport.request(
url,
{ method: 'GET', headers: { Accept: 'text/event-stream', Cookie: cookieHeader } },
(res) => {
if (res.statusCode !== 200) {
res.resume();
reject(new Error(`SSE on ${baseUrl} returned HTTP ${res.statusCode}`));
return;
}
onSubscribed?.();
const timer = setTimeout(() => {
req.destroy();
reject(
new Error(
`SSE timed out after ${timeoutMs}ms; received: ${events.map((e) => e.name).join(', ') || '(none)'}`,
),
);
}, timeoutMs);
let buffer = '';
res.setEncoding('utf8');
res.on('data', (chunk: string) => {
buffer += chunk;
let sep: number;
while ((sep = buffer.indexOf('\n\n')) >= 0) {
const frame = buffer.slice(0, sep);
buffer = buffer.slice(sep + 2);
const event = parseSseFrame(frame);
if (!event) continue;
events.push(event);
if (until(event)) {
clearTimeout(timer);
req.destroy();
resolve(events);
return;
}
}
});
res.on('end', () => {
clearTimeout(timer);
resolve(events);
});
res.on('error', (error) => {
clearTimeout(timer);
reject(error);
});
},
);
req.on('error', reject);
req.end();
});
}
function parseSseFrame(frame: string): SseEvent | undefined {
let id: number | undefined;
let name: string | undefined;
const dataLines: string[] = [];
for (const line of frame.split('\n')) {
if (line.startsWith(':')) continue; // keep-alive comment
if (line.startsWith('id:')) id = Number(line.slice(3).trim());
else if (line.startsWith('event:')) name = line.slice(6).trim();
else if (line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /, ''));
}
if (dataLines.length === 0) return undefined;
try {
const data = JSON.parse(dataLines.join('\n')) as SseEvent['data'];
return { id, name: name ?? data.type, data };
} catch {
return undefined;
}
}
test.describe(
'Instance AI multi-main @capability:proxy',
{ annotation: [{ type: 'owner', description: 'instanceAI' }] },
() => {
test('@mode:multi-main streams a run produced on one main to an SSE held by another', async ({
api,
mainUrls,
createApiForMain,
}) => {
test.skip(mainUrls.length < 2, 'Requires at least 2 mains');
// Thread is created via the load balancer; lives in the shared DB.
const thread = await api.createInstanceAiThread();
const mainA = await createApiForMain(0); // holds the SSE connection
const mainB = await createApiForMain(1); // runs the agent
const { cookies } = await mainA.request.storageState();
const authCookie = cookies.find((c) => c.name === N8N_AUTH_COOKIE);
expect(authCookie, 'auth cookie present for main A').toBeTruthy();
const cookieHeader = `${authCookie!.name}=${authCookie!.value}`;
// Subscribe to the SSE on main A and wait until the subscription is
// registered (headers received) BEFORE producing on main B — otherwise
// A's relay handler drops B's events (it gates on `hasSubscribers`) and
// A never buffers them (it's not the producer). Deterministic, no sleep.
let onSubscribed!: () => void;
const subscribed = new Promise<void>((resolve) => {
onSubscribed = resolve;
});
const ssePromise = collectSseUntil({
baseUrl: mainUrls[0],
threadId: thread.id,
cookieHeader,
until: (event) => event.name === 'run-finish',
timeoutMs: 120_000,
onSubscribed: () => onSubscribed(),
});
await subscribed;
const { runId } = await mainB.startInstanceAiChat(
thread.id,
'Reply with exactly the single word: pong. Do not use any tools.',
);
const events = await ssePromise;
const forRun = events.filter((event) => event.data?.runId === runId);
expect(
forRun.some((event) => event.name === 'run-start'),
'main A received run-start for the run produced on main B',
).toBeTruthy();
expect(
forRun.some((event) => event.name === 'run-finish'),
'main A received run-finish for the run produced on main B',
).toBeTruthy();
});
},
);