@browserbasehq/orca 3.2.0-preview.2 → 3.2.0-preview.4
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/dist/cjs/lib/v3/agent/AnthropicCUAClient.js +5 -5
- package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/GoogleCUAClient.js +5 -5
- package/dist/cjs/lib/v3/agent/GoogleCUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/OpenAICUAClient.js +5 -5
- package/dist/cjs/lib/v3/agent/OpenAICUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/act.js +1 -10
- package/dist/cjs/lib/v3/agent/tools/act.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/ariaTree.js +1 -12
- package/dist/cjs/lib/v3/agent/tools/ariaTree.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/braveSearch.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/browserbaseSearch.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/click.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/clickAndHold.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/extract.js +1 -10
- package/dist/cjs/lib/v3/agent/tools/extract.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/fillFormVision.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/fillform.js +1 -10
- package/dist/cjs/lib/v3/agent/tools/fillform.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/index.d.ts +2 -2
- package/dist/cjs/lib/v3/agent/tools/index.js +53 -5
- package/dist/cjs/lib/v3/agent/tools/index.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/keys.d.ts +1 -1
- package/dist/cjs/lib/v3/agent/tools/keys.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/type.js.map +1 -1
- package/dist/cjs/lib/v3/api.js +9 -2
- package/dist/cjs/lib/v3/api.js.map +1 -1
- package/dist/cjs/lib/v3/flowlogger/EventEmitter.d.ts +7 -0
- package/dist/cjs/lib/v3/flowlogger/EventEmitter.js +30 -0
- package/dist/cjs/lib/v3/flowlogger/EventEmitter.js.map +1 -0
- package/dist/cjs/lib/v3/flowlogger/EventSink.d.ts +44 -0
- package/dist/cjs/lib/v3/flowlogger/EventSink.js +217 -0
- package/dist/cjs/lib/v3/flowlogger/EventSink.js.map +1 -0
- package/dist/cjs/lib/v3/flowlogger/EventStore.d.ts +26 -0
- package/dist/cjs/lib/v3/flowlogger/EventStore.js +135 -0
- package/dist/cjs/lib/v3/flowlogger/EventStore.js.map +1 -0
- package/dist/{esm/lib/v3/flowLogger.d.ts → cjs/lib/v3/flowlogger/FlowLogger.d.ts} +32 -31
- package/dist/cjs/lib/v3/flowlogger/FlowLogger.js +591 -0
- package/dist/cjs/lib/v3/flowlogger/FlowLogger.js.map +1 -0
- package/dist/cjs/lib/v3/flowlogger/prettify.d.ts +6 -0
- package/dist/cjs/lib/v3/flowlogger/prettify.js +395 -0
- package/dist/cjs/lib/v3/flowlogger/prettify.js.map +1 -0
- package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.js +26 -28
- package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js +2 -2
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js +3 -5
- package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
- package/dist/cjs/lib/v3/llm/aisdk.js +9 -9
- package/dist/cjs/lib/v3/llm/aisdk.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/options.d.ts +2 -0
- package/dist/cjs/lib/v3/types/public/options.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/cdp.d.ts +1 -1
- package/dist/cjs/lib/v3/understudy/cdp.js +83 -43
- package/dist/cjs/lib/v3/understudy/cdp.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/page.js +18 -23
- package/dist/cjs/lib/v3/understudy/page.js.map +1 -1
- package/dist/cjs/lib/v3/v3.d.ts +5 -5
- package/dist/cjs/lib/v3/v3.js +48 -46
- package/dist/cjs/lib/v3/v3.js.map +1 -1
- package/dist/cjs/tests/integration/flowLogger.spec.d.ts +1 -0
- package/dist/cjs/tests/integration/flowLogger.spec.js +714 -0
- package/dist/cjs/tests/integration/flowLogger.spec.js.map +1 -0
- package/dist/cjs/tests/integration/testUtils.d.ts +33 -0
- package/dist/cjs/tests/integration/testUtils.js +144 -0
- package/dist/cjs/tests/integration/testUtils.js.map +1 -1
- package/dist/cjs/tests/integration/timeouts.spec.js +112 -2
- package/dist/cjs/tests/integration/timeouts.spec.js.map +1 -1
- package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.d.ts +1 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.js +95 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.js.map +1 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.d.ts +1 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.js +43 -0
- package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.js.map +1 -0
- package/dist/cjs/tests/unit/flowlogger-eventstore.test.d.ts +1 -0
- package/dist/cjs/tests/unit/flowlogger-eventstore.test.js +250 -0
- package/dist/cjs/tests/unit/flowlogger-eventstore.test.js.map +1 -0
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.js +1 -1
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/GoogleCUAClient.js +1 -1
- package/dist/esm/lib/v3/agent/GoogleCUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/OpenAICUAClient.js +1 -1
- package/dist/esm/lib/v3/agent/OpenAICUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/act.js +1 -10
- package/dist/esm/lib/v3/agent/tools/act.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/ariaTree.js +1 -12
- package/dist/esm/lib/v3/agent/tools/ariaTree.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/braveSearch.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/browserbaseSearch.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/click.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/clickAndHold.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/extract.js +1 -10
- package/dist/esm/lib/v3/agent/tools/extract.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/fillFormVision.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/fillform.js +1 -10
- package/dist/esm/lib/v3/agent/tools/fillform.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/index.d.ts +2 -2
- package/dist/esm/lib/v3/agent/tools/index.js +53 -5
- package/dist/esm/lib/v3/agent/tools/index.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/keys.d.ts +1 -1
- package/dist/esm/lib/v3/agent/tools/keys.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/type.js.map +1 -1
- package/dist/esm/lib/v3/api.js +9 -2
- package/dist/esm/lib/v3/api.js.map +1 -1
- package/dist/esm/lib/v3/flowlogger/EventEmitter.d.ts +7 -0
- package/dist/esm/lib/v3/flowlogger/EventEmitter.js +26 -0
- package/dist/esm/lib/v3/flowlogger/EventEmitter.js.map +1 -0
- package/dist/esm/lib/v3/flowlogger/EventSink.d.ts +44 -0
- package/dist/esm/lib/v3/flowlogger/EventSink.js +206 -0
- package/dist/esm/lib/v3/flowlogger/EventSink.js.map +1 -0
- package/dist/esm/lib/v3/flowlogger/EventStore.d.ts +26 -0
- package/dist/esm/lib/v3/flowlogger/EventStore.js +127 -0
- package/dist/esm/lib/v3/flowlogger/EventStore.js.map +1 -0
- package/dist/{cjs/lib/v3/flowLogger.d.ts → esm/lib/v3/flowlogger/FlowLogger.d.ts} +32 -31
- package/dist/esm/lib/v3/flowlogger/FlowLogger.js +583 -0
- package/dist/esm/lib/v3/flowlogger/FlowLogger.js.map +1 -0
- package/dist/esm/lib/v3/flowlogger/prettify.d.ts +6 -0
- package/dist/esm/lib/v3/flowlogger/prettify.js +389 -0
- package/dist/esm/lib/v3/flowlogger/prettify.js.map +1 -0
- package/dist/esm/lib/v3/handlers/handlerUtils/actHandlerUtils.js +25 -27
- package/dist/esm/lib/v3/handlers/handlerUtils/actHandlerUtils.js.map +1 -1
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js +1 -1
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js +2 -4
- package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
- package/dist/esm/lib/v3/llm/aisdk.js +1 -1
- package/dist/esm/lib/v3/llm/aisdk.js.map +1 -1
- package/dist/esm/lib/v3/types/public/options.d.ts +2 -0
- package/dist/esm/lib/v3/types/public/options.js.map +1 -1
- package/dist/esm/lib/v3/understudy/cdp.d.ts +1 -1
- package/dist/esm/lib/v3/understudy/cdp.js +78 -38
- package/dist/esm/lib/v3/understudy/cdp.js.map +1 -1
- package/dist/esm/lib/v3/understudy/page.js +13 -18
- package/dist/esm/lib/v3/understudy/page.js.map +1 -1
- package/dist/esm/lib/v3/v3.d.ts +5 -5
- package/dist/esm/lib/v3/v3.js +43 -41
- package/dist/esm/lib/v3/v3.js.map +1 -1
- package/dist/esm/tests/integration/flowLogger.spec.d.ts +1 -0
- package/dist/esm/tests/integration/flowLogger.spec.js +712 -0
- package/dist/esm/tests/integration/flowLogger.spec.js.map +1 -0
- package/dist/esm/tests/integration/testUtils.d.ts +33 -0
- package/dist/esm/tests/integration/testUtils.js +138 -0
- package/dist/esm/tests/integration/testUtils.js.map +1 -1
- package/dist/esm/tests/integration/timeouts.spec.js +112 -2
- package/dist/esm/tests/integration/timeouts.spec.js.map +1 -1
- package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.d.ts +1 -0
- package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.js +93 -0
- package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.js.map +1 -0
- package/dist/esm/tests/unit/flowlogger-capturing-llm.test.d.ts +1 -0
- package/dist/esm/tests/unit/flowlogger-capturing-llm.test.js +41 -0
- package/dist/esm/tests/unit/flowlogger-capturing-llm.test.js.map +1 -0
- package/dist/esm/tests/unit/flowlogger-eventstore.test.d.ts +1 -0
- package/dist/esm/tests/unit/flowlogger-eventstore.test.js +248 -0
- package/dist/esm/tests/unit/flowlogger-eventstore.test.js.map +1 -0
- package/package.json +3 -1
- package/dist/cjs/lib/v3/eventStore.d.ts +0 -41
- package/dist/cjs/lib/v3/eventStore.js +0 -375
- package/dist/cjs/lib/v3/eventStore.js.map +0 -1
- package/dist/cjs/lib/v3/flowLogger.js +0 -470
- package/dist/cjs/lib/v3/flowLogger.js.map +0 -1
- package/dist/esm/lib/v3/eventStore.d.ts +0 -41
- package/dist/esm/lib/v3/eventStore.js +0 -363
- package/dist/esm/lib/v3/eventStore.js.map +0 -1
- package/dist/esm/lib/v3/flowLogger.js +0 -462
- package/dist/esm/lib/v3/flowLogger.js.map +0 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { FlowEvent } from "./FlowLogger.js";
|
|
2
|
+
import type { EventStoreApi, EventStoreQuery } from "./EventStore.js";
|
|
3
|
+
export interface EventSink {
|
|
4
|
+
emit(event: FlowEvent): Promise<void>;
|
|
5
|
+
query(query: EventStoreQuery): Promise<FlowEvent[]>;
|
|
6
|
+
destroy(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
declare abstract class FileEventSink implements EventSink {
|
|
9
|
+
private readonly streamPromise;
|
|
10
|
+
constructor(sessionDirPromise: Promise<string | null>, fileName: string);
|
|
11
|
+
protected abstract serialize(event: FlowEvent): Promise<string | null>;
|
|
12
|
+
emit(event: FlowEvent): Promise<void>;
|
|
13
|
+
query(): Promise<FlowEvent[]>;
|
|
14
|
+
destroy(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare class JsonlFileEventSink extends FileEventSink {
|
|
17
|
+
constructor(sessionDirPromise: Promise<string | null>);
|
|
18
|
+
protected serialize(event: FlowEvent): Promise<string>;
|
|
19
|
+
}
|
|
20
|
+
export declare class PrettyLogFileEventSink extends FileEventSink {
|
|
21
|
+
private readonly store;
|
|
22
|
+
constructor(sessionDirPromise: Promise<string | null>, store: Pick<EventStoreApi, "query">);
|
|
23
|
+
protected serialize(event: FlowEvent): Promise<string | null>;
|
|
24
|
+
}
|
|
25
|
+
export declare class PrettyStderrEventSink implements EventSink {
|
|
26
|
+
private readonly store;
|
|
27
|
+
constructor(store: Pick<EventStoreApi, "query">);
|
|
28
|
+
emit(event: FlowEvent): Promise<void>;
|
|
29
|
+
query(): Promise<FlowEvent[]>;
|
|
30
|
+
destroy(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export declare class InMemoryEventSink implements EventSink {
|
|
33
|
+
protected readonly limit: number;
|
|
34
|
+
constructor(limit?: number);
|
|
35
|
+
protected readonly events: FlowEvent[];
|
|
36
|
+
protected storeEvent(event: FlowEvent): FlowEvent;
|
|
37
|
+
emit(event: FlowEvent): Promise<void>;
|
|
38
|
+
query(query: EventStoreQuery): Promise<FlowEvent[]>;
|
|
39
|
+
destroy(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export declare class ShallowInMemoryEventSink extends InMemoryEventSink {
|
|
42
|
+
protected storeEvent(event: FlowEvent): FlowEvent;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { FlowEvent } from "./FlowLogger.js";
|
|
4
|
+
import { prettifyColorStderrLine, prettifyEvent, prettifyIsCdpEvent, prettifySanitizeEvent, } from "./prettify.js";
|
|
5
|
+
// Checks whether an event matches a query used by queryable sinks. `eventId` matches both the event itself and descendants of that event.
|
|
6
|
+
function matchesEventStoreQuery(event, query) {
|
|
7
|
+
if (query.sessionId && event.sessionId !== query.sessionId)
|
|
8
|
+
return false;
|
|
9
|
+
if (query.eventId) {
|
|
10
|
+
const matchesEvent = event.eventId === query.eventId ||
|
|
11
|
+
event.eventParentIds.includes(query.eventId);
|
|
12
|
+
if (!matchesEvent) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (query.eventType) {
|
|
17
|
+
const pattern = new RegExp(`^${query.eventType
|
|
18
|
+
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
19
|
+
.replace(/\\\*/g, ".*")}$`);
|
|
20
|
+
if (!pattern.test(event.eventType)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// File Sink Helpers
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Returns true when a file sink's stream is still open and writable.
|
|
30
|
+
function isWritable(stream) {
|
|
31
|
+
return !!(stream && !stream.destroyed && stream.writable);
|
|
32
|
+
}
|
|
33
|
+
// Writes a serialized event to a file sink and converts callback-style stream completion into a promise.
|
|
34
|
+
function writeToStream(stream, value) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
try {
|
|
37
|
+
stream.write(value, (error) => {
|
|
38
|
+
if (error) {
|
|
39
|
+
reject(error);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
resolve();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
reject(error);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// =============================================================================
|
|
51
|
+
// Event Sink Implementations
|
|
52
|
+
// =============================================================================
|
|
53
|
+
class FileEventSink {
|
|
54
|
+
streamPromise; // Lazily opens the one file stream owned by this sink when the session directory resolves.
|
|
55
|
+
// Creates a best-effort file sink bound to a single session directory.
|
|
56
|
+
constructor(sessionDirPromise, fileName) {
|
|
57
|
+
this.streamPromise = sessionDirPromise.then((sessionDir) => sessionDir
|
|
58
|
+
? fs.createWriteStream(path.join(sessionDir, fileName), { flags: "a" })
|
|
59
|
+
: null);
|
|
60
|
+
}
|
|
61
|
+
// Serializes and appends a single event. File sinks are intentionally best-effort and never allowed to affect library execution flow.
|
|
62
|
+
async emit(event) {
|
|
63
|
+
try {
|
|
64
|
+
const stream = await this.streamPromise;
|
|
65
|
+
if (!isWritable(stream)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const serialized = await this.serialize(event);
|
|
69
|
+
if (!serialized) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
await writeToStream(stream, serialized);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// best effort only
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// File sinks are write-only and do not support query reads.
|
|
79
|
+
async query() {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
// Closes the underlying file stream when the owning store shuts down.
|
|
83
|
+
async destroy() {
|
|
84
|
+
const stream = await this.streamPromise.catch(() => null);
|
|
85
|
+
if (!isWritable(stream)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
await new Promise((resolve) => {
|
|
89
|
+
stream.end(resolve);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export class JsonlFileEventSink extends FileEventSink {
|
|
94
|
+
// Writes full verbatim events to `session_events.jsonl`.
|
|
95
|
+
constructor(sessionDirPromise) {
|
|
96
|
+
super(sessionDirPromise, "session_events.jsonl");
|
|
97
|
+
}
|
|
98
|
+
// Serializes the full event for lossless machine-readable storage.
|
|
99
|
+
async serialize(event) {
|
|
100
|
+
return `${JSON.stringify(event)}\n`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export class PrettyLogFileEventSink extends FileEventSink {
|
|
104
|
+
store;
|
|
105
|
+
// Writes human-readable pretty lines to `session_events.log`.
|
|
106
|
+
constructor(sessionDirPromise, store) {
|
|
107
|
+
super(sessionDirPromise, "session_events.log");
|
|
108
|
+
this.store = store;
|
|
109
|
+
}
|
|
110
|
+
// Pretty-prints the event using recent in-memory ancestry.
|
|
111
|
+
async serialize(event) {
|
|
112
|
+
const line = await prettifyEvent(this.store, prettifySanitizeEvent(event));
|
|
113
|
+
return line ? `${line}\n` : null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export class PrettyStderrEventSink {
|
|
117
|
+
store;
|
|
118
|
+
// Writes pretty lines to stderr for verbose local debugging. CDP events are intentionally omitted here to keep stderr high-signal.
|
|
119
|
+
constructor(store) {
|
|
120
|
+
this.store = store;
|
|
121
|
+
} // Queried during prettification so stderr lines can include recent ancestry tags.
|
|
122
|
+
// Best-effort stderr writer used only for interactive debugging output.
|
|
123
|
+
async emit(event) {
|
|
124
|
+
try {
|
|
125
|
+
if (prettifyIsCdpEvent(event)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const line = await prettifyEvent(this.store, prettifySanitizeEvent(event));
|
|
129
|
+
if (!line) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
await new Promise((resolve, reject) => {
|
|
133
|
+
try {
|
|
134
|
+
process.stderr.write(`${prettifyColorStderrLine(line)}\n`, (error) => {
|
|
135
|
+
if (error) {
|
|
136
|
+
reject(error);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
resolve();
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
reject(error);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// best effort only
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Stderr sink is write-only and does not support query reads.
|
|
152
|
+
async query() {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
// No teardown is required for stderr.
|
|
156
|
+
async destroy() { }
|
|
157
|
+
}
|
|
158
|
+
export class InMemoryEventSink {
|
|
159
|
+
limit;
|
|
160
|
+
// Retains recent events for query lookups. Tests usually attach this sink explicitly when they need full historical payloads.
|
|
161
|
+
constructor(limit = Infinity) {
|
|
162
|
+
this.limit = limit;
|
|
163
|
+
}
|
|
164
|
+
events = []; // Retained history; `emit()` appends to it and trims old entries when `limit` is exceeded.
|
|
165
|
+
// Gives subclasses a hook to transform events before they are retained.
|
|
166
|
+
storeEvent(event) {
|
|
167
|
+
return event;
|
|
168
|
+
}
|
|
169
|
+
// Stores a new event and trims the oldest retained entries once the sink exceeds its configured limit.
|
|
170
|
+
async emit(event) {
|
|
171
|
+
this.events.push(this.storeEvent(event));
|
|
172
|
+
if (this.events.length > this.limit) {
|
|
173
|
+
this.events.splice(0, this.events.length - this.limit);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Returns retained events that match the query, ordered by creation time.
|
|
177
|
+
async query(query) {
|
|
178
|
+
const filtered = this.events.filter((event) => matchesEventStoreQuery(event, query));
|
|
179
|
+
filtered.sort((left, right) => {
|
|
180
|
+
const createdAtOrder = left.eventCreatedAt.localeCompare(right.eventCreatedAt);
|
|
181
|
+
if (createdAtOrder !== 0) {
|
|
182
|
+
return createdAtOrder;
|
|
183
|
+
}
|
|
184
|
+
return left.eventId.localeCompare(right.eventId);
|
|
185
|
+
});
|
|
186
|
+
return query.limit ? filtered.slice(-query.limit) : filtered;
|
|
187
|
+
}
|
|
188
|
+
// Clears retained history when the owning store shuts down.
|
|
189
|
+
async destroy() {
|
|
190
|
+
this.events.length = 0;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
export class ShallowInMemoryEventSink extends InMemoryEventSink {
|
|
194
|
+
// Retains only ancestry metadata for the default query sink so verbose or long-running sessions do not hold onto large payloads such as screenshots.
|
|
195
|
+
storeEvent(event) {
|
|
196
|
+
return new FlowEvent({
|
|
197
|
+
eventType: event.eventType,
|
|
198
|
+
eventId: event.eventId,
|
|
199
|
+
eventCreatedAt: event.eventCreatedAt,
|
|
200
|
+
sessionId: event.sessionId,
|
|
201
|
+
eventParentIds: [...event.eventParentIds],
|
|
202
|
+
data: {},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=EventSink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventSink.js","sourceRoot":"","sources":["../../../../../lib/v3/flowlogger/EventSink.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { FlowEvent } from \"./FlowLogger.js\";\nimport type { EventStoreApi, EventStoreQuery } from \"./EventStore.js\";\nimport {\n prettifyColorStderrLine,\n prettifyEvent,\n prettifyIsCdpEvent,\n prettifySanitizeEvent,\n} from \"./prettify.js\";\n\n// =============================================================================\n// Event Sink Contracts\n// =============================================================================\n\nexport interface EventSink {\n emit(event: FlowEvent): Promise<void>;\n query(query: EventStoreQuery): Promise<FlowEvent[]>;\n destroy(): Promise<void>;\n}\n\n// Checks whether an event matches a query used by queryable sinks. `eventId` matches both the event itself and descendants of that event.\nfunction matchesEventStoreQuery(\n event: FlowEvent,\n query: EventStoreQuery,\n): boolean {\n if (query.sessionId && event.sessionId !== query.sessionId) return false;\n\n if (query.eventId) {\n const matchesEvent =\n event.eventId === query.eventId ||\n event.eventParentIds.includes(query.eventId);\n if (!matchesEvent) {\n return false;\n }\n }\n\n if (query.eventType) {\n const pattern = new RegExp(\n `^${query.eventType\n .replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\\\\\*/g, \".*\")}$`,\n );\n if (!pattern.test(event.eventType)) {\n return false;\n }\n }\n\n return true;\n}\n\n// =============================================================================\n// File Sink Helpers\n// =============================================================================\n\n// Returns true when a file sink's stream is still open and writable.\nfunction isWritable(stream: fs.WriteStream | null): stream is fs.WriteStream {\n return !!(stream && !stream.destroyed && stream.writable);\n}\n\n// Writes a serialized event to a file sink and converts callback-style stream completion into a promise.\nfunction writeToStream(stream: fs.WriteStream, value: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n try {\n stream.write(value, (error?: Error | null) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n } catch (error) {\n reject(error);\n }\n });\n}\n\n// =============================================================================\n// Event Sink Implementations\n// =============================================================================\n\nabstract class FileEventSink implements EventSink {\n private readonly streamPromise: Promise<fs.WriteStream | null>; // Lazily opens the one file stream owned by this sink when the session directory resolves.\n\n // Creates a best-effort file sink bound to a single session directory.\n constructor(sessionDirPromise: Promise<string | null>, fileName: string) {\n this.streamPromise = sessionDirPromise.then((sessionDir) =>\n sessionDir\n ? fs.createWriteStream(path.join(sessionDir, fileName), { flags: \"a\" })\n : null,\n );\n }\n\n protected abstract serialize(event: FlowEvent): Promise<string | null>;\n\n // Serializes and appends a single event. File sinks are intentionally best-effort and never allowed to affect library execution flow.\n async emit(event: FlowEvent): Promise<void> {\n try {\n const stream = await this.streamPromise;\n if (!isWritable(stream)) {\n return;\n }\n\n const serialized = await this.serialize(event);\n if (!serialized) {\n return;\n }\n\n await writeToStream(stream, serialized);\n } catch {\n // best effort only\n }\n }\n\n // File sinks are write-only and do not support query reads.\n async query(): Promise<FlowEvent[]> {\n return [];\n }\n\n // Closes the underlying file stream when the owning store shuts down.\n async destroy(): Promise<void> {\n const stream = await this.streamPromise.catch((): null => null);\n if (!isWritable(stream)) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n stream.end(resolve);\n });\n }\n}\n\nexport class JsonlFileEventSink extends FileEventSink {\n // Writes full verbatim events to `session_events.jsonl`.\n constructor(sessionDirPromise: Promise<string | null>) {\n super(sessionDirPromise, \"session_events.jsonl\");\n }\n\n // Serializes the full event for lossless machine-readable storage.\n protected async serialize(event: FlowEvent): Promise<string> {\n return `${JSON.stringify(event)}\\n`;\n }\n}\n\nexport class PrettyLogFileEventSink extends FileEventSink {\n // Writes human-readable pretty lines to `session_events.log`.\n constructor(\n sessionDirPromise: Promise<string | null>,\n private readonly store: Pick<EventStoreApi, \"query\">, // Queried during prettification so each line can recover recent ancestry tags.\n ) {\n super(sessionDirPromise, \"session_events.log\");\n }\n\n // Pretty-prints the event using recent in-memory ancestry.\n protected async serialize(event: FlowEvent): Promise<string | null> {\n const line = await prettifyEvent(this.store, prettifySanitizeEvent(event));\n return line ? `${line}\\n` : null;\n }\n}\n\nexport class PrettyStderrEventSink implements EventSink {\n // Writes pretty lines to stderr for verbose local debugging. CDP events are intentionally omitted here to keep stderr high-signal.\n constructor(private readonly store: Pick<EventStoreApi, \"query\">) {} // Queried during prettification so stderr lines can include recent ancestry tags.\n\n // Best-effort stderr writer used only for interactive debugging output.\n async emit(event: FlowEvent): Promise<void> {\n try {\n if (prettifyIsCdpEvent(event)) {\n return;\n }\n\n const line = await prettifyEvent(\n this.store,\n prettifySanitizeEvent(event),\n );\n if (!line) {\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n try {\n process.stderr.write(\n `${prettifyColorStderrLine(line)}\\n`,\n (error?: Error | null) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n },\n );\n } catch (error) {\n reject(error);\n }\n });\n } catch {\n // best effort only\n }\n }\n\n // Stderr sink is write-only and does not support query reads.\n async query(): Promise<FlowEvent[]> {\n return [];\n }\n\n // No teardown is required for stderr.\n async destroy(): Promise<void> {}\n}\n\nexport class InMemoryEventSink implements EventSink {\n // Retains recent events for query lookups. Tests usually attach this sink explicitly when they need full historical payloads.\n constructor(protected readonly limit = Infinity) {}\n\n protected readonly events: FlowEvent[] = []; // Retained history; `emit()` appends to it and trims old entries when `limit` is exceeded.\n\n // Gives subclasses a hook to transform events before they are retained.\n protected storeEvent(event: FlowEvent): FlowEvent {\n return event;\n }\n\n // Stores a new event and trims the oldest retained entries once the sink exceeds its configured limit.\n async emit(event: FlowEvent): Promise<void> {\n this.events.push(this.storeEvent(event));\n if (this.events.length > this.limit) {\n this.events.splice(0, this.events.length - this.limit);\n }\n }\n\n // Returns retained events that match the query, ordered by creation time.\n async query(query: EventStoreQuery): Promise<FlowEvent[]> {\n const filtered = this.events.filter((event) =>\n matchesEventStoreQuery(event, query),\n );\n filtered.sort((left, right) => {\n const createdAtOrder = left.eventCreatedAt.localeCompare(\n right.eventCreatedAt,\n );\n if (createdAtOrder !== 0) {\n return createdAtOrder;\n }\n\n return left.eventId.localeCompare(right.eventId);\n });\n return query.limit ? filtered.slice(-query.limit) : filtered;\n }\n\n // Clears retained history when the owning store shuts down.\n async destroy(): Promise<void> {\n this.events.length = 0;\n }\n}\n\nexport class ShallowInMemoryEventSink extends InMemoryEventSink {\n // Retains only ancestry metadata for the default query sink so verbose or long-running sessions do not hold onto large payloads such as screenshots.\n protected override storeEvent(event: FlowEvent): FlowEvent {\n return new FlowEvent({\n eventType: event.eventType,\n eventId: event.eventId,\n eventCreatedAt: event.eventCreatedAt,\n sessionId: event.sessionId,\n eventParentIds: [...event.eventParentIds],\n data: {},\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { V3Options } from "../types/public/index.js";
|
|
2
|
+
import { EventSink } from "./EventSink.js";
|
|
3
|
+
import { FlowEvent } from "./FlowLogger.js";
|
|
4
|
+
export interface EventStoreQuery {
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
eventId?: string;
|
|
7
|
+
eventType?: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface EventStoreApi {
|
|
11
|
+
readonly sessionId: string;
|
|
12
|
+
emit(event: FlowEvent): Promise<void>;
|
|
13
|
+
query(query: EventStoreQuery): Promise<FlowEvent[]>;
|
|
14
|
+
destroy(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare function getConfigDir(): string;
|
|
17
|
+
export declare class EventStore implements EventStoreApi {
|
|
18
|
+
readonly sessionId: string;
|
|
19
|
+
private readonly sinks;
|
|
20
|
+
private destroyed;
|
|
21
|
+
query: (query: EventStoreQuery) => Promise<FlowEvent[]>;
|
|
22
|
+
constructor(sessionId: string, options?: V3Options, querySink?: EventSink);
|
|
23
|
+
private registerSink;
|
|
24
|
+
emit: (event: FlowEvent) => Promise<void>;
|
|
25
|
+
destroy(): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { JsonlFileEventSink, PrettyLogFileEventSink, PrettyStderrEventSink, ShallowInMemoryEventSink, } from "./EventSink.js";
|
|
4
|
+
import { FlowEvent } from "./FlowLogger.js";
|
|
5
|
+
const DEFAULT_IN_MEMORY_EVENT_LIMIT = 500; // Per-session ancestry window retained by the default shallow query sink.
|
|
6
|
+
const CONFIG_DIR = process.env.BROWSERBASE_CONFIG_DIR || ""; // Base directory for session metadata + file-backed flow logs.
|
|
7
|
+
const FLOW_LOGS_ENABLED = process.env.BROWSERBASE_FLOW_LOGS === "1"; // Force-enables the pretty stderr flow sink even when `verbose !== 2`.
|
|
8
|
+
const SENSITIVE_KEYS = /key|secret|token|api-key|apikey|api_key|password|passwd|pwd|credential|auth/i; // Redacts obvious secrets before session options are written to disk.
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Filesystem Helpers
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Redacts secrets before session options are written to `session.json` inside a config-dir-backed session directory.
|
|
13
|
+
function sanitizeOptions(options) {
|
|
14
|
+
const sanitize = (value) => {
|
|
15
|
+
if (typeof value !== "object" || value === null)
|
|
16
|
+
return value;
|
|
17
|
+
if (Array.isArray(value))
|
|
18
|
+
return value.map(sanitize);
|
|
19
|
+
const result = {};
|
|
20
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
21
|
+
result[key] = SENSITIVE_KEYS.test(key) ? "******" : sanitize(entry);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
return sanitize({ ...options });
|
|
26
|
+
}
|
|
27
|
+
// Resolves the configured Browserbase config directory used by file sinks.
|
|
28
|
+
export function getConfigDir() {
|
|
29
|
+
return CONFIG_DIR ? path.resolve(CONFIG_DIR) : "";
|
|
30
|
+
}
|
|
31
|
+
// Creates the per-session directory used by file sinks and writes best-effort metadata such as the sanitized `session.json` file and `latest` symlink.
|
|
32
|
+
async function createSessionDir(sessionId, options) {
|
|
33
|
+
const configDir = getConfigDir();
|
|
34
|
+
if (!configDir) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const sessionDir = path.join(configDir, "sessions", sessionId);
|
|
38
|
+
await fs.promises.mkdir(sessionDir, { recursive: true });
|
|
39
|
+
if (options) {
|
|
40
|
+
await fs.promises.writeFile(path.join(sessionDir, "session.json"), JSON.stringify(sanitizeOptions(options), null, 2), "utf-8");
|
|
41
|
+
}
|
|
42
|
+
const latestLink = path.join(configDir, "sessions", "latest");
|
|
43
|
+
try {
|
|
44
|
+
try {
|
|
45
|
+
await fs.promises.unlink(latestLink);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// ignore missing link
|
|
49
|
+
}
|
|
50
|
+
await fs.promises.symlink(sessionId, latestLink, "dir");
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// symlink best effort only
|
|
54
|
+
}
|
|
55
|
+
return sessionDir;
|
|
56
|
+
}
|
|
57
|
+
// =============================================================================
|
|
58
|
+
// Event Store
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Per-session flow event sink manager.
|
|
61
|
+
// This is not an event bus. V3 forwards already-emitted FlowEvents into it so
|
|
62
|
+
// the store can fan them out to configured sinks, answer `query()` calls from
|
|
63
|
+
// its one query sink, and tear down its sinks when the session closes.
|
|
64
|
+
// We keep this as a separate object instead of wiring sinks directly with
|
|
65
|
+
// `v3.bus.on("*", sink.emit)` because pretty sinks need access to a shared
|
|
66
|
+
// query interface while rendering. Prettified lines often need to look up
|
|
67
|
+
// related parent/child events to recover the readable ancestry tags and labels.
|
|
68
|
+
// Passing sinks into each other to share that state gets messy quickly, so the
|
|
69
|
+
// EventStore contains the circular dependency: all sinks live here, and any
|
|
70
|
+
// sink that needs historical context can call the one `EventStore.query()`
|
|
71
|
+
// entrypoint backed by the main query sink for this session.
|
|
72
|
+
export class EventStore {
|
|
73
|
+
sessionId;
|
|
74
|
+
sinks = new Set(); // All sinks attached for this session; constructor registers them here and `destroy()` tears them down.
|
|
75
|
+
destroyed = false; // Flipped by `destroy()` so later emits and teardown calls become no-ops.
|
|
76
|
+
query; // Always reads from the one query sink chosen at construction time.
|
|
77
|
+
// Creates the per-instance store owned by a single V3 session. This store is intentionally single-session; it ignores events for other session ids.
|
|
78
|
+
constructor(
|
|
79
|
+
// Usually matches `browserbaseSessionId` today, but it is the store's own Stagehand session identifier and may diverge in the future.
|
|
80
|
+
sessionId, options, querySink = new ShallowInMemoryEventSink(DEFAULT_IN_MEMORY_EVENT_LIMIT)) {
|
|
81
|
+
this.sessionId = sessionId;
|
|
82
|
+
const sessionDirPromise = createSessionDir(sessionId, options);
|
|
83
|
+
this.registerSink(querySink);
|
|
84
|
+
this.query = async (query) => {
|
|
85
|
+
if (query.sessionId && query.sessionId !== this.sessionId) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
return querySink.query({
|
|
89
|
+
...query,
|
|
90
|
+
sessionId: this.sessionId,
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
if (getConfigDir()) {
|
|
94
|
+
this.registerSink(new JsonlFileEventSink(sessionDirPromise));
|
|
95
|
+
this.registerSink(new PrettyLogFileEventSink(sessionDirPromise, this));
|
|
96
|
+
}
|
|
97
|
+
if (options?.verbose === 2 || FLOW_LOGS_ENABLED) {
|
|
98
|
+
this.registerSink(new PrettyStderrEventSink(this));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Adds a sink to the direct fanout list used by `emit()`.
|
|
102
|
+
registerSink(sink) {
|
|
103
|
+
this.sinks.add(sink);
|
|
104
|
+
}
|
|
105
|
+
// Emits an event to all attached sinks when it belongs to this store's single session.
|
|
106
|
+
emit = async (event) => {
|
|
107
|
+
if (!(event instanceof FlowEvent)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (this.destroyed || event.sessionId !== this.sessionId) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
await Promise.allSettled([...this.sinks].map((sink) => sink.emit(event)));
|
|
114
|
+
};
|
|
115
|
+
// Tears down all sinks when the V3 instance is closed.
|
|
116
|
+
async destroy() {
|
|
117
|
+
if (this.destroyed) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.destroyed = true;
|
|
121
|
+
await Promise.all([...this.sinks].map((sink) => sink.destroy().catch(() => {
|
|
122
|
+
// best effort cleanup
|
|
123
|
+
})));
|
|
124
|
+
this.sinks.clear();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=EventStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventStore.js","sourceRoot":"","sources":["../../../../../lib/v3/flowlogger/EventStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAEL,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,6BAA6B,GAAG,GAAG,CAAC,CAAC,0EAA0E;AACrH,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,+DAA+D;AAC5H,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG,CAAC,CAAC,uEAAuE;AAC5I,MAAM,cAAc,GAClB,8EAA8E,CAAC,CAAC,sEAAsE;AAoBxJ,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF,qHAAqH;AACrH,SAAS,eAAe,CAAC,OAAkB;IACzC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAW,EAAE;QAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAErD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,CAA4B,CAAC;AAC7D,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACpD,CAAC;AAED,uJAAuJ;AACvJ,KAAK,UAAU,gBAAgB,CAC7B,SAAiB,EACjB,OAAmB;IAEnB,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IAC/D,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzD,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CACzB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EACrC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EACjD,OAAO,CACR,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;QACD,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gFAAgF;AAChF,cAAc;AACd,gFAAgF;AAEhF,uCAAuC;AACvC,8EAA8E;AAC9E,8EAA8E;AAC9E,uEAAuE;AACvE,0EAA0E;AAC1E,2EAA2E;AAC3E,0EAA0E;AAC1E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,2EAA2E;AAC3E,6DAA6D;AAC7D,MAAM,OAAO,UAAU;IAQH;IAPD,KAAK,GAAG,IAAI,GAAG,EAAa,CAAC,CAAC,wGAAwG;IAC/I,SAAS,GAAG,KAAK,CAAC,CAAC,0EAA0E;IAC9F,KAAK,CAAmD,CAAC,oEAAoE;IAEpI,oJAAoJ;IACpJ;IACE,sIAAsI;IACtH,SAAiB,EACjC,OAAmB,EACnB,YAAuB,IAAI,wBAAwB,CACjD,6BAA6B,CAC9B;QAJe,cAAS,GAAT,SAAS,CAAQ;QAMjC,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE/D,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE;YAC3B,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC1D,OAAO,EAAE,CAAC;YACZ,CAAC;YAED,OAAO,SAAS,CAAC,KAAK,CAAC;gBACrB,GAAG,KAAK;gBACR,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,IAAI,YAAY,EAAE,EAAE,CAAC;YACnB,IAAI,CAAC,YAAY,CAAC,IAAI,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAC7D,IAAI,CAAC,YAAY,CAAC,IAAI,sBAAsB,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC,CAAC;QACzE,CAAC;QAED,IAAI,OAAO,EAAE,OAAO,KAAK,CAAC,IAAI,iBAAiB,EAAE,CAAC;YAChD,IAAI,CAAC,YAAY,CAAC,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,0DAA0D;IAClD,YAAY,CAAC,IAAe;QAClC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,uFAAuF;IACvF,IAAI,GAAG,KAAK,EAAE,KAAgB,EAAiB,EAAE;QAC/C,IAAI,CAAC,CAAC,KAAK,YAAY,SAAS,CAAC,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;YACzD,OAAO;QACT,CAAC;QAED,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC;IAEF,uDAAuD;IACvD,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,MAAM,OAAO,CAAC,GAAG,CACf,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC3B,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YACxB,sBAAsB;QACxB,CAAC,CAAC,CACH,CACF,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF","sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { V3Options } from \"../types/public/index.js\";\nimport {\n EventSink,\n JsonlFileEventSink,\n PrettyLogFileEventSink,\n PrettyStderrEventSink,\n ShallowInMemoryEventSink,\n} from \"./EventSink.js\";\nimport { FlowEvent } from \"./FlowLogger.js\";\n\nconst DEFAULT_IN_MEMORY_EVENT_LIMIT = 500; // Per-session ancestry window retained by the default shallow query sink.\nconst CONFIG_DIR = process.env.BROWSERBASE_CONFIG_DIR || \"\"; // Base directory for session metadata + file-backed flow logs.\nconst FLOW_LOGS_ENABLED = process.env.BROWSERBASE_FLOW_LOGS === \"1\"; // Force-enables the pretty stderr flow sink even when `verbose !== 2`.\nconst SENSITIVE_KEYS =\n /key|secret|token|api-key|apikey|api_key|password|passwd|pwd|credential|auth/i; // Redacts obvious secrets before session options are written to disk.\n\n// =============================================================================\n// Public Contracts\n// =============================================================================\n\nexport interface EventStoreQuery {\n sessionId?: string;\n eventId?: string;\n eventType?: string;\n limit?: number;\n}\n\nexport interface EventStoreApi {\n readonly sessionId: string;\n emit(event: FlowEvent): Promise<void>;\n query(query: EventStoreQuery): Promise<FlowEvent[]>;\n destroy(): Promise<void>;\n}\n\n// =============================================================================\n// Filesystem Helpers\n// =============================================================================\n\n// Redacts secrets before session options are written to `session.json` inside a config-dir-backed session directory.\nfunction sanitizeOptions(options: V3Options): Record<string, unknown> {\n const sanitize = (value: unknown): unknown => {\n if (typeof value !== \"object\" || value === null) return value;\n if (Array.isArray(value)) return value.map(sanitize);\n\n const result: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n result[key] = SENSITIVE_KEYS.test(key) ? \"******\" : sanitize(entry);\n }\n return result;\n };\n\n return sanitize({ ...options }) as Record<string, unknown>;\n}\n\n// Resolves the configured Browserbase config directory used by file sinks.\nexport function getConfigDir(): string {\n return CONFIG_DIR ? path.resolve(CONFIG_DIR) : \"\";\n}\n\n// Creates the per-session directory used by file sinks and writes best-effort metadata such as the sanitized `session.json` file and `latest` symlink.\nasync function createSessionDir(\n sessionId: string,\n options?: V3Options,\n): Promise<string | null> {\n const configDir = getConfigDir();\n if (!configDir) {\n return null;\n }\n\n const sessionDir = path.join(configDir, \"sessions\", sessionId);\n await fs.promises.mkdir(sessionDir, { recursive: true });\n\n if (options) {\n await fs.promises.writeFile(\n path.join(sessionDir, \"session.json\"),\n JSON.stringify(sanitizeOptions(options), null, 2),\n \"utf-8\",\n );\n }\n\n const latestLink = path.join(configDir, \"sessions\", \"latest\");\n try {\n try {\n await fs.promises.unlink(latestLink);\n } catch {\n // ignore missing link\n }\n await fs.promises.symlink(sessionId, latestLink, \"dir\");\n } catch {\n // symlink best effort only\n }\n\n return sessionDir;\n}\n\n// =============================================================================\n// Event Store\n// =============================================================================\n\n// Per-session flow event sink manager.\n// This is not an event bus. V3 forwards already-emitted FlowEvents into it so\n// the store can fan them out to configured sinks, answer `query()` calls from\n// its one query sink, and tear down its sinks when the session closes.\n// We keep this as a separate object instead of wiring sinks directly with\n// `v3.bus.on(\"*\", sink.emit)` because pretty sinks need access to a shared\n// query interface while rendering. Prettified lines often need to look up\n// related parent/child events to recover the readable ancestry tags and labels.\n// Passing sinks into each other to share that state gets messy quickly, so the\n// EventStore contains the circular dependency: all sinks live here, and any\n// sink that needs historical context can call the one `EventStore.query()`\n// entrypoint backed by the main query sink for this session.\nexport class EventStore implements EventStoreApi {\n private readonly sinks = new Set<EventSink>(); // All sinks attached for this session; constructor registers them here and `destroy()` tears them down.\n private destroyed = false; // Flipped by `destroy()` so later emits and teardown calls become no-ops.\n public query: (query: EventStoreQuery) => Promise<FlowEvent[]>; // Always reads from the one query sink chosen at construction time.\n\n // Creates the per-instance store owned by a single V3 session. This store is intentionally single-session; it ignores events for other session ids.\n constructor(\n // Usually matches `browserbaseSessionId` today, but it is the store's own Stagehand session identifier and may diverge in the future.\n public readonly sessionId: string,\n options?: V3Options,\n querySink: EventSink = new ShallowInMemoryEventSink(\n DEFAULT_IN_MEMORY_EVENT_LIMIT,\n ),\n ) {\n const sessionDirPromise = createSessionDir(sessionId, options);\n\n this.registerSink(querySink);\n this.query = async (query) => {\n if (query.sessionId && query.sessionId !== this.sessionId) {\n return [];\n }\n\n return querySink.query({\n ...query,\n sessionId: this.sessionId,\n });\n };\n\n if (getConfigDir()) {\n this.registerSink(new JsonlFileEventSink(sessionDirPromise));\n this.registerSink(new PrettyLogFileEventSink(sessionDirPromise, this));\n }\n\n if (options?.verbose === 2 || FLOW_LOGS_ENABLED) {\n this.registerSink(new PrettyStderrEventSink(this));\n }\n }\n\n // Adds a sink to the direct fanout list used by `emit()`.\n private registerSink(sink: EventSink): void {\n this.sinks.add(sink);\n }\n\n // Emits an event to all attached sinks when it belongs to this store's single session.\n emit = async (event: FlowEvent): Promise<void> => {\n if (!(event instanceof FlowEvent)) {\n return;\n }\n\n if (this.destroyed || event.sessionId !== this.sessionId) {\n return;\n }\n\n await Promise.allSettled([...this.sinks].map((sink) => sink.emit(event)));\n };\n\n // Tears down all sinks when the V3 instance is closed.\n async destroy(): Promise<void> {\n if (this.destroyed) {\n return;\n }\n\n this.destroyed = true;\n await Promise.all(\n [...this.sinks].map((sink) =>\n sink.destroy().catch(() => {\n // best effort cleanup\n }),\n ),\n );\n this.sinks.clear();\n }\n}\n"]}
|
|
@@ -1,27 +1,38 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
1
|
import type { LanguageModelMiddleware } from "ai";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
eventParentIds
|
|
10
|
-
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { EventEmitterWithWildcardSupport } from "./EventEmitter.js";
|
|
4
|
+
export declare const FlowEventDataSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
5
|
+
export declare const FlowEventInputSchema: z.ZodObject<{
|
|
6
|
+
eventType: z.ZodString;
|
|
7
|
+
eventId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
eventParentIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
9
|
+
eventCreatedAt: z.ZodOptional<z.ZodString>;
|
|
10
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
export type FlowEventData = z.infer<typeof FlowEventDataSchema>;
|
|
14
|
+
export type FlowEventInput = z.input<typeof FlowEventInputSchema>;
|
|
15
|
+
type FlowEventFields = Omit<FlowEventInput, "eventId" | "eventParentIds" | "eventCreatedAt" | "sessionId" | "data"> & {
|
|
16
|
+
eventId: string;
|
|
17
|
+
eventParentIds: string[];
|
|
18
|
+
eventCreatedAt: string;
|
|
19
|
+
sessionId: string;
|
|
20
|
+
data: FlowEventData;
|
|
11
21
|
};
|
|
12
|
-
export declare class FlowEvent {
|
|
13
|
-
static
|
|
22
|
+
export declare class FlowEvent implements FlowEventFields {
|
|
23
|
+
private static deriveEventIdSuffix;
|
|
24
|
+
static createEventId(eventType: string): string;
|
|
14
25
|
eventType: string;
|
|
15
26
|
eventId: string;
|
|
16
27
|
eventParentIds: string[];
|
|
17
|
-
|
|
28
|
+
eventCreatedAt: string;
|
|
18
29
|
sessionId: string;
|
|
19
30
|
data: FlowEventData;
|
|
20
31
|
constructor(input: FlowEventInput);
|
|
21
32
|
}
|
|
22
33
|
export interface FlowLoggerContext {
|
|
23
34
|
sessionId: string;
|
|
24
|
-
eventBus:
|
|
35
|
+
eventBus: EventEmitterWithWildcardSupport;
|
|
25
36
|
parentEvents: FlowEvent[];
|
|
26
37
|
}
|
|
27
38
|
type AsyncOriginalMethod<TArgs extends unknown[] = unknown[], TResult = unknown, TThis = unknown> = (this: TThis, ...args: TArgs) => Promise<TResult>;
|
|
@@ -30,19 +41,22 @@ type FlowLoggerLogOptions = FlowEventInput & {
|
|
|
30
41
|
};
|
|
31
42
|
export declare class FlowLogger {
|
|
32
43
|
private static cloneContext;
|
|
44
|
+
private static resolveReentryContext;
|
|
33
45
|
private static emit;
|
|
34
46
|
private static runWithAutoStatusEventLogging;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
static
|
|
47
|
+
private static logCdpEvent;
|
|
48
|
+
private static emitLlmEvent;
|
|
49
|
+
private static buildMiddlewarePromptSummary;
|
|
50
|
+
private static buildMiddlewareOutputSummary;
|
|
51
|
+
static init(sessionId: string, eventBus: EventEmitterWithWildcardSupport): FlowLoggerContext;
|
|
39
52
|
static close(context?: FlowLoggerContext | null): Promise<void>;
|
|
40
53
|
static get currentContext(): FlowLoggerContext;
|
|
54
|
+
static resolveContext(fallbackContext?: FlowLoggerContext | null): FlowLoggerContext | null;
|
|
41
55
|
static wrapWithLogging<TMethod extends AsyncOriginalMethod>(options: FlowLoggerLogOptions): <TWrappedMethod extends AsyncOriginalMethod<Parameters<TMethod>, Awaited<ReturnType<TMethod>>, ThisParameterType<TMethod>>>(originalMethod: TWrappedMethod) => TWrappedMethod;
|
|
42
56
|
static runWithLogging<TMethod extends AsyncOriginalMethod>(options: FlowLoggerLogOptions, originalMethod: TMethod, params: Readonly<Parameters<TMethod>>): Promise<Awaited<ReturnType<TMethod>>>;
|
|
43
57
|
static runWithLogging<TResult>(options: FlowLoggerLogOptions, originalMethod: AsyncOriginalMethod<[], TResult>, params: ReadonlyArray<unknown>): Promise<Awaited<TResult>>;
|
|
58
|
+
static withContext<T>(context: FlowLoggerContext, fn: () => T): T;
|
|
44
59
|
private static readonly NOISY_CDP_EVENTS;
|
|
45
|
-
private static logCdpEvent;
|
|
46
60
|
static logCdpCallEvent(context: FlowLoggerContext, data: {
|
|
47
61
|
method: string;
|
|
48
62
|
params?: object;
|
|
@@ -71,16 +85,8 @@ export declare class FlowLogger {
|
|
|
71
85
|
inputTokens?: number;
|
|
72
86
|
outputTokens?: number;
|
|
73
87
|
}): void;
|
|
74
|
-
/**
|
|
75
|
-
* Create middleware for wrapping language models with LLM call logging.
|
|
76
|
-
* Returns a no-op middleware when logging is disabled.
|
|
77
|
-
*/
|
|
78
88
|
static createLlmLoggingMiddleware(modelId: string): Pick<LanguageModelMiddleware, "wrapGenerate">;
|
|
79
89
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Format a prompt summary from LLM messages for logging.
|
|
82
|
-
* Returns format like: "some text +{5.8kb image} +{schema} +{12 tools}"
|
|
83
|
-
*/
|
|
84
90
|
export declare function extractLlmPromptSummary(messages: Array<{
|
|
85
91
|
role: string;
|
|
86
92
|
content: unknown;
|
|
@@ -88,11 +94,6 @@ export declare function extractLlmPromptSummary(messages: Array<{
|
|
|
88
94
|
toolCount?: number;
|
|
89
95
|
hasSchema?: boolean;
|
|
90
96
|
}): string | undefined;
|
|
91
|
-
/**
|
|
92
|
-
* Extract a text summary from CUA-style messages.
|
|
93
|
-
* Accepts various message formats (Anthropic, OpenAI, Google).
|
|
94
|
-
*/
|
|
95
97
|
export declare function extractLlmCuaPromptSummary(messages: unknown[]): string | undefined;
|
|
96
|
-
/** Format a CUA response summary for logging */
|
|
97
98
|
export declare function extractLlmCuaResponseSummary(output: unknown): string;
|
|
98
99
|
export {};
|