drafted 1.11.31 → 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 +37 -0
- package/install-mcp.sh +2 -4
- package/mcp/server.mjs +45 -7
- package/package.json +1 -1
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
|
|
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
|
-
<
|
|
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':
|
|
@@ -709,8 +709,14 @@ function schedulePendingAuthPoll(delayMs = 2000) {
|
|
|
709
709
|
// session before any operation runs.
|
|
710
710
|
function getAuthHeaders() {
|
|
711
711
|
const sid = getState().sessionId;
|
|
712
|
-
|
|
713
|
-
|
|
712
|
+
const headers = {};
|
|
713
|
+
if (sid) headers.Cookie = `gc_session=${sid}`;
|
|
714
|
+
// Stable human name for this agent (DRAFTED_AGENT_NAME), so the server names this
|
|
715
|
+
// session's surface tab by the AGENT, not the project — lets the user tell concurrent
|
|
716
|
+
// agents apart even in the same project.
|
|
717
|
+
const agentName = (process.env.DRAFTED_AGENT_NAME || '').trim();
|
|
718
|
+
if (agentName) headers['X-Drafted-Agent'] = agentName;
|
|
719
|
+
return headers;
|
|
714
720
|
}
|
|
715
721
|
|
|
716
722
|
// Mint a fresh per-instance child session from the shared root login and bind it
|
|
@@ -724,9 +730,15 @@ async function cloneSession() {
|
|
|
724
730
|
if (!bootstrapId) return false;
|
|
725
731
|
|
|
726
732
|
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();
|
|
727
738
|
const res = await fetch(`${getServerUrl()}/auth/session/clone`, {
|
|
728
739
|
method: 'POST',
|
|
729
|
-
headers: { Cookie: `gc_session=${bootstrapId}` },
|
|
740
|
+
headers: { 'Content-Type': 'application/json', Cookie: `gc_session=${bootstrapId}` },
|
|
741
|
+
body: JSON.stringify({ agentKey }),
|
|
730
742
|
});
|
|
731
743
|
if (res.ok) {
|
|
732
744
|
const data = await res.json();
|
|
@@ -1033,6 +1045,9 @@ async function connectAgentWs() {
|
|
|
1033
1045
|
|
|
1034
1046
|
agentWs.on('open', () => {
|
|
1035
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 {}
|
|
1036
1051
|
if (getState().projectId) {
|
|
1037
1052
|
agentWs.send(JSON.stringify({ type: 'join', projectId: getState().projectId, agent: true, agentLabel: getAgentLabel() }));
|
|
1038
1053
|
}
|
|
@@ -3651,8 +3666,12 @@ function collectorGateError(e) {
|
|
|
3651
3666
|
}
|
|
3652
3667
|
|
|
3653
3668
|
tool('collector', {
|
|
3654
|
-
action: z.enum(['meta', 'list', 'get', 'create', 'update', 'enable', 'disable', 'delete']).describe('Operation to perform.'),
|
|
3655
|
-
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'),
|
|
3656
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.'),
|
|
3657
3676
|
name: z.string().optional().describe('[create|update] collector name'),
|
|
3658
3677
|
description: z.string().optional().describe('[create|update] one-line description shown to the consumer'),
|
|
@@ -3676,6 +3695,11 @@ tool('collector', {
|
|
|
3676
3695
|
filenameTemplate: z.string().optional().describe('e.g. "<id>.md"'),
|
|
3677
3696
|
skillSlug: z.string().optional().describe('skill that shapes the produced frame'),
|
|
3678
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'),
|
|
3679
3703
|
}).optional().describe('[create|update] where/how the producible lands'),
|
|
3680
3704
|
limit: z.number().optional().describe('[list] max results per page (default 25, max 100)'),
|
|
3681
3705
|
offset: z.number().optional().describe('[list] skip N results for pagination (default 0)'),
|
|
@@ -3733,6 +3757,20 @@ tool('collector', {
|
|
|
3733
3757
|
if (!args.id) throw new Error('id is required for action=delete');
|
|
3734
3758
|
return ok(await api('DELETE', `/api/collectors/${args.id}`));
|
|
3735
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
|
+
}
|
|
3736
3774
|
default:
|
|
3737
3775
|
throw new Error(`Unknown collector action: ${action}`);
|
|
3738
3776
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drafted",
|
|
3
|
-
"version": "1.11.
|
|
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": [
|