nothumanallowed 8.0.4 → 8.1.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "8.0.4",
3
+ "version": "8.1.1",
4
4
  "description": "NotHumanAllowed — 38 AI agents + unified productivity suite. Gmail, Calendar, Drive, Contacts, Tasks, GitHub, Notion, Slack, voice chat, smart scheduler. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -575,6 +575,28 @@ export async function cmdUI(args) {
575
575
  return;
576
576
  }
577
577
 
578
+ // POST /api/tasks/:id/delete
579
+ const taskDeleteMatch = pathname.match(/^\/api\/tasks\/(\d+)\/delete$/);
580
+ if (method === 'POST' && taskDeleteMatch) {
581
+ const { deleteTask } = await import('../services/task-store.mjs');
582
+ const taskId = parseInt(taskDeleteMatch[1], 10);
583
+ const success = deleteTask(taskId);
584
+ sendJSON(res, 200, { ok: success, id: taskId });
585
+ logRequest(method, pathname, 200, Date.now() - start);
586
+ return;
587
+ }
588
+
589
+ // POST /api/tasks/clear
590
+ if (method === 'POST' && pathname === '/api/tasks/clear') {
591
+ const { clearTasks } = await import('../services/task-store.mjs');
592
+ const body = await parseBody(req);
593
+ const mode = body.mode || 'all';
594
+ const count = clearTasks(mode);
595
+ sendJSON(res, 200, { ok: true, removed: count });
596
+ logRequest(method, pathname, 200, Date.now() - start);
597
+ return;
598
+ }
599
+
578
600
  // GET /api/plan
