@sovara/runner 0.3.4 → 0.4.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.
Files changed (92) hide show
  1. package/README.md +24 -24
  2. package/dist/agentCredentials.d.ts +13 -0
  3. package/dist/agentCredentials.d.ts.map +1 -0
  4. package/dist/agentCredentials.js +90 -0
  5. package/dist/agentCredentials.js.map +1 -0
  6. package/dist/capture/responseEnvelope.d.ts +6 -1
  7. package/dist/capture/responseEnvelope.d.ts.map +1 -1
  8. package/dist/capture/responseEnvelope.js +24 -3
  9. package/dist/capture/responseEnvelope.js.map +1 -1
  10. package/dist/capture/sensitiveHeaders.d.ts +7 -0
  11. package/dist/capture/sensitiveHeaders.d.ts.map +1 -0
  12. package/dist/capture/sensitiveHeaders.js +41 -0
  13. package/dist/capture/sensitiveHeaders.js.map +1 -0
  14. package/dist/capture/streamNormalizers.d.ts +6 -0
  15. package/dist/capture/streamNormalizers.d.ts.map +1 -0
  16. package/dist/capture/streamNormalizers.js +301 -0
  17. package/dist/capture/streamNormalizers.js.map +1 -0
  18. package/dist/codeVersion.d.ts +0 -2
  19. package/dist/codeVersion.d.ts.map +1 -1
  20. package/dist/codeVersion.js +0 -18
  21. package/dist/codeVersion.js.map +1 -1
  22. package/dist/constants.d.ts +0 -1
  23. package/dist/constants.d.ts.map +1 -1
  24. package/dist/constants.js +3 -4
  25. package/dist/constants.js.map +1 -1
  26. package/dist/context.d.ts +23 -10
  27. package/dist/context.d.ts.map +1 -1
  28. package/dist/context.js +230 -56
  29. package/dist/context.js.map +1 -1
  30. package/dist/env.d.ts +0 -1
  31. package/dist/env.d.ts.map +1 -1
  32. package/dist/env.js +0 -2
  33. package/dist/env.js.map +1 -1
  34. package/dist/failsafe.d.ts +8 -0
  35. package/dist/failsafe.d.ts.map +1 -0
  36. package/dist/failsafe.js +28 -0
  37. package/dist/failsafe.js.map +1 -0
  38. package/dist/index.d.ts +3 -5
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +11 -6
  41. package/dist/index.js.map +1 -1
  42. package/dist/lessons.d.ts +6 -0
  43. package/dist/lessons.d.ts.map +1 -0
  44. package/dist/lessons.js +68 -0
  45. package/dist/lessons.js.map +1 -0
  46. package/dist/lessonsBlockGuidance.json +3 -0
  47. package/dist/mcpPatch.d.ts.map +1 -1
  48. package/dist/mcpPatch.js +17 -13
  49. package/dist/mcpPatch.js.map +1 -1
  50. package/dist/payload/editIoExcludePatterns.json +3 -0
  51. package/dist/platformSession.d.ts +3 -0
  52. package/dist/platformSession.d.ts.map +1 -0
  53. package/dist/platformSession.js +15 -0
  54. package/dist/platformSession.js.map +1 -0
  55. package/dist/runtimeClient.d.ts +46 -30
  56. package/dist/runtimeClient.d.ts.map +1 -1
  57. package/dist/runtimeClient.js +105 -68
  58. package/dist/runtimeClient.js.map +1 -1
  59. package/dist/serverLifecycle.d.ts +11 -1
  60. package/dist/serverLifecycle.d.ts.map +1 -1
  61. package/dist/serverLifecycle.js +54 -14
  62. package/dist/serverLifecycle.js.map +1 -1
  63. package/dist/toolTraceUtils.d.ts.map +1 -1
  64. package/dist/toolTraceUtils.js +6 -4
  65. package/dist/toolTraceUtils.js.map +1 -1
  66. package/dist/transport/awsSdkHttp2Patch.d.ts.map +1 -1
  67. package/dist/transport/awsSdkHttp2Patch.js +34 -25
  68. package/dist/transport/awsSdkHttp2Patch.js.map +1 -1
  69. package/dist/transport/fetchPatch.d.ts.map +1 -1
  70. package/dist/transport/fetchPatch.js +33 -17
  71. package/dist/transport/fetchPatch.js.map +1 -1
  72. package/dist/transport/nodeHttpPatch.d.ts.map +1 -1
  73. package/dist/transport/nodeHttpPatch.js +19 -12
  74. package/dist/transport/nodeHttpPatch.js.map +1 -1
  75. package/package.json +2 -2
  76. package/dist/annotation.d.ts +0 -3
  77. package/dist/annotation.d.ts.map +0 -1
  78. package/dist/annotation.js +0 -27
  79. package/dist/annotation.js.map +0 -1
  80. package/dist/priors.d.ts +0 -7
  81. package/dist/priors.d.ts.map +0 -1
  82. package/dist/priors.js +0 -87
  83. package/dist/priors.js.map +0 -1
  84. package/dist/priorsBlockGuidance.json +0 -3
  85. package/dist/projectContext.d.ts +0 -14
  86. package/dist/projectContext.d.ts.map +0 -1
  87. package/dist/projectContext.js +0 -32
  88. package/dist/projectContext.js.map +0 -1
  89. package/dist/sseListener.d.ts +0 -9
  90. package/dist/sseListener.d.ts.map +0 -1
  91. package/dist/sseListener.js +0 -56
  92. package/dist/sseListener.js.map +0 -1
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  TypeScript runner for [Sovara](https://docs.sovara-labs.com) — wrap your script in a
4
4
  single function and Sovara records every LLM call, tool invocation, and log
5
- line into a structured run graph.
5
+ line into structured run steps.
6
6
 
7
7
  The runner is the TS counterpart to the Python `sovara` runner. It targets
8
- the same Sovara server, produces the same node shapes, and is safe to mix
8
+ the same Sovara server, produces the same step shapes, and is safe to mix
9
9
  with Python runs in the same project.
10
10
 
11
11
  Documentation: https://docs.sovara-labs.com
@@ -68,7 +68,7 @@ Importing `@sovara/runner` patches two transports at module load:
68
68
  Inside `withSovaraRun(...)`, calls to whitelisted endpoints are observed:
69
69
  the runner builds a request envelope, posts it to the Sovara server's
70
70
  `/internal/runner/llm/prepare` endpoint (which optionally applies
71
- runtime preparation such as priors injection), executes the prepared
71
+ runtime preparation such as lessons injection), executes the prepared
72
72
  request on the wire, and records the response or exception. Outside the
