@vellumai/assistant 0.4.23 → 0.4.26
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/bun.lock +3 -0
- package/package.json +2 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -15
- package/src/__tests__/assistant-events-sse-hardening.test.ts +9 -3
- package/src/__tests__/call-controller.test.ts +80 -0
- package/src/__tests__/config-schema.test.ts +38 -178
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +4 -1
- package/src/__tests__/credential-security-invariants.test.ts +0 -2
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +2 -2
- package/src/__tests__/ipc-snapshot.test.ts +0 -9
- package/src/__tests__/onboarding-template-contract.test.ts +10 -20
- package/src/__tests__/relay-server.test.ts +3 -3
- package/src/__tests__/runtime-events-sse-parity.test.ts +10 -0
- package/src/__tests__/runtime-events-sse.test.ts +7 -0
- package/src/__tests__/session-runtime-assembly.test.ts +34 -8
- package/src/__tests__/system-prompt.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +12 -8
- package/src/__tests__/twilio-routes-twiml.test.ts +2 -2
- package/src/__tests__/twilio-routes.test.ts +2 -3
- package/src/__tests__/voice-quality.test.ts +21 -132
- package/src/calls/call-controller.ts +34 -29
- package/src/calls/relay-server.ts +11 -5
- package/src/calls/twilio-routes.ts +4 -38
- package/src/calls/voice-quality.ts +7 -63
- package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +7 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -5
- package/src/config/bundled-skills/phone-calls/SKILL.md +144 -83
- package/src/config/bundled-skills/sms-setup/SKILL.md +0 -20
- package/src/config/bundled-skills/twilio-setup/SKILL.md +9 -17
- package/src/config/bundled-skills/voice-setup/SKILL.md +36 -1
- package/src/config/bundled-skills/voice-setup/icon.svg +20 -0
- package/src/config/calls-schema.ts +3 -53
- package/src/config/elevenlabs-schema.ts +33 -0
- package/src/config/schema.ts +183 -137
- package/src/config/types.ts +0 -1
- package/src/daemon/handlers/browser.ts +1 -6
- package/src/daemon/ipc-contract/browser.ts +5 -14
- package/src/daemon/ipc-contract-inventory.json +0 -2
- package/src/daemon/session-agent-loop-handlers.ts +3 -0
- package/src/daemon/session-runtime-assembly.ts +9 -7
- package/src/mcp/client.ts +2 -1
- package/src/memory/conversation-crud.ts +339 -166
- package/src/runtime/auth/middleware.ts +87 -26
- package/src/runtime/routes/events-routes.ts +7 -0
- package/src/runtime/routes/inbound-message-handler.ts +3 -4
- package/src/schedule/scheduler.ts +159 -45
- package/src/security/secure-keys.ts +3 -3
- package/src/tools/browser/browser-manager.ts +72 -228
- package/src/tools/browser/browser-screencast.ts +0 -5
- package/src/tools/network/script-proxy/certs.ts +7 -237
- package/src/tools/network/script-proxy/connect-tunnel.ts +1 -82
- package/src/tools/network/script-proxy/http-forwarder.ts +2 -151
- package/src/tools/network/script-proxy/logging.ts +12 -196
- package/src/tools/network/script-proxy/mitm-handler.ts +2 -270
- package/src/tools/network/script-proxy/policy.ts +4 -152
- package/src/tools/network/script-proxy/router.ts +2 -60
- package/src/tools/network/script-proxy/server.ts +5 -137
- package/src/tools/network/script-proxy/types.ts +19 -125
- package/src/tools/system/voice-config.ts +23 -1
- package/src/util/logger.ts +4 -1
- package/src/__tests__/elevenlabs-config.test.ts +0 -95
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -407
- package/src/calls/elevenlabs-config.ts +0 -32
|
@@ -1,82 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* CONNECT tunnel handler — establishes a raw TCP tunnel between the
|
|
3
|
-
* client and a remote host for HTTPS pass-through (no MITM).
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { IncomingMessage } from 'node:http';
|
|
7
|
-
import { connect, type Socket } from 'node:net';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Parse and validate a CONNECT target of the form `host:port`.
|
|
11
|
-
* Returns null if the target is malformed.
|
|
12
|
-
* Strips brackets from IPv6 literals (e.g. `[::1]:443` -> `::1`).
|
|
13
|
-
*/
|
|
14
|
-
function parseTarget(url: string | undefined): { host: string; port: number } | null {
|
|
15
|
-
if (!url) return null;
|
|
16
|
-
|
|
17
|
-
const colonIdx = url.lastIndexOf(':');
|
|
18
|
-
if (colonIdx <= 0) return null; // no port separator, or leading colon only
|
|
19
|
-
|
|
20
|
-
let host = url.slice(0, colonIdx);
|
|
21
|
-
const portStr = url.slice(colonIdx + 1);
|
|
22
|
-
|
|
23
|
-
if (!host || !portStr) return null;
|
|
24
|
-
|
|
25
|
-
const port = Number(portStr);
|
|
26
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
|
|
27
|
-
|
|
28
|
-
// Strip brackets from IPv6 literals — net.connect expects the raw address
|
|
29
|
-
if (host.startsWith('[') && host.endsWith(']')) {
|
|
30
|
-
host = host.slice(1, -1);
|
|
31
|
-
if (!host) return null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return { host, port };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Handle an HTTP CONNECT request by establishing a bidirectional TCP
|
|
39
|
-
* tunnel to the requested target.
|
|
40
|
-
*/
|
|
41
|
-
export function handleConnect(
|
|
42
|
-
req: IncomingMessage,
|
|
43
|
-
clientSocket: Socket,
|
|
44
|
-
head: Buffer,
|
|
45
|
-
): void {
|
|
46
|
-
const target = parseTarget(req.url);
|
|
47
|
-
|
|
48
|
-
if (!target) {
|
|
49
|
-
clientSocket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
50
|
-
clientSocket.destroy();
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let tunnelEstablished = false;
|
|
55
|
-
|
|
56
|
-
const upstream = connect(target.port, target.host, () => {
|
|
57
|
-
tunnelEstablished = true;
|
|
58
|
-
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
|
59
|
-
|
|
60
|
-
// Forward any data already buffered by the HTTP parser
|
|
61
|
-
if (head.length > 0) {
|
|
62
|
-
upstream.write(head);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Bidirectional piping
|
|
66
|
-
upstream.pipe(clientSocket);
|
|
67
|
-
clientSocket.pipe(upstream);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
upstream.on('error', () => {
|
|
71
|
-
// Only send HTTP error if the tunnel hasn't been established yet;
|
|
72
|
-
// once established the client expects raw TLS, not HTTP framing
|
|
73
|
-
if (!tunnelEstablished && clientSocket.writable) {
|
|
74
|
-
clientSocket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
|
75
|
-
}
|
|
76
|
-
clientSocket.destroy();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
clientSocket.on('error', () => {
|
|
80
|
-
upstream.destroy();
|
|
81
|
-
});
|
|
82
|
-
}
|
|
1
|
+
export { handleConnect } from "@vellumai/proxy-sidecar";
|
|
@@ -1,151 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* them to the upstream server with full body streaming.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { type IncomingMessage, request as httpRequest, type ServerResponse } from 'node:http';
|
|
7
|
-
import { URL } from 'node:url';
|
|
8
|
-
|
|
9
|
-
/** Hop-by-hop headers that MUST NOT be forwarded between proxy hops. */
|
|
10
|
-
const HOP_BY_HOP = new Set([
|
|
11
|
-
'connection',
|
|
12
|
-
'keep-alive',
|
|
13
|
-
'proxy-authenticate',
|
|
14
|
-
'proxy-authorization',
|
|
15
|
-
'proxy-connection',
|
|
16
|
-
'te',
|
|
17
|
-
'trailer',
|
|
18
|
-
'transfer-encoding',
|
|
19
|
-
'upgrade',
|
|
20
|
-
]);
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Optional callback for credential injection or policy gating.
|
|
24
|
-
* Called before the upstream request is sent. Returns extra headers
|
|
25
|
-
* to merge, or null to reject the request.
|
|
26
|
-
*/
|
|
27
|
-
export type PolicyCallback = (
|
|
28
|
-
hostname: string,
|
|
29
|
-
port: number | null,
|
|
30
|
-
path: string,
|
|
31
|
-
scheme: 'http' | 'https',
|
|
32
|
-
) => Promise<Record<string, string> | null>;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Strip hop-by-hop headers and Connection-token headers (RFC 7230 s6.1)
|
|
36
|
-
* from an incoming header set, preserving multi-value arrays.
|
|
37
|
-
*/
|
|
38
|
-
function filterHeaders(raw: IncomingMessage['headers']): Record<string, string | string[]> {
|
|
39
|
-
// Collect extra headers listed in the Connection header (RFC 7230 s6.1)
|
|
40
|
-
const connectionTokens = new Set<string>();
|
|
41
|
-
const connValue = raw['connection'];
|
|
42
|
-
if (connValue) {
|
|
43
|
-
const values = Array.isArray(connValue) ? connValue : [connValue];
|
|
44
|
-
for (const v of values) {
|
|
45
|
-
for (const token of v.split(',')) {
|
|
46
|
-
connectionTokens.add(token.trim().toLowerCase());
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const out: Record<string, string | string[]> = {};
|
|
52
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
53
|
-
const lower = key.toLowerCase();
|
|
54
|
-
if (HOP_BY_HOP.has(lower)) continue;
|
|
55
|
-
if (connectionTokens.has(lower)) continue;
|
|
56
|
-
if (value === undefined) continue;
|
|
57
|
-
out[key] = value;
|
|
58
|
-
}
|
|
59
|
-
return out;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Forward a plain HTTP proxy request (absolute-URL form) to the upstream
|
|
64
|
-
* server and stream the response back to the client.
|
|
65
|
-
*/
|
|
66
|
-
export function forwardHttpRequest(
|
|
67
|
-
clientReq: IncomingMessage,
|
|
68
|
-
clientRes: ServerResponse,
|
|
69
|
-
policyCallback?: PolicyCallback,
|
|
70
|
-
): void {
|
|
71
|
-
const urlStr = clientReq.url;
|
|
72
|
-
if (!urlStr) {
|
|
73
|
-
clientRes.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
74
|
-
clientRes.end('Bad Request');
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
let parsed: URL;
|
|
79
|
-
try {
|
|
80
|
-
parsed = new URL(urlStr);
|
|
81
|
-
} catch {
|
|
82
|
-
clientRes.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
83
|
-
clientRes.end('Bad Request');
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (parsed.protocol !== 'http:') {
|
|
88
|
-
clientRes.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
89
|
-
clientRes.end('Only HTTP is supported for non-CONNECT proxy requests');
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const hostname = parsed.hostname;
|
|
94
|
-
const port = parsed.port ? Number(parsed.port) : 80;
|
|
95
|
-
const path = parsed.pathname + parsed.search;
|
|
96
|
-
|
|
97
|
-
const doForward = (extraHeaders: Record<string, string> = {}) => {
|
|
98
|
-
const headers = { ...filterHeaders(clientReq.headers), ...extraHeaders };
|
|
99
|
-
// Ensure Host header matches the upstream target
|
|
100
|
-
headers['host'] = parsed.host;
|
|
101
|
-
|
|
102
|
-
const upstreamReq = httpRequest(
|
|
103
|
-
{
|
|
104
|
-
hostname,
|
|
105
|
-
port,
|
|
106
|
-
path,
|
|
107
|
-
method: clientReq.method,
|
|
108
|
-
headers,
|
|
109
|
-
},
|
|
110
|
-
(upstreamRes: IncomingMessage) => {
|
|
111
|
-
const responseHeaders = filterHeaders(upstreamRes.headers);
|
|
112
|
-
clientRes.writeHead(upstreamRes.statusCode ?? 502, responseHeaders);
|
|
113
|
-
upstreamRes.on('error', () => {
|
|
114
|
-
clientRes.destroy();
|
|
115
|
-
});
|
|
116
|
-
upstreamRes.pipe(clientRes);
|
|
117
|
-
},
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
upstreamReq.on('error', () => {
|
|
121
|
-
// Don't leak internal error details — generic 502
|
|
122
|
-
if (!clientRes.headersSent) {
|
|
123
|
-
clientRes.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
124
|
-
}
|
|
125
|
-
clientRes.end('Bad Gateway');
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Stream client body to upstream
|
|
129
|
-
clientReq.pipe(upstreamReq);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
if (policyCallback) {
|
|
133
|
-
policyCallback(hostname, parsed.port ? Number(parsed.port) : null, path, 'http')
|
|
134
|
-
.then((extraHeaders) => {
|
|
135
|
-
if (extraHeaders == null) {
|
|
136
|
-
clientRes.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
137
|
-
clientRes.end('Forbidden');
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
doForward(extraHeaders);
|
|
141
|
-
})
|
|
142
|
-
.catch(() => {
|
|
143
|
-
if (!clientRes.headersSent) {
|
|
144
|
-
clientRes.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
145
|
-
}
|
|
146
|
-
clientRes.end('Bad Gateway');
|
|
147
|
-
});
|
|
148
|
-
} else {
|
|
149
|
-
doForward();
|
|
150
|
-
}
|
|
151
|
-
}
|
|
1
|
+
export type { PolicyCallback } from "@vellumai/proxy-sidecar";
|
|
2
|
+
export { forwardHttpRequest } from "@vellumai/proxy-sidecar";
|
|
@@ -1,196 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* Replace values of sensitive header keys with a redaction placeholder.
|
|
14
|
-
*
|
|
15
|
-
* Matching is case-insensitive — "Authorization" and "authorization"
|
|
16
|
-
* are both caught. The caller supplies the set of sensitive key names
|
|
17
|
-
* (lowercased) because different credential templates inject into
|
|
18
|
-
* different headers.
|
|
19
|
-
*/
|
|
20
|
-
export function sanitizeHeaders(
|
|
21
|
-
headers: Record<string, string>,
|
|
22
|
-
sensitiveKeys: string[],
|
|
23
|
-
): Record<string, string> {
|
|
24
|
-
const lower = new Set(sensitiveKeys.map((k) => k.toLowerCase()));
|
|
25
|
-
const out: Record<string, string> = {};
|
|
26
|
-
|
|
27
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
28
|
-
out[key] = lower.has(key.toLowerCase()) ? REDACTED : value;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return out;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Redact query-parameter values for sensitive param names.
|
|
36
|
-
*
|
|
37
|
-
* Returns a URL string where the values of `sensitiveParams` are
|
|
38
|
-
* replaced with the redaction placeholder. Non-sensitive params and
|
|
39
|
-
* the rest of the URL are preserved verbatim.
|
|
40
|
-
*/
|
|
41
|
-
export function sanitizeUrl(
|
|
42
|
-
url: string,
|
|
43
|
-
sensitiveParams: string[],
|
|
44
|
-
): string {
|
|
45
|
-
if (sensitiveParams.length === 0) return url;
|
|
46
|
-
|
|
47
|
-
// Guard against malformed input — return the URL unchanged if it
|
|
48
|
-
// doesn't contain a query string at all.
|
|
49
|
-
const qIdx = url.indexOf('?');
|
|
50
|
-
if (qIdx === -1) return url;
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
// Build a full URL if given an absolute path, otherwise parse as-is
|
|
54
|
-
const parseable = url.startsWith('/') ? `http://placeholder${url}` : url;
|
|
55
|
-
const parsed = new URL(parseable);
|
|
56
|
-
const lower = new Set(sensitiveParams.map((p) => p.toLowerCase()));
|
|
57
|
-
|
|
58
|
-
for (const key of Array.from(parsed.searchParams.keys())) {
|
|
59
|
-
if (lower.has(key.toLowerCase())) {
|
|
60
|
-
parsed.searchParams.set(key, REDACTED);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Reconstruct the original shape: if the input was a path we strip
|
|
65
|
-
// the placeholder origin so the caller gets back a relative path.
|
|
66
|
-
if (url.startsWith('/')) {
|
|
67
|
-
return parsed.pathname + parsed.search;
|
|
68
|
-
}
|
|
69
|
-
return parsed.toString();
|
|
70
|
-
} catch {
|
|
71
|
-
// Fail closed: if we can't parse the URL, strip the query string
|
|
72
|
-
// entirely rather than risk leaking secrets in log output.
|
|
73
|
-
return url.slice(0, qIdx);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Build a log-safe snapshot of an outbound proxy request.
|
|
79
|
-
*
|
|
80
|
-
* `sensitiveKeys` should include header names and query param names
|
|
81
|
-
* that carry credential values (e.g. "Authorization", "api_key").
|
|
82
|
-
*/
|
|
83
|
-
export function createSafeLogEntry(
|
|
84
|
-
req: { method: string; url: string; headers: Record<string, string> },
|
|
85
|
-
sensitiveKeys: string[],
|
|
86
|
-
): { method: string; url: string; headers: Record<string, string> } {
|
|
87
|
-
return {
|
|
88
|
-
method: req.method,
|
|
89
|
-
url: sanitizeUrl(req.url, sensitiveKeys),
|
|
90
|
-
headers: sanitizeHeaders(req.headers, sensitiveKeys),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ---------------------------------------------------------------------------
|
|
95
|
-
// Policy/rewrite decision trace
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
|
|
98
|
-
import type { PolicyDecision } from './types.js';
|
|
99
|
-
|
|
100
|
-
export interface ProxyDecisionTrace {
|
|
101
|
-
/** Target hostname. */
|
|
102
|
-
host: string;
|
|
103
|
-
/** Target port (null = default for scheme). */
|
|
104
|
-
port: number | null;
|
|
105
|
-
/** Request path. */
|
|
106
|
-
path: string;
|
|
107
|
-
/** Protocol scheme. */
|
|
108
|
-
scheme: 'http' | 'https';
|
|
109
|
-
/** The decision kind emitted by the policy engine. */
|
|
110
|
-
decisionKind: PolicyDecision['kind'];
|
|
111
|
-
/** Number of candidate templates that matched before disambiguation. */
|
|
112
|
-
candidateCount: number;
|
|
113
|
-
/** The host pattern of the selected template, if any. */
|
|
114
|
-
selectedPattern: string | null;
|
|
115
|
-
/** The credential ID of the selected credential, if any. */
|
|
116
|
-
selectedCredentialId: string | null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Strip the query string from a URL path so that secrets passed as
|
|
121
|
-
* query parameters (API keys, tokens) are never recorded in traces.
|
|
122
|
-
*/
|
|
123
|
-
export function stripQueryString(p: string): string {
|
|
124
|
-
const idx = p.indexOf('?');
|
|
125
|
-
return idx === -1 ? p : p.slice(0, idx);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Build a structured trace record from a policy decision.
|
|
130
|
-
*
|
|
131
|
-
* Intentionally excludes all secret-bearing fields (header values,
|
|
132
|
-
* storage keys, injected tokens) — only patterns, counts, and
|
|
133
|
-
* decision metadata are included. Query parameters are stripped from
|
|
134
|
-
* the path to prevent leaking secrets (API keys, tokens) into logs.
|
|
135
|
-
*/
|
|
136
|
-
export function buildDecisionTrace(
|
|
137
|
-
host: string,
|
|
138
|
-
port: number | null,
|
|
139
|
-
path: string,
|
|
140
|
-
scheme: 'http' | 'https',
|
|
141
|
-
decision: PolicyDecision,
|
|
142
|
-
): ProxyDecisionTrace {
|
|
143
|
-
let candidateCount = 0;
|
|
144
|
-
let selectedPattern: string | null = null;
|
|
145
|
-
let selectedCredentialId: string | null = null;
|
|
146
|
-
|
|
147
|
-
switch (decision.kind) {
|
|
148
|
-
case 'matched':
|
|
149
|
-
candidateCount = 1;
|
|
150
|
-
selectedPattern = decision.template.hostPattern;
|
|
151
|
-
selectedCredentialId = decision.credentialId;
|
|
152
|
-
break;
|
|
153
|
-
case 'ambiguous':
|
|
154
|
-
candidateCount = decision.candidates.length;
|
|
155
|
-
break;
|
|
156
|
-
case 'ask_missing_credential':
|
|
157
|
-
candidateCount = decision.matchingPatterns.length;
|
|
158
|
-
break;
|
|
159
|
-
// 'missing', 'unauthenticated', 'ask_unauthenticated' — no candidates
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
host,
|
|
164
|
-
port,
|
|
165
|
-
path: stripQueryString(path),
|
|
166
|
-
scheme,
|
|
167
|
-
decisionKind: decision.kind,
|
|
168
|
-
candidateCount,
|
|
169
|
-
selectedPattern,
|
|
170
|
-
selectedCredentialId,
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
// Credential ref resolution trace
|
|
176
|
-
// ---------------------------------------------------------------------------
|
|
177
|
-
|
|
178
|
-
export interface CredentialRefTrace {
|
|
179
|
-
/** The raw refs provided by the caller. */
|
|
180
|
-
rawRefs: string[];
|
|
181
|
-
/** The resolved canonical UUIDs. */
|
|
182
|
-
resolvedIds: string[];
|
|
183
|
-
/** Any refs that could not be resolved. */
|
|
184
|
-
unresolvedRefs: string[];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Build a credential ref resolution trace for diagnostic logging.
|
|
189
|
-
*/
|
|
190
|
-
export function buildCredentialRefTrace(
|
|
191
|
-
rawRefs: string[],
|
|
192
|
-
resolvedIds: string[],
|
|
193
|
-
unresolvedRefs: string[],
|
|
194
|
-
): CredentialRefTrace {
|
|
195
|
-
return { rawRefs, resolvedIds, unresolvedRefs };
|
|
196
|
-
}
|
|
1
|
+
export type {
|
|
2
|
+
CredentialRefTrace,
|
|
3
|
+
ProxyDecisionTrace,
|
|
4
|
+
} from "@vellumai/proxy-sidecar";
|
|
5
|
+
export {
|
|
6
|
+
buildCredentialRefTrace,
|
|
7
|
+
buildDecisionTrace,
|
|
8
|
+
createSafeLogEntry,
|
|
9
|
+
sanitizeHeaders,
|
|
10
|
+
sanitizeUrl,
|
|
11
|
+
stripQueryString,
|
|
12
|
+
} from "@vellumai/proxy-sidecar";
|