drafted 1.11.6 → 1.11.7

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 +37 -0
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -85,6 +85,7 @@ function getOrCreateSessionState(sid) {
85
85
  s = {
86
86
  activeProjectId: null,
87
87
  activeProjectMeta: null,
88
+ boundOrgId: null,
88
89
  loadedSkillIds: new Set(),
89
90
  gates: createGateState(),
90
91
  cachedOrgId: null,
@@ -699,6 +700,7 @@ async function cloneSession() {
699
700
  const data = await res.json();
700
701
  if (data.sessionId) {
701
702
  getState().sessionId = data.sessionId;
703
+ await restoreBoundOrg(data.orgId);
702
704
  return true;
703
705
  }
704
706
  }
@@ -706,6 +708,33 @@ async function cloneSession() {
706
708
  return false;
707
709
  }
708
710
 
711
+ // A fresh clone inherits the ROOT login's current org. If this MCP session was
712
+ // already working in a different org (bound via project open or get_org switch),
713
+ // re-assert it on the new session so a WS-drop / 401 / server-bounce recovery is
714
+ // transparent. Otherwise the recovered session silently lands on the root's org,
715
+ // the next request appends the active project's id while scoped to the wrong org,
716
+ // the API returns "project not found", and the api() error handler clears the
717
+ // active project — the churn that shows up as active-project "flapping" after a
718
+ // server restart. Best-effort: a raw fetch (not api()) avoids recursing back
719
+ // through cloneSession on 401; if it fails, that stale-project clear is the
720
+ // backstop, same as before this fix.
721
+ async function restoreBoundOrg(clonedOrgId) {
722
+ const sess = getSessionState();
723
+ const want = sess.boundOrgId;
724
+ if (!want || want === clonedOrgId) return;
725
+ try {
726
+ const res = await fetch(`${getServerUrl()}/auth/switch-org`, {
727
+ method: 'POST',
728
+ headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
729
+ body: JSON.stringify({ orgId: want }),
730
+ });
731
+ if (res.ok) {
732
+ sess.cachedOrgId = null;
733
+ sess.cachedOrgIdTime = 0;
734
+ }
735
+ } catch { /* best-effort; the api() stale-project clear is the backstop */ }
736
+ }
737
+
709
738
  async function ensureSession() {
710
739
  if (getState().sessionId) return;
711
740
  // A pending device-code login (from `auth get_link`) takes priority; consuming
@@ -922,6 +951,11 @@ function setMcpActiveProject(projectId, meta = null) {
922
951
  const sess = getSessionState();
923
952
  sess.activeProjectId = projectId;
924
953
  sess.activeProjectMeta = meta;
954
+ // Remember the org this session is working in so session recovery (a re-clone
955
+ // after a WS drop / 401 / server bounce) can restore it instead of passively
956
+ // inheriting the root login's current org. Only set when known — a (null,null)
957
+ // clear must NOT wipe the bound org (the switch handler sets it explicitly).
958
+ if (meta?.orgId) sess.boundOrgId = meta.orgId;
925
959
  }
926
960
 
927
961
  // Clear the active project if its orgId no longer matches the current org.
@@ -1840,6 +1874,9 @@ tool('get_org', {
1840
1874
  // Clear active project too — projects are scoped to orgs, so the
1841
1875
  // previous one isn't valid in the new org.
1842
1876
  setMcpActiveProject(null, null);
1877
+ // Bind this session to the chosen org so session recovery re-asserts it
1878
+ // (set AFTER the clear above, which would otherwise leave it unchanged).
1879
+ sess.boundOrgId = args.orgId;
1843
1880
  const me = await api('GET', '/auth/me');
1844
1881
  const orgs = (await api('GET', '/api/orgs')).orgs || [];
1845
1882
  const activeOrg = (orgs || []).map(o => ({ id: o.orgId || o.id, name: o.orgName || o.name })).find(o => o.id === me?.orgId) || null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.11.6",
3
+ "version": "1.11.7",
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": [