@amalgm/chat 0.1.1 → 0.2.0
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/AGENTS.md +98 -0
- package/PURPOSE.md +143 -104
- package/README.md +82 -54
- package/dist/acp/index.d.ts +7 -0
- package/dist/acp/index.d.ts.map +1 -0
- package/dist/acp/index.js +7 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/acp/projection.d.ts +30 -0
- package/dist/acp/projection.d.ts.map +1 -0
- package/dist/acp/projection.js +147 -0
- package/dist/acp/projection.js.map +1 -0
- package/dist/api/conversations.d.ts +66 -0
- package/dist/api/conversations.d.ts.map +1 -0
- package/dist/api/conversations.js +202 -0
- package/dist/api/conversations.js.map +1 -0
- package/dist/api/index.d.ts +3 -3
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1 -2
- package/dist/api/index.js.map +1 -1
- package/dist/conversations/index.d.ts +3 -0
- package/dist/conversations/index.d.ts.map +1 -0
- package/dist/conversations/index.js +3 -0
- package/dist/conversations/index.js.map +1 -0
- package/dist/conversations/transcript.d.ts +13 -0
- package/dist/conversations/transcript.d.ts.map +1 -0
- package/dist/conversations/transcript.js +171 -0
- package/dist/conversations/transcript.js.map +1 -0
- package/dist/conversations/types.d.ts +147 -0
- package/dist/conversations/types.d.ts.map +1 -0
- package/dist/conversations/types.js +9 -0
- package/dist/conversations/types.js.map +1 -0
- package/dist/events/constructors.d.ts +9 -15
- package/dist/events/constructors.d.ts.map +1 -1
- package/dist/events/constructors.js +6 -12
- package/dist/events/constructors.js.map +1 -1
- package/dist/events/index.d.ts +2 -3
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +2 -3
- package/dist/events/index.js.map +1 -1
- package/dist/events/schema.d.ts +2 -5
- package/dist/events/schema.d.ts.map +1 -1
- package/dist/events/schema.js +2 -5
- package/dist/events/schema.js.map +1 -1
- package/dist/execution/contract.d.ts +51 -0
- package/dist/execution/contract.d.ts.map +1 -0
- package/dist/execution/contract.js +127 -0
- package/dist/execution/contract.js.map +1 -0
- package/dist/execution/index.d.ts +6 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/index.js +4 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/execution/revision.d.ts +5 -0
- package/dist/execution/revision.d.ts.map +1 -0
- package/dist/execution/revision.js +17 -0
- package/dist/execution/revision.js.map +1 -0
- package/dist/execution/store.d.ts +26 -0
- package/dist/execution/store.d.ts.map +1 -0
- package/dist/execution/store.js +38 -0
- package/dist/execution/store.js.map +1 -0
- package/dist/index.d.ts +8 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -23
- package/dist/index.js.map +1 -1
- package/dist/input/index.d.ts +3 -0
- package/dist/input/index.d.ts.map +1 -0
- package/dist/input/index.js +2 -0
- package/dist/input/index.js.map +1 -0
- package/dist/input/prompt.d.ts +17 -0
- package/dist/input/prompt.d.ts.map +1 -0
- package/dist/input/prompt.js +104 -0
- package/dist/input/prompt.js.map +1 -0
- package/dist/normalizers/claude.d.ts +6 -9
- package/dist/normalizers/claude.d.ts.map +1 -1
- package/dist/normalizers/claude.js +5 -8
- package/dist/normalizers/claude.js.map +1 -1
- package/dist/normalizers/codex.d.ts +5 -8
- package/dist/normalizers/codex.d.ts.map +1 -1
- package/dist/normalizers/codex.js +4 -7
- package/dist/normalizers/codex.js.map +1 -1
- package/dist/normalizers/cursor.d.ts +1 -2
- package/dist/normalizers/cursor.d.ts.map +1 -1
- package/dist/normalizers/cursor.js +2 -3
- package/dist/normalizers/cursor.js.map +1 -1
- package/dist/normalizers/index.d.ts +3 -5
- package/dist/normalizers/index.d.ts.map +1 -1
- package/dist/normalizers/index.js +3 -5
- package/dist/normalizers/index.js.map +1 -1
- package/dist/normalizers/opencode.d.ts +2 -4
- package/dist/normalizers/opencode.d.ts.map +1 -1
- package/dist/normalizers/opencode.js +2 -4
- package/dist/normalizers/opencode.js.map +1 -1
- package/dist/normalizers/pi.d.ts +3 -4
- package/dist/normalizers/pi.d.ts.map +1 -1
- package/dist/normalizers/pi.js +3 -4
- package/dist/normalizers/pi.js.map +1 -1
- package/dist/sessions/chat.d.ts +176 -0
- package/dist/sessions/chat.d.ts.map +1 -0
- package/dist/sessions/chat.js +701 -0
- package/dist/sessions/chat.js.map +1 -0
- package/dist/sessions/index.d.ts +3 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +2 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/transcript/index.d.ts +1 -1
- package/dist/transcript/index.js +1 -1
- package/dist/transcript/message-reducer.d.ts +1 -1
- package/dist/transcript/message-reducer.js +1 -1
- package/dist/transcript/part-accumulator.d.ts +1 -1
- package/dist/transcript/part-accumulator.js +1 -1
- package/dist/transcript/tool-display.d.ts +1 -1
- package/dist/transcript/tool-display.js +1 -1
- package/dist/transcript/tool-shape.d.ts +1 -1
- package/dist/transcript/tool-shape.js +1 -1
- package/dist/usage/finalize.d.ts +7 -9
- package/dist/usage/finalize.d.ts.map +1 -1
- package/dist/usage/finalize.js +11 -13
- package/dist/usage/finalize.js.map +1 -1
- package/dist/usage/index.d.ts +2 -3
- package/dist/usage/index.d.ts.map +1 -1
- package/dist/usage/index.js +2 -3
- package/dist/usage/index.js.map +1 -1
- package/dist/usage/model-catalog.d.ts +2 -3
- package/dist/usage/model-catalog.d.ts.map +1 -1
- package/dist/usage/model-catalog.js +10 -3
- package/dist/usage/model-catalog.js.map +1 -1
- package/dist/usage/normalize.d.ts +5 -7
- package/dist/usage/normalize.d.ts.map +1 -1
- package/dist/usage/normalize.js +3 -5
- package/dist/usage/normalize.js.map +1 -1
- package/dist/usage/ports.d.ts +14 -24
- package/dist/usage/ports.d.ts.map +1 -1
- package/dist/usage/ports.js +9 -17
- package/dist/usage/ports.js.map +1 -1
- package/dist/usage/steps.d.ts +70 -0
- package/dist/usage/steps.d.ts.map +1 -0
- package/dist/usage/steps.js +152 -0
- package/dist/usage/steps.js.map +1 -0
- package/dist/usage/tokens.d.ts +4 -7
- package/dist/usage/tokens.d.ts.map +1 -1
- package/dist/usage/tokens.js +4 -7
- package/dist/usage/tokens.js.map +1 -1
- package/docs/contracts/acp-and-step-usage.md +51 -0
- package/docs/contracts/conversation-persistence.md +47 -0
- package/docs/contracts/input-and-execution.md +177 -0
- package/docs/contracts/platform-authorization.md +17 -0
- package/host/adapters/acp-capabilities.js +30 -0
- package/host/adapters/acp-client.js +156 -0
- package/host/adapters/acp.js +284 -0
- package/host/adapters/claude.js +235 -0
- package/host/adapters/codex.js +733 -0
- package/host/adapters/cursor.js +347 -0
- package/host/adapters/input-capabilities.js +89 -0
- package/host/adapters/opencode.js +486 -0
- package/host/adapters/pi.js +519 -0
- package/host/adapters/prompt.js +212 -0
- package/host/auth.js +257 -0
- package/host/credentials/store.js +209 -0
- package/host/http.d.ts +32 -0
- package/host/http.js +238 -0
- package/host/index.d.ts +84 -0
- package/host/index.js +59 -0
- package/host/lib/runtime-paths.js +44 -0
- package/host/model-resolution.js +313 -0
- package/host/native-contract.js +185 -0
- package/host/native-runtime.js +194 -0
- package/host/permissions.js +27 -0
- package/host/platform-egress.js +114 -0
- package/host/recorder.js +61 -0
- package/host/sqlite/index.d.ts +69 -0
- package/host/sqlite/index.js +704 -0
- package/host/tooling/mcp-bundle.js +214 -0
- package/host/tooling/mcp-relay.js +83 -0
- package/host/tooling/native-binaries.js +667 -0
- package/host/tooling/native-config.js +460 -0
- package/host/tooling/package-import.js +111 -0
- package/host/tooling/runtime-home.js +26 -0
- package/host/tooling/system-instructions.js +69 -0
- package/host/tooling/system-prompt.js +35 -0
- package/package.json +45 -21
- package/skills/chat/SKILL.md +97 -0
- package/skills/chat/agents/openai.yaml +4 -0
- package/skills/chat/references/contracts.md +131 -0
- package/dist/api/chat-payloads.d.ts +0 -10
- package/dist/api/chat-payloads.d.ts.map +0 -1
- package/dist/api/chat-payloads.js +0 -57
- package/dist/api/chat-payloads.js.map +0 -1
- package/dist/contract/chat-input.d.ts +0 -144
- package/dist/contract/chat-input.d.ts.map +0 -1
- package/dist/contract/chat-input.js +0 -261
- package/dist/contract/chat-input.js.map +0 -1
- package/dist/contract/create.d.ts +0 -191
- package/dist/contract/create.d.ts.map +0 -1
- package/dist/contract/create.js +0 -249
- package/dist/contract/create.js.map +0 -1
- package/dist/contract/freeze.d.ts +0 -85
- package/dist/contract/freeze.d.ts.map +0 -1
- package/dist/contract/freeze.js +0 -151
- package/dist/contract/freeze.js.map +0 -1
- package/dist/contract/harnesses.d.ts +0 -21
- package/dist/contract/harnesses.d.ts.map +0 -1
- package/dist/contract/harnesses.js +0 -39
- package/dist/contract/harnesses.js.map +0 -1
- package/dist/contract/index.d.ts +0 -17
- package/dist/contract/index.d.ts.map +0 -1
- package/dist/contract/index.js +0 -17
- package/dist/contract/index.js.map +0 -1
- package/dist/contract/input.d.ts +0 -45
- package/dist/contract/input.d.ts.map +0 -1
- package/dist/contract/input.js +0 -78
- package/dist/contract/input.js.map +0 -1
- package/dist/contract/models.d.ts +0 -57
- package/dist/contract/models.d.ts.map +0 -1
- package/dist/contract/models.js +0 -380
- package/dist/contract/models.js.map +0 -1
- package/dist/contract/payload-merge.d.ts +0 -76
- package/dist/contract/payload-merge.d.ts.map +0 -1
- package/dist/contract/payload-merge.js +0 -215
- package/dist/contract/payload-merge.js.map +0 -1
- package/dist/events/tool-display.d.ts +0 -22
- package/dist/events/tool-display.d.ts.map +0 -1
- package/dist/events/tool-display.js +0 -60
- package/dist/events/tool-display.js.map +0 -1
- package/dist/events/tool-shape.d.ts +0 -48
- package/dist/events/tool-shape.d.ts.map +0 -1
- package/dist/events/tool-shape.js +0 -225
- package/dist/events/tool-shape.js.map +0 -1
- package/dist/turns/index.d.ts +0 -11
- package/dist/turns/index.d.ts.map +0 -1
- package/dist/turns/index.js +0 -11
- package/dist/turns/index.js.map +0 -1
- package/dist/turns/queries.d.ts +0 -36
- package/dist/turns/queries.d.ts.map +0 -1
- package/dist/turns/queries.js +0 -37
- package/dist/turns/queries.js.map +0 -1
- package/dist/turns/replay.d.ts +0 -105
- package/dist/turns/replay.d.ts.map +0 -1
- package/dist/turns/replay.js +0 -163
- package/dist/turns/replay.js.map +0 -1
- package/dist/turns/status.d.ts +0 -49
- package/dist/turns/status.d.ts.map +0 -1
- package/dist/turns/status.js +0 -56
- package/dist/turns/status.js.map +0 -1
- package/dist/turns/turn-store.d.ts +0 -109
- package/dist/turns/turn-store.d.ts.map +0 -1
- package/dist/turns/turn-store.js +0 -169
- package/dist/turns/turn-store.js.map +0 -1
- package/dist/warm/client-cache.d.ts +0 -47
- package/dist/warm/client-cache.d.ts.map +0 -1
- package/dist/warm/client-cache.js +0 -48
- package/dist/warm/client-cache.js.map +0 -1
- package/dist/warm/envelope-cache.d.ts +0 -79
- package/dist/warm/envelope-cache.d.ts.map +0 -1
- package/dist/warm/envelope-cache.js +0 -85
- package/dist/warm/envelope-cache.js.map +0 -1
- package/dist/warm/index.d.ts +0 -18
- package/dist/warm/index.d.ts.map +0 -1
- package/dist/warm/index.js +0 -12
- package/dist/warm/index.js.map +0 -1
- package/dist/warm/payload.d.ts +0 -137
- package/dist/warm/payload.d.ts.map +0 -1
- package/dist/warm/payload.js +0 -112
- package/dist/warm/payload.js.map +0 -1
- package/dist/warm/revision.d.ts +0 -70
- package/dist/warm/revision.d.ts.map +0 -1
- package/dist/warm/revision.js +0 -63
- package/dist/warm/revision.js.map +0 -1
- package/dist/warm/store-policy.d.ts +0 -57
- package/dist/warm/store-policy.d.ts.map +0 -1
- package/dist/warm/store-policy.js +0 -89
- package/dist/warm/store-policy.js.map +0 -1
- package/dist/warm/store.d.ts +0 -42
- package/dist/warm/store.d.ts.map +0 -1
- package/dist/warm/store.js +0 -69
- package/dist/warm/store.js.map +0 -1
- package/dist/wire/frames.d.ts +0 -208
- package/dist/wire/frames.d.ts.map +0 -1
- package/dist/wire/frames.js +0 -245
- package/dist/wire/frames.js.map +0 -1
- package/dist/wire/index.d.ts +0 -7
- package/dist/wire/index.d.ts.map +0 -1
- package/dist/wire/index.js +0 -7
- package/dist/wire/index.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** One host permission decision shape shared by every native adapter. */
|
|
2
|
+
|
|
3
|
+
export const PERMISSION_OUTCOMES = Object.freeze([
|
|
4
|
+
'allow_once',
|
|
5
|
+
'allow_always',
|
|
6
|
+
'deny',
|
|
7
|
+
'cancel',
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
export async function requestPermission(port, request) {
|
|
11
|
+
if (typeof port !== 'function') return Object.freeze({ outcome: 'deny' });
|
|
12
|
+
const decision = await port(Object.freeze({ ...request }));
|
|
13
|
+
const outcome = decision?.outcome;
|
|
14
|
+
if (!PERMISSION_OUTCOMES.includes(outcome)) {
|
|
15
|
+
throw new TypeError(`permission port returned an invalid outcome: ${String(outcome)}`);
|
|
16
|
+
}
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
outcome,
|
|
19
|
+
...(decision.updatedInput && typeof decision.updatedInput === 'object'
|
|
20
|
+
? { updatedInput: decision.updatedInput }
|
|
21
|
+
: {}),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function allowed(decision) {
|
|
26
|
+
return decision.outcome === 'allow_once' || decision.outcome === 'allow_always';
|
|
27
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const HOP_HEADERS = new Set([
|
|
2
|
+
'accept-encoding',
|
|
3
|
+
'connection',
|
|
4
|
+
'content-encoding',
|
|
5
|
+
'content-length',
|
|
6
|
+
'host',
|
|
7
|
+
'transfer-encoding',
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
function metadataHeaders(contract) {
|
|
11
|
+
return {
|
|
12
|
+
'x-amalgm-session-id': contract.sessionId,
|
|
13
|
+
'x-amalgm-message-id': contract.assistantMessageId || '',
|
|
14
|
+
'x-amalgm-turn-id': contract.assistantMessageId || '',
|
|
15
|
+
'x-amalgm-agent-id': contract.agentId,
|
|
16
|
+
'x-amalgm-harness-id': contract.harness,
|
|
17
|
+
'x-amalgm-auth-method': contract.authMethod,
|
|
18
|
+
'x-amalgm-model-id': contract.usageModelId || contract.modelId,
|
|
19
|
+
'x-amalgm-cwd': contract.cwd || '',
|
|
20
|
+
'x-amalgm-usage-owner': contract.usageOwner,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function requestHeaders(request) {
|
|
25
|
+
const headers = {};
|
|
26
|
+
for (const [name, value] of Object.entries(request.headers || {})) {
|
|
27
|
+
const lower = name.toLowerCase();
|
|
28
|
+
if (HOP_HEADERS.has(lower) || lower === 'authorization' || lower === 'x-api-key') continue;
|
|
29
|
+
if (value !== undefined) headers[lower] = Array.isArray(value) ? value[0] : value;
|
|
30
|
+
}
|
|
31
|
+
return headers;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function responseHeaders(response) {
|
|
35
|
+
const headers = {};
|
|
36
|
+
for (const [name, value] of response.headers.entries()) {
|
|
37
|
+
if (!HOP_HEADERS.has(name.toLowerCase())) headers[name] = value;
|
|
38
|
+
}
|
|
39
|
+
return headers;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Forward one provider request through the platform proxy for its bound session. */
|
|
43
|
+
export async function forwardPlatformEgress({ request, response, contract, proxy, upstreamPath }) {
|
|
44
|
+
const authorization = String(request.headers.authorization || '').replace(/^Bearer\s+/i, '').trim();
|
|
45
|
+
const apiKey = String(request.headers['x-api-key'] || '').trim();
|
|
46
|
+
if (!contract.auth?.tokenRef || (authorization || apiKey) !== contract.auth.tokenRef) {
|
|
47
|
+
response.writeHead(401, { 'content-type': 'application/json', 'cache-control': 'no-store' });
|
|
48
|
+
response.end(JSON.stringify({ error: 'Invalid Chat egress token' }));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const method = request.method || 'GET';
|
|
52
|
+
const url = `${proxy.baseUrl.replace(/\/$/, '')}${upstreamPath}`;
|
|
53
|
+
const body = ['GET', 'HEAD'].includes(method) ? undefined : await requestBytes(request);
|
|
54
|
+
const fetchImpl = proxy.fetch || fetch;
|
|
55
|
+
let upstream;
|
|
56
|
+
try {
|
|
57
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
58
|
+
const grant = await proxy.credentialBroker.authorize({
|
|
59
|
+
audience: 'amalgm-api-proxy',
|
|
60
|
+
scopes: [scopeForPath(upstreamPath)],
|
|
61
|
+
method,
|
|
62
|
+
url,
|
|
63
|
+
});
|
|
64
|
+
upstream = await fetchImpl(url, {
|
|
65
|
+
method,
|
|
66
|
+
headers: {
|
|
67
|
+
...requestHeaders(request),
|
|
68
|
+
...metadataHeaders(contract),
|
|
69
|
+
authorization: grant.authorization,
|
|
70
|
+
dpop: grant.dpop,
|
|
71
|
+
},
|
|
72
|
+
body,
|
|
73
|
+
});
|
|
74
|
+
if (upstream.status !== 401 || attempt === 1) break;
|
|
75
|
+
await upstream.body?.cancel().catch(() => undefined);
|
|
76
|
+
proxy.credentialBroker.invalidate(grant);
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
response.writeHead(503, { 'content-type': 'application/json', 'cache-control': 'no-store' });
|
|
80
|
+
response.end(JSON.stringify({
|
|
81
|
+
error: 'Platform authorization unavailable',
|
|
82
|
+
code: typeof error?.code === 'string' ? error.code : 'PLATFORM_AUTHORIZATION_FAILED',
|
|
83
|
+
}));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!upstream) throw new Error('Platform egress produced no upstream response');
|
|
87
|
+
response.writeHead(upstream.status, responseHeaders(upstream));
|
|
88
|
+
if (upstream.body) {
|
|
89
|
+
const reader = upstream.body.getReader();
|
|
90
|
+
while (true) {
|
|
91
|
+
const { done, value } = await reader.read();
|
|
92
|
+
if (done) break;
|
|
93
|
+
if (value) response.write(Buffer.from(value));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
response.end();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function scopeForPath(path) {
|
|
100
|
+
if (path.startsWith('/anthropic')) return 'llm:anthropic';
|
|
101
|
+
if (path.startsWith('/ai_gateway')) return 'llm:gateway';
|
|
102
|
+
if (path.startsWith('/google') || path.startsWith('/gemini')) return 'llm:google';
|
|
103
|
+
if (path.startsWith('/xai')) return 'llm:xai';
|
|
104
|
+
if (path.startsWith('/openai')) return 'llm:openai';
|
|
105
|
+
throw Object.assign(new Error('Platform egress route has no authorization scope'), {
|
|
106
|
+
code: 'EGRESS_SCOPE_UNKNOWN',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function requestBytes(request) {
|
|
111
|
+
const chunks = [];
|
|
112
|
+
for await (const chunk of request) chunks.push(Buffer.from(chunk));
|
|
113
|
+
return chunks.length > 0 ? Buffer.concat(chunks) : undefined;
|
|
114
|
+
}
|
package/host/recorder.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Opt-in redacted native-event recorder for adapter diagnostics. */
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { amalgmDir } from './lib/runtime-paths.js';
|
|
7
|
+
|
|
8
|
+
const SENSITIVE_KEY = /(api[-_]?key|authorization|auth|bearer|cookie|password|secret|credential|access[-_]?token|refresh[-_]?token|session[-_]?token|id[-_]?token)/i;
|
|
9
|
+
|
|
10
|
+
function recordEnabled() {
|
|
11
|
+
return process.env.AMALGM_CHAT_RECORD === '1';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function recordPath() {
|
|
15
|
+
if (process.env.AMALGM_CHAT_RECORD_PATH) return process.env.AMALGM_CHAT_RECORD_PATH;
|
|
16
|
+
const root = amalgmDir();
|
|
17
|
+
const session = process.env.AMALGM_CHAT_RECORD_SESSION || new Date().toISOString().slice(0, 10);
|
|
18
|
+
return path.join(root, 'chat-recordings', `${session}.ndjson`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function redactString(value) {
|
|
22
|
+
const home = os.homedir();
|
|
23
|
+
let next = String(value);
|
|
24
|
+
if (home) next = next.split(home).join('~');
|
|
25
|
+
next = next.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [redacted]');
|
|
26
|
+
next = next.replace(/\b(?:sk|sk-ant|v0)_[A-Za-z0-9._-]{12,}\b/g, '[redacted-key]');
|
|
27
|
+
return next;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function redact(value, depth = 0) {
|
|
31
|
+
if (depth > 8) return '[redacted-depth]';
|
|
32
|
+
if (value == null) return value;
|
|
33
|
+
if (typeof value === 'string') return redactString(value);
|
|
34
|
+
if (typeof value !== 'object') return value;
|
|
35
|
+
if (Array.isArray(value)) return value.map((entry) => redact(entry, depth + 1));
|
|
36
|
+
|
|
37
|
+
const output = {};
|
|
38
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
39
|
+
output[key] = SENSITIVE_KEY.test(key) ? '[redacted]' : redact(entry, depth + 1);
|
|
40
|
+
}
|
|
41
|
+
return output;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function recordNativeEvent(source, payload, metadata = {}) {
|
|
45
|
+
if (!recordEnabled()) return;
|
|
46
|
+
const file = recordPath();
|
|
47
|
+
const row = {
|
|
48
|
+
recordedAt: new Date().toISOString(),
|
|
49
|
+
source,
|
|
50
|
+
metadata: redact(metadata),
|
|
51
|
+
payload: redact(payload),
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
55
|
+
fs.appendFileSync(file, `${JSON.stringify(row)}\n`);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (process.env.AMALGM_CHAT_RECORD_DEBUG === '1') {
|
|
58
|
+
console.warn('[amalgm-chat-recorder] failed:', err.message);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type {
|
|
3
|
+
AppendTurnUpdateInput,
|
|
4
|
+
AppendTurnUpdatesInput,
|
|
5
|
+
BeginConversationTurnInput,
|
|
6
|
+
ConversationMessage,
|
|
7
|
+
ConversationRecord,
|
|
8
|
+
ConversationScope,
|
|
9
|
+
ConversationStorePort,
|
|
10
|
+
ConversationTurn,
|
|
11
|
+
CreateConversationInput,
|
|
12
|
+
ListConversationsOptions,
|
|
13
|
+
SealConversationTurnInput,
|
|
14
|
+
StoredTurnUpdate,
|
|
15
|
+
ProviderCheckpointRecord,
|
|
16
|
+
} from '../../dist/conversations/index.js';
|
|
17
|
+
|
|
18
|
+
export interface SqliteConversationStoreOptions {
|
|
19
|
+
databasePath?: string;
|
|
20
|
+
database?: Database.Database;
|
|
21
|
+
busyTimeoutMs?: number;
|
|
22
|
+
synchronous?: 'full' | 'normal';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class SqliteConversationStoreError extends Error {
|
|
26
|
+
readonly code: string;
|
|
27
|
+
constructor(code: string, message: string);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createSqliteJournalBatchPolicy(options?: {
|
|
31
|
+
maxUpdates?: number;
|
|
32
|
+
maxDelayMs?: number;
|
|
33
|
+
}): {
|
|
34
|
+
readonly maxUpdates: number;
|
|
35
|
+
readonly maxDelayMs: number;
|
|
36
|
+
readonly wait: (milliseconds: number) => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class SqliteConversationStore implements ConversationStorePort {
|
|
40
|
+
readonly database: Database.Database;
|
|
41
|
+
readonly databasePath: string;
|
|
42
|
+
constructor(options: SqliteConversationStoreOptions);
|
|
43
|
+
createConversation(input: CreateConversationInput): Promise<ConversationRecord>;
|
|
44
|
+
getConversation(scope: ConversationScope, conversationId: string): Promise<ConversationRecord | null>;
|
|
45
|
+
getConversationForOwner(ownerUserId: string, conversationId: string, includeArchived?: boolean): {
|
|
46
|
+
readonly scope: ConversationScope;
|
|
47
|
+
readonly record: ConversationRecord;
|
|
48
|
+
} | null;
|
|
49
|
+
listConversations(ownerUserId: string, options?: ListConversationsOptions): Promise<readonly ConversationRecord[]>;
|
|
50
|
+
updateTitle(scope: ConversationScope, conversationId: string, title: string, updatedAt: string): Promise<ConversationRecord>;
|
|
51
|
+
markSeen(scope: ConversationScope, conversationId: string, updatedAt: string): Promise<ConversationRecord>;
|
|
52
|
+
updateProjection(scope: ConversationScope, conversationId: string, input: {
|
|
53
|
+
readonly providerSessionId?: string | null;
|
|
54
|
+
readonly lastExecutionComputerId?: string | null;
|
|
55
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
56
|
+
}, updatedAt: string): ConversationRecord;
|
|
57
|
+
archiveConversation(scope: ConversationScope, conversationId: string, archivedAt: string): Promise<ConversationRecord>;
|
|
58
|
+
beginTurn(input: BeginConversationTurnInput): Promise<ConversationTurn>;
|
|
59
|
+
appendTurnUpdate(input: AppendTurnUpdateInput): Promise<StoredTurnUpdate>;
|
|
60
|
+
appendTurnUpdates(input: AppendTurnUpdatesInput): Promise<readonly StoredTurnUpdate[]>;
|
|
61
|
+
sealTurn(input: SealConversationTurnInput): Promise<ConversationTurn>;
|
|
62
|
+
getTurn(scope: ConversationScope, turnId: string): Promise<ConversationTurn | null>;
|
|
63
|
+
getLatestTurn(scope: ConversationScope, conversationId: string): Promise<ConversationTurn | null>;
|
|
64
|
+
listTurnUpdates(scope: ConversationScope, turnId: string, afterSequence?: number, limit?: number): Promise<readonly StoredTurnUpdate[]>;
|
|
65
|
+
listMessages(scope: ConversationScope, conversationId: string): Promise<readonly ConversationMessage[]>;
|
|
66
|
+
getProviderCheckpoint(scope: ConversationScope, conversationId: string, executionRevision: string): Promise<ProviderCheckpointRecord | null>;
|
|
67
|
+
recoverInterrupted(ownerUserId: string, recoveredAt: string): Promise<readonly ConversationTurn[]>;
|
|
68
|
+
close(): void;
|
|
69
|
+
}
|