pi-langfuse 1.5.13 → 1.5.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,8 +108,11 @@ export LANGFUSE_CAPTURE_OUTPUTS=true
108
108
  export LANGFUSE_CAPTURE_TOOL_IO=false
109
109
  export LANGFUSE_CAPTURE_SYSTEM_PROMPT=false
110
110
  export LANGFUSE_CAPTURE_CWD=false
111
+ export LANGFUSE_CAPTURE_SOURCE_METADATA=false
111
112
  ```
112
113
 
114
+ Source metadata remains off in every preset unless `LANGFUSE_CAPTURE_SOURCE_METADATA=true` is set explicitly.
115
+
113
116
  All captured payloads are redacted before upload. The extension masks common API keys, bearer tokens, passwords, cookies, private keys, Langfuse keys, GitHub/npm/AWS-style tokens, and local absolute paths.
114
117
 
115
118
  ### Payload limits
@@ -198,50 +201,44 @@ The package also includes a Langfuse CLI skill, so Langfuse data can be queried
198
201
 
199
202
  ## Source Metadata
200
203
 
201
- Local prototype note: source metadata support in this installed package is a local prototype patch. A durable solution should be shipped through an upstream PR, a fork, or a maintained package version so reinstalling the extension does not lose the behavior.
204
+ Repository source capture is independent of the privacy presets and disabled by default. Enable it only after deciding that commit identity is appropriate for the Langfuse project:
205
+
206
+ ```bash
207
+ export LANGFUSE_CAPTURE_SOURCE_METADATA=true
208
+ ```
202
209
 
203
- For Git-backed runs, the extension attaches safe source metadata to traces:
210
+ For a Git worktree, the extension records only revision state:
204
211
 
205
212
  ```json
206
213
  {
207
214
  "source_type": "git-repo",
208
- "repo_identity": "owner/repo",
209
- "repo_owner": "owner",
210
- "repo_name": "repo",
211
- "repo_root_name": "repo",
212
- "git_branch": "main",
213
- "git_commit": "abc123",
214
- "git_remote_host": "github.com",
215
- "git_remote_path": "owner/repo",
215
+ "vcs.ref.head.revision": "0123456789abcdef...",
216
+ "git_detached": "false",
217
+ "git_dirty": "false",
216
218
  "metadata_source": "git-detection"
217
219
  }
218
220
  ```
219
221
 
220
- `repo_identity` is `owner/repo`. `repo_name` is the repo name only and must not contain a slash.
222
+ The revision is the full `HEAD` commit. Dirty state includes tracked changes and untracked files, but never their paths or contents. Detached state is reported without a branch or tag name. Git remotes, URLs, credentials, usernames, branches, absolute paths, and repository names are never inspected or uploaded by this collector.
221
223
 
222
- A Git repo may optionally provide `.pi-langfuse.metadata.json`. Overrides are whitelist-only; unknown keys are ignored. Allowed keys are:
224
+ When capture is off, the collector does not invoke Git and reports `source_type: "disabled"`. Non-Git directories report `non-git`; a missing or unusable Git executable and incomplete Git state report `unavailable`.
223
225
 
224
- ```text
225
- repo_identity
226
- repo_owner
227
- repo_name
228
- source_type
229
- service_name
230
- project_slug
231
- environment
232
- observability_owner
226
+ Use native Langfuse and OpenTelemetry settings for deployment identity and explicit operator-owned overrides instead of repository files:
227
+
228
+ ```bash
229
+ export LANGFUSE_RELEASE="1.2.3"
230
+ export LANGFUSE_TRACING_ENVIRONMENT="production"
231
+ export OTEL_SERVICE_NAME="pi-agent"
232
+ export OTEL_RESOURCE_ATTRIBUTES="service.version=1.2.3,vcs.repository.name=public-repo"
233
233
  ```
234
234
 
235
- Repo-local overrides are used only after the working directory is confirmed to be inside a usable Git repo. If Git detection fails for any reason, including a missing Git command, corrupted repo, or non-Git folder, the extension ignores repo-local identity files and emits only:
235
+ `LANGFUSE_RELEASE` and `LANGFUSE_TRACING_ENVIRONMENT` keep their Langfuse semantics. `OTEL_SERVICE_NAME` and `OTEL_RESOURCE_ATTRIBUTES` are loaded as process-scoped OpenTelemetry resource attributes; values such as `vcs.repository.name` apply to every session sharing the runtime, may identify private source, and require a runtime restart to change.
236
236
 
