amalgm 0.1.51 → 0.1.53

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 (71) hide show
  1. package/lib/tunnel-events.js +48 -23
  2. package/package.json +2 -2
  3. package/runtime/lib/harnesses.js +12 -4
  4. package/runtime/scripts/amalgm-mcp/agents/store.js +5 -5
  5. package/runtime/scripts/amalgm-mcp/{artifacts → apps}/advertise.js +39 -24
  6. package/runtime/scripts/amalgm-mcp/apps/rest.js +144 -0
  7. package/runtime/scripts/amalgm-mcp/apps/store.js +171 -0
  8. package/runtime/scripts/amalgm-mcp/apps/supervisor.js +439 -0
  9. package/runtime/scripts/amalgm-mcp/apps/tools.js +176 -0
  10. package/runtime/scripts/amalgm-mcp/automations/cell-references.js +237 -0
  11. package/runtime/scripts/amalgm-mcp/automations/context.js +41 -0
  12. package/runtime/scripts/amalgm-mcp/automations/rest.js +148 -0
  13. package/runtime/scripts/amalgm-mcp/automations/runner.js +613 -0
  14. package/runtime/scripts/amalgm-mcp/automations/scheduler.js +90 -0
  15. package/runtime/scripts/amalgm-mcp/automations/store.js +1125 -0
  16. package/runtime/scripts/amalgm-mcp/automations/tool-actions.js +177 -0
  17. package/runtime/scripts/amalgm-mcp/automations/tools.js +418 -0
  18. package/runtime/scripts/amalgm-mcp/automations/validator.js +225 -0
  19. package/runtime/scripts/amalgm-mcp/browser/agent-browser.js +547 -0
  20. package/runtime/scripts/amalgm-mcp/browser/electron-bridge.js +222 -0
  21. package/runtime/scripts/amalgm-mcp/browser/page.js +13 -631
  22. package/runtime/scripts/amalgm-mcp/browser/tools.js +9 -7
  23. package/runtime/scripts/amalgm-mcp/config.js +33 -48
  24. package/runtime/scripts/amalgm-mcp/deps.js +1 -31
  25. package/runtime/scripts/amalgm-mcp/events/ingress.js +50 -42
  26. package/runtime/scripts/amalgm-mcp/events/internal-workflows.js +169 -0
  27. package/runtime/scripts/amalgm-mcp/events/matcher.js +45 -14
  28. package/runtime/scripts/amalgm-mcp/events/store.js +106 -57
  29. package/runtime/scripts/amalgm-mcp/index.js +12 -14
  30. package/runtime/scripts/amalgm-mcp/lib/prefs.js +229 -65
  31. package/runtime/scripts/amalgm-mcp/lib/tool-result.js +13 -27
  32. package/runtime/scripts/amalgm-mcp/server/core-tools.js +2 -3
  33. package/runtime/scripts/amalgm-mcp/server/http.js +106 -56
  34. package/runtime/scripts/amalgm-mcp/slack/inbound.js +1 -1
  35. package/runtime/scripts/amalgm-mcp/state/db.js +119 -0
  36. package/runtime/scripts/amalgm-mcp/state/snapshot.js +16 -3
  37. package/runtime/scripts/amalgm-mcp/tasks/executor.js +1 -1
  38. package/runtime/scripts/amalgm-mcp/tests/automations-store-runner.test.js +348 -0
  39. package/runtime/scripts/amalgm-mcp/tests/events-matcher.test.js +23 -0
  40. package/runtime/scripts/amalgm-mcp/tests/workflows-store-runner.test.js +67 -0
  41. package/runtime/scripts/amalgm-mcp/toolbox/tools.js +16 -3
  42. package/runtime/scripts/amalgm-mcp/workflows/compiler.js +222 -0
  43. package/runtime/scripts/amalgm-mcp/workflows/runner.js +593 -0
  44. package/runtime/scripts/amalgm-mcp/workflows/store.js +237 -0
  45. package/runtime/scripts/chat-core/adapters/claude.js +2 -1
  46. package/runtime/scripts/chat-core/auth.js +82 -12
  47. package/runtime/scripts/chat-core/contract.js +5 -1
  48. package/runtime/scripts/chat-core/engine.js +103 -62
  49. package/runtime/scripts/chat-core/event-schema.js +8 -0
  50. package/runtime/scripts/chat-core/events.js +5 -0
  51. package/runtime/scripts/chat-core/normalizers/codex.js +13 -1
  52. package/runtime/scripts/chat-core/parts.js +21 -6
  53. package/runtime/scripts/chat-core/sse.js +3 -0
  54. package/runtime/scripts/chat-core/tests/auth.test.js +84 -6
  55. package/runtime/scripts/chat-core/tests/engine.test.js +312 -0
  56. package/runtime/scripts/chat-core/tests/native-config.test.js +23 -0
  57. package/runtime/scripts/chat-core/tool-shape.js +4 -4
  58. package/runtime/scripts/chat-core/tooling/active-memory.js +5 -4
  59. package/runtime/scripts/chat-core/tooling/native-binaries.js +34 -9
  60. package/runtime/scripts/chat-core/tooling/native-config.js +34 -3
  61. package/runtime/scripts/local-gateway.js +34 -27
  62. package/runtime/scripts/platform-context.txt +76 -94
  63. package/runtime/scripts/amalgm-mcp/artifacts/rest.js +0 -103
  64. package/runtime/scripts/amalgm-mcp/artifacts/store.js +0 -157
  65. package/runtime/scripts/amalgm-mcp/artifacts/supervisor.js +0 -439
  66. package/runtime/scripts/amalgm-mcp/artifacts/tools.js +0 -176
  67. package/runtime/scripts/amalgm-mcp/events/executor.js +0 -258
  68. package/runtime/scripts/amalgm-mcp/events/rest.js +0 -214
  69. package/runtime/scripts/amalgm-mcp/events/tools.js +0 -323
  70. package/runtime/scripts/amalgm-mcp/tasks/rest.js +0 -110
  71. package/runtime/scripts/amalgm-mcp/tasks/tools.js +0 -416
