drafted 1.11.32 → 1.11.33

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.
package/cli/drafted.mjs CHANGED
@@ -2014,6 +2014,43 @@ collectorCmd
2014
2014
  .option('--format <fmt>', 'output format: json or text', 'text')
2015
2015
  .action((id, opts) => collectorSetEnabled(id, false, opts.format));
2016
2016
 
2017
+ async function collectorTestPost(id, path, body, format) {
2018
+ requireLogin();
2019
+ const server = getServerUrl().replace(/\/$/, '');
2020
+ const res = await authFetch(`${server}/api/collectors/${encodeURIComponent(id)}/${path}`, {
2021
+ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}),
2022
+ });
2023
+ const data = await res.json().catch(() => ({}));
2024
+ if (!res.ok) {
2025
+ if (format === 'json') console.log(JSON.stringify({ status: res.status === 404 ? 'not-found' : 'error', error: data.error || `HTTP ${res.status}` }));
2026
+ else console.log(['error', data.error || `HTTP ${res.status}`].join('\t'));
2027
+ process.exit(1);
2028
+ }
2029
+ console.log(JSON.stringify(data));
2030
+ }
2031
+
2032
+ collectorCmd
2033
+ .command('test-start <id>')
2034
+ .description('QA: start (or resume) a test run of a collector you own — works even when disabled. --fresh for a new run.')
2035
+ .option('--fresh', 'start a brand-new run instead of resuming', false)
2036
+ .option('--format <fmt>', 'output format: json (default)', 'json')
2037
+ .action((id, opts) => collectorTestPost(id, 'test-start', { fresh: !!opts.fresh }, opts.format));
2038
+
2039
+ collectorCmd
2040
+ .command('test-say <id>')
2041
+ .description('QA: send a text answer to your test run; returns the agent reply, checklist state, pending actions.')
2042
+ .requiredOption('--text <text>', 'the consumer message to send')
2043
+ .option('--format <fmt>', 'output format: json (default)', 'json')
2044
+ .action((id, opts) => collectorTestPost(id, 'test-message', { content: opts.text }, opts.format));
2045
+
2046
+ collectorCmd
2047
+ .command('test-resolve <id>')
2048
+ .description('QA: approve or reject a pending destructive action in your test run.')
2049
+ .requiredOption('--action <actionId>', 'the pending action id')
2050
+ .option('--reject', 'reject instead of approve', false)
2051
+ .option('--format <fmt>', 'output format: json (default)', 'json')
2052
+ .action((id, opts) => collectorTestPost(id, 'test-resolve', { actionId: opts.action, approve: !opts.reject }, opts.format));
2053
+
2017
2054
  collectorCmd
2018
2055
  .command('delete <id>')
2019
2056
  .description('Delete a collector (past submissions are kept as history)')
