multicorn-shield 0.11.0 → 0.12.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.
@@ -8,7 +8,9 @@
8
8
  "additionalProperties": false,
9
9
  "properties": {
10
10
  "apiKey": {
11
- "type": "string"
11
+ "type": "string",
12
+ "pattern": "^mcs_.{12,}$",
13
+ "minLength": 16
12
14
  },
13
15
  "baseUrl": {
14
16
  "type": "string"
package/dist/proxy.cjs CHANGED
@@ -437,12 +437,186 @@ function mapMcpToolToScope(toolName) {
437
437
  return { service: head, permissionLevel, actionType };
438
438
  }
439
439
 
440
+ // src/logger/action-logger.ts
441
+ function createActionLogger(config) {
442
+ if (!config.apiKey || config.apiKey.trim().length === 0) {
443
+ throw new Error(
444
+ "[ActionLogger] API key is required. Provide it via the 'apiKey' config option."
445
+ );
446
+ }
447
+ const baseUrl = config.baseUrl ?? "https://api.multicorn.ai";
448
+ const timeout = config.timeout ?? 5e3;
449
+ if (!baseUrl.startsWith("https://") && !baseUrl.startsWith("http://localhost")) {
450
+ throw new Error(
451
+ `[ActionLogger] Base URL must use HTTPS for security. Received: "${baseUrl}". Use https:// or http://localhost for local development.`
452
+ );
453
+ }
454
+ const endpoint = `${baseUrl}/api/v1/actions`;
455
+ const batchEnabled = config.batchMode?.enabled ?? false;
456
+ const maxBatchSize = config.batchMode?.maxSize ?? 10;
457
+ const flushInterval = config.batchMode?.flushIntervalMs ?? 5e3;
458
+ const queue = [];
459
+ let flushTimer;
460
+ let isShutdown = false;
461
+ async function sendActions(actions) {
462
+ if (actions.length === 0) return;
463
+ const convertAction = (action) => ({
464
+ agent: action.agent,
465
+ service: action.service,
466
+ actionType: action.actionType,
467
+ status: action.status,
468
+ ...action.cost !== void 0 ? { cost: action.cost } : {},
469
+ ...action.metadata !== void 0 ? { metadata: action.metadata } : {}
470
+ });
471
+ const convertedActions = actions.map(convertAction);
472
+ const payload = batchEnabled ? { actions: convertedActions } : convertedActions[0];
473
+ let lastError;
474
+ for (let attempt = 0; attempt < 2; attempt++) {
475
+ try {
476
+ const controller = new AbortController();
477
+ const timeoutId = setTimeout(() => {
478
+ controller.abort();
479
+ }, timeout);
480
+ const response = await fetch(endpoint, {
481
+ method: "POST",
482
+ headers: {
483
+ "Content-Type": "application/json",
484
+ "X-Multicorn-Key": config.apiKey
485
+ },
486
+ body: JSON.stringify(payload),
487
+ signal: controller.signal
488
+ });
489
+ clearTimeout(timeoutId);
490
+ if (response.ok) {
491
+ return;
492
+ }
493
+ if (response.status >= 400 && response.status < 500) {
494
+ const body = await response.text().catch(() => "");
495
+ throw new Error(
496
+ `[ActionLogger] Client error (${String(response.status)}): ${response.statusText}. Response: ${body}. Check your API key and payload format.`
497
+ );
498
+ }
499
+ if (response.status >= 500 && attempt === 0) {
500
+ lastError = new Error(
501
+ `[ActionLogger] Server error (${String(response.status)}): ${response.statusText}. Retrying once...`
502
+ );
503
+ await sleep(100 * Math.pow(2, attempt));
504
+ continue;
505
+ }
506
+ throw new Error(
507
+ `[ActionLogger] Server error (${String(response.status)}) after retry: ${response.statusText}. Multicorn API may be experiencing issues.`
508
+ );
509
+ } catch (error) {
510
+ if (error instanceof Error) {
511
+ if (error.name === "AbortError") {
512
+ lastError = new Error(
513
+ `[ActionLogger] Request timeout after ${String(timeout)}ms. Increase the 'timeout' config option or check your network connection.`
514
+ );
515
+ } else if (error.message.includes("Client error") || error.message.includes("Server error")) {
516
+ lastError = error;
517
+ } else {
518
+ lastError = new Error(
519
+ `[ActionLogger] Network error: ${error.message}. Check your network connection and API endpoint.`
520
+ );
521
+ }
522
+ } else {
523
+ lastError = new Error(`[ActionLogger] Unknown error: ${String(error)}`);
524
+ }
525
+ if (attempt === 0 && !lastError.message.includes("Client error")) {
526
+ await sleep(100 * Math.pow(2, attempt));
527
+ continue;
528
+ }
529
+ break;
530
+ }
531
+ }
532
+ if (lastError) {
533
+ if (config.onError) {
534
+ config.onError(lastError);
535
+ }
536
+ }
537
+ }
538
+ async function flushQueue() {
539
+ if (queue.length === 0) return;
540
+ const actions = queue.map((item) => item.payload);
541
+ queue.length = 0;
542
+ await sendActions(actions);
543
+ }
544
+ function startFlushTimer() {
545
+ if (flushTimer !== void 0) return;
546
+ flushTimer = setInterval(() => {
547
+ flushQueue().catch(() => {
548
+ });
549
+ }, flushInterval);
550
+ const timer = flushTimer;
551
+ if (typeof timer.unref === "function") {
552
+ timer.unref();
553
+ }
554
+ }
555
+ function stopFlushTimer() {
556
+ if (flushTimer) {
557
+ clearInterval(flushTimer);
558
+ flushTimer = void 0;
559
+ }
560
+ }
561
+ if (batchEnabled) {
562
+ startFlushTimer();
563
+ }
564
+ return {
565
+ logAction(action) {
566
+ if (isShutdown) {
567
+ throw new Error(
568
+ "[ActionLogger] Cannot log action after shutdown. Create a new logger instance."
569
+ );
570
+ }
571
+ if (action.agent.trim().length === 0) {
572
+ throw new Error("[ActionLogger] Action must have a non-empty 'agent' field.");
573
+ }
574
+ if (action.service.trim().length === 0) {
575
+ throw new Error("[ActionLogger] Action must have a non-empty 'service' field.");
576
+ }
577
+ if (action.actionType.trim().length === 0) {
578
+ throw new Error("[ActionLogger] Action must have a non-empty 'actionType' field.");
579
+ }
580
+ if (action.status.trim().length === 0) {
581
+ throw new Error("[ActionLogger] Action must have a non-empty 'status' field.");
582
+ }
583
+ if (batchEnabled) {
584
+ queue.push({ payload: action, timestamp: Date.now() });
585
+ if (queue.length >= maxBatchSize) {
586
+ flushQueue().catch(() => {
587
+ });
588
+ }
589
+ } else {
590
+ sendActions([action]).catch(() => {
591
+ });
592
+ }
593
+ return Promise.resolve();
594
+ },
595
+ async flush() {
596
+ if (!batchEnabled) return;
597
+ await flushQueue();
598
+ },
599
+ async shutdown() {
600
+ if (isShutdown) return;
601
+ isShutdown = true;
602
+ stopFlushTimer();
603
+ if (batchEnabled) {
604
+ await flushQueue();
605
+ }
606
+ }
607
+ };
608
+ }
609
+ function sleep(ms) {
610
+ return new Promise((resolve) => setTimeout(resolve, ms));
611
+ }
612
+
440
613
  exports.ShieldAuthError = ShieldAuthError;
441
614
  exports.buildAuthErrorResponse = buildAuthErrorResponse;
442
615
  exports.buildBlockedResponse = buildBlockedResponse;
443
616
  exports.buildInternalErrorResponse = buildInternalErrorResponse;
444
617
  exports.buildServiceUnreachableResponse = buildServiceUnreachableResponse;
445
618
  exports.buildSpendingBlockedResponse = buildSpendingBlockedResponse;
619
+ exports.createActionLogger = createActionLogger;
446
620
  exports.createLogger = createLogger;
447
621
  exports.deriveDashboardUrl = deriveDashboardUrl;
448
622
  exports.extractActionFromToolName = extractActionFromToolName;
package/dist/proxy.d.cts CHANGED
@@ -68,6 +68,23 @@ declare const PERMISSION_LEVELS: {
68
68
  readonly Create: "create";
69
69
  };
70
70
  type PermissionLevel = (typeof PERMISSION_LEVELS)[keyof typeof PERMISSION_LEVELS];
71
+ /**
72
+ * Lifecycle states for an action processed by the policy engine.
73
+ *
74
+ * - `approved`: action passed policy checks and was executed
75
+ * - `blocked`: action was denied by policy
76
+ * - `pending`: action is awaiting human approval
77
+ * - `flagged`: action was executed but flagged for review
78
+ * - `requires_approval`: action requires content review before execution
79
+ */
80
+ declare const ACTION_STATUSES: {
81
+ readonly Approved: "approved";
82
+ readonly Blocked: "blocked";
83
+ readonly Pending: "pending";
84
+ readonly Flagged: "flagged";
85
+ readonly RequiresApproval: "requires_approval";
86
+ };
87
+ type ActionStatus = (typeof ACTION_STATUSES)[keyof typeof ACTION_STATUSES];
71
88
  /**
72
89
  * A single permission scope binding a service to an access level.
73
90
  *
@@ -232,4 +249,214 @@ interface McpToolScopeMapping {
232
249
  */
233
250
  declare function mapMcpToolToScope(toolName: string): McpToolScopeMapping;
234
251
 
235
- export { type AgentRecord, type JsonRpcError, type JsonRpcRequest, type JsonRpcResponse, type LogLevel, type ProxyLogger, ShieldAuthError, type ToolCallParams, buildAuthErrorResponse, buildBlockedResponse, buildInternalErrorResponse, buildServiceUnreachableResponse, buildSpendingBlockedResponse, createLogger, deriveDashboardUrl, extractActionFromToolName, extractServiceFromToolName, extractToolCallParams, fetchGrantedScopes, findAgentByName, isValidLogLevel, mapMcpToolToScope, parseJsonRpcLine, registerAgent, validateScopeAccess };
252
+ /**
253
+ * Action logging client for Multicorn Shield.
254
+ *
255
+ * Sends structured action events to the Multicorn hosted API, providing the
256
+ * observability backbone for Shield. Every agent action is logged with
257
+ * metadata, cost information, and status tracking.
258
+ *
259
+ * **Design principles:**
260
+ * - **Fire-and-forget**: Logging failures MUST NOT block the agent's action.
261
+ * - **Security (Jordan persona)**: API keys never logged, HTTPS enforced.
262
+ * - **Clear errors (Yuki persona)**: Descriptive messages for all failure modes.
263
+ * - **Clean async (The Team persona)**: Proper promise handling, no hanging requests.
264
+ *
265
+ * @module logger/action-logger
266
+ */
267
+
268
+ /**
269
+ * Configuration options for the action logger client.
270
+ *
271
+ * @example
272
+ * ```ts
273
+ * const config: ActionLoggerConfig = {
274
+ * apiKey: process.env.MULTICORN_API_KEY,
275
+ * baseUrl: "https://api.multicorn.ai",
276
+ * timeout: 5000,
277
+ * batchMode: {
278
+ * enabled: true,
279
+ * maxSize: 10,
280
+ * flushIntervalMs: 5000,
281
+ * },
282
+ * };
283
+ * ```
284
+ */
285
+ interface ActionLoggerConfig {
286
+ /**
287
+ * Multicorn API key for authentication.
288
+ * Passed as `X-Multicorn-Key` header. Never logged.
289
+ */
290
+ readonly apiKey: string;
291
+ /**
292
+ * Base URL for the Multicorn API.
293
+ * @default "https://api.multicorn.ai"
294
+ */
295
+ readonly baseUrl?: string;
296
+ /**
297
+ * Request timeout in milliseconds.
298
+ * @default 5000
299
+ */
300
+ readonly timeout?: number;
301
+ /**
302
+ * Optional batch mode configuration.
303
+ * When enabled, actions are queued and flushed periodically.
304
+ */
305
+ readonly batchMode?: BatchModeConfig;
306
+ /**
307
+ * Optional error handler for logging failures.
308
+ * Called asynchronously. Does not block the main action flow.
309
+ */
310
+ readonly onError?: (error: Error) => void;
311
+ }
312
+ /**
313
+ * Batch mode configuration.
314
+ *
315
+ * Actions are flushed when **either** condition is met:
316
+ * - The queue reaches `maxSize` actions, OR
317
+ * - `flushIntervalMs` milliseconds have elapsed since the last flush.
318
+ */
319
+ interface BatchModeConfig {
320
+ /** Whether batch mode is enabled. */
321
+ readonly enabled: boolean;
322
+ /**
323
+ * Maximum number of actions to queue before forcing a flush.
324
+ * @default 10
325
+ */
326
+ readonly maxSize?: number;
327
+ /**
328
+ * Maximum time (ms) between flushes.
329
+ * @default 5000
330
+ */
331
+ readonly flushIntervalMs?: number;
332
+ }
333
+ /**
334
+ * A single action event to be logged.
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * const action: ActionPayload = {
339
+ * agent: "inbox-assistant",
340
+ * service: "gmail",
341
+ * actionType: "send_email",
342
+ * status: "approved",
343
+ * cost: 0.002,
344
+ * metadata: {
345
+ * recipient: "user@example.com",
346
+ * subject: "Weekly report",
347
+ * },
348
+ * };
349
+ * ```
350
+ */
351
+ interface ActionPayload {
352
+ /** Agent identifier (e.g. "inbox-assistant"). */
353
+ readonly agent: string;
354
+ /** Service being accessed (e.g. "gmail", "slack"). */
355
+ readonly service: string;
356
+ /** Type of action performed (e.g. "send_email", "read_message"). */
357
+ readonly actionType: string;
358
+ /** Lifecycle status of the action. */
359
+ readonly status: ActionStatus;
360
+ /**
361
+ * Optional cost in USD incurred by this action.
362
+ * Present only for actions with usage-based pricing.
363
+ */
364
+ readonly cost?: number;
365
+ /**
366
+ * Optional structured metadata for additional context.
367
+ * Keys and values must be serializable to JSON.
368
+ */
369
+ readonly metadata?: Readonly<Record<string, string | number | boolean>>;
370
+ }
371
+ /**
372
+ * HTTP client for logging agent actions to the Multicorn Shield API.
373
+ *
374
+ * Supports both immediate and batched delivery modes. All network failures
375
+ * are handled gracefully to ensure logging never blocks the agent's execution.
376
+ *
377
+ * @example Basic usage (immediate mode)
378
+ * ```ts
379
+ * const logger = createActionLogger({
380
+ * apiKey: process.env.MULTICORN_API_KEY!,
381
+ * });
382
+ *
383
+ * await logger.logAction({
384
+ * agent: "email-assistant",
385
+ * service: "gmail",
386
+ * actionType: "send_email",
387
+ * status: "approved",
388
+ * cost: 0.002,
389
+ * });
390
+ * ```
391
+ *
392
+ * @example Batch mode with error handling
393
+ * ```ts
394
+ * const logger = createActionLogger({
395
+ * apiKey: process.env.MULTICORN_API_KEY!,
396
+ * batchMode: { enabled: true, maxSize: 10, flushIntervalMs: 5000 },
397
+ * onError: (err) => console.error("[ActionLogger]", err.message),
398
+ * });
399
+ *
400
+ * // Actions are queued and flushed automatically
401
+ * logger.logAction({
402
+ * agent: "inbox-assistant",
403
+ * service: "gmail",
404
+ * actionType: "read_message",
405
+ * status: "approved",
406
+ * });
407
+ *
408
+ * // Force immediate flush
409
+ * await logger.flush();
410
+ *
411
+ * // Clean up resources
412
+ * logger.shutdown();
413
+ * ```
414
+ */
415
+ interface ActionLogger {
416
+ /**
417
+ * Log a single action event.
418
+ *
419
+ * In immediate mode, sends the action to the API right away (non-blocking).
420
+ * In batch mode, queues the action and flushes when thresholds are met.
421
+ *
422
+ * @param action - The action event to log.
423
+ * @returns A promise that resolves when the action is sent (immediate mode)
424
+ * or queued (batch mode). Rejects only on validation errors, not
425
+ * network failures (those are passed to `onError`).
426
+ */
427
+ logAction(action: ActionPayload): Promise<void>;
428
+ /**
429
+ * Flush all queued actions immediately (batch mode only).
430
+ *
431
+ * In immediate mode, this is a no-op.
432
+ *
433
+ * @returns A promise that resolves when all queued actions have been sent.
434
+ */
435
+ flush(): Promise<void>;
436
+ /**
437
+ * Shut down the logger and clean up resources.
438
+ *
439
+ * Stops the flush timer (if in batch mode) and flushes any remaining actions.
440
+ * After calling `shutdown()`, the logger should not be used.
441
+ */
442
+ shutdown(): Promise<void>;
443
+ }
444
+ /**
445
+ * Create a new action logger client.
446
+ *
447
+ * @param config - Configuration options.
448
+ * @returns An {@link ActionLogger} instance.
449
+ * @throws {Error} If the API key is missing or the base URL is not HTTPS.
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * const logger = createActionLogger({
454
+ * apiKey: process.env.MULTICORN_API_KEY!,
455
+ * baseUrl: "https://api.multicorn.ai",
456
+ * timeout: 3000,
457
+ * });
458
+ * ```
459
+ */
460
+ declare function createActionLogger(config: ActionLoggerConfig): ActionLogger;
461
+
462
+ export { type ActionLogger, type ActionLoggerConfig, type ActionPayload, type AgentRecord, type JsonRpcError, type JsonRpcRequest, type JsonRpcResponse, type LogLevel, type PermissionLevel, type ProxyLogger, type Scope, ShieldAuthError, type ToolCallParams, buildAuthErrorResponse, buildBlockedResponse, buildInternalErrorResponse, buildServiceUnreachableResponse, buildSpendingBlockedResponse, createActionLogger, createLogger, deriveDashboardUrl, extractActionFromToolName, extractServiceFromToolName, extractToolCallParams, fetchGrantedScopes, findAgentByName, isValidLogLevel, mapMcpToolToScope, parseJsonRpcLine, registerAgent, validateScopeAccess };
package/dist/proxy.d.ts CHANGED
@@ -68,6 +68,23 @@ declare const PERMISSION_LEVELS: {
68
68
  readonly Create: "create";
69
69
  };
70
70
  type PermissionLevel = (typeof PERMISSION_LEVELS)[keyof typeof PERMISSION_LEVELS];
71
+ /**
72
+ * Lifecycle states for an action processed by the policy engine.
73
+ *
74
+ * - `approved`: action passed policy checks and was executed
75
+ * - `blocked`: action was denied by policy
76
+ * - `pending`: action is awaiting human approval
77
+ * - `flagged`: action was executed but flagged for review
78
+ * - `requires_approval`: action requires content review before execution
79
+ */
80
+ declare const ACTION_STATUSES: {
81
+ readonly Approved: "approved";
82
+ readonly Blocked: "blocked";
83
+ readonly Pending: "pending";
84
+ readonly Flagged: "flagged";
85
+ readonly RequiresApproval: "requires_approval";
86
+ };
87
+ type ActionStatus = (typeof ACTION_STATUSES)[keyof typeof ACTION_STATUSES];
71
88
  /**
72
89
  * A single permission scope binding a service to an access level.
73
90
  *
@@ -232,4 +249,214 @@ interface McpToolScopeMapping {
232
249
  */
233
250
  declare function mapMcpToolToScope(toolName: string): McpToolScopeMapping;
234
251
 
235
- export { type AgentRecord, type JsonRpcError, type JsonRpcRequest, type JsonRpcResponse, type LogLevel, type ProxyLogger, ShieldAuthError, type ToolCallParams, buildAuthErrorResponse, buildBlockedResponse, buildInternalErrorResponse, buildServiceUnreachableResponse, buildSpendingBlockedResponse, createLogger, deriveDashboardUrl, extractActionFromToolName, extractServiceFromToolName, extractToolCallParams, fetchGrantedScopes, findAgentByName, isValidLogLevel, mapMcpToolToScope, parseJsonRpcLine, registerAgent, validateScopeAccess };
252
+ /**
253
+ * Action logging client for Multicorn Shield.
254
+ *
255
+ * Sends structured action events to the Multicorn hosted API, providing the
256
+ * observability backbone for Shield. Every agent action is logged with
257
+ * metadata, cost information, and status tracking.
258
+ *
259
+ * **Design principles:**
260
+ * - **Fire-and-forget**: Logging failures MUST NOT block the agent's action.
261
+ * - **Security (Jordan persona)**: API keys never logged, HTTPS enforced.
262
+ * - **Clear errors (Yuki persona)**: Descriptive messages for all failure modes.
263
+ * - **Clean async (The Team persona)**: Proper promise handling, no hanging requests.
264
+ *
265
+ * @module logger/action-logger
266
+ */
267
+
268
+ /**
269
+ * Configuration options for the action logger client.
270
+ *
271
+ * @example
272
+ * ```ts
273
+ * const config: ActionLoggerConfig = {
274
+ * apiKey: process.env.MULTICORN_API_KEY,
275
+ * baseUrl: "https://api.multicorn.ai",
276
+ * timeout: 5000,
277
+ * batchMode: {
278
+ * enabled: true,
279
+ * maxSize: 10,
280
+ * flushIntervalMs: 5000,
281
+ * },
282
+ * };
283
+ * ```
284
+ */
285
+ interface ActionLoggerConfig {
286
+ /**
287
+ * Multicorn API key for authentication.
288
+ * Passed as `X-Multicorn-Key` header. Never logged.
289
+ */
290
+ readonly apiKey: string;
291
+ /**
292
+ * Base URL for the Multicorn API.
293
+ * @default "https://api.multicorn.ai"
294
+ */
295
+ readonly baseUrl?: string;
296
+ /**
297
+ * Request timeout in milliseconds.
298
+ * @default 5000
299
+ */
300
+ readonly timeout?: number;
301
+ /**
302
+ * Optional batch mode configuration.
303
+ * When enabled, actions are queued and flushed periodically.
304
+ */
305
+ readonly batchMode?: BatchModeConfig;
306
+ /**
307
+ * Optional error handler for logging failures.
308
+ * Called asynchronously. Does not block the main action flow.
309
+ */
310
+ readonly onError?: (error: Error) => void;
311
+ }
312
+ /**
313
+ * Batch mode configuration.
314
+ *
315
+ * Actions are flushed when **either** condition is met:
316
+ * - The queue reaches `maxSize` actions, OR
317
+ * - `flushIntervalMs` milliseconds have elapsed since the last flush.
318
+ */
319
+ interface BatchModeConfig {
320
+ /** Whether batch mode is enabled. */
321
+ readonly enabled: boolean;
322
+ /**
323
+ * Maximum number of actions to queue before forcing a flush.
324
+ * @default 10
325
+ */
326
+ readonly maxSize?: number;
327
+ /**
328
+ * Maximum time (ms) between flushes.
329
+ * @default 5000
330
+ */
331
+ readonly flushIntervalMs?: number;
332
+ }
333
+ /**
334
+ * A single action event to be logged.
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * const action: ActionPayload = {
339
+ * agent: "inbox-assistant",
340
+ * service: "gmail",
341
+ * actionType: "send_email",
342
+ * status: "approved",
343
+ * cost: 0.002,
344
+ * metadata: {
345
+ * recipient: "user@example.com",
346
+ * subject: "Weekly report",
347
+ * },
348
+ * };
349
+ * ```
350
+ */
351
+ interface ActionPayload {
352
+ /** Agent identifier (e.g. "inbox-assistant"). */
353
+ readonly agent: string;
354
+ /** Service being accessed (e.g. "gmail", "slack"). */
355
+ readonly service: string;
356
+ /** Type of action performed (e.g. "send_email", "read_message"). */
357
+ readonly actionType: string;
358
+ /** Lifecycle status of the action. */
359
+ readonly status: ActionStatus;
360
+ /**
361
+ * Optional cost in USD incurred by this action.
362
+ * Present only for actions with usage-based pricing.
363
+ */
364
+ readonly cost?: number;
365
+ /**
366
+ * Optional structured metadata for additional context.
367
+ * Keys and values must be serializable to JSON.
368
+ */
369
+ readonly metadata?: Readonly<Record<string, string | number | boolean>>;
370
+ }
371
+ /**
372
+ * HTTP client for logging agent actions to the Multicorn Shield API.
373
+ *
374
+ * Supports both immediate and batched delivery modes. All network failures
375
+ * are handled gracefully to ensure logging never blocks the agent's execution.
376
+ *
377
+ * @example Basic usage (immediate mode)
378
+ * ```ts
379
+ * const logger = createActionLogger({
380
+ * apiKey: process.env.MULTICORN_API_KEY!,
381
+ * });
382
+ *
383
+ * await logger.logAction({
384
+ * agent: "email-assistant",
385
+ * service: "gmail",
386
+ * actionType: "send_email",
387
+ * status: "approved",
388
+ * cost: 0.002,
389
+ * });
390
+ * ```
391
+ *
392
+ * @example Batch mode with error handling
393
+ * ```ts
394
+ * const logger = createActionLogger({
395
+ * apiKey: process.env.MULTICORN_API_KEY!,
396
+ * batchMode: { enabled: true, maxSize: 10, flushIntervalMs: 5000 },
397
+ * onError: (err) => console.error("[ActionLogger]", err.message),
398
+ * });
399
+ *
400
+ * // Actions are queued and flushed automatically
401
+ * logger.logAction({
402
+ * agent: "inbox-assistant",
403
+ * service: "gmail",
404
+ * actionType: "read_message",
405
+ * status: "approved",
406
+ * });
407
+ *
408
+ * // Force immediate flush
409
+ * await logger.flush();
410
+ *
411
+ * // Clean up resources
412
+ * logger.shutdown();
413
+ * ```
414
+ */
415
+ interface ActionLogger {
416
+ /**
417
+ * Log a single action event.
418
+ *
419
+ * In immediate mode, sends the action to the API right away (non-blocking).
420
+ * In batch mode, queues the action and flushes when thresholds are met.
421
+ *
422
+ * @param action - The action event to log.
423
+ * @returns A promise that resolves when the action is sent (immediate mode)
424
+ * or queued (batch mode). Rejects only on validation errors, not
425
+ * network failures (those are passed to `onError`).
426
+ */
427
+ logAction(action: ActionPayload): Promise<void>;
428
+ /**
429
+ * Flush all queued actions immediately (batch mode only).
430
+ *
431
+ * In immediate mode, this is a no-op.
432
+ *
433
+ * @returns A promise that resolves when all queued actions have been sent.
434
+ */
435
+ flush(): Promise<void>;
436
+ /**
437
+ * Shut down the logger and clean up resources.
438
+ *
439
+ * Stops the flush timer (if in batch mode) and flushes any remaining actions.
440
+ * After calling `shutdown()`, the logger should not be used.
441
+ */
442
+ shutdown(): Promise<void>;
443
+ }
444
+ /**
445
+ * Create a new action logger client.
446
+ *
447
+ * @param config - Configuration options.
448
+ * @returns An {@link ActionLogger} instance.
449
+ * @throws {Error} If the API key is missing or the base URL is not HTTPS.
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * const logger = createActionLogger({
454
+ * apiKey: process.env.MULTICORN_API_KEY!,
455
+ * baseUrl: "https://api.multicorn.ai",
456
+ * timeout: 3000,
457
+ * });
458
+ * ```
459
+ */
460
+ declare function createActionLogger(config: ActionLoggerConfig): ActionLogger;
461
+
462
+ export { type ActionLogger, type ActionLoggerConfig, type ActionPayload, type AgentRecord, type JsonRpcError, type JsonRpcRequest, type JsonRpcResponse, type LogLevel, type PermissionLevel, type ProxyLogger, type Scope, ShieldAuthError, type ToolCallParams, buildAuthErrorResponse, buildBlockedResponse, buildInternalErrorResponse, buildServiceUnreachableResponse, buildSpendingBlockedResponse, createActionLogger, createLogger, deriveDashboardUrl, extractActionFromToolName, extractServiceFromToolName, extractToolCallParams, fetchGrantedScopes, findAgentByName, isValidLogLevel, mapMcpToolToScope, parseJsonRpcLine, registerAgent, validateScopeAccess };