drafted 1.11.34 → 1.11.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/mcp/server.mjs +21 -20
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -701,12 +701,12 @@ function schedulePendingAuthPoll(delayMs = 2000) {
701
701
  }
702
702
 
703
703
  // Cookie for normal tool operations. Deliberately uses ONLY this instance's
704
- // per-process session (getState().sessionId) and never the shared root login in
705
- // ~/.drafted/auth.json. The root is a *credential for minting child sessions*
706
- // (see cloneSession): every agent process on the machine reads the same root, so
707
- // operating on it directly is exactly what let one agent's get_org switch move
708
- // every other agent's active org. ensureSession() guarantees a per-instance
709
- // session before any operation runs.
704
+ // MCP requests carry a cloned child session (getState().sessionId) minted from the
705
+ // shared root login in ~/.drafted/auth.json. NOTE: org isolation is per-REQUEST via
706
+ // the X-Drafted-Org header (server: effectiveOrgId), NOT via the session the old
707
+ // "clone per process so one agent's org switch doesn't move another's" model is
708
+ // DEPRECATED. The clone remains only to give the agent a stable surface identity
709
+ // (one playful tab name across reconnects/restarts — see cloneSession).
710
710
  function getAuthHeaders() {
711
711
  const sid = getState().sessionId;
712
712
  const headers = {};
@@ -719,22 +719,21 @@ function getAuthHeaders() {
719
719
  return headers;
720
720
  }
721
721
 
722
- // Mint a fresh per-instance child session from the shared root login and bind it
723
- // to this process. The root session id (getBootstrapSessionId) is shared by every
724
- // agent on this machine via ~/.drafted/auth.json; the clone gives THIS process its
725
- // own server-side session so its active org/project stay isolated from other
726
- // agents. Returns true once a per-instance session is set.
722
+ // Mint (or reuse) a child session from the shared root login. The server dedups by
723
+ // (user, agentKey), so the SAME session comes back across reconnects/restarts one
724
+ // stable surface + one playful tab name. Org isolation is per-request (X-Drafted-Org),
725
+ // NOT per-session, so reusing one child session across processes is safe.
727
726
  async function cloneSession() {
728
727
  if (getState().sessionId) return true;
729
728
  const bootstrapId = getBootstrapSessionId();
730
729
  if (!bootstrapId) return false;
731
730
 
732
731
  try {
733
- // A named agent (DRAFTED_AGENT_NAME) gets a STABLE child session: the server
734
- // returns the same session for the same (user, agentKey) across reconnects, so a
735
- // restarted agent reconnects its existing named session instead of churning a new
736
- // one. Anonymous clients send no key and keep a fresh-per-process session.
737
- const agentKey = (process.env.DRAFTED_AGENT_NAME || '').trim();
732
+ // Named agents key on DRAFTED_AGENT_NAME; anonymous agents key on the root login
733
+ // id (stable per machine) so they ALSO keep one stable session/name across restarts
734
+ // instead of churning a fresh session every process. (Org is per-request, so a
735
+ // shared child session does NOT cause cross-agent org clobbering.)
736
+ const agentKey = (process.env.DRAFTED_AGENT_NAME || '').trim() || bootstrapId;
738
737
  const res = await fetch(`${getServerUrl()}/auth/session/clone`, {
739
738
  method: 'POST',
740
739
  headers: { 'Content-Type': 'application/json', Cookie: `gc_session=${bootstrapId}` },
@@ -1056,8 +1055,10 @@ async function connectAgentWs() {
1056
1055
  agentWs.on('close', () => {
1057
1056
  console.error('[MCP-WS] Disconnected, reconnecting in 5s...');
1058
1057
  agentWs = null;
1059
- // Server may have restarted invalidate session so ensureSession re-clones
1060
- getState().sessionId = null;
1058
+ // KEEP the cloned sessionId across disconnects/reconnects. Sessions live in Postgres
1059
+ // and survive server restarts, so reusing the clone keeps this agent's surface (and
1060
+ // its playful tab name) stable across blips — otherwise every reconnect re-clones a
1061
+ // fresh session → a NEW greyed tab with a NEW name. A real 401 still triggers re-clone.
1061
1062
  clearTimeout(agentWsReconnectTimer);
1062
1063
  agentWsReconnectTimer = setTimeout(() => {
1063
1064
  connectAgentWs().catch((e) => {
@@ -1314,8 +1315,8 @@ async function consumePendingDeviceCode() {
1314
1315
  const data = await res.json();
1315
1316
  if (data.status === 'approved' && data.sessionId) {
1316
1317
  // data.sessionId is the shared ROOT login. Persist it as the machine-level
1317
- // credential, then mint our OWN per-instance child session from it (mirrors
1318
- // the `auth login` tool) so this process never operates on the shared root.
1318
+ // credential, then mint a child session from it (mirrors the `auth login` tool)
1319
+ // so the agent gets a stable surface identity (see cloneSession).
1319
1320
  persistAuthSession(data);
1320
1321
  clearPendingDeviceCode();
1321
1322
  getState().sessionId = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.11.34",
3
+ "version": "1.11.36",
4
4
  "description": "Drafted — visual thinking surface for humans and AI agents. Renders HTML, markdown, images, and code as frames on a zoomable canvas, with MCP tools for AI agents and real-time sync for humans.",
5
5
  "type": "module",
6
6
  "files": [