73
73
  run scope, both patches are no-ops.
74
74
 
@@ -95,6 +95,7 @@ completes.
95
95
  ```ts
96
96
  export type WithSovaraRunOptions = {
97
97
  name: string; // required run name
98
+ clientRunId?: string; // optional project-scoped correlation id
98
99
  url?: string; // server URL (default: http://127.0.0.1:5959)
99
100
  captureLogs?: boolean; // default: true
100
101
  project?: { name?: string; description?: string };
@@ -116,14 +117,13 @@ whatever `fn` returns.
116
117
  user setup is missing.
117
118
  3. Resolves the project for `process.cwd()`. Fails hard with a `sovara init`
118
119
  hint if the cwd isn't part of a known Sovara project.
119
- 4. Registers the run, opens an SSE shutdown listener, runs `fn` inside
120
- an `AsyncLocalStorage` scope, and deregisters in `finally`.
120
+ 4. Registers the run, runs `fn` inside an `AsyncLocalStorage` scope, and
121
+ deregisters in `finally`.
121
122
 
122
- **Nested call** (called from inside another `withSovaraRun`):
123
+ **Nested top-level call** (called from inside another `withSovaraRun`):
123
124
 
124
- The nested call is registered as a subrun via `/runner/subrun`, inheriting
125
- the parent's `client` and `url` settings. It deregisters its own subrun
126
- when `fn` returns.
125
+ The nested `withSovaraRun` call is ignored with a warning and `fn` runs inside
126
+ the existing active scope. Use `withSovaraSubrun(...)` for child execution.
127
127
 
128
128
  `fn` exceptions propagate untouched. The run is deregistered in either
129
129
  case.
@@ -132,12 +132,12 @@ case.
132
132
 
133
133
  ```ts
