drafted 1.19.52 → 1.19.54

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 +18 -16
  2. package/package.json +14 -4
package/mcp/server.mjs CHANGED
@@ -174,7 +174,7 @@ export function textFromLocalFile(filePath) {
174
174
  // validation, and every handler stay byte-for-byte unchanged.
175
175
  const REMOTE_JSON_STRING_PARAMS = {
176
176
  fs: ['ops', 'state'],
177
- minion: ['target', 'checklist', 'output'],
177
+ minion: ['target', 'checklist', 'output', 'context'],
178
178
  trigger: ['payload'],
179
179
  };
180
180
 
@@ -466,7 +466,7 @@ const TOOL_ANNOTATIONS = {
466
466
  screenshot: { title: 'Screenshot', readOnlyHint: true, destructiveHint: false, openWorldHint: false, description: 'Render a PNG via headless browser. `scope=frame` for a single frame, `scope=canvas` for a region of the project surface.' },
467
467
 
468
468
  // Minions — checklist-driven intake surfaces bound to a project
469
- minion: { title: 'Minions', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Manage Minions: checklist-driven intake surfaces that guide a consumer through a checklist (via a shareable /c/<slug>-<token> link) and write a producible into the project. Dispatch by `action`: meta, list, get, create, update, enable, disable, delete. QA your own Minions with test_start/test_say/test_resolve — drive the checklist conversation yourself (works even when disabled). Requires the agent allowlist.' },
469
+ minion: { title: 'Minions', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Manage Minions: Drafted\'s agent for people OUTSIDE the org (bidders, clients, suppliers). A visitor opens a shareable /c/<slug>-<token> link, the Minion guides them through a checklist, writes ONE submission record into the project, and on submit marks it needs_review and queues it on output.handoff (a queue trigger) for YOUR agent to claim with trigger pending/claim/complete. A Minion never sends, shares or contacts anyone — outbound follow-up is your job. Everything in a Minion\'s config and its context pack is visible to anyone holding the link. Dispatch by `action`: meta, list, get, create, update, enable, disable, delete. QA your own Minions with test_start/test_say (works even when disabled). Requires the agent allowlist.' },
470
470
  trigger: { title: 'Inbound triggers', readOnlyHint: false, destructiveHint: true, openWorldHint: true, description: 'Manage inbound webhook triggers for the ACTIVE PROJECT: an external system (AppSheet bot, GitHub, form tool) POSTs to the trigger URL and the server runs an agent conversation in the project from the stored prompt template + payload. Dispatch by `action`: create (returns URL + secret token ONCE — relay it to the user immediately, not retrievable later), list, update (enable/disable, edit template, daily limit, executor), rotate (new token), test (fire a synthetic delivery), deliveries (audit log), delete; for executor="queue" triggers, pending/claim/complete let a LOCAL agent poll and work queued deliveries. Requires the agent allowlist.' },
471
471
  fs: { title: 'Filesystem', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Navigate Drafted like a local filesystem: /wiki/<path> pages, /skills/<slug> procedures, /projects/<folder?>/<project>/<layer>/<lane>/<file> frames. Verbs: ls, read, write, edit, mv, rm, search, link, unlink, links.' },
472
472
  // Google Drive mirror triage. Mutating (resolve/retry_all/sync rewrite frame
@@ -479,7 +479,7 @@ function isMutatingToolCall(name, args = {}) {
479
479
  const action = args?.action;
480
480
  switch (name) {
481
481
  case 'minion':
482
- return ['create', 'update', 'enable', 'disable', 'delete', 'test_start', 'test_say', 'test_resolve'].includes(action);
482
+ return ['create', 'update', 'enable', 'disable', 'delete', 'test_start', 'test_say'].includes(action);
483
483
  case 'trigger':
484
484
  return ['create', 'update', 'rotate', 'delete'].includes(action);
485
485
  case 'fs':
@@ -2878,8 +2878,8 @@ tool('tour', {
2878
2878
  // re-pointed at content the human never read. See SECURITY.md.
2879
2879
  tool('action', {
2880
2880
  action: z.enum(['propose', 'list', 'read']).describe('propose: ask a human to authorise one outward effect, or a chain of them approved together. list: your org\'s pending proposals. read: one chain with every payload as the approver will see it.'),
2881
- type: z.string().optional().describe('[propose] One of: share_project, share_frame, change_share_role, create_public_frame_link, create_public_lane_link, comment_mention. Anything else is refused — the list is fixed in source, not data.'),
2882
- payload: z.string().optional().describe('[propose] JSON object for this type. share_project {projectId,email,role}; share_frame {frameId,email,role}; change_share_role {shareId,role}; create_public_frame_link {frameId}; create_public_lane_link {projectId,layer,lane}; comment_mention {frameId,body,email}. Optional expiresInHours on the share types.'),
2881
+ type: z.string().optional().describe('[propose] One of: share_project, share_frame, change_share_role, create_public_frame_link, create_public_lane_link, comment_mention, send_email. Anything else is refused — the list is fixed in source, not data.'),
2882
+ payload: z.string().optional().describe('[propose] JSON object for this type. share_project {projectId,email,role}; share_frame {frameId,email,role}; change_share_role {shareId,role}; create_public_frame_link {frameId}; create_public_lane_link {projectId,layer,lane}; comment_mention {frameId,body,email}; send_email {from,to,subject,body} (plain text, sent AS you from your own Gmail connected in this workspace; `from` is that address; only you can approve it). Optional expiresInHours on the share types.'),
2883
2883
  actions: z.string().optional().describe('[propose] JSON array of {type,payload} to approve TOGETHER as one chain — e.g. share with three people, then comment tagging them. One screen, one decision. Execution is sequential and stops at the first failure; a sent email cannot be rolled back, so it is never all-or-nothing.'),
2884
2884
  groupId: z.string().optional().describe('[read] The chain to read.'),
2885
2885
  reason: z.string().optional().describe('[propose] One line telling the approver WHY. They see it next to what the action does.'),
@@ -3152,12 +3152,10 @@ function ensureSkillInstallIgnored(dir) {
3152
3152
 
3153
3153
 
3154
3154
  tool('minion', {
3155
- action: z.enum(['meta', 'list', 'get', 'create', 'update', 'enable', 'disable', 'delete', 'test_start', 'test_say', 'test_resolve']).describe('Operation to perform. test_* drive a QA conversation against a Minion you own (even disabled) to verify it end-to-end.'),
3155
+ action: z.enum(['meta', 'list', 'get', 'create', 'update', 'enable', 'disable', 'delete', 'test_start', 'test_say']).describe('Operation to perform. test_* drive a QA conversation against a Minion you own (even disabled) to verify it end-to-end.'),
3156
3156
  id: z.string().optional().describe('[get|update|enable|disable|delete|test_*] Minion ID (UUID)'),
3157
3157
  text: z.string().optional().describe('[test_say] the consumer message to send to your test run'),
3158
3158
  fresh: z.boolean().optional().describe('[test_start] start a brand-new run instead of resuming your latest'),
3159
- actionId: z.string().optional().describe('[test_resolve] id of the pending action to resolve'),
3160
- approve: z.boolean().optional().describe('[test_resolve] approve (default true) or reject the pending action'),
3161
3159
  projectId: z.string().optional().describe('[meta|list|create] the project this Minion belongs to — REQUIRED, as a path (/projects/<name> or /projects/<folder>/<name>), slug, or UUID. Minions are project-scoped: they write producibles into that project\'s surface. No session fallback — name the project.'),
3162
3160
  name: z.string().optional().describe('[create|update] Minion name'),
3163
3161
  description: z.string().optional().describe('[create|update] one-line description shown to the consumer'),
@@ -3186,8 +3184,15 @@ tool('minion', {
3186
3184
  sheet: z.string().describe('existing Google Sheet frame path or id to append rows to'),
3187
3185
  tab: z.string().optional().describe('sheet tab/range name to append to, e.g. "Erfassung" (defaults to Sheet1)'),
3188
3186
  columns: z.array(z.string()).optional().describe('columns in order; a link to the produced record is included'),
3189
- }).optional().describe('also append one row per submission to an existing sheet, linking the produced record'),
3187
+ }).optional().describe('one row per submission in an existing sheet. With a handoff the row travels in the handoff payload for your agent to append; without one the Minion appends it (raw values, no read access)'),
3188
+ handoff: z.object({
3189
+ triggerId: z.string().describe('a trigger with executor "queue" in the SAME project'),
3190
+ }).optional().describe('where a submitted record is queued for your connected agent to claim (trigger pending/claim/complete); the payload is marked trust:"untrusted-external"'),
3190
3191
  }).optional().describe('[create|update] where/how the producible lands'),
3192
+ context: z.object({
3193
+ frames: z.array(z.string()).describe('frame ids in the SAME project, max 20'),
3194
+ }).optional().describe('[create|update] the brief pack the Minion may read and answer from (read_brief). DISCLOSED to every visitor — include only what they may see. The Minion cannot read anything else in the org.'),
3195
+ visitorDailyLimit: z.number().optional().describe('[create|update] turns per visitor per UTC day (default 30); dailyLimit is shared by all visitors'),
3191
3196
  limit: z.number().optional().describe('[list] max results per page (default 25, max 100)'),
3192
3197
  offset: z.number().optional().describe('[list] skip N results for pagination (default 0)'),
3193
3198
  compact: z.boolean().optional().describe('[list] return only {id,slug,name,enabled,projectId} per Minion'),
@@ -3216,20 +3221,22 @@ tool('minion', {
3216
3221
  }
3217
3222
  case 'create': {
3218
3223
  // projectId lives in the BODY (the POST route reads body, ignores the query).
3219
- const { name, description, target, checklist, output, enabled } = args;
3224
+ const { name, description, target, checklist, output, enabled, context, visitorDailyLimit } = args;
3220
3225
  if (!name || !target || !Array.isArray(checklist) || !output) {
3221
3226
  throw new Error('name, target, checklist[], and output are required for action=create');
3222
3227
  }
3223
3228
  const body = { projectId: project.id, name, target, checklist, output };
3224
3229
  if (description !== undefined) body.description = description;
3225
3230
  if (enabled !== undefined) body.enabled = enabled;
3231
+ if (context !== undefined) body.context = context;
3232
+ if (visitorDailyLimit !== undefined) body.visitorDailyLimit = visitorDailyLimit;
3226
3233
  const created = await scoped(() => api('POST', '/api/minions', body));
3227
3234
  return ok({ ...created, projectPath });
3228
3235
  }
3229
3236
  case 'update': {
3230
3237
  if (!args.id) throw new Error('id is required for action=update');
3231
3238
  const body = {};
3232
- for (const k of ['name', 'description', 'target', 'checklist', 'output', 'enabled']) {
3239
+ for (const k of ['name', 'description', 'target', 'checklist', 'output', 'enabled', 'context', 'visitorDailyLimit']) {
3233
3240
  if (args[k] !== undefined) body[k] = args[k];
3234
3241
  }
3235
3242
  if (Object.keys(body).length === 0) throw new Error('At least one field is required for action=update');
@@ -3253,11 +3260,6 @@ tool('minion', {
3253
3260
  if (!args.text) throw new Error('text is required for action=test_say');
3254
3261
  return ok(await api('POST', `/api/minions/${args.id}/test-message`, { content: args.text }));
3255
3262
  }
3256
- case 'test_resolve': {
3257
- if (!args.id) throw new Error('id is required for action=test_resolve');
3258
- if (!args.actionId) throw new Error('actionId is required for action=test_resolve');
3259
- return ok(await api('POST', `/api/minions/${args.id}/test-resolve`, { actionId: args.actionId, approve: args.approve !== false }));
3260
- }
3261
3263
  default:
3262
3264
  throw new Error(`Unknown minion action: ${action}`);
3263
3265
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.19.52",
3
+ "version": "1.19.54",
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": [
@@ -63,19 +63,29 @@
63
63
  "lucide": "^1.16.0",
64
64
  "mdast-util-from-markdown": "^2.0.3",
65
65
  "multer": "^2.1.1",
66
- "nodemailer": "^8.0.2",
66
+ "nodemailer": "^10.0.13",
67
67
  "parse5": "^8.0.1",
68
68
  "pg": "^8.20.0",
69
69
  "playwright": "^1.58.2",
70
- "puppeteer": "^24.37.5",
70
+ "puppeteer": "^25.12.0",
71
71
  "qrcode-terminal": "^0.12.0",
72
72
  "react": "^18.3.1",
73
73
  "react-dom": "^18.3.1",
74
- "sharp": "^0.34.5",
74
+ "sharp": "^0.35.5",
75
75
  "ws": "^8.21.3",
76
76
  "yaml": "^2.8.3",
77
77
  "zod": "^4.3.6"
78
78
  },
79
+ "overrides": {
80
+ "@excalidraw/excalidraw": {
81
+ "nanoid": "^3.3.19"
82
+ },
83
+ "@excalidraw/mermaid-to-excalidraw": {
84
+ "nanoid": "^5.1.16"
85
+ },
86
+ "immutable": "^4.3.9",
87
+ "lodash-es": "^4.18.1"
88
+ },
79
89
  "keywords": [
80
90
  "design",
81
91
  "canvas",