drafted 1.11.38 → 1.12.0

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 +69 -0
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -215,6 +215,9 @@ const TOOL_ANNOTATIONS = {
215
215
  // Auth — initiates external browser / email flows
216
216
  auth: { title: 'Sign in', readOnlyHint: false, destructiveHint: false, openWorldHint: true, description: 'Sign in to Drafted. `action=get_link` returns a URL immediately and starts background approval polling; after the user opens the link, later Drafted tool calls also auto-consume the approved login. `action=login` opens a browser when needed and explicitly waits/polls for approval.' },
217
217
 
218
+ // Identity — read-only introspection of THIS agent's session
219
+ whoami: { title: 'Session identity', readOnlyHint: true, destructiveHint: false, openWorldHint: false, description: 'Return THIS agent session\'s identity: its server-assigned human-readable name + emoji (the correlation key between an agent window and its web-app session tab), sessionId, userId, orgId, active projectId, editor label, server URL, and surfaced/alive state. Read-only. Use this — not guesses from the host environment — to report which session you are.' },
220
+
218
221
  // Projects
219
222
  project: { title: 'Projects', readOnlyHint: false, destructiveHint: false, openWorldHint: false, widgetUri: 'ui://widget/drafted-canvas-overview.html', description: 'Manage projects: list (start here), open (switch active project), create, update, move to another org.' },
220
223
  get_org: { title: 'Organization', readOnlyHint: false, destructiveHint: false, openWorldHint: false, description: 'Get the active organization (action="get", default), Google Drive availability, or switch to a different org (action="switch", orgId=...). Use switch when you need wiki/skill work in an org that has no projects — opening a project also switches, but is unavailable in empty orgs. When googleDrive.connected is true, strongly prefer Google Workspace frames for documents, sheets, and slides.' },
@@ -1064,6 +1067,10 @@ function withProjectBreadcrumb(result) {
1064
1067
 
1065
1068
  let agentWs = null;
1066
1069
  let agentWsReconnectTimer = null;
1070
+ // Cached agent-hello-ack: this agent's own surface identity (name/emoji/alive) as
1071
+ // assigned by the server. The playful name is the correlation key between an agent
1072
+ // window and its web-app session tab; whoami reads it from here.
1073
+ let agentSurface = null;
1067
1074
 
1068
1075
  function setMcpActiveProject(projectId, meta = null) {
1069
1076
  const s = getState();
@@ -1115,6 +1122,21 @@ async function connectAgentWs() {
1115
1122
  }
1116
1123
  });
1117
1124
 
1125
+ agentWs.on('message', (raw) => {
1126
+ // agent-hello-ack carries this agent's server-assigned surface identity back on the
1127
+ // owning connection. Cache it so whoami can report the name without a prod WS probe.
1128
+ try {
1129
+ const m = JSON.parse(raw.toString());
1130
+ if (m.type === 'agent-hello-ack') {
1131
+ agentSurface = {
1132
+ sessionId: m.sessionId, userId: m.userId, orgId: m.orgId, projectId: m.projectId,
1133
+ name: m.name, emoji: m.emoji, alive: m.alive, surfaced: true,
1134
+ capturedAt: Date.now(),
1135
+ };
1136
+ }
1137
+ } catch { /* ignore non-JSON / unexpected */ }
1138
+ });
1139
+
1118
1140
  agentWs.on('close', () => {
1119
1141
  console.error('[MCP-WS] Disconnected, reconnecting in 5s...');
1120
1142
  agentWs = null;
@@ -1489,6 +1511,53 @@ if (!isRemote) tool('auth', 'Sign in to Drafted. `action=get_link` returns a ver
1489
1511
  } catch (error) { return err(error); }
1490
1512
  });
1491
1513
 
1514
+ // Identity: report THIS agent session's own surface identity. The name/emoji come from
1515
+ // the agent-hello-ack cached on the WS; falls back to /auth/me for userId/org if the WS
1516
+ // ack hasn't landed yet. Read-only — no state changed.
1517
+ tool('whoami', 'Return THIS agent session\'s identity: its server-assigned human-readable name + emoji (the correlation key between an agent window and its web-app session tab), sessionId, userId, orgId, active projectId, editor label, server URL, and surfaced/alive state. Read-only.', {}, async () => {
1518
+ try {
1519
+ const server = getServerUrl();
1520
+ const editor = (process.env.DRAFTED_AGENT_NAME || '').trim() || null;
1521
+ const base = {
1522
+ server,
1523
+ editor,
1524
+ agentLabel: getAgentLabel(),
1525
+ sessionId: agentSurface?.sessionId || getState().sessionId || null,
1526
+ };
1527
+ if (agentSurface) {
1528
+ return ok({
1529
+ ...base,
1530
+ userId: agentSurface.userId ?? null,
1531
+ orgId: agentSurface.orgId ?? null,
1532
+ projectId: agentSurface.projectId ?? null,
1533
+ name: agentSurface.name,
1534
+ emoji: agentSurface.emoji,
1535
+ surfaced: true,
1536
+ alive: !!agentSurface.alive,
1537
+ });
1538
+ }
1539
+ // No WS ack yet — best-effort identity from /auth/me so callers still get a userId/org.
1540
+ const sid = base.sessionId || getBootstrapSessionId();
1541
+ let me = null;
1542
+ if (sid) {
1543
+ try {
1544
+ const res = await fetch(`${server}/auth/me`, { headers: { Cookie: `gc_session=${sid}` } });
1545
+ if (res.ok) me = await res.json();
1546
+ } catch { /* not yet authenticated */ }
1547
+ }
1548
+ return ok({
1549
+ ...base,
1550
+ userId: me?.userId ?? null,
1551
+ orgId: me?.currentOrg?.id ?? null,
1552
+ projectId: getState().projectId ?? null,
1553
+ name: null,
1554
+ emoji: null,
1555
+ surfaced: false,
1556
+ alive: false,
1557
+ });
1558
+ } catch (error) { return err(error); }
1559
+ });
1560
+
1492
1561
  // ── Project management tools (direct HTTP) ────────────────────────
1493
1562
 
1494
1563
  tool('project', 'START HERE for project management. Dispatch by `action`: list (lists all projects across all orgs — always call first), open (switch the active project; required before reading/writing frames), create (new project, optionally from a template), update (change name/folder/description/layers), move (transfer to another org). Opening a project auto-switches the org. To change orgs WITHOUT a project (for wiki/skill work in an empty org), use get_org(action="switch", orgId=...). **Skill gate:** projects with attached skills will REJECT all mutations (write, edit, mv, rm, shape, group, connector, layout, layer, asset upload) until you have loaded each attached skill via skill(action="load"). Skills tell you HOW to do the work — they\'re not optional. Open returns the attached skill list and auto-inlines content for projects with ≤3 skills.', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.11.38",
3
+ "version": "1.12.0",
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": [