134
134
  await withSovaraRun("pipeline", async () => {
135
- const docs = await withSovaraRun("retrieve", async () => loadDocs());
136
- await withSovaraRun("summarize", async () => summarize(docs));
135
+ const docs = await withSovaraSubrun("retrieve", async () => loadDocs());
136
+ await withSovaraSubrun("summarize", async () => summarize(docs));
137
137
  });
138
138
  ```
139
139
 
140
- Every nested `withSovaraRun` becomes a subrun under the active scope.
140
+ Every explicit `withSovaraSubrun` becomes a child run under the active scope.
141
141
 
142
142
  ### Logs
143
143
 
@@ -172,8 +172,8 @@ the `sovara init` requirement — they just refine the values.
172
172
  ### `trace(fn, options?)`
173
173
 
174
174
  Use `trace` when an important custom function or tool does not make an LLM
175
- HTTP call on its own, but should still appear as a tool node in the Sovara
176
- run graph.
175
+ HTTP call on its own, but should still appear as a tool step in the Sovara
176
+ run steps.
177
177
 
178
178
  ```ts
179
179
  import { trace, withSovaraRun } from "@sovara/runner";
@@ -191,15 +191,15 @@ await withSovaraRun("support-agent", async () => {
191
191
  });
192
192
  ```
193
193
 
194
- `trace` records function arguments as the node input and the return value as
195
- the node output. If the wrapped function throws, the error type and message
194
+ `trace` records function arguments as the step input and the return value as
195
+ the step output. If the wrapped function throws, the error type and message
196
196
  are recorded and the original exception is re-thrown. Outside an active
197
197
  `withSovaraRun(...)` scope, the wrapper calls the function normally without
198
198
  recording anything.
199
199
 
200
200
  ```ts
201
201
  export type TraceOptions = {
202
- name?: string; // display name for the tool node, default: function/method name
202
+ name?: string; // display name for the tool step, default: function/method name
203
203
  meta?: Record<string, unknown>; // optional metadata stored with input/output
204
204
  };
205
205
  ```
@@ -234,13 +234,13 @@ That single import change is the entire integration. The wrapper:
234
234
 
235
235
  - Routes the spawned Claude CLI subprocess through Sovara's local Claude
236
236
  proxy by setting `ANTHROPIC_BASE_URL` in the subprocess env, so every
237
- LLM request the SDK makes shows up in your run graph.
237
+ LLM request the SDK makes shows up as a run step.
238
238
  - Registers a unique proxy client with the server before each `query()` /
239
239
  `startup()` so concurrent SDK calls don't collide.
240
240
  - Sets `CLAUDE_CODE_ENTRYPOINT=sdk-ts` and `SOVARA_CLAUDE_PROXY_ACTIVE=1`
241
241
  to mark the subprocess as running under Sovara.
242
242
  - Wraps the returned `AsyncIterable` so each yielded message is parsed
243
- for `tool_use` / `tool_result` blocks and recorded as nodes on the
243
+ for `tool_use` / `tool_result` blocks and recorded as steps on the
244
244
  run.
245
245
  - Throws if `options.transport` (custom transport) is supplied — this
246
246
  matches the Python runner's behavior.
@@ -285,13 +285,13 @@ for await (const message of handle.query("What did I install last?")) {
285
285
 
286
286
  | Variable | Purpose | Default |
287
287
  | ---------------------------- | -------------------------------------------------------------------------- | ----------------------- |
288
- | `HOST` | Server host (used to compute the default URL) | `127.0.0.1` |
289
- | `PYTHON_PORT` | Server port (used to compute the default URL) | `5959` |
288
+ | `SOVARA_EXEC_SERVER_PORT` | Exec server port (used to compute the default URL) | `5960` |
289
+ | `SOVARA_EXEC_SERVER_URL` | Explicit exec server URL | `http://127.0.0.1:5960` |
290
290
  | `SOVARA_CLAUDE_PROXY_ACTIVE` | Set automatically by `@sovara/runner/claude` to prevent nested proxy loops | unset |
