neoagent 2.2.1-beta.5 → 2.2.1-beta.7
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/docs/automation.md +2 -2
- package/docs/capabilities.md +7 -10
- package/docs/hardware.md +4 -7
- package/docs/index.md +6 -7
- package/docs/integrations.md +1 -1
- package/docs/operations.md +1 -1
- package/docs/why-neoagent.md +2 -2
- package/package.json +1 -1
- package/server/db/database.js +76 -61
- package/server/http/routes.js +1 -2
- package/server/public/assets/AssetManifest.json +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +65118 -64805
- package/server/routes/{scheduler.js → tasks.js} +31 -29
- package/server/routes/widgets.js +7 -7
- package/server/services/ai/capabilityHealth.js +4 -4
- package/server/services/ai/engine.js +31 -12
- package/server/services/ai/systemPrompt.js +7 -7
- package/server/services/ai/taskAnalysis.js +3 -3
- package/server/services/ai/toolResult.js +6 -8
- package/server/services/ai/tools.js +62 -95
- package/server/services/commands/router.js +14 -6
- package/server/services/integrations/whatsapp/provider.js +23 -1
- package/server/services/manager.js +14 -14
- package/server/services/memory/manager.js +7 -7
- package/server/services/memory/policy.js +1 -1
- package/server/services/messaging/formatting_guides.js +0 -4
- package/server/services/messaging/manager.js +0 -2
- package/server/services/tasks/adapters/gmail_message_received.js +36 -0
- package/server/services/tasks/adapters/index.js +10 -0
- package/server/services/tasks/adapters/outlook_email_received.js +38 -0
- package/server/services/tasks/adapters/schedule.js +57 -0
- package/server/services/tasks/adapters/slack_message_received.js +39 -0
- package/server/services/tasks/adapters/teams_message_received.js +39 -0
- package/server/services/tasks/adapters/whatsapp_personal_message_received.js +42 -0
- package/server/services/tasks/integration_runtime.js +260 -0
- package/server/services/tasks/runtime.js +539 -0
- package/server/services/{scheduler/cron_utils.js → tasks/schedule_utils.js} +2 -0
- package/server/services/tasks/security.js +60 -0
- package/server/services/tasks/task_repository.js +162 -0
- package/server/services/tasks/trigger_registry.js +29 -0
- package/server/services/tasks/utils.js +45 -0
- package/server/services/websocket.js +1 -1
- package/server/services/widgets/service.js +38 -25
- package/server/routes/wearable_device.js +0 -147
- package/server/services/messaging/waveshare_wearable.js +0 -40
- package/server/services/scheduler/cron.js +0 -580
- package/server/services/wearables/device_auth.js +0 -228
|
@@ -3,79 +3,81 @@ const router = express.Router();
|
|
|
3
3
|
const { requireAuth } = require('../middleware/auth');
|
|
4
4
|
const { sanitizeError } = require('../utils/security');
|
|
5
5
|
const { getAgentIdFromRequest, resolveAgentId } = require('../services/agents/manager');
|
|
6
|
-
const cron = require('node-cron');
|
|
7
6
|
|
|
8
7
|
router.use(requireAuth);
|
|
9
8
|
|
|
10
|
-
// List scheduled tasks
|
|
11
9
|
router.get('/', (req, res) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
try {
|
|
11
|
+
const tasks = req.app.locals.taskRuntime;
|
|
12
|
+
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
|
13
|
+
res.json(tasks.listTasks(req.session.userId, { agentId }));
|
|
14
|
+
} catch (error) {
|
|
15
|
+
(req.app.locals.logger?.error || console.error)('[Tasks] Failed to list tasks', error);
|
|
16
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
17
|
+
}
|
|
15
18
|
});
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
router.post('/', (req, res) => {
|
|
20
|
+
router.get('/catalog', (req, res) => {
|
|
19
21
|
try {
|
|
20
|
-
const
|
|
22
|
+
const tasks = req.app.locals.taskRuntime;
|
|
21
23
|
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
res.json(tasks.getTriggerCatalog(req.session.userId, { agentId }));
|
|
25
|
+
} catch (error) {
|
|
26
|
+
(req.app.locals.logger?.error || console.error)('[Tasks] Failed to load trigger catalog', error);
|
|
27
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
router.post('/', async (req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
const tasks = req.app.locals.taskRuntime;
|
|
34
|
+
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
|
35
|
+
const task = await tasks.createTask(req.session.userId, {
|
|
36
|
+
...req.body,
|
|
37
|
+
agentId,
|
|
38
|
+
});
|
|
31
39
|
res.status(201).json(task);
|
|
32
40
|
} catch (err) {
|
|
33
41
|
res.status(400).json({ error: sanitizeError(err) });
|
|
34
42
|
}
|
|
35
43
|
});
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
router.put('/:id', (req, res) => {
|
|
45
|
+
router.put('/:id', async (req, res) => {
|
|
39
46
|
try {
|
|
40
47
|
const taskId = Number.parseInt(req.params.id, 10);
|
|
41
48
|
if (!Number.isInteger(taskId) || taskId <= 0) {
|
|
42
49
|
return res.status(400).json({ error: 'Invalid task id' });
|
|
43
50
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
const scheduler = req.app.locals.scheduler;
|
|
48
|
-
const task = scheduler.updateTask(taskId, req.session.userId, req.body);
|
|
51
|
+
const tasks = req.app.locals.taskRuntime;
|
|
52
|
+
const task = await tasks.updateTask(taskId, req.session.userId, req.body);
|
|
49
53
|
res.json(task);
|
|
50
54
|
} catch (err) {
|
|
51
55
|
res.status(400).json({ error: sanitizeError(err) });
|
|
52
56
|
}
|
|
53
57
|
});
|
|
54
58
|
|
|
55
|
-
// Delete a scheduled task
|
|
56
59
|
router.delete('/:id', (req, res) => {
|
|
57
60
|
try {
|
|
58
61
|
const taskId = Number.parseInt(req.params.id, 10);
|
|
59
62
|
if (!Number.isInteger(taskId) || taskId <= 0) {
|
|
60
63
|
return res.status(400).json({ error: 'Invalid task id' });
|
|
61
64
|
}
|
|
62
|
-
const
|
|
63
|
-
|
|
65
|
+
const tasks = req.app.locals.taskRuntime;
|
|
66
|
+
tasks.deleteTask(taskId, req.session.userId);
|
|
64
67
|
res.json({ success: true });
|
|
65
68
|
} catch (err) {
|
|
66
69
|
res.status(400).json({ error: sanitizeError(err) });
|
|
67
70
|
}
|
|
68
71
|
});
|
|
69
72
|
|
|
70
|
-
// Run a task immediately
|
|
71
73
|
router.post('/:id/run', (req, res) => {
|
|
72
74
|
try {
|
|
73
75
|
const taskId = Number.parseInt(req.params.id, 10);
|
|
74
76
|
if (!Number.isInteger(taskId) || taskId <= 0) {
|
|
75
77
|
return res.status(400).json({ error: 'Invalid task id' });
|
|
76
78
|
}
|
|
77
|
-
const
|
|
78
|
-
const result =
|
|
79
|
+
const tasks = req.app.locals.taskRuntime;
|
|
80
|
+
const result = tasks.runTaskNow(taskId, req.session.userId);
|
|
79
81
|
res.json(result);
|
|
80
82
|
} catch (err) {
|
|
81
83
|
res.status(400).json({ error: sanitizeError(err) });
|
package/server/routes/widgets.js
CHANGED
|
@@ -40,26 +40,26 @@ router.get('/', (req, res) => {
|
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
router.post('/', (req, res) => {
|
|
43
|
+
router.post('/', async (req, res) => {
|
|
44
44
|
try {
|
|
45
45
|
const service = widgetService(req);
|
|
46
46
|
if (!service) {
|
|
47
47
|
return res.status(500).json({ error: 'Widget service unavailable.' });
|
|
48
48
|
}
|
|
49
|
-
const widget = service.createWidget(req.session.userId, req.body || {});
|
|
49
|
+
const widget = await service.createWidget(req.session.userId, req.body || {});
|
|
50
50
|
res.status(201).json(widget);
|
|
51
51
|
} catch (err) {
|
|
52
52
|
res.status(400).json({ error: sanitizeError(err) });
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
router.put('/:id', (req, res) => {
|
|
56
|
+
router.put('/:id', async (req, res) => {
|
|
57
57
|
try {
|
|
58
58
|
const service = widgetService(req);
|
|
59
59
|
if (!service) {
|
|
60
60
|
return res.status(500).json({ error: 'Widget service unavailable.' });
|
|
61
61
|
}
|
|
62
|
-
const widget = service.updateWidget(req.session.userId, req.params.id, req.body || {});
|
|
62
|
+
const widget = await service.updateWidget(req.session.userId, req.params.id, req.body || {});
|
|
63
63
|
res.json(widget);
|
|
64
64
|
} catch (err) {
|
|
65
65
|
res.status(400).json({ error: sanitizeError(err) });
|
|
@@ -81,8 +81,8 @@ router.delete('/:id', (req, res) => {
|
|
|
81
81
|
router.post('/:id/refresh', (req, res) => {
|
|
82
82
|
try {
|
|
83
83
|
const service = widgetService(req);
|
|
84
|
-
const
|
|
85
|
-
if (!service || !
|
|
84
|
+
const taskRuntime = req.app?.locals?.taskRuntime;
|
|
85
|
+
if (!service || !taskRuntime) {
|
|
86
86
|
return res.status(500).json({ error: 'Widget refresh unavailable.' });
|
|
87
87
|
}
|
|
88
88
|
const widget = service.getWidget(req.session.userId, req.params.id);
|
|
@@ -92,7 +92,7 @@ router.post('/:id/refresh', (req, res) => {
|
|
|
92
92
|
if (!widget.scheduledTaskId) {
|
|
93
93
|
return res.status(400).json({ error: 'Widget is missing its refresh task.' });
|
|
94
94
|
}
|
|
95
|
-
res.json(
|
|
95
|
+
res.json(taskRuntime.runTaskNow(widget.scheduledTaskId, req.session.userId));
|
|
96
96
|
} catch (err) {
|
|
97
97
|
res.status(400).json({ error: sanitizeError(err) });
|
|
98
98
|
}
|
|
@@ -354,7 +354,7 @@ function getMemoryHealth(engine) {
|
|
|
354
354
|
});
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
function
|
|
357
|
+
function getTaskHealth(userId, agentId = null) {
|
|
358
358
|
const taskCount = agentId
|
|
359
359
|
? db.prepare('SELECT COUNT(*) AS count FROM scheduled_tasks WHERE user_id = ? AND agent_id = ?').get(userId, agentId)?.count || 0
|
|
360
360
|
: db.prepare('SELECT COUNT(*) AS count FROM scheduled_tasks WHERE user_id = ?').get(userId)?.count || 0;
|
|
@@ -363,8 +363,8 @@ function getSchedulerHealth(userId, agentId = null) {
|
|
|
363
363
|
configured: true,
|
|
364
364
|
healthy: true,
|
|
365
365
|
summary: taskCount > 0
|
|
366
|
-
? `${taskCount}
|
|
367
|
-
: 'No
|
|
366
|
+
? `${taskCount} task(s) exist for this user.`
|
|
367
|
+
: 'No tasks are configured.',
|
|
368
368
|
details: { taskCount },
|
|
369
369
|
});
|
|
370
370
|
}
|
|
@@ -385,7 +385,7 @@ async function getCapabilityHealth({ userId, agentId = null, app, engine }) {
|
|
|
385
385
|
integrations: getIntegrationHealth(userId, app, agentId),
|
|
386
386
|
mcp: getMcpHealth(userId, app, engine, agentId),
|
|
387
387
|
skills: getSkillHealth(app, engine),
|
|
388
|
-
|
|
388
|
+
tasks: getTaskHealth(userId, agentId),
|
|
389
389
|
},
|
|
390
390
|
};
|
|
391
391
|
}
|
|
@@ -39,6 +39,7 @@ const {
|
|
|
39
39
|
} = require('./interim');
|
|
40
40
|
|
|
41
41
|
const MAX_CONSECUTIVE_TOOL_FAILURES = 3;
|
|
42
|
+
const WIDGET_REFRESH_MAX_ITERATIONS = 6;
|
|
42
43
|
|
|
43
44
|
function generateTitle(task) {
|
|
44
45
|
if (!task || typeof task !== 'string') return 'Untitled';
|
|
@@ -458,6 +459,7 @@ function classifyToolExecution(toolName, toolArgs = {}, result, errorMessage = '
|
|
|
458
459
|
'recordings_list',
|
|
459
460
|
'recordings_get',
|
|
460
461
|
'recordings_search',
|
|
462
|
+
'list_tasks',
|
|
461
463
|
'wait_subagent',
|
|
462
464
|
]);
|
|
463
465
|
const stateChangingExact = new Set([
|
|
@@ -470,10 +472,9 @@ function classifyToolExecution(toolName, toolArgs = {}, result, errorMessage = '
|
|
|
470
472
|
'create_skill',
|
|
471
473
|
'update_skill',
|
|
472
474
|
'delete_skill',
|
|
473
|
-
'
|
|
474
|
-
'
|
|
475
|
-
'
|
|
476
|
-
'schedule_run',
|
|
475
|
+
'create_task',
|
|
476
|
+
'update_task',
|
|
477
|
+
'delete_task',
|
|
477
478
|
'create_ai_widget',
|
|
478
479
|
'update_ai_widget',
|
|
479
480
|
'delete_ai_widget',
|
|
@@ -502,8 +503,8 @@ function classifyToolExecution(toolName, toolArgs = {}, result, errorMessage = '
|
|
|
502
503
|
? 'command'
|
|
503
504
|
: name.includes('skill')
|
|
504
505
|
? 'skills'
|
|
505
|
-
: name
|
|
506
|
-
? '
|
|
506
|
+
: (name === 'create_task' || name === 'update_task' || name === 'delete_task' || name === 'list_tasks' || name.includes('widget'))
|
|
507
|
+
? 'tasks'
|
|
507
508
|
: name === 'send_message' || name === 'make_call'
|
|
508
509
|
? 'messaging'
|
|
509
510
|
: name.startsWith('recordings_') || name === 'read_health_data'
|
|
@@ -646,7 +647,7 @@ class AgentEngine {
|
|
|
646
647
|
this.messagingManager = services.messagingManager || null;
|
|
647
648
|
this.mcpManager = services.mcpManager || services.mcpClient || null;
|
|
648
649
|
this.skillRunner = services.skillRunner || null;
|
|
649
|
-
this.
|
|
650
|
+
this.taskRuntime = services.taskRuntime || null;
|
|
650
651
|
this.memoryManager = services.memoryManager || null;
|
|
651
652
|
this.voiceRuntimeManager = services.voiceRuntimeManager || null;
|
|
652
653
|
}
|
|
@@ -1324,8 +1325,9 @@ class AgentEngine {
|
|
|
1324
1325
|
runMeta.toolPids.delete(pid);
|
|
1325
1326
|
}
|
|
1326
1327
|
|
|
1327
|
-
getIterationLimit(triggerType, aiSettings) {
|
|
1328
|
+
getIterationLimit(triggerType, aiSettings, options = {}) {
|
|
1328
1329
|
if (triggerType === 'subagent') return aiSettings.subagent_max_iterations;
|
|
1330
|
+
if (options.widgetId) return Math.min(this.maxIterations, WIDGET_REFRESH_MAX_ITERATIONS);
|
|
1329
1331
|
return this.maxIterations;
|
|
1330
1332
|
}
|
|
1331
1333
|
|
|
@@ -1467,7 +1469,7 @@ class AgentEngine {
|
|
|
1467
1469
|
Number(options.historyWindow || aiSettings.chat_history_window) || aiSettings.chat_history_window,
|
|
1468
1470
|
);
|
|
1469
1471
|
const toolReplayBudget = aiSettings.tool_replay_budget_chars;
|
|
1470
|
-
const maxIterations = this.getIterationLimit(triggerType, aiSettings);
|
|
1472
|
+
const maxIterations = this.getIterationLimit(triggerType, aiSettings, options);
|
|
1471
1473
|
const providerStatusConfig = {
|
|
1472
1474
|
agentId,
|
|
1473
1475
|
onStatus: (status) => {
|
|
@@ -1507,6 +1509,7 @@ class AgentEngine {
|
|
|
1507
1509
|
explicitMessageSent: carriedExplicitMessageSent,
|
|
1508
1510
|
lastSentMessage: carriedExplicitMessageSent ? carriedVisibleMessage : '',
|
|
1509
1511
|
sentMessages: [],
|
|
1512
|
+
widgetSnapshotSaved: false,
|
|
1510
1513
|
triggerType,
|
|
1511
1514
|
triggerSource,
|
|
1512
1515
|
startedAt: Date.now(),
|
|
@@ -2092,6 +2095,14 @@ class AgentEngine {
|
|
|
2092
2095
|
if (runMeta) {
|
|
2093
2096
|
runMeta.lastToolName = toolName;
|
|
2094
2097
|
runMeta.lastToolTarget = toolName === 'send_message' ? toolArgs.to : null;
|
|
2098
|
+
if (toolName === 'save_widget_snapshot' && !toolErrorMessage) {
|
|
2099
|
+
runMeta.widgetSnapshotSaved = true;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
if (toolName === 'save_widget_snapshot' && !toolErrorMessage) {
|
|
2104
|
+
lastContent = 'Widget snapshot updated.';
|
|
2105
|
+
break;
|
|
2095
2106
|
}
|
|
2096
2107
|
|
|
2097
2108
|
if (runMeta?.terminalInterim) {
|
|
@@ -2101,6 +2112,7 @@ class AgentEngine {
|
|
|
2101
2112
|
|
|
2102
2113
|
if (this.isRunStopped(runId)) break;
|
|
2103
2114
|
if (this.getRunMeta(runId)?.terminalInterim) break;
|
|
2115
|
+
if (this.getRunMeta(runId)?.widgetSnapshotSaved) break;
|
|
2104
2116
|
if (!this.activeRuns.has(runId)) break;
|
|
2105
2117
|
}
|
|
2106
2118
|
|
|
@@ -2119,6 +2131,9 @@ class AgentEngine {
|
|
|
2119
2131
|
if (runMeta?.terminalInterim) {
|
|
2120
2132
|
lastContent = '';
|
|
2121
2133
|
}
|
|
2134
|
+
if (runMeta?.widgetSnapshotSaved && !lastContent) {
|
|
2135
|
+
lastContent = 'Widget snapshot updated.';
|
|
2136
|
+
}
|
|
2122
2137
|
const messagingSent = runMeta?.messagingSent || false;
|
|
2123
2138
|
const lastToolWasMessaging = runMeta?.lastToolName === 'send_message' || runMeta?.lastToolName === 'make_call';
|
|
2124
2139
|
|
|
@@ -2147,7 +2162,11 @@ class AgentEngine {
|
|
|
2147
2162
|
}
|
|
2148
2163
|
}
|
|
2149
2164
|
|
|
2150
|
-
if (
|
|
2165
|
+
if (
|
|
2166
|
+
!normalizeOutgoingMessage(lastContent, options?.source || null)
|
|
2167
|
+
&& !messagingSent
|
|
2168
|
+
&& runMeta?.widgetSnapshotSaved !== true
|
|
2169
|
+
) {
|
|
2151
2170
|
if (iteration >= maxIterations) {
|
|
2152
2171
|
throw new Error(`Iteration limit reached before explicit completion after ${maxIterations} iterations.`);
|
|
2153
2172
|
}
|
|
@@ -2476,7 +2495,7 @@ class AgentEngine {
|
|
|
2476
2495
|
messagingManager: this.messagingManager,
|
|
2477
2496
|
mcpManager: this.mcpManager,
|
|
2478
2497
|
skillRunner: this.skillRunner,
|
|
2479
|
-
|
|
2498
|
+
taskRuntime: this.taskRuntime,
|
|
2480
2499
|
memoryManager: this.memoryManager,
|
|
2481
2500
|
});
|
|
2482
2501
|
|
|
@@ -2813,7 +2832,7 @@ class AgentEngine {
|
|
|
2813
2832
|
if (toolName === 'send_message') return 'messaging';
|
|
2814
2833
|
if (toolName === 'make_call') return 'messaging';
|
|
2815
2834
|
if (toolName.startsWith('mcp_') || toolName.includes('mcp')) return 'mcp';
|
|
2816
|
-
if (toolName
|
|
2835
|
+
if (toolName === 'create_task' || toolName === 'update_task' || toolName === 'delete_task' || toolName === 'list_tasks' || toolName.includes('widget')) return 'tasks';
|
|
2817
2836
|
if (toolName.includes('subagent')) return 'subagent';
|
|
2818
2837
|
if (toolName === 'think') return 'thinking';
|
|
2819
2838
|
return 'tool';
|
|
@@ -79,8 +79,8 @@ REPORT ACTUAL RESULTS
|
|
|
79
79
|
When a tool returns data, share the relevant parts — summarized if large, direct if short. Never paste raw JSON as the answer. Never narrate what you're about to do at length before doing it.
|
|
80
80
|
Never promise an action in the final answer unless you already took that action in this run. Do not say "I'll check", "I'll fix it", or "I'll send it" and then stop. Either do it first or say you have not done it yet.
|
|
81
81
|
Do not promise future follow-up work unless that work will actually happen automatically before the current run ends.
|
|
82
|
-
For
|
|
83
|
-
If the user asks you to debug
|
|
82
|
+
For task-config changes, never claim that a task was created, updated, deleted, enabled, disabled, or “fixed” unless the corresponding task tool call succeeded in this run. If you did not verify the actual task config, say that clearly instead of guessing.
|
|
83
|
+
If the user asks you to debug task timing or trigger behavior, inspect the current task list first and separate three things clearly: what you observed, what you infer, and what you actually changed.
|
|
84
84
|
|
|
85
85
|
RELIABILITY
|
|
86
86
|
If a claim depends on current external facts, status, timelines, or ambiguous relative dates, verify it with fresh evidence before stating it as fact. When relative time could be misunderstood, anchor it to explicit calendar dates.
|
|
@@ -97,7 +97,7 @@ Not every result is worth a message. If background work completes and the output
|
|
|
97
97
|
|
|
98
98
|
MEMORY
|
|
99
99
|
If the user references past work or context, use session_search before asking them to repeat themselves. Surface relevant memory naturally — never announce that you're "accessing memory" or "retrieving context". Just know it.
|
|
100
|
-
Store only durable memory candidates. Do not turn recent
|
|
100
|
+
Store only durable memory candidates. Do not turn recent task runs, task execution recaps, last-run statuses, or similar operational noise into long-term memory.
|
|
101
101
|
Never rely on memory alone for risky actions, private data changes, payments, sending messages, or current factual claims. Use memory to guide search and interpretation, then verify with the appropriate source.
|
|
102
102
|
Update core memory only for standing preferences, stable user facts, or durable agent-behavior preferences. For ordinary task facts, use regular memory or do nothing.
|
|
103
103
|
|
|
@@ -137,12 +137,12 @@ Messages to the user in the active conversation do not need extra confirmation.
|
|
|
137
137
|
When drafting on behalf of the user, match their likely voice from available context and relationship to the recipient. Keep the draft editable and do not send it until the user approves, unless the current message explicitly says to send.
|
|
138
138
|
If the user approves a previously shown draft, send that draft rather than silently rewriting it.
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
Use one-time
|
|
140
|
+
TASKS
|
|
141
|
+
Use one-time schedule triggers for single reminders or delayed actions, recurring schedule triggers for repeating automation, and official integration triggers when the task should react to connected Gmail, Outlook, Slack, Teams, or WhatsApp Personal events. Make task prompts self-contained: who/what to check, exact action to take, when to notify, and which channel to use if known.
|
|
142
142
|
Do not create vague tasks like "check this" when the future run would not know what "this" means. Resolve references into names, links, file paths, IDs, dates, and success criteria before saving the task.
|
|
143
143
|
For notification tasks, distinguish between notifying the user in their current messaging channel, emailing the user, and contacting someone else. Default reminders should notify the user through the active messaging channel unless the user explicitly asks for email, phone, or a third party.
|
|
144
|
-
When creating or updating a
|
|
145
|
-
For
|
|
144
|
+
When creating or updating a task, include whether it should notify every time, only on change, only on errors, or only when a condition is met. If unspecified, choose the least noisy useful behavior and say what you chose.
|
|
145
|
+
For tasks that may become stale, include an expiry condition or narrow scope when the user provided one.
|
|
146
146
|
|
|
147
147
|
SKILLS
|
|
148
148
|
Create or improve a skill only when it is clearly reusable, polished, and likely to matter again. Most completed tasks should not become skills.
|
|
@@ -49,7 +49,7 @@ const PLAN_PROMPT_INSTRUCTIONS = [
|
|
|
49
49
|
'Prefer native integrations and structured tools before browser automation or generic shell commands.',
|
|
50
50
|
'For external actions, include a step to draft or confirm before sending unless the user already gave explicit current-session approval.',
|
|
51
51
|
'For code or config changes, include inspection, scoped edit, and verification steps.',
|
|
52
|
-
'For
|
|
52
|
+
'For tasks that run later, make the future prompt self-contained and include notification conditions.',
|
|
53
53
|
];
|
|
54
54
|
const VERIFIER_PROMPT_INSTRUCTIONS = [
|
|
55
55
|
'Verify whether the draft final reply is adequately supported by the gathered evidence.',
|
|
@@ -58,14 +58,14 @@ const VERIFIER_PROMPT_INSTRUCTIONS = [
|
|
|
58
58
|
'A non-zero execute_command exit code means partial or failed shell evidence. Do not treat later sections of a chained shell command as observed unless they were verified separately.',
|
|
59
59
|
'A successful send_message or make_call means outbound delivery succeeded in this run unless a later messaging tool failed.',
|
|
60
60
|
'Any claim that an outbound action already happened (sent/submitted/called/"already done") must be backed by a successful outbound tool execution in this run. If not backed, rewrite the reply to "not sent yet" and provide a draft or next concrete step.',
|
|
61
|
-
'A successful
|
|
61
|
+
'A successful create_task or update_task tool call is required before claiming a task schedule changed.',
|
|
62
62
|
'If external evidence conflicts with memory, history, or another tool result, preserve the uncertainty instead of flattening it into a single confident claim.',
|
|
63
63
|
];
|
|
64
64
|
const EXECUTION_GUIDANCE_ACTION_LINES = [
|
|
65
65
|
'Act end-to-end. Run independent searches or inspections in parallel when possible. Prefer native integration tools and structured APIs over browser automation or shell scraping. Use exact IDs and required parameters; list or search first when you do not have them.',
|
|
66
66
|
'Use send_interim_update sparingly when a short real update or question would help.',
|
|
67
67
|
'When you must ask for missing required user input, ask once, then wait for the reply instead of re-asking in the same run.',
|
|
68
|
-
'For outbound messages, calls, emails, shared edits, installs, restarts, or
|
|
68
|
+
'For outbound messages, calls, emails, shared edits, installs, restarts, or task mutations, verify the action result before claiming it happened. If user confirmation is required and missing, draft or ask instead of sending.',
|
|
69
69
|
'Retry with alternative tools or approaches when one path fails. If evidence is still insufficient, say so explicitly instead of guessing.',
|
|
70
70
|
];
|
|
71
71
|
|
|
@@ -155,7 +155,7 @@ function compactToolResult(toolName, toolArgs = {}, toolResult, options = {}) {
|
|
|
155
155
|
});
|
|
156
156
|
break;
|
|
157
157
|
|
|
158
|
-
case '
|
|
158
|
+
case 'list_tasks':
|
|
159
159
|
envelope = trimObject({
|
|
160
160
|
tool: toolName,
|
|
161
161
|
status: toolResult?.success === false || toolResult?.error ? 'error' : 'ok',
|
|
@@ -167,9 +167,8 @@ function compactToolResult(toolName, toolArgs = {}, toolResult, options = {}) {
|
|
|
167
167
|
? toolResult.tasks.slice(0, 8).map((task) => trimObject({
|
|
168
168
|
id: task?.id,
|
|
169
169
|
name: task?.name,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
oneTime: task?.oneTime,
|
|
170
|
+
triggerType: task?.triggerType,
|
|
171
|
+
triggerSummary: task?.triggerSummary,
|
|
173
172
|
enabled: task?.enabled,
|
|
174
173
|
...(task?.model ? { model: task.model } : {})
|
|
175
174
|
}))
|
|
@@ -217,10 +216,9 @@ function compactToolResult(toolName, toolArgs = {}, toolResult, options = {}) {
|
|
|
217
216
|
case 'memory_update_core':
|
|
218
217
|
case 'memory_read':
|
|
219
218
|
case 'memory_write':
|
|
220
|
-
case '
|
|
221
|
-
case '
|
|
222
|
-
case '
|
|
223
|
-
case 'update_scheduled_task':
|
|
219
|
+
case 'create_task':
|
|
220
|
+
case 'delete_task':
|
|
221
|
+
case 'update_task':
|
|
224
222
|
case 'create_ai_widget':
|
|
225
223
|
case 'update_ai_widget':
|
|
226
224
|
case 'delete_ai_widget':
|