237
- ```json
238
- {
239
- "source_type": "non-git",
240
- "metadata_source": "non-git"
241
- }
242
- ```
237
+ ### Compatibility and rollback
238
+
239
+ Earlier versions emitted `git_commit`, branch, remote, owner, repository, and repo-local `.pi-langfuse.metadata.json` values by default. New traces use `vcs.ref.head.revision` and stop reading that file. Update dashboards before enabling source capture; historical traces are unchanged.
243
240
 
244
- The extension must not upload raw absolute local paths, credentialed remotes, tokens, unknown override keys, or folder names for non-Git folders.
241
+ Unset `LANGFUSE_CAPTURE_SOURCE_METADATA` to stop collection immediately. Pin `pi-langfuse@1.5.12` only if the old schema is required during migration; doing so also restores its broader default source disclosure.
245
242
 
246
243
  ## Troubleshooting
247
244
 
package/README_CN.md CHANGED
@@ -108,8 +108,11 @@ export LANGFUSE_CAPTURE_OUTPUTS=true
108
108
  export LANGFUSE_CAPTURE_TOOL_IO=false
109
109
  export LANGFUSE_CAPTURE_SYSTEM_PROMPT=false
110
110
  export LANGFUSE_CAPTURE_CWD=false
111
+ export LANGFUSE_CAPTURE_SOURCE_METADATA=false
111
112
  ```
112
113
 
114
+ 所有隐私预设默认都关闭源码元数据;只有显式设置 `LANGFUSE_CAPTURE_SOURCE_METADATA=true` 才会启用。
115
+
113
116
  所有被采集的负载在上传前仍会脱敏。扩展会隐藏常见 API key、Bearer token、密码、Cookie、私钥、Langfuse key、GitHub/npm/AWS 风格 token,并对本地绝对路径做 hash。
114
117
 
115
118
  ### 方式 3:持久化 `config.json`
@@ -175,6 +178,47 @@ pi list
175
178
  /pi-langfuse-langfuse <查询内容>
176
179
  ```
177
180
 
181
+ ## 源码元数据
182
+
183
+ 仓库源码采集独立于隐私预设,默认关闭。只有在确认提交标识适合写入当前 Langfuse 项目后才启用:
184
+
185
+ ```bash
186
+ export LANGFUSE_CAPTURE_SOURCE_METADATA=true
187
+ ```
188
+
189
+ 对于 Git 工作树,扩展只记录修订状态:
190
+
191
+ ```json
192
+ {
193
+ "source_type": "git-repo",
194
+ "vcs.ref.head.revision": "0123456789abcdef...",
195
+ "git_detached": "false",
196
+ "git_dirty": "false",
197
+ "metadata_source": "git-detection"
198
+ }
199
+ ```
200
+
201
+ 修订值为完整的 `HEAD` 提交。dirty 状态包含已跟踪变更和未跟踪文件,但绝不包含路径或内容。detached 状态不会附带分支名或标签名。此采集器不会检查或上传 Git remote、URL、凭据、用户名、分支、绝对路径或仓库名。
202
+
203
+ 关闭采集时,扩展不会调用 Git,并报告 `source_type: "disabled"`。非 Git 目录报告 `non-git`;Git 不可用或仓库状态不完整时报告 `unavailable`。
204
+
205
+ 部署标识和显式覆盖应使用 Langfuse 与 OpenTelemetry 原生配置,而不是仓库文件:
206
+
207
+ ```bash
208
+ export LANGFUSE_RELEASE="1.2.3"
209
+ export LANGFUSE_TRACING_ENVIRONMENT="production"
210
+ export OTEL_SERVICE_NAME="pi-agent"
211
+ export OTEL_RESOURCE_ATTRIBUTES="service.version=1.2.3,vcs.repository.name=public-repo"
212
+ ```
213
+
214
+ `LANGFUSE_RELEASE` 与 `LANGFUSE_TRACING_ENVIRONMENT` 保持 Langfuse 原生语义。`OTEL_SERVICE_NAME` 和 `OTEL_RESOURCE_ATTRIBUTES` 会作为进程级 OpenTelemetry resource attributes 加载;`vcs.repository.name` 等值会应用到共享同一 runtime 的所有会话,可能暴露私有源码身份,而且修改后需要重启 runtime。
215
+
216
+ ### 兼容与回滚
217
+
218
+ 旧版本默认发送 `git_commit`、分支、remote、owner、仓库名及 `.pi-langfuse.metadata.json` 中的值。新 trace 改用 `vcs.ref.head.revision`,并停止读取该文件。启用源码采集前应更新 dashboard;历史 trace 不受影响。
219
+
220
+ 取消设置 `LANGFUSE_CAPTURE_SOURCE_METADATA` 可立即停止采集。迁移期间只有在必须保留旧 schema 时才固定到 `pi-langfuse@1.5.12`;这样也会恢复旧版本更宽泛的默认源码披露。
221
+
178
222
  ## 故障排除