package/install-mcp.sh CHANGED
@@ -664,14 +664,12 @@ install_desktop_app() {
664
664
  <string>live.drafted.desktop</string>
665
665
  <key>ProgramArguments</key>
666
666
  <array>
667
- <string>open</string>
668
- <string>-a</string>
669
- <string>$app_bundle</string>
667
+ <string>$app_exe</string>
670
668
  </array>
671
669
  <key>RunAtLoad</key>
672
670
  <true/>
673
671
  <key>KeepAlive</key>
674
- <false/>
672
+ <true/>
675
673
  </dict>
676
674
  </plist>
677
675
  PLIST
package/mcp/server.mjs CHANGED
@@ -249,7 +249,7 @@ const TOOL_ANNOTATIONS = {
249
249
  wiki: { title: 'Wiki', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Per-org wiki. Markdown pages with paths as hierarchy. Dispatch by `action`.' },
250
250
 
251
251
  // Collectors — checklist-driven, Minion-run intake surfaces bound to a project
252
- collector: { title: 'Collectors', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Manage Collectors: checklist-driven intake surfaces that guide a consumer through a checklist (via a shareable /c/<slug> link) and then write a producible into the project. Dispatch by `action`: meta (discover layers/lanes/frames), list, get, create, update, enable, disable, delete. Requires the agent allowlist (same gate as Minion).' },
252
+ collector: { title: 'Collectors', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Manage Collectors: checklist-driven intake surfaces that guide a consumer through a checklist (via a shareable /c/<slug> link) and then write a producible into the project. Dispatch by `action`: meta (discover layers/lanes/frames), list, get, create, update, enable, disable, delete. QA your own collectors with test_start/test_say/test_resolve — drive the checklist conversation yourself (works even when disabled) and verify it produces the right Doc/Sheet output. Requires the agent allowlist (same gate as Minion).' },
253
253
  };
254
254
 
255
255
  function isMutatingToolCall(name, args = {}) {
@@ -276,7 +276,7 @@ function isMutatingToolCall(name, args = {}) {
276
276
  case 'wiki':
277
277
  return ['log', 'write', 'edit', 'mv', 'rm', 'source-register', 'bulk-write'].includes(action);
278
278
  case 'collector':
279
- return ['create', 'update', 'enable', 'disable', 'delete'].includes(action);
279
+ return ['create', 'update', 'enable', 'disable', 'delete', 'test_start', 'test_say', 'test_resolve'].includes(action);
280
280
  case 'rm':
281
281
  case 'shape':
282
282
  case 'group':
@@ -1045,6 +1045,9 @@ async function connectAgentWs() {
1045
1045
 
1046
1046
  agentWs.on('open', () => {
1047
1047
  console.error('[MCP-WS] Connected');
1048
+ // Announce presence immediately so the agent surfaces (greyed/idle, no project) the
1049
+ // moment it connects — before opening any project. Project work later flips it active.
1050
+ try { agentWs.send(JSON.stringify({ type: 'agent-hello', agentLabel: getAgentLabel() })); } catch {}
1048
1051
  if (getState().projectId) {
1049
1052
  agentWs.send(JSON.stringify({ type: 'join', projectId: getState().projectId, agent: true, agentLabel: getAgentLabel() }));
1050
1053
  }
@@ -3663,8 +3666,12 @@ function collectorGateError(e) {
3663
3666
  }
3664
3667
 
3665
3668
  tool('collector', {
3666
- action: z.enum(['meta', 'list', 'get', 'create', 'update', 'enable', 'disable', 'delete']).describe('Operation to perform.'),
3667
- id: z.string().optional().describe('[get|update|enable|disable|delete] collector ID (UUID)'),
3669
+ 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 collector you own (even disabled) to verify it end-to-end.'),
3670
+ id: z.string().optional().describe('[get|update|enable|disable|delete|test_*] collector ID (UUID)'),
3671
+ text: z.string().optional().describe('[test_say] the consumer message to send to your test run'),
3672
+ fresh: z.boolean().optional().describe('[test_start] start a brand-new run instead of resuming your latest'),
3673
+ actionId: z.string().optional().describe('[test_resolve] id of the pending action to resolve'),
3674
+ approve: z.boolean().optional().describe('[test_resolve] approve (default true) or reject the pending action'),
3668
3675
  projectId: z.string().optional().describe('[create|meta] project to bind/scope to (defaults to the active project). Operates on the session’s active org — open the target project first via project(action="open"), or get_org(action="switch") to change org.'),
3669
3676
  name: z.string().optional().describe('[create|update] collector name'),
3670
3677
  description: z.string().optional().describe('[create|update] one-line description shown to the consumer'),
@@ -3688,6 +3695,11 @@ tool('collector', {
3688
3695
  filenameTemplate: z.string().optional().describe('e.g. "<id>.md"'),
3689
3696
  skillSlug: z.string().optional().describe('skill that shapes the produced frame'),
3690
3697
  grouping: z.string().optional().describe('"lane" gives each submission its own lane'),
3698
+ format: z.enum(['markdown', 'google-doc', 'google-sheet', 'google-slide']).optional().describe('primary producible format; google-* require the org Drive connected (default markdown)'),
3699
+ register: z.object({
3700
+ sheet: z.string().describe('existing Google Sheet frame path or id to append rows to'),
3701
+ columns: z.array(z.string()).optional().describe('columns in order; a link to the produced record is included'),
3702
+ }).optional().describe('also append one row per submission to an existing sheet, linking the produced record'),
3691
3703
  }).optional().describe('[create|update] where/how the producible lands'),
3692
3704
  limit: z.number().optional().describe('[list] max results per page (default 25, max 100)'),
3693
3705
  offset: z.number().optional().describe('[list] skip N results for pagination (default 0)'),
@@ -3745,6 +3757,20 @@ tool('collector', {
3745
3757
  if (!args.id) throw new Error('id is required for action=delete');
3746
3758
  return ok(await api('DELETE', `/api/collectors/${args.id}`));
3747
3759
  }
3760
+ case 'test_start': {
3761
+ if (!args.id) throw new Error('id is required for action=test_start');
3762
+ return ok(await api('POST', `/api/collectors/${args.id}/test-start`, { fresh: !!args.fresh }));
3763
+ }
3764
+ case 'test_say': {
3765
+ if (!args.id) throw new Error('id is required for action=test_say');
3766
+ if (!args.text) throw new Error('text is required for action=test_say');
3767
+ return ok(await api('POST', `/api/collectors/${args.id}/test-message`, { content: args.text }));
3768
+ }
3769
+ case 'test_resolve': {
3770
+ if (!args.id) throw new Error('id is required for action=test_resolve');
3771
+ if (!args.actionId) throw new Error('actionId is required for action=test_resolve');
3772
+ return ok(await api('POST', `/api/collectors/${args.id}/test-resolve`, { actionId: args.actionId, approve: args.approve !== false }));
3773
+ }
3748
3774
  default:
3749
3775
  throw new Error(`Unknown collector action: ${action}`);
3750
3776
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.11.32",
3
+ "version": "1.11.33",
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": [