@sovara/runner 0.3.3 → 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 (103) hide show
  1. package/README.md +30 -35
  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/claudeAgentSdk/wrapper.d.ts.map +1 -1
  19. package/dist/claudeAgentSdk/wrapper.js +8 -20
  20. package/dist/claudeAgentSdk/wrapper.js.map +1 -1
  21. package/dist/codeVersion.d.ts +0 -2
  22. package/dist/codeVersion.d.ts.map +1 -1
  23. package/dist/codeVersion.js +0 -18
  24. package/dist/codeVersion.js.map +1 -1
  25. package/dist/constants.d.ts +0 -1
  26. package/dist/constants.d.ts.map +1 -1
  27. package/dist/constants.js +3 -4
  28. package/dist/constants.js.map +1 -1
  29. package/dist/context.d.ts +24 -15
  30. package/dist/context.d.ts.map +1 -1
  31. package/dist/context.js +231 -62
  32. package/dist/context.js.map +1 -1
  33. package/dist/env.d.ts +1 -2
  34. package/dist/env.d.ts.map +1 -1
  35. package/dist/env.js +2 -75
  36. package/dist/env.js.map +1 -1
  37. package/dist/failsafe.d.ts +8 -0
  38. package/dist/failsafe.d.ts.map +1 -0
  39. package/dist/failsafe.js +28 -0
  40. package/dist/failsafe.js.map +1 -0
  41. package/dist/index.d.ts +3 -5
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +11 -6
  44. package/dist/index.js.map +1 -1
  45. package/dist/lessons.d.ts +6 -0
  46. package/dist/lessons.d.ts.map +1 -0
  47. package/dist/lessons.js +68 -0
  48. package/dist/lessons.js.map +1 -0
  49. package/dist/lessonsBlockGuidance.json +3 -0
  50. package/dist/mcpPatch.d.ts.map +1 -1
  51. package/dist/mcpPatch.js +17 -13
  52. package/dist/mcpPatch.js.map +1 -1
  53. package/dist/payload/editIoExcludePatterns.json +3 -0
  54. package/dist/platformSession.d.ts +3 -0
  55. package/dist/platformSession.d.ts.map +1 -0
  56. package/dist/platformSession.js +15 -0
  57. package/dist/platformSession.js.map +1 -0
  58. package/dist/runtimeClient.d.ts +46 -36
  59. package/dist/runtimeClient.d.ts.map +1 -1
  60. package/dist/runtimeClient.js +106 -90
  61. package/dist/runtimeClient.js.map +1 -1
  62. package/dist/serverLifecycle.d.ts +11 -1
  63. package/dist/serverLifecycle.d.ts.map +1 -1
  64. package/dist/serverLifecycle.js +54 -14
  65. package/dist/serverLifecycle.js.map +1 -1
  66. package/dist/toolTraceUtils.d.ts.map +1 -1
  67. package/dist/toolTraceUtils.js +6 -4
  68. package/dist/toolTraceUtils.js.map +1 -1
  69. package/dist/transport/awsSdkHttp2Patch.d.ts.map +1 -1
  70. package/dist/transport/awsSdkHttp2Patch.js +34 -25
  71. package/dist/transport/awsSdkHttp2Patch.js.map +1 -1
  72. package/dist/transport/fetchPatch.d.ts.map +1 -1
  73. package/dist/transport/fetchPatch.js +33 -17
  74. package/dist/transport/fetchPatch.js.map +1 -1
  75. package/dist/transport/nodeHttpPatch.d.ts.map +1 -1
  76. package/dist/transport/nodeHttpPatch.js +19 -12
  77. package/dist/transport/nodeHttpPatch.js.map +1 -1
  78. package/package.json +5 -4
  79. package/dist/annotation.d.ts +0 -3
  80. package/dist/annotation.d.ts.map +0 -1
  81. package/dist/annotation.js +0 -27
  82. package/dist/annotation.js.map +0 -1
  83. package/dist/claudeAgentSdk/parseHook.d.ts +0 -20
  84. package/dist/claudeAgentSdk/parseHook.d.ts.map +0 -1
  85. package/dist/claudeAgentSdk/parseHook.js +0 -160
  86. package/dist/claudeAgentSdk/parseHook.js.map +0 -1
  87. package/dist/priors.d.ts +0 -7
  88. package/dist/priors.d.ts.map +0 -1
  89. package/dist/priors.js +0 -87
  90. package/dist/priors.js.map +0 -1
  91. package/dist/priorsBlockGuidance.json +0 -3
  92. package/dist/projectContext.d.ts +0 -14
  93. package/dist/projectContext.d.ts.map +0 -1
  94. package/dist/projectContext.js +0 -32
  95. package/dist/projectContext.js.map +0 -1
  96. package/dist/sseListener.d.ts +0 -9
  97. package/dist/sseListener.d.ts.map +0 -1
  98. package/dist/sseListener.js +0 -56
  99. package/dist/sseListener.js.map +0 -1
  100. package/dist/user.d.ts +0 -11
  101. package/dist/user.d.ts.map +0 -1
  102. package/dist/user.js +0 -31
  103. package/dist/user.js.map +0 -1
