export const GENAI = { OPERATION_NAME: 'gen_ai.operation.name', SYSTEM: 'gen_ai.system', REQUEST_MODEL: 'gen_ai.request.model', REQUEST_TEMPERATURE: 'gen_ai.request.temperature', REQUEST_MAX_TOKENS: 'gen_ai.request.max_tokens', RESPONSE_MODEL: 'gen_ai.response.model', RESPONSE_FINISH_REASONS: 'gen_ai.response.finish_reasons', RESPONSE_ID: 'gen_ai.response.id', USAGE_INPUT_TOKENS: 'gen_ai.usage.input_tokens', USAGE_OUTPUT_TOKENS: 'gen_ai.usage.output_tokens', /** * Which kind of token a usage measurement counts. The convention keys * one metric by this rather than minting a metric name per kind, so a * dashboard that sums the metric gets the whole picture instead of one * slice of it. */ TOKEN_TYPE: 'gen_ai.token.type', TOOL_NAME: 'gen_ai.tool.name', TOOL_TYPE: 'gen_ai.tool.type', /** * The id of the tool call this span is about. * * Spelled `call.id`, matching the registry and matching the two * neighbours above — this read `gen_ai.tool.call_id`, one underscore * where the convention has a dot, so a consumer grouping by the * conventional name found nothing under it. Nothing errored, because a * span attribute is a free-form key and a wrong one is simply a key * nobody asked for. * * Pinned by `telemetry/__tests__/tool-call-id-attribute.test.ts`, which * also drives the emitter: the spelling was only half the defect, and * the constant had no writer at all. */ TOOL_CALL_ID: 'gen_ai.tool.call.id', AGENT_NAME: 'gen_ai.agent.name', AGENT_ID: 'gen_ai.agent.id', /** * The conversation a span belongs to. In namzu that is the session: the * value is the `sessionId`. */ CONVERSATION_ID: 'gen_ai.conversation.id', } as const export const NAMZU = { TURN_ID: 'namzu.turn.id', TURN_STATUS: 'namzu.turn.status', /** Set on a child session's spans: the parent session that delegated it. */ SESSION_PARENT_ID: 'namzu.session.parent_id', ITERATION: 'namzu.iteration', TOOL_SUCCESS: 'namzu.tool.success', TOOL_ERROR: 'namzu.tool.error', COST_TOTAL: 'namzu.cost.total', CACHE_READ_TOKENS: 'namzu.cache.read_tokens', CACHE_WRITE_TOKENS: 'namzu.cache.write_tokens', CACHE_DISCOUNT: 'namzu.cache.discount', // Correlation attributes for a turn's own logger — see // TurnContextFactory.buildLogger (runtime/query/context.ts). TURN_ID above // covers the turn itself; these are the layers above it // (Tenant -> Project -> Topic -> Session -> Turn, Convention #17). SESSION_ID: 'namzu.session.id', THREAD_ID: 'namzu.thread.id', PROJECT_ID: 'namzu.project.id', TENANT_ID: 'namzu.tenant.id', // AbstractAgent's per-instance identity (agents/AbstractAgent.ts). The id // half reuses GENAI.AGENT_ID — an agent's id is the same concept whether // it is read off a span or a log record, and GENAI.AGENT_ID already // exists and is already bound at the turn's own correlated logger and // root span. TYPE has no GENAI analogue, so it is new. AGENT_TYPE: 'namzu.agent.type', // ManagedRegistry's per-subclass distinction (registry/ManagedRegistry.ts). // scope.name is fixed to 'registry' for every ManagedRegistry subclass — // they all log through this one file — so the per-instance name that // `component: config.componentName` used to carry moves here instead. // `ManagedRegistryConfig.componentName`'s FIELD NAME is unchanged. REGISTRY_NAME: 'namzu.registry.name', // InMemoryCredentialVault (vault/InMemoryCredentialVault.ts) — closes a // PRE-EXISTING violation of "no attribute string literal at a call // site": both keys already existed as raw string literals there before // this change. Values are unchanged. CREDENTIAL_ID: 'namzu.credential.id', CREDENTIAL_LABEL: 'namzu.credential.label', // MCPClient (connector/mcp/client.ts). SERVER_ID is the OPERATOR's own // configured identifier for the connector instance being dialed; // SERVER_NAME is the REMOTE server's own self-reported name from its // initialize response — kept separate on purpose, since the remote's // self-report is attacker-influenced data (see // connector/mcp/__tests__/a-server-cannot-forge-a-second-log-line.test.ts). // SERVER_NAME closes the same kind of pre-existing literal violation as // the two CREDENTIAL_* keys above. SERVER_ID: 'namzu.connector.server.id', SERVER_NAME: 'namzu.connector.server.name', // The MCP server this process HOSTS (connector/mcp/server/server.ts), not // one it dials. Deliberately not SERVER_ID above: that key is the client // side's identifier for a remote, and a record carrying both concepts // under one name is the collision the namespace rule exists to stop. The // value is an `MCPServerId` minted here, so unlike SERVER_NAME it is not // attacker-influenced. MCP_SERVER_ID: 'namzu.mcp.server.id', // The concrete subclass behind a base class that logs on its children's // behalf — the same shape as REGISTRY_NAME above, and for the same // reason: `scope.name` is the MODULE (`connector/base`, `execution/base`) // because that is the file the record is emitted from, so the subclass // identity every one of those records used to carry as `component: // this.constructor.name` needs somewhere to go that is not the scope. CONNECTOR_TYPE: 'namzu.connector.type', EXECUTION_TYPE: 'namzu.execution.type', // LocalSandbox (sandbox/provider/local.ts). The file hosts two logging // classes — the sandbox and its provider — and both scope to // `sandbox/provider/local`, so this id is what separates one sandbox's // records from the provider's and from each other's. SANDBOX_ID: 'namzu.sandbox.id', } as const /** * The boot narrative's closed event-name vocabulary — every `eventName` the * SDK itself emits between process start and `namzu.boot.ready`. A call * site spells `BOOT_EVENT_NAMES.X`, not the literal string, so a typo * becomes a missing property at compile time rather than a name that * silently never matches a host's filter. * * `BootEventName` is DERIVED from this record rather than declared beside * it, so the two cannot drift — there is only one place a member is added. * `LogRecord.eventName` itself stays `string`, not this union: widening it * to a fixed union would make the field unusable for a future emitter * naming events from a different vocabulary. */ export const BOOT_EVENT_NAMES = { BOOT_START: 'namzu.boot.start', CONFIG_RESOLVED: 'namzu.config.resolved', SANDBOX_RESOLVED: 'namzu.sandbox.resolved', PROVIDER_RESOLVED: 'namzu.provider.resolved', CAPABILITY_DETECTED: 'namzu.capability.detected', CAPABILITY_BROKEN: 'namzu.capability.broken', TELEMETRY_STATUS: 'namzu.telemetry.status', DISCOVERY_COMPLETED: 'namzu.discovery.completed', BOOT_REFUSED: 'namzu.boot.refused', BOOT_READY: 'namzu.boot.ready', } as const export type BootEventName = (typeof BOOT_EVENT_NAMES)[keyof typeof BOOT_EVENT_NAMES]