amalgm 0.0.0 → 0.0.32
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/README.md +41 -1
- package/bin/amalgm.js +8 -2
- package/lib/auth-store.js +223 -0
- package/lib/cli.js +1132 -0
- package/lib/paths.js +30 -0
- package/lib/supervisor.js +476 -0
- package/lib/tunnel-chat.js +328 -0
- package/lib/tunnel-events.js +499 -0
- package/package.json +30 -3
- package/runtime/README.md +4 -0
- package/runtime/lib/chatInput.js +315 -0
- package/runtime/lib/harnesses.js +988 -0
- package/runtime/lib/local/amalgmStore.js +136 -0
- package/runtime/lib/local/credentialResolver.js +425 -0
- package/runtime/lib/mcpApps/registry.js +619 -0
- package/runtime/package.json +5 -0
- package/runtime/scripts/amalgm-mcp/agents/rest.js +144 -0
- package/runtime/scripts/amalgm-mcp/agents/store.js +684 -0
- package/runtime/scripts/amalgm-mcp/agents/talk.js +1162 -0
- package/runtime/scripts/amalgm-mcp/agents/tools.js +221 -0
- package/runtime/scripts/amalgm-mcp/artifacts/advertise.js +132 -0
- package/runtime/scripts/amalgm-mcp/artifacts/rest.js +103 -0
- package/runtime/scripts/amalgm-mcp/artifacts/store.js +157 -0
- package/runtime/scripts/amalgm-mcp/artifacts/supervisor.js +402 -0
- package/runtime/scripts/amalgm-mcp/artifacts/tools.js +176 -0
- package/runtime/scripts/amalgm-mcp/browser/page.js +637 -0
- package/runtime/scripts/amalgm-mcp/browser/tools.js +688 -0
- package/runtime/scripts/amalgm-mcp/config.js +140 -0
- package/runtime/scripts/amalgm-mcp/credentials/rest.js +45 -0
- package/runtime/scripts/amalgm-mcp/deps.js +40 -0
- package/runtime/scripts/amalgm-mcp/email/inbound.js +215 -0
- package/runtime/scripts/amalgm-mcp/events/executor.js +179 -0
- package/runtime/scripts/amalgm-mcp/events/ingress.js +113 -0
- package/runtime/scripts/amalgm-mcp/events/matcher.js +125 -0
- package/runtime/scripts/amalgm-mcp/events/rest.js +200 -0
- package/runtime/scripts/amalgm-mcp/events/ring-buffer.js +19 -0
- package/runtime/scripts/amalgm-mcp/events/store.js +113 -0
- package/runtime/scripts/amalgm-mcp/events/tools.js +306 -0
- package/runtime/scripts/amalgm-mcp/events/webhook-url.js +28 -0
- package/runtime/scripts/amalgm-mcp/fs/rest.js +293 -0
- package/runtime/scripts/amalgm-mcp/index.js +100 -0
- package/runtime/scripts/amalgm-mcp/lib/chat-runner.js +167 -0
- package/runtime/scripts/amalgm-mcp/lib/email-md.js +288 -0
- package/runtime/scripts/amalgm-mcp/lib/mcp-resolver.js +63 -0
- package/runtime/scripts/amalgm-mcp/lib/prefs.js +441 -0
- package/runtime/scripts/amalgm-mcp/lib/storage.js +92 -0
- package/runtime/scripts/amalgm-mcp/lib/supabase.js +118 -0
- package/runtime/scripts/amalgm-mcp/lib/tool-result.js +177 -0
- package/runtime/scripts/amalgm-mcp/local/rest.js +87 -0
- package/runtime/scripts/amalgm-mcp/mcp-connections/rest.js +234 -0
- package/runtime/scripts/amalgm-mcp/notify/index.js +107 -0
- package/runtime/scripts/amalgm-mcp/server/core-tools.js +20 -0
- package/runtime/scripts/amalgm-mcp/server/http.js +377 -0
- package/runtime/scripts/amalgm-mcp/server/mcp.js +127 -0
- package/runtime/scripts/amalgm-mcp/slack/inbound.js +200 -0
- package/runtime/scripts/amalgm-mcp/state/db.js +194 -0
- package/runtime/scripts/amalgm-mcp/state/events.js +113 -0
- package/runtime/scripts/amalgm-mcp/state/rest.js +64 -0
- package/runtime/scripts/amalgm-mcp/state/snapshot.js +76 -0
- package/runtime/scripts/amalgm-mcp/tasks/executor.js +225 -0
- package/runtime/scripts/amalgm-mcp/tasks/rest.js +110 -0
- package/runtime/scripts/amalgm-mcp/tasks/schedule-normalization.js +85 -0
- package/runtime/scripts/amalgm-mcp/tasks/scheduler.js +105 -0
- package/runtime/scripts/amalgm-mcp/tasks/store.js +154 -0
- package/runtime/scripts/amalgm-mcp/tasks/tools.js +391 -0
- package/runtime/scripts/amalgm-mcp/toolbox/rest.js +75 -0
- package/runtime/scripts/amalgm-mcp/toolbox/runner.js +257 -0
- package/runtime/scripts/amalgm-mcp/toolbox/store.js +933 -0
- package/runtime/scripts/amalgm-mcp/toolbox/tools.js +269 -0
- package/runtime/scripts/amalgm-mcp/user-api-keys/rest.js +105 -0
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +497 -0
- package/runtime/scripts/chat-core/adapters/claude.js +165 -0
- package/runtime/scripts/chat-core/adapters/codex.js +313 -0
- package/runtime/scripts/chat-core/adapters/opencode.js +412 -0
- package/runtime/scripts/chat-core/auth.js +177 -0
- package/runtime/scripts/chat-core/contract.js +328 -0
- package/runtime/scripts/chat-core/credentials/store.js +212 -0
- package/runtime/scripts/chat-core/egress.js +87 -0
- package/runtime/scripts/chat-core/engine.js +253 -0
- package/runtime/scripts/chat-core/event-schema.js +231 -0
- package/runtime/scripts/chat-core/events.js +190 -0
- package/runtime/scripts/chat-core/index.js +11 -0
- package/runtime/scripts/chat-core/input.js +50 -0
- package/runtime/scripts/chat-core/normalizers/claude.js +450 -0
- package/runtime/scripts/chat-core/normalizers/codex.js +380 -0
- package/runtime/scripts/chat-core/normalizers/normalizer_spec.md +259 -0
- package/runtime/scripts/chat-core/normalizers/opencode.js +552 -0
- package/runtime/scripts/chat-core/normalizers/tool_contract.md +123 -0
- package/runtime/scripts/chat-core/normalizers/usage_contract.md +304 -0
- package/runtime/scripts/chat-core/parts.js +253 -0
- package/runtime/scripts/chat-core/recorder.js +65 -0
- package/runtime/scripts/chat-core/runtime.js +86 -0
- package/runtime/scripts/chat-core/server.js +163 -0
- package/runtime/scripts/chat-core/sse.js +196 -0
- package/runtime/scripts/chat-core/stores.js +100 -0
- package/runtime/scripts/chat-core/tool-display.js +149 -0
- package/runtime/scripts/chat-core/tool-shape.js +143 -0
- package/runtime/scripts/chat-core/tooling/mcp-bundle.js +161 -0
- package/runtime/scripts/chat-core/tooling/mcp-relay.js +97 -0
- package/runtime/scripts/chat-core/tooling/native-binaries.js +608 -0
- package/runtime/scripts/chat-core/tooling/system-prompt.js +20 -0
- package/runtime/scripts/chat-core/usage.js +343 -0
- package/runtime/scripts/chat-server/config.js +110 -0
- package/runtime/scripts/chat-server/db.js +529 -0
- package/runtime/scripts/chat-server/index.js +33 -0
- package/runtime/scripts/chat-server/model-catalog.js +327 -0
- package/runtime/scripts/chat-server.js +75 -0
- package/runtime/scripts/credential-adapter.js +131 -0
- package/runtime/scripts/fs-watcher.js +888 -0
- package/runtime/scripts/local-gateway.js +854 -0
- package/runtime/scripts/platform-context.txt +246 -0
- package/runtime/scripts/port-monitor.js +175 -0
- package/runtime/scripts/proxy-token-store.js +162 -0
- package/runtime/scripts/runtime-auth.js +163 -0
- package/runtime/scripts/test-claude-code-models.js +87 -0
- package/runtime/tsconfig.json +15 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function textFromPart(part) {
|
|
4
|
+
if (!part || typeof part !== 'object') return '';
|
|
5
|
+
if (part.type === 'text' && typeof part.text === 'string') return part.text;
|
|
6
|
+
if (typeof part.content === 'string') return part.content;
|
|
7
|
+
return '';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function normalizeInput(payload) {
|
|
11
|
+
const chatInput = payload.chatInput && typeof payload.chatInput === 'object' ? payload.chatInput : null;
|
|
12
|
+
const parts = Array.isArray(payload.userParts) && payload.userParts.length > 0
|
|
13
|
+
? payload.userParts
|
|
14
|
+
: Array.isArray(chatInput?.parts) && chatInput.parts.length > 0
|
|
15
|
+
? chatInput.parts
|
|
16
|
+
: [{ type: 'text', text: payload.prompt || '' }];
|
|
17
|
+
const text = String(payload.prompt || parts.map(textFromPart).filter(Boolean).join('\n') || '').trim();
|
|
18
|
+
return {
|
|
19
|
+
text,
|
|
20
|
+
parts,
|
|
21
|
+
files: Array.isArray(chatInput?.files) ? chatInput.files : [],
|
|
22
|
+
systemPrompt: payload.systemPrompt || chatInput?.agent?.systemPrompt || '',
|
|
23
|
+
env: process.env,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function promptText(input, contract) {
|
|
28
|
+
const chunks = [];
|
|
29
|
+
if (input.files?.length) {
|
|
30
|
+
chunks.push(`<files>\n${input.files.map((file) => JSON.stringify(file)).join('\n')}\n</files>`);
|
|
31
|
+
}
|
|
32
|
+
chunks.push(input.text || '');
|
|
33
|
+
return chunks.filter(Boolean).join('\n\n');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function openCodeParts(input) {
|
|
37
|
+
const text = input.text || input.parts.map(textFromPart).filter(Boolean).join('\n');
|
|
38
|
+
const out = [];
|
|
39
|
+
if (text) out.push({ type: 'text', text });
|
|
40
|
+
for (const file of input.files || []) {
|
|
41
|
+
if (file?.path) out.push({ type: 'file', path: file.path });
|
|
42
|
+
}
|
|
43
|
+
return out.length ? out : [{ type: 'text', text: '' }];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
normalizeInput,
|
|
48
|
+
openCodeParts,
|
|
49
|
+
promptText,
|
|
50
|
+
};
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { compactionFinished, compactionStarted, reasoningDelta, textDelta, toolCompleted, toolStarted, toolUpdated, usageFinal } = require('../events');
|
|
4
|
+
const { summarizeToolRequest } = require('../tool-display');
|
|
5
|
+
const { mcpMetadataFromName, normalizeError, titleForTool, toolKind } = require('../tool-shape');
|
|
6
|
+
const { normalizeUsage } = require('../usage');
|
|
7
|
+
|
|
8
|
+
const TOOL_USE_BLOCK_TYPES = new Set(['tool_use', 'server_tool_use', 'mcp_tool_use']);
|
|
9
|
+
|
|
10
|
+
function usageFromClaude(usage = {}, contextLimit, options = {}) {
|
|
11
|
+
return normalizeUsage({
|
|
12
|
+
inputTokens: usage.input_tokens,
|
|
13
|
+
outputTokens: usage.output_tokens,
|
|
14
|
+
cacheReadTokens: usage.cache_read_input_tokens,
|
|
15
|
+
cacheWriteTokens: usage.cache_creation_input_tokens,
|
|
16
|
+
contextLimit,
|
|
17
|
+
inputContextSize: options.inputContextSize,
|
|
18
|
+
totalProcessedTokens: options.totalProcessedTokens,
|
|
19
|
+
operation: options.operation || 'chat_step',
|
|
20
|
+
billable: options.billable,
|
|
21
|
+
source: options.source || 'claude_agent_sdk',
|
|
22
|
+
exact: options.exact === false ? false : true,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function usageFromClaudeModelUsage(modelUsage = {}, contextLimit, options = {}) {
|
|
27
|
+
return normalizeUsage({
|
|
28
|
+
inputTokens: modelUsage.inputTokens,
|
|
29
|
+
outputTokens: modelUsage.outputTokens,
|
|
30
|
+
cacheReadTokens: modelUsage.cacheReadInputTokens,
|
|
31
|
+
cacheWriteTokens: modelUsage.cacheCreationInputTokens,
|
|
32
|
+
contextLimit: contextLimit || modelUsage.contextWindow,
|
|
33
|
+
inputContextSize: options.inputContextSize,
|
|
34
|
+
totalProcessedTokens: options.totalProcessedTokens,
|
|
35
|
+
operation: options.operation || 'chat_turn',
|
|
36
|
+
billable: options.billable,
|
|
37
|
+
source: options.source || 'claude_agent_sdk_model_usage',
|
|
38
|
+
exact: options.exact === false ? false : true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function claudeUsageTotal(usage = {}) {
|
|
43
|
+
const explicitTotal = Number(usage.total_tokens);
|
|
44
|
+
if (Number.isFinite(explicitTotal) && explicitTotal > 0) return explicitTotal;
|
|
45
|
+
return Number(usage.input_tokens || 0)
|
|
46
|
+
+ Number(usage.output_tokens || 0)
|
|
47
|
+
+ Number(usage.cache_read_input_tokens || 0)
|
|
48
|
+
+ Number(usage.cache_creation_input_tokens || 0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function hasClaudeTokenBuckets(usage = {}) {
|
|
52
|
+
return [
|
|
53
|
+
usage.input_tokens,
|
|
54
|
+
usage.output_tokens,
|
|
55
|
+
usage.cache_read_input_tokens,
|
|
56
|
+
usage.cache_creation_input_tokens,
|
|
57
|
+
].some((value) => Number(value || 0) > 0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function hasClaudeModelUsageBuckets(usage = {}) {
|
|
61
|
+
return [
|
|
62
|
+
usage.inputTokens,
|
|
63
|
+
usage.outputTokens,
|
|
64
|
+
usage.cacheReadInputTokens,
|
|
65
|
+
usage.cacheCreationInputTokens,
|
|
66
|
+
].some((value) => Number(value || 0) > 0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function claudeModelUsageTotal(usage = {}) {
|
|
70
|
+
return Number(usage.inputTokens || 0)
|
|
71
|
+
+ Number(usage.outputTokens || 0)
|
|
72
|
+
+ Number(usage.cacheReadInputTokens || 0)
|
|
73
|
+
+ Number(usage.cacheCreationInputTokens || 0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function selectClaudeModelUsage(modelUsage = {}) {
|
|
77
|
+
if (!modelUsage || typeof modelUsage !== 'object') return null;
|
|
78
|
+
const entries = Object.values(modelUsage).filter((entry) => (
|
|
79
|
+
entry && typeof entry === 'object' && hasClaudeModelUsageBuckets(entry)
|
|
80
|
+
));
|
|
81
|
+
return entries[0] || null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function claudeUsageFingerprint(usage = {}) {
|
|
85
|
+
return [
|
|
86
|
+
usage.input_tokens,
|
|
87
|
+
usage.output_tokens,
|
|
88
|
+
usage.cache_read_input_tokens,
|
|
89
|
+
usage.cache_creation_input_tokens,
|
|
90
|
+
].map((value) => Number(value || 0)).join(':');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function usageRecordsFromClaudeResult(resultOrUsage = {}, contextLimit) {
|
|
94
|
+
const result = resultOrUsage && resultOrUsage.usage
|
|
95
|
+
? resultOrUsage
|
|
96
|
+
: { usage: resultOrUsage };
|
|
97
|
+
const usage = result.usage || {};
|
|
98
|
+
const iterations = Array.isArray(usage.iterations)
|
|
99
|
+
? usage.iterations.filter(hasClaudeTokenBuckets)
|
|
100
|
+
: [];
|
|
101
|
+
const latestIterationTotal = iterations.length > 0
|
|
102
|
+
? claudeUsageTotal(iterations[iterations.length - 1])
|
|
103
|
+
: 0;
|
|
104
|
+
const inputContextSize = latestIterationTotal > 0 ? latestIterationTotal : undefined;
|
|
105
|
+
const modelUsage = selectClaudeModelUsage(result.modelUsage);
|
|
106
|
+
if (modelUsage) {
|
|
107
|
+
const totalProcessedTokens = claudeModelUsageTotal(modelUsage);
|
|
108
|
+
return [usageFromClaudeModelUsage(modelUsage, contextLimit, {
|
|
109
|
+
inputContextSize,
|
|
110
|
+
totalProcessedTokens: totalProcessedTokens > 0 ? totalProcessedTokens : undefined,
|
|
111
|
+
})];
|
|
112
|
+
}
|
|
113
|
+
const totalProcessedTokens = claudeUsageTotal(usage);
|
|
114
|
+
if (!hasClaudeTokenBuckets(usage)) return [];
|
|
115
|
+
return [usageFromClaude(usage, contextLimit, {
|
|
116
|
+
operation: 'chat_turn',
|
|
117
|
+
source: 'claude_agent_sdk_result',
|
|
118
|
+
inputContextSize,
|
|
119
|
+
totalProcessedTokens: totalProcessedTokens > 0 ? totalProcessedTokens : undefined,
|
|
120
|
+
})];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function ensureClaudeToolState(state) {
|
|
124
|
+
if (!state) return null;
|
|
125
|
+
if (!state.claudeTools) state.claudeTools = new Map();
|
|
126
|
+
if (!state.claudeToolsByIndex) state.claudeToolsByIndex = new Map();
|
|
127
|
+
if (!state.claudeStartedTools) state.claudeStartedTools = new Set();
|
|
128
|
+
if (!state.claudeCompletedTools) state.claudeCompletedTools = new Set();
|
|
129
|
+
return state;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function stableStringify(value) {
|
|
133
|
+
if (value == null || typeof value !== 'object') return JSON.stringify(value);
|
|
134
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
|
|
135
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(',')}}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function tryParseJsonRecord(text) {
|
|
139
|
+
try {
|
|
140
|
+
const parsed = JSON.parse(text);
|
|
141
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function eventRaw(source, method, payload) {
|
|
148
|
+
return { raw: { source, method, payload } };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function stringifyClaudeContent(content) {
|
|
152
|
+
if (content == null) return '';
|
|
153
|
+
if (typeof content === 'string') return content;
|
|
154
|
+
if (Array.isArray(content)) {
|
|
155
|
+
return content.map((entry) => {
|
|
156
|
+
if (entry == null) return '';
|
|
157
|
+
if (typeof entry === 'string') return entry;
|
|
158
|
+
if (typeof entry === 'object') {
|
|
159
|
+
if (typeof entry.text === 'string') return entry.text;
|
|
160
|
+
if (typeof entry.content === 'string') return entry.content;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
return JSON.stringify(entry);
|
|
164
|
+
} catch {
|
|
165
|
+
return String(entry);
|
|
166
|
+
}
|
|
167
|
+
}).filter(Boolean).join('\n');
|
|
168
|
+
}
|
|
169
|
+
if (typeof content === 'object') {
|
|
170
|
+
if (typeof content.text === 'string') return content.text;
|
|
171
|
+
if (typeof content.content === 'string') return content.content;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
return JSON.stringify(content);
|
|
175
|
+
} catch {
|
|
176
|
+
return String(content);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function rememberClaudeTool(state, id, data, index) {
|
|
181
|
+
const toolState = ensureClaudeToolState(state);
|
|
182
|
+
if (!toolState || !id) return;
|
|
183
|
+
const key = String(id);
|
|
184
|
+
const previous = toolState.claudeTools.get(key) || {};
|
|
185
|
+
const next = { ...previous, ...data, id: key };
|
|
186
|
+
toolState.claudeTools.set(key, next);
|
|
187
|
+
if (index !== undefined && index !== null) toolState.claudeToolsByIndex.set(Number(index), next);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function startOrUpdateClaudeTool(block, providerSessionId, state, raw) {
|
|
191
|
+
const toolName = block.name || block.tool_name || block.type || 'unknown';
|
|
192
|
+
const id = block.id || block.tool_use_id;
|
|
193
|
+
if (!id) return [];
|
|
194
|
+
|
|
195
|
+
const input = block.input && typeof block.input === 'object' ? block.input : {};
|
|
196
|
+
const detail = summarizeToolRequest(toolName, input);
|
|
197
|
+
const metadata = {
|
|
198
|
+
blockType: block.type,
|
|
199
|
+
...mcpMetadataFromName(toolName),
|
|
200
|
+
};
|
|
201
|
+
const kind = toolKind(toolName, input, metadata);
|
|
202
|
+
const title = titleForTool({ name: toolName, input, metadata, fallback: toolName });
|
|
203
|
+
const toolState = ensureClaudeToolState(state);
|
|
204
|
+
const key = String(id);
|
|
205
|
+
const fingerprint = stableStringify(input);
|
|
206
|
+
const known = toolState?.claudeTools.get(key);
|
|
207
|
+
rememberClaudeTool(state, key, {
|
|
208
|
+
toolName,
|
|
209
|
+
title,
|
|
210
|
+
kind,
|
|
211
|
+
metadata,
|
|
212
|
+
detail,
|
|
213
|
+
input,
|
|
214
|
+
blockType: block.type,
|
|
215
|
+
lastEmittedInputFingerprint: known?.lastEmittedInputFingerprint,
|
|
216
|
+
}, block.index);
|
|
217
|
+
|
|
218
|
+
if (!toolState?.claudeStartedTools.has(key)) {
|
|
219
|
+
toolState?.claudeStartedTools.add(key);
|
|
220
|
+
const next = toolState?.claudeTools.get(key);
|
|
221
|
+
if (next) next.lastEmittedInputFingerprint = fingerprint;
|
|
222
|
+
return [toolStarted(key, {
|
|
223
|
+
providerSessionId,
|
|
224
|
+
toolName,
|
|
225
|
+
title,
|
|
226
|
+
kind,
|
|
227
|
+
detail,
|
|
228
|
+
input,
|
|
229
|
+
metadata,
|
|
230
|
+
data: {
|
|
231
|
+
toolName,
|
|
232
|
+
input,
|
|
233
|
+
blockType: block.type,
|
|
234
|
+
},
|
|
235
|
+
...raw,
|
|
236
|
+
})];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!fingerprint || known?.lastEmittedInputFingerprint === fingerprint) return [];
|
|
240
|
+
const next = toolState?.claudeTools.get(key);
|
|
241
|
+
if (next) next.lastEmittedInputFingerprint = fingerprint;
|
|
242
|
+
return [toolUpdated(key, {
|
|
243
|
+
providerSessionId,
|
|
244
|
+
toolName,
|
|
245
|
+
title,
|
|
246
|
+
kind,
|
|
247
|
+
detail,
|
|
248
|
+
inputDelta: input,
|
|
249
|
+
status: 'in_progress',
|
|
250
|
+
metadata,
|
|
251
|
+
data: {
|
|
252
|
+
toolName,
|
|
253
|
+
input,
|
|
254
|
+
blockType: block.type,
|
|
255
|
+
},
|
|
256
|
+
...raw,
|
|
257
|
+
})];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function normalizeClaudeContentBlock(block, providerSessionId, state, raw = undefined) {
|
|
261
|
+
if (!block || typeof block !== 'object') return [];
|
|
262
|
+
if (block.type === 'text' && block.text) return [textDelta(block.text, { providerSessionId, ...raw })];
|
|
263
|
+
if ((block.type === 'thinking' || block.type === 'redacted_thinking') && block.thinking) {
|
|
264
|
+
return [reasoningDelta(block.thinking, { providerSessionId, ...raw })];
|
|
265
|
+
}
|
|
266
|
+
if (TOOL_USE_BLOCK_TYPES.has(block.type)) {
|
|
267
|
+
return startOrUpdateClaudeTool(block, providerSessionId, state, raw);
|
|
268
|
+
}
|
|
269
|
+
if (block.type === 'tool_result') {
|
|
270
|
+
const toolId = block.tool_use_id || block.id;
|
|
271
|
+
if (!toolId) return [];
|
|
272
|
+
const toolKey = String(toolId);
|
|
273
|
+
const toolState = ensureClaudeToolState(state);
|
|
274
|
+
if (toolState?.claudeCompletedTools.has(toolKey)) return [];
|
|
275
|
+
toolState?.claudeCompletedTools.add(toolKey);
|
|
276
|
+
const known = toolState?.claudeTools.get(toolKey);
|
|
277
|
+
const result = stringifyClaudeContent(block.content);
|
|
278
|
+
const toolName = known?.toolName || 'tool';
|
|
279
|
+
const input = known?.input || {};
|
|
280
|
+
const metadata = known?.metadata || mcpMetadataFromName(toolName);
|
|
281
|
+
const error = block.is_error === true
|
|
282
|
+
? normalizeError(result || block.content || 'Tool failed')
|
|
283
|
+
: undefined;
|
|
284
|
+
return [toolCompleted(toolKey, {
|
|
285
|
+
providerSessionId,
|
|
286
|
+
toolName,
|
|
287
|
+
title: known?.title || titleForTool({ name: toolName, input, metadata, fallback: toolName }),
|
|
288
|
+
kind: known?.kind || toolKind(toolName, input, metadata),
|
|
289
|
+
detail: known?.detail || summarizeToolRequest(toolName, input),
|
|
290
|
+
input,
|
|
291
|
+
output: block.content || '',
|
|
292
|
+
result,
|
|
293
|
+
isError: block.is_error === true,
|
|
294
|
+
error,
|
|
295
|
+
metadata,
|
|
296
|
+
data: {
|
|
297
|
+
toolName,
|
|
298
|
+
input,
|
|
299
|
+
result: block,
|
|
300
|
+
},
|
|
301
|
+
...raw,
|
|
302
|
+
})];
|
|
303
|
+
}
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function normalizeClaudeStreamEvent(message, providerSessionId, state) {
|
|
308
|
+
const event = message?.event && typeof message.event === 'object' ? message.event : message;
|
|
309
|
+
if (!event || typeof event !== 'object') return [];
|
|
310
|
+
const raw = eventRaw('claude.sdk.message', `claude/${event.type || 'unknown'}`, message);
|
|
311
|
+
|
|
312
|
+
if (event.type === 'content_block_start') {
|
|
313
|
+
const block = event.content_block || event.block;
|
|
314
|
+
if (!block || !TOOL_USE_BLOCK_TYPES.has(block.type)) return [];
|
|
315
|
+
return startOrUpdateClaudeTool({ ...block, index: event.index }, providerSessionId, state, raw);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (event.type === 'content_block_delta' && event.delta?.type === 'input_json_delta') {
|
|
319
|
+
const toolState = ensureClaudeToolState(state);
|
|
320
|
+
const tool = toolState?.claudeToolsByIndex.get(Number(event.index));
|
|
321
|
+
const partial = event.delta.partial_json;
|
|
322
|
+
if (!tool || typeof partial !== 'string') return [];
|
|
323
|
+
const partialInputJson = `${tool.partialInputJson || ''}${partial}`;
|
|
324
|
+
const parsedInput = tryParseJsonRecord(partialInputJson);
|
|
325
|
+
const nextTool = { ...tool, partialInputJson, ...(parsedInput ? { input: parsedInput } : {}) };
|
|
326
|
+
toolState.claudeToolsByIndex.set(Number(event.index), nextTool);
|
|
327
|
+
toolState.claudeTools.set(String(tool.id), nextTool);
|
|
328
|
+
if (!parsedInput) return [];
|
|
329
|
+
|
|
330
|
+
const fingerprint = stableStringify(parsedInput);
|
|
331
|
+
if (!fingerprint || tool.lastEmittedInputFingerprint === fingerprint) return [];
|
|
332
|
+
nextTool.lastEmittedInputFingerprint = fingerprint;
|
|
333
|
+
return [toolUpdated(tool.id, {
|
|
334
|
+
providerSessionId,
|
|
335
|
+
toolName: tool.toolName,
|
|
336
|
+
title: tool.title || tool.toolName,
|
|
337
|
+
kind: tool.kind,
|
|
338
|
+
detail: summarizeToolRequest(tool.toolName, parsedInput),
|
|
339
|
+
inputDelta: parsedInput,
|
|
340
|
+
status: 'in_progress',
|
|
341
|
+
metadata: tool.metadata,
|
|
342
|
+
data: {
|
|
343
|
+
toolName: tool.toolName,
|
|
344
|
+
input: parsedInput,
|
|
345
|
+
blockType: tool.blockType,
|
|
346
|
+
},
|
|
347
|
+
...raw,
|
|
348
|
+
})];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return [];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function normalizeClaudeMessage(message, state) {
|
|
355
|
+
if (!message || typeof message !== 'object') return [];
|
|
356
|
+
const providerSessionId = state?.providerSessionId || message.session_id || null;
|
|
357
|
+
|
|
358
|
+
if (message.type === 'system' && message.subtype === 'status' && message.status === 'compacting') {
|
|
359
|
+
return [compactionStarted({
|
|
360
|
+
providerSessionId,
|
|
361
|
+
trigger: 'auto',
|
|
362
|
+
...eventRaw('claude.sdk.message', 'system/status/compacting', message),
|
|
363
|
+
})];
|
|
364
|
+
}
|
|
365
|
+
if (message.type === 'system' && message.subtype === 'compact_boundary') {
|
|
366
|
+
const meta = message.compact_metadata || {};
|
|
367
|
+
return [compactionFinished({
|
|
368
|
+
providerSessionId,
|
|
369
|
+
trigger: meta.trigger || 'auto',
|
|
370
|
+
preTokens: meta.pre_tokens,
|
|
371
|
+
postTokens: meta.post_tokens,
|
|
372
|
+
durationMs: meta.duration_ms,
|
|
373
|
+
...eventRaw('claude.sdk.message', 'system/compact_boundary', message),
|
|
374
|
+
})];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (message.type === 'assistant') {
|
|
378
|
+
const events = [];
|
|
379
|
+
for (const block of message.message?.content || []) {
|
|
380
|
+
events.push(...normalizeClaudeContentBlock(
|
|
381
|
+
block,
|
|
382
|
+
providerSessionId,
|
|
383
|
+
state,
|
|
384
|
+
eventRaw('claude.sdk.message', 'claude/assistant/content_block', message),
|
|
385
|
+
));
|
|
386
|
+
}
|
|
387
|
+
const usage = message.message?.usage || message.usage;
|
|
388
|
+
if (hasClaudeTokenBuckets(usage)) {
|
|
389
|
+
if (state && !state.claudeUsageSnapshotFingerprints) state.claudeUsageSnapshotFingerprints = new Set();
|
|
390
|
+
const fingerprint = claudeUsageFingerprint(usage);
|
|
391
|
+
if (!state?.claudeUsageSnapshotFingerprints?.has(fingerprint)) {
|
|
392
|
+
state?.claudeUsageSnapshotFingerprints?.add(fingerprint);
|
|
393
|
+
events.push(usageFinal(usageFromClaude(usage, state?.contextLimit, {
|
|
394
|
+
billable: false,
|
|
395
|
+
exact: false,
|
|
396
|
+
source: 'claude_agent_sdk_snapshot',
|
|
397
|
+
}), {
|
|
398
|
+
providerSessionId,
|
|
399
|
+
...eventRaw('claude.sdk.message', 'claude/assistant/usage', message),
|
|
400
|
+
}));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return events;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (message.type === 'user') {
|
|
407
|
+
const events = [];
|
|
408
|
+
for (const block of message.message?.content || []) {
|
|
409
|
+
if (block?.type === 'tool_result') {
|
|
410
|
+
events.push(...normalizeClaudeContentBlock(
|
|
411
|
+
block,
|
|
412
|
+
providerSessionId,
|
|
413
|
+
state,
|
|
414
|
+
eventRaw('claude.sdk.message', 'claude/user/tool_result', message),
|
|
415
|
+
));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return events;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const streamEvents = normalizeClaudeStreamEvent(message, providerSessionId, state);
|
|
422
|
+
if (streamEvents.length > 0) return streamEvents;
|
|
423
|
+
|
|
424
|
+
if (message.type === 'tool_progress' && message.tool_use_id) {
|
|
425
|
+
const toolName = message.tool_name || 'tool';
|
|
426
|
+
const metadata = mcpMetadataFromName(toolName);
|
|
427
|
+
return [toolUpdated(message.tool_use_id, {
|
|
428
|
+
providerSessionId,
|
|
429
|
+
toolName,
|
|
430
|
+
title: titleForTool({ name: toolName, input: {}, metadata, fallback: toolName }),
|
|
431
|
+
kind: toolKind(toolName, {}, metadata),
|
|
432
|
+
status: 'in_progress',
|
|
433
|
+
outputDelta: message.output || message.summary || undefined,
|
|
434
|
+
metadata,
|
|
435
|
+
...eventRaw('claude.sdk.message', 'claude/tool_progress', message),
|
|
436
|
+
})];
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return [];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
module.exports = {
|
|
443
|
+
normalizeClaudeContentBlock,
|
|
444
|
+
normalizeClaudeMessage,
|
|
445
|
+
stableStringify,
|
|
446
|
+
stringifyClaudeContent,
|
|
447
|
+
usageRecordsFromClaudeResult,
|
|
448
|
+
usageFromClaude,
|
|
449
|
+
usageFromClaudeModelUsage,
|
|
450
|
+
};
|