@dalmasonto/taskflow-mcp 1.0.35 → 2.1.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 (101) hide show
  1. package/README.md +138 -193
  2. package/dist/attachment-download.d.ts +74 -0
  3. package/dist/attachment-download.js +193 -0
  4. package/dist/attachment-download.js.map +1 -0
  5. package/dist/attachments.d.ts +23 -0
  6. package/dist/attachments.js +66 -0
  7. package/dist/attachments.js.map +1 -0
  8. package/dist/client.d.ts +206 -0
  9. package/dist/client.js +301 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/config.d.ts +137 -18
  12. package/dist/config.js +187 -106
  13. package/dist/config.js.map +1 -0
  14. package/dist/connect.d.ts +89 -0
  15. package/dist/connect.js +269 -0
  16. package/dist/connect.js.map +1 -0
  17. package/dist/doctor.d.ts +24 -0
  18. package/dist/doctor.js +120 -0
  19. package/dist/doctor.js.map +1 -0
  20. package/dist/events.d.ts +208 -0
  21. package/dist/events.js +454 -0
  22. package/dist/events.js.map +1 -0
  23. package/dist/index.d.ts +7 -1
  24. package/dist/index.js +121 -218
  25. package/dist/index.js.map +1 -0
  26. package/dist/instructions.d.ts +12 -0
  27. package/dist/instructions.js +114 -0
  28. package/dist/instructions.js.map +1 -0
  29. package/dist/mint.d.ts +62 -0
  30. package/dist/mint.js +135 -0
  31. package/dist/mint.js.map +1 -0
  32. package/dist/mirror.d.ts +68 -0
  33. package/dist/mirror.js +103 -0
  34. package/dist/mirror.js.map +1 -0
  35. package/dist/pane-queue.d.ts +29 -0
  36. package/dist/pane-queue.js +35 -0
  37. package/dist/pane-queue.js.map +1 -0
  38. package/dist/prompts.d.ts +79 -0
  39. package/dist/prompts.js +211 -0
  40. package/dist/prompts.js.map +1 -0
  41. package/dist/resolve.d.ts +72 -0
  42. package/dist/resolve.js +89 -0
  43. package/dist/resolve.js.map +1 -0
  44. package/dist/runtime.d.ts +54 -0
  45. package/dist/runtime.js +339 -0
  46. package/dist/runtime.js.map +1 -0
  47. package/dist/server.d.ts +56 -0
  48. package/dist/server.js +793 -0
  49. package/dist/server.js.map +1 -0
  50. package/dist/session-identifier.d.ts +48 -0
  51. package/dist/session-identifier.js +44 -0
  52. package/dist/session-identifier.js.map +1 -0
  53. package/dist/sessions-store.d.ts +38 -0
  54. package/dist/sessions-store.js +88 -0
  55. package/dist/sessions-store.js.map +1 -0
  56. package/dist/tmux.d.ts +200 -0
  57. package/dist/tmux.js +580 -0
  58. package/dist/tmux.js.map +1 -0
  59. package/hooks/metadata.mjs +99 -0
  60. package/hooks/permission-prompt.mjs +100 -0
  61. package/hooks/taskflow-hook.mjs +499 -0
  62. package/hooks/tool-logging.mjs +63 -0
  63. package/package.json +38 -29
  64. package/dist/agent-registry.d.ts +0 -28
  65. package/dist/agent-registry.js +0 -158
  66. package/dist/db.d.ts +0 -5
  67. package/dist/db.js +0 -220
  68. package/dist/helpers.d.ts +0 -21
  69. package/dist/helpers.js +0 -27
  70. package/dist/resources.d.ts +0 -2
  71. package/dist/resources.js +0 -89
  72. package/dist/retry.d.ts +0 -34
  73. package/dist/retry.js +0 -94
  74. package/dist/sse.d.ts +0 -10
  75. package/dist/sse.js +0 -824
  76. package/dist/tmux-bridge.d.ts +0 -13
  77. package/dist/tmux-bridge.js +0 -217
  78. package/dist/tools/activity.d.ts +0 -39
  79. package/dist/tools/activity.js +0 -152
  80. package/dist/tools/agent-inbox.d.ts +0 -12
  81. package/dist/tools/agent-inbox.js +0 -272
  82. package/dist/tools/agent.d.ts +0 -14
  83. package/dist/tools/agent.js +0 -168
  84. package/dist/tools/analytics.d.ts +0 -21
  85. package/dist/tools/analytics.js +0 -191
  86. package/dist/tools/checkpoint.d.ts +0 -27
  87. package/dist/tools/checkpoint.js +0 -105
  88. package/dist/tools/notifications.d.ts +0 -31
  89. package/dist/tools/notifications.js +0 -59
  90. package/dist/tools/projects.d.ts +0 -55
  91. package/dist/tools/projects.js +0 -112
  92. package/dist/tools/settings.d.ts +0 -19
  93. package/dist/tools/settings.js +0 -73
  94. package/dist/tools/tasks.d.ts +0 -105
  95. package/dist/tools/tasks.js +0 -403
  96. package/dist/tools/terminal.d.ts +0 -4
  97. package/dist/tools/terminal.js +0 -98
  98. package/dist/tools/timer.d.ts +0 -37
  99. package/dist/tools/timer.js +0 -154
  100. package/dist/types.d.ts +0 -83
  101. package/dist/types.js +0 -30
