amalgm 0.0.0 → 0.0.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.
Files changed (107) hide show
  1. package/README.md +37 -1
  2. package/bin/amalgm.js +8 -2
  3. package/lib/auth-store.js +223 -0
  4. package/lib/cli.js +1000 -0
  5. package/lib/paths.js +30 -0
  6. package/lib/supervisor.js +467 -0
  7. package/lib/tunnel-chat.js +328 -0
  8. package/lib/tunnel-events.js +499 -0
  9. package/package.json +29 -3
  10. package/runtime/README.md +4 -0
  11. package/runtime/lib/chatInput.js +306 -0
  12. package/runtime/lib/harnesses.js +988 -0
  13. package/runtime/lib/local/amalgmStore.js +128 -0
  14. package/runtime/lib/local/credentialResolver.js +425 -0
  15. package/runtime/lib/mcpApps/registry.js +619 -0
  16. package/runtime/package.json +5 -0
  17. package/runtime/scripts/amalgm-mcp/agents/rest.js +165 -0
  18. package/runtime/scripts/amalgm-mcp/agents/store.js +153 -0
  19. package/runtime/scripts/amalgm-mcp/agents/talk.js +1156 -0
  20. package/runtime/scripts/amalgm-mcp/agents/tools.js +210 -0
  21. package/runtime/scripts/amalgm-mcp/artifacts/advertise.js +132 -0
  22. package/runtime/scripts/amalgm-mcp/artifacts/rest.js +103 -0
  23. package/runtime/scripts/amalgm-mcp/artifacts/store.js +141 -0
  24. package/runtime/scripts/amalgm-mcp/artifacts/supervisor.js +402 -0
  25. package/runtime/scripts/amalgm-mcp/artifacts/tools.js +176 -0
  26. package/runtime/scripts/amalgm-mcp/browser/page.js +637 -0
  27. package/runtime/scripts/amalgm-mcp/browser/tools.js +688 -0
  28. package/runtime/scripts/amalgm-mcp/config.js +138 -0
  29. package/runtime/scripts/amalgm-mcp/credentials/rest.js +45 -0
  30. package/runtime/scripts/amalgm-mcp/deps.js +40 -0
  31. package/runtime/scripts/amalgm-mcp/email/inbound.js +215 -0
  32. package/runtime/scripts/amalgm-mcp/events/executor.js +179 -0
  33. package/runtime/scripts/amalgm-mcp/events/ingress.js +113 -0
  34. package/runtime/scripts/amalgm-mcp/events/matcher.js +125 -0
  35. package/runtime/scripts/amalgm-mcp/events/rest.js +200 -0
  36. package/runtime/scripts/amalgm-mcp/events/ring-buffer.js +19 -0
  37. package/runtime/scripts/amalgm-mcp/events/store.js +98 -0
  38. package/runtime/scripts/amalgm-mcp/events/tools.js +306 -0
  39. package/runtime/scripts/amalgm-mcp/events/webhook-url.js +28 -0
  40. package/runtime/scripts/amalgm-mcp/fs/rest.js +293 -0
  41. package/runtime/scripts/amalgm-mcp/index.js +100 -0
  42. package/runtime/scripts/amalgm-mcp/lib/chat-runner.js +167 -0
  43. package/runtime/scripts/amalgm-mcp/lib/email-md.js +288 -0
  44. package/runtime/scripts/amalgm-mcp/lib/mcp-resolver.js +63 -0
  45. package/runtime/scripts/amalgm-mcp/lib/prefs.js +393 -0
  46. package/runtime/scripts/amalgm-mcp/lib/storage.js +92 -0
  47. package/runtime/scripts/amalgm-mcp/lib/supabase.js +118 -0
  48. package/runtime/scripts/amalgm-mcp/lib/tool-result.js +177 -0
  49. package/runtime/scripts/amalgm-mcp/local/rest.js +80 -0
  50. package/runtime/scripts/amalgm-mcp/mcp-connections/rest.js +151 -0
  51. package/runtime/scripts/amalgm-mcp/notify/index.js +107 -0
  52. package/runtime/scripts/amalgm-mcp/server/http.js +335 -0
  53. package/runtime/scripts/amalgm-mcp/server/mcp.js +116 -0
  54. package/runtime/scripts/amalgm-mcp/slack/inbound.js +200 -0
  55. package/runtime/scripts/amalgm-mcp/tasks/executor.js +225 -0
  56. package/runtime/scripts/amalgm-mcp/tasks/rest.js +110 -0
  57. package/runtime/scripts/amalgm-mcp/tasks/schedule-normalization.js +85 -0
  58. package/runtime/scripts/amalgm-mcp/tasks/scheduler.js +105 -0
  59. package/runtime/scripts/amalgm-mcp/tasks/store.js +139 -0
  60. package/runtime/scripts/amalgm-mcp/tasks/tools.js +391 -0
  61. package/runtime/scripts/amalgm-mcp/user-api-keys/rest.js +105 -0
  62. package/runtime/scripts/amalgm-mcp/workspace/rest.js +389 -0
  63. package/runtime/scripts/chat-core/adapters/claude.js +163 -0
  64. package/runtime/scripts/chat-core/adapters/codex.js +313 -0
  65. package/runtime/scripts/chat-core/adapters/opencode.js +412 -0
  66. package/runtime/scripts/chat-core/auth.js +177 -0
  67. package/runtime/scripts/chat-core/contract.js +326 -0
  68. package/runtime/scripts/chat-core/credentials/store.js +212 -0
  69. package/runtime/scripts/chat-core/egress.js +87 -0
  70. package/runtime/scripts/chat-core/engine.js +195 -0
  71. package/runtime/scripts/chat-core/event-schema.js +231 -0
  72. package/runtime/scripts/chat-core/events.js +190 -0
  73. package/runtime/scripts/chat-core/index.js +11 -0
  74. package/runtime/scripts/chat-core/input.js +50 -0
  75. package/runtime/scripts/chat-core/normalizers/claude.js +450 -0
  76. package/runtime/scripts/chat-core/normalizers/codex.js +380 -0
  77. package/runtime/scripts/chat-core/normalizers/normalizer_spec.md +259 -0
  78. package/runtime/scripts/chat-core/normalizers/opencode.js +552 -0
  79. package/runtime/scripts/chat-core/normalizers/tool_contract.md +123 -0
  80. package/runtime/scripts/chat-core/normalizers/usage_contract.md +304 -0
  81. package/runtime/scripts/chat-core/parts.js +253 -0
  82. package/runtime/scripts/chat-core/recorder.js +65 -0
  83. package/runtime/scripts/chat-core/runtime.js +86 -0
  84. package/runtime/scripts/chat-core/server.js +163 -0
  85. package/runtime/scripts/chat-core/sse.js +196 -0
  86. package/runtime/scripts/chat-core/stores.js +100 -0
  87. package/runtime/scripts/chat-core/tool-display.js +149 -0
  88. package/runtime/scripts/chat-core/tool-shape.js +143 -0
  89. package/runtime/scripts/chat-core/tooling/mcp-bundle.js +161 -0
  90. package/runtime/scripts/chat-core/tooling/mcp-relay.js +97 -0
  91. package/runtime/scripts/chat-core/tooling/native-binaries.js +608 -0
  92. package/runtime/scripts/chat-core/tooling/system-prompt.js +20 -0
  93. package/runtime/scripts/chat-core/usage.js +343 -0
  94. package/runtime/scripts/chat-server/config.js +110 -0
  95. package/runtime/scripts/chat-server/db.js +529 -0
  96. package/runtime/scripts/chat-server/index.js +33 -0
  97. package/runtime/scripts/chat-server/model-catalog.js +327 -0
  98. package/runtime/scripts/chat-server.js +75 -0
  99. package/runtime/scripts/credential-adapter.js +129 -0
  100. package/runtime/scripts/fs-watcher.js +888 -0
  101. package/runtime/scripts/local-gateway.js +852 -0
  102. package/runtime/scripts/platform-context.txt +246 -0
  103. package/runtime/scripts/port-monitor.js +175 -0
  104. package/runtime/scripts/proxy-token-store.js +162 -0
  105. package/runtime/scripts/runtime-auth.js +163 -0
  106. package/runtime/scripts/test-claude-code-models.js +87 -0
  107. package/runtime/tsconfig.json +15 -0
