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
@@ -1,416 +0,0 @@
1
- /**
2
- * Scheduled-tasks MCP tools.
3
- *
4
- * 8 tools: create, list, get, update, delete, run_now, cancel, history.
5
- * Each entry is self-contained: name, description, inputSchema, handler.
6
- * Storage lives in ./store.js, execution in ./executor.js.
7
- */
8
-
9
- const crypto = require('crypto');
10
- const { textResult, errorResult } = require('../lib/tool-result');
11
- const { claimTaskRun, loadTasks, saveTasks, readRunLog } = require('./store');
12
- const { validateCronExpr } = require('./scheduler');
13
- const { normalizeTaskSchedule } = require('./schedule-normalization');
14
- const { executeTask, isRunning, getRunning, abortRunning } = require('./executor');
15
- const {
16
- chatInputToLegacyFields,
17
- getChatInputText,
18
- normalizeChatInput,
19
- } = require('../../../lib/chatInput');
20
- const {
21
- DEFAULT_SELECTED_MODELS,
22
- getSelectedModel,
23
- hydrateModelPreferences,
24
- } = require('../lib/prefs');
25
- const credentialAdapter = require('../../credential-adapter');
26
- const activeMemory = require('../../chat-core/tooling/active-memory');
27
-
28
- function resolveTaskHarness(harness, chatInput) {
29
- const candidate =
30
- (chatInput && chatInput.agent && typeof chatInput.agent.harness === 'string' && chatInput.agent.harness)
31
- || (typeof harness === 'string' && harness)
32
- || 'claude_code';
33
- return candidate;
34
- }
35
-
36
- function resolveTaskModel(harness, model) {
37
- if (typeof model === 'string' && model) return model;
38
- return getSelectedModel(harness) || DEFAULT_SELECTED_MODELS[harness] || null;
39
- }
40
-
41
- function resolveTaskAuthMethod(harness, authMethod) {
42
- if (typeof authMethod === 'string' && authMethod) return authMethod;
43
- if (credentialAdapter.VALID_HARNESS_IDS.includes(harness)) {
44
- return credentialAdapter.getPersistedAuthMode(harness);
45
- }
46
- return 'amalgm';
47
- }
48
-
49
- function normalizeTaskRecord(taskLike) {
50
- const harness = resolveTaskHarness(taskLike.harness, taskLike.chatInput);
51
- const model = resolveTaskModel(harness, taskLike.model);
52
- const authMethod = resolveTaskAuthMethod(harness, taskLike.authMethod);
53
- const chatInput = normalizeChatInput(taskLike.chatInput, {
54
- prompt: taskLike.prompt,
55
- harness,
56
- modelId: model,
57
- authMethod,
58
- projectPath: taskLike.projectPath,
59
- cwd: taskLike.projectPath,
60
- });
61
- const legacy = chatInputToLegacyFields(chatInput, {
62
- prompt: taskLike.prompt,
63
- harness,
64
- modelId: model,
65
- authMethod,
66
- projectPath: taskLike.projectPath,
67
- cwd: taskLike.projectPath,
68
- });
69
-
70
- return {
71
- ...taskLike,
72
- chatInput,
73
- prompt: getChatInputText(chatInput, taskLike.prompt) || legacy.prompt,
74
- harness: legacy.harness || harness,
75
- model: legacy.modelId || model,
76
- authMethod: legacy.authMethod || authMethod,
77
- projectPath: legacy.cwd || taskLike.projectPath || null,
78
- };
79
- }
80
-
81
- module.exports = [
82
- {
83
- name: 'scheduled_tasks_create',
84
- description:
85
- 'Create a new scheduled task that runs a prompt on a schedule. Supports cron (recurring), once (one-shot), and interval (fixed ms) schedules.',
86
- inputSchema: {
87
- type: 'object',
88
- properties: {
89
- name: { type: 'string', description: 'Human-readable task name' },
90
- schedule: {
91
- type: 'object',
92
- description: 'Schedule configuration',
93
- properties: {
94
- kind: {
95
- type: 'string',
96
- enum: ['cron', 'once', 'interval'],
97
- description: 'cron=recurring, once=one-shot, interval=fixed ms',
98
- },
99
- expr: { type: 'string', description: 'Cron expression (for kind=cron), e.g. "0 9 * * *"' },
100
- tz: { type: 'string', description: 'IANA timezone (defaults to the local user timezone)' },
101
- at: { type: 'string', description: 'ISO datetime (for kind=once)' },
102
- ms: { type: 'number', description: 'Interval in ms (for kind=interval, min 60000)' },
103
- },
104
- required: ['kind'],
105
- },
106
- description: { type: 'string', description: 'Optional markdown description shown in the UI' },
107
- prompt: { type: 'string', description: 'The prompt to execute when the task runs' },
108
- enabled: { type: 'boolean', description: 'Whether the task is active (default: true)' },
109
- endsAt: { type: 'string', description: 'ISO datetime when the task auto-disables (null = never)' },
110
- maxConcurrentRuns: { type: 'number', description: 'Max parallel executions (default: 1)' },
111
- harness: { type: 'string', description: 'Agent harness (claude_code, codex, opencode)' },
112
- model: { type: 'string', description: 'Model ID to use for execution' },
113
- modelSettings: { type: 'object', description: 'Advanced model settings such as effort and fastMode' },
114
- authMethod: { type: 'string', description: 'Auth method for this task run' },
115
- projectPath: { type: 'string', description: 'Working directory path' },
116
- chatInput: { type: 'object', description: 'Shared chat input shape for the task run' },
117
- },
118
- required: ['name', 'schedule'],
119
- },
120
- async handler({ name, description, schedule, prompt, enabled, endsAt, maxConcurrentRuns, harness, model, modelSettings, authMethod, projectPath, chatInput }) {
121
- const normalizedScheduleResult = normalizeTaskSchedule(schedule);
122
- if (normalizedScheduleResult.error) {
123
- return errorResult(normalizedScheduleResult.error);
124
- }
125
- const normalizedSchedule = normalizedScheduleResult.schedule;
126
-
127
- if (normalizedSchedule.kind === 'cron') {
128
- const err = validateCronExpr(normalizedSchedule.expr, normalizedSchedule.tz);
129
- if (err) return errorResult(`Invalid cron expression: ${err}`);
130
- }
131
- if (!prompt && !chatInput) {
132
- return errorResult('prompt or chatInput is required');
133
- }
134
-
135
- await hydrateModelPreferences();
136
-
137
- const task = normalizeTaskRecord({
138
- id: crypto.randomUUID(),
139
- name,
140
- description: description || '',
141
- enabled: enabled !== false,
142
- schedule: normalizedSchedule,
143
- prompt: prompt || null,
144
- endsAt: endsAt || null,
145
- maxConcurrentRuns: maxConcurrentRuns || 1,
146
- harness: harness || null,
147
- model: model || null,
148
- modelSettings: modelSettings || null,
149
- authMethod: authMethod || null,
150
- projectPath: projectPath || null,
151
- chatInput: chatInput || null,
152
- createdAt: new Date().toISOString(),
153
- lastRunAt: null,
154
- lastStatus: null,
155
- });
156
- const data = loadTasks();
157
- data.tasks.push(task);
158
- saveTasks(data);
159
- activeMemory.ensureConstructMemory({
160
- type: 'task',
161
- id: task.id,
162
- name: task.name,
163
- projectPath: task.projectPath,
164
- }, { source: 'task:create' });
165
- console.log(`[AmalgmMCP] Created task: ${task.id} (${task.name})`);
166
- return textResult(`Task created successfully.\n\n${JSON.stringify(task, null, 2)}`);
167
- },
168
- },
169
- {
170
- name: 'scheduled_tasks_list',
171
- description: 'List all scheduled tasks with their current status',
172
- inputSchema: {
173
- type: 'object',
174
- properties: {
175
- enabled_only: { type: 'boolean', description: 'If true, only return enabled tasks' },
176
- },
177
- },
178
- async handler({ enabled_only }) {
179
- const data = loadTasks();
180
- let tasks = data.tasks.map(normalizeTaskRecord);
181
- if (enabled_only) tasks = tasks.filter((t) => t.enabled);
182
- if (tasks.length === 0) return textResult('No scheduled tasks found.');
183
- const summary = tasks
184
- .map((t) => {
185
- const running = isRunning(t.id) ? ' [RUNNING]' : '';
186
- const sched =
187
- t.schedule.kind === 'cron'
188
- ? `cron: ${t.schedule.expr} (${t.schedule.tz || 'UTC'})`
189
- : t.schedule.kind === 'once'
190
- ? `once: ${t.schedule.at}`
191
- : `every ${t.schedule.ms}ms`;
192
- return `- ${t.name} (${t.id})\n ${t.enabled ? 'enabled' : 'disabled'} | ${sched} | last: ${t.lastStatus || 'never'}${running}`;
193
- })
194
- .join('\n\n');
195
- return textResult(summary);
196
- },
197
- },
198
- {
199
- name: 'scheduled_tasks_get',
200
- description: 'Get detailed information about a specific task including recent run history',
201
- inputSchema: {
202
- type: 'object',
203
- properties: { task_id: { type: 'string', description: 'The task ID' } },
204
- required: ['task_id'],
205
- },
206
- async handler({ task_id }) {
207
- const data = loadTasks();
208
- const task = data.tasks.find((t) => t.id === task_id);
209
- if (!task) return errorResult(`Task not found: ${task_id}`);
210
- const normalizedTask = normalizeTaskRecord(task);
211
- return textResult(
212
- JSON.stringify(
213
- { ...normalizedTask, isRunning: isRunning(task_id), recentRuns: readRunLog(task_id, 10) },
214
- null,
215
- 2,
216
- ),
217
- );
218
- },
219
- },
220
- {
221
- name: 'scheduled_tasks_update',
222
- description: 'Update properties of an existing scheduled task',
223
- inputSchema: {
224
- type: 'object',
225
- properties: {
226
- task_id: { type: 'string' },
227
- name: { type: 'string' },
228
- schedule: {
229
- type: 'object',
230
- properties: {
231
- kind: { type: 'string', enum: ['cron', 'once', 'interval'] },
232
- expr: { type: 'string' },
233
- tz: { type: 'string' },
234
- at: { type: 'string' },
235
- ms: { type: 'number' },
236
- },
237
- },
238
- description: { type: 'string' },
239
- prompt: { type: 'string' },
240
- enabled: { type: 'boolean' },
241
- endsAt: { type: 'string' },
242
- nextRunAt: { type: 'string' },
243
- maxConcurrentRuns: { type: 'number' },
244
- harness: { type: 'string' },
245
- model: { type: 'string' },
246
- modelSettings: { type: 'object' },
247
- authMethod: { type: 'string' },
248
- projectPath: { type: 'string' },
249
- chatInput: { type: 'object' },
250
- },
251
- required: ['task_id'],
252
- },
253
- async handler({ task_id, name, description, schedule, prompt, enabled, endsAt, nextRunAt, maxConcurrentRuns, harness, model, modelSettings, authMethod, projectPath, chatInput }) {
254
- const data = loadTasks();
255
- const task = data.tasks.find((t) => t.id === task_id);
256
- if (!task) return errorResult(`Task not found: ${task_id}`);
257
-
258
- await hydrateModelPreferences();
259
-
260
- if (schedule) {
261
- const normalizedScheduleResult = normalizeTaskSchedule(schedule);
262
- if (normalizedScheduleResult.error) {
263
- return errorResult(normalizedScheduleResult.error);
264
- }
265
-
266
- const normalizedSchedule = normalizedScheduleResult.schedule;
267
- if (normalizedSchedule.kind === 'cron') {
268
- const err = validateCronExpr(normalizedSchedule.expr, normalizedSchedule.tz);
269
- if (err) return errorResult(`Invalid cron expression: ${err}`);
270
- }
271
- task.schedule = normalizedSchedule;
272
- }
273
- if (name !== undefined) task.name = name;
274
- if (description !== undefined) task.description = description || '';
275
- if (prompt !== undefined) task.prompt = prompt;
276
- if (enabled !== undefined) task.enabled = enabled;
277
- if (endsAt !== undefined) task.endsAt = endsAt || null;
278
- if (nextRunAt !== undefined) task.nextRunAt = nextRunAt || null;
279
- if (maxConcurrentRuns !== undefined) task.maxConcurrentRuns = maxConcurrentRuns;
280
- if (harness !== undefined) task.harness = harness || null;
281
- if (model !== undefined) task.model = model || null;
282
- if (modelSettings !== undefined) task.modelSettings = modelSettings || null;
283
- if (authMethod !== undefined) task.authMethod = authMethod || null;
284
- if (projectPath !== undefined) task.projectPath = projectPath || null;
285
- if (chatInput !== undefined) task.chatInput = chatInput || null;
286
- if (
287
- chatInput === undefined
288
- && (
289
- prompt !== undefined
290
- || harness !== undefined
291
- || model !== undefined
292
- || authMethod !== undefined
293
- || projectPath !== undefined
294
- )
295
- ) {
296
- const existingNonTextParts = Array.isArray(task.chatInput?.parts)
297
- ? task.chatInput.parts.filter((part) => part.type !== 'text')
298
- : [];
299
- task.chatInput = {
300
- ...(task.chatInput || {}),
301
- parts: [
302
- ...existingNonTextParts,
303
- ...((typeof task.prompt === 'string' && task.prompt)
304
- ? [{ type: 'text', text: task.prompt }]
305
- : []),
306
- ],
307
- agent: {
308
- ...(task.chatInput?.agent || {}),
309
- ...(task.harness ? { harness: task.harness } : {}),
310
- ...(task.model ? { model: task.model } : {}),
311
- ...(task.authMethod ? { authMethod: task.authMethod } : {}),
312
- },
313
- tools: task.chatInput?.tools || { mcpAppIds: [] },
314
- execution: {
315
- ...(task.chatInput?.execution || {}),
316
- ...(projectPath !== undefined ? { cwd: task.projectPath || null } : {}),
317
- },
318
- };
319
- }
320
-
321
- const normalizedTask = normalizeTaskRecord(task);
322
- Object.assign(task, normalizedTask);
323
-
324
- saveTasks(data, { preserveNextRunAt: nextRunAt !== undefined });
325
- activeMemory.ensureConstructMemory({
326
- type: 'task',
327
- id: task.id,
328
- name: task.name,
329
- projectPath: task.projectPath,
330
- }, { source: 'task:update' });
331
- console.log(`[AmalgmMCP] Updated task: ${task.id} (${task.name})`);
332
- return textResult(`Task updated.\n\n${JSON.stringify(task, null, 2)}`);
333
- },
334
- },
335
- {
336
- name: 'scheduled_tasks_delete',
337
- description: 'Delete a scheduled task permanently',
338
- inputSchema: {
339
- type: 'object',
340
- properties: { task_id: { type: 'string' } },
341
- required: ['task_id'],
342
- },
343
- async handler({ task_id }) {
344
- const data = loadTasks();
345
- const idx = data.tasks.findIndex((t) => t.id === task_id);
346
- if (idx === -1) return errorResult(`Task not found: ${task_id}`);
347
- abortRunning(task_id);
348
- const deleted = data.tasks.splice(idx, 1)[0];
349
- saveTasks(data);
350
- console.log(`[AmalgmMCP] Deleted task: ${task_id} (${deleted.name})`);
351
- return textResult(`Task "${deleted.name}" deleted.`);
352
- },
353
- },
354
- {
355
- name: 'scheduled_tasks_run_now',
356
- description: 'Trigger immediate execution of a scheduled task, bypassing its schedule',
357
- inputSchema: {
358
- type: 'object',
359
- properties: { task_id: { type: 'string' } },
360
- required: ['task_id'],
361
- },
362
- async handler({ task_id }) {
363
- const data = loadTasks();
364
- const task = data.tasks.find((t) => t.id === task_id);
365
- if (!task) return errorResult(`Task not found: ${task_id}`);
366
- if (isRunning(task_id)) return textResult(`Task "${task.name}" is already running.`);
367
- const claimed = claimTaskRun(task, {
368
- now: new Date(),
369
- scheduledFor: new Date().toISOString(),
370
- source: 'tasks:run-now',
371
- });
372
- if (!claimed) return textResult(`Task "${task.name}" is already running.`);
373
- executeTask(claimed.task, claimed.run).catch((err) =>
374
- console.error(`[AmalgmMCP:RunNow] ${task_id} failed:`, err.message),
375
- );
376
- return textResult(`Task "${task.name}" triggered. Running in background.`);
377
- },
378
- },
379
- {
380
- name: 'scheduled_tasks_cancel',
381
- description: 'Cancel a currently running task execution',
382
- inputSchema: {
383
- type: 'object',
384
- properties: { task_id: { type: 'string' } },
385
- required: ['task_id'],
386
- },
387
- async handler({ task_id }) {
388
- const running = getRunning(task_id);
389
- if (!running) return textResult(`No running execution for task: ${task_id}`);
390
- abortRunning(task_id);
391
- return textResult(`Cancelled execution of task ${task_id} (run: ${running.runId}).`);
392
- },
393
- },
394
- {
395
- name: 'scheduled_tasks_history',
396
- description: 'Get execution history for a scheduled task',
397
- inputSchema: {
398
- type: 'object',
399
- properties: {
400
- task_id: { type: 'string' },
401
- limit: { type: 'number', description: 'Max runs to return (default: 20)' },
402
- },
403
- required: ['task_id'],
404
- },
405
- async handler({ task_id, limit }) {
406
- const data = loadTasks();
407
- const task = data.tasks.find((t) => t.id === task_id);
408
- if (!task) return errorResult(`Task not found: ${task_id}`);
409
- const runs = readRunLog(task_id, limit || 20);
410
- if (runs.length === 0) return textResult(`No execution history for "${task.name}".`);
411
- return textResult(
412
- `Execution history for "${task.name}":\n\n${JSON.stringify(runs, null, 2)}`,
413
- );
414
- },
415
- },
416
- ];