package/README.md CHANGED
@@ -1,18 +1,20 @@
1
1
  # @sovara/runner
2
2
 
3
- TypeScript runner for [Sovara](https://sovaralabs.github.io/sovara/) — wrap your script in a
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
+ Documentation: https://docs.sovara-labs.com
12
+
11
13
  ## Requirements
12
14
 
13
15
  - Node 18 or newer (built-in `fetch`).
14
- - A reachable Sovara backend. For local use, open the Sovara desktop app;
15
- for enterprise use, point `SOVARA_SERVER_URL` at the remote endpoint.
16
+ - A reachable Sovara backend. For local use, open the Sovara desktop app.
17
+ Enterprise agent projects should use their project-specific connection flow.
16
18
  - A Sovara user and project, configured once with `sovara init`.
17
19
 
18
20
  ## Install
@@ -66,7 +68,7 @@ Importing `@sovara/runner` patches two transports at module load:
66
68
  Inside `withSovaraRun(...)`, calls to whitelisted endpoints are observed:
67
69
  the runner builds a request envelope, posts it to the Sovara server's
68
70
  `/internal/runner/llm/prepare` endpoint (which optionally applies
69
- runtime preparation such as priors injection), executes the prepared
71
+ runtime preparation such as lessons injection), executes the prepared
70
72
  request on the wire, and records the response or exception. Outside the
71
73
  run scope, both patches are no-ops.
72
74
 
@@ -93,9 +95,9 @@ completes.
93
95
  ```ts
94
96
  export type WithSovaraRunOptions = {
95
97
  name: string; // required run name
96
- url?: string; // server URL (default: SOVARA_SERVER_URL or http://127.0.0.1:5959)
98
+ clientRunId?: string; // optional project-scoped correlation id
99
+ url?: string; // server URL (default: http://127.0.0.1:5959)
97
100
  captureLogs?: boolean; // default: true
98
- user?: { fullName?: string; email?: string }; // overrides only
99
101
  project?: { name?: string; description?: string };
100
102
  };
101
103
 
@@ -115,14 +117,13 @@ whatever `fn` returns.
115
117
  user setup is missing.
116
118
  3. Resolves the project for `process.cwd()`. Fails hard with a `sovara init`
117
119
  hint if the cwd isn't part of a known Sovara project.
118
- 4. Registers the run, opens an SSE shutdown listener, runs `fn` inside
119
- an `AsyncLocalStorage` scope, and deregisters in `finally`.
120
+ 4. Registers the run, runs `fn` inside an `AsyncLocalStorage` scope, and
121
+ deregisters in `finally`.
120
122
 
121
- **Nested call** (called from inside another `withSovaraRun`):
123
+ **Nested top-level call** (called from inside another `withSovaraRun`):
122
124
 
123
- The nested call is registered as a subrun via `/runner/subrun`, inheriting
124
- the parent's `client` and `url` settings. It deregisters its own subrun
125
- 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.
126
127
 
127
128
  `fn` exceptions propagate untouched. The run is deregistered in either
128
129
  case.
@@ -131,12 +132,12 @@ case.
131
132
 
132
133
  ```ts