179
223
 
180
224
  ### 没有看到 trace
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-langfuse",
3
- "version": "1.5.13",
3
+ "version": "1.5.14",
4
4
  "description": "Langfuse extension for Pi coding agent",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,4 +1,4 @@
1
- import { redactValue } from "./redaction.js";
1
+ import { hashPath, redactValue } from "./redaction.js";
2
2
 
3
3
  export interface CapturePolicy {
4
4
  readonly captureInputs: boolean;
@@ -6,6 +6,7 @@ export interface CapturePolicy {
6
6
  readonly captureToolIo: boolean;
7
7
  readonly captureSystemPrompt: boolean;
8
8
  readonly captureCwd: boolean;
9
+ readonly captureSourceMetadata: boolean;
9
10
  }
10
11
 
11
12
  export type PrivacyPreset = "metadata-only" | "prompts-only" | "conversations" | "full-debug";
@@ -36,6 +37,7 @@ const PRESETS: Record<PrivacyPreset, CapturePolicy> = {
36
37
  captureToolIo: false,
37
38
  captureSystemPrompt: false,
38
39
  captureCwd: false,
40
+ captureSourceMetadata: false,
39
41
  },
40
42
  "prompts-only": {
41
43
  captureInputs: true,
@@ -43,6 +45,7 @@ const PRESETS: Record<PrivacyPreset, CapturePolicy> = {
43
45
  captureToolIo: false,
44
46
  captureSystemPrompt: false,
45
47
  captureCwd: false,
48
+ captureSourceMetadata: false,
46
49
  },
47
50
  conversations: {
48
51
  captureInputs: true,
@@ -50,6 +53,7 @@ const PRESETS: Record<PrivacyPreset, CapturePolicy> = {
50
53
  captureToolIo: false,
51
54
  captureSystemPrompt: false,
52
55
  captureCwd: false,
56
+ captureSourceMetadata: false,
53
57
  },
54
58
  "full-debug": {
55
59
  captureInputs: true,
@@ -57,6 +61,7 @@ const PRESETS: Record<PrivacyPreset, CapturePolicy> = {
57
61
  captureToolIo: true,
58
62
  captureSystemPrompt: true,
59
63
  captureCwd: true,
64
+ captureSourceMetadata: false,
60
65
  },
61
66
  };
62
67
 
@@ -66,6 +71,7 @@ const FLAG_TO_FIELD = {
66
71
  LANGFUSE_CAPTURE_TOOL_IO: "captureToolIo",
67
72
  LANGFUSE_CAPTURE_SYSTEM_PROMPT: "captureSystemPrompt",
68
73
  LANGFUSE_CAPTURE_CWD: "captureCwd",
74
+ LANGFUSE_CAPTURE_SOURCE_METADATA: "captureSourceMetadata",
69
75
  } as const;
70
76
 
71
77
  function parseFlag(value: string | undefined): boolean | undefined {
@@ -105,7 +111,13 @@ function redactMetadata(metadata: Record<string, unknown> | undefined, policy: C
105
111
 
106
112
  const output: Record<string, unknown> = {};
107
113
  for (const [key, value] of Object.entries(metadata)) {
108
- if (key === "cwd" && !policy.captureCwd) {
114
+ if (key === "cwd") {
115
+ if (!policy.captureCwd) {
116
+ continue;
117
+ }
118
+ output[key] = typeof value === "string" && /^(?:\/|[A-Za-z]:[\\/]|\\\\)/.test(value)
119
+ ? hashPath(value)
120
+ : redactValue(value);
109
121
  continue;
110
122
  }
111
123
  output[key] = redactValue(value);
package/src/commands.ts CHANGED
@@ -75,7 +75,7 @@ function isPrivacyPreset(value: string | undefined): value is PrivacyPreset {
75
75
  }
76
76
 
77
77
  function inferPreset(policy: CapturePolicy): PrivacyPreset | "custom" {
78
- const entries: Array<[PrivacyPreset, CapturePolicy]> = [
78
+ const entries: Array<[PrivacyPreset, Omit<CapturePolicy, "captureSourceMetadata">]> = [
79
79
  [
80
80
  "metadata-only",
81
81
  {
@@ -139,6 +139,7 @@ function describePolicy(policy: CapturePolicy) {
139
139
  `captureToolIo: ${policy.captureToolIo}`,
140
140
  `captureSystemPrompt: ${policy.captureSystemPrompt}`,
141
141
  `captureCwd: ${policy.captureCwd}`,
142
+ `captureSourceMetadata: ${policy.captureSourceMetadata}`,
142
143
  ].join("\n");
143
144
  }
144
145
 
@@ -59,7 +59,8 @@ export async function startAgentRun(event: Record<string, unknown>, ctx: any) {
59
59
  images: event.images,
60
60
  context: event.context ?? event.attachments,
61
61
  });
62
- const sourceMetadata = collectSourceMetadata(cwd);
62
+ const capturePolicy = getCapturePolicy();
63
+ const sourceMetadata = collectSourceMetadata(cwd, capturePolicy.captureSourceMetadata);
63
64
  const captured = applyCapturePolicy(
64
65
  {
65
66
  input: rawPromptInput,
@@ -71,7 +72,7 @@ export async function startAgentRun(event: Record<string, unknown>, ctx: any) {
71
72
  sessionId: state.currentSessionId || undefined,
72
73
  },
73
74
  },
74
- getCapturePolicy(),
75
+ capturePolicy,
75
76
  );
76
77
 
77
78
  state.agentState = {
package/src/langfuse.ts CHANGED
@@ -660,6 +660,7 @@ export async function getRuntime(): Promise<LangfuseRuntime> {
660
660
  if (!runtime) {
661
661
  const [
662
662
  { BasicTracerProvider },
663
+ { resources },
663
664
  { context },
664
665
  { AsyncHooksContextManager },
665
666
  { LangfuseSpanProcessor },
@@ -667,6 +668,7 @@ export async function getRuntime(): Promise<LangfuseRuntime> {
667
668
  { LangfuseClient },
668
669
  ] = await Promise.all([
669
670
  import("@opentelemetry/sdk-trace-base"),
671
+ import("@opentelemetry/sdk-node"),
670
672
  import("@opentelemetry/api"),
671
673
  import("@opentelemetry/context-async-hooks"),
672
674
  import("@langfuse/otel"),
@@ -692,7 +694,10 @@ export async function getRuntime(): Promise<LangfuseRuntime> {
692
694
  secretKey: state.config.secretKey,
693
695
  baseUrl: state.config.host,
694
696
  });
695
- const tracerProvider = new BasicTracerProvider({ spanProcessors: [spanProcessor] });
697
+ const resource = resources.defaultResource().merge(
698
+ resources.detectResources({ detectors: [resources.envDetector] }),
699
+ );
700
+ const tracerProvider = new BasicTracerProvider({ resource, spanProcessors: [spanProcessor] });
696
701
  tracing.setLangfuseTracerProvider(tracerProvider);
697
702
 
698
703
  runtime = {
@@ -1,160 +1,73 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { basename, dirname, join } from "node:path";
3
1
  import { execFileSync } from "node:child_process";
4
2
 
5
3
  export type SourceMetadata = Record<string, string>;
6
4
 
7
- const OVERRIDE_KEYS = new Set([
8
- "repo_identity",
9
- "repo_owner",
10
- "repo_name",
11
- "source_type",
12
- "service_name",
13
- "project_slug",
14
- "environment",
15
- "observability_owner",
16
- ]);
5
+ type GitResult =
6
+ | { status: "ok"; value: string }
7
+ | { status: "failed" }
8
+ | { status: "unavailable" };
17
9
 
18
- function nonGitMetadata(): SourceMetadata {
10
+ function sourceStatus(status: "disabled" | "non-git" | "unavailable"): SourceMetadata {
19
11
  return {
20
- source_type: "non-git",
21
- metadata_source: "non-git",
12
+ source_type: status,
13
+ metadata_source: status,
22
14
  };
23
15
  }
24
16
 
25
- function runGit(cwd: string, args: string[]): string {
26
- return execFileSync("git", args, {
27
- cwd,
28
- encoding: "utf8",
29
- stdio: ["ignore", "pipe", "ignore"],
30
- timeout: 2000,
31
- }).trim();
32
- }
33
-
34
- function firstLine(value: string): string {
35
- return value.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? "";
36
- }
37
-
38
- export function sanitizeGitRemote(remoteUrl: string): Partial<Pick<SourceMetadata, "git_remote_host" | "git_remote_path">> {
39
- const raw = firstLine(remoteUrl).replace(/\.git$/i, "");
40
- if (!raw) {
41
- return {};
42
- }
43
-
17
+ function runGit(cwd: string, args: string[], env: NodeJS.ProcessEnv): GitResult {
44
18
  try {
45
- const parsed = new URL(raw);
46
- const path = parsed.pathname.replace(/^\/+/, "").replace(/\.git$/i, "");
47
- return parsed.hostname && path ? { git_remote_host: parsed.hostname, git_remote_path: path } : {};
48
- } catch {
49
- // Continue with scp-like SSH syntax, for example git@github.com:owner/repo.git.
50
- }
51
-
52
- const scpLike = raw.match(/^(?:[^@\s/:]+@)?([^:\s]+):(.+)$/);
53
- if (scpLike) {
54
- const host = scpLike[1];
55
- const path = scpLike[2].replace(/^\/+/, "").replace(/\.git$/i, "");
56
- return host && path && !path.includes("@") ? { git_remote_host: host, git_remote_path: path } : {};
57
- }
58
-
59
- return {};
60
- }
61
-
62
- function deriveIdentity(remotePath: string | undefined): Partial<Pick<SourceMetadata, "repo_identity" | "repo_owner" | "repo_name">> {
63
- if (!remotePath) {
64
- return {};
65
- }
66
- const parts = remotePath.split("/").filter(Boolean);
67
- if (parts.length < 2) {
68
- return {};
69
- }
70
- const repoName = parts[parts.length - 1];
71
- const owner = parts[parts.length - 2];
72
- if (!repoName || !owner || repoName.includes("/")) {
73
- return {};
19
+ return {
20
+ status: "ok",
21
+ value: execFileSync("git", args, {
22
+ cwd,
23
+ encoding: "utf8",
24
+ env,
25
+ stdio: ["ignore", "pipe", "ignore"],
26
+ timeout: 2000,
27
+ }).trim(),
28
+ };
29
+ } catch (error) {
30
+ const failure = error as NodeJS.ErrnoException & { status?: number | null };
31
+ return typeof failure.status === "number"
32
+ ? { status: "failed" }
33
+ : { status: "unavailable" };
74
34
  }
75
- return {
76
- repo_identity: `${owner}/${repoName}`,
77
- repo_owner: owner,
78
- repo_name: repoName,
79
- };
80
35
  }
81
36
 
82
- function findRepoMetadataFile(cwd: string, gitRoot: string): string | undefined {
83
- let current = cwd;
84
- const root = gitRoot;
85
- while (true) {
86
- const candidate = join(current, ".pi-langfuse.metadata.json");
87
- if (existsSync(candidate)) {
88
- return candidate;
89
- }
90
- if (current === root) {
91
- break;
92
- }
93
- const parent = dirname(current);
94
- if (parent === current) {
95
- break;
96
- }
97
- current = parent;
37
+ export function collectSourceMetadata(
38
+ cwd: string,
39
+ enabled: boolean,
40
+ env: NodeJS.ProcessEnv = process.env,
41
+ ): SourceMetadata {
42
+ if (!enabled) {
43
+ return sourceStatus("disabled");
98
44
  }
99
- return undefined;
100
- }
101
45
 
102
- function readWhitelistedOverrides(cwd: string, gitRoot: string): SourceMetadata {
103
- const path = findRepoMetadataFile(cwd, gitRoot);
104
- if (!path) {
105
- return {};
46
+ const inside = runGit(cwd, ["rev-parse", "--is-inside-work-tree"], env);
47
+ if (inside.status === "unavailable") {
48
+ return sourceStatus("unavailable");
106
49
  }
107
- try {
108
- const parsed = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
109
- const output: SourceMetadata = {};
110
- for (const [key, value] of Object.entries(parsed)) {
111
- if (OVERRIDE_KEYS.has(key) && typeof value === "string" && value.trim()) {
112
- output[key] = value.trim();
113
- }
114
- }
115
- if (output.repo_name?.includes("/")) {
116
- delete output.repo_name;
117
- }
118
- return output;
119
- } catch {
120
- return {};
50
+ if (inside.status !== "ok" || inside.value !== "true") {
51
+ return sourceStatus("non-git");
121
52
  }
122
- }
123
-
124
- export function collectSourceMetadata(cwd: string): SourceMetadata {
125
- try {
126
- const inside = runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
127
- if (inside !== "true") {
128
- return nonGitMetadata();
129
- }
130
-
131
- const gitRoot = runGit(cwd, ["rev-parse", "--show-toplevel"]);
132
- const commit = runGit(cwd, ["rev-parse", "HEAD"]);
133
- const branch = runGit(cwd, ["branch", "--show-current"]);
134
- const remote = runGit(cwd, ["config", "--get", "remote.origin.url"]);
135
- const remoteMetadata = sanitizeGitRemote(remote);
136
- const derivedIdentity = deriveIdentity(remoteMetadata.git_remote_path);
137
- const overrides = readWhitelistedOverrides(cwd, gitRoot);
138
-
139
- const metadata: SourceMetadata = {
140
- source_type: "git-repo",
141
- repo_root_name: basename(gitRoot),
142
- ...(branch ? { git_branch: branch } : {}),
143
- git_commit: commit,
144
- ...remoteMetadata,
145
- ...derivedIdentity,
146
- ...overrides,
147
- metadata_source: Object.keys(overrides).length > 0 ? "repo-file" : "git-detection",
148
- };
149
53
 
150
- if (metadata.repo_name?.includes("/")) {
151
- delete metadata.repo_name;
152
- }
153
- if (!metadata.repo_identity && metadata.repo_owner && metadata.repo_name) {
154
- metadata.repo_identity = `${metadata.repo_owner}/${metadata.repo_name}`;
155
- }
156
- return metadata;
157
- } catch {
158
- return nonGitMetadata();
54
+ const revision = runGit(cwd, ["rev-parse", "--verify", "HEAD"], env);
55
+ const ref = runGit(cwd, ["symbolic-ref", "--quiet", "--short", "HEAD"], env);
56
+ const workingTree = runGit(cwd, ["status", "--porcelain=v1", "--untracked-files=normal"], env);
57
+ if (
58
+ revision.status !== "ok" ||
59
+ !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(revision.value) ||
60
+ workingTree.status !== "ok" ||
61
+ ref.status === "unavailable"
62
+ ) {
63
+ return sourceStatus("unavailable");
159
64
  }
65
+
66
+ return {
67
+ source_type: "git-repo",
68
+ "vcs.ref.head.revision": revision.value,
69
+ git_detached: String(ref.status !== "ok"),
70
+ git_dirty: String(Boolean(workingTree.value)),
71
+ metadata_source: "git-detection",
72
+ };
160
73
  }
@@ -1,6 +1,6 @@
1
1
  declare module "@opentelemetry/sdk-trace-base" {
2
2
  export class BasicTracerProvider {
3
- constructor(options?: { spanProcessors?: unknown[] });
3
+ constructor(options?: { resource?: unknown; spanProcessors?: unknown[] });
4
4
  forceFlush?(): Promise<void>;
5
5
  shutdown?(): Promise<void>;
6
6
  }