579
601
  if (method === 'GET' && pathname === '/api/plan') {
580
602
  const plan = loadTodayPlan();
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '8.0.4';
8
+ export const VERSION = '8.1.1';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -140,13 +140,16 @@ export async function runPlanningPipeline(config, opts = {}) {
140
140
  // ── Phase 6: CONDUCTOR — Synthesize daily plan ─────────────────────────
141
141
  info('Phase 6: CONDUCTOR synthesizing daily plan...');
142
142
 
143
- const conductorPrompt = `You are the NHA Daily Planner. Synthesize intelligence from 4 specialist agents into a structured, practical daily plan.
143
+ const conductorPrompt = `You are the NHA Daily Planner. Synthesize intelligence from specialist agents into a structured daily plan.
144
144
 
145
- IMPORTANT GUIDELINES:
146
- - Be PRACTICAL, not alarmist. Routine notifications (Google login alerts from your own devices, npm publish confirmations, GitHub security notices) are NOT security incidents.
147
- - Only escalate to "security_alerts" if there is a GENUINE, actionable threat (unknown logins from strange locations, actual phishing, credential leaks).
148
- - Focus on making the user's day productive, not on creating false urgency.
149
- - Suggest realistic time blocks based on the actual task complexity.
145
+ CRITICAL RULES — READ CAREFULLY:
146
+ 1. ONLY include events that appear in the CALENDAR section below. NEVER invent, hallucinate, or assume meetings/appointments that are not listed.
147
+ 2. If there are 0 events in the calendar, the schedule section must ONLY contain suggested focus blocks and task time — NO invented meetings.
148
+ 3. Be PRACTICAL, not alarmist. Routine notifications (Google login alerts from your own devices, npm publish confirmations, GitHub 2FA, password change emails) are NOT security threats. Mark them as SAFE.
149
+ 4. Only put items in "security_alerts" for GENUINE threats: phishing, unknown device access from unexpected locations, credential leaks, social engineering.
150
+ 5. Security alerts must be simple strings, NOT JSON objects. Example: "Verify Google login from unknown Mac device in Italy"
151
+ 6. The "schedule" section should reflect REAL calendar events + suggested blocks for tasks. Do not fabricate appointments.
152
+ 7. Do not create new_tasks that duplicate existing tasks.
150
153
 
151
154
  AGENT REPORTS:
152
155
  ${agentResults.saber ? `\n[SABER — Security Scan]\n${agentResults.saber}` : ''}
@@ -160,22 +163,22 @@ Events: ${events.length}
160
163
  Unread emails: ${emails.length}
161
164
  Tasks: ${tasks.length}
162
165
 
163
- CALENDAR:
164
- ${calendarContext || 'No events.'}
166
+ CALENDAR (these are the ONLY real events — do NOT add any others):
167
+ ${calendarContext || 'No events scheduled.'}
165
168
 
166
- EXISTING TASKS:
169
+ EXISTING TASKS (do NOT duplicate these):
167
170
  ${taskContext || 'No tasks.'}
168
171
 
169
- Create a comprehensive daily plan. Output strict JSON:
172
+ Output strict JSON:
170
173
  {
171
174
  "date": "${dateStr}",
172
- "executive_summary": "2-3 sentence overview",
175
+ "executive_summary": "2-3 sentence overview of the ACTUAL day based on real data",
173
176
  "priority_actions": [{ "time": "HH:MM", "action": "...", "source": "email|calendar|task", "priority": "critical|high|medium|low" }],
174
177
  "schedule": [{ "time_start": "HH:MM", "time_end": "HH:MM", "type": "meeting|focus|break|task", "title": "...", "notes": "...", "preparation": "..." }],
175
178
  "email_actions": [{ "from": "...", "subject": "...", "action": "reply|archive|flag|defer", "suggested_reply": "..." }],
176
- "security_alerts": [],
177
- "new_tasks": [{ "description": "...", "priority": "high|medium|low", "estimated_minutes": N, "suggested_slot": "HH:MM" }],
178
- "insights": []
179
+ "security_alerts": ["simple string descriptions only"],
180
+ "new_tasks": [{ "description": "...", "priority": "high|medium|low", "estimated_minutes": 30, "suggested_slot": "HH:MM" }],
181
+ "insights": ["simple string insights only"]
179
182
  }`;
180
183
 
181
184
  let plan;
@@ -266,7 +269,8 @@ function displayPlan(plan) {
266
269
  if (plan.security_alerts?.length > 0) {
267
270
  console.log(` ${BOLD}\x1b[0;31mSecurity Alerts${NC}`);
268
271
  for (const a of plan.security_alerts) {
269
- console.log(` \x1b[0;31m!\x1b[0m ${typeof a === 'string' ? a : a.message || JSON.stringify(a)}`);
272
+ const text = typeof a === 'string' ? a : a.description || a.message || a.action_required || `[${a.type || 'alert'}] ${a.severity || ''} — ${JSON.stringify(a)}`;
273
+ console.log(` \x1b[0;31m!\x1b[0m ${text}`);
270
274
  }
271
275
  console.log('');
272
276
  }
@@ -274,7 +278,8 @@ function displayPlan(plan) {
274
278
  if (plan.insights?.length > 0) {
275
279
  console.log(` ${BOLD}${D}Insights${NC}`);
276
280
  for (const i of plan.insights) {
277
- console.log(` ${D}→ ${typeof i === 'string' ? i : i.message || JSON.stringify(i)}${NC}`);
281
+ const text = typeof i === 'string' ? i : i.message || i.insight || JSON.stringify(i);
282
+ console.log(` ${D}→ ${text}${NC}`);
278
283
  }
279
284
  console.log('');
280
285
  }
@@ -113,6 +113,55 @@ export function moveTask(taskId, fromDate, toDate) {
113
113
  return true;
114
114
  }
115
115
 
116
+ /**
117
+ * Delete a single task by ID.
118
+ * @param {number} taskId
119
+ * @param {string} [date]
120
+ * @returns {boolean} success
121
+ */
122
+ export function deleteTask(taskId, date) {
123
+ const data = loadDay(date);
124
+ const idx = data.tasks.findIndex(t => t.id === taskId);
125
+ if (idx === -1) return false;
126
+ data.tasks.splice(idx, 1);
127
+ saveDay(date, data);
128
+ return true;
129
+ }
130
+
131
+ /**
132
+ * Clear all tasks for a date (delete completed, or all).
133
+ * @param {'done'|'all'} mode — 'done' removes only completed, 'all' removes everything
134
+ * @param {string} [date]
135
+ * @returns {number} count of removed tasks
136
+ */
137
+ export function clearTasks(mode = 'all', date) {
138
+ const data = loadDay(date);
139
+ const before = data.tasks.length;
140
+ if (mode === 'done') {
141
+ data.tasks = data.tasks.filter(t => t.status !== 'done');
142
+ } else {
143
+ data.tasks = [];
144
+ }
145
+ saveDay(date, data);
146
+ return before - data.tasks.length;
147
+ }
148
+
149
+ /**
150
+ * Edit task priority.
151
+ * @param {number} taskId
152
+ * @param {string} priority
153
+ * @param {string} [date]
154
+ * @returns {boolean}
155
+ */
156
+ export function editTaskPriority(taskId, priority, date) {
157
+ const data = loadDay(date);
158
+ const task = data.tasks.find(t => t.id === taskId);
159
+ if (!task) return false;
160
+ task.priority = priority;
161
+ saveDay(date, data);
162
+ return true;
163
+ }
164
+
116
165
  /**
117
166
  * Get tasks for the week (Mon-Sun).
118
167
  */
@@ -36,6 +36,10 @@ import {
36
36
  addTask,
37
37
  completeTask,
38
38
  moveTask,
39
+ deleteTask,
40
+ clearTasks,
41
+ editTask,
42
+ editTaskPriority,
39
43
  } from './task-store.mjs';
40
44
 
41
45
  import { notify } from './notification.mjs';
@@ -53,6 +57,8 @@ export const DESTRUCTIVE_ACTIONS = new Set([
53
57
  'calendar_update',
54
58
  'contact_delete',
55
59
  'task_done',
60
+ 'task_delete',
61
+ 'task_clear',
56
62
  'notify_remind',
57
63
  'slack_send',
58
64
  'github_create_issue',
@@ -157,6 +163,15 @@ TOOLS:
157
163
  23. task_move(id: number, toDate: string)
158
164
  Move a task to another date (YYYY-MM-DD).
159
165
 
166
+ 24. task_delete(id: number)
167
+ Delete a specific task permanently. ALWAYS confirm before deleting.
168
+
169
+ 25. task_clear(mode?: "all"|"done")
170
+ Clear tasks. mode="done" removes only completed tasks. mode="all" removes ALL tasks. Default: "all". ALWAYS confirm before clearing.
171
+
172
+ 26. task_edit(id: number, description?: string, priority?: "low"|"medium"|"high"|"critical")
173
+ Edit a task's description and/or priority.
174
+
160
175
  --- CONTACTS ---
161
176
 
162
177
  24. contact_search(query: string)
@@ -381,6 +396,10 @@ export function describeAction(action, params) {
381
396
  return `Delete contact "${params.query}"`;
382
397
  case 'task_done':
383
398
  return `Mark task #${params.id} as completed`;
399
+ case 'task_delete':
400
+ return `Delete task #${params.id} permanently`;
401
+ case 'task_clear':
402
+ return `Clear ${params.mode === 'done' ? 'completed' : 'ALL'} tasks`;
384
403
  case 'notify_remind':
385
404
  return `Set reminder: "${params.message}" at ${params.atTime}`;
386
405
  case 'slack_send':
@@ -759,6 +778,32 @@ export async function executeTool(action, params, config) {
759
778
  return success ? `Task #${params.id} moved to ${toDate}.` : `Task #${params.id} not found.`;
760
779
  }
761
780
 
781
+ case 'task_delete': {
782
+ const success = deleteTask(params.id);
783
+ return success ? `Task #${params.id} deleted.` : `Task #${params.id} not found.`;
784
+ }
785
+
786
+ case 'task_clear': {
787
+ const mode = params.mode || 'all';
788
+ const count = clearTasks(mode);
789
+ return count > 0
790
+ ? `${count} task${count !== 1 ? 's' : ''} ${mode === 'done' ? 'completed' : ''} removed.`
791
+ : 'No tasks to remove.';
792
+ }
793
+
794
+ case 'task_edit': {
795
+ if (params.description) {
796
+ editTask(params.id, params.description);
797
+ }
798
+ if (params.priority) {
799
+ editTaskPriority(params.id, params.priority);
800
+ }
801
+ const parts = [];
802
+ if (params.description) parts.push(`description updated`);
803
+ if (params.priority) parts.push(`priority set to ${params.priority}`);
804
+ return parts.length > 0 ? `Task #${params.id}: ${parts.join(', ')}.` : `No changes specified for task #${params.id}.`;
805
+ }
806
+
762
807
  // ── Notifications ─────────────────────────────────────────────────────
763
808
  case 'notify_remind': {
764
809
  const atTime = resolveTime(params.atTime);
@@ -360,10 +360,13 @@ function sendChat(){
360
360
  function renderTasks(el){
361
361
  var t=dash.tasks;
362
362
  var h='<div class="task-bar"><input id="taskInput" placeholder="Add a new task..."><select id="taskPriority"><option value="medium">Medium</option><option value="high">High</option><option value="critical">Critical</option><option value="low">Low</option></select><button onclick="addTaskUI()">Add</button></div>';
363
+ if(t.length>0){
364
+ h+='<div style="display:flex;gap:8px;margin-bottom:12px"><button onclick="clearTasksUI(\\x27done\\x27)" style="background:var(--bg3);color:var(--dim);border:1px solid var(--border);padding:6px 12px;border-radius:var(--r);font-size:11px;cursor:pointer">Clear completed</button><button onclick="clearTasksUI(\\x27all\\x27)" style="background:var(--bg3);color:var(--red);border:1px solid var(--border);padding:6px 12px;border-radius:var(--r);font-size:11px;cursor:pointer">Clear all</button></div>';
365
+ }
363
366
  t.sort(function(a,b){if(a.status==='done'&&b.status!=='done')return 1;if(a.status!=='done'&&b.status==='done')return -1;return 0});
364
367
  t.forEach(function(x){
365
368
  var isDone=x.status==='done';
366
- h+='<div class="card task'+(isDone?' task--done':'')+'" onclick="toggleTask('+x.id+')"><span class="task__check'+(isDone?' task__check--done':'')+'">'+(isDone?'\\u2713':'')+'</span><span class="task__desc">'+esc(x.description)+'</span><span class="task__priority task__priority--'+esc(x.priority)+'">'+esc(x.priority)+'</span></div>';
369
+ h+='<div class="card task'+(isDone?' task--done':'')+'"><span class="task__check'+(isDone?' task__check--done':'')+'" onclick="toggleTask('+x.id+')">'+(isDone?'\\u2713':'')+'</span><span class="task__desc" onclick="toggleTask('+x.id+')">'+esc(x.description)+'</span><span class="task__priority task__priority--'+esc(x.priority)+'">'+esc(x.priority)+'</span><span onclick="deleteTaskUI('+x.id+')" style="color:var(--dim);cursor:pointer;padding:4px 8px;font-size:14px;opacity:0.5" title="Delete task">&times;</span></div>';
367
370
  });
368
371
  el.innerHTML=h;
369
372
  var inp=document.getElementById('taskInput');
@@ -377,7 +380,16 @@ function addTaskUI(){
377
380
  });
378
381
  }
379
382
  function toggleTask(id){
380
- apiPatch('/api/tasks/'+id+'/done').then(function(){loadDash().then(function(){if(currentView==='tasks')render()})});
383
+ apiPatch('/api/tasks/'+id+'/done').then(function(){loadDash().then(function(){if(currentView==='tasks'||currentView==='dashboard')render()})});
384
+ }
385
+ function deleteTaskUI(id){
386
+ if(!confirm('Delete task #'+id+'?'))return;
387
+ apiPost('/api/tasks/'+id+'/delete',{}).then(function(){loadDash().then(function(){render()})});
388
+ }
389
+ function clearTasksUI(mode){
390
+ var msg=mode==='all'?'Delete ALL tasks? This cannot be undone.':'Remove all completed tasks?';
391
+ if(!confirm(msg))return;
392
+ apiPost('/api/tasks/clear',{mode:mode}).then(function(){loadDash().then(function(){render()})});
381
393
  }
382
394
 
383
395
  // ---- PLAN ----
@@ -389,8 +401,8 @@ function renderPlan(el){
389
401
  var h='<div class="plan-summary">'+esc(p.executive_summary||'No summary')+'</div>';
390
402
  if(p.priority_actions&&p.priority_actions.length>0){h+='<div class="section-title">Priority Actions</div>';p.priority_actions.forEach(function(a){h+='<div class="card plan-action"><span class="plan-action__time">'+esc(a.time||'')+'</span><span class="plan-action__text">'+esc(a.action)+'</span></div>'})}
391
403
  if(p.schedule&&p.schedule.length>0){h+='<div class="section-title">Schedule</div>';p.schedule.forEach(function(s){h+='<div class="card event"><span class="event__time">'+esc(s.time_start)+'-'+esc(s.time_end)+'</span><span class="event__title">'+esc(s.title)+'</span></div>'})}
392
- if(p.security_alerts&&p.security_alerts.length>0){h+='<div class="section-title" style="color:var(--red)">Security Alerts</div>';p.security_alerts.forEach(function(a){h+='<div class="card" style="border-color:var(--red)"><span style="color:var(--red)">'+esc(typeof a==='string'?a:a.message||JSON.stringify(a))+'</span></div>'})}
393
- if(p.insights&&p.insights.length>0){h+='<div class="section-title">Insights</div>';p.insights.forEach(function(i){h+='<div style="color:var(--dim);padding:4px 0;font-size:12px">\\u2192 '+esc(typeof i==='string'?i:i.message||'')+'</div>'})}
404
+ if(p.security_alerts&&p.security_alerts.length>0){h+='<div class="section-title" style="color:var(--red)">Security Alerts</div>';p.security_alerts.forEach(function(a){var txt=typeof a==='string'?a:(a.description||a.message||a.action_required||'Alert');var sev=typeof a==='object'&&a.severity?' ['+a.severity.toUpperCase()+']':'';h+='<div class="card" style="border-color:var(--red);padding:14px"><span style="color:var(--red);font-weight:700">!'+esc(sev)+'</span> '+esc(txt)+(typeof a==='object'&&a.action_required&&a.action_required!==txt?'<div style="color:var(--amber);font-size:11px;margin-top:6px">Action: '+esc(a.action_required)+'</div>':'')+'</div>'})}
405
+ if(p.insights&&p.insights.length>0){h+='<div class="section-title">Insights</div>';p.insights.forEach(function(i){var txt=typeof i==='string'?i:(i.message||i.insight||'');h+='<div style="color:var(--dim);padding:4px 0;font-size:12px">\\u2192 '+esc(txt)+'</div>'})}
394
406
  h+='<div style="margin-top:16px;text-align:center"><button class="btn btn--secondary" onclick="refreshPlan()">Regenerate</button></div>';
395
407
  el.innerHTML=h;
396
408
  });