@@ -32,32 +32,18 @@ function humanizeToolName(toolName) {
32
32
 
33
33
  function buildActionDescriptor(toolName, args = {}) {
34
34
  switch (toolName) {
35
- case 'scheduled_tasks_create':
36
- return `Create scheduled task${quoteIfPresent(args.name)}`;
37
- case 'scheduled_tasks_list':
38
- return args.enabled_only ? 'List enabled scheduled tasks' : 'List scheduled tasks';
39
- case 'scheduled_tasks_get':
40
- return `Get scheduled task${quoteIfPresent(args.task_id)}`;
41
- case 'scheduled_tasks_update':
42
- return `Update scheduled task${quoteIfPresent(args.task_id || args.name)}`;
43
- case 'scheduled_tasks_delete':
44
- return `Delete scheduled task${quoteIfPresent(args.task_id)}`;
45
- case 'scheduled_tasks_run_now':
46
- return `Run scheduled task now${quoteIfPresent(args.task_id)}`;
47
- case 'scheduled_tasks_cancel':
48
- return `Cancel scheduled task${quoteIfPresent(args.task_id)}`;
49
- case 'scheduled_tasks_history':
50
- return `Get scheduled task history${quoteIfPresent(args.task_id)}`;
51
- case 'event_triggers_create':
52
- return `Create event trigger${quoteIfPresent(args.name)}`;
53
- case 'event_triggers_list':
54
- return args.enabled_only ? 'List enabled event triggers' : 'List event triggers';
55
- case 'event_triggers_get':
56
- return `Get event trigger${quoteIfPresent(args.trigger_id)}`;
57
- case 'event_triggers_update':
58
- return `Update event trigger${quoteIfPresent(args.trigger_id || args.name)}`;
59
- case 'event_triggers_delete':
60
- return `Delete event trigger${quoteIfPresent(args.trigger_id)}`;
35
+ case 'automations_create':
36
+ return `Create automation${quoteIfPresent(args.name)}`;
37
+ case 'automations_list':
38
+ return args.enabled_only ? 'List enabled automations' : 'List automations';
39
+ case 'automations_get':
40
+ return `Get automation${quoteIfPresent(args.automation_id)}`;
41
+ case 'automations_update':
42
+ return `Update automation${quoteIfPresent(args.automation_id || args.name)}`;
43
+ case 'automations_delete':
44
+ return `Delete automation${quoteIfPresent(args.automation_id)}`;
45
+ case 'automations_run_now':
46
+ return `Run automation now${quoteIfPresent(args.automation_id)}`;
61
47
  case 'notify_user':
62
48
  return `Notify user${quoteIfPresent(args.subject || args.level || 'info')}`;
63
49
  case 'agents_list':
@@ -126,7 +112,7 @@ function buildActionDescriptor(toolName, args = {}) {
126
112
  case 'browser_cua_double_click':
127
113
  return 'Double-click browser coordinates';
128
114
  case 'browser_cua_move':
129
- return 'Move browser cursor';
115
+ return 'Move browser mouse';
130
116
  case 'browser_cua_scroll':
131
117
  return 'Scroll browser';
132
118
  case 'browser_cua_type':
@@ -9,11 +9,10 @@
9
9
  */
10
10
 
11
11
  const CORE_TOOLS = [
12
- ...require('../tasks/tools'),
13
- ...require('../events/tools'),
12
+ ...require('../automations/tools'),
14
13
  ...require('../notify'),
15
14
  ...require('../agents/tools'),
16
- ...require('../artifacts/tools'),
15
+ ...require('../apps/tools'),
17
16
  ...require('../browser/tools'),
18
17
  ];
19
18
 
@@ -4,15 +4,11 @@
4
4
  * Route table:
5
5
  * GET /healthz → stats snapshot
6
6
  * * /mcp → MCP streamable-HTTP (delegated to SDK transport)
7
- * POST /events → webhook ingress (signature match + agent run)
7
+ * POST /events → webhook ingress (signature match + workflow run)
8
8
  * GET /events → recent events (in-memory ring buffer)
9
- * GET /tasks/list → list tasks
10
- * POST /tasks/create → create task
11
- * POST /tasks/update → update task
12
- * POST /tasks/delete → delete task
13
- * POST /tasks/run-now → run task immediately
14
- * GET /event-triggers/list → list triggers
15
- * POST /event-triggers/{create,update,delete}
9
+ * GET /automations/list → list automations/triggers/workflows
10
+ * POST /automations/{get,create,update,delete,run-now,validate}
11
+ * POST /automations/triggers/{update,delete}
16
12
  * GET /workspace/projects → list projects
17
13
  * POST /workspace/import → copy a local folder into workspaces
18
14
  * POST /github/clone → clone repository onto the computer
@@ -29,13 +25,19 @@
29
25
  * POST /mcp-connections → save/update MCP connection
30
26
  * DELETE /mcp-connections → remove MCP connection
31
27
  * GET /toolbox → list local ToolboxDB tools/actions
28
+ * GET /toolbox/tools?id=... → get one toolbox tool
32
29
  * POST /toolbox/tools → save/update a toolbox tool
30
+ * PATCH /toolbox/tools → partially update a toolbox tool
33
31
  * DELETE /toolbox/tools → remove a toolbox tool
32
+ * GET /toolbox/actions?id=... → get one toolbox action
34
33
  * POST /toolbox/actions → save/update a toolbox action
34
+ * PATCH /toolbox/actions → partially update a toolbox action
35
35
  * DELETE /toolbox/actions → remove a toolbox action
36
36
  * GET /state/snapshot → current Local Live Store snapshot
37
37
  * GET /state/events → replay Local Live Store events
38
38
  * GET /state/stream → SSE Local Live Store stream
39
+ * GET /user-preferences → current local user preferences
40
+ * PUT /user-preferences → update local user preferences
39
41
  * POST /local/init → init local computer state
40
42
  * GET /local/credentials → discover local agent auth
41
43
  * POST /local/credentials/resolve → resolve spawn env for agent auth
@@ -46,9 +48,10 @@
46
48
  * DELETE /credentials → remove local Amalgm credential profile
47
49
  * POST /user-api-keys → legacy save BYOK credential file
48
50
  * DELETE /user-api-keys → legacy remove BYOK credential file
49
- * GET /artifacts/list → list artifacts
50
- * GET /artifacts/routes artifact DNS routes for tunnel adverts
51
- * POST /artifacts/{register,redeploy,start,stop,connect-dns,disconnect-dns}
51
+ * GET /apps/list → list apps
52
+ * GET /apps/routes app DNS routes for tunnel adverts
53
+ * POST /apps/{register,redeploy,start,stop,connect-dns,disconnect-dns}
54
+ * Legacy /artifacts/* aliases remain for older clients during the rename.
52
55
  * GET /agents/list → list agents
53
56
  * POST /agents/{get,create,update,delete}
54
57
  */
@@ -58,17 +61,17 @@ const { PORT, STORAGE_DIR } = require('../config');
58
61
  const { hasSupabase } = require('../lib/supabase');
59
62
  const { authorizeRuntimeHttp } = require('../../runtime-auth');
60
63
 
61
- const { loadTasks } = require('../tasks/store');
62
64
  const { loadAgents } = require('../agents/store');
63
- const { loadArtifacts } = require('../artifacts/store');
65
+ const { loadApps } = require('../apps/store');
64
66
  const { activeAgentConversations } = require('../agents/talk');
65
- const { getSchedulerStatus, startScheduler, stopScheduler } = require('../tasks/scheduler');
67
+ const { listAutomations } = require('../automations/store');
68
+ const { startAutomationScheduler, stopAutomationScheduler } = require('../automations/scheduler');
66
69
  const { createTransport } = require('./mcp');
67
70
  const { handleEventsPost } = require('../events/ingress');
68
71
  const eventsRing = require('../events/ring-buffer');
69
- const eventsRest = require('../events/rest');
72
+ const automationsRest = require('../automations/rest');
70
73
  const agentsRest = require('../agents/rest');
71
- const artifactsRest = require('../artifacts/rest');
74
+ const appsRest = require('../apps/rest');
72
75
  const emailInbound = require('../email/inbound');
73
76
  const slackInbound = require('../slack/inbound');
74
77
  const fsRest = require('../fs/rest');
@@ -76,7 +79,7 @@ const localRest = require('../local/rest');
76
79
  const mcpConnectionsRest = require('../mcp-connections/rest');
77
80
  const toolboxRest = require('../toolbox/rest');
78
81
  const stateRest = require('../state/rest');
79
- const tasksRest = require('../tasks/rest');
82
+ const prefsStore = require('../lib/prefs');
80
83
  const userApiKeysRest = require('../user-api-keys/rest');
81
84
  const credentialsRest = require('../credentials/rest');
82
85
  const workspaceRest = require('../workspace/rest');
@@ -112,14 +115,13 @@ function createServer() {
112
115
 
113
116
  // ── Health ──────────────────────────────────────────────────────────────
114
117
  if (req.url === '/healthz') {
115
- const tasks = loadTasks();
116
118
  const agents = loadAgents();
119
+ const automations = listAutomations();
117
120
  return sendJson(200, {
118
121
  status: 'ok',
119
- tasks: tasks.tasks.length,
120
- enabledTasks: tasks.tasks.filter((t) => t.enabled).length,
121
- scheduler: getSchedulerStatus(),
122
- artifacts: loadArtifacts().artifacts.length,
122
+ automations: automations.length,
123
+ enabledAutomations: automations.filter((automation) => automation.enabled !== false).length,
124
+ apps: loadApps().apps.length,
123
125
  customAgents: agents.agents.length,
124
126
  activeAgentConversations: activeAgentConversations.size,
125
127
  });
@@ -136,6 +138,15 @@ function createServer() {
136
138
  return stateRest.handleStream(req, res, getQuery());
137
139
  }
138
140
 
141
+ // ── /user-preferences REST ───────────────────────────────────────────
142
+ if (req.url === '/user-preferences' && req.method === 'GET') {
143
+ return sendJson(200, prefsStore.readUserPreferences());
144
+ }
145
+ if (req.url === '/user-preferences' && req.method === 'PUT') {
146
+ const preferences = prefsStore.updateUserPreferences(await readJsonBody());
147
+ return sendJson(200, { success: true, preferences });
148
+ }
149
+
139
150
  // ── /agents (REST, not MCP) ─────────────────────────────────────────────
140
151
  if (req.url === '/agents/list' && req.method === 'GET') {
141
152
  return agentsRest.handleList(sendJson);
@@ -161,35 +172,33 @@ function createServer() {
161
172
  return sendJson(200, { events: eventsRing.recent(50) });
162
173
  }
163
174
 
164
- // ── /tasks REST ─────────────────────────────────────────────────────────
165
- if (req.url === '/tasks/list' && req.method === 'GET') {
166
- return tasksRest.handleList(sendJson);
175
+ // ── /automations REST ─────────────────────────────────────────────────
176
+ if (req.url === '/automations/list' && req.method === 'GET') {
177
+ return automationsRest.handleList(req, sendJson);
167
178
  }
168
- if (req.url === '/tasks/create' && req.method === 'POST') {
169
- return tasksRest.handleCreate(await readJsonBody(), sendJson);
179
+ if (req.url === '/automations/get' && req.method === 'POST') {
180
+ return automationsRest.handleGet(await readJsonBody(), sendJson);
170
181
  }
171
- if (req.url === '/tasks/update' && req.method === 'POST') {
172
- return tasksRest.handleUpdate(await readJsonBody(), sendJson);
182
+ if (req.url === '/automations/create' && req.method === 'POST') {
183
+ return automationsRest.handleCreate(await readJsonBody(), sendJson);
173
184
  }
174
- if (req.url === '/tasks/delete' && req.method === 'POST') {
175
- return tasksRest.handleDelete(await readJsonBody(), sendJson);
185
+ if (req.url === '/automations/update' && req.method === 'POST') {
186
+ return automationsRest.handleUpdate(await readJsonBody(), sendJson);
176
187
  }
177
- if (req.url === '/tasks/run-now' && req.method === 'POST') {
178
- return tasksRest.handleRunNow(await readJsonBody(), sendJson);
188
+ if (req.url === '/automations/delete' && req.method === 'POST') {
189
+ return automationsRest.handleDelete(await readJsonBody(), sendJson);
179
190
  }
180
-
181
- // ── /event-triggers REST ────────────────────────────────────────────────
182
- if (req.url === '/event-triggers/list' && req.method === 'GET') {
183
- return eventsRest.handleList(req, sendJson);
191
+ if (req.url === '/automations/run-now' && req.method === 'POST') {
192
+ return automationsRest.handleRunNow(await readJsonBody(), sendJson);
184
193
  }
185
- if (req.url === '/event-triggers/create' && req.method === 'POST') {
186
- return eventsRest.handleCreate(await readJsonBody(), sendJson);
194
+ if (req.url === '/automations/validate' && req.method === 'POST') {
195
+ return automationsRest.handleValidate(await readJsonBody(), sendJson);
187
196
  }
188
- if (req.url === '/event-triggers/update' && req.method === 'POST') {
189
- return eventsRest.handleUpdate(await readJsonBody(), sendJson);
197
+ if (req.url === '/automations/triggers/update' && req.method === 'POST') {
198
+ return automationsRest.handleTriggerUpdate(await readJsonBody(), sendJson);
190
199
  }
191
- if (req.url === '/event-triggers/delete' && req.method === 'POST') {
192
- return eventsRest.handleDelete(await readJsonBody(), sendJson);
200
+ if (req.url === '/automations/triggers/delete' && req.method === 'POST') {
201
+ return automationsRest.handleTriggerDelete(await readJsonBody(), sendJson);
193
202
  }
194
203
 
195
204
  // ── /workspace REST ─────────────────────────────────────────────────────
@@ -265,15 +274,27 @@ function createServer() {
265
274
  if (req.url === '/toolbox' && req.method === 'GET') {
266
275
  return toolboxRest.handleList(sendJson);
267
276
  }
277
+ if ((req.url === '/toolbox/tools' || req.url.startsWith('/toolbox/tools?')) && req.method === 'GET') {
278
+ return toolboxRest.handleGetTool(getQuery(), sendJson);
279
+ }
268
280
  if (req.url === '/toolbox/tools' && req.method === 'POST') {
269
281
  return toolboxRest.handleUpsertTool(await readJsonBody(), sendJson);
270
282
  }
283
+ if (req.url === '/toolbox/tools' && req.method === 'PATCH') {
284
+ return toolboxRest.handleUpdateTool(await readJsonBody(), sendJson);
285
+ }
271
286
  if (req.url === '/toolbox/tools' && req.method === 'DELETE') {
272
287
  return toolboxRest.handleDeleteTool(await readJsonBody(), sendJson);
273
288
  }
289
+ if ((req.url === '/toolbox/actions' || req.url.startsWith('/toolbox/actions?')) && req.method === 'GET') {
290
+ return toolboxRest.handleGetAction(getQuery(), sendJson);
291
+ }
274
292
  if (req.url === '/toolbox/actions' && req.method === 'POST') {
275
293
  return toolboxRest.handleUpsertAction(await readJsonBody(), sendJson);
276
294
  }
295
+ if (req.url === '/toolbox/actions' && req.method === 'PATCH') {
296
+ return toolboxRest.handleUpdateAction(await readJsonBody(), sendJson);
297
+ }
277
298
  if (req.url === '/toolbox/actions' && req.method === 'DELETE') {
278
299
  return toolboxRest.handleDeleteAction(await readJsonBody(), sendJson);
279
300
  }
@@ -316,33 +337,62 @@ function createServer() {
316
337
  return userApiKeysRest.handleDelete(sendJson);
317
338
  }
318
339
 
319
- // ── /artifacts REST ────────────────────────────────────────────────────
340
+ // ── /apps REST ────────────────────────────────────────────────────
341
+ if (req.url === '/apps/list' && req.method === 'GET') {
342
+ return appsRest.handleList(sendJson);
343
+ }
344
+ if (req.url === '/apps/routes' && req.method === 'GET') {
345
+ return appsRest.handleRoutes(sendJson);
346
+ }
347
+ if (req.url === '/apps/register' && req.method === 'POST') {
348
+ return appsRest.handleRegister(await readJsonBody(), sendJson);
349
+ }
350
+ if (req.url === '/apps/redeploy' && req.method === 'POST') {
351
+ return appsRest.handleRedeploy(await readJsonBody(), sendJson);
352
+ }
353
+ if (req.url === '/apps/start' && req.method === 'POST') {
354
+ return appsRest.handleStart(await readJsonBody(), sendJson);
355
+ }
356
+ if (req.url === '/apps/stop' && req.method === 'POST') {
357
+ return appsRest.handleStop(await readJsonBody(), sendJson);
358
+ }
359
+ if (req.url === '/apps/delete' && req.method === 'POST') {
360
+ return appsRest.handleDelete(await readJsonBody(), sendJson);
361
+ }
362
+ if (req.url === '/apps/connect-dns' && req.method === 'POST') {
363
+ return appsRest.handleConnectDns(await readJsonBody(), sendJson);
364
+ }
365
+ if (req.url === '/apps/disconnect-dns' && req.method === 'POST') {
366
+ return appsRest.handleDisconnectDns(await readJsonBody(), sendJson);
367
+ }
368
+
369
+ // ── legacy /artifacts REST aliases ────────────────────────────────
320
370
  if (req.url === '/artifacts/list' && req.method === 'GET') {
321
- return artifactsRest.handleList(sendJson);
371
+ return appsRest.handleList(sendJson, { legacy: true });
322
372
  }
323
373
  if (req.url === '/artifacts/routes' && req.method === 'GET') {
324
- return artifactsRest.handleRoutes(sendJson);
374
+ return appsRest.handleRoutes(sendJson, { legacy: true });
325
375
  }
326
376
  if (req.url === '/artifacts/register' && req.method === 'POST') {
327
- return artifactsRest.handleRegister(await readJsonBody(), sendJson);
377
+ return appsRest.handleRegister(await readJsonBody(), sendJson, { legacy: true });
328
378
  }
329
379
  if (req.url === '/artifacts/redeploy' && req.method === 'POST') {
330
- return artifactsRest.handleRedeploy(await readJsonBody(), sendJson);
380
+ return appsRest.handleRedeploy(await readJsonBody(), sendJson, { legacy: true });
331
381
  }
332
382
  if (req.url === '/artifacts/start' && req.method === 'POST') {
333
- return artifactsRest.handleStart(await readJsonBody(), sendJson);
383
+ return appsRest.handleStart(await readJsonBody(), sendJson, { legacy: true });
334
384
  }
335
385
  if (req.url === '/artifacts/stop' && req.method === 'POST') {
336
- return artifactsRest.handleStop(await readJsonBody(), sendJson);
386
+ return appsRest.handleStop(await readJsonBody(), sendJson, { legacy: true });
337
387
  }
338
388
  if (req.url === '/artifacts/delete' && req.method === 'POST') {
339
- return artifactsRest.handleDelete(await readJsonBody(), sendJson);
389
+ return appsRest.handleDelete(await readJsonBody(), sendJson, { legacy: true });
340
390
  }
341
391
  if (req.url === '/artifacts/connect-dns' && req.method === 'POST') {
342
- return artifactsRest.handleConnectDns(await readJsonBody(), sendJson);
392
+ return appsRest.handleConnectDns(await readJsonBody(), sendJson, { legacy: true });
343
393
  }
344
394
  if (req.url === '/artifacts/disconnect-dns' && req.method === 'POST') {
345
- return artifactsRest.handleDisconnectDns(await readJsonBody(), sendJson);
395
+ return appsRest.handleDisconnectDns(await readJsonBody(), sendJson, { legacy: true });
346
396
  }
347
397
 
348
398
  res.writeHead(404, { 'Content-Type': 'text/plain' });
@@ -363,9 +413,9 @@ async function listen() {
363
413
  console.log(
364
414
  `[AmalgmMCP] Supabase: ${hasSupabase() ? 'connected' : 'not configured (runs are local-only)'}`,
365
415
  );
366
- // Hydrate user model prefs non-blocking (requires Supabase).
416
+ // Hydrate user model prefs non-blocking from local SQLite.
367
417
  require('../lib/prefs').hydrateModelPreferences().catch(() => {});
368
- startScheduler();
418
+ startAutomationScheduler();
369
419
  });
370
420
 
371
421
  // Graceful shutdown. Running tasks get aborted via SIGTERM → process exit;
@@ -373,7 +423,7 @@ async function listen() {
373
423
  // runtime teardown paths.
374
424
  function shutdown() {
375
425
  console.log('[AmalgmMCP] Shutting down...');
376
- stopScheduler();
426
+ stopAutomationScheduler();
377
427
  httpServer.close(() => process.exit(0));
378
428
  setTimeout(() => process.exit(0), 5000);
379
429
  }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * The api-proxy verifies Slack, resolves/creates the Supabase session, then
5
5
  * forwards a clean Slack payload here. This local route runs the agent through
6
- * the same chat-server path as email/tasks/events, then asks the proxy to send
6
+ * the same chat-server path as email/automations, then asks the proxy to send
7
7
  * the Slack reply with the bot token that lives outside the local computer.
8
8
  */
9
9
 
@@ -61,6 +61,125 @@ function migrate(database = openLocalDb()) {
61
61
  updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
62
62
  );
63
63
 
64
+ CREATE TABLE IF NOT EXISTS automation_workflows (
65
+ id TEXT PRIMARY KEY,
66
+ name TEXT NOT NULL,
67
+ description TEXT,
68
+ enabled INTEGER NOT NULL DEFAULT 1,
69
+ project_path TEXT,
70
+ workflow_text TEXT NOT NULL,
71
+ workflow_ir_json TEXT,
72
+ compiler_errors_json TEXT,
73
+ allowlist_json TEXT NOT NULL DEFAULT '{}',
74
+ limits_json TEXT NOT NULL DEFAULT '{}',
75
+ created_at TEXT NOT NULL,
76
+ updated_at TEXT NOT NULL,
77
+ workflow_json TEXT NOT NULL
78
+ );
79
+
80
+ CREATE INDEX IF NOT EXISTS automation_workflows_enabled_idx
81
+ ON automation_workflows(enabled);
82
+
83
+ CREATE TABLE IF NOT EXISTS automation_definitions (
84
+ id TEXT PRIMARY KEY,
85
+ name TEXT NOT NULL,
86
+ description TEXT,
87
+ enabled INTEGER NOT NULL DEFAULT 1,
88
+ project_path TEXT,
89
+ workflow_id TEXT NOT NULL,
90
+ created_at TEXT NOT NULL,
91
+ updated_at TEXT NOT NULL,
92
+ automation_json TEXT NOT NULL,
93
+ FOREIGN KEY(workflow_id) REFERENCES automation_workflows(id) ON DELETE CASCADE
94
+ );
95
+
96
+ CREATE INDEX IF NOT EXISTS automation_definitions_workflow_idx
97
+ ON automation_definitions(workflow_id);
98
+
99
+ CREATE INDEX IF NOT EXISTS automation_definitions_enabled_idx
100
+ ON automation_definitions(enabled);
101
+
102
+ CREATE TABLE IF NOT EXISTS automation_triggers (
103
+ id TEXT PRIMARY KEY,
104
+ automation_id TEXT NOT NULL,
105
+ kind TEXT NOT NULL,
106
+ name TEXT NOT NULL,
107
+ description TEXT,
108
+ enabled INTEGER NOT NULL DEFAULT 1,
109
+ schedule_kind TEXT,
110
+ schedule_json TEXT,
111
+ next_run_at TEXT,
112
+ last_fired_at TEXT,
113
+ source TEXT,
114
+ event TEXT,
115
+ source_url TEXT,
116
+ webhook_url TEXT,
117
+ secret TEXT,
118
+ created_at TEXT NOT NULL,
119
+ updated_at TEXT NOT NULL,
120
+ trigger_json TEXT NOT NULL,
121
+ FOREIGN KEY(automation_id) REFERENCES automation_definitions(id) ON DELETE CASCADE
122
+ );
123
+
124
+ CREATE INDEX IF NOT EXISTS automation_triggers_automation_idx
125
+ ON automation_triggers(automation_id);
126
+
127
+ CREATE INDEX IF NOT EXISTS automation_triggers_kind_idx
128
+ ON automation_triggers(kind);
129
+
130
+ CREATE INDEX IF NOT EXISTS automation_triggers_due_idx
131
+ ON automation_triggers(kind, enabled, next_run_at);
132
+
133
+ CREATE TABLE IF NOT EXISTS automation_runs (
134
+ id TEXT PRIMARY KEY,
135
+ automation_id TEXT NOT NULL,
136
+ trigger_id TEXT,
137
+ workflow_id TEXT,
138
+ status TEXT NOT NULL,
139
+ started_at TEXT,
140
+ finished_at TEXT,
141
+ project_path TEXT,
142
+ error TEXT,
143
+ run_json TEXT NOT NULL,
144
+ created_at TEXT NOT NULL,
145
+ updated_at TEXT NOT NULL,
146
+ FOREIGN KEY(automation_id) REFERENCES automation_definitions(id) ON DELETE CASCADE,
147
+ FOREIGN KEY(trigger_id) REFERENCES automation_triggers(id) ON DELETE SET NULL,
148
+ FOREIGN KEY(workflow_id) REFERENCES automation_workflows(id) ON DELETE SET NULL
149
+ );
150
+
151
+ CREATE INDEX IF NOT EXISTS automation_runs_automation_updated_idx
152
+ ON automation_runs(automation_id, updated_at DESC);
153
+
154
+ CREATE INDEX IF NOT EXISTS automation_runs_trigger_updated_idx
155
+ ON automation_runs(trigger_id, updated_at DESC);
156
+
157
+ CREATE INDEX IF NOT EXISTS automation_runs_status_idx
158
+ ON automation_runs(status);
159
+
160
+ CREATE TABLE IF NOT EXISTS workflow_cell_runs (
161
+ id TEXT PRIMARY KEY,
162
+ run_id TEXT NOT NULL,
163
+ automation_id TEXT NOT NULL,
164
+ trigger_id TEXT,
165
+ workflow_id TEXT,
166
+ cell_name TEXT NOT NULL,
167
+ cell_kind TEXT NOT NULL,
168
+ status TEXT NOT NULL,
169
+ started_at TEXT,
170
+ finished_at TEXT,
171
+ output_json TEXT,
172
+ error TEXT,
173
+ cell_json TEXT NOT NULL,
174
+ created_at TEXT NOT NULL,
175
+ updated_at TEXT NOT NULL,
176
+ FOREIGN KEY(run_id) REFERENCES automation_runs(id) ON DELETE CASCADE,
177
+ FOREIGN KEY(automation_id) REFERENCES automation_definitions(id) ON DELETE CASCADE
178
+ );
179
+
180
+ CREATE INDEX IF NOT EXISTS workflow_cell_runs_run_idx
181
+ ON workflow_cell_runs(run_id);
182
+
64
183
  CREATE TABLE IF NOT EXISTS scheduled_tasks (
65
184
  id TEXT PRIMARY KEY,
66
185
  name TEXT NOT NULL,
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { currentSeq } = require('./events');
4
4
 
5
- const DEFAULT_RESOURCES = ['tasks', 'task_runs', 'event_runs', 'event_triggers', 'agents', 'artifacts', 'toolbox', 'tools', 'tool_actions', 'hooks', 'projects', 'workspaces', 'memories', 'mcp_connections'];
5
+ const DEFAULT_RESOURCES = ['automations', 'triggers', 'workflows', 'automation_runs', 'workflow_cell_runs', 'agents', 'apps', 'toolbox', 'tools', 'tool_actions', 'hooks', 'projects', 'workspaces', 'memories', 'mcp_connections'];
6
6
 
7
7
  function normalizeResources(resources) {
8
8
  const values = resources ? String(resources).split(',') : DEFAULT_RESOURCES;
@@ -12,6 +12,16 @@ function normalizeResources(resources) {
12
12
 
13
13
  function readResource(resource, cache) {
14
14
  switch (resource) {
15
+ case 'automations':
16
+ cache.automations ||= require('../automations/store').listAutomations();
17
+ return cache.automations;
18
+ case 'triggers':
19
+ cache.triggers ||= require('../automations/store').listTriggers();
20
+ return cache.triggers;
21
+ case 'automation_runs':
22
+ return require('../automations/store').listAutomationRuns({ limit: 100 });
23
+ case 'workflow_cell_runs':
24
+ return require('../automations/store').listWorkflowCellRuns({ limit: 100 });
15
25
  case 'tasks':
16
26
  return require('../tasks/store').loadTasks().tasks;
17
27
  case 'task_runs':
@@ -20,10 +30,13 @@ function readResource(resource, cache) {
20
30
  return require('../events/store').listEventRuns({ limit: 100 });
21
31
  case 'event_triggers':
22
32
  return require('../events/store').loadEventTriggers().triggers;
33
+ case 'workflows':
34
+ cache.workflows ||= require('../automations/store').listWorkflows();
35
+ return cache.workflows;
23
36
  case 'agents':
24
37
  return require('../agents/store').getAllAgentsWithBuiltins();
25
- case 'artifacts':
26
- return require('../artifacts/store').loadArtifacts().artifacts;
38
+ case 'apps':
39
+ return require('../apps/store').loadApps().apps;
27
40
  case 'toolbox':
28
41
  cache.toolbox ||= require('../toolbox/store').readToolbox();
29
42
  return cache.toolbox;
@@ -8,7 +8,7 @@
8
8
  * 4. Append a run-log JSONL entry and update task metadata.
9
9
  *
10
10
  * Held state (runningTasks) is purely in-process — it tracks abort controllers
11
- * so MCP's scheduled_tasks_cancel can abort a live run.
11
+ * so legacy compatibility callers can abort a live run.
12
12
  */
13
13
 
14
14
  const crypto = require('crypto');