@@ -1,98 +0,0 @@
1
- import { z } from 'zod';
2
- import { execFileSync } from 'child_process';
3
- import { getDb } from '../db.js';
4
- import { logActivity, errorResponse, successResponse } from '../helpers.js';
5
- const MAX_KEYS_LENGTH = 200;
6
- // Patterns that should never be sent via remote key injection
7
- const BLOCKED_PATTERNS = [
8
- /^\s*!/, // Claude Code shell escape (! command)
9
- /;\s*!/, // shell escape after semicolon
10
- ];
11
- /** Validate keys before sending — blocks shell escape patterns */
12
- export function validateKeys(keys) {
13
- if (keys.length > MAX_KEYS_LENGTH) {
14
- return `Keys too long (${keys.length} chars, max ${MAX_KEYS_LENGTH}) — potential injection`;
15
- }
16
- for (const pattern of BLOCKED_PATTERNS) {
17
- if (pattern.test(keys)) {
18
- return 'Blocked: shell escape pattern detected (! prefix). Use the terminal directly for shell commands.';
19
- }
20
- }
21
- return null; // valid
22
- }
23
- function getAgentPane(agentName, requireConnected = false) {
24
- const db = getDb();
25
- const agent = db.prepare('SELECT * FROM agent_registry WHERE name = ?').get(agentName);
26
- if (!agent)
27
- return { error: errorResponse(`Agent "${agentName}" not found`, 'NOT_FOUND') };
28
- if (!agent.tmux_pane)
29
- return { error: errorResponse(`Agent "${agentName}" has no tmux pane`, 'VALIDATION_ERROR') };
30
- if (requireConnected && agent.status !== 'connected') {
31
- return { error: errorResponse(`Agent "${agentName}" is disconnected — sending keys to a bare shell is blocked for security`, 'VALIDATION_ERROR') };
32
- }
33
- return { agent };
34
- }
35
- export function registerTerminalTools(server) {
36
- server.tool('capture_terminal', 'Capture the current terminal content of an agent\'s tmux pane. Returns the visible text on screen. Useful for seeing what prompts or output an agent is displaying.', {
37
- agent_name: z.string().describe('Name of the agent whose terminal to capture'),
38
- }, { readOnlyHint: true }, async (params) => {
39
- const result = getAgentPane(params.agent_name);
40
- if (result.error)
41
- return result.error;
42
- try {
43
- const output = execFileSync('tmux', ['capture-pane', '-p', '-S', '-', '-t', result.agent.tmux_pane], { timeout: 5000 }).toString();
44
- return successResponse({
45
- agent: params.agent_name,
46
- pane: result.agent.tmux_pane,
47
- content: output,
48
- });
49
- }
50
- catch (err) {
51
- return errorResponse(`Failed to capture pane: ${err.message}`, 'VALIDATION_ERROR');
52
- }
53
- });
54
- server.tool('send_keys', 'Send raw keystrokes to an agent\'s tmux pane. Use this to respond to interactive prompts (yes/no, numbered choices, permission approvals) displayed in an agent\'s terminal. By default appends Enter after the keys.', {
55
- agent_name: z.string().describe('Name of the agent whose terminal to send keys to'),
56
- keys: z.string().describe('The keys/text to send (e.g. "yes", "1", "y") or tmux key names (e.g. "Escape", "Up", "Down", "BTab")'),
57
- enter: z.boolean().optional().describe('Whether to press Enter after the keys (default: true)'),
58
- literal: z.boolean().optional().describe('Send as literal text with -l flag (default: true). Set false for tmux key names like Escape, Up, Down, Left, Right, BTab'),
59
- }, { destructiveHint: true }, async (params) => {
60
- const result = getAgentPane(params.agent_name, true);
61
- if (result.error)
62
- return result.error;
63
- // Validate keys for injection attacks
64
- const violation = validateKeys(params.keys);
65
- if (violation)
66
- return errorResponse(violation, 'VALIDATION_ERROR');
67
- const sendEnter = params.enter !== false;
68
- const isLiteral = params.literal !== false;
69
- try {
70
- const pane = result.agent.tmux_pane;
71
- if (isLiteral) {
72
- // Send as literal text (like typing on a keyboard)
73
- execFileSync('tmux', ['send-keys', '-t', pane, '-l', params.keys], { stdio: 'ignore', timeout: 5000 });
74
- if (sendEnter) {
75
- execFileSync('tmux', ['send-keys', '-t', pane, 'Enter'], { stdio: 'ignore', timeout: 5000 });
76
- }
77
- }
78
- else {
79
- // Send as tmux key names (Escape, Up, Down, etc.)
80
- const args = ['send-keys', '-t', pane, params.keys];
81
- if (sendEnter)
82
- args.push('Enter');
83
- execFileSync('tmux', args, { stdio: 'ignore', timeout: 5000 });
84
- }
85
- logActivity('terminal_send_keys', `Sent keys to ${params.agent_name}: ${params.keys.slice(0, 50)}`, { entityType: 'agent' });
86
- return successResponse({
87
- agent: params.agent_name,
88
- pane: result.agent.tmux_pane,
89
- keys: params.keys,
90
- enter: sendEnter,
91
- sent: true,
92
- });
93
- }
94
- catch (err) {
95
- return errorResponse(`Failed to send keys: ${err.message}`, 'VALIDATION_ERROR');
96
- }
97
- });
98
- }
@@ -1,37 +0,0 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function startTimer(params: {
3
- task_id: number;
4
- }): Promise<{
5
- content: {
6
- type: "text";
7
- text: string;
8
- }[];
9
- }>;
10
- export declare function pauseTimer(params: {
11
- task_id: number;
12
- }): Promise<{
13
- content: {
14
- type: "text";
15
- text: string;
16
- }[];
17
- }>;
18
- export declare function stopTimer(params: {
19
- task_id: number;
20
- final_status?: string;
21
- }): Promise<{
22
- content: {
23
- type: "text";
24
- text: string;
25
- }[];
26
- }>;
27
- export declare function listSessions(params: {
28
- task_id?: number;
29
- start_date?: string;
30
- end_date?: string;
31
- }): Promise<{
32
- content: {
33
- type: "text";
34
- text: string;
35
- }[];
36
- }>;
37
- export declare function registerTimerTools(server: McpServer): void;
@@ -1,154 +0,0 @@
1
- import { z } from 'zod';
2
- import { getDb } from '../db.js';
3
- import { logActivity, errorResponse, successResponse, now, broadcastChange } from '../helpers.js';
4
- import { VALID_TRANSITIONS } from '../types.js';
5
- // ─── exported handler functions ───────────────────────────────────────
6
- export async function startTimer(params) {
7
- const db = getDb();
8
- const task = db.prepare('SELECT id, title, status FROM tasks WHERE id = ?').get(params.task_id);
9
- if (!task)
10
- return errorResponse('Task not found', 'NOT_FOUND');
11
- // Check no open session exists
12
- const openSession = db.prepare('SELECT id FROM sessions WHERE task_id = ? AND end IS NULL').get(params.task_id);
13
- if (openSession)
14
- return errorResponse('A timer session is already active for this task', 'SESSION_ALREADY_ACTIVE');
15
- // Check valid transition to in_progress
16
- const currentStatus = task.status;
17
- const allowed = VALID_TRANSITIONS[currentStatus];
18
- if (!allowed || !allowed.includes('in_progress')) {
19
- return errorResponse(`Cannot transition from "${currentStatus}" to "in_progress"`, 'INVALID_TRANSITION');
20
- }
21
- const ts = now();
22
- // Create session
23
- const result = db.prepare('INSERT INTO sessions (task_id, start, end) VALUES (?, ?, ?)').run(params.task_id, ts, null);
24
- // Update task status to in_progress
25
- db.prepare('UPDATE tasks SET status = ?, updated_at = ? WHERE id = ?').run('in_progress', ts, params.task_id);
26
- logActivity('timer_started', task.title, { entityType: 'task', entityId: params.task_id });
27
- const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(result.lastInsertRowid);
28
- broadcastChange('timer', 'timer_started', { task_id: params.task_id, session, task_status: 'in_progress' });
29
- return successResponse(session);
30
- }
31
- export async function pauseTimer(params) {
32
- const db = getDb();
33
- const task = db.prepare('SELECT id, title, status FROM tasks WHERE id = ?').get(params.task_id);
34
- if (!task)
35
- return errorResponse('Task not found', 'NOT_FOUND');
36
- // Find open session
37
- const session = db.prepare('SELECT * FROM sessions WHERE task_id = ? AND end IS NULL').get(params.task_id);
38
- const endTime = now();
39
- let duration;
40
- let closedSession;
41
- if (session) {
42
- // Normal path — close the active session
43
- duration = new Date(endTime).getTime() - new Date(session.start).getTime();
44
- db.prepare('UPDATE sessions SET end = ? WHERE id = ?').run(endTime, session.id);
45
- closedSession = { ...session, end: endTime };
46
- }
47
- else {
48
- // Fallback — session already closed (orphan cleanup, auto-close)
49
- const lastSession = db.prepare('SELECT * FROM sessions WHERE task_id = ? ORDER BY start DESC LIMIT 1').get(params.task_id);
50
- if (!lastSession)
51
- return errorResponse('No timer sessions found for this task', 'NO_ACTIVE_SESSION');
52
- duration = new Date(lastSession.end).getTime() - new Date(lastSession.start).getTime();
53
- closedSession = lastSession;
54
- }
55
- // Update task to paused
56
- db.prepare('UPDATE tasks SET status = ?, updated_at = ? WHERE id = ?').run('paused', endTime, params.task_id);
57
- logActivity('timer_paused', task.title, {
58
- detail: `Duration: ${duration}ms`,
59
- entityType: 'task',
60
- entityId: params.task_id,
61
- });
62
- const pausedSession = { ...closedSession, duration };
63
- broadcastChange('timer', 'timer_paused', { task_id: params.task_id, session: pausedSession, task_status: 'paused' });
64
- return successResponse(pausedSession);
65
- }
66
- export async function stopTimer(params) {
67
- const db = getDb();
68
- const task = db.prepare('SELECT id, title, status FROM tasks WHERE id = ?').get(params.task_id);
69
- if (!task)
70
- return errorResponse('Task not found', 'NOT_FOUND');
71
- // Find open session
72
- const session = db.prepare('SELECT * FROM sessions WHERE task_id = ? AND end IS NULL').get(params.task_id);
73
- const finalStatus = params.final_status ?? 'done';
74
- const endTime = now();
75
- let duration;
76
- let closedSession;
77
- if (session) {
78
- // Normal path — close the active session
79
- duration = new Date(endTime).getTime() - new Date(session.start).getTime();
80
- db.prepare('UPDATE sessions SET end = ? WHERE id = ?').run(endTime, session.id);
81
- closedSession = { ...session, end: endTime };
82
- }
83
- else {
84
- // Fallback — session was already closed (e.g. by orphan cleanup or update_task_status)
85
- // Still update the task status so the caller's intent is honored
86
- const lastSession = db.prepare('SELECT * FROM sessions WHERE task_id = ? ORDER BY start DESC LIMIT 1').get(params.task_id);
87
- if (!lastSession)
88
- return errorResponse('No timer sessions found for this task', 'NO_ACTIVE_SESSION');
89
- duration = new Date(lastSession.end).getTime() - new Date(lastSession.start).getTime();
90
- closedSession = lastSession;
91
- }
92
- // Update task to final_status
93
- db.prepare('UPDATE tasks SET status = ?, updated_at = ? WHERE id = ?').run(finalStatus, endTime, params.task_id);
94
- logActivity('timer_stopped', task.title, {
95
- detail: `Duration: ${duration}ms, final status: ${finalStatus}`,
96
- entityType: 'task',
97
- entityId: params.task_id,
98
- });
99
- // Also log status-specific action
100
- if (finalStatus === 'done') {
101
- logActivity('task_completed', task.title, { entityType: 'task', entityId: params.task_id });
102
- }
103
- else if (finalStatus === 'partial_done') {
104
- logActivity('task_partial_done', task.title, { entityType: 'task', entityId: params.task_id });
105
- }
106
- const stoppedSession = { ...closedSession, duration };
107
- broadcastChange('timer', 'timer_stopped', { task_id: params.task_id, session: stoppedSession, task_status: finalStatus });
108
- return successResponse(stoppedSession);
109
- }
110
- export async function listSessions(params) {
111
- const db = getDb();
112
- const conditions = [];
113
- const values = [];
114
- if (params.task_id != null) {
115
- conditions.push('task_id = ?');
116
- values.push(params.task_id);
117
- }
118
- if (params.start_date) {
119
- conditions.push('start >= ?');
120
- values.push(params.start_date);
121
- }
122
- if (params.end_date) {
123
- conditions.push('start <= ?');
124
- values.push(params.end_date);
125
- }
126
- let sql = 'SELECT * FROM sessions';
127
- if (conditions.length > 0) {
128
- sql += ' WHERE ' + conditions.join(' AND ');
129
- }
130
- sql += ' ORDER BY start ASC';
131
- const rows = db.prepare(sql).all(...values);
132
- const nowMs = Date.now();
133
- const sessions = rows.map(row => {
134
- const startMs = new Date(row.start).getTime();
135
- const endMs = row.end ? new Date(row.end).getTime() : nowMs;
136
- const duration = endMs - startMs;
137
- return { ...row, duration };
138
- });
139
- return successResponse(sessions);
140
- }
141
- // ─── MCP registration ─────────────────────────────────────────────────
142
- export function registerTimerTools(server) {
143
- server.tool('start_timer', 'Start a timer session for a task. Call this before beginning work on any task to track focused time. Automatically transitions the task to "in_progress".', { task_id: z.number() }, { readOnlyHint: false }, async (params) => startTimer(params));
144
- server.tool('pause_timer', 'Pause the active timer session for a task. Call when switching context or waiting for user input. The task transitions to "paused".', { task_id: z.number() }, { readOnlyHint: false }, async (params) => pauseTimer(params));
145
- server.tool('stop_timer', 'Stop the active timer and set a final status. Call when finishing work — use "done" if complete, "partial_done" if more remains, "blocked" if stuck.', {
146
- task_id: z.number(),
147
- final_status: z.enum(['done', 'partial_done', 'blocked']).optional(),
148
- }, { readOnlyHint: false }, async (params) => stopTimer(params));
149
- server.tool('list_sessions', 'List timer sessions with optional filters. Use to review time spent on a task or across a date range.', {
150
- task_id: z.number().optional(),
151
- start_date: z.string().optional(),
152
- end_date: z.string().optional(),
153
- }, { readOnlyHint: true }, async (params) => listSessions(params));
154
- }
package/dist/types.d.ts DELETED
@@ -1,83 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const TaskStatus: z.ZodEnum<{
3
- not_started: "not_started";
4
- in_progress: "in_progress";
5
- paused: "paused";
6
- blocked: "blocked";
7
- partial_done: "partial_done";
8
- done: "done";
9
- }>;
10
- export type TaskStatus = z.infer<typeof TaskStatus>;
11
- export declare const TaskPriority: z.ZodEnum<{
12
- low: "low";
13
- medium: "medium";
14
- high: "high";
15
- critical: "critical";
16
- }>;
17
- export type TaskPriority = z.infer<typeof TaskPriority>;
18
- export declare const ProjectType: z.ZodEnum<{
19
- active_project: "active_project";
20
- project_idea: "project_idea";
21
- }>;
22
- export type ProjectType = z.infer<typeof ProjectType>;
23
- export declare const NotificationType: z.ZodEnum<{
24
- info: "info";
25
- error: "error";
26
- success: "success";
27
- warning: "warning";
28
- }>;
29
- export type NotificationType = z.infer<typeof NotificationType>;
30
- export declare const AgentMessageStatus: z.ZodEnum<{
31
- pending: "pending";
32
- answered: "answered";
33
- dismissed: "dismissed";
34
- }>;
35
- export type AgentMessageStatus = z.infer<typeof AgentMessageStatus>;
36
- export declare const AgentStatus: z.ZodEnum<{
37
- connected: "connected";
38
- disconnected: "disconnected";
39
- }>;
40
- export type AgentStatus = z.infer<typeof AgentStatus>;
41
- export declare const ActivityAction: z.ZodEnum<{
42
- task_created: "task_created";
43
- task_deleted: "task_deleted";
44
- task_status_changed: "task_status_changed";
45
- task_completed: "task_completed";
46
- task_partial_done: "task_partial_done";
47
- timer_started: "timer_started";
48
- timer_paused: "timer_paused";
49
- timer_stopped: "timer_stopped";
50
- project_created: "project_created";
51
- project_deleted: "project_deleted";
52
- project_updated: "project_updated";
53
- tasks_bulk_created: "tasks_bulk_created";
54
- settings_saved: "settings_saved";
55
- data_seeded: "data_seeded";
56
- data_cleared: "data_cleared";
57
- task_linked: "task_linked";
58
- task_unlinked: "task_unlinked";
59
- dependency_added: "dependency_added";
60
- dependency_removed: "dependency_removed";
61
- link_added: "link_added";
62
- tag_added: "tag_added";
63
- tag_removed: "tag_removed";
64
- debug_log: "debug_log";
65
- agent_question: "agent_question";
66
- agent_question_answered: "agent_question_answered";
67
- agent_broadcast: "agent_broadcast";
68
- agent_connected: "agent_connected";
69
- agent_disconnected: "agent_disconnected";
70
- agent_renamed: "agent_renamed";
71
- agent_deleted: "agent_deleted";
72
- terminal_send_keys: "terminal_send_keys";
73
- terminal_captured: "terminal_captured";
74
- compaction_summary: "compaction_summary";
75
- activity_compacted: "activity_compacted";
76
- }>;
77
- export type ActivityAction = z.infer<typeof ActivityAction>;
78
- export declare const VALID_TRANSITIONS: Record<TaskStatus, TaskStatus[]>;
79
- export type ErrorCode = 'NOT_FOUND' | 'INVALID_TRANSITION' | 'VALIDATION_ERROR' | 'CYCLE_DETECTED' | 'SESSION_ALREADY_ACTIVE' | 'NO_ACTIVE_SESSION' | 'ALREADY_ANSWERED' | 'CONFLICT';
80
- export declare const LinkSchema: z.ZodObject<{
81
- label: z.ZodString;
82
- url: z.ZodString;
83
- }, z.core.$strip>;
package/dist/types.js DELETED
@@ -1,30 +0,0 @@
1
- import { z } from 'zod';
2
- export const TaskStatus = z.enum([
3
- 'not_started', 'in_progress', 'paused', 'blocked', 'partial_done', 'done'
4
- ]);
5
- export const TaskPriority = z.enum(['low', 'medium', 'high', 'critical']);
6
- export const ProjectType = z.enum(['active_project', 'project_idea']);
7
- export const NotificationType = z.enum(['info', 'success', 'warning', 'error']);
8
- export const AgentMessageStatus = z.enum(['pending', 'answered', 'dismissed']);
9
- export const AgentStatus = z.enum(['connected', 'disconnected']);
10
- export const ActivityAction = z.enum([
11
- 'task_created', 'task_deleted', 'task_status_changed', 'task_completed',
12
- 'task_partial_done', 'timer_started', 'timer_paused', 'timer_stopped',
13
- 'project_created', 'project_deleted', 'project_updated',
14
- 'tasks_bulk_created', 'settings_saved', 'data_seeded', 'data_cleared',
15
- 'task_linked', 'task_unlinked', 'dependency_added', 'dependency_removed',
16
- 'link_added', 'tag_added', 'tag_removed', 'debug_log',
17
- 'agent_question', 'agent_question_answered', 'agent_broadcast',
18
- 'agent_connected', 'agent_disconnected', 'agent_renamed', 'agent_deleted',
19
- 'terminal_send_keys', 'terminal_captured',
20
- 'compaction_summary', 'activity_compacted',
21
- ]);
22
- export const VALID_TRANSITIONS = {
23
- not_started: ['in_progress', 'blocked'],
24
- in_progress: ['paused', 'blocked', 'partial_done', 'done'],
25
- paused: ['in_progress', 'blocked', 'partial_done', 'done'],
26
- blocked: ['not_started', 'in_progress'],
27
- partial_done: ['in_progress', 'done'],
28
- done: ['in_progress'],
29
- };
30
- export const LinkSchema = z.object({ label: z.string(), url: z.string() });