Real-time collaboration

See other people's and agents' saves as they happen, see who else has a page open, and get warned before you overwrite someone's work.

AgentDocs uses Socket.IO to keep open pages up to date. When someone saves a page you have open (in the editor, over the API or through an agent), your copy updates within a moment. You also see who else has the page open.

How it works

When you sign in, the editor opens one live connection. Opening a page joins that page's room. The editor saves the whole page about half a second after you stop typing; the server stores a new version and sends it to everyone else in the room. If the live connection is down, the editor saves over the normal API instead.

Connecting to the real-time server

The real-time server runs at https://agentdocs.eu on the standard Socket.IO path (/socket.io/). Use any Socket.IO v4 client. Scripts and agents sign in with their account API token; the AgentDocs editor uses your browser session.

import { io } from 'socket.io-client';

// Scripts and agents: your account API token
const socket = io('https://agentdocs.eu', {
  auth: { token: 'Token YOUR_API_TOKEN' },
});

socket.on('connect_error', (err) => console.error(err.message));

// The AgentDocs editor sends its browser session instead:
// io('https://agentdocs.eu', { auth: { token: 'Bearer ' + jwt } });

The client connects over WebSocket and falls back to HTTP long-polling. The server checks the token once, when you connect. If it refuses, the client gets a connect_error event with the reason, such as "Invalid API token". A message larger than 1 MB closes the connection.

Space tokens and OAuth tokens from MCP connectors work only with the REST API and MCP. The real-time server refuses them; connect with an account API token instead.

Socket events

Clients send these events to join pages and save changes. page:open and page:change return their result through a Socket.IO acknowledgement: pass a callback as the last argument of emit.

Client → server events

EventPayloadDescription
page:open{ page_id }Join a page. Needs read access. The acknowledgement returns content, version, title, last_author and users_editing, or an error.
page:change{ page_id, content, version, title? }Save the page. content replaces the whole body, version is the version you edited, and title is optional. Needs edit access. The acknowledgement returns success, version and content, a conflict (see below) or an error. Limit: 60 per minute per connection.
page:cursor{ page_id, position }Pass a cursor position to the page's other clients. Works only after page:open. The AgentDocs editor doesn't use it.
page:leave{ page_id }Leave the page. The others receive page:user_left. Disconnecting leaves every page you opened.

Server → client events

EventPayloadDescription
page:changed{ page_id, content, title, version, author_id, author_name, edited_via, token_name }Someone else saved the page: in an editor, over the API or MCP, or by approving a proposal. You don't receive your own socket saves.
page:user_joined{ page_id, user_id, user_name, users_editing }Another client opened the page. users_editing lists everyone on the page, once per user.
page:user_left{ page_id, user_id, user_name, users_editing }A client left the page or disconnected.
page:cursor{ page_id, user_id, user_name, position }Another client sent page:cursor.
page:evicted{ page_id, space_id, reason }You lost access to an open page: it moved to a space you can't read (reason page_moved), or you were removed from its space (access_revoked). Updates for it stop.
session:revoked{ reason }Your credential was revoked by a password change or reset, or by a regenerated API token. The server then disconnects you.

Version conflicts

AgentDocs uses optimistic concurrency control to handle simultaneous edits. Each page has a version number that goes up with every save.

How conflict detection works

  1. 1The client opens a page and gets its content and version (say, 5) in the page:open acknowledgement.
  2. 2The client edits and sends page:change with version 5.
  3. 3If the page is still at version 5, the server saves version 6, acknowledges with success and version 6, and sends page:changed to everyone else.
  4. !If someone saved first, nothing is written and the acknowledgement is a conflict response with conflict: true, current_content and current_version. Merge your edit into current_content, then send page:change again with current_version.

In the AgentDocs editor, a conflict opens a Version Conflict dialog with two choices. Keep my changes replaces their version with yours. Use their version discards your unsaved text. There is no automatic merge.