133
134
  await withSovaraRun("pipeline", async () => {
134
- const docs = await withSovaraRun("retrieve", async () => loadDocs());
135
- await withSovaraRun("summarize", async () => summarize(docs));
135
+ const docs = await withSovaraSubrun("retrieve", async () => loadDocs());
136
+ await withSovaraSubrun("summarize", async () => summarize(docs));
136
137
  });
137
138
  ```
138
139
 
139
- Every nested `withSovaraRun` becomes a subrun under the active scope.
140
+ Every explicit `withSovaraSubrun` becomes a child run under the active scope.
140
141
 
141
142
  ### Logs
142
143
 
@@ -171,8 +172,8 @@ the `sovara init` requirement — they just refine the values.
171
172
  ### `trace(fn, options?)`
172
173
 
173
174
  Use `trace` when an important custom function or tool does not make an LLM
174
- HTTP call on its own, but should still appear as a tool node in the Sovara
175
- run graph.
175
+ HTTP call on its own, but should still appear as a tool step in the Sovara
176
+ run steps.
176
177
 
177
178
  ```ts
178
179
  import { trace, withSovaraRun } from "@sovara/runner";
@@ -190,15 +191,15 @@ await withSovaraRun("support-agent", async () => {
190
191
  });
191
192
  ```
192
193
 
193
- `trace` records function arguments as the node input and the return value as
194
- 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
195
196
  are recorded and the original exception is re-thrown. Outside an active
196
197
  `withSovaraRun(...)` scope, the wrapper calls the function normally without
197
198
  recording anything.
198
199
 
199
200
  ```ts
200
201
  export type TraceOptions = {
201
- 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
202
203
  meta?: Record<string, unknown>; // optional metadata stored with input/output
203
204
  };
204
205
  ```
@@ -233,13 +234,13 @@ That single import change is the entire integration. The wrapper:
233
234
 
234
235
  - Routes the spawned Claude CLI subprocess through Sovara's local Claude
235
236
  proxy by setting `ANTHROPIC_BASE_URL` in the subprocess env, so every
236
- LLM request the SDK makes shows up in your run graph.
237
+ LLM request the SDK makes shows up as a run step.
237
238
  - Registers a unique proxy client with the server before each `query()` /
238
239
  `startup()` so concurrent SDK calls don't collide.
239
240
  - Sets `CLAUDE_CODE_ENTRYPOINT=sdk-ts` and `SOVARA_CLAUDE_PROXY_ACTIVE=1`
240
241
  to mark the subprocess as running under Sovara.
241
242
  - Wraps the returned `AsyncIterable` so each yielded message is parsed
242
- 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
243
244
  run.
244
245
  - Throws if `options.transport` (custom transport) is supplied — this
245
246
  matches the Python runner's behavior.
@@ -284,30 +285,24 @@ for await (const message of handle.query("What did I install last?")) {
284
285
 
285
286
  | Variable | Purpose | Default |
286
287
  | ---------------------------- | -------------------------------------------------------------------------- | ----------------------- |
287
- | `SOVARA_SERVER_URL` | Override the Sovara server URL | `http://127.0.0.1:5959` |
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
- Either pass `url` per call:
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);
298
298
  ```
299
299
 
300
- Or set `SOVARA_SERVER_URL` once for the process.
301
-
302
300
  ## Error messages
303
301
 
304
302
  The runner fails fast for misconfiguration. Common ones:
305
303
 
306
- - **`Sovara backend is not reachable at <url>. Open the Sovara desktop app or configure SOVARA_SERVER_URL to a reachable enterprise Sovara backend endpoint.`**
307
- Open the desktop app for local runs, or point `SOVARA_SERVER_URL` at the enterprise backend.
308
-
309
- - **``Sovara user setup is missing. Run `sovara init --user-name <full-name> --user-email <email>` and retry.``**
310
- Run the suggested `sovara init` command.
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.
311
306
 
312
307
  - **``Sovara project setup is missing for <cwd>. Run `sovara init --project-root "<cwd>" --project-name <project-name>` and retry.``**
313
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":"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"}
@@ -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"}