nexus-agents 8.127.10 → 8.128.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.
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // src/version.ts
6
6
  import semver from "semver";
7
- var VERSION = true ? "8.127.10" : "dev";
7
+ var VERSION = true ? "8.128.0" : "dev";
8
8
  var NODE_ENGINE_RANGE = ">=22.5.0";
9
9
  function isNodeVersionSupported(version) {
10
10
  return semver.satisfies(version, NODE_ENGINE_RANGE);
@@ -106,4 +106,4 @@ export {
106
106
  contextForLogging,
107
107
  measuredTrustTier
108
108
  };
109
- //# sourceMappingURL=chunk-HBABJ3TY.js.map
109
+ //# sourceMappingURL=chunk-BZCNC3A5.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/mcp/middleware/request-context.ts"],"sourcesContent":["/**\n * nexus-agents - Version constant\n *\n * Injected at build time via tsup define from package.json.\n * Do NOT edit VERSION manually — it is replaced during build.\n */\n\nimport semver from 'semver';\n\ndeclare const __NEXUS_VERSION__: string;\n\nexport const VERSION: string = typeof __NEXUS_VERSION__ !== 'undefined' ? __NEXUS_VERSION__ : 'dev';\n\n/** Supported Node.js versions. Pinned to package.json by a regression test. */\nexport const NODE_ENGINE_RANGE = '>=22.5.0';\n\n/** Returns whether a Node.js version satisfies the package engine requirement. */\nexport function isNodeVersionSupported(version: string): boolean {\n return semver.satisfies(version, NODE_ENGINE_RANGE);\n}\n","/**\n * nexus-agents/mcp - Request Context Middleware\n *\n * Provides request ID generation and caller context tracking for MCP tools.\n * (Source: Issue #185 Phase 1 - Request context & PolicyFirewall integration)\n *\n * @module mcp/middleware/request-context\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { randomBytes } from 'node:crypto';\nimport { getTimeProvider } from '../../core/index.js';\nimport type { TrustTier } from '../../security/trust-types.js';\n\n/**\n * Authenticated user information.\n * (Source: Issue #739 - MCP authentication)\n */\nexport interface AuthenticatedUser {\n /** Unique user/client identifier */\n readonly id: string;\n /** Human-readable name (optional) */\n readonly name?: string;\n /** Granted permissions/scopes (optional) */\n readonly permissions?: readonly string[];\n}\n\n/**\n * Caller identification for audit trails.\n */\nexport interface CallerInfo {\n /** Client identifier (e.g., 'claude-cli', 'gemini-cli') */\n readonly clientId?: string;\n /** User agent string if available */\n readonly userAgent?: string;\n /** Session ID for request correlation */\n readonly sessionId?: string;\n /** IP address or transport identifier */\n readonly transport?: string;\n /** Whether the request is authenticated (Issue #739) */\n readonly authenticated?: boolean;\n /** Authenticated user information (Issue #739) */\n readonly authenticatedUser?: AuthenticatedUser;\n}\n\n/**\n * Request context for MCP tool invocations.\n * Immutable once created.\n */\nexport interface RequestContext {\n /** Unique request identifier (format: req_<16 hex chars>) */\n readonly requestId: string;\n /** Timestamp when request was received (ISO 8601, ET) */\n readonly timestamp: string;\n /** Tool being invoked */\n readonly toolName: string;\n /** Caller information for audit */\n readonly caller: CallerInfo;\n /**\n * Trust tier for this request (Issue #828).\n * Derived from caller authentication state:\n * - '1' = Authenticated + known client, or stdio (local-only)\n * - '2' = Authenticated via network\n * - '3' = Unauthenticated network request\n * - '4' = Request with detected injection patterns (set by sanitizer)\n */\n readonly trustTier: TrustTier;\n /** Trace ID for distributed tracing correlation */\n readonly traceId?: string;\n /** Parent span ID if part of a larger trace */\n readonly parentSpanId?: string;\n}\n\n/**\n * Options for creating a request context.\n */\nexport interface CreateContextOptions {\n /** Tool name being invoked */\n toolName: string;\n /** Optional caller information */\n caller?: CallerInfo;\n /** Optional explicit trust tier override (defaults to derived from caller) */\n trustTier?: TrustTier;\n /** Optional trace ID for correlation */\n traceId?: string;\n /** Optional parent span ID */\n parentSpanId?: string;\n /**\n * Reuse this request id instead of minting a new one (#4981).\n *\n * Lets a nested layer join an outer layer's request identity while still\n * deriving its OWN caller, trust tier and timestamp. Adopting the outer\n * context object wholesale would discard those, which for a handler\n * configured with `callerInfo` silently downgrades the trust tier and the\n * audit actor.\n */\n inheritRequestId?: string;\n}\n\n/**\n * Generates a cryptographically secure request ID.\n * Format: req_<16 hex characters>\n *\n * @returns Unique request identifier\n */\nexport function generateRequestId(): string {\n const bytes = randomBytes(8);\n return `req_${bytes.toString('hex')}`;\n}\n\n/**\n * Generates a session ID for request correlation.\n * Format: sess_<12 hex characters>\n *\n * @returns Unique session identifier\n */\nexport function generateSessionId(): string {\n const bytes = randomBytes(6);\n return `sess_${bytes.toString('hex')}`;\n}\n\n/**\n * Formats timestamp in ISO 8601 format with ET timezone.\n * (Source: CLAUDE.md - Time Authority section)\n */\nfunction formatTimestamp(): string {\n const now = new Date(getTimeProvider().now());\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'America/New_York',\n timeZoneName: 'shortOffset',\n });\n const parts = formatter.formatToParts(now);\n const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? '-05:00';\n const base = now.toLocaleString('sv-SE', {\n timeZone: 'America/New_York',\n hour12: false,\n });\n return base.replace(' ', 'T') + offset.replace('GMT', '');\n}\n\n/**\n * Derives trust tier from caller authentication state (Issue #828).\n *\n * - Authenticated + known CLI client (claude, gemini, codex) → Tier 1\n * - stdio transport (local-only, no network) → Tier 1\n * - Authenticated via network → Tier 2\n * - Unauthenticated → Tier 3\n */\nexport function deriveTrustTier(caller: CallerInfo): TrustTier {\n const knownClients = ['claude-cli', 'gemini-cli', 'codex-cli'];\n\n if (caller.transport === 'stdio') return '1';\n\n if (caller.authenticated === true) {\n if (caller.clientId !== undefined && knownClients.includes(caller.clientId)) {\n return '1';\n }\n return '2';\n }\n\n return '3';\n}\n\n/**\n * The MCP transports this server can identify (#6795). Only stdio ships in\n * this tree; a transport the server cannot identify is not recorded at all, so\n * its requests stay on the authentication branch of {@link deriveTrustTier}.\n */\nexport type McpServerTransport = 'stdio';\n\n/**\n * The transport the server connected, recorded by `connectTransport` (#6795).\n * Process-wide because an MCP server process connects one transport, and the\n * request context is built per call by middleware that never sees it.\n */\nlet serverTransport: McpServerTransport | undefined;\n\n/**\n * Record the transport the server is connected over, or `undefined` to clear\n * it (an unidentified transport, a failed connect, or test cleanup).\n */\nexport function recordServerTransport(transport: McpServerTransport | undefined): void {\n serverTransport = transport;\n}\n\n/**\n * The caller information the server itself measured for every request: the\n * transport it arrived over (#6795). Empty when no transport was recorded, which\n * {@link measuredTrustTier} reports as unmeasured rather than as a tier.\n */\nexport function serverCallerInfo(): CallerInfo {\n return serverTransport === undefined ? {} : { transport: serverTransport };\n}\n\n/**\n * Creates an immutable request context for an MCP tool invocation.\n *\n * @param options - Context creation options\n * @returns Immutable request context\n */\nexport function createRequestContext(options: CreateContextOptions): RequestContext {\n const caller = options.caller ?? {};\n const context: RequestContext = {\n requestId: options.inheritRequestId ?? generateRequestId(),\n timestamp: formatTimestamp(),\n toolName: options.toolName,\n caller,\n trustTier: options.trustTier ?? deriveTrustTier(caller),\n ...(options.traceId !== undefined && { traceId: options.traceId }),\n ...(options.parentSpanId !== undefined && { parentSpanId: options.parentSpanId }),\n };\n\n // Freeze to ensure immutability\n return Object.freeze(context);\n}\n\n/**\n * The request context for the call currently in flight (#4981).\n *\n * Exists because a tool call passes through two middleware implementations\n * that each used to mint their own `RequestContext`, producing two unlinked\n * `req_*` ids and two `Tool invocation started` pairs per call. Argument\n * threading cannot fix that: `createSecureHandler` returns a 1-arity function,\n * so the chain's arity dispatch drops `ctx`, and 1-arity intermediaries such\n * as the `withPrerequisite` wrappers sit between the layers on some tools.\n * An ambient channel survives both.\n *\n * This mirrors the AbortSignal and progress-context storages the chain already\n * uses, so it is the established idiom here rather than a new mechanism.\n */\nconst requestContextStorage = new AsyncLocalStorage<ContextHolder>();\n\n/**\n * The ambient context plus its liveness.\n *\n * AsyncLocalStorage keeps a store reachable from anything spawned inside the\n * scope, including work that OUTLIVES the call — a `setTimeout`, or the\n * detached body `runAsJob` fires from inside the secure handler. Without this\n * flag such work still reads the context of a request that already completed,\n * and a nested handler for the same tool would adopt a finished identity, then\n * log neither a start (suppressed) nor a completion (the chain already closed\n * its own pair).\n *\n * Identity is not liveness, so the adoption gate needs both.\n */\ninterface ContextHolder {\n readonly context: RequestContext;\n settled: boolean;\n}\n\n/**\n * Runs `fn` with `context` as the ambient request context, so any nested layer\n * can adopt it instead of minting a second one.\n */\nexport function runWithRequestContext<T>(\n context: RequestContext,\n fn: () => Promise<T>\n): Promise<T> {\n const holder: ContextHolder = { context, settled: false };\n return requestContextStorage.run(holder, () =>\n fn().finally(() => {\n holder.settled = true;\n })\n );\n}\n\n/**\n * The ambient request context, or `undefined` when nothing established one.\n *\n * `undefined` is a real answer, not a failure: a caller that composes a secure\n * handler without the middleware chain — every direct test of it does — has no\n * outer context to inherit, and must mint its own.\n */\nexport function getCurrentRequestContext(): RequestContext | undefined {\n const holder = requestContextStorage.getStore();\n if (holder === undefined || holder.settled) return undefined;\n return holder.context;\n}\n\n/**\n * Formats request context for logging.\n * Extracts essential fields for log context.\n *\n * @param ctx - Request context\n * @returns Log-friendly context object\n */\nexport function contextForLogging(ctx: RequestContext): Record<string, unknown> {\n return {\n requestId: ctx.requestId,\n toolName: ctx.toolName,\n trustTier: ctx.trustTier,\n ...(ctx.caller.clientId !== undefined && { clientId: ctx.caller.clientId }),\n ...(ctx.traceId !== undefined && { traceId: ctx.traceId }),\n };\n}\n\n/**\n * Type guard to check if a value is a valid RequestContext.\n */\nexport function isRequestContext(value: unknown): value is RequestContext {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n return (\n typeof obj['requestId'] === 'string' &&\n obj['requestId'].startsWith('req_') &&\n typeof obj['timestamp'] === 'string' &&\n typeof obj['toolName'] === 'string' &&\n typeof obj['caller'] === 'object' &&\n typeof obj['trustTier'] === 'string' &&\n ['1', '2', '3', '4'].includes(obj['trustTier'])\n );\n}\n\n/**\n * The request's trust tier ONLY when it was actually derived from caller\n * information (#4733).\n *\n * `createRequestContext` falls back to `caller = {}`, and `deriveTrustTier({})`\n * returns `'3'` — so an absent caller is indistinguishable from a genuinely\n * untrusted one at the reading end. Since #6795 the middleware chain supplies\n * the transport the server connected ({@link serverCallerInfo}), so a stdio\n * server measures tier 1; a context built with no recorded transport and no\n * authentication fact is still that fallback.\n *\n * Returning `undefined` for the fallback lets a consumer record `unmeasured`\n * rather than a constant that reads as a measurement.\n *\n * NOTE this is caller AUTHENTICATION (transport / authenticated / clientId),\n * not content provenance. A trusted client can submit hostile content. Consumers\n * wanting provenance need a different signal — `classifyTrust` is the closest,\n * but it requires a GitHub actor and does not apply to a bare goal string.\n */\nexport function measuredTrustTier(context: RequestContext): string | undefined {\n // `caller` is always set by `createRequestContext` (to `{}` at minimum), but\n // a partially-constructed context can omit it — and an absent caller is the\n // same condition as an empty one: nothing was measured.\n const caller: unknown = context.caller;\n if (typeof caller !== 'object' || caller === null) return undefined;\n\n // Gate on the fields `deriveTrustTier` actually reads, not on \"the object has\n // any key at all\" (#4738 review). A caller carrying only `sessionId` or\n // `userAgent` feeds nothing into the derivation, so a non-empty check would\n // call the '3' fallback a measurement — reintroducing the constant this\n // function exists to prevent, in the function that prevents it.\n // `clientId` is NOT sufficient either: `deriveTrustTier` reads `clientId`\n // only INSIDE the `authenticated === true` branch, so\n // `{ clientId: 'claude-cli' }` returns the same '3' fallback as `{}`. (The\n // removed env-sniffing `extractCallerInfo` produced exactly that shape.)\n const info = caller as Partial<CallerInfo>;\n // `authenticated: false` counts: it is a measured fact (\"we checked, they\n // are not authenticated\"), which is different from the field being absent.\n const derivable = info.transport !== undefined || info.authenticated !== undefined;\n\n return derivable ? context.trustTier : undefined;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/version.ts","../src/mcp/middleware/request-context.ts"],"sourcesContent":["/**\n * nexus-agents - Version constant\n *\n * Injected at build time via tsup define from package.json.\n * Do NOT edit VERSION manually — it is replaced during build.\n */\n\nimport semver from 'semver';\n\ndeclare const __NEXUS_VERSION__: string;\n\nexport const VERSION: string = typeof __NEXUS_VERSION__ !== 'undefined' ? __NEXUS_VERSION__ : 'dev';\n\n/** Supported Node.js versions. Pinned to package.json by a regression test. */\nexport const NODE_ENGINE_RANGE = '>=22.5.0';\n\n/** Returns whether a Node.js version satisfies the package engine requirement. */\nexport function isNodeVersionSupported(version: string): boolean {\n return semver.satisfies(version, NODE_ENGINE_RANGE);\n}\n","/**\n * nexus-agents/mcp - Request Context Middleware\n *\n * Provides request ID generation and caller context tracking for MCP tools.\n * (Source: Issue #185 Phase 1 - Request context & PolicyFirewall integration)\n *\n * @module mcp/middleware/request-context\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { randomBytes } from 'node:crypto';\nimport { getTimeProvider } from '../../core/index.js';\nimport type { TrustTier } from '../../security/trust-types.js';\n\n/**\n * Authenticated user information.\n * (Source: Issue #739 - MCP authentication)\n */\nexport interface AuthenticatedUser {\n /** Unique user/client identifier */\n readonly id: string;\n /** Human-readable name (optional) */\n readonly name?: string;\n /** Granted permissions/scopes (optional) */\n readonly permissions?: readonly string[];\n}\n\n/**\n * Caller identification for audit trails.\n */\nexport interface CallerInfo {\n /** Client identifier (e.g., 'claude-cli', 'gemini-cli') */\n readonly clientId?: string;\n /** User agent string if available */\n readonly userAgent?: string;\n /** Session ID for request correlation */\n readonly sessionId?: string;\n /** IP address or transport identifier */\n readonly transport?: string;\n /** Whether the request is authenticated (Issue #739) */\n readonly authenticated?: boolean;\n /** Authenticated user information (Issue #739) */\n readonly authenticatedUser?: AuthenticatedUser;\n}\n\n/**\n * Request context for MCP tool invocations.\n * Immutable once created.\n */\nexport interface RequestContext {\n /** Unique request identifier (format: req_<16 hex chars>) */\n readonly requestId: string;\n /** Timestamp when request was received (ISO 8601, ET) */\n readonly timestamp: string;\n /** Tool being invoked */\n readonly toolName: string;\n /** Caller information for audit */\n readonly caller: CallerInfo;\n /**\n * Trust tier for this request (Issue #828).\n * Derived from caller authentication state:\n * - '1' = Authenticated + known client, or stdio (local-only)\n * - '2' = Authenticated via network\n * - '3' = Unauthenticated network request\n * - '4' = Request with detected injection patterns (set by sanitizer)\n */\n readonly trustTier: TrustTier;\n /** Trace ID for distributed tracing correlation */\n readonly traceId?: string;\n /** Parent span ID if part of a larger trace */\n readonly parentSpanId?: string;\n}\n\n/**\n * Options for creating a request context.\n */\nexport interface CreateContextOptions {\n /** Tool name being invoked */\n toolName: string;\n /** Optional caller information */\n caller?: CallerInfo;\n /** Optional explicit trust tier override (defaults to derived from caller) */\n trustTier?: TrustTier;\n /** Optional trace ID for correlation */\n traceId?: string;\n /** Optional parent span ID */\n parentSpanId?: string;\n /**\n * Reuse this request id instead of minting a new one (#4981).\n *\n * Lets a nested layer join an outer layer's request identity while still\n * deriving its OWN caller, trust tier and timestamp. Adopting the outer\n * context object wholesale would discard those, which for a handler\n * configured with `callerInfo` silently downgrades the trust tier and the\n * audit actor.\n */\n inheritRequestId?: string;\n}\n\n/**\n * Generates a cryptographically secure request ID.\n * Format: req_<16 hex characters>\n *\n * @returns Unique request identifier\n */\nexport function generateRequestId(): string {\n const bytes = randomBytes(8);\n return `req_${bytes.toString('hex')}`;\n}\n\n/**\n * Generates a session ID for request correlation.\n * Format: sess_<12 hex characters>\n *\n * @returns Unique session identifier\n */\nexport function generateSessionId(): string {\n const bytes = randomBytes(6);\n return `sess_${bytes.toString('hex')}`;\n}\n\n/**\n * Formats timestamp in ISO 8601 format with ET timezone.\n * (Source: CLAUDE.md - Time Authority section)\n */\nfunction formatTimestamp(): string {\n const now = new Date(getTimeProvider().now());\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone: 'America/New_York',\n timeZoneName: 'shortOffset',\n });\n const parts = formatter.formatToParts(now);\n const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? '-05:00';\n const base = now.toLocaleString('sv-SE', {\n timeZone: 'America/New_York',\n hour12: false,\n });\n return base.replace(' ', 'T') + offset.replace('GMT', '');\n}\n\n/**\n * Derives trust tier from caller authentication state (Issue #828).\n *\n * - Authenticated + known CLI client (claude, gemini, codex) → Tier 1\n * - stdio transport (local-only, no network) → Tier 1\n * - Authenticated via network → Tier 2\n * - Unauthenticated → Tier 3\n */\nexport function deriveTrustTier(caller: CallerInfo): TrustTier {\n const knownClients = ['claude-cli', 'gemini-cli', 'codex-cli'];\n\n if (caller.transport === 'stdio') return '1';\n\n if (caller.authenticated === true) {\n if (caller.clientId !== undefined && knownClients.includes(caller.clientId)) {\n return '1';\n }\n return '2';\n }\n\n return '3';\n}\n\n/**\n * The MCP transports this server can identify (#6795). Only stdio ships in\n * this tree; a transport the server cannot identify is not recorded at all, so\n * its requests stay on the authentication branch of {@link deriveTrustTier}.\n */\nexport type McpServerTransport = 'stdio';\n\n/**\n * The transport the server connected, recorded by `connectTransport` (#6795).\n * Process-wide because an MCP server process connects one transport, and the\n * request context is built per call by middleware that never sees it.\n */\nlet serverTransport: McpServerTransport | undefined;\n\n/**\n * Record the transport the server is connected over, or `undefined` to clear\n * it (an unidentified transport, a failed connect, or test cleanup).\n */\nexport function recordServerTransport(transport: McpServerTransport | undefined): void {\n serverTransport = transport;\n}\n\n/**\n * The caller information the server itself measured for every request: the\n * transport it arrived over (#6795). Empty when no transport was recorded, which\n * {@link measuredTrustTier} reports as unmeasured rather than as a tier.\n */\nexport function serverCallerInfo(): CallerInfo {\n return serverTransport === undefined ? {} : { transport: serverTransport };\n}\n\n/**\n * Creates an immutable request context for an MCP tool invocation.\n *\n * @param options - Context creation options\n * @returns Immutable request context\n */\nexport function createRequestContext(options: CreateContextOptions): RequestContext {\n const caller = options.caller ?? {};\n const context: RequestContext = {\n requestId: options.inheritRequestId ?? generateRequestId(),\n timestamp: formatTimestamp(),\n toolName: options.toolName,\n caller,\n trustTier: options.trustTier ?? deriveTrustTier(caller),\n ...(options.traceId !== undefined && { traceId: options.traceId }),\n ...(options.parentSpanId !== undefined && { parentSpanId: options.parentSpanId }),\n };\n\n // Freeze to ensure immutability\n return Object.freeze(context);\n}\n\n/**\n * The request context for the call currently in flight (#4981).\n *\n * Exists because a tool call passes through two middleware implementations\n * that each used to mint their own `RequestContext`, producing two unlinked\n * `req_*` ids and two `Tool invocation started` pairs per call. Argument\n * threading cannot fix that: `createSecureHandler` returns a 1-arity function,\n * so the chain's arity dispatch drops `ctx`, and 1-arity intermediaries such\n * as the `withPrerequisite` wrappers sit between the layers on some tools.\n * An ambient channel survives both.\n *\n * This mirrors the AbortSignal and progress-context storages the chain already\n * uses, so it is the established idiom here rather than a new mechanism.\n */\nconst requestContextStorage = new AsyncLocalStorage<ContextHolder>();\n\n/**\n * The ambient context plus its liveness.\n *\n * AsyncLocalStorage keeps a store reachable from anything spawned inside the\n * scope, including work that OUTLIVES the call — a `setTimeout`, or the\n * detached body `runAsJob` fires from inside the secure handler. Without this\n * flag such work still reads the context of a request that already completed,\n * and a nested handler for the same tool would adopt a finished identity, then\n * log neither a start (suppressed) nor a completion (the chain already closed\n * its own pair).\n *\n * Identity is not liveness, so the adoption gate needs both.\n */\ninterface ContextHolder {\n readonly context: RequestContext;\n settled: boolean;\n}\n\n/**\n * Runs `fn` with `context` as the ambient request context, so any nested layer\n * can adopt it instead of minting a second one.\n */\nexport function runWithRequestContext<T>(\n context: RequestContext,\n fn: () => Promise<T>\n): Promise<T> {\n const holder: ContextHolder = { context, settled: false };\n return requestContextStorage.run(holder, () =>\n fn().finally(() => {\n holder.settled = true;\n })\n );\n}\n\n/**\n * The ambient request context, or `undefined` when nothing established one.\n *\n * `undefined` is a real answer, not a failure: a caller that composes a secure\n * handler without the middleware chain — every direct test of it does — has no\n * outer context to inherit, and must mint its own.\n */\nexport function getCurrentRequestContext(): RequestContext | undefined {\n const holder = requestContextStorage.getStore();\n if (holder === undefined || holder.settled) return undefined;\n return holder.context;\n}\n\n/**\n * Formats request context for logging.\n * Extracts essential fields for log context.\n *\n * @param ctx - Request context\n * @returns Log-friendly context object\n */\nexport function contextForLogging(ctx: RequestContext): Record<string, unknown> {\n return {\n requestId: ctx.requestId,\n toolName: ctx.toolName,\n trustTier: ctx.trustTier,\n ...(ctx.caller.clientId !== undefined && { clientId: ctx.caller.clientId }),\n ...(ctx.traceId !== undefined && { traceId: ctx.traceId }),\n };\n}\n\n/**\n * Type guard to check if a value is a valid RequestContext.\n */\nexport function isRequestContext(value: unknown): value is RequestContext {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n return (\n typeof obj['requestId'] === 'string' &&\n obj['requestId'].startsWith('req_') &&\n typeof obj['timestamp'] === 'string' &&\n typeof obj['toolName'] === 'string' &&\n typeof obj['caller'] === 'object' &&\n typeof obj['trustTier'] === 'string' &&\n ['1', '2', '3', '4'].includes(obj['trustTier'])\n );\n}\n\n/**\n * The request's trust tier ONLY when it was actually derived from caller\n * information (#4733).\n *\n * `createRequestContext` falls back to `caller = {}`, and `deriveTrustTier({})`\n * returns `'3'` — so an absent caller is indistinguishable from a genuinely\n * untrusted one at the reading end. Since #6795 the middleware chain supplies\n * the transport the server connected ({@link serverCallerInfo}), so a stdio\n * server measures tier 1; a context built with no recorded transport and no\n * authentication fact is still that fallback.\n *\n * Returning `undefined` for the fallback lets a consumer record `unmeasured`\n * rather than a constant that reads as a measurement.\n *\n * NOTE this is caller AUTHENTICATION (transport / authenticated / clientId),\n * not content provenance. A trusted client can submit hostile content. Consumers\n * wanting provenance need a different signal — `classifyTrust` is the closest,\n * but it requires a GitHub actor and does not apply to a bare goal string.\n */\nexport function measuredTrustTier(context: RequestContext): string | undefined {\n // `caller` is always set by `createRequestContext` (to `{}` at minimum), but\n // a partially-constructed context can omit it — and an absent caller is the\n // same condition as an empty one: nothing was measured.\n const caller: unknown = context.caller;\n if (typeof caller !== 'object' || caller === null) return undefined;\n\n // Gate on the fields `deriveTrustTier` actually reads, not on \"the object has\n // any key at all\" (#4738 review). A caller carrying only `sessionId` or\n // `userAgent` feeds nothing into the derivation, so a non-empty check would\n // call the '3' fallback a measurement — reintroducing the constant this\n // function exists to prevent, in the function that prevents it.\n // `clientId` is NOT sufficient either: `deriveTrustTier` reads `clientId`\n // only INSIDE the `authenticated === true` branch, so\n // `{ clientId: 'claude-cli' }` returns the same '3' fallback as `{}`. (The\n // removed env-sniffing `extractCallerInfo` produced exactly that shape.)\n const info = caller as Partial<CallerInfo>;\n // `authenticated: false` counts: it is a measured fact (\"we checked, they\n // are not authenticated\"), which is different from the field being absent.\n const derivable = info.transport !== undefined || info.authenticated !== undefined;\n\n return derivable ? context.trustTier : undefined;\n}\n"],"mappings":"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","names":[]}
@@ -19,10 +19,10 @@ import {
19
19
  toolSuccessStructured,
20
20
  withProgressHeartbeat,
21
21
  wrapToolWithTimeout
22
- } from "./chunk-L6OAYUYG.js";
22
+ } from "./chunk-MZCKUIQT.js";
23
23
  import {
24
24
  VERSION
25
- } from "./chunk-HBABJ3TY.js";
25
+ } from "./chunk-BZCNC3A5.js";
26
26
  import {
27
27
  allOf
28
28
  } from "./chunk-EZNIYU5T.js";
@@ -9416,4 +9416,4 @@ export {
9416
9416
  CONSENSUS_VOTE_TOOL_SCHEMA,
9417
9417
  registerConsensusVoteTool
9418
9418
  };
9419
- //# sourceMappingURL=chunk-M2PQUYVA.js.map
9419
+ //# sourceMappingURL=chunk-L3ORUJO3.js.map
@@ -5,7 +5,7 @@ import {
5
5
  getCurrentRequestContext,
6
6
  runWithRequestContext,
7
7
  serverCallerInfo
8
- } from "./chunk-HBABJ3TY.js";
8
+ } from "./chunk-BZCNC3A5.js";
9
9
  import {
10
10
  DEFAULT_EXECUTION_MODE
11
11
  } from "./chunk-ECJ52NSK.js";
@@ -2335,4 +2335,4 @@ export {
2335
2335
  DEFAULT_CONSENSUS_CONFIG,
2336
2336
  ConsensusMetricsSchema
2337
2337
  };
2338
- //# sourceMappingURL=chunk-L6OAYUYG.js.map
2338
+ //# sourceMappingURL=chunk-MZCKUIQT.js.map