If someone else saves while you're typing, or within 5 seconds of your last keystroke, the editor doesn't replace your text. A banner says who changed the page and offers Load their version or Keep editing. If you keep editing, your next save reports a conflict.

The REST API has the same check. Send the version you edited with PUT /api/pages/:id. If the page has changed since, you get 409 with current_version and current_content instead of overwriting it. Without version, the last write wins.

Presence

While others have the same page open, you see:

  • •Who's here — the names of everyone else on the page, next to a people icon in the toolbar
  • •Connection status — a green Wi-Fi icon when connected, a grey crossed-out one when not
  • •Change notices — a short notice when someone else's save arrives. Saves made by an agent also show a robot badge with the agent's name: the space token or connected app name, or "agent" for an account API token.

// Example: listening for presence events

socket.on('page:user_joined', ({ user_name, users_editing }) => {
  console.log(user_name + ' opened the page; ' + users_editing.length + ' here now');
});

socket.on('page:user_left', ({ user_name }) => {
  console.log(user_name + ' left the page');
});

Auto-save

The editor saves about half a second after you stop typing. The toolbar shows where the save stands; nothing is shown when there is nothing to save:

  • Saving — changes are being sent to the server
  • Saved — your changes are stored (shown for 2 seconds)
  • Conflict — someone saved first; choose which version to keep
  • Error — the save failed; a message says why

Disconnection and reconnection

Socket.IO reconnects automatically, waiting a little longer after each failed attempt. If your connection drops:

  • •The Wi-Fi icon in the toolbar turns grey.
  • •Your text stays in the editor, and saves continue over the normal API as long as your internet connection works.
  • •On reconnect, the editor rejoins the page and loads any newer version, or shows the banner if you're typing.
  • •If someone saved while you were disconnected and you then save your older copy, you get the conflict dialog.

Tip

Closing the tab right after typing, or while the toolbar shows Error, loses text that hasn't been saved. Wait for Saved, or copy your text first. Moving to another page inside AgentDocs is safe: the pending save still goes through.

For developers: full Socket.IO example

A complete client: connect, open a page, follow other people's saves, and save with a version check.

import { io } from 'socket.io-client';

const PAGE_ID = 'your-page-uuid';
let version = null;

const socket = io('https://agentdocs.eu', {
  auth: { token: 'Token YOUR_API_TOKEN' }, // account token; space tokens are refused
});

socket.on('connect_error', (err) => {
  console.error('Connection refused:', err.message);
});

// Runs on the first connect and after every reconnect
socket.on('connect', () => {
  socket.emit('page:open', { page_id: PAGE_ID }, (res) => {
    if (res.error) return console.error(res.error);
    version = res.version;
    console.log('Opened "' + res.title + '" at version ' + version);
  });
});

// Saves by others: editors, API clients, agents (never your own)
socket.on('page:changed', (data) => {
  version = data.version;
  console.log(data.author_name + ' saved version ' + data.version);
});

socket.on('page:user_joined', ({ user_name, users_editing }) => {
  console.log(user_name + ' opened the page (' + users_editing.length + ' here)');
});

socket.on('page:user_left', ({ user_name }) => {
  console.log(user_name + ' left the page');
});

// Replace the page body. Send the version you edited.
function save(content) {
  socket.emit('page:change', { page_id: PAGE_ID, content, version }, (res) => {
    if (res.conflict) {
      // Someone saved first. Merge into res.current_content,
      // then call save() again: it will use res.current_version.
      version = res.current_version;
      return;
    }
    if (res.error) return console.error(res.error);
    version = res.version;
  });
}

function leave() {
  socket.emit('page:leave', { page_id: PAGE_ID });
}

Each accepted page:change saves a new page version, sends page.updated webhooks and counts as one API call toward your plan's monthly limit. Saves made with an API token show in the editor as agent edits.

AgentDocs v1.1.0 · b55b6f7