@@ -0,0 +1,165 @@
1
+ /**
2
+ * /agents/* REST routes (UI-only, not MCP tools).
3
+ */
4
+
5
+ const crypto = require('crypto');
6
+ const {
7
+ loadAgents,
8
+ saveAgents,
9
+ getAllAgentsWithBuiltins,
10
+ resolveAgent,
11
+ } = require('./store');
12
+ const credentialAdapter = require('../../credential-adapter');
13
+
14
+ function coerceAuthMethodForHarness(harnessId, authMethod) {
15
+ const supported = credentialAdapter.SUPPORTED_AUTH_BY_HARNESS?.[harnessId] || ['amalgm'];
16
+ return supported.includes(authMethod) ? authMethod : supported[0] || 'amalgm';
17
+ }
18
+
19
+ function normalizeStringList(values) {
20
+ if (!Array.isArray(values)) return [];
21
+ return [...new Set(values
22
+ .filter((value) => typeof value === 'string')
23
+ .map((value) => value.trim())
24
+ .filter(Boolean))];
25
+ }
26
+
27
+ function normalizeMcpConfig(mcp, mcpAppIds, nativeMcps) {
28
+ return {
29
+ inheritAll: mcp?.inheritAll !== false,
30
+ customServers: Array.isArray(mcp?.customServers) ? mcp.customServers : [],
31
+ appIds: normalizeStringList(mcp?.appIds ?? mcpAppIds),
32
+ nativeMcps: normalizeStringList(mcp?.nativeMcps ?? nativeMcps),
33
+ };
34
+ }
35
+
36
+ async function handleList(sendJson) {
37
+ sendJson(200, { agents: getAllAgentsWithBuiltins() });
38
+ }
39
+
40
+ async function handleGet(body, sendJson) {
41
+ const { agent_id } = body;
42
+ const agent = resolveAgent(agent_id);
43
+ if (!agent) return sendJson(404, { error: `Agent not found: ${agent_id}` });
44
+ sendJson(200, { agent });
45
+ }
46
+
47
+ async function handleCreate(body, sendJson) {
48
+ const {
49
+ name,
50
+ description,
51
+ baseHarnessId,
52
+ baseModelId,
53
+ systemPrompt,
54
+ files,
55
+ skills,
56
+ mcpAppIds,
57
+ nativeMcps,
58
+ mcp,
59
+ authMethod,
60
+ } = body;
61
+ if (!name || !name.trim()) return sendJson(400, { error: 'name is required' });
62
+ if (!baseHarnessId) return sendJson(400, { error: 'baseHarnessId is required' });
63
+
64
+ const data = loadAgents();
65
+ if (data.agents.some((a) => a.name.toLowerCase() === name.trim().toLowerCase())) {
66
+ return sendJson(400, { error: `An agent named "${name.trim()}" already exists` });
67
+ }
68
+
69
+ const agent = {
70
+ id: `custom-${crypto.randomUUID()}`,
71
+ name: name.trim(),
72
+ description: description || '',
73
+ baseHarnessId,
74
+ baseModelId: baseModelId || '',
75
+ systemPrompt: systemPrompt || '',
76
+ files: normalizeStringList(files),
77
+ skills: normalizeStringList(skills),
78
+ mcpAppIds: normalizeStringList(mcpAppIds),
79
+ nativeMcps: normalizeStringList(nativeMcps),
80
+ mcp: normalizeMcpConfig(mcp, mcpAppIds, nativeMcps),
81
+ authMethod: coerceAuthMethodForHarness(baseHarnessId, authMethod),
82
+ createdAt: new Date().toISOString(),
83
+ updatedAt: new Date().toISOString(),
84
+ };
85
+ data.agents.push(agent);
86
+ saveAgents(data);
87
+ console.log(`[AmalgmMCP] Created agent: ${agent.id} (${agent.name})`);
88
+ sendJson(200, { ok: true, agent });
89
+ }
90
+
91
+ async function handleUpdate(body, sendJson) {
92
+ const { agent_id, ...updates } = body;
93
+ if (!agent_id) return sendJson(400, { error: 'agent_id is required' });
94
+ const data = loadAgents();
95
+ const agent = data.agents.find((a) => a.id === agent_id);
96
+ if (!agent) return sendJson(404, { error: `Agent not found: ${agent_id}` });
97
+
98
+ if (
99
+ updates.name &&
100
+ data.agents.some(
101
+ (a) => a.id !== agent_id && a.name.toLowerCase() === updates.name.trim().toLowerCase(),
102
+ )
103
+ ) {
104
+ return sendJson(400, { error: `An agent named "${updates.name.trim()}" already exists` });
105
+ }
106
+
107
+ const allowedFields = [
108
+ 'name',
109
+ 'description',
110
+ 'baseHarnessId',
111
+ 'baseModelId',
112
+ 'systemPrompt',
113
+ 'files',
114
+ 'skills',
115
+ 'mcpAppIds',
116
+ 'nativeMcps',
117
+ 'mcp',
118
+ 'authMethod',
119
+ ];
120
+ for (const field of allowedFields) {
121
+ if (updates[field] === undefined) continue;
122
+
123
+ if (field === 'files' || field === 'skills' || field === 'mcpAppIds' || field === 'nativeMcps') {
124
+ agent[field] = normalizeStringList(updates[field]);
125
+ continue;
126
+ }
127
+
128
+ if (field === 'mcp') {
129
+ agent.mcp = normalizeMcpConfig(
130
+ updates.mcp,
131
+ updates.mcpAppIds ?? agent.mcpAppIds,
132
+ updates.nativeMcps ?? agent.nativeMcps,
133
+ );
134
+ continue;
135
+ }
136
+
137
+ if (field === 'authMethod') {
138
+ agent.authMethod = coerceAuthMethodForHarness(agent.baseHarnessId, updates.authMethod);
139
+ continue;
140
+ }
141
+
142
+ agent[field] = updates[field];
143
+ }
144
+ agent.authMethod = coerceAuthMethodForHarness(agent.baseHarnessId, agent.authMethod);
145
+ agent.mcpAppIds = normalizeStringList(agent.mcp?.appIds ?? agent.mcpAppIds);
146
+ agent.nativeMcps = normalizeStringList(agent.mcp?.nativeMcps ?? agent.nativeMcps);
147
+ agent.updatedAt = new Date().toISOString();
148
+ saveAgents(data);
149
+ console.log(`[AmalgmMCP] Updated agent: ${agent.id} (${agent.name})`);
150
+ sendJson(200, { ok: true, agent });
151
+ }
152
+
153
+ async function handleDelete(body, sendJson) {
154
+ const { agent_id } = body;
155
+ if (!agent_id) return sendJson(400, { error: 'agent_id is required' });
156
+ const data = loadAgents();
157
+ const idx = data.agents.findIndex((a) => a.id === agent_id);
158
+ if (idx === -1) return sendJson(404, { error: `Agent not found: ${agent_id}` });
159
+ const deleted = data.agents.splice(idx, 1)[0];
160
+ saveAgents(data);
161
+ console.log(`[AmalgmMCP] Deleted agent: ${agent_id} (${deleted.name})`);
162
+ sendJson(200, { ok: true, deleted });
163
+ }
164
+
165
+ module.exports = { handleList, handleGet, handleCreate, handleUpdate, handleDelete };
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Agents storage — ~/.amalgm/agents.json + built-in defaults.
3
+ *
4
+ * Built-ins are compile-time (claude_code, codex, opencode, pi, amp, cursor). Custom agents
5
+ * persist to agents.json with id prefix "custom-".
6
+ */
7
+
8
+ const path = require('path');
9
+ const { AGENTS_FILE, AGENT_CONVOS_DIR, STORAGE_DIR } = require('../config');
10
+ const {
11
+ ensureDir,
12
+ readJson,
13
+ writeJsonAtomic,
14
+ appendJsonl,
15
+ tailJsonl,
16
+ } = require('../lib/storage');
17
+
18
+ const BUILTIN_AGENTS = [
19
+ {
20
+ id: 'claude_code',
21
+ name: 'Claude Code',
22
+ description:
23
+ 'Anthropic Claude agent — general-purpose coding, research, and reasoning',
24
+ baseHarnessId: 'claude_code',
25
+ baseModelId: 'anthropic/claude-opus-4.7',
26
+ systemPrompt: '',
27
+ mcp: { inheritAll: true, customServers: [] },
28
+ builtin: true,
29
+ },
30
+ {
31
+ id: 'codex',
32
+ name: 'Codex',
33
+ description: 'OpenAI Codex agent — code generation and analysis',
34
+ baseHarnessId: 'codex',
35
+ baseModelId: 'openai/gpt-5.4-thinking-xhigh',
36
+ systemPrompt: '',
37
+ mcp: { inheritAll: true, customServers: [] },
38
+ builtin: true,
39
+ },
40
+ {
41
+ id: 'opencode',
42
+ name: 'OpenCode',
43
+ description: 'Open-source agent — multi-provider coding assistant',
44
+ baseHarnessId: 'opencode',
45
+ baseModelId: 'opencode-moonshotai-kimi-k2.5',
46
+ systemPrompt: '',
47
+ mcp: { inheritAll: true, customServers: [] },
48
+ builtin: true,
49
+ },
50
+ {
51
+ id: 'pi',
52
+ name: 'Pi',
53
+ description: 'Pi agent — multi-provider coding assistant',
54
+ baseHarnessId: 'pi',
55
+ baseModelId: 'pi-anthropic-claude-sonnet-4.6',
56
+ systemPrompt: '',
57
+ mcp: { inheritAll: true, customServers: [] },
58
+ builtin: true,
59
+ },
60
+ {
61
+ id: 'amp',
62
+ name: 'Amp',
63
+ description: 'Sourcegraph Amp agent — terminal-auth coding assistant',
64
+ baseHarnessId: 'amp',
65
+ baseModelId: 'amp-default',
66
+ systemPrompt: '',
67
+ mcp: { inheritAll: true, customServers: [] },
68
+ builtin: true,
69
+ },
70
+ {
71
+ id: 'cursor',
72
+ name: 'Cursor',
73
+ description: 'Cursor Agent CLI — account-auth coding assistant',
74
+ baseHarnessId: 'cursor',
75
+ baseModelId: 'cursor/composer-2-fast',
76
+ systemPrompt: '',
77
+ mcp: { inheritAll: true, customServers: [] },
78
+ builtin: true,
79
+ },
80
+ ];
81
+
82
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
83
+
84
+ function normalizeConversationId(value) {
85
+ const clean = String(value || '').trim();
86
+ return UUID_RE.test(clean) ? clean : null;
87
+ }
88
+
89
+ function conversationLogPath(sessionId) {
90
+ const conversationId = normalizeConversationId(sessionId);
91
+ if (!conversationId) throw new Error('conversation_id must be a UUID');
92
+ return path.join(AGENT_CONVOS_DIR, `${conversationId}.jsonl`);
93
+ }
94
+
95
+ function ensureAgentsDirs() {
96
+ ensureDir(STORAGE_DIR);
97
+ ensureDir(AGENT_CONVOS_DIR);
98
+ if (!readJson(AGENTS_FILE, null)) writeJsonAtomic(AGENTS_FILE, { version: 1, agents: [] });
99
+ }
100
+
101
+ function loadAgents() {
102
+ return readJson(AGENTS_FILE, { version: 1, agents: [] });
103
+ }
104
+
105
+ function saveAgents(data) {
106
+ writeJsonAtomic(AGENTS_FILE, data);
107
+ }
108
+
109
+ function getAllAgentsWithBuiltins() {
110
+ const data = loadAgents();
111
+ return [...BUILTIN_AGENTS, ...data.agents];
112
+ }
113
+
114
+ function resolveAgent(agentId) {
115
+ const builtin = BUILTIN_AGENTS.find((a) => a.id === agentId);
116
+ if (builtin) return builtin;
117
+ const data = loadAgents();
118
+ return data.agents.find((a) => a.id === agentId) || null;
119
+ }
120
+
121
+ function normalizeAgentLookup(value) {
122
+ return String(value || '').trim().toLowerCase().replace(/[\s_-]+/g, '');
123
+ }
124
+
125
+ function resolveAgentByNameOrId(value) {
126
+ const clean = normalizeAgentLookup(value);
127
+ if (!clean) return null;
128
+ return getAllAgentsWithBuiltins().find((agent) => (
129
+ normalizeAgentLookup(agent.id) === clean ||
130
+ normalizeAgentLookup(agent.name) === clean
131
+ )) || null;
132
+ }
133
+
134
+ function appendAgentConvoLog(sessionId, entry) {
135
+ appendJsonl(conversationLogPath(sessionId), entry);
136
+ }
137
+
138
+ function readAgentConvoLog(sessionId, limit = 50) {
139
+ return tailJsonl(conversationLogPath(sessionId), limit);
140
+ }
141
+
142
+ module.exports = {
143
+ BUILTIN_AGENTS,
144
+ ensureAgentsDirs,
145
+ loadAgents,
146
+ saveAgents,
147
+ getAllAgentsWithBuiltins,
148
+ resolveAgent,
149
+ resolveAgentByNameOrId,
150
+ normalizeConversationId,
151
+ appendAgentConvoLog,
152
+ readAgentConvoLog,
153
+ };