291
291
 
292
292
  ### Server URL
293
293
 
294
- The TypeScript runner uses the local backend by default. Pass `url` per call only when the caller explicitly manages a remote backend connection:
294
+ The TypeScript runner uses the local exec server by default. Pass `url` per call only when the caller explicitly manages a different exec-server connection:
295
295
 
296
296
  ```ts
297
297
  await withSovaraRun({ name: "demo", url: "http://localhost:6000" }, fn);
@@ -301,8 +301,8 @@ await withSovaraRun({ name: "demo", url: "http://localhost:6000" }, fn);
301
301
 
302
302
  The runner fails fast for misconfiguration. Common ones:
303
303
 
304
- - **`Sovara backend is not reachable at <url>. Open the Sovara desktop app or pass an explicit url to withSovaraRun for a managed remote run.`**
305
- Open the desktop app for local runs, or pass `url` when the caller owns remote backend setup.
304
+ - **`Sovara exec server is not reachable at <url>. Run sovara-exec-server start or use the Sovara CLI package that bundles it.`**
305
+ Start the exec server or pass `url` when the caller owns exec-server setup.
306
306
 
307
307
  - **``Sovara project setup is missing for <cwd>. Run `sovara init --project-root "<cwd>" --project-name <project-name>` and retry.``**
308
308
  Run the suggested `sovara init` command.
@@ -0,0 +1,13 @@
1
+ export type ProjectConfig = {
2
+ projectName: string;
3
+ projectRoot: string;
4
+ };
5
+ export type AgentConnection = {
6
+ projectId: string;
7
+ serverUrl: string;
8
+ agentToken: string;
9
+ };
10
+ export declare function findProjectConfig(startPath: string): ProjectConfig | null;
11
+ export declare function resolveProjectConfig(cwd: string, explicitProjectName?: string): ProjectConfig;
12
+ export declare function loadAgentConnection(projectName: string, home?: string): AgentConnection | null;
13
+ //# sourceMappingURL=agentCredentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentCredentials.d.ts","sourceRoot":"","sources":["../src/agentCredentials.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAOF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAwBzE;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,mBAAmB,CAAC,EAAE,MAAM,GAC3B,aAAa,CAYf;AAMD,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,MAAkB,GACvB,eAAe,GAAG,IAAI,CA2CxB"}
@@ -0,0 +1,90 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ function normalizeProjectName(projectName) {
5
+ const normalized = projectName?.trim();
6
+ return normalized ? normalized : null;
7
+ }
8
+ export function findProjectConfig(startPath) {
9
+ let current = resolve(startPath);
10
+ while (true) {
11
+ const configPath = join(current, ".sovara");
12
+ try {
13
+ if (existsSync(configPath) && statSync(configPath).isFile()) {
14
+ const projectName = normalizeProjectName(readFileSync(configPath, "utf8"));
15
+ if (!projectName) {
16
+ throw new Error("Project name is required.");
17
+ }
18
+ return { projectName, projectRoot: current };
19
+ }
20
+ }
21
+ catch (error) {
22
+ if (error instanceof Error) {
23
+ throw error;
24
+ }
25
+ throw new Error("Could not read Sovara project configuration.");
26
+ }
27
+ const parent = dirname(current);
28
+ if (parent === current) {
29
+ return null;
30
+ }
31
+ current = parent;
32
+ }
33
+ }
34
+ export function resolveProjectConfig(cwd, explicitProjectName) {
35
+ const explicit = normalizeProjectName(explicitProjectName);
36
+ const local = findProjectConfig(cwd);
37
+ if (explicit) {
38
+ return { projectName: explicit, projectRoot: local?.projectRoot ?? cwd };
39
+ }
40
+ if (local) {
41
+ return local;
42
+ }
43
+ throw new Error(`Sovara project setup is missing for ${cwd}. Run \`sovara init --project-root ${JSON.stringify(cwd)} --project-name <project-name>\` and retry.`);
44
+ }
45
+ function credentialsPath(home) {
46
+ return join(home, ".sovara", "agent_connections.json");
47
+ }
48
+ export function loadAgentConnection(projectName, home = homedir()) {
49
+ const account = normalizeProjectName(projectName);
50
+ if (!account)
51
+ return null;
52
+ let raw;
53
+ try {
54
+ raw = readFileSync(credentialsPath(home), "utf8");
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ let payload;
60
+ try {
61
+ payload = JSON.parse(raw);
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ if (typeof payload !== "object" || payload === null)
67
+ return null;
68
+ const entry = payload[account];
69
+ if (typeof entry !== "object" || entry === null)
70
+ return null;
71
+ const projectId = entry.project_id;
72
+ const serverUrl = entry.server_url;
73
+ const agentToken = entry.agent_token;
74
+ if (typeof projectId !== "string" ||
75
+ typeof serverUrl !== "string" ||
76
+ typeof agentToken !== "string") {
77
+ return null;
78
+ }
79
+ const normalizedProjectId = projectId.trim();
80
+ const normalizedUrl = serverUrl.trim().replace(/\/$/, "");
81
+ const normalizedToken = agentToken.trim();
82
+ if (!normalizedProjectId || !normalizedUrl || !normalizedToken)
83
+ return null;
84
+ return {
85
+ projectId: normalizedProjectId,
86
+ serverUrl: normalizedUrl,
87
+ agentToken: normalizedToken,
88
+ };
89
+ }
90
+ //# sourceMappingURL=agentCredentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentCredentials.js","sourceRoot":"","sources":["../src/agentCredentials.ts"],"names":[],"mappings":"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"}
@@ -4,10 +4,15 @@ export type ResponseEnvelope = {
4
4
  request_url: string | null;
5
5
  headers: Record<string, string>;
6
6
  content: unknown;
7
- _response_format?: "sse";
7
+ _response_format?: "sse" | "ndjson";
8
8
  sse?: {
9
9
  events: Array<Record<string, unknown>>;
10
10
  };
11
+ ndjson?: {
12
+ events: unknown[];
13
+ };
14
+ _stream_normalization?: Record<string, string>;
15
+ error?: unknown;
11
16
  _encoding?: string;
12
17
  };
13
18
  export declare function buildResponseEnvelope(response: Response): Promise<ResponseEnvelope>;
@@ -1 +1 @@
1
- {"version":3,"file":"responseEnvelope.d.ts","sourceRoot":"","sources":["../../src/capture/responseEnvelope.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE;QAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAkBF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6BzF"}
1
+ {"version":3,"file":"responseEnvelope.d.ts","sourceRoot":"","sources":["../../src/capture/responseEnvelope.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IACpC,GAAG,CAAC,EAAE;QAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC;IACjD,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA8BF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqCzF"}
@@ -1,4 +1,6 @@
1
- import { extractSseDisplayContent, parseSseEvents } from "./sseUtils.js";
1
+ import { scrubSensitiveHeaders } from "./sensitiveHeaders.js";
2
+ import { parseSseEvents } from "./sseUtils.js";
3
+ import { normalizeNdjsonStream, normalizeSseStream, parseNdjsonEvents, } from "./streamNormalizers.js";
2
4
  function headersToObject(headers) {
3
5
  const out = {};
4
6
  for (const [key, value] of headers.entries()) {
@@ -13,8 +15,20 @@ function inferEncoding(contentType) {
13
15
  const match = /charset=([^;]+)/i.exec(contentType);
14
16
  return match ? match[1].trim() : "utf-8";
15
17
  }
18
+ function shouldParseNdjson(requestUrl) {
19
+ if (!requestUrl) {
20
+ return false;
21
+ }
22
+ try {
23
+ const path = new URL(requestUrl).pathname;
24
+ return path.endsWith("/api/chat") || path.endsWith("/api/generate");
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
16
30
  export async function buildResponseEnvelope(response) {
17
- const headers = headersToObject(response.headers);
31
+ const headers = scrubSensitiveHeaders(headersToObject(response.headers));
18
32
  const contentType = response.headers.get("content-type");
19
33
  const text = await response.text();
20
34
  const envelope = {
@@ -28,8 +42,15 @@ export async function buildResponseEnvelope(response) {
28
42
  const parsedSse = parseSseEvents(text);
29
43
  if (parsedSse !== null) {
30
44
  envelope._response_format = "sse";
31
- envelope.content = extractSseDisplayContent(parsedSse);
32
45
  envelope.sse = { events: parsedSse };
46
+ Object.assign(envelope, normalizeSseStream(parsedSse, envelope.request_url));
47
+ return envelope;
48
+ }
49
+ const parsedNdjson = shouldParseNdjson(envelope.request_url) ? parseNdjsonEvents(text) : null;
50
+ if (parsedNdjson !== null) {
51
+ envelope._response_format = "ndjson";
52
+ envelope.ndjson = { events: parsedNdjson };
53
+ Object.assign(envelope, normalizeNdjsonStream(parsedNdjson, envelope.request_url));
33
54
  return envelope;
34
55
  }
35
56
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"responseEnvelope.js","sourceRoot":"","sources":["../../src/capture/responseEnvelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAazE,SAAS,eAAe,CAAC,OAAgB;IACvC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC7C,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,WAA0B;IAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,QAAkB;IAC5D,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEnC,MAAM,QAAQ,GAAqB;QACjC,WAAW,EAAE,QAAQ,CAAC,MAAM;QAC5B,aAAa,EAAE,QAAQ,CAAC,UAAU;QAClC,WAAW,EAAE,QAAQ,CAAC,GAAG,IAAI,IAAI;QACjC,OAAO;QACP,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC;KACtC,CAAC;IAEF,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,QAAQ,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAClC,QAAQ,CAAC,OAAO,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAC;QACvD,QAAQ,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QACrC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"responseEnvelope.js","sourceRoot":"","sources":["../../src/capture/responseEnvelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAgBhC,SAAS,eAAe,CAAC,OAAgB;IACvC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC7C,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,WAA0B;IAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;AAC3C,CAAC;AAED,SAAS,iBAAiB,CAAC,UAAyB;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC;QAC1C,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,QAAkB;IAC5D,MAAM,OAAO,GAAG,qBAAqB,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACzE,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEnC,MAAM,QAAQ,GAAqB;QACjC,WAAW,EAAE,QAAQ,CAAC,MAAM;QAC5B,aAAa,EAAE,QAAQ,CAAC,UAAU;QAClC,WAAW,EAAE,QAAQ,CAAC,GAAG,IAAI,IAAI;QACjC,OAAO;QACP,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC;KACtC,CAAC;IAEF,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,QAAQ,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAClC,QAAQ,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QAC7E,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,YAAY,GAAG,iBAAiB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9F,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1B,QAAQ,CAAC,gBAAgB,GAAG,QAAQ,CAAC;QACrC,QAAQ,CAAC,MAAM,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,qBAAqB,CAAC,YAAY,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QACnF,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare function isSensitiveHeader(name: string): boolean;
2
+ export declare function scrubSensitiveHeaders(headers: Record<string, string>): Record<string, string>;
3
+ export declare function scrubRequestEnvelopeForStorage<T extends {
4
+ headers: Record<string, string>;
5
+ }>(envelope: T): T;
6
+ export declare function extractSensitiveHeaders(headers: Record<string, string>): Record<string, string>;
7
+ //# sourceMappingURL=sensitiveHeaders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensitiveHeaders.d.ts","sourceRoot":"","sources":["../../src/capture/sensitiveHeaders.ts"],"names":[],"mappings":"AAcA,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB;AAKD,wBAAgB,8BAA8B,CAAC,CAAC,SAAS;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAC1F,QAAQ,EAAE,CAAC,GACV,CAAC,CAEH;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB"}
@@ -0,0 +1,41 @@
1
+ // Provider auth / session headers that must never be persisted to recorded
2
+ // trace data. They are stripped from stored request/response envelopes; replay
3
+ // re-supplies auth from the user's configured API key, so dropping them here is
4
+ // safe. Live outbound requests keep their headers (see fetch patch re-merge).
5
+ const SENSITIVE_HEADER_NAMES = new Set([
6
+ "authorization",
7
+ "proxy-authorization",
8
+ "x-api-key",
9
+ "api-key",
10
+ "x-goog-api-key",
11
+ "cookie",
12
+ "set-cookie",
13
+ ]);
14
+ export function isSensitiveHeader(name) {
15
+ return SENSITIVE_HEADER_NAMES.has(name.toLowerCase());
16
+ }
17
+ export function scrubSensitiveHeaders(headers) {
18
+ const out = {};
19
+ for (const [key, value] of Object.entries(headers)) {
20
+ if (!isSensitiveHeader(key)) {
21
+ out[key] = value;
22
+ }
23
+ }
24
+ return out;
25
+ }
26
+ // Returns a copy of a request envelope safe to persist: auth headers are
27
+ // stripped while the original (used for the live outbound request) is left
28
+ // untouched. Generic over the envelope shape to avoid an import cycle.
29
+ export function scrubRequestEnvelopeForStorage(envelope) {
30
+ return { ...envelope, headers: scrubSensitiveHeaders(envelope.headers) };
31
+ }
32
+ export function extractSensitiveHeaders(headers) {
33
+ const out = {};
34
+ for (const [key, value] of Object.entries(headers)) {
35
+ if (isSensitiveHeader(key)) {
36
+ out[key] = value;
37
+ }
38
+ }
39
+ return out;
40
+ }
41
+ //# sourceMappingURL=sensitiveHeaders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensitiveHeaders.js","sourceRoot":"","sources":["../../src/capture/sensitiveHeaders.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IAC1D,eAAe;IACf,qBAAqB;IACrB,WAAW;IACX,SAAS;IACT,gBAAgB;IAChB,QAAQ;IACR,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,OAA+B;IAE/B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,yEAAyE;AACzE,2EAA2E;AAC3E,uEAAuE;AACvE,MAAM,UAAU,8BAA8B,CAC5C,QAAW;IAEX,OAAO,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,OAA+B;IAE/B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,6 @@
1
+ type RecordValue = Record<string, unknown>;
2
+ export declare function normalizeSseStream(events: Array<RecordValue>, requestUrl: string | null): RecordValue;
3
+ export declare function parseNdjsonEvents(text: string): unknown[] | null;
4
+ export declare function normalizeNdjsonStream(events: unknown[], requestUrl: string | null): RecordValue;
5
+ export {};
6
+ //# sourceMappingURL=streamNormalizers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamNormalizers.d.ts","sourceRoot":"","sources":["../../src/capture/streamNormalizers.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAiL3C,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,EAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,WAAW,CA4Cb;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAchE;AA6CD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CAoC/F"}