@amodalai/runtime 0.3.64 → 0.3.65
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/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/routes/route-helpers.js +1 -0
- package/dist/src/routes/route-helpers.js.map +1 -1
- package/dist/src/routes/route-helpers.test.js +21 -1
- package/dist/src/routes/route-helpers.test.js.map +1 -1
- package/dist/src/session/stream-hooks.d.ts +6 -0
- package/dist/src/sinks/http-audit-sink.d.ts +57 -0
- package/dist/src/sinks/http-audit-sink.js +109 -0
- package/dist/src/sinks/http-audit-sink.js.map +1 -0
- package/dist/src/sinks/http-audit-sink.test.d.ts +6 -0
- package/dist/src/sinks/http-audit-sink.test.js +124 -0
- package/dist/src/sinks/http-audit-sink.test.js.map +1 -0
- package/dist/src/sinks/http-usage-sink.d.ts +49 -0
- package/dist/src/sinks/http-usage-sink.js +101 -0
- package/dist/src/sinks/http-usage-sink.js.map +1 -0
- package/dist/src/sinks/http-usage-sink.test.d.ts +6 -0
- package/dist/src/sinks/http-usage-sink.test.js +105 -0
- package/dist/src/sinks/http-usage-sink.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
package/dist/src/index.d.ts
CHANGED
|
@@ -36,6 +36,10 @@ export type { CredentialResolver } from './credentials.js';
|
|
|
36
36
|
export { DbScopeResolver, encryptSecret, decryptSecret, loadSecretsKey, SecretsCryptoError, SecretsKeyError, createSecretsRouter, } from './secrets/index.js';
|
|
37
37
|
export type { DbScopeResolverOptions, CreateSecretsRouterOptions } from './secrets/index.js';
|
|
38
38
|
export type { StreamHooks, TokenCounts, UsageReport } from './session/stream-hooks.js';
|
|
39
|
+
export { HttpAuditSink } from './sinks/http-audit-sink.js';
|
|
40
|
+
export type { AuditEntry, HttpAuditSinkOptions } from './sinks/http-audit-sink.js';
|
|
41
|
+
export { HttpUsageSink } from './sinks/http-usage-sink.js';
|
|
42
|
+
export type { HttpUsageSinkOptions } from './sinks/http-usage-sink.js';
|
|
39
43
|
export { routeOutput } from './output/output-router.js';
|
|
40
44
|
export { errorHandler } from './middleware/error-handler.js';
|
|
41
45
|
export { AmodalError, ProviderError, RateLimitError, ProviderTimeoutError, ToolExecutionError, StoreError, ConnectionError, SessionError, CompactionError, ConfigError, } from './errors.js';
|
package/dist/src/index.js
CHANGED
|
@@ -47,6 +47,9 @@ export { resolveScope } from './scope.js';
|
|
|
47
47
|
// Credential resolution
|
|
48
48
|
export { EnvCredentialResolver, ScopeSecretsResolver, ChainResolver } from './credentials.js';
|
|
49
49
|
export { DbScopeResolver, encryptSecret, decryptSecret, loadSecretsKey, SecretsCryptoError, SecretsKeyError, createSecretsRouter, } from './secrets/index.js';
|
|
50
|
+
// HTTP-batching sinks for audit + usage. Wire to `onAuditLog` / `onUsageReport`.
|
|
51
|
+
export { HttpAuditSink } from './sinks/http-audit-sink.js';
|
|
52
|
+
export { HttpUsageSink } from './sinks/http-usage-sink.js';
|
|
50
53
|
// Output routing (for automation result delivery)
|
|
51
54
|
export { routeOutput } from './output/output-router.js';
|
|
52
55
|
// Error handler
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-helpers.js","sourceRoot":"","sources":["../../../src/routes/route-helpers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,EAA0F;IAE1F,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC;AAE3C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AAW3D;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,KAA8B,EAC9B,OAAgB;IAEhB,IAAI,CAAC,KAAK,EAAE,aAAa;QAAE,OAAO,SAAS,CAAC;IAC5C,OAAO,CAAC,KAAgB,EAAE,EAAE;QAC1B,KAAK,CAAC,aAAa,EAAE,CAAC;YACpB,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,aAAa,EAAE,CAAC;YAChB,MAAM,EAAE;gBACN,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,YAAY,EAAE,KAAK,CAAC,iBAAiB;aACtC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,cAAwC,EACxC,KAA8B,EAC9B,GAAiB;IAEjB,MAAM,EAAC,OAAO,EAAE,SAAS,EAAC,GAAG,GAAG,CAAC;IAEjC,2EAA2E;IAC3E,2EAA2E;IAC3E,4EAA4E;IAC5E,MAAM,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,KAAK,EAAE,UAAU,EAAE,CAAC;QACtB,KAAK,CAAC,UAAU,CAAC;YACf,KAAK,EAAE,uBAAuB;YAC9B,aAAa,EAAE,OAAO,CAAC,EAAE;YACzB,OAAO,EAAE;gBACP,UAAU,EAAE,OAAO,CAAC,EAAE;gBACtB,UAAU,EAAE,SAAS;gBACrB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,QAAQ,EAAE,OAAO,CAAC,YAAY;aAC/B;SACF,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAK,EAAE,gBAAgB,EAAE,CAAC;QAC5B,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE;YAChE,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,QAAQ,EAAE,OAAO,CAAC,YAAY;SAC/B,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"route-helpers.js","sourceRoot":"","sources":["../../../src/routes/route-helpers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,EAA0F;IAE1F,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC;AAE3C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AAW3D;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,KAA8B,EAC9B,OAAgB;IAEhB,IAAI,CAAC,KAAK,EAAE,aAAa;QAAE,OAAO,SAAS,CAAC;IAC5C,OAAO,CAAC,KAAgB,EAAE,EAAE;QAC1B,KAAK,CAAC,aAAa,EAAE,CAAC;YACpB,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,aAAa,EAAE,CAAC;YAChB,MAAM,EAAE;gBACN,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,YAAY,EAAE,KAAK,CAAC,iBAAiB;aACtC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,cAAwC,EACxC,KAA8B,EAC9B,GAAiB;IAEjB,MAAM,EAAC,OAAO,EAAE,SAAS,EAAC,GAAG,GAAG,CAAC;IAEjC,2EAA2E;IAC3E,2EAA2E;IAC3E,4EAA4E;IAC5E,MAAM,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,KAAK,EAAE,UAAU,EAAE,CAAC;QACtB,KAAK,CAAC,UAAU,CAAC;YACf,KAAK,EAAE,uBAAuB;YAC9B,aAAa,EAAE,OAAO,CAAC,EAAE;YACzB,OAAO,EAAE;gBACP,UAAU,EAAE,OAAO,CAAC,EAAE;gBACtB,UAAU,EAAE,SAAS;gBACrB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,QAAQ,EAAE,OAAO,CAAC,YAAY;aAC/B;SACF,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAK,EAAE,gBAAgB,EAAE,CAAC;QAC5B,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE;YAChE,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,QAAQ,EAAE,OAAO,CAAC,YAAY;SAC/B,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
import { describe, expect, it, vi } from 'vitest';
|
|
7
7
|
import { adaptOnUsage } from './route-helpers.js';
|
|
8
8
|
describe('adaptOnUsage', () => {
|
|
9
|
-
it('includes
|
|
9
|
+
it('includes session id and scope id in usage reports', () => {
|
|
10
10
|
const onUsageReport = vi.fn();
|
|
11
11
|
const session = {
|
|
12
12
|
id: 'session-1',
|
|
13
|
+
scopeId: 'end-user-abc',
|
|
13
14
|
model: 'claude-sonnet-4-20250514',
|
|
14
15
|
};
|
|
15
16
|
const usage = {
|
|
@@ -23,6 +24,7 @@ describe('adaptOnUsage', () => {
|
|
|
23
24
|
adaptOnUsage({ onUsageReport }, session)?.(usage);
|
|
24
25
|
expect(onUsageReport).toHaveBeenCalledWith({
|
|
25
26
|
sessionId: 'session-1',
|
|
27
|
+
scopeId: 'end-user-abc',
|
|
26
28
|
model: 'claude-sonnet-4-20250514',
|
|
27
29
|
taskAgentRuns: 0,
|
|
28
30
|
tokens: {
|
|
@@ -32,5 +34,23 @@ describe('adaptOnUsage', () => {
|
|
|
32
34
|
},
|
|
33
35
|
});
|
|
34
36
|
});
|
|
37
|
+
it('passes empty scope id through for anonymous sessions', () => {
|
|
38
|
+
const onUsageReport = vi.fn();
|
|
39
|
+
const session = {
|
|
40
|
+
id: 'session-1',
|
|
41
|
+
scopeId: '',
|
|
42
|
+
model: 'claude-sonnet-4-20250514',
|
|
43
|
+
};
|
|
44
|
+
const usage = {
|
|
45
|
+
inputTokens: 1,
|
|
46
|
+
outputTokens: 1,
|
|
47
|
+
cachedInputTokens: 0,
|
|
48
|
+
cacheCreationInputTokens: 0,
|
|
49
|
+
totalTokens: 2,
|
|
50
|
+
turnNumber: 1,
|
|
51
|
+
};
|
|
52
|
+
adaptOnUsage({ onUsageReport }, session)?.(usage);
|
|
53
|
+
expect(onUsageReport).toHaveBeenCalledWith(expect.objectContaining({ scopeId: '' }));
|
|
54
|
+
});
|
|
35
55
|
});
|
|
36
56
|
//# sourceMappingURL=route-helpers.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-helpers.test.js","sourceRoot":"","sources":["../../../src/routes/route-helpers.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAEhD,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"route-helpers.test.js","sourceRoot":"","sources":["../../../src/routes/route-helpers.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAEhD,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG;YACd,EAAE,EAAE,WAAW;YACf,OAAO,EAAE,cAAc;YACvB,KAAK,EAAE,0BAA0B;SACvB,CAAC;QACb,MAAM,KAAK,GAAc;YACvB,WAAW,EAAE,GAAG;YAChB,YAAY,EAAE,EAAE;YAChB,iBAAiB,EAAE,EAAE;YACrB,wBAAwB,EAAE,CAAC;YAC3B,WAAW,EAAE,GAAG;YAChB,UAAU,EAAE,CAAC;SACd,CAAC;QAEF,YAAY,CAAC,EAAC,aAAa,EAAC,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,CAAC,aAAa,CAAC,CAAC,oBAAoB,CAAC;YACzC,SAAS,EAAE,WAAW;YACtB,OAAO,EAAE,cAAc;YACvB,KAAK,EAAE,0BAA0B;YACjC,aAAa,EAAE,CAAC;YAChB,MAAM,EAAE;gBACN,WAAW,EAAE,GAAG;gBAChB,YAAY,EAAE,EAAE;gBAChB,YAAY,EAAE,EAAE;aACjB;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG;YACd,EAAE,EAAE,WAAW;YACf,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,0BAA0B;SACvB,CAAC;QACb,MAAM,KAAK,GAAc;YACvB,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,CAAC;YACf,iBAAiB,EAAE,CAAC;YACpB,wBAAwB,EAAE,CAAC;YAC3B,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;SACd,CAAC;QAEF,YAAY,CAAC,EAAC,aAAa,EAAC,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,CAAC,aAAa,CAAC,CAAC,oBAAoB,CACxC,MAAM,CAAC,gBAAgB,CAAC,EAAC,OAAO,EAAE,EAAE,EAAC,CAAC,CACvC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -18,6 +18,12 @@ export interface TokenCounts {
|
|
|
18
18
|
export interface UsageReport {
|
|
19
19
|
/** Runtime session that produced this usage event. */
|
|
20
20
|
sessionId: string;
|
|
21
|
+
/**
|
|
22
|
+
* End-user identity (extracted from the auth JWT's scope claims), used
|
|
23
|
+
* for per-end-user billing and quota attribution. Empty string when the
|
|
24
|
+
* session has no associated end user (e.g. anonymous public agent).
|
|
25
|
+
*/
|
|
26
|
+
scopeId: string;
|
|
21
27
|
model: string;
|
|
22
28
|
taskAgentRuns: number;
|
|
23
29
|
tokens: TokenCounts;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
export interface AuditEntry {
|
|
7
|
+
event: string;
|
|
8
|
+
resource_name: string;
|
|
9
|
+
author?: string;
|
|
10
|
+
details?: Record<string, unknown>;
|
|
11
|
+
timestamp?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface HttpAuditSinkOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Full URL the batch is POSTed to. May include the literal placeholder
|
|
16
|
+
* `{agentId}`, which is replaced at send time with the URL-encoded
|
|
17
|
+
* `agentId` from the call. Example:
|
|
18
|
+
*
|
|
19
|
+
* 'https://platform.example.com/api/agents/{agentId}/audit-logs'
|
|
20
|
+
*/
|
|
21
|
+
url: string;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the bearer token to send with each batch. Called once per send;
|
|
24
|
+
* callers may rotate per (agent, partitionKey) to mirror per-end-user auth.
|
|
25
|
+
*/
|
|
26
|
+
getBearerToken: (ctx: {
|
|
27
|
+
agentId: string;
|
|
28
|
+
partitionKey?: string;
|
|
29
|
+
}) => string;
|
|
30
|
+
/** Default 2000ms */
|
|
31
|
+
flushIntervalMs?: number;
|
|
32
|
+
/** Default 20 — auto-flush threshold per (agentId, partitionKey) batch */
|
|
33
|
+
maxBatchSize?: number;
|
|
34
|
+
/** Default 10000ms — abort if a single POST takes longer */
|
|
35
|
+
requestTimeoutMs?: number;
|
|
36
|
+
}
|
|
37
|
+
export declare class HttpAuditSink {
|
|
38
|
+
private readonly url;
|
|
39
|
+
private readonly getBearerToken;
|
|
40
|
+
private readonly flushIntervalMs;
|
|
41
|
+
private readonly maxBatchSize;
|
|
42
|
+
private readonly requestTimeoutMs;
|
|
43
|
+
private readonly pending;
|
|
44
|
+
private flushTimer;
|
|
45
|
+
constructor(options: HttpAuditSinkOptions);
|
|
46
|
+
/**
|
|
47
|
+
* Queue an audit entry for batched delivery. Fire-and-forget — never throws.
|
|
48
|
+
*
|
|
49
|
+
* `partitionKey` separates batches that need different bearer tokens (e.g.
|
|
50
|
+
* one batch per end-user when their session JWT is the audit auth). Pass
|
|
51
|
+
* `undefined` when one shared bearer token covers all entries.
|
|
52
|
+
*/
|
|
53
|
+
log(agentId: string, partitionKey: string | undefined, entry: AuditEntry): void;
|
|
54
|
+
flush(): Promise<void>;
|
|
55
|
+
shutdown(): Promise<void>;
|
|
56
|
+
private send;
|
|
57
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Batched HTTP sink for audit entries.
|
|
8
|
+
*
|
|
9
|
+
* Wraps the boilerplate of queueing, batching, and POSTing audit events to a
|
|
10
|
+
* configurable URL with bearer-token auth. Wire to `StreamHooks.onAuditLog`
|
|
11
|
+
* in hosted setups; self-hosters point the URL at their own audit ingest.
|
|
12
|
+
*
|
|
13
|
+
* Failures are non-fatal — the sink logs and drops the batch rather than
|
|
14
|
+
* blocking chat handling. Use a Logger sink (Axiom, stdout JSON, etc.) to
|
|
15
|
+
* surface delivery problems.
|
|
16
|
+
*/
|
|
17
|
+
import { log } from '../logger.js';
|
|
18
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 2000;
|
|
19
|
+
const DEFAULT_MAX_BATCH_SIZE = 20;
|
|
20
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
|
|
21
|
+
export class HttpAuditSink {
|
|
22
|
+
url;
|
|
23
|
+
getBearerToken;
|
|
24
|
+
flushIntervalMs;
|
|
25
|
+
maxBatchSize;
|
|
26
|
+
requestTimeoutMs;
|
|
27
|
+
pending = new Map();
|
|
28
|
+
flushTimer;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.url = options.url;
|
|
31
|
+
this.getBearerToken = options.getBearerToken;
|
|
32
|
+
this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
|
|
33
|
+
this.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
|
|
34
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
35
|
+
this.flushTimer = setInterval(() => {
|
|
36
|
+
void this.flush();
|
|
37
|
+
}, this.flushIntervalMs);
|
|
38
|
+
this.flushTimer.unref();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Queue an audit entry for batched delivery. Fire-and-forget — never throws.
|
|
42
|
+
*
|
|
43
|
+
* `partitionKey` separates batches that need different bearer tokens (e.g.
|
|
44
|
+
* one batch per end-user when their session JWT is the audit auth). Pass
|
|
45
|
+
* `undefined` when one shared bearer token covers all entries.
|
|
46
|
+
*/
|
|
47
|
+
log(agentId, partitionKey, entry) {
|
|
48
|
+
const key = partitionKey ? `${agentId}:${partitionKey}` : agentId;
|
|
49
|
+
let batch = this.pending.get(key);
|
|
50
|
+
if (!batch) {
|
|
51
|
+
batch = { agentId, partitionKey, entries: [] };
|
|
52
|
+
this.pending.set(key, batch);
|
|
53
|
+
}
|
|
54
|
+
batch.entries.push({
|
|
55
|
+
...entry,
|
|
56
|
+
timestamp: entry.timestamp ?? new Date().toISOString(),
|
|
57
|
+
});
|
|
58
|
+
if (batch.entries.length >= this.maxBatchSize) {
|
|
59
|
+
const toSend = batch;
|
|
60
|
+
this.pending.delete(key);
|
|
61
|
+
void this.send(toSend);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async flush() {
|
|
65
|
+
const batches = [...this.pending.values()];
|
|
66
|
+
this.pending.clear();
|
|
67
|
+
await Promise.all(batches.map((b) => this.send(b)));
|
|
68
|
+
}
|
|
69
|
+
async shutdown() {
|
|
70
|
+
if (this.flushTimer) {
|
|
71
|
+
clearInterval(this.flushTimer);
|
|
72
|
+
this.flushTimer = null;
|
|
73
|
+
}
|
|
74
|
+
await this.flush();
|
|
75
|
+
}
|
|
76
|
+
async send(batch) {
|
|
77
|
+
const url = this.url.replace('{agentId}', encodeURIComponent(batch.agentId));
|
|
78
|
+
const token = this.getBearerToken({
|
|
79
|
+
agentId: batch.agentId,
|
|
80
|
+
partitionKey: batch.partitionKey,
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetch(url, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
86
|
+
headers: {
|
|
87
|
+
'Content-Type': 'application/json',
|
|
88
|
+
Authorization: `Bearer ${token}`,
|
|
89
|
+
},
|
|
90
|
+
body: JSON.stringify({ entries: batch.entries }),
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
log.warn('audit_sink_http_error', {
|
|
94
|
+
status: response.status,
|
|
95
|
+
agentId: batch.agentId,
|
|
96
|
+
batchSize: batch.entries.length,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
log.warn('audit_sink_send_failed', {
|
|
102
|
+
agentId: batch.agentId,
|
|
103
|
+
batchSize: batch.entries.length,
|
|
104
|
+
error: err instanceof Error ? err.message : String(err),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=http-audit-sink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-audit-sink.js","sourceRoot":"","sources":["../../../src/sinks/http-audit-sink.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import { HttpAuditSink } from './http-audit-sink.js';
|
|
8
|
+
function captureFetch(status = 200) {
|
|
9
|
+
const calls = [];
|
|
10
|
+
const fetchMock = vi.fn(async (input, init) => {
|
|
11
|
+
const url = String(input);
|
|
12
|
+
const body = typeof init?.body === 'string'
|
|
13
|
+
? JSON.parse(init.body)
|
|
14
|
+
: { entries: [] };
|
|
15
|
+
const headers = init?.headers;
|
|
16
|
+
const authorization = headers?.['Authorization'] ?? '';
|
|
17
|
+
calls.push({ url, body, authorization });
|
|
18
|
+
return new Response(null, { status });
|
|
19
|
+
});
|
|
20
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
21
|
+
return { fetchMock, calls };
|
|
22
|
+
}
|
|
23
|
+
describe('HttpAuditSink', () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.useFakeTimers();
|
|
26
|
+
});
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
vi.useRealTimers();
|
|
29
|
+
vi.unstubAllGlobals();
|
|
30
|
+
});
|
|
31
|
+
it('substitutes {agentId} in the URL template and sends with bearer auth', async () => {
|
|
32
|
+
const { calls } = captureFetch();
|
|
33
|
+
const sink = new HttpAuditSink({
|
|
34
|
+
url: 'https://api.example.com/api/agents/{agentId}/audit-logs',
|
|
35
|
+
getBearerToken: () => 'jwt-value',
|
|
36
|
+
maxBatchSize: 1,
|
|
37
|
+
});
|
|
38
|
+
sink.log('agent/special chars', undefined, {
|
|
39
|
+
event: 'session_completed',
|
|
40
|
+
resource_name: 'session-1',
|
|
41
|
+
});
|
|
42
|
+
await vi.runOnlyPendingTimersAsync();
|
|
43
|
+
expect(calls).toHaveLength(1);
|
|
44
|
+
expect(calls[0]?.url).toBe('https://api.example.com/api/agents/agent%2Fspecial%20chars/audit-logs');
|
|
45
|
+
expect(calls[0]?.authorization).toBe('Bearer jwt-value');
|
|
46
|
+
expect(calls[0]?.body.entries).toHaveLength(1);
|
|
47
|
+
expect(calls[0]?.body.entries[0]?.event).toBe('session_completed');
|
|
48
|
+
expect(calls[0]?.body.entries[0]?.timestamp).toBeDefined();
|
|
49
|
+
await sink.shutdown();
|
|
50
|
+
});
|
|
51
|
+
it('batches entries with the same agent + partition key', async () => {
|
|
52
|
+
const { calls } = captureFetch();
|
|
53
|
+
const sink = new HttpAuditSink({
|
|
54
|
+
url: 'https://api.example.com/audit',
|
|
55
|
+
getBearerToken: () => 'jwt',
|
|
56
|
+
maxBatchSize: 100,
|
|
57
|
+
});
|
|
58
|
+
sink.log('agent-1', 'user-a', { event: 'e1', resource_name: 'r' });
|
|
59
|
+
sink.log('agent-1', 'user-a', { event: 'e2', resource_name: 'r' });
|
|
60
|
+
sink.log('agent-1', 'user-a', { event: 'e3', resource_name: 'r' });
|
|
61
|
+
await sink.flush();
|
|
62
|
+
expect(calls).toHaveLength(1);
|
|
63
|
+
expect(calls[0]?.body.entries).toHaveLength(3);
|
|
64
|
+
await sink.shutdown();
|
|
65
|
+
});
|
|
66
|
+
it('separates batches by partition key', async () => {
|
|
67
|
+
const { calls } = captureFetch();
|
|
68
|
+
const sink = new HttpAuditSink({
|
|
69
|
+
url: 'https://api.example.com/audit',
|
|
70
|
+
getBearerToken: ({ partitionKey }) => partitionKey === 'user-a' ? 'token-a' : 'token-b',
|
|
71
|
+
maxBatchSize: 100,
|
|
72
|
+
});
|
|
73
|
+
sink.log('agent-1', 'user-a', { event: 'e1', resource_name: 'r' });
|
|
74
|
+
sink.log('agent-1', 'user-b', { event: 'e2', resource_name: 'r' });
|
|
75
|
+
await sink.flush();
|
|
76
|
+
expect(calls).toHaveLength(2);
|
|
77
|
+
const tokens = calls.map((c) => c.authorization).sort();
|
|
78
|
+
expect(tokens).toEqual(['Bearer token-a', 'Bearer token-b']);
|
|
79
|
+
await sink.shutdown();
|
|
80
|
+
});
|
|
81
|
+
it('auto-flushes when batch size threshold is hit', async () => {
|
|
82
|
+
const { calls } = captureFetch();
|
|
83
|
+
const sink = new HttpAuditSink({
|
|
84
|
+
url: 'https://api.example.com/audit',
|
|
85
|
+
getBearerToken: () => 'jwt',
|
|
86
|
+
maxBatchSize: 2,
|
|
87
|
+
});
|
|
88
|
+
sink.log('agent-1', undefined, { event: 'e1', resource_name: 'r' });
|
|
89
|
+
sink.log('agent-1', undefined, { event: 'e2', resource_name: 'r' });
|
|
90
|
+
// Auto-flush kicks in after the second entry — give microtasks a tick
|
|
91
|
+
await vi.runOnlyPendingTimersAsync();
|
|
92
|
+
expect(calls).toHaveLength(1);
|
|
93
|
+
expect(calls[0]?.body.entries).toHaveLength(2);
|
|
94
|
+
await sink.shutdown();
|
|
95
|
+
});
|
|
96
|
+
it('preserves the entry-supplied timestamp when present', async () => {
|
|
97
|
+
const { calls } = captureFetch();
|
|
98
|
+
const sink = new HttpAuditSink({
|
|
99
|
+
url: 'https://api.example.com/audit',
|
|
100
|
+
getBearerToken: () => 'jwt',
|
|
101
|
+
maxBatchSize: 1,
|
|
102
|
+
});
|
|
103
|
+
sink.log('agent-1', undefined, {
|
|
104
|
+
event: 'e1',
|
|
105
|
+
resource_name: 'r',
|
|
106
|
+
timestamp: '2026-01-01T00:00:00.000Z',
|
|
107
|
+
});
|
|
108
|
+
await vi.runOnlyPendingTimersAsync();
|
|
109
|
+
expect(calls[0]?.body.entries[0]?.timestamp).toBe('2026-01-01T00:00:00.000Z');
|
|
110
|
+
await sink.shutdown();
|
|
111
|
+
});
|
|
112
|
+
it('does not throw when fetch rejects (fire-and-forget)', async () => {
|
|
113
|
+
vi.stubGlobal('fetch', vi.fn(() => Promise.reject(new Error('network down'))));
|
|
114
|
+
const sink = new HttpAuditSink({
|
|
115
|
+
url: 'https://api.example.com/audit',
|
|
116
|
+
getBearerToken: () => 'jwt',
|
|
117
|
+
maxBatchSize: 1,
|
|
118
|
+
});
|
|
119
|
+
sink.log('agent-1', undefined, { event: 'e1', resource_name: 'r' });
|
|
120
|
+
await expect(sink.flush()).resolves.toBeUndefined();
|
|
121
|
+
await sink.shutdown();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=http-audit-sink.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-audit-sink.test.js","sourceRoot":"","sources":["../../../src/sinks/http-audit-sink.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import type { UsageReport } from '../session/stream-hooks.js';
|
|
7
|
+
export interface HttpUsageSinkOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Full URL the batch is POSTed to. May include the literal placeholder
|
|
10
|
+
* `{agentId}`, which is replaced at send time with the URL-encoded
|
|
11
|
+
* `agentId` from the call. Example:
|
|
12
|
+
*
|
|
13
|
+
* 'https://platform.example.com/api/agents/{agentId}/usage'
|
|
14
|
+
*/
|
|
15
|
+
url: string;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the bearer token to send with each batch. Called once per send.
|
|
18
|
+
* Typically returns the per-deploy JWT carried by the runtime — usage is
|
|
19
|
+
* not user-attributed at the auth layer (attribution is in `scopeId`).
|
|
20
|
+
*/
|
|
21
|
+
getBearerToken: (ctx: {
|
|
22
|
+
agentId: string;
|
|
23
|
+
}) => string;
|
|
24
|
+
/** Default 2000ms */
|
|
25
|
+
flushIntervalMs?: number;
|
|
26
|
+
/** Default 20 — auto-flush threshold per agent batch */
|
|
27
|
+
maxBatchSize?: number;
|
|
28
|
+
/** Default 10000ms — abort if a single POST takes longer */
|
|
29
|
+
requestTimeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
export declare class HttpUsageSink {
|
|
32
|
+
private readonly url;
|
|
33
|
+
private readonly getBearerToken;
|
|
34
|
+
private readonly flushIntervalMs;
|
|
35
|
+
private readonly maxBatchSize;
|
|
36
|
+
private readonly requestTimeoutMs;
|
|
37
|
+
private readonly pending;
|
|
38
|
+
private flushTimer;
|
|
39
|
+
constructor(options: HttpUsageSinkOptions);
|
|
40
|
+
/**
|
|
41
|
+
* Queue a usage report for batched delivery. Fire-and-forget — never throws.
|
|
42
|
+
* Reports are batched per `agentId`; consumers attribute per end-user via
|
|
43
|
+
* each report's `scopeId` field.
|
|
44
|
+
*/
|
|
45
|
+
log(agentId: string, report: UsageReport): void;
|
|
46
|
+
flush(): Promise<void>;
|
|
47
|
+
shutdown(): Promise<void>;
|
|
48
|
+
private send;
|
|
49
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Batched HTTP sink for per-turn usage reports.
|
|
8
|
+
*
|
|
9
|
+
* Wire to `StreamHooks.onUsageReport` in hosted setups; self-hosters point
|
|
10
|
+
* the URL at their own metering endpoint. Each report carries `scopeId`
|
|
11
|
+
* (end-user identity from the auth JWT) and `sessionId`, so consumers can
|
|
12
|
+
* attribute tokens per end-user — something LiteLLM-style proxies can't do
|
|
13
|
+
* because they only see the deploy's virtual key.
|
|
14
|
+
*
|
|
15
|
+
* Failures are non-fatal — the sink logs and drops the batch rather than
|
|
16
|
+
* blocking chat handling.
|
|
17
|
+
*/
|
|
18
|
+
import { log } from '../logger.js';
|
|
19
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 2000;
|
|
20
|
+
const DEFAULT_MAX_BATCH_SIZE = 20;
|
|
21
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
|
|
22
|
+
export class HttpUsageSink {
|
|
23
|
+
url;
|
|
24
|
+
getBearerToken;
|
|
25
|
+
flushIntervalMs;
|
|
26
|
+
maxBatchSize;
|
|
27
|
+
requestTimeoutMs;
|
|
28
|
+
pending = new Map();
|
|
29
|
+
flushTimer;
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.url = options.url;
|
|
32
|
+
this.getBearerToken = options.getBearerToken;
|
|
33
|
+
this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
|
|
34
|
+
this.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
|
|
35
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
36
|
+
this.flushTimer = setInterval(() => {
|
|
37
|
+
void this.flush();
|
|
38
|
+
}, this.flushIntervalMs);
|
|
39
|
+
this.flushTimer.unref();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Queue a usage report for batched delivery. Fire-and-forget — never throws.
|
|
43
|
+
* Reports are batched per `agentId`; consumers attribute per end-user via
|
|
44
|
+
* each report's `scopeId` field.
|
|
45
|
+
*/
|
|
46
|
+
log(agentId, report) {
|
|
47
|
+
let batch = this.pending.get(agentId);
|
|
48
|
+
if (!batch) {
|
|
49
|
+
batch = { agentId, reports: [] };
|
|
50
|
+
this.pending.set(agentId, batch);
|
|
51
|
+
}
|
|
52
|
+
batch.reports.push(report);
|
|
53
|
+
if (batch.reports.length >= this.maxBatchSize) {
|
|
54
|
+
const toSend = batch;
|
|
55
|
+
this.pending.delete(agentId);
|
|
56
|
+
void this.send(toSend);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async flush() {
|
|
60
|
+
const batches = [...this.pending.values()];
|
|
61
|
+
this.pending.clear();
|
|
62
|
+
await Promise.all(batches.map((b) => this.send(b)));
|
|
63
|
+
}
|
|
64
|
+
async shutdown() {
|
|
65
|
+
if (this.flushTimer) {
|
|
66
|
+
clearInterval(this.flushTimer);
|
|
67
|
+
this.flushTimer = null;
|
|
68
|
+
}
|
|
69
|
+
await this.flush();
|
|
70
|
+
}
|
|
71
|
+
async send(batch) {
|
|
72
|
+
const url = this.url.replace('{agentId}', encodeURIComponent(batch.agentId));
|
|
73
|
+
const token = this.getBearerToken({ agentId: batch.agentId });
|
|
74
|
+
try {
|
|
75
|
+
const response = await fetch(url, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
78
|
+
headers: {
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
Authorization: `Bearer ${token}`,
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({ reports: batch.reports }),
|
|
83
|
+
});
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
log.warn('usage_sink_http_error', {
|
|
86
|
+
status: response.status,
|
|
87
|
+
agentId: batch.agentId,
|
|
88
|
+
batchSize: batch.reports.length,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
log.warn('usage_sink_send_failed', {
|
|
94
|
+
agentId: batch.agentId,
|
|
95
|
+
batchSize: batch.reports.length,
|
|
96
|
+
error: err instanceof Error ? err.message : String(err),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=http-usage-sink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-usage-sink.js","sourceRoot":"","sources":["../../../src/sinks/http-usage-sink.ts"],"names":[],"mappings":"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"}
|