[{"data":1,"prerenderedAt":2201},["ShallowReactive",2],{"navigation_docs":3,"-docs-architecture-telemetry-ingest":158,"-docs-architecture-telemetry-ingest-surround":2198},[4,15,74,98,113],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":6},"Getting Started",false,"\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started",[10],{"title":11,"path":12,"stem":13,"icon":14},"Zeeq Introduction","\u002Fdocs\u002Fgetting-started\u002Fintroduction","docs\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"children":19,"page":6},"Key Features","\u002Fdocs\u002Fkey-features","docs\u002F3.key-features",[20,25,30,35,40,45,50,55,59,64,69],{"title":21,"path":22,"stem":23,"icon":24},"Membership Invitations","\u002Fdocs\u002Fkey-features\u002Fmembership-invitations","docs\u002F3.key-features\u002F1.membership-invitations","i-hugeicons-cube",{"title":26,"path":27,"stem":28,"icon":29},"Command Palette","\u002Fdocs\u002Fkey-features\u002Fcommand-palette","docs\u002F3.key-features\u002F10.command-palette","i-hugeicons-search-01",{"title":31,"path":32,"stem":33,"icon":34},"Library Import and Export","\u002Fdocs\u002Fkey-features\u002Flibrary-import-export","docs\u002F3.key-features\u002F11.library-import-export","i-lucide-package-open",{"title":36,"path":37,"stem":38,"icon":39},"LLM Settings Configuration","\u002Fdocs\u002Fkey-features\u002Fllm-settings-configuration","docs\u002F3.key-features\u002F2.llm-settings-configuration","i-hugeicons-ai-magic",{"title":41,"path":42,"stem":43,"icon":44},"Agent Code Review Findings","\u002Fdocs\u002Fkey-features\u002Fagent-code-review-findings","docs\u002F3.key-features\u002F3.agent-code-review-findings","i-lucide-list-checks",{"title":46,"path":47,"stem":48,"icon":49},"Credentials & API Tokens","\u002Fdocs\u002Fkey-features\u002Fcredentials","docs\u002F3.key-features\u002F4.credentials","i-hugeicons-key-01",{"title":51,"path":52,"stem":53,"icon":54},"Manual Document Libraries","\u002Fdocs\u002Fkey-features\u002Fmanual-document-libraries","docs\u002F3.key-features\u002F5.manual-document-libraries","i-lucide-library-big",{"title":56,"path":57,"stem":58,"icon":29},"Snippet Search","\u002Fdocs\u002Fkey-features\u002Fsnippet-search","docs\u002F3.key-features\u002F6.snippet-search",{"title":60,"path":61,"stem":62,"icon":63},"Shared Prompt Fragment","\u002Fdocs\u002Fkey-features\u002Fshared-prompt-fragment","docs\u002F3.key-features\u002F7.shared-prompt-fragment","i-lucide-notebook-pen",{"title":65,"path":66,"stem":67,"icon":68},"Code Review Sources & Telemetry","\u002Fdocs\u002Fkey-features\u002Fcode-review-sources","docs\u002F3.key-features\u002F8.code-review-sources","i-hugeicons-book-01",{"title":70,"path":71,"stem":72,"icon":73},"Excluding Documents from Code Reviews","\u002Fdocs\u002Fkey-features\u002Fcode-review-document-exclusion","docs\u002F3.key-features\u002F9.code-review-document-exclusion","i-lucide-eye-off",{"title":75,"path":76,"stem":77,"children":78,"page":6},"Runtime Configuration","\u002Fdocs\u002Fconfiguration","docs\u002F5.configuration",[79,84,89,93],{"title":80,"path":81,"stem":82,"icon":83},"Google Cloud Configuration","\u002Fdocs\u002Fconfiguration\u002Fgcp-runtime","docs\u002F5.configuration\u002F1.gcp-runtime","i-hugeicons-google",{"title":85,"path":86,"stem":87,"icon":88},"GitHub Configuration","\u002Fdocs\u002Fconfiguration\u002Fgithub-configuration","docs\u002F5.configuration\u002F2.github-configuration","i-hugeicons-github",{"title":90,"path":91,"stem":92},"Local OpenIddict Certificates","\u002Fdocs\u002Fconfiguration\u002Flocal-openiddict-certificates","docs\u002F5.configuration\u002F3.local-openiddict-certificates",{"title":94,"path":95,"stem":96,"icon":97},"System Admin Access","\u002Fdocs\u002Fconfiguration\u002Fsystem-admin-access","docs\u002F5.configuration\u002F4.system-admin-access","i-hugeicons-shield-01",{"title":99,"path":100,"stem":101,"children":102,"page":6},"Metrics","\u002Fdocs\u002Fmetrics","docs\u002F6.metrics",[103,108],{"title":104,"path":105,"stem":106,"icon":107},"Metrics Dashboard","\u002Fdocs\u002Fmetrics\u002Fmetrics-dashboard","docs\u002F6.metrics\u002F1.metrics-dashboard","i-hugeicons-analytics-01",{"title":109,"path":110,"stem":111,"icon":112},"Metrics Pipeline","\u002Fdocs\u002Fmetrics\u002Fmetrics-pipeline","docs\u002F6.metrics\u002F2.metrics-pipeline","i-hugeicons-database-02",{"title":114,"path":115,"stem":116,"children":117,"page":6},"Design and Architecture","\u002Fdocs\u002Farchitecture","docs\u002F9.architecture",[118,123,128,133,137,141,145,150,154],{"title":119,"path":120,"stem":121,"icon":122},"Caching","\u002Fdocs\u002Farchitecture\u002Fcaching","docs\u002F9.architecture\u002F1.caching","i-lucide-database",{"title":124,"path":125,"stem":126,"icon":127},"Messaging and Queueing","\u002Fdocs\u002Farchitecture\u002Fmessaging-and-queueing","docs\u002F9.architecture\u002F2.messaging-and-queueing","i-lucide-message-square",{"title":129,"path":130,"stem":131,"icon":132},"GitHub Comment Rendering","\u002Fdocs\u002Farchitecture\u002Fgithub-comment-rendering","docs\u002F9.architecture\u002F3.github-comment-rendering","i-lucide-github",{"title":134,"path":135,"stem":136},"GitHub Integration","\u002Fdocs\u002Farchitecture\u002Fgithub-integration","docs\u002F9.architecture\u002F4.github-integration",{"title":138,"path":139,"stem":140},"LLM Settings and Secret Storage","\u002Fdocs\u002Farchitecture\u002Fllm-settings-and-secret-storage","docs\u002F9.architecture\u002F5.llm-settings-and-secret-storage",{"title":142,"path":143,"stem":144},"Organization Activation and Routes","\u002Fdocs\u002Farchitecture\u002Forganization-activation-and-routes","docs\u002F9.architecture\u002F6.organization-activation-and-routes",{"title":146,"path":147,"stem":148,"icon":149},"MCP Uploaded-Diff Code Reviews","\u002Fdocs\u002Farchitecture\u002Fmcp-uploaded-diff-code-reviews","docs\u002F9.architecture\u002F7.mcp-uploaded-diff-code-reviews","i-lucide-file-search",{"title":151,"path":152,"stem":153},"Repository Content Ingest","\u002Fdocs\u002Farchitecture\u002Frepository-content-ingest","docs\u002F9.architecture\u002F8.repository-content-ingest",{"title":155,"path":156,"stem":157},"Telemetry Ingest","\u002Fdocs\u002Farchitecture\u002Ftelemetry-ingest","docs\u002F9.architecture\u002F9.telemetry-ingest",{"id":159,"title":155,"body":160,"description":2191,"extension":2192,"links":2193,"meta":2194,"navigation":255,"path":156,"seo":2196,"stem":157,"__hash__":2197},"docs\u002Fdocs\u002F9.architecture\u002F9.telemetry-ingest.md",{"type":161,"value":162,"toc":2177},"minimark",[163,168,172,188,192,195,198,202,206,211,214,219,582,585,589,592,603,1548,1552,1555,1570,1574,1580,1647,1651,1672,1676,1691,1865,1890,1916,1920,1928,1969,1998,2025,2029,2035,2114,2117,2142,2148,2173],[164,165,167],"h2",{"id":166},"summary","Summary",[169,170,171],"p",{},"Zeeq ingests and processes OpenTelemetry (OTEL) data from agent harnesses which carry information about:",[173,174,175,179,182,185],"ol",{},[176,177,178],"li",{},"Token usage",[176,180,181],{},"Tool usage",[176,183,184],{},"Prompts",[176,186,187],{},"Pricing (Claude)",[164,189,191],{"id":190},"flow","Flow",[169,193,194],{},"Telemetry volume can easily overwhelm the system, so Zeeq uses a multi-stage ingest pipeline to process telemetry data.",[169,196,197],{},"While Zeeq can directly ingest OTEL logs and spans, it is recommended to use the open source OTEL collector to pre-process and filter high volume telemetry data streams to retain only the relevant logs and spans for Zeeq.",[199,200,201],"tip",{},"It can be used suitably as a standalone collector in low-volume scenarios",[164,203,205],{"id":204},"otel-collector-configuration","OTEL Collector Configuration",[207,208,210],"h3",{"id":209},"local-development","Local Development",[169,212,213],{},"For local development, the OTEL collector should be configured to send telemetry data to both Zeeq and Aspire as this will allow troubleshooting of the telemetry flow with better visibility since agents can directly dump the telemetry from Aspire.",[215,216,218],"h4",{"id":217},"aspire-only-for-full-telemetry-flow","Aspire Only (For Full Telemetry Flow)",[220,221,226],"pre",{"className":222,"code":223,"language":224,"meta":225,"style":225},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Configuration to use for an OTEL collector forwarding to Aspire\n# This is a basic setup that allows sending traces and logs to Aspire which makes\n# it easier to understand the real shape of the telemetry flow.\n\nreceivers:\n  otlp:\n    protocols:\n      grpc:\n        endpoint: 0.0.0.0:4317\n      http:\n        endpoint: 0.0.0.0:4318\n\nexporters:\n  otlp\u002Faspire:\n    endpoint: \"${env:OTEL_EXPORTER_OTLP_ENDPOINT}\"\n    headers:\n      x-otlp-api-key: \"${env:ASPIRE_DASHBOARD_OTLP_API_KEY}\"\n    tls:\n      insecure: true\n\nprocessors:\n  batch:\n    send_batch_max_size: 100\n    send_batch_size: 10\n    timeout: 10s\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [otlp\u002Faspire]\n    logs:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [otlp\u002Faspire]\n","yaml","",[227,228,229,238,244,250,257,268,276,284,292,305,313,323,328,336,344,361,369,384,392,404,409,417,425,437,448,459,464,472,480,488,505,520,535,543,556,569],"code",{"__ignoreMap":225},[230,231,234],"span",{"class":232,"line":233},"line",1,[230,235,237],{"class":236},"sHwdD","# Configuration to use for an OTEL collector forwarding to Aspire\n",[230,239,241],{"class":232,"line":240},2,[230,242,243],{"class":236},"# This is a basic setup that allows sending traces and logs to Aspire which makes\n",[230,245,247],{"class":232,"line":246},3,[230,248,249],{"class":236},"# it easier to understand the real shape of the telemetry flow.\n",[230,251,253],{"class":232,"line":252},4,[230,254,256],{"emptyLinePlaceholder":255},true,"\n",[230,258,260,264],{"class":232,"line":259},5,[230,261,263],{"class":262},"swJcz","receivers",[230,265,267],{"class":266},"sMK4o",":\n",[230,269,271,274],{"class":232,"line":270},6,[230,272,273],{"class":262},"  otlp",[230,275,267],{"class":266},[230,277,279,282],{"class":232,"line":278},7,[230,280,281],{"class":262},"    protocols",[230,283,267],{"class":266},[230,285,287,290],{"class":232,"line":286},8,[230,288,289],{"class":262},"      grpc",[230,291,267],{"class":266},[230,293,295,298,301],{"class":232,"line":294},9,[230,296,297],{"class":262},"        endpoint",[230,299,300],{"class":266},":",[230,302,304],{"class":303},"sfazB"," 0.0.0.0:4317\n",[230,306,308,311],{"class":232,"line":307},10,[230,309,310],{"class":262},"      http",[230,312,267],{"class":266},[230,314,316,318,320],{"class":232,"line":315},11,[230,317,297],{"class":262},[230,319,300],{"class":266},[230,321,322],{"class":303}," 0.0.0.0:4318\n",[230,324,326],{"class":232,"line":325},12,[230,327,256],{"emptyLinePlaceholder":255},[230,329,331,334],{"class":232,"line":330},13,[230,332,333],{"class":262},"exporters",[230,335,267],{"class":266},[230,337,339,342],{"class":232,"line":338},14,[230,340,341],{"class":262},"  otlp\u002Faspire",[230,343,267],{"class":266},[230,345,347,350,352,355,358],{"class":232,"line":346},15,[230,348,349],{"class":262},"    endpoint",[230,351,300],{"class":266},[230,353,354],{"class":266}," \"",[230,356,357],{"class":303},"${env:OTEL_EXPORTER_OTLP_ENDPOINT}",[230,359,360],{"class":266},"\"\n",[230,362,364,367],{"class":232,"line":363},16,[230,365,366],{"class":262},"    headers",[230,368,267],{"class":266},[230,370,372,375,377,379,382],{"class":232,"line":371},17,[230,373,374],{"class":262},"      x-otlp-api-key",[230,376,300],{"class":266},[230,378,354],{"class":266},[230,380,381],{"class":303},"${env:ASPIRE_DASHBOARD_OTLP_API_KEY}",[230,383,360],{"class":266},[230,385,387,390],{"class":232,"line":386},18,[230,388,389],{"class":262},"    tls",[230,391,267],{"class":266},[230,393,395,398,400],{"class":232,"line":394},19,[230,396,397],{"class":262},"      insecure",[230,399,300],{"class":266},[230,401,403],{"class":402},"sfNiH"," true\n",[230,405,407],{"class":232,"line":406},20,[230,408,256],{"emptyLinePlaceholder":255},[230,410,412,415],{"class":232,"line":411},21,[230,413,414],{"class":262},"processors",[230,416,267],{"class":266},[230,418,420,423],{"class":232,"line":419},22,[230,421,422],{"class":262},"  batch",[230,424,267],{"class":266},[230,426,428,431,433],{"class":232,"line":427},23,[230,429,430],{"class":262},"    send_batch_max_size",[230,432,300],{"class":266},[230,434,436],{"class":435},"sbssI"," 100\n",[230,438,440,443,445],{"class":232,"line":439},24,[230,441,442],{"class":262},"    send_batch_size",[230,444,300],{"class":266},[230,446,447],{"class":435}," 10\n",[230,449,451,454,456],{"class":232,"line":450},25,[230,452,453],{"class":262},"    timeout",[230,455,300],{"class":266},[230,457,458],{"class":303}," 10s\n",[230,460,462],{"class":232,"line":461},26,[230,463,256],{"emptyLinePlaceholder":255},[230,465,467,470],{"class":232,"line":466},27,[230,468,469],{"class":262},"service",[230,471,267],{"class":266},[230,473,475,478],{"class":232,"line":474},28,[230,476,477],{"class":262},"  pipelines",[230,479,267],{"class":266},[230,481,483,486],{"class":232,"line":482},29,[230,484,485],{"class":262},"    traces",[230,487,267],{"class":266},[230,489,491,494,496,499,502],{"class":232,"line":490},30,[230,492,493],{"class":262},"      receivers",[230,495,300],{"class":266},[230,497,498],{"class":266}," [",[230,500,501],{"class":303},"otlp",[230,503,504],{"class":266},"]\n",[230,506,508,511,513,515,518],{"class":232,"line":507},31,[230,509,510],{"class":262},"      processors",[230,512,300],{"class":266},[230,514,498],{"class":266},[230,516,517],{"class":303},"batch",[230,519,504],{"class":266},[230,521,523,526,528,530,533],{"class":232,"line":522},32,[230,524,525],{"class":262},"      exporters",[230,527,300],{"class":266},[230,529,498],{"class":266},[230,531,532],{"class":303},"otlp\u002Faspire",[230,534,504],{"class":266},[230,536,538,541],{"class":232,"line":537},33,[230,539,540],{"class":262},"    logs",[230,542,267],{"class":266},[230,544,546,548,550,552,554],{"class":232,"line":545},34,[230,547,493],{"class":262},[230,549,300],{"class":266},[230,551,498],{"class":266},[230,553,501],{"class":303},[230,555,504],{"class":266},[230,557,559,561,563,565,567],{"class":232,"line":558},35,[230,560,510],{"class":262},[230,562,300],{"class":266},[230,564,498],{"class":266},[230,566,517],{"class":303},[230,568,504],{"class":266},[230,570,572,574,576,578,580],{"class":232,"line":571},36,[230,573,525],{"class":262},[230,575,300],{"class":266},[230,577,498],{"class":266},[230,579,532],{"class":303},[230,581,504],{"class":266},[199,583,584],{},"Use this setup when testing canonical behavior since it can show all messages or add a filter to verify filter behavior.",[215,586,588],{"id":587},"aspire-and-zeeq-for-simulation-of-upstream-filtering","Aspire and Zeeq (For Simulation of Upstream Filtering)",[169,590,591],{},"The collector can be configured to forward to multiple endpoints simultaneously which allows sending to Aspire as well as Zeeq. This is useful for simulating the upstream filtering that would occur in production.",[169,593,594,595,598,599,602],{},"This is the actual config used locally (",[227,596,597],{},"host\u002Fotel-collector-config.filtered.yaml","), bind-mounted into the ",[227,600,601],{},"otel-collector"," Aspire resource:",[220,604,606],{"className":222,"code":605,"language":224,"meta":225,"style":225},"# Configured to forward to multiple targets with JWT auth and header forwarding\n\nextensions:\n  # Validates the caller's Bearer JWT against Zeeq's OpenIddict issuer via\n  # standard OIDC discovery (\u002F.well-known\u002Fopenid-configuration + JWKS).\n  # This is a cheap outer gate that keeps unauthenticated noise off Zeeq.\n  oidc:\n    issuer_url: \"${env:ZEEQ_ISSUER_URL}\"              # e.g. https:\u002F\u002Fapp.zeeq.ai\u002Fauth\n    # No dedicated telemetry OpenIddict resource\u002Fscope exists yet, so this\n    # points at the same resource already registered for MCP user tokens\n    # (AppSettings:Auth:Resource) - an existing Zeeq user token satisfies\n    # this check without new server-side provisioning.\n    audience: \"${env:ZEEQ_TELEMETRY_AUDIENCE}\"\n    username_claim: sub\n\n  # Forwards the original caller's Authorization header from the inbound request\n  # onto the exporter request, so Zeeq sees the end user's token, not a\n  # collector service identity. Requires include_metadata: true on the receiver\n  # and metadata_keys on the batch processor (see below).\n  headers_setter:\n    headers:\n      - action: upsert\n        key: authorization\n        from_context: authorization\n\nreceivers:\n  otlp:\n    protocols:\n      grpc:\n        endpoint: 0.0.0.0:4317\n        auth:\n          authenticator: oidc\n        include_metadata: true                           # required for from_context in headers_setter\n      http:\n        endpoint: 0.0.0.0:4318\n        auth:\n          authenticator: oidc\n        include_metadata: true\n\nexporters:\n  # Forward to Zeeq with per-user JWT pass-through via headers_setter.\n  # The Authorization header from the inbound request is forwarded; if no\n  # user token is present, the collector can fall back to a service-level\n  # client_credentials identity via additional_auth on headers_setter.\n  # otlphttp (not gRPC): Zeeq's Kestrel endpoint accepts both HTTP\u002F1.1 and\n  # HTTP\u002F2 locally, so a plain HTTP exporter\u002Freceiver pair is simplest.\n  # otlphttp auto-appends \u002Fv1\u002Flogs and \u002Fv1\u002Ftraces to this base endpoint.\n  otlphttp\u002Fzeeq:\n    endpoint: \"${env:ZEEQ_OTLP_HTTP_ENDPOINT}\"\n    auth:\n      authenticator: headers_setter\n    sending_queue:\n      enabled: true\n    tls:\n      insecure: true                                     # Local\n\n  # Fan-out to Aspire dashboard for full-telemetry visibility.\n  otlp\u002Faspire:\n    endpoint: \"${env:OTEL_EXPORTER_OTLP_ENDPOINT}\"\n    headers:\n      x-otlp-api-key: \"${env:ASPIRE_DASHBOARD_OTLP_API_KEY}\"\n    tls:\n      insecure: true\n\nprocessors:\n  # ---- LOGS: keep only Claude Code + Codex session events (drop-if-true) ----\n  filter\u002Fagent_session_logs:\n    error_mode: ignore\n    logs:\n      log_record:\n        - |\n          not (\n            resource.attributes[\"service.name\"] == \"claude-code\" and\n            (log.body == \"claude_code.user_prompt\" or\n             log.body == \"claude_code.tool_result\" or\n             log.body == \"claude_code.api_request\" or\n             log.body == \"claude_code.tool_decision\")\n          ) and not (\n            IsMatch(resource.attributes[\"service.name\"], \".*codex.*\") and\n            (log.attributes[\"prompt_length\"] != nil or\n             (log.attributes[\"tool_name\"] != nil and log.attributes[\"call_id\"] != nil) or\n             log.attributes[\"event.kind\"] == \"response.completed\" or\n             (log.attributes[\"mcp_servers\"] != nil and log.attributes[\"approval_policy\"] != nil))\n          )\n\n  # ---- TRACES: keep only Copilot Chat business spans (drop-if-true) ----\n  filter\u002Fagent_session_traces:\n    error_mode: ignore\n    traces:\n      span:\n        - |\n          not (\n            resource.attributes[\"service.name\"] == \"copilot-chat\" and\n            span.attributes[\"copilot_chat.chat_session_id\"] != nil and\n            (span.attributes[\"gen_ai.operation.name\"] == \"invoke_agent\" or\n             span.attributes[\"gen_ai.operation.name\"] == \"chat\" or\n             span.attributes[\"gen_ai.operation.name\"] == \"execute_tool\")\n          )\n\n  batch:\n    send_batch_size: 128\n    send_batch_max_size: 512\n    timeout: 5s\n    # One batcher per distinct caller token: preserves per-user Authorization\n    # forwarding correctness. Without this, records from two users could land\n    # in one exported batch carrying a single Authorization header.\n    metadata_keys: [authorization]\n    metadata_cardinality_limit: 1000\n\nservice:\n  extensions: [oidc, headers_setter]\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [filter\u002Fagent_session_traces, batch]\n      exporters: [otlp\u002Faspire, otlphttp\u002Fzeeq]\n    logs:\n      receivers: [otlp]\n      processors: [filter\u002Fagent_session_logs, batch]\n      exporters: [otlp\u002Faspire, otlphttp\u002Fzeeq]\n",[227,607,608,613,617,624,629,634,639,646,664,669,674,679,684,698,708,712,717,722,727,732,739,745,758,768,777,781,787,793,799,805,813,820,830,843,849,857,863,872,881,886,893,899,905,911,917,923,929,935,943,957,965,976,984,994,1001,1013,1018,1024,1031,1044,1051,1064,1071,1080,1085,1092,1098,1106,1117,1124,1132,1142,1148,1154,1160,1166,1172,1178,1184,1190,1196,1202,1208,1214,1220,1225,1231,1239,1248,1255,1263,1270,1275,1281,1287,1293,1299,1305,1310,1315,1322,1332,1342,1352,1358,1364,1370,1385,1396,1401,1408,1429,1436,1443,1456,1475,1493,1500,1513,1531],{"__ignoreMap":225},[230,609,610],{"class":232,"line":233},[230,611,612],{"class":236},"# Configured to forward to multiple targets with JWT auth and header forwarding\n",[230,614,615],{"class":232,"line":240},[230,616,256],{"emptyLinePlaceholder":255},[230,618,619,622],{"class":232,"line":246},[230,620,621],{"class":262},"extensions",[230,623,267],{"class":266},[230,625,626],{"class":232,"line":252},[230,627,628],{"class":236},"  # Validates the caller's Bearer JWT against Zeeq's OpenIddict issuer via\n",[230,630,631],{"class":232,"line":259},[230,632,633],{"class":236},"  # standard OIDC discovery (\u002F.well-known\u002Fopenid-configuration + JWKS).\n",[230,635,636],{"class":232,"line":270},[230,637,638],{"class":236},"  # This is a cheap outer gate that keeps unauthenticated noise off Zeeq.\n",[230,640,641,644],{"class":232,"line":278},[230,642,643],{"class":262},"  oidc",[230,645,267],{"class":266},[230,647,648,651,653,655,658,661],{"class":232,"line":286},[230,649,650],{"class":262},"    issuer_url",[230,652,300],{"class":266},[230,654,354],{"class":266},[230,656,657],{"class":303},"${env:ZEEQ_ISSUER_URL}",[230,659,660],{"class":266},"\"",[230,662,663],{"class":236},"              # e.g. https:\u002F\u002Fapp.zeeq.ai\u002Fauth\n",[230,665,666],{"class":232,"line":294},[230,667,668],{"class":236},"    # No dedicated telemetry OpenIddict resource\u002Fscope exists yet, so this\n",[230,670,671],{"class":232,"line":307},[230,672,673],{"class":236},"    # points at the same resource already registered for MCP user tokens\n",[230,675,676],{"class":232,"line":315},[230,677,678],{"class":236},"    # (AppSettings:Auth:Resource) - an existing Zeeq user token satisfies\n",[230,680,681],{"class":232,"line":325},[230,682,683],{"class":236},"    # this check without new server-side provisioning.\n",[230,685,686,689,691,693,696],{"class":232,"line":330},[230,687,688],{"class":262},"    audience",[230,690,300],{"class":266},[230,692,354],{"class":266},[230,694,695],{"class":303},"${env:ZEEQ_TELEMETRY_AUDIENCE}",[230,697,360],{"class":266},[230,699,700,703,705],{"class":232,"line":338},[230,701,702],{"class":262},"    username_claim",[230,704,300],{"class":266},[230,706,707],{"class":303}," sub\n",[230,709,710],{"class":232,"line":346},[230,711,256],{"emptyLinePlaceholder":255},[230,713,714],{"class":232,"line":363},[230,715,716],{"class":236},"  # Forwards the original caller's Authorization header from the inbound request\n",[230,718,719],{"class":232,"line":371},[230,720,721],{"class":236},"  # onto the exporter request, so Zeeq sees the end user's token, not a\n",[230,723,724],{"class":232,"line":386},[230,725,726],{"class":236},"  # collector service identity. Requires include_metadata: true on the receiver\n",[230,728,729],{"class":232,"line":394},[230,730,731],{"class":236},"  # and metadata_keys on the batch processor (see below).\n",[230,733,734,737],{"class":232,"line":406},[230,735,736],{"class":262},"  headers_setter",[230,738,267],{"class":266},[230,740,741,743],{"class":232,"line":411},[230,742,366],{"class":262},[230,744,267],{"class":266},[230,746,747,750,753,755],{"class":232,"line":419},[230,748,749],{"class":266},"      -",[230,751,752],{"class":262}," action",[230,754,300],{"class":266},[230,756,757],{"class":303}," upsert\n",[230,759,760,763,765],{"class":232,"line":427},[230,761,762],{"class":262},"        key",[230,764,300],{"class":266},[230,766,767],{"class":303}," authorization\n",[230,769,770,773,775],{"class":232,"line":439},[230,771,772],{"class":262},"        from_context",[230,774,300],{"class":266},[230,776,767],{"class":303},[230,778,779],{"class":232,"line":450},[230,780,256],{"emptyLinePlaceholder":255},[230,782,783,785],{"class":232,"line":461},[230,784,263],{"class":262},[230,786,267],{"class":266},[230,788,789,791],{"class":232,"line":466},[230,790,273],{"class":262},[230,792,267],{"class":266},[230,794,795,797],{"class":232,"line":474},[230,796,281],{"class":262},[230,798,267],{"class":266},[230,800,801,803],{"class":232,"line":482},[230,802,289],{"class":262},[230,804,267],{"class":266},[230,806,807,809,811],{"class":232,"line":490},[230,808,297],{"class":262},[230,810,300],{"class":266},[230,812,304],{"class":303},[230,814,815,818],{"class":232,"line":507},[230,816,817],{"class":262},"        auth",[230,819,267],{"class":266},[230,821,822,825,827],{"class":232,"line":522},[230,823,824],{"class":262},"          authenticator",[230,826,300],{"class":266},[230,828,829],{"class":303}," oidc\n",[230,831,832,835,837,840],{"class":232,"line":537},[230,833,834],{"class":262},"        include_metadata",[230,836,300],{"class":266},[230,838,839],{"class":402}," true",[230,841,842],{"class":236},"                           # required for from_context in headers_setter\n",[230,844,845,847],{"class":232,"line":545},[230,846,310],{"class":262},[230,848,267],{"class":266},[230,850,851,853,855],{"class":232,"line":558},[230,852,297],{"class":262},[230,854,300],{"class":266},[230,856,322],{"class":303},[230,858,859,861],{"class":232,"line":571},[230,860,817],{"class":262},[230,862,267],{"class":266},[230,864,866,868,870],{"class":232,"line":865},37,[230,867,824],{"class":262},[230,869,300],{"class":266},[230,871,829],{"class":303},[230,873,875,877,879],{"class":232,"line":874},38,[230,876,834],{"class":262},[230,878,300],{"class":266},[230,880,403],{"class":402},[230,882,884],{"class":232,"line":883},39,[230,885,256],{"emptyLinePlaceholder":255},[230,887,889,891],{"class":232,"line":888},40,[230,890,333],{"class":262},[230,892,267],{"class":266},[230,894,896],{"class":232,"line":895},41,[230,897,898],{"class":236},"  # Forward to Zeeq with per-user JWT pass-through via headers_setter.\n",[230,900,902],{"class":232,"line":901},42,[230,903,904],{"class":236},"  # The Authorization header from the inbound request is forwarded; if no\n",[230,906,908],{"class":232,"line":907},43,[230,909,910],{"class":236},"  # user token is present, the collector can fall back to a service-level\n",[230,912,914],{"class":232,"line":913},44,[230,915,916],{"class":236},"  # client_credentials identity via additional_auth on headers_setter.\n",[230,918,920],{"class":232,"line":919},45,[230,921,922],{"class":236},"  # otlphttp (not gRPC): Zeeq's Kestrel endpoint accepts both HTTP\u002F1.1 and\n",[230,924,926],{"class":232,"line":925},46,[230,927,928],{"class":236},"  # HTTP\u002F2 locally, so a plain HTTP exporter\u002Freceiver pair is simplest.\n",[230,930,932],{"class":232,"line":931},47,[230,933,934],{"class":236},"  # otlphttp auto-appends \u002Fv1\u002Flogs and \u002Fv1\u002Ftraces to this base endpoint.\n",[230,936,938,941],{"class":232,"line":937},48,[230,939,940],{"class":262},"  otlphttp\u002Fzeeq",[230,942,267],{"class":266},[230,944,946,948,950,952,955],{"class":232,"line":945},49,[230,947,349],{"class":262},[230,949,300],{"class":266},[230,951,354],{"class":266},[230,953,954],{"class":303},"${env:ZEEQ_OTLP_HTTP_ENDPOINT}",[230,956,360],{"class":266},[230,958,960,963],{"class":232,"line":959},50,[230,961,962],{"class":262},"    auth",[230,964,267],{"class":266},[230,966,968,971,973],{"class":232,"line":967},51,[230,969,970],{"class":262},"      authenticator",[230,972,300],{"class":266},[230,974,975],{"class":303}," headers_setter\n",[230,977,979,982],{"class":232,"line":978},52,[230,980,981],{"class":262},"    sending_queue",[230,983,267],{"class":266},[230,985,987,990,992],{"class":232,"line":986},53,[230,988,989],{"class":262},"      enabled",[230,991,300],{"class":266},[230,993,403],{"class":402},[230,995,997,999],{"class":232,"line":996},54,[230,998,389],{"class":262},[230,1000,267],{"class":266},[230,1002,1004,1006,1008,1010],{"class":232,"line":1003},55,[230,1005,397],{"class":262},[230,1007,300],{"class":266},[230,1009,839],{"class":402},[230,1011,1012],{"class":236},"                                     # Local\n",[230,1014,1016],{"class":232,"line":1015},56,[230,1017,256],{"emptyLinePlaceholder":255},[230,1019,1021],{"class":232,"line":1020},57,[230,1022,1023],{"class":236},"  # Fan-out to Aspire dashboard for full-telemetry visibility.\n",[230,1025,1027,1029],{"class":232,"line":1026},58,[230,1028,341],{"class":262},[230,1030,267],{"class":266},[230,1032,1034,1036,1038,1040,1042],{"class":232,"line":1033},59,[230,1035,349],{"class":262},[230,1037,300],{"class":266},[230,1039,354],{"class":266},[230,1041,357],{"class":303},[230,1043,360],{"class":266},[230,1045,1047,1049],{"class":232,"line":1046},60,[230,1048,366],{"class":262},[230,1050,267],{"class":266},[230,1052,1054,1056,1058,1060,1062],{"class":232,"line":1053},61,[230,1055,374],{"class":262},[230,1057,300],{"class":266},[230,1059,354],{"class":266},[230,1061,381],{"class":303},[230,1063,360],{"class":266},[230,1065,1067,1069],{"class":232,"line":1066},62,[230,1068,389],{"class":262},[230,1070,267],{"class":266},[230,1072,1074,1076,1078],{"class":232,"line":1073},63,[230,1075,397],{"class":262},[230,1077,300],{"class":266},[230,1079,403],{"class":402},[230,1081,1083],{"class":232,"line":1082},64,[230,1084,256],{"emptyLinePlaceholder":255},[230,1086,1088,1090],{"class":232,"line":1087},65,[230,1089,414],{"class":262},[230,1091,267],{"class":266},[230,1093,1095],{"class":232,"line":1094},66,[230,1096,1097],{"class":236},"  # ---- LOGS: keep only Claude Code + Codex session events (drop-if-true) ----\n",[230,1099,1101,1104],{"class":232,"line":1100},67,[230,1102,1103],{"class":262},"  filter\u002Fagent_session_logs",[230,1105,267],{"class":266},[230,1107,1109,1112,1114],{"class":232,"line":1108},68,[230,1110,1111],{"class":262},"    error_mode",[230,1113,300],{"class":266},[230,1115,1116],{"class":303}," ignore\n",[230,1118,1120,1122],{"class":232,"line":1119},69,[230,1121,540],{"class":262},[230,1123,267],{"class":266},[230,1125,1127,1130],{"class":232,"line":1126},70,[230,1128,1129],{"class":262},"      log_record",[230,1131,267],{"class":266},[230,1133,1135,1138],{"class":232,"line":1134},71,[230,1136,1137],{"class":266},"        -",[230,1139,1141],{"class":1140},"s7zQu"," |\n",[230,1143,1145],{"class":232,"line":1144},72,[230,1146,1147],{"class":303},"          not (\n",[230,1149,1151],{"class":232,"line":1150},73,[230,1152,1153],{"class":303},"            resource.attributes[\"service.name\"] == \"claude-code\" and\n",[230,1155,1157],{"class":232,"line":1156},74,[230,1158,1159],{"class":303},"            (log.body == \"claude_code.user_prompt\" or\n",[230,1161,1163],{"class":232,"line":1162},75,[230,1164,1165],{"class":303},"             log.body == \"claude_code.tool_result\" or\n",[230,1167,1169],{"class":232,"line":1168},76,[230,1170,1171],{"class":303},"             log.body == \"claude_code.api_request\" or\n",[230,1173,1175],{"class":232,"line":1174},77,[230,1176,1177],{"class":303},"             log.body == \"claude_code.tool_decision\")\n",[230,1179,1181],{"class":232,"line":1180},78,[230,1182,1183],{"class":303},"          ) and not (\n",[230,1185,1187],{"class":232,"line":1186},79,[230,1188,1189],{"class":303},"            IsMatch(resource.attributes[\"service.name\"], \".*codex.*\") and\n",[230,1191,1193],{"class":232,"line":1192},80,[230,1194,1195],{"class":303},"            (log.attributes[\"prompt_length\"] != nil or\n",[230,1197,1199],{"class":232,"line":1198},81,[230,1200,1201],{"class":303},"             (log.attributes[\"tool_name\"] != nil and log.attributes[\"call_id\"] != nil) or\n",[230,1203,1205],{"class":232,"line":1204},82,[230,1206,1207],{"class":303},"             log.attributes[\"event.kind\"] == \"response.completed\" or\n",[230,1209,1211],{"class":232,"line":1210},83,[230,1212,1213],{"class":303},"             (log.attributes[\"mcp_servers\"] != nil and log.attributes[\"approval_policy\"] != nil))\n",[230,1215,1217],{"class":232,"line":1216},84,[230,1218,1219],{"class":303},"          )\n",[230,1221,1223],{"class":232,"line":1222},85,[230,1224,256],{"emptyLinePlaceholder":255},[230,1226,1228],{"class":232,"line":1227},86,[230,1229,1230],{"class":236},"  # ---- TRACES: keep only Copilot Chat business spans (drop-if-true) ----\n",[230,1232,1234,1237],{"class":232,"line":1233},87,[230,1235,1236],{"class":262},"  filter\u002Fagent_session_traces",[230,1238,267],{"class":266},[230,1240,1242,1244,1246],{"class":232,"line":1241},88,[230,1243,1111],{"class":262},[230,1245,300],{"class":266},[230,1247,1116],{"class":303},[230,1249,1251,1253],{"class":232,"line":1250},89,[230,1252,485],{"class":262},[230,1254,267],{"class":266},[230,1256,1258,1261],{"class":232,"line":1257},90,[230,1259,1260],{"class":262},"      span",[230,1262,267],{"class":266},[230,1264,1266,1268],{"class":232,"line":1265},91,[230,1267,1137],{"class":266},[230,1269,1141],{"class":1140},[230,1271,1273],{"class":232,"line":1272},92,[230,1274,1147],{"class":303},[230,1276,1278],{"class":232,"line":1277},93,[230,1279,1280],{"class":303},"            resource.attributes[\"service.name\"] == \"copilot-chat\" and\n",[230,1282,1284],{"class":232,"line":1283},94,[230,1285,1286],{"class":303},"            span.attributes[\"copilot_chat.chat_session_id\"] != nil and\n",[230,1288,1290],{"class":232,"line":1289},95,[230,1291,1292],{"class":303},"            (span.attributes[\"gen_ai.operation.name\"] == \"invoke_agent\" or\n",[230,1294,1296],{"class":232,"line":1295},96,[230,1297,1298],{"class":303},"             span.attributes[\"gen_ai.operation.name\"] == \"chat\" or\n",[230,1300,1302],{"class":232,"line":1301},97,[230,1303,1304],{"class":303},"             span.attributes[\"gen_ai.operation.name\"] == \"execute_tool\")\n",[230,1306,1308],{"class":232,"line":1307},98,[230,1309,1219],{"class":303},[230,1311,1313],{"class":232,"line":1312},99,[230,1314,256],{"emptyLinePlaceholder":255},[230,1316,1318,1320],{"class":232,"line":1317},100,[230,1319,422],{"class":262},[230,1321,267],{"class":266},[230,1323,1325,1327,1329],{"class":232,"line":1324},101,[230,1326,442],{"class":262},[230,1328,300],{"class":266},[230,1330,1331],{"class":435}," 128\n",[230,1333,1335,1337,1339],{"class":232,"line":1334},102,[230,1336,430],{"class":262},[230,1338,300],{"class":266},[230,1340,1341],{"class":435}," 512\n",[230,1343,1345,1347,1349],{"class":232,"line":1344},103,[230,1346,453],{"class":262},[230,1348,300],{"class":266},[230,1350,1351],{"class":303}," 5s\n",[230,1353,1355],{"class":232,"line":1354},104,[230,1356,1357],{"class":236},"    # One batcher per distinct caller token: preserves per-user Authorization\n",[230,1359,1361],{"class":232,"line":1360},105,[230,1362,1363],{"class":236},"    # forwarding correctness. Without this, records from two users could land\n",[230,1365,1367],{"class":232,"line":1366},106,[230,1368,1369],{"class":236},"    # in one exported batch carrying a single Authorization header.\n",[230,1371,1373,1376,1378,1380,1383],{"class":232,"line":1372},107,[230,1374,1375],{"class":262},"    metadata_keys",[230,1377,300],{"class":266},[230,1379,498],{"class":266},[230,1381,1382],{"class":303},"authorization",[230,1384,504],{"class":266},[230,1386,1388,1391,1393],{"class":232,"line":1387},108,[230,1389,1390],{"class":262},"    metadata_cardinality_limit",[230,1392,300],{"class":266},[230,1394,1395],{"class":435}," 1000\n",[230,1397,1399],{"class":232,"line":1398},109,[230,1400,256],{"emptyLinePlaceholder":255},[230,1402,1404,1406],{"class":232,"line":1403},110,[230,1405,469],{"class":262},[230,1407,267],{"class":266},[230,1409,1411,1414,1416,1418,1421,1424,1427],{"class":232,"line":1410},111,[230,1412,1413],{"class":262},"  extensions",[230,1415,300],{"class":266},[230,1417,498],{"class":266},[230,1419,1420],{"class":303},"oidc",[230,1422,1423],{"class":266},",",[230,1425,1426],{"class":303}," headers_setter",[230,1428,504],{"class":266},[230,1430,1432,1434],{"class":232,"line":1431},112,[230,1433,477],{"class":262},[230,1435,267],{"class":266},[230,1437,1439,1441],{"class":232,"line":1438},113,[230,1440,485],{"class":262},[230,1442,267],{"class":266},[230,1444,1446,1448,1450,1452,1454],{"class":232,"line":1445},114,[230,1447,493],{"class":262},[230,1449,300],{"class":266},[230,1451,498],{"class":266},[230,1453,501],{"class":303},[230,1455,504],{"class":266},[230,1457,1459,1461,1463,1465,1468,1470,1473],{"class":232,"line":1458},115,[230,1460,510],{"class":262},[230,1462,300],{"class":266},[230,1464,498],{"class":266},[230,1466,1467],{"class":303},"filter\u002Fagent_session_traces",[230,1469,1423],{"class":266},[230,1471,1472],{"class":303}," batch",[230,1474,504],{"class":266},[230,1476,1478,1480,1482,1484,1486,1488,1491],{"class":232,"line":1477},116,[230,1479,525],{"class":262},[230,1481,300],{"class":266},[230,1483,498],{"class":266},[230,1485,532],{"class":303},[230,1487,1423],{"class":266},[230,1489,1490],{"class":303}," otlphttp\u002Fzeeq",[230,1492,504],{"class":266},[230,1494,1496,1498],{"class":232,"line":1495},117,[230,1497,540],{"class":262},[230,1499,267],{"class":266},[230,1501,1503,1505,1507,1509,1511],{"class":232,"line":1502},118,[230,1504,493],{"class":262},[230,1506,300],{"class":266},[230,1508,498],{"class":266},[230,1510,501],{"class":303},[230,1512,504],{"class":266},[230,1514,1516,1518,1520,1522,1525,1527,1529],{"class":232,"line":1515},119,[230,1517,510],{"class":262},[230,1519,300],{"class":266},[230,1521,498],{"class":266},[230,1523,1524],{"class":303},"filter\u002Fagent_session_logs",[230,1526,1423],{"class":266},[230,1528,1472],{"class":303},[230,1530,504],{"class":266},[230,1532,1534,1536,1538,1540,1542,1544,1546],{"class":232,"line":1533},120,[230,1535,525],{"class":262},[230,1537,300],{"class":266},[230,1539,498],{"class":266},[230,1541,532],{"class":303},[230,1543,1423],{"class":266},[230,1545,1490],{"class":303},[230,1547,504],{"class":266},[207,1549,1551],{"id":1550},"authentication","Authentication",[169,1553,1554],{},"The OTLP receivers validate incoming JWTs using the OIDC extension (configured with Zeeq's OpenIddict issuer). Only authenticated requests are accepted; invalid or missing bearer tokens are rejected at the collector edge.",[169,1556,1557,1561,1562,1565,1566,1569],{},[1558,1559,1560],"strong",{},"Note",": Aspire health checks (172.18.0.1) will log ",[227,1563,1564],{},"\"Authentication failed: missing or empty header\""," warnings in the collector logs. These are expected and benign—health checks intentionally have no bearer token. Use the separate ",[227,1567,1568],{},"otlp\u002Fhealth"," receivers (ports 4319\u002F4320) if cleaner logs are preferred, though they add complexity for what is fundamentally a deployment detail.",[207,1571,1573],{"id":1572},"local-networking-notes","Local Networking Notes",[169,1575,1576,1577,1579],{},"The ",[227,1578,1420],{}," extension fetches discovery\u002FJWKS once at collector startup and fails the whole container (no retry) if that fetch errors, so a few local-dev-only details matter:",[1581,1582,1583,1612,1629],"ul",{},[176,1584,1585,1588,1589,1592,1593,1596,1597,1599,1600,1603,1604,1607,1608,1611],{},[1558,1586,1587],{},"Issuer trailing slash."," OpenIddict's ",[227,1590,1591],{},"SetIssuer"," canonicalizes to a root-path URI (",[227,1594,1595],{},"http:\u002F\u002Fzeeq-web.localhost:8095\u002F","), and the ",[227,1598,1420],{}," extension does an exact string match between ",[227,1601,1602],{},"issuer_url"," and the discovery document's self-reported ",[227,1605,1606],{},"issuer"," field. Omitting the trailing slash fails collector startup with a ",[227,1609,1610],{},"did not match the issuer URL returned by provider"," error.",[176,1613,1614,1617,1618,1621,1622,1625,1626,1628],{},[1558,1615,1616],{},"DNS resolution inside the container."," ",[227,1619,1620],{},"zeeq-web.localhost"," is a browser\u002FOS-resolved loopback hostname, not a Docker network name, and does not resolve inside the collector container by default. The AppHost maps it via ",[227,1623,1624],{},"--add-host zeeq-web.localhost:host-gateway"," (Docker 20.10+), which routes back through the host machine to YARP's published port, preserving the hostname the ",[227,1627,1420],{}," extension needs verbatim.",[176,1630,1631,1634,1635,1638,1639,1642,1643,1646],{},[1558,1632,1633],{},"Startup ordering."," Discovery routes through ",[227,1636,1637],{},"zeeq-web.localhost -> yarp-reverse-proxy -> Vite dev server -> zeeq-server",", not directly to the backend, so the collector resource waits on the full chain (",[227,1640,1641],{},"WaitFor(backend\u002Ffrontend\u002Fproxy)","), and the frontend resource has an explicit ",[227,1644,1645],{},"WithHttpHealthCheck(\"\u002F\")"," so that wait blocks on Vite actually serving, not just the process having started.",[164,1648,1650],{"id":1649},"agent-harness-configuration","Agent Harness Configuration",[169,1652,1653,1654,1657,1658,1660,1661,1664,1665,1660,1668,1671],{},"Verified live against Claude Code 2.1.197, Codex CLI 0.144.3, and VS Code Copilot Chat. Each harness needs its OTLP exporter pointed at the collector's HTTP endpoint (",[227,1655,1656],{},"http:\u002F\u002Flocalhost:44318"," locally), with the caller's Zeeq user token attached, matching what the ",[227,1659,1420],{},"\u002F",[227,1662,1663],{},"filter"," blocks above expect. Claude Code and Copilot Chat use a base URL that gets ",[227,1666,1667],{},"\u002Fv1\u002Flogs",[227,1669,1670],{},"\u002Fv1\u002Ftraces"," auto-appended; Codex does not auto-append and needs the full path (see its section below).",[207,1673,1675],{"id":1674},"claude-code","Claude Code",[169,1677,1678,1679,1682,1683,1686,1687,1690],{},"Configured via ",[227,1680,1681],{},"env"," in ",[227,1684,1685],{},".claude\u002Fsettings.json"," (or ",[227,1688,1689],{},"settings.local.json","):",[220,1692,1696],{"className":1693,"code":1694,"language":1695,"meta":225,"style":225},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"env\": {\n    \"CLAUDE_CODE_ENABLE_TELEMETRY\": \"1\",\n    \"OTEL_EXPORTER_OTLP_PROTOCOL\": \"http\u002Fprotobuf\",\n    \"OTEL_EXPORTER_OTLP_ENDPOINT\": \"http:\u002F\u002Flocalhost:44318\",\n    \"OTEL_EXPORTER_OTLP_HEADERS\": \"Authorization=Bearer \u003Czeeq-user-token>\",\n    \"OTEL_LOGS_EXPORTER\": \"otlp\",\n    \"OTEL_LOG_USER_PROMPTS\": \"1\",\n    \"OTEL_LOG_TOOL_DETAILS\": \"1\"\n  }\n}\n","json",[227,1697,1698,1703,1718,1741,1761,1780,1800,1819,1838,1855,1860],{"__ignoreMap":225},[230,1699,1700],{"class":232,"line":233},[230,1701,1702],{"class":266},"{\n",[230,1704,1705,1708,1711,1713,1715],{"class":232,"line":240},[230,1706,1707],{"class":266},"  \"",[230,1709,1681],{"class":1710},"spNyl",[230,1712,660],{"class":266},[230,1714,300],{"class":266},[230,1716,1717],{"class":266}," {\n",[230,1719,1720,1723,1727,1729,1731,1733,1736,1738],{"class":232,"line":246},[230,1721,1722],{"class":266},"    \"",[230,1724,1726],{"class":1725},"sBMFI","CLAUDE_CODE_ENABLE_TELEMETRY",[230,1728,660],{"class":266},[230,1730,300],{"class":266},[230,1732,354],{"class":266},[230,1734,1735],{"class":303},"1",[230,1737,660],{"class":266},[230,1739,1740],{"class":266},",\n",[230,1742,1743,1745,1748,1750,1752,1754,1757,1759],{"class":232,"line":252},[230,1744,1722],{"class":266},[230,1746,1747],{"class":1725},"OTEL_EXPORTER_OTLP_PROTOCOL",[230,1749,660],{"class":266},[230,1751,300],{"class":266},[230,1753,354],{"class":266},[230,1755,1756],{"class":303},"http\u002Fprotobuf",[230,1758,660],{"class":266},[230,1760,1740],{"class":266},[230,1762,1763,1765,1768,1770,1772,1774,1776,1778],{"class":232,"line":259},[230,1764,1722],{"class":266},[230,1766,1767],{"class":1725},"OTEL_EXPORTER_OTLP_ENDPOINT",[230,1769,660],{"class":266},[230,1771,300],{"class":266},[230,1773,354],{"class":266},[230,1775,1656],{"class":303},[230,1777,660],{"class":266},[230,1779,1740],{"class":266},[230,1781,1782,1784,1787,1789,1791,1793,1796,1798],{"class":232,"line":270},[230,1783,1722],{"class":266},[230,1785,1786],{"class":1725},"OTEL_EXPORTER_OTLP_HEADERS",[230,1788,660],{"class":266},[230,1790,300],{"class":266},[230,1792,354],{"class":266},[230,1794,1795],{"class":303},"Authorization=Bearer \u003Czeeq-user-token>",[230,1797,660],{"class":266},[230,1799,1740],{"class":266},[230,1801,1802,1804,1807,1809,1811,1813,1815,1817],{"class":232,"line":278},[230,1803,1722],{"class":266},[230,1805,1806],{"class":1725},"OTEL_LOGS_EXPORTER",[230,1808,660],{"class":266},[230,1810,300],{"class":266},[230,1812,354],{"class":266},[230,1814,501],{"class":303},[230,1816,660],{"class":266},[230,1818,1740],{"class":266},[230,1820,1821,1823,1826,1828,1830,1832,1834,1836],{"class":232,"line":286},[230,1822,1722],{"class":266},[230,1824,1825],{"class":1725},"OTEL_LOG_USER_PROMPTS",[230,1827,660],{"class":266},[230,1829,300],{"class":266},[230,1831,354],{"class":266},[230,1833,1735],{"class":303},[230,1835,660],{"class":266},[230,1837,1740],{"class":266},[230,1839,1840,1842,1845,1847,1849,1851,1853],{"class":232,"line":294},[230,1841,1722],{"class":266},[230,1843,1844],{"class":1725},"OTEL_LOG_TOOL_DETAILS",[230,1846,660],{"class":266},[230,1848,300],{"class":266},[230,1850,354],{"class":266},[230,1852,1735],{"class":303},[230,1854,360],{"class":266},[230,1856,1857],{"class":232,"line":307},[230,1858,1859],{"class":266},"  }\n",[230,1861,1862],{"class":232,"line":315},[230,1863,1864],{"class":266},"}\n",[199,1866,1867,1868,1660,1871,1874,1875,1878,1879,1881,1882,1885,1886,1889],{},"Deliberately no ",[227,1869,1870],{},"OTEL_TRACES_EXPORTER",[227,1872,1873],{},"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA"," here. Claude Code is ",[1558,1876,1877],{},"logs-only",": ",[227,1880,1467],{}," only has an allow condition for ",[227,1883,1884],{},"service.name == \"copilot-chat\"",", so any Claude Code spans would be emitted and immediately dropped at the collector. Cost (",[227,1887,1888],{},"cost_usd",") is a log-only field on Claude Code's wire anyway - there's nothing spans would add here that logs don't already carry.",[169,1891,1892,1893,1896,1897,1896,1900,1903,1904,1907,1908,1911,1912,1915],{},"The filter above keeps ",[227,1894,1895],{},"claude_code.user_prompt",", ",[227,1898,1899],{},"claude_code.tool_result",[227,1901,1902],{},"claude_code.api_request",", and ",[227,1905,1906],{},"claude_code.tool_decision",", identified by ",[227,1909,1910],{},"log.body"," (Claude Code emits the event type as the log body, not as an ",[227,1913,1914],{},"event.name"," attribute despite what Anthropic's docs claim).",[207,1917,1919],{"id":1918},"codex","Codex",[169,1921,1678,1922,1682,1925,300],{},[227,1923,1924],{},"[otel]",[227,1926,1927],{},"~\u002F.codex\u002Fconfig.toml",[220,1929,1933],{"className":1930,"code":1931,"language":1932,"meta":225,"style":225},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[otel]\nexporter = \"otlp-http\"\nlog_user_prompt = true\n\n[otel.exporter.\"otlp-http\"]\nendpoint = \"http:\u002F\u002Flocalhost:44318\u002Fv1\u002Flogs\"\nheaders = { authorization = \"Bearer \u003Czeeq-user-token>\" }\n","toml",[227,1934,1935,1940,1945,1950,1954,1959,1964],{"__ignoreMap":225},[230,1936,1937],{"class":232,"line":233},[230,1938,1939],{},"[otel]\n",[230,1941,1942],{"class":232,"line":240},[230,1943,1944],{},"exporter = \"otlp-http\"\n",[230,1946,1947],{"class":232,"line":246},[230,1948,1949],{},"log_user_prompt = true\n",[230,1951,1952],{"class":232,"line":252},[230,1953,256],{"emptyLinePlaceholder":255},[230,1955,1956],{"class":232,"line":259},[230,1957,1958],{},"[otel.exporter.\"otlp-http\"]\n",[230,1960,1961],{"class":232,"line":270},[230,1962,1963],{},"endpoint = \"http:\u002F\u002Flocalhost:44318\u002Fv1\u002Flogs\"\n",[230,1965,1966],{"class":232,"line":278},[230,1967,1968],{},"headers = { authorization = \"Bearer \u003Czeeq-user-token>\" }\n",[199,1970,1971,1972,1974,1975,1660,1977,1979,1980,1983,1984,1987,1988,1991,1992,1994,1995,1997],{},"Unlike Claude Code and Copilot Chat (both of which use the generic ",[227,1973,1767],{}," convention and auto-append ",[227,1976,1667],{},[227,1978,1670],{}," to a base URL), Codex's ",[227,1981,1982],{},"endpoint"," is used verbatim with no path appended - confirmed directly from Codex's Rust source (",[227,1985,1986],{},"codex-rs\u002Fotel\u002Ftests\u002Fsuite\u002Fotlp_http_loopback.rs","), where every ",[227,1989,1990],{},"OtlpHttp"," test endpoint is constructed with an explicit ",[227,1993,1667],{}," or ",[227,1996,1670],{}," suffix. Omitting it here means every export 404s silently at the collector.",[169,1999,2000,2001,2005,2006,2008,2009,2012,2013,2016,2017,2020,2021,2024],{},"The filter above keeps Codex session events by attribute ",[2002,2003,2004],"em",{},"shape",", not by an ",[227,2007,1914],{}," attribute (also absent on Codex's wire despite docs claiming otherwise): a ",[227,2010,2011],{},"prompt_length"," attribute identifies a prompt, ",[227,2014,2015],{},"tool_name","+",[227,2018,2019],{},"call_id"," identifies a tool result, and ",[227,2022,2023],{},"event.kind == \"response.completed\""," identifies a completion.",[207,2026,2028],{"id":2027},"copilot-chat","Copilot Chat",[169,2030,2031,2032,300],{},"Configured via VS Code ",[227,2033,2034],{},"settings.json",[220,2036,2038],{"className":1693,"code":2037,"language":1695,"meta":225,"style":225},"{\n  \"github.copilot.chat.otel.enabled\": true,\n  \"github.copilot.chat.otel.exporterType\": \"otlp-http\",\n  \"github.copilot.chat.otel.otlpEndpoint\": \"http:\u002F\u002Flocalhost:44318\",\n  \"github.copilot.chat.otel.captureContent\": true\n}\n",[227,2039,2040,2044,2058,2078,2097,2110],{"__ignoreMap":225},[230,2041,2042],{"class":232,"line":233},[230,2043,1702],{"class":266},[230,2045,2046,2048,2051,2053,2055],{"class":232,"line":240},[230,2047,1707],{"class":266},[230,2049,2050],{"class":1710},"github.copilot.chat.otel.enabled",[230,2052,660],{"class":266},[230,2054,300],{"class":266},[230,2056,2057],{"class":266}," true,\n",[230,2059,2060,2062,2065,2067,2069,2071,2074,2076],{"class":232,"line":246},[230,2061,1707],{"class":266},[230,2063,2064],{"class":1710},"github.copilot.chat.otel.exporterType",[230,2066,660],{"class":266},[230,2068,300],{"class":266},[230,2070,354],{"class":266},[230,2072,2073],{"class":303},"otlp-http",[230,2075,660],{"class":266},[230,2077,1740],{"class":266},[230,2079,2080,2082,2085,2087,2089,2091,2093,2095],{"class":232,"line":252},[230,2081,1707],{"class":266},[230,2083,2084],{"class":1710},"github.copilot.chat.otel.otlpEndpoint",[230,2086,660],{"class":266},[230,2088,300],{"class":266},[230,2090,354],{"class":266},[230,2092,1656],{"class":303},[230,2094,660],{"class":266},[230,2096,1740],{"class":266},[230,2098,2099,2101,2104,2106,2108],{"class":232,"line":259},[230,2100,1707],{"class":266},[230,2102,2103],{"class":1710},"github.copilot.chat.otel.captureContent",[230,2105,660],{"class":266},[230,2107,300],{"class":266},[230,2109,403],{"class":266},[230,2111,2112],{"class":232,"line":270},[230,2113,1864],{"class":266},[169,2115,2116],{},"Auth header via environment instead, since VS Code settings don't support one directly:",[220,2118,2122],{"className":2119,"code":2120,"language":2121,"meta":225,"style":225},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export OTEL_EXPORTER_OTLP_HEADERS=\"Authorization=Bearer \u003Czeeq-user-token>\"\n","bash",[227,2123,2124],{"__ignoreMap":225},[230,2125,2126,2129,2133,2136,2138,2140],{"class":232,"line":233},[230,2127,2128],{"class":1710},"export",[230,2130,2132],{"class":2131},"sTEyZ"," OTEL_EXPORTER_OTLP_HEADERS",[230,2134,2135],{"class":266},"=",[230,2137,660],{"class":266},[230,2139,1795],{"class":303},[230,2141,360],{"class":266},[199,2143,2144,2147],{},[227,2145,2146],{},"captureContent"," is required for prompt text and tool arguments\u002Fresults to appear at all - it is off by default, and without it only token counts and identity metadata are captured.",[169,2149,2150,2151,2154,2155,2158,2159,1660,2162,1660,2165,2168,2169,2172],{},"Copilot Chat is ",[1558,2152,2153],{},"traces-only"," for practical purposes: cost (",[227,2156,2157],{},"copilot_chat.copilot_usage_nano_aiu","), MCP tool identity, and prompt\u002Fresponse content are all trace-only fields, and Copilot's logs (when they arrive - often delayed relative to the matching trace spans) are a strict subset with none of that. The filter above keeps ",[227,2160,2161],{},"invoke_agent",[227,2163,2164],{},"chat",[227,2166,2167],{},"execute_tool"," spans, gated on ",[227,2170,2171],{},"copilot_chat.chat_session_id"," being present to exclude Next Edit Suggestions completions and other sessionless housekeeping spans.",[2174,2175,2176],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":225,"searchDepth":240,"depth":240,"links":2178},[2179,2180,2181,2186],{"id":166,"depth":240,"text":167},{"id":190,"depth":240,"text":191},{"id":204,"depth":240,"text":205,"children":2182},[2183,2184,2185],{"id":209,"depth":246,"text":210},{"id":1550,"depth":246,"text":1551},{"id":1572,"depth":246,"text":1573},{"id":1649,"depth":240,"text":1650,"children":2187},[2188,2189,2190],{"id":1674,"depth":246,"text":1675},{"id":1918,"depth":246,"text":1919},{"id":2027,"depth":246,"text":2028},"Ingest and processing of OpenTelemetry data from agent harnesses.","md",null,{"icon":2195},"i-lucide-rss",{"title":155,"description":2191},"oSHprwXTZLh_pfH8nIbiumboJM4g97P6pw9h2xd1aFg",[2199,2193],{"title":151,"path":152,"stem":153,"description":2200,"children":-1},"How Zeeq pulls Markdown documentation out of public and private GitHub repositories and turns it into searchable library